From 1d0f57cbceb778139ca215cc4fcfd1584951f6dd Mon Sep 17 00:00:00 2001 From: dshaligram Date: Wed, 22 Nov 2006 08:41:20 +0000 Subject: Merged stone_soup r15:451 into trunk. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@452 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/AppHdr.h | 100 +- crawl-ref/source/Crawl.xcodeproj/project.pbxproj | 401 +- crawl-ref/source/FixAry.h | 2 + crawl-ref/source/FixVec.h | 96 +- crawl-ref/source/Kills.cc | 147 +- crawl-ref/source/Kills.h | 9 +- crawl-ref/source/MacString.cc | 199 - crawl-ref/source/MacString.h | 71 - crawl-ref/source/abl-show.cc | 111 +- crawl-ref/source/abyss.cc | 2 + crawl-ref/source/acr.cc | 1751 ++++---- crawl-ref/source/beam.cc | 1698 ++++---- crawl-ref/source/beam.h | 5 + crawl-ref/source/chardump.cc | 860 ++-- crawl-ref/source/chardump.h | 12 +- crawl-ref/source/cloud.cc | 2 + crawl-ref/source/clua.cc | 87 +- crawl-ref/source/command.cc | 510 ++- crawl-ref/source/command.h | 8 + crawl-ref/source/dat/levdes.vim | 75 + crawl-ref/source/dat/splev.des | 1563 ++++++++ crawl-ref/source/dat/vaults.des | 1689 ++++++++ crawl-ref/source/debug.cc | 679 +++- crawl-ref/source/debug.h | 6 + crawl-ref/source/decks.cc | 40 +- crawl-ref/source/decks.h | 2 + crawl-ref/source/defines.h | 143 +- crawl-ref/source/delay.cc | 1129 ++++-- crawl-ref/source/delay.h | 16 + crawl-ref/source/describe.cc | 377 +- crawl-ref/source/describe.h | 9 +- crawl-ref/source/direct.cc | 361 +- crawl-ref/source/direct.h | 29 +- crawl-ref/source/dungeon.cc | 1177 +++--- crawl-ref/source/dungeon.h | 19 + crawl-ref/source/effects.cc | 448 ++- crawl-ref/source/effects.h | 5 +- crawl-ref/source/enum.h | 1332 +++++-- crawl-ref/source/externs.h | 382 +- crawl-ref/source/fight.cc | 761 ++-- crawl-ref/source/fight.h | 8 +- crawl-ref/source/files.cc | 1057 ++--- crawl-ref/source/files.h | 27 +- crawl-ref/source/food.cc | 550 ++- crawl-ref/source/food.h | 4 + crawl-ref/source/hiscores.cc | 2862 +++++++------ crawl-ref/source/hiscores.h | 6 +- crawl-ref/source/initfile.cc | 1248 ++++-- crawl-ref/source/initfile.h | 8 +- crawl-ref/source/insult.cc | 2 + crawl-ref/source/invent.cc | 995 ++--- crawl-ref/source/invent.h | 134 +- crawl-ref/source/it_use2.cc | 65 +- crawl-ref/source/it_use2.h | 3 +- crawl-ref/source/it_use3.cc | 82 +- crawl-ref/source/item_use.cc | 1650 +++++--- crawl-ref/source/item_use.h | 27 +- crawl-ref/source/itemname.cc | 1792 +++------ crawl-ref/source/itemname.h | 62 +- crawl-ref/source/itemprop.cc | 2085 ++++++++++ crawl-ref/source/itemprop.h | 148 + crawl-ref/source/items.cc | 520 +-- crawl-ref/source/items.h | 10 +- crawl-ref/source/lev-pand.cc | 2 + crawl-ref/source/libdos.cc | 21 + crawl-ref/source/libdos.h | 6 + crawl-ref/source/libemx.cc | 233 -- crawl-ref/source/libemx.h | 38 - crawl-ref/source/libmac.cc | 2116 ---------- crawl-ref/source/libmac.h | 86 - crawl-ref/source/libunix.cc | 221 +- crawl-ref/source/libunix.h | 7 +- crawl-ref/source/libutil.cc | 681 ++-- crawl-ref/source/libutil.h | 93 +- crawl-ref/source/libw32c.cc | 53 +- crawl-ref/source/libw32c.h | 5 + crawl-ref/source/lua/runrest.lua | 3 + crawl-ref/source/lua/safechnk.lua | 41 + crawl-ref/source/lua/safechunk.lua | 41 - crawl-ref/source/lua/stash.lua | 2 +- crawl-ref/source/lua/trapwalk.lua | 46 + crawl-ref/source/machdr.h | 185 - crawl-ref/source/macro.cc | 19 +- crawl-ref/source/macro.h | 2 + crawl-ref/source/makefile | 23 +- crawl-ref/source/makefile.bor | 53 - crawl-ref/source/makefile.bsd | 64 - crawl-ref/source/makefile.dos | 115 +- crawl-ref/source/makefile.emx | 53 - crawl-ref/source/makefile.lnx | 68 - crawl-ref/source/makefile.mgw | 151 +- crawl-ref/source/makefile.obj | 6 +- crawl-ref/source/makefile.osx | 83 +- crawl-ref/source/makefile.sgi | 54 - crawl-ref/source/makefile.sol | 68 - crawl-ref/source/makefile.unix | 198 + crawl-ref/source/mapdef.cc | 412 ++ crawl-ref/source/mapdef.h | 167 + crawl-ref/source/maps.cc | 3744 +---------------- crawl-ref/source/maps.h | 30 +- crawl-ref/source/menu.cc | 880 +++- crawl-ref/source/menu.h | 251 +- crawl-ref/source/message.cc | 122 +- crawl-ref/source/message.h | 14 + crawl-ref/source/misc.cc | 484 ++- crawl-ref/source/misc.h | 46 +- crawl-ref/source/misc/header | 2 + crawl-ref/source/misc/src-pkg-excludes.lst | 3 + crawl-ref/source/mon-data.h | 1807 ++++++--- crawl-ref/source/mon-pick.cc | 149 +- crawl-ref/source/mon-pick.h | 10 +- crawl-ref/source/mon-spll.h | 61 +- crawl-ref/source/mon-util.cc | 1000 +++-- crawl-ref/source/mon-util.h | 154 +- crawl-ref/source/monplace.cc | 406 +- crawl-ref/source/monplace.h | 24 +- crawl-ref/source/monspeak.cc | 12 +- crawl-ref/source/monstuff.cc | 1561 +++++--- crawl-ref/source/monstuff.h | 14 +- crawl-ref/source/mstuff2.cc | 562 ++- crawl-ref/source/mstuff2.h | 25 +- crawl-ref/source/mutation.cc | 99 +- crawl-ref/source/newgame.cc | 746 ++-- crawl-ref/source/notes.cc | 405 ++ crawl-ref/source/notes.h | 67 + crawl-ref/source/ouch.cc | 552 ++- crawl-ref/source/ouch.h | 2 + crawl-ref/source/output.cc | 46 +- crawl-ref/source/output.h | 2 + crawl-ref/source/overmap.cc | 55 +- crawl-ref/source/overmap.h | 2 + crawl-ref/source/player.cc | 963 +++-- crawl-ref/source/player.h | 46 +- crawl-ref/source/prebuilt/levcomp.lex.cc | 2522 ++++++++++++ crawl-ref/source/prebuilt/levcomp.tab.cc | 1907 +++++++++ crawl-ref/source/prebuilt/levcomp.tab.h | 125 + crawl-ref/source/randart.cc | 59 +- crawl-ref/source/religion.cc | 1090 +++-- crawl-ref/source/religion.h | 89 +- crawl-ref/source/shopping.cc | 280 +- crawl-ref/source/shopping.h | 2 + crawl-ref/source/skills.cc | 82 +- crawl-ref/source/skills.h | 4 +- crawl-ref/source/skills2.cc | 214 +- crawl-ref/source/skills2.h | 4 +- crawl-ref/source/spells1.cc | 286 +- crawl-ref/source/spells1.h | 14 +- crawl-ref/source/spells2.cc | 142 +- crawl-ref/source/spells2.h | 4 +- crawl-ref/source/spells3.cc | 106 +- crawl-ref/source/spells3.h | 10 +- crawl-ref/source/spells4.cc | 206 +- crawl-ref/source/spells4.h | 7 +- crawl-ref/source/spl-book.cc | 282 +- crawl-ref/source/spl-book.h | 4 + crawl-ref/source/spl-cast.cc | 622 +-- crawl-ref/source/spl-cast.h | 6 +- crawl-ref/source/spl-data.h | 224 +- crawl-ref/source/spl-util.cc | 62 +- crawl-ref/source/spl-util.h | 9 +- crawl-ref/source/stash.cc | 175 +- crawl-ref/source/stash.h | 4 - crawl-ref/source/stuff.cc | 477 ++- crawl-ref/source/stuff.h | 150 +- crawl-ref/source/tags.cc | 172 +- crawl-ref/source/tags.h | 4 +- crawl-ref/source/transfor.cc | 100 +- crawl-ref/source/transfor.h | 8 +- crawl-ref/source/travel.cc | 485 ++- crawl-ref/source/travel.h | 39 +- crawl-ref/source/unrand.h | 2 + crawl-ref/source/util/levcomp.cc | 18 + crawl-ref/source/util/levcomp.h | 12 + crawl-ref/source/util/levcomp.lpp | 155 + crawl-ref/source/util/levcomp.ypp | 226 ++ crawl-ref/source/version.h | 7 +- crawl-ref/source/view.cc | 4651 ++++++++++------------ crawl-ref/source/view.h | 43 +- crawl-ref/source/wpn-misc.cc | 268 -- crawl-ref/source/wpn-misc.h | 67 - 180 files changed, 39016 insertions(+), 25829 deletions(-) delete mode 100644 crawl-ref/source/MacString.cc delete mode 100644 crawl-ref/source/MacString.h create mode 100644 crawl-ref/source/dat/levdes.vim create mode 100644 crawl-ref/source/dat/splev.des create mode 100644 crawl-ref/source/dat/vaults.des create mode 100644 crawl-ref/source/itemprop.cc create mode 100644 crawl-ref/source/itemprop.h create mode 100644 crawl-ref/source/libdos.cc create mode 100644 crawl-ref/source/libdos.h delete mode 100644 crawl-ref/source/libemx.cc delete mode 100644 crawl-ref/source/libemx.h delete mode 100644 crawl-ref/source/libmac.cc delete mode 100644 crawl-ref/source/libmac.h create mode 100644 crawl-ref/source/lua/safechnk.lua delete mode 100644 crawl-ref/source/lua/safechunk.lua create mode 100644 crawl-ref/source/lua/trapwalk.lua delete mode 100644 crawl-ref/source/machdr.h delete mode 100644 crawl-ref/source/makefile.bor delete mode 100644 crawl-ref/source/makefile.bsd delete mode 100644 crawl-ref/source/makefile.emx delete mode 100644 crawl-ref/source/makefile.lnx delete mode 100644 crawl-ref/source/makefile.sgi delete mode 100644 crawl-ref/source/makefile.sol create mode 100644 crawl-ref/source/makefile.unix create mode 100644 crawl-ref/source/mapdef.cc create mode 100644 crawl-ref/source/mapdef.h create mode 100644 crawl-ref/source/misc/src-pkg-excludes.lst create mode 100644 crawl-ref/source/notes.cc create mode 100644 crawl-ref/source/notes.h create mode 100644 crawl-ref/source/prebuilt/levcomp.lex.cc create mode 100644 crawl-ref/source/prebuilt/levcomp.tab.cc create mode 100644 crawl-ref/source/prebuilt/levcomp.tab.h create mode 100644 crawl-ref/source/util/levcomp.cc create mode 100644 crawl-ref/source/util/levcomp.h create mode 100644 crawl-ref/source/util/levcomp.lpp create mode 100644 crawl-ref/source/util/levcomp.ypp delete mode 100644 crawl-ref/source/wpn-misc.cc delete mode 100644 crawl-ref/source/wpn-misc.h diff --git a/crawl-ref/source/AppHdr.h b/crawl-ref/source/AppHdr.h index 44c115b80a..602d351444 100644 --- a/crawl-ref/source/AppHdr.h +++ b/crawl-ref/source/AppHdr.h @@ -14,6 +14,8 @@ * * Copyright © 1999 Jesse Jones. * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <9> 9 Aug 2001 MV Added USE_RIVERS,USE_NEW_UNRANDS @@ -58,35 +60,26 @@ // // #define CLUA_BINDINGS -// ========================================================================= -// System Defines -// ========================================================================= -// Define plain_term for Unix and dos_term for DOS and EMX. - -#if defined(LINUX) - #define UNIX - #define USE_UNIX_SIGNALS -#elif defined(SOLARIS) - #define UNIX - #define USE_UNIX_SIGNALS -#elif defined(BSD) - #define UNIX -#elif defined(OSX) +// OS X's Terminal.app has color handling problems; dark grey is +// especially bad, so we'll want to remap that. OS X is otherwise +// Unix-ish, so we shouldn't need other special handling. +#if defined(OSX) #define UNIX - // Darkgrey is a particular problem in Terminal.app. #define USE_8_COLOUR_TERM_MAP #define COL_TO_REPLACE_DARKGREY BLUE -#elif defined(HPUX) - #define UNIX - #define USE_UNIX_SIGNALS - // Under HP-UX it's typically easier to use ncurses than try and - // get the colour curses library to work. -- bwr - #define CURSES_INCLUDE_FILE #endif +// ========================================================================= +// System Defines +// ========================================================================= +// Define plain_term for Unix and dos_term for DOS. + #ifdef UNIX #define PLAIN_TERM #define MULTIUSER + #define USE_UNIX_SIGNALS + + #define FILE_SEPARATOR '/' #define CHARACTER_SET 0 #define USE_ASCII_CHARACTERS @@ -143,30 +136,13 @@ #include "libunix.h" -// To compile with EMX for OS/2 define USE_EMX macro with compiler command line -// (already defined in supplied makefile.emx) -#elif defined(USE_EMX) - #define DOS_TERM - #define EOL "\n" - #define CHARACTER_SET A_ALTCHARSET - - #include - #include "libemx.h" - #elif _MSC_VER >= 1100 #include #include "WinHdr.h" #error MSVC is not supported yet #define CHARACTER_SET A_ALTCHARSET -// macintosh is predefined on all the common Mac compilers -#elif defined(macintosh) - #define PLAIN_TERM - #define HAS_NAMESPACES 1 - #define EOL "\r" - #define CHARACTER_SET A_ALTCHARSET - #include - #include "libmac.h" + #define FILE_SEPARATOR '/' #elif defined(DOS) #define DOS_TERM @@ -174,13 +150,20 @@ #define EOL "\r\n" #define CHARACTER_SET A_ALTCHARSET + #define FILE_SEPARATOR '\\' + #include + #include "libdos.h" #ifdef __DJGPP__ #define NEED_SNPRINTF + + // [dshaligram] This is distressing, but djgpp lacks (v)snprintf, and + // we have to support DOS. Ow. FIXME + #define vsnprintf(buf, size, format, args) vsprintf(buf, format, args) #endif -#elif defined(WIN32CONSOLE) && (defined(__IBMCPP__) || defined(__BCPLUSPLUS__) || defined(__MINGW32__)) +#elif defined(WIN32CONSOLE) && (defined(__IBMCPP__) || defined(__MINGW32__)) #include "libw32c.h" #define PLAIN_TERM #define SHORT_FILE_NAMES @@ -188,6 +171,9 @@ #define CHARACTER_SET A_ALTCHARSET #define getstr(X,Y) getConsoleString(X,Y) + // NT and better are happy with /; I'm not sure how 9x reacts. + #define FILE_SEPARATOR '/' + // Uncomment to play sounds. winmm must be linked in if this is uncommented. // #define WINMM_PLAY_SOUNDS @@ -271,9 +257,6 @@ //jmf: New defines for a bunch of optional features. // ================================================= -------------------------- -// New silence code -- seems to actually work! Use it! -#define USE_SILENCE_CODE - // Use special colours for various channels of messages #define USE_COLOUR_MESSAGES @@ -300,7 +283,7 @@ // mv: (new 9 Aug 01) turns off missile trails, might be slow on some computers // #define MISSILE_TRAILS_OFF -// bwr: allow player to destroy items in inventory (but not equiped items) +// bwr: allow player to destroy items in inventory (but not equipped items) // See comment at items.cc::cmd_destroy_item() for details/issues. // #define ALLOW_DESTROY_ITEM_COMMAND @@ -315,7 +298,7 @@ #ifdef MULTIUSER // Define SAVE_DIR to the directory where saves, bones, and score file - // will go... end it with a '\'. Since all player files will be in the + // will go... end it with a '/'. Since all player files will be in the // same directory, the players UID will be appended when this option // is set. // @@ -323,7 +306,13 @@ // be dumped in the current directory. // // #define SAVE_DIR_PATH "/opt/crawl/lib/" - #define SAVE_DIR_PATH "" + // #define SAVE_DIR_PATH "" + + // Define DATA_DIR_PATH to the directory where level-description (.des) + // files are stored. NOTE: If you're installing Crawl for a real multiuser + // system, you MUST do this. The directory must exist on the filesystem. + + // #define DATA_DIR_PATH "/opt/crawl" // will make this little thing go away. Define SAVE_PACKAGE_CMD // to a command to compress and bundle the save game files into a @@ -334,14 +323,19 @@ // PACKAGE_SUFFIX is used when the package file name is needed // // Comment these lines out if you want to leave the save files uncompressed. - // - // #define SAVE_PACKAGE_CMD "/usr/bin/zip -m -q -j -1 %s.zip %s.*" - // #define LOAD_UNPACKAGE_CMD "/usr/bin/unzip -q -o %s.zip -d" SAVE_DIR_PATH - // #define PACKAGE_SUFFIX ".zip" + #define SAVE_PACKAGE_CMD "/usr/bin/zip -m -q -j -1 %s.zip %s.*" +#ifdef SAVE_DIR_PATH + #define LOAD_UNPACKAGE_CMD "/usr/bin/unzip -q -o %s.zip -d" SAVE_DIR_PATH +#else + #define LOAD_UNPACKAGE_CMD "/usr/bin/unzip -q -o %s.zip" +#endif + +#ifdef SAVE_PACKAGE_CMD + // This is used to unpack specific files from the archive. + #define UNPACK_SPECIFIC_FILE_CMD LOAD_UNPACKAGE_CMD " %s" +#endif - // This provides some rudimentary protection against people using - // save file cheats on multi-user systems. - #define DO_ANTICHEAT_CHECKS + #define PACKAGE_SUFFIX ".zip" // This defines the chmod permissions for score and bones files. #define SHARED_FILES_CHMOD_PRIVATE 0664 diff --git a/crawl-ref/source/Crawl.xcodeproj/project.pbxproj b/crawl-ref/source/Crawl.xcodeproj/project.pbxproj index 418dde20a2..4ae4c17819 100644 --- a/crawl-ref/source/Crawl.xcodeproj/project.pbxproj +++ b/crawl-ref/source/Crawl.xcodeproj/project.pbxproj @@ -51,8 +51,6 @@ 7B237E950A8EC9D000580F30 /* files.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B237E0E0A8EC9D000580F30 /* files.cc */; }; 7B237E960A8EC9D000580F30 /* direct.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B237E0F0A8EC9D000580F30 /* direct.cc */; }; 7B237E970A8EC9D000580F30 /* dungeon.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7B237E100A8EC9D000580F30 /* dungeon.h */; }; - 7B237E980A8EC9D000580F30 /* wpn-misc.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7B237E110A8EC9D000580F30 /* wpn-misc.h */; }; - 7B237E990A8EC9D000580F30 /* wpn-misc.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B237E120A8EC9D000580F30 /* wpn-misc.cc */; }; 7B237E9A0A8EC9D000580F30 /* view.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7B237E130A8EC9D000580F30 /* view.h */; }; 7B237E9B0A8EC9D000580F30 /* view.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B237E140A8EC9D000580F30 /* view.cc */; }; 7B237E9C0A8EC9D000580F30 /* spl-book.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B237E150A8EC9D000580F30 /* spl-book.cc */; }; @@ -143,6 +141,15 @@ 7B237EF10A8EC9D000580F30 /* mt19937ar.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B237E6A0A8EC9D000580F30 /* mt19937ar.cc */; }; 7B237EF20A8EC9D000580F30 /* mstuff2.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7B237E6B0A8EC9D000580F30 /* mstuff2.h */; }; 7B237F150A8ECD2E00580F30 /* libncurses.5.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B237F140A8ECD2E00580F30 /* libncurses.5.dylib */; }; + 7B352EA00B00183400CABB32 /* mapdef.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B352E9D0B00183400CABB32 /* mapdef.cc */; }; + 7B352EA10B00183400CABB32 /* mapdef.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7B352E9E0B00183400CABB32 /* mapdef.h */; }; + 7BBC4A060B0F782800F27D45 /* levcomp.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B352EEC0B001F4200CABB32 /* levcomp.cc */; }; + 7BBC4A070B0F783C00F27D45 /* levcomp.lpp in Sources */ = {isa = PBXBuildFile; fileRef = 7B352ED10B001B9E00CABB32 /* levcomp.lpp */; }; + 7BBC4A080B0F783C00F27D45 /* levcomp.ypp in Sources */ = {isa = PBXBuildFile; fileRef = 7B352ED20B001B9E00CABB32 /* levcomp.ypp */; }; + 7BC222E70ABBB286003A7D9A /* itemprop.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BC222E50ABBB286003A7D9A /* itemprop.cc */; }; + 7BC222E80ABBB286003A7D9A /* itemprop.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7BC222E60ABBB286003A7D9A /* itemprop.h */; }; + 7BD75A350AC214A200B74F6E /* notes.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BD75A330AC214A200B74F6E /* notes.cc */; }; + 7BD75A360AC214A200B74F6E /* notes.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7BD75A340AC214A200B74F6E /* notes.h */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -175,7 +182,6 @@ 7B237E910A8EC9D000580F30 /* player.h in CopyFiles */, 7B237E930A8EC9D000580F30 /* effects.h in CopyFiles */, 7B237E970A8EC9D000580F30 /* dungeon.h in CopyFiles */, - 7B237E980A8EC9D000580F30 /* wpn-misc.h in CopyFiles */, 7B237E9A0A8EC9D000580F30 /* view.h in CopyFiles */, 7B237E9D0A8EC9D000580F30 /* spells4.h in CopyFiles */, 7B237E9F0A8EC9D000580F30 /* spells3.h in CopyFiles */, @@ -219,6 +225,9 @@ 7B237EEC0A8EC9D000580F30 /* abyss.h in CopyFiles */, 7B237EF00A8EC9D000580F30 /* mt19937ar.h in CopyFiles */, 7B237EF20A8EC9D000580F30 /* mstuff2.h in CopyFiles */, + 7BC222E80ABBB286003A7D9A /* itemprop.h in CopyFiles */, + 7BD75A360AC214A200B74F6E /* notes.h in CopyFiles */, + 7B352EA10B00183400CABB32 /* mapdef.h in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; @@ -269,8 +278,6 @@ 7B237E0E0A8EC9D000580F30 /* files.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = files.cc; sourceTree = ""; }; 7B237E0F0A8EC9D000580F30 /* direct.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = direct.cc; sourceTree = ""; }; 7B237E100A8EC9D000580F30 /* dungeon.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dungeon.h; sourceTree = ""; }; - 7B237E110A8EC9D000580F30 /* wpn-misc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "wpn-misc.h"; sourceTree = ""; }; - 7B237E120A8EC9D000580F30 /* wpn-misc.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = "wpn-misc.cc"; sourceTree = ""; }; 7B237E130A8EC9D000580F30 /* view.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = view.h; sourceTree = ""; }; 7B237E140A8EC9D000580F30 /* view.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = view.cc; sourceTree = ""; }; 7B237E150A8EC9D000580F30 /* spl-book.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = "spl-book.cc"; sourceTree = ""; }; @@ -361,6 +368,17 @@ 7B237E6A0A8EC9D000580F30 /* mt19937ar.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = mt19937ar.cc; sourceTree = ""; }; 7B237E6B0A8EC9D000580F30 /* mstuff2.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = mstuff2.h; sourceTree = ""; }; 7B237F140A8ECD2E00580F30 /* libncurses.5.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libncurses.5.dylib; path = /usr/lib/libncurses.5.dylib; sourceTree = ""; }; + 7B352E9D0B00183400CABB32 /* mapdef.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = mapdef.cc; sourceTree = ""; }; + 7B352E9E0B00183400CABB32 /* mapdef.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = mapdef.h; sourceTree = ""; }; + 7B352ED10B001B9E00CABB32 /* levcomp.lpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.lex; name = levcomp.lpp; path = util/levcomp.lpp; sourceTree = ""; }; + 7B352ED20B001B9E00CABB32 /* levcomp.ypp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.yacc; name = levcomp.ypp; path = util/levcomp.ypp; sourceTree = ""; }; + 7B352EEC0B001F4200CABB32 /* levcomp.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = levcomp.cc; path = util/levcomp.cc; sourceTree = ""; }; + 7B352F1F0B00232500CABB32 /* splev.des */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = splev.des; path = dat/splev.des; sourceTree = ""; }; + 7B352F200B00232500CABB32 /* vaults.des */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = vaults.des; path = dat/vaults.des; sourceTree = ""; }; + 7BC222E50ABBB286003A7D9A /* itemprop.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = itemprop.cc; sourceTree = ""; }; + 7BC222E60ABBB286003A7D9A /* itemprop.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = itemprop.h; sourceTree = ""; }; + 7BD75A330AC214A200B74F6E /* notes.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = notes.cc; sourceTree = ""; }; + 7BD75A340AC214A200B74F6E /* notes.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = notes.h; sourceTree = ""; }; 8DD76FB20486AB0100D96B5E /* crawl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = crawl; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -380,6 +398,7 @@ isa = PBXGroup; children = ( 08FB7795FE84155DC02AAC07 /* Source */, + 7B352F1B0B0022C900CABB32 /* Resources */, 7B237F120A8ECCDE00580F30 /* Libraries */, C6A0FF2B0290797F04C91782 /* Documentation */, 1AB674ADFE9D54B511CA2CBB /* Products */, @@ -390,159 +409,207 @@ 08FB7795FE84155DC02AAC07 /* Source */ = { isa = PBXGroup; children = ( - 7B237DE50A8EC9D000580F30 /* maps.h */, - 7B237DE60A8EC9D000580F30 /* maps.cc */, - 7B237DE70A8EC9D000580F30 /* mon-pick.h */, - 7B237DE80A8EC9D000580F30 /* mon-pick.cc */, - 7B237DE90A8EC9D000580F30 /* misc.h */, - 7B237DEA0A8EC9D000580F30 /* insult.h */, - 7B237DEB0A8EC9D000580F30 /* insult.cc */, - 7B237DEC0A8EC9D000580F30 /* initfile.h */, - 7B237DED0A8EC9D000580F30 /* overmap.h */, - 7B237DEE0A8EC9D000580F30 /* overmap.cc */, - 7B237DEF0A8EC9D000580F30 /* output.h */, - 7B237DF00A8EC9D000580F30 /* item_use.h */, - 7B237DF10A8EC9D000580F30 /* initfile.cc */, - 7B237DF20A8EC9D000580F30 /* hiscores.h */, - 7B237DF30A8EC9D000580F30 /* hiscores.cc */, - 7B237DF40A8EC9D000580F30 /* travel.h */, - 7B237DF50A8EC9D000580F30 /* travel.cc */, - 7B237DF60A8EC9D000580F30 /* lev-pand.h */, - 7B237DF70A8EC9D000580F30 /* lev-pand.cc */, - 7B237DF80A8EC9D000580F30 /* items.h */, - 7B237DF90A8EC9D000580F30 /* newgame.cc */, - 7B237DFA0A8EC9D000580F30 /* mutation.h */, - 7B237DFB0A8EC9D000580F30 /* mutation.cc */, - 7B237DFC0A8EC9D000580F30 /* misc.cc */, - 7B237DFD0A8EC9D000580F30 /* message.h */, - 7B237DFE0A8EC9D000580F30 /* mon-util.h */, - 7B237DFF0A8EC9D000580F30 /* mon-util.cc */, - 7B237E000A8EC9D000580F30 /* it_use3.h */, - 7B237E010A8EC9D000580F30 /* it_use3.cc */, - 7B237E020A8EC9D000580F30 /* it_use2.h */, - 7B237E030A8EC9D000580F30 /* itemname.cc */, - 7B237E040A8EC9D000580F30 /* command.cc */, - 7B237E050A8EC9D000580F30 /* describe.h */, - 7B237E060A8EC9D000580F30 /* spl-util.h */, - 7B237E070A8EC9D000580F30 /* spl-util.cc */, - 7B237E080A8EC9D000580F30 /* randart.h */, - 7B237E090A8EC9D000580F30 /* randart.cc */, - 7B237E0A0A8EC9D000580F30 /* player.h */, - 7B237E0B0A8EC9D000580F30 /* player.cc */, - 7B237E0C0A8EC9D000580F30 /* effects.h */, + 7B352EF00B001F5B00CABB32 /* Crawl */, + 7B352E950B0017CF00CABB32 /* Levcomp */, + 7B352EF30B001FA700CABB32 /* Shared */, + ); + name = Source; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76FB20486AB0100D96B5E /* crawl */, + ); + name = Products; + sourceTree = ""; + }; + 7B237F120A8ECCDE00580F30 /* Libraries */ = { + isa = PBXGroup; + children = ( + 7B237F140A8ECD2E00580F30 /* libncurses.5.dylib */, + ); + name = Libraries; + sourceTree = ""; + }; + 7B352E950B0017CF00CABB32 /* Levcomp */ = { + isa = PBXGroup; + children = ( + 7B352EEC0B001F4200CABB32 /* levcomp.cc */, + 7B352ED10B001B9E00CABB32 /* levcomp.lpp */, + 7B352ED20B001B9E00CABB32 /* levcomp.ypp */, + ); + name = Levcomp; + sourceTree = ""; + }; + 7B352EF00B001F5B00CABB32 /* Crawl */ = { + isa = PBXGroup; + children = ( 7B237E0D0A8EC9D000580F30 /* abl-show.cc */, - 7B237E0E0A8EC9D000580F30 /* files.cc */, - 7B237E0F0A8EC9D000580F30 /* direct.cc */, - 7B237E100A8EC9D000580F30 /* dungeon.h */, - 7B237E110A8EC9D000580F30 /* wpn-misc.h */, - 7B237E120A8EC9D000580F30 /* wpn-misc.cc */, - 7B237E130A8EC9D000580F30 /* view.h */, - 7B237E140A8EC9D000580F30 /* view.cc */, - 7B237E150A8EC9D000580F30 /* spl-book.cc */, - 7B237E160A8EC9D000580F30 /* spells4.h */, - 7B237E170A8EC9D000580F30 /* spells4.cc */, - 7B237E180A8EC9D000580F30 /* spells3.h */, - 7B237E190A8EC9D000580F30 /* macro.h */, - 7B237E1A0A8EC9D000580F30 /* debug.h */, - 7B237E1B0A8EC9D000580F30 /* direct.h */, - 7B237E1C0A8EC9D000580F30 /* decks.h */, - 7B237E1D0A8EC9D000580F30 /* stuff.h */, - 7B237E1E0A8EC9D000580F30 /* stuff.cc */, - 7B237E1F0A8EC9D000580F30 /* stash.h */, - 7B237E200A8EC9D000580F30 /* stash.cc */, - 7B237E210A8EC9D000580F30 /* transfor.h */, - 7B237E220A8EC9D000580F30 /* transfor.cc */, - 7B237E230A8EC9D000580F30 /* tags.h */, - 7B237E240A8EC9D000580F30 /* tags.cc */, - 7B237E250A8EC9D000580F30 /* libutil.h */, - 7B237E260A8EC9D000580F30 /* item_use.cc */, - 7B237E270A8EC9D000580F30 /* acr.cc */, - 7B237E280A8EC9D000580F30 /* abyss.cc */, 7B237E290A8EC9D000580F30 /* abl-show.h */, - 7B237E2A0A8EC9D000580F30 /* chardump.cc */, - 7B237E2B0A8EC9D000580F30 /* chardump.h */, + 7B237E280A8EC9D000580F30 /* abyss.cc */, + 7B237E650A8EC9D000580F30 /* abyss.h */, + 7B237E270A8EC9D000580F30 /* acr.cc */, 7B237E2C0A8EC9D000580F30 /* beam.cc */, 7B237E2D0A8EC9D000580F30 /* beam.h */, - 7B237E2E0A8EC9D000580F30 /* spl-cast.h */, - 7B237E2F0A8EC9D000580F30 /* spl-cast.cc */, - 7B237E300A8EC9D000580F30 /* spl-book.h */, - 7B237E310A8EC9D000580F30 /* output.cc */, - 7B237E320A8EC9D000580F30 /* ouch.h */, - 7B237E330A8EC9D000580F30 /* ouch.cc */, - 7B237E340A8EC9D000580F30 /* newgame.h */, - 7B237E350A8EC9D000580F30 /* message.cc */, - 7B237E360A8EC9D000580F30 /* menu.h */, - 7B237E370A8EC9D000580F30 /* menu.cc */, - 7B237E380A8EC9D000580F30 /* macro.cc */, - 7B237E390A8EC9D000580F30 /* shopping.cc */, - 7B237E3A0A8EC9D000580F30 /* religion.h */, - 7B237E3B0A8EC9D000580F30 /* religion.cc */, - 7B237E3C0A8EC9D000580F30 /* spells3.cc */, - 7B237E3D0A8EC9D000580F30 /* spells2.h */, - 7B237E3E0A8EC9D000580F30 /* spells2.cc */, - 7B237E3F0A8EC9D000580F30 /* mstuff2.cc */, - 7B237E400A8EC9D000580F30 /* monstuff.h */, - 7B237E410A8EC9D000580F30 /* monstuff.cc */, - 7B237E420A8EC9D000580F30 /* monspeak.h */, - 7B237E430A8EC9D000580F30 /* delay.h */, + 7B237E2A0A8EC9D000580F30 /* chardump.cc */, + 7B237E2B0A8EC9D000580F30 /* chardump.h */, + 7B237E590A8EC9D000580F30 /* cloud.cc */, + 7B237E580A8EC9D000580F30 /* cloud.h */, + 7B237E570A8EC9D000580F30 /* clua.cc */, + 7B237E560A8EC9D000580F30 /* clua.h */, + 7B237E040A8EC9D000580F30 /* command.cc */, 7B237E440A8EC9D000580F30 /* command.h */, - 7B237E450A8EC9D000580F30 /* spells1.h */, - 7B237E460A8EC9D000580F30 /* spells1.cc */, - 7B237E470A8EC9D000580F30 /* skills2.h */, - 7B237E480A8EC9D000580F30 /* skills2.cc */, + 7B237E680A8EC9D000580F30 /* debug.cc */, + 7B237E1A0A8EC9D000580F30 /* debug.h */, 7B237E490A8EC9D000580F30 /* decks.cc */, + 7B237E1C0A8EC9D000580F30 /* decks.h */, + 7B237E660A8EC9D000580F30 /* delay.cc */, + 7B237E430A8EC9D000580F30 /* delay.h */, 7B237E4A0A8EC9D000580F30 /* describe.cc */, - 7B237E4B0A8EC9D000580F30 /* monspeak.cc */, - 7B237E4C0A8EC9D000580F30 /* monplace.h */, - 7B237E4D0A8EC9D000580F30 /* monplace.cc */, - 7B237E4E0A8EC9D000580F30 /* Kills.h */, - 7B237E4F0A8EC9D000580F30 /* Kills.cc */, - 7B237E500A8EC9D000580F30 /* files.h */, - 7B237E510A8EC9D000580F30 /* fight.h */, + 7B237E050A8EC9D000580F30 /* describe.h */, + 7B237E0F0A8EC9D000580F30 /* direct.cc */, + 7B237E1B0A8EC9D000580F30 /* direct.h */, + 7B237E670A8EC9D000580F30 /* dungeon.cc */, + 7B237E100A8EC9D000580F30 /* dungeon.h */, 7B237E520A8EC9D000580F30 /* effects.cc */, + 7B237E0C0A8EC9D000580F30 /* effects.h */, 7B237E530A8EC9D000580F30 /* fight.cc */, + 7B237E510A8EC9D000580F30 /* fight.h */, + 7B237E0E0A8EC9D000580F30 /* files.cc */, + 7B237E500A8EC9D000580F30 /* files.h */, 7B237E540A8EC9D000580F30 /* food.cc */, 7B237E550A8EC9D000580F30 /* food.h */, - 7B237E560A8EC9D000580F30 /* clua.h */, - 7B237E570A8EC9D000580F30 /* clua.cc */, - 7B237E580A8EC9D000580F30 /* cloud.h */, - 7B237E590A8EC9D000580F30 /* cloud.cc */, - 7B237E5A0A8EC9D000580F30 /* items.cc */, - 7B237E5B0A8EC9D000580F30 /* itemname.h */, - 7B237E5C0A8EC9D000580F30 /* skills.h */, - 7B237E5D0A8EC9D000580F30 /* skills.cc */, - 7B237E5E0A8EC9D000580F30 /* shopping.h */, - 7B237E5F0A8EC9D000580F30 /* it_use2.cc */, - 7B237E600A8EC9D000580F30 /* invent.h */, + 7B237DF30A8EC9D000580F30 /* hiscores.cc */, + 7B237DF20A8EC9D000580F30 /* hiscores.h */, + 7B237DF10A8EC9D000580F30 /* initfile.cc */, + 7B237DEC0A8EC9D000580F30 /* initfile.h */, + 7B237DEB0A8EC9D000580F30 /* insult.cc */, + 7B237DEA0A8EC9D000580F30 /* insult.h */, 7B237E610A8EC9D000580F30 /* invent.cc */, - 7B237E620A8EC9D000580F30 /* libutil.cc */, - 7B237E630A8EC9D000580F30 /* libunix.h */, + 7B237E600A8EC9D000580F30 /* invent.h */, + 7B237E5F0A8EC9D000580F30 /* it_use2.cc */, + 7B237E020A8EC9D000580F30 /* it_use2.h */, + 7B237E010A8EC9D000580F30 /* it_use3.cc */, + 7B237E000A8EC9D000580F30 /* it_use3.h */, + 7B237E260A8EC9D000580F30 /* item_use.cc */, + 7B237DF00A8EC9D000580F30 /* item_use.h */, + 7B237E030A8EC9D000580F30 /* itemname.cc */, + 7B237E5B0A8EC9D000580F30 /* itemname.h */, + 7BC222E50ABBB286003A7D9A /* itemprop.cc */, + 7BC222E60ABBB286003A7D9A /* itemprop.h */, + 7B237E5A0A8EC9D000580F30 /* items.cc */, + 7B237DF80A8EC9D000580F30 /* items.h */, + 7B237E4F0A8EC9D000580F30 /* Kills.cc */, + 7B237E4E0A8EC9D000580F30 /* Kills.h */, + 7B237DF70A8EC9D000580F30 /* lev-pand.cc */, + 7B237DF60A8EC9D000580F30 /* lev-pand.h */, 7B237E640A8EC9D000580F30 /* libunix.cc */, - 7B237E650A8EC9D000580F30 /* abyss.h */, - 7B237E660A8EC9D000580F30 /* delay.cc */, - 7B237E670A8EC9D000580F30 /* dungeon.cc */, - 7B237E680A8EC9D000580F30 /* debug.cc */, - 7B237E690A8EC9D000580F30 /* mt19937ar.h */, - 7B237E6A0A8EC9D000580F30 /* mt19937ar.cc */, + 7B237E630A8EC9D000580F30 /* libunix.h */, + 7B237E620A8EC9D000580F30 /* libutil.cc */, + 7B237E250A8EC9D000580F30 /* libutil.h */, + 7B237E380A8EC9D000580F30 /* macro.cc */, + 7B237E190A8EC9D000580F30 /* macro.h */, + 7B237DE60A8EC9D000580F30 /* maps.cc */, + 7B237DE50A8EC9D000580F30 /* maps.h */, + 7B237E370A8EC9D000580F30 /* menu.cc */, + 7B237E360A8EC9D000580F30 /* menu.h */, + 7B237E350A8EC9D000580F30 /* message.cc */, + 7B237DFD0A8EC9D000580F30 /* message.h */, + 7B237DFC0A8EC9D000580F30 /* misc.cc */, + 7B237DE90A8EC9D000580F30 /* misc.h */, + 7B237DE80A8EC9D000580F30 /* mon-pick.cc */, + 7B237DE70A8EC9D000580F30 /* mon-pick.h */, + 7B237DFF0A8EC9D000580F30 /* mon-util.cc */, + 7B237DFE0A8EC9D000580F30 /* mon-util.h */, + 7B237E4D0A8EC9D000580F30 /* monplace.cc */, + 7B237E4C0A8EC9D000580F30 /* monplace.h */, + 7B237E4B0A8EC9D000580F30 /* monspeak.cc */, + 7B237E420A8EC9D000580F30 /* monspeak.h */, + 7B237E410A8EC9D000580F30 /* monstuff.cc */, + 7B237E400A8EC9D000580F30 /* monstuff.h */, + 7B237E3F0A8EC9D000580F30 /* mstuff2.cc */, 7B237E6B0A8EC9D000580F30 /* mstuff2.h */, + 7B237E6A0A8EC9D000580F30 /* mt19937ar.cc */, + 7B237E690A8EC9D000580F30 /* mt19937ar.h */, + 7B237DFB0A8EC9D000580F30 /* mutation.cc */, + 7B237DFA0A8EC9D000580F30 /* mutation.h */, + 7B237DF90A8EC9D000580F30 /* newgame.cc */, + 7B237E340A8EC9D000580F30 /* newgame.h */, + 7BD75A330AC214A200B74F6E /* notes.cc */, + 7BD75A340AC214A200B74F6E /* notes.h */, + 7B237E330A8EC9D000580F30 /* ouch.cc */, + 7B237E320A8EC9D000580F30 /* ouch.h */, + 7B237E310A8EC9D000580F30 /* output.cc */, + 7B237DEF0A8EC9D000580F30 /* output.h */, + 7B237DEE0A8EC9D000580F30 /* overmap.cc */, + 7B237DED0A8EC9D000580F30 /* overmap.h */, + 7B237E0B0A8EC9D000580F30 /* player.cc */, + 7B237E0A0A8EC9D000580F30 /* player.h */, + 7B237E090A8EC9D000580F30 /* randart.cc */, + 7B237E080A8EC9D000580F30 /* randart.h */, + 7B237E3B0A8EC9D000580F30 /* religion.cc */, + 7B237E3A0A8EC9D000580F30 /* religion.h */, + 7B237E390A8EC9D000580F30 /* shopping.cc */, + 7B237E5E0A8EC9D000580F30 /* shopping.h */, + 7B237E5D0A8EC9D000580F30 /* skills.cc */, + 7B237E5C0A8EC9D000580F30 /* skills.h */, + 7B237E480A8EC9D000580F30 /* skills2.cc */, + 7B237E470A8EC9D000580F30 /* skills2.h */, + 7B237E460A8EC9D000580F30 /* spells1.cc */, + 7B237E450A8EC9D000580F30 /* spells1.h */, + 7B237E3E0A8EC9D000580F30 /* spells2.cc */, + 7B237E3D0A8EC9D000580F30 /* spells2.h */, + 7B237E3C0A8EC9D000580F30 /* spells3.cc */, + 7B237E180A8EC9D000580F30 /* spells3.h */, + 7B237E170A8EC9D000580F30 /* spells4.cc */, + 7B237E160A8EC9D000580F30 /* spells4.h */, + 7B237E150A8EC9D000580F30 /* spl-book.cc */, + 7B237E300A8EC9D000580F30 /* spl-book.h */, + 7B237E2F0A8EC9D000580F30 /* spl-cast.cc */, + 7B237E2E0A8EC9D000580F30 /* spl-cast.h */, + 7B237E070A8EC9D000580F30 /* spl-util.cc */, + 7B237E060A8EC9D000580F30 /* spl-util.h */, + 7B237E200A8EC9D000580F30 /* stash.cc */, + 7B237E1F0A8EC9D000580F30 /* stash.h */, + 7B237E1E0A8EC9D000580F30 /* stuff.cc */, + 7B237E1D0A8EC9D000580F30 /* stuff.h */, + 7B237E240A8EC9D000580F30 /* tags.cc */, + 7B237E230A8EC9D000580F30 /* tags.h */, + 7B237E220A8EC9D000580F30 /* transfor.cc */, + 7B237E210A8EC9D000580F30 /* transfor.h */, + 7B237DF50A8EC9D000580F30 /* travel.cc */, + 7B237DF40A8EC9D000580F30 /* travel.h */, + 7B237E140A8EC9D000580F30 /* view.cc */, + 7B237E130A8EC9D000580F30 /* view.h */, ); - name = Source; + name = Crawl; sourceTree = ""; }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { + 7B352EF30B001FA700CABB32 /* Shared */ = { isa = PBXGroup; children = ( - 8DD76FB20486AB0100D96B5E /* crawl */, + 7B352E9D0B00183400CABB32 /* mapdef.cc */, + 7B352E9E0B00183400CABB32 /* mapdef.h */, ); - name = Products; + name = Shared; sourceTree = ""; }; - 7B237F120A8ECCDE00580F30 /* Libraries */ = { + 7B352F1B0B0022C900CABB32 /* Resources */ = { isa = PBXGroup; children = ( - 7B237F140A8ECD2E00580F30 /* libncurses.5.dylib */, + 7B352F1E0B0022E100CABB32 /* Levels */, ); - name = Libraries; + name = Resources; + sourceTree = ""; + }; + 7B352F1E0B0022E100CABB32 /* Levels */ = { + isa = PBXGroup; + children = ( + 7B352F1F0B00232500CABB32 /* splev.des */, + 7B352F200B00232500CABB32 /* vaults.des */, + ); + name = Levels; sourceTree = ""; }; C6A0FF2B0290797F04C91782 /* Documentation */ = { @@ -593,6 +660,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7BBC4A080B0F783C00F27D45 /* levcomp.ypp in Sources */, + 7BBC4A070B0F783C00F27D45 /* levcomp.lpp in Sources */, 7B237E6D0A8EC9D000580F30 /* maps.cc in Sources */, 7B237E6F0A8EC9D000580F30 /* mon-pick.cc in Sources */, 7B237E720A8EC9D000580F30 /* insult.cc in Sources */, @@ -600,6 +669,7 @@ 7B237E780A8EC9D000580F30 /* initfile.cc in Sources */, 7B237E7A0A8EC9D000580F30 /* hiscores.cc in Sources */, 7B237E7C0A8EC9D000580F30 /* travel.cc in Sources */, + 7BBC4A060B0F782800F27D45 /* levcomp.cc in Sources */, 7B237E7E0A8EC9D000580F30 /* lev-pand.cc in Sources */, 7B237E800A8EC9D000580F30 /* newgame.cc in Sources */, 7B237E820A8EC9D000580F30 /* mutation.cc in Sources */, @@ -614,7 +684,6 @@ 7B237E940A8EC9D000580F30 /* abl-show.cc in Sources */, 7B237E950A8EC9D000580F30 /* files.cc in Sources */, 7B237E960A8EC9D000580F30 /* direct.cc in Sources */, - 7B237E990A8EC9D000580F30 /* wpn-misc.cc in Sources */, 7B237E9B0A8EC9D000580F30 /* view.cc in Sources */, 7B237E9C0A8EC9D000580F30 /* spl-book.cc in Sources */, 7B237E9E0A8EC9D000580F30 /* spells4.cc in Sources */, @@ -661,6 +730,9 @@ 7B237EEE0A8EC9D000580F30 /* dungeon.cc in Sources */, 7B237EEF0A8EC9D000580F30 /* debug.cc in Sources */, 7B237EF10A8EC9D000580F30 /* mt19937ar.cc in Sources */, + 7BC222E70ABBB286003A7D9A /* itemprop.cc in Sources */, + 7BD75A350AC214A200B74F6E /* notes.cc in Sources */, + 7B352EA00B00183400CABB32 /* mapdef.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -674,7 +746,10 @@ GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_OPTIMIZATION_LEVEL = 1; GCC_PREPROCESSOR_DEFINITIONS = OSX; + GCC_WARN_UNUSED_FUNCTION = NO; + GCC_WARN_UNUSED_LABEL = NO; PRODUCT_NAME = crawl; + YACC_GENERATED_FILE_STEM = InputFileStem; ZERO_LINK = YES; }; name = Development; @@ -688,17 +763,22 @@ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; GCC_WARN_PEDANTIC = YES; - GCC_WARN_SHADOW = NO; + GCC_WARN_SHADOW = YES; GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; + MACOSX_DEPLOYMENT_TARGET_ppc = 10.3; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; @@ -717,7 +797,10 @@ DEBUG_ITEM_SCAN, FULLDEBUG, ); + GCC_WARN_UNUSED_FUNCTION = NO; + GCC_WARN_UNUSED_LABEL = NO; PRODUCT_NAME = crawl; + YACC_GENERATED_FILE_STEM = InputFileStem; ZERO_LINK = YES; }; name = Debug; @@ -736,9 +819,23 @@ GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_PEDANTIC = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; + MACOSX_DEPLOYMENT_TARGET_ppc = 10.3; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; @@ -756,7 +853,10 @@ DEBUG, DEBUG_ITEM_SCAN, ); + GCC_WARN_UNUSED_FUNCTION = NO; + GCC_WARN_UNUSED_LABEL = NO; PRODUCT_NAME = crawl; + YACC_GENERATED_FILE_STEM = InputFileStem; ZERO_LINK = YES; }; name = Wizard; @@ -774,9 +874,23 @@ GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_PEDANTIC = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; + MACOSX_DEPLOYMENT_TARGET_ppc = 10.3; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; @@ -793,7 +907,10 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = 1; GCC_PREPROCESSOR_DEFINITIONS = OSX; + GCC_WARN_UNUSED_FUNCTION = NO; + GCC_WARN_UNUSED_LABEL = NO; PRODUCT_NAME = crawl; + YACC_GENERATED_FILE_STEM = InputFileStem; ZERO_LINK = NO; }; name = Release; @@ -810,9 +927,23 @@ GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_PEDANTIC = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; + MACOSX_DEPLOYMENT_TARGET_ppc = 10.3; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; diff --git a/crawl-ref/source/FixAry.h b/crawl-ref/source/FixAry.h index 99f802e2f3..4270949150 100644 --- a/crawl-ref/source/FixAry.h +++ b/crawl-ref/source/FixAry.h @@ -3,6 +3,8 @@ * Summary: Fixed size 2D vector class that asserts if you do something bad. * Written by: Jesse Jones * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> 6/16/00 JDJ Created diff --git a/crawl-ref/source/FixVec.h b/crawl-ref/source/FixVec.h index b0afb34e82..176ced5cf4 100644 --- a/crawl-ref/source/FixVec.h +++ b/crawl-ref/source/FixVec.h @@ -12,7 +12,8 @@ #ifndef FIXVEC_H #define FIXVEC_H -#include +#include +#include #include "debug.h" @@ -39,16 +40,6 @@ public: typedef TYPE* iterator; typedef const TYPE* const_iterator; -#if 0 -#if MSVC >= 1100 - typedef std::reverse_iterator const_reverse_iterator; - typedef std::reverse_iterator reverse_iterator; -#else - typedef std::reverse_iterator const_reverse_iterator; - typedef std::reverse_iterator reverse_iterator; -#endif -#endif - //----------------------------------- // Initialization/Destruction // @@ -57,6 +48,10 @@ public: FixedVector() {} + FixedVector(TYPE def) : mData() { + init(def); + } + FixedVector(TYPE value0, TYPE value1, ...); // Allows for something resembling C array initialization, eg // instead of "int a[3] = {0, 1, 2}" you'd use "FixedVector @@ -71,35 +66,31 @@ public: // API // public: - // ----- Size ----- - bool empty() const {return SIZE == 0;} - int size() const {return SIZE;} - - // ----- Access ----- - TYPE& operator[](unsigned long index) {ASSERT(index < SIZE); return mData[index];} - const TYPE& operator[](unsigned long index) const {ASSERT(index < SIZE); return mData[index];} - - TYPE& front() {ASSERT(SIZE > 0); return mData[0];} - const TYPE& front() const {ASSERT(SIZE > 0); return mData[0];} - - TYPE& back() {ASSERT(SIZE > 0); return mData[SIZE - 1];} - const TYPE& back() const {ASSERT(SIZE > 0); return mData[SIZE - 1];} - - TYPE* buffer() {return mData;} - const TYPE* buffer() const {return mData;} - - // ----- Iterating ----- - iterator begin() {return mData;} - const_iterator begin() const {return mData;} - - iterator end() {return this->begin() + this->size();} - const_iterator end() const {return this->begin() + this->size();} - -// reverse_iterator rbegin() {return reverse_iterator(this->end());} -// const_reverse_iterator rbegin() const {return const_reverse_iterator(this->end());} - -// reverse_iterator rend() {return reverse_iterator(this->begin());} -// const_reverse_iterator rend() const {return const_reverse_iterator(this->begin());} +// ----- Size ----- + bool empty() const {return SIZE == 0;} + size_t size() const {return SIZE;} + +// ----- Access ----- + TYPE& operator[](unsigned long index) {ASSERT(index < SIZE); return mData[index];} + const TYPE& operator[](unsigned long index) const {ASSERT(index < SIZE); return mData[index];} + + TYPE& front() {ASSERT(SIZE > 0); return mData[0];} + const TYPE& front() const {ASSERT(SIZE > 0); return mData[0];} + + TYPE& back() {ASSERT(SIZE > 0); return mData[SIZE - 1];} + const TYPE& back() const {ASSERT(SIZE > 0); return mData[SIZE - 1];} + + TYPE* buffer() {return mData;} + const TYPE* buffer() const {return mData;} + +// ----- Iterating ----- + iterator begin() {return mData;} + const_iterator begin() const {return mData;} + + iterator end() {return this->begin() + this->size();} + const_iterator end() const {return this->begin() + this->size();} + + void init(TYPE def); //----------------------------------- // Member Data @@ -115,19 +106,26 @@ protected: template FixedVector::FixedVector(TYPE value0, TYPE value1, ...) { - mData[0] = value0; - mData[1] = value1; + mData[0] = value0; + mData[1] = value1; + + va_list ap; + va_start(ap, value1); // second argument is last fixed parameter - va_list ap; - va_start(ap, value1); // second argument is last fixed parameter + for (int index = 2; index < SIZE; index++) { + TYPE value = va_arg(ap, TYPE); - for (int index = 2; index < SIZE; index++) { - TYPE value = va_arg(ap, TYPE); + mData[index] = value; + } - mData[index] = value; - } + va_end(ap); +} - va_end(ap); +template +void FixedVector::init(TYPE def) +{ + for (int i = 0; i < SIZE; ++i) + mData[i] = def; } #endif // FIXVEC_H diff --git a/crawl-ref/source/Kills.cc b/crawl-ref/source/Kills.cc index 4a1eaa0ebf..bd1936db50 100644 --- a/crawl-ref/source/Kills.cc +++ b/crawl-ref/source/Kills.cc @@ -2,18 +2,23 @@ * File: Kills.cc * Summary: Player kill tracking * Written by: Darshan Shaligram + * + * Modified for Crawl Reference by $Author$ on $Date$ */ #include "AppHdr.h" + +#include + #include "chardump.h" #include "describe.h" #include "mon-util.h" #include "files.h" #include "itemname.h" +#include "misc.h" #include "travel.h" #include "tags.h" #include "Kills.h" #include "clua.h" -#include #define KILLS_MAJOR_VERSION 4 #define KILLS_MINOR_VERSION 1 @@ -22,25 +27,6 @@ static void kill_lua_filltable(std::vector &v); #endif - -unsigned short get_packed_place( unsigned char branch, int subdepth, - char level_type ) -{ - unsigned short place = (unsigned short) - ( (branch << 8) | subdepth ); - if (level_type == LEVEL_ABYSS || level_type == LEVEL_PANDEMONIUM - || level_type == LEVEL_LABYRINTH) - place = (unsigned short) ( (level_type << 8) | 0xFF ); - return place; -} - -unsigned short get_packed_place() -{ - return get_packed_place( you.where_are_you, - subdungeon_depth(you.where_are_you, you.your_level), - you.level_type ); -} - /////////////////////////////////////////////////////////////////////////// // KillMaster // @@ -52,7 +38,7 @@ const char *kill_category_names[] = "others", }; -const char *KillMaster::category_name(KillCategory kc) const +const char *KillMaster::category_name(kill_category kc) const { if (kc >= KC_YOU && kc < KC_NCATEGORIES) return (kill_category_names[kc]); @@ -95,7 +81,7 @@ void KillMaster::load(FILE *file) void KillMaster::record_kill(const monsters *mon, int killer, bool ispet) { - KillCategory kc = + kill_category kc = (killer == KILL_YOU || killer == KILL_YOU_MISSILE)? KC_YOU : (ispet)? KC_FRIENDLY : KC_OTHER; @@ -134,7 +120,7 @@ std::string KillMaster::kill_info() const kills, count, i == KC_YOU? NULL : - category_name((KillCategory) i), + category_name((kill_category) i), needseparator ); needseparator = true; } @@ -155,13 +141,11 @@ std::string KillMaster::kill_info() const #endif { // We can sum up ourselves, if Lua doesn't want to. - // FIXME: I'm not happy with the looks/wording of the grand total - // count. if (categories > 1) { // Give ourselves a newline first - killtext += EOL; - killtext += grandt + EOL; + killtext += "\n"; + killtext += grandt + "\n"; } } @@ -201,23 +185,23 @@ void KillMaster::add_kill_info(std::string &killtext, } #endif if (separator) - killtext += EOL; + killtext += "\n"; killtext += "Vanquished Creatures"; if (category) killtext += std::string(" (") + category + ")"; - killtext += EOL; + killtext += "\n"; for (int i = 0, sz = kills.size(); i < sz; ++i) { killtext += " " + kills[i].desc; - killtext += EOL; + killtext += "\n"; } { char numbuf[100]; snprintf(numbuf, sizeof numbuf, - "%ld creature%s vanquished." EOL, count, + "%ld creature%s vanquished." "\n", count, count > 1? "s" : ""); killtext += numbuf; } @@ -240,10 +224,10 @@ void Kills::merge(const Kills &k) i != k.kills.end(); ++i) { const kill_monster_desc &kmd = i->first; - kill_def &k = kills[kmd]; + kill_def &ki = kills[kmd]; const kill_def &ko = i->second; bool uniq = mons_is_unique(kmd.monnum); - k.merge(ko, uniq); + ki.merge(ko, uniq); } } @@ -290,85 +274,13 @@ long Kills::get_kills(std::vector &all_kills) const return (count); } -// Takes a packed 'place' and returns a compact stringified place name. -// XXX: This is done in several other places; a unified function to -// describe places would be nice. -std::string short_place_name(unsigned short place) -{ - unsigned char branch = (unsigned char) ((place >> 8) & 0xFF); - int lev = place & 0xFF; - - const char *s; - bool level_num = false; - if (lev == 0xFF) - { - switch (branch) - { - case LEVEL_ABYSS: - s = "Abyss"; - break; - case LEVEL_PANDEMONIUM: - s = "Pan"; - break; - case LEVEL_LABYRINTH: - s = "Lab"; - break; - default: - s = "Buggy Badlands"; - break; - } - } - else - { - switch (branch) - { - case BRANCH_VESTIBULE_OF_HELL: - s = "Hell"; - break; - case BRANCH_HALL_OF_BLADES: - s = "Blade"; - break; - case BRANCH_ECUMENICAL_TEMPLE: - s = "Temple"; - break; - default: - level_num = true; - s = (branch == BRANCH_DIS) ? "Dis:" : - (branch == BRANCH_GEHENNA) ? "Geh:" : - (branch == BRANCH_COCYTUS) ? "Coc:" : - (branch == BRANCH_TARTARUS) ? "Tar:" : - (branch == BRANCH_ORCISH_MINES) ? "Orc:" : - (branch == BRANCH_HIVE) ? "Hive:" : - (branch == BRANCH_LAIR) ? "Lair:" : - (branch == BRANCH_SLIME_PITS) ? "Slime:" : - (branch == BRANCH_VAULTS) ? "Vault:" : - (branch == BRANCH_CRYPT) ? "Crypt:" : - (branch == BRANCH_HALL_OF_ZOT) ? "Zot:" : - (branch == BRANCH_SNAKE_PIT) ? "Snake:" : - (branch == BRANCH_ELVEN_HALLS) ? "Elf:" : - (branch == BRANCH_TOMB) ? "Tomb:" : - (branch == BRANCH_SWAMP) ? "Swamp:" : "D:"; - break; - } - } - - std::string pl = s; - if (level_num) - { - char buf[20]; - snprintf(buf, sizeof buf, "%d", lev); - pl += buf; - } - return pl; -} - void Kills::save(FILE *file) const { // How many kill records do we have? writeLong(file, kills.size()); - kill_map::const_iterator iter = kills.begin(); - for ( ; iter != kills.end(); ++iter) + for ( kill_map::const_iterator iter = kills.begin(); + iter != kills.end(); ++iter) { iter->first.save(file); iter->second.save(file); @@ -377,7 +289,7 @@ void Kills::save(FILE *file) const // How many ghosts do we have? writeShort(file, ghosts.size()); for (ghost_vec::const_iterator iter = ghosts.begin(); - iter != ghosts.end(); ++iter) + iter != ghosts.end(); ++iter) { iter->save(file); } @@ -407,13 +319,13 @@ void Kills::load(FILE *file) void Kills::record_ghost_kill(const struct monsters *mon) { - kill_ghost ghost(mon); - ghosts.push_back(ghost); + kill_ghost ghostk(mon); + ghosts.push_back(ghostk); } kill_def::kill_def(const struct monsters *mon) : kills(0), exp(0) { - exp = exper_value( (struct monsters *) mon); + exp = exper_value(mon); add_kill(mon, get_packed_place()); } @@ -460,9 +372,7 @@ static std::string pluralize(const std::string &name, // whole name is not suffixed by a modifier, such as 'zombie' or 'skeleton' if ( (pos = name.find(" of ")) != std::string::npos && !ends_with(name, no_of) ) - { return pluralize(name.substr(0, pos)) + name.substr(pos); - } else if (ends_with(name, "us")) // Fungus, ufetubus, for instance. return name.substr(0, name.length() - 2) + "i"; @@ -704,7 +614,7 @@ void kill_def::load(FILE *file) kill_ghost::kill_ghost(const struct monsters *mon) { - exp = exper_value( (struct monsters *) mon); + exp = exper_value(mon); place = get_packed_place(); ghost_name = ghost.name; @@ -738,7 +648,6 @@ void kill_ghost::load(FILE *file) kill_monster_desc::kill_monster_desc(const monsters *mon) { - // TODO: We need to understand how shapeshifters are handled. monnum = mon->type; modifier = M_NORMAL; switch (mon->type) @@ -758,8 +667,8 @@ kill_monster_desc::kill_monster_desc(const monsters *mon) } if (modifier != M_NORMAL) monnum = mon->number; - if (mons_has_ench((struct monsters *) mon, ENCH_SHAPESHIFTER) || - mons_has_ench((struct monsters *) mon, ENCH_GLOWING_SHAPESHIFTER)) + if (mons_has_ench(mon, ENCH_SHAPESHIFTER) || + mons_has_ench(mon, ENCH_GLOWING_SHAPESHIFTER)) modifier = M_SHAPESHIFTER; // XXX: Ugly hack - merge all mimics into one mimic record. @@ -907,7 +816,7 @@ static int kill_lualc_holiness(lua_State *ls) } else { - switch (mons_holiness(ke->monnum)) + switch (mons_class_holiness(ke->monnum)) { case MH_HOLY: verdict = "holy"; break; case MH_NATURAL: verdict = "natural"; break; @@ -985,7 +894,7 @@ static int kill_lualc_rawwrite(lua_State *ls) *skill += s; *skill += "\n"; - + return 0; } diff --git a/crawl-ref/source/Kills.h b/crawl-ref/source/Kills.h index 28c30a88c8..f3ada4fe40 100644 --- a/crawl-ref/source/Kills.h +++ b/crawl-ref/source/Kills.h @@ -160,7 +160,7 @@ public: std::string kill_info() const; private: - const char *category_name(KillCategory kc) const; + const char *category_name(kill_category kc) const; Kills categorized_kills[KC_NCATEGORIES]; private: @@ -169,13 +169,6 @@ private: const; }; -unsigned short get_packed_place(); - -unsigned short get_packed_place( unsigned char branch, int subdepth, - char level_type ); - -std::string short_place_name(unsigned short place); - enum KILL_DUMP_OPTIONS { KDO_NO_PLACES, // Don't dump places at all diff --git a/crawl-ref/source/MacString.cc b/crawl-ref/source/MacString.cc deleted file mode 100644 index 2d5c14442e..0000000000 --- a/crawl-ref/source/MacString.cc +++ /dev/null @@ -1,199 +0,0 @@ -/* - * File: MacString.cc - * Summary: Wrapper around an immutable CFString. - * Written by: Jesse Jones (jesjones@mindspring.com) - * - * Change History (most recent first): - * - * <1> 6/04/02 JDJ Created - */ - -#include "AppHdr.h" -#include "MacString.h" - -#if macintosh - -#include - -#include "debug.h" - - -// ======================================================================== -// Internal Functions -// ======================================================================== - -//--------------------------------------------------------------- -// -// ThrowIf -// -//--------------------------------------------------------------- -static void ThrowIf(bool predicate, const std::string& text) -{ - if (predicate) - throw std::runtime_error(text); -} - -#if __MWERKS__ -#pragma mark - -#endif - -// ============================================================================ -// class MacString -// ============================================================================ - -//--------------------------------------------------------------- -// -// MacString::~MacString -// -//--------------------------------------------------------------- -MacString::~MacString() -{ - CFRelease(mString); -} - - -//--------------------------------------------------------------- -// -// MacString::MacString () -// -//--------------------------------------------------------------- -MacString::MacString() -{ - mString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, NULL, 0); - ThrowIf(mString == NULL, "Couldn't create the CFString"); -} - - -//--------------------------------------------------------------- -// -// MacString::MacString (unsigned char*) -// -//--------------------------------------------------------------- -MacString::MacString(const unsigned char* str) -{ - ASSERT(str != NULL); - - CFStringEncoding encoding = CFStringGetSystemEncoding(); - mString = CFStringCreateWithPascalString(kCFAllocatorSystemDefault, str, encoding); - ThrowIf(mString == NULL, "Couldn't create the CFString"); -} - - -//--------------------------------------------------------------- -// -// MacString::MacString (char*) -// -//--------------------------------------------------------------- -MacString::MacString(const char* str) -{ - ASSERT(str != NULL); - - CFStringEncoding encoding = CFStringGetSystemEncoding(); - mString = CFStringCreateWithCString(kCFAllocatorSystemDefault, str, encoding); - ThrowIf(mString == NULL, "Couldn't create the CFString"); -} - - -//--------------------------------------------------------------- -// -// MacString::MacString (CFStringRef) -// -//--------------------------------------------------------------- -MacString::MacString(CFStringRef str) -{ - ASSERT(str != NULL); - - mString = str; - CFRetain(mString); -} - - -//--------------------------------------------------------------- -// -// MacString::MacString (CFMutableStringRef) -// -//--------------------------------------------------------------- -MacString::MacString(CFMutableStringRef str) -{ - ASSERT(str != NULL); - - mString = CFStringCreateCopy(kCFAllocatorSystemDefault, str); - ThrowIf(mString == NULL, "Couldn't create the CFString"); -} - - -//--------------------------------------------------------------- -// -// MacString::MacString (int) -// -//--------------------------------------------------------------- -MacString::MacString(int value) -{ - char buffer[32]; - sprintf(buffer, "%d", value); - - CFStringEncoding encoding = CFStringGetSystemEncoding(); - mString = CFStringCreateWithCString(kCFAllocatorSystemDefault, buffer, encoding); - ThrowIf(mString == NULL, "Couldn't create the CFString"); -} - - -//--------------------------------------------------------------- -// -// MacString::MacString (MacString) -// -//--------------------------------------------------------------- -MacString::MacString(const MacString& str) -{ - mString = str.mString; // immutable so we can refcount - CFRetain(mString); -} - - -//--------------------------------------------------------------- -// -// MacString::operator= (MacString) -// -//--------------------------------------------------------------- -MacString& MacString::operator=(const MacString& rhs) -{ - if (this != &rhs) - { - CFRelease(mString); - - mString = rhs.mString; // immutable so we can refcount - CFRetain(mString); - } - - return *this; -} - - -//--------------------------------------------------------------- -// -// MacString::length -// -//--------------------------------------------------------------- -size_t MacString::length() const -{ - size_t len = (size_t) CFStringGetLength(mString); - - return len; -} - - -//--------------------------------------------------------------- -// -// MacString::CopyTo -// -//--------------------------------------------------------------- -void MacString::CopyTo(unsigned char* buffer, CFIndex bytes) -{ - ASSERT(buffer != NULL || bytes == 0); - - bool converted = CFStringGetPascalString(mString, buffer, bytes, CFStringGetSystemEncoding()); - ThrowIf(!converted, "Couldn't convert the CFString into a Pascal string"); -} - - -#endif // macintosh diff --git a/crawl-ref/source/MacString.h b/crawl-ref/source/MacString.h deleted file mode 100644 index d2cad4b1be..0000000000 --- a/crawl-ref/source/MacString.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * File: MacString.h - * Summary: Wrapper around an immutable CFString. - * Written by: Jesse Jones (jesjones@mindspring.com) - * - * Change History (most recent first): - * - * <1> 6/04/02 JDJ Created - */ - -#ifndef MAC_STRING_H -#define MAC_STRING_H - -#if macintosh - -#include - - -// ============================================================================ -// class MacString -//! Wrapper around an immutable CFString. -// ============================================================================ -class MacString { - -//----------------------------------- -// Initialization/Destruction -// -public: - ~MacString(); - - MacString(); - - MacString(const char* str); - MacString(const unsigned char* str); - /**< Uses default system encoding. */ - - MacString(CFStringRef str); - /**< Bumps the ref count. */ - - MacString(CFMutableStringRef str); - /**< Makes a copy. */ - - explicit MacString(int value); - - MacString(const MacString& str); - MacString& operator=(const MacString& rhs); - -//----------------------------------- -// API -// -public: - // ----- Size ----- - size_t length() const; - size_t size() const {return this->length();} - bool empty() const {return this->length() == 0;} - - // ----- Access ----- - void CopyTo(unsigned char* buffer, CFIndex bytes); - - operator CFStringRef() const {return mString;} - -//----------------------------------- -// Member Data -// -private: - CFStringRef mString; -}; - - -#endif // macintosh -#endif // MAC_STRING_H diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc index 64a1c10e74..d321365e74 100644 --- a/crawl-ref/source/abl-show.cc +++ b/crawl-ref/source/abl-show.cc @@ -105,12 +105,11 @@ static const struct ability_def Ability_List[] = { ABIL_BREATHE_STICKY_FLAME, "Breathe Sticky Flame", 0, 0, 125, 0, ABFLAG_BREATH }, { ABIL_BREATHE_STEAM, "Breathe Steam", 0, 0, 75, 0, ABFLAG_BREATH }, - // Handled with breath weapons, but doesn't cause a breathing delay - { ABIL_SPIT_ACID, "Spit Acid", 0, 0, 125, 0, ABFLAG_NONE }, + { ABIL_SPIT_ACID, "Spit Acid", 0, 0, 125, 0, ABFLAG_BREATH }, { ABIL_FLY, "Fly", 3, 0, 100, 0, ABFLAG_NONE }, { ABIL_SUMMON_MINOR_DEMON, "Summon Minor Demon", 3, 3, 75, 0, ABFLAG_NONE }, - { ABIL_SUMMON_DEMON, "Summon Demon", 5, 5, 150, 0, ABFLAG_NONE }, + { ABIL_SUMMON_DEMONS, "Summon Demons", 5, 5, 150, 0, ABFLAG_NONE }, { ABIL_HELLFIRE, "Hellfire", 8, 8, 200, 0, ABFLAG_NONE }, { ABIL_TORMENT, "Torment", 9, 0, 250, 0, ABFLAG_PAIN }, { ABIL_RAISE_DEAD, "Raise Dead", 5, 5, 150, 0, ABFLAG_NONE }, @@ -160,7 +159,7 @@ static const struct ability_def Ability_List[] = { ABIL_TSO_REPEL_UNDEAD, "Repel Undead", 1, 0, 100, 0, ABFLAG_NONE }, { ABIL_TSO_SMITING, "Smiting", 3, 0, 50, 2, ABFLAG_NONE }, { ABIL_TSO_ANNIHILATE_UNDEAD, "Annihilate Undead", 3, 0, 50, 2, ABFLAG_NONE }, - { ABIL_TSO_THUNDERBOLT, "Thunderbolt", 5, 0, 100, 2, ABFLAG_NONE }, + { ABIL_TSO_CLEANSING_FLAME, "Cleansing Flame", 5, 0, 100, 2, ABFLAG_NONE }, { ABIL_TSO_SUMMON_DAEVA, "Summon Daeva", 8, 0, 150, 4, ABFLAG_NONE }, // Kikubaaqudgha @@ -175,9 +174,6 @@ static const struct ability_def Ability_List[] = { ABIL_YRED_DRAIN_LIFE, "Drain Life", 6, 0, 200, 2, ABFLAG_NONE }, { ABIL_YRED_CONTROL_UNDEAD, "Control Undead", 5, 0, 150, 2, ABFLAG_NONE }, - // Vehumet - { ABIL_VEHUMET_CHANNEL_ENERGY, "Channel Energy", 0, 0, 50, 0, ABFLAG_NONE }, - // Okawaru { ABIL_OKAWARU_MIGHT, "Might", 2, 0, 50, 1, ABFLAG_NONE }, { ABIL_OKAWARU_HEALING, "Healing", 2, 0, 75, 1, ABFLAG_NONE }, @@ -190,6 +186,7 @@ static const struct ability_def Ability_List[] = { ABIL_MAKHLEB_GREATER_SERVANT_OF_MAKHLEB, "Greater Servant of Makhleb", 6, 0, 100, 3, ABFLAG_NONE }, // Sif Muna + { ABIL_SIF_MUNA_CHANNEL_ENERGY, "Channel Energy", 0, 0, 100, 0, ABFLAG_NONE }, { ABIL_SIF_MUNA_FORGET_SPELL, "Forget Spell", 5, 0, 0, 8, ABFLAG_NONE }, // Trog @@ -211,7 +208,6 @@ static const struct ability_def Ability_List[] = { ABIL_ROTTING, "Rotting", 4, 4, 0, 2, ABFLAG_NONE }, { ABIL_TORMENT_II, "Call Torment", 9, 0, 0, 3, ABFLAG_PAIN }, - { ABIL_SHUGGOTH_SEED, "Sow Shuggoth Seed", 12, 8, 0, 6, ABFLAG_NONE }, { ABIL_RENOUNCE_RELIGION, "Renounce Religion", 0, 0, 0, 0, ABFLAG_NONE }, }; @@ -467,12 +463,10 @@ bool activate_ability(void) // no turning back now... {dlb} const struct ability_def abil = get_ability_def(Curr_abil[abil_used].which); - // currently only delayed fireball is instantaneous -- bwr - you.turn_is_over = ((abil.flags & ABFLAG_INSTANT) ? 0 : 1); - if (random2avg(100, 3) < Curr_abil[abil_used].fail) { mpr("You fail to use your ability."); + you.turn_is_over = true; return (false); } @@ -576,8 +570,7 @@ bool activate_ability(void) case ABIL_BREATHE_POWER: case ABIL_BREATHE_STICKY_FLAME: case ABIL_BREATHE_STEAM: - if (you.duration[DUR_BREATH_WEAPON] - && Curr_abil[abil_used].which != ABIL_SPIT_ACID) + if (you.duration[DUR_BREATH_WEAPON]) { canned_msg(MSG_CANNOT_DO_YET); return (false); @@ -643,16 +636,11 @@ bool activate_ability(void) } - if (Curr_abil[abil_used].which != ABIL_SPIT_ACID) - { - you.duration[DUR_BREATH_WEAPON] = 3 + random2(5) - + random2(30 - you.experience_level); - } + you.duration[DUR_BREATH_WEAPON] = + 3 + random2(4) + random2(30 - you.experience_level) / 2; if (Curr_abil[abil_used].which == ABIL_BREATHE_STEAM) - { you.duration[DUR_BREATH_WEAPON] /= 2; - } break; case ABIL_EVOKE_BLINK: // randarts @@ -670,8 +658,9 @@ bool activate_ability(void) return (false); } - go_berserk(true); - exercise( SK_EVOCATIONS, 1 ); + // only exercise if berserk succeeds + if ( go_berserk(true) ) + exercise( SK_EVOCATIONS, 1 ); break; // fly (kenku) -- eventually becomes permanent (see acr.cc) @@ -710,13 +699,15 @@ bool activate_ability(void) summon_any_demon(DEMON_LESSER) ); break; - case ABIL_SUMMON_DEMON: + case ABIL_SUMMON_DEMONS: summon_ice_beast_etc( you.experience_level * 4, summon_any_demon(DEMON_COMMON) ); break; case ABIL_HELLFIRE: - your_spells(SPELL_HELLFIRE, 20 + you.experience_level, false); + if (your_spells(SPELL_HELLFIRE, + 20 + you.experience_level, false) == -1) + return (false); break; case ABIL_TORMENT: @@ -726,7 +717,7 @@ bool activate_ability(void) return (false); } - torment(you.x_pos, you.y_pos); + torment(TORMENT_GENERIC, you.x_pos, you.y_pos); break; case ABIL_RAISE_DEAD: @@ -873,14 +864,14 @@ bool activate_ability(void) exercise(SK_INVOCATIONS, 2 + random2(4)); break; - case ABIL_TSO_THUNDERBOLT: + case ABIL_TSO_CLEANSING_FLAME: if (spell_direction(spd, beam) == -1) { canned_msg(MSG_OK); return (false); } - zapping(ZAP_LIGHTNING, you.skills[SK_INVOCATIONS] * 6, beam); + zapping(ZAP_CLEANSING_FLAME, 20 + you.skills[SK_INVOCATIONS] * 6, beam); exercise(SK_INVOCATIONS, 3 + random2(6)); break; @@ -906,7 +897,8 @@ bool activate_ability(void) break; case ABIL_KIKU_INVOKE_DEATH: - summon_ice_beast_etc(20 + you.skills[SK_INVOCATIONS] * 3, MONS_REAPER); + summon_ice_beast_etc( + 20 + you.skills[SK_INVOCATIONS] * 3, MONS_REAPER, true); exercise(SK_INVOCATIONS, 10 + random2(14)); break; @@ -943,7 +935,7 @@ bool activate_ability(void) exercise(SK_INVOCATIONS, 3 + random2(4)); break; - case ABIL_VEHUMET_CHANNEL_ENERGY: + case ABIL_SIF_MUNA_CHANNEL_ENERGY: mpr("You channel some magical energy."); inc_mp(1 + random2(you.skills[SK_INVOCATIONS] / 4 + 2), false); @@ -1029,12 +1021,12 @@ bool activate_ability(void) beam.flavour = BEAM_ELECTRICITY; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "blast of lightning"); + beam.name = "blast of lightning"; beam.colour = LIGHTCYAN; beam.thrower = KILL_YOU; beam.aux_source = "Makhleb's lightning strike"; beam.ex_size = 1 + you.skills[SK_INVOCATIONS] / 8; - beam.isTracer = false; + beam.is_tracer = false; // ... and fire! explosion(beam); @@ -1135,7 +1127,9 @@ bool activate_ability(void) return (false); } - your_spells( SPELL_HELLFIRE, 20 + you.experience_level, false ); + if (your_spells( SPELL_HELLFIRE, + 20 + you.experience_level, false ) == -1) + return (false); you.duration[DUR_BREATH_WEAPON] += 3 + random2(5) + random2(30 - you.experience_level); @@ -1153,21 +1147,7 @@ bool activate_ability(void) return (false); } - torment(you.x_pos, you.y_pos); - exercise(SK_INVOCATIONS, 2 + random2(4)); - break; - - case ABIL_SHUGGOTH_SEED: - if (you.duration[DUR_SHUGGOTH_SEED_RELOAD]) - { - canned_msg(MSG_CANNOT_DO_YET); - return (false); - } - - cast_shuggoth_seed( you.experience_level * 2 - + you.skills[SK_INVOCATIONS] * 3 ); - - you.duration[DUR_SHUGGOTH_SEED_RELOAD] = 10 + random2avg(39, 2); + torment(TORMENT_GENERIC, you.x_pos, you.y_pos); exercise(SK_INVOCATIONS, 2 + random2(4)); break; @@ -1188,6 +1168,9 @@ bool activate_ability(void) break; } + // currently only delayed fireball is instantaneous -- bwr + you.turn_is_over = !(abil.flags & ABFLAG_INSTANT); + // All failures should have returned by this point, so we'll // apply the costs -- its not too neat, but it works for now. -- bwr const int food_cost = abil.food_cost + random2avg(abil.food_cost, 2); @@ -1324,7 +1307,7 @@ char show_abilities( void ) if (cost_str.length() > 24) cost_str = cost_str.substr( 0, 24 ); - cprintf( cost_str.c_str() ); + cprintf( "%s", cost_str.c_str() ); gotoxy(60, wherey()); @@ -1468,7 +1451,7 @@ bool generate_abilities( void ) insert_ability( ABIL_SUMMON_MINOR_DEMON ); if (you.mutation[MUT_SUMMON_DEMONS]) - insert_ability( ABIL_SUMMON_DEMON ); + insert_ability( ABIL_SUMMON_DEMONS ); if (you.mutation[MUT_HURL_HELLFIRE]) insert_ability( ABIL_HELLFIRE ); @@ -1532,7 +1515,7 @@ bool generate_abilities( void ) if (you.piety >= 75) insert_ability( ABIL_TSO_ANNIHILATE_UNDEAD ); if (you.piety >= 100) - insert_ability( ABIL_TSO_THUNDERBOLT ); + insert_ability( ABIL_TSO_CLEANSING_FLAME ); if (you.piety >= 120) insert_ability( ABIL_TSO_SUMMON_DAEVA ); break; @@ -1602,15 +1585,12 @@ bool generate_abilities( void ) break; case GOD_SIF_MUNA: + if (you.piety >= 30) + insert_ability( ABIL_SIF_MUNA_CHANNEL_ENERGY ); if (you.piety >= 50) insert_ability( ABIL_SIF_MUNA_FORGET_SPELL ); break; - case GOD_VEHUMET: - if (you.piety >= 100) - insert_ability( ABIL_VEHUMET_CHANNEL_ENERGY ); - break; - default: break; } @@ -1754,11 +1734,6 @@ void set_god_ability_slots( void ) num_abil = 5; break; - case GOD_VEHUMET: - abil_start = ABIL_VEHUMET_CHANNEL_ENERGY; - num_abil = 1; - break; - case GOD_OKAWARU: abil_start = ABIL_OKAWARU_MIGHT; num_abil = 3; @@ -1770,8 +1745,8 @@ void set_god_ability_slots( void ) break; case GOD_SIF_MUNA: - abil_start = ABIL_SIF_MUNA_FORGET_SPELL; - num_abil = 1; + abil_start = ABIL_SIF_MUNA_CHANNEL_ENERGY; + num_abil = 2; break; case GOD_TROG: @@ -1784,6 +1759,7 @@ void set_god_ability_slots( void ) num_abil = 5; break; + case GOD_VEHUMET: case GOD_NEMELEX_XOBEH: case GOD_XOM: default: @@ -1973,7 +1949,7 @@ static bool insert_ability( int which_ability ) failure = 35 - you.experience_level; break; - case ABIL_SUMMON_DEMON: + case ABIL_SUMMON_DEMONS: failure = 40 - you.experience_level; break; @@ -2090,7 +2066,7 @@ static bool insert_ability( int which_ability ) failure = 40 - (you.piety / 20) - (5 * you.skills[SK_INVOCATIONS]); break; - case ABIL_VEHUMET_CHANNEL_ENERGY: + case ABIL_SIF_MUNA_CHANNEL_ENERGY: invoc = true; failure = 40 - you.intel - you.skills[SK_INVOCATIONS]; break; @@ -2113,7 +2089,7 @@ static bool insert_ability( int which_ability ) break; case ABIL_ZIN_HOLY_WORD: - case ABIL_TSO_THUNDERBOLT: + case ABIL_TSO_CLEANSING_FLAME: case ABIL_ELYVILON_RESTORATION: case ABIL_YRED_CONTROL_UNDEAD: case ABIL_OKAWARU_HASTE: @@ -2151,11 +2127,6 @@ static bool insert_ability( int which_ability ) failure = 70 - (you.piety / 25) - (you.skills[SK_INVOCATIONS] * 4); break; - case ABIL_SHUGGOTH_SEED: - invoc = true; - failure = 85 - (you.piety / 25) - (you.skills[SK_INVOCATIONS] * 3); - break; - case ABIL_RENOUNCE_RELIGION: invoc = true; perfect = true; diff --git a/crawl-ref/source/abyss.cc b/crawl-ref/source/abyss.cc index 3f48ed7b35..ca7c02b160 100644 --- a/crawl-ref/source/abyss.cc +++ b/crawl-ref/source/abyss.cc @@ -3,6 +3,8 @@ * Summary: Misc functions (most of which don't appear to be related to priests). * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 10/11/99 BCR Added Daniel's crash patch diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index d736658d9e..3596b0959b 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -3,6 +3,8 @@ * Summary: Main entry point, event loop, and some initialization functions * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <18> 7/29/00 JDJ values.h isn't included on Macs @@ -39,7 +41,7 @@ #include // I don't seem to need values.h for VACPP.. -#if !defined(__IBMCPP__) && !defined(MAC) +#if !defined(__IBMCPP__) #include #endif @@ -64,16 +66,6 @@ #include #endif -#ifdef USE_EMX -#include -#endif - -#ifdef OS9 -#include -#else -#include -#endif - #include "externs.h" #include "abl-show.h" @@ -97,15 +89,18 @@ #include "it_use2.h" #include "it_use3.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "lev-pand.h" #include "macro.h" +#include "maps.h" #include "misc.h" #include "monplace.h" #include "monstuff.h" #include "mon-util.h" #include "mutation.h" #include "newgame.h" +#include "notes.h" #include "ouch.h" #include "output.h" #include "overmap.h" @@ -126,25 +121,27 @@ #include "transfor.h" #include "travel.h" #include "view.h" -#include "wpn-misc.h" #include "stash.h" struct crawl_environment env; struct player you; struct system_environment SysEnv; +std::string init_file_location; // externed in newgame.cc + char info[ INFO_SIZE ]; // messaging queue extern'd everywhere {dlb} -int stealth; // externed in view.h // no it is not {dlb} +int stealth; // externed in view.cc char use_colour = 1; -FixedVector< char, NUM_STATUE_TYPES > Visible_Statue; +bool just_autoprayed = false; +bool about_to_autopray = false; // set to true once a new game starts or an old game loads bool game_has_started = false; // Clockwise, around the compass from north (same order as enum RUN_DIR) -static const struct coord_def Compass[8] = +const struct coord_def Compass[8] = { { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, @@ -166,33 +163,25 @@ static const struct coord_def Compass[8] = */ -void (*viewwindow) (char, bool); - -/* these are all defined in view.cc: */ -extern unsigned char (*mapch) (unsigned char); -extern unsigned char (*mapch2) (unsigned char); -unsigned char mapchar(unsigned char ldfk); -unsigned char mapchar2(unsigned char ldfk); -unsigned char mapchar3(unsigned char ldfk); -unsigned char mapchar4(unsigned char ldfk); - -/* - Function pointers are used to make switching between Unix and DOS char sets - possible as a runtime option (command-line -c) -*/ - -// these two are defined in view.cc. What does the player look like? -// (changed for shapechanging) -extern unsigned char your_sign; -extern unsigned char your_colour; - // Functions in main module -static void close_door(char move_x, char move_y); +static void close_door(int move_x, int move_y); static void do_berserk_no_combat_penalty(void); static bool initialise(void); static void input(void); -static void move_player(char move_x, char move_y); -static void open_door(char move_x, char move_y); +static void move_player(int move_x, int move_y); +static void open_door(int move_x, int move_y, bool check_confused = true); +static void start_running( int dir, int mode ); +static void close_door(int move_x, int move_y); + +static void init_io(); +static void prep_input(); +static void input(); +static void middle_input(); +static void world_reacts(); +static command_type get_next_cmd(); +typedef int keycode_type; +static keycode_type get_next_keycode(); +static command_type keycode_to_command( keycode_type key ); /* It all starts here. Some initialisations are run first, then straight to @@ -200,18 +189,6 @@ static void open_door(char move_x, char move_y); */ int main( int argc, char *argv[] ) { -#ifdef USE_ASCII_CHARACTERS - // Default to the non-ibm set when it makes sense. - viewwindow = &viewwindow3; - mapch = &mapchar3; - mapch2 = &mapchar4; -#else - // Use the standard ibm default - viewwindow = &viewwindow2; - mapch = &mapchar; - mapch2 = &mapchar2; -#endif - // Load in the system environment variables get_system_environment(); @@ -239,7 +216,10 @@ int main( int argc, char *argv[] ) } // Read the init file - read_init_file(); + init_file_location = read_init_file(); + + // Read special levels and vaults. + read_maps(); // now parse the args again, looking for everything else. parse_args( argc, argv, false ); @@ -251,26 +231,13 @@ int main( int argc, char *argv[] ) exit(0); } -#ifdef UNIX - unixcurses_startup(); -#endif - -#ifdef MAC - init_mac(); -#endif - -#ifdef WIN32CONSOLE - init_libw32c(); -#endif + init_io(); #ifdef USE_MACROS // Load macros macro_init(); #endif - init_overmap(); // in overmap.cc (duh?) - clear_ids(); // in itemname.cc - bool game_start = initialise(); if (game_start || Options.always_greet) @@ -357,16 +324,23 @@ int main( int argc, char *argv[] ) unixcurses_shutdown(); #endif -#ifdef MAC - deinit_mac(); + return 0; +} // end main() + +static void init_io() +{ +#ifdef UNIX + unixcurses_startup(); #endif -#ifdef USE_EMX - deinit_emx(); +#ifdef WIN32CONSOLE + init_libw32c(); #endif - return 0; -} // end main() +#ifdef DOS + init_libdos(); +#endif +} #ifdef WIZARD static void handle_wizard_command( void ) @@ -438,7 +412,7 @@ static void handle_wizard_command( void ) case 'v': // this command isn't very exciting... feel free to replace - i = prompt_invent_item( "Value of which item?", -1 ); + i = prompt_invent_item( "Value of which item?", MT_INVSELECT, -1 ); if (i == PROMPT_ABORT || !is_random_artefact( you.inv[i] )) { canned_msg( MSG_OK ); @@ -452,7 +426,8 @@ static void handle_wizard_command( void ) break; case '+': - i = prompt_invent_item( "Make an artefact out of which item?", -1 ); + i = prompt_invent_item( + "Make an artefact out of which item?", MT_INVSELECT, -1 ); if (i == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -593,6 +568,22 @@ static void handle_wizard_command( void ) tweak_object(); break; + case 'T': + debug_make_trap(); + break; + + case '\\': + debug_make_shop(); + break; + + case 'f': + debug_fight_statistics(false); + break; + + case 'F': + debug_fight_statistics(true); + break; + case 'm': create_spec_monster(); break; @@ -695,7 +686,11 @@ static void handle_wizard_command( void ) break; case '{': - magic_mapping(99, 100); + magic_mapping(1000, 100); + break; + + case '@': + debug_set_stats(); break; case '^': @@ -765,437 +760,312 @@ static void handle_wizard_command( void ) } #endif -// This function creates "equivalence classes" so that undiscovered -// traps and secret doors aren't running stopping points. -static char base_grid_type( char grid ) +// Set up the running variables for the current run. +static void start_running( int dir, int mode ) { - // Don't stop for undiscovered traps: - if (grid == DNGN_UNDISCOVERED_TRAP) - return (DNGN_FLOOR); - - // Or secret doors (which currently always look like rock walls): - if (grid == DNGN_SECRET_DOOR) - return (DNGN_ROCK_WALL); - - return (grid); + you.running.initialise(dir, mode); } -// Set up the front facing array for detecting terrain based stops -static void set_run_check( int index, int dir ) +static bool recharge_rod( item_def &rod, bool wielded ) { - you.run_check[index].dx = Compass[dir].x; - you.run_check[index].dy = Compass[dir].y; + if (!item_is_rod(rod) || rod.plus >= rod.plus2 || !enough_mp(1, true)) + return (false); - const int targ_x = you.x_pos + Compass[dir].x; - const int targ_y = you.y_pos + Compass[dir].y; + const int charge = rod.plus / ROD_CHARGE_MULT; - you.run_check[index].grid = base_grid_type( grd[ targ_x ][ targ_y ] ); -} + int rate = ((charge + 1) * ROD_CHARGE_MULT) / 10; + + rate *= (10 + skill_bump( SK_EVOCATIONS )); + rate = div_rand_round( rate, 100 ); -// Set up the running variables for the current run. -static void start_running( int dir, char mode ) -{ - if (dir == RDIR_REST) + if (rate < 5) + rate = 5; + else if (rate > ROD_CHARGE_MULT / 2) + rate = ROD_CHARGE_MULT / 2; + + // If not wielded, the rod charges far more slowly. + if (!wielded) + rate /= 5; + // Shields hamper recharging for wielded rods. + else if (player_shield()) + rate /= 2; + + if (rod.plus / ROD_CHARGE_MULT != (rod.plus + rate) / ROD_CHARGE_MULT) { - you.run_x = 0; - you.run_y = 0; - you.running = mode; - return; + dec_mp(1); + if (wielded) + you.wield_change = true; } - ASSERT( dir >= 0 && dir <= 7 ); - - you.run_x = Compass[dir].x; - you.run_y = Compass[dir].y; - you.running = mode; + rod.plus += rate; + if (rod.plus > rod.plus2) + rod.plus = rod.plus2; - // Get the compass point to the left/right of intended travel: - const int left = (dir - 1 < 0) ? 7 : (dir - 1); - const int right = (dir + 1 > 7) ? 0 : (dir + 1); + if (wielded && rod.plus == rod.plus2 && is_resting()) + stop_running(); - // Record the direction and starting tile type for later reference: - set_run_check( 0, left ); - set_run_check( 1, dir ); - set_run_check( 2, right ); + return (true); } -// Returns true if one of the front three grids has changed. -static bool check_stop_running( void ) +static void recharge_rods() { - if (env.cgrid[you.x_pos + you.run_x][you.y_pos + you.run_y] != EMPTY_CLOUD) - return (true); - - if (mgrd[you.x_pos + you.run_x][you.y_pos + you.run_y] != NON_MONSTER) - return (true); - - for (int i = 0; i < 3; i++) + const int wielded = you.equip[EQ_WEAPON]; + if (wielded != -1) { - const int targ_x = you.x_pos + you.run_check[i].dx; - const int targ_y = you.y_pos + you.run_check[i].dy; - const int targ_grid = base_grid_type( grd[ targ_x ][ targ_y ] ); - - if (you.run_check[i].grid != targ_grid) - return (true); + if (recharge_rod( you.inv[wielded], true )) + return ; } - return (false); + for (int i = 0; i < ENDOFPACK; ++i) + { + if (i != wielded && is_valid_item(you.inv[i]) + && one_chance_in(3) + && recharge_rod( you.inv[i], false )) + return; + } } +/* used to determine whether to apply the berserk penalty at end + of round */ +bool apply_berserk_penalty = false; + +/* There is now a distinction between keycodes and commands. + A keycode_type gets mapped through keycode_to_command to + become a command_type. + So a keycode_type could be something like 'H'; + a command_type would be something like COMMAND_RUN_LEFT. +*/ /* This function handles the player's input. It's called from main(), from inside an endless loop. - */ -static void input(void) -{ - - bool its_quiet; //jmf: for silence messages - FixedVector < int, 2 > plox; - char move_x = 0; - char move_y = 0; - - int keyin = 0; - -#ifdef UNIX - // Stuff for the Unix keypad kludge - bool running = false; - bool opening = false; -#endif - - you.shield_blocks = 0; // no blocks this round - - you.time_taken = player_speed(); -#ifdef UNIX - update_screen(); -#else - window( 1, 1, 80, get_number_of_lines() ); -#endif - - textcolor(LIGHTGREY); + It's now undergone major refactoring. The code path is now: + 1. Get next player input item (key) + 2. Translate key to command + 3. Execute the command + 4. Update rest of world if necessary + */ +static void input() { - set_redraw_status( REDRAW_LINE_2_MASK | REDRAW_LINE_3_MASK ); - print_stats(); + you.turn_is_over = false; + prep_input(); - if (you.paralysis) - { - keyin = '.'; // end of if you.paralysis == 0 + /* you.paralysis check */ + if ( you.paralysis ) { + world_reacts(); + return; } - else - { -#ifdef STASH_TRACKING - if (Options.stash_tracking) - stashes.update_visible_stashes( - Options.stash_tracking == STM_ALL? - StashTracker::ST_AGGRESSIVE : - StashTracker::ST_PASSIVE); -#endif - handle_delay(); - - gotoxy(18, 9); - - if (you_are_delayed()) - keyin = '.'; - else if (you.activity) - { - keyin = 128; - you.turn_is_over = 0; - perform_activity(); - } - else - { - if (you.running < 0) // Travel and explore - travel(&keyin, &move_x, &move_y); + + middle_input(); - if (you.running > 0) - { - keyin = 128; + handle_delay(); - move_x = you.run_x; - move_y = you.run_y; + gotoxy(18,9); - if (kbhit()) - { - stop_running(); - goto gutch; - } - - if (you.run_x == 0 && you.run_y == 0) - { - you.running--; - keyin = '.'; - } - } - else if (!you.running) - { + if ( you_are_delayed() ) { + world_reacts(); + return; + } -#if DEBUG_DIAGNOSTICS - // save hunger at start of round - // for use with hunger "delta-meter" in output.cc - you.old_hunger = you.hunger; -#endif + /* Change from previous code! */ + if ( you.turn_is_over ) { + world_reacts(); + return; + } -#if DEBUG_ITEM_SCAN - debug_item_scan(); -#endif + command_type cmd = get_next_cmd(); - gutch: - flush_input_buffer( FLUSH_BEFORE_COMMAND ); - keyin = getch_with_command_macros(); - } + // [dshaligram] If get_next_cmd encountered a Lua macro binding, your turn + // may be ended by the first invoke of the macro. + if (!you.turn_is_over && cmd != CMD_NEXT_CMD) + process_command( cmd ); - mesclr(); + if ( you.turn_is_over ) { -#ifdef UNIX - // Kludging running and opening as two character sequences - // for Unix systems. This is an easy way out... all the - // player has to do is find a termcap and numlock setting - // that will get curses the numbers from the keypad. This - // will hopefully be easy. + if ( apply_berserk_penalty ) + do_berserk_no_combat_penalty(); - if (keyin == '*') - { - opening = true; - keyin = getch(); - } - else if (keyin == '/') - { - running = true; - keyin = getch(); - } + world_reacts(); + } + else + viewwindow(true, false); +} - // Translate keypad codes into command enums - keyin = key_to_command(keyin); -#else - // Old DOS keypad support - if (keyin == 0) // ALT also works - see ..\KEYTEST.CPP - { - keyin = getch(); - switch (keyin) - { - case 'O': move_x = -1; move_y = 1; break; - case 'P': move_x = 0; move_y = 1; break; - case 'I': move_x = 1; move_y = -1; break; - case 'H': move_x = 0; move_y = -1; break; - case 'G': move_x = -1; move_y = -1; break; - case 'K': move_x = -1; move_y = 0; break; - case 'Q': move_x = 1; move_y = 1; break; - case 'M': move_x = 1; move_y = 0; break; - - case 119: open_door(-1, -1); move_x = 0; move_y = 0; break; - case 141: open_door( 0, -1); move_x = 0; move_y = 0; break; - case 132: open_door( 1, -1); move_x = 0; move_y = 0; break; - case 116: open_door( 1, 0); move_x = 0; move_y = 0; break; - case 118: open_door( 1, 1); move_x = 0; move_y = 0; break; - case 145: open_door( 0, 1); move_x = 0; move_y = 0; break; - case 117: open_door(-1, 1); move_x = 0; move_y = 0; break; - case 115: open_door(-1, 0); move_x = 0; move_y = 0; break; - - case 76: - case 'S': - keyin = '.'; - goto get_keyin_again; - } +static int toggle_flag( bool* flag, const char* flagname ) { + char buf[INFO_SIZE]; + *flag = !(*flag); + sprintf( buf, "%s is now %s.", flagname, + (*flag) ? "on" : "off" ); + mpr(buf); + return *flag; +} - keyin = 128; - } -#endif - } +static void go_upstairs() { + if (grd[you.x_pos][you.y_pos] == DNGN_ENTER_SHOP) { + shop(); + return; } - - if (keyin != 128) - { - move_x = 0; - move_y = 0; - you.turn_is_over = 0; + else if ((grd[you.x_pos][you.y_pos] < DNGN_STONE_STAIRS_UP_I + || grd[you.x_pos][you.y_pos] > DNGN_ROCK_STAIRS_UP) + && (grd[you.x_pos][you.y_pos] < DNGN_RETURN_FROM_ORCISH_MINES + || grd[you.x_pos][you.y_pos] >= 150)) { + mpr( "You can't go up here!" ); + return; } -#ifndef UNIX - get_keyin_again: -#endif //jmf: just stops an annoying gcc warning + tag_followers(); // only those beside us right now can follow + start_delay( DELAY_ASCENDING_STAIRS, + 1 + (you.burden_state > BS_UNENCUMBERED) ); +} - if (is_userfunction(keyin)) - { - run_macro(get_userfunction(keyin)); - keyin = 128; +static void go_downstairs() { + + if ((grd[you.x_pos][you.y_pos] < DNGN_ENTER_LABYRINTH + || grd[you.x_pos][you.y_pos] > DNGN_ROCK_STAIRS_DOWN) + && grd[you.x_pos][you.y_pos] != DNGN_ENTER_HELL + && ((grd[you.x_pos][you.y_pos] < DNGN_ENTER_DIS + || grd[you.x_pos][you.y_pos] > DNGN_TRANSIT_PANDEMONIUM) + && grd[you.x_pos][you.y_pos] != DNGN_STONE_ARCH) + && !(grd[you.x_pos][you.y_pos] >= DNGN_ENTER_ORCISH_MINES + && grd[you.x_pos][you.y_pos] < DNGN_RETURN_FROM_ORCISH_MINES)) { + mpr( "You can't go down here!" ); + return; } - - switch (keyin) - { - case CONTROL('Y'): - case CMD_OPEN_DOOR_UP_RIGHT: - open_door(-1, -1); move_x = 0; move_y = 0; break; - - case CONTROL('K'): - case CMD_OPEN_DOOR_UP: - open_door( 0, -1); move_x = 0; move_y = 0; break; - case CONTROL('U'): - case CMD_OPEN_DOOR_UP_LEFT: - open_door( 1, -1); move_x = 0; move_y = 0; break; - - case CONTROL('L'): - case CMD_OPEN_DOOR_RIGHT: - open_door( 1, 0); move_x = 0; move_y = 0; break; - - case CONTROL('N'): - case CMD_OPEN_DOOR_DOWN_RIGHT: - open_door( 1, 1); move_x = 0; move_y = 0; break; - - case CONTROL('J'): - case CMD_OPEN_DOOR_DOWN: - open_door( 0, 1); move_x = 0; move_y = 0; break; + tag_followers(); // only those beside us right now can follow + start_delay( DELAY_DESCENDING_STAIRS, + 1 + (you.burden_state > BS_UNENCUMBERED), + you.your_level ); +} - case CONTROL('B'): - case CMD_OPEN_DOOR_DOWN_LEFT: - open_door(-1, 1); move_x = 0; move_y = 0; break; +static void experience_check() { + snprintf( info, INFO_SIZE, "You are a level %d %s %s.", + you.experience_level, + species_name(you.species,you.experience_level), + you.class_name); + mpr(info); + + if (you.experience_level < 27) { + int xp_needed = (exp_needed(you.experience_level+2)-you.experience)+1; + snprintf( info, INFO_SIZE, + "Level %d requires %ld experience (%d point%s to go!)", + you.experience_level + 1, + exp_needed(you.experience_level + 2) + 1, + xp_needed, + (xp_needed > 1) ? "s" : ""); + mpr(info); + } + else { + mpr( "I'm sorry, level 27 is as high as you can go." ); + mpr( "With the way you've been playing, I'm surprised you got this far." ); + } - case CONTROL('H'): - case CMD_OPEN_DOOR_LEFT: - open_door(-1, 0); move_x = 0; move_y = 0; break; + if (you.real_time != -1) { + const time_t curr = you.real_time + (time(NULL) - you.start_time); + char buff[200]; - case 'b': case CMD_MOVE_DOWN_LEFT: move_x = -1; move_y = 1; break; - case 'j': case CMD_MOVE_DOWN: move_y = 1; move_x = 0; break; - case 'u': case CMD_MOVE_UP_RIGHT: move_x = 1; move_y = -1; break; - case 'k': case CMD_MOVE_UP: move_y = -1; move_x = 0; break; - case 'y': case CMD_MOVE_UP_LEFT: move_y = -1; move_x = -1; break; - case 'h': case CMD_MOVE_LEFT: move_x = -1; move_y = 0; break; - case 'n': case CMD_MOVE_DOWN_RIGHT: move_y = 1; move_x = 1; break; - case 'l': case CMD_MOVE_RIGHT: move_x = 1; move_y = 0; break; + make_time_string( curr, buff, sizeof(buff) ); - case CMD_REST: - // Yes this is backwards, but everyone here is used to using - // straight 5s for long rests... might need to define a roguelike - // rest key and get people switched over. -- bwr + snprintf( info, INFO_SIZE, "Play time: %s (%ld turns)", + buff, you.num_turns ); -#ifdef UNIX - if (!running && !opening) - start_running( RDIR_REST, 100 ); - else - { - search_around(); - move_x = 0; - move_y = 0; - you.turn_is_over = 1; - } + mpr( info ); + } +#ifdef DEBUG_DIAGNOSTICS + if (wearing_amulet(AMU_THE_GOURMAND)) + mprf(MSGCH_DIAGNOSTICS, "Gourmand charge: %d", + you.duration[DUR_GOURMAND]); #endif - break; - - case 'B': case CMD_RUN_DOWN_LEFT: - start_running( RDIR_DOWN_LEFT, 2 ); break; - - case 'J': case CMD_RUN_DOWN: - start_running( RDIR_DOWN, 2 ); break; - - case 'U': case CMD_RUN_UP_RIGHT: - start_running( RDIR_UP_RIGHT, 2 ); break; - - case 'K': case CMD_RUN_UP: - start_running( RDIR_UP, 2 ); break; - - case 'Y': case CMD_RUN_UP_LEFT: - start_running( RDIR_UP_LEFT, 2 ); break; +} - case 'H': case CMD_RUN_LEFT: - start_running( RDIR_LEFT, 2 ); break; +/* note that in some actions, you don't want to clear afterwards. + e.g. list_jewellery, etc. */ - case 'N': case CMD_RUN_DOWN_RIGHT: - start_running( RDIR_DOWN_RIGHT, 2 ); break; +void process_command( command_type cmd ) { - case 'L': case CMD_RUN_RIGHT: - start_running( RDIR_RIGHT, 2 ); break; + FixedVector < int, 2 > plox; + apply_berserk_penalty = true; + + switch ( cmd ) { + + case CMD_OPEN_DOOR_UP_RIGHT: open_door(-1, -1); break; + case CMD_OPEN_DOOR_UP: open_door( 0, -1); break; + case CMD_OPEN_DOOR_UP_LEFT: open_door( 1, -1); break; + case CMD_OPEN_DOOR_RIGHT: open_door( 1, 0); break; + case CMD_OPEN_DOOR_DOWN_RIGHT: open_door( 1, 1); break; + case CMD_OPEN_DOOR_DOWN: open_door( 0, 1); break; + case CMD_OPEN_DOOR_DOWN_LEFT: open_door(-1, 1); break; + case CMD_OPEN_DOOR_LEFT: open_door(-1, 0); break; + + case CMD_MOVE_DOWN_LEFT: move_player(-1, 1); break; + case CMD_MOVE_DOWN: move_player( 0, 1); break; + case CMD_MOVE_UP_RIGHT: move_player( 1, -1); break; + case CMD_MOVE_UP: move_player( 0, -1); break; + case CMD_MOVE_UP_LEFT: move_player(-1, -1); break; + case CMD_MOVE_LEFT: move_player(-1, 0); break; + case CMD_MOVE_DOWN_RIGHT: move_player( 1, 1); break; + case CMD_MOVE_RIGHT: move_player( 1, 0); break; -#ifdef UNIX - // taken care of via key -> command mapping -#else - // Old DOS keypad support - case '1': start_running( RDIR_DOWN_LEFT, 2 ); break; - case '2': start_running( RDIR_DOWN, 2 ); break; - case '9': start_running( RDIR_UP_RIGHT, 2 ); break; - case '8': start_running( RDIR_UP, 2 ); break; - case '7': start_running( RDIR_UP_LEFT, 2 ); break; - case '4': start_running( RDIR_LEFT, 2 ); break; - case '3': start_running( RDIR_DOWN_RIGHT, 2 ); break; - case '6': start_running( RDIR_RIGHT, 2 ); break; - case '5': start_running( RDIR_REST, 100 ); break; + case CMD_REST: + start_running( RDIR_REST, RMODE_REST_DURATION ); + break; -#endif + case CMD_RUN_DOWN_LEFT: + start_running( RDIR_DOWN_LEFT, RMODE_START ); + break; + case CMD_RUN_DOWN: + start_running( RDIR_DOWN, RMODE_START ); + break; + case CMD_RUN_UP_RIGHT: + start_running( RDIR_UP_RIGHT, RMODE_START ); + break; + case CMD_RUN_UP: + start_running( RDIR_UP, RMODE_START ); + break; + case CMD_RUN_UP_LEFT: + start_running( RDIR_UP_LEFT, RMODE_START ); + break; + case CMD_RUN_LEFT: + start_running( RDIR_LEFT, RMODE_START ); + break; + case CMD_RUN_DOWN_RIGHT: + start_running( RDIR_DOWN_RIGHT, RMODE_START ); + break; + case CMD_RUN_RIGHT: + start_running( RDIR_RIGHT, RMODE_START ); + break; - case CONTROL('A'): case CMD_TOGGLE_AUTOPICKUP: - autopickup_on = !autopickup_on; - strcpy(info, "Autopickup is now "); - strcat(info, (autopickup_on) ? "on" : "off"); - strcat(info, "."); - mpr(info); + toggle_flag( &Options.autopickup_on, "Autopickup"); break; - case CONTROL('C'): - case CMD_CLEAR_MAP: - if (you.level_type != LEVEL_LABYRINTH && you.level_type != LEVEL_ABYSS) - { - mpr("Clearing level map."); - clear_map(); - } + case CMD_TOGGLE_AUTOPRAYER: + toggle_flag( &Options.autoprayer_on, "Autoprayer" ); break; - - case '<': - case CMD_GO_UPSTAIRS: - if (grd[you.x_pos][you.y_pos] == DNGN_ENTER_SHOP) - { - shop(); - break; - } - else if ((grd[you.x_pos][you.y_pos] < DNGN_STONE_STAIRS_UP_I - || grd[you.x_pos][you.y_pos] > DNGN_ROCK_STAIRS_UP) - && (grd[you.x_pos][you.y_pos] < DNGN_RETURN_FROM_ORCISH_MINES - || grd[you.x_pos][you.y_pos] >= 150)) - { - mpr( "You can't go up here!" ); - break; - } - tag_followers(); // only those beside us right now can follow - start_delay( DELAY_ASCENDING_STAIRS, - 1 + (you.burden_state > BS_UNENCUMBERED) ); + case CMD_TOGGLE_NOFIZZLE: + toggle_flag( &Options.fizzlecheck_on, "Fizzle confirmation" ); break; - - case '>': - case CMD_GO_DOWNSTAIRS: - if ((grd[you.x_pos][you.y_pos] < DNGN_ENTER_LABYRINTH - || grd[you.x_pos][you.y_pos] > DNGN_ROCK_STAIRS_DOWN) - && grd[you.x_pos][you.y_pos] != DNGN_ENTER_HELL - && ((grd[you.x_pos][you.y_pos] < DNGN_ENTER_DIS - || grd[you.x_pos][you.y_pos] > DNGN_TRANSIT_PANDEMONIUM) - && grd[you.x_pos][you.y_pos] != DNGN_STONE_ARCH) - && !(grd[you.x_pos][you.y_pos] >= DNGN_ENTER_ORCISH_MINES - && grd[you.x_pos][you.y_pos] < DNGN_RETURN_FROM_ORCISH_MINES)) - { - mpr( "You can't go down here!" ); - break; - } - - tag_followers(); // only those beside us right now can follow - start_delay( DELAY_DESCENDING_STAIRS, - 1 + (you.burden_state > BS_UNENCUMBERED), - you.your_level ); + + case CMD_MAKE_NOTE: + make_user_note(); break; - case 'O': - case CMD_DISPLAY_OVERMAP: - display_overmap(); + case CMD_CLEAR_MAP: + if (you.level_type != LEVEL_LABYRINTH && + you.level_type != LEVEL_ABYSS) { + mpr("Clearing level map."); + clear_map(); + } break; - case 'o': - case CMD_OPEN_DOOR: - open_door(0, 0); - break; - case 'c': - case CMD_CLOSE_DOOR: - close_door(0, 0); - break; + case CMD_GO_UPSTAIRS: go_upstairs(); break; + case CMD_GO_DOWNSTAIRS: go_downstairs(); break; + case CMD_DISPLAY_OVERMAP: display_overmap(); break; + case CMD_OPEN_DOOR: open_door(0, 0); break; + case CMD_CLOSE_DOOR: close_door(0, 0); break; - case 'd': case CMD_DROP: drop(); #ifdef STASH_TRACKING @@ -1205,166 +1075,129 @@ static void input(void) break; #ifdef STASH_TRACKING - case CONTROL('F'): case CMD_SEARCH_STASHES: stashes.search_stashes(); break; - case CONTROL('S'): case CMD_MARK_STASH: if (Options.stash_tracking >= STM_EXPLICIT) stashes.add_stash(-1, -1, true); break; - case CONTROL('E'): case CMD_FORGET_STASH: if (Options.stash_tracking >= STM_EXPLICIT) stashes.no_stash(); break; #endif - case 'D': case CMD_BUTCHER: butchery(); break; - case 'i': case CMD_DISPLAY_INVENTORY: get_invent(-1); break; - case 'I': - case CMD_OBSOLETE_INVOKE: - // We'll leave this message in for a while. Eventually, this - // might be some special for of inventory command, or perhaps - // actual god invocations will be split to here from abilities. -- bwr - mpr( "This command is now 'E'voke wielded item.", MSGCH_WARN ); - break; - - case 'E': case CMD_EVOKE: if (!evoke_wielded()) flush_input_buffer( FLUSH_ON_FAILURE ); break; - case 'g': - case ',': case CMD_PICKUP: pickup(); break; - case ';': case CMD_INSPECT_FLOOR: item_check(';'); break; - case 'w': case CMD_WIELD_WEAPON: wield_weapon(false); break; - case 't': case CMD_THROW: throw_anything(); break; - case 'f': case CMD_FIRE: shoot_thing(); break; - case 'W': case CMD_WEAR_ARMOUR: wear_armour(); break; - case 'T': case CMD_REMOVE_ARMOUR: - { - int index=0; + { + int index=0; - if (armour_prompt("Take off which item?", &index)) - takeoff_armour(index); - } - break; + if (armour_prompt("Take off which item?", &index, OPER_TAKEOFF)) + takeoff_armour(index); + } + break; - case 'R': case CMD_REMOVE_JEWELLERY: remove_ring(); break; - case 'P': + case CMD_WEAR_JEWELLERY: - puton_ring(); + puton_ring(-1, false); break; - case '=': case CMD_ADJUST_INVENTORY: adjust(); - return; + break; - case 'M': case CMD_MEMORISE_SPELL: if (!learn_spell()) flush_input_buffer( FLUSH_ON_FAILURE ); break; - case 'z': case CMD_ZAP_WAND: zap_wand(); break; - case 'e': case CMD_EAT: eat_food(); break; - case 'a': case CMD_USE_ABILITY: if (!activate_ability()) flush_input_buffer( FLUSH_ON_FAILURE ); break; - case 'A': case CMD_DISPLAY_MUTATIONS: display_mutations(); redraw_screen(); break; - case 'v': case CMD_EXAMINE_OBJECT: - original_name(); + examine_object(); break; - case 'p': case CMD_PRAY: pray(); break; - case '^': case CMD_DISPLAY_RELIGION: describe_god( you.religion, true ); redraw_screen(); break; - case '.': case CMD_MOVE_NOWHERE: + case CMD_SEARCH: search_around(); - move_x = 0; - move_y = 0; - you.turn_is_over = 1; + you.turn_is_over = true; break; - case 'q': case CMD_QUAFF: drink(); break; - case 'r': case CMD_READ: read_scroll(); break; - case 'x': case CMD_LOOK_AROUND: mpr("Move the cursor around to observe a square.", MSGCH_PROMPT); mpr("Press '?' for a monster description.", MSGCH_PROMPT); @@ -1376,13 +1209,6 @@ static void input(void) start_travel( lmove.tx, lmove.ty ); break; - case 's': - case CMD_SEARCH: - search_around(); - you.turn_is_over = 1; - break; - - case 'Z': case CMD_CAST_SPELL: /* randart wpns */ if (scan_randarts(RAP_PREVENT_SPELLCASTING)) @@ -1396,23 +1222,20 @@ static void input(void) flush_input_buffer( FLUSH_ON_FAILURE ); break; - case '\'': case CMD_WEAPON_SWAP: wield_weapon(true); break; - // [ds] Waypoints can be added from the level-map, and we need Ctrl+F for - // nobler things. Who uses waypoints, anyway? - // Update: Appears people do use waypoints. Reinstating, on CONTROL('W'). - // This means Ctrl+W is no longer a wizmode trigger, but there's - // always '&'. :-) + // [ds] Waypoints can be added from the level-map, and we need + // Ctrl+F for nobler things. Who uses waypoints, anyway? + // Update: Appears people do use waypoints. Reinstating, on + // CONTROL('W'). This means Ctrl+W is no longer a wizmode + // trigger, but there's always '&'. :-) case CMD_FIX_WAYPOINT: - case CONTROL('W'): travel_cache.add_waypoint(); break; case CMD_INTERLEVEL_TRAVEL: - case CONTROL('G'): if (!can_travel_interlevel()) { mpr("Sorry, you can't auto-travel out of here."); @@ -1422,7 +1245,6 @@ static void input(void) redraw_screen(); break; - case CONTROL('O'): case CMD_EXPLORE: if (you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS) { @@ -1433,7 +1255,6 @@ static void input(void) start_explore(); break; - case 'X': case CMD_DISPLAY_MAP: #if (!DEBUG_DIAGNOSTICS) if (you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS) @@ -1443,44 +1264,37 @@ static void input(void) } #endif plox[0] = 0; - show_map(plox); + show_map(plox, true); redraw_screen(); if (plox[0] > 0) start_travel(plox[0], plox[1]); break; - case '\\': case CMD_DISPLAY_KNOWN_OBJECTS: - check_item_knowledge(); //nothing = check_item_knowledge(); - redraw_screen(); + check_item_knowledge(); break; #ifdef ALLOW_DESTROY_ITEM_COMMAND - case CONTROL('D'): case CMD_DESTROY_ITEM: cmd_destroy_item(); break; #endif - case CONTROL('P'): case CMD_REPLAY_MESSAGES: replay_messages(); redraw_screen(); break; - case CONTROL('R'): case CMD_REDRAW_SCREEN: redraw_screen(); break; - case CONTROL('X'): case CMD_SAVE_GAME_NOW: mpr("Saving game... please wait."); save_game(true); break; #ifdef USE_UNIX_SIGNALS - case CONTROL('Z'): case CMD_SUSPEND_GAME: // CTRL-Z suspend behaviour is implemented here, // because we want to have CTRL-Y available... @@ -1493,71 +1307,37 @@ static void input(void) break; #endif - case '?': case CMD_DISPLAY_COMMANDS: list_commands(false); redraw_screen(); break; - case 'C': case CMD_EXPERIENCE_CHECK: - snprintf( info, INFO_SIZE, "You are a level %d %s %s.", you.experience_level, - species_name(you.species,you.experience_level), you.class_name); - mpr(info); - - if (you.experience_level < 27) - { - int xp_needed = (exp_needed(you.experience_level+2) - you.experience) + 1; - snprintf( info, INFO_SIZE, "Level %d requires %ld experience (%d point%s to go!)", - you.experience_level + 1, - exp_needed(you.experience_level + 2) + 1, - xp_needed, - (xp_needed > 1) ? "s" : ""); - mpr(info); - } - else - { - mpr( "I'm sorry, level 27 is as high as you can go." ); - mpr( "With the way you've been playing, I'm surprised you got this far." ); - } - - if (you.real_time != -1) - { - const time_t curr = you.real_time + (time(NULL) - you.start_time); - char buff[200]; - - make_time_string( curr, buff, sizeof(buff) ); - - snprintf( info, INFO_SIZE, "Play time: %s (%ld turns)", - buff, you.num_turns ); - - mpr( info ); - } + experience_check(); break; - - case '!': case CMD_SHOUT: yell(); /* in effects.cc */ break; - case '@': case CMD_DISPLAY_CHARACTER_STATUS: display_char_status(); break; + + case CMD_RESISTS_SCREEN: + resists_screen(); + break; - case 'm': case CMD_DISPLAY_SKILLS: show_skills(); redraw_screen(); break; - case '#': case CMD_CHARACTER_DUMP: char name_your[kNameLen+1]; strncpy(name_your, you.your_name, kNameLen); - name_your[kNameLen] = '\0'; + name_your[kNameLen] = 0; if (dump_char( name_your, false )) strcpy(info, "Char dumped successfully."); else @@ -1566,134 +1346,78 @@ static void input(void) break; #ifdef USE_MACROS - case '`': case CMD_MACRO_ADD: macro_add_query(); break; - case '~': - case CMD_MACRO_SAVE: - mpr("Saving macros."); - macro_save(); - break; #endif - case ')': case CMD_LIST_WEAPONS: list_weapons(); - return; + break; - case ']': + case CMD_INSCRIBE_ITEM: + inscribe_item(); + break; + case CMD_LIST_ARMOUR: list_armour(); - return; + break; - case '"': case CMD_LIST_JEWELLERY: list_jewellery(); - return; + break; #ifdef WIZARD case CMD_WIZARD: - case '&': handle_wizard_command(); break; #endif - case 'S': case CMD_SAVE_GAME: - if (yesno("Save game and exit?", false, 'n')) + if (yesno("Save game and exit?", true, 'n')) save_game(true); break; - case 'Q': case CMD_QUIT: quit_game(); break; - case 'V': case CMD_GET_VERSION: version(); break; - case 128: // Can't use this char -- it's the special value 128 - break; - - default: case CMD_NO_CMD: + default: mpr("Unknown command."); break; } +} +static void prep_input() { + you.time_taken = player_speed(); + you.shield_blocks = 0; // no blocks this round #ifdef UNIX - // New Unix keypad stuff - if (running) - { - int dir = -1; - - // XXX: ugly hack to interface this with the new running code. -- bwr - for (int i = 0; i < 8; i++) - { - if (Compass[i].x == move_x && Compass[i].y == move_y) - { - dir = i; - break; - } - } - - if (dir != -1) - { - start_running( dir, 2 ); - move_x = 0; - move_y = 0; - } - } - else if (opening) - { - open_door(move_x, move_y); - move_x = 0; - move_y = 0; - } + update_screen(); +#else + window( 1, 1, 80, get_number_of_lines() ); #endif - if (move_x != 0 || move_y != 0) - move_player(move_x, move_y); - else if (you.turn_is_over) // we did something other than move/attack - do_berserk_no_combat_penalty(); - - if (!you.turn_is_over) - { - viewwindow(1, false); - return; - } - - if (you.num_turns != -1) - you.num_turns++; - - //if (random2(10) < you.skills [SK_TRAPS_DOORS] + 2) search_around(); - - stealth = check_stealth(); - -#if 0 - // too annoying for regular diagnostics - snprintf( info, INFO_SIZE, "stealth: %d", stealth ); - mpr( info, MSGCH_DIAGNOSTICS ); -#endif + textcolor(LIGHTGREY); - if (you.special_wield != SPWLD_NONE) - special_wielded(); + set_redraw_status( REDRAW_LINE_2_MASK | REDRAW_LINE_3_MASK ); + print_stats(); +} - if (one_chance_in(10)) - { - // this is instantaneous - if (player_teleport() > 0 && one_chance_in(100 / player_teleport())) - you_teleport2( true ); - else if (you.level_type == LEVEL_ABYSS && one_chance_in(30)) - you_teleport2( false, true ); // to new area of the Abyss +static void decrement_durations() +{ + if (wearing_amulet(AMU_THE_GOURMAND)) + { + if (you.duration[DUR_GOURMAND] < GOURMAND_MAX && coinflip()) + you.duration[DUR_GOURMAND]++; } - - if (env.cgrid[you.x_pos][you.y_pos] != EMPTY_CLOUD) - in_a_cloud(); + else + you.duration[DUR_GOURMAND] = 0; if (you.duration[DUR_REPEL_UNDEAD] > 1) you.duration[DUR_REPEL_UNDEAD]--; @@ -1722,7 +1446,7 @@ static void input(void) const int res_fire = player_res_fire(); mpr( "You are covered in liquid flames!", MSGCH_WARN ); - scrolls_burn(8, OBJ_SCROLLS); + expose_player_to_element(BEAM_NAPALM, 12); if (res_fire > 0) { @@ -1733,13 +1457,13 @@ static void input(void) if (res_fire <= 0) { ouch(((random2avg(9, 2) + 1) * you.time_taken) / 10, 0, - KILLED_BY_BURNING); + KILLED_BY_BURNING); } if (res_fire < 0) { ouch(((random2avg(9, 2) + 1) * you.time_taken) / 10, 0, - KILLED_BY_BURNING); + KILLED_BY_BURNING); } if (you.duration[DUR_CONDENSATION_SHIELD] > 0) @@ -1815,11 +1539,11 @@ static void input(void) you.duration[DUR_PRAYER]--; else if (you.duration[DUR_PRAYER] == 1) { - god_speaks(you.religion, "Your prayer is over."); + mpr( "Your prayer is over.", MSGCH_PRAY, you.religion ); + about_to_autopray = true; you.duration[DUR_PRAYER] = 0; } - //jmf: more flexible weapon branding code if (you.duration[DUR_WEAPON_BRAND] > 1) you.duration[DUR_WEAPON_BRAND]--; @@ -1839,16 +1563,11 @@ static void input(void) switch (temp_effect) { case SPWPN_VORPAL: - if (damage_type(you.inv[you.equip[EQ_WEAPON]].base_type, - you.inv[you.equip[EQ_WEAPON]].sub_type) != DVORP_CRUSHING) - { + if (get_vorpal_type(you.inv[you.equip[EQ_WEAPON]]) + == DVORP_SLICING) strcat(info, " seems blunter."); - } else - { - //jmf: for Maxwell's Silver Hammer strcat(info, " feels lighter."); - } break; case SPWPN_FLAMING: @@ -1865,7 +1584,8 @@ static void input(void) break; case SPWPN_DISTORTION: strcat( info, " seems straighter." ); - miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, "a distortion effect" ); + // [dshaligram] Makes the brand unusable + // miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, "a distortion effect" ); break; default: strcat(info, " seems inexplicably less special."); @@ -1938,7 +1658,7 @@ static void input(void) you.duration[DUR_STONEMAIL]--; if (you.duration[DUR_STONEMAIL] == 6) { - mpr("Your scaley stone armour is starting to flake away.", MSGCH_DURATION); + mpr("Your scaly stone armour is starting to flake away.", MSGCH_DURATION); you.redraw_armour_class = 1; if (coinflip()) you.duration[DUR_STONEMAIL]--; @@ -1946,7 +1666,7 @@ static void input(void) } else if (you.duration[DUR_STONEMAIL] == 1) { - mpr("Your scaley stone armour disappears.", MSGCH_DURATION); + mpr("Your scaly stone armour disappears.", MSGCH_DURATION); you.duration[DUR_STONEMAIL] = 0; you.redraw_armour_class = 1; burden_change(); @@ -1978,7 +1698,8 @@ static void input(void) { you.duration[DUR_CONDENSATION_SHIELD]--; - scrolls_burn( 1, OBJ_POTIONS ); + // [dshaligram] Makes this spell useless + // scrolls_burn( 1, OBJ_POTIONS ); if (player_res_cold() < 0) { @@ -2034,7 +1755,6 @@ static void input(void) { mpr("You feel uncertain.", MSGCH_DURATION); you.duration[DUR_CONTROL_TELEPORT] = 0; - you.attribute[ATTR_CONTROL_TELEPORT]--; } if (you.duration[DUR_RESIST_POISON] > 1) @@ -2069,20 +1789,6 @@ static void input(void) you.duration[DUR_DEATH_CHANNEL] = 0; } - if (you.duration[DUR_SHUGGOTH_SEED_RELOAD] > 0) //jmf: added - you.duration[DUR_SHUGGOTH_SEED_RELOAD]--; - - if (you.duration[DUR_INFECTED_SHUGGOTH_SEED] > 1) - you.duration[DUR_INFECTED_SHUGGOTH_SEED]--; - else if (you.duration[DUR_INFECTED_SHUGGOTH_SEED] == 1) - { - //jmf: use you.max_hp instead? or would that be too evil? - you.duration[DUR_INFECTED_SHUGGOTH_SEED] = 0; - mpr("A horrible thing bursts from your chest!", MSGCH_WARN); - ouch(1 + you.hp / 2, 0, KILLED_BY_SHUGGOTH); - make_shuggoth(you.x_pos, you.y_pos, 1 + you.hp / 2); - } - if (you.invis > 1) { you.invis--; @@ -2139,7 +1845,7 @@ static void input(void) you.berserker = 0; //jmf: guilty for berserking /after/ berserk - naughty( NAUGHTY_STIMULANTS, 6 + random2(6) ); + did_god_conduct( DID_STIMULANTS, 6 + random2(6) ); // // Sometimes berserk leaves us physically drained @@ -2155,23 +1861,55 @@ static void input(void) // this should make it a bit more interesting for // Crusaders again. // - similarly for the amulet - int chance = 10 + you.mutation[MUT_BERSERK] * 25 - + (wearing_amulet( AMU_RAGE ) ? 10 : 0) - + (player_has_spell( SPELL_BERSERKER_RAGE ) ? 5 : 0); - + int chances[4]; + chances[0] = 10; + chances[1] = you.mutation[MUT_BERSERK] * 25; + chances[2] = (wearing_amulet( AMU_RAGE ) ? 10 : 0); + chances[3] = (player_has_spell( SPELL_BERSERKER_RAGE ) ? 5 : 0); + const char* reasons[4] = { + "You struggle, and manage to stay standing.", + "Your mutated body refuses to collapse.", + "You feel your neck pulse as blood rushes through your body.", + "Your mind masters your body." + }; + const int chance = chances[0] + chances[1] + chances[2] + chances[3]; + + if (you.berserk_penalty == NO_BERSERK_PENALTY) + mpr("The very source of your rage keeps you on your feet."); // Note the beauty of Trog! They get an extra save that's at // the very least 20% and goes up to 100%. - if (you.berserk_penalty == NO_BERSERK_PENALTY - || (you.religion == GOD_TROG && you.piety > random2(150)) - || !one_chance_in( chance )) + else if ( you.religion == GOD_TROG && you.piety > random2(150) && + !player_under_penance() ) + mpr("Trog's vigour flows through your veins."); + else if ( !one_chance_in(chance) ) { - mpr("You are exhausted."); + // Survived the probabilistic check. + // Figure out why. + + int cause = random2(chance); // philosophically speaking... + int i; + for ( i = 0; i < 4; ++i ) + { + if ( cause < chances[i] ) + { + // only print a reason if it actually exists + if ( reasons[i][0] != 0 ) + mpr(reasons[i]); + break; + } + else + cause -= chances[i]; + } + if (i == 4) + mpr("Oops. Couldn't find a reason. Well, lucky you."); } else { mpr("You pass out from exhaustion.", MSGCH_WARN); you.paralysis += roll_dice( 1, 4 ); } + if ( you.paralysis == 0 ) + mpr("You are exhausted.", MSGCH_WARN); // this resets from an actual penalty or from NO_BERSERK_PENALTY you.berserk_penalty = 0; @@ -2229,20 +1967,8 @@ static void input(void) burden_change(); you.duration[DUR_CONTROLLED_FLIGHT] = 0; - if (grd[you.x_pos][you.y_pos] == DNGN_LAVA - || grd[you.x_pos][you.y_pos] == DNGN_DEEP_WATER - || grd[you.x_pos][you.y_pos] == DNGN_SHALLOW_WATER) - { - if (you.species == SP_MERFOLK - && grd[you.x_pos][you.y_pos] != DNGN_LAVA) - { - mpr("You dive into the water and return to your normal form."); - merfolk_start_swimming(); - } - - if (grd[you.x_pos][you.y_pos] != DNGN_SHALLOW_WATER) - fall_into_a_pool(true, grd[you.x_pos][you.y_pos]); - } + // re-enter the terrain: + move_player_to_grid( you.x_pos, you.y_pos, false, true, true ); } if (you.rotting > 0) @@ -2332,6 +2058,47 @@ static void input(void) more(); } } +} + +/* Perhaps we should write functions like: update_repel_undead(), + update_liquid_flames(), and so on. Even better, we could have a + vector of callback functions (or objects) which get installed + at some point. +*/ + +static void world_reacts() { + + bool its_quiet; //jmf: for silence messages + + if (you.num_turns != -1) + you.num_turns++; + + //if (random2(10) < you.skills [SK_TRAPS_DOORS] + 2) search_around(); + + stealth = check_stealth(); + +#if 0 + // too annoying for regular diagnostics + snprintf( info, INFO_SIZE, "stealth: %d", stealth ); + mpr( info, MSGCH_DIAGNOSTICS ); +#endif + + if (you.special_wield != SPWLD_NONE) + special_wielded(); + + if (one_chance_in(10)) + { + // this is instantaneous + if (player_teleport() > 0 && one_chance_in(100 / player_teleport())) + you_teleport2( true ); + else if (you.level_type == LEVEL_ABYSS && one_chance_in(30)) + you_teleport2( false, true ); // to new area of the Abyss + } + + if (env.cgrid[you.x_pos][you.y_pos] != EMPTY_CLOUD) + in_a_cloud(); + + decrement_durations(); const int food_use = player_hunger_rate(); @@ -2347,14 +2114,10 @@ static void input(void) while (tmp >= 100) { - if (you.hp >= you.hp_max - 1 - && you.running && you.run_x == 0 && you.run_y == 0) - { - stop_running(); - } - inc_hp(1, false); tmp -= 100; + + you.running.check_hp(); } ASSERT( tmp >= 0 && tmp < 100 ); @@ -2369,19 +2132,18 @@ static void input(void) while (tmp >= 100) { - if (you.magic_points >= you.max_magic_points - 1 - && you.running && you.run_x == 0 && you.run_y == 0) - { - stop_running(); - } - inc_mp(1, false); tmp -= 100; + + you.running.check_mp(); } ASSERT( tmp >= 0 && tmp < 100 ); you.magic_points_regeneration = static_cast< unsigned char >( tmp ); + // If you're wielding a rod, it'll gradually recharge. + recharge_rods(); + viewwindow(1, true); handle_monsters(); @@ -2407,46 +2169,6 @@ static void input(void) if (you.fire_shield > 0) manage_fire_shield(); - // There used to be signs of intent to have statues as some sort - // of more complex state machine... I'm boiling them down to bare - // basics for now. -- bwr - if (Visible_Statue[ STATUE_SILVER ]) - { - interrupt_activity( AI_STATUE ); - - if ((!you.invis && one_chance_in(3)) || one_chance_in(5)) - { - char wc[30]; - - weird_colours( random2(256), wc ); - snprintf(info, INFO_SIZE, "The silver statue's eyes glow %s.", wc); - mpr( info, MSGCH_WARN ); - - create_monster( summon_any_demon((coinflip() ? DEMON_COMMON - : DEMON_LESSER)), - ENCH_ABJ_V, BEH_HOSTILE, - you.x_pos, you.y_pos, - MHITYOU, 250 ); - } - - Visible_Statue[ STATUE_SILVER ] = 0; - } - - if (Visible_Statue[ STATUE_ORANGE_CRYSTAL ]) - { - interrupt_activity( AI_STATUE ); - - if ((!you.invis && coinflip()) || one_chance_in(4)) - { - mpr("A hostile presence attacks your mind!", MSGCH_WARN); - - miscast_effect( SPTYP_DIVINATION, random2(15), random2(150), 100, - "an orange crystal statue" ); - } - - Visible_Statue[ STATUE_ORANGE_CRYSTAL ] = 0; - } - // food death check: if (you.is_undead != US_UNDEAD && you.hunger <= 500) { @@ -2518,20 +2240,284 @@ static void input(void) if (you.level_type == LEVEL_PANDEMONIUM && one_chance_in(50)) pandemonium_mons(); - // No monsters in the Labyrinth, or Ecumenical Temple + // No monsters in the Labyrinth, or the Ecumenical Temple return; } +static command_type get_next_cmd() { + if (Options.autoprayer_on && you.duration[DUR_PRAYER] == 0 && + just_autoprayed == false && you.religion != GOD_NO_GOD && + grid_altar_god( grd[you.x_pos][you.y_pos] ) == GOD_NO_GOD && + i_feel_safe()) + { + just_autoprayed = true; + about_to_autopray = false; + return CMD_PRAY; + } + if ( just_autoprayed && you.duration[DUR_PRAYER] == 0 ) + { + /* oops */ + mpr("Autoprayer failed, deactivating.", MSGCH_WARN); + Options.autoprayer_on = false; + } + just_autoprayed = false; + if ( Options.autoprayer_on && about_to_autopray && + you.religion != GOD_NO_GOD && + you.duration[DUR_PRAYER] == 0 ) + { + mpr("Autoprayer not resuming prayer.", MSGCH_WARN); + about_to_autopray = false; + } + +#if DEBUG_DIAGNOSTICS + // save hunger at start of round + // for use with hunger "delta-meter" in output.cc + you.old_hunger = you.hunger; +#endif + +#if DEBUG_ITEM_SCAN + debug_item_scan(); +#endif + keycode_type keyin = get_next_keycode(); + + if (is_userfunction(keyin)) + { + run_macro(get_userfunction(keyin)); + return (CMD_NEXT_CMD); + } + + return keycode_to_command(keyin); +} + +/* for now, this is an extremely yucky hack */ +command_type keycode_to_command( keycode_type key ) { + switch ( key ) { + case 'b': return CMD_MOVE_DOWN_LEFT; + case 'h': return CMD_MOVE_LEFT; + case 'j': return CMD_MOVE_DOWN; + case 'k': return CMD_MOVE_UP; + case 'l': return CMD_MOVE_RIGHT; + case 'n': return CMD_MOVE_DOWN_RIGHT; + case 'u': return CMD_MOVE_UP_RIGHT; + case 'y': return CMD_MOVE_UP_LEFT; + + case 'a': return CMD_USE_ABILITY; + case 'c': return CMD_CLOSE_DOOR; + case 'd': return CMD_DROP; + case 'e': return CMD_EAT; + case 'f': return CMD_FIRE; + case 'g': return CMD_PICKUP; + case 'i': return CMD_DISPLAY_INVENTORY; + case 'm': return CMD_DISPLAY_SKILLS; + case 'o': return CMD_OPEN_DOOR; + case 'p': return CMD_PRAY; + case 'q': return CMD_QUAFF; + case 'r': return CMD_READ; + case 's': return CMD_SEARCH; + case 't': return CMD_THROW; + case 'v': return CMD_EXAMINE_OBJECT; + case 'w': return CMD_WIELD_WEAPON; + case 'x': return CMD_LOOK_AROUND; + case 'z': return CMD_ZAP_WAND; + + case 'B': return CMD_RUN_DOWN_LEFT; + case 'H': return CMD_RUN_LEFT; + case 'J': return CMD_RUN_DOWN; + case 'K': return CMD_RUN_UP; + case 'L': return CMD_RUN_RIGHT; + case 'N': return CMD_RUN_DOWN_RIGHT; + case 'U': return CMD_RUN_UP_RIGHT; + case 'Y': return CMD_RUN_UP_LEFT; + + case 'A': return CMD_DISPLAY_MUTATIONS; + case 'C': return CMD_EXPERIENCE_CHECK; + case 'D': return CMD_BUTCHER; + case 'E': return CMD_EVOKE; + case 'F': return CMD_NO_CMD; + case 'G': return CMD_NO_CMD; + case 'I': return CMD_NO_CMD; + case 'M': return CMD_MEMORISE_SPELL; + case 'O': return CMD_DISPLAY_OVERMAP; + case 'P': return CMD_WEAR_JEWELLERY; + case 'Q': return CMD_QUIT; + case 'R': return CMD_REMOVE_JEWELLERY; + case 'S': return CMD_SAVE_GAME; + case 'T': return CMD_REMOVE_ARMOUR; + case 'V': return CMD_GET_VERSION; + case 'W': return CMD_WEAR_ARMOUR; + case 'X': return CMD_DISPLAY_MAP; + case 'Z': return CMD_CAST_SPELL; + + case '.': return CMD_MOVE_NOWHERE; + case '<': return CMD_GO_UPSTAIRS; + case '>': return CMD_GO_DOWNSTAIRS; + case '@': return CMD_DISPLAY_CHARACTER_STATUS; + case '%': return CMD_RESISTS_SCREEN; + case ',': return CMD_PICKUP; + case ':': return CMD_MAKE_NOTE; + case ';': return CMD_INSPECT_FLOOR; + case '!': return CMD_SHOUT; + case '^': return CMD_DISPLAY_RELIGION; + case '#': return CMD_CHARACTER_DUMP; + case '=': return CMD_ADJUST_INVENTORY; + case '?': return CMD_DISPLAY_COMMANDS; + case '~': return CMD_MACRO_ADD; + case '&': return CMD_WIZARD; + case '"': return CMD_LIST_JEWELLERY; + case '{': return CMD_INSCRIBE_ITEM; + case '[': return CMD_LIST_ARMOUR; + case ']': return CMD_LIST_ARMOUR; + case ')': return CMD_LIST_WEAPONS; + case '(': return CMD_LIST_WEAPONS; + case '\\': return CMD_DISPLAY_KNOWN_OBJECTS; + case '\'': return CMD_WEAPON_SWAP; + + case '0': return CMD_NO_CMD; + +#ifdef UNIX + case '1': return CMD_MOVE_DOWN_LEFT; + case '2': return CMD_MOVE_DOWN; + case '3': return CMD_MOVE_DOWN_RIGHT; + case '4': return CMD_MOVE_LEFT; + case '5': return CMD_REST; + case '6': return CMD_MOVE_RIGHT; + case '7': return CMD_MOVE_UP_LEFT; + case '8': return CMD_MOVE_UP; + case '9': return CMD_MOVE_UP_RIGHT; +#else + case '1': return CMD_RUN_DOWN_LEFT; + case '2': return CMD_RUN_DOWN; + case '3': return CMD_RUN_DOWN_RIGHT; + case '4': return CMD_RUN_LEFT; + case '5': return CMD_REST; + case '6': return CMD_RUN_RIGHT; + case '7': return CMD_RUN_UP_LEFT; + case '8': return CMD_RUN_UP; + case '9': return CMD_RUN_UP_RIGHT; +#endif + + case CONTROL('B'): return CMD_OPEN_DOOR_DOWN_LEFT; + case CONTROL('H'): return CMD_OPEN_DOOR_LEFT; + case CONTROL('J'): return CMD_OPEN_DOOR_DOWN; + case CONTROL('K'): return CMD_OPEN_DOOR_UP; + case CONTROL('L'): return CMD_OPEN_DOOR_RIGHT; + case CONTROL('N'): return CMD_OPEN_DOOR_DOWN_RIGHT; + case CONTROL('U'): return CMD_OPEN_DOOR_UP_LEFT; + case CONTROL('Y'): return CMD_OPEN_DOOR_UP_RIGHT; + + case CONTROL('A'): return CMD_TOGGLE_AUTOPICKUP; + case CONTROL('C'): return CMD_CLEAR_MAP; + case CONTROL('D'): return CMD_NO_CMD; + case CONTROL('E'): return CMD_FORGET_STASH; + case CONTROL('F'): return CMD_SEARCH_STASHES; + case CONTROL('G'): return CMD_INTERLEVEL_TRAVEL; + case CONTROL('I'): return CMD_NO_CMD; + case CONTROL('M'): return CMD_NO_CMD; + case CONTROL('O'): return CMD_EXPLORE; + case CONTROL('P'): return CMD_REPLAY_MESSAGES; + case CONTROL('Q'): return CMD_NO_CMD; + case CONTROL('R'): return CMD_REDRAW_SCREEN; + case CONTROL('S'): return CMD_MARK_STASH; + case CONTROL('T'): return CMD_TOGGLE_NOFIZZLE; + case CONTROL('V'): return CMD_TOGGLE_AUTOPRAYER; + case CONTROL('W'): return CMD_FIX_WAYPOINT; + case CONTROL('X'): return CMD_SAVE_GAME_NOW; + case CONTROL('Z'): return CMD_SUSPEND_GAME; + default: return CMD_NO_CMD; + } +} + +#ifdef UNIX +static keycode_type numpad2vi(keycode_type key) +{ + if (key >= '1' && key <= '9') + { + const char *vikeys = "bjnh.lyku"; + return keycode_type(vikeys[key - '1']); + } + return (key); +} +#endif + +keycode_type get_next_keycode() { + + keycode_type keyin; + + flush_input_buffer( FLUSH_BEFORE_COMMAND ); + keyin = getch_with_command_macros(); + +#ifdef UNIX + // Kludging running and opening as two character sequences + // for Unix systems. This is an easy way out... all the + // player has to do is find a termcap and numlock setting + // that will get curses the numbers from the keypad. This + // will hopefully be easy. + + /* can we say yuck? -- haranp */ + if (keyin == '*') + { + keyin = getch(); + // return control-key + keyin = CONTROL(toupper(numpad2vi(keyin))); + } + else if (keyin == '/') + { + keyin = getch(); + // return shift-key + keyin = toupper(numpad2vi(keyin)); + } +#else + // Old DOS keypad support + if (keyin == 0) + { + /* FIXME haranp - hackiness */ + const char DOSidiocy[10] = { "OPQKSMGHI" }; + const char DOSunidiocy[10] = { "bjnh.lyku" }; + const int DOScontrolidiocy[9] = { + 117, 145, 118, 115, 76, 116, 119, 141, 132 + }; + keyin = getch(); + for (int j = 0; j < 9; ++j ) { + if (keyin == DOSidiocy[j]) { + keyin = DOSunidiocy[j]; + break; + } + if (keyin == DOScontrolidiocy[j]) { + keyin = CONTROL(toupper(DOSunidiocy[j])); + break; + } + } + } +#endif + mesclr(); + + return keyin; +} + +static void middle_input() { + if (Options.stash_tracking) + stashes.update_visible_stashes( + Options.stash_tracking == STM_ALL? + StashTracker::ST_AGGRESSIVE : + StashTracker::ST_PASSIVE); +} + /* Opens doors and handles some aspects of untrapping. If either move_x or move_y are non-zero, the pair carries a specific direction for the door to be opened (eg if you type ctrl - dir). */ -static void open_door(char move_x, char move_y) +static void open_door(int move_x, int move_y, bool check_confused) { struct dist door_move; int dx, dy; // door x, door y + if (check_confused && you.conf && !one_chance_in(3)) + { + move_x = random2(3) - 1; + move_y = random2(3) - 1; + } + door_move.dx = move_x; door_move.dy = move_y; @@ -2546,7 +2532,7 @@ static void open_door(char move_x, char move_y) if (mon != NON_MONSTER && !mons_has_ench( &menv[mon], ENCH_SUBMERGED )) { you_attack(mgrd[dx][dy], true); - you.turn_is_over = 1; + you.turn_is_over = true; if (you.berserk_penalty != NO_BERSERK_PENALTY) you.berserk_penalty = 0; @@ -2595,20 +2581,20 @@ static void open_door(char move_x, char move_y) } grd[dx][dy] = DNGN_OPEN_DOOR; - you.turn_is_over = 1; + you.turn_is_over = true; } else { mpr("You swing at nothing."); make_hungry(3, true); - you.turn_is_over = 1; + you.turn_is_over = true; } } // end open_door() /* Similar to open_door. Can you spot the difference? */ -static void close_door(char door_x, char door_y) +static void close_door(int door_x, int door_y) { struct dist door_move; int dx, dy; // door x, door y @@ -2638,7 +2624,8 @@ static void close_door(char door_x, char door_y) { if (mgrd[dx][dy] != NON_MONSTER) { - // Need to make sure that turn_is_over = 1 if creature is invisible + // Need to make sure that turn_is_over is set if creature is + // invisible mpr("There's a creature in the doorway!"); door_move.dx = 0; door_move.dy = 0; @@ -2667,7 +2654,7 @@ static void close_door(char door_x, char door_y) } grd[dx][dy] = DNGN_CLOSED_DOOR; - you.turn_is_over = 1; + you.turn_is_over = true; } else { @@ -2684,8 +2671,8 @@ static bool initialise(void) int i = 0, j = 0; // counter variables {dlb} - your_sign = '@'; - your_colour = LIGHTGREY; + you.symbol = '@'; + you.colour = LIGHTGREY; // system initialisation stuff: textbackground(0); @@ -2694,19 +2681,19 @@ static bool initialise(void) directvideo = 1; #endif -#ifdef USE_EMX - init_emx(); -#endif - seed_rng(); + init_overmap(); // in overmap.cc (duh?) + clear_ids(); // in itemname.cc + init_feature_table(); - mons_init(mcolour); // this needs to be way up top {dlb} + init_properties(); + init_monsters(mcolour); // this needs to be way up top {dlb} init_playerspells(); // this needs to be way up top {dlb} clrscr(); // init item array: - for (i = 1; i < MAX_ITEMS; i++) + for (i = 0; i < MAX_ITEMS; i++) init_item( i ); // empty messaging string @@ -2748,7 +2735,7 @@ static bool initialise(void) } for (i = 0; i < NUM_STATUE_TYPES; i++) - Visible_Statue[i] = 0; + you.visible_statue[i] = 0; // initialize tag system before we try loading anything! tag_init(); @@ -2797,8 +2784,12 @@ static bool initialise(void) // set vision radius to player's current vision setLOSRadius( you.current_vision ); - viewwindow(1, false); // This just puts the view up for the first turn. - item(); + + if (newc) + { + // For a new game, wipe out monsters in LOS. + zap_los_monsters(); + } #ifdef CLUA_BINDINGS clua.runhook("chk_startgame", "%b", ret); @@ -2806,15 +2797,20 @@ static bool initialise(void) read_init_file(true); strncpy(you.your_name, yname.c_str(), kNameLen); you.your_name[kNameLen - 1] = 0; + + // In case Lua changed the character set. + init_feature_table(); #endif + viewwindow(1, false); // This just puts the view up for the first turn. + activate_notes(true); return (ret); } // An attempt to tone down berserk a little bit. -- bwross // // This function does the accounting for not attacking while berserk -// This gives a triangluar number function for the additional penalty +// This gives a triangular number function for the additional penalty // Turn: 1 2 3 4 5 6 7 8 // Penalty: 1 3 6 10 15 21 28 36 // @@ -2868,13 +2864,11 @@ static void do_berserk_no_combat_penalty(void) // Called when the player moves by walking/running. Also calls // attack function and trap function etc when necessary. -static void move_player(char move_x, char move_y) +static void move_player(int move_x, int move_y) { bool attacking = false; bool moving = true; // used to prevent eventual movement (swap) - - int i; - bool trap_known; + bool swap = false; if (you.conf) { @@ -2886,224 +2880,84 @@ static void move_player(char move_x, char move_y) const int new_targ_x = you.x_pos + move_x; const int new_targ_y = you.y_pos + move_y; - const unsigned char new_targ_grid = grd[ new_targ_x ][ new_targ_y ]; - - if (new_targ_grid < MINMOVE) + if (!in_bounds(new_targ_x, new_targ_y) + || grid_is_solid(grd[new_targ_x][new_targ_y])) { - you.turn_is_over = 1; + you.turn_is_over = true; mpr("Ouch!"); + apply_berserk_penalty = true; return; } + } // end of if you.conf - if (new_targ_grid == DNGN_LAVA - && you.duration[DUR_CONDENSATION_SHIELD] > 0) - { - mpr("Your icy shield dissipates!", MSGCH_DURATION); - you.duration[DUR_CONDENSATION_SHIELD] = 0; - you.redraw_armour_class = 1; - } - - if ((new_targ_grid == DNGN_LAVA - || new_targ_grid == DNGN_DEEP_WATER - || new_targ_grid == DNGN_SHALLOW_WATER) - && !player_is_levitating()) - { - if (you.species == SP_MERFOLK && new_targ_grid != DNGN_LAVA) - { - mpr("You stumble into the water and return to your normal form."); - merfolk_start_swimming(); - } - - if (new_targ_grid != DNGN_SHALLOW_WATER) - fall_into_a_pool( false, new_targ_grid ); - - you.turn_is_over = 1; - do_berserk_no_combat_penalty(); - return; - } - } // end of if you.conf - - if (you.running > 0 && you.running != 2 && check_stop_running()) + if (you.running.check_stop_running()) { - stop_running(); move_x = 0; move_y = 0; - you.turn_is_over = 0; + // [ds] Do we need this? Shouldn't it be false to start with? + you.turn_is_over = false; return; } const int targ_x = you.x_pos + move_x; const int targ_y = you.y_pos + move_y; - const unsigned char old_grid = grd[ you.x_pos ][ you.y_pos ]; const unsigned char targ_grid = grd[ targ_x ][ targ_y ]; const unsigned char targ_monst = mgrd[ targ_x ][ targ_y ]; + const bool targ_solid = grid_is_solid(targ_grid); - if (targ_monst != NON_MONSTER) + if (targ_monst != NON_MONSTER && !mons_is_submerged(&menv[targ_monst])) { struct monsters *mon = &menv[targ_monst]; - if (mons_has_ench( mon, ENCH_SUBMERGED )) - goto break_out; - // you can swap places with a friendly monster if you // can see it and you're not confused if (mons_friendly( mon ) && player_monster_visible( mon ) && !you.conf) { - if (!swap_places( mon )) + if (swap_places( mon )) + swap = true; + else moving = false; - - goto break_out; } - - you_attack( targ_monst, true ); - you.turn_is_over = 1; - - // we don't want to create a penalty if there isn't - // supposed to be one - if (you.berserk_penalty != NO_BERSERK_PENALTY) - you.berserk_penalty = 0; - - attacking = true; - } - - break_out: - if (targ_grid == DNGN_LAVA && you.duration[DUR_CONDENSATION_SHIELD] > 0) - { - mpr("Your icy shield dissipates!", MSGCH_DURATION); - you.duration[DUR_CONDENSATION_SHIELD] = 0; - you.redraw_armour_class = 1; - } - - // Handle dangerous tiles - if ((targ_grid == DNGN_LAVA - || targ_grid == DNGN_DEEP_WATER - || targ_grid == DNGN_SHALLOW_WATER) - && !attacking && !player_is_levitating() && moving) - { - // Merfold automatically enter deep water... every other case - // we ask for confirmation. - if (you.species == SP_MERFOLK && targ_grid != DNGN_LAVA) - { - // Only mention diving if we just entering the water. - if (!player_in_water()) - { - mpr("You dive into the water and return to your normal form."); - merfolk_start_swimming(); - } - } - else if (targ_grid != DNGN_SHALLOW_WATER) + else // attack! { - bool enter = yesno("Do you really want to step there?", false, 'n'); + you_attack( targ_monst, true ); + you.turn_is_over = true; - if (enter) - { - fall_into_a_pool( false, targ_grid ); - you.turn_is_over = 1; - do_berserk_no_combat_penalty(); - return; - } - else - { - canned_msg(MSG_OK); - return; - } + // we don't want to create a penalty if there isn't + // supposed to be one + if (you.berserk_penalty != NO_BERSERK_PENALTY) + you.berserk_penalty = 0; + + attacking = true; } } - if (!attacking && targ_grid >= MINMOVE && moving) + if (!attacking && !targ_solid && moving) { - if (targ_grid == DNGN_UNDISCOVERED_TRAP - && random2(you.skills[SK_TRAPS_DOORS] + 1) > 3) - { - strcpy(info, "Wait a moment, "); - strcat(info, you.your_name); - strcat(info, "! Do you really want to step there?"); - mpr(info, MSGCH_WARN); - more(); - you.turn_is_over = 0; - - i = trap_at_xy( targ_x, targ_y ); - if (i != -1) - grd[ targ_x ][ targ_y ] = trap_category(env.trap[i].type); - return; - } - - you.x_pos += move_x; - you.y_pos += move_y; + you.time_taken *= player_movement_speed(); + you.time_taken /= 10; + move_player_to_grid(targ_x, targ_y, true, false, swap); - if (targ_grid == DNGN_SHALLOW_WATER && !player_is_levitating()) + // Returning the random trap scans as a way to get more use from the + // skill and acute mutations. + if (you.mutation[MUT_ACUTE_VISION] >= 2 + || (!you.mutation[MUT_BLURRY_VISION] + && random2(100) < + stat_mult(you.intel, skill_bump(SK_TRAPS_DOORS)))) { - if (you.species != SP_MERFOLK) - { - if (one_chance_in(3) && !silenced(you.x_pos, you.y_pos)) - { - mpr("Splash!"); - noisy( 10, you.x_pos, you.y_pos ); - } - - you.time_taken *= 13 + random2(8); - you.time_taken /= 10; - - if (old_grid != DNGN_SHALLOW_WATER) - { - mpr( "You enter the shallow water. " - "Moving in this stuff is going to be slow." ); - - if (you.invis) - mpr( "And don't expect to remain undetected." ); - } - } - else if (old_grid != DNGN_SHALLOW_WATER - && old_grid != DNGN_DEEP_WATER) - { - mpr("You return to your normal form as you enter the water."); - merfolk_start_swimming(); - } + search_around(); } - move_x = 0; move_y = 0; - you.time_taken *= player_movement_speed(); - you.time_taken /= 10; - - you.turn_is_over = 1; - item_check(0); - - if (targ_grid >= DNGN_TRAP_MECHANICAL - && targ_grid <= DNGN_UNDISCOVERED_TRAP) - { - if (targ_grid == DNGN_UNDISCOVERED_TRAP) - { - i = trap_at_xy(you.x_pos, you.y_pos); - if (i != -1) - grd[ you.x_pos ][ you.y_pos ] = trap_category(env.trap[i].type); - trap_known = false; - } - else - { - trap_known = true; - } - - i = trap_at_xy( you.x_pos, you.y_pos ); - if (i != -1) - { - if (player_is_levitating() - && trap_category(env.trap[i].type) == DNGN_TRAP_MECHANICAL) - { - goto out_of_traps; // can fly over mechanical traps - } - - handle_traps(env.trap[i].type, i, trap_known); - } - } // end of if another grd == trap + you.turn_is_over = true; + item_check( false ); } - out_of_traps: // BCR - Easy doors single move - if (targ_grid == DNGN_CLOSED_DOOR && (Options.easy_open || you.running < 0)) - open_door(move_x, move_y); - else if (targ_grid <= MINMOVE) + if (targ_grid == DNGN_CLOSED_DOOR && Options.easy_open) + open_door(move_x, move_y, false); + else if (targ_solid) { stop_running(); move_x = 0; @@ -3111,8 +2965,8 @@ static void move_player(char move_x, char move_y) you.turn_is_over = 0; } - if (you.running == 2) - you.running = 1; + if (you.running == RMODE_START) + you.running = RMODE_CONTINUE; if (you.level_type == LEVEL_ABYSS && (you.x_pos <= 15 || you.x_pos >= (GXM - 16) @@ -3150,8 +3004,5 @@ static void move_player(char move_x, char move_y) #endif } - if (!attacking) - { - do_berserk_no_combat_penalty(); - } + apply_berserk_penalty = !attacking; } // end move_player() diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index afcbd0445b..d431817532 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -3,6 +3,8 @@ * Summary: Functions related to ranged attacks. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <7> 21mar2001 GDL Replaced all FP arithmetic with integer*100 math @@ -38,6 +40,7 @@ #include "it_use2.h" #include "itemname.h" #include "items.h" +#include "itemprop.h" #include "misc.h" #include "monplace.h" #include "monstuff.h" @@ -59,26 +62,19 @@ #define MON_UNAFFECTED 1 // monster unaffected #define MON_AFFECTED 2 // monster was unaffected -extern FixedVector< char, NUM_STATUE_TYPES > Visible_Statue; // in acr.cc - static int spreadx[] = { 0, 0, 1, -1 }; static int spready[] = { -1, 1, 0, 0 }; static int opdir[] = { 2, 1, 4, 3 }; static FixedArray < bool, 19, 19 > explode_map; -// helper functions (some of these, esp. affect(), should probably +// helper functions (some of these, esp. affect(), should probably // be public): static void sticky_flame_monster( int mn, bool source, int hurt_final ); static bool affectsWalls(struct bolt &beam); -static int affect(struct bolt &beam, int x, int y); -static bool isBouncy(struct bolt &beam); +static bool isBouncy(struct bolt &beam, unsigned char gridtype); static void beam_drop_object( struct bolt &beam, item_def *item, int x, int y ); -static bool beam_term_on_target(struct bolt &beam); +static bool beam_term_on_target(struct bolt &beam, int x, int y); static void beam_explodes(struct bolt &beam, int x, int y); -static int bounce(int &step1, int &step2, int w1, int w2, int &n1, int &n2, - int l1, int l2, int &t1, int &t2, bool topBlocked, bool sideBlocked); -static bool fuzzyLine(int nx, int ny, int &tx, int &ty, int lx, int ly, - int stepx, int stepy, bool roundX, bool roundY); static int affect_wall(struct bolt &beam, int x, int y); static int affect_place_clouds(struct bolt &beam, int x, int y); static void affect_place_explosion_clouds(struct bolt &beam, int x, int y); @@ -92,8 +88,70 @@ static void explosion_map(struct bolt &beam, int x, int y, int count, int dir, int r); static void explosion_cell(struct bolt &beam, int x, int y, bool drawOnly); +static void ench_animation( int flavour, const monsters *mon = NULL, bool force = false); static void zappy(char z_type, int power, struct bolt &pbolt); +static bool beam_is_blockable( struct bolt &pbolt ) +{ + // BEAM_ELECTRICITY is added here because chain lighting is not + // a true beam (stops at the first target it gets to and redirects + // from there)... but we don't want it shield blockable. + return (!pbolt.is_beam && !pbolt.is_explosion + && pbolt.flavour != BEAM_ELECTRICITY); +} + +// simple animated flash from Rupert Smith (and expanded to be more generic): +void zap_animation( int colour, const monsters *mon, bool force ) +{ + int x = you.x_pos, y = you.y_pos; + + // default to whatever colour magic is today + if (colour == -1) + colour = element_colour( EC_MAGIC ); + + if (mon) + { + if (!force && !player_monster_visible( mon )) + return; + + x = mon->x; + y = mon->y; + } + + if (!see_grid( x, y )) + return; + + const int drawx = x - you.x_pos + 18; + const int drawy = y - you.y_pos + 9; + + if (drawx > 8 && drawx < 26 && drawy > 0 && drawy < 18) + { + textcolor( colour ); + gotoxy( drawx, drawy ); + putch( SYM_ZAP ); + +#ifdef UNIX + update_screen(); +#endif + + delay(50); + } +} + +// special front function for zap_animation to interpret enchantment flavours +static void ench_animation( int flavour, const monsters *mon, bool force ) +{ + const int elem = (flavour == BEAM_HEALING) ? EC_HEAL : + (flavour == BEAM_PAIN) ? EC_UNHOLY : + (flavour == BEAM_DISPEL_UNDEAD) ? EC_HOLY : + (flavour == BEAM_POLYMORPH) ? EC_MUTAGENIC : + (flavour == BEAM_TELEPORT + || flavour == BEAM_BANISH + || flavour == BEAM_BLINK) ? EC_WARP + : EC_ENCHANT; + zap_animation( element_colour( elem ), mon, force ); +} + void zapping(char ztype, int power, struct bolt &pbolt) { @@ -103,7 +161,7 @@ void zapping(char ztype, int power, struct bolt &pbolt) #endif // GDL: note that rangeMax is set to 0, which means that max range is - // equal to range. This is OK, since rangeMax really only matters for + // equal to range. This is OK, since rangeMax really only matters for // stuff monsters throw/zap. // all of the following might be changed by zappy(): @@ -114,11 +172,11 @@ void zapping(char ztype, int power, struct bolt &pbolt) pbolt.type = 0; // default for "0" beams pbolt.flavour = BEAM_MAGIC; // default for "0" beams pbolt.ench_power = power; - pbolt.obviousEffect = false; - pbolt.isBeam = false; // default for all beams. - pbolt.isTracer = false; // default for all player beams + pbolt.obvious_effect = false; + pbolt.is_beam = false; // default for all beams. + pbolt.is_tracer = false; // default for all player beams pbolt.thrower = KILL_YOU_MISSILE; // missile from player - pbolt.aux_source = NULL; // additional source info, unused + pbolt.aux_source.clear(); // additional source info, unused // fill in the bolt structure zappy( ztype, power, pbolt ); @@ -423,29 +481,29 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) switch (z_type) { case ZAP_STRIKING: // cap 25 - strcpy(pbolt.beam_name, "force bolt"); + pbolt.name = "force bolt"; pbolt.colour = BLACK; pbolt.range = 8 + random2(5); pbolt.damage = dice_def( 1, 5 ); // dam: 5 pbolt.hit = 8 + power / 10; // 25: 10 pbolt.type = SYM_SPACE; pbolt.flavour = BEAM_MMISSILE; // unresistable - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_MAGIC_DARTS: // cap 25 - strcpy(pbolt.beam_name, "magic dart"); + pbolt.name = "magic dart"; pbolt.colour = LIGHTMAGENTA; pbolt.range = random2(5) + 8; pbolt.damage = dice_def( 1, 3 + power / 5 ); // 25: 1d8 pbolt.hit = 1500; // hits always pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_MMISSILE; // unresistable - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_STING: // cap 25 - strcpy(pbolt.beam_name, "sting"); + pbolt.name = "sting"; pbolt.colour = GREEN; pbolt.range = 8 + random2(5); pbolt.damage = dice_def( 1, 3 + power / 5 ); // 25: 1d8 @@ -453,11 +511,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_POISON; // extra damage - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_ELECTRICITY: // cap 20 - strcpy(pbolt.beam_name, "zap"); + pbolt.name = "zap"; pbolt.colour = LIGHTCYAN; pbolt.range = 6 + random2(8); // extended in beam pbolt.damage = dice_def( 1, 3 + random2(power) / 2 ); // 25: 1d11 @@ -465,12 +523,12 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_ELECTRICITY; // beams & reflects - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_DISRUPTION: // cap 25 - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_DISINTEGRATION; pbolt.range = 7 + random2(8); pbolt.damage = dice_def( 1, 4 + power / 5 ); // 25: 1d9 @@ -478,7 +536,7 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) break; case ZAP_PAIN: // cap 25 - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_PAIN; pbolt.range = 7 + random2(8); pbolt.damage = dice_def( 1, 4 + power / 5 ); // 25: 1d9 @@ -487,7 +545,7 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) break; case ZAP_FLAME_TONGUE: // cap 25 - strcpy(pbolt.beam_name, "flame"); + pbolt.name = "flame"; pbolt.colour = RED; pbolt.range = 1 + random2(2) + random2(power) / 10; @@ -499,17 +557,17 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_BOLT; pbolt.flavour = BEAM_FIRE; - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_SMALL_SANDBLAST: // cap 25 - strcpy(pbolt.beam_name, "blast of "); + pbolt.name = "blast of "; temp_rand = random2(4); - strcat(pbolt.beam_name, (temp_rand == 0) ? "dust" : - (temp_rand == 1) ? "dirt" : - (temp_rand == 2) ? "grit" : "sand"); + pbolt.name += (temp_rand == 0) ? "dust" : + (temp_rand == 1) ? "dirt" : + (temp_rand == 2) ? "grit" : "sand"; pbolt.colour = BROWN; pbolt.range = (random2(power) > random2(30)) ? 2 : 1; @@ -518,11 +576,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_BOLT; pbolt.flavour = BEAM_FRAG; // extra AC resist - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_SANDBLAST: // cap 50 - strcpy(pbolt.beam_name, coinflip() ? "blast of rock" : "rocky blast"); + pbolt.name = coinflip() ? "blast of rock" : "rocky blast"; pbolt.colour = BROWN; pbolt.range = 2 + random2(power) / 20; @@ -534,11 +592,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_BOLT; pbolt.flavour = BEAM_FRAG; // extra AC resist - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_BONE_SHARDS: - strcpy(pbolt.beam_name, "spray of bone shards"); + pbolt.name = "spray of bone shards"; pbolt.colour = LIGHTGREY; pbolt.range = 7 + random2(10); @@ -551,12 +609,12 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_MAGIC; // unresisted - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_FLAME: // cap 50 - strcpy(pbolt.beam_name, "puff of flame"); + pbolt.name = "puff of flame"; pbolt.colour = RED; pbolt.range = 8 + random2(5); pbolt.damage = dice_def( 2, 4 + power / 10 ); // 25: 2d6 50: 2d9 @@ -564,11 +622,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_FIRE; - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_FROST: // cap 50 - strcpy(pbolt.beam_name, "puff of frost"); + pbolt.name = "puff of frost"; pbolt.colour = WHITE; pbolt.range = 8 + random2(5); pbolt.damage = dice_def( 2, 4 + power / 10 ); // 25: 2d6 50: 2d9 @@ -576,11 +634,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_COLD; - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_STONE_ARROW: // cap 100 - strcpy(pbolt.beam_name, "stone arrow"); + pbolt.name = "stone arrow"; pbolt.colour = LIGHTGREY; pbolt.range = 8 + random2(5); pbolt.damage = dice_def( 2, 4 + power / 8 ); // 25: 2d7 50: 2d10 @@ -588,11 +646,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_MISSILE; pbolt.flavour = BEAM_MMISSILE; // unresistable - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_STICKY_FLAME: // cap 100 - strcpy(pbolt.beam_name, "sticky flame"); // extra damage + pbolt.name = "sticky flame"; // extra damage pbolt.colour = RED; pbolt.range = 8 + random2(5); pbolt.damage = dice_def( 2, 3 + power / 12 ); // 50: 2d7 100: 2d11 @@ -600,11 +658,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_FIRE; - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_MYSTIC_BLAST: // cap 100 - strcpy(pbolt.beam_name, "orb of energy"); + pbolt.name = "orb of energy"; pbolt.colour = LIGHTMAGENTA; pbolt.range = 8 + random2(5); pbolt.damage = calc_dice( 2, 15 + (power * 2) / 5 ); @@ -612,11 +670,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_MMISSILE; // unresistable - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_ICE_BOLT: // cap 100 - strcpy(pbolt.beam_name, "bolt of ice"); + pbolt.name = "bolt of ice"; pbolt.colour = WHITE; pbolt.range = 8 + random2(5); pbolt.damage = calc_dice( 3, 10 + power / 2 ); @@ -626,7 +684,7 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) break; case ZAP_DISPEL_UNDEAD: // cap 100 - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_DISPEL_UNDEAD; pbolt.range = 7 + random2(8); pbolt.damage = calc_dice( 3, 20 + (power * 3) / 4 ); @@ -635,7 +693,7 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) break; case ZAP_MAGMA: // cap 150 - strcpy(pbolt.beam_name, "bolt of magma"); + pbolt.name = "bolt of magma"; pbolt.colour = RED; pbolt.range = 5 + random2(4); pbolt.damage = calc_dice( 4, 10 + (power * 3) / 5 ); @@ -643,12 +701,12 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_LAVA; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_FIRE: // cap 150 - strcpy(pbolt.beam_name, "bolt of fire"); + pbolt.name = "bolt of fire"; pbolt.colour = RED; pbolt.range = 7 + random2(10); pbolt.damage = calc_dice( 6, 20 + (power * 3) / 4 ); @@ -656,12 +714,12 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_FIRE; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_COLD: // cap 150 - strcpy(pbolt.beam_name, "bolt of cold"); + pbolt.name = "bolt of cold"; pbolt.colour = WHITE; pbolt.range = 7 + random2(10); pbolt.damage = calc_dice( 6, 20 + (power * 3) / 4 ); @@ -669,12 +727,12 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_COLD; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_VENOM_BOLT: // cap 150 - strcpy(pbolt.beam_name, "bolt of poison"); + pbolt.name = "bolt of poison"; pbolt.colour = LIGHTGREEN; pbolt.range = 8 + random2(10); pbolt.damage = calc_dice( 4, 15 + power / 2 ); @@ -682,12 +740,12 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_POISON; // extra damage - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_NEGATIVE_ENERGY: // cap 150 - strcpy(pbolt.beam_name, "bolt of negative energy"); + pbolt.name = "bolt of negative energy"; pbolt.colour = DARKGREY; pbolt.range = 7 + random2(10); pbolt.damage = calc_dice( 4, 15 + (power * 3) / 5 ); @@ -695,46 +753,46 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_NEG; // drains levels - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_IRON_BOLT: // cap 150 - strcpy(pbolt.beam_name, "iron bolt"); + pbolt.name = "iron bolt"; pbolt.colour = LIGHTCYAN; pbolt.range = 5 + random2(5); pbolt.damage = calc_dice( 9, 15 + (power * 3) / 4 ); pbolt.hit = 7 + power / 15; // 50: 10 100: 13 pbolt.type = SYM_MISSILE; pbolt.flavour = BEAM_MMISSILE; // unresistable - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_POISON_ARROW: // cap 150 - strcpy(pbolt.beam_name, "poison arrow"); + pbolt.name = "poison arrow"; pbolt.colour = LIGHTGREEN; pbolt.range = 8 + random2(5); pbolt.damage = calc_dice( 4, 15 + power ); pbolt.hit = 5 + power / 10; // 50: 10 100: 15 pbolt.type = SYM_MISSILE; pbolt.flavour = BEAM_POISON_ARROW; // extra damage - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_DISINTEGRATION: // cap 150 - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_DISINTEGRATION; pbolt.range = 7 + random2(8); pbolt.damage = calc_dice( 3, 15 + (power * 3) / 4 ); pbolt.ench_power *= 5; pbolt.ench_power /= 2; - pbolt.isBeam = true; + pbolt.is_beam = true; break; case ZAP_LIGHTNING: // cap 150 // also for breath (at pow = lev * 2; max dam: 33) - strcpy(pbolt.beam_name, "bolt of lightning"); + pbolt.name = "bolt of lightning"; pbolt.colour = LIGHTCYAN; pbolt.range = 8 + random2(10); // extended in beam pbolt.damage = calc_dice( 1, 10 + (power * 3) / 5 ); @@ -742,22 +800,23 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_ELECTRICITY; // beams & reflects - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_FIREBALL: // cap 150 - strcpy(pbolt.beam_name, "fireball"); + pbolt.name = "fireball"; pbolt.colour = RED; pbolt.range = 8 + random2(5); pbolt.damage = calc_dice( 3, 10 + power / 2 ); pbolt.hit = 40; // hit: 40 pbolt.type = SYM_ZAP; - pbolt.flavour = BEAM_EXPLOSION; // fire + pbolt.flavour = BEAM_FIRE; // fire + pbolt.is_explosion = true; break; case ZAP_ORB_OF_ELECTRICITY: // cap 150 - strcpy(pbolt.beam_name, "orb of electricity"); + pbolt.name = "orb of electricity"; pbolt.colour = LIGHTBLUE; pbolt.range = 9 + random2(12); pbolt.damage = calc_dice( 1, 15 + (power * 4) / 5 ); @@ -765,33 +824,35 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.hit = 40; // hit: 40 pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_ELECTRICITY; + pbolt.is_explosion = true; break; case ZAP_ORB_OF_FRAGMENTATION: // cap 150 - strcpy(pbolt.beam_name, "metal orb"); + pbolt.name = "metal orb"; pbolt.colour = CYAN; pbolt.range = 9 + random2(7); pbolt.damage = calc_dice( 3, 30 + (power * 3) / 4 ); pbolt.hit = 20; // hit: 20 pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_FRAG; // extra AC resist + pbolt.is_explosion = true; break; - case ZAP_CLEANSING_FLAME: // cap 200 - strcpy(pbolt.beam_name, "golden flame"); + case ZAP_CLEANSING_FLAME: + pbolt.name = "golden flame"; pbolt.colour = YELLOW; - pbolt.range = 7 + random2(10); - pbolt.damage = calc_dice( 6, 30 + power ); - pbolt.hit = 20; // hit: 20 + pbolt.range = 7; + pbolt.damage = calc_dice( 3, 30 + (power * 3) / 4 ); + pbolt.hit = 150; pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_HOLY; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_explosion = true; break; case ZAP_CRYSTAL_SPEAR: // cap 200 - strcpy(pbolt.beam_name, "crystal spear"); + pbolt.name = "crystal spear"; pbolt.colour = WHITE; pbolt.range = 7 + random2(10); pbolt.damage = calc_dice( 12, 30 + (power * 4) / 3 ); @@ -799,24 +860,24 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_MISSILE; pbolt.flavour = BEAM_MMISSILE; // unresistable - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_HELLFIRE: // cap 200 - strcpy(pbolt.beam_name, "hellfire"); + pbolt.name = "hellfire"; pbolt.colour = RED; pbolt.range = 7 + random2(10); pbolt.damage = calc_dice( 3, 10 + (power * 3) / 4 ); pbolt.hit = 20 + power / 10; // 50: 25 100: 30 pbolt.type = SYM_ZAP; - pbolt.flavour = BEAM_EXPLOSION; + pbolt.flavour = BEAM_HELLFIRE; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_explosion = true; break; case ZAP_ICE_STORM: // cap 200 - strcpy(pbolt.beam_name, "great blast of cold"); + pbolt.name = "great blast of cold"; pbolt.colour = BLUE; pbolt.range = 9 + random2(5); pbolt.damage = calc_dice( 6, 15 + power ); @@ -825,10 +886,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.ench_power = power; // used for radius pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_ICE; // half resisted + pbolt.is_explosion = true; break; case ZAP_BEAM_OF_ENERGY: // bolt of innacuracy - strcpy(pbolt.beam_name, "narrow beam of energy"); + pbolt.name = "narrow beam of energy"; pbolt.colour = YELLOW; pbolt.range = 7 + random2(10); pbolt.damage = calc_dice( 12, 40 + (power * 3) / 2 ); @@ -836,13 +898,13 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_ENERGY; // unresisted - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_SPIT_POISON: // cap 50 // max pow = lev + mut * 5 = 42 - strcpy(pbolt.beam_name, "splash of poison"); + pbolt.name = "splash of poison"; pbolt.colour = GREEN; pbolt.range = 3 + random2( 1 + power / 2 ); @@ -853,12 +915,12 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.hit = 5 + random2( 1 + power / 3 ); // max hit: 19 pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_POISON; - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; case ZAP_BREATHE_FIRE: // cap 50 // max pow = lev + mut * 4 + 12 = 51 (capped to 50) - strcpy(pbolt.beam_name, "fiery breath"); + pbolt.name = "fiery breath"; pbolt.colour = RED; pbolt.range = 3 + random2( 1 + power / 2 ); @@ -870,13 +932,13 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_FIRE; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_BREATHE_FROST: // cap 50 // max power = lev = 27 - strcpy(pbolt.beam_name, "freezing breath"); + pbolt.name = "freezing breath"; pbolt.colour = WHITE; pbolt.range = 3 + random2( 1 + power / 2 ); @@ -888,13 +950,13 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_COLD; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_BREATHE_ACID: // cap 50 // max power = lev for ability, 50 for minor destruction (max dam: 57) - strcpy(pbolt.beam_name, "acid"); + pbolt.name = "acid"; pbolt.colour = YELLOW; pbolt.range = 3 + random2( 1 + power / 2 ); @@ -906,13 +968,13 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_ACID; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_BREATHE_POISON: // leaves clouds of gas // cap 50 // max power = lev = 27 - strcpy(pbolt.beam_name, "poison gas"); + pbolt.name = "poison gas"; pbolt.colour = GREEN; pbolt.range = 3 + random2( 1 + power / 2 ); @@ -924,12 +986,12 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_POISON; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_BREATHE_POWER: // cap 50 - strcpy(pbolt.beam_name, "bolt of energy"); + pbolt.name = "bolt of energy"; // max power = lev = 27 pbolt.colour = BLUE; @@ -949,13 +1011,13 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_MMISSILE; // unresistable - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_BREATHE_STEAM: // cap 50 // max power = lev = 27 - strcpy(pbolt.beam_name, "ball of steam"); + pbolt.name = "ball of steam"; pbolt.colour = LIGHTGREY; pbolt.range = 6 + random2(5); @@ -965,133 +1027,133 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.damage = dice_def( 3, 4 + power / 5 ); // max dam: 27 pbolt.hit = 10 + random2( 1 + power / 5 ); pbolt.type = SYM_ZAP; - pbolt.flavour = BEAM_FIRE; + pbolt.flavour = BEAM_STEAM; - pbolt.obviousEffect = true; - pbolt.isBeam = true; + pbolt.obvious_effect = true; + pbolt.is_beam = true; break; case ZAP_SLOWING: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_SLOW; - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_HASTING: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_HASTE; - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_PARALYSIS: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_PARALYSIS; - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_CONFUSION: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_CONFUSION; - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_INVISIBILITY: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_INVISIBILITY; - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_HEALING: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_HEALING; pbolt.damage = dice_def( 1, 7 + power / 3 ); - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_DIGGING: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_DIGGING; // not ordinary "0" beam range {dlb} pbolt.range = 3 + random2( power / 5 ) + random2(5); - pbolt.isBeam = true; + pbolt.is_beam = true; break; case ZAP_TELEPORTATION: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_TELEPORT; pbolt.range = 9 + random2(5); - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_POLYMORPH_OTHER: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_POLYMORPH; pbolt.range = 9 + random2(5); - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_ENSLAVEMENT: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_CHARM; pbolt.range = 7 + random2(5); - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_BANISHMENT: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_BANISH; pbolt.range = 7 + random2(5); - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_DEGENERATION: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_DEGENERATE; pbolt.range = 7 + random2(5); - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_ENSLAVE_UNDEAD: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_ENSLAVE_UNDEAD; pbolt.range = 7 + random2(5); - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_AGONY: - strcpy(pbolt.beam_name, "0agony"); + pbolt.name = "0agony"; pbolt.flavour = BEAM_PAIN; pbolt.range = 7 + random2(8); pbolt.ench_power *= 5; - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_CONTROL_DEMON: - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_ENSLAVE_DEMON; pbolt.range = 7 + random2(5); pbolt.ench_power *= 3; pbolt.ench_power /= 2; - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_SLEEP: //jmf: added - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_SLEEP; pbolt.range = 7 + random2(5); - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_BACKLIGHT: //jmf: added - strcpy(pbolt.beam_name, "0"); + pbolt.name = "0"; pbolt.flavour = BEAM_BACKLIGHT; pbolt.colour = BLUE; pbolt.range = 7 + random2(5); - // pbolt.isBeam = true; + // pbolt.is_beam = true; break; case ZAP_DEBUGGING_RAY: - strcpy( pbolt.beam_name, "debugging ray" ); + pbolt.name = "debugging ray"; pbolt.colour = random_colour(); pbolt.range = 7 + random2(10); pbolt.damage = dice_def( 1500, 1 ); // dam: 1500 @@ -1099,11 +1161,11 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_DEBUG; pbolt.flavour = BEAM_MMISSILE; // unresistable - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; default: - strcpy(pbolt.beam_name, "buggy beam"); + pbolt.name = "buggy beam"; pbolt.colour = random_colour(); pbolt.range = 7 + random2(10); pbolt.damage = dice_def( 1, 0 ); @@ -1111,16 +1173,16 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) pbolt.type = SYM_DEBUG; pbolt.flavour = BEAM_MMISSILE; // unresistable - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; break; } // end of switch } // end zappy() /* NEW (GDL): - * Now handles all beamed/thrown items and spells, tracers, and their effects. + * Now handles all beamed/thrown items and spells, tracers, and their effects. * item is used for items actually thrown/launched * - * if item is NULL, there is no physical object being thrown that could + * if item is NULL, there is no physical object being thrown that could * land on the ground. */ @@ -1140,7 +1202,7 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) * 4. Check for beam termination on target * 5. Affect the cell which the beam just moved into -> affect() * 6. Decrease remaining range appropriately - * 7. Check for early out due to aimedAtFeet + * 7. Check for early out due to aimed_at_feet * 8. Draw the beam * 9. Drop an object where the beam 'landed' *10. Beams explode where the beam 'landed' @@ -1151,78 +1213,48 @@ static void zappy( char z_type, int power, struct bolt &pbolt ) void fire_beam( struct bolt &pbolt, item_def *item ) { - int dx, dy; // total delta between source & target - int lx, ly; // last affected x,y - int stepx, stepy; // x,y increment - FP - int wx, wy; // 'working' x,y - FP bool beamTerminate; // has beam been 'stopped' by something? - int nx, ny; // test(new) x,y - FP - int tx, ty; // test(new) x,y - integer - bool roundX, roundY; // which to round? + int tx = 0, ty = 0; // test(new) x,y - integer int rangeRemaining; - bool fuzzyOK; // fuzzification resulted in OK move - bool sideBlocked, topBlocked, random_beam; #if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, "%s%s (%d,%d) to (%d,%d): ty=%d col=%d flav=%d hit=%d dam=%dd%d", - (pbolt.isBeam) ? "beam" : "missile", - (pbolt.isTracer) ? " tracer" : "", - pbolt.source_x, pbolt.source_y, - pbolt.target_x, pbolt.target_y, - pbolt.type, pbolt.colour, pbolt.flavour, - pbolt.hit, pbolt.damage.num, pbolt.damage.size ); - - mpr( info, MSGCH_DIAGNOSTICS ); + if (pbolt.flavour != BEAM_LINE_OF_SIGHT) + { + mprf( MSGCH_DIAGNOSTICS, + "%s%s%s (%d,%d) to (%d,%d): ty=%d col=%d flav=%d hit=%d dam=%dd%d", + (pbolt.is_beam) ? "beam" : "missile", + (pbolt.is_explosion) ? "*" : + (pbolt.is_big_cloud) ? "+" : "", + (pbolt.is_tracer) ? " tracer" : "", + pbolt.source_x, pbolt.source_y, + pbolt.target_x, pbolt.target_y, + pbolt.type, pbolt.colour, pbolt.flavour, + pbolt.hit, pbolt.damage.num, pbolt.damage.size ); + } #endif // init - pbolt.aimedAtFeet = false; - pbolt.msgGenerated = false; - pbolt.isExplosion = false; - roundY = false; - roundX = false; - - // first, calculate beam step - dx = pbolt.target_x - pbolt.source_x; - dy = pbolt.target_y - pbolt.source_y; - - // check for aim at feet - if (dx == 0 && dy == 0) - { - pbolt.aimedAtFeet = true; - stepx = 0; - stepy = 0; - tx = pbolt.source_x; - ty = pbolt.source_y; - } - else - { - if (abs(dx) >= abs(dy)) - { - stepx = (dx > 0) ? 100 : -100; - stepy = 100 * dy / (abs(dx)); - roundY = true; - } - else - { - stepy = (dy > 0) ? 100 : -100; - stepx = 100 * dx / (abs(dy)); - roundX = true; - } - } + pbolt.aimed_at_feet = + (pbolt.target_x == pbolt.source_x) && + (pbolt.target_y == pbolt.source_y); + pbolt.msg_generated = false; + + ray_def ray; - // give chance for beam to affect one cell even if aimedAtFeet. + find_ray( pbolt.source_x, pbolt.source_y, pbolt.target_x, pbolt.target_y, + true, ray); + + if ( !pbolt.aimed_at_feet ) + ray.advance(); + + // give chance for beam to affect one cell even if aimed_at_feet. beamTerminate = false; - // setup working coords - lx = pbolt.source_x; - wx = 100 * lx; - ly = pbolt.source_y; - wy = 100 * ly; + // setup range rangeRemaining = pbolt.range; if (pbolt.rangeMax > pbolt.range) { - if (pbolt.isTracer) + if (pbolt.is_tracer) rangeRemaining = pbolt.rangeMax; else rangeRemaining += random2((pbolt.rangeMax - pbolt.range) + 1); @@ -1231,140 +1263,79 @@ void fire_beam( struct bolt &pbolt, item_def *item ) // before we start drawing the beam, turn buffering off #ifdef WIN32CONSOLE bool oldValue = true; - if (!pbolt.isTracer) + if (!pbolt.is_tracer) oldValue = setBuffering(false); #endif - // cannot use source_x, source_y, target_x, target_y during - // step algorithm due to bouncing. - - // now, one step at a time, try to move towards target. while(!beamTerminate) { - nx = wx + stepx; - ny = wy + stepy; - - if (roundY) - { - tx = nx / 100; - ty = (ny + 50) / 100; - } - if (roundX) - { - ty = ny / 100; - tx = (nx + 50) / 100; - } - - // check that tx, ty are valid. If not, set to last - // x,y and break. - if (tx < 0 || tx >= GXM || ty < 0 || ty >= GYM) - { - tx = lx; - ty = ly; - break; - } + tx = ray.x(); + ty = ray.y(); // see if tx, ty is blocked by something - if (grd[tx][ty] < MINMOVE) + if (grid_is_solid(grd[tx][ty])) { // first, check to see if this beam affects walls. if (affectsWalls(pbolt)) { // should we ever get a tracer with a wall-affecting - // beam (possible I suppose), we'll quit tracing now. - if (!pbolt.isTracer) + // beam (possible I suppose), we'll quit tracing now. + if (!pbolt.is_tracer) rangeRemaining -= affect(pbolt, tx, ty); // if it's still a wall, quit. - if (grd[tx][ty] < MINMOVE) - { + if (grid_is_solid(grd[tx][ty])) break; // breaks from line tracing - } } else { - // BEGIN fuzzy line algorithm - fuzzyOK = fuzzyLine(nx,ny,tx,ty,lx,ly,stepx,stepy,roundX,roundY); - if (!fuzzyOK) - { - // BEGIN bounce case - if (!isBouncy(pbolt)) - { - tx = lx; - ty = ly; - break; // breaks from line tracing - } - - sideBlocked = false; - topBlocked = false; - // BOUNCE -- guaranteed to return reasonable tx, ty. - // if it doesn't, we'll quit in the next if stmt anyway. - if (roundY) - { - if ( grd[lx + stepx / 100][ly] < MINMOVE) - sideBlocked = true; + // BEGIN bounce case + if (!isBouncy(pbolt, grd[tx][ty])) { + ray.regress(); + tx = ray.x(); + ty = ray.y(); + break; // breaks from line tracing + } - if (dy != 0) - { - if ( grd[lx][ly + (stepy>0?1:-1)] < MINMOVE) - topBlocked = true; - } + // bounce + do { + ray.regress(); + ray.advance_and_bounce(); + --rangeRemaining; + } while ( rangeRemaining > 0 && + grid_is_solid(grd[ray.x()][ray.y()]) ); - rangeRemaining -= bounce(stepx, stepy, wx, wy, nx, ny, - lx, ly, tx, ty, topBlocked, sideBlocked); - } - else - { - if ( grd[lx][ly + stepy / 100] < MINMOVE) - sideBlocked = true; - - if (dx != 0) - { - if ( grd[lx + (stepx>0?1:-1)][ly] < MINMOVE) - topBlocked = true; - } + if (rangeRemaining < 1) + break; + tx = ray.x(); + ty = ray.y(); - rangeRemaining -= bounce(stepy, stepx, wy, wx, ny, nx, - ly, lx, ty, tx, topBlocked, sideBlocked); - } - // END bounce case - range check - if (rangeRemaining < 1) - { - tx = lx; - ty = ly; - break; - } - } } // end else - beam doesn't affect walls } // endif - is tx, ty wall? // at this point, if grd[tx][ty] is still a wall, we // couldn't find any path: bouncy, fuzzy, or not - so break. - if (grd[tx][ty] < MINMOVE) - { - tx = lx; - ty = ly; + if (grid_is_solid(grd[tx][ty])) break; - } - + // check for "target termination" // occurs when beam can be targetted at empty // cell (e.g. a mage wants an explosion to happen // between two monsters) - // in this case, don't affect the cell - players - // /monsters have no chance to dodge or block such - // a beam, and we want to avoid silly messages. + // in this case, don't affect the cell - players and + // monsters have no chance to dodge or block such + // a beam, and we want to avoid silly messages. if (tx == pbolt.target_x && ty == pbolt.target_y) - beamTerminate = beam_term_on_target(pbolt); + beamTerminate = beam_term_on_target(pbolt, tx, ty); - // affect the cell, except in the special case noted + // affect the cell, except in the special case noted // above -- affect() will early out if something gets // hit and the beam is type 'term on target'. - if (!beamTerminate) + if (!beamTerminate || !pbolt.is_explosion) { // random beams: randomize before affect - random_beam = false; + bool random_beam = false; if (pbolt.flavour == BEAM_RANDOM) { random_beam = true; @@ -1385,12 +1356,12 @@ void fire_beam( struct bolt &pbolt, item_def *item ) beamTerminate = true; // special case - beam was aimed at feet - if (pbolt.aimedAtFeet) + if (pbolt.aimed_at_feet) beamTerminate = true; // actually draw the beam/missile/whatever, // if the player can see the cell. - if (!pbolt.isTracer && pbolt.beam_name[0] != '0' && see_grid(tx,ty)) + if (!pbolt.is_tracer && pbolt.name[0] != '0' && see_grid(tx,ty)) { // we don't clean up the old position. // first, most people like seeing the full path, @@ -1419,24 +1390,17 @@ void fire_beam( struct bolt &pbolt, item_def *item ) delay(15); #ifdef MISSILE_TRAILS_OFF - if (!pbolt.isBeam || pbolt.beam_name[0] == '0') + if (!pbolt.is_beam || pbolt.name[0] == '0') viewwindow(1,false); // mv: added. It's not optimal but // is usually enough #endif } } - - // set some stuff up for the next iteration - lx = tx; - ly = ty; - - wx = nx; - wy = ny; - + ray.advance(); } // end- while !beamTerminate - // the beam has finished, and terminated at tx, ty + // the beam has finished, and terminated at tx, ty // leave an object, if applicable if (item) @@ -1450,22 +1414,22 @@ void fire_beam( struct bolt &pbolt, item_def *item ) beam_explodes(pbolt, tx, ty); - if (pbolt.isTracer) + if (pbolt.is_tracer) { pbolt.target_x = ox; pbolt.target_y = oy; } // canned msg for enchantments that affected no-one - if (pbolt.beam_name[0] == '0' && pbolt.flavour != BEAM_DIGGING) + if (pbolt.name[0] == '0' && pbolt.flavour != BEAM_DIGGING) { - if (!pbolt.isTracer && !pbolt.msgGenerated && !pbolt.obviousEffect) + if (!pbolt.is_tracer && !pbolt.msg_generated && !pbolt.obvious_effect) canned_msg(MSG_NOTHING_HAPPENS); } // that's it! #ifdef WIN32CONSOLE - if (!pbolt.isTracer) + if (!pbolt.is_tracer) setBuffering(oldValue); #endif } // end fire_beam(); @@ -1476,7 +1440,7 @@ void fire_beam( struct bolt &pbolt, item_def *item ) int mons_adjust_flavoured( struct monsters *monster, struct bolt &pbolt, int hurted, bool doFlavouredEffects ) { - // if we're not doing flavored effects, must be preliminary + // if we're not doing flavored effects, must be preliminary // damage check only; do not print messages or apply any side // effects! int resist; @@ -1484,6 +1448,7 @@ int mons_adjust_flavoured( struct monsters *monster, struct bolt &pbolt, switch (pbolt.flavour) { case BEAM_FIRE: + case BEAM_STEAM: resist = mons_res_fire(monster); if (resist > 1) { @@ -1580,8 +1545,7 @@ int mons_adjust_flavoured( struct monsters *monster, struct bolt &pbolt, // Poison arrow can poison any living thing regardless of // poison resistance. -- bwr - const int holy = mons_holiness( monster->type ); - if (holy == MH_PLANT || holy == MH_NATURAL) + if (mons_has_lifeforce(monster)) poison_monster( monster, YOU_KILL(pbolt.thrower), 2, true ); } @@ -1623,17 +1587,47 @@ int mons_adjust_flavoured( struct monsters *monster, struct bolt &pbolt, } // end else break; - case BEAM_HOLY: // flame of cleansing - if (mons_holiness(monster->type) == MH_NATURAL - || mons_holiness(monster->type) == MH_NONLIVING - || mons_holiness(monster->type) == MH_PLANT - || mons_holiness(monster->type) == MH_HOLY) + case BEAM_MIASMA: + if (mons_res_negative_energy( monster ) >= 3) { if (doFlavouredEffects) simple_monster_message(monster, " appears unharmed."); hurted = 0; } + else + { + // early out for tracer/no side effects + if (!doFlavouredEffects) + return (hurted); + + if (mons_res_poison( monster ) <= 0) + poison_monster( monster, YOU_KILL(pbolt.thrower) ); + + if (one_chance_in( 3 + 2 * mons_res_negative_energy(monster) )) + { + struct bolt beam; + beam.flavour = BEAM_SLOW; + mons_ench_f2( monster, beam ); + } + } + break; + + case BEAM_HOLY: // flame of cleansing + if (mons_is_unholy( monster )) + { + if (doFlavouredEffects) + simple_monster_message( monster, " writhes in agony!" ); + + hurted = (hurted * 3) / 2; + } + else if (!mons_is_evil( monster )) + { + if (doFlavouredEffects) + simple_monster_message( monster, " appears unharmed." ); + + hurted = 0; + } break; case BEAM_ICE: @@ -1687,7 +1681,7 @@ int mons_adjust_flavoured( struct monsters *monster, struct bolt &pbolt, hurted /= 10; } } - else if (stricmp(pbolt.beam_name, "hellfire") == 0) + else if (pbolt.name == "hellfire") { resist = mons_res_fire(monster); if (resist > 2) @@ -1732,7 +1726,7 @@ int mons_adjust_flavoured( struct monsters *monster, struct bolt &pbolt, bool mass_enchantment( int wh_enchant, int pow, int origin ) { int i; // loop variable {dlb} - bool msgGenerated = false; + bool msg_generated = false; struct monsters *monster; viewwindow(0, false); @@ -1753,7 +1747,7 @@ bool mass_enchantment( int wh_enchant, int pow, int origin ) if (mons_friendly(monster)) continue; - if (mons_holiness(monster->type) != MH_UNDEAD) + if (mons_class_holiness(monster->type) != MH_UNDEAD) continue; if (check_mons_resist_magic( monster, pow )) @@ -1762,7 +1756,7 @@ bool mass_enchantment( int wh_enchant, int pow, int origin ) continue; } } - else if (mons_holiness(monster->type) == MH_NATURAL) + else if (mons_holiness(monster) == MH_NATURAL) { if (check_mons_resist_magic( monster, pow )) { @@ -1784,7 +1778,7 @@ bool mass_enchantment( int wh_enchant, int pow, int origin ) if (player_monster_visible( monster )) { // turn message on - msgGenerated = true; + msg_generated = true; switch (wh_enchant) { case ENCH_FEAR: @@ -1801,7 +1795,7 @@ bool mass_enchantment( int wh_enchant, int pow, int origin ) break; default: // oops, I guess not! - msgGenerated = false; + msg_generated = false; } } @@ -1811,10 +1805,10 @@ bool mass_enchantment( int wh_enchant, int pow, int origin ) } } // end "for i" - if (!msgGenerated) + if (!msg_generated) canned_msg(MSG_NOTHING_HAPPENS); - return (msgGenerated); + return (msg_generated); } // end mass_enchantmenet() /* @@ -1832,22 +1826,23 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) switch (pbolt.flavour) /* put in magic resistance */ { case BEAM_SLOW: /* 0 = slow monster */ - // try to remove haste, if monster is hasted + // try to remove haste, if monster is hasted if (mons_del_ench(monster, ENCH_HASTE)) { if (simple_monster_message(monster, " is no longer moving quickly.")) - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; return (MON_AFFECTED); } - // not hasted, slow it - if (mons_add_ench(monster, ENCH_SLOW)) + // not hasted, slow it + if (!mons_has_ench(monster, ENCH_SLOW) + && mons_add_ench(monster, ENCH_SLOW)) { // put in an exception for fungi, plants and other things you won't // notice slow down. if (simple_monster_message(monster, " seems to slow down.")) - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; } return (MON_AFFECTED); @@ -1855,7 +1850,7 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) if (mons_del_ench(monster, ENCH_SLOW)) { if (simple_monster_message(monster, " is no longer moving slowly.")) - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; return (MON_AFFECTED); } @@ -1866,7 +1861,7 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) // put in an exception for fungi, plants and other things you won't // notice speed up. if (simple_monster_message(monster, " seems to speed up.")) - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; } return (MON_AFFECTED); @@ -1877,12 +1872,12 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) { if (simple_monster_message(monster, "'s wounds heal themselves!")) - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; } else { if (simple_monster_message(monster, " is healed somewhat.")) - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; } } return (MON_AFFECTED); @@ -1891,10 +1886,10 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) monster->speed_increment = 0; if (simple_monster_message(monster, " suddenly stops moving!")) - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; - if (grd[monster->x][monster->y] == DNGN_LAVA_X - || grd[monster->x][monster->y] == DNGN_WATER_X) + if (grd[monster->x][monster->y] == DNGN_LAVA + || grid_is_water(grd[monster->x][monster->y])) { if (mons_flies(monster) == 1) { @@ -1904,8 +1899,8 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) { strcpy(info, ptr_monam(monster, DESC_CAP_THE)); strcat(info, " falls into the "); - strcat(info, (grd[monster->x][monster->y] == DNGN_WATER_X) - ? "water" : "lava"); + strcat(info, (grd[monster->x][monster->y] == DNGN_LAVA) + ? "lava" : "water"); strcat(info, "!"); mpr(info); } @@ -1932,7 +1927,7 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) // put in an exception for fungi, plants and other things you won't // notice becoming confused. if (simple_monster_message(monster, " appears confused.")) - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; } return (MON_AFFECTED); @@ -1952,7 +1947,7 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) mpr( info ); } - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; } return (MON_AFFECTED); @@ -1962,7 +1957,7 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) // put in an exception for fungi, plants and other things you won't // notice becoming charmed. if (simple_monster_message(monster, " is charmed.")) - pbolt.obviousEffect = true; + pbolt.obvious_effect = true; } return (MON_AFFECTED); @@ -1973,6 +1968,56 @@ int mons_ench_f2(struct monsters *monster, struct bolt &pbolt) return (MON_AFFECTED); } // end mons_ench_f2() +// degree is ignored. +static void slow_monster(monsters *mon, int degree) +{ + bolt beam; + beam.flavour = BEAM_SLOW; + mons_ench_f2(mon, beam); +} + +// Returns true if the curare killed the monster. +bool curare_hits_monster( const bolt &beam, + monsters *monster, + bool fromPlayer, + int levels ) +{ + const bool res_poison = mons_res_poison(monster); + bool mondied = false; + + poison_monster(monster, fromPlayer, levels, false); + + if (!mons_res_asphyx(monster)) + { + int hurted = roll_dice(2, 6); + + // Note that the hurtage is halved by poison resistance. + if (res_poison) + hurted /= 2; + + if (hurted) + { + simple_monster_message(monster, " appears to choke."); + if ((monster->hit_points -= hurted) < 1) + { + const int thrower = YOU_KILL(beam.thrower) ? + KILL_YOU_MISSILE : KILL_MON_MISSILE; + monster_die(monster, thrower, beam.beam_source); + mondied = true; + } + } + + if (!mondied) + slow_monster(monster, levels); + } + + // Deities take notice. + if (fromPlayer) + did_god_conduct( DID_POISON, 5 + random2(3) ); + + return (mondied); +} + // actually poisons a monster (w/ message) void poison_monster( struct monsters *monster, bool fromPlayer, int levels, bool force ) @@ -2031,10 +2076,7 @@ void poison_monster( struct monsters *monster, bool fromPlayer, int levels, // finally, take care of deity preferences if (fromPlayer) - { - naughty(NAUGHTY_POISON, 5 + random2(3)); //jmf: TSO now hates poison - done_good(GOOD_POISON, 5); //jmf: had test god who liked poison - } + did_god_conduct( DID_POISON, 5 + random2(3) ); } // end poison_monster() // actually napalms a monster (w/ message) @@ -2080,7 +2122,7 @@ void sticky_flame_monster( int mn, bool fromPlayer, int levels ) mons_del_ench( monster, ENCH_YOUR_STICKY_FLAME_I, ENCH_YOUR_STICKY_FLAME_IV, true ); - // increase sticky flame strength, cap at 3 (level is 0..3) + // increase sticky flame strength, cap at 3 (level is 0..3) currentStrength += levels; if (currentStrength > 3) @@ -2108,42 +2150,59 @@ void sticky_flame_monster( int mn, bool fromPlayer, int levels ) * fr_count, foe_count: a count of how many friends and foes will (probably) * be hit by this beam * fr_power, foe_power: a measure of how many 'friendly' hit dice it will - * affect, and how many 'unfriendly' hit dice. + * affect, and how many 'unfriendly' hit dice. * - * note that beam properties must be set, as the tracer will take them - * into account, as well as the monster's intelligence. + * note that beam properties must be set, as the tracer will take them + * into account, as well as the monster's intelligence. * */ void fire_tracer(struct monsters *monster, struct bolt &pbolt) { // don't fiddle with any input parameters other than tracer stuff! - pbolt.isTracer = true; + pbolt.is_tracer = true; pbolt.source_x = monster->x; // always safe to do. pbolt.source_y = monster->y; pbolt.beam_source = monster_index(monster); - pbolt.canSeeInvis = (mons_see_invis(monster) != 0); - pbolt.smartMonster = (mons_intel(monster->type) == I_HIGH || + pbolt.can_see_invis = (mons_see_invis(monster) != 0); + pbolt.smart_monster = (mons_intel(monster->type) == I_HIGH || mons_intel(monster->type) == I_NORMAL); - pbolt.isFriendly = mons_friendly(monster); + pbolt.is_friendly = mons_friendly(monster); // init tracer variables pbolt.foe_count = pbolt.fr_count = 0; pbolt.foe_power = pbolt.fr_power = 0; - pbolt.foeRatio = 80; // default - see mons_should_fire() + pbolt.foe_ratio = 80; // default - see mons_should_fire() // foe ratio for summon gtr. demons & undead -- they may be // summoned, but they're hostile and would love nothing better // than to nuke the player and his minions - if (monster->attitude != ATT_FRIENDLY) - pbolt.foeRatio = 25; + if (pbolt.is_friendly && monster->attitude != ATT_FRIENDLY) + pbolt.foe_ratio = 25; // fire! fire_beam(pbolt); // unset tracer flag (convenience) - pbolt.isTracer = false; + pbolt.is_tracer = false; } // end tracer_f() +bool check_line_of_sight( int sx, int sy, int tx, int ty ) +{ + const int dist = grid_distance( sx, sy, tx, ty ); + + // can always see one square away + if (dist <= 1) + return (true); + + // currently we limit the range to 8 + if (dist > MONSTER_LOS_RANGE) + return (false); + + // Note that we are guaranteed to be within the player LOS range, + // so fallback is unnecessary. + ray_def ray; + return find_ray( sx, sy, tx, ty, false, ray ); +} /* When a mimic is hit by a ranged attack, it teleports away (the slow way) @@ -2158,12 +2217,15 @@ void mimic_alert(struct monsters *mimic) monster_teleport( mimic, !one_chance_in(3) ); } // end mimic_alert() -static bool isBouncy(struct bolt &beam) +static bool isBouncy(struct bolt &beam, unsigned char gridtype) { - // at present, only non-enchantment eletrcical beams bounce. - if (beam.beam_name[0] != '0' && beam.flavour == BEAM_ELECTRICITY) - return (true); - + if (beam.name[0] == '0') + return false; + if (beam.flavour == BEAM_ELECTRICITY && gridtype != DNGN_METAL_WALL) + return true; + if ( (beam.flavour == BEAM_FIRE || beam.flavour == BEAM_COLD) && + (gridtype == DNGN_GREEN_CRYSTAL_WALL) ) + return true; return (false); } @@ -2178,7 +2240,7 @@ static void beam_explodes(struct bolt &beam, int x, int y) beam.target_y = y; // generic explosion - if (beam.flavour == BEAM_EXPLOSION || beam.flavour == BEAM_HOLY) + if (beam.is_explosion) // beam.flavour == BEAM_EXPLOSION || beam.flavour == BEAM_HOLY) { explosion1(beam); return; @@ -2235,7 +2297,7 @@ static void beam_explodes(struct bolt &beam, int x, int y) // cloud producer -- POISON BLAST - if (strcmp(beam.beam_name, "blast of poison") == 0) + if (beam.name == "blast of poison") { cloud_type = YOU_KILL(beam.thrower) ? CLOUD_POISON : CLOUD_POISON_MON; big_cloud( cloud_type, x, y, 0, 7 + random2(5) ); @@ -2243,52 +2305,67 @@ static void beam_explodes(struct bolt &beam, int x, int y) } // cloud producer -- FOUL VAPOR (SWAMP DRAKE?) - if (strcmp(beam.beam_name, "foul vapour") == 0) + if (beam.name == "foul vapour") { cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STINK : CLOUD_STINK_MON; + if (beam.flavour == BEAM_MIASMA) + cloud_type = YOU_KILL(beam.thrower) ? + CLOUD_MIASMA : CLOUD_MIASMA_MON; big_cloud( cloud_type, x, y, 0, 9 ); return; } // special cases - orbs & blasts of cold - if (strcmp(beam.beam_name, "orb of electricity") == 0 - || strcmp(beam.beam_name, "metal orb") == 0 - || strcmp(beam.beam_name, "great blast of cold") == 0) + if (beam.name == "orb of electricity" + || beam.name == "metal orb" + || beam.name == "great blast of cold") { explosion1( beam ); return; } // cloud producer only -- stinking cloud - if (strcmp(beam.beam_name, "ball of vapour") == 0) + if (beam.name == "ball of vapour") { explosion1( beam ); return; } } -static bool beam_term_on_target(struct bolt &beam) +static bool beam_term_on_target(struct bolt &beam, int x, int y) { + if (beam.flavour == BEAM_LINE_OF_SIGHT) + { + beam.foe_count++; + return (true); + } // generic - all explosion-type beams can be targetted at empty space, // and will explode there. This semantic also means that a creature - // in the target cell will have no chance to dodge or block, so we + // in the target cell will have no chance to dodge or block, so we // DON'T affect() the cell if this function returns true! - if (beam.flavour == BEAM_EXPLOSION || beam.flavour == BEAM_HOLY) + if (beam.is_explosion || beam.is_big_cloud) return (true); // POISON BLAST - if (strcmp(beam.beam_name, "blast of poison") == 0) + if (beam.name == "blast of poison") return (true); // FOUL VAPOR (SWAMP DRAKE) - if (strcmp(beam.beam_name, "foul vapour") == 0) + if (beam.name == "foul vapour") return (true); // STINKING CLOUD - if (strcmp(beam.beam_name, "ball of vapour") == 0) + if (beam.name == "ball of vapour") + return (true); + + // [dshaligram] We have to decide what beams are eligible for stopping on + // target. + /* + if (beam.aimed_at_spot && x == beam.target_x && y == beam.target_y) return (true); + */ return (false); } @@ -2298,22 +2375,25 @@ static void beam_drop_object( struct bolt &beam, item_def *item, int x, int y ) ASSERT( item != NULL ); // conditions: beam is missile and not tracer. - if (beam.isTracer || beam.flavour != BEAM_MISSILE) + if (beam.is_tracer || beam.flavour != BEAM_MISSILE) return; if (YOU_KILL(beam.thrower) // ie if you threw it. - && (grd[x][y] != DNGN_LAVA && grd[x][y] != DNGN_DEEP_WATER)) + && (!grid_destroys_items(grd[x][y]))) { int chance; - // Using Throwing skill as the fletching/ammo preserving skill. -- bwr + // [dshaligram] Removed influence of Throwing on ammo preservation. + // The effect is nigh impossible to perceive. switch (item->sub_type) { - case MI_NEEDLE: chance = 6 + you.skills[SK_THROWING] / 6; break; - case MI_STONE: chance = 3 + you.skills[SK_THROWING] / 4; break; - case MI_DART: chance = 2 + you.skills[SK_THROWING] / 6; break; - case MI_ARROW: chance = 2 + you.skills[SK_THROWING] / 4; break; - case MI_BOLT: chance = 2 + you.skills[SK_THROWING] / 5; break; + case MI_NEEDLE: + chance = (get_ammo_brand(*item) == SPMSL_CURARE? 3 : 6); + break; + case MI_STONE: chance = 4; break; + case MI_DART: chance = 3; break; + case MI_ARROW: chance = 4; break; + case MI_BOLT: chance = 4; break; case MI_LARGE_ROCK: default: @@ -2325,206 +2405,83 @@ static void beam_drop_object( struct bolt &beam, item_def *item, int x, int y ) copy_item_to_grid( *item, x, y, 1 ); } else if (MON_KILL(beam.thrower) // monster threw it. - && (grd[x][y] != DNGN_LAVA && grd[x][y] != DNGN_DEEP_WATER) - && coinflip()) + && !grid_destroys_items(grd[x][y]) && coinflip()) { copy_item_to_grid( *item, x, y, 1 ); } // if (thing_throw == 2) ... } -// somewhat complicated BOUNCE function -// returns # of times beam bounces during routine (usually 1) -// -// step 1 is always the step value from the stepping direction. -#define B_HORZ 1 -#define B_VERT 2 -#define B_BOTH 3 - -static int bounce(int &step1, int &step2, int w1, int w2, int &n1, int &n2, - int l1, int l2, int &t1, int &t2, bool topBlocked, bool sideBlocked) +// Returns true if the beam hits the player, fuzzing the beam if necessary +// for monsters without see invis firing tracers at the player. +static bool found_player(const bolt &beam, int x, int y) { - int bounceType = 0; - int bounceCount = 1; - - if (topBlocked) bounceType = B_HORZ; - if (sideBlocked) bounceType = B_VERT; - if (topBlocked && sideBlocked) - { - // check for veritcal bounce only - if ((w2 + step2 - 50)/100 == (w2 - 50)/100) - bounceType = B_VERT; - else - bounceType = B_BOTH; - } - - switch (bounceType) - { - case B_VERT: // easiest - n1 = w1; - n2 = w2 + step2; - step1 = -step1; - t1 = n1 / 100; - t2 = (n2 + 50)/100; - // check top - if (t2 != n2/100 && topBlocked) - t2 = n2/100; - break; - case B_HORZ: // a little tricky - if (step2 > 0) - n2 = (100 + 200*(w2/100)) - (w2 + step2); - else - n2 = (100 + 200*((w2 - 50)/100)) - (w2 + step2); - n1 = w1 + step1; - t1 = n1 /100; - t2 = (n2 + 50) / 100; - step2 = -step2; - break; - case B_BOTH: - // vertical: - n1 = w1; - t1 = l1; - t2 = l2; - // horizontal: - if (step2 > 0) - n2 = (100 + 200*(w2/100)) - (w2 + step2); - else - n2 = (100 + 200*((w2 - 50)/100)) - (w2 + step2); - // reverse both directions - step1 =- step1; - step2 =- step2; - bounceCount = 2; - break; - default: - bounceCount = 0; - break; - } + const bool needs_fuzz = beam.is_tracer && !beam.can_see_invis + && you.invis; + const int dist = needs_fuzz? 2 : 0; - return (bounceCount); + return (grid_distance(x, y, you.x_pos, you.y_pos) <= dist); } -static bool fuzzyLine(int nx, int ny, int &tx, int &ty, int lx, int ly, - int stepx, int stepy, bool roundX, bool roundY) -{ - bool fuzzyOK = false; - int fx, fy; // fuzzy x,y - - // BEGIN fuzzy line algorithm - fx = tx; - fy = ty; - if (roundY) - { - // try up - fy = (ny + 100) / 100; - // check for monotonic - if (fy != ty && ((stepy>0 && fy >= ly) - || (stepy<0 && fy <= ly))) - fuzzyOK = true; - // see if up try is blocked - if (fuzzyOK && grd[tx][fy] < MINMOVE) - fuzzyOK = false; - - // try down - if (!fuzzyOK) - fy = ny / 100; - // check for monotonic - if (fy != ty && ((stepy>0 && fy >= ly) - || (stepy<0 && fy <= ly))) - fuzzyOK = true; - if (fuzzyOK && grd[tx][fy] < MINMOVE) - fuzzyOK = false; - } - if (roundX) - { - // try up - fx = (nx + 100) / 100; - // check for monotonic - if (fx != tx && ((stepx>0 && fx >= lx) - || (stepx<0 && fx <= lx))) - fuzzyOK = true; - // see if up try is blocked - if (fuzzyOK && grd[fx][ty] < MINMOVE) - fuzzyOK = false; - - // try down - if (!fuzzyOK) - fx = nx / 100; - // check for monotonic - if (fx != tx && ((stepx>0 && fx >= lx) - || (stepx<0 && fx <= lx))) - fuzzyOK = true; - if (fuzzyOK && grd[fx][ty] < MINMOVE) - fuzzyOK = false; - } - // END fuzzy line algorithm - - if (fuzzyOK) - { - tx = fx; - ty = fy; - } - - return (fuzzyOK); -} - -// affects a single cell. -// returns the amount of extra range 'used up' by this beam -// during the affectation. -// -// pseudo-code: -// -// 1. If wall, and wall affecting non-tracer, affect the wall. -// 1b. If for some reason the wall-affect didn't make it into -// a non-wall, return affect_wall() -// 2. for non-tracers, produce cloud effects affect_place_clouds() -// 3. if cell holds player, affect player affect_player() -// 4. if cell holds monster, affect monster affect_monster() -// 5. return range used affectation. - -static int affect(struct bolt &beam, int x, int y) +int affect(struct bolt &beam, int x, int y) { // extra range used by hitting something int rangeUsed = 0; - if (grd[x][y] < MINMOVE) + // line of sight never affects anything + if (beam.flavour == BEAM_LINE_OF_SIGHT) + return (0); + + if (grid_is_solid(grd[x][y])) { - if (beam.isTracer) // tracers always stop on walls. + if (beam.is_tracer) // tracers always stop on walls. return (BEAM_STOP); if (affectsWalls(beam)) { rangeUsed += affect_wall(beam, x, y); } - // if it's still a wall, quit - we can't do anything else to + // if it's still a wall, quit - we can't do anything else to // a wall. Otherwise effects (like clouds, etc) are still possible. - if (grd[x][y] < MINMOVE) + if (grid_is_solid(grd[x][y])) return (rangeUsed); } // grd[x][y] will NOT be a wall for the remainder of this function. // if not a tracer, place clouds - if (!beam.isTracer) + if (!beam.is_tracer) rangeUsed += affect_place_clouds(beam, x, y); - // if player is at this location, try to affect unless term_on_target - if (x == you.x_pos && y == you.y_pos) + // if player is at this location, try to affect unless term_on_target + if (found_player(beam, x, y)) { - if (beam_term_on_target(beam) && !beam.isExplosion) + // Done this way so that poison blasts affect the target once (via + // place_cloud) and explosion spells only affect the target once + // (during the explosion phase, not an initial hit during the + // beam phase). + if (!beam.is_big_cloud + && (!beam.is_explosion || beam.in_explosion_phase)) + { + rangeUsed += affect_player( beam ); + } + + if (beam_term_on_target(beam, x, y)) return (BEAM_STOP); - - rangeUsed += affect_player(beam); } - // if there is a monster at this location, affect it + // if there is a monster at this location, affect it // submerged monsters aren't really there -- bwr int mid = mgrd[x][y]; if (mid != NON_MONSTER && !mons_has_ench( &menv[mid], ENCH_SUBMERGED )) { - if (beam_term_on_target(beam) && !beam.isExplosion) + if (!beam.is_big_cloud + && (!beam.is_explosion || beam.in_explosion_phase)) + { + rangeUsed += affect_monster( beam, &menv[mid] ); + } + + if (beam_term_on_target(beam, x, y)) return (BEAM_STOP); - - struct monsters* monster = &menv[mid]; - rangeUsed += affect_monster(beam, monster); } return (rangeUsed); @@ -2534,7 +2491,7 @@ static bool affectsWalls(struct bolt &beam) { // don't know of any explosion that affects walls. But change it here // if there is. - if (beam.isExplosion) + if (beam.is_explosion) return (false); // digging @@ -2546,7 +2503,7 @@ static bool affectsWalls(struct bolt &beam) if (beam.flavour == BEAM_DISINTEGRATION && beam.damage.num >= 3) return (true); - // eye of devestation? + // eye of devastation? if (beam.flavour == BEAM_NUKE) return (true); @@ -2574,15 +2531,15 @@ static int affect_wall(struct bolt &beam, int x, int y) { grd[x][y] = DNGN_FLOOR; - if (!beam.msgGenerated) + if (!beam.msg_generated) { if (!silenced(you.x_pos, you.y_pos)) { - mpr("You hear a grinding noise."); - beam.obviousEffect = true; + mpr("You hear a grinding noise.", MSGCH_SOUND); + beam.obvious_effect = true; } - beam.msgGenerated = true; + beam.msg_generated = true; } } @@ -2601,22 +2558,25 @@ static int affect_wall(struct bolt &beam, int x, int y) grd[ x ][ y ] = DNGN_FLOOR; if (!silenced(you.x_pos, you.y_pos)) { - mpr("You hear a grinding noise."); - beam.obviousEffect = true; + mpr("You hear a grinding noise.", MSGCH_SOUND); + beam.obvious_effect = true; } } - if (targ_grid == DNGN_ORCISH_IDOL || (targ_grid >= DNGN_SILVER_STATUE - && targ_grid <= DNGN_STATUE_39)) + if (targ_grid == DNGN_ORCISH_IDOL + || targ_grid == DNGN_SILVER_STATUE + || targ_grid == DNGN_GRANITE_STATUE + || targ_grid == DNGN_ORANGE_CRYSTAL_STATUE) { grd[x][y] = DNGN_FLOOR; if (!silenced(you.x_pos, you.y_pos)) { if (!see_grid( x, y )) - mpr("You hear a hideous screaming!"); + mpr("You hear a hideous screaming!", MSGCH_SOUND); else - mpr("The statue screams as its substance crumbles away!"); + mpr("The statue screams as its substance crumbles away!", + MSGCH_SOUND); } else { @@ -2625,11 +2585,11 @@ static int affect_wall(struct bolt &beam, int x, int y) } if (targ_grid == DNGN_SILVER_STATUE) - Visible_Statue[ STATUE_SILVER ] = 0; + you.visible_statue[ STATUE_SILVER ] = 0; else if (targ_grid == DNGN_ORANGE_CRYSTAL_STATUE) - Visible_Statue[ STATUE_ORANGE_CRYSTAL ] = 0; + you.visible_statue[ STATUE_ORANGE_CRYSTAL ] = 0; - beam.obviousEffect = 1; + beam.obvious_effect = 1; } return (BEAM_STOP); @@ -2642,7 +2602,7 @@ static int affect_place_clouds(struct bolt &beam, int x, int y) { int cloud_type; - if (beam.isExplosion) + if (beam.in_explosion_phase) { affect_place_explosion_clouds( beam, x, y ); return (0); // return value irrelevant for explosions @@ -2656,7 +2616,7 @@ static int affect_place_clouds(struct bolt &beam, int x, int y) env.cloud[ env.cgrid[x][y] ].type = 1 + random2(8); // now exit (all enchantments) - if (beam.beam_name[0] == '0') + if (beam.name[0] == '0') return (0); int clouty = env.cgrid[x][y]; @@ -2673,7 +2633,7 @@ static int affect_place_clouds(struct bolt &beam, int x, int y) if (!silenced(x, y) && !silenced(you.x_pos, you.y_pos)) { - mpr("You hear a sizzling sound!"); + mpr("You hear a sizzling sound!", MSGCH_SOUND); } delete_cloud( clouty ); @@ -2682,7 +2642,7 @@ static int affect_place_clouds(struct bolt &beam, int x, int y) } // POISON BLAST - if (strcmp(beam.beam_name, "blast of poison") == 0) + if (beam.name == "blast of poison") { cloud_type = YOU_KILL(beam.thrower) ? CLOUD_POISON : CLOUD_POISON_MON; @@ -2699,29 +2659,35 @@ static int affect_place_clouds(struct bolt &beam, int x, int y) } // ORB OF ENERGY - if (strcmp(beam.beam_name, "orb of energy") == 0) + if (beam.name == "orb of energy") place_cloud( CLOUD_PURP_SMOKE, x, y, random2(5) + 1 ); // GREAT BLAST OF COLD - if (strcmp(beam.beam_name, "great blast of cold") == 0) + if (beam.name == "great blast of cold") place_cloud( CLOUD_COLD, x, y, random2(5) + 3 ); // BALL OF STEAM - if (strcmp(beam.beam_name, "ball of steam") == 0) + if (beam.name == "ball of steam") { cloud_type = YOU_KILL(beam.thrower) ? CLOUD_STEAM : CLOUD_STEAM_MON; place_cloud( cloud_type, x, y, random2(5) + 2 ); } + if (beam.flavour == BEAM_MIASMA) + { + cloud_type = YOU_KILL( beam.thrower ) ? CLOUD_MIASMA : CLOUD_MIASMA_MON; + place_cloud( cloud_type, x, y, random2(5) + 2 ); + } + // STICKY FLAME - if (strcmp(beam.beam_name, "sticky flame") == 0) + if (beam.name == "sticky flame") { place_cloud( CLOUD_BLACK_SMOKE, x, y, random2(4) + 2 ); } // POISON GAS - if (strcmp(beam.beam_name, "poison gas") == 0) + if (beam.name == "poison gas") { cloud_type = YOU_KILL(beam.thrower) ? CLOUD_POISON : CLOUD_POISON_MON; place_cloud( cloud_type, x, y, random2(4) + 3 ); @@ -2809,18 +2775,18 @@ static void affect_place_explosion_clouds(struct bolt &beam, int x, int y) } // then check for more specific explosion cloud types. - if (stricmp(beam.beam_name, "ice storm") == 0) + if (beam.name == "ice storm") { place_cloud( CLOUD_COLD, x, y, 2 + random2avg(5, 2) ); } - if (stricmp(beam.beam_name, "stinking cloud") == 0) + if (beam.name == "stinking cloud") { duration = 1 + random2(4) + random2( (beam.ench_power / 50) + 1 ); place_cloud( CLOUD_STINK, x, y, duration ); } - if (strcmp(beam.beam_name, "great blast of fire") == 0) + if (beam.name == "great blast of fire") { duration = 1 + random2(5) + roll_dice( 2, beam.ench_power / 5 ); @@ -2855,7 +2821,7 @@ static void affect_items(struct bolt &beam, int x, int y) break; } - if (stricmp(beam.beam_name, "hellfire") == 0) + if (beam.name == "hellfire") objs_vulnerable = OBJ_SCROLLS; if (igrd[x][y] != NON_ITEM) @@ -2879,11 +2845,16 @@ static void affect_items(struct bolt &beam, int x, int y) } } +static int beam_ouch_agent(const bolt &beam) +{ + return YOU_KILL(beam.thrower)? 0 : beam.beam_source; +} + // A little helper function to handle the calling of ouch()... static void beam_ouch( int dam, struct bolt &beam ) { // The order of this is important. - if (YOU_KILL( beam.thrower ) && !beam.aux_source) + if (YOU_KILL( beam.thrower ) && beam.aux_source.empty()) { ouch( dam, 0, KILLED_BY_TARGETTING ); } @@ -2892,12 +2863,44 @@ static void beam_ouch( int dam, struct bolt &beam ) if (beam.flavour == BEAM_SPORE) ouch( dam, beam.beam_source, KILLED_BY_SPORE ); else - ouch( dam, beam.beam_source, KILLED_BY_BEAM, beam.aux_source ); + ouch( dam, beam.beam_source, KILLED_BY_BEAM, + beam.aux_source.c_str() ); } else // KILL_MISC || (YOU_KILL && aux_source) { - ouch( dam, beam.beam_source, KILLED_BY_WILD_MAGIC, beam.aux_source ); + ouch( dam, beam.beam_source, KILLED_BY_WILD_MAGIC, + beam.aux_source.c_str() ); + } +} + +// [ds] Apply a fuzz if the monster lacks see invisible and is trying to target +// an invisible player. This makes invisibility slightly more powerful. +static bool fuzz_invis_tracer(bolt &beem) +{ + // Did the monster have a rough idea of where you are? + int dist = grid_distance(beem.target_x, beem.target_y, + you.x_pos, you.y_pos); + + // No, ditch this. + if (dist > 2) + return (false); + + // Apply fuzz now. + int xfuzz = random_range(-2, 2), + yfuzz = random_range(-2, 2); + + const int newx = beem.target_x + xfuzz, + newy = beem.target_y + yfuzz; + if (in_bounds(newx, newy) + && (newx != beem.source_x + || newy != beem.source_y)) + { + beem.target_x = newx; + beem.target_y = newy; } + + // Fire away! + return (true); } // return amount of extra range used up by affectation of the player @@ -2910,23 +2913,20 @@ static int affect_player( struct bolt &beam ) return (0); // check for tracer - if (beam.isTracer) + if (beam.is_tracer) { // check can see player - // XXX: note the cheat to allow for ME_ALERT to target the player... - // replace this with a time since alert system, rather than just - // peeking to see if the character is still there. -- bwr - if (beam.canSeeInvis || !you.invis - || (you.x_pos == beam.target_x && you.y_pos == beam.target_y)) + if (beam.can_see_invis || !you.invis + || fuzz_invis_tracer(beam)) { - if (beam.isFriendly) + if (beam.is_friendly) { beam.fr_count += 1; beam.fr_power += you.experience_level; } else { - beam.foe_count += 1; + beam.foe_count++; beam.foe_power += you.experience_level; } } @@ -2934,24 +2934,26 @@ static int affect_player( struct bolt &beam ) } // BEGIN real beam code - beam.msgGenerated = true; + beam.msg_generated = true; - // use beamHit, NOT beam.hit, for modification of tohit.. geez! + // use beamHit, NOT beam.hit, for modification of tohit.. geez! beamHit = beam.hit; - if (beam.beam_name[0] != '0') + if (beam.name[0] != '0') { - if (!beam.isExplosion && !beam.aimedAtFeet) + if (!beam.is_explosion && !beam.aimed_at_feet) { // BEGIN BEAM/MISSILE - int dodge = random2limit( player_evasion(), 40 ) + // [ds] To compensate for the new monster beam hit numbers, allow + // for slightly more benefit from evasion (limit was 40) + int dodge = random2limit( player_evasion(), 45 ) + random2( you.dex ) / 3 - 2; - if (beam.isBeam) + if (beam.is_beam) { // beams can be dodged if (player_light_armour() - && !beam.aimedAtFeet && coinflip()) + && !beam.aimed_at_feet && coinflip()) { exercise(SK_DODGING, 1); } @@ -2967,34 +2969,34 @@ static int affect_player( struct bolt &beam ) if (beamHit < dodge) { - strcpy(info, "The "); - strcat(info, beam.beam_name); - strcat(info, " misses you."); - mpr(info); + mprf("The %s misses you.", beam.name.c_str()); return (0); // no extra used by miss! } } - else + else if (beam_is_blockable(beam)) { // non-beams can be blocked or dodged if (you.equip[EQ_SHIELD] != -1 - && !beam.aimedAtFeet + && !beam.aimed_at_feet && player_shield_class() > 0) { int exer = one_chance_in(3) ? 1 : 0; - const int hit = random2( beam.hit * 5 - + 10 * you.shield_blocks * you.shield_blocks ); + // [dshaligram] beam.hit multiplier lowered to 3 - was 5. + // In favour of blocking, dex multiplier changed to .25 + // (was .2), added shield skill into the equation with a + // skill bump. + const int hit = random2( beam.hit * 3 + + 5 * you.shield_blocks * you.shield_blocks ); const int block = random2(player_shield_class()) - + (random2(you.dex) / 5) - 1; + + (random2(you.dex) / 4) + + (random2(skill_bump(SK_SHIELDS)) / 4) + - 1; if (hit < block) { you.shield_blocks++; - snprintf( info, INFO_SIZE, "You block the %s.", - beam.beam_name ); - mpr( info ); - + mprf( "You block the %s.", beam.name.c_str() ); exercise( SK_SHIELDS, exer + 1 ); return (BEAM_STOP); } @@ -3003,7 +3005,7 @@ static int affect_player( struct bolt &beam ) exercise( SK_SHIELDS, exer ); } - if (player_light_armour() && !beam.aimedAtFeet + if (player_light_armour() && !beam.aimed_at_feet && coinflip()) exercise(SK_DODGING, 1); @@ -3017,9 +3019,7 @@ static int affect_player( struct bolt &beam ) // miss message if (beamHit < dodge || you.duration[DUR_DEFLECT_MISSILES]) { - strcpy(info, "The "); - strcat(info, beam.beam_name); - strcat(info, " misses you."); + mprf("The %s misses you.", beam.name.c_str()); return (0); } } @@ -3032,63 +3032,70 @@ static int affect_player( struct bolt &beam ) && beam.flavour != BEAM_INVISIBILITY && beam.flavour != BEAM_HEALING && ((beam.flavour != BEAM_TELEPORT && beam.flavour != BEAM_BANISH) - || !beam.aimedAtFeet) + || !beam.aimed_at_feet) && you_resist_magic( beam.ench_power )) { canned_msg(MSG_YOU_RESIST); return (range_used_on_hit(beam)); } + ench_animation( beam.flavour ); + // these colors are misapplied - see mons_ench_f2() {dlb} switch (beam.flavour) { case BEAM_SLOW: potion_effect( POT_SLOWING, beam.ench_power ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; // slow case BEAM_HASTE: potion_effect( POT_SPEED, beam.ench_power ); contaminate_player( 1 ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; // haste case BEAM_HEALING: potion_effect( POT_HEAL_WOUNDS, beam.ench_power ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; // heal (heal wounds potion eff) case BEAM_PARALYSIS: potion_effect( POT_PARALYSIS, beam.ench_power ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; // paralysis case BEAM_CONFUSION: potion_effect( POT_CONFUSION, beam.ench_power ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; // confusion case BEAM_INVISIBILITY: potion_effect( POT_INVISIBILITY, beam.ench_power ); contaminate_player( 1 + random2(2) ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; // invisibility // 6 is used by digging case BEAM_TELEPORT: you_teleport(); - beam.obviousEffect = true; + beam.obvious_effect = true; + break; + + case BEAM_BLINK: + random_blink(0); + beam.obvious_effect = true; break; case BEAM_POLYMORPH: mpr("This is polymorph other only!"); - beam.obviousEffect = true; + beam.obvious_effect = true; break; case BEAM_CHARM: potion_effect( POT_CONFUSION, beam.ench_power ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; // enslavement - confusion? case BEAM_BANISH: @@ -3100,7 +3107,7 @@ static int affect_player( struct bolt &beam ) mpr("You are cast into the Abyss!"); more(); banished(DNGN_ENTER_ABYSS); - beam.obviousEffect = true; + beam.obvious_effect = true; break; // banishment to the abyss case BEAM_PAIN: // pain @@ -3112,11 +3119,11 @@ static int affect_player( struct bolt &beam ) mpr("Pain shoots through your body!"); - if (!beam.aux_source) + if (beam.aux_source.empty()) beam.aux_source = "by nerve-wracking pain"; beam_ouch( roll_dice( beam.damage ), beam ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; case BEAM_DISPEL_UNDEAD: @@ -3128,21 +3135,21 @@ static int affect_player( struct bolt &beam ) mpr( "You convulse!" ); - if (!beam.aux_source) + if (beam.aux_source.empty()) beam.aux_source = "by dispel undead"; beam_ouch( roll_dice( beam.damage ), beam ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; case BEAM_DISINTEGRATION: mpr("You are blasted!"); - if (!beam.aux_source) + if (beam.aux_source.empty()) beam.aux_source = "disintegration bolt"; beam_ouch( roll_dice( beam.damage ), beam ); - beam.obviousEffect = true; + beam.obvious_effect = true; break; default: @@ -3160,12 +3167,12 @@ static int affect_player( struct bolt &beam ) // THE BEAM IS NOW GUARANTEED TO BE A NON-ENCHANTMENT WHICH HIT - snprintf( info, INFO_SIZE, "The %s %s you!", - beam.beam_name, (beam.isExplosion ? "engulfs" : "hits") ); - mpr( info ); + const bool engulfs = (beam.is_explosion || beam.is_big_cloud); + mprf( "The %s %s you!", + beam.name.c_str(), (engulfs) ? "engulfs" : "hits" ); int hurted = 0; - int burn_power = (beam.isExplosion) ? 5 : ((beam.isBeam) ? 3 : 2); + int burn_power = (beam.is_explosion) ? 5 : ((beam.is_beam) ? 3 : 2); // Roll the damage hurted += roll_dice( beam.damage ); @@ -3194,7 +3201,7 @@ static int affect_player( struct bolt &beam ) if (you.equip[EQ_BODY_ARMOUR] != -1) { if (!player_light_armour() && one_chance_in(4) - && random2(1000) <= mass_item( you.inv[you.equip[EQ_BODY_ARMOUR]] )) + && random2(1000) <= item_mass( you.inv[you.equip[EQ_BODY_ARMOUR]] )) { exercise( SK_ARMOUR, 1 ); } @@ -3205,21 +3212,38 @@ static int affect_player( struct bolt &beam ) hurted = check_your_resists( hurted, beam.flavour ); + if (beam.flavour == BEAM_MIASMA && hurted > 0) + { + if (player_res_poison() <= 0) + poison_player(1); + + if (one_chance_in( 3 + 2 * player_prot_life() )) + potion_effect( POT_SLOWING, 5 ); + } + // poisoning - if (strstr(beam.beam_name, "poison") != NULL + if (beam.name.find("poison") != std::string::npos && beam.flavour != BEAM_POISON && beam.flavour != BEAM_POISON_ARROW && !player_res_poison()) { - if (hurted || (strstr( beam.beam_name, "needle" ) != NULL + if (hurted || (beam.ench_power == AUTOMATIC_HIT && random2(100) < 90 - (3 * player_AC()))) { poison_player( 1 + random2(3) ); } } + if (beam.name.find("curare") != std::string::npos) + { + if (random2(100) < 90 - (3 * player_AC())) + { + curare_hits_player( beam_ouch_agent(beam), 1 + random2(3) ); + } + } + // sticky flame - if (strcmp(beam.beam_name, "sticky flame") == 0 + if (beam.name == "sticky flame" && (you.species != SP_MOTTLED_DRACONIAN || you.experience_level < 6)) { @@ -3227,24 +3251,24 @@ static int affect_player( struct bolt &beam ) you.duration[DUR_LIQUID_FLAMES] += random2avg(7, 3) + 1; } - // simple cases for scroll burns - if (beam.flavour == BEAM_LAVA || stricmp(beam.beam_name, "hellfire") == 0) - scrolls_burn( burn_power, OBJ_SCROLLS ); + // simple cases for scroll burns FIXME + if (beam.flavour == BEAM_LAVA || beam.name == "hellfire") + expose_player_to_element(BEAM_LAVA, burn_power); // more complex (geez..) - if (beam.flavour == BEAM_FIRE && strcmp(beam.beam_name, "ball of steam") != 0) - scrolls_burn( burn_power, OBJ_SCROLLS ); + if (beam.flavour == BEAM_FIRE && beam.name != "ball of steam") + expose_player_to_element(BEAM_FIRE, burn_power); // potions exploding if (beam.flavour == BEAM_COLD) - scrolls_burn( burn_power, OBJ_POTIONS ); + expose_player_to_element(BEAM_COLD, burn_power); if (beam.flavour == BEAM_ACID) splash_with_acid(5); // spore pops - if (beam.isExplosion && beam.flavour == BEAM_SPORE) - scrolls_burn( 2, OBJ_FOOD ); + if (beam.in_explosion_phase && beam.flavour == BEAM_SPORE) + expose_player_to_element(BEAM_SPORE, burn_power); #if DEBUG_DIAGNOSTICS snprintf( info, INFO_SIZE, "Damage: %d", hurted ); @@ -3256,10 +3280,21 @@ static int affect_player( struct bolt &beam ) return (range_used_on_hit( beam )); } +static int beam_source(const bolt &beam) +{ + return MON_KILL(beam.thrower) ? beam.beam_source : + beam.thrower == KILL_MISC ? MHITNOT : + MHITYOU; +} + // return amount of range used up by affectation of this monster -static int affect_monster(struct bolt &beam, struct monsters *mon) +static int affect_monster(struct bolt &beam, struct monsters *mon) { - int tid = mgrd[mon->x][mon->y]; + const int tid = mgrd[mon->x][mon->y]; + const int mons_type = menv[tid].type; + const int thrower = YOU_KILL(beam.thrower)? KILL_YOU_MISSILE + : KILL_MON_MISSILE; + int hurt; int hurt_final; @@ -3268,37 +3303,59 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) return (0); // fire storm creates these, so we'll avoid affecting them - if (strcmp(beam.beam_name, "great blast of fire") == 0 + if (beam.name == "great blast of fire" && mon->type == MONS_FIRE_VORTEX) { return (0); } // check for tracer - if (beam.isTracer) + if (beam.is_tracer) { // check can see other monster - if (!beam.canSeeInvis && mons_has_ench(&menv[tid], ENCH_INVIS)) + if (!beam.can_see_invis && mons_has_ench(&menv[tid], ENCH_INVIS)) { // can't see this monster, ignore it return 0; } } + else if ((beam.flavour == BEAM_DISINTEGRATION || beam.flavour == BEAM_NUKE) + && mons_is_statue(mons_type)) + { + if (!silenced(you.x_pos, you.y_pos)) + { + if (!see_grid( mon->x, mon->y )) + mpr("You hear a hideous screaming!", MSGCH_SOUND); + else + mpr("The statue screams as its substance crumbles away!", + MSGCH_SOUND); + } + else + { + if (see_grid( mon->x, mon->y )) + mpr("The statue twists and shakes as its substance " + "crumbles away!"); + } + beam.obvious_effect = true; + mon->hit_points = 0; + monster_die(mon, thrower, beam.beam_source); + return (BEAM_STOP); + } - if (beam.beam_name[0] == '0') + if (beam.name[0] == '0') { - if (beam.isTracer) + if (beam.is_tracer) { // enchant case -- enchantments always hit, so update target immed. - if (beam.isFriendly ^ mons_friendly(mon)) + if (beam.is_friendly != mons_friendly(mon)) { beam.foe_count += 1; - beam.foe_power += mons_power(tid); + beam.foe_power += mons_power(mons_type); } else { beam.fr_count += 1; - beam.fr_power += mons_power(tid); + beam.fr_power += mons_power(mons_type); } return (range_used_on_hit(beam)); @@ -3310,16 +3367,20 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) // naughty (even if a monster might resist) if (nasty_beam(mon, beam)) { - if (mons_friendly(mon) && YOU_KILL(beam.thrower)) - naughty(NAUGHTY_ATTACK_FRIEND, 5); + if (YOU_KILL( beam.thrower )) + { + if (mons_friendly( mon )) + did_god_conduct( DID_ATTACK_FRIEND, 5 ); + + if (mons_holiness( mon ) == MH_HOLY) + did_god_conduct( DID_ATTACK_HOLY, mon->hit_dice ); + } - behaviour_event( mon, ME_ANNOY, - MON_KILL(beam.thrower) ? beam.beam_source : MHITYOU ); + behaviour_event( mon, ME_ANNOY, beam_source(beam) ); } else { - behaviour_event( mon, ME_ALERT, - MON_KILL(beam.thrower) ? beam.beam_source : MHITYOU ); + behaviour_event( mon, ME_ALERT, beam_source(beam) ); } // !@#*( affect_monster_enchantment() has side-effects on @@ -3327,17 +3388,21 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) // so call it now and store. int rangeUsed = range_used_on_hit(beam); + // Doing this here so that the player gets to see monsters + // "flicker and vanish" when turning invisible.... + ench_animation( beam.flavour, mon ); + // now do enchantment affect int ench_result = affect_monster_enchantment(beam, mon); switch(ench_result) { case MON_RESIST: if (simple_monster_message(mon, " resists.")) - beam.msgGenerated = true; + beam.msg_generated = true; break; case MON_UNAFFECTED: if (simple_monster_message(mon, " is unaffected.")) - beam.msgGenerated = true; + beam.msg_generated = true; break; default: break; @@ -3349,10 +3414,10 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) // BEGIN non-enchantment (could still be tracer) - if (mons_has_ench( mon, ENCH_SUBMERGED ) && !beam.aimedAtFeet) + if (mons_has_ench( mon, ENCH_SUBMERGED ) && !beam.aimed_at_feet) return (0); // missed me! - // we need to know how much the monster _would_ be hurt by this, before + // we need to know how much the monster _would_ be hurt by this, before // we decide if it actually hits. // Roll the damage: @@ -3360,7 +3425,7 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) hurt_final = hurt; - if (beam.isTracer) + if (beam.is_tracer) hurt_final -= mon->armour_class / 2; else hurt_final -= random2(1 + mon->armour_class); @@ -3380,12 +3445,12 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) const int old_hurt = hurt_final; #endif - // check monster resists, _without_ side effects (since the + // check monster resists, _without_ side effects (since the // beam/missile might yet miss!) hurt_final = mons_adjust_flavoured( mon, beam, hurt_final, false ); #if DEBUG_DIAGNOSTICS - if (!beam.isTracer) + if (!beam.is_tracer) { snprintf( info, INFO_SIZE, "Monster: %s; Damage: pre-AC: %d; post-AC: %d; post-resist: %d", @@ -3395,27 +3460,27 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) } #endif - // now, we know how much this monster would (probably) be + // now, we know how much this monster would (probably) be // hurt by this beam. - if (beam.isTracer) + if (beam.is_tracer) { if (hurt_final != 0) { - // monster could be hurt somewhat, but only apply the + // monster could be hurt somewhat, but only apply the // monster's power based on how badly it is affected. - // For example, if a fire giant (power 16) threw a - // fireball at another fire giant, and it only took - // 1/3 damage, then power of 5 would be applied to + // For example, if a fire giant (power 16) threw a + // fireball at another fire giant, and it only took + // 1/3 damage, then power of 5 would be applied to // foe_power or fr_power. - if (beam.isFriendly ^ mons_friendly(mon)) + if (beam.is_friendly != mons_friendly(mon)) { beam.foe_count += 1; - beam.foe_power += hurt_final * mons_power(tid) / hurt; + beam.foe_power += 2 * hurt_final * mons_power(mons_type) / hurt; } else { beam.fr_count += 1; - beam.fr_power += hurt_final * mons_power(tid) / hurt; + beam.fr_power += 2 * hurt_final * mons_power(mons_type) / hurt; } } // either way, we could hit this monster, so return range used @@ -3428,39 +3493,33 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) // player beams which hit friendly MIGHT annoy them and be considered // naughty if they do much damage (this is so as not to penalize // players that fling fireballs into a melee with fire elementals - // on their side - the elementals won't give a sh*t, after all) + // on their side - the elementals won't give a sh*t, after all) if (nasty_beam(mon, beam)) { - // could be naughty if it's your beam & the montster is friendly - if (mons_friendly(mon) && YOU_KILL(beam.thrower)) + if (YOU_KILL(beam.thrower)) { - // but did you do enough damage to piss them off? - if (hurt_final > mon->hit_dice / 3) - { - naughty(NAUGHTY_ATTACK_FRIEND, 5); - behaviour_event( mon, ME_ANNOY, MHITYOU ); - } - } - else - { - behaviour_event(mon, ME_ANNOY, - MON_KILL(beam.thrower) ? beam.beam_source : MHITYOU ); + if (mons_friendly(mon)) + did_god_conduct( DID_ATTACK_FRIEND, 5 ); + + if (mons_holiness( mon ) == MH_HOLY) + did_god_conduct( DID_ATTACK_HOLY, mon->hit_dice ); } + + behaviour_event(mon, ME_ANNOY, beam_source(beam) ); } // explosions always 'hit' - if (!beam.isExplosion && beam.hit < random2(mon->evasion)) + const bool engulfs = (beam.is_explosion || beam.is_big_cloud); + + if (!engulfs && beam.hit < random2(mon->evasion)) { // if the PLAYER cannot see the monster, don't tell them anything! if (player_monster_visible( &menv[tid] ) && mons_near(mon)) { - strcpy(info, "The "); - strcat(info, beam.beam_name); - strcat(info, " misses "); - strcat(info, ptr_monam(mon, DESC_NOCAP_THE)); - strcat(info, "."); - mpr(info); + mprf("The %s misses %s.", + beam.name.c_str(), + ptr_monam(mon, DESC_NOCAP_THE)); } return (0); } @@ -3468,17 +3527,12 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) // the beam hit. if (mons_near(mon)) { - strcpy(info, "The "); - strcat(info, beam.beam_name); - strcat(info, beam.isExplosion?" engulfs ":" hits "); - - if (player_monster_visible( &menv[tid] )) - strcat(info, ptr_monam(mon, DESC_NOCAP_THE)); - else - strcat(info, "something"); - - strcat(info, "."); - mpr(info); + mprf("The %s %s %s.", + beam.name.c_str(), + engulfs? "engulfs" : "hits", + player_monster_visible(&menv[tid])? + ptr_monam(mon, DESC_NOCAP_THE) + : "something"); } else { @@ -3486,12 +3540,7 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) // if _they_ fired a missile (not beam) if (!silenced(you.x_pos, you.y_pos) && beam.flavour == BEAM_MISSILE && YOU_KILL(beam.thrower)) - { - strcpy(info, "The "); - strcat(info, beam.beam_name); - strcat(info, " hits something."); - mpr(info); - } + mprf(MSGCH_SOUND, "The %s hits something.", beam.name.c_str()); } // note that hurt_final was calculated above, so we don't need it again. @@ -3502,8 +3551,6 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) // now hurt monster hurt_monster( mon, hurt_final ); - int thrower = YOU_KILL(beam.thrower) ? KILL_YOU_MISSILE : KILL_MON_MISSILE; - if (mon->hit_points < 1) { monster_die(mon, thrower, beam.beam_source); @@ -3514,7 +3561,7 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) print_wounds(mon); // sticky flame - if (strcmp(beam.beam_name, "sticky flame") == 0) + if (beam.name == "sticky flame") { int levels = 1 + random2( hurt_final ) / 2; if (levels > 4) @@ -3526,11 +3573,12 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) /* looks for missiles which aren't poison but are poison*ed* */ - if (strstr(beam.beam_name, "poison") != NULL + if (beam.name.find("poison") != std::string::npos && beam.flavour != BEAM_POISON && beam.flavour != BEAM_POISON_ARROW) { - if (strstr(beam.beam_name, "needle") != NULL + // ench_power == AUTOMATIC_HIT if this is a poisoned needle. + if (beam.ench_power == AUTOMATIC_HIT && random2(100) < 90 - (3 * mon->armour_class)) { poison_monster( mon, YOU_KILL(beam.thrower), 2 ); @@ -3541,7 +3589,14 @@ static int affect_monster(struct bolt &beam, struct monsters *mon) } } - if (mons_is_mimic( mon->type )) + bool wake_mimic = true; + if (beam.name.find("curare") != std::string::npos) + { + if (curare_hits_monster( beam, mon, YOU_KILL(beam.thrower), 2 )) + wake_mimic = false; + } + + if (wake_mimic && mons_is_mimic( mon->type )) mimic_alert(mon); } @@ -3553,29 +3608,44 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) if (beam.flavour == BEAM_TELEPORT) // teleportation { if (check_mons_resist_magic( mon, beam.ench_power ) - && !beam.aimedAtFeet) + && !beam.aimed_at_feet) { return (MON_RESIST); } if (simple_monster_message(mon, " looks slightly unstable.")) - beam.obviousEffect = true; + beam.obvious_effect = true; monster_teleport(mon, false); return (MON_AFFECTED); } + if (beam.flavour == BEAM_BLINK) + { + if (!beam.aimed_at_feet + && check_mons_resist_magic( mon, beam.ench_power )) + { + return (MON_RESIST); + } + + if (mons_near( mon ) && player_monster_visible( mon )) + beam.obvious_effect = true; + + monster_blink( mon ); + return (MON_AFFECTED); + } + if (beam.flavour == BEAM_POLYMORPH) { - if (mons_holiness( mon->type ) != MH_NATURAL) + if (mons_holiness( mon ) != MH_NATURAL) return (MON_UNAFFECTED); if (check_mons_resist_magic( mon, beam.ench_power )) return (MON_RESIST); if (monster_polymorph(mon, RANDOM_MONSTER, 100)) - beam.obviousEffect = true; + beam.obvious_effect = true; return (MON_AFFECTED); } @@ -3592,13 +3662,13 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) else monster_die(mon, KILL_RESET, beam.beam_source); - beam.obviousEffect = true; + beam.obvious_effect = true; return (MON_AFFECTED); } if (beam.flavour == BEAM_DEGENERATE) { - if (mons_holiness(mon->type) != MH_NATURAL + if (mons_holiness(mon) != MH_NATURAL || mon->type == MONS_PULSATING_LUMP) { return (MON_UNAFFECTED); @@ -3608,18 +3678,18 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) return (MON_RESIST); if (monster_polymorph(mon, MONS_PULSATING_LUMP, 100)) - beam.obviousEffect = true; + beam.obvious_effect = true; return (MON_AFFECTED); } if (beam.flavour == BEAM_DISPEL_UNDEAD) { - if (mons_holiness(mon->type) != MH_UNDEAD) + if (mons_holiness(mon) != MH_UNDEAD) return (MON_UNAFFECTED); if (simple_monster_message(mon, " convulses!")) - beam.obviousEffect = true; + beam.obvious_effect = true; hurt_monster( mon, roll_dice( beam.damage ) ); @@ -3627,7 +3697,7 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) } if (beam.flavour == BEAM_ENSLAVE_UNDEAD - && mons_holiness(mon->type) == MH_UNDEAD) + && mons_holiness(mon) == MH_UNDEAD) { #if DEBUG_DIAGNOSTICS snprintf( info, INFO_SIZE, "HD: %d; pow: %d", @@ -3640,7 +3710,7 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) return (MON_RESIST); simple_monster_message(mon, " is enslaved."); - beam.obviousEffect = true; + beam.obvious_effect = true; // wow, permanent enslaving mon->attitude = ATT_FRIENDLY; @@ -3648,7 +3718,7 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) } if (beam.flavour == BEAM_ENSLAVE_DEMON - && mons_holiness(mon->type) == MH_DEMONIC) + && mons_holiness(mon) == MH_DEMONIC) { #if DEBUG_DIAGNOSTICS snprintf( info, INFO_SIZE, "HD: %d; pow: %d", @@ -3661,7 +3731,7 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) return (MON_RESIST); simple_monster_message(mon, " is enslaved."); - beam.obviousEffect = true; + beam.obvious_effect = true; // wow, permanent enslaving mon->attitude = ATT_FRIENDLY; @@ -3687,9 +3757,9 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) return (MON_UNAFFECTED); if (simple_monster_message(mon, " convulses in agony!")) - beam.obviousEffect = true; + beam.obvious_effect = true; - if (strstr( beam.beam_name, "agony" ) != NULL) + if (beam.name.find("agony") != std::string::npos) { // AGONY mon->hit_points = mon->hit_points / 2; @@ -3709,7 +3779,7 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) if (beam.flavour == BEAM_DISINTEGRATION) /* disrupt/disintegrate */ { if (simple_monster_message(mon, " is blasted.")) - beam.obviousEffect = true; + beam.obvious_effect = true; hurt_monster( mon, roll_dice( beam.damage ) ); @@ -3722,11 +3792,11 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) if (mons_has_ench( mon, ENCH_SLEEP_WARY )) // slept recently return (MON_RESIST); - if (mons_holiness(mon->type) != MH_NATURAL) // no unnatural + if (mons_holiness(mon) != MH_NATURAL) // no unnatural return (MON_UNAFFECTED); if (simple_monster_message(mon, " looks drowsy...")) - beam.obviousEffect = true; + beam.obvious_effect = true; mon->behaviour = BEH_SLEEP; mons_add_ench( mon, ENCH_SLEEP_WARY ); @@ -3738,7 +3808,7 @@ static int affect_monster_enchantment(struct bolt &beam, struct monsters *mon) { if (backlight_monsters(mon->x, mon->y, beam.hit, 0)) { - beam.obviousEffect = true; + beam.obvious_effect = true; return (MON_AFFECTED); } return (MON_UNAFFECTED); @@ -3771,11 +3841,11 @@ deathCheck: static int range_used_on_hit(struct bolt &beam) { // non-beams can only affect one thing (player/monster) - if (!beam.isBeam) + if (!beam.is_beam) return (BEAM_STOP); // CHECK ENCHANTMENTS - if (beam.beam_name[0] == '0') + if (beam.name[0] == '0') { switch(beam.flavour) { @@ -3806,11 +3876,11 @@ static int range_used_on_hit(struct bolt &beam) } // hellfire stops for nobody! - if (strcmp( beam.beam_name, "hellfire" ) == 0) + if (beam.name == "hellfire") return (0); // generic explosion - if (beam.flavour == BEAM_EXPLOSION) + if (beam.is_explosion || beam.is_big_cloud) return (BEAM_STOP); // plant spit @@ -3844,9 +3914,9 @@ static void explosion1(struct bolt &pbolt) // assume that the player can see/hear the explosion, or // gets burned by it anyway. :) - pbolt.msgGenerated = true; + pbolt.msg_generated = true; - if (stricmp(pbolt.beam_name, "hellfire") == 0) + if (pbolt.name == "hellfire") { seeMsg = "The hellfire explodes!"; hearMsg = "You hear a strangely unpleasant explosion."; @@ -3855,16 +3925,17 @@ static void explosion1(struct bolt &pbolt) pbolt.flavour = BEAM_HELLFIRE; } - if (stricmp(pbolt.beam_name, "golden flame") == 0) + if (pbolt.name == "golden flame") { seeMsg = "The flame explodes!"; - hearMsg = "You hear a strange explosion."; + hearMsg = "You feel a deep, resonant explosion."; pbolt.type = SYM_BURST; - pbolt.flavour = BEAM_HOLY; // same as golden flame? [dlb] + pbolt.flavour = BEAM_HOLY; + ex_size = 2; } - - if (stricmp(pbolt.beam_name, "fireball") == 0) + + if (pbolt.name == "fireball") { seeMsg = "The fireball explodes!"; hearMsg = "You hear an explosion."; @@ -3874,7 +3945,7 @@ static void explosion1(struct bolt &pbolt) ex_size = 1; } - if (stricmp(pbolt.beam_name, "orb of electricity") == 0) + if (pbolt.name == "orb of electricity") { seeMsg = "The orb of electricity explodes!"; hearMsg = "You hear a clap of thunder!"; @@ -3886,46 +3957,46 @@ static void explosion1(struct bolt &pbolt) ex_size = 2; } - if (stricmp(pbolt.beam_name, "orb of energy") == 0) + if (pbolt.name == "orb of energy") { seeMsg = "The orb of energy explodes."; hearMsg = "You hear an explosion."; } - if (stricmp(pbolt.beam_name, "metal orb") == 0) + if (pbolt.name == "metal orb") { seeMsg = "The orb explodes into a blast of deadly shrapnel!"; hearMsg = "You hear an explosion!"; - strcpy(pbolt.beam_name, "blast of shrapnel"); + pbolt.name = "blast of shrapnel"; pbolt.type = SYM_ZAP; pbolt.flavour = BEAM_FRAG; // sets it from pure damage to shrapnel (which is absorbed extra by armour) } - if (stricmp(pbolt.beam_name, "great blast of cold") == 0) + if (pbolt.name == "great blast of cold") { seeMsg = "The blast explodes into a great storm of ice!"; hearMsg = "You hear a raging storm!"; - strcpy(pbolt.beam_name, "ice storm"); + pbolt.name = "ice storm"; pbolt.damage.num = 6; pbolt.type = SYM_ZAP; pbolt.colour = WHITE; ex_size = 2 + (random2( pbolt.ench_power ) > 75); } - if (stricmp(pbolt.beam_name, "ball of vapour") == 0) + if (pbolt.name == "ball of vapour") { seeMsg = "The ball expands into a vile cloud!"; hearMsg = "You hear a gentle \'poof\'."; - strcpy(pbolt.beam_name, "stinking cloud"); + pbolt.name = "stinking cloud"; } - if (stricmp(pbolt.beam_name, "potion") == 0) + if (pbolt.name == "potion") { seeMsg = "The potion explodes!"; hearMsg = "You hear an explosion!"; - strcpy(pbolt.beam_name, "cloud"); + pbolt.name = "cloud"; } if (seeMsg == NULL) @@ -3935,7 +4006,7 @@ static void explosion1(struct bolt &pbolt) } - if (!pbolt.isTracer) + if (!pbolt.is_tracer) { // check for see/hear/no msg if (see_grid(x,y) || (x == you.x_pos && y == you.y_pos)) @@ -3943,9 +4014,9 @@ static void explosion1(struct bolt &pbolt) else { if (!(silenced(x,y) || silenced(you.x_pos, you.y_pos))) - mpr(hearMsg); + mpr(hearMsg, MSGCH_SOUND); else - pbolt.msgGenerated = false; + pbolt.msg_generated = false; } } @@ -3958,7 +4029,7 @@ static void explosion1(struct bolt &pbolt) // explosion is considered to emanate from beam->target_x, target_y // and has a radius equal to ex_size. The explosion will respect -// boundaries like walls, but go through/around statues/idols/etc. +// boundaries like walls, but go through/around statues/idols/etc. // for each cell affected by the explosion, affect() is called. @@ -3966,8 +4037,8 @@ void explosion( struct bolt &beam, bool hole_in_the_middle ) { int r = beam.ex_size; - // beam is now an explosion; set isExplosion. - beam.isExplosion = true; + // beam is now an explosion; set in_explosion_phase + beam.in_explosion_phase = true; #if DEBUG_DIAGNOSTICS snprintf( info, INFO_SIZE, @@ -4000,16 +4071,16 @@ void explosion( struct bolt &beam, bool hole_in_the_middle ) // as the recursion runs approximately as R^2 explosion_map(beam, 0, 0, 0, 0, r); - // go through affected cells, drawing effect and + // go through affected cells, drawing effect and // calling affect() and affect_items() for each. - // now, we get a bit fancy, drawing all radius 0 + // now, we get a bit fancy, drawing all radius 0 // effects, then radius 1, radius 2, etc. It looks // a bit better that way. // turn buffering off #ifdef WIN32CONSOLE bool oldValue = true; - if (!beam.isTracer) + if (!beam.is_tracer) oldValue = setBuffering(false); #endif @@ -4053,7 +4124,7 @@ void explosion( struct bolt &beam, bool hole_in_the_middle ) update_screen(); #endif // only delay on real explosion - if (!beam.isTracer && drawing) + if (!beam.is_tracer && drawing) delay(50); } @@ -4063,13 +4134,13 @@ void explosion( struct bolt &beam, bool hole_in_the_middle ) // ---------------- end boom -------------------------- #ifdef WIN32CONSOLE - if (!beam.isTracer) + if (!beam.is_tracer) setBuffering(oldValue); #endif // duplicate old behaviour - pause after entire explosion // has been drawn. - if (!beam.isTracer) + if (!beam.is_tracer) more(); } @@ -4095,7 +4166,7 @@ static void explosion_cell(struct bolt &beam, int x, int y, bool drawOnly) } // early out for tracer - if (beam.isTracer) + if (beam.is_tracer) return; // now affect items @@ -4136,7 +4207,7 @@ static void explosion_map( struct bolt &beam, int x, int y, return; // 3. check to see if we're blocked by something - // specifically, we're blocked by WALLS. Not + // specifically, we're blocked by WALLS. Not // statues, idols, etc. int dngn_feat = grd[beam.target_x + x][beam.target_y + y]; @@ -4170,8 +4241,8 @@ static void explosion_map( struct bolt &beam, int x, int y, // returns true if the beam is harmful (ignoring monster // resists) -- mon is given for 'special' cases where, -// for example, "Heal" might actually hurt undead, or -// "Holy Word" being ignored by holy monsters, etc. +// for example, "Heal" might actually hurt undead, or +// "Holy Word" being ignored by holy monsters, etc. // // only enchantments should need the actual monster type // to determine this; non-enchantments are pretty @@ -4179,18 +4250,18 @@ static void explosion_map( struct bolt &beam, int x, int y, bool nasty_beam(struct monsters *mon, struct bolt &beam) { // take care of non-enchantments - if (beam.beam_name[0] != '0') + if (beam.name[0] != '0') return (true); // now for some non-hurtful enchantments // degeneration / sleep if (beam.flavour == BEAM_DEGENERATE || beam.flavour == BEAM_SLEEP) - return (mons_holiness(mon->type) == MH_NATURAL); + return (mons_holiness(mon) == MH_NATURAL); // dispel undead / control undead if (beam.flavour == BEAM_DISPEL_UNDEAD || beam.flavour == BEAM_ENSLAVE_UNDEAD) - return (mons_holiness(mon->type) == MH_UNDEAD); + return (mons_holiness(mon) == MH_UNDEAD); // pain/agony if (beam.flavour == BEAM_PAIN) @@ -4198,7 +4269,7 @@ bool nasty_beam(struct monsters *mon, struct bolt &beam) // control demon if (beam.flavour == BEAM_ENSLAVE_DEMON) - return (mons_holiness(mon->type) == MH_DEMONIC); + return (mons_holiness(mon) == MH_DEMONIC); // haste if (beam.flavour == BEAM_HASTE) @@ -4211,3 +4282,38 @@ bool nasty_beam(struct monsters *mon, struct bolt &beam) // everything else is considered nasty by everyone return (true); } + +//////////////////////////////////////////////////////////////////////////// +// bolt + +// A constructor for bolt to help guarantee that we start clean (this has +// caused way too many bugs). Putting it here since there's no good place to +// put it, and it doesn't do anything other than initialize it's members. +// +// TODO: Eventually it'd be nice to have a proper factory for these things +// (extended from setup_mons_cast() and zapping() which act as limited ones). +bolt::bolt() : range(0), rangeMax(0), type(SYM_ZAP), colour(BLACK), + flavour(BEAM_MAGIC), source_x(0), source_y(0), damage(0,0), + ench_power(0), hit(0), target_x(0), target_y(0), + thrower(KILL_MISC), ex_size(0), beam_source(MHITNOT), name(), + is_beam(false), is_explosion(false), is_big_cloud(false), + is_enchant(false), is_energy(false), is_launched(false), + is_thrown(false), target_first(false), aimed_at_spot(false), + aux_source(), obvious_effect(false), fr_count(0), foe_count(0), + fr_power(0), foe_power(0), is_tracer(false), + aimed_at_feet(false), msg_generated(false), + in_explosion_phase(false), smart_monster(false), + can_see_invis(false), is_friendly(false), foe_ratio(0) +{ } + +void bolt::set_target(const dist &d) +{ + if (!d.isValid) + return; + + target_x = d.tx; + target_y = d.ty; + + if (d.isEndpoint) + aimed_at_spot = true; +} diff --git a/crawl-ref/source/beam.h b/crawl-ref/source/beam.h index d2935843ec..c529b727fe 100644 --- a/crawl-ref/source/beam.h +++ b/crawl-ref/source/beam.h @@ -3,6 +3,8 @@ * Summary: Functions related to ranged attacks. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -82,6 +84,7 @@ void place_cloud(unsigned char cl_type, unsigned char ctarget_x, unsigned char c * *********************************************************************** */ void fire_tracer( struct monsters *monster, struct bolt &pbolt ); +bool check_line_of_sight( int sx, int sy, int tx, int ty ); /* *********************************************************************** * called from: monstuff @@ -91,4 +94,6 @@ void mimic_alert( struct monsters *mimic ); void zapping( char ztype, int power, struct bolt &pbolt ); +int affect(struct bolt &beam, int x, int y); + #endif diff --git a/crawl-ref/source/chardump.cc b/crawl-ref/source/chardump.cc index 5fd7b18f57..4bb87f04e5 100644 --- a/crawl-ref/source/chardump.cc +++ b/crawl-ref/source/chardump.cc @@ -3,6 +3,8 @@ * Summary: Dumps character info out to the morgue file. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * @@ -15,27 +17,18 @@ #include "AppHdr.h" #include "chardump.h" +#include "clua.h" #include #include #include #include #include -#if !(defined(__IBMCPP__) || defined(__BCPLUSPLUS__)) +#if !defined(__IBMCPP__) #include #endif #include -#ifdef USE_EMX -#include -#endif - -#ifdef OS9 -#include -#else -#include -#endif - #ifdef DOS #include #endif @@ -45,9 +38,12 @@ #include "debug.h" #include "describe.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "macro.h" +#include "misc.h" #include "mutation.h" +#include "notes.h" #include "output.h" #include "player.h" #include "randart.h" @@ -62,25 +58,198 @@ #include "version.h" #include "view.h" -// Defined in view.cc -extern unsigned char (*mapch2) (unsigned char); +static bool dump_show_prices = false; +static bool dump_full_id = false; + +static void sdump_header(const std::string §ion, std::string &text); +static void sdump_stats(const std::string §ion, std::string &text); +static void sdump_location(const std::string §ion, std::string &text); +static void sdump_religion(const std::string §ion, std::string &text); +static void sdump_burden(const std::string §ion, std::string &text); +static void sdump_hunger(const std::string §ion, std::string &text); +static void sdump_transform(const std::string §ion, std::string &text); +static void sdump_misc(const std::string §ion, std::string &text); +static void sdump_notes(const std::string §ion, std::string &text); +static void sdump_inventory(const std::string §ion, std::string &text); +static void sdump_skills(const std::string §ion, std::string &text); +static void sdump_spells(const std::string §ion, std::string &text); +static void sdump_mutations(const std::string §ion, std::string &text); +static void sdump_messages(const std::string §ion, std::string &text); +static void sdump_screenshot(const std::string §ion, std::string &text); +static void sdump_kills(const std::string §ion, std::string &text); +static void sdump_newline(const std::string §ion, std::string &text); +static void sdump_separator(const std::string §ion, std::string &text); +#ifdef CLUA_BINDINGS +static void sdump_lua(const std::string §ion, std::string &text); +#endif +static bool write_dump(const std::string &fname, const std::string &text, + bool full_id); +static void dump_stats2( std::string & text, bool calc_unid); +static void dump_stats( std::string & text ); + +struct dump_section_handler +{ + const char *name; + void (*handler)(const std::string §ion, std::string &text); +}; + +static dump_section_handler dump_handlers[] = { + { "header", sdump_header }, + { "stats", sdump_stats }, + { "location", sdump_location }, + { "religion", sdump_religion }, + { "burden", sdump_burden }, + { "hunger", sdump_hunger }, + { "transform", sdump_transform }, + { "misc", sdump_misc }, + { "notes", sdump_notes }, + { "inventory", sdump_inventory }, + { "skills", sdump_skills }, + { "spells", sdump_spells }, + { "mutations", sdump_mutations }, + { "messages", sdump_messages }, + { "screenshot", sdump_screenshot }, + { "kills", sdump_kills }, + + // Conveniences for the .crawlrc artist. + { "", sdump_newline }, + { "-", sdump_separator }, + +#ifdef CLUA_BINDINGS + { NULL, sdump_lua } +#else + { NULL, NULL } +#endif +}; - // ======================================================================== - // Internal Functions - // ======================================================================== +static void dump_section(const std::string §ion, std::string &text) +{ + for (int i = 0; ; ++i) + { + if (!dump_handlers[i].name || section == dump_handlers[i].name) + { + if (dump_handlers[i].handler) + (*dump_handlers[i].handler)(section, text); + break; + } + } +} - // fillstring() is a hack to get around a missing constructor in - // Borland C++ implementation of the STD basic_string. Argh!!! -static std::string fillstring(size_t strlen, char filler) +bool dump_char(const std::string &fname, bool show_prices, bool full_id) { - std::string s; + // start with enough room for 100 80 character lines + std::string text; + text.reserve(100 * 80); + + dump_show_prices = show_prices; + dump_full_id = full_id; + + for (int i = 0, size = Options.dump_order.size(); i < size; ++i) + { + const std::string §ion = Options.dump_order[i]; + dump_section(section, text); + } + + return write_dump(fname, text, full_id); +} + +static void sdump_header(const std::string &, std::string &text) +{ + text += " " CRAWL " version " VERSION " character file.\n\n"; +} + +static void sdump_stats(const std::string &, std::string &text) +{ + if (Options.detailed_stat_dump) + dump_stats2(text, dump_full_id); + else + dump_stats(text); +} - for (size_t i=0; is", &luatext); + text += luatext; +} +#endif + //--------------------------------------------------------------- // // munge_description @@ -95,6 +264,7 @@ static std::string fillstring(size_t strlen, char filler) std::string munge_description(const std::string & inStr) { std::string outStr; + std::string eol = "\n"; outStr.reserve(inStr.length() + 32); @@ -103,7 +273,7 @@ std::string munge_description(const std::string & inStr) long i = 0; - outStr += fillstring(kIndent, ' '); + outStr += std::string(kIndent, ' '); while (i < (long) inStr.length()) { @@ -111,9 +281,9 @@ std::string munge_description(const std::string & inStr) if (ch == '$') { - outStr += EOL; + outStr += eol; - outStr += fillstring(kIndent, ' '); + outStr += std::string(kIndent, ' '); lineLen = kIndent; while (inStr[++i] == '$') @@ -123,8 +293,8 @@ std::string munge_description(const std::string & inStr) { if (lineLen >= 79) { - outStr += EOL; - outStr += fillstring(kIndent, ' '); + outStr += eol; + outStr += std::string(kIndent, ' '); lineLen = kIndent; } @@ -148,8 +318,8 @@ std::string munge_description(const std::string & inStr) if (lineLen + word.length() >= 79) { - outStr += EOL; - outStr += fillstring(kIndent, ' '); + outStr += eol; + outStr += std::string(kIndent, ' '); lineLen = kIndent; } @@ -158,148 +328,27 @@ std::string munge_description(const std::string & inStr) } } - outStr += EOL; + outStr += eol; return (outStr); } // end munge_description() - //--------------------------------------------------------------- - // - // dump_screenshot - // - // Grabs a screenshot and appends the text into the given std::string, - // using several ugly hacks in the process. - //--------------------------------------------------------------- -static void dump_screenshot( std::string &text ) +static void sdump_messages(const std::string &, std::string &text) { // A little message history: if (Options.dump_message_count > 0) { - text += " Last Messages" EOL EOL; + text += "Message History\n\n"; text += get_last_messages(Options.dump_message_count); } +} - FixedVector < char, 1500 > buffy; //[800]; //392]; - int bufcount = 0; - unsigned short ch, color; - int count_x, count_y; - - // Urg, ugly screen capture. CVS Crawl may have a better way of doing this, - // but until the next release... - for (count_y = (you.y_pos - 8); (count_y < you.y_pos + 9); count_y++) - { - bufcount += 8; - for (count_x = (you.x_pos - 8); (count_x < you.x_pos + 9); count_x++) - { - if (count_x == you.x_pos && count_y == you.y_pos) - { - extern unsigned char your_sign; - ch = your_sign; - } - else - { - unsigned int object = env.show[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9]; - get_non_ibm_symbol(object, &ch, &color); - } - - buffy[bufcount++] = (char) ch; - } - bufcount += 8; - } - - int maxbuf = bufcount; - bufcount = 0; - - for (count_y = 0; count_y < 17; count_y++) - { - for (count_x = 0; count_x < 33; count_x++) - { - if (count_x + you.x_pos - 17 < 3 - || count_y + you.y_pos - 9 < 3 - || count_x + you.x_pos - 14 > (GXM - 3) - || count_y + you.y_pos - 9 > (GYM - 3)) - { - buffy[bufcount++] = ' '; - continue; - } - - if (count_x >= 8 && count_x <= 24 && count_y >= 0 - && count_y <= 16 && buffy[bufcount] != 0) - { - bufcount++; - continue; - } - - unsigned char envc = (unsigned char) - env.map[count_x + you.x_pos - 17] - [count_y + you.y_pos - 9]; - if (envc) - { - // If it's printable, use it directly. - if (envc < 127 && envc >= 32) - ch = envc; - else - { - // Otherwise get what's on the grid and get an ASCII - // character for that. - unsigned int object = grd[count_x + you.x_pos - 16] - [count_y + you.y_pos - 8]; - - // Special case secret doors so that monsters that open - // doors out of hero's LOS don't reveal the secret door in - // the dump - if (envc == mapch2(DNGN_SECRET_DOOR)) - object = DNGN_SECRET_DOOR; - - get_non_ibm_symbol(object, &ch, &color); - } - - buffy[bufcount++] = (char) ch; - } - else - { - buffy[bufcount++] = ' '; - } - } - } - - if (bufcount > maxbuf) maxbuf = bufcount; - - while (maxbuf > 0 && (!buffy[maxbuf - 1] || buffy[maxbuf - 1] == ' ')) - --maxbuf; - - // 33 columns and a null terminator. More hardcoding. :-( - char buf[34]; - char *s = buf; - bool leadblanks = true; - for (int i = 0; i < maxbuf; ) - { - *s++ = buffy[i]? buffy[i] : ' '; - - ++i; - if (!(i % 33) || i >= maxbuf) - { - *s = 0; - while (s > buf && *--s == ' ') - *s = 0; - - if (s == buf && !*s && leadblanks) - continue; - - leadblanks = false; - text += buf; - text += EOL; - s = buf; - } - } +static void sdump_screenshot(const std::string &, std::string &text) +{ + text += screenshot(); + text += "\n\n"; } - //--------------------------------------------------------------- - // - // dump_stats - // - //--------------------------------------------------------------- static void dump_stats( std::string & text ) { char st_prn[20]; @@ -311,7 +360,7 @@ static void dump_stats( std::string & text ) text += " ("; text += species_name(you.species, you.experience_level); text += ")"; - text += EOL; + text += "\n"; text += "(Level "; itoa(you.experience_level, st_prn, 10); @@ -319,7 +368,7 @@ static void dump_stats( std::string & text ) text += " "; text += you.class_name; text += ")"; - text += EOL EOL; + text += "\n\n"; if (you.real_time != -1) { @@ -334,7 +383,7 @@ static void dump_stats( std::string & text ) text += " Number of turns: "; itoa( you.num_turns, st_prn, 10 ); text += st_prn; - text += EOL EOL; + text += "\n\n"; } text += "Experience : "; @@ -343,7 +392,7 @@ static void dump_stats( std::string & text ) text += "/"; itoa(you.experience, st_prn, 10); text += st_prn; - text += EOL; + text += "\n\n"; text += "Strength "; itoa(you.strength, st_prn, 10); @@ -374,7 +423,7 @@ static void dump_stats( std::string & text ) itoa(you.max_intel, st_prn, 10); text += st_prn; } - text += EOL; + text += "\n"; text += "Hit Points : "; itoa(you.hp, st_prn, 10); @@ -412,7 +461,7 @@ static void dump_stats( std::string & text ) itoa(you.max_magic_points, st_prn, 10); text += st_prn; } - text += EOL; + text += "\n"; text += "AC : "; itoa(player_AC(), st_prn, 10); @@ -425,13 +474,13 @@ static void dump_stats( std::string & text ) text += " Shield : "; itoa(player_shield_class(), st_prn, 10); text += st_prn; - text += EOL; + text += "\n"; text += "GP : "; itoa( you.gold, st_prn, 10 ); text += st_prn; - text += EOL; - text += EOL; + text += "\n"; + text += "\n"; } // end dump_stats() //--------------------------------------------------------------- @@ -441,38 +490,52 @@ static void dump_stats( std::string & text ) //--------------------------------------------------------------- static void dump_stats2( std::string & text, bool calc_unid) { - char buffer[25*3][45]; + char buffer[24*3][45]; char str_pass[80]; char* ptr_n; get_full_detail(&buffer[0][0], calc_unid); - for (int i = 0; i < 25; i++) + for (int i = 0; i < 24; i++) { ptr_n = &buffer[i][0]; - if (buffer[i+25][0] == '\0' && buffer[i+50][0] == '\0') + if (buffer[i+24][0] == 0 && buffer[i+24*2][0] == 0) snprintf(&str_pass[0], 45, "%s", ptr_n); else snprintf(&str_pass[0], 45, "%-32s", ptr_n); text += str_pass; - ptr_n = &buffer[i+25][0]; - if (buffer[i+50][0] == '\0') + ptr_n = &buffer[i+24][0]; + if (buffer[i+24*2][0] == 0) snprintf(&str_pass[0], 45, "%s", ptr_n); else snprintf(&str_pass[0], 45, "%-20s", ptr_n); text += str_pass; - ptr_n = &buffer[i+50][0]; - if (buffer[i+50][0] != '\0') + ptr_n = &buffer[i+24*2][0]; + if (buffer[i+24*2][0] != 0) { snprintf(&str_pass[0], 45, "%s", ptr_n); text += str_pass; } - text += EOL; + text += "\n"; } - text += EOL EOL; + text += "\n" "\n"; +} + +static void sdump_notes(const std::string &, std::string& text) +{ + if ( note_list.size() == 0 || Options.use_notes == false ) + return; + + text += "\nNotes\n| Turn |Location | Note\n"; + text += "--------------------------------------------------------------\n"; + for ( unsigned i = 0; i < note_list.size(); ++i ) { + text += describe_note(note_list[i]); + text += "\n"; + } + text += "\n"; } //--------------------------------------------------------------- @@ -480,88 +543,27 @@ static void dump_stats2( std::string & text, bool calc_unid) // dump_location // //--------------------------------------------------------------- -static void dump_location( std::string & text ) +static void sdump_location(const std::string &, std::string & text) { - if (you.level_type != LEVEL_DUNGEON || you.your_level != -1) - text += "You are "; - - if (you.level_type == LEVEL_PANDEMONIUM) - text += "in Pandemonium"; - else if (you.level_type == LEVEL_ABYSS) - text += "in the Abyss"; - else if (you.level_type == LEVEL_LABYRINTH) - text += "in a labyrinth"; - else if (you.where_are_you == BRANCH_DIS) - text += "in Dis"; - else if (you.where_are_you == BRANCH_GEHENNA) - text += "in Gehenna"; - else if (you.where_are_you == BRANCH_VESTIBULE_OF_HELL) - text += "in the Vestibule of Hell"; - else if (you.where_are_you == BRANCH_COCYTUS) - text += "in Cocytus"; - else if (you.where_are_you == BRANCH_TARTARUS) - text += "in Tartarus"; - else if (you.where_are_you == BRANCH_INFERNO) - text += "in the Inferno"; - else if (you.where_are_you == BRANCH_THE_PIT) - text += "in the Pit"; - else if (you.where_are_you == BRANCH_ORCISH_MINES) - text += "in the Mines"; - else if (you.where_are_you == BRANCH_HIVE) - text += "in the Hive"; - else if (you.where_are_you == BRANCH_LAIR) - text += "in the Lair"; - else if (you.where_are_you == BRANCH_SLIME_PITS) - text += "in the Slime Pits"; - else if (you.where_are_you == BRANCH_VAULTS) - text += "in the Vaults"; - else if (you.where_are_you == BRANCH_CRYPT) - text += "in the Crypt"; - else if (you.where_are_you == BRANCH_HALL_OF_BLADES) - text += "in the Hall of Blades"; - else if (you.where_are_you == BRANCH_HALL_OF_ZOT) - text += "in the Hall of Zot"; - else if (you.where_are_you == BRANCH_ECUMENICAL_TEMPLE) - text += "in the Ecumenical Temple"; - else if (you.where_are_you == BRANCH_SNAKE_PIT) - text += "in the Snake Pit"; - else if (you.where_are_you == BRANCH_ELVEN_HALLS) - text += "in the Elven Halls"; - else if (you.where_are_you == BRANCH_TOMB) - text += "in the Tomb"; - else if (you.where_are_you == BRANCH_SWAMP) - text += "in the Swamp"; + if (you.your_level == -1 + && you.where_are_you == BRANCH_MAIN_DUNGEON + && you.level_type == LEVEL_DUNGEON) + text += "You escaped"; else - { - if (you.your_level == -1) - text += "You escaped"; - else - { - text += "on level "; - - char st_prn[20]; - itoa(you.your_level + 1, st_prn, 10); - text += st_prn; - } - } + text += "You are " + prep_branch_level_name(); text += "."; - text += EOL; + text += "\n"; } // end dump_location() - //--------------------------------------------------------------- - // - // dump_religion - // - //--------------------------------------------------------------- -static void dump_religion( std::string & text ) +static void sdump_religion(const std::string &, std::string & text) { if (you.religion != GOD_NO_GOD) { text += "You worship "; text += god_name(you.religion); text += "."; - text += EOL; + text += "\n"; if (!player_under_penance()) { @@ -577,17 +579,17 @@ static void dump_religion( std::string & text ) (you.piety <= 130) ? "extremely pleased with you" : "exalted by your worship"); text += "."; - text += EOL; + text += "\n"; } } else { text += god_name(you.religion); text += " is demanding penance."; - text += EOL; + text += "\n"; } } -} // end dump_religion() +} extern char id[4][50]; // itemname.cc static bool dump_item_origin(const item_def &item, int value) @@ -645,7 +647,7 @@ static bool dump_item_origin(const item_def &item, int value) // dump_inventory // //--------------------------------------------------------------- -static void dump_inventory( std::string & text, bool show_prices ) +static void sdump_inventory(const std::string &, std::string & text) { int i, j; char temp_id[4][50]; @@ -683,12 +685,12 @@ static void dump_inventory( std::string & text, bool show_prices ) if (!inv_count) { text += "You aren't carrying anything."; - text += EOL; + text += "\n"; } else { text += " Inventory:"; - text += EOL; + text += "\n"; for (i = 0; i < OBJ_GOLD; i++) { @@ -713,7 +715,7 @@ static void dump_inventory( std::string & text, bool show_prices ) default: DEBUGSTR("Bad item class"); } - text += EOL; + text += "\n"; for (j = 0; j < ENDOFPACK; j++) { @@ -727,7 +729,7 @@ static void dump_inventory( std::string & text, bool show_prices ) inv_count--; int ival = -1; - if (show_prices) + if (dump_show_prices) { text += " ("; @@ -742,7 +744,7 @@ static void dump_inventory( std::string & text, bool show_prices ) if (origin_describable(you.inv[j]) && dump_item_origin(you.inv[j], ival)) { - text += EOL " (" + origin_desc(you.inv[j]) + ")"; + text += "\n" " (" + origin_desc(you.inv[j]) + ")"; } if (is_dumpable_artifact( you.inv[j], @@ -756,13 +758,14 @@ static void dump_inventory( std::string & text, bool show_prices ) } else { - text += EOL; + text += "\n"; } } } } } } + text += "\n\n"; } // end dump_inventory() //--------------------------------------------------------------- @@ -770,14 +773,19 @@ static void dump_inventory( std::string & text, bool show_prices ) // dump_skills // //--------------------------------------------------------------- -static void dump_skills( std::string & text ) +static void sdump_skills(const std::string &, std::string & text) { char tmp_quant[20]; - text += EOL; - text += EOL; + text += " You have "; + itoa( you.exp_available, tmp_quant, 10 ); + text += tmp_quant; + text += " experience left."; + + text += "\n"; + text += "\n"; text += " Skills:"; - text += EOL; + text += "\n"; for (unsigned char i = 0; i < 50; i++) { @@ -792,12 +800,12 @@ static void dump_skills( std::string & text ) text += tmp_quant; text += " "; text += skill_name(i); - text += EOL; + text += "\n"; } } - text += EOL; - text += EOL; + text += "\n"; + text += "\n"; } // end dump_skills() //--------------------------------------------------------------- @@ -805,24 +813,24 @@ static void dump_skills( std::string & text ) // Return string of the i-th spell type, with slash if required // //--------------------------------------------------------------- -static std::string spell_type_name(int spell_class, bool slash) +static std::string spell_type_shortname(int spell_class, bool slash) { std::string ret; if (slash) ret = "/"; - ret += spelltype_name(spell_class); + ret += spelltype_short_name(spell_class); return (ret); -} // end spell_type_name() +} // end spell_type_shortname() //--------------------------------------------------------------- // // dump_spells // //--------------------------------------------------------------- -static void dump_spells( std::string & text ) +static void sdump_spells(const std::string &, std::string & text) { char tmp_quant[20]; @@ -860,20 +868,20 @@ static void dump_spells( std::string & text ) text += " spell levels left."; } - text += EOL; + text += "\n"; if (!you.spell_no) { text += "You don't know any spells."; - text += EOL; + text += "\n"; } else { - text += "You know the following spells:" EOL; - text += EOL; + text += "You know the following spells:" "\n"; + text += "\n"; - text += " Your Spells Type Success Level" EOL; + text += " Your Spells Type Power Success Level" "\n"; for (int j = 0; j < 52; j++) { @@ -882,20 +890,17 @@ static void dump_spells( std::string & text ) if (spell != SPELL_NO_SPELL) { - std::string spell_line = " "; + std::string spell_line; - char strng[2]; - strng[0] = letter; - strng[1] = '\0'; - - spell_line += strng; + spell_line += letter; spell_line += " - "; spell_line += spell_title( spell ); - for (int i = spell_line.length(); i < 34; i++) - { - spell_line += ' '; - } + if ( spell_line.length() > 24 ) + spell_line = spell_line.substr(0, 24); + + for (int i = spell_line.length(); i < 26; i++) + spell_line += ' '; bool already = false; @@ -903,18 +908,30 @@ static void dump_spells( std::string & text ) { if (spell_typematch( spell, spell_type_index[i] )) { - spell_line += - spell_type_name(spell_type_index[i], already); + spell_line += spell_type_shortname(spell_type_index[i], + already); already = true; } } - if (spell_line.length() > 57) - spell_line = spell_line.substr(0, 57); - for (int i = spell_line.length(); i < 58; i++) - { - spell_line += ' '; - } + for (int i = spell_line.length(); i < 41; ++i ) + spell_line += ' '; + + int spell_p = calc_spell_power( spell, true ); + spell_line += ( (spell_p > 100) ? "Enormous" : + (spell_p > 90) ? "Huge" : + (spell_p > 80) ? "Massive" : + (spell_p > 70) ? "Major" : + (spell_p > 60) ? "Impressive" : + (spell_p > 50) ? "Reasonable" : + (spell_p > 40) ? "Moderate" : + (spell_p > 30) ? "Adequate" : + (spell_p > 20) ? "Mediocre" : + (spell_p > 10) ? "Minor" + : "Negligible"); + + for (int i = spell_line.length(); i < 56; ++i ) + spell_line += ' '; int fail_rate = spell_fail( spell ); @@ -931,12 +948,12 @@ static void dump_spells( std::string & text ) (fail_rate > 0) ? "Excellent" : "Perfect"; - for (int i = spell_line.length(); i < 70; i++) + for (int i = spell_line.length(); i < 68; i++) spell_line += ' '; itoa((int) spell_difficulty( spell ), tmp_quant, 10 ); spell_line += tmp_quant; - spell_line += EOL; + spell_line += "\n"; text += spell_line; } @@ -945,22 +962,12 @@ static void dump_spells( std::string & text ) } // end dump_spells() -//--------------------------------------------------------------- -// -// dump_kills -// -//--------------------------------------------------------------- -static void dump_kills( std::string & text ) +static void sdump_kills(const std::string &, std::string & text) { text += you.kills.kill_info(); } -//--------------------------------------------------------------- -// -// dump_mutations -// -//--------------------------------------------------------------- -static void dump_mutations( std::string & text ) +static void sdump_mutations(const std::string &, std::string & text) { // Can't use how_mutated() here, as it doesn't count demonic powers int xz = 0; @@ -974,9 +981,9 @@ static void dump_mutations( std::string & text ) if (xz > 0) { text += ""; - text += EOL; + text += "\n"; text += " Mutations & Other Weirdness"; - text += EOL; + text += "\n"; for (int j = 0; j < 100; j++) { @@ -986,16 +993,13 @@ static void dump_mutations( std::string & text ) text += "* "; text += mutation_name(j); - text += EOL; + text += "\n"; } } } + text += "\n\n"; } // end dump_mutations() -#if MAC -#pragma mark - -#endif - // ======================================================================== // Public Functions // ======================================================================== @@ -1008,168 +1012,36 @@ const char *hunger_level(void) (you.hunger < 11000) ? "full" : "completely stuffed"); } -//--------------------------------------------------------------- -// -// dump_char -// -// Creates a disk record of a character. Returns true if the -// character was successfully saved. -// -//--------------------------------------------------------------- -bool dump_char( const char fname[30], bool show_prices ) // $$$ a try block? +static bool write_dump( + const std::string &fname, + const std::string &text, + bool full_id) { bool succeeded = false; - std::string text; - - // start with enough room for 100 80 character lines - text.reserve(100 * 80); - - text += " Dungeon Crawl version " VERSION " character file."; - text += EOL; - text += EOL; - - if (Options.detailed_stat_dump) - dump_stats2(text, show_prices); - else - dump_stats(text); - - dump_location(text); - dump_religion(text); - - switch (you.burden_state) - { - case BS_OVERLOADED: - text += "You are overloaded with stuff."; - text += EOL; - break; - case BS_ENCUMBERED: - text += "You are encumbered."; - text += EOL; - break; - } - - text += "You are "; - - text += hunger_level(); - - text += "."; - text += EOL; - text += EOL; - - if (you.attribute[ATTR_TRANSFORMATION]) - { - switch (you.attribute[ATTR_TRANSFORMATION]) - { - case TRAN_SPIDER: - text += "You are in spider-form."; - break; - case TRAN_BLADE_HANDS: - text += "Your hands are blades."; - break; - case TRAN_STATUE: - text += "You are a stone statue."; - break; - case TRAN_ICE_BEAST: - text += "You are a creature of crystalline ice."; - break; - case TRAN_DRAGON: - text += "You are a fearsome dragon!"; - break; - case TRAN_LICH: - text += "You are in lich-form."; - break; - case TRAN_SERPENT_OF_HELL: - text += "You are a huge, demonic serpent!"; - break; - case TRAN_AIR: - text += "You are a cloud of diffuse gas."; - break; - } - - text += EOL; - text += EOL; - } - - dump_inventory(text, show_prices); - - char tmp_quant[20]; - - text += EOL; - text += EOL; - text += " You have "; - itoa( you.exp_available, tmp_quant, 10 ); - text += tmp_quant; - text += " experience left."; - - dump_skills(text); - dump_spells(text); - dump_mutations(text); - - text += EOL; - text += EOL; - - dump_screenshot(text); - text += EOL EOL; - - dump_kills(text); - - char file_name[kPathLen] = "\0"; - + std::string file_name; if (SysEnv.crawl_dir) - strncpy(file_name, SysEnv.crawl_dir, kPathLen); + file_name += SysEnv.crawl_dir; - strncat(file_name, fname, kPathLen); + file_name += fname; #ifdef STASH_TRACKING - char stash_file_name[kPathLen] = ""; - strncpy(stash_file_name, file_name, kPathLen); -#endif - if (strcmp(fname, "morgue.txt") != 0) - { - strncat(file_name, ".txt", kPathLen); -#ifdef STASH_TRACKING - strncat(stash_file_name, ".lst", kPathLen); - stashes.dump(stash_file_name); -#endif - } -#ifdef STASH_TRACKING - else - { - // Grr. Filename is morgue.txt, it needs to be morgue.lst - int len = strlen(stash_file_name); - stash_file_name[len - 3] = 'l'; - stash_file_name[len - 2] = 's'; - // Fully identified stash dump. - stashes.dump(stash_file_name, true); - } + std::string stash_file_name; + stash_file_name = file_name; + stash_file_name += ".lst"; + stashes.dump(stash_file_name.c_str(), full_id); #endif - FILE *handle = fopen(file_name, "wb"); + file_name += ".txt"; + FILE *handle = fopen(file_name.c_str(), "w"); #if DEBUG_DIAGNOSTICS - strcpy( info, "File name: " ); - strcat( info, file_name ); - mpr( info, MSGCH_DIAGNOSTICS ); + mprf(MSGCH_DIAGNOSTICS, "File name: %s", file_name.c_str()); #endif if (handle != NULL) { - size_t begin = 0; - size_t end = text.find(EOL); - - while (end != std::string::npos) - { - end += strlen(EOL); - - size_t len = end - begin; - - fwrite(text.c_str() + begin, len, 1, handle); - - begin = end; - end = text.find(EOL, end); - } - + fputs(text.c_str(), handle); fclose(handle); succeeded = true; } @@ -1178,3 +1050,73 @@ bool dump_char( const char fname[30], bool show_prices ) // $$$ a try block? return (succeeded); } // end dump_char() + +static void set_resist_dump_color( const char* data ) { + while ( *data && *data != ':' ) + ++data; + if ( *data == 0 ) + return; + ++data; + int pluscount = 0; + while ( *data ) { + if ( *data == '+' ) + ++pluscount; + if ( *data == 'x' ) + --pluscount; + ++data; + } + switch ( pluscount ) { + case 3: + case 2: + textcolor(LIGHTGREEN); + break; + case 1: + textcolor(GREEN); + break; + case -1: + textcolor(RED); + break; + case -2: + case -3: + textcolor(LIGHTRED); + break; + default: + textcolor(LIGHTGREY); + break; + } +} + +void resists_screen() { +#ifdef DOS_TERM + char dosbuffer[4000]; + gettext( 1, 1, 80, 25, dosbuffer ); + window( 1, 1, 80, 25 ); +#endif + clrscr(); + textcolor(LIGHTGREY); + char buffer[24*3][45]; + + get_full_detail(&buffer[0][0], false); + + for (int line = 0; line < 24; ++line ) { + for ( int block = 0; block < 3; ++block ) { + const int idx = block * 24 + line; + if ( buffer[idx][0] ) { + gotoxy( block == 2 ? 53 : block * 32 + 1, line+1 ); + /* FIXME - hack - magic number 14 */ + if ( block != 0 && line < 14 ) + set_resist_dump_color(buffer[idx]); + cprintf("%s", buffer[idx] ); + textcolor(LIGHTGREY); + } + } + } + + getch(); +#ifdef DOS_TERM + puttext(1, 1, 80, 25, dosbuffer); + window(1, 1, 80, 25); +#endif + + redraw_screen(); +} diff --git a/crawl-ref/source/chardump.h b/crawl-ref/source/chardump.h index 22b03ac8d3..1603ae9f75 100644 --- a/crawl-ref/source/chardump.h +++ b/crawl-ref/source/chardump.h @@ -3,6 +3,8 @@ * Summary: Dumps character info out to the morgue file. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> 4/20/99 JDJ Reformatted, uses string objects, split out @@ -15,11 +17,11 @@ #include -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: acr - ouch - * *********************************************************************** */ -bool dump_char( const char fname[30], bool show_prices ); +bool dump_char(const std::string &fname, + bool show_prices, + bool full_id = false); + +void resists_screen(); std::string munge_description(const std::string &inStr); diff --git a/crawl-ref/source/cloud.cc b/crawl-ref/source/cloud.cc index 808c4cce02..7217240c57 100644 --- a/crawl-ref/source/cloud.cc +++ b/crawl-ref/source/cloud.cc @@ -3,6 +3,8 @@ * Summary: Functions related to clouds. * Written by: Brent Ross * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Creating a cloud module so all the cloud stuff can be isolated. * * Change History (most recent first): diff --git a/crawl-ref/source/clua.cc b/crawl-ref/source/clua.cc index 21f2566c15..9885fc05d3 100644 --- a/crawl-ref/source/clua.cc +++ b/crawl-ref/source/clua.cc @@ -1,3 +1,7 @@ +/* + * Modified for Crawl Reference by $Author$ on $Date$ + */ + #include "AppHdr.h" #ifdef CLUA_BINDINGS @@ -7,10 +11,12 @@ #include "abl-show.h" #include "command.h" #include "chardump.h" +#include "delay.h" #include "food.h" #include "invent.h" #include "initfile.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "item_use.h" #include "libutil.h" @@ -23,7 +29,6 @@ #include "skills2.h" #include "spl-util.h" #include "stuff.h" -#include "wpn-misc.h" #include @@ -297,7 +302,7 @@ void CLua::vfnreturns(const char *format, va_list args) } static void push_monster(lua_State *ls, monsters *mons); -static int push_activity_interrupt(lua_State *ls, activity_interrupt_t *t); +static int push_activity_interrupt(lua_State *ls, activity_interrupt_data *t); int CLua::push_args(lua_State *ls, const char *format, va_list args, va_list *targ) { @@ -346,7 +351,7 @@ int CLua::push_args(lua_State *ls, const char *format, va_list args, break; case 'A': argc += push_activity_interrupt( - ls, va_arg(args, activity_interrupt_t *)); + ls, va_arg(args, activity_interrupt_data *)); break; default: --argc; @@ -376,7 +381,7 @@ int CLua::return_count(lua_State *ls, const char *format) // blowing the stack. if (ci < 0) ci = 0; - else if (ci > 5) + else if (ci > 10) ci = 10; return (ci); } @@ -1239,22 +1244,25 @@ static int l_item_equip_type(lua_State *ls) if (!item || !is_valid_item(*item)) return (0); - int eq = -1; + equipment_type eq = EQ_NONE; + if (item->base_type == OBJ_WEAPONS || item->base_type == OBJ_STAVES) eq = EQ_WEAPON; else if (item->base_type == OBJ_ARMOUR) - eq = armour_equip_slot(*item); + eq = get_armour_slot(*item); else if (item->base_type == OBJ_JEWELLERY) eq = item->sub_type >= AMU_RAGE? EQ_AMULET : EQ_RINGS; - if (eq != -1) + if (eq != EQ_NONE) + { lua_pushnumber(ls, eq); - else - lua_pushnil(ls); - if (eq != -1) lua_pushstring(ls, equip_slot_to_name(eq)); + } else + { + lua_pushnil(ls); lua_pushnil(ls); + } return (2); } @@ -1264,12 +1272,15 @@ static int l_item_weap_skill(lua_State *ls) if (!item || !is_valid_item(*item)) return (0); - int skill = weapon_skill( item->base_type, item->sub_type ); + int skill = range_skill(*item); + if (skill == SK_RANGED_COMBAT) + skill = weapon_skill(*item); if (skill == SK_FIGHTING) return (0); lua_pushstring(ls, skill_name(skill)); - return (1); + lua_pushnumber(ls, skill); + return (2); } static int l_item_artifact(lua_State *ls) @@ -1407,7 +1418,7 @@ static bool eat_item(const item_def &item) { eat_from_inventory(item.link); burden_change(); - you.turn_is_over = 1; + you.turn_is_over = true; return (true); } @@ -1444,42 +1455,6 @@ static int food_eat(lua_State *ls) return (1); } -// Giving away chunk type information is spoily. -/* -static int food_chunktype(lua_State *ls) -{ - LUA_ITEM(item, 1); - if (item && item->base_type == OBJ_FOOD && item->sub_type == FOOD_CHUNK) - { - int mons_type = item->plus; - int chunk_type = mons_corpse_thingy(mons_type); - const char *schunktype = "unknown"; - switch (chunk_type) - { - case CE_HCL: - case CE_MUTAGEN_GOOD: - case CE_MUTAGEN_BAD: - case CE_MUTAGEN_RANDOM: - schunktype = "mutagenic"; - break; - case CE_POISONOUS: - schunktype = "poisonous"; - break; - case CE_CONTAMINATED: - schunktype = "contaminated"; - break; - case CE_CLEAN: - schunktype = "clean"; - break; - } - lua_pushstring(ls, schunktype); - } - else - lua_pushnil(ls); - return (1); -} -*/ - static int food_rotting(lua_State *ls) { LUA_ITEM(item, 1); @@ -1557,7 +1532,7 @@ static int crawl_c_input_line(lua_State *ls) { char linebuf[500]; - bool valid = cancelable_get_line(linebuf, sizeof linebuf); + bool valid = !cancelable_get_line(linebuf, sizeof linebuf); if (valid) lua_pushstring(ls, linebuf); else @@ -1903,9 +1878,15 @@ static option_handler handlers[] = { "show_uncursed", &Options.show_uncursed, option_hboolean }, { "always_greet", &Options.always_greet, option_hboolean }, { "easy_open", &Options.easy_open, option_hboolean }, - { "easy_armour", &Options.easy_armour, option_hboolean }, + { "easy_armour", &Options.easy_unequip, option_hboolean }, + { "easy_unequip", &Options.easy_unequip, option_hboolean }, { "easy_butcher", &Options.easy_butcher, option_hboolean }, { "terse_hand", &Options.terse_hand, option_hboolean }, + { "increasing_skill_progress", &Options.increasing_skill_progress, option_hboolean }, + { "confirm_self_target", &Options.confirm_self_target, option_hboolean }, + { "safe_autopickup", &Options.safe_autopickup, option_hboolean }, + { "note_skill_max", &Options.note_skill_max, option_hboolean }, + { "use_notes", &Options.use_notes, option_hboolean }, { "delay_message_clear", &Options.delay_message_clear, option_hboolean }, { "no_dark_brand", &Options.no_dark_brand, option_hboolean }, { "auto_list", &Options.auto_list, option_hboolean }, @@ -1913,9 +1894,11 @@ static option_handler handlers[] = option_hboolean }, { "pickup_thrown", &Options.pickup_thrown, option_hboolean }, { "pickup_dropped", &Options.pickup_dropped, option_hboolean }, + { "sort_menus", &Options.sort_menus, option_hboolean }, { "show_waypoints", &Options.show_waypoints, option_hboolean }, { "item_colour", &Options.item_colour, option_hboolean }, { "target_zero_exp", &Options.target_zero_exp, option_hboolean }, + { "safe_zero_exp", &Options.safe_zero_exp, option_hboolean }, { "target_wrap", &Options.target_wrap, option_hboolean }, { "easy_exit_menu", &Options.easy_exit_menu, option_hboolean }, { "dos_use_background_intensity", &Options.dos_use_background_intensity, @@ -2066,7 +2049,7 @@ static int monster_set(lua_State *ls) return (0); } -static int push_activity_interrupt(lua_State *ls, activity_interrupt_t *t) +static int push_activity_interrupt(lua_State *ls, activity_interrupt_data *t) { if (!t->data) { diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc index 25644bdb0b..437192ad34 100644 --- a/crawl-ref/source/command.cc +++ b/crawl-ref/source/command.cc @@ -3,6 +3,8 @@ * Summary: Misc commands. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <4> 10/12/99 BCR BUILD_DATE is now used in version() @@ -25,17 +27,23 @@ #include "itemname.h" #include "item_use.h" #include "items.h" +#include "libutil.h" #include "menu.h" #include "ouch.h" #include "spl-cast.h" #include "spl-util.h" #include "stuff.h" #include "version.h" -#include "wpn-misc.h" +#include "view.h" static void adjust_item(void); static void adjust_spells(void); static void adjust_ability(void); +static void list_wizard_commands(); +#ifdef OBSOLETE_COMMAND_HELP +static const char *command_string( int i ); +#endif +static const char *wizard_string( int i ); void quit_game(void) { @@ -64,7 +72,7 @@ static const char *features[] = { void version(void) { - mpr( "This is Dungeon Crawl " VERSION " (Last build " BUILD_DATE ")." ); + mpr( "This is " CRAWL " " VERSION " (" BUILD_DATE ")." ); std::string feats = "Features: "; for (int i = 1, size = sizeof features / sizeof *features; i < size; ++i) @@ -140,7 +148,7 @@ static void adjust_item(void) return; } - from_slot = prompt_invent_item( "Adjust which item?", -1 ); + from_slot = prompt_invent_item( "Adjust which item?", MT_INVSELECT, -1 ); if (from_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -150,7 +158,12 @@ static void adjust_item(void) in_name( from_slot, DESC_INVENTORY_EQUIP, str_pass ); mpr( str_pass ); - to_slot = prompt_invent_item( "Adjust to which letter?", -1, false, false ); + to_slot = prompt_invent_item( + "Adjust to which letter?", + MT_INVSELECT, + -1, + false, + false ); if (to_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -551,3 +564,492 @@ void list_weapons(void) mpr( info, MSGCH_EQUIPMENT, menu_colour(info) ); } // end list_weapons() + +static void cmdhelp_showline(int index, const MenuEntry *me) +{ + static_cast(me->data)->display(); +} + +static int cmdhelp_keyfilter(int keyin) +{ + switch (keyin) + { + case CK_DOWN: + case '+': + case '=': + return ('>'); + case CK_UP: + case '-': + case '_': + return ('<'); + case 'x': + return (CK_ESCAPE); + default: + return (keyin); + } +} + +static bool cmdhelp_textfilter(const std::string &tag) +{ +#ifdef STASH_TRACKING + if (tag == "s") + return (true); +#endif +#ifdef WIZARD + if (tag == "wiz") + return (true); +#endif + return (false); +} + +static const char *level_map_help = + "Level Map ('X' in main screen):\n" + "Esc : leave level map (also Space)\n" + "Dir.: move cursor\n" + "/ Dir., Shift-Dir.: move cursor far\n" + "+/- : scroll level map up/down\n" + ". : travel (also Enter and , and ;)\n" + " (moves cursor to last travel\n" + " destination if still on @)\n" + "<</> : cycle through up/down stairs\n" + "^ : cycle through traps\n" + "Tab : cycle through shops and portals\n" + "X : cycle through travel eXclusions\n" + "W : cycle through waypoints\n" + "I : cycle through stashes\n" + "Ctrl-X : set travel eXclusion\n" + "Ctrl-E : Erase all travel exclusions\n" + "Ctrl-W : set Waypoint\n" + "Ctrl-C : Clear level and main maps\n"; + +static void show_keyhelp_menu(const std::vector &lines) +{ + Menu cmd_help; + + // Set flags, and don't use easy exit. + cmd_help.set_flags( + MF_NOSELECT | MF_ALWAYS_SHOW_MORE | MF_NOWRAP, + false); + + // FIXME: Allow for hiding Page down when at the end of the listing, ditto + // for page up at start of listing. + cmd_help.set_more( + formatted_string::parse_string( + "[ + : Page down. - : Page up." + " Esc/x exits.]")); + cmd_help.f_drawitem = cmdhelp_showline; + cmd_help.f_keyfilter = cmdhelp_keyfilter; + + std::vector entries; + + for (unsigned i = 0, size = lines.size(); i < size; ++i) + { + MenuEntry *me = new MenuEntry; + me->data = new formatted_string(lines[i]); + entries.push_back(me); + + cmd_help.add_entry(me); + } + + cmd_help.show(); + + for (unsigned i = 0, size = entries.size(); i < size; ++i) + delete static_cast( entries[i]->data ); +} + +void show_levelmap_help() +{ + std::vector lines = + split_string("\n", level_map_help, false, true); + std::vector formatted_lines; + for (int i = 0, size = lines.size(); i < size; ++i) + formatted_lines.push_back( + formatted_string::parse_string( + lines[i], true, cmdhelp_textfilter)); + show_keyhelp_menu(formatted_lines); +} + +void list_commands(bool wizard) +{ + if (wizard) + { + list_wizard_commands(); + return; + } + + // 2 columns, split at column 40. + column_composer cols(2, 41); + // Page size is number of lines - one line for --more-- prompt. + cols.set_pagesize(get_number_of_lines() - 1); + + cols.add_formatted( + 0, + "Movement:\n" + "To move in a direction or to attack, use\n" + "the numpad (try Numlock both off and on)\n" + "or vi keys:\n" + " 1 2 3 y k u\n" + " \\|/ \\|/\n" + " 4-5-6" + " h-.-l\n" + " /|\\ /|\\\n" + " 7 8 9 b j n\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 0, + "Rest/Search:\n" + "5 (numpad), ., s, Del: " + "rest one turn and\n" + " search adjacent squares.\n" + "Shift-5 (numpad), 5: rest until HP/MP are\n" + "full or something found or 100 turns over\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 0, + "Dungeon Interaction and Information:\n" + "o/c : Open/Close door\n" + "<</> : use staircase (<< also enters shop)\n" + "; : examine occupied tile\n" + "x : eXamine surroundings/targets\n" + "X : eXamine level map\n" + "O : show dungeon Overview\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 0, + "Item Interaction (inventory):\n" + "v : View item description\n" + "{ : inscribe item\n" + "t : Throw/shoot an item\n" + "f : Fire first available missile\n" + "q : Quaff a potion\n" + "e : Eat food (but tries floor first)\n" + "z : Zap a wand\n" + "r : Read a scroll or book\n" + "M : Memorise a spell from a book\n" + "w : Wield an item ( - for none)\n" + "' : wield item a, or switch to b\n" + "E : Evoke power of wielded item\n" + "W : Wear armour\n" + "T : Take off armour\n" + "P : Put on jewellery\n" + "R : Remove jewellery\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 0, + "Other Gameplay Actions:\n" + "a : use special Ability\n" + "p : Pray\n" + "Z : cast a spell\n" + "! : shout or command allies\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 0, + "In-game Toggles:\n" + "Ctrl-A : toggle Autopickup\n" + "Ctrl-V : toggle auto-prayer\n" + "Ctrl-T : toggle spell fizzle check\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 0, + level_map_help, + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 1, + "Extended Movement:\n" + "Ctrl-G : interlevel travel\n" + "Ctrl-O : auto-explore\n" + "Ctrl-W : set Waypoint\n" + "/ Dir., Shift-Dir.: long walk\n" + "* Dir., Ctrl-Dir. : untrap, attack\n" + " without move, open door\n", + true, true, cmdhelp_textfilter, 45); + + + cols.add_formatted( + 1, + "Game Saving and Quitting:\n" + "S : Save game and exit \n" + "Q : Quit without saving\n" + "Ctrl-X : save game without query\n", + true, true, cmdhelp_textfilter, 45); + + cols.add_formatted( + 1, + "Player Character Information:\n" + "@ : display character status\n" + "[ : display worn armour\n" + "\" : display worn jewellery\n" + "C : display experience info\n" + "^ : show religion screen\n" + "A : show Abilities/mutations\n" + "\\ : show item knowledge\n" + "m : show skill screen\n" + "i : show Inventory list\n" + "% : show resistances\n", + true, true, cmdhelp_textfilter, 45); + + cols.add_formatted( + 1, + "Item Interaction (floor):\n" + ", : pick up items (also g) \n" + " (press twice for pick up menu) \n" + "d : Drop an item\n" + "d#: Drop exact number of items \n" + "D : Dissect a corpse \n" + "e : Eat food from floor \n" + "z : Zap a wand \n" + "r : Read a scroll or book \n" + "M : Memorise a spell from a book \n" + "w : Wield an item ( - for none) \n" + "' : wield item a, or switch to b \n" + "E : Evoke power of wielded item\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 1, + "Non-Gameplay Commands / Info\n" + "V : display Version information\n" + "Ctrl-P : show Previous messages\n" + "Ctrl-R : Redraw screen\n" + "Ctrl-C : Clear main and level maps\n" + "# : dump character to file\n" + ": : add note to dump file\n" + "` : add macro\n" + "~ : save macros\n" + "= : reassign inventory/spell letters\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 1, + "Stash Management Commands:\n" + "Ctrl-S : mark Stash\n" + "Ctrl-E : Erase stash (ignore square)\n" + "Ctrl-F : Find (in stashes and shops)\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 1, + "Targeting, Surroundings ('x' in main):\n" + " x : stop targeting (also Esc and Space)\n" + " + : cycle monsters forward\n" + " - : cycle monsters backward\n" + " * : cycle objects forward (also ')\n" + " / : cycle objects backward (also ;)\n" + " . : choose target/move (also Enter)\n" + " ? : describe monster under cursor\n" + "<</> : cycle through up/down stairs\n", + true, true, cmdhelp_textfilter); + + cols.add_formatted( + 1, + "Shortcuts in Lists (like multidrop):\n" + "(/) : selects all missiles/hand weapons\n" + "%/& : selects all food/carrion\n" + "+/? : selects all books/scrolls\n" + "//\\ : selects all wands/staves\n" + "!/\" : selects all potions/jewellry\n" + "[/} : selects all armour/misc. items\n" + ",/- : global select/deselect\n" + "* : invert selection\n", + true, true, cmdhelp_textfilter); + + show_keyhelp_menu(cols.formatted_lines()); +} + +static void list_wizard_commands() +{ + const char *line; + int j = 0; + +#ifdef DOS_TERM + char buffer[4800]; + + window(1, 1, 80, 25); + gettext(1, 1, 80, 25, buffer); +#endif + + clrscr(); + + // BCR - Set to screen length - 1 to display the "more" string + int moreLength = (get_number_of_lines() - 1) * 2; + + for (int i = 0; i < 500; i++) + { + line = wizard_string( i ); + + if (strlen( line ) != 0) + { + // BCR - If we've reached the end of the screen, clear + if (j == moreLength) + { + gotoxy(2, j / 2 + 1); + cprintf("More..."); + getch(); + clrscr(); + j = 0; + } + + gotoxy( ((j % 2) ? 40 : 2), ((j / 2) + 1) ); + cprintf( "%s", line ); + + j++; + } + } + + getch(); + +#ifdef DOS_TERM + puttext(1, 1, 80, 25, buffer); +#endif + + return; +} // end list_commands() + +static const char *wizard_string( int i ) +{ + UNUSED( i ); + +#ifdef WIZARD + return((i == 10) ? "a : acquirement" : + (i == 13) ? "A : set all skills to level" : + (i == 15) ? "b : controlled blink" : + (i == 20) ? "B : banish yourself to the Abyss" : + (i == 30) ? "g : add a skill" : + (i == 35) ? "G : remove all monsters" : + (i == 40) ? "h/H : heal yourself (super-Heal)" : + (i == 50) ? "i/I : identify/unidentify inventory": + (i == 70) ? "l : make entrance to labyrinth" : + (i == 80) ? "m/M : create monster by number/name": + (i == 90) ? "o/% : create an object" : + (i == 100) ? "p : make entrance to pandemonium" : + (i == 110) ? "x : gain an experience level" : + (i == 115) ? "r : change character's species" : + (i == 120) ? "s : gain 20000 skill points" : + (i == 130) ? "S : set skill to level" : + (i == 140) ? "t : tweak object properties" : + (i == 150) ? "X : Receive a gift from Xom" : + (i == 160) ? "z/Z : cast any spell by number/name": + (i == 200) ? "$ : get 1000 gold" : + (i == 210) ? " : create up/down staircase" : + (i == 220) ? "u/d : shift up/down one level" : + (i == 230) ? "~/\" : goto a level" : + (i == 240) ? "( : create a feature" : + (i == 250) ? "] : get a mutation" : + (i == 260) ? "[ : get a demonspawn mutation" : + (i == 270) ? ": : find branch" : + (i == 280) ? "{ : magic mapping" : + (i == 290) ? "^ : gain piety" : + (i == 300) ? "_ : gain religion" : + (i == 310) ? "\' : list items" : + (i == 320) ? "? : list wizard commands" : + (i == 330) ? "| : acquire all unrand artefacts" : + (i == 340) ? "+ : turn item into random artefact" : + (i == 350) ? "= : sum skill points" + : ""); + +#else + return (""); +#endif +} // end wizard_string() + +#ifdef OBSOLETE_COMMAND_HELP +static const char *command_string( int i ) +{ + /* + * BCR - Command printing, case statement + * Note: The numbers in this case indicate the order in which the + * commands will be printed out. Make sure none of these + * numbers is greater than 500, because that is the limit. + * + * Arranged alpha lower, alpha upper, punctuation, ctrl. + * + */ + + return((i == 10) ? "a : use special ability" : + (i == 20) ? "d(#) : drop (exact quantity of) items" : + (i == 30) ? "e : eat food" : + (i == 40) ? "f : fire first available missile" : + (i == 50) ? "i : inventory listing" : + (i == 55) ? "m : check skills" : + (i == 60) ? "o/c : open / close a door" : + (i == 65) ? "p : pray" : + (i == 70) ? "q : quaff a potion" : + (i == 80) ? "r : read a scroll or book" : + (i == 90) ? "s : search adjacent tiles" : + (i == 100) ? "t : throw/shoot an item" : + (i == 110) ? "v : view item description" : + (i == 120) ? "w : wield an item" : + (i == 130) ? "x : examine visible surroundings" : + (i == 135) ? "z : zap a wand" : + (i == 140) ? "A : list abilities/mutations" : + (i == 141) ? "C : check experience" : + (i == 142) ? "D : dissect a corpse" : + (i == 145) ? "E : evoke power of wielded item" : + (i == 150) ? "M : memorise a spell" : + (i == 155) ? "O : overview of the dungeon" : + (i == 160) ? "P/R : put on / remove jewellery" : + (i == 165) ? "Q : quit without saving" : + (i == 168) ? "S : save game and exit" : + (i == 179) ? "V : version information" : + (i == 200) ? "W/T : wear / take off armour" : + (i == 210) ? "X : examine level map" : + (i == 220) ? "Z : cast a spell" : + (i == 240) ? ",/g : pick up items" : + (i == 242) ? "./del: rest one turn" : + (i == 250) ? " : ascend / descend a staircase" : + (i == 270) ? "; : examine occupied tile" : + (i == 280) ? "\\ : check item knowledge" : +#ifdef WIZARD + (i == 290) ? "& : invoke your Wizardly powers" : +#endif + (i == 300) ? "+/- : scroll up/down [level map only]" : + (i == 310) ? "! : shout or command allies" : + (i == 325) ? "^ : describe religion" : + (i == 337) ? "@ : status" : + (i == 340) ? "# : dump character to file" : + (i == 350) ? "= : reassign inventory/spell letters" : + (i == 360) ? "\' : wield item a, or switch to b" : + (i == 370) ? ": : make a note" : +#ifdef USE_MACROS + (i == 380) ? "` : add macro" : + (i == 390) ? "~ : save macros" : +#endif + (i == 400) ? "] : display worn armour" : + (i == 410) ? "\" : display worn jewellery" : + (i == 415) ? "{ : inscribe an item" : + (i == 420) ? "Ctrl-P : see old messages" : +#ifdef PLAIN_TERM + (i == 430) ? "Ctrl-R : Redraw screen" : +#endif + (i == 440) ? "Ctrl-A : toggle autopickup" : + (i == 445) ? "Ctrl-M : toggle autoprayer" : + (i == 447) ? "Ctrl-T : toggle fizzle" : + (i == 450) ? "Ctrl-X : Save game without query" : + +#ifdef ALLOW_DESTROY_ITEM_COMMAND + (i == 451) ? "Ctrl-D : Destroy inventory item" : +#endif + (i == 453) ? "Ctrl-G : interlevel travel" : + (i == 455) ? "Ctrl-O : explore" : + +#ifdef STASH_TRACKING + (i == 456) ? "Ctrl-S : mark stash" : + (i == 457) ? "Ctrl-E : forget stash" : + (i == 458) ? "Ctrl-F : search stashes" : +#endif + + (i == 460) ? "Shift & DIR : long walk" : + (i == 465) ? "/ DIR : long walk" : + (i == 470) ? "Ctrl & DIR : door; untrap; attack" : + (i == 475) ? "* DIR : door; untrap; attack" : + (i == 478) ? "Shift & 5 on keypad : rest 100 turns" + : ""); +} // end command_string() +#endif // OBSOLETE_COMMAND_HELP diff --git a/crawl-ref/source/command.h b/crawl-ref/source/command.h index 7bdd3a9e85..54424d4045 100644 --- a/crawl-ref/source/command.h +++ b/crawl-ref/source/command.h @@ -3,6 +3,8 @@ * Summary: Misc commands. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -12,6 +14,7 @@ #ifndef COMMAND_H #define COMMAND_H +#include "enum.h" // last updated 12may2000 {dlb} /* *********************************************************************** @@ -57,4 +60,9 @@ void list_jewellery(void); void swap_inv_slots(int slot1, int slot2, bool verbose); +void show_levelmap_help(); + +// Actually defined in acr.cc; we may want to move this to command.cc +void process_command(command_type cmd); + #endif diff --git a/crawl-ref/source/dat/levdes.vim b/crawl-ref/source/dat/levdes.vim new file mode 100644 index 0000000000..c489be52a7 --- /dev/null +++ b/crawl-ref/source/dat/levdes.vim @@ -0,0 +1,75 @@ +" levdes.vim: +" +" Basic Vim syntax highlighting for Dungeon Crawl Stone Soup level design +" (.des) files. + +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +syn case match + +setlocal iskeyword+=: +setlocal iskeyword+=- + +syn keyword desDeclarator NAME: ORIENT: DEPTH: PLACE: MONS: FLAGS: SYMBOL: default-depth: TAGS: CHANCE: +syn keyword desOrientation encompass north south east west northeast northwest southeast southwest + +syn match desComment "^\s*#.*$" + +syn keyword desMapBookend MAP ENDMAP contained +syn match desMapFloor /\./ contained +syn match desMapWall /x/ contained +syn match desMapDoor /[+=]/ contained +syn match desMapStoneWall /c/ contained +syn match desMapCrystalWall /b/ contained +syn match desMapMetalWall /v/ contained +syn match desMapWaxWall /a/ contained +syn match desMapMonst /[0-9]/ contained +syn match desMapGold /\$/ contained +syn match desMapLava /l/ contained +syn match desMapWater /w/ contained +syn match desMapEntry /@/ contained +syn match desMapTrap /\^/ contained + +syn match desMapValuable /[R%*|]/ contained +syn match desMapRune /[PO]/ contained +syn match desMapOrb /Z/ contained + +syn cluster desMapElements contains=desMapBookend,desMapWall,desMapFloor +syn cluster desMapElements add=desMapMonst,desMapCrystalWall,desMapGold +syn cluster desMapElements add=desMapLava,desMapMetalWall,desMapDoor +syn cluster desMapElements add=desMapStoneWall,desMapWater,desMapTrap +syn cluster desMapElements add=desMapEntry,desMapWaxWall + +syn cluster desMapElements add=desMapRune,desMapOrb,desMapValuable + +syn region desMap start=/^\s*\\s*$/ end=/^\s*\\s*$/ contains=@desMapElements keepend + +hi link desDeclarator Statement +hi link desMapBookend Statement +hi link desComment Comment +hi link desMap String +hi link desOrientation Type + +hi desMapWall guifg=darkgrey term=bold gui=bold +hi desMapCrystalWall guifg=#009040 term=bold gui=bold +hi desMapStoneWall guifg=black gui=bold +hi desMapMetalWall guifg=#004090 term=bold gui=bold +hi desMapWaxWall guifg=#a0a000 gui=bold +hi desMapFloor guifg=#008000 +hi desMapMonst guifg=red +hi desMapLava guifg=red gui=bold +hi desMapTrap guifg=red gui=bold +hi desMapWater guifg=lightblue +hi desMapGold guifg=#c09000 +hi desMapDoor guifg=brown gui=bold +hi desMapEntry guifg=black guibg=white gui=bold + +hi desMapValuable guifg=darkgreen gui=bold +hi desMapRune guifg=orange gui=bold +hi desMapOrb guibg=gold guifg=black + +syn sync minlines=45 diff --git a/crawl-ref/source/dat/splev.des b/crawl-ref/source/dat/splev.des new file mode 100644 index 0000000000..96c2ee5f81 --- /dev/null +++ b/crawl-ref/source/dat/splev.des @@ -0,0 +1,1563 @@ +############################################################################## +# splev.des: special levels definitions. +# +# If you want to define random vaults and minivaults, they should go +# to vaults.des. +# +# key: +# x - DNGN_ROCK_WALL +# X - DNGN_PERMAROCK_WALL -> should always be undiggable! -- bwr +# c - DNGN_STONE_WALL +# v - DNGN_METAL_WALL +# b - DNGN_GREEN_CRYSTAL_WALL +# a - DNGN_WAX_WALL +# . - DNGN_FLOOR +# + - DNGN_CLOSED_DOOR +# = - DNGN_SECRET_DOOR +# @ - entry point - must be on outside and on a particular side - see templates +# w - water +# l - lava +# >< - extra stairs - you can leave level by these but will never be placed on +# them from another level +# }{ - stairs 82/86 - You must be able to reach these from each other +# )( - stairs 83/87 +# ][ - stairs 84/88 +# I - orcish idol (does nothing) +# ^ - random trap +# +# A - Vestibule gateway (opened by Horn). Can also be put on other levels for +# colour, where it won't do anything. +# B - Altar. These are assigned specific types (eg of Zin etc) in dungeon.cc, +# in order. +# C - Random Altar. +# F - Typically a Granite Statue, but may be Orange or Silver (1 in 100) +# G - Granite statue (does nothing) +# H - orange crystal statue (attacks mind) +# S - Silver statue (summons demons). Avoid using (rare). +# T - Water fountain +# U - Magic fountain +# V - Permanently dry fountain +# +# Statues can't be walked over and are only destroyed by disintegration +# +# $ - gold +# % - normal item +# * - higher level item (good) +# | - acquirement-level item (almost guaranteed excellent) +# O - place an appropriate rune here +# P - maybe place a rune here (50%) +# R - honeycomb (2/3) or royal jelly (1/3) +# Z - the Orb of Zot +# +# 0 - normal monster +# 9 - +5 depth monster +# 8 - (+2) * 2 depth monster (aargh!). Can get golden dragons and titans +# this way. +# 1-7 - monster array monster +# used to allocate specific monsters for a vault. +# is filled with RANDOM_MONSTER if monster not specified +# +# note that a lot of the vaults are in there mainly to add some interest to the +# scenery, and are not the lethal treasure-fests you find in Angband +# (not that there's anything wrong with that) +# +# Guidelines for creating new vault maps: +# +# [ds] If your map is not a minivault, make sure the side(s) that form the +# border have a rock wall padding at least 6 deep. For instance, if your map +# is ORIENT: north, you must have a 6 deep border of rock wall (or any +# other kind of wall) along the northern, eastern, and western edges of the +# map. If you're doing a fullscreen map (encompass), you must pad all around +# the map with 6 layers of wall. +# +# Basically you can just let your creativity run wild. You do not have +# to place all of the stairs unless the level is full screen, in which +# case you must place all except the extra stairs (> and <). The <> stairs +# can be put anywhere and in any quantities but do not have to be there. Any +# of the other stairs which are not present in the vault will be randomly +# placed outside it. Also generally try to avoid rooms with no exit. +# +# You can use the templates below to build vaults. The entry point '@' must be +# present (except full-screen vaults where it must not) and be on the same +# side of the vault as it is on the template, but can be anywhere along that +# side. +# +# I think that's all. Have fun! +# +# ps - remember to add one to the monster array value when placing monsters +# on each map (it is 1-7, not 0-6) {dlb} +# +# [dshaligram] All special levels MUST have an ORIENT: attribute; if there's +# no ORIENT: attribute, the level is considered to be a minivault, which is +# usually not what you want. +# +# Special levels are selected either by PLACE: (for most special levels) or +# TAGS: (for the Pandemonium demon lords). If you want to define alternate +# levels, duplicate the selector and use different names. For instance, to +# define an alternate Vestibule level, you could use something like this: +# +# NAME: vestibule_of_hell_alternate +# PLACE: Hell +# +# To define an alternate level for Cerebov, you could do: +# +# NAME: cerebov_alternate +# TAGS: cerebov +# +# You can also use CHANCE: to control the weight a level is assigned. For +# instance, if you want to make cerebov_alternate five times more likely than +# the standard cerebov level, you'd do: +# +# NAME: cerebov_alternate +# TAGS: cerebov +# CHANCE: 50 +# +# (The default weight is 10 if CHANCE: is omitted.) +# +############################################################################## + +NAME: vestibule_of_hell +PLACE: Hell +ORIENT: encompass + +# [dshaligram] If modifying the Vestibule, ensure that: +# +# * The portal to Dis is surrounded by floor squares and nothing but floor +# squares. +# * The portal to Tartarus is surrounded only by floor, doors of any kind, and +# at least one rock wall. +# * The portal to Gehenna is surrounded by nothing but floor and at least one +# lava square. +# * The portal to Cocytus is surrounded by nothing but floor and at least one +# water square. +# +# If you don't do this, the portals will not be unbarred correctly when +# Geryon's horn is sounded. + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvvvvvvvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..v.....v..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.....v.....v.....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxx........v.....v........xxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxx..........v..A..v..........xxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx............v.....v............xxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx.............v.....v.............xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxx..............vvv+vvv..............xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxx.....................................xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxx.......................................xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxx.........................................xxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxx...........................................xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxx.............................................xxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxx...............................................xxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxx.................................................xxxxxxxxxxxxxxxx +xxxxxxxxxxxxxx...................................................xxxxxxxxxxxxxxx +xxxxxxxxxxxxx.....................................................xxxxxxxxxxxxxx +xxxxxxxxxxxxx.....................................................xxxxxxxxxxxxxx +xxxxxxxxxxxx.......................................................xxxxxxxxxxxxx +xxxxxxxxxxxx.......................................................xxxxxxxxxxxxx +xxxxxxxxxxx.........................................................xxxxxxxxxxxx +xxxxxxxxxxx............................{............................xxxxxxxxxxxx +xxxxxxxxxxx.........................................................xxxxxxxxxxxx +xxxxxxxxxx...l.l.....................................................xxxxxxxxxxx +xxxxxxxxxx..l.l.l.l..................................................xxxxxxxxxxx +xxxxxxxxxx.l.l.l.l.l.................................................xxxxxxxxxxx +xxxxxxxxx.l.l.l.l.l...................................................xxxxxxxxxx +xxxxxxxxxl.l.l.l.l.l..................................................xxxxxxxxxx +xxxxxxxxx.l.l.l.A.l.l.................}1].............................=Axxxxxxxx +xxxxxxxxxl.l.l.l.l.l.l.................)..............................xxxxxxxxxx +xxxxxxxxx.l.l.l.l.l.l.................................................xxxxxxxxxx +xxxxxxxxxx.l.l.l.l.l.l...............................................xxxxxxxxxxx +xxxxxxxxxx..l.l.l.l..................................................xxxxxxxxxxx +xxxxxxxxxx.....l.l...................................................xxxxxxxxxxx +xxxxxxxxxxx......................[...........(......................xxxxxxxxxxxx +xxxxxxxxxxx.........................................................xxxxxxxxxxxx +xxxxxxxxxxx.........................................................xxxxxxxxxxxx +xxxxxxxxxxxx.......................................................xxxxxxxxxxxxx +xxxxxxxxxxxx.......................................................xxxxxxxxxxxxx +xxxxxxxxxxxxx.....................................................xxxxxxxxxxxxxx +xxxxxxxxxxxxx.....................................................xxxxxxxxxxxxxx +xxxxxxxxxxxxxx...................................................xxxxxxxxxxxxxxx +xxxxxxxxxxxxxxx....................wwwww........................xxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxx..................wwwwwwww.....................xxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxx..............wwwwwwwwwwwww..................xxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxx...........w..wwww..wwwww..w...............xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxx..........w...ww.....ww..wwwww...........xxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxx.........ww......ww....wwwwwwwww.......xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxx.........ww....wwww...wwwwwwwwww.....xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxx.........ww....ww....wwwwwwwwwww...xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx........wwww.......wwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx......wwwwwww....wwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxx...wwwwwwwwwwAwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: Geryon +MONS: random, random, random, random, random, random + +############################################################################# +# Dispater's castle - rest of level filled up with plan_4 (irregular city) +# + +NAME: castle_dis +PLACE: Dis:7 +TAGS: dis +ORIENT: north + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvxxxxxxxx +xxxxxxxxv..............................................................vxxxxxxxx +xxxxxxxxv..vvvvvvvvv........................................vvvvvvvvv..vxxxxxxxx +xxxxxxxxv..v3.....|v........................................v|.....2v..vxxxxxxxx +xxxxxxxxv..v.vv+vvvv.v.v.v.v.v.v.v.v.v..v.v.v.v.v.v.v.v.v.v.vvvv+vv.v..vxxxxxxxx +xxxxxxxxv..v.v.....vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.....v.v..vxxxxxxxx +xxxxxxxxv..v|v.....+$$v$$+$$v||vvvvvvvvvvvvvvvvv$$$$v4.4.v$$v.....v|v..vxxxxxxxx +xxxxxxxxv..vvvv+vvvv$$+$$v$$+||v...............v$$$$+.4.4+$$v+vv+vvvv..vxxxxxxxx +xxxxxxxxv....vv.vvvvvvvvvvvvvvvv.v..v..v..v..v.v$$$$v4.4.v$$+||v.vv5...vxxxxxxxx +xxxxxxxxv...vvv................v...............vvvvvvvvvvvvvvvvv.vvv...vxxxxxxxx +xxxxxxxxv...5vv................+...............+.................vv....vxxxxxxxx +xxxxxxxxv...vvv+vvvvvvvvvvvvvvvv.v..v..v..v..v.vvvvvvvvvvvvvvvvv.vvv...vxxxxxxxx +xxxxxxxxv....vv..v.+$$$$$v.....v...............vvvvvvvvvvvvvvvvv.vv5...vxxxxxxxx +xxxxxxxxv...vvv..v.v$$$$$v.....v...............vv|$|$|vv|$|$|$vv.vvv...vxxxxxxxx +xxxxxxxxv...5vv..v.vvvvvvv.....vvvvv.......vvvvvv$|$|$++$|$|$|vv.vv....vxxxxxxxx +xxxxxxxxv...vvv..v...............v.vvvv+vvvvvvvvvvvvvvvvvvvvv+vv.vvv...vxxxxxxxx +xxxxxxxxv....vvv+v..........vvvvv.4vvv...vvvvvvvvvvvvvvvvvvvv+vv.vv5...vxxxxxxxx +xxxxxxxxv...vvv..v.v..v..v....2vvv+vv5...5vvvvvvv.4.4.vv.4.4.4vv.vvv...vxxxxxxxx +xxxxxxxxv...5vv.................vv|vvv...vvvvv.++4.4.4++4.4.4.vv.vv....vxxxxxxxx +xxxxxxxxv...vvv.................1vOvv5...5vvvv.vvvvvvvvvvvvvvvvv.vvv...vxxxxxxxx +xxxxxxxxv....vv.................vv|vvv...vvvvv.vvvvvvvvvvvvvvvvv.vv5...vxxxxxxxx +xxxxxxxxv...vvv.v..v..v..v....3vvv+vv5...5vvvv...................vvv...vxxxxxxxx +xxxxxxxxv...5vv.............vvvvv.4vvv...vvvvvvvvvvvvvvvvvvvvvvv.vv....vxxxxxxxx +xxxxxxxxv..vvvv+vvvv.............v.vv5...5vvvvvvvvvvvvvvvvvvvvvv+vvvv..vxxxxxxxx +xxxxxxxxv..v|v.....vvvvvvvvvvvvvvvvvvv...vvvvvvvvvvvvvvvvvvvv.....v|v..vxxxxxxxx +xxxxxxxxv..v.v.....vvvvvvvvvvvvvvvvvvvv+vvvvvvvvvvvvvvvvvvvvv.....v.v..vxxxxxxxx +xxxxxxxxv..v.vv+vvvv5.............5.........5..............5vvvv+vv.v..vxxxxxxxx +xxxxxxxxv..v2.....|v........................................v|.....3v..vxxxxxxxx +xxxxxxxxv..vvvvvvvvv........................................vvvvvvvvv..vxxxxxxxx +xxxxxxxxv............................{.[.(.............................vxxxxxxxx +ENDMAP + +MONS: Dispater, Fiend, Ice Fiend, iron devil, metal gargoyle +MONS: random, random + +############################################################################# +# Asmodeus +# + +NAME: asmodeus +PLACE: Geh:7 +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxx....xxxxxxxxxxxxxxx.xxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxx..xxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxx....xxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxx...xxx................................xxxxxx....xxxxxxxxxxx +xxxxxxxxxxxx.x.xxxxx.........................................xxx....xxxxxxxxxxxx +xxxxxxxxxxxx....xx.....................4......................xx...xxxxxxxxxxxxx +xxxxxxxxxxx......x......................llllllllllllll.........x..xxxxxxxxxxxxxx +xxxxxxxxxxx..xx..................lllllllllllllllllllllllll........xxxxxxxxxxxxxx +xxxxxxxxxx...xxx....0..........llllllllllllllllllllllllll........xx...xxxxxxxxxx +xxxxxxxxx....xxx.............llllllllllllllllllllllllllll..............xxxxxxxxx +xxxxxxxxxx....xx...........lllllllllllllllllllllllllllll...............xxxxxxxxx +xxxxxxxxxxxx..............llllllllllllllllllllllllllllll...2..xx...0...xxxxxxxxx +xxxxxxxxxxxxx...........lllllllllllllllllll.......llllll......xx......xxxxxxxxxx +xxxxxxxxxxxxxx.......llllllllllllllllll............llllll.............xxxxxxxxxx +xxxxxxxxxxxxxxx......lllllllll..........4.........4.lllllll..........xxxxxxxxxxx +xxxxxxxxxx...xx...ll3lllll......4...................llllllll......x.xxxxxxxxxxxx +xxxxxxxxx.......lllll.l................................llll.......xxxxxxxxxxxxxx +xxxxxxxxxx..4..llllll...cccccccc+c+c+c+c+c+c+c+c+c+c....lll......xxxxxxxxxxxxxxx +xxxxxxxxxxx..lllllll..4.c.....c....................c....llll.....xxxxxxxxxxxxxxx +xxxxxxxxxx...llllll.....c.V.V.+....0.....3.....0...c.....llll....x..xxxxxxxxxxxx +xxxxxxxxx...llllll...l..c.....c....................c....lllll........xxxxxxxxxxx +xxxxxxxxxx...lllll..ll..c..5..cccccccccccccccccccccc.4..llllll........xxxxxxxxxx +xxxxxxxxx...lllll..llll.c.....c...............c....c....lllllll.......xxxxxxxxxx +xxxxxxxxx...lllll..llll.c.V.V.c.......0.......c....c....lllllll.......xxxxxxxxxx +xxxxxxxxxx...lllll..lll.c.....+...............+....c...lllllll........xxxxxxxxxx +xxxxxxxxxxx..lllll...ll.cccccccccc....0.......c....c...llllllll........xxxxxxxxx +xxxxxxxxxx...lllll..4...c|$$||$$|c............c.0..c...llllllll........xxxxxxxxx +xxxxxxxxx...lllll.......c$$$$$$$$cccccccccccccc....c...lllllll.........xxxxxxxxx +xxxxxxxxx...lllll.......c$$|2|$$|c..0.........+....c...lllllll........xxxxxxxxxx +xxxxxxxxxx.lllllll......c|$$$$$$$c........9...c....c....llllllll.....xxxxxxxxxxx +xxxxxxxxxx.lllllll......c$|$|$$|$c+ccccccccccccccccc....lllllll......xxxxxxxxxxx +xxxxxxxxxx..llllll......cccccccc+c.....9.......c.........llllll......x.xxxxxxxxx +xxxxxxxxxx..lllllll.....c$$$$$$+3c.....8...3...c.....4...llllll........xxxxxxxxx +xxxxxxxxxx..llllllll....c$$$$$$c.c.....9.......c..ll....llllll.........xxxxxxxxx +xxxxxxxxxx...llllll..4..c$$2$$$c.ccccccccccccc+c.lll...lllllll...0....xxxxxxxxxx +xxxxxxxxxxx..llllll.....c$$$$$$c..+............c.ll...lllllll..........xxxxxxxxx +xxxxxxxxxxx..llllllll...ccccccccc+cccccccccccccc.....lllllll...........xxxxxxxxx +xxxxxxxxxxxx..llllllll.........cc..........cc........lllllll.......x..xxxxxxxxxx +xxxxxxxxxxxxx.llllllllll.......ccc.........cc......lllllllll.......xxxxxxxxxxxxx +xxxxxxxxxx....lllllllllll...4...cc.....2.2.cc....llllllllll.4.......xxxxxxxxxxxx +xxxxxxxxx....4.lllllllllllll....cccccccc+cccc..lllllllllll.....xx....xxxxxxxxxxx +xxxxxxxxxx.....llllllllllllll...cccccccc+cccc..llllllllll......xx....xxxxxxxxxxx +xxxxxxxxxxx.....lllllllllllllll..cc......cc...lllllllllll...........xxxxxxxxxxxx +xxxxxxxxxxx.....llllllllllllll...ccO1....cc.4..lllllllll...........xxxxxxxxxxxxx +xxxxxxxxxxxx.....lllllllllllll...cc......cc....lllllllll.......xx.xxxxxxxxxxxxxx +xxxxxxxxxxxx.......llllllllllll..cccccccccc...lllllllll........xxxxxxxxxxxxxxxxx +xxxxxxxxx.........llllllllllllll.cccccccccc.lllllllllll.......xxxxxxxxxxxxxxxxxx +xxxxxxxxxx....0...llllllllllllll............lllllllll....0....xxxxxxxxxxxxxxxxxx +xxxxxxxxxx.......4.lllllllllllllll..4....lllllllll...........xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx..........llllllllllllll....lllllll....4.....x........xxxxxxxxxxxxxxx +xxxxxxxxxxx...xx.........lllllllllllllllll...................xx{xxxxxxxxxxxxxxxx +xxxxxxxxxxxxx..xx................lllllll.....................xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxx.........xxx.................xxxxxx......xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxx....xxxxxxxx...xxx......xxxxxxxxxx.......xxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxx(xxxxxxxxxxxx[xxxxx...xxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: Asmodeus, Fiend, Balrug, molten gargoyle +MONS: Serpent of Hell, random, random + +############################################################################ +# Antaeus; bottom of Cocytus. This needs work. +# + +NAME: antaeus +PLACE: Coc:7 +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxx........................xxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxx..........................xxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx................................xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx....cccccccccccc..cccccccccccc....xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxx....ccccccccccccc2.ccccccccccccc....xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxx....cc..........................cc....xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxx....cc............................cc....xxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwwwwwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwwwwwwwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwwwwwwwwwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxx....cc...ww.......3..........3.......ww...cc....xxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxx....cc...ww............................ww...cc....xxxxxxxxxxxxxxx +xxxxxxxxxxxxxx....cc...ww....cccccccccccccccccccccc....ww...cc....xxxxxxxxxxxxxx +xxxxxxxxxxxxx....cc...ww....cccccccccccccccccccccccc....ww...cc....xxxxxxxxxxxxx +xxxxxxxxxxxx....cc...ww....cc......................cc....ww...cc....xxxxxxxxxxxx +xxxxxxxxxxx....cc...ww....cc...T................T...cc....ww...cc....xxxxxxxxxxx +xxxxxxxxxx....cc...ww....cc..........wwwwww..........cc....ww...cc....xxxxxxxxxx +xxxxxxxxx....cc...ww....cc.......wwwwwwwwwwwwww.......cc....ww...cc....xxxxxxxxx +xxxxxxxxx....cc...ww...cc.....wwwwwwwwwwwwwwwwwwww.....cc...ww...cc....xxxxxxxxx +xxxxxxxxx....cc..www..cc....wwwwwwwwwccccccwwwwwwwww....cc..www..cc....xxxxxxxxx +xxxxxxxxx....cc..www.cc....wwwwwwwwccc2O12cccwwwwwwww....cc.www..cc....xxxxxxxxx +xxxxxxxxx....cc..www.cc...wwwwwwwwcc2+....+2ccwwwwwwww...cc.www..cc....xxxxxxxxx +xxxxxxxxx....cc..www.cc...wwwwwwwwcc+cc++cc+ccwwwwwwww...cc.www..cc....xxxxxxxxx +xxxxxxxxx....cc..www..c..wwwwwwwwwc|||c..c$$$cwwwwwwwww..c..www..cc....xxxxxxxxx +xxxxxxxxx....cc..wwww.c.wwwwwwwwwwc|||c..c$$$cwwwwwwwwww.c.wwww..cc....xxxxxxxxx +xxxxxxxxx....cc..wwww.c.wwwwwwwwwwcc||c..c$$ccwwwwwwwwww.c.wwww..cc....xxxxxxxxx +xxxxxxxxx....cc..wwww.c.wwwwwwwwwwwcccc++ccccwwwwwwwwwww.c.wwww..cc....xxxxxxxxx +xxxxxxxxx....cc..www..c..wwwwwwwwwwwwww..wwwwwwwwwwwwww..c..www..cc....xxxxxxxxx +xxxxxxxxx....cc..www.cc...wwwwwwwwwwwwwwwwwwwwwwwwwwww...cc.www..cc....xxxxxxxxx +xxxxxxxxx....cc..www.cc....wwwwwwwwwwwwwwwwwwwwwwwwwww...cc.www..cc....xxxxxxxxx +xxxxxxxxx....cc..www.cc....wwwwwwwwwwwwwwwwwwwwwwwwww....cc.www..cc....xxxxxxxxx +xxxxxxxxx....cc..www..cc....wwwwwwwwwwwwwwwwwwwwwwww....cc..www..cc....xxxxxxxxx +xxxxxxxxx....cc...ww...cc.....wwwwwwwwwwwwwwwwwwww.....cc...ww...cc....xxxxxxxxx +xxxxxxxxx....cc...ww....cc.......wwwwwwwwwwwwww.......cc....ww...cc....xxxxxxxxx +xxxxxxxxxx....cc...ww....cc..........wwwwww..........cc....ww...cc....xxxxxxxxxx +xxxxxxxxxxx....cc...ww....cc...T................T...cc....ww...cc....xxxxxxxxxxx +xxxxxxxxxxxx....cc...ww....cc......................cc....ww...cc....xxxxxxxxxxxx +xxxxxxxxxxxxx....cc...ww....ccccccccccc..ccccccccccc....ww...cc....xxxxxxxxxxxxx +xxxxxxxxxxxxxx....cc...ww....cccccccccc2.cccccccccc....ww...cc....xxxxxxxxxxxxxx +xxxxxxxxxxxxxxx....cc...ww............................ww...cc....xxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxx....cc...ww..........................ww...cc....xxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwwww..wwwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwww..wwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxx....cc...wwwwwwwwwww..wwwwwwwwwww...cc....xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxx....cc............................cc....xxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxx....cc..........................cc....xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxx....cccccccccccccccccccccccccccc....xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx....cccccccccccccccccccccccccc....xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx................................xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxx..........{.(.[...........xxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: Antaeus, Ice Fiend, ice dragon +MONS: random, random, random, random + +############################################################################## +# Ereshkigal (Tartarus) +# + +NAME: ereshkigal +PLACE: Tar:7 +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx.................cccc..........ccc............................xxxxxxxxx +xxxxxxxxx.............ccccc..cccc.....ccc.cccc.........................xxxxxxxxx +xxxxxxxxx...........ccc.........ccccccc.....cc.........................xxxxxxxxx +xxxxxxxxx.........ccc.......2............V..cc.........................xxxxxxxxx +xxxxxxxxx........cc4........................cc...........xxxxxxxx......xxxxxxxxx +xxxxxxxxx........cc44xxx==xxx...............cc..........xx......xx.....xxxxxxxxx +xxxxxxxxx........ccxxx......xxx.......ccc++ccc.........xx........xx....xxxxxxxxx +xxxxxxxxx........cxx..........xxx.....ccc44ccc.........x..........x....xxxxxxxxx +xxxxxxxxx........cx............xx....cccc44cc.........xx..........xx...xxxxxxxxx +xxxxxxxxx.......ccx.G........G.xxx7ccc..c44c..........x.....|......x...xxxxxxxxx +xxxxxxxxx.......cxx............xxxcc..................x......7.....x...xxxxxxxxx +xxxxxxxxx......ccx..............xxc...................xx..........xx...xxxxxxxxx +xxxxxxxxx......ccx..G........G..xxc..x.........x.......x..........x....xxxxxxxxx +xxxxxxxxx......ccx..............xcc....................xx........xx....xxxxxxxxx +xxxxxxxxx.......cxx............xxc......................xx......xx.....xxxxxxxxx +xxxxxxxxx.......ccx.F........F.xcc.......................xxxxxxxx......xxxxxxxxx +xxxxxxxxx........cx............xc......................................xxxxxxxxx +xxxxxxxxx........cxx....17....xxc....x.........x.......................xxxxxxxxx +xxxxxxxxx........ccxxx......xxxcc......................................xxxxxxxxx +xxxxxxxxx........cccc=xxxxxx=cccc......................................xxxxxxxxx +xxxxxxxxx........cc||cccccccc||cc......................................xxxxxxxxx +xxxxxxxxx.........cc||||O|||||cc.......................................xxxxxxxxx +xxxxxxxxx..........cccccccccccc......x.........x............V..........xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx...........................................xx$$$$xxx|||||xx...xxxxxxxxx +xxxxxxxxx.......V........V...........x.........x....xx$$$$xxx|||||xx...xxxxxxxxx +xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx...........................................xx44444xx22222xx...xxxxxxxxx +xxxxxxxxx.......xxxxxxxxx+xxxxxxxxx.................xx44444xx22222xx...xxxxxxxxx +xxxxxxxxx.......x3.2..........3...x..x.........x..xxxxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx.......x.x.x.x.x.x.x.x.x.x.................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx.......x...2.3..4..5..4..x......................=.......xxx...xxxxxxxxx +xxxxxxxxx.......xx.x.x.x.x.x.x.x.xx......................=.......xxx...xxxxxxxxx +xxxxxxxxx.......x..65..3..6.6...5.x.................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx.......x.x.x.x.x.x.x.x.x.x..x.........x..xxxxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx.......x...4...3.....4...x.................xx.....xx555555x...xxxxxxxxx +xxxxxxxxx.......xx=xxxxx.x.xxxxxxxx.................xx.....xx555555x...xxxxxxxxx +xxxxxxxxx.......x$$$$$$x.25.x$$$||x.................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx.......x$x$$x$xx.x.x$x$x|x.................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx.......x||||||x.556=$$$||x..x.........x....xx$$xx56565xx$|x...xxxxxxxxx +xxxxxxxxx.......xxxxxxxxxxxxxxxxxxx.................xx$$xx65656xx|7x...xxxxxxxxx +xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx........(...........................................[.........xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................{...............................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: Ereshkigal, necrophage, wraith, shadow, small zombie +MONS: small skeleton, Shadow Fiend + + +########################################################################### +# mnoleg. +# +# NOTE: The Pandemonium demonlord levels are requested by tag; if you change +# the tag, also update dungeon.cc. +# +# You can define alternate levels for a Pandemonium lord by using the same +# TAGS: and a different NAME: +# + +NAME: mnoleg +TAGS: mnoleg +ORIENT: northeast + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +x.................2............xxxxxxxxx +x.....2........................xxxxxxxxx +x..cccccccc...ccccccc..ccccccc.xxxxxxxxx +x..ccccccccc.2.ccccccc..cccccc.xxxxxxxxx +x..cccccccccc...ccccccc..ccccc.xxxxxxxxx +x..ccccccccccc.1.ccccccc..cccc.xxxxxxxxx +x2.cccccccccc.2..Occccccc2.ccc.xxxxxxxxx +x..ccccccccc.....ccccccccc..cc.xxxxxxxxx +x..cccccccc...c...ccccccccc..c.xxxxxxxxx +x..ccccccc...ccc...ccccccccc...xxxxxxxxx +x..cccccc...ccccc...ccccccccc..xxxxxxxxx +x..ccccc...ccccccc...ccccccccc.xxxxxxxxx +x..cccc...ccccccccc...ccccccc..xxxxxxxxx +x..ccc.2.ccccccccccc.2.ccccc...xxxxxxxxx +x..cc.....ccccccccccc...ccc....xxxxxxxxx +x..c...c...ccccccccccc...c.2...xxxxxxxxx +x.....ccc.2.ccccccccccc......c.xxxxxxxxx +x....ccccc...ccccccccccc....cc.xxxxxxxxx +x.2.ccccccc...ccccccccccc..ccc.xxxxxxxxx +x.................2.......cccc.xxxxxxxxx +x...c..ccccccc.ccccccc...ccccc.xxxxxxxxx +x..ccc......2c.c2cccc...cccccc.xxxxxxxxx +x.ccccc..ccc.c.c2ccc.2.ccccccc.xxxxxxxxx +x.cccccc..cc.c.c.cc...cccccccc.xxxxxxxxx +x.ccccccc..c.c.c.c...ccccccccc.xxxxxxxxx +x.cccccccc...c.c....cccccccccc.xxxxxxxxx +x.ccccccccc..c.c...ccccccccccc.xxxxxxxxx +x..............................xxxxxxxxx +xxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: Mnoleg, neqoxec +MONS: random, random, random, random, random + +####################################################################### +# lom_lobon +# + +NAME: lom_lobon +TAGS: lom_lobon +ORIENT: north + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwww.......wwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwbbbwwwwwww.......wwwwwwwxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxwwwwwwwwwwwwbbbbbbbbbbbwwwwww.........wwwwwwxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxwwwwwwwwwwwwbbbbwwwwwwwwwbbbbwwwwww.........wwwwwwxxxxxxxxxxxxxxx +xxxxxxxxxxxxxwwwwwwwbbbbbbbbwwwwwwwwwwwwwwwbbbwwwww...........wwwwwxxxxxxxxxxxxx +xxxxxxxxxxxxwwwwwbbbb......bbbwwwwwwwwwwww...bbwwwww.............wwwxxxxxxxxxxxx +xxxxxxxxxxxxwwwbbb...........bbbwwwwww........bbwwwww.............wwxxxxxxxxxxxx +xxxxxxxxxxxwwwbb...............bbwwww..........bwwwwww.............wwxxxxxxxxxxx +xxxxxxxxxxxwwbb........1O.......bbww...........bbwwww..............wwxxxxxxxxxxx +xxxxxxxxxxwwwb...................bw......2......bwww.....U....2.....wwxxxxxxxxxx +xxxxxxxxxxwwbb...................bb.............bww.................wwxxxxxxxxxx +xxxxxxxxxxwwbb..3................bbb............bbw..............4..wwxxxxxxxxxx +xxxxxxxxxwwbbb...................b.b............4....................wwxxxxxxxxx +xxxxxxxxxwwbwbb.................bb.......U......4..........U..........wxxxxxxxxx +xxxxxxxxxwwbwwbb...............bb..b............bbw..............4.....xxxxxxxxx +xxxxxxxxxwwbbwwbbb...........bbb..bb............bwww...................xxxxxxxxx +xxxxxxxxxwwwbwwwwb..b..2..bbbb....b.............bwww...................xxxxxxxxx +xxxxxxxxxxwwbwwww...bbbbbbb.......bw.....3.....bbwwww...U.....3.......xxxxxxxxxx +xxxxxxxxxxwwbbww.................bbww........wwbwwwww.................xxxxxxxxxx +xxxxxxxxxxwwwbbw................bbwwwww....wwwbbwwww..................xxxxxxxxxx +xxxxxxxxxxwwwwbb...4...U........bwwwwwwwwwwwwbbwww....................xxxxxxxxxx +xxxxxxxxxxxwwwwbbb...........bbbbbwwwwwwwwwbbbwww....................xxxxxxxxxxx +xxxxxxxxxxxwwwwwwbbbb.....bbbbwwwbbbbwwwbbbbwwww....................xxxxxxxxxxxx +xxxxxxxxxxxwwwwwwwwwbbbbbbbwwwwwwwwwbbbbbwwwww......4.....4........xxxxxxxxxxxxx +xxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww......................xxxxxxxxxxxxxx +xxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwww.......................xxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwww........................xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxwwwwwww......................xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...@.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: Lom Lobon, giant orange brain, rakshasa, wizard +MONS: random, random, random + +############################################################################# +# cerebov + +NAME: cerebov +TAGS: cerebov +ORIENT: northeast + +# you might not want to teleport too much on this level - unless you can +# reliably teleport away again. + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +...............................xxxxxxxxx +.............vvvvv.............xxxxxxxxx +.............v$$$v.............xxxxxxxxx +.............v|||v.............xxxxxxxxx +.............v$$$v.............xxxxxxxxx +.vvvvv...vvvvvvvvvvvvv...vvvvv.xxxxxxxxx +.v|$|vvvvv...........vvvvv$|$v.xxxxxxxxx +.v$|$v.....vvvvvvvvv.....v|$|v.xxxxxxxxx +.v|$|v.vvvvvvvvOvvvvvvvv.v$|$v.xxxxxxxxx +.vvvvv.vvvvvv..3..vvvvvv.vvvvv.xxxxxxxxx +...v...vv.....vvv.....vv...v...xxxxxxxxx +...v.vvvv....vv1vv....vvvv.v...xxxxxxxxx +...v.vv......v...v......vv.v...xxxxxxxxx +...v.vvvv.............vvvv.v...xxxxxxxxx +...v...vv..2.......2..vv...v...xxxxxxxxx +.vvvvv.vv..2.......2..vv.vvvvv.xxxxxxxxx +.v|$|v.vv.............vv.v$|$v.xxxxxxxxx +.v|$|v.vv...vv...vv...vv.v$|$v.xxxxxxxxx +.v|$|v.vv...vv+++vv...vv.v$|$v.xxxxxxxxx +.vvvvv.vvvvvvv...vvvvvvv.vvvvv.xxxxxxxxx +....v..vvvvvvv...vvvvvvv..v....xxxxxxxxx +....vv...................vv....xxxxxxxxx +.....vv.vvvvv..2..vvvvv.vv.....xxxxxxxxx +......vvv|||v.....v$$$vvv......xxxxxxxxx +........v|$|vv...vv$|$v........xxxxxxxxx +........v|||v.....v$$$v........xxxxxxxxx +........vvvvv.....vvvvv........xxxxxxxxx +...............................xxxxxxxxx +...............@...............xxxxxxxxx +ENDMAP + +MONS: Cerebov, Balrug, Pit Fiend +MONS: random, random, random, random + + +############################################################################## +# Gloorx Vloq + +NAME: gloorx_vloq +TAGS: gloorx_vloq +ORIENT: southwest + +MAP +xxxxxxxxxxxxxxxxxxxxxxx@.xxxxxxxxxxxxxxx +xxxxxxxxx..............................x +xxxxxxxxx..............................x +xxxxxxxxx..............................x +xxxxxxxxx.x.x.x.x.x.x.x..x.x.x.x.x.x.x.x +xxxxxxxxx..............................x +xxxxxxxxx.x.xxxx=xxxxxxxxxxxx=xxxxxx.x.x +xxxxxxxxx...xx....................xx...x +xxxxxxxxx.x.x..ccccc..4..4..ccccc..x.x.x +xxxxxxxxx...x.cc.3............3.cc.x...x +xxxxxxxxx.x.x.c..ccccc.cc.ccccc..c.x.x.x +xxxxxxxxx...x.c.cc.....cc.....cc.c.x...x +xxxxxxxxx.x.x.c.c.2...cccc...2.c.c.x.x.x +xxxxxxxxx...x...c...ccc..ccc...c...=...x +xxxxxxxxx.x.x.3.....2..1O..2.....3.x.x.x +xxxxxxxxx...=...c...ccc..ccc...c...x...x +xxxxxxxxx.x.x.c.c.2...cccc...2.c.c.x.x.x +xxxxxxxxx...x.c.cc.....cc.....cc.c.x...x +xxxxxxxxx.x.x.c..ccccc.cc.ccccc..c.x.x.x +xxxxxxxxx...x.cc.3............3.cc.x...x +xxxxxxxxx.x.x..ccccc..4..4..ccccc..=.x.x +xxxxxxxxx...xx....................xx...x +xxxxxxxxx.x.xxxx=xxxx=xxxxxxxx=xxxxx.x.x +xxxxxxxxx..............................x +xxxxxxxxx.x.x.x.x.x.x.x..x.x.x.x.x.x.x.x +xxxxxxxxx..............................x +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: Gloorx Vloq, Executioner, demonic crawler, shadow demon +MONS: random, random, random + +############################################################################ +# Hive:4 beehive + +NAME: beehive +PLACE: Hive:4 +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxaaaaaaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxaaaaaaaaaaaRaaaaaaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxaaaaaaaaaaRa2aaR1RaaRa2aaaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxaaaaaaaaaaRa2a3R3aRaRaRaaaaaaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxaaaaRaRaRaaa3aaa3aRa.a.aaaaaaaaaaaxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxaaaaaaRa.aRa2a2a2a2aRaRa.a.a3aaaaaaaaaaaaaaxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxx4aaaaaaaaa.aaRaRaa2aa2aaRaaa.aa3a33aaaaaaaaaa.44xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx.4aaaaaaa.222a3a.aaaRaaa.aaa.R3aa3a3aaaaaaaa.....4xxxxxxxxxxxxxxxxxxx +xxxxxxxxxx....aaaaaaa.aRa.a3aRaRa.a3a.a.a.a.aRa2aaaaaa....xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx...aaaaaa3a3a.a.a.a3aRa2aRa3a.a.aRaRa.aaaaa...xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx...aa2aRa3a3a3aRa.a3a.a.a.a.a.a.a.a3a.aaa...xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx...aaa.a.a.a2a.aaa.aRaRa2a.a2a3a.a2aaaa..T..xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx.....a2a.a2a.aRaaaaa3a.a.aaa3a3a3a3a.a.........xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx.4...aaRRaa.a2a.a3a3a3a.aaa.a.aRa.a.aa..4.......xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx......a.a.aaa.a3a.a.a.a.aaa2a.a2a.a.aRaa.....4...xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx.....aa3a2aaa.a.a.a3a3a3a3aRaaa.a2a.a2aa........xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx...aaaa.a2aRa.a.a2aaa.a.a.a.aaa.a.aaaa.....xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx..aaa.a.a.a.a.a.a.aaa2a.a3a2a.a2aaa.....xxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxx.aaaa3a.a2aRa.a.aaaRa.a.aa.a.aaa....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxx...aaaaRa.a3a3a.a.a.aaa.aa.aa....4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxx........aa.a2a.a.aaa2aa.aa.aaa....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxx....4.....a.a2a2a.a2a.a2a.......4.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx.............a.a.a.a.a.a.....4....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx..............4..a.a.a......4...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx.................a.a.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx........................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx.....4...T............xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx.......................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx.........................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx.................T.........xxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx.......4.....................xxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx..............xx...............xxxxxx....xxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx............xxxxx........4......xxxx..4....xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx..T..........xxx................xxxxx...T.xxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx............xxx........T.........xxx........xxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx....4........xx....................x..........xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx...............x.x...xxx...............xx.xxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx.........4...........xxx..................xxxxxxxxxxxxxxxxxxaaaaaxxxxx +xxxxxxxxx.....4.....................4......4...........4...xxxxxxxxxxaa5a5aaxxxx +xxxxxxxxx.................................................wwwwwwwwxxxa5*|*5axxxx +xxxxxxxxx............x...x...T.....xxxx.................wwwwwwwwwwwwxaa*|*aaxxxx +xxxxxxxxxx.........xx.............xxxxx................wwwwwwwwwwwwwwxaa5aaxxxxx +xxxxxxxxxxx.......x..................xxx....4..........wwwwwwwwwwwwwwwxa5axxxxxx +xxxxxxxxxxx.....xxx...4...........................xxxx.4wwwwwwwwwwwwwwwa=axxxxxx +xxxxxxxxxxxx..xxx.............xx....(.........xxxxxxxx....wwwwwwwwwwwwwwaaxxxxxx +xxxxxxxxxxxxxxxx.............xxxx..................xxxx......wwwwwwwwwwxxxxxxxxx +xxxxxxxxxxxxxxxxx....{..}..xxxxxx..]......xxx...........4.wwwwwwwwwwwwxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxx........xxx........xxxxxx....4....wwwwwwwwwwwwwwxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx..[.xxx........xxx)....wwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: queen bee, killer bee, killer bee larva, plant, yellow wasp +MONS: random, random + +############################################################################ +# Vault:8 last level of the vaults -- dungeon.cc will change all these 'x's +# + +NAME: vaults_vault +PLACE: Vault:8 +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxxxx....xxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx +xxxxxxxxx..x.........................x....xxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx +xxxxxxxxx..x.xxxxxxxxxxx..xxxxxxxxxx.x....xxxxx.................xxxxx..xxxxxxxxx +xxxxxxxxx..x.x*.*.*.*.*x..x........x.x....xxx..........8..........xxx..xxxxxxxxx +xxxxxxxxx..x.x.*.*.*.*.x..x........x.x....xxx.....................xxx..xxxxxxxxx +xxxxxxxxx..x.x*.*.*.*.*x..x...||...x.x....xx......9........9.......xx..xxxxxxxxx +xxxxxxxxx..x.x.*.*.*.*.x..x...||...x.x....xx.......................xx..xxxxxxxxx +xxxxxxxxx..x.x*.*.*.*.*x..x...||...x.x....xx......xxxxxxxxxxx......xx..xxxxxxxxx +xxxxxxxxx..x.x.*.*.*.*.x..x........x.x....xx......x.........x......xx..xxxxxxxxx +xxxxxxxxx..x.x*.*.*.*.*+..+........x.x....xx....xxx..........xx....xx..xxxxxxxxx +xxxxxxxxx..x.x.*.*.*.*.xxxxxxxxxxxxx.x....xx.9..x....xxxxx....x..8.xx..xxxxxxxxx +xxxxxxxxx..x.xxxxxxxxxxx9998.........x....xx....x...xx$$$xx...x....xx..xxxxxxxxx +xxxxxxxxx..x...........899xxxxxxxxxx.x....xx....x..xx$***$xx..x....xx..xxxxxxxxx +xxxxxxxxx..x.xxxxxxxxxxx99x........x.x....xx....x..x$$*|*$$x..x....xx..xxxxxxxxx +xxxxxxxxx..x.x.....|...x88x.$$$$$$.x.x....xx..8.x..xx$***$xx..x....xx..xxxxxxxxx +xxxxxxxxx..x.x.|..|..|.x..x.$$$$$$.x.x....xx....x....x$$$xx...x..9.xx..xxxxxxxxx +xxxxxxxxx..x.x.........x..x.$$$$$$.x.x....xx....xxx..xxxxx..xxx....xx..xxxxxxxxx +xxxxxxxxx..x.x.|..|..|.x..x.$$$$$$.x.x....xx......x.........x......xx..xxxxxxxxx +xxxxxxxxx..x.x.........x..x.$$$$$$.x.x....xx......xxxxxxxxxxx......xx..xxxxxxxxx +xxxxxxxxx..x.x|..|..|..x..x.$$$$$$.x.x....xxx.....................xxx..xxxxxxxxx +xxxxxxxxx..x.x.........x..+........x.x....xxx......9.........9....xxx..xxxxxxxxx +xxxxxxxxx..x.xxxxxxxxx+x..xxxxxxxxxx.xx.11....xx................xxxxx..xxxxxxxxx +xxxxxxxxx..x...........x..x...........x1111...xxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx +xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxx..1....1..xxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx +xxxxxxxxx..........................xx1..(}..1..........................xxxxxxxxx +xxxxxxxxx...........................11.[..{.11.........................xxxxxxxxx +xxxxxxxxx............................1..])..1..........................xxxxxxxxx +xxxxxxxxx.............................1....1...........................xxxxxxxxx +xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxx.....1111..x.xxx.xxxxxxxxxxxxxxxxxx..xxxxxxxxx +xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.....11..........................x..xxxxxxxxx +xxxxxxxxx..x.x.x.x.x.x.x.x|x.x.x.x.x................................x..xxxxxxxxx +xxxxxxxxx..xx.x|x.x.x.x.x.x.x.x.x.x.x.....x.........................x..xxxxxxxxx +xxxxxxxxx..x.x.x.x.x.x.x.x9x.x.x.x.x.x..........8..........9........x..xxxxxxxxx +xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.xx....x..9......................x..xxxxxxxxx +xxxxxxxxx..x.x.x.x.x.x.x.x.x.x|x.x.x.x....x.........................x..xxxxxxxxx +xxxxxxxxx..xx.x8x.x.x|x.x.x.x.x.x.x.xx....x..............9...9......x..xxxxxxxxx +xxxxxxxxx..x.x.x.x.x9x.x.x.x.x.x.x.x.x...........8..................x..xxxxxxxxx +xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.xx....x..9......................x..xxxxxxxxx +xxxxxxxxx..x.x.x.x.x.x.x.x.x|x.x9x.x.x....x.........................x..xxxxxxxxx +xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.xx....x...................9.....x..xxxxxxxxx +xxxxxxxxx..x.x.x9x.x.x.x.x.x.x.x.x.x.x....x....9......8.............x..xxxxxxxxx +xxxxxxxxx..xx.x.x.x.x.x.x9x.x.x.x.x.xx....x.........................x..xxxxxxxxx +xxxxxxxxx..x.x.x.x.x.x.x.x.x.x.x.x.x.x....x.........................x..xxxxxxxxx +xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.xx....x.......9......9..........x..xxxxxxxxx +xxxxxxxxx..x.x.x.x.x.x.x.x.x.x8x.x.x.x....x.....................8...x..xxxxxxxxx +xxxxxxxxx..xx.x8x.x.x.x.x.x.x.x.x.x.xx....x.....................||.|x..xxxxxxxxx +xxxxxxxxx..x.x|x.x.x.x.x.x.x|x.x.x.x.x....x.....................|...x..xxxxxxxxx +xxxxxxxxx..xx.x.x.x.x.x.x8x.x.x.x.x.xx....x......8..................x..xxxxxxxxx +xxxxxxxxx..x.x.x.x.x.x.x.x.x.x.x.x.x.x....x..........8..8...8.....||x..xxxxxxxxx +xxxxxxxxx..xO.x.x.x.x.x.x.x.x.x.x|x.xx....x.....................|.||x..xxxxxxxxx +xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxxxx....xxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: vault guard +MONS: random, random, random, random, random, random + +############################################################################ +# snake_pit + +NAME: snake_pit +PLACE: Snake:5 +ORIENT: southwest + +# Rotation makes this look really bad if the console font is not square. +FLAGS: no_rotate + +# Hey, this looks a bit like a face ... + +MAP +xxxxxxxxxxxxxxxxxxxxxxx..@.xxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxx.............xxxxxxxx +xxxxxxxxxxxxxx....x.............x..xxxxx +xxxxxxxxxxxx....2.x.............x.2..xxx +xxxxxxxxxxx.....2.x....x.....x..x..3.xxx +xxxxxxxxxxx.....22x.............xx.2..xx +xxxxxxxxxxx.......xx..x........xx..3..xx +xxxxxxxxxx.....x23.xx....T...xxx.44...xx +xxxxxxxxxx......4.4.x.........x.333....x +xxxxxxxxxx......3.x4...x.......4x4.....x +xxxxxxxxxx.......3.......x.............x +xxxxxxxxxx..c......3.........x.......c.x +xxxxxxxxx...cc...................3..cc.x +xxxxxxxxx...cc..........4.4.........cc.x +xxxxxxxxx...cc...3...x........2.....cc.x +xxxxxxxxx...cc.........1...1.......cc..x +xxxxxxxxxx..cc.....1.....1.....1..ccc.xx +xxxxxxxxxx...ccc..................cc..xx +xxxxxxxxxx....cccc....3333333.....cc..xx +xxxxxxxxxx.....ccccccc...........cc...xx +xxxxxxxxxx........cccccccO...ccccc....xx +xxxxxxxxxxx........cccccccccccccc....xxx +xxxxxxxxxxx.........cccccccccccc.....xxx +xxxxxxxxxxxxx.......................xxxx +xxxxxxxxxxxxxxxx..................xxxxxx +xxxxxxxxxxxxxxxxxxxxx.......xxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: greater naga, naga, naga mage, naga warrior +MONS: random, random, random + +############################################################################ +# elf_hall + +NAME: elf_hall +PLACE: Elf:7 +ORIENT: northwest +FLAGS: no_rotate + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxcccccccccccccccccxxxxx +xxxxxxxxxxxxxxxxxcc*|*|*|**|||||c$ccxxxx +xxxxxxxxxxxxxxxxcc*$*|*|*|*|||||c$$ccxxx +xxxxxxxxxxxxxxxcc*$|*$***$$|||||c|$$ccxx +xxxxxxxxxxxxxxcc*$*|**ccccccccccc$$$$ccx +xxxxxxxxxxxxxxc*|*$*$ccc.....2..c+$|$$cx +xxxxxxxxxxxxxxc$*$*ccc...........c$$$$cx +xxxxxxxxxxxxxxc||**cc...5.......4cc$|$cx +xxxxxxxxxxxxxxc*$$cc........3..ccccccccx +xxxxxxxxxxxxxxc$+ccc.....2....cc.....5cx +xxxxxxxxxxxxxxc$c....5.......cc.......cx +xxxxxxxxxxxxxxccc......5....cc..2....ccx +xxxxxxxxxxxxxxxxc..........cc.......ccxx +xxxxxxxxxxxxxxxcc..1..U..........4..ccxx +xxxxxxxxxxxxxxcc.....................ccx +xxxxxxxxxxxxxxc...........3...........cx +xxxxxxxxxxxxxxc.......2.......3.......cx +xxxxxxxxxxxxxxc..2................2..5cx +xxxxxxxxxxxxxxc......x.........x......cx +xxxxxxxxxxxxxxc.....xx.........xx.....cx +xxxxxxxxxxxxxxc2...xxx....1....xxx.4..cx +xxxxxxxxxxxxxxc..xxxx...........xxxx..cx +xxxxxxxxxxxxxxc.xxx.....cc.cc.....xxx.cx +xxxxxxxxxxxxxxc.x.....cccc.cccc.....x.cx +xxxxxxxxxxxxxxc.3...cccxxc.cxxccc.3...cx +xxxxxxxxxxxxxxc...cccxxxxc.cxxxxccc...cx +xxxxxxxxxxxxxxc.cccxxxxxxc.cxxxxxxccc.cx +xxxxxxxxxxxxxxcccxxxxxxxxc.cxxxxxxxxcccx +xxxxxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxx +ENDMAP + +MONS: deep elf high priest, deep elf demonologist +MONS: deep elf annihilator, deep elf sorcerer +MONS: deep elf death mage +MONS: random, random + +############################################################################## +# slime_pit + +NAME: slime_pit +PLACE: Slime:$ +ORIENT: encompass + +# Slime pit take is reduced pending an increase in difficulty of this +# subdungeon. -- bwr + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxx....................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxx......................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxx..........................x.xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx.............................xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx.................................xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx..................................xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxx....(................................xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxx..........................................xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxx..........................................xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxx............................................xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxx............................................xxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxx.....................ccc..ccc............]......xxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxx...................cccc2ccccc...................xxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxx...................cc*cc..cc*cc....................xxxxxxxxxxxxxxx +xxxxxxxxxxxxxx..................cc***cc4c***cc..................xxxxxxxxxxxxxxxx +xxxxxxxxxxxxx..................cc*|*cc..cc*|*cc..................xxxxxxxxxxxxxxx +xxxxxxxxxxxxx.................cc*|P|*c4cc*|P|*cc.................xxxxxxxxxxxxxxx +xxxxxxxxxxxxx.................cc**|*cc..cc*|**cc....................xxxxxxxxxxxx +xxxxxxxxxxxx..................ccc**c|cc4c|c**ccc...................xxxxxxxxxxxxx +xxxxxxxxxxxx..................cccccccc..cccccccc....................xxxxxxxxxxxx +xxxxxxxxxxx...................c.4.c.4.1..4.c.4.c.....................xxxxxxxxxxx +xxxxxxxxxxx...................2.c.4.c..3.c.4.c.2.....................xxxxxxxxxxx +xxxxxxxxxxx..........)........cccccccc..cccccccc.....................xxxxxxxxxxx +xxxxxxxxxxx...................ccc**c|cc4c|c**ccc.....................xxxxxxxxxxx +xxxxxxxxxx....................cc**|*cc..cc*|**cc....................xxxxxxxxxxxx +xxxxxxxxxx....................cc*|P|*c4cc*|P|*cc....................xxxxxxxxxxxx +xxxxxxxxxx.....................cc*|*cc..cc*|*cc....................xxxxxxxxxxxxx +xxxxxxxxxxx.....................cc***cc4c***cc.....................xxxxxxxxxxxxx +xxxxxxxxxxxx.....................cc*cc..cc*cc......................xxxxxxxxxxxxx +xxxxxxxxxxxxx.....................cccc2ccccc......................xxxxxxxxxxxxxx +xxxxxxxxxxxxxx.....................ccc..ccc.......................xxxxxxxxxxxxxx +xxxxxxxxxxxxxx...........................................[.........xxxxxxxxxxxxx +xxxxxxxxxxxxx......................................................xxxxxxxxxxxxx +xxxxxxxxxxxxx..............................................xxxxx...xxxxxxxxxxxxx +xxxxxxxxxxxxxx...........................................xxxxxxxx.xxxxxxxxxxxxxx +xxxxxxxxxxxxxx..........................................xxxxxxxxx.xxxxxxxxxxxxxx +xxxxxxxxxxxxxxxx........................................xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxx.........................................xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxx.......................................xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxx.....................................xxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxx.............................}......xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx.................................xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxx...........{........xxx..xxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: royal jelly, acid blob, great orb of eyes +MONS: random, random, random, random + + +############################################################################## +# The Hall of Blades + +NAME: hall_of_blades +PLACE: Blade +ORIENT: north + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccxxxxxxxx +xxxxxxxxccc....cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.....cccxxxxxxxx +xxxxxxxxcc......cc...cc...cc...cc...cc...cc...cc...cc...cc...cc.......ccxxxxxxxx +xxxxxxxxc..............................................................cxxxxxxxx +xxxxxxxxc..........c..............c..............c..............c......cxxxxxxxx +xxxxxxxxc.........ccc............ccc............ccc............ccc.....cxxxxxxxx +xxxxxxxxc........ccccc..........ccccc..........ccccc..........ccccc....cxxxxxxxx +xxxxxxxxc.........ccc............ccc............ccc...........ccccc....cxxxxxxxx +xxxxxxxxc..........c..............c..............c.............ccc.....cxxxxxxxx +xxxxxxxxc......................................................ccc.....cxxxxxxxx +xxxxxxxxc.......................................................c......cxxxxxxxx +xxxxxxxxc..............................................................cxxxxxxxx +xxxxxxxxc..............................................................cxxxxxxxx +xxxxxxxxc..............................................................cxxxxxxxx +xxxxxxxxc..............................................................cxxxxxxxx +xxxxxxxxc..............................................................cxxxxxxxx +xxxxxxxxc..............................................................cxxxxxxxx +xxxxxxxxc.......................................................c......cxxxxxxxx +xxxxxxxxc......................................................ccc.....cxxxxxxxx +xxxxxxxxc..........c..............c..............c.............ccc.....cxxxxxxxx +xxxxxxxxc.........ccc............ccc............ccc...........ccccc....cxxxxxxxx +xxxxxxxxc........ccccc..........ccccc..........ccccc..........ccccc....cxxxxxxxx +xxxxxxxxc.........ccc............ccc............ccc............ccc.....cxxxxxxxx +xxxxxxxxc..........c..............c..............c..............c......cxxxxxxxx +xxxxxxxxc..............................................................cxxxxxxxx +xxxxxxxxc.......cc...cc...cc...cc...cc...cc...cc...cc...cc...cc.......ccxxxxxxxx +xxxxxxxxcc.....cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.....cccxxxxxxxx +xxxxxxxxccc...ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccxxxxxxxx +xxxxxxxxcccc.............................cccccccccccccccccccccccccccccccxxxxxxxx +xxxxxxxxcccccccccccccccccccccccccccccc.@.cccccccccccccccccccccccccccccccxxxxxxxx +ENDMAP + +MONS: dancing weapon +MONS: random, random, random, random, random, random + +############################################################################## +# hall_of_Zot + +NAME: hall_of_Zot +PLACE: Zot:5 +ORIENT: north + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccccccccccccccxxxxxxxxxxxxxxxxxxxxxxxccccccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxcccc..............ccccxxxxxxxxxxxxxxxxxcccc............ccccxxxxxxxxxx +xxxxxxxxxxcc....................cccxxxxxxxxxxxxxccc..................ccxxxxxxxxx +xxxxxxxxxcc...........3...........ccxxxxxxxxxxxcc...........3.........ccxxxxxxxx +xxxxxxxxxc..8......................cXXXXXXXXXXXc....................8..cxxxxxxxx +xxxxxxxxxc...................8.....XXX...1...XXX....8..................cxxxxxxxx +xxxxxxxxxcc........................XX..1...1..XX......................ccxxxxxxxx +xxxxxxxxxxcc.......................X1.........1X.....................ccxxxxxxxxx +xxxxxxxxxxxcc.....4....2..............1..Z..1............2....4.....ccxxxxxxxxxx +xxxxxxxxxxcc.......................X1.........1X.....................ccxxxxxxxxx +xxxxxxxxxcc........................XX..1...1..XX......................ccxxxxxxxx +xxxxxxxxxc...................8.....XXX...1...XXX....8..................cxxxxxxxx +xxxxxxxxxc...8.....................cXXXXXXXXXXXc...................8...cxxxxxxxx +xxxxxxxxxcc..........8............ccccccccccccccc..........8..........ccxxxxxxxx +xxxxxxxxxxcc....................ccccccccccccccccccc..................ccxxxxxxxxx +xxxxxxxxxxxcc................ccccccccccccccccccccccccc..............ccxxxxxxxxxx +xxxxxxxxxxxxccF111FcccccccccccccccccccccccccccccccccccccccccccF111Fccxxxxxxxxxxx +xxxxxxxxxxxcc................^^1.ccccccccccccccccc.1^^..............ccxxxxxxxxxx +xxxxxxxxxxcc.................cc1...ccccccccccccc...1cc...............ccxxxxxxxxx +xxxxxxxxxcc.............8.....ccc...ccccccccccc...ccc...8.............ccxxxxxxxx +xxxxxxxxxc....8................ccc...............ccc...........8...8...cxxxxxxxx +xxxxxxxxxc.......8.....8...8...cxcc.............ccxc...................cxxxxxxxx +xxxxxxxxxc.....................cxxc.............cxxc.......8...........cxxxxxxxx +xxxxxxxxxc.....................cxxcc.1...1...1.ccxxc............8....8.cxxxxxxxx +xxxxxxxxxc.......8....8.....8..cxxxc...........cxxxc....8..............cxxxxxxxx +xxxxxxxxxc.....................cxxcc...........ccxxc..........8........cxxxxxxxx +xxxxxxxxxcc...5...............ccxxc.............cxxcc..............8..ccxxxxxxxx +xxxxxxxxxxcc........8........ccxxcc.............ccxxcc....8....5.....ccxxxxxxxxx +xxxxxxxxxxxcc...............ccxxxc...............cxxxcc.............ccxxxxxxxxxx +xxxxxxxxxxxxcccccccccccccccccxxxxcccccccc@ccccccccxxxxcccccccccccccccxxxxxxxxxxx +ENDMAP + +MONS: Orb Guardian, Killer Klown, electric golem, orb of fire +MONS: ancient lich +MONS: random, random + + +########################################################################## +# Ecumenical temple + +NAME: temple +PLACE: Temple +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxcc............<............cxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxcc...........................cxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxcc.............................cxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxcc...............................cxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxcc.................................cxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxcc...................................cxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxcc.....................................cxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxcc.......................................cxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxcc.........................................cxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxcc...........................................cxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxcc.............................................cxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc...............................................cxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxcc.................................................cxxxxxxxxxxxxxxx +xxxxxxxxxxxxcc...................................................cxxxxxxxxxxxxxx +xxxxxxxxxxxcc..........................B..........................cxxxxxxxxxxxxx +xxxxxxxxxxcc.......................................................cxxxxxxxxxxxx +xxxxxxxxxcc.....................B.............B.....................cxxxxxxxxxxx +xxxxxxxxcc...........................................................cxxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.................B.........................B.................cxxxxxxxxx +xxxxxxxxc..............................T..............................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc..............B...............................B..............cxxxxxxxxx +xxxxxxxxc(....................T.................T....................{cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc.............................................................cxxxxxxxxx +xxxxxxxxc................B...........................B................cxxxxxxxxx +xxxxxxxxcc...........................................................ccxxxxxxxxx +xxxxxxxxxcc............................T............................ccxxxxxxxxxx +xxxxxxxxxxcc.......................................................ccxxxxxxxxxxx +xxxxxxxxxxxcc.....................................................ccxxxxxxxxxxxx +xxxxxxxxxxxxcc...................................................ccxxxxxxxxxxxxx +xxxxxxxxxxxxxcc.................................................ccxxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc...............B................B..............ccxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxcc.............................................ccxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxcc.....................B.....................ccxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxcc.........................................ccxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxcc.......................................ccxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxcc.....................................ccxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxcc...................................ccxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxcc.................................ccxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxcc...............................ccxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxcc.............................ccxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxcc...........................ccxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxcc............[............ccxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +############################################################################## +# Tomb:1 + +NAME: tomb_1 +PLACE: Tomb:1 +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxx(.............................[..............................{xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..........ccccccccccccccccccccccccccccccccccccccccccc.........xxxxxxxxx +xxxxxxxxx..........ccccccccccccccccccccccccccccccccccccccccccc.........xxxxxxxxx +xxxxxxxxx..........cc..........................^............cc.........xxxxxxxxx +xxxxxxxxx..........cc.........^....................^........cc.........xxxxxxxxx +xxxxxxxxx..........cc..ccccccccccccccccccccccccccccccccccc..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c....^....^..c................c.^)c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..ccccccccc.c..3.............c.^.c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..c222c111c.c...............5c..^c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..c2c222c.^.c......2.........+cccc..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..ccccccccccc..........3......5..c.^cc.........xxxxxxxxx +xxxxxxxxx..........cc..c.................................c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..........................3......c..cc.........xxxxxxxxx +xxxxxxxxx..........cc^.cccccccccccccc.......2............c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c............c....................c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c............c.................3..c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..cccccccc..c..........2.........c^5cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..c.^.c11c..c....................c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..c.c.c11c..c...3................c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..c^c.11cc..c..............2.....c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..c.cccccc..c.......2............c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..c..^..^...c.................2..c..cc.........xxxxxxxxx +xxxxxxxxx..........cc5^c..ccccccccccc....................c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c.................................c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c.................................c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..cccccccccccccc..^.^..cccccccccccccc..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c...........ccc+++++ccc........^..c.^cc.........xxxxxxxxx +xxxxxxxxx..........cc..c.^.....^...cc.2...2.cc......^....c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..ccccccc..cc.F...F.cc..ccccccc..c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..cc.322c..cc.......cc..c22..cc..c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..c].c22c..cc.......cc..c22c.}c^.c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c..cccc..c.^cc.G...G.cc..c3.cccc..c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c.....^..c..cc.......cc.^c........c..cc.........xxxxxxxxx +xxxxxxxxx..........cc..c........c..cc.......cc..c....^...c..cc.........xxxxxxxxx +xxxxxxxxx..........cc^.cccccccccc..cc.G...G.cc..cccccccccc.^cc.........xxxxxxxxx +xxxxxxxxx..........cc......^.......cc.......cc..........^...cc.........xxxxxxxxx +xxxxxxxxx..........cc...........^..cc.......cc.....^........cc.........xxxxxxxxx +xxxxxxxxx..........cccccccccccccccccc.G...G.cccccccccccccccccc.........xxxxxxxxx +xxxxxxxxx..........cccccccccccccccccc.......cccccccccccccccccc.........xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx.............................G...G............................xxxxxxxxx +xxxxxxxxx...........................4.......4..........................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx...........................4..V.V..4..........................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx...........................4.......4..........................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: mummy, guardian mummy, mummy priest, sphinx, greater mummy +MONS: random, random + +############################################################################### +# Tomb:2 + +NAME: tomb_2 +PLACE: Tomb:2 + +# Can be rotated! +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc{...c......c.....3....c........c.......ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c.....^c^........^c......2^c.......ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c...2.^+..2.....2^+^..2....+.......ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc.3.^c^.....c^.........c^2.....^c^......ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc...^+^.....c..........c........c...2...ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccc+ccccccccccccccccccccccccccccccc....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc..^.c.............................c....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c.............................c..3.ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c..ccc4.................4ccc..c....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c..ccc...................ccc..c..2.ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c..ccc.........1.........ccc..c)..}ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc.3..c..ccc.....2.......2.....ccc..cccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c.............................c....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c.............................c^2..ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c........c...........c........+....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc]...c.............................c^2..ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccccc.....3........(........3.....c....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c.............................c.^.^ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc...^c........c...........c........ccc+cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....+.............................c..^.ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc...^c.............................c....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c..ccc.....2.......2.....ccc..c..2.ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccccc..ccc.........1.........ccc..c....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c..ccc...................ccc..c....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc...^+..ccc4.................4ccc..c2...ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c.............................c....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccccc.............................c..2.ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....c.............................ccc+cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc....+cccc+ccccccccccccccc+ccccccccc.^.^ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc.1.^^.c.^..c............c^.......c.3...ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc...2..c.1..c.....1.1....c.....2..c.....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc......c....c..1......1.^c..2.....c...2.ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc..3...c.1..c...1...1..1^+........c.....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc......c....c[...........c.......3c.....ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: mummy, guardian mummy, mummy priest, greater mummy +MONS: random, random, random + +############################################################################# +# Tomb:3 + +NAME: tomb_3 +PLACE: Tomb:3 +ORIENT: encompass + +# Unless you have a square aspect ratio, Tomb:3 looks utterly lame when +# rotated. +FLAGS: no_rotate + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccccc.............................cccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccc...............cccccc..............ccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc...............cccccccc..............cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc.......4......ccccO4cccc......4......cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc............cccc......cccc...........cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc............cccc........cccc...........ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc............cccc........cccc...........ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc...........cccc..444444..cccc..........ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc.......................................ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc.......................................ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcc.................222222................ccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc................223322...............cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc...3............223322............3..cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccc...............222222..............ccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccc....2..........................2...ccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccc....2......................2....cccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccc............................cccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccccccc+ccc..................ccc+ccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccc....cc................cc....cccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccc.......cc22222222222222cc......$cccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccc....^.....cc............cc..^.....$ccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccc.^.........cc..........cc.....^.^.$ccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc$...^...^..^.cc........cc..........$$cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc$$$...........cc222222cc.^........$$$cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc|$$$...........c......c.....^...$$$$$cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc||$$$$...^.....c......c^......$$$$$$$cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccc|||||$$.....^..c......c......$$$$$$$$cccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccc|||||$........c......c...^.$$$$$$$$ccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccc||||$$..^....c......c.....$$$$$$$cccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccc||||$.......c......c.....$$$$$$ccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccccc|||$$....^.c......c.^.^$$$$$$cccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccc|||$^....cc..{...cc...$$$$$ccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxccccccccc||$.....cc...(..cc..$$$$$cccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccccc|$...cccc..[...cccc$$$$ccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: mummy, guardian mummy, mummy priest, greater mummy +MONS: random, random + +############################################################################# +# Swamp:5 + +NAME: swamp +PLACE: Swamp:5 +ORIENT: southeast + +# NB - most of the 'x's here will be set to water in dungeon.cc + +MAP +xxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx2xxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx2x.xxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxcc.ccxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxcc...ccxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxcc3.2..ccxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxcc.1.3.2.ccxxxxxxxxxxxxxxxxxxxxxxx +xxxxxccc....1.1cccxxxxxxxxxxxxxxxxxxxxxx +xxxxxcc.1.32....ccxxxxxxxxxxxxxxxxxxxxxx +xxxxxcc...3..1.3ccxxxxxxxxxxxxxxxxxxxxxx +xxxxxcc2.1.3..2.ccxxxxxxxxxxxxxxxxxxxxxx +xxxxxccc33..1..cccxxxxxxxxxxxxxxxxxxxxxx +xxxxxxcccc3O3ccccxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxcccccccccxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxcccccccxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxcccxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: swamp dragon, swamp drake, hydra +MONS: random, random, random, random diff --git a/crawl-ref/source/dat/vaults.des b/crawl-ref/source/dat/vaults.des new file mode 100644 index 0000000000..0cc79560ab --- /dev/null +++ b/crawl-ref/source/dat/vaults.des @@ -0,0 +1,1689 @@ +############################################################################## +# Random vaults and minivaults; special levels (including V:8!) should go to +# splev.des. Do NOT put special levels here, or they could be selected when the +# game looks for random vaults. +# +# key: +# x - DNGN_ROCK_WALL +# X - DNGN_PERMAROCK_WALL -> should always be undiggable! -- bwr +# c - DNGN_STONE_WALL +# v - DNGN_METAL_WALL +# b - DNGN_GREEN_CRYSTAL_WALL +# a - DNGN_WAX_WALL +# . - DNGN_FLOOR +# + - DNGN_CLOSED_DOOR +# = - DNGN_SECRET_DOOR +# @ - entry point - must be on outside and on a particular side - see templates +# w - water +# l - lava +# >< - extra stairs - you can leave level by these but will never be placed on +# them from another level +# }{ - stairs 82/86 - You must be able to reach these from each other +# )( - stairs 83/87 +# ][ - stairs 84/88 +# I - orcish idol (does nothing) +# ^ - random trap +# +# A - Vestibule gateway (opened by Horn). Can also be put on other levels for +# colour, where it won't do anything. +# B - Altar. These are assigned specific types (eg of Zin etc) in dungeon.cc, +# in order. +# C - Random Altar. +# F - Typically a Granite Statue, but may be Orange or Silver (1 in 100) +# G - Granite statue (does nothing) +# H - orange crystal statue (attacks mind) +# S - Silver statue (summons demons). Avoid using (rare). +# T - Water fountain +# U - Magic fountain +# V - Permanently dry fountain +# +# Statues can't be walked over and are only destroyed by disintegration +# +# $ - gold +# % - normal item +# * - higher level item (good) +# | - acquirement-level item (almost guaranteed excellent) +# O - place an appropriate rune here +# P - maybe place a rune here (50%) +# R - honeycomb (2/3) or royal jelly (1/3) +# Z - the Orb of Zot +# +# 0 - normal monster +# 9 - +5 depth monster +# 8 - (+2) * 2 depth monster (aargh!). Can get golden dragons and titans +# this way. +# 1-7 - monster array monster +# used to allocate specific monsters for a vault. +# is filled with RANDOM_MONSTER if monster not specified +# +# note that a lot of the vaults are in there mainly to add some interest to the +# scenery, and are not the lethal treasure-fests you find in Angband +# (not that there's anything wrong with that) +# +# Guidelines for creating new vault maps: +# +# [ds] If your map is not a minivault, make sure the side(s) that form the +# border have a rock wall padding at least 6 deep. For instance, if your map +# is ORIENT: north, you must have a 6 deep border of rock wall (or any +# other kind of wall) along the northern, eastern, and western edges of the +# map. If you're doing a fullscreen map (encompass), you must pad all around +# the map with 6 layers of wall. +# +# Basically you can just let your creativity run wild. You do not have +# to place all of the stairs unless the level is full screen, in which +# case you must place all except the extra stairs (> and <). The <> stairs +# can be put anywhere and in any quantities but do not have to be there. Any +# of the other stairs which are not present in the vault will be randomly +# placed outside it. Also generally try to avoid rooms with no exit. +# +# You can use the templates below to build vaults. The entry point '@' must be +# present (except full-screen vaults where it must not) and be on the same +# side of the vault as it is on the template, but can be anywhere along that +# side. +# +# I think that's all. Have fun! +# +# ps - remember to add one to the monster array value when placing monsters +# on each map (it is 1-7, not 0-6) {dlb} +# +# [dshaligram] All vaults MUST have an ORIENT: attribute; if there's no +# ORIENT: attribute, the vault is considered to be a minivault, which is +# usually not what you want. +# +############################################################################## + +default-depth: 1-27 + +NAME: vault_1 +ORIENT: north + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx....x........x........x.................................xxxxxxxxxxxx +xxxxxxxxxx|=8...x........+........x......x....x1...x2...x2...x3...x...xxxxxxxxxx +xxxxxxxxxx|x....x........x........x....................................xxxxxxxxx +xxxxxxxxxxxxxxxx+xxx+xxxxxxxxxxxxxx..................................xxxxxxxxxxx +xxxxxxxxx.......x.................+...................................8xxxxxxxxx +xxxxxxxxx.......x.................x..................................xxxxxxxxxxx +xxxxxxxxx.......+........3........xx+xx................................xxxxxxxxx +xxxxxxxxx.......x.................x...x..x....x1...x2...x2...x3...x...xxxxxxxxxx +xxxxxxxxx.......x.................x...x.............................xxxxxxxxxxxx +xxxxxxxxxx+xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxx.........................x.S.x...xxxxxx..................|||||xxxxxxxxx +xxxxxxxxx....xxxxxxxxxxxxxxxxxx...x...x......xxxxxx..................||xxxxxxxxx +xxxxxxxxx....x...$$$$x****.999x...x...x.........xxxxxx.................xxxxxxxxx +xxxxxxxxx....+...$$$$x****....x...x...+............xxxxxx.........8....xxxxxxxxx +xxxxxxxxx....x...$$$$x****....+...x...x...............xxxxxx...........xxxxxxxxx +xxxxxxxxx....x...$$$$x****....x...x999x..................xxxxxx........xxxxxxxxx +xxxxxxxxx....xxxxxxxxxxxxxxxxxx...x...xxx...................xxxxxx.....xxxxxxxxx +xxxxxxxxx.........................x...xxxxxx...................xxxxxx..xxxxxxxxx +xxxxxxxxxxxxx+xxxxxxxx+xxxxxxx+xxxx...xxxxxx+xxxxxxxx+xxxxxxxx+xxxxxxx=xxxxxxxxx +xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx +xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx +xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx +xxxxxxxxx....1....x...2...x...3...x...x....3....x....2...x......1......xxxxxxxxx +xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx +xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx +xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: shapeshifter, shapeshifter, glowing shapeshifter + +############################################################################## +# A cell vault + +NAME: vault_2 +ORIENT: northwest + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxcccccccccccccccccccccccccccccccc +xxxxxxxxccw......^......w......^......wc +xxxxxxxxcc.ccccccccccccc.ccccccccccccc.c +xxxxxxxxcc.c....c.c....c.c....c.c....c.c +xxxxxxxxcc.c.8..+.c....c.c....+.c..9.c.c +xxxxxxxxcc.c....c.+..9.c.c.9..c.+....c.c +xxxxxxxxcc.c....c.c....c.c....c.c....c.c +xxxxxxxxcc.cccccc.cccccc.cccccc.cccccc.c +xxxxxxxxcc^c....c.c....c.c....c.c....c.c +xxxxxxxxcc.c....c.c....c.c....+.c....c.c +xxxxxxxxcc.c8...+.+..8.c.c.8..c.+....c.c +xxxxxxxxcc.c....c.c....c.c....c.c....c.c +xxxxxxxxcc.cccccc.cccccc.cccccc.cccccc.c +xxxxxxxxcc.c....c.c....c.c....c.c....c.c +xxxxxxxxcc.c....+.c....c.c.0..c.c....c.c +xxxxxxxxcc.c..9.c.+.8..c^c....+.+.0..c.c +xxxxxxxxcc.c....c.c....c.c....c.c....c.c +xxxxxxxxcc.cccccc.cccccc.cccccc.cccccc.c +xxxxxxxxcc.c....c.c....c.c....c.c....c.c +xxxxxxxxcc.c.0..+.+.0..c.c....+.+....c.c +xxxxxxxxcc.c....c.c....c.c.0..c.c.8..c.c +xxxxxxxxcc.cccccc.c....c.c....c.cccccc.c +xxxxxxxxcc.c....c.cccccc.cccccc.c....c^c +xxxxxxxxcc.c....c.c....c.c..9.+.+....c.c +xxxxxxxxcc.c.0..+.+....c.c9...c.c.0..c.c +xxxxxxxxcc.c....c.c.8..c.c....c.c....c.c +xxxxxxxxcc.cccccc^cccccc.cccccc^cccccc.c +xxxxxxxxccw.......Twwwwc.cwwwwT.......wc +xxxxxxxxcccccccccccccccc.ccccccccccccccc +xxxxxxxxxxxxxxxxxxxxxxxc@cxxxxxxxxxxxxxx +ENDMAP + +############################################################################## +# A little maze vault + +NAME: vault_3 +ORIENT: northeast + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +x900x..............x..........xxxxxxxxxx +x999x.xxxxxxxxxxxx.x.xxxxxxxx.xxxxxxxxxx +x000x.x............x.x......x.xxxxxxxxxx +xx.xx.xxxxxxxxxxxxxx.x.xxxx.x.xxxxxxxxxx +xx.x..............xx.x.88|x.x.xxxxxxxxxx +xx.x.x.xxxxxxxxxx.xx.xxxxxx.x.xxxxxxxxxx +xx.x.x.x........x...........x.xxxxxxxxxx +xx.x.x.x.xxxxxx.xxxxxxxxxxxxx.xxxxxxxxxx +xx.xxx.x.x$$$$x...............xxxxxxxxxx +xx.....x.x$$$$x.xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxx.x$$$$x...............xxxxxxxxxx +x........x$$$$x.xxxxxxxxxxxxx.xxxxxxxxxx +x.xxxxxx.xxxx.x.............x.xxxxxxxxxx +x.xxxxxx.xxxx.xxxxxxxxxxxxx.x.xxxxxxxxxx +x.x.......xxx.x...........x.x.xxxxxxxxxx +x.x.xxxxx.....x.x.xxxxx...x.x.xxxxxxxxxx +x.x.x999xxxxxxx.x.x***x...x.x.xxxxxxxxxx +x.x.x889........x.x|||xxxxx.x.xxxxxxxxxx +x.x.x899x.xxxxx.x.x***xxxxx.x.xxxxxxxxxx +x.x.xxxxx.xxxxx.x.xx.xxxxxx.x.xxxxxxxxxx +x.x..........xx.x.xx........x.xxxxxxxxxx +x.xxxxxxx.xx.xx.x.xxxxx.xxxxx.xxxxxxxxxx +x.xxx000x.xx.xx.x.x$$$x.xxxxx.xxxxxxxxxx +x|||x000x.x$$$x.x.x$$$x%%x%%%.xxxxxxxxxx +x|||x000..x$8$x.x.x$$$x%%x%8%xxxxxxxxxxx +x|||xxxxxxx$$$x.x..$$$xxxx%%%xxxxxxxxxxx +xxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +############################################################################## +# thingy vault. + +NAME: vault_4 +ORIENT: southwest +FLAGS: no_rotate + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx^xxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx.....xxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx.....xxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxx.........xxxxxxxxxx +xxxxxxxxxxxxxxxxx......0...0......xxxxxx +xxxxxxxxxxxxxx.......................xxx +xxxxxxxxxxxxxx.........0...0.........xxx +xxxxxxxxxxxxx8......0.........0......8xx +xxxxxxxxxxxxxx.........0...0.........xxx +xxxxxxxxxxxxxx.......................xxx +xxxxxxxxxxxxxxx........0...0........xxxx +xxxxxxxxxxxxxxxxxxxx...........xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx.....xxxxxxxxxxxx +xxxxxxxxxxxxxxxxxx...............xxxxxxx +xxxxxxxxxxxxxxxx8.................8xxxxx +xxxxxxxxxxxxxxxxxxx.............xxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxx999xxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + + +############################################################################## +# hourglass vault. + +NAME: vault_5 +ORIENT: southeast +FLAGS: no_rotate + +MAP +xxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxx.................xxxxxxxxxxxxxxxxx +xxxxx...................xxxxxxxxxxxxxxxx +xxxxx...................xxxxxxxxxxxxxxxx +xxxxxx.................xxxxxxxxxxxxxxxxx +xxxxxx.................xxxxxxxxxxxxxxxxx +xxxxxx.................xxxxxxxxxxxxxxxxx +xxxxxxx...............xxxxxxxxxxxxxxxxxx +xxxxxxx...............xxxxxxxxxxxxxxxxxx +xxxxxxxx.............xxxxxxxxxxxxxxxxxxx +xxxxxxxxx.....8.....xxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx...999...xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx00000xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxx===xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx.....xxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxx +xxxxxxxxx...........xxxxxxxxxxxxxxxxxxxx +xxxxxxxx......|......xxxxxxxxxxxxxxxxxxx +xxxxxxx...............xxxxxxxxxxxxxxxxxx +xxxxxxx...............xxxxxxxxxxxxxxxxxx +xxxxxx........$........xxxxxxxxxxxxxxxxx +xxxxxx.......$$$.......xxxxxxxxxxxxxxxxx +xxxxxx....$$$$$$$$$....xxxxxxxxxxxxxxxxx +xxxxx$$$$$$$$$$$$$$$$$$$xxxxxxxxxxxxxxxx +xxxxx$$$$$$$$$$$$$$$$$$$xxxxxxxxxxxxxxxx +xxxxxx$$$$$$$$$$$$$$$$$xxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +########################################################################## +# A more Angbandy vault + +NAME: vault_6 +ORIENT: northeast + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ccccccccccccccccccccccccccccccccxxxxxxxx +c*******cc..9...cc.+8c0c*c.c*c8cxxxxxxxx +c******cc..cc..cc..cc0c.c.c.c8ccxxxxxxxx +c*****cc..cc..cc..cc.c$c.c.c8c.cxxxxxxxx +c****cc9.cc..cc8.cc|c.c|c.c*c0ccxxxxxxxx +c***cc..cc..cc..cc.c.c.c.c.c.c$cxxxxxxxx +c**cc..cc8.cc..cc.c*c.c.c.c.c.ccxxxxxxxx +c+cc9.cc..cc..cc.c.c.c.c*c.c.c.cxxxxxxxx +c^c..cc..cc..cc.c$c.c.c.c.c.c*ccxxxxxxxx +c...cc..cc..cc.c.c.c9c$c.c.c.c9cxxxxxxxx +c..cc..cc..cc$c.c.c*c.c.c.c9c9ccxxxxxxxx +c.cc..cc..cc.c.c|c.c.c.c.c$c.c9cxxxxxxxx +ccc..cc..cc.c.c.c.c.c.c.c.c.cc+cxxxxxxxx +cc..cc..cc.c*c.c.c.c.c.c$c.cc..cxxxxxxxx +c0.cc..cc.c.c.c.c8c.c*c.c.cc0.ccxxxxxxxx +c.cc..cc*c.c.c.c.c$c.c.c.cc..cccxxxxxxxx +c^c..cc.c.c9c.c.c.c.c.c.cc..cc.cxxxxxxxx +c0..cc$c.c.c*c0c.c.c.c.cc..cc.0cxxxxxxxx +c..cc.c.c9c.c.c.c$c.c.cc.9cc...cxxxxxxxx +c.cc9c.c.c.c.c.c.c.c.cc..cc..c^cxxxxxxxx +ccc.c.c$c.c.c.c.c.c$cc..cc..cc^cxxxxxxxx +cc$c.c.c.c.c$c.c0c.cc..cc..cc..cxxxxxxxx +c.c.c.c.c.c.c.c.c.cc9.cc..cc..ccxxxxxxxx +cc.c8c.c.c$c.c.c.cc..cc..cc0.cccxxxxxxxx +c.c$c.c$c0c.c.c.cc..cc..cc..cc$cxxxxxxxx +cc.c.c.c.c.c*c.cc..cc..cc..cc$$cxxxxxxxx +c.c.c.c.c.c.c.cc..cc0.cc..cc$$$cxxxxxxxx +cc.c.c.c.c.c$cc..cc..cc..cc$$$$cxxxxxxxx +c.c.c.c.c.c.cc.8.^..cc....+$$$$cxxxxxxxx +cccc@cccccccccccccccccccccccccccxxxxxxxx +ENDMAP + +############################################################################ +# four-leaf vault + +NAME: vault_7 +ORIENT: northwest + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxx.........^..^.........xxxxx +xxxxxxxxxxxx...xxxxxxxx..xxxxxxxx...xxxx +xxxxxxxxxxx...xxxxxxxxx..xxxxxxxxx...xxx +xxxxxxxxxx...xx$*....xx..xx....$$xx...xx +xxxxxxxxx...xx$*$....xx..xx....$*$xx...x +xxxxxxxxx..xx*$*$....xx..xx....*$$$xx..x +xxxxxxxxx..xx$$$.00..xx..xx..00.*$*xx..x +xxxxxxxxx..xx....09..xx..xx..90....xx..x +xxxxxxxxx..xx......+xx....xx+......xx..x +xxxxxxxxx..xx......x^......^x......xx..x +xxxxxxxxx..xxxxxxxxx........xxxxxxxxx..x +xxxxxxxxx..xxxxxxxx..........xxxxxxxx..x +xxxxxxxxx..............TT..............x +xxxxxxxxx..............TT..............x +xxxxxxxxx..xxxxxxxx..........xxxxxxxx..x +xxxxxxxxx..xxxxxxxxx........xxxxxxxxx..x +xxxxxxxxx..xx......x^......^x......xx..x +xxxxxxxxx..xx......+xx....xx+......xx..x +xxxxxxxxx..xx....09..xx..xx..90....xx..x +xxxxxxxxx..xx$$*.00..xx..xx..00.*$$xx..x +xxxxxxxxx..xx*$*$....xx..xx....*$$*xx..x +xxxxxxxxx...xx*$*....xx..xx....$$$xx...x +xxxxxxxxxx...xx*$....xx..xx....*$xx...xx +xxxxxxxxxxx...xxxxxxxxx..xxxxxxxxx...xxx +xxxxxxxxxxxx...xxxxxxxx..xxxxxxxx...xxxx +xxxxxxxxxxxxx..^................^..xxxxx +xxxxxxxxxxxxxxxxxxxxxxx^^xxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx++xxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxx +ENDMAP + +############################################################################ +# Cross-vault + +NAME: vault_8 +ORIENT: northwest + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxx............xxxxxxxxxx +xxxxxxxxxxxxxxx..................xxxxxxx +xxxxxxxxxxxxx......................xxxxx +xxxxxxxxxxxx..........w..w..........xxxx +xxxxxxxxxxx........wwww++wwww........xxx +xxxxxxxxxxx......wwwvvv^^vvvwww......xxx +xxxxxxxxxx......wwwwv.9..9.vwwww......xx +xxxxxxxxxx.....wwwwwv......vwwwww.....xx +xxxxxxxxxx....wwwwwvv......vvwwwww....xx +xxxxxxxxx....wwwwwvv........vvwwwww....x +xxxxxxxxx....wwvvvv....vv....vvvvww....x +xxxxxxxxx...wwwv......vvvv......vwww...x +xxxxxxxxx...wwwv....vv8vv8vv....vwww...x +xxxxxxxxx..wwwwv...vvvv||vvvv...vwwww..x +xxxxxxxxx^^wwwwv...vvvv||vvvv...vwwww^^x +xxxxxxxxx..wwwwv....vv8vv8vv....vwwww..x +xxxxxxxxx...wwwv......vvvv......vwww...x +xxxxxxxxx...wwwvvvv....vv....vvvvwww...x +xxxxxxxxx....wwwwwvv........vvwwwww....x +xxxxxxxxxx...wwwwwwvv......vvwwwwww...xx +xxxxxxxxxx....wwwwwwv......vwwwwww....xx +xxxxxxxxxx.....wwwwwv......vwwwww.....xx +xxxxxxxxxxx.....wwwwvvvvvvvvwwww.....xxx +xxxxxxxxxxx.......wwwwwwwwwwww.......xxx +xxxxxxxxxxxx.........wwwwww.........xxxx +xxxxxxxxxxxxx.........^..^.........xxxxx +xxxxxxxxxxxxxxx.......x++x.......xxxxxxx +xxxxxxxxxxxxxxxxxx...xx..xx...xxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxx..@.xxxxxxxxxxxxxx +ENDMAP + +############################################################################ +# Another thingy vault + +NAME: vault_9 +ORIENT: southeast + +MAP +xxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxx^xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxx +xx.....^...............^.....xxxxxxxxxxx +x..bb..xxxxxxxxxxxxxxxxx..bb..xxxxxxxxxx +x..b...xxxxxxxxxxxxxxxxx...b..xxxxxxxxxx +x...b..xxxxbbbbbbbbbxxxx..b...xxxxxxxxxx +x..bb..xxbbb.......bbbxx..bb..xxxxxxxxxx +x......xxb....9.9....bxx......xxxxxxxxxx +x..bb..xbb..%$$$$$%..bbx..bb..xxxxxxxxxx +x...b..xb..0%$***$%0..bx..b...xxxxxxxxxx +x..b...xb..0%$*H*$%0..bx...b..xxxxxxxxxx +x...b..xb..0%$***$%0..bx..b...xxxxxxxxxx +x..b...xb...%$$$$$%...bx...b..xxxxxxxxxx +x...b..xbb.900000009.bbx..b...xxxxxxxxxx +x..b...xxb...........bxx...b..xxxxxxxxxx +x..bb..xxbbb..9.9..bbbxx..bb..xxxxxxxxxx +x......xxxxbbbb.bbbbxxxx......xxxxxxxxxx +x..bb..xxxxxxxb=bxxxxxxx..bb..xxxxxxxxxx +x..b...xxxxxxxx=xxxxxxxx...b..xxxxxxxxxx +x...b..xxxxxxxx^xxxxxxxx..b...xxxxxxxxxx +x..b....xxxxxxx=xxxxxxx....b..xxxxxxxxxx +x...b...^.............^...b...xxxxxxxxxx +x..b....xxxxxxxxxxxxxxx....b..xxxxxxxxxx +x..bb..xxxxxxxxxxxxxxxxx..bb..xxxxxxxxxx +xx....xxxxxxxxxxxxxxxxxxx....xxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +############################################################################ +# Impenetrable vault + +NAME: vault_10 +ORIENT: southeast + +MAP +..............@................xxxxxxxxx +...............................xxxxxxxxx +...............................xxxxxxxxx +...............................xxxxxxxxx +...............................xxxxxxxxx +.....cccccccccccccccc..........xxxxxxxxx +.....c[^...........9cc.........xxxxxxxxx +.....c^xxxxx=xxxxxx..cc........xxxxxxxxx +.....c.x9..^^^...9xx..cc.......xxxxxxxxx +.....c.x.xxx=xxxx..xx..cc......xxxxxxxxx +.....c.x^x$$$$$$xx..xx.9c......xxxxxxxxx +.....c.=^=$*|||*$xx..xx.c......xxxxxxxxx +.....c.x^xx$*|||*$xx.9x.c......xxxxxxxxx +.....c.x9.xx$*|||*$xx^x.c......xxxxxxxxx +.....c.xx..xx$*|||*$=^=.c......xxxxxxxxx +.....c9.xx..xx$$$$$$x^x.c......xxxxxxxxx +.....cc..xx..xxxx=xxx.x.c......xxxxxxxxx +......cc..xx9...^^^..9x.c......xxxxxxxxx +.......cc..xxxxxx=xxxxx^c......xxxxxxxxx +........cc9...........^]c......xxxxxxxxx +.........cccccccccccccccc......xxxxxxxxx +...............................xxxxxxxxx +...............................xxxxxxxxx +...............................xxxxxxxxx +...............................xxxxxxxxx +...............................xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +######################################################################### +# Orc temple + +NAME: orc_temple +ORIENT: southwest +FLAGS: no_rotate + +MAP +xxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxx4.4xxxxxxxxxxxxxxx +xxxxxxxxx**..........x414x..........**xx +xxxxxxxxx**..........x4.4x..........**xx +xxxxxxxxx............+...+....4.......xx +xxxxxxxxx....4..4....x...x............xx +xxxxxxxxx............x...x.......4....xx +xxxxxxxxx............xx.xx............xx +xxxxxxxxx...4......xxxx+xxxx......6...xx +xxxxxxxxx........xxx.......xxx........xx +xxxxxxxxxxx...xxxx..2.....2..xxxx...xxxx +xxxxxxxxxxxx+xxxx.............xxxx+xxxxx +xxxxxxxxxxx...xxx.............xxx...xxxx +xxxxxxxxx......x...............x......xx +xxxxxxxxx..4...x...2...I...2...x...5..xx +xxxxxxxxx......x...............x......xx +xxxxxxxxx...4..xx.............xx..5...xx +xxxxxxxxx$......x....2...2....x......$xx +xxxxxxxxx$6..5..xx.....3.....xx.5...7$xx +xxxxxxxxx$$$.....xxx.......xxx.....$$$xx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: orc warlord, orc priest, orc high priest, orc warrior, orc wizard +MONS: orc knight, orc sorcerer + +############################################################################# +# Matthew Ludivico (my_map) + +NAME: my_map +ORIENT: southwest + +# The hell hounds can be deadly at lower levels. +DEPTH: 18-27 + +MAP +xxxxxxxxxx.@.xxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxx..........................xx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..xx +xxxxxxxxx.^^..........................xx +xxxxxxxxxx.^^xx+xxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxx.^...11....xxxxxxxx..xxxxxxxx +xxxxxxxxxxxx..x.1..6..xxx........xx..xxx +xxxxxxxxxxxxx.xxxxxxxxx...vvvvv...x...xx +xxxxxxxxx6..1...x.........+1..v.......xx +xxxxxxxxx..1....x.........vvvvv........x +xxxxxxxxx..5...xx......................x +xxxxxxxxxxxxxx^++...........vvvvvvv....x +xxxxxxxxxxxxxx^xx...xx=xx...vv$%$vvvvv.x +xxxxxxxxxxxxxx^x...xxv1vxx...vvv*2...v.x +xxxxxxxxxxxxxx^x..vvvv7.vvvv...vv.vv+v^x +xxxxxxxxx..xxx^..vvvb....bvvv...vvv^...x +xxxxxxxxx%%.xx..vvvvb....bvvvv.......xxx +xxxxxxxxxx.....vvbbb......bbbvv.....xxxx +xxxxxxxxxxx....vvb....66....bvvxxxxxxxxx +xxxxxxxxxxxxxxvvvb..llllll..bvvvxxxxxxxx +xxxxxxxxxvvvvvvvvb..ll45ll..bvvvvvvvvxxx +xxxxxxxxxccc***+== .l3.2.l..cccccccccxxx +xxxxxxxxxccc+cccbb....ll....c..$$$$+$*cx +xxxxxxxxxcc|||cbb...3llll2...cc%*%*c$|cx +xxxxxxxxxcccccccbbbbbbbbbbbccccccccccccx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: hell hound, necromancer, wizard, orange demon, rotting devil +MONS: hell knight, great orb of eyes + +########################################################################### +# Farm and country (Matthew Ludivico) + +NAME: farm_and_country +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxx..........................................xxxxxxxx}.xxxxxxxxxxx +xxxxxxxxxxxxxxxxxxx............xxxxxx....xxx.......xx...........xxxx..]xxxxxxxxx +xxxxxxxxxxxxxxx***x...........xxx..xxx............xxxx...........xx..xxxxxxxxxxx +xxxxxxxxxxxxxxx|*$=...xx.xxxxxxx....xxxxxxxxxx......xx................xxxxxxxxxx +xxxxxxxxxxxxxxxxxxx....xxxxxxxx......3..xxx.................x..........xxxxxxxxx +xxxxxxxxxxxxxxxxxx......x........x......xx.........w...................xxxxxxxxx +xxxxxxxxxxx)......xx...xxx.....xxx......x........www3....3.............xxxxxxxxx +xxxxxxxxxxxx=xxxxxxxxxxx...xxxxxxxxx..xxx.....wwwww....%%%.............xxxxxxxxx +xxxxxxxxxx......xxx.......xx.xxxx.x...xxxxxxxwwwwwww..5%%%..........xx.xxxxxxxxx +xxxxxxxxx.........x..xxxxxxxx.....x........3wwwwwwwww..%%%........xxx..xxxxxxxxx +xxxxxxxxx....5...xx..x.xxxxx.....xxx........wwwwwwwww..%%%..........xx.xxxxxxxxx +xxxxxxxxxxx.....xxx..xx..xx........xxxxxxxxxwwwwwwwww..............xxx.xxxxxxxxx +xxxxxxxxxx........x..x...............xx..xxxxwwwwwwwwwwwwww............xxxxxxxxx +xxxxxxxxx.............................x.....xxwwwwww3wwwwww............xxxxxxxxx +xxxxxxxxxxx...x...........5.....7...............ww.......ww.....44....xxxxxxxxxx +xxxxxxxxxwxx..xx.....622...2.26...6.2...22.6...62..2..226ww.....44xx...xxxxxxxxx +xxxxxxxxxwwxxxx......2....2.22....2..2...2.2.......22...2ww....xxxx..xxxxxxxxxxx +xxxxxxxxxwwwwxxx......2...2.2.2...2.22..2.22...22.2.2..22ww.....xxx....xxxxxxxxx +xxxxxxxxxwwwwwx....4..2...2...........22...277..2..2.2.22ww...........xxxxxxxxxx +xxxxxxxxxwwwwwxx....42..2....22.4..2..2...2.4..2.22..22.2ww............xxxxxxxxx +xxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwww..2.........xxxxxxxxx +xxxxxxxxxwwwwwxx.....62....2.26...62.2.2..26...6...22..26..............xxxxxxxxx +xxxxxxxxxwwwww.........................................................xxxxxxxxx +xxxxxxxxxwwwwwxx....222.2.22..2.7.......7..............................xxxxxxxxx +xxxxxxxxxwwwww...........ccccccc+ccccccc...ccc......cc+ccc...xxxxx.....xxxxxxxxx +xxxxxxxxxwwwwwxx.........c$$*.c$$5$+.5.c...+5c......c%%%%c......xxx3...xxxxxxxxx +xxxxxxxxxwwwwwx....2.....c$.c+cccccc.%.c...ccc......c%%%%c....xxxxx....xxxxxxxxx +xxxxxxxxxwwwwwx..........c..c..........c............cccccc......xxx....xxxxxxxxx +xxxxxxxxxwwxxxxxxx.......ccccc+ccccccccc.........................xx....xxxxxxxxx +xxxxxxxxxwxx.....xxxx........c...c.................2...................xxxxxxxxx +xxxxxxxxxxx.........xxxx...........2....xxxx...........................xxxxxxxxx +xxxxxxxxx..............xxxx..........xxxx..x...........................xxxxxxxxx +xxxxxxxxx.................xxxxx++xxxxx.....xx............xx...x........xxxxxxxxx +xxxxxxxxx.....................c..c..........xxxxx..........xxxxx.......xxxxxxxxx +xxxxxxxxx.......cccc..........c..c...cccc......xxx...........x.........xxxxxxxxx +xxxxxxxxx.......c..c..........c++c...c..c........xxx.........x.........xxxxxxxxx +xxxxxxxxx.......c..c..........c..c...c..c..........xxx.................xxxxxxxxx +xxxxxxxxx....cccc++cccccccccccc++ccccc..ccccccc......xxx...............xxxxxxxxx +xxxxxxxxx....c..........1.....................c........xxx.............xxxxxxxxx +xxxxxxxxx.cccc.....w....w....%1.....w.....%...c..........xxx...........xxxxxxxxx +xxxxxxxxx.c1.+....www..www..%%%....www...%%%1.c...........xxxxxxxxx....xxxxxxxxx +xxxxxxxxx.cccc.....w....w....%......w.....%...c..................xxx...xxxxxxxxx +xxxxxxxxx....c.......5........................c....................xxxxxxxxxxxxx +xxxxxxxxx....ccc....%%%%%....cccccccccccccccccc........................xxxxxxxxx +xxxxxxxxx......cc...........cc.........................................xxxxxxxxx +xxxxxxxxx.......cccccc+cccccc..........................................xxxxxxxxx +xxxxxxxxx........cc.......cc...........................................xxxxxxxxx +xxxxxxxxx.........cc.....cc.....................cccccccccccccccccccccccxxxxxxxxx +xxxxxxxxx..........ccc+ccc......................c......vvv.............xxxxxxxxx +xxxxxxxxx..........ccc.c........................c......v5+...vvvvv.....xxxxxxxxx +xxxxxxxxx..........ccc.c........................c......vvv...v.5.v.....xxxxxxxxx +xxxxxxxxxccccccccccccc.ccc......................c............v..5v.....xxxxxxxxx +xxxxxxxxx..........c.....cccccccccccccccccccccccccccc..........vv+vv...xxxxxxxxx +xxxxxxxxx..........c............................+................5111..xxxxxxxxx +xxxxxxxxx..........c.{([.c......................+................5.....xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: death yak, plant, griffon, killer bee, ogre, oklob plant +MONS: wandering mushroom + +############################################################################# +# Fort Yaktaur (Matthew Ludivico) + +NAME: fort_yaktaur +ORIENT: southeast + +MAP +.........@....wwwwwwwwwwwwwwwwwxxxxxxxxx +.ccccc.......ww....wwww....wwwwxxxxxxxxx +.c$c%c......ww.ccccccccc.......xxxxxxxxx +.c+c+c......ww.c.%$....ccccccccxxxxxxxxx +.c...+......ww.c*.115..c$$+|*|cxxxxxxxxx +.c1..c.....ww..c...55+ccc+cxx=cxxxxxxxxx +.ccccc.....ww..ccccccc....c|=*cxxxxxxxxx +............ww.......c5...cxx=cxxxxxxxxx +....6.ccccc.ww.w...2.+51..c|1.cxxxxxxxxx +....63+...c..wwww..21+51..c2.2cxxxxxxxxx +....6.ccccc..wwwwww..c5...cc+ccxxxxxxxxx +............wwwwwww..c........cxxxxxxxxx +............wwwwwww..ccccccccccxxxxxxxxx +...........ww1w..www...........xxxxxxxxx +.......566.www.....www.........xxxxxxxxx +.........1ww....ccccc..........xxxxxxxxx +.....566.w......+...c..........xxxxxxxxx +.........www....ccccc..........xxxxxxxxx +...........ww............wwwwwwxxxxxxxxx +.......3....wwwww......www.....xxxxxxxxx +......666.......ww...www.......xxxxxxxxx +.....cc+cc.......wwwww.........xxxxxxxxx +.....c...c.....................xxxxxxxxx +.....ccccc.....................xxxxxxxxx +...............................xxxxxxxxx +...............................xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + +MONS: yaktaur, death yak, minotaur, random +MONS: yak, gnoll, random + +######################################################################## +# box level (John Savard) +# + +NAME: box_level +ORIENT: encompass + +MAP +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxx.................xx.............x...................^.........xxxxxxxxx +xxxxxxxxx.................xx...xxxxxx....x.xxxxxxx.xxxxxxxxxxxxxxxxxxx.xxxxxxxxx +xxxxxxxxx.................xx...xx.0......x.x........x......x.........x.xxxxxxxxx +xxxxxxxxx..$..............xx...xx........x.x........x.....%x.x..*..xxx.xxxxxxxxx +xxxxxxxxx......................xx........x.x........x.xxxxxx.x.....x...xxxxxxxxx +xxxxxxxxx......................xx....%...x.x........x.x......xxxxxxx.x.xxxxxxxxx +xxxxxxxxx.................xx...xx........x.x........x.x.xxxxxx.......x.xxxxxxxxx +xxxxxxxxx.................xx...xx........x.x..{.....x.x..............x.xxxxxxxxx +xxxxxxxxx.............0...xx...xxxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxxxxxxxx.xxxxxxxxx +xxxxxxxxx.................xx...........................................xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxx}x.........................>=........xxxxxxxxx +xxxxxxxxx..................x...xxx.x.xxx+xxxxxxxxxxxxxxxx+xxxxx........xxxxxxxxx +xxxxxxxxx..xxxxxxxxxxxxxx..x...xxx.x.x0...x..0..............0.x........xxxxxxxxx +xxxxxxxxx..x............x..x...xxx.x.x....x...................x........xxxxxxxxx +xxxxxxxxx....xxxxxxxxx..x..x...xxx.x.x....x...................x......8*xxxxxxxxx +xxxxxxxxx..x.x....0..x..x..x...xxx...x...%x...................x......*|xxxxxxxxx +xxxxxxxxx..x.x..........x..x...xxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxx..x.x*......x..x..x..........x...........0...x...%............xxxxxxxxx +xxxxxxxxx..x.xxxxxxxxx..x..=..........x.xxxxxxxxxxxxx.x................xxxxxxxxx +xxxxxxxxx..x......0.....xxxxxxx.......x.x...x...x...x.x................xxxxxxxxx +xxxxxxxxx..xxxxxxxxxxxxxxxxxxxx..0....x...x.x.x.x.x.x.x......0.........xxxxxxxxx +xxxxxxxxx..........^.........xx.......x.x.x.x.x.x.x...+................xxxxxxxxx +xxxxxxxxxcccccccccccccccccc..xx.......x.x$x...x...xxxxx................xxxxxxxxx +xxxxxxxxxc...........9....c..xx.......x.x.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxc......c............xx.......x.x.x...x..0.....................xxxxxxxxx +xxxxxxxxxc.....|c............xx.......x.x.x.x.x........................xxxxxxxxx +xxxxxxxxxc...........9....c..xx.......x.x...x.x........................xxxxxxxxx +xxxxxxxxxcccccccccccccccccc..xx.......x.xxxxx.x........................xxxxxxxxx +xxxxxxxxx....................xx.......x.x.....=....................*...xxxxxxxxx +xxxxxxxxx....................xx.......x.x.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.......x.x.x...........................(xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxx.x$x..xxxx.xxxxxxxxxxxxxxxxxxxx.xxxxxxxxx +xxxxxxxxx...............................x.x..x.......................x.xxxxxxxxx +xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.x..x.xxxxxxxxxxxxx.........x.xxxxxxxxx +xxxxxxxxx.............)xxx................x..x.xxxxxxxxxxxxx.........x.xxxxxxxxx +xxxxxxxxx..............xxx.xxxxxxxxxxxxxxxx..x.xxxxxxxxxxxxx.........x.xxxxxxxxx +xxxxxxxxx..............xxx...................x.x...........xxxxx+xxxxx.xxxxxxxxx +xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.x..$........x.........x.xxxxxxxxx +xxxxxxxxx......9.......xxxxxxxxxxxxxxxxxxxxxxx.x...........x........%x.xxxxxxxxx +xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.x.0.........x0........x.xxxxxxxxx +xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.x.......$...x.........x.xxxxxxxxx +xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.x...........xxxxxxxxxxx.xxxxxxxxx +xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxx.x...........xxxxxxxxx +xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.............x...........xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx+xxxxxxxxxxxxxxxxxx +xxxxxxxxx..............................................................xxxxxxxxx +xxxxxxxxx.xxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxx.xxxxxxx=xxxxxx.xxxxxx.xxxxxxxxx +xxxxxxxxx.....xx.................xxxxxxxxxxx.......x........x.....x....xxxxxxxxx +xxxxxxxxx....0xx.................xxxxxxxxxxx.%.....x.0......x...0.x....xxxxxxxxx +xxxxxxxxx.....xx.9...............xxxxxxxxxxx.......x........x.%...x..$.xxxxxxxxx +xxxxxxxxx.....xx.................xxxxxxxxxxx.......x........x.....x....xxxxxxxxx +xxxxxxxxx.....xx.................xxxxxxxxxxx.......x........x.....x..0.xxxxxxxxx +xxxxxxxxx....0xx.................xxxxxxxxxxx.......x$.......x.....x....xxxxxxxxx +xxxxxxxxx]....xx................*xxxxxxxxxxx......[x........x.....x$...xxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ENDMAP + + +############################################################################## +# Minivaults +############################################################################## +# +# NOTE: *Cannot* place 8,9 or 0 monsters in branch vaults which neither use the +# normal mons_level function or are around level 35, or generation will crash. +# +# [dshaligram] Minivaults were traditionally drawn after rotating the vault by +# 90 degrees anticlockwise. Under the new dungeon vault handling, the map may +# be freely rotated and mirrored to any position (20061105). +# +# Minivaults are traditionally 12x12 (hardcoded limitation in dungeon.cc). Under +# the new rules they can be any size, but the smaller the better. I'd recommend +# no larger than 35x30 or so. +# +# NOTE: Minivaults must NOT have an ORIENT: attribute. Including an ORIENT: +# attribute makes the map a normal vault, and mayhem can result. + +NAME: minivault_1 + +MAP +............ +..xxxx=xxx.. +.xx..x...xx. +.x....x...x. +.x...x....x. +.xx.x*x.x.=. +.=.x.x*x.xx. +.x....x...x. +.x...x....x. +.xx...x..xx. +..xxx=xxxx.. +............ +ENDMAP + +################################### + +NAME: minivault_2 + +MAP +............ +..xxxx.xxxx. +..xx.....xx. +..x.......x. +..x.......x. +......C..... +..x.......x. +..x.......x. +..xx.....xx. +..xxxx.xxxx. +............ +............ +ENDMAP + +################################### + +NAME: minitemple + +# More common than the others. +CHANCE: 20 + +MAP +............ +.cccccccccc. +.cccccccccc. +.cBcBcBcBcc. +.G.c.c.c.Bc. +.........Bc. +.........Bc. +.G.c.c.c.Bc. +.cBcBcBcBcc. +.cccccccccc. +.cccccccccc. +............ +ENDMAP + +################################### + +NAME: minivault_4 + +MAP +............ +....xwxx.... +..xxxwwxwx.. +..xwwwwwwx.. +.xwwxwwxwxx. +.xwwwwwwwwx. +.xwwxwwwxww. +.xxwwwwwwxx. +..wwwwxwwx.. +..xxxwwxxw.. +....xxww.... +............ +ENDMAP + +################################### + +NAME: minivault_5 + +MAP +............ +.x.xxxxxxxx. +.x.x......x. +.x.x.xxxx.x. +.x.x.x**x.x. +.x.x.x**x.x. +.x.x.xx.x.x. +.x.x....x.x. +.x.xxxxxx.x. +.x........x. +.xxxxxxxxxx. +............ +ENDMAP + +################################### +# Wizard's laboratory +NAME: minivault_6 + +MAP +............ +.ccccccc+cc. +.c........c. +.c........c. +.c..1.....c. +.c........c. +.cc+ccccccc. +.c***c3232c. +.c|**+2223c. +.c||*c3322c. +.cccccccccc. +............ +ENDMAP + +MONS: wizard, small abomination, large abomination + +################################### +# Beehive minivault + +NAME: minivault_7 + +MAP +............ +....aaaa.... +..a2a2aaaa.. +..aaRa3a2a.. +.aa2aRa2aaa. +.a3aRa1aRa2. +.aa3aRaRa2a. +.aaa2a2a3aa. +..a3aRa2aa.. +...aa2aa2a.. +....aaaa.... +............ +ENDMAP + +MONS: queen bee, killer bee, killer bee larva + +################################### +# Lava pond +NAME: minivault_8 + +MAP +x.x.x.x.x.x. +.c.c.c.c.c.x +x...l1l...c. +.c.llllll..x +x.lllllll1c. +.c.llFGll..x +x..llGFll.c. +.c1lllllll.x +x..llllll.c. +.c...l1l...x +x.c.c.c.c.c. +.x.x.x.x.x.x +ENDMAP + +MONS: molten gargoyle + +################################### +# Evil zoo + +NAME: minivault_9 +DEPTH: 15-27 + +MAP +............ +.==========. +.==========. +.==========. +.===8888===. +.===8998===. +.===8998===. +.===8888===. +.==========. +.==========. +.==========. +............ +ENDMAP + +################################### + +NAME: minivault_10 + +MAP +............ +.xxxx..xxxx. +.x**x..x**x. +.x**+..+**x. +.xx+x..x+xx. +............ +............ +.xx+x..x+xx. +.x**+..+**x. +.x**x..x**x. +.xxxx..xxxx. +............ +ENDMAP + +################################### +# Multicoloured onion + +NAME: minivault_11 + +MAP +............ +.+xxxxxxxx+. +.x........x. +.x.+cccc+.x. +.x.c....c.x. +.x.c.bb.c.x. +.x.c.bb.c.x. +.x.c....c.x. +.x.+cccc+.x. +.x........x. +.+xxxxxxxx+. +............ +ENDMAP + +################################### +# Closed-box minivault + +NAME: minivault_12 + +MAP +............ +.xxxxxxxxxx. +.x>9$9$9$x. +.xxxxxxxxxx. +............ +ENDMAP + +################################### +# Little trap spiral + +NAME: minivault_13 + +MAP +............ +.xxxxxxxxxx. +.=.^x..=.9x. +.x.$=.^x..x. +.xxxxxxxx=x. +.x.8+|0x8.x. +.x8$x.|x..x. +.xx=xxxx=xx. +.x.9=^.x..x. +.x..x.^=9.x. +.xxxxxxxxxx. +............ +ENDMAP + +################################### +# Water cross + +NAME: minivault_14 + +MAP +............ +.wwwww.wwww. +.wwwww.wwww. +.wwwww.wwww. +.wwwww.wwww. +.......wwww. +.wwww....... +.wwww.wwwww. +.wwww.wwwww. +.wwww.wwwww. +.wwww.wwwww. +............ +ENDMAP + +################################### +# Lava pond + +NAME: minivault_15 + +MAP +............ +............ +....lll..... +...vvlvv.... +..lv|*|vl... +..ll*S*ll... +..lv|*|vl... +...vvlvv.... +....lll..... +............ +............ +............ +ENDMAP + +################################### + +NAME: minivault_16 + +MAP +............ +............ +............ +............ +............ +............ +......S..... +............ +............ +............ +............ +............ +ENDMAP + +################################### + +NAME: minivault_17 + +MAP +............ +............ +............ +............ +............ +.....F...... +............ +............ +............ +............ +............ +............ +ENDMAP + +################################### + +NAME: minivault_18 + +MAP +............ +............ +............ +............ +............ +.....H...... +............ +............ +............ +............ +............ +............ +ENDMAP + +################################### + +NAME: minivault_19 + +MAP +............ +.xx......xx. +.xxx....xxx. +..xxx..xxx.. +...xxxxxx... +....xxxx.... +....xxxx.... +...xxxxxx... +..xxx..xxx.. +.xxx....xxx. +.xx......xx. +............ +ENDMAP + +################################### + +NAME: minivault_20 +MAP +............ +.xxxx..xxxx. +.x........x. +.x..xxxx..x. +.x.x....x.x. +...x.x9.x... +...x.9x.x... +.x.x....x.x. +.x..xxxx..x. +.x........x. +.xxxx..xxxx. +............ +ENDMAP + +################################### + +NAME: minivault_21 + +MAP +............ +.^xxxxxxxx^. +.x........x. +.x.cccccc.x. +.x.c|....|c.x. +.x.cccccc.x. +.x........x. +.^xxxxxxxx^. +............ +ENDMAP + +################################### + +NAME: minivault_22 + +MAP +............ +.....xx..... +...xxxxxx... +..x^x..x^x.. +..xx.xx.xx.. +.xx.x$$x.xx. +.xx.x$$x.xx. +..xx.xx.xx.. +..x^x..x^x.. +...xxxxxx... +.....xx..... +............ +ENDMAP + +################################### + +NAME: minivault_23 + +MAP +x.x.x.x.x.x. +.x.x.x.x.x.x +x.x.x.x.x.x. +.x.x.x.x.x.x +x.x.x.x.x.x. +.x.x.x.x.x.x +x.x.x.x.x.x. +.x.x.x.x.x.x +x.x.x.x.x.x. +.x.x.x.x.x.x +x.x.x.x.x.x. +.x.x.x.x.x.x +ENDMAP + +################################### + +NAME: minivault_24 + +MAP +............ +....xxxx.... +....xxxx.... +....xxxx.... +.xxxx.x.xxx. +.xxx.x.xxxx. +.xxxx.x.xxx. +.xxx.x.xxxx. +....xxxx.... +....xxxx.... +....xxxx.... +............ +ENDMAP + +################################### + +NAME: minivault_25 + +MAP +............ +.xx+xxxxxxx. +.x........x. +.x........+. +.x........x. +.x........x. +.x........x. +.x........x. +.+........x. +.x........x. +.xxxxxxx+xx. +............ +ENDMAP + +################################### + +NAME: minivault_26 + +MAP +c..........c +.c...cc...c. +..c..cc..c.. +...c....c... +....c..c.... +.cc..cc..cc. +.cc..cc..cc. +....c..c.... +...c....c... +..c..cc..c.. +.c...cc...c. +c..........c +ENDMAP + +################################### + +NAME: minivault_27 + +MAP +............ +.x.xxxxxxxx. +.x........x. +.xxxxxxxx.x. +.x........x. +.x.xxxxxxxx. +.x........x. +.xxxxxxxx.x. +.x........x. +.x.xxxxxxxx. +............ +............ +ENDMAP + +################################### + +NAME: minivault_28 + +MAP +............ +.xxxx.xxxx.. +.x.......x.. +.x..999..x.. +.x.9...9.x.. +...9.I.9.... +.x.9...9.x.. +.x..999..x.. +.x.......x.. +.xxxx.xxxx.. +............ +............ +ENDMAP + +################################### +# Anthill + +NAME: minivault_29 + +MAP +.3......3... +...x.xx.x.2. +.xxx2xxxxx.. +.xxxx42xxx2. +.2xx243432x3 +.xx421424xx. +3xx423242x.. +.x2x3243xxx. +.x2xx42422x. +..xxxxxxxx2. +...x2xxxx3.. +.3.......33. +ENDMAP + +MONS: queen ant, soldier ant, giant ant, ant larva + +################################### +# Solitary fountain + +NAME: minivault_30 + +MAP +............ +............ +............ +............ +............ +.....?...... +............ +............ +............ +............ +............ +............ +ENDMAP + +SYMBOL: TUV + +################################### +# jmf: Multi-god temple thing + +NAME: multi_god_temple + +MAP +............ +.=xxxxxxxx=. +.x9......9x. +.xT......Tx. +.x..C..C..x. +.xT......Tx. +.xxxxxxxxxx. +.xxx$$$$xxx. +.xx8....8xx. +..xx....xx.. +...xG..Gx... +............ +ENDMAP + +################################### +# jmf: Another multi-god temple thing + +NAME: multi_god_temple2 + +MAP +............ +..vvvvvvvv.. +.vv......vv. +.v..x..x..v. +.v.Cx..xC.v. +.v..x..x..v. +.vT8x..x8Tv. +.vvvx==xvvv. +...Gx99xG... +...+*99*+... +...GxxxxG... +............ +ENDMAP + +############################################################################# +# Pandemonium lesser demon vaults +############################################################################# +# Pandemonium demon vaults are selected by tag. They must have the tag "pan", +# or they won't even be considered. Tags are case-sensitive. +# +# Tagging these levels also ensures they won't be generated in the main +# dungeon. + +NAME: rand_demon_1 +TAGS: pan +MONS: pandemonium demon, random, random, random, random, random + +MAP +............ +.xx.xx.x.xx. +..x.x..x.x.. +..x.x..x.x.. +..x.x..x.x.. +..x.x..x.x.. +..x.x1.x.x.. +..x.x..x.x.. +..x.x..x.x.. +..x.x..x.x.. +.xx.x.xx.xx. +............ +ENDMAP + +################################### + +NAME: rand_demon_2 +TAGS: pan +MONS: pandemonium demon, greater demon, common demon +MONS: common demon, random, random + +MAP +............ +.xxxxxxxx3x. +.3.....xx.x. +.xxxxxx4x.x. +.xx4x..xx.x. +.x.x.22.x.x. +.x.x.12.x.x. +.x.xx..x4xx. +.x.x4xxxxxx. +.x.xx.....3. +.x3xxxxxxxx. +............ +ENDMAP + +################################### + +NAME: rand_demon_3 +TAGS: pan +MONS: pandemonium demon, common demon, common demon + +MAP +............ +.x.x.x3x.x.. +..x.x3x3x.x. +.x.x.x2x.x.. +..x3x2x2x3x. +.x3x2x1x2x3. +..x3x2x2x3x. +.x.x.x2x3x.. +..x.x3x3x.x. +.x.x.x3x.x.. +..x.x.x.x.x. +............ +ENDMAP + +################################### + +NAME: rand_demon_4 +TAGS: pan +MONS: pandemonium demon, any demon, any demon + +MAP +............ +.xxxxxxxxx.. +.x$=*=3=|x.. +.xxxxxxx=x.. +.x2=3=2x|x.. +.x=xxxxx=x.. +.x3=*x1=Px.. +.x=x=xxxxx.. +.x*x2=3=2=.. +.xxxxxxxxx.. +............ +............ +ENDMAP + +################################### + +NAME: rand_demon_5 +TAGS: pan +MONS: pandemonium demon, any demon, any demon + +MAP +............ +...xxxxxx... +..xx....xx.. +.xx......xx. +.x..3232..x. +.x..2|P3..x. +.x..3P|2..x. +.x..2123..x. +.xx......xx. +..xx....xx.. +...xxxxxx... +............ +ENDMAP + +################################### + +NAME: rand_demon_6 +TAGS: pan +MONS: pandemonium demon, any demon, any demon + +MAP +............ +............ +......2..... +............ +.3.......... +..........2. +.....1...... +............ +............ +.2.......3.. +............ +............ +ENDMAP + +################################### + +NAME: rand_demon_7 +TAGS: pan +MONS: pandemonium demon, any demon, greater demon + +MAP +............ +.xxx....xxx. +.x|xx=xxx|x. +.xx=....=xx. +..x.x==x.x.. +..x.=12=.=.. +..=.=23=.x.. +..x.x==x.x.. +.xx=....=xx. +.x|xxx=xx|x. +.xxx....xxx. +............ +ENDMAP + +################################### + +NAME: rand_demon_8 +TAGS: pan +MONS: pandemonium demon, greater demon, any demon + +MAP +............ +....xxxxxxx. +..xxx....1x. +.xx..2....x. +.x........x. +.xx.......x. +..xx33..2.x. +....33...xx. +.....x...x.. +..F..xx.xx.. +......xxx... +............ +ENDMAP + +################################### + +NAME: rand_demon_9 +TAGS: pan +MONS: pandemonium demon, any demon, greater demon + +MAP +............ +.xxxxxxxxxx. +.x2=3=3=3xx. +.x=xxxxxx2x. +.x3x^^^^x=x. +.x=x^P^^x2x. +.x3x^^1^x=x. +.x=x^^^^x3x. +.x2xxxx=x=x. +.xx2=2=3x3x. +.xxxxxxxx=x. +............ +ENDMAP diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index 8bfdb776d8..545ec6ffc3 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -3,6 +3,8 @@ * Summary: Debug and wizard related functions. * Written by: Linley Henzell and Jesse Jones * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <4> 14/12/99 LRH Added cast_spec_spell_name() @@ -19,8 +21,13 @@ #include #include #include +#include #include +#ifdef UNIX +#include +#endif + #ifdef DOS #include #endif @@ -28,9 +35,13 @@ #include "externs.h" #include "direct.h" +#include "describe.h" #include "dungeon.h" +#include "fight.h" #include "invent.h" #include "itemname.h" +#include "itemprop.h" +#include "item_use.h" #include "items.h" #include "misc.h" #include "monplace.h" @@ -45,10 +56,8 @@ #include "spl-cast.h" #include "spl-util.h" #include "stuff.h" - -#ifndef WIZARD -#define WIZARD -#endif +#include "travel.h" +#include "version.h" #if DEBUG && WIN #define MyDebugBreak() _asm {int 3} @@ -74,23 +83,11 @@ static HANDLE sConsole = NULL; static void BreakStrToDebugger(const char *mesg) { -#if OSX +#if OSX || defined(__MINGW32__) fprintf(stderr, mesg); // raise(SIGINT); // this is what DebugStr() does on OS X according to Tech Note 2030 int* p = NULL; // but this gives us a stack crawl... *p = 0; -#elif MAC - unsigned char s[50]; - - int len = strlen(mesg); - - if (len > 255) - len = 255; - - s[0] = (Byte) len; - BlockMoveData(mesg, s + 1, len); - - DebugStr(s); #elif WIN MSG msg; // remove pending quit messages so the message box displays @@ -309,10 +306,6 @@ static void TraceString(const char *mesg) } #endif -#if MAC -#pragma mark - -#endif - // ======================================================================== // Global Functions // ======================================================================== @@ -327,10 +320,6 @@ void AssertFailed(const char *expr, const char *file, int line) { char mesg[512]; -#if MAC - sprintf(mesg, "ASSERT(%s) in %s at line %d failed.", expr, file, line); - -#else const char *fileName = file + strlen(file); // strip off path while (fileName > file && fileName[-1] != '\\') @@ -338,7 +327,6 @@ void AssertFailed(const char *expr, const char *file, int line) sprintf(mesg, "ASSERT(%s) in '%s' at line %d failed.", expr, fileName, line); -#endif BreakStrToDebugger(mesg); } @@ -386,17 +374,11 @@ void TRACE(const char *format, ...) } #endif // DEBUG -//--------------------------------------------------------------- -// -// debug_prompt_for_monster -// -//--------------------------------------------------------------- #ifdef WIZARD + static int debug_prompt_for_monster( void ) { char specs[80]; - char obj_name[ ITEMNAME_SIZE ]; - char *ptr; mpr( "(Hint: 'generated' names, eg 'orc zombie', won't work)", MSGCH_PROMPT ); mpr( "Which monster by name? ", MSGCH_PROMPT ); @@ -405,28 +387,7 @@ static int debug_prompt_for_monster( void ) if (specs[0] == '\0') return (-1); - int mon = -1; - - for (int i = 0; i < NUM_MONSTERS; i++) - { - moname( i, true, DESC_PLAIN, obj_name ); - - ptr = strstr( strlwr(obj_name), strlwr(specs) ); - if (ptr != NULL) - { - mpr( obj_name ); - if (ptr == obj_name) - { - // we prefer prefixes over partial matches - mon = i; - break; - } - else - mon = i; - } - } - - return (mon); + return (get_monster_by_name(specs)); } #endif @@ -494,7 +455,7 @@ void debug_change_species( void ) int sp = -1; - for (int i = SP_HUMAN; i < NUM_SPECIES; i++) + for (i = SP_HUMAN; i < NUM_SPECIES; i++) { char sp_name[80]; strncpy( sp_name, species_name(i, you.experience_level), sizeof( sp_name ) ); @@ -626,7 +587,8 @@ void create_spec_monster(void) if (mon == -1) canned_msg( MSG_OK ); else - create_monster( mon, 0, BEH_SLEEP, you.x_pos, you.y_pos, MHITNOT, 250 ); + create_monster( mon, 0, BEH_SLEEP, + you.x_pos, you.y_pos, MHITNOT, 250, true ); } // end create_spec_monster() #endif @@ -650,7 +612,8 @@ void create_spec_monster_name(void) } else { - create_monster(mon, 0, BEH_SLEEP, you.x_pos, you.y_pos, MHITNOT, 250); + create_monster(mon, 0, BEH_SLEEP, + you.x_pos, you.y_pos, MHITNOT, 250, false); } } // end create_spec_monster_name() #endif @@ -736,7 +699,7 @@ void create_spec_object(void) MSGCH_PROMPT); mpr("= - jewellery ! - potions : - books | - staves 0 - The Orb", MSGCH_PROMPT); - mpr("} - miscellany X - corpses %% - food $ - gold ESC - exit", + mpr("} - miscellany X - corpses % - food $ - gold ESC - exit", MSGCH_PROMPT); mpr("What class of item? ", MSGCH_PROMPT); @@ -814,7 +777,7 @@ void create_spec_object(void) { mon = debug_prompt_for_monster(); - if (mon == -1) + if (mon == -1 || mon == MONS_PROGRAM_BUG) { mpr( "No such monster." ); return; @@ -825,7 +788,7 @@ void create_spec_object(void) mitm[thing_created].plus = mon; mitm[thing_created].plus2 = 0; mitm[thing_created].special = 210; - mitm[thing_created].colour = mons_colour(mon);; + mitm[thing_created].colour = mons_class_colour(mon);; mitm[thing_created].quantity = 1; mitm[thing_created].flags = 0; } @@ -856,13 +819,11 @@ void create_spec_object(void) { if (strstr( "naga barding", specs )) { - mitm[thing_created].sub_type = ARM_BOOTS; - mitm[thing_created].plus2 = TBOOT_NAGA_BARDING; + mitm[thing_created].sub_type = ARM_NAGA_BARDING; } else if (strstr( "centaur barding", specs )) { - mitm[thing_created].sub_type = ARM_BOOTS; - mitm[thing_created].plus2 = TBOOT_CENTAUR_BARDING; + mitm[thing_created].sub_type = ARM_CENTAUR_BARDING; } else if (strstr( "wizard's hat", specs )) { @@ -972,6 +933,14 @@ void create_spec_object(void) mitm[thing_created].plus = 24; break; + case OBJ_STAVES: + if (item_is_rod( mitm[thing_created] )) + { + mitm[thing_created].plus = MAX_ROD_CHARGE * ROD_CHARGE_MULT; + mitm[thing_created].plus2 = MAX_ROD_CHARGE * ROD_CHARGE_MULT; + } + break; + case OBJ_MISCELLANY: // Runes to "demonic", decks have 50 cards, ignored elsewhere? mitm[thing_created].plus = 50; @@ -1012,7 +981,7 @@ void tweak_object(void) char specs[50]; char keyin; - int item = prompt_invent_item( "Tweak which item? ", -1 ); + int item = prompt_invent_item("Tweak which item? ", MT_INVSELECT, -1); if (item == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -1031,7 +1000,7 @@ void tweak_object(void) item_name( you.inv[item], DESC_INVENTORY_EQUIP, info ); mpr( info ); - mpr( "a - plus b - plus2 c - special d - quantity ESC - exit", + mpr( "a - plus b - plus2 c - special d - quantity e - flags ESC - exit", MSGCH_PROMPT ); mpr( "Which field? ", MSGCH_PROMPT ); @@ -1045,6 +1014,8 @@ void tweak_object(void) field_ptr = &(you.inv[item].special); else if (keyin == 'd') field_ptr = &(you.inv[item].quantity); + else if (keyin == 'e') + field_ptr = &(you.inv[item].flags); else if (keyin == ESCAPE || keyin == ' ' || keyin == '\r' || keyin == '\n') { @@ -1052,11 +1023,11 @@ void tweak_object(void) return; } - if (keyin >= 'a' && keyin <= 'd') + if (keyin >= 'a' && keyin <= 'e') break; } - if (keyin != 'c') + if (keyin != 'c' && keyin != 'e') { const short *const ptr = static_cast< short * >( field_ptr ); snprintf( info, INFO_SIZE, "Old value: %d (0x%04x)", *ptr, *ptr ); @@ -1081,7 +1052,7 @@ void tweak_object(void) if (new_value == 0 && end == specs) return; - if (keyin != 'c') + if (keyin != 'c' && keyin != 'e') { short *ptr = static_cast< short * >( field_ptr ); *ptr = new_value; @@ -1708,3 +1679,571 @@ void error_message_to_player(void) mpr("I suggest you leave this level then save as soon as possible."); } // end error_message_to_player() + +#ifdef WIZARD + +static int create_fsim_monster(int mtype, int hp) +{ + const int mi = + create_monster( mtype, 0, BEH_HOSTILE, you.x_pos, you.y_pos, + MHITNOT, 250 ); + + if (mi == -1) + return (mi); + + monsters *mon = &menv[mi]; + mon->hit_points = mon->max_hit_points = hp; + return (mi); +} + +static skill_type fsim_melee_skill(const item_def *item) +{ + skill_type sk = SK_UNARMED_COMBAT; + if (item) + sk = weapon_skill(*item); + return (sk); +} + +static void fsim_set_melee_skill(int skill, const item_def *item) +{ + you.skills[fsim_melee_skill(item)] = skill; + you.skills[SK_FIGHTING] = skill * 15 / 27; +} + +static void fsim_set_ranged_skill(int skill, const item_def *item) +{ + you.skills[range_skill(*item)] = skill; + you.skills[SK_RANGED_COMBAT] = skill * 15 / 27; +} + +static void fsim_item(FILE *out, + bool melee, + const item_def *weap, + int wskill, unsigned long damage, + long iterations, long hits, + int maxdam, unsigned long time) +{ + double hitdam = hits? double(damage) / hits : 0.0; + int avspeed = (int) (time / iterations); + fprintf(out, " %2d | %3ld%% | %5.2f | %5.2f | %5.2f | %3d | %2ld\n", + wskill, + 100 * hits / iterations, + double(damage) / iterations, + hitdam, + double(damage) * player_speed() / avspeed / iterations, + maxdam, + time / iterations); +} + +static bool fsim_ranged_combat(FILE *out, int wskill, int mi, + const item_def *item, int missile_slot) +{ + monsters &mon = menv[mi]; + unsigned long cumulative_damage = 0L; + unsigned long time_taken = 0L; + long hits = 0L; + int maxdam = 0; + + const int thrown = missile_slot == -1? get_fire_item_index() : missile_slot; + if (thrown == ENDOFPACK || thrown == -1) + { + mprf("No suitable missiles for combat simulation."); + return (false); + } + + fsim_set_ranged_skill(wskill, item); + + no_messages mx; + const long iter_limit = Options.fsim_rounds; + const int hunger = you.hunger; + for (long i = 0; i < iter_limit; ++i) + { + mon.hit_points = mon.max_hit_points; + bolt beam; + you.time_taken = player_speed(); + if (throw_it(beam, thrown, &mon)) + hits++; + you.hunger = hunger; + time_taken += you.time_taken; + + int damage = (mon.max_hit_points - mon.hit_points); + cumulative_damage += damage; + if (damage > maxdam) + maxdam = damage; + } + fsim_item(out, false, item, wskill, cumulative_damage, + iter_limit, hits, maxdam, time_taken); + + return (true); +} + +static bool fsim_melee_combat(FILE *out, int wskill, int mi, + const item_def *item) +{ + monsters &mon = menv[mi]; + unsigned long cumulative_damage = 0L; + unsigned long time_taken = 0L; + long hits = 0L; + int maxdam = 0; + + fsim_set_melee_skill(wskill, item); + + no_messages mx; + const long iter_limit = Options.fsim_rounds; + const int hunger = you.hunger; + for (long i = 0; i < iter_limit; ++i) + { + mon.hit_points = mon.max_hit_points; + you.time_taken = player_speed(); + if (you_attack(mi, true)) + hits++; + + you.hunger = hunger; + time_taken += you.time_taken; + + int damage = (mon.max_hit_points - mon.hit_points); + cumulative_damage += damage; + if (damage > maxdam) + maxdam = damage; + } + fsim_item(out, true, item, wskill, cumulative_damage, iter_limit, hits, + maxdam, time_taken); + + return (true); +} + +static bool debug_fight_simulate(FILE *out, int wskill, int mi, int miss_slot) +{ + int weapon = you.equip[EQ_WEAPON]; + const item_def *iweap = weapon != -1? &you.inv[weapon] : NULL; + + if (iweap && iweap->base_type == OBJ_WEAPONS + && is_range_weapon(*iweap)) + return fsim_ranged_combat(out, wskill, mi, iweap, miss_slot); + else + return fsim_melee_combat(out, wskill, mi, iweap); +} + +static const item_def *fsim_weap_item() +{ + const int weap = you.equip[EQ_WEAPON]; + if (weap == -1) + return NULL; + + return &you.inv[weap]; +} + +static std::string fsim_wskill() +{ + const item_def *iweap = fsim_weap_item(); + return iweap && iweap->base_type == OBJ_WEAPONS + && is_range_weapon(*iweap)? + skill_name( range_skill(*iweap) ) : + iweap? skill_name( fsim_melee_skill(iweap) ) : + skill_name( SK_UNARMED_COMBAT ); +} + +static std::string fsim_weapon(int missile_slot) +{ + char item_buf[ITEMNAME_SIZE]; + if (you.equip[EQ_WEAPON] != -1) + { + const item_def &weapon = you.inv[ you.equip[EQ_WEAPON] ]; + item_name(weapon, DESC_PLAIN, item_buf, true); + + if (is_range_weapon(weapon)) + { + const int missile = + missile_slot == -1? get_fire_item_index() : + missile_slot; + if (missile < ENDOFPACK) + { + std::string base = item_buf; + base += " with "; + in_name(missile, DESC_PLAIN, item_buf, true); + return (base + item_buf); + } + } + } + else + { + strncpy(item_buf, "unarmed", sizeof item_buf); + } + return (item_buf); +} + +static std::string fsim_time_string() +{ + time_t curr_time = time(NULL); + struct tm *ltime = localtime(&curr_time); + if (ltime) + { + char buf[100]; + snprintf(buf, sizeof buf, "%4d%02d%02d/%2d:%02d:%02d", + ltime->tm_year + 1900, + ltime->tm_mon + 1, + ltime->tm_mday, + ltime->tm_hour, + ltime->tm_min, + ltime->tm_sec); + return (buf); + } + return (""); +} + +static void fsim_mon_stats(FILE *o, const monsters &mon) +{ + char buf[ITEMNAME_SIZE]; + fprintf(o, "Monster : %s\n", + moname(mon.type, true, DESC_PLAIN, buf)); + fprintf(o, "HD : %d\n", mon.hit_dice); + fprintf(o, "AC : %d\n", mon.armour_class); + fprintf(o, "EV : %d\n", mon.evasion); +} + +static void fsim_title(FILE *o, int mon, int ms) +{ + char buf[ITEMNAME_SIZE]; + fprintf(o, CRAWL " version " VERSION "\n\n"); + fprintf(o, "Combat simulation: %s %s vs. %s (%ld rounds) (%s)\n", + species_name(you.species, you.experience_level), + you.class_name, + moname(menv[mon].type, true, DESC_PLAIN, buf), + Options.fsim_rounds, + fsim_time_string().c_str()); + fprintf(o, "Experience: %d\n", you.experience_level); + fprintf(o, "Strength : %d\n", you.strength); + fprintf(o, "Intel. : %d\n", you.intel); + fprintf(o, "Dexterity : %d\n", you.dex); + fprintf(o, "Base speed: %d\n", player_speed()); + fprintf(o, "\n"); + fsim_mon_stats(o, menv[mon]); + fprintf(o, "\n"); + fprintf(o, "Weapon : %s\n", fsim_weapon(ms).c_str()); + fprintf(o, "Skill : %s\n", fsim_wskill().c_str()); + fprintf(o, "\n"); + fprintf(o, "Skill | Accuracy | Av.Dam | Av.HitDam | Eff.Dam | Max.Dam | Av.Time\n"); +} + +static int cap_stat(int stat) +{ + return (stat < 1 ? 1 : + stat > 127 ? 127 : + stat); +} + +static bool debug_fight_sim(int mindex, int missile_slot) +{ + FILE *ostat = fopen("fight.stat", "a"); + if (!ostat) + { + // I'm not sure what header provides errno on djgpp, + // and it's insufficiently important for a wizmode-only + // feature. +#ifndef DOS + mprf("Can't write fight.stat: %s", strerror(errno)); +#endif + return (false); + } + + bool success = true; + FixedVector skill_backup = you.skills; + int ystr = you.strength, + yint = you.intel, + ydex = you.dex; + int yxp = you.experience_level; + + for (int i = SK_FIGHTING; i < NUM_SKILLS; ++i) + you.skills[i] = 0; + + you.experience_level = Options.fsim_xl; + if (you.experience_level < 1) + you.experience_level = 1; + if (you.experience_level > 27) + you.experience_level = 27; + + you.strength = cap_stat(Options.fsim_str); + you.intel = cap_stat(Options.fsim_int); + you.dex = cap_stat(Options.fsim_dex); + + fsim_title(ostat, mindex, missile_slot); + for (int wskill = 0; wskill <= 27; ++wskill) + { + mesclr(); + mprf("Calculating average damage for %s at skill %d", + fsim_weapon(missile_slot).c_str(), wskill); + if (!debug_fight_simulate(ostat, wskill, mindex, missile_slot)) + goto done_combat_sim; + + fflush(ostat); + // Not checking in the combat loop itself; that would be more responsive + // for the user, but slow down the sim with all the calls to kbhit(). + if (kbhit() && getch() == 27) + { + success = false; + mprf("Canceling simulation\n"); + goto done_combat_sim; + } + } + you.skills = skill_backup; + you.strength = ystr; + you.intel = yint; + you.dex = ydex; + you.experience_level = yxp; + + mprf("Done fight simulation with %s", fsim_weapon(missile_slot).c_str()); + +done_combat_sim: + fprintf(ostat, "-----------------------------------\n\n"); + fclose(ostat); + + return (success); +} + +int fsim_kit_equip(const std::string &kit) +{ + int missile_slot = -1; + char item_buf[ITEMNAME_SIZE]; + + std::string::size_type ammo_div = kit.find("/"); + std::string weapon = kit; + std::string missile; + if (ammo_div != std::string::npos) + { + weapon = kit.substr(0, ammo_div); + missile = kit.substr(ammo_div + 1); + trim_string(weapon); + trim_string(missile); + } + + for (int i = 0; i < ENDOFPACK; ++i) + { + if (!is_valid_item(you.inv[i])) + continue; + + in_name(i, DESC_PLAIN, item_buf, true); + if (std::string(item_buf).find(weapon) != std::string::npos) + { + if (i != you.equip[EQ_WEAPON]) + { + wield_weapon(true, i, false); + if (i != you.equip[EQ_WEAPON]) + return -100; + } + break; + } + } + + if (!missile.empty()) + { + for (int i = 0; i < ENDOFPACK; ++i) + { + if (!is_valid_item(you.inv[i])) + continue; + + in_name(i, DESC_PLAIN, item_buf, true); + if (std::string(item_buf).find(missile) != std::string::npos) + { + missile_slot = i; + break; + } + } + } + + return (missile_slot); +} + +// Writes statistics about a fight to fight.stat in the current directory. +// For fight purposes, a punching bag is summoned and given lots of hp, and the +// average damage the player does to the p. bag over 10000 hits is noted, +// advancing the weapon skill from 0 to 27, and keeping fighting skill to 2/5 +// of current weapon skill. +void debug_fight_statistics(bool use_defaults) +{ + int punching_bag = get_monster_by_name(Options.fsim_mons); + if (punching_bag == -1 || punching_bag == MONS_PROGRAM_BUG) + punching_bag = MONS_WORM; + + int mindex = create_fsim_monster(punching_bag, 500); + if (mindex == -1) + { + mprf("Failed to create punching bag"); + return; + } + + if (!use_defaults) + { + debug_fight_sim(mindex, -1); + goto fsim_mcleanup; + } + + for (int i = 0, size = Options.fsim_kit.size(); i < size; ++i) + { + int missile = fsim_kit_equip(Options.fsim_kit[i]); + if (missile == -100) + { + mprf("Aborting sim on %s", Options.fsim_kit[i].c_str()); + goto fsim_mcleanup; + } + if (!debug_fight_sim(mindex, missile)) + break; + } +fsim_mcleanup: + monster_die(&menv[mindex], KILL_DISMISSED, 0); +} + +static int find_trap_slot() +{ + for (int i = 0; i < MAX_TRAPS; ++i) + { + if (env.trap[i].type == TRAP_UNASSIGNED) + return (i); + } + return (-1); +} + +void debug_make_trap() +{ + char requested_trap[80]; + int trap_slot = find_trap_slot(); + trap_type trap = TRAP_UNASSIGNED; + int gridch = grd[you.x_pos][you.y_pos]; + + if (trap_slot == -1) + { + mpr("Sorry, this level can't take any more traps."); + return; + } + + if (gridch != DNGN_FLOOR) + { + mpr("You need to be on a floor square to make a trap."); + return; + } + + mprf(MSGCH_PROMPT, "What kind of trap? "); + get_input_line( requested_trap, sizeof( requested_trap ) ); + if (!*requested_trap) + return; + + strlwr(requested_trap); + for (int t = TRAP_DART; t < NUM_TRAPS; ++t) + { + if (strstr(requested_trap, + trap_name(trap_type(t)))) + { + trap = trap_type(t); + break; + } + } + + if (trap == TRAP_UNASSIGNED) + { + mprf("I know no traps named \"%s\"", requested_trap); + return; + } + + place_specific_trap(you.x_pos, you.y_pos, trap); + + mprf("Created a %s trap, marked it undiscovered", + trap_name(trap)); + + // Also tell travel that its world-view must change. + travel_init_new_level(); +} + +static const char *shop_types[] = { + "weapon", + "armour", + "antique weapon", + "antique armour", + "antiques", + "jewellery", + "wand", + "book", + "food", + "distillery", + "scroll", + "general" +}; + +void debug_make_shop() +{ + char requested_shop[80]; + int gridch = grd[you.x_pos][you.y_pos]; + bool have_shop_slots = false; + int new_shop_type = SHOP_UNASSIGNED; + bool representative = false; + + if (gridch != DNGN_FLOOR) + { + mpr("Insufficient floor-space for new Wal-Mart."); + return; + } + + for (int i = 0; i < MAX_SHOPS; ++i) + { + if (env.shop[i].type == SHOP_UNASSIGNED) + { + have_shop_slots = true; + break; + } + } + + if (!have_shop_slots) + { + mpr("There are too many shops on this level."); + return; + } + + mprf(MSGCH_PROMPT, "What kind of shop? "); + get_input_line( requested_shop, sizeof( requested_shop ) ); + if (!*requested_shop) + return; + + strlwr(requested_shop); + for (unsigned i = 0; i < sizeof(shop_types) / sizeof (*shop_types); ++i) + { + if (strstr(requested_shop, shop_types[i])) + { + new_shop_type = i; + break; + } + } + + if (new_shop_type == SHOP_UNASSIGNED) + { + mprf("Bad shop type: \"%s\"", requested_shop); + return; + } + + representative = !!strchr(requested_shop, '*'); + + place_spec_shop(you.your_level, you.x_pos, you.y_pos, + new_shop_type, representative); + link_items(); + mprf("Done."); +} + +void debug_set_stats() +{ + char buf[80]; + mprf(MSGCH_PROMPT, "Enter values for Str, Int, Dex (space separated): "); + if (cancelable_get_line(buf, sizeof buf)) + return; + + int sstr = you.strength, + sdex = you.dex, + sint = you.intel; + sscanf(buf, "%d %d %d", &sstr, &sint, &sdex); + + you.max_strength = you.strength = cap_stat(sstr); + you.max_dex = you.dex = cap_stat(sdex); + you.max_intel = you.intel = cap_stat(sint); + + you.redraw_strength = true; + you.redraw_dexterity = true; + you.redraw_intelligence = true; +} + +#endif diff --git a/crawl-ref/source/debug.h b/crawl-ref/source/debug.h index cbc8161d3b..922d4c3a7d 100644 --- a/crawl-ref/source/debug.h +++ b/crawl-ref/source/debug.h @@ -3,6 +3,8 @@ * Summary: Debug and wizard related functions. * Written by: Linley Henzell and Jesse Jones * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <4> 5/30/99 JDJ Added synch checks. @@ -143,5 +145,9 @@ void stethoscope(int mwh); void debug_item_scan( void ); void debug_get_religion( void ); void debug_change_species( void ); +void debug_fight_statistics( bool use_init_defaults ); +void debug_make_trap( void ); +void debug_make_shop( void ); +void debug_set_stats( void ); #endif diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc index 5f958b0512..8755413256 100644 --- a/crawl-ref/source/decks.cc +++ b/crawl-ref/source/decks.cc @@ -2,6 +2,8 @@ * File: decks.cc * Summary: Functions with decks of cards. * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -224,7 +226,7 @@ void deck_of_cards(unsigned char which_deck) unsigned char *card = deck_of_wonders; unsigned char max_card = 0; int i = 0; - int brownie_points = 0; // for passing to done_good() {dlb} + int brownie_points = 0; // for passing to did_god_conduct() {dlb} mpr("You draw a card..."); @@ -292,7 +294,7 @@ void deck_of_cards(unsigned char which_deck) if (which_deck == DECK_OF_WONDERS || one_chance_in(3)) brownie_points++; - done_good(GOOD_CARDS, brownie_points); + did_god_conduct(DID_CARDS, brownie_points); } return; @@ -774,7 +776,7 @@ static void cards(unsigned char which_card) case CARD_TORMENT: mpr("You have drawn the Symbol of Torment."); - torment( you.x_pos, you.y_pos ); + torment( TORMENT_CARDS, you.x_pos, you.y_pos ); break; // what about checking whether there are items there, too? {dlb} @@ -800,36 +802,20 @@ static void cards(unsigned char which_card) case CARD_ALTAR: mpr("You have drawn the Altar."); - if (you.religion == GOD_NO_GOD) + if (you.religion == GOD_NO_GOD || + grd[you.x_pos][you.y_pos] != DNGN_FLOOR) { canned_msg(MSG_NOTHING_HAPPENS); } else { dvar1 = 179 + you.religion; - - if (grd[you.x_pos][you.y_pos] == DNGN_FLOOR) - { - strcpy(info, "An altar grows from the floor "); - strcat(info, - (you.species == SP_NAGA || you.species == SP_CENTAUR) - ? "before you!" : "at your feet!"); - mpr(info); - grd[you.x_pos][you.y_pos] = dvar1; - } - else - { - do - { - dvar[0] = 10 + random2(GXM - 20); - dvar[1] = 10 + random2(GYM - 20); - } - while (grd[dvar[0]][dvar[1]] != DNGN_FLOOR); - - grd[dvar[0]][dvar[1]] = dvar1; - - mpr( "You sense divine power!" ); - } + + snprintf( info, INFO_SIZE, "An altar grows from the floor %s!", + (you.species == SP_NAGA || you.species == SP_CENTAUR) + ? "before you" : "at your feet"); + mpr(info); + grd[you.x_pos][you.y_pos] = dvar1; } break; diff --git a/crawl-ref/source/decks.h b/crawl-ref/source/decks.h index 1227ee19f1..43f41e0395 100644 --- a/crawl-ref/source/decks.h +++ b/crawl-ref/source/decks.h @@ -2,6 +2,8 @@ * File: decks.cc * Summary: Functions with decks of cards. * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created diff --git a/crawl-ref/source/defines.h b/crawl-ref/source/defines.h index dbb079a458..b36afc1ad3 100644 --- a/crawl-ref/source/defines.h +++ b/crawl-ref/source/defines.h @@ -10,6 +10,8 @@ * * Copyright © 1999 Brian Robinson. // Me? How come? * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <4> 7/29/00 JDJ Renamed MNG NON_MONSTER, MNST MAX_MONSTERS, ITEMS MAX_ITEMS, @@ -22,11 +24,12 @@ #ifndef DEFINES_H #define DEFINES_H +#define NUM_MONSTER_SPELL_SLOTS 6 + #define ESCAPE '\x1b' // most ansi-friendly way I can think of defining this. // there's got to be a better way... -#ifdef _LIBUNIX_IMPLEMENTATION -#elif macintosh +#ifndef _LIBUNIX_IMPLEMENTATION #else #ifndef TRUE #define TRUE 1 @@ -41,6 +44,9 @@ // max size of inventory array {dlb}: #define ENDOFPACK 52 +// minimum value for strength required on armour and weapons +#define STR_REQ_THRESHOLD 10 + // max size of monter array {dlb}: #define MAX_MONSTERS 200 // number of monster enchantments @@ -48,6 +54,8 @@ // non-monster for mgrd[][] -- (MNST + 1) {dlb}: #define NON_MONSTER 201 +#define MAX_SUBTYPES 50 + // max size of item list {dlb}: #define MAX_ITEMS 500 // non-item -- (ITEMS + 1) {dlb} @@ -64,6 +72,55 @@ // max y-bound for level generation {dlb} #define GYM 70 +// this is the size of the border around the playing area (see in_bounds()) +#define BOUNDARY_BORDER 1 + +// Now some defines about the actual play area: +// Note: these boundaries are exclusive for the zone the player can move/dig, +// and are inclusive for the area that we display on the map. +// Note: that the right (bottom) boundary is one smaller here. +#define X_BOUND_1 BOUNDARY_BORDER +#define X_BOUND_2 (GXM - BOUNDARY_BORDER) +#define X_WIDTH (X_BOUND_2 - X_BOUND_1 + 1) + +#define Y_BOUND_1 BOUNDARY_BORDER +#define Y_BOUND_2 (GYM - BOUNDARY_BORDER) +#define Y_WIDTH (Y_BOUND_2 - Y_BOUND_1 + 1) + +// these mark the center zone where the player moves without shifting +#define ABYSS_SHIFT_RADIUS 10 + +#define X_ABYSS_1 (X_BOUND_1 + ABYSS_SHIFT_RADIUS) +#define X_ABYSS_2 (GXM - X_ABYSS_1) +#define X_ABYSS_WIDTH (X_ABYSS_2 - X_ABYSS_1 + 1) +#define X_ABYSS_CENTER (X_ABYSS_1 + X_ABYSS_WIDTH / 2) + +#define Y_ABYSS_1 (Y_BOUND_1 + ABYSS_SHIFT_RADIUS) +#define Y_ABYSS_2 (GYM - Y_ABYSS_1) +#define Y_ABYSS_WIDTH (Y_ABYSS_2 - Y_ABYSS_1 + 1) +#define Y_ABYSS_CENTER (Y_ABYSS_1 + Y_ABYSS_WIDTH / 2) + +#define LOS_SX 8 +#define LOS_EX 25 +#define LOS_SY 1 +#define LOS_EY 17 + +#define VIEW_SX 1 +#define VIEW_EX 33 +#define VIEW_SY 1 +#define VIEW_EY 17 + +#define VIEW_WIDTH (VIEW_EX - VIEW_SX + 1) +#define VIEW_HEIGHT (VIEW_EY - VIEW_SY + 1) + +#define VIEW_Y_DIFF (((VIEW_EX - VIEW_SX + 1) - (VIEW_EY - VIEW_SY + 1)) / 2) + +// View centre must be the same as LOS centre. +// VIEW_CX == 17 +#define VIEW_CX ((VIEW_SX + VIEW_EX) / 2) +// VIEW_CY == 9 +#define VIEW_CY ((VIEW_SY + VIEW_EY) / 2) + // max traps per level #define MAX_TRAPS 30 @@ -76,6 +133,69 @@ // lowest grid value which can be seen through #define MINSEE 11 +// This value is used to make test_hit checks always succeed +#define AUTOMATIC_HIT 1500 + +// grids that monsters can see +#define MONSTER_LOS_RANGE 8 + +// Maximum charge level for rods +#define MAX_ROD_CHARGE 17 +#define ROD_CHARGE_MULT 100 + +// Should never exceed 255 - durations are saved as single bytes. +#define GOURMAND_MAX 200 +#define GOURMAND_NUTRITION_BASE 20 + +#define CHUNK_BASE_NUTRITION 1000 + +// This value is used to mark immune levels of MR +#define MAG_IMMUNE 5000 + +// This is the damage amount used to signal insta-death +#define INSTANT_DEATH -9999 + +// grids that monsters can see +#define MONSTER_LOS_RANGE 8 + +// most items allowed in a shop +#define MAX_SHOP_ITEMS 16 + +// sound level standards +// mininum is the base, we add mult * radius to it: +#define SL_EXPLODE_MIN 10 +#define SL_EXPLODE_MULT 10 + +// #define SL_BOW 3 +#define SL_TRAP_CLICK 3 +#define SL_HISS 6 +#define SL_BUZZ 6 +#define SL_GROWL 8 +#define SL_MOAN 8 +#define SL_SPLASH 8 +#define SL_CREAK 8 +#define SL_CROAK 8 +#define SL_BARK 10 +#define SL_YELL 10 +#define SL_TRAP_JAM 12 +#define SL_SHRIEK 12 +#define SL_ROAR 15 +#define SL_DIG 15 +#define SL_NOISY_WEAPON 20 +#define SL_HORN 25 +#define SL_NOISE_SCROLL 30 +#define SL_THUNDER 30 +#define SL_PROJECTED_NOISE 30 +#define SL_EARTHQUAKE 30 +#define SL_TRAP_ZOT 30 + +// Maximum enchantment on weapons/armour/secondary armours +// Note: use armour_max_enchant(item) to get the correct limit for item +#define MAX_WPN_ENCHANT 5 +#define MAX_ARM_ENCHANT 5 +#define MAX_SEC_ENCHANT 2 + +#define NUM_STAVE_ADJ 9 // some shortcuts: #define menv env.mons @@ -84,15 +204,6 @@ #define mgrd env.mgrid #define igrd env.igrid -#define ENVF_FLAGS 0xFF00U -#define ENVF_DETECT_MONS 0x0100U -#define ENVF_DETECT_ITEM 0x0200U - -// This square is known because of detect-creatures/detect-items -#define ENVF_DETECTED 0x0800U - -#define ENVF_COLOR(x) (((x) >> 12) & 0xF) - // (MNG) -- for a reason! see usage {dlb}: #define MHITNOT 201 // (MNG + 1) -- for a reason! see usage {dlb}: @@ -139,6 +250,8 @@ #define COLFLAG_WILLSTAB 0x0400 #define COLFLAG_MAYSTAB 0x0800 + #define COLFLAG_REVERSE 0x1000 + enum CHAR_ATTRIBUTES { CHATTR_NORMAL, /* 0 */ @@ -157,12 +270,18 @@ //#endif +#define PDESCS(colour) (colour) +#define PDESCQ(qualifier, colour) (((qualifier) * PDC_NCOLOURS) + (colour)) + +#define PCOLOUR(desc) ((desc) % PDC_NCOLOURS) +#define PQUAL(desc) ((desc) / PDC_NCOLOURS) + #define MINIMUM( xxx, yyy ) (((xxx) < (yyy)) ? (xxx) : (yyy)) #define MAXIMUM( xxx, yyy ) (((xxx) > (yyy)) ? (xxx) : (yyy)) // Convert capital letters into mystic numbers representing // CTRL sequences. This is a macro because a lot of the type // it wants to be used in case labels. -#define CONTROL( xxx ) (xxx - 'A' + 1) +#define CONTROL( xxx ) ((xxx) - 'A' + 1) #endif diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc index 0894207395..6998a3df50 100644 --- a/crawl-ref/source/delay.cc +++ b/crawl-ref/source/delay.cc @@ -2,6 +2,8 @@ * File: delay.cc * Summary: Functions for handling multi-turn actions. * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> Sept 09, 2001 BWR Created @@ -13,40 +15,111 @@ #include #include +#include "clua.h" +#include "command.h" #include "delay.h" #include "enum.h" #include "food.h" +#include "invent.h" #include "items.h" #include "itemname.h" +#include "itemprop.h" #include "item_use.h" #include "it_use2.h" #include "message.h" #include "misc.h" #include "monstuff.h" +#include "mstuff2.h" #include "ouch.h" #include "output.h" #include "player.h" #include "randart.h" #include "spl-util.h" #include "stuff.h" +#include "travel.h" + +extern std::vector items_for_multidrop; + +static void armour_wear_effects(const int item_inv_slot); +static void handle_run_delays(const delay_queue_item &delay); +static void handle_macro_delay(); + +// Returns true if this delay can act as a parent to other delays, i.e. if +// other delays can be spawned while this delay is running. If is_parent_delay +// returns true, new delays will be pushed immediately to the front of the +// delay in question, rather than at the end of the queue. +static bool is_parent_delay(int delay) +{ + // Interlevel travel can do upstairs/downstairs delays. + // Lua macros can in theory perform any of the other delays, + // including travel; in practise travel still assumes there can be + // no parent delay. + return (delay == DELAY_TRAVEL || delay == DELAY_MACRO); +} + +static void push_delay(const delay_queue_item &delay) +{ + for (delay_queue_type::iterator i = you.delay_queue.begin(); + i != you.delay_queue.end(); + ++i) + { + if (is_parent_delay( i->type )) + { + you.delay_queue.insert(i, delay); + return; + } + } + you.delay_queue.push_back( delay ); +} + +static void pop_delay() +{ + if (!you.delay_queue.empty()) + you.delay_queue.erase( you.delay_queue.begin() ); +} + +static void clear_pending_delays() +{ + while (you.delay_queue.size() > 1) + you.delay_queue.pop_back(); +} void start_delay( int type, int turns, int parm1, int parm2 ) /***********************************************************/ { - delay_queue_item delay; + delay_queue_item delay; delay.type = type; delay.duration = turns; delay.parm1 = parm1; delay.parm2 = parm2; - you.delay_queue.push( delay ); + switch ( delay.type ) { + case DELAY_ARMOUR_ON: + mpr("You start putting on your armour.", MSGCH_MULTITURN_ACTION); + break; + case DELAY_ARMOUR_OFF: + mpr("You start removing your armour.", MSGCH_MULTITURN_ACTION); + break; + case DELAY_MEMORISE: + mpr("You start memorising the spell.", MSGCH_MULTITURN_ACTION); + break; + case DELAY_PASSWALL: + mpr("You begin to meditate on the wall.", MSGCH_MULTITURN_ACTION); + break; + default: + break; + } + push_delay( delay ); } void stop_delay( void ) /*********************/ { - delay_queue_item delay = you.delay_queue.front(); + if ( you.delay_queue.empty() ) + return; + + delay_queue_item delay = you.delay_queue.front(); // At the very least we can remove any queued delays, right // now there is no problem with doing this... note that @@ -54,42 +127,60 @@ void stop_delay( void ) // as the effect of a delay doesn't normally allow interaction // until it is done... it merely chains up individual actions // into a single action. -- bwr - if (you.delay_queue.size() > 1) - { - while (you.delay_queue.size()) - you.delay_queue.pop(); - - you.delay_queue.push( delay ); - } + clear_pending_delays(); switch (delay.type) { case DELAY_BUTCHER: // Corpse keeps track of work in plus2 field, see handle_delay() -- bwr mpr( "You stop butchering the corpse." ); - you.delay_queue.pop(); + pop_delay(); break; - case DELAY_MEMORIZE: + case DELAY_MEMORISE: // Losing work here is okay... having to start from // scratch is a reasonable behaviour. -- bwr mpr( "Your memorization is interrupted." ); - you.delay_queue.pop(); + pop_delay(); break; case DELAY_PASSWALL: // The lost work here is okay since this spell requires - // the player to "attune to the rock". If changed, the + // the player to "attune to the rock". If changed, then // the delay should be increased to reduce the power of // this spell. -- bwr mpr( "Your meditation is interrupted." ); - you.delay_queue.pop(); + pop_delay(); + break; + + case DELAY_MULTIDROP: + // No work lost + mpr( "You stop dropping stuff." ); + pop_delay(); + break; + + case DELAY_RUN: + case DELAY_REST: + case DELAY_TRAVEL: + case DELAY_MACRO: + // Always interruptible. + pop_delay(); + + // Keep things consistent, otherwise disturbing phenomena can occur. + // Note that runrest::stop() will turn around and call stop_delay() + // again, but that's okay because the delay is already popped off + // the queue. + if (is_run_delay(delay.type) && you.running) + stop_running(); + + // There's no special action needed for macros - if we don't call out + // to the Lua function, it can't do damage. break; - case DELAY_INTERUPTABLE: - // always stopable by definition... + case DELAY_INTERRUPTIBLE: + // always stoppable by definition... // try using a more specific type anyways. -- bwr - you.delay_queue.pop(); + pop_delay(); break; case DELAY_EAT: @@ -101,7 +192,7 @@ void stop_delay( void ) case DELAY_ARMOUR_ON: case DELAY_ARMOUR_OFF: - // These two have the default action of not being interuptable, + // These two have the default action of not being interruptible, // although they will often be chained (remove cloak, remove // armour, wear new armour, replace cloak), all of which can // be stopped when complete. This is a fairly reasonable @@ -117,7 +208,8 @@ void stop_delay( void ) case DELAY_DROP_ITEM: // one turn... only used for easy armour drops case DELAY_ASCENDING_STAIRS: // short... and probably what people want case DELAY_DESCENDING_STAIRS: // short... and probably what people want - case DELAY_UNINTERUPTABLE: // never stopable + case DELAY_UNINTERRUPTIBLE: // never stoppable + case DELAY_JEWELLERY_ON: // one turn default: break; } @@ -136,414 +228,767 @@ int current_delay_action( void ) : DELAY_NOT_DELAYED); } +bool is_run_delay(int delay) +{ + return (delay == DELAY_RUN || delay == DELAY_REST + || delay == DELAY_TRAVEL); +} + void handle_delay( void ) /***********************/ { - int ego; char str_pass[ ITEMNAME_SIZE ]; - if (you_are_delayed()) + if (!you_are_delayed()) + return; + + delay_queue_item &delay = you.delay_queue.front(); + + // Run delays and Lua delays don't have a specific end time. + if (is_run_delay(delay.type)) { - delay_queue_item &delay = you.delay_queue.front(); + handle_run_delays(delay); + return; + } - // First check cases where delay may no longer be valid: - // XXX: need to handle passwall when monster digs -- bwr - if (delay.type == DELAY_BUTCHER) + if (delay.type == DELAY_MACRO) + { + handle_macro_delay(); + return; + } + + // First check cases where delay may no longer be valid: + // XXX: need to handle passwall when monster digs -- bwr + if (delay.type == DELAY_BUTCHER) + { + // A monster may have raised the corpse you're chopping up! -- bwr + // Note that a monster could have raised the corpse and another + // monster could die and create a corpse with the same ID number... + // However, it would not be at the player's square like the + // original and that's why we do it this way. Note that + // we ignore the conversion to skeleton possibility just to + // be nice. -- bwr + if (is_valid_item( mitm[ delay.parm1 ] ) + && mitm[ delay.parm1 ].base_type == OBJ_CORPSES + && mitm[ delay.parm1 ].x == you.x_pos + && mitm[ delay.parm1 ].y == you.y_pos ) { - // A monster may have raised the corpse you're chopping up! -- bwr - // Note that a monster could have raised the corpse and another - // monster could die and create a corpse with the same ID number... - // However, it would not be at the player's square like the - // original and that's why we do it this way. Note that - // we ignore the conversion to skeleton possiblity just to - // be nice. -- bwr - if (is_valid_item( mitm[ delay.parm1 ] ) - && mitm[ delay.parm1 ].base_type == OBJ_CORPSES - && mitm[ delay.parm1 ].x == you.x_pos - && mitm[ delay.parm1 ].y == you.y_pos) - { - // mark work done on the corpse in case we stop -- bwr - mitm[ delay.parm1 ].plus2++; - } - else - { - // corpse is no longer valid! - stop_delay(); - return; + // special < 100 is the rottenness check + if ( (mitm[delay.parm1].special < 100) && + (delay.parm2 >= 100) ) { + mpr("The corpse rots.", MSGCH_ROTTEN_MEAT); + delay.parm2 = 99; // don't give the message twice } + + // mark work done on the corpse in case we stop -- bwr + mitm[ delay.parm1 ].plus2++; + } + else + { + // corpse is no longer valid! + stop_delay(); + return; } + } + if ( delay.type == DELAY_MULTIDROP ) + { + + // Throw away invalid items; items usually go invalid because + // of chunks rotting away. + while (!items_for_multidrop.empty() + // Don't look for gold in inventory + && items_for_multidrop[0].slot != PROMPT_GOT_SPECIAL + && !is_valid_item(you.inv[ items_for_multidrop[0].slot ])) + items_for_multidrop.erase( items_for_multidrop.begin() ); - // Handle delay: - if (delay.duration > 0) + if ( items_for_multidrop.empty() ) { + // ran out of things to drop + pop_delay(); + return; + } + } + + // Handle delay: + if (delay.duration > 0) + { #if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, "Delay type: %d duration: %d", - delay.type, delay.duration ); + snprintf( info, INFO_SIZE, "Delay type: %d (%s), duration: %d", + delay.type, delay_name(delay.type), delay.duration ); - mpr( info, MSGCH_DIAGNOSTICS ); + mpr( info, MSGCH_DIAGNOSTICS ); #endif - delay.duration--; + switch ( delay.type ) + { + case DELAY_ARMOUR_ON: + in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass ); + snprintf( info, INFO_SIZE, + "You continue putting on %s.", str_pass ); + mpr(info, MSGCH_MULTITURN_ACTION); + break; + case DELAY_ARMOUR_OFF: + in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass ); + snprintf( info, INFO_SIZE, + "You continue taking off %s.", str_pass ); + mpr(info, MSGCH_MULTITURN_ACTION); + break; + case DELAY_BUTCHER: + mpr("You continue butchering the corpse.", + MSGCH_MULTITURN_ACTION); + break; + case DELAY_MEMORISE: + mpr("You continue memorising.", MSGCH_MULTITURN_ACTION); + break; + case DELAY_PASSWALL: + mpr("You continue meditating on the rock.", + MSGCH_MULTITURN_ACTION); + break; + case DELAY_MULTIDROP: + drop_item( items_for_multidrop[0].slot, + items_for_multidrop[0].quantity ); + items_for_multidrop.erase( items_for_multidrop.begin() ); + break; + default: + break; } - else + delay.duration--; + } + else + { + switch (delay.type) { - switch (delay.type) - { - case DELAY_AUTOPICKUP: - break; + case DELAY_AUTOPICKUP: + break; - case DELAY_WEAPON_SWAP: - weapon_switch( delay.parm1 ); - break; + case DELAY_WEAPON_SWAP: + weapon_switch( delay.parm1 ); + break; - case DELAY_ARMOUR_ON: - set_ident_flags( you.inv[ delay.parm1 ], ISFLAG_EQ_ARMOUR_MASK ); + case DELAY_JEWELLERY_ON: + puton_ring( delay.parm1, false ); + break; - in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass ); - snprintf( info, INFO_SIZE, "You finish putting on %s.", str_pass ); - mpr(info); + case DELAY_ARMOUR_ON: + armour_wear_effects( delay.parm1 ); + break; + + case DELAY_ARMOUR_OFF: + { + in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass ); + snprintf( info, INFO_SIZE, "You finish taking off %s.", str_pass ); + mpr(info); - if (you.inv[ delay.parm1 ].sub_type < ARM_SHIELD - || you.inv[ delay.parm1 ].sub_type > ARM_LARGE_SHIELD) + const equipment_type slot = + get_armour_slot( you.inv[delay.parm1] ); + + if (slot == EQ_BODY_ARMOUR) + { + you.equip[EQ_BODY_ARMOUR] = -1; + } + else + { + switch (slot) { - you.equip[EQ_BODY_ARMOUR] = delay.parm1; + case EQ_SHIELD: + if (delay.parm1 == you.equip[EQ_SHIELD]) + you.equip[EQ_SHIELD] = -1; + break; - if (you.duration[DUR_ICY_ARMOUR] != 0) - { - mpr( "Your icy armour melts away.", MSGCH_DURATION ); - you.redraw_armour_class = 1; - you.duration[DUR_ICY_ARMOUR] = 0; - } + case EQ_CLOAK: + if (delay.parm1 == you.equip[EQ_CLOAK]) + you.equip[EQ_CLOAK] = -1; + break; + + case EQ_HELMET: + if (delay.parm1 == you.equip[EQ_HELMET]) + you.equip[EQ_HELMET] = -1; + break; + + + case EQ_GLOVES: + if (delay.parm1 == you.equip[EQ_GLOVES]) + you.equip[EQ_GLOVES] = -1; + break; + + case EQ_BOOTS: + if (delay.parm1 == you.equip[EQ_BOOTS]) + you.equip[EQ_BOOTS] = -1; + break; + + default: + break; } - else + } + + unwear_armour( delay.parm1 ); + + you.redraw_armour_class = 1; + you.redraw_evasion = 1; + break; + } + case DELAY_EAT: + mpr( "You finish eating." ); + // For chunks, warn the player if they're not getting much + // nutrition. + if (delay.parm1) + chunk_nutrition_message(delay.parm1); + break; + + case DELAY_MEMORISE: + mpr( "You finish memorising." ); + add_spell_to_memory( delay.parm1 ); + break; + + case DELAY_PASSWALL: + { + mpr( "You finish merging with the rock." ); + more(); // or the above message won't be seen + + const int pass_x = delay.parm1; + const int pass_y = delay.parm2; + + if (pass_x != 0 && pass_y != 0) { - switch (you.inv[ delay.parm1 ].sub_type) + + switch (grd[ pass_x ][ pass_y ]) { - case ARM_BUCKLER: - case ARM_LARGE_SHIELD: - case ARM_SHIELD: - if (you.duration[DUR_CONDENSATION_SHIELD]) - { - mpr( "Your icy shield evaporates.", MSGCH_DURATION ); - you.duration[DUR_CONDENSATION_SHIELD] = 0; - } - you.equip[EQ_SHIELD] = delay.parm1; - break; - case ARM_CLOAK: - you.equip[EQ_CLOAK] = delay.parm1; - break; - case ARM_HELMET: - you.equip[EQ_HELMET] = delay.parm1; + case DNGN_ROCK_WALL: + case DNGN_STONE_WALL: + case DNGN_METAL_WALL: + case DNGN_GREEN_CRYSTAL_WALL: + case DNGN_WAX_WALL: + case DNGN_SILVER_STATUE: + case DNGN_ORANGE_CRYSTAL_STATUE: + ouch(1 + you.hp, 0, KILLED_BY_PETRIFICATION); break; - case ARM_GLOVES: - you.equip[EQ_GLOVES] = delay.parm1; + + case DNGN_SECRET_DOOR: // oughtn't happen + case DNGN_CLOSED_DOOR: // open the door + grd[ pass_x ][ pass_y ] = DNGN_OPEN_DOOR; break; - case ARM_BOOTS: - you.equip[EQ_BOOTS] = delay.parm1; + + default: break; } - } - ego = get_armour_ego_type( you.inv[ delay.parm1 ] ); - if (ego != SPARM_NORMAL) - { - switch (ego) + // move any monsters out of the way: + int mon = mgrd[ pass_x ][ pass_y ]; + if (mon != NON_MONSTER) { - case SPARM_RUNNING: - strcpy(info, "You feel quick"); - strcat(info, (you.species == SP_NAGA - || you.species == SP_CENTAUR) ? "." : " on your feet."); - mpr(info); - break; + // one square, a few squares, anywhere... + if (!shift_monster(&menv[mon]) + && !monster_blink(&menv[mon])) + { + monster_teleport( &menv[mon], true, true ); + } + } - case SPARM_FIRE_RESISTANCE: - mpr("You feel resistant to fire."); - break; + move_player_to_grid(pass_x, pass_y, false, true, true); + redraw_screen(); + } + } + break; - case SPARM_COLD_RESISTANCE: - mpr("You feel resistant to cold."); - break; + case DELAY_BUTCHER: + strcpy( info, "You finish " ); + strcat( info, (you.species == SP_TROLL + || you.species == SP_GHOUL) ? "ripping" + : "chopping" ); - case SPARM_POISON_RESISTANCE: - mpr("You feel healthy."); - break; + strcat( info, " the corpse into pieces." ); + mpr( info ); - case SPARM_SEE_INVISIBLE: - mpr("You feel perceptive."); - break; + turn_corpse_into_chunks( mitm[ delay.parm1 ] ); - case SPARM_DARKNESS: - if (!you.invis) - mpr("You become transparent for a moment."); - break; + if (you.berserker && you.berserk_penalty != NO_BERSERK_PENALTY) + { + mpr("You enjoyed that."); + you.berserk_penalty = 0; + } + break; - case SPARM_STRENGTH: - modify_stat(STAT_STRENGTH, 3, false); - break; + case DELAY_DROP_ITEM: + // Note: checking if item is droppable is assumed to + // be done before setting up this delay... this includes + // quantity (delay.parm2). -- bwr - case SPARM_DEXTERITY: - modify_stat(STAT_DEXTERITY, 3, false); - break; + // Make sure item still exists. + if (!is_valid_item( you.inv[ delay.parm1 ] )) + break; - case SPARM_INTELLIGENCE: - modify_stat(STAT_INTELLIGENCE, 3, false); - break; + // Must handle unwield_item before we attempt to copy + // so that temporary brands and such are cleared. -- bwr + if (delay.parm1 == you.equip[EQ_WEAPON]) + { + unwield_item( delay.parm1 ); + you.equip[EQ_WEAPON] = -1; + canned_msg( MSG_EMPTY_HANDED ); + } - case SPARM_PONDEROUSNESS: - mpr("You feel rather ponderous."); - // you.speed += 2; - you.redraw_evasion = 1; - break; + if (!copy_item_to_grid( you.inv[ delay.parm1 ], + you.x_pos, you.y_pos, delay.parm2, + true )) + { + mpr("Too many items on this level, not dropping the item."); + } + else + { + quant_name( you.inv[ delay.parm1 ], delay.parm2, + DESC_NOCAP_A, str_pass ); - case SPARM_LEVITATION: - mpr("You feel rather light."); - break; + snprintf( info, INFO_SIZE, "You drop %s.", str_pass ); + mpr(info); - case SPARM_MAGIC_RESISTANCE: - mpr("You feel resistant to magic."); - break; + dec_inv_item_quantity( delay.parm1, delay.parm2 ); + } + break; + + case DELAY_ASCENDING_STAIRS: + up_stairs(); + untag_followers(); + break; + + case DELAY_DESCENDING_STAIRS: + down_stairs( false, delay.parm1 ); + untag_followers(); + break; + + case DELAY_INTERRUPTIBLE: + case DELAY_UNINTERRUPTIBLE: + // these are simple delays that have no effect when complete + break; + + default: + mpr( "You finish doing something." ); + break; + } - case SPARM_PROTECTION: - mpr("You feel protected."); - break; + you.wield_change = true; + print_stats(); // force redraw of the stats + you.turn_is_over = true; + pop_delay(); + } +} - case SPARM_STEALTH: - mpr("You feel stealthy."); - break; +static void armour_wear_effects(const int item_slot) +{ + item_def &arm = you.inv[item_slot]; - case SPARM_RESISTANCE: - mpr("You feel resistant to extremes of temperature."); - break; + set_ident_flags(arm, ISFLAG_EQ_ARMOUR_MASK ); + mprf("You finish putting on %s.", item_name(arm, DESC_NOCAP_YOUR)); - case SPARM_POSITIVE_ENERGY: - mpr("Your life-force is being protected."); - break; + const equipment_type eq_slot = get_armour_slot(arm); - case SPARM_ARCHMAGI: - if (!you.skills[SK_SPELLCASTING]) - mpr("You feel strangely numb."); - else - mpr("You feel extremely powerful."); - break; - } - } + if (eq_slot == EQ_BODY_ARMOUR) + { + you.equip[EQ_BODY_ARMOUR] = item_slot; - if (is_random_artefact( you.inv[ delay.parm1 ] )) - use_randart( delay.parm1 ); + if (you.duration[DUR_ICY_ARMOUR] != 0) + { + mpr( "Your icy armour melts away.", MSGCH_DURATION ); + you.redraw_armour_class = 1; + you.duration[DUR_ICY_ARMOUR] = 0; + } + } + else + { + switch (eq_slot) + { + case EQ_SHIELD: + if (you.duration[DUR_CONDENSATION_SHIELD]) + { + mpr( "Your icy shield evaporates.", MSGCH_DURATION ); + you.duration[DUR_CONDENSATION_SHIELD] = 0; + } + you.equip[EQ_SHIELD] = item_slot; + break; + case EQ_CLOAK: + you.equip[EQ_CLOAK] = item_slot; + break; + case EQ_HELMET: + you.equip[EQ_HELMET] = item_slot; + break; + case EQ_GLOVES: + you.equip[EQ_GLOVES] = item_slot; + break; + case EQ_BOOTS: + you.equip[EQ_BOOTS] = item_slot; + break; + default: + break; + } + } - if (item_cursed( you.inv[ delay.parm1 ] )) - mpr( "Oops, that feels deathly cold." ); + int ego = get_armour_ego_type( arm ); + if (ego != SPARM_NORMAL) + { + switch (ego) + { + case SPARM_RUNNING: + mprf("You feel quick%s.", + (you.species == SP_NAGA || you.species == SP_CENTAUR) + ? "" : " on your feet"); + break; + + case SPARM_FIRE_RESISTANCE: + mpr("You feel resistant to fire."); + break; + + case SPARM_COLD_RESISTANCE: + mpr("You feel resistant to cold."); + break; + + case SPARM_POISON_RESISTANCE: + mpr("You feel healthy."); + break; + + case SPARM_SEE_INVISIBLE: + mpr("You feel perceptive."); + break; + + case SPARM_DARKNESS: + if (!you.invis) + mpr("You become transparent for a moment."); + break; + + case SPARM_STRENGTH: + modify_stat(STAT_STRENGTH, 3, false); + break; + + case SPARM_DEXTERITY: + modify_stat(STAT_DEXTERITY, 3, false); + break; + + case SPARM_INTELLIGENCE: + modify_stat(STAT_INTELLIGENCE, 3, false); + break; + + case SPARM_PONDEROUSNESS: + mpr("You feel rather ponderous."); + // you.speed += 2; + you.redraw_evasion = 1; + break; + + case SPARM_LEVITATION: + mpr("You feel rather light."); + break; + + case SPARM_MAGIC_RESISTANCE: + mpr("You feel resistant to magic."); + break; + + case SPARM_PROTECTION: + mpr("You feel protected."); + break; + + case SPARM_STEALTH: + mpr("You feel stealthy."); + break; + + case SPARM_RESISTANCE: + mpr("You feel resistant to extremes of temperature."); + break; + + case SPARM_POSITIVE_ENERGY: + mpr("Your life-force is being protected."); + break; + + case SPARM_ARCHMAGI: + if (!you.skills[SK_SPELLCASTING]) + mpr("You feel strangely numb."); + else + mpr("You feel extremely powerful."); + break; + } + } - you.redraw_armour_class = 1; - you.redraw_evasion = 1; - break; + if (is_random_artefact( arm )) + use_randart( item_slot ); - case DELAY_ARMOUR_OFF: - in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass ); - snprintf( info, INFO_SIZE, "You finish taking off %s.", str_pass ); - mpr(info); + if (item_cursed( arm )) + mpr( "Oops, that feels deathly cold." ); - if (you.inv[ delay.parm1 ].sub_type < ARM_SHIELD - || you.inv[ delay.parm1 ].sub_type > ARM_LARGE_SHIELD) - { - you.equip[EQ_BODY_ARMOUR] = -1; - } - else - { - switch (you.inv[ delay.parm1 ].sub_type) - { - case ARM_BUCKLER: - case ARM_LARGE_SHIELD: - case ARM_SHIELD: - if (delay.parm1 == you.equip[EQ_SHIELD]) - you.equip[EQ_SHIELD] = -1; - break; + warn_shield_penalties(); - case ARM_CLOAK: - if (delay.parm1 == you.equip[EQ_CLOAK]) - you.equip[EQ_CLOAK] = -1; - break; + you.redraw_armour_class = 1; + you.redraw_evasion = 1; +} - case ARM_HELMET: - if (delay.parm1 == you.equip[EQ_HELMET]) - you.equip[EQ_HELMET] = -1; - break; +static command_type get_running_command() { + if ( kbhit() ) { + stop_running(); + return CMD_NO_CMD; + } + if ( is_resting() ) { + you.running.rest(); + return CMD_MOVE_NOWHERE; + } + return direction_to_command( you.running.x, you.running.y ); +} +static void handle_run_delays(const delay_queue_item &delay) +{ + // Handle inconsistencies between the delay queue and you.running. + // We don't want to send the game into a deadlock. + if (!you.running) + { + pop_delay(); + return; + } - case ARM_GLOVES: - if (delay.parm1 == you.equip[EQ_GLOVES]) - you.equip[EQ_GLOVES] = -1; - break; + ASSERT( !you.turn_is_over ); - case ARM_BOOTS: - if (delay.parm1 == you.equip[EQ_BOOTS]) - you.equip[EQ_BOOTS] = -1; - break; - } - } + command_type cmd = CMD_NO_CMD; + switch (delay.type) + { + case DELAY_REST: + case DELAY_RUN: + cmd = get_running_command(); + break; + case DELAY_TRAVEL: + cmd = travel(); + break; + } - unwear_armour( delay.parm1 ); + if (cmd != CMD_NO_CMD) + { + mesclr(); + process_command(cmd); + } - you.redraw_armour_class = 1; - you.redraw_evasion = 1; - break; + // If you.running has gone to zero, and the run delay was not + // removed, remove it now. This is needed to clean up after + // find_travel_pos() function in travel.cc. + if (!you.running && is_run_delay(current_delay_action())) + pop_delay(); +} - case DELAY_EAT: - mpr( "You finish eating." ); - break; +static void handle_macro_delay() +{ + run_macro(); +} - case DELAY_MEMORIZE: - mpr( "You finish memorising." ); - add_spell_to_memory( delay.parm1 ); - break; +void run_macro(const char *macroname) +{ + const int currdelay = current_delay_action(); + if (currdelay != DELAY_NOT_DELAYED && currdelay != DELAY_MACRO) + return; - case DELAY_PASSWALL: - { - mpr( "You finish merging with the rock." ); - more(); // or the above message won't be seen +#ifdef CLUA_BINDINGS + if (!clua) + { + mpr("Lua not initialized", MSGCH_DIAGNOSTICS); + stop_delay(); + return; + } - const int pass_x = delay.parm1; - const int pass_y = delay.parm2; + if (!clua.callbooleanfn(false, "c_macro", "s", macroname)) + { + if (clua.error.length()) + mpr(clua.error.c_str()); - if (pass_x != 0 && pass_y != 0) - { + stop_delay(); + } + else + { + start_delay(DELAY_MACRO, 1); + } +#else + stop_delay(); +#endif +} - switch (grd[ pass_x ][ pass_y ]) - { - case DNGN_ROCK_WALL: - case DNGN_STONE_WALL: - case DNGN_METAL_WALL: - case DNGN_GREEN_CRYSTAL_WALL: - case DNGN_WAX_WALL: - case DNGN_SILVER_STATUE: - case DNGN_ORANGE_CRYSTAL_STATUE: - ouch(1 + you.hp, 0, KILLED_BY_PETRIFICATION); - break; - - case DNGN_SECRET_DOOR: // oughtn't happen - case DNGN_CLOSED_DOOR: // open the door - grd[ pass_x ][ pass_y ] = DNGN_OPEN_DOOR; - break; - - default: - break; - } +// Returns true if the delay should be interrupted, false if the user function +// had no opinion on the matter, -1 if the delay should not be interrupted. +static int userdef_interrupt_activity( const delay_queue_item &idelay, + activity_interrupt_type ai, + const activity_interrupt_data &at ) +{ +#ifdef CLUA_BINDINGS + const int delay = idelay.type; + lua_State *ls = clua.state(); + if (!ls || ai == AI_FORCE_INTERRUPT) + return (true); + + // Kludge: We have to continue to support ch_stop_run. :-( + if (is_run_delay(delay) && you.running && ai == AI_SEE_MONSTER) + { + bool stop_run = false; + if (clua.callfn("ch_stop_run", "M>b", + (const monsters *) at.data, &stop_run)) + { + if (stop_run) + return (true); - //jmf: hmm, what to do. kill the monster? (seems too powerful) - // displace the monster? randomly teleport the monster? - // This seems fair: try to move the monster, but if not - // able to, then kill it. - int mon = mgrd[ pass_x ][ pass_y ]; - if (mon != NON_MONSTER) - { - monster_blink( &menv[ mon ] ); + // No further processing. + return (-1); + } - // recheck square for monster - mon = mgrd[ pass_x ][ pass_y ]; - if (mon != NON_MONSTER) - monster_die( &menv[ mon ], KILL_YOU, 0 ); - } + // If we get here, ch_stop_run wasn't defined, fall through to the + // other handlers. + } + + const char *interrupt_name = activity_interrupt_name(ai); + const char *act_name = delay_name(delay); - you.x_pos = pass_x; - you.y_pos = pass_y; - redraw_screen(); + bool ran = clua.callfn("c_interrupt_activity", "1:ssA", + act_name, interrupt_name, &at); + if (ran) + { + // If the function returned nil, we want to cease processing. + if (lua_isnil(ls, -1)) + { + lua_pop(ls, 1); + return (-1); + } - const unsigned char grid = grd[ you.x_pos ][ you.y_pos ]; - if ((grid == DNGN_LAVA || grid == DNGN_DEEP_WATER) - && !player_is_levitating()) - { - if (you.species == SP_MERFOLK && grid == DNGN_DEEP_WATER) - { - mpr("You fall into the water and return " - "to your normal form."); - merfolk_start_swimming(); - } - else - { - fall_into_a_pool( true, grid ); - redraw_screen(); - } - } - } - } - break; + bool stopact = lua_toboolean(ls, -1); + lua_pop(ls, 1); + if (stopact) + return (true); + } - case DELAY_BUTCHER: - strcpy( info, "You finish " ); - strcat( info, (you.species == SP_TROLL - || you.species == SP_GHOUL) ? "ripping" - : "chopping" ); + if (delay == DELAY_MACRO && + clua.callbooleanfn(true, "c_interrupt_macro", + "sA", interrupt_name, &at)) + return (true); - strcat( info, " the corpse into pieces." ); - mpr( info ); +#endif + return (false); +} - turn_corpse_into_chunks( mitm[ delay.parm1 ] ); +// Returns true if the activity should be interrupted, false otherwise. +static bool should_stop_activity(const delay_queue_item &item, + activity_interrupt_type ai, + const activity_interrupt_data &at) +{ + int userd = userdef_interrupt_activity(item, ai, at); - if (you.berserker && you.berserk_penalty != NO_BERSERK_PENALTY) - { - mpr("You enjoyed that."); - you.berserk_penalty = 0; - } - break; + // If the user script wanted to stop the activity or cease processing, + // do so. + if (userd) + return (userd == 1); - case DELAY_DROP_ITEM: - // Note: checking if item is dropable is assumed to - // be done before setting up this delay... this includes - // quantity (delay.parm2). -- bwr + return (ai == AI_FORCE_INTERRUPT || + (Options.activity_interrupts[item.type][ai])); +} - // Make sure item still exists. - if (!is_valid_item( you.inv[ delay.parm1 ] )) - break; +void interrupt_activity( activity_interrupt_type ai, + const activity_interrupt_data &at ) +{ + const int delay = current_delay_action(); + if (delay == DELAY_NOT_DELAYED) + return; - // Must handle unwield_item before we attempt to copy - // so that temporary brands and such are cleared. -- bwr - if (delay.parm1 == you.equip[EQ_WEAPON]) - { - unwield_item( delay.parm1 ); - you.equip[EQ_WEAPON] = -1; - canned_msg( MSG_EMPTY_HANDED ); - } + // First try to stop the current delay. + const delay_queue_item &item = you.delay_queue.front(); + + if (should_stop_activity(item, ai, at)) + { + stop_delay(); + return; + } - if (!copy_item_to_grid( you.inv[ delay.parm1 ], - you.x_pos, you.y_pos, delay.parm2 )) + // Check the other queued delays; the first delay that is interruptible + // will kill itself and all subsequent delays. This is so that a travel + // delay stacked behind a delay such as stair/autopickup will be killed + // correctly by interrupts that the simple stair/autopickup delay ignores. + for (int i = 1, size = you.delay_queue.size(); i < size; ++i) + { + const delay_queue_item &it = you.delay_queue[i]; + if (should_stop_activity(it, ai, at)) + { + // Do we have a queued run delay? If we do, flush the delay queue + // so that stop running Lua notifications happen. + for (int j = i; j < size; ++j) + { + if (is_run_delay( you.delay_queue[j].type )) { - mpr("Too many items on this level, not dropping the item."); + stop_delay(); + return; } - else - { - quant_name( you.inv[ delay.parm1 ], delay.parm2, - DESC_NOCAP_A, str_pass ); + } - snprintf( info, INFO_SIZE, "You drop %s.", str_pass ); - mpr(info); + // Non-run queued delays can be discarded without any processing. + you.delay_queue.erase( you.delay_queue.begin() + i, + you.delay_queue.end() ); + break; + } + } +} - dec_inv_item_quantity( delay.parm1, delay.parm2 ); - } - break; +static const char *activity_interrupt_names[] = +{ + "force", "keypress", "full_hp", "full_mp", "statue", + "hungry", "message", "hp_loss", "burden", "stat", + "monster", "monster_attack", "teleport" +}; - case DELAY_ASCENDING_STAIRS: - up_stairs(); - untag_followers(); - break; +const char *activity_interrupt_name(activity_interrupt_type ai) +{ + ASSERT( sizeof(activity_interrupt_names) + / sizeof(*activity_interrupt_names) == NUM_AINTERRUPTS ); - case DELAY_DESCENDING_STAIRS: - down_stairs( false, delay.parm1 ); - untag_followers(); - break; + if (ai == NUM_AINTERRUPTS) + return (""); - case DELAY_INTERUPTABLE: - case DELAY_UNINTERUPTABLE: - // these are simple delays that have no effect when complete - break; + return activity_interrupt_names[ai]; +} - default: - mpr( "You finish doing something." ); - break; - } +activity_interrupt_type get_activity_interrupt(const std::string &name) +{ + ASSERT( sizeof(activity_interrupt_names) + / sizeof(*activity_interrupt_names) == NUM_AINTERRUPTS ); - you.wield_change = true; - print_stats(); // force redraw of the stats - you.turn_is_over = 1; - you.delay_queue.pop(); - } + for (int i = 0; i < NUM_AINTERRUPTS; ++i) + if (name == activity_interrupt_names[i]) + return activity_interrupt_type(i); + + return (NUM_AINTERRUPTS); +} + +static const char *delay_names[] = +{ + "not_delayed", "eat", "armour_on", "armour_off", "jewellery_on", + "memorise", "butcher", "autopickup", "weapon_swap", "passwall", + "drop_item", "multidrop", "ascending_stairs", "descending_stairs", "run", + "rest", "travel", "macro", "interruptible", "uninterruptible", +}; + +// Gets a delay given its name. +// name must be lowercased already! +delay_type get_delay(const std::string &name) +{ + ASSERT( sizeof(delay_names) / sizeof(*delay_names) == NUM_DELAYS ); + + for (int i = 0; i < NUM_DELAYS; ++i) + { + if (name == delay_names[i]) + return delay_type(i); } + + // Also check American spellings: + if (name == "armor_on") + return (DELAY_ARMOUR_ON); + + if (name == "armor_off") + return (DELAY_ARMOUR_OFF); + + if (name == "memorize") + return (DELAY_MEMORISE); + + if (name == "jewelry_on") + return (DELAY_JEWELLERY_ON); + + return (NUM_DELAYS); +} + +const char *delay_name(int delay) +{ + ASSERT( sizeof(delay_names) / sizeof(*delay_names) == NUM_DELAYS ); + + if (delay < 0 || delay >= NUM_DELAYS) + return (""); + + return delay_names[delay]; } diff --git a/crawl-ref/source/delay.h b/crawl-ref/source/delay.h index e171e355c8..778a14eeca 100644 --- a/crawl-ref/source/delay.h +++ b/crawl-ref/source/delay.h @@ -2,6 +2,8 @@ * File: delay.h * Summary: Functions for handling multi-turn actions. * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> 09/09/01 BWR Created @@ -10,10 +12,24 @@ #ifndef DELAY_H #define DELAY_H +#include "enum.h" + void start_delay( int type, int turns, int parm1 = 0, int parm2 = 0 ); void stop_delay( void ); bool you_are_delayed( void ); int current_delay_action( void ); void handle_delay( void ); +bool is_run_delay(int delay); +const char *activity_interrupt_name(activity_interrupt_type ai); +activity_interrupt_type get_activity_interrupt(const std::string &); + +const char *delay_name(int delay); +delay_type get_delay(const std::string &); + +void perform_activity(); +void interrupt_activity( activity_interrupt_type ai, + const activity_interrupt_data &a + = activity_interrupt_data() ); + #endif diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index 173e9b0ed4..f2608ed7ac 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -3,6 +3,8 @@ * Summary: Functions used to print information about various game objects. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <4> 10/14/99 BCR enummed describe_god() @@ -33,6 +35,7 @@ #include "debug.h" #include "fight.h" #include "itemname.h" +#include "itemprop.h" #include "macro.h" #include "mon-util.h" #include "player.h" @@ -41,7 +44,6 @@ #include "skills2.h" #include "spl-book.h" #include "stuff.h" -#include "wpn-misc.h" #include "spl-util.h" @@ -112,7 +114,8 @@ static void print_description( const std::string &d ) if (nextLine >= currentPos && nextLine < currentPos + lineWidth) { - cprintf((d.substr(currentPos, nextLine - currentPos)).c_str()); + cprintf("%s", + (d.substr(currentPos, nextLine - currentPos)).c_str()); currentPos = nextLine + 1; continue; } @@ -331,6 +334,19 @@ static void randart_descpr( std::string &description, const item_def &item ) } } +static const char *trap_names[] = +{ + "dart", "arrow", "spear", "axe", + "teleport", "amnesia", "blade", + "bolt", "zot", "needle", +}; + +const char *trap_name(trap_type trap) +{ + if (trap >= TRAP_DART && trap < NUM_TRAPS) + return trap_names[ static_cast( trap ) ]; + return (NULL); +} //--------------------------------------------------------------- // @@ -682,7 +698,7 @@ static std::string describe_demon(void) // describe_weapon // //--------------------------------------------------------------- -static std::string describe_weapon( const item_def &item, char verbose) +static std::string describe_weapon( const item_def &item, bool verbose) { std::string description; @@ -785,7 +801,7 @@ static std::string describe_weapon( const item_def &item, char verbose) } else { - if (verbose == 1) + if (verbose) { switch (item.sub_type) { @@ -872,6 +888,12 @@ static std::string describe_weapon( const item_def &item, char verbose) "and a skilled user can use it to great effect. "; break; + case WPN_LONGBOW: + description += "A long, strong bow made of yew. " + "It does excellent damage in combat " + "and a skilled archer can use it to great effect. "; + break; + case WPN_BLOWGUN: description += "A long, light tube, open at both ends. Doing " "very little damage, its main use is to fire poisoned " @@ -926,6 +948,17 @@ static std::string describe_weapon( const item_def &item, char verbose) "single-edged blade. "; break; + case WPN_LAJATANG: + description += "A very rare and extremely effective " + "imported weapon, featuring a pole with half-moon blades " + "at both ends. "; + break; + + case WPN_LOCHABER_AXE: + description += "An enormous combination of a pike " + "and a battle axe."; + break; + case WPN_EXECUTIONERS_AXE: description += "A huge axe. "; break; @@ -962,6 +995,10 @@ static std::string describe_weapon( const item_def &item, char verbose) "A terrible weapon, forged in the fires of Hell. "; break; + case WPN_BLESSED_BLADE: + description += "A blade blessed by the Shining One. "; + break; + case WPN_DEMON_WHIP: description += "A terrible weapon, woven " "in the depths of the inferno. "; @@ -989,8 +1026,8 @@ static std::string describe_weapon( const item_def &item, char verbose) description += "A large and heavy mace. "; break; - case WPN_GREAT_FLAIL: - description += "A large and heavy flail. "; + case WPN_DIRE_FLAIL: + description += "A flail with spiked lumps on both ends."; break; case WPN_FALCHION: @@ -1005,9 +1042,9 @@ static std::string describe_weapon( const item_def &item, char verbose) } } - if (verbose == 1 && !launches_things( item.sub_type )) + if (verbose) { - description += "$Damage rating: "; + description += "$Damage rating: "; append_value(description, property( item, PWPN_DAMAGE ), false); description += "$Accuracy rating: "; @@ -1015,7 +1052,7 @@ static std::string describe_weapon( const item_def &item, char verbose) description += "$Base attack delay: "; append_value(description, property( item, PWPN_SPEED ) * 10, false); - description += "%%"; + description += "%"; } description += "$"; @@ -1023,7 +1060,7 @@ static std::string describe_weapon( const item_def &item, char verbose) { int spec_ench = get_weapon_brand( item ); - if (!is_random_artefact( item ) && verbose == 0) + if (!is_random_artefact( item ) && !verbose) spec_ench = SPWPN_NORMAL; // special weapon descrip @@ -1061,7 +1098,7 @@ static std::string describe_weapon( const item_def &item, char verbose) "all of orcish descent. "; break; case SPWPN_VENOM: - if (launches_things( item.sub_type )) + if (is_range_weapon(item)) description += "It poisons the unbranded ammo it fires. "; else description += "It poisons the flesh of those it strikes. "; @@ -1075,7 +1112,7 @@ static std::string describe_weapon( const item_def &item, char verbose) "it drains the life of those it strikes. "; break; case SPWPN_SPEED: - if (launches_things( item.sub_type )) + if (is_range_weapon(item)) { description += "It allows its wielder to fire twice when " "they would otherwise have fired only once. "; @@ -1087,7 +1124,16 @@ static std::string describe_weapon( const item_def &item, char verbose) } break; case SPWPN_VORPAL: - description += "It inflicts extra damage upon your enemies. "; + if (is_range_weapon(item)) + { + description += "Any "; + description += ammo_name( item ); + description += " fired from it inflicts extra damage."; + } + else + { + description += "It inflicts extra damage upon your enemies. "; + } break; case SPWPN_FLAME: description += "It turns projectiles fired from it into " @@ -1147,7 +1193,7 @@ static std::string describe_weapon( const item_def &item, char verbose) description += "$It has a curse placed upon it."; } - if (verbose == 1 && !launches_things( item.sub_type )) + if (verbose && !is_range_weapon(item)) { #ifdef USE_NEW_COMBAT_STATS const int str_weight = weapon_str_weight( item.base_type, item.sub_type ); @@ -1162,18 +1208,21 @@ static std::string describe_weapon( const item_def &item, char verbose) description += "$This weapon is better for the dexterous."; #endif - switch (hands_reqd_for_weapon(item.base_type, item.sub_type)) + switch (hands_reqd(item, player_size())) { - case HANDS_ONE_HANDED: + case HANDS_ONE: description += "$It is a one handed weapon."; break; - case HANDS_ONE_OR_TWO_HANDED: + case HANDS_HALF: description += "$It can be used with one hand, or more " "effectively with two (i.e. when not using a shield)."; break; - case HANDS_TWO_HANDED: + case HANDS_TWO: description += "$It is a two handed weapon."; break; + default: + description += "$It is a buggy weapon."; + break; } } @@ -1186,7 +1235,7 @@ static std::string describe_weapon( const item_def &item, char verbose) break; } - if (launches_things( item.sub_type )) + if (is_range_weapon(item)) { switch (get_equip_race( item )) { @@ -1206,7 +1255,7 @@ static std::string describe_weapon( const item_def &item, char verbose) } } - if (verbose == 1) + if (verbose) { description += "$It falls into the"; @@ -1216,6 +1265,7 @@ static std::string describe_weapon( const item_def &item, char verbose) description += " 'slings' category. "; break; case WPN_BOW: + case WPN_LONGBOW: description += " 'bows' category. "; break; case WPN_HAND_CROSSBOW: @@ -1290,7 +1340,7 @@ static std::string describe_ammo( const item_def &item ) case MI_LARGE_ROCK: description += "A rock, used by giants as a missile. "; break; - case MI_EGGPLANT: + case MI_NONE: // was eggplant description += "A purple vegetable. " "The presence of this object in the game " "indicates a bug (or some kind of cheating on your part). "; @@ -1330,7 +1380,7 @@ static std::string describe_ammo( const item_def &item ) // describe_armour // //--------------------------------------------------------------- -static std::string describe_armour( const item_def &item, char verbose ) +static std::string describe_armour( const item_def &item, bool verbose ) { std::string description; @@ -1345,7 +1395,7 @@ static std::string describe_armour( const item_def &item, char verbose ) } else { - if (verbose == 1) + if (verbose) { switch (item.sub_type) { @@ -1403,16 +1453,21 @@ static std::string describe_armour( const item_def &item, char verbose ) case ARM_GLOVES: description += "A pair of gloves. "; break; + + case ARM_CENTAUR_BARDING: + description += "An armour made for centaurs, " + "to wear over their equine half."; + break; + + case ARM_NAGA_BARDING: + description += "A special armour made for nagas, " + "to wear over their tails."; + break; + case ARM_BOOTS: - if (item.plus2 == TBOOT_NAGA_BARDING) - description += "A special armour made for Nagas, " - "to wear over their tails. "; - else if (item.plus2 == TBOOT_CENTAUR_BARDING) - description += "An armour made for centaurs, " - "to wear over their equine half. "; - else - description += "A pair of sturdy boots. "; + description += "A pair of boots."; break; + case ARM_BUCKLER: description += "A small shield. "; break; @@ -1543,7 +1598,7 @@ static std::string describe_armour( const item_def &item, char verbose ) } } - if (verbose == 1 + if (verbose && item.sub_type != ARM_SHIELD && item.sub_type != ARM_BUCKLER && item.sub_type != ARM_LARGE_SHIELD) @@ -1557,7 +1612,8 @@ static std::string describe_armour( const item_def &item, char verbose ) // caps and wizard hats don't have a base AC append_value(description, 0, false); } - else if (item.sub_type == ARM_BOOTS && item.plus2 != TBOOT_BOOTS) + else if (item.sub_type == ARM_NAGA_BARDING + || item.sub_type == ARM_CENTAUR_BARDING) { // Barding has AC value 4. append_value(description, 4, false); @@ -1575,7 +1631,7 @@ static std::string describe_armour( const item_def &item, char verbose ) int ego = get_armour_ego_type( item ); if (ego != SPARM_NORMAL && item_ident( item, ISFLAG_KNOW_TYPE ) - && verbose == 1) + && verbose) { description += "$"; @@ -1794,7 +1850,8 @@ static std::string describe_stick( const item_def &item ) case WAND_DISINTEGRATION: description += "disrupts the physical structure of " - "an object, especially a creature's body. "; + "anything but the hardest walls -- even rigid " + "statues, to say nothing of flesh. "; break; default: @@ -2422,7 +2479,7 @@ static std::string describe_scroll( const item_def &item ) // describe_jewellery // //--------------------------------------------------------------- -static std::string describe_jewellery( const item_def &item, char verbose) +static std::string describe_jewellery( const item_def &item, bool verbose) { std::string description; @@ -2437,11 +2494,11 @@ static std::string describe_jewellery( const item_def &item, char verbose) else if ((!is_random_artefact( item ) && get_ident_type( OBJ_JEWELLERY, item.sub_type ) != ID_KNOWN_TYPE) || (is_random_artefact( item ) - && item_not_ident( item, ISFLAG_KNOW_TYPE ))) + && !item_ident( item, ISFLAG_KNOW_TYPE ))) { description += "A piece of jewellery."; } - else if (verbose == 1 || is_random_artefact( item )) + else if (verbose || is_random_artefact( item )) { switch (item.sub_type) { @@ -2623,22 +2680,28 @@ static std::string describe_jewellery( const item_def &item, char verbose) case AMU_WARDING: description += "This amulet repels some of the attacks of creatures " - "which have been magically summoned. "; + "which have been magically summoned, and also " + "makes the wearer more resistant to draining attacks. "; break; case AMU_RESIST_CORROSION: description += - "This amulet protects the armour and weaponry of its " - "wearer from corrosion caused by acids, although not " + "This amulet protects the wearer and their equipment " + "from corrosion caused by acids, although not " "infallibly so. "; break; case AMU_THE_GOURMAND: description += + "This amulet allows its wearer to digest raw meat " + "even when not hungry. Its effects on the wearer's digestion " + "and palate are cumulative over time, and are initially small."; + /* "This amulet allows its wearer to consume meat in " "various states of decay without suffering unduly as " "a result. Poisonous or cursed flesh is still not " "recommended. "; + */ break; case AMU_CONSERVATION: @@ -2676,7 +2739,7 @@ static std::string describe_jewellery( const item_def &item, char verbose) description += "$"; } - if ((verbose == 1 || is_random_artefact( item )) + if ((verbose || is_random_artefact( item )) && item_ident( item, ISFLAG_KNOW_PLUSES )) { // Explicit description of ring power (useful for randarts) @@ -2845,11 +2908,12 @@ static std::string describe_staff( const item_def &item ) description += "allows its wielder to smite foes from afar. The wielder " "must be at least level four to safely use this ability, " - "which costs 4 magic points. "; + "which drains four charges. "; break; case STAFF_STRIKING: - description += "allows its wielder to strike foes from afar. "; + description += "allows its wielder to strike foes from afar " + "with force bolts. "; break; case STAFF_SPELL_SUMMONING: @@ -2893,13 +2957,16 @@ static std::string describe_staff( const item_def &item ) if (item_is_rod( item )) { - description += - "Casting a spell from it consumes no food, and will not fail.$"; + if (item.sub_type != STAFF_STRIKING) + description += + "$$It uses its own mana reservoir for casting spells, and " + "recharges automatically by channeling mana from its " + "wielder."; } else { description += - "$$Damage rating: 7 $Accuracy rating: +6 $Attack delay: 120%%"; + "$$Damage rating: 7 $Accuracy rating: +6 $Attack delay: 120%"; description += "$$It falls into the 'staves' category. "; } @@ -3094,15 +3161,11 @@ static std::string describe_misc_item( const item_def &item ) return (description); } -#if MAC -#pragma mark - -#endif - // ======================================================================== // Public Functions // ======================================================================== -bool is_dumpable_artifact( const item_def &item, char verbose) +bool is_dumpable_artifact( const item_def &item, bool verbose) { bool ret = false; @@ -3111,13 +3174,13 @@ bool is_dumpable_artifact( const item_def &item, char verbose) ret = item_ident( item, ISFLAG_KNOW_PROPERTIES ); } else if (item.base_type == OBJ_ARMOUR - && (verbose == 1 && item_ident( item, ISFLAG_KNOW_TYPE ))) + && (verbose && item_ident( item, ISFLAG_KNOW_TYPE ))) { const int spec_ench = get_armour_ego_type( item ); ret = (spec_ench >= SPARM_RUNNING && spec_ench <= SPARM_PRESERVATION); } else if (item.base_type == OBJ_JEWELLERY - && (verbose == 1 + && (verbose && get_ident_type(OBJ_JEWELLERY, item.sub_type) == ID_KNOWN_TYPE)) { ret = true; @@ -3135,7 +3198,7 @@ bool is_dumpable_artifact( const item_def &item, char verbose) // be interpreted as carriage returns. // //--------------------------------------------------------------- -std::string get_item_description( const item_def &item, char verbose, bool dump ) +std::string get_item_description( const item_def &item, bool verbose, bool dump ) { std::string description; description.reserve(500); @@ -3236,22 +3299,22 @@ std::string get_item_description( const item_def &item, char verbose, bool dump description += "This item should not exist. Mayday! Mayday! "; } - if (verbose == 1) + if (verbose) { description += "$It weighs around "; - const int mass = mass_item( item ); + const int mass = item_mass( item ); char item_mass[16]; itoa( mass / 10, item_mass, 10 ); for (int i = 0; i < 14; i++) { - if (item_mass[i] == '\0') + if (item_mass[i] == 0) { item_mass[i] = '.'; item_mass[i+1] = (mass % 10) + '0'; - item_mass[i+2] = '\0'; + item_mass[i+2] = 0; break; } } @@ -3274,11 +3337,9 @@ std::string get_item_description( const item_def &item, char verbose, bool dump void describe_item( const item_def &item ) { #ifdef DOS_TERM - char buffer[3400]; - - gettext(25, 1, 80, 25, buffer); - - window(25, 1, 80, 25); + char buffer[4000]; + gettext(1, 1, 80, 25, buffer); + // window(25, 1, 80, 25); #endif clrscr(); @@ -3306,8 +3367,7 @@ void describe_item( const item_def &item ) getch(); #ifdef DOS_TERM - puttext(25, 1, 80, 25, buffer); - window(1, 1, 80, 25); + puttext(1, 1, 80, 25, buffer); #endif } // end describe_item() @@ -3326,10 +3386,9 @@ void describe_spell(int spelled) description.reserve(500); #ifdef DOS_TERM - char buffer[3400]; - - gettext(25, 1, 80, 25, buffer); - window(25, 1, 80, 25); + char buffer[4000]; + gettext(1, 1, 80, 25, buffer); + // window(25, 1, 80, 25); #endif clrscr(); @@ -4110,7 +4169,8 @@ void describe_spell(int spelled) case SPELL_AIRSTRIKE: // jet planes in Crawl ??? {dlb} description += "causes the air around a creature to twist itself into " - "a whirling vortex of meteorological fury. "; + "a whirling vortex of meteorological fury. This spell " + "is especially effective against flying enemies."; break; case SPELL_SHADOW_CREATURES: @@ -4325,14 +4385,6 @@ void describe_spell(int spelled) "corporeal undead. "; break; - case SPELL_SHUGGOTH_SEED: - description += "implants a shuggoth seed, the larval parasitic form " - "of the fearsome shuggoth, in a living host. The " - "shuggoth seed will draw life from its host and then " - "hatch, whereupon a fully grown shuggoth will burst " - "from the unfortunate host's chest. "; - break; - case SPELL_MAXWELLS_SILVER_HAMMER: description += "bestows a lethal but temporary gravitic field " "to a crushing implement held by the caster. " @@ -4371,6 +4423,11 @@ void describe_spell(int spelled) "can be difficult to control. "; break; + case SPELL_CHAIN_LIGHTNING: + description += "releases a massive electrical discharge that " + "arcs from target to target until it grounds out."; + break; + case SPELL_TWIST: description += "causes a slight spatial distortion around a monster " "in line of sight of the caster, causing injury. "; @@ -4421,7 +4478,7 @@ void describe_spell(int spelled) getch(); #ifdef DOS_TERM - puttext(25, 1, 80, 25, buffer); + puttext(1, 1, 80, 25, buffer); window(1, 1, 80, 25); #endif } // end describe_spell() @@ -4441,14 +4498,14 @@ void describe_monsters(int class_described, unsigned char which_mons) description.reserve(200); #ifdef DOS_TERM - char buffer[3400]; - - gettext(25, 1, 80, 25, buffer); - window(25, 1, 80, 25); + char buffer[4000]; + gettext(1, 1, 80, 25, buffer); + // window(25, 1, 80, 25); #endif clrscr(); - description = std::string( ptr_monam( &(menv[ which_mons ]), DESC_CAP_A ) ); + + description = ptr_monam( &(menv[ which_mons ]), DESC_CAP_A ); description += "$$"; switch (class_described) @@ -4835,6 +4892,10 @@ void describe_monsters(int class_described, unsigned char which_mons) description += "It smells horrible."; break; + case MONS_DEATH_DRAKE: + description += "A small dragon, radiating evil."; + break; + case MONS_FIREDRAKE: description += "A small dragon, puffing clouds of smoke."; break; @@ -5806,6 +5867,44 @@ void describe_monsters(int class_described, unsigned char which_mons) "A small and slimy eel, crackling with electrical discharge."; break; + case MONS_DRACONIAN: + case MONS_RED_DRACONIAN: + case MONS_WHITE_DRACONIAN: + case MONS_GREEN_DRACONIAN: + case MONS_PALE_DRACONIAN: + case MONS_MOTTLED_DRACONIAN: + case MONS_BLACK_DRACONIAN: + case MONS_YELLOW_DRACONIAN: + case MONS_PURPLE_DRACONIAN: + case MONS_DRACONIAN_SHIFTER: + case MONS_DRACONIAN_SCORCHER: + case MONS_DRACONIAN_ZEALOT: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_CALLER: + case MONS_DRACONIAN_MONK: + case MONS_DRACONIAN_KNIGHT: + { + description += "A "; + + const int subsp = draco_subspecies( &menv[which_mons] ); + switch (subsp) + { + case MONS_DRACONIAN: description += "brown "; break; + case MONS_BLACK_DRACONIAN: description += "black "; break; + case MONS_MOTTLED_DRACONIAN: description += "mottled "; break; + case MONS_YELLOW_DRACONIAN: description += "yellow "; break; + case MONS_GREEN_DRACONIAN: description += "green "; break; + case MONS_PURPLE_DRACONIAN: description += "purple "; break; + case MONS_RED_DRACONIAN: description += "red "; break; + case MONS_WHITE_DRACONIAN: description += "white "; break; + case MONS_PALE_DRACONIAN: description += "pale "; break; + default: + break; + } + + description += "scaled humanoid with wings."; + break; + } case MONS_PLAYER_GHOST: description += "The apparition of "; description += ghost_description(); @@ -6050,6 +6149,16 @@ void describe_monsters(int class_described, unsigned char which_mons) "covered in thick red scales and thorns."; break; + case MONS_ORANGE_STATUE: + description += "An intricately carved statue of glittering orange " + "crystal. Its eyes fix on yours with a piercing gaze."; + break; + + case MONS_SILVER_STATUE: + description += "A beautiful statue of silver. Its eyes " + "glow with an otherworldly radiance."; + break; + case MONS_PROGRAM_BUG: default: description += "If this monster is a \"program bug\", then it's " @@ -6062,17 +6171,9 @@ void describe_monsters(int class_described, unsigned char which_mons) #if DEBUG_DIAGNOSTICS - if (mons_flag( menv[ which_mons ].type, M_SPELLCASTER )) + if (mons_class_flag( menv[ which_mons ].type, M_SPELLCASTER )) { - int hspell_pass[6] = { MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL, - MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL }; - - int msecc = ((class_described == MONS_HELLION) ? MST_BURNING_DEVIL : - (class_described == MONS_PANDEMONIUM_DEMON) ? MST_GHOST - : menv[ which_mons ].number); - - mons_spell_list(msecc, hspell_pass); - + const monster_spells &hspell_pass = menv[which_mons].spells; bool found_spell = false; for (int i = 0; i < 6; i++) @@ -6123,7 +6224,7 @@ void describe_monsters(int class_described, unsigned char which_mons) getch(); #ifdef DOS_TERM - puttext(25, 1, 80, 25, buffer); + puttext(1, 1, 80, 25, buffer); window(1, 1, 80, 25); #endif } // end describe_monsters @@ -6212,7 +6313,7 @@ static void print_god_abil_desc( int abil ) std::string str( abil_info.name ); str += std::string( 79 - str.length() - cost.length(), ' ' ) + cost + EOL; - cprintf( str.c_str() ); + cprintf( "%s", str.c_str() ); } @@ -6257,7 +6358,7 @@ void describe_god( int which_god, bool give_title ) //mv: print god's name and title - if you can think up better titles //I have nothing against textcolor(colour); - cprintf (god_name(which_god,true)); //print long god's name + cprintf( "%s", god_name(which_god,true)); //print long god's name cprintf (EOL EOL); //mv: print god's description @@ -6268,17 +6369,19 @@ void describe_god( int which_god, bool give_title ) case GOD_ZIN: description = "Zin is an ancient and revered God, dedicated to the establishment of order" EOL "and the destruction of the forces of chaos and night. Valued worshippers " EOL - "can gain a variety of powers useful in the fight against the evil, but must" EOL - "abstain from the use of necromancy and other forms of unholy magic." EOL - "Zin appreciates long-standing faith as well as sacrifices of valued objects." EOL; + "can gain blessings on their weapons and a variety of powers useful in the" EOL + "fight against the evil, but must abstain from the use of necromancy and other" EOL + "forms of unholy magic. Zin appreciates long-standing faith as well as " EOL + "sacrifices of valued objects."; break; case GOD_SHINING_ONE: description = "The Shining One is a powerful crusading deity, allied with Zin in the fight" EOL - "against evil. Followers may be granted with the ability to summarily dispense" EOL - "the wrath of heaven, but must never use any form of evil magic and should" EOL - "fight honourably. The Shining One appreciates long-standing persistence in " EOL - "the endless crusade, as well as the dedicated destruction of unholy creatures."; + "against evil. Followers may be granted blessings on their weapons and the " EOL + "ability to summarily dispense the wrath of heaven, but must never use any " EOL + "form of evil magic and should fight honourably. The Shining One appreciates" EOL + "long-standing persistence in the endless crusade, as well as the dedicated " EOL + "destruction of unholy creatures."; break; case GOD_KIKUBAAQUDGHA: @@ -6358,7 +6461,7 @@ void describe_god( int which_god, bool give_title ) "be reported to dev-team."; } - cprintf(description); + cprintf("%s", description); //end of printing description // title only shown for our own god @@ -6372,17 +6475,17 @@ void describe_god( int which_god, bool give_title ) // based on your god if (you.piety > 160) { - cprintf((which_god == GOD_SHINING_ONE) ? "Champion of Law" : + cprintf("%s", (which_god == GOD_SHINING_ONE) ? "Champion of Law" : (which_god == GOD_ZIN) ? "Divine Warrior" : (which_god == GOD_ELYVILON) ? "Champion of Light" : - (which_god == GOD_OKAWARU) ? "Master of Thousand Battles" : + (which_god == GOD_OKAWARU) ? "Master of a Thousand Battles" : (which_god == GOD_YREDELEMNUL) ? "Master of Eternal Death" : (which_god == GOD_KIKUBAAQUDGHA) ? "Lord of Darkness" : (which_god == GOD_MAKHLEB) ? "Champion of Chaos" : (which_god == GOD_VEHUMET) ? "Lord of Destruction" : (which_god == GOD_TROG) ? "Great Slayer" : (which_god == GOD_NEMELEX_XOBEH) ? "Great Trickster" : - (which_god == GOD_SIF_MUNA) ? "Master of Arcane" : + (which_god == GOD_SIF_MUNA) ? "Master of the Arcane" : (which_god == GOD_XOM) ? "Teddy Bear" : "Bogy the Lord of the Bugs"); // Xom and no god is handled before } @@ -6443,16 +6546,13 @@ void describe_god( int which_god, bool give_title ) if (you.religion != which_god) { textcolor (colour); - snprintf( info, INFO_SIZE, - (you.penance[which_god] >= 50) ? "%s's wrath is upon you!" : + cprintf( (you.penance[which_god] >= 50) ? "%s's wrath is upon you!" : (you.penance[which_god] >= 20) ? "%s is annoyed with you." : (you.penance[which_god] >= 5) ? "%s well remembers your sins." : (you.penance[which_god] > 0) ? "%s is ready to forgive your sins." : (you.worshipped[which_god]) ? "%s is ambivalent towards you." : "%s is neutral towards you.", god_name(which_god) ); - - cprintf(info); } else { @@ -6470,32 +6570,23 @@ void describe_god( int which_god, bool give_title ) cprintf("You are ignored."); else { - snprintf( info, INFO_SIZE, - - (you.piety > 130) ? "A prized avatar of %s.": + cprintf( (you.piety > 130) ? "A prized avatar of %s.": (you.piety > 100) ? "A shining star in the eyes of %s." : (you.piety > 70) ? "A rising star in the eyes of %s." : (you.piety > 40) ? "%s is most pleased with you." : (you.piety > 20) ? "%s has noted your presence." : (you.piety > 5) ? "%s is noncommittal." - : "You are beneath notice.", - - god_name(which_god) - ); - - cprintf(info); + : "You are beneath %s's notice.", + god_name(which_god)); } } //end of favour //mv: following code shows abilities given from god (if any) - - textcolor(LIGHTGRAY); cprintf(EOL EOL "Granted powers : (Cost)" EOL); textcolor(colour); - // mv: these gods protects you during your prayer (not mentioning XOM) // chance for doing so is (random2(you.piety) >= 30) // Note that it's not depending on penance. @@ -6503,33 +6594,31 @@ void describe_god( int which_god, bool give_title ) // because god isn't really protecting player - he only sometimes // saves his life (probably it shouldn't be displayed at all). // What about this ? + bool penance_ability = false; if ((which_god == GOD_ZIN || which_god == GOD_SHINING_ONE || which_god == GOD_ELYVILON - || which_god == GOD_OKAWARU || which_god == GOD_YREDELEMNUL) && you.piety >= 30) { - snprintf( info, INFO_SIZE, - "%s %s watches over you during prayer." EOL, - god_name(which_god), - (you.piety >= 150) ? "carefully": // > 4/5 - (you.piety >= 90) ? "often" : // > 2/3 - "sometimes" // less than 2:3 - ); - - cprintf(info); + penance_ability = true; // suppress "none" later + cprintf( "%s %s watches over you during prayer." EOL, + god_name(which_god), + (you.piety >= 150) ? "carefully": // > 4/5 + (you.piety >= 90) ? "often" : // > 2/3 + "sometimes"); // less than 2:3 } // mv: No abilities (except divine protection) // under penance (fix me if I'm wrong) if (player_under_penance()) - { - cprintf( "None." EOL ); + { + if ( !penance_ability ) + cprintf( "None." EOL ); } else { - switch (which_god) //mv: finaly let's print abilities + switch (which_god) //mv: finally let's print abilities { case GOD_ZIN: if (you.piety >= 30) @@ -6563,7 +6652,7 @@ void describe_god( int which_god, bool give_title ) print_god_abil_desc( ABIL_TSO_ANNIHILATE_UNDEAD ); if (you.piety >= 100) - print_god_abil_desc( ABIL_TSO_THUNDERBOLT ); + print_god_abil_desc( ABIL_TSO_CLEANSING_FLAME ); if (you.piety >= 120) print_god_abil_desc( ABIL_TSO_SUMMON_DAEVA ); @@ -6619,9 +6708,6 @@ void describe_god( int which_god, bool give_title ) if (you.piety >= 75) cprintf( "During prayer you have some protection from summoned creatures." EOL ); - - if (you.piety >= 100) - print_god_abil_desc( ABIL_VEHUMET_CHANNEL_ENERGY ); break; @@ -6661,11 +6747,14 @@ void describe_god( int which_god, bool give_title ) break; case GOD_SIF_MUNA: - if (you.piety >= 50) - print_god_abil_desc( ABIL_SIF_MUNA_FORGET_SPELL ); + if (you.piety >= 30) + print_god_abil_desc( ABIL_SIF_MUNA_CHANNEL_ENERGY ); else cprintf( "None." EOL ); + if (you.piety >= 50) + print_god_abil_desc( ABIL_SIF_MUNA_FORGET_SPELL ); + if (you.piety >= 100) cprintf( "You are protected from some side-effects of spellcasting." EOL ); break; diff --git a/crawl-ref/source/describe.h b/crawl-ref/source/describe.h index 981f90ce01..9128c27555 100644 --- a/crawl-ref/source/describe.h +++ b/crawl-ref/source/describe.h @@ -4,6 +4,8 @@ * Summary: Functions used to print information about various game objects. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 5/21/99 BWR Changed from is_artifact to is_dumpable_artifact @@ -15,18 +17,19 @@ #include #include "externs.h" +#include "enum.h" // last updated 12may2000 {dlb} /* *********************************************************************** * called from: chardump - spells4 * *********************************************************************** */ -bool is_dumpable_artifact( const item_def &item, char verbose ); +bool is_dumpable_artifact( const item_def &item, bool verbose ); // last updated 12may2000 {dlb} /* *********************************************************************** * called from: chardump - describe * *********************************************************************** */ -std::string get_item_description( const item_def &item, char verbose, +std::string get_item_description( const item_def &item, bool verbose, bool dump = false ); // last updated 12may2000 {dlb} @@ -61,4 +64,6 @@ void describe_spell(int spelled); * *********************************************************************** */ std::string ghost_description(bool concise = false); +const char *trap_name(trap_type trap); + #endif diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc index 911ccc8522..3e7b6fd845 100644 --- a/crawl-ref/source/direct.cc +++ b/crawl-ref/source/direct.cc @@ -3,6 +3,8 @@ * Summary: Functions used when picking squares. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <5> 01/08/01 GDL complete rewrite of direction() @@ -76,7 +78,8 @@ static const char ycomp[9] = { 1, 1, 1, 0, 0, 0, -1, -1, -1 }; // [dshaligram] Removed . and 5 from dirchars so it's easier to // special case them. static const char dirchars[19] = { "b1j2n3h4bbl6y7k8u9" }; -static const char DOSidiocy[10] = { "OPQKSMGHI" }; +static const char DOSidiocy[10] = { "OPQKSMGHI" }; +static const char DOSunidiocy[10] = { "bjnh.lyku" }; static const char *aim_prompt = "Aim (move cursor or -/+/=, change mode with CTRL-F, select with . or >)"; static void describe_feature(int mx, int my, bool oos); @@ -97,6 +100,12 @@ static bool is_mapped(int x, int y) return (is_player_mapped(x, y)); } +int dos_direction_unmunge(int doskey) +{ + const char *pos = strchr(DOSidiocy, doskey); + return (pos? DOSunidiocy[ pos - DOSidiocy ] : doskey); +} + //--------------------------------------------------------------- // // direction @@ -127,7 +136,8 @@ static bool is_mapped(int x, int y) // // targetting mode is handled by look_around() //--------------------------------------------------------------- -void direction( struct dist &moves, int restrict, int mode ) + +void direction2( struct dist &moves, int restrict, int mode ) { bool dirChosen = false; bool targChosen = false; @@ -143,7 +153,7 @@ void direction( struct dist &moves, int restrict, int mode ) // XXX. this is ALWAYS in relation to the player. But a bit of a hack // nonetheless! --GDL - gotoxy( 18, 9 ); + gotoxy( VIEW_CX + 1, VIEW_CY ); int keyin = getchm(KC_TARGETING); @@ -320,13 +330,43 @@ void direction( struct dist &moves, int restrict, int mode ) moves.ty = you.y_pos + moves.dy * my; } +/* safe version of direction */ +void direction( struct dist &moves, int restrict, int mode, + bool confirm_fizzle ) +{ + while ( 1 ) + { + direction2( moves, restrict, mode ); + if ( moves.isMe && Options.confirm_self_target == true && + mode != TARG_FRIEND ) + { + if ( yesno("Really target yourself? ", false, 'n') ) + return; + else + mpr("Choose a better target.", MSGCH_PROMPT); + } + else if ( confirm_fizzle && !moves.isValid && Options.fizzlecheck_on ) + { + if ( yesno("Really fizzle? ", false, 'n') ) + return; + else + mpr("Try again.", MSGCH_PROMPT); + } + else + { + return; + } + } +} + + // Attempts to describe a square that's not in line-of-sight. If // there's a stash on the square, announces the top item and number // of items, otherwise, if there's a stair that's in the travel // cache and noted in the Dungeon (O)verview, names the stair. static void describe_oos_square(int x, int y) { - if (!is_mapped(x, y)) + if (!in_bounds(x, y) || !is_mapped(x, y)) return; #ifdef STASH_TRACKING @@ -365,8 +405,8 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) bool dirChosen = false; bool targChosen = false; int dir = 0; - int cx = 17; - int cy = 9; + int cx = VIEW_CX; + int cy = VIEW_CY; int newcx, newcy; int mx, my; // actual map x,y (scratch) int mid; // monster id (scratch) @@ -414,6 +454,11 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) if (!justLooking && keyin == ' ') keyin = '\r'; + // [dshaligram] Fudge: in targeting mode, '>' says shoot a missile + // that lands here. + if (!justLooking && keyin == '>') + keyin = 'X'; + // DOS idiocy if (keyin == 0) { @@ -441,6 +486,33 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) // handle non-directional keys switch (keyin) { +#ifdef WIZARD + case 'F': + targChosen = true; + mx = you.x_pos + cx - VIEW_CX; + my = you.y_pos + cy - VIEW_CY; + if (!in_bounds(mx, my)) + break; + mid = mgrd[mx][my]; + + if (mid == NON_MONSTER) + break; + + mprf("Changing attitude of %s\n", + ptr_monam(&menv[mid], DESC_PLAIN)); + menv[mid].attitude = + menv[mid].attitude == ATT_HOSTILE? + ATT_FRIENDLY + : ATT_HOSTILE; + + describe_monsters( menv[ mid ].type, mid ); + redraw_screen(); + mesclr( true ); + // describe the cell again. + describe_cell(view2gridX(cx), view2gridY(cy)); + break; +#endif + case CONTROL('F'): mode = (mode + 1) % TARG_NUM_MODES; @@ -519,8 +591,10 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) case '?': targChosen = true; - mx = you.x_pos + cx - 17; - my = you.y_pos + cy - 9; + mx = you.x_pos + cx - VIEW_CX; + my = you.y_pos + cy - VIEW_CY; + if (!in_bounds(mx, my)) + break; mid = mgrd[mx][my]; if (mid == NON_MONSTER) @@ -535,22 +609,23 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) redraw_screen(); mesclr( true ); // describe the cell again. - describe_cell(you.x_pos + cx - 17, you.y_pos + cy - 9); + describe_cell(view2gridX(cx), view2gridY(cy)); break; case '\r': case '\n': case '.': case '5': + case 'X': // If we're in look-around mode, and the cursor is on // the character and there's a valid travel target // within the viewport, jump to that target. - if (justLooking && cx == 17 && cy == 9) + if (justLooking && cx == VIEW_CX && cy == VIEW_CY) { if (you.travel_x > 0 && you.travel_y > 0) { - int nx = you.travel_x - you.x_pos + 17; - int ny = you.travel_y - you.y_pos + 9; + int nx = grid2viewX(you.travel_x); + int ny = grid2viewY(you.travel_y); if (in_viewport_bounds(nx, ny)) { newcx = nx; @@ -562,7 +637,7 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) else { dirChosen = true; - dir = 4; + dir = keyin == 'X'? -1 : 4; } break; @@ -584,12 +659,12 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) break; // check for SELECTION - if (dirChosen && dir == 4) + if (dirChosen && (dir == 4 || dir == -1)) { // [dshaligram] We no longer vet the square coordinates if // we're justLooking. By not vetting the coordinates, we make 'x' // look_around() nicer for travel purposes. - if (!justLooking) + if (!justLooking || !in_bounds(view2gridX(cx), view2gridY(cy))) { // RULE: cannot target what you cannot see if (!in_vlos(cx, cy)) @@ -602,16 +677,17 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) moves.isValid = true; moves.isTarget = true; - moves.tx = you.x_pos + cx - 17; - moves.ty = you.y_pos + cy - 9; + moves.isEndpoint = (dir == -1); + moves.tx = you.x_pos + cx - VIEW_CX; + moves.ty = you.y_pos + cy - VIEW_CY; if (moves.tx == you.x_pos && moves.ty == you.y_pos) moves.isMe = true; else { // try to set you.previous target - mx = you.x_pos + cx - 17; - my = you.y_pos + cy - 9; + mx = you.x_pos + cx - VIEW_CX; + my = you.y_pos + cy - VIEW_CY; mid = mgrd[mx][my]; if (mid == NON_MONSTER) @@ -633,10 +709,10 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) } // bounds check for newcx, newcy - if (newcx < 1) newcx = 1; - if (newcx > 33) newcx = 33; - if (newcy < 1) newcy = 1; - if (newcy > 17) newcy = 17; + if (newcx < VIEW_SX) newcx = VIEW_SX; + if (newcx > VIEW_EX) newcx = VIEW_EX; + if (newcy < VIEW_SY) newcy = VIEW_SY; + if (newcy > VIEW_EY) newcy = VIEW_EY; // no-op if the cursor doesn't move. if (newcx == cx && newcy == cy) @@ -646,14 +722,19 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) cx = newcx; cy = newcy; mesclr( true ); + + const int gridX = view2gridX(cx), + gridY = view2gridY(cy); + if (!in_vlos(cx, cy)) { mpr("You can't see that place."); - describe_oos_square(you.x_pos + cx - 17, - you.y_pos + cy - 9); + describe_oos_square(gridX, gridY); continue; } - describe_cell(you.x_pos + cx - 17, you.y_pos + cy - 9); + + if (in_bounds(gridX, gridY)) + describe_cell(gridX, gridY); } // end WHILE mesclr( true ); @@ -661,18 +742,19 @@ void look_around(struct dist &moves, bool justLooking, int first_move, int mode) bool in_vlos(int x, int y) { - return in_los_bounds(x, y) && (env.show[x - 8][y] || (x == 17 && y == 9)); + return in_los_bounds(x, y) + && (env.show[x - LOS_SX][y] || (x == VIEW_CX && y == VIEW_CY)); } bool in_los(int x, int y) { - const int tx = x + 9 - you.x_pos, - ty = y + 9 - you.y_pos; + const int tx = x + VIEW_CX - you.x_pos, + ty = y + VIEW_CY - you.y_pos; - if (!in_los_bounds(tx + 8, ty)) + if (!in_los_bounds(tx, ty)) return (false); - return (x == you.x_pos && y == you.y_pos) || env.show[tx][ty]; + return (x == you.x_pos && y == you.y_pos) || env.show[tx - LOS_SX][ty]; } static bool find_monster( int x, int y, int mode ) @@ -688,7 +770,7 @@ static bool find_monster( int x, int y, int mode ) && !mons_friendly( &menv[targ_mon] ) && (Options.target_zero_exp || - !mons_flag( menv[targ_mon].type, M_NO_EXP_GAIN )) ))); + !mons_class_flag( menv[targ_mon].type, M_NO_EXP_GAIN )) ))); } static bool find_feature( int x, int y, int mode ) @@ -760,12 +842,12 @@ static int next_los(int dir, int los, bool wrap) bool in_viewport_bounds(int x, int y) { - return (x >= 1 && x <= 33 && y >= 1 && y <= 17); + return (x >= VIEW_SX && x <= VIEW_EX && y >= VIEW_SY && y <= VIEW_EY); } bool in_los_bounds(int x, int y) { - return !(x > 25 || x < 8 || y > 17 || y < 1); + return !(x > LOS_EX || x < LOS_SX || y > LOS_EY || y < LOS_SY); } //--------------------------------------------------------------- @@ -805,7 +887,8 @@ static char find_square( unsigned char xps, unsigned char yps, { // We've been told to flip between visible/hidden, so we // need to find what we're currently on. - bool vis = (env.show[xps - 8][yps] || (xps == 17 && yps == 9)); + bool vis = (env.show[xps - 8][yps] + || (xps == VIEW_CX && yps == VIEW_CY)); if (wrap && (vis != (los == LOS_FLIPVH)) == (direction == 1)) { @@ -828,9 +911,9 @@ static char find_square( unsigned char xps, unsigned char yps, onlyVis = (los & LOS_VISIBLE); onlyHidden = (los & LOS_HIDDEN); - const int minx = 1, maxx = 33, - miny = -7, maxy = 25, - ctrx = 17, ctry = 9; + const int minx = VIEW_SX, maxx = VIEW_EX, + miny = VIEW_SY - VIEW_Y_DIFF, maxy = VIEW_EY + VIEW_Y_DIFF, + ctrx = VIEW_CX, ctry = VIEW_CY; while (temp_xps >= minx - 1 && temp_xps <= maxx && temp_yps <= maxy && temp_yps >= miny - 1) @@ -963,7 +1046,7 @@ static char find_square( unsigned char xps, unsigned char yps, // continue; if (temp_xps < minx - 1 || temp_xps > maxx - || temp_yps < 1 || temp_yps > 17) + || temp_yps < VIEW_SY || temp_yps > VIEW_EY) continue; if (targ_x < 1 || targ_x >= GXM || targ_y < 1 || targ_y >= GYM) @@ -986,61 +1069,12 @@ static char find_square( unsigned char xps, unsigned char yps, next_los(direction, los, wrap))); } -static bool is_shopstair(int x, int y) -{ - return (is_stair(grd[x][y]) || grd[x][y] == DNGN_ENTER_SHOP); -} - -extern unsigned char (*mapch2) (unsigned char); -static bool is_full_mapped(int x, int y) -{ - unsigned grid = grd[x][y]; - int envch = env.map[x - 1][y - 1]; - return (envch && envch == mapch2(grid)); -} - -static int surround_nonshopstair_count(int x, int y) -{ - int count = 0; - for (int ix = -1; ix < 2; ++ix) - { - for (int iy = -1; iy < 2; ++iy) - { - int nx = x + ix, ny = y + iy; - if (nx <= 0 || nx >= GXM || ny <= 0 || ny >= GYM) - continue; - if (is_full_mapped(nx, ny) && !is_shopstair(nx, ny)) - count++; - } - } - return (count); -} - -// For want of a better name... -static bool clear_mapped(int x, int y) -{ - if (!is_full_mapped(x, y)) - return (false); - - if (is_shopstair(x, y)) - return (surround_nonshopstair_count(x, y) > 0); - - return (true); -} - static void describe_feature(int mx, int my, bool oos) { - if (oos && !clear_mapped(mx, my)) + if (oos && !is_terrain_seen(mx, my)) return; - unsigned oldfeat = grd[mx][my]; - if (oos && env.map[mx - 1][my - 1] == mapch2(DNGN_SECRET_DOOR)) - grd[mx][my] = DNGN_ROCK_WALL; - std::string desc = feature_description(mx, my); - - grd[mx][my] = oldfeat; - if (desc.length()) { if (oos) @@ -1049,10 +1083,29 @@ static void describe_feature(int mx, int my, bool oos) } } -std::string feature_description(int mx, int my) +// Returns a vector of features matching the given pattern. +std::vector features_by_desc(const text_pattern &pattern) { - int trf; // used for trap type?? - switch (grd[mx][my]) + std::vector features; + + if (pattern.valid()) + { + for (int i = 0; i < NUM_FEATURES; ++i) + { + std::string fdesc = feature_description(i); + if (fdesc.empty()) + continue; + + if (pattern.matches( fdesc )) + features.push_back( dungeon_feature_type(i) ); + } + } + return (features); +} + +std::string feature_description(int grid) +{ + switch (grid) { case DNGN_STONE_WALL: return ("A stone wall."); @@ -1105,57 +1158,14 @@ std::string feature_description(int mx, int my) return ("A stone staircase leading up."); case DNGN_ENTER_HELL: return ("A gateway to hell."); - case DNGN_BRANCH_STAIRS: - return ("A staircase to a branch level."); case DNGN_TRAP_MECHANICAL: + return ("A mechanical trap."); case DNGN_TRAP_MAGICAL: + return ("A magical trap."); case DNGN_TRAP_III: - for (trf = 0; trf < MAX_TRAPS; trf++) - { - if (env.trap[trf].x == mx - && env.trap[trf].y == my) - { - break; - } - - if (trf == MAX_TRAPS - 1) - { - mpr("Error - couldn't find that trap."); - error_message_to_player(); - break; - } - } - - switch (env.trap[trf].type) - { - case TRAP_DART: - return ("A dart trap."); - case TRAP_ARROW: - return ("An arrow trap."); - case TRAP_SPEAR: - return ("A spear trap."); - case TRAP_AXE: - return ("An axe trap."); - case TRAP_TELEPORT: - return ("A teleportation trap."); - case TRAP_AMNESIA: - return ("An amnesia trap."); - case TRAP_BLADE: - return ("A blade trap."); - case TRAP_BOLT: - return ("A bolt trap."); - case TRAP_ZOT: - return ("A Zot trap."); - case TRAP_NEEDLE: - return ("A needle trap."); - default: - mpr("An undefined trap. Huh?"); - error_message_to_player(); - break; - } - break; + return ("A trap."); case DNGN_ENTER_SHOP: - return (shop_name(mx, my)); + return ("A shop."); case DNGN_ENTER_LABYRINTH: return ("A labyrinth entrance."); case DNGN_ENTER_DIS: @@ -1258,8 +1268,72 @@ std::string feature_description(int mx, int my) case DNGN_DRY_FOUNTAIN_VIII: case DNGN_PERMADRY_FOUNTAIN: return ("A dry fountain."); + default: + return (""); + } +} + +std::string feature_description(int mx, int my) +{ + int trf; // used for trap type?? + + const int grid = grd[mx][my]; + std::string desc = feature_description(grid); + switch (grid) + { + case DNGN_TRAP_MECHANICAL: + case DNGN_TRAP_MAGICAL: + case DNGN_TRAP_III: + for (trf = 0; trf < MAX_TRAPS; trf++) + { + if (env.trap[trf].x == mx + && env.trap[trf].y == my) + { + break; + } + + if (trf == MAX_TRAPS - 1) + { + mpr("Error - couldn't find that trap."); + error_message_to_player(); + break; + } + } + + switch (env.trap[trf].type) + { + case TRAP_DART: + return ("A dart trap."); + case TRAP_ARROW: + return ("An arrow trap."); + case TRAP_SPEAR: + return ("A spear trap."); + case TRAP_AXE: + return ("An axe trap."); + case TRAP_TELEPORT: + return ("A teleportation trap."); + case TRAP_AMNESIA: + return ("An amnesia trap."); + case TRAP_BLADE: + return ("A blade trap."); + case TRAP_BOLT: + return ("A bolt trap."); + case TRAP_ZOT: + return ("A Zot trap."); + case TRAP_NEEDLE: + return ("A needle trap."); + default: + mpr("An undefined trap. Huh?"); + error_message_to_player(); + break; + } + break; + case DNGN_ENTER_SHOP: + return (shop_name(mx, my)); + default: + break; } - return (""); + return (desc); } static void describe_cell(int mx, int my) @@ -1343,7 +1417,8 @@ static void describe_cell(int mx, int my) if (mons_is_mimic( menv[i].type )) mimic_item = true; - else if (!mons_flag(menv[i].type, M_NO_EXP_GAIN)) + else if (!mons_class_flag(menv[i].type, M_NO_EXP_GAIN) + && !mons_is_statue(menv[i].type)) { if (menv[i].behaviour == BEH_SLEEP) { @@ -1355,7 +1430,7 @@ static void describe_cell(int mx, int my) else if (menv[i].behaviour == BEH_FLEE) { strcpy(info, mons_pronoun(menv[i].type, PRONOUN_CAP)); - strcat(info, " is fleeing in terror."); + strcat(info, " is retreating."); mpr(info); } // hostile with target != you @@ -1422,7 +1497,7 @@ static void describe_cell(int mx, int my) strcat(info, " is covered in liquid flames."); break; default: - info[0] = '\0'; + info[0] = 0; break; } // end switch if (info[0]) diff --git a/crawl-ref/source/direct.h b/crawl-ref/source/direct.h index e591107578..cb31de043d 100644 --- a/crawl-ref/source/direct.h +++ b/crawl-ref/source/direct.h @@ -3,6 +3,8 @@ * Summary: Functions used when picking squares. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -28,7 +30,7 @@ #define DIR_DIR 2 void direction( struct dist &moves, int restricts = DIR_NONE, - int mode = TARG_ANY ); + int mode = TARG_ANY, bool confirm_fizzle = false ); // last updated 12may2000 {dlb} /* *********************************************************************** @@ -42,6 +44,31 @@ bool in_viewport_bounds(int x, int y); bool in_los(int x, int y); bool in_vlos(int x, int y); +int dos_direction_unmunge(int doskey); + std::string feature_description(int mx, int my); +std::string feature_description(int grid); + +std::vector features_by_desc(const text_pattern &pattern); + +inline int view2gridX(int vx) +{ + return (you.x_pos + vx - VIEW_CX); +} + +inline int view2gridY(int vy) +{ + return (you.y_pos + vy - VIEW_CY); +} + +inline int grid2viewX(int gx) +{ + return (gx - you.x_pos + VIEW_CX); +} + +inline int grid2viewY(int gy) +{ + return (gy - you.y_pos + VIEW_CY); +} #endif diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc index a18ab7c726..011b46444b 100644 --- a/crawl-ref/source/dungeon.cc +++ b/crawl-ref/source/dungeon.cc @@ -34,18 +34,29 @@ #include "defines.h" #include "enum.h" #include "externs.h" +#include "direct.h" #include "dungeon.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" +#include "mapdef.h" #include "maps.h" +#include "misc.h" #include "mon-util.h" #include "mon-pick.h" #include "monplace.h" +#include "notes.h" #include "player.h" #include "randart.h" #include "spl-book.h" #include "stuff.h" -#include "wpn-misc.h" + +#define MAX_PIT_MONSTERS 10 + +struct pit_mons_def { + int type; + int rare; +}; struct spec_t { bool created; @@ -73,8 +84,6 @@ static void builder_items(int level_number, char level_type, int items_wanted); static void builder_monsters(int level_number, char level_type, int mon_wanted); static void place_specific_stair(unsigned char stair); static void place_branch_entrances(int dlevel, char level_type); -static bool place_specific_trap(unsigned char spec_x, unsigned char spec_y, - unsigned char spec_type); static void place_traps( int level_number ); static void prepare_swamp(void); static void prepare_water( int level_number ); @@ -115,18 +124,15 @@ static void diamond_rooms(int level_number); // ITEM & SHOP FUNCTIONS static void place_shops(int level_number); -static void place_spec_shop(int level_number, unsigned char shop_x, - unsigned char shop_y, unsigned char force_s_type); static unsigned char item_in_shop(unsigned char shop_type); static bool treasure_area(int level_number, unsigned char ta1_x, unsigned char ta2_x, unsigned char ta1_y, unsigned char ta2_y); -static char rare_weapon(unsigned char w_type); static bool is_weapon_special(int the_weapon); static void set_weapon_special(int the_weapon, int spwpn); static void big_room(int level_number); -static void chequerboard(spec_room &sr, unsigned char - target, unsigned char floor1, unsigned char floor2); +static void chequerboard(spec_room &sr, unsigned char target, + unsigned char floor1, unsigned char floor2); static void roguey_level(int level_number, spec_room &sr); static void morgue(spec_room &sr); @@ -134,9 +140,10 @@ static void morgue(spec_room &sr); static void special_room(int level_number, spec_room &sr); static void specr_2(spec_room &sr); static void beehive(spec_room &sr); +static void jelly_pit(int level_number, spec_room &sr); // VAULT FUNCTIONS -static void build_vaults(int level_number, int force_vault); +static void build_vaults(int level_number, int vault_number); static void build_minivaults(int level_number, int force_vault); static int vault_grid( int level_number, int vx, int vy, int altar_count, FixedVector < char, 7 > &acq_item_class, @@ -148,22 +155,84 @@ static int vault_grid( int level_number, int vx, int vy, int altar_count, static int pick_an_altar(void); static void place_altar(void); +static std::string level_name(int subdepth) +{ + return place_name( + get_packed_place( + you.where_are_you, subdepth, you.level_type ) ); +} + +static bool got_curare_roll(const int item_level) +{ + return one_chance_in(item_level > 27? 6 : + item_level < 2 ? 15 : + (364 - 7 * item_level) / 25); +} -/* - ************************************************** - * * - * BEGIN PUBLIC FUNCTIONS * - * * - ************************************************** -*/ +static void place_altars() +{ + int altar_chances[][2] = { + // These are percentages + { BRANCH_MAIN_DUNGEON, 8 }, + { BRANCH_DIS, 0 }, + { BRANCH_GEHENNA, 0 }, + { BRANCH_VESTIBULE_OF_HELL, 0 }, + { BRANCH_COCYTUS, 0 }, + { BRANCH_TARTARUS, 0 }, + { BRANCH_INFERNO, 0 }, + { BRANCH_THE_PIT, 0 }, + { BRANCH_ORCISH_MINES, 20 }, + { BRANCH_HIVE, 0 }, + { BRANCH_LAIR, 5 }, + { BRANCH_SLIME_PITS, 5 }, + { BRANCH_VAULTS, 5 }, + { BRANCH_CRYPT, 5 }, + { BRANCH_HALL_OF_BLADES, 0 }, + { BRANCH_HALL_OF_ZOT, 1 }, + { BRANCH_ECUMENICAL_TEMPLE, 0 }, + { BRANCH_SNAKE_PIT, 10 }, + { BRANCH_ELVEN_HALLS, 8 }, + { BRANCH_TOMB, 0 }, + { BRANCH_SWAMP, 0 }, + { BRANCH_CAVERNS, 0 }, + { -1, -1 } + }; + + // No altars before level 5. + if (you.your_level < 4) + return; + + for (int i = 0; altar_chances[i][0] != -1; ++i) + { + if (player_in_branch( altar_chances[i][0] )) + { + int prob = altar_chances[i][1]; + while (prob) + { + if (random2(100) >= prob) + break; + +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Placing an altar"); +#endif + place_altar(); + // Reduce the chance and try to place another. + prob /= 5; + } + break; + } + } +} + +/********************************************************************** + * builder() - kickoff for the dungeon generator. + *********************************************************************/ void builder(int level_number, char level_type) { int i; // generic loop variable int x,y; // generic map loop variables - // srandom(time(NULL)); - // blank level with DNGN_ROCK_WALL make_box(0,0,GXM-1,GYM-1,DNGN_ROCK_WALL,DNGN_ROCK_WALL); @@ -225,7 +294,7 @@ void builder(int level_number, char level_type) } } - // hook up the special room (if there is one, and it hasn't + // hook up the special room (if there is one, and it hasn't // been hooked up already in roguey_level() if (sr.created && !sr.hooked_up) specr_2(sr); @@ -274,7 +343,7 @@ void builder(int level_number, char level_type) if (level_number > 5 && one_chance_in(500 - 5 * level_number)) items_wanted = 10 + random2avg( 90, 2 ); // rich level! - // change pre-rock (105) to rock, and pre-floor (106) to floor + // change pre-rock (105) to rock, and pre-floor (106) to floor replace_area( 0,0,GXM-1,GYM-1, DNGN_BUILDER_SPECIAL_WALL, DNGN_ROCK_WALL ); replace_area( 0,0,GXM-1,GYM-1, DNGN_BUILDER_SPECIAL_FLOOR, DNGN_FLOOR ); @@ -284,7 +353,7 @@ void builder(int level_number, char level_type) // place monsters builder_monsters(level_number, level_type, mon_wanted); - // place shops, if appropriate + // place shops, if appropriate if (player_in_branch( BRANCH_MAIN_DUNGEON ) || player_in_branch( BRANCH_ORCISH_MINES ) || player_in_branch( BRANCH_ELVEN_HALLS ) @@ -385,8 +454,7 @@ void builder(int level_number, char level_type) mons_place( MONS_CURSE_SKULL, BEH_SLEEP, MHITNOT, false, 0, 0 ); } - if (player_in_branch( BRANCH_ORCISH_MINES ) && one_chance_in(5)) - place_altar(); + place_altars(); // hall of blades (1 level deal) - no down staircases, thanks! if (player_in_branch( BRANCH_HALL_OF_BLADES )) @@ -441,19 +509,6 @@ int items( int allow_uniques, // not just true-false, if (p == NON_ITEM) return (NON_ITEM); - // clear all properties except mitm.base_type {dlb}: - mitm[p].sub_type = 0; - mitm[p].flags = 0; - mitm[p].special = 0; - mitm[p].plus = 0; - mitm[p].plus2 = 0; - mitm[p].x = 0; - mitm[p].y = 0; - mitm[p].link = NON_ITEM; - mitm[p].slot = 0; - mitm[p].orig_monnum = 0; - mitm[p].orig_place = 0; - // cap item_level unless an acquirement-level item {dlb}: if (item_level > 50 && item_level != MAKE_GOOD_ITEM) item_level = 50; @@ -528,9 +583,10 @@ int items( int allow_uniques, // not just true-false, // this replaced the infinite loop (wasteful) -- may need // to make into its own function to allow ease of tweaking // distribution {dlb}: - temp_rand = random2(9); + temp_rand = random2(10); - mitm[p].sub_type = ((temp_rand == 8) ? WPN_DEMON_BLADE : + mitm[p].sub_type = ((temp_rand == 9) ? WPN_LAJATANG : + (temp_rand == 8) ? WPN_DEMON_BLADE : (temp_rand == 7) ? WPN_DEMON_TRIDENT : (temp_rand == 6) ? WPN_DEMON_WHIP : (temp_rand == 5) ? WPN_DOUBLE_SWORD : @@ -547,7 +603,7 @@ int items( int allow_uniques, // not just true-false, { temp_value = (unsigned char) random2(NUM_WEAPONS); - if (rare_weapon(temp_value) >= random2(10) + 1) + if (weapon_rarity(temp_value) >= random2(10) + 1) { mitm[p].sub_type = temp_value; break; @@ -614,8 +670,14 @@ int items( int allow_uniques, // not just true-false, if (item_level > 6 && random2(3000) <= 30 + (item_level * 3) && one_chance_in(12)) { +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Making fixed artifact."); +#endif if (make_item_fixed_artefact( mitm[p], (item_level == 51) )) + { + quant = 1; break; + } } } @@ -647,7 +709,7 @@ int items( int allow_uniques, // not just true-false, case WPN_FLAIL: case WPN_SPIKED_FLAIL: case WPN_GREAT_MACE: - case WPN_GREAT_FLAIL: + case WPN_DIRE_FLAIL: if (one_chance_in(6)) set_equip_race( mitm[p], ISFLAG_ELVEN ); if (one_chance_in(4)) @@ -729,6 +791,7 @@ int items( int allow_uniques, // not just true-false, case WPN_HALBERD: case WPN_GLAIVE: case WPN_EXECUTIONERS_AXE: + case WPN_LOCHABER_AXE: if (one_chance_in(5)) set_equip_race( mitm[p], ISFLAG_ORCISH ); break; @@ -739,6 +802,7 @@ int items( int allow_uniques, // not just true-false, break; case WPN_KATANA: + case WPN_LAJATANG: case WPN_KNIFE: case WPN_SLING: set_equip_race( mitm[p], ISFLAG_NO_RACE ); @@ -752,6 +816,11 @@ int items( int allow_uniques, // not just true-false, set_equip_race( mitm[p], ISFLAG_ELVEN ); break; + case WPN_LONGBOW: + set_equip_race( mitm[p], one_chance_in(3) ? ISFLAG_ELVEN + : ISFLAG_NO_RACE ); + break; + case WPN_CROSSBOW: if (one_chance_in(4)) set_equip_race( mitm[p], ISFLAG_ORCISH ); @@ -793,7 +862,7 @@ int items( int allow_uniques, // not just true-false, // there's a good chance that we'll just strip them of // their ego type at the bottom of this function. -- bwr if (item_level == MAKE_GOOD_ITEM - && cmp_equip_race( mitm[p], ISFLAG_ORCISH )) + && get_equip_race( mitm[p] ) == ISFLAG_ORCISH) { set_equip_race( mitm[p], ISFLAG_NO_RACE ); } @@ -824,7 +893,7 @@ int items( int allow_uniques, // not just true-false, if ((random2(200) <= 50 + item_level || item_level == MAKE_GOOD_ITEM - || is_demonic(mitm[p].sub_type)) + || is_demonic(mitm[p])) // nobody would bother enchanting a club && mitm[p].sub_type != WPN_CLUB && mitm[p].sub_type != WPN_GIANT_CLUB @@ -836,7 +905,7 @@ int items( int allow_uniques, // not just true-false, { if (random2(300) <= 100 + item_level || item_level == MAKE_GOOD_ITEM - || is_demonic( mitm[p].sub_type )) + || is_demonic( mitm[p] )) { // note: this doesn't guarantee special enchantment switch (mitm[p].sub_type) @@ -869,7 +938,7 @@ int items( int allow_uniques, // not just true-false, // **** intentional fall through here **** case WPN_FLAIL: case WPN_SPIKED_FLAIL: - case WPN_GREAT_FLAIL: + case WPN_DIRE_FLAIL: case WPN_HAMMER: if (one_chance_in(25)) set_weapon_special(p, SPWPN_PAIN); @@ -1054,14 +1123,15 @@ int items( int allow_uniques, // not just true-false, case WPN_HALBERD: case WPN_GLAIVE: + case WPN_SCYTHE: + case WPN_TRIDENT: + case WPN_LOCHABER_AXE: if (one_chance_in(30)) set_weapon_special(p, SPWPN_HOLY_WRATH); if (one_chance_in(4)) set_weapon_special(p, SPWPN_PROTECTION); // **** intentional fall through here **** - case WPN_SCYTHE: - case WPN_TRIDENT: if (one_chance_in(5)) set_weapon_special(p, SPWPN_SPEED); // **** intentional fall through here **** @@ -1102,18 +1172,18 @@ int items( int allow_uniques, // not just true-false, break; // **** possible intentional fall through here **** case WPN_BOW: + case WPN_LONGBOW: case WPN_CROSSBOW: - if (one_chance_in(30)) - set_weapon_special( p, SPWPN_SPEED ); - - if (one_chance_in(5)) - set_weapon_special( p, SPWPN_PROTECTION ); + { + const int tmp = random2(1000); - if (coinflip()) - set_weapon_special(p, (coinflip() ? SPWPN_FLAME - : SPWPN_FROST)); + set_weapon_special( p, (tmp < 375) ? SPWPN_FLAME : + (tmp < 750) ? SPWPN_FROST : + (tmp < 920) ? SPWPN_PROTECTION : + (tmp < 980) ? SPWPN_VORPAL + : SPWPN_SPEED ); break; - + } case WPN_BLOWGUN: if (one_chance_in(7)) set_weapon_special(p, SPWPN_VENOM); @@ -1173,6 +1243,10 @@ int items( int allow_uniques, // not just true-false, set_weapon_special(p, SPWPN_VENOM); break; + case WPN_BLESSED_BLADE: // special gift of TSO + set_weapon_special( p, SPWPN_HOLY_WRATH ); + break; + // unlisted weapons have no associated, standard ego-types {dlb} default: break; @@ -1227,7 +1301,7 @@ int items( int allow_uniques, // not just true-false, } // value was "0" comment said "orc" so I went with comment {dlb} - if (cmp_equip_race( mitm[p], ISFLAG_ORCISH )) + if (get_equip_race(mitm[p]) == ISFLAG_ORCISH) { // no holy wrath or slay orc and 1/2 the time no-ego const int brand = get_weapon_brand( mitm[p] ); @@ -1253,8 +1327,8 @@ int items( int allow_uniques, // not just true-false, && mitm[p].sub_type != WPN_CLUB && mitm[p].sub_type != WPN_GIANT_CLUB && mitm[p].sub_type != WPN_GIANT_SPIKED_CLUB - && cmp_equip_desc( mitm[p], 0 ) - && cmp_equip_race( mitm[p], 0 )) + && get_equip_desc(mitm[p]) == 0 + && get_equip_race(mitm[p]) == 0) { set_equip_desc( mitm[p], (coinflip() ? ISFLAG_GLOWING : ISFLAG_RUNED) ); @@ -1346,7 +1420,10 @@ int items( int allow_uniques, // not just true-false, if (mitm[p].sub_type == MI_NEEDLE && (item_level == MAKE_GOOD_ITEM || !one_chance_in(5))) { - set_item_ego_type( mitm[p], OBJ_MISSILES, SPMSL_POISONED_II ); + const int pois = + got_curare_roll(item_level) + ? SPMSL_CURARE : SPMSL_POISONED_II; + set_item_ego_type( mitm[p], OBJ_MISSILES, pois ); } else { @@ -1365,7 +1442,7 @@ int items( int allow_uniques, // not just true-false, // orcish ammo gets poisoned a lot more often -- in the original // code it was poisoned every time!? - if (cmp_equip_race( mitm[p], ISFLAG_ORCISH ) && one_chance_in(3)) + if (get_equip_race(mitm[p]) == ISFLAG_ORCISH && one_chance_in(3)) set_item_ego_type( mitm[p], OBJ_MISSILES, SPMSL_POISONED_II ); // reduced quantity if special @@ -1378,9 +1455,9 @@ int items( int allow_uniques, // not just true-false, mitm[p].plus += random2(5); // elven arrows and dwarven bolts are quality items - if ((cmp_equip_race( mitm[p], ISFLAG_ELVEN ) + if ((get_equip_race(mitm[p]) == ISFLAG_ELVEN && mitm[p].sub_type == MI_ARROW) - || (cmp_equip_race( mitm[p], ISFLAG_DWARVEN ) + || (get_equip_race(mitm[p]) == ISFLAG_DWARVEN && mitm[p].sub_type == MI_BOLT)) { mitm[p].plus += random2(3); @@ -1466,17 +1543,18 @@ int items( int allow_uniques, // not just true-false, } } - hide2armour( &(mitm[p].sub_type) ); + hide2armour(mitm[p]); // mitm[p].special = SPARM_RANDART_II + random2(4); make_item_randart( mitm[p] ); mitm[p].plus = 0; - if (mitm[p].sub_type == ARM_BOOTS) + if (mitm[p].sub_type == ARM_BOOTS + && one_chance_in(10)) { - mitm[p].plus2 = TBOOT_BOOTS; - if (one_chance_in(10)) - mitm[p].plus2 = random2( NUM_BOOT_TYPES ); + mitm[p].sub_type = + coinflip()? ARM_NAGA_BARDING + : ARM_CENTAUR_BARDING; } mitm[p].plus += random2(4); @@ -1530,17 +1608,22 @@ int items( int allow_uniques, // not just true-false, set_equip_race( mitm[p], ISFLAG_ELVEN ); break; + case ARM_NAGA_BARDING: + case ARM_CENTAUR_BARDING: case ARM_BOOTS: - if (one_chance_in(4)) + if (mitm[p].sub_type == ARM_BOOTS) { - mitm[p].plus2 = TBOOT_NAGA_BARDING; - break; - } + if (one_chance_in(4)) + { + mitm[p].sub_type = ARM_NAGA_BARDING; + break; + } - if (one_chance_in(4)) - { - mitm[p].plus2 = TBOOT_CENTAUR_BARDING; - break; + if (one_chance_in(4)) + { + mitm[p].sub_type = ARM_CENTAUR_BARDING; + break; + } } if (one_chance_in(4)) @@ -1552,8 +1635,8 @@ int items( int allow_uniques, // not just true-false, break; case ARM_HELMET: - if (cmp_helmet_type( mitm[p], THELM_CAP ) - || cmp_helmet_type( mitm[p], THELM_WIZARD_HAT )) + if (get_helmet_type(mitm[p]) == THELM_CAP + || get_helmet_type(mitm[p]) == THELM_WIZARD_HAT) { if (one_chance_in(6)) set_equip_race( mitm[p], ISFLAG_ELVEN ); @@ -1612,7 +1695,7 @@ int items( int allow_uniques, // not just true-false, if (50 + item_level >= random2(250) || item_level == MAKE_GOOD_ITEM || (mitm[p].sub_type == ARM_HELMET - && cmp_helmet_type( mitm[p], THELM_WIZARD_HAT ))) + && get_helmet_type(mitm[p]) == THELM_WIZARD_HAT)) { mitm[p].plus += random2(3); @@ -1621,7 +1704,7 @@ int items( int allow_uniques, // not just true-false, if (30 + item_level >= random2(350) && (item_level == MAKE_GOOD_ITEM - || (!cmp_equip_race( mitm[p], ISFLAG_ORCISH ) + || (!get_equip_race(mitm[p]) == ISFLAG_ORCISH || (mitm[p].sub_type <= ARM_PLATE_MAIL && coinflip())))) { switch (mitm[p].sub_type) @@ -1629,12 +1712,21 @@ int items( int allow_uniques, // not just true-false, case ARM_SHIELD: // shield - must do special things for this! case ARM_LARGE_SHIELD: case ARM_BUCKLER: - set_item_ego_type( mitm[p], OBJ_ARMOUR, SPARM_PROTECTION ); + { + const int tmp = random2(1000); + + set_item_ego_type( mitm[p], OBJ_ARMOUR, + (tmp < 40) ? SPARM_RESISTANCE : + (tmp < 160) ? SPARM_FIRE_RESISTANCE : + (tmp < 280) ? SPARM_COLD_RESISTANCE : + (tmp < 400) ? SPARM_POISON_RESISTANCE : + (tmp < 520) ? SPARM_POSITIVE_ENERGY + : SPARM_PROTECTION ); break; // prot //break; - + } case ARM_CLOAK: - if (cmp_equip_race( mitm[p], ISFLAG_DWARVEN )) + if (get_equip_race(mitm[p]) == ISFLAG_DWARVEN) break; switch (random2(4)) @@ -1660,7 +1752,7 @@ int items( int allow_uniques, // not just true-false, break; case ARM_HELMET: - if (cmp_helmet_type(mitm[p],THELM_WIZARD_HAT) && coinflip()) + if (get_helmet_type(mitm[p]) == THELM_WIZARD_HAT && coinflip()) { if (one_chance_in(3)) { @@ -1688,20 +1780,20 @@ int items( int allow_uniques, // not just true-false, break; case ARM_BOOTS: - switch (random2(3)) - { - case 0: - if (mitm[p].plus2 == TBOOT_BOOTS) - set_item_ego_type(mitm[p], OBJ_ARMOUR, SPARM_RUNNING); - break; - case 1: - set_item_ego_type(mitm[p], OBJ_ARMOUR, SPARM_LEVITATION); - break; - case 2: - set_item_ego_type(mitm[p], OBJ_ARMOUR, SPARM_STEALTH); - break; - } + case ARM_NAGA_BARDING: + case ARM_CENTAUR_BARDING: + { + const int tmp = random2(600) + + 200 * (mitm[p].sub_type != ARM_BOOTS); + + set_item_ego_type( mitm[p], OBJ_ARMOUR, + (tmp < 200) ? SPARM_RUNNING : + (tmp < 400) ? SPARM_LEVITATION : + (tmp < 600) ? SPARM_STEALTH : + (tmp < 700) ? SPARM_COLD_RESISTANCE + : SPARM_FIRE_RESISTANCE ); break; + } case ARM_ROBE: switch (random2(4)) @@ -1782,8 +1874,8 @@ int items( int allow_uniques, // not just true-false, } // if not given a racial type, and special, give shiny/runed/etc desc. - if (cmp_equip_race( mitm[p], 0 ) - && cmp_equip_desc( mitm[p], 0 ) + if (get_equip_race(mitm[p]) == 0 + && get_equip_desc(mitm[p]) == 0 && (((is_random_artefact(mitm[p]) || get_armour_ego_type( mitm[p] ) != SPARM_NORMAL) && !one_chance_in(10)) @@ -1810,7 +1902,7 @@ int items( int allow_uniques, // not just true-false, // would be an enchanted item which somehow didn't get converted // into armour). if (item_level == MAKE_GOOD_ITEM) - hide2armour( &(mitm[p].sub_type) ); // what of animal hides? {dlb} + hide2armour(mitm[p]); // what of animal hides? {dlb} // skin armours + Crystal PM don't get special enchantments // or species, but can be randarts @@ -1903,7 +1995,7 @@ int items( int allow_uniques, // not just true-false, for (count = 0; count < 1000; count++) { temp_rand = random2( NUM_MONSTERS ); // random monster - temp_rand = mons_charclass( temp_rand ); // corpse base type + temp_rand = mons_species( temp_rand ); // corpse base type if (mons_weight( temp_rand ) > 0) // drops a corpse break; @@ -2339,7 +2431,13 @@ int items( int allow_uniques, // not just true-false, } } - mitm[p].special = random2(9); + mitm[p].special = random2(NUM_STAVE_ADJ); + + if (item_is_rod( mitm[p] )) + { + mitm[p].plus2 = random_range(9, 14) * ROD_CHARGE_MULT; + mitm[p].plus = mitm[p].plus2; + } quant = 1; break; @@ -2486,6 +2584,20 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes if (thing_created == NON_ITEM) return; + // don't give top-tier wands before 5 HD + if ( menv[mid].hit_dice < 5 ) + { + // technically these wands will be undercharged, but it + // doesn't really matter + if ( mitm[thing_created].sub_type == WAND_FIRE ) + mitm[thing_created].sub_type = WAND_FLAME; + if ( mitm[thing_created].sub_type == WAND_COLD ) + mitm[thing_created].sub_type = WAND_FROST; + if ( mitm[thing_created].sub_type == WAND_LIGHTNING ) + mitm[thing_created].sub_type = (coinflip() ? + WAND_FLAME : WAND_FROST); + } + mitm[thing_created].flags = 0; menv[mid].inv[MSLOT_WAND] = thing_created; } @@ -2508,13 +2620,6 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes if (bp == NON_ITEM) return; - mitm[bp].quantity = 0; // set below if force_item, else we toss this item! - mitm[bp].plus = 0; - mitm[bp].plus2 = 0; - mitm[bp].special = 0; - mitm[bp].orig_place = 0; - mitm[bp].orig_monnum = 0; - // this flags things to "goto give_armour" below ... {dlb} mitm[bp].base_type = 101; @@ -2533,7 +2638,7 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes { case MONS_KOBOLD: // a few of the smarter kobolds have blowguns. - if (one_chance_in(15) && level_number > 1) + if (one_chance_in(10) && level_number > 1) { mitm[bp].base_type = OBJ_WEAPONS; mitm[bp].sub_type = WPN_BLOWGUN; @@ -2647,8 +2752,11 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes // deliberate fall through {gdl} case MONS_ORC_PRIEST: item_race = MAKE_ITEM_ORCISH; - // deliberate fall through {dlb} + // deliberate fall through {gdl} + case MONS_TERENCE: + case MONS_DRACONIAN: + case MONS_DRACONIAN_ZEALOT: if (!one_chance_in(5)) { mitm[bp].base_type = OBJ_WEAPONS; @@ -2681,13 +2789,14 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes item_race = MAKE_ITEM_ELVEN; mitm[bp].base_type = OBJ_WEAPONS; - temp_rand = random2(8); - mitm[bp].sub_type = ((temp_rand > 5) ? WPN_LONG_SWORD : // 2 in 8 - (temp_rand > 3) ? WPN_SHORT_SWORD : // 2 in 8 - (temp_rand > 2) ? WPN_SCIMITAR : // 1 in 8 - (temp_rand > 1) ? WPN_MACE : // 1 in 8 - (temp_rand > 0) ? WPN_BOW // 1 in 8 - : WPN_HAND_CROSSBOW); // 1 in 8 + temp_rand = random2(100); + mitm[bp].sub_type = ((temp_rand > 79) ? WPN_LONG_SWORD : // 20% + (temp_rand > 59) ? WPN_SHORT_SWORD : // 20% + (temp_rand > 45) ? WPN_SCIMITAR : // 14% + (temp_rand > 31) ? WPN_MACE : // 14% + (temp_rand > 18) ? WPN_BOW : // 13% + (temp_rand > 5) ? WPN_HAND_CROSSBOW // 13% + : WPN_LONGBOW); // 6% break; case MONS_DEEP_ELF_ANNIHILATOR: @@ -2697,6 +2806,10 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes case MONS_DEEP_ELF_MAGE: case MONS_DEEP_ELF_SORCERER: case MONS_DEEP_ELF_SUMMONER: + case MONS_DRACONIAN_SHIFTER: + case MONS_DRACONIAN_SCORCHER: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_CALLER: item_race = MAKE_ITEM_ELVEN; mitm[bp].base_type = OBJ_WEAPONS; @@ -2725,6 +2838,14 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes case MONS_RUPERT: case MONS_SKELETAL_WARRIOR: case MONS_WAYNE: + case MONS_PALE_DRACONIAN: + case MONS_RED_DRACONIAN: + case MONS_WHITE_DRACONIAN: + case MONS_GREEN_DRACONIAN: + case MONS_MOTTLED_DRACONIAN: + case MONS_BLACK_DRACONIAN: + case MONS_YELLOW_DRACONIAN: + case MONS_PURPLE_DRACONIAN: mitm[bp].base_type = OBJ_WEAPONS; temp_rand = random2(120); @@ -2757,18 +2878,20 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes case MONS_URUG: case MONS_VAULT_GUARD: case MONS_VAMPIRE_KNIGHT: + case MONS_DRACONIAN_KNIGHT: mitm[bp].base_type = OBJ_WEAPONS; - temp_rand = random2(24); - mitm[bp].sub_type = ((temp_rand > 19) ? WPN_GREAT_SWORD :// 16.67% - (temp_rand > 15) ? WPN_LONG_SWORD : // 16.67% - (temp_rand > 11) ? WPN_BATTLEAXE : // 16.67% - (temp_rand > 7) ? WPN_WAR_AXE : // 16.67% - (temp_rand > 5) ? WPN_GREAT_MACE : // 8.33% - (temp_rand > 3) ? WPN_GREAT_FLAIL :// 8.33% - (temp_rand > 1) ? WPN_GLAIVE : // 8.33% - (temp_rand > 0) ? WPN_BROAD_AXE // 4.17% - : WPN_HALBERD); // 4.17% + temp_rand = random2(25); + mitm[bp].sub_type = ((temp_rand > 20) ? WPN_GREAT_SWORD : // 16% + (temp_rand > 16) ? WPN_LONG_SWORD : // 16% + (temp_rand > 12) ? WPN_BATTLEAXE : // 16% + (temp_rand > 8) ? WPN_WAR_AXE : // 16% + (temp_rand > 5) ? WPN_GREAT_MACE : // 12% + (temp_rand > 3) ? WPN_DIRE_FLAIL : // 8% + (temp_rand > 2) ? WPN_LOCHABER_AXE : // 4% + (temp_rand > 1) ? WPN_GLAIVE : // 4% + (temp_rand > 0) ? WPN_BROAD_AXE // 4% + : WPN_HALBERD); // 4% if (one_chance_in(4)) mitm[bp].plus += 1 + random2(3); @@ -2795,7 +2918,7 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes if (one_chance_in(10) || menv[mid].type == MONS_ETTIN) { - mitm[bp].sub_type = ((one_chance_in(10)) ? WPN_GREAT_FLAIL + mitm[bp].sub_type = ((one_chance_in(10)) ? WPN_DIRE_FLAIL : WPN_GREAT_MACE); } break; @@ -2837,7 +2960,7 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes if (one_chance_in(10)) { - mitm[bp].sub_type = (one_chance_in(10) ? WPN_GREAT_FLAIL + mitm[bp].sub_type = (one_chance_in(10) ? WPN_DIRE_FLAIL : WPN_GREAT_MACE); } break; @@ -2847,6 +2970,9 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes item_race = MAKE_ITEM_NO_RACE; mitm[bp].base_type = OBJ_WEAPONS; mitm[bp].sub_type = WPN_BOW; + if (menv[mid].type == MONS_CENTAUR_WARRIOR + && one_chance_in(3)) + mitm[bp].sub_type = WPN_LONGBOW; break; case MONS_YAKTAUR: @@ -2893,7 +3019,7 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes mitm[bp].base_type = OBJ_WEAPONS; mitm[bp].colour = WHITE; // forced by force_item above {dlb} - mitm[bp].sub_type = (one_chance_in(4) ? WPN_GREAT_SWORD + mitm[bp].sub_type = (one_chance_in(4) ? WPN_BLESSED_BLADE : WPN_LONG_SWORD); set_equip_desc( mitm[bp], ISFLAG_GLOWING ); @@ -3039,7 +3165,7 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes (temp_rand == 1) ? WPN_BOW : WPN_HALBERD); - if (mitm[bp].sub_type == WPN_BOW) + if (is_range_weapon(mitm[bp])) set_item_ego_type( mitm[bp], OBJ_WEAPONS, SPWPN_FLAME ); else set_item_ego_type( mitm[bp], OBJ_WEAPONS, SPWPN_FLAMING ); @@ -3115,10 +3241,10 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes // mv: gives ammunition // note that item_race is not reset for this section if (menv[mid].inv[MSLOT_WEAPON] != NON_ITEM - && launches_things( mitm[menv[mid].inv[MSLOT_WEAPON]].sub_type )) + && is_range_weapon( mitm[menv[mid].inv[MSLOT_WEAPON]] )) { xitc = OBJ_MISSILES; - xitt = launched_by(mitm[menv[mid].inv[MSLOT_WEAPON]].sub_type); + xitt = fires_ammo_type(mitm[menv[mid].inv[MSLOT_WEAPON]]); thing_created = items( 0, xitc, xitt, true, give_level, item_race ); if (thing_created == NON_ITEM) @@ -3127,7 +3253,10 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes // monsters will always have poisoned needles -- otherwise // they are just going to behave badly --GDL if (xitt == MI_NEEDLE) - set_item_ego_type(mitm[thing_created], OBJ_MISSILES, SPMSL_POISONED); + set_item_ego_type(mitm[thing_created], OBJ_MISSILES, + got_curare_roll(give_level)? + SPMSL_CURARE + : SPMSL_POISONED); mitm[thing_created].x = 0; mitm[thing_created].y = 0; @@ -3141,12 +3270,6 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes if (bp == NON_ITEM) return; - mitm[bp].x = 0; - mitm[bp].y = 0; - mitm[bp].link = NON_ITEM; - mitm[bp].orig_place = 0; - mitm[bp].orig_monnum = 0; - item_race = MAKE_ITEM_RANDOM_RACE; give_level = 1 + (level_number / 2); @@ -3265,6 +3388,22 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes case MONS_JESSICA: case MONS_KOBOLD_DEMONOLOGIST: case MONS_OGRE_MAGE: + case MONS_DRACONIAN: + case MONS_RED_DRACONIAN: + case MONS_WHITE_DRACONIAN: + case MONS_GREEN_DRACONIAN: + case MONS_PALE_DRACONIAN: + case MONS_MOTTLED_DRACONIAN: + case MONS_BLACK_DRACONIAN: + case MONS_YELLOW_DRACONIAN: + case MONS_PURPLE_DRACONIAN: + case MONS_DRACONIAN_SHIFTER: + case MONS_DRACONIAN_SCORCHER: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_CALLER: + case MONS_DRACONIAN_MONK: + case MONS_DRACONIAN_ZEALOT: + case MONS_DRACONIAN_KNIGHT: case MONS_ORC_WIZARD: case MONS_WIZARD: item_race = MAKE_ITEM_NO_RACE; @@ -3365,16 +3504,16 @@ static void check_doors(void) solid_count = 0; // first half of each conditional represents bounds checking {dlb}: - if (grd[x - 1][y] < DNGN_LAST_SOLID_TILE) + if (grid_is_solid( grd[x - 1][y] )) solid_count++; - if (grd[x + 1][y] < DNGN_LAST_SOLID_TILE) + if (grid_is_solid( grd[x + 1][y] )) solid_count++; - if (grd[x][y - 1] < DNGN_LAST_SOLID_TILE) + if (grid_is_solid( grd[x][y - 1] )) solid_count++; - if (grd[x][y + 1] < DNGN_LAST_SOLID_TILE) + if (grid_is_solid( grd[x][y + 1] )) solid_count++; grd[x][y] = ((solid_count < 2) ? DNGN_FLOOR : DNGN_CLOSED_DOOR); @@ -3421,9 +3560,11 @@ static void prepare_swamp(void) int i, j; // loop variables int temp_rand; // probability determination {dlb} - for (i = 10; i < (GXM - 10); i++) + const int margin = 10; + + for (i = margin; i < (GXM - margin); i++) { - for (j = 10; j < (GYM - 10); j++) + for (j = margin; j < (GYM - margin); j++) { // doors -> floors {dlb} if (grd[i][j] == DNGN_CLOSED_DOOR || grd[i][j] == DNGN_SECRET_DOOR) @@ -3507,7 +3648,7 @@ static bool find_in_area(int sx, int sy, int ex, int ey, unsigned char feature) return (false); } -// stamp a box. can avoid a possible type, and walls and floors can +// stamp a box. can avoid a possible type, and walls and floors can // be different (or not stamped at all) // Note that the box boundaries are INclusive. static bool make_box(int room_x1, int room_y1, int room_x2, int room_y2, @@ -3547,7 +3688,7 @@ static bool make_box(int room_x1, int room_y1, int room_x2, int room_y2, // take care of labyrinth, abyss, pandemonium // returns 1 if we should skip further generation, -// -1 if we should immediately quit, and 0 otherwise. +// -1 if we should immediately quit, and 0 otherwise. static int builder_by_type(int level_number, char level_type) { if (level_type == LEVEL_LABYRINTH) @@ -3586,13 +3727,35 @@ static int builder_by_type(int level_number, char level_type) if (which_demon >= 0) { + const char *pandemon_level_names[] = { + "mnoleg", "lom_lobon", "cerebov", "gloorx_vloq" + }; you.unique_creatures[40 + which_demon] = 1; - build_vaults(level_number, which_demon + 60); + + const int vault = + random_map_for_tag(pandemon_level_names[(int) which_demon]); + + ASSERT(vault != -1); + if (vault == -1) + { + fprintf(stderr, "Failed to find Pandemonium level %s!\n", + pandemon_level_names[(int) which_demon]); + exit(1); + } + + build_vaults(level_number, vault); } else { plan_main(level_number, 0); - build_minivaults(level_number, 300 + random2(9)); + int vault = random_map_for_tag("pan"); + ASSERT( vault != -1 ); + if (vault == -1) + { + fprintf(stderr, "Failed to build Pandemonium minivault!\n"); + exit(1); + } + build_minivaults(level_number, vault); } return 1; @@ -3603,110 +3766,38 @@ static int builder_by_type(int level_number, char level_type) } // returns 1 if we should skip further generation, -// -1 if we should immediately quit, and 0 otherwise. +// -1 if we should immediately quit, and 0 otherwise. static int builder_by_branch(int level_number) { + int subdepth = subdungeon_depth(you.where_are_you, level_number); + const std::string name = level_name(subdepth); + std::string altname; + + if (subdepth == branch_depth( branch_stair(you.where_are_you) )) + altname = level_name(0); + + int vault = random_map_for_place(name); + if (vault == -1) + vault = random_map_for_place(altname); + + if (vault != -1) + { + build_vaults(level_number, vault); + // [dshaligram] XXX: Is link_items() needed for all vaults? + link_items(); + return 1; + } + switch (you.where_are_you) { case BRANCH_HIVE: - if (level_number == you.branch_stairs[STAIRS_HIVE] - + branch_depth(STAIRS_HIVE)) - build_vaults(level_number, 80); - else - spotty_level(false, 100 + random2(500), false); + spotty_level(false, 100 + random2(500), false); return 1; case BRANCH_SLIME_PITS: - if (level_number == you.branch_stairs[STAIRS_SLIME_PITS] - + branch_depth(STAIRS_SLIME_PITS)) - { - build_vaults(level_number, 81); - } - else - spotty_level(false, 100 + random2(500), false); + spotty_level(false, 100 + random2(500), false); return 1; - case BRANCH_VAULTS: - if (level_number == you.branch_stairs[STAIRS_VAULTS] - + branch_depth(STAIRS_VAULTS)) - { - build_vaults(level_number, 82); - return 1; - } - break; - - case BRANCH_HALL_OF_BLADES: - if (level_number == you.branch_stairs[STAIRS_HALL_OF_BLADES] - + branch_depth(STAIRS_HALL_OF_BLADES)) - { - build_vaults(level_number, 83); - return 1; - } - break; - - case BRANCH_HALL_OF_ZOT: - if (level_number == you.branch_stairs[STAIRS_HALL_OF_ZOT] - + branch_depth(STAIRS_HALL_OF_ZOT)) - { - build_vaults(level_number, 84); - return 1; - } - break; - - case BRANCH_ECUMENICAL_TEMPLE: - if (level_number == you.branch_stairs[STAIRS_ECUMENICAL_TEMPLE] - + branch_depth(STAIRS_ECUMENICAL_TEMPLE)) - { - build_vaults(level_number, 85); - return 1; - } - break; - - case BRANCH_SNAKE_PIT: - if (level_number == you.branch_stairs[STAIRS_SNAKE_PIT] - + branch_depth(STAIRS_SNAKE_PIT)) - { - build_vaults(level_number, 86); - return 1; - } - break; - - case BRANCH_ELVEN_HALLS: - if (level_number == you.branch_stairs[STAIRS_ELVEN_HALLS] - + branch_depth(STAIRS_ELVEN_HALLS)) - { - build_vaults(level_number, 87); - return 1; - } - break; - - case BRANCH_TOMB: - if (level_number == you.branch_stairs[STAIRS_TOMB] + 1) - { - build_vaults(level_number, 88); - return 1; - } - else if (level_number == you.branch_stairs[STAIRS_TOMB] + 2) - { - build_vaults(level_number, 89); - return 1; - } - else if (level_number == you.branch_stairs[STAIRS_TOMB] + 3) - { - build_vaults(level_number, 90); - return 1; - } - break; - - case BRANCH_SWAMP: - if (level_number == you.branch_stairs[STAIRS_SWAMP] - + branch_depth(STAIRS_SWAMP)) - { - build_vaults(level_number, 91); - return 1; - } - break; - case BRANCH_ORCISH_MINES: spotty_level(false, 100 + random2(500), false); return 1; @@ -3719,43 +3810,6 @@ static int builder_by_branch(int level_number) } break; - case BRANCH_VESTIBULE_OF_HELL: - build_vaults( level_number, 50 ); - link_items(); - return -1; - - case BRANCH_DIS: - if (level_number == 33) - { - build_vaults(level_number, 51); - return 1; - } - break; - - case BRANCH_GEHENNA: - if (level_number == 33) - { - build_vaults(level_number, 52); - return 1; - } - break; - - case BRANCH_COCYTUS: - if (level_number == 33) - { - build_vaults(level_number, 53); - return 1; - } - break; - - case BRANCH_TARTARUS: - if (level_number == 33) - { - build_vaults(level_number, 54); - return 1; - } - break; - default: break; } @@ -3773,17 +3827,25 @@ static int builder_normal(int level_number, char level_type, spec_room &sr) bool skipped = false; bool done_city = false; - if (player_in_branch( BRANCH_DIS )) + int vault = random_map_for_place( + level_name( + subdungeon_depth(you.where_are_you, level_number))); + + if (vault == -1 + && player_in_branch( BRANCH_MAIN_DUNGEON ) + && level_number > 10 && level_number < 26 && one_chance_in(9)) + vault = random_map_for_depth(level_number); + + if (vault != -1) { - city_level(level_number); + // Can't have vaults on you.where_are_you != BRANCH_MAIN_DUNGEON levels + build_vaults(level_number, vault); return 1; } - if (player_in_branch( BRANCH_MAIN_DUNGEON ) - && level_number > 10 && level_number < 23 && one_chance_in(9)) + if (player_in_branch( BRANCH_DIS )) { - // Can't have vaults on you.where_are_you != BRANCH_MAIN_DUNGEON levels - build_vaults(level_number, 100); + city_level(level_number); return 1; } @@ -3816,7 +3878,11 @@ static int builder_normal(int level_number, char level_type, spec_room &sr) plan_main(level_number, 0); if (one_chance_in(3) && level_number > 6) - build_minivaults(level_number, 200); + { + int mvault = random_map_for_depth(level_number, true); + if (mvault != -1) + build_minivaults(level_number, mvault); + } return 1; } @@ -3834,8 +3900,12 @@ static int builder_normal(int level_number, char level_type, spec_room &sr) && player_in_branch( BRANCH_MAIN_DUNGEON ) && one_chance_in(4)) { - build_minivaults(level_number, 200); - return 1; + int mvault = random_map_for_depth(level_number, true); + if (mvault != -1) + { + build_minivaults(level_number, mvault); + return 1; + } } } else @@ -3850,7 +3920,7 @@ static int builder_normal(int level_number, char level_type, spec_room &sr) } } - // maybe create a special room, if roguey_level hasn't done it + // maybe create a special room, if roguey_level hasn't done it // already. if (!sr.created && level_number > 5 && !done_city && one_chance_in(5)) special_room(level_number, sr); @@ -3858,7 +3928,7 @@ static int builder_normal(int level_number, char level_type, spec_room &sr) return 0; } -// returns 1 if we should skip extras(), otherwise 0 +// returns 1 if we should skip extras(), otherwise 0 static int builder_basic(int level_number) { int temp_rand; @@ -3967,8 +4037,12 @@ static void builder_extras( int level_number, int level_type ) && player_in_branch( BRANCH_MAIN_DUNGEON ) && one_chance_in(3)) { - build_minivaults(level_number, 200); - return; + int mvault = random_map_for_depth(level_number, true); + if (mvault != -1) + { + build_minivaults(level_number, mvault); + return; + } } if (level_number > 5 && one_chance_in(10)) @@ -4037,31 +4111,38 @@ static void place_traps(int level_number) } while (grd[env.trap[i].x][env.trap[i].y] != DNGN_FLOOR); - env.trap[i].type = TRAP_DART; + unsigned char &trap_type = env.trap[i].type; + trap_type = TRAP_DART; if ((random2(1 + level_number) > 1) && one_chance_in(4)) - env.trap[i].type = TRAP_NEEDLE; - if (random2(1 + level_number) > 2) - env.trap[i].type = TRAP_ARROW; + trap_type = TRAP_NEEDLE; if (random2(1 + level_number) > 3) - env.trap[i].type = TRAP_SPEAR; + trap_type = TRAP_SPEAR; if (random2(1 + level_number) > 5) - env.trap[i].type = TRAP_AXE; + trap_type = TRAP_AXE; + + // Note we're boosting arrow trap numbers by moving it + // down the list, and making spear and axe traps rarer. + if (trap_type == TRAP_DART? + random2(1 + level_number) > 2 + : one_chance_in(7)) + trap_type = TRAP_ARROW; + if (random2(1 + level_number) > 7) - env.trap[i].type = TRAP_BOLT; + trap_type = TRAP_BOLT; if (random2(1 + level_number) > 11) - env.trap[i].type = TRAP_BLADE; + trap_type = TRAP_BLADE; if ((random2(1 + level_number) > 14 && one_chance_in(3)) || (player_in_branch( BRANCH_HALL_OF_ZOT ) && coinflip())) { - env.trap[i].type = TRAP_ZOT; + trap_type = TRAP_ZOT; } if (one_chance_in(20)) - env.trap[i].type = TRAP_TELEPORT; + trap_type = TRAP_TELEPORT; if (one_chance_in(40)) - env.trap[i].type = TRAP_AMNESIA; + trap_type = TRAP_AMNESIA; grd[env.trap[i].x][env.trap[i].y] = DNGN_UNDISCOVERED_TRAP; } // end "for i" @@ -4174,6 +4255,10 @@ static void place_branch_entrances(int dlevel, char level_type) stair = branch + DNGN_ENTER_ORCISH_MINES; place_specific_stair(stair); +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Placing stair: %s", + feature_description(stair).c_str()); +#endif } // end loop - possible branch entrances } @@ -4334,7 +4419,7 @@ static void make_trail(int xs, int xr, int ys, int yr, int corrlength, static int good_door_spot(int x, int y) { - if ((grd[x][y] > DNGN_WATER_X && grd[x][y] < DNGN_ENTER_PANDEMONIUM) + if ((!grid_is_solid(grd[x][y]) && grd[x][y] < DNGN_ENTER_PANDEMONIUM) || grd[x][y] == DNGN_CLOSED_DOOR) { return 1; @@ -4395,8 +4480,8 @@ static bool make_room(int sx,int sy,int ex,int ey,int max_doors, int doorlevel) { // left side if (grd[sx-1][ry] == DNGN_FLOOR - && grd[sx-1][ry-1] < DNGN_LAST_SOLID_TILE - && grd[sx-1][ry+1] < DNGN_LAST_SOLID_TILE) + && grid_is_solid(grd[sx-1][ry-1]) + && grid_is_solid(grd[sx-1][ry+1])) { if (random2(10) < doorlevel) grd[sx-1][ry] = DNGN_CLOSED_DOOR; @@ -4404,8 +4489,8 @@ static bool make_room(int sx,int sy,int ex,int ey,int max_doors, int doorlevel) // right side if (grd[ex+1][ry] == DNGN_FLOOR - && grd[ex+1][ry-1] < DNGN_LAST_SOLID_TILE - && grd[ex+1][ry+1] < DNGN_LAST_SOLID_TILE) + && grid_is_solid(grd[ex+1][ry-1]) + && grid_is_solid(grd[ex+1][ry+1])) { if (random2(10) < doorlevel) grd[ex+1][ry] = DNGN_CLOSED_DOOR; @@ -4417,8 +4502,8 @@ static bool make_room(int sx,int sy,int ex,int ey,int max_doors, int doorlevel) { // top if (grd[rx][sy-1] == DNGN_FLOOR - && grd[rx-1][sy-1] < DNGN_LAST_SOLID_TILE - && grd[rx+1][sy-1] < DNGN_LAST_SOLID_TILE) + && grid_is_solid(grd[rx-1][sy-1]) + && grid_is_solid(grd[rx+1][sy-1])) { if (random2(10) < doorlevel) grd[rx][sy-1] = DNGN_CLOSED_DOOR; @@ -4426,8 +4511,8 @@ static bool make_room(int sx,int sy,int ex,int ey,int max_doors, int doorlevel) // bottom if (grd[rx][ey+1] == DNGN_FLOOR - && grd[rx-1][ey+1] < DNGN_LAST_SOLID_TILE - && grd[rx+1][ey+1] < DNGN_LAST_SOLID_TILE) + && grid_is_solid(grd[rx-1][ey+1]) + && grid_is_solid(grd[rx+1][ey+1])) { if (random2(10) < doorlevel) grd[rx][ey+1] = DNGN_CLOSED_DOOR; @@ -4477,7 +4562,7 @@ static void builder_monsters(int level_number, char level_type, int mon_wanted) while(which_unique < 0 || you.unique_creatures[which_unique]) { - // sometimes, we just quit if a unique is already placed. + // sometimes, we just quit if a unique is already placed. if (which_unique >= 0 && !one_chance_in(3)) { which_unique = -1; @@ -4656,7 +4741,7 @@ static void builder_items(int level_number, char level_type, int items_wanted) // the entire intent of this function is to find a // hallway from a special room to a floor space somewhere, // changing the special room wall (DNGN_BUILDER_SPECIAL_WALL) -// to a closed door, and normal rock wall to pre-floor. +// to a closed door, and normal rock wall to pre-floor. // Anything that might otherwise block the hallway is changed // to pre-floor. static void specr_2(spec_room &sr) @@ -4759,6 +4844,79 @@ static void specr_2(spec_room &sr) sr.hooked_up = true; } // end specr_2() +// Fill special room sr with monsters from the pit_list at density%... +// then place a "lord of the pit" of lord_type at (lordx, lordy). +static void fill_monster_pit( spec_room &sr, + FixedVector &pit_list, + int density, int lord_type, int lordx, int lordy ) +{ + int i, x, y; + + // make distribution cumulative + for (i = 1; i < MAX_PIT_MONSTERS; i++) + { + // assuming that the first zero rarity is the end of the list: + if (!pit_list[i].rare) + break; + + pit_list[i].rare = pit_list[i].rare + pit_list[i - 1].rare; + } + + const int num_types = i; + const int rare_sum = pit_list[num_types - 1].rare; + + // calculate die_size, factoring in the density% of the pit + const int die_size = (rare_sum * 100) / density; + +#if DEBUG_DIAGNOSTICS + for (i = 0; i < num_types; i++) + { + char buff[ ITEMNAME_SIZE ]; + + const int delta = ((i > 0) ? pit_list[i].rare - pit_list[i - 1].rare + : pit_list[i].rare); + + const float perc = (static_cast( delta ) * 100.0) + / static_cast( rare_sum ); + + mprf( MSGCH_DIAGNOSTICS, "%6.2f%%: %s", perc, + moname( pit_list[i].type, true, DESC_PLAIN, buff ) ); + } +#endif + + // put the boss monster down + if (lord_type != MONS_PROGRAM_BUG) + mons_place( lord_type, BEH_SLEEP, MHITNOT, true, lordx, lordy ); + + // place monsters and give them items {dlb}: + for (x = sr.x1; x <= sr.x2; x++) + { + for (y = sr.y1; y <= sr.y2; y++) + { + // avoid the boss (or anyone else we may have dropped already) + if (mgrd[x][y] != NON_MONSTER) + continue; + + const int roll = random2( die_size ); + + // density skip (no need to iterate) + if (roll >= rare_sum) + continue; + + // run throught the cumulative chances and place a monster + for (i = 0; i < num_types; i++) + { + if (roll < pit_list[i].rare) + { + mons_place( pit_list[i].type, BEH_SLEEP, MHITNOT, + true, x, y ); + break; + } + } + } + } +} + static void special_room(int level_number, spec_room &sr) { char spec_room_type = SROOM_LAIR_KOBOLD; @@ -4804,6 +4962,7 @@ static void special_room(int level_number, spec_room &sr) if ((level_number > 13 && spec_room_type == SROOM_LAIR_KOBOLD) || (level_number < 16 && spec_room_type == SROOM_MORGUE) + || (level_number < 14 && spec_room_type == SROOM_JELLY_PIT) || (level_number < 17 && one_chance_in(4))) { spec_room_type = SROOM_LAIR_ORC; @@ -4811,8 +4970,13 @@ static void special_room(int level_number, spec_room &sr) if (level_number > 19 && coinflip()) spec_room_type = SROOM_MORGUE; - } + if (level_number > 13 && + one_chance_in(6 - (level_number > 23) - (level_number > 18))) + { + spec_room_type = SROOM_JELLY_PIT; + } + } switch (spec_room_type) { @@ -4959,6 +5123,10 @@ static void special_room(int level_number, spec_room &sr) case SROOM_MORGUE: morgue(sr); break; + + case SROOM_JELLY_PIT: + jelly_pit(level_number, sr); + break; } } // end special_room() @@ -5007,7 +5175,7 @@ static void beehive(spec_room &sr) mons_place( one_chance_in(7) ? MONS_KILLER_BEE_LARVA : MONS_KILLER_BEE, - BEH_SLEEP, MHITNOT, true, x, y ); + BEH_SLEEP, MHITNOT, true, x, y ); } } @@ -5039,29 +5207,29 @@ static void build_minivaults(int level_number, int force_vault) RANDOM_MONSTER, RANDOM_MONSTER); - char vgrid[81][81]; - - if (force_vault == 200) - { - force_vault = 200 + random2(37); - } - - vault_main(vgrid, mons_array, force_vault, level_number); + map_type vgrid; + vault_placement place; + vault_main(vgrid, mons_array, place, force_vault, level_number); int vx, vy; int v1x, v1y; + // [ds] The margin around the edges of the map where the minivault won't be + // placed. Purely arbitrary as far as I can see. + const int margin = 12; + /* find a target area which can be safely overwritten: */ while(1) { - //if ( one_chance_in(1000) ) return; - v1x = 12 + random2(45); - v1y = 12 + random2(35); + v1x = random_range( margin, GXM - margin - place.width ); + v1y = random_range( margin, GYM - margin - place.height ); - for (vx = v1x; vx < v1x + 12; vx++) + for (vx = v1x; vx < v1x + place.width; vx++) { - for (vy = v1y; vy < v1y + 12; vy++) + for (vy = v1y; vy < v1y + place.height; vy++) { + // [ds] An escape hatch to keep from hanging forever looking + // for a good place. if (one_chance_in(2000)) return; @@ -5078,12 +5246,11 @@ static void build_minivaults(int level_number, int force_vault) } /* must not be completely isolated: */ - for (vx = v1x; vx < v1x + 12; vx++) + for (vx = v1x; vx < v1x + place.width; vx++) { // if (vx != v1x && vx != v1x + 12) continue; - for (vy = v1y; vy < v1y + 12; vy++) + for (vy = v1y; vy < v1y + place.height; vy++) { - // if (vy != v1y && vy != v1y + 12) continue; if (grd[vx][vy] == DNGN_FLOOR || grd[vx][vy] == DNGN_CLOSED_DOOR || grd[vx][vy] == DNGN_SECRET_DOOR) @@ -5098,11 +5265,13 @@ static void build_minivaults(int level_number, int force_vault) break; } - for (vx = v1x; vx < v1x + 12; vx++) + for (vx = v1x; vx < v1x + place.width; vx++) { - for (vy = v1y; vy < v1y + 12; vy++) + for (vy = v1y; vy < v1y + place.height; vy++) { - grd[vx][vy] = vgrid[vx - v1x][vy - v1y]; + // [dshaligram] vault_main always populates vgrid[y][x] instead of + // vgrid[x][y] now. + grd[vx][vy] = vgrid[vy - v1y][vx - v1x]; } } @@ -5111,9 +5280,9 @@ static void build_minivaults(int level_number, int force_vault) int num_runes = 0; // paint the minivault onto the grid - for (vx = v1x; vx < v1x + 12; vx++) + for (vx = v1x; vx < v1x + place.width; vx++) { - for (vy = v1y; vy < v1y + 12; vy++) + for (vy = v1y; vy < v1y + place.height; vy++) { altar_count = vault_grid( level_number, vx, vy, altar_count, acq_item_class, mons_array, @@ -5159,9 +5328,11 @@ static void build_vaults(int level_number, int force_vault) //bool exclusive2 = coinflip(); // usage commented out below {dlb} - char vgrid[81][81]; + map_type vgrid; + vault_placement place; - char gluggy = vault_main(vgrid, mons_array, force_vault, level_number); + int gluggy = + vault_main(vgrid, mons_array, place, force_vault, level_number); int vx, vy; int v1x = 0, v1y = 0, v2x = 0, v2y = 0; @@ -5187,67 +5358,48 @@ static void build_vaults(int level_number, int force_vault) } } - switch (gluggy) - { - case MAP_NORTH: - v1x = 1; - v2x = GXM; - v1y = 1; - v2y = 35; - initial_y++; - dig_dir_x = 0; - dig_dir_y = 1; - break; + // If the map takes the whole screen, our work is done. + if (gluggy == MAP_ENCOMPASS) + return; - case MAP_NORTHWEST: - v1x = 1; - v2x = 40; - v1y = 1; - v2y = 35; - initial_y++; - dig_dir_x = 1; - dig_dir_y = 0; - break; + // Figure out which way we need to go to dig our way out of the vault. + const bool x_edge = + initial_x == place.x || initial_x == place.x + place.width - 1; + const bool y_edge = + initial_y == place.y || initial_y == place.y + place.height - 1; - case MAP_NORTHEAST: - v1x = 40; - v2x = GXM; - v1y = 1; - v2y = 35; - initial_y++; - dig_dir_x = -1; - dig_dir_y = 0; - break; + if (x_edge) + { + dig_dir_x = place.x == 0? 1 : -1; + initial_x += dig_dir_x; + } - case MAP_SOUTHWEST: - v1x = 1; - v2x = 40; - v1y = 35; - v2y = GYM; - initial_y--; - dig_dir_x = 0; - dig_dir_y = -1; - break; + if (y_edge) + { + dig_dir_y = place.y == 0? 1 : -1; + initial_y += dig_dir_y; + } - case MAP_SOUTHEAST: - v1x = 40; - v2x = GXM; - v1y = 35; - v2y = GYM; - initial_y--; - dig_dir_x = 0; - dig_dir_y = -1; - break; + // Does this level require Dis treatment (metal wallification)? + // XXX: Change this so the level definition can explicitly state what + // kind of wallification it wants. + const bool dis_wallify = place.map->has_tag("dis"); - case MAP_ENCOMPASS: - return; + v1x = place.x + 1; + v1y = place.y + 1; + v2x = place.x + place.width; + v2y = place.y + place.height; - case MAP_NORTH_DIS: - v1x = 1; - v2x = GXM; - v1y = 1; - v2y = 35; - plan_4(1, 1, 80, 35, DNGN_METAL_WALL); +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, + "Vault: (%d,%d)-(%d,%d), exit (%d,%d), dir (%d,%d); Dis: %s", + v1x, v1y, v2x, v2y, initial_x, initial_y, dig_dir_x, dig_dir_y, + dis_wallify? "yes" : "no"); +#endif + + if (dis_wallify) + { + plan_4(v1x, v1y, v2x, v2y, DNGN_METAL_WALL); goto vstair; } @@ -5323,7 +5475,7 @@ static void build_vaults(int level_number, int force_vault) dig_place_x = initial_x; dig_place_y = initial_y; - if (gluggy != MAP_NORTH_DIS) + if (!dis_wallify) { for (i = 0; i < 40; i++) { @@ -5402,7 +5554,8 @@ static int vault_grid( int level_number, int vx, int vy, int altar_count, int not_used; // first, set base tile for grids {dlb}: - grd[vx][vy] = ((vgrid == 'x') ? DNGN_ROCK_WALL : + const int grid = + grd[vx][vy] = ((vgrid == 'x') ? DNGN_ROCK_WALL : (vgrid == 'X') ? DNGN_PERMAROCK_WALL : (vgrid == 'c') ? DNGN_STONE_WALL : (vgrid == 'v') ? DNGN_METAL_WALL : @@ -5539,6 +5692,19 @@ static int vault_grid( int level_number, int vx, int vy, int altar_count, break; } + if (grid == DNGN_ORANGE_CRYSTAL_STATUE + || grid == DNGN_SILVER_STATUE) + { + const int mtype = + grid == DNGN_ORANGE_CRYSTAL_STATUE? MONS_ORANGE_STATUE + : MONS_SILVER_STATUE; + + grd[vx][vy] = DNGN_FLOOR; + + place_monster( not_used, mtype, 30, BEH_HOSTILE, + MHITNOT, true, vx, vy, false); + } + // finally, handle grids that place monsters {dlb}: if (vgrid >= '0' && vgrid <= '9') { @@ -5786,9 +5952,9 @@ void item_colour( item_def &item ) break; } - if (is_demonic( item.sub_type )) + if (is_demonic( item )) item.colour = random_colour(); - else if (launches_things( item.sub_type )) + else if (is_range_weapon( item )) item.colour = BROWN; else { @@ -5810,7 +5976,7 @@ void item_colour( item_def &item ) break; default: item.colour = LIGHTCYAN; - if (cmp_equip_race( item, ISFLAG_DWARVEN )) + if (get_equip_race(item) == ISFLAG_DWARVEN) item.colour = CYAN; break; } @@ -5834,7 +6000,7 @@ void item_colour( item_def &item ) break; default: item.colour = LIGHTCYAN; - if (cmp_equip_race( item, ISFLAG_DWARVEN )) + if (get_equip_race(item) == ISFLAG_DWARVEN) item.colour = CYAN; break; } @@ -5848,13 +6014,16 @@ void item_colour( item_def &item ) { case ARM_CLOAK: case ARM_ROBE: + case ARM_NAGA_BARDING: + case ARM_CENTAUR_BARDING: + case ARM_CAP: item.colour = random_colour(); break; case ARM_HELMET: //caps and wizard's hats are random coloured - if (cmp_helmet_type( item, THELM_CAP ) - || cmp_helmet_type( item, THELM_WIZARD_HAT )) + if (get_helmet_type(item) == THELM_CAP + || get_helmet_type(item) == THELM_WIZARD_HAT) { item.colour = random_colour(); } @@ -5869,45 +6038,45 @@ void item_colour( item_def &item ) break; case ARM_DRAGON_HIDE: case ARM_DRAGON_ARMOUR: - item.colour = mons_colour( MONS_DRAGON ); + item.colour = mons_class_colour( MONS_DRAGON ); break; case ARM_TROLL_HIDE: case ARM_TROLL_LEATHER_ARMOUR: - item.colour = mons_colour( MONS_TROLL ); + item.colour = mons_class_colour( MONS_TROLL ); break; case ARM_CRYSTAL_PLATE_MAIL: item.colour = LIGHTGREY; break; case ARM_ICE_DRAGON_HIDE: case ARM_ICE_DRAGON_ARMOUR: - item.colour = mons_colour( MONS_ICE_DRAGON ); + item.colour = mons_class_colour( MONS_ICE_DRAGON ); break; case ARM_STEAM_DRAGON_HIDE: case ARM_STEAM_DRAGON_ARMOUR: - item.colour = mons_colour( MONS_STEAM_DRAGON ); + item.colour = mons_class_colour( MONS_STEAM_DRAGON ); break; case ARM_MOTTLED_DRAGON_HIDE: case ARM_MOTTLED_DRAGON_ARMOUR: - item.colour = mons_colour( MONS_MOTTLED_DRAGON ); + item.colour = mons_class_colour( MONS_MOTTLED_DRAGON ); break; case ARM_STORM_DRAGON_HIDE: case ARM_STORM_DRAGON_ARMOUR: - item.colour = mons_colour( MONS_STORM_DRAGON ); + item.colour = mons_class_colour( MONS_STORM_DRAGON ); break; case ARM_GOLD_DRAGON_HIDE: case ARM_GOLD_DRAGON_ARMOUR: - item.colour = mons_colour( MONS_GOLDEN_DRAGON ); + item.colour = mons_class_colour( MONS_GOLDEN_DRAGON ); break; case ARM_ANIMAL_SKIN: item.colour = BROWN; break; case ARM_SWAMP_DRAGON_HIDE: case ARM_SWAMP_DRAGON_ARMOUR: - item.colour = mons_colour( MONS_SWAMP_DRAGON ); + item.colour = mons_class_colour( MONS_SWAMP_DRAGON ); break; default: item.colour = LIGHTCYAN; - if (cmp_equip_race( item, ISFLAG_DWARVEN )) + if (get_equip_race(item) == ISFLAG_DWARVEN) item.colour = CYAN; break; } @@ -6045,7 +6214,7 @@ void item_colour( item_def &item ) break; case FOOD_CHUNK: // set the appropriate colour of the meat: - temp_value = mons_colour( item.plus ); + temp_value = mons_class_colour( item.plus ); item.colour = (temp_value == BLACK) ? LIGHTRED : temp_value; break; default: @@ -6284,7 +6453,7 @@ void item_colour( item_def &item ) case OBJ_CORPSES: // set the appropriate colour of the body: - temp_value = mons_colour( item.plus ); + temp_value = mons_class_colour( item.plus ); item.colour = (temp_value == BLACK) ? LIGHTRED : temp_value; break; @@ -6294,77 +6463,6 @@ void item_colour( item_def &item ) } } // end item_colour() -// Checks how rare a weapon is. Many of these have special routines for -// placement, especially those with a rarity of zero. Chance is out of 10. -static char rare_weapon(unsigned char w_type) -{ - - // zero value weapons must be placed specially -- see items() {dlb} - if (is_demonic(w_type)) - return 0; - - switch (w_type) - { - case WPN_CLUB: - case WPN_DAGGER: - return 10; - case WPN_HAND_AXE: - case WPN_FALCHION: - case WPN_MACE: - case WPN_QUARTERSTAFF: - return 9; - case WPN_BOW: - case WPN_FLAIL: - case WPN_HAMMER: - case WPN_SABRE: - case WPN_SHORT_SWORD: - case WPN_SLING: - case WPN_SPEAR: - return 8; - case WPN_LONG_SWORD: - case WPN_MORNINGSTAR: - case WPN_WAR_AXE: - return 7; - case WPN_BATTLEAXE: - case WPN_CROSSBOW: - case WPN_GREAT_SWORD: - case WPN_SCIMITAR: - case WPN_TRIDENT: - return 6; - case WPN_GLAIVE: - case WPN_HALBERD: - case WPN_BLOWGUN: - return 5; - case WPN_BROAD_AXE: - case WPN_HAND_CROSSBOW: - case WPN_SPIKED_FLAIL: - case WPN_WHIP: - return 4; - case WPN_GREAT_MACE: - return 3; - case WPN_ANCUS: - case WPN_GREAT_FLAIL: - case WPN_SCYTHE: - return 2; - case WPN_GIANT_CLUB: - case WPN_GIANT_SPIKED_CLUB: - return 1; - // zero value weapons must be placed specially -- see items() {dlb} - case WPN_DOUBLE_SWORD: - case WPN_EVENINGSTAR: - case WPN_EXECUTIONERS_AXE: - case WPN_KATANA: - case WPN_KNIFE: - case WPN_QUICK_BLADE: - case WPN_TRIPLE_SWORD: - return 0; - - default: - // should not happen now that calls are bounded by NUM_WEAPONS {dlb} - return -1; - } -} // end rare_weapon() - //jmf: generate altar based on where you are, or possibly randomly static int pick_an_altar(void) { @@ -6549,15 +6647,18 @@ static void place_shops(int level_number) } } // end place_shops() -static void place_spec_shop( int level_number, - unsigned char shop_x, unsigned char shop_y, - unsigned char force_s_type ) +void place_spec_shop( int level_number, + unsigned char shop_x, unsigned char shop_y, + unsigned char force_s_type, bool representative ) { int orb = 0; int i = 0; int j = 0; // loop variable int item_level; + bool note_status = notes_are_active(); + activate_notes(false); + for (i = 0; i < MAX_SHOPS; i++) { if (env.shop[i].type == SHOP_UNASSIGNED) @@ -6593,6 +6694,8 @@ static void place_spec_shop( int level_number, } int plojy = 5 + random2avg(12, 3); + if (representative) + plojy = env.shop[i].type == SHOP_WAND? NUM_WANDS : 16; for (j = 0; j < plojy; j++) { @@ -6614,7 +6717,8 @@ static void place_spec_shop( int level_number, // General Stores (see item_in_shop() below) (GDL) while(true) { - orb = items( 1, item_in_shop(env.shop[i].type), OBJ_RANDOM, true, + const int subtype = representative? j : OBJ_RANDOM; + orb = items( 1, item_in_shop(env.shop[i].type), subtype, true, item_level, 250 ); if (orb != NON_ITEM @@ -6637,6 +6741,9 @@ static void place_spec_shop( int level_number, if (orb == NON_ITEM) break; + if (representative && mitm[orb].base_type == OBJ_WANDS) + mitm[orb].plus = 7; + // set object 'position' (gah!) & ID status mitm[orb].x = 0; mitm[orb].y = 5 + i; @@ -6653,6 +6760,8 @@ static void place_spec_shop( int level_number, env.shop[i].y = shop_y; grd[shop_x][shop_y] = DNGN_ENTER_SHOP; + + activate_notes(note_status); } // end place_spec_shop() static unsigned char item_in_shop(unsigned char shop_type) @@ -7270,11 +7379,8 @@ static bool octa_room(spec_room &sr, int oblique_max, unsigned char type_floor) if (grd[x][y] == DNGN_FLOOR && type_floor == DNGN_SHALLOW_WATER) grd[x][y] = DNGN_SHALLOW_WATER; - if (type_floor >= DNGN_LAST_SOLID_TILE - && grd[x][y] == DNGN_CLOSED_DOOR) - { + if (grd[x][y] == DNGN_CLOSED_DOOR && !grid_is_solid(type_floor)) grd[x][y] = DNGN_FLOOR; // ick - } } if (oblique > 0) @@ -7289,8 +7395,6 @@ static bool octa_room(spec_room &sr, int oblique_max, unsigned char type_floor) static void labyrinth_level(int level_number) { - int temp_rand; // probability determination {dlb} - int keep_lx = 0, keep_ly = 0; int keep_lx2 = 0, keep_ly2 = 0; char start_point_x = 10; @@ -7421,14 +7525,12 @@ static void labyrinth_level(int level_number) finishing: start_point_x = 10 + random2(GXM - 20); - int treasure_item = 0; - - unsigned char glopop = OBJ_RANDOM; // used in calling items() {dlb} + int glopop = OBJ_RANDOM; // used in calling items() {dlb} int num_items = 8 + random2avg(9, 2); for (int i = 0; i < num_items; i++) { - temp_rand = random2(11); + int temp_rand = random2(11); glopop = ((temp_rand == 0 || temp_rand == 9) ? OBJ_WEAPONS : (temp_rand == 1 || temp_rand == 10) ? OBJ_ARMOUR : @@ -7440,8 +7542,8 @@ static void labyrinth_level(int level_number) (temp_rand == 7) ? OBJ_BOOKS /* (temp_rand == 8) */ : OBJ_STAVES); - treasure_item = items( 1, glopop, OBJ_RANDOM, true, - level_number * 3, 250 ); + const int treasure_item = items( 1, glopop, OBJ_RANDOM, true, + level_number * 3, 250 ); if (treasure_item != NON_ITEM) { @@ -7457,10 +7559,8 @@ static void labyrinth_level(int level_number) link_items(); // turn rock walls into undiggable stone or metal: - temp_rand = random2(50); - - unsigned char wall_xform = ((temp_rand > 10) ? DNGN_STONE_WALL // 78.0% - : DNGN_METAL_WALL); // 22.0% + unsigned char wall_xform = ((random2(50) > 10) ? DNGN_STONE_WALL // 78.0% + : DNGN_METAL_WALL); // 22.0% replace_area(0,0,GXM-1,GYM-1,DNGN_ROCK_WALL,wall_xform); @@ -7511,7 +7611,7 @@ static int box_room_door_spot(int x, int y) static int box_room_doors( int bx1, int bx2, int by1, int by2, int new_doors) { - int good_doors[200]; // 1 == good spot, 2 == door placed! + int good_doors[200]; // 1 == good spot, 2 == door placed! int spot; int i,j; int doors_placed = new_doors; @@ -7520,7 +7620,7 @@ static int box_room_doors( int bx1, int bx2, int by1, int by2, int new_doors) if ( 2 * ( (bx2 - bx1) + (by2-by1) ) > 200) return 0; - // go through, building list of good door spots, and replacing wall + // go through, building list of good door spots, and replacing wall // with door if we're about to block off another door. int spot_count = 0; @@ -7907,7 +8007,7 @@ static void big_room(int level_number) // helper function for chequerboard rooms // note that box boundaries are INclusive -static void chequerboard( spec_room &sr, unsigned char target, +static void chequerboard( spec_room &sr, unsigned char target, unsigned char floor1, unsigned char floor2 ) { int i, j; @@ -8129,8 +8229,50 @@ static void morgue(spec_room &sr) } } // end morgue() -static bool place_specific_trap(unsigned char spec_x, unsigned char spec_y, - unsigned char spec_type) +static void jelly_pit(int level_number, spec_room &sr) +{ + FixedVector< pit_mons_def, MAX_PIT_MONSTERS > pit_list; + const int lordx = sr.x1 + random2(sr.x2 - sr.x1); + const int lordy = sr.y1 + random2(sr.y2 - sr.y1); + + for (int i = 0; i < MAX_PIT_MONSTERS; i++) + { + pit_list[i].type = MONS_PROGRAM_BUG; + pit_list[i].rare = 0; + } + +#if DEBUG_DIAGNOSTICS + mprf( MSGCH_DIAGNOSTICS, "Build: Jelly Pit" ); +#endif + pit_list[0].type = MONS_OOZE; + pit_list[0].rare = 27 - level_number / 5; + + pit_list[1].type = MONS_JELLY; + pit_list[1].rare = 20; + + pit_list[2].type = MONS_BROWN_OOZE; + pit_list[2].rare = 3 + level_number; + + pit_list[3].type = MONS_DEATH_OOZE; + pit_list[3].rare = 2 + (2 * level_number) / 3; + + if (level_number >= 12) + { + pit_list[4].type = MONS_AZURE_JELLY; + pit_list[4].rare = 1 + (level_number - 12) / 3; + } + + if (level_number >= 15) + { + pit_list[5].type = MONS_ACID_BLOB; + pit_list[5].rare = 1 + (level_number - 15) / 4; + } + + fill_monster_pit( sr, pit_list, 90, MONS_PROGRAM_BUG, lordx, lordy ); +} + +bool place_specific_trap(unsigned char spec_x, unsigned char spec_y, + unsigned char spec_type) { if (spec_type == TRAP_RANDOM) spec_type = random2(NUM_TRAPS); @@ -8202,12 +8344,12 @@ void define_zombie( int mid, int ztype, int cs, int power ) { // this limit can be updated if mons->number goes >8 bits.. test = random2(182); // not guaranteed to be valid, so.. - cls = mons_charclass(test); + cls = mons_species(test); if (cls == MONS_PROGRAM_BUG) continue; - // on certain branches, zombie creation will fail if we use - // the mons_rarity() functions, because (for example) there + // on certain branches, zombie creation will fail if we use + // the mons_rarity() functions, because (for example) there // are NO zombifiable "native" abyss creatures. Other branches // where this is a problem are hell levels and the crypt. // we have to watch for summoned zombies on other levels, too, @@ -8266,8 +8408,8 @@ void define_zombie( int mid, int ztype, int cs, int power ) break; } - // every so often, we'll relax the OOD restrictions. Avoids - // infinite loops (if we don't do this, things like creating + // every so often, we'll relax the OOD restrictions. Avoids + // infinite loops (if we don't do this, things like creating // a large skeleton on level 1 may hang the game! if (one_chance_in(5)) relax++; @@ -8279,7 +8421,7 @@ void define_zombie( int mid, int ztype, int cs, int power ) } else { - menv[mid].number = mons_charclass(ztype); + menv[mid].number = mons_species(ztype); mons_sec2 = menv[mid].number; } @@ -8348,6 +8490,7 @@ void define_zombie( int mid, int ztype, int cs, int power ) } menv[mid].number = mons_sec2; + menv[mid].colour = mons_class_colour(cs); } // end define_zombie() #ifdef USE_RIVERS diff --git a/crawl-ref/source/dungeon.h b/crawl-ref/source/dungeon.h index dedd41ae68..7db4d30569 100644 --- a/crawl-ref/source/dungeon.h +++ b/crawl-ref/source/dungeon.h @@ -3,6 +3,8 @@ * Summary: Functions used when building new levels. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -17,6 +19,14 @@ #define MAKE_GOOD_ITEM 351 +// Should be the larger of GXM/GYM +#define MAP_SIDE GXM + +// This may sometimes be used as map_type[x][y] (for minivaults) and as +// map_type[y][x] for large-scale vaults. Keep an eye out for the associated +// brain-damage. [dshaligram] +typedef char map_type[MAP_SIDE + 1][MAP_SIDE + 1]; + void item_colour( item_def &item ); // last updated 12may2000 {dlb} @@ -46,4 +56,13 @@ void give_item(int mid, int level_number); * *********************************************************************** */ void define_zombie(int mid, int ztype, int cs, int power); +bool is_wall(int feature); + +bool place_specific_trap(unsigned char spec_x, unsigned char spec_y, + unsigned char spec_type); + +void place_spec_shop(int level_number, unsigned char shop_x, + unsigned char shop_y, unsigned char force_s_type, + bool representative = false ); + #endif diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc index 16517cba40..f3116be7f2 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -3,6 +3,8 @@ * Summary: Misc stuff. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -20,6 +22,7 @@ #include "direct.h" #include "dungeon.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "misc.h" #include "monplace.h" @@ -37,27 +40,63 @@ #include "spl-util.h" #include "stuff.h" #include "view.h" -#include "wpn-misc.h" // torment_monsters is called with power 0 because torment is // UNRESISTABLE except for being undead or having torment -// resistance! Even if we used maximum power of 1000, high +// resistance! Even if we used maximum power of 1000, high // level monsters and characters would save too often. (GDL) -static int torment_monsters(int x, int y, int pow, int garbage) +int torment_monsters(int x, int y, int pow, int caster) { UNUSED( pow ); - UNUSED( garbage ); // is player? if (x == you.x_pos && y == you.y_pos) { - if (you.is_undead || you.mutation[MUT_TORMENT_RESISTANCE]) + // [dshaligram] Switched to using ouch() instead of dec_hp so that + // notes can also track torment and activities can be interrupted + // correctly. + int hploss = 0; + if (!player_res_torment()) + { + // negative energy resistance can alleviate torment + hploss = you.hp * (50 - player_prot_life() * 5) / 100 - 1; + if (hploss >= you.hp) + hploss = you.hp - 1; + if (hploss < 0) + hploss = 0; + } + + if (!hploss) mpr("You feel a surge of unholy energy."); else { mpr("Your body is wracked with pain!"); - dec_hp((you.hp / 2) - 1, false); + + const char *aux = "torment"; + if (caster < 0) + { + switch (caster) + { + case TORMENT_CARDS: + case TORMENT_SPELL: + aux = "Symbol of Torment"; + break; + case TORMENT_SPWLD: + // FIXME: If we ever make any other weapon / randart + // eligible to torment, this will be incorrect. + aux = "Sceptre of Torment"; + break; + case TORMENT_SCROLL: + aux = "scroll of torment"; + break; + } + caster = TORMENT_GENERIC; + } + ouch(hploss, caster, + caster != TORMENT_GENERIC? KILLED_BY_MONSTER + : KILLED_BY_SOMETHING, + aux); } return 1; @@ -83,9 +122,9 @@ static int torment_monsters(int x, int y, int pow, int garbage) return 1; } -void torment(int tx, int ty) +void torment(int caster, int tx, int ty) { - apply_area_within_radius(torment_monsters, tx, ty, 0, 8, 0); + apply_area_within_radius(torment_monsters, tx, ty, 0, 8, caster); } // end torment() void banished(unsigned char gate_type) @@ -215,15 +254,16 @@ void direct_effect(struct bolt &pbolt) { case DMNBM_HELLFIRE: mpr( "You are engulfed in a burst of hellfire!" ); - strcpy( pbolt.beam_name, "hellfire" ); + pbolt.name = "hellfire"; pbolt.ex_size = 1; - pbolt.flavour = BEAM_EXPLOSION; + pbolt.flavour = BEAM_HELLFIRE; + pbolt.is_explosion = true; pbolt.type = SYM_ZAP; pbolt.colour = RED; pbolt.thrower = KILL_MON_MISSILE; - pbolt.aux_source = NULL; - pbolt.isBeam = false; - pbolt.isTracer = false; + pbolt.aux_source.clear(); + pbolt.is_beam = false; + pbolt.is_tracer = false; pbolt.hit = 20; pbolt.damage = dice_def( 3, 20 ); pbolt.aux_source = "burst of hellfire"; @@ -232,7 +272,7 @@ void direct_effect(struct bolt &pbolt) case DMNBM_SMITING: mpr( "Something smites you!" ); - strcpy( pbolt.beam_name, "smiting" ); // for ouch + pbolt.name = "smiting"; pbolt.aux_source = "by divine providence"; damage_taken = 7 + random2avg(11, 2); break; @@ -256,7 +296,8 @@ void direct_effect(struct bolt &pbolt) // apply damage and handle death, where appropriate {dlb} if (damage_taken > 0) - ouch(damage_taken, pbolt.beam_source, KILLED_BY_BEAM, pbolt.aux_source); + ouch(damage_taken, pbolt.beam_source, KILLED_BY_BEAM, + pbolt.aux_source.c_str()); return; } // end direct_effect() @@ -276,7 +317,7 @@ void mons_direct_effect(struct bolt &pbolt, int i) { case DMNBM_HELLFIRE: simple_monster_message(monster, " is engulfed in hellfire."); - strcpy(pbolt.beam_name, "hellfire"); + pbolt.name = "hellfire"; pbolt.flavour = BEAM_LAVA; damage_taken = 5 + random2(10) + random2(5); @@ -285,7 +326,7 @@ void mons_direct_effect(struct bolt &pbolt, int i) case DMNBM_SMITING: simple_monster_message(monster, " is smitten."); - strcpy(pbolt.beam_name, "smiting"); + pbolt.name = "smiting"; pbolt.flavour = BEAM_MISSILE; damage_taken += 7 + random2avg(11, 2); @@ -295,7 +336,7 @@ void mons_direct_effect(struct bolt &pbolt, int i) break; case DMNBM_MUTATION: - if (mons_holiness( monster->type ) != MH_NATURAL) + if (mons_holiness(monster) != MH_NATURAL) simple_monster_message(monster, " is unaffected."); else if (check_mons_resist_magic( monster, pbolt.ench_power )) simple_monster_message(monster, " resists."); @@ -471,23 +512,23 @@ bool acquirement(unsigned char force_class, int agent) //mpr("[r|R] - Just give me something good."); mpr("What kind of item would you like to acquire? ", MSGCH_PROMPT); - keyin = get_ch(); + keyin = tolower( get_ch() ); - if (keyin == 'a' || keyin == 'A') + if (keyin == 'a') class_wanted = OBJ_WEAPONS; - else if (keyin == 'b' || keyin == 'B') + else if (keyin == 'b') class_wanted = OBJ_ARMOUR; - else if (keyin == 'c' || keyin == 'C') + else if (keyin == 'c') class_wanted = OBJ_JEWELLERY; - else if (keyin == 'd' || keyin == 'D') + else if (keyin == 'd') class_wanted = OBJ_BOOKS; - else if (keyin == 'e' || keyin == 'E') + else if (keyin == 'e') class_wanted = OBJ_STAVES; - else if (keyin == 'f' || keyin == 'F') + else if (keyin == 'f') class_wanted = OBJ_FOOD; - else if (keyin == 'g' || keyin == 'G') + else if (keyin == 'g') class_wanted = OBJ_MISCELLANY; - else if (keyin == 'h' || keyin == 'H') + else if (keyin == 'h') class_wanted = OBJ_GOLD; } else @@ -505,7 +546,7 @@ bool acquirement(unsigned char force_class, int agent) if (class_wanted == OBJ_FOOD) { - // food is a little less predicatable now -- bwr + // food is a little less predictable now -- bwr if (you.species == SP_GHOUL) { @@ -514,7 +555,7 @@ bool acquirement(unsigned char force_class, int agent) } else { - // Meat is better than bread (except for herbivors), and + // Meat is better than bread (except for herbivores), and // by choosing it as the default we don't have to worry // about special cases for carnivorous races (ie kobold) type_wanted = FOOD_MEAT_RATION; @@ -523,7 +564,7 @@ bool acquirement(unsigned char force_class, int agent) type_wanted = FOOD_BREAD_RATION; // If we have some regular rations, then we're probably be more - // interested in faster foods (escpecially royal jelly)... + // interested in faster foods (especially royal jelly)... // otherwise the regular rations should be a good enough offer. if (already_has[FOOD_MEAT_RATION] + already_has[FOOD_BREAD_RATION] >= 2 || coinflip()) @@ -550,17 +591,26 @@ bool acquirement(unsigned char force_class, int agent) int skill = SK_FIGHTING; // Can't do much with launchers, so we'll avoid them for now -- bwr - for (int i = SK_SHORT_BLADES; i <= SK_STAVES; i++) + for (int i = SK_SHORT_BLADES; i < SK_DARTS; i++) { if (i == SK_UNUSED_1) continue; // Adding a small constant allows for the occasional - // weapon in an untrained skill. - count += (you.skills[i] + 1); + // weapon in an untrained skill. Since the game + // doesn't allow for much in the way of good launchers, + // we'll lower their weight. + + int weight = 0; + + weight = you.skills[i] + 1; + if (weight) + { + count += weight; - if (random2(count) < you.skills[i] + 1) - skill = i; + if (random2(count) < weight) + skill = i; + } } if (skill == SK_STAVES) @@ -572,21 +622,25 @@ bool acquirement(unsigned char force_class, int agent) // skipping clubs, knives, blowguns for (int i = WPN_MACE; i < NUM_WEAPONS; i++) { - // skipping launchers - if (i == WPN_SLING) - i = WPN_GLAIVE; - + // FIXME: Add a flag to itemprop.cc to do these exclusions // skipping giant clubs - if (i == WPN_GIANT_CLUB) - i = WPN_EVENINGSTAR; + if (i == WPN_GIANT_CLUB || i == WPN_GIANT_SPIKED_CLUB) + continue; // skipping knife and blowgun - if (i == WPN_KNIFE) - i = WPN_FALCHION; + if (i == WPN_KNIFE || i == WPN_BLOWGUN) + continue; + + // blessed blades can only be created by the player, never found + if (i == WPN_BLESSED_BLADE) + continue; // "rare" weapons are only considered some of the time... // still, the chance is higher than actual random creation - if (weapon_skill( OBJ_WEAPONS, i ) == skill + int wskill = range_skill(OBJ_WEAPONS, i); + if (wskill == SK_RANGED_COMBAT) + wskill = weapon_skill(OBJ_WEAPONS, i); + if (wskill == skill && (i < WPN_EVENINGSTAR || i > WPN_BROAD_AXE || (i >= WPN_HAMMER && i <= WPN_SABRE) || one_chance_in(4))) @@ -598,6 +652,72 @@ bool acquirement(unsigned char force_class, int agent) } } } + else if (class_wanted == OBJ_MISSILES) + { + int count = 0; + int skill = SK_RANGED_COMBAT; + + for (int i = SK_SLINGS; i <= SK_DARTS; i++) + { + if (you.skills[i]) + { + count += you.skills[i]; + if (random2(count) < you.skills[i]) + skill = i; + } + } + + switch (skill) + { + case SK_SLINGS: + type_wanted = MI_STONE; + break; + + case SK_BOWS: + type_wanted = MI_ARROW; + break; + + case SK_CROSSBOWS: + type_wanted = MI_DART; + for (int i = 0; i < ENDOFPACK; i++) + { + // Assuming that crossbow in inventory means that they + // want bolts for it (not darts for a hand crossbow)... + // perhaps we should check for both and compare ammo + // amounts on hand? + if (is_valid_item( you.inv[i] ) + && you.inv[i].base_type == OBJ_WEAPONS + && you.inv[i].sub_type == WPN_CROSSBOW) + { + type_wanted = MI_BOLT; + break; + } + } + break; + + case SK_DARTS: + type_wanted = MI_DART; + for (int i = 0; i < ENDOFPACK; i++) + { + if (is_valid_item( you.inv[i] ) + && you.inv[i].base_type == OBJ_WEAPONS + && you.inv[i].sub_type == WPN_BLOWGUN) + { + // Assuming that blowgun in inventory means that they + // may want needles for it (but darts might also be + // wanted). Maybe expand this... see above comment. + if (coinflip()) + type_wanted = MI_NEEDLE; + break; + } + } + break; + + default: + type_wanted = MI_DART; + break; + } + } else if (class_wanted == OBJ_ARMOUR) { // Increasing the representation of the non-body armour @@ -631,11 +751,11 @@ bool acquirement(unsigned char force_class, int agent) case SP_PALE_DRACONIAN: case SP_UNK0_DRACONIAN: case SP_UNK1_DRACONIAN: - case SP_UNK2_DRACONIAN: + case SP_BASE_DRACONIAN: case SP_SPRIGGAN: if (type_wanted == ARM_GLOVES || type_wanted == ARM_BOOTS) { - type_wanted = OBJ_RANDOM; + type_wanted = ARM_ROBE; // no heavy armour } else if (type_wanted == ARM_SHIELD) { @@ -819,7 +939,7 @@ bool acquirement(unsigned char force_class, int agent) if (best_any >= SK_FIGHTING && best_any <= SK_STAVES) { - // Fighter mage's get the fighting enchantment books + // Fighter mages get the fighting enchantment books if (!you.had_book[BOOK_WAR_CHANTS]) type_wanted = BOOK_WAR_CHANTS; else if (!you.had_book[BOOK_TUKIMA]) @@ -1050,10 +1170,11 @@ bool acquirement(unsigned char force_class, int agent) while (already_has[type_wanted] && !one_chance_in(200)); } - if (grd[you.x_pos][you.y_pos] == DNGN_LAVA - || grd[you.x_pos][you.y_pos] == DNGN_DEEP_WATER) + if (grid_destroys_items(grd[you.x_pos][you.y_pos])) { - mpr("You hear a splash."); // how sad (and stupid) + // how sad (and stupid) + mprf(MSGCH_SOUND, + grid_item_destruction_message(grd[you.x_pos][you.y_pos])); } else { @@ -1067,39 +1188,46 @@ bool acquirement(unsigned char force_class, int agent) return (false); } + // easier to read this way + item_def& thing(mitm[thing_created]); + + // give some more gold + if ( class_wanted == OBJ_GOLD ) + thing.quantity += 150; + // remove curse flag from item - do_uncurse_item( mitm[thing_created] ); + do_uncurse_item( thing ); - if (mitm[thing_created].base_type == OBJ_BOOKS) + if (thing.base_type == OBJ_BOOKS) { - if (mitm[thing_created].base_type == BOOK_MINOR_MAGIC_I - || mitm[thing_created].base_type == BOOK_MINOR_MAGIC_II - || mitm[thing_created].base_type == BOOK_MINOR_MAGIC_III) + if (thing.base_type == BOOK_MINOR_MAGIC_I + || thing.base_type == BOOK_MINOR_MAGIC_II + || thing.base_type == BOOK_MINOR_MAGIC_III) { you.had_book[ BOOK_MINOR_MAGIC_I ] = 1; you.had_book[ BOOK_MINOR_MAGIC_II ] = 1; you.had_book[ BOOK_MINOR_MAGIC_III ] = 1; } - else if (mitm[thing_created].base_type == BOOK_CONJURATIONS_I - || mitm[thing_created].base_type == BOOK_CONJURATIONS_II) + else if (thing.base_type == BOOK_CONJURATIONS_I + || thing.base_type == BOOK_CONJURATIONS_II) { you.had_book[ BOOK_CONJURATIONS_I ] = 1; you.had_book[ BOOK_CONJURATIONS_II ] = 1; } else { - you.had_book[ mitm[thing_created].sub_type ] = 1; + you.had_book[ thing.sub_type ] = 1; } } - else if (mitm[thing_created].base_type == OBJ_JEWELLERY) + else if (thing.base_type == OBJ_JEWELLERY) { - switch (mitm[thing_created].sub_type) + switch (thing.sub_type) { case RING_SLAYING: // make sure plus to damage is >= 1 - mitm[thing_created].plus2 = abs( mitm[thing_created].plus2 ); - if (mitm[thing_created].plus2 == 0) - mitm[thing_created].plus2 = 1; + thing.plus2 = abs( thing.plus2 ); + if (thing.plus2 == 0) + thing.plus2 = 1; // fall through... case RING_PROTECTION: @@ -1108,24 +1236,24 @@ bool acquirement(unsigned char force_class, int agent) case RING_DEXTERITY: case RING_EVASION: // make sure plus is >= 1 - mitm[thing_created].plus = abs( mitm[thing_created].plus ); - if (mitm[thing_created].plus == 0) - mitm[thing_created].plus = 1; + thing.plus = abs( thing.plus ); + if (thing.plus == 0) + thing.plus = 1; break; case RING_HUNGER: case AMU_INACCURACY: // these are the only truly bad pieces of jewellery if (!one_chance_in(9)) - make_item_randart( mitm[thing_created] ); + make_item_randart( thing ); break; default: break; } } - else if (mitm[thing_created].base_type == OBJ_WEAPONS - && !is_fixed_artefact( mitm[thing_created] )) + else if (thing.base_type == OBJ_WEAPONS + && !is_fixed_artefact( thing )) { // HACK: make unwieldable weapons wieldable // Note: messing with fixed artefacts is probably very bad. @@ -1135,23 +1263,23 @@ bool acquirement(unsigned char force_class, int agent) case SP_MUMMY: case SP_GHOUL: { - int brand = get_weapon_brand( mitm[thing_created] ); + int brand = get_weapon_brand( thing ); if (brand == SPWPN_HOLY_WRATH || brand == SPWPN_DISRUPTION) { - if (!is_random_artefact( mitm[thing_created] )) + if (!is_random_artefact( thing )) { - set_item_ego_type( mitm[thing_created], + set_item_ego_type( thing, OBJ_WEAPONS, SPWPN_VORPAL ); } else { - // keep reseting seed until it's good: + // keep resetting seed until it's good: for (; brand == SPWPN_HOLY_WRATH || brand == SPWPN_DISRUPTION; - brand = get_weapon_brand(mitm[thing_created])) + brand = get_weapon_brand(thing)) { - make_item_randart( mitm[thing_created] ); + make_item_randart( thing ); } } } @@ -1162,30 +1290,35 @@ bool acquirement(unsigned char force_class, int agent) case SP_GNOME: case SP_KOBOLD: case SP_SPRIGGAN: - switch (mitm[thing_created].sub_type) + switch (thing.sub_type) { + case WPN_LONGBOW: + thing.sub_type = WPN_BOW; + break; + case WPN_GREAT_SWORD: case WPN_TRIPLE_SWORD: - mitm[thing_created].sub_type = + thing.sub_type = (coinflip() ? WPN_FALCHION : WPN_LONG_SWORD); break; case WPN_GREAT_MACE: - case WPN_GREAT_FLAIL: - mitm[thing_created].sub_type = + case WPN_DIRE_FLAIL: + thing.sub_type = (coinflip() ? WPN_MACE : WPN_FLAIL); break; case WPN_BATTLEAXE: case WPN_EXECUTIONERS_AXE: - mitm[thing_created].sub_type = + thing.sub_type = (coinflip() ? WPN_HAND_AXE : WPN_WAR_AXE); break; case WPN_HALBERD: case WPN_GLAIVE: case WPN_SCYTHE: - mitm[thing_created].sub_type = + case WPN_LOCHABER_AXE: + thing.sub_type = (coinflip() ? WPN_SPEAR : WPN_TRIDENT); break; } @@ -1195,16 +1328,16 @@ bool acquirement(unsigned char force_class, int agent) break; } } - else if (mitm[thing_created].base_type == OBJ_ARMOUR - && !is_fixed_artefact( mitm[thing_created] )) + else if (thing.base_type == OBJ_ARMOUR + && !is_fixed_artefact( thing )) { // HACK: make unwearable hats and boots wearable // Note: messing with fixed artefacts is probably very bad. - switch (mitm[thing_created].sub_type) + switch (thing.sub_type) { case ARM_HELMET: - if ((cmp_helmet_type( mitm[thing_created], THELM_HELM ) - || cmp_helmet_type( mitm[thing_created], THELM_HELMET )) + if ((get_helmet_type(thing) == THELM_HELM + || get_helmet_type(thing) == THELM_HELMET) && ((you.species >= SP_OGRE && you.species <= SP_OGRE_MAGE) || player_genus(GENPC_DRACONIAN) || you.species == SP_MINOTAUR @@ -1213,26 +1346,40 @@ bool acquirement(unsigned char force_class, int agent) || you.mutation[MUT_HORNS])) { // turn it into a cap or wizard hat - set_helmet_type( mitm[thing_created], - coinflip() ? THELM_CAP : THELM_WIZARD_HAT ); + set_helmet_type(thing, + coinflip() ? THELM_CAP : THELM_WIZARD_HAT); - mitm[thing_created].colour = random_colour(); + thing.colour = random_colour(); } break; case ARM_BOOTS: if (you.species == SP_NAGA) - mitm[thing_created].plus2 = TBOOT_NAGA_BARDING; + thing.sub_type = ARM_NAGA_BARDING; else if (you.species == SP_CENTAUR) - mitm[thing_created].plus2 = TBOOT_CENTAUR_BARDING; - else - mitm[thing_created].plus2 = TBOOT_BOOTS; + thing.sub_type = ARM_CENTAUR_BARDING; // fix illegal barding ego types caused by above hack - if (mitm[thing_created].plus2 != TBOOT_BOOTS - && get_armour_ego_type( mitm[thing_created] ) == SPARM_RUNNING) + if (thing.sub_type != ARM_BOOTS && + get_armour_ego_type(thing) == SPARM_RUNNING) { - set_item_ego_type( mitm[thing_created], OBJ_ARMOUR, SPARM_NORMAL ); + set_item_ego_type( thing, OBJ_ARMOUR, SPARM_NORMAL ); + } + break; + + case ARM_NAGA_BARDING: + case ARM_CENTAUR_BARDING: + // make barding appropriate + if (you.species == SP_NAGA ) + thing.sub_type = ARM_NAGA_BARDING; + else if ( you.species == SP_CENTAUR ) + thing.sub_type = ARM_CENTAUR_BARDING; + else { + thing.sub_type = ARM_BOOTS; + // Fix illegal ego types + if (get_armour_ego_type(thing) == SPARM_COLD_RESISTANCE || + get_armour_ego_type(thing) == SPARM_FIRE_RESISTANCE) + set_item_ego_type(thing, OBJ_ARMOUR, SPARM_NORMAL); } break; @@ -1260,48 +1407,91 @@ bool acquirement(unsigned char force_class, int agent) bool recharge_wand(void) { - if (you.equip[EQ_WEAPON] == -1 - || you.inv[you.equip[EQ_WEAPON]].base_type != OBJ_WANDS) - { + if (you.equip[EQ_WEAPON] == -1) return (false); - } - unsigned char charge_gain = 0; + item_def &wand = you.inv[ you.equip[EQ_WEAPON] ]; - switch (you.inv[you.equip[EQ_WEAPON]].sub_type) + if (wand.base_type != OBJ_WANDS && !item_is_rod(wand)) + return (false); + + int charge_gain = 0; + if (wand.base_type == OBJ_WANDS) { - case WAND_INVISIBILITY: - case WAND_FIREBALL: - case WAND_HEALING: - charge_gain = 3; - break; + // remove "empty" autoinscription + const char* empty_inscription = "[empty]"; + size_t p = wand.inscription.find(empty_inscription); + if ( p != std::string::npos ) { + // found it, delete it + size_t prespace = 0; + if ( p != 0 && wand.inscription[p-1] == ' ' ) + prespace = 1; + wand.inscription = + wand.inscription.substr(0, p - prespace) + + wand.inscription.substr(p + strlen(empty_inscription), + std::string::npos); + } + switch (wand.sub_type) + { + case WAND_INVISIBILITY: + case WAND_FIREBALL: + case WAND_HEALING: + case WAND_HASTING: + charge_gain = 3; + break; - case WAND_LIGHTNING: - case WAND_DRAINING: - charge_gain = 4; - break; + case WAND_LIGHTNING: + case WAND_DRAINING: + charge_gain = 4; + break; - case WAND_FIRE: - case WAND_COLD: - charge_gain = 5; - break; + case WAND_FIRE: + case WAND_COLD: + charge_gain = 5; + break; - default: - charge_gain = 8; - break; + default: + charge_gain = 8; + break; + } + + char str_pass[ ITEMNAME_SIZE ]; + item_name(wand, DESC_CAP_YOUR, str_pass); + mprf("%s glows for a moment.", str_pass); + + wand.plus += 1 + random2avg( ((charge_gain - 1) * 3) + 1, 3 ); + + if (wand.plus > charge_gain * 3) + wand.plus = charge_gain * 3; } + else + { + // This is a rod. + bool work = false; - char str_pass[ ITEMNAME_SIZE ]; - in_name(you.equip[EQ_WEAPON], DESC_CAP_YOUR, str_pass); - strcpy(info, str_pass); - strcat(info, " glows for a moment."); - mpr(info); + if (wand.plus2 <= MAX_ROD_CHARGE * ROD_CHARGE_MULT) + { + wand.plus2 += ROD_CHARGE_MULT; + + if (wand.plus2 > MAX_ROD_CHARGE * ROD_CHARGE_MULT) + wand.plus2 = MAX_ROD_CHARGE * ROD_CHARGE_MULT; + + work = true; + } - you.inv[you.equip[EQ_WEAPON]].plus += - 1 + random2avg( ((charge_gain - 1) * 3) + 1, 3 ); + if (wand.plus < wand.plus2) + { + wand.plus = wand.plus2; + work = true; + } - if (you.inv[you.equip[EQ_WEAPON]].plus > charge_gain * 3) - you.inv[you.equip[EQ_WEAPON]].plus = charge_gain * 3; + if (!work) + return (false); + + char str_pass[ITEMNAME_SIZE]; + item_name( wand, DESC_CAP_YOUR, str_pass ); + mprf("%s glows for a moment.", str_pass); + } you.wield_change = true; return (true); @@ -1346,8 +1536,8 @@ void yell(void) switch (keyn) { case '!': - mpr("You yell for attention!"); - you.turn_is_over = 1; + mpr("You yell for attention!", MSGCH_SOUND); + you.turn_is_over = true; noisy( 12, you.x_pos, you.y_pos ); return; diff --git a/crawl-ref/source/effects.h b/crawl-ref/source/effects.h index b7442a493e..44d1ba56a5 100644 --- a/crawl-ref/source/effects.h +++ b/crawl-ref/source/effects.h @@ -3,6 +3,8 @@ * Summary: Misc stuff. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -86,7 +88,8 @@ void yell(void); * called from: ability - decks - fight - it_use3 - item_use - mstuff2 - * spell * *********************************************************************** */ -void torment( int tx, int ty ); +void torment( int caster, int tx, int ty ); +int torment_monsters(int x, int y, int pow, int caster); #endif diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 55d24085e1..4bf996d51f 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -3,6 +3,8 @@ * Summary: Global (ick) enums. * Written by: Daniel Ligon * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <11> 7 Aug 01 MV Changed MSLOT_UNASSIGNED_I to MSLOT_MISCELLANY @@ -30,7 +32,7 @@ #ifndef ENUM_H #define ENUM_H -enum ABILITIES +enum ability_type { ABIL_NON_ABILITY = -1, ABIL_SPIT_POISON = 1, // 1 @@ -49,7 +51,7 @@ enum ABILITIES ABIL_BREATHE_STEAM, ABIL_FLY, // 15 ABIL_SUMMON_MINOR_DEMON, - ABIL_SUMMON_DEMON, + ABIL_SUMMON_DEMONS, ABIL_HELLFIRE, ABIL_TORMENT, ABIL_RAISE_DEAD, // 20 @@ -79,7 +81,7 @@ enum ABILITIES ABIL_TSO_REPEL_UNDEAD = 120, // 120 ABIL_TSO_SMITING, ABIL_TSO_ANNIHILATE_UNDEAD, - ABIL_TSO_THUNDERBOLT, + ABIL_TSO_CLEANSING_FLAME, ABIL_TSO_SUMMON_DAEVA, // 124 ABIL_KIKU_RECALL_UNDEAD_SLAVES = 130, // 130 ABIL_KIKU_ENSLAVE_UNDEAD = 132, // 132 @@ -89,7 +91,7 @@ enum ABILITIES ABIL_YRED_ANIMATE_DEAD, ABIL_YRED_DRAIN_LIFE, ABIL_YRED_CONTROL_UNDEAD, // 144 - ABIL_VEHUMET_CHANNEL_ENERGY = 160, // 160 + // 160 - reserved for Vehumet ABIL_OKAWARU_MIGHT = 170, // 170 ABIL_OKAWARU_HEALING, ABIL_OKAWARU_HASTE, // 172 @@ -97,7 +99,8 @@ enum ABILITIES ABIL_MAKHLEB_LESSER_SERVANT_OF_MAKHLEB, ABIL_MAKHLEB_MAJOR_DESTRUCTION, ABIL_MAKHLEB_GREATER_SERVANT_OF_MAKHLEB, // 183 - ABIL_SIF_MUNA_FORGET_SPELL = 190, // 190 + ABIL_SIF_MUNA_CHANNEL_ENERGY = 190, // 190 + ABIL_SIF_MUNA_FORGET_SPELL, ABIL_TROG_BERSERK = 200, // 200 ABIL_TROG_MIGHT, ABIL_TROG_HASTE_SELF, // 202 @@ -110,11 +113,15 @@ enum ABILITIES ABIL_TRAN_SERPENT_OF_HELL, ABIL_ROTTING, ABIL_TORMENT_II, - ABIL_SHUGGOTH_SEED, + ABIL_PAIN, + ABIL_ENSLAVE_UNDEAD, + ABIL_BOLT_OF_FIRE, + ABIL_BOLT_OF_COLD, + ABIL_HELLFROST, ABIL_RENOUNCE_RELIGION = 250 // 250 }; -enum ABILITY_FLAGS +enum ability_flag_type { ABFLAG_NONE = 0x00000000, ABFLAG_BREATH = 0x00000001, // ability uses DUR_BREATH_WEAPON @@ -126,34 +133,28 @@ enum ABILITY_FLAGS ABFLAG_PERMANENT_MP = 0x00000040 // costs permanent MPs }; -enum ACTIVITY +enum activity_interrupt_type { - ACT_NONE = 0, - ACT_MULTIDROP, - ACT_RUNNING, - ACT_TRAVELING, - ACT_MACRO, - - ACT_ACTIVITY_COUNT -}; + AI_FORCE_INTERRUPT = 0, // Forcibly kills any activity that can be + // interrupted. + AI_KEYPRESS, + AI_FULL_HP, // Player is fully healed + AI_FULL_MP, // Player has recovered all mp + AI_STATUE, // Bad statue has come into view + AI_HUNGRY, // Hunger increased + AI_MESSAGE, // Message was displayed + AI_HP_LOSS, + AI_BURDEN_CHANGE, + AI_STAT_CHANGE, + AI_SEE_MONSTER, + AI_MONSTER_ATTACKS, + AI_TELEPORT, -enum ACT_INTERRUPT -{ - AI_FORCE_INTERRUPT = 0, // Forcibly kills any activity - AI_KEYPRESS = 0x01, - AI_FULL_HP = 0x02, // Player is fully healed - AI_FULL_MP = 0x04, // Player has recovered all mp - AI_STATUE = 0x08, // Bad statue has come into view - AI_HUNGRY = 0x10, // Hunger increased - AI_MESSAGE = 0x20, // Message was displayed - AI_HP_LOSS = 0x40, - AI_BURDEN_CHANGE = 0x80, - AI_STAT_CHANGE = 0x100, - AI_SEE_MONSTER = 0x200, - AI_TELEPORT = 0x400 + // Always the last. + NUM_AINTERRUPTS }; -enum AI_PAYLOAD +enum activity_interrupt_payload_type { AIP_NONE, AIP_INT, @@ -162,7 +163,7 @@ enum AI_PAYLOAD AIP_HP_LOSS }; -enum AMMUNITION_DESCRIPTIONS +enum ammunition_description_type { DAMMO_ORCISH = 3, // 3 DAMMO_ELVEN, @@ -170,7 +171,7 @@ enum AMMUNITION_DESCRIPTIONS }; // Various ways to get the acquirement effect. -enum AQ_AGENTS +enum acquirement_agent_type { AQ_SCROLL = 0, @@ -184,7 +185,7 @@ enum AQ_AGENTS AQ_WIZMODE = 200 }; -enum ARMOUR +enum armour_type { ARM_ROBE, // 0 ARM_LEATHER_ARMOUR, @@ -219,10 +220,16 @@ enum ARMOUR ARM_ANIMAL_SKIN, // 30 ARM_SWAMP_DRAGON_HIDE, ARM_SWAMP_DRAGON_ARMOUR, + ARM_STUDDED_LEATHER_ARMOUR, + ARM_CAP, + ARM_CENTAUR_BARDING, // 35 + ARM_NAGA_BARDING, + NUM_ARMOURS }; -enum ARMOUR_DESCRIPTIONS +// these are for the old system (still used for reading old files) +enum armour_description_type { DARM_PLAIN, // added for the heck of it, 15 Apr 2000 {dlb} DARM_EMBROIDERED_SHINY = 1, // which it is dependent upon armour subtype {dlb} @@ -233,19 +240,18 @@ enum ARMOUR_DESCRIPTIONS DARM_ORCISH }; -enum ARMOUR_PROPERTIES +enum armour_property_type { PARM_AC, // 0 PARM_EVASION }; -// Note: currently the size of the attr array is hard coded at 30! ick! -- bwr -enum ATTRIBUTES +enum attribute_type { ATTR_DIVINE_LIGHTNING_PROTECTION, // 0 // ATTR_SPEC_AIR, // don't use this! // ATTR_SPEC_EARTH, - ATTR_CONTROL_TELEPORT = 3, + ATTR_CONTROL_TELEPORT = 3, // obsolete ATTR_WALK_SLOWLY, ATTR_TRANSFORMATION, // 5 ATTR_CARD_COUNTDOWN, @@ -258,10 +264,15 @@ enum ATTRIBUTES ATTR_WALLS, ATTR_LAST_WALLS, ATTR_DELAYED_FIREBALL, // bwr: reserve fireballs - NUM_ATTRIBUTES // must always remain last member {dlb} + ATTR_DEMONIC_POWER_1, + ATTR_DEMONIC_POWER_2, + ATTR_DEMONIC_POWER_3, + ATTR_DEMONIC_POWER_4, + ATTR_DEMONIC_POWER_5, // 19 + NUM_ATTRIBUTES = 30 // must be at least 30 }; -enum BANDS +enum band_type { BAND_NO_BAND = 0, BAND_KOBOLDS = 1, @@ -306,10 +317,11 @@ enum BANDS BAND_BOGGARTS, BAND_BLINK_FROGS, BAND_SKELETAL_WARRIORS, // 44 + BAND_DRACONIAN, // 45 NUM_BANDS // always last }; -enum BEAMS // beam[].flavour +enum beam_type // beam[].flavour { BEAM_MISSILE, // 0 BEAM_MMISSILE, // 1 - and similarly unresistable things @@ -319,13 +331,17 @@ enum BEAMS // beam[].flavour BEAM_ELECTRICITY, // 5 BEAM_POISON, BEAM_NEG, - BEAM_ACID, // 8 - BEAM_EXPLOSION = 10, // 10 - BEAM_SPORE, // 11 - BEAM_POISON_ARROW, // 12 - BEAM_HELLFIRE, // 13 - found 11jan2000 {dlb} - BEAM_ENERGY = 17, - BEAM_HOLY = 18, // 18 - aka beam of cleansing, golden flame + BEAM_ACID, + BEAM_MIASMA, + // BEAM_EXPLOSION, // 10 - use is_explosion, and BEAM of flavour + BEAM_SPORE = 11, + BEAM_POISON_ARROW, + BEAM_HELLFIRE, + BEAM_NAPALM, + BEAM_STEAM, // 15 + BEAM_HELLFROST, + BEAM_ENERGY, + BEAM_HOLY, // 18 - aka beam of cleansing, golden flame BEAM_FRAG, BEAM_LAVA, // 20 BEAM_BACKLIGHT, @@ -352,6 +368,8 @@ enum BEAMS // beam[].flavour BEAM_DISPEL_UNDEAD, // YELLOW BEAM_DISINTEGRATION, // WHITE BEAM_ENSLAVE_DEMON, // colour "16" + BEAM_BLINK, + BEAM_PETRIFY, // new beams for evaporate BEAM_POTION_STINKING_CLOUD, @@ -363,10 +381,12 @@ enum BEAMS // beam[].flavour BEAM_POTION_BLACK_SMOKE, BEAM_POTION_BLUE_SMOKE, BEAM_POTION_PURP_SMOKE, - BEAM_POTION_RANDOM + BEAM_POTION_RANDOM, + + BEAM_LINE_OF_SIGHT // only used for checking monster LOS }; -enum BOOKS +enum book_type { BOOK_MINOR_MAGIC_I, // 0 BOOK_MINOR_MAGIC_II, @@ -419,7 +439,7 @@ enum BOOKS NUM_BOOKS }; -enum BRANCHES // you.where_are_you +enum branch_type // you.where_are_you { BRANCH_MAIN_DUNGEON, // 0 BRANCH_DIS, @@ -441,10 +461,11 @@ enum BRANCHES // you.where_are_you BRANCH_SNAKE_PIT, BRANCH_ELVEN_HALLS, // 20 BRANCH_TOMB, - BRANCH_SWAMP + BRANCH_SWAMP, + BRANCH_CAVERNS }; -enum BRANCH_STAIRS // you.branch_stairs[] - 10 less than BRANCHES {dlb} +enum branch_stair_type // you.branch_stairs[] - 10 less than BRANCHES {dlb} { STAIRS_ORCISH_MINES, // 0 STAIRS_HIVE, @@ -458,17 +479,18 @@ enum BRANCH_STAIRS // you.branch_stairs[] - 10 less than BRANCHES {dlb} STAIRS_SNAKE_PIT, STAIRS_ELVEN_HALLS, // 10 STAIRS_TOMB, - STAIRS_SWAMP + STAIRS_SWAMP, + STAIRS_CAVERNS }; -enum BURDEN_STATES // you.burden_state +enum burden_state_type // you.burden_state { BS_UNENCUMBERED, // 0 BS_ENCUMBERED = 2, // 2 BS_OVERLOADED = 5 // 5 }; -enum CANNED_MESSAGES // canned_msg() - unsigned char +enum canned_message_type // canned_msg() - unsigned char { MSG_SOMETHING_APPEARS, // 0 MSG_NOTHING_HAPPENS, @@ -480,10 +502,22 @@ enum CANNED_MESSAGES // canned_msg() - unsigned char MSG_UNTHINKING_ACT, MSG_SPELL_FIZZLES, MSG_HUH, - MSG_EMPTY_HANDED + MSG_EMPTY_HANDED, + MSG_NOT_IN_PRESENT_FORM, + MSG_TOO_CONFUSED, + MSG_DISORIENTED, + MSG_CANT_REACH }; -enum CLOUD_TYPES // cloud_type[], place_cloud(), big_cloud() +enum char_set_type +{ + CSET_ASCII, // flat 7-bit ASCII + CSET_IBM, // 8-bit ANSI/Code Page 437 + CSET_DEC, // 8-bit DEC, 0xE0-0xFF shifted for line drawing chars + NUM_CSET +}; + +enum cloud_type { CLOUD_NONE, // 0 CLOUD_FIRE, // 1 @@ -496,6 +530,7 @@ enum CLOUD_TYPES // cloud_type[], place_cloud(), big_cloud() CLOUD_STEAM, // 8 CLOUD_MIASMA = 9, // 9: found 11jan2000 {dlb} CLOUD_BLACK_SMOKE = 10, //was: CLOUD_STICKY_FLAME and wrong 19jan2000 {dlb} + CLOUD_RANDOM = 98, CLOUD_DEBUGGING = 99, // 99: used once as 'nonexistent cloud' {dlb} // if env.cloud_type > 100, it is a monster's cloud {dlb} CLOUD_FIRE_MON = 101, // 101: found 11jan2000 {dlb} @@ -510,7 +545,7 @@ enum CLOUD_TYPES // cloud_type[], place_cloud(), big_cloud() CLOUD_BLACK_SMOKE_MON = 110 // 110: added 19jan2000 {dlb} }; -enum COMMANDS +enum command_type { CMD_NO_CMD = 1000, // 1000 CMD_MOVE_NOWHERE, @@ -589,14 +624,12 @@ enum COMMANDS CMD_REPLAY_MESSAGES, CMD_REDRAW_SCREEN, CMD_MACRO_ADD, - CMD_MACRO_SAVE, CMD_SAVE_GAME, CMD_SAVE_GAME_NOW, - CMD_SUSPEND_GAME, // 1000 + 80 - CMD_QUIT, + CMD_SUSPEND_GAME, + CMD_QUIT, // 1000 + 80 CMD_WIZARD, CMD_DESTROY_ITEM, - CMD_OBSOLETE_INVOKE, CMD_MARK_STASH, CMD_FORGET_STASH, @@ -605,17 +638,133 @@ enum COMMANDS CMD_INTERLEVEL_TRAVEL, CMD_FIX_WAYPOINT, - CMD_CLEAR_MAP -}; - -enum CONFIRM_LEVEL + CMD_CLEAR_MAP, + CMD_INSCRIBE_ITEM, + CMD_TOGGLE_NOFIZZLE, + CMD_TOGGLE_AUTOPRAYER, + CMD_MAKE_NOTE, + CMD_RESISTS_SCREEN, + + /* overmap commands */ + CMD_MAP_CLEAR_MAP, + CMD_MAP_ADD_WAYPOINT, + CMD_MAP_EXCLUDE_AREA, + CMD_MAP_CLEAR_EXCLUDES, + + CMD_MAP_MOVE_LEFT, + CMD_MAP_MOVE_DOWN, + CMD_MAP_MOVE_UP, + CMD_MAP_MOVE_RIGHT, + CMD_MAP_MOVE_UP_LEFT, + CMD_MAP_MOVE_DOWN_LEFT, + CMD_MAP_MOVE_UP_RIGHT, + CMD_MAP_MOVE_DOWN_RIGHT, + + CMD_MAP_JUMP_LEFT, + CMD_MAP_JUMP_DOWN, + CMD_MAP_JUMP_UP, + CMD_MAP_JUMP_RIGHT, + CMD_MAP_JUMP_UP_LEFT, + CMD_MAP_JUMP_DOWN_LEFT, + CMD_MAP_JUMP_UP_RIGHT, + CMD_MAP_JUMP_DOWN_RIGHT, + + CMD_MAP_SCROLL_DOWN, + CMD_MAP_SCROLL_UP, + + CMD_MAP_FIND_UPSTAIR, + CMD_MAP_FIND_DOWNSTAIR, + CMD_MAP_FIND_YOU, + CMD_MAP_FIND_PORTAL, + CMD_MAP_FIND_TRAP, + CMD_MAP_FIND_ALTAR, + CMD_MAP_FIND_EXCLUDED, + CMD_MAP_FIND_F, + CMD_MAP_FIND_WAYPOINT, + CMD_MAP_FIND_STASH, + + CMD_MAP_GOTO_TARGET, + + CMD_MAP_EXIT_MAP, + + /* targeting commands */ + CMD_TARGET_DOWN_LEFT, + CMD_TARGET_DOWN, + CMD_TARGET_DOWN_RIGHT, + CMD_TARGET_LEFT, + CMD_TARGET_RIGHT, + CMD_TARGET_UP_LEFT, + CMD_TARGET_UP, + CMD_TARGET_UP_RIGHT, + CMD_TARGET_CYCLE_TARGET_MODE, + CMD_TARGET_PREV_TARGET, + CMD_TARGET_SELECT, + CMD_TARGET_OBJ_CYCLE_BACK, + CMD_TARGET_OBJ_CYCLE_FORWARD, + CMD_TARGET_CYCLE_FORWARD, + CMD_TARGET_CYCLE_BACK, + CMD_TARGET_CENTER, + CMD_TARGET_CANCEL, + CMD_TARGET_OLD_SPACE, + CMD_TARGET_FIND_TRAP, + CMD_TARGET_FIND_PORTAL, + CMD_TARGET_FIND_ALTAR, + CMD_TARGET_FIND_UPSTAIR, + CMD_TARGET_FIND_DOWNSTAIR, + CMD_TARGET_FIND_YOU, + CMD_TARGET_DESCRIBE, + + // [ds] Silently ignored, requests another round of input. + CMD_NEXT_CMD + +}; + +enum confirm_level_type { CONFIRM_NONE_EASY, CONFIRM_SAFE_EASY, CONFIRM_ALL_EASY }; -enum CORPSE_EFFECTS +enum conduct_type +{ + DID_NECROMANCY = 1, // vamp/drain/pain wpns, Zong/Curses + DID_UNHOLY, // demon wpns, demon spells + DID_ATTACK_HOLY, + DID_ATTACK_FRIEND, + DID_FRIEND_DIES, + DID_STABBING, + DID_POISON, + DID_DEDICATED_BUTCHERY, + DID_DEDICATED_KILL_LIVING, + DID_DEDICATED_KILL_UNDEAD, + DID_DEDICATED_KILL_DEMON, + DID_DEDICATED_KILL_NATURAL_EVIL, // unused + DID_DEDICATED_KILL_WIZARD, + DID_DEDICATED_KILL_PRIEST, // unused + + // [dshaligram] No distinction between killing Angels during prayer or + // otherwise, borrowed from bwr 4.1. + DID_KILL_ANGEL, + DID_LIVING_KILLED_BY_UNDEAD_SLAVE, + DID_LIVING_KILLED_BY_SERVANT, + DID_UNDEAD_KILLED_BY_SERVANT, + DID_DEMON_KILLED_BY_SERVANT, + DID_NATURAL_EVIL_KILLED_BY_SERVANT, // unused + DID_ANGEL_KILLED_BY_SERVANT, + DID_SPELL_MEMORISE, + DID_SPELL_CASTING, + DID_SPELL_PRACTISE, + DID_SPELL_NONUTILITY, // unused + DID_CARDS, + DID_STIMULANTS, // unused + DID_EAT_MEAT, // unused + DID_CREATED_LIFE, // unused + + NUM_CONDUCTS +}; + +enum corpse_effect_type { CE_NOCORPSE, // 0 CE_CLEAN, // 1 @@ -629,13 +778,13 @@ enum CORPSE_EFFECTS CE_ROTTEN = 50 // 50 - must remain at 50 for now {dlb} }; -enum CORPSES +enum corpse_type { CORPSE_BODY, // 0 CORPSE_SKELETON }; -enum DEATH_KNIGHT_CHOICES +enum death_knight_type { DK_NO_SELECTION, DK_NECROMANCY, @@ -643,7 +792,16 @@ enum DEATH_KNIGHT_CHOICES DK_RANDOM }; -enum DECKS +enum startup_book_type +{ + SBT_NO_SELECTION = 0, + SBT_FIRE, + SBT_COLD, + SBT_SUMM, + SBT_RANDOM +}; + +enum deck_type { DECK_OF_WONDERS, // 0 DECK_OF_SUMMONING, @@ -652,25 +810,40 @@ enum DECKS DECK_OF_PUNISHMENT }; -enum DELAY +// When adding new delays, update their names in delay.cc, or bad things will +// happen. +enum delay_type { DELAY_NOT_DELAYED, DELAY_EAT, DELAY_ARMOUR_ON, DELAY_ARMOUR_OFF, - DELAY_MEMORIZE, + DELAY_JEWELLERY_ON, + DELAY_MEMORISE, DELAY_BUTCHER, DELAY_AUTOPICKUP, DELAY_WEAPON_SWAP, // for easy_butcher DELAY_PASSWALL, DELAY_DROP_ITEM, + DELAY_MULTIDROP, DELAY_ASCENDING_STAIRS, DELAY_DESCENDING_STAIRS, - DELAY_INTERUPTABLE = 100, // simple interuptable delay - DELAY_UNINTERUPTABLE // simple uninteruptable delay + + // [dshaligram] Shift-running, resting, travel and macros are now + // also handled as delays. + DELAY_RUN, + DELAY_REST, + DELAY_TRAVEL, + + DELAY_MACRO, + + DELAY_INTERRUPTIBLE, // simple interruptible delay + DELAY_UNINTERRUPTIBLE, // simple uninterruptible delay + + NUM_DELAYS }; -enum DEMON_BEAMS +enum demon_beam_type { DMNBM_HELLFIRE, // 0 DMNBM_SMITING, @@ -678,14 +851,15 @@ enum DEMON_BEAMS DMNBM_MUTATION }; -enum DEMON_CLASSES // summon_any_demon() +enum demon_class_type { DEMON_LESSER, // 0: Class V DEMON_COMMON, // 1: Class II-IV - DEMON_GREATER // 2: Class I + DEMON_GREATER, // 2: Class I + DEMON_RANDOM // any of the above }; -enum DESCRIPTION_LEVEL +enum description_level_type { DESC_CAP_THE, // 0 DESC_NOCAP_THE, // 1 @@ -699,19 +873,71 @@ enum DESCRIPTION_LEVEL DESC_INVENTORY // 8 }; -enum DIRECTION // (unsigned char) you.char_direction +enum dragon_class_type +{ + DRAGON_LIZARD, + DRAGON_DRACONIAN, + DRAGON_DRAGON +}; + +enum game_direction_type { DIR_DESCENDING = 0, // 0 - change and lose savefile compatibility (!!!) DIR_ASCENDING = 1 // 1 - change and lose savefile compatibility (!!!) }; -enum DROP_MODE +// NOTE: The order of these is very important to their usage! +// [dshaligram] If adding/removing from this list, also update view.cc! +enum dungeon_char_type +{ + DCHAR_WALL, // 0 + DCHAR_WALL_MAGIC, + DCHAR_FLOOR, + DCHAR_FLOOR_MAGIC, + DCHAR_DOOR_OPEN, + DCHAR_DOOR_CLOSED, // 5 + DCHAR_TRAP, + DCHAR_STAIRS_DOWN, + DCHAR_STAIRS_UP, + DCHAR_ALTAR, + DCHAR_ARCH, // 10 + DCHAR_FOUNTAIN, + DCHAR_WAVY, + DCHAR_STATUE, + DCHAR_INVIS_EXPOSED, + DCHAR_ITEM_DETECTED, // 15 + DCHAR_ITEM_ORB, + DCHAR_ITEM_WEAPON, + DCHAR_ITEM_ARMOUR, + DCHAR_ITEM_WAND, + DCHAR_ITEM_FOOD, // 20 + DCHAR_ITEM_SCROLL, + DCHAR_ITEM_RING, + DCHAR_ITEM_POTION, + DCHAR_ITEM_MISSILE, + DCHAR_ITEM_BOOK, // 25 + DCHAR_ITEM_STAVE, + DCHAR_ITEM_MISCELLANY, + DCHAR_ITEM_CORPSE, + DCHAR_ITEM_GOLD, + DCHAR_ITEM_AMULET, // 30 + DCHAR_CLOUD, // 31 + NUM_DCHAR_TYPES +}; + +enum drop_mode_type { DM_SINGLE, DM_MULTI }; -enum DUNGEON_FEATURES // (unsigned char) grd[][] +// lowest grid value which can be occupied (walk, swim, fly) +#define MINMOVE 31 + +// lowest grid value which can be seen through +#define MINSEE 11 + +enum dungeon_feature_type { DNGN_UNSEEN, // 0 DNGN_ROCK_WALL, @@ -723,14 +949,12 @@ enum DUNGEON_FEATURES // (unsigned char) grd[][] DNGN_ORCISH_IDOL, DNGN_WAX_WALL, // 8 DNGN_PERMAROCK_WALL, // 9 - for undiggable walls - DNGN_LAST_SOLID_TILE = 10, // 10 - just here temporarily {dlb} - DNGN_LAVA_X = 11, // 11 - DNGN_WATER_X, // 12 DNGN_SILVER_STATUE = 21, // 21 DNGN_GRANITE_STATUE, DNGN_ORANGE_CRYSTAL_STATUE, // 23 - DNGN_STATUE_39 = 39, // 39 + DNGN_STATUE_RESERVED_1, + DNGN_STATUE_RESERVED_2, // 25 DNGN_LAVA = 61, // 61 DNGN_DEEP_WATER, // 62 @@ -738,9 +962,10 @@ enum DUNGEON_FEATURES // (unsigned char) grd[][] DNGN_WATER_STUCK, DNGN_FLOOR, // 67 - DNGN_ENTER_HELL = 69, // 69 + DNGN_EXIT_HELL, // 68 + DNGN_ENTER_HELL, // 69 DNGN_OPEN_DOOR, // 70 - DNGN_BRANCH_STAIRS, // 71 + // DNGN_BRANCH_STAIRS, // 71 DNGN_TRAP_MECHANICAL = 75, // 75 DNGN_TRAP_MAGICAL, DNGN_TRAP_III, @@ -786,6 +1011,10 @@ enum DUNGEON_FEATURES // (unsigned char) grd[][] DNGN_ENTER_ELVEN_HALLS, // 120 DNGN_ENTER_TOMB, DNGN_ENTER_SWAMP, // 122 + DNGN_ENTER_RESERVED_1, + DNGN_ENTER_RESERVED_2, + DNGN_ENTER_RESERVED_3, + DNGN_ENTER_RESERVED_4, // 126 DNGN_RETURN_FROM_ORCISH_MINES = 130, // 130 DNGN_RETURN_FROM_HIVE, @@ -800,6 +1029,10 @@ enum DUNGEON_FEATURES // (unsigned char) grd[][] DNGN_RETURN_FROM_ELVEN_HALLS, // 140 DNGN_RETURN_FROM_TOMB, DNGN_RETURN_FROM_SWAMP, // 142 + DNGN_RETURN_RESERVED_1, + DNGN_RETURN_RESERVED_2, + DNGN_RETURN_RESERVED_3, + DNGN_RETURN_RESERVED_4, // 146 DNGN_ALTAR_ZIN = 180, // 180 DNGN_ALTAR_SHINING_ONE, @@ -824,10 +1057,41 @@ enum DUNGEON_FEATURES // (unsigned char) grd[][] DNGN_DRY_FOUNTAIN_VI, DNGN_DRY_FOUNTAIN_VII, DNGN_DRY_FOUNTAIN_VIII, - DNGN_PERMADRY_FOUNTAIN = 210 // added (from dungeon.cc/maps.cc) 22jan2000 {dlb} -}; - -enum DURATIONS // you.duration[] + DNGN_PERMADRY_FOUNTAIN = 210, // added (from dungeon.cc/maps.cc) 22jan2000 {dlb} + + // Real terrain must all occur before 256 to guarantee it fits + // into the unsigned char used for the grid! + + // There aren't really terrain, but they're passed in and used + // to get their appearance character so I'm putting them here for now. + DNGN_ITEM_ORB = 256, + DNGN_INVIS_EXPOSED = 257, + DNGN_ITEM_WEAPON = 258, + DNGN_ITEM_ARMOUR = 259, + DNGN_ITEM_WAND = 260, + DNGN_ITEM_FOOD = 261, + DNGN_ITEM_UNUSED_1 = 262, + DNGN_ITEM_SCROLL = 263, + DNGN_ITEM_RING = 264, + DNGN_ITEM_POTION = 265, + DNGN_ITEM_MISSILE = 266, + DNGN_ITEM_BOOK = 267, + DNGN_ITEM_UNUSED_2 = 268, + DNGN_ITEM_STAVE = 269, + DNGN_ITEM_MISCELLANY = 270, + DNGN_ITEM_CORPSE = 271, + DNGN_ITEM_GOLD = 272, + DNGN_ITEM_AMULET = 273, + DNGN_ITEM_DETECTED = 274, + + DNGN_CLOUD = 280, + NUM_FEATURES, // for use in lookup table in view.cc + + DNGN_RANDOM, + DNGN_START_OF_MONSTERS = 297 // don't go past here! see view.cc +}; + +enum duration_type { DUR_LIQUID_FLAMES, // 0 DUR_ICY_ARMOUR, @@ -850,18 +1114,54 @@ enum DURATIONS // you.duration[] DUR_SEE_INVISIBLE, DUR_WEAPON_BRAND, // general "branding" spell counter DUR_SILENCE, - DUR_GLAMOUR, // 20 - DUR_SHUGGOTH_SEED_RELOAD, - DUR_INFECTED_SHUGGOTH_SEED, - DUR_CONDENSATION_SHIELD, // 23 + DUR_GLAMOUR, // 20 + DUR_CONDENSATION_SHIELD = 23, // 23 DUR_STONESKIN, - DUR_REPEL_UNDEAD, // 25 - DUR_LAST_DUR, //jmf: for asserts - NUM_DURATIONS = 30 - // set at 30 to prevent savefile incompatibilities 12mar2000{dlb} -}; - -enum ENCHANTMENT // menv[].enchantment[] + DUR_REPEL_UNDEAD, // 25 + DUR_STUN, + DUR_CUT, // 27 + DUR_GOURMAND, // 28 + NUM_DURATIONS = 30 // must be at least 30 +}; + +// various elemental colour schemes... used for abstracting random short lists +// MUST match the order in initfile.cc or breakage results. +enum element_type +{ + EC_FIRE = 32, // fiery colours (must be first and > higest colour) + EC_ICE, // icy colours + EC_EARTH, // earthy colours + EC_ELECTRICITY, // electrical side of air + EC_AIR, // non-electric and general air magic + EC_POISON, // used only for venom mage and stalker stuff + EC_WATER, // used only for the elemental + EC_MAGIC, // general magical effect + EC_MUTAGENIC, // transmute, poly, radiation effects + EC_WARP, // teleportation and anything similar + EC_ENCHANT, // magical enhancements + EC_HEAL, // holy healing (not necromantic stuff) + EC_HOLY, // general "good" god effects + EC_DARK, // darkness + EC_DEATH, // currently only assassin (and equal to EC_NECRO) + EC_NECRO, // necromancy stuff + EC_UNHOLY, // demonology stuff + EC_VEHUMET, // vehumet's odd-ball colours + EC_CRYSTAL, // colours of crystal + EC_BLOOD, // colours of blood + EC_SMOKE, // colours of smoke + EC_SLIME, // colours of slime + EC_JEWEL, // colourful + EC_ELVEN, // used for colouring elf fabric items + EC_DWARVEN, // used for colouring dwarf fabric items + EC_ORCISH, // used for colouring orc fabric items + EC_GILA, // gila monster colours + EC_FLOOR, // colour of the area's floor + EC_ROCK, // colour of the area's rock + EC_STONE, // colour of the area's stone + EC_RANDOM // any colour (except BLACK) +}; + +enum enchant_type { ENCH_NONE = 0, // 0 ENCH_SLOW, @@ -924,14 +1224,23 @@ enum ENCHANTMENT // menv[].enchantment[] NUM_ENCHANTMENTS }; -enum ENCHANT_STATS +enum enchant_retval +{ + ERV_FAIL, + ERV_NEW, + ERV_INCREASED +}; + +enum enchant_stat_type { ENCHANT_TO_HIT, ENCHANT_TO_DAM }; -enum EQUIPMENT +enum equipment_type { + EQ_NONE = -1, + EQ_WEAPON, // 0 EQ_CLOAK, EQ_HELMET, @@ -952,7 +1261,7 @@ enum EQUIPMENT EQ_ALL_ARMOUR // check all armour types }; -enum FIRE_TYPES +enum fire_type { FIRE_NONE, FIRE_LAUNCHER, @@ -962,19 +1271,24 @@ enum FIRE_TYPES FIRE_SPEAR, FIRE_HAND_AXE, FIRE_CLUB, + FIRE_ROCK, NUM_FIRE_TYPES }; -enum FLUSH_REASONS +enum flush_reason_type { FLUSH_ON_FAILURE, // spell/ability failed to cast FLUSH_BEFORE_COMMAND, // flush before getting a command FLUSH_ON_MESSAGE, // flush when printing a message - FLUSH_LUA, // flush when Lua wants us to + FLUSH_ON_WARNING_MESSAGE, // flush on MSGCH_WARN messages + FLUSH_ON_DANGER_MESSAGE, // flush on MSGCH_DANGER messages + FLUSH_ON_PROMPT, // flush on MSGCH_PROMPT messages + FLUSH_ON_UNSAFE_YES_OR_NO_PROMPT, // flush when !safe set to yesno() + FLUSH_LUA, // flush when Lua wants to flush NUM_FLUSH_REASONS }; -enum FOODS // mitm[].sub_type[] +enum food_type { FOOD_MEAT_RATION, // 0 FOOD_BREAD_RATION, @@ -1001,21 +1315,21 @@ enum FOODS // mitm[].sub_type[] NUM_FOODS }; -enum GENUS_PLAYER // see player::player_genus() +enum genus_type { GENPC_DRACONIAN, // 0 GENPC_ELVEN, // 1 GENPC_DWARVEN // 2 }; -enum GENDER +enum gender_type { GENDER_NEUTER, GENDER_MALE, GENDER_FEMALE }; -enum GHOST_VALUES +enum ghost_value_type { GVAL_MAX_HP, // 0 GVAL_EV, @@ -1047,9 +1361,9 @@ enum GHOST_VALUES GVAL_DEMONLORD_CYCLE_COLOUR // 13 }; -enum GODS // you.religion +enum god_type { - GOD_NO_GOD, // 0 + GOD_NO_GOD, // 0 -- must be zero GOD_ZIN, GOD_SHINING_ONE, GOD_KIKUBAAQUDGHA, @@ -1067,35 +1381,16 @@ enum GODS // you.religion GOD_RANDOM = 100 }; -enum GOOD_THINGS +enum hands_reqd_type { - GOOD_KILLED_LIVING = 1, // 1 - killed a living monster in god's name - GOOD_KILLED_UNDEAD, // 2 - killed an undead in god's name - GOOD_KILLED_DEMON, // 3 - killed a demon in god's name - GOOD_KILLED_ANGEL_I, // 4 - killed an angel (any time) - GOOD_KILLED_ANGEL_II, // 5 - killed an angel in god's name - // (all above pass HD of monster as pgain) - GOOD_HACKED_CORPSE, // 6 - hacked up a corpse in god's name - GOOD_OFFER_STUFF, // 7 - offered inanimate stuff at an altar - GOOD_OFFER_CORPSE, // as above,including at least one corpse - GOOD_SLAVES_KILL_LIVING,// 9 - undead slaves killed a living thing - GOOD_SERVANTS_KILL, // 10 - any servants kill anything - GOOD_CARDS, // 11 - cards (Nemelex) - GOOD_KILLED_WIZARD, - GOOD_KILLED_PRIEST, - GOOD_POISON, - GOOD_ATTACKED_FRIEND, - NUM_GOOD_THINGS -}; + HANDS_ONE, + HANDS_HALF, + HANDS_TWO, -enum HANDS_REQUIRED -{ - HANDS_ONE_HANDED = 1, // 1 - HANDS_TWO_HANDED, - HANDS_ONE_OR_TWO_HANDED + HANDS_DOUBLE // not a level, marks double ended weapons (== half) }; -enum HELMET_TYPES // used in pluses2 +enum helmet_type { THELM_HELMET = 0x0000, THELM_HELM = 0x0001, @@ -1106,7 +1401,6 @@ enum HELMET_TYPES // used in pluses2 THELM_SPECIAL = 0x0004, // type used only for artefacts (mask, hat) THELM_TYPE_MASK = 0x00ff, - THELM_DESC_PLAIN = 0x0000, THELM_DESC_WINGED = 0x0100, THELM_DESC_HORNED = 0x0200, @@ -1119,30 +1413,8 @@ enum HELMET_TYPES // used in pluses2 THELM_DESC_MASK = 0xff00 }; -#if 0 -enum HELMET_DESCRIPTIONS -{ - DHELM_WINGED = 1, // 1 - DHELM_HORNED, - DHELM_CRESTED, - DHELM_PLUMED, - DHELM_SPIKED, // 5 - DHELM_VISORED, - DHELM_JEWELLED -}; - -enum HELMET_TYPES // used in pluses2 -{ - THELM_HELMET = 0, - THELM_HELM, - THELM_CAP, - THELM_WIZARD_HAT, - NUM_HELMET_TYPES, - THELM_SPECIAL // type used only for artefacts (mask, hat) -}; -#endif -enum BOOT_TYPES // used in pluses2 +enum boot_type // used in pluses2 { TBOOT_BOOTS = 0, TBOOT_NAGA_BARDING, @@ -1151,7 +1423,7 @@ enum BOOT_TYPES // used in pluses2 }; -enum HUNGER_STATES // you.hunger_state +enum hunger_state // you.hunger_state { HS_RAVENOUS, // 0: not used within code, really HS_STARVING, @@ -1161,7 +1433,7 @@ enum HUNGER_STATES // you.hunger_state HS_ENGORGED // 5 }; -enum ITEM_STATUS_FLAGS // per item flags: ie. ident status, cursed status +enum item_status_flag_type // per item flags: ie. ident status, cursed status { ISFLAG_KNOW_CURSE = 0x00000001, // curse status ISFLAG_KNOW_TYPE = 0x00000002, // artefact name, sub/special types @@ -1175,9 +1447,9 @@ enum ITEM_STATUS_FLAGS // per item flags: ie. ident status, cursed status ISFLAG_EQ_JEWELLERY_MASK = 0x0000000F, // mask of flags for known jewellery ISFLAG_CURSED = 0x00000100, // cursed - ISFLAG_RESERVED_1 = 0x00000200, // reserved (re-curses on wield?) - ISFLAG_RESERVED_2 = 0x00000400, // reserved (heavy cursed?) - ISFLAG_RESERVED_3 = 0x00000800, // reserved (perma-cursed?) + ISFLAG_RESERVED_1 = 0x00000200, // reserved + ISFLAG_RESERVED_2 = 0x00000400, // reserved + ISFLAG_RESERVED_3 = 0x00000800, // reserved ISFLAG_CURSE_MASK = 0x00000F00, // mask of all curse related flags ISFLAG_RANDART = 0x00001000, // special value is seed @@ -1186,6 +1458,7 @@ enum ITEM_STATUS_FLAGS // per item flags: ie. ident status, cursed status ISFLAG_DROPPED = 0x00004000, // dropped item (no autopickup) ISFLAG_THROWN = 0x00008000, // thrown missile weapon + // these don't have to remain as flags ISFLAG_NO_DESC = 0x00000000, // used for clearing these flags ISFLAG_GLOWING = 0x00010000, // weapons or armour ISFLAG_RUNED = 0x00020000, // weapons or armour @@ -1201,16 +1474,17 @@ enum ITEM_STATUS_FLAGS // per item flags: ie. ident status, cursed status ISFLAG_DEBUG_MARK = 0x80000000 // used for testing item structure }; -enum ITEM_DESCRIPTIONS +enum item_description_type { IDESC_WANDS, IDESC_POTIONS, IDESC_SCROLLS, // special field (like the others) IDESC_RINGS, - IDESC_SCROLLS_II // pluses field + IDESC_SCROLLS_II, + NUM_IDESC }; -enum ITEM_MAKE_SPECIES // used only for race during creation +enum item_make_species_type { MAKE_ITEM_ELVEN = 1, MAKE_ITEM_DWARVEN = 2, @@ -1220,7 +1494,7 @@ enum ITEM_MAKE_SPECIES // used only for race during creation MAKE_ITEM_RANDOM_RACE = 250 }; -enum ITEM_ORIGIN_DUMP_SELECT +enum item_origin_dump_selector { IODS_PRICE = 0, // Extra info is provided based on price IODS_ARTIFACTS = 1, // Extra information on artifacts @@ -1234,22 +1508,23 @@ enum ITEM_ORIGIN_DUMP_SELECT IODS_EVERYTHING = 0xFF }; -enum ITEM_TYPE_ID // used for first index of id[4][50] +enum item_type_id_type { IDTYPE_WANDS = 0, IDTYPE_SCROLLS, IDTYPE_JEWELLERY, - IDTYPE_POTIONS + IDTYPE_POTIONS, + NUM_IDTYPE }; -enum ITEM_TYPE_ID_STATE // used for values in id[4][50] +enum item_type_id_state_type // used for values in id[4][50] { ID_UNKNOWN_TYPE = 0, ID_KNOWN_TYPE, ID_TRIED_TYPE }; -enum JEWELLERY +enum jewellery_type { RING_REGENERATION, // 0 RING_PROTECTION, @@ -1288,7 +1563,7 @@ enum JEWELLERY NUM_JEWELLERY }; -enum JOB +enum job_type { JOB_FIGHTER, // 0 JOB_WIZARD, @@ -1324,7 +1599,7 @@ enum JOB JOB_UNKNOWN = 100 }; -enum KILLBY +enum kill_method_type { KILLED_BY_MONSTER, // 0 KILLED_BY_POISON, @@ -1352,14 +1627,17 @@ enum KILLBY KILLED_BY_SPORE, KILLED_BY_TSO_SMITING, KILLED_BY_PETRIFICATION, // 25 - KILLED_BY_SHUGGOTH, - KILLED_BY_SOMETHING, + KILLED_BY_SOMETHING = 27, KILLED_BY_FALLING_DOWN_STAIRS, KILLED_BY_ACID, + KILLED_BY_CURARE, + KILLED_BY_MELTING, + KILLED_BY_BLEEDING, + NUM_KILLBY }; -enum KillCategory +enum kill_category { KC_YOU, KC_FRIENDLY, @@ -1367,20 +1645,28 @@ enum KillCategory KC_NCATEGORIES }; -enum KILLER // monster_die(), thing_thrown +enum killer_type // monster_die(), thing_thrown { KILL_YOU = 1, // 1 KILL_MON, KILL_YOU_MISSILE, KILL_MON_MISSILE, KILL_MISC, // 5 - KILL_RESET // abjuration, etc. + KILL_RESET, // abjuration, etc. + KILL_DISMISSED // only on new game startup }; #define YOU_KILL(x) ((x) == KILL_YOU || (x) == KILL_YOU_MISSILE) #define MON_KILL(x) ((x) == KILL_MON || (x) == KILL_MON_MISSILE) -enum LEVEL_TYPES // you.level_type +enum launch_retval +{ + LRET_FUMBLED = 0, // must be left as 0 + LRET_LAUNCHED, + LRET_THROWN +}; + +enum level_area_type // you.level_type { LEVEL_DUNGEON, // 0 LEVEL_LABYRINTH, @@ -1388,48 +1674,67 @@ enum LEVEL_TYPES // you.level_type LEVEL_PANDEMONIUM }; -enum LOAD_MODE +enum load_mode_type { LOAD_START_GAME, LOAD_RESTART_GAME, LOAD_ENTER_LEVEL }; -enum MAP_SECTIONS // see maps.cc and dungeon.cc {dlb} +// [dshaligram] Maps can be mirrored; for every orientation, there must be +// a suitable mirror. +enum map_section_type // see maps.cc and dungeon.cc {dlb} { + MAP_NONE = -1, MAP_NORTH = 1, // 1 - MAP_NORTHWEST, + MAP_SOUTH, + MAP_EAST, + MAP_WEST, + MAP_NORTHWEST, // 5 MAP_NORTHEAST, MAP_SOUTHWEST, - MAP_SOUTHEAST, // 5 - MAP_ENCOMPASS, - MAP_NORTH_DIS + MAP_SOUTHEAST, + MAP_ENCOMPASS +}; + +enum menu_type +{ + MT_INVSELECT, // General - select single item + MT_INVLIST, // List inventory + MT_DROP }; // if you mess with this list, you'll need to make changes in initfile.cc -enum MESSAGE_CHANNEL +// to message_channel_names, and probably also to message.cc to colour +// everything properly +enum msg_channel_type { MSGCH_PLAIN, // regular text MSGCH_PROMPT, // various prompts MSGCH_GOD, // god/religion (param is god) + MSGCH_PRAY, // praying messages (param is god) MSGCH_DURATION, // effect down/warnings MSGCH_DANGER, // serious life threats (ie very large HP attacks) MSGCH_WARN, // much less serious threats MSGCH_FOOD, // hunger notices MSGCH_RECOVERY, // recovery from disease/stat/poison condition + MSGCH_SOUND, // messages about things the player hears MSGCH_TALK, // monster talk (param is monster type) MSGCH_INTRINSIC_GAIN, // player level/stat/species-power gains MSGCH_MUTATION, // player gain/lose mutations MSGCH_MONSTER_SPELL, // monsters casting spells MSGCH_MONSTER_ENCHANT,// monsters enchantments up and down MSGCH_MONSTER_DAMAGE, // monster damage reports (param is level) + MSGCH_MONSTER_TARGET, // message marking the monster as a target MSGCH_ROTTEN_MEAT, // messages about chunks/corpses becoming rotten MSGCH_EQUIPMENT, // equipment listing messages - MSGCH_DIAGNOSTICS, // various diagnostic messages + MSGCH_FLOOR_ITEMS, // like equipment, but lists of floor items + MSGCH_MULTITURN_ACTION, // delayed action messages + MSGCH_DIAGNOSTICS, // various diagnostic messages NUM_MESSAGE_CHANNELS // always last }; -enum MESSAGE_COLOURS +enum msg_colour_type { MSGCOL_BLACK = 0, // the order of these colours is important MSGCOL_BLUE, @@ -1452,7 +1757,7 @@ enum MESSAGE_COLOURS MSGCOL_PLAIN // same as plain channel }; -enum MISCELLANY // mitm[].sub_type +enum misc_item_type { MISC_BOTTLED_EFREET, // 0 MISC_CRYSTAL_BALL_OF_SEEING, @@ -1475,7 +1780,7 @@ enum MISCELLANY // mitm[].sub_type NUM_MISCELLANY // mv: used for random generation }; -enum MISSILES // (unsigned char) +enum missile_type { MI_STONE, // 0 MI_ARROW, @@ -1484,10 +1789,83 @@ enum MISSILES // (unsigned char) MI_NEEDLE, MI_LARGE_ROCK, //jmf: it'd be nice to move MI_LARGE_ROCK to DEBRIS_ROCK NUM_MISSILES, - MI_EGGPLANT + MI_NONE // was MI_EGGPLANT... used for launch type detection +}; + +// properties of the monster class (other than resists/vulnerabilities) +enum mons_class_flags +{ + M_NO_FLAGS = 0, + + M_SPELLCASTER = (1<< 0), // any non-physical-attack powers, + M_ACTUAL_SPELLS = (1<< 1), // monster is a wizard, + M_PRIEST = (1<< 2), // monster is a priest + M_FIGHTER = (1<< 3), // monster is skilled fighter + + M_FLIES = (1<< 4), // will crash to ground if paralysed? + M_LEVITATE = (1<< 5), // ... but not if this is set + M_INVIS = (1<< 6), // is created invis + M_SEE_INVIS = (1<< 7), // can see invis + M_SPEAKS = (1<< 8), // uses talking code + M_CONFUSED = (1<< 9), // monster is perma-confused, + M_BATTY = (1<<10), // monster is batty + M_SPLITS = (1<<11), // monster can split + M_AMPHIBIOUS = (1<<12), // monster can swim in water, + M_THICK_SKIN = (1<<13), // monster has more effective AC, + M_HUMANOID = (1<<14), // for Glamour + M_COLD_BLOOD = (1<<15), // susceptible to cold + M_WARM_BLOOD = (1<<16), // no effect currently + M_REGEN = (1<<17), // regenerates quickly + M_BURROWS = (1<<18), // monster digs through rock + M_EVIL = (1<<19), // monster vulnerable to holy spells + + M_UNIQUE = (1<<20), // monster is a unique + M_FROZEN = (1<<21), // XXX: Potentially ditchable + + + M_SPECIAL_ABILITY = (1<<26), // XXX: eventually make these spells? + M_COLOUR_SHIFT = (1<<27), // flag for element colour shifters + M_DCHAR_SYMBOL = (1<<28), // monster looks like a DCHAR terrain + + M_NO_SKELETON = (1<<29), // boneless corpses + M_NO_WOUNDS = (1<<30), // doesn't show would level + M_NO_EXP_GAIN = (1<<31) // worth 0 xp +}; + +enum mon_resist_flags +{ + MR_NO_FLAGS = 0, + + // resistances + // Notes: + // - negative energy is mostly handled via mons_has_life_force() + // - acid is handled mostly by genus (jellies) plus non-living + // - asphyx-resistance replaces hellfrost resistance. + MR_RES_ELEC = (1<< 0), + MR_RES_POISON = (1<< 1), + MR_RES_FIRE = (1<< 2), + MR_RES_HELLFIRE = (1<< 3), + MR_RES_COLD = (1<< 4), + MR_RES_ASPHYX = (1<< 5), + + // vulnerabilities + MR_VUL_ELEC = (1<< 6), + MR_VUL_POISON = (1<< 7), + MR_VUL_FIRE = (1<< 8), + MR_VUL_COLD = (1<< 9), + + // melee armour resists/vulnerabilities + // XXX: how to do combos (bludgeon/slice, bludgeon/pierce) + MR_RES_PIERCE = (1<<10), + MR_RES_SLICE = (1<<11), + MR_RES_BLUDGEON = (1<<12), + + MR_VUL_PIERCE = (1<<13), + MR_VUL_SLICE = (1<<14), + MR_VUL_BLUDGEON = (1<<15) }; -enum MON_TARG_MODE +enum targ_mode_type { TARG_ANY, TARG_ENEMY, @@ -1495,7 +1873,8 @@ enum MON_TARG_MODE TARG_NUM_MODES }; -enum MONSTERS // (int) menv[].type +// note this order is very sensitive... look at mons_is_unique() +enum monster_type // (int) menv[].type { MONS_GIANT_ANT, // 0 MONS_GIANT_BAT, @@ -1662,6 +2041,7 @@ enum MONSTERS // (int) menv[].type MONS_RED_WASP, // 170 MONS_SWAMP_DRAGON, MONS_SWAMP_DRAKE, + MONS_DEATH_DRAKE, MONS_SOLDIER_ANT, MONS_HILL_GIANT, MONS_QUEEN_ANT, // 175 @@ -1775,6 +2155,23 @@ enum MONSTERS // (int) menv[].type MONS_BORIS, // 310 // BCR - end second batch of uniques. + MONS_DRACONIAN, + MONS_BLACK_DRACONIAN, + MONS_MOTTLED_DRACONIAN, + MONS_YELLOW_DRACONIAN, + MONS_GREEN_DRACONIAN, // 315 + MONS_PURPLE_DRACONIAN, + MONS_RED_DRACONIAN, + MONS_WHITE_DRACONIAN, + MONS_PALE_DRACONIAN, + MONS_DRACONIAN_CALLER, // 320 + MONS_DRACONIAN_MONK, + MONS_DRACONIAN_ZEALOT, + MONS_DRACONIAN_SHIFTER, + MONS_DRACONIAN_ANNIHILATOR, + MONS_DRACONIAN_KNIGHT, // 325 + MONS_DRACONIAN_SCORCHER, + // The Lords of Hell: MONS_GERYON = 340, // 340 MONS_DISPATER, @@ -1848,57 +2245,62 @@ enum MONSTERS // (int) menv[].type MONS_WATER_ELEMENTAL, MONS_SWAMP_WORM, // 435 + // Statuary + MONS_ORANGE_STATUE, + MONS_SILVER_STATUE, + NUM_MONSTERS, // used for polymorph RANDOM_MONSTER = 1000, // used to distinguish between a random monster and using program bugs for error trapping {dlb} WANDERING_MONSTER = 2500 // only used in monster placement routines - forced limit checks {dlb} }; -enum MONSTER_BEHAVIOUR // create_monster() +enum beh_type { BEH_SLEEP, // 0 BEH_WANDER, BEH_SEEK, BEH_FLEE, BEH_CORNERED, + BEH_PANIC, // like flee but without running away + BEH_INVESTIGATE, // investigating an ME_DISTURB NUM_BEHAVIOURS, // max # of legal states BEH_CHARMED, // hostile-but-charmed; create only BEH_FRIENDLY, // used during creation only BEH_HOSTILE, // creation only - BEH_GOD_GIFT // creation only + BEH_GOD_GIFT, // creation only + BEH_GUARD // creation only - monster is guard }; -enum MONSTER_ATTITUDES +enum mon_attitude_type { ATT_HOSTILE, // 0, default in most cases ATT_FRIENDLY, // created friendly (or tamed?) ATT_NEUTRAL }; -enum MONSTER_EVENTS +enum mon_event_type { ME_EVAL, // 0, evaluate monster AI state ME_DISTURB, // noisy ME_ANNOY, // annoy at range ME_ALERT, // alert to presence ME_WHACK, // physical attack + ME_SHOT, // attack at range ME_SCARE, // frighten monster ME_CORNERED // cannot flee }; -#if 0 -// Obsolete... use mons_charclass() -enum MONSTER_CATEGORIES +enum mon_flight_type { - MC_MIMIC, // 0 - NUM_MC, - MC_UNSPECIFIED = 255 // keep at end !!! mind the upper limit of 255 {dlb} + FLY_NOT, + FLY_POWERED, // wings, etc... paralysis == fall + FLY_LEVITATION // doesn't require physical effort }; -#endif // Note: These are currently stored in chars!!! // Need to fix struct monsters and the savefile if you want more. -enum MONSTER_FLAGS +enum monster_flag_type { MF_CREATED_FRIENDLY = 0x01, // no benefit from killing MF_GOD_GIFT = 0x02, // player not penalized by its death @@ -1906,12 +2308,12 @@ enum MONSTER_FLAGS MF_JUST_SUMMONED = 0x08, // monster skips next available action MF_TAKING_STAIRS = 0x10, // is following player through stairs - MF_UNUSED_I = 0x20, - MF_UNUSED_II = 0x40, - MF_UNUSED_III = 0x80 + MF_INTERESTING = 0x20, // Player finds monster interesting + MF_SEEN = 0x40, // Player already seen monster + MF_UNUSED_I = 0x80 }; -enum MONSTER_DAMAGE +enum mon_dam_level_type { MDAM_OKAY, MDAM_LIGHTLY_DAMAGED, @@ -1922,14 +2324,14 @@ enum MONSTER_DAMAGE MDAM_DEAD }; -enum MONSTER_DESCRIPTORS // things that cross categorical lines {dlb} +enum mon_desc_type // things that cross categorical lines {dlb} { MDSC_LEAVES_HIDE, // 0 MDSC_REGENERATES, MDSC_NOMSG_WOUNDS }; -enum MONSTER_HOLINESS // matches (char) H_foo in mon-util.h, see: monster_holiness() +enum mon_holy_type // matches (char) H_foo in mon-util.h, see: monster_holiness() { MH_HOLY, // 0 - was -1 MH_NATURAL, // 1 - was 0 @@ -1939,7 +2341,7 @@ enum MONSTER_HOLINESS // matches (char) H_foo in mon-util.h, see: monster_holine MH_PLANT // plants }; -enum MONSTER_INVENTORY_SLOTS // (int) menv[].inv[] +enum mon_inv_type // (int) menv[].inv[] { MSLOT_WEAPON, MSLOT_MISSILE, // although it is a second weapon for MONS_TWO_HEADED_OGRE - how to reconcile cleanly? {dlb} @@ -1952,16 +2354,20 @@ enum MONSTER_INVENTORY_SLOTS // (int) menv[].inv[] NUM_MONSTER_SLOTS = 8 // value must remain 8 for savefile compatibility {dlb} }; -enum MONSTER_ITEM_USE +// order of these is important: +enum mon_itemuse_type { MONUSE_NOTHING, MONUSE_EATS_ITEMS, MONUSE_OPEN_DOORS, MONUSE_STARTING_EQUIPMENT, - MONUSE_WEAPONS_ARMOUR + MONUSE_WEAPONS_ARMOUR, + MONUSE_MAGIC_ITEMS }; -enum MONSTER_SPELLS // mons_cast(), mspell_list[], mons_spells() +// XXX: someday merge these into SPELL_ +// If changing this list, keep mon-util.cc spell names in sync. +enum mon_spell_type { MS_MMISSILE, // 0 MS_FLAME, @@ -2016,12 +2422,25 @@ enum MONSTER_SPELLS // mons_cast(), mspell_list[], mons_spells() MS_METAL_SPLINTERS, // 50 MS_SUMMON_DEMON_GREATER, // [foo]_1 was confusing - renamed 13jan2000 {dlb} MS_BANISHMENT, + MS_CONTROLLED_BLINK, + MS_CONTROL_UNDEAD, + MS_MIASMA, // 55 + MS_SUMMON_LIZARDS, + MS_BLINK_OTHER, + MS_DISPEL_UNDEAD, + MS_HELLFROST, + MS_POISON_ARROW, // 60 + MS_SUMMON_SMALL_MAMMALS, + MS_SUMMON_MUSHROOMS, + // XXX: before adding more monster versions of player spells we should + // consider merging the two lists into one and just having monsters + // fail to implement the ones that are impractical. NUM_MONSTER_SPELLS, MS_NO_SPELL = 100 }; // XXX: These still need to be applied in mon-data.h -enum MONSTER_SPELL_TEMPLATES +enum mon_spellbook_type { MST_ORC_WIZARD_I = 0, MST_ORC_WIZARD_II, @@ -2036,7 +2455,8 @@ enum MONSTER_SPELL_TEMPLATES MST_VAMPIRE_KNIGHT, MST_VAMPIRE_MAGE, MST_EFREET = 50, - MST_BRAIN_WORM = 52, + MST_KILLER_KLOWN, + MST_BRAIN_WORM, MST_GIANT_ORANGE_BRAIN, MST_RAKSHASA, MST_GREAT_ORB_OF_EYES, // 55 @@ -2109,13 +2529,19 @@ enum MONSTER_SPELL_TEMPLATES MST_BOGGART, MST_EYE_OF_DEVASTATION, // 125 MST_QUICKSILVER_DRAGON, - MST_IRON_DRAGON, // 127 + MST_IRON_DRAGON, MST_SKELETAL_WARRIOR, + MST_MYSTIC, + MST_DEATH_DRAKE, // 130 + MST_DRAC_SCORCHER, // As Bioster would say.. pig*s + MST_DRAC_CALLER, + MST_DRAC_SHIFTER, + MST_CURSE_TOE, NUM_MSTYPES, MST_NO_SPELLS = 250 }; -enum MUTATIONS +enum mutation_type { MUT_TOUGH_SKIN, // 0 MUT_STRONG, @@ -2201,25 +2627,7 @@ enum MUTATIONS NUM_MUTATIONS }; -enum NAUGHTY_THINGS -{ - NAUGHTY_NECROMANCY = 1, // 1 - using necromancy (spell or device) - NAUGHTY_UNHOLY, // 2 - using unholy stuff (call imp, summon things) - NAUGHTY_KILLING, // 3 - killing in the name of a peaceful deity - NAUGHTY_ATTACK_HOLY, // 4 - attacking holy things - NAUGHTY_ATTACK_FRIEND, // 5 - attacking friendly things - NAUGHTY_FRIEND_DIES, // 6 - allowing friendly things to die - NAUGHTY_BUTCHER, // 7 - butchering in the name of a peaceful deity - NAUGHTY_STABBING, // 8 - stabbing - NAUGHTY_SPELLCASTING, // 9 - spellcasting - NAUGHTY_POISON, // 10 - poisoning - NAUGHTY_STIMULANTS, //jmf: next three new, some not yet used - NAUGHTY_ATE_MEAT, - NAUGHTY_CREATED_LIFE, - NUM_NAUGHTY_THINGS -}; - -enum OBJECT_CLASSES // (unsigned char) mitm[].base_type +enum object_class_type // (unsigned char) mitm[].base_type { OBJ_WEAPONS, // 0 OBJ_MISSILES, @@ -2244,18 +2652,86 @@ enum OBJECT_CLASSES // (unsigned char) mitm[].base_type // for blanket random sub_type .. see dungeon::items() }; -enum OBJECT_SELECTORS +enum object_selector { OSEL_ANY = -1, OSEL_WIELD = -2 }; -enum ORBS +enum operation_types +{ + OPER_WIELD = 'w', + OPER_QUAFF = 'q', + OPER_DROP = 'd', + OPER_EAT = 'e', + OPER_TAKEOFF = 'T', + OPER_WEAR = 'W', + OPER_PUTON = 'P', + OPER_REMOVE = 'R', + OPER_READ = 'r', + OPER_MEMORISE = 'M', + OPER_ZAP = 'z', + OPER_THROW = 't', + OPER_EXAMINE = 'v', + OPER_ANY = 0 +}; + +enum orb_type { ORB_ZOT // 0 }; -enum POTIONS +enum player_size_type +{ + PSIZE_BODY, // entire body size -- used for EV/size of target + PSIZE_TORSO, // torso only (hybrids -- size of parts that use equip) + PSIZE_PROFILE // profile only (for stealth checks) +}; + +// [dshaligram] If you edit potion colours/descriptions, also update +// itemname.cc. +enum potion_description_colour_type +{ + PDC_CLEAR, + PDC_BLUE, + PDC_BLACK, + PDC_SILVERY, + PDC_CYAN, + PDC_PURPLE, + PDC_ORANGE, + PDC_INKY, + PDC_RED, + PDC_YELLOW, + PDC_GREEN, + PDC_BROWN, + PDC_PINK, + PDC_WHITE, + PDC_NCOLOURS +}; + +// [dshaligram] If you edit potion colours/descriptions, also update +// itemname.cc. +enum potion_description_qualifier_type +{ + PDQ_NONE, + PDQ_BUBBLING, + PDQ_FUMING, + PDQ_FIZZY, + PDQ_VISCOUS, + PDQ_LUMPY, + PDQ_SMOKY, + PDQ_GLOWING, + PDQ_SEDIMENTED, + PDQ_METALLIC, + PDQ_MURKY, + PDQ_GLUGGY, + PDQ_OILY, + PDQ_SLIMY, + PDQ_EMULSIFIED, + PDQ_NQUALS +}; + +enum potion_type { POT_HEALING, // 0 POT_HEAL_WOUNDS, @@ -2284,7 +2760,7 @@ enum POTIONS NUM_POTIONS }; -enum PRONOUN_TYPE +enum pronoun_type { PRONOUN_CAP, // 0 PRONOUN_NOCAP, // 1 @@ -2293,7 +2769,7 @@ enum PRONOUN_TYPE PRONOUN_REFLEXIVE // 4 (reflexive is always lowercase) }; -enum PROXIMITY // proximity to player to create monster +enum proximity_type // proximity to player to create monster { PROX_ANYWHERE, PROX_CLOSE_TO_PLAYER, @@ -2301,7 +2777,7 @@ enum PROXIMITY // proximity to player to create monster PROX_NEAR_STAIRS }; -enum RANDART_PROP +enum randart_prop_type { RAP_BRAND, // 0 RAP_AC, @@ -2332,17 +2808,25 @@ enum RANDART_PROP RAP_ACCURACY, RAP_DAMAGE, RAP_CURSED, - RAP_STEALTH + RAP_STEALTH, + RAP_NUM_PROPERTIES }; -enum READ_BOOK_ACTION +enum read_book_action_type { RBOOK_USE_STAFF, - RBOOK_MEMORIZE, + RBOOK_MEMORISE, RBOOK_READ_SPELL }; -enum RUN_DIR +enum run_check_type +{ + RCHECK_LEFT, + RCHECK_FRONT, + RCHECK_RIGHT +}; + +enum run_dir_type { RDIR_UP = 0, RDIR_UP_RIGHT, @@ -2355,7 +2839,18 @@ enum RUN_DIR RDIR_REST }; -enum RUNE_TYPES +enum run_mode_type +{ + RMODE_INTERLEVEL = -3, // Interlevel travel (Ctrl+G) + RMODE_EXPLORE = -2, // Exploring (Ctrl+O) + RMODE_TRAVEL = -1, // Classic or Plain Old travel + RMODE_NOT_RUNNING = 0, // must remain equal to 0 + RMODE_CONTINUE, + RMODE_START, + RMODE_REST_DURATION = 100 +}; + +enum rune_type { // Note: that runes DIS-SWAMP have the same numberic value as the branch RUNE_DIS = 1, @@ -2379,17 +2874,18 @@ enum RUNE_TYPES RUNE_LOM_LOBON, RUNE_CEREBOV, RUNE_GLOORX_VLOQ, - NUM_RUNE_TYPES // should always be last + NUM_RUNE_TYPES, // should always be last + RUNE_NONE }; -enum SCORE_FORMAT +enum score_format_type { SCORE_TERSE, // one line SCORE_REGULAR, // two lines (name, cause, blank) SCORE_VERBOSE // everything (dates, times, god, etc) }; -enum SCROLLS +enum scroll_type { SCR_IDENTIFY, // 0 SCR_TELEPORTATION, @@ -2417,7 +2913,7 @@ enum SCROLLS NUM_SCROLLS }; -enum SHOPS // (unsigned char) env.sh_type[], item_in_shop(), in_a_shop() +enum shop_type // (unsigned char) env.sh_type[], item_in_shop(), in_a_shop() { SHOP_WEAPON, // 0 SHOP_ARMOUR, @@ -2436,7 +2932,47 @@ enum SHOPS // (unsigned char) env.sh_type[], item_in_shop(), in_a_shop() SHOP_RANDOM = 255 // keep set at 255 for now {dlb} }; -enum SKILLS +enum shout_type +{ + S_SILENT, // silent + S_SHOUT, // shout + S_BARK, // bark + S_SHOUT2, // shout twice + S_ROAR, // roar + S_SCREAM, // scream + S_BELLOW, // bellow (?) + S_SCREECH, // screech + S_BUZZ, // buzz + S_MOAN, // moan + S_WHINE, // irritating whine (mosquito) + S_CROAK, // frog croak + S_GROWL, // for bears + S_HISS, // for snakes and lizards + NUM_SHOUTS, + S_RANDOM +}; + +// These are often addressed relative to each other (esp. delta SIZE_MEDIUM) +enum size_type +{ + SIZE_TINY, // rat/bat + SIZE_LITTLE, // spriggan + SIZE_SMALL, // halfling/kobold/gnome + SIZE_MEDIUM, // human/elf/dwarf + SIZE_LARGE, // troll/ogre + SIZE_BIG, // centaur/naga/large quadrupeds + SIZE_GIANT, // giant + SIZE_HUGE, // dragon + NUM_SIZE_LEVELS, + SIZE_CHARACTER // transformations that don't change size +}; + +// [dshaligram] If you add a new skill, update skills2.cc, specifically +// the skills[] array and skill_display_order[]. New skills must go at the +// end of the list or in the unused skill numbers. NEVER rearrange this enum or +// move existing skills to new numbers; save file compatibility depends on this +// order. +enum skill_type { SK_FIGHTING, // 0 SK_SHORT_BLADES, @@ -2450,7 +2986,7 @@ enum SKILLS SK_BOWS, SK_CROSSBOWS, // 10 SK_DARTS, - SK_THROWING, + SK_RANGED_COMBAT, SK_ARMOUR, SK_DODGING, SK_STEALTH, // 15 @@ -2473,10 +3009,14 @@ enum SKILLS SK_POISON_MAGIC, SK_INVOCATIONS, SK_EVOCATIONS, - NUM_SKILLS // must remain last member {dlb} + NUM_SKILLS, // must remain last regular member + + SK_BLANK_LINE, // used for skill output + SK_COLUMN_BREAK, // used for skill output + SK_NONE }; -enum SPECIAL_ARMOR +enum special_armour_type { SPARM_NORMAL, // 0 SPARM_RUNNING, @@ -2504,32 +3044,42 @@ enum SPECIAL_ARMOR SPARM_RANDART_V = 29 // 29 - highest value found thus far {dlb} }; -enum SPECIAL_MISSILES // to separate from weapons in general {dlb} +enum special_missile_type // to separate from weapons in general {dlb} { SPMSL_NORMAL, // 0 SPMSL_FLAME, // 1 SPMSL_ICE, // 2 SPMSL_POISONED, // 3 - from poison_ammo() enchantment {dlb} - SPMSL_POISONED_II // 4 + SPMSL_POISONED_II, // 4 + SPMSL_CURARE // 5 }; -enum SPECIAL_ROOMS +enum special_room_type { SROOM_LAIR_ORC, // 0 SROOM_LAIR_KOBOLD, SROOM_TREASURY, SROOM_BEEHIVE, + SROOM_JELLY_PIT, SROOM_MORGUE, NUM_SPECIAL_ROOMS // 5 - must remain final member {dlb} }; -enum SPECIAL_RINGS // jewellery mitm[].special values +enum special_ring_type // jewellery mitm[].special values { SPRING_RANDART = 200, SPRING_UNRANDART = 201 }; -enum SPECIAL_WEAPONS // equivalent to (you.inv[].special or mitm[].special) % 30 +// order is important on these (see player_speed()) +enum speed_type +{ + SPEED_SLOWED, + SPEED_NORMAL, + SPEED_HASTED +}; + +enum brand_type // equivalent to (you.inv[].special or mitm[].special) % 30 { SPWPN_NORMAL, // 0 SPWPN_FLAMING, @@ -2579,7 +3129,7 @@ enum SPECIAL_WEAPONS // equivalent to (you.inv[].special or mitm[].special) % 30 SPWPN_STAFF_OF_WUCAD_MU // 195 }; -enum SPECIAL_WIELD // you.special_wield +enum special_wield_type // you.special_wield { SPWLD_NONE, // 0 SPWLD_SING, @@ -2599,7 +3149,7 @@ enum SPECIAL_WIELD // you.special_wield SPWLD_SHOUT // 54 - see it_use3::special_wielded() {dlb} }; -enum SPECIES +enum species_type { SP_HUMAN = 1, // 1 SP_ELF, @@ -2629,7 +3179,7 @@ enum SPECIES SP_PALE_DRACONIAN, SP_UNK0_DRACONIAN, SP_UNK1_DRACONIAN, - SP_UNK2_DRACONIAN, + SP_BASE_DRACONIAN, SP_CENTAUR, // 30 SP_DEMIGOD, SP_SPRIGGAN, @@ -2643,7 +3193,7 @@ enum SPECIES SP_UNKNOWN = 100 }; -enum SPELLS +enum spell_type { SPELL_IDENTIFY, // 0 SPELL_TELEPORT_SELF, @@ -2827,18 +3377,38 @@ enum SPELLS SPELL_AIR_WALK, // "dematerialize" (air/transmigration) SPELL_SANDBLAST, // mini-frag; can use stones for material comp 195 SPELL_ROTTING, // evil god power or necromantic transmigration - SPELL_SHUGGOTH_SEED, // evil god power or necromantic summoning SPELL_MAXWELLS_SILVER_HAMMER, // vorpal-brand maces etc. SPELL_CONDENSATION_SHIELD, // "shield" of icy vapour - SPELL_SEMI_CONTROLLED_BLINK, //jmf: to test effect 200 - SPELL_STONESKIN, + SPELL_SEMI_CONTROLLED_BLINK, //jmf: to test effect + SPELL_STONESKIN, // 200 SPELL_SIMULACRUM, - SPELL_CONJURE_BALL_LIGHTNING, // 203 (be wary of 210, see below) + SPELL_CONJURE_BALL_LIGHTNING, + SPELL_CHAIN_LIGHTNING, // 203 (be wary of 209/210, see below) NUM_SPELLS, SPELL_NO_SPELL = 210 // 210 - added 22jan2000 {dlb} }; -enum SPELL_TYPES //jmf: 24jul2000: changed from integer-list to bitfield +enum spflag_type +{ + SPFLAG_NONE = 0x0000, + SPFLAG_DIR_OR_TARGET = 0x0001, // use DIR_NONE targeting + SPFLAG_TARGET = 0x0002, // use DIR_TARGET targeting + SPFLAG_GRID = 0x0004, // use DIR_GRID targeting + SPFLAG_DIR = 0x0008, // use DIR_DIR targeting + SPFLAG_TARGETING_MASK = 0x000f, // used to test for targeting + SPFLAG_HELPFUL = 0x0010, // TARG_FRIENDS used + SPFLAG_NOT_SELF = 0x0020, // aborts on isMe + SPFLAG_UNHOLY = 0x0040 // counts at "unholy" +}; + +enum spret_type +{ + SPRET_ABORT = 0, // should be left as 0 + SPRET_FAIL, + SPRET_SUCCESS +}; + +enum spschool_flag_type { SPTYP_NONE = 0, // "0" is reserved for no type at all {dlb} SPTYP_CONJURATION = 1, // was 11, but only for old typematch routine {dlb} @@ -2859,13 +3429,13 @@ enum SPELL_TYPES //jmf: 24jul2000: changed from integer-list to bitfield SPTYP_RANDOM = 1<<14 }; -enum SLOT_SELECT_MODES +enum slot_select_mode { SS_FORWARD = 0, SS_BACKWARD = 1 }; -enum STATS +enum stat_type { STAT_STRENGTH, // 0 STAT_DEXTERITY, @@ -2875,14 +3445,14 @@ enum STATS STAT_RANDOM = 255 // leave at 255, added for increase_stats() handling {dlb} }; -enum STATUE_TYPES +enum statue_type { STATUE_SILVER, STATUE_ORANGE_CRYSTAL, NUM_STATUE_TYPES }; -enum STATUS_REDRAW_FLAGS +enum status_redraw_flag_type { REDRAW_HUNGER = 0x00000001, REDRAW_BURDEN = 0x00000002, @@ -2908,7 +3478,7 @@ enum STATUS_REDRAW_FLAGS REDRAW_LINE_3_MASK = 0x007f0000 }; -enum STAVES +enum stave_type { STAFF_WIZARDRY, // 0 STAFF_POWER, @@ -2936,7 +3506,8 @@ enum STAVES NUM_STAVES // must remain last member {dlb} }; -enum SYMBOLS // beam[].type - note that this (and its variants) also accepts values from other enums - confusing {dlb} +// beam[].type - note that this (and its variants) also accepts values from other enums - confusing {dlb} +enum zap_symbol_type { SYM_SPACE = ' ', // 32 SYM_FLASK = '!', // 33 @@ -2951,10 +3522,11 @@ enum SYMBOLS // beam[].type - note that this (and its variants) also accepts val SYM_SCROLL = '?', // 63 SYM_DEBUG = 'X', // 88 SYM_ARMOUR = '[', // 91 - SYM_MISSILE = '`' // 96 + SYM_MISSILE = '`', // 96 + SYM_EXPLOSION = '#' }; -enum TAGS // used during save/load process to identify data blocks +enum tag_type // used during save/load process to identify data blocks { TAG_VERSION = 0, // should NEVER be read in! TAG_YOU = 1, // 'you' structure @@ -2968,15 +3540,25 @@ enum TAGS // used during save/load process to identify data blocks NUM_TAGS }; -enum TAGTYPES // file types supported by tag system +enum tag_file_type // file types supported by tag system { TAGTYPE_PLAYER=0, // Foo.sav TAGTYPE_LEVEL, // Foo.00a, .01a, etc. - TAGTYPE_GHOST // bones.xxx + TAGTYPE_GHOST, // bones.xxx + + TAGTYPE_PLAYER_NAME // Used only to read the player name }; +enum torment_source_type +{ + TORMENT_GENERIC = -1, + TORMENT_CARDS = -2, // Symbol of torment + TORMENT_SPWLD = -3, // Special wield torment + TORMENT_SCROLL = -4, + TORMENT_SPELL = -5 // SPELL_SYMBOL_OF_TORMENT +}; -enum TRANSFORMATIONS +enum transformation_type { TRAN_NONE, // 0 TRAN_SPIDER, @@ -2990,7 +3572,7 @@ enum TRANSFORMATIONS NUM_TRANSFORMATIONS // must remain last member {dlb} }; -enum TRAPS // env.trap_type[] +enum trap_type // env.trap_type[] { TRAP_DART, // 0 TRAP_ARROW, @@ -3004,10 +3586,11 @@ enum TRAPS // env.trap_type[] TRAP_NEEDLE, NUM_TRAPS, // must remain last 'regular' member {dlb} TRAP_UNASSIGNED = 100, // keep set at 100 for now {dlb} + TRAP_NONTELEPORT = 254, TRAP_RANDOM = 255 // set at 255 to avoid potential conflicts {dlb} }; -enum UNARMED_ATTACKS +enum unarmed_attack_type { UNAT_NO_ATTACK, // 0 UNAT_KICK, @@ -3016,31 +3599,150 @@ enum UNARMED_ATTACKS UNAT_PUNCH }; -enum UNDEAD_STATES // you.is_undead +enum undead_state_type // you.is_undead { US_ALIVE, // 0 US_HUNGRY_DEAD, US_UNDEAD }; -enum UNIQUE_ITEM_STATUS +enum unique_item_status_type { UNIQ_NOT_EXISTS = 0, UNIQ_EXISTS = 1, UNIQ_LOST_IN_ABYSS = 2 }; -enum VORPAL_DESCRIPTIONS -{ - DVORP_CRUSHING, // 0 - DVORP_SLICING, - DVORP_PIERCING, - DVORP_CHOPPING +// NOTE: THE ORDER AND VALUE OF THESE IS CURRENTLY VERY IMPORTANT! +enum vault_type +{ + VAULT_VAULT_1 = 0, + VAULT_VAULT_2 = 1, + VAULT_VAULT_3 = 2, + VAULT_VAULT_4 = 3, + VAULT_VAULT_5 = 4, + VAULT_VAULT_6 = 5, + VAULT_VAULT_7 = 6, + VAULT_VAULT_8 = 7, + VAULT_VAULT_9 = 8, + VAULT_VAULT_10 = 9, + VAULT_ORC_TEMPLE = 10, + VAULT_FARM_AND_COUNTRY = 11, + VAULT_FORT_YAKTAUR = 12, + VAULT_BOX_LEVEL = 13, + VAULT_MY_MAP = 14, + + VAULT_VESTIBULE_MAP = 50, + VAULT_CASTLE_DIS = 51, + VAULT_ASMODEUS = 52, + VAULT_ANTAEUS = 53, + VAULT_ERESHKIGAL = 54, + + VAULT_MNOLEG = 60, + VAULT_LOM_LOBON = 61, + VAULT_CEREBOV = 62, + VAULT_GLOORX_VLOQ = 63, + // VAULT_MOLLUSC = 64, + + VAULT_BEEHIVE = 80, + VAULT_SLIME_PIT = 81, + VAULT_BOTTOM_OF_VAULTS = 82, + VAULT_HALL_OF_BLADES = 83, + VAULT_HALL_OF_ZOT = 84, + VAULT_TEMPLE = 85, + VAULT_SNAKE_PIT = 86, + VAULT_ELF_HALL = 87, + VAULT_TOMB_1 = 88, + VAULT_TOMB_2 = 89, + VAULT_TOMB_3 = 90, + VAULT_SWAMP = 91, + + VAULT_RANDOM = 100, + + VAULT_MINIVAULT_1 = 200, + VAULT_MINIVAULT_2 = 201, + VAULT_MINIVAULT_3 = 202, + VAULT_MINIVAULT_4 = 203, + VAULT_MINIVAULT_5 = 204, + VAULT_MINIVAULT_6 = 205, + VAULT_MINIVAULT_7 = 206, + VAULT_MINIVAULT_8 = 207, + VAULT_MINIVAULT_9 = 208, + VAULT_MINIVAULT_10 = 209, + VAULT_MINIVAULT_11 = 210, + VAULT_MINIVAULT_12 = 211, + VAULT_MINIVAULT_13 = 212, + VAULT_MINIVAULT_14 = 213, + VAULT_MINIVAULT_15 = 214, + VAULT_MINIVAULT_16 = 215, + VAULT_MINIVAULT_17 = 216, + VAULT_MINIVAULT_18 = 217, + VAULT_MINIVAULT_19 = 218, + VAULT_MINIVAULT_20 = 219, + VAULT_MINIVAULT_21 = 220, + VAULT_MINIVAULT_22 = 221, + VAULT_MINIVAULT_23 = 222, + VAULT_MINIVAULT_24 = 223, + VAULT_MINIVAULT_25 = 224, + VAULT_MINIVAULT_26 = 225, + VAULT_MINIVAULT_27 = 226, + VAULT_MINIVAULT_28 = 227, + VAULT_MINIVAULT_29 = 228, + VAULT_MINIVAULT_30 = 229, + VAULT_MINIVAULT_31 = 230, + VAULT_MINIVAULT_32 = 231, + VAULT_MINIVAULT_33 = 232, + VAULT_MINIVAULT_34 = 233, + VAULT_MINIVAULT_35 = 234, + + VAULT_RAND_DEMON_1 = 300, + VAULT_RAND_DEMON_2 = 301, + VAULT_RAND_DEMON_3 = 302, + VAULT_RAND_DEMON_4 = 303, + VAULT_RAND_DEMON_5 = 304, + VAULT_RAND_DEMON_6 = 305, + VAULT_RAND_DEMON_7 = 306, + VAULT_RAND_DEMON_8 = 307, + VAULT_RAND_DEMON_9 = 308 +}; + +enum vorpal_damage_type +{ + // Types of damage a weapon can do... currently assuming that anything + // with BLUDGEON always does "AND" with any other specified types, + // and and sets not including BLUDGEON are "OR". + DAM_BASH = 0x0000, // non-melee weapon blugeoning + DAM_BLUDGEON = 0x0001, // crushing + DAM_SLICE = 0x0002, // slicing/chopping + DAM_PIERCE = 0x0004, // stabbing/piercing + DAM_WHIP = 0x0008, // whip slashing (no butcher) + + // These are used for vorpal weapon desc (don't set more than one) + DVORP_NONE = 0x0000, // used for non-melee weapons + DVORP_CRUSHING = 0x1000, + DVORP_SLICING = 0x2000, + DVORP_PIERCING = 0x3000, + DVORP_CHOPPING = 0x4000, // used for axes + DVORP_SLASHING = 0x5000, // used for whips + DVORP_STABBING = 0x6000, // used for knives/daggers + + // These are shortcuts to tie vorpal/damage types for easy setting... + // as above, setting more than one vorpal type is trouble. + DAMV_NON_MELEE = DVORP_NONE | DAM_BASH, // launchers + DAMV_CRUSHING = DVORP_CRUSHING | DAM_BLUDGEON, + DAMV_SLICING = DVORP_SLICING | DAM_SLICE, + DAMV_PIERCING = DVORP_PIERCING | DAM_PIERCE, + DAMV_CHOPPING = DVORP_CHOPPING | DAM_SLICE, + DAMV_SLASHING = DVORP_SLASHING | DAM_WHIP, + DAMV_STABBING = DVORP_STABBING | DAM_PIERCE, + + DAM_MASK = 0x0fff, // strips vorpal specification + DAMV_MASK = 0xf000 // strips non-vorpal specification }; // NOTE: This order is very special! Its basically the same as ZAP_*, // and there are bits of the code that still use that fact.. see zap_wand(). -enum WANDS // mitm[].subtype +enum wand_type // mitm[].subtype { WAND_FLAME, // 0 WAND_FROST, @@ -3065,7 +3767,7 @@ enum WANDS // mitm[].subtype NUM_WANDS // must remain last member {dlb} }; -enum WEAPONS +enum weapon_type { // Base weapons WPN_CLUB, // 0 @@ -3111,18 +3813,24 @@ enum WEAPONS WPN_TRIDENT, WPN_SPIKED_FLAIL, WPN_GREAT_MACE, - WPN_GREAT_FLAIL, // 40 + WPN_DIRE_FLAIL, // 40 WPN_KNIFE, WPN_BLOWGUN, WPN_FALCHION, - NUM_WEAPONS, // 44 - must remain last regular member {dlb} + WPN_BLESSED_BLADE, // 44 + WPN_LONGBOW, + WPN_LAJATANG, + WPN_LOCHABER_AXE, + + NUM_WEAPONS, // 48 - must be last regular member {dlb} + // special cases WPN_UNARMED = 500, // 500 WPN_UNKNOWN = 1000, // 1000 WPN_RANDOM }; -enum WEAPON_DESCRIPTIONS +enum weapon_description_type { DWPN_PLAIN = 0, // 0 - added to round out enum {dlb} DWPN_RUNED = 1, // 1 @@ -3132,7 +3840,7 @@ enum WEAPON_DESCRIPTIONS DWPN_DWARVEN // 5 }; -enum WEAPON_PROPERTIES +enum weapon_property_type { PWPN_DAMAGE, // 0 PWPN_HIT, @@ -3141,7 +3849,7 @@ enum WEAPON_PROPERTIES #ifdef WIZARD -enum WIZARD_OPTIONS +enum wizard_option_type { WIZ_NEVER, // protect player from accidental wiz WIZ_NO, // don't start character in wiz mode @@ -3150,7 +3858,7 @@ enum WIZARD_OPTIONS #endif -enum ZAPS // zapping(), zappy() +enum zap_type { ZAP_FLAME, // 0 ZAP_FROST, @@ -3199,7 +3907,7 @@ enum ZAPS // zapping(), zappy() ZAP_AGONY, ZAP_DISRUPTION, // 45 ZAP_DISINTEGRATION, // 46 - ZAP_ISKS_CROSS, // 47: Isk's Cross -- commented out, deprecated {dlb} + // ZAP_ISKS_CROSS, // 47: Isk's Cross -- commented out, deprecated {dlb} ZAP_BREATHE_STEAM = 48, // 48 ZAP_CONTROL_DEMON, ZAP_ORB_OF_FRAGMENTATION, // 50 @@ -3212,8 +3920,18 @@ enum ZAPS // zapping(), zappy() ZAP_SMALL_SANDBLAST, ZAP_MAGMA, ZAP_POISON_ARROW, + ZAP_BREATHE_STICKY_FLAME, + ZAP_BREATHE_LIGHTNING, + ZAP_PETRIFY, + ZAP_HELLFROST, NUM_ZAPS // must remain last member {dlb} }; +enum zombie_size_type +{ + Z_NOZOMBIE, + Z_SMALL, + Z_BIG +}; #endif // ENUM_H diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index 0a3a423b7c..51288baa43 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -16,7 +16,6 @@ #ifndef EXTERNS_H #define EXTERNS_H -#include #include #include #include @@ -45,26 +44,14 @@ extern unsigned char show_green; // defined in view.cc {dlb} // defined in mon-util.cc -- w/o this screen redraws *really* slow {dlb} extern FixedVector mcolour; +const int kNameLen = 30; #ifdef SHORT_FILE_NAMES - const int kNameLen = 30; const int kFileNameLen = 6; - const int kFileNameSize = 5 + kFileNameLen; - #else - #ifdef SAVE_DIR_PATH - // file name length has to be able to cover full paths -- bwross - const int kNameLen = 30; - const int kFileNameLen = 250; - const int kFileNameSize = 5 + kFileNameLen; - #else - const int kNameLen = 30; - const int kFileNameLen = 28; - const int kFileNameSize = 5 + kFileNameLen; - #endif + const int kFileNameLen = 250; #endif - -// Length of Path + File Name +// Used only to bound the init file name length const int kPathLen = 256; // This value is used to mark that the current berserk is free from @@ -74,36 +61,36 @@ const int kPathLen = 256; struct monsters; struct ait_hp_loss; -struct activity_interrupt_t +struct activity_interrupt_data { - AI_PAYLOAD apt; + activity_interrupt_payload_type apt; const void *data; - activity_interrupt_t() + activity_interrupt_data() : apt(AIP_NONE), data(NULL) { } - activity_interrupt_t(const int *i) + activity_interrupt_data(const int *i) : apt(AIP_INT), data(i) { } - activity_interrupt_t(const char *s) + activity_interrupt_data(const char *s) : apt(AIP_STRING), data(s) { } - activity_interrupt_t(const std::string &s) + activity_interrupt_data(const std::string &s) : apt(AIP_STRING), data(s.c_str()) { } - activity_interrupt_t(const monsters *m) + activity_interrupt_data(const monsters *m) : apt(AIP_MONSTER), data(m) { } - activity_interrupt_t(const ait_hp_loss *ahl) + activity_interrupt_data(const ait_hp_loss *ahl) : apt(AIP_HP_LOSS), data(ahl) { } - activity_interrupt_t(const activity_interrupt_t &a) + activity_interrupt_data(const activity_interrupt_data &a) : apt(a.apt), data(a.data) { } @@ -147,6 +134,7 @@ struct dist bool isTarget; // target (true), or direction (false)? bool isMe; // selected self (convenience: tx == you.x_pos, // ty == you.y_pos) + bool isEndpoint; // Does the player want the attack to stop at (tx,ty)? bool isCancel; // user cancelled (usually key) int tx,ty; // target x,y or logical extension of beam to map edge int dx,dy; // delta x and y if direction - always -1,0,1 @@ -171,26 +159,59 @@ struct bolt char thrower; // what kind of thing threw this? char ex_size; // explosion radius (0==none) int beam_source; // NON_MONSTER or monster index # - char beam_name[40]; - bool isBeam; // beams? (can hits multiple targets?) - const char *aux_source; // source of KILL_MISC beams + std::string name; + bool is_beam; // beams? (can hits multiple targets?) + bool is_explosion; + bool is_big_cloud; // expands into big_cloud at endpoint + bool is_enchant; // no block/dodge, but mag resist + bool is_energy; // mostly energy/non-physical attack + bool is_launched; // was fired from launcher? + bool is_thrown; // was thrown from hand? + bool target_first; // targeting by direction + bool aimed_at_spot; // aimed at (x,y), should not cross + std::string aux_source; // source of KILL_MISC beams // OUTPUT parameters (tracing, ID) - bool obviousEffect; // did an 'obvious' effect happen? + bool obvious_effect; // did an 'obvious' effect happen? int fr_count, foe_count; // # of times a friend/foe is "hit" int fr_power, foe_power; // total levels/hit dice affected // INTERNAL use - should not usually be set outside of beam.cc - bool isTracer; // is this a tracer? - bool aimedAtFeet; // this was aimed at self! - bool msgGenerated; // an appropriate msg was already mpr'd - bool isExplosion; // explosion phase (as opposed to beam phase) - bool smartMonster; // tracer firer can guess at other mons. resists? - bool canSeeInvis; // tracer firer can see invisible? - bool isFriendly; // tracer firer is enslaved or pet - int foeRatio; // 100* foe ratio (see mons_should_fire()) + bool is_tracer; // is this a tracer? + bool aimed_at_feet; // this was aimed at self! + bool msg_generated; // an appropriate msg was already mpr'd + bool in_explosion_phase; // explosion phase (as opposed to beam phase) + bool smart_monster; // tracer firer can guess at other mons. resists? + bool can_see_invis; // tracer firer can see invisible? + bool is_friendly; // tracer firer is enslaved or pet + int foe_ratio; // 100* foe ratio (see mons_should_fire()) + +public: + // A constructor to try and fix some of the bugs that occur because + // this struct never seems to be properly initialized. Definition + // is over in beam.cc. + bolt(); + + void set_target(const dist &); }; +struct ray_def +{ + double accx; + double accy; + double slope; + // Quadrant 1: down-right + // Quadrant 2: down-left + // Quadrant 3: up-left + // Quadrant 4: up-right + int quadrant; + + int x() const { return (int)(accx); } + int y() const { return (int)(accy); } + int advance(); // returns the direction taken (0,1,2) + void advance_and_bounce(); + void regress(); +}; struct run_check_dir { @@ -217,7 +238,7 @@ struct item_def short plus2; // +to dam, sub-sub type for boots and helms long special; // special stuff unsigned char colour; // item colour - unsigned long flags; // item statuc flags + unsigned long flags; // item status flags short quantity; // number of items short x; // x-location; for inventory items = -1 @@ -227,12 +248,20 @@ struct item_def unsigned short orig_place; short orig_monnum; + + std::string inscription; - item_def() : base_type(0), sub_type(0), plus(0), plus2(0), + item_def() : base_type(OBJ_UNASSIGNED), sub_type(0), plus(0), plus2(0), special(0L), colour(0), flags(0L), quantity(0), - x(0), y(0), link(0), slot(0), orig_place(0), + x(0), y(0), link(NON_ITEM), slot(0), orig_place(0), orig_monnum(0) { + inscription.clear(); + } + + void clear() + { + *this = item_def(); } }; @@ -256,25 +285,67 @@ private: size_t maxsize; }; +class runrest +{ +public: + int runmode; + int mp; + int hp; + int x, y; + + FixedVector run_check; // array of grids to check + +public: + runrest(); + void initialise(int rdir, int mode); + + operator int () const; + const runrest &operator = (int newrunmode); + + // Returns true if we're currently resting. + bool is_rest() const; + + // Clears run state. + void clear(); + + // Stops running. + void stop(); + + // Take one off the rest counter. + void rest(); + + // Decrements the run counter. Identical to rest. + void rundown(); + + // Checks if shift-run should be aborted and aborts the run if necessary. + // Returns true if you were running and are now no longer running. + bool check_stop_running(); + + // Check if we've reached the HP/MP stop-rest condition + void check_hp(); + void check_mp(); + +private: + void set_run_check(int index, int compass_dir); + bool run_grids_changed() const; +}; + +typedef std::vector delay_queue_type; + struct player { - ACTIVITY activity; // The current multiturn activity, usually set to ACT_NONE - char turn_is_over; // flag signaling that player has performed a timed action + bool turn_is_over; // flag signaling that player has performed a timed action unsigned char prev_targ; char your_name[kNameLen]; unsigned char species; - char run_x; - char run_y; - // Coordinates of last travel target; note that this is never used by // travel itself, only by the level-map to remember the last travel target. short travel_x, travel_y; - FixedVector< run_check_dir, 3 > run_check; // array of grids to check - signed char running; // Nonzero if running/traveling. + runrest running; // Nonzero if running/traveling. char special_wield; char deaths_door; @@ -316,6 +387,13 @@ struct player bool wield_change; // redraw weapon unsigned long redraw_status_flags; + + // PC's symbol (usually @) and colour. + int symbol; + int colour; + + FixedVector< char, NUM_STATUE_TYPES > visible_statue; + char redraw_hit_points; char redraw_magic_points; char redraw_strength; @@ -327,6 +405,8 @@ struct player char redraw_gold; char redraw_evasion; + unsigned char flash_colour; + unsigned char hit_points_regeneration; unsigned char magic_points_regeneration; @@ -376,7 +456,7 @@ struct player char is_undead; // see UNDEAD_STATES in enum.h - std::queue< delay_queue_item > delay_queue; // pending actions + delay_queue_type delay_queue; // pending actions FixedVector skills; FixedVector practise_skill; @@ -403,6 +483,7 @@ struct player unsigned char gift_timeout; FixedVector penance; FixedVector worshipped; + FixedVector num_gifts; FixedVector mutation; @@ -440,10 +521,29 @@ struct player // table contains soft links. FixedVector spell_letter_table; // ref to spell by slot FixedVector ability_letter_table; // ref to ability by enum + +public: + player(); + void init(); + + bool is_valid() const; + std::string short_desc() const; + + // For sorting + bool operator < (const player &p) const; }; extern struct player you; +class monster_spells : public FixedVector +{ +public: + monster_spells() + : FixedVector(MS_NO_SPELL) + { } + void clear() { init(MS_NO_SPELL); } +}; + struct monsters { int type; @@ -459,12 +559,16 @@ struct monsters unsigned char target_x; unsigned char target_y; FixedVector inv; + monster_spells spells; unsigned char attitude; // from MONS_ATTITUDE unsigned int behaviour; unsigned int foe; FixedVector enchantment; unsigned char flags; // bitfield of boolean flags + unsigned int number; // #heads (hydra), etc. + int colour; + int foe_memory; // how long to 'remember' foe x,y // once they go out of sight }; @@ -536,10 +640,6 @@ struct ghost_struct extern struct ghost_struct ghost; - -extern void (*viewwindow) (char, bool); - - struct system_environment { char *crawl_name; @@ -548,6 +648,8 @@ struct system_environment char *crawl_dir; char *home; // only used by MULTIUSER systems bool board_with_nail; // Easter Egg silliness + + std::string crawl_executable_path; // path to crawl from argv[0] }; extern system_environment SysEnv; @@ -585,62 +687,26 @@ struct colour_mapping int colour; }; -class formatted_string +class InitLineInput; +struct game_options { public: - formatted_string() : ops() { } - formatted_string(const std::string &s); - - operator std::string() const; - void display(int start = 0, int end = -1) const; - std::string tostring(int start = 0, int end = -1) const; + game_options(); + void reset_startup_options(); + void reset_options(); - void cprintf(const char *s, ...); - void cprintf(const std::string &s); - void gotoxy(int x, int y); - void textcolor(int color); + void read_option_line(const std::string &s, bool runscripts = false); + void read_options(InitLineInput &, bool runscripts); -private: - enum fs_op_type - { - FSOP_COLOUR, - FSOP_CURSOR, - FSOP_TEXT - }; +public: + std::string save_dir; // Directory where saves and bones go. - struct fs_op - { - fs_op_type type; - int x, y; - std::string text; - - fs_op(int color) - : type(FSOP_COLOUR), x(color), y(-1), text() - { - } - - fs_op(int cx, int cy) - : type(FSOP_CURSOR), x(cx), y(cy), text() - { - } - - fs_op(const std::string &s) - : type(FSOP_TEXT), x(-1), y(-1), text(s) - { - } - - operator fs_op_type () const - { - return type; - } - void display() const; - }; + std::string player_name; - std::vector ops; -}; + bool autopickup_on; + bool autoprayer_on; + bool fizzlecheck_on; -struct game_options -{ long autopickups; // items to autopickup bool verbose_dump; // make character dumps contain more detail bool detailed_stat_dump; // add detailed stat and resist dump @@ -649,14 +715,23 @@ struct game_options bool show_uncursed; // label known uncursed items as "uncursed" bool always_greet; // display greeting message when reloading bool easy_open; // open doors with movement - bool easy_armour; // allow auto-removing of armour + bool easy_unequip; // allow auto-removing of armour / jewelry bool easy_butcher; // open doors with movement + bool increasing_skill_progress; // skills go from 0-10 or 10-0 + bool confirm_self_target; // require confirmation before selftarget + bool safe_autopickup; // don't autopickup when monsters visible + bool note_skill_max; // take note when skills reach new max + bool note_all_spells; // take note when learning any spell + bool use_notes; // take (and dump) notes + int note_hp_percent; // percentage hp for notetaking + int ood_interesting; // how many levels OOD is noteworthy? int easy_confirm; // make yesno() confirming easier int easy_quit_item_prompts; // make item prompts quitable on space int colour[16]; // macro fg colours to other colours int background; // select default background colour int channels[NUM_MESSAGE_CHANNELS]; // msg channel colouring int weapon; // auto-choose weapon for character + int book; // auto-choose book for character int chaos_knight; // choice of god for Chaos Knights (Xom/Makleb) int death_knight; // choice of god/necromancy for Death Knights int priest; // choice of god for priests (Zin/Yred) @@ -680,6 +755,9 @@ struct game_options bool flush_input[NUM_FLUSH_REASONS]; // when to flush input buff bool lowercase_invocations; // prefer lowercase invocations + char_set_type char_set; + FixedVector char_table; + int num_colours; // used for setting up curses colour table (8 or 16) #ifdef WIZARD @@ -690,16 +768,31 @@ struct game_options int sc_entries; // # of score entries int sc_format; // Format for score entries + + std::string map_file_name; // name of mapping file to use std::vector banned_objects; // Objects we'll never pick up + std::vector note_monsters; // Interesting monsters + std::vector note_messages; // Interesting messages + std::vector > autoinscriptions; + std::vector note_items; // Objects to note + std::vector note_skill_levels; // Skill levels to note + bool pickup_thrown; // Pickup thrown missiles bool pickup_dropped; // Pickup dropped objects int travel_delay; // How long to pause between travel moves - std::vector stop_travel; // Messages that stop travel + // Messages that stop travel + std::vector travel_stop_message; int stash_tracking; // How stashes are tracked bool travel_colour; // Colour levelmap using travel information? + int tc_reachable; // Colour for squares that are reachable + int tc_excluded; // Colour for excluded squares. + int tc_exclude_circle; // Colour for squares in the exclusion radius + int tc_dangerous; // Colour for trapped squares, deep water, lava. + int tc_disconnected;// Areas that are completely disconnected. + int travel_stair_cost; int travel_exclude_radius2; // Square of the travel exclude radius @@ -709,8 +802,6 @@ struct game_options unsigned detected_monster_colour; // Colour of detected monsters unsigned detected_item_colour; // Colour of detected items - unsigned remembered_monster_colour; // Colour for monsters remembered - // on the map. unsigned heap_brand; // Highlight heaps of items in the playing area unsigned stab_brand; // Highlight monsters that are stabbable @@ -722,12 +813,18 @@ struct game_options std::vector sound_mappings; std::vector menu_colour_mappings; + bool sort_menus; + int dump_kill_places; // How to dump place information for kills. int dump_message_count; // How many old messages to dump int dump_item_origins; // Show where items came from? int dump_item_origin_price; + // Order of sections in the character dump. + std::vector dump_order; + + bool safe_zero_exp; // If true, you feel safe around 0xp monsters bool target_zero_exp; // If true, targeting targets zero-exp // monsters. bool target_wrap; // Wrap around from last to first target @@ -745,7 +842,7 @@ struct game_options std::vector drop_filter; - FixedVector< unsigned, ACT_ACTIVITY_COUNT > activity_interrupts; + FixedArray activity_interrupts; // Previous startup options bool remember_name; // Remember and reprompt with last name @@ -757,6 +854,15 @@ struct game_options // If the player prefers to merge kill records, this option can do that. int kill_map[KC_NCATEGORIES]; + +#ifdef WIZARD + // Parameters for fight simulations. + long fsim_rounds; + int fsim_str, fsim_int, fsim_dex; + int fsim_xl; + std::string fsim_mons; + std::vector fsim_kit; +#endif // WIZARD typedef std::map opt_map; opt_map named_options; // All options not caught above are @@ -771,7 +877,30 @@ struct game_options char prev_cls; int prev_ck, prev_dk, prev_pr; int prev_weapon; + int prev_book; bool prev_randpick; + +public: + // Convenience accessors for the second-class options in named_options. + int o_int(const char *name, int def = 0) const; + long o_long(const char *name, long def = 0L) const; + bool o_bool(const char *name, bool def = false) const; + std::string o_str(const char *name, const char *def = NULL) const; + int o_colour(const char *name, int def = LIGHTGREY) const; + +private: + void set_default_activity_interrupts(); + void clear_activity_interrupts(FixedVector &eints); + void set_activity_interrupt( + FixedVector &eints, + const std::string &interrupt); + void set_activity_interrupt(const std::string &activity_name, + const std::string &interrupt_names, + bool append_interrupts); + void add_dump_fields(const std::string &text); + void do_kill_map(const std::string &from, const std::string &to); + + static const std::string interrupt_prefix; }; extern game_options Options; @@ -784,6 +913,7 @@ struct tagHeader struct scorefile_entry { +public: char version; char release; long points; @@ -820,6 +950,46 @@ struct scorefile_entry long num_turns; // number of turns taken int num_diff_runes; // number of rune types in inventory int num_runes; // total number of runes in inventory + +public: + scorefile_entry(); + scorefile_entry(int damage, int death_source, int death_type, + const char *aux, bool death_cause_only = false); + + void init_death_cause(int damage, int death_source, int death_type, + const char *aux); + void init(); + void reset(); + + enum death_desc_verbosity { + DDV_TERSE, + DDV_ONELINE, + DDV_NORMAL, + DDV_VERBOSE + }; + + std::string hiscore_line(death_desc_verbosity verbosity) const; + + std::string character_description(death_desc_verbosity) const; + // Full description of death: Killed by an xyz wielding foo + std::string death_description(death_desc_verbosity) const; + + std::string death_place(death_desc_verbosity) const; + + std::string game_time(death_desc_verbosity) const; + +private: + std::string single_cdesc() const; + std::string strip_article_a(const std::string &s) const; + std::string terse_missile_cause() const; + std::string terse_beam_cause() const; + std::string terse_wild_magic() const; + std::string terse_trap() const; + const char *damage_verb() const; + const char *death_source_desc() const; + std::string damage_string(bool terse = false) const; }; +extern const struct coord_def Compass[8]; + #endif // EXTERNS_H diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index 6a93de441e..48444db55e 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -3,6 +3,8 @@ * Summary: Functions used during combat. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <11> 07-jul-2000 JDJ Fixed some of the code in you_attack so it doesn't @@ -48,6 +50,7 @@ #include "it_use2.h" #include "items.h" #include "itemname.h" +#include "itemprop.h" #include "macro.h" #include "misc.h" #include "monplace.h" @@ -67,7 +70,6 @@ #include "spl-cast.h" #include "stuff.h" #include "view.h" -#include "wpn-misc.h" #define HIT_WEAK 7 #define HIT_MED 18 @@ -126,70 +128,207 @@ int effective_stat_bonus( int wepType ) #endif } -void you_attack(int monster_attacked, bool unarmed_attacks) -{ - struct monsters *defender = &menv[monster_attacked]; +// returns random2(x) is random_factor is true, otherwise +// the mean. +static int maybe_random2( int x, bool random_factor ) { + if ( random_factor ) + return random2(x); + else + return x / 2; +} + +// Returns the to-hit for your extra unarmed.attacks. +// DOES NOT do the final roll (i.e., random2(your_to_hit)). +static int calc_your_to_hit_unarmed() { int your_to_hit; - int damage_done = 0; - bool hit = false; - unsigned char stab_bonus = 0; // this is never negative {dlb} - int temp_rand; // for probability determination {dlb} + + your_to_hit = 13 + you.dex / 2 + you.skills[SK_UNARMED_COMBAT] / 2 + + you.skills[SK_FIGHTING] / 5; + + if (wearing_amulet(AMU_INACCURACY)) + your_to_hit -= 5; + + if (you.hunger_state == HS_STARVING) + your_to_hit -= 3; + + your_to_hit += slaying_bonus(PWPN_HIT); + + return your_to_hit; +} + +// Calculates your to-hit roll. If random_factor is true, be +// stochastic; if false, determinstic (e.g. for chardumps.) +int calc_your_to_hit( int heavy_armour, + bool hand_and_a_half_bonus, + bool water_attack, + bool random_factor ) { const int weapon = you.equip[EQ_WEAPON]; const bool ur_armed = (weapon != -1); // compacts code a bit {dlb} - const bool bearing_shield = (you.equip[EQ_SHIELD] != -1); - const int melee_brand = player_damage_brand(); + int wpn_skill = SK_UNARMED_COMBAT; - bool water_attack = false; + if (weapon != -1) { + wpn_skill = weapon_skill( you.inv[weapon].base_type, + you.inv[weapon].sub_type ); + } - char damage_noise[80], damage_noise2[80]; + int your_to_hit; - int heavy_armour = 0; - char str_pass[ ITEMNAME_SIZE ]; + // preliminary to_hit modifications: + your_to_hit = 15 + (calc_stat_to_hit_base() / 2); -#if DEBUG_DIAGNOSTICS - char st_prn[ 20 ]; -#endif + if (water_attack) + your_to_hit += 5; - FixedVector< char, RA_PROPERTIES > art_proprt; + if (wearing_amulet(AMU_INACCURACY)) + your_to_hit -= 5; - if (ur_armed && you.inv[weapon].base_type == OBJ_WEAPONS - && is_random_artefact( you.inv[weapon] )) - { - randart_wpn_properties( you.inv[weapon], art_proprt ); + // if you can't see yourself, you're a little less acurate. + if (you.invis && !player_see_invis()) + your_to_hit -= 5; + + // fighting contribution + your_to_hit += maybe_random2(1 + you.skills[SK_FIGHTING], random_factor); + + // weapon skill contribution + if (ur_armed) { + if (wpn_skill != SK_FIGHTING) { + your_to_hit += maybe_random2(you.skills[wpn_skill] + 1, + random_factor); + } } - else + else { // ...you must be unarmed + your_to_hit += + (you.species == SP_TROLL || you.species == SP_GHOUL) ? 4 : 2; + + your_to_hit += maybe_random2(1 + you.skills[SK_UNARMED_COMBAT], + random_factor); + } + + // weapon bonus contribution + if (ur_armed) { - // Probably over protective, but in this code we're going - // to play it safe. -- bwr - for (int i = 0; i < RA_PROPERTIES; i++) - art_proprt[i] = 0; + if (you.inv[ weapon ].base_type == OBJ_WEAPONS) + { + your_to_hit += you.inv[ weapon ].plus; + your_to_hit += property( you.inv[ weapon ], PWPN_HIT ); + + if (get_equip_race(you.inv[ weapon ]) == ISFLAG_ELVEN + && player_genus(GENPC_ELVEN)) + { + your_to_hit += (random_factor && coinflip() ? 2 : 1); + } + } + else if (item_is_staff( you.inv[ weapon ] )) + { + // magical staff + your_to_hit += property( you.inv[ weapon ], PWPN_HIT ); + } + } + + // slaying bonus + your_to_hit += slaying_bonus(PWPN_HIT); + + // hunger penalty + if (you.hunger_state == HS_STARVING) + your_to_hit -= 3; + + // armour penalty + your_to_hit -= heavy_armour; + +#if DEBUG_DIAGNOSTICS + int roll_hit = your_to_hit; +#endif + + // hit roll + your_to_hit = maybe_random2(your_to_hit, random_factor); + +#if DEBUG_DIAGNOSTICS + snprintf( info, INFO_SIZE, "to hit die: %d; rolled value: %d", + roll_hit, your_to_hit ); + + mpr( info, MSGCH_DIAGNOSTICS ); +#endif + + if (hand_and_a_half_bonus) + your_to_hit += maybe_random2(3, random_factor); + + if (ur_armed && wpn_skill == SK_SHORT_BLADES && you.sure_blade) + your_to_hit += 5 + + (random_factor ? random2limit( you.sure_blade, 10 ) : + you.sure_blade / 2); + + // other stuff + if (!ur_armed) { + if ( you.confusing_touch ) + // just trying to touch is easier that trying to damage + your_to_hit += maybe_random2(you.dex, random_factor); + + switch ( you.attribute[ATTR_TRANSFORMATION] ) { + case TRAN_NONE: + break; + case TRAN_SPIDER: + your_to_hit += maybe_random2(10, random_factor); + break; + case TRAN_ICE_BEAST: + your_to_hit += maybe_random2(10, random_factor); + break; + case TRAN_BLADE_HANDS: + your_to_hit += maybe_random2(12, random_factor); + break; + case TRAN_STATUE: + your_to_hit += maybe_random2(9, random_factor); + break; + case TRAN_SERPENT_OF_HELL: + case TRAN_DRAGON: + your_to_hit += maybe_random2(10, random_factor); + break; + case TRAN_LICH: + your_to_hit += maybe_random2(10, random_factor); + break; + case TRAN_AIR: + your_to_hit = 0; + break; + default: + break; + } } + return your_to_hit; +} + +// Calculates your heavy armour penalty. If random_factor is true, +// be stochastic; if false, deterministic (e.g. for chardumps.) +int calc_heavy_armour_penalty( bool random_factor ) { + + const bool ur_armed = (you.equip[EQ_WEAPON] != -1); + int heavy_armour = 0; + // heavy armour modifiers for shield borne - if (bearing_shield) + if (you.equip[EQ_SHIELD] != -1) { switch (you.inv[you.equip[EQ_SHIELD]].sub_type) { case ARM_SHIELD: - if (you.skills[SK_SHIELDS] < random2(7)) + if (you.skills[SK_SHIELDS] < maybe_random2(7, random_factor)) heavy_armour++; break; case ARM_LARGE_SHIELD: if ((you.species >= SP_OGRE && you.species <= SP_OGRE_MAGE) || player_genus(GENPC_DRACONIAN)) { - if (you.skills[SK_SHIELDS] < random2(13)) + if (you.skills[SK_SHIELDS] < maybe_random2(13, random_factor)) heavy_armour++; // was potentially "+= 3" {dlb} } else { for (int i = 0; i < 3; i++) { - if (you.skills[SK_SHIELDS] < random2(13)) - heavy_armour += random2(3); + if (you.skills[SK_SHIELDS] < maybe_random2(13, + random_factor)) + heavy_armour += maybe_random2(3, random_factor); } } break; @@ -204,14 +343,69 @@ void you_attack(int monster_attacked, bool unarmed_attacks) const int ev_pen = property( you.inv[you.equip[EQ_BODY_ARMOUR]], PARM_EVASION ); - if (ev_pen < 0 && random2(you.skills[SK_ARMOUR]) < abs( ev_pen )) - heavy_armour += random2( abs(ev_pen) ); + if (ev_pen < 0 && + maybe_random2(you.skills[SK_ARMOUR], random_factor) < abs(ev_pen)) + heavy_armour += maybe_random2( abs(ev_pen), random_factor ); } // ??? what is the reasoning behind this ??? {dlb} // My guess is that its supposed to encourage monk-style play -- bwr - if (!ur_armed) - heavy_armour *= (coinflip() ? 3 : 2); + if (!ur_armed) { + if ( random_factor ) { + heavy_armour *= (coinflip() ? 3 : 2); + } + // (2+3)/2 + else { + heavy_armour *= 5; + heavy_armour /= 2; + } + } + return heavy_armour; +} + +// Returns true if you hit the monster. +bool you_attack(int monster_attacked, bool unarmed_attacks) +{ + struct monsters *defender = &menv[monster_attacked]; + + int your_to_hit; + int damage_done = 0; + bool hit = false; + unsigned char stab_bonus = 0; // this is never negative {dlb} + int temp_rand; // for probability determination {dlb} + + const int weapon = you.equip[EQ_WEAPON]; + const bool ur_armed = (weapon != -1); // compacts code a bit {dlb} + const bool bearing_shield = (you.equip[EQ_SHIELD] != -1); + + const int melee_brand = player_damage_brand(); + + bool water_attack = false; + + char damage_noise[80], damage_noise2[80]; + + char str_pass[ ITEMNAME_SIZE ]; + +#if DEBUG_DIAGNOSTICS + char st_prn[ 20 ]; +#endif + + FixedVector< char, RA_PROPERTIES > art_proprt; + + if (ur_armed && you.inv[weapon].base_type == OBJ_WEAPONS + && is_random_artefact( you.inv[weapon] )) + { + randart_wpn_properties( you.inv[weapon], art_proprt ); + } + else + { + // Probably over protective, but in this code we're going + // to play it safe. -- bwr + for (int i = 0; i < RA_PROPERTIES; i++) + art_proprt[i] = 0; + } + + const int heavy_armour = calc_heavy_armour_penalty(true); // Calculate the following two flags in advance // to know what player does this combat turn: @@ -229,24 +423,23 @@ void you_attack(int monster_attacked, bool unarmed_attacks) bool use_hand_and_a_half_bonus = false; int wpn_skill = SK_UNARMED_COMBAT; - int hands_reqd = HANDS_ONE_HANDED; + int hands = HANDS_ONE; if (weapon != -1) { wpn_skill = weapon_skill( you.inv[weapon].base_type, you.inv[weapon].sub_type ); - hands_reqd = hands_reqd_for_weapon( you.inv[weapon].base_type, - you.inv[weapon].sub_type ); + hands = hands_reqd( you.inv[weapon], player_size() ); } if (unarmed_attacks && !can_do_unarmed_combat && !bearing_shield && ur_armed - && item_uncursed( you.inv[ weapon ] ) - && hands_reqd == HANDS_ONE_OR_TWO_HANDED) + && !item_cursed( you.inv[ weapon ] ) + && hands == HANDS_HALF) { - // currently: +1 dam, +1 hit, -1 spd (loosly) + // currently: +1 dam, +1 hit, -1 spd (loosely) use_hand_and_a_half_bonus = true; } @@ -258,10 +451,11 @@ void you_attack(int monster_attacked, bool unarmed_attacks) * * ************************************************************************** */ - bool helpless = mons_flag(defender->type, M_NO_EXP_GAIN); + bool helpless = mons_class_flag(defender->type, M_NO_EXP_GAIN) + || mons_is_statue(defender->type); if (mons_friendly(defender)) - naughty(NAUGHTY_ATTACK_FRIEND, 5); + did_god_conduct(DID_ATTACK_FRIEND, 5); if (you.pet_target == MHITNOT) you.pet_target = monster_attacked; @@ -272,66 +466,36 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (random2(you.dex) < 4 || one_chance_in(5)) { mpr("Unstable footing causes you to fumble your attack."); - return; + return (false); } } // wet merfolk if (player_is_swimming() - // monster not a water creature - && monster_habitat( defender->type ) != DNGN_DEEP_WATER - && !mons_flag( defender->type, M_AMPHIBIOUS ) - // monster in water - && (grd[defender->x][defender->y] == DNGN_SHALLOW_WATER - || grd[defender->x][defender->y] == DNGN_DEEP_WATER) - // monster not flying - && !mons_flies( defender )) + // monster not a water creature, but is in water + && monster_floundering(defender)) { water_attack = true; } - // preliminary to_hit modifications: - your_to_hit = 15 + (calc_stat_to_hit_base() / 2); - - if (water_attack) - your_to_hit += 5; - - if (wearing_amulet(AMU_INACCURACY)) - your_to_hit -= 5; - - // if you can't see yourself, you're a little less acurate. - if (you.invis && !player_see_invis()) - your_to_hit -= 5; - - your_to_hit += random2(1 + you.skills[SK_FIGHTING]); + // new unified to-hit calculation + your_to_hit = calc_your_to_hit( heavy_armour, use_hand_and_a_half_bonus, + water_attack, true ); - if (ur_armed) - { - if (wpn_skill) - your_to_hit += random2(you.skills[wpn_skill] + 1); - } - else // ...you must be unarmed - { - your_to_hit += (you.species == SP_TROLL - || you.species == SP_GHOUL) ? 4 : 2; - - your_to_hit += random2(1 + you.skills[SK_UNARMED_COMBAT]); - } + //jmf: check for backlight enchantment + if (mons_has_ench(defender, ENCH_BACKLIGHT_I, ENCH_BACKLIGHT_IV)) + your_to_hit += 2 + random2(8); // energy expenditure in terms of food: make_hungry(3, true); - if (ur_armed - && you.inv[ weapon ].base_type == OBJ_WEAPONS - && is_random_artefact( you.inv[ weapon ] )) + if (ur_armed && + you.inv[ weapon ].base_type == OBJ_WEAPONS && + is_random_artefact( you.inv[ weapon ] ) && + art_proprt[RAP_ANGRY] >= 1 && + random2(1 + art_proprt[RAP_ANGRY])) { - if (art_proprt[RAP_ANGRY] >= 1) - { - if (random2(1 + art_proprt[RAP_ANGRY])) - { - go_berserk(false); - } - } + go_berserk(false); } switch (you.special_wield) @@ -352,63 +516,11 @@ void you_attack(int monster_attacked, bool unarmed_attacks) break; } - if (you.mutation[MUT_BERSERK]) - { - if (random2(100) < (you.mutation[MUT_BERSERK] * 10) - 5) - go_berserk(false); - } - - if (ur_armed) - { - if (you.inv[ weapon ].base_type == OBJ_WEAPONS) - { - // there was some stupid conditional here that applied this - // "if (plus >= 0)" and "else" that .. which is all - // cases (d'oh!) {dlb} - your_to_hit += you.inv[ weapon ].plus; - your_to_hit += property( you.inv[ weapon ], PWPN_HIT ); - - if (cmp_equip_race( you.inv[ weapon ], ISFLAG_ELVEN ) - && player_genus(GENPC_ELVEN)) - { - your_to_hit += (coinflip() ? 2 : 1); - } - } - else if (item_is_staff( you.inv[ weapon ] )) - { - /* magical staff */ - your_to_hit += property( you.inv[ weapon ], PWPN_HIT ); - } - } - - your_to_hit += slaying_bonus(PWPN_HIT); // see: player.cc - - if (you.hunger_state == HS_STARVING) - your_to_hit -= 3; - - your_to_hit -= heavy_armour; - -#if DEBUG_DIAGNOSTICS - int roll_hit = your_to_hit; -#endif - - // why does this come here and not later? {dlb} - // apparently to give the following pluses more significance -- bwr - your_to_hit = random2(your_to_hit); - -#if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, "to hit die: %d; rolled value: %d", - roll_hit, your_to_hit ); - - mpr( info, MSGCH_DIAGNOSTICS ); -#endif - - if (use_hand_and_a_half_bonus) - your_to_hit += random2(3); - - if (ur_armed && wpn_skill == SK_SHORT_BLADES && you.sure_blade) - your_to_hit += 5 + random2limit( you.sure_blade, 10 ); + if (you.mutation[MUT_BERSERK] && + (random2(100) < (you.mutation[MUT_BERSERK] * 10) - 5)) + go_berserk(false); + // calculate damage int damage = 1; if (!ur_armed) // empty-handed @@ -417,9 +529,6 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (you.confusing_touch) { - // just trying to touch is easier that trying to damage - // special_brand = SPWPN_CONFUSE; - your_to_hit += random2(you.dex); // no base hand damage while using this spell damage = 0; } @@ -434,34 +543,27 @@ void you_attack(int monster_attacked, bool unarmed_attacks) case TRAN_SPIDER: damage = 5; // special_brand = SPWPN_VENOM; - your_to_hit += random2(10); break; case TRAN_ICE_BEAST: damage = 12; // special_brand = SPWPN_FREEZING; - your_to_hit += random2(10); break; case TRAN_BLADE_HANDS: damage = 12 + (you.strength / 4) + (you.dex / 4); - your_to_hit += random2(12); break; case TRAN_STATUE: damage = 12 + you.strength; - your_to_hit += random2(9); break; case TRAN_SERPENT_OF_HELL: case TRAN_DRAGON: damage = 20 + you.strength; - your_to_hit += random2(10); break; case TRAN_LICH: damage = 5; // special_brand = SPWPN_DRAINING; - your_to_hit += random2(10); break; case TRAN_AIR: - damage = 0; - your_to_hit = 0; + damage = 0; break; } } @@ -491,10 +593,6 @@ void you_attack(int monster_attacked, bool unarmed_attacks) const int base_damage = damage; #endif - //jmf: check for backlight enchantment - if (mons_has_ench(defender, ENCH_BACKLIGHT_I, ENCH_BACKLIGHT_IV)) - your_to_hit += 2 + random2(8); - int weapon_speed2 = 10; int min_speed = 3; @@ -513,7 +611,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) min_speed = 5; // Using both hands can get a weapon up to speed 7 - if ((hands_reqd == HANDS_TWO_HANDED || use_hand_and_a_half_bonus) + if ((hands == HANDS_TWO || use_hand_and_a_half_bonus) && min_speed > 7) { min_speed = 7; @@ -545,10 +643,11 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (you.burden_state == BS_UNENCUMBERED && one_chance_in(heavy_armour + 1)) { - weapon_speed2 = 10 - you.skills[SK_UNARMED_COMBAT] / 3; - - if (weapon_speed2 < 4) - weapon_speed2 = 4; + weapon_speed2 = 10 - you.skills[SK_UNARMED_COMBAT] / 5; + + /* this shouldn't happen anyway...sanity */ + if (weapon_speed2 < 5) + weapon_speed2 = 5; } } @@ -556,11 +655,14 @@ void you_attack(int monster_attacked, bool unarmed_attacks) { switch (you.inv[you.equip[EQ_SHIELD]].sub_type) { - case ARM_SHIELD: - weapon_speed2++; - break; case ARM_LARGE_SHIELD: - weapon_speed2 += 2; + if (you.skills[SK_SHIELDS] <= 10 + random2(17)) + weapon_speed2++; + // [dshaligram] Fall-through + + case ARM_SHIELD: + if (you.skills[SK_SHIELDS] <= 3 + random2(17)) + weapon_speed2++; break; } } @@ -599,7 +701,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) // confused (but not perma-confused) if (mons_has_ench(defender, ENCH_CONFUSION) - && !mons_flag(defender->type, M_CONFUSED)) + && !mons_class_flag(defender->type, M_CONFUSED)) { stabAttempt = true; stab_bonus = 2; @@ -642,10 +744,9 @@ void you_attack(int monster_attacked, bool unarmed_attacks) mpr( info, MSGCH_DIAGNOSTICS ); #endif - if ((your_to_hit >= defender->evasion || one_chance_in(30)) - || ((defender->speed_increment <= 60 - || defender->behaviour == BEH_SLEEP) - && !one_chance_in(10 + you.skills[SK_STABBING]))) + if ((your_to_hit >= defender->evasion || one_chance_in(30)) || + ((mons_is_paralysed(defender) || defender->behaviour == BEH_SLEEP) + && !one_chance_in(10 + you.skills[SK_STABBING]))) { hit = true; int dammod = 78; @@ -742,13 +843,13 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (use_hand_and_a_half_bonus) damage_done += random2(3); - if (cmp_equip_race( you.inv[ weapon ], ISFLAG_DWARVEN ) + if (get_equip_race(you.inv[ weapon ]) == ISFLAG_DWARVEN && player_genus(GENPC_DWARVEN)) { damage_done += random2(3); } - if (cmp_equip_race( you.inv[ weapon ], ISFLAG_ORCISH ) + if (get_equip_race(you.inv[ weapon ]) == ISFLAG_ORCISH && you.species == SP_HILL_ORC && coinflip()) { damage_done++; @@ -758,8 +859,8 @@ void you_attack(int monster_attacked, bool unarmed_attacks) bonus_damage = damage_done; #endif - if (!launches_things( you.inv[ weapon ].sub_type ) - && item_not_ident( you.inv[ weapon ], ISFLAG_KNOW_PLUSES ) + if (!is_range_weapon( you.inv[weapon] ) + && !item_ident( you.inv[ weapon ], ISFLAG_KNOW_PLUSES ) && random2(100) < you.skills[ wpn_skill ]) { set_ident_flags( you.inv[ weapon ], ISFLAG_KNOW_PLUSES ); @@ -781,10 +882,10 @@ void you_attack(int monster_attacked, bool unarmed_attacks) exercise(SK_STABBING, 1 + random2avg(5, 4)); - if (mons_holiness(defender->type) == MH_NATURAL - || mons_holiness(defender->type) == MH_HOLY) + if (mons_holiness(defender) == MH_NATURAL + || mons_holiness(defender) == MH_HOLY) { - naughty(NAUGHTY_STABBING, 4); + did_god_conduct(DID_STABBING, 4); } } else @@ -917,7 +1018,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) #endif if (ur_armed && melee_brand == SPWPN_VAMPIRICISM) { - if (mons_holiness(defender->type) == MH_NATURAL + if (mons_holiness(defender) == MH_NATURAL && damage_done > 0 && you.hp < you.hp_max && !one_chance_in(5)) { @@ -929,7 +1030,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (you.hunger_state != HS_ENGORGED) lessen_hunger(30 + random2avg(59, 2), true); - naughty(NAUGHTY_NECROMANCY, 2); + did_god_conduct(DID_NECROMANCY, 2); } } @@ -945,17 +1046,19 @@ void you_attack(int monster_attacked, bool unarmed_attacks) snprintf( info, INFO_SIZE, "You %s the ball lightning.", damage_noise); mpr(info); } - return; + return (true); } if (damage_done < 1 && player_monster_visible( defender )) { hit = true; - snprintf( info, INFO_SIZE, "You %s ", damage_noise); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); - strcat(info, ", but do no damage."); - mpr(info); + if ( !ur_armed || melee_brand != SPWPN_VORPAL ) + { + snprintf(info, INFO_SIZE, "You %s %s, but do no damage.", + damage_noise, ptr_monam(defender, DESC_NOCAP_THE)); + mpr(info); + } } } else @@ -1002,44 +1105,44 @@ void you_attack(int monster_attacked, bool unarmed_attacks) mpr(info); - if (mons_holiness(defender->type) == MH_HOLY) - done_good(GOOD_KILLED_ANGEL_I, 1); + if (mons_holiness(defender) == MH_HOLY) + did_god_conduct(DID_KILL_ANGEL, 1); if (you.special_wield == SPWLD_TORMENT) { - torment(you.x_pos, you.y_pos); - naughty(NAUGHTY_UNHOLY, 5); + torment(TORMENT_SPWLD, you.x_pos, you.y_pos); + did_god_conduct(DID_UNHOLY, 5); } if (you.special_wield == SPWLD_ZONGULDROK || you.special_wield == SPWLD_CURSE) { - naughty(NAUGHTY_NECROMANCY, 3); + did_god_conduct(DID_NECROMANCY, 3); } } - if (defender->type == MONS_JELLY - || defender->type == MONS_BROWN_OOZE - || defender->type == MONS_ACID_BLOB - || defender->type == MONS_ROYAL_JELLY) - { - weapon_acid(5); - } - /* remember, damage_done is still useful! */ if (hit) { + if (defender->type == MONS_JELLY + || defender->type == MONS_BROWN_OOZE + || defender->type == MONS_ACID_BLOB + || defender->type == MONS_ROYAL_JELLY) + { + weapon_acid(5); + } + int specdam = 0; if (ur_armed && you.inv[ weapon ].base_type == OBJ_WEAPONS - && is_demonic( you.inv[ weapon ].sub_type )) + && is_demonic( you.inv[ weapon ] )) { - naughty(NAUGHTY_UNHOLY, 1); + did_god_conduct(DID_UNHOLY, 1); } - if (mons_holiness(defender->type) == MH_HOLY) - naughty(NAUGHTY_ATTACK_HOLY, defender->hit_dice); + if (mons_holiness(defender) == MH_HOLY) + did_god_conduct(DID_ATTACK_HOLY, defender->hit_dice); if (defender->type == MONS_HYDRA) { @@ -1204,7 +1307,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) ptr_monam(defender, DESC_CAP_THE) ); mpr(info); - naughty(NAUGHTY_NECROMANCY, 4); + did_god_conduct(DID_NECROMANCY, 4); } } break; @@ -1228,7 +1331,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) { dec_mp(STAFF_COST); - if (item_not_ident( you.inv[weapon], ISFLAG_KNOW_TYPE )) + if (!item_ident( you.inv[weapon], ISFLAG_KNOW_TYPE )) { set_ident_flags( you.inv[weapon], ISFLAG_KNOW_TYPE ); strcpy(info, "You are wielding "); @@ -1316,7 +1419,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) // there should be a case in here for holy monsters, // see elsewhere in fight.cc {dlb} specdam = 0; - switch (mons_holiness(defender->type)) + switch (mons_holiness(defender)) { case MH_UNDEAD: specdam = 1 + random2(damage_done); @@ -1325,6 +1428,9 @@ void you_attack(int monster_attacked, bool unarmed_attacks) case MH_DEMONIC: specdam = 1 + (random2(damage_done * 15) / 10); break; + + default: + break; } break; @@ -1344,7 +1450,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) break; case SPWPN_ORC_SLAYING: - if (mons_charclass(defender->type) == MONS_ORC) + if (mons_species(defender->type) == MONS_ORC) hurt_monster(defender, 1 + random2(damage_done)); break; @@ -1375,19 +1481,22 @@ void you_attack(int monster_attacked, bool unarmed_attacks) defender->hit_points = 0; specdam = 1 + (random2(damage_done) / 2); - naughty( NAUGHTY_NECROMANCY, 2 ); + did_god_conduct( DID_NECROMANCY, 2 ); break; /* 9 = speed - done before */ case SPWPN_VORPAL: specdam = 1 + random2(damage_done) / 2; + if (damage_done < 1) + mprf(MSGCH_PLAIN, "You strike %s.", + ptr_monam(defender, DESC_NOCAP_THE)); break; case SPWPN_VAMPIRICISM: specdam = 0; // NB: does no extra damage - if (mons_holiness(defender->type) != MH_NATURAL) + if (mons_holiness(defender) != MH_NATURAL) break; else if (mons_res_negative_energy(defender) > 0) break; @@ -1411,12 +1520,12 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (you.hunger_state != HS_ENGORGED) lessen_hunger(random2avg(59, 2), true); - naughty( NAUGHTY_NECROMANCY, 2 ); + did_god_conduct( DID_NECROMANCY, 2 ); break; case SPWPN_DISRUPTION: specdam = 0; - if (mons_holiness(defender->type) == MH_UNDEAD && !one_chance_in(3)) + if (mons_holiness(defender) == MH_UNDEAD && !one_chance_in(3)) { simple_monster_message(defender, " shudders."); specdam += random2avg((1 + (damage_done * 3)), 3); @@ -1431,7 +1540,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) simple_monster_message(defender, " convulses in agony."); specdam += random2( 1 + you.skills[SK_NECROMANCY] ); } - naughty(NAUGHTY_NECROMANCY, 4); + did_god_conduct(DID_NECROMANCY, 4); break; case SPWPN_DISTORTION: @@ -1484,7 +1593,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (coinflip()) { monster_die(defender, KILL_RESET, 0); - return; + return (true); } break; @@ -1519,7 +1628,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (defender->hit_points < 1) { monster_die(defender, KILL_YOU, 0); - return; + return (hit); } if (unarmed_attacks) @@ -1608,13 +1717,13 @@ void you_attack(int monster_attacked, bool unarmed_attacks) sc_dam += 5; if (you.equip[EQ_HELMET] != -1 - && (cmp_helmet_type( you.inv[you.equip[EQ_HELMET]], THELM_HELMET ) - || cmp_helmet_type( you.inv[you.equip[EQ_HELMET]], THELM_HELM ))) + && (get_helmet_type(you.inv[you.equip[EQ_HELMET]]) == THELM_HELMET + || get_helmet_type(you.inv[you.equip[EQ_HELMET]]) == THELM_HELM)) { sc_dam += 2; - if (cmp_helmet_desc( you.inv[you.equip[EQ_HELMET]], THELM_DESC_SPIKED ) - || cmp_helmet_desc( you.inv[you.equip[EQ_HELMET]], THELM_DESC_HORNED )) + if (get_helmet_desc(you.inv[you.equip[EQ_HELMET]]) == THELM_DESC_SPIKED + || get_helmet_desc(you.inv[you.equip[EQ_HELMET]]) == THELM_DESC_HORNED) { sc_dam += 3; } @@ -1676,7 +1785,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) /* no punching with a shield or 2-handed wpn, except staves */ if (bearing_shield || coinflip() - || (ur_armed && hands_reqd == HANDS_TWO_HANDED + || (ur_armed && hands == HANDS_TWO && you.inv[weapon].base_type != OBJ_STAVES && you.inv[weapon].sub_type != WPN_QUARTERSTAFF) ) { @@ -1701,19 +1810,11 @@ void you_attack(int monster_attacked, bool unarmed_attacks) } - your_to_hit = 13 + you.dex / 2 + you.skills[SK_UNARMED_COMBAT] / 2 - + you.skills[SK_FIGHTING] / 5; - - if (wearing_amulet(AMU_INACCURACY)) - your_to_hit -= 5; + // unified to-hit calculation + your_to_hit = calc_your_to_hit_unarmed(); + your_to_hit = random2(your_to_hit); - make_hungry(2, true); - - if (you.hunger_state == HS_STARVING) - your_to_hit -= 3; - - your_to_hit += slaying_bonus(PWPN_HIT); - your_to_hit = random2(your_to_hit); + make_hungry(2, true); damage = sc_dam; //4 + you.experience_level / 3; @@ -1721,7 +1822,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (your_to_hit >= defender->evasion || one_chance_in(30)) { - bool hit = true; + hit = true; int dammod = 10; const int dam_stat_val = calc_stat_to_dam_base(); @@ -1793,8 +1894,8 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (brand == SPWPN_VENOM && coinflip()) poison_monster( defender, true ); - if (mons_holiness(defender->type) == MH_HOLY) - done_good(GOOD_KILLED_ANGEL_I, 1); + if (mons_holiness(defender) == MH_HOLY) + did_god_conduct(DID_KILL_ANGEL, 1); hit = true; } @@ -1833,7 +1934,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) strcat(info, "the ball lightning."); mpr(info); } - return; + return (true); } } else @@ -1851,7 +1952,7 @@ void you_attack(int monster_attacked, bool unarmed_attacks) if (hit) print_wounds(defender); - return; + return (hit); } // end you_attack() void monster_attack(int monster_attacking) @@ -1888,8 +1989,7 @@ void monster_attack(int monster_attacking) // This should happen after the mons_friendly check so we're // only disturbed by hostiles. -- bwr - if (you_are_delayed()) - stop_delay(); + interrupt_activity( AI_MONSTER_ATTACKS, attacker ); if (attacker->type == MONS_GIANT_SPORE || attacker->type == MONS_BALL_LIGHTNING) @@ -1898,7 +1998,7 @@ void monster_attack(int monster_attacking) return; } - // if a friend wants to help, they can attack + // if a friend wants to help, they can attack if (you.pet_target == MHITNOT) you.pet_target = monster_attacking; @@ -1906,7 +2006,7 @@ void monster_attack(int monster_attacking) return; if (you.duration[DUR_REPEL_UNDEAD] - && mons_holiness( attacker->type ) == MH_UNDEAD + && mons_holiness( attacker ) == MH_UNDEAD && !check_mons_resist_magic( attacker, you.piety )) { simple_monster_message(attacker, @@ -1932,7 +2032,7 @@ void monster_attack(int monster_attacking) if (grd[attacker->x][attacker->y] == DNGN_SHALLOW_WATER && !mons_flies( attacker ) - && !mons_flag( attacker->type, M_AMPHIBIOUS ) + && !mons_class_flag( attacker->type, M_AMPHIBIOUS ) && monster_habitat( attacker->type ) == DNGN_FLOOR && one_chance_in(4)) { @@ -2015,16 +2115,25 @@ void monster_attack(int monster_attacking) } // Factors against blocking - const int con_block = 15 + attacker->hit_dice - + (10 * you.shield_blocks * you.shield_blocks); - - // Factors for blocking - const int pro_block = player_shield_class() + (random2(you.dex) / 5); + // [dshaligram] Scaled back HD effect to 50% of previous, reduced + // shield_blocks multiplier to 5. + const int con_block = + random2(15 + attacker->hit_dice / 2 + + (5 * you.shield_blocks * you.shield_blocks)); + + // Factors for blocking: + // [dshaligram] Added weighting for shields skill, increased dex bonus + // from .2 to .25 + const int pro_block = + random2(player_shield_class()) + + (random2(you.dex) / 4) + + (random2(skill_bump(SK_SHIELDS)) / 4) + - 1; if (!you.paralysis && !you_are_delayed() && !you.conf && player_monster_visible( attacker ) && player_shield_class() > 0 - && random2(con_block) <= random2(pro_block)) + && con_block <= pro_block) { you.shield_blocks++; @@ -2066,8 +2175,8 @@ void monster_attack(int monster_attacking) damage_taken = random2(damage_size); - if (cmp_equip_race(mitm[attacker->inv[hand_used]],ISFLAG_ORCISH) - && mons_charclass(attacker->type) == MONS_ORC + if (get_equip_race(mitm[attacker->inv[hand_used]]) == ISFLAG_ORCISH + && mons_species(attacker->type) == MONS_ORC && coinflip()) { damage_taken++; @@ -2155,7 +2264,7 @@ void monster_attack(int monster_attacking) if (attacker->type != MONS_DANCING_WEAPON && mmov_x != NON_ITEM && mitm[mmov_x].base_type == OBJ_WEAPONS - && !launches_things( mitm[mmov_x].sub_type )) + && !is_range_weapon( mitm[mmov_x] )) { strcat(info, " with "); it_name(mmov_x, DESC_NOCAP_A, str_pass); // was 7 @@ -2170,7 +2279,7 @@ void monster_attack(int monster_attacking) { if (you.equip[EQ_BODY_ARMOUR] != -1) { - const int body_arm_mass = mass_item( you.inv[you.equip[EQ_BODY_ARMOUR]] ); + const int body_arm_mass = item_mass( you.inv[you.equip[EQ_BODY_ARMOUR]] ); if (!player_light_armour() && coinflip() && random2(1000) <= body_arm_mass) @@ -2308,7 +2417,7 @@ void monster_attack(int monster_attacking) } damage_taken += extraDamage; - scrolls_burn(1, OBJ_SCROLLS); + expose_player_to_element(BEAM_FIRE, 1); break; case MONS_SMALL_SNAKE: @@ -2402,8 +2511,8 @@ void monster_attack(int monster_attacking) mpr("You still can't move!", MSGCH_WARN); else mpr("You suddenly lose the ability to move!", MSGCH_WARN); - - you.paralysis += 1 + random2(3); + if ( you.paralysis == 0 || mclas == MONS_RED_WASP ) + you.paralysis += 1 + random2(3); } break; @@ -2445,7 +2554,7 @@ void monster_attack(int monster_attacking) mpr(info); damage_taken += extraDamage; - scrolls_burn( 1, OBJ_POTIONS ); + expose_player_to_element(BEAM_COLD, 1); break; case MONS_ICE_DEVIL: @@ -2483,7 +2592,7 @@ void monster_attack(int monster_attacking) damage_taken += extraDamage; - scrolls_burn(1, OBJ_POTIONS); + expose_player_to_element(BEAM_COLD, 1); break; case MONS_ELECTRIC_GOLEM: @@ -2579,8 +2688,18 @@ void monster_attack(int monster_attacking) give_bad_mutation(); } break; + + case MONS_MOTH_OF_WRATH: + if (one_chance_in(3)) { + simple_monster_message(attacker, " infuriates you!"); + go_berserk(false); + } + break; + + default: + break; } // end of switch for special attacks. - /* use brek for level drain, maybe with beam variables, + /* use break for level drain, maybe with beam variables, because so many creatures use it. */ } @@ -2912,7 +3031,7 @@ bool monsters_fight(int monster_attacking, int monster_attacked) int weapon = -1; // monster weapon, if any int damage_taken = 0; bool hit = false; - int mmov_x = 0; + // int mmov_x = 0; bool water_attack = false; int specdam = 0; int hand_used = 0; @@ -2939,19 +3058,14 @@ bool monsters_fight(int monster_attacking, int monster_attacked) return false; } - if (grd[attacker->x][attacker->y] == DNGN_SHALLOW_WATER - && !mons_flies( attacker ) - && !mons_flag( attacker->type, M_AMPHIBIOUS ) - && habitat == DNGN_FLOOR - && one_chance_in(4)) + if (monster_floundering(attacker) && one_chance_in(4)) { mpr("You hear a splashing noise."); return true; } - if (grd[defender->x][defender->y] == DNGN_SHALLOW_WATER - && !mons_flies(defender) - && habitat == DNGN_DEEP_WATER) + // habitat is the favoured habitat of the attacker + if (monster_floundering(defender) && habitat == DNGN_DEEP_WATER) { water_attack = true; } @@ -3022,21 +3136,21 @@ bool monsters_fight(int monster_attacking, int monster_attacked) mons_to_hit = random2(mons_to_hit); - if (mons_to_hit >= defender->evasion - || ((defender->speed_increment <= 60 - || defender->behaviour == BEH_SLEEP) && !one_chance_in(20))) + if (mons_to_hit >= defender->evasion || + ((mons_is_paralysed(defender) || defender->behaviour == BEH_SLEEP) + && !one_chance_in(20))) { hit = true; if (attacker->inv[hand_used] != NON_ITEM && mitm[attacker->inv[hand_used]].base_type == OBJ_WEAPONS - && !launches_things( mitm[attacker->inv[hand_used]].sub_type )) + && !is_range_weapon( mitm[attacker->inv[hand_used]] )) { damage_taken = random2(property( mitm[attacker->inv[hand_used]], PWPN_DAMAGE )); - if (cmp_equip_race(mitm[attacker->inv[hand_used]],ISFLAG_ORCISH) - && mons_charclass(attacker->type) == MONS_ORC + if (get_equip_race(mitm[attacker->inv[hand_used]]) == ISFLAG_ORCISH + && mons_species(attacker->type) == MONS_ORC && coinflip()) { damage_taken++; @@ -3077,7 +3191,11 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " misses "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); + if (attacker == defender) + strcat(info, + mons_pronoun(attacker->type, PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); strcat(info, "."); mpr(info); } @@ -3089,8 +3207,11 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " hits "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); - + if ( attacker == defender ) + strcat(info, + mons_pronoun(attacker->type, PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); #if DEBUG_DIAGNOSTICS strcat(info, " for "); // note: doesn't take account of special weapons etc @@ -3110,12 +3231,16 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " hits "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); + if (attacker == defender) + strcat(info, + mons_pronoun(attacker->type, PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); if (attacker->type != MONS_DANCING_WEAPON && attacker->inv[hand_used] != NON_ITEM && mitm[attacker->inv[hand_used]].base_type == OBJ_WEAPONS - && !launches_things( mitm[attacker->inv[hand_used]].sub_type )) + && !is_range_weapon( mitm[attacker->inv[hand_used]] )) { strcat(info, " with "); it_name(mmov_x, DESC_NOCAP_A, str_pass); // was 7 @@ -3148,7 +3273,11 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " stings "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); + if (attacker == defender) + strcat(info, mons_pronoun(attacker->type, + PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); strcat(info, "."); mpr(info); } @@ -3165,7 +3294,11 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " stings "); - strcpy(info, ptr_monam(defender, DESC_NOCAP_THE)); + if (attacker == defender) + strcat(info, mons_pronoun(attacker->type, + PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); strcat(info, "."); mpr(info); } @@ -3218,7 +3351,11 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " stings "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); + if (attacker == defender) + strcat(info, mons_pronoun(attacker->type, + PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); strcat(info, "."); mpr(info); } @@ -3295,7 +3432,11 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " freezes "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); + if (attacker == defender) + strcat(info, mons_pronoun(attacker->type, + PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); strcat(info, "."); mpr(info); } @@ -3324,7 +3465,11 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " freezes "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); + if (attacker == defender) + strcat(info, mons_pronoun(attacker->type, + PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); strcat(info, "."); mpr(info); } @@ -3341,7 +3486,11 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " shocks "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); + if (attacker == defender) + strcat(info, mons_pronoun(attacker->type, + PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); strcat(info, "."); mpr(info); } @@ -3405,7 +3554,11 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " burns "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE )); + if (attacker == defender) + strcat(info, mons_pronoun(attacker->type, + PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE )); if (specdam < 3) strcat(info, "."); @@ -3429,11 +3582,15 @@ bool monsters_fight(int monster_attacking, int monster_attacked) { if (sees) { - mmov_x = attacker->inv[hand_used]; + // mmov_x = attacker->inv[hand_used]; strcpy(info, ptr_monam(attacker, DESC_CAP_THE)); strcat(info, " freezes "); - strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); + if (attacker == defender) + strcat(info, mons_pronoun(attacker->type, + PRONOUN_REFLEXIVE)); + else + strcat(info, ptr_monam(defender, DESC_NOCAP_THE)); if (specdam < 3) strcat(info, "."); @@ -3451,7 +3608,7 @@ bool monsters_fight(int monster_attacking, int monster_attacked) if (attacker->type == MONS_PLAYER_GHOST) break; specdam = 0; - switch (mons_holiness(defender->type)) + switch (mons_holiness(defender)) { case MH_HOLY: // I would think that it would do zero damage {dlb} @@ -3465,6 +3622,9 @@ bool monsters_fight(int monster_attacking, int monster_attacked) case MH_DEMONIC: specdam += 1 + (random2(damage_taken) * 15) / 10; break; + + default: + break; } break; @@ -3496,7 +3656,7 @@ bool monsters_fight(int monster_attacking, int monster_attacked) break; case SPWPN_ORC_SLAYING: - if (mons_charclass(defender->type) == MONS_ORC) + if (mons_species(defender->type) == MONS_ORC) hurt_monster(defender, 1 + random2(damage_taken)); break; @@ -3563,7 +3723,7 @@ bool monsters_fight(int monster_attacking, int monster_attacked) specdam = 0; - if (mons_holiness(defender->type) == MH_UNDEAD + if (mons_holiness(defender) == MH_UNDEAD && !one_chance_in(3)) { simple_monster_message(defender, " shudders."); @@ -3676,7 +3836,7 @@ static int weapon_type_modify( int weapnum, char noise[80], char noise2[80], else if (you.inv[weapnum].base_type == OBJ_WEAPONS) weap_type = you.inv[weapnum].sub_type; - noise2[0] = '\0'; + noise2[0] = 0; // All weak hits look the same, except for when the player // has a non-weapon in hand. -- bwr @@ -3690,7 +3850,7 @@ static int weapon_type_modify( int weapnum, char noise[80], char noise2[80], return (damage); } - // take transformations into account, if no weapon is weilded + // take transformations into account, if no weapon is wielded if (weap_type == WPN_UNARMED && you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE) { @@ -3755,6 +3915,7 @@ static int weapon_type_modify( int weapnum, char noise[80], char noise2[80], return (damage); case WPN_BOW: + case WPN_LONGBOW: case WPN_CROSSBOW: case WPN_HAND_CROSSBOW: if (damage < HIT_STRONG) @@ -3776,11 +3937,14 @@ static int weapon_type_modify( int weapnum, char noise[80], char noise2[80], case WPN_SCYTHE: case WPN_QUICK_BLADE: case WPN_KATANA: + case WPN_LAJATANG: + case WPN_LOCHABER_AXE: case WPN_EXECUTIONERS_AXE: case WPN_DOUBLE_SWORD: case WPN_TRIPLE_SWORD: case WPN_SABRE: case WPN_DEMON_BLADE: + case WPN_BLESSED_BLADE: if (damage < HIT_MED) strcpy( noise, "slash" ); else if (damage < HIT_STRONG) @@ -3797,7 +3961,7 @@ static int weapon_type_modify( int weapnum, char noise[80], char noise2[80], case WPN_MACE: case WPN_FLAIL: case WPN_GREAT_MACE: - case WPN_GREAT_FLAIL: + case WPN_DIRE_FLAIL: case WPN_QUARTERSTAFF: case WPN_GIANT_CLUB: case WPN_HAMMER: @@ -3857,7 +4021,7 @@ int weapon_str_weight( int wpn_class, int wpn_type ) int ret; const int wpn_skill = weapon_skill( wpn_class, wpn_type ); - const int hands_reqd = hands_reqd_for_weapon( wpn_class, wpn_type ); + const int hands = hands_reqd( wpn_class, wpn_type, player_size() ); // These are low values, because we'll be adding some bonus to the // larger weapons later. Remember also that 1-1/2-hand weapons get @@ -3898,7 +4062,7 @@ int weapon_str_weight( int wpn_class, int wpn_type ) else if (wpn_type == WPN_QUICK_BLADE) // high dex is very good for these ret = 1; - if (hands_reqd == HANDS_TWO_HANDED) + if (hands == HANDS_TWO) ret += 2; // most weapons are capped at 8 @@ -3929,10 +4093,9 @@ static inline int player_weapon_str_weight( void ) int ret = weapon_str_weight( you.inv[weapon].base_type, you.inv[weapon].sub_type ); const bool shield = (you.equip[EQ_SHIELD] != -1); - const int hands_reqd = hands_reqd_for_weapon( you.inv[weapon].base_type, - you.inv[weapon].sub_type ); + const int hands = hands_reqd(you.inv[weapon], player_size()); - if (hands_reqd == HANDS_ONE_OR_TWO_HANDED && !shield) + if (hands == HANDS_HALF && !shield) ret += 1; return (ret); diff --git a/crawl-ref/source/fight.h b/crawl-ref/source/fight.h index 10515868c6..fb6a1a812b 100644 --- a/crawl-ref/source/fight.h +++ b/crawl-ref/source/fight.h @@ -3,6 +3,8 @@ * Summary: Functions used during combat. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -32,7 +34,7 @@ int weapon_str_weight( int wpn_class, int wpn_type ); /* *********************************************************************** * called from: acr - it_use3 * *********************************************************************** */ -void you_attack(int monster_attacked, bool unarmed_attacks); +bool you_attack(int monster_attacked, bool unarmed_attacks); // last updated: 08jun2000 {dlb} @@ -48,5 +50,9 @@ void monster_attack(int monster_attacking); * *********************************************************************** */ bool monsters_fight(int monster_attacking, int monster_attacked); +int calc_your_to_hit( int heavy_armour, bool hand_and_a_half_bonus, + bool water_attack, bool random_factor ); + +int calc_heavy_armour_penalty( bool random_factor ); #endif diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc index 367fe30ec4..03b145a45b 100644 --- a/crawl-ref/source/files.cc +++ b/crawl-ref/source/files.cc @@ -24,6 +24,7 @@ #include "AppHdr.h" #include "files.h" +#include "version.h" #include #include @@ -31,6 +32,8 @@ #include #include +#include + #ifdef DOS #include #include @@ -43,22 +46,13 @@ #include #endif -#ifdef USE_EMX -#include -#include -#include -#endif - -#ifdef OS9 -#include -#else -#include -#endif - #ifdef __MINGW32__ #include +#include #endif +#include + #include "externs.h" #include "cloud.h" @@ -66,12 +60,15 @@ #include "debug.h" #include "dungeon.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" +#include "libutil.h" #include "message.h" #include "misc.h" #include "monstuff.h" #include "mon-util.h" #include "mstuff2.h" +#include "notes.h" #include "player.h" #include "randart.h" #include "skills2.h" @@ -79,7 +76,12 @@ #include "stuff.h" #include "tags.h" #include "travel.h" -#include "wpn-misc.h" + +#ifdef SHARED_FILES_CHMOD_PRIVATE +#define DO_CHMOD_PRIVATE(x) chmod( (x), SHARED_FILES_CHMOD_PRIVATE ) +#else +#define DO_CHMOD_PRIVATE(x) // empty command +#endif void save_level(int level_saved, bool was_a_labyrinth, char where_were_you); @@ -205,69 +207,340 @@ static void restore_tagged_file( FILE *restoreFile, int fileType, static void load_ghost(); -std::string get_savedir_filename(const char *prefix, const char *suffix, - const char *extension) +static std::string uid_as_string() { - char filename[1200]; -#ifdef SAVE_DIR_PATH - snprintf(filename, sizeof filename, SAVE_DIR_PATH "%s%d%s.%s", - prefix, (int) getuid(), suffix, extension); +#ifdef MULTIUSER + char struid[20]; + snprintf( struid, sizeof struid, "%d", (int)getuid() ); + return std::string(struid); #else - snprintf(filename, sizeof filename, "%s%s.%s", - prefix, suffix, extension); + return std::string(); +#endif +} + +static bool is_uid_file(const std::string &name, const std::string &ext) +{ + std::string save_suffix = get_savedir_filename("", "", ""); + save_suffix += ext; #ifdef DOS - strupr(filename); + // Grumble grumble. Hang all retarded operating systems. + uppercase(save_suffix); #endif + + save_suffix = save_suffix.substr(Options.save_dir.length()); + + std::string::size_type suffix_pos = name.find(save_suffix); + return (suffix_pos != std::string::npos + && suffix_pos == name.length() - save_suffix.length() + && suffix_pos != 0 +#ifdef MULTIUSER + // See verifyPlayerName() in newgame.cc + && !isdigit(name[suffix_pos - 1]) #endif - return std::string(filename); + ); + } -std::string get_prefs_filename() +static bool is_save_file_name(const std::string &name) { - return get_savedir_filename("start", "ns", "prf"); + return is_uid_file(name, ".sav"); } -void make_filename( char *buf, const char *prefix, int level, int where, - bool isLabyrinth, bool isGhost ) +#ifdef LOAD_UNPACKAGE_CMD +static bool is_packed_save(const std::string &name) { - UNUSED( isGhost ); + return is_uid_file(name, PACKAGE_SUFFIX); +} +#endif - char suffix[4], lvl[5]; - char finalprefix[kFileNameLen]; +// Returns a full player struct read from the save. +static player read_character_info(const std::string &savefile) +{ + player fromfile; + player backup = you; - strcpy(suffix, (level < 10) ? "0" : ""); - itoa(level, lvl, 10); - strcat(suffix, lvl); - suffix[2] = where + 97; - suffix[3] = '\0'; + FILE *charf = fopen(savefile.c_str(), "rb"); + if (!charf) + return fromfile; + + char majorVersion = 0; + char minorVersion = 0; + + if (!determine_version(charf, majorVersion, minorVersion)) + goto done_reading_character; + + if (majorVersion != SAVE_MAJOR_VERSION) + goto done_reading_character; - // init buf - buf[0] = '\0'; + restore_tagged_file(charf, TAGTYPE_PLAYER_NAME, minorVersion); + fromfile = you; + you = backup; + +done_reading_character: + fclose(charf); + return fromfile; +} -#ifdef SAVE_DIR_PATH - strcpy(buf, SAVE_DIR_PATH); +// Returns the names of all files in the given directory. Note that the +// filenames returned are relative to the directory. +static std::vector get_dir_files(const std::string &dirname) +{ + std::vector files; + + DIR *dir = opendir(dirname.c_str()); + if (!dir) + return (files); + + while (dirent *entry = readdir(dir)) + { + std::string name = entry->d_name; + if (name == "." || name == "..") + continue; + + files.push_back(name); + } + closedir(dir); + + return (files); +} + +static bool file_exists(const std::string &name) +{ + FILE *f = fopen(name.c_str(), "r"); + const bool exists = !!f; + if (f) + fclose(f); + return (exists); +} + +// Low-tech existence check. +static bool dir_exists(const std::string &dir) +{ + DIR *d = opendir(dir.c_str()); + const bool exists = !!d; + if (d) + closedir(d); + + return (exists); +} + +static int create_directory(const char *dir) +{ +#if defined(MULTIUSER) + return mkdir(dir, SHARED_FILES_CHMOD_PUBLIC | 0111); +#elif defined(DOS) + // djgpp doesn't seem to have mkdir. + return (-1); +#else + return mkdir(dir); #endif +} - strncpy(finalprefix, prefix, kFileNameLen); - finalprefix[kFileNameLen] = '\0'; +static bool create_dirs(const std::string &dir) +{ + std::string sep = " "; + sep[0] = FILE_SEPARATOR; + std::vector segments = + split_string( + sep.c_str(), + dir, + false, + false); + + std::string path; + for (int i = 0, size = segments.size(); i < size; ++i) + { + path += segments[i]; + path += FILE_SEPARATOR; + + if (!dir_exists(path) && create_directory(path.c_str())) + return (false); + } + return (true); +} + +std::string datafile_path(const std::string &basename) +{ + std::string cdir = SysEnv.crawl_dir? SysEnv.crawl_dir : ""; + + const std::string rawbases[] = { +#ifdef DATA_DIR_PATH + DATA_DIR_PATH, +#else + cdir, +#endif + }; - strcat(buf, finalprefix); + const std::string prefixes[] = { + std::string("dat") + FILE_SEPARATOR, + std::string("data") + FILE_SEPARATOR, + std::string("crawl-data") + FILE_SEPARATOR, + "", + }; -#ifdef SAVE_DIR_PATH - // everyone sees everyone else's ghosts. :) - char uid[10]; - if (!isGhost) + std::vector bases; + for (unsigned i = 0; i < sizeof(rawbases) / sizeof(*rawbases); ++i) { - itoa( (int) getuid(), uid, 10 ); - strcat(buf, uid); + std::string base = rawbases[i]; + if (base.empty()) + continue; + + if (base[base.length() - 1] != FILE_SEPARATOR) + base += FILE_SEPARATOR; + bases.push_back(base); } + +#ifndef DATA_DIR_PATH + bases.push_back(""); #endif - strcat(buf, "."); - if (isLabyrinth) - strcat(buf, "lab"); // temporary level - else - strcat(buf, suffix); + for (unsigned b = 0, size = bases.size(); b < size; ++b) + { + for (unsigned p = 0; p < sizeof(prefixes) / sizeof(*prefixes); ++p) + { + std::string name = bases[b] + prefixes[p] + basename; + if (file_exists(name)) + return (name); + } + } + + // Die horribly. + fprintf(stderr, "Cannot find data file '%s' anywhere, aborting\n", + basename.c_str()); + exit(1); + + return (""); +} + +void check_savedir(std::string &dir) +{ + if (dir.empty()) + return; + + std::string sep = " "; + sep[0] = FILE_SEPARATOR; + + dir = replace_all(dir, "/", sep); + dir = replace_all(dir, "\\", sep); + + // Suffix the separator if necessary + if (dir[dir.length() - 1] != FILE_SEPARATOR) + dir += FILE_SEPARATOR; + + if (!dir_exists(dir) && !create_dirs(dir)) + { + fprintf(stderr, "Save directory \"%s\" does not exist " + "and I can't create it.\n", + dir.c_str()); + exit(1); + } +} + +// Given a simple (relative) name of a save file, returns the full path of +// the file in the Crawl saves directory. +std::string get_savedir_path(const std::string &shortpath) +{ + return (Options.save_dir + shortpath); +} + +/* + * Returns a list of the names of characters that are already saved for the + * current user. + */ +std::vector find_saved_characters() +{ + std::string searchpath = Options.save_dir; + + if (searchpath.empty()) + searchpath = "."; + + std::vector allfiles = get_dir_files(searchpath); + std::vector chars; + for (int i = 0, size = allfiles.size(); i < size; ++i) + { + std::string filename = allfiles[i]; +#ifdef LOAD_UNPACKAGE_CMD + if (!is_packed_save(filename)) + continue; + + std::string basename = + filename.substr( + 0, + filename.length() - strlen(PACKAGE_SUFFIX)); + + std::string zipname = get_savedir_path(basename); + + // This is the filename we actually read ourselves. + filename = basename + ".sav"; + + char cmd_buff[1024]; + snprintf( cmd_buff, sizeof(cmd_buff), UNPACK_SPECIFIC_FILE_CMD, + zipname.c_str(), + filename.c_str() ); + + if (system(cmd_buff) != 0) + continue; +#endif + if (is_save_file_name(filename)) + { + player p = read_character_info(get_savedir_path(filename)); + if (p.is_valid()) + chars.push_back(p); + } + +#ifdef LOAD_UNPACKAGE_CMD + // If we unpacked the .sav file, throw it away now. + unlink( get_savedir_path(filename).c_str() ); +#endif + } + + std::sort(chars.begin(), chars.end()); + return (chars); +} + +std::string get_savedir_filename(const char *prefix, const char *suffix, + const char *extension, bool suppress_uid) +{ + std::string result = Options.save_dir; + + // Shorten string as appropriate + result += std::string(prefix).substr(0, kFileNameLen); + + // Technically we should shorten the string first. But if + // MULTIUSER is set we'll have long filenames anyway. Caveat + // emptor. + if ( !suppress_uid ) + result += uid_as_string(); + + result += suffix; + + if ( *extension ) { + result += '.'; + result += extension; + } + +#ifdef DOS + uppercase(result); +#endif + return result; +} + +std::string get_prefs_filename() +{ + return get_savedir_filename("start", "ns", "prf"); +} + +std::string make_filename( const char *prefix, int level, int where, + bool isLabyrinth, bool isGhost ) +{ + char suffix[4], lvl[5]; + strcpy(suffix, (level < 10) ? "0" : ""); + itoa(level, lvl, 10); + strcat(suffix, lvl); + suffix[2] = where + 97; + suffix[3] = 0; + return get_savedir_filename( prefix, "", isLabyrinth ? "lab" : suffix, + isGhost ); } static void write_tagged_file( FILE *dataFile, char majorVersion, @@ -300,16 +573,9 @@ static void write_tagged_file( FILE *dataFile, char majorVersion, bool travel_load_map( char branch, int absdepth ) { - char cha_fil[kFileNameSize]; - - make_filename( cha_fil, you.your_name, absdepth, branch, - false, false ); -#ifdef DOS - strupr(cha_fil); -#endif - // Try to open level savefile. - FILE *levelFile = fopen(cha_fil, "rb"); + FILE *levelFile = fopen(make_filename(you.your_name, absdepth, branch, + false, false).c_str(), "rb"); if (!levelFile) return false; @@ -320,7 +586,7 @@ bool travel_load_map( char branch, int absdepth ) char minorVersion; if (!determine_level_version( levelFile, majorVersion, minorVersion ) - || majorVersion != 4) + || majorVersion != SAVE_MAJOR_VERSION) { fclose(levelFile); return false; @@ -333,37 +599,22 @@ bool travel_load_map( char branch, int absdepth ) return true; } +struct follower { + monsters mons; + std::vector items; + + follower() : mons(), items() { } + follower(const monsters &m) : mons(m), items() { } +}; + void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth, char old_level, char where_were_you2 ) { int j = 0; int i = 0, count_x = 0, count_y = 0; - char cha_fil[kFileNameSize]; - - int foll_class[8]; - int foll_hp[8]; - int foll_hp_max[8]; - unsigned char foll_HD[8]; - int foll_AC[8]; - char foll_ev[8]; - unsigned char foll_speed[8]; - unsigned char foll_speed_inc[8]; - - unsigned char foll_targ_1_x[8]; - unsigned char foll_targ_1_y[8]; - unsigned char foll_beh[8]; - unsigned char foll_att[8]; - int foll_sec[8]; - unsigned char foll_hit[8]; - - unsigned char foll_ench[8][NUM_MON_ENCHANTS]; - unsigned char foll_flags[8]; - - item_def foll_item[8][8]; - - int itmf = 0; - int ic = 0; - int imn = 0; + + std::vector followers; + int val; bool just_created_level = false; @@ -372,15 +623,17 @@ void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth, window(1, 1, 80, 25); #endif - make_filename( cha_fil, you.your_name, you.your_level, you.where_are_you, - you.level_type != LEVEL_DUNGEON, false ); + std::string cha_fil = make_filename( you.your_name, you.your_level, + you.where_are_you, + you.level_type != LEVEL_DUNGEON, + false ); if (you.level_type == LEVEL_DUNGEON) { if (tmp_file_pairs[you.your_level][you.where_are_you] == false) { // make sure old file is gone - unlink(cha_fil); + unlink(cha_fil.c_str()); // save the information for later deletion -- DML 6/11/99 tmp_file_pairs[you.your_level][you.where_are_you] = true; @@ -389,9 +642,6 @@ void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth, you.prev_targ = MHITNOT; - int following = -1; - int minvc = 0; - // Don't delete clouds just because the player saved and restarted. if (load_mode != LOAD_RESTART_GAME) { @@ -412,13 +662,10 @@ void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth, if (count_x == you.x_pos && count_y == you.y_pos) continue; - following++; - foll_class[following] = -1; - if (mgrd[count_x][count_y] == NON_MONSTER) continue; - struct monsters *fmenv = &menv[mgrd[count_x][count_y]]; + monsters *fmenv = &menv[mgrd[count_x][count_y]]; if (fmenv->type == MONS_PLAYER_GHOST && fmenv->hit_points < fmenv->max_hit_points / 2) @@ -438,52 +685,23 @@ void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth, mpr( info, MSGCH_DIAGNOSTICS ); #endif - foll_class[following] = fmenv->type; - foll_hp[following] = fmenv->hit_points; - foll_hp_max[following] = fmenv->max_hit_points; - foll_HD[following] = fmenv->hit_dice; - foll_AC[following] = fmenv->armour_class; - foll_ev[following] = fmenv->evasion; - foll_speed[following] = fmenv->speed; - foll_speed_inc[following] = fmenv->speed_increment; - foll_targ_1_x[following] = fmenv->target_x; - foll_targ_1_y[following] = fmenv->target_y; - - for (minvc = 0; minvc < NUM_MONSTER_SLOTS; ++minvc) + follower f(*fmenv); + + for (int minvc = 0; minvc < NUM_MONSTER_SLOTS; ++minvc) { const int item = fmenv->inv[minvc]; if (item == NON_ITEM) { - foll_item[following][minvc].quantity = 0; + f.items.push_back(item_def()); continue; } - foll_item[following][minvc] = mitm[item]; + f.items.push_back(mitm[item]); destroy_item( item ); } - foll_beh[following] = fmenv->behaviour; - foll_att[following] = fmenv->attitude; - foll_sec[following] = fmenv->number; - foll_hit[following] = fmenv->foe; - - for (j = 0; j < NUM_MON_ENCHANTS; j++) - { - foll_ench[following][j] = fmenv->enchantment[j]; - fmenv->enchantment[j] = ENCH_NONE; - } - - foll_flags[following] = fmenv->flags; - - fmenv->flags = 0; - fmenv->type = -1; - fmenv->hit_points = 0; - fmenv->max_hit_points = 0; - fmenv->hit_dice = 0; - fmenv->armour_class = 0; - fmenv->evasion = 0; - - mgrd[count_x][count_y] = NON_MONSTER; + followers.push_back(f); + monster_cleanup(fmenv); } } // end of grabbing followers @@ -495,22 +713,18 @@ void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth, // clear out ghost/demon lord information: strcpy( ghost.name, "" ); - for (ic = 0; ic < NUM_GHOST_VALUES; ++ic) + for (int ic = 0; ic < NUM_GHOST_VALUES; ++ic) ghost.values[ic] = 0; -#ifdef DOS - strupr(cha_fil); -#endif - // Try to open level savefile. - FILE *levelFile = fopen(cha_fil, "rb"); + FILE *levelFile = fopen(cha_fil.c_str(), "rb"); // GENERATE new level when the file can't be opened: if (levelFile == NULL) { strcpy(ghost.name, ""); - for (imn = 0; imn < NUM_GHOST_VALUES; ++imn) + for (int imn = 0; imn < NUM_GHOST_VALUES; ++imn) ghost.values[imn] = 0; builder( you.your_level, you.level_type ); @@ -541,7 +755,7 @@ void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth, // sanity check - EOF if (!feof( levelFile )) { - snprintf( info, INFO_SIZE, "\nIncomplete read of \"%s\" - aborting.\n", cha_fil); + snprintf( info, INFO_SIZE, "\nIncomplete read of \"%s\" - aborting.\n", cha_fil.c_str()); perror(info); end(-1); } @@ -747,15 +961,14 @@ found_stair: if (you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS) grd[you.x_pos][you.y_pos] = DNGN_FLOOR; */ - following = 0; - int fmenv = -1; + int following = 0; // actually "move" the followers if applicable if ((you.level_type == LEVEL_DUNGEON || you.level_type == LEVEL_PANDEMONIUM) && load_mode == LOAD_ENTER_LEVEL) { - for (ic = 0; ic < 2; ic++) + for (int ic = 0; ic < 2; ic++) { for (count_x = you.x_pos - 6; count_x < you.x_pos + 7; count_x++) @@ -789,64 +1002,41 @@ found_stair: goto out_of_foll; } - while (fmenv < 7) + if (followers.size()) { - fmenv++; - - if (foll_class[fmenv] == -1) - continue; - - menv[following].type = foll_class[fmenv]; - menv[following].hit_points = foll_hp[fmenv]; - menv[following].max_hit_points = foll_hp_max[fmenv]; - menv[following].hit_dice = foll_HD[fmenv]; - menv[following].armour_class = foll_AC[fmenv]; - menv[following].evasion = foll_ev[fmenv]; - menv[following].speed = foll_speed[fmenv]; + follower f = followers.front(); + followers.erase(followers.begin()); + + menv[following] = f.mons; menv[following].x = count_x; menv[following].y = count_y; menv[following].target_x = 0; menv[following].target_y = 0; - menv[following].speed_increment = foll_speed_inc[fmenv]; + menv[following].flags |= MF_JUST_SUMMONED; - for (minvc = 0; minvc < NUM_MONSTER_SLOTS; minvc++) + for (int minvc = 0; minvc < NUM_MONSTER_SLOTS; minvc++) { + menv[following].inv[minvc] = NON_ITEM; - if (!is_valid_item(foll_item[fmenv][minvc])) - { - menv[following].inv[minvc] = NON_ITEM; - continue; - } - - itmf = get_item_slot(0); - if (itmf == NON_ITEM) + const item_def &minvitem = f.items[minvc]; + if (minvitem.base_type != OBJ_UNASSIGNED) { - menv[following].inv[minvc] = NON_ITEM; - continue; + int itmf = get_item_slot(0); + if (itmf == NON_ITEM) + { + menv[following].inv[minvc] = NON_ITEM; + continue; + } + + mitm[itmf] = minvitem; + mitm[itmf].x = 0; + mitm[itmf].y = 0; + mitm[itmf].link = NON_ITEM; + menv[following].inv[minvc] = itmf; } - - mitm[itmf] = foll_item[fmenv][minvc]; - mitm[itmf].x = 0; - mitm[itmf].y = 0; - mitm[itmf].link = NON_ITEM; - - menv[following].inv[minvc] = itmf; } - - menv[following].behaviour = foll_beh[fmenv]; - menv[following].attitude = foll_att[fmenv]; - menv[following].number = foll_sec[fmenv]; - menv[following].foe = foll_hit[fmenv]; - - for (j = 0; j < NUM_MON_ENCHANTS; j++) - menv[following].enchantment[j]=foll_ench[fmenv][j]; - - menv[following].flags = foll_flags[fmenv]; - menv[following].flags |= MF_JUST_SUMMONED; - mgrd[count_x][count_y] = following; - break; - } + } // followers.size() } } } @@ -932,24 +1122,18 @@ found_stair: void save_level(int level_saved, bool was_a_labyrinth, char where_were_you) { - char cha_fil[kFileNameSize]; - - make_filename( cha_fil, you.your_name, level_saved, where_were_you, - was_a_labyrinth, false ); + std::string cha_fil = make_filename( you.your_name, level_saved, + where_were_you, was_a_labyrinth, + false ); you.prev_targ = MHITNOT; -#ifdef DOS - strupr(cha_fil); -#endif - - FILE *saveFile = fopen(cha_fil, "wb"); + FILE *saveFile = fopen(cha_fil.c_str(), "wb"); if (saveFile == NULL) { - strcpy(info, "Unable to open \""); - strcat(info, cha_fil ); - strcat(info, "\" for writing!"); + snprintf(info, INFO_SIZE, "Unable to open \"%s\" for writing!", + cha_fil.c_str()); perror(info); end(-1); } @@ -957,164 +1141,95 @@ void save_level(int level_saved, bool was_a_labyrinth, char where_were_you) // nail all items to the ground fix_item_coordinates(); - // 4.0 initial genesis of saved format - // 4.1 added attitude tag - // 4.2 replaced old 'enchantment1' and with 'flags' (bitfield) - // 4.3 changes to make the item structure more sane - // 4.4 changes to the ghost save section - // 4.5 spell and ability letter arrays - // 4.6 inventory slots of items - // 4.7 origin tracking for items - // 4.8 widened env.map to 2 bytes + // 0.0 initial genesis of saved format + // 0.1 added attitude tag + // 0.2 replaced old 'enchantment1' and with 'flags' (bitfield) + // 0.3 changes to make the item structure more sane + // 0.4 changes to the ghost save section + // 0.5 spell and ability letter arrays + // 0.6 inventory slots of items + // 0.7 origin tracking for items + // 0.8 widened env.map to 2 bytes + // 0.9 inscriptions (hp) + // 0.10 Monster colour and spells separated from mons->number. - write_tagged_file( saveFile, 4, 8, TAGTYPE_LEVEL ); + write_tagged_file( saveFile, SAVE_MAJOR_VERSION, 10, TAGTYPE_LEVEL ); fclose(saveFile); -#ifdef SHARED_FILES_CHMOD_PRIVATE - chmod(cha_fil, SHARED_FILES_CHMOD_PRIVATE); -#endif + DO_CHMOD_PRIVATE(cha_fil.c_str()); } // end save_level() + void save_game(bool leave_game) { - char charFile[kFileNameSize]; -#ifdef STASH_TRACKING - char stashFile[kFileNameSize + 4]; -#endif - char killFile[kFileNameSize + 4]; - char travelCacheFile[kFileNameSize + 4]; -#ifdef CLUA_BINDINGS - char luaFile[kFileNameSize + 4]; -#endif - -#ifdef SAVE_PACKAGE_CMD - char cmd_buff[1024]; - char name_buff[kFileNameSize]; - - snprintf( name_buff, sizeof(name_buff), - SAVE_DIR_PATH "%s%d", you.your_name, (int) getuid() ); - - snprintf( cmd_buff, sizeof(cmd_buff), - SAVE_PACKAGE_CMD, name_buff, name_buff ); #ifdef STASH_TRACKING - strcpy(stashFile, name_buff); -#endif -#ifdef CLUA_BINDINGS - strcpy(luaFile, name_buff); -#endif - strcpy(killFile, name_buff); - strcpy(travelCacheFile, name_buff); - snprintf( charFile, sizeof(charFile), - "%s.sav", name_buff ); - -#else - strncpy(charFile, you.your_name, kFileNameLen); - charFile[kFileNameLen] = 0; - -#ifdef STASH_TRACKING - strcpy(stashFile, charFile); -#endif -#ifdef CLUA_BINDINGS - strcpy(luaFile, charFile); -#endif - strcpy(killFile, charFile); - strcpy(travelCacheFile, charFile); - strcat(charFile, ".sav"); - -#ifdef DOS - strupr(charFile); -#ifdef STASH_TRACKING - strupr(stashFile); -#endif -#ifdef CLUA_BINDINGS - strupr(luaFile); -#endif - strupr(killFile); - strupr(travelCacheFile); -#endif -#endif - -#ifdef STASH_TRACKING - strcat(stashFile, ".st"); -#endif -#ifdef CLUA_BINDINGS - strcat(luaFile, ".lua"); -#endif - strcat(killFile, ".kil"); - strcat(travelCacheFile, ".tc"); - -#ifdef STASH_TRACKING - FILE *stashf = fopen(stashFile, "wb"); - if (stashf) - { + /* Stashes */ + std::string stashFile = get_savedir_filename( you.your_name, "", "st" ); + FILE *stashf = fopen(stashFile.c_str(), "wb"); + if (stashf) { stashes.save(stashf); fclose(stashf); - -#ifdef SHARED_FILES_CHMOD_PRIVATE - // change mode (unices) - chmod(stashFile, SHARED_FILES_CHMOD_PRIVATE); -#endif + DO_CHMOD_PRIVATE(stashFile.c_str()); } -#endif // STASH_TRACKING - -#ifdef CLUA_BINDINGS - clua.save(luaFile); -#ifdef SHARED_FILES_CHMOD_PRIVATE - // change mode; note that luaFile may not exist - chmod(luaFile, SHARED_FILES_CHMOD_PRIVATE); #endif -#endif // CLUA_BINDINGS - FILE *travelf = fopen(travelCacheFile, "wb"); - if (travelf) - { - travel_cache.save(travelf); - fclose(travelf); -#ifdef SHARED_FILES_CHMOD_PRIVATE - // change mode (unices) - chmod(travelCacheFile, SHARED_FILES_CHMOD_PRIVATE); +#ifdef CLUA_BINDINGS + /* lua */ + std::string luaFile = get_savedir_filename( you.your_name, "", "lua" ); + clua.save(luaFile.c_str()); + // note that luaFile may not exist + DO_CHMOD_PRIVATE(luaFile.c_str()); #endif - } - FILE *killf = fopen(killFile, "wb"); - if (killf) - { + /* kills */ + std::string killFile = get_savedir_filename( you.your_name, "", "kil" ); + FILE *killf = fopen(killFile.c_str(), "wb"); + if (killf) { you.kills.save(killf); fclose(killf); - -#ifdef SHARED_FILES_CHMOD_PRIVATE - // change mode (unices) - chmod(killFile, SHARED_FILES_CHMOD_PRIVATE); -#endif + DO_CHMOD_PRIVATE(killFile.c_str()); } - FILE *saveFile = fopen(charFile, "wb"); - - if (saveFile == NULL) - { - strcpy(info, "Unable to open \""); - strcat(info, charFile ); - strcat(info, "\" for writing!"); - perror(info); - end(-1); + /* travel cache */ + std::string travelCacheFile = get_savedir_filename(you.your_name,"","tc"); + FILE *travelf = fopen(travelCacheFile.c_str(), "wb"); + if (travelf) { + travel_cache.save(travelf); + fclose(travelf); + DO_CHMOD_PRIVATE(travelCacheFile.c_str()); + } + + /* notes */ + std::string notesFile = get_savedir_filename(you.your_name, "", "nts"); + FILE *notesf = fopen(notesFile.c_str(), "wb"); + if (notesf) { + save_notes(notesf); + fclose(notesf); + DO_CHMOD_PRIVATE(notesFile.c_str()); } - // 4.0 initial genesis of saved format - // 4.1 changes to make the item structure more sane - // 4.2 spell and ability tables - // 4.3 added you.magic_contamination (05/03/05) - // 4.4 added item origins - - write_tagged_file( saveFile, 4, 4, TAGTYPE_PLAYER ); + std::string charFile = get_savedir_filename(you.your_name, "", "sav"); + FILE *charf = fopen(charFile.c_str(), "wb"); + if (!charf) { + snprintf(info, INFO_SIZE, "Unable to open \"%s\" for writing!\n", + charFile.c_str()); + perror(info); + end(-1); + } - fclose(saveFile); + // 0.0 initial genesis of saved format + // 0.1 changes to make the item structure more sane + // 0.2 spell and ability tables + // 0.3 added you.magic_contamination (05/03/05) + // 0.4 added item origins + // 0.5 added num_gifts + // 0.6 inscriptions + write_tagged_file( charf, SAVE_MAJOR_VERSION, 6, TAGTYPE_PLAYER ); -#ifdef SHARED_FILES_CHMOD_PRIVATE - // change mode (unices) - chmod(charFile, SHARED_FILES_CHMOD_PRIVATE); -#endif + fclose(charf); + DO_CHMOD_PRIVATE(charFile.c_str()); // if just save, early out if (!leave_game) @@ -1131,21 +1246,19 @@ void save_game(bool leave_game) clrscr(); #ifdef SAVE_PACKAGE_CMD - if (system( cmd_buff ) != 0) - { - cprintf( EOL "Warning: Zip command (SAVE_PACKAGE_CMD) returned non-zero value!" EOL ); - } + std::string basename = get_savedir_filename(you.your_name, "", ""); + char cmd_buff[1024]; -#ifdef SHARED_FILES_CHMOD_PRIVATE - strcat( name_buff, PACKAGE_SUFFIX ); - // change mode (unices) - chmod( name_buff, SHARED_FILES_CHMOD_PRIVATE ); -#endif + snprintf( cmd_buff, sizeof(cmd_buff), + SAVE_PACKAGE_CMD, basename.c_str(), basename.c_str() ); + if (system( cmd_buff ) != 0) { + cprintf( EOL "Warning: Zip command (SAVE_PACKAGE_CMD) returned non-zero value!" EOL ); + } + DO_CHMOD_PRIVATE ( (basename + PACKAGE_SUFFIX).c_str() ); #endif cprintf( "See you soon, %s!" EOL , you.your_name ); - end(0); } // end save_game() @@ -1153,14 +1266,15 @@ void load_ghost(void) { char majorVersion; char minorVersion; - char cha_fil[kFileNameSize]; int imn; int i; - make_filename( cha_fil, "bones", you.your_level, you.where_are_you, - (you.level_type != LEVEL_DUNGEON), true ); + std::string cha_fil = make_filename("bones", you.your_level, + you.where_are_you, + (you.level_type != LEVEL_DUNGEON), + true ); - FILE *gfile = fopen(cha_fil, "rb"); + FILE *gfile = fopen(cha_fil.c_str(), "rb"); if (gfile == NULL) return; // no such ghost. @@ -1170,7 +1284,7 @@ void load_ghost(void) fclose(gfile); #if DEBUG_DIAGNOSTICS snprintf( info, INFO_SIZE, "Ghost file \"%s\" seems to be invalid.", - cha_fil); + cha_fil.c_str()); mpr( info, MSGCH_DIAGNOSTICS ); more(); #endif @@ -1184,8 +1298,8 @@ void load_ghost(void) { fclose(gfile); #if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, "Incomplete read of \"%s\".", cha_fil); - mpr( info, MSGCH_DIAGNOSTICS ); + mprf(MSGCH_DIAGNOSTICS, "Incomplete read of \"%s\".", + cha_fil.c_str() ); more(); #endif return; @@ -1198,7 +1312,7 @@ void load_ghost(void) #endif // remove bones file - ghosts are hardly permanent. - unlink(cha_fil); + unlink(cha_fil.c_str()); // translate ghost to monster and place. for (imn = 0; imn < MAX_MONSTERS - 10; imn++) @@ -1219,17 +1333,9 @@ void load_ghost(void) menv[imn].flags = 0; menv[imn].foe = MHITNOT; menv[imn].foe_memory = 0; - + menv[imn].colour = mons_class_colour(MONS_PLAYER_GHOST); menv[imn].number = 250; - - for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++) - { - if (ghost.values[i] != MS_NO_SPELL) - { - menv[imn].number = MST_GHOST; - break; - } - } + mons_load_spells(&menv[imn], MST_GHOST); for (i = 0; i < NUM_MONSTER_SLOTS; i++) menv[imn].inv[i] = NON_ITEM; @@ -1253,58 +1359,12 @@ void load_ghost(void) void restore_game(void) { - char char_f[kFileNameSize]; - char kill_f[kFileNameSize]; - char travel_f[kFileNameSize]; -#ifdef STASH_TRACKING - char stash_f[kFileNameSize]; -#endif - -#ifdef CLUA_BINDINGS - char lua_f[kFileNameSize]; -#endif - -#ifdef SAVE_DIR_PATH - snprintf( char_f, sizeof(char_f), - SAVE_DIR_PATH "%s%d", you.your_name, (int) getuid() ); -#else - strncpy(char_f, you.your_name, kFileNameLen); - char_f[kFileNameLen] = 0; -#endif - - strcpy(kill_f, char_f); - strcpy(travel_f, char_f); -#ifdef CLUA_BINDINGS - strcpy(lua_f, char_f); - strcat(lua_f, ".lua"); -#endif -#ifdef STASH_TRACKING - strcpy(stash_f, char_f); - strcat(stash_f, ".st"); -#endif - strcat(kill_f, ".kil"); - strcat(travel_f, ".tc"); - strcat(char_f, ".sav"); - -#ifdef DOS - strupr(char_f); -#ifdef STASH_TRACKING - strupr(stash_f); -#endif - strupr(kill_f); - strupr(travel_f); -#ifdef CLUA_BINDINGS - strupr(lua_f); -#endif -#endif - - FILE *restoreFile = fopen(char_f, "rb"); - - if (restoreFile == NULL) + std::string charFile = get_savedir_filename(you.your_name, "", "sav"); + FILE *charf = fopen(charFile.c_str(), "rb"); + if (!charf ) { - strcpy(info, "Unable to open \""); - strcat(info, char_f ); - strcat(info, "\" for reading!"); + snprintf(info, INFO_SIZE, "Unable to open %s for reading!\n", + charFile.c_str() ); perror(info); end(-1); } @@ -1312,49 +1372,58 @@ void restore_game(void) char majorVersion = 0; char minorVersion = 0; - if (!determine_version(restoreFile, majorVersion, minorVersion)) + if (!determine_version(charf, majorVersion, minorVersion)) { perror("\nSavefile appears to be invalid.\n"); end(-1); } - restore_version(restoreFile, majorVersion, minorVersion); + restore_version(charf, majorVersion, minorVersion); // sanity check - EOF - if (!feof(restoreFile)) + if (!feof(charf)) { - snprintf( info, INFO_SIZE, "\nIncomplete read of \"%s\" - aborting.\n", char_f); + snprintf( info, INFO_SIZE, "\nIncomplete read of \"%s\" - aborting.\n", + charFile.c_str()); perror(info); end(-1); } - fclose(restoreFile); + fclose(charf); #ifdef STASH_TRACKING - FILE *stashFile = fopen(stash_f, "rb"); - if (stashFile) - { - stashes.load(stashFile); - fclose(stashFile); + std::string stashFile = get_savedir_filename( you.your_name, "", "st" ); + FILE *stashf = fopen(stashFile.c_str(), "rb"); + if (stashf) { + stashes.load(stashf); + fclose(stashf); } #endif #ifdef CLUA_BINDINGS - clua.execfile( lua_f ); -#endif // CLUA_BINDINGS + std::string luaFile = get_savedir_filename( you.your_name, "", "lua" ); + clua.execfile( luaFile.c_str() ); +#endif - FILE *travelFile = fopen(travel_f, "rb"); - if (travelFile) - { - travel_cache.load(travelFile); - fclose(travelFile); + std::string killFile = get_savedir_filename( you.your_name, "", "kil" ); + FILE *killf = fopen(killFile.c_str(), "rb"); + if (killf) { + you.kills.load(killf); + fclose(killf); } - FILE *killFile = fopen(kill_f, "rb"); - if (killFile) - { - you.kills.load(killFile); - fclose(killFile); + std::string travelCacheFile = get_savedir_filename(you.your_name,"","tc"); + FILE *travelf = fopen(travelCacheFile.c_str(), "rb"); + if (travelf) { + travel_cache.load(travelf); + fclose(travelf); + } + + std::string notesFile = get_savedir_filename(you.your_name, "", "nts"); + FILE *notesf = fopen(notesFile.c_str(), "rb"); + if (notesf) { + load_notes(notesf); + fclose(notesf); } } @@ -1366,23 +1435,14 @@ static bool determine_version( FILE *restoreFile, if (read2(restoreFile, buf, 2) != 2) return false; // empty file? - // check for 3.30 - if (buf[0] == you.your_name[0] && buf[1] == you.your_name[1]) - { - majorVersion = 0; - minorVersion = 0; - rewind(restoreFile); - return true; - } - // otherwise, read version and validate. majorVersion = buf[0]; minorVersion = buf[1]; - if (majorVersion == 1 || majorVersion == 4) + if (majorVersion == SAVE_MAJOR_VERSION) return true; - return false; // if its not 1 or 4, no idea! + return false; // if its not 0, no idea } static void restore_version( FILE *restoreFile, @@ -1390,7 +1450,7 @@ static void restore_version( FILE *restoreFile, { // assuming the following check can be removed once we can read all // savefile versions. - if (majorVersion < 4) + if (majorVersion != SAVE_MAJOR_VERSION) { snprintf( info, INFO_SIZE, "\nSorry, this release cannot read a v%d.%d savefile.\n", majorVersion, minorVersion); @@ -1400,7 +1460,7 @@ static void restore_version( FILE *restoreFile, switch(majorVersion) { - case 4: + case SAVE_MAJOR_VERSION: restore_tagged_file(restoreFile, TAGTYPE_PLAYER, minorVersion); break; default: @@ -1423,6 +1483,8 @@ static void restore_tagged_file( FILE *restoreFile, int fileType, if (i == 0) // no tag! break; tags[i] = 0; // tag read + if (fileType == TAGTYPE_PLAYER_NAME) + break; } // go through and init missing tags @@ -1441,23 +1503,14 @@ static bool determine_level_version( FILE *levelFile, if (read2(levelFile, buf, 2) != 2) return false; // empty file? - // check for 3.30 -- simply started right in with player name. - if (isprint(buf[0]) && buf[0] > 4) // who knows? - { - majorVersion = 0; - minorVersion = 0; - rewind(levelFile); - return true; - } - // otherwise, read version and validate. majorVersion = buf[0]; minorVersion = buf[1]; - if (majorVersion == 1 || majorVersion == 4) + if (majorVersion == SAVE_MAJOR_VERSION) return true; - return false; // if its not 1 or 4, no idea! + return false; // if its not SAVE_MAJOR_VERSION, no idea } static void restore_level_version( FILE *levelFile, @@ -1465,7 +1518,7 @@ static void restore_level_version( FILE *levelFile, { // assuming the following check can be removed once we can read all // savefile versions. - if (majorVersion < 4) + if (majorVersion != SAVE_MAJOR_VERSION) { snprintf( info, INFO_SIZE, "\nSorry, this release cannot read a v%d.%d level file.\n", majorVersion, minorVersion); @@ -1475,7 +1528,7 @@ static void restore_level_version( FILE *levelFile, switch(majorVersion) { - case 4: + case SAVE_MAJOR_VERSION: restore_tagged_file(levelFile, TAGTYPE_LEVEL, minorVersion); break; default: @@ -1504,43 +1557,20 @@ static bool determine_ghost_version( FILE *ghostFile, majorVersion = buf[0]; minorVersion = buf[1]; - if (majorVersion == 4) + if (majorVersion == SAVE_MAJOR_VERSION) return true; - return false; // if its not 4, no idea! -} - -static void restore_old_ghost( FILE *ghostFile ) -{ - char buf[41]; - - read2(ghostFile, buf, 41); // 41 causes EOF. 40 will not. - - // translate - memcpy( ghost.name, buf, 20 ); - - for (int i = 0; i < 20; i++) - ghost.values[i] = static_cast< unsigned short >( buf[i+20] ); - - if (ghost.values[ GVAL_RES_FIRE ] >= 97) - ghost.values[ GVAL_RES_FIRE ] -= 100; - - if (ghost.values[ GVAL_RES_COLD ] >= 97) - ghost.values[ GVAL_RES_COLD ] -= 100; + return false; // if its not SAVE_MAJOR_VERSION, no idea! } static void restore_ghost_version( FILE *ghostFile, char majorVersion, char minorVersion ) { - // currently, we can read all known ghost versions. switch(majorVersion) { - case 4: + case SAVE_MAJOR_VERSION: restore_tagged_file(ghostFile, TAGTYPE_GHOST, minorVersion); break; - case 0: - restore_old_ghost(ghostFile); - break; default: break; } @@ -1548,16 +1578,17 @@ static void restore_ghost_version( FILE *ghostFile, void save_ghost( bool force ) { - char cha_fil[kFileNameSize]; const int wpn = you.equip[EQ_WEAPON]; if (!force && (you.your_level < 2 || you.is_undead)) return; - make_filename( cha_fil, "bones", you.your_level, you.where_are_you, - (you.level_type != LEVEL_DUNGEON), true ); + std::string cha_fil = make_filename( "bones", you.your_level, + you.where_are_you, + (you.level_type != LEVEL_DUNGEON), + true ); - FILE *gfile = fopen(cha_fil, "rb"); + FILE *gfile = fopen(cha_fil.c_str(), "rb"); // don't overwrite existing bones! if (gfile != NULL) @@ -1566,7 +1597,7 @@ void save_ghost( bool force ) return; } - memcpy(ghost.name, you.your_name, 20); + snprintf(ghost.name, sizeof ghost.name, "%s", you.your_name); ghost.values[ GVAL_MAX_HP ] = ((you.hp_max >= 150) ? 150 : you.hp_max); ghost.values[ GVAL_EV ] = player_evasion(); @@ -1628,20 +1659,20 @@ void save_ghost( bool force ) add_spells(ghost); - gfile = fopen(cha_fil, "wb"); + gfile = fopen(cha_fil.c_str(), "wb"); if (gfile == NULL) { - strcpy(info, "Error creating ghost file: "); - strcat(info, cha_fil); + snprintf(info, INFO_SIZE, "Error creating ghost file: %s", + cha_fil.c_str()); mpr(info); more(); return; } - // 4.0-4.3 old tagged savefile (values as unsigned char) - // 4.4 new tagged savefile (values as signed short) - write_tagged_file( gfile, 4, 4, TAGTYPE_GHOST ); + // 0.0-0.3 old tagged savefile (values as unsigned char) + // 0.4 new tagged savefile (values as signed short) + write_tagged_file( gfile, SAVE_MAJOR_VERSION, 4, TAGTYPE_GHOST ); fclose(gfile); @@ -1649,9 +1680,7 @@ void save_ghost( bool force ) mpr( "Saved ghost.", MSGCH_DIAGNOSTICS ); #endif -#ifdef SHARED_FILES_CHMOD_PUBLIC - chmod(cha_fil, SHARED_FILES_CHMOD_PUBLIC); -#endif + DO_CHMOD_PRIVATE(cha_fil.c_str()); } // end save_ghost() /* @@ -1793,10 +1822,12 @@ unsigned char translate_spell(unsigned char spel) case MEPHITIC_CLOUD: return ; */ case SPELL_VENOM_BOLT: return (MS_VENOM_BOLT); + case SPELL_POISON_ARROW: + return (MS_POISON_ARROW); case SPELL_TELEPORT_OTHER: return (MS_TELEPORT_OTHER); case SPELL_SUMMON_SMALL_MAMMAL: - return (MS_VAMPIRE_SUMMON); /* approximate */ + return (MS_SUMMON_SMALL_MAMMALS); case SPELL_BOLT_OF_DRAINING: return (MS_NEGATIVE_BOLT); case SPELL_LEHUDIBS_CRYSTAL_SPEAR: @@ -1807,6 +1838,8 @@ unsigned char translate_spell(unsigned char spel) return (MS_ORB_ENERGY); case SPELL_SUMMON_HORRIBLE_THINGS: return (MS_LEVEL_SUMMON); /* approximate */ + case SPELL_SHADOW_CREATURES: + return (MS_LEVEL_SUMMON); /* approximate */ case SPELL_ANIMATE_DEAD: return (MS_ANIMATE_DEAD); case SPELL_PAIN: @@ -1861,7 +1894,7 @@ void generate_random_demon(void) char st_p[ITEMNAME_SIZE]; - make_name(random2(250), random2(250), random2(250), 3, st_p); + make_name(random_int(), false, st_p); strcpy(ghost.name, st_p); // hp - could be defined below (as could ev, AC etc). Oh well, too late: @@ -1944,7 +1977,7 @@ void generate_random_demon(void) menv[rdem].evasion = ghost.values[ GVAL_EV ]; menv[rdem].speed = (one_chance_in(3) ? 10 : 6 + roll_dice(2, 9)); menv[rdem].speed_increment = 70; - menv[rdem].number = random_colour(); // demon's colour + menv[rdem].colour = random_colour(); // demon's colour for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++) ghost.values[i] = SPELL_NO_SPELL; @@ -2079,8 +2112,6 @@ void generate_random_demon(void) // Largest string we'll save #define STR_CAP 1000 -using std::string; - void writeShort(FILE *file, short s) { char data[2]; @@ -2112,7 +2143,7 @@ unsigned char readByte(FILE *file) return byte; } -void writeString(FILE* file, const string &s) +void writeString(FILE* file, const std::string &s) { int length = s.length(); if (length > STR_CAP) length = STR_CAP; @@ -2120,14 +2151,14 @@ void writeString(FILE* file, const string &s) write2(file, s.c_str(), length); } -string readString(FILE *file) +std::string readString(FILE *file) { char buf[STR_CAP + 1]; short length = readShort(file); if (length) read2(file, buf, length); - buf[length] = '\0'; - return string(buf); + buf[length] = 0; + return std::string(buf); } void writeLong(FILE* file, long num) diff --git a/crawl-ref/source/files.h b/crawl-ref/source/files.h index a2d357cac8..b7fd81a5fd 100644 --- a/crawl-ref/source/files.h +++ b/crawl-ref/source/files.h @@ -3,6 +3,8 @@ * Summary: Functions used to save and load levels/games. * Written by: Linley Henzell and Alexey Guzeev * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -12,9 +14,11 @@ #ifndef FILES_H #define FILES_H +#include "externs.h" #include "FixAry.h" #include #include +#include // referenced in files - newgame - ouch - overmap: #define MAX_LEVELS 50 @@ -25,20 +29,16 @@ // referenced in files - newgame - ouch: extern FixedArray tmp_file_pairs; -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: acr - misc - * *********************************************************************** */ -#if 0 -void load( unsigned char stair_taken, bool moving_level, - bool was_a_labyrinth, char old_level, bool want_followers, - bool is_new_game, char where_were_you2 ); -#endif +std::string datafile_path(const std::string &basename); + +void check_savedir(std::string &dir); bool travel_load_map( char branch, int absdepth ); +std::vector find_saved_characters(); + std::string get_savedir_filename(const char *pre, const char *suf, - const char *ext); + const char *ext, bool suppress_uid = false); std::string get_prefs_filename(); @@ -47,7 +47,7 @@ void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth, // last updated 12may2000 {dlb} /* *********************************************************************** - * called from: acr - libmac - misc + * called from: acr - misc * *********************************************************************** */ void save_game(bool leave_game); @@ -69,9 +69,8 @@ void save_ghost( bool force = false ); /* *********************************************************************** * called from: files hiscores * *********************************************************************** */ -void make_filename( char *buf, const char *prefix, int level, int where, - bool isLabyrinth, bool isGhost ); - +std::string make_filename( const char *prefix, int level, int where, + bool isLabyrinth, bool isGhost ); void writeShort(FILE *file, short s); diff --git a/crawl-ref/source/food.cc b/crawl-ref/source/food.cc index db95c533b8..f124cf3ae4 100644 --- a/crawl-ref/source/food.cc +++ b/crawl-ref/source/food.cc @@ -3,6 +3,8 @@ * Summary: Functions for eating and butchering. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 5/20/99 BWR Added CRAWL_PIZZA. @@ -30,6 +32,7 @@ #include "invent.h" #include "items.h" #include "itemname.h" +#include "itemprop.h" #include "item_use.h" #include "it_use2.h" #include "macro.h" @@ -41,7 +44,6 @@ #include "skills2.h" #include "spells2.h" #include "stuff.h" -#include "wpn-misc.h" static int determine_chunk_effect(int which_chunk_type, bool rotten_chunk); static void eat_chunk( int chunk_effect ); @@ -138,7 +140,7 @@ void weapon_switch( int targ ) // code is a mess... this whole function's purpose was to // isolate this hack until there's a proper way to do things. -- bwr if (you.equip[EQ_WEAPON] != -1) - unwield_item(you.equip[EQ_WEAPON]); + unwield_item(you.equip[EQ_WEAPON], false); you.equip[EQ_WEAPON] = targ; @@ -147,14 +149,49 @@ void weapon_switch( int targ ) wield_effects( targ, false ); } +// look for a butchering implement, prompting user if no obvious +// options exist. Returns whether a weapon was switched. +static bool find_butchering_implement() { + + // look for a butchering implement in your pack + for (int i = 0; i < ENDOFPACK; ++i) { + if (is_valid_item( you.inv[i] ) + && can_cut_meat( you.inv[i] ) + && you.inv[i].base_type == OBJ_WEAPONS + && item_known_uncursed(you.inv[i]) + && item_ident( you.inv[i], ISFLAG_KNOW_TYPE ) + && get_weapon_brand(you.inv[i]) != SPWPN_DISTORTION + && can_wield( &you.inv[i] )) + { + mpr("Switching to a butchering implement."); + wield_weapon( true, i, false ); + + // Account for the weapon switch...we're doing this here + // since the above switch may reveal information about the + // weapon (curse status, ego type). So even if the + // character fails to or decides not to butcher past this + // point, they have achieved something and there should be + // a cost. + start_delay( DELAY_UNINTERRUPTIBLE, 1 ); + return true; + } + } + + // if we didn't swap above, then we still can't cut...let's call + // wield_weapon() in the "prompt the user" way... + + // prompt for new weapon + int old_weapon = you.equip[EQ_WEAPON]; + mpr( "What would you like to use?", MSGCH_PROMPT ); + wield_weapon( false ); + + // let's see if the user did something... + return (you.equip[EQ_WEAPON] != old_weapon); +} + bool butchery(void) { char str_pass[ ITEMNAME_SIZE ]; - int items_here = 0; - int o = igrd[you.x_pos][you.y_pos]; - int k = 0; - int item_got; - unsigned char keyin; bool can_butcher = false; bool wpn_switch = false; @@ -162,326 +199,138 @@ bool butchery(void) int old_weapon = you.equip[EQ_WEAPON]; bool barehand_butcher = (you.equip[ EQ_GLOVES ] == -1) - && (you.species == SP_TROLL - || you.species == SP_GHOUL - || you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS - || you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON - || you.mutation[MUT_CLAWS]); - - + && (you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS || + you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON || + (you.attribute[ATTR_TRANSFORMATION] == TRAN_NONE && + (you.species == SP_TROLL || + you.species == SP_GHOUL || + you.mutation[MUT_CLAWS]))); + + can_butcher = barehand_butcher || + (you.equip[EQ_WEAPON] != -1 && + can_cut_meat(you.inv[you.equip[EQ_WEAPON]])); + if (igrd[you.x_pos][you.y_pos] == NON_ITEM) { mpr("There isn't anything here!"); return (false); } - if (player_is_levitating() && !wearing_amulet(AMU_CONTROLLED_FLIGHT)) + if (!Options.easy_butcher && !can_butcher) { - mpr("You can't reach the floor from up here."); + mpr("Maybe you should try using a sharper implement."); return (false); } - if (barehand_butcher) - can_butcher = true; - else - { - if (you.equip[EQ_WEAPON] != -1) - { - can_butcher = can_cut_meat( you.inv[you.equip[EQ_WEAPON]].base_type, - you.inv[you.equip[EQ_WEAPON]].sub_type ); - } - - // Should probably check for cursed-weapons, bare hands and - // non-weapons in hand here, but wield_weapon will be used for - // this swap and it will do all that (although the player might - // be annoyed with the excess prompt). - if (Options.easy_butcher && !can_butcher) - { - //mv: check for berserk first - if (you.berserker) - { - mpr ("You are too berserk to search for a butchering knife!"); - return (false); - } - - // We'll now proceed to look through the entire inventory for - // choppers/slicers. We'll skip special weapons because - // wielding/unwielding a foo of distortion would be disastrous. - for (int i = 0; i < ENDOFPACK; ++i) - { - if (is_valid_item( you.inv[i] ) - && can_cut_meat( you.inv[i].base_type, - you.inv[i].sub_type ) - && you.inv[i].base_type == OBJ_WEAPONS - && item_known_uncursed(you.inv[i]) - && item_ident( you.inv[i], ISFLAG_KNOW_TYPE ) - && get_weapon_brand(you.inv[i]) - != SPWPN_DISTORTION - && can_wield( you.inv[i] )) - { - mpr("Switching to a butchering implement."); - wpn_switch = true; - wield_weapon( true, i, false ); - break; - } - } - - // if we didn't swap above, then we still can't cut... let's - // call wield_weapon() in the "prompt the user" way... - if (!wpn_switch) - { - // prompt for new weapon - mpr( "What would you like to use?", MSGCH_PROMPT ); - wield_weapon( false ); - - // let's see if the user did something... - if (you.equip[EQ_WEAPON] != old_weapon) - wpn_switch = true; - } - } - - // weapon might have changed (to bare hands as well), we'll - // update the can_butcher status accordingly (note: if we could - // butcher with our bare hands we wouldn't be here) -- bwr - if (wpn_switch && you.equip[EQ_WEAPON] != -1) - { - can_butcher = can_cut_meat( you.inv[you.equip[EQ_WEAPON]].base_type, - you.inv[you.equip[EQ_WEAPON]].sub_type ); - } - } - - // Account for the weapon switch above if it happened... we're - // doing this here since the above switch may reveal information - // about the weapon (curse status, ego type). So even if the - // character fails to or decides not to butcher past this point, - // they have achieved something and there should be a cost. - if (wpn_switch) - start_delay( DELAY_UNINTERUPTABLE, 1, old_weapon ); - - // check to see if the new implement is cursed - if so, set a - // flag indicating this. If a player actually butchers anything, - // this flag can be checked before switching back. - int wpn = you.equip[EQ_WEAPON]; - - if (wpn != -1 - && you.inv[wpn].base_type == OBJ_WEAPONS - && item_cursed( you.inv[wpn] )) - { - new_cursed = true; - } - - // Final checks and clue-giving... - if (!barehand_butcher && you.equip[EQ_WEAPON] == -1) - { - if (you.equip[ EQ_GLOVES ] == -1) - mpr("What, with your bare hands?"); - else - mpr("You can't use your claws with your gloves on!"); - - // Switching back to avoid possible bug where player can use - // this to switch weapons in zero time. - if (wpn_switch) - weapon_switch( old_weapon ); - - return (false); - } - else if (!can_butcher) + if (player_is_levitating() && !wearing_amulet(AMU_CONTROLLED_FLIGHT)) { - mpr("Maybe you should try using a sharper implement."); - - // Switching back to avoid possible bug where player can use - // this to switch weapons in zero time. - if (wpn_switch && !new_cursed) - weapon_switch( old_weapon ); - + mpr("You can't reach the floor from up here."); return (false); } - // No turning back at this point, we better be qualified. - ASSERT( can_butcher ); - - int last_item = NON_ITEM; - - int objl = igrd[you.x_pos][you.y_pos]; - int hrg = 0; - int counter = 0; - - while (objl != NON_ITEM) - { - counter++; - - last_item = objl; - - hrg = mitm[objl].link; - objl = hrg; - items_here++; - - if (counter > 1000) - { - error_message_to_player(); - - if (wpn_switch && !new_cursed) - weapon_switch( old_weapon ); - - return (false); - } + // It makes more sense that you first find out if there's anything + // to butcher, *then* decide to actually butcher it. + // The old code did it the other way. + if ( !can_butcher && you.berserker ) { + mpr ("You are too berserk to search for a butchering knife!"); + return (false); } - if (items_here == 1 - && (mitm[igrd[you.x_pos][you.y_pos]].base_type == OBJ_CORPSES && - mitm[igrd[you.x_pos][you.y_pos]].sub_type == CORPSE_BODY)) - { - strcpy(info, "Butcher "); - it_name(igrd[you.x_pos][you.y_pos], DESC_NOCAP_A, str_pass); - strcat(info, str_pass); - strcat(info, "\?"); - mpr(info, MSGCH_PROMPT); - - unsigned char keyin = getch(); - - if (keyin == 0) - { - getch(); - keyin = 0; - } - - if (keyin != 'y' && keyin != 'Y') - { - if (wpn_switch && !new_cursed) - weapon_switch( old_weapon ); - - return (false); - } - - int item_got = igrd[you.x_pos][you.y_pos]; - - last_item = NON_ITEM; - - if (barehand_butcher) - mpr("You start tearing the corpse apart."); - else - mpr("You start hacking away."); - - if (you.duration[DUR_PRAYER] - && (you.religion == GOD_OKAWARU - || you.religion == GOD_MAKHLEB || you.religion == GOD_TROG)) - { - offer_corpse(item_got); - destroy_item(item_got); - // XXX: need an extra turn here for weapon swapping? - } - else - { - int work_req = 3 - mitm[item_got].plus2; - if (work_req < 0) - work_req = 0; - - start_delay( DELAY_BUTCHER, work_req, item_got ); + int objl; + + for (objl = igrd[you.x_pos][you.y_pos]; objl != NON_ITEM; + objl = mitm[objl].link) { + + if ( (mitm[objl].base_type != OBJ_CORPSES) || + (mitm[objl].sub_type != CORPSE_BODY) ) + continue; + + // offer the possibility of butchering + it_name(objl, DESC_NOCAP_A, str_pass); + snprintf(info, INFO_SIZE, "Butcher %s?", str_pass); + int answer = yesnoquit( info, true, 'n', false ); + if ( answer == -1 ) + break; + if ( answer == 0 ) + continue; + + if ( Options.easy_butcher && !can_butcher ) { + + // try to find a butchering implement + wpn_switch = find_butchering_implement(); + const int wpn = you.equip[EQ_WEAPON]; + if ( wpn_switch ) { + new_cursed = + (wpn != -1) && + (you.inv[wpn].base_type == OBJ_WEAPONS) && + item_cursed( you.inv[wpn]); + } + + // note that barehanded butchery would not reach this + // stage, so if wpn == -1 the user selected '-' when + // switching weapons + + if (!wpn_switch || wpn == -1 || !can_cut_meat(you.inv[wpn])) { + + // still can't butcher. Early out + if ( wpn == -1 ) { + if (you.equip[EQ_GLOVES] == -1) + mpr("What, with your bare hands?"); + else + mpr("Your gloves aren't that sharp!"); + } + else + mpr("Maybe you should try using a sharper implement."); + + if ( !new_cursed && wpn_switch ) + start_delay( DELAY_WEAPON_SWAP, 1, old_weapon ); + + return false; + } + + // switched to a good butchering knife + can_butcher = true; + } + + if ( can_butcher ) { + + // we actually butcher now + if ( barehand_butcher ) + mpr("You start tearing the corpse apart."); + else + mpr("You start hacking away."); + + if (you.duration[DUR_PRAYER] && + (you.religion == GOD_OKAWARU || you.religion == GOD_MAKHLEB || + you.religion == GOD_TROG)) { + offer_corpse(objl); + destroy_item(objl); + } + else { + int work_req = 3 - mitm[objl].plus2; + if (work_req < 0) + work_req = 0; + + start_delay(DELAY_BUTCHER, work_req, objl, mitm[objl].special); + } } - // cue up switching weapon back - if (wpn_switch && !new_cursed) + // switch weapon back + if (!new_cursed && wpn_switch) start_delay( DELAY_WEAPON_SWAP, 1, old_weapon ); - you.turn_is_over = 1; - - return (true); - - } // end "if items_here == 1" - else if (items_here > 1) - { - last_item = NON_ITEM; - o = igrd[you.x_pos][you.y_pos]; - - for (k = 0; k < items_here; k++) - { - if (mitm[o].base_type != OBJ_CORPSES - || mitm[o].sub_type != CORPSE_BODY) - { - goto out_of_eating; - } - - strcpy(info, "Butcher "); - it_name(o, DESC_NOCAP_A, str_pass); - strcat(info, str_pass); - strcat(info, "\?"); - mpr(info, MSGCH_PROMPT); - - keyin = getch(); - if (keyin == 0) - { - getch(); - keyin = 0; - } - - if (keyin == 'q') - { - if (wpn_switch && !new_cursed) - weapon_switch( old_weapon ); - - return (false); - } - - if (keyin == 'y') - { - item_got = o; - - if (barehand_butcher) - mpr("You start tearing the corpse apart."); - else - mpr("You start hacking away."); - - if (you.duration[DUR_PRAYER] - && (you.religion == GOD_OKAWARU - || you.religion == GOD_MAKHLEB - || you.religion == GOD_TROG)) - { - offer_corpse(item_got); - destroy_item(item_got); - // XXX: need an extra turn here for weapon swapping? - } - else - { - int work_req = 3 - mitm[item_got].plus2; - if (work_req < 0) - work_req = 0; - - start_delay( DELAY_BUTCHER, work_req, item_got ); - } - - if (wpn_switch && !new_cursed) - { - // weapon_switch( old_weapon ); - // need to count the swap delay in this case - start_delay( DELAY_WEAPON_SWAP, 1, old_weapon ); - } - - you.turn_is_over = 1; - return (true); - } - - out_of_eating: - - if (is_valid_item( mitm[o] )) - last_item = o; - - hrg = mitm[o].link; - o = hrg; - - if (o == NON_ITEM) - break; + you.turn_is_over = true; - if (items_here == 0) - break; - } // end "for k" loop + return true; } mpr("There isn't anything to dissect here."); - if (wpn_switch && !new_cursed) + if (!new_cursed && wpn_switch) { // should never happen weapon_switch( old_weapon ); - - return (false); + } + + return false; } // end butchery() #ifdef CLUA_BINDINGS @@ -541,7 +390,12 @@ bool prompt_eat_from_inventory(void) } int which_inventory_slot = - prompt_invent_item( "Eat which item?", OBJ_FOOD ); + prompt_invent_item( + "Eat which item?", + MT_INVSELECT, + OBJ_FOOD, + true, true, true, 0, NULL, + OPER_EAT ); if (which_inventory_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -565,7 +419,7 @@ bool prompt_eat_from_inventory(void) eat_from_inventory(which_inventory_slot); burden_change(); - you.turn_is_over = 1; + you.turn_is_over = true; return (true); } @@ -663,7 +517,7 @@ static bool food_change(bool suppress_message) } // end food_change() -// food_increment is positive for eating, negative for hungering +// food_increment is positive for eating, negative for hungering static void describe_food_change(int food_increment) { int magnitude = (food_increment > 0)?food_increment:(-food_increment); @@ -693,7 +547,7 @@ void eat_from_inventory(int which_inventory_slot) // this is a bit easier to read... most compilers should // handle this the same -- bwr const int mons_type = you.inv[ which_inventory_slot ].plus; - const int chunk_type = mons_corpse_thingy( mons_type ); + const int chunk_type = mons_corpse_effect( mons_type ); const bool rotten = (you.inv[which_inventory_slot].special < 100); eat_chunk( determine_chunk_effect( chunk_type, rotten ) ); @@ -711,7 +565,7 @@ void eat_floor_item(int item_link) { if (mitm[item_link].sub_type == FOOD_CHUNK) { - const int chunk_type = mons_corpse_thingy( mitm[item_link].plus ); + const int chunk_type = mons_corpse_effect( mitm[item_link].plus ); const bool rotten = (mitm[item_link].special < 100); eat_chunk( determine_chunk_effect( chunk_type, rotten ) ); @@ -721,7 +575,7 @@ void eat_floor_item(int item_link) eating( mitm[item_link].base_type, mitm[item_link].sub_type ); } - you.turn_is_over = 1; + you.turn_is_over = true; dec_mitm_item_quantity( item_link, 1 ); } @@ -767,6 +621,67 @@ bool eat_from_floor(void) return (false); } +static const char *chunk_flavour_phrase(bool likes_chunks) +{ + const char *phrase = + likes_chunks? "tastes great." : "tastes terrible."; + + const int gourmand = you.duration[DUR_GOURMAND]; + if (gourmand >= GOURMAND_MAX) + phrase = + one_chance_in(8)? "tastes like chicken!" + : "tastes great."; + else if (gourmand > GOURMAND_MAX * 75 / 100) + phrase = "tastes very good."; + else if (gourmand > GOURMAND_MAX * 50 / 100) + phrase = "tastes good."; + else if (gourmand > GOURMAND_MAX * 25 / 100) + phrase = "is not very appetising."; + + return (phrase); +} + +void chunk_nutrition_message(int nutrition) +{ + int perc_nutrition = nutrition * 100 / CHUNK_BASE_NUTRITION; + if (perc_nutrition < 15) + mpr("That was extremely unsatisfying."); + else if (perc_nutrition < 35) + mpr("That was not very filling."); +} + +static int chunk_nutrition(bool likes_chunks) +{ + int nutrition = CHUNK_BASE_NUTRITION; + if (likes_chunks || you.hunger_state < HS_SATIATED) + return (nutrition); + + const int gourmand = + wearing_amulet(AMU_THE_GOURMAND)? + you.duration[DUR_GOURMAND] + : 0; + + int effective_nutrition = + nutrition * (gourmand + GOURMAND_NUTRITION_BASE) + / (GOURMAND_MAX + GOURMAND_NUTRITION_BASE); + +#ifdef DEBUG_DIAGNOSTICS + const int epercent = effective_nutrition * 100 / nutrition; + mprf(MSGCH_DIAGNOSTICS, + "Gourmand factor: %d, chunk base: %d, effective: %d, %%: %d", + gourmand, + nutrition, + effective_nutrition, + epercent); +#endif + + return (effective_nutrition); +} + +static void say_chunk_flavour(bool likes_chunks) +{ + mprf("This raw flesh %s", chunk_flavour_phrase(likes_chunks)); +} // never called directly - chunk_effect values must pass // through food::determine_chunk_effect() first {dlb}: @@ -781,7 +696,7 @@ static void eat_chunk( int chunk_effect ) { ghoul_eat_flesh( chunk_effect ); start_delay( DELAY_EAT, 2 ); - lessen_hunger( 1000, true ); + lessen_hunger( CHUNK_BASE_NUTRITION, true ); } else { @@ -815,16 +730,14 @@ static void eat_chunk( int chunk_effect ) // note that this is the only case that takes time and forces redraw case CE_CLEAN: - strcpy(info, "This raw flesh "); - - strcat(info, (likes_chunks) ? "tastes good." - : "is not very appetising."); - mpr(info); - - start_delay( DELAY_EAT, 2 ); - lessen_hunger( 1000, true ); + { + say_chunk_flavour(likes_chunks); + const int nutrition = chunk_nutrition(likes_chunks); + start_delay( DELAY_EAT, 2, nutrition ); + lessen_hunger( nutrition, true ); break; } + } } return; @@ -1364,12 +1277,15 @@ static int determine_chunk_effect(int which_chunk_type, bool rotten_chunk) { if (you.species == SP_GHOUL) { + // [dshaligram] Leaving rotting chunk effect intact for ghouls. if (this_chunk_effect == CE_CLEAN) this_chunk_effect = CE_ROTTEN; } else { - if (this_chunk_effect == CE_ROTTEN) + // [dshaligram] New AotG behaviour - contaminated chunks become + // clean, but rotten chunks remain rotten. + if (this_chunk_effect == CE_CONTAMINATED) this_chunk_effect = CE_CLEAN; } } diff --git a/crawl-ref/source/food.h b/crawl-ref/source/food.h index 1c8e5f5a74..764f577ee0 100644 --- a/crawl-ref/source/food.h +++ b/crawl-ref/source/food.h @@ -3,6 +3,8 @@ * Summary: Functions for eating and butchering. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -65,4 +67,6 @@ void eat_from_inventory(int which_inventory_slot); bool prompt_eat_from_inventory(void); +void chunk_nutrition_message(int nutrition); + #endif diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc index ab84678410..bb15621d36 100644 --- a/crawl-ref/source/hiscores.cc +++ b/crawl-ref/source/hiscores.cc @@ -3,6 +3,8 @@ * Summary: deal with reading and writing of highscore file * Written by: Gordon Lipford * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> 16feb2001 gdl Created @@ -35,9 +37,14 @@ #include "hiscores.h" #include "itemname.h" +#include "itemprop.h" +#include "items.h" +#include "libutil.h" +#include "misc.h" #include "mon-util.h" #include "player.h" #include "religion.h" +#include "shopping.h" #include "stuff.h" #include "tags.h" #include "view.h" @@ -51,7 +58,7 @@ #endif // enough memory allocated to snarf in the scorefile entries -static struct scorefile_entry hs_list[SCORE_FILE_ENTRIES]; +static scorefile_entry hs_list[SCORE_FILE_ENTRIES]; // hackish: scorefile position of newest entry. Will be highlit during // highscore printing (always -1 when run from command line). @@ -59,18 +66,19 @@ static int newest_entry = -1; static FILE *hs_open(const char *mode); static void hs_close(FILE *handle, const char *mode); -static bool hs_read(FILE *scores, struct scorefile_entry &dest); -static void hs_parse_numeric(char *inbuf, struct scorefile_entry &dest); -static void hs_parse_string(char *inbuf, struct scorefile_entry &dest); -static void hs_copy(struct scorefile_entry &dest, struct scorefile_entry &src); -static void hs_write(FILE *scores, struct scorefile_entry &entry); -static void hs_nextstring(char *&inbuf, char *dest); +static bool hs_read(FILE *scores, scorefile_entry &dest); +static void hs_parse_numeric(char *inbuf, scorefile_entry &dest); +static void hs_parse_string(char *inbuf, scorefile_entry &dest); +static void hs_write(FILE *scores, scorefile_entry &entry); +static void hs_nextstring(char *&inbuf, char *dest, size_t bufsize); static int hs_nextint(char *&inbuf); static long hs_nextlong(char *&inbuf); // functions dealing with old scorefile entries -static void hs_parse_generic_1(char *&inbuf, char *outbuf, const char *stopvalues); -static void hs_parse_generic_2(char *&inbuf, char *outbuf, const char *continuevalues); +static void hs_parse_generic_1(char *&inbuf, char *outbuf, size_t outsz, + const char *stopvalues); +static void hs_parse_generic_2(char *&inbuf, char *outbuf, size_t outsz, + const char *continuevalues); static void hs_stripblanks(char *buf); static void hs_search_death(char *inbuf, struct scorefile_entry &se); static void hs_search_where(char *inbuf, struct scorefile_entry &se); @@ -81,7 +89,7 @@ static bool lock_file_handle( FILE *handle, int type ); static bool unlock_file_handle( FILE *handle ); #endif // USE_FILE_LOCKING -void hiscores_new_entry( struct scorefile_entry &ne ) +void hiscores_new_entry( const scorefile_entry &ne ) { FILE *scores; int i, total_entries; @@ -90,6 +98,9 @@ void hiscores_new_entry( struct scorefile_entry &ne ) // open highscore file (reading) -- note that NULL is not fatal! scores = hs_open("r"); + for (i = 0; i < SCORE_FILE_ENTRIES; ++i) + hs_list[i].reset(); + // read highscore file, inserting new entry at appropriate point, for (i = 0; i < SCORE_FILE_ENTRIES; i++) { @@ -105,23 +116,23 @@ void hiscores_new_entry( struct scorefile_entry &ne ) // Fixed a nasty overflow bug here -- Sharp if (i+1 < SCORE_FILE_ENTRIES) { - hs_copy(hs_list[i+1], hs_list[i]); - hs_copy(hs_list[i], ne); + hs_list[i + 1] = hs_list[i]; + hs_list[i] = ne; i++; } else { - // copy new entry to current position - hs_copy(hs_list[i], ne); + // copy new entry to current position + hs_list[i] = ne; } } } - // special case: lowest score, with room + // special case: lowest score, with room if (!inserted && i < SCORE_FILE_ENTRIES) { newest_entry = i; inserted = true; // copy new entry - hs_copy(hs_list[i], ne); + hs_list[i] = ne; i++; } @@ -199,16 +210,16 @@ void hiscores_print_list( int display_count, int format ) // print position (tracked implicitly by order score file) snprintf( info, INFO_SIZE, "%3d.", i + 1 ); if (use_printf) - printf(info); + printf("%s", info); else - cprintf(info); + cprintf("%s", info); // format the entry if (format == SCORE_TERSE) { hiscores_format_single( info, hs_list[i] ); // truncate if we want short format - info[75] = '\0'; + info[75] = 0; } else { @@ -219,9 +230,9 @@ void hiscores_print_list( int display_count, int format ) // print entry strcat(info, EOL); if(use_printf) - printf(info); + printf("%s", info); else - cprintf(info); + cprintf("%s", info); if (i == newest_entry && !use_printf) textcolor(LIGHTGREY); @@ -233,7 +244,7 @@ static const char *const range_type_verb( const char *const aux ) { if (strncmp( aux, "Shot ", 5 ) == 0) // launched return ("shot"); - else if (aux[0] == '\0' // unknown + else if (aux[0] == 0 // unknown || strncmp( aux, "Hit ", 4 ) == 0 // thrown || strncmp( aux, "volley ", 7 ) == 0) // manticore spikes { @@ -243,1585 +254,1782 @@ static const char *const range_type_verb( const char *const aux ) return ("blasted"); // spells, wands } -void hiscores_format_single(char *buf, struct scorefile_entry &se) +void hiscores_format_single(char *buf, const scorefile_entry &se) { - char scratch[100]; + std::string line = se.hiscore_line(scorefile_entry::DDV_ONELINE); + strncpy(buf, line.c_str(), INFO_SIZE); + buf[INFO_SIZE - 1] = 0; +} - // Now that we have a long format, I'm starting to make this - // more terse, in hopes that it will better fit. -- bwr +static bool hiscore_same_day( time_t t1, time_t t2 ) +{ + struct tm *d1 = localtime( &t1 ); + const int year = d1->tm_year; + const int mon = d1->tm_mon; + const int day = d1->tm_mday; - // race_class_name overrides race & class - if (se.race_class_name[0] == '\0') - { - snprintf( scratch, sizeof(scratch), "%s%s", - get_species_abbrev( se.race ), get_class_abbrev( se.cls ) ); - } - else - { - strcpy( scratch, se.race_class_name ); - } + struct tm *d2 = localtime( &t2 ); - se.name[10]='\0'; - sprintf( buf, "%8ld %-10s %s-%02d%s", se.points, se.name, - scratch, se.lvl, (se.wiz_mode == 1) ? "W" : "" ); + return (d2->tm_mday == day && d2->tm_mon == mon && d2->tm_year == year); +} - // get monster type & number, if applicable - int mon_type = se.death_source; - int mon_number = se.mon_num; +static void hiscore_date_string( time_t time, char buff[INFO_SIZE] ) +{ + struct tm *date = localtime( &time ); - // remember -- we have 36 characters (not including initial space): - switch (se.death_type) - { - case KILLED_BY_MONSTER: - strcat( buf, " slain by " ); + const char *mons[12] = { "Jan", "Feb", "Mar", "Apr", "May", "June", + "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; - // if death_source_name is non-null, override lookup (names might have - // changed!) - if (se.death_source_name[0] != '\0') - strcat( buf, se.death_source_name ); - else - strcat( buf, monam( mon_number, mon_type, true, DESC_PLAIN ) ); + snprintf( buff, INFO_SIZE, "%s %d, %d", mons[date->tm_mon], + date->tm_mday, date->tm_year + 1900 ); +} - break; +static std::string hiscore_newline_string() +{ + return (EOL " "); +} - case KILLED_BY_POISON: - //if (dam == -9999) strcat(buf, "an overload of "); - strcat( buf, " succumbed to poison" ); - break; +void hiscores_format_single_long( char *buf, const scorefile_entry &se, + bool verbose ) +{ + std::string line = + se.hiscore_line( + verbose? + scorefile_entry::DDV_VERBOSE + : scorefile_entry::DDV_NORMAL ); + strncpy(buf, line.c_str(), HIGHSCORE_SIZE); + buf[HIGHSCORE_SIZE - 1] = 0; +} - case KILLED_BY_CLOUD: - if (se.auxkilldata[0] == '\0') - strcat( buf, " engulfed by a cloud" ); - else - { - const int len = strlen( se.auxkilldata ); +// -------------------------------------------------------------------------- +// BEGIN private functions +// -------------------------------------------------------------------------- - // Squeeze out "a cloud of" if required. -- bwr - snprintf( scratch, sizeof(scratch), " engulfed by %s%s", - (len < 15) ? "a cloud of " : "", - se.auxkilldata ); +// first, some file locking stuff for multiuser crawl +#ifdef USE_FILE_LOCKING - strcat( buf, scratch ); - } - break; +static bool lock_file_handle( FILE *handle, int type ) +{ + struct flock lock; + int status; - case KILLED_BY_BEAM: - // keeping this short to leave room for the deep elf spellcasters: - snprintf( scratch, sizeof(scratch), " %s by ", - range_type_verb( se.auxkilldata ) ); - strcat( buf, scratch ); - - // if death_source_name is non-null, override this - if (se.death_source_name[0] != '\0') - strcat( buf, se.death_source_name ); - else - strcat( buf, monam( mon_number, mon_type, true, DESC_PLAIN ) ); - break; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + lock.l_type = type; -/* - case KILLED_BY_DEATHS_DOOR: - // death's door running out - NOTE: This is no longer fatal - strcat(buf, " ran out of time"); - break; -*/ +#ifdef USE_BLOCKING_LOCK - case KILLED_BY_LAVA: - if (se.race == SP_MUMMY) - strcat( buf, " turned to ash by lava" ); - else - strcat( buf, " took a swim in lava" ); - break; + status = fcntl( fileno( handle ), F_SETLKW, &lock ); - case KILLED_BY_WATER: - if (se.race == SP_MUMMY) - strcat( buf, " soaked and fell apart" ); - else - strcat( buf, " drowned" ); - break; +#else - // these three are probably only possible if you wear a ring - // of >= +3 ability, get drained to 3, then take it off, or have a very - // low abil and wear a -ve ring. or, as of 2.7x, mutations can cause this - // Don't forget decks of cards (they have some nasty code for this) -- bwr - case KILLED_BY_STUPIDITY: - strcat( buf, " died of stupidity" ); - break; + for (int i = 0; i < 30; i++) + { + status = fcntl( fileno( handle ), F_SETLK, &lock ); - case KILLED_BY_WEAKNESS: - strcat( buf, " became too weak to continue" ); - break; + // success + if (status == 0) + break; - case KILLED_BY_CLUMSINESS: - strcat( buf, " slipped on a banana peel" ); - break; + // known failure + if (status == -1 && (errno != EACCES && errno != EAGAIN)) + break; - case KILLED_BY_TRAP: - snprintf( scratch, sizeof(scratch), " triggered a%s trap", - (se.auxkilldata[0] != '\0') ? se.auxkilldata : "" ); - strcat( buf, scratch ); - break; + perror( "Problems locking file... retrying..." ); + delay( 1000 ); + } - case KILLED_BY_LEAVING: - strcat( buf, " got out of the dungeon alive" ); - break; +#endif - case KILLED_BY_WINNING: - strcat( buf, " escaped with the Orb!" ); - break; + return (status == 0); +} - case KILLED_BY_QUITTING: - strcat( buf, " quit the game" ); - break; +static bool unlock_file_handle( FILE *handle ) +{ + struct flock lock; + int status; - case KILLED_BY_DRAINING: - strcat( buf, " drained of all life" ); - break; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + lock.l_type = F_UNLCK; - case KILLED_BY_STARVATION: - strcat( buf, " starved to death" ); - break; +#ifdef USE_BLOCKING_LOCK - case KILLED_BY_FREEZING: - strcat( buf, " froze to death" ); - break; + status = fcntl( fileno( handle ), F_SETLKW, &lock ); - case KILLED_BY_BURNING: // only sticky flame - strcat( buf, " burnt to a crisp" ); - break; +#else - case KILLED_BY_WILD_MAGIC: - if (se.auxkilldata[0] == '\0') - strcat( buf, " killed by wild magic" ); - else - { - const bool need_by = (strncmp( se.auxkilldata, "by ", 3 ) == 0); - const int len = strlen( se.auxkilldata ); + for (int i = 0; i < 30; i++) + { + status = fcntl( fileno( handle ), F_SETLK, &lock ); - // Squeeze out "killed" if we're getting a bit long. -- bwr - snprintf( scratch, sizeof(scratch), " %s%s%s", - (len + 3 * (need_by) < 30) ? "killed" : "", - (need_by) ? "by " : "", - se.auxkilldata ); + // success + if (status == 0) + break; - strcat( buf, scratch ); - } - break; + // known failure + if (status == -1 && (errno != EACCES && errno != EAGAIN)) + break; - case KILLED_BY_XOM: // only used for old Xom kills - strcat( buf, " killed for Xom's enjoyment" ); - break; + perror( "Problems unlocking file... retrying..." ); + delay( 1000 ); + } - case KILLED_BY_STATUE: - strcat( buf, " killed by a statue" ); - break; +#endif - case KILLED_BY_ROTTING: - strcat( buf, " rotted away" ); - break; + return (status == 0); +} - case KILLED_BY_TARGETTING: - strcat( buf, " killed by bad targeting" ); - break; +#endif - case KILLED_BY_SPORE: - strcat( buf, " killed by an exploding spore" ); - break; - case KILLED_BY_TSO_SMITING: - strcat( buf, " smote by The Shining One" ); - break; - case KILLED_BY_PETRIFICATION: - strcat( buf, " turned to stone" ); - break; +FILE *hs_open( const char *mode ) +{ + std::string scores = Options.save_dir + "scores"; + FILE *handle = fopen(scores.c_str(), mode); +#ifdef SHARED_FILES_CHMOD_PUBLIC + chmod(scores.c_str(), SHARED_FILES_CHMOD_PUBLIC); +#endif - case KILLED_BY_SHUGGOTH: - strcat( buf, " eviscerated by a hatching shuggoth" ); - break; +#ifdef USE_FILE_LOCKING + int locktype = F_RDLCK; + if (stricmp(mode, "w") == 0) + locktype = F_WRLCK; - case KILLED_BY_SOMETHING: - strcat( buf, " died" ); - break; + if (handle && !lock_file_handle( handle, locktype )) + { + perror( "Could not lock scorefile... " ); + fclose( handle ); + handle = NULL; + } +#endif + return handle; +} - case KILLED_BY_FALLING_DOWN_STAIRS: - strcat( buf, " fell down a flight of stairs" ); - break; +void hs_close( FILE *handle, const char *mode ) +{ + UNUSED( mode ); - case KILLED_BY_ACID: - strcat( buf, " splashed by acid" ); - break; + if (handle == NULL) + return; - default: - strcat( buf, " nibbled to death by software bugs" ); - break; - } // end switch +#ifdef USE_FILE_LOCKING + unlock_file_handle( handle ); +#endif - if (se.death_type != KILLED_BY_LEAVING && se.death_type != KILLED_BY_WINNING) - { - if (se.level_type == LEVEL_ABYSS) - { - strcat(buf, " (Abyss)"); - return; - } - else if (se.level_type == LEVEL_PANDEMONIUM) - { - strcat(buf, " (Pan)"); - return; - } - else if (se.level_type == LEVEL_LABYRINTH) - { - strcat(buf, " (Lab)"); - return; - } - else if (se.branch == BRANCH_VESTIBULE_OF_HELL) - { - strcat(buf, " (Hell)"); // Gate? Vest? - return; - } - else if (se.branch == BRANCH_HALL_OF_BLADES) - { - strcat(buf, " (Blade)"); - return; - } - else if (se.branch == BRANCH_ECUMENICAL_TEMPLE) - { - strcat(buf, " (Temple)"); - return; - } + // actually close + fclose(handle); - snprintf( scratch, sizeof(scratch), " (%s%d)", - (se.branch == BRANCH_DIS) ? "Dis " : - (se.branch == BRANCH_GEHENNA) ? "Geh " : - (se.branch == BRANCH_COCYTUS) ? "Coc " : - (se.branch == BRANCH_TARTARUS) ? "Tar " : - (se.branch == BRANCH_ORCISH_MINES) ? "Orc " : - (se.branch == BRANCH_HIVE) ? "Hive " : - (se.branch == BRANCH_LAIR) ? "Lair " : - (se.branch == BRANCH_SLIME_PITS) ? "Slime " : - (se.branch == BRANCH_VAULTS) ? "Vault " : - (se.branch == BRANCH_CRYPT) ? "Crypt " : - (se.branch == BRANCH_HALL_OF_ZOT) ? "Zot " : - (se.branch == BRANCH_SNAKE_PIT) ? "Snake " : - (se.branch == BRANCH_ELVEN_HALLS) ? "Elf " : - (se.branch == BRANCH_TOMB) ? "Tomb " : - (se.branch == BRANCH_SWAMP) ? "Swamp " : "DLv ", - se.dlvl ); - - strcat( buf, scratch ); - } // endif - killed by winning - - return; +#ifdef SHARED_FILES_CHMOD_PUBLIC + if (stricmp(mode, "w") == 0) + { + std::string scores = Options.save_dir + "scores"; + chmod(scores.c_str(), SHARED_FILES_CHMOD_PUBLIC); + } +#endif } -static bool hiscore_same_day( time_t t1, time_t t2 ) +bool hs_read( FILE *scores, scorefile_entry &dest ) { - struct tm *d1 = localtime( &t1 ); - const int year = d1->tm_year; - const int mon = d1->tm_mon; - const int day = d1->tm_mday; + char inbuf[200]; + int c = EOF; - struct tm *d2 = localtime( &t2 ); + memset(inbuf, 0, sizeof inbuf); + dest.reset(); - return (d2->tm_mday == day && d2->tm_mon == mon && d2->tm_year == year); -} + // get a character.. + if (scores != NULL) + c = fgetc(scores); -static void hiscore_date_string( time_t time, char buff[INFO_SIZE] ) -{ - struct tm *date = localtime( &time ); + // check for NULL scores file or EOF + if (scores == NULL || c == EOF) + return false; - const char *mons[12] = { "Jan", "Feb", "Mar", "Apr", "May", "June", - "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; + // get a line - this is tricky. "Lines" come in three flavors: + // 1) old-style lines which were 80 character blocks + // 2) 4.0 pr1 through pr7 versions which were newline terminated + // 3) 4.0 pr8 and onwards which are 'current' ASCII format, and + // may exceed 80 characters! - snprintf( buff, INFO_SIZE, "%s %d, %d", mons[date->tm_mon], - date->tm_mday, date->tm_year + 1900 ); -} + // put 'c' in first spot + inbuf[0] = c; -static void hiscore_newline( char *buf, int &line_count ) -{ - strncat( buf, EOL " ", HIGHSCORE_SIZE ); - line_count++; + if (fgets(&inbuf[1], (c==':') ? (sizeof(inbuf) - 2) : 81, scores) == NULL) + return false; + + // check type; lines starting with a colon are new-style scores. + if (c == ':') + hs_parse_numeric(inbuf, dest); + else + hs_parse_string(inbuf, dest); + + return true; } -int hiscores_format_single_long( char *buf, struct scorefile_entry &se, - bool verbose ) +static void hs_nextstring(char *&inbuf, char *dest, size_t destsize) { - char scratch[INFO_SIZE]; - int line_count = 1; + ASSERT(destsize > 0); - // race_class_name could/used to override race & class - // strcpy(scratch, se.race_class_name); + char *p = dest; - // Please excuse the following bit of mess in the name of flavour ;) - if (verbose) + if (*inbuf == 0) { - strncpy( scratch, skill_title( se.best_skill, se.best_skill_lvl, - se.race, se.str, se.dex, se.god ), - INFO_SIZE ); + *p = 0; + return; + } - snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s (level %d", - se.points, se.name, scratch, se.lvl ); - - } - else - { - snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s %s (level %d", - se.points, se.name, species_name(se.race, se.lvl), - get_class_name(se.cls), se.lvl ); - } + // assume we're on a ':' + if (*inbuf == ':') + inbuf++; - if (se.final_max_max_hp > 0) // as the other two may be negative - { - snprintf( scratch, INFO_SIZE, ", %d/%d", se.final_hp, se.final_max_hp ); - strncat( buf, scratch, HIGHSCORE_SIZE ); + while (*inbuf && *inbuf != ':' && (p - dest) < (int) destsize - 1) + *p++ = *inbuf++; - if (se.final_max_hp < se.final_max_max_hp) - { - snprintf( scratch, INFO_SIZE, " (%d)", se.final_max_max_hp ); - strncat( buf, scratch, HIGHSCORE_SIZE ); - } + // If we ran out of buffer, discard the rest of the field. + while (*inbuf && *inbuf != ':') + inbuf++; - strncat( buf, " HPs", HIGHSCORE_SIZE ); - } + *p = 0; +} - strncat( buf, ((se.wiz_mode) ? ") *WIZ*" : ")"), HIGHSCORE_SIZE ); - hiscore_newline( buf, line_count ); +static int hs_nextint(char *&inbuf) +{ + char num[20]; + hs_nextstring(inbuf, num, sizeof num); - if (verbose) - { - const char *const race = species_name( se.race, se.lvl ); + return (num[0] == 0 ? 0 : atoi(num)); +} - snprintf( scratch, INFO_SIZE, "Began as a%s %s %s", - is_vowel(race[0]) ? "n" : "", race, get_class_name(se.cls) ); - strncat( buf, scratch, HIGHSCORE_SIZE ); +static long hs_nextlong(char *&inbuf) +{ + char num[20]; + hs_nextstring(inbuf, num, sizeof num); - if (se.birth_time > 0) - { - strncat( buf, " on ", HIGHSCORE_SIZE ); - hiscore_date_string( se.birth_time, scratch ); - strncat( buf, scratch, HIGHSCORE_SIZE ); - } + return (num[0] == 0 ? 0 : atol(num)); +} - strncat( buf, "." , HIGHSCORE_SIZE ); - hiscore_newline( buf, line_count ); +static int val_char( char digit ) +{ + return (digit - '0'); +} - if (se.race != SP_DEMIGOD && se.god != -1) - { - if (se.god == GOD_XOM) - { - snprintf( scratch, INFO_SIZE, "Was a %sPlaything of Xom.", - (se.lvl >= 20) ? "Favourite " : "" ); +static time_t hs_nextdate(char *&inbuf) +{ + char buff[20]; + struct tm date; - strncat( buf, scratch, HIGHSCORE_SIZE ); - hiscore_newline( buf, line_count ); - } - else if (se.god != GOD_NO_GOD) - { - // Not exactly the same as the religon screen, but - // good enough to fill this slot for now. - snprintf( scratch, INFO_SIZE, "Was %s of %s%s", - (se.piety > 160) ? "the Champion" : - (se.piety >= 120) ? "a High Priest" : - (se.piety >= 100) ? "an Elder" : - (se.piety >= 75) ? "a Priest" : - (se.piety >= 50) ? "a Believer" : - (se.piety >= 30) ? "a Follower" - : "an Initiate", - god_name( se.god ), - (se.penance > 0) ? " (penitent)." : "." ); + hs_nextstring(inbuf, buff, sizeof buff); - strncat( buf, scratch, HIGHSCORE_SIZE ); - hiscore_newline( buf, line_count ); - } - } - } + if (strlen( buff ) < 15) + return (static_cast(0)); - // get monster type & number, if applicable - int mon_type = se.death_source; - int mon_number = se.mon_num; + date.tm_year = val_char( buff[0] ) * 1000 + val_char( buff[1] ) * 100 + + val_char( buff[2] ) * 10 + val_char( buff[3] ) - 1900; - bool needs_beam_cause_line = false; - bool needs_called_by_monster_line = false; - bool needs_damage = false; + date.tm_mon = val_char( buff[4] ) * 10 + val_char( buff[5] ); + date.tm_mday = val_char( buff[6] ) * 10 + val_char( buff[7] ); + date.tm_hour = val_char( buff[8] ) * 10 + val_char( buff[9] ); + date.tm_min = val_char( buff[10] ) * 10 + val_char( buff[11] ); + date.tm_sec = val_char( buff[12] ) * 10 + val_char( buff[13] ); + date.tm_isdst = (buff[14] == 'D'); - switch (se.death_type) - { - case KILLED_BY_MONSTER: - // GDL: here's an example of using final_hp. Verbiage could be better. - // bwr: changed "blasted" since this is for melee - snprintf( scratch, INFO_SIZE, "%s %s", - (se.final_hp > -6) ? "Slain by" : - (se.final_hp > -14) ? "Mangled by" : - (se.final_hp > -22) ? "Demolished by" - : "Annihilated by", + return (mktime( &date )); +} - (se.death_source_name[0] != '\0') - ? se.death_source_name - : monam( mon_number, mon_type, true, DESC_PLAIN ) ); +static void hs_parse_numeric(char *inbuf, struct scorefile_entry &se) +{ + se.version = hs_nextint(inbuf); + se.release = hs_nextint(inbuf); - strncat( buf, scratch, HIGHSCORE_SIZE ); + // this would be a good point to check for version numbers and branch + // appropriately - // put the damage on the weapon line if there is one - if (se.auxkilldata[0] == '\0') - needs_damage = true; - break; + // acceptable versions are 0 (converted from old hiscore format) and 4 + if (se.version != 0 && se.version != 4) + return; - case KILLED_BY_POISON: - //if (dam == -9999) strcat(buf, "an overload of "); - strcat( buf, "Succumbed to poison" ); - break; + se.points = hs_nextlong(inbuf); - case KILLED_BY_CLOUD: - if (se.auxkilldata[0] == '\0') - strcat( buf, "Engulfed by a cloud" ); - else - { - snprintf( scratch, sizeof(scratch), "Engulfed by a cloud of %s", - se.auxkilldata ); - strcat( buf, scratch ); - } - needs_damage = true; - break; + hs_nextstring(inbuf, se.name, sizeof se.name); - case KILLED_BY_BEAM: - if (isupper( se.auxkilldata[0] )) // already made (ie shot arrows) - { - strcat( buf, se.auxkilldata ); - needs_damage = true; - } - else if (verbose && strncmp( se.auxkilldata, "by ", 3 ) == 0) - { - // "by" is used for priest attacks where the effect is indirect - // in verbose format we have another line for the monster - needs_called_by_monster_line = true; - snprintf( scratch, sizeof(scratch), "Killed %s", se.auxkilldata ); - strncat( buf, scratch, HIGHSCORE_SIZE ); - } - else - { - // Note: This is also used for the "by" cases in non-verbose - // mode since listing the monster is more imporatant. - strcat( buf, "Killed from afar by " ); + se.uid = hs_nextlong(inbuf); + se.race = hs_nextint(inbuf); + se.cls = hs_nextint(inbuf); - // if death_source_name is non-null, override this - if (se.death_source_name[0] != '\0') - strcat(buf, se.death_source_name); - else - strcat(buf, monam( mon_number, mon_type, true, DESC_PLAIN )); + hs_nextstring(inbuf, se.race_class_name, sizeof se.race_class_name); - if (se.auxkilldata[0] != '\0') - needs_beam_cause_line = true; - } - break; + se.lvl = hs_nextint(inbuf); + se.best_skill = hs_nextint(inbuf); + se.best_skill_lvl = hs_nextint(inbuf); + se.death_type = hs_nextint(inbuf); + se.death_source = hs_nextint(inbuf); + se.mon_num = hs_nextint(inbuf); - case KILLED_BY_LAVA: - if (se.race == SP_MUMMY) - strcat( buf, "Turned to ash by lava" ); - else - strcat( buf, "Took a swim in molten lava" ); - break; + hs_nextstring(inbuf, se.death_source_name, sizeof se.death_source_name); - case KILLED_BY_WATER: - if (se.race == SP_MUMMY) - strcat( buf, "Soaked and fell apart" ); - else - strcat( buf, "Drowned" ); - break; + // To try and keep the scorefile backwards compatible, + // we'll branch on version > 4.0 to read the auxkilldata + // text field. + if (se.version == 4 && se.release >= 1) + hs_nextstring( inbuf, se.auxkilldata, sizeof se.auxkilldata ); + else + se.auxkilldata[0] = 0; - case KILLED_BY_STUPIDITY: - strcat( buf, "Forgot to breathe" ); - break; + se.dlvl = hs_nextint(inbuf); + se.level_type = hs_nextint(inbuf); + se.branch = hs_nextint(inbuf); - case KILLED_BY_WEAKNESS: - strcat( buf, "Collapsed under their own weight" ); - break; + // Trying to fix some bugs that have been around since at + // least pr19, if not longer. From now on, dlvl should + // be calculated on death and need no further modification. + if (se.version < 4 || se.release < 2) + { + if (se.level_type == LEVEL_DUNGEON) + { + if (se.branch == BRANCH_MAIN_DUNGEON) + se.dlvl += 1; + else if (se.branch < BRANCH_ORCISH_MINES) // ie the hells + se.dlvl -= 1; + } + } - case KILLED_BY_CLUMSINESS: - strcat( buf, "Slipped on a banana peel" ); - break; + se.final_hp = hs_nextint(inbuf); + if (se.version == 4 && se.release >= 2) + { + se.final_max_hp = hs_nextint(inbuf); + se.final_max_max_hp = hs_nextint(inbuf); + se.damage = hs_nextint(inbuf); + se.str = hs_nextint(inbuf); + se.intel = hs_nextint(inbuf); + se.dex = hs_nextint(inbuf); + se.god = hs_nextint(inbuf); + se.piety = hs_nextint(inbuf); + se.penance = hs_nextint(inbuf); + } + else + { + se.final_max_hp = -1; + se.final_max_max_hp = -1; + se.damage = -1; + se.str = -1; + se.intel = -1; + se.dex = -1; + se.god = -1; + se.piety = -1; + se.penance = -1; + } - case KILLED_BY_TRAP: - snprintf( scratch, sizeof(scratch), "Killed by triggering a%s trap", - (se.auxkilldata[0] != '\0') ? se.auxkilldata : "" ); - strcat( buf, scratch ); - needs_damage = true; - break; + se.wiz_mode = hs_nextint(inbuf); - case KILLED_BY_LEAVING: - if (se.num_runes > 0) - strcat( buf, "Got out of the dungeon" ); - else - strcat( buf, "Got out of the dungeon alive!" ); - break; + se.birth_time = hs_nextdate(inbuf); + se.death_time = hs_nextdate(inbuf); - case KILLED_BY_WINNING: - strcat( buf, "Escaped with the Orb" ); - if (se.num_runes < 1) - strcat( buf, "!" ); - break; + if (se.version == 4 && se.release >= 2) + { + se.real_time = hs_nextint(inbuf); + se.num_turns = hs_nextint(inbuf); + } + else + { + se.real_time = -1; + se.num_turns = -1; + } - case KILLED_BY_QUITTING: - strcat( buf, "Quit the game" ); - break; + se.num_diff_runes = hs_nextint(inbuf); + se.num_runes = hs_nextint(inbuf); +} - case KILLED_BY_DRAINING: - strcat( buf, "Was drained of all life" ); - break; +static void hs_write( FILE *scores, scorefile_entry &se ) +{ + char buff[80]; // should be more than enough for date stamps - case KILLED_BY_STARVATION: - strcat( buf, "Starved to death" ); - break; + se.version = 4; + se.release = 2; - case KILLED_BY_FREEZING: // refrigeration spell - strcat( buf, "Froze to death" ); - needs_damage = true; - break; + fprintf( scores, ":%d:%d:%ld:%s:%ld:%d:%d:%s:%d:%d:%d", + se.version, se.release, se.points, se.name, + se.uid, se.race, se.cls, se.race_class_name, se.lvl, + se.best_skill, se.best_skill_lvl ); - case KILLED_BY_BURNING: // sticky flame - strcat( buf, "Burnt to a crisp" ); - needs_damage = true; - break; + // XXX: need damage + fprintf( scores, ":%d:%d:%d:%s:%s:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + se.death_type, se.death_source, se.mon_num, + se.death_source_name, se.auxkilldata, + se.dlvl, se.level_type, se.branch, + se.final_hp, se.final_max_hp, se.final_max_max_hp, se.damage, + se.str, se.intel, se.dex, + se.god, se.piety, se.penance, se.wiz_mode ); - case KILLED_BY_WILD_MAGIC: - if (se.auxkilldata[0] == '\0') - strcat( buf, "Killed by wild magic" ); - else - { - // A lot of sources for this case... some have "by" already. - snprintf( scratch, sizeof(scratch), "Killed %s%s", - (strncmp( se.auxkilldata, "by ", 3 ) != 0) ? "by " : "", - se.auxkilldata ); + make_date_string( se.birth_time, buff ); + fprintf( scores, ":%s", buff ); - strcat( buf, scratch ); - } + make_date_string( se.death_time, buff ); + fprintf( scores, ":%s", buff ); - needs_damage = true; - break; + fprintf( scores, ":%ld:%ld:%d:%d:\n", + se.real_time, se.num_turns, se.num_diff_runes, se.num_runes ); +} +// ------------------------------------------------------------------------- +// functions dealing with old-style scorefile entries. +// ------------------------------------------------------------------------- - case KILLED_BY_XOM: // only used for old Xom kills - strcat( buf, "It was good that Xom killed them" ); - needs_damage = true; - break; +static void hs_parse_string(char *inbuf, struct scorefile_entry &se) +{ + /* old entries are of the following format (Brent introduced some + spacing at one point, we have to take this into account): - case KILLED_BY_STATUE: - strcat( buf, "Killed by a statue" ); - needs_damage = true; - break; + // Actually, I believe it might have been Brian who added the spaces, + // I was quite happy with the condensed version, given the 80 column + // restriction. -- bwr - case KILLED_BY_ROTTING: - strcat( buf, "Rotted away" ); - break; +6263 BoBo - DSD10 Wiz, killed by an acid blob on L1 of the Slime Pits. +5877 Aldus-DGM10, killed by a lethal dose of poison on L10. +5419 Yarf - Koa10, killed by a warg on L1 of the Mines. - case KILLED_BY_TARGETTING: - strcat( buf, "Killed themselves with bad targeting" ); - needs_damage = true; - break; + 1. All numerics up to the first non-numeric are the score + 2. All non '-' characters are the name. Strip spaces. + 3. All alphabetics up to the first numeric are race/class + 4. All numerics up to the comma are the clevel + 5. From the comma, search for known fixed substrings and + translate to death_type. Leave death source = 0 for old + scores, and just copy in the monster name. + 6. Look for the branch type (again, substring search for + fixed strings) and level. - case KILLED_BY_SPORE: - strcat( buf, "Killed by an exploding spore" ); - needs_damage = true; - break; + Very ugly and time consuming. - case KILLED_BY_TSO_SMITING: - strcat( buf, "Smote by The Shining One" ); - needs_damage = true; - break; + */ - case KILLED_BY_PETRIFICATION: - strcat( buf, "Turned to stone" ); - break; + char scratch[80]; + const int inlen = strlen(inbuf); + char *start = inbuf; - case KILLED_BY_SHUGGOTH: - strcat( buf, "Eviscerated by a hatching shuggoth" ); - needs_damage = true; - break; + // 1. get score + hs_parse_generic_2(inbuf, scratch, sizeof scratch, "0123456789"); - case KILLED_BY_SOMETHING: - strcat( buf, "Died" ); - break; + se.version = 0; // version # of converted score + se.release = 0; + se.points = atoi(scratch); - case KILLED_BY_FALLING_DOWN_STAIRS: - strcat( buf, "Fell down a flight of stairs" ); - needs_damage = true; - break; + // 2. get name + hs_parse_generic_1(inbuf, scratch, sizeof scratch, "-"); + hs_stripblanks(scratch); + strncpy(se.name, scratch, sizeof se.name); + se.name[ sizeof(se.name) - 1 ] = 0; - case KILLED_BY_ACID: - strcat( buf, "Splashed by acid" ); - needs_damage = true; - break; + // 3. get race, class + // skip '-' + if (++inbuf - start >= inlen) + return; - default: - strcat( buf, "Nibbled to death by software bugs" ); - break; - } // end switch + hs_parse_generic_1(inbuf, scratch, sizeof scratch, "0123456789"); + hs_stripblanks(scratch); + strncpy(se.race_class_name, scratch, sizeof se.race_class_name); + se.race_class_name[ sizeof(se.race_class_name) - 1 ] = 0; + se.race = 0; + se.cls = 0; - // TODO: Eventually, get rid of "..." for cases where the text fits. - if (verbose) - { - bool done_damage = false; // paranoia + // 4. get clevel + hs_parse_generic_2(inbuf, scratch, sizeof scratch, "0123456789"); + se.lvl = atoi(scratch); - if (needs_damage && se.damage > 0) - { - snprintf( scratch, INFO_SIZE, " (%d damage)", se.damage ); - strncat( buf, scratch, HIGHSCORE_SIZE ); - needs_damage = false; - done_damage = true; - } + // 4a. get wizard mode + hs_parse_generic_1(inbuf, scratch, sizeof scratch, ","); + if (strstr(scratch, "Wiz") != NULL) + se.wiz_mode = 1; + else + se.wiz_mode = 0; - if ((se.death_type == KILLED_BY_LEAVING - || se.death_type == KILLED_BY_WINNING) - && se.num_runes > 0) - { - hiscore_newline( buf, line_count ); + // Skip comma + if (++inbuf - start >= inlen) + return; - snprintf( scratch, INFO_SIZE, "... %s %d rune%s", - (se.death_type == KILLED_BY_WINNING) ? "and" : "with", - se.num_runes, (se.num_runes > 1) ? "s" : "" ); - strncat( buf, scratch, HIGHSCORE_SIZE ); + // 5. get death type + hs_search_death(inbuf, se); - if (se.num_diff_runes > 1) - { - snprintf( scratch, INFO_SIZE, " (of %d types)", - se.num_diff_runes ); - strncat( buf, scratch, HIGHSCORE_SIZE ); - } + // 6. get branch, level + hs_search_where(inbuf, se); - if (se.death_time > 0 - && !hiscore_same_day( se.birth_time, se.death_time )) - { - strcat( buf, " on " ); - hiscore_date_string( se.death_time, scratch ); - strcat( buf, scratch ); - } + // set things that can't be picked out of old scorefile entries + se.uid = 0; + se.best_skill = 0; + se.best_skill_lvl = 0; + se.final_hp = 0; + se.final_max_hp = -1; + se.final_max_max_hp = -1; + se.damage = -1; + se.str = -1; + se.intel = -1; + se.dex = -1; + se.god = -1; + se.piety = -1; + se.penance = -1; + se.birth_time = 0; + se.death_time = 0; + se.real_time = -1; + se.num_turns = -1; + se.num_runes = 0; + se.num_diff_runes = 0; + se.auxkilldata[0] = 0; +} - strcat( buf, "!" ); - hiscore_newline( buf, line_count ); - } - else if (se.death_type != KILLED_BY_QUITTING) - { - hiscore_newline( buf, line_count ); +static void hs_parse_generic_1(char *&inbuf, char *outbuf, size_t outsz, const char *stopvalues) +{ + ASSERT(outsz > 0); - if (se.death_type == KILLED_BY_MONSTER && se.auxkilldata[0]) - { - snprintf(scratch, INFO_SIZE, "... wielding %s", se.auxkilldata); - strncat(buf, scratch, HIGHSCORE_SIZE); - needs_damage = true; - } - else if (needs_beam_cause_line) - { - strcat( buf, (is_vowel( se.auxkilldata[0] )) ? "... with an " - : "... with a " ); - strcat( buf, se.auxkilldata ); - needs_damage = true; - } - else if (needs_called_by_monster_line) - { - snprintf( scratch, sizeof(scratch), "... called down by %s", - se.death_source_name ); - strncat( buf, scratch, HIGHSCORE_SIZE ); - needs_damage = true; - } + char *p = outbuf; - if (needs_damage && !done_damage) - { - if (se.damage > 0) - { - snprintf( scratch, INFO_SIZE, " (%d damage)", se.damage ); - strncat( buf, scratch, HIGHSCORE_SIZE ); - hiscore_newline( buf, line_count ); - } - } - } - } - - if (se.death_type == KILLED_BY_LEAVING - || se.death_type == KILLED_BY_WINNING) + if (!*inbuf) { - // TODO: strcat "after reaching level %d"; for LEAVING - if (!verbose) - { - if (se.num_runes > 0) - strcat( buf, "!" ); - - hiscore_newline( buf, line_count ); - } + *p = 0; + return; } - else - { - if (verbose && se.death_type != KILLED_BY_QUITTING) - strcat( buf, "..." ); - - if (se.level_type == LEVEL_ABYSS) - strcat( buf, " in the Abyss" ); - else if (se.level_type == LEVEL_PANDEMONIUM) - strcat( buf, " in Pandemonium" ); - else if (se.level_type == LEVEL_LABYRINTH) - strcat( buf, " in a labyrinth" ); - else - { - switch (se.branch) - { - case BRANCH_ECUMENICAL_TEMPLE: - strcat( buf, " in the Ecumenical Temple" ); - break; - case BRANCH_HALL_OF_BLADES: - strcat( buf, " in the Hall of Blades" ); - break; - case BRANCH_VESTIBULE_OF_HELL: - strcat( buf, " in the Vestibule" ); - break; - - case BRANCH_DIS: - strcat( buf, " on Dis" ); - break; - case BRANCH_GEHENNA: - strcat( buf, " on Gehenna" ); - break; - case BRANCH_COCYTUS: - strcat( buf, " on Cocytus" ); - break; - case BRANCH_TARTARUS: - strcat( buf, " on Tartarus" ); - break; - case BRANCH_ORCISH_MINES: - strcat( buf, " on Orcish Mines" ); - break; - case BRANCH_HIVE: - strcat( buf, " on Hive" ); - break; - case BRANCH_LAIR: - strcat( buf, " on Lair" ); - break; - case BRANCH_SLIME_PITS: - strcat( buf, " on Slime Pits" ); - break; - case BRANCH_VAULTS: - strcat( buf, " on Vault" ); - break; - case BRANCH_CRYPT: - strcat( buf, " on Crypt" ); - break; - case BRANCH_HALL_OF_ZOT: - strcat( buf, " on Hall of Zot" ); - break; - case BRANCH_SNAKE_PIT: - strcat( buf, " on Snake Pit" ); - break; - case BRANCH_ELVEN_HALLS: - strcat( buf, " on Elven Halls" ); - break; - case BRANCH_TOMB: - strcat( buf, " on Tomb" ); - break; - case BRANCH_SWAMP: - strcat( buf, " on Swamp" ); - break; - case BRANCH_MAIN_DUNGEON: - strcat( buf, " on Dungeon" ); - break; - } - - if (se.branch != BRANCH_VESTIBULE_OF_HELL - && se.branch != BRANCH_ECUMENICAL_TEMPLE - && se.branch != BRANCH_HALL_OF_BLADES) - { - snprintf( scratch, sizeof(scratch), " Level %d", se.dlvl ); - strcat( buf, scratch ); - } - } - - if (verbose && se.death_time - && !hiscore_same_day( se.birth_time, se.death_time )) - { - strcat( buf, " on " ); - hiscore_date_string( se.death_time, scratch ); - strcat( buf, scratch ); - } - - strcat( buf, "." ); - hiscore_newline( buf, line_count ); - } // endif - killed by winning - - if (verbose) - { - if (se.real_time > 0) - { - char username[80] = "The"; - char tmp[80]; - -#ifdef MULTIUSER - if (se.uid > 0) - { - struct passwd *pw_entry = getpwuid( se.uid ); - strncpy( username, pw_entry->pw_name, sizeof(username) ); - strncat( username, "'s", sizeof(username) ); - username[0] = toupper( username[0] ); - } -#endif - - make_time_string( se.real_time, tmp, sizeof(tmp) ); - snprintf( scratch, INFO_SIZE, "%s game lasted %s (%ld turns).", - username, tmp, se.num_turns ); + while (strchr(stopvalues, *inbuf) == NULL + && *inbuf != 0 + && (p - outbuf) < (int) outsz - 1) + *p++ = *inbuf++; - strncat( buf, scratch, HIGHSCORE_SIZE ); - hiscore_newline( buf, line_count ); - } - } + while (strchr(stopvalues, *inbuf) == NULL + && *inbuf != 0) + inbuf++; - return (line_count); + *p = 0; } -// -------------------------------------------------------------------------- -// BEGIN private functions -// -------------------------------------------------------------------------- - -// first, some file locking stuff for multiuser crawl -#ifdef USE_FILE_LOCKING - -static bool lock_file_handle( FILE *handle, int type ) +static void hs_parse_generic_2( + char *&inbuf, char *outbuf, size_t outsz, const char *continuevalues) { - struct flock lock; - int status; + ASSERT(outsz > 0); - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - lock.l_type = type; - -#ifdef USE_BLOCKING_LOCK - - status = fcntl( fileno( handle ), F_SETLKW, &lock ); - -#else + char *p = outbuf; - for (int i = 0; i < 30; i++) + if (!*inbuf) { - status = fcntl( fileno( handle ), F_SETLK, &lock ); - - // success - if (status == 0) - break; - - // known failure - if (status == -1 && (errno != EACCES && errno != EAGAIN)) - break; - - perror( "Problems locking file... retrying..." ); - delay( 1000 ); + *p = 0; + return; } -#endif + while (strchr(continuevalues, *inbuf) != NULL + && *inbuf + && (p - outbuf) < (int) outsz - 1) + *p++ = *inbuf++; - return (status == 0); + while (strchr(continuevalues, *inbuf) != NULL + && *inbuf) + inbuf++; + + *p = 0; } -static bool unlock_file_handle( FILE *handle ) +static void hs_stripblanks(char *buf) { - struct flock lock; - int status; - - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - lock.l_type = F_UNLCK; - -#ifdef USE_BLOCKING_LOCK - - status = fcntl( fileno( handle ), F_SETLKW, &lock ); - -#else - - for (int i = 0; i < 30; i++) - { - status = fcntl( fileno( handle ), F_SETLK, &lock ); - - // success - if (status == 0) - break; - - // known failure - if (status == -1 && (errno != EACCES && errno != EAGAIN)) - break; + char *p = buf; + char *q = buf; - perror( "Problems unlocking file... retrying..." ); - delay( 1000 ); - } + // strip leading + while(*p == ' ') + p++; -#endif + while(*p != 0 && p != q) + *q++ = *p++; - return (status == 0); + *q-- = 0; + // strip trailing + while (q >= buf && *q == ' ') + *q-- = 0; } -#endif - - - -FILE *hs_open( const char *mode ) +static void hs_search_death(char *inbuf, struct scorefile_entry &se) { -#ifdef SAVE_DIR_PATH - FILE *handle = fopen(SAVE_DIR_PATH "scores", mode); -#ifdef SHARED_FILES_CHMOD_PUBLIC - chmod(SAVE_DIR_PATH "scores", SHARED_FILES_CHMOD_PUBLIC); -#endif -#else - FILE *handle = fopen("scores", mode); -#endif - -#ifdef USE_FILE_LOCKING - int locktype = F_RDLCK; - if (stricmp(mode, "w") == 0) - locktype = F_WRLCK; + // assume killed by monster + se.death_type = KILLED_BY_MONSTER; - if (handle && !lock_file_handle( handle, locktype )) + // sigh.. + if (strstr(inbuf, "killed by a lethal dose of poison") != NULL) + se.death_type = KILLED_BY_POISON; + else if (strstr(inbuf, "killed by a cloud") != NULL) + se.death_type = KILLED_BY_CLOUD; + else if (strstr(inbuf, "killed from afar by") != NULL) + se.death_type = KILLED_BY_BEAM; + else if (strstr(inbuf, "took a swim in molten lava") != NULL) + se.death_type = KILLED_BY_LAVA; + else if (strstr(inbuf, "asphyxiated")) + se.death_type = KILLED_BY_CURARE; + else if (strstr(inbuf, "soaked and fell apart") != NULL) { - perror( "Could not lock scorefile... " ); - fclose( handle ); - handle = NULL; + se.death_type = KILLED_BY_WATER; + se.race = SP_MUMMY; } -#endif - return handle; -} - -void hs_close( FILE *handle, const char *mode ) -{ - UNUSED( mode ); + else if (strstr(inbuf, "drowned") != NULL) + se.death_type = KILLED_BY_WATER; + else if (strstr(inbuf, "died of stupidity") != NULL) + se.death_type = KILLED_BY_STUPIDITY; + else if (strstr(inbuf, "too weak to continue adventuring") != NULL) + se.death_type = KILLED_BY_WEAKNESS; + else if (strstr(inbuf, "slipped on a banana peel") != NULL) + se.death_type = KILLED_BY_CLUMSINESS; + else if (strstr(inbuf, "killed by a trap") != NULL) + se.death_type = KILLED_BY_TRAP; + else if (strstr(inbuf, "got out of the dungeon alive") != NULL) + se.death_type = KILLED_BY_LEAVING; + else if (strstr(inbuf, "escaped with the Orb") != NULL) + se.death_type = KILLED_BY_WINNING; + else if (strstr(inbuf, "quit") != NULL) + se.death_type = KILLED_BY_QUITTING; + else if (strstr(inbuf, "was drained of all life") != NULL) + se.death_type = KILLED_BY_DRAINING; + else if (strstr(inbuf, "starved to death") != NULL) + se.death_type = KILLED_BY_STARVATION; + else if (strstr(inbuf, "froze to death") != NULL) + se.death_type = KILLED_BY_FREEZING; + else if (strstr(inbuf, "burnt to a crisp") != NULL) + se.death_type = KILLED_BY_BURNING; + else if (strstr(inbuf, "killed by wild magic") != NULL) + se.death_type = KILLED_BY_WILD_MAGIC; + else if (strstr(inbuf, "killed by Xom") != NULL) + se.death_type = KILLED_BY_XOM; + else if (strstr(inbuf, "killed by a statue") != NULL) + se.death_type = KILLED_BY_STATUE; + else if (strstr(inbuf, "rotted away") != NULL) + se.death_type = KILLED_BY_ROTTING; + else if (strstr(inbuf, "killed by bad target") != NULL) + se.death_type = KILLED_BY_TARGETTING; + else if (strstr(inbuf, "killed by an exploding spore") != NULL) + se.death_type = KILLED_BY_SPORE; + else if (strstr(inbuf, "smote by The Shining One") != NULL) + se.death_type = KILLED_BY_TSO_SMITING; + else if (strstr(inbuf, "turned to stone") != NULL) + se.death_type = KILLED_BY_PETRIFICATION; + else if (strstr(inbuf, "melted into a puddle") != NULL) + se.death_type = KILLED_BY_MELTING; + else if (strstr(inbuf, "bled to death") != NULL) + se.death_type = KILLED_BY_BLEEDING; - if (handle == NULL) - return; + // whew! -#ifdef USE_FILE_LOCKING - unlock_file_handle( handle ); -#endif + // now, if we're still KILLED_BY_MONSTER, make sure that there is + // a "killed by" somewhere, or else we're setting it to UNKNOWN. + if (se.death_type == KILLED_BY_MONSTER) + { + if (strstr(inbuf, "killed by") == NULL) + se.death_type = KILLED_BY_SOMETHING; + } - // actually close - fclose(handle); + // set some fields + se.death_source = 0; + se.mon_num = 0; + *se.death_source_name = 0; -#ifdef SHARED_FILES_CHMOD_PUBLIC - if (stricmp(mode, "w") == 0) + // now try to pull the monster out. + // [dshaligram] Holy brain damage, Batman. + if (se.death_type == KILLED_BY_MONSTER || se.death_type == KILLED_BY_BEAM) { - #ifdef SAVE_DIR_PATH - chmod(SAVE_DIR_PATH "scores", SHARED_FILES_CHMOD_PUBLIC); - #else - chmod("scores", SHARED_FILES_CHMOD_PUBLIC); - #endif + char *p = strstr(inbuf, " by "); + if (p) + { + p += 4; + char *q = strstr(inbuf, " on "); + if (q == NULL) + q = strstr(inbuf, " in "); + + if (q && q > p) + { + char *d = se.death_source_name; + const int maxread = sizeof(se.death_source_name) - 1; + + while (p < q && (d - se.death_source_name) < maxread) + *d++ = *p++; + + *d = 0; + } + } } -#endif } -static void hs_init( struct scorefile_entry &dest ) +static void hs_search_where(char *inbuf, struct scorefile_entry &se) { - // simple init - dest.version = 0; - dest.release = 0; - dest.points = -1; - dest.name[0] = '\0'; - dest.uid = 0; - dest.race = 0; - dest.cls = 0; - dest.lvl = 0; - dest.race_class_name[0] = '\0'; - dest.best_skill = 0; - dest.best_skill_lvl = 0; - dest.death_type = KILLED_BY_SOMETHING; - dest.death_source = 0; - dest.mon_num = 0; - dest.death_source_name[0] = '\0'; - dest.auxkilldata[0] = '\0'; - dest.dlvl = 0; - dest.level_type = 0; - dest.branch = 0; - dest.final_hp = -1; - dest.final_max_hp = -1; - dest.final_max_max_hp = -1; - dest.str = -1; - dest.intel = -1; - dest.dex = -1; - dest.damage = -1; - dest.god = -1; - dest.piety = -1; - dest.penance = -1; - dest.wiz_mode = 0; - dest.birth_time = 0; - dest.death_time = 0; - dest.real_time = -1; - dest.num_turns = -1; - dest.num_diff_runes = 0; - dest.num_runes = 0; -} + char scratch[6]; -void hs_copy(struct scorefile_entry &dest, struct scorefile_entry &src) -{ - // simple field copy -- assume src is well constructed. - - dest.version = src.version; - dest.release = src.release; - dest.points = src.points; - strcpy(dest.name, src.name); - dest.uid = src.uid; - dest.race = src.race; - dest.cls = src.cls; - dest.lvl = src.lvl; - strcpy(dest.race_class_name, src.race_class_name); - dest.best_skill = src.best_skill; - dest.best_skill_lvl = src.best_skill_lvl; - dest.death_type = src.death_type; - dest.death_source = src.death_source; - dest.mon_num = src.mon_num; - strcpy( dest.death_source_name, src.death_source_name ); - strcpy( dest.auxkilldata, src.auxkilldata ); - dest.dlvl = src.dlvl; - dest.level_type = src.level_type; - dest.branch = src.branch; - dest.final_hp = src.final_hp; - dest.final_max_hp = src.final_max_hp; - dest.final_max_max_hp = src.final_max_max_hp; - dest.str = src.str; - dest.intel = src.intel; - dest.dex = src.dex; - dest.damage = src.damage; - dest.god = src.god; - dest.piety = src.piety; - dest.penance = src.penance; - dest.wiz_mode = src.wiz_mode; - dest.birth_time = src.birth_time; - dest.death_time = src.death_time; - dest.real_time = src.real_time; - dest.num_turns = src.num_turns; - dest.num_diff_runes = src.num_diff_runes; - dest.num_runes = src.num_runes; -} + se.level_type = LEVEL_DUNGEON; + se.branch = BRANCH_MAIN_DUNGEON; + se.dlvl = 0; -bool hs_read( FILE *scores, struct scorefile_entry &dest ) -{ - char inbuf[200]; - int c = EOF; + // early out + if (se.death_type == KILLED_BY_LEAVING + || se.death_type == KILLED_BY_WINNING) + return; - hs_init( dest ); + // here we go again. + if (strstr(inbuf, "in the Abyss") != NULL) + se.level_type = LEVEL_ABYSS; + else if (strstr(inbuf, "in Pandemonium") != NULL) + se.level_type = LEVEL_PANDEMONIUM; + else if (strstr(inbuf, "in a labyrinth") != NULL) + se.level_type = LEVEL_LABYRINTH; - // get a character.. - if (scores != NULL) - c = fgetc(scores); + // early out for special level types + if (se.level_type != LEVEL_DUNGEON) + return; - // check for NULL scores file or EOF - if (scores == NULL || c == EOF) - return false; + // check for vestible + if (strstr(inbuf, "in the Vestibule") != NULL) + { + se.branch = BRANCH_VESTIBULE_OF_HELL; + return; + } - // get a line - this is tricky. "Lines" come in three flavors: - // 1) old-style lines which were 80 character blocks - // 2) 4.0 pr1 through pr7 versions which were newline terminated - // 3) 4.0 pr8 and onwards which are 'current' ASCII format, and - // may exceed 80 characters! + // from here, we have branch and level. + char *p = strstr(inbuf, "on L"); + if (p != NULL) + { + p += 4; + hs_parse_generic_2(p, scratch, sizeof scratch, "0123456789"); + se.dlvl = atoi( scratch ); + } - // put 'c' in first spot - inbuf[0] = c; + // get branch. + if (strstr(inbuf, "of Dis") != NULL) + se.branch = BRANCH_DIS; + else if (strstr(inbuf, "of Gehenna") != NULL) + se.branch = BRANCH_GEHENNA; + else if (strstr(inbuf, "of Cocytus") != NULL) + se.branch = BRANCH_COCYTUS; + else if (strstr(inbuf, "of Tartarus") != NULL) + se.branch = BRANCH_TARTARUS; + else if (strstr(inbuf, "of the Mines") != NULL) + se.branch = BRANCH_ORCISH_MINES; + else if (strstr(inbuf, "of the Hive") != NULL) + se.branch = BRANCH_HIVE; + else if (strstr(inbuf, "of the Lair") != NULL) + se.branch = BRANCH_LAIR; + else if (strstr(inbuf, "of the Slime Pits") != NULL) + se.branch = BRANCH_SLIME_PITS; + else if (strstr(inbuf, "of the Vaults") != NULL) + se.branch = BRANCH_VAULTS; + else if (strstr(inbuf, "of the Crypt") != NULL) + se.branch = BRANCH_CRYPT; + else if (strstr(inbuf, "of the Hall") != NULL) + se.branch = BRANCH_HALL_OF_BLADES; + else if (strstr(inbuf, "of Zot's Hall") != NULL) + se.branch = BRANCH_HALL_OF_ZOT; + else if (strstr(inbuf, "of the Temple") != NULL) + se.branch = BRANCH_ECUMENICAL_TEMPLE; + else if (strstr(inbuf, "of the Snake Pit") != NULL) + se.branch = BRANCH_SNAKE_PIT; + else if (strstr(inbuf, "of the Elf Hall") != NULL) + se.branch = BRANCH_ELVEN_HALLS; + else if (strstr(inbuf, "of the Tomb") != NULL) + se.branch = BRANCH_TOMB; + else if (strstr(inbuf, "of the Swamp") != NULL) + se.branch = BRANCH_SWAMP; +} - if (fgets(&inbuf[1], (c==':') ? (sizeof(inbuf) - 2) : 81, scores) == NULL) - return false; +////////////////////////////////////////////////////////////////////////// +// scorefile_entry - // check type; lines starting with a colon are new-style scores. - if (c == ':') - hs_parse_numeric(inbuf, dest); - else - hs_parse_string(inbuf, dest); +scorefile_entry::scorefile_entry(int dam, int dsource, int dtype, + const char *aux, bool death_cause_only) +{ + reset(); - return true; + init_death_cause(dam, dsource, dtype, aux); + if (!death_cause_only) + init(); } -static void hs_nextstring(char *&inbuf, char *dest) +scorefile_entry::scorefile_entry() { - char *p = dest; + // Completely uninitialized, caveat user. + reset(); +} - if (*inbuf == '\0') +void scorefile_entry::init_death_cause(int dam, int dsrc, + int dtype, const char *aux) +{ + death_source = dsrc; + death_type = dtype; + damage = dam; + + // Set the default aux data value... + // If aux is passed in (ie for a trap), we'll default to that. + if (aux == NULL) + auxkilldata[0] = 0; + else { - *p = '\0'; - return; + strncpy( auxkilldata, aux, ITEMNAME_SIZE ); + auxkilldata[ ITEMNAME_SIZE - 1 ] = 0; } - // assume we're on a ':' - inbuf ++; - while(*inbuf != ':' && *inbuf != '\0') - *p++ = *inbuf++; - - *p = '\0'; -} + // for death by monster + if ((death_type == KILLED_BY_MONSTER || death_type == KILLED_BY_BEAM) + && death_source >= 0 && death_source < MAX_MONSTERS) + { + const monsters *monster = &menv[death_source]; + + if (monster->type > 0 || monster->type <= NUM_MONSTERS) + { + death_source = monster->type; + mon_num = monster->number; + + // Previously the weapon was only used for dancing weapons, + // but now we pass it in as a string through the scorefile + // entry to be appended in hiscores_format_single in long or + // medium scorefile formats. + // It still isn't used in monam for anything but flying weapons + // though + if (death_type == KILLED_BY_MONSTER + && monster->inv[MSLOT_WEAPON] != NON_ITEM) + { + // [ds] The highscore entry may be constructed while the player + // is alive (for notes), so make sure we don't reveal info we + // shouldn't. + if (you.hp <= 0) + { +#if HISCORE_WEAPON_DETAIL + set_ident_flags( mitm[monster->inv[MSLOT_WEAPON]], + ISFLAG_IDENT_MASK ); +#else + // changing this to ignore the pluses to keep it short + unset_ident_flags( mitm[monster->inv[MSLOT_WEAPON]], + ISFLAG_IDENT_MASK ); -static int hs_nextint(char *&inbuf) -{ - char num[20]; - hs_nextstring(inbuf, num); + set_ident_flags( mitm[monster->inv[MSLOT_WEAPON]], + ISFLAG_KNOW_TYPE ); - return (num[0] == '\0' ? 0 : atoi(num)); -} + // clear "runed" description text to make shorter yet + set_equip_desc( mitm[monster->inv[MSLOT_WEAPON]], 0 ); +#endif + } -static long hs_nextlong(char *&inbuf) -{ - char num[20]; - hs_nextstring(inbuf, num); + // Setting this is redundant for dancing weapons, however + // we do care about the above indentification. -- bwr + if (monster->type != MONS_DANCING_WEAPON) + { + it_name( monster->inv[MSLOT_WEAPON], + DESC_NOCAP_A, + info ); + strncpy( auxkilldata, info, ITEMNAME_SIZE ); + auxkilldata[ ITEMNAME_SIZE - 1 ] = 0; + } + } + + strcpy( info, + monam( monster->number, monster->type, true, DESC_NOCAP_A, + monster->inv[MSLOT_WEAPON] ) ); - return (num[0] == '\0' ? 0 : atol(num)); + strncpy( death_source_name, info, 40 ); + death_source_name[39] = 0; + } + } + else + { + death_source = death_source; + mon_num = 0; + death_source_name[0] = 0; + } } -static int val_char( char digit ) +void scorefile_entry::reset() { - return (digit - '0'); + // simple init + version = 0; + release = 0; + points = -1; + name[0] = 0; + uid = 0; + race = 0; + cls = 0; + lvl = 0; + race_class_name[0] = 0; + best_skill = 0; + best_skill_lvl = 0; + death_type = KILLED_BY_SOMETHING; + death_source = 0; + mon_num = 0; + death_source_name[0] = 0; + auxkilldata[0] = 0; + dlvl = 0; + level_type = 0; + branch = 0; + final_hp = -1; + final_max_hp = -1; + final_max_max_hp = -1; + str = -1; + intel = -1; + dex = -1; + damage = -1; + god = -1; + piety = -1; + penance = -1; + wiz_mode = 0; + birth_time = 0; + death_time = 0; + real_time = -1; + num_turns = -1; + num_diff_runes = 0; + num_runes = 0; } -static time_t hs_nextdate(char *&inbuf) +void scorefile_entry::init() { - char buff[20]; - struct tm date; + // Score file entry version: + // + // 4.0 - original versioned entry + // 4.1 - added real_time and num_turn fields + // 4.2 - stats and god info - hs_nextstring( inbuf, buff ); + version = 4; + release = 2; - if (strlen( buff ) < 15) - return (static_cast(0)); + strncpy( name, you.your_name, sizeof name ); + name[ sizeof(name) - 1 ] = 0; - date.tm_year = val_char( buff[0] ) * 1000 + val_char( buff[1] ) * 100 - + val_char( buff[2] ) * 10 + val_char( buff[3] ) - 1900; +#ifdef MULTIUSER + uid = (int) getuid(); +#else + uid = 0; +#endif - date.tm_mon = val_char( buff[4] ) * 10 + val_char( buff[5] ); - date.tm_mday = val_char( buff[6] ) * 10 + val_char( buff[7] ); - date.tm_hour = val_char( buff[8] ) * 10 + val_char( buff[9] ); - date.tm_min = val_char( buff[10] ) * 10 + val_char( buff[11] ); - date.tm_sec = val_char( buff[12] ) * 10 + val_char( buff[13] ); - date.tm_isdst = (buff[14] == 'D'); + // do points first. + points = you.gold; + points += (you.experience * 7) / 10; - return (mktime( &date )); -} + //if (death_type == KILLED_BY_WINNING) points += points / 2; + //if (death_type == KILLED_BY_LEAVING) points += points / 10; + // these now handled by giving player the value of their inventory + char temp_id[4][50]; -static void hs_parse_numeric(char *inbuf, struct scorefile_entry &se) -{ - se.version = hs_nextint(inbuf); - se.release = hs_nextint(inbuf); + for (int d = 0; d < 4; d++) + { + for (int e = 0; e < 50; e++) + temp_id[d][e] = 1; + } - // this would be a good point to check for version numbers and branch - // appropriately + FixedVector< int, NUM_RUNE_TYPES > rune_array; - // acceptable versions are 0 (converted from old hiscore format) and 4 - if (se.version != 0 && se.version != 4) - return; + num_runes = 0; + num_diff_runes = 0; - se.points = hs_nextlong(inbuf); + for (int i = 0; i < NUM_RUNE_TYPES; i++) + rune_array[i] = 0; - hs_nextstring(inbuf, se.name); + // Calculate value of pack and runes when character leaves dungeon + if (death_type == KILLED_BY_LEAVING || death_type == KILLED_BY_WINNING) + { + for (int d = 0; d < ENDOFPACK; d++) + { + if (is_valid_item( you.inv[d] )) + { + points += item_value( you.inv[d], temp_id, true ); - se.uid = hs_nextlong(inbuf); - se.race = hs_nextint(inbuf); - se.cls = hs_nextint(inbuf); + if (you.inv[d].base_type == OBJ_MISCELLANY + && you.inv[d].sub_type == MISC_RUNE_OF_ZOT) + { + if (rune_array[ you.inv[d].plus ] == 0) + num_diff_runes++; - hs_nextstring(inbuf, se.race_class_name); + num_runes += you.inv[d].quantity; + rune_array[ you.inv[d].plus ] += you.inv[d].quantity; + } + } + } - se.lvl = hs_nextint(inbuf); - se.best_skill = hs_nextint(inbuf); - se.best_skill_lvl = hs_nextint(inbuf); - se.death_type = hs_nextint(inbuf); - se.death_source = hs_nextint(inbuf); - se.mon_num = hs_nextint(inbuf); + // Bonus for exploring different areas, not for collecting a + // huge stack of demonic runes in Pandemonium (gold value + // is enough for those). -- bwr + if (num_diff_runes >= 3) + points += ((num_diff_runes + 2) * (num_diff_runes + 2) * 1000); + } - hs_nextstring(inbuf, se.death_source_name); + // Players will have a hard time getting 1/10 of this (see XP cap): + if (points > 99999999) + points = 99999999; - // To try and keep the scorefile backwards compatible, - // we'll branch on version > 4.0 to read the auxkilldata - // text field. - if (se.version == 4 && se.release >= 1) - hs_nextstring( inbuf, se.auxkilldata ); - else - se.auxkilldata[0] = '\0'; + race = you.species; + cls = you.char_class; - se.dlvl = hs_nextint(inbuf); - se.level_type = hs_nextint(inbuf); - se.branch = hs_nextint(inbuf); + race_class_name[0] = 0; - // Trying to fix some bugs that have been around since at - // least pr19, if not longer. From now on, dlvl should - // be calculated on death and need no further modification. - if (se.version < 4 || se.release < 2) - { - if (se.level_type == LEVEL_DUNGEON) - { - if (se.branch == BRANCH_MAIN_DUNGEON) - se.dlvl += 1; - else if (se.branch < BRANCH_ORCISH_MINES) // ie the hells - se.dlvl -= 1; - } - } + lvl = you.experience_level; + best_skill = ::best_skill( SK_FIGHTING, NUM_SKILLS - 1, 99 ); + best_skill_lvl = you.skills[ best_skill ]; - se.final_hp = hs_nextint(inbuf); - if (se.version == 4 && se.release >= 2) + final_hp = you.hp; + final_max_hp = you.hp_max; + final_max_max_hp = you.hp_max + player_rotted(); + str = you.strength; + intel = you.intel; + dex = you.dex; + + god = you.religion; + if (you.religion != GOD_NO_GOD) { - se.final_max_hp = hs_nextint(inbuf); - se.final_max_max_hp = hs_nextint(inbuf); - se.damage = hs_nextint(inbuf); - se.str = hs_nextint(inbuf); - se.intel = hs_nextint(inbuf); - se.dex = hs_nextint(inbuf); - se.god = hs_nextint(inbuf); - se.piety = hs_nextint(inbuf); - se.penance = hs_nextint(inbuf); + piety = you.piety; + penance = you.penance[you.religion]; } - else + + // main dungeon: level is simply level + dlvl = you.your_level + 1; + switch (you.where_are_you) { - se.final_max_hp = -1; - se.final_max_max_hp = -1; - se.damage = -1; - se.str = -1; - se.intel = -1; - se.dex = -1; - se.god = -1; - se.piety = -1; - se.penance = -1; + case BRANCH_ORCISH_MINES: + case BRANCH_HIVE: + case BRANCH_LAIR: + case BRANCH_SLIME_PITS: + case BRANCH_VAULTS: + case BRANCH_CRYPT: + case BRANCH_HALL_OF_BLADES: + case BRANCH_HALL_OF_ZOT: + case BRANCH_ECUMENICAL_TEMPLE: + case BRANCH_SNAKE_PIT: + case BRANCH_ELVEN_HALLS: + case BRANCH_TOMB: + case BRANCH_SWAMP: + dlvl = you.your_level - you.branch_stairs[you.where_are_you - 10]; + break; + + case BRANCH_DIS: + case BRANCH_GEHENNA: + case BRANCH_VESTIBULE_OF_HELL: + case BRANCH_COCYTUS: + case BRANCH_TARTARUS: + case BRANCH_INFERNO: + case BRANCH_THE_PIT: + dlvl = you.your_level - 26; + break; } - se.wiz_mode = hs_nextint(inbuf); + branch = you.where_are_you; // no adjustments necessary. + level_type = you.level_type; // pandemonium, labyrinth, dungeon.. - se.birth_time = hs_nextdate(inbuf); - se.death_time = hs_nextdate(inbuf); + birth_time = you.birth_time; // start time of game + death_time = time( NULL ); // end time of game - if (se.version == 4 && se.release >= 2) - { - se.real_time = hs_nextint(inbuf); - se.num_turns = hs_nextint(inbuf); - } - else - { - se.real_time = -1; - se.num_turns = -1; - } + if (you.real_time != -1) + real_time = you.real_time + (death_time - you.start_time); + else + real_time = -1; - se.num_diff_runes = hs_nextint(inbuf); - se.num_runes = hs_nextint(inbuf); + num_turns = you.num_turns; + +#ifdef WIZARD + wiz_mode = (you.wizard ? 1 : 0); +#else + wiz_mode = 0; +#endif } -static void hs_write( FILE *scores, struct scorefile_entry &se ) +std::string scorefile_entry::hiscore_line(death_desc_verbosity verbosity) const { - char buff[80]; // should be more than enough for date stamps + std::string line = character_description(verbosity); + line += death_description(verbosity); + line += death_place(verbosity); + line += game_time(verbosity); - se.version = 4; - se.release = 2; + return (line); +} - fprintf( scores, ":%d:%d:%ld:%s:%ld:%d:%d:%s:%d:%d:%d", - se.version, se.release, se.points, se.name, - se.uid, se.race, se.cls, se.race_class_name, se.lvl, - se.best_skill, se.best_skill_lvl ); +std::string scorefile_entry::game_time(death_desc_verbosity verbosity) const +{ + std::string line; - // XXX: need damage - fprintf( scores, ":%d:%d:%d:%s:%s:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", - se.death_type, se.death_source, se.mon_num, - se.death_source_name, se.auxkilldata, - se.dlvl, se.level_type, se.branch, - se.final_hp, se.final_max_hp, se.final_max_max_hp, se.damage, - se.str, se.intel, se.dex, - se.god, se.piety, se.penance, se.wiz_mode ); + if (verbosity == DDV_VERBOSE) + { + if (real_time > 0) + { + char username[80] = "The"; + char scratch[INFO_SIZE]; + char tmp[80]; - make_date_string( se.birth_time, buff ); - fprintf( scores, ":%s", buff ); +#ifdef MULTIUSER + if (uid > 0) + { + struct passwd *pw_entry = getpwuid( uid ); + strncpy( username, pw_entry->pw_name, sizeof(username) ); + strncat( username, "'s", sizeof(username) ); + username[0] = toupper( username[0] ); + } +#endif - make_date_string( se.death_time, buff ); - fprintf( scores, ":%s", buff ); + make_time_string( real_time, tmp, sizeof(tmp) ); - fprintf( scores, ":%ld:%ld:%d:%d:\n", - se.real_time, se.num_turns, se.num_diff_runes, se.num_runes ); -} -// ------------------------------------------------------------------------- -// functions dealing with old-style scorefile entries. -// ------------------------------------------------------------------------- + snprintf( scratch, INFO_SIZE, "%s game lasted %s (%ld turns).", + username, tmp, num_turns ); -static void hs_parse_string(char *inbuf, struct scorefile_entry &se) + line += scratch; + line += hiscore_newline_string(); + } + } + + return (line); +} + +const char *scorefile_entry::damage_verb() const { - /* old entries are of the following format (Brent introduced some - spacing at one point, we have to take this into account): + // GDL: here's an example of using final_hp. Verbiage could be better. + // bwr: changed "blasted" since this is for melee + return (final_hp > -6) ? "Slain" : + (final_hp > -14) ? "Mangled" : + (final_hp > -22) ? "Demolished" : + "Annihilated"; +} - // Actually, I believe it might have been Brian who added the spaces, - // I was quite happy with the condensed version, given the 80 column - // restriction. -- bwr +const char *scorefile_entry::death_source_desc() const +{ + if (death_type != KILLED_BY_MONSTER && death_type != KILLED_BY_BEAM) + return (""); -6263 BoBo - DSD10 Wiz, killed by an acid blob on L1 of the Slime Pits. -5877 Aldus-DGM10, killed by a lethal dose of poison on L10. -5419 Yarf - Koa10, killed by a warg on L1 of the Mines. + return (*death_source_name? + death_source_name + : monam( mon_num, death_source, true, DESC_PLAIN ) ); +} - 1. All numerics up to the first non-numeric are the score - 2. All non '-' characters are the name. Strip spaces. - 3. All alphabetics up to the first numeric are race/class - 4. All numerics up to the comma are the clevel - 5. From the comma, search for known fixed substrings and - translate to death_type. Leave death source = 0 for old - scores, and just copy in the monster name. - 6. Look for the branch type (again, substring search for - fixed strings) and level. +std::string scorefile_entry::damage_string(bool terse) const +{ + char scratch[50]; + snprintf( scratch, sizeof scratch, "(%d%s)", damage, + terse? "" : " damage" ); + return (scratch); +} - Very ugly and time consuming. +std::string scorefile_entry::strip_article_a(const std::string &s) const +{ + if (s.find("a ") == 0) + return (s.substr(2)); + else if (s.find("an ") == 0) + return (s.substr(3)); + return (s); +} - */ +std::string scorefile_entry::terse_missile_cause() const +{ + std::string cause; + std::string aux = auxkilldata; - char scratch[80]; + std::string monster_prefix = " by "; + // We're looking for Shot with a%s %s by %s/ Hit by a%s %s thrown by %s + std::string::size_type by = aux.rfind(monster_prefix); + if (by == std::string::npos) + return ("???"); - // 1. get score - hs_parse_generic_2(inbuf, scratch, "0123456789"); + std::string mcause = aux.substr(by + monster_prefix.length()); + mcause = strip_article_a(mcause); - se.version = 0; // version # of converted score - se.release = 0; - se.points = atoi(scratch); + std::string missile; + const std::string pre_post[][2] = { + { "Shot with a", " by " }, + { "Hit by a", " thrown by " } + }; - // 2. get name - hs_parse_generic_1(inbuf, scratch, "-"); - hs_stripblanks(scratch); - strcpy(se.name, scratch); + for (unsigned i = 0; i < sizeof(pre_post) / sizeof(*pre_post); ++i) + { + if (aux.find(pre_post[i][0]) != 0) + continue; - // 3. get race, class - inbuf++; // skip '-' - hs_parse_generic_1(inbuf, scratch, "0123456789"); - hs_stripblanks(scratch); - strcpy(se.race_class_name, scratch); - se.race = 0; - se.cls = 0; + std::string::size_type end = aux.rfind(pre_post[i][1]); + if (end == std::string::npos) + continue; - // 4. get clevel - hs_parse_generic_2(inbuf, scratch, "0123456789"); - se.lvl = atoi(scratch); + int istart = pre_post[i][0].length(); + int nchars = end - istart; + missile = aux.substr(istart, nchars); - // 4a. get wizard mode - hs_parse_generic_1(inbuf, scratch, ","); - if (strstr(scratch, "Wiz") != NULL) - se.wiz_mode = 1; - else - se.wiz_mode = 0; + // Was this prefixed by "an"? + if (missile.find("n ") == 0) + missile = missile.substr(2); + } - // 5. get death type - inbuf++; // skip comma - hs_search_death(inbuf, se); + if (missile.length()) + mcause += "/" + missile; - // 6. get branch, level - hs_search_where(inbuf, se); + return (mcause); +} - // set things that can't be picked out of old scorefile entries - se.uid = 0; - se.best_skill = 0; - se.best_skill_lvl = 0; - se.final_hp = 0; - se.final_max_hp = -1; - se.final_max_max_hp = -1; - se.damage = -1; - se.str = -1; - se.intel = -1; - se.dex = -1; - se.god = -1; - se.piety = -1; - se.penance = -1; - se.birth_time = 0; - se.death_time = 0; - se.real_time = -1; - se.num_turns = -1; - se.num_runes = 0; - se.num_diff_runes = 0; - se.auxkilldata[0] = '\0'; +std::string scorefile_entry::terse_beam_cause() const +{ + std::string cause = auxkilldata; + if (cause.find("by ") == 0 || cause.find("By ") == 0) + cause = cause.substr(3); + return (cause); } -static void hs_parse_generic_1(char *&inbuf, char *outbuf, const char *stopvalues) +std::string scorefile_entry::terse_wild_magic() const { - char *p = outbuf; + return terse_beam_cause(); +} - while(strchr(stopvalues, *inbuf) == NULL && *inbuf != '\0') - *p++ = *inbuf++; +std::string scorefile_entry::terse_trap() const +{ + std::string trap = *auxkilldata? std::string(auxkilldata) + " trap" + : "trap"; + if (trap.find("n ") == 0) + trap = trap.substr(2); + trim_string(trap); - *p = '\0'; + return (trap); } -static void hs_parse_generic_2(char *&inbuf, char *outbuf, const char *continuevalues) +std::string scorefile_entry::single_cdesc() const { - char *p = outbuf; + char scratch[INFO_SIZE]; + char buf[INFO_SIZE]; - while(strchr(continuevalues, *inbuf) != NULL && *inbuf != '\0') - *p++ = *inbuf++; + if (!*race_class_name) + { + snprintf( scratch, sizeof(scratch), "%s%s", + get_species_abbrev( race ), get_class_abbrev( cls ) ); + } + else + { + strncpy( scratch, race_class_name, sizeof scratch ); + scratch[ sizeof(scratch) - 1 ] = 0; + } + + std::string scname = name; + if (scname.length() > 10) + scname = scname.substr(0, 10); + + snprintf( buf, sizeof buf, + "%8ld %-10s %s-%02d%s", points, scname.c_str(), + scratch, lvl, (wiz_mode == 1) ? "W" : "" ); - *p = '\0'; + return (buf); } -static void hs_stripblanks(char *buf) +std::string +scorefile_entry::character_description(death_desc_verbosity verbosity) const { - char *p = buf; - char *q = buf; + bool single = verbosity == DDV_TERSE || verbosity == DDV_ONELINE; - // strip leading - while(*p == ' ') - p++; - while(*p != '\0') - *q++ = *p++; + if (single) + return single_cdesc(); - *q-- = '\0'; - // strip trailing - while(*q == ' ') + bool verbose = verbosity == DDV_VERBOSE; + char scratch[INFO_SIZE]; + char buf[INFO_SIZE]; + + std::string desc; + // Please excuse the following bit of mess in the name of flavour ;) + if (verbose) + { + strncpy( scratch, skill_title( best_skill, best_skill_lvl, + race, str, dex, god ), + INFO_SIZE ); + + snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s (level %d", + points, name, scratch, lvl ); + + desc = buf; + } + else + { + snprintf( buf, HIGHSCORE_SIZE, "%8ld %s the %s %s (level %d", + points, name, species_name(race, lvl), + get_class_name(cls), lvl ); + desc = buf; + } + + if (final_max_max_hp > 0) // as the other two may be negative + { + snprintf( scratch, INFO_SIZE, ", %d/%d", final_hp, final_max_hp ); + desc += scratch; + + if (final_max_hp < final_max_max_hp) + { + snprintf( scratch, INFO_SIZE, " (%d)", final_max_max_hp ); + desc += scratch; + } + + desc += " HPs"; + } + + desc += wiz_mode? ") *WIZ*" : ")"; + desc += hiscore_newline_string(); + + if (verbose) { - *q = '\0'; - q--; + const char *const srace = species_name( race, lvl ); + + snprintf( scratch, INFO_SIZE, "Began as a%s %s %s", + is_vowel(*srace) ? "n" : "", srace, get_class_name(cls) ); + desc += scratch; + + if (birth_time > 0) + { + desc += " on "; + hiscore_date_string( birth_time, scratch ); + desc += scratch; + } + + desc += "."; + desc += hiscore_newline_string(); + + if (race != SP_DEMIGOD && god != -1) + { + if (god == GOD_XOM) + { + snprintf( scratch, INFO_SIZE, "Was a %sPlaything of Xom.", + (lvl >= 20) ? "Favourite " : "" ); + + desc += scratch; + desc += hiscore_newline_string(); + } + else if (god != GOD_NO_GOD) + { + // Not exactly the same as the religon screen, but + // good enough to fill this slot for now. + snprintf( scratch, INFO_SIZE, "Was %s of %s%s", + (piety > 160) ? "the Champion" : + (piety >= 120) ? "a High Priest" : + (piety >= 100) ? "an Elder" : + (piety >= 75) ? "a Priest" : + (piety >= 50) ? "a Believer" : + (piety >= 30) ? "a Follower" + : "an Initiate", + god_name( god ), + (penance > 0) ? " (penitent)." : "." ); + + desc += scratch; + desc += hiscore_newline_string(); + } + } } + + return (desc); } -static void hs_search_death(char *inbuf, struct scorefile_entry &se) +std::string scorefile_entry::death_place(death_desc_verbosity verbosity) const { - // assume killed by monster - se.death_type = KILLED_BY_MONSTER; + bool verbose = verbosity == DDV_VERBOSE; + std::string place; - // sigh.. - if (strstr(inbuf, "killed by a lethal dose of poison") != NULL) - se.death_type = KILLED_BY_POISON; - else if (strstr(inbuf, "killed by a cloud") != NULL) - se.death_type = KILLED_BY_CLOUD; - else if (strstr(inbuf, "killed from afar by") != NULL) - se.death_type = KILLED_BY_BEAM; - else if (strstr(inbuf, "took a swim in molten lava") != NULL) - se.death_type = KILLED_BY_LAVA; - else if (strstr(inbuf, "soaked and fell apart") != NULL) - { - se.death_type = KILLED_BY_WATER; - se.race = SP_MUMMY; - } - else if (strstr(inbuf, "drowned") != NULL) - se.death_type = KILLED_BY_WATER; - else if (strstr(inbuf, "died of stupidity") != NULL) - se.death_type = KILLED_BY_STUPIDITY; - else if (strstr(inbuf, "too weak to continue adventuring") != NULL) - se.death_type = KILLED_BY_WEAKNESS; - else if (strstr(inbuf, "slipped on a banana peel") != NULL) - se.death_type = KILLED_BY_CLUMSINESS; - else if (strstr(inbuf, "killed by a trap") != NULL) - se.death_type = KILLED_BY_TRAP; - else if (strstr(inbuf, "got out of the dungeon alive") != NULL) - se.death_type = KILLED_BY_LEAVING; - else if (strstr(inbuf, "escaped with the Orb") != NULL) - se.death_type = KILLED_BY_WINNING; - else if (strstr(inbuf, "quit") != NULL) - se.death_type = KILLED_BY_QUITTING; - else if (strstr(inbuf, "was drained of all life") != NULL) - se.death_type = KILLED_BY_DRAINING; - else if (strstr(inbuf, "starved to death") != NULL) - se.death_type = KILLED_BY_STARVATION; - else if (strstr(inbuf, "froze to death") != NULL) - se.death_type = KILLED_BY_FREEZING; - else if (strstr(inbuf, "burnt to a crisp") != NULL) - se.death_type = KILLED_BY_BURNING; - else if (strstr(inbuf, "killed by wild magic") != NULL) - se.death_type = KILLED_BY_WILD_MAGIC; - else if (strstr(inbuf, "killed by Xom") != NULL) - se.death_type = KILLED_BY_XOM; - else if (strstr(inbuf, "killed by a statue") != NULL) - se.death_type = KILLED_BY_STATUE; - else if (strstr(inbuf, "rotted away") != NULL) - se.death_type = KILLED_BY_ROTTING; - else if (strstr(inbuf, "killed by bad target") != NULL) - se.death_type = KILLED_BY_TARGETTING; - else if (strstr(inbuf, "killed by an exploding spore") != NULL) - se.death_type = KILLED_BY_SPORE; - else if (strstr(inbuf, "smote by The Shining One") != NULL) - se.death_type = KILLED_BY_TSO_SMITING; - else if (strstr(inbuf, "turned to stone") != NULL) - se.death_type = KILLED_BY_PETRIFICATION; - else if (strstr(inbuf, "eviscerated by a hatching") != NULL) - se.death_type = KILLED_BY_SHUGGOTH; + if (death_type == KILLED_BY_LEAVING || death_type == KILLED_BY_WINNING) + return (""); - // whew! + char scratch[ INFO_SIZE ]; - // now, if we're still KILLED_BY_MONSTER, make sure that there is - // a "killed by" somewhere, or else we're setting it to UNKNOWN. - if (se.death_type == KILLED_BY_MONSTER) + if (verbosity == DDV_ONELINE || verbosity == DDV_TERSE) { - if (strstr(inbuf, "killed by") == NULL) - se.death_type = KILLED_BY_SOMETHING; + snprintf( scratch, sizeof scratch, " (%s)", + place_name(get_packed_place(branch, dlvl, level_type), + false, true).c_str()); + return (scratch); } - // set some fields - se.death_source = 0; - se.mon_num = 0; - strcpy(se.death_source_name, ""); + if (verbose && death_type != KILLED_BY_QUITTING) + place += "..."; - // now try to pull the monster out. - if (se.death_type == KILLED_BY_MONSTER || se.death_type == KILLED_BY_BEAM) + // where did we die? + std::string placename = + place_name(get_packed_place(branch, dlvl, level_type), + true, true); + + // add appropriate prefix + if (placename.find("Level") == 0) + place += " on "; + else + place += " in "; + + place += placename; + + if (verbose && death_time + && !hiscore_same_day( birth_time, death_time )) { - char *p = strstr(inbuf, " by "); - p += 4; - char *q = strstr(inbuf, " on "); - if (q == NULL) - q = strstr(inbuf, " in "); - char *d = se.death_source_name; - while(p != q) - *d++ = *p++; - - *d = '\0'; + place += " on "; + hiscore_date_string( death_time, scratch ); + place += scratch; } + + place += "."; + place += hiscore_newline_string(); + + return (place); } -static void hs_search_where(char *inbuf, struct scorefile_entry &se) +std::string +scorefile_entry::death_description(death_desc_verbosity verbosity) const { - char scratch[6]; + bool needs_beam_cause_line = false; + bool needs_called_by_monster_line = false; + bool needs_damage = false; - se.level_type = LEVEL_DUNGEON; - se.branch = BRANCH_MAIN_DUNGEON; - se.dlvl = 0; + bool terse = verbosity == DDV_TERSE; + bool verbose = verbosity == DDV_VERBOSE; + bool oneline = verbosity == DDV_ONELINE; - // early out - if (se.death_type == KILLED_BY_LEAVING || se.death_type == KILLED_BY_WINNING) - return; + char scratch[INFO_SIZE]; - // here we go again. - if (strstr(inbuf, "in the Abyss") != NULL) - se.level_type = LEVEL_ABYSS; - else if (strstr(inbuf, "in Pandemonium") != NULL) - se.level_type = LEVEL_PANDEMONIUM; - else if (strstr(inbuf, "in a labyrinth") != NULL) - se.level_type = LEVEL_LABYRINTH; + std::string desc; + + if (oneline) + desc = " "; - // early out for special level types - if (se.level_type != LEVEL_DUNGEON) - return; + switch (death_type) + { + case KILLED_BY_MONSTER: + if (terse) + desc += death_source_desc(); + else if (oneline) + desc += std::string("slain by ") + death_source_desc(); + else + { + snprintf( scratch, sizeof scratch, "%s by %s", + damage_verb(), + death_source_desc() ); - // check for vestible - if (strstr(inbuf, "in the Vestibule") != NULL) + desc += scratch; + } + + // put the damage on the weapon line if there is one + if (auxkilldata[0] == 0) + needs_damage = true; + break; + + case KILLED_BY_POISON: + desc += terse? "poison" : "Succumbed to poison"; + break; + + case KILLED_BY_CLOUD: + if (auxkilldata[0] == 0) + desc += terse? "cloud" : "Engulfed by a cloud"; + else + { + snprintf( scratch, sizeof(scratch), "%scloud of %s", + terse? "" : "Engulfed by a ", + auxkilldata ); + desc += scratch; + } + needs_damage = true; + break; + + case KILLED_BY_BEAM: + if (oneline) + { + // keeping this short to leave room for the deep elf spellcasters: + snprintf( scratch, sizeof(scratch), "%s by ", + range_type_verb( auxkilldata ) ); + desc += scratch; + desc += death_source_desc(); + } + else if (isupper( auxkilldata[0] )) // already made (ie shot arrows) + { + // If terse we have to parse the information from the string. + // Darn it to heck. + desc += terse? terse_missile_cause() : auxkilldata; + needs_damage = true; + } + else if (verbose && strncmp( auxkilldata, "by ", 3 ) == 0) + { + // "by" is used for priest attacks where the effect is indirect + // in verbose format we have another line for the monster + needs_called_by_monster_line = true; + snprintf( scratch, sizeof(scratch), "Killed %s", auxkilldata ); + desc += scratch; + } + else + { + // Note: This is also used for the "by" cases in non-verbose + // mode since listing the monster is more imporatant. + if (!terse) + desc += "Killed from afar by "; + + desc += death_source_desc(); + + if (*auxkilldata) + needs_beam_cause_line = true; + + needs_damage = true; + } + break; + + case KILLED_BY_CURARE: + desc += terse? "asphyx" : "Asphyxiated"; + break; + + case KILLED_BY_LAVA: + if (terse) + desc += "lava"; + else + { + if (race == SP_MUMMY) + desc += "Turned to ash by lava"; + else + desc += "Took a swim in molten lava"; + } + break; + + case KILLED_BY_WATER: + if (race == SP_MUMMY) + desc += terse? "fell apart" : "Soaked and fell apart"; + else + desc += terse? "drowned" : "Drowned"; + break; + + case KILLED_BY_STUPIDITY: + desc += terse? "stupidity" : "Forgot to breathe"; + break; + + case KILLED_BY_WEAKNESS: + desc += terse? "collapsed " : "Collapsed under their own weight"; + break; + + case KILLED_BY_CLUMSINESS: + desc += terse? "clumsiness" : "Slipped on a banana peel"; + break; + + case KILLED_BY_TRAP: + if (terse) + desc += terse_trap(); + else + { + snprintf( scratch, sizeof(scratch), "Killed by triggering a%s trap", + (*auxkilldata) ? auxkilldata : "" ); + desc += scratch; + } + needs_damage = true; + break; + + case KILLED_BY_LEAVING: + if (terse) + desc += "left"; + else + { + if (num_runes > 0) + desc += "Got out of the dungeon"; + else + desc += "Got out of the dungeon alive"; + } + break; + + case KILLED_BY_WINNING: + desc += terse? "escaped" : "Escaped with the Orb"; + if (num_runes < 1) + desc += "!"; + break; + + case KILLED_BY_QUITTING: + desc += terse? "quit" : "Quit the game"; + break; + + case KILLED_BY_DRAINING: + desc += terse? "drained" : "Was drained of all life"; + break; + + case KILLED_BY_STARVATION: + desc += terse? "starvation" : "Starved to death"; + break; + + case KILLED_BY_FREEZING: // refrigeration spell + desc += terse? "frozen" : "Froze to death"; + needs_damage = true; + break; + + case KILLED_BY_BURNING: // sticky flame + desc += terse? "burnt" : "Burnt to a crisp"; + needs_damage = true; + break; + + case KILLED_BY_WILD_MAGIC: + if (!*auxkilldata) + desc += terse? "wild magic" : "Killed by wild magic"; + else + { + if (terse) + desc += terse_wild_magic(); + else + { + // A lot of sources for this case... some have "by" already. + snprintf( scratch, sizeof(scratch), "Killed %s%s", + (strncmp( auxkilldata, "by ", 3 ) != 0) ? "by " : "", + auxkilldata ); + desc += scratch; + } + } + + needs_damage = true; + break; + + case KILLED_BY_XOM: // only used for old Xom kills + desc += terse? "xom" : "Killed for Xom's enjoyment"; + needs_damage = true; + break; + + case KILLED_BY_STATUE: + desc += terse? "statue" : "Killed by a statue"; + needs_damage = true; + break; + + case KILLED_BY_ROTTING: + desc += terse? "rotting" : "Rotted away"; + break; + + case KILLED_BY_TARGETTING: + desc += terse? "shot self" : "Killed themselves with bad targeting"; + needs_damage = true; + break; + + case KILLED_BY_SPORE: + desc += terse? "spore" : "Killed by an exploding spore"; + needs_damage = true; + break; + + case KILLED_BY_TSO_SMITING: + desc += terse? "smote by Shining One" : "Smote by The Shining One"; + needs_damage = true; + break; + + case KILLED_BY_PETRIFICATION: + desc += terse? "petrified" : "Turned to stone"; + break; + + case KILLED_BY_MELTING: + desc += terse? "melted" : " melted into a puddle"; + break; + + case KILLED_BY_BLEEDING: + desc += terse? "bleeding" : " bled to death"; + break; + + case KILLED_BY_SOMETHING: + if (auxkilldata) + desc += auxkilldata; + else + desc += terse? "died" : "Died"; + needs_damage = true; + break; + + case KILLED_BY_FALLING_DOWN_STAIRS: + desc += terse? "fell downstairs" : "Fell down a flight of stairs"; + needs_damage = true; + break; + + case KILLED_BY_ACID: + desc += terse? "acid" : "Splashed by acid"; + needs_damage = true; + break; + + default: + desc += terse? "program bug" : "Nibbled to death by software bugs"; + break; + } // end switch + + if (oneline && desc.length() > 2) + desc[1] = tolower(desc[1]); + + // TODO: Eventually, get rid of "..." for cases where the text fits. + if (terse) { - se.branch = BRANCH_VESTIBULE_OF_HELL; - return; + if (death_type == KILLED_BY_MONSTER && *auxkilldata) + { + desc += "/"; + desc += strip_article_a(auxkilldata); + needs_damage = true; + } + else if (needs_beam_cause_line) + desc += "/" + terse_beam_cause(); + else if (needs_called_by_monster_line) + desc += death_source_name; + + if (needs_damage && damage > 0) + desc += " " + damage_string(true); } + else if (verbose) + { + bool done_damage = false; // paranoia - // from here, we have branch and level. - char *p = strstr(inbuf, "on L"); - if (p != NULL) + if (needs_damage && damage > 0) + { + desc += " " + damage_string(); + needs_damage = false; + done_damage = true; + } + + if ((death_type == KILLED_BY_LEAVING + || death_type == KILLED_BY_WINNING) + && num_runes > 0) + { + desc += hiscore_newline_string(); + + snprintf( scratch, INFO_SIZE, "... %s %d rune%s", + (death_type == KILLED_BY_WINNING) ? "and" : "with", + num_runes, (num_runes > 1) ? "s" : "" ); + desc += scratch; + + if (num_diff_runes > 1) + { + snprintf( scratch, INFO_SIZE, " (of %d types)", + num_diff_runes ); + desc += scratch; + } + + if (death_time > 0 + && !hiscore_same_day( birth_time, death_time )) + { + desc += " on "; + hiscore_date_string( death_time, scratch ); + desc += scratch; + } + + desc += "!"; + desc += hiscore_newline_string(); + } + else if (death_type != KILLED_BY_QUITTING) + { + desc += hiscore_newline_string(); + + if (death_type == KILLED_BY_MONSTER && auxkilldata[0]) + { + snprintf(scratch, INFO_SIZE, "... wielding %s", auxkilldata); + desc += scratch; + needs_damage = true; + } + else if (needs_beam_cause_line) + { + desc += (is_vowel( auxkilldata[0] )) ? "... with an " + : "... with a "; + desc += auxkilldata; + needs_damage = true; + } + else if (needs_called_by_monster_line) + { + snprintf( scratch, sizeof(scratch), "... called down by %s", + death_source_name ); + desc += scratch; + needs_damage = true; + } + + if (needs_damage && !done_damage && damage > 0) + desc += " " + damage_string(); + + if (needs_damage) + desc += hiscore_newline_string(); + } + } + + if (!oneline) { - p += 4; - hs_parse_generic_2(p, scratch, "0123456789"); - se.dlvl = atoi( scratch ); + if (death_type == KILLED_BY_LEAVING + || death_type == KILLED_BY_WINNING) + { + // TODO: strcat "after reaching level %d"; for LEAVING + if (verbosity == DDV_NORMAL) + { + if (num_runes > 0) + desc += "!"; + } + desc += hiscore_newline_string(); + } } - // get branch. - if (strstr(inbuf, "of Dis") != NULL) - se.branch = BRANCH_DIS; - else if (strstr(inbuf, "of Gehenna") != NULL) - se.branch = BRANCH_GEHENNA; - else if (strstr(inbuf, "of Cocytus") != NULL) - se.branch = BRANCH_COCYTUS; - else if (strstr(inbuf, "of Tartarus") != NULL) - se.branch = BRANCH_TARTARUS; - else if (strstr(inbuf, "of the Mines") != NULL) - se.branch = BRANCH_ORCISH_MINES; - else if (strstr(inbuf, "of the Hive") != NULL) - se.branch = BRANCH_HIVE; - else if (strstr(inbuf, "of the Lair") != NULL) - se.branch = BRANCH_LAIR; - else if (strstr(inbuf, "of the Slime Pits") != NULL) - se.branch = BRANCH_SLIME_PITS; - else if (strstr(inbuf, "of the Vaults") != NULL) - se.branch = BRANCH_VAULTS; - else if (strstr(inbuf, "of the Crypt") != NULL) - se.branch = BRANCH_CRYPT; - else if (strstr(inbuf, "of the Hall") != NULL) - se.branch = BRANCH_HALL_OF_BLADES; - else if (strstr(inbuf, "of Zot's Hall") != NULL) - se.branch = BRANCH_HALL_OF_ZOT; - else if (strstr(inbuf, "of the Temple") != NULL) - se.branch = BRANCH_ECUMENICAL_TEMPLE; - else if (strstr(inbuf, "of the Snake Pit") != NULL) - se.branch = BRANCH_SNAKE_PIT; - else if (strstr(inbuf, "of the Elf Hall") != NULL) - se.branch = BRANCH_ELVEN_HALLS; - else if (strstr(inbuf, "of the Tomb") != NULL) - se.branch = BRANCH_TOMB; - else if (strstr(inbuf, "of the Swamp") != NULL) - se.branch = BRANCH_SWAMP; + if (terse) + { + trim_string(desc); + desc = strip_article_a(desc); + } + + return (desc); } diff --git a/crawl-ref/source/hiscores.h b/crawl-ref/source/hiscores.h index 2cb5f4786f..93b22e660d 100644 --- a/crawl-ref/source/hiscores.h +++ b/crawl-ref/source/hiscores.h @@ -16,7 +16,7 @@ /* *********************************************************************** * called from: ouch * *********************************************************************** */ -void hiscores_new_entry( struct scorefile_entry &se ); +void hiscores_new_entry( const scorefile_entry &se ); // last updated 16feb2001 {gdl} /* *********************************************************************** @@ -28,8 +28,8 @@ void hiscores_print_list( int display_count = -1, int format = SCORE_TERSE ); /* *********************************************************************** * called from: ouch hiscores * *********************************************************************** */ -void hiscores_format_single( char *buffer, struct scorefile_entry &se ); -int hiscores_format_single_long( char *buffer, struct scorefile_entry &se, +void hiscores_format_single( char *buffer, const scorefile_entry &se ); +void hiscores_format_single_long( char *buffer, const scorefile_entry &se, bool verbose = false ); #endif // HISCORES_H diff --git a/crawl-ref/source/initfile.cc b/crawl-ref/source/initfile.cc index 46bf73e40e..a797100754 100644 --- a/crawl-ref/source/initfile.cc +++ b/crawl-ref/source/initfile.cc @@ -13,6 +13,8 @@ */ #include "AppHdr.h" +#include "externs.h" + #include "initfile.h" #include @@ -21,9 +23,9 @@ #include #include "clua.h" +#include "delay.h" #include "Kills.h" #include "files.h" -#include "externs.h" #include "defines.h" #include "libutil.h" #include "player.h" @@ -33,34 +35,23 @@ #include "items.h" #include "view.h" -game_options Options; - -extern void (*viewwindow) (char, bool); -extern unsigned char (*mapch) (unsigned char); -extern unsigned char (*mapch2) (unsigned char); -extern unsigned char mapchar3(unsigned char ldfk); -extern unsigned char mapchar4(unsigned char ldfk); +const std::string game_options::interrupt_prefix = "interrupt_"; +game_options Options; -#ifdef UNIX -extern int character_set; // unices only -#endif - -static std::string & tolower_string( std::string &str ); +std::string &tolower_string( std::string &str ); const static char *obj_syms = ")([/%.?=!.+\\0}X$"; const static int obj_syms_len = 16; static void read_startup_prefs(); -static void read_options(InitLineInput &il, bool runscript); -template void append_vector( - std::vector &dest, const std::vector &src) +template void append_vector(A &dest, const B &src) { dest.insert( dest.end(), src.begin(), src.end() ); } // returns -1 if unmatched else returns 0-15 -static short str_to_colour( const std::string &str ) +int str_to_colour( const std::string &str, int default_colour ) { int ret; @@ -71,6 +62,15 @@ static short str_to_colour( const std::string &str ) "lightred", "lightmagenta", "yellow", "white" }; + const std::string element_cols[] = + { + "fire", "ice", "earth", "electricity", "air", "poison", "water", + "magic", "mutagenic", "warp", "enchant", "heal", "holy", "dark", + "death", "necro", "unholy", "vehumet", "crystal", "blood", "smoke", + "slime", "jewel", "elven", "dwarven", "orcish", "gila", "floor", + "rock", "stone", "random" + }; + for (ret = 0; ret < 16; ret++) { if (str == cols[ret]) @@ -86,6 +86,21 @@ static short str_to_colour( const std::string &str ) ret = 8; } + if (ret == 16) + { + // Maybe we have an element colour attribute. + for (unsigned i = 0; i < sizeof(element_cols) / sizeof(*element_cols); + ++i) + { + if (str == element_cols[i]) + { + // Ugh. + ret = element_type(EC_FIRE + i); + break; + } + } + } + if (ret == 16) { // Check if we have a direct colour index @@ -96,7 +111,7 @@ static short str_to_colour( const std::string &str ) ret = ci; } - return ((ret == 16) ? -1 : ret); + return ((ret == 16) ? default_colour : ret); } // returns -1 if unmatched else returns 0-15 @@ -121,16 +136,16 @@ static int str_to_channel_colour( const std::string &str ) static const std::string message_channel_names[ NUM_MESSAGE_CHANNELS ] = { - "plain", "prompt", "god", "duration", "danger", "warning", "food", - "recovery", "talk", "intrinsic_gain", "mutation", "monster_spell", - "monster_enchant", "monster_damage", "rotten_meat", "equipment", - "diagnostic", + "plain", "prompt", "god", "pray", "duration", "danger", "warning", "food", + "recovery", "sound", "talk", "intrinsic_gain", "mutation", "monster_spell", + "monster_enchant", "monster_damage", "monster_target", + "rotten_meat", "equipment", "floor", "multiturn", "diagnostic", }; -// returns -1 if unmatched else returns 0-15 +// returns -1 if unmatched else returns 0--(NUM_MESSAGE_CHANNELS-1) int str_to_channel( const std::string &str ) { - short ret; + int ret; for (ret = 0; ret < NUM_MESSAGE_CHANNELS; ret++) { @@ -149,6 +164,19 @@ std::string channel_to_str( int channel ) return message_channel_names[channel]; } +static int str_to_book( const std::string& str ) +{ + if ( str == "fire" || str == "flame" ) + return SBT_FIRE; + if ( str == "cold" || str == "ice" ) + return SBT_COLD; + if ( str == "summ" || str == "summoning" ) + return SBT_SUMM; + if ( str == "random" ) + return SBT_RANDOM; + return SBT_NO_SELECTION; +} + static int str_to_weapon( const std::string &str ) { if (str == "shortsword" || str == "short sword") @@ -249,7 +277,7 @@ static char str_to_race( const std::string &str ) index -= (SP_CENTAUR - SP_RED_DRACONIAN - 1); // SP_HUMAN is at 1, therefore we must subtract one. - return ((index != -1) ? index_to_letter( index - 1 ) : '\0'); + return ((index != -1) ? index_to_letter( index - 1 ) : 0); } static char str_to_class( const std::string &str ) @@ -265,10 +293,10 @@ static char str_to_class( const std::string &str ) if (index == -1) index = get_class_index_by_name( str.c_str() ); - return ((index != -1) ? index_to_letter( index ) : '\0'); + return ((index != -1) ? index_to_letter( index ) : 0); } -static std::string & tolower_string( std::string &str ) +std::string & tolower_string( std::string &str ) { if (str.length()) { @@ -285,10 +313,10 @@ static bool read_bool( const std::string &field, bool def_value ) { bool ret = def_value; - if (field == "true" || field == "1") + if (field == "true" || field == "1" || field == "yes") ret = true; - if (field == "false" || field == "0") + if (field == "false" || field == "0" || field == "no") ret = false; return (ret); @@ -323,211 +351,379 @@ static unsigned curses_attribute(const std::string &field) return CHATTR_NORMAL; } +void game_options::add_dump_fields(const std::string &text) +{ + // Easy; chardump.cc has most of the intelligence. + append_vector(dump_order, split_string(",", text, true, true)); +} -static void reset_startup_options(bool clear_name = true) +void game_options::reset_startup_options() { - if (clear_name) - you.your_name[0] = '\0'; - Options.race = '\0'; - Options.cls = '\0'; - Options.weapon = WPN_UNKNOWN; - Options.random_pick = false; - Options.chaos_knight = GOD_NO_GOD; - Options.death_knight = DK_NO_SELECTION; - Options.priest = GOD_NO_GOD; + race = 0; + cls = 0; + weapon = WPN_UNKNOWN; + book = SBT_NO_SELECTION; + random_pick = false; + chaos_knight = GOD_NO_GOD; + death_knight = DK_NO_SELECTION; + priest = GOD_NO_GOD; } -void reset_options(bool clear_name) +void game_options::set_default_activity_interrupts() { - // Option initialization - Options.activity_interrupts[ ACT_MULTIDROP ] = - AI_HP_LOSS | AI_STAT_CHANGE | AI_TELEPORT; - Options.activity_interrupts[ ACT_RUNNING ] = - 0xFFFF ^ AI_SEE_MONSTER; - Options.activity_interrupts[ ACT_TRAVELING ] = - 0xFFFF ^ (AI_MESSAGE | AI_SEE_MONSTER); - - reset_startup_options(clear_name); - Options.prev_race = 0; - Options.prev_cls = 0; - Options.prev_ck = GOD_NO_GOD; - Options.prev_dk = DK_NO_SELECTION; - Options.prev_pr = GOD_NO_GOD; - Options.prev_weapon = WPN_UNKNOWN; - Options.prev_randpick = false; - Options.remember_name = false; - - Options.autopickups = 0x0000; - Options.verbose_dump = false; - Options.detailed_stat_dump = true; - Options.colour_map = false; - Options.clean_map = false; - Options.show_uncursed = true; - Options.always_greet = false; - Options.easy_open = true; - Options.easy_armour = true; - Options.easy_butcher = false; - Options.easy_confirm = CONFIRM_SAFE_EASY; - Options.easy_quit_item_prompts = false; - Options.hp_warning = 10; - Options.hp_attention = 25; - Options.terse_hand = true; - Options.auto_list = false; - Options.delay_message_clear = false; - Options.pickup_dropped = true; - Options.travel_colour = true; - Options.travel_delay = -1; - Options.travel_stair_cost = 500; - Options.travel_exclude_radius2 = 68; - Options.show_waypoints = true; - Options.item_colour = false; - - Options.detected_item_colour = DARKGREY; - Options.detected_monster_colour= DARKGREY; - Options.remembered_monster_colour = 0; - - Options.easy_exit_menu = true; - Options.dos_use_background_intensity = false; - Options.assign_item_slot = SS_FORWARD; - - Options.macro_meta_entry = true; + for (int adelay = 0; adelay < NUM_DELAYS; ++adelay) + for (int aint = 0; aint < NUM_AINTERRUPTS; ++aint) + activity_interrupts[adelay][aint] = true; + + const char *default_activity_interrupts[] = { + "interrupt_armour_on = hp_loss, monster_attack", + "interrupt_armour_off = interrupt_armour_on", + "interrupt_drop_item = interrupt_armour_on", + "interrupt_jewellery_on = interrupt_armour_on", + "interrupt_memorise = interrupt_armour_on, stat", + "interrupt_butcher = interrupt_armour_on, teleport, stat", + "interrupt_passwall = interrupt_butcher", + "interrupt_multidrop = interrupt_butcher", + "interrupt_macro = interrupt_multidrop", + "interrupt_travel = interrupt_butcher, statue, hungry, burden, monster", + "interrupt_run = interrupt_travel, message", + "interrupt_rest = interrupt_run", + + // Stair ascents/descents cannot be interrupted, attempts to interrupt + // the delay will just trash all queued delays, including travel. + "interrupt_ascending_stairs =", + "interrupt_descending_stairs =", + "interrupt_uninterruptible =", + "interrupt_autopickup =", + "interrupt_weapon_swap =", + + NULL + }; - // 10 was the cursor step default on Linux. - Options.level_map_cursor_step = 10; + for (int i = 0; default_activity_interrupts[i]; ++i) + read_option_line( default_activity_interrupts[i], false ); +} -#ifdef STASH_TRACKING - Options.stash_tracking = STM_NONE; +void game_options::clear_activity_interrupts( + FixedVector &eints) +{ + for (int i = 0; i < NUM_AINTERRUPTS; ++i) + eints[i] = false; +} + +void game_options::set_activity_interrupt( + FixedVector &eints, + const std::string &interrupt) +{ + if (interrupt.find(interrupt_prefix) == 0) + { + std::string delay_name = interrupt.substr( interrupt_prefix.length() ); + delay_type delay = get_delay(delay_name); + if (delay == NUM_DELAYS) + { + fprintf(stderr, "Unknown delay: %s\n", delay_name.c_str()); + return; + } + + FixedVector &refints = + activity_interrupts[delay]; + + for (int i = 0; i < NUM_AINTERRUPTS; ++i) + if (refints[i]) + eints[i] = true; + + return; + } + + activity_interrupt_type ai = get_activity_interrupt(interrupt); + if (ai == NUM_AINTERRUPTS) + { + fprintf(stderr, "Delay interrupt name \"%s\" not recognised.\n", + interrupt.c_str()); + return; + } + + eints[ai] = true; +} + +void game_options::set_activity_interrupt(const std::string &activity_name, + const std::string &interrupt_names, + bool append_interrupts) +{ + const delay_type delay = get_delay(activity_name); + if (delay == NUM_DELAYS) + { + fprintf(stderr, "Unknown delay: %s\n", activity_name.c_str()); + return; + } + + FixedVector &eints = activity_interrupts[ delay ]; + + if (!append_interrupts) + clear_activity_interrupts(eints); + + std::vector interrupts = split_string(",", interrupt_names); + for (int i = 0, size = interrupts.size(); i < size; ++i) + set_activity_interrupt(eints, interrupts[i]); + + eints[AI_FORCE_INTERRUPT] = true; +} + +void game_options::reset_options() +{ + set_default_activity_interrupts(); + + reset_startup_options(); + +#ifdef SAVE_DIR_PATH + save_dir = SAVE_DIR_PATH; +#else + save_dir.clear(); +#endif + + player_name.clear(); + + autopickup_on = true; + autoprayer_on = false; + fizzlecheck_on = false; + + prev_race = 0; + prev_cls = 0; + prev_ck = GOD_NO_GOD; + prev_dk = DK_NO_SELECTION; + prev_pr = GOD_NO_GOD; + prev_weapon = WPN_UNKNOWN; + prev_book = SBT_NO_SELECTION; + prev_randpick = false; + remember_name = false; + +#ifdef USE_ASCII_CHARACTERS + char_set = CSET_ASCII; +#else + char_set = CSET_IBM; +#endif + init_char_table(char_set); + + // set it to the .crawlrc default + autopickups = ((1L << 15) | // gold + (1L << 6) | // scrolls + (1L << 8) | // potions + (1L << 10) | // books + (1L << 7) | // jewellery + (1L << 3) | // wands + (1L << 4)); // food + verbose_dump = false; + detailed_stat_dump = true; + colour_map = true; + clean_map = false; + show_uncursed = true; + always_greet = true; + easy_open = true; + easy_unequip = true; + easy_butcher = true; + easy_confirm = CONFIRM_SAFE_EASY; + easy_quit_item_prompts = true; + hp_warning = 10; + hp_attention = 25; + confirm_self_target = true; + safe_autopickup = false; + use_notes = true; + note_skill_max = false; + note_all_spells = false; + note_hp_percent = 0; + ood_interesting = 8; + terse_hand = false; + increasing_skill_progress = false; + + // [ds] Grumble grumble. + auto_list = true; + + delay_message_clear = false; + pickup_dropped = false; + pickup_thrown = true; + + travel_colour = true; + travel_delay = 10; + travel_stair_cost = 500; + travel_exclude_radius2 = 68; + + sort_menus = false; + + tc_reachable = BLUE; + tc_excluded = LIGHTMAGENTA; + tc_exclude_circle = RED; + tc_dangerous = CYAN; + tc_disconnected = DARKGREY; + + show_waypoints = true; + item_colour = true; + + // [ds] Default to jazzy colours. + detected_item_colour = LIGHTGREEN; + detected_monster_colour= LIGHTRED; + + easy_exit_menu = true; +#ifdef DOS + dos_use_background_intensity = false; +#else + dos_use_background_intensity = true; #endif - Options.explore_stop = ES_ITEM | ES_STAIR | ES_SHOP | ES_ALTAR; - Options.target_zero_exp = true; - Options.target_wrap = false; - Options.target_oos = true; - Options.target_los_first = true; - Options.dump_kill_places = KDO_ONE_PLACE; - Options.dump_message_count = 4; - Options.dump_item_origins = IODS_ARTIFACTS | IODS_RODS; - Options.dump_item_origin_price = -1; - Options.drop_mode = DM_SINGLE; + assign_item_slot = SS_FORWARD; - Options.flush_input[ FLUSH_ON_FAILURE ] = true; - Options.flush_input[ FLUSH_BEFORE_COMMAND ] = false; - Options.flush_input[ FLUSH_ON_MESSAGE ] = false; - Options.flush_input[ FLUSH_LUA ] = true; + macro_meta_entry = true; + + // 10 was the cursor step default on Linux. + level_map_cursor_step = 10; - Options.lowercase_invocations = false; +#ifdef STASH_TRACKING + stash_tracking = STM_ALL; +#endif + explore_stop = ES_ITEM | ES_STAIR | ES_SHOP | ES_ALTAR; + safe_zero_exp = true; + target_zero_exp = false; + target_wrap = true; + target_oos = true; + target_los_first = true; + dump_kill_places = KDO_ONE_PLACE; + dump_message_count = 7; + dump_item_origins = IODS_ARTIFACTS | IODS_RODS; + dump_item_origin_price = -1; + + drop_mode = DM_SINGLE; + + flush_input[ FLUSH_ON_FAILURE ] = true; + flush_input[ FLUSH_BEFORE_COMMAND ] = false; + flush_input[ FLUSH_ON_MESSAGE ] = false; + flush_input[ FLUSH_LUA ] = true; + + lowercase_invocations = false; // Note: These fire options currently match the old behaviour. -- bwr - Options.fire_items_start = 0; // start at slot 'a' + fire_items_start = 0; // start at slot 'a' - Options.fire_order[0] = FIRE_LAUNCHER; // fire first from bow... - Options.fire_order[1] = FIRE_DART; // then only consider darts + fire_order[0] = FIRE_LAUNCHER; // fire first from bow... + fire_order[1] = FIRE_DART; // then only consider darts - // clear the reast of the list + // clear the rest of the list for (int i = 2; i < NUM_FIRE_TYPES; i++) - Options.fire_order[i] = FIRE_NONE; + fire_order[i] = FIRE_NONE; + +#ifdef WIZARD + fsim_rounds = 40000L; + fsim_mons = "worm"; + fsim_str = fsim_int = fsim_dex = 15; + fsim_xl = 10; + fsim_kit.clear(); +#endif // These are only used internally, and only from the commandline: // XXX: These need a better place. - Options.sc_entries = 0; - Options.sc_format = SCORE_REGULAR; + sc_entries = 0; + sc_format = SCORE_REGULAR; -//#ifdef USE_COLOUR_OPTS - Options.friend_brand = CHATTR_NORMAL; - Options.heap_brand = CHATTR_NORMAL; - Options.stab_brand = CHATTR_NORMAL; - Options.may_stab_brand = CHATTR_NORMAL; - Options.no_dark_brand = 0; -//#endif + friend_brand = CHATTR_NORMAL; + heap_brand = CHATTR_NORMAL; + stab_brand = CHATTR_NORMAL; + may_stab_brand = CHATTR_NORMAL; + no_dark_brand = 0; #ifdef WIZARD - Options.wiz_mode = WIZ_NO; + wiz_mode = WIZ_NO; #endif // map each colour to itself as default #ifdef USE_8_COLOUR_TERM_MAP for (int i = 0; i < 16; i++) - Options.colour[i] = i % 8; + colour[i] = i % 8; - Options.colour[ DARKGREY ] = COL_TO_REPLACE_DARKGREY; + colour[ DARKGREY ] = COL_TO_REPLACE_DARKGREY; #else for (int i = 0; i < 16; i++) - Options.colour[i] = i; + colour[i] = i; #endif // map each channel to plain (well, default for now since I'm testing) for (int i = 0; i < NUM_MESSAGE_CHANNELS; i++) - Options.channels[i] = MSGCOL_DEFAULT; + channels[i] = MSGCOL_DEFAULT; // Clear vector options. - Options.banned_objects.clear(); - Options.stop_travel.clear(); - Options.sound_mappings.clear(); - Options.menu_colour_mappings.clear(); - Options.drop_filter.clear(); - - Options.named_options.clear(); + dump_order.clear(); + add_dump_fields("header,stats,misc,inventory,skills," + "spells,mutations,messages,screenshot,kills,notes"); + + banned_objects.clear(); + note_monsters.clear(); + note_messages.clear(); + autoinscriptions.clear(); + note_items.clear(); + note_skill_levels.clear(); + travel_stop_message.clear(); + sound_mappings.clear(); + menu_colour_mappings.clear(); + drop_filter.clear(); + map_file_name.clear(); + named_options.clear(); + + clear_cset_overrides(); + clear_feature_overrides(); // Map each category to itself. The user can override in init.txt - Options.kill_map[KC_YOU] = KC_YOU; - Options.kill_map[KC_FRIENDLY] = KC_FRIENDLY; - Options.kill_map[KC_OTHER] = KC_OTHER; + kill_map[KC_YOU] = KC_YOU; + kill_map[KC_FRIENDLY] = KC_FRIENDLY; + kill_map[KC_OTHER] = KC_OTHER; // Setup travel information. What's a better place to do this? initialise_travel(); } -void read_init_file(bool runscript) +// returns where the init file was read from +std::string read_init_file(bool runscript) { - FILE *f; - char name_buff[kPathLen]; + const char* locations_data[][2] = { + { SysEnv.crawl_rc, "" }, + { SysEnv.crawl_dir, "init.txt" }, +#ifdef MULTIUSER + { SysEnv.home, "/.crawlrc" }, + { SysEnv.home, "init.txt" }, +#endif + { "", "init.txt" }, +#ifdef WIN32CONSOLE + { "", ".crawlrc" }, + { "", "_crawlrc" }, +#endif + { NULL, NULL } // placeholder to mark end + }; - reset_options(!runscript); + Options.reset_options(); - if (!runscript) - you.your_name[0] = '\0'; - - if (SysEnv.crawl_rc) - { - f = fopen(SysEnv.crawl_rc, "r"); + FILE* f = NULL; + char name_buff[kPathLen]; + // Check all possibilities for init.txt + for ( int i = 0; f == NULL && locations_data[i][1] != NULL; ++i ) { + // Don't look at unset options + if ( locations_data[i][0] != NULL ) { + snprintf( name_buff, sizeof name_buff, "%s%s", + locations_data[i][0], locations_data[i][1] ); + f = fopen( name_buff, "r" ); + } } - else if (SysEnv.crawl_dir) - { - strncpy(name_buff, SysEnv.crawl_dir, kPathLen); - name_buff[ kPathLen - 1 ] = '\0'; - strncat(name_buff, "init.txt", kPathLen); - name_buff[ kPathLen - 1 ] = '\0'; - f = fopen(name_buff, "r"); - } -#ifdef MULTIUSER - else if (SysEnv.home) + if ( f == NULL ) { - // init.txt isn't such a good choice if we're looking in - // the user's home directory, we'll use Un*x standard - strncpy(name_buff, SysEnv.home, kPathLen); - name_buff[ kPathLen - 1 ] = '\0'; - - // likely not to have a closing slash so we'll insert one... - strncat(name_buff, "/.crawlrc", kPathLen); - name_buff[ kPathLen - 1 ] = '\0'; - - f = fopen(name_buff, "r"); - } +#ifdef MULTIUSER + return "not found (~/.crawlrc missing)"; +#else + return "not found (init.txt missing from current directory)"; #endif - else - { - f = fopen("init.txt", "r"); } - if (f == NULL) - return; - + read_options(f, runscript); if (!runscript) read_startup_prefs(); - read_options(f, runscript); fclose(f); + return std::string(name_buff); } // end read_init_file() static void read_startup_prefs() @@ -536,19 +732,21 @@ static void read_startup_prefs() FILE *f = fopen(fn.c_str(), "r"); if (!f) return; - read_options(f); - fclose(f); - Options.prev_randpick = Options.random_pick; - Options.prev_weapon = Options.weapon; - Options.prev_pr = Options.priest; - Options.prev_dk = Options.death_knight; - Options.prev_ck = Options.chaos_knight; - Options.prev_cls = Options.cls; - Options.prev_race = Options.race; - Options.prev_name = you.your_name; + game_options temp; + FileLineInput fl(f); + temp.read_options(fl, false); + fclose(f); - reset_startup_options(); + Options.prev_randpick = temp.random_pick; + Options.prev_weapon = temp.weapon; + Options.prev_pr = temp.priest; + Options.prev_dk = temp.death_knight; + Options.prev_ck = temp.chaos_knight; + Options.prev_cls = temp.cls; + Options.prev_race = temp.race; + Options.prev_book = temp.book; + Options.prev_name = temp.player_name; } static void write_newgame_options(FILE *f) @@ -589,6 +787,15 @@ static void write_newgame_options(FILE *f) Options.prev_pr == GOD_YREDELEMNUL? "yredelemnul" : "random"); } + + if (Options.prev_book != SBT_NO_SELECTION ) + { + fprintf(f, "book = %s\n", + Options.prev_book == SBT_FIRE ? "fire" : + Options.prev_book == SBT_COLD ? "cold" : + Options.prev_book == SBT_SUMM ? "summ" : + "random"); + } } void write_newgame_options_file() @@ -606,15 +813,9 @@ void save_player_name() if (!Options.remember_name) return ; - std::string playername = you.your_name; - // Read other preferences read_startup_prefs(); - // Put back your name - strncpy(you.your_name, playername.c_str(), kNameLen); - you.your_name[kNameLen - 1] = 0; - // And save write_newgame_options_file(); } @@ -622,16 +823,21 @@ void save_player_name() void read_options(FILE *f, bool runscript) { FileLineInput fl(f); - read_options(fl, runscript); + Options.read_options(fl, runscript); } void read_options(const std::string &s, bool runscript) { StringLineInput st(s); - read_options(st, runscript); + Options.read_options(st, runscript); +} + +game_options::game_options() +{ + reset_options(); } -static void read_options(InitLineInput &il, bool runscript) +void game_options::read_options(InitLineInput &il, bool runscript) { unsigned int line = 0; @@ -759,7 +965,7 @@ static void read_options(InitLineInput &il, bool runscript) continue; } - parse_option_line(str, runscript); + read_option_line(str, runscript); } #ifdef CLUA_BINDINGS @@ -774,6 +980,9 @@ static void read_options(InitLineInput &il, bool runscript) } } #endif + + // Validate save_dir + check_savedir(save_dir); } static int str_to_killcategory(const std::string &s) @@ -791,15 +1000,15 @@ static int str_to_killcategory(const std::string &s) return -1; } -static void do_kill_map(const std::string &from, const std::string &to) +void game_options::do_kill_map(const std::string &from, const std::string &to) { int ifrom = str_to_killcategory(from), ito = str_to_killcategory(to); if (ifrom != -1 && ito != -1) - Options.kill_map[ifrom] = ito; + kill_map[ifrom] = ito; } -void parse_option_line(const std::string &str, bool runscript) +void game_options::read_option_line(const std::string &str, bool runscript) { std::string key = ""; std::string subkey = ""; @@ -808,6 +1017,8 @@ void parse_option_line(const std::string &str, bool runscript) int first_equals = str.find('='); int first_dot = str.find('.'); + bool plus_equal = false; + // all lines with no equal-signs we ignore if (first_equals < 0) return; @@ -828,6 +1039,15 @@ void parse_option_line(const std::string &str, bool runscript) // Clean up our data... tolower_string( trim_string( key ) ); + + // Is this a case of key += val? + if (key.length() && key[key.length() - 1] == '+') + { + plus_equal = true; + key = key.substr(0, key.length() - 1); + trim_string(key); + } + tolower_string( trim_string( subkey ) ); // some fields want capitals... none care about external spaces @@ -839,7 +1059,12 @@ void parse_option_line(const std::string &str, bool runscript) if (key != "name" && key != "crawl_dir" && key != "race" && key != "class" && key != "ban_pickup" && key != "stop_travel" && key != "sound" - && key != "drop_filter" && key != "lua_file") + && key != "travel_stop_message" + && key != "drop_filter" && key != "lua_file" + && key != "note_items" && key != "autoinscribe" + && key != "note_monsters" && key != "note_messages" + && key.find("cset") != 0 && key != "dungeon" + && key != "feature") { tolower_string( field ); } @@ -847,6 +1072,9 @@ void parse_option_line(const std::string &str, bool runscript) // everything not a valid line is treated as a comment if (key == "autopickup") { + // clear out autopickup + autopickups = 0L; + for (size_t i = 0; i < field.length(); i++) { char type = field[i]; @@ -880,7 +1108,7 @@ void parse_option_line(const std::string &str, bool runscript) ; if (j < obj_syms_len) - Options.autopickups |= (1L << j); + autopickups |= (1L << j); else { fprintf( stderr, "Bad object type '%c' for autopickup.\n", @@ -891,59 +1119,100 @@ void parse_option_line(const std::string &str, bool runscript) else if (key == "name") { // field is already cleaned up from trim_string() - strncpy(you.your_name, field.c_str(), kNameLen); - you.your_name[ kNameLen - 1 ] = '\0'; + player_name = field; } - else if (key == "verbose_dump") + else if (key == "char_set" || key == "ascii_display") { - // gives verbose info in char dumps - Options.verbose_dump = read_bool( field, Options.verbose_dump ); + bool valid = true; + + if (key == "ascii_display") + { + char_set = + read_bool(field, char_set == CSET_ASCII)? + CSET_ASCII + : CSET_IBM; + valid = true; + } + else + { + if (field == "ascii") + char_set = CSET_ASCII; + else if (field == "ibm") + char_set = CSET_IBM; + else if (field == "dec") + char_set = CSET_DEC; + else + { + fprintf( stderr, "Bad character set: %s\n", field.c_str() ); + valid = false; + } + } + if (valid) + init_char_table(char_set); + } + else if (key == "default_autopickup") + { + // should autopickup default to on or off? + autopickup_on = read_bool( field, autopickup_on ); + } + else if (key == "default_autoprayer") + { + // should autoprayer default to on or off? + autoprayer_on = read_bool( field, autoprayer_on ); + } + else if (key == "default_fizzlecheck") + { + // should fizzlecheck default to on or off? + fizzlecheck_on = read_bool( field, fizzlecheck_on ); } else if (key == "detailed_stat_dump") { - Options.detailed_stat_dump = - read_bool( field, Options.detailed_stat_dump ); + detailed_stat_dump = + read_bool( field, detailed_stat_dump ); } else if (key == "clean_map") { // removes monsters/clouds from map - Options.clean_map = read_bool( field, Options.clean_map ); + clean_map = read_bool( field, clean_map ); } else if (key == "colour_map" || key == "color_map") { // colour-codes play-screen map - Options.colour_map = read_bool( field, Options.colour_map ); + colour_map = read_bool( field, colour_map ); } else if (key == "easy_confirm") { // allows both 'Y'/'N' and 'y'/'n' on yesno() prompts if (field == "none") - Options.easy_confirm = CONFIRM_NONE_EASY; + easy_confirm = CONFIRM_NONE_EASY; else if (field == "safe") - Options.easy_confirm = CONFIRM_SAFE_EASY; + easy_confirm = CONFIRM_SAFE_EASY; else if (field == "all") - Options.easy_confirm = CONFIRM_ALL_EASY; + easy_confirm = CONFIRM_ALL_EASY; } - else if (key == "easy_quit_item_lists") + else if (key == "easy_quit_item_lists" + || key == "easy_quit_item_prompts") { // allow aborting of item lists with space - Options.easy_quit_item_prompts = read_bool( field, - Options.easy_quit_item_prompts ); + easy_quit_item_prompts = read_bool( field, + easy_quit_item_prompts ); } else if (key == "easy_open") { // automatic door opening with movement - Options.easy_open = read_bool( field, Options.easy_open ); + easy_open = read_bool( field, easy_open ); } - else if (key == "easy_armour" || key == "easy_armour") + else if (key == "easy_armor" + || key == "easy_armour" + || key == "easy_unequip") { // automatic removal of armour when dropping - Options.easy_armour = read_bool( field, Options.easy_armour ); + easy_unequip = read_bool( field, easy_unequip ); } else if (key == "easy_butcher") { // automatic knife switching - Options.easy_butcher = read_bool( field, Options.easy_butcher ); + easy_butcher = read_bool( field, easy_butcher ); } else if (key == "lua_file" && runscript) { @@ -960,7 +1229,7 @@ void parse_option_line(const std::string &str, bool runscript) const int result_col = str_to_colour( field ); if (orig_col != -1 && result_col != -1) - Options.colour[orig_col] = result_col; + colour[orig_col] = result_col; else { fprintf( stderr, "Bad colour -- %s=%d or %s=%d\n", @@ -973,7 +1242,7 @@ void parse_option_line(const std::string &str, bool runscript) const int col = str_to_channel_colour( field ); if (chnl != -1 && col != -1) - Options.channels[chnl] = col; + channels[chnl] = col; else if (chnl == -1) fprintf( stderr, "Bad channel -- %s\n", subkey.c_str() ); else if (col == -1) @@ -986,7 +1255,7 @@ void parse_option_line(const std::string &str, bool runscript) const int col = str_to_colour( field ); if (col != -1) - Options.background = col; + background = col; else fprintf( stderr, "Bad colour -- %s\n", field.c_str() ); @@ -995,7 +1264,7 @@ void parse_option_line(const std::string &str, bool runscript) { const int col = str_to_colour( field ); if (col != -1) - Options.detected_item_colour = col; + detected_item_colour = col; else fprintf( stderr, "Bad detected_item_colour -- %s\n", field.c_str()); @@ -1004,102 +1273,116 @@ void parse_option_line(const std::string &str, bool runscript) { const int col = str_to_colour( field ); if (col != -1) - Options.detected_monster_colour = col; + detected_monster_colour = col; else fprintf( stderr, "Bad detected_monster_colour -- %s\n", field.c_str()); } - else if (key == "remembered_monster_colour") + else if (key.find(interrupt_prefix) == 0) { - if (field == "real") - Options.remembered_monster_colour = 0xFFFFU; - else if (field == "auto") - Options.remembered_monster_colour = 0; - else { - const int col = str_to_colour( field ); - if (col != -1) - Options.remembered_monster_colour = col; - else - fprintf( stderr, "Bad remembered_monster_colour -- %s\n", - field.c_str()); - } + set_activity_interrupt(key.substr(interrupt_prefix.length()), + field, + plus_equal); + } + else if (key.find("cset") == 0) + { + std::string cset = key.substr(4); + if (!cset.empty() && cset[0] == '_') + cset = cset.substr(1); + + char_set_type cs = NUM_CSET; + if (cset == "ascii") + cs = CSET_ASCII; + else if (cset == "ibm") + cs = CSET_IBM; + else if (cset == "dec") + cs = CSET_DEC; + + add_cset_override(cs, field); + } + else if (key == "feature" || key == "dungeon") + { + std::vector defs = split_string(";", field); + + for (int i = 0, size = defs.size(); i < size; ++i) + add_feature_override( defs[i] ); } else if (key == "friend_brand") { // Use curses attributes to mark friend // Some may look bad on some terminals. // As a suggestion, try "rxvt -rv -fn 10x20" under Un*xes - Options.friend_brand = curses_attribute(field); + friend_brand = curses_attribute(field); } else if (key == "stab_brand") { - Options.stab_brand = curses_attribute(field); + stab_brand = curses_attribute(field); } else if (key == "may_stab_brand") { - Options.may_stab_brand = curses_attribute(field); + may_stab_brand = curses_attribute(field); } else if (key == "no_dark_brand") { // This is useful for terms where dark grey does // not have standout modes (since it's black on black). // This option will use light-grey instead in these cases. - Options.no_dark_brand = read_bool( field, Options.no_dark_brand ); + no_dark_brand = read_bool( field, no_dark_brand ); } else if (key == "heap_brand") { // See friend_brand option upstairs. no_dark_brand applies // here as well. - Options.heap_brand = curses_attribute(field); - } - else if (key == "show_uncursed") - { - // label known uncursed items as "uncursed" - Options.show_uncursed = read_bool( field, Options.show_uncursed ); + heap_brand = curses_attribute(field); } else if (key == "always_greet") { // show greeting when reloading game - Options.always_greet = read_bool( field, Options.always_greet ); + always_greet = read_bool( field, always_greet ); } else if (key == "weapon") { // choose this weapon for classes that get choice - Options.weapon = str_to_weapon( field ); + weapon = str_to_weapon( field ); + } + else if (key == "book") + { + // choose this book for classes that get choice + book = str_to_book( field ); } else if (key == "chaos_knight") { // choose god for Chaos Knights if (field == "xom") - Options.chaos_knight = GOD_XOM; + chaos_knight = GOD_XOM; else if (field == "makhleb") - Options.chaos_knight = GOD_MAKHLEB; + chaos_knight = GOD_MAKHLEB; else if (field == "random") - Options.chaos_knight = GOD_RANDOM; + chaos_knight = GOD_RANDOM; } else if (key == "death_knight") { if (field == "necromancy") - Options.death_knight = DK_NECROMANCY; + death_knight = DK_NECROMANCY; else if (field == "yredelemnul") - Options.death_knight = DK_YREDELEMNUL; + death_knight = DK_YREDELEMNUL; else if (field == "random") - Options.death_knight = DK_RANDOM; + death_knight = DK_RANDOM; } else if (key == "priest") { // choose this weapon for classes that get choice if (field == "zin") - Options.priest = GOD_ZIN; + priest = GOD_ZIN; else if (field == "yredelemnul") - Options.priest = GOD_YREDELEMNUL; + priest = GOD_YREDELEMNUL; else if (field == "random") - Options.priest = GOD_RANDOM; + priest = GOD_RANDOM; } else if (key == "fire_items_start") { if (isalpha( field[0] )) - Options.fire_items_start = letter_to_index( field[0] ); + fire_items_start = letter_to_index( field[0] ); else { fprintf( stderr, "Bad fire item start index -- %s\n", @@ -1109,39 +1392,69 @@ void parse_option_line(const std::string &str, bool runscript) else if (key == "assign_item_slot") { if (field == "forward") - Options.assign_item_slot = SS_FORWARD; + assign_item_slot = SS_FORWARD; else if (field == "backward") - Options.assign_item_slot = SS_BACKWARD; + assign_item_slot = SS_BACKWARD; } else if (key == "fire_order") { - str_to_fire_order( field, Options.fire_order ); + str_to_fire_order( field, fire_order ); } else if (key == "random_pick") { // randomly generate character - Options.random_pick = read_bool( field, Options.random_pick ); + random_pick = read_bool( field, random_pick ); } else if (key == "remember_name") { - Options.remember_name = read_bool( field, Options.remember_name ); + remember_name = read_bool( field, remember_name ); } + else if (key == "save_dir") + { + // If SAVE_DIR_PATH was defined, there are very likely security issues + // with allowing the user to specify a different directory. +#ifndef SAVE_DIR_PATH + save_dir = field; +#endif + } else if (key == "hp_warning") { - Options.hp_warning = atoi( field.c_str() ); - if (Options.hp_warning < 0 || Options.hp_warning > 100) + hp_warning = atoi( field.c_str() ); + if (hp_warning < 0 || hp_warning > 100) { - Options.hp_warning = 0; + hp_warning = 0; fprintf( stderr, "Bad HP warning percentage -- %s\n", field.c_str() ); } } + else if (key == "ood_interesting") + { + ood_interesting = atoi( field.c_str() ); + } + else if (key == "note_monsters") + { + append_vector(note_monsters, split_string(",", field)); + } + else if (key == "note_messages") + { + append_vector(note_messages, split_string(",", field)); + } + else if (key == "note_hp_percent") + { + note_hp_percent = atoi( field.c_str() ); + if (note_hp_percent < 0 || note_hp_percent > 100) + { + note_hp_percent = 0; + fprintf( stderr, "Bad HP note percentage -- %s\n", + field.c_str() ); + } + } else if (key == "hp_attention") { - Options.hp_attention = atoi( field.c_str() ); - if (Options.hp_attention < 0 || Options.hp_attention > 100) + hp_attention = atoi( field.c_str() ); + if (hp_attention < 0 || hp_attention > 100) { - Options.hp_attention = 0; + hp_attention = 0; fprintf( stderr, "Bad HP attention percentage -- %s\n", field.c_str() ); } @@ -1155,158 +1468,242 @@ void parse_option_line(const std::string &str, bool runscript) if (SysEnv.crawl_dir) { - strncpy(SysEnv.crawl_dir, field.c_str(), kNameLen - 1); - SysEnv.crawl_dir[ kNameLen - 1 ] = '\0'; + strncpy(SysEnv.crawl_dir, field.c_str(), kPathLen - 1); + SysEnv.crawl_dir[ kPathLen - 1 ] = 0; } } else if (key == "race") { - Options.race = str_to_race( field ); + race = str_to_race( field ); - if (Options.race == '\0') + if (race == 0) fprintf( stderr, "Unknown race choice: %s\n", field.c_str() ); } else if (key == "class") { - Options.cls = str_to_class( field ); + cls = str_to_class( field ); - if (Options.cls == '\0') + if (cls == 0) fprintf( stderr, "Unknown class choice: %s\n", field.c_str() ); } else if (key == "auto_list") { - Options.auto_list = read_bool( field, Options.auto_list ); + auto_list = read_bool( field, auto_list ); + } + else if (key == "confirm_self_target") + { + confirm_self_target = read_bool( field, confirm_self_target ); + } + else if (key == "safe_autopickup") + { + safe_autopickup = read_bool( field, safe_autopickup ); + } + else if (key == "use_notes") + { + use_notes = read_bool( field, use_notes ); + } + else if (key == "note_skill_max") + { + note_skill_max = read_bool( field, note_skill_max ); + } + else if (key == "note_all_spells") + { + note_all_spells = read_bool( field, note_all_spells ); } else if (key == "delay_message_clear") { - Options.delay_message_clear = read_bool( field, Options.delay_message_clear ); + delay_message_clear = read_bool( field, delay_message_clear ); } else if (key == "terse_hand") { - Options.terse_hand = read_bool( field, Options.terse_hand ); + terse_hand = read_bool( field, terse_hand ); + } + else if (key == "increasing_skill_progress") + { + increasing_skill_progress = read_bool( field, increasing_skill_progress ); } else if (key == "flush") { if (subkey == "failure") { - Options.flush_input[FLUSH_ON_FAILURE] - = read_bool(field, Options.flush_input[FLUSH_ON_FAILURE]); + flush_input[FLUSH_ON_FAILURE] + = read_bool(field, flush_input[FLUSH_ON_FAILURE]); } else if (subkey == "command") { - Options.flush_input[FLUSH_BEFORE_COMMAND] - = read_bool(field, Options.flush_input[FLUSH_BEFORE_COMMAND]); + flush_input[FLUSH_BEFORE_COMMAND] + = read_bool(field, flush_input[FLUSH_BEFORE_COMMAND]); } else if (subkey == "message") { - Options.flush_input[FLUSH_ON_MESSAGE] - = read_bool(field, Options.flush_input[FLUSH_ON_MESSAGE]); + flush_input[FLUSH_ON_MESSAGE] + = read_bool(field, flush_input[FLUSH_ON_MESSAGE]); } else if (subkey == "lua") { - Options.flush_input[FLUSH_LUA] - = read_bool(field, Options.flush_input[FLUSH_LUA]); + flush_input[FLUSH_LUA] + = read_bool(field, flush_input[FLUSH_LUA]); } } else if (key == "lowercase_invocations") { - Options.lowercase_invocations - = read_bool(field, Options.lowercase_invocations); + lowercase_invocations + = read_bool(field, lowercase_invocations); } else if (key == "wiz_mode") { // wiz_mode is recognized as a legal key in all compiles -- bwr #ifdef WIZARD if (field == "never") - Options.wiz_mode = WIZ_NEVER; + wiz_mode = WIZ_NEVER; else if (field == "no") - Options.wiz_mode = WIZ_NO; + wiz_mode = WIZ_NO; else if (field == "yes") - Options.wiz_mode = WIZ_YES; + wiz_mode = WIZ_YES; else fprintf(stderr, "Unknown wiz_mode option: %s\n", field.c_str()); #endif } else if (key == "ban_pickup") { - append_vector(Options.banned_objects, split_string(",", field)); + append_vector(banned_objects, split_string(",", field)); + } + else if (key == "note_items") + { + append_vector(note_items, split_string(",", field)); + } + else if (key == "autoinscribe") + { + std::vector thesplit = + split_string(":", field); + autoinscriptions.push_back( + std::pair(thesplit[0], + thesplit[1])); + } + else if (key == "map_file_name") + { + map_file_name = field; + } + else if (key == "note_skill_levels") + { + std::vector thesplit = split_string(",", field); + for ( unsigned i = 0; i < thesplit.size(); ++i ) { + int num = atoi(thesplit[i].c_str()); + if ( num > 0 && num <= 27 ) { + note_skill_levels.push_back(num); + } + else { + fprintf(stderr, "Bad skill level to note -- %s\n", + thesplit[i].c_str()); + continue; + } + } } else if (key == "pickup_thrown") { - Options.pickup_thrown = read_bool(field, Options.pickup_thrown); + pickup_thrown = read_bool(field, pickup_thrown); } else if (key == "pickup_dropped") { - Options.pickup_dropped = read_bool(field, Options.pickup_dropped); + pickup_dropped = read_bool(field, pickup_dropped); } - else if (key == "show_waypoints") +#ifdef WIZARD + else if (key == "fsim_kit") { - Options.show_waypoints = read_bool(field, Options.show_waypoints); + append_vector(fsim_kit, split_string(",", field)); + } + else if (key == "fsim_rounds") + { + fsim_rounds = atol(field.c_str()); + if (fsim_rounds < 1000) + fsim_rounds = 1000; + if (fsim_rounds > 500000L) + fsim_rounds = 500000L; + } + else if (key == "fsim_mons") + { + fsim_mons = field; + } + else if (key == "fsim_str") + { + fsim_str = atoi(field.c_str()); + } + else if (key == "fsim_int") + { + fsim_int = atoi(field.c_str()); + } + else if (key == "fsim_dex") + { + fsim_dex = atoi(field.c_str()); + } + else if (key == "fsim_xl") + { + fsim_xl = atoi(field.c_str()); + } +#endif // WIZARD + else if (key == "sort_menus") + { + sort_menus = read_bool(field, sort_menus); } else if (key == "travel_delay") { // Read travel delay in milliseconds. - Options.travel_delay = atoi( field.c_str() ); - if (Options.travel_delay < -1) - Options.travel_delay = -1; - if (Options.travel_delay > 2000) - Options.travel_delay = 2000; + travel_delay = atoi( field.c_str() ); + if (travel_delay < -1) + travel_delay = -1; + if (travel_delay > 2000) + travel_delay = 2000; } else if (key == "level_map_cursor_step") { - Options.level_map_cursor_step = atoi( field.c_str() ); - if (Options.level_map_cursor_step < 1) - Options.level_map_cursor_step = 1; - if (Options.level_map_cursor_step > 50) - Options.level_map_cursor_step = 50; + level_map_cursor_step = atoi( field.c_str() ); + if (level_map_cursor_step < 1) + level_map_cursor_step = 1; + if (level_map_cursor_step > 50) + level_map_cursor_step = 50; } else if (key == "macro_meta_entry") { - Options.macro_meta_entry = read_bool(field, Options.macro_meta_entry); - } - else if (key == "travel_stair_cost") - { - Options.travel_stair_cost = atoi( field.c_str() ); - if (Options.travel_stair_cost < 1) - Options.travel_stair_cost = 1; - else if (Options.travel_stair_cost > 1000) - Options.travel_stair_cost = 1000; + macro_meta_entry = read_bool(field, macro_meta_entry); } else if (key == "travel_exclude_radius2") { - Options.travel_exclude_radius2 = atoi( field.c_str() ); - if (Options.travel_exclude_radius2 < 0) - Options.travel_exclude_radius2 = 0; - else if (Options.travel_exclude_radius2 > 400) - Options.travel_exclude_radius2 = 400; + travel_exclude_radius2 = atoi( field.c_str() ); + if (travel_exclude_radius2 < 0) + travel_exclude_radius2 = 0; + else if (travel_exclude_radius2 > 400) + travel_exclude_radius2 = 400; } - else if (key == "stop_travel") + else if (key == "stop_travel" || key == "travel_stop_message") { std::vector fragments = split_string(",", field); - for (int i = 0, count = fragments.size(); i < count; ++i) { + for (int i = 0, count = fragments.size(); i < count; ++i) + { if (fragments[i].length() == 0) continue; std::string::size_type pos = fragments[i].find(":"); - if (pos && pos != std::string::npos) { + if (pos && pos != std::string::npos) + { std::string prefix = fragments[i].substr(0, pos); int channel = str_to_channel( prefix ); - if (channel != -1 || prefix == "any") { + if (channel != -1 || prefix == "any") + { std::string s = fragments[i].substr( pos + 1 ); trim_string( s ); - Options.stop_travel.push_back( + travel_stop_message.push_back( message_filter( channel, s ) ); continue; } } - Options.stop_travel.push_back( + travel_stop_message.push_back( message_filter( fragments[i] ) ); } } else if (key == "drop_filter") { - append_vector(Options.drop_filter, split_string(",", field)); + append_vector(drop_filter, split_string(",", field)); } else if (key == "travel_avoid_terrain") { @@ -1314,48 +1711,64 @@ void parse_option_line(const std::string &str, bool runscript) for (int i = 0, count = seg.size(); i < count; ++i) prevent_travel_to( seg[i] ); } - else if (key == "travel_colour") + else if (key == "tc_reachable") + { + tc_reachable = str_to_colour(field, tc_reachable); + } + else if (key == "tc_excluded") + { + tc_excluded = str_to_colour(field, tc_excluded); + } + else if (key == "tc_exclude_circle") { - Options.travel_colour = read_bool(field, Options.travel_colour); + tc_exclude_circle = + str_to_colour(field, tc_exclude_circle); } - else if (key == "item_colour") + else if (key == "tc_dangerous") { - Options.item_colour = read_bool(field, Options.item_colour); + tc_dangerous = str_to_colour(field, tc_dangerous); + } + else if (key == "tc_disconnected") + { + tc_disconnected = str_to_colour(field, tc_disconnected); + } + else if (key == "item_colour" || key == "item_color") + { + item_colour = read_bool(field, item_colour); } else if (key == "easy_exit_menu") { - Options.easy_exit_menu = read_bool(field, Options.easy_exit_menu); + easy_exit_menu = read_bool(field, easy_exit_menu); } else if (key == "dos_use_background_intensity") { - Options.dos_use_background_intensity = - read_bool(field, Options.dos_use_background_intensity); + dos_use_background_intensity = + read_bool(field, dos_use_background_intensity); } else if (key == "explore_stop") { - Options.explore_stop = ES_NONE; + explore_stop = ES_NONE; std::vector stops = split_string(",", field); for (int i = 0, count = stops.size(); i < count; ++i) { const std::string &c = stops[i]; if (c == "item" || c == "items") - Options.explore_stop |= ES_ITEM; + explore_stop |= ES_ITEM; else if (c == "shop" || c == "shops") - Options.explore_stop |= ES_SHOP; + explore_stop |= ES_SHOP; else if (c == "stair" || c == "stairs") - Options.explore_stop |= ES_STAIR; + explore_stop |= ES_STAIR; else if (c == "altar" || c == "altars") - Options.explore_stop |= ES_ALTAR; + explore_stop |= ES_ALTAR; } } #ifdef STASH_TRACKING else if (key == "stash_tracking") { - Options.stash_tracking = - field == "explicit"? STM_EXPLICIT : + stash_tracking = field == "dropped" ? STM_DROPPED : field == "all" ? STM_ALL : - STM_NONE; + STM_EXPLICIT; } else if (key == "stash_filter") { @@ -1374,7 +1787,7 @@ void parse_option_line(const std::string &str, bool runscript) sound_mapping mapping; mapping.pattern = sub.substr(0, cpos); mapping.soundfile = sub.substr(cpos + 1); - Options.sound_mappings.push_back(mapping); + sound_mappings.push_back(mapping); } } } @@ -1389,13 +1802,20 @@ void parse_option_line(const std::string &str, bool runscript) mapping.pattern = sub.substr(cpos + 1); mapping.colour = str_to_colour(sub.substr(0, cpos)); if (mapping.colour != -1) - Options.menu_colour_mappings.push_back(mapping); + menu_colour_mappings.push_back(mapping); } } } + else if (key == "dump_order") + { + if (!plus_equal) + dump_order.clear(); + + add_dump_fields(field); + } else if (key == "dump_kill_places") { - Options.dump_kill_places = + dump_kill_places = field == "none"? KDO_NO_PLACES : field == "all" ? KDO_ALL_PLACES : KDO_ONE_PLACE; @@ -1417,66 +1837,66 @@ void parse_option_line(const std::string &str, bool runscript) else if (key == "dump_message_count") { // Capping is implicit - Options.dump_message_count = atoi( field.c_str() ); + dump_message_count = atoi( field.c_str() ); } else if (key == "dump_item_origins") { - Options.dump_item_origins = IODS_PRICE; + dump_item_origins = IODS_PRICE; std::vector choices = split_string(",", field); for (int i = 0, count = choices.size(); i < count; ++i) { const std::string &ch = choices[i]; if (ch == "artifacts") - Options.dump_item_origins |= IODS_ARTIFACTS; + dump_item_origins |= IODS_ARTIFACTS; else if (ch == "ego_arm" || ch == "ego armour" || ch == "ego_armour") - Options.dump_item_origins |= IODS_EGO_ARMOUR; + dump_item_origins |= IODS_EGO_ARMOUR; else if (ch == "ego_weap" || ch == "ego weapon" || ch == "ego_weapon" || ch == "ego weapons" || ch == "ego_weapons") - Options.dump_item_origins |= IODS_EGO_WEAPON; + dump_item_origins |= IODS_EGO_WEAPON; else if (ch == "jewellery" || ch == "jewelry") - Options.dump_item_origins |= IODS_JEWELLERY; + dump_item_origins |= IODS_JEWELLERY; else if (ch == "runes") - Options.dump_item_origins |= IODS_RUNES; + dump_item_origins |= IODS_RUNES; else if (ch == "rods") - Options.dump_item_origins |= IODS_RODS; + dump_item_origins |= IODS_RODS; else if (ch == "staves") - Options.dump_item_origins |= IODS_STAVES; + dump_item_origins |= IODS_STAVES; else if (ch == "books") - Options.dump_item_origins |= IODS_BOOKS; + dump_item_origins |= IODS_BOOKS; else if (ch == "all" || ch == "everything") - Options.dump_item_origins = IODS_EVERYTHING; + dump_item_origins = IODS_EVERYTHING; } } else if (key == "dump_item_origin_price") { - Options.dump_item_origin_price = atoi( field.c_str() ); - if (Options.dump_item_origin_price < -1) - Options.dump_item_origin_price = -1; + dump_item_origin_price = atoi( field.c_str() ); + if (dump_item_origin_price < -1) + dump_item_origin_price = -1; } - else if (key == "target_zero_exp") + else if (key == "safe_zero_exp") { - Options.target_zero_exp = read_bool(field, Options.target_zero_exp); + safe_zero_exp = read_bool(field, safe_zero_exp); } - else if (key == "target_wrap") + else if (key == "target_zero_exp") { - Options.target_wrap = read_bool(field, Options.target_wrap); + target_zero_exp = read_bool(field, target_zero_exp); } else if (key == "target_oos") { - Options.target_oos = read_bool(field, Options.target_oos); + target_oos = read_bool(field, target_oos); } else if (key == "target_los_first") { - Options.target_los_first = read_bool(field, Options.target_los_first); + target_los_first = read_bool(field, target_los_first); } else if (key == "drop_mode") { if (field.find("multi") != std::string::npos) - Options.drop_mode = DM_MULTI; + drop_mode = DM_MULTI; else - Options.drop_mode = DM_SINGLE; + drop_mode = DM_SINGLE; } // Catch-all else, copies option into map else @@ -1490,7 +1910,7 @@ void parse_option_line(const std::string &str, bool runscript) if (clua.error.length()) mpr(clua.error.c_str()); #endif - Options.named_options[key] = orig_field; + named_options[key] = orig_field; } } } @@ -1530,8 +1950,19 @@ static const char *cmd_ops[] = { "scores", "name", "race", "class", const int num_cmd_ops = 10; bool arg_seen[num_cmd_ops]; +static void set_crawl_path(const std::string &path) +{ + const std::string::size_type slash = path.rfind(FILE_SEPARATOR); + SysEnv.crawl_executable_path = + slash != std::string::npos? path.substr(0, slash + 1) + : std::string(""); +} + bool parse_args( int argc, char **argv, bool rc_only ) { + if (argc >= 1) + set_crawl_path(argv[0]); + if (argc < 2) // no args! return (true); @@ -1630,10 +2061,7 @@ bool parse_args( int argc, char **argv, bool rc_only ) return (false); if (!rc_only) - { - strncpy(you.your_name, next_arg, kNameLen); - you.your_name[ kNameLen - 1 ] = '\0'; - } + Options.player_name = next_arg; nextUsed = true; break; @@ -1673,12 +2101,8 @@ bool parse_args( int argc, char **argv, bool rc_only ) if (!rc_only) { - viewwindow = &viewwindow3; - mapch = &mapchar3; - mapch2 = &mapchar4; -#ifdef UNIX - character_set = 0; -#endif + Options.char_set = CSET_ASCII; + init_char_table(Options.char_set); } break; @@ -1709,3 +2133,61 @@ bool parse_args( int argc, char **argv, bool rc_only ) return (true); } + +////////////////////////////////////////////////////////////////////////// +// game_options + +int game_options::o_int(const char *name, int def) const +{ + int val = def; + opt_map::const_iterator i = named_options.find(name); + if (i != named_options.end()) + { + val = atoi(i->second.c_str()); + } + return (val); +} + +long game_options::o_long(const char *name, long def) const +{ + long val = def; + opt_map::const_iterator i = named_options.find(name); + if (i != named_options.end()) + { + const char *s = i->second.c_str(); + char *es = NULL; + long num = strtol(s, &es, 10); + if (s != (const char *) es && es) + val = num; + } + return (val); +} + +bool game_options::o_bool(const char *name, bool def) const +{ + bool val = def; + opt_map::const_iterator i = named_options.find(name); + if (i != named_options.end()) + val = read_bool(i->second, val); + return (val); +} + +std::string game_options::o_str(const char *name, const char *def) const +{ + std::string val; + opt_map::const_iterator i = named_options.find(name); + if (i != named_options.end()) + val = i->second; + else if (def) + val = def; + return (val); +} + +int game_options::o_colour(const char *name, int def) const +{ + std::string val = o_str(name); + trim_string(val); + tolower_string(val); + int col = str_to_colour(val); + return (col == -1? def : col); +} diff --git a/crawl-ref/source/initfile.h b/crawl-ref/source/initfile.h index d5860fdd77..ffe3ff5c30 100644 --- a/crawl-ref/source/initfile.h +++ b/crawl-ref/source/initfile.h @@ -3,6 +3,8 @@ * Summary: Simple reading of init file. * Written by: David Loewenstern * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> 6/9/99 DML Created @@ -16,12 +18,14 @@ #include std::string & trim_string( std::string &str ); +std::string & tolower_string( std::string &str ); +int str_to_colour( const std::string &str, int default_colour = -1 ); // last updated 12may2000 {dlb} /* *********************************************************************** * called from: acr * *********************************************************************** */ -void read_init_file(bool runscript = false); +std::string read_init_file(bool runscript = false); void read_options(FILE *f, bool runscript = false); @@ -29,6 +33,8 @@ void read_options(const std::string &s, bool runscript = false); void parse_option_line(const std::string &line, bool runscript = false); +void apply_ascii_display(bool ascii); + // last updated 12may2000 {dlb} /* *********************************************************************** * called from: acr diff --git a/crawl-ref/source/insult.cc b/crawl-ref/source/insult.cc index 1e8c629ab9..bb3b62f11b 100644 --- a/crawl-ref/source/insult.cc +++ b/crawl-ref/source/insult.cc @@ -4,6 +4,8 @@ // to link with Linley Henzel's Dungeon Crawl (or Crawl) without change to // Crawl's license. // +// Modified for Crawl Reference by $Author$ on $Date$ +// // The goal of this stuff is catachronistic feel. #include "AppHdr.h" diff --git a/crawl-ref/source/invent.cc b/crawl-ref/source/invent.cc index 2145f80f72..437b01c4fc 100644 --- a/crawl-ref/source/invent.cc +++ b/crawl-ref/source/invent.cc @@ -3,6 +3,8 @@ * Summary: Functions for inventory related commands. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <5> 10/9/99 BCR Added wizard help screen @@ -35,199 +37,341 @@ #include "view.h" #include "menu.h" -const char *command_string( int i ); -const char *wizard_string( int i ); +/////////////////////////////////////////////////////////////////////////////// +// Inventory menu shenanigans + +static void get_inv_items_to_show( + std::vector &v, int selector); -struct InvTitle : public MenuEntry +InvTitle::InvTitle( Menu *mn, const std::string &title, + invtitle_annotator tfn ) + : MenuEntry( title, MEL_TITLE ) { - Menu *m; - std::string (*titlefn)( int menuflags, const std::string &oldt ); - - InvTitle( Menu *mn, const char *title, - std::string (*tfn)( int menuflags, const std::string &oldt ) ) - : MenuEntry( title ) + m = mn; + titlefn = tfn; +} + +std::string InvTitle::get_text() const +{ + return titlefn? titlefn( m, MenuEntry::get_text() ) : + MenuEntry::get_text(); +} + +InvEntry::InvEntry( const item_def &i ) : MenuEntry( "", MEL_ITEM ), item( &i ) +{ + data = const_cast( item ); + + char buf[ITEMNAME_SIZE]; + if (i.base_type == OBJ_GOLD) + snprintf(buf, sizeof buf, "%d gold piece%s", i.quantity, + (i.quantity > 1? "s" : "")); + else + item_name(i, + in_inventory(i)? + DESC_INVENTORY_EQUIP : DESC_NOCAP_A, buf, false); + text = buf; + + if (i.base_type != OBJ_GOLD && in_inventory(i)) { - m = mn; - titlefn = tfn; + // FIXME: This is HORRIBLE! We're skipping the inventory letter prefix + // which looks like this: "a - ". + text = text.substr( 4 ); + add_hotkey(index_to_letter( i.link )); } - - std::string get_text() const + else { - return titlefn? titlefn( m->get_flags(), MenuEntry::get_text() ) : - MenuEntry::get_text(); + // Dummy hotkey for gold or non-inventory items. + add_hotkey(' '); } -}; + add_class_hotkeys(i); + + quantity = i.quantity; +} -class InvShowPrices; -class InvEntry : public MenuEntry +std::string InvEntry::get_text() const { -private: - static bool show_prices; - static char temp_id[4][50]; - static void set_show_prices(bool doshow); + char buf[ITEMNAME_SIZE]; + char suffix[ITEMNAME_SIZE] = ""; - friend class InvShowPrices; -public: - const item_def *item; + if (InvEntry::show_prices) + { + int value = item_value(*item, temp_id, true); + if (value > 0) + snprintf(suffix, sizeof suffix, + " (%d gold)", value); + } + snprintf( buf, sizeof buf, + " %c %c %s%s", + hotkeys[0], + (!selected_qty? '-' : selected_qty < quantity? '#' : '+'), + text.c_str(), + suffix ); + return (buf); +} - InvEntry( const item_def &i ) : MenuEntry( "", MEL_ITEM ), item( &i ) +void InvEntry::add_class_hotkeys(const item_def &i) +{ + switch (i.base_type) { - data = const_cast( item ); - - char buf[ITEMNAME_SIZE]; - if (i.base_type == OBJ_GOLD) - snprintf(buf, sizeof buf, "%d gold piece%s", i.quantity, - (i.quantity > 1? "s" : "")); - else - item_name(i, - in_inventory(i)? - DESC_INVENTORY_EQUIP : DESC_NOCAP_A, buf, false); - text = buf; + case OBJ_GOLD: + add_hotkey('$'); + break; + case OBJ_MISSILES: + add_hotkey('('); + break; + case OBJ_WEAPONS: + add_hotkey(')'); + break; + case OBJ_ARMOUR: + add_hotkey('['); + break; + case OBJ_WANDS: + add_hotkey('/'); + break; + case OBJ_FOOD: + add_hotkey('%'); + break; + case OBJ_BOOKS: + add_hotkey('+'); + add_hotkey(':'); + break; + case OBJ_SCROLLS: + add_hotkey('?'); + break; + case OBJ_JEWELLERY: + add_hotkey(i.sub_type >= AMU_RAGE? '"' : '='); + break; + case OBJ_POTIONS: + add_hotkey('!'); + break; + case OBJ_STAVES: + add_hotkey('\\'); + add_hotkey('|'); + break; + case OBJ_MISCELLANY: + add_hotkey('}'); + break; + case OBJ_CORPSES: + add_hotkey('&'); + break; + default: + break; + } +} - if (i.base_type != OBJ_GOLD) - { - if (in_inventory(i)) - { - text = text.substr( 4 ); // Skip the inventory letter. - add_hotkey(index_to_letter( i.link )); - } - else - add_hotkey(' '); // Dummy hotkey - } - else - { - // Dummy hotkey for gold. - add_hotkey(' '); - } - add_class_hotkeys(i); +bool InvEntry::show_prices = false; +char InvEntry::temp_id[4][50]; - quantity = i.quantity; +void InvEntry::set_show_prices(bool doshow) +{ + if ((show_prices = doshow)) + { + memset(temp_id, 1, sizeof temp_id); } +} + +InvShowPrices::InvShowPrices(bool doshow) +{ + InvEntry::set_show_prices(doshow); +} + +InvShowPrices::~InvShowPrices() +{ + InvEntry::set_show_prices(false); +} + +// Returns vector of item_def pointers to each item_def in the given +// vector. Note: make sure the original vector stays around for the lifetime +// of the use of the item pointers, or mayhem results! +std::vector +InvMenu::xlat_itemvect(const std::vector &v) +{ + std::vector xlatitems; + for (unsigned i = 0, size = v.size(); i < size; ++i) + xlatitems.push_back( &v[i] ); + return (xlatitems); +} + +void InvMenu::set_type(menu_type t) +{ + type = t; +} + +void InvMenu::set_title_annotator(invtitle_annotator afn) +{ + title_annotate = afn; +} + +void InvMenu::set_title(MenuEntry *t) +{ + Menu::set_title(t); +} - std::string get_text() const +void InvMenu::set_preselect(const std::vector *pre) +{ + pre_select = pre; +} + +void InvMenu::set_title(const std::string &s) +{ + std::string stitle = s; + if (stitle.empty()) { - char buf[ITEMNAME_SIZE]; - char suffix[ITEMNAME_SIZE] = ""; + const int cap = carrying_capacity(); - if (InvEntry::show_prices) - { - int value = item_value(*item, temp_id, true); - if (value > 0) - snprintf(suffix, sizeof suffix, - " (%d gold)", value); - } - snprintf( buf, sizeof buf, - "%c %c %s%s", - hotkeys[0], - (!selected_qty? '-' : selected_qty < quantity? '#' : '+'), - text.c_str(), - suffix ); - return (buf); + char title_buf[200]; + snprintf( title_buf, sizeof title_buf, + "Inventory: %d.%d/%d.%d aum (%d%%, %d/52 slots)", + you.burden / 10, you.burden % 10, + cap / 10, cap % 10, + (you.burden * 100) / cap, + inv_count() ); + stitle = title_buf; } -private: - void add_class_hotkeys(const item_def &i) + + set_title(new InvTitle(this, stitle, title_annotate)); +} + +void InvMenu::load_inv_items(int item_selector, + MenuEntry *(*procfn)(MenuEntry *me)) +{ + std::vector tobeshown; + get_inv_items_to_show(tobeshown, item_selector); + + load_items(tobeshown, procfn); + if (!item_count()) { - switch (i.base_type) + std::string s; + switch (item_selector) { - case OBJ_GOLD: - add_hotkey('$'); - break; - case OBJ_MISSILES: - add_hotkey('('); + case OSEL_ANY: + s = "You aren't carrying anything."; break; + case OSEL_WIELD: case OBJ_WEAPONS: - add_hotkey(')'); - break; - case OBJ_ARMOUR: - add_hotkey('['); - break; - case OBJ_WANDS: - add_hotkey('/'); - break; - case OBJ_FOOD: - add_hotkey('%'); - break; - case OBJ_BOOKS: - add_hotkey('+'); - add_hotkey(':'); - break; - case OBJ_SCROLLS: - add_hotkey('?'); - break; - case OBJ_JEWELLERY: - add_hotkey(i.sub_type >= AMU_RAGE? '"' : '='); - break; - case OBJ_POTIONS: - add_hotkey('!'); - break; - case OBJ_STAVES: - add_hotkey('\\'); - add_hotkey('|'); - break; - case OBJ_MISCELLANY: - add_hotkey('}'); - break; - case OBJ_CORPSES: - add_hotkey('&'); + s = "You aren't carrying any weapons."; break; default: + s = "You aren't carrying any such object."; break; } + set_title(s); } -}; + else + { + set_title(""); + } +} -bool InvEntry::show_prices = false; -char InvEntry::temp_id[4][50]; +static bool compare_menu_entries(const MenuEntry* a, const MenuEntry* b) +{ + return (*a < *b); +} -void InvEntry::set_show_prices(bool doshow) +void InvMenu::load_items(const std::vector &mitems, + MenuEntry *(*procfn)(MenuEntry *me)) { - if ((show_prices = doshow)) + FixedVector< int, NUM_OBJECT_CLASSES > inv_class(0); + for (int i = 0, count = mitems.size(); i < count; ++i) + inv_class[ mitems[i]->base_type ]++; + + menu_letter ckey; + std::vector items_in_class; + + for (int i = 0; i < NUM_OBJECT_CLASSES; ++i) { - memset(temp_id, 1, sizeof temp_id); + if (!inv_class[i]) continue; + + add_entry( new MenuEntry( item_class_name(i), MEL_SUBTITLE ) ); + items_in_class.clear(); + + for (int j = 0, count = mitems.size(); j < count; ++j) + { + if (mitems[j]->base_type != i) continue; + items_in_class.push_back( new InvEntry(*mitems[j]) ); + } + + if (Options.sort_menus) + std::sort( items_in_class.begin(), items_in_class.end(), + compare_menu_entries ); + + for (unsigned int j = 0; j < items_in_class.size(); ++j) + { + InvEntry *ie = items_in_class[j]; + // If there's no hotkey, provide one. + if (ie->hotkeys[0] == ' ') + ie->hotkeys[0] = ckey++; + do_preselect(ie); + + add_entry( procfn? (*procfn)(ie) : ie ); + } } } -class InvShowPrices { -public: - InvShowPrices(bool doshow = true) - { - InvEntry::set_show_prices(doshow); - } - ~InvShowPrices() - { - InvEntry::set_show_prices(false); - } -}; +void InvMenu::do_preselect(InvEntry *ie) +{ + if (!pre_select || pre_select->empty()) + return; -class InvMenu : public Menu + for (int i = 0, size = pre_select->size(); i < size; ++i) + if (ie->item && ie->item == (*pre_select)[i].item) + { + ie->selected_qty = (*pre_select)[i].quantity; + break; + } +} + +std::vector InvMenu::get_selitems() const { -public: - InvMenu(int flags = MF_MULTISELECT) : Menu(flags) { } -protected: - bool process_key(int key); -}; + std::vector selected_items; + for (int i = 0, count = sel.size(); i < count; ++i) + { + InvEntry *inv = dynamic_cast(sel[i]); + selected_items.push_back( + SelItem( + inv->hotkeys[0], + inv->selected_qty, + inv->item ) ); + } + return (selected_items); +} bool InvMenu::process_key( int key ) { - if (items.size() && (key == CONTROL('D') || key == '@')) + if (items.size() + && type == MT_DROP + && (key == CONTROL('D') || key == '@')) { - int newflag = + int newflag = is_set(MF_MULTISELECT)? - MF_SINGLESELECT | MF_EASY_EXIT | MF_ANYPRINTABLE + MF_SINGLESELECT | MF_ANYPRINTABLE : MF_MULTISELECT; - flags &= ~(MF_SINGLESELECT | MF_MULTISELECT | - MF_EASY_EXIT | MF_ANYPRINTABLE); + flags &= ~(MF_SINGLESELECT | MF_MULTISELECT | MF_ANYPRINTABLE); flags |= newflag; deselect_all(); - sel->clear(); - draw_select_count(0); + sel.clear(); + draw_select_count(0, true); return true; } return Menu::process_key( key ); } +unsigned char InvMenu::getkey() const +{ + unsigned char mkey = lastch; + // Fake an ESCAPE if the menu is empty. + if (!item_count()) + mkey = ESCAPE; + if (!isalnum(mkey) && mkey != '$' && mkey != '-' && mkey != '?' + && mkey != '*' && mkey != ESCAPE) + mkey = ' '; + return (mkey); +} + +////////////////////////////////////////////////////////////////////////////// + bool in_inventory( const item_def &i ) { return i.x == -1 && i.y == -1; @@ -235,10 +379,8 @@ bool in_inventory( const item_def &i ) unsigned char get_invent( int invent_type ) { - unsigned char nothing = invent_select( invent_type ); - + unsigned char nothing = invent_select(NULL, MT_INVLIST, invent_type); redraw_screen(); - return (nothing); } // end get_invent() @@ -291,84 +433,20 @@ std::string item_class_name( int type, bool terse ) return (""); } -void populate_item_menu( Menu *menu, const std::vector &items, - void (*callback)(MenuEntry *me) ) -{ - FixedVector< int, NUM_OBJECT_CLASSES > inv_class; - for (int i = 0; i < NUM_OBJECT_CLASSES; ++i) - inv_class[i] = 0; - for (int i = 0, count = items.size(); i < count; ++i) - inv_class[ items[i].base_type ]++; - - menu_letter ckey; - for (int i = 0; i < NUM_OBJECT_CLASSES; ++i) - { - if (!inv_class[i]) continue; - - menu->add_entry( new MenuEntry( item_class_name(i), MEL_SUBTITLE ) ); - - for (int j = 0, count = items.size(); j < count; ++j) - { - if (items[j].base_type != i) continue; - - InvEntry *ie = new InvEntry( items[j] ); - ie->hotkeys[0] = ckey++; - callback(ie); - - menu->add_entry( ie ); - } - } -} - -std::vector select_items( std::vector &items, - const char *title ) +std::vector select_items( const std::vector &items, + const char *title, bool noselect ) { std::vector selected; - - if (items.empty()) - return selected; - - FixedVector< int, NUM_OBJECT_CLASSES > inv_class; - for (int i = 0; i < NUM_OBJECT_CLASSES; ++i) - inv_class[i] = 0; - for (int i = 0, count = items.size(); i < count; ++i) - inv_class[ items[i]->base_type ]++; - - Menu menu; - menu.set_title( new MenuEntry(title) ); - - char ckey = 'a'; - for (int i = 0; i < NUM_OBJECT_CLASSES; ++i) + if (!items.empty()) { - if (!inv_class[i]) continue; - - menu.add_entry( new MenuEntry( item_class_name(i), MEL_SUBTITLE ) ); - - for (int j = 0, count = items.size(); j < count; ++j) - { - if (items[j]->base_type != i) continue; - - InvEntry *ie = new InvEntry( *items[j] ); - ie->hotkeys[0] = ckey; - - menu.add_entry( ie ); - - ckey = ckey == 'z'? 'A' : - ckey == 'Z'? 'a' : - ckey + 1; - } - } - menu.set_flags( MF_MULTISELECT | MF_SELECT_ANY_PAGE ); - std::vector< MenuEntry * > sel = menu.show(); - for (int i = 0, count = sel.size(); i < count; ++i) - { - InvEntry *inv = (InvEntry *) sel[i]; - selected.push_back( SelItem( inv->hotkeys[0], - inv->selected_qty, - inv->item ) ); + InvMenu menu; + menu.set_title(title); + menu.load_items(items); + menu.set_flags(noselect ? MF_NOSELECT : MF_MULTISELECT); + menu.show(); + selected = menu.get_selitems(); } - - return selected; + return (selected); } static bool item_class_selected(const item_def &i, int selector) @@ -409,7 +487,7 @@ static bool is_item_selected(const item_def &i, int selector) || userdef_item_selected(i, selector); } -static void get_inv_items_to_show(std::vector &v, int selector) +static void get_inv_items_to_show(std::vector &v, int selector) { for (int i = 0; i < ENDOFPACK; i++) { @@ -418,127 +496,45 @@ static void get_inv_items_to_show(std::vector &v, int selector) } } -static void set_vectitem_classes(const std::vector &v, - FixedVector &fv) -{ - for (int i = 0; i < NUM_OBJECT_CLASSES; i++) - fv[i] = 0; - - for (int i = 0, size = v.size(); i < size; i++) - fv[ v[i]->base_type ]++; -} - unsigned char invent_select( + const char *title, + menu_type type, int item_selector, int flags, - std::string (*titlefn)( int menuflags, - const std::string &oldt ), + invtitle_annotator titlefn, std::vector *items, std::vector *filter, - Menu::selitem_tfn selitemfn ) + Menu::selitem_tfn selitemfn, + const std::vector *pre_select ) { - InvMenu menu; + InvMenu menu(flags); - menu.selitem_text = selitemfn; + menu.set_preselect(pre_select); + menu.set_title_annotator(titlefn); + menu.f_selitem = selitemfn; if (filter) menu.set_select_filter( *filter ); + menu.load_inv_items(item_selector); + menu.set_type(type); - FixedVector< int, NUM_OBJECT_CLASSES > inv_class2; - for (int i = 0; i < NUM_OBJECT_CLASSES; i++) - inv_class2[i] = 0; + // Don't override title if there are no items. + if (title && menu.item_count()) + menu.set_title(title); - int inv_count = 0; + menu.show(true); - for (int i = 0; i < ENDOFPACK; i++) - { - if (you.inv[i].quantity) - { - inv_class2[ you.inv[i].base_type ]++; - inv_count++; - } - } - - if (!inv_count) - { - menu.set_title( new MenuEntry( "You aren't carrying anything." ) ); - menu.show(); - return 0; - } - - std::vector tobeshown; - get_inv_items_to_show( tobeshown, item_selector ); - set_vectitem_classes( tobeshown, inv_class2 ); - - if (tobeshown.size()) - { - const int cap = carrying_capacity(); - - char title_buf[200]; - snprintf( title_buf, sizeof title_buf, - "Inventory: %d.%d/%d.%d aum (%d%%, %d/52 slots)", - you.burden / 10, you.burden % 10, - cap / 10, cap % 10, - (you.burden * 100) / cap, - inv_count ); - menu.set_title( new InvTitle( &menu, title_buf, titlefn ) ); - - for (int i = 0; i < 15; i++) - { - if (inv_class2[i] != 0) - { - std::string s = item_class_name(i); - menu.add_entry( new MenuEntry( s, MEL_SUBTITLE ) ); - - for (int j = 0, size = tobeshown.size(); j < size; ++j) - { - if (tobeshown[j]->base_type == i) - { - menu.add_entry( new InvEntry( *tobeshown[j] ) ); - } - } // end of j loop - } // end of if inv_class2 - } // end of i loop. - } - else - { - std::string s; - if (item_selector == -1) - s = "You aren't carrying anything."; - else - { - if (item_selector == OBJ_WEAPONS || item_selector == OSEL_WIELD) - s = "You aren't carrying any weapons."; - else if (item_selector == OBJ_MISSILES) - s = "You aren't carrying any ammunition."; - else - s = "You aren't carrying any such object."; - } - menu.set_title( new MenuEntry( s ) ); - } - - menu.set_flags( flags ); - std::vector< MenuEntry * > sel = menu.show(); if (items) - { - for (int i = 0, count = sel.size(); i < count; ++i) - items->push_back( SelItem( sel[i]->hotkeys[0], - sel[i]->selected_qty ) ); - } + *items = menu.get_selitems(); - unsigned char mkey = menu.getkey(); - if (!isalnum(mkey) && mkey != '$' && mkey != '-' && mkey != '?' - && mkey != '*') - mkey = ' '; - return mkey; + return (menu.getkey()); } unsigned char invent( int item_class_inv, bool show_price ) { InvShowPrices show_item_prices(show_price); - return (invent_select(item_class_inv)); + return (invent_select(NULL, MT_INVLIST, item_class_inv)); } // end invent() - // Reads in digits for a count and apprends then to val, the // return value is the character that stopped the reading. static unsigned char get_invent_quant( unsigned char keyin, int &quant ) @@ -577,31 +573,31 @@ static unsigned char get_invent_quant( unsigned char keyin, int &quant ) // Note: This function never checks if the item is appropriate. std::vector prompt_invent_items( const char *prompt, + menu_type mtype, int type_expect, - std::string (*titlefn)( int menuflags, - const std::string &oldt ), + invtitle_annotator titlefn, bool allow_auto_list, bool allow_easy_quit, const char other_valid_char, std::vector *select_filter, - Menu::selitem_tfn fn ) + Menu::selitem_tfn fn, + const std::vector *pre_select ) { unsigned char keyin = 0; - int ret = -1; + int ret = PROMPT_ABORT; bool need_redraw = false; bool need_prompt = true; bool need_getch = true; + bool auto_list = Options.auto_list && allow_auto_list; - if (Options.auto_list && allow_auto_list) + if (auto_list) { - need_getch = false; - need_redraw = false; - need_prompt = false; + need_prompt = need_getch = false; keyin = '?'; } - std::vector< SelItem > items; + std::vector items; int count = -1; for (;;) { @@ -630,16 +626,21 @@ std::vector prompt_invent_items( } else if (keyin == '?' || keyin == '*' || keyin == ',') { - int selmode = Options.drop_mode == DM_SINGLE? - MF_SINGLESELECT | MF_EASY_EXIT | MF_ANYPRINTABLE : - MF_MULTISELECT; + int selmode = + Options.drop_mode == DM_SINGLE + && (!pre_select || pre_select->empty())? + MF_SINGLESELECT | MF_EASY_EXIT | MF_ANYPRINTABLE : + MF_MULTISELECT; // The "view inventory listing" mode. int ch = invent_select( - keyin == '*'? -1 : type_expect, - selmode | MF_SELECT_ANY_PAGE, - titlefn, &items, select_filter, fn ); - - if (selmode & MF_SINGLESELECT) + prompt, + mtype, + keyin == '*'? OSEL_ANY : type_expect, + selmode, + titlefn, &items, select_filter, fn, + pre_select ); + + if ((selmode & MF_SINGLESELECT) || ch == ESCAPE) { keyin = ch; need_getch = false; @@ -653,11 +654,11 @@ std::vector prompt_invent_items( if (items.size()) { redraw_screen(); - mesclr( true ); + mesclr(true); - for (int i = 0, count = items.size(); i < count; ++i) + for (unsigned int i = 0; i < items.size(); ++i) items[i].slot = letter_to_index( items[i].slot ); - return items; + return (items); } need_redraw = !(keyin == '?' || keyin == '*' @@ -694,6 +695,11 @@ std::vector prompt_invent_items( // we've got a character we don't understand... canned_msg( MSG_HUH ); } + else + { + // We're going to loop back up, so don't draw another prompt. + need_prompt = false; + } } if (ret != PROMPT_ABORT) @@ -701,6 +707,52 @@ std::vector prompt_invent_items( return items; } +static int digit_to_index( char digit, operation_types oper ) { + + int i; + unsigned int j; + char iletter = (char)(oper); + + for ( i = 0; i < ENDOFPACK; ++i ) { + if (is_valid_item(you.inv[i])) { + const std::string& r(you.inv[i].inscription); + /* note that r.size() is unsigned */ + for ( j = 0; j + 2 < r.size(); ++j ) { + if ( r[j] == '@' && + (r[j+1] == iletter || r[j+1] == '*') && + r[j+2] == digit ) { + return i; + } + } + } + } + return -1; +} + +/* return true if user OK'd it (or no warning), false otherwise */ +static bool check_warning_inscriptions( const item_def& item, + operation_types oper ) +{ + unsigned int i; + char iletter = (char)(oper); + char name[ITEMNAME_SIZE]; + char prompt[ITEMNAME_SIZE + 100]; + item_name(item, DESC_INVENTORY, name, false); + strcpy( prompt, "Really choose "); + strncat( prompt, name, ITEMNAME_SIZE ); + strcat( prompt, "?"); + + const std::string& r(item.inscription); + for ( i = 0; i + 1 < r.size(); ++i ) { + if ( r[i] == '!' && + (r[i+1] == iletter || r[i+1] == '*') ) { + + return yesno(prompt, false, 'n'); + } + } + return true; +} + // This function prompts the user for an item, handles the '?' and '*' // listings, and returns the inventory slot to the caller (which if // must_exist is true (the default) will be an assigned item, with @@ -710,45 +762,26 @@ std::vector prompt_invent_items( // It returns PROMPT_GOT_SPECIAL if the player hits the "other_valid_char". // // Note: This function never checks if the item is appropriate. -int prompt_invent_item( const char *prompt, int type_expect, +int prompt_invent_item( const char *prompt, + menu_type mtype, int type_expect, bool must_exist, bool allow_auto_list, bool allow_easy_quit, const char other_valid_char, - int *const count ) + int *const count, + operation_types oper ) { unsigned char keyin = 0; - int ret = -1; + int ret = PROMPT_ABORT; bool need_redraw = false; bool need_prompt = true; bool need_getch = true; + bool auto_list = Options.auto_list && allow_auto_list; - if (Options.auto_list && allow_auto_list) + if (auto_list) { - std::vector< SelItem > items; - - // pretend the player has hit '?' and setup state. - keyin = invent_select( type_expect, - MF_SINGLESELECT | MF_ANYPRINTABLE - | MF_EASY_EXIT, - NULL, &items ); - - if (items.size()) - { - if (count) - *count = items[0].quantity; - redraw_screen(); - mesclr( true ); - return letter_to_index( keyin ); - } - - need_getch = false; - - // Don't redraw if we're just going to display another listing - need_redraw = (keyin != '?' && keyin != '*'); - - // A prompt is nice for when we're moving to "count" mode. - need_prompt = (count != NULL && isdigit( keyin )); + need_prompt = need_getch = false; + keyin = '?'; } for (;;) @@ -780,11 +813,14 @@ int prompt_invent_item( const char *prompt, int type_expect, { // The "view inventory listing" mode. std::vector< SelItem > items; - keyin = invent_select( keyin == '*'? OSEL_ANY : type_expect, - MF_SINGLESELECT | MF_ANYPRINTABLE - | MF_EASY_EXIT, - NULL, - &items ); + keyin = invent_select( + prompt, + mtype, + keyin == '*'? OSEL_ANY : type_expect, + MF_SINGLESELECT | MF_ANYPRINTABLE + | MF_EASY_EXIT, + NULL, + &items ); if (items.size()) { @@ -799,9 +835,7 @@ int prompt_invent_item( const char *prompt, int type_expect, // Don't redraw if we're just going to display another listing need_redraw = (keyin != '?' && keyin != '*'); - - // A prompt is nice for when we're moving to "count" mode. - need_prompt = (count != NULL && isdigit( keyin )); + need_prompt = need_redraw; } else if (count != NULL && isdigit( keyin )) { @@ -811,6 +845,17 @@ int prompt_invent_item( const char *prompt, int type_expect, need_prompt = false; need_getch = false; } + /*** HP CHANGE ***/ + else if ( count == NULL && isdigit( keyin ) ) + { + /* scan for our item */ + int res = digit_to_index( keyin, oper ); + if ( res != -1 ) { + ret = res; + if ( check_warning_inscriptions( you.inv[ret], oper ) ) + break; + } + } else if (keyin == ESCAPE || (Options.easy_quit_item_prompts && allow_easy_quit @@ -825,203 +870,23 @@ int prompt_invent_item( const char *prompt, int type_expect, if (must_exist && !is_valid_item( you.inv[ret] )) mpr( "You do not have any such object." ); - else - break; + else { + if ( check_warning_inscriptions( you.inv[ret], oper ) ) { + break; + } + } } else if (!isspace( keyin )) { // we've got a character we don't understand... canned_msg( MSG_HUH ); } - } - - return (ret); -} - -void list_commands(bool wizard) -{ - const char *line; - int j = 0; - -#ifdef DOS_TERM - char buffer[4800]; - - window(1, 1, 80, 25); - gettext(1, 1, 80, 25, buffer); -#endif - - clrscr(); - - // BCR - Set to screen length - 1 to display the "more" string - int moreLength = (get_number_of_lines() - 1) * 2; - - for (int i = 0; i < 500; i++) - { - if (wizard) - line = wizard_string( i ); else - line = command_string( i ); - - if (strlen( line ) != 0) { - // BCR - If we've reached the end of the screen, clear - if (j == moreLength) - { - gotoxy(2, j / 2 + 1); - cprintf("More..."); - getch(); - clrscr(); - j = 0; - } - - gotoxy( ((j % 2) ? 40 : 2), ((j / 2) + 1) ); - cprintf( line ); - - j++; + // We're going to loop back up, so don't draw another prompt. + need_prompt = false; } } - getch(); - -#ifdef DOS_TERM - puttext(1, 1, 80, 25, buffer); -#endif - - return; -} // end list_commands() - -const char *wizard_string( int i ) -{ - UNUSED( i ); - -#ifdef WIZARD - return((i == 10) ? "a : acquirement" : - (i == 13) ? "A : set all skills to level" : - (i == 15) ? "b : controlled blink" : - (i == 20) ? "B : banish yourself to the Abyss" : - (i == 30) ? "g : add a skill" : - (i == 35) ? "G : remove all monsters" : - (i == 40) ? "h/H : heal yourself (super-Heal)" : - (i == 50) ? "i/I : identify/unidentify inventory": - (i == 70) ? "l : make entrance to labyrinth" : - (i == 80) ? "m/M : create monster by number/name": - (i == 90) ? "o/%% : create an object" : - (i == 100) ? "p : make entrance to pandemonium" : - (i == 110) ? "x : gain an experience level" : - (i == 115) ? "r : change character's species" : - (i == 120) ? "s : gain 20000 skill points" : - (i == 130) ? "S : set skill to level" : - (i == 140) ? "t : tweak object properties" : - (i == 150) ? "X : Receive a gift from Xom" : - (i == 160) ? "z/Z : cast any spell by number/name": - (i == 200) ? "$ : get 1000 gold" : - (i == 210) ? " : create up/down staircase" : - (i == 220) ? "u/d : shift up/down one level" : - (i == 230) ? "~/\" : goto a level" : - (i == 240) ? "( : create a feature" : - (i == 250) ? "] : get a mutation" : - (i == 260) ? "[ : get a demonspawn mutation" : - (i == 270) ? ": : find branch" : - (i == 280) ? "{ : magic mapping" : - (i == 290) ? "^ : gain piety" : - (i == 300) ? "_ : gain religion" : - (i == 310) ? "\' : list items" : - (i == 320) ? "? : list wizard commands" : - (i == 330) ? "| : acquire all unrand artefacts" : - (i == 340) ? "+ : turn item into random artefact" : - (i == 350) ? "= : sum skill points" - : ""); - -#else - return (""); -#endif -} // end wizard_string() - -const char *command_string( int i ) -{ - /* - * BCR - Command printing, case statement - * Note: The numbers in this case indicate the order in which the - * commands will be printed out. Make sure none of these - * numbers is greater than 500, because that is the limit. - * - * Arranged alpha lower, alpha upper, punctuation, ctrl. - * - */ - - return((i == 10) ? "a : use special ability" : - (i == 20) ? "d(#) : drop (exact quantity of) items" : - (i == 30) ? "e : eat food" : - (i == 40) ? "f : fire first available missile" : - (i == 50) ? "i : inventory listing" : - (i == 55) ? "m : check skills" : - (i == 60) ? "o/c : open / close a door" : - (i == 65) ? "p : pray" : - (i == 70) ? "q : quaff a potion" : - (i == 80) ? "r : read a scroll or book" : - (i == 90) ? "s : search adjacent tiles" : - (i == 100) ? "t : throw/shoot an item" : - (i == 110) ? "v : view item description" : - (i == 120) ? "w : wield an item" : - (i == 130) ? "x : examine visible surroundings" : - (i == 135) ? "z : zap a wand" : - (i == 140) ? "A : list abilities/mutations" : - (i == 141) ? "C : check experience" : - (i == 142) ? "D : dissect a corpse" : - (i == 145) ? "E : evoke power of wielded item" : - (i == 150) ? "M : memorise a spell" : - (i == 155) ? "O : overview of the dungeon" : - (i == 160) ? "P/R : put on / remove jewellery" : - (i == 165) ? "Q : quit without saving" : - (i == 168) ? "S : save game and exit" : - (i == 179) ? "V : version information" : - (i == 200) ? "W/T : wear / take off armour" : - (i == 210) ? "X : examine level map" : - (i == 220) ? "Z : cast a spell" : - (i == 240) ? ",/g : pick up items" : - (i == 242) ? "./del: rest one turn" : - (i == 250) ? " : ascend / descend a staircase" : - (i == 270) ? "; : examine occupied tile" : - (i == 280) ? "\\ : check item knowledge" : -#ifdef WIZARD - (i == 290) ? "& : invoke your Wizardly powers" : -#endif - (i == 300) ? "+/- : scroll up/down [level map only]" : - (i == 310) ? "! : shout or command allies" : - (i == 325) ? "^ : describe religion" : - (i == 337) ? "@ : status" : - (i == 340) ? "# : dump character to file" : - (i == 350) ? "= : reassign inventory/spell letters" : - (i == 360) ? "\' : wield item a, or switch to b" : -#ifdef USE_MACROS - (i == 380) ? "` : add macro" : - (i == 390) ? "~ : save macros" : -#endif - (i == 400) ? "] : display worn armour" : - (i == 410) ? "\" : display worn jewellery" : - (i == 420) ? "Ctrl-P : see old messages" : -#ifdef PLAIN_TERM - (i == 430) ? "Ctrl-R : Redraw screen" : -#endif - (i == 440) ? "Ctrl-A : toggle autopickup" : - (i == 450) ? "Ctrl-X : Save game without query" : - -#ifdef ALLOW_DESTROY_ITEM_COMMAND - (i == 451) ? "Ctrl-D : Destroy inventory item" : -#endif - (i == 453) ? "Ctrl-G : interlevel travel" : - (i == 455) ? "Ctrl-O : explore" : - -#ifdef STASH_TRACKING - (i == 456) ? "Ctrl-S : mark stash" : - (i == 457) ? "Ctrl-E : forget stash" : - (i == 458) ? "Ctrl-F : search stashes" : -#endif - - (i == 460) ? "Shift & DIR : long walk" : - (i == 465) ? "/ DIR : long walk" : - (i == 470) ? "Ctrl & DIR : door; untrap; attack" : - (i == 475) ? "* DIR : door; untrap; attack" : - (i == 478) ? "Shift & 5 on keypad : rest 100 turns" - : ""); -} // end command_string() + return (ret); +} diff --git a/crawl-ref/source/invent.h b/crawl-ref/source/invent.h index 624e603fc1..566bf42b44 100644 --- a/crawl-ref/source/invent.h +++ b/crawl-ref/source/invent.h @@ -3,6 +3,8 @@ * Summary: Functions for inventory related commands. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -15,6 +17,7 @@ #include #include #include "menu.h" +#include "enum.h" #define PROMPT_ABORT -1 #define PROMPT_GOT_SPECIAL -2 @@ -32,28 +35,124 @@ struct SelItem } }; +typedef std::string (*invtitle_annotator)( + const Menu *m, const std::string &oldtitle); + +struct InvTitle : public MenuEntry +{ + Menu *m; + invtitle_annotator titlefn; + + InvTitle( Menu *mn, const std::string &title, + invtitle_annotator tfn ); + + std::string get_text() const; +}; -int prompt_invent_item( const char *prompt, int type_expect, +class InvShowPrices; +class InvEntry : public MenuEntry +{ +private: + static bool show_prices; + static char temp_id[4][50]; + static void set_show_prices(bool doshow); + + friend class InvShowPrices; + +public: + const item_def *item; + + InvEntry( const item_def &i ); + std::string get_text() const; + +private: + void add_class_hotkeys(const item_def &i); +}; + +class InvShowPrices { +public: + InvShowPrices(bool doshow = true); + ~InvShowPrices(); +}; + +class InvMenu : public Menu +{ +public: + InvMenu(int mflags = MF_MULTISELECT) + : Menu(mflags), type(MT_INVSELECT), pre_select(NULL), + title_annotate(NULL) + { + } + + unsigned char getkey() const; + + void set_preselect(const std::vector *pre); + void set_type(menu_type t); + + // Sets function to annotate the title with meta-information if needed. + // If you set this, do so *before* calling set_title, or it won't take + // effect. + void set_title_annotator(invtitle_annotator fn); + + void set_title(MenuEntry *title); + void set_title(const std::string &s); + + // Loads items into the menu. If "procfn" is provided, it'll be called + // for each MenuEntry added. + // NOTE: Does not set menu title, ever! You *must* set the title explicitly + void load_items(const std::vector &items, + MenuEntry *(*procfn)(MenuEntry *me) = NULL); + + // Loads items from the player's inventory into the menu, and sets the + // title to the stock title. If "procfn" is provided, it'll be called for + // each MenuEntry added, *excluding the title*. + void load_inv_items(int item_selector = OSEL_ANY, + MenuEntry *(*procfn)(MenuEntry *me) = NULL); + + std::vector get_selitems() const; + + // Returns vector of item_def pointers to each item_def in the given + // vector. Note: make sure the original vector stays around for the lifetime + // of the use of the item pointers, or mayhem results! + static std::vector xlat_itemvect( + const std::vector &); +protected: + bool process_key(int key); + void do_preselect(InvEntry *ie); + +protected: + menu_type type; + const std::vector *pre_select; + + invtitle_annotator title_annotate; +}; + + +int prompt_invent_item( const char *prompt, + menu_type type, + int type_expect, bool must_exist = true, bool allow_auto_list = true, bool allow_easy_quit = true, const char other_valid_char = '\0', - int *const count = NULL ); + int *const count = NULL, + operation_types oper = OPER_ANY ); -std::vector select_items( std::vector &items, - const char *title ); +std::vector select_items( + const std::vector &items, + const char *title, bool noselect = false ); std::vector prompt_invent_items( const char *prompt, + menu_type type, int type_expect, - std::string (*titlefn)( int menuflags, - const std::string &oldt ) - = NULL, + invtitle_annotator titlefn = NULL, bool allow_auto_list = true, bool allow_easy_quit = true, const char other_valid_char = '\0', std::vector *filter = NULL, - Menu::selitem_tfn fn = NULL ); + Menu::selitem_tfn fn = NULL, + const std::vector *pre_select = NULL ); // last updated 12may2000 {dlb} @@ -62,14 +161,18 @@ std::vector prompt_invent_items( * *********************************************************************** */ unsigned char invent( int item_class_inv, bool show_price ); -unsigned char invent_select(int item_class_inv, - int select_flags = MF_NOSELECT, - std::string (*titlefn)( int menuflags, - const std::string &oldt ) - = NULL, +unsigned char invent_select( + // Use NULL for stock Inventory title + const char *title = NULL, + // MT_DROP allows the multidrop toggle + menu_type type = MT_INVLIST, + int item_selector = OSEL_ANY, + int menu_select_flags = MF_NOSELECT, + invtitle_annotator titlefn = NULL, std::vector *sels = NULL, std::vector *filter = NULL, - Menu::selitem_tfn fn = NULL ); + Menu::selitem_tfn fn = NULL, + const std::vector *pre_select = NULL ); // last updated 24may2000 {dlb} /* *********************************************************************** @@ -87,7 +190,4 @@ void list_commands(bool wizard); std::string item_class_name(int type, bool terse = false); -void populate_item_menu( Menu *menu, const std::vector &items, - void (*callback)(MenuEntry *me) ); - #endif diff --git a/crawl-ref/source/it_use2.cc b/crawl-ref/source/it_use2.cc index 45449716d9..0878c7dbba 100644 --- a/crawl-ref/source/it_use2.cc +++ b/crawl-ref/source/it_use2.cc @@ -3,6 +3,8 @@ * Summary: Functions for using wands, potions, and weapon/armour removal.4\3 * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * 26jun2000 jmf added ZAP_MAGMA @@ -105,7 +107,7 @@ bool potion_effect( char pot_eff, int pow ) if (you.might > 80) you.might = 80; - naughty( NAUGHTY_STIMULANTS, 4 + random2(4) ); + did_god_conduct( DID_STIMULANTS, 4 + random2(4) ); } break; @@ -279,14 +281,14 @@ bool potion_effect( char pot_eff, int pow ) mutate(100, false); } - naughty(NAUGHTY_STIMULANTS, 4 + random2(4)); + did_god_conduct(DID_STIMULANTS, 4 + random2(4)); break; } return (effect); } // end potion_effect() -void unwield_item(char unw) +void unwield_item(char unw, bool showMsgs) { you.special_wield = SPWLD_NONE; you.wield_change = true; @@ -298,10 +300,12 @@ void unwield_item(char unw) switch (you.inv[unw].special) { case SPWPN_SINGING_SWORD: - mpr("The Singing Sword sighs."); + if (showMsgs) + mpr("The Singing Sword sighs."); break; case SPWPN_WRATH_OF_TROG: - mpr("You feel less violent."); + if (showMsgs) + mpr("You feel less violent."); break; case SPWPN_SCYTHE_OF_CURSES: case SPWPN_STAFF_OF_OLGREB: @@ -330,42 +334,56 @@ void unwield_item(char unw) char str_pass[ ITEMNAME_SIZE ]; in_name(unw, DESC_CAP_YOUR, str_pass); strcpy(info, str_pass); - + switch (brand) { case SPWPN_SWORD_OF_CEREBOV: case SPWPN_FLAMING: - strcat(info, " stops flaming."); - mpr(info); + if (showMsgs) + { + strcat(info, " stops flaming."); + mpr(info); + } break; case SPWPN_FREEZING: case SPWPN_HOLY_WRATH: - strcat(info, " stops glowing."); - mpr(info); + if (showMsgs) + { + strcat(info, " stops glowing."); + mpr(info); + } break; case SPWPN_ELECTROCUTION: - strcat(info, " stops crackling."); - mpr(info); + if (showMsgs) + { + strcat(info, " stops crackling."); + mpr(info); + } break; case SPWPN_VENOM: - strcat(info, " stops dripping with poison."); - mpr(info); + if (showMsgs) + { + strcat(info, " stops dripping with poison."); + mpr(info); + } break; case SPWPN_PROTECTION: - mpr("You feel less protected."); + if (showMsgs) + mpr("You feel less protected."); you.redraw_armour_class = 1; break; case SPWPN_VAMPIRICISM: - mpr("You feel the strange hunger wane."); + if (showMsgs) + mpr("You feel the strange hunger wane."); break; - /* case 8: draining - case 9: speed, 10 slicing etc */ + /* case 8: draining + case 9: speed, 10 slicing etc */ case SPWPN_DISTORTION: // Removing the translocations skill reduction of effect, @@ -385,11 +403,11 @@ void unwield_item(char unw) // effect in vorpalise weapon scroll effect in read_scoll } // end switch - if (you.duration[DUR_WEAPON_BRAND]) { you.duration[DUR_WEAPON_BRAND] = 0; set_item_ego_type( you.inv[unw], OBJ_WEAPONS, SPWPN_NORMAL ); + // we're letting this through mpr("Your branding evaporates."); } } // end if @@ -502,10 +520,15 @@ void unwear_armour(char unw) void unuse_randart(unsigned char unw) { - ASSERT( is_random_artefact( you.inv[unw] ) ); + unuse_randart( you.inv[unw] ); +} + +void unuse_randart(const item_def &item) +{ + ASSERT( is_random_artefact( item ) ); FixedVector< char, RA_PROPERTIES > proprt; - randart_wpn_properties( you.inv[unw], proprt ); + randart_wpn_properties( item, proprt ); if (proprt[RAP_AC]) you.redraw_armour_class = 1; diff --git a/crawl-ref/source/it_use2.h b/crawl-ref/source/it_use2.h index 2177b4083b..8351086bea 100644 --- a/crawl-ref/source/it_use2.h +++ b/crawl-ref/source/it_use2.h @@ -28,6 +28,7 @@ bool potion_effect(char pot_eff, int pow); * *********************************************************************** */ void unuse_randart(unsigned char unw); +void unuse_randart(const item_def &item); /* *********************************************************************** * called from: item_use - transfor @@ -38,6 +39,6 @@ void unwear_armour(char unw); /* *********************************************************************** * called from: decks - it_use3 - item_use - items - spells3 - transfor * *********************************************************************** */ -void unwield_item(char unw); +void unwield_item(char unw, bool showMsgs = true); #endif diff --git a/crawl-ref/source/it_use3.cc b/crawl-ref/source/it_use3.cc index f3973806b2..3de5614691 100644 --- a/crawl-ref/source/it_use3.cc +++ b/crawl-ref/source/it_use3.cc @@ -3,6 +3,8 @@ * Summary: Functions for using some of the wackier inventory items. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <4> 6/13/99 BWR Auto ID Channel staff @@ -27,6 +29,7 @@ #include "items.h" #include "it_use2.h" #include "itemname.h" +#include "itemprop.h" #include "misc.h" #include "monplace.h" #include "monstuff.h" @@ -42,7 +45,6 @@ #include "spl-util.h" #include "stuff.h" #include "view.h" -#include "wpn-misc.h" static bool ball_of_energy(void); static bool ball_of_fixation(void); @@ -102,7 +104,7 @@ void special_wielded(void) (temp_rand == 29) ? "speaks gibberish." : (temp_rand == 30) ? "raves incoherently." : "yells in some weird language."); - mpr(info); + mpr(info, MSGCH_SOUND); } break; @@ -142,8 +144,8 @@ void special_wielded(void) if (one_chance_in(200)) { - torment( you.x_pos, you.y_pos ); - naughty( NAUGHTY_UNHOLY, 1 ); + torment( TORMENT_SPWLD, you.x_pos, you.y_pos ); + did_god_conduct( DID_UNHOLY, 1 ); } break; @@ -153,7 +155,7 @@ void special_wielded(void) if (one_chance_in(5)) { animate_dead( 1 + random2(3), BEH_HOSTILE, MHITYOU, 1 ); - naughty( NAUGHTY_NECROMANCY, 1 ); + did_god_conduct( DID_NECROMANCY, 1 ); } break; @@ -185,7 +187,7 @@ void special_wielded(void) if (random2(8) <= player_spec_death()) { - naughty( NAUGHTY_NECROMANCY, 1 ); + did_god_conduct( DID_NECROMANCY, 1 ); create_monster( MONS_SHADOW, ENCH_ABJ_II, BEH_FRIENDLY, you.x_pos, you.y_pos, you.pet_target, 250 ); } @@ -199,7 +201,7 @@ void special_wielded(void) in_name(wpn, DESC_CAP_YOUR, str_pass); strcpy(info, str_pass); strcat(info, " lets out a weird humming sound."); - mpr(info); + mpr(info, MSGCH_SOUND); } break; // to noisy() call at foot 2apr2000 {dlb} @@ -209,18 +211,18 @@ void special_wielded(void) in_name(wpn, DESC_CAP_YOUR, str_pass); strcpy(info, str_pass); strcat(info, " chimes like a gong."); - mpr(info); + mpr(info, MSGCH_SOUND); } break; case SPWLD_BECKON: if (makes_noise) - mpr("You hear a voice call your name."); + mpr("You hear a voice call your name.", MSGCH_SOUND); break; case SPWLD_SHOUT: if (makes_noise) - mpr("You hear a shout."); + mpr("You hear a shout.", MSGCH_SOUND); break; //case SPWLD_PRUNE: @@ -250,7 +252,7 @@ static void reaching_weapon_attack(void) mpr("Attack whom?", MSGCH_PROMPT); - direction( beam, DIR_TARGET, TARG_ENEMY ); + direction( beam, DIR_TARGET, TARG_ENEMY, true ); if (!beam.isValid) return; @@ -372,7 +374,7 @@ bool evoke_wielded( void ) mpr("You feel the staff feeding on your energy!"); - dec_hp( 5 + random2avg(19, 2), false ); + dec_hp( 5 + random2avg(19, 2), false, "Staff of Dispater" ); dec_mp( 2 + random2avg(5, 2) ); make_hungry( 100, false ); @@ -484,8 +486,12 @@ bool evoke_wielded( void ) if (item_is_rod( you.inv[wield] )) { pract = staff_spell( wield ); + // [ds] Early exit, no turns are lost. + if (pract == -1) + return (false); + did_work = true; // staff_spell() will handle messages - } + } else if (you.inv[wield].sub_type == STAFF_CHANNELING) { if (you.magic_points < you.max_magic_points @@ -497,8 +503,8 @@ bool evoke_wielded( void ) pract = (one_chance_in(5) ? 1 : 0); did_work = true; - if (item_not_ident( you.inv[you.equip[EQ_WEAPON]], - ISFLAG_KNOW_TYPE )) + if (!item_ident( you.inv[you.equip[EQ_WEAPON]], + ISFLAG_KNOW_TYPE )) { set_ident_flags( you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_TYPE ); @@ -575,8 +581,28 @@ bool evoke_wielded( void ) { opened_gates++; + // [dshaligram] New approach to placing Hell + // portals to handle the possibility of a mirrored + // Vestibule. + int surround_grid = DNGN_FLOOR; + + for (int y = -1; y <= 1; ++y) + for (int x = -1; x <= 1; ++x) + { + if (!x && !y) + continue; + const int grid = + grd[count_x + x][count_y + y]; + + if (grid != DNGN_FLOOR + && grid != DNGN_SECRET_DOOR + && grid != DNGN_CLOSED_DOOR + && grid != DNGN_OPEN_DOOR) + surround_grid = grid; + } + // this may generate faulty [][] values {dlb} - switch (grd[count_x + 2][count_y]) + switch (surround_grid) { case DNGN_FLOOR: grd[count_x][count_y] = DNGN_ENTER_DIS; @@ -604,9 +630,13 @@ bool evoke_wielded( void ) else { mpr("You produce a hideous howling noise!"); - pract = (one_chance_in(3) ? 1 : 0); - create_monster( MONS_BEAST, ENCH_ABJ_IV, BEH_HOSTILE, - you.x_pos, you.y_pos, MHITYOU, 250 ); + int midx = create_monster( MONS_BEAST, ENCH_ABJ_IV, + BEH_HOSTILE, you.x_pos, you.y_pos, + MHITYOU, 250 ); + // avoid scumming; also prevents it from showing up on notes + if ( midx != -1 ) + menv[midx].flags |= MF_CREATED_FRIENDLY; + // no practice } break; @@ -681,7 +711,7 @@ bool evoke_wielded( void ) else if (pract > 0) exercise( SK_EVOCATIONS, pract ); - you.turn_is_over = 1; + you.turn_is_over = true; return (did_work); } // end evoke_wielded() @@ -750,7 +780,6 @@ static bool ball_of_seeing(void) static bool disc_of_storms(void) { int temp_rand = 0; // probability determination {dlb} - struct bolt beam; int disc_count = 0; unsigned char which_zap = 0; @@ -771,6 +800,8 @@ static bool disc_of_storms(void) while (disc_count) { + bolt beam; + temp_rand = random2(3); which_zap = ((temp_rand > 1) ? ZAP_LIGHTNING : @@ -811,7 +842,7 @@ void tome_of_power(char sc_read_2) strcat( info, "." ); mpr( info ); - you.turn_is_over = 1; + you.turn_is_over = true; if (!yesno("Read it?")) return; @@ -870,14 +901,15 @@ void tome_of_power(char sc_read_2) beam.flavour = BEAM_FIRE; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy( beam.beam_name, "fiery explosion" ); + beam.name = "fiery explosion"; beam.colour = RED; // your explosion, (not someone else's explosion) beam.beam_source = NON_MONSTER; beam.thrower = KILL_YOU; beam.aux_source = "an exploding Tome of Power"; beam.ex_size = 2; - beam.isTracer = false; + beam.is_tracer = false; + beam.is_explosion = true; explosion(beam); return; @@ -933,7 +965,7 @@ void skill_manual(char sc_read_2) strcat(info, "!"); mpr(info); - you.turn_is_over = 1; + you.turn_is_over = true; if (!yesno("Read it?")) return; diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index 7ea1db4900..f683822891 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -3,6 +3,8 @@ * Summary: Functions for making use of inventory items. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <8> 28July2000 GDL Revised player throwing @@ -45,6 +47,7 @@ #include "it_use3.h" #include "items.h" #include "itemname.h" +#include "itemprop.h" #include "misc.h" #include "monplace.h" #include "monstuff.h" @@ -64,66 +67,103 @@ #include "stuff.h" #include "transfor.h" #include "view.h" -#include "wpn-misc.h" bool drink_fountain(void); -static void throw_it(struct bolt &pbolt, int throw_2); -void use_randart(unsigned char item_wield_2); -static bool enchant_weapon( int which_stat, bool quiet = false ); static bool enchant_armour( void ); // Rather messy - we've gathered all the can't-wield logic from wield_weapon() // here. -bool can_wield(const item_def& weapon) +bool can_wield(const item_def *weapon, bool say_reason) { - if (you.berserker) return false; +#define SAY(x) if (say_reason) { x; } else + + if (you.berserker) + { + SAY(canned_msg(MSG_TOO_BERSERK)); + return false; + } if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE && !can_equip( EQ_WEAPON )) + { + SAY(mpr("You can't wield anything in your present form.")); return false; + } if (you.equip[EQ_WEAPON] != -1 && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_WEAPONS && item_cursed( you.inv[you.equip[EQ_WEAPON]] )) + { + SAY(mpr("You can't unwield your weapon to draw a new one!")); return false; + } - if (weapon.base_type != OBJ_WEAPONS && weapon.base_type == OBJ_STAVES - && you.equip[EQ_SHIELD] != -1) - return false; + // If we don't have an actual weapon to check, return now. + if (!weapon) + return (true); + + for (int i = EQ_CLOAK; i <= EQ_AMULET; i++) + { + if (you.equip[i] != -1 && &you.inv[you.equip[i]] == weapon) + { + SAY(mpr("You are wearing that object!")); + return (false); + } + } + + if (is_shield_incompatible(*weapon)) + { + SAY(mpr("You can't wield that with a shield.")); + return (false); + } + + // We don't have to check explicitly for staves - all staves are wieldable + // by everyone. + if (weapon->base_type != OBJ_WEAPONS) + return (true); if ((you.species < SP_OGRE || you.species > SP_OGRE_MAGE) - && mass_item( weapon ) >= 500) + && item_mass( *weapon ) >= 300) + { + SAY(mpr("That's too large and heavy for you to wield.")); return false; + } if ((you.species == SP_HALFLING || you.species == SP_GNOME || you.species == SP_KOBOLD || you.species == SP_SPRIGGAN) - && (weapon.sub_type == WPN_GREAT_SWORD - || weapon.sub_type == WPN_TRIPLE_SWORD - || weapon.sub_type == WPN_GREAT_MACE - || weapon.sub_type == WPN_GREAT_FLAIL - || weapon.sub_type == WPN_BATTLEAXE - || weapon.sub_type == WPN_EXECUTIONERS_AXE - || weapon.sub_type == WPN_HALBERD - || weapon.sub_type == WPN_GLAIVE - || weapon.sub_type == WPN_GIANT_CLUB - || weapon.sub_type == WPN_GIANT_SPIKED_CLUB - || weapon.sub_type == WPN_SCYTHE)) - return false; - - if (hands_reqd_for_weapon( weapon.base_type, - weapon.sub_type ) == HANDS_TWO_HANDED - && you.equip[EQ_SHIELD] != -1) + && (weapon->sub_type == WPN_GREAT_SWORD + || weapon->sub_type == WPN_TRIPLE_SWORD + || weapon->sub_type == WPN_GREAT_MACE + || weapon->sub_type == WPN_DIRE_FLAIL + || weapon->sub_type == WPN_BATTLEAXE + || weapon->sub_type == WPN_EXECUTIONERS_AXE + || weapon->sub_type == WPN_LOCHABER_AXE + || weapon->sub_type == WPN_HALBERD + || weapon->sub_type == WPN_GLAIVE + || weapon->sub_type == WPN_GIANT_CLUB + || weapon->sub_type == WPN_GIANT_SPIKED_CLUB + || weapon->sub_type == WPN_LONGBOW + || weapon->sub_type == WPN_SCYTHE)) + { + SAY(mpr("That's too large for you to wield.")); return false; + } - int weap_brand = get_weapon_brand( weapon ); - + int weap_brand = get_weapon_brand( *weapon ); if ((you.is_undead || you.species == SP_DEMONSPAWN) - && (!is_fixed_artefact( weapon ) + && (!is_fixed_artefact( *weapon ) && (weap_brand == SPWPN_HOLY_WRATH - || weap_brand == SPWPN_DISRUPTION))) + || weap_brand == SPWPN_DISRUPTION + || (weapon->base_type == OBJ_WEAPONS + && weapon->sub_type == WPN_BLESSED_BLADE)))) + { + SAY(mpr("This weapon will not allow you to wield it.")); return false; + } // We can wield this weapon. Phew! return true; + +#undef SAY } bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) @@ -137,28 +177,9 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) return (false); } - if (you.berserker) - { - canned_msg(MSG_TOO_BERSERK); + // Any general reasons why we can't wield a new object? + if (!can_wield(NULL, true)) return (false); - } - - if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE) - { - if (!can_equip( EQ_WEAPON )) - { - mpr("You can't wield anything in your present form."); - return (false); - } - } - - if (you.equip[EQ_WEAPON] != -1 - && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_WEAPONS - && item_cursed( you.inv[you.equip[EQ_WEAPON]] )) - { - mpr("You can't unwield your weapon to draw a new one!"); - return (false); - } if (you.sure_blade) { @@ -185,8 +206,10 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) if (!auto_wield || !is_valid_item(you.inv[item_slot]) || force_unwield) { if (!auto_wield) - item_slot = prompt_invent_item( "Wield which item (- for none)?", - OSEL_WIELD, true, true, true, '-' ); + item_slot = prompt_invent_item( + "Wield which item (- for none)?", + MT_INVSELECT, OSEL_WIELD, + true, true, true, '-', NULL, OPER_WIELD); else item_slot = PROMPT_GOT_SPECIAL; @@ -199,8 +222,8 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) { if (you.equip[EQ_WEAPON] != -1) { - unwield_item(you.equip[EQ_WEAPON]); - you.turn_is_over = 1; + unwield_item(you.equip[EQ_WEAPON], show_weff_messages); + you.turn_is_over = true; you.equip[EQ_WEAPON] = -1; canned_msg( MSG_EMPTY_HANDED ); @@ -221,83 +244,14 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) return (true); } - for (int i = EQ_CLOAK; i <= EQ_AMULET; i++) - { - if (item_slot == you.equip[i]) - { - mpr("You are wearing that object!"); - return (false); - } - } - - if (you.inv[item_slot].base_type != OBJ_WEAPONS) - { - if (you.inv[item_slot].base_type == OBJ_STAVES - && you.equip[EQ_SHIELD] != -1) - { - mpr("You can't wield that with a shield."); - return (false); - } - - if (you.equip[EQ_WEAPON] != -1) - unwield_item(you.equip[EQ_WEAPON]); - - you.equip[EQ_WEAPON] = item_slot; - } - else - { - if ((you.species < SP_OGRE || you.species > SP_OGRE_MAGE) - && mass_item( you.inv[item_slot] ) >= 500) - { - mpr("That's too large and heavy for you to wield."); - return (false); - } - - if ((you.species == SP_HALFLING || you.species == SP_GNOME - || you.species == SP_KOBOLD || you.species == SP_SPRIGGAN) - - && (you.inv[item_slot].sub_type == WPN_GREAT_SWORD - || you.inv[item_slot].sub_type == WPN_TRIPLE_SWORD - || you.inv[item_slot].sub_type == WPN_GREAT_MACE - || you.inv[item_slot].sub_type == WPN_GREAT_FLAIL - || you.inv[item_slot].sub_type == WPN_BATTLEAXE - || you.inv[item_slot].sub_type == WPN_EXECUTIONERS_AXE - || you.inv[item_slot].sub_type == WPN_HALBERD - || you.inv[item_slot].sub_type == WPN_GLAIVE - || you.inv[item_slot].sub_type == WPN_GIANT_CLUB - || you.inv[item_slot].sub_type == WPN_GIANT_SPIKED_CLUB - || you.inv[item_slot].sub_type == WPN_SCYTHE)) - { - mpr("That's too large for you to wield."); - return (false); - - } - - if (hands_reqd_for_weapon( you.inv[item_slot].base_type, - you.inv[item_slot].sub_type ) == HANDS_TWO_HANDED - && you.equip[EQ_SHIELD] != -1) - { - mpr("You can't wield that with a shield."); - return (false); - } - - int weap_brand = get_weapon_brand( you.inv[item_slot] ); - - if ((you.is_undead || you.species == SP_DEMONSPAWN) - && (!is_fixed_artefact( you.inv[item_slot] ) - && (weap_brand == SPWPN_HOLY_WRATH - || weap_brand == SPWPN_DISRUPTION))) - { - mpr("This weapon will not allow you to wield it."); - you.turn_is_over = 1; - return (false); - } + if (!can_wield(&you.inv[item_slot], true)) + return (false); - if (you.equip[EQ_WEAPON] != -1) - unwield_item(you.equip[EQ_WEAPON]); + // Go ahead and wield the weapon. + if (you.equip[EQ_WEAPON] != -1) + unwield_item(you.equip[EQ_WEAPON], show_weff_messages); - you.equip[EQ_WEAPON] = item_slot; - } + you.equip[EQ_WEAPON] = item_slot; // any oddness on wielding taken care of here wield_effects(item_slot, show_weff_messages); @@ -313,13 +267,77 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) you.time_taken /= 10; you.wield_change = true; - you.turn_is_over = 1; + you.turn_is_over = true; return (true); } +static const char *shield_base_name(const item_def *shield) +{ + return (shield->sub_type == ARM_BUCKLER? "buckler" + : "shield"); +} + +static const char *shield_impact_degree(int impact) +{ + return (impact > 160? "severely " : + impact > 130? "significantly " : + impact > 110? "" : + NULL); +} + +static void warn_rod_shield_interference(const item_def &) +{ + const int leakage = rod_shield_leakage(); + const char *leak_degree = shield_impact_degree(leakage); + + // Any way to avoid the double entendre? :-) + if (leak_degree) + mprf(MSGCH_WARN, + "Your %s %sreduces the effectiveness of your rod.", + shield_base_name(player_shield()), + leak_degree); +} + +static void warn_launcher_shield_slowdown(const item_def &launcher) +{ + const int slowspeed = + launcher_final_speed(launcher, player_shield()) * player_speed() / 100; + const int normspeed = + launcher_final_speed(launcher, NULL) * player_speed() / 100; + + // Don't warn the player unless the slowdown is real. + if (slowspeed > normspeed) + { + const char *slow_degree = + shield_impact_degree(slowspeed * 100 / normspeed); + if (slow_degree) + mprf(MSGCH_WARN, + "Your %s %sslows your rate of fire.", + shield_base_name(player_shield()), + slow_degree); + } +} + +// Warn if your shield is greatly impacting the effectiveness of your weapon? +void warn_shield_penalties() +{ + if (!player_shield()) + return; + + // Warnings are limited to rods and bows at the moment. + const item_def *weapon = player_weapon(); + if (!weapon) + return; + + if (item_is_rod(*weapon)) + warn_rod_shield_interference(*weapon); + else if (is_range_weapon(*weapon)) + warn_launcher_shield_slowdown(*weapon); +} + // provide a function for handling initial wielding of 'special' -// weapons, or those whose function is annoying to reproduce in +// weapons, or those whose function is annoying to reproduce in // other places *cough* auto-butchering *cough* {gdl} void wield_effects(int item_wield_2, bool showMsgs) @@ -356,7 +374,7 @@ void wield_effects(int item_wield_2, bool showMsgs) if (you.inv[item_wield_2].base_type == OBJ_WEAPONS) { - if (is_demonic(you.inv[item_wield_2].sub_type) + if (is_demonic(you.inv[item_wield_2]) && (you.religion == GOD_ZIN || you.religion == GOD_SHINING_ONE || you.religion == GOD_ELYVILON)) { @@ -554,6 +572,9 @@ void wield_effects(int item_wield_2, bool showMsgs) if (item_cursed( you.inv[item_wield_2] )) mpr("It sticks to your hand!"); } + + if (showMsgs) + warn_shield_penalties(); } // end wield_weapon() //--------------------------------------------------------------- @@ -564,7 +585,7 @@ void wield_effects(int item_wield_2, bool showMsgs) // something legit. // //--------------------------------------------------------------- -bool armour_prompt( const std::string & mesg, int *index ) +bool armour_prompt( const std::string & mesg, int *index, operation_types oper) { ASSERT(index != NULL); @@ -577,7 +598,9 @@ bool armour_prompt( const std::string & mesg, int *index ) canned_msg(MSG_TOO_BERSERK); else { - slot = prompt_invent_item( mesg.c_str(), OBJ_ARMOUR ); + slot = prompt_invent_item( mesg.c_str(), MT_INVSELECT, OBJ_ARMOUR, + true, true, true, 0, NULL, + oper ); if (slot != PROMPT_ABORT) { @@ -609,43 +632,28 @@ void wear_armour(void) { int armour_wear_2; - if (!armour_prompt("Wear which item?", &armour_wear_2)) + if (!armour_prompt("Wear which item?", &armour_wear_2, OPER_WEAR)) return; do_wear_armour( armour_wear_2, false ); } -int armour_equip_slot(const item_def &item) +static int armour_equip_delay(const item_def &item) { - int wh_equip = EQ_BODY_ARMOUR; + int delay = property( item, PARM_AC ); - switch (item.sub_type) - { - case ARM_BUCKLER: - case ARM_LARGE_SHIELD: - case ARM_SHIELD: - wh_equip = EQ_SHIELD; - break; - case ARM_CLOAK: - wh_equip = EQ_CLOAK; - break; - case ARM_HELMET: - wh_equip = EQ_HELMET; - break; - case ARM_GLOVES: - wh_equip = EQ_GLOVES; - break; - case ARM_BOOTS: - wh_equip = EQ_BOOTS; - break; - } - return (wh_equip); + // Shields are comparatively easy to wear. + if (is_shield( item )) + delay = delay / 2 + 1; + + if (delay < 1) + delay = 1; + + return (delay); } bool do_wear_armour( int item, bool quiet ) { - char wh_equip = 0; - if (!is_valid_item( you.inv[item] )) { if (!quiet) @@ -654,7 +662,8 @@ bool do_wear_armour( int item, bool quiet ) return (false); } - if (you.inv[item].base_type != OBJ_ARMOUR) + const int base_type = you.inv[item].base_type; + if (base_type != OBJ_ARMOUR) { if (!quiet) mpr("You can't wear that."); @@ -662,6 +671,10 @@ bool do_wear_armour( int item, bool quiet ) return (false); } + const int sub_type = you.inv[item].sub_type; + const item_def &invitem = you.inv[item]; + const equipment_type slot = get_armour_slot(invitem); + if (item == you.equip[EQ_WEAPON]) { if (!quiet) @@ -684,12 +697,10 @@ bool do_wear_armour( int item, bool quiet ) // if you're wielding something, if (you.equip[EQ_WEAPON] != -1 // attempting to wear a shield, - && (you.inv[item].sub_type == ARM_SHIELD - || you.inv[item].sub_type == ARM_BUCKLER - || you.inv[item].sub_type == ARM_LARGE_SHIELD) - // weapon is two-handed - && hands_reqd_for_weapon(you.inv[you.equip[EQ_WEAPON]].base_type, - you.inv[you.equip[EQ_WEAPON]].sub_type) == HANDS_TWO_HANDED) + && is_shield(you.inv[item]) + && is_shield_incompatible( + you.inv[you.equip[EQ_WEAPON]], + &you.inv[item])) { if (!quiet) mpr("You'd need three hands to do that!"); @@ -697,21 +708,26 @@ bool do_wear_armour( int item, bool quiet ) return (false); } - if (you.inv[item].sub_type == ARM_BOOTS) - { - if (you.species != SP_NAGA && you.inv[item].plus2 == TBOOT_NAGA_BARDING) - { - if (!quiet) - mpr("You can't wear that!"); + bool can_wear = true; + if (sub_type == ARM_NAGA_BARDING) + can_wear = (you.species == SP_NAGA); - return (false); - } + if (sub_type == ARM_CENTAUR_BARDING) + can_wear = (you.species == SP_CENTAUR); - if (you.species != SP_CENTAUR && you.inv[item].plus2 == TBOOT_CENTAUR_BARDING) + if (!can_wear) + { + if (!quiet) + mpr("You can't wear that!"); + return (false); + } + + if (you.inv[item].sub_type == ARM_BOOTS) + { + if (you.species == SP_NAGA || you.species == SP_CENTAUR) { if (!quiet) - mpr("You can't wear that!"); - + mpr("You can't wear that!"); return (false); } @@ -724,28 +740,24 @@ bool do_wear_armour( int item, bool quiet ) } } - wh_equip = armour_equip_slot(you.inv[item]); - - if (you.species == SP_NAGA && you.inv[item].sub_type == ARM_BOOTS - && you.inv[item].plus2 == TBOOT_NAGA_BARDING + if (you.species == SP_NAGA && sub_type == ARM_NAGA_BARDING && !player_is_shapechanged()) { // it fits } else if (you.species == SP_CENTAUR - && you.inv[item].sub_type == ARM_BOOTS - && you.inv[item].plus2 == TBOOT_CENTAUR_BARDING + && sub_type == ARM_CENTAUR_BARDING && !player_is_shapechanged()) { // it fits } - else if (you.inv[item].sub_type == ARM_HELMET - && (cmp_helmet_type( you.inv[item], THELM_CAP ) - || cmp_helmet_type( you.inv[item], THELM_WIZARD_HAT ))) + else if (sub_type == ARM_HELMET + && (get_helmet_type(invitem) == THELM_CAP + || get_helmet_type(invitem) == THELM_WIZARD_HAT)) { // caps & wiz hats always fit, unless your head's too big (ogres &c) } - else if (!can_equip( wh_equip )) + else if (!can_equip( slot )) { if (!quiet) mpr("You can't wear that in your present form."); @@ -755,8 +767,8 @@ bool do_wear_armour( int item, bool quiet ) // Cannot swim in heavy armour if (player_is_swimming() - && wh_equip == EQ_BODY_ARMOUR - && !is_light_armour( you.inv[item] )) + && slot == EQ_BODY_ARMOUR + && !is_light_armour( invitem )) { if (!quiet) mpr("You can't swim in that!"); @@ -768,14 +780,14 @@ bool do_wear_armour( int item, bool quiet ) if ((you.species >= SP_OGRE && you.species <= SP_OGRE_MAGE) || player_genus(GENPC_DRACONIAN)) { - if ((you.inv[item].sub_type >= ARM_LEATHER_ARMOUR - && you.inv[item].sub_type <= ARM_PLATE_MAIL) - || (you.inv[item].sub_type >= ARM_GLOVES - && you.inv[item].sub_type <= ARM_BUCKLER) - || you.inv[item].sub_type == ARM_CRYSTAL_PLATE_MAIL - || (you.inv[item].sub_type == ARM_HELMET - && (cmp_helmet_type( you.inv[item], THELM_HELM ) - || cmp_helmet_type( you.inv[item], THELM_HELMET )))) + if ((sub_type >= ARM_LEATHER_ARMOUR + && sub_type <= ARM_PLATE_MAIL) + || (sub_type >= ARM_GLOVES + && sub_type <= ARM_BUCKLER) + || sub_type == ARM_CRYSTAL_PLATE_MAIL + || (sub_type == ARM_HELMET + && (get_helmet_type(invitem) == THELM_HELM + || get_helmet_type(invitem) == THELM_HELMET))) { if (!quiet) mpr("This armour doesn't fit on your body."); @@ -787,16 +799,16 @@ bool do_wear_armour( int item, bool quiet ) // Tiny races if (you.species == SP_SPRIGGAN) { - if ((you.inv[item].sub_type >= ARM_LEATHER_ARMOUR - && you.inv[item].sub_type <= ARM_PLATE_MAIL) - || you.inv[item].sub_type == ARM_GLOVES - || you.inv[item].sub_type == ARM_BOOTS - || you.inv[item].sub_type == ARM_SHIELD - || you.inv[item].sub_type == ARM_LARGE_SHIELD - || you.inv[item].sub_type == ARM_CRYSTAL_PLATE_MAIL - || (you.inv[item].sub_type == ARM_HELMET - && (cmp_helmet_type( you.inv[item], THELM_HELM ) - || cmp_helmet_type( you.inv[item], THELM_HELMET )))) + if ((sub_type >= ARM_LEATHER_ARMOUR + && sub_type <= ARM_PLATE_MAIL) + || sub_type == ARM_GLOVES + || sub_type == ARM_BOOTS + || sub_type == ARM_SHIELD + || sub_type == ARM_LARGE_SHIELD + || sub_type == ARM_CRYSTAL_PLATE_MAIL + || (sub_type == ARM_HELMET + && (get_helmet_type(invitem) == THELM_HELM + || get_helmet_type(invitem) == THELM_HELMET))) { if (!quiet) mpr("This armour doesn't fit on your body."); @@ -808,11 +820,10 @@ bool do_wear_armour( int item, bool quiet ) bool removedCloak = false; int cloak = -1; - if ((you.inv[item].sub_type < ARM_SHIELD - || you.inv[item].sub_type > ARM_LARGE_SHIELD) - && (you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed())) + if (slot == EQ_BODY_ARMOUR + && you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed()) { - if (item_uncursed( you.inv[you.equip[EQ_CLOAK]] )) + if (!item_cursed( you.inv[you.equip[EQ_CLOAK]] )) { cloak = you.equip[ EQ_CLOAK ]; if (!takeoff_armour(you.equip[EQ_CLOAK])) @@ -829,54 +840,45 @@ bool do_wear_armour( int item, bool quiet ) } } - if (you.inv[item].sub_type == ARM_CLOAK && you.equip[EQ_CLOAK] != -1) + if (slot == EQ_CLOAK && you.equip[EQ_CLOAK] != -1) { if (!takeoff_armour(you.equip[EQ_CLOAK])) return (false); } - if (you.inv[item].sub_type == ARM_HELMET && you.equip[EQ_HELMET] != -1) + if (slot == EQ_HELMET && you.equip[EQ_HELMET] != -1) { if (!takeoff_armour(you.equip[EQ_HELMET])) return (false); } - if (you.inv[item].sub_type == ARM_GLOVES && you.equip[EQ_GLOVES] != -1) + if (slot == EQ_GLOVES && you.equip[EQ_GLOVES] != -1) { if (!takeoff_armour(you.equip[EQ_GLOVES])) return (false); } - if (you.inv[item].sub_type == ARM_BOOTS && you.equip[EQ_BOOTS] != -1) + if (slot == EQ_BOOTS && you.equip[EQ_BOOTS] != -1) { if (!takeoff_armour(you.equip[EQ_BOOTS])) return (false); } - if ((you.inv[item].sub_type == ARM_SHIELD - || you.inv[item].sub_type == ARM_LARGE_SHIELD - || you.inv[item].sub_type == ARM_BUCKLER) - && you.equip[EQ_SHIELD] != -1) + if (slot == EQ_SHIELD && you.equip[EQ_SHIELD] != -1) { if (!takeoff_armour(you.equip[EQ_SHIELD])) return (false); } - if ((you.inv[item].sub_type < ARM_SHIELD - || you.inv[item].sub_type > ARM_LARGE_SHIELD) - && you.equip[EQ_BODY_ARMOUR] != -1) + if (slot == EQ_BODY_ARMOUR && you.equip[EQ_BODY_ARMOUR] != -1) { if (!takeoff_armour(you.equip[EQ_BODY_ARMOUR])) return (false); } - you.turn_is_over = 1; - - int delay = property( you.inv[item], PARM_AC ); - - if (delay < 1) - delay = 1; + you.turn_is_over = true; + int delay = armour_equip_delay( you.inv[item] ); if (delay) start_delay( DELAY_ARMOUR_ON, delay, item ); @@ -910,13 +912,13 @@ bool takeoff_armour(int item) bool removedCloak = false; int cloak = -1; + const equipment_type slot = get_armour_slot(you.inv[item]); - if (you.inv[item].sub_type < ARM_SHIELD - || you.inv[item].sub_type > ARM_LARGE_SHIELD) + if (slot == EQ_BODY_ARMOUR) { if (you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed()) { - if (item_uncursed( you.inv[you.equip[EQ_CLOAK]] )) + if (!item_cursed( you.inv[you.equip[EQ_CLOAK]] )) { cloak = you.equip[ EQ_CLOAK ]; if (!takeoff_armour(you.equip[EQ_CLOAK])) @@ -941,11 +943,9 @@ bool takeoff_armour(int item) } else { - switch (you.inv[item].sub_type) + switch (slot) { - case ARM_BUCKLER: - case ARM_LARGE_SHIELD: - case ARM_SHIELD: + case EQ_SHIELD: if (item != you.equip[EQ_SHIELD]) { mpr("You aren't wearing that!"); @@ -953,7 +953,7 @@ bool takeoff_armour(int item) } break; - case ARM_CLOAK: + case EQ_CLOAK: if (item != you.equip[EQ_CLOAK]) { mpr("You aren't wearing that!"); @@ -961,7 +961,7 @@ bool takeoff_armour(int item) } break; - case ARM_HELMET: + case EQ_HELMET: if (item != you.equip[EQ_HELMET]) { mpr("You aren't wearing that!"); @@ -969,8 +969,7 @@ bool takeoff_armour(int item) } break; - - case ARM_GLOVES: + case EQ_GLOVES: if (item != you.equip[EQ_GLOVES]) { mpr("You aren't wearing that!"); @@ -978,23 +977,22 @@ bool takeoff_armour(int item) } break; - case ARM_BOOTS: + case EQ_BOOTS: if (item != you.equip[EQ_BOOTS]) { mpr("You aren't wearing that!"); return false; } break; + + default: + break; } } - you.turn_is_over = 1; - - int delay = property( you.inv[item], PARM_AC ); - - if (delay < 1) - delay = 1; + you.turn_is_over = true; + int delay = armour_equip_delay( you.inv[item] ); start_delay( DELAY_ARMOUR_OFF, delay, item ); if (removedCloak) @@ -1019,7 +1017,9 @@ void throw_anything(void) return; } - throw_slot = prompt_invent_item( "Throw which item?", OBJ_MISSILES ); + throw_slot = prompt_invent_item( "Throw which item?", MT_INVSELECT, + OBJ_MISSILES, true, true, true, 0, NULL, + OPER_THROW ); if (throw_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -1108,9 +1108,9 @@ int get_fire_item_index( void ) // check if we have ammo for a wielded launcher: if (weapon != -1 && you.inv[ weapon ].base_type == OBJ_WEAPONS - && launches_things( you.inv[ weapon ].sub_type )) + && is_range_weapon( you.inv[ weapon ] )) { - int type_wanted = launched_by( you.inv[ weapon ].sub_type ); + int type_wanted = fires_ammo_type( you.inv[ weapon ] ); item = try_finding_missile( type_wanted ); } break; @@ -1179,9 +1179,103 @@ void shoot_thing(void) throw_it( beam, item ); } // end shoot_thing() +// Returns delay multiplier numerator (denominator should be 100) for the +// launcher with the currently equipped shield. +int launcher_shield_slowdown(const item_def &launcher, const item_def *shield) +{ + int speed_adjust = 100; + if (!shield) + return (speed_adjust); + + const int shield_type = shield->sub_type; + hands_reqd_type hands = hands_reqd(launcher, player_size()); + + switch (hands) + { + default: + case HANDS_ONE: + case HANDS_HALF: + speed_adjust = shield_type == ARM_BUCKLER ? 105 : + shield_type == ARM_SHIELD ? 125 : + 150; + break; + + case HANDS_TWO: + speed_adjust = shield_type == ARM_BUCKLER ? 125 : + shield_type == ARM_SHIELD ? 150 : + 200; + break; + } + + // Adjust for shields skill. + if (speed_adjust > 100) + speed_adjust -= ((speed_adjust - 100) * 5 / 10) + * you.skills[SK_SHIELDS] / 27; + + return (speed_adjust); +} + +// Returns the attack cost of using the launcher, taking skill and shields +// into consideration. NOTE: You must pass in the shield; if you send in +// NULL, this function assumes no shield is in use. +int launcher_final_speed(const item_def &launcher, const item_def *shield) +{ + const int str_weight = weapon_str_weight( launcher ); + const int dex_weight = 10 - str_weight; + const skill_type launcher_skill = range_skill( launcher ); + const int shoot_skill = you.skills[launcher_skill]; + const int bow_brand = get_weapon_brand( launcher ); + + int speed_base = 10 * property( launcher, PWPN_SPEED ); + int speed_min = 70; + int speed_stat = str_weight * you.strength + dex_weight * you.dex; + + // Reduce runaway bow overpoweredness. + if (launcher_skill == SK_BOWS) + speed_min = 60; + + if (shield) + { + const int speed_adjust = launcher_shield_slowdown(launcher, shield); + + // Shields also reduce the speed cap. + speed_base = speed_base * speed_adjust / 100; + speed_min = speed_min * speed_adjust / 100; + } + + int speed = speed_base - 4 * shoot_skill * speed_stat / 250; + if (speed < speed_min) + speed = speed_min; + + if (bow_brand == SPWPN_SPEED) + { + // Speed nerf as per 4.1. Even with the nerf, bows of speed are the + // best bows, bar none. + speed = 2 * speed / 3; + } + + return (speed); +} + +// Determines if the end result of the combined launcher + ammo brands a +// fire/frost beam. +bool elemental_missile_beam(int launcher_brand, int ammo_brand) +{ + int element = (launcher_brand == SPWPN_FROST) + + (ammo_brand == SPMSL_ICE) + - (launcher_brand == SPWPN_FLAME) + - (ammo_brand == SPMSL_FLAME); + return (element); +} + // throw_it - currently handles player throwing only. Monster // throwing is handled in mstuff2:mons_throw() -static void throw_it(struct bolt &pbolt, int throw_2) +// Note: If dummy_target is non-NULL, throw_it fakes a bolt and calls +// affect() on the monster's square. +// +// Return value is only relevant if dummy_target is non-NULL, and returns +// true if dummy_target is hit. +bool throw_it(struct bolt &pbolt, int throw_2, monsters *dummy_target) { struct dist thr; char shoot_skill = 0; @@ -1194,31 +1288,50 @@ static void throw_it(struct bolt &pbolt, int throw_2) int lnchHitBonus = 0, lnchDamBonus = 0; // special add from launcher int exHitBonus = 0, exDamBonus = 0; // 'extra' bonus from skill/dex/str int effSkill = 0; // effective launcher skill + int dice_mult = 100; bool launched = false; // item is launched bool thrown = false; // item is sensible thrown item + int slayDam = 0; - // Making a copy of the item: changed only for venom launchers - item_def item = you.inv[throw_2]; - item.quantity = 1; - item.slot = index_to_letter(item.link); - origin_set_unknown(item); - - char str_pass[ ITEMNAME_SIZE ]; - - mpr( STD_DIRECTION_PROMPT, MSGCH_PROMPT ); - - message_current_target(); - - direction( thr, DIR_NONE, TARG_ENEMY ); + if (dummy_target) + { + thr.isValid = true; + thr.isCancel = false; + thr.tx = dummy_target->x; + thr.ty = dummy_target->y; + } + else + { + mpr( STD_DIRECTION_PROMPT, MSGCH_PROMPT ); + message_current_target(); + direction( thr, DIR_NONE, TARG_ENEMY ); + } if (!thr.isValid) { if (thr.isCancel) canned_msg(MSG_OK); - return; + return (false); + } + + // Must unwield before fire_beam() makes a copy in order to remove things + // like temporary branding. -- bwr + if (throw_2 == you.equip[EQ_WEAPON] && you.inv[throw_2].quantity == 1) + { + unwield_item( throw_2 ); + you.equip[EQ_WEAPON] = -1; + canned_msg( MSG_EMPTY_HANDED ); } + // Making a copy of the item: changed only for venom launchers + item_def item = you.inv[throw_2]; + item.quantity = 1; + item.slot = index_to_letter(item.link); + origin_set_unknown(item); + + char str_pass[ ITEMNAME_SIZE ]; + if (you.conf) { thr.isTarget = true; @@ -1226,10 +1339,9 @@ static void throw_it(struct bolt &pbolt, int throw_2) thr.ty = you.y_pos + random2(13) - 6; } - // even though direction is allowed, we're throwing so we + // even though direction is allowed, we're throwing so we // want to use tx, ty to make the missile fly to map edge. - pbolt.target_x = thr.tx; - pbolt.target_y = thr.ty; + pbolt.set_target(thr); pbolt.flavour = BEAM_MISSILE; // pbolt.range is set below @@ -1257,10 +1369,10 @@ static void throw_it(struct bolt &pbolt, int throw_2) pbolt.colour = item.colour; item_name( item, DESC_PLAIN, str_pass ); - strcpy( pbolt.beam_name, str_pass ); + pbolt.name = str_pass; pbolt.thrower = KILL_YOU_MISSILE; - pbolt.aux_source = NULL; + pbolt.aux_source.clear(); // get the ammo/weapon type. Convenience. wepClass = item.base_type; @@ -1281,11 +1393,11 @@ static void throw_it(struct bolt &pbolt, int throw_2) } // baseHit and damage for generic objects - baseHit = you.strength - mass_item(item) / 10; + baseHit = you.strength - item_mass(item) / 10; if (baseHit > 0) baseHit = 0; - baseDam = mass_item(item) / 100; + baseDam = item_mass(item) / 100; // special: might be throwing generic weapon; // use base wep. damage, w/ penalty @@ -1313,21 +1425,49 @@ static void throw_it(struct bolt &pbolt, int throw_2) // CALCULATIONS FOR LAUNCHED WEAPONS if (launched) { - const int bow_brand = get_weapon_brand( you.inv[you.equip[EQ_WEAPON]] ); + const item_def &launcher = you.inv[you.equip[EQ_WEAPON]]; + const int bow_brand = get_weapon_brand( launcher ); const int ammo_brand = get_ammo_brand( item ); bool poisoned = (ammo_brand == SPMSL_POISONED || ammo_brand == SPMSL_POISONED_II); + const int rc_skill = you.skills[SK_RANGED_COMBAT]; - // this is deliberately confusing: the 'hit' value for - // ammo is the _damage_ when used with a launcher. Geez. - baseHit = 0; - baseDam = property( item, PWPN_HIT ); + const int item_base_dam = property( item, PWPN_DAMAGE ); + const int lnch_base_dam = property( launcher, PWPN_DAMAGE ); + + const skill_type launcher_skill = range_skill( launcher ); + + baseHit = property( launcher, PWPN_HIT ); + baseDam = lnch_base_dam + random2(1 + item_base_dam); + + // Slings are terribly weakened otherwise + if (lnch_base_dam == 0) + baseDam = item_base_dam; + + // If we've a zero base damage + an elemental brand, up the damage + // slightly so the brand has something to work with. This should + // only apply to needles. + if (!baseDam && elemental_missile_beam(bow_brand, ammo_brand)) + baseDam = 4; + + // [dshaligram] This is a horrible hack - we force beam.cc to consider + // this beam "needle-like". + if (wepClass == OBJ_MISSILES && wepType == MI_NEEDLE) + pbolt.ench_power = AUTOMATIC_HIT; + +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, + "Base hit == %d; Base damage == %d " + "(item %d + launcher %d)", + baseHit, baseDam, + item_base_dam, lnch_base_dam); +#endif // fix ammo damage bonus, since missiles only use inv_plus ammoDamBonus = ammoHitBonus; // check for matches; dwarven,elven,orcish - if (!cmp_equip_race( you.inv[you.equip[EQ_WEAPON]], 0 )) + if (!get_equip_race(you.inv[you.equip[EQ_WEAPON]]) == 0) { if (get_equip_race( you.inv[you.equip[EQ_WEAPON]] ) == get_equip_race( item )) @@ -1336,7 +1476,8 @@ static void throw_it(struct bolt &pbolt, int throw_2) baseDam += 1; // elves with elven bows - if (cmp_equip_race(you.inv[you.equip[EQ_WEAPON]], ISFLAG_ELVEN) + if (get_equip_race(you.inv[you.equip[EQ_WEAPON]]) + == ISFLAG_ELVEN && player_genus(GENPC_ELVEN)) { baseHit += 1; @@ -1344,86 +1485,78 @@ static void throw_it(struct bolt &pbolt, int throw_2) } } - if (you.inv[you.equip[EQ_WEAPON]].sub_type == WPN_CROSSBOW) - { - // extra time taken, as a percentage. range from 30 -> 12 - int extraTime = 30 - ((you.skills[SK_CROSSBOWS] * 2) / 3); - - you.time_taken = (100 + extraTime) * you.time_taken; - you.time_taken /= 100; - } - - if (bow_brand == SPWPN_SPEED) - { - you.time_taken *= 5; - you.time_taken /= 10; - } - - // for all launched weapons, maximum effective specific skill + // for all launched weapons, maximum effective specific skill // is twice throwing skill. This models the fact that no matter - // how 'good' you are with a bow, if you know nothing about + // how 'good' you are with a bow, if you know nothing about // trajectories you're going to be a damn poor bowman. Ditto // for crossbows and slings. - switch (lnchType) - { - case WPN_SLING: - shoot_skill = you.skills[SK_SLINGS]; - break; - case WPN_BOW: - shoot_skill = you.skills[SK_BOWS]; - break; - case WPN_BLOWGUN: - shoot_skill = you.skills[SK_DARTS]; - break; - case WPN_CROSSBOW: - case WPN_HAND_CROSSBOW: - shoot_skill = you.skills[SK_CROSSBOWS]; - break; - default: - shoot_skill = 0; - break; - } - effSkill = you.skills[SK_THROWING] * 2 + 1; - effSkill = (shoot_skill > effSkill) ? effSkill : shoot_skill; + // [dshaligram] Throwing now two parts launcher skill, one part + // ranged combat. Removed the old model which is... silly. + + shoot_skill = you.skills[launcher_skill]; + effSkill = (shoot_skill * 2 + rc_skill) / 3; + + const int speed = launcher_final_speed(launcher, player_shield()); +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Final launcher speed: %d", speed); +#endif + you.time_taken = speed * you.time_taken / 100; + + // effSkill = you.skills[SK_RANGED_COMBAT] * 2 + 1; + // effSkill = (shoot_skill > effSkill) ? effSkill : shoot_skill; + + // [dshaligram] Improving missile weapons: + // - Remove the strength/enchantment cap where you need to be strong + // to exploit a launcher bonus. + // - Add on launcher and missile pluses to extra damage. + + // [dshaligram] This can get large... + exDamBonus = lnchDamBonus + random2(1 + ammoDamBonus); + exDamBonus = exDamBonus > 0? random2(exDamBonus + 1) + : -random2(-exDamBonus + 1); + exHitBonus = lnchHitBonus > 0? random2(lnchHitBonus + 1) + : -random2(-lnchHitBonus + 1); // removed 2 random2(2)s from each of the learning curves, but // left slings because they're hard enough to develop without // a good source of shot in the dungeon. - switch (lnchType) + switch (launcher_skill) + { + case SK_SLINGS: { - case WPN_SLING: // Slings are really easy to learn because they're not // really all that good, and its harder to get ammo anyways. exercise(SK_SLINGS, 1 + random2avg(3, 2)); baseHit += 0; - exHitBonus = (effSkill * 3) / 2; + exHitBonus += (effSkill * 3) / 2; // strength is good if you're using a nice sling. - exDamBonus = (10 * (you.strength - 10)) / 9; - exDamBonus = (exDamBonus * (2 * baseDam + ammoDamBonus)) / 20; + int strbonus = (10 * (you.strength - 10)) / 9; + strbonus = (strbonus * (2 * baseDam + ammoDamBonus)) / 20; // cap - if (exDamBonus > lnchDamBonus + 1) - exDamBonus = lnchDamBonus + 1; + if (strbonus > lnchDamBonus + 1) + strbonus = lnchDamBonus + 1; + exDamBonus += strbonus; // add skill for slings.. helps to find those vulnerable spots - exDamBonus += effSkill / 2; + dice_mult = dice_mult * (14 + random2(1 + effSkill)) / 14; // now kill the launcher damage bonus if (lnchDamBonus > 0) lnchDamBonus = 0; break; - + } // blowguns take a _very_ steady hand; a lot of the bonus // comes from dexterity. (Dex bonus here as well as below) - case WPN_BLOWGUN: - exercise(SK_DARTS, (coinflip()? 2 : 1)); + case SK_DARTS: baseHit -= 2; - exHitBonus = (effSkill * 3) / 2 + you.dex / 2; + exercise(SK_DARTS, (coinflip()? 2 : 1)); + exHitBonus += (effSkill * 3) / 2 + you.dex / 2; // no extra damage for blowguns - exDamBonus = 0; + // exDamBonus = 0; // now kill the launcher damage and ammo bonuses if (lnchDamBonus > 0) @@ -1432,52 +1565,70 @@ static void throw_it(struct bolt &pbolt, int throw_2) ammoDamBonus = 0; break; - - case WPN_BOW: + case SK_BOWS: + { + baseHit -= 3; exercise(SK_BOWS, (coinflip()? 2 : 1)); - baseHit -= 4; - exHitBonus = (effSkill * 2); + exHitBonus += (effSkill * 2); // strength is good if you're using a nice bow - exDamBonus = (10 * (you.strength - 10)) / 4; - exDamBonus = (exDamBonus * (2 * baseDam + ammoDamBonus)) / 20; + int strbonus = (10 * (you.strength - 10)) / 4; + strbonus = (strbonus * (2 * baseDam + ammoDamBonus)) / 20; - // cap - if (exDamBonus > (lnchDamBonus + 1) * 3) - exDamBonus = (lnchDamBonus + 1) * 3; + // cap; reduced this cap, because we don't want to allow + // the extremely-strong to quadruple the enchantment bonus. + if (strbonus > lnchDamBonus + 1) + strbonus = lnchDamBonus + 1; + + exDamBonus += strbonus; // add in skill for bows.. help you to find those vulnerable spots. - exDamBonus += effSkill; + // exDamBonus += effSkill; + + dice_mult = dice_mult * (17 + random2(1 + effSkill)) / 17; // now kill the launcher damage bonus if (lnchDamBonus > 0) lnchDamBonus = 0; break; - + } // Crossbows are easy for unskilled people. - case WPN_CROSSBOW: + case SK_CROSSBOWS: exercise(SK_CROSSBOWS, (coinflip()? 2 : 1)); - baseHit += 2; - exHitBonus = (3 * effSkill) / 2 + 6; - exDamBonus = effSkill / 2 + 4; + baseHit++; + exHitBonus += (3 * effSkill) / 2 + 6; + // exDamBonus += effSkill * 2 / 3 + 4; + + dice_mult = dice_mult * (22 + random2(1 + effSkill)) / 22; + + if (lnchType == WPN_HAND_CROSSBOW) + { + exHitBonus -= 2; + dice_mult = dice_mult * 26 / 30; + } break; - case WPN_HAND_CROSSBOW: - exercise(SK_CROSSBOWS, (coinflip()? 2 : 1)); - baseHit += 1; - exHitBonus = (3 * effSkill) / 2 + 4; - exDamBonus = effSkill / 2 + 2; + default: break; } // all launched weapons have a slight chance of improving // throwing skill if (coinflip()) - exercise(SK_THROWING, 1); + exercise(SK_RANGED_COMBAT, 1); + + // all launched weapons get a minor tohit boost from throwing skill. + exHitBonus += you.skills[SK_RANGED_COMBAT] / 5; - // all launched weapons get a tohit boost from throwing skill. - exHitBonus += (3 * you.skills[SK_THROWING]) / 4; + if (bow_brand == SPWPN_VORPAL) + { + // Vorpal brand adds 30% damage bonus. Increased from 25% + // because at 25%, vorpal brand is completely inferior to + // speed. At 30% it's marginally better than speed when + // fighting monsters with very heavy armour. + dice_mult = dice_mult * 130 / 100; + } // special cases for flame, frost, poison, etc. // check for venom brand (usually only available for blowguns) @@ -1486,25 +1637,27 @@ static void throw_it(struct bolt &pbolt, int throw_2) // poison brand the ammo set_item_ego_type( item, OBJ_MISSILES, SPMSL_POISONED ); item_name( item, DESC_PLAIN, str_pass ); - strcpy( pbolt.beam_name, str_pass ); + pbolt.name = str_pass; } // Note that bow_brand is known since the bow is equiped. if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME) && ammo_brand != SPMSL_ICE && bow_brand != SPWPN_FROST) { - baseDam += 1 + random2(5); + // [dshaligram] Branded arrows are much stronger. + dice_mult = dice_mult * 150 / 100; + pbolt.flavour = BEAM_FIRE; - strcpy(pbolt.beam_name, "bolt of "); + pbolt.name = "bolt of "; if (poisoned) - strcat(pbolt.beam_name, "poison "); + pbolt.name += "poison "; - strcat(pbolt.beam_name, "flame"); + pbolt.name += "flame"; pbolt.colour = RED; pbolt.type = SYM_BOLT; pbolt.thrower = KILL_YOU_MISSILE; - pbolt.aux_source = NULL; + pbolt.aux_source.clear(); // ammo known if we can't attribute it to the bow if (bow_brand != SPWPN_FLAME) @@ -1517,18 +1670,20 @@ static void throw_it(struct bolt &pbolt, int throw_2) if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_ICE) && ammo_brand != SPMSL_FLAME && bow_brand != SPWPN_FLAME) { - baseDam += 1 + random2(5); + // [dshaligram] Branded arrows are much stronger. + dice_mult = dice_mult * 150 / 100; + pbolt.flavour = BEAM_COLD; - strcpy(pbolt.beam_name, "bolt of "); + pbolt.name = "bolt of "; if (poisoned) - strcat(pbolt.beam_name, "poison "); + pbolt.name += "poison "; - strcat(pbolt.beam_name, "frost"); + pbolt.name += "frost"; pbolt.colour = WHITE; pbolt.type = SYM_BOLT; pbolt.thrower = KILL_YOU_MISSILE; - pbolt.aux_source = NULL; + pbolt.aux_source.clear(); // ammo known if we can't attribute it to the bow if (bow_brand != SPWPN_FROST) @@ -1554,8 +1709,19 @@ static void throw_it(struct bolt &pbolt, int throw_2) * and vice versa */ // ID check - if (item_not_ident( you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_PLUSES ) - && random2(100) < shoot_skill) + if (item_ident(you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_PLUSES)) + { + if ( !item_ident(you.inv[throw_2], ISFLAG_KNOW_PLUSES) && + random2(100) < rc_skill ) + { + set_ident_flags( item, ISFLAG_KNOW_PLUSES ); + set_ident_flags( you.inv[throw_2], ISFLAG_KNOW_PLUSES ); + in_name( throw_2, DESC_NOCAP_A, str_pass); + snprintf(info, INFO_SIZE, "You are firing %s.", str_pass); + mpr(info); + } + } + else if (random2(100) < shoot_skill) { set_ident_flags(you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_PLUSES); @@ -1568,6 +1734,7 @@ static void throw_it(struct bolt &pbolt, int throw_2) more(); you.wield_change = true; } + } // CALCULATIONS FOR THROWN WEAPONS @@ -1584,7 +1751,8 @@ static void throw_it(struct bolt &pbolt, int throw_2) || (wepClass == OBJ_MISSILES && wepType == MI_STONE)) { // elves with elven weapons - if (cmp_equip_race(item, ISFLAG_ELVEN) && player_genus(GENPC_ELVEN)) + if (get_equip_race(item) == ISFLAG_ELVEN + && player_genus(GENPC_ELVEN)) baseHit += 1; // give an appropriate 'tohit' - @@ -1608,14 +1776,14 @@ static void throw_it(struct bolt &pbolt, int throw_2) } } - exHitBonus = you.skills[SK_THROWING] * 2; + exHitBonus = you.skills[SK_RANGED_COMBAT] * 2; baseDam = property( item, PWPN_DAMAGE ); exDamBonus = - (10 * (you.skills[SK_THROWING] / 2 + you.strength - 10)) / 12; + (10 * (you.skills[SK_RANGED_COMBAT] / 2 + you.strength - 10)) / 12; // now, exDamBonus is a multiplier. The full multiplier - // is applied to base damage, but only a third is applied + // is applied to base damage, but only a third is applied // to the magical modifier. exDamBonus = (exDamBonus * (3 * baseDam + ammoDamBonus)) / 30; } @@ -1627,16 +1795,35 @@ static void throw_it(struct bolt &pbolt, int throw_2) baseDam = property( item, PWPN_DAMAGE ); exHitBonus = you.skills[SK_DARTS] * 2; - exHitBonus += (you.skills[SK_THROWING] * 2) / 3; - exDamBonus = you.skills[SK_DARTS] / 4; + exHitBonus += (you.skills[SK_RANGED_COMBAT] * 2) / 3; + exDamBonus = you.skills[SK_DARTS] / 3; + exDamBonus += you.skills[SK_RANGED_COMBAT] / 5; // exercise skills exercise(SK_DARTS, 1 + random2avg(3, 2)); } + // [dshaligram] The defined base damage applies only when used + // for launchers. Hand-thrown stones and darts do only half + // base damage. Yet another evil 4.0ism. + if (wepClass == OBJ_MISSILES + && (wepType == MI_DART || wepType == MI_STONE)) + baseDam = div_rand_round(baseDam, 2); + // exercise skill if (coinflip()) - exercise(SK_THROWING, 1); + exercise(SK_RANGED_COMBAT, 1); + + // ID check + if ( !item_ident(you.inv[throw_2], ISFLAG_KNOW_PLUSES) && + random2(100) < you.skills[SK_RANGED_COMBAT] ) + { + set_ident_flags( item, ISFLAG_KNOW_PLUSES ); + set_ident_flags( you.inv[throw_2], ISFLAG_KNOW_PLUSES ); + in_name( throw_2, DESC_NOCAP_A, str_pass); + snprintf(info, INFO_SIZE, "You are throwing %s.", str_pass); + mpr(info); + } } // range, dexterity bonus, possible skill increase for silly throwing @@ -1659,7 +1846,11 @@ static void throw_it(struct bolt &pbolt, int throw_2) // slaying bonuses if (!(launched && wepType == MI_NEEDLE)) - exDamBonus += slaying_bonus(PWPN_DAMAGE); + { + slayDam = slaying_bonus(PWPN_DAMAGE); + slayDam = slayDam < 0? -random2(1 - slayDam) + : random2(1 + slayDam); + } exHitBonus += slaying_bonus(PWPN_HIT); } @@ -1667,7 +1858,7 @@ static void throw_it(struct bolt &pbolt, int throw_2) else { // range based on mass & strength, between 1 and 9 - pbolt.range = you.strength - mass_item(item) / 10 + 3; + pbolt.range = you.strength - item_mass(item) / 10 + 3; if (pbolt.range < 1) pbolt.range = 1; @@ -1678,9 +1869,22 @@ static void throw_it(struct bolt &pbolt, int throw_2) pbolt.rangeMax = pbolt.range; if (one_chance_in(20)) - exercise(SK_THROWING, 1); + exercise(SK_RANGED_COMBAT, 1); exHitBonus = you.dex / 4; + + if (wepClass == OBJ_MISSILES && wepType == MI_NEEDLE) + { + // Throwing needles is now seriously frowned upon; it's difficult + // to grip a fiddly little needle, and not penalising it cheapens + // blowguns. + exHitBonus -= (30 - you.skills[SK_DARTS]) / 3; + baseHit -= (30 - you.skills[SK_DARTS]) / 3; +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Needle base hit = %d, exHitBonus = %d", + baseHit, exHitBonus); +#endif + } } // FINALIZE tohit and damage @@ -1694,6 +1898,11 @@ static void throw_it(struct bolt &pbolt, int throw_2) else pbolt.damage = dice_def( 1, baseDam - random2(0 - (exDamBonus - 1)) ); + pbolt.damage.size = dice_mult * pbolt.damage.size / 100; + pbolt.damage.size += slayDam; + + scale_dice( pbolt.damage ); + // only add bonuses if we're throwing something sensible if (thrown || launched || wepClass == OBJ_WEAPONS) { @@ -1702,59 +1911,215 @@ static void throw_it(struct bolt &pbolt, int throw_2) } #if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, - "H:%d+%d;a%dl%d. D:%d+%d;a%dl%d -> %d,%dd%d", + mprf( MSGCH_DIAGNOSTICS, + "H:%d+%d;a%dl%d. D:%d+%d;a%dl%d -> %d,%dd%d", baseHit, exHitBonus, ammoHitBonus, lnchHitBonus, baseDam, exDamBonus, ammoDamBonus, lnchDamBonus, pbolt.hit, pbolt.damage.num, pbolt.damage.size ); - - mpr( info, MSGCH_DIAGNOSTICS ); #endif - // Must unwield before fire_beam() makes a copy in order to remove things - // like temporary branding. -- bwr - if (throw_2 == you.equip[EQ_WEAPON] && you.inv[throw_2].quantity == 1) - { - unwield_item( throw_2 ); - you.equip[EQ_WEAPON] = -1; - canned_msg( MSG_EMPTY_HANDED ); - } - // create message if (launched) strcpy(info, "You shoot "); else strcpy(info, "You throw "); - item_name( item, DESC_NOCAP_A, str_pass ); + item_name( item, DESC_NOCAP_A, str_pass ); strcat(info, str_pass); strcat(info, "."); mpr(info); // ensure we're firing a 'missile'-type beam - pbolt.isBeam = false; - pbolt.isTracer = false; + pbolt.is_beam = false; + pbolt.is_tracer = false; // mark this item as thrown if it's a missile, so that we'll pick it up // when we walk over it. if (wepClass == OBJ_MISSILES || wepClass == OBJ_WEAPONS) item.flags |= ISFLAG_THROWN; + bool hit = false; // using copy, since the launched item might be differect (venom blowgun) - fire_beam( pbolt, &item ); + if (dummy_target) + hit = (affect( pbolt, dummy_target->x, dummy_target->y ) != 0); + else + { + fire_beam( pbolt, &item ); + dec_inv_item_quantity( throw_2, 1 ); + } + + // throwing and blowguns are silent + if (launched && lnchType != WPN_BLOWGUN) + noisy( 6, you.x_pos, you.y_pos ); + + // but any monster nearby can see that something has been thrown: + alert_nearby_monsters(); - dec_inv_item_quantity( throw_2, 1 ); + you.turn_is_over = true; + + return (hit); +} // end throw_it() + +void jewellery_wear_effects(item_def &item) +{ + int ident = ID_TRIED_TYPE; + + switch (item.sub_type) + { + case RING_FIRE: + case RING_HUNGER: + case RING_ICE: + case RING_LIFE_PROTECTION: + case RING_POISON_RESISTANCE: + case RING_PROTECTION_FROM_COLD: + case RING_PROTECTION_FROM_FIRE: + case RING_PROTECTION_FROM_MAGIC: + case RING_SUSTAIN_ABILITIES: + case RING_SUSTENANCE: + case RING_SLAYING: + case RING_SEE_INVISIBLE: + case RING_TELEPORTATION: + case RING_WIZARDRY: + case RING_REGENERATION: + case RING_TELEPORT_CONTROL: + break; + + case RING_PROTECTION: + you.redraw_armour_class = 1; + if (item.plus != 0) + ident = ID_KNOWN_TYPE; + break; + + case RING_INVISIBILITY: + if (!you.invis) + { + mpr("You become transparent for a moment."); + ident = ID_KNOWN_TYPE; + } + break; + + case RING_EVASION: + you.redraw_evasion = 1; + if (item.plus != 0) + ident = ID_KNOWN_TYPE; + break; + + case RING_STRENGTH: + modify_stat(STAT_STRENGTH, item.plus, true); + if (item.plus != 0) + ident = ID_KNOWN_TYPE; + break; + + case RING_DEXTERITY: + modify_stat(STAT_DEXTERITY, item.plus, true); + if (item.plus != 0) + ident = ID_KNOWN_TYPE; + break; + + case RING_INTELLIGENCE: + modify_stat(STAT_INTELLIGENCE, item.plus, true); + if (item.plus != 0) + ident = ID_KNOWN_TYPE; + break; + + case RING_MAGICAL_POWER: + calc_mp(); + ident = ID_KNOWN_TYPE; + break; + + case RING_LEVITATION: + mpr("You feel buoyant."); + ident = ID_KNOWN_TYPE; + break; + + case AMU_RAGE: + mpr("You feel a brief urge to hack something to bits."); + ident = ID_KNOWN_TYPE; + break; + + case AMU_THE_GOURMAND: + you.duration[DUR_GOURMAND] = 0; + break; + } + + // Artefacts have completely different appearance than base types + // so we don't allow them to make the base types known + if (is_random_artefact( item )) + use_randart(item); + else + set_ident_type( item.base_type, item.sub_type, ident ); + + if (ident == ID_KNOWN_TYPE) + set_ident_flags( item, ISFLAG_EQ_JEWELLERY_MASK ); + + if (item_cursed( item )) + mprf("Oops, that %s feels deathly cold.", + jewellery_is_amulet(item)? "amulet" : "ring"); + + // cursed or not, we know that since we've put the ring on + set_ident_flags( item, ISFLAG_KNOW_CURSE ); + + mpr( item_name( item, DESC_INVENTORY_EQUIP ) ); +} + +static int prompt_ring_to_remove(int new_ring) +{ + const item_def &left = you.inv[ you.equip[EQ_LEFT_RING] ]; + const item_def &right = you.inv[ you.equip[EQ_RIGHT_RING] ]; + + if (item_cursed(left) && item_cursed(right)) + { + mprf("You're already wearing two cursed rings!"); + return (-1); + } + + mesclr(); + mprf("Wearing %s.", in_name(new_ring, DESC_NOCAP_A)); + + char lslot = index_to_letter(left.link), + rslot = index_to_letter(right.link); + + mprf(MSGCH_PROMPT, + "You're wearing two rings. Remove which one? (%c/%c/Esc)", + lslot, rslot); + mprf(" %s", item_name( left, DESC_INVENTORY )); + mprf(" %s", item_name( right, DESC_INVENTORY )); + + int c; + do + c = getch(); + while (c != lslot && c != rslot && c != ESCAPE && c != ' '); + + mesclr(); + + if (c == ESCAPE || c == ' ') + return (-1); + + int eqslot = c == lslot? EQ_LEFT_RING : EQ_RIGHT_RING; + return (you.equip[eqslot]); +} + +// Assumptions: +// you.inv[ring_slot] is a valid ring. +// EQ_LEFT_RING and EQ_RIGHT_RING are both occupied, and ring_slot is not +// one of the worn rings. +// +// Does not do amulets. +static bool swap_rings(int ring_slot) +{ + // Ask the player which existing ring is persona non grata. + int unwanted = prompt_ring_to_remove(ring_slot); + if (unwanted == -1) + return (false); - // throwing and blowguns are silent - if (launched && lnchType != WPN_BLOWGUN) - noisy( 6, you.x_pos, you.y_pos ); + if (!remove_ring(unwanted, false)) + return (false); - // but any monster nearby can see that something has been thrown: - alert_nearby_monsters(); + start_delay(DELAY_JEWELLERY_ON, 1, ring_slot); - you.turn_is_over = 1; -} // end throw_it() + return (true); +} bool puton_item(int item_slot, bool prompt_finger) { @@ -1774,13 +2139,11 @@ bool puton_item(int item_slot, bool prompt_finger) if (you.inv[item_slot].base_type != OBJ_JEWELLERY) { - //jmf: let's not take our inferiority complex out on players, eh? :-p - //mpr("You're sadly mistaken if you consider that jewellery.") mpr("You can only put on jewellery."); return (false); } - bool is_amulet = (you.inv[item_slot].sub_type >= AMU_RAGE); + bool is_amulet = jewellery_is_amulet( you.inv[item_slot] ); if (!is_amulet) // ie it's a ring { @@ -1791,28 +2154,22 @@ bool puton_item(int item_slot, bool prompt_finger) return (false); } - if (you.inv[item_slot].base_type == OBJ_JEWELLERY - && you.equip[EQ_LEFT_RING] != -1 - && you.equip[EQ_RIGHT_RING] != -1) - { - // and you are trying to wear body you.equip. - mpr("You've already put a ring on each hand."); - return (false); - } + if (you.equip[EQ_LEFT_RING] != -1 + && you.equip[EQ_RIGHT_RING] != -1) + return swap_rings(item_slot); } else if (you.equip[EQ_AMULET] != -1) { - strcpy(info, "You are already wearing an amulet."); + if (!remove_ring( you.equip[EQ_AMULET], true )) + return (false); - if (one_chance_in(20)) - { - strcat(info, " And I must say it looks quite fetching."); - } + start_delay(DELAY_JEWELLERY_ON, 1, item_slot); - mpr(info); - return (false); + // Assume it's going to succeed. + return (true); } + // First ring goes on left hand if we're choosing automatically. int hand_used = 0; if (you.equip[EQ_LEFT_RING] != -1) @@ -1823,38 +2180,83 @@ bool puton_item(int item_slot, bool prompt_finger) if (is_amulet) hand_used = 2; - else if (you.equip[EQ_LEFT_RING] == -1 && you.equip[EQ_RIGHT_RING] == -1) + else if (prompt_finger + && you.equip[EQ_LEFT_RING] == -1 + && you.equip[EQ_RIGHT_RING] == -1) { - if (prompt_finger) - { - mpr("Put on which hand (l or r)?", MSGCH_PROMPT); + mpr("Put on which hand (l or r)?", MSGCH_PROMPT); - int keyin = get_ch(); + int keyin = get_ch(); - if (keyin == 'l') - hand_used = 0; - else if (keyin == 'r') - hand_used = 1; - else if (keyin == ESCAPE) - return (false); - else - { - mpr("You don't have such a hand!"); - return (false); - } - } + if (keyin == 'l') + hand_used = 0; + else if (keyin == 'r') + hand_used = 1; + else if (keyin == ESCAPE) + return (false); else { - // First ring goes on left hand if we're choosing automatically. - hand_used = 0; + mpr("You don't have such a hand!"); + return (false); } } you.equip[ EQ_LEFT_RING + hand_used ] = item_slot; - int ident = ID_TRIED_TYPE; + jewellery_wear_effects( you.inv[item_slot] ); + + // Putting on jewellery is as fast as wielding weapons. + you.time_taken = you.time_taken * 5 / 10; + you.turn_is_over = true; + + return (true); +} + +bool puton_ring(int slot, bool prompt_finger) +{ + int item_slot; + + if (inv_count() < 1) + { + canned_msg(MSG_NOTHING_CARRIED); + return (false); + } + + if (you.berserker) + { + canned_msg(MSG_TOO_BERSERK); + return (false); + } + + if (slot != -1) + item_slot = slot; + else + item_slot = prompt_invent_item( "Put on which piece of jewellery?", + MT_INVSELECT, OBJ_JEWELLERY ); + + if (item_slot == PROMPT_ABORT) + { + canned_msg( MSG_OK ); + return (false); + } + + return puton_item(item_slot, prompt_finger); +} // end puton_ring() + +void jewellery_remove_effects(item_def &item) +{ + // The ring/amulet must already be removed from you.equip at this point. + + // Turn off show_uncursed before getting the item name, because this item + // was just removed, and the player knows it's uncursed. + bool old_showuncursed = Options.show_uncursed; + Options.show_uncursed = false; - switch (you.inv[item_slot].sub_type) + mprf("You remove %s.", item_name(item, DESC_NOCAP_YOUR)); + + Options.show_uncursed = old_showuncursed; + + switch (item.sub_type) { case RING_FIRE: case RING_HUNGER: @@ -1864,143 +2266,68 @@ bool puton_item(int item_slot, bool prompt_finger) case RING_PROTECTION_FROM_COLD: case RING_PROTECTION_FROM_FIRE: case RING_PROTECTION_FROM_MAGIC: + case RING_REGENERATION: + case RING_SEE_INVISIBLE: + case RING_SLAYING: case RING_SUSTAIN_ABILITIES: case RING_SUSTENANCE: - case RING_SLAYING: - case RING_SEE_INVISIBLE: case RING_TELEPORTATION: case RING_WIZARDRY: - case RING_REGENERATION: + case RING_TELEPORT_CONTROL: break; case RING_PROTECTION: you.redraw_armour_class = 1; - if (you.inv[item_slot].plus != 0) - ident = ID_KNOWN_TYPE; - break; - - case RING_INVISIBILITY: - if (!you.invis) - { - mpr("You become transparent for a moment."); - ident = ID_KNOWN_TYPE; - } break; case RING_EVASION: you.redraw_evasion = 1; - if (you.inv[item_slot].plus != 0) - ident = ID_KNOWN_TYPE; break; case RING_STRENGTH: - modify_stat(STAT_STRENGTH, you.inv[item_slot].plus, true); - if (you.inv[item_slot].plus != 0) - ident = ID_KNOWN_TYPE; + modify_stat(STAT_STRENGTH, -item.plus, true); break; case RING_DEXTERITY: - modify_stat(STAT_DEXTERITY, you.inv[item_slot].plus, true); - if (you.inv[item_slot].plus != 0) - ident = ID_KNOWN_TYPE; + modify_stat(STAT_DEXTERITY, -item.plus, true); break; case RING_INTELLIGENCE: - modify_stat(STAT_INTELLIGENCE, you.inv[item_slot].plus, true); - if (you.inv[item_slot].plus != 0) - ident = ID_KNOWN_TYPE; + modify_stat(STAT_INTELLIGENCE, -item.plus, true); break; - case RING_MAGICAL_POWER: - calc_mp(); - ident = ID_KNOWN_TYPE; + case RING_INVISIBILITY: + // removing this ring effectively cancels all invisibility {dlb} + if (you.invis) + you.invis = 1; break; case RING_LEVITATION: - mpr("You feel buoyant."); - ident = ID_KNOWN_TYPE; + // removing this ring effectively cancels all levitation {dlb} + if (you.levitation) + you.levitation = 1; break; - case RING_TELEPORT_CONTROL: - // XXX: is this safe or should we make it a function -- bwr - you.attribute[ATTR_CONTROL_TELEPORT]++; + case RING_MAGICAL_POWER: + // dec_max_mp(9); break; - case AMU_RAGE: - mpr("You feel a brief urge to hack something to bits."); - ident = ID_KNOWN_TYPE; + case AMU_THE_GOURMAND: + you.duration[DUR_GOURMAND] = 0; break; } - you.turn_is_over = 1; - - // Artefacts have completely different appearance than base types - // so we don't allow them to make the base types known - if (is_random_artefact( you.inv[item_slot] )) - use_randart(item_slot); - else - { - set_ident_type( you.inv[item_slot].base_type, - you.inv[item_slot].sub_type, ident ); - } - - if (ident == ID_KNOWN_TYPE) - set_ident_flags( you.inv[item_slot], ISFLAG_EQ_JEWELLERY_MASK ); - - if (item_cursed( you.inv[item_slot] )) - { - snprintf( info, INFO_SIZE, - "Oops, that %s feels deathly cold.", (is_amulet) ? "amulet" - : "ring" ); - mpr(info); - } - - // cursed or not, we know that since we've put the ring on - set_ident_flags( you.inv[item_slot], ISFLAG_KNOW_CURSE ); - - char str_pass[ ITEMNAME_SIZE ]; - in_name( item_slot, DESC_INVENTORY_EQUIP, str_pass ); - mpr( str_pass ); + if (is_random_artefact(item)) + unuse_randart(item); - return (true); + // must occur after ring is removed -- bwr + calc_mp(); } -bool puton_ring(int slot, bool prompt_finger) -{ - int item_slot; - - if (inv_count() < 1) - { - canned_msg(MSG_NOTHING_CARRIED); - return (false); - } - - if (you.berserker) - { - canned_msg(MSG_TOO_BERSERK); - return (false); - } - - if (slot != -1) - item_slot = slot; - else - item_slot = prompt_invent_item( "Put on which piece of jewellery?", - OBJ_JEWELLERY ); - - if (item_slot == PROMPT_ABORT) - { - canned_msg( MSG_OK ); - return (false); - } - - return puton_item(item_slot, prompt_finger); -} // end puton_ring() - -bool remove_ring(int slot) +bool remove_ring(int slot, bool announce) { int hand_used = 10; int ring_wear_2; - char str_pass[ ITEMNAME_SIZE ]; if (you.equip[EQ_LEFT_RING] == -1 && you.equip[EQ_RIGHT_RING] == -1 && you.equip[EQ_AMULET] == -1) @@ -2045,7 +2372,9 @@ bool remove_ring(int slot) { int equipn = slot == -1? prompt_invent_item( "Remove which piece of jewellery?", - OBJ_JEWELLERY ) + MT_INVSELECT, + OBJ_JEWELLERY, true, true, true, + 0, NULL, OPER_REMOVE) : slot; if (equipn == PROMPT_ABORT) @@ -2081,99 +2410,33 @@ bool remove_ring(int slot) return (false); } - if (you.equip[hand_used + 7] == -1) + if (you.equip[hand_used + EQ_LEFT_RING] == -1) { mpr("I don't think you really meant that."); return (false); } - if (item_cursed( you.inv[you.equip[hand_used + 7]] )) + if (item_cursed( you.inv[you.equip[hand_used + EQ_LEFT_RING]] )) { - mpr("It's stuck to you!"); + if (announce) + mprf("%s is stuck to you!", + in_name(you.equip[hand_used + EQ_LEFT_RING], + DESC_CAP_YOUR)); + else + mpr("It's stuck to you!"); - set_ident_flags( you.inv[you.equip[hand_used + 7]], ISFLAG_KNOW_CURSE ); + set_ident_flags( you.inv[you.equip[hand_used + EQ_LEFT_RING]], + ISFLAG_KNOW_CURSE ); return (false); } - strcpy(info, "You remove "); - in_name(you.equip[hand_used + 7], DESC_NOCAP_YOUR, str_pass); - - strcat(info, str_pass); - strcat(info, "."); - mpr(info); - - // I'll still use ring_wear_2 here. - ring_wear_2 = you.equip[hand_used + 7]; - - switch (you.inv[ring_wear_2].sub_type) - { - case RING_FIRE: - case RING_HUNGER: - case RING_ICE: - case RING_LIFE_PROTECTION: - case RING_POISON_RESISTANCE: - case RING_PROTECTION_FROM_COLD: - case RING_PROTECTION_FROM_FIRE: - case RING_PROTECTION_FROM_MAGIC: - case RING_REGENERATION: - case RING_SEE_INVISIBLE: - case RING_SLAYING: - case RING_SUSTAIN_ABILITIES: - case RING_SUSTENANCE: - case RING_TELEPORTATION: - case RING_WIZARDRY: - break; - - case RING_PROTECTION: - you.redraw_armour_class = 1; - break; - - case RING_EVASION: - you.redraw_evasion = 1; - break; - - case RING_STRENGTH: - modify_stat(STAT_STRENGTH, -you.inv[ring_wear_2].plus, true); - break; - - case RING_DEXTERITY: - modify_stat(STAT_DEXTERITY, -you.inv[ring_wear_2].plus, true); - break; - - case RING_INTELLIGENCE: - modify_stat(STAT_INTELLIGENCE, -you.inv[ring_wear_2].plus, true); - break; - - case RING_INVISIBILITY: - // removing this ring effectively cancels all invisibility {dlb} - if (you.invis) - you.invis = 1; - break; - - case RING_LEVITATION: - // removing this ring effectively cancels all levitation {dlb} - if (you.levitation) - you.levitation = 1; - break; - - case RING_MAGICAL_POWER: - // dec_max_mp(9); - break; - - case RING_TELEPORT_CONTROL: - you.attribute[ATTR_CONTROL_TELEPORT]--; - break; - } - - if (is_random_artefact( you.inv[ring_wear_2] )) - unuse_randart(ring_wear_2); + ring_wear_2 = you.equip[hand_used + EQ_LEFT_RING]; + you.equip[hand_used + EQ_LEFT_RING] = -1; - you.equip[hand_used + 7] = -1; + jewellery_remove_effects(you.inv[ring_wear_2]); - // must occur after ring is removed -- bwr - calc_mp(); - - you.turn_is_over = 1; + you.time_taken = you.time_taken * 5 / 10; + you.turn_is_over = true; return (true); } // end remove_ring() @@ -2189,7 +2452,7 @@ void zap_wand(void) // system will default to cycling through all monsters. -- bwr int targ_mode = TARG_ANY; - beam.obviousEffect = false; + beam.obvious_effect = false; if (inv_count() < 1) { @@ -2203,21 +2466,46 @@ void zap_wand(void) return; } - item_slot = prompt_invent_item( "Zap which item?", OBJ_WANDS ); + item_slot = prompt_invent_item( "Zap which item?", + MT_INVSELECT, + OBJ_WANDS, + true, true, true, 0, NULL, + OPER_ZAP ); if (item_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); return; } - if (you.inv[item_slot].base_type != OBJ_WANDS - || you.inv[item_slot].plus < 1) + if (you.inv[item_slot].base_type != OBJ_WANDS) { canned_msg(MSG_NOTHING_HAPPENS); - you.turn_is_over = 1; + you.turn_is_over = true; return; } + // if you happen to be wielding the wand, its display might change + if (you.equip[EQ_WEAPON] == item_slot) + you.wield_change = true; + + if ( you.inv[item_slot].plus < 1 ) { + // it's an empty wand, inscribe it that way + canned_msg(MSG_NOTHING_HAPPENS); + you.turn_is_over = true; + if ( !item_ident(you.inv[item_slot], ISFLAG_KNOW_PLUSES) ) { + + if ( you.inv[item_slot].inscription.find("empty") == + std::string::npos ) { + + if ( !you.inv[item_slot].inscription.empty() ) + you.inv[item_slot].inscription += ' '; + you.inv[item_slot].inscription += "[empty]"; + + } + } + return; + } + if (item_ident( you.inv[item_slot], ISFLAG_KNOW_TYPE )) { if (you.inv[item_slot].sub_type == WAND_HASTING @@ -2275,12 +2563,11 @@ void zap_wand(void) beam.source_x = you.x_pos; beam.source_y = you.y_pos; - beam.target_x = zap_wand.tx; - beam.target_y = zap_wand.ty; + beam.set_target(zap_wand); zapping( type_zapped, 30 + roll_dice(2, you.skills[SK_EVOCATIONS]), beam ); - if (beam.obviousEffect == 1 || you.inv[item_slot].sub_type == WAND_FIREBALL) + if (beam.obvious_effect == 1 || you.inv[item_slot].sub_type == WAND_FIREBALL) { if (get_ident_type( you.inv[item_slot].base_type, you.inv[item_slot].sub_type ) != ID_KNOWN_TYPE) @@ -2309,7 +2596,7 @@ void zap_wand(void) && (item_ident( you.inv[item_slot], ISFLAG_KNOW_PLUSES ) || you.skills[SK_EVOCATIONS] > 5 + random2(15))) { - if (item_not_ident( you.inv[item_slot], ISFLAG_KNOW_PLUSES )) + if (!item_ident( you.inv[item_slot], ISFLAG_KNOW_PLUSES )) { mpr("Your skill with magical items lets you calculate the power of this device..."); } @@ -2325,9 +2612,34 @@ void zap_wand(void) exercise( SK_EVOCATIONS, 1 ); alert_nearby_monsters(); - you.turn_is_over = 1; + you.turn_is_over = true; } // end zap_wand() +/*** HP CHANGE ***/ +void inscribe_item() +{ + int item_slot; + char buf[79]; + if (inv_count() < 1) + { + mpr("You don't have anything to inscribe."); + return; + } + item_slot = prompt_invent_item( + "Inscribe which item? ", + MT_INVSELECT, + OSEL_ANY ); + if (item_slot == PROMPT_ABORT) + { + canned_msg( MSG_OK ); + return; + } + mpr( "Inscribe with what? ", MSGCH_PROMPT ); + get_input_line( buf, sizeof(buf) ); + you.inv[item_slot].inscription = std::string(buf); + you.wield_change = true; +} + void drink(void) { int item_slot; @@ -2357,7 +2669,10 @@ void drink(void) return; } - item_slot = prompt_invent_item( "Drink which item?", OBJ_POTIONS ); + item_slot = prompt_invent_item( "Drink which item?", + MT_INVSELECT, OBJ_POTIONS, + true, true, true, 0, NULL, + OPER_QUAFF ); if (item_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -2384,7 +2699,7 @@ void drink(void) } dec_inv_item_quantity( item_slot, 1 ); - you.turn_is_over = 1; + you.turn_is_over = true; lessen_hunger(40, true); } // end drink() @@ -2474,7 +2789,7 @@ bool drink_fountain(void) grd[you.x_pos][you.y_pos] = DNGN_DRY_FOUNTAIN_II; } - you.turn_is_over = 1; + you.turn_is_over = true; return true; } // end drink_fountain() @@ -2491,8 +2806,7 @@ static bool affix_weapon_enchantment( void ) switch (get_weapon_brand( you.inv[wpn] )) { case SPWPN_VORPAL: - if (damage_type( you.inv[wpn].base_type, - you.inv[wpn].sub_type ) != DVORP_CRUSHING) + if (get_vorpal_type( you.inv[wpn] ) != DVORP_CRUSHING) { strcat(info, "'s sharpness seems more permanent."); } @@ -2512,12 +2826,13 @@ static bool affix_weapon_enchantment( void ) beam.flavour = 2; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "fiery explosion"); + beam.name = "fiery explosion"; beam.colour = RED; beam.thrower = KILL_YOU; beam.aux_source = "a fiery explosion"; beam.ex_size = 2; - beam.isTracer = false; + beam.is_tracer = false; + beam.is_explosion = true; explosion(beam); break; @@ -2541,11 +2856,15 @@ static bool affix_weapon_enchantment( void ) break; case SPWPN_DISTORTION: + // [dshaligram] Attempting to fix a distortion brand gets you a free + // distortion effect, and no permabranding. Sorry, them's the breaks. strcat(info, " twongs alarmingly."); mpr(info); // from unwield_item - miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, "a distortion effect" ); + miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, + "a distortion effect" ); + success = false; break; default: @@ -2559,7 +2878,7 @@ static bool affix_weapon_enchantment( void ) return (success); } -static bool enchant_weapon( int which_stat, bool quiet ) +bool enchant_weapon( int which_stat, bool quiet ) { const int wpn = you.equip[ EQ_WEAPON ]; bool affected = true; @@ -2693,7 +3012,7 @@ static bool enchant_armour( void ) you.redraw_armour_class = 1; - hide2armour( &(you.inv[nthing].sub_type) ); + hide2armour(you.inv[nthing]); return (true); } @@ -2824,7 +3143,7 @@ void read_scroll(void) char str_pass[ ITEMNAME_SIZE ]; // added: scroll effects are never tracers. - beam.isTracer = false; + beam.is_tracer = false; if (you.berserker) { @@ -2838,7 +3157,10 @@ void read_scroll(void) return; } - int item_slot = prompt_invent_item( "Read which item?", OBJ_SCROLLS ); + int item_slot = prompt_invent_item( + "Read which item?", + MT_INVSELECT, + OBJ_SCROLLS ); if (item_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -2866,7 +3188,7 @@ void read_scroll(void) } // ok - now we FINALLY get to read a scroll !!! {dlb} - you.turn_is_over = 1; + you.turn_is_over = true; // imperfect vision prevents players from reading actual content {dlb}: if (you.mutation[MUT_BLURRY_VISION] @@ -2909,6 +3231,8 @@ void read_scroll(void) case SCR_PAPER: // remember paper scrolls handled as special case above, too: mpr("This scroll appears to be blank."); + if (you.mutation[MUT_BLURRY_VISION] == 3) + id_the_scroll = false; break; case SCR_RANDOM_USELESSNESS: @@ -2977,17 +3301,19 @@ void read_scroll(void) break; case SCR_TORMENT: - torment( you.x_pos, you.y_pos ); + torment( TORMENT_SCROLL, you.x_pos, you.y_pos ); // is only naughty if you know you're doing it if (get_ident_type( OBJ_SCROLLS, SCR_TORMENT ) == ID_KNOWN_TYPE) { - naughty(NAUGHTY_UNHOLY, 10); + did_god_conduct(DID_UNHOLY, 10); } break; case SCR_IMMOLATION: mpr("The scroll explodes in your hands!"); + // we do this here to prevent it from blowing itself up + dec_inv_item_quantity( item_slot, 1 ); beam.type = SYM_BURST; beam.damage = dice_def( 3, 10 ); @@ -2995,17 +3321,23 @@ void read_scroll(void) beam.flavour = BEAM_FIRE; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "fiery explosion"); + beam.name = "fiery explosion"; beam.colour = RED; // your explosion, (not someone else's explosion) beam.thrower = KILL_YOU; beam.aux_source = "reading a scroll of immolation"; beam.ex_size = 2; + beam.is_explosion = true; explosion(beam); break; case SCR_IDENTIFY: + if ( get_ident_type( OBJ_SCROLLS, scroll_type ) != ID_KNOWN_TYPE ) { + mpr("This is a scroll of identify!"); + more(); + } + set_ident_flags( you.inv[item_slot], ISFLAG_IDENT_MASK ); // important {dlb} @@ -3083,7 +3415,7 @@ void read_scroll(void) || (you.inv[ nthing ].base_type == OBJ_WEAPONS && (is_fixed_artefact( you.inv[ nthing ] ) || is_random_artefact( you.inv[ nthing ] ) - || launches_things( you.inv[ nthing ].sub_type )))) + || you.inv[nthing].sub_type == WPN_BLOWGUN))) { canned_msg(MSG_NOTHING_HAPPENS); break; @@ -3136,7 +3468,7 @@ void read_scroll(void) affected = EQ_WEAPON; for (i = EQ_CLOAK; i <= EQ_BODY_ARMOUR; i++) { - if (you.equip[i] != -1 && item_uncursed( you.inv[you.equip[i]] )) + if (you.equip[i] != -1 && !item_cursed( you.inv[you.equip[i]] )) { count++; if (one_chance_in( count )) @@ -3162,7 +3494,8 @@ void read_scroll(void) } // end switch // finally, destroy and identify the scroll - if (scroll_type != SCR_PAPER) + // scrolls of immolation were already destroyed earlier + if (scroll_type != SCR_PAPER && scroll_type != SCR_IMMOLATION) { dec_inv_item_quantity( item_slot, 1 ); } @@ -3171,9 +3504,12 @@ void read_scroll(void) (id_the_scroll) ? ID_KNOWN_TYPE : ID_TRIED_TYPE ); } // end read_scroll() -void original_name(void) +void examine_object(void) { - int item_slot = prompt_invent_item( "Examine which item?", -1 ); + int item_slot = prompt_invent_item( "Examine which item?", + MT_INVSELECT, -1, + true, true, true, 0, NULL, + OPER_EXAMINE ); if (item_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -3182,14 +3518,20 @@ void original_name(void) describe_item( you.inv[item_slot] ); redraw_screen(); + mesclr(true); } // end original_name() void use_randart(unsigned char item_wield_2) { - ASSERT( is_random_artefact( you.inv[ item_wield_2 ] ) ); + use_randart( you.inv[ item_wield_2 ] ); +} + +void use_randart(const item_def &item) +{ + ASSERT( is_random_artefact( item ) ); FixedVector< char, RA_PROPERTIES > proprt; - randart_wpn_properties( you.inv[item_wield_2], proprt ); + randart_wpn_properties( item, proprt ); if (proprt[RAP_AC]) you.redraw_armour_class = 1; diff --git a/crawl-ref/source/item_use.h b/crawl-ref/source/item_use.h index 87c8e31455..5f79db1476 100644 --- a/crawl-ref/source/item_use.h +++ b/crawl-ref/source/item_use.h @@ -3,6 +3,8 @@ * Summary: Functions for making use of inventory items. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 5/26/99 JDJ Exposed armour_prompt. takeoff_armour takes an index argument. @@ -15,13 +17,16 @@ #include +#include "externs.h" +#include "enum.h" // last updated 12may2000 {dlb} /* *********************************************************************** * called from: acr - item_use * *********************************************************************** */ -bool armour_prompt(const std::string & mesg, int *index); +bool armour_prompt(const std::string & mesg, int *index, + operation_types oper); // last updated 12may2000 {dlb} @@ -37,12 +42,13 @@ bool takeoff_armour(int index); * *********************************************************************** */ void drink(void); +bool elemental_missile_beam(int launcher_brand, int ammo_brand); // last updated 12may2000 {dlb} /* *********************************************************************** * called from: acr * *********************************************************************** */ -void original_name(void); +void examine_object(void); // last updated 12may2000 {dlb} @@ -63,7 +69,7 @@ void read_scroll(void); /* *********************************************************************** * called from: acr * *********************************************************************** */ -bool remove_ring(int slot = -1); +bool remove_ring(int slot = -1, bool announce = false); // last updated 12may2000 {dlb} @@ -98,7 +104,7 @@ struct item_def; /* *********************************************************************** * called from: food * *********************************************************************** */ -bool can_wield(const item_def& weapon); +bool can_wield(const item_def *weapon, bool say_why = false); // last updated 12may2000 {dlb} /* *********************************************************************** @@ -125,9 +131,20 @@ void wield_effects(int item_wield_2, bool showMsgs); * called from: delay.cc item_use.cc it_use2.cc * *********************************************************************** */ void use_randart( unsigned char item_wield_2 ); +void use_randart(const item_def &item); bool puton_item(int slot, bool prompt_finger = true); -int armour_equip_slot(const item_def &item); +bool enchant_weapon( int which_stat, bool quiet = false ); + +bool throw_it(struct bolt &pbolt, int throw_2, monsters *dummy_target = NULL); + +void inscribe_item(); +int launcher_shield_slowdown(const item_def &launcher, + const item_def *shield); +int launcher_final_speed(const item_def &launcher, + const item_def *shield); + +void warn_shield_penalties(); #endif diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc index b92dcfc387..4524ab919b 100644 --- a/crawl-ref/source/itemname.cc +++ b/crawl-ref/source/itemname.cc @@ -28,25 +28,28 @@ #include "externs.h" #include "invent.h" +#include "itemprop.h" #include "macro.h" #include "mon-util.h" +#include "notes.h" #include "randart.h" #include "skills2.h" #include "stuff.h" -#include "wpn-misc.h" #include "view.h" +#include "items.h" +char id[NUM_IDTYPE][MAX_SUBTYPES]; -char id[4][50]; -int prop[4][50][3]; -FixedArray < int, 20, 50 > mss; +static bool is_random_name_space( char let ); +static bool is_random_name_vowel( char let); +static const char *item_name_2( + const item_def &item, char buff[ ITEMNAME_SIZE ], bool terse ); -static bool is_random_name_vowel(unsigned char let); -static char item_name_2( const item_def &item, char buff[ ITEMNAME_SIZE ], bool terse ); +static char retvow(int sed); +static char retlet(int sed ); -char reduce(unsigned char reducee); -char retbit(char sed); -char retvow(char sed); +// [dshaligram] For calls to item_name without a pre-allocated buffer. +static char default_itembuf[ITEMNAME_SIZE]; bool is_vowel( const char chr ) { @@ -55,42 +58,6 @@ bool is_vowel( const char chr ) return (low == 'a' || low == 'e' || low == 'i' || low == 'o' || low == 'u'); } -// Some convenient functions to hide the bit operations and create -// an interface layer between the code and the data in case this -// gets changed again. -- bwr -bool item_cursed( const item_def &item ) -{ - return (item.flags & ISFLAG_CURSED); -} - -bool item_uncursed( const item_def &item ) -{ - return !(item.flags & ISFLAG_CURSED); -} - -bool item_known_cursed( const item_def &item ) -{ - return ((item.flags & ISFLAG_KNOW_CURSE) && (item.flags & ISFLAG_CURSED)); -} - -bool item_known_uncursed( const item_def &item ) -{ - return ((item.flags & ISFLAG_KNOW_CURSE) && !(item.flags & ISFLAG_CURSED)); -} - -#if 0 -// currently unused -bool fully_identified( const item_def &item ) -{ - return ((item.flags & ISFLAG_IDENT_MASK) == ISFLAG_IDENT_MASK); -} -#endif - -bool item_ident( const item_def &item, unsigned long flags ) -{ - return (item.flags & flags); -} - bool item_type_known( const item_def &item ) { return item_ident(item, ISFLAG_KNOW_TYPE) @@ -98,293 +65,35 @@ bool item_type_known( const item_def &item ) && id[IDTYPE_JEWELLERY][item.sub_type] == ID_KNOWN_TYPE); } -bool item_not_ident( const item_def &item, unsigned long flags ) -{ - return ( !(item.flags & flags) ); -} - -void do_curse_item( item_def &item ) -{ - item.flags |= ISFLAG_CURSED; -} - -void do_uncurse_item( item_def &item ) -{ - item.flags &= (~ISFLAG_CURSED); -} - -void set_ident_flags( item_def &item, unsigned long flags ) -{ - item.flags |= flags; -} - -void unset_ident_flags( item_def &item, unsigned long flags ) -{ - item.flags &= (~flags); -} - -// These six functions might seem silly, but they provide a nice layer -// for later changes to these systems. -- bwr -unsigned long get_equip_race( const item_def &item ) -{ - return (item.flags & ISFLAG_RACIAL_MASK); -} - -unsigned long get_equip_desc( const item_def &item ) -{ - return (item.flags & ISFLAG_COSMETIC_MASK); -} - -bool cmp_equip_race( const item_def &item, unsigned long val ) -{ - return (get_equip_race( item ) == val); -} - -bool cmp_equip_desc( const item_def &item, unsigned long val ) -{ - return (get_equip_desc( item ) == val); -} - -void set_equip_race( item_def &item, unsigned long flags ) -{ - ASSERT( (flags & ~ISFLAG_RACIAL_MASK) == 0 ); - - // first check for base-sub pairs that can't ever have racial types - switch (item.base_type) - { - case OBJ_WEAPONS: - if (item.sub_type == WPN_GIANT_CLUB - || item.sub_type == WPN_GIANT_SPIKED_CLUB - || item.sub_type == WPN_KATANA - || item.sub_type == WPN_SLING - || item.sub_type == WPN_KNIFE - || item.sub_type == WPN_QUARTERSTAFF - || item.sub_type == WPN_DEMON_BLADE - || item.sub_type == WPN_DEMON_WHIP - || item.sub_type == WPN_DEMON_TRIDENT) - { - return; - } - break; - - case OBJ_ARMOUR: - if (item.sub_type >= ARM_DRAGON_HIDE) - { - return; - } - break; - - case OBJ_MISSILES: - if (item.sub_type == MI_STONE || item.sub_type == MI_LARGE_ROCK) - { - return; - } - break; - - default: - return; - } - - // check that item is appropriate for racial type - switch (flags) - { - case ISFLAG_ELVEN: - if (item.base_type == OBJ_ARMOUR - && (item.sub_type == ARM_SPLINT_MAIL - || item.sub_type == ARM_BANDED_MAIL - || item.sub_type == ARM_PLATE_MAIL)) - { - return; - } - break; - - case ISFLAG_DWARVEN: - if (item.base_type == OBJ_ARMOUR - && (item.sub_type == ARM_ROBE - || item.sub_type == ARM_LEATHER_ARMOUR)) - { - return; - } - break; - - case ISFLAG_ORCISH: - default: - break; - } - - item.flags &= ~ISFLAG_RACIAL_MASK; // delete previous - item.flags |= flags; -} - -void set_equip_desc( item_def &item, unsigned long flags ) -{ - ASSERT( (flags & ~ISFLAG_COSMETIC_MASK) == 0 ); - - item.flags &= ~ISFLAG_COSMETIC_MASK; // delete previous - item.flags |= flags; -} - -short get_helmet_type( const item_def &item ) -{ - ASSERT( item.base_type == OBJ_ARMOUR && item.sub_type == ARM_HELMET ); - - return (item.plus2 & THELM_TYPE_MASK); -} - -short get_helmet_desc( const item_def &item ) -{ - ASSERT( item.base_type == OBJ_ARMOUR && item.sub_type == ARM_HELMET ); - - return (item.plus2 & THELM_DESC_MASK); -} - -void set_helmet_type( item_def &item, short type ) -{ - ASSERT( (type & ~THELM_TYPE_MASK) == 0 ); - ASSERT( item.base_type == OBJ_ARMOUR && item.sub_type == ARM_HELMET ); - - item.plus2 &= ~THELM_TYPE_MASK; - item.plus2 |= type; -} - -void set_helmet_desc( item_def &item, short type ) -{ - ASSERT( (type & ~THELM_DESC_MASK) == 0 ); - ASSERT( item.base_type == OBJ_ARMOUR && item.sub_type == ARM_HELMET ); - - item.plus2 &= ~THELM_DESC_MASK; - item.plus2 |= type; -} - -void set_helmet_random_desc( item_def &item ) -{ - ASSERT( item.base_type == OBJ_ARMOUR && item.sub_type == ARM_HELMET ); - - item.plus2 &= ~THELM_DESC_MASK; - item.plus2 |= (random2(8) << 8); -} - -bool cmp_helmet_type( const item_def &item, short val ) -{ - ASSERT( item.base_type == OBJ_ARMOUR && item.sub_type == ARM_HELMET ); - - return (get_helmet_type( item ) == val); -} - -bool cmp_helmet_desc( const item_def &item, short val ) -{ - ASSERT( item.base_type == OBJ_ARMOUR && item.sub_type == ARM_HELMET ); - - return (get_helmet_desc( item ) == val); -} - -bool set_item_ego_type( item_def &item, int item_type, int ego_type ) -{ - if (item.base_type == item_type - && !is_random_artefact( item ) - && !is_fixed_artefact( item )) - { - item.special = ego_type; - return (true); - } - - return (false); -} - -int get_weapon_brand( const item_def &item ) -{ - // Weapon ego types are "brands", so we do the randart lookup here. - - // Staves "brands" handled specially - if (item.base_type != OBJ_WEAPONS) - return (SPWPN_NORMAL); - - if (is_fixed_artefact( item )) - { - switch (item.special) - { - case SPWPN_SWORD_OF_CEREBOV: - return (SPWPN_FLAMING); - - case SPWPN_STAFF_OF_OLGREB: - return (SPWPN_VENOM); - - case SPWPN_VAMPIRES_TOOTH: - return (SPWPN_VAMPIRICISM); - - default: - return (SPWPN_NORMAL); - } - } - else if (is_random_artefact( item )) - { - return (randart_wpn_property( item, RAP_BRAND )); - } - - return (item.special); -} - -int get_ammo_brand( const item_def &item ) -{ - // no artefact arrows yet -- bwr - if (item.base_type != OBJ_MISSILES || is_random_artefact( item )) - return (SPMSL_NORMAL); - - return (item.special); -} - -int get_armour_ego_type( const item_def &item ) -{ - // artefact armours have no ego type, must look up powers separately - if (item.base_type != OBJ_ARMOUR - || (is_random_artefact( item ) && !is_unrandom_artefact( item ))) - { - return (SPARM_NORMAL); - } - - return (item.special); -} - -bool item_is_rod( const item_def &item ) -{ - return (item.base_type == OBJ_STAVES - && item.sub_type >= STAFF_SMITING && item.sub_type < STAFF_AIR); -} - -bool item_is_staff( const item_def &item ) -{ - // Isn't De Morgan's law wonderful. -- bwr - return (item.base_type == OBJ_STAVES - && (item.sub_type < STAFF_SMITING || item.sub_type >= STAFF_AIR)); -} - // it_name() and in_name() are now somewhat obsolete now that itemname -// takes item_def, so consider them depricated. -void it_name( int itn, char des, char buff[ ITEMNAME_SIZE ], bool terse ) +// takes item_def, so consider them deprecated. +const char *it_name( int itn, char des, char *buff, bool terse ) { - item_name( mitm[itn], des, buff, terse ); + return item_name( mitm[itn], des, buff, terse ); } // end it_name() -void in_name( int inn, char des, char buff[ ITEMNAME_SIZE ], bool terse ) +const char *in_name( int inn, char des, char *buff, bool terse ) { - item_name( you.inv[inn], des, buff, terse ); + return item_name( you.inv[inn], des, buff, terse ); } // end in_name() // quant_name is usful since it prints out a different number of items // than the item actually contains. -void quant_name( const item_def &item, int quant, char des, +const char *quant_name( const item_def &item, int quant, char des, char buff[ ITEMNAME_SIZE ], bool terse ) { // item_name now requires a "real" item, so we'll mangle a tmp item_def tmp = item; tmp.quantity = quant; - item_name( tmp, des, buff, terse ); + return item_name( tmp, des, buff, terse ); } // end quant_name() -char item_name( const item_def &item, char descrip, char buff[ ITEMNAME_SIZE ], - bool terse ) +// buff must be at least ITEMNAME_SIZE if non-NULL. If NULL, a static +// item buffer will be used. +const char *item_name( const item_def &item, char descrip, + char *buff, bool terse ) { const int item_clas = item.base_type; const int item_typ = item.sub_type; @@ -393,6 +102,9 @@ char item_name( const item_def &item, char descrip, char buff[ ITEMNAME_SIZE ], char tmp_quant[20]; char itm_name[ ITEMNAME_SIZE ] = ""; + if (!buff) + buff = default_itembuf; + item_name_2( item, itm_name, terse ); buff[0] = '\0'; @@ -400,7 +112,7 @@ char item_name( const item_def &item, char descrip, char buff[ ITEMNAME_SIZE ], if (descrip == DESC_INVENTORY_EQUIP || descrip == DESC_INVENTORY) { if (in_inventory(item)) // actually in inventory - snprintf( buff, ITEMNAME_SIZE, (terse) ? "%c) " : "%c - ", + snprintf( buff, ITEMNAME_SIZE, (terse) ? "%c) " : "%c - ", index_to_letter( item.link ) ); else descrip = DESC_CAP_A; @@ -561,15 +273,24 @@ char item_name( const item_def &item, char descrip, char buff[ ITEMNAME_SIZE ], strncat( buff, " (around neck)", ITEMNAME_SIZE ); } } + /*** HP CHANGE -- warning -- possible stack overflow error ***/ + if ( item.inscription.size() > 0 ) // has an inscription + { + strncat( buff, " {", 2 ); + strncat( buff, item.inscription.c_str(), ITEMNAME_SIZE ); + strncat( buff, "}", 1 ); + } - return (1); + return (buff); } // end item_name() // Note that "terse" is only currently used for the "in hand" listing on // the game screen. -static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], - bool terse ) +static const char *item_name_2( + const item_def &item, + char buff[ITEMNAME_SIZE], + bool terse ) { const int item_clas = item.base_type; const int item_typ = item.sub_type; @@ -598,7 +319,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], if (item_cursed( item )) strncat(buff, "cursed ", ITEMNAME_SIZE ); else if (Options.show_uncursed - && item_not_ident( item, ISFLAG_KNOW_PLUSES )) + && !item_ident( item, ISFLAG_KNOW_PLUSES )) { strncat(buff, "uncursed ", ITEMNAME_SIZE ); } @@ -687,7 +408,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], // Now that we can have "glowing elven" weapons, it's // probably a good idea to cut out the descriptive // term once it's become obsolete. -- bwr - if (item_not_ident( item, ISFLAG_KNOW_PLUSES ) && !terse) + if (!item_ident( item, ISFLAG_KNOW_PLUSES ) && !terse) { switch (get_equip_desc( item )) { @@ -760,7 +481,14 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], strncat(buff, (terse) ? " (speed)" : " of speed", ITEMNAME_SIZE ); break; case SPWPN_VORPAL: - switch (damage_type(item_clas, item_typ)) + if (is_range_weapon( item )) + { + strncat(buff, (terse) ? " (velocity)" : " of velocity", + ITEMNAME_SIZE ); + break; + } + + switch (get_vorpal_type(item)) { case DVORP_CRUSHING: strncat(buff, (terse) ? " (crush)" : " of crushing", ITEMNAME_SIZE ); @@ -774,6 +502,12 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], case DVORP_CHOPPING: strncat(buff, (terse) ? " (chop)" : " of chopping", ITEMNAME_SIZE ); break; + case DVORP_SLASHING: + strncat(buff, (terse) ? " (slash)" : " of slashing", ITEMNAME_SIZE ); + break; + case DVORP_STABBING: + strncat(buff, (terse) ? " (stab)" : " of stabbing", ITEMNAME_SIZE ); + break; } break; @@ -819,6 +553,11 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], { strncat( buff, (terse) ? "poison " : "poisoned ", ITEMNAME_SIZE ); } + + if (brand == SPMSL_CURARE) + { + strncat( buff, (terse) ? "curare " : "curare-tipped ", ITEMNAME_SIZE); + } if (item_ident( item, ISFLAG_KNOW_PLUSES )) { @@ -848,8 +587,8 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], (item_typ == MI_BOLT) ? "bolt" : (item_typ == MI_DART) ? "dart" : (item_typ == MI_NEEDLE) ? "needle" : - (item_typ == MI_EGGPLANT) ? "eggplant" : - (item_typ == MI_LARGE_ROCK) ? "large rock" : "", ITEMNAME_SIZE); + (item_typ == MI_LARGE_ROCK) ? "large rock" : + "hysterical raisin", ITEMNAME_SIZE); // this should probably be "" {dlb} if (it_quant > 1) @@ -862,7 +601,9 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], (brand == SPMSL_ICE) ? ((terse) ? " (ice)" : " of ice") : (brand == SPMSL_NORMAL) ? "" : (brand == SPMSL_POISONED) ? "" : - (brand == SPMSL_POISONED_II) ? "" : " (buggy)", ITEMNAME_SIZE ); + (brand == SPMSL_POISONED_II) ? "" : + (brand == SPMSL_CURARE) ? "" : + " (buggy)", ITEMNAME_SIZE ); } break; @@ -872,7 +613,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], if (item_cursed( item )) strncat(buff, "cursed ", ITEMNAME_SIZE ); else if (Options.show_uncursed - && item_not_ident( item, ISFLAG_KNOW_PLUSES )) + && !item_ident( item, ISFLAG_KNOW_PLUSES )) { strncat(buff, "uncursed ", ITEMNAME_SIZE ); } @@ -889,8 +630,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], strncat(buff, " ", ITEMNAME_SIZE ); } - if (item_typ == ARM_GLOVES - || (item_typ == ARM_BOOTS && item_plus2 == TBOOT_BOOTS)) + if (item_typ == ARM_GLOVES || item_typ == ARM_BOOTS) { strncat( buff, "pair of ", ITEMNAME_SIZE ); } @@ -904,7 +644,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], // Now that we can have "glowing elven" armour, it's // probably a good idea to cut out the descriptive // term once it's become obsolete. -- bwr - if (item_not_ident( item, ISFLAG_KNOW_PLUSES ) && !terse) + if (!item_ident( item, ISFLAG_KNOW_PLUSES ) && !terse) { switch (get_equip_desc( item )) { @@ -1129,43 +869,36 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], } else { - char primary = item.special / 14; - char secondary = item.special % 14; - - strncat(buff, - (primary == 0) ? "" : - (primary == 1) ? "bubbling " : - (primary == 2) ? "lumpy " : - (primary == 3) ? "fuming " : - (primary == 4) ? "smoky " : - (primary == 5) ? "fizzy " : - (primary == 6) ? "glowing " : - (primary == 7) ? "sedimented " : - (primary == 8) ? "metallic " : - (primary == 9) ? "murky " : - (primary == 10) ? "gluggy " : - (primary == 11) ? "viscous " : - (primary == 12) ? "oily " : - (primary == 13) ? "slimy " : - (primary == 14) ? "emulsified " : "buggy ", ITEMNAME_SIZE); - - strncat(buff, - (secondary == 0) ? "clear" : - (secondary == 1) ? "blue" : - (secondary == 2) ? "black" : - (secondary == 3) ? "silvery" : - (secondary == 4) ? "cyan" : - (secondary == 5) ? "purple" : - (secondary == 6) ? "orange" : - (secondary == 7) ? "inky" : - (secondary == 8) ? "red" : - (secondary == 9) ? "yellow" : - (secondary == 10) ? "green" : - (secondary == 11) ? "brown" : - (secondary == 12) ? "pink" : - (secondary == 13) ? "white" : "buggy", ITEMNAME_SIZE); - - strncat(buff, " potion", ITEMNAME_SIZE ); + int pqual = PQUAL(item.special); + int pcolour = PCOLOUR(item.special); + + static const char *potion_qualifiers[] = { + "", "bubbling ", "fuming ", "fizzy ", "viscous ", "lumpy ", + "smoky ", "glowing ", "sedimented ", "metallic ", "murky ", + "gluggy ", "oily ", "slimy ", "emulsified " + }; + ASSERT( sizeof(potion_qualifiers) / sizeof(*potion_qualifiers) + == PDQ_NQUALS ); + + static const char *potion_colours[] = { + "clear", "blue", "black", "silvery", "cyan", "purple", + "orange", "inky", "red", "yellow", "green", "brown", "pink", + "white" + }; + ASSERT( sizeof(potion_colours) / sizeof(*potion_colours) + == PDC_NCOLOURS ); + + const char *qualifier = + (pqual < 0 || pqual >= PDQ_NQUALS)? "bug-filled " + : potion_qualifiers[pqual]; + + const char *colour = + (pcolour < 0 || pcolour >= PDC_NCOLOURS)? "bogus" + : potion_colours[pcolour]; + + strncat(buff, qualifier, ITEMNAME_SIZE - strlen(buff)); + strncat(buff, colour, ITEMNAME_SIZE - strlen(buff)); + strncat(buff, " potion", ITEMNAME_SIZE - strlen(buff) ); if (it_quant > 1) strncat(buff, "s", ITEMNAME_SIZE ); @@ -1309,7 +1042,11 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], strncat(buff, "labeled ", ITEMNAME_SIZE ); char buff3[ ITEMNAME_SIZE ]; - make_name( item.special, it_plus, item_clas, 2, buff3 ); + const unsigned long sseed = + item.special + + static_cast(it_plus) + + (static_cast(item_clas) << 16); + make_name( sseed, true, buff3 ); strncat( buff, buff3 , ITEMNAME_SIZE ); if (id[ IDTYPE_SCROLLS ][item_typ] == ID_TRIED_TYPE) @@ -1328,8 +1065,14 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], { if (item_cursed( item )) strncat(buff, "cursed ", ITEMNAME_SIZE ); - else if (Options.show_uncursed - && item_not_ident( item, ISFLAG_KNOW_PLUSES )) + else if (Options.show_uncursed + && !terse + && (!ring_has_pluses(item) + || !item_ident(item, ISFLAG_KNOW_PLUSES)) + + // If the item is worn, its curse status is known, + // no need to belabour the obvious. + && get_equip_slot( &item ) == -1) { strncat(buff, "uncursed ", ITEMNAME_SIZE ); } @@ -1683,7 +1426,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], switch (item_typ) { case MISC_RUNE_OF_ZOT: - strncat( buff, (it_plus == RUNE_DIS) ? "iron" : + strncat(buff, (it_plus == RUNE_DIS) ? "iron" : (it_plus == RUNE_GEHENNA) ? "obsidian" : (it_plus == RUNE_COCYTUS) ? "icy" : (it_plus == RUNE_TARTARUS) ? "bone" : @@ -1721,7 +1464,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], case MISC_DECK_OF_TRICKS: case MISC_DECK_OF_WONDERS: strncat(buff, "deck of ", ITEMNAME_SIZE ); - strncat(buff, item_not_ident( item, ISFLAG_KNOW_TYPE ) ? "cards" : + strncat(buff, !item_ident( item, ISFLAG_KNOW_TYPE ) ? "cards" : (item_typ == MISC_DECK_OF_WONDERS) ? "wonders" : (item_typ == MISC_DECK_OF_SUMMONINGS) ? "summonings" : (item_typ == MISC_DECK_OF_TRICKS) ? "tricks" : @@ -1773,7 +1516,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], break; case MISC_LANTERN_OF_SHADOWS: - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) strncat(buff, "bone ", ITEMNAME_SIZE ); strncat(buff, "lantern", ITEMNAME_SIZE ); @@ -1782,7 +1525,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], break; case MISC_HORN_OF_GERYON: - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) strncat(buff, "silver ", ITEMNAME_SIZE ); strncat(buff, "horn", ITEMNAME_SIZE ); @@ -1791,7 +1534,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], break; case MISC_DISC_OF_STORMS: - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) strncat(buff, "grey ", ITEMNAME_SIZE ); strncat(buff, "disc", ITEMNAME_SIZE ); @@ -1800,7 +1543,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], break; case MISC_STONE_OF_EARTH_ELEMENTALS: - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) strncat(buff, "nondescript ", ITEMNAME_SIZE ); strncat(buff, "stone", ITEMNAME_SIZE ); @@ -1809,7 +1552,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], break; case MISC_BOTTLED_EFREET: - strncat(buff, (item_not_ident( item, ISFLAG_KNOW_TYPE )) + strncat(buff, (!item_ident( item, ISFLAG_KNOW_TYPE )) ? "sealed bronze flask" : "bottled efreet", ITEMNAME_SIZE ); break; @@ -1826,7 +1569,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], // compacted 15 Apr 2000 {dlb}: case OBJ_BOOKS: - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) { char primary = (item.special / 10); char secondary = (item.special % 10); @@ -1923,7 +1666,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], // compacted 15 Apr 2000 {dlb}: case OBJ_STAVES: - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) { strncat(buff, (item.special == 0) ? "curved" : (item.special == 1) ? "glowing" : @@ -1989,6 +1732,19 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], (item_typ == STAFF_CHANNELING) ? "channeling" : "bugginess", ITEMNAME_SIZE ); } + + if (item_is_rod( item ) && item.sub_type != STAFF_STRIKING + && item_ident( item, ISFLAG_KNOW_TYPE )) + { + strncat( buff, " (", ITEMNAME_SIZE ); + itoa( item.plus / ROD_CHARGE_MULT, tmp_quant, 10 ); + strncat( buff, tmp_quant, ITEMNAME_SIZE ); + strncat( buff, "/", ITEMNAME_SIZE ); + itoa( item.plus2 / ROD_CHARGE_MULT, tmp_quant, 10 ); + strncat( buff, tmp_quant, ITEMNAME_SIZE ); + strncat( buff, ")", ITEMNAME_SIZE ); + } + break; @@ -2133,7 +1889,7 @@ static char item_name_2( const item_def &item, char buff[ITEMNAME_SIZE], strncat(buff, "s", ITEMNAME_SIZE ); } - return 1; + return (buff); } // end item_name_2() void save_id(char identy[4][50]) @@ -2219,1035 +1975,401 @@ char get_ident_type(char cla, int ty) } } // end get_ident_type() -int property( const item_def &item, int prop_type ) +static MenuEntry *discoveries_item_mangle(MenuEntry *me) { - switch (item.base_type) - { - case OBJ_ARMOUR: - case OBJ_WEAPONS: - case OBJ_MISSILES: - return (prop[ item.base_type ][ item.sub_type ][ prop_type ]); - - case OBJ_STAVES: - if (item_is_staff( item )) - return (prop[ OBJ_WEAPONS ][ WPN_QUARTERSTAFF ][ prop_type ]); - else if (prop_type == PWPN_SPEED) // item is rod - return (10); // extra protection against speed 0 - else - return (0); - - default: - return (0); - } + InvEntry *ie = dynamic_cast(me); + MenuEntry *newme = new MenuEntry; + std::string txt = item_name(*ie->item, DESC_PLAIN); + newme->text = " " + txt; + newme->quantity = 0; + delete me; + + return (newme); } -int mass_item( const item_def &item ) +void check_item_knowledge() { - int unit_mass = 0; - - if (item.base_type == OBJ_GOLD) - { - unit_mass = 0; + int i,j; + + std::vector items; + + int idx_to_objtype[4] = { OBJ_WANDS, OBJ_SCROLLS, + OBJ_JEWELLERY, OBJ_POTIONS }; + int idx_to_maxtype[4] = { NUM_WANDS, NUM_SCROLLS, + NUM_JEWELLERY, NUM_POTIONS }; + + for (i = 0; i < 4; i++) { + for (j = 0; j < idx_to_maxtype[i]; j++) { + if (id[i][j] == ID_KNOWN_TYPE) { + item_def* ptmp = new item_def; + if ( ptmp != 0 ) { + ptmp->base_type = idx_to_objtype[i]; + ptmp->sub_type = j; + ptmp->colour = 1; + ptmp->quantity = 1; + items.push_back(ptmp); + } + } + } } - else if (item.base_type == OBJ_CORPSES) - { - unit_mass = mons_weight( item.plus ); - if (item.sub_type == CORPSE_SKELETON) - unit_mass /= 2; - } - else - { - unit_mass = mss[ item.base_type ][ item.sub_type ]; + if (items.empty()) + mpr("You don't recognise anything yet!"); + else { + InvMenu menu; + menu.set_title("You recognise:"); + menu.load_items(items, discoveries_item_mangle); + menu.set_flags(MF_NOSELECT); + menu.show(); + redraw_screen(); + + for ( std::vector::iterator iter = items.begin(); + iter != items.end(); ++iter ) + delete *iter; } +} // end check_item_knowledge() - return (unit_mass > 0 ? unit_mass : 0); -} -void init_properties(void) +// Used for: Pandemonium demonlords, shopkeepers, scrolls, random artefacts +int make_name( unsigned long seed, bool all_cap, char buff[ ITEMNAME_SIZE ] ) { - prop[OBJ_ARMOUR][ARM_ROBE][PARM_AC] = 1; - prop[OBJ_ARMOUR][ARM_ROBE][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_ROBE] = 60; - - prop[OBJ_ARMOUR][ARM_LEATHER_ARMOUR][PARM_AC] = 2; - prop[OBJ_ARMOUR][ARM_LEATHER_ARMOUR][PARM_EVASION] = -1; - mss[OBJ_ARMOUR][ARM_LEATHER_ARMOUR] = 150; - - prop[OBJ_ARMOUR][ARM_RING_MAIL][PARM_AC] = 4; - prop[OBJ_ARMOUR][ARM_RING_MAIL][PARM_EVASION] = -2; - mss[OBJ_ARMOUR][ARM_RING_MAIL] = 300; - - prop[OBJ_ARMOUR][ARM_SCALE_MAIL][PARM_AC] = 5; - prop[OBJ_ARMOUR][ARM_SCALE_MAIL][PARM_EVASION] = -2; - mss[OBJ_ARMOUR][ARM_SCALE_MAIL] = 400; - - prop[OBJ_ARMOUR][ARM_CHAIN_MAIL][PARM_AC] = 6; - prop[OBJ_ARMOUR][ARM_CHAIN_MAIL][PARM_EVASION] = -3; - mss[OBJ_ARMOUR][ARM_CHAIN_MAIL] = 450; - - prop[OBJ_ARMOUR][ARM_SPLINT_MAIL][PARM_AC] = 8; - prop[OBJ_ARMOUR][ARM_SPLINT_MAIL][PARM_EVASION] = -5; - mss[OBJ_ARMOUR][ARM_SPLINT_MAIL] = 550; - - prop[OBJ_ARMOUR][ARM_BANDED_MAIL][PARM_AC] = 7; - prop[OBJ_ARMOUR][ARM_BANDED_MAIL][PARM_EVASION] = -4; - mss[OBJ_ARMOUR][ARM_BANDED_MAIL] = 500; - - prop[OBJ_ARMOUR][ARM_PLATE_MAIL][PARM_AC] = 9; - prop[OBJ_ARMOUR][ARM_PLATE_MAIL][PARM_EVASION] = -5; - mss[OBJ_ARMOUR][ARM_PLATE_MAIL] = 650; - - prop[OBJ_ARMOUR][ARM_DRAGON_HIDE][PARM_AC] = 2; - prop[OBJ_ARMOUR][ARM_DRAGON_HIDE][PARM_EVASION] = -2; - mss[OBJ_ARMOUR][ARM_DRAGON_HIDE] = 220; - - prop[OBJ_ARMOUR][ARM_TROLL_HIDE][PARM_AC] = 1; - prop[OBJ_ARMOUR][ARM_TROLL_HIDE][PARM_EVASION] = -1; - mss[OBJ_ARMOUR][ARM_TROLL_HIDE] = 180; - - prop[OBJ_ARMOUR][ARM_CRYSTAL_PLATE_MAIL][PARM_AC] = 16; - prop[OBJ_ARMOUR][ARM_CRYSTAL_PLATE_MAIL][PARM_EVASION] = -8; - mss[OBJ_ARMOUR][ARM_CRYSTAL_PLATE_MAIL] = 1200; - - prop[OBJ_ARMOUR][ARM_DRAGON_ARMOUR][PARM_AC] = 8; - prop[OBJ_ARMOUR][ARM_DRAGON_ARMOUR][PARM_EVASION] = -2; - mss[OBJ_ARMOUR][ARM_DRAGON_ARMOUR] = 220; - - prop[OBJ_ARMOUR][ARM_TROLL_LEATHER_ARMOUR][PARM_AC] = 3; - prop[OBJ_ARMOUR][ARM_TROLL_LEATHER_ARMOUR][PARM_EVASION] = -1; - mss[OBJ_ARMOUR][ARM_TROLL_LEATHER_ARMOUR] = 180; - - prop[OBJ_ARMOUR][ARM_ICE_DRAGON_HIDE][PARM_AC] = 2; - prop[OBJ_ARMOUR][ARM_ICE_DRAGON_HIDE][PARM_EVASION] = -2; - mss[OBJ_ARMOUR][ARM_ICE_DRAGON_HIDE] = 220; - - prop[OBJ_ARMOUR][ARM_ICE_DRAGON_ARMOUR][PARM_AC] = 9; - prop[OBJ_ARMOUR][ARM_ICE_DRAGON_ARMOUR][PARM_EVASION] = -2; - mss[OBJ_ARMOUR][ARM_ICE_DRAGON_ARMOUR] = 220; - - prop[OBJ_ARMOUR][ARM_STEAM_DRAGON_HIDE][PARM_AC] = 0; - prop[OBJ_ARMOUR][ARM_STEAM_DRAGON_HIDE][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_STEAM_DRAGON_HIDE] = 120; - - prop[OBJ_ARMOUR][ARM_STEAM_DRAGON_ARMOUR][PARM_AC] = 3; - prop[OBJ_ARMOUR][ARM_STEAM_DRAGON_ARMOUR][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_STEAM_DRAGON_ARMOUR] = 120; - - prop[OBJ_ARMOUR][ARM_MOTTLED_DRAGON_HIDE][PARM_AC] = 1; - prop[OBJ_ARMOUR][ARM_MOTTLED_DRAGON_HIDE][PARM_EVASION] = -1; - mss[OBJ_ARMOUR][ARM_MOTTLED_DRAGON_HIDE] = 150; - - prop[OBJ_ARMOUR][ARM_MOTTLED_DRAGON_ARMOUR][PARM_AC] = 5; - prop[OBJ_ARMOUR][ARM_MOTTLED_DRAGON_ARMOUR][PARM_EVASION] = -1; - mss[OBJ_ARMOUR][ARM_MOTTLED_DRAGON_ARMOUR] = 150; - - prop[OBJ_ARMOUR][ARM_STORM_DRAGON_HIDE][PARM_AC] = 2; - prop[OBJ_ARMOUR][ARM_STORM_DRAGON_HIDE][PARM_EVASION] = -5; - mss[OBJ_ARMOUR][ARM_STORM_DRAGON_HIDE] = 400; - - prop[OBJ_ARMOUR][ARM_STORM_DRAGON_ARMOUR][PARM_AC] = 10; - prop[OBJ_ARMOUR][ARM_STORM_DRAGON_ARMOUR][PARM_EVASION] = -5; - mss[OBJ_ARMOUR][ARM_STORM_DRAGON_ARMOUR] = 400; - - prop[OBJ_ARMOUR][ARM_GOLD_DRAGON_HIDE][PARM_AC] = 2; - prop[OBJ_ARMOUR][ARM_GOLD_DRAGON_HIDE][PARM_EVASION] = -10; - mss[OBJ_ARMOUR][ARM_GOLD_DRAGON_HIDE] = 1100; - - prop[OBJ_ARMOUR][ARM_GOLD_DRAGON_ARMOUR][PARM_AC] = 13; - prop[OBJ_ARMOUR][ARM_GOLD_DRAGON_ARMOUR][PARM_EVASION] = -10; - mss[OBJ_ARMOUR][ARM_GOLD_DRAGON_ARMOUR] = 1100; - - prop[OBJ_ARMOUR][ARM_ANIMAL_SKIN][PARM_AC] = 1; - prop[OBJ_ARMOUR][ARM_ANIMAL_SKIN][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_ANIMAL_SKIN] = 100; - - prop[OBJ_ARMOUR][ARM_SWAMP_DRAGON_HIDE][PARM_AC] = 1; - prop[OBJ_ARMOUR][ARM_SWAMP_DRAGON_HIDE][PARM_EVASION] = -2; - mss[OBJ_ARMOUR][ARM_SWAMP_DRAGON_HIDE] = 200; - - prop[OBJ_ARMOUR][ARM_SWAMP_DRAGON_ARMOUR][PARM_AC] = 7; - prop[OBJ_ARMOUR][ARM_SWAMP_DRAGON_ARMOUR][PARM_EVASION] = -2; - mss[OBJ_ARMOUR][ARM_SWAMP_DRAGON_ARMOUR] = 200; - - prop[OBJ_ARMOUR][ARM_SHIELD][PARM_AC] = 0; - prop[OBJ_ARMOUR][ARM_SHIELD][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_SHIELD] = 100; - - prop[OBJ_ARMOUR][ARM_CLOAK][PARM_AC] = 1; - prop[OBJ_ARMOUR][ARM_CLOAK][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_CLOAK] = 20; - - prop[OBJ_ARMOUR][ARM_HELMET][PARM_AC] = 1; - prop[OBJ_ARMOUR][ARM_HELMET][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_HELMET] = 80; - - prop[OBJ_ARMOUR][ARM_GLOVES][PARM_AC] = 1; - prop[OBJ_ARMOUR][ARM_GLOVES][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_GLOVES] = 20; - - prop[OBJ_ARMOUR][ARM_BOOTS][PARM_AC] = 1; - prop[OBJ_ARMOUR][ARM_BOOTS][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_BOOTS] = 40; - - prop[OBJ_ARMOUR][ARM_BUCKLER][PARM_AC] = 0; - prop[OBJ_ARMOUR][ARM_BUCKLER][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_BUCKLER] = 50; - - prop[OBJ_ARMOUR][ARM_LARGE_SHIELD][PARM_AC] = 0; - prop[OBJ_ARMOUR][ARM_LARGE_SHIELD][PARM_EVASION] = 0; - mss[OBJ_ARMOUR][ARM_LARGE_SHIELD] = 250; + char name[ITEMNAME_SIZE]; + int numb[17]; int i = 0; + bool want_vowel = false; + bool has_space = false; + + for (i = 0; i < ITEMNAME_SIZE; i++) + name[i] = '\0'; + + const int var1 = (seed & 0xFF); + const int var2 = ((seed >> 8) & 0xFF); + const int var3 = ((seed >> 16) & 0xFF); + const int var4 = ((seed >> 24) & 0xFF); + + numb[0] = 373 * var1 + 409 * var2 + 281 * var3; + numb[1] = 163 * var4 + 277 * var2 + 317 * var3; + numb[2] = 257 * var1 + 179 * var4 + 83 * var3; + numb[3] = 61 * var1 + 229 * var2 + 241 * var4; + numb[4] = 79 * var1 + 263 * var2 + 149 * var3; + numb[5] = 233 * var4 + 383 * var2 + 311 * var3; + numb[6] = 199 * var1 + 211 * var4 + 103 * var3; + numb[7] = 139 * var1 + 109 * var2 + 349 * var4; + numb[8] = 43 * var1 + 389 * var2 + 359 * var3; + numb[9] = 367 * var4 + 101 * var2 + 251 * var3; + numb[10] = 293 * var1 + 59 * var4 + 151 * var3; + numb[11] = 331 * var1 + 107 * var2 + 307 * var4; + numb[12] = 73 * var1 + 157 * var2 + 347 * var3; + numb[13] = 379 * var4 + 353 * var2 + 227 * var3; + numb[14] = 181 * var1 + 173 * var4 + 193 * var3; + numb[15] = 131 * var1 + 167 * var2 + 53 * var4; + numb[16] = 313 * var1 + 127 * var2 + 401 * var3 + 337 * var4; + + int len = 3 + numb[0] % 5 + ((numb[1] % 5 == 0) ? numb[2] % 6 : 1); + + if (all_cap) + len += 6; + + int j = numb[3] % 17; + const int k = numb[4] % 17; + int count = 0; - for (i = 0; i < 50; i++) - { - mss[OBJ_WANDS][i] = 100; - mss[OBJ_FOOD][i] = 100; - mss[OBJ_UNKNOWN_I][i] = 200; // labeled as "books" elsewhere - - //jmf: made scrolls, jewellery and potions weigh less. - mss[OBJ_SCROLLS][i] = 20; - mss[OBJ_JEWELLERY][i] = 10; - mss[OBJ_POTIONS][i] = 40; - mss[OBJ_UNKNOWN_II][i] = 5; // "gems" - mss[OBJ_BOOKS][i] = 70; - - mss[OBJ_STAVES][i] = 130; - mss[OBJ_ORBS][i] = 300; - mss[OBJ_MISCELLANY][i] = 100; - mss[OBJ_CORPSES][i] = 100; - } - - // rods are lighter than staves - for (i = STAFF_SMITING; i < STAFF_AIR; i++) - { - mss[OBJ_STAVES][i] = 70; - } - - // this is food, right? - mss[OBJ_FOOD][FOOD_MEAT_RATION] = 80; - mss[OBJ_FOOD][FOOD_BREAD_RATION] = 80; - mss[OBJ_FOOD][FOOD_PEAR] = 20; - mss[OBJ_FOOD][FOOD_APPLE] = 20; - mss[OBJ_FOOD][FOOD_CHOKO] = 30; - mss[OBJ_FOOD][FOOD_HONEYCOMB] = 40; - mss[OBJ_FOOD][FOOD_ROYAL_JELLY] = 55; - mss[OBJ_FOOD][FOOD_SNOZZCUMBER] = 50; - mss[OBJ_FOOD][FOOD_PIZZA] = 40; - mss[OBJ_FOOD][FOOD_APRICOT] = 15; - mss[OBJ_FOOD][FOOD_ORANGE] = 20; - mss[OBJ_FOOD][FOOD_BANANA] = 20; - mss[OBJ_FOOD][FOOD_STRAWBERRY] = 5; - mss[OBJ_FOOD][FOOD_RAMBUTAN] = 10; - mss[OBJ_FOOD][FOOD_LEMON] = 20; - mss[OBJ_FOOD][FOOD_GRAPE] = 5; - mss[OBJ_FOOD][FOOD_SULTANA] = 3; - mss[OBJ_FOOD][FOOD_LYCHEE] = 10; - mss[OBJ_FOOD][FOOD_BEEF_JERKY] = 20; - mss[OBJ_FOOD][FOOD_CHEESE] = 40; - mss[OBJ_FOOD][FOOD_SAUSAGE] = 40; - mss[OBJ_FOOD][FOOD_CHUNK] = 100; - /* mss [OBJ_FOOD] [21] = 40; - mss [OBJ_FOOD] [22] = 50; - mss [OBJ_FOOD] [23] = 60; - mss [OBJ_FOOD] [24] = 60; - mss [OBJ_FOOD] [25] = 100; */ - - mss[OBJ_MISCELLANY][MISC_BOTTLED_EFREET] = 250; - - mss[OBJ_MISCELLANY][MISC_CRYSTAL_BALL_OF_SEEING] = 200; - mss[OBJ_MISCELLANY][MISC_CRYSTAL_BALL_OF_ENERGY] = 200; - mss[OBJ_MISCELLANY][MISC_CRYSTAL_BALL_OF_FIXATION] = 200; - - // weapons: blunt weapons are first to help grouping them together - // note: AC prop can't be 0 or less because of division. - // If it's 1, makes no difference - - // NOTE: I have *removed* AC bit for weapons - just doesn't work - // prop [x] [2] is speed - - prop[OBJ_WEAPONS][WPN_CLUB][PWPN_DAMAGE] = 5; - prop[OBJ_WEAPONS][WPN_CLUB][PWPN_HIT] = 4; // helps to get past evasion - prop[OBJ_WEAPONS][WPN_CLUB][PWPN_SPEED] = 12; - mss[OBJ_WEAPONS][WPN_CLUB] = 50; - - prop[OBJ_WEAPONS][WPN_MACE][PWPN_DAMAGE] = 8; - prop[OBJ_WEAPONS][WPN_MACE][PWPN_HIT] = 3; // helps to get past evasion - prop[OBJ_WEAPONS][WPN_MACE][PWPN_SPEED] = 14; - mss[OBJ_WEAPONS][WPN_MACE] = 140; - - prop[OBJ_WEAPONS][WPN_GREAT_MACE][PWPN_DAMAGE] = 16; - prop[OBJ_WEAPONS][WPN_GREAT_MACE][PWPN_HIT] = -3; - prop[OBJ_WEAPONS][WPN_GREAT_MACE][PWPN_SPEED] = 18; - mss[OBJ_WEAPONS][WPN_GREAT_MACE] = 260; - - prop[OBJ_WEAPONS][WPN_FLAIL][PWPN_DAMAGE] = 9; - prop[OBJ_WEAPONS][WPN_FLAIL][PWPN_HIT] = 2; // helps to get past evasion - prop[OBJ_WEAPONS][WPN_FLAIL][PWPN_SPEED] = 15; - mss[OBJ_WEAPONS][WPN_FLAIL] = 150; - - prop[OBJ_WEAPONS][WPN_SPIKED_FLAIL][PWPN_DAMAGE] = 12; - prop[OBJ_WEAPONS][WPN_SPIKED_FLAIL][PWPN_HIT] = 1; - prop[OBJ_WEAPONS][WPN_SPIKED_FLAIL][PWPN_SPEED] = 16; - mss[OBJ_WEAPONS][WPN_SPIKED_FLAIL] = 170; - - prop[OBJ_WEAPONS][WPN_GREAT_FLAIL][PWPN_DAMAGE] = 17; - prop[OBJ_WEAPONS][WPN_GREAT_FLAIL][PWPN_HIT] = -4; - prop[OBJ_WEAPONS][WPN_GREAT_FLAIL][PWPN_SPEED] = 19; - mss[OBJ_WEAPONS][WPN_GREAT_FLAIL] = 300; - - prop[OBJ_WEAPONS][WPN_DAGGER][PWPN_DAMAGE] = 3; - prop[OBJ_WEAPONS][WPN_DAGGER][PWPN_HIT] = 6; // helps to get past evasion - prop[OBJ_WEAPONS][WPN_DAGGER][PWPN_SPEED] = 11; - mss[OBJ_WEAPONS][WPN_DAGGER] = 20; - - prop[OBJ_WEAPONS][WPN_KNIFE][PWPN_DAMAGE] = 2; - prop[OBJ_WEAPONS][WPN_KNIFE][PWPN_HIT] = 0; // helps to get past evasion - prop[OBJ_WEAPONS][WPN_KNIFE][PWPN_SPEED] = 11; - mss[OBJ_WEAPONS][WPN_KNIFE] = 10; - - prop[OBJ_WEAPONS][WPN_MORNINGSTAR][PWPN_DAMAGE] = 10; - prop[OBJ_WEAPONS][WPN_MORNINGSTAR][PWPN_HIT] = 2; - prop[OBJ_WEAPONS][WPN_MORNINGSTAR][PWPN_SPEED] = 15; - mss[OBJ_WEAPONS][WPN_MORNINGSTAR] = 150; - - prop[OBJ_WEAPONS][WPN_SHORT_SWORD][PWPN_DAMAGE] = 6; - prop[OBJ_WEAPONS][WPN_SHORT_SWORD][PWPN_HIT] = 5; - prop[OBJ_WEAPONS][WPN_SHORT_SWORD][PWPN_SPEED] = 12; - mss[OBJ_WEAPONS][WPN_SHORT_SWORD] = 100; - - prop[OBJ_WEAPONS][WPN_LONG_SWORD][PWPN_DAMAGE] = 10; - prop[OBJ_WEAPONS][WPN_LONG_SWORD][PWPN_HIT] = 3; - prop[OBJ_WEAPONS][WPN_LONG_SWORD][PWPN_SPEED] = 14; - mss[OBJ_WEAPONS][WPN_LONG_SWORD] = 160; - - prop[OBJ_WEAPONS][WPN_GREAT_SWORD][PWPN_DAMAGE] = 16; - prop[OBJ_WEAPONS][WPN_GREAT_SWORD][PWPN_HIT] = -1; - prop[OBJ_WEAPONS][WPN_GREAT_SWORD][PWPN_SPEED] = 17; - mss[OBJ_WEAPONS][WPN_GREAT_SWORD] = 250; - - prop[OBJ_WEAPONS][WPN_FALCHION][PWPN_DAMAGE] = 8; - prop[OBJ_WEAPONS][WPN_FALCHION][PWPN_HIT] = 2; - prop[OBJ_WEAPONS][WPN_FALCHION][PWPN_SPEED] = 13; - mss[OBJ_WEAPONS][WPN_FALCHION] = 130; - - prop[OBJ_WEAPONS][WPN_SCIMITAR][PWPN_DAMAGE] = 11; - prop[OBJ_WEAPONS][WPN_SCIMITAR][PWPN_HIT] = 1; - prop[OBJ_WEAPONS][WPN_SCIMITAR][PWPN_SPEED] = 14; - mss[OBJ_WEAPONS][WPN_SCIMITAR] = 170; - - prop[OBJ_WEAPONS][WPN_HAND_AXE][PWPN_DAMAGE] = 7; - prop[OBJ_WEAPONS][WPN_HAND_AXE][PWPN_HIT] = 2; - prop[OBJ_WEAPONS][WPN_HAND_AXE][PWPN_SPEED] = 13; - mss[OBJ_WEAPONS][WPN_HAND_AXE] = 110; - - prop[OBJ_WEAPONS][WPN_WAR_AXE][PWPN_DAMAGE] = 11; - prop[OBJ_WEAPONS][WPN_WAR_AXE][PWPN_HIT] = 0; - prop[OBJ_WEAPONS][WPN_WAR_AXE][PWPN_SPEED] = 16; - mss[OBJ_WEAPONS][WPN_WAR_AXE] = 150; - - prop[OBJ_WEAPONS][WPN_BROAD_AXE][PWPN_DAMAGE] = 14; - prop[OBJ_WEAPONS][WPN_BROAD_AXE][PWPN_HIT] = 1; - prop[OBJ_WEAPONS][WPN_BROAD_AXE][PWPN_SPEED] = 17; - mss[OBJ_WEAPONS][WPN_BROAD_AXE] = 180; - - prop[OBJ_WEAPONS][WPN_BATTLEAXE][PWPN_DAMAGE] = 17; - prop[OBJ_WEAPONS][WPN_BATTLEAXE][PWPN_HIT] = -2; - prop[OBJ_WEAPONS][WPN_BATTLEAXE][PWPN_SPEED] = 18; - mss[OBJ_WEAPONS][WPN_BATTLEAXE] = 200; - - prop[OBJ_WEAPONS][WPN_SPEAR][PWPN_DAMAGE] = 5; - prop[OBJ_WEAPONS][WPN_SPEAR][PWPN_HIT] = 3; - prop[OBJ_WEAPONS][WPN_SPEAR][PWPN_SPEED] = 13; - mss[OBJ_WEAPONS][WPN_SPEAR] = 50; - - prop[OBJ_WEAPONS][WPN_TRIDENT][PWPN_DAMAGE] = 9; - prop[OBJ_WEAPONS][WPN_TRIDENT][PWPN_HIT] = -2; - prop[OBJ_WEAPONS][WPN_TRIDENT][PWPN_SPEED] = 17; - mss[OBJ_WEAPONS][WPN_TRIDENT] = 160; - - prop[OBJ_WEAPONS][WPN_DEMON_TRIDENT][PWPN_DAMAGE] = 15; - prop[OBJ_WEAPONS][WPN_DEMON_TRIDENT][PWPN_HIT] = -2; - prop[OBJ_WEAPONS][WPN_DEMON_TRIDENT][PWPN_SPEED] = 17; - mss[OBJ_WEAPONS][WPN_DEMON_TRIDENT] = 160; - - prop[OBJ_WEAPONS][WPN_HALBERD][PWPN_DAMAGE] = 13; - prop[OBJ_WEAPONS][WPN_HALBERD][PWPN_HIT] = -3; - prop[OBJ_WEAPONS][WPN_HALBERD][PWPN_SPEED] = 19; - mss[OBJ_WEAPONS][WPN_HALBERD] = 200; - - // sling - // - the three properties are _not_ irrelevant here - // - when something hits something else (either may be you) - // in melee, these are used. - prop[OBJ_WEAPONS][WPN_SLING][PWPN_DAMAGE] = 1; - prop[OBJ_WEAPONS][WPN_SLING][PWPN_HIT] = -1; - prop[OBJ_WEAPONS][WPN_SLING][PWPN_SPEED] = 11; - mss[OBJ_WEAPONS][WPN_SLING] = 10; - - prop[OBJ_WEAPONS][WPN_BOW][PWPN_DAMAGE] = 2; - prop[OBJ_WEAPONS][WPN_BOW][PWPN_HIT] = -3; // helps to get past evasion - prop[OBJ_WEAPONS][WPN_BOW][PWPN_SPEED] = 11; - mss[OBJ_WEAPONS][WPN_BOW] = 100; - - prop[OBJ_WEAPONS][WPN_BLOWGUN][PWPN_DAMAGE] = 1; - prop[OBJ_WEAPONS][WPN_BLOWGUN][PWPN_HIT] = 0; // helps to get past evasion - prop[OBJ_WEAPONS][WPN_BLOWGUN][PWPN_SPEED] = 10; - mss[OBJ_WEAPONS][WPN_BLOWGUN] = 50; - - prop[OBJ_WEAPONS][WPN_CROSSBOW][PWPN_DAMAGE] = 2; - prop[OBJ_WEAPONS][WPN_CROSSBOW][PWPN_HIT] = -1; - prop[OBJ_WEAPONS][WPN_CROSSBOW][PWPN_SPEED] = 15; - mss[OBJ_WEAPONS][WPN_CROSSBOW] = 250; - - prop[OBJ_WEAPONS][WPN_HAND_CROSSBOW][PWPN_DAMAGE] = 1; - prop[OBJ_WEAPONS][WPN_HAND_CROSSBOW][PWPN_HIT] = -1; - prop[OBJ_WEAPONS][WPN_HAND_CROSSBOW][PWPN_SPEED] = 15; - mss[OBJ_WEAPONS][WPN_HAND_CROSSBOW] = 70; - - prop[OBJ_WEAPONS][WPN_GLAIVE][PWPN_DAMAGE] = 15; - prop[OBJ_WEAPONS][WPN_GLAIVE][PWPN_HIT] = -3; - prop[OBJ_WEAPONS][WPN_GLAIVE][PWPN_SPEED] = 18; - mss[OBJ_WEAPONS][WPN_GLAIVE] = 200; - - // staff - hmmm - prop[OBJ_WEAPONS][WPN_QUARTERSTAFF][PWPN_DAMAGE] = 7; - prop[OBJ_WEAPONS][WPN_QUARTERSTAFF][PWPN_HIT] = 6; - prop[OBJ_WEAPONS][WPN_QUARTERSTAFF][PWPN_SPEED] = 12; - mss[OBJ_WEAPONS][WPN_QUARTERSTAFF] = 130; - - prop[OBJ_WEAPONS][WPN_SCYTHE][PWPN_DAMAGE] = 14; - prop[OBJ_WEAPONS][WPN_SCYTHE][PWPN_HIT] = -4; - prop[OBJ_WEAPONS][WPN_SCYTHE][PWPN_SPEED] = 22; - mss[OBJ_WEAPONS][WPN_SCYTHE] = 230; - - // these two should have the same speed because of 2-h ogres. - prop[OBJ_WEAPONS][WPN_GIANT_CLUB][PWPN_DAMAGE] = 15; - prop[OBJ_WEAPONS][WPN_GIANT_CLUB][PWPN_HIT] = -5; - prop[OBJ_WEAPONS][WPN_GIANT_CLUB][PWPN_SPEED] = 16; - mss[OBJ_WEAPONS][WPN_GIANT_CLUB] = 750; - - prop[OBJ_WEAPONS][WPN_GIANT_SPIKED_CLUB][PWPN_DAMAGE] = 18; - prop[OBJ_WEAPONS][WPN_GIANT_SPIKED_CLUB][PWPN_HIT] = -6; - prop[OBJ_WEAPONS][WPN_GIANT_SPIKED_CLUB][PWPN_SPEED] = 17; - mss[OBJ_WEAPONS][WPN_GIANT_SPIKED_CLUB] = 850; - // these two should have the same speed because of 2-h ogres. - - prop[OBJ_WEAPONS][WPN_EVENINGSTAR][PWPN_DAMAGE] = 12; - prop[OBJ_WEAPONS][WPN_EVENINGSTAR][PWPN_HIT] = 2; - prop[OBJ_WEAPONS][WPN_EVENINGSTAR][PWPN_SPEED] = 15; - mss[OBJ_WEAPONS][WPN_EVENINGSTAR] = 150; - - prop[OBJ_WEAPONS][WPN_QUICK_BLADE][PWPN_DAMAGE] = 5; - prop[OBJ_WEAPONS][WPN_QUICK_BLADE][PWPN_HIT] = 6; - prop[OBJ_WEAPONS][WPN_QUICK_BLADE][PWPN_SPEED] = 7; - mss[OBJ_WEAPONS][WPN_QUICK_BLADE] = 100; - - prop[OBJ_WEAPONS][WPN_KATANA][PWPN_DAMAGE] = 13; - prop[OBJ_WEAPONS][WPN_KATANA][PWPN_HIT] = 4; - prop[OBJ_WEAPONS][WPN_KATANA][PWPN_SPEED] = 13; - mss[OBJ_WEAPONS][WPN_KATANA] = 160; - - prop[OBJ_WEAPONS][WPN_EXECUTIONERS_AXE][PWPN_DAMAGE] = 20; - prop[OBJ_WEAPONS][WPN_EXECUTIONERS_AXE][PWPN_HIT] = -4; - prop[OBJ_WEAPONS][WPN_EXECUTIONERS_AXE][PWPN_SPEED] = 20; - mss[OBJ_WEAPONS][WPN_EXECUTIONERS_AXE] = 320; - - prop[OBJ_WEAPONS][WPN_DOUBLE_SWORD][PWPN_DAMAGE] = 15; - prop[OBJ_WEAPONS][WPN_DOUBLE_SWORD][PWPN_HIT] = 3; - prop[OBJ_WEAPONS][WPN_DOUBLE_SWORD][PWPN_SPEED] = 16; - mss[OBJ_WEAPONS][WPN_DOUBLE_SWORD] = 220; - - prop[OBJ_WEAPONS][WPN_TRIPLE_SWORD][PWPN_DAMAGE] = 19; - prop[OBJ_WEAPONS][WPN_TRIPLE_SWORD][PWPN_HIT] = -1; - prop[OBJ_WEAPONS][WPN_TRIPLE_SWORD][PWPN_SPEED] = 19; - mss[OBJ_WEAPONS][WPN_TRIPLE_SWORD] = 300; - - prop[OBJ_WEAPONS][WPN_HAMMER][PWPN_DAMAGE] = 7; - prop[OBJ_WEAPONS][WPN_HAMMER][PWPN_HIT] = 2; - prop[OBJ_WEAPONS][WPN_HAMMER][PWPN_SPEED] = 13; - mss[OBJ_WEAPONS][WPN_HAMMER] = 130; - - prop[OBJ_WEAPONS][WPN_ANCUS][PWPN_DAMAGE] = 9; - prop[OBJ_WEAPONS][WPN_ANCUS][PWPN_HIT] = 1; - prop[OBJ_WEAPONS][WPN_ANCUS][PWPN_SPEED] = 14; - mss[OBJ_WEAPONS][WPN_ANCUS] = 160; - - prop[OBJ_WEAPONS][WPN_WHIP][PWPN_DAMAGE] = 3; - prop[OBJ_WEAPONS][WPN_WHIP][PWPN_HIT] = 1; // helps to get past evasion - prop[OBJ_WEAPONS][WPN_WHIP][PWPN_SPEED] = 14; - mss[OBJ_WEAPONS][WPN_WHIP] = 30; - - prop[OBJ_WEAPONS][WPN_SABRE][PWPN_DAMAGE] = 7; - prop[OBJ_WEAPONS][WPN_SABRE][PWPN_HIT] = 4; - prop[OBJ_WEAPONS][WPN_SABRE][PWPN_SPEED] = 12; - mss[OBJ_WEAPONS][WPN_SABRE] = 110; - - prop[OBJ_WEAPONS][WPN_DEMON_BLADE][PWPN_DAMAGE] = 13; - prop[OBJ_WEAPONS][WPN_DEMON_BLADE][PWPN_HIT] = 2; - prop[OBJ_WEAPONS][WPN_DEMON_BLADE][PWPN_SPEED] = 15; - mss[OBJ_WEAPONS][WPN_DEMON_BLADE] = 200; - - prop[OBJ_WEAPONS][WPN_DEMON_WHIP][PWPN_DAMAGE] = 10; - prop[OBJ_WEAPONS][WPN_DEMON_WHIP][PWPN_HIT] = 1; - prop[OBJ_WEAPONS][WPN_DEMON_WHIP][PWPN_SPEED] = 14; - mss[OBJ_WEAPONS][WPN_DEMON_WHIP] = 30; - - - // MISSILES: - - prop[OBJ_MISSILES][MI_STONE][PWPN_DAMAGE] = 2; - prop[OBJ_MISSILES][MI_STONE][PWPN_HIT] = 4; - mss[OBJ_MISSILES][MI_STONE] = 5; - - prop[OBJ_MISSILES][MI_ARROW][PWPN_DAMAGE] = 2; - prop[OBJ_MISSILES][MI_ARROW][PWPN_HIT] = 6; - mss[OBJ_MISSILES][MI_ARROW] = 10; - - prop[OBJ_MISSILES][MI_NEEDLE][PWPN_DAMAGE] = 0; - prop[OBJ_MISSILES][MI_NEEDLE][PWPN_HIT] = 1; - mss[OBJ_MISSILES][MI_NEEDLE] = 1; - - prop[OBJ_MISSILES][MI_BOLT][PWPN_DAMAGE] = 2; - prop[OBJ_MISSILES][MI_BOLT][PWPN_HIT] = 8; - mss[OBJ_MISSILES][MI_BOLT] = 12; - - prop[OBJ_MISSILES][MI_DART][PWPN_DAMAGE] = 2; - prop[OBJ_MISSILES][MI_DART][PWPN_HIT] = 4; //whatever - for hand crossbow - mss[OBJ_MISSILES][MI_DART] = 5; - - // large rock - prop[OBJ_MISSILES][MI_LARGE_ROCK][PWPN_DAMAGE] = 20; - prop[OBJ_MISSILES][MI_LARGE_ROCK][PWPN_HIT] = 10; - mss[OBJ_MISSILES][MI_LARGE_ROCK] = 1000; -} - - -unsigned char check_item_knowledge(void) -{ - char st_pass[ITEMNAME_SIZE] = ""; - int i, j; - char lines = 0; - unsigned char anything = 0; - int ft = 0; - int max = 0; - int yps = 0; - int inv_count = 0; - unsigned char ki = 0; - - const int num_lines = get_number_of_lines(); - -#ifdef DOS_TERM - char buffer[2400]; - - gettext(35, 1, 80, 25, buffer); -#endif - -#ifdef DOS_TERM - window(35, 1, 80, 25); -#endif - - clrscr(); - - for (i = 0; i < 4; i++) + for (i = 0; i < len; i++) { - for (j = 0; j < 30; j++) + j = (j + 1) % 17; + if (j == 0) { - if (id[i][j] == ID_KNOWN_TYPE) - inv_count++; + count++; + if (count > 9) + break; } - } - - if (inv_count == 0) - { - cprintf("You don't recognise anything yet!"); - if (getch() == 0) - getch(); - goto putty; - } - - textcolor(BLUE); - cprintf(" You recognise:"); - textcolor(LIGHTGREY); - lines++; - for (i = 0; i < 4; i++) - { - switch (i) + if (!has_space && i > 5 && i < len - 4 + && (numb[(k + 10 * j) % 17] % 5) != 3) { - case IDTYPE_WANDS: - ft = OBJ_WANDS; - max = NUM_WANDS; - break; - case IDTYPE_SCROLLS: - ft = OBJ_SCROLLS; - max = NUM_SCROLLS; - break; - case IDTYPE_JEWELLERY: - ft = OBJ_JEWELLERY; - max = NUM_JEWELLERY; - break; - case IDTYPE_POTIONS: - ft = OBJ_POTIONS; - max = NUM_POTIONS; - break; + want_vowel = true; + name[i] = ' '; } - - for (j = 0; j < max; j++) + else if (i > 0 + && (want_vowel + || (i > 1 + && is_random_name_vowel( name[i - 1] ) + && !is_random_name_vowel( name[i - 2] ) + && (numb[(k + 4 * j) % 17] % 5) <= 1 ))) { - if (lines > num_lines - 2 && inv_count > 0) - { - gotoxy(1, num_lines); - cprintf("-more-"); - - ki = getch(); + want_vowel = true; + name[i] = retvow( numb[(k + 7 * j) % 17] ); - if (ki == ESCAPE) + if (is_random_name_space( name[i] )) + { + if (i == 0) { -#ifdef DOS_TERM - puttext(35, 1, 80, 25, buffer); -#endif - return ESCAPE; + want_vowel = false; + name[i] = retlet( numb[(k + 14 * j) % 17] ); } - if (ki >= 'A' && ki <= 'z') + else if (len < 7 + || i <= 2 || i >= len - 3 + || is_random_name_space( name[i - 1] ) + || (i > 1 && is_random_name_space( name[i - 2] )) + || (i > 2 + && !is_random_name_vowel( name[i - 1] ) + && !is_random_name_vowel( name[i - 2] ))) { -#ifdef DOS_TERM - puttext(35, 1, 80, 25, buffer); -#endif - return ki; + i--; + continue; } - - if (ki == 0) - ki = getch(); - - lines = 0; - clrscr(); - gotoxy(1, 1); - anything = 0; } - - int ident_level = get_ident_type( ft, j ); - - if (ident_level == ID_KNOWN_TYPE) + else if (i > 1 + && name[i] == name[i - 1] + && (name[i] == 'y' || name[i] == 'i' + || (numb[(k + 12 * j) % 17] % 5) <= 1)) { - anything++; - - if (lines > 0) - cprintf(EOL); - lines++; - cprintf(" "); - - yps = wherey(); - - // item_name now requires a "real" item, so we'll create a tmp - item_def tmp;// = { ft, j, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; - tmp.base_type = ft; - tmp.sub_type = j; - tmp.colour = 1; - - item_name( tmp, DESC_PLAIN, st_pass ); - - cprintf(st_pass); - - inv_count--; - - if (wherey() != yps) - lines++; + i--; + continue; } - } // end of j loop - } - - if (anything > 0) - { - ki = getch(); - //ki = getch(); - //ki = anything; - - if (ki >= 'A' && ki <= 'z') - { -#ifdef DOS_TERM - puttext(35, 1, 80, 25, buffer); -#endif - return ki; } - - if (ki == 0) - ki = getch(); -#ifdef DOS_TERM - puttext(35, 1, 80, 25, buffer); -#endif - return anything; - } - - putty: -#ifdef DOS_TERM - puttext(35, 1, 80, 25, buffer); -#endif - - return ki; -} // end check_item_knowledge() - - -// must be certain that you are passing the subtype -// to an OBJ_ARMOUR and nothing else, or as they say, -// "Bad Things Will Happen" {dlb}: -bool hide2armour( unsigned char *which_subtype ) -{ - switch (*which_subtype) - { - case ARM_DRAGON_HIDE: - *which_subtype = ARM_DRAGON_ARMOUR; - return true; - case ARM_TROLL_HIDE: - *which_subtype = ARM_TROLL_LEATHER_ARMOUR; - return true; - case ARM_ICE_DRAGON_HIDE: - *which_subtype = ARM_ICE_DRAGON_ARMOUR; - return true; - case ARM_MOTTLED_DRAGON_HIDE: - *which_subtype = ARM_MOTTLED_DRAGON_ARMOUR; - return true; - case ARM_STORM_DRAGON_HIDE: - *which_subtype = ARM_STORM_DRAGON_ARMOUR; - return true; - case ARM_GOLD_DRAGON_HIDE: - *which_subtype = ARM_GOLD_DRAGON_ARMOUR; - return true; - case ARM_SWAMP_DRAGON_HIDE: - *which_subtype = ARM_SWAMP_DRAGON_ARMOUR; - return true; - case ARM_STEAM_DRAGON_HIDE: - *which_subtype = ARM_STEAM_DRAGON_ARMOUR; - return true; - default: - return false; - } -} // end hide2armour() - - -void make_name(unsigned char var1, unsigned char var2, unsigned char var3, - char ncase, char buff[ITEMNAME_SIZE]) -{ - char name[ ITEMNAME_SIZE ] = ""; - FixedVector < unsigned char, 15 > numb; - int len; - int i = 0; - int nexty = 0; - int j = 0; - int x = 0; - - numb[0] = var1 * var2; - numb[1] = var1 * var3; - numb[2] = var2 * var3; - numb[3] = var1 * var2 * var3; - numb[4] = var1 + var2; - numb[5] = var2 + var3; - numb[6] = var1 * var2 + var3; - numb[7] = var1 * var3 + var2; - numb[8] = var2 * var3 + var1; - numb[9] = var1 * var2 * var3 - var1; - numb[10] = var1 + var2 + var2; - numb[11] = var2 + var3 * var1; - numb[12] = var1 * var2 * var3; - numb[13] = var1 * var3 * var1; - numb[14] = var2 * var3 * var3; - - for (i = 0; i < 15; i++) - { - while (numb[i] >= 25) + else { - numb[i] -= 25; - } - } - - j = numb[6]; - - len = reduce(numb[reduce(numb[11]) / 2]); - - while (len < 5 && j < 10) - { - len += 1 + reduce(1 + numb[j]); - j++; - } - - while (len > 14) - { - len -= 8; - } - - nexty = retbit(numb[4]); - - char k = 0; + if ((len > 3 || i != 0) + && (numb[(k + 13 * j) % 17] % 7) <= 1 + && (i < len - 2 || (i > 0 && !is_random_name_space(name[i - 1])))) + { + const bool beg = ((i < 1) || is_random_name_space(name[i - 1])); + const bool end = (i >= len - 2); - j = 0; + const int first = (beg ? 0 : (end ? 14 : 0)); + const int last = (beg ? 27 : (end ? 56 : 67)); - for (i = 0; i < len; i++) - { - j++; - - if (j >= 15) - { - j = 0; + const int num = last - first; - k++; + i++; - if (k > 9) - break; - } - - if (nexty == 1 || (i > 0 && !is_random_name_vowel(name[i]))) - { - name[i] = retvow(numb[j]); - if ((i == 0 || i == len - 1) && name[i] == 32) + switch (numb[(k + 11 * j) % 17] % num + first) + { + // start, middle + case 0: strcat(name, "kl"); break; + case 1: strcat(name, "gr"); break; + case 2: strcat(name, "cl"); break; + case 3: strcat(name, "cr"); break; + case 4: strcat(name, "fr"); break; + case 5: strcat(name, "pr"); break; + case 6: strcat(name, "tr"); break; + case 7: strcat(name, "tw"); break; + case 8: strcat(name, "br"); break; + case 9: strcat(name, "pl"); break; + case 10: strcat(name, "bl"); break; + case 11: strcat(name, "str"); i++; len++; break; + case 12: strcat(name, "shr"); i++; len++; break; + case 13: strcat(name, "thr"); i++; len++; break; + // start, middle, end + case 14: strcat(name, "sm"); break; + case 15: strcat(name, "sh"); break; + case 16: strcat(name, "ch"); break; + case 17: strcat(name, "th"); break; + case 18: strcat(name, "ph"); break; + case 19: strcat(name, "pn"); break; + case 20: strcat(name, "kh"); break; + case 21: strcat(name, "gh"); break; + case 22: strcat(name, "mn"); break; + case 23: strcat(name, "ps"); break; + case 24: strcat(name, "st"); break; + case 25: strcat(name, "sk"); break; + case 26: strcat(name, "sch"); i++; len++; break; + // middle, end + case 27: strcat(name, "ts"); break; + case 28: strcat(name, "cs"); break; + case 29: strcat(name, "xt"); break; + case 30: strcat(name, "nt"); break; + case 31: strcat(name, "ll"); break; + case 32: strcat(name, "rr"); break; + case 33: strcat(name, "ss"); break; + case 34: strcat(name, "wk"); break; + case 35: strcat(name, "wn"); break; + case 36: strcat(name, "ng"); break; + case 37: strcat(name, "cw"); break; + case 38: strcat(name, "mp"); break; + case 39: strcat(name, "ck"); break; + case 40: strcat(name, "nk"); break; + case 41: strcat(name, "dd"); break; + case 42: strcat(name, "tt"); break; + case 43: strcat(name, "bb"); break; + case 44: strcat(name, "pp"); break; + case 45: strcat(name, "nn"); break; + case 46: strcat(name, "mm"); break; + case 47: strcat(name, "kk"); break; + case 48: strcat(name, "gg"); break; + case 49: strcat(name, "ff"); break; + case 50: strcat(name, "pt"); break; + case 51: strcat(name, "tz"); break; + case 52: strcat(name, "dgh"); i++; len++; break; + case 53: strcat(name, "rgh"); i++; len++; break; + case 54: strcat(name, "rph"); i++; len++; break; + case 55: strcat(name, "rch"); i++; len++; break; + // middle only + case 56: strcat(name, "cz"); break; + case 57: strcat(name, "xk"); break; + case 58: strcat(name, "zx"); break; + case 59: strcat(name, "xz"); break; + case 60: strcat(name, "cv"); break; + case 61: strcat(name, "vv"); break; + case 62: strcat(name, "nl"); break; + case 63: strcat(name, "rh"); break; + case 64: strcat(name, "dw"); break; + case 65: strcat(name, "nw"); break; + case 66: strcat(name, "khl"); i++; len++; break; + default: + i--; + break; + } + } + else { - i--; - continue; + if (i == 0) + { + name[i] = 'a' + (numb[(k + 8 * j) % 17] % 26); + want_vowel = is_random_name_vowel( name[i] ); + } + else + { + name[i] = retlet( numb[(k + 3 * j) % 17] ); + } } } - else + + if (name[i] == '\0') { - if (numb[i / 2] <= 1 && i > 3 && is_random_name_vowel(name[i])) - goto two_letter; - else - name[i] = numb[j]; + i--; + continue; } - hello: - - if ((nexty == 0 && is_random_name_vowel(name[i])) - || (nexty == 1 && !is_random_name_vowel(name[i]))) + if (want_vowel && !is_random_name_vowel( name[i] ) + || (!want_vowel && is_random_name_vowel( name[i] ))) { - if (nexty == 1 && i > 0 && !is_random_name_vowel(name[i - 1])) - i--; - i--; continue; } - if (!is_random_name_vowel(name[i])) - nexty = 1; + if (is_random_name_space( name[i] )) + has_space = true; + + if (!is_random_name_vowel( name[i] )) + want_vowel = true; else - nexty = 0; + want_vowel = false; + } - x++; + // catch break and try to give a final letter + if (i > 0 + && !is_random_name_space( name[i - 1] ) + && name[i - 1] != 'y' + && is_random_name_vowel( name[i - 1] ) + && (count > 9 || (i < 8 && numb[16] % 3))) + { + name[i] = retlet( numb[j] ); } + + len = strlen( name ); - switch (ncase) + if (len) { - case 2: - for (i = 0; i < len + 1; i++) + for (i = len - 1; i > 0; i--) { - if (i > 3 && name[i] == 0 && name[i + 1] == 0) - { - name[i] = 0; - if (name[i - 1] == 32) - name[i - 1] = 0; + if (!isspace( name[i] )) break; - } - if (name[i] != 32 && name[i] < 30) - name[i] += 65; - if (name[i] > 96) - name[i] -= 32; - } - break; - - case 3: - for (i = 0; i < len + 0; i++) - { - if (i != 0 && name[i] >= 65 && name[i] < 97) + else { - if (name[i - 1] == 32) - name[i] += 32; - } - - if (name[i] > 97) - { - if (i == 0 || name[i - 1] == 32) - name[i] -= 32; - } - - if (name[i] < 30) - { - if (i == 0 || (name[i] != 32 && name[i - 1] == 32)) - name[i] += 65; - else - name[i] += 97; + name[i] = '\0'; + len--; } } - break; - - case 0: - for (i = 0; i < len; i++) - { - if (name[i] != 32 && name[i] < 30) - name[i] += 97; - } - break; - - case 1: - name[i] += 65; - - for (i = 1; i < len; i++) - { - if (name[i] != 32 && name[i] < 30) - name[i] += 97; //97; - } - break; } - if (strlen( name ) == 0) - strncpy( buff, "Plog", 80 ); + if (len >= 3) + strncpy( buff, name, ITEMNAME_SIZE ); else - strncpy( buff, name, 80 ); - - buff[79] = '\0'; - return; - - two_letter: - if (nexty == 1) - goto hello; - - if (!is_random_name_vowel(name[i - 1])) - goto hello; + { + strncpy( buff, "plog", ITEMNAME_SIZE ); + len = 4; + } - i++; + buff[ ITEMNAME_SIZE - 1 ] = '\0'; - switch (i * (retbit(j) + 1)) + for (i = 0; i < len; i++) { - case 0: - strcat(name, "sh"); - break; - case 1: - strcat(name, "ch"); - break; - case 2: - strcat(name, "tz"); - break; - case 3: - strcat(name, "ts"); - break; - case 4: - strcat(name, "cs"); - break; - case 5: - strcat(name, "cz"); - break; - case 6: - strcat(name, "xt"); - break; - case 7: - strcat(name, "xk"); - break; - case 8: - strcat(name, "kl"); - break; - case 9: - strcat(name, "cl"); - break; - case 10: - strcat(name, "fr"); - break; - case 11: - strcat(name, "sh"); - break; - case 12: - strcat(name, "ch"); - break; - case 13: - strcat(name, "gh"); - break; - case 14: - strcat(name, "pr"); - break; - case 15: - strcat(name, "tr"); - break; - case 16: - strcat(name, "mn"); - break; - case 17: - strcat(name, "ph"); - break; - case 18: - strcat(name, "pn"); - break; - case 19: - strcat(name, "cv"); - break; - case 20: - strcat(name, "zx"); - break; - case 21: - strcat(name, "xz"); - break; - case 23: - strcat(name, "dd"); - break; - case 24: - strcat(name, "tt"); - break; - case 25: - strcat(name, "ll"); - break; - //case 26: strcat(name, "sh"); break; - //case 12: strcat(name, "sh"); break; - //case 13: strcat(name, "sh"); break; - default: - i--; - goto hello; + if (all_cap || i == 0 || buff[i - 1] == ' ') + buff[i] = toupper( buff[i] ); } - x += 2; - - goto hello; + return (len); } // end make_name() - -char reduce(unsigned char reducee) +bool is_random_name_space(char let) { - while (reducee >= 26) - { - reducee -= 26; - } - - return reducee; -} // end reduce() + return (let == ' '); +} -bool is_random_name_vowel(unsigned char let) +static bool is_random_name_vowel( char let ) { - return (let == 0 || let == 4 || let == 8 || let == 14 || let == 20 - || let == 24 || let == 32); + return (let == 'a' || let == 'e' || let == 'i' || let == 'o' || let == 'u' + || let == 'y' || let == ' '); } // end is_random_name_vowel() -char retvow(char sed) +static char retvow( int sed ) { + static const char vowels[] = "aeiouaeiouaeiouy "; + return (vowels[ sed % (sizeof(vowels) - 1) ]); +} // end retvow() - while (sed > 6) - sed -= 6; +static char retlet( int sed ) +{ + static const char consonants[] = "bcdfghjklmnpqrstvwxzcdfghlmnrstlmnrst"; + return (consonants[ sed % (sizeof(consonants) - 1) ]); +} - switch (sed) - { - case 0: - return 0; - case 1: - return 4; - case 2: - return 8; - case 3: - return 14; - case 4: - return 20; - case 5: - return 24; - case 6: - return 32; - } +bool is_interesting_item( const item_def& item ) { + if ( is_random_artefact(item) || + is_unrandom_artefact(item) || + is_fixed_artefact(item) ) + return true; - return 0; -} // end retvow() + char name[ITEMNAME_SIZE]; + item_name(item, DESC_PLAIN, name, false); + std::string iname(name); + for (unsigned i = 0; i < Options.note_items.size(); ++i) + if (Options.note_items[i].matches(iname)) + return true; + return false; +} -char retbit(char sed) -{ - return (sed % 2); -} // end retbit() +bool fully_identified( const item_def& item ) { + long flagset = ISFLAG_IDENT_MASK; + switch ( item.base_type ) { + case OBJ_BOOKS: + case OBJ_MISCELLANY: + case OBJ_ORBS: + case OBJ_SCROLLS: + case OBJ_POTIONS: + flagset = ISFLAG_KNOW_TYPE; + break; + case OBJ_FOOD: + flagset = 0; + break; + case OBJ_WANDS: + flagset = (ISFLAG_KNOW_TYPE | ISFLAG_KNOW_PLUSES); + break; + case OBJ_JEWELLERY: + flagset = (ISFLAG_KNOW_CURSE | ISFLAG_KNOW_TYPE); + if ( ring_has_pluses(item) ) + flagset |= ISFLAG_KNOW_PLUSES; + break; + default: + break; + } + if ( is_random_artefact(item) || + is_fixed_artefact(item) || + is_unrandom_artefact(item) ) + flagset |= ISFLAG_KNOW_PROPERTIES; + + return item_ident( item, flagset ); +} diff --git a/crawl-ref/source/itemname.h b/crawl-ref/source/itemname.h index 0d1ccc79a2..9bdc87360e 100644 --- a/crawl-ref/source/itemname.h +++ b/crawl-ref/source/itemname.h @@ -3,6 +3,8 @@ * Summary: Misc functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -27,17 +29,11 @@ char get_ident_type(char cla, int ty); * it_use2 - item_use - items - monstuff - mstuff2 - ouch - * shopping - spells1 - spells2 - spells3 * *********************************************************************** */ -char item_name( const item_def &item, char descrip, char buff[ITEMNAME_SIZE], +const char *item_name( const item_def &item, char descrip, + char *buff = NULL, bool terse = false ); -/* *********************************************************************** - * called from: beam - describe - fight - item_use - items - monstuff - - * player - * *********************************************************************** */ -int mass_item( const item_def &item ); - - /* *********************************************************************** * called from: debug - describe - dungeon - fight - files - item_use - * monstuff - mstuff2 - players - spells0 @@ -48,7 +44,7 @@ int property( const item_def &item, int prop_type ); /* *********************************************************************** * called from: acr * *********************************************************************** */ -unsigned char check_item_knowledge(void); +void check_item_knowledge(void); /* *********************************************************************** @@ -61,59 +57,30 @@ void clear_ids(void); * called from: direct - fight - food - items - monstuff - religion - * shopping * *********************************************************************** */ -void it_name(int itn, char des, char buff[ITEMNAME_SIZE], bool terse = false); +const char *it_name(int itn, char des, char *buff = NULL, bool terse = false); /* *********************************************************************** * called from: acr - chardump - command - effects - fight - invent - * it_use2 - it_use3 - item_use - items - ouch - output - * spell - spells1 - spells2 - spells3 - spells4 - transfor * *********************************************************************** */ -void in_name(int inn, char des, char buff[ITEMNAME_SIZE], bool terse = false); +const char *in_name(int inn, char des, char *buff = NULL, bool terse = false); /* *********************************************************************** * called from: itemname.cc items.cc item_use.cc mstuff2.cc * *********************************************************************** */ -void quant_name( const item_def &item, int quant, char des, +const char *quant_name( const item_def &item, int quant, char des, char buff[ITEMNAME_SIZE], bool terse = false ); /* *********************************************************************** * bit operations called from a large number of files * *********************************************************************** */ bool item_cursed( const item_def &item ); -bool item_uncursed( const item_def &item ); - bool item_known_cursed( const item_def &item ); bool item_known_uncursed( const item_def &item ); -// bool fully_indentified( const item_def &item ); +bool fully_identified( const item_def &item ); -bool item_ident( const item_def &item, unsigned long flags ); bool item_type_known( const item_def &item ); -bool item_not_ident( const item_def &item, unsigned long flags ); - -void do_curse_item( item_def &item ); -void do_uncurse_item( item_def &item ); - -void set_ident_flags( item_def &item, unsigned long flags ); -void unset_ident_flags( item_def &item, unsigned long flags ); - -void set_equip_race( item_def &item, unsigned long flags ); -void set_equip_desc( item_def &item, unsigned long flags ); - -unsigned long get_equip_race( const item_def &item ); -unsigned long get_equip_desc( const item_def &item ); - -bool cmp_equip_race( const item_def &item, unsigned long val ); -bool cmp_equip_desc( const item_def &item, unsigned long val ); - -void set_helmet_type( item_def &item, short flags ); -void set_helmet_desc( item_def &item, short flags ); -void set_helmet_random_desc( item_def &item ); - -short get_helmet_type( const item_def &item ); -short get_helmet_desc( const item_def &item ); - -bool cmp_helmet_type( const item_def &item, short val ); -bool cmp_helmet_desc( const item_def &item, short val ); bool set_item_ego_type( item_def &item, int item_type, int ego_type ); @@ -121,20 +88,12 @@ int get_weapon_brand( const item_def &item ); int get_ammo_brand( const item_def &item ); int get_armour_ego_type( const item_def &item ); -bool item_is_rod( const item_def &item ); -bool item_is_staff( const item_def &item ); - /* *********************************************************************** * called from: acr * *********************************************************************** */ void init_properties(void); -/* *********************************************************************** - * called from: files - randart - shopping - * *********************************************************************** */ -void make_name( unsigned char var1, unsigned char var2, unsigned char var3, - char ncase, char buff[ITEMNAME_SIZE] ); - +int make_name( unsigned long seed, bool all_caps, char buff[ ITEMNAME_SIZE ] ); /* *********************************************************************** * called from: files - shopping @@ -153,5 +112,6 @@ void set_ident_type( char cla, int ty, char setting, bool force = false ); * *********************************************************************** */ bool hide2armour( unsigned char *which_subtype ); +bool is_interesting_item( const item_def& item ); #endif diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc new file mode 100644 index 0000000000..e3a70c440b --- /dev/null +++ b/crawl-ref/source/itemprop.cc @@ -0,0 +1,2085 @@ +/* + * File: itemprop.cc + * Summary: Misc functions. + * Written by: Brent Ross + * + * Modified for Crawl Reference by $Author$ on $Date$ + * + * Change History (most recent first): + * + * <1> -/--/-- BWR Created + */ + +#include "AppHdr.h" +#include "itemname.h" + +#include +#include +#include +#include + +#ifdef DOS +#include +#endif + +#include "externs.h" + +#include "items.h" +#include "itemprop.h" +#include "macro.h" +#include "mon-util.h" +#include "notes.h" +#include "player.h" +#include "randart.h" +#include "skills2.h" +#include "stuff.h" +#include "transfor.h" +#include "view.h" + + +// XXX: name strings in most of the following are currently unused! +struct armour_def +{ + armour_type id; + const char *name; + int ac; + int ev; + int mass; + + bool light; + equipment_type slot; + size_type fit_min; + size_type fit_max; +}; + +// Note: the Little-Giant range is used to make armours which are very +// flexible and adjustable and can be worn by any player character... +// providing they also pass the shape test, of course. +static int Armour_index[NUM_ARMOURS]; +static armour_def Armour_prop[NUM_ARMOURS] = +{ + { ARM_ANIMAL_SKIN, "animal skin", 2, 0, 100, + true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_ROBE, "robe", 2, 0, 60, + true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_BIG }, + { ARM_LEATHER_ARMOUR, "leather armour", 3, -1, 150, + true, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM }, + { ARM_STUDDED_LEATHER_ARMOUR,"studded leather armour",4, -1, 180, + true, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM }, + + { ARM_RING_MAIL, "ring mail", 4, -2, 250, + false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM }, + { ARM_SCALE_MAIL, "scale mail", 5, -3, 350, + false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM }, + { ARM_CHAIN_MAIL, "chain mail", 6, -4, 400, + false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM }, + { ARM_BANDED_MAIL, "banded mail", 7, -5, 500, + false, EQ_BODY_ARMOUR, SIZE_MEDIUM, SIZE_MEDIUM }, + { ARM_SPLINT_MAIL, "splint mail", 8, -5, 550, + false, EQ_BODY_ARMOUR, SIZE_MEDIUM, SIZE_MEDIUM }, + { ARM_PLATE_MAIL, "plate mail", 10, -6, 650, + false, EQ_BODY_ARMOUR, SIZE_MEDIUM, SIZE_MEDIUM }, + { ARM_CRYSTAL_PLATE_MAIL, "crystal plate mail", 14, -8, 1200, + false, EQ_BODY_ARMOUR, SIZE_MEDIUM, SIZE_MEDIUM }, + + { ARM_TROLL_HIDE, "troll hide", 2, -1, 220, + true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_TROLL_LEATHER_ARMOUR, "troll leather armour", 4, -1, 220, + true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_STEAM_DRAGON_HIDE, "steam dragon hide", 2, 0, 120, + true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_STEAM_DRAGON_ARMOUR, "steam dragon armour", 4, 0, 120, + true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_MOTTLED_DRAGON_HIDE, "mottled dragon hide", 2, -1, 150, + true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_MOTTLED_DRAGON_ARMOUR,"mottled dragon armour", 5, -1, 150, + true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + + { ARM_SWAMP_DRAGON_HIDE, "swamp dragon hide", 3, -2, 200, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_SWAMP_DRAGON_ARMOUR, "swamp dragon armour", 5, -2, 200, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_DRAGON_HIDE, "dragon hide", 3, -3, 350, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_DRAGON_ARMOUR, "dragon armour", 6, -3, 350, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_ICE_DRAGON_HIDE, "ice dragon hide", 3, -3, 350, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_ICE_DRAGON_ARMOUR, "ice dragon armour", 6, -3, 350, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_STORM_DRAGON_HIDE, "storm dragon hide", 4, -5, 600, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_STORM_DRAGON_ARMOUR, "storm dragon armour", 8, -5, 600, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_GOLD_DRAGON_HIDE, "gold dragon hide", 5, -8, 1100, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + { ARM_GOLD_DRAGON_ARMOUR, "gold dragon armour", 10, -8, 1100, + false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT }, + + { ARM_CLOAK, "cloak", 1, 0, 40, + true, EQ_CLOAK, SIZE_LITTLE, SIZE_BIG }, + { ARM_GLOVES, "gloves", 1, 0, 20, + true, EQ_GLOVES, SIZE_SMALL, SIZE_MEDIUM }, + + { ARM_HELMET, "helmet", 1, 0, 80, + false, EQ_HELMET, SIZE_SMALL, SIZE_MEDIUM }, + { ARM_CAP, "cap", 0, 0, 40, + true, EQ_HELMET, SIZE_LITTLE, SIZE_LARGE }, + + // Note that barding size is compared against torso so it currently + // needs to fit medium, but that doesn't matter as much as race + // and shapeshift status. + { ARM_BOOTS, "boots", 1, 0, 30, + true, EQ_BOOTS, SIZE_SMALL, SIZE_MEDIUM }, + { ARM_CENTAUR_BARDING, "centaur barding", 4, -2, 100, + true, EQ_BOOTS, SIZE_MEDIUM, SIZE_MEDIUM }, + { ARM_NAGA_BARDING, "naga barding", 4, -2, 100, + true, EQ_BOOTS, SIZE_MEDIUM, SIZE_MEDIUM }, + + // Note: shields use ac-value as sh-value, EV pen is used for heavy_shield + { ARM_BUCKLER, "buckler", 3, 0, 90, + true, EQ_SHIELD, SIZE_LITTLE, SIZE_MEDIUM }, + { ARM_SHIELD, "shield", 5, -1, 150, + false, EQ_SHIELD, SIZE_SMALL, SIZE_BIG }, + { ARM_LARGE_SHIELD, "large shield", 7, -2, 230, + false, EQ_SHIELD, SIZE_MEDIUM, SIZE_GIANT }, +}; + +struct weapon_def +{ + int id; + const char *name; + int dam; + int hit; + int speed; + int mass; + int str_weight; + + skill_type skill; + hands_reqd_type hands; + size_type fit_size; // actual size is one size smaller + missile_type ammo; // MI_NONE for non-launchers + bool throwable; + + int dam_type; +}; + +static int Weapon_index[NUM_WEAPONS]; +static weapon_def Weapon_prop[NUM_WEAPONS] = +{ + // Maces & Flails + { WPN_WHIP, "whip", 4, 2, 13, 30, 2, + SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_SLASHING }, + { WPN_CLUB, "club", 5, 3, 13, 50, 7, + SK_MACES_FLAILS, HANDS_ONE, SIZE_SMALL, MI_NONE, true, + DAMV_CRUSHING }, + { WPN_HAMMER, "hammer", 7, 3, 13, 90, 7, + SK_MACES_FLAILS, HANDS_ONE, SIZE_SMALL, MI_NONE, false, + DAMV_CRUSHING }, + { WPN_MACE, "mace", 8, 3, 14, 120, 8, + SK_MACES_FLAILS, HANDS_ONE, SIZE_SMALL, MI_NONE, false, + DAMV_CRUSHING }, + { WPN_FLAIL, "flail", 9, 2, 15, 130, 8, + SK_MACES_FLAILS, HANDS_ONE, SIZE_SMALL, MI_NONE, false, + DAMV_CRUSHING }, + { WPN_ANCUS, "ancus", 9, 2, 14, 120, 8, + SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_CRUSHING }, + { WPN_MORNINGSTAR, "morningstar", 10, -1, 15, 140, 8, + SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_PIERCING | DAM_BLUDGEON }, + { WPN_DEMON_WHIP, "demon whip", 10, 1, 13, 30, 2, + SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_SLASHING }, + { WPN_SPIKED_FLAIL, "spiked flail", 12, -2, 16, 190, 8, + SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_PIERCING | DAM_BLUDGEON }, + { WPN_EVENINGSTAR, "eveningstar", 12, -1, 15, 180, 8, + SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_PIERCING | DAM_BLUDGEON }, + { WPN_DIRE_FLAIL, "dire flail", 13, -3, 14, 240, 9, + SK_MACES_FLAILS, HANDS_DOUBLE, SIZE_MEDIUM, MI_NONE, false, + DAMV_PIERCING | DAM_BLUDGEON }, + { WPN_GREAT_MACE, "great mace", 16, -4, 18, 270, 9, + SK_MACES_FLAILS, HANDS_TWO, SIZE_LARGE, MI_NONE, false, + DAMV_CRUSHING }, + { WPN_GIANT_CLUB, "giant club", 18, -6, 19, 330, 10, + SK_MACES_FLAILS, HANDS_TWO, SIZE_BIG, MI_NONE, false, + DAMV_CRUSHING }, + { WPN_GIANT_SPIKED_CLUB, "giant spiked club", 20, -7, 20, 350, 10, + SK_MACES_FLAILS, HANDS_TWO, SIZE_BIG, MI_NONE, false, + DAMV_PIERCING | DAM_BLUDGEON }, + + // Short blades + { WPN_KNIFE, "knife", 3, 5, 10, 10, 1, + SK_SHORT_BLADES, HANDS_ONE, SIZE_LITTLE, MI_NONE, false, + DAMV_STABBING | DAM_SLICE }, + { WPN_DAGGER, "dagger", 4, 6, 10, 20, 1, + SK_SHORT_BLADES, HANDS_ONE, SIZE_LITTLE, MI_NONE, true, + DAMV_STABBING | DAM_SLICE }, + { WPN_QUICK_BLADE, "quick blade", 5, 6, 7, 50, 0, + SK_SHORT_BLADES, HANDS_ONE, SIZE_LITTLE, MI_NONE, false, + DAMV_STABBING | DAM_SLICE }, + { WPN_SHORT_SWORD, "short sword", 6, 4, 11, 80, 2, + SK_SHORT_BLADES, HANDS_ONE, SIZE_SMALL, MI_NONE, false, + DAMV_SLICING | DAM_PIERCE }, + { WPN_SABRE, "sabre", 7, 4, 12, 90, 2, + SK_SHORT_BLADES, HANDS_ONE, SIZE_SMALL, MI_NONE, false, + DAMV_SLICING | DAM_PIERCE }, + + // Long swords + { WPN_FALCHION, "falchion", 8, 2, 13, 170, 4, + SK_LONG_SWORDS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_SLICING }, // or perhaps DAMV_CHOPPING is more apt? + { WPN_LONG_SWORD, "long sword", 10, 1, 14, 160, 3, + SK_LONG_SWORDS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_SLICING }, + { WPN_SCIMITAR, "scimitar", 11, -1, 14, 170, 3, + SK_LONG_SWORDS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_SLICING }, + { WPN_KATANA, "katana", 13, 2, 13, 160, 3, + SK_LONG_SWORDS, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false, + DAMV_SLICING }, + { WPN_DEMON_BLADE, "demon blade", 13, -1, 15, 200, 4, + SK_LONG_SWORDS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_SLICING }, + { WPN_BLESSED_BLADE, "blessed blade", 14, 0, 14, 200, 4, + SK_LONG_SWORDS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_SLICING }, + { WPN_DOUBLE_SWORD, "double sword", 15, -2, 16, 220, 5, + SK_LONG_SWORDS, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false, + DAMV_SLICING }, + { WPN_GREAT_SWORD, "great sword", 16, -3, 17, 250, 6, + SK_LONG_SWORDS, HANDS_TWO, SIZE_LARGE, MI_NONE, false, + DAMV_SLICING }, + { WPN_TRIPLE_SWORD, "triple sword", 19, -4, 19, 260, 6, + SK_LONG_SWORDS, HANDS_TWO, SIZE_LARGE, MI_NONE, false, + DAMV_SLICING }, + + // Axes + { WPN_HAND_AXE, "hand axe", 7, 3, 13, 80, 6, + SK_AXES, HANDS_ONE, SIZE_SMALL, MI_NONE, true, + DAMV_CHOPPING }, + { WPN_WAR_AXE, "war axe", 11, 0, 16, 180, 7, + SK_AXES, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false, + DAMV_CHOPPING }, + { WPN_BROAD_AXE, "broad axe", 14, -2, 17, 230, 8, + SK_AXES, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false, + DAMV_CHOPPING }, + { WPN_BATTLEAXE, "battleaxe", 17, -4, 18, 250, 8, + SK_AXES, HANDS_TWO, SIZE_LARGE, MI_NONE, false, + DAMV_CHOPPING }, + { WPN_EXECUTIONERS_AXE, "executioner\'s axe", 20, -6, 20, 280, 9, + SK_AXES, HANDS_TWO, SIZE_LARGE, MI_NONE, false, + DAMV_CHOPPING }, + + // Polearms + { WPN_SPEAR, "spear", 6, 3, 12, 50, 3, + SK_POLEARMS, HANDS_HALF, SIZE_SMALL, MI_NONE, true, + DAMV_PIERCING }, + { WPN_TRIDENT, "trident", 9, 2, 14, 160, 4, + SK_POLEARMS, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false, + DAMV_PIERCING }, + { WPN_DEMON_TRIDENT, "demon trident", 13, 0, 14, 160, 4, + SK_POLEARMS, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false, + DAMV_PIERCING }, + { WPN_HALBERD, "halberd", 13, -3, 16, 200, 5, + SK_POLEARMS, HANDS_TWO, SIZE_LARGE, MI_NONE, false, + DAMV_CHOPPING | DAM_PIERCE }, + { WPN_SCYTHE, "scythe", 14, -4, 20, 220, 7, + SK_POLEARMS, HANDS_TWO, SIZE_LARGE, MI_NONE, false, + DAMV_SLICING }, + { WPN_GLAIVE, "glaive", 15, -3, 18, 200, 6, + SK_POLEARMS, HANDS_TWO, SIZE_LARGE, MI_NONE, false, + DAMV_CHOPPING }, + { WPN_LOCHABER_AXE, "lochaber axe", 18, -6, 20, 200, 8, + SK_POLEARMS, HANDS_TWO, SIZE_LARGE, MI_NONE, false, + DAMV_CHOPPING }, + + { WPN_QUARTERSTAFF, "quarterstaff", 9, 2, 12, 180, 7, + SK_STAVES, HANDS_DOUBLE, SIZE_LARGE, MI_NONE, false, + DAMV_CRUSHING }, + { WPN_LAJATANG, "lajatang", 14, -3, 14, 200, 3, + SK_STAVES, HANDS_DOUBLE, SIZE_LARGE, MI_NONE, false, + DAMV_SLICING }, + + // Range weapons + // Notes: + // - HANDS_HALF means a reloading time penalty if using shield + // - damage field is used for bonus strength damage (string tension) + // - slings get a bonus from dex, not str (as tension is meaningless) + // - str weight is used for speed and applying dex to skill + { WPN_BLOWGUN, "blowgun", 0, 2, 10, 20, 0, + SK_DARTS, HANDS_HALF, SIZE_LITTLE, MI_NEEDLE, false, + DAMV_NON_MELEE }, + { WPN_SLING, "sling", 0, 2, 11, 20, 1, + SK_SLINGS, HANDS_HALF, SIZE_LITTLE, MI_STONE, false, + DAMV_NON_MELEE }, + { WPN_HAND_CROSSBOW, "hand crossbow", 3, 4, 15, 70, 5, + SK_CROSSBOWS, HANDS_HALF, SIZE_SMALL, MI_DART, false, + DAMV_NON_MELEE }, + { WPN_CROSSBOW, "crossbow", 5, 4, 15, 150, 8, + SK_CROSSBOWS, HANDS_TWO, SIZE_MEDIUM, MI_BOLT, false, + DAMV_NON_MELEE }, + { WPN_BOW, "bow", 4, 1, 11, 90, 2, + SK_BOWS, HANDS_TWO, SIZE_MEDIUM, MI_ARROW, false, + DAMV_NON_MELEE }, + { WPN_LONGBOW, "longbow", 5, 0, 12, 120, 3, + SK_BOWS, HANDS_TWO, SIZE_LARGE, MI_ARROW, false, + DAMV_NON_MELEE }, +}; + +struct missile_def +{ + int id; + const char *name; + int dam; + int mass; + bool throwable; +}; + +static int Missile_index[NUM_MISSILES]; +static missile_def Missile_prop[NUM_MISSILES] = +{ + { MI_NEEDLE, "needle", 0, 1, false }, + { MI_STONE, "stone", 4, 2, true }, + { MI_DART, "dart", 5, 3, true }, + { MI_ARROW, "arrow", 6, 5, false }, + { MI_BOLT, "bolt", 8, 5, false }, + { MI_LARGE_ROCK, "large rock", 20, 1000, true }, +}; + +struct food_def +{ + int id; + const char *name; + int value; + int carn_mod; + int herb_mod; + int mass; + int turns; +}; + +// NOTE: Any food with special random messages or side effects +// currently only takes one turn to eat (except ghouls and chunks)... +// if this changes then those items will have to have special code +// (like ghoul chunks) to guarantee that the special thing is only +// done once. See the ghoul eating code over in food.cc. +static int Food_index[NUM_FOODS]; +static food_def Food_prop[NUM_FOODS] = +{ + { FOOD_MEAT_RATION, "meat ration", 5000, 500, -1500, 80, 4 }, + { FOOD_SAUSAGE, "sausage", 1500, 150, -400, 40, 1 }, + { FOOD_CHUNK, "chunk", 1000, 100, -500, 100, 3 }, + { FOOD_BEEF_JERKY, "beef jerky", 800, 100, -250, 20, 1 }, + + { FOOD_BREAD_RATION, "bread ration", 4400, -1500, 750, 80, 4 }, + { FOOD_SNOZZCUMBER, "snozzcumber", 1500, -500, 500, 50, 1 }, + { FOOD_ORANGE, "orange", 1000, -350, 400, 20, 1 }, + { FOOD_BANANA, "banana", 1000, -350, 400, 20, 1 }, + { FOOD_LEMON, "lemon", 1000, -350, 400, 20, 1 }, + { FOOD_PEAR, "pear", 700, -250, 300, 20, 1 }, + { FOOD_APPLE, "apple", 700, -250, 300, 20, 1 }, + { FOOD_APRICOT, "apricot", 700, -250, 300, 15, 1 }, + { FOOD_CHOKO, "choko", 600, -200, 250, 30, 1 }, + { FOOD_RAMBUTAN, "rambutan", 600, -200, 250, 10, 1 }, + { FOOD_LYCHEE, "lychee", 600, -200, 250, 10, 1 }, + { FOOD_STRAWBERRY, "strawberry", 200, -80, 100, 5, 1 }, + { FOOD_GRAPE, "grape", 100, -40, 50, 2, 1 }, + { FOOD_SULTANA, "sultana", 70, -30, 30, 1, 1 }, + + { FOOD_ROYAL_JELLY, "royal jelly", 4000, 0, 0, 55, 1 }, + { FOOD_HONEYCOMB, "honeycomb", 2000, 0, 0, 40, 1 }, + { FOOD_PIZZA, "pizza", 1500, 0, 0, 40, 1 }, + { FOOD_CHEESE, "cheese", 1200, 0, 0, 40, 1 }, +}; + +// Must call this functions early on so that the above tables can +// be accessed correctly. +void init_properties(void) +{ + int i; + + for (i = 0; i < NUM_ARMOURS; i++) + Armour_index[ Armour_prop[i].id ] = i; + + for (i = 0; i < NUM_WEAPONS; i++) + Weapon_index[ Weapon_prop[i].id ] = i; + + for (i = 0; i < NUM_MISSILES; i++) + Missile_index[ Missile_prop[i].id ] = i; + + for (i = 0; i < NUM_FOODS; i++) + Food_index[ Food_prop[i].id ] = i; +} + + +// Some convenient functions to hide the bit operations and create +// an interface layer between the code and the data in case this +// gets changed again. -- bwr + +// +// Item cursed status functions: +// +bool item_cursed( const item_def &item ) +{ + return (item.flags & ISFLAG_CURSED); +} + +bool item_known_cursed( const item_def &item ) +{ + return ((item.flags & ISFLAG_KNOW_CURSE) && (item.flags & ISFLAG_CURSED)); +} + +bool item_known_uncursed( const item_def &item ) +{ + return ((item.flags & ISFLAG_KNOW_CURSE) && !(item.flags & ISFLAG_CURSED)); +} + +void do_curse_item( item_def &item ) +{ + item.flags |= ISFLAG_CURSED; +} + +void do_uncurse_item( item_def &item ) +{ + item.flags &= (~ISFLAG_CURSED); +} + +// +// Item identification status: +// +bool item_ident( const item_def &item, unsigned long flags ) +{ + return ((item.flags & flags) == flags); +} + +void set_ident_flags( item_def &item, unsigned long flags ) +{ + bool known_before = fully_identified(item); + item.flags |= flags; + if ( !known_before && fully_identified(item) ) { + /* make a note of it */ + if ( is_interesting_item(item) ) { + char buf[ITEMNAME_SIZE]; + char buf2[ITEMNAME_SIZE]; + item_name( item, DESC_NOCAP_A, buf ); + strcpy(buf2, origin_desc(item).c_str()); + take_note(Note(NOTE_ID_ITEM, 0, 0, buf, buf2)); + } + } +} + +void unset_ident_flags( item_def &item, unsigned long flags ) +{ + item.flags &= (~flags); +} + +// +// Equipment race and description: +// +unsigned long get_equip_race( const item_def &item ) +{ + return (item.flags & ISFLAG_RACIAL_MASK); +} + +unsigned long get_equip_desc( const item_def &item ) +{ + return (item.flags & ISFLAG_COSMETIC_MASK); +} + +void set_equip_race( item_def &item, unsigned long flags ) +{ + ASSERT( (flags & ~ISFLAG_RACIAL_MASK) == 0 ); + + // first check for base-sub pairs that can't ever have racial types + switch (item.base_type) + { + case OBJ_WEAPONS: + if (item.sub_type == WPN_GIANT_CLUB + || item.sub_type == WPN_GIANT_SPIKED_CLUB + || item.sub_type == WPN_KATANA + || item.sub_type == WPN_LAJATANG + || item.sub_type == WPN_SLING + || item.sub_type == WPN_KNIFE + || item.sub_type == WPN_QUARTERSTAFF + || item.sub_type == WPN_DEMON_BLADE + || item.sub_type == WPN_DEMON_WHIP + || item.sub_type == WPN_DEMON_TRIDENT) + { + return; + } + break; + + case OBJ_ARMOUR: + // not hides, dragon armour, crystal plate, or barding + if (item.sub_type >= ARM_DRAGON_HIDE + && item.sub_type <= ARM_SWAMP_DRAGON_ARMOUR + && item.sub_type != ARM_CENTAUR_BARDING + && item.sub_type != ARM_NAGA_BARDING) + { + return; + } + break; + + case OBJ_MISSILES: + if (item.sub_type == MI_STONE || item.sub_type == MI_LARGE_ROCK) + return; + break; + + default: + return; + } + + // check that item is appropriate for racial type + switch (flags) + { + case ISFLAG_ELVEN: + if (item.base_type == OBJ_ARMOUR + && (item.sub_type == ARM_SPLINT_MAIL + || item.sub_type == ARM_BANDED_MAIL + || item.sub_type == ARM_PLATE_MAIL)) + { + return; + } + break; + + case ISFLAG_DWARVEN: + if (item.base_type == OBJ_ARMOUR + && (item.sub_type == ARM_ROBE + || item.sub_type == ARM_LEATHER_ARMOUR + || item.sub_type == ARM_STUDDED_LEATHER_ARMOUR)) + { + return; + } + break; + + case ISFLAG_ORCISH: + default: + break; + } + + item.flags &= ~ISFLAG_RACIAL_MASK; // delete previous + item.flags |= flags; +} + +void set_equip_desc( item_def &item, unsigned long flags ) +{ + ASSERT( (flags & ~ISFLAG_COSMETIC_MASK) == 0 ); + + item.flags &= ~ISFLAG_COSMETIC_MASK; // delete previous + item.flags |= flags; +} + +// +// These functions handle the description and subtypes for helmets/caps +// +short get_helmet_type( const item_def &item ) +{ + ASSERT( item.base_type == OBJ_ARMOUR + && get_armour_slot( item ) == EQ_HELMET ); + + return (item.plus2 & THELM_TYPE_MASK); +} + +short get_helmet_desc( const item_def &item ) +{ + ASSERT( item.base_type == OBJ_ARMOUR + && get_armour_slot( item ) == EQ_HELMET ); + + return (item.plus2 & THELM_DESC_MASK); +} + +void set_helmet_type( item_def &item, short type ) +{ + ASSERT( (type & ~THELM_TYPE_MASK) == 0 ); + ASSERT( item.base_type == OBJ_ARMOUR + && get_armour_slot( item ) == EQ_HELMET ); + + // make sure we have the right sub_type (to get properties correctly) + if (type == THELM_HELMET || type == THELM_HELM) + item.sub_type = ARM_HELMET; + + // [dshaligram] FIXME: This is for when we import caps + // else + // item.sub_type = ARM_CAP; + + item.plus2 &= ~THELM_TYPE_MASK; + item.plus2 |= type; +} + +void set_helmet_desc( item_def &item, short type ) +{ + ASSERT( (type & ~THELM_DESC_MASK) == 0 ); + ASSERT( item.base_type == OBJ_ARMOUR + && get_armour_slot( item ) == EQ_HELMET ); + + const int helmtype = get_helmet_type(item); + if ((helmtype == THELM_CAP || helmtype == THELM_WIZARD_HAT) + && type > THELM_DESC_PLUMED) + type = THELM_DESC_PLAIN; + + item.plus2 &= ~THELM_DESC_MASK; + item.plus2 |= type; +} + +bool is_hard_helmet(const item_def &item) +{ + return (item.base_type == OBJ_ARMOUR + && item.sub_type == ARM_HELMET + && (get_helmet_type(item) == THELM_HELM + || get_helmet_type(item) == THELM_HELMET)); +} + +void set_helmet_random_desc( item_def &item ) +{ + ASSERT( item.base_type == OBJ_ARMOUR + && get_armour_slot( item ) == EQ_HELMET ); + + item.plus2 &= ~THELM_DESC_MASK; + + if (is_hard_helmet(item)) + item.plus2 |= (random2(8) << 8); + else + item.plus2 |= (random2(5) << 8); + +} + +bool is_helmet_type( const item_def &item, short val ) +{ + if (item.base_type != OBJ_ARMOUR || get_armour_slot( item ) != EQ_HELMET) + return (false); + + return (get_helmet_type( item ) == val); +} + +// +// Ego item functions: +// +bool set_item_ego_type( item_def &item, int item_type, int ego_type ) +{ + if (item.base_type == item_type + && !is_random_artefact( item ) + && !is_fixed_artefact( item )) + { + item.special = ego_type; + return (true); + } + + return (false); +} + +int get_weapon_brand( const item_def &item ) +{ + // Weapon ego types are "brands", so we do the randart lookup here. + + // Staves "brands" handled specially + if (item.base_type != OBJ_WEAPONS) + return (SPWPN_NORMAL); + + if (is_fixed_artefact( item )) + { + switch (item.special) + { + case SPWPN_SWORD_OF_CEREBOV: + return (SPWPN_FLAMING); + + case SPWPN_STAFF_OF_OLGREB: + return (SPWPN_VENOM); + + case SPWPN_VAMPIRES_TOOTH: + return (SPWPN_VAMPIRICISM); + + default: + return (SPWPN_NORMAL); + } + } + else if (is_random_artefact( item )) + { + return (randart_wpn_property( item, RAP_BRAND )); + } + + return (item.special); +} + +int get_ammo_brand( const item_def &item ) +{ + // no artefact arrows yet -- bwr + if (item.base_type != OBJ_MISSILES || is_random_artefact( item )) + return (SPMSL_NORMAL); + + return (item.special); +} + +int get_armour_ego_type( const item_def &item ) +{ + // artefact armours have no ego type, must look up powers separately + if (item.base_type != OBJ_ARMOUR + || (is_random_artefact( item ) && !is_unrandom_artefact( item ))) + { + return (SPARM_NORMAL); + } + + return (item.special); +} + +// +// Armour information and checking functions +// +bool hide2armour( item_def &item ) +{ + if (item.base_type != OBJ_ARMOUR) + return (false); + + switch (item.sub_type) + { + default: + return (false); + + case ARM_DRAGON_HIDE: + item.sub_type = ARM_DRAGON_ARMOUR; + break; + + case ARM_TROLL_HIDE: + item.sub_type = ARM_TROLL_LEATHER_ARMOUR; + break; + + case ARM_ICE_DRAGON_HIDE: + item.sub_type = ARM_ICE_DRAGON_ARMOUR; + break; + + case ARM_MOTTLED_DRAGON_HIDE: + item.sub_type = ARM_MOTTLED_DRAGON_ARMOUR; + break; + + case ARM_STORM_DRAGON_HIDE: + item.sub_type = ARM_STORM_DRAGON_ARMOUR; + break; + + case ARM_GOLD_DRAGON_HIDE: + item.sub_type = ARM_GOLD_DRAGON_ARMOUR; + break; + + case ARM_SWAMP_DRAGON_HIDE: + item.sub_type = ARM_SWAMP_DRAGON_ARMOUR; + break; + + case ARM_STEAM_DRAGON_HIDE: + item.sub_type = ARM_STEAM_DRAGON_ARMOUR; + break; + } + + return (true); +} // end hide2armour() + +// Return the enchantment limit of a piece of armour +int armour_max_enchant( const item_def &item ) +{ + ASSERT( item.base_type == OBJ_ARMOUR ); + + const int eq_slot = get_armour_slot( item ); + + int max_plus = MAX_SEC_ENCHANT; + if (eq_slot == EQ_BODY_ARMOUR || eq_slot == EQ_SHIELD) + max_plus = MAX_ARM_ENCHANT; + + return (max_plus); +} + +// doesn't include animal skin (only skins we can make and enchant) +bool armour_is_hide( const item_def &item, bool inc_made ) +{ + ASSERT( item.base_type == OBJ_ARMOUR ); + + switch (item.sub_type) + { + case ARM_TROLL_LEATHER_ARMOUR: + case ARM_DRAGON_ARMOUR: + case ARM_ICE_DRAGON_ARMOUR: + case ARM_STEAM_DRAGON_ARMOUR: + case ARM_MOTTLED_DRAGON_ARMOUR: + case ARM_STORM_DRAGON_ARMOUR: + case ARM_GOLD_DRAGON_ARMOUR: + case ARM_SWAMP_DRAGON_ARMOUR: + return (inc_made); + + case ARM_TROLL_HIDE: + case ARM_DRAGON_HIDE: + case ARM_ICE_DRAGON_HIDE: + case ARM_STEAM_DRAGON_HIDE: + case ARM_MOTTLED_DRAGON_HIDE: + case ARM_STORM_DRAGON_HIDE: + case ARM_GOLD_DRAGON_HIDE: + case ARM_SWAMP_DRAGON_HIDE: + return (true); + + default: + break; + } + + return (false); +} + +// used to distinguish shiny and embroidered +bool armour_not_shiny( const item_def &item ) +{ + ASSERT( item.base_type == OBJ_ARMOUR ); + + switch (item.sub_type) + { + case ARM_ROBE: + case ARM_CLOAK: + case ARM_GLOVES: + case ARM_BOOTS: + case ARM_NAGA_BARDING: + case ARM_CENTAUR_BARDING: + case ARM_CAP: + case ARM_LEATHER_ARMOUR: + case ARM_STUDDED_LEATHER_ARMOUR: + case ARM_ANIMAL_SKIN: + case ARM_TROLL_HIDE: + case ARM_TROLL_LEATHER_ARMOUR: + return (true); + + case ARM_HELMET: + { + const int helmtype = get_helmet_type(item); + return (helmtype == THELM_CAP || helmtype == THELM_WIZARD_HAT); + } + default: + break; + } + + return (false); +} + +int armour_str_required( const item_def &arm ) +{ + ASSERT (arm.base_type == OBJ_ARMOUR ); + + int ret = 0; + + const equipment_type slot = get_armour_slot( arm ); + const int mass = item_mass( arm ); + + switch (slot) + { + case EQ_BODY_ARMOUR: + ret = mass / 35; + break; + + case EQ_SHIELD: + ret = mass / 15; + break; + + default: + break; + } + + return ((ret < STR_REQ_THRESHOLD) ? 0 : ret); +} + +equipment_type get_armour_slot( const item_def &item ) +{ + ASSERT( item.base_type == OBJ_ARMOUR ); + + return (Armour_prop[ Armour_index[item.sub_type] ].slot); +} + +bool jewellery_is_amulet( const item_def &item ) +{ + ASSERT( item.base_type == OBJ_JEWELLERY ); + + return (item.sub_type >= AMU_RAGE); +} + +bool check_jewellery_size( const item_def &item, size_type size ) +{ + ASSERT( item.base_type == OBJ_JEWELLERY ); + + // Currently assuming amulets are always wearable (only needs + // to be held over head or heart... giants can strap it on with + // a bit of binder twine). However, rings need to actually fit + // around the ring finger to work, and so the big cannot use them. + return (size <= SIZE_LARGE || jewellery_is_amulet( item )); +} + +// Returns the basic light status of an armour, ignoring things like the +// elven bonus... you probably want is_light_armour() most times. +bool base_armour_is_light( const item_def &item ) +{ + ASSERT( item.base_type == OBJ_ARMOUR ); + + return (Armour_prop[ Armour_index[item.sub_type] ].light); +} + +// returns number of sizes off +int fit_armour_size( const item_def &item, size_type size ) +{ + ASSERT( item.base_type == OBJ_ARMOUR ); + + const size_type min = Armour_prop[ Armour_index[item.sub_type] ].fit_min; + const size_type max = Armour_prop[ Armour_index[item.sub_type] ].fit_max; + + if (size < min) + return (min - size); // -'ve means levels too small + else if (size > max) + return (max - size); // +'ve means levels too large + + return (0); +} + +// returns true if armour fits size (shape needs additional verification) +bool check_armour_size( const item_def &item, size_type size ) +{ + ASSERT( item.base_type == OBJ_ARMOUR ); + + return (fit_armour_size( item, size ) == 0); +} + +// Note that this function is used to check validity of equipment +// coming out of transformations... so it shouldn't contain any +// wield/unwield only checks like two-handed weapons and shield. +bool check_armour_shape( const item_def &item, bool quiet ) +{ + ASSERT( item.base_type == OBJ_ARMOUR ); + + const int slot = get_armour_slot( item ); + + if (!player_is_shapechanged()) + { + switch (slot) + { + case EQ_BOOTS: + switch (you.species) + { + case SP_NAGA: + if (item.sub_type != ARM_NAGA_BARDING) + { + if (!quiet) + mpr( "You can't wear that!" ); + + return (false); + } + break; + + case SP_CENTAUR: + if (item.sub_type != ARM_CENTAUR_BARDING) + { + if (!quiet) + mpr( "You can't wear that!" ); + + return (false); + } + break; + + case SP_KENKU: + if (!quiet) + { + if (item.sub_type == ARM_BOOTS) + mpr( "Boots don't fit your feet!" ); + else + mpr( "You can't wear barding!" ); + } + return (false); + + case SP_MERFOLK: + if (player_in_water() && item.sub_type == ARM_BOOTS) + { + if (!quiet) + mpr( "You don't currently have feet!" ); + + return (false); + } + // intentional fall-through + default: + if (item.sub_type == ARM_NAGA_BARDING + || item.sub_type == ARM_CENTAUR_BARDING) + { + if (!quiet) + mpr( "You can't wear barding!" ); + + return (false); + } + + if (you.mutation[MUT_HOOVES] >= 2) + { + if (!quiet) + mpr( "You can't wear boots with hooves!" ); + + return (false); + } + break; + } + break; + + case EQ_HELMET: + if (item.sub_type == ARM_CAP + || get_helmet_type(item) == THELM_CAP + || get_helmet_type(item) == THELM_WIZARD_HAT) + break; + + if (you.species == SP_KENKU) + { + if (!quiet) + mpr( "That helmet does not fit your head!" ); + + return (false); + } + + if (you.species == SP_MINOTAUR || you.mutation[MUT_HORNS]) + { + if (!quiet) + mpr( "You can't wear that with your horns!" ); + + return (false); + } + break; + + case EQ_GLOVES: + if (you.mutation[MUT_CLAWS] >= 3) + { + if (!quiet) + mpr( "You can't wear gloves with your huge claws!" ); + + return (false); + } + break; + + case EQ_BODY_ARMOUR: + // Cannot swim in heavy armour + if (player_is_swimming() && !is_light_armour( item )) + { + if (!quiet) + mpr("You can't swim in that!"); + + return (false); + } + + // Draconians are human-sized, but have wings that cause problems + // with most body armours (only very flexible fit allowed). + if (player_genus( GENPC_DRACONIAN ) + && !check_armour_size( item, SIZE_BIG )) + { + if (!quiet) + mpr( "That armour doesn't fit your wings." ); + + return (false); + } + break; + + default: + break; + } + } + else + { + // Note: some transformation include all of the above as well + if (item.sub_type == ARM_NAGA_BARDING + || item.sub_type == ARM_CENTAUR_BARDING) + { + if (!quiet) + mpr( "You can't wear barding in your current form!" ); + + return (false); + } + } + + // Note: This need to be checked after all the special cases + // above, and in addition to shapechanged or not. This is + // a simple check against the armour types that are forced off. + + // FIXME FIXME FIXME + /* + if (!transform_can_equip_type( slot )) + { + if (!quiet) + mpr( "You can't wear that in your current form!" ); + + return (false); + } + */ + + return (true); +} + +// +// Weapon information and checking functions: +// + +// Checks how rare a weapon is. Many of these have special routines for +// placement, especially those with a rarity of zero. Chance is out of 10. +int weapon_rarity( int w_type ) +{ + switch (w_type) + { + case WPN_CLUB: + case WPN_DAGGER: + return (10); + + case WPN_HAND_AXE: + case WPN_MACE: + case WPN_QUARTERSTAFF: + return (9); + + case WPN_BOW: + case WPN_FLAIL: + case WPN_HAMMER: + case WPN_SABRE: + case WPN_SHORT_SWORD: + case WPN_SLING: + case WPN_SPEAR: + return (8); + + case WPN_FALCHION: + case WPN_LONG_SWORD: + case WPN_MORNINGSTAR: + case WPN_WAR_AXE: + return (7); + + case WPN_BATTLEAXE: + case WPN_CROSSBOW: + case WPN_GREAT_SWORD: + case WPN_SCIMITAR: + case WPN_TRIDENT: + return (6); + + case WPN_GLAIVE: + case WPN_HALBERD: + case WPN_BLOWGUN: + return (5); + + case WPN_BROAD_AXE: + case WPN_HAND_CROSSBOW: + case WPN_SPIKED_FLAIL: + case WPN_WHIP: + return (4); + + case WPN_GREAT_MACE: + return (3); + + case WPN_ANCUS: + case WPN_DIRE_FLAIL: + case WPN_SCYTHE: + case WPN_LONGBOW: + return (2); + + case WPN_GIANT_CLUB: + case WPN_GIANT_SPIKED_CLUB: + case WPN_LOCHABER_AXE: + return (1); + + case WPN_DOUBLE_SWORD: + case WPN_EVENINGSTAR: + case WPN_EXECUTIONERS_AXE: + case WPN_KATANA: + case WPN_LAJATANG: + case WPN_KNIFE: + case WPN_QUICK_BLADE: + case WPN_TRIPLE_SWORD: + case WPN_DEMON_TRIDENT: + case WPN_DEMON_WHIP: + case WPN_DEMON_BLADE: + case WPN_BLESSED_BLADE: + // zero value weapons must be placed specially -- see make_item() {dlb} + return (0); + + default: + break; + } + + return (0); +} // end rare_weapon() + +int get_vorpal_type( const item_def &item ) +{ + int ret = DVORP_NONE; + + if (item.base_type == OBJ_WEAPONS) + ret = (Weapon_prop[ Weapon_index[item.sub_type] ].dam_type & DAMV_MASK); + + return (ret); +} // end vorpal_type() + +int get_damage_type( const item_def &item ) +{ + int ret = DAM_BASH; + + if (item.base_type == OBJ_WEAPONS) + ret = (Weapon_prop[ Weapon_index[item.sub_type] ].dam_type & DAM_MASK); + + return (ret); +} // end damage_type() + +bool does_damage_type( const item_def &item, int dam_type ) +{ + return (get_damage_type( item ) & dam_type); +} // end does_damage_type() + + +hands_reqd_type hands_reqd(int base_type, int sub_type, size_type size) +{ + item_def item; + item.base_type = base_type; + item.sub_type = sub_type; + return hands_reqd(item, size); +} + +// give hands required to wield weapon for a torso of "size" +hands_reqd_type hands_reqd( const item_def &item, size_type size ) +{ + int ret = HANDS_ONE; + int fit; + bool doub = false; + + switch (item.base_type) + { + case OBJ_STAVES: + case OBJ_WEAPONS: + // Merging staff with magical staves for consistency... doing + // as a special case because we want to be very flexible with + // these useful objects (we want spriggans and ogre magi to + // be able to use them). + if (item.base_type == OBJ_STAVES || item.sub_type == WPN_QUARTERSTAFF) + { + if (size < SIZE_SMALL) + ret = HANDS_TWO; + else if (size > SIZE_LARGE) + ret = HANDS_ONE; + else + ret = HANDS_HALF; + break; + } + + ret = Weapon_prop[ Weapon_index[item.sub_type] ].hands; + + // size is the level where we can use one hand for one end + if (ret == HANDS_DOUBLE) + { + doub = true; + ret = HANDS_HALF; + } + + // adjust handedness for size only for non-whip melee weapons + if (!is_range_weapon( item ) + && item.sub_type != WPN_WHIP + && item.sub_type != WPN_DEMON_WHIP) + { + fit = cmp_weapon_size( item, size ); + + // Adjust handedness for non-medium races: + // (XX values don't matter, see fit_weapon_wieldable_size) + // + // Spriggan Kobold Human Ogre Big Giant + // Little 0 0 0 XX XX XX + // Small +1 0 0 -2 XX XX + // Medium XX +1 0 -1 -2 XX + // Large XX XX 0 0 -1 -2 + // Big XX XX XX 0 0 -1 + // Giant XX XX XX XX 0 0 + + // Note the stretching of double weapons for larger characters + // by one level since they tend to be larger weapons. + if (size < SIZE_MEDIUM && fit > 0) + ret += fit; + else if (size > SIZE_MEDIUM && fit < 0) + ret += (fit + doub); + } + break; + + case OBJ_CORPSES: // unwieldy + ret = HANDS_TWO; + break; + + case OBJ_ARMOUR: // barding and body armours are unwieldy + if (item.sub_type == ARM_NAGA_BARDING + || item.sub_type == ARM_CENTAUR_BARDING + || get_armour_slot( item ) == EQ_BODY_ARMOUR) + { + ret = HANDS_TWO; + } + break; + + default: + break; + } + + if (ret > HANDS_TWO) + ret = HANDS_TWO; + else if (ret < HANDS_ONE) + ret = HANDS_ONE; + + return (static_cast< hands_reqd_type >( ret )); +} + +bool is_double_ended( const item_def &item ) +{ + if (item.base_type == OBJ_STAVES) + return (true); + else if (item.base_type != OBJ_WEAPONS) + return (false); + + return (Weapon_prop[ Weapon_index[item.sub_type] ].hands == HANDS_DOUBLE); +} + +int double_wpn_awkward_speed( const item_def &item ) +{ + ASSERT( is_double_ended( item ) ); + + const int base = property( item, PWPN_SPEED ); + + return ((base * 30 + 10) / 20 + 2); +} + + +bool is_demonic( const item_def &item ) +{ + if (item.base_type == OBJ_WEAPONS) + { + switch (item.sub_type) + { + case WPN_DEMON_BLADE: + case WPN_DEMON_WHIP: + case WPN_DEMON_TRIDENT: + return (true); + + default: + break; + } + } + + return (false); +} // end is_demonic() + +int weapon_str_weight( const item_def &wpn ) +{ + ASSERT (wpn.base_type == OBJ_WEAPONS || wpn.base_type == OBJ_STAVES); + + if (wpn.base_type == OBJ_STAVES) + return (Weapon_prop[ Weapon_index[WPN_QUARTERSTAFF] ].str_weight); + + return (Weapon_prop[ Weapon_index[wpn.sub_type] ].str_weight); +} + +int weapon_dex_weight( const item_def &wpn ) +{ + return (10 - weapon_str_weight( wpn )); +} + +int weapon_impact_mass( const item_def &wpn ) +{ + ASSERT (wpn.base_type == OBJ_WEAPONS || wpn.base_type == OBJ_STAVES); + + return ((weapon_str_weight( wpn ) * item_mass( wpn ) + 5) / 10); +} + +int weapon_str_required( const item_def &wpn, bool hand_half ) +{ + ASSERT (wpn.base_type == OBJ_WEAPONS || wpn.base_type == OBJ_STAVES); + + const int req = weapon_impact_mass( wpn ) / ((hand_half) ? 11 : 10); + + return ((req < STR_REQ_THRESHOLD) ? 0 : req); +} + +// returns melee skill of item +skill_type weapon_skill( const item_def &item ) +{ + if (item.base_type == OBJ_WEAPONS && !is_range_weapon( item )) + return (Weapon_prop[ Weapon_index[item.sub_type] ].skill); + else if (item.base_type == OBJ_STAVES) + return (SK_STAVES); + + // this is used to mark that only fighting applies. + return (SK_FIGHTING); +} + +// front function for the above when we don't have a physical item to check +skill_type weapon_skill( int wclass, int wtype ) +{ + item_def wpn; + + wpn.base_type = wclass; + wpn.sub_type = wtype; + + return (weapon_skill( wpn )); +} // end weapon_skill() + +// returns range skill of the item +skill_type range_skill( const item_def &item ) +{ + if (item.base_type == OBJ_WEAPONS && is_range_weapon( item )) + return (Weapon_prop[ Weapon_index[item.sub_type] ].skill); + else if (item.base_type == OBJ_MISSILES && item.sub_type == MI_DART) + return (SK_DARTS); + + return (SK_RANGED_COMBAT); +} + +// front function for the above when we don't have a physical item to check +skill_type range_skill( int wclass, int wtype ) +{ + item_def wpn; + + wpn.base_type = wclass; + wpn.sub_type = wtype; + + return (range_skill( wpn )); +} // end weapon_skill() + + +// Calculate the bonus to melee EV for using "wpn", with "skill" and "dex" +// to protect a body of size "body". +int weapon_ev_bonus( const item_def &wpn, int skill, size_type body, int dex, + bool hide_hidden ) +{ + ASSERT( wpn.base_type == OBJ_WEAPONS || wpn.base_type == OBJ_STAVES ); + + int ret = 0; + + // Note: ret currently measured in halves (see skill factor) + if (wpn.sub_type == WPN_WHIP || wpn.sub_type == WPN_DEMON_WHIP) + ret = 3 + (dex / 5); + else if (weapon_skill( wpn ) == SK_POLEARMS) + ret = 2 + (dex / 5); + + // weapons of reaching are naturally a bit longer/flexier + if (!hide_hidden || item_ident( wpn, ISFLAG_KNOW_TYPE )) + { + if (get_weapon_brand( wpn ) == SPWPN_REACHING) + ret += 1; + } + + // only consider additional modifications if we have a positive base: + if (ret > 0) + { + // Size factors: + // - large characters can't cover their flanks as well + // - note that not all weapons are available to small characters + if (body > SIZE_LARGE) + ret -= (4 * (body - SIZE_LARGE) - 2); + else if (body < SIZE_MEDIUM) + ret += 1; + + // apply skill (and dividing by 2) + ret = (ret * (skill + 10)) / 20; + + // make sure things can't get too insane + if (ret > 8) + ret = 8 + (ret - 8) / 2; + } + + // Note: this is always a bonus + return ((ret > 0) ? ret : 0); +} + +static size_type weapon_size( const item_def &item ) +{ + ASSERT (item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES); + + if (item.base_type == OBJ_STAVES) + return (Weapon_prop[ Weapon_index[WPN_QUARTERSTAFF] ].fit_size); + + return (Weapon_prop[ Weapon_index[item.sub_type] ].fit_size); +} + +// returns number of sizes off +int cmp_weapon_size( const item_def &item, size_type size ) +{ + ASSERT( item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES ); + + return (weapon_size( item ) - size); +} + +// Returns number of sizes away from being a usable weapon +int fit_weapon_wieldable_size( const item_def &item, size_type size ) +{ + ASSERT( item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES ); + + const int fit = cmp_weapon_size( item, size ); + + return ((fit < -2) ? fit + 2 : (fit > 1) ? fit - 1 : 0); +} + +// Returns number of sizes away from being throwable... the window +// is currently [size - 5, size - 1]. +int fit_item_throwable_size( const item_def &item, size_type size ) +{ + int ret = item_size( item ) - size; + + return ((ret >= 0) ? ret + 1 : (ret > -6) ? 0 : ret + 5); +} + +// Returns true if weapon is usable as a tool +// Note that we assume that tool usable >= wieldable +bool check_weapon_tool_size( const item_def &item, size_type size ) +{ + ASSERT( item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES ); + + // Staves are currently usable for everyone just to be nice. + if (item.base_type == OBJ_STAVES || item.sub_type == WPN_QUARTERSTAFF) + return (true); + + const int fit = cmp_weapon_size( item, size ); + + return (fit >= -3 && fit <= 1); +} + +// Returns true if weapon is usable as a weapon +bool check_weapon_wieldable_size( const item_def &item, size_type size ) +{ + ASSERT( item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES ); + + return (fit_weapon_wieldable_size( item, size ) == 0); +} + +// Note that this function is used to check validity of equipment +// coming out of transformations... so it shouldn't contain any +// wield/unwield only checks like two-handed weapons and shield. +// check_id is only used for descriptions, where we don't want to +// give away any information the player doesn't have yet. +bool check_weapon_shape( const item_def &item, bool quiet, bool check_id ) +{ + const int brand = get_weapon_brand( item ); + + if ((!check_id || item_ident( item, ISFLAG_KNOW_TYPE )) + && ((item.base_type == OBJ_WEAPONS + && item.sub_type == WPN_BLESSED_BLADE) + || brand == SPWPN_HOLY_WRATH + || brand == SPWPN_DISRUPTION) + && (you.is_undead || you.species == SP_DEMONSPAWN)) + { + if (!quiet) + mpr( "This weapon will not allow you to wield it." ); + + return (false); + } + + // Note: this should always be done last, see check_armour_shape() + // FIXME FIXME FIXME + /* + if (!transform_can_equip_type( EQ_WEAPON )) + { + if (!quiet) + mpr( "You can't wield anything in your current form!" ); + + return (false); + } + */ + + return (true); +} + +// Returns the you.inv[] index of our wielded weapon or -1 (no item, not wield) +int get_inv_wielded( void ) +{ + return (player_weapon_wielded() ? you.equip[EQ_WEAPON] : -1); +} + +// Returns the you.inv[] index of our hand tool or -1 (no item, not usable) +// Note that we don't count armour and such as "tools" here because +// this function is used to judge if the item will sticky curse to +// our hands. +int get_inv_hand_tool( void ) +{ + const int tool = you.equip[EQ_WEAPON]; + + // FIXME + /* + if (tool == -1 || !is_tool( you.inv[tool] )) + return (-1); + + if (you.inv[tool].base_type == OBJ_WEAPONS + || you.inv[tool].base_type == OBJ_STAVES) + { + // assuring that bad "shape" weapons aren't in hand + ASSERT( check_weapon_shape( you.inv[tool], false ) ); + + if (!check_weapon_tool_size( you.inv[tool], player_size() )) + return (-1); + } + */ + + return (tool); +} + +// Returns the you.inv[] index of the thing in our hand... this is provided +// as a service to specify that both of the above are irrelevant. +// Do not use this for low level functions dealing with wielding directly. +int get_inv_in_hand( void ) +{ + return (you.equip[EQ_WEAPON]); +} + +// +// Launcher and ammo functions: +// +missile_type fires_ammo_type( const item_def &item ) +{ + if (item.base_type != OBJ_WEAPONS) + return (MI_NONE); + + return (Weapon_prop[ Weapon_index[item.sub_type] ].ammo); +} + +missile_type fires_ammo_type( weapon_type wtype ) +{ + item_def wpn; + wpn.base_type = OBJ_WEAPONS; + wpn.sub_type = wtype; + + return (fires_ammo_type(wpn)); +} + +bool is_range_weapon( const item_def &item ) +{ + return (fires_ammo_type( item ) != MI_NONE); +} + +bool is_range_weapon_type( weapon_type wtype ) +{ + item_def wpn; + + wpn.base_type = OBJ_WEAPONS; + wpn.sub_type = wtype; + + return (is_range_weapon( wpn )); +} + +const char * ammo_name( const item_def &bow ) +{ + ASSERT( is_range_weapon( bow ) ); + + const int ammo = fires_ammo_type( bow ); + + return (Missile_prop[ Missile_index[ammo] ].name); +} + +// returns true if item can be reasonably thrown without a launcher +bool is_throwable( const item_def &wpn ) +{ + if (wpn.base_type == OBJ_WEAPONS) + return (Weapon_prop[ Weapon_index[wpn.sub_type] ].throwable); + else if (wpn.base_type == OBJ_MISSILES) + return (Missile_prop[ Missile_index[wpn.sub_type] ].throwable); + + return (false); +} + +// FIXME +#if 0 +// decide if "being" is launching or throwing "ammo" +launch_retval is_launched( int being_id, const item_def &ammo, bool msg ) +{ + ASSERT( being_id != MHITNOT ); + + launch_retval ret = LRET_FUMBLED; + + const item_def * lnch = 0; + int fit = 0; + + if (being_id == MHITYOU) + { + const int wpn = get_inv_wielded(); + lnch = (wpn == -1) ? 0 : &you.inv[wpn]; + fit = fit_item_throwable_size( ammo, player_size() ); + } + else // monster case + { + const int wpn = menv[being_id].inv[MSLOT_WEAPON]; + lnch = (wpn == NON_ITEM) ? 0 : &mitm[wpn]; + fit = fit_item_throwable_size( ammo, mons_size( menv[being_id].type ) ); + } + + if (lnch + && lnch->base_type == OBJ_WEAPONS + && is_range_weapon( *lnch ) + && ammo.base_type == OBJ_MISSILES + && ammo.sub_type == fires_ammo_type( *lnch )) + { + ret = LRET_LAUNCHED; + } + else if (is_throwable( ammo )) + { + if (fit == 0) + ret = LRET_THROWN; + else + { + ret = LRET_FUMBLED; + + if (being_id == MHITYOU && msg) + { + mpr( MSGCH_WARN, "It's difficult to throw such a%s object.", + (fit > 0) ? " large" : (fit < 0) ? " small" : "n awkward" ); + } + } + } + + return (ret); +} +#endif + +// +// Staff/rod functions: +// +bool item_is_rod( const item_def &item ) +{ + return (item.base_type == OBJ_STAVES + && item.sub_type >= STAFF_SMITING && item.sub_type < STAFF_AIR); +} + +bool item_is_staff( const item_def &item ) +{ + // Isn't De Morgan's law wonderful. -- bwr + return (item.base_type == OBJ_STAVES + && (item.sub_type < STAFF_SMITING || item.sub_type >= STAFF_AIR)); +} + +// +// Ring functions: +// +// Returns number of pluses on jewellery (always none for amulets yet). +int ring_has_pluses( const item_def &item ) +{ + ASSERT (item.base_type == OBJ_JEWELLERY); + + switch (item.sub_type) + { + case RING_SLAYING: + return (2); + + case RING_PROTECTION: + case RING_EVASION: + case RING_STRENGTH: + case RING_INTELLIGENCE: + case RING_DEXTERITY: + return (1); + + default: + break; + } + + return (0); +} + +// +// Food functions: +// +bool food_is_meat( const item_def &item ) +{ + ASSERT( is_valid_item( item ) && item.base_type == OBJ_FOOD ); + + return (Food_prop[ Food_index[item.sub_type] ].carn_mod > 0); +} + +bool food_is_veg( const item_def &item ) +{ + ASSERT( is_valid_item( item ) && item.base_type == OBJ_FOOD ); + + return (Food_prop[ Food_index[item.sub_type] ].herb_mod > 0); +} + +// returns food value for one turn of eating +int food_value( const item_def &item ) +{ + ASSERT( is_valid_item( item ) && item.base_type == OBJ_FOOD ); + + const int herb = you.mutation[MUT_HERBIVOROUS]; + + // XXX: this needs to be better merged with the mutation system + const int carn = (you.species == SP_KOBOLD || you.species == SP_GHOUL) ? 3 + : you.mutation[MUT_CARNIVOROUS]; + + const food_def &food = Food_prop[ Food_index[item.sub_type] ]; + + int ret = food.value; + + ret += (carn * food.carn_mod); + ret += (herb * food.herb_mod); + + return ((ret > 0) ? div_rand_round( ret, food.turns ) : 0); +} + +int food_turns( const item_def &item ) +{ + ASSERT( is_valid_item( item ) && item.base_type == OBJ_FOOD ); + + return (Food_prop[ Food_index[item.sub_type] ].turns); +} + +bool can_cut_meat( const item_def &item ) +{ + return (does_damage_type( item, DAM_SLICE )); +} + +// returns true if item counts as a tool for tool size comparisons and msgs +bool is_tool( const item_def &item ) +{ + // Currently using OBJ_WEAPONS instead of can_cut_meat() as almost + // any weapon might be an evocable artefact. + return (item.base_type == OBJ_WEAPONS + || item.base_type == OBJ_STAVES + || (item.base_type == OBJ_MISCELLANY + && item.sub_type != MISC_RUNE_OF_ZOT)); +} + + +// +// Generic item functions: +// +int property( const item_def &item, int prop_type ) +{ + switch (item.base_type) + { + case OBJ_ARMOUR: + if (prop_type == PARM_AC) + return (Armour_prop[ Armour_index[item.sub_type] ].ac); + else if (prop_type == PARM_EVASION) + return (Armour_prop[ Armour_index[item.sub_type] ].ev); + break; + + case OBJ_WEAPONS: + if (prop_type == PWPN_DAMAGE) + return (Weapon_prop[ Weapon_index[item.sub_type] ].dam); + else if (prop_type == PWPN_HIT) + return (Weapon_prop[ Weapon_index[item.sub_type] ].hit); + else if (prop_type == PWPN_SPEED) + return (Weapon_prop[ Weapon_index[item.sub_type] ].speed); + break; + + case OBJ_MISSILES: + if (prop_type == PWPN_DAMAGE) + return (Missile_prop[ Missile_index[item.sub_type] ].dam); + break; + + case OBJ_STAVES: + if (prop_type == PWPN_DAMAGE) + return (Weapon_prop[ Weapon_index[WPN_QUARTERSTAFF] ].dam); + else if (prop_type == PWPN_HIT) + return (Weapon_prop[ Weapon_index[WPN_QUARTERSTAFF] ].hit); + else if (prop_type == PWPN_SPEED) + return (Weapon_prop[ Weapon_index[WPN_QUARTERSTAFF] ].speed); + break; + + default: + break; + } + + return (0); +} + +int item_mass( const item_def &item ) +{ + int unit_mass = 0; + + switch (item.base_type) + { + case OBJ_WEAPONS: + unit_mass = Weapon_prop[ Weapon_index[item.sub_type] ].mass; + break; + + case OBJ_ARMOUR: + unit_mass = Armour_prop[ Armour_index[item.sub_type] ].mass; + + if (get_equip_race( item ) == ISFLAG_ELVEN) + { + const int reduc = (unit_mass >= 25) ? unit_mass / 5 : 5; + + // truncate to the nearest 5 and reduce the item mass: + unit_mass -= ((reduc / 5) * 5); + unit_mass = MAXIMUM( unit_mass, 5 ); + } + break; + + case OBJ_MISSILES: + unit_mass = Missile_prop[ Missile_index[item.sub_type] ].mass; + break; + + case OBJ_FOOD: + unit_mass = Food_prop[ Food_index[item.sub_type] ].mass; + break; + + case OBJ_WANDS: + unit_mass = 100; + break; + + case OBJ_UNKNOWN_I: + unit_mass = 200; // labeled "books" + break; + + case OBJ_SCROLLS: + unit_mass = 20; + break; + + case OBJ_JEWELLERY: + unit_mass = 10; + break; + + case OBJ_POTIONS: + unit_mass = 40; + break; + + case OBJ_UNKNOWN_II: + unit_mass = 5; // labeled "gems" + break; + + case OBJ_BOOKS: + unit_mass = 70; + break; + + case OBJ_STAVES: + unit_mass = 130; + break; + + case OBJ_ORBS: + unit_mass = 300; + break; + + case OBJ_MISCELLANY: + switch (item.sub_type) + { + case MISC_BOTTLED_EFREET: + case MISC_CRYSTAL_BALL_OF_SEEING: + case MISC_CRYSTAL_BALL_OF_ENERGY: + case MISC_CRYSTAL_BALL_OF_FIXATION: + unit_mass = 150; + break; + + default: + unit_mass = 100; + break; + } + break; + + case OBJ_CORPSES: + unit_mass = mons_weight( item.plus ); + + if (item.sub_type == CORPSE_SKELETON) + unit_mass /= 10; + break; + + default: + case OBJ_GOLD: + unit_mass = 0; + break; + } + + return ((unit_mass > 0) ? unit_mass : 0); +} + +// Note that this function, an item sizes in general aren't quite on the +// same scale as PCs and monsters. +size_type item_size( const item_def &item ) +{ + int size = SIZE_TINY; + + switch (item.base_type) + { + case OBJ_WEAPONS: + case OBJ_STAVES: + size = Weapon_prop[ Weapon_index[item.sub_type] ].fit_size - 1; + break; + + case OBJ_ARMOUR: + size = SIZE_MEDIUM; + switch (item.sub_type) + { + case ARM_GLOVES: + case ARM_HELMET: + case ARM_CAP: + case ARM_BOOTS: + case ARM_BUCKLER: + // tiny armour + break; + + case ARM_SHIELD: + size = SIZE_LITTLE; + break; + + case ARM_LARGE_SHIELD: + size = SIZE_SMALL; + break; + + default: // body armours and bardings + size = SIZE_MEDIUM; + break; + } + break; + + case OBJ_MISSILES: + if (item.sub_type == MI_LARGE_ROCK) + size = SIZE_SMALL; + break; + + case OBJ_MISCELLANY: + if (item.sub_type == MISC_PORTABLE_ALTAR_OF_NEMELEX) + { + size = SIZE_SMALL; + } + break; + + case OBJ_CORPSES: + // FIXME + // size = mons_size( item.plus, PSIZE_BODY ); + size = SIZE_SMALL; + break; + + default: // sundry tiny items + break; + } + + if (size < SIZE_TINY) + size = SIZE_TINY; + else if (size > SIZE_HUGE) + size = SIZE_HUGE; + + return (static_cast( size )); +} + +// returns true if we might be interested in dumping the colour +bool is_colourful_item( const item_def &item ) +{ + bool ret = false; + + switch (item.base_type) + { + case OBJ_ARMOUR: + if (item.sub_type == ARM_ROBE + || item.sub_type == ARM_CLOAK + || item.sub_type == ARM_CAP + || item.sub_type == ARM_NAGA_BARDING + || item.sub_type == ARM_CENTAUR_BARDING) + { + ret = true; + } + break; + + default: + break; + } + + return (ret); +} + +bool is_shield(const item_def &item) +{ + return item.base_type == OBJ_ARMOUR + && (item.sub_type == ARM_SHIELD + || item.sub_type == ARM_BUCKLER + || item.sub_type == ARM_LARGE_SHIELD); +} + +// Returns true if the given item cannot be wielded with the given shield. +// The currently equipped shield is used if no shield is passed in. +bool is_shield_incompatible(const item_def &weapon, const item_def *shield) +{ + // If there's no shield, there's no problem. + if (!shield && !(shield = player_shield())) + return (false); + + hands_reqd_type hand = hands_reqd(weapon, player_size()); + return hand == HANDS_TWO + && !item_is_rod(weapon) + && !is_range_weapon(weapon); +} diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h new file mode 100644 index 0000000000..9e5428106e --- /dev/null +++ b/crawl-ref/source/itemprop.h @@ -0,0 +1,148 @@ +/* + * File: itemname.cc + * Summary: Misc functions. + * Written by: Brent Ross + * + * Change History (most recent first): + * + * <1> -/--/-- BWR Created + */ + + +#ifndef ITEMPROP_H +#define ITEMPROP_H + +#include "externs.h" + +void init_properties(void); + +// cursed: +bool item_cursed( const item_def &item ); +bool item_known_cursed( const item_def &item ); +bool item_known_uncursed( const item_def &item ); +void do_curse_item( item_def &item ); +void do_uncurse_item( item_def &item ); + +// ident: +bool item_ident( const item_def &item, unsigned long flags ); +void set_ident_flags( item_def &item, unsigned long flags ); +void unset_ident_flags( item_def &item, unsigned long flags ); + +// racial item and item descriptions: +void set_equip_race( item_def &item, unsigned long flags ); +void set_equip_desc( item_def &item, unsigned long flags ); +unsigned long get_equip_race( const item_def &item ); +unsigned long get_equip_desc( const item_def &item ); + +// helmet functions: +void set_helmet_type( item_def &item, short flags ); +void set_helmet_desc( item_def &item, short flags ); +void set_helmet_random_desc( item_def &item ); + +short get_helmet_type( const item_def &item ); +short get_helmet_desc( const item_def &item ); + +bool is_helmet_type( const item_def &item, short val ); + +// ego items: +bool set_item_ego_type( item_def &item, int item_type, int ego_type ); +int get_weapon_brand( const item_def &item ); +int get_armour_ego_type( const item_def &item ); +int get_ammo_brand( const item_def &item ); + +// armour functions: +int armour_max_enchant( const item_def &item ); +bool armour_is_hide( const item_def &item, bool inc_made = false ); +bool armour_not_shiny( const item_def &item ); +int armour_str_required( const item_def &arm ); + +equipment_type get_armour_slot( const item_def &item ); + +bool jewellery_is_amulet( const item_def &item ); +bool check_jewellery_size( const item_def &item, size_type size ); + +bool hide2armour( item_def &item ); + +bool base_armour_is_light( const item_def &item ); +int fit_armour_size( const item_def &item, size_type size ); +bool check_armour_size( const item_def &item, size_type size ); +bool check_armour_shape( const item_def &item, bool quiet ); + +// weapon functions: +int weapon_rarity( int w_type ); + +int cmp_weapon_size( const item_def &item, size_type size ); +bool check_weapon_tool_size( const item_def &item, size_type size ); +int fit_weapon_wieldable_size( const item_def &item, size_type size ); +bool check_weapon_wieldable_size( const item_def &item, size_type size ); + +int fit_item_throwable_size( const item_def &item, size_type size ); + +bool check_weapon_shape( const item_def &item, bool quiet, bool check_id = false ); + +int weapon_ev_bonus( const item_def &wpn, int skill, size_type body, int dex, + bool hide_hidden = false ); + +int get_inv_wielded( void ); +int get_inv_hand_tool( void ); +int get_inv_in_hand( void ); + +hands_reqd_type hands_reqd( const item_def &item, size_type size ); +hands_reqd_type hands_reqd(int base_type, int sub_type, size_type size); +bool is_double_ended( const item_def &item ); + +int double_wpn_awkward_speed( const item_def &item ); + +bool is_demonic( const item_def &item ); + +int get_vorpal_type( const item_def &item ); +int get_damage_type( const item_def &item ); +bool does_damage_type( const item_def &item, int dam_type ); + +int weapon_str_weight( const item_def &wpn ); +int weapon_dex_weight( const item_def &wpn ); +int weapon_impact_mass( const item_def &wpn ); +int weapon_str_required( const item_def &wpn, bool half ); + +skill_type weapon_skill( const item_def &item ); +skill_type weapon_skill( int wclass, int wtype ); + +skill_type range_skill( const item_def &item ); +skill_type range_skill( int wclass, int wtype ); + +// launcher and ammo functions: +bool is_range_weapon( const item_def &item ); +bool is_range_weapon_type( weapon_type wtype ); +missile_type fires_ammo_type( const item_def &item ); +missile_type fires_ammo_type( weapon_type wtype ); +const char * ammo_name( const item_def &bow ); +bool is_throwable( const item_def &wpn ); +launch_retval is_launched( int being_id, const item_def &ammo, bool msg = false ); + +// staff/rod functions: +bool item_is_rod( const item_def &item ); +bool item_is_staff( const item_def &item ); + +// ring functions: +int ring_has_pluses( const item_def &item ); + +// food functions: +bool food_is_meat( const item_def &item ); +bool food_is_veg( const item_def &item ); +int food_value( const item_def &item ); +int food_turns( const item_def &item ); +bool can_cut_meat( const item_def &item ); + +// generic item property functions: +bool is_tool( const item_def &item ); +int property( const item_def &item, int prop_type ); +int item_mass( const item_def &item ); +size_type item_size( const item_def &item ); + +bool is_colourful_item( const item_def &item ); + +bool is_shield(const item_def &item); +bool is_shield_incompatible(const item_def &weapon, + const item_def *shield = NULL); + +#endif diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc index 5949668139..dfbacc206b 100644 --- a/crawl-ref/source/items.cc +++ b/crawl-ref/source/items.cc @@ -3,6 +3,8 @@ * Summary: Misc (mostly) inventory related functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <9> 7/08/01 MV Added messages for chunks/corpses rotting @@ -40,6 +42,7 @@ #include "it_use2.h" #include "item_use.h" #include "itemname.h" +#include "itemprop.h" #include "misc.h" #include "monplace.h" #include "monstuff.h" @@ -57,6 +60,11 @@ static void autopickup(void); static bool is_stackable_item( const item_def &item ); +static bool invisible_to_player( const item_def& item ); +static void item_list_on_square( std::vector& items, + int obj, bool force_squelch = false ); +static void autoinscribe_item( item_def& item ); +static void autoinscribe_items( void ); // Used to be called "unlink_items", but all it really does is make // sure item coordinates are correct to the stack they're in. -- bwr @@ -100,7 +108,7 @@ void link_items(void) { if (!is_valid_item(mitm[i]) || (mitm[i].x == 0 && mitm[i].y == 0)) { - // item is not assigned, or is monster item. ignore. + // item is not assigned, or is monster item. ignore. mitm[i].link = NON_ITEM; continue; } @@ -178,9 +186,9 @@ int cull_items(void) else if (is_unrandom_artefact( mitm[item] )) { // 9. unmark unrandart - int x = find_unrandart_index(item); - if (x >= 0) - set_unrandart_exist(x, 0); + int z = find_unrandart_index(item); + if (z >= 0) + set_unrandart_exist(z, 0); } // POOF! @@ -282,18 +290,7 @@ void init_item( int item ) if (item == NON_ITEM) return; - mitm[item].base_type = OBJ_UNASSIGNED; - mitm[item].sub_type = 0; - mitm[item].plus = 0; - mitm[item].plus2 = 0; - mitm[item].special = 0; - mitm[item].quantity = 0; - mitm[item].colour = 0; - mitm[item].flags = 0; - - mitm[item].x = 0; - mitm[item].y = 0; - mitm[item].link = NON_ITEM; + mitm[item].clear(); } // Returns an unused mitm slot, or NON_ITEM if none available. @@ -465,14 +462,6 @@ void unlink_item( int dest ) #endif } // end unlink_item() -static void item_cleanup(item_def &item) -{ - item.base_type = OBJ_UNASSIGNED; - item.quantity = 0; - item.orig_place = 0; - item.orig_monnum = 0; -} - void destroy_item( int dest ) { // Don't destroy non-items, but this function may be called upon @@ -483,7 +472,8 @@ void destroy_item( int dest ) unlink_item( dest ); - item_cleanup(mitm[dest]); + // paranoia, shouldn't be needed + mitm[dest].clear(); } void destroy_item_stack( int x, int y ) @@ -517,18 +507,7 @@ void destroy_item_stack( int x, int y ) } } - -/* - * Takes keyin as an argument because it will only display a long list of items - * if ; is pressed. - */ -void item_check(char keyin) -{ - char item_show[50][50]; - char temp_quant[10]; - - int counter = 0; - int counter_max = 0; +static void describe_floor() { const int grid = grd[you.x_pos][you.y_pos]; @@ -595,6 +574,8 @@ void item_check(char keyin) break; case DNGN_ENTER_ORCISH_MINES: mpr("There is a staircase to the Orcish Mines here."); + mpr("Reaching the bottom shouldn't be too hard..."); + mpr("But getting back might take some resources."); break; case DNGN_ENTER_HIVE: mpr("There is a staircase to the Hive here."); @@ -710,21 +691,74 @@ void item_check(char keyin) } } } +} + +static bool invisible_to_player( const item_def& item ) { + return strstr(item.inscription.c_str(), "=k") != 0; +} + +static bool has_nonsquelched_items( int obj ) { + while ( obj != NON_ITEM ) { + if ( !invisible_to_player(mitm[obj]) ) + return true; + obj = mitm[obj].link; + } + return false; +} + +/* Fill items with the items on a square. + Squelched items (marked with =k) are ignored, unless + the square contains *only* squelched items, in which case they + are included. If force_squelch is true, squelched items are + never displayed. + */ +static void item_list_on_square( std::vector& items, + int obj, bool force_squelch ) { + + const bool have_nonsquelched = (force_squelch || + has_nonsquelched_items(obj)); + + /* loop through the items */ + while ( obj != NON_ITEM ) { + /* add them to the items list if they qualify */ + if ( !have_nonsquelched || !invisible_to_player(mitm[obj]) ) + items.push_back( &mitm[obj] ); + obj = mitm[obj].link; + } +} + +/* + * Takes keyin as an argument because it will only display a long list of items + * if ; is pressed. + */ +void item_check(char keyin) +{ + char item_show[50][50]; + char temp_quant[10]; + + int counter = 0; + int counter_max = 0; + + describe_floor(); if (igrd[you.x_pos][you.y_pos] == NON_ITEM && keyin == ';') { mpr("There are no items here."); return; } - + + autoinscribe_items(); autopickup(); origin_set(you.x_pos, you.y_pos); - int objl = igrd[you.x_pos][you.y_pos]; - while (objl != NON_ITEM) + for ( int objl = igrd[you.x_pos][you.y_pos]; objl != NON_ITEM; + objl = mitm[objl].link ) { + if ( invisible_to_player(mitm[objl]) ) + continue; + counter++; if (counter > 45) @@ -749,7 +783,6 @@ void item_check(char keyin) strcpy(item_show[counter], str_pass); } - objl = mitm[objl].link; } counter_max = counter; @@ -785,13 +818,25 @@ void item_check(char keyin) mpr("There are several objects here."); } +void show_items() +{ + std::vector items; + item_list_on_square( items, igrd[you.x_pos][you.y_pos], true ); + + if ( items.empty() ) + mpr("There are no items here."); + else { + select_items( items, "Things that are here:", true ); + redraw_screen(); + } + + describe_floor(); +} void pickup_menu(int item_link) { - std::vector items; - - for (int i = item_link; i != NON_ITEM; i = mitm[i].link) - items.push_back( &mitm[i] ); + std::vector items; + item_list_on_square( items, item_link, false ); std::vector selected = select_items( items, "Select items to pick up" ); @@ -945,12 +990,7 @@ static std::string origin_monster_desc(const item_def &item) static std::string origin_place_desc(const item_def &item) { - std::string place = branch_level_name(item.orig_place); - if (place.length() && place != "Pandemonium") - place[0] = tolower(place[0]); - return (place.find("level") == 0? - "on " + place - : "in " + place); + return prep_branch_level_name(item.orig_place); } bool is_rune(const item_def &item) @@ -1101,12 +1141,10 @@ bool pickup_single_item(int link, int qty) return (true); } -void pickup(void) +void pickup() { - int o = 0; int m = 0; - unsigned char keyin = 0; - int next; + int keyin = 'x'; char str_pass[ ITEMNAME_SIZE ]; if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR @@ -1165,7 +1203,7 @@ void pickup(void) } } - o = igrd[you.x_pos][you.y_pos]; + int o = igrd[you.x_pos][you.y_pos]; if (o == NON_ITEM) { @@ -1173,49 +1211,41 @@ void pickup(void) } else if (mitm[o].link == NON_ITEM) // just one item? { + // deliberately allowing the player to pick up + // a killed item here pickup_single_item(o, mitm[o].quantity); } // end of if items_here else - { + { + int next; mpr("There are several objects here."); - - while (o != NON_ITEM) + const bool hide_squelched = has_nonsquelched_items(o); + while ( o != NON_ITEM ) { + // must save this because pickup can destroy the item next = mitm[o].link; + if ( hide_squelched && invisible_to_player(mitm[o]) ) { + o = next; + continue; + } + if (keyin != 'a') { - strcpy(info, "Pick up "); - - if (mitm[o].base_type == OBJ_GOLD) - { - char st_prn[20]; - itoa(mitm[o].quantity, st_prn, 10); - strcat(info, st_prn); - strcat(info, " gold piece"); - - if (mitm[o].quantity > 1) - strcat(info, "s"); - } - else - { - it_name(o, DESC_NOCAP_A, str_pass); - strcat(info, str_pass); - } - - strcat(info, "\? (y,n,a,*,q)"); + char buf[ITEMNAME_SIZE]; + item_name( mitm[o], DESC_NOCAP_A, buf ); + snprintf( info, INFO_SIZE, "Pick up %s? (y/n/a/*?g,/q)", buf ); mpr( info, MSGCH_PROMPT ); - keyin = get_ch(); } - if (keyin == '*' || keyin == '?' || keyin == ',') + if (keyin == '*' || keyin == '?' || keyin == ',' || keyin == 'g') { pickup_menu(o); break; } - if (keyin == 'q') + if (keyin == 'q' || keyin == ESCAPE) break; if (keyin == 'y' || keyin == 'a') @@ -1234,7 +1264,7 @@ void pickup(void) break; } } - + o = next; } } @@ -1299,8 +1329,8 @@ bool items_stack( const item_def &item1, const item_def &item2 ) // if either isn't identified and they look different. -- bwr if (item1.base_type == OBJ_POTIONS && item1.special != item2.special - && (item_not_ident( item1, ISFLAG_KNOW_TYPE ) - || item_not_ident( item2, ISFLAG_KNOW_TYPE ))) + && (!item_ident( item1, ISFLAG_KNOW_TYPE ) + || !item_ident( item2, ISFLAG_KNOW_TYPE ))) { return (false); } @@ -1386,7 +1416,7 @@ int find_free_slot(const item_def &i) // the player's inventory is full. int move_item_to_player( int obj, int quant_got, bool quiet ) { - int item_mass = 0; + int imass = 0; int unit_mass = 0; int retval = quant_got; char brek = 0; @@ -1409,21 +1439,21 @@ int move_item_to_player( int obj, int quant_got, bool quiet ) mpr(info); } - you.turn_is_over = 1; + you.turn_is_over = true; return (retval); } - unit_mass = mass_item( mitm[obj] ); + unit_mass = item_mass( mitm[obj] ); if (quant_got > mitm[obj].quantity || quant_got <= 0) quant_got = mitm[obj].quantity; - item_mass = unit_mass * quant_got; + imass = unit_mass * quant_got; brek = 0; // multiply both constants * 10 - if ((int) you.burden + item_mass > carrying_capacity()) + if ((int) you.burden + imass > carrying_capacity()) { // calculate quantity we can actually pick up int part = (carrying_capacity() - (int)you.burden) / unit_mass; @@ -1457,7 +1487,7 @@ int move_item_to_player( int obj, int quant_got, bool quiet ) mpr(info); } - you.turn_is_over = 1; + you.turn_is_over = true; return (retval); } @@ -1486,6 +1516,10 @@ int move_item_to_player( int obj, int quant_got, bool quiet ) item.y = -1; item.link = freeslot; + /*** HP CHANGE: do autoinscribe ***/ + autoinscribe_item( item ); + + origin_freeze(item, you.x_pos, you.y_pos); item.quantity = quant_got; @@ -1506,7 +1540,7 @@ int move_item_to_player( int obj, int quant_got, bool quiet ) you.char_direction = DIR_ASCENDING; } - you.turn_is_over = 1; + you.turn_is_over = true; return (retval); } // end move_item_to_player() @@ -1658,6 +1692,9 @@ bool move_top_item( int src_x, int src_y, int dest_x, int dest_y ) //--------------------------------------------------------------- static void drop_gold(unsigned int amount) { + const unsigned long BIGGEST_QUANT_VALUE = + ((1L << (sizeof(mitm[0].quantity)*8 - 1)) - 1000); + if (you.gold > 0) { if (amount > you.gold) @@ -1674,10 +1711,17 @@ static void drop_gold(unsigned int amount) { if (mitm[i].base_type == OBJ_GOLD) { + if ( mitm[i].quantity + amount > BIGGEST_QUANT_VALUE ) { + amount = BIGGEST_QUANT_VALUE - mitm[i].quantity; + snprintf(info, INFO_SIZE, + "But there's only room for %d.", amount); + mpr(info); + + } inc_mitm_item_quantity( i, amount ); you.gold -= amount; you.redraw_gold = 1; - you.turn_is_over = 1; + you.turn_is_over = true; return; } @@ -1692,9 +1736,17 @@ static void drop_gold(unsigned int amount) mpr( "Too many items on this level, not dropping the gold." ); return; } + if (amount > BIGGEST_QUANT_VALUE) { + amount = BIGGEST_QUANT_VALUE; + snprintf(info, INFO_SIZE, + "But there's only room for %d.", amount); + mpr(info); + } mitm[i].base_type = OBJ_GOLD; mitm[i].quantity = amount; + // [ds] #&^#@&#! + mitm[i].colour = YELLOW; mitm[i].flags = 0; move_item_to_grid( &i, you.x_pos, you.y_pos ); @@ -1702,7 +1754,7 @@ static void drop_gold(unsigned int amount) you.gold -= amount; you.redraw_gold = 1; - you.turn_is_over = 1; + you.turn_is_over = true; } else { @@ -1728,7 +1780,15 @@ bool drop_item( int item_dropped, int quant_drop ) { || item_dropped == you.equip[EQ_RIGHT_RING] || item_dropped == you.equip[EQ_AMULET]) { - mpr("You will have to take that off first."); + if (!Options.easy_unequip) + { + mpr("You will have to take that off first."); + return (false); + } + + if (remove_ring( item_dropped, true )) + start_delay( DELAY_DROP_ITEM, 1, item_dropped, 1 ); + return (false); } @@ -1744,7 +1804,7 @@ bool drop_item( int item_dropped, int quant_drop ) { { if (item_dropped == you.equip[i] && you.equip[i] != -1) { - if (!Options.easy_armour) + if (!Options.easy_unequip) { mpr("You will have to take that off first."); } @@ -1754,7 +1814,7 @@ bool drop_item( int item_dropped, int quant_drop ) { if (takeoff_armour( item_dropped )) { start_delay( DELAY_DROP_ITEM, 1, item_dropped, 1 ); - you.turn_is_over = 0; // turn happens later + you.turn_is_over = false; // turn happens later } } @@ -1765,6 +1825,8 @@ bool drop_item( int item_dropped, int quant_drop ) { } } + // [ds] easy_unequip does not apply to weapons. + // // Unwield needs to be done before copy in order to clear things // like temporary brands. -- bwr if (item_dropped == you.equip[EQ_WEAPON]) @@ -1774,9 +1836,11 @@ bool drop_item( int item_dropped, int quant_drop ) { canned_msg( MSG_EMPTY_HANDED ); } - if (!copy_item_to_grid( you.inv[item_dropped], - you.x_pos, you.y_pos, quant_drop, true )) - { + const unsigned char my_grid = grd[you.x_pos][you.y_pos]; + + if ( !grid_destroys_items(my_grid) && + !copy_item_to_grid( you.inv[item_dropped], + you.x_pos, you.y_pos, quant_drop, true )) { mpr( "Too many items on this level, not dropping the item." ); return (false); } @@ -1785,20 +1849,51 @@ bool drop_item( int item_dropped, int quant_drop ) { quant_name( you.inv[item_dropped], quant_drop, DESC_NOCAP_A, str_pass ); snprintf( info, INFO_SIZE, "You drop %s.", str_pass ); mpr(info); + + if ( grid_destroys_items(my_grid) ) { + mprf(MSGCH_SOUND, grid_item_destruction_message(my_grid)); + } dec_inv_item_quantity( item_dropped, quant_drop ); - you.turn_is_over = 1; + you.turn_is_over = true; return (true); } +static std::string drop_menu_invstatus(const Menu *menu) +{ + char buf[100]; + const int cap = carrying_capacity(); + + std::string s_newweight; + std::vector se = menu->selected_entries(); + if (!se.empty()) + { + int newweight = you.burden; + for (int i = 0, size = se.size(); i < size; ++i) + { + const item_def *item = static_cast( se[i]->data ); + newweight -= item_mass(*item) * se[i]->selected_qty; + } -static std::string drop_menu_title( int menuflags, const std::string &oldt ) { - std::string res = oldt; - res.erase(0, res.find_first_not_of(" \n\t")); - if (menuflags & MF_MULTISELECT) + snprintf(buf, sizeof buf, ">%d.%d", newweight / 10, newweight % 10); + s_newweight = buf; + } + + snprintf(buf, sizeof buf, "(Inv: %d.%d%s/%d.%d aum)", + you.burden / 10, you.burden % 10, + s_newweight.c_str(), + cap / 10, cap % 10); + return (buf); +} + +static std::string drop_menu_title(const Menu *menu, const std::string &oldt) +{ + std::string res = drop_menu_invstatus(menu) + " " + oldt; + if (menu->is_set( MF_MULTISELECT )) res = "[Multidrop] " + res; - return " " + res; + + return (res); } int get_equip_slot(const item_def *item) @@ -1823,13 +1918,13 @@ static std::string drop_selitem_text( const std::vector *s ) char buf[130]; bool extraturns = false; - if (!s->size()) + if (s->empty()) return ""; for (int i = 0, size = s->size(); i < size; ++i) { const item_def *item = static_cast( (*s)[i]->data ); - int eq = get_equip_slot(item); + const int eq = get_equip_slot(item); if (eq > EQ_WEAPON && eq < NUM_EQUIP) { extraturns = true; @@ -1837,12 +1932,14 @@ static std::string drop_selitem_text( const std::vector *s ) } } - snprintf( buf, sizeof buf, " (%lu%s turn%s)", (unsigned long) (s->size()), - extraturns? "+" : "", - s->size() > 1? "s" : "" ); + snprintf( buf, sizeof buf, " (%lu%s turn%s)", + (unsigned long) (s->size()), + extraturns? "+" : "", + s->size() > 1? "s" : "" ); return buf; } +std::vector items_for_multidrop; //--------------------------------------------------------------- // // drop @@ -1855,122 +1952,33 @@ void drop(void) if (inv_count() < 1 && you.gold == 0) { canned_msg(MSG_NOTHING_CARRIED); - you.activity = ACT_NONE; return; } - static std::vector selected; + items_for_multidrop = prompt_invent_items( "Drop what?", + MT_DROP, + -1, + drop_menu_title, + true, true, '$', + &Options.drop_filter, + drop_selitem_text, + &items_for_multidrop ); - if (!you.activity || selected.empty()) - selected = prompt_invent_items( "Drop which item?", -1, - drop_menu_title, - true, true, '$', - &Options.drop_filter, - drop_selitem_text ); - - if (selected.empty()) + if (items_for_multidrop.empty()) { canned_msg( MSG_OK ); - you.activity = ACT_NONE; return; } - // Throw away invalid items; items usually go invalid because - // of chunks rotting away. - while (!selected.empty() - // Don't look for gold in inventory - && selected[0].slot != PROMPT_GOT_SPECIAL - && !is_valid_item(you.inv[ selected[0].slot ])) - selected.erase( selected.begin() ); - - // Did the defunct item filter above deprive us of a drop target? - if (!selected.empty()) - { - drop_item( selected[0].slot, selected[0].quantity ); - // Forget the item we just dropped - selected.erase( selected.begin() ); - } - - // If we still have items that want to be dropped, start the multiturn - // activity - you.activity = selected.empty()? ACT_NONE : ACT_MULTIDROP; -} // end drop() - -//--------------------------------------------------------------- -// -// shift_monster -// -// Moves a monster to approximately (x,y) and returns true -// if monster was moved. -// -//--------------------------------------------------------------- -static bool shift_monster( struct monsters *mon, int x, int y ) -{ - bool found_move = false; - - int i, j; - int tx, ty; - int nx = 0, ny = 0; - - int count = 0; - - if (x == 0 && y == 0) + if ( items_for_multidrop.size() == 1 ) // only one item { - // try and find a random floor space some distance away - for (i = 0; i < 50; i++) - { - tx = 5 + random2( GXM - 10 ); - ty = 5 + random2( GYM - 10 ); - - int dist = grid_distance(x, y, tx, ty); - if (grd[tx][ty] == DNGN_FLOOR && dist > 10) - break; - } - - if (i == 50) - return (false); + drop_item( items_for_multidrop[0].slot, + items_for_multidrop[0].quantity ); + items_for_multidrop.clear(); + you.turn_is_over = true; } - - for (i = -1; i <= 1; i++) - { - for (j = -1; j <= 1; j++) - { - tx = x + i; - ty = y + j; - - if (tx < 5 || tx > GXM - 5 || ty < 5 || ty > GXM - 5) - continue; - - // won't drop on anything but vanilla floor right now - if (grd[tx][ty] != DNGN_FLOOR) - continue; - - if (mgrd[tx][ty] != NON_MONSTER) - continue; - - if (tx == you.x_pos && ty == you.y_pos) - continue; - - count++; - if (one_chance_in(count)) - { - nx = tx; - ny = ty; - found_move = true; - } - } - } - - if (found_move) - { - const int mon_index = mgrd[mon->x][mon->y]; - mgrd[mon->x][mon->y] = NON_MONSTER; - mgrd[nx][ny] = mon_index; - mon->x = nx; - mon->y = ny; - } - - return (found_move); + else + start_delay( DELAY_MULTIDROP, items_for_multidrop.size() ); } //--------------------------------------------------------------- @@ -2616,9 +2624,9 @@ void handle_time( long time_delta ) int added_contamination = 0; // Account for mutagenic radiation. Invis and haste will give the - // player about .1 points per turn, mutagenic randarts will give - // about 1.5 points on average, so they can corrupt the player - // quite quickly. Wielding one for a short battle is OK, which is + // player about .1 points per turn, mutagenic randarts will give + // about 1.5 points on average, so they can corrupt the player + // quite quickly. Wielding one for a short battle is OK, which is // as things should be. -- GDL if (you.invis && random2(10) < 6) added_contamination++; @@ -2639,12 +2647,18 @@ void handle_time( long time_delta ) // only check for badness once every other turn if (coinflip()) { + // [ds] Be less harsh with glow mutation; Brent and Mark Mackey note + // that the commented out random2(X) <= MC check was a bug. I've + // uncommented it but dropped the roll sharply from 150. (Brent used + // the original roll of 150 for 4.1.2, but I think players are + // sufficiently used to beta 26's unkindness that we can use a lower + // roll.) if (you.magic_contamination >= 5 - /* && random2(150) <= you.magic_contamination */) + && random2(50) <= you.magic_contamination) { mpr("Your body shudders with the violent release of wild energies!", MSGCH_WARN); - // for particularly violent releases, make a little boom + // for particularly violent releases, make a little boom if (you.magic_contamination > 25 && one_chance_in(3)) { struct bolt boom; @@ -2657,9 +2671,10 @@ void handle_time( long time_delta ) boom.thrower = KILL_MISC; boom.aux_source = "a magical explosion"; boom.beam_source = NON_MONSTER; - boom.isBeam = false; - boom.isTracer = false; - strcpy(boom.beam_name, "magical storm"); + boom.is_beam = false; + boom.is_tracer = false; + boom.is_explosion = true; + boom.name = "magical storm"; boom.ench_power = (you.magic_contamination * 5); boom.ex_size = (you.magic_contamination / 15); @@ -2669,13 +2684,13 @@ void handle_time( long time_delta ) explosion(boom); } - // we want to warp the player, not do good stuff! + // we want to warp the player, not do good stuff! if (one_chance_in(5)) mutate(100); else give_bad_mutation(coinflip()); - // we're meaner now, what with explosions and whatnot, but + // we're meaner now, what with explosions and whatnot, but // we dial down the contamination a little faster if its actually // mutating you. -- GDL contaminate_player( -(random2(you.magic_contamination / 4) + 1) ); @@ -2686,7 +2701,7 @@ void handle_time( long time_delta ) // and an appropriate other spell skill... is 1/20 too fast? if (you.equip[EQ_WEAPON] != -1 && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_STAVES - && item_not_ident( you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_TYPE ) + && !item_ident( you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_TYPE ) && one_chance_in(20)) { int total_skill = you.skills[SK_SPELLCASTING]; @@ -2848,7 +2863,7 @@ void handle_time( long time_delta ) case SP_GHOUL: //likes it temp_rand = random2(8); mpr( ((temp_rand < 5) ? "You smell something rotten." : - (temp_rand == 5) ? "Smell of rotting flesh makes you more hungry." : + (temp_rand == 5) ? "The smell of rotting flesh makes you hungry." : (temp_rand == 6) ? "You smell decay. Yum-yum." : "Wow! There is something tasty in your inventory."), MSGCH_ROTTEN_MEAT ); @@ -2869,7 +2884,7 @@ void handle_time( long time_delta ) default: temp_rand = random2(8); mpr( ((temp_rand < 5) ? "You smell something rotten." : - (temp_rand == 5) ? "Smell of rotting flesh makes you sick." : + (temp_rand == 5) ? "The smell of rotting flesh makes you sick." : (temp_rand == 6) ? "You smell decay. Yuk..." : "Ugh! There is something really disgusting in your inventory."), MSGCH_ROTTEN_MEAT ); @@ -2880,7 +2895,7 @@ void handle_time( long time_delta ) // exercise armour *xor* stealth skill: {dlb} if (!player_light_armour()) { - if (random2(1000) <= mass_item( you.inv[you.equip[EQ_BODY_ARMOUR]] )) + if (random2(1000) <= item_mass( you.inv[you.equip[EQ_BODY_ARMOUR]] )) { return; } @@ -2897,7 +2912,7 @@ void handle_time( long time_delta ) return; if (you.equip[EQ_BODY_ARMOUR] != -1 - && random2( mass_item( you.inv[you.equip[EQ_BODY_ARMOUR]] )) >= 100) + && random2( item_mass( you.inv[you.equip[EQ_BODY_ARMOUR]] )) >= 100) { return; } @@ -2909,7 +2924,23 @@ void handle_time( long time_delta ) return; } // end handle_time() -int autopickup_on = 1; +static void autoinscribe_item( item_def& item ) +{ + char name[ITEMNAME_SIZE]; + item_name(item, DESC_INVENTORY, name, false); + + std::string iname = name; + + /* if there's an inscription already do nothing */ + if ( item.inscription.size() > 0 ) + return; + + for ( unsigned i = 0; i < Options.autoinscriptions.size(); ++i ) { + if ( Options.autoinscriptions[i].first.matches(iname) ) { + item.inscription += Options.autoinscriptions[i].second; + } + } +} static bool is_banned(const item_def &item) { static char name[ITEMNAME_SIZE]; @@ -2923,13 +2954,26 @@ static bool is_banned(const item_def &item) { return false; } +static void autoinscribe_items() +{ + int o, next; + o = igrd[you.x_pos][you.y_pos]; + + while (o != NON_ITEM) + { + next = mitm[o].link; + autoinscribe_item( mitm[o] ); + o = next; + } +} + static void autopickup(void) { //David Loewenstern 6/99 int result, o, next; bool did_pickup = false; - if (autopickup_on == 0 || Options.autopickups == 0L) + if (!Options.autopickup_on || Options.autopickups == 0L) return; if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR @@ -2941,20 +2985,26 @@ static void autopickup(void) if (player_is_levitating() && !wearing_amulet(AMU_CONTROLLED_FLIGHT)) return; + if ( Options.safe_autopickup && !i_feel_safe() ) + return; + o = igrd[you.x_pos][you.y_pos]; while (o != NON_ITEM) { next = mitm[o].link; - if ( ((mitm[o].flags & ISFLAG_THROWN) && Options.pickup_thrown) || + if ( + (strstr(mitm[o].inscription.c_str(), "=g") != 0) || ( + + ((mitm[o].flags & ISFLAG_THROWN) && Options.pickup_thrown) || ( (Options.autopickups & (1L << mitm[o].base_type) #ifdef CLUA_BINDINGS || clua.callbooleanfn(false, "ch_autopickup", "u", &mitm[o]) #endif ) && (Options.pickup_dropped || !(mitm[o].flags & ISFLAG_DROPPED)) - && !is_banned(mitm[o]))) + && !is_banned(mitm[o])))) { mitm[o].flags &= ~(ISFLAG_THROWN | ISFLAG_DROPPED); @@ -2982,7 +3032,7 @@ static void autopickup(void) if (did_pickup) { - you.turn_is_over = 1; + you.turn_is_over = true; start_delay( DELAY_AUTOPICKUP, 1 ); } } diff --git a/crawl-ref/source/items.h b/crawl-ref/source/items.h index c4bca72527..53a779c2b9 100644 --- a/crawl-ref/source/items.h +++ b/crawl-ref/source/items.h @@ -3,6 +3,8 @@ * Summary: Misc (mostly) inventory related functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 6/9/99 DML Autopickup @@ -15,12 +17,6 @@ #include "externs.h" -// used in acr.cc {dlb}: -extern int autopickup_on; - -// used in initfile.cc {dlb}: -extern long autopickups; - bool is_valid_item( const item_def &item ); bool dec_inv_item_quantity( int obj, int amount ); @@ -121,6 +117,8 @@ void handle_time( long time_delta ); * *********************************************************************** */ int inv_count(void); +void show_items(); + void cmd_destroy_item( void ); bool pickup_single_item(int link, int qty); diff --git a/crawl-ref/source/lev-pand.cc b/crawl-ref/source/lev-pand.cc index a2fadad876..b152da62d4 100644 --- a/crawl-ref/source/lev-pand.cc +++ b/crawl-ref/source/lev-pand.cc @@ -3,6 +3,8 @@ * Summary: Functions used in Pandemonium. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created diff --git a/crawl-ref/source/libdos.cc b/crawl-ref/source/libdos.cc new file mode 100644 index 0000000000..4eb30860f0 --- /dev/null +++ b/crawl-ref/source/libdos.cc @@ -0,0 +1,21 @@ +/* + * File: libdos.cc + * Summary: Functions for DOS support. + * Written by: Darshan Shaligram + * + * Added for Crawl Reference by $Author: nlanza $ on $Date: 2006-09-26T03:22:57.300929Z $ + */ + +// Every .cc must include AppHdr or bad things happen. +#include "AppHdr.h" +#include + +void init_libdos() +{ + struct termios charmode; + + tcgetattr (0, &charmode); + // Ignore Ctrl-C + charmode.c_lflag &= ~ISIG; + tcsetattr (0, TCSANOW, &charmode); +} diff --git a/crawl-ref/source/libdos.h b/crawl-ref/source/libdos.h new file mode 100644 index 0000000000..647ce12b00 --- /dev/null +++ b/crawl-ref/source/libdos.h @@ -0,0 +1,6 @@ +#ifndef __LIBDOS_H__ +#define __LIBDOS_H__ + +void init_libdos(); + +#endif diff --git a/crawl-ref/source/libemx.cc b/crawl-ref/source/libemx.cc deleted file mode 100644 index 5634adbef8..0000000000 --- a/crawl-ref/source/libemx.cc +++ /dev/null @@ -1,233 +0,0 @@ -#include -#include -#include -#include - -#include "libemx.h" - - -static int cursor_start = 0, cursor_end = 0; -static int gx = 0, gy = 0, gxx = 79, gyy = 24; -static char buf[4096]; - - - - -void init_emx() -{ - v_init(); - v_getctype(&cursor_start, &cursor_end); -} - - - - -void deinit_emx() -{ - // nothing to do -} - - - - -void _setcursortype(int curstype) -{ - if ( curstype == _NOCURSOR ) - v_hidecursor(); - else - v_ctype(cursor_start, cursor_end); -} - - - - -void clrscr() -{ - if ( (gx == 0) && (gy == 0) && (gxx == 79) && (gyy == 24) ) - { - v_clear(); - v_gotoxy(0, 0); - } - else - { - for (int i = gy; i <= gyy; ++i) - { - v_gotoxy(gx, i); - v_putn(' ', gxx - gx + 1); - } - v_gotoxy(gx, gy); - } -} - - - - -void gotoxy(int x, int y) -{ - v_gotoxy(x - 1 + gx, y - 1 + gy); -} - - - - -void textcolor(int c) -{ - v_attrib(c); -} - - - - -static void cprintf_aux(const char *s) -{ - char *ptr = buf; - - while (*s) - { - if ( *s == '\n' ) - { - *ptr = 0; - v_puts(buf); - int x, y; - - v_getxy(&x, &y); - if ( y != 24 ) - v_gotoxy(gx, y + 1); - else - v_putc('\n'); - - ptr = buf; - } - else if ( *s != '\r' ) - *ptr++ = *s; - - ++s; - } - - *ptr = 0; - v_puts(buf); - -} - -void cprintf(const char *format, ...) -{ - va_list argp; - char buffer[4096]; // one could hope it's enough - - va_start( argp, format ); - - vsprintf(buffer, format, argp); - cprintf_aux(buffer); - - va_end(argp); -} - -void puttext(int x, int y, int lx, int ly, unsigned const char *buf) -{ - puttext(x, y, lx, ly, (const char *) buf); -} - - - - -void puttext(int x, int y, int lx, int ly, const char *buf) -{ - int count = (lx - x + 1); - - for (int i = y - 1; i < ly; ++i) - { - v_putline(buf + 2 * count * i, x - 1, i, count); - } -} - - - - -void gettext(int x, int y, int lx, int ly, unsigned char *buf) -{ - gettext(x, y, lx, ly, (char *) buf); -} - - - - -void gettext(int x, int y, int lx, int ly, char *buf) -{ - int count = (lx - x + 1); - - for (int i = y - 1; i < ly; ++i) - { - v_getline(buf + 2 * count * i, x - 1, i, count); - } -} - - - - -void window(int x, int y, int lx, int ly) -{ - gx = x - 1; - gy = y - 1; - gxx = lx - 1; - gyy = ly - 1; -} - - - - -int wherex() -{ - int x, y; - - v_getxy(&x, &y); - - return x + 1 - gx; -} - - - - -int wherey() -{ - int x, y; - - v_getxy(&x, &y); - - return y + 1 - gy; -} - - - - -void putch(char c) -{ - v_putc(c); -} - - - - -int kbhit() -{ - return 0; -} - - - - -void delay(int ms) -{ - __sleep2(ms); -} - - - - -void textbackground(int c) -{ - if ( c != 0 ) - { - fprintf(stderr, "bad background=%d", c); - exit(1); - } -} diff --git a/crawl-ref/source/libemx.h b/crawl-ref/source/libemx.h deleted file mode 100644 index ac33612284..0000000000 --- a/crawl-ref/source/libemx.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef LIBEMX_H -#define LIBEMX_H -#ifndef __LIBEMX_C__ - - -#include -#include - -#define itoa _itoa - - -#define _NORMALCURSOR 1 -#define _NOCURSOR 0 - - -void init_emx(); -void deinit_emx(); -void _setcursortype(int curstype); -void clrscr(); -void gotoxy(int x, int y); -void textcolor(int c); -void cprintf (const char *format, ...); - -void puttext(int x, int y, int lx, int ly, const char *buf); -void puttext(int x, int y, int lx, int ly, unsigned const char *buf); -void gettext(int x, int y, int lx, int ly, char *buf); -void gettext(int x, int y, int lx, int ly, unsigned char *buf); -void window(int x, int y, int lx, int ly); -int wherex(); -int wherey(); -void putch(char c); -int kbhit(); -void delay(int ms); -void textbackground(int c); - - -#endif // __LIBEMX_C__ -#endif diff --git a/crawl-ref/source/libmac.cc b/crawl-ref/source/libmac.cc deleted file mode 100644 index 35e2055344..0000000000 --- a/crawl-ref/source/libmac.cc +++ /dev/null @@ -1,2116 +0,0 @@ -/* - * File: libmac.cc - * Summary: Mac specific routines used by Crawl. - * Written by: Jesse Jones (jesjones@mindspring.com) - * - * Modified for Crawl Reference by $Author$ on $Date$ - * - * Change History (most recent first): - * - * <5> 5/25/02 JDJ Rewrote to use Carbon Events and Mach-O. - * <4> 9/25/99 CDL linuxlib -> liblinux - * - * <3> 5/30/99 JDJ Quit only pops up save changes dialog if game_has_started is true. - * <2> 4/24/99 JDJ HandleMenu calls save_game instead of returning a 'S' - * character ('S' now prompts). kTermHeight is now initialized - * to NUMBER_OF_LINES. - * <2> 4/24/99 JDJ getstr only adds printable characters to the buffer. - * <1> 3/23/99 JDJ Created - */ - -#include "AppHdr.h" -#include "libmac.h" - -#if macintosh - -#include -#include -#include -#include - -#include -#include -#include - -#include "debug.h" -#include "defines.h" -#include "files.h" -#include "MacString.h" -#include "version.h" - - -//----------------------------------- -// Forward References -// -class CApplication; - - -//----------------------------------- -// Constants -// -const int kTermWidth = 80; -const int kTermHeight = MAC_NUMBER_OF_LINES; - -const int kLeftArrowKey = 0x7B; -const int kUpArrowKey = 0x7E; -const int kRightArrowKey = 0x7C; -const int kDownArrowKey = 0x7D; - -const int kNumPad0Key = 0x52; -const int kNumPad1Key = 0x53; -const int kNumPad2Key = 0x54; -const int kNumPad3Key = 0x55; -const int kNumPad4Key = 0x56; -const int kNumPad5Key = 0x57; -const int kNumPad6Key = 0x58; -const int kNumPad7Key = 0x59; -const int kNumPad8Key = 0x5B; -const int kNumPad9Key = 0x5C; - -const int kNumPadMultKey = 0x43; -const int kNumPadAddKey = 0x45; -const int kNumPadSubKey = 0x4E; -const int kNumPadDecimalKey = 0x41; -const int kNumPadDivideKey = 0x4B; - -const char kEnterChar = 0x03; -const short kEscapeKey = 0x35; -const char kCheckMarkChar = 0x12; -const char kNoMarkChar = 0x00; - -const short kSaveBtn = 1; -const short kCancelBtn = 2; -const short kDontSaveBtn = 3; - -const Rect kBigRect = {0, 0, 32000, 32000}; - -const RGBColor kBlack = {0, 0, 0}; -const RGBColor kWhite = {65535, 65535, 65535}; - -enum EAskSaveResult -{ - kSaveChanges = 1, - kCancelSave = 2, - kDontSave = 3 -}; - - -//----------------------------------- -// Variables -// -static CApplication* sApp = NULL; -static CTabHandle sColors = NULL; - -static bool sInDialog = false; - -extern bool game_has_started; - - -// ======================================================================== -// Internal Functions -// ======================================================================== - -//--------------------------------------------------------------- -// -// CreateSpec -// -//--------------------------------------------------------------- -static EventTypeSpec CreateSpec(UInt32 c, UInt32 k) -{ - EventTypeSpec spec; - - spec.eventClass = c; - spec.eventKind = k; - - return spec; -} - - -//--------------------------------------------------------------- -// -// DrawChar -// -//--------------------------------------------------------------- -inline void DrawChar(short x, short y, char ch) -{ - MoveTo(x, y); - DrawText(&ch, 0, 1); -} - - -//--------------------------------------------------------------- -// -// ThrowIf -// -//--------------------------------------------------------------- -static void ThrowIf(bool predicate, const std::string& text) -{ - if (predicate) - throw std::runtime_error(text); -} - - -//--------------------------------------------------------------- -// -// ThrowIfOSErr -// -//--------------------------------------------------------------- -static void ThrowIfOSErr(OSErr err, const std::string& text) -{ - if (err != noErr) - { - char buffer[256]; - sprintf(buffer, "%s (%d)", text.c_str(), err); - - throw std::runtime_error(buffer); - } -} - - -//--------------------------------------------------------------- -// -// FlashButton -// -//--------------------------------------------------------------- -static void FlashButton(DialogPtr dialog, short item) -{ - ASSERT(dialog != nil); - ASSERT(item >= 1 && item <= CountDITL(dialog)); - - ControlRef control; - (void) GetDialogItemAsControl(dialog, item, &control); - - HiliteControl(control, 1); -// QDAddRectToDirtyRegion(GetDialogPort(dialog), &kBigRect); - QDFlushPortBuffer(GetDialogPort(dialog), NULL); - - unsigned long ticks; - Delay(8, &ticks); - - HiliteControl(control, 0); -// QDAddRectToDirtyRegion(GetDialogPort(dialog), &kBigRect); - QDFlushPortBuffer(GetDialogPort(dialog), NULL); -} - - -//--------------------------------------------------------------- -// -// WarnUser -// -// Pops up an error dialog. -// -//--------------------------------------------------------------- -static void WarnUser(const char* message) -{ - int len = strlen(message); - if (len > 250) - len = 250; - - Str255 text; - for (int i = 0; i < len; i++) - text[i + 1] = message[i]; - text[0] = len; - - AlertStdAlertParamRec params; - params.movable = true; - params.helpButton = false; - params.filterProc = NULL; - params.defaultText = (StringPtr) -1L; // use default (ie "OK") - params.cancelText = NULL; - params.otherText = NULL; - params.defaultButton = 1; - params.cancelButton = 0; - params.position = kWindowAlertPositionParentWindowScreen; - - short item; - sInDialog = true; - OSErr err = StandardAlert(kAlertCautionAlert, text, "\p", ¶ms, &item); - ASSERT(err == noErr); // seems kind of pointless to throw - sInDialog = false; -} - - -//--------------------------------------------------------------- -// -// SaveChangesFilter -// -//--------------------------------------------------------------- -static pascal Boolean SaveChangesFilter(DialogPtr dptr, EventRecord* event, short* item) -{ - bool handled = false; - - if (event->what == keyDown) - { - char ch = (char) toupper((char) (event->message & charCodeMask)); - short key = (short) ((event->message & keyCodeMask) >> 8); - - if (ch == 'S') - { - *item = 1; - handled = true; - } - else if (ch == 'D') - { - *item = 3; - handled = true; - } - else if (ch == '\r' || ch == kEnterChar) - { - *item = 1; - handled = true; - } - else if (key == kEscapeKey) - { - *item = 2; - handled = true; - } - - if (handled) - FlashButton(dptr, *item); - } - - return handled; -} - - -//--------------------------------------------------------------- -// -// AskSaveChanges -// -//--------------------------------------------------------------- -static EAskSaveResult AskSaveChanges() -{ -#if 1 - static ModalFilterUPP filterProc = NewModalFilterUPP(SaveChangesFilter); - - AlertStdAlertParamRec params; - params.movable = true; - params.helpButton = false; - params.filterProc = filterProc; - params.defaultText = "\pSave"; - params.cancelText = "\pCancel"; - params.otherText = "\pDon't Save"; - params.defaultButton = kSaveBtn; - params.cancelButton = kCancelBtn; - params.position = kWindowAlertPositionParentWindowScreen; - - short item = kSaveBtn; - sInDialog = true; - OSErr err = StandardAlert(kAlertStopAlert, "\pDo you want to save your game before quitting?", "\p", ¶ms, &item); - ASSERT(err == noErr); // seems kind of pointless to throw - sInDialog = false; - - EAskSaveResult result; - if (item == kSaveBtn) - result = kSaveChanges; - else if (item == kDontSaveBtn) - result = kDontSave; - else - result = kCancelSave; - - return result; - -#else - NavDialogOptions options; - OSStatus err = NavGetDefaultDialogOptions(&options); - ASSERT(err == noErr); // seems kind of pointless to throw - - NavAskSaveChangesResult reply = kSaveChanges; - if (err == noErr) - { - std::string name = "foobar"; - UInt32 length = std::min(name.size(), sizeof(options.savedFileName) - 1); - BlockMoveData(name.c_str(), options.savedFileName + 1, length); - options.savedFileName[0] = length; - - err = NavAskSaveChanges(&options, - action, - &reply, - NULL, - 0UL); - ASSERT(err == noErr); // seems kind of pointless to throw - } - - return (EAskSaveResult) reply; -#endif -} - - -//--------------------------------------------------------------- -// -// ConvertColor -// -//--------------------------------------------------------------- -static RGBColor ConvertColor(int c) -{ - ASSERT(c >= 0); - ASSERT(c < 16); - - RGBColor color; - - if (c == BLACK) - color = (**sColors).ctTable[15].rgb; // QD likes black and white swapped from DOS indices - - else if (c == WHITE) - color = (**sColors).ctTable[0].rgb; - else - color = (**sColors).ctTable[c].rgb; - - return color; -} - -#if __MWERKS__ -#pragma mark - -#endif - -// ============================================================================ -// class CApplication -// ============================================================================ -class CApplication { - -//----------------------------------- -// Initialization/Destruction -// -public: - ~CApplication(); - CApplication(); - -//----------------------------------- -// API -// -public: - void Quit(); - - char GetChar(); - // Block until a key is pressed. - - bool PeekChar(); - // Return true if a key event is on the event queue. - - void Clear(); - void SetCursor(int x, int y); - Point GetCursor() const {return mCursor;} - void SetChar(unsigned char ch); - void Print(const char* buffer); - void ShowCursor(bool show) {mShowCursor = show;} - - void SetForeColor(const RGBColor& color); - void SetBackColor(const RGBColor& color); - - void SetFont(const unsigned char *name) {this->DoSetFont(name, mPointSize);} - void SetFontSize(int size) {this->DoSetFont(mFontName, size);} - -//----------------------------------- -// Internal Types -// -private: - struct SCell { - unsigned char ch; - RGBColor color; - - SCell() - { - ch = ' '; - color.red = color.green = color.blue = 65535; - } - }; - - typedef vector Line; - -//----------------------------------- -// Internal API -// -private: - void DoAbout(); - void DoClearToEOL(); - void DoDrawCell(const Rect& area, const SCell& cell); - OSStatus DoEnableCommand(MenuRef menuH, MenuCommand command, MenuItemIndex index); - OSStatus DoHandleCommand(MenuCommand command); - void DoInitMenus(); - void DoInitWindows(); - char DoMungeChar(char inChar, UInt32 inKey, UInt32 modifiers) const; - OSStatus DoOpenMenu(MenuRef menuH); - void DoReadPrefs(); - void DoRender(); - void DoScroll(); - void DoSetChar(unsigned char ch); - void DoSetFont(const unsigned char* name, int size); - void DoWritePrefs(); - - static pascal OSStatus DoKeyDown(EventHandlerCallRef handler, EventRef event, void* refCon); - static pascal OSStatus DoMenuEvent(EventHandlerCallRef handler, EventRef event, void* refCon); - static pascal OSErr DoQuit(const AppleEvent* event, AppleEvent* reply, SInt32 refCon); - static pascal OSStatus DoWindowEvent(EventHandlerCallRef handler, EventRef event, void* refCon); - -//----------------------------------- -// Member Data -// -private: - WindowRef mWindow; - vector mLines; - char mChar; - - Point mCursor; - bool mShowCursor; - - RGBColor mForeColor; - RGBColor mBackColor; - - Str255 mFontName; - int mPointSize; - UInt32 mNumFonts; - MenuRef mFontMenu; - - short mFontNum; - int mAscent; - int mCellHeight; - int mCellWidth; -}; - -//--------------------------------------------------------------- -// -// CApplication::~CApplication -// -//--------------------------------------------------------------- -CApplication::~CApplication() -{ - this->DoWritePrefs(); - - DisposeWindow(mWindow); -} - - -//--------------------------------------------------------------- -// -// CApplication::CApplication -// -//--------------------------------------------------------------- -CApplication::CApplication() -{ - InitCursor(); - - mCursor.h = 0; - mCursor.v = 0; - mShowCursor = false; - mChar = 0; - - mNumFonts = 0; - mFontName[0] = '\0'; - mFontNum = -2; - mPointSize = 0; - mAscent = 0; - mCellHeight = 0; - mCellWidth = 0; - mFontMenu = NULL; - - mForeColor = kWhite; - mBackColor = kBlack; - - try - { - // install a handler for the quit apple event - OSStatus err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(DoQuit), 0, false); - ThrowIfOSErr(err, "Couldn't install the quit handler!"); - - // install a custom key handler - std::vector specs; - specs.push_back(CreateSpec(kEventClassKeyboard, kEventRawKeyDown)); - specs.push_back(CreateSpec(kEventClassKeyboard, kEventRawKeyRepeat)); - - err = InstallApplicationEventHandler(NewEventHandlerUPP(DoKeyDown), specs.size(), &specs[0], this, NULL); - ThrowIfOSErr(err, "Couldn't install the key handler!"); - - // create the window - this->DoInitWindows(); - - // init the menus - this->DoInitMenus(); - - // update some state - this->DoReadPrefs(); - - this->Clear(); - - // show the window - ShowWindow(mWindow); - } - catch (const std::exception& e) - { - WarnUser(e.what()); - ExitToShell(); - } - catch (...) - { - WarnUser("Couldn't initialize the application."); - ExitToShell(); - } -} - - -//--------------------------------------------------------------- -// -// CApplication::Quit -// -//--------------------------------------------------------------- -void CApplication::Quit() -{ - if (game_has_started) - { - EAskSaveResult answer = AskSaveChanges(); - - if (answer == kSaveChanges) - { - save_game(true); - } - else if (answer == kDontSave) - { - deinit_mac(); - ExitToShell(); - } - } - else - { - deinit_mac(); - ExitToShell(); - } -} - - -//--------------------------------------------------------------- -// -// CApplication::Clear -// -//--------------------------------------------------------------- -void CApplication::Clear() -{ - mLines.resize(kTermHeight); - - for (int y = 0; y < mLines.size(); ++y) - { - Line& line = mLines[y]; - line.resize(kTermWidth); - - for (int x = 0; x < line.size(); ++x) - { - SCell& cell = line[x]; - - cell.ch = ' '; - cell.color = kWhite; - } - } - - mCursor.h = 0; - mCursor.v = 0; - - (void) InvalWindowRect(mWindow, &kBigRect); -} - - -//--------------------------------------------------------------- -// -// CApplication::SetCursor -// -//--------------------------------------------------------------- -void CApplication::SetCursor(int x, int y) -{ - ASSERT(x >= 0); - ASSERT(x < kTermWidth); - ASSERT(y >= 0); - ASSERT(y < kTermHeight); - - if (x != mCursor.h || y != mCursor.v) - { - if (mShowCursor) - { - Rect area; - area.top = mCursor.v * mCellHeight; - area.bottom = area.top + mCellHeight; - area.left = mCursor.h * mCellWidth; - area.right = area.left + mCellWidth; - - (void) InvalWindowRect(mWindow, &area); - } - - mCursor.h = x; - mCursor.v = y; - - if (mShowCursor) - { - Rect area; - area.top = mCursor.v * mCellHeight; - area.bottom = area.top + mCellHeight; - area.left = mCursor.h * mCellWidth; - area.right = area.left + mCellWidth; - - (void) InvalWindowRect(mWindow, &area); - } - } -} - - -//--------------------------------------------------------------- -// -// CApplication::SetChar -// -//--------------------------------------------------------------- -void CApplication::SetChar(unsigned char ch) -{ - ASSERT(ch != '\t'); - ASSERT(mLines.size() == kTermHeight); - - const int TABSIZE = 8; - - int x = mCursor.h; // this is from the ncurses source - int y = mCursor.v; - - switch (ch) { - case '\t': - x += (TABSIZE - (x % TABSIZE)); - - // Space-fill the tab on the bottom line so that we'll get the - // "correct" cursor position. - if (x < kTermWidth) - { - char blank = ' '; - - while (mCursor.h < x) - this->DoSetChar(blank); - break; - - } - else - { - this->DoClearToEOL(); - if (++y >= kTermHeight) - { - x = kTermWidth - 1; - y--; - this->DoScroll(); - x = 0; - } - else - x = 0; - } - break; - -#if 1 - case '\n': - case '\r': - this->DoClearToEOL(); - if (++y >= kTermHeight) - { - y--; - this->DoScroll(); - } - x = 0; - break; - -#else - case '\n': - this->DoClearToEOL(); - if (++y >= kTermHeight) - { - y--; - this->DoScroll(); - } - /* FALLTHRU */ - - case '\r': - x = 0; - break; -#endif - - case '\b': - if (x == 0) - return; - mCursor.h--; - this->DoSetChar(' '); - x--; - break; - - case 159: - case 176: - case 177: - case 220: - case 239: - case 240: - case 247: - case 249: - case 250: - case 206: - case 254: - this->DoSetChar(ch); - return; - break; - - default: - if (ch == '\0') - ch = ' '; - -// ASSERT(ch >= ' '); -// ASSERT(ch <= '~'); - - if (ch >= ' ' && ch <= '~') - this->DoSetChar(ch); - else - this->DoSetChar('À'); - return; - } - - mCursor.h = x; - mCursor.v = y; -} - - -//--------------------------------------------------------------- -// -// CApplication::Print -// -//--------------------------------------------------------------- -void CApplication::Print(const char* buffer) -{ - ASSERT(buffer != NULL); - - const char* p = buffer; - - while (*p != '\0') - { - char ch = *p++; - - this->SetChar(ch); - } -} - - -//--------------------------------------------------------------- -// -// CApplication::SetForeColor -// -//--------------------------------------------------------------- -void CApplication::SetForeColor(const RGBColor & color) -{ - if (color.red != mForeColor.red || color.green != mForeColor.green || color.blue != mForeColor.blue) - { - mForeColor = color; - - (void) InvalWindowRect(mWindow, &kBigRect); - } -} - - -//--------------------------------------------------------------- -// -// CApplication::SetBackColor -// -//--------------------------------------------------------------- -void CApplication::SetBackColor(const RGBColor & color) -{ - if (color.red != mBackColor.red || color.green != mBackColor.green || color.blue != mBackColor.blue) - { - mBackColor = color; - - (void) InvalWindowRect(mWindow, &kBigRect); - } -} - - -//--------------------------------------------------------------- -// -// CApplication::GetChar -// -//--------------------------------------------------------------- -char CApplication::GetChar() -{ - mChar = 0; - RunApplicationEventLoop(); - - (void) InvalWindowRect(mWindow, &kBigRect); - - return mChar; -} - - -//--------------------------------------------------------------- -// -// CApplication::PeekChar -// -//--------------------------------------------------------------- -bool CApplication::PeekChar() -{ - EventTypeSpec specs[2]; - specs[0] = CreateSpec(kEventClassKeyboard, kEventRawKeyDown); - specs[1] = CreateSpec(kEventClassKeyboard, kEventRawKeyRepeat); - - EventRef event = NULL; - OSStatus err = ReceiveNextEvent(2, specs, kEventDurationNoWait, false, &event); - - return err == noErr; -} - -#if __MWERKS__ -#pragma mark ~ -#endif - -//--------------------------------------------------------------- -// -// CApplication::DoAbout -// -//--------------------------------------------------------------- -void CApplication::DoAbout() -{ - AlertStdAlertParamRec params; - params.movable = true; - params.helpButton = false; - params.filterProc = NULL; - params.defaultText = (StringPtr) -1L; // use default (ie "OK") VERSION - params.cancelText = NULL; - params.otherText = NULL; - params.defaultButton = 1; - params.cancelButton = 0; - params.position = kWindowAlertPositionParentWindowScreen; - - short item; - sInDialog = true; - OSErr err = StandardAlert(kAlertNoteAlert, "\p Crawl " VERSION, "\p© 1997-2002 by Linley Henzell\nMac Port by Jesse Jones", ¶ms, &item); - ASSERT(err == noErr); // seems kind of pointless to throw - sInDialog = false; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoClearToEOL -// -//--------------------------------------------------------------- -void CApplication::DoClearToEOL() -{ - ASSERT(mCursor.h < kTermWidth); - ASSERT(mCursor.v < kTermHeight); - - Line& line = mLines[mCursor.v]; - for (int x = mCursor.h; x < kTermWidth; ++x) - { - SCell& cell = line[x]; - cell.ch = ' '; - } - - Rect area; - area.top = mCursor.v * mCellHeight; - area.bottom = area.top + mCellHeight; - area.left = mCursor.h * mCellWidth; - area.right = 16000; - - (void) InvalWindowRect(mWindow, &kBigRect); -} - - -//--------------------------------------------------------------- -// -// CApplication::DoDrawCell -// -//--------------------------------------------------------------- -void CApplication::DoDrawCell(const Rect& area, const SCell& cell) -{ - RGBForeColor(&cell.color); - - switch (cell.ch) { - case 159: // fountain - DrawChar(area.left, area.top + mAscent, '´'); - break; - - case 177: // wall - case 176: - PaintRect(&area); - break; - - case 247: // water/lava - PaintRect(&area); - break; - - case 249: // floor - case 250: // undiscovered trap? -// FillRect(&area, &qd.gray); - DrawChar(area.left, area.top + mAscent, '.'); - break; - - case 206: - case 254: // door - { - Rect temp = area; - InsetRect(&temp, 2, 2); - PaintRect(&temp); - } - break; - - case 220: // altar - DrawChar(area.left, area.top + mAscent, 'Æ'); - break; - - case 239: // staircase to hell - case 240: // branch staircase - DrawChar(area.left, area.top + mAscent, '²'); - break; - - default: - DrawChar(area.left, area.top + mAscent, cell.ch); - } -} - - -//--------------------------------------------------------------- -// -// CApplication::DoEnableCommand -// -//--------------------------------------------------------------- -OSStatus CApplication::DoEnableCommand(MenuRef menuH, MenuCommand command, MenuItemIndex index) -{ - OSStatus err = noErr; - - if (command == 'Abut') - { - EnableMenuItem(menuH, index); - } - else if (command == 'Save') - { - if (game_has_started) - EnableMenuItem(menuH, index); - else - DisableMenuItem(menuH, index); - } - else if (command >= 'Size' && command <= 'Size'+128) - { - if (mPointSize == command - 'Size') - SetItemMark(menuH, index, kCheckMarkChar); - else - SetItemMark(menuH, index, kNoMarkChar); - } - else if (command >= 'Font' && command <= 'Font'+128) - { - Str255 name; - GetMenuItemText(menuH, index, name); - - if (EqualString(mFontName, name, true, true)) - SetItemMark(menuH, index, kCheckMarkChar); - else - SetItemMark(menuH, index, noMark); - } - else - err = eventNotHandledErr; - - return err; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoHandleCommand -// -//--------------------------------------------------------------- -OSStatus CApplication::DoHandleCommand(MenuCommand command) -{ - OSStatus err = noErr; - - if (command == 'Abut') - { - this->DoAbout(); - } - else if (command == 'Save') - { - save_game(false); - } - else if (command >= 'Size' && command <= 'Size'+128) - { - int size = command - 'Size'; - - this->SetFontSize(size); - } - else if (command >= 'Font' && command <= 'Font'+128) - { - int index = command - 'Font'; - - Str255 name; - GetMenuItemText(mFontMenu, index, name); - - this->SetFont(name); - } - else if (command == kHICommandQuit) - { - this->Quit(); - } - else - err = eventNotHandledErr; - - return err; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoInitMenus -// -//--------------------------------------------------------------- -void CApplication::DoInitMenus() -{ - const int kFileMenu = 257; - const int kFontMenu = 258; - const int kSizeMenu = 259; - - // add the About menu item - MenuRef menuH = NewMenu(0, "\pð"); - - OSStatus err = InsertMenuItemTextWithCFString(menuH, MacString("About Crawl"), 0, kMenuItemAttrIgnoreMeta, 'Abut'); - ThrowIfOSErr(err, "Couldn't add the about menu item!"); - - InsertMenu(menuH, 0); - - // create the File menu - err = CreateNewMenu(kFileMenu, kMenuAttrAutoDisable, &menuH); - ThrowIfOSErr(err, "Couldn't create the file menu!"); - - err = SetMenuTitleWithCFString(menuH, MacString("File")); - ThrowIfOSErr(err, "Couldn't set the file menu name!"); - - InsertMenu(menuH, 0); - - // add the File menu items - err = AppendMenuItemTextWithCFString(menuH, MacString("Save"), kMenuItemAttrIgnoreMeta, 'Save', NULL); - ThrowIfOSErr(err, "Couldn't add the save menu item!"); - - err = SetMenuItemCommandKey(menuH, 1, false, 'S'); - ThrowIfOSErr(err, "Couldn't set the save menu item's command key!"); - - // create the Font menu - err = CreateNewMenu(kFontMenu, kMenuAttrAutoDisable, &mFontMenu); - ThrowIfOSErr(err, "Couldn't create the font menu!"); - - err = CreateStandardFontMenu(mFontMenu, 0, 0, kNilOptions, &mNumFonts); - ThrowIfOSErr(err, "Couldn't initialize the font menu!"); - - err = SetMenuTitleWithCFString(mFontMenu, MacString("Font")); - ThrowIfOSErr(err, "Couldn't set the font menu name!"); - - UInt16 numItems = CountMenuItems(mFontMenu); - for (UInt16 index = 1; index <= numItems; ++index) - { - // set the font for each menu item so we're WYSIWYG - Str255 fontName; - GetMenuItemText(mFontMenu, index, fontName); - - short fontNum; - GetFNum(fontName, &fontNum); - - SetMenuItemFontID(mFontMenu, index, fontNum); - - // set the command id so we can process the items (CreateStandardFontMenu - // leaves all of these at 0) - err = SetMenuItemCommandID(mFontMenu, index, 'Font' + index); - ThrowIfOSErr(err, "Couldn't set the font menu item's command ids!"); - } - - InsertMenu(mFontMenu, 0); - - // create the Size menu - err = CreateNewMenu(kSizeMenu, kMenuAttrAutoDisable, &menuH); - ThrowIfOSErr(err, "Couldn't create the size menu!"); - - err = SetMenuTitleWithCFString(menuH, MacString("Size")); - ThrowIfOSErr(err, "Couldn't set the size menu name!"); - - InsertMenu(menuH, 0); - - // add the Size menu items - const char* items[] = {"9", "10", "12", "16", "18", "20", "32", "48", "64", NULL}; - int sizes[] = {9, 10, 12, 16, 18, 20, 32, 48, 64}; - for (int i = 0; items[i] != NULL; ++i) - { - err = AppendMenuItemTextWithCFString(menuH, MacString(items[i]), kMenuItemAttrIgnoreMeta, 'Size' + sizes[i], NULL); - ThrowIfOSErr(err, "Couldn't add a size menu item!"); - } - - // install a custom menu handler - std::vector specs; - specs.push_back(CreateSpec(kEventClassCommand, kEventCommandProcess)); - specs.push_back(CreateSpec(kEventClassCommand, kEventCommandUpdateStatus)); - specs.push_back(CreateSpec(kEventClassMenu, kEventMenuOpening)); - - err = InstallApplicationEventHandler(NewEventHandlerUPP(DoMenuEvent), specs.size(), &specs[0], this, NULL); - ThrowIfOSErr(err, "Couldn't install the menu handlers!"); - - // draw the new menubar - DrawMenuBar(); -} - - -//--------------------------------------------------------------- -// -// CApplication::DoInitWindows -// -//--------------------------------------------------------------- -void CApplication::DoInitWindows() -{ - // create the window - Rect bounds = {32, 32, 64, 64}; // we position properly later - - WindowAttributes attrs = kWindowCollapseBoxAttribute | kWindowStandardHandlerAttribute; - OSStatus err = CreateNewWindow(kDocumentWindowClass, attrs, &bounds, &mWindow); - ThrowIfOSErr(err, "Couldn't create the window!"); - - // install a custom window handler - std::vector specs; - specs.push_back(CreateSpec(kEventClassWindow, kEventWindowDrawContent)); - - err = InstallWindowEventHandler(mWindow, NewEventHandlerUPP(DoWindowEvent), specs.size(), &specs[0], this, NULL); - ThrowIfOSErr(err, "Couldn't install the window event handler!"); -} - - -//--------------------------------------------------------------- -// -// CApplication::DoKeyDown [static] -// -//--------------------------------------------------------------- -pascal OSStatus CApplication::DoKeyDown(EventHandlerCallRef handler, EventRef event, void* refCon) -{ - OSStatus err = eventNotHandledErr; - - if (!sInDialog) - { - CApplication* thisPtr = static_cast(refCon); - - try - { - char ch; - (void) GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(ch), NULL, &ch); - - UInt32 key; - (void) GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(key), NULL, &key); - - UInt32 modifiers; - (void) GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers); - - if ((modifiers & cmdKey) == 0) - thisPtr->mChar = thisPtr->DoMungeChar(ch, key, modifiers); - - if (thisPtr->mChar != 0) - QuitApplicationEventLoop(); - - err = noErr; - } - catch (const std::exception& e) - { - DEBUGSTR((std::string("Couldn't complete the operation (") + e.what() + ").").c_str()); - err = eventNotHandledErr; - } - catch (...) - { - DEBUGSTR("Couldn't complete the operation."); - err = eventNotHandledErr; - } - } - - return err; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoMenuEvent [static] -// -//--------------------------------------------------------------- -pascal OSStatus CApplication::DoMenuEvent(EventHandlerCallRef handler, EventRef event, void* refCon) -{ - OSStatus err = noErr; - CApplication* thisPtr = static_cast(refCon); - - UInt32 kind = GetEventKind(event); - try - { - HICommand command; - - if (kind == kEventCommandUpdateStatus) - { - err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command); - ThrowIfOSErr(err, "Couldn't get the direct object in DoMenuEvent"); - - err = thisPtr->DoEnableCommand(command.menu.menuRef, command.commandID, command.menu.menuItemIndex); - } - else if (kind == kEventCommandProcess) - { - err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command); - ThrowIfOSErr(err, "Couldn't get the direct object in DoMenuEvent"); - - err = thisPtr->DoHandleCommand(command.commandID); - } - else if (kind == kEventMenuOpening) - { - Boolean firstTime; - err = GetEventParameter(event, kEventParamMenuFirstOpen, typeBoolean, NULL, sizeof(firstTime), NULL, &firstTime); - ThrowIfOSErr(err, "Couldn't get the first open flag in DoMenuEvent"); - - if (firstTime) // only call the callbacks the first time we open the menu (during this drag) - { - MenuRef menuH; - err = GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(MenuRef), NULL, &menuH); - ThrowIfOSErr(err, "Couldn't get the direct object in DoMenuEvent"); - - err = thisPtr->DoOpenMenu(menuH); - } - } - else - err = eventNotHandledErr; - } - catch (const std::exception& e) - { - DEBUGSTR((std::string("Couldn't complete the operation (") + e.what() + ").").c_str()); - err = eventNotHandledErr; - } - catch (...) - { - DEBUGSTR("Couldn't complete the operation."); - err = eventNotHandledErr; - } - - if (kind == kEventCommandProcess) - HiliteMenu(0); - - return err; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoMungeChar -// -//--------------------------------------------------------------- -char CApplication::DoMungeChar(char ch, UInt32 key, UInt32 modifiers) const -{ - switch (key) { - case kNumPad1Key: - if (modifiers & shiftKey) - ch = 'B'; - else if (modifiers & controlKey) - ch = 2; - else - ch = 'b'; - break; - - case kNumPad2Key: - case kDownArrowKey: - if (modifiers & shiftKey) - ch = 'J'; - else if (modifiers & controlKey) - ch = 10; - else - ch = 'j'; - break; - - case kNumPad3Key: - if (modifiers & shiftKey) - ch = 'N'; - else if (modifiers & controlKey) - ch = 14; - else - ch = 'n'; - break; - - case kNumPad4Key: - case kLeftArrowKey: - if (modifiers & shiftKey) - ch = 'H'; - else if (modifiers & controlKey) - ch = 8; - else - ch = 'h'; - break; - - case kNumPad5Key: - if (modifiers & shiftKey) - ch = '5'; - else - ch = '.'; - break; - - case kNumPad6Key: - case kRightArrowKey: - if (modifiers & shiftKey) - ch = 'L'; - else if (modifiers & controlKey) - ch = 12; - else - ch = 'l'; - break; - - case kNumPad7Key: - if (modifiers & shiftKey) - ch = 'Y'; - else if (modifiers & controlKey) - ch = 25; - else - ch = 'y'; - break; - - case kNumPad8Key: - case kUpArrowKey: - if (modifiers & shiftKey) - ch = 'K'; - else if (modifiers & controlKey) - ch = 11; - else - ch = 'k'; - break; - - case kNumPad9Key: - if (modifiers & shiftKey) - ch = 'U'; - else if (modifiers & controlKey) - ch = 21; - else - ch = 'u'; - break; - } - - return ch; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoOpenMenu -// -//--------------------------------------------------------------- -OSStatus CApplication::DoOpenMenu(MenuRef menuH) -{ - // select the curret font - - return noErr; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoQuit [static] -// -//--------------------------------------------------------------- -pascal OSErr CApplication::DoQuit(const AppleEvent* event, AppleEvent* reply, SInt32 refCon) -{ - CApplication* thisPtr = reinterpret_cast(refCon); - thisPtr->Quit(); - - return noErr; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoReadPrefs -// -//--------------------------------------------------------------- -void CApplication::DoReadPrefs() -{ - MacString appID("Crawl4"); - - // window location - Rect bounds; - - MacString name("window_x"); - Boolean existsX = false; - bounds.left = CFPreferencesGetAppIntegerValue(name, appID, &existsX); - - name = MacString("window_y"); - Boolean existsY = false; - bounds.top = CFPreferencesGetAppIntegerValue(name, appID, &existsY); - - if (existsX && existsY) - { - bounds.right = bounds.left + 32; // DoSetFont will reset the window dimensions - bounds.bottom = bounds.top + 32; - - OSStatus err = SetWindowBounds(mWindow, kWindowStructureRgn, &bounds); - ASSERT(err == noErr); - } - else - { - OSStatus err = RepositionWindow(mWindow, NULL, kWindowCenterOnMainScreen); - ASSERT(err == noErr); - } - - // mFontName - name = MacString("font_name"); - - CFTypeRef dataRef = CFPreferencesCopyAppValue(name, appID); - if (dataRef != NULL && CFGetTypeID(dataRef) == CFStringGetTypeID()) - { - MacString data(static_cast(dataRef)); - - Str255 fontName; - data.CopyTo(fontName, sizeof(fontName)); - CFRelease(dataRef); - - // mPointSize - name = MacString("font_size"); - CFIndex fontSize = CFPreferencesGetAppIntegerValue(name, appID, NULL); - if (fontSize > 0) - this->DoSetFont(fontName, fontSize); - else - this->DoSetFont(fontName, 12); - } - else - this->DoSetFont("\pMonaco", 12); - - // make sure the window isn't off-screen - Rect area; - OSStatus err = GetWindowGreatestAreaDevice(mWindow, kWindowStructureRgn, NULL, &area); - if (err == noErr) - { - err = GetWindowBounds(mWindow, kWindowDragRgn, &bounds); - if (err == noErr) - { - SectRect(&area, &bounds, &bounds); - - int pixels = (bounds.right - bounds.left)*(bounds.bottom - bounds.top); - if (pixels < 64) // only move the window if there are fewer than 64 draggable pixels - { - err = ConstrainWindowToScreen(mWindow, kWindowStructureRgn, kWindowConstrainStandardOptions, NULL, NULL); - ASSERT(err == noErr); - } - } - } -} - - -//--------------------------------------------------------------- -// -// CApplication::DoRender -// -//--------------------------------------------------------------- -void CApplication::DoRender() -{ - ASSERT(mLines.size() == kTermHeight); - - SetPortWindowPort(mWindow); - - TextFont(mFontNum); - TextSize(mPointSize); - - RGBBackColor(&mBackColor); - EraseRect(&kBigRect); - - Rect area; - for (int y = 0; y < mLines.size(); ++y) - { - area.top = y*mCellHeight; - area.bottom = area.top + mCellHeight; - area.left = 0; - area.right = area.left + mCellWidth; - - const Line& line = mLines[y]; - ASSERT(line.size() == kTermWidth); - - for (int x = 0; x < line.size(); ++x) - { - const SCell& cell = line[x]; - - this->DoDrawCell(area, cell); - - if (x == mCursor.h && y == mCursor.v && mShowCursor) - { - ::RGBForeColor(&kWhite); - ::MoveTo(area.left + 1, area.top + mAscent); - ::Line(area.right - area.left - 2, 0); - } - - area.left += mCellWidth; - area.right += mCellWidth; - } - } -} - - -//--------------------------------------------------------------- -// -// CApplication::DoSetChar -// -//--------------------------------------------------------------- -void CApplication::DoSetChar(unsigned char ch) -{ - ASSERT(mCursor.h < kTermWidth); - ASSERT(mCursor.v < kTermHeight); - ASSERT(mCursor.h >= 0); - ASSERT(mCursor.v >= 0); - - int x = mCursor.h; - - Line& line = mLines[mCursor.v]; - ASSERT(line.size() == kTermWidth); - - SCell& cell = line[x++]; - cell.ch = ch; - cell.color = mForeColor; - - Rect area; - area.top = mCursor.v * mCellHeight; - area.bottom = area.top + mCellHeight; - area.left = mCursor.h * mCellWidth; - area.right = area.left + mCellWidth; - - (void) InvalWindowRect(mWindow, &kBigRect); - - if (x >= kTermWidth) - { - if (++mCursor.v >= kTermHeight) - { - mCursor.v = kTermHeight - 1; - mCursor.h = kTermWidth - 1; - this->DoScroll(); - } - x = 0; - } - - mCursor.h = x; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoScroll -// -//--------------------------------------------------------------- -void CApplication::DoScroll() -{ - mLines.erase(mLines.begin()); - - mLines.push_back(Line()); - mLines.back().resize(kTermWidth); - - (void) InvalWindowRect(mWindow, &kBigRect); -} - - -//--------------------------------------------------------------- -// -// CApplication::DoSetFont -// -//--------------------------------------------------------------- -void CApplication::DoSetFont(const unsigned char* name, int size) -{ - ASSERT(name != NULL); - ASSERT(size > 0); - - short fontNum; - GetFNum(name, &fontNum); - - if (fontNum != mFontNum || size != mPointSize) - { - BlockMoveData(name, mFontName, name[0] + 1); - - mFontNum = fontNum; - mPointSize = size; - - SetPortWindowPort(mWindow); - TextFont(mFontNum); - TextSize(mPointSize); - - FontInfo info; - GetFontInfo(&info); - - mCellHeight = info.ascent + info.descent; - mAscent = info.ascent; - - short width = StringWidth("\pMMMMM"); // widMax is usually much too wide so we'll compute this ourselves... - mCellWidth = width/5 + 1; - - Rect bounds; - bounds.top = 0; - bounds.left = 0; - bounds.bottom = mCellHeight * kTermHeight; - bounds.right = mCellWidth * kTermWidth; - SizeWindow(mWindow, bounds.right, bounds.bottom, false); - - (void) InvalWindowRect(mWindow, &kBigRect); - } -} - - -//--------------------------------------------------------------- -// -// CApplication::DoWindowEvent [static] -// -//--------------------------------------------------------------- -pascal OSStatus CApplication::DoWindowEvent(EventHandlerCallRef handler, EventRef event, void* refCon) -{ - OSStatus err = noErr; - CApplication* thisPtr = static_cast(refCon); - - try - { - WindowRef window = NULL; - (void) GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(window), NULL, &window); - ASSERT(window == thisPtr->mWindow); - - UInt32 kind = GetEventKind(event); - switch (kind) { -// case kEventWindowBoundsChanging: -// thisPtr->DoConstrainWindow(event); -// break; - - case kEventWindowDrawContent: - thisPtr->DoRender(); - break; - -// case kEventWindowGetIdealSize: -// case kEventWindowGetMaximumSize: -// { -// ::Point maxSize = thisPtr->OnGetMaxSize(); -// SetEventParameter(event, kEventParamDimensions, &maxSize); -// } -// break; - -// case kEventWindowGetMinimumSize: -// { -// ::Point minSize = thisPtr->OnGetMinSize(); -// SetEventParameter(event, kEventParamDimensions, &minSize); -// } -// break; - - default: - err = eventNotHandledErr; - } - } - catch (const std::exception& e) - { - DEBUGSTR((std::string("Couldn't complete the operation (") + e.what() + ").").c_str()); - err = eventNotHandledErr; - } - catch (...) - { - DEBUGSTR("Couldn't complete the operation."); - err = eventNotHandledErr; - } - - return err; -} - - -//--------------------------------------------------------------- -// -// CApplication::DoWritePrefs , , mWindow->location -// -//--------------------------------------------------------------- -void CApplication::DoWritePrefs() -{ - MacString appID("Crawl4"); - - // mFontName - MacString name("font_name"); - MacString data(mFontName); - CFPreferencesSetAppValue(name, data, appID); - - // mPointSize - name = MacString("font_size"); - data = MacString(mPointSize); - CFPreferencesSetAppValue(name, data, appID); - - // window location - Rect bounds; - OSStatus err = GetWindowBounds(mWindow, kWindowStructureRgn, &bounds); - if (err == noErr) - { - name = MacString("window_x"); - data = MacString(bounds.left); - CFPreferencesSetAppValue(name, data, appID); - - name = MacString("window_y"); - data = MacString(bounds.top); - CFPreferencesSetAppValue(name, data, appID); - } - - // flush - VERIFY(CFPreferencesAppSynchronize(appID)); -} - -#if __MWERKS__ -#pragma mark - -#endif - -// ======================================================================== -// Non-ANSI Functions -// ======================================================================== - -//--------------------------------------------------------------- -// -// stricmp -// -// Case insensitive string comparison (code is from MSL which -// is why it looks so dorky). -// -//--------------------------------------------------------------- -int stricmp(const char* lhs, const char* rhs) -{ - ASSERT(lhs != NULL); - ASSERT(rhs != NULL); - - const unsigned char* p1 = (unsigned char*) lhs - 1; - const unsigned char* p2 = (unsigned char*) rhs - 1; - unsigned long c1, c2; - - while ((c1 = tolower(*++p1)) == (c2 = tolower(*++p2))) - if (c1 == '\0') - return (0); - - return c1 - c2; -} - - -//--------------------------------------------------------------- -// -// strlwr -// -// In place conversion to lower case. -// -//--------------------------------------------------------------- -char* strlwr(char* str) -{ - ASSERT(str != NULL); - - for (int i = 0; i < strlen(str); ++i) - str[i] = tolower(str[i]); - - return str; -} - - -//--------------------------------------------------------------- -// -// itoa -// -// Converts an integer to a string (after libunix.cc). -// -//--------------------------------------------------------------- -void itoa(int value, char* buffer, int radix) -{ - ASSERT(buffer != NULL); - ASSERT(radix == 10 || radix == 2); - - if (radix == 10) - sprintf(buffer, "%i", value); - - if (radix == 2) - { /* int to "binary string" */ - unsigned int bitmask = 32768; - int ctr = 0; - int startflag = 0; - - while (bitmask) - { - if (value & bitmask) - { - startflag = 1; - sprintf(buffer + ctr, "1"); - - } - else - { - if (startflag) - sprintf(buffer + ctr, "0"); - } - - bitmask = bitmask >> 1; - if (startflag) - ctr++; - } - - if (!startflag) /* Special case if value == 0 */ - sprintf((buffer + ctr++), "0"); - buffer[ctr] = (char) NULL; - } -} - -#if __MWERKS__ -#pragma mark - -#endif - -// ======================================================================== -// Curses(?) Functions -// ======================================================================== - -//--------------------------------------------------------------- -// -// window -// -//--------------------------------------------------------------- -void window(int x, int y, int lx, int ly) -{ - ASSERT(lx == kTermWidth); // window size is hard-coded - ASSERT(ly == kTermHeight); - - gotoxy(x, y); -} - - -//--------------------------------------------------------------- -// -// clrscr -// -//--------------------------------------------------------------- -void clrscr() -{ - ASSERT(sApp != NULL); - - sApp->Clear(); -} - - -//--------------------------------------------------------------- -// -// textcolor -// -//--------------------------------------------------------------- -void textcolor(int c) -{ - ASSERT(c >= 0); - ASSERT(c < 16); - ASSERT(sApp != NULL); - - RGBColor color = ConvertColor(c); - sApp->SetForeColor(color); -} - - -//--------------------------------------------------------------- -// -// textbackground -// -//--------------------------------------------------------------- -void textbackground(int c) -{ - ASSERT(c >= 0); - ASSERT(c < 16); - ASSERT(sApp != NULL); - - RGBColor color = ConvertColor(c); - sApp->SetBackColor(color); -} - - -//--------------------------------------------------------------- -// -// gotoxy -// -//--------------------------------------------------------------- -void gotoxy(int x, int y) -{ - ASSERT(x >= 1); - ASSERT(y >= 1); - ASSERT(sApp != NULL); - - sApp->SetCursor(x - 1, y - 1); -} - - -//--------------------------------------------------------------- -// -// wherex -// -//--------------------------------------------------------------- -int wherex() -{ - ASSERT(sApp != NULL); - - Point pos = sApp->GetCursor(); - - return pos.h + 1; -} - - -//--------------------------------------------------------------- -// -// wherey -// -//--------------------------------------------------------------- -int wherey() -{ - ASSERT(sApp != NULL); - - Point pos = sApp->GetCursor(); - - return pos.v + 1; -} - - -//--------------------------------------------------------------- -// -// putch -// -//--------------------------------------------------------------- -void putch(char ch) -{ - ASSERT(sApp != NULL); - - char buffer[2]; - - buffer[0] = ch; - buffer[1] = '\0'; - - sApp->SetChar(ch); -// sApp->Print(buffer); -} - - -//--------------------------------------------------------------- -// -// cprintf -// -//--------------------------------------------------------------- -void cprintf(const char* format,...) -{ - ASSERT(sApp != NULL); - - char buffer[2048]; - - va_list argp; - - va_start(argp, format); - vsprintf(buffer, format, argp); - va_end(argp); - - sApp->Print(buffer); -} - - -//--------------------------------------------------------------- -// -// kbhit -// -//--------------------------------------------------------------- -int kbhit() -{ - return sApp->PeekChar(); -} - - -//--------------------------------------------------------------- -// -// getche -// -//--------------------------------------------------------------- -char getche() -{ - char ch = getch(); - - if (ch != '\r') - putch(ch); - - return ch; -} - - -//--------------------------------------------------------------- -// -// getstr -// -//--------------------------------------------------------------- -void getstr(char* buffer, int bufferSize) -{ - ASSERT(buffer != NULL); - ASSERT(bufferSize > 1); - - int index = 0; - - while (index < bufferSize - 1) - { - char ch = getche(); - - if (ch == '\r') - break; - else if (ch == '\b' && index > 0) - --index; - else if (isprint(ch)) - buffer[index++] = ch; - } - - buffer[index] = '\0'; -} - - -//--------------------------------------------------------------- -// -// _setcursortype -// -//--------------------------------------------------------------- -void _setcursortype(int curstype) -{ - ASSERT(curstype == _NORMALCURSOR || curstype == _NOCURSOR); - ASSERT(sApp != NULL); - - sApp->ShowCursor(curstype == _NORMALCURSOR); -} - - -//--------------------------------------------------------------- -// -// getch -// -//--------------------------------------------------------------- -int getch() -{ - ASSERT(sApp != NULL); - - return sApp->GetChar(); -} - -#if __MWERKS__ -#pragma mark - -#endif - -// ======================================================================== -// Misc Functions -// ======================================================================== - -//--------------------------------------------------------------- -// -// delay -// -//--------------------------------------------------------------- -void delay(int ms) -{ - ASSERT(ms >= 0); - - usleep(1000*ms); -} - - -//--------------------------------------------------------------- -// -// init_mac -// -//--------------------------------------------------------------- -void init_mac() -{ - ASSERT(sApp == NULL); - - // Read in the color table - sColors = GetCTable(256); - if (sColors == NULL) - { - WarnUser("Couldn't load the colour table!"); - ExitToShell(); - } - - // Create the application object - sApp = new CApplication; -} - - -//--------------------------------------------------------------- -// -// deinit_mac -// -//--------------------------------------------------------------- -void deinit_mac() -{ - delete sApp; - sApp = NULL; -} - - -#endif // macintosh diff --git a/crawl-ref/source/libmac.h b/crawl-ref/source/libmac.h deleted file mode 100644 index 8b7e3903d6..0000000000 --- a/crawl-ref/source/libmac.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * File: libmac.h - * Summary: Mac specific routines used by Crawl. - * Written by: Jesse Jones - * - * Change History (most recent first): - * - * <2> 5/25/02 JDJ Updated for Mach-O targets - * <1> 3/23/99 JDJ Created - */ - -#ifndef LIBMAC_H -#define LIBMAC_H - -#if macintosh - -#ifdef _BSD_SIZE_T_ // $$$ is there a better way to test for OS X? - #define OSX 1 -#else - #define OS9 1 -#endif - -#include -#include -#include - -#if OSX - #include -#endif - -#define MAC_NUMBER_OF_LINES 30 - -// constants -const int _NORMALCURSOR = 1; -const int _NOCURSOR = 0; - - -// non-ANSI functions -int stricmp(const char* lhs, const char* rhs); -char* strlwr(char* str); -void itoa(int n, char* buffer, int radix); - -#if !OSX - inline void srandom(unsigned int seed) - { - srand(seed); - } - - int open(const char* path, int openFlags, int permissions); - int open(const char* path, int openFlags, int permissions, int mysteryFlags); - int close(int desc); - int read(int desc, void *buffer, unsigned int bytes); - int write(int desc, const void *buffer, unsigned int bytes); - int unlink(const char* path); -#endif - - -// curses(?) functions -void clrscr(); -void gotoxy(int x, int y); -void textcolor(int c); -void cprintf(const char* format,...); - -void window(int x, int y, int lx, int ly); -int wherex(); -int wherey(); -void putch(char c); -int kbhit(); - -char getche(); -int getch(); -void getstr(char* buffer, int bufferSize); - -void textbackground(int c); -void _setcursortype(int curstype); - - -// misc functions -void delay(int ms); - -void init_mac(); -void deinit_mac(); - - -#endif // macintosh -#endif // LIBMAC_H diff --git a/crawl-ref/source/libunix.cc b/crawl-ref/source/libunix.cc index 72441dfd3d..99605bea5f 100644 --- a/crawl-ref/source/libunix.cc +++ b/crawl-ref/source/libunix.cc @@ -63,15 +63,12 @@ static struct termios game_term; #endif // Character set variable -int character_set = CHARACTER_SET; +int Character_Set = CHARACTER_SET; // Globals holding current text/backg. colors short FG_COL = WHITE; short BG_COL = BLACK; -// a lookup table to convert keypresses to command enums -static int key_to_command_table[KEY_MAX]; - static unsigned int convert_to_curses_attr( int chattr ) { switch (chattr & CHATTR_ATTRMASK) @@ -150,10 +147,18 @@ static short translate_colour( short col ) } } +void set_altcharset( bool alt_on ) +{ + Character_Set = ((alt_on) ? A_ALTCHARSET : 0); +} -static void setup_colour_pairs( void ) +bool get_altcharset( void ) { + return (Character_Set != 0); +} +static void setup_colour_pairs( void ) +{ short i, j; for (i = 0; i < 8; i++) @@ -192,8 +197,10 @@ static void termio_init() int getch_ck() { int c = getch(); switch (c) { + // [dshaligram] MacOS ncurses returns 127 for backspace. + case 127: case KEY_BACKSPACE: return CK_BKSP; - case KEY_DC: return CK_DELETE; + case KEY_DC: return CK_DELETE; case KEY_HOME: return CK_HOME; case KEY_PPAGE: return CK_PGUP; case KEY_END: return CK_END; @@ -206,188 +213,6 @@ int getch_ck() { } } -void init_key_to_command() -{ - int i; - - // initialize to "do nothing" - for (i = 0; i < KEY_MAX; i++) - { - key_to_command_table[i] = CMD_NO_CMD; - } - - // lower case - key_to_command_table[(int) 'a'] = CMD_USE_ABILITY; - key_to_command_table[(int) 'b'] = CMD_MOVE_DOWN_LEFT; - key_to_command_table[(int) 'c'] = CMD_CLOSE_DOOR; - key_to_command_table[(int) 'd'] = CMD_DROP; - key_to_command_table[(int) 'e'] = CMD_EAT; - key_to_command_table[(int) 'f'] = CMD_FIRE; - key_to_command_table[(int) 'g'] = CMD_PICKUP; - key_to_command_table[(int) 'h'] = CMD_MOVE_LEFT; - key_to_command_table[(int) 'i'] = CMD_DISPLAY_INVENTORY; - key_to_command_table[(int) 'j'] = CMD_MOVE_DOWN; - key_to_command_table[(int) 'k'] = CMD_MOVE_UP; - key_to_command_table[(int) 'l'] = CMD_MOVE_RIGHT; - key_to_command_table[(int) 'm'] = CMD_DISPLAY_SKILLS; - key_to_command_table[(int) 'n'] = CMD_MOVE_DOWN_RIGHT; - key_to_command_table[(int) 'o'] = CMD_OPEN_DOOR; - key_to_command_table[(int) 'p'] = CMD_PRAY; - key_to_command_table[(int) 'q'] = CMD_QUAFF; - key_to_command_table[(int) 'r'] = CMD_READ; - key_to_command_table[(int) 's'] = CMD_SEARCH; - key_to_command_table[(int) 't'] = CMD_THROW; - key_to_command_table[(int) 'u'] = CMD_MOVE_UP_RIGHT; - key_to_command_table[(int) 'v'] = CMD_EXAMINE_OBJECT; - key_to_command_table[(int) 'w'] = CMD_WIELD_WEAPON; - key_to_command_table[(int) 'x'] = CMD_LOOK_AROUND; - key_to_command_table[(int) 'y'] = CMD_MOVE_UP_LEFT; - key_to_command_table[(int) 'z'] = CMD_ZAP_WAND; - - // upper case - key_to_command_table[(int) 'A'] = CMD_DISPLAY_MUTATIONS; - key_to_command_table[(int) 'B'] = CMD_RUN_DOWN_LEFT; - key_to_command_table[(int) 'C'] = CMD_EXPERIENCE_CHECK; - key_to_command_table[(int) 'D'] = CMD_BUTCHER; - key_to_command_table[(int) 'E'] = CMD_EVOKE; - key_to_command_table[(int) 'F'] = CMD_NO_CMD; - key_to_command_table[(int) 'G'] = CMD_NO_CMD; - key_to_command_table[(int) 'H'] = CMD_RUN_LEFT; - key_to_command_table[(int) 'I'] = CMD_OBSOLETE_INVOKE; - key_to_command_table[(int) 'J'] = CMD_RUN_DOWN; - key_to_command_table[(int) 'K'] = CMD_RUN_UP; - key_to_command_table[(int) 'L'] = CMD_RUN_RIGHT; - key_to_command_table[(int) 'M'] = CMD_MEMORISE_SPELL; - key_to_command_table[(int) 'N'] = CMD_RUN_DOWN_RIGHT; - key_to_command_table[(int) 'O'] = CMD_DISPLAY_OVERMAP; - key_to_command_table[(int) 'P'] = CMD_WEAR_JEWELLERY; - key_to_command_table[(int) 'Q'] = CMD_QUIT; - key_to_command_table[(int) 'R'] = CMD_REMOVE_JEWELLERY; - key_to_command_table[(int) 'S'] = CMD_SAVE_GAME; - key_to_command_table[(int) 'T'] = CMD_REMOVE_ARMOUR; - key_to_command_table[(int) 'U'] = CMD_RUN_UP_RIGHT; - key_to_command_table[(int) 'V'] = CMD_GET_VERSION; - key_to_command_table[(int) 'W'] = CMD_WEAR_ARMOUR; - key_to_command_table[(int) 'X'] = CMD_DISPLAY_MAP; - key_to_command_table[(int) 'Y'] = CMD_RUN_UP_LEFT; - key_to_command_table[(int) 'Z'] = CMD_CAST_SPELL; - - // control - key_to_command_table[ (int) CONTROL('A') ] = CMD_TOGGLE_AUTOPICKUP; - key_to_command_table[ (int) CONTROL('B') ] = CMD_OPEN_DOOR_DOWN_LEFT; - key_to_command_table[ (int) CONTROL('C') ] = CMD_CLEAR_MAP; - -#ifdef ALLOW_DESTROY_ITEM_COMMAND - key_to_command_table[ (int) CONTROL('D') ] = CMD_DESTROY_ITEM; -#else - key_to_command_table[ (int) CONTROL('D') ] = CMD_NO_CMD; -#endif - - key_to_command_table[ (int) CONTROL('E') ] = CMD_FORGET_STASH; - key_to_command_table[ (int) CONTROL('F') ] = CMD_SEARCH_STASHES; - key_to_command_table[ (int) CONTROL('G') ] = CMD_INTERLEVEL_TRAVEL; - key_to_command_table[ (int) CONTROL('H') ] = CMD_OPEN_DOOR_LEFT; - key_to_command_table[ (int) CONTROL('I') ] = CMD_NO_CMD; - key_to_command_table[ (int) CONTROL('J') ] = CMD_OPEN_DOOR_DOWN; - key_to_command_table[ (int) CONTROL('K') ] = CMD_OPEN_DOOR_UP; - key_to_command_table[ (int) CONTROL('L') ] = CMD_OPEN_DOOR_RIGHT; - key_to_command_table[ (int) CONTROL('M') ] = CMD_NO_CMD; - key_to_command_table[ (int) CONTROL('N') ] = CMD_OPEN_DOOR_DOWN_RIGHT; - key_to_command_table[ (int) CONTROL('O') ] = CMD_EXPLORE; - key_to_command_table[ (int) CONTROL('P') ] = CMD_REPLAY_MESSAGES; - key_to_command_table[ (int) CONTROL('Q') ] = CMD_NO_CMD; - key_to_command_table[ (int) CONTROL('R') ] = CMD_REDRAW_SCREEN; - key_to_command_table[ (int) CONTROL('S') ] = CMD_MARK_STASH; - key_to_command_table[ (int) CONTROL('T') ] = CMD_NO_CMD; - key_to_command_table[ (int) CONTROL('U') ] = CMD_OPEN_DOOR_UP_LEFT; - key_to_command_table[ (int) CONTROL('V') ] = CMD_NO_CMD; - key_to_command_table[ (int) CONTROL('W') ] = CMD_FIX_WAYPOINT; - key_to_command_table[ (int) CONTROL('X') ] = CMD_SAVE_GAME_NOW; - key_to_command_table[ (int) CONTROL('Y') ] = CMD_OPEN_DOOR_UP_RIGHT; - key_to_command_table[ (int) CONTROL('Z') ] = CMD_SUSPEND_GAME; - - // other printables - key_to_command_table[(int) '.'] = CMD_MOVE_NOWHERE; - key_to_command_table[(int) '<'] = CMD_GO_UPSTAIRS; - key_to_command_table[(int) '>'] = CMD_GO_DOWNSTAIRS; - key_to_command_table[(int) '@'] = CMD_DISPLAY_CHARACTER_STATUS; - key_to_command_table[(int) ','] = CMD_PICKUP; - key_to_command_table[(int) ':'] = CMD_NO_CMD; - key_to_command_table[(int) ';'] = CMD_INSPECT_FLOOR; - key_to_command_table[(int) '!'] = CMD_SHOUT; - key_to_command_table[(int) '^'] = CMD_DISPLAY_RELIGION; - key_to_command_table[(int) '#'] = CMD_CHARACTER_DUMP; - key_to_command_table[(int) '='] = CMD_ADJUST_INVENTORY; - key_to_command_table[(int) '?'] = CMD_DISPLAY_COMMANDS; - key_to_command_table[(int) '`'] = CMD_MACRO_ADD; - key_to_command_table[(int) '~'] = CMD_MACRO_SAVE; - key_to_command_table[(int) '&'] = CMD_WIZARD; - key_to_command_table[(int) '"'] = CMD_LIST_JEWELLERY; - - - // I'm making this both, because I'm tried of changing it - // back to '[' (the character that represents armour on the map) -- bwr - key_to_command_table[(int) '['] = CMD_LIST_ARMOUR; - key_to_command_table[(int) ']'] = CMD_LIST_ARMOUR; - - // This one also ended up backwards this time, so it's also going on - // both now -- should be ')'... the same character that's used to - // represent weapons. - key_to_command_table[(int) ')'] = CMD_LIST_WEAPONS; - key_to_command_table[(int) '('] = CMD_LIST_WEAPONS; - - key_to_command_table[(int) '\\'] = CMD_DISPLAY_KNOWN_OBJECTS; - key_to_command_table[(int) '\''] = CMD_WEAPON_SWAP; - - // digits - key_to_command_table[(int) '0'] = CMD_NO_CMD; - key_to_command_table[(int) '1'] = CMD_MOVE_DOWN_LEFT; - key_to_command_table[(int) '2'] = CMD_MOVE_DOWN; - key_to_command_table[(int) '3'] = CMD_MOVE_DOWN_RIGHT; - key_to_command_table[(int) '4'] = CMD_MOVE_LEFT; - key_to_command_table[(int) '5'] = CMD_REST; - key_to_command_table[(int) '6'] = CMD_MOVE_RIGHT; - key_to_command_table[(int) '7'] = CMD_MOVE_UP_LEFT; - key_to_command_table[(int) '8'] = CMD_MOVE_UP; - key_to_command_table[(int) '9'] = CMD_MOVE_UP_RIGHT; - - // keypad - key_to_command_table[KEY_A1] = CMD_MOVE_UP_LEFT; - key_to_command_table[KEY_A3] = CMD_MOVE_UP_RIGHT; - key_to_command_table[KEY_C1] = CMD_MOVE_DOWN_LEFT; - key_to_command_table[KEY_C3] = CMD_MOVE_DOWN_RIGHT; - - key_to_command_table[KEY_HOME] = CMD_MOVE_UP_LEFT; - key_to_command_table[KEY_PPAGE] = CMD_MOVE_UP_RIGHT; - key_to_command_table[KEY_END] = CMD_MOVE_DOWN_LEFT; - key_to_command_table[KEY_NPAGE] = CMD_MOVE_DOWN_RIGHT; - - key_to_command_table[KEY_B2] = CMD_REST; - - key_to_command_table[KEY_UP] = CMD_MOVE_UP; - key_to_command_table[KEY_DOWN] = CMD_MOVE_DOWN; - key_to_command_table[KEY_LEFT] = CMD_MOVE_LEFT; - key_to_command_table[KEY_RIGHT] = CMD_MOVE_RIGHT; - - - // other odd things - // key_to_command_table[ 263 ] = CMD_OPEN_DOOR_LEFT; // backspace - - // these are invalid keys, but to help kludge running - // pass them through unmolested - key_to_command_table[128] = 128; - key_to_command_table[(int) '*'] = '*'; -} - -int key_to_command(int keyin) -{ - bool is_userfunction(int key); - - if (is_userfunction(keyin)) - return keyin; - return (key_to_command_table[keyin]); -} - void unixcurses_startup( void ) { termio_init(); @@ -424,12 +249,7 @@ void unixcurses_startup( void ) start_color(); setup_colour_pairs(); - init_key_to_command(); - -#ifndef SOLARIS - // These can cause some display problems under Solaris scrollok(stdscr, TRUE); -#endif } @@ -575,6 +395,11 @@ int get_number_of_lines_from_curses(void) return (LINES); } +int get_number_of_cols_from_curses(void) +{ + return (COLS); +} + void get_input_line_from_curses( char *const buff, int len ) { echo(); @@ -605,9 +430,15 @@ inline unsigned get_brand(int col) (col & COLFLAG_ITEM_HEAP)? Options.heap_brand : (col & COLFLAG_WILLSTAB)? Options.stab_brand : (col & COLFLAG_MAYSTAB)? Options.may_stab_brand : + (col & COLFLAG_REVERSE)? CHATTR_REVERSE : CHATTR_NORMAL; } +void textattr(int col) +{ + textcolor(col); +} + void textcolor(int col) { short fg, bg; @@ -660,7 +491,7 @@ void textcolor(int col) // figure out which colour pair we want const int pair = (fg == 0 && bg == 0) ? 63 : (bg * 8 + fg); - attrset( COLOR_PAIR(pair) | flags | character_set ); + attrset( COLOR_PAIR(pair) | flags | Character_Set ); } @@ -715,7 +546,7 @@ void textbackground(int col) // figure out which colour pair we want const int pair = (fg == 0 && bg == 0) ? 63 : (bg * 8 + fg); - attrset( COLOR_PAIR(pair) | flags | character_set ); + attrset( COLOR_PAIR(pair) | flags | Character_Set ); } diff --git a/crawl-ref/source/libunix.h b/crawl-ref/source/libunix.h index c416733666..d42454dadc 100644 --- a/crawl-ref/source/libunix.h +++ b/crawl-ref/source/libunix.h @@ -17,7 +17,6 @@ int cprintf(const char *format,...); int gotoxy(int x, int y); int itoa(int value, char *strptr, int radix); int kbhit(void); -int key_to_command(int); int putch(unsigned char chr); int stricmp(const char *str1, const char *str2); int translate_keypad(int keyin); @@ -28,15 +27,19 @@ void update_screen(void); void clear_to_end_of_line(void); void clear_to_end_of_screen(void); int get_number_of_lines_from_curses(void); +int get_number_of_cols_from_curses(void); void get_input_line_from_curses( char *const buff, int len ); void _setcursortype(int curstype); void delay(unsigned long time); -void init_key_to_command(); void unixcurses_shutdown(void); void unixcurses_startup(void); void textbackground(int bg); void textcolor(int col); +void textattr(int col); + +void set_altcharset(bool alt_on); +bool get_altcharset(); #ifndef _LIBUNIX_IMPLEMENTATION /* Some stuff from curses, to remove compiling warnings.. */ diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc index 734ae2dc0a..556365968e 100644 --- a/crawl-ref/source/libutil.cc +++ b/crawl-ref/source/libutil.cc @@ -13,6 +13,7 @@ #include "AppHdr.h" #include "defines.h" +#include "initfile.h" #include "libutil.h" #include "externs.h" #include @@ -47,7 +48,7 @@ // the shell can do damage with. bool shell_safe(const char *file) { - int match = strcspn(file, "`$*?|><"); + int match = strcspn(file, "`$*?|><&\n"); return !(match >= 0 && file[match]); } @@ -59,7 +60,7 @@ void play_sound( const char *file ) sndPlaySound(file, SND_ASYNC | SND_NODEFAULT); #elif defined(SOUND_PLAY_COMMAND) char command[255]; - command[0] = '\0'; + command[0] = 0; if (file && *file && (strlen(file) + strlen(SOUND_PLAY_COMMAND) < 255) && shell_safe(file)) { @@ -69,19 +70,68 @@ void play_sound( const char *file ) #endif } +void uppercase(std::string &s) +{ + /* yes, this is bad, but std::transform() has its own problems */ + for (unsigned int i = 0; i < s.size(); ++i) + s[i] = toupper(s[i]); +} + +void lowercase(std::string &s) +{ + for (unsigned int i = 0; i < s.size(); ++i) + s[i] = tolower(s[i]); +} + +std::string replace_all(std::string s, + const std::string &tofind, + const std::string &replacement) +{ + std::string::size_type start = 0; + std::string::size_type found; + + while ((found = s.find_first_of(tofind, start)) != std::string::npos) + { + s.replace( found, tofind.length(), replacement ); + start = found + replacement.length(); + } + + return (s); +} + +int count_occurrences(const std::string &text, const std::string &s) +{ + int nfound = 0; + std::string::size_type pos = 0; + + while ((pos = text.find(s, pos)) != std::string::npos) + { + ++nfound; + pos += s.length(); + } + + return (nfound); +} + void get_input_line( char *const buff, int len ) { - buff[0] = '\0'; // just in case + buff[0] = 0; // just in case #if defined(UNIX) get_input_line_from_curses( buff, len ); // implemented in libunix.cc -#elif defined(MAC) || defined(WIN32CONSOLE) - getstr( buff, len ); // implemented in libmac.cc +#elif defined(WIN32CONSOLE) + getstr( buff, len ); #else + + // [dshaligram] Turn on the cursor for DOS. +#ifdef DOS + _setcursortype(_NORMALCURSOR); +#endif + fgets( buff, len, stdin ); // much safer than gets() #endif - buff[ len - 1 ] = '\0'; // just in case + buff[ len - 1 ] = 0; // just in case // Removing white space from the end in order to get rid of any // newlines or carriage returns that any of the above might have @@ -92,17 +142,20 @@ void get_input_line( char *const buff, int len ) for (i = end - 1; i >= 0; i++) { if (isspace( buff[i] )) - buff[i] = '\0'; + buff[i] = 0; else break; } } #ifdef DOS -static int getch_ck() { +static int getch_ck() +{ int c = getch(); - if (!c) { - switch (c = getch()) { + if (!c) + { + switch (c = getch()) + { case 'O': return CK_END; case 'P': return CK_DOWN; case 'I': return CK_PGUP; @@ -128,8 +181,9 @@ static int getch_ck() { // Hacky wrapper around getch() that returns CK_ codes for keys // we want to use in cancelable_get_line() and menus. -int c_getch() { -#if defined(DOS) || defined(LINUX) || defined(WIN32CONSOLE) +int c_getch() +{ +#if defined(DOS) || defined(UNIX) || defined(WIN32CONSOLE) return getch_ck(); #else return getch(); @@ -144,7 +198,8 @@ int wrapcprintf( int wrapcol, const char *s, ... ) va_start(args, s); // XXX: If snprintf isn't available, vsnprintf probably isn't, either. - int len = vsnprintf(buf, sizeof buf, s, args), olen = len; + int len = vsnprintf(buf, sizeof buf, s, args); + int olen = len; va_end(args); char *run = buf; @@ -157,7 +212,8 @@ int wrapcprintf( int wrapcol, const char *s, ... ) int avail = wrapcol - x + 1; int c = 0; - if (len > avail) { + if (len > avail) + { c = run[avail]; run[avail] = 0; } @@ -173,195 +229,49 @@ int wrapcprintf( int wrapcol, const char *s, ... ) return (olen); } -#define WX(x) ( ((x) - 1) % maxcol + 1 ) -#define WY(x,y) ( (y) + ((x) - 1) / maxcol ) -#define WC(x,y) WX(x), WY(x,y) -#define GOTOXY(x,y) gotoxy( WC(x,y) ) -bool cancelable_get_line( char *buf, int len, int maxcol, - input_history *mh ) +int cancelable_get_line( char *buf, int len, int maxcol, + input_history *mh, int (*keyproc)(int &ch) ) { - if (len <= 0) return false; - - buf[0] = 0; - - char *cur = buf; - int start = wherex(), line = wherey(); - int length = 0, pos = 0; + line_reader reader(buf, len, maxcol); + reader.set_input_history(mh); + reader.set_keyproc(keyproc); - if (mh) - mh->go_end(); - - for ( ; ; ) { - int ch = c_getch(); - - switch (ch) { - case CK_ESCAPE: - return false; - case CK_UP: - case CK_DOWN: - { - if (!mh) - break; - const std::string *text = ch == CK_UP? mh->prev() : mh->next(); - if (text) - { - int olen = length; - length = text->length(); - if (length >= len) - length = len - 1; - memcpy(buf, text->c_str(), length); - buf[length] = 0; - GOTOXY(start, line); - - int clear = length < olen? olen - length : 0; - wrapcprintf(maxcol, "%s%*s", buf, clear, ""); - - pos = length; - cur = buf + pos; - GOTOXY(start + pos, line); - } - break; - } - case CK_ENTER: - buf[length] = 0; - if (mh && length) - mh->new_input(buf); - return true; - case CONTROL('K'): - { - // Kill to end of line - int erase = length - pos; - if (erase) - { - length = pos; - buf[length] = 0; - wrapcprintf( maxcol, "%*s", erase, "" ); - GOTOXY(start + pos, line); - } - break; - } - case CK_DELETE: - if (pos < length) { - char *c = cur; - while (c - buf < length) { - *c = c[1]; - c++; - } - --length; - - GOTOXY( start + pos, line ); - buf[length] = 0; - wrapcprintf( maxcol, "%s ", cur ); - GOTOXY( start + pos, line ); - } - break; - case CK_BKSP: - if (pos) { - --cur; - char *c = cur; - while (*c) { - *c = c[1]; - c++; - } - --pos; - --length; - - GOTOXY( start + pos, line ); - buf[length] = 0; - wrapcprintf( maxcol, "%s ", cur ); - GOTOXY( start + pos, line ); - } - break; - case CK_LEFT: - if (pos) { - --pos; - cur = buf + pos; - GOTOXY( start + pos, line ); - } - break; - case CK_RIGHT: - if (pos < length) { - ++pos; - cur = buf + pos; - GOTOXY( start + pos, line ); - } - break; - case CK_HOME: - case CONTROL('A'): - pos = 0; - cur = buf + pos; - GOTOXY( start + pos, line ); - break; - case CK_END: - case CONTROL('E'): - pos = length; - cur = buf + pos; - GOTOXY( start + pos, line ); - break; - default: - if (isprint(ch) && length < len - 1) { - if (pos < length) { - char *c = buf + length - 1; - while (c >= cur) { - c[1] = *c; - c--; - } - } - *cur++ = (char) ch; - ++length; - ++pos; - putch(ch); - if (pos < length) { - buf[length] = 0; - wrapcprintf( maxcol, "%s", cur ); - } - GOTOXY(start + pos, line); - } - break; - } - } + return reader.read_line(); } -#undef GOTOXY -#undef WC -#undef WX -#undef WY // also used with macros std::string & trim_string( std::string &str ) { - // OK, this is really annoying. Borland C++ seems to define - // basic_string::erase to take iterators, and basic_string::remove - // to take size_t or integer. This is ass-backwards compared to - // nearly all other C++ compilers. Crap. (GDL) - // - // Borland 5.5 does this correctly now... leaving the old code - // around for now in case anyone needs it. -- bwr -// #ifdef __BCPLUSPLUS__ -// str.remove( 0, str.find_first_not_of( " \t\n\r" ) ); -// str.remove( str.find_last_not_of( " \t\n\r" ) + 1 ); -// #else str.erase( 0, str.find_first_not_of( " \t\n\r" ) ); str.erase( str.find_last_not_of( " \t\n\r" ) + 1 ); -// #endif return (str); } -std::vector split_string(const char *sep, std::string s) +std::vector split_string( + const char *sep, + std::string s, + bool trim_segments, + bool accept_empty_segments) { std::vector segments; + int separator_length = strlen(sep); std::string::size_type pos; - while ((pos = s.find(sep, 0)) != std::string::npos) { - if (pos > 0) + while ((pos = s.find(sep, 0)) != std::string::npos) + { + if (pos > 0 || accept_empty_segments) segments.push_back(s.substr(0, pos)); - s.erase(0, pos + 1); + s.erase(0, pos + separator_length); } if (s.length() > 0) segments.push_back(s); - for (int i = 0, count = segments.size(); i < count; ++i) - trim_string(segments[i]); + if (trim_segments) + { + for (int i = 0, count = segments.size(); i < count; ++i) + trim_string(segments[i]); + } return segments; } @@ -399,16 +309,23 @@ int snprintf( char *str, size_t size, const char *format, ... ) va_list argp; va_start( argp, format ); - char buff[ 10 * size ]; // hopefully enough + char *buff = new char [ 10 * size ]; // hopefully enough + if (!buff) + { + fprintf(stderr, "Out of memory\n"); + exit(1); + } vsprintf( buff, format, argp ); strncpy( str, buff, size ); - str[ size - 1 ] = '\0'; + str[ size - 1 ] = 0; int ret = strlen( str ); if ((unsigned int) ret == size - 1 && strlen( buff ) >= size) ret = -1; + delete [] buff; + va_end( argp ); return (ret); @@ -419,7 +336,8 @@ int snprintf( char *str, size_t size, const char *format, ... ) /////////////////////////////////////////////////////////////////////// // Pattern matching -inline int pm_lower(int ch, bool icase) { +inline int pm_lower(int ch, bool icase) +{ return icase? tolower(ch) : ch; } @@ -459,7 +377,8 @@ static bool glob_match( const char *pattern, const char *text, bool icase ) //////////////////////////////////////////////////////////////////// // Perl Compatible Regular Expressions -void *compile_pattern(const char *pattern, bool icase) { +void *compile_pattern(const char *pattern, bool icase) +{ const char *error; int erroffset; int flags = icase? PCRE_CASELESS : 0; @@ -470,7 +389,8 @@ void *compile_pattern(const char *pattern, bool icase) { NULL); } -void free_compiled_pattern(void *cp) { +void free_compiled_pattern(void *cp) +{ if (cp) pcre_free(cp); } @@ -494,7 +414,8 @@ bool pattern_match(void *compiled_pattern, const char *text, int length) //////////////////////////////////////////////////////////////////// // POSIX regular expressions -void *compile_pattern(const char *pattern, bool icase) { +void *compile_pattern(const char *pattern, bool icase) +{ regex_t *re = new regex_t; if (!re) return NULL; @@ -504,15 +425,18 @@ void *compile_pattern(const char *pattern, bool icase) { flags |= REG_ICASE; int rc = regcomp(re, pattern, flags); // Nonzero return code == failure - if (rc) { + if (rc) + { delete re; return NULL; } return re; } -void free_compiled_pattern(void *cp) { - if (cp) { +void free_compiled_pattern(void *cp) +{ + if (cp) + { regex_t *re = static_cast( cp ); regfree(re); delete re; @@ -541,7 +465,8 @@ void *compile_pattern(const char *pattern, bool icase) // If we're using simple globs, we need to box the pattern with '*' std::string s = std::string("*") + pattern + "*"; glob_info *gi = new glob_info; - if (gi) { + if (gi) + { gi->s = s; gi->ignore_case = icase; } @@ -563,113 +488,6 @@ bool pattern_match(void *compiled_pattern, const char *text, int length) #endif - -//////////////////////////////////////////////////////////////////// -// formatted_string -// - -formatted_string::formatted_string(const std::string &s) - : ops() -{ - ops.push_back( s ); -} - -formatted_string::operator std::string() const -{ - std::string s; - for (int i = 0, size = ops.size(); i < size; ++i) - { - if (ops[i] == FSOP_TEXT) - s += ops[i].text; - } - return s; -} - -inline void cap(int &i, int max) -{ - if (i < 0 && -i <= max) - i += max; - if (i >= max) - i = max - 1; - if (i < 0) - i = 0; -} - -std::string formatted_string::tostring(int s, int e) const -{ - std::string st; - - int size = ops.size(); - cap(s, size); - cap(e, size); - - for (int i = s; i <= e && i < size; ++i) - { - if (ops[i] == FSOP_TEXT) - st += ops[i].text; - } - return st; -} - -void formatted_string::display(int s, int e) const -{ - int size = ops.size(); - if (!size) - return ; - - cap(s, size); - cap(e, size); - - for (int i = s; i <= e && i < size; ++i) - ops[i].display(); -} - -void formatted_string::gotoxy(int x, int y) -{ - ops.push_back( fs_op(x, y) ); -} - -void formatted_string::textcolor(int color) -{ - ops.push_back(color); -} - -void formatted_string::cprintf(const char *s, ...) -{ - char buf[1000]; - va_list args; - va_start(args, s); - vsnprintf(buf, sizeof buf, s, args); - va_end(args); - - cprintf(std::string(buf)); -} - -void formatted_string::cprintf(const std::string &s) -{ - ops.push_back(s); -} - -void formatted_string::fs_op::display() const -{ - switch (type) - { - case FSOP_CURSOR: - { - int cx = (x == -1? wherex() : x); - int cy = (y == -1? wherey() : y); - ::gotoxy(cx, cy); - break; - } - case FSOP_COLOUR: - ::textcolor(x); - break; - case FSOP_TEXT: - ::cprintf("%s", text.c_str()); - break; - } -} - ///////////////////////////////////////////////////////////// // input_history // @@ -728,3 +546,270 @@ void input_history::clear() history.clear(); go_end(); } + +///////////////////////////////////////////////////////////////////////// +// line_reader + +line_reader::line_reader(char *buf, size_t sz, int wrap) + : buffer(buf), bufsz(sz), history(NULL), start_x(0), + start_y(0), keyfn(NULL), wrapcol(wrap), cur(NULL), + length(0), pos(-1) +{ +} + +std::string line_reader::get_text() const +{ + return (buffer); +} + +void line_reader::set_input_history(input_history *i) +{ + history = i; +} + +void line_reader::set_keyproc(keyproc fn) +{ + keyfn = fn; +} + +void line_reader::cursorto(int ncx) +{ + int x = (start_x + ncx - 1) % wrapcol + 1; + int y = start_y + (start_x + ncx - 1) / wrapcol; + ::gotoxy(x, y); +} + +int line_reader::read_line(bool clear_previous) +{ + if (bufsz <= 0) return false; + + cursor_control coff(true); + + if (clear_previous) + *buffer = 0; + + start_x = wherex(); + start_y = wherey(); + + length = strlen(buffer); + + // Remember the previous cursor position, if valid. + if (pos < 0 || pos > length) + pos = length; + + cur = buffer + pos; + + if (length) + wrapcprintf(wrapcol, "%s", buffer); + + if (pos != length) + cursorto(pos); + + if (history) + history->go_end(); + + for ( ; ; ) + { + int ch = c_getch(); + + if (keyfn) + { + int whattodo = (*keyfn)(ch); + if (whattodo == 0) + { + buffer[length] = 0; + if (history && length) + history->new_input(buffer); + return (0); + } + else if (whattodo == -1) + { + buffer[length] = 0; + return (ch); + } + } + + int ret = process_key(ch); + if (ret != -1) + return (ret); + } +} + +void line_reader::backspace() +{ + if (pos) + { + --cur; + char *c = cur; + while (*c) + { + *c = c[1]; + c++; + } + --pos; + --length; + + cursorto(pos); + buffer[length] = 0; + wrapcprintf( wrapcol, "%s ", cur ); + cursorto(pos); + } +} + +bool line_reader::is_wordchar(int c) +{ + return isalnum(c) || c == '_' || c == '-'; +} + +void line_reader::killword() +{ + if (!pos || cur == buffer) + return; + + bool foundwc = false; + while (pos) + { + if (is_wordchar(cur[-1])) + foundwc = true; + else if (foundwc) + break; + + backspace(); + } +} + +int line_reader::process_key(int ch) +{ + switch (ch) + { + case CK_ESCAPE: + return (ch); + case CK_UP: + case CK_DOWN: + { + if (!history) + break; + + const std::string *text = + ch == CK_UP? history->prev() : history->next(); + + if (text) + { + int olen = length; + length = text->length(); + if (length >= (int) bufsz) + length = bufsz - 1; + memcpy(buffer, text->c_str(), length); + buffer[length] = 0; + cursorto(0); + + int clear = length < olen? olen - length : 0; + wrapcprintf(wrapcol, "%s%*s", buffer, clear, ""); + + pos = length; + cur = buffer + pos; + cursorto(pos); + } + break; + } + case CK_ENTER: + buffer[length] = 0; + if (history && length) + history->new_input(buffer); + return (0); + + case CONTROL('K'): + { + // Kill to end of line + int erase = length - pos; + if (erase) + { + length = pos; + buffer[length] = 0; + wrapcprintf( wrapcol, "%*s", erase, "" ); + cursorto(pos); + } + break; + } + case CK_DELETE: + if (pos < length) + { + char *c = cur; + while (c - buffer < length) + { + *c = c[1]; + c++; + } + --length; + + cursorto(pos); + buffer[length] = 0; + wrapcprintf( wrapcol, "%s ", cur ); + cursorto(pos); + } + break; + + case CK_BKSP: + backspace(); + break; + + case CONTROL('W'): + killword(); + break; + + case CK_LEFT: + if (pos) + { + --pos; + cur = buffer + pos; + cursorto(pos); + } + break; + case CK_RIGHT: + if (pos < length) + { + ++pos; + cur = buffer + pos; + cursorto(pos); + } + break; + case CK_HOME: + case CONTROL('A'): + pos = 0; + cur = buffer + pos; + cursorto(pos); + break; + case CK_END: + case CONTROL('E'): + pos = length; + cur = buffer + pos; + cursorto(pos); + break; + default: + if (isprint(ch) && length < (int) bufsz - 1) + { + if (pos < length) + { + char *c = buffer + length - 1; + while (c >= cur) + { + c[1] = *c; + c--; + } + } + *cur++ = (char) ch; + ++length; + ++pos; + putch(ch); + if (pos < length) + { + buffer[length] = 0; + wrapcprintf( wrapcol, "%s", cur ); + } + cursorto(pos); + } + break; + } + + return (-1); +} diff --git a/crawl-ref/source/libutil.h b/crawl-ref/source/libutil.h index 807d3f25a2..d05ef53bd4 100644 --- a/crawl-ref/source/libutil.h +++ b/crawl-ref/source/libutil.h @@ -2,6 +2,8 @@ * File: libutil.h * Summary: System indepentant functions * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> 2001/Nov/01 BWR Created @@ -11,9 +13,19 @@ #ifndef LIBUTIL_H #define LIBUTIL_H +#include "defines.h" #include #include +void lowercase(std::string &s); +void uppercase(std::string &s); + +std::string replace_all(std::string s, + const std::string &tofind, + const std::string &replacement); + +int count_occurrences(const std::string &text, const std::string &searchfor); + // getch() that returns a consistent set of values for all platforms. int c_getch(); @@ -28,12 +40,27 @@ void get_input_line( char *const buff, int len ); class input_history; -// Returns true if user pressed Enter, false if user hit Escape. -bool cancelable_get_line( char *buf, int len, int wrapcol = 80, - input_history *mh = NULL ); +// In view.cc, declared here for default argument to cancelable_get_line() +int get_number_of_cols(void); + +// Returns zero if user entered text and pressed Enter, otherwise returns the +// key pressed that caused the exit, usually Escape. +// +// If keyproc is provided, it must return 1 for normal processing, 0 to exit +// normally (pretend the user pressed Enter), or -1 to exit as if the user +// pressed Escape +int cancelable_get_line( char *buf, + int len, + int wrapcol = get_number_of_cols(), + input_history *mh = NULL, + int (*keyproc)(int &c) = NULL ); std::string & trim_string( std::string &str ); -std::vector split_string(const char *sep, std::string s); +std::vector split_string( + const char *sep, + std::string s, + bool trim = true, + bool accept_empties = false); #ifdef NEED_USLEEP void usleep( unsigned long time ); @@ -48,7 +75,7 @@ enum KEYS { CK_ENTER = '\r', CK_BKSP = 8, - CK_ESCAPE = '\x1b', + CK_ESCAPE = ESCAPE, // 128 is off-limits because it's the code that's used when running CK_DELETE = 129, @@ -97,6 +124,57 @@ enum KEYS CK_CTRL_PGDN }; +class cursor_control +{ +public: + cursor_control(bool cs) : cstate(cs) { + _setcursortype(cs? _NORMALCURSOR : _NOCURSOR); + } + ~cursor_control() { + _setcursortype(cstate? _NOCURSOR : _NORMALCURSOR); + } +private: + bool cstate; +}; + +// Reads lines of text; used internally by cancelable_get_line. +class line_reader +{ +public: + line_reader(char *buffer, size_t bufsz, + int wrap_col = get_number_of_cols()); + + typedef int (*keyproc)(int &key); + + int read_line(bool clear_previous = true); + + std::string get_text() const; + + void set_input_history(input_history *ih); + void set_keyproc(keyproc fn); + +protected: + void cursorto(int newcpos); + int process_key(int ch); + void backspace(); + void killword(); + + bool is_wordchar(int c); + +private: + char *buffer; + size_t bufsz; + input_history *history; + int start_x, start_y; + keyproc keyfn; + int wrapcol; + + // These are subject to change during editing. + char *cur; + int length; + int pos; +}; + class base_pattern { public: @@ -117,12 +195,13 @@ public: text_pattern() : pattern(), compiled_pattern(NULL), - isvalid(false), ignore_case(false) + isvalid(false), ignore_case(false) { } text_pattern(const text_pattern &tp) - : pattern(tp.pattern), + : base_pattern(tp), + pattern(tp.pattern), compiled_pattern(NULL), isvalid(tp.isvalid), ignore_case(tp.ignore_case) diff --git a/crawl-ref/source/libw32c.cc b/crawl-ref/source/libw32c.cc index 8426b82b53..60062c36ea 100644 --- a/crawl-ref/source/libw32c.cc +++ b/crawl-ref/source/libw32c.cc @@ -3,6 +3,8 @@ * Summary: Functions for windows32 console mode support * Written by: Gordon Lipford * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 8 Mar 2001 GDL Rewrite to use low level IO @@ -62,10 +64,12 @@ // END -- WINDOWS INCLUDES +#ifdef __MINGW32__ +#include +#endif + #include -#ifdef __BCPLUSPLUS__ #include -#endif #include "AppHdr.h" #include "version.h" #include "defines.h" @@ -278,8 +282,7 @@ void bFlush(void) xy.X = cx; xy.Y = cy; CLOCKIN - if (SetConsoleCursorPosition(outbuf, xy) == 0) - fputs("SetConsoleCursorPosition() failed!", stderr); + SetConsoleCursorPosition(outbuf, xy); CLOCKOUT(2) } } @@ -299,12 +302,12 @@ void setStringInput(bool value) outmodes = 0; } - if ( SetConsoleMode( inbuf, inmodes ) == 0) { + if ( SetConsoleMode( inbuf, inmodes ) == 0) { fputs("Error initialising console input mode.", stderr); exit(0); } - if ( SetConsoleMode( outbuf, outmodes ) == 0) { + if ( SetConsoleMode( outbuf, outmodes ) == 0) { fputs("Error initialising console output mode.", stderr); exit(0); } @@ -327,6 +330,13 @@ static void init_colors(char *windowTitle) // if not found, quit. } +#ifdef __MINGW32__ +static void install_sighandlers() +{ + signal(SIGINT, SIG_IGN); +} +#endif + void init_libw32c(void) { inbuf = GetStdHandle( STD_INPUT_HANDLE ); @@ -338,11 +348,15 @@ void init_libw32c(void) } GetConsoleTitle( oldTitle, 78 ); - SetConsoleTitle( "Crawl " VERSION ); + SetConsoleTitle( CRAWL " " VERSION ); + +#ifdef __MINGW32__ + install_sighandlers(); +#endif init_colors(oldTitle); - // by default, set string input to false: use char-input only + // by default, set string input to false: use char-input only setStringInput( false ); if (SetConsoleMode( outbuf, 0 ) == 0) { fputs("Error initialising console output mode.", stderr); @@ -410,8 +424,7 @@ void deinit_libw32c(void) // only on input. void _setcursortype(int curstype) { - UNUSED( curstype ); - ; + _setcursortype_internal(curstype); } @@ -423,13 +436,13 @@ void _setcursortype_internal(int curstype) return; cci.dwSize = 5; - cci.bVisible = (bool)curstype; + cci.bVisible = curstype? TRUE : FALSE; current_cursor = curstype; CLOCKIN SetConsoleCursorInfo( outbuf, &cci ); CLOCKOUT(1) - // now, if we just changed from NOCURSOR to CURSOR, + // now, if we just changed from NOCURSOR to CURSOR, // actually move screen cursor if (current_cursor != _NOCURSOR) gotoxy(cx+1, cy+1); @@ -502,6 +515,11 @@ void gotoxy(int x, int y) } } +void textattr(int c) +{ + textcolor(c); +} + void textcolor(int c) { // change current color used to stamp chars @@ -584,7 +602,7 @@ void putch(char c) // translate virtual keys #define VKEY_MAPPINGS 10 -static int vk_tr[4][VKEY_MAPPINGS] = // virtual key, unmodified, shifted, control +static int vk_tr[4][VKEY_MAPPINGS] = // virtual key, unmodified, shifted, control { { VK_END, VK_DOWN, VK_NEXT, VK_LEFT, VK_CLEAR, VK_RIGHT, VK_HOME, VK_UP, VK_PRIOR, VK_INSERT }, { CK_END, CK_DOWN, CK_PGDN, CK_LEFT, CK_CLEAR, CK_RIGHT, CK_HOME, CK_UP, CK_PGUP , CK_INSERT }, @@ -696,9 +714,6 @@ int getch_ck(void) return repeat_key; } - bool oldValue = current_cursor; - _setcursortype_internal(_NORMALCURSOR); - while(1) { CLOCKIN @@ -728,8 +743,6 @@ int getch_ck(void) // DEBUG //fprintf(foo, "getch() returning %02x (%c)\n", key, key); - _setcursortype_internal(oldValue); - return key; } @@ -801,8 +814,8 @@ int getConsoleString(char *buf, int maxlen) if (ReadConsole( inbuf, buf, (DWORD)(maxlen-1), &nread, NULL) == 0) fputs("Error in ReadConsole()!", stderr); - // terminate string, then strip CRLF, replace with \0 - buf[maxlen-1] = '\0'; + // terminate string, then strip CRLF, replace with \0 + buf[maxlen-1] = 0; for (unsigned i=(nread<3 ? 0 : nread-3); i 0 then + return true + end + + if sc_hcl[mon] or sc_mut[mon] then + return false + end + + -- Only contaminated and clean chunks remain, in theory. We'll accept + -- either + return true +end diff --git a/crawl-ref/source/lua/safechunk.lua b/crawl-ref/source/lua/safechunk.lua deleted file mode 100644 index cd0c9f036d..0000000000 --- a/crawl-ref/source/lua/safechunk.lua +++ /dev/null @@ -1,41 +0,0 @@ --- SPOILER WARNING --- --- This file contains spoiler information. Do not read or use this file if you --- don't want to be spoiled. Further, the Lua code in this file may use this --- spoily information to take actions on your behalf. If you don't want --- automatic exploitation of spoilers, don't use this. --- ---------------------------------------------------------------------------- --- safechunk.lua: --- Determines whether a chunk of meat is safe for your character to eat. --- --- To use this, add this line to your init.txt: --- lua_file = lua/safechunk.lua --- --- This file has no directly usable functions, but is needed by gourmand.lua --- and enables auto_eat_chunks in eat.lua. ---------------------------------------------------------------------------- - -function sc_safechunk(rot, race, mon) - if race == "Ghoul" then - return true - end - - if rot then - if race ~= "Kobold" and race ~= "Troll" and not you.gourmand() then - return false - end - end - - if sc_pois[mon] and you.res_poison() > 0 then - return true - end - - if sc_hcl[mon] or sc_mut[mon] then - return false - end - - -- Only contaminated and clean chunks remain, in theory. We'll accept - -- either - return true -end diff --git a/crawl-ref/source/lua/stash.lua b/crawl-ref/source/lua/stash.lua index 461a504cf3..640144743c 100644 --- a/crawl-ref/source/lua/stash.lua +++ b/crawl-ref/source/lua/stash.lua @@ -1,6 +1,6 @@ --------------------------------------------------------------------------- -- stash.lua --- Annotates items for the stash-tracker. +-- Annotates items for the stash-tracker's search. -- -- To use this, add this line to your init.txt: -- lua_file = lua/stash.lua diff --git a/crawl-ref/source/lua/trapwalk.lua b/crawl-ref/source/lua/trapwalk.lua new file mode 100644 index 0000000000..511860353c --- /dev/null +++ b/crawl-ref/source/lua/trapwalk.lua @@ -0,0 +1,46 @@ +--------------------------------------------------------------------------- +-- trapwalk.lua: (requires base.lua) +-- (Thanks to JPEG for this script.) +-- +-- Allows travel to cross traps provided you have sufficient HP to survive the +-- trap. +-- +-- To use this, add this line to your init.txt: +-- lua_file = lua/trapwalk.lua +-- and add +-- trapwalk_safe_hp = dart:15, needle:25, spear:50 +-- or similar to your init.txt. +-- +-- What it does: +-- +-- * Normally autotravel automatically avoids all traps +-- * This script allows you to customize at which hp what type of trap is +-- regarded as safe for autotravel +-- +-- IMPORTANT: trapwalk options must be defined *after* sourcing trapwalk.lua. +--------------------------------------------------------------------------- + +-- Travel will cross certain traps if you have more than safe_hp hp. + +function ch_cross_trap(trap) + + if not options.trapwalk_safe_hp then + return false + end + + local opt = options.trapwalk_safe_hp + + local hpstr + _, _, hpstr = string.find(opt, trap .. "%s*:%s*(%d+)") + + if not hpstr then + return false + end + + local safe_hp = tonumber(hpstr) + local hp = you.hp() + + -- finally compare current hp with safe limit + return hp >= safe_hp + +end diff --git a/crawl-ref/source/machdr.h b/crawl-ref/source/machdr.h deleted file mode 100644 index 5bad24eab7..0000000000 --- a/crawl-ref/source/machdr.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * File: MacHdr.h - * Summary: OS specific stuff for inclusion into AppHdr.h. - * Written by: Jesse Jones - * - * Copyright © 1999 Jesse Jones. - * - * Change History (most recent first): - * - * <1> 5/30/99 JDJ Created. - */ -#ifndef MACHDR_H -#define MACHDR_H - -#pragma check_header_flags on - - -// =================================================================================== -// MSL Defines -// =================================================================================== -#include - -#ifndef __dest_os -#define __dest_os __mac_os -#endif - -#ifndef __MSL_LONGLONG_SUPPORT__ -#define __MSL_LONGLONG_SUPPORT__ -#endif - -#ifndef MSIPL_DEF_EXPLICIT -#define MSIPL_DEF_EXPLICIT // prevent explicit from being defined away -#endif - -#if __MWERKS__ >= 0x2000 -#define MSIPL_TEMPL_NEWSPEC 1 // enable null_template -#endif - - -// =================================================================================== -// Universal headers -// =================================================================================== -#define OLDROUTINENAMES 0 -#define OLDROUTINELOCATIONS 0 - -#define STRICT_WINDOWS 0 -#define STRICT_CONTROLS 0 -#define STRICT_LISTS 0 -#define STRICT_MENUS 0 - -#define USE_OLD_INPUT_SPROCKET_LABELS 0 -#define USE_OLD_ISPNEED_STRUCT 0 - -#if 1 -// #include -// #include -// #include -// #include -// #include -// #include -#include -#include -// #include -// #include -// #include -// #include -// #include -#include -// #include -// #include -// #include -#include -// #include -// #include -#include -// #include -// #include -#include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -#include -#include -// #include -// #include -// #include -#include -// #include -#include -#include -// #include -// #include -// #include -// #include -#include -#include -// #include -#include -// #include -// #include -// #include -#include -// #include -#include -#include -// #include -// #include -// #include -#include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -#include -// #include -// #include -// #include -#include -#include -// #include -#include -// #include -// #include -// #include -// #include -// #include -// #include -#include -#include -#include -// #include -// #include -#include -// #include -// #include -#include -#include -#include -#include -#include -// #include -#include -// #include -// #include -#include -// #include -// #include -#include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -#include -// #include -#include -// #include -// #include -#include -// #include -#include -// #include -// #include -#include -// #include -// #include -#include -// #include -#include -// #include -// #include -#include -// #include -#endif // PRECOMPILE_SYSTEM_HEADERS - -#endif diff --git a/crawl-ref/source/macro.cc b/crawl-ref/source/macro.cc index 13838a947c..ed03525f9f 100644 --- a/crawl-ref/source/macro.cc +++ b/crawl-ref/source/macro.cc @@ -3,6 +3,8 @@ * Summary: Crude macro-capability * Written by: Juho Snellman * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <3> 6/25/02 JS Completely rewritten @@ -553,11 +555,11 @@ int getch_with_command_macros( void ) keyseq keys = getch_mul(); // ... and add them into the buffer (apply keymaps) macro_buf_add_long( keys ); + + // Apply longest matching macro at front of buffer: + macro_buf_apply_command_macro(); } - // Apply longest matching macro at front of buffer: - macro_buf_apply_command_macro(); - return (macro_buf_get()); } @@ -578,8 +580,7 @@ void macro_add_query( void ) KeymapContext keymc = KC_DEFAULT; mesclr(); - mpr( "Command (m)acro or keymap [(k) default, (x) level-map or " - "(t)argeting]? ", MSGCH_PROMPT ); + mpr( "(m)acro, keymap [(k) default, (x) level-map or (t)argeting], (s)ave?", MSGCH_PROMPT ); input = getch(); if (input == 0) input = getch(); @@ -602,7 +603,13 @@ void macro_add_query( void ) } else if (input == 'm') keymap = false; - else + else if (input == 's') + { + mpr("Saving macros."); + macro_save(); + return; + } + else { mpr( "Aborting." ); return; diff --git a/crawl-ref/source/macro.h b/crawl-ref/source/macro.h index dba3bafafe..f56edcf219 100644 --- a/crawl-ref/source/macro.h +++ b/crawl-ref/source/macro.h @@ -3,6 +3,8 @@ * Summary: Crude macro-capability * Written by: Juho Snellman * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 6/25/02 JS Removed old cruft diff --git a/crawl-ref/source/makefile b/crawl-ref/source/makefile index 1f7dac1cb0..74adf5d88c 100644 --- a/crawl-ref/source/makefile +++ b/crawl-ref/source/makefile @@ -4,19 +4,20 @@ #Makefile chooser. Choose one: -#MAKEFILE = makefile.lnx -#MAKEFILE = makefile.sgi +MAKEFILE = makefile.unix #MAKEFILE = makefile.dos -#MAKEFILE = makefile.emx -#MAKEFILE = makefile.sol -MAKEFILE = makefile.osx +#MAKEFILE = makefile.osx +#MAKEFILE = makefile.mgw #jmf: number of concurrent jobs -- good value is (#_of_CPUs * 2) + 1 # cuts build time a lot on multi-cpu machines #OTHER=-j2 all: - $(MAKE) $(OTHER) -f $(MAKEFILE) EXTRA_FLAGS='-O2 -fno-strength-reduce' + $(MAKE) $(OTHER) -f $(MAKEFILE) EXTRA_FLAGS='-O3' + +profile: + $(MAKE) $(OTHER) -f $(MAKEFILE) EXTRA_FLAGS='-O3 -pg' noopt: $(MAKE) $(OTHER) -f $(MAKEFILE) @@ -36,7 +37,15 @@ wizard: # DEBUG mode includes WIZARD mode as well as copious debugging input debug: - $(MAKE) $(OTHER) -f $(MAKEFILE) debug EXTRA_FLAGS='-g -DFULLDEBUG -DWIZARD' + $(MAKE) $(OTHER) -f $(MAKEFILE) DEBUG_CRAWL=y debug EXTRA_FLAGS='-g -DFULLDEBUG -DWIZARD' + +# [dshaligram] The individual makefile need not necessarily support +# package-source; only makefile.unix does at the moment. +package-source: + $(MAKE) -f $(MAKEFILE) package-source + +prebuildyacc: + $(MAKE) -f $(MAKEFILE) prebuildyacc # DO NOT DELETE THIS LINE -- $(MAKE) depend depends on it. diff --git a/crawl-ref/source/makefile.bor b/crawl-ref/source/makefile.bor deleted file mode 100644 index d90cf636d9..0000000000 --- a/crawl-ref/source/makefile.bor +++ /dev/null @@ -1,53 +0,0 @@ -# -# Makefile for Borland C++ 5.5 free commandline tools -# - -!include makefile.obj - -GAME = ..\crawl.exe - -all : $(GAME) - -.PHONY : clean -.PHONY : debug -.PHONY : noopt -.PHONY : install -.PHONY : wizard - -clean : - $(DEL) *.o - -debug : - -noopt : - -install : - -wizard : - -# -# Borland C++ tools -# -CC = bcc32 -LINK = ilink32 -DEL = del - -# -# windows defines, including windows version to make sure code runs on -# all windows systems -# -WIN32DEFINES = WIN32CONSOLE;NEED_SPRINTF;WINVER=0x0400;_WIN32_WINNT=0x0400;__BCPLUSPLUS__ - -# -# Options -# -LINKOPTS = -Tpe -ap -c -x /V4.0 -CFLAGS = -I. -D$(WIN32DEFINES) -4 -a -k- -Oc -OS -Oi -Ov -H- -P -c -NICEFLAGS = -w-8004 -DEBUGFLAGS = -v -y - -$(GAME) : $(OBJECTS) libw32c.o - $(LINK) $(LINKOPTS) c0x32.obj $?, $(GAME),, import32.lib cw32.lib - -.cc.o: - $(CC) $(CFLAGS) $(NICEFLAGS) -o$*.o $< diff --git a/crawl-ref/source/makefile.bsd b/crawl-ref/source/makefile.bsd deleted file mode 100644 index 9e10d1ebf8..0000000000 --- a/crawl-ref/source/makefile.bsd +++ /dev/null @@ -1,64 +0,0 @@ -# -*- Makefile -*- for Dungeon Crawl (BSD) - -# -# Modified for Crawl Reference by $Author$ on $Date$ -# - -GAME = crawl - -# this file contains a list of the libraries. -# it will make a variable called OBJECTS that contains all the libraries -include makefile.obj - -OBJECTS += libunix.o - -CXX = g++ -DELETE = rm -f -COPY = cp -OS_TYPE = BSD -CFLAGS = -Wall -D$(OS_TYPE) $(EXTRA_FLAGS) -LDFLAGS = -static -L/usr/lib -MCHMOD = 711 -# INSTALLDIR = /usr/games -INSTALLDIR = /tmp/CRAWLTEST/testdev -LIB = -lncurses - -INCLUDES = -I/usr/include/ncurses - -all: $(GAME) - -install: $(GAME) - $(COPY) $(GAME) ${INSTALLDIR} - chmod ${MCHMOD} ${INSTALLDIR}/$(GAME) - -clean: - $(DELETE) *.o - -distclean: - $(DELETE) *.o - $(DELETE) bones.* - $(DELETE) morgue.txt - $(DELETE) scores - $(DELETE) $(GAME) - $(DELETE) *.sav - $(DELETE) core - $(DELETE) *.0* - $(DELETE) *.lab - - -$(GAME): $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) - strip $(GAME) - chmod ${MCHMOD} $(GAME) - -debug: $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) - -profile: $(OBJECTS) - ${CXX} -g -p ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) - -.cc.o: - ${CXX} ${CFLAGS} -c $< ${INCLUDE} - -.h.cc: - touch $@ diff --git a/crawl-ref/source/makefile.dos b/crawl-ref/source/makefile.dos index 869d6f8745..f8e43f631f 100644 --- a/crawl-ref/source/makefile.dos +++ b/crawl-ref/source/makefile.dos @@ -1,4 +1,6 @@ # Make file for Dungeon Crawl (dos) +# +# Modified for Crawl Reference by $Author$ on $Date$ # this file contains a list of the libraries. # it will make a variable called OBJECTS that contains all the libraries @@ -10,18 +12,107 @@ CXX = gxx DELETE = del COPY = copy OS_TYPE = DOS -CFLAGS = -D$(OS_TYPE) $(EXTRA_FLAGS) -LDFLAGS = -Bc:/djgpp/contrib/pdcurs22/lib -INSTALLDIR = c:\games\crawl + +LIB = + +# If you don't have flex or bison, set DOYACC to N or empty. +DOYACC := n +LEX := flex +YACC := bison -y + +INCLUDES := -Iutil -I. + +CFWARN := -Wall -Wwrite-strings -Wshadow -Werror -pedantic +CFOTHERS := -D$(OS_TYPE) $(EXTRA_FLAGS) -fsigned-char -fstrict-aliasing + +CFLAGS := $(INCLUDES) $(CFWARN) $(CFOTHERS) +YCFLAGS := $(INCLUDES) $(CFOTHERS) + +OBJECTS += libdos.o + +LDFLAGS = # LIB = -lcurso -lpano +UTIL = util/ + +# DOS brain damage. What to do, what to do. +YTABC := levcom~1.c +YTABH := levcom~1.h + +ifeq ($(LEX),) +DOYACC := +endif + +ifeq ($(YACC),) +DOYACC := +endif + +OBJECTS := $(UTIL)levcomp.o $(UTIL)levtab.o $(UTIL)levlex.o $(OBJECTS) + +GAME_DEPENDS := $(OBJECTS) + +########################################################################## + all: $(APPNAME) -install: $(APPNAME) - $(COPY) $(APPNAME) ${INSTALLDIR} +########################################################################## +# The level compiler +# + +# [dshaligram] A million plagues on djgpp make! It doesn't want to use a +# generic rule for $(UTIL)%.cc; it always uses the .cc.o: rule instead. + +LINC := + +# [ds] If we're using the prebuilt include, we can't copy it around because +# djgpp copy mangles the name irreparably. +ifneq ($(DOYACC),y) +LINC += -I prebuilt +endif + +$(UTIL)levcomp.o: $(UTIL)levcomp.cc + $(subst /,\,$(CXX) $(LINC) $(YCFLAGS) -o $@ -c $<) + +$(UTIL)levlex.o: $(UTIL)levlex.cc + $(subst /,\,$(CXX) $(LINC) $(YCFLAGS) -o $@ -c $<) + +$(UTIL)levtab.o: $(UTIL)levtab.cc + $(subst /,\,$(CXX) $(LINC) $(YCFLAGS) -o $@ -c $<) + +ifeq ($(DOYACC),y) + +$(UTIL)levtab.cc: $(UTIL)levcomp.ypp + $(subst /,\,cd $(UTIL)) + $(YACC) -d -b levcomp levcomp.ypp + copy $(YTABC) levtab.cc + cd .. + +# djgpp flex must not have a space between -o and its parameter, or no business +# will result. +$(UTIL)levlex.cc: $(UTIL)levcomp.lpp + $(subst /,\,cd $(UTIL)) + $(LEX) -olevlex.cc levcomp.lpp + cd .. + +else + +$(UTIL)levtab.cc: prebuilt/levcom~2.cc + $(subst /,\,$(COPY) $< $@) + +$(UTIL)levlex.cc: prebuilt/levcom~1.cc + $(subst /,\,$(COPY) $< $@) + +endif + +########################################################################## clean: $(DELETE) *.o + $(subst /,\,$(DELETE) $(UTIL)*.o) + $(subst /,\,$(DELETE) $(UTIL)*.exe) + $(subst /,\,$(DELETE) $(UTIL)levcom~1.*) + $(subst /,\,$(DELETE) $(UTIL)levtab.*) + $(subst /,\,$(DELETE) $(UTIL)levlex.*) distclean: $(DELETE) *.o @@ -34,18 +125,18 @@ distclean: $(DELETE) *.0* $(DELETE) *.lab -$(APPNAME): $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) +$(APPNAME): $(GAME_DEPENDS) + ${CXX} ${LDFLAGS} $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) strip $(APPNAME) -debug: $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) +debug: $(GAME_DEPENDS) + ${CXX} ${LDFLAGS} $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) -profile: $(OBJECTS) - ${CXX} -g -p ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) +profile: $(GAME_DEPENDS) + ${CXX} -g -p ${LDFLAGS} $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) .cc.o: - ${CXX} ${CFLAGS} -c $< ${INCLUDE} + ${CXX} ${CFLAGS} -c $< # I don't have touch for DOS, but if you do, you can put this back. # diff --git a/crawl-ref/source/makefile.emx b/crawl-ref/source/makefile.emx deleted file mode 100644 index 13e99e47fe..0000000000 --- a/crawl-ref/source/makefile.emx +++ /dev/null @@ -1,53 +0,0 @@ -# Makefile for Dungeon Crawl (OS/2 EMX port) -# 1998 (C) Alexey Guzeev, aga@russia.crosswinds.net -# EMX is covered by GNU GPL -# Dungeon Crawl is covered by Crawl GPL -# OS/2 is a trademark of IBM Corp. -# IBM is a trademark of IBM Corp. -# :) - -# 1. make some directory, like \crawl -# 2. make subdirectory for sources, \crawl\src -# 3. put crawl sources in \crawl\src directory -# 4. make directory \crawl\src current -# 5. execute command 'dmake -B -r -f makefile.emx install' -# 6. remove \crawl\src subdirectory with all contents -# 7. delete \crawl\scoretable.exe - I don't know what it does :) -# 8. run \crawl\crawl.exe & enjoy! - - -CC = gcc -CFLAGS = -Wall -O3 -MMD -Zmt -DUSE_EMX -LIBS = -lvideo -lbsd -AR = ar - -include makefile.obj - -OBJ = $(OBJECTS) - - -all: crawl.exe scoretable.exe - -install: ..\crawl.exe ..\scoretable.exe - -crawl.a: $(OBJS) - $(AR) r crawl.a $(OBJS) - -..\crawl.exe: crawl.exe - +copy crawl.exe .. - emxbind -s ..\crawl.exe - -..\scoretable.exe: scoretable.exe - +copy scoretable.exe .. - emxbind -s ..\scoretable.exe -clean: - +del *.o - -crawl.exe: crawl.a libemx.o - $(CC) -o crawl.exe crawl.a libemx.o $(LIBS) - -scoretable.exe: scoretab.o libemx.o - $(CC) -o scoretable.exe scoretab.o libemx.o $(LIBS) - -.cc.o: - $(CC) $(CFLAGS) -c $*.cc diff --git a/crawl-ref/source/makefile.lnx b/crawl-ref/source/makefile.lnx deleted file mode 100644 index 8b8b8994fd..0000000000 --- a/crawl-ref/source/makefile.lnx +++ /dev/null @@ -1,68 +0,0 @@ -# -*- Makefile -*- for Dungeon Crawl (linux) - -# -# Modified for Crawl Reference by $Author$ on $Date$ -# - -GAME = crawl - -# this file contains a list of the libraries. -# it will make a variable called OBJECTS that contains all the libraries -include makefile.obj - -OBJECTS += libunix.o - -CXX = g++ -DELETE = rm -f -COPY = cp -OS_TYPE = LINUX - -CFLAGS = -Wall -Wwrite-strings -fsigned-char \ - -g -D$(OS_TYPE) $(EXTRA_FLAGS) - -MCHMOD = 2755 - -# [dshaligram] More common location than /opt/crawl/bin - this is from the -# Debian patch. -INSTALLDIR = /usr/games -LIB = -lncurses - -# Include for Linux -INCLUDES = -I/usr/include/ncurses - -all: $(GAME) - -install: $(GAME) - $(COPY) $(GAME) ${INSTALLDIR} - chmod ${MCHMOD} ${INSTALLDIR}/$(GAME) - -clean: - $(DELETE) *.o - -distclean: - $(DELETE) *.o - $(DELETE) bones.* - $(DELETE) morgue.txt - $(DELETE) scores - $(DELETE) $(GAME) - $(DELETE) *.sav - $(DELETE) core - $(DELETE) *.0* - $(DELETE) *.lab - - -$(GAME): $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) - chmod ${MCHMOD} $(GAME) - -debug: $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) - -profile: $(OBJECTS) - ${CXX} -g -p ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) - -.cc.o: - ${CXX} ${CFLAGS} -c $< ${INCLUDE} - -.h.cc: - touch $@ diff --git a/crawl-ref/source/makefile.mgw b/crawl-ref/source/makefile.mgw index 499d3e22d2..17a47f132c 100644 --- a/crawl-ref/source/makefile.mgw +++ b/crawl-ref/source/makefile.mgw @@ -1,51 +1,160 @@ -# Make file for Dungeon Crawl (dos) +# Make file for Dungeon Crawl (Win32, MinGW) -# this file contains a list of the libraries. -# it will make a variable called OBJECTS that contains all the libraries +# makefile.obj includes a list of object files needed to build Crawl. include makefile.obj +ifeq ($(DEBUG_CRAWL),) +OPATH := rel +else +OPATH := dbg +endif + # need .exe so make will find the right file -APPNAME = crawl.exe +APPNAME = $(OPATH)\crawl.exe CXX = g++ -DELETE = rm +DELETE = del COPY = copy OS_TYPE = WIN32CONSOLE -CFLAGS = -Wall -Wwrite-strings -Wstrict-prototypes \ - -Wmissing-prototypes -Wmissing-declarations \ - -D$(OS_TYPE) $(EXTRA_FLAGS) + +INSTALLDIR := $(OPATH) + +LIB = -lwinmm -static + +CFWARN := -Wall -Wwrite-strings \ + -Wshadow \ + -Werror \ + -pedantic + +INCLUDES := -Iutil -I. + +CFOTHERS := -fsigned-char \ + -fstrict-aliasing \ + -pedantic \ + -D$(OS_TYPE) $(EXTRA_FLAGS) \ + -DWINMM_PLAY_SOUNDS + +CFLAGS := $(INCLUDES) $(CFWARN) $(CFOTHERS) +YCFLAGS := $(INCLUDES) $(CFOTHERS) + +OBJECTS := $(OBJECTS) libw32c.o + LDFLAGS = -INSTALLDIR = . + #LIB = -lcurso -lpano -LIB = -lwinmm -static -lpcre -lluacore -lluastd + +UTIL = util/ + +# If you don't have flex or bison, set DOYACC to N or empty. +DOYACC := n + +LEX := flex +YACC := bison -y + +YTABC := levcomp.tab.c +YTABH := levcomp.tab.h + + +ifeq ($(LEX),) +DOYACC := +endif + +ifeq ($(YACC),) +DOYACC := +endif + +# Do the levcomp stuff first because that's the most likely to fail. +OBJECTS := levcomp.tab.o levcomp.lex.o levcomp.o \ + $(OBJECTS) + +OBJECTS := $(foreach file,$(OBJECTS),$(OPATH)/$(file)) + +GAME_DEPENDS := prepare $(OBJECTS) + +########################################################################## all: $(APPNAME) +prepare: + if not exist $(OPATH) mkdir $(OPATH) + +########################################################################## +# The level compiler +# +ifeq ($(DOYACC),y) + +# [ds] A plague on the broken copy command on Windoze. +prebuildyacc: $(UTIL)levcomp.lex.cc $(UTIL)levcomp.tab.cc $(UTIL)levcomp.tab.h + $(subst /,\,for %%f in ($^) do $(COPY) %%f prebuilt) + +$(UTIL)levcomp.tab.cc: $(UTIL)levcomp.ypp + $(subst /,\, cd $(UTIL)) && $(YACC) -d -b levcomp levcomp.ypp + $(subst /,\, cd $(UTIL)) && move $(YTABC) levcomp.tab.cc + +$(UTIL)levcomp.lex.cc: $(UTIL)levcomp.lpp + $(subst /,\, cd $(UTIL) && $(LEX) -olevcomp.lex.cc levcomp.lpp) + +else + +$(UTIL)levcomp.tab.cc: prebuilt/levcomp.tab.cc + $(subst /,\,$(COPY) prebuilt/*.h $(UTIL)) + $(subst /,\,$(COPY) $< $@) + +$(UTIL)levcomp.lex.cc: prebuilt/levcomp.lex.cc + $(subst /,\,$(COPY) $< $@) + +endif + +########################################################################## + install: $(APPNAME) +ifneq ($(OPATH),$(INSTALLDIR)) $(COPY) $(APPNAME) ${INSTALLDIR} +endif + mkdir $(INSTALLDIR)\dat 2>nul || echo "" >nul + copy dat\*.des $(INSTALLDIR)\dat clean: - $(DELETE) *.o + $(DELETE) $(OPATH)\*.o + $(subst /,\,$(DELETE) $(UTIL)*.o) + $(subst /,\,$(DELETE) $(UTIL)*.exe) + $(subst /,\,$(DELETE) $(UTIL)*.lex.cc) + $(subst /,\,$(DELETE) $(UTIL)*.tab.cc) + $(subst /,\,$(DELETE) $(UTIL)*.tab.h) + $(subst /,\,$(DELETE) $(UTIL)*.tab.c) + $(subst /,\,$(DELETE) *.ixx) distclean: - $(DELETE) *.o + $(DELETE) $(OPATH)\*.o + $(DELETE) *.o $(DELETE) bones.* + $(DELETE) $(OPATH)\bones.* $(DELETE) morgue.txt + $(DELETE) $(OPATH)\morgue.txt $(DELETE) scores - $(DELETE) $(APPNAME) + $(DELETE) $(OPATH)\scores + $(DELETE) crawl.exe + $(DELETE) $(subst /,\,$(APPNAME)) $(DELETE) *.sav + $(DELETE) $(OPATH)\*.sav $(DELETE) core + $(DELETE) $(OPATH)\core $(DELETE) *.0* + $(DELETE) $(OPATH)\*.0* $(DELETE) *.lab + $(DELETE) $(OPATH)\*.lab -$(APPNAME): $(OBJECTS) libw32c.o - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) libw32c.o -o $(APPNAME) $(LIB) +$(APPNAME): $(GAME_DEPENDS) + ${CXX} ${LDFLAGS} $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) strip $(APPNAME) -debug: $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) +debug: $(GAME_DEPENDS) + ${CXX} ${LDFLAGS} $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) + +profile: $(GAME_DEPENDS) + ${CXX} -g -p ${LDFLAGS} $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) -profile: $(OBJECTS) - ${CXX} -g -p ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) +$(OPATH)/%.o: %.cc + ${CXX} ${CFLAGS} -o $@ -c $< -.cc.o: - ${CXX} ${CFLAGS} -c $< ${INCLUDE} +$(OPATH)/%.o: $(UTIL)%.cc + $(CXX) $(YCFLAGS) -o $@ -c $< diff --git a/crawl-ref/source/makefile.obj b/crawl-ref/source/makefile.obj index e15334a033..2b811f1c56 100644 --- a/crawl-ref/source/makefile.obj +++ b/crawl-ref/source/makefile.obj @@ -1,3 +1,5 @@ +# Modified for Crawl Reference by $Author$ on $Date$ + OBJECTS = \ abl-show.o \ abyss.o \ @@ -24,10 +26,12 @@ it_use2.o \ it_use3.o \ item_use.o \ itemname.o \ +itemprop.o \ items.o \ lev-pand.o \ libutil.o \ macro.o \ +mapdef.o \ maps.o \ menu.o \ message.o \ @@ -40,6 +44,7 @@ mon-util.o \ mstuff2.o \ mutation.o \ newgame.o \ +notes.o \ ouch.o \ output.o \ overmap.o \ @@ -62,7 +67,6 @@ tags.o \ transfor.o \ travel.o \ view.o \ -wpn-misc.o \ Kills.o \ mt19937ar.o \ clua.o diff --git a/crawl-ref/source/makefile.osx b/crawl-ref/source/makefile.osx index 7cee7c3452..69c78b127e 100644 --- a/crawl-ref/source/makefile.osx +++ b/crawl-ref/source/makefile.osx @@ -4,58 +4,41 @@ # Modified for Crawl Reference by $Author$ on $Date$ # -GAME = crawl - -# this file contains a list of the libraries. -# it will make a variable called OBJECTS that contains all the libraries -include makefile.obj - -OBJECTS += libunix.o - -CXX = g++ -DELETE = rm -f -COPY = cp -OS_TYPE = OSX - -CFLAGS = -Wall -Werror -D$(OS_TYPE) $(EXTRA_FLAGS) - -MCHMOD = 711 -INSTALLDIR = /tmp/CRAWLTEST/testdev -LIB = -lncurses -lstdc++ +PROJECT = Crawl.xcodeproj +GAME = Crawl all: $(GAME) -install: $(GAME) - $(COPY) $(GAME) ${INSTALLDIR} - ${MCHMOD} ${INSTALLDIR}/$(GAME) +# Xcode handles dependencies, so install does _not_ have to depend on building. +install: + xcodebuild -project $(PROJECT) -target $(GAME) -configuration Release install clean: - $(DELETE) *.o - -distclean: - $(DELETE) *.o - $(DELETE) bones.* - $(DELETE) morgue.txt - $(DELETE) scores - $(DELETE) $(GAME) - $(DELETE) *.sav - $(DELETE) core - $(DELETE) *.0* - $(DELETE) *.lab - - -$(GAME): $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) - -debug: $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) - -profile: $(OBJECTS) - ${CXX} -g -p ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) - -.cc.o: - ${CXX} ${CFLAGS} $(INCLUDES) -c $< ${INCLUDE} - -.h.cc: - touch $@ - + xcodebuild -project $(PROJECT) -target $(GAME) -configuration Release clean + xcodebuild -project $(PROJECT) -target $(GAME) -configuration Debug clean + xcodebuild -project $(PROJECT) -target $(GAME) -configuration Wizard clean + +distclean: clean + rm -f bones.* + rm -f morgue.txt + rm -f scores + rm -f $(GAME) + rm -f *.sav + rm -f core + rm -f *.0* + rm -f *.lab + +$(GAME): + xcodebuild -project $(PROJECT) -target $(GAME) -configuration Release build + +debug: + xcodebuild -project $(PROJECT) -target $(GAME) -configuration Debug build + +wizard: + xcodebuild -project $(PROJECT) -target $(GAME) -configuration Wizard build + +devel: + xcodebuild -project $(PROJECT) -target $(GAME) -configuration Development build + +devclean: + xcodebuild -project $(PROJECT) -target $(GAME) -configuration Development clean diff --git a/crawl-ref/source/makefile.sgi b/crawl-ref/source/makefile.sgi deleted file mode 100644 index 67815feaae..0000000000 --- a/crawl-ref/source/makefile.sgi +++ /dev/null @@ -1,54 +0,0 @@ -# Make file for Dungeon Crawl (irix) - -APPNAME = crawl - -# this file contains a list of the libraries. -# it will make a variable called OBJECTS that contains all the libraries -include makefile.obj - -CXX = CC -DELETE = rm -f -COPY = cp -OS_TYPE = LINUX -CFLAGS = -Wall -D$(OS_TYPE) $(EXTRA_FLAGS) -LDFLAGS = -L/usr/lib -MCHMOD = 711 -INSTALLDIR = /usr/games -LIB = -lncurses - -# Include for Linux -INCLUDES = -I/usr/include/ncurses - -all: $(APPNAME) - -%.o: %.cc - @echo hello - @${CXX} ${CFLAGS} -c $< ${INCLUDE} - -install: $(APPNAME) - $(COPY) $(APPNAME) ${INSTALLDIR} - chmod ${MCHMOD} ${INSTALLDIR}/$(APPNAME) - -clean: - $(DELETE) *.o - -distclean: - $(DELETE) *.o - $(DELETE) bones.* - $(DELETE) morgue.txt - $(DELETE) scores - $(DELETE) $(APPNAME) - -$(APPNAME): $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) - strip $(APPNAME) - chmod ${MCHMOD} $(APPNAME) - -debug: $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) - -profile: $(OBJECTS) - ${CXX} -g -p ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) - -.h.cc: - touch $@ diff --git a/crawl-ref/source/makefile.sol b/crawl-ref/source/makefile.sol deleted file mode 100644 index e79d7e3a53..0000000000 --- a/crawl-ref/source/makefile.sol +++ /dev/null @@ -1,68 +0,0 @@ -# Make file for Dungeon Crawl (solaris) - -# -# Modified for Crawl Reference by $Author$ on $Date$ -# - -APPNAME = crawl - -# this file contains a list of the libraries. -# it will make a variable called OBJECTS that contains all the libraries -include makefile.obj - -OBJECTS += libunix.o - -CXX = g++ -DELETE = rm -f -COPY = cp -GROUP = games -MOVE = mv -OS_TYPE = SOLARIS - -CFLAGS = -Wall -Wwrite-strings -Wstrict-prototypes \ - -Wmissing-prototypes -Wmissing-declarations \ - -g -D$(OS_TYPE) $(EXTRA_FLAGS) - -LDFLAGS = -static -MCHMOD = 2755 -INSTALLDIR = /opt/local/newcrawl/bin -LIB = -lcurses - -all: $(APPNAME) - -install: $(APPNAME) - #strip $(APPNAME) - $(MOVE) ${INSTALLDIR}/${APPNAME} ${INSTALLDIR}/${APPNAME}.old - $(COPY) $(APPNAME) ${INSTALLDIR} - chgrp ${GROUP} ${INSTALLDIR}/${APPNAME} - chmod ${MCHMOD} ${INSTALLDIR}/$(APPNAME) - -clean: - $(DELETE) *.o - -distclean: - $(DELETE) *.o - $(DELETE) bones.* - $(DELETE) morgue.txt - $(DELETE) scores - $(DELETE) $(APPNAME) - $(DELETE) *.sav - $(DELETE) core - $(DELETE) *.0* - $(DELETE) *.lab - - -$(APPNAME): $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) - -debug: $(OBJECTS) - ${CXX} ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) - -profile: $(OBJECTS) - ${CXX} -g -p ${LDFLAGS} $(INCLUDES) $(CFLAGS) $(OBJECTS) -o $(APPNAME) $(LIB) - -.cc.o: - ${CXX} ${CFLAGS} -c $< ${INCLUDE} - -.h.cc: - touch $@ diff --git a/crawl-ref/source/makefile.unix b/crawl-ref/source/makefile.unix new file mode 100644 index 0000000000..c656630acb --- /dev/null +++ b/crawl-ref/source/makefile.unix @@ -0,0 +1,198 @@ +# -*- Makefile -*- for Dungeon Crawl (linux) + +# +# Modified for Crawl Reference by $Author$ on $Date$ +# + +GAME = crawl + +# this file contains a list of the libraries. +# it will make a variable called OBJECTS that contains all the libraries +include makefile.obj + +OBJECTS += libunix.o + +CXX = g++ +DELETE = rm -f +COPY = cp +OS_TYPE = UNIX + +# Include path for curses or ncurses. +INCLUDES = -I/usr/include/ncurses + +MCHMOD = 2755 +INSTALLDIR := /usr/games + +# If you're installing Crawl for multiple users, you *must* set this to a +# valid path before building Crawl. +DATADIR := + +LIB = -lncurses + +INCLUDES := $(INCLUDES) -Iutil -I. + +CFWARN := -Wall -Wwrite-strings \ + -Wshadow -pedantic + +CFOTHERS := -fsigned-char -g -D$(OS_TYPE) $(EXTRA_FLAGS) + +ifneq ($(DATADIR),) +CFOTHERS += '-DDATA_DIR_PATH="$(DATADIR)"' +endif + +CFLAGS := $(INCLUDES) $(CFWARN) $(CFOTHERS) +YCFLAGS := $(INCLUDES) $(CFOTHERS) + + +# If you have lex and yacc, set DOYACC to y (lowercase y). +DOYACC := y + +UTIL = util/ + +LEX := lex +YACC := bison -y + +YTABC := levcomp.tab.c +YTABH := levcomp.tab.h + +OBJECTS := $(UTIL)levcomp.tab.o $(UTIL)levcomp.lex.o $(UTIL)levcomp.o \ + $(OBJECTS) + +ifeq ($(LEX),) +DOYACC := +endif + +ifeq ($(YACC),) +DOYACC := +endif + +GAME_DEPENDS := $(OBJECTS) +SRC_PKG_BASE := stone_soup +SRC_VERSION := $(shell egrep 'VERSION ".*"' version.h | \ + egrep -o '[0-9]\.[0-9](\.[0-9])?') +PKG_SRC_DIR := $(SRC_PKG_BASE)-$(SRC_VERSION)-src +SRC_PKG_TAR := $(PKG_SRC_DIR).tbz2 +SRC_PKG_ZIP := $(PKG_SRC_DIR).zip + +PKG_TIDY_LIST := $(UTIL)*.o $(LEVCOMP) *.o \ + $(UTIL)*.tab.cc $(UTIL)*.tab.h $(UTIL)*.lex.cc *.ixx +PKG_EXCLUDES := $(PWD)/misc/src-pkg-excludes.lst + +########################################################################## + +all: $(GAME) + +########################################################################## +# The level compiler +# + +ifeq ($(DOYACC),y) + +prebuildyacc: $(UTIL)levcomp.tab.cc $(UTIL)levcomp.tab.h $(UTIL)levcomp.lex.cc + cp $^ prebuilt/ + +$(UTIL)levcomp.tab.cc: $(UTIL)levcomp.ypp + cd $(UTIL) && $(YACC) -d -b levcomp levcomp.ypp \ + && mv $(YTABC) levcomp.tab.cc + +$(UTIL)levcomp.lex.cc: $(UTIL)levcomp.lpp + cd $(UTIL) && $(LEX) -olevcomp.lex.cc levcomp.lpp + +else + +# Pull the level-compiler stuff up from prebuilt/ + +$(UTIL)levcomp.tab.cc: prebuilt/levcomp.tab.cc + cp prebuilt/*.h $(UTIL) + cp $< $@ + + +$(UTIL)levcomp.lex.cc: prebuilt/levcomp.lex.cc + cp $< $@ + +endif + +########################################################################## + + +########################################################################## +# The actual build targets +# + +install: $(GAME) + $(COPY) $(GAME) ${INSTALLDIR} + chmod ${MCHMOD} ${INSTALLDIR}/$(GAME) +ifeq ($(DATADIR),) + $(error DATADIR not set! Set DATADIR and run make clean install again) +endif + mkdir -p $(DATADIR)/data + cp dat/*.des $(DATADIR)/data + +clean: + $(DELETE) *.o + $(DELETE) $(UTIL)*.o + $(DELETE) $(LEVCOMP) + $(DELETE) $(UTIL)*.tab.cc $(UTIL)*.tab.c $(UTIL)*.tab.h $(UTIL)*.lex.cc + $(DELETE) *.ixx + +distclean: clean + $(DELETE) bones.* + $(DELETE) morgue.txt + $(DELETE) scores + $(DELETE) $(GAME) + $(DELETE) *.sav + $(DELETE) core + $(DELETE) *.0* + $(DELETE) *.lab + +$(GAME): $(GAME_DEPENDS) + ${CXX} ${LDFLAGS} $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) + chmod ${MCHMOD} $(GAME) + +debug: $(GAME_DEPENDS) + ${CXX} ${LDFLAGS} $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) + +profile: $(GAME_DEPENDS) + ${CXX} -g -p ${LDFLAGS} $(CFLAGS) $(OBJECTS) -o $(GAME) $(LIB) + +.cc.o: + ${CXX} ${CFLAGS} -c $< + +# [ds] Note we don't use the standard CFLAGS here; that's intentional, most +# flex/bison combos I've tried don't produce code that passes the warnings +# test. +$(UTIL)%.o: $(UTIL)%.cc + $(CXX) $(YCFLAGS) -o $@ -c $< + +.h.cc: + touch $@ + +############################################################################# +# Packaging a source tarball for release +# + +# To package, you *must* have lex and yacc to generate the intermediates. +ifeq ($(DOYACC),y) +package-source: distclean prebuildyacc pkgtidy removeold vlink pkgtarbz2 pkgzip + +pkgtidy: + $(DELETE) $(PKG_TIDY_LIST) + +removeold: + if [ -f ../../$(SRC_PKG_TAR) ]; then $(DELETE) ../../$(SRC_PKG_TAR); fi + if [ -f ../../$(SRC_PKG_ZIP) ]; then $(DELETE) ../../$(SRC_PKG_ZIP); fi + +# [ds] Existing directory names could produce a bad package! +vlink: + cd .. && WHERE=$$PWD && cd .. && \ + ( [ -e $(PKG_SRC_DIR) ] || ln -sf $$WHERE $(PKG_SRC_DIR) ) + +pkgtarbz2: + cd ../.. && tar -ch --bzip2 -f $(SRC_PKG_TAR) \ + -X $(PKG_EXCLUDES) $(PKG_SRC_DIR) + +pkgzip: + cd ../.. && zip -rq $(SRC_PKG_ZIP) $(PKG_SRC_DIR) \ + -x@$(PKG_EXCLUDES) + +endif diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc new file mode 100644 index 0000000000..84e48329d6 --- /dev/null +++ b/crawl-ref/source/mapdef.cc @@ -0,0 +1,412 @@ +#include +#include +#include + +#include "AppHdr.h" +#include "libutil.h" +#include "mapdef.h" +#include "mon-util.h" +#include "stuff.h" + +/////////////////////////////////////////////// +// level_range +// + +level_range::level_range(int s, int d) + : shallowest(), deepest() +{ + set(s, d); +} + +void level_range::set(int s, int d) +{ + shallowest = s; + deepest = d; + if (deepest == -1) + deepest = shallowest; +} + +void level_range::reset() +{ + deepest = shallowest = -1; +} + +bool level_range::contains(int x) const +{ + // [ds] The level ranges used by the game are zero-based, adjust for that. + ++x; + return (x >= shallowest && x <= deepest); +} + +bool level_range::valid() const +{ + return (shallowest > 0 && deepest >= shallowest); +} + +int level_range::span() const +{ + return (deepest - shallowest); +} + +/////////////////////////////////////////////// +// map_lines +// + +map_lines::map_lines() : lines(), map_width(0) +{ +} + +map_lines::map_lines(int nlines, ...) : lines(), map_width(0) +{ + va_list args; + va_start(args, nlines); + + for (int i = 0; i < nlines; ++i) + add_line( va_arg(args, const char *) ); + + va_end(args); +} + +const std::vector &map_lines::get_lines() const +{ + return (lines); +} + +void map_lines::add_line(const std::string &s) +{ + lines.push_back(s); + if ((int) s.length() > map_width) + map_width = s.length(); +} + +int map_lines::width() const +{ + return map_width; +} + +int map_lines::height() const +{ + return lines.size(); +} + +void map_lines::clear() +{ + lines.clear(); + map_width = 0; +} + +void map_lines::resolve(std::string &s, const std::string &fill) +{ + std::string::size_type pos; + while ((pos = s.find('?')) != std::string::npos) + s[pos] = fill[ random2(fill.length()) ]; +} + +void map_lines::resolve(const std::string &fillins) +{ + if (fillins.empty() || fillins.find('?') != std::string::npos) + return; + + for (int i = 0, size = lines.size(); i < size; ++i) + resolve(lines[i], fillins); +} + +void map_lines::normalise(char fillch) +{ + for (int i = 0, size = lines.size(); i < size; ++i) + { + std::string &s = lines[i]; + if ((int) s.length() < map_width) + s += std::string( map_width - s.length(), fillch ); + } +} + +// Should never be attempted if the map has a defined orientation, or if one +// of the dimensions is greater than the lesser of GXM,GYM. +void map_lines::rotate(bool clockwise) +{ + std::vector newlines; + + // normalise() first for convenience. + normalise(); + + const int xs = clockwise? 0 : map_width - 1, + xe = clockwise? map_width : -1, + xi = clockwise? 1 : -1; + + const int ys = clockwise? (int) lines.size() - 1 : 0, + ye = clockwise? -1 : (int) lines.size(), + yi = clockwise? -1 : 1; + + for (int i = xs; i != xe; i += xi) + { + std::string line; + + for (int j = ys; j != ye; j += yi) + line += lines[j][i]; + + newlines.push_back(line); + } + + map_width = lines.size(); + lines = newlines; +} + +void map_lines::vmirror() +{ + const int size = lines.size(); + const int midpoint = size / 2; + + for (int i = 0; i < midpoint; ++i) + { + std::string temp = lines[i]; + lines[i] = lines[size - 1 - i]; + lines[size - 1 - i] = temp; + } +} + +void map_lines::hmirror() +{ + const int midpoint = map_width / 2; + for (int i = 0, size = lines.size(); i < size; ++i) + { + std::string &s = lines[i]; + for (int j = 0; j < midpoint; ++j) + { + int c = s[j]; + s[j] = s[map_width - 1 - j]; + s[map_width - 1 - j] = c; + } + } +} + +/////////////////////////////////////////////// +// map_def +// + +void map_def::init() +{ + name.clear(); + tags.clear(); + place.clear(); + depth.reset(); + orient = MAP_NONE; + + // Base chance; this is not a percentage. + chance = 10; + + // The map designer must explicitly disallow these if unwanted. + flags = MAPF_MIRROR_VERTICAL | MAPF_MIRROR_HORIZONTAL + | MAPF_ROTATE; + + random_symbols.clear(); + + map.clear(); + mons.clear(); +} + +bool map_def::is_minivault() const +{ + return (orient == MAP_NONE); +} + +void map_def::hmirror() +{ + if (!(flags & MAPF_MIRROR_HORIZONTAL)) + return; + +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Mirroring %s horizontally.", name.c_str()); +#endif + map.hmirror(); + + switch (orient) + { + case MAP_EAST: orient = MAP_WEST; break; + case MAP_NORTHEAST: orient = MAP_NORTHWEST; break; + case MAP_SOUTHEAST: orient = MAP_SOUTHWEST; break; + case MAP_WEST: orient = MAP_EAST; break; + case MAP_NORTHWEST: orient = MAP_NORTHEAST; break; + case MAP_SOUTHWEST: orient = MAP_SOUTHEAST; break; + default: break; + } +} + +void map_def::vmirror() +{ + if (!(flags & MAPF_MIRROR_VERTICAL)) + return; + +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Mirroring %s vertically.", name.c_str()); +#endif + map.vmirror(); + + switch (orient) + { + case MAP_NORTH: orient = MAP_SOUTH; break; + case MAP_NORTHEAST: orient = MAP_SOUTHEAST; break; + case MAP_NORTHWEST: orient = MAP_SOUTHWEST; break; + + case MAP_SOUTH: orient = MAP_NORTH; break; + case MAP_SOUTHEAST: orient = MAP_NORTHEAST; break; + case MAP_SOUTHWEST: orient = MAP_NORTHWEST; break; + default: break; + } +} + +void map_def::rotate(bool clock) +{ + if (!(flags & MAPF_ROTATE)) + return; + +#define GMINM ((GXM) < (GYM)? (GXM) : (GYM)) + // Make sure the largest dimension fits in the smaller map bound. + if (map.width() <= GMINM && map.height() <= GMINM) + { +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Rotating %s %sclockwise.", + name.c_str(), + !clock? "anti-" : ""); +#endif + map.rotate(clock); + + // Orientation shifts for clockwise rotation: + const map_section_type clockrotate_orients[][2] = { + { MAP_NORTH, MAP_EAST }, + { MAP_NORTHEAST, MAP_SOUTHEAST }, + { MAP_EAST, MAP_SOUTH }, + { MAP_SOUTHEAST, MAP_SOUTHWEST }, + { MAP_SOUTH, MAP_WEST }, + { MAP_SOUTHWEST, MAP_NORTHWEST }, + { MAP_WEST, MAP_NORTH }, + { MAP_NORTHWEST, MAP_NORTHEAST }, + }; + const int nrots = sizeof(clockrotate_orients) + / sizeof(*clockrotate_orients); + + const int refindex = !clock; + for (int i = 0; i < nrots; ++i) + if (orient == clockrotate_orients[i][refindex]) + { + orient = clockrotate_orients[i][!refindex]; + break; + } + } +} + +void map_def::normalise() +{ + // Minivaults are padded out with floor tiles, normal maps are + // padded out with rock walls. + map.normalise(is_minivault()? '.' : 'x'); +} + +void map_def::resolve() +{ + map.resolve( random_symbols ); +} + +void map_def::fixup() +{ + normalise(); + resolve(); +} + +bool map_def::has_tag(const std::string &tagwanted) const +{ + return !tags.empty() && !tagwanted.empty() + && tags.find(" " + tagwanted + " ") != std::string::npos; +} + +/////////////////////////////////////////////////////////////////// +// mons_list +// + +mons_list::mons_list(int nids, ...) : mons_ids() +{ + va_list args; + va_start(args, nids); + + for (int i = 0; i < nids; ++i) + mons_ids.push_back( va_arg(args, int) ); + + va_end(args); +} + +mons_list::mons_list() : mons_ids() +{ +} + +const std::vector &mons_list::get_ids() const +{ + return (mons_ids); +} + +void mons_list::clear() +{ + mons_ids.clear(); +} + +bool mons_list::add_mons(const std::string &s) +{ + const int mid = mons_by_name(s); + if (mid != MONS_PROGRAM_BUG) + mons_ids.push_back(mid); + return (mid != MONS_PROGRAM_BUG); +} + +int mons_list::mons_by_name(std::string name) const +{ + lowercase(name); + + name = replace_all( name, "_", " " ); + + // Special casery: + if (name == "pandemonium demon") + return (MONS_PANDEMONIUM_DEMON); + + if (name == "random" || name == "random monster") + return (RANDOM_MONSTER); + + if (name == "any demon" || name == "demon" || name == "random demon") + return (-100 - DEMON_RANDOM); + + if (name == "any lesser demon" + || name == "lesser demon" || name == "random lesser demon") + return (-100 - DEMON_LESSER); + + if (name == "any common demon" + || name == "common demon" || name == "random common demon") + return (-100 - DEMON_COMMON); + + if (name == "any greater demon" + || name == "greater demon" || name == "random greater demon") + return (-100 - DEMON_GREATER); + + if (name == "small zombie") + return (MONS_ZOMBIE_SMALL); + if (name == "large zombie") + return (MONS_ZOMBIE_LARGE); + + if (name == "small skeleton") + return (MONS_SKELETON_SMALL); + if (name == "large skeleton") + return (MONS_SKELETON_LARGE); + + if (name == "spectral thing") + return (MONS_SPECTRAL_THING); + + if (name == "small simulacrum") + return (MONS_SIMULACRUM_SMALL); + if (name == "large simulacrum") + return (MONS_SIMULACRUM_LARGE); + + if (name == "small abomination") + return (MONS_ABOMINATION_SMALL); + + if (name == "large abomination") + return (MONS_ABOMINATION_LARGE); + + return (get_monster_by_name(name, true)); +} diff --git a/crawl-ref/source/mapdef.h b/crawl-ref/source/mapdef.h new file mode 100644 index 0000000000..9fc686ea94 --- /dev/null +++ b/crawl-ref/source/mapdef.h @@ -0,0 +1,167 @@ +/* + * mapdef.h: + * Header for map structures used by the level compiler. + * + * NOTE: When we refer to map, this could be a full map, filling an entire + * level or a minivault that occupies just a portion of the level. + */ + +#ifndef __MAPDEF_H__ +#define __MAPDEF_H__ + +#include +#include + +#include "enum.h" + +enum map_flags { + MAPF_PANDEMONIUM_VAULT = 0x01, // A pandemonium minivault. + + MAPF_MIRROR_VERTICAL = 0x10, // The map may be mirrored vertically + MAPF_MIRROR_HORIZONTAL = 0x20, // may be mirrored horizontally. + MAPF_ROTATE = 0x40 // may be rotated +}; + +class level_range { +public: + int shallowest, deepest; + +public: + level_range(int s = -1, int d = -1); + + void set(int s, int d = -1); + void reset(); + bool contains(int depth) const; + + bool valid() const; + int span() const; +}; + +class map_lines { +public: + map_lines(); + + // NULL-terminated list of map lines. + map_lines(int nlines, ...); + + void add_line(const std::string &s); + void set_orientation(const std::string &s); + + int width() const; + int height() const; + + void resolve(const std::string &fillins); + + // Make all lines the same length. + void normalise(char fillc = 'x'); + + // Rotate 90 degrees either clockwise or anticlockwise + void rotate(bool clockwise); + void hmirror(); + void vmirror(); + + void clear(); + + const std::vector &get_lines() const; + +private: + void resolve(std::string &s, const std::string &fill); + +private: + std::vector lines; + int map_width; +}; + +class mons_list { +public: + mons_list(int nids, ...); + mons_list(); + + void clear(); + const std::vector &get_ids() const; + + // Returns false if the monster is unrecognised. + bool add_mons(const std::string &s); + +private: + int mons_by_name(std::string name) const; + +private: + std::vector mons_ids; +}; + +// Not providing a constructor to make life easy for C-style initialisation. +class map_def { +public: + std::string name; + std::string tags; + std::string place; + level_range depth; + map_section_type orient; + int chance; + long flags; + + map_lines map; + mons_list mons; + + std::string random_symbols; + +public: + void init(); + + void hmirror(); + void vmirror(); + void rotate(bool clockwise); + void normalise(); + void resolve(); + void fixup(); + + bool is_minivault() const; + bool has_tag(const std::string &tag) const; +}; + +class monster_chance { +public: + int mclass; + int level; + int rarity; +}; + +class level_def { +public: + // The range of levels to which this def applies. + level_range range; + + // Can be empty, in which case the default colours are applied. + std::string floor_colour, rock_colour; + + std::string tags; + + // The probability of requesting a random vault. + int p_vault; + + // The probability of requesting a random minivault. + int p_minivault; + + // If non-empty, any upstair will go straight to this level. + std::string upstair_targ, downstair_targ; + + std::vector monsters; +}; + +class dungeon_def { +public: + std::string idstr; + int id; + std::string short_desc, full_desc; + + std::vector level_specs; + +public: + const level_def &specs(int subdepth); +}; + +std::string escape_string(std::string in, const std::string &toesc, + const std::string &escapewith); + +#endif diff --git a/crawl-ref/source/maps.cc b/crawl-ref/source/maps.cc index 3e99b298e1..68d851c596 100644 --- a/crawl-ref/source/maps.cc +++ b/crawl-ref/source/maps.cc @@ -3,6 +3,8 @@ * Summary: Functions used to create vaults. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 5/20/99 BWR Added stone lining to Zot vault, @@ -13,112 +15,37 @@ #include "AppHdr.h" #include "maps.h" -#include -#include +#include +#include +#include +#include "dungeon.h" #include "enum.h" +#include "files.h" #include "monplace.h" +#include "mapdef.h" #include "stuff.h" +#include "levcomp.h" -static char vault_1(char vgrid[81][81], FixedVector& mons_array); -static char vault_2(char vgrid[81][81], FixedVector& mons_array); -static char vault_3(char vgrid[81][81], FixedVector& mons_array); -static char vault_4(char vgrid[81][81], FixedVector& mons_array); -static char vault_5(char vgrid[81][81], FixedVector& mons_array); -static char vault_6(char vgrid[81][81], FixedVector& mons_array); -static char vault_7(char vgrid[81][81], FixedVector& mons_array); -static char vault_8(char vgrid[81][81], FixedVector& mons_array); -static char vault_9(char vgrid[81][81], FixedVector& mons_array); -static char vault_10(char vgrid[81][81], FixedVector& mons_array); - - -static char antaeus(char vgrid[81][81], FixedVector& mons_array); -static char asmodeus(char vgrid[81][81], FixedVector& mons_array); -static char beehive(char vgrid[81][81], FixedVector& mons_array); -static char box_level(char vgrid[81][81], FixedVector& mons_array); -static char castle_dis(char vgrid[81][81], FixedVector& mons_array); -static char elf_hall(char vgrid[81][81], FixedVector& mons_array); -static char ereshkigal(char vgrid[81][81], FixedVector& mons_array); -static char farm_and_country(char vgrid[81][81], FixedVector& mons_array); -static char fort_yaktaur(char vgrid[81][81], FixedVector& mons_array); -static char hall_of_Zot(char vgrid[81][81], FixedVector& mons_array); -static char hall_of_blades(char vgrid[81][81], FixedVector& mons_array); -static char gloorx_vloq(char vgrid[81][81], FixedVector& mons_array); -//static char mollusc(char vgrid[81][81], FixedVector& mons_array); -static char my_map(char vgrid[81][81], FixedVector& mons_array); -static char mnoleg(char vgrid[81][81], FixedVector& mons_array); -static char cerebov(char vgrid[81][81], FixedVector& mons_array); -static char orc_temple(char vgrid[81][81], FixedVector& mons_array); -static char lom_lobon(char vgrid[81][81], FixedVector& mons_array); -static char slime_pit(char vgrid[81][81], FixedVector& mons_array); -static char snake_pit(char vgrid[81][81], FixedVector& mons_array); -static char swamp(char vgrid[81][81], FixedVector& mons_array); -static char temple(char vgrid[81][81], FixedVector& mons_array); -static char tomb_1(char vgrid[81][81], FixedVector& mons_array); -static char tomb_2(char vgrid[81][81], FixedVector& mons_array); -static char tomb_3(char vgrid[81][81], FixedVector& mons_array); -static char vaults_vault(char vgrid[81][81], FixedVector& mons_array); -static char vestibule_map(char vgrid[81][81], FixedVector& mons_array); - - -static char minivault_1(char vgrid[81][81], FixedVector& mons_array); -static char minivault_2(char vgrid[81][81], FixedVector& mons_array); -static char minivault_3(char vgrid[81][81], FixedVector& mons_array); -static char minivault_4(char vgrid[81][81], FixedVector& mons_array); -static char minivault_5(char vgrid[81][81], FixedVector& mons_array); -static char minivault_6(char vgrid[81][81], FixedVector& mons_array); -static char minivault_7(char vgrid[81][81], FixedVector& mons_array); -static char minivault_8(char vgrid[81][81], FixedVector& mons_array); -static char minivault_9(char vgrid[81][81], FixedVector& mons_array); -static char minivault_10(char vgrid[81][81], FixedVector& mons_array); -static char minivault_11(char vgrid[81][81], FixedVector& mons_array); -static char minivault_12(char vgrid[81][81], FixedVector& mons_array); -static char minivault_13(char vgrid[81][81], FixedVector& mons_array); -static char minivault_14(char vgrid[81][81], FixedVector& mons_array); -static char minivault_15(char vgrid[81][81], FixedVector& mons_array); -static char minivault_16(char vgrid[81][81], FixedVector& mons_array); -static char minivault_17(char vgrid[81][81], FixedVector& mons_array); -static char minivault_18(char vgrid[81][81], FixedVector& mons_array); -static char minivault_19(char vgrid[81][81], FixedVector& mons_array); -static char minivault_20(char vgrid[81][81], FixedVector& mons_array); -static char minivault_21(char vgrid[81][81], FixedVector& mons_array); -static char minivault_22(char vgrid[81][81], FixedVector& mons_array); -static char minivault_23(char vgrid[81][81], FixedVector& mons_array); -static char minivault_24(char vgrid[81][81], FixedVector& mons_array); -static char minivault_25(char vgrid[81][81], FixedVector& mons_array); -static char minivault_26(char vgrid[81][81], FixedVector& mons_array); -static char minivault_27(char vgrid[81][81], FixedVector& mons_array); -static char minivault_28(char vgrid[81][81], FixedVector& mons_array); -static char minivault_29(char vgrid[81][81], FixedVector& mons_array); -static char minivault_30(char vgrid[81][81], FixedVector& mons_array); -static char minivault_31(char vgrid[81][81], FixedVector& mons_array); -static char minivault_32(char vgrid[81][81], FixedVector& mons_array); -static char minivault_33(char vgrid[81][81], FixedVector& mons_array); - -//jmf: originals and slim wrappers to fit into don's non-switch -static char minivault_34(char vgrid[81][81], FixedVector& mons_array, bool orientation); -static char minivault_35(char vgrid[81][81], FixedVector& mons_array, bool orientation); -static char minivault_34_a(char vgrid[81][81], FixedVector& mons_array); -static char minivault_34_b(char vgrid[81][81], FixedVector& mons_array); -static char minivault_35_a(char vgrid[81][81], FixedVector& mons_array); -static char minivault_35_b(char vgrid[81][81], FixedVector& mons_array); - -static char rand_demon_1(char vgrid[81][81], FixedVector& mons_array); -static char rand_demon_2(char vgrid[81][81], FixedVector& mons_array); -static char rand_demon_3(char vgrid[81][81], FixedVector& mons_array); -static char rand_demon_4(char vgrid[81][81], FixedVector& mons_array); -static char rand_demon_5(char vgrid[81][81], FixedVector& mons_array); -static char rand_demon_6(char vgrid[81][81], FixedVector& mons_array); -static char rand_demon_7(char vgrid[81][81], FixedVector& mons_array); -static char rand_demon_8(char vgrid[81][81], FixedVector& mons_array); -static char rand_demon_9(char vgrid[81][81], FixedVector& mons_array); +static int write_vault(const map_def &mdef, map_type mt, + FixedVector &marray, + vault_placement &); +static int apply_vault_definition( + const map_def &def, + map_type map, + FixedVector &marray, + vault_placement &); +static void resolve_map(map_def &def); +////////////////////////////////////////////////////////////////////////// +// New style vault definitions -/* ******************** BEGIN PUBLIC FUNCTIONS ******************* */ +static std::vector vdefs; +/* ******************** BEGIN PUBLIC FUNCTIONS ******************* */ // remember (!!!) - if a member of the monster array isn't specified // within a vault subroutine, assume that it holds a random monster @@ -127,3515 +54,282 @@ static char rand_demon_9(char vgrid[81][81], FixedVector& mons_array); // make sure that vault_n, where n is a number, is a vault which can be put // anywhere, while other vault names are for specific level ranges, etc. -char vault_main( char vgrid[81][81], FixedVector& mons_array, int vault_force, int many_many ) -{ - - int which_vault = 0; - unsigned char vx, vy; - char (*fnc_vault) (char[81][81], FixedVector&) = 0; - -// first, fill in entirely with walls and null-terminate {dlb}: - for (vx = 0; vx < 80; vx++) +int vault_main( + map_type vgrid, + FixedVector& mons_array, + vault_placement &place, + int which_vault, + int many_many ) +{ +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Generating level: %s", + vdefs[which_vault].name.c_str()); +#endif + + // first, fill in entirely with walls and null-terminate {dlb}: + for (int vx = 0; vx < MAP_SIDE; vx++) { - for (vy = 0; vy < 80; vy++) + for (int vy = 0; vy < MAP_SIDE; vy++) vgrid[vx][vy] = 'x'; - vgrid[80][vx] = '\0'; - vgrid[vx][80] = '\0'; - } - - // next, select an appropriate vault to place {dlb}: - for (;;) - { - which_vault = ( (vault_force == 100) ? random2(14) : vault_force ); - - // endless loops result if forced vault cannot pass these tests {dlb}: - if ( which_vault == 9 ) - { - // if ( many_many > 23 || many_many > 12 ) - if (many_many > 12) - break; - } - else if (which_vault == 11 || which_vault == 12) - { - if (many_many > 20) - break; - } - else - break; + vgrid[MAP_SIDE][vx] = 0; + vgrid[vx][MAP_SIDE] = 0; } - // then, determine which drawing routine to use {dlb}: - fnc_vault = ( (which_vault == 0) ? vault_1 : - (which_vault == 1) ? vault_2 : - (which_vault == 2) ? vault_3 : - (which_vault == 3) ? vault_4 : - (which_vault == 4) ? vault_5 : - (which_vault == 5) ? vault_6 : - (which_vault == 6) ? vault_7 : - (which_vault == 7) ? vault_8 : - (which_vault == 8) ? vault_9 : - (which_vault == 9) ? ( (many_many > 23) ? my_map : orc_temple ) : - (which_vault == 10) ? vault_10 : - (which_vault == 11) ? farm_and_country : - (which_vault == 12) ? fort_yaktaur : - (which_vault == 13) ? box_level : - - // the hell vaults: - (which_vault == 50) ? vestibule_map : - (which_vault == 51) ? castle_dis : - (which_vault == 52) ? asmodeus : - (which_vault == 53) ? antaeus : - (which_vault == 54) ? ereshkigal : - - // the pandemonium big demonlord vaults: - (which_vault == 60) ? mnoleg : // was nemelex - (which_vault == 61) ? lom_lobon : // was sif muna - (which_vault == 62) ? cerebov : // was okawaru - (which_vault == 63) ? gloorx_vloq : // was kikubaaqudgha - - //(which_vault == 64) ? mollusc : - (which_vault == 80) ? beehive : - (which_vault == 81) ? slime_pit : - (which_vault == 82) ? vaults_vault : - (which_vault == 83) ? hall_of_blades : - (which_vault == 84) ? hall_of_Zot : - (which_vault == 85) ? temple : - (which_vault == 86) ? snake_pit : - (which_vault == 87) ? elf_hall : - (which_vault == 88) ? tomb_1 : - (which_vault == 89) ? tomb_2 : - (which_vault == 90) ? tomb_3 : - (which_vault == 91) ? swamp : - (which_vault == 200) ? minivault_1 : - (which_vault == 201) ? minivault_2 : - (which_vault == 202) ? minivault_3 : - (which_vault == 203) ? minivault_4 : - (which_vault == 204) ? minivault_5 : - (which_vault == 205) ? ( (many_many > 15) ? minivault_6 : minivault_1 ) : - (which_vault == 206) ? ( (many_many > 10) ? minivault_7 : minivault_2 ) : - (which_vault == 207) ? ( (many_many > 15) ? minivault_8 : minivault_3 ) : - (which_vault == 208) ? ( (many_many > 15) ? minivault_9 : minivault_4 ) : - (which_vault == 209) ? minivault_10 : - (which_vault == 210) ? minivault_11 : - (which_vault == 211) ? minivault_12 : - (which_vault == 212) ? minivault_13 : - (which_vault == 213) ? minivault_14 : - (which_vault == 214) ? minivault_15 : - (which_vault == 215) ? minivault_16 : - (which_vault == 216) ? minivault_17 : - (which_vault == 217) ? minivault_18 : - (which_vault == 218) ? minivault_19 : - (which_vault == 219) ? minivault_20 : - (which_vault == 220) ? minivault_21 : - (which_vault == 221) ? minivault_22 : - (which_vault == 222) ? minivault_23 : - (which_vault == 223) ? minivault_24 : - (which_vault == 224) ? minivault_25 : - (which_vault == 225) ? minivault_26 : - (which_vault == 226) ? minivault_27 : - (which_vault == 227) ? minivault_28 : - (which_vault == 228) ? minivault_29 : - (which_vault == 229) ? minivault_30 : - (which_vault == 230) ? minivault_31 : - (which_vault == 231) ? minivault_32 : - (which_vault == 232) ? minivault_33 : - (which_vault == 233) ? minivault_34_a : - (which_vault == 234) ? minivault_34_b : - (which_vault == 235) ? minivault_35_a : - (which_vault == 236) ? minivault_35_b : - (which_vault == 300) ? rand_demon_1 : - (which_vault == 301) ? rand_demon_2 : - (which_vault == 302) ? rand_demon_3 : - (which_vault == 303) ? rand_demon_4 : - (which_vault == 304) ? rand_demon_5 : - (which_vault == 305) ? rand_demon_6 : - (which_vault == 306) ? rand_demon_7 : - (which_vault == 307) ? rand_demon_8 : - (which_vault == 308) ? rand_demon_9 - : 0 ); // yep, NULL -- original behaviour {dlb} - // NB - a return value of zero is not handled well by dungeon.cc (but there it is) 10mar2000 {dlb} - return ( (fnc_vault == 0) ? 0 : fnc_vault(vgrid, mons_array) ); + return write_vault( vdefs[which_vault], vgrid, mons_array, place ); } // end vault_main() -/* ********************* END PUBLIC FUNCTIONS ******************** */ - -/* - key: - x - DNGN_ROCK_WALL - X - DNGN_PERMAROCK_WALL -> should always be undiggable! -- bwr - c - DNGN_STONE_WALL - v - DNGN_METAL_WALL - b - DNGN_GREEN_CRYSTAL_WALL - a - DNGN_WAX_WALL - . - DNGN_FLOOR - + - DNGN_CLOSED_DOOR - = - DNGN_SECRET_DOOR - @ - entry point - must be on outside and on a particular side - see templates - w - water - l - lava - >< - extra stairs - you can leave level by these but will never be placed on them from another level - }{ - stairs 82/86 - You must be able to reach these from each other - )( - stairs 83/87 - ][ - stairs 84/88 - I - orcish idol (does nothing) - ^ - random trap - - A - Vestibule gateway (opened by Horn). Can also be put on other levels for colour, where it won't do anything. - B - Altar. These are assigned specific types (eg of Zin etc) in dungeon.cc, in order. - C - Random Altar. - F - Typically a Granite Statue, but may be Orange or Silver (1 in 100) - G - Granite statue (does nothing) - H - orange crystal statue (attacks mind) - S - Silver statue (summons demons). Avoid using (rare). - T - Water fountain - U - Magic fountain - V - Permenantly dry fountain - - Statues can't be walked over and are only destroyed by disintegration - - $ - gold - % - normal item - * - higher level item (good) - | - acquirement-level item (almost guaranteed excellent) - O - place an appropriate rune here - P - maybe place a rune here (50%) - R - honeycomb (2/3) or royal jelly (1/3) - Z - the Orb of Zot - - 0 - normal monster - 9 - +5 depth monster - 8 - (+2) * 2 depth monster (aargh!). Can get golden dragons and titans this way. - 1-7 - monster array monster - used to allocate specific monsters for a vault. - is filled with RANDOM_MONSTER if monster not specified - - note that a lot of the vaults are in there mainly to add some interest to the - scenery, and are not the lethal treasure-fests you find in Angband - (not that there's anything wrong with that) - - Guidelines for creating new vault maps: - - Basically you can just let your creativity run wild. You do not have - to place all of the stairs unless the level is full screen, in which - case you must place all except the extra stairs (> and <). The <> stairs - can be put anywhere and in any quantities but do not have to be there. Any - of the other stairs which are not present in the vault will be randomly - placed outside it. Also generally try to avoid rooms with no exit. - - You can use the templates below to build vaults, and remember to return the - same number (this number is used in builder2.cc to determine where on the map - the vault is located). The entry point '@' must be present (except - full-screen vaults where it must not) and be on the same side of the vault - as it is on the template, but can be anywhere along that side. - - You'll have to tell me the level range in which you want the vault to appear, - so that I can code it into the vault management function. Unless you've - placed specific monster types this will probably be unnecessary. - - I think that's all. Have fun! - - ps - remember to add one to the monster array value when placing monsters - on each map (it is 1-7, not 0-6) {dlb} - */ - -static char vault_1(char vgrid[81][81], FixedVector& mons_array) -{ // my first vault - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxx....x........x........x.................................xxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxx|=8...x........+........x......x....x1...x2...x2...x3...x...xxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxx|x....x........x........x....................................xxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxx+xxx+xxxxxxxxxxxxxx..................................xxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxx.......x.................+...................................8xxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxx.......x.................x..................................xxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxx.......+........3........xx+xx................................xxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxx.......x.................x...x..x....x1...x2...x2...x3...x...xxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxx.......x.................x...x.............................xxxxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxx+xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxx.........................x.S.x...xxxxxx..................|||||xxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxx....xxxxxxxxxxxxxxxxxx...x...x......xxxxxx..................||xxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxx....x...$$$$x****.999x...x...x.........xxxxxx.................xxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxx....+...$$$$x****....x...x...+............xxxxxx.........8....xxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxx....x...$$$$x****....+...x...x...............xxxxxx...........xxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxx....x...$$$$x****....x...x999x..................xxxxxx........xxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxx....xxxxxxxxxxxxxxxxxx...x...xxx...................xxxxxx.....xxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxx.........................x...xxxxxx...................xxxxxx..xxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxxxxx+xxxxxxxx+xxxxxxx+xxxx...xxxxxx+xxxxxxxx+xxxxxxxx+xxxxxxx=xxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxx....1....x...2...x...3...x...x....3....x....2...x......1......xxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxx.........x.......x.......x...x.........x........x.............xxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_SHAPESHIFTER; - mons_array[1] = MONS_SHAPESHIFTER; - mons_array[2] = MONS_GLOWING_SHAPESHIFTER; - - return MAP_NORTH; -} - - -static char vault_2(char vgrid[81][81], FixedVector& mons_array) -{ // cell vault - UNUSED( mons_array ); - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxcccccccccccccccccccccccccccccccc"); - strcpy(vgrid[7], "xxxxxxxxccw......^......w......^......wc"); - strcpy(vgrid[8], "xxxxxxxxcc.ccccccccccccc.ccccccccccccc.c"); - strcpy(vgrid[9], "xxxxxxxxcc.c....c.c....c.c....c.c....c.c"); - strcpy(vgrid[10], "xxxxxxxxcc.c.8..+.c....c.c....+.c..9.c.c"); - strcpy(vgrid[11], "xxxxxxxxcc.c....c.+..9.c.c.9..c.+....c.c"); - strcpy(vgrid[12], "xxxxxxxxcc.c....c.c....c.c....c.c....c.c"); - strcpy(vgrid[13], "xxxxxxxxcc.cccccc.cccccc.cccccc.cccccc.c"); - strcpy(vgrid[14], "xxxxxxxxcc^c....c.c....c.c....c.c....c.c"); - strcpy(vgrid[15], "xxxxxxxxcc.c....c.c....c.c....+.c....c.c"); - strcpy(vgrid[16], "xxxxxxxxcc.c8...+.+..8.c.c.8..c.+....c.c"); - strcpy(vgrid[17], "xxxxxxxxcc.c....c.c....c.c....c.c....c.c"); - strcpy(vgrid[18], "xxxxxxxxcc.cccccc.cccccc.cccccc.cccccc.c"); - strcpy(vgrid[19], "xxxxxxxxcc.c....c.c....c.c....c.c....c.c"); - strcpy(vgrid[20], "xxxxxxxxcc.c....+.c....c.c.0..c.c....c.c"); - strcpy(vgrid[21], "xxxxxxxxcc.c..9.c.+.8..c^c....+.+.0..c.c"); - strcpy(vgrid[22], "xxxxxxxxcc.c....c.c....c.c....c.c....c.c"); - strcpy(vgrid[23], "xxxxxxxxcc.cccccc.cccccc.cccccc.cccccc.c"); - strcpy(vgrid[24], "xxxxxxxxcc.c....c.c....c.c....c.c....c.c"); - strcpy(vgrid[25], "xxxxxxxxcc.c.0..+.+.0..c.c....+.+....c.c"); - strcpy(vgrid[26], "xxxxxxxxcc.c....c.c....c.c.0..c.c.8..c.c"); - strcpy(vgrid[27], "xxxxxxxxcc.cccccc.c....c.c....c.cccccc.c"); - strcpy(vgrid[28], "xxxxxxxxcc.c....c.cccccc.cccccc.c....c^c"); - strcpy(vgrid[29], "xxxxxxxxcc.c....c.c....c.c..9.+.+....c.c"); - strcpy(vgrid[30], "xxxxxxxxcc.c.0..+.+....c.c9...c.c.0..c.c"); - strcpy(vgrid[31], "xxxxxxxxcc.c....c.c.8..c.c....c.c....c.c"); - strcpy(vgrid[32], "xxxxxxxxcc.cccccc^cccccc.cccccc^cccccc.c"); - strcpy(vgrid[33], "xxxxxxxxccw.......Twwwwc.cwwwwT.......wc"); - strcpy(vgrid[34], "xxxxxxxxcccccccccccccccc.ccccccccccccccc"); - strcpy(vgrid[35], "xxxxxxxxxxxxxxxxxxxxxxxc@cxxxxxxxxxxxxxx"); - - return MAP_NORTHWEST; -} - - -static char vault_3(char vgrid[81][81], FixedVector& mons_array) -{ // little maze vault - UNUSED( mons_array ); - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcat(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[9], "x900x..............x..........xxxxxxxxxx"); - strcat(vgrid[10], "x999x.xxxxxxxxxxxx.x.xxxxxxxx.xxxxxxxxxx"); - strcat(vgrid[11], "x000x.x............x.x......x.xxxxxxxxxx"); - strcat(vgrid[12], "xx.xx.xxxxxxxxxxxxxx.x.xxxx.x.xxxxxxxxxx"); - strcat(vgrid[13], "xx.x..............xx.x.88|x.x.xxxxxxxxxx"); - strcat(vgrid[14], "xx.x.x.xxxxxxxxxx.xx.xxxxxx.x.xxxxxxxxxx"); - strcat(vgrid[15], "xx.x.x.x........x...........x.xxxxxxxxxx"); - strcat(vgrid[16], "xx.x.x.x.xxxxxx.xxxxxxxxxxxxx.xxxxxxxxxx"); - strcat(vgrid[17], "xx.xxx.x.x$$$$x...............xxxxxxxxxx"); - strcat(vgrid[18], "xx.....x.x$$$$x.xxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[19], "xxxxxxxx.x$$$$x...............xxxxxxxxxx"); - strcat(vgrid[20], "x........x$$$$x.xxxxxxxxxxxxx.xxxxxxxxxx"); - strcat(vgrid[21], "x.xxxxxx.xxxx.x.............x.xxxxxxxxxx"); - strcat(vgrid[22], "x.xxxxxx.xxxx.xxxxxxxxxxxxx.x.xxxxxxxxxx"); - strcat(vgrid[23], "x.x.......xxx.x...........x.x.xxxxxxxxxx"); - strcat(vgrid[24], "x.x.xxxxx.....x.x.xxxxx...x.x.xxxxxxxxxx"); - strcat(vgrid[25], "x.x.x999xxxxxxx.x.x***x...x.x.xxxxxxxxxx"); - strcat(vgrid[26], "x.x.x889........x.x|||xxxxx.x.xxxxxxxxxx"); - strcat(vgrid[27], "x.x.x899x.xxxxx.x.x***xxxxx.x.xxxxxxxxxx"); - strcat(vgrid[28], "x.x.xxxxx.xxxxx.x.xx.xxxxxx.x.xxxxxxxxxx"); - strcat(vgrid[29], "x.x..........xx.x.xx........x.xxxxxxxxxx"); - strcat(vgrid[30], "x.xxxxxxx.xx.xx.x.xxxxx.xxxxx.xxxxxxxxxx"); - strcat(vgrid[31], "x.xxx000x.xx.xx.x.x$$$x.xxxxx.xxxxxxxxxx"); - strcat(vgrid[32], "x|||x000x.x$$$x.x.x$$$x%%x%%%.xxxxxxxxxx"); - strcat(vgrid[33], "x|||x000..x$8$x.x.x$$$x%%x%8%xxxxxxxxxxx"); - strcat(vgrid[34], "x|||xxxxxxx$$$x.x..$$$xxxx%%%xxxxxxxxxxx"); - strcat(vgrid[35], "xxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxx"); - - return MAP_NORTHEAST; -} - - -static char vault_4(char vgrid[81][81], FixedVector& mons_array) -{ // thingy vault - UNUSED( mons_array ); - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcpy(vgrid[36], "xxxxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxxxxxxxxxxxxxxxxx^xxxxxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxxxxxxxxxxxxxxxx.....xxxxxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxxxxxxxxxxxxxxxx.....xxxxxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxxxxxxxxxxxxxx.........xxxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxxxxxxxxxx......0...0......xxxxxx"); - strcpy(vgrid[45], "xxxxxxxxxxxxxx.......................xxx"); - strcpy(vgrid[46], "xxxxxxxxxxxxxx.........0...0.........xxx"); - strcpy(vgrid[47], "xxxxxxxxxxxxx8......0.........0......8xx"); - strcpy(vgrid[48], "xxxxxxxxxxxxxx.........0...0.........xxx"); - strcpy(vgrid[49], "xxxxxxxxxxxxxx.......................xxx"); - strcpy(vgrid[50], "xxxxxxxxxxxxxxx........0...0........xxxx"); - strcpy(vgrid[51], "xxxxxxxxxxxxxxxxxxxx...........xxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxxxxxxxxxxxxxxx.....xxxxxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxxxxxxxxxx...............xxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxx8.................8xxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxx.............xxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxx999xxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - return MAP_SOUTHWEST; -} - -static char vault_5(char vgrid[81][81], FixedVector& mons_array) -{ // hourglass vault - UNUSED( mons_array ); - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcat(vgrid[36], "xxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[37], "xxxxxx.................xxxxxxxxxxxxxxxxx"); - strcat(vgrid[38], "xxxxx...................xxxxxxxxxxxxxxxx"); - strcat(vgrid[39], "xxxxx...................xxxxxxxxxxxxxxxx"); - strcat(vgrid[40], "xxxxxx.................xxxxxxxxxxxxxxxxx"); - strcat(vgrid[41], "xxxxxx.................xxxxxxxxxxxxxxxxx"); - strcat(vgrid[42], "xxxxxx.................xxxxxxxxxxxxxxxxx"); - strcat(vgrid[43], "xxxxxxx...............xxxxxxxxxxxxxxxxxx"); - strcat(vgrid[44], "xxxxxxx...............xxxxxxxxxxxxxxxxxx"); - strcat(vgrid[45], "xxxxxxxx.............xxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[46], "xxxxxxxxx.....8.....xxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[47], "xxxxxxxxxx...999...xxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[48], "xxxxxxxxxxxx00000xxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[49], "xxxxxxxxxxxxx===xxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[50], "xxxxxxxxxxxx.....xxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[51], "xxxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[52], "xxxxxxxxx...........xxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[53], "xxxxxxxx......|......xxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[54], "xxxxxxx...............xxxxxxxxxxxxxxxxxx"); - strcat(vgrid[55], "xxxxxxx...............xxxxxxxxxxxxxxxxxx"); - strcat(vgrid[56], "xxxxxx........$........xxxxxxxxxxxxxxxxx"); - strcat(vgrid[57], "xxxxxx.......$$$.......xxxxxxxxxxxxxxxxx"); - strcat(vgrid[58], "xxxxxx....$$$$$$$$$....xxxxxxxxxxxxxxxxx"); - strcat(vgrid[59], "xxxxx$$$$$$$$$$$$$$$$$$$xxxxxxxxxxxxxxxx"); - strcat(vgrid[60], "xxxxx$$$$$$$$$$$$$$$$$$$xxxxxxxxxxxxxxxx"); - strcat(vgrid[61], "xxxxxx$$$$$$$$$$$$$$$$$xxxxxxxxxxxxxxxxx"); - strcat(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - return MAP_SOUTHEAST; -} - - -static char vault_6(char vgrid[81][81], FixedVector& mons_array) -{ // a more Angbandy vault - UNUSED( mons_array ); - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcat(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[6], "ccccccccccccccccccccccccccccccccxxxxxxxx"); - strcat(vgrid[7], "c*******cc..9...cc.+8c0c*c.c*c8cxxxxxxxx"); - strcat(vgrid[8], "c******cc..cc..cc..cc0c.c.c.c8ccxxxxxxxx"); - strcat(vgrid[9], "c*****cc..cc..cc..cc.c$c.c.c8c.cxxxxxxxx"); - strcat(vgrid[10], "c****cc9.cc..cc8.cc|c.c|c.c*c0ccxxxxxxxx"); - strcat(vgrid[11], "c***cc..cc..cc..cc.c.c.c.c.c.c$cxxxxxxxx"); - strcat(vgrid[12], "c**cc..cc8.cc..cc.c*c.c.c.c.c.ccxxxxxxxx"); - strcat(vgrid[13], "c+cc9.cc..cc..cc.c.c.c.c*c.c.c.cxxxxxxxx"); - strcat(vgrid[14], "c^c..cc..cc..cc.c$c.c.c.c.c.c*ccxxxxxxxx"); - strcat(vgrid[15], "c...cc..cc..cc.c.c.c9c$c.c.c.c9cxxxxxxxx"); - strcat(vgrid[16], "c..cc..cc..cc$c.c.c*c.c.c.c9c9ccxxxxxxxx"); - strcat(vgrid[17], "c.cc..cc..cc.c.c|c.c.c.c.c$c.c9cxxxxxxxx"); - strcat(vgrid[18], "ccc..cc..cc.c.c.c.c.c.c.c.c.cc+cxxxxxxxx"); - strcat(vgrid[19], "cc..cc..cc.c*c.c.c.c.c.c$c.cc..cxxxxxxxx"); - strcat(vgrid[20], "c0.cc..cc.c.c.c.c8c.c*c.c.cc0.ccxxxxxxxx"); - strcat(vgrid[21], "c.cc..cc*c.c.c.c.c$c.c.c.cc..cccxxxxxxxx"); - strcat(vgrid[22], "c^c..cc.c.c9c.c.c.c.c.c.cc..cc.cxxxxxxxx"); - strcat(vgrid[23], "c0..cc$c.c.c*c0c.c.c.c.cc..cc.0cxxxxxxxx"); - strcat(vgrid[24], "c..cc.c.c9c.c.c.c$c.c.cc.9cc...cxxxxxxxx"); - strcat(vgrid[25], "c.cc9c.c.c.c.c.c.c.c.cc..cc..c^cxxxxxxxx"); - strcat(vgrid[26], "ccc.c.c$c.c.c.c.c.c$cc..cc..cc^cxxxxxxxx"); - strcat(vgrid[27], "cc$c.c.c.c.c$c.c0c.cc..cc..cc..cxxxxxxxx"); - strcat(vgrid[28], "c.c.c.c.c.c.c.c.c.cc9.cc..cc..ccxxxxxxxx"); - strcat(vgrid[29], "cc.c8c.c.c$c.c.c.cc..cc..cc0.cccxxxxxxxx"); - strcat(vgrid[30], "c.c$c.c$c0c.c.c.cc..cc..cc..cc$cxxxxxxxx"); - strcat(vgrid[31], "cc.c.c.c.c.c*c.cc..cc..cc..cc$$cxxxxxxxx"); - strcat(vgrid[32], "c.c.c.c.c.c.c.cc..cc0.cc..cc$$$cxxxxxxxx"); - strcat(vgrid[33], "cc.c.c.c.c.c$cc..cc..cc..cc$$$$cxxxxxxxx"); - strcat(vgrid[34], "c.c.c.c.c.c.cc.8.^..cc....+$$$$cxxxxxxxx"); - strcat(vgrid[35], "cccc@cccccccccccccccccccccccccccxxxxxxxx"); - - return MAP_NORTHEAST; -} - -static char vault_7(char vgrid[81][81], FixedVector& mons_array) -{ // four-leaf vault - UNUSED( mons_array ); - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxx.........^..^.........xxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxx...xxxxxxxx..xxxxxxxx...xxxx"); - strcpy(vgrid[9], "xxxxxxxxxxx...xxxxxxxxx..xxxxxxxxx...xxx"); - strcpy(vgrid[10], "xxxxxxxxxx...xx$*....xx..xx....$$xx...xx"); - strcpy(vgrid[11], "xxxxxxxxx...xx$*$....xx..xx....$*$xx...x"); - strcpy(vgrid[12], "xxxxxxxxx..xx*$*$....xx..xx....*$$$xx..x"); - strcpy(vgrid[13], "xxxxxxxxx..xx$$$.00..xx..xx..00.*$*xx..x"); - strcpy(vgrid[14], "xxxxxxxxx..xx....09..xx..xx..90....xx..x"); - strcpy(vgrid[15], "xxxxxxxxx..xx......+xx....xx+......xx..x"); - strcpy(vgrid[16], "xxxxxxxxx..xx......x^......^x......xx..x"); - strcpy(vgrid[17], "xxxxxxxxx..xxxxxxxxx........xxxxxxxxx..x"); - strcpy(vgrid[18], "xxxxxxxxx..xxxxxxxx..........xxxxxxxx..x"); - strcpy(vgrid[19], "xxxxxxxxx..............TT..............x"); - strcpy(vgrid[20], "xxxxxxxxx..............TT..............x"); - strcpy(vgrid[21], "xxxxxxxxx..xxxxxxxx..........xxxxxxxx..x"); - strcpy(vgrid[22], "xxxxxxxxx..xxxxxxxxx........xxxxxxxxx..x"); - strcpy(vgrid[23], "xxxxxxxxx..xx......x^......^x......xx..x"); - strcpy(vgrid[24], "xxxxxxxxx..xx......+xx....xx+......xx..x"); - strcpy(vgrid[25], "xxxxxxxxx..xx....09..xx..xx..90....xx..x"); - strcpy(vgrid[26], "xxxxxxxxx..xx$$*.00..xx..xx..00.*$$xx..x"); - strcpy(vgrid[27], "xxxxxxxxx..xx*$*$....xx..xx....*$$*xx..x"); - strcpy(vgrid[28], "xxxxxxxxx...xx*$*....xx..xx....$$$xx...x"); - strcpy(vgrid[29], "xxxxxxxxxx...xx*$....xx..xx....*$xx...xx"); - strcpy(vgrid[30], "xxxxxxxxxxx...xxxxxxxxx..xxxxxxxxx...xxx"); - strcpy(vgrid[31], "xxxxxxxxxxxx...xxxxxxxx..xxxxxxxx...xxxx"); - strcpy(vgrid[32], "xxxxxxxxxxxxx..^................^..xxxxx"); - strcpy(vgrid[33], "xxxxxxxxxxxxxxxxxxxxxxx^^xxxxxxxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxxxxxxxxxxxxxxx++xxxxxxxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxx"); - - return MAP_NORTHWEST; -} - -static char vault_8(char vgrid[81][81], FixedVector& mons_array) -{ // cross vault - UNUSED( mons_array ); - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxx............xxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxx..................xxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxx......................xxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxx..........w..w..........xxxx"); - strcpy(vgrid[11], "xxxxxxxxxxx........wwww++wwww........xxx"); - strcpy(vgrid[12], "xxxxxxxxxxx......wwwvvv^^vvvwww......xxx"); - strcpy(vgrid[13], "xxxxxxxxxx......wwwwv.9..9.vwwww......xx"); - strcpy(vgrid[14], "xxxxxxxxxx.....wwwwwv......vwwwww.....xx"); - strcpy(vgrid[15], "xxxxxxxxxx....wwwwwvv......vvwwwww....xx"); - strcpy(vgrid[16], "xxxxxxxxx....wwwwwvv........vvwwwww....x"); - strcpy(vgrid[17], "xxxxxxxxx....wwvvvv....vv....vvvvww....x"); - strcpy(vgrid[18], "xxxxxxxxx...wwwv......vvvv......vwww...x"); - strcpy(vgrid[19], "xxxxxxxxx...wwwv....vv8vv8vv....vwww...x"); - strcpy(vgrid[20], "xxxxxxxxx..wwwwv...vvvv||vvvv...vwwww..x"); - strcpy(vgrid[21], "xxxxxxxxx^^wwwwv...vvvv||vvvv...vwwww^^x"); - strcpy(vgrid[22], "xxxxxxxxx..wwwwv....vv8vv8vv....vwwww..x"); - strcpy(vgrid[23], "xxxxxxxxx...wwwv......vvvv......vwww...x"); - strcpy(vgrid[24], "xxxxxxxxx...wwwvvvv....vv....vvvvwww...x"); - strcpy(vgrid[25], "xxxxxxxxx....wwwwwvv........vvwwwww....x"); - strcpy(vgrid[26], "xxxxxxxxxx...wwwwwwvv......vvwwwwww...xx"); - strcpy(vgrid[27], "xxxxxxxxxx....wwwwwwv......vwwwwww....xx"); - strcpy(vgrid[28], "xxxxxxxxxx.....wwwwwv......vwwwww.....xx"); - strcpy(vgrid[29], "xxxxxxxxxxx.....wwwwvvvvvvvvwwww.....xxx"); - strcpy(vgrid[30], "xxxxxxxxxxx.......wwwwwwwwwwww.......xxx"); - strcpy(vgrid[31], "xxxxxxxxxxxx.........wwwwww.........xxxx"); - strcpy(vgrid[32], "xxxxxxxxxxxxx.........^..^.........xxxxx"); - strcpy(vgrid[33], "xxxxxxxxxxxxxxx.......x++x.......xxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxxxxxxxxxx...xx..xx...xxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxxxxxxxxxxxxxx..@.xxxxxxxxxxxxxx"); - - return MAP_NORTHWEST; -} - - -static char vault_9(char vgrid[81][81], FixedVector& mons_array) -{ // another thingy vault - UNUSED( mons_array ); - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcat(vgrid[36], "xxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[37], "xxxxxxxxxxxxxxx^xxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[38], "xxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[39], "xx.....^...............^.....xxxxxxxxxxx"); - strcat(vgrid[40], "x..bb..xxxxxxxxxxxxxxxxx..bb..xxxxxxxxxx"); - strcat(vgrid[41], "x..b...xxxxxxxxxxxxxxxxx...b..xxxxxxxxxx"); - strcat(vgrid[42], "x...b..xxxxbbbbbbbbbxxxx..b...xxxxxxxxxx"); - strcat(vgrid[43], "x..bb..xxbbb.......bbbxx..bb..xxxxxxxxxx"); - strcat(vgrid[44], "x......xxb....9.9....bxx......xxxxxxxxxx"); - strcat(vgrid[45], "x..bb..xbb..%$$$$$%..bbx..bb..xxxxxxxxxx"); - strcat(vgrid[46], "x...b..xb..0%$***$%0..bx..b...xxxxxxxxxx"); - strcat(vgrid[47], "x..b...xb..0%$*H*$%0..bx...b..xxxxxxxxxx"); - strcat(vgrid[48], "x...b..xb..0%$***$%0..bx..b...xxxxxxxxxx"); - strcat(vgrid[49], "x..b...xb...%$$$$$%...bx...b..xxxxxxxxxx"); - strcat(vgrid[50], "x...b..xbb.900000009.bbx..b...xxxxxxxxxx"); - strcat(vgrid[51], "x..b...xxb...........bxx...b..xxxxxxxxxx"); - strcat(vgrid[52], "x..bb..xxbbb..9.9..bbbxx..bb..xxxxxxxxxx"); - strcat(vgrid[53], "x......xxxxbbbb.bbbbxxxx......xxxxxxxxxx"); - strcat(vgrid[54], "x..bb..xxxxxxxb=bxxxxxxx..bb..xxxxxxxxxx"); - strcat(vgrid[55], "x..b...xxxxxxxx=xxxxxxxx...b..xxxxxxxxxx"); - strcat(vgrid[56], "x...b..xxxxxxxx^xxxxxxxx..b...xxxxxxxxxx"); - strcat(vgrid[57], "x..b....xxxxxxx=xxxxxxx....b..xxxxxxxxxx"); - strcat(vgrid[58], "x...b...^.............^...b...xxxxxxxxxx"); - strcat(vgrid[59], "x..b....xxxxxxxxxxxxxxx....b..xxxxxxxxxx"); - strcat(vgrid[60], "x..bb..xxxxxxxxxxxxxxxxx..bb..xxxxxxxxxx"); - strcat(vgrid[61], "xx....xxxxxxxxxxxxxxxxxxx....xxxxxxxxxxx"); - strcat(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - return MAP_SOUTHEAST; -} - - -static char vault_10(char vgrid[81][81], FixedVector& mons_array) -{ // impenetrable vault - UNUSED( mons_array ); - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcat(vgrid[36], "..............@................xxxxxxxxx"); - strcat(vgrid[37], "...............................xxxxxxxxx"); - strcat(vgrid[38], "...............................xxxxxxxxx"); - strcat(vgrid[39], "...............................xxxxxxxxx"); - strcat(vgrid[40], "...............................xxxxxxxxx"); - strcat(vgrid[41], ".....cccccccccccccccc..........xxxxxxxxx"); - strcat(vgrid[42], ".....c[^...........9cc.........xxxxxxxxx"); - strcat(vgrid[43], ".....c^xxxxx=xxxxxx..cc........xxxxxxxxx"); - strcat(vgrid[44], ".....c.x9..^^^...9xx..cc.......xxxxxxxxx"); - strcat(vgrid[45], ".....c.x.xxx=xxxx..xx..cc......xxxxxxxxx"); - strcat(vgrid[46], ".....c.x^x$$$$$$xx..xx.9c......xxxxxxxxx"); - strcat(vgrid[47], ".....c.=^=$*|||*$xx..xx.c......xxxxxxxxx"); - strcat(vgrid[48], ".....c.x^xx$*|||*$xx.9x.c......xxxxxxxxx"); - strcat(vgrid[49], ".....c.x9.xx$*|||*$xx^x.c......xxxxxxxxx"); - strcat(vgrid[50], ".....c.xx..xx$*|||*$=^=.c......xxxxxxxxx"); - strcat(vgrid[51], ".....c9.xx..xx$$$$$$x^x.c......xxxxxxxxx"); - strcat(vgrid[52], ".....cc..xx..xxxx=xxx.x.c......xxxxxxxxx"); - strcat(vgrid[53], "......cc..xx9...^^^..9x.c......xxxxxxxxx"); - strcat(vgrid[54], ".......cc..xxxxxx=xxxxx^c......xxxxxxxxx"); - strcat(vgrid[55], "........cc9...........^]c......xxxxxxxxx"); - strcat(vgrid[56], ".........cccccccccccccccc......xxxxxxxxx"); - strcat(vgrid[57], "...............................xxxxxxxxx"); - strcat(vgrid[58], "...............................xxxxxxxxx"); - strcat(vgrid[59], "...............................xxxxxxxxx"); - strcat(vgrid[60], "...............................xxxxxxxxx"); - strcat(vgrid[61], "...............................xxxxxxxxx"); - strcat(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - return MAP_SOUTHEAST; -} - - -static char orc_temple(char vgrid[81][81], FixedVector& mons_array) -{ - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcpy(vgrid[36], "xxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxxxxxxxxxxxxxxx4.4xxxxxxxxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxx**..........x414x..........**xx"); - strcpy(vgrid[41], "xxxxxxxxx**..........x4.4x..........**xx"); - strcpy(vgrid[42], "xxxxxxxxx............+...+....4.......xx"); - strcpy(vgrid[43], "xxxxxxxxx....4..4....x...x............xx"); - strcpy(vgrid[44], "xxxxxxxxx............x...x.......4....xx"); - strcpy(vgrid[45], "xxxxxxxxx............xx.xx............xx"); - strcpy(vgrid[46], "xxxxxxxxx...4......xxxx+xxxx......6...xx"); - strcpy(vgrid[47], "xxxxxxxxx........xxx.......xxx........xx"); - strcpy(vgrid[48], "xxxxxxxxxxx...xxxx..2.....2..xxxx...xxxx"); - strcpy(vgrid[49], "xxxxxxxxxxxx+xxxx.............xxxx+xxxxx"); - strcpy(vgrid[50], "xxxxxxxxxxx...xxx.............xxx...xxxx"); - strcpy(vgrid[51], "xxxxxxxxx......x...............x......xx"); - strcpy(vgrid[52], "xxxxxxxxx..4...x...2...I...2...x...5..xx"); - strcpy(vgrid[53], "xxxxxxxxx......x...............x......xx"); - strcpy(vgrid[54], "xxxxxxxxx...4..xx.............xx..5...xx"); - strcpy(vgrid[55], "xxxxxxxxx$......x....2...2....x......$xx"); - strcpy(vgrid[56], "xxxxxxxxx$6..5..xx.....3.....xx.5...7$xx"); - strcpy(vgrid[57], "xxxxxxxxx$$$.....xxx.......xxx.....$$$xx"); - strcpy(vgrid[58], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_ORC_WARLORD; - mons_array[1] = MONS_ORC_PRIEST; - mons_array[2] = MONS_ORC_HIGH_PRIEST; - mons_array[3] = MONS_ORC_WARRIOR; - mons_array[4] = MONS_ORC_WIZARD; - mons_array[5] = MONS_ORC_KNIGHT; - mons_array[6] = MONS_ORC_SORCERER; - - return MAP_SOUTHWEST; -} - -static char my_map(char vgrid[81][81], FixedVector& mons_array) -{ // by Matthew Ludivico - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcpy(vgrid[36], "xxxxxxxxxx.@.xxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxxxx..........................xx"); - strcpy(vgrid[39], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..xx"); - strcpy(vgrid[40], "xxxxxxxxx.^^..........................xx"); - strcpy(vgrid[41], "xxxxxxxxxx.^^xx+xxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxxxx.^...11....xxxxxxxx..xxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxxxxx..x.1..6..xxx........xx..xxx"); - strcpy(vgrid[44], "xxxxxxxxxxxxx.xxxxxxxxx...vvvvv...x...xx"); - strcpy(vgrid[45], "xxxxxxxxx6..1...x.........+1..v.......xx"); - strcpy(vgrid[46], "xxxxxxxxx..1....x.........vvvvv........x"); - strcpy(vgrid[47], "xxxxxxxxx..5...xx......................x"); - strcpy(vgrid[48], "xxxxxxxxxxxxxx^++...........vvvvvvv....x"); - strcpy(vgrid[49], "xxxxxxxxxxxxxx^xx...xx=xx...vv$%$vvvvv.x"); - strcpy(vgrid[50], "xxxxxxxxxxxxxx^x...xxv1vxx...vvv*2...v.x"); - strcpy(vgrid[51], "xxxxxxxxxxxxxx^x..vvvv7.vvvv...vv.vv+v^x"); - strcpy(vgrid[52], "xxxxxxxxx..xxx^..vvvb....bvvv...vvv^...x"); - strcpy(vgrid[53], "xxxxxxxxx%%.xx..vvvvb....bvvvv.......xxx"); - strcpy(vgrid[54], "xxxxxxxxxx.....vvbbb......bbbvv.....xxxx"); - strcpy(vgrid[55], "xxxxxxxxxxx....vvb....66....bvvxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxxxxxvvvb..llllll..bvvvxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxvvvvvvvvb..ll45ll..bvvvvvvvvxxx"); - strcpy(vgrid[58], "xxxxxxxxxccc***+== .l3.2.l..cccccccccxxx"); - strcpy(vgrid[59], "xxxxxxxxxccc+cccbb....ll....c..$$$$+$*cx"); - strcpy(vgrid[60], "xxxxxxxxxcc|||cbb...3llll2...cc%*%*c$|cx"); - strcpy(vgrid[61], "xxxxxxxxxcccccccbbbbbbbbbbbccccccccccccx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_HELL_HOUND; - mons_array[1] = MONS_NECROMANCER; - mons_array[2] = MONS_WIZARD; - mons_array[3] = MONS_ORANGE_DEMON; - mons_array[4] = MONS_ROTTING_DEVIL; - mons_array[5] = MONS_HELL_KNIGHT; - mons_array[6] = MONS_GREAT_ORB_OF_EYES; - - return MAP_SOUTHWEST; -} - -static char farm_and_country(char vgrid[81][81], FixedVector& mons_array) -{ // by Matthew Ludivico (mll6@lehigh.edu) - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxx..........................................xxxxxxxx}.xxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxx............xxxxxx....xxx.......xx...........xxxx..]xxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxx***x...........xxx..xxx............xxxx...........xx..xxxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxx|*$=...xx.xxxxxxx....xxxxxxxxxx......xx................xxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxxxxxxx....xxxxxxxx......3..xxx.................x..........xxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxxxxxxxx......x........x......xx.........w...................xxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxx)......xx...xxx.....xxx......x........www3....3.............xxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxx=xxxxxxxxxxx...xxxxxxxxx..xxx.....wwwww....%%%.............xxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxx......xxx.......xx.xxxx.x...xxxxxxxwwwwwww..5%%%..........xx.xxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxx.........x..xxxxxxxx.....x........3wwwwwwwww..%%%........xxx..xxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxx....5...xx..x.xxxxx.....xxx........wwwwwwwww..%%%..........xx.xxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxx.....xxx..xx..xx........xxxxxxxxxwwwwwwwww..............xxx.xxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxx........x..x...............xx..xxxxwwwwwwwwwwwwww............xxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxx.............................x.....xxwwwwww3wwwwww............xxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxx...x...........5.....7...............ww.......ww.....44....xxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxwxx..xx.....622...2.26...6.2...22.6...62..2..226ww.....44xx...xxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxwwxxxx......2....2.22....2..2...2.2.......22...2ww....xxxx..xxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxwwwwxxx......2...2.2.2...2.22..2.22...22.2.2..22ww.....xxx....xxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxwwwwwx....4..2...2...........22...277..2..2.2.22ww...........xxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxxwwwwwxx....42..2....22.4..2..2...2.4..2.22..22.2ww............xxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwww..2.........xxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxxwwwwwxx.....62....2.26...62.2.2..26...6...22..26..............xxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxxwwwww.........................................................xxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxxwwwwwxx....222.2.22..2.7.......7..............................xxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxwwwww...........ccccccc+ccccccc...ccc......cc+ccc...xxxxx.....xxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxxwwwwwxx.........c$$*.c$$5$+.5.c...+5c......c%%%%c......xxx3...xxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxwwwwwx....2.....c$.c+cccccc.%.c...ccc......c%%%%c....xxxxx....xxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxwwwwwx..........c..c..........c............cccccc......xxx....xxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxwwxxxxxxx.......ccccc+ccccccccc.........................xx....xxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxxwxx.....xxxx........c...c.................2...................xxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxxx.........xxxx...........2....xxxx...........................xxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxx..............xxxx..........xxxx..x...........................xxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxx.................xxxxx++xxxxx.....xx............xx...x........xxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxx.....................c..c..........xxxxx..........xxxxx.......xxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxx.......cccc..........c..c...cccc......xxx...........x.........xxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxx.......c..c..........c++c...c..c........xxx.........x.........xxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxx.......c..c..........c..c...c..c..........xxx.................xxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxx....cccc++cccccccccccc++ccccc..ccccccc......xxx...............xxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxx....c..........1.....................c........xxx.............xxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxx.cccc.....w....w....%1.....w.....%...c..........xxx...........xxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxx.c1.+....www..www..%%%....www...%%%1.c...........xxxxxxxxx....xxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxx.cccc.....w....w....%......w.....%...c..................xxx...xxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxx....c.......5........................c....................xxxxxxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxx....ccc....%%%%%....cccccccccccccccccc........................xxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxx......cc...........cc.........................................xxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxx.......cccccc+cccccc..........................................xxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxx........cc.......cc...........................................xxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxx.........cc.....cc.....................cccccccccccccccccccccccxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxx..........ccc+ccc......................c......vvv.............xxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxx..........ccc.c........................c......v5+...vvvvv.....xxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxx..........ccc.c........................c......vvv...v.5.v.....xxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxccccccccccccc.ccc......................c............v..5v.....xxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxx..........c.....cccccccccccccccccccccccccccc..........vv+vv...xxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxx..........c............................+................5111..xxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxx..........c.{([.c......................+................5.....xxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_DEATH_YAK; - mons_array[1] = MONS_PLANT; - mons_array[2] = MONS_GRIFFON; - mons_array[3] = MONS_KILLER_BEE; - mons_array[4] = MONS_OGRE; - mons_array[5] = MONS_OKLOB_PLANT; - mons_array[6] = MONS_WANDERING_MUSHROOM; - - return MAP_ENCOMPASS; -} - - -static char fort_yaktaur(char vgrid[81][81], FixedVector& mons_array) -{ // by Matthew Ludivico (mll6@lehigh.edu) - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcat(vgrid[36], ".........@....wwwwwwwwwwwwwwwwwxxxxxxxxx"); - strcat(vgrid[37], ".ccccc.......ww....wwww....wwwwxxxxxxxxx"); - strcat(vgrid[38], ".c$c%c......ww.ccccccccc.......xxxxxxxxx"); - strcat(vgrid[39], ".c+c+c......ww.c.%$....ccccccccxxxxxxxxx"); - strcat(vgrid[40], ".c...+......ww.c*.115..c$$+|*|cxxxxxxxxx"); - strcat(vgrid[41], ".c1..c.....ww..c...55+ccc+cxx=cxxxxxxxxx"); - strcat(vgrid[42], ".ccccc.....ww..ccccccc....c|=*cxxxxxxxxx"); - strcat(vgrid[43], "............ww.......c5...cxx=cxxxxxxxxx"); - strcat(vgrid[44], "....6.ccccc.ww.w...2.+51..c|1.cxxxxxxxxx"); // last 1 here was 7 - strcat(vgrid[45], "....63+...c..wwww..21+51..c2.2cxxxxxxxxx"); - strcat(vgrid[46], "....6.ccccc..wwwwww..c5...cc+ccxxxxxxxxx"); - strcat(vgrid[47], "............wwwwwww..c........cxxxxxxxxx"); - strcat(vgrid[48], "............wwwwwww..ccccccccccxxxxxxxxx"); - strcat(vgrid[49], "...........ww1w..www...........xxxxxxxxx"); - strcat(vgrid[50], ".......566.www.....www.........xxxxxxxxx"); - strcat(vgrid[51], ".........1ww....ccccc..........xxxxxxxxx"); - strcat(vgrid[52], ".....566.w......+...c..........xxxxxxxxx"); - strcat(vgrid[53], ".........www....ccccc..........xxxxxxxxx"); - strcat(vgrid[54], "...........ww............wwwwwwxxxxxxxxx"); - strcat(vgrid[55], ".......3....wwwww......www.....xxxxxxxxx"); - strcat(vgrid[56], "......666.......ww...www.......xxxxxxxxx"); - strcat(vgrid[57], ".....cc+cc.......wwwww.........xxxxxxxxx"); - strcat(vgrid[58], ".....c...c.....................xxxxxxxxx"); - strcat(vgrid[59], ".....ccccc.....................xxxxxxxxx"); - strcat(vgrid[60], "...............................xxxxxxxxx"); - strcat(vgrid[61], "...............................xxxxxxxxx"); - strcat(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_YAKTAUR; - mons_array[1] = MONS_DEATH_YAK; - mons_array[2] = MONS_MINOTAUR; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = MONS_YAK; - mons_array[5] = MONS_GNOLL; - mons_array[6] = RANDOM_MONSTER; - - return MAP_SOUTHEAST; -} - - -static char box_level(char vgrid[81][81], FixedVector& mons_array) -{ // by John Savard - UNUSED( mons_array ); - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxx.................xx.............x...................^.........xxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxx.................xx...xxxxxx....x.xxxxxxx.xxxxxxxxxxxxxxxxxxx.xxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxx.................xx...xx.0......x.x........x......x.........x.xxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxx..$..............xx...xx........x.x........x.....%x.x..*..xxx.xxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxx......................xx........x.x........x.xxxxxx.x.....x...xxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxx......................xx....%...x.x........x.x......xxxxxxx.x.xxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxx.................xx...xx........x.x........x.x.xxxxxx.......x.xxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxx.................xx...xx........x.x..{.....x.x..............x.xxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxx.............0...xx...xxxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxxxxxxxx.xxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxx.................xx...........................................xxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxx}x.........................>=........xxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxx..................x...xxx.x.xxx+xxxxxxxxxxxxxxxx+xxxxx........xxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxx..xxxxxxxxxxxxxx..x...xxx.x.x0...x..0..............0.x........xxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxx..x............x..x...xxx.x.x....x...................x........xxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxx....xxxxxxxxx..x..x...xxx.x.x....x...................x......8*xxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxx..x.x....0..x..x..x...xxx...x...%x...................x......*|xxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxx..x.x..........x..x...xxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxx..x.x*......x..x..x..........x...........0...x...%............xxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxx..x.xxxxxxxxx..x..=..........x.xxxxxxxxxxxxx.x................xxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxx..x......0.....xxxxxxx.......x.x...x...x...x.x................xxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxx..xxxxxxxxxxxxxxxxxxxx..0....x...x.x.x.x.x.x.x......0.........xxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxx..........^.........xx.......x.x.x.x.x.x.x...+................xxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxxcccccccccccccccccc..xx.......x.x$x...x...xxxxx................xxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxc...........9....c..xx.......x.x.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxxc......c............xx.......x.x.x...x..0.....................xxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxc.....|c............xx.......x.x.x.x.x........................xxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxc...........9....c..xx.......x.x...x.x........................xxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxcccccccccccccccccc..xx.......x.xxxxx.x........................xxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxx....................xx.......x.x.....=....................*...xxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxx....................xx.......x.x.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.......x.x.x...........................(xxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxx.x$x..xxxx.xxxxxxxxxxxxxxxxxxxx.xxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxx...............................x.x..x.......................x.xxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.x..x.xxxxxxxxxxxxx.........x.xxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxx.............)xxx................x..x.xxxxxxxxxxxxx.........x.xxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxx..............xxx.xxxxxxxxxxxxxxxx..x.xxxxxxxxxxxxx.........x.xxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxx..............xxx...................x.x...........xxxxx+xxxxx.xxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.x..$........x.........x.xxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxx......9.......xxxxxxxxxxxxxxxxxxxxxxx.x...........x........%x.xxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.x.0.........x0........x.xxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.x.......$...x.........x.xxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.x...........xxxxxxxxxxx.xxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxx.x...........xxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxx..............xxxxxxxxxxxxxxxxxxxxxxx.............x...........xxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx+xxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxx.xxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxx.xxxxxxx=xxxxxx.xxxxxx.xxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxx.....xx.................xxxxxxxxxxx.......x........x.....x....xxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxx....0xx.................xxxxxxxxxxx.%.....x.0......x...0.x....xxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxx.....xx.9...............xxxxxxxxxxx.......x........x.%...x..$.xxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxx.....xx.................xxxxxxxxxxx.......x........x.....x....xxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxx.....xx.................xxxxxxxxxxx.......x........x.....x..0.xxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxx....0xx.................xxxxxxxxxxx.......x$.......x.....x....xxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxx]....xx................*xxxxxxxxxxx......[x........x.....x$...xxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - return MAP_ENCOMPASS; -} - - -static char vestibule_map(char vgrid[81][81], FixedVector& mons_array) -{ // this is the vestibule - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvvvvvvvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..v.....v..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.....v.....v.....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxxxxxxxxxxxxx........v.....v........xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxxxxxxxxxxxx..........v..A..v..........xxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxxxxxxxxxxxx............v.....v............xxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxxxxxxxxxxxxx.............v.....v.............xxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxxxxxxxxxxxxx..............vvv+vvv..............xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxxxxxxxxxxx.....................................xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxxxxxxxxxxxx.......................................xxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxxxxxxxxxxx.........................................xxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxxxxxxxxxx...........................................xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxxxxxxxx.............................................xxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxxxxxxxx...............................................xxxxxxxxxxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxxxxxxx.................................................xxxxxxxxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxxxxx...................................................xxxxxxxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxxxxx.....................................................xxxxxxxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxxxxx.....................................................xxxxxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxxxx.......................................................xxxxxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxxxx.......................................................xxxxxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxxxx.........................................................xxxxxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxxxx............................{............................xxxxxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxxxx.........................................................xxxxxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxxx...l.l.....................................................xxxxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxxx..l.l.l.l..................................................xxxxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxx.l.l.l.l.l.................................................xxxxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxx.l.l.l.l.l...................................................xxxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxl.l.l.l.l.l..................................................xxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxx.l.l.l.A.l.l.................}1].............................=Axxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxl.l.l.l.l.l.l.................)..............................xxxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxx.l.l.l.l.l.l.................................................xxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxx.l.l.l.l.l.l...............................................xxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxx..l.l.l.l..................................................xxxxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxxx.....l.l...................................................xxxxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxxxx......................[...........(......................xxxxxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxxxx.........................................................xxxxxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxxxx.........................................................xxxxxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxxxxx.......................................................xxxxxxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxxxxx.......................................................xxxxxxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxxxxxx.....................................................xxxxxxxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxxxxxx.....................................................xxxxxxxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxxxxxxx...................................................xxxxxxxxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxxxxxxxx....................wwwww........................xxxxxxxxxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxxxxxxxxx..................wwwwwwww.....................xxxxxxxxxxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxxxxxxxxxx..............wwwwwwwwwwwww..................xxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxxxxxxxxxxx...........w..wwww..wwwww..w...............xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxxxxxxxxxx..........w...ww.....ww..wwwww...........xxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxxxxxxxxxxxxx.........ww......ww....wwwwwwwww.......xxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxxxxxxxxxxxxxx.........ww....wwww...wwwwwwwwww.....xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxxxxxxxxxxxxxxx.........ww....ww....wwwwwwwwwww...xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxxxxxxxxxxxxxx........wwww.......wwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxxxxxxxxxxxxxxxx......wwwwwww....wwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxxxxxxxxxxxxxxxxxx...wwwwwwwwwwAwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_GERYON; - mons_array[1] = RANDOM_MONSTER; - mons_array[2] = RANDOM_MONSTER; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_ENCOMPASS; -} - - -static char castle_dis(char vgrid[81][81], FixedVector& mons_array) -{ // Dispater's castle - rest of level filled up with plan_4 (irregular city) - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxv..............................................................vxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxv..vvvvvvvvv........................................vvvvvvvvv..vxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxv..v3.....|v........................................v|.....2v..vxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxv..v.vv+vvvv.v.v.v.v.v.v.v.v.v..v.v.v.v.v.v.v.v.v.v.vvvv+vv.v..vxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxv..v.v.....vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.....v.v..vxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxv..v|v.....+$$v$$+$$v||vvvvvvvvvvvvvvvvv$$$$v4.4.v$$v.....v|v..vxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxv..vvvv+vvvv$$+$$v$$+||v...............v$$$$+.4.4+$$v+vv+vvvv..vxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxv....vv.vvvvvvvvvvvvvvvv.v..v..v..v..v.v$$$$v4.4.v$$+||v.vv5...vxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxv...vvv................v...............vvvvvvvvvvvvvvvvv.vvv...vxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxv...5vv................+...............+.................vv....vxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxv...vvv+vvvvvvvvvvvvvvvv.v..v..v..v..v.vvvvvvvvvvvvvvvvv.vvv...vxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxv....vv..v.+$$$$$v.....v...............vvvvvvvvvvvvvvvvv.vv5...vxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxv...vvv..v.v$$$$$v.....v...............vv|$|$|vv|$|$|$vv.vvv...vxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxv...5vv..v.vvvvvvv.....vvvvv.......vvvvvv$|$|$++$|$|$|vv.vv....vxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxv...vvv..v...............v.vvvv+vvvvvvvvvvvvvvvvvvvvv+vv.vvv...vxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxv....vvv+v..........vvvvv.4vvv...vvvvvvvvvvvvvvvvvvvv+vv.vv5...vxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxv...vvv..v.v..v..v....2vvv+vv5...5vvvvvvv.4.4.vv.4.4.4vv.vvv...vxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxv...5vv.................vv|vvv...vvvvv.++4.4.4++4.4.4.vv.vv....vxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxv...vvv.................1vOvv5...5vvvv.vvvvvvvvvvvvvvvvv.vvv...vxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxv....vv.................vv|vvv...vvvvv.vvvvvvvvvvvvvvvvv.vv5...vxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxv...vvv.v..v..v..v....3vvv+vv5...5vvvv...................vvv...vxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxv...5vv.............vvvvv.4vvv...vvvvvvvvvvvvvvvvvvvvvvv.vv....vxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxv..vvvv+vvvv.............v.vv5...5vvvvvvvvvvvvvvvvvvvvvv+vvvv..vxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxv..v|v.....vvvvvvvvvvvvvvvvvvv...vvvvvvvvvvvvvvvvvvvv.....v|v..vxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxv..v.v.....vvvvvvvvvvvvvvvvvvvv+vvvvvvvvvvvvvvvvvvvvv.....v.v..vxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxv..v.vv+vvvv5.............5.........5..............5vvvv+vv.v..vxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxv..v2.....|v........................................v|.....3v..vxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxv..vvvvvvvvv........................................vvvvvvvvv..vxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxv............................{.[.(.............................vxxxxxxxx"); - - mons_array[0] = MONS_DISPATER; - mons_array[1] = MONS_FIEND; - mons_array[2] = MONS_ICE_FIEND; - mons_array[3] = MONS_IRON_DEVIL; - mons_array[4] = MONS_METAL_GARGOYLE; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_NORTH_DIS; -} - - -static char asmodeus(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxx....xxxxxxxxxxxxxxx.xxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxx..xxxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxx....xxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxxxxxxx...xxx................................xxxxxx....xxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxx.x.xxxxx.........................................xxx....xxxxxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxx....xx.....................4......................xx...xxxxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxx......x......................llllllllllllll.........x..xxxxxxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxx..xx..................lllllllllllllllllllllllll........xxxxxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxx...xxx....0..........llllllllllllllllllllllllll........xx...xxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxx....xxx.............llllllllllllllllllllllllllll..............xxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxx....xx...........lllllllllllllllllllllllllllll...............xxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxxx..............llllllllllllllllllllllllllllll...2..xx...0...xxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxxxxx...........lllllllllllllllllll.......llllll......xx......xxxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxxxxxx.......llllllllllllllllll............llllll.............xxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxxxxxx......lllllllll..........4.........4.lllllll..........xxxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxx...xx...ll3lllll......4...................llllllll......x.xxxxxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxx.......lllll.l................................llll.......xxxxxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxx..4..llllll...cccccccc+c+c+c+c+c+c+c+c+c+c....lll......xxxxxxxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxxx..lllllll..4.c.....c....................c....llll.....xxxxxxxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxxx...llllll.....c.V.V.+....0.....3.....0...c.....llll....x..xxxxxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxx...llllll...l..c.....c....................c....lllll........xxxxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxxx...lllll..ll..c..5..cccccccccccccccccccccc.4..llllll........xxxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxx...lllll..llll.c.....c...............c....c....lllllll.......xxxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxx...lllll..llll.c.V.V.c.......0.......c....c....lllllll.......xxxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxx...lllll..lll.c.....+...............+....c...lllllll........xxxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxxxx..lllll...ll.cccccccccc....0.......c....c...llllllll........xxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxx...lllll..4...c|$$||$$|c............c.0..c...llllllll........xxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxx...lllll.......c$$$$$$$$cccccccccccccc....c...lllllll.........xxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxx...lllll.......c$$|2|$$|c..0.........+....c...lllllll........xxxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxxx.lllllll......c|$$$$$$$c........9...c....c....llllllll.....xxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxx.lllllll......c$|$|$$|$c+ccccccccccccccccc....lllllll......xxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxx..llllll......cccccccc+c.....9.......c.........llllll......x.xxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxxx..lllllll.....c$$$$$$+3c.....8...3...c.....4...llllll........xxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxxx..llllllll....c$$$$$$c.c.....9.......c..ll....llllll.........xxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxxx...llllll..4..c$$2$$$c.ccccccccccccc+c.lll...lllllll...0....xxxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxxxx..llllll.....c$$$$$$c..+............c.ll...lllllll..........xxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxxxx..llllllll...ccccccccc+cccccccccccccc.....lllllll...........xxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxxxxx..llllllll.........cc..........cc........lllllll.......x..xxxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxxxxxx.llllllllll.......ccc.........cc......lllllllll.......xxxxxxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxxx....lllllllllll...4...cc.....2.2.cc....llllllllll.4.......xxxxxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxx....4.lllllllllllll....cccccccc+cccc..lllllllllll.....xx....xxxxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxxx.....llllllllllllll...cccccccc+cccc..llllllllll......xx....xxxxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxxxx.....lllllllllllllll..cc......cc...lllllllllll...........xxxxxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxxxx.....llllllllllllll...ccO1....cc.4..lllllllll...........xxxxxxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxxxxx.....lllllllllllll...cc......cc....lllllllll.......xx.xxxxxxxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxxx.......llllllllllll..cccccccccc...lllllllll........xxxxxxxxxxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxx.........llllllllllllll.cccccccccc.lllllllllll.......xxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxxx....0...llllllllllllll............lllllllll....0....xxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxxx.......4.lllllllllllllll..4....lllllllll...........xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxx..........llllllllllllll....lllllll....4.....x........xxxxxxxxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxxx...xx.........lllllllllllllllll...................xx{xxxxxxxxxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxxxxx..xx................lllllll.....................xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxxx.........xxx.................xxxxxx......xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxx....xxxxxxxx...xxx......xxxxxxxxxx.......xxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxx(xxxxxxxxxxxx[xxxxx...xxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_ASMODEUS; - mons_array[1] = MONS_FIEND; - mons_array[2] = MONS_BALRUG; - mons_array[3] = MONS_MOLTEN_GARGOYLE; - mons_array[4] = MONS_SERPENT_OF_HELL; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_ENCOMPASS; -} - -static char antaeus(char vgrid[81][81], FixedVector& mons_array) -{ // bottom of Cocytus. This needs work - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxxx........................xxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxxxxxx..........................xxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxxxxxxxxxxxx................................xxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxxxxxxxxxxxxx....cccccccccccc..cccccccccccc....xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxxxxxxxxxxxxx....ccccccccccccc2.ccccccccccccc....xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxxxxxxxxxxx....cc..........................cc....xxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxxxxxxxxxxxx....cc............................cc....xxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwwwwwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwwwwwwwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwwwwwwwwwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxxxxxxxx....cc...ww.......3..........3.......ww...cc....xxxxxxxxxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxxxxxxx....cc...ww............................ww...cc....xxxxxxxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxxxxx....cc...ww....cccccccccccccccccccccc....ww...cc....xxxxxxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxxxxx....cc...ww....cccccccccccccccccccccccc....ww...cc....xxxxxxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxxxx....cc...ww....cc......................cc....ww...cc....xxxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxxx....cc...ww....cc...T................T...cc....ww...cc....xxxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxx....cc...ww....cc..........wwwwww..........cc....ww...cc....xxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxx....cc...ww....cc.......wwwwwwwwwwwwww.......cc....ww...cc....xxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxx....cc...ww...cc.....wwwwwwwwwwwwwwwwwwww.....cc...ww...cc....xxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxx....cc..www..cc....wwwwwwwwwccccccwwwwwwwww....cc..www..cc....xxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxx....cc..www.cc....wwwwwwwwccc2O12cccwwwwwwww....cc.www..cc....xxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxx....cc..www.cc...wwwwwwwwcc2+....+2ccwwwwwwww...cc.www..cc....xxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxx....cc..www.cc...wwwwwwwwcc+cc++cc+ccwwwwwwww...cc.www..cc....xxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxx....cc..www..c..wwwwwwwwwc|||c..c$$$cwwwwwwwww..c..www..cc....xxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxx....cc..wwww.c.wwwwwwwwwwc|||c..c$$$cwwwwwwwwww.c.wwww..cc....xxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxx....cc..wwww.c.wwwwwwwwwwcc||c..c$$ccwwwwwwwwww.c.wwww..cc....xxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxx....cc..wwww.c.wwwwwwwwwwwcccc++ccccwwwwwwwwwww.c.wwww..cc....xxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxx....cc..www..c..wwwwwwwwwwwwww..wwwwwwwwwwwwww..c..www..cc....xxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxx....cc..www.cc...wwwwwwwwwwwwwwwwwwwwwwwwwwww...cc.www..cc....xxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxx....cc..www.cc....wwwwwwwwwwwwwwwwwwwwwwwwwww...cc.www..cc....xxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxx....cc..www.cc....wwwwwwwwwwwwwwwwwwwwwwwwww....cc.www..cc....xxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxx....cc..www..cc....wwwwwwwwwwwwwwwwwwwwwwww....cc..www..cc....xxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxx....cc...ww...cc.....wwwwwwwwwwwwwwwwwwww.....cc...ww...cc....xxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxx....cc...ww....cc.......wwwwwwwwwwwwww.......cc....ww...cc....xxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxxx....cc...ww....cc..........wwwwww..........cc....ww...cc....xxxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxxxx....cc...ww....cc...T................T...cc....ww...cc....xxxxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxxxxx....cc...ww....cc......................cc....ww...cc....xxxxxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxxxxxx....cc...ww....ccccccccccc..ccccccccccc....ww...cc....xxxxxxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxxxxxxx....cc...ww....cccccccccc2.cccccccccc....ww...cc....xxxxxxxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxxxxxxxx....cc...ww............................ww...cc....xxxxxxxxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxxxxxxxxx....cc...ww..........................ww...cc....xxxxxxxxxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwwww..wwwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxxxxxxxxxxx....cc...wwwwwwwwwwww..wwwwwwwwwwww...cc....xxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxxxxxxxxxx....cc...wwwwwwwwwww..wwwwwwwwwww...cc....xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxxxxxxxxxxxxx....cc............................cc....xxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxxxxxxxxxxxxxx....cc..........................cc....xxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxxxxxxxxxxxxxxx....cccccccccccccccccccccccccccc....xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxxxxxxxxxxxxxx....cccccccccccccccccccccccccc....xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxxxxxxxxxxxxxxxx................................xxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxxxxxxxxxx..........{.(.[...........xxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_ANTAEUS; - mons_array[1] = MONS_ICE_FIEND; - mons_array[2] = MONS_ICE_DRAGON; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_ENCOMPASS; -} - - -static char ereshkigal(char vgrid[81][81], FixedVector& mons_array) -{ // Tartarus - // note that the tomb on the right isn't supposed to have any doors - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxx.................cccc..........ccc............................xxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxx.............ccccc..cccc.....ccc.cccc.........................xxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxx...........ccc.........ccccccc.....cc.........................xxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxx.........ccc.......2............V..cc.........................xxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxx........cc4........................cc...........xxxxxxxx......xxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxx........cc44xxx==xxx...............cc..........xx......xx.....xxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxx........ccxxx......xxx.......ccc++ccc.........xx........xx....xxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxx........cxx..........xxx.....ccc44ccc.........x..........x....xxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxx........cx............xx....cccc44cc.........xx..........xx...xxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxx.......ccx.G........G.xxx7ccc..c44c..........x.....|......x...xxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxx.......cxx............xxxcc..................x......7.....x...xxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxx......ccx..............xxc...................xx..........xx...xxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxx......ccx..G........G..xxc..x.........x.......x..........x....xxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxx......ccx..............xcc....................xx........xx....xxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxx.......cxx............xxc......................xx......xx.....xxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxx.......ccx.F........F.xcc.......................xxxxxxxx......xxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxx........cx............xc......................................xxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxx........cxx....17....xxc....x.........x.......................xxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxx........ccxxx......xxxcc......................................xxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxx........cccc=xxxxxx=cccc......................................xxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxx........cc||cccccccc||cc......................................xxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxx.........cc||||O|||||cc.......................................xxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxx..........cccccccccccc......x.........x............V..........xxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxx...........................................xx$$$$xxx|||||xx...xxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxx.......V........V...........x.........x....xx$$$$xxx|||||xx...xxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxx...........................................xx44444xx22222xx...xxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxx.......xxxxxxxxx+xxxxxxxxx.................xx44444xx22222xx...xxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxx.......x3.2..........3...x..x.........x..xxxxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxx.......x.x.x.x.x.x.x.x.x.x.................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxx.......x...2.3..4..5..4..x......................=.......xxx...xxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxx.......xx.x.x.x.x.x.x.x.xx......................=.......xxx...xxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxx.......x..65..3..6.6...5.x.................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxx.......x.x.x.x.x.x.x.x.x.x..x.........x..xxxxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxx.......x...4...3.....4...x.................xx.....xx555555x...xxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxx.......xx=xxxxx.x.xxxxxxxx.................xx.....xx555555x...xxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxx.......x$$$$$$x.25.x$$$||x.................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxx.......x$x$$x$xx.x.x$x$x|x.................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxx.......x||||||x.556=$$$||x..x.........x....xx$$xx56565xx$|x...xxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxx.......xxxxxxxxxxxxxxxxxxx.................xx$$xx65656xx|7x...xxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxx...........................................xxxxxxxxxxxxxxxx...xxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxx........(...........................................[.........xxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxx..............................{...............................xxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_ERESHKIGAL; - mons_array[1] = MONS_NECROPHAGE; - mons_array[2] = MONS_WRAITH; - mons_array[3] = MONS_SHADOW; - mons_array[4] = MONS_ZOMBIE_SMALL; - mons_array[5] = MONS_SKELETON_SMALL; - mons_array[6] = MONS_SHADOW_FIEND; - - return MAP_ENCOMPASS; -} - - -static char mnoleg(char vgrid[81][81], FixedVector& mons_array) -{ - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcat(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[7], "x.................2............xxxxxxxxx"); - strcat(vgrid[8], "x.....2........................xxxxxxxxx"); - strcat(vgrid[9], "x..cccccccc...ccccccc..ccccccc.xxxxxxxxx"); - strcat(vgrid[10], "x..ccccccccc.2.ccccccc..cccccc.xxxxxxxxx"); - strcat(vgrid[11], "x..cccccccccc...ccccccc..ccccc.xxxxxxxxx"); - strcat(vgrid[12], "x..ccccccccccc.1.ccccccc..cccc.xxxxxxxxx"); - strcat(vgrid[13], "x2.cccccccccc.2..Occccccc2.ccc.xxxxxxxxx"); - strcat(vgrid[14], "x..ccccccccc.....ccccccccc..cc.xxxxxxxxx"); - strcat(vgrid[15], "x..cccccccc...c...ccccccccc..c.xxxxxxxxx"); - strcat(vgrid[16], "x..ccccccc...ccc...ccccccccc...xxxxxxxxx"); - strcat(vgrid[17], "x..cccccc...ccccc...ccccccccc..xxxxxxxxx"); - strcat(vgrid[18], "x..ccccc...ccccccc...ccccccccc.xxxxxxxxx"); - strcat(vgrid[19], "x..cccc...ccccccccc...ccccccc..xxxxxxxxx"); - strcat(vgrid[20], "x..ccc.2.ccccccccccc.2.ccccc...xxxxxxxxx"); - strcat(vgrid[21], "x..cc.....ccccccccccc...ccc....xxxxxxxxx"); - strcat(vgrid[22], "x..c...c...ccccccccccc...c.2...xxxxxxxxx"); - strcat(vgrid[23], "x.....ccc.2.ccccccccccc......c.xxxxxxxxx"); - strcat(vgrid[24], "x....ccccc...ccccccccccc....cc.xxxxxxxxx"); - strcat(vgrid[25], "x.2.ccccccc...ccccccccccc..ccc.xxxxxxxxx"); - strcat(vgrid[26], "x.................2.......cccc.xxxxxxxxx"); - strcat(vgrid[27], "x...c..ccccccc.ccccccc...ccccc.xxxxxxxxx"); - strcat(vgrid[28], "x..ccc......2c.c2cccc...cccccc.xxxxxxxxx"); - strcat(vgrid[29], "x.ccccc..ccc.c.c2ccc.2.ccccccc.xxxxxxxxx"); - strcat(vgrid[30], "x.cccccc..cc.c.c.cc...cccccccc.xxxxxxxxx"); - strcat(vgrid[31], "x.ccccccc..c.c.c.c...ccccccccc.xxxxxxxxx"); - strcat(vgrid[32], "x.cccccccc...c.c....cccccccccc.xxxxxxxxx"); - strcat(vgrid[33], "x.ccccccccc..c.c...ccccccccccc.xxxxxxxxx"); - strcat(vgrid[34], "x..............................xxxxxxxxx"); - strcat(vgrid[35], "xxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_MNOLEG; - mons_array[1] = MONS_NEQOXEC; - mons_array[2] = RANDOM_MONSTER; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_NORTHEAST; -} - - -static char lom_lobon(char vgrid[81][81], FixedVector& mons_array) +static int write_vault(const map_def &mdef, map_type map, + FixedVector &marray, + vault_placement &place) { + place.map = &mdef; - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwww.......wwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwbbbwwwwwww.......wwwwwwwxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxxxwwwwwwwwwwwwbbbbbbbbbbbwwwwww.........wwwwwwxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxwwwwwwwwwwwwbbbbwwwwwwwwwbbbbwwwwww.........wwwwwwxxxxxxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxwwwwwwwbbbbbbbbwwwwwwwwwwwwwwwbbbwwwww...........wwwwwxxxxxxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxxwwwwwbbbb......bbbwwwwwwwwwwww...bbwwwww.............wwwxxxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxxxwwwbbb...........bbbwwwwww........bbwwwww.............wwxxxxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxwwwbb...............bbwwww..........bwwwwww.............wwxxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxxxwwbb........1O.......bbww...........bbwwww..............wwxxxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxxwwwb...................bw......2......bwww.....U....2.....wwxxxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxxwwbb...................bb.............bww.................wwxxxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxwwbb..3................bbb............bbw..............4..wwxxxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxwwbbb...................b.b............4....................wwxxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxwwbwbb.................bb.......U......4..........U..........wxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxwwbwwbb...............bb..b............bbw..............4.....xxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxwwbbwwbbb...........bbb..bb............bwww...................xxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxwwwbwwwwb..b..2..bbbb....b.............bwww...................xxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxxwwbwwww...bbbbbbb.......bw.....3.....bbwwww...U.....3.......xxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxxwwbbww.................bbww........wwbwwwww.................xxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxxxwwwbbw................bbwwwww....wwwbbwwww..................xxxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxxxwwwwbb...4...U........bwwwwwwwwwwwwbbwww....................xxxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxxxxwwwwbbb...........bbbbbwwwwwwwwwbbbwww....................xxxxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxxxxwwwwwwbbbb.....bbbbwwwbbbbwwwbbbbwwww....................xxxxxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxxxxwwwwwwwwwbbbbbbbwwwwwwwwwbbbbbwwwww......4.....4........xxxxxxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww......................xxxxxxxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwww.......................xxxxxxxxxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwww........................xxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxxxxxxxxxxxxxxxxxwwwwwww......................xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...@.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_LOM_LOBON; - mons_array[1] = MONS_GIANT_ORANGE_BRAIN; - mons_array[2] = MONS_RAKSHASA; - mons_array[3] = MONS_WIZARD; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_NORTH; -} - - -static char cerebov(char vgrid[81][81], FixedVector& mons_array) -{ // you might not want to teleport too much on this level - - // unless you can reliably teleport away again. - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcat(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[7], "...............................xxxxxxxxx"); - strcat(vgrid[8], ".............vvvvv.............xxxxxxxxx"); - strcat(vgrid[9], ".............v$$$v.............xxxxxxxxx"); - strcat(vgrid[10], ".............v|||v.............xxxxxxxxx"); - strcat(vgrid[11], ".............v$$$v.............xxxxxxxxx"); - strcat(vgrid[12], ".vvvvv...vvvvvvvvvvvvv...vvvvv.xxxxxxxxx"); - strcat(vgrid[13], ".v|$|vvvvv...........vvvvv$|$v.xxxxxxxxx"); - strcat(vgrid[14], ".v$|$v.....vvvvvvvvv.....v|$|v.xxxxxxxxx"); - strcat(vgrid[15], ".v|$|v.vvvvvvvvOvvvvvvvv.v$|$v.xxxxxxxxx"); - strcat(vgrid[16], ".vvvvv.vvvvvv..3..vvvvvv.vvvvv.xxxxxxxxx"); - strcat(vgrid[17], "...v...vv.....vvv.....vv...v...xxxxxxxxx"); - strcat(vgrid[18], "...v.vvvv....vv1vv....vvvv.v...xxxxxxxxx"); - strcat(vgrid[19], "...v.vv......v...v......vv.v...xxxxxxxxx"); - strcat(vgrid[20], "...v.vvvv.............vvvv.v...xxxxxxxxx"); - strcat(vgrid[21], "...v...vv..2.......2..vv...v...xxxxxxxxx"); - strcat(vgrid[22], ".vvvvv.vv..2.......2..vv.vvvvv.xxxxxxxxx"); - strcat(vgrid[23], ".v|$|v.vv.............vv.v$|$v.xxxxxxxxx"); - strcat(vgrid[24], ".v|$|v.vv...vv...vv...vv.v$|$v.xxxxxxxxx"); - strcat(vgrid[25], ".v|$|v.vv...vv+++vv...vv.v$|$v.xxxxxxxxx"); - strcat(vgrid[26], ".vvvvv.vvvvvvv...vvvvvvv.vvvvv.xxxxxxxxx"); - strcat(vgrid[27], "....v..vvvvvvv...vvvvvvv..v....xxxxxxxxx"); - strcat(vgrid[28], "....vv...................vv....xxxxxxxxx"); - strcat(vgrid[29], ".....vv.vvvvv..2..vvvvv.vv.....xxxxxxxxx"); - strcat(vgrid[30], "......vvv|||v.....v$$$vvv......xxxxxxxxx"); - strcat(vgrid[31], "........v|$|vv...vv$|$v........xxxxxxxxx"); - strcat(vgrid[32], "........v|||v.....v$$$v........xxxxxxxxx"); - strcat(vgrid[33], "........vvvvv.....vvvvv........xxxxxxxxx"); - strcat(vgrid[34], "...............................xxxxxxxxx"); - strcat(vgrid[35], "...............@...............xxxxxxxxx"); - - mons_array[0] = MONS_CEREBOV; - mons_array[1] = MONS_BALRUG; - mons_array[2] = MONS_PIT_FIEND; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_NORTHEAST; -} - - -static char gloorx_vloq(char vgrid[81][81], FixedVector& mons_array) -{ - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcpy(vgrid[36], "xxxxxxxxxxxxxxxxxxxxxxx@.xxxxxxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxx..............................x"); - strcpy(vgrid[38], "xxxxxxxxx..............................x"); - strcpy(vgrid[39], "xxxxxxxxx..............................x"); - strcpy(vgrid[40], "xxxxxxxxx.x.x.x.x.x.x.x..x.x.x.x.x.x.x.x"); - strcpy(vgrid[41], "xxxxxxxxx..............................x"); - strcpy(vgrid[42], "xxxxxxxxx.x.xxxx=xxxxxxxxxxxx=xxxxxx.x.x"); - strcpy(vgrid[43], "xxxxxxxxx...xx....................xx...x"); - strcpy(vgrid[44], "xxxxxxxxx.x.x..ccccc..4..4..ccccc..x.x.x"); - strcpy(vgrid[45], "xxxxxxxxx...x.cc.3............3.cc.x...x"); - strcpy(vgrid[46], "xxxxxxxxx.x.x.c..ccccc.cc.ccccc..c.x.x.x"); - strcpy(vgrid[47], "xxxxxxxxx...x.c.cc.....cc.....cc.c.x...x"); - strcpy(vgrid[48], "xxxxxxxxx.x.x.c.c.2...cccc...2.c.c.x.x.x"); - strcpy(vgrid[49], "xxxxxxxxx...x...c...ccc..ccc...c...=...x"); - strcpy(vgrid[50], "xxxxxxxxx.x.x.3.....2..1O..2.....3.x.x.x"); - strcpy(vgrid[51], "xxxxxxxxx...=...c...ccc..ccc...c...x...x"); - strcpy(vgrid[52], "xxxxxxxxx.x.x.c.c.2...cccc...2.c.c.x.x.x"); - strcpy(vgrid[53], "xxxxxxxxx...x.c.cc.....cc.....cc.c.x...x"); - strcpy(vgrid[54], "xxxxxxxxx.x.x.c..ccccc.cc.ccccc..c.x.x.x"); - strcpy(vgrid[55], "xxxxxxxxx...x.cc.3............3.cc.x...x"); - strcpy(vgrid[56], "xxxxxxxxx.x.x..ccccc..4..4..ccccc..=.x.x"); - strcpy(vgrid[57], "xxxxxxxxx...xx....................xx...x"); - strcpy(vgrid[58], "xxxxxxxxx.x.xxxx=xxxx=xxxxxxxx=xxxxx.x.x"); - strcpy(vgrid[59], "xxxxxxxxx..............................x"); - strcpy(vgrid[60], "xxxxxxxxx.x.x.x.x.x.x.x..x.x.x.x.x.x.x.x"); - strcpy(vgrid[61], "xxxxxxxxx..............................x"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_GLOORX_VLOQ; - mons_array[1] = MONS_EXECUTIONER; - mons_array[2] = MONS_DEMONIC_CRAWLER; - mons_array[3] = MONS_SHADOW_DEMON; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_SOUTHWEST; -} - - -static char beehive(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxaaaaaaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxxxxxxxxaaaaaaaaaaaRaaaaaaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxxxxxaaaaaaaaaaRa2aaR1RaaRa2aaaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxxxxxxxxaaaaaaaaaaRa2a3R3aRaRaRaaaaaaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxxxxxxxxxxxxaaaaRaRaRaaa3aaa3aRa.a.aaaaaaaaaaaxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxxxxxxaaaaaaRa.aRa2a2a2a2aRaRa.a.a3aaaaaaaaaaaaaaxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxxxxx4aaaaaaaaa.aaRaRaa2aa2aaRaaa.aa3a33aaaaaaaaaa.44xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxxx.4aaaaaaa.222a3a.aaaRaaa.aaa.R3aa3a3aaaaaaaa.....4xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxx....aaaaaaa.aRa.a3aRaRa.a3a.a.a.a.aRa2aaaaaa....xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxx...aaaaaa3a3a.a.a.a3aRa2aRa3a.a.aRaRa.aaaaa...xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxxxx...aa2aRa3a3a3aRa.a3a.a.a.a.a.a.a.a3a.aaa...xxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxxxx...aaa.a.a.a2a.aaa.aRaRa2a.a2a3a.a2aaaa..T..xxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxx.....a2a.a2a.aRaaaaa3a.a.aaa3a3a3a3a.a.........xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxxx.4...aaRRaa.a2a.a3a3a3a.aaa.a.aRa.a.aa..4.......xxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxx......a.a.aaa.a3a.a.a.a.aaa2a.a2a.a.aRaa.....4...xxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxx.....aa3a2aaa.a.a.a3a3a3a3aRaaa.a2a.a2aa........xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxxx...aaaa.a2aRa.a.a2aaa.a.a.a.aaa.a.aaaa.....xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxxxxx..aaa.a.a.a.a.a.a.aaa2a.a3a2a.a2aaa.....xxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxxxxxxx.aaaa3a.a2aRa.a.aaaRa.a.aa.a.aaa....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxxxxxxxx...aaaaRa.a3a3a.a.a.aaa.aa.aa....4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxxxxxx........aa.a2a.a.aaa2aa.aa.aaa....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxxxxxx....4.....a.a2a2a.a2a.a2a.......4.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxxx.............a.a.a.a.a.a.....4....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxxx..............4..a.a.a......4...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxx.................a.a.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxxx........................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxxx.....4...T............xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxxxx.......................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxxxx.........................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxxxx.................T.........xxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxxxxx.......4.....................xxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxxxx..............xx...............xxxxxx....xxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxxxx............xxxxx........4......xxxx..4....xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxxxx..T..........xxx................xxxxx...T.xxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxxxx............xxx........T.........xxx........xxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxxxx....4........xx....................x..........xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxxx...............x.x...xxx...............xx.xxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxxx.........4...........xxx..................xxxxxxxxxxxxxxxxxxaaaaaxxxxx"); - strcpy(vgrid[47], "xxxxxxxxx.....4.....................4......4...........4...xxxxxxxxxxaa5a5aaxxxx"); - strcpy(vgrid[48], "xxxxxxxxx.................................................wwwwwwwwxxxa5*|*5axxxx"); - strcpy(vgrid[49], "xxxxxxxxx............x...x...T.....xxxx.................wwwwwwwwwwwwxaa*|*aaxxxx"); - strcpy(vgrid[50], "xxxxxxxxxx.........xx.............xxxxx................wwwwwwwwwwwwwwxaa5aaxxxxx"); - strcpy(vgrid[51], "xxxxxxxxxxx.......x..................xxx....4..........wwwwwwwwwwwwwwwxa5axxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxx.....xxx...4...........................xxxx.4wwwwwwwwwwwwwwwa=axxxxxx"); - strcpy(vgrid[53], "xxxxxxxxxxxx..xxx.............xx....(.........xxxxxxxx....wwwwwwwwwwwwwwaaxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxxxxxxxxx.............xxxx..................xxxx......wwwwwwwwwwxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxxxxxxxxxx....{..}..xxxxxx..]......xxx...........4.wwwwwwwwwwwwxxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxxxxxxxxxxx........xxx........xxxxxx....4....wwwwwwwwwwwwwwxxxxxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxxxxxxxxxxxxxxxxx..[.xxx........xxx)....wwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxxxxxxxxxxxxxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_QUEEN_BEE; - mons_array[1] = MONS_KILLER_BEE; - mons_array[2] = MONS_KILLER_BEE_LARVA; - mons_array[3] = MONS_PLANT; - mons_array[4] = MONS_YELLOW_WASP; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_ENCOMPASS; -} - - -static char vaults_vault(char vgrid[81][81], FixedVector& mons_array) -{ // last level of the vaults -- dungeon.cc will change all these 'x's - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxxxx....xxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxx..x.........................x....xxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxx..x.xxxxxxxxxxx..xxxxxxxxxx.x....xxxxx.................xxxxx..xxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxx..x.x*.*.*.*.*x..x........x.x....xxx..........8..........xxx..xxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxx..x.x.*.*.*.*.x..x........x.x....xxx.....................xxx..xxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxx..x.x*.*.*.*.*x..x...||...x.x....xx......9........9.......xx..xxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxx..x.x.*.*.*.*.x..x...||...x.x....xx.......................xx..xxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxx..x.x*.*.*.*.*x..x...||...x.x....xx......xxxxxxxxxxx......xx..xxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxx..x.x.*.*.*.*.x..x........x.x....xx......x.........x......xx..xxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxx..x.x*.*.*.*.*+..+........x.x....xx....xxx..........xx....xx..xxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxx..x.x.*.*.*.*.xxxxxxxxxxxxx.x....xx.9..x....xxxxx....x..8.xx..xxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxx..x.xxxxxxxxxxx9998.........x....xx....x...xx$$$xx...x....xx..xxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxx..x...........899xxxxxxxxxx.x....xx....x..xx$***$xx..x....xx..xxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxx..x.xxxxxxxxxxx99x........x.x....xx....x..x$$*|*$$x..x....xx..xxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxx..x.x.....|...x88x.$$$$$$.x.x....xx..8.x..xx$***$xx..x....xx..xxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxx..x.x.|..|..|.x..x.$$$$$$.x.x....xx....x....x$$$xx...x..9.xx..xxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxx..x.x.........x..x.$$$$$$.x.x....xx....xxx..xxxxx..xxx....xx..xxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxx..x.x.|..|..|.x..x.$$$$$$.x.x....xx......x.........x......xx..xxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxx..x.x.........x..x.$$$$$$.x.x....xx......xxxxxxxxxxx......xx..xxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxx..x.x|..|..|..x..x.$$$$$$.x.x....xxx.....................xxx..xxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxx..x.x.........x..+........x.x....xxx......9.........9....xxx..xxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxx..x.xxxxxxxxx+x..xxxxxxxxxx.xx.11....xx................xxxxx..xxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxx..x...........x..x...........x1111...xxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxx..1....1..xxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxx..........................xx1..(}..1..........................xxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxx...........................11.[..{.11.........................xxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxx............................1..])..1..........................xxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxx.............................1....1...........................xxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxx.....1111..x.xxx.xxxxxxxxxxxxxxxxxx..xxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.....11..........................x..xxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxx..x.x.x.x.x.x.x.x|x.x.x.x.x................................x..xxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxx..xx.x|x.x.x.x.x.x.x.x.x.x.x.....x.........................x..xxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxx..x.x.x.x.x.x.x.x9x.x.x.x.x.x..........8..........9........x..xxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.xx....x..9......................x..xxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxx..x.x.x.x.x.x.x.x.x.x|x.x.x.x....x.........................x..xxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxx..xx.x8x.x.x|x.x.x.x.x.x.x.xx....x..............9...9......x..xxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxx..x.x.x.x.x9x.x.x.x.x.x.x.x.x...........8..................x..xxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.xx....x..9......................x..xxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxx..x.x.x.x.x.x.x.x.x|x.x9x.x.x....x.........................x..xxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.xx....x...................9.....x..xxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxx..x.x.x9x.x.x.x.x.x.x.x.x.x.x....x....9......8.............x..xxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxx..xx.x.x.x.x.x.x9x.x.x.x.x.xx....x.........................x..xxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxx..x.x.x.x.x.x.x.x.x.x.x.x.x.x....x.........................x..xxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxx..xx.x.x.x.x.x.x.x.x.x.x.x.xx....x.......9......9..........x..xxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxx..x.x.x.x.x.x.x.x.x.x8x.x.x.x....x.....................8...x..xxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxx..xx.x8x.x.x.x.x.x.x.x.x.x.xx....x.....................||.|x..xxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxx..x.x|x.x.x.x.x.x.x|x.x.x.x.x....x.....................|...x..xxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxx..xx.x.x.x.x.x.x8x.x.x.x.x.xx....x......8..................x..xxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxx..x.x.x.x.x.x.x.x.x.x.x.x.x.x....x..........8..8...8.....||x..xxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxx..xO.x.x.x.x.x.x.x.x.x.x|x.xx....x.....................|.||x..xxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxx..xxxxxxxxxxxxxxxxxxxxxxxxxxx....xxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_VAULT_GUARD; - mons_array[1] = RANDOM_MONSTER; - mons_array[2] = RANDOM_MONSTER; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_ENCOMPASS; -} - - -static char snake_pit(char vgrid[81][81], FixedVector& mons_array) -{ // Hey, this looks a bit like a face ... - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcpy(vgrid[36], "xxxxxxxxxxxxxxxxxxxxxxx..@.xxxxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxxxxxxxxxxx.............xxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxxxxxx....x.............x..xxxxx"); - strcpy(vgrid[39], "xxxxxxxxxxxx....2.x.............x.2..xxx"); - strcpy(vgrid[40], "xxxxxxxxxxx.....2.x....x.....x..x..3.xxx"); - strcpy(vgrid[41], "xxxxxxxxxxx.....22x.............xx.2..xx"); - strcpy(vgrid[42], "xxxxxxxxxxx.......xx..x........xx..3..xx"); - strcpy(vgrid[43], "xxxxxxxxxx.....x23.xx....T...xxx.44...xx"); - strcpy(vgrid[44], "xxxxxxxxxx......4.4.x.........x.333....x"); - strcpy(vgrid[45], "xxxxxxxxxx......3.x4...x.......4x4.....x"); - strcpy(vgrid[46], "xxxxxxxxxx.......3.......x.............x"); - strcpy(vgrid[47], "xxxxxxxxxx..c......3.........x.......c.x"); - strcpy(vgrid[48], "xxxxxxxxx...cc...................3..cc.x"); - strcpy(vgrid[49], "xxxxxxxxx...cc..........4.4.........cc.x"); - strcpy(vgrid[50], "xxxxxxxxx...cc...3...x........2.....cc.x"); - strcpy(vgrid[51], "xxxxxxxxx...cc.........1...1.......cc..x"); - strcpy(vgrid[52], "xxxxxxxxxx..cc.....1.....1.....1..ccc.xx"); - strcpy(vgrid[53], "xxxxxxxxxx...ccc..................cc..xx"); - strcpy(vgrid[54], "xxxxxxxxxx....cccc....3333333.....cc..xx"); - strcpy(vgrid[55], "xxxxxxxxxx.....ccccccc...........cc...xx"); - strcpy(vgrid[56], "xxxxxxxxxx........cccccccO...ccccc....xx"); - strcpy(vgrid[57], "xxxxxxxxxxx........cccccccccccccc....xxx"); - strcpy(vgrid[58], "xxxxxxxxxxx.........cccccccccccc.....xxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxx.......................xxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxx..................xxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxx.......xxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_GREATER_NAGA; - mons_array[1] = MONS_NAGA; - mons_array[2] = MONS_NAGA_MAGE; - mons_array[3] = MONS_NAGA_WARRIOR; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_SOUTHWEST; -} - - -static char elf_hall(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxcccccccccccccccccxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxcc*|*|*|**|||||c$ccxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxcc*$*|*|*|*|||||c$$ccxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxcc*$|*$***$$|||||c|$$ccxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxxcc*$*|**ccccccccccc$$$$ccx"); - strcpy(vgrid[12], "xxxxxxxxxxxxxxc*|*$*$ccc.....2..c+$|$$cx"); - strcpy(vgrid[13], "xxxxxxxxxxxxxxc$*$*ccc...........c$$$$cx"); - strcpy(vgrid[14], "xxxxxxxxxxxxxxc||**cc...5.......4cc$|$cx"); - strcpy(vgrid[15], "xxxxxxxxxxxxxxc*$$cc........3..ccccccccx"); - strcpy(vgrid[16], "xxxxxxxxxxxxxxc$+ccc.....2....cc.....5cx"); - strcpy(vgrid[17], "xxxxxxxxxxxxxxc$c....5.......cc.......cx"); - strcpy(vgrid[18], "xxxxxxxxxxxxxxccc......5....cc..2....ccx"); - strcpy(vgrid[19], "xxxxxxxxxxxxxxxxc..........cc.......ccxx"); - strcpy(vgrid[20], "xxxxxxxxxxxxxxxcc..1..U..........4..ccxx"); - strcpy(vgrid[21], "xxxxxxxxxxxxxxcc.....................ccx"); - strcpy(vgrid[22], "xxxxxxxxxxxxxxc...........3...........cx"); - strcpy(vgrid[23], "xxxxxxxxxxxxxxc.......2.......3.......cx"); - strcpy(vgrid[24], "xxxxxxxxxxxxxxc..2................2..5cx"); - strcpy(vgrid[25], "xxxxxxxxxxxxxxc......x.........x......cx"); - strcpy(vgrid[26], "xxxxxxxxxxxxxxc.....xx.........xx.....cx"); - strcpy(vgrid[27], "xxxxxxxxxxxxxxc2...xxx....1....xxx.4..cx"); - strcpy(vgrid[28], "xxxxxxxxxxxxxxc..xxxx...........xxxx..cx"); - strcpy(vgrid[29], "xxxxxxxxxxxxxxc.xxx.....cc.cc.....xxx.cx"); - strcpy(vgrid[30], "xxxxxxxxxxxxxxc.x.....cccc.cccc.....x.cx"); - strcpy(vgrid[31], "xxxxxxxxxxxxxxc.3...cccxxc.cxxccc.3...cx"); - strcpy(vgrid[32], "xxxxxxxxxxxxxxc...cccxxxxc.cxxxxccc...cx"); - strcpy(vgrid[33], "xxxxxxxxxxxxxxc.cccxxxxxxc.cxxxxxxccc.cx"); - strcpy(vgrid[34], "xxxxxxxxxxxxxxcccxxxxxxxxc.cxxxxxxxxcccx"); - strcpy(vgrid[35], "xxxxxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxx"); - - mons_array[0] = MONS_DEEP_ELF_HIGH_PRIEST; - mons_array[1] = MONS_DEEP_ELF_DEMONOLOGIST; - mons_array[2] = MONS_DEEP_ELF_ANNIHILATOR; - mons_array[3] = MONS_DEEP_ELF_SORCERER; - mons_array[4] = MONS_DEEP_ELF_DEATH_MAGE; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_NORTHWEST; -} - - -// Slime pit take is reduced pending an increase in difficulty -// of this subdungeon. -- bwr -static char slime_pit(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..xxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx....................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxxxxxxxxxxxxxx......................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxxxxxxxxxxxxxx..........................x.xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxxxxxxxxxxxxxxxx.............................xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxxxxxxxxxxxxxx.................................xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxxxxxxxxxxxxxxxx..................................xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxxxxxxxxxxxxxx....(................................xxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxxxxxxxxxx..........................................xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxxxxxxxxxxx..........................................xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxxxxxxxxx............................................xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxxxxxxxx............................................xxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxxxxxx.....................ccc..ccc............]......xxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxxxxxxx...................cccc2ccccc...................xxxxxxxxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxxxxxx...................cc*cc..cc*cc....................xxxxxxxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxxxxxx..................cc***cc4c***cc..................xxxxxxxxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxxxxxx..................cc*|*cc..cc*|*cc..................xxxxxxxxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxxxxxx.................cc*|P|*c4cc*|P|*cc.................xxxxxxxxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxxxxxx.................cc**|*cc..cc*|**cc....................xxxxxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxxxxx..................ccc**c|cc4c|c**ccc...................xxxxxxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxxxxx..................cccccccc..cccccccc....................xxxxxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxxx...................c.4.c.4.1..4.c.4.c.....................xxxxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxxxx...................2.c.4.c..3.c.4.c.2.....................xxxxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxxx..........)........cccccccc..cccccccc.....................xxxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxxx...................ccc**c|cc4c|c**ccc.....................xxxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxx....................cc**|*cc..cc*|**cc....................xxxxxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxxx....................cc*|P|*c4cc*|P|*cc....................xxxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxx.....................cc*|*cc..cc*|*cc....................xxxxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxxx.....................cc***cc4c***cc.....................xxxxxxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxxxxx.....................cc*cc..cc*cc......................xxxxxxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxxxxxx.....................cccc2ccccc......................xxxxxxxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxxxxxxx.....................ccc..ccc.......................xxxxxxxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxxxxxxx...........................................[.........xxxxxxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxxxxxx......................................................xxxxxxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxxxxxx..............................................xxxxx...xxxxxxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxxxxxxx...........................................xxxxxxxx.xxxxxxxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxxxxxxx..........................................xxxxxxxxx.xxxxxxxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxxxxxxxxx........................................xxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxxxxxxxxx.........................................xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxxxxxxxxxxx.......................................xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxxxxxxxxxxxx.....................................xxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxxxxxxxxxxxxxx.............................}......xxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxxxxxxxxxxxxxxxx.................................xxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxxxxxxxxxxxxxxxx..............................xxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxxxxxxxxxxxxxxxxxx............................xxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxxxxxxxxxxxxxxxxxxxx...........{........xxx..xxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_ROYAL_JELLY; - mons_array[1] = MONS_ACID_BLOB; - mons_array[2] = MONS_GREAT_ORB_OF_EYES; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_ENCOMPASS; - -} - - -static char hall_of_blades(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxccc....cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.....cccxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxcc......cc...cc...cc...cc...cc...cc...cc...cc...cc...cc.......ccxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxc..............................................................cxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxc..........c..............c..............c..............c......cxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxc.........ccc............ccc............ccc............ccc.....cxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxc........ccccc..........ccccc..........ccccc..........ccccc....cxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxc.........ccc............ccc............ccc...........ccccc....cxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxc..........c..............c..............c.............ccc.....cxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxc......................................................ccc.....cxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxc.......................................................c......cxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxc..............................................................cxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxc..............................................................cxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxc..............................................................cxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxc..............................................................cxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxc..............................................................cxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxc..............................................................cxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxc.......................................................c......cxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxc......................................................ccc.....cxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxc..........c..............c..............c.............ccc.....cxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxc.........ccc............ccc............ccc...........ccccc....cxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxc........ccccc..........ccccc..........ccccc..........ccccc....cxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxc.........ccc............ccc............ccc............ccc.....cxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxc..........c..............c..............c..............c......cxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxc..............................................................cxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxc.......cc...cc...cc...cc...cc...cc...cc...cc...cc...cc.......ccxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxcc.....cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.cccc.....cccxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxccc...ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxcccc.............................cccccccccccccccccccccccccccccccxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxcccccccccccccccccccccccccccccc.@.cccccccccccccccccccccccccccccccxxxxxxxx"); - - mons_array[0] = MONS_DANCING_WEAPON; - mons_array[1] = RANDOM_MONSTER; - mons_array[2] = RANDOM_MONSTER; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_NORTH; -} - - -static char hall_of_Zot(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxccccccccccccccccxxxxxxxxxxxxxxxxxxxxxxxccccccccccccccxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxcccc..............ccccxxxxxxxxxxxxxxxxxcccc............ccccxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxcc....................cccxxxxxxxxxxxxxccc..................ccxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxcc...........3...........ccxxxxxxxxxxxcc...........3.........ccxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxc..8......................cXXXXXXXXXXXc....................8..cxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxc...................8.....XXX...1...XXX....8..................cxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxcc........................XX..1...1..XX......................ccxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxcc.......................X1.........1X.....................ccxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxcc.....4....2..............1..Z..1............2....4.....ccxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxxcc.......................X1.........1X.....................ccxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxcc........................XX..1...1..XX......................ccxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxc...................8.....XXX...1...XXX....8..................cxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxc...8.....................cXXXXXXXXXXXc...................8...cxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxcc..........8............ccccccccccccccc..........8..........ccxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxxcc....................ccccccccccccccccccc..................ccxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxxcc................ccccccccccccccccccccccccc..............ccxxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxxxxccF111FcccccccccccccccccccccccccccccccccccccccccccF111Fccxxxxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxxxcc................^^1.ccccccccccccccccc.1^^..............ccxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxxcc.................cc1...ccccccccccccc...1cc...............ccxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxcc.............8.....ccc...ccccccccccc...ccc...8.............ccxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxxc....8................ccc...............ccc...........8...8...cxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxxc.......8.....8...8...cxcc.............ccxc...................cxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxxc.....................cxxc.............cxxc.......8...........cxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxxc.....................cxxcc.1...1...1.ccxxc............8....8.cxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxxc.......8....8.....8..cxxxc...........cxxxc....8..............cxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxc.....................cxxcc...........ccxxc..........8........cxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxxcc...5...............ccxxc.............cxxcc..............8..ccxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxxcc........8........ccxxcc.............ccxxcc....8....5.....ccxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxxxcc...............ccxxxc...............cxxxcc.............ccxxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxxxxcccccccccccccccccxxxxcccccccc@ccccccccxxxxcccccccccccccccxxxxxxxxxxx"); - - mons_array[0] = MONS_ORB_GUARDIAN; - mons_array[1] = MONS_KILLER_KLOWN; - mons_array[2] = MONS_ELECTRIC_GOLEM; - mons_array[3] = MONS_ORB_OF_FIRE; - mons_array[4] = MONS_ANCIENT_LICH; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_NORTH; -} - - -static char temple(char vgrid[81][81], FixedVector& mons_array) -{ // this is the ecumenical temple level - UNUSED( mons_array ); - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxxxxcc............<............cxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxxxxxxxxxcc...........................cxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxxxxxxxxxcc.............................cxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxxxxxxxxxxcc...............................cxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxxxxxxxxxxxcc.................................cxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxxxxxxxxxxxcc...................................cxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxxxxxxxxxcc.....................................cxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxxxxxxxxxxcc.......................................cxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxxxxxxxxxcc.........................................cxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxxxxxxxxcc...........................................cxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxxxxxxcc.............................................cxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxxxxxxcc...............................................cxxxxxxxxxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxxxxxcc.................................................cxxxxxxxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxxxcc...................................................cxxxxxxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxxxcc..........................B..........................cxxxxxxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxxcc.......................................................cxxxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxcc.....................B.............B.....................cxxxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxcc...........................................................cxxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxc.................B.........................B.................cxxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxc..............................T..............................cxxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxc..............B...............................B..............cxxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxc(....................T.................T....................{cxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxc.............................................................cxxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxc................B...........................B................cxxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxcc...........................................................ccxxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxxcc............................T............................ccxxxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxxxcc.......................................................ccxxxxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxxxxcc.....................................................ccxxxxxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxxxxxcc...................................................ccxxxxxxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxxxxxxcc.................................................ccxxxxxxxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxxxxxxxcc...............B................B..............ccxxxxxxxxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxxxxxxxxcc.............................................ccxxxxxxxxxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxxxxxxxcc.....................B.....................ccxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxxxxxxxxxxcc.........................................ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxxxxxxxxxxxcc.......................................ccxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxxxxxxxxxxxxcc.....................................ccxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxxxxxxxxxxxcc...................................ccxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxxxxxxxxxxxxxcc.................................ccxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxxxxxxxxxxxxxxcc...............................ccxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxxxxxxxxxcc.............................ccxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxxxxxxxcc...........................ccxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxxxcc............[............ccxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - return MAP_ENCOMPASS; -} - - -static char tomb_1(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxx(.............................[..............................{xxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxx..........ccccccccccccccccccccccccccccccccccccccccccc.........xxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxx..........ccccccccccccccccccccccccccccccccccccccccccc.........xxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxx..........cc..........................^............cc.........xxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxx..........cc.........^....................^........cc.........xxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxx..........cc..ccccccccccccccccccccccccccccccccccc..cc.........xxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxx..........cc..c....^....^..c................c.^)c..cc.........xxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxx..........cc..c..ccccccccc.c..3.............c.^.c..cc.........xxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxx..........cc..c..c222c111c.c...............5c..^c..cc.........xxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxx..........cc..c..c2c222c.^.c......2.........+cccc..cc.........xxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxx..........cc..c..ccccccccccc..........3......5..c.^cc.........xxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxx..........cc..c.................................c..cc.........xxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxx..........cc..c..........................3......c..cc.........xxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxx..........cc^.cccccccccccccc.......2............c..cc.........xxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxx..........cc..c............c....................c..cc.........xxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxx..........cc..c............c.................3..c..cc.........xxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxx..........cc..c..cccccccc..c..........2.........c^5cc.........xxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxx..........cc..c..c.^.c11c..c....................c..cc.........xxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxx..........cc..c..c.c.c11c..c...3................c..cc.........xxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxx..........cc..c..c^c.11cc..c..............2.....c..cc.........xxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxx..........cc..c..c.cccccc..c.......2............c..cc.........xxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxx..........cc..c..c..^..^...c.................2..c..cc.........xxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxx..........cc5^c..ccccccccccc....................c..cc.........xxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxx..........cc..c.................................c..cc.........xxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxx..........cc..c.................................c..cc.........xxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxx..........cc..cccccccccccccc..^.^..cccccccccccccc..cc.........xxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxx..........cc..c...........ccc+++++ccc........^..c.^cc.........xxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxx..........cc..c.^.....^...cc.2...2.cc......^....c..cc.........xxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxx..........cc..c..ccccccc..cc.F...F.cc..ccccccc..c..cc.........xxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxx..........cc..c..cc.322c..cc.......cc..c22..cc..c..cc.........xxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxx..........cc..c..c].c22c..cc.......cc..c22c.}c^.c..cc.........xxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxx..........cc..c..cccc..c.^cc.G...G.cc..c3.cccc..c..cc.........xxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxx..........cc..c.....^..c..cc.......cc.^c........c..cc.........xxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxx..........cc..c........c..cc.......cc..c....^...c..cc.........xxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxx..........cc^.cccccccccc..cc.G...G.cc..cccccccccc.^cc.........xxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxx..........cc......^.......cc.......cc..........^...cc.........xxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxx..........cc...........^..cc.......cc.....^........cc.........xxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxx..........cccccccccccccccccc.G...G.cccccccccccccccccc.........xxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxx..........cccccccccccccccccc.......cccccccccccccccccc.........xxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxx.............................G...G............................xxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxx...........................4.......4..........................xxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxx...........................4..V.V..4..........................xxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxx...........................4.......4..........................xxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxx..............................................................xxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_MUMMY; - mons_array[1] = MONS_GUARDIAN_MUMMY; - mons_array[2] = MONS_MUMMY_PRIEST; - mons_array[3] = MONS_SPHINX; - mons_array[4] = MONS_GREATER_MUMMY; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_ENCOMPASS; -} - - -static char tomb_2(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxxxxxxxxxcc{...c......c.....3....c........c.......ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxxxxxxxxxxxcc....c.....^c^........^c......2^c.......ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxxxxxxxxxxxcc....c...2.^+..2.....2^+^..2....+.......ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxxxxxxxxxxxcc.3.^c^.....c^.........c^2.....^c^......ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxxxxxxxxxxcc...^+^.....c..........c........c...2...ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxxxxxxxxxxxccccc+ccccccccccccccccccccccccccccccc....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxxxxxxxxxxxcc..^.c.............................c....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxxxxxxxxxxcc....c.............................c..3.ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxxxxxxxxxxxcc....c..ccc4.................4ccc..c....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxxxxxxxxxxxcc....c..ccc...................ccc..c..2.ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxxxxxxxxxxxcc....c..ccc.........1.........ccc..c)..}ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxxxxxxxxxxxcc.3..c..ccc.....2.......2.....ccc..cccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxxxxxxxxxxxxcc....c.............................c....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxxxxxxxxxxxxcc....c.............................c^2..ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxxxxxxxxxxxxcc....c........c...........c........+....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxxxxxxxxxxxxcc]...c.............................c^2..ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxxxxxxxxxxxxccccccc.....3........(........3.....c....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxxxxxxxxxxxcc....c.............................c.^.^ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxxxxxxxxxxxxcc...^c........c...........c........ccc+cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxxxxxxxxxxxcc....+.............................c..^.ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxxxxxxxxxxxcc...^c.............................c....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxxxxxxxxxxxcc....c..ccc.....2.......2.....ccc..c..2.ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxxxxxxxxxxxxccccccc..ccc.........1.........ccc..c....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxxxxxxxxxxxcc....c..ccc...................ccc..c....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxxxxxxxxxxxcc...^+..ccc4.................4ccc..c2...ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxxxxxxxxxxxxcc....c.............................c....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxxxxxxxxxxxxccccccc.............................c..2.ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxxxxxxxxxxxxcc....c.............................ccc+cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxxxxxxxxxxxxcc....+cccc+ccccccccccccccc+ccccccccc.^.^ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxxxxxxxxxxxxcc.1.^^.c.^..c............c^.......c.3...ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxxxxxxxxxxxxcc...2..c.1..c.....1.1....c.....2..c.....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxxxxxxxxxxxxcc......c....c..1......1.^c..2.....c...2.ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxxxxxxxxxxxxcc..3...c.1..c...1...1..1^+........c.....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxxxxxxxxxxxxcc......c....c[...........c.......3c.....ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_MUMMY; - mons_array[1] = MONS_GUARDIAN_MUMMY; - mons_array[2] = MONS_MUMMY_PRIEST; - mons_array[3] = MONS_GREATER_MUMMY; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_ENCOMPASS; -} - - -static char tomb_3(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[2], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[3], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[4], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[5], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[6], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[7], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[8], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[9], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[10], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[11], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[12], "xxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[13], "xxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[14], "xxxxxxxxxxxxxxxxxxxccccccc.............................cccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[15], "xxxxxxxxxxxxxxxxxxxcccc...............cccccc..............ccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[16], "xxxxxxxxxxxxxxxxxxxccc...............cccccccc..............cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[17], "xxxxxxxxxxxxxxxxxxxccc.......4......ccccO4cccc......4......cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[18], "xxxxxxxxxxxxxxxxxxxccc............cccc......cccc...........cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[19], "xxxxxxxxxxxxxxxxxxxcc............cccc........cccc...........ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[20], "xxxxxxxxxxxxxxxxxxxcc............cccc........cccc...........ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[21], "xxxxxxxxxxxxxxxxxxxcc...........cccc..444444..cccc..........ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[22], "xxxxxxxxxxxxxxxxxxxcc.......................................ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[23], "xxxxxxxxxxxxxxxxxxxcc.......................................ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[24], "xxxxxxxxxxxxxxxxxxxcc.................222222................ccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[25], "xxxxxxxxxxxxxxxxxxxccc................223322...............cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[26], "xxxxxxxxxxxxxxxxxxxccc...3............223322............3..cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[27], "xxxxxxxxxxxxxxxxxxxcccc...............222222..............ccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[28], "xxxxxxxxxxxxxxxxxxxcccc....2..........................2...ccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[29], "xxxxxxxxxxxxxxxxxxxcccccc....2......................2....cccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[30], "xxxxxxxxxxxxxxxxxxxcccccccc............................cccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[31], "xxxxxxxxxxxxxxxxxxxccccccccc+ccc..................ccc+ccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[32], "xxxxxxxxxxxxxxxxxxxcccccccc....cc................cc....cccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[33], "xxxxxxxxxxxxxxxxxxxcccccc.......cc22222222222222cc......$cccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[34], "xxxxxxxxxxxxxxxxxxxcccc....^.....cc............cc..^.....$ccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[35], "xxxxxxxxxxxxxxxxxxxcccc.^.........cc..........cc.....^.^.$ccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[36], "xxxxxxxxxxxxxxxxxxxccc$...^...^..^.cc........cc..........$$cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[37], "xxxxxxxxxxxxxxxxxxxccc$$$...........cc222222cc.^........$$$cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[38], "xxxxxxxxxxxxxxxxxxxccc|$$$...........c......c.....^...$$$$$cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[39], "xxxxxxxxxxxxxxxxxxxccc||$$$$...^.....c......c^......$$$$$$$cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[40], "xxxxxxxxxxxxxxxxxxxccc|||||$$.....^..c......c......$$$$$$$$cccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[41], "xxxxxxxxxxxxxxxxxxxcccc|||||$........c......c...^.$$$$$$$$ccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[42], "xxxxxxxxxxxxxxxxxxxccccc||||$$..^....c......c.....$$$$$$$cccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[43], "xxxxxxxxxxxxxxxxxxxcccccc||||$.......c......c.....$$$$$$ccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[44], "xxxxxxxxxxxxxxxxxxxccccccc|||$$....^.c......c.^.^$$$$$$cccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[45], "xxxxxxxxxxxxxxxxxxxcccccccc|||$^....cc..{...cc...$$$$$ccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[46], "xxxxxxxxxxxxxxxxxxxccccccccc||$.....cc...(..cc..$$$$$cccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[47], "xxxxxxxxxxxxxxxxxxxcccccccccc|$...cccc..[...cccc$$$$ccccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[48], "xxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[49], "xxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[50], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[51], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[52], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[53], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[54], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[55], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[56], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[57], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[58], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[59], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[60], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcpy(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_MUMMY; - mons_array[1] = MONS_GUARDIAN_MUMMY; - mons_array[2] = MONS_MUMMY_PRIEST; - mons_array[3] = MONS_GREATER_MUMMY; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_ENCOMPASS; -} - + // Copy the map so we can monkey with it. + map_def def = mdef; + resolve_map(def); - -static char swamp(char vgrid[81][81], FixedVector& mons_array) -{ // NB - most of the 'x's here will be set to water in dungeon.cc - - for (unsigned char i = 0; i < 81; i++) - strcpy(vgrid[i], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - strcat(vgrid[36], "xxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[37], "xxxxxxxxxxx2xxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[38], "xxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[39], "xxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[40], "xxxxxxxxxx2x.xxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[41], "xxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[42], "xxxxxxxxxcc.ccxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[43], "xxxxxxxxcc...ccxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[44], "xxxxxxxcc3.2..ccxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[45], "xxxxxxcc.1.3.2.ccxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[46], "xxxxxccc....1.1cccxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[47], "xxxxxcc.1.32....ccxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[48], "xxxxxcc...3..1.3ccxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[49], "xxxxxcc2.1.3..2.ccxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[50], "xxxxxccc33..1..cccxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[51], "xxxxxxcccc3O3ccccxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[52], "xxxxxxxcccccccccxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[53], "xxxxxxxxcccccccxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[54], "xxxxxxxxxxcccxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[55], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[56], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[57], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[58], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[59], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[60], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[61], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[62], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[63], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[64], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[65], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[66], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[67], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[68], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - strcat(vgrid[69], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - mons_array[0] = MONS_SWAMP_DRAGON; - mons_array[1] = MONS_SWAMP_DRAKE; - mons_array[2] = MONS_HYDRA; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - mons_array[6] = RANDOM_MONSTER; - - return MAP_SOUTHEAST; -} - - -/* - NOTE: *Cannot* place 8,9 or 0 monsters in branch vaults which neither use the - normal mons_level function or are around level 35, or generation will crash. - - Remember, minivaults are always sidewards -*/ - -static char minivault_1(char vgrid[81][81], FixedVector& mons_array) -{ - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "..xxxx=xxx.."); - strcpy(vgrid[2], ".xx..x...xx."); - strcpy(vgrid[3], ".x....x...x."); - strcpy(vgrid[4], ".x...x....x."); - strcpy(vgrid[5], ".xx.x*x.x.=."); - strcpy(vgrid[6], ".=.x.x*x.xx."); - strcpy(vgrid[7], ".x....x...x."); - strcpy(vgrid[8], ".x...x....x."); - strcpy(vgrid[9], ".xx...x..xx."); - strcpy(vgrid[10], "..xxx=xxxx.."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} - - -static char minivault_2(char vgrid[81][81], FixedVector& mons_array) -{ - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "..xxxx.xxxx."); - strcpy(vgrid[2], "..xx.....xx."); - strcpy(vgrid[3], "..x.......x."); - strcpy(vgrid[4], "..x.......x."); - strcpy(vgrid[5], "......C....."); - strcpy(vgrid[6], "..x.......x."); - strcpy(vgrid[7], "..x.......x."); - strcpy(vgrid[8], "..xx.....xx."); - strcpy(vgrid[9], "..xxxx.xxxx."); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} - - -static char minivault_3(char vgrid[81][81], FixedVector& mons_array) -{ - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".cccccccccc."); - strcpy(vgrid[2], ".cccccccccc."); - strcpy(vgrid[3], ".cBcBcBcBcc."); - strcpy(vgrid[4], ".G.c.c.c.Bc."); - strcpy(vgrid[5], ".........Bc."); - strcpy(vgrid[6], ".........Bc."); - strcpy(vgrid[7], ".G.c.c.c.Bc."); - strcpy(vgrid[8], ".cBcBcBcBcc."); - strcpy(vgrid[9], ".cccccccccc."); - strcpy(vgrid[10], ".cccccccccc."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} - - -static char minivault_4(char vgrid[81][81], FixedVector& mons_array) -{ - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "....xwxx...."); - strcpy(vgrid[2], "..xxxwwxwx.."); - strcpy(vgrid[3], "..xwwwwwwx.."); - strcpy(vgrid[4], ".xwwxwwxwxx."); - strcpy(vgrid[5], ".xwwwwwwwwx."); - strcpy(vgrid[6], ".xwwxwwwxww."); - strcpy(vgrid[7], ".xxwwwwwwxx."); - strcpy(vgrid[8], "..wwwwxwwx.."); - strcpy(vgrid[9], "..xxxwwxxw.."); - strcpy(vgrid[10], "....xxww...."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; + return apply_vault_definition(def, map, marray, place); } - -static char minivault_5(char vgrid[81][81], FixedVector& mons_array) +// Mirror the map if appropriate, resolve substitutable symbols (?), +static void resolve_map(map_def &map) { - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".x.xxxxxxxx."); - strcpy(vgrid[2], ".x.x......x."); - strcpy(vgrid[3], ".x.x.xxxx.x."); - strcpy(vgrid[4], ".x.x.x**x.x."); - strcpy(vgrid[5], ".x.x.x**x.x."); - strcpy(vgrid[6], ".x.x.xx.x.x."); - strcpy(vgrid[7], ".x.x....x.x."); - strcpy(vgrid[8], ".x.xxxxxx.x."); - strcpy(vgrid[9], ".x........x."); - strcpy(vgrid[10], ".xxxxxxxxxx."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} - - -static char minivault_6(char vgrid[81][81], FixedVector& mons_array) -{ // Wizard's laboratory - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".ccccccc+cc."); - strcpy(vgrid[2], ".c........c."); - strcpy(vgrid[3], ".c........c."); - strcpy(vgrid[4], ".c..1.....c."); - strcpy(vgrid[5], ".c........c."); - strcpy(vgrid[6], ".cc+ccccccc."); - strcpy(vgrid[7], ".c***c3232c."); - strcpy(vgrid[8], ".c|**+2223c."); - strcpy(vgrid[9], ".c||*c3322c."); - strcpy(vgrid[10], ".cccccccccc."); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_WIZARD; - mons_array[1] = MONS_ABOMINATION_SMALL; - mons_array[2] = MONS_ABOMINATION_LARGE; - - return MAP_NORTH; - -} - - -static char minivault_7(char vgrid[81][81], FixedVector& mons_array) -{ // beehive minivault - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "....aaaa...."); - strcpy(vgrid[2], "..a2a2aaaa.."); - strcpy(vgrid[3], "..aaRa3a2a.."); - strcpy(vgrid[4], ".aa2aRa2aaa."); - strcpy(vgrid[5], ".a3aRa1aRa2."); - strcpy(vgrid[6], ".aa3aRaRa2a."); - strcpy(vgrid[7], ".aaa2a2a3aa."); - strcpy(vgrid[8], "..a3aRa2aa.."); - strcpy(vgrid[9], "...aa2aa2a.."); - strcpy(vgrid[10], "....aaaa...."); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_QUEEN_BEE; - mons_array[1] = MONS_KILLER_BEE; - mons_array[2] = MONS_KILLER_BEE_LARVA; - - return MAP_NORTH; -} - - -static char minivault_8(char vgrid[81][81], FixedVector& mons_array) -{ // lava pond - - strcpy(vgrid[0], "x.x.x.x.x.x."); - strcpy(vgrid[1], ".c.c.c.c.c.x"); - strcpy(vgrid[2], "x...l1l...c."); - strcpy(vgrid[3], ".c.llllll..x"); - strcpy(vgrid[4], "x.lllllll1c."); - strcpy(vgrid[5], ".c.llFGll..x"); - strcpy(vgrid[6], "x..llGFll.c."); - strcpy(vgrid[7], ".c1lllllll.x"); - strcpy(vgrid[8], "x..llllll.c."); - strcpy(vgrid[9], ".c...l1l...x"); - strcpy(vgrid[10], "x.c.c.c.c.c."); - strcpy(vgrid[11], ".x.x.x.x.x.x"); - - mons_array[0] = MONS_MOLTEN_GARGOYLE; - - return MAP_NORTH; -} - - -static char minivault_9(char vgrid[81][81], FixedVector& mons_array) -{ // evil zoo - UNUSED( mons_array ); + // Mirroring is possible for any map that does not explicitly forbid it. + // Note that mirroring also flips the orientation. + if (coinflip()) + map.hmirror(); - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".==========."); - strcpy(vgrid[2], ".==========."); - strcpy(vgrid[3], ".==========."); - strcpy(vgrid[4], ".===8888===."); - strcpy(vgrid[5], ".===8998===."); - strcpy(vgrid[6], ".===8998===."); - strcpy(vgrid[7], ".===8888===."); - strcpy(vgrid[8], ".==========."); - strcpy(vgrid[9], ".==========."); - strcpy(vgrid[10], ".==========."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; + if (coinflip()) + map.vmirror(); + // The map may also refuse to be rotated. + if (coinflip()) + map.rotate( coinflip() ); } - -static char minivault_10(char vgrid[81][81], FixedVector& mons_array) +static void apply_monsters( + const map_def &def, + FixedVector &marray) { - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xxxx..xxxx."); - strcpy(vgrid[2], ".x**x..x**x."); - strcpy(vgrid[3], ".x**+..+**x."); - strcpy(vgrid[4], ".xx+x..x+xx."); - strcpy(vgrid[5], "............"); - strcpy(vgrid[6], "............"); - strcpy(vgrid[7], ".xx+x..x+xx."); - strcpy(vgrid[8], ".x**+..+**x."); - strcpy(vgrid[9], ".x**x..x**x."); - strcpy(vgrid[10], ".xxxx..xxxx."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} - - -static char minivault_11(char vgrid[81][81], FixedVector& mons_array) -{ // multicoloured onion - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".+xxxxxxxx+."); - strcpy(vgrid[2], ".x........x."); - strcpy(vgrid[3], ".x.+cccc+.x."); - strcpy(vgrid[4], ".x.c....c.x."); - strcpy(vgrid[5], ".x.c.bb.c.x."); - strcpy(vgrid[6], ".x.c.bb.c.x."); - strcpy(vgrid[7], ".x.c....c.x."); - strcpy(vgrid[8], ".x.+cccc+.x."); - strcpy(vgrid[9], ".x........x."); - strcpy(vgrid[10], ".+xxxxxxxx+."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} - - -static char minivault_12(char vgrid[81][81], FixedVector& mons_array) -{ // closed box minivault - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xxxxxxxxxx."); - strcpy(vgrid[2], ".x>9$9$9$x."); - strcpy(vgrid[10], ".xxxxxxxxxx."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} - - -static char minivault_13(char vgrid[81][81], FixedVector& mons_array) -{ // little trap spiral - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xxxxxxxxxx."); - strcpy(vgrid[2], ".=.^x..=.9x."); - strcpy(vgrid[3], ".x.$=.^x..x."); - strcpy(vgrid[4], ".xxxxxxxx=x."); - strcpy(vgrid[5], ".x.8+|0x8.x."); - strcpy(vgrid[6], ".x8$x.|x..x."); - strcpy(vgrid[7], ".xx=xxxx=xx."); - strcpy(vgrid[8], ".x.9=^.x..x."); - strcpy(vgrid[9], ".x..x.^=9.x."); - strcpy(vgrid[10], ".xxxxxxxxxx."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} - + const std::vector &mids = def.mons.get_ids(); + size_t ndx = 0; + for (int i = 0, size = mids.size(); i < size; ++i) + { + int mid = mids[i]; + if (mid < 0) + { + mid = -100 - mid; + if (mid == DEMON_RANDOM) + mid = random2(DEMON_RANDOM); + mid = summon_any_demon( mid ); + } -static char minivault_14(char vgrid[81][81], FixedVector& mons_array) -{ // water cross - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".wwwww.wwww."); - strcpy(vgrid[2], ".wwwww.wwww."); - strcpy(vgrid[3], ".wwwww.wwww."); - strcpy(vgrid[4], ".wwwww.wwww."); - strcpy(vgrid[5], ".......wwww."); - strcpy(vgrid[6], ".wwww......."); - strcpy(vgrid[7], ".wwww.wwwww."); - strcpy(vgrid[8], ".wwww.wwwww."); - strcpy(vgrid[9], ".wwww.wwwww."); - strcpy(vgrid[10], ".wwww.wwwww."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; + if (ndx < marray.size()) + marray[ndx++] = mid; + } } -static char minivault_15(char vgrid[81][81], FixedVector& mons_array) /* lava pond */ +static void apply_vault_grid(const map_def &def, map_type map, + vault_placement &place) { - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "............"); - strcpy(vgrid[2], "....lll....."); - strcpy(vgrid[3], "...vvlvv...."); - strcpy(vgrid[4], "..lv|*|vl..."); - strcpy(vgrid[5], "..ll*S*ll..."); - strcpy(vgrid[6], "..lv|*|vl..."); - strcpy(vgrid[7], "...vvlvv...."); - strcpy(vgrid[8], "....lll....."); - strcpy(vgrid[9], "............"); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return 1; -} - + const map_lines &ml = def.map; + const int orient = def.orient; + const int width = ml.width(); + const int height = ml.height(); -static char minivault_16(char vgrid[81][81], FixedVector& mons_array) -{ // lava pond - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "............"); - strcpy(vgrid[2], "............"); - strcpy(vgrid[3], "............"); - strcpy(vgrid[4], "............"); - strcpy(vgrid[5], "............"); - strcpy(vgrid[6], "......S....."); - strcpy(vgrid[7], "............"); - strcpy(vgrid[8], "............"); - strcpy(vgrid[9], "............"); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} + int startx = 0, starty = 0; + if (orient == MAP_SOUTH || orient == MAP_SOUTHEAST + || orient == MAP_SOUTHWEST) + starty = GYM - height; -static char minivault_17(char vgrid[81][81], FixedVector& mons_array) -{ // lava pond - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "............"); - strcpy(vgrid[2], "............"); - strcpy(vgrid[3], "............"); - strcpy(vgrid[4], "............"); - strcpy(vgrid[5], ".....F......"); - strcpy(vgrid[6], "............"); - strcpy(vgrid[7], "............"); - strcpy(vgrid[8], "............"); - strcpy(vgrid[9], "............"); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} - + if (orient == MAP_EAST || orient == MAP_NORTHEAST + || orient == MAP_SOUTHEAST) + startx = GXM - width; -static char minivault_18(char vgrid[81][81], FixedVector& mons_array) -{ // lava pond - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "............"); - strcpy(vgrid[2], "............"); - strcpy(vgrid[3], "............"); - strcpy(vgrid[4], "............"); - strcpy(vgrid[5], ".....H......"); - strcpy(vgrid[6], "............"); - strcpy(vgrid[7], "............"); - strcpy(vgrid[8], "............"); - strcpy(vgrid[9], "............"); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} + // Handle maps aligned along cardinals that are smaller than + // the corresponding map dimension. + if ((orient == MAP_NORTH || orient == MAP_SOUTH + || orient == MAP_ENCOMPASS) + && width < GXM) + startx = (GXM - width) / 2; + if ((orient == MAP_EAST || orient == MAP_WEST + || orient == MAP_ENCOMPASS) + && height < GYM) + starty = (GYM - height) / 2; -static char minivault_19(char vgrid[81][81], FixedVector& mons_array) -{ - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xx......xx."); - strcpy(vgrid[2], ".xxx....xxx."); - strcpy(vgrid[3], "..xxx..xxx.."); - strcpy(vgrid[4], "...xxxxxx..."); - strcpy(vgrid[5], "....xxxx...."); - strcpy(vgrid[6], "....xxxx...."); - strcpy(vgrid[7], "...xxxxxx..."); - strcpy(vgrid[8], "..xxx..xxx.."); - strcpy(vgrid[9], ".xxx....xxx."); - strcpy(vgrid[10], ".xx......xx."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; -} + const std::vector &lines = ml.get_lines(); +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Applying %s at (%d,%d), dimensions (%d,%d)", + def.name.c_str(), startx, starty, width, height); +#endif + for (int y = starty; y < starty + height; ++y) + { + const std::string &s = lines[y - starty]; + strncpy(&map[y][startx], s.c_str(), s.length()); + } -static char minivault_20(char vgrid[81][81], FixedVector& mons_array) -{ // lava pond - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xxxx..xxxx."); - strcpy(vgrid[2], ".x........x."); - strcpy(vgrid[3], ".x..xxxx..x."); - strcpy(vgrid[4], ".x.x....x.x."); - strcpy(vgrid[5], "...x.x9.x..."); - strcpy(vgrid[6], "...x.9x.x..."); - strcpy(vgrid[7], ".x.x....x.x."); - strcpy(vgrid[8], ".x..xxxx..x."); - strcpy(vgrid[9], ".x........x."); - strcpy(vgrid[10], ".xxxx..xxxx."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; + place.x = startx; + place.y = starty; + place.width = width; + place.height = height; } - -static char minivault_21(char vgrid[81][81], FixedVector& mons_array) +static int apply_vault_definition( + const map_def &def, + map_type map, + FixedVector &marray, + vault_placement &place) { - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".^xxxxxxxx^."); - strcpy(vgrid[2], ".x........x."); - strcpy(vgrid[3], ".x.cccccc.x."); - strcpy(vgrid[4], ".x.c|....|c.x."); - strcpy(vgrid[8], ".x.cccccc.x."); - strcpy(vgrid[9], ".x........x."); - strcpy(vgrid[10], ".^xxxxxxxx^."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; + apply_monsters(def, marray); + apply_vault_grid(def, map, place); + int orient = def.orient; + if (orient == MAP_NONE) + orient = MAP_NORTH; + return (orient); } +/////////////////////////////////////////////////////////////////////////// +// Map lookups -static char minivault_22(char vgrid[81][81], FixedVector& mons_array) +// Returns a map for which PLACE: matches the given place. +int random_map_for_place(const std::string &place, bool want_minivault) { - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".....xx....."); - strcpy(vgrid[2], "...xxxxxx..."); - strcpy(vgrid[3], "..x^x..x^x.."); - strcpy(vgrid[4], "..xx.xx.xx.."); - strcpy(vgrid[5], ".xx.x$$x.xx."); - strcpy(vgrid[6], ".xx.x$$x.xx."); - strcpy(vgrid[7], "..xx.xx.xx.."); - strcpy(vgrid[8], "..x^x..x^x.."); - strcpy(vgrid[9], "...xxxxxx..."); - strcpy(vgrid[10], ".....xx....."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; - -} + if (place.empty()) + return (-1); + int mapindex = -1; + int rollsize = 0; -static char minivault_23(char vgrid[81][81], FixedVector& mons_array) -{ - UNUSED( mons_array ); - - strcpy(vgrid[0], "x.x.x.x.x.x."); - strcpy(vgrid[1], ".x.x.x.x.x.x"); - strcpy(vgrid[2], "x.x.x.x.x.x."); - strcpy(vgrid[3], ".x.x.x.x.x.x"); - strcpy(vgrid[4], "x.x.x.x.x.x."); - strcpy(vgrid[5], ".x.x.x.x.x.x"); - strcpy(vgrid[6], "x.x.x.x.x.x."); - strcpy(vgrid[7], ".x.x.x.x.x.x"); - strcpy(vgrid[8], "x.x.x.x.x.x."); - strcpy(vgrid[9], ".x.x.x.x.x.x"); - strcpy(vgrid[10], "x.x.x.x.x.x."); - strcpy(vgrid[11], ".x.x.x.x.x.x"); - - return MAP_NORTH; - -} + for (unsigned i = 0, size = vdefs.size(); i < size; ++i) + { + // We also accept tagged levels here. + if (vdefs[i].place == place + && vdefs[i].is_minivault() == want_minivault) + { + rollsize += vdefs[i].chance; + if (rollsize && random2(rollsize) < vdefs[i].chance) + mapindex = i; + } + } -static char minivault_24(char vgrid[81][81], FixedVector& mons_array) -{ - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "....xxxx...."); - strcpy(vgrid[2], "....xxxx...."); - strcpy(vgrid[3], "....xxxx...."); - strcpy(vgrid[4], ".xxxx.x.xxx."); - strcpy(vgrid[5], ".xxx.x.xxxx."); - strcpy(vgrid[6], ".xxxx.x.xxx."); - strcpy(vgrid[7], ".xxx.x.xxxx."); - strcpy(vgrid[8], "....xxxx...."); - strcpy(vgrid[9], "....xxxx...."); - strcpy(vgrid[10], "....xxxx...."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; +#ifdef DEBUG_DIAGNOSTICS + if (mapindex != -1) + mprf(MSGCH_DIAGNOSTICS, "Found map %s for %s", + vdefs[mapindex].name.c_str(), place.c_str()); +#endif + return (mapindex); } - -static char minivault_25(char vgrid[81][81], FixedVector& mons_array) +int find_map_named(const std::string &name) { - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xx+xxxxxxx."); - strcpy(vgrid[2], ".x........x."); - strcpy(vgrid[3], ".x........+."); - strcpy(vgrid[4], ".x........x."); - strcpy(vgrid[5], ".x........x."); - strcpy(vgrid[6], ".x........x."); - strcpy(vgrid[7], ".x........x."); - strcpy(vgrid[8], ".+........x."); - strcpy(vgrid[9], ".x........x."); - strcpy(vgrid[10], ".xxxxxxx+xx."); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; - -} - + if (name.empty()) + return (-1); -static char minivault_26(char vgrid[81][81], FixedVector& mons_array) -{ - UNUSED( mons_array ); - - strcpy(vgrid[0], "c..........c"); - strcpy(vgrid[1], ".c...cc...c."); - strcpy(vgrid[2], "..c..cc..c.."); - strcpy(vgrid[3], "...c....c..."); - strcpy(vgrid[4], "....c..c...."); - strcpy(vgrid[5], ".cc..cc..cc."); - strcpy(vgrid[6], ".cc..cc..cc."); - strcpy(vgrid[7], "....c..c...."); - strcpy(vgrid[8], "...c....c..."); - strcpy(vgrid[9], "..c..cc..c.."); - strcpy(vgrid[10], ".c...cc...c."); - strcpy(vgrid[11], "c..........c"); - - return MAP_NORTH; +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Looking for map named \"%s\"", name.c_str()); +#endif + for (unsigned i = 0, size = vdefs.size(); i < size; ++i) + if (vdefs[i].name == name) + return (i); + return (-1); } - -static char minivault_27(char vgrid[81][81], FixedVector& mons_array) +int random_map_for_depth(int depth, bool want_minivault) { - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".x.xxxxxxxx."); - strcpy(vgrid[2], ".x........x."); - strcpy(vgrid[3], ".xxxxxxxx.x."); - strcpy(vgrid[4], ".x........x."); - strcpy(vgrid[5], ".x.xxxxxxxx."); - strcpy(vgrid[6], ".x........x."); - strcpy(vgrid[7], ".xxxxxxxx.x."); - strcpy(vgrid[8], ".x........x."); - strcpy(vgrid[9], ".x.xxxxxxxx."); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; - -} + int mapindex = -1; + int rollsize = 0; + for (unsigned i = 0, size = vdefs.size(); i < size; ++i) + if (vdefs[i].depth.contains(depth) + // Tagged levels cannot be selected by depth. This is + // the only thing preventing Pandemonium demon vaults from + // showing up in the main dungeon. + && vdefs[i].tags.empty() + && vdefs[i].is_minivault() == want_minivault) + { + rollsize += vdefs[i].chance; -static char minivault_28(char vgrid[81][81], FixedVector& mons_array) -{ - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xxxx.xxxx.."); - strcpy(vgrid[2], ".x.......x.."); - strcpy(vgrid[3], ".x..999..x.."); - strcpy(vgrid[4], ".x.9...9.x.."); - strcpy(vgrid[5], "...9.I.9...."); - strcpy(vgrid[6], ".x.9...9.x.."); - strcpy(vgrid[7], ".x..999..x.."); - strcpy(vgrid[8], ".x.......x.."); - strcpy(vgrid[9], ".xxxx.xxxx.."); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; + if (rollsize && random2(rollsize) < vdefs[i].chance) + mapindex = i; + } + return (mapindex); } - -static char minivault_29(char vgrid[81][81], FixedVector& mons_array) +int random_map_for_tag(const std::string &tag) { + int mapindex = -1; + int rollsize = 0; - strcpy(vgrid[0], ".3......3..."); - strcpy(vgrid[1], "...x.xx.x.2."); - strcpy(vgrid[2], ".xxx2xxxxx.."); - strcpy(vgrid[3], ".xxxx42xxx2."); - strcpy(vgrid[4], ".2xx243432x3"); - strcpy(vgrid[5], ".xx421424xx."); - strcpy(vgrid[6], "3xx423242x.."); - strcpy(vgrid[7], ".x2x3243xxx."); - strcpy(vgrid[8], ".x2xx42422x."); - strcpy(vgrid[9], "..xxxxxxxx2."); - strcpy(vgrid[10], "...x2xxxx3.."); - strcpy(vgrid[11], ".3.......33."); - - mons_array[0] = MONS_QUEEN_ANT; - mons_array[1] = MONS_SOLDIER_ANT; - mons_array[2] = MONS_GIANT_ANT; - mons_array[3] = MONS_ANT_LARVA; - - return MAP_NORTH; - -} - - -static char minivault_30(char vgrid[81][81], FixedVector& mons_array) -{ // lava pond - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "............"); - strcpy(vgrid[2], "............"); - strcpy(vgrid[3], "............"); - strcpy(vgrid[4], "............"); - strcpy(vgrid[5], ".....T......"); - strcpy(vgrid[6], "............"); - strcpy(vgrid[7], "............"); - strcpy(vgrid[8], "............"); - strcpy(vgrid[9], "............"); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; - -} - - -static char minivault_31(char vgrid[81][81], FixedVector& mons_array) -{ // lava pond - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "............"); - strcpy(vgrid[2], "............"); - strcpy(vgrid[3], "............"); - strcpy(vgrid[4], "............"); - strcpy(vgrid[5], ".....T......"); - strcpy(vgrid[6], "............"); - strcpy(vgrid[7], "............"); - strcpy(vgrid[8], "............"); - strcpy(vgrid[9], "............"); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; - -} - - -static char minivault_32(char vgrid[81][81], FixedVector& mons_array) -{ // lava pond - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "............"); - strcpy(vgrid[2], "............"); - strcpy(vgrid[3], "............"); - strcpy(vgrid[4], "............"); - strcpy(vgrid[5], ".....U......"); - strcpy(vgrid[6], "............"); - strcpy(vgrid[7], "............"); - strcpy(vgrid[8], "............"); - strcpy(vgrid[9], "............"); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; - -} - - -static char minivault_33(char vgrid[81][81], FixedVector& mons_array) -{ // lava pond - UNUSED( mons_array ); - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "............"); - strcpy(vgrid[2], "............"); - strcpy(vgrid[3], "............"); - strcpy(vgrid[4], "............"); - strcpy(vgrid[5], ".....V......"); - strcpy(vgrid[6], "............"); - strcpy(vgrid[7], "............"); - strcpy(vgrid[8], "............"); - strcpy(vgrid[9], "............"); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - return MAP_NORTH; - -} - -static char minivault_34(char vgrid[81][81], FixedVector& mons_array, bool orientation) -{ - //jmf: multi-god temple thing - UNUSED( mons_array ); - int i, di; - - if ( orientation ) - { - i = 0; - di = +1; - } - else - { - i = 11; - di = -1; - } - - for (int c=0; c <= 11; c++, i += di) + for (unsigned i = 0, size = vdefs.size(); i < size; ++i) { - strcpy(vgrid[i], "............"); - strcpy(vgrid[i], ".=xxxxxxxx=."); - strcpy(vgrid[i], ".x9......9x."); - strcpy(vgrid[i], ".xT......Tx."); - strcpy(vgrid[i], ".x..C..C..x."); - strcpy(vgrid[i], ".xT......Tx."); - strcpy(vgrid[i], ".xxxxxxxxxx."); - strcpy(vgrid[i], ".xxx$$$$xxx."); - strcpy(vgrid[i], ".xx8....8xx."); - strcpy(vgrid[i], "..xx....xx.."); - strcpy(vgrid[i], "...xG..Gx..."); - strcpy(vgrid[i], "............"); - } - - return MAP_NORTH; -} - -static char minivault_34_a(char vgrid[81][81], FixedVector& mons_array) -{ - return minivault_34(vgrid, mons_array, true); -} - -static char minivault_34_b(char vgrid[81][81], FixedVector& mons_array) -{ - return minivault_34(vgrid, mons_array, false); -} - -static char minivault_35(char vgrid[81][81], FixedVector& mons_array, bool orientation) -{ - UNUSED( mons_array ); - //jmf: another multi-god temple thing - int i, di; - - if (orientation) - { - i = 0; - di = +1; - } - else - { - i = 11; - di = -1; - } + if (vdefs[i].has_tag(tag)) + { + rollsize += vdefs[i].chance; - for (int c=0; c <= 11; c++, i += di) - { - strcpy(vgrid[i], "............"); - strcpy(vgrid[i], "..vvvvvvvv.."); - strcpy(vgrid[i], ".vv......vv."); - strcpy(vgrid[i], ".v..x..x..v."); - strcpy(vgrid[i], ".v.Cx..xC.v."); - strcpy(vgrid[i], ".v..x..x..v."); - strcpy(vgrid[i], ".vT8x..x8Tv."); - strcpy(vgrid[i], ".vvvx==xvvv."); - strcpy(vgrid[i], "...Gx99xG..."); - strcpy(vgrid[i], "...+*99*+..."); - strcpy(vgrid[i], "...GxxxxG..."); - strcpy(vgrid[i], "............"); + if (rollsize && random2(rollsize) < vdefs[i].chance) + mapindex = i; + } } - return MAP_NORTH; -} - -static char minivault_35_a(char vgrid[81][81], FixedVector& mons_array) -{ - return minivault_35(vgrid, mons_array, true); -} - -static char minivault_35_b(char vgrid[81][81], FixedVector& mons_array) -{ - return minivault_35(vgrid, mons_array, false); -} - - -static char rand_demon_1(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xx.xx.x.xx."); - strcpy(vgrid[2], "..x.x..x.x.."); - strcpy(vgrid[3], "..x.x..x.x.."); - strcpy(vgrid[4], "..x.x..x.x.."); - strcpy(vgrid[5], "..x.x..x.x.."); - strcpy(vgrid[6], "..x.x1.x.x.."); - strcpy(vgrid[7], "..x.x..x.x.."); - strcpy(vgrid[8], "..x.x..x.x.."); - strcpy(vgrid[9], "..x.x..x.x.."); - strcpy(vgrid[10], ".xx.x.xx.xx."); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_PANDEMONIUM_DEMON; - mons_array[1] = RANDOM_MONSTER; - mons_array[2] = RANDOM_MONSTER; - mons_array[3] = RANDOM_MONSTER; - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - - return MAP_NORTH; -} - - -static char rand_demon_2(char vgrid[81][81], FixedVector& mons_array) -{ - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xxxxxxxx3x."); - strcpy(vgrid[2], ".3.....xx.x."); - strcpy(vgrid[3], ".xxxxxx4x.x."); - strcpy(vgrid[4], ".xx4x..xx.x."); - strcpy(vgrid[5], ".x.x.22.x.x."); - strcpy(vgrid[6], ".x.x.12.x.x."); - strcpy(vgrid[7], ".x.xx..x4xx."); - strcpy(vgrid[8], ".x.x4xxxxxx."); - strcpy(vgrid[9], ".x.xx.....3."); - strcpy(vgrid[10], ".x3xxxxxxxx."); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_PANDEMONIUM_DEMON; - mons_array[1] = summon_any_demon(DEMON_GREATER); - mons_array[2] = summon_any_demon(DEMON_COMMON); - mons_array[3] = summon_any_demon(DEMON_COMMON); - mons_array[4] = RANDOM_MONSTER; - mons_array[5] = RANDOM_MONSTER; - - return MAP_NORTH; -} - - -static char rand_demon_3(char vgrid[81][81], FixedVector& mons_array) -{ +#ifdef DEBUG_DIAGNOSTICS + if (mapindex != -1) + mprf(MSGCH_DIAGNOSTICS, "Found map %s tagged '%s'", + vdefs[mapindex].name.c_str(), tag.c_str()); + else + mprf(MSGCH_DIAGNOSTICS, "No map for tag '%s'", tag.c_str()); +#endif - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".x.x.x3x.x.."); - strcpy(vgrid[2], "..x.x3x3x.x."); - strcpy(vgrid[3], ".x.x.x2x.x.."); - strcpy(vgrid[4], "..x3x2x2x3x."); - strcpy(vgrid[5], ".x3x2x1x2x3."); - strcpy(vgrid[6], "..x3x2x2x3x."); - strcpy(vgrid[7], ".x.x.x2x3x.."); - strcpy(vgrid[8], "..x.x3x3x.x."); - strcpy(vgrid[9], ".x.x.x3x.x.."); - strcpy(vgrid[10], "..x.x.x.x.x."); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_PANDEMONIUM_DEMON; - mons_array[1] = summon_any_demon(DEMON_COMMON); - mons_array[2] = summon_any_demon(DEMON_COMMON); - - return MAP_NORTH; + return (mapindex); } +///////////////////////////////////////////////////////////////////////////// +// Reading maps from .des files. -static char rand_demon_4(char vgrid[81][81], FixedVector& mons_array) +static void parse_maps(const std::string &s) { - //jmf: all 3s below were 1s -- may have been bug - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xxxxxxxxx.."); - strcpy(vgrid[2], ".x$=*=3=|x.."); - strcpy(vgrid[3], ".xxxxxxx=x.."); - strcpy(vgrid[4], ".x2=3=2x|x.."); - strcpy(vgrid[5], ".x=xxxxx=x.."); - strcpy(vgrid[6], ".x3=*x1=Px.."); - strcpy(vgrid[7], ".x=x=xxxxx.."); - strcpy(vgrid[8], ".x*x2=3=2=.."); - strcpy(vgrid[9], ".xxxxxxxxx.."); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_PANDEMONIUM_DEMON; - mons_array[1] = summon_any_demon(random2(3)); - mons_array[2] = summon_any_demon(random2(3)); - - return MAP_NORTH; - -} - - -static char rand_demon_5(char vgrid[81][81], FixedVector& mons_array) -{ // obviously possible to get stuck - too bad (should've come prepared) - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "...xxxxxx..."); - strcpy(vgrid[2], "..xx....xx.."); - strcpy(vgrid[3], ".xx......xx."); - strcpy(vgrid[4], ".x..3232..x."); - strcpy(vgrid[5], ".x..2|P3..x."); - strcpy(vgrid[6], ".x..3P|2..x."); - strcpy(vgrid[7], ".x..2123..x."); - strcpy(vgrid[8], ".xx......xx."); - strcpy(vgrid[9], "..xx....xx.."); - strcpy(vgrid[10], "...xxxxxx..."); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_PANDEMONIUM_DEMON; - mons_array[1] = summon_any_demon(random2(3)); - mons_array[2] = summon_any_demon(random2(3)); - - return MAP_NORTH; - -} - + FILE *dat = fopen(s.c_str(), "r"); + if (!dat) + { + fprintf(stderr, "Failed to open %s for reading: %d\n", s.c_str(), + errno); + exit(1); + } -static char rand_demon_6(char vgrid[81][81], FixedVector& mons_array) -{ + reset_map_parser(); - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "............"); - strcpy(vgrid[2], "......2....."); - strcpy(vgrid[3], "............"); - strcpy(vgrid[4], ".3.........."); - strcpy(vgrid[5], "..........2."); - strcpy(vgrid[6], ".....1......"); - strcpy(vgrid[7], "............"); - strcpy(vgrid[8], "............"); - strcpy(vgrid[9], ".2.......3.."); - strcpy(vgrid[10], "............"); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_PANDEMONIUM_DEMON; - mons_array[1] = summon_any_demon(random2(3)); - mons_array[2] = summon_any_demon(random2(3)); - - return MAP_NORTH; + extern int yyparse(void); + extern FILE *yyin; + yyin = dat; + yyparse(); + fclose(dat); } - -static char rand_demon_7(char vgrid[81][81], FixedVector& mons_array) +void read_maps() { - - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xxx....xxx."); - strcpy(vgrid[2], ".x|xx=xxx|x."); - strcpy(vgrid[3], ".xx=....=xx."); - strcpy(vgrid[4], "..x.x==x.x.."); - strcpy(vgrid[5], "..x.=12=.=.."); - strcpy(vgrid[6], "..=.=23=.x.."); - strcpy(vgrid[7], "..x.x==x.x.."); - strcpy(vgrid[8], ".xx=....=xx."); - strcpy(vgrid[9], ".x|xxx=xx|x."); - strcpy(vgrid[10], ".xxx....xxx."); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_PANDEMONIUM_DEMON; - mons_array[1] = summon_any_demon(random2(3)); - mons_array[2] = summon_any_demon(DEMON_GREATER); - - return MAP_NORTH; - + parse_maps( lc_desfile = datafile_path( "splev.des" ) ); + parse_maps( lc_desfile = datafile_path( "vaults.des" ) ); } - -static char rand_demon_8(char vgrid[81][81], FixedVector& mons_array) +void add_parsed_map( const map_def &md ) { + map_def map = md; - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], "....xxxxxxx."); - strcpy(vgrid[2], "..xxx....1x."); - strcpy(vgrid[3], ".xx..2....x."); - strcpy(vgrid[4], ".x........x."); - strcpy(vgrid[5], ".xx.......x."); - strcpy(vgrid[6], "..xx33..2.x."); - strcpy(vgrid[7], "....33...xx."); - strcpy(vgrid[8], ".....x...x.."); - strcpy(vgrid[9], "..F..xx.xx.."); - strcpy(vgrid[10], "......xxx..."); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_PANDEMONIUM_DEMON; - mons_array[1] = summon_any_demon(DEMON_GREATER); - mons_array[2] = summon_any_demon(random2(3)); - - return MAP_NORTH; - + map.fixup(); + vdefs.push_back( map ); } +////////////////////////////////////////////////////////////////// +// map_lines -static char rand_demon_9(char vgrid[81][81], FixedVector& mons_array) -{ - strcpy(vgrid[0], "............"); - strcpy(vgrid[1], ".xxxxxxxxxx."); - strcpy(vgrid[2], ".x2=3=3=3xx."); - strcpy(vgrid[3], ".x=xxxxxx2x."); - strcpy(vgrid[4], ".x3x^^^^x=x."); - strcpy(vgrid[5], ".x=x^P^^x2x."); - strcpy(vgrid[6], ".x3x^^1^x=x."); - strcpy(vgrid[7], ".x=x^^^^x3x."); - strcpy(vgrid[8], ".x2xxxx=x=x."); - strcpy(vgrid[9], ".xx2=2=3x3x."); - strcpy(vgrid[10], ".xxxxxxxx=x."); - strcpy(vgrid[11], "............"); - - mons_array[0] = MONS_PANDEMONIUM_DEMON; - mons_array[1] = summon_any_demon(random2(3)); - mons_array[2] = summon_any_demon(DEMON_GREATER); - - return MAP_NORTH; - -} diff --git a/crawl-ref/source/maps.h b/crawl-ref/source/maps.h index e2ce11af80..7159448f85 100644 --- a/crawl-ref/source/maps.h +++ b/crawl-ref/source/maps.h @@ -13,13 +13,33 @@ #define MAPS_H #include "FixVec.h" +#include "dungeon.h" +class map_def; +struct vault_placement +{ + int x, y; + int width, height; + const map_def *map; -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: dungeon - * *********************************************************************** */ -char vault_main(char vgrid[81][81], FixedVector& mons_array, int vault_force, int many_many); + vault_placement() + : x(-1), y(-1), width(0), height(0), map(NULL) + { + } +}; +int vault_main(map_type vgrid, + FixedVector& mons_array, + vault_placement &vp, + int vault_force, + int many_many); + +int random_map_for_place(const std::string &place, bool mini = false); +int find_map_named(const std::string &name); +int random_map_for_depth(int depth, bool want_minivault = false); +int random_map_for_tag(const std::string &tag); +void add_parsed_map(const map_def &md); + +void read_maps(); #endif diff --git a/crawl-ref/source/menu.cc b/crawl-ref/source/menu.cc index 0728980058..c804c5343b 100644 --- a/crawl-ref/source/menu.cc +++ b/crawl-ref/source/menu.cc @@ -1,23 +1,37 @@ +/* + * File: menu.cc + * Summary: Menus and associated malarkey. + * Written by: Darshan Shaligram + * + * Modified for Crawl Reference by $Author$ on $Date$ + */ #include +#include "AppHdr.h" #include "menu.h" #include "macro.h" #include "view.h" +#include "initfile.h" Menu::Menu( int _flags ) - : selitem_text(NULL), + : f_selitem(NULL), + f_drawitem(NULL), + f_keyfilter(NULL), title(NULL), flags(_flags), first_entry(0), y_offset(0), pagesize(0), + max_pagesize(0), + more("-more-", true), items(), - sel(NULL), + sel(), select_filter(), highlighter(new MenuHighlighter), num(-1), lastch(0), alive(false) { + set_flags(flags); } Menu::~Menu() @@ -28,6 +42,33 @@ Menu::~Menu() delete highlighter; } +void Menu::clear() +{ + for (int i = 0, count = items.size(); i < count; ++i) + delete items[i]; + items.clear(); +} + +void Menu::set_maxpagesize(int max) +{ + max_pagesize = max; +} + +void Menu::set_flags(int new_flags, bool use_options) +{ + flags = new_flags; + if (use_options) + { + if (Options.easy_exit_menu) + flags |= MF_EASY_EXIT; + } +} + +void Menu::set_more(const formatted_string &fs) +{ + more = fs; +} + void Menu::set_highlighter( MenuHighlighter *mh ) { if (highlighter != mh) @@ -54,14 +95,17 @@ void Menu::reset() first_entry = 0; } -std::vector Menu::show() +std::vector Menu::show(bool reuse_selections) { - std::vector< MenuEntry * > selected; - - deselect_all(false); + if (reuse_selections) + get_selected(&sel); + else + deselect_all(false); - // Lose lines for the title + room for more. - pagesize = get_number_of_lines() - 2; + // Lose lines for the title + room for -more- line. + pagesize = get_number_of_lines() - !!title - 1; + if (max_pagesize > 0 && pagesize > max_pagesize) + pagesize = max_pagesize; #ifdef DOS_TERM char buffer[4600]; @@ -70,19 +114,18 @@ std::vector Menu::show() window(1, 1, 80, 25); #endif - do_menu( &selected ); + do_menu(); #ifdef DOS_TERM puttext(1, 1, 80, 25, buffer); #endif - return selected; + return (sel); } -void Menu::do_menu( std::vector *selected ) +void Menu::do_menu() { - sel = selected; - draw_menu( selected ); + draw_menu(); alive = true; while (alive) @@ -96,71 +139,88 @@ void Menu::do_menu( std::vector *selected ) bool Menu::is_set(int flag) const { - if (flag == MF_EASY_EXIT && Options.easy_exit_menu) - return true; return (flags & flag) == flag; } +int Menu::pre_process(int k) +{ + return (k); +} + +int Menu::post_process(int k) +{ + return (k); +} + bool Menu::process_key( int keyin ) { if (items.size() == 0) return false; bool nav = false, repaint = false; + + if (f_keyfilter) + keyin = (*f_keyfilter)(keyin); + + keyin = pre_process(keyin); + switch (keyin) { - case CK_ENTER: - return false; - case CK_ESCAPE: - sel->clear(); - lastch = keyin; - return false; - case ' ': case CK_PGDN: case '>': case '\'': - nav = true; - repaint = page_down(); - if (!repaint && flags && !is_set(MF_EASY_EXIT)) - { - repaint = first_entry != 0; - first_entry = 0; - } - break; - case CK_PGUP: case '<': case ';': - nav = true; - repaint = page_up(); - break; - case CK_UP: - nav = true; - repaint = line_up(); - break; - case CK_DOWN: - nav = true; - repaint = line_down(); - break; - default: - lastch = keyin; + case 0: + return true; + case CK_ENTER: + return false; + case CK_ESCAPE: + sel.clear(); + lastch = keyin; + return false; + case ' ': case CK_PGDN: case '>': case '\'': + nav = true; + repaint = page_down(); + if (!repaint && !is_set(MF_EASY_EXIT) && !is_set(MF_NOWRAP)) + { + repaint = first_entry != 0; + first_entry = 0; + } + break; + case CK_PGUP: case '<': case ';': + nav = true; + repaint = page_up(); + break; + case CK_UP: + nav = true; + repaint = line_up(); + break; + case CK_DOWN: + nav = true; + repaint = line_down(); + break; + default: + keyin = post_process(keyin); + lastch = keyin; - // If no selection at all is allowed, exit now. - if (!(flags & (MF_SINGLESELECT | MF_MULTISELECT))) - return false; + // If no selection at all is allowed, exit now. + if (!(flags & (MF_SINGLESELECT | MF_MULTISELECT))) + return false; - if (!is_set(MF_NO_SELECT_QTY) && isdigit( keyin )) - { - if (num > 999) - num = -1; - num = (num == -1)? keyin - '0' : - num * 10 + keyin - '0'; - } - - select_items( keyin, num ); - get_selected( sel ); - if (sel->size() == 1 && (flags & MF_SINGLESELECT)) - return false; - draw_select_count( sel->size() ); + if (!is_set(MF_NO_SELECT_QTY) && isdigit( keyin )) + { + if (num > 999) + num = -1; + num = (num == -1)? keyin - '0' : + num * 10 + keyin - '0'; + } + + select_items( keyin, num ); + get_selected( &sel ); + if (sel.size() == 1 && (flags & MF_SINGLESELECT)) + return false; + draw_select_count( sel.size() ); - if (flags & MF_ANYPRINTABLE && !isdigit( keyin )) - return false; + if (flags & MF_ANYPRINTABLE && !isdigit( keyin )) + return false; - break; + break; } if (!isdigit( keyin )) @@ -169,11 +229,11 @@ bool Menu::process_key( int keyin ) if (nav) { if (repaint) - draw_menu( sel ); + draw_menu(); // Easy exit should not kill the menu if there are selected items. - else if (sel->empty() && (!flags || is_set(MF_EASY_EXIT))) + else if (sel.empty() && is_set(MF_EASY_EXIT)) { - sel->clear(); + sel.clear(); return false; } } @@ -188,32 +248,32 @@ bool Menu::draw_title_suffix( const std::string &s, bool titlefirst ) draw_title(); int x = wherex(); - if (x > GXM || x < 1) + if (x > get_number_of_cols() || x < 1) { gotoxy(oldx, oldy); return false; } - // Note: 1 <= x <= GXM; we have no fear of overflow. - unsigned avail_width = GXM - x + 1; + // Note: 1 <= x <= get_number_of_cols(); we have no fear of overflow. + unsigned avail_width = get_number_of_cols() - x + 1; std::string towrite = s.length() > avail_width? s.substr(0, avail_width) : s.length() == avail_width? s : s + std::string(avail_width - s.length(), ' '); - cprintf(towrite.c_str()); + cprintf("%s", towrite.c_str()); gotoxy( oldx, oldy ); return true; } -void Menu::draw_select_count( int count ) +void Menu::draw_select_count( int count, bool force ) { - if (!is_set(MF_MULTISELECT)) + if (!force && !is_set(MF_MULTISELECT)) return; - if (selitem_text) + if (f_selitem) { - draw_title_suffix( selitem_text( sel ) ); + draw_title_suffix( f_selitem( &sel ) ); } else { @@ -226,6 +286,13 @@ void Menu::draw_select_count( int count ) } } +std::vector Menu::selected_entries() const +{ + std::vector selection; + get_selected(&selection); + return (selection); +} + void Menu::get_selected( std::vector *selected ) const { selected->clear(); @@ -257,7 +324,7 @@ bool Menu::is_hotkey(int i, int key) items[i]->is_primary_hotkey(key) : items[i]->is_hotkey(key); - return is_set(MF_SELECT_ANY_PAGE)? ishotkey + return !is_set(MF_SELECT_BY_PAGE)? ishotkey : ishotkey && i >= first_entry && i < end; } @@ -371,13 +438,13 @@ void Menu::select_index( int index, int qty ) } } -void Menu::draw_menu( std::vector< MenuEntry* > *selected ) +void Menu::draw_menu() { clrscr(); draw_title(); - draw_select_count( selected->size() ); - y_offset = 2; + draw_select_count( sel.size() ); + y_offset = 1 + !!title; int end = first_entry + pagesize; if (end > (int) items.size()) end = items.size(); @@ -386,11 +453,10 @@ void Menu::draw_menu( std::vector< MenuEntry* > *selected ) { draw_item( i ); } - if (end < (int) items.size()) + if (end < (int) items.size() || is_set(MF_ALWAYS_SHOW_MORE)) { gotoxy( 1, y_offset + pagesize ); - textcolor( LIGHTGREY ); - cprintf("-more-"); + more.display(); } } @@ -401,7 +467,7 @@ void Menu::update_title() gotoxy(x, y); } -int Menu::item_colour(const MenuEntry *entry) const +int Menu::item_colour(int, const MenuEntry *entry) const { int icol = -1; if (highlighter) @@ -415,18 +481,45 @@ void Menu::draw_title() if (title) { gotoxy(1, 1); - textcolor( item_colour(title) ); - cprintf( "%s", title->get_text().c_str() ); + write_title(); } } +void Menu::write_title() +{ + textattr( item_colour(-1, title) ); + cprintf("%s", title->get_text().c_str()); + + const int x = wherex(), y = wherey(); + cprintf("%-*s", get_number_of_cols() - x, ""); + gotoxy(x, y); +} + +bool Menu::in_page(int index) const +{ + return (index >= first_entry && index < first_entry + pagesize); +} + void Menu::draw_item( int index ) const { - if (index < first_entry || index >= first_entry + pagesize) + if (!in_page(index)) return; - gotoxy( 1, y_offset + index - first_entry ); - textcolor( item_colour(items[index]) ); + + draw_item(index, items[index]); +} + +void Menu::draw_item(int index, const MenuEntry *me) const +{ + if (f_drawitem) + (*f_drawitem)(index, me); + else + draw_stock_item(index, me); +} + +void Menu::draw_stock_item(int index, const MenuEntry *me) const +{ + textattr( item_colour(index, items[index]) ); cprintf( "%s", items[index]->get_text().c_str() ); } @@ -480,6 +573,258 @@ bool Menu::line_up() return false; } +///////////////////////////////////////////////////////////////// +// slider_menu + +slider_menu::slider_menu(int fl) + : Menu(fl), less(), starty(1), endy(get_number_of_lines()), + selected(0), search() +{ + less.textcolor(DARKGREY); + less.cprintf("<---- More"); + more.clear(); + more.textcolor(DARKGREY); + more.cprintf("More ---->"); +} + +void slider_menu::set_search(const std::string &s) +{ + search = s; +} + +void slider_menu::set_limits(int y1, int y2) +{ + starty = y1; + endy = y2; +} + +void slider_menu::select_search(const std::string &s) +{ + std::string srch = s; + tolower_string(srch); + + for (int i = 0, size = items.size(); i < size; ++i) + { + std::string text = items[i]->get_text(); + tolower_string(text); + + std::string::size_type found = text.find(srch); + if (found != std::string::npos + && found == text.find_first_not_of(" ")) + { + move_selection(i); + break; + } + } +} + +int slider_menu::post_process(int key) +{ + select_search( search += key ); + return (key); +} + +bool slider_menu::process_key(int key) +{ + // Some of this key processing should really be in a user-passed-in function + // If we ever need to use slider_menu elsewhere, we should factor it out. + if (key == CK_ESCAPE || key == '\t') + { + int old = selected; + selected = -1; + draw_item(old); + sel.clear(); + search.clear(); + lastch = key; + return (false); + } + + if (selected == 0 && + (key == CK_UP || key == CK_PGUP || key == '<' || key == ';') && + Menu::is_set(MF_EASY_EXIT)) + { + int old = selected; + selected = -1; + draw_item(old); + search.clear(); + return (false); + } + + return Menu::process_key(key); +} + +void slider_menu::display() +{ + // We lose two lines for each of the --More prompts + pagesize = endy - starty - 1 - !!title; + selected = -1; + draw_menu(); +} + +std::vector slider_menu::show() +{ + cursor_control coff(false); + + sel.clear(); + + // We lose two lines for each of the --More prompts + pagesize = endy - starty - 1 - !!title; + + if (selected == -1) + selected = 0; + + select_search(search); + do_menu(); + + if (selected >= 0 && selected <= (int) items.size()) + sel.push_back(items[selected]); + return (sel); +} + +const MenuEntry *slider_menu::selected_entry() const +{ + if (selected >= 0 && selected <= (int) items.size()) + return (items[selected]); + + return (NULL); +} + +void slider_menu::draw_stock_item(int index, const MenuEntry *me) const +{ + Menu::draw_stock_item(index, me); + cprintf("%-*s", get_number_of_cols() - wherex() + 1, ""); +} + +int slider_menu::item_colour(int index, const MenuEntry *me) const +{ + int colour = Menu::item_colour(index, me); + if (index == selected && selected != -1) + { +#if defined(WIN32CONSOLE) || defined(DOS) + colour = dos_brand(colour, CHATTR_REVERSE); +#else + colour |= COLFLAG_REVERSE; +#endif + } + return (colour); +} + +void slider_menu::draw_menu() +{ + gotoxy(1, starty); + write_title(); + y_offset = starty + !!title + 1; + + int end = first_entry + pagesize; + if (end > (int) items.size()) end = items.size(); + + // We're using get_number_of_cols() - 1 because we want to avoid line wrap + // on DOS (the conio.h functions go batshit if that happens). + gotoxy(1, y_offset - 1); + if (first_entry > 0) + less.display(); + else + { + textattr(LIGHTGREY); + cprintf("%-*s", get_number_of_cols() - 1, ""); + } + + for (int i = first_entry; i < end; ++i) + draw_item( i ); + + textattr(LIGHTGREY); + for (int i = end; i < first_entry + pagesize; ++i) + { + gotoxy(1, y_offset + i - first_entry); + cprintf("%-*s", get_number_of_cols() - 1, ""); + } + + gotoxy( 1, y_offset + pagesize ); + if (end < (int) items.size() || is_set(MF_ALWAYS_SHOW_MORE)) + more.display(); + else + { + textattr(LIGHTGREY); + cprintf("%-*s", get_number_of_cols() - 1, ""); + } +} + +void slider_menu::select_items(int, int) +{ + // Ignored. +} + +bool slider_menu::is_set(int flag) const +{ + if (flag == MF_EASY_EXIT) + return (false); + return Menu::is_set(flag); +} + +bool slider_menu::fix_entry() +{ + if (selected < 0 || selected >= (int) items.size()) + return (false); + + const int oldfirst = first_entry; + if (selected < first_entry) + first_entry = selected; + else if (selected >= first_entry + pagesize) + { + first_entry = selected - pagesize + 1; + if (first_entry < 0) + first_entry = 0; + } + + return (first_entry != oldfirst); +} + +void slider_menu::new_selection(int nsel) +{ + if (nsel < 0) + nsel = 0; + if (nsel >= (int) items.size()) + nsel = items.size() - 1; + + const int old = selected; + selected = nsel; + if (old != selected && nsel >= first_entry && nsel < first_entry + pagesize) + { + draw_item(old); + draw_item(selected); + } +} + +bool slider_menu::move_selection(int nsel) +{ + new_selection(nsel); + return fix_entry(); +} + +bool slider_menu::page_down() +{ + search.clear(); + return move_selection( selected + pagesize ); +} + +bool slider_menu::page_up() +{ + search.clear(); + return move_selection( selected - pagesize ); +} + +bool slider_menu::line_down() +{ + search.clear(); + return move_selection( selected + 1 ); +} + +bool slider_menu::line_up() +{ + search.clear(); + return move_selection( selected - 1 ); +} + ///////////////////////////////////////////////////////////////// // Menu colouring // @@ -499,3 +844,364 @@ int MenuHighlighter::entry_colour(const MenuEntry *entry) const { return (::menu_colour(entry->get_text())); } + + +//////////////////////////////////////////////////////////////////// +// formatted_string +// + +formatted_string::formatted_string(const std::string &s, bool init_style) + : ops() +{ + if (init_style) + ops.push_back(LIGHTGREY); + ops.push_back(s); +} + +int formatted_string::get_colour(const std::string &tag) +{ + if (tag == "h") + return (YELLOW); + + if (tag == "w") + return (WHITE); + + const int colour = str_to_colour(tag); + return (colour != -1? colour : LIGHTGREY); +} + +formatted_string formatted_string::parse_string( + const std::string &s, + bool eol_ends_format, + bool (*process)(const std::string &tag)) +{ + // FIXME This is a lame mess, just good enough for the task on hand + // (keyboard help). + formatted_string fs; + std::string::size_type tag = std::string::npos; + std::string::size_type length = s.length(); + + std::string currs; + int curr_colour = LIGHTGREY; + bool masked = false; + + for (tag = 0; tag < length; ++tag) + { + bool invert_colour = false; + std::string::size_type endpos = std::string::npos; + + if (s[tag] != '<' || tag >= length - 2) + { + if (!masked) + currs += s[tag]; + continue; + } + + // Is this a << escape? + if (s[tag + 1] == '<') + { + if (!masked) + currs += s[tag]; + tag++; + continue; + } + + endpos = s.find('>', tag + 1); + // No closing >? + if (endpos == std::string::npos) + { + if (!masked) + currs += s[tag]; + continue; + } + + std::string tagtext = s.substr(tag + 1, endpos - tag - 1); + if (tagtext.empty() || tagtext == "/") + { + if (!masked) + currs += s[tag]; + continue; + } + + if (tagtext[0] == '/') + { + invert_colour = true; + tagtext = tagtext.substr(1); + tag++; + } + + if (tagtext[0] == '?') + { + if (tagtext.length() == 1) + masked = false; + else if (process && !process(tagtext.substr(1))) + masked = true; + + tag += tagtext.length() + 1; + continue; + } + + const int new_colour = invert_colour? LIGHTGREY : get_colour(tagtext); + if (new_colour != curr_colour) + { + fs.cprintf(currs); + currs.clear(); + fs.textcolor( curr_colour = new_colour ); + } + tag += tagtext.length() + 1; + } + if (currs.length()) + fs.cprintf(currs); + + if (eol_ends_format && curr_colour != LIGHTGREY) + fs.textcolor(LIGHTGREY); + + return (fs); +} + +formatted_string::operator std::string() const +{ + std::string s; + for (unsigned i = 0, size = ops.size(); i < size; ++i) + { + if (ops[i] == FSOP_TEXT) + s += ops[i].text; + } + return (s); +} + +const formatted_string & +formatted_string::operator += (const formatted_string &other) +{ + ops.insert(ops.end(), other.ops.begin(), other.ops.end()); + return (*this); +} + +std::string::size_type formatted_string::length() const +{ + // Just add up the individual string lengths. + std::string::size_type len = 0; + for (unsigned i = 0, size = ops.size(); i < size; ++i) + if (ops[i] == FSOP_TEXT) + len += ops[i].text.length(); + return (len); +} + +inline void cap(int &i, int max) +{ + if (i < 0 && -i <= max) + i += max; + if (i >= max) + i = max - 1; + if (i < 0) + i = 0; +} + +std::string formatted_string::tostring(int s, int e) const +{ + std::string st; + + int size = ops.size(); + cap(s, size); + cap(e, size); + + for (int i = s; i <= e && i < size; ++i) + { + if (ops[i] == FSOP_TEXT) + st += ops[i].text; + } + return st; +} + +void formatted_string::display(int s, int e) const +{ + int size = ops.size(); + if (!size) + return ; + + cap(s, size); + cap(e, size); + + for (int i = s; i <= e && i < size; ++i) + ops[i].display(); +} + +void formatted_string::gotoxy(int x, int y) +{ + ops.push_back( fs_op(x, y) ); +} + +void formatted_string::movexy(int x, int y) +{ + ops.push_back( fs_op(x, y, true) ); +} + +void formatted_string::textcolor(int color) +{ + ops.push_back(color); +} + +void formatted_string::clear() +{ + ops.clear(); +} + +void formatted_string::cprintf(const char *s, ...) +{ + char buf[1000]; + va_list args; + va_start(args, s); + vsnprintf(buf, sizeof buf, s, args); + va_end(args); + + cprintf(std::string(buf)); +} + +void formatted_string::cprintf(const std::string &s) +{ + ops.push_back(s); +} + +void formatted_string::fs_op::display() const +{ + switch (type) + { + case FSOP_CURSOR: + { + int cx = x, cy = y; + if (relative) + { + cx += wherex(); + cy += wherey(); + } + else + { + if (cx == -1) + cx = wherex(); + if (cy == -1) + cy = wherey(); + } + ::gotoxy(cx, cy); + break; + } + case FSOP_COLOUR: + ::textattr(x); + break; + case FSOP_TEXT: + ::cprintf("%s", text.c_str()); + break; + } +} + +/////////////////////////////////////////////////////////////////////// +// column_composer + +column_composer::column_composer(int cols, ...) + : ncols(cols), pagesize(0), columns() +{ + ASSERT(cols > 0); + + va_list args; + va_start(args, cols); + + columns.push_back( column(1) ); + int lastcol = 1; + for (int i = 1; i < cols; ++i) + { + int nextcol = va_arg(args, int); + ASSERT(nextcol > lastcol); + + lastcol = nextcol; + columns.push_back( column(nextcol) ); + } + + va_end(args); +} + +void column_composer::set_pagesize(int ps) +{ + pagesize = ps; +} + +void column_composer::clear() +{ + flines.clear(); +} + +void column_composer::add_formatted( + int ncol, + const std::string &s, + bool add_separator, + bool eol_ends_format, + bool (*tfilt)(const std::string &), + int margin) +{ + ASSERT(ncol >= 0 && ncol < (int) columns.size()); + + column &col = columns[ncol]; + std::vector segs = split_string("\n", s, false, true); + + std::vector newlines; + // Add a blank line if necessary. Blank lines will not + // be added at page boundaries. + if (add_separator && col.lines && !segs.empty() + && (!pagesize || col.lines % pagesize)) + newlines.push_back(formatted_string()); + + for (unsigned i = 0, size = segs.size(); i < size; ++i) + { + newlines.push_back( + formatted_string::parse_string( + segs[i], + eol_ends_format, + tfilt)); + } + + strip_blank_lines(newlines); + + compose_formatted_column( + newlines, + col.lines, + margin == -1? col.margin : margin); + + col.lines += newlines.size(); + + strip_blank_lines(flines); +} + +std::vector column_composer::formatted_lines() const +{ + return (flines); +} + +void column_composer::strip_blank_lines(std::vector &fs) const +{ + for (int i = fs.size() - 1; i >= 0; --i) + { + if (fs[i].length() == 0) + fs.erase( fs.begin() + i ); + else + break; + } +} + +void column_composer::compose_formatted_column( + const std::vector &lines, + int startline, + int margin) +{ + if (flines.size() < startline + lines.size()) + flines.resize(startline + lines.size()); + + for (unsigned i = 0, size = lines.size(); i < size; ++i) + { + int f = i + startline; + if (margin > 1) + { + int xdelta = margin - flines[f].length() - 1; + if (xdelta > 0) + flines[f].cprintf("%-*s", xdelta, ""); + } + flines[f] += lines[i]; + } +} diff --git a/crawl-ref/source/menu.h b/crawl-ref/source/menu.h index 05e4796383..441685e11a 100644 --- a/crawl-ref/source/menu.h +++ b/crawl-ref/source/menu.h @@ -5,6 +5,7 @@ #include #include #include "AppHdr.h" +#include "externs.h" #include "defines.h" #include "libutil.h" @@ -33,7 +34,8 @@ struct menu_letter return *this; } - menu_letter operator ++ (int postfix) + // dummy postfix argument unnamed to stop gcc from complaining + menu_letter operator ++ (int) { menu_letter copy = *this; this->operator++(); @@ -41,6 +43,77 @@ struct menu_letter } }; +class formatted_string +{ +public: + formatted_string() : ops() { } + formatted_string(const std::string &s, bool init_style = false); + + operator std::string() const; + void display(int start = 0, int end = -1) const; + std::string tostring(int start = 0, int end = -1) const; + + void cprintf(const char *s, ...); + void cprintf(const std::string &s); + void gotoxy(int x, int y); + void movexy(int delta_x, int delta_y); + void textcolor(int color); + + void clear(); + + std::string::size_type length() const; + + const formatted_string &operator += (const formatted_string &other); + +public: + static formatted_string parse_string( + const std::string &s, + bool eol_ends_format = true, + bool (*process_tag)(const std::string &tag) = NULL ); + +private: + enum fs_op_type + { + FSOP_COLOUR, + FSOP_CURSOR, + FSOP_TEXT + }; + + static int get_colour(const std::string &tag); + +private: + struct fs_op + { + fs_op_type type; + int x, y; + bool relative; + std::string text; + + fs_op(int color) + : type(FSOP_COLOUR), x(color), y(-1), relative(false), text() + { + } + + fs_op(int cx, int cy, bool rel = false) + : type(FSOP_CURSOR), x(cx), y(cy), relative(rel), text() + { + } + + fs_op(const std::string &s) + : type(FSOP_TEXT), x(-1), y(-1), relative(false), text(s) + { + } + + operator fs_op_type () const + { + return type; + } + void display() const; + }; + + std::vector ops; +}; + struct item_def; struct MenuEntry { @@ -51,7 +124,7 @@ struct MenuEntry MenuEntryLevel level; void *data; - MenuEntry( const std::string &txt = std::string(""), + MenuEntry( const std::string &txt = std::string(), MenuEntryLevel lev = MEL_ITEM, int qty = 0, int hotk = 0 ) : @@ -66,6 +139,10 @@ struct MenuEntry } virtual ~MenuEntry() { } + bool operator<( const MenuEntry& rhs ) const { + return text < rhs.text; + } + void add_hotkey( int key ) { if (key && !is_hotkey(key)) @@ -91,8 +168,7 @@ struct MenuEntry "%c - %s", hotkeys[0], text.c_str()); return std::string(buf); } - return std::string(level == MEL_SUBTITLE? " " : - level == MEL_ITEM? "" : " ") + text; + return text; } virtual bool selected() const @@ -118,15 +194,19 @@ public: enum MenuFlag { - MF_NOSELECT = 0, // No selection is permitted - MF_SINGLESELECT = 1, // Select just one item - MF_MULTISELECT = 2, // Select multiple items - MF_NO_SELECT_QTY = 4, // Disallow partial selections - MF_ANYPRINTABLE = 8, // Any printable character is valid, and - // closes the menu. - MF_SELECT_ANY_PAGE = 16, // Allow selections to occur on any page. - - MF_EASY_EXIT = 64 + MF_NOSELECT = 0x0000, // No selection is permitted + MF_SINGLESELECT = 0x0001, // Select just one item + MF_MULTISELECT = 0x0002, // Select multiple items + MF_NO_SELECT_QTY = 0x0004, // Disallow partial selections + MF_ANYPRINTABLE = 0x0008, // Any printable character is valid, and + // closes the menu. + MF_SELECT_BY_PAGE = 0x0010, // Allow selections to occur only on + // currently visible page. + + MF_ALWAYS_SHOW_MORE = 0x0020, // Always show the -more- footer + MF_NOWRAP = 0x0040, // Paging past the end will not wrap back. + + MF_EASY_EXIT = 0x1000 }; /////////////////////////////////////////////////////////////////////// @@ -142,18 +222,28 @@ public: Menu( int flags = MF_MULTISELECT ); virtual ~Menu(); - void set_flags( int new_flags ) { this->flags = new_flags; } + // Remove all items from the Menu, leave title intact. + void clear(); + + // Sets menu flags to new_flags. If use_options is true, game options may + // override options. + void set_flags(int new_flags, bool use_options = true); int get_flags() const { return flags; } - bool is_set( int flag ) const; + virtual bool is_set( int flag ) const; bool draw_title_suffix( const std::string &s, bool titlefirst = true ); void update_title(); + + // Sets a replacement for the --more-- string. + void set_more(const formatted_string &more); void set_highlighter( MenuHighlighter *h ); void set_title( MenuEntry *e ); void add_entry( MenuEntry *entry ); void get_selected( std::vector *sel ) const; + void set_maxpagesize(int max); + void set_select_filter( std::vector filter ) { select_filter = filter; @@ -162,22 +252,31 @@ public: unsigned char getkey() const { return lastch; } void reset(); - std::vector show(); + std::vector show(bool reuse_selections = false); + std::vector selected_entries() const; + + size_t item_count() const { return items.size(); } public: typedef std::string (*selitem_tfn)( const std::vector *sel ); + typedef void (*drawitem_tfn)(int index, const MenuEntry *me); + typedef int (*keyfilter_tfn)(int keyin); - selitem_tfn selitem_text; + selitem_tfn f_selitem; + drawitem_tfn f_drawitem; + keyfilter_tfn f_keyfilter; protected: MenuEntry *title; int flags; int first_entry, y_offset; - int pagesize; + int pagesize, max_pagesize; + + formatted_string more; std::vector items; - std::vector *sel; + std::vector sel; std::vector select_filter; // Class that is queried to colour menu entries. @@ -189,28 +288,122 @@ protected: bool alive; - void do_menu( std::vector *selected ); - virtual void draw_select_count( int count ); - void draw_item( int index ) const; +protected: + void do_menu(); + virtual void draw_select_count(int count, bool force = false); + virtual void draw_item( int index ) const; + virtual void draw_item(int index, const MenuEntry *me) const; + virtual void draw_stock_item(int index, const MenuEntry *me) const; + virtual void draw_title(); - void draw_menu( std::vector *selected ); - bool page_down(); - bool line_down(); - bool page_up(); - bool line_up(); + virtual void write_title(); + virtual void draw_menu(); + virtual bool page_down(); + virtual bool line_down(); + virtual bool page_up(); + virtual bool line_up(); + + virtual int pre_process(int key); + virtual int post_process(int key); + + bool in_page(int index) const; void deselect_all(bool update_view = true); - void select_items( int key, int qty = -1 ); + virtual void select_items( int key, int qty = -1 ); void select_index( int index, int qty = -1 ); bool is_hotkey(int index, int key ); bool is_selectable(int index) const; - int item_colour(const MenuEntry *me) const; + virtual int item_colour(int index, const MenuEntry *me) const; virtual bool process_key( int keyin ); }; +// Uses a sliding selector rather than hotkeyed selection. +class slider_menu : public Menu +{ +public: + // Multiselect would be awkward to implement. + slider_menu(int flags = MF_SINGLESELECT); + void display(); + std::vector show(); + + void set_search(const std::string &search); + void set_limits(int starty, int endy); + const MenuEntry *selected_entry() const; + +protected: + int item_colour(int index, const MenuEntry *me) const; + void draw_stock_item(int index, const MenuEntry *me) const; + void draw_menu(); + + void new_selection(int nsel); + bool move_selection(int nsel); + + bool page_down(); + bool line_down(); + bool page_up(); + bool line_up(); + + bool is_set(int flag) const; + void select_items( int key, int qty ); + bool fix_entry(); + bool process_key( int keyin ); + + int post_process(int key); + + void select_search(const std::string &search); + +protected: + formatted_string less; + int starty, endy; + int selected; + std::string search; +}; + +// This is only tangentially related to menus, but what the heck. +// Note, column margins start on 1, not 0. +class column_composer +{ +public: + // Number of columns and left margins for 2nd, 3rd, ... nth column. + column_composer(int ncols, ...); + + void clear(); + void add_formatted(int ncol, + const std::string &tagged_text, + bool add_separator = true, + bool eol_ends_format = true, + bool (*text_filter)(const std::string &tag) = NULL, + int margin = -1); + + std::vector formatted_lines() const; + + void set_pagesize(int pagesize); + +private: + struct column; + void compose_formatted_column( + const std::vector &lines, + int start_col, + int margin); + void strip_blank_lines(std::vector &) const; + +private: + struct column + { + int margin; + int lines; + + column(int marg = 1) : margin(marg), lines(0) { } + }; + + int ncols, pagesize; + std::vector columns; + std::vector flines; +}; + int menu_colour(const std::string &itemtext); #endif diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc index 65faacbbd8..fee17318d6 100644 --- a/crawl-ref/source/message.cc +++ b/crawl-ref/source/message.cc @@ -16,7 +16,8 @@ #include "message.h" #include "religion.h" -#include +#include +#include #ifdef DOS #include @@ -26,11 +27,12 @@ #include "initfile.h" #include "macro.h" -#include "player.h" +#include "delay.h" #include "stuff.h" #include "travel.h" #include "view.h" - +#include "notes.h" +#include "stash.h" // circular buffer for keeping past messages message_item Store_Message[ NUM_STORED_MESSAGES ]; // buffer of old messages @@ -38,6 +40,19 @@ int Next_Message = 0; // end of messages char Message_Line = 0; // line of next (previous?) message +static bool suppress_messages = false; +static void base_mpr(const char *inf, int channel, int param); + +no_messages::no_messages() : msuppressed(suppress_messages) +{ + suppress_messages = true; +} + +no_messages::~no_messages() +{ + suppress_messages = msuppressed; +} + static char god_message_altar_colour( char god ) { int rnd; @@ -103,9 +118,9 @@ static char channel_to_colour( int channel, int param ) { case MSGCOL_PLAIN: // note that if the plain channel is muted, then we're protecting - // the player from having that spead to other other channels here. + // the player from having that spread to other channels here. // The intent of plain is to give non-coloured messages, not to - // supress them. + // suppress them. if (Options.channels[ MSGCH_PLAIN ] >= MSGCOL_DEFAULT) ret = LIGHTGREY; else @@ -117,6 +132,7 @@ static char channel_to_colour( int channel, int param ) switch (channel) { case MSGCH_GOD: + case MSGCH_PRAY: ret = (Options.channels[ channel ] == MSGCOL_DEFAULT) ? god_colour( param ) : god_message_altar_colour( param ); @@ -167,7 +183,8 @@ static char channel_to_colour( int channel, int param ) break; case MSGCH_DIAGNOSTICS: - ret = DARKGREY; // makes is easier to ignore at times -- bwr + case MSGCH_MULTITURN_ACTION: + ret = DARKGREY; // makes it easier to ignore at times -- bwr break; case MSGCH_PLAIN: @@ -215,14 +232,92 @@ static char channel_to_colour( int channel, int param ) #endif +static void do_message_print( int channel, int param, + const char *format, va_list argp ) +{ + char buff[200]; + vsnprintf( buff, sizeof( buff ), format, argp ); + buff[199] = 0; + + mpr(buff, channel, param); +} + +void mprf( int channel, const char *format, ... ) +{ + va_list argp; + va_start( argp, format ); + do_message_print( channel, 0, format, argp ); + va_end( argp ); +} + +void mprf( const char *format, ... ) +{ + va_list argp; + va_start( argp, format ); + do_message_print( MSGCH_PLAIN, 0, format, argp ); + va_end( argp ); +} + void mpr(const char *inf, int channel, int param) { - char info2[80]; + char mbuf[400]; + size_t i = 0; + const int stepsize = get_number_of_cols() - 1; + const size_t msglen = strlen(inf); + const int lookback_size = (stepsize < 12 ? 0 : 12); + // if a message is exactly STEPSIZE characters long, + // it should precisely fit in one line. The printing is thus + // from I to I + STEPSIZE - 1. Stop when I reaches MSGLEN. + while ( i < msglen || i == 0 ) + { + strncpy( mbuf, inf + i, stepsize ); + mbuf[stepsize] = 0; + // did the message break? + if ( i + stepsize < msglen ) + { + // yes, find a nicer place to break it. + int lookback, where = 0; + for ( lookback = 0; lookback < lookback_size; ++lookback ) + { + where = stepsize - 1 - lookback; + if ( where >= 0 && isspace(mbuf[where]) ) + // aha! + break; + } + + if ( lookback != lookback_size ) + { + // found a good spot to break + mbuf[where] = 0; + i += where + 1; // skip past the space! + } + else + i += stepsize; + } + else + i += stepsize; + base_mpr( mbuf, channel, param ); + } +} + +static void base_mpr(const char *inf, int channel, int param) +{ + if (suppress_messages) + return; int colour = channel_to_colour( channel, param ); if (colour == MSGCOL_MUTED) return; + std::string imsg = inf; + + for (unsigned i = 0; i < Options.note_messages.size(); ++i) { + if (Options.note_messages[i].matches(imsg)) { + take_note(Note(NOTE_MESSAGE, channel, param, inf)); + break; + } + } + interrupt_activity( AI_MESSAGE, channel_to_str(channel) + ":" + inf ); // If you're travelling, only certain user-specified messages can break @@ -230,9 +325,9 @@ void mpr(const char *inf, int channel, int param) if (you.running < 0) { std::string message = inf; - for (unsigned i = 0; i < Options.stop_travel.size(); ++i) + for (unsigned i = 0; i < Options.travel_stop_message.size(); ++i) { - if (Options.stop_travel[i].is_filtered( channel, message )) + if (Options.travel_stop_message[i].is_filtered( channel, message )) { stop_running(); break; @@ -246,7 +341,7 @@ void mpr(const char *inf, int channel, int param) for (unsigned i = 0; i < Options.sound_mappings.size(); i++) { // Maybe we should allow message channel matching as for - // stop_travel? + // travel_stop_message? if (Options.sound_mappings[i].pattern.matches(message)) { play_sound(Options.sound_mappings[i].soundfile.c_str()); @@ -269,11 +364,8 @@ void mpr(const char *inf, int channel, int param) more(); gotoxy( (Options.delay_message_clear) ? 2 : 1, Message_Line + 18 ); - strncpy(info2, inf, 78); - info2[78] = 0; - textcolor( colour ); - cprintf(info2); + cprintf("%s", inf); // // reset colour textcolor(LIGHTGREY); @@ -491,7 +583,7 @@ void replay_messages(void) #if DEBUG_DIAGNOSTICS cprintf( "%d: %s", line, Store_Message[ line ].text.c_str() ); #else - cprintf( Store_Message[ line ].text.c_str() ); + cprintf( "%s", Store_Message[ line ].text.c_str() ); #endif cprintf(EOL); diff --git a/crawl-ref/source/message.h b/crawl-ref/source/message.h index c7fe736ef1..584dc07554 100644 --- a/crawl-ref/source/message.h +++ b/crawl-ref/source/message.h @@ -3,6 +3,8 @@ * Summary: Functions used to print messages. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 5/08/99 JDJ mpr takes a const char* instead of a char array. @@ -53,6 +55,18 @@ void more(void); * *********************************************************************** */ void mpr(const char *inf, int channel = MSGCH_PLAIN, int param = 0); +// 4.1-style mpr, currently named mprf for minimal disruption. +void mprf( int channel, const char *format, ... ); +void mprf( const char *format, ... ); + +class no_messages +{ +public: + no_messages(); + ~no_messages(); +private: + bool msuppressed; +}; // last updated 12may2000 {dlb} /* *********************************************************************** diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 705a8acadb..c0567ef369 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -3,6 +3,8 @@ * Summary: Misc functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <3> 11/14/99 cdl evade with random40(ev) vice random2(ev) @@ -13,9 +15,10 @@ #include "AppHdr.h" #include "misc.h" +#include "notes.h" #include -#if !(defined(__IBMCPP__) || defined(__BCPLUSPLUS__)) +#if !defined(__IBMCPP__) #include #endif @@ -45,6 +48,7 @@ #include "monplace.h" #include "mon-util.h" #include "monstuff.h" +#include "notes.h" #include "ouch.h" #include "player.h" #include "shopping.h" @@ -57,6 +61,7 @@ #include "travel.h" #include "view.h" +extern FixedVector Visible_Statue; // defined in acr.cc bool scramble(void); bool trap_item(char base_type, char sub_type, char beam_x, char beam_y); @@ -93,7 +98,7 @@ void turn_corpse_into_chunks( item_def &item ) mitm[o].plus2 = 0; mitm[o].special = 0; mitm[o].flags = 0; - mitm[o].colour = mons_colour( mons_class ); + mitm[o].colour = mons_class_colour( mons_class ); // these values cannot be set by a reasonable formula: {dlb} switch (mons_class) @@ -132,18 +137,92 @@ void turn_corpse_into_chunks( item_def &item ) } } // end place_chunks() +bool grid_is_wall( int grid ) +{ + return (grid == DNGN_ROCK_WALL + || grid == DNGN_STONE_WALL + || grid == DNGN_METAL_WALL + || grid == DNGN_GREEN_CRYSTAL_WALL + || grid == DNGN_WAX_WALL + || grid == DNGN_PERMAROCK_WALL); +} + +bool grid_is_opaque( int grid ) +{ + return (grid < MINSEE && grid != DNGN_ORCISH_IDOL); +} + +bool grid_is_solid( int grid ) +{ + return (grid < MINMOVE); +} + +bool grid_is_water( int grid ) +{ + return (grid == DNGN_SHALLOW_WATER || grid == DNGN_DEEP_WATER); +} + +bool grid_destroys_items( int grid ) +{ + return (grid == DNGN_LAVA || grid == DNGN_DEEP_WATER); +} + +// returns 0 is grid is not an altar, else it returns the GOD_* type +god_type grid_altar_god( unsigned char grid ) +{ + if (grid >= DNGN_ALTAR_ZIN && grid <= DNGN_ALTAR_ELYVILON) + return (static_cast( grid - DNGN_ALTAR_ZIN + 1 )); + + return (GOD_NO_GOD); +} + +bool grid_is_branch_stairs( unsigned char grid ) +{ + return ((grid >= DNGN_ENTER_ORCISH_MINES && grid <= DNGN_ENTER_RESERVED_4) + || (grid >= DNGN_ENTER_DIS && grid <= DNGN_ENTER_TARTARUS)); +} + +int grid_secret_door_appearance( int gx, int gy ) +{ + int ret = DNGN_FLOOR; + + for (int dx = -1; dx <= 1; dx++) + { + for (int dy = -1; dy <= 1; dy++) + { + // only considering orthogonal grids + if ((abs(dx) + abs(dy)) % 2 == 0) + continue; + + const int targ = grd[gx + dx][gy + dy]; + + if (!grid_is_wall( targ )) + continue; + + if (ret == DNGN_FLOOR) + ret = targ; + else if (ret != targ) + ret = ((ret < targ) ? ret : targ); + } + } + + return ((ret == DNGN_FLOOR) ? DNGN_ROCK_WALL + : ret); +} + +const char *grid_item_destruction_message( unsigned char grid ) +{ + return grid == DNGN_DEEP_WATER? "You hear a splash." + : grid == DNGN_LAVA ? "You hear a sizzling splash." + : "You hear an empty echo."; +} + void search_around(void) { char srx = 0; char sry = 0; int i; - // Never if doing something else... this prevents a slight asymetry - // where using autopickup was giving free searches in comparison to - // not using autopickup. -- bwr - if (you_are_delayed()) - return; - for (srx = you.x_pos - 1; srx < you.x_pos + 2; srx++) { for (sry = you.y_pos - 1; sry < you.y_pos + 2; sry++) @@ -218,7 +297,7 @@ void in_a_cloud(void) hurted /= (1 + resist * resist); ouch( hurted, cl, KILLED_BY_CLOUD, "flame" ); } - scrolls_burn(7, OBJ_SCROLLS); + expose_player_to_element(BEAM_FIRE, 7); break; case CLOUD_STINK: @@ -269,7 +348,7 @@ void in_a_cloud(void) hurted /= (1 + resist * resist); ouch( hurted, cl, KILLED_BY_CLOUD, "freezing vapour" ); } - scrolls_burn(7, OBJ_POTIONS); + expose_player_to_element(BEAM_COLD, 7); break; case CLOUD_POISON: @@ -298,13 +377,7 @@ void in_a_cloud(void) case CLOUD_STEAM: case CLOUD_STEAM_MON: mpr("You are engulfed in a cloud of scalding steam!"); - if (you.species == SP_PALE_DRACONIAN && you.experience_level > 5) - { - mpr("It doesn't seem to affect you."); - return; - } - - if (player_equip( EQ_BODY_ARMOUR, ARM_STEAM_DRAGON_ARMOUR )) + if (player_res_steam() > 0) { mpr("It doesn't seem to affect you."); return; @@ -343,6 +416,27 @@ void in_a_cloud(void) return; } // end in_a_cloud() +void curare_hits_player(int agent, int degree) +{ + const bool res_poison = player_res_poison(); + + poison_player(degree); + + if (!player_res_asphyx()) + { + int hurted = roll_dice(2, 6); + // Note that the hurtage is halved by poison resistance. + if (res_poison) + hurted /= 2; + + if (hurted) + { + mpr("You feel difficulty breathing."); + ouch( hurted, agent, KILLED_BY_CURARE, "curare-induced apnoea" ); + } + potion_effect(POT_SLOWING, 2 + random2(4 + degree)); + } +} void merfolk_start_swimming(void) { @@ -409,14 +503,14 @@ void up_stairs(void) ouch( roll_dice( 3 + you.burden_state, 5 ), 0, KILLED_BY_FALLING_DOWN_STAIRS ); - you.turn_is_over = 1; + you.turn_is_over = true; return; } if (you.burden_state == BS_OVERLOADED) { mpr("You are carrying too much to climb upwards."); - you.turn_is_over = 1; + you.turn_is_over = true; return; } @@ -533,7 +627,7 @@ void up_stairs(void) load(stair_taken, LOAD_ENTER_LEVEL, was_a_labyrinth, old_level, old_where); - you.turn_is_over = 1; + you.turn_is_over = true; save_game(false); @@ -703,7 +797,6 @@ void down_stairs( bool remove_stairs, int old_level, bool force ) if (collect_travel_data) old_level_info.update(); - if (you.level_type == LEVEL_PANDEMONIUM && stair_find == DNGN_TRANSIT_PANDEMONIUM) { @@ -836,41 +929,21 @@ void down_stairs( bool remove_stairs, int old_level, bool force ) you.level_type = LEVEL_PANDEMONIUM; } - if (you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS - || you.level_type == LEVEL_PANDEMONIUM) + // When going downstairs into a special level, delete any previous + // instances of it + if (you.level_type == LEVEL_LABYRINTH || + you.level_type == LEVEL_ABYSS || + you.level_type == LEVEL_PANDEMONIUM) { - char glorpstr[kFileNameSize]; - char del_file[kFileNameSize]; - int sysg; - -#ifdef SAVE_DIR_PATH - snprintf( glorpstr, sizeof(glorpstr), - SAVE_DIR_PATH "%s%d", you.your_name, (int) getuid() ); -#else - strncpy(glorpstr, you.your_name, kFileNameLen); - - // glorpstr [strlen(glorpstr)] = 0; - // This is broken. Length is not valid yet! We have to check if we got - // a trailing NULL; if not, write one: - /* is name 6 chars or more? */ - if (strlen(you.your_name) > kFileNameLen - 1) - glorpstr[kFileNameLen] = '\0'; -#endif - - strcpy(del_file, glorpstr); - strcat(del_file, ".lab"); - -#ifdef DOS - strupr(del_file); -#endif - sysg = unlink(del_file); - + std::string lname = make_filename(you.your_name, you.your_level, + you.where_are_you, + true, false ); #if DEBUG_DIAGNOSTICS - strcpy( info, "Deleting: " ); - strcat( info, del_file ); + snprintf( info, INFO_SIZE, "Deleting: %s", lname.c_str() ); mpr( info, MSGCH_DIAGNOSTICS ); more(); #endif + unlink(lname.c_str()); } if (stair_find == DNGN_EXIT_ABYSS || stair_find == DNGN_EXIT_PANDEMONIUM) @@ -993,7 +1066,7 @@ void down_stairs( bool remove_stairs, int old_level, bool force ) break; } - you.turn_is_over = 1; + you.turn_is_over = true; save_game(false); @@ -1065,6 +1138,7 @@ void new_level(void) env.floor_colour = LIGHTGREY; env.rock_colour = BROWN; + take_note(Note(NOTE_DUNGEON_LEVEL_CHANGE)); if (you.level_type == LEVEL_PANDEMONIUM) { cprintf("- Pandemonium "); @@ -1240,7 +1314,7 @@ static void dart_trap( bool trap_known, int trapped, struct bolt &pbolt, if (random2(10) < 2 || (trap_known && !one_chance_in(4))) { snprintf( info, INFO_SIZE, "You avoid triggering a%s trap.", - pbolt.beam_name ); + pbolt.name.c_str() ); mpr(info); return; } @@ -1248,9 +1322,9 @@ static void dart_trap( bool trap_known, int trapped, struct bolt &pbolt, if (you.equip[EQ_SHIELD] != -1 && one_chance_in(3)) exercise( SK_SHIELDS, 1 ); - snprintf( info, INFO_SIZE, "A%s shoots out and ", pbolt.beam_name ); + snprintf( info, INFO_SIZE, "A%s shoots out and ", pbolt.name.c_str() ); - if (random2( 50 + 10 * you.shield_blocks * you.shield_blocks ) + if (random2( 20 + 5 * you.shield_blocks * you.shield_blocks ) < player_shield_class()) { you.shield_blocks++; @@ -1280,7 +1354,7 @@ static void dart_trap( bool trap_known, int trapped, struct bolt &pbolt, damage_taken -= random2( player_AC() + 1 ); if (damage_taken > 0) - ouch( damage_taken, 0, KILLED_BY_TRAP, pbolt.beam_name ); + ouch( damage_taken, 0, KILLED_BY_TRAP, pbolt.name.c_str() ); } else { @@ -1351,37 +1425,37 @@ void handle_traps(char trt, int i, bool trap_known) switch (trt) { case TRAP_DART: - strcpy(beam.beam_name, " dart"); + beam.name = " dart"; beam.damage = dice_def( 1, 4 + (you.your_level / 2) ); dart_trap(trap_known, i, beam, false); break; case TRAP_NEEDLE: - strcpy(beam.beam_name, " needle"); + beam.name = " needle"; beam.damage = dice_def( 1, 0 ); dart_trap(trap_known, i, beam, true); break; case TRAP_ARROW: - strcpy(beam.beam_name, "n arrow"); + beam.name = "n arrow"; beam.damage = dice_def( 1, 7 + you.your_level ); dart_trap(trap_known, i, beam, false); break; case TRAP_BOLT: - strcpy(beam.beam_name, " bolt"); + beam.name = " bolt"; beam.damage = dice_def( 1, 13 + you.your_level ); dart_trap(trap_known, i, beam, false); break; case TRAP_SPEAR: - strcpy(beam.beam_name, " spear"); + beam.name = " spear"; beam.damage = dice_def( 1, 10 + you.your_level ); dart_trap(trap_known, i, beam, false); break; case TRAP_AXE: - strcpy(beam.beam_name, "n axe"); + beam.name = "n axe"; beam.damage = dice_def( 1, 15 + you.your_level ); dart_trap(trap_known, i, beam, false); break; @@ -1462,7 +1536,7 @@ void disarm_trap( struct dist &disa ) { mpr("You failed to disarm the trap."); - you.turn_is_over = 1; + you.turn_is_over = true; if (random2(you.dex) > 5 + random2(5 + you.your_level)) exercise(SK_TRAPS_DOORS, 1 + random2(you.your_level / 5)); @@ -1499,7 +1573,7 @@ void disarm_trap( struct dist &disa ) grd[you.x_pos + disa.dx][you.y_pos + disa.dy] = DNGN_FLOOR; env.trap[i].type = TRAP_UNASSIGNED; - you.turn_is_over = 1; + you.turn_is_over = true; // reduced from 5 + random2(5) exercise(SK_TRAPS_DOORS, 1 + random2(5) + (you.your_level / 5)); @@ -1604,7 +1678,9 @@ void weird_writing(char stringy[40]) } // end weird_writing() // must be a better name than 'place' for the first parameter {dlb} -void fall_into_a_pool(bool place, unsigned char terrain) +// returns true if we manage to scramble free. +bool fall_into_a_pool( int entry_x, int entry_y, bool allow_shift, + unsigned char terrain ) { bool escape = false; FixedVector< char, 2 > empty; @@ -1613,7 +1689,7 @@ void fall_into_a_pool(bool place, unsigned char terrain) { // These can happen when we enter deep water directly -- bwr merfolk_start_swimming(); - return; + return (false); } strcpy(info, "You fall into the "); @@ -1635,67 +1711,73 @@ void fall_into_a_pool(bool place, unsigned char terrain) if (resist <= 0) { mpr( "The lava burns you to a cinder!" ); - ouch( -9999, 0, KILLED_BY_LAVA ); + ouch( INSTANT_DEATH, 0, KILLED_BY_LAVA ); } else { // should boost # of bangs per damage in the future {dlb} mpr( "The lava burns you!" ); - ouch( (10 + random2avg(100, 2)) / resist, 0, KILLED_BY_LAVA ); + ouch( (10 + roll_dice(2,50)) / resist, 0, KILLED_BY_LAVA ); } - if (you.duration[DUR_CONDENSATION_SHIELD] > 0) - { - mpr("Your icy shield dissipates!", MSGCH_DURATION); - you.duration[DUR_CONDENSATION_SHIELD] = 0; - you.redraw_armour_class = 1; - } + expose_player_to_element( BEAM_LAVA, 14 ); } // a distinction between stepping and falling from you.levitation // prevents stepping into a thin stream of lava to get to the other side. if (scramble()) { - if (place) + if (allow_shift) { - if (empty_surrounds(you.x_pos, you.y_pos, DNGN_FLOOR, false, empty)) + if (empty_surrounds( you.x_pos, you.y_pos, DNGN_FLOOR, 1, + false, empty )) { - you.x_pos = empty[0]; - you.y_pos = empty[1]; escape = true; } else + { escape = false; + } } else - escape = true; + { + // back out the way we came in, if possible + if (grid_distance( you.x_pos, you.y_pos, entry_x, entry_y ) == 1 + && (entry_x != empty[0] || entry_y != empty[1])) + { + escape = true; + empty[0] = entry_x; + empty[1] = entry_y; + } + else // zero or two or more squares away, with no way back + { + escape = false; + } + } } else { - // that is, don't display following when fall from levitating - if (!place) - mpr("You try to escape, but your burden drags you down!"); + mpr("You try to escape, but your burden drags you down!"); } - if (escape) + if (escape && move_player_to_grid( empty[0], empty[1], false, false, true )) { mpr("You manage to scramble free!"); if (terrain == DNGN_LAVA) - scrolls_burn(10, OBJ_SCROLLS); + expose_player_to_element( BEAM_LAVA, 14 ); - return; + return (true); } mpr("You drown..."); if (terrain == DNGN_LAVA) - ouch(-9999, 0, KILLED_BY_LAVA); + ouch( INSTANT_DEATH, 0, KILLED_BY_LAVA ); else if (terrain == DNGN_DEEP_WATER) - ouch(-9999, 0, KILLED_BY_WATER); + ouch( INSTANT_DEATH, 0, KILLED_BY_WATER ); - // Okay, so you don't trigger a trap when you scramble onto it. - //I really can't be bothered right now. + return (false); } // end fall_into_a_pool() bool scramble(void) @@ -1823,6 +1905,9 @@ bool trap_item(char base_type, char sub_type, char beam_x, char beam_y) else { set_item_ego_type( item, OBJ_MISSILES, SPMSL_NORMAL ); + + if (sub_type == MI_ARROW) + item.colour = BROWN; } } else @@ -1888,3 +1973,224 @@ int trap_at_xy(int which_x, int which_y) // no idea how well this will be handled elsewhere: {dlb} return (-1); } // end trap_at_xy() + +bool i_feel_safe() +{ + /* This is probably unnecessary, but I'm not sure that + you're always at least 9 away from a wall */ + int ystart = you.y_pos - 9, xstart = you.x_pos - 9; + int yend = you.y_pos + 9, xend = you.x_pos + 9; + if ( xstart < 0 ) xstart = 0; + if ( ystart < 0 ) ystart = 0; + if ( xend >= GXM ) xend = 0; + if ( ystart >= GYM ) yend = 0; + + /* statue check */ + if (you.visible_statue[STATUE_SILVER] || + you.visible_statue[STATUE_ORANGE_CRYSTAL] ) + return false; + + /* monster check */ + for ( int y = ystart; y < yend; ++y ) { + for ( int x = xstart; x < xend; ++x ) { + /* if you can see a nonfriendly monster then you feel + unsafe */ + if ( see_grid(x,y) ) { + const unsigned char targ_monst = mgrd[x][y]; + if ( targ_monst != NON_MONSTER ) { + struct monsters *mon = &menv[targ_monst]; + if ( !mons_friendly(mon) && + player_monster_visible(mon) && + !mons_is_mimic(mon->type) && + (!Options.safe_zero_exp || + !mons_class_flag( mon->type, M_NO_EXP_GAIN ))) { + return false; + } + } + } + } + } + return true; +} + +// Do not attempt to use level_id if level_type != LEVEL_DUNGEON +std::string short_place_name(level_id id) +{ + return short_place_name( + get_packed_place(id.branch, id.depth, LEVEL_DUNGEON)); +} + +unsigned short get_packed_place( unsigned char branch, int subdepth, + char level_type ) +{ + unsigned short place = (unsigned short) + ( (branch << 8) | (subdepth & 0xFF) ); + if (level_type == LEVEL_ABYSS || level_type == LEVEL_PANDEMONIUM + || level_type == LEVEL_LABYRINTH) + place = (unsigned short) ( (level_type << 8) | 0xFF ); + return place; +} + +unsigned short get_packed_place() +{ + return get_packed_place( you.where_are_you, + subdungeon_depth(you.where_are_you, you.your_level), + you.level_type ); +} + +std::string place_name( unsigned short place, bool long_name, + bool include_number ) { + + unsigned char branch = (unsigned char) ((place >> 8) & 0xFF); + int lev = place & 0xFF; + + std::string result; + if (lev == 0xFF) + { + switch (branch) + { + case LEVEL_ABYSS: + return ( long_name ? "The Abyss" : "Abyss" ); + case LEVEL_PANDEMONIUM: + return ( long_name ? "Pandemonium" : "Pan" ); + case LEVEL_LABYRINTH: + return ( long_name ? "a Labyrinth" : "Lab" ); + default: + return ( long_name ? "Buggy Badlands" : "Bug" ); + } + } + else + { + switch (branch) + { + case BRANCH_VESTIBULE_OF_HELL: + return ( long_name ? "The Vestibule of Hell" : "Hell" ); + case BRANCH_HALL_OF_BLADES: + return ( long_name ? "The Hall of Blades" : "Blade" ); + case BRANCH_ECUMENICAL_TEMPLE: + return ( long_name ? "The Ecumenical Temple" : "Temple" ); + case BRANCH_DIS: + result = ( long_name ? "The Iron City of Dis" : "Dis"); + break; + case BRANCH_GEHENNA: + result = ( long_name ? "Gehenna" : "Geh" ); + break; + case BRANCH_COCYTUS: + result = ( long_name ? "Cocytus" : "Coc" ); + break; + case BRANCH_TARTARUS: + result = ( long_name ? "Tartarus" : "Tar" ); + break; + case BRANCH_ORCISH_MINES: + result = ( long_name ? "The Orcish Mines" : "Orc" ); + break; + case BRANCH_HIVE: + result = ( long_name ? "The Hive" : "Hive" ); + break; + case BRANCH_LAIR: + result = ( long_name ? "The Lair" : "Lair" ); + break; + case BRANCH_SLIME_PITS: + result = ( long_name ? "The Slime Pits" : "Slime" ); + break; + case BRANCH_VAULTS: + result = ( long_name ? "The Vaults" : "Vault" ); + break; + case BRANCH_CRYPT: + result = ( long_name ? "The Crypt" : "Crypt" ); + break; + case BRANCH_HALL_OF_ZOT: + result = ( long_name ? "The Hall of Zot" : "Zot" ); + break; + case BRANCH_SNAKE_PIT: + result = ( long_name ? "The Snake Pit" : "Snake" ); + break; + case BRANCH_ELVEN_HALLS: + result = ( long_name ? "The Elven Halls" : "Elf" ); + break; + case BRANCH_TOMB: + result = ( long_name ? "The Tomb" : "Tomb" ); + break; + case BRANCH_SWAMP: + result = ( long_name ? "The Swamp" : "Swamp" ); + break; + default: + result = ( long_name ? "The Dungeon" : "D" ); + break; + } + } + + if ( include_number ) { + char buf[200]; + if ( long_name ) { + // decapitalize 'the' + if ( result.find("The") == 0 ) + result[0] = 't'; + snprintf( buf, sizeof buf, "Level %d of %s", + lev, result.c_str() ); + } + else if (lev) { + snprintf( buf, sizeof buf, "%s:%d", + result.c_str(), lev ); + } + else { + snprintf( buf, sizeof buf, "%s:$", + result.c_str() ); + } + result = buf; + } + return result; +} + +// Takes a packed 'place' and returns a compact stringified place name. +// XXX: This is done in several other places; a unified function to +// describe places would be nice. +std::string short_place_name(unsigned short place) +{ + return place_name( place, false, true ); +} + +// Prepositional form of branch level name. For example, "in the +// Abyss" or "on level 3 of the Main Dungeon". +std::string prep_branch_level_name(unsigned short packed_place) +{ + std::string place = place_name( packed_place, true, true ); + if (place.length() && place != "Pandemonium") + place[0] = tolower(place[0]); + return (place.find("level") == 0? + "on " + place + : "in " + place); +} + +// Use current branch and depth +std::string prep_branch_level_name() +{ + return prep_branch_level_name( get_packed_place() ); +} + +int absdungeon_depth(unsigned char branch, int subdepth) +{ + int realdepth = subdepth - 1; + + if (branch >= BRANCH_ORCISH_MINES && branch <= BRANCH_SWAMP) + realdepth = subdepth + you.branch_stairs[branch - 10]; + + if (branch >= BRANCH_DIS && branch <= BRANCH_THE_PIT) + realdepth = subdepth + 26; + + return realdepth; +} + +int subdungeon_depth(unsigned char branch, int depth) +{ + int curr_subdungeon_level = depth + 1; + + if (branch >= BRANCH_DIS && branch <= BRANCH_THE_PIT) + curr_subdungeon_level = depth - 26; + + if (branch >= BRANCH_ORCISH_MINES && branch <= BRANCH_SWAMP) + curr_subdungeon_level = depth + - you.branch_stairs[branch - 10]; + + return curr_subdungeon_level; +} diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h index 7a754ffdd0..a11d043940 100644 --- a/crawl-ref/source/misc.h +++ b/crawl-ref/source/misc.h @@ -3,6 +3,8 @@ * Summary: Misc functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -14,6 +16,7 @@ #include "externs.h" +#include "travel.h" // last updated 08jan2001 {gdl} /* *********************************************************************** @@ -54,7 +57,8 @@ void down_stairs(bool remove_stairs, int old_level, bool force = false); /* *********************************************************************** * called from: acr * *********************************************************************** */ -void fall_into_a_pool(bool place, unsigned char grype); +bool fall_into_a_pool( int entry_x, int entry_y, bool allow_shift, + unsigned char terrain ); // last updated 12may2000 {dlb} @@ -131,5 +135,45 @@ void weird_writing(char stringy[40]); * *********************************************************************** */ unsigned char trap_category(unsigned char trap_type); +bool grid_is_wall(int grid); +bool grid_is_opaque(int grid); +bool grid_is_solid(int grid); +bool grid_is_water(int grid); +god_type grid_altar_god( unsigned char grid ); +bool grid_is_branch_stairs( unsigned char grid ); +int grid_secret_door_appearance( int gx, int gy ); +bool grid_destroys_items( int grid ); + +const char *grid_item_destruction_message( unsigned char grid ); + +void curare_hits_player(int agent, int degree); + +bool i_feel_safe(); + +////////////////////////////////////////////////////////////////////// +// Places and names +// +unsigned short get_packed_place(); + +unsigned short get_packed_place( unsigned char branch, int subdepth, + char level_type ); + +std::string short_place_name(unsigned short place); +std::string short_place_name(level_id id); +std::string place_name( unsigned short place, bool long_name = false, + bool include_number = true ); + +// Prepositional form of branch level name. For example, "in the +// Abyss" or "on level 3 of the Main Dungeon". +std::string prep_branch_level_name(unsigned short packed_place); +std::string prep_branch_level_name(); + +// Get displayable depth in the current branch, given the absolute +// depth. +int subdungeon_depth(unsigned char branch, int depth); + +// Get absolute depth given the displayable depth in the branch. +int absdungeon_depth(unsigned char branch, int subdepth); +////////////////////////////////////////////////////////////////////// #endif diff --git a/crawl-ref/source/misc/header b/crawl-ref/source/misc/header index 9f91952461..b49c13f3ad 100644 --- a/crawl-ref/source/misc/header +++ b/crawl-ref/source/misc/header @@ -3,6 +3,8 @@ * Summary: * Written by: * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> --/--/-- --- Created diff --git a/crawl-ref/source/misc/src-pkg-excludes.lst b/crawl-ref/source/misc/src-pkg-excludes.lst new file mode 100644 index 0000000000..1d28c6f19f --- /dev/null +++ b/crawl-ref/source/misc/src-pkg-excludes.lst @@ -0,0 +1,3 @@ +,svn +**/.svn/* +.* diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index 8580b8758b..47f443f951 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -1,6 +1,12 @@ +/* + * Modified for Crawl Reference by $Author$ on $Date$ + */ + #ifndef MONDATA_H #define MONDATA_H +#include "enum.h" + /* This whole file was very generously condensed from its initial ugly form by Wladimir van der Laan ($pellbinder). @@ -104,7 +110,8 @@ { MONS_PROGRAM_BUG, 'B', LIGHTRED, "program bug", M_NO_EXP_GAIN, - 0, 10, MONS_PROGRAM_BUG, MH_NATURAL, -3, + MR_NO_FLAGS, + 0, 10, MONS_PROGRAM_BUG, MONS_PROGRAM_BUG, MH_NATURAL, -3, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 0, 0, 0, 0, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -115,8 +122,9 @@ // real monsters begin here {dlb}: { MONS_GIANT_ANT, 'a', DARKGREY, "giant ant", - M_ED_POISON, - 700, 10, MONS_GIANT_ANT, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 700, 10, MONS_GIANT_ANT, MONS_GIANT_ANT, MH_NATURAL, -3, { 8, 0, 0, 0 }, { 3, 3, 5, 0 }, 4, 10, 12, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_INSECT, @@ -127,7 +135,8 @@ { MONS_GIANT_BAT, 'b', DARKGREY, "giant bat", M_FLIES | M_SEE_INVIS | M_WARM_BLOOD, - 150, 4, MONS_GIANT_BAT, MH_NATURAL, -1, + MR_NO_FLAGS, + 150, 4, MONS_GIANT_BAT, MONS_GIANT_BAT, MH_NATURAL, -1, { 1, 0, 0, 0 }, { 1, 2, 3, 0 }, 1, 14, 30, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_ANIMAL, @@ -138,7 +147,8 @@ { MONS_CENTAUR, 'c', BROWN, "centaur", M_WARM_BLOOD, - 1500, 10, MONS_CENTAUR, MH_NATURAL, -3, + MR_NO_FLAGS, + 1500, 10, MONS_CENTAUR, MONS_CENTAUR, MH_NATURAL, -3, { 10, 0, 0, 0 }, { 4, 3, 5, 0 }, 3, 7, 15, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_SHOUT, I_NORMAL, @@ -148,8 +158,9 @@ { MONS_RED_DEVIL, '4', RED, "red devil", - M_RES_POISON | M_RES_HELLFIRE | M_ED_COLD | M_FLIES, - 0, 10, MONS_RED_DEVIL, MH_DEMONIC, -7, + M_FLIES | M_EVIL, + MR_RES_POISON | MR_RES_HELLFIRE | MR_VUL_COLD, + 0, 10, MONS_RED_DEVIL, MONS_RED_DEVIL, MH_DEMONIC, -7, { 18, 0, 0, 0 }, { 5, 3, 5, 0 }, 10, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -159,8 +170,9 @@ { MONS_ETTIN, 'C', BROWN, "ettin", - M_WARM_BLOOD, - 0, 10, MONS_ETTIN, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_HILL_GIANT, MONS_ETTIN, MH_NATURAL, -3, { 18, 12, 0, 0 }, { 7, 3, 5, 0 }, 3, 4, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SHOUT2, I_NORMAL, @@ -170,8 +182,9 @@ { MONS_FUNGUS, 'f', LIGHTGREY, "fungus", - M_NO_EXP_GAIN | M_RES_POISON, - 0, 10, MONS_FUNGUS, MH_PLANT, 5000, + M_NO_EXP_GAIN, + MR_RES_POISON, + 0, 10, MONS_PLANT, MONS_FUNGUS, MH_PLANT, MAG_IMMUNE, { 0, 0, 0, 0 }, { 8, 3, 5, 0 }, 1, 0, 0, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -181,8 +194,9 @@ { MONS_GOBLIN, 'g', LIGHTGREY, "goblin", - M_WARM_BLOOD, - 400, 10, MONS_GOBLIN, MH_NATURAL, -1, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 400, 10, MONS_GOBLIN, MONS_GOBLIN, MH_NATURAL, -1, { 4, 0, 0, 0 }, { 1, 2, 4, 0 }, 0, 12, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -193,7 +207,8 @@ { MONS_HOUND, 'h', BROWN, "hound", M_SEE_INVIS | M_WARM_BLOOD, - 300, 10, MONS_HOUND, MH_NATURAL, -3, + MR_NO_FLAGS, + 300, 10, MONS_HOUND, MONS_HOUND, MH_NATURAL, -3, { 6, 0, 0, 0 }, { 3, 3, 5, 0 }, 2, 13, 15, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_BARK, I_ANIMAL, @@ -204,8 +219,9 @@ // note: these things regenerate { MONS_IMP, '5', RED, "imp", - M_RES_POISON | M_RES_HELLFIRE | M_ED_COLD | M_FLIES | M_SEE_INVIS | M_SPEAKS, - 0, 13, MONS_IMP, MH_DEMONIC, -9, + M_FLIES | M_SEE_INVIS | M_SPEAKS | M_EVIL | M_SPECIAL_ABILITY, + MR_RES_POISON | MR_RES_HELLFIRE | MR_VUL_COLD, + 0, 13, MONS_IMP, MONS_IMP, MH_DEMONIC, -9, { 4, 0, 0, 0 }, { 3, 3, 3, 0 }, 3, 14, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -216,7 +232,8 @@ { MONS_JACKAL, 'j', YELLOW, "jackal", M_WARM_BLOOD, - 200, 10, MONS_JACKAL, MH_NATURAL, -1, + MR_NO_FLAGS, + 200, 10, MONS_HOUND, MONS_JACKAL, MH_NATURAL, -1, { 3, 0, 0, 0 }, { 1, 3, 5, 0 }, 2, 12, 14, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_BARK, I_ANIMAL, @@ -226,8 +243,9 @@ { MONS_KILLER_BEE, 'k', YELLOW, "killer bee", - M_ED_POISON | M_FLIES, - 150, 11, MONS_KILLER_BEE, MH_NATURAL, -3, + M_FLIES, + MR_VUL_POISON, + 150, 11, MONS_KILLER_BEE, MONS_KILLER_BEE, MH_NATURAL, -3, { 10, 0, 0, 0 }, { 3, 3, 5, 0 }, 2, 18, 20, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_BUZZ, I_INSECT, @@ -237,8 +255,9 @@ { MONS_KILLER_BEE_LARVA, 'w', LIGHTGREY, "killer bee larva", - M_ED_POISON | M_NO_SKELETON, - 150, 5, MONS_KILLER_BEE_LARVA, MH_NATURAL, -3, + M_NO_SKELETON, + MR_VUL_POISON, + 150, 5, MONS_KILLER_BEE_LARVA, MONS_KILLER_BEE_LARVA, MH_NATURAL, -3, { 3, 0, 0, 0 }, { 1, 3, 5, 0 }, 1, 5, 5, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_INSECT, @@ -248,8 +267,9 @@ { MONS_MANTICORE, 'm', BROWN, "manticore", - M_WARM_BLOOD, - 1800, 10, MONS_MANTICORE, MH_NATURAL, -3, + M_WARM_BLOOD | M_SPECIAL_ABILITY, + MR_NO_FLAGS, + 1800, 10, MONS_MANTICORE, MONS_MANTICORE, MH_NATURAL, -3, { 14, 8, 8, 0 }, { 9, 3, 5, 0 }, 5, 7, 7, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SILENT, I_NORMAL, @@ -260,8 +280,9 @@ // this thing doesn't have nr. 13 for nothing, has it? ($pellbinder) { MONS_NECROPHAGE, 'n', DARKGREY, "necrophage", - M_RES_POISON | M_RES_COLD, - 500, 10, MONS_NECROPHAGE, MH_UNDEAD, -5, + M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 500, 10, MONS_GHOUL, MONS_NECROPHAGE, MH_UNDEAD, -5, { 8, 0, 0, 0 }, { 5, 3, 5, 0 }, 2, 10, 10, 7, MST_NO_SPELLS, CE_HCL, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -271,8 +292,9 @@ { MONS_ORC, 'o', LIGHTRED, "orc", - M_WARM_BLOOD, - 600, 10, MONS_ORC, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 600, 10, MONS_ORC, MONS_ORC, MH_NATURAL, -3, { 5, 0, 0, 0 }, { 1, 4, 6, 0 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -284,8 +306,9 @@ // dangerous, but still come out at 200+ XP { MONS_PHANTOM, 'p', BLUE, "phantom", - M_RES_POISON | M_RES_COLD, - 0, 5, MONS_PHANTOM, MH_UNDEAD, -4, + M_EVIL | M_SPECIAL_ABILITY, + MR_RES_POISON | MR_RES_COLD, + 0, 5, MONS_PHANTOM, MONS_PHANTOM, MH_UNDEAD, -4, { 10, 0, 0, 0 }, { 7, 3, 5, 0 }, 3, 13, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -295,8 +318,9 @@ { MONS_QUASIT, 'q', LIGHTGREY, "quasit", - M_RES_POISON | M_RES_FIRE | M_RES_COLD, - 0, 10, MONS_QUASIT, MH_DEMONIC, 50, + M_EVIL, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD, + 0, 10, MONS_QUASIT, MONS_QUASIT, MH_DEMONIC, 50, { 3, 2, 2, 0 }, { 3, 2, 6, 0 }, 5, 17, 15, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_INSECT, @@ -307,7 +331,8 @@ { MONS_RAT, 'r', BROWN, "rat", M_WARM_BLOOD, - 200, 10, MONS_RAT, MH_NATURAL, -1, + MR_NO_FLAGS, + 200, 10, MONS_RAT, MONS_RAT, MH_NATURAL, -1, { 3, 0, 0, 0 }, { 1, 1, 3, 0 }, 1, 10, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_ANIMAL, @@ -317,8 +342,9 @@ { MONS_SCORPION, 's', DARKGREY, "scorpion", - M_ED_POISON, - 500, 10, MONS_SCORPION, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 500, 10, MONS_SCORPION, MONS_SCORPION, MH_NATURAL, -3, { 10, 0, 0, 0 }, { 3, 3, 5, 0 }, 5, 10, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_INSECT, @@ -331,8 +357,9 @@ // not until it can be reimplemented safely {dlb} { MONS_TUNNELING_WORM, 't', LIGHTRED, "tunneling worm", - M_RES_POISON, - 0, 10, 19, MH_NATURAL, 5000, + M_NO_FLAGS, + MR_RES_POISON, + 0, 10, 19, MH_NATURAL, MAG_IMMUNE, { 50, 0, 0, 0 }, { 10, 5, 5, 0 }, 3, 3, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_REPTILE, @@ -344,7 +371,8 @@ { MONS_UGLY_THING, 'u', BROWN, "ugly thing", M_WARM_BLOOD | M_AMPHIBIOUS, - 600, 10, MONS_UGLY_THING, MH_NATURAL, -3, + MR_NO_FLAGS, + 600, 10, MONS_UGLY_THING, MONS_UGLY_THING, MH_NATURAL, -3, { 12, 0, 0, 0 }, { 8, 3, 5, 0 }, 3, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -354,8 +382,9 @@ { MONS_FIRE_VORTEX, 'v', RED, "fire vortex", - M_RES_POISON | M_RES_FIRE | M_ED_COLD | M_RES_ELEC | M_LEVITATE | M_CONFUSED, - 0, 5, MONS_FIRE_VORTEX, MH_NONLIVING, 5000, + M_LEVITATE | M_CONFUSED, + MR_RES_POISON | MR_RES_FIRE | MR_VUL_COLD | MR_RES_ELEC, + 0, 5, MONS_FIRE_VORTEX, MONS_FIRE_VORTEX, MH_NONLIVING, MAG_IMMUNE, { 30, 0, 0, 0 }, { 3, 3, 5, 0 }, 0, 5, 15, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -366,7 +395,8 @@ { MONS_WORM, 'w', LIGHTRED, "worm", M_NO_SKELETON, - 350, 4, MONS_WORM, MH_NATURAL, -2, + MR_NO_FLAGS, + 350, 4, MONS_WORM, MONS_WORM, MH_NATURAL, -2, { 12, 0, 0, 0 }, { 5, 3, 5, 0 }, 1, 5, 6, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_INSECT, @@ -377,8 +407,9 @@ // random { MONS_ABOMINATION_SMALL, 'x', BLACK, "abomination", - M_NO_FLAGS, - 0, 10, MONS_ABOMINATION_SMALL, MH_DEMONIC, -5, + M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_ABOMINATION_SMALL, MONS_ABOMINATION_SMALL, MH_DEMONIC, -5, { 23, 0, 0, 0 }, { 6, 2, 5, 0 }, 0, 0, 0, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -388,8 +419,9 @@ { MONS_YELLOW_WASP, 'y', YELLOW, "yellow wasp", - M_ED_POISON | M_FLIES, - 220, 12, MONS_YELLOW_WASP, MH_NATURAL, -3, + M_FLIES, + MR_VUL_POISON, + 220, 12, MONS_YELLOW_WASP, MONS_YELLOW_WASP, MH_NATURAL, -3, { 13, 0, 0, 0 }, { 4, 3, 5, 0 }, 5, 14, 15, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_INSECT, @@ -400,8 +432,9 @@ // small zombie { MONS_ZOMBIE_SMALL, 'z', BROWN, "", - M_RES_POISON | M_RES_COLD, - 0, 6, MONS_ZOMBIE_SMALL, MH_UNDEAD, -1, + M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 6, MONS_ZOMBIE_SMALL, MONS_ZOMBIE_SMALL, MH_UNDEAD, -1, { 10, 0, 0, 0 }, { 1, 5, 5, 0 }, 0, 4, 5, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -411,8 +444,9 @@ { MONS_ANGEL, 'A', WHITE, "Angel", - M_RES_POISON | M_FLIES | M_RES_ELEC | M_SPELLCASTER, - 0, 10, MONS_ANGEL, MH_HOLY, -8, + M_FLIES | M_SPELLCASTER, + MR_RES_POISON | MR_RES_ELEC, + 0, 10, MONS_ANGEL, MONS_ANGEL, MH_HOLY, -8, { 20, 0, 0, 0 }, { 9, 3, 5, 0 }, 10, 10, 10, 7, MST_ANGEL, CE_NOCORPSE, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -422,8 +456,9 @@ { MONS_GIANT_BEETLE, 'B', DARKGREY, "giant beetle", - M_ED_POISON, - 1000, 10, MONS_GIANT_BEETLE, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 1000, 10, MONS_GIANT_BEETLE, MONS_GIANT_BEETLE, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 5, 7, 6, 0 }, 10, 3, 5, 7, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_SILENT, I_INSECT, @@ -433,8 +468,9 @@ { MONS_CYCLOPS, 'C', BROWN, "cyclops", - M_WARM_BLOOD, - 2500, 10, MONS_CYCLOPS, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 2500, 10, MONS_HILL_GIANT, MONS_CYCLOPS, MH_NATURAL, -3, { 35, 0, 0, 0 }, { 9, 3, 5, 0 }, 5, 3, 7, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_SHOUT, I_NORMAL, @@ -444,8 +480,9 @@ { MONS_DRAGON, 'D', GREEN, "dragon", - M_RES_POISON | M_RES_FIRE | M_ED_COLD | M_FLIES, //jmf: warm blood? - 2200, 12, MONS_DRAGON, MH_NATURAL, -4, + M_FLIES | M_SPECIAL_ABILITY, //jmf: warm blood? + MR_RES_POISON | MR_RES_FIRE | MR_VUL_COLD, + 2200, 12, MONS_DRAGON, MONS_DRAGON, MH_NATURAL, -4, { 20, 13, 13, 0 }, { 12, 5, 5, 0 }, 10, 8, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_SILENT, I_NORMAL, @@ -457,8 +494,9 @@ // that they wield two weapons... I'm raising their xp modifier. -- bwr { MONS_TWO_HEADED_OGRE, 'O', LIGHTRED, "two-headed ogre", - M_WARM_BLOOD, - 1500, 15, MONS_TWO_HEADED_OGRE, MH_NATURAL, -4, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 1500, 15, MONS_OGRE, MONS_TWO_HEADED_OGRE, MH_NATURAL, -4, { 17, 13, 0, 0 }, { 6, 3, 5, 0 }, 1, 4, 8, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SHOUT2, I_NORMAL, @@ -468,8 +506,9 @@ { MONS_FIEND, '1', LIGHTRED, "Fiend", //jmf: was RED, like Balrog - M_RES_POISON | M_RES_HELLFIRE | M_ED_COLD | M_FLIES | M_SEE_INVIS, - 0, 18, MONS_FIEND, MH_DEMONIC, -12, + M_FLIES | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_HELLFIRE | MR_VUL_COLD, + 0, 18, MONS_FIEND, MONS_FIEND, MH_DEMONIC, -12, { 25, 15, 15, 0 }, { 18, 3, 5, 0 }, 15, 6, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_ROAR, I_HIGH, @@ -479,8 +518,9 @@ { MONS_GIANT_SPORE, 'G', GREEN, "giant spore", - M_RES_POISON | M_LEVITATE, - 0, 10, MONS_GIANT_SPORE, MH_NATURAL, -3, + M_LEVITATE, + MR_RES_POISON | MR_RES_ASPHYX, + 0, 10, MONS_PLANT, MONS_GIANT_SPORE, MH_NATURAL, -3, { 1, 0, 0, 0 }, { 1, 0, 0, 1 }, 0, 10, 15, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -490,8 +530,9 @@ { MONS_HOBGOBLIN, 'g', BROWN, "hobgoblin", - M_WARM_BLOOD, - 500, 10, MONS_HOBGOBLIN, MH_NATURAL, -1, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 500, 10, MONS_GOBLIN, MONS_HOBGOBLIN, MH_NATURAL, -1, { 5, 0, 0, 0 }, { 1, 4, 5, 0 }, 2, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -501,8 +542,9 @@ { MONS_ICE_BEAST, 'I', WHITE, "ice beast", - M_RES_POISON | M_ED_FIRE | M_RES_COLD, - 0, 12, MONS_ICE_BEAST, MH_NATURAL, -3, + M_NO_FLAGS, + MR_RES_POISON | MR_RES_ASPHYX | MR_VUL_FIRE | MR_RES_COLD, + 0, 12, MONS_ICE_BEAST, MONS_ICE_BEAST, MH_NATURAL, -3, { 5, 0, 0, 0 }, { 5, 3, 5, 0 }, 5, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_ANIMAL_LIKE, @@ -512,8 +554,9 @@ { MONS_JELLY, 'J', LIGHTRED, "jelly", - M_RES_POISON | M_SEE_INVIS | M_SPLITS | M_AMPHIBIOUS, - 0, 13, MONS_JELLY, MH_NATURAL, -3, + M_SEE_INVIS | M_SPLITS | M_AMPHIBIOUS, + MR_RES_POISON | MR_RES_ASPHYX, + 0, 13, MONS_JELLY, MONS_JELLY, MH_NATURAL, -3, { 8, 0, 0, 0 }, { 3, 5, 5, 0 }, 0, 2, 9, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -524,7 +567,8 @@ { MONS_KOBOLD, 'K', BROWN, "kobold", M_WARM_BLOOD, - 400, 10, MONS_KOBOLD, MH_NATURAL, -1, + MR_NO_FLAGS, + 400, 10, MONS_KOBOLD, MONS_KOBOLD, MH_NATURAL, -1, { 4, 0, 0, 0 }, { 1, 2, 3, 0 }, 2, 12, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SHOUT, I_NORMAL, @@ -534,8 +578,9 @@ { MONS_LICH, 'L', WHITE, "lich", - M_RES_POISON | M_RES_COLD | M_RES_ELEC | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS, - 0, 16, MONS_LICH, MH_UNDEAD, -11, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD | MR_RES_ELEC, + 0, 16, MONS_LICH, MONS_LICH, MH_UNDEAD, -11, { 15, 0, 0, 0 }, { 20, 2, 4, 0 }, 10, 10, 10, 7, MST_LICH_I, CE_NOCORPSE, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -545,8 +590,9 @@ { MONS_MUMMY, 'M', WHITE, "mummy", - M_RES_POISON | M_ED_FIRE | M_RES_COLD, - 0, 10, MONS_MUMMY, MH_UNDEAD, -5, + M_EVIL, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD, + 0, 10, MONS_MUMMY, MONS_MUMMY, MH_UNDEAD, -5, { 20, 0, 0, 0 }, { 3, 5, 3, 0 }, 3, 6, 6, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_ANIMAL, @@ -556,8 +602,9 @@ { MONS_GUARDIAN_NAGA, 'N', LIGHTGREEN, "guardian naga", - M_RES_POISON | M_SPELLCASTER | M_SEE_INVIS | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 350, 10, MONS_GUARDIAN_NAGA, MH_NATURAL, -6, + M_SPELLCASTER | M_SEE_INVIS | M_ACTUAL_SPELLS | M_WARM_BLOOD, + MR_RES_POISON, + 350, 10, MONS_NAGA, MONS_GUARDIAN_NAGA, MH_NATURAL, -6, { 19, 0, 0, 0 }, { 8, 3, 5, 0 }, 6, 14, 15, 7, MST_GUARDIAN_NAGA, CE_MUTAGEN_RANDOM, Z_SMALL, S_SHOUT, I_HIGH, @@ -567,8 +614,9 @@ { MONS_OGRE, 'O', BROWN, "ogre", - M_WARM_BLOOD, - 1300, 10, MONS_OGRE, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 1300, 10, MONS_OGRE, MONS_OGRE, MH_NATURAL, -3, { 17, 0, 0, 0 }, { 5, 3, 5, 0 }, 1, 6, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SHOUT, I_NORMAL, @@ -579,7 +627,8 @@ { MONS_PLANT, 'P', GREEN, "plant", M_NO_EXP_GAIN, - 0, 10, MONS_PLANT, MH_PLANT, 5000, + MR_NO_FLAGS, + 0, 10, MONS_PLANT, MONS_PLANT, MH_PLANT, MAG_IMMUNE, { 0, 0, 0, 0 }, { 10, 3, 5, 0 }, 10, 0, 0, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -589,8 +638,9 @@ { MONS_QUEEN_BEE, 'Q', YELLOW, "queen bee", - M_ED_POISON | M_FLIES, - 200, 14, MONS_QUEEN_BEE, MH_NATURAL, -3, + M_FLIES, + MR_VUL_POISON, + 200, 14, MONS_KILLER_BEE, MONS_QUEEN_BEE, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 7, 3, 5, 0 }, 10, 10, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_INSECT, @@ -600,8 +650,9 @@ { MONS_RAKSHASA, 'R', YELLOW, "rakshasa", - M_RES_POISON | M_SPELLCASTER | M_SEE_INVIS, - 0, 15, MONS_RAKSHASA, MH_NATURAL, -10, + M_SPELLCASTER | M_SEE_INVIS | M_EVIL, + MR_RES_POISON, + 0, 15, MONS_RAKSHASA, MONS_RAKSHASA, MH_NATURAL, -10, { 20, 0, 0, 0 }, { 10, 3, 5, 0 }, 10, 14, 10, 7, MST_RAKSHASA, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -612,7 +663,8 @@ { MONS_SNAKE, 'S', GREEN, "snake", M_COLD_BLOOD | M_AMPHIBIOUS, - 200, 10, MONS_SNAKE, MH_NATURAL, -3, + MR_NO_FLAGS, + 200, 10, MONS_SNAKE, MONS_SNAKE, MH_NATURAL, -3, { 5, 0, 0, 0 }, { 2, 3, 5, 0 }, 1, 15, 13, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_REPTILE, @@ -622,8 +674,9 @@ { MONS_TROLL, 'T', BROWN, "troll", - M_WARM_BLOOD, - 1500, 10, MONS_TROLL, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 1500, 10, MONS_TROLL, MONS_TROLL, MH_NATURAL, -3, { 20, 15, 15, 0 }, { 7, 3, 5, 0 }, 3, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SHOUT, I_NORMAL, @@ -633,8 +686,9 @@ { MONS_UNSEEN_HORROR, 'x', MAGENTA, "unseen horror", - M_LEVITATE | M_SEE_INVIS | M_RES_ELEC | M_INVIS, - 0, 12, MONS_UNSEEN_HORROR, MH_NATURAL, -3, + M_LEVITATE | M_SEE_INVIS | M_INVIS, + MR_RES_ELEC, + 0, 12, MONS_UNSEEN_HORROR, MONS_UNSEEN_HORROR, MH_NATURAL, -3, { 12, 0, 0, 0 }, { 7, 3, 5, 0 }, 5, 10, 30, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_ANIMAL_LIKE, @@ -644,8 +698,9 @@ { MONS_VAMPIRE, 'V', RED, "vampire", - M_RES_POISON | M_RES_COLD | M_SPELLCASTER | M_SEE_INVIS, - 0, 11, MONS_VAMPIRE, MH_UNDEAD, -6, + M_SPELLCASTER | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 11, MONS_VAMPIRE, MONS_VAMPIRE, MH_UNDEAD, -6, { 22, 0, 0, 0 }, { 6, 3, 5, 0 }, 10, 10, 10, 7, MST_VAMPIRE, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -655,8 +710,9 @@ { MONS_WRAITH, 'W', DARKGREY, "wraith", - M_RES_POISON | M_RES_COLD | M_LEVITATE | M_SEE_INVIS, - 0, 11, MONS_WRAITH, MH_UNDEAD, -7, + M_LEVITATE | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 11, MONS_WRAITH, MONS_WRAITH, MH_UNDEAD, -7, { 13, 0, 0, 0 }, { 6, 3, 5, 0 }, 10, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -667,8 +723,9 @@ // Large abom: (the previous one was small) { MONS_ABOMINATION_LARGE, 'X', BLACK, "abomination", - M_NO_FLAGS, - 0, 10, MONS_ABOMINATION_LARGE, MH_DEMONIC, -7, + M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_ABOMINATION_SMALL, MONS_ABOMINATION_LARGE, MH_DEMONIC, -7, { 40, 0, 0, 0 }, { 11, 2, 5, 0 }, 0, 0, 0, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -678,8 +735,9 @@ { MONS_YAK, 'Y', BROWN, "yak", - M_WARM_BLOOD, - 1200, 10, MONS_YAK, MH_NATURAL, -3, + M_WARM_BLOOD, + MR_NO_FLAGS, + 1200, 10, MONS_YAK, MONS_YAK, MH_NATURAL, -3, { 18, 0, 0, 0 }, { 7, 3, 5, 0 }, 4, 7, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_BELLOW, I_ANIMAL, @@ -690,8 +748,9 @@ // big zombie { MONS_ZOMBIE_LARGE, 'Z', BROWN, "", - M_RES_POISON | M_RES_COLD, - 0, 6, MONS_ZOMBIE_LARGE, MH_UNDEAD, -1, + M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 6, MONS_ZOMBIE_SMALL, MONS_ZOMBIE_LARGE, MH_UNDEAD, -1, { 23, 0, 0, 0 }, { 6, 3, 5, 0 }, 8, 5, 5, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -701,8 +760,9 @@ { MONS_ORC_WARRIOR, 'o', YELLOW, "orc warrior", - M_WARM_BLOOD, - 0, 10, MONS_ORC, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_ORC, MONS_ORC, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 4, 4, 6, 0 }, 0, 13, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -712,8 +772,9 @@ { MONS_KOBOLD_DEMONOLOGIST, 'K', MAGENTA, "kobold demonologist", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 0, 10, MONS_KOBOLD, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_KOBOLD, MONS_KOBOLD, MH_NATURAL, -5, { 4, 0, 0, 0 }, { 4, 3, 5, 0 }, 2, 13, 10, 7, MST_KOBOLD_DEMONOLOGIST, CE_POISONOUS, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -723,8 +784,9 @@ { MONS_ORC_WIZARD, 'o', MAGENTA, "orc wizard", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 0, 10, MONS_ORC, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_ORC, MONS_ORC, MH_NATURAL, -5, { 5, 0, 0, 0 }, { 3, 3, 4, 0 }, 1, 12, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -734,8 +796,9 @@ { MONS_ORC_KNIGHT, 'o', LIGHTCYAN, "orc knight", - M_WARM_BLOOD, - 0, 10, MONS_ORC, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_ORC, MONS_ORC, MH_NATURAL, -3, { 25, 0, 0, 0 }, { 9, 4, 7, 0 }, 2, 13, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -748,8 +811,9 @@ // not until it can be reimplemented safely {dlb} { MONS_WORM_TAIL, '~', LIGHTRED, "worm tail", - M_NO_EXP_GAIN | M_RES_POISON, - 0, 10, 56, MH_NATURAL, 5000, + M_NO_EXP_GAIN, + MR_RES_POISON, + 0, 10, 56, MH_NATURAL, MAG_IMMUNE, { 0, 0, 0, 0 }, { 10, 5, 5, 0 }, 3, 3, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -760,8 +824,9 @@ { MONS_WYVERN, 'D', LIGHTRED, "wyvern", - M_NO_FLAGS, //jmf: warm blood? - 2000, 10, MONS_WYVERN, MH_NATURAL, -3, + M_NO_FLAGS, //jmf: warm blood? + MR_NO_FLAGS, + 2000, 10, MONS_WYVERN, MONS_WYVERN, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 5, 3, 5, 0 }, 5, 10, 15, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_ANIMAL, @@ -771,8 +836,9 @@ { MONS_BIG_KOBOLD, 'K', RED, "big kobold", - M_WARM_BLOOD, - 0, 10, MONS_BIG_KOBOLD, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_KOBOLD, MONS_BIG_KOBOLD, MH_NATURAL, -3, { 7, 0, 0, 0 }, { 5, 3, 5, 0 }, 3, 12, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_NORMAL, @@ -783,7 +849,8 @@ { MONS_GIANT_EYEBALL, 'G', WHITE, "giant eyeball", M_NO_SKELETON | M_LEVITATE, - 400, 10, MONS_GIANT_EYEBALL, MH_NATURAL, -3, + MR_RES_ASPHYX, + 400, 10, MONS_GIANT_EYEBALL, MONS_GIANT_EYEBALL, MH_NATURAL, -3, { 0, 0, 0, 0 }, { 3, 3, 5, 0 }, 0, 1, 3, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_PLANT, @@ -793,8 +860,9 @@ { MONS_WIGHT, 'W', LIGHTGREY, "wight", - M_RES_POISON | M_RES_COLD, - 0, 10, MONS_WIGHT, MH_UNDEAD, -4, + M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 10, MONS_WRAITH, MONS_WIGHT, MH_UNDEAD, -4, { 8, 0, 0, 0 }, { 3, 3, 5, 0 }, 4, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -804,8 +872,9 @@ { MONS_OKLOB_PLANT, 'P', GREEN, "oklob plant", - M_RES_POISON, - 0, 10, MONS_OKLOB_PLANT, MH_PLANT, -3, + M_SPECIAL_ABILITY, + MR_RES_POISON, + 0, 10, MONS_PLANT, MONS_OKLOB_PLANT, MH_PLANT, -3, { 0, 0, 0, 0 }, { 10, 3, 5, 0 }, 10, 0, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -815,8 +884,9 @@ { MONS_WOLF_SPIDER, 's', BROWN, "wolf spider", - M_ED_POISON, - 800, 10, MONS_WOLF_SPIDER, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 800, 10, MONS_WOLF_SPIDER, MONS_WOLF_SPIDER, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 8, 3, 5, 0 }, 3, 10, 15, 7, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_SILENT, I_INSECT, @@ -826,8 +896,9 @@ { MONS_SHADOW, ' ', BLACK, "shadow", - M_RES_POISON | M_RES_COLD | M_SEE_INVIS, - 0, 10, MONS_SHADOW, MH_UNDEAD, -5, + M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 10, MONS_WRAITH, MONS_SHADOW, MH_UNDEAD, -5, { 5, 0, 0, 0 }, { 3, 3, 5, 0 }, 12, 10, 10, 7, BLACK, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_ANIMAL, @@ -837,8 +908,9 @@ { MONS_HUNGRY_GHOST, 'p', GREEN, "hungry ghost", - M_RES_POISON | M_RES_COLD | M_SEE_INVIS | M_FLIES, - 0, 10, MONS_HUNGRY_GHOST, MH_UNDEAD, -4, + M_SEE_INVIS | M_FLIES | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 10, MONS_PHANTOM, MONS_HUNGRY_GHOST, MH_UNDEAD, -4, { 5, 0, 0, 0 }, { 7, 3, 5, 0 }, 0, 17, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -849,7 +921,8 @@ { MONS_EYE_OF_DRAINING, 'G', LIGHTGREY, "eye of draining", M_NO_SKELETON | M_LEVITATE | M_SEE_INVIS, - 400, 10, MONS_EYE_OF_DRAINING, MH_NATURAL, 5000, + MR_RES_ASPHYX, + 400, 10, MONS_GIANT_EYEBALL, MONS_EYE_OF_DRAINING, MH_NATURAL, MAG_IMMUNE, { 0, 0, 0, 0 }, { 7, 3, 5, 0 }, 3, 1, 5, 7, MST_NO_SPELLS, CE_MUTAGEN_RANDOM, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -859,8 +932,9 @@ { MONS_BUTTERFLY, 'b', BLACK, "butterfly", - M_FLIES | M_ED_POISON | M_CONFUSED, - 150, 10, MONS_BUTTERFLY, MH_NATURAL, -3, + M_FLIES | M_CONFUSED, + MR_VUL_POISON | MR_RES_ASPHYX, + 150, 10, MONS_BUTTERFLY, MONS_BUTTERFLY, MH_NATURAL, -3, { 0, 0, 0, 0 }, { 1, 3, 5, 0 }, 0, 25, 25, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_INSECT, @@ -870,8 +944,9 @@ { MONS_WANDERING_MUSHROOM, 'f', BROWN, "wandering mushroom", - M_RES_POISON, - 0, 10, MONS_WANDERING_MUSHROOM, MH_PLANT, -3, + M_NO_FLAGS, + MR_RES_POISON, + 0, 10, MONS_PLANT, MONS_WANDERING_MUSHROOM, MH_PLANT, -3, { 20, 0, 0, 0 }, { 8, 3, 5, 0 }, 5, 0, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -881,8 +956,9 @@ { MONS_EFREET, 'E', RED, "efreet", - M_RES_POISON | M_RES_FIRE | M_ED_COLD | M_SPELLCASTER | M_LEVITATE, - 0, 12, MONS_EFREET, MH_DEMONIC, -3, + M_SPELLCASTER | M_LEVITATE | M_EVIL, + MR_RES_POISON | MR_RES_FIRE | MR_VUL_COLD, + 0, 12, MONS_EFREET, MONS_EFREET, MH_DEMONIC, -3, { 12, 0, 0, 0 }, { 7, 3, 5, 0 }, 10, 5, 10, 7, MST_EFREET, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -893,7 +969,8 @@ { MONS_BRAIN_WORM, 'w', LIGHTMAGENTA, "brain worm", M_SPELLCASTER, - 150, 10, MONS_BRAIN_WORM, MH_NATURAL, -3, + MR_NO_FLAGS, + 150, 10, MONS_WORM, MONS_BRAIN_WORM, MH_NATURAL, -3, { 0, 0, 0, 0 }, { 5, 3, 3, 0 }, 1, 5, 10, 7, MST_BRAIN_WORM, CE_POISONOUS, Z_SMALL, S_SILENT, I_REPTILE, @@ -904,7 +981,8 @@ { MONS_GIANT_ORANGE_BRAIN, 'G', LIGHTRED, "giant orange brain", M_NO_SKELETON | M_SPELLCASTER | M_LEVITATE | M_SEE_INVIS, - 1000, 13, MONS_GIANT_ORANGE_BRAIN, MH_NATURAL, -8, + MR_RES_ASPHYX, + 1000, 13, MONS_GIANT_ORANGE_BRAIN, MONS_GIANT_ORANGE_BRAIN, MH_NATURAL, -8, { 0, 0, 0, 0 }, { 10, 3, 5, 0 }, 2, 4, 10, 7, MST_GIANT_ORANGE_BRAIN, CE_MUTAGEN_RANDOM, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -914,8 +992,9 @@ { MONS_BOULDER_BEETLE, 'B', LIGHTGREY, "boulder beetle", - M_ED_POISON, - 2500, 10, MONS_BOULDER_BEETLE, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 2500, 10, MONS_GIANT_BEETLE, MONS_BOULDER_BEETLE, MH_NATURAL, -3, { 35, 0, 0, 0 }, { 9, 3, 5, 0 }, 20, 2, 3, 7, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_SILENT, I_INSECT, @@ -925,8 +1004,9 @@ { MONS_FLYING_SKULL, 'z', WHITE, "flying skull", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC | M_LEVITATE, - 0, 10, MONS_FLYING_SKULL, MH_UNDEAD, -3, + M_LEVITATE, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_SKELETON_SMALL, MONS_FLYING_SKULL, MH_UNDEAD, -3, { 7, 0, 0, 0 }, { 2, 3, 5, 0 }, 10, 17, 15, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SCREAM, I_ANIMAL, @@ -936,8 +1016,9 @@ { MONS_HELL_HOUND, 'h', DARKGREY, "hell hound", - M_RES_POISON | M_RES_HELLFIRE | M_ED_COLD | M_SEE_INVIS, - 0, 10, MONS_HELL_HOUND, MH_DEMONIC, -3, + M_SEE_INVIS | M_EVIL | M_SPECIAL_ABILITY, + MR_RES_POISON | MR_RES_HELLFIRE | MR_VUL_COLD, + 0, 10, MONS_HOUND, MONS_HELL_HOUND, MH_DEMONIC, -3, { 13, 0, 0, 0 }, { 5, 3, 5, 0 }, 6, 13, 15, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_BARK, I_NORMAL, @@ -948,7 +1029,8 @@ { MONS_MINOTAUR, 'm', LIGHTRED, "minotaur", M_WARM_BLOOD, - 1500, 10, MONS_MINOTAUR, MH_NATURAL, -3, + MR_NO_FLAGS, + 1500, 10, MONS_MINOTAUR, MONS_MINOTAUR, MH_NATURAL, -3, { 35, 0, 0, 0 }, { 13, 3, 5, 0 }, 5, 7, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_BELLOW, I_NORMAL, @@ -958,8 +1040,9 @@ { MONS_ICE_DRAGON, 'D', WHITE, "ice dragon", - M_RES_POISON | M_ED_FIRE | M_RES_COLD | M_FLIES, - 2200, 10, MONS_ICE_DRAGON, MH_NATURAL, -3, + M_FLIES | M_SPECIAL_ABILITY, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD, + 2200, 10, MONS_DRAGON, MONS_ICE_DRAGON, MH_NATURAL, -3, { 17, 17, 17, 0 }, { 12, 5, 5, 0 }, 10, 8, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_SILENT, I_NORMAL, @@ -969,8 +1052,9 @@ { MONS_SLIME_CREATURE, 'J', GREEN, "slime creature", - M_RES_POISON | M_AMPHIBIOUS, - 0, 5, MONS_SLIME_CREATURE, MH_NATURAL, -3, + M_AMPHIBIOUS, + MR_RES_POISON, + 0, 5, MONS_SLIME_CREATURE, MONS_SLIME_CREATURE, MH_NATURAL, -3, { 22, 0, 0, 0 }, { 11, 3, 5, 0 }, 1, 4, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -980,8 +1064,9 @@ { MONS_FREEZING_WRAITH, 'W', LIGHTBLUE, "freezing wraith", - M_RES_POISON | M_ED_FIRE | M_RES_COLD | M_LEVITATE | M_SEE_INVIS, - 0, 10, MONS_FREEZING_WRAITH, MH_UNDEAD, -4, + M_LEVITATE | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD, + 0, 10, MONS_WRAITH, MONS_FREEZING_WRAITH, MH_UNDEAD, -4, { 19, 0, 0, 0 }, { 8, 3, 5, 0 }, 12, 10, 8, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -992,8 +1077,9 @@ // fake R - conjured by the R's illusion spell. { MONS_RAKSHASA_FAKE, 'R', YELLOW, "rakshasa", - M_RES_POISON, - 0, 10, MONS_RAKSHASA_FAKE, MH_NATURAL, 5000, + M_EVIL, + MR_RES_POISON, + 0, 10, MONS_RAKSHASA_FAKE, MONS_RAKSHASA_FAKE, MH_NATURAL, MAG_IMMUNE, { 0, 0, 0, 0 }, { 1, 0, 0, 1 }, 0, 30, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_ROAR, I_PLANT, @@ -1003,8 +1089,9 @@ { MONS_GREAT_ORB_OF_EYES, 'G', LIGHTGREEN, "great orb of eyes", - M_NO_SKELETON | M_RES_POISON | M_SPELLCASTER | M_LEVITATE | M_SEE_INVIS, - 900, 13, MONS_GREAT_ORB_OF_EYES, MH_NATURAL, 5000, + M_NO_SKELETON | M_SPELLCASTER | M_LEVITATE | M_SEE_INVIS, + MR_RES_POISON, + 900, 13, MONS_GIANT_EYEBALL, MONS_GREAT_ORB_OF_EYES, MH_NATURAL, MAG_IMMUNE, { 20, 0, 0, 0 }, { 12, 3, 5, 0 }, 10, 3, 10, 7, MST_GREAT_ORB_OF_EYES, CE_MUTAGEN_RANDOM, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -1013,20 +1100,22 @@ , { - MONS_HELLION, '3', BLACK, "hellion", - M_RES_POISON | M_RES_HELLFIRE | M_ED_COLD | M_SPELLCASTER | M_ON_FIRE, - 0, 11, MONS_HELLION, MH_DEMONIC, -7, + MONS_HELLION, '3', EC_FIRE, "hellion", + M_SPELLCASTER | M_EVIL, + MR_RES_POISON | MR_RES_HELLFIRE | MR_VUL_COLD, + 0, 11, MONS_HELLION, MONS_HELLION, MH_DEMONIC, -7, { 10, 0, 0, 0 }, { 7, 3, 5, 0 }, - 5, 10, 13, 7, RED, CE_NOCORPSE, Z_NOZOMBIE, S_SCREAM, I_HIGH, + 5, 10, 13, 7, MST_BURNING_DEVIL, CE_NOCORPSE, Z_NOZOMBIE, S_SCREAM, I_HIGH, MONUSE_OPEN_DOORS } , { MONS_ROTTING_DEVIL, '4', GREEN, "rotting devil", - M_RES_POISON | M_RES_COLD, - 0, 10, MONS_ROTTING_DEVIL, MH_DEMONIC, -7, + M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 10, MONS_ROTTING_DEVIL, MONS_ROTTING_DEVIL, MH_DEMONIC, -7, { 8, 0, 0, 0 }, { 5, 3, 5, 0 }, 2, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -1036,8 +1125,9 @@ { MONS_TORMENTOR, '3', YELLOW, "tormentor", - M_RES_POISON | M_RES_FIRE | M_SPELLCASTER | M_FLIES | M_SPEAKS, - 0, 10, MONS_TORMENTOR, MH_DEMONIC, -6, + M_SPELLCASTER | M_FLIES | M_SPEAKS | M_EVIL, + MR_RES_POISON | MR_RES_FIRE, + 0, 10, MONS_TORMENTOR, MONS_TORMENTOR, MH_DEMONIC, -6, { 8, 8, 0, 0 }, { 7, 3, 5, 0 }, 12, 12, 13, 7, MST_TORMENTOR, CE_NOCORPSE, Z_NOZOMBIE, S_ROAR, I_HIGH, @@ -1047,8 +1137,9 @@ { MONS_REAPER, '2', LIGHTGREY, "reaper", - M_RES_POISON | M_RES_COLD | M_SEE_INVIS, - 0, 10, MONS_REAPER, MH_DEMONIC, 5000, + M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 10, MONS_REAPER, MONS_REAPER, MH_DEMONIC, MAG_IMMUNE, { 32, 0, 0, 0 }, { 8, 3, 5, 0 }, 15, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -1058,8 +1149,9 @@ { MONS_SOUL_EATER, '2', DARKGREY, "soul eater", - M_RES_POISON | M_RES_COLD | M_LEVITATE | M_SEE_INVIS, - 0, 12, MONS_SOUL_EATER, MH_DEMONIC, -10, + M_LEVITATE | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 12, MONS_SOUL_EATER, MONS_SOUL_EATER, MH_DEMONIC, -10, { 25, 0, 0, 0 }, { 11, 3, 5, 0 }, 18, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -1069,8 +1161,9 @@ { MONS_HAIRY_DEVIL, '4', LIGHTRED, "hairy devil", - M_RES_POISON, - 0, 10, MONS_HAIRY_DEVIL, MH_DEMONIC, -4, + M_EVIL, + MR_RES_POISON, + 0, 10, MONS_HAIRY_DEVIL, MONS_HAIRY_DEVIL, MH_DEMONIC, -4, { 9, 9, 0, 0 }, { 6, 3, 5, 0 }, 7, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -1080,8 +1173,9 @@ { MONS_ICE_DEVIL, '2', WHITE, "ice devil", - M_RES_POISON | M_ED_FIRE | M_RES_COLD | M_SEE_INVIS, - 0, 11, MONS_ICE_DEVIL, MH_DEMONIC, -6, + M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD, + 0, 11, MONS_ICE_DEVIL, MONS_ICE_DEVIL, MH_DEMONIC, -6, { 16, 0, 0, 0 }, { 11, 3, 5, 0 }, 12, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -1091,8 +1185,9 @@ { MONS_BLUE_DEVIL, '3', BLUE, "blue devil", - M_RES_POISON | M_ED_FIRE | M_RES_COLD | M_FLIES, - 0, 10, MONS_BLUE_DEVIL, MH_DEMONIC, -5, + M_FLIES | M_EVIL, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD, + 0, 10, MONS_BLUE_DEVIL, MONS_BLUE_DEVIL, MH_DEMONIC, -5, { 21, 0, 0, 0 }, { 7, 3, 5, 0 }, 14, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -1103,8 +1198,9 @@ // random { MONS_BEAST, '4', BROWN, "beast", - M_NO_FLAGS, - 0, 10, MONS_BEAST, MH_DEMONIC, -3, + M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_BEAST, MONS_BEAST, MH_DEMONIC, -3, { 12, 0, 0, 0 }, { 5, 3, 5, 0 }, 0, 0, 0, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_RANDOM, I_NORMAL, @@ -1114,8 +1210,9 @@ { MONS_IRON_DEVIL, '3', CYAN, "iron devil", - M_RES_ELEC | M_RES_POISON | M_RES_HELLFIRE | M_RES_COLD, - 0, 10, MONS_IRON_DEVIL, MH_DEMONIC, -6, + M_EVIL, + MR_RES_ELEC | MR_RES_POISON | MR_RES_HELLFIRE | MR_RES_COLD, + 0, 10, MONS_IRON_DEVIL, MONS_IRON_DEVIL, MH_DEMONIC, -6, { 14, 14, 0, 0 }, { 8, 3, 5, 0 }, 16, 8, 8, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SCREECH, I_HIGH, @@ -1126,7 +1223,8 @@ { MONS_GLOWING_SHAPESHIFTER, '@', RED, "glowing shapeshifter", M_NO_FLAGS, - 600, 10, MONS_SHAPESHIFTER, MH_NATURAL, -6, + MR_NO_FLAGS, + 600, 10, MONS_SHAPESHIFTER, MONS_GLOWING_SHAPESHIFTER, MH_NATURAL, -6, { 15, 0, 0, 0 }, { 10, 3, 5, 0 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_MUTAGEN_RANDOM, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -1137,7 +1235,8 @@ { MONS_SHAPESHIFTER, '@', LIGHTRED, "shapeshifter", M_NO_FLAGS, - 600, 10, MONS_SHAPESHIFTER, MH_NATURAL, -6, + MR_NO_FLAGS, + 600, 10, MONS_SHAPESHIFTER, MONS_SHAPESHIFTER, MH_NATURAL, -6, { 5, 0, 0, 0 }, { 7, 3, 5, 0 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_MUTAGEN_RANDOM, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -1147,8 +1246,9 @@ { MONS_GIANT_MITE, 's', LIGHTRED, "giant mite", - M_ED_POISON, - 350, 10, MONS_GIANT_MITE, MH_NATURAL, -1, + M_NO_FLAGS, + MR_VUL_POISON, + 350, 10, MONS_GIANT_MITE, MONS_GIANT_MITE, MH_NATURAL, -1, { 5, 0, 0, 0 }, { 2, 3, 5, 0 }, 1, 7, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_INSECT, @@ -1157,9 +1257,10 @@ , { - MONS_STEAM_DRAGON, 'd', LIGHTGREY, "steam dragon", + MONS_STEAM_DRAGON, 'D', LIGHTGREY, "steam dragon", M_SPELLCASTER | M_FLIES, - 1000, 10, MONS_STEAM_DRAGON, MH_NATURAL, -3, + MR_NO_FLAGS, + 1000, 10, MONS_DRAGON, MONS_STEAM_DRAGON, MH_NATURAL, -3, { 12, 0, 0, 0 }, { 4, 5, 5, 0 }, 5, 10, 10, 7, MST_STEAM_DRAGON, CE_CLEAN, Z_BIG, S_SILENT, I_ANIMAL_LIKE, @@ -1170,7 +1271,8 @@ { MONS_VERY_UGLY_THING, 'u', RED, "very ugly thing", M_WARM_BLOOD | M_AMPHIBIOUS, - 750, 10, MONS_VERY_UGLY_THING, MH_NATURAL, -3, + MR_NO_FLAGS, + 750, 10, MONS_UGLY_THING, MONS_VERY_UGLY_THING, MH_NATURAL, -3, { 17, 0, 0, 0 }, { 12, 3, 5, 0 }, 4, 8, 8, 7, MST_NO_SPELLS, CE_MUTAGEN_RANDOM, Z_BIG, S_SHOUT, I_NORMAL, @@ -1180,8 +1282,9 @@ { MONS_ORC_SORCERER, 'o', DARKGREY, "orc sorcerer", - M_RES_FIRE | M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 600, 12, MONS_ORC, MH_NATURAL, -3, + M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_EVIL, + MR_RES_FIRE, + 600, 12, MONS_ORC, MONS_ORC, MH_NATURAL, -3, { 7, 0, 0, 0 }, { 8, 2, 3, 0 }, 5, 12, 10, 7, MST_ORC_SORCERER, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -1192,7 +1295,8 @@ { MONS_HIPPOGRIFF, 'H', BROWN, "hippogriff", M_FLIES | M_WARM_BLOOD, - 1000, 10, MONS_HIPPOGRIFF, MH_NATURAL, -3, + MR_NO_FLAGS, + 1000, 10, MONS_HIPPOGRIFF, MONS_HIPPOGRIFF, MH_NATURAL, -3, { 10, 8, 8, 0 }, { 7, 3, 5, 0 }, 2, 7, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_SCREECH, I_ANIMAL, @@ -1203,7 +1307,8 @@ { MONS_GRIFFON, 'H', YELLOW, "griffon", M_FLIES | M_WARM_BLOOD, - 1800, 10, MONS_GRIFFON, MH_NATURAL, -3, + MR_NO_FLAGS, + 1800, 10, MONS_GRIFFON, MONS_GRIFFON, MH_NATURAL, -3, { 18, 10, 10, 0 }, { 12, 3, 5, 0 }, 4, 6, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_SCREECH, I_ANIMAL, @@ -1213,8 +1318,9 @@ { MONS_HYDRA, 'D', LIGHTGREEN, "hydra", - M_RES_POISON | M_AMPHIBIOUS, // because it likes the swamp -- bwr - 1800, 11, MONS_HYDRA, MH_NATURAL, -3, + M_AMPHIBIOUS, // because it likes the swamp -- bwr + MR_RES_POISON, + 1800, 11, MONS_HYDRA, MONS_HYDRA, MH_NATURAL, -3, { 18, 0, 0, 0 }, { 13, 3, 5, 0 }, 0, 5, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_ROAR, I_REPTILE, @@ -1225,8 +1331,9 @@ // small skeleton { MONS_SKELETON_SMALL, 'z', LIGHTGREY, "", - M_RES_POISON | M_RES_COLD, - 0, 10, MONS_SKELETON_SMALL, MH_UNDEAD, -1, + M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 10, MONS_SKELETON_SMALL, MONS_SKELETON_SMALL, MH_UNDEAD, -1, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 0, 0, 0, 0, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1237,8 +1344,9 @@ // large skeleton { MONS_SKELETON_LARGE, 'Z', LIGHTGREY, "", - M_RES_POISON | M_RES_COLD, - 0, 10, MONS_SKELETON_LARGE, MH_UNDEAD, -1, + M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 10, MONS_SKELETON_SMALL, MONS_SKELETON_LARGE, MH_UNDEAD, -1, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 0, 0, 0, 0, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1249,8 +1357,9 @@ { MONS_HELL_KNIGHT, '@', RED, "hell knight", - M_RES_FIRE | M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 550, 10, MONS_HUMAN, MH_NATURAL, -3, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_EVIL, + MR_RES_FIRE, + 550, 10, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -3, { 13, 0, 0, 0 }, { 10, 3, 6, 0 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -1260,8 +1369,9 @@ { MONS_NECROMANCER, '@', DARKGREY, "necromancer", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 550, 10, MONS_HUMAN, MH_NATURAL, -4, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 550, 10, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -4, { 6, 0, 0, 0 }, { 10, 2, 4, 0 }, 0, 13, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -1271,8 +1381,9 @@ { MONS_WIZARD, '@', MAGENTA, "wizard", - M_RES_ELEC | M_SPELLCASTER | M_SPEAKS | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 550, 10, MONS_HUMAN, MH_NATURAL, -4, + M_SPELLCASTER | M_SPEAKS | M_ACTUAL_SPELLS | M_WARM_BLOOD, + MR_RES_ELEC, + 550, 10, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -4, { 6, 0, 0, 0 }, { 10, 2, 4, 0 }, 0, 13, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -1282,8 +1393,9 @@ { MONS_ORC_PRIEST, 'o', LIGHTGREEN, "orc priest", - M_SPELLCASTER | M_PRIEST | M_WARM_BLOOD, - 600, 10, MONS_ORC, MH_NATURAL, -4, + M_SPELLCASTER | M_PRIEST | M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 600, 10, MONS_ORC, MONS_ORC, MH_NATURAL, -4, { 6, 0, 0, 0 }, { 3, 3, 4, 0 }, 1, 10, 10, 7, MST_ORC_PRIEST, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -1293,8 +1405,9 @@ { MONS_ORC_HIGH_PRIEST, 'o', GREEN, "orc high priest", - M_RES_HELLFIRE | M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS | M_PRIEST | M_WARM_BLOOD, - 600, 10, MONS_ORC, MH_NATURAL, -4, + M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS | M_PRIEST | M_WARM_BLOOD | M_EVIL, + MR_RES_HELLFIRE, + 600, 10, MONS_ORC, MONS_ORC, MH_NATURAL, -4, { 7, 0, 0, 0 }, { 11, 3, 4, 0 }, 1, 12, 10, 7, MST_ORC_HIGH_PRIEST, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -1310,7 +1423,8 @@ { MONS_HUMAN, '@', LIGHTGRAY, "human", M_WARM_BLOOD, - 550, 10, MONS_HUMAN, MH_NATURAL, -3, + MR_NO_FLAGS, + 550, 10, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -3, { 10, 0, 0, 0 }, { 1, 3, 5, 0 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -1321,7 +1435,8 @@ { MONS_GNOLL, 'g', YELLOW, "gnoll", M_WARM_BLOOD, - 750, 10, MONS_GNOLL, MH_NATURAL, -3, + MR_NO_FLAGS, + 750, 10, MONS_GNOLL, MONS_GNOLL, MH_NATURAL, -3, { 9, 0, 0, 0 }, { 2, 4, 5, 0 }, 2, 9, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -1331,8 +1446,9 @@ { MONS_CLAY_GOLEM, '8', BROWN, "clay golem", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC | M_SEE_INVIS, - 0, 10, MONS_CLAY_GOLEM, MH_NONLIVING, 5000, + M_SEE_INVIS, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_CLAY_GOLEM, MONS_CLAY_GOLEM, MH_NONLIVING, MAG_IMMUNE, { 11, 11, 0, 0 }, { 8, 7, 3, 0 }, 7, 5, 8, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1342,8 +1458,9 @@ { MONS_WOOD_GOLEM, '8', YELLOW, "wood golem", - M_RES_POISON | M_ED_FIRE | M_RES_COLD | M_RES_ELEC, - 0, 10, MONS_WOOD_GOLEM, MH_NONLIVING, 5000, + M_NO_FLAGS, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_CLAY_GOLEM, MONS_WOOD_GOLEM, MH_NONLIVING, MAG_IMMUNE, { 10, 0, 0, 0 }, { 6, 6, 3, 0 }, 5, 6, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1353,8 +1470,9 @@ { MONS_STONE_GOLEM, '8', LIGHTGREY, "stone golem", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC, - 0, 10, MONS_STONE_GOLEM, MH_NONLIVING, 5000, + M_NO_FLAGS, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_CLAY_GOLEM, MONS_STONE_GOLEM, MH_NONLIVING, MAG_IMMUNE, { 28, 0, 0, 0 }, { 12, 7, 4, 0 }, 12, 4, 7, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1364,8 +1482,9 @@ { MONS_IRON_GOLEM, '8', CYAN, "iron golem", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC | M_SEE_INVIS, - 0, 10, MONS_IRON_GOLEM, MH_NONLIVING, 5000, + M_SEE_INVIS, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_CLAY_GOLEM, MONS_IRON_GOLEM, MH_NONLIVING, MAG_IMMUNE, { 35, 0, 0, 0 }, { 15, 7, 4, 0 }, 15, 3, 7, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1375,8 +1494,9 @@ { MONS_CRYSTAL_GOLEM, '8', WHITE, "crystal golem", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC | M_SEE_INVIS, - 0, 10, MONS_CRYSTAL_GOLEM, MH_NONLIVING, 5000, + M_SEE_INVIS, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_CLAY_GOLEM, MONS_CRYSTAL_GOLEM, MH_NONLIVING, MAG_IMMUNE, { 40, 0, 0, 0 }, { 13, 7, 4, 0 }, 22, 3, 7, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1386,8 +1506,9 @@ { MONS_TOENAIL_GOLEM, '8', LIGHTGREY, "toenail golem", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC, - 0, 10, MONS_TOENAIL_GOLEM, MH_NONLIVING, 5000, + M_NO_FLAGS, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_CLAY_GOLEM, MONS_TOENAIL_GOLEM, MH_NONLIVING, MAG_IMMUNE, { 13, 0, 0, 0 }, { 9, 5, 3, 0 }, 8, 5, 8, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1396,9 +1517,10 @@ , { - MONS_MOTTLED_DRAGON, 'd', LIGHTMAGENTA, "mottled dragon", - M_RES_POISON | M_RES_FIRE | M_SPELLCASTER | M_FLIES, - 1100, 10, MONS_MOTTLED_DRAGON, MH_NATURAL, -3, + MONS_MOTTLED_DRAGON, 'D', RED, "mottled dragon", + M_SPELLCASTER | M_FLIES, + MR_RES_POISON | MR_RES_FIRE, + 1100, 10, MONS_DRAGON, MONS_MOTTLED_DRAGON, MH_NATURAL, -3, { 15, 0, 0, 0 }, { 5, 3, 5, 0 }, 5, 10, 10, 7, MST_MOTTLED_DRAGON, CE_POISONOUS, Z_BIG, S_SILENT, I_ANIMAL_LIKE, @@ -1408,8 +1530,9 @@ { MONS_EARTH_ELEMENTAL, '#', BROWN, "earth elemental", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC, - 0, 10, MONS_EARTH_ELEMENTAL, MH_NONLIVING, 5000, + M_NO_FLAGS, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_EARTH_ELEMENTAL, MONS_EARTH_ELEMENTAL, MH_NONLIVING, MAG_IMMUNE, { 40, 0, 0, 0 }, { 6, 5, 5, 0 }, 14, 4, 6, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1419,8 +1542,9 @@ { MONS_FIRE_ELEMENTAL, '#', YELLOW, "fire elemental", - M_RES_POISON | M_RES_HELLFIRE | M_ED_COLD | M_RES_ELEC | M_FLIES, - 0, 10, MONS_FIRE_ELEMENTAL, MH_NONLIVING, 5000, + M_FLIES, + MR_RES_POISON | MR_RES_HELLFIRE | MR_VUL_COLD | MR_RES_ELEC, + 0, 10, MONS_EARTH_ELEMENTAL, MONS_FIRE_ELEMENTAL, MH_NONLIVING, MAG_IMMUNE, { 5, 0, 0, 0 }, { 6, 3, 5, 0 }, 4, 12, 13, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1430,8 +1554,9 @@ { MONS_AIR_ELEMENTAL, 'v', LIGHTGREY, "air elemental", - M_RES_ELEC | M_RES_POISON | M_LEVITATE | M_SEE_INVIS | M_FLIES, - 0, 5, MONS_AIR_ELEMENTAL, MH_NONLIVING, 5000, + M_LEVITATE | M_SEE_INVIS | M_FLIES, + MR_RES_ELEC | MR_RES_POISON, + 0, 5, MONS_EARTH_ELEMENTAL, MONS_AIR_ELEMENTAL, MH_NONLIVING, MAG_IMMUNE, { 15, 0, 0, 0 }, { 6, 3, 5, 0 }, 2, 18, 25, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1443,8 +1568,9 @@ { MONS_ICE_FIEND, '1', WHITE, "Ice Fiend", - M_RES_POISON | M_ED_FIRE | M_RES_COLD | M_SPELLCASTER | M_FLIES | M_SEE_INVIS | M_FROZEN, - 0, 10, MONS_ICE_FIEND, MH_DEMONIC, -12, + M_SPELLCASTER | M_FLIES | M_SEE_INVIS | M_FROZEN | M_EVIL, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD, + 0, 10, MONS_FIEND, MONS_ICE_FIEND, MH_DEMONIC, -12, { 25, 25, 0, 0 }, { 18, 3, 5, 0 }, 15, 6, 10, 7, MST_ICE_FIEND, CE_CONTAMINATED, Z_NOZOMBIE, S_ROAR, I_HIGH, @@ -1454,8 +1580,9 @@ { MONS_SHADOW_FIEND, '1', DARKGREY, "Shadow Fiend", - M_RES_POISON | M_RES_COLD | M_RES_ELEC | M_SPELLCASTER | M_LEVITATE | M_SEE_INVIS, - 0, 10, MONS_SHADOW_FIEND, MH_DEMONIC, -13, + M_SPELLCASTER | M_LEVITATE | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_FIEND, MONS_SHADOW_FIEND, MH_DEMONIC, -13, { 25, 15, 15, 0 }, { 18, 3, 5, 0 }, 15, 6, 10, 7, MST_SHADOW_FIEND, CE_CONTAMINATED, Z_NOZOMBIE, S_ROAR, I_HIGH, @@ -1465,8 +1592,9 @@ { MONS_BROWN_SNAKE, 'S', BROWN, "brown snake", - M_RES_POISON | M_COLD_BLOOD | M_AMPHIBIOUS, - 300, 10, MONS_BROWN_SNAKE, MH_NATURAL, -3, + M_COLD_BLOOD | M_AMPHIBIOUS, + MR_RES_POISON, + 300, 10, MONS_SNAKE, MONS_BROWN_SNAKE, MH_NATURAL, -3, { 10, 0, 0, 0 }, { 4, 3, 5, 0 }, 2, 15, 14, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_HISS, I_REPTILE, @@ -1477,7 +1605,8 @@ { MONS_GIANT_LIZARD, 'l', GREEN, "giant lizard", M_COLD_BLOOD, - 600, 10, MONS_GIANT_LIZARD, MH_NATURAL, -3, + MR_NO_FLAGS, + 600, 10, MONS_GIANT_LIZARD, MONS_GIANT_LIZARD, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 5, 3, 5, 0 }, 4, 10, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_REPTILE, @@ -1487,8 +1616,9 @@ { MONS_SPECTRAL_WARRIOR, 'W', LIGHTGREEN, "spectral warrior", - M_RES_POISON | M_RES_COLD | M_LEVITATE | M_SEE_INVIS, - 0, 13, MONS_SPECTRAL_WARRIOR, MH_UNDEAD, -6, + M_LEVITATE | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 13, MONS_WRAITH, MONS_SPECTRAL_WARRIOR, MH_UNDEAD, -6, { 18, 0, 0, 0 }, { 9, 3, 5, 0 }, 12, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -1498,8 +1628,9 @@ { MONS_PULSATING_LUMP, 'J', RED, "pulsating lump", - M_RES_POISON | M_SEE_INVIS, - 0, 3, MONS_PULSATING_LUMP, MH_NATURAL, -3, + M_SEE_INVIS, + MR_RES_POISON | MR_RES_ASPHYX, + 0, 3, MONS_JELLY, MONS_PULSATING_LUMP, MH_NATURAL, -3, { 13, 0, 0, 0 }, { 10, 3, 5, 0 }, 2, 6, 5, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1509,8 +1640,9 @@ { MONS_STORM_DRAGON, 'D', LIGHTBLUE, "storm dragon", - M_RES_ELEC | M_RES_COLD | M_SPELLCASTER | M_FLIES, - 2800, 12, MONS_STORM_DRAGON, MH_NATURAL, -5, + M_SPELLCASTER | M_FLIES, + MR_RES_ELEC | MR_RES_COLD, + 2800, 12, MONS_DRAGON, MONS_STORM_DRAGON, MH_NATURAL, -5, { 25, 15, 15, 0 }, { 14, 5, 5, 0 }, 13, 10, 12, 7, MST_STORM_DRAGON, CE_CLEAN, Z_BIG, S_ROAR, I_NORMAL, @@ -1521,7 +1653,8 @@ { MONS_YAKTAUR, 'c', LIGHTRED, "yaktaur", M_WARM_BLOOD, - 2000, 10, MONS_YAKTAUR, MH_NATURAL, -3, + MR_NO_FLAGS, + 2000, 10, MONS_YAKTAUR, MONS_YAKTAUR, MH_NATURAL, -3, { 15, 0, 0, 0 }, { 8, 3, 5, 0 }, 4, 4, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_SHOUT, I_NORMAL, @@ -1531,8 +1664,9 @@ { MONS_DEATH_YAK, 'Y', DARKGREY, "death yak", - M_WARM_BLOOD, - 1500, 10, MONS_DEATH_YAK, MH_NATURAL, -5, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 1500, 10, MONS_YAK, MONS_DEATH_YAK, MH_NATURAL, -5, { 30, 0, 0, 0 }, { 14, 3, 5, 0 }, 9, 5, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_BELLOW, I_ANIMAL, @@ -1542,8 +1676,9 @@ { MONS_ROCK_TROLL, 'T', LIGHTGREY, "rock troll", - M_WARM_BLOOD, - 2200, 11, MONS_ROCK_TROLL, MH_NATURAL, -4, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 2200, 11, MONS_TROLL, MONS_ROCK_TROLL, MH_NATURAL, -4, { 30, 20, 20, 0 }, { 11, 3, 5, 0 }, 13, 6, 8, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SHOUT, I_NORMAL, @@ -1553,8 +1688,9 @@ { MONS_STONE_GIANT, 'C', LIGHTGREY, "stone giant", - M_WARM_BLOOD, - 3000, 10, MONS_STONE_GIANT, MH_NATURAL, -4, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 3000, 10, MONS_HILL_GIANT, MONS_STONE_GIANT, MH_NATURAL, -4, { 45, 0, 0, 0 }, { 16, 3, 5, 0 }, 12, 2, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SHOUT, I_NORMAL, @@ -1564,8 +1700,9 @@ { MONS_FLAYED_GHOST, 'p', RED, "flayed ghost", - M_RES_POISON | M_FLIES, - 0, 10, MONS_FLAYED_GHOST, MH_UNDEAD, -4, + M_FLIES | M_EVIL, + MR_RES_POISON, + 0, 10, MONS_PHANTOM, MONS_FLAYED_GHOST, MH_UNDEAD, -4, { 30, 0, 0, 0 }, { 11, 3, 5, 0 }, 0, 14, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1575,8 +1712,9 @@ { MONS_BUMBLEBEE, 'k', RED, "bumblebee", - M_ED_POISON | M_FLIES, - 300, 10, MONS_BUMBLEBEE, MH_NATURAL, -3, + M_FLIES, + MR_VUL_POISON, + 300, 10, MONS_KILLER_BEE, MONS_BUMBLEBEE, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 7, 3, 5, 0 }, 4, 15, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_BUZZ, I_INSECT, @@ -1586,8 +1724,9 @@ { MONS_REDBACK, 's', RED, "redback", - M_ED_POISON, - 1000, 14, MONS_REDBACK, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 1000, 14, MONS_WOLF_SPIDER, MONS_REDBACK, MH_NATURAL, -3, { 18, 0, 0, 0 }, { 6, 3, 5, 0 }, 2, 12, 15, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_INSECT, @@ -1597,8 +1736,9 @@ { MONS_INSUBSTANTIAL_WISP, 'p', LIGHTGREY, "insubstantial wisp", - M_RES_ELEC | M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_LEVITATE, - 0, 17, MONS_INSUBSTANTIAL_WISP, MH_NONLIVING, 5000, + M_LEVITATE | M_SPECIAL_ABILITY, + MR_RES_ELEC | MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD, + 0, 17, MONS_INSUBSTANTIAL_WISP, MONS_INSUBSTANTIAL_WISP, MH_NONLIVING, MAG_IMMUNE, { 12, 0, 0, 0 }, { 6, 1, 2, 0 }, 20, 20, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_MOAN, I_PLANT, @@ -1608,8 +1748,9 @@ { MONS_VAPOUR, '#', LIGHTGREY, "vapour", - M_RES_ELEC | M_RES_POISON | M_SPELLCASTER | M_LEVITATE | M_SEE_INVIS | M_INVIS | M_CONFUSED, - 0, 21, MONS_VAPOUR, MH_NONLIVING, 5000, + M_SPELLCASTER | M_LEVITATE | M_SEE_INVIS | M_INVIS | M_CONFUSED, + MR_RES_ELEC | MR_RES_POISON, + 0, 21, MONS_VAPOUR, MONS_VAPOUR, MH_NONLIVING, MAG_IMMUNE, { 0, 0, 0, 0 }, { 12, 2, 3, 0 }, 0, 12, 10, 7, MST_STORM_DRAGON, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1619,8 +1760,9 @@ { MONS_OGRE_MAGE, 'O', MAGENTA, "ogre-mage", - M_RES_ELEC | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_WARM_BLOOD, - 0, 16, MONS_OGRE, MH_NATURAL, -6, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_WARM_BLOOD | M_EVIL, + MR_RES_ELEC, + 0, 16, MONS_OGRE, MONS_OGRE, MH_NATURAL, -6, { 12, 0, 0, 0 }, { 10, 3, 5, 0 }, 1, 7, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SHOUT, I_HIGH, @@ -1630,8 +1772,9 @@ { MONS_SPINY_WORM, 'w', DARKGREY, "spiny worm", - M_ED_POISON, - 1300, 13, MONS_SPINY_WORM, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 1300, 13, MONS_WORM, MONS_SPINY_WORM, MH_NATURAL, -3, { 32, 0, 0, 0 }, { 12, 3, 5, 0 }, 10, 6, 9, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_PLANT, @@ -1643,8 +1786,9 @@ // to examine them. { MONS_DANCING_WEAPON, '(', BLACK, "dancing weapon", - M_RES_POISON | M_RES_HELLFIRE | M_RES_COLD | M_RES_ELEC | M_LEVITATE, - 0, 10, MONS_DANCING_WEAPON, MH_NONLIVING, 5000, + M_LEVITATE, + MR_RES_POISON | MR_RES_HELLFIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_DANCING_WEAPON, MONS_DANCING_WEAPON, MH_NONLIVING, MAG_IMMUNE, { 30, 0, 0, 0 }, { 15, 0, 0, 15 }, 10, 20, 15, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -1654,8 +1798,9 @@ { MONS_TITAN, 'C', MAGENTA, "titan", - M_RES_ELEC | M_SPELLCASTER | M_WARM_BLOOD | M_SEE_INVIS, - 3500, 12, MONS_TITAN, MH_NATURAL, -7, + M_SPELLCASTER | M_WARM_BLOOD | M_SEE_INVIS | M_EVIL, + MR_RES_ELEC, + 3500, 12, MONS_HILL_GIANT, MONS_TITAN, MH_NATURAL, -7, { 55, 0, 0, 0 }, { 20, 3, 5, 0 }, 10, 3, 10, 7, MST_TITAN, CE_CLEAN, Z_BIG, S_SHOUT, I_HIGH, @@ -1665,8 +1810,9 @@ { MONS_GOLDEN_DRAGON, 'D', YELLOW, "golden dragon", - M_RES_ELEC | M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_SPELLCASTER | M_FLIES | M_SEE_INVIS, - 3000, 17, MONS_GOLDEN_DRAGON, MH_NATURAL, -8, + M_SPELLCASTER | M_FLIES | M_SEE_INVIS, + MR_RES_ELEC | MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD, + 3000, 17, MONS_DRAGON, MONS_GOLDEN_DRAGON, MH_NATURAL, -8, { 40, 20, 20, 0 }, { 18, 4, 4, 0 }, 15, 7, 10, 7, MST_GOLDEN_DRAGON, CE_POISONOUS, Z_BIG, S_ROAR, I_HIGH, @@ -1679,10 +1825,11 @@ { MONS_ELF, 'e', DARKGREY, "elf", M_WARM_BLOOD, - 450, 10, MONS_ELF, MH_NATURAL, -3, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -3, { 10, 0, 0, 0 }, { 3, 3, 3, 0 }, - 0, 12, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SILENT, I_PLANT, + 0, 12, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SILENT, I_NORMAL, MONUSE_WEAPONS_ARMOUR } , @@ -1693,8 +1840,9 @@ // more intuitively know that this isn't a regular lizard. -- bwr { MONS_LINDWURM, 'd', LIGHTGREEN, "lindwurm", - M_NO_FLAGS, - 1000, 11, MONS_LINDWURM, MH_NATURAL, -3, + M_SPECIAL_ABILITY, + MR_NO_FLAGS, + 1000, 11, MONS_DRAGON, MONS_LINDWURM, MH_NATURAL, -3, { 20, 10, 10, 0 }, { 9, 3, 5, 0 }, 8, 6, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_ROAR, I_REPTILE, @@ -1704,8 +1852,9 @@ { MONS_ELEPHANT_SLUG, 'm', LIGHTGREY, "elephant slug", - M_ED_POISON | M_NO_SKELETON, - 1500, 10, MONS_ELEPHANT_SLUG, MH_NATURAL, -3, + M_NO_SKELETON, + MR_VUL_POISON, + 1500, 10, MONS_GIANT_SLUG, MONS_ELEPHANT_SLUG, MH_NATURAL, -3, { 40, 0, 0, 0 }, { 20, 5, 3, 0 }, 2, 1, 4, 10, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_SILENT, I_INSECT, @@ -1716,7 +1865,8 @@ { MONS_WAR_DOG, 'h', CYAN, "war dog", M_SEE_INVIS | M_WARM_BLOOD, - 350, 10, MONS_WAR_DOG, MH_NATURAL, -3, + MR_NO_FLAGS, + 350, 10, MONS_HOUND, MONS_WAR_DOG, MH_NATURAL, -3, { 12, 0, 0, 0 }, { 4, 3, 5, 0 }, 4, 15, 17, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_BARK, I_ANIMAL, @@ -1727,7 +1877,8 @@ { MONS_GREY_RAT, 'r', LIGHTGREY, "grey rat", M_WARM_BLOOD, - 250, 10, MONS_GREY_RAT, MH_NATURAL, -3, + MR_NO_FLAGS, + 250, 10, MONS_RAT, MONS_GREY_RAT, MH_NATURAL, -3, { 5, 0, 0, 0 }, { 1, 3, 6, 0 }, 2, 12, 12, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SILENT, I_ANIMAL, @@ -1738,7 +1889,8 @@ { MONS_GREEN_RAT, 'r', LIGHTGREEN, "green rat", M_WARM_BLOOD, - 250, 10, MONS_GREEN_RAT, MH_NATURAL, -3, + MR_NO_FLAGS, + 250, 10, MONS_RAT, MONS_GREEN_RAT, MH_NATURAL, -3, { 10, 0, 0, 0 }, { 2, 3, 5, 0 }, 5, 11, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_ANIMAL, @@ -1749,7 +1901,8 @@ { MONS_ORANGE_RAT, 'r', LIGHTRED, "orange rat", M_WARM_BLOOD, - 250, 10, MONS_ORANGE_RAT, MH_NATURAL, -3, + MR_NO_FLAGS, + 250, 10, MONS_RAT, MONS_ORANGE_RAT, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 3, 3, 5, 0 }, 7, 10, 12, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_ROAR, I_ANIMAL, @@ -1759,8 +1912,9 @@ { MONS_BLACK_SNAKE, 'S', DARKGREY, "black snake", - M_RES_POISON | M_COLD_BLOOD, - 500, 12, MONS_BLACK_SNAKE, MH_NATURAL, -3, + M_COLD_BLOOD, + MR_RES_POISON, + 500, 12, MONS_SNAKE, MONS_BLACK_SNAKE, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 7, 3, 5, 0 }, 4, 15, 18, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_HISS, I_REPTILE, @@ -1771,7 +1925,8 @@ { MONS_SHEEP, 'Y', LIGHTGREY, "sheep", M_WARM_BLOOD, - 1200, 10, MONS_SHEEP, MH_NATURAL, -3, + MR_NO_FLAGS, + 1200, 10, MONS_SHEEP, MONS_SHEEP, MH_NATURAL, -3, { 13, 0, 0, 0 }, { 3, 3, 5, 0 }, 2, 7, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_BELLOW, I_ANIMAL, @@ -1781,8 +1936,9 @@ { MONS_GHOUL, 'n', RED, "ghoul", - M_RES_POISON | M_RES_COLD, - 500, 12, MONS_GHOUL, MH_UNDEAD, -5, + M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 500, 12, MONS_GHOUL, MONS_GHOUL, MH_UNDEAD, -5, { 9, 0, 0, 0 }, { 4, 3, 5, 0 }, 4, 10, 10, 7, MST_NO_SPELLS, CE_HCL, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -1793,7 +1949,8 @@ { MONS_HOG, 'h', LIGHTRED, "hog", M_WARM_BLOOD, - 700, 10, MONS_HOG, MH_NATURAL, -3, + MR_NO_FLAGS, + 700, 10, MONS_HOG, MONS_HOG, MH_NATURAL, -3, { 14, 0, 0, 0 }, { 6, 3, 5, 0 }, 2, 9, 13, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_ANIMAL, @@ -1803,8 +1960,9 @@ { MONS_GIANT_MOSQUITO, 'y', DARKGREY, "giant mosquito", - M_ED_POISON | M_FLIES, - 100, 10, MONS_GIANT_MOSQUITO, MH_NATURAL, -3, + M_FLIES, + MR_VUL_POISON, + 100, 10, MONS_GIANT_MOSQUITO, MONS_GIANT_MOSQUITO, MH_NATURAL, -3, { 10, 0, 0, 0 }, { 1, 3, 5, 0 }, 0, 13, 12, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_WHINE, I_PLANT, @@ -1814,8 +1972,9 @@ { MONS_GIANT_CENTIPEDE, 's', GREEN, "giant centipede", - M_ED_POISON, - 350, 10, MONS_GIANT_CENTIPEDE, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 350, 10, MONS_GIANT_CENTIPEDE, MONS_GIANT_CENTIPEDE, MH_NATURAL, -3, { 2, 0, 0, 0 }, { 2, 3, 3, 0 }, 2, 14, 13, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_INSECT, @@ -1827,8 +1986,9 @@ { MONS_IRON_TROLL, 'T', CYAN, "iron troll", - M_RES_FIRE | M_RES_COLD | M_WARM_BLOOD, - 2400, 10, MONS_IRON_TROLL, MH_NATURAL, -5, + M_WARM_BLOOD | M_EVIL, + MR_RES_FIRE | MR_RES_COLD, + 2400, 10, MONS_TROLL, MONS_IRON_TROLL, MH_NATURAL, -5, { 35, 25, 25, 0 }, { 16, 3, 5, 0 }, 20, 4, 7, 7, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_ROAR, I_NORMAL, @@ -1838,9 +1998,10 @@ { MONS_NAGA, 'N', GREEN, "naga", - M_RES_POISON | M_SPELLCASTER | M_SEE_INVIS | M_WARM_BLOOD, - 750, 10, MONS_NAGA, MH_NATURAL, -6, - { 6, 0, 0, 0 }, + M_SPELLCASTER | M_SEE_INVIS | M_WARM_BLOOD, + MR_RES_POISON, + 750, 10, MONS_NAGA, MONS_NAGA, MH_NATURAL, -6, + { 13, 0, 0, 0 }, { 5, 3, 5, 0 }, 6, 10, 8, 7, MST_NAGA, CE_POISONOUS, Z_SMALL, S_SHOUT, I_NORMAL, MONUSE_WEAPONS_ARMOUR @@ -1849,8 +2010,9 @@ { MONS_FIRE_GIANT, 'C', RED, "fire giant", - M_RES_FIRE | M_SPELLCASTER | M_WARM_BLOOD | M_SEE_INVIS, - 2400, 11, MONS_FIRE_GIANT, MH_NATURAL, -4, + M_SPELLCASTER | M_WARM_BLOOD | M_SEE_INVIS | M_EVIL, + MR_RES_FIRE, + 2400, 11, MONS_HILL_GIANT, MONS_FIRE_GIANT, MH_NATURAL, -4, { 30, 0, 0, 0 }, { 16, 3, 6, 0 }, 8, 4, 10, 7, MST_EFREET, CE_CONTAMINATED, Z_BIG, S_SHOUT, I_NORMAL, @@ -1860,8 +2022,9 @@ { MONS_FROST_GIANT, 'C', LIGHTBLUE, "frost giant", - M_RES_COLD | M_SPELLCASTER | M_WARM_BLOOD | M_SEE_INVIS, - 2600, 11, MONS_FROST_GIANT, MH_NATURAL, -4, + M_SPELLCASTER | M_WARM_BLOOD | M_SEE_INVIS | M_EVIL, + MR_RES_COLD, + 2600, 11, MONS_HILL_GIANT, MONS_FROST_GIANT, MH_NATURAL, -4, { 35, 0, 0, 0 }, { 16, 4, 5, 0 }, 9, 3, 10, 7, MST_FROST_GIANT, CE_CONTAMINATED, Z_BIG, S_SHOUT, I_NORMAL, @@ -1870,9 +2033,10 @@ , { - MONS_FIREDRAKE, 'd', RED, "firedrake", - M_RES_FIRE | M_FLIES, - 900, 10, MONS_FIREDRAKE, MH_NATURAL, -3, + MONS_FIREDRAKE, 'l', RED, "firedrake", + M_FLIES | M_SPECIAL_ABILITY, + MR_RES_FIRE, + 900, 10, MONS_FIREDRAKE, MONS_FIREDRAKE, MH_NATURAL, -3, { 8, 0, 0, 0 }, { 6, 3, 5, 0 }, 3, 12, 12, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SILENT, I_ANIMAL, @@ -1882,8 +2046,9 @@ { MONS_SHADOW_DRAGON, 'D', DARKGREY, "shadow dragon", - M_RES_POISON | M_RES_COLD | M_SPELLCASTER | M_FLIES | M_SEE_INVIS, - 2000, 12, MONS_SHADOW_DRAGON, MH_NATURAL, -5, + M_SPELLCASTER | M_FLIES | M_SEE_INVIS, + MR_RES_POISON | MR_RES_COLD, + 2000, 12, MONS_DRAGON, MONS_SHADOW_DRAGON, MH_NATURAL, -5, { 20, 15, 15, 0 }, { 17, 5, 5, 0 }, 15, 10, 10, 7, MST_SHADOW_DRAGON, CE_CLEAN, Z_BIG, S_ROAR, I_HIGH, @@ -1892,8 +2057,9 @@ , { MONS_YELLOW_SNAKE, 'S', YELLOW, "yellow snake", - M_RES_POISON | M_COLD_BLOOD, - 400, 10, MONS_YELLOW_SNAKE, MH_NATURAL, -3, + M_COLD_BLOOD, + MR_RES_POISON, + 400, 10, MONS_SNAKE, MONS_YELLOW_SNAKE, MH_NATURAL, -3, { 15, 0, 0, 0 }, { 6, 3, 5, 0 }, 4, 14, 13, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_HISS, I_REPTILE, @@ -1904,7 +2070,8 @@ { MONS_GREY_SNAKE, 'S', LIGHTGREY, "grey snake", M_COLD_BLOOD, - 600, 10, MONS_GREY_SNAKE, MH_NATURAL, -3, + MR_NO_FLAGS, + 600, 10, MONS_SNAKE, MONS_GREY_SNAKE, MH_NATURAL, -3, { 30, 0, 0, 0 }, { 11, 3, 5, 0 }, 4, 16, 18, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_HISS, I_REPTILE, @@ -1914,8 +2081,9 @@ { MONS_DEEP_TROLL, 'T', DARKGREY, "deep troll", - M_WARM_BLOOD | M_SEE_INVIS, - 1500, 12, MONS_DEEP_TROLL, MH_NATURAL, -3, + M_WARM_BLOOD | M_SEE_INVIS | M_EVIL, + MR_NO_FLAGS, + 1500, 12, MONS_TROLL, MONS_DEEP_TROLL, MH_NATURAL, -3, { 27, 20, 20, 0 }, { 10, 3, 5, 0 }, 6, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SHOUT, I_NORMAL, @@ -1925,8 +2093,9 @@ { MONS_GIANT_BLOWFLY, 'y', LIGHTGREY, "giant blowfly", - M_ED_POISON | M_FLIES, - 200, 10, MONS_GIANT_BLOWFLY, MH_NATURAL, -3, + M_FLIES, + MR_VUL_POISON, + 200, 10, MONS_GIANT_BLOWFLY, MONS_GIANT_BLOWFLY, MH_NATURAL, -3, { 13, 0, 0, 0 }, { 5, 3, 5, 0 }, 2, 15, 19, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_BUZZ, I_PLANT, @@ -1936,8 +2105,9 @@ { MONS_RED_WASP, 'y', RED, "red wasp", - M_ED_POISON | M_FLIES, - 400, 14, MONS_RED_WASP, MH_NATURAL, -3, + M_FLIES, + MR_VUL_POISON, + 400, 14, MONS_YELLOW_WASP, MONS_RED_WASP, MH_NATURAL, -3, { 23, 0, 0, 0 }, { 8, 3, 5, 0 }, 7, 14, 15, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_BUZZ, I_PLANT, @@ -1947,9 +2117,10 @@ { MONS_SWAMP_DRAGON, 'D', BROWN, "swamp dragon", - M_SPELLCASTER | M_FLIES | M_RES_POISON, - 1900, 11, MONS_SWAMP_DRAGON, MH_NATURAL, -3, - { 13, 9, 9, 0 }, + M_SPELLCASTER | M_FLIES, + MR_RES_POISON, + 1900, 11, MONS_DRAGON, MONS_SWAMP_DRAGON, MH_NATURAL, -3, + { 18, 9, 9, 0 }, { 9, 5, 5, 0 }, 7, 7, 10, 7, MST_SWAMP_DRAGON, CE_CONTAMINATED, Z_BIG, S_ROAR, I_ANIMAL_LIKE, MONUSE_OPEN_DOORS @@ -1957,20 +2128,34 @@ , { - MONS_SWAMP_DRAKE, 'd', BROWN, "swamp drake", - M_SPELLCASTER | M_FLIES | M_RES_POISON, - 900, 11, MONS_SWAMP_DRAKE, MH_NATURAL, -3, - { 11, 0, 0, 0 }, + MONS_SWAMP_DRAKE, 'l', BROWN, "swamp drake", + M_SPELLCASTER | M_FLIES | M_EVIL, + MR_RES_POISON, + 900, 11, MONS_DRAGON, MONS_SWAMP_DRAKE, MH_NATURAL, -3, + { 14, 0, 0, 0 }, { 4, 5, 5, 0 }, 3, 11, 11, 7, MST_SWAMP_DRAKE, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_ANIMAL_LIKE, MONUSE_OPEN_DOORS } , +{ + MONS_DEATH_DRAKE, 'l', DARKGREY, "death drake", + M_SPELLCASTER | M_FLIES | M_EVIL, + MR_RES_POISON, + 900, 11, MONS_DRAGON, MONS_DEATH_DRAKE, MH_NATURAL, -3, + { 12, 0, 0, 0 }, + { 9, 5, 7, 0 }, + 6, 14, 13, 10, MST_DEATH_DRAKE, CE_HCL, Z_BIG, S_ROAR, I_ANIMAL_LIKE, + MONUSE_OPEN_DOORS +} +, + { MONS_SOLDIER_ANT, 'a', LIGHTGREY, "soldier ant", - M_ED_POISON, - 900, 10, MONS_SOLDIER_ANT, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 900, 10, MONS_GIANT_ANT, MONS_SOLDIER_ANT, MH_NATURAL, -3, { 14, 0, 0, 0 }, { 6, 3, 5, 0 }, 8, 10, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_INSECT, @@ -1980,8 +2165,9 @@ { MONS_HILL_GIANT, 'C', LIGHTRED, "hill giant", - M_WARM_BLOOD, - 1600, 10, MONS_HILL_GIANT, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 1600, 10, MONS_HILL_GIANT, MONS_HILL_GIANT, MH_NATURAL, -3, { 30, 0, 0, 0 }, { 11, 3, 5, 0 }, 3, 4, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SHOUT, I_NORMAL, @@ -1991,8 +2177,9 @@ { MONS_QUEEN_ANT, 'Q', DARKGREY, "queen ant", - M_ED_POISON, - 1200, 10, MONS_QUEEN_ANT, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 1200, 10, MONS_GIANT_ANT, MONS_QUEEN_ANT, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 13, 3, 5, 0 }, 14, 3, 7, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_INSECT, @@ -2002,8 +2189,9 @@ { MONS_ANT_LARVA, 'w', LIGHTGREY, "ant larva", - M_ED_POISON | M_NO_SKELETON, - 350, 5, MONS_ANT_LARVA, MH_NATURAL, -3, + M_NO_SKELETON, + MR_VUL_POISON, + 350, 5, MONS_GIANT_ANT, MONS_ANT_LARVA, MH_NATURAL, -3, { 5, 0, 0, 0 }, { 2, 3, 5, 0 }, 2, 6, 6, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_INSECT, @@ -2015,7 +2203,8 @@ { MONS_GIANT_FROG, 'F', GREEN, "giant frog", M_COLD_BLOOD | M_AMPHIBIOUS, - 500, 10, MONS_GIANT_FROG, MH_NATURAL, -3, + MR_NO_FLAGS, + 500, 10, MONS_GIANT_FROG, MONS_GIANT_FROG, MH_NATURAL, -3, { 9, 0, 0, 0 }, { 4, 3, 5, 0 }, 0, 12, 15, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_CROAK, I_ANIMAL, @@ -2026,7 +2215,8 @@ { MONS_GIANT_BROWN_FROG, 'F', BROWN, "giant brown frog", M_COLD_BLOOD | M_AMPHIBIOUS, - 890, 10, MONS_GIANT_BROWN_FROG, MH_NATURAL, -3, + MR_NO_FLAGS, + 890, 10, MONS_GIANT_FROG, MONS_GIANT_BROWN_FROG, MH_NATURAL, -3, { 14, 0, 0, 0 }, { 8, 3, 5, 0 }, 2, 11, 13, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_CROAK, I_ANIMAL, @@ -2036,8 +2226,9 @@ { MONS_SPINY_FROG, 'F', YELLOW, "spiny frog", - M_COLD_BLOOD | M_RES_POISON | M_AMPHIBIOUS, - 1000, 10, MONS_SPINY_FROG, MH_NATURAL, -3, + M_COLD_BLOOD | M_AMPHIBIOUS, + MR_RES_POISON, + 1000, 10, MONS_GIANT_FROG, MONS_SPINY_FROG, MH_NATURAL, -3, { 26, 0, 0, 0 }, { 7, 3, 5, 0 }, 6, 9, 12, 7, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_CROAK, I_ANIMAL, @@ -2047,8 +2238,9 @@ { MONS_BLINK_FROG, 'F', LIGHTGREEN, "blink frog", - M_COLD_BLOOD | M_AMPHIBIOUS, - 800, 12, MONS_BLINK_FROG, MH_NATURAL, -5, + M_COLD_BLOOD | M_AMPHIBIOUS | M_SPECIAL_ABILITY, + MR_NO_FLAGS, + 800, 12, MONS_GIANT_FROG, MONS_BLINK_FROG, MH_NATURAL, -5, { 20, 0, 0, 0 }, { 6, 3, 5, 0 }, 3, 12, 14, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_CROAK, I_ANIMAL, @@ -2058,7 +2250,8 @@ { MONS_GIANT_COCKROACH, 'a', BROWN, "giant cockroach", M_NO_FLAGS, - 250, 10, MONS_GIANT_COCKROACH, MH_NATURAL, -1, + MR_NO_FLAGS, + 250, 10, MONS_GIANT_COCKROACH, MONS_GIANT_COCKROACH, MH_NATURAL, -1, { 2, 0, 0, 0 }, { 1, 3, 4, 0 }, 3, 10, 12, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SILENT, I_INSECT, @@ -2068,7 +2261,8 @@ { MONS_SMALL_SNAKE, 'S', LIGHTGREEN, "small snake", M_COLD_BLOOD, - 100, 13, MONS_SMALL_SNAKE, MH_NATURAL, -1, + MR_NO_FLAGS, + 100, 13, MONS_SNAKE, MONS_SMALL_SNAKE, MH_NATURAL, -1, { 2, 0, 0, 0 }, { 1, 2, 3, 0 }, 0, 11, 12, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_REPTILE, @@ -2078,8 +2272,9 @@ { MONS_WHITE_IMP, '5', WHITE, "white imp", - M_RES_COLD | M_SPELLCASTER | M_FLIES | M_SPEAKS, - 0, 10, MONS_WHITE_IMP, MH_DEMONIC, -3, + M_SPELLCASTER | M_FLIES | M_SPEAKS | M_EVIL, + MR_RES_COLD, + 0, 10, MONS_IMP, MONS_WHITE_IMP, MH_DEMONIC, -3, { 4, 0, 0, 0 }, { 2, 3, 5, 0 }, 4, 10, 10, 7, MST_WHITE_IMP, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -2089,8 +2284,9 @@ { MONS_LEMURE, '5', YELLOW, "lemure", - M_RES_POISON, - 0, 10, MONS_LEMURE, MH_DEMONIC, -3, + M_EVIL, + MR_RES_POISON, + 0, 10, MONS_LEMURE, MONS_LEMURE, MH_DEMONIC, -3, { 12, 0, 0, 0 }, { 2, 3, 5, 0 }, 1, 12, 12, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_MOAN, I_NORMAL, @@ -2100,8 +2296,9 @@ { MONS_UFETUBUS, '5', LIGHTCYAN, "ufetubus", - M_ED_FIRE | M_RES_COLD, - 0, 10, MONS_UFETUBUS, MH_DEMONIC, -3, + M_EVIL, + MR_VUL_FIRE | MR_RES_COLD, + 0, 10, MONS_UFETUBUS, MONS_UFETUBUS, MH_DEMONIC, -3, { 5, 5, 0, 0 }, { 1, 4, 6, 0 }, 2, 15, 15, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -2111,8 +2308,9 @@ { MONS_MANES, '5', LIGHTRED, "manes", - M_RES_ELEC | M_RES_FIRE | M_RES_COLD | M_RES_POISON, - 0, 10, MONS_MANES, MH_DEMONIC, -3, + M_EVIL, + MR_RES_ELEC | MR_RES_FIRE | MR_RES_COLD | MR_RES_POISON, + 0, 10, MONS_MANES, MONS_MANES, MH_DEMONIC, -3, { 5, 3, 3, 0 }, { 3, 3, 5, 0 }, 2, 8, 8, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -2122,8 +2320,9 @@ { MONS_MIDGE, '5', LIGHTGREEN, "midge", - M_RES_POISON | M_FLIES, - 0, 10, MONS_MIDGE, MH_DEMONIC, -3, + M_FLIES | M_EVIL, + MR_RES_POISON, + 0, 10, MONS_MIDGE, MONS_MIDGE, MH_DEMONIC, -3, { 8, 0, 0, 0 }, { 2, 3, 5, 0 }, 4, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -2133,8 +2332,9 @@ { MONS_NEQOXEC, '3', MAGENTA, "neqoxec", - M_RES_POISON | M_SPELLCASTER | M_LEVITATE, - 0, 12, MONS_NEQOXEC, MH_DEMONIC, -6, + M_SPELLCASTER | M_LEVITATE | M_EVIL, + MR_RES_POISON, + 0, 12, MONS_NEQOXEC, MONS_NEQOXEC, MH_DEMONIC, -6, { 15, 0, 0, 0 }, { 6, 3, 5, 0 }, 4, 12, 10, 7, MST_NEQOXEC, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -2144,8 +2344,9 @@ { MONS_ORANGE_DEMON, '3', LIGHTRED, "orange demon", - M_NO_FLAGS, - 0, 12, MONS_ORANGE_DEMON, MH_DEMONIC, -6, + M_EVIL, + MR_NO_FLAGS, + 0, 12, MONS_ORANGE_DEMON, MONS_ORANGE_DEMON, MH_DEMONIC, -6, { 10, 5, 0, 0 }, { 8, 4, 5, 0 }, 3, 7, 7, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SCREECH, I_NORMAL, @@ -2155,8 +2356,9 @@ { MONS_HELLWING, '3', LIGHTGREY, "hellwing", - M_RES_POISON | M_SPELLCASTER | M_FLIES, - 0, 12, MONS_HELLWING, MH_DEMONIC, -6, + M_SPELLCASTER | M_FLIES | M_EVIL, + MR_RES_POISON, + 0, 12, MONS_HELLWING, MONS_HELLWING, MH_DEMONIC, -6, { 17, 10, 0, 0 }, { 7, 4, 5, 0 }, 8, 10, 10, 7, MST_HELLWING, CE_CONTAMINATED, Z_NOZOMBIE, S_MOAN, I_NORMAL, @@ -2166,8 +2368,9 @@ { MONS_SMOKE_DEMON, '4', LIGHTGREY, "smoke demon", - M_RES_POISON | M_RES_FIRE | M_SPELLCASTER | M_FLIES, - 0, 12, MONS_SMOKE_DEMON, MH_DEMONIC, -6, + M_SPELLCASTER | M_FLIES | M_EVIL, + MR_RES_POISON | MR_RES_FIRE, + 0, 12, MONS_SMOKE_DEMON, MONS_SMOKE_DEMON, MH_DEMONIC, -6, { 8, 5, 5, 0 }, { 7, 3, 5, 0 }, 5, 9, 9, 7, MST_SMOKE_DEMON, CE_CONTAMINATED, Z_NOZOMBIE, S_ROAR, I_NORMAL, @@ -2177,8 +2380,9 @@ { MONS_YNOXINUL, '3', CYAN, "ynoxinul", - M_RES_ELEC | M_RES_POISON | M_RES_COLD | M_SPELLCASTER | M_FLIES | M_SEE_INVIS, - 0, 12, MONS_YNOXINUL, MH_DEMONIC, -6, + M_SPELLCASTER | M_FLIES | M_SEE_INVIS | M_EVIL, + MR_RES_ELEC | MR_RES_POISON | MR_RES_COLD, + 0, 12, MONS_YNOXINUL, MONS_YNOXINUL, MH_DEMONIC, -6, { 12, 0, 0, 0 }, { 6, 3, 5, 0 }, 3, 10, 10, 7, MST_YNOXINUL, CE_CONTAMINATED, Z_NOZOMBIE, S_BELLOW, I_NORMAL, @@ -2188,8 +2392,9 @@ { MONS_EXECUTIONER, '1', LIGHTGREY, "Executioner", - M_SPELLCASTER | M_RES_ELEC | M_RES_FIRE | M_RES_COLD | M_RES_POISON | M_SEE_INVIS, - 0, 14, MONS_EXECUTIONER, MH_DEMONIC, -9, + M_SPELLCASTER | M_SEE_INVIS | M_EVIL, + MR_RES_ELEC | MR_RES_FIRE | MR_RES_COLD | MR_RES_POISON, + 0, 14, MONS_EXECUTIONER, MONS_EXECUTIONER, MH_DEMONIC, -9, { 30, 10, 10, 0 }, { 12, 3, 5, 0 }, 10, 15, 20, 7, MST_HELL_KNIGHT_I, CE_CONTAMINATED, Z_NOZOMBIE, S_SCREAM, I_HIGH, @@ -2199,8 +2404,9 @@ { MONS_GREEN_DEATH, '1', GREEN, "Green Death", - M_RES_POISON | M_SPELLCASTER | M_SEE_INVIS, - 0, 14, MONS_GREEN_DEATH, MH_DEMONIC, -9, + M_SPELLCASTER | M_SEE_INVIS | M_EVIL, + MR_RES_POISON, + 0, 14, MONS_GREEN_DEATH, MONS_GREEN_DEATH, MH_DEMONIC, -9, { 32, 0, 0, 0 }, { 13, 3, 5, 0 }, 5, 7, 12, 7, MST_GREEN_DEATH, CE_POISONOUS, Z_NOZOMBIE, S_ROAR, I_HIGH, @@ -2210,8 +2416,9 @@ { MONS_BLUE_DEATH, '1', BLUE, "Blue Death", - M_RES_POISON | M_SPELLCASTER | M_ED_FIRE | M_RES_COLD | M_RES_ELEC | M_FLIES | M_SEE_INVIS, - 0, 14, MONS_BLUE_DEATH, MH_DEMONIC, -9, + M_SPELLCASTER | M_FLIES | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 14, MONS_BLUE_DEATH, MONS_BLUE_DEATH, MH_DEMONIC, -9, { 20, 20, 0, 0 }, { 12, 3, 5, 0 }, 10, 10, 12, 7, MST_BLUE_DEATH, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -2221,8 +2428,9 @@ { MONS_BALRUG, '1', RED, "Balrug", - M_RES_POISON | M_RES_HELLFIRE | M_ED_COLD | M_SPELLCASTER | M_FLIES | M_SEE_INVIS, - 0, 14, MONS_BALRUG, MH_DEMONIC, -9, + M_SPELLCASTER | M_FLIES | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_HELLFIRE | MR_VUL_COLD, + 0, 14, MONS_BALRUG, MONS_BALRUG, MH_DEMONIC, -9, { 25, 0, 0, 0 }, { 14, 3, 5, 0 }, 5, 12, 12, 7, MST_BALRUG, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -2232,8 +2440,9 @@ { MONS_CACODEMON, '1', YELLOW, "Cacodemon", - M_RES_POISON | M_RES_ELEC | M_SPELLCASTER | M_LEVITATE | M_SEE_INVIS, - 0, 14, MONS_CACODEMON, MH_DEMONIC, -9, + M_SPELLCASTER | M_LEVITATE | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_ELEC, + 0, 14, MONS_CACODEMON, MONS_CACODEMON, MH_DEMONIC, -9, { 22, 0, 0, 0 }, { 13, 3, 5, 0 }, 11, 10, 10, 7, MST_CACODEMON, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -2244,8 +2453,9 @@ { MONS_DEMONIC_CRAWLER, '3', DARKGREY, "demonic crawler", - M_RES_ELEC | M_RES_POISON | M_RES_COLD | M_RES_FIRE | M_SEE_INVIS, - 0, 12, MONS_DEMONIC_CRAWLER, MH_DEMONIC, -6, + M_SEE_INVIS | M_EVIL, + MR_RES_ELEC | MR_RES_POISON | MR_RES_COLD | MR_RES_FIRE, + 0, 12, MONS_DEMONIC_CRAWLER, MONS_DEMONIC_CRAWLER, MH_DEMONIC, -6, { 13, 13, 13, 13 }, { 9, 3, 5, 0 }, 10, 6, 9, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SCREAM, I_NORMAL, @@ -2255,8 +2465,9 @@ { MONS_SUN_DEMON, '2', YELLOW, "sun demon", - M_RES_ELEC | M_RES_POISON | M_ED_COLD | M_RES_HELLFIRE | M_SEE_INVIS | M_LEVITATE, - 0, 14, MONS_SUN_DEMON, MH_DEMONIC, -6, + M_SEE_INVIS | M_LEVITATE | M_EVIL, + MR_RES_ELEC | MR_RES_POISON | MR_VUL_COLD | MR_RES_HELLFIRE, + 0, 14, MONS_SUN_DEMON, MONS_SUN_DEMON, MH_DEMONIC, -6, { 30, 0, 0, 0 }, { 10, 3, 5, 0 }, 10, 12, 12, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -2266,8 +2477,9 @@ { MONS_SHADOW_IMP, '5', DARKGREY, "shadow imp", - M_RES_COLD | M_SPELLCASTER | M_FLIES | M_RES_POISON | M_SPEAKS, - 0, 11, MONS_SHADOW_IMP, MH_DEMONIC, -3, + M_SPELLCASTER | M_FLIES | M_SPEAKS | M_EVIL, + MR_RES_COLD | MR_RES_POISON, + 0, 11, MONS_IMP, MONS_SHADOW_IMP, MH_DEMONIC, -3, { 6, 0, 0, 0 }, { 2, 3, 5, 0 }, 3, 11, 10, 7, MST_SHADOW_IMP, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -2277,8 +2489,9 @@ { MONS_SHADOW_DEMON, '3', DARKGREY, "shadow demon", - M_RES_POISON | M_RES_COLD | M_SEE_INVIS | M_INVIS, - 0, 12, MONS_SHADOW_DEMON, MH_DEMONIC, -7, + M_SEE_INVIS | M_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 12, MONS_SHADOW_DEMON, MONS_SHADOW_DEMON, MH_DEMONIC, -7, { 21, 0, 0, 0 }, { 6, 3, 5, 0 }, 7, 12, 11, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_CROAK, I_HIGH, @@ -2288,8 +2501,9 @@ { MONS_LOROCYPROCA, '2', BLUE, "Lorocyproca", - M_RES_POISON | M_RES_COLD | M_RES_FIRE | M_RES_ELEC | M_SEE_INVIS | M_INVIS, - 0, 12, MONS_LOROCYPROCA, MH_DEMONIC, -7, + M_SEE_INVIS | M_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD | MR_RES_FIRE | MR_RES_ELEC, + 0, 12, MONS_LOROCYPROCA, MONS_LOROCYPROCA, MH_DEMONIC, -7, { 25, 25, 0, 0 }, { 12, 3, 5, 0 }, 10, 12, 9, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_MOAN, I_HIGH, @@ -2299,8 +2513,9 @@ { MONS_SHADOW_WRAITH, 'W', BLUE, "shadow wraith", - M_RES_POISON | M_LEVITATE | M_SEE_INVIS | M_INVIS, - 0, 15, MONS_SHADOW_WRAITH, MH_UNDEAD, -8, + M_LEVITATE | M_SEE_INVIS | M_INVIS | M_EVIL, + MR_RES_POISON, + 0, 15, MONS_WRAITH, MONS_SHADOW_WRAITH, MH_UNDEAD, -8, { 20, 0, 0, 0 }, { 10, 3, 5, 0 }, 7, 7, 10, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_MOAN, I_HIGH, @@ -2310,8 +2525,9 @@ { MONS_GIANT_AMOEBA, 'J', BLUE, "giant amoeba", - M_RES_POISON | M_NO_SKELETON | M_SEE_INVIS | M_AMPHIBIOUS, - 1000, 10, MONS_GIANT_AMOEBA, MH_NATURAL, -3, + M_NO_SKELETON | M_SEE_INVIS | M_AMPHIBIOUS, + MR_RES_POISON, + 1000, 10, MONS_GIANT_AMOEBA, MONS_GIANT_AMOEBA, MH_NATURAL, -3, { 25, 0, 0, 0 }, { 12, 3, 5, 0 }, 0, 4, 10, 10, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -2322,7 +2538,8 @@ { MONS_GIANT_SLUG, 'm', GREEN, "giant slug", M_NO_SKELETON | M_AMPHIBIOUS, - 700, 10, MONS_GIANT_SLUG, MH_NATURAL, -3, + MR_NO_FLAGS, + 700, 10, MONS_GIANT_SLUG, MONS_GIANT_SLUG, MH_NATURAL, -3, { 23, 0, 0, 0 }, { 10, 5, 3, 0 }, 0, 2, 6, 10, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_SILENT, I_INSECT, @@ -2333,7 +2550,8 @@ { MONS_GIANT_SNAIL, 'm', LIGHTGREEN, "giant snail", M_NO_SKELETON | M_AMPHIBIOUS, - 900, 10, MONS_GIANT_SNAIL, MH_NATURAL, -3, + MR_NO_FLAGS, + 900, 10, MONS_GIANT_SLUG, MONS_GIANT_SNAIL, MH_NATURAL, -3, { 18, 0, 0, 0 }, { 14, 5, 3, 0 }, 7, 2, 4, 10, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_SILENT, I_INSECT, @@ -2343,8 +2561,9 @@ { MONS_SPATIAL_VORTEX, 'v', BLACK, "spatial vortex", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC | M_LEVITATE | M_CONFUSED, - 0, 5, MONS_SPATIAL_VORTEX, MH_NONLIVING, 5000, + M_LEVITATE | M_CONFUSED, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 5, MONS_FIRE_VORTEX, MONS_SPATIAL_VORTEX, MH_NONLIVING, MAG_IMMUNE, { 50, 0, 0, 0 }, { 6, 6, 6, 0 }, 0, 5, 15, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -2354,8 +2573,9 @@ { MONS_PIT_FIEND, '1', BROWN, "Pit Fiend", - M_RES_POISON | M_RES_HELLFIRE | M_RES_COLD | M_FLIES | M_SEE_INVIS | M_RES_ELEC, - 0, 18, MONS_PIT_FIEND, MH_DEMONIC, -12, + M_FLIES | M_SEE_INVIS | M_EVIL | M_SPECIAL_ABILITY, + MR_RES_POISON | MR_RES_HELLFIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 18, MONS_FIEND, MONS_PIT_FIEND, MH_DEMONIC, -12, { 28, 21, 21, 0 }, { 19, 4, 5, 0 }, 17, 5, 8, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_ROAR, I_HIGH, @@ -2365,8 +2585,9 @@ { MONS_BORING_BEETLE, 'B', BROWN, "boring beetle", - M_ED_POISON, - 1300, 10, MONS_BORING_BEETLE, MH_NATURAL, -3, + M_NO_FLAGS, + MR_VUL_POISON, + 1300, 10, MONS_GIANT_BEETLE, MONS_BORING_BEETLE, MH_NATURAL, -3, { 26, 0, 0, 0 }, { 8, 3, 5, 0 }, 13, 4, 6, 7, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_SILENT, I_INSECT, @@ -2376,8 +2597,9 @@ { MONS_GARGOYLE, 'g', DARKGREY, "gargoyle", - M_RES_POISON | M_RES_ELEC | M_FLIES, - 0, 12, MONS_GARGOYLE, MH_NONLIVING, -6, + M_FLIES, + MR_RES_POISON | MR_RES_ELEC, + 0, 12, MONS_GARGOYLE, MONS_GARGOYLE, MH_NONLIVING, -6, { 10, 6, 6, 0 }, { 4, 3, 5, 0 }, 18, 6, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -2388,8 +2610,9 @@ // only appear in Dis castle { MONS_METAL_GARGOYLE, 'g', CYAN, "metal gargoyle", - M_RES_POISON | M_RES_ELEC | M_FLIES, - 0, 12, MONS_METAL_GARGOYLE, MH_NONLIVING, -6, + M_FLIES, + MR_RES_POISON | MR_RES_ELEC, + 0, 12, MONS_GARGOYLE, MONS_METAL_GARGOYLE, MH_NONLIVING, -6, { 19, 10, 10, 0 }, { 8, 3, 5, 0 }, 20, 4, 7, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -2400,8 +2623,9 @@ // only appear in Gehenna castle & one minivault { MONS_MOLTEN_GARGOYLE, 'g', RED, "molten gargoyle", - M_RES_POISON | M_RES_ELEC | M_FLIES | M_RES_FIRE, - 0, 12, MONS_MOLTEN_GARGOYLE, MH_NONLIVING, -6, + M_FLIES, + MR_RES_POISON | MR_RES_ELEC | MR_RES_FIRE, + 0, 12, MONS_GARGOYLE, MONS_MOLTEN_GARGOYLE, MH_NONLIVING, -6, { 12, 8, 8, 0 }, { 5, 3, 5, 0 }, 14, 7, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -2415,8 +2639,9 @@ { MONS_MNOLEG, '&', LIGHTGREEN, "Mnoleg", - M_RES_ELEC | M_RES_POISON | M_RES_FIRE | M_SEE_INVIS | M_SPELLCASTER | M_SPEAKS, - 0, 25, MONS_MNOLEG, MH_DEMONIC, 5000, + M_SEE_INVIS | M_SPELLCASTER | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_RES_ELEC | MR_RES_POISON | MR_RES_FIRE, + 0, 25, MONS_MNOLEG, MONS_MNOLEG, MH_DEMONIC, MAG_IMMUNE, { 23, 23, 0, 0 }, { 17, 0, 0, 199 }, 10, 13, 13, 7, MST_MNOLEG, CE_CONTAMINATED, Z_NOZOMBIE, S_BUZZ, I_HIGH, @@ -2426,8 +2651,9 @@ { MONS_LOM_LOBON, '&', LIGHTBLUE, "Lom Lobon", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC | M_LEVITATE | M_SEE_INVIS | M_SPELLCASTER | M_SPEAKS, - 0, 25, MONS_LOM_LOBON, MH_DEMONIC, 5000, + M_LEVITATE | M_SEE_INVIS | M_SPELLCASTER | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 25, MONS_LOM_LOBON, MONS_LOM_LOBON, MH_DEMONIC, MAG_IMMUNE, { 40, 0, 0, 0 }, { 19, 0, 0, 223 }, 10, 7, 8, 7, MST_LOM_LOBON, CE_CONTAMINATED, Z_NOZOMBIE, S_SCREAM, I_HIGH, @@ -2437,8 +2663,9 @@ { MONS_CEREBOV, '&', RED, "Cerebov", - M_RES_ELEC | M_RES_POISON | M_RES_HELLFIRE | M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS, - 0, 25, MONS_CEREBOV, MH_DEMONIC, -6, + M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_RES_ELEC | MR_RES_POISON | MR_RES_HELLFIRE, + 0, 25, MONS_CEREBOV, MONS_CEREBOV, MH_DEMONIC, -6, { 50, 0, 0, 0 }, { 21, 0, 0, 253 }, 15, 8, 10, 7, MST_CEREBOV, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -2448,8 +2675,9 @@ { MONS_GLOORX_VLOQ, '&', DARKGREY, "Gloorx Vloq", - M_RES_POISON | M_RES_COLD | M_RES_ELEC | M_LEVITATE | M_SEE_INVIS | M_SPELLCASTER | M_SPEAKS, - 0, 25, MONS_GLOORX_VLOQ, MH_DEMONIC, -14, + M_LEVITATE | M_SEE_INVIS | M_SPELLCASTER | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_RES_POISON | MR_RES_COLD | MR_RES_ELEC, + 0, 25, MONS_GLOORX_VLOQ, MONS_GLOORX_VLOQ, MH_DEMONIC, -14, { 20, 0, 0, 0 }, { 16, 0, 0, 234 }, 10, 10, 10, 7, MST_GLOORX_VLOQ, CE_CONTAMINATED, Z_NOZOMBIE, S_MOAN, I_HIGH, @@ -2458,16 +2686,18 @@ , /* ****************************************************************** -{MONS_MOLLUSC_LORD, 'U', GREEN, "The Mollusc Lord", M_RES_POISON, +{MONS_MOLLUSC_LORD, 'U', GREEN, "The Mollusc Lord", M_NO_FLAGS, + MR_RES_POISON, 0, 25, 255, MH_DEMONIC, -3, {30,0,0,0}, {16,0,0,100}, 10, 10, 10, 7, 93, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_HIGH, 1}, ****************************************************************** */ { MONS_NAGA_MAGE, 'N', LIGHTRED, "naga mage", - M_RES_POISON | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_WARM_BLOOD, - 750, 13, MONS_NAGA, MH_NATURAL, -6, - { 5, 0, 0, 0 }, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_WARM_BLOOD, + MR_RES_POISON, + 750, 13, MONS_NAGA, MONS_NAGA, MH_NATURAL, -6, + { 10, 0, 0, 0 }, { 7, 3, 5, 0 }, 6, 10, 8, 7, MST_NAGA_MAGE, CE_POISONOUS, Z_SMALL, S_SHOUT, I_NORMAL, MONUSE_WEAPONS_ARMOUR @@ -2476,9 +2706,10 @@ { MONS_NAGA_WARRIOR, 'N', BLUE, "naga warrior", - M_RES_POISON | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_WARM_BLOOD, - 750, 12, MONS_NAGA, MH_NATURAL, -6, - { 11, 0, 0, 0 }, + M_SPELLCASTER | M_SEE_INVIS | M_WARM_BLOOD, + MR_RES_POISON, + 750, 12, MONS_NAGA, MONS_NAGA, MH_NATURAL, -6, + { 20, 0, 0, 0 }, { 10, 5, 5, 0 }, 6, 10, 8, 7, MST_NAGA, CE_POISONOUS, Z_SMALL, S_SHOUT, I_NORMAL, MONUSE_WEAPONS_ARMOUR @@ -2487,8 +2718,9 @@ { MONS_ORC_WARLORD, 'o', RED, "orc warlord", - M_WARM_BLOOD, - 600, 15, MONS_ORC, MH_NATURAL, -3, + M_WARM_BLOOD | M_EVIL, + MR_NO_FLAGS, + 600, 15, MONS_ORC, MONS_ORC, MH_NATURAL, -3, { 32, 0, 0, 0 }, { 15, 4, 7, 0 }, 3, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2499,7 +2731,8 @@ { MONS_DEEP_ELF_SOLDIER, 'e', CYAN, "deep elf soldier", M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 450, 10, MONS_ELF, MH_NATURAL, -6, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 6, 0, 0, 0 }, { 3, 3, 3, 0 }, 0, 12, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2510,7 +2743,8 @@ { MONS_DEEP_ELF_FIGHTER, 'e', LIGHTBLUE, "deep elf fighter", M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 450, 10, MONS_ELF, MH_NATURAL, -6, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 9, 0, 0, 0 }, { 6, 3, 3, 0 }, 0, 13, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2521,7 +2755,8 @@ { MONS_DEEP_ELF_KNIGHT, 'e', BLUE, "deep elf knight", M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 450, 10, MONS_ELF, MH_NATURAL, -6, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 14, 0, 0, 0 }, { 11, 3, 3, 0 }, 0, 15, 11, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2531,8 +2766,9 @@ { MONS_DEEP_ELF_MAGE, 'e', LIGHTRED, "deep elf mage", - M_RES_ELEC | M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 450, 10, MONS_ELF, MH_NATURAL, -6, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, + MR_RES_ELEC, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 5, 0, 0, 0 }, { 4, 3, 3, 0 }, 0, 13, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2543,7 +2779,8 @@ { MONS_DEEP_ELF_SUMMONER, 'e', YELLOW, "deep elf summoner", M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 450, 10, MONS_ELF, MH_NATURAL, -6, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 5, 0, 0, 0 }, { 6, 3, 3, 0 }, 0, 13, 10, 7, MST_DEEP_ELF_SUMMONER, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2553,8 +2790,9 @@ { MONS_DEEP_ELF_CONJURER, 'e', LIGHTGREEN, "deep elf conjurer", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_RES_ELEC | M_WARM_BLOOD, - 450, 10, MONS_ELF, MH_NATURAL, -6, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, + MR_RES_ELEC, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 5, 0, 0, 0 }, { 6, 3, 3, 0 }, 0, 13, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2565,7 +2803,8 @@ { MONS_DEEP_ELF_PRIEST, 'e', LIGHTGREY, "deep elf priest", M_SPELLCASTER | M_PRIEST | M_WARM_BLOOD, - 450, 10, MONS_ELF, MH_NATURAL, -6, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 9, 0, 0, 0 }, { 5, 3, 3, 0 }, 0, 13, 10, 7, MST_DEEP_ELF_PRIEST, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2575,8 +2814,9 @@ { MONS_DEEP_ELF_HIGH_PRIEST, 'e', DARKGREY, "deep elf high priest", - M_SPELLCASTER | M_SPEAKS | M_PRIEST | M_WARM_BLOOD | M_SEE_INVIS, - 450, 10, MONS_ELF, MH_NATURAL, -6, + M_SPELLCASTER | M_SPEAKS | M_PRIEST | M_WARM_BLOOD | M_SEE_INVIS | M_EVIL, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 14, 0, 0, 0 }, { 11, 3, 3, 0 }, 3, 13, 10, 7, MST_DEEP_ELF_HIGH_PRIEST, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2586,8 +2826,9 @@ { MONS_DEEP_ELF_DEMONOLOGIST, 'e', MAGENTA, "deep elf demonologist", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_SEE_INVIS, - 450, 10, MONS_ELF, MH_NATURAL, -6, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_SEE_INVIS | M_EVIL, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 12, 0, 0, 0 }, { 12, 3, 3, 0 }, 0, 13, 10, 7, MST_DEEP_ELF_DEMONOLOGIST, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2597,8 +2838,9 @@ { MONS_DEEP_ELF_ANNIHILATOR, 'e', GREEN, "deep elf annihilator", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_RES_ELEC | M_SEE_INVIS, - 450, 10, MONS_ELF, MH_NATURAL, -6, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_SEE_INVIS, + MR_RES_ELEC, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 12, 0, 0, 0 }, { 15, 3, 3, 0 }, 0, 13, 10, 7, MST_DEEP_ELF_ANNIHILATOR, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2609,7 +2851,8 @@ { MONS_DEEP_ELF_SORCERER, 'e', RED, "deep elf sorcerer", M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_SEE_INVIS | M_SPEAKS, - 450, 10, MONS_ELF, MH_NATURAL, -6, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 12, 0, 0, 0 }, { 14, 3, 3, 0 }, 0, 13, 10, 7, MST_DEEP_ELF_SORCERER, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2619,8 +2862,9 @@ { MONS_DEEP_ELF_DEATH_MAGE, 'e', WHITE, "deep elf death mage", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_SEE_INVIS, - 450, 10, MONS_ELF, MH_NATURAL, -6, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD | M_SEE_INVIS | M_EVIL, + MR_NO_FLAGS, + 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6, { 12, 0, 0, 0 }, { 15, 3, 3, 0 }, 0, 13, 10, 7, MST_DEEP_ELF_DEATH_MAGE, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -2630,8 +2874,9 @@ { MONS_BROWN_OOZE, 'J', BROWN, "brown ooze", - M_RES_POISON | M_NO_SKELETON | M_SEE_INVIS, - 0, 11, MONS_BROWN_OOZE, MH_NATURAL, -7, + M_NO_SKELETON | M_SEE_INVIS, + MR_RES_POISON | MR_RES_ASPHYX, + 0, 11, MONS_JELLY, MONS_BROWN_OOZE, MH_NATURAL, -7, { 25, 0, 0, 0 }, { 7, 3, 5, 0 }, 10, 1, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -2641,8 +2886,9 @@ { MONS_AZURE_JELLY, 'J', LIGHTBLUE, "azure jelly", - M_RES_POISON | M_RES_COLD | M_ED_FIRE | M_RES_ELEC | M_NO_SKELETON | M_SEE_INVIS, - 0, 11, MONS_AZURE_JELLY, MH_NATURAL, -4, + M_NO_SKELETON | M_SEE_INVIS, + MR_RES_POISON | MR_RES_COLD | MR_VUL_FIRE | MR_RES_ELEC | MR_RES_ASPHYX, + 0, 11, MONS_JELLY, MONS_AZURE_JELLY, MH_NATURAL, -4, { 12, 12, 12, 12 }, { 15, 3, 5, 0 }, 5, 10, 12, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -2652,8 +2898,9 @@ { MONS_DEATH_OOZE, 'J', DARKGREY, "death ooze", - M_RES_POISON | M_RES_COLD | M_NO_SKELETON | M_SEE_INVIS, - 0, 13, MONS_DEATH_OOZE, MH_UNDEAD, -8, + M_NO_SKELETON | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD | MR_RES_ASPHYX, + 0, 13, MONS_JELLY, MONS_DEATH_OOZE, MH_UNDEAD, -8, { 32, 32, 0, 0 }, { 11, 3, 3, 0 }, 2, 4, 12, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -2663,8 +2910,9 @@ { MONS_ACID_BLOB, 'J', LIGHTGREEN, "acid blob", - M_RES_POISON | M_NO_SKELETON | M_SEE_INVIS, - 0, 12, MONS_ACID_BLOB, MH_NATURAL, -7, + M_NO_SKELETON | M_SEE_INVIS | M_SPECIAL_ABILITY, + MR_RES_POISON | MR_RES_ASPHYX, + 0, 12, MONS_JELLY, MONS_ACID_BLOB, MH_NATURAL, -7, { 42, 0, 0, 0 }, { 18, 3, 5, 0 }, 1, 3, 14, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -2674,8 +2922,9 @@ { MONS_ROYAL_JELLY, 'J', YELLOW, "royal jelly", - M_RES_POISON | M_NO_SKELETON | M_SEE_INVIS, - 0, 20, MONS_ROYAL_JELLY, MH_NATURAL, -7, + M_NO_SKELETON | M_SEE_INVIS, + MR_RES_POISON | MR_RES_ASPHYX, + 0, 20, MONS_JELLY, MONS_ROYAL_JELLY, MH_NATURAL, -7, { 50, 0, 0, 0 }, { 21, 0, 0, 111 }, 8, 4, 12, 7, MST_NO_SPELLS, CE_CLEAN, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -2685,8 +2934,9 @@ { MONS_TERENCE, '@', LIGHTCYAN, "Terence", - M_WARM_BLOOD| M_SPEAKS, - 0, 20, MONS_HUMAN, MH_NATURAL, -3, + M_WARM_BLOOD | M_SPEAKS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -3, { 3, 0, 0, 0 }, { 1, 0, 0, 14 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2696,8 +2946,9 @@ { MONS_JESSICA, '@', LIGHTGREY, "Jessica", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_HUMAN, MH_NATURAL, -3, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -3, { 4, 0, 0, 0 }, { 1, 0, 0, 10 }, 0, 10, 10, 7, MST_ORC_WIZARD_I, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2707,8 +2958,9 @@ { MONS_IJYB, 'g', BLUE, "Ijyb", - M_WARM_BLOOD | M_SPEAKS, - 0, 5, MONS_GOBLIN, MH_NATURAL, -3, + M_WARM_BLOOD | M_SPEAKS | M_UNIQUE, + MR_NO_FLAGS, + 0, 5, MONS_GOBLIN, MONS_GOBLIN, MH_NATURAL, -3, { 4, 0, 0, 0 }, { 3, 0, 0, 28 }, 2, 12, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2718,8 +2970,9 @@ { MONS_SIGMUND, '@', YELLOW, "Sigmund", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_HUMAN, MH_NATURAL, -3, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -3, { 5, 0, 0, 0 }, { 3, 0, 0, 25 }, 0, 11, 10, 7, MST_ORC_WIZARD_II, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2729,8 +2982,9 @@ { MONS_BLORK_THE_ORC, 'o', BROWN, "Blork the orc", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_ORC, MH_NATURAL, -4, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_ORC, MONS_ORC, MH_NATURAL, -4, { 7, 0, 0, 0 }, { 3, 0, 0, 32 }, 0, 9, 8, 7, MST_ORC_WIZARD_III, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2740,8 +2994,9 @@ { MONS_EDMUND, '@', RED, "Edmund", - M_WARM_BLOOD | M_SPEAKS, - 0, 20, MONS_HUMAN, MH_NATURAL, -4, + M_WARM_BLOOD | M_SPEAKS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -4, { 6, 0, 0, 0 }, { 4, 0, 0, 27 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2751,8 +3006,9 @@ { MONS_PSYCHE, '@', LIGHTMAGENTA, "Psyche", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_HUMAN, MH_NATURAL, -4, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -4, { 7, 0, 0, 0 }, { 5, 0, 0, 24 }, 0, 12, 13, 7, MST_ORC_WIZARD_III, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2762,8 +3018,10 @@ { MONS_EROLCHA, 'O', LIGHTBLUE, "Erolcha", - M_RES_ELEC | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_WARM_BLOOD | M_SPEAKS, - 0, 20, MONS_OGRE, MH_NATURAL, -7, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_WARM_BLOOD + | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_RES_ELEC, + 0, 20, MONS_OGRE, MONS_OGRE, MH_NATURAL, -7, { 20, 0, 0, 0 }, { 6, 0, 0, 45 }, 3, 7, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2773,8 +3031,9 @@ { MONS_DONALD, '@', BLUE, "Donald", - M_WARM_BLOOD | M_SPEAKS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_WARM_BLOOD | M_SPEAKS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 8, 0, 0, 0 }, { 5, 0, 0, 33 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2784,8 +3043,9 @@ { MONS_URUG, 'o', RED, "Urug", - M_WARM_BLOOD | M_SPEAKS, - 0, 20, MONS_ORC, MH_NATURAL, -5, + M_WARM_BLOOD | M_SPEAKS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_ORC, MONS_ORC, MH_NATURAL, -5, { 12, 0, 0, 0 }, { 6, 0, 0, 38 }, 0, 11, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2795,8 +3055,9 @@ { MONS_MICHAEL, '@', LIGHTGREY, "Michael", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 9, 0, 0, 0 }, { 6, 0, 0, 36 }, 0, 10, 10, 7, MST_ORC_WIZARD_III, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2806,8 +3067,9 @@ { MONS_JOSEPH, '@', CYAN, "Joseph", - M_WARM_BLOOD | M_SPEAKS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_WARM_BLOOD | M_SPEAKS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 9, 0, 0, 0 }, { 7, 0, 0, 42 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2817,8 +3079,9 @@ { MONS_SNORG, 'T', GREEN, "Snorg", - M_WARM_BLOOD | M_SPEAKS, - 0, 20, MONS_TROLL, MH_NATURAL, -6, + M_WARM_BLOOD | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_TROLL, MONS_TROLL, MH_NATURAL, -6, { 20, 15, 15, 0 }, { 8, 0, 0, 45 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2828,8 +3091,9 @@ { MONS_ERICA, '@', MAGENTA, "Erica", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 10, 0, 0, 0 }, { 9, 0, 0, 43 }, 0, 11, 11, 7, MST_WIZARD_II, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2839,8 +3103,9 @@ { MONS_JOSEPHINE, '@', WHITE, "Josephine", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 11, 0, 0, 0 }, { 9, 0, 0, 47 }, 0, 10, 10, 7, MST_NECROMANCER_I, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2850,8 +3115,9 @@ { MONS_HAROLD, '@', LIGHTGREEN, "Harold", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 12, 0, 0, 0 }, { 9, 0, 0, 51 }, 0, 8, 10, 7, MST_HELL_KNIGHT_II, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2861,8 +3127,9 @@ { MONS_NORBERT, '@', BROWN, "Norbert", - M_WARM_BLOOD | M_SPEAKS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_WARM_BLOOD | M_SPEAKS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 14, 0, 0, 0 }, { 10, 0, 0, 53 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2872,8 +3139,9 @@ { MONS_JOZEF, '@', LIGHTMAGENTA, "Jozef", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 14, 0, 0, 0 }, { 11, 0, 0, 60 }, 0, 9, 10, 7, MST_GUARDIAN_NAGA, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2883,8 +3151,9 @@ { MONS_AGNES, '@', LIGHTBLUE, "Agnes", - M_WARM_BLOOD | M_SPEAKS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_WARM_BLOOD | M_SPEAKS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 11, 0, 0, 0 }, { 11, 0, 0, 64 }, 0, 10, 15, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2894,8 +3163,9 @@ { MONS_MAUD, '@', RED, "Maud", - M_WARM_BLOOD | M_SPEAKS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_WARM_BLOOD | M_SPEAKS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 14, 0, 0, 0 }, { 13, 0, 0, 55 }, 0, 10, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2905,8 +3175,9 @@ { MONS_LOUISE, '@', BLUE, "Louise", - M_SPELLCASTER | M_RES_ELEC | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_UNIQUE, + MR_RES_ELEC, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 12, 0, 0, 0 }, { 13, 0, 0, 52 }, 0, 10, 10, 7, MST_WIZARD_IV, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2916,8 +3187,10 @@ { MONS_FRANCIS, '@', YELLOW, "Francis", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_SEE_INVIS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD + | M_SEE_INVIS | M_EVIL | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 12, 0, 0, 0 }, { 14, 0, 0, 67 }, 0, 10, 10, 7, MST_ORC_HIGH_PRIEST, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2927,8 +3200,10 @@ { MONS_FRANCES, '@', YELLOW, "Frances", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_SEE_INVIS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD + | M_SEE_INVIS | M_EVIL | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 11, 0, 0, 0 }, { 14, 0, 0, 70 }, 0, 10, 10, 7, MST_ORC_HIGH_PRIEST, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2938,8 +3213,10 @@ { MONS_RUPERT, '@', RED, "Rupert", - M_SPELLCASTER | M_RES_ELEC | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_SEE_INVIS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD + | M_SEE_INVIS | M_UNIQUE, + MR_RES_ELEC, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 13, 0, 0, 0 }, { 16, 0, 0, 80 }, 0, 10, 10, 7, MST_WIZARD_IV, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2949,8 +3226,10 @@ { MONS_WAYNE, '@', YELLOW, "Wayne", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_SEE_INVIS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD + | M_SEE_INVIS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 14, 0, 0, 0 }, { 17, 0, 0, 78 }, 1, 10, 7, 7, MST_ORC_PRIEST, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2960,8 +3239,10 @@ { MONS_DUANE, '@', YELLOW, "Duane", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_SEE_INVIS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD + | M_SEE_INVIS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 14, 0, 0, 0 }, { 18, 0, 0, 83 }, 0, 10, 10, 7, MST_ORC_WIZARD_I, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -2971,8 +3252,9 @@ { MONS_XTAHUA, 'D', RED, "Xtahua", - M_SPEAKS | M_SEE_INVIS | M_RES_POISON | M_RES_FIRE | M_ED_COLD | M_FLIES, - 0, 20, MONS_DRAGON, MH_NATURAL, -7, + M_SPEAKS | M_SEE_INVIS | M_FLIES | M_SPECIAL_ABILITY | M_UNIQUE, + MR_RES_POISON | MR_RES_FIRE | MR_VUL_COLD, + 0, 20, MONS_DRAGON, MONS_DRAGON, MH_NATURAL, -7, { 29, 17, 17, 0 }, { 19, 0, 0, 133 }, 15, 7, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_NORMAL, @@ -2982,19 +3264,23 @@ { MONS_NORRIS, '@', LIGHTRED, "Norris", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_SEE_INVIS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD + | M_SEE_INVIS | M_EVIL | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 16, 0, 0, 0 }, { 20, 0, 0, 95 }, - 1, 9, 9, 7, MST_HELL_KNIGHT_II, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, + 1, 9, 9, 7, MST_MYSTIC, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, MONUSE_WEAPONS_ARMOUR } , { MONS_ADOLF, '@', DARKGREY, "Adolf", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_SEE_INVIS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD + | M_SEE_INVIS | M_EVIL | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 17, 0, 0, 0 }, { 21, 0, 0, 105 }, 0, 10, 10, 7, MST_LICH_IV, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -3004,8 +3290,10 @@ { MONS_MARGERY, '@', RED, "Margery", - M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD | M_SEE_INVIS, - 0, 20, MONS_HUMAN, MH_NATURAL, -5, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS | M_WARM_BLOOD + | M_SEE_INVIS | M_UNIQUE, + MR_NO_FLAGS, + 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { 18, 0, 0, 0 }, { 22, 0, 0, 119 }, 0, 10, 10, 7, MST_EFREET, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -3015,8 +3303,10 @@ { MONS_BORIS, 'L', RED, "Boris", - M_RES_POISON | M_RES_COLD | M_RES_ELEC | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_SPEAKS, - 0, 23, MONS_LICH, MH_UNDEAD, -11, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_SPEAKS | M_EVIL + | M_UNIQUE, + MR_RES_POISON | MR_RES_COLD | MR_RES_ELEC, + 0, 23, MONS_LICH, MONS_LICH, MH_UNDEAD, -11, { 15, 0, 0, 0 }, { 22, 0, 0, 99 }, 12, 10, 10, 7, MST_LICH_IV, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -3027,8 +3317,9 @@ { MONS_GERYON, '&', GREEN, "Geryon", - M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS, - 0, 25, MONS_GERYON, MH_DEMONIC, -6, + M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_NO_FLAGS, + 0, 25, MONS_GERYON, MONS_GERYON, MH_DEMONIC, -6, { 30, 0, 0, 0 }, { 15, 0, 0, 240 }, 15, 6, 10, 7, MST_GERYON, CE_CONTAMINATED, Z_NOZOMBIE, S_ROAR, I_NORMAL, @@ -3038,8 +3329,9 @@ { MONS_DISPATER, '&', MAGENTA, "Dispater", - M_RES_ELEC | M_RES_POISON | M_RES_HELLFIRE | M_RES_COLD | M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS, - 0, 25, MONS_DISPATER, MH_DEMONIC, -10, + M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_RES_ELEC | MR_RES_POISON | MR_RES_HELLFIRE | MR_RES_COLD, + 0, 25, MONS_DISPATER, MONS_DISPATER, MH_DEMONIC, -10, { 15, 0, 0, 0 }, { 16, 0, 0, 222 }, 15, 3, 6, 7, MST_DISPATER, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -3049,8 +3341,9 @@ { MONS_ASMODEUS, '&', LIGHTMAGENTA, "Asmodeus", - M_RES_ELEC | M_RES_POISON | M_RES_HELLFIRE | M_SPELLCASTER | M_FLIES | M_SEE_INVIS | M_SPEAKS, - 0, 25, MONS_ASMODEUS, MH_DEMONIC, -12, + M_SPELLCASTER | M_FLIES | M_SEE_INVIS | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_RES_ELEC | MR_RES_POISON | MR_RES_HELLFIRE, + 0, 25, MONS_ASMODEUS, MONS_ASMODEUS, MH_DEMONIC, -12, { 20, 0, 0, 0 }, { 17, 0, 0, 245 }, 12, 7, 9, 7, MST_ASMODEUS, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -3061,8 +3354,9 @@ // Antaeus is now demonic so that he'll resist torment. -- bwr { MONS_ANTAEUS, 'C', LIGHTCYAN, "Antaeus", - M_RES_ELEC | M_ED_FIRE | M_RES_COLD | M_SPELLCASTER | M_SPEAKS, - 0, 25, MONS_ANTAEUS, MH_DEMONIC, -9, + M_SPELLCASTER | M_SPEAKS | M_UNIQUE, + MR_RES_ELEC | MR_VUL_FIRE | MR_RES_COLD, + 0, 25, MONS_HILL_GIANT, MONS_ANTAEUS, MH_DEMONIC, -9, { 30, 0, 0, 0 }, { 22, 0, 0, 250 }, 10, 4, 7, 7, MST_ANTAEUS, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -3072,8 +3366,9 @@ { MONS_ERESHKIGAL, '&', WHITE, "Ereshkigal", - M_RES_ELEC | M_RES_POISON | M_RES_COLD | M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS, - 0, 25, MONS_ERESHKIGAL, MH_DEMONIC, -10, + M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_RES_ELEC | MR_RES_POISON | MR_RES_COLD, + 0, 25, MONS_ERESHKIGAL, MONS_ERESHKIGAL, MH_DEMONIC, -10, { 20, 0, 0, 0 }, { 18, 0, 0, 238 }, 15, 6, 9, 7, MST_ERESHKIGAL, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -3083,8 +3378,9 @@ { MONS_ANCIENT_LICH, 'L', DARKGREY, "ancient lich", - M_RES_POISON | M_RES_COLD | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_RES_FIRE | M_RES_ELEC, - 0, 20, MONS_LICH, MH_UNDEAD, -14, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD | MR_RES_FIRE | MR_RES_ELEC, + 0, 20, MONS_LICH, MONS_LICH, MH_UNDEAD, -14, { 20, 0, 0, 0 }, { 27, 2, 4, 0 }, 20, 10, 12, 7, MST_LICH_I, CE_NOCORPSE, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -3095,8 +3391,9 @@ { MONS_OOZE, 'J', LIGHTGREY, "ooze", - M_RES_POISON | M_NO_SKELETON | M_SEE_INVIS, - 0, 5, MONS_OOZE, MH_NATURAL, -6, + M_NO_SKELETON | M_SEE_INVIS, + MR_RES_POISON | MR_RES_ASPHYX, + 0, 5, MONS_JELLY, MONS_OOZE, MH_NATURAL, -6, { 5, 0, 0, 0 }, { 3, 3, 5, 0 }, 1, 3, 8, 7, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3107,7 +3404,8 @@ { MONS_VAULT_GUARD, '@', CYAN, "vault guard", M_WARM_BLOOD | M_SEE_INVIS, - 0, 12, MONS_HUMAN, MH_NATURAL, -3, + MR_NO_FLAGS, + 0, 12, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 13, 3, 5, 0 }, 1, 13, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -3119,8 +3417,9 @@ placed in the Crypt. */ { MONS_CURSE_SKULL, 'z', DARKGREY, "curse skull", - M_RES_ELEC | M_RES_POISON | M_RES_HELLFIRE | M_RES_COLD | M_LEVITATE | M_SPELLCASTER | M_SEE_INVIS, - 0, 50, MONS_CURSE_SKULL, MH_UNDEAD, 5000, + M_LEVITATE | M_SPELLCASTER | M_SEE_INVIS | M_EVIL, + MR_RES_ELEC | MR_RES_POISON | MR_RES_HELLFIRE | MR_RES_COLD, + 0, 50, MONS_LICH, MONS_CURSE_SKULL, MH_UNDEAD, MAG_IMMUNE, { 0, 0, 0, 0 }, { 13, 0, 0, 66 }, 40, 3, 10, 7, MST_CURSE_SKULL, CE_NOCORPSE, Z_NOZOMBIE, S_MOAN, I_HIGH, @@ -3130,8 +3429,9 @@ { MONS_VAMPIRE_KNIGHT, 'V', CYAN, "vampire knight", - M_RES_POISON | M_RES_COLD | M_SPELLCASTER | M_SEE_INVIS, - 0, 13, MONS_VAMPIRE, MH_UNDEAD, -6, + M_SPELLCASTER | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 13, MONS_VAMPIRE, MONS_VAMPIRE, MH_UNDEAD, -6, { 33, 0, 0, 0 }, { 11, 3, 7, 0 }, 10, 10, 10, 7, MST_VAMPIRE_KNIGHT, CE_NOCORPSE, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -3141,8 +3441,9 @@ { MONS_VAMPIRE_MAGE, 'V', MAGENTA, "vampire mage", - M_RES_POISON | M_RES_COLD | M_SPELLCASTER | M_SEE_INVIS | M_FLIES, - 0, 15, MONS_VAMPIRE, MH_UNDEAD, -6, + M_SPELLCASTER | M_SEE_INVIS | M_FLIES | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 15, MONS_VAMPIRE, MONS_VAMPIRE, MH_UNDEAD, -6, { 22, 0, 0, 0 }, { 8, 3, 4, 0 }, 10, 10, 10, 7, MST_VAMPIRE_MAGE, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -3153,7 +3454,8 @@ { MONS_SHINING_EYE, 'G', LIGHTMAGENTA, "shining eye", M_NO_SKELETON | M_LEVITATE | M_SPELLCASTER | M_SEE_INVIS, - 0, 14, MONS_SHINING_EYE, MH_NATURAL, 5000, + MR_RES_ASPHYX, + 0, 14, MONS_SHINING_EYE, MONS_SHINING_EYE, MH_NATURAL, MAG_IMMUNE, { 0, 0, 0, 0 }, { 10, 3, 5, 0 }, 3, 1, 7, 7, MST_SHINING_EYE, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3164,8 +3466,9 @@ { MONS_ORB_GUARDIAN, 'X', MAGENTA, "Orb Guardian", M_NO_SKELETON | M_SEE_INVIS, - 0, 20, MONS_ORB_GUARDIAN, MH_NATURAL, -6, - { 40, 0, 0, 0 }, + MR_NO_FLAGS, + 0, 20, MONS_ORB_GUARDIAN, MONS_ORB_GUARDIAN, MH_NATURAL, -6, + { 45, 0, 0, 0 }, { 15, 3, 5, 0 }, 13, 13, 14, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, MONUSE_OPEN_DOORS @@ -3174,8 +3477,9 @@ { MONS_DAEVA, 'A', YELLOW, "Daeva", - M_RES_POISON | M_LEVITATE | M_SPELLCASTER, - 0, 12, MONS_DAEVA, MH_HOLY, -8, + M_LEVITATE | M_SPELLCASTER, + MR_RES_POISON, + 0, 12, MONS_ANGEL, MONS_DAEVA, MH_HOLY, -8, { 25, 0, 0, 0 }, { 12, 3, 5, 0 }, 10, 13, 13, 7, MST_DAEVA, CE_NOCORPSE, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -3186,8 +3490,9 @@ /* spectral thing - similar to zombies/skeletons */ { MONS_SPECTRAL_THING, 'W', GREEN, "", - M_RES_POISON | M_RES_COLD | M_LEVITATE | M_SEE_INVIS, - 0, 11, MONS_SPECTRAL_THING, MH_UNDEAD, 5000, + M_LEVITATE | M_SEE_INVIS, + MR_RES_POISON | MR_RES_COLD, + 0, 11, MONS_WRAITH, MONS_SPECTRAL_THING, MH_UNDEAD, MAG_IMMUNE, { 20, 0, 0, 0 }, { 8, 3, 5, 0 }, 8, 5, 7, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3197,9 +3502,10 @@ { MONS_GREATER_NAGA, 'N', RED, "greater naga", - M_RES_POISON | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_WARM_BLOOD, - 750, 10, MONS_NAGA, MH_NATURAL, 5000, - { 18, 0, 0, 0 }, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_WARM_BLOOD, + MR_RES_POISON, + 750, 10, MONS_NAGA, MONS_NAGA, MH_NATURAL, MAG_IMMUNE, + { 24, 0, 0, 0 }, { 15, 3, 5, 0 }, 6, 10, 8, 7, MST_NAGA_MAGE, CE_POISONOUS, Z_SMALL, S_SHOUT, I_HIGH, MONUSE_WEAPONS_ARMOUR @@ -3208,8 +3514,9 @@ { MONS_SKELETAL_DRAGON, 'D', LIGHTGREY, "skeletal dragon", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC | M_SEE_INVIS, - 0, 12, MONS_SKELETAL_DRAGON, MH_UNDEAD, -4, + M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 12, MONS_SKELETAL_WARRIOR, MONS_SKELETAL_DRAGON, MH_UNDEAD, -4, { 30, 20, 20, 0 }, { 20, 8, 8, 0 }, 20, 4, 8, 7, MST_NO_SPELLS, CE_CLEAN, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3219,8 +3526,9 @@ { MONS_TENTACLED_MONSTROSITY, 'X', GREEN, "tentacled monstrosity", - M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_RES_ELEC | M_SEE_INVIS | M_AMPHIBIOUS, - 0, 10, MONS_TENTACLED_MONSTROSITY, MH_NATURAL, -5, + M_SEE_INVIS | M_AMPHIBIOUS, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_TENTACLED_MONSTROSITY, MONS_TENTACLED_MONSTROSITY, MH_NATURAL, -5, { 22, 17, 13, 19 }, { 25, 3, 5, 0 }, 5, 5, 9, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -3231,7 +3539,8 @@ { MONS_SPHINX, 'H', LIGHTGREY, "sphinx", M_FLIES | M_SEE_INVIS | M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD, - 0, 10, MONS_SPHINX, MH_NATURAL, -3, + MR_NO_FLAGS, + 0, 10, MONS_SPHINX, MONS_SPHINX, MH_NATURAL, -3, { 25, 12, 12, 0 }, { 16, 3, 5, 0 }, 5, 5, 13, 7, MST_SPHINX, CE_CLEAN, Z_NOZOMBIE, S_SHOUT, I_HIGH, @@ -3241,8 +3550,9 @@ { MONS_ROTTING_HULK, 'n', BROWN, "rotting hulk", - M_RES_POISON | M_RES_COLD, - 0, 12, MONS_ROTTING_HULK, MH_UNDEAD, -5, + M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 12, MONS_GHOUL, MONS_ROTTING_HULK, MH_UNDEAD, -5, { 25, 0, 0, 0 }, { 10, 3, 5, 0 }, 5, 7, 8, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -3252,8 +3562,9 @@ { MONS_GUARDIAN_MUMMY, 'M', YELLOW, "guardian mummy", - M_RES_POISON | M_RES_COLD | M_SEE_INVIS, - 0, 13, MONS_GUARDIAN_MUMMY, MH_UNDEAD, -5, + M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 13, MONS_MUMMY, MONS_GUARDIAN_MUMMY, MH_UNDEAD, -5, { 30, 0, 0, 0 }, { 7, 5, 3, 0 }, 6, 9, 9, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3263,8 +3574,9 @@ { MONS_GREATER_MUMMY, 'M', DARKGREY, "greater mummy", - M_RES_POISON | M_RES_COLD | M_RES_ELEC | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS, - 0, 20, MONS_MUMMY, MH_UNDEAD, 5000, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD | MR_RES_ELEC, + 0, 20, MONS_MUMMY, MONS_MUMMY, MH_UNDEAD, MAG_IMMUNE, { 35, 0, 0, 0 }, { 15, 5, 3, 100 }, 10, 6, 10, 7, MST_MUMMY, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3274,8 +3586,9 @@ { MONS_MUMMY_PRIEST, 'M', RED, "mummy priest", - M_RES_POISON | M_RES_COLD | M_RES_ELEC | M_SPELLCASTER | M_PRIEST | M_SEE_INVIS, - 0, 16, MONS_MUMMY, MH_UNDEAD, 5000, + M_SPELLCASTER | M_PRIEST | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_COLD | MR_RES_ELEC, + 0, 16, MONS_MUMMY, MONS_MUMMY, MH_UNDEAD, MAG_IMMUNE, { 30, 0, 0, 0 }, { 10, 5, 3, 0 }, 8, 7, 9, 7, MST_MUMMY, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3286,7 +3599,8 @@ { MONS_CENTAUR_WARRIOR, 'c', YELLOW, "centaur warrior", M_WARM_BLOOD, - 1500, 12, MONS_CENTAUR, MH_NATURAL, -3, + MR_NO_FLAGS, + 1500, 12, MONS_CENTAUR, MONS_CENTAUR, MH_NATURAL, -3, { 16, 0, 0, 0 }, { 9, 3, 5, 0 }, 4, 8, 15, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_SHOUT, I_HIGH, @@ -3297,7 +3611,8 @@ { MONS_YAKTAUR_CAPTAIN, 'c', RED, "yaktaur captain", M_WARM_BLOOD, - 2000, 10, MONS_YAKTAUR, MH_NATURAL, -3, + MR_NO_FLAGS, + 2000, 10, MONS_YAKTAUR, MONS_YAKTAUR, MH_NATURAL, -3, { 23, 0, 0, 0 }, { 14, 3, 5, 0 }, 5, 5, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_SHOUT, I_HIGH, @@ -3305,10 +3620,205 @@ } , +{ // Base draconian -- for use like MONS_HUMAN, MONS_ELF although we + // now store the draconian subspecies in the high byte of mon->number + // for those listed as species MONS_DRACONIAN. + MONS_DRACONIAN, 'd', BROWN, "draconian", + M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_NO_FLAGS, + 900, 10, MONS_DRACONIAN, MONS_DRACONIAN, MH_NATURAL, -1, + { 15, 0, 0, 0 }, + { 3, 6, 4, 0 }, + 7, 8, 10, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_BLACK_DRACONIAN, 'd', DARKGREY, "black draconian", + M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_RES_ELEC, + 900, 10, MONS_DRACONIAN, MONS_BLACK_DRACONIAN, MH_NATURAL, -2, + { 20, 0, 0, 0 }, + { 14, 5, 4, 0 }, + 9, 10, 10, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_YELLOW_DRACONIAN, 'd', YELLOW, "yellow draconian", + M_FLIES | M_HUMANOID | M_COLD_BLOOD | M_SPECIAL_ABILITY, + MR_NO_FLAGS, + 900, 10, MONS_DRACONIAN, MONS_YELLOW_DRACONIAN, MH_NATURAL, -2, + { 20, 0, 0, 0 }, + { 14, 5, 4, 0 }, + 9, 10, 10, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_PALE_DRACONIAN, 'd', LIGHTGREY, "pale draconian", + M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_RES_FIRE, + 900, 10, MONS_DRACONIAN, MONS_PALE_DRACONIAN, MH_NATURAL, -2, + { 20, 0, 0, 0 }, + { 14, 5, 4, 0 }, + 9, 14, 12, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_GREEN_DRACONIAN, 'd', LIGHTGREEN, "green draconian", + M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_RES_POISON, + 900, 10, MONS_DRACONIAN, MONS_GREEN_DRACONIAN, MH_NATURAL, -2, + { 20, 0, 0, 0 }, + { 14, 5, 4, 0 }, + 9, 10, 10, 10, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_PURPLE_DRACONIAN, 'd', MAGENTA, "purple draconian", + M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_NO_FLAGS, + 900, 10, MONS_DRACONIAN, MONS_PURPLE_DRACONIAN, MH_NATURAL, -2, + { 20, 0, 0, 0 }, + { 14, 5, 4, 0 }, + 8, 10, 10, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_RED_DRACONIAN, 'd', RED, "red draconian", + M_HUMANOID | M_FLIES | M_COLD_BLOOD | M_SPECIAL_ABILITY, + MR_RES_FIRE, + 900, 10, MONS_DRACONIAN, MONS_RED_DRACONIAN, MH_NATURAL, -2, + { 20, 0, 0, 0 }, + { 14, 5, 4, 0 }, + 9, 10, 10, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_WHITE_DRACONIAN, 'd', WHITE, "white draconian", + M_HUMANOID | M_FLIES | M_COLD_BLOOD | M_SPECIAL_ABILITY, + MR_RES_COLD, + 900, 10, MONS_DRACONIAN, MONS_WHITE_DRACONIAN, MH_NATURAL, -2, + { 20, 0, 0, 0 }, + { 14, 5, 4, 0 }, + 9, 10, 10, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_MOTTLED_DRACONIAN, 'd', LIGHTMAGENTA, "mottled draconian", + M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_RES_FIRE, + 900, 10, MONS_DRACONIAN, MONS_MOTTLED_DRACONIAN, MH_NATURAL, -2, + { 20, 0, 0, 0 }, + { 14, 5, 4, 0 }, + 9, 10, 10, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_DRACONIAN_CALLER, 'd', BROWN, "draconian caller", + M_SPELLCASTER | M_ACTUAL_SPELLS | M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_NO_FLAGS, + 900, 10, MONS_DRACONIAN, MONS_DRACONIAN, MH_NATURAL, -3, + { 20, 0, 0, 0 }, + { 16, 4, 3, 0 }, + 9, 10, 10, 10, MST_DRAC_CALLER, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_DRACONIAN_MONK, 'd', BLUE, "draconian monk", + M_FIGHTER | M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_NO_FLAGS, + 900, 10, MONS_DRACONIAN, MONS_DRACONIAN, MH_NATURAL, -3, + { 35, 20, 15, 0 }, + { 16, 6, 3, 0 }, + 6, 20, 10, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_DRACONIAN_ZEALOT, 'd', LIGHTBLUE, "draconian zealot", + M_SPELLCASTER | M_HUMANOID | M_PRIEST | M_FLIES | M_COLD_BLOOD, + MR_NO_FLAGS, + 900, 10, MONS_DRACONIAN, MONS_DRACONIAN, MH_NATURAL, -3, + { 15, 0, 0, 0 }, + { 16, 4, 2, 0 }, + 12, 10, 10, 10, MST_DEEP_ELF_HIGH_PRIEST, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_DRACONIAN_SHIFTER, 'd', LIGHTCYAN, "draconian shifter", + M_SPELLCASTER | M_ACTUAL_SPELLS | M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_NO_FLAGS, + 900, 10, MONS_DRACONIAN, MONS_DRACONIAN, MH_NATURAL, -4, + { 15, 0, 0, 0 }, + { 16, 4, 4, 0 }, + 8, 16, 10, 10, MST_DRAC_SHIFTER, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_DRACONIAN_ANNIHILATOR, 'd', GREEN, "draconian annihilator", + M_SPELLCASTER | M_ACTUAL_SPELLS | M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_NO_FLAGS, + 900, 10, MONS_DRACONIAN, MONS_DRACONIAN, MH_NATURAL, -4, + { 15, 0, 0, 0 }, + { 16, 4, 2, 0 }, + 8, 10, 10, 10, MST_DEEP_ELF_ANNIHILATOR, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_DRACONIAN_KNIGHT, 'd', CYAN, "draconian knight", + M_SPELLCASTER | M_HUMANOID | M_FIGHTER | M_FLIES | M_COLD_BLOOD, + MR_NO_FLAGS, + 900, 10, MONS_DRACONIAN, MONS_DRACONIAN, MH_NATURAL, -4, + { 15, 0, 0, 0 }, + { 16, 6, 4, 0 }, + 12, 12, 10, 6, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + +{ + MONS_DRACONIAN_SCORCHER, 'd', LIGHTRED, "draconian scorcher", + M_SPELLCASTER | M_ACTUAL_SPELLS | M_HUMANOID | M_FLIES | M_COLD_BLOOD, + MR_RES_FIRE | MR_RES_HELLFIRE, + 900, 10, MONS_DRACONIAN, MONS_DRACONIAN, MH_NATURAL, -4, + { 15, 0, 0, 0 }, + { 16, 4, 2, 0 }, + 8, 12, 10, 10, MST_DRAC_SCORCHER, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, + MONUSE_STARTING_EQUIPMENT +} +, + { MONS_KILLER_KLOWN, '@', BLACK, "Killer Klown", - M_RES_FIRE | M_RES_COLD | M_RES_POISON | M_SEE_INVIS | M_SPEAKS | M_WARM_BLOOD, - 0, 15, MONS_KILLER_KLOWN, MH_NATURAL, 5000, + M_SEE_INVIS | M_SPEAKS | M_WARM_BLOOD | M_SPECIAL_ABILITY, + MR_RES_FIRE | MR_RES_COLD | MR_RES_POISON, + 0, 15, MONS_HUMAN, MONS_KILLER_KLOWN, MH_NATURAL, MAG_IMMUNE, { 30, 0, 0, 0 }, { 20, 5, 5, 0 }, 10, 15, 15, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH, @@ -3318,8 +3828,9 @@ { MONS_ELECTRIC_GOLEM, '8', LIGHTCYAN, "electric golem", - M_SPELLCASTER | M_RES_ELEC | M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_SEE_INVIS, - 0, 10, MONS_ELECTRIC_GOLEM, MH_NONLIVING, 5000, + M_SPELLCASTER | M_SEE_INVIS, + MR_RES_ELEC | MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD, + 0, 10, MONS_CLAY_GOLEM, MONS_ELECTRIC_GOLEM, MH_NONLIVING, MAG_IMMUNE, { 12, 12, 12, 12 }, { 15, 7, 4, 0 }, 5, 20, 20, 7, MST_ELECTRIC_GOLEM, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3329,8 +3840,9 @@ { MONS_BALL_LIGHTNING, '*', LIGHTCYAN, "ball lightning", - M_FLIES | M_RES_ELEC | M_CONFUSED | M_SPELLCASTER, - 0, 20, MONS_BALL_LIGHTNING, MH_NONLIVING, 5000, + M_FLIES | M_CONFUSED | M_SPELLCASTER | M_SPECIAL_ABILITY, + MR_RES_ELEC, + 0, 20, MONS_BALL_LIGHTNING, MONS_BALL_LIGHTNING, MH_NONLIVING, MAG_IMMUNE, { 5, 0, 0, 0 }, { 12, 0, 0, 1 }, 0, 10, 20, 7, MST_STORM_DRAGON, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_PLANT, @@ -3340,8 +3852,9 @@ { MONS_ORB_OF_FIRE, '*', RED, "orb of fire", - M_SPELLCASTER | M_FLIES | M_RES_ELEC | M_RES_FIRE | M_RES_COLD | M_RES_POISON | M_SEE_INVIS, - 0, 10, MONS_ORB_OF_FIRE, MH_NONLIVING, 5000, + M_SPELLCASTER | M_FLIES | M_SEE_INVIS, + MR_RES_ELEC | MR_RES_FIRE | MR_RES_COLD | MR_RES_POISON, + 0, 10, MONS_ORB_OF_FIRE, MONS_ORB_OF_FIRE, MH_NONLIVING, MAG_IMMUNE, { 0, 0, 0, 0 }, { 30, 0, 0, 150 }, 20, 20, 20, 7, MST_ORB_OF_FIRE, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, @@ -3352,7 +3865,8 @@ { MONS_QUOKKA, 'r', LIGHTGREY, "quokka", M_WARM_BLOOD, - 300, 10, MONS_QUOKKA, MH_NATURAL, -1, + MR_NO_FLAGS, + 300, 10, MONS_QUOKKA, MONS_QUOKKA, MH_NATURAL, -1, { 5, 0, 0, 0 }, { 1, 3, 5, 0 }, 2, 13, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_NORMAL, @@ -3363,7 +3877,8 @@ { MONS_EYE_OF_DEVASTATION, 'G', YELLOW, "eye of devastation", M_NO_SKELETON | M_LEVITATE | M_SPELLCASTER | M_SEE_INVIS, - 0, 11, MONS_EYE_OF_DEVASTATION, MH_NATURAL, 5000, + MR_RES_ASPHYX, + 0, 11, MONS_GIANT_EYEBALL, MONS_EYE_OF_DEVASTATION, MH_NATURAL, MAG_IMMUNE, { 0, 0, 0, 0 }, { 10, 3, 5, 0 }, 12, 1, 7, 7, MST_EYE_OF_DEVASTATION, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3374,7 +3889,8 @@ { MONS_MOTH_OF_WRATH, 'y', BROWN, "moth of wrath", M_FLIES, - 0, 10, MONS_MOTH_OF_WRATH, MH_NATURAL, -3, + MR_NO_FLAGS, + 0, 10, MONS_MOTH_OF_WRATH, MONS_MOTH_OF_WRATH, MH_NATURAL, -3, { 25, 0, 0, 0 }, { 9, 3, 5, 0 }, 0, 10, 12, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SHOUT, I_NORMAL, @@ -3384,8 +3900,9 @@ { MONS_DEATH_COB, '%', YELLOW, "death cob", - M_RES_POISON | M_RES_COLD | M_SPEAKS, - 0, 10, MONS_DEATH_COB, MH_UNDEAD, -3, + M_SPEAKS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 10, MONS_DEATH_COB, MONS_DEATH_COB, MH_UNDEAD, -3, { 20, 0, 0, 0 }, { 10, 4, 5, 0 }, 10, 15, 25, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_MOAN, I_NORMAL, @@ -3394,12 +3911,13 @@ , { - MONS_CURSE_TOE, 'z', DARKGREY, "curse toe", - M_RES_ELEC | M_RES_POISON | M_RES_HELLFIRE | M_RES_COLD | M_LEVITATE | M_SPELLCASTER | M_SEE_INVIS, - 0, 60, MONS_CURSE_TOE, MH_UNDEAD, 5000, + MONS_CURSE_TOE, 'z', YELLOW, "curse toe", + M_LEVITATE | M_SPELLCASTER | M_SEE_INVIS | M_EVIL, + MR_RES_ELEC | MR_RES_POISON | MR_RES_HELLFIRE | MR_RES_COLD, + 0, 60, MONS_LICH, MONS_CURSE_TOE, MH_UNDEAD, MAG_IMMUNE, { 0, 0, 0, 0 }, { 14, 0, 0, 77 }, - 50, 1, 12, 7, MST_CURSE_SKULL, CE_NOCORPSE, Z_NOZOMBIE, S_MOAN, I_HIGH, + 50, 1, 12, 7, MST_CURSE_TOE, CE_NOCORPSE, Z_NOZOMBIE, S_MOAN, I_HIGH, MONUSE_NOTHING } , @@ -3407,8 +3925,9 @@ { // gold mimics are the only mimics that actually use their name -- bwr MONS_GOLD_MIMIC, '$', YELLOW, "pile of gold coins", - M_NO_SKELETON | M_RES_POISON | M_RES_ELEC | M_RES_FIRE | M_RES_COLD, - 0, 13, MONS_GOLD_MIMIC, MH_NONLIVING, -3, + M_NO_SKELETON, + MR_RES_POISON | MR_RES_ELEC | MR_RES_FIRE | MR_RES_COLD, + 0, 13, MONS_GOLD_MIMIC, MONS_GOLD_MIMIC, MH_NONLIVING, -3, { 12, 12, 12, 0 }, { 8, 3, 5, 0 }, 5, 1, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -3418,8 +3937,9 @@ { MONS_WEAPON_MIMIC, ')', BLACK, "mimic", - M_NO_SKELETON | M_RES_POISON | M_RES_ELEC | M_RES_FIRE | M_RES_COLD, - 0, 13, MONS_GOLD_MIMIC, MH_NONLIVING, -3, + M_NO_SKELETON, + MR_RES_POISON | MR_RES_ELEC | MR_RES_FIRE | MR_RES_COLD, + 0, 13, MONS_GOLD_MIMIC, MONS_GOLD_MIMIC, MH_NONLIVING, -3, { 17, 17, 17, 0 }, { 8, 3, 5, 0 }, 5, 1, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -3429,8 +3949,9 @@ { MONS_ARMOUR_MIMIC, '[', BLACK, "mimic", - M_NO_SKELETON | M_RES_POISON | M_RES_ELEC | M_RES_FIRE | M_RES_COLD, - 0, 13, MONS_GOLD_MIMIC, MH_NONLIVING, -3, + M_NO_SKELETON, + MR_RES_POISON | MR_RES_ELEC | MR_RES_FIRE | MR_RES_COLD, + 0, 13, MONS_GOLD_MIMIC, MONS_GOLD_MIMIC, MH_NONLIVING, -3, { 12, 12, 12, 0 }, { 8, 3, 5, 0 }, 15, 1, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -3440,8 +3961,9 @@ { MONS_SCROLL_MIMIC, '?', LIGHTGREY, "mimic", - M_NO_SKELETON | M_RES_POISON | M_RES_ELEC | M_RES_FIRE | M_RES_COLD, - 0, 13, MONS_GOLD_MIMIC, MH_NONLIVING, -3, + M_NO_SKELETON, + MR_RES_POISON | MR_RES_ELEC | MR_RES_FIRE | MR_RES_COLD, + 0, 13, MONS_GOLD_MIMIC, MONS_GOLD_MIMIC, MH_NONLIVING, -3, { 12, 12, 12, 0 }, { 8, 3, 5, 0 }, 5, 1, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -3451,8 +3973,9 @@ { MONS_POTION_MIMIC, '!', BLACK, "mimic", - M_NO_SKELETON | M_RES_POISON | M_RES_ELEC | M_RES_FIRE | M_RES_COLD, - 0, 13, MONS_GOLD_MIMIC, MH_NONLIVING, -3, + M_NO_SKELETON, + MR_RES_POISON | MR_RES_ELEC | MR_RES_FIRE | MR_RES_COLD, + 0, 13, MONS_GOLD_MIMIC, MONS_GOLD_MIMIC, MH_NONLIVING, -3, { 12, 12, 12, 0 }, { 8, 3, 5, 0 }, 5, 1, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_NORMAL, @@ -3462,8 +3985,9 @@ { MONS_HELL_HOG, 'h', RED, "hell-hog", - M_SPELLCASTER, - 0, 10, MONS_HELL_HOG, MH_DEMONIC, -3, + M_SPELLCASTER | M_THICK_SKIN | M_EVIL, + MR_NO_FLAGS, + 0, 10, MONS_HELL_HOG, MONS_HELL_HOG, MH_DEMONIC, -3, { 20, 0, 0, 0 }, { 11, 3, 5, 0 }, 2, 9, 14, 7, MST_HELL_HOG, CE_CLEAN, Z_NOZOMBIE, S_SILENT, I_ANIMAL, @@ -3473,8 +3997,9 @@ { MONS_SERPENT_OF_HELL, 'D', RED, "Serpent of Hell", - M_SPELLCASTER | M_RES_POISON | M_RES_HELLFIRE | M_FLIES | M_SEE_INVIS, - 0, 18, MONS_SERPENT_OF_HELL, MH_DEMONIC, -13, + M_SPELLCASTER | M_FLIES | M_SEE_INVIS | M_EVIL, + MR_RES_POISON | MR_RES_HELLFIRE, + 0, 18, MONS_SERPENT_OF_HELL, MONS_SERPENT_OF_HELL, MH_DEMONIC, -13, { 35, 15, 15, 0 }, { 20, 4, 4, 0 }, 12, 9, 14, 7, MST_SERPENT_OF_HELL, CE_CLEAN, Z_NOZOMBIE, S_ROAR, I_HIGH, @@ -3485,7 +4010,8 @@ { MONS_BOGGART, 'g', DARKGREY, "boggart", M_SPELLCASTER | M_ACTUAL_SPELLS | M_SEE_INVIS, - 0, 14, MONS_BOGGART, MH_NATURAL, -7, + MR_NO_FLAGS, + 0, 14, MONS_BOGGART, MONS_BOGGART, MH_NATURAL, -7, { 5, 0, 0, 0 }, { 2, 3, 5, 0 }, 0, 12, 12, 7, MST_BOGGART, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, @@ -3496,7 +4022,8 @@ { MONS_QUICKSILVER_DRAGON, 'D', LIGHTCYAN, "quicksilver dragon", M_SPELLCASTER | M_FLIES | M_SEE_INVIS, - 0, 14, MONS_QUICKSILVER_DRAGON, MH_NATURAL, -7, + MR_NO_FLAGS, + 0, 14, MONS_DRAGON, MONS_QUICKSILVER_DRAGON, MH_NATURAL, -7, { 45, 0, 0, 0 }, { 16, 3, 5, 0 }, 10, 15, 15, 7, MST_QUICKSILVER_DRAGON, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, @@ -3506,8 +4033,9 @@ { MONS_IRON_DRAGON, 'D', CYAN, "iron dragon", - M_SPELLCASTER | M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_SEE_INVIS, - 0, 14, MONS_IRON_DRAGON, MH_NATURAL, -7, + M_SPELLCASTER | M_SEE_INVIS, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD, + 0, 14, MONS_DRAGON, MONS_IRON_DRAGON, MH_NATURAL, -7, { 25, 25, 25, 0 }, { 18, 5, 3, 0 }, 20, 6, 8, 7, MST_IRON_DRAGON, CE_CONTAMINATED, Z_SMALL, S_ROAR, I_HIGH, @@ -3517,8 +4045,9 @@ { MONS_SKELETAL_WARRIOR, 'z', CYAN, "skeletal warrior", - M_SPELLCASTER | M_RES_POISON | M_RES_COLD | M_ACTUAL_SPELLS, - 0, 10, MONS_SKELETAL_WARRIOR, MH_UNDEAD, -7, + M_SPELLCASTER | M_ACTUAL_SPELLS | M_EVIL, + MR_RES_POISON | MR_RES_COLD, + 0, 10, MONS_SKELETAL_WARRIOR, MONS_SKELETAL_WARRIOR, MH_UNDEAD, -7, { 25, 0, 0, 0 }, { 10, 5, 3, 0 }, 15, 10, 10, 7, MST_SKELETAL_WARRIOR, CE_CONTAMINATED, Z_SMALL, S_SILENT, I_NORMAL, @@ -3530,8 +4059,9 @@ /* player ghost - only one per level. stats are stored in ghost struct */ { MONS_PLAYER_GHOST, 'p', DARKGREY, "", - M_RES_POISON | M_SPEAKS | M_SPELLCASTER | M_ACTUAL_SPELLS | M_FLIES, - 0, 15, MONS_PLAYER_GHOST, MH_UNDEAD, -5, + M_SPEAKS | M_SPELLCASTER | M_ACTUAL_SPELLS | M_FLIES | M_UNIQUE, + MR_RES_POISON, + 0, 15, MONS_PHANTOM, MONS_PLAYER_GHOST, MH_UNDEAD, -5, { 5, 0, 0, 0 }, { 4, 2, 3, 0 }, 1, 2, 10, 7, MST_GHOST, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_HIGH, @@ -3542,8 +4072,9 @@ /* random demon in pan - only one per level. stats are stored in ghost struct */ { MONS_PANDEMONIUM_DEMON, '&', BLACK, "&", - M_SPELLCASTER | M_RES_POISON | M_SPEAKS, - 0, 14, MONS_PANDEMONIUM_DEMON, MH_DEMONIC, -5, + M_SPELLCASTER | M_SPEAKS | M_EVIL | M_UNIQUE, + MR_RES_POISON, + 0, 14, MONS_PANDEMONIUM_DEMON, MONS_PANDEMONIUM_DEMON, MH_DEMONIC, -5, { 5, 0, 0, 0 }, { 4, 2, 3, 0 }, 1, 2, 10, 7, MST_GHOST, CE_CONTAMINATED, Z_NOZOMBIE, S_RANDOM, I_HIGH, @@ -3554,8 +4085,9 @@ // begin lava monsters {dlb} { MONS_LAVA_WORM, 'w', RED, "lava worm", - M_RES_FIRE | M_ED_COLD, - 0, 10, MONS_LAVA_WORM, MH_NATURAL, -3, + M_NO_FLAGS, + MR_RES_FIRE | MR_VUL_COLD, + 0, 10, MONS_LAVA_WORM, MONS_LAVA_WORM, MH_NATURAL, -3, { 15, 0, 0, 0 }, { 6, 3, 5, 0 }, 1, 10, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, I_ANIMAL_LIKE, @@ -3565,8 +4097,9 @@ { MONS_LAVA_FISH, ';', RED, "lava fish", - M_RES_FIRE | M_ED_COLD, - 0, 10, MONS_LAVA_FISH, MH_NATURAL, -3, + M_NO_FLAGS, + MR_RES_FIRE | MR_VUL_COLD, + 0, 10, MONS_BIG_FISH, MONS_LAVA_FISH, MH_NATURAL, -3, { 10, 0, 0, 0 }, { 4, 3, 5, 0 }, 4, 15, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, I_ANIMAL_LIKE, @@ -3576,8 +4109,9 @@ { MONS_LAVA_SNAKE, 'S', RED, "lava snake", - M_RES_FIRE | M_ED_COLD, - 0, 10, MONS_LAVA_SNAKE, MH_NATURAL, -3, + M_SPECIAL_ABILITY, + MR_RES_FIRE | MR_VUL_COLD, + 0, 10, MONS_SNAKE, MONS_LAVA_SNAKE, MH_NATURAL, -3, { 7, 0, 0, 0 }, { 3, 3, 5, 0 }, 2, 17, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_HISS, I_ANIMAL_LIKE, @@ -3587,8 +4121,9 @@ { // mv: was another lava thing MONS_SALAMANDER, 'S', LIGHTRED, "salamander", - M_RES_FIRE | M_ED_COLD | M_WARM_BLOOD, - 0, 10, MONS_SALAMANDER, MH_NATURAL, -3, + M_WARM_BLOOD, + MR_RES_FIRE | MR_VUL_COLD, + 0, 10, MONS_SALAMANDER, MONS_SALAMANDER, MH_NATURAL, -3, { 23, 0, 0, 0 }, { 14, 3, 5, 0 }, 5, 5, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, I_HIGH, @@ -3602,7 +4137,8 @@ { MONS_BIG_FISH, ';', LIGHTGREEN, "big fish", M_COLD_BLOOD, - 0, 10, MONS_BIG_FISH, MH_NATURAL, -3, + MR_NO_FLAGS, + 0, 10, MONS_BIG_FISH, MONS_BIG_FISH, MH_NATURAL, -3, { 8, 0, 0, 0 }, { 4, 3, 5, 0 }, 1, 12, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, I_ANIMAL_LIKE, @@ -3613,7 +4149,8 @@ { MONS_GIANT_GOLDFISH, ';', LIGHTRED, "giant goldfish", M_COLD_BLOOD, - 0, 10, MONS_GIANT_GOLDFISH, MH_NATURAL, -3, + MR_NO_FLAGS, + 0, 10, MONS_BIG_FISH, MONS_GIANT_GOLDFISH, MH_NATURAL, -3, { 15, 0, 0, 0 }, { 7, 3, 5, 0 }, 5, 7, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, I_ANIMAL_LIKE, @@ -3623,8 +4160,9 @@ { MONS_ELECTRICAL_EEL, ';', LIGHTBLUE, "electrical eel", - M_RES_ELEC | M_COLD_BLOOD, - 0, 10, MONS_ELECTRICAL_EEL, MH_NATURAL, -3, + M_COLD_BLOOD | M_SPECIAL_ABILITY, + MR_RES_ELEC, + 0, 10, MONS_ELECTRICAL_EEL, MONS_ELECTRICAL_EEL, MH_NATURAL, -3, { 0, 0, 0, 0 }, { 3, 3, 5, 0 }, 1, 15, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, I_ANIMAL_LIKE, @@ -3634,8 +4172,9 @@ { MONS_JELLYFISH, 'J', CYAN, "jellyfish", - M_RES_POISON, - 0, 10, MONS_JELLYFISH, MH_NATURAL, -3, + M_NO_FLAGS, + MR_RES_POISON, + 0, 10, MONS_JELLYFISH, MONS_JELLYFISH, MH_NATURAL, -3, { 1, 1, 0, 0 }, { 4, 3, 5, 0 }, 0, 5, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, I_PLANT, @@ -3645,8 +4184,9 @@ { MONS_WATER_ELEMENTAL, '{', LIGHTBLUE, "water elemental", - M_RES_POISON | M_ED_FIRE | M_FLIES | M_RES_ELEC, - 0, 10, MONS_WATER_ELEMENTAL, MH_NONLIVING, 5000, + M_FLIES, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_ELEC, + 0, 10, MONS_EARTH_ELEMENTAL, MONS_WATER_ELEMENTAL, MH_NONLIVING, MAG_IMMUNE, { 25, 0, 0, 0 }, { 6, 5, 3, 0 }, 0, 7, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3657,7 +4197,8 @@ { MONS_SWAMP_WORM, 'w', BROWN, "swamp worm", M_AMPHIBIOUS, - 0, 10, MONS_SWAMP_WORM, MH_NATURAL, -3, + MR_NO_FLAGS, + 0, 10, MONS_WORM, MONS_SWAMP_WORM, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 5, 5, 5, 0 }, 3, 12, 12, 0, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, I_PLANT, @@ -3679,8 +4220,9 @@ not think it fits into Crawl ... {dlb} #if 0 { MONS_SHUGGOTH, 'A', LIGHTGREEN, "shuggoth", - M_NO_SKELETON | M_RES_ELEC | M_RES_POISON | M_RES_FIRE | M_RES_COLD | M_SEE_INVIS, - 1000, 10, MONS_SHUGGOTH, MH_DEMONIC, 300, + M_NO_SKELETON | M_SEE_INVIS, + MR_RES_ELEC | MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD, + 1000, 10, MONS_SHUGGOTH, MONS_SHUGGOTH, MH_DEMONIC, 300, { 5, 5, 5, 0 }, { 10, 4, 4, 0 }, 10, 10, 20, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, -1, I_NORMAL, @@ -3692,7 +4234,8 @@ not think it fits into Crawl ... {dlb} { MONS_WOLF, 'h', LIGHTGREY, "wolf", M_WARM_BLOOD | M_SEE_INVIS, //jmf: until smell exists - 450, 10, MONS_WOLF, MH_NATURAL, -3, + MR_NO_FLAGS, + 450, 10, MONS_HOUND, MONS_WOLF, MH_NATURAL, -3, { 8, 2, 2, 0 }, { 4, 3, 5, 0 }, 3, 15, 17, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_BARK, I_ANIMAL, @@ -3702,8 +4245,9 @@ not think it fits into Crawl ... {dlb} { MONS_WARG, 'h', DARKGREY, "warg", - M_SEE_INVIS | M_RES_POISON | M_WARM_BLOOD, - 600, 12, MONS_WARG, MH_NATURAL, -6, + M_SEE_INVIS | M_WARM_BLOOD, + MR_RES_POISON, + 600, 12, MONS_HOUND, MONS_WARG, MH_NATURAL, -6, { 12, 3, 3, 0 }, { 4, 4, 5, 0 }, 4, 12, 13, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_BARK, I_ANIMAL, @@ -3714,7 +4258,8 @@ not think it fits into Crawl ... {dlb} { MONS_BEAR, 'U', BROWN, "bear", M_WARM_BLOOD, - 2000, 10, MONS_BEAR, MH_NATURAL, -3, + MR_NO_FLAGS, + 2000, 10, MONS_BEAR, MONS_BEAR, MH_NATURAL, -3, { 10, 6, 6, 0 }, { 7, 3, 3, 0 }, 4, 4, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_GROWL, I_ANIMAL, @@ -3725,7 +4270,8 @@ not think it fits into Crawl ... {dlb} { MONS_GRIZZLY_BEAR, 'U', LIGHTGREY, "grizzly bear", M_WARM_BLOOD, - 2500, 10, MONS_GRIZZLY_BEAR, MH_NATURAL, -3, + MR_NO_FLAGS, + 2500, 10, MONS_BEAR, MONS_GRIZZLY_BEAR, MH_NATURAL, -3, { 12, 8, 8, 0 }, { 7, 4, 4, 0 }, 5, 8, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_GROWL, I_ANIMAL, @@ -3735,8 +4281,9 @@ not think it fits into Crawl ... {dlb} { MONS_POLAR_BEAR, 'U', WHITE, "polar bear", - M_RES_COLD | M_WARM_BLOOD | M_AMPHIBIOUS, - 2500, 10, MONS_POLAR_BEAR, MH_NATURAL, -3, + M_WARM_BLOOD | M_AMPHIBIOUS, + MR_RES_COLD, + 2500, 10, MONS_BEAR, MONS_POLAR_BEAR, MH_NATURAL, -3, { 20, 5, 5, 0 }, //jmf: polar bears have very strong jaws & necks { 7, 5, 3, 0 }, 7, 8, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_BIG, S_GROWL, I_ANIMAL, @@ -3747,7 +4294,8 @@ not think it fits into Crawl ... {dlb} { MONS_BLACK_BEAR, 'U', DARKGREY, "black bear", M_WARM_BLOOD, - 1800, 10, MONS_BLACK_BEAR, MH_NATURAL, -3, + MR_NO_FLAGS, + 1800, 10, MONS_BEAR, MONS_BLACK_BEAR, MH_NATURAL, -3, { 4, 4, 4, 0 }, { 6, 3, 3, 0 }, 2, 8, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_GROWL, I_ANIMAL, @@ -3758,8 +4306,9 @@ not think it fits into Crawl ... {dlb} // small simulacrum { MONS_SIMULACRUM_SMALL, 'z', WHITE, "", - M_RES_POISON | M_ED_FIRE | M_RES_COLD, - 0, 6, MONS_SIMULACRUM_SMALL, MH_UNDEAD, -1, + M_EVIL, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD, + 0, 6, MONS_SIMULACRUM_SMALL, MONS_SIMULACRUM_SMALL, MH_UNDEAD, -1, { 6, 0, 0, 0 }, { 2, 3, 5, 0 }, 10, 4, 7, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3770,8 +4319,9 @@ not think it fits into Crawl ... {dlb} // large simulacrum { MONS_SIMULACRUM_LARGE, 'Z', WHITE, "", - M_RES_POISON | M_ED_FIRE | M_RES_COLD, - 0, 6, MONS_SIMULACRUM_LARGE, MH_UNDEAD, -1, + M_EVIL, + MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD, + 0, 6, MONS_SIMULACRUM_SMALL, MONS_SIMULACRUM_LARGE, MH_UNDEAD, -1, { 14, 0, 0, 0 }, { 5, 3, 5, 0 }, 10, 5, 7, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, @@ -3782,7 +4332,8 @@ not think it fits into Crawl ... {dlb} { MONS_GIANT_NEWT, 'l', LIGHTGREEN, "giant newt", M_COLD_BLOOD | M_AMPHIBIOUS, - 150, 10, MONS_GIANT_NEWT, MH_NATURAL, -3, + MR_NO_FLAGS, + 150, 10, MONS_GIANT_LIZARD, MONS_GIANT_NEWT, MH_NATURAL, -3, { 3, 0, 0, 0 }, { 1, 1, 2, 0 }, 0, 15, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_REPTILE, @@ -3793,7 +4344,8 @@ not think it fits into Crawl ... {dlb} { MONS_GIANT_GECKO, 'l', YELLOW, "giant gecko", M_COLD_BLOOD, - 250, 10, MONS_GIANT_GECKO, MH_NATURAL, -3, + MR_NO_FLAGS, + 250, 10, MONS_GIANT_LIZARD, MONS_GIANT_GECKO, MH_NATURAL, -3, { 5, 0, 0, 0 }, { 1, 3, 5, 0 }, 1, 14, 12, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_REPTILE, @@ -3804,7 +4356,8 @@ not think it fits into Crawl ... {dlb} { MONS_GIANT_IGUANA, 'l', BLUE, "giant iguana", M_COLD_BLOOD, - 400, 10, MONS_GIANT_IGUANA, MH_NATURAL, -3, + MR_NO_FLAGS, + 400, 10, MONS_GIANT_LIZARD, MONS_GIANT_IGUANA, MH_NATURAL, -3, { 15, 0, 0, 0 }, { 3, 3, 5, 0 }, 5, 9, 10, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_HISS, I_REPTILE, @@ -3816,7 +4369,8 @@ not think it fits into Crawl ... {dlb} // gila monsters colours: lightmagenta, magenta, lightred, red, yellow MONS_GILA_MONSTER, 'l', BLACK, "gila monster", M_COLD_BLOOD, - 500, 10, MONS_GILA_MONSTER, MH_NATURAL, -3, + MR_NO_FLAGS, + 500, 10, MONS_GIANT_LIZARD, MONS_GILA_MONSTER, MH_NATURAL, -3, { 20, 0, 0, 0 }, { 5, 4, 4, 0 }, 3, 12, 10, 7, MST_NO_SPELLS, CE_POISONOUS, Z_BIG, S_HISS, I_REPTILE, @@ -3825,9 +4379,10 @@ not think it fits into Crawl ... {dlb} , { - MONS_KOMODO_DRAGON, 'l', BROWN, "komodo dragon", + MONS_KOMODO_DRAGON, 'l', LIGHTRED, "komodo dragon", M_COLD_BLOOD | M_AMPHIBIOUS, - 800, 10, MONS_KOMODO_DRAGON, MH_NATURAL, -3, + MR_NO_FLAGS, + 800, 10, MONS_GIANT_LIZARD, MONS_KOMODO_DRAGON, MH_NATURAL, -3, { 30, 0, 0, 0 }, { 8, 3, 5, 0 }, 7, 8, 10, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_HISS, I_REPTILE, @@ -3835,4 +4390,28 @@ not think it fits into Crawl ... {dlb} } , +{ + MONS_ORANGE_STATUE, '8', LIGHTRED, "orange crystal statue", + M_SPECIAL_ABILITY, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_CLAY_GOLEM, MONS_ORANGE_STATUE, MH_NONLIVING, MAG_IMMUNE, + { 0, 0, 0, 0 }, + { 3, 50, 30, 120 }, + 30, 3, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, + MONUSE_NOTHING +} +, + +{ + MONS_SILVER_STATUE, '8', WHITE, "silver statue", + M_SPECIAL_ABILITY, + MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC, + 0, 10, MONS_CLAY_GOLEM, MONS_SILVER_STATUE, MH_NONLIVING, MAG_IMMUNE, + { 0, 0, 0, 0 }, + { 3, 50, 0, 120 }, + 30, 3, 10, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH, + MONUSE_NOTHING +} +, + #endif diff --git a/crawl-ref/source/mon-pick.cc b/crawl-ref/source/mon-pick.cc index f4a3446230..ee5dba276a 100644 --- a/crawl-ref/source/mon-pick.cc +++ b/crawl-ref/source/mon-pick.cc @@ -3,6 +3,8 @@ * Summary: Functions used to help determine which monsters should appear. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 08-Mar-2000 DLB enumeration & clean-up @@ -46,6 +48,8 @@ static int mons_tartarus_level(int mcls); static int mons_tartarus_rare(int mcls); static int mons_tomb_level(int mcls); static int mons_tomb_rare(int mcls); +static int mons_caverns_level(int mcls); +static int mons_caverns_rare(int mcls); /* ******************* BEGIN EXTERNAL FUNCTIONS ******************* */ int branch_depth(int branch) @@ -86,6 +90,28 @@ int branch_depth(int branch) } } // end branch_depth() +int branch_stair(int branch) +{ + switch (branch) + { + case BRANCH_ORCISH_MINES: return STAIRS_ORCISH_MINES; + case BRANCH_HIVE: return STAIRS_HIVE; + case BRANCH_LAIR: return STAIRS_LAIR; + case BRANCH_SLIME_PITS: return STAIRS_SLIME_PITS; + case BRANCH_VAULTS: return STAIRS_VAULTS; + case BRANCH_CRYPT: return STAIRS_CRYPT; + case BRANCH_HALL_OF_BLADES: return STAIRS_HALL_OF_BLADES; + case BRANCH_HALL_OF_ZOT: return STAIRS_HALL_OF_ZOT; + case BRANCH_ECUMENICAL_TEMPLE: return STAIRS_ECUMENICAL_TEMPLE; + case BRANCH_SNAKE_PIT: return STAIRS_SNAKE_PIT; + case BRANCH_ELVEN_HALLS: return STAIRS_ELVEN_HALLS; + case BRANCH_TOMB: return STAIRS_TOMB; + case BRANCH_SWAMP: return STAIRS_SWAMP; + default: + return -1; + } +} + // NB - When adding new branches or levels above 50, you must // change pre-game deletion routine in new_game in newgame.cc @@ -116,7 +142,8 @@ int mons_level(int mcls) (you.where_are_you == BRANCH_ELVEN_HALLS) ? mons_hallelf_level : (you.where_are_you == BRANCH_TOMB) ? mons_tomb_level : (you.where_are_you == BRANCH_SWAMP) ? mons_swamp_level : - (you.where_are_you == BRANCH_VAULTS) ? mons_standard_level + (you.where_are_you == BRANCH_VAULTS) ? mons_standard_level : + (you.where_are_you == BRANCH_CAVERNS) ? mons_caverns_level : mons_standard_level); monster_level = fnc_level(mcls); @@ -153,7 +180,8 @@ int mons_rarity(int mcls) (you.where_are_you == BRANCH_ELVEN_HALLS) ? mons_hallelf_rare : (you.where_are_you == BRANCH_TOMB) ? mons_tomb_rare : (you.where_are_you == BRANCH_SWAMP) ? mons_swamp_rare : - (you.where_are_you == BRANCH_VAULTS) ? mons_standard_rare + (you.where_are_you == BRANCH_VAULTS) ? mons_standard_rare : + (you.where_are_you == BRANCH_CAVERNS) ? mons_caverns_rare : mons_standard_rare); monster_rarity = fnc_rarity(mcls); @@ -188,6 +216,7 @@ bool mons_abyss(int mcls) case MONS_FLAYED_GHOST: case MONS_FLYING_SKULL: case MONS_FREEZING_WRAITH: + case MONS_DEATH_DRAKE: case MONS_FUNGUS: case MONS_GIANT_EYEBALL: case MONS_GIANT_ORANGE_BRAIN: @@ -539,11 +568,12 @@ static int mons_dis_level(int mcls) case MONS_FLAYED_GHOST: case MONS_FREEZING_WRAITH: + case MONS_DEATH_DRAKE: case MONS_HAIRY_DEVIL: case MONS_IRON_DEVIL: case MONS_VAMPIRE: case MONS_WRAITH: - mlev += 4; + mlev += 3; break; case MONS_BLUE_DEVIL: @@ -1092,6 +1122,7 @@ static int mons_tartarus_rare(int mcls) case MONS_IMP: case MONS_SHADOW_DRAGON: + case MONS_DEATH_DRAKE: return 20; case MONS_RED_DEVIL: @@ -2139,6 +2170,7 @@ static int mons_hallzot_level(int mcls) case MONS_SHADOW_DRAGON: case MONS_SKELETAL_DRAGON: case MONS_STORM_DRAGON: + case MONS_CURSE_TOE: mlev += 5; break; case MONS_DEATH_COB: @@ -2146,11 +2178,21 @@ static int mons_hallzot_level(int mcls) case MONS_ICE_DRAGON: mlev += 4; break; - case MONS_DEEP_ELF_ANNIHILATOR: - case MONS_DEEP_ELF_DEATH_MAGE: - case MONS_DEEP_ELF_DEMONOLOGIST: - case MONS_DEEP_ELF_HIGH_PRIEST: - case MONS_DEEP_ELF_SORCERER: + case MONS_MOTTLED_DRACONIAN: + case MONS_YELLOW_DRACONIAN: + case MONS_BLACK_DRACONIAN: + case MONS_WHITE_DRACONIAN: + case MONS_RED_DRACONIAN: + case MONS_PURPLE_DRACONIAN: + case MONS_PALE_DRACONIAN: + case MONS_GREEN_DRACONIAN: + case MONS_DRACONIAN_CALLER: + case MONS_DRACONIAN_MONK: + case MONS_DRACONIAN_SCORCHER: + case MONS_DRACONIAN_KNIGHT: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_ZEALOT: + case MONS_DRACONIAN_SHIFTER: case MONS_TENTACLED_MONSTROSITY: mlev += 3; break; @@ -2186,16 +2228,32 @@ static int mons_hallzot_rare(int mcls) case MONS_SKELETAL_DRAGON: return 40; case MONS_SHADOW_DRAGON: + case MONS_DEATH_DRAKE: return 30; - case MONS_DEEP_ELF_ANNIHILATOR: - case MONS_DEEP_ELF_DEATH_MAGE: - case MONS_DEEP_ELF_DEMONOLOGIST: - case MONS_DEEP_ELF_HIGH_PRIEST: - case MONS_DEEP_ELF_SORCERER: - return 29; case MONS_GUARDIAN_MUMMY: case MONS_ELECTRIC_GOLEM: + case MONS_CURSE_TOE: return 20; + + case MONS_MOTTLED_DRACONIAN: + case MONS_YELLOW_DRACONIAN: + case MONS_BLACK_DRACONIAN: + case MONS_WHITE_DRACONIAN: + case MONS_RED_DRACONIAN: + case MONS_PURPLE_DRACONIAN: + case MONS_PALE_DRACONIAN: + case MONS_GREEN_DRACONIAN: + return 18; + + case MONS_DRACONIAN_CALLER: + case MONS_DRACONIAN_MONK: + case MONS_DRACONIAN_SCORCHER: + case MONS_DRACONIAN_KNIGHT: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_ZEALOT: + case MONS_DRACONIAN_SHIFTER: + return 16; + case MONS_KILLER_KLOWN: case MONS_ORB_OF_FIRE: return 15; @@ -2204,6 +2262,68 @@ static int mons_hallzot_rare(int mcls) } } // end mons_hallzot_rare() +static int mons_caverns_level( int mcls ) +{ + + int mlev = you.branch_stairs[STAIRS_CAVERNS] + 1; + + switch (mcls) + { + case MONS_YELLOW_DRACONIAN: + case MONS_BLACK_DRACONIAN: + case MONS_WHITE_DRACONIAN: + case MONS_RED_DRACONIAN: + case MONS_PURPLE_DRACONIAN: + case MONS_PALE_DRACONIAN: + case MONS_GREEN_DRACONIAN: + case MONS_MOTTLED_DRACONIAN: + mlev++; + break; + + case MONS_DRACONIAN_CALLER: + case MONS_DRACONIAN_MONK: + case MONS_DRACONIAN_SCORCHER: + case MONS_DRACONIAN_KNIGHT: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_ZEALOT: + case MONS_DRACONIAN_SHIFTER: + mlev += 3; + break; + + default: + mlev += 99; + break; + } + + return (mlev); +} + +static int mons_caverns_rare( int mcls ) +{ + switch (mcls) + { + case MONS_YELLOW_DRACONIAN: + case MONS_BLACK_DRACONIAN: + case MONS_WHITE_DRACONIAN: + case MONS_RED_DRACONIAN: + case MONS_PURPLE_DRACONIAN: + case MONS_PALE_DRACONIAN: + case MONS_GREEN_DRACONIAN: + case MONS_MOTTLED_DRACONIAN: + case MONS_DRACONIAN_CALLER: + case MONS_DRACONIAN_MONK: + case MONS_DRACONIAN_SCORCHER: + case MONS_DRACONIAN_KNIGHT: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_ZEALOT: + case MONS_DRACONIAN_SHIFTER: + return (500); + + default: + return (0); + } +} + static int mons_standard_level(int mcls) { switch (mcls) @@ -2602,6 +2722,7 @@ static int mons_standard_rare(int mcls) case MONS_NECROPHAGE: case MONS_POTION_MIMIC: case MONS_SCROLL_MIMIC: + case MONS_QUASIT: case MONS_SKELETAL_WARRIOR: case MONS_SMALL_SNAKE: case MONS_SOUL_EATER: diff --git a/crawl-ref/source/mon-pick.h b/crawl-ref/source/mon-pick.h index 79312e6043..880efa2550 100644 --- a/crawl-ref/source/mon-pick.h +++ b/crawl-ref/source/mon-pick.h @@ -41,18 +41,14 @@ bool mons_abyss(int mcls); int mons_rare_abyss(int mcls); -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: dungeon - spells3 - * *********************************************************************** */ -int branch_depth(int branch); - - // last updated 10jun2000 {dlb} /* *********************************************************************** * called from: levels - mon-pick * *********************************************************************** */ bool mons_pan(int mcls); +// [ds] Why in the name of all that's holy are these in mon-pick.cc? +int branch_depth(int branch); +int branch_stair(int branch); #endif diff --git a/crawl-ref/source/mon-spll.h b/crawl-ref/source/mon-spll.h index 4799b4e2db..650a5103ee 100644 --- a/crawl-ref/source/mon-spll.h +++ b/crawl-ref/source/mon-spll.h @@ -1,3 +1,7 @@ +/* + * Modified for Crawl Reference by $Author$ on $Date$ + */ + #ifndef MON_SPLL_H #define MON_SPLL_H @@ -355,7 +359,7 @@ MS_SUMMON_DEMON }, { MST_GREEN_DEATH, - MS_POISON_BLAST, + MS_POISON_ARROW, MS_POISON_BLAST, MS_NO_SPELL, MS_VENOM_BOLT, @@ -443,7 +447,7 @@ MS_NO_SPELL }, { MST_GLOORX_VLOQ, - MS_POISON_BLAST, + MS_POISON_ARROW, MS_SLOW, MS_SUMMON_DEMON, MS_NEGATIVE_BOLT, @@ -519,7 +523,7 @@ MS_CRYSTAL_SPEAR, MS_BLINK, MS_IRON_BOLT, - MS_POISON_BLAST, + MS_POISON_ARROW, MS_TELEPORT }, { MST_DEEP_ELF_SORCERER, @@ -558,7 +562,7 @@ MS_VENOM_BOLT, MS_ORB_ENERGY, MS_HASTE, - MS_VENOM_BOLT, + MS_POISON_ARROW, MS_TELEPORT_OTHER, MS_TELEPORT }, @@ -730,5 +734,54 @@ MS_NO_SPELL, MS_NO_SPELL }, + { MST_MYSTIC, + MS_BRAIN_FEED, + MS_SMITE, + MS_INVIS, + MS_CONFUSE, + MS_PARALYSIS, + MS_HEAL }, + + { MST_DEATH_DRAKE, + MS_MIASMA, + MS_MIASMA, + MS_NO_SPELL, + MS_MIASMA, + MS_MIASMA, + MS_NO_SPELL }, + + { MST_DRAC_SCORCHER, + MS_FIRE_BOLT, + MS_STICKY_FLAME, + MS_NO_SPELL, + MS_FIREBALL, + MS_HELLFIRE, + MS_HELLFIRE_BURST }, + + { MST_DRAC_CALLER, + MS_NO_SPELL, + MS_SUMMON_LIZARDS, + MS_SUMMON_LIZARDS, + MS_NO_SPELL, + MS_NO_SPELL, + MS_SUMMON_LIZARDS }, + + { MST_DRAC_SHIFTER, + MS_NO_SPELL, + MS_BLINK_OTHER, + MS_BLINK, + MS_NO_SPELL, + MS_BLINK_OTHER, + MS_CONTROLLED_BLINK }, + + // Curse toe menu should be kept full, because otherwise the toe spends + // too much time crawling around. + { MST_CURSE_TOE, + MS_SUMMON_UNDEAD, + MS_SUMMON_MUSHROOMS, // fungal theme + MS_SUMMON_MUSHROOMS, + MS_TORMENT, + MS_SUMMON_UNDEAD, + MS_TORMENT }, #endif diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 4df429aab7..f4ef56fb8d 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -30,6 +30,8 @@ #include "debug.h" #include "itemname.h" +#include "itemprop.h" +#include "monplace.h" #include "mstuff2.h" #include "player.h" #include "randart.h" @@ -42,6 +44,20 @@ static FixedVector < int, NUM_MONSTERS > mon_entry; // really important extern -- screen redraws suck w/o it {dlb} FixedVector < unsigned short, 1000 > mcolour; +enum habitat_type +{ + // Flying monsters will appear in all categories + HT_NORMAL, // Normal critters + HT_SHALLOW_WATER, // Union of normal + water + HT_DEEP_WATER, // Water critters + HT_LAVA, // Lava critters + + NUM_HABITATS +}; + +static bool initialized_randmons = false; +static std::vector monsters_by_habitat[NUM_HABITATS]; + static struct monsterentry mondata[] = { #include "mon-data.h" }; @@ -59,8 +75,8 @@ static const char *monster_spell_name[] = { "Throw Frost", "Paralysis", "Slow", - "Haste", - "Confuse", + "Haste", + "Confuse", "Venom Bolt", "Fire Bolt", "Cold Bolt", @@ -79,45 +95,153 @@ static const char *monster_spell_name[] = { "Orb Energy", "Brain Feed", "Level Summon", - "Fake Rakshasa Summon", + "Fake Rakshasa Summon", "Steam Ball", "Summon Demon", "Animate Dead", "Pain", - "Smite", + "Smite", "Sticky Flame", "Poison Blast", "Summon Demon Lesser", "Summon Ufetubus", - "Purple Blast", + "Purple Blast", "Summon Beast", "Energy Bolt", "Sting", "Iron Bolt", - "Stone Arrow", + "Stone Arrow", "Poison Splash", "Summon Undead", - "Mutation", + "Mutation", "Cantrip", "Disintegrate", "Marsh Gas", "Quicksilver Bolt", "Torment", "Hellfire", - "Metal Splinters", + "Metal Splinters", "Summon Demon Greater", "Banishment", + "Controlled Blink", + "Control Undead", + "Miasma", + "Summon Lizards", + "Blink Other", + "Dispel Undead", + "Hellfrost", + "Poison Arrow", + "Summon Small Mammals", + "Summon Mushrooms", }; #endif static int mons_exp_mod(int mclass); -static monsterentry *seekmonster(int *p_monsterid); +static monsterentry *seekmonster(int p_monsterid); // macro that saves some typing, nothing more -#define smc seekmonster(&mc) +#define smc seekmonster(mc) /* ******************** BEGIN PUBLIC FUNCTIONS ******************** */ -void mons_init(FixedVector < unsigned short, 1000 > &colour) + +habitat_type grid2habitat(int grid) +{ + switch (grid) + { + case DNGN_DEEP_WATER: + return (HT_DEEP_WATER); + case DNGN_SHALLOW_WATER: + return (HT_SHALLOW_WATER); + case DNGN_LAVA: + return (HT_LAVA); + default: + return (HT_NORMAL); + } +} + +int habitat2grid(habitat_type ht) +{ + switch (ht) + { + case HT_DEEP_WATER: + return (DNGN_DEEP_WATER); + case HT_SHALLOW_WATER: + return (DNGN_SHALLOW_WATER); + case HT_LAVA: + return (DNGN_LAVA); + case HT_NORMAL: + default: + return (DNGN_FLOOR); + } +} + +static void initialize_randmons() +{ + for (int i = 0; i < NUM_HABITATS; ++i) + { + int grid = habitat2grid( habitat_type(i) ); + + for (int m = 0; m < NUM_MONSTERS; ++m) + { + if (invalid_monster_class(m)) + continue; + if (monster_habitable_grid(m, grid)) + monsters_by_habitat[i].push_back(m); + } + } + initialized_randmons = true; +} + +monster_type random_monster_at_grid(int x, int y) +{ + return (random_monster_at_grid(grd[x][y])); +} + +monster_type random_monster_at_grid(int grid) +{ + if (!initialized_randmons) + initialize_randmons(); + + const habitat_type ht = grid2habitat(grid); + const std::vector &valid_mons = monsters_by_habitat[ht]; + ASSERT(!valid_mons.empty()); + return valid_mons.empty()? MONS_PROGRAM_BUG + : monster_type(valid_mons[ random2(valid_mons.size()) ]); +} + +monster_type get_monster_by_name(std::string name, bool exact) +{ + lowercase(name); + + monster_type mon = MONS_PROGRAM_BUG; + for (unsigned i = 0; i < sizeof(mondata) / sizeof(*mondata); ++i) + { + std::string candidate = mondata[i].name; + lowercase(candidate); + + const int mtype = mondata[i].mc; + + if (exact) + { + if (name == candidate) + return monster_type(mtype); + + continue; + } + + const std::string::size_type match = candidate.find(name); + if (match == std::string::npos) + continue; + + mon = monster_type(mtype); + // we prefer prefixes over partial matches + if (match == 0) + break; + } + return (mon); +} + +void init_monsters(FixedVector < unsigned short, 1000 > &colour) { unsigned int x; // must be unsigned to match size_t {dlb} @@ -143,13 +267,40 @@ void mons_init(FixedVector < unsigned short, 1000 > &colour) //return (monsterentry *) 0; // return value should not matter here {dlb} } // end mons_init() +unsigned long get_mons_class_resists(int mc) +{ + return (smc->resists); +} -int mons_flag(int mc, int bf) +unsigned long get_mons_resists(const monsters *mon) { - return ((smc->bitfields & bf) != 0); -} // end mons_flag() + unsigned long resists = get_mons_class_resists(mon->type); + if (mons_genus(mon->type) == MONS_DRACONIAN + && mon->type != MONS_DRACONIAN) + { + monster_type draco_species = draco_subspecies(mon); + if (draco_species != mon->type) + resists |= get_mons_class_resists(draco_species); + } + return (resists); +} + +unsigned long mons_resist(const monsters *mon, unsigned long flags) +{ + return (get_mons_resists(mon) & flags); +} + +bool mons_class_flag(int mc, int bf) +{ + const monsterentry *me = smc; + + if (!me) + return (false); -static int scan_mon_inv_randarts( struct monsters *mon, int ra_prop ) + return ((me->bitfields & bf) != 0); +} // end mons_class_flag() + +static int scan_mon_inv_randarts( const monsters *mon, int ra_prop ) { int ret = 0; @@ -181,15 +332,52 @@ static int scan_mon_inv_randarts( struct monsters *mon, int ra_prop ) return (ret); } +mon_holy_type mons_holiness(const monsters *mon) +{ + return (mons_class_holiness(mon->type)); +} -int mons_holiness(int mc) +mon_holy_type mons_class_holiness(int mc) { return (smc->holiness); } // end mons_holiness() +bool mons_class_is_stationary(int type) +{ + return (type == MONS_OKLOB_PLANT + || type == MONS_PLANT + || type == MONS_FUNGUS + || type == MONS_CURSE_SKULL + || mons_is_statue(type) + || mons_is_mimic(type)); +} + +bool mons_is_stationary(const monsters *mons) +{ + return (mons_class_is_stationary(mons->type)); +} + +bool invalid_monster(const monsters *mons) +{ + return (!mons || mons->type == -1); +} + +bool invalid_monster_class(int mclass) +{ + return (mclass < 0 + || mclass >= NUM_MONSTERS + || mon_entry[mclass] == -1 + || mon_entry[mclass] == MONS_PROGRAM_BUG); +} + +bool mons_is_statue(int mc) +{ + return (mc == MONS_ORANGE_STATUE || mc == MONS_SILVER_STATUE); +} + bool mons_is_mimic( int mc ) { - return (mons_charclass( mc ) == MONS_GOLD_MIMIC); + return (mons_species( mc ) == MONS_GOLD_MIMIC); } bool mons_is_demon( int mc ) @@ -197,7 +385,7 @@ bool mons_is_demon( int mc ) const int show_char = mons_char( mc ); // Not every demonic monster is a demon (ie hell hog, hell hound) - if (mons_holiness( mc ) == MH_DEMONIC + if (mons_class_holiness( mc ) == MH_DEMONIC && (isdigit( show_char ) || show_char == '&')) { return (true); @@ -248,18 +436,34 @@ int mons_weight(int mc) return (smc->weight); } // end mons_weight() - -int mons_corpse_thingy(int mc) +corpse_effect_type mons_corpse_effect(int mc) { return (smc->corpse_thingy); -} // end mons_corpse_thingy() +} // end mons_corpse_effect() + + +monster_type mons_species( int mc ) +{ + const monsterentry *me = seekmonster(mc); + return (me? me->species : MONS_PROGRAM_BUG); +} // end mons_species() +monster_type mons_genus( int mc ) +{ + return (smc->genus); +} -int mons_charclass(int mc) +monster_type draco_subspecies( const monsters *mon ) { - return (smc->charclass); -} // end mons_charclass() + ASSERT( mons_genus( mon->type ) == MONS_DRACONIAN ); + + monster_type ret = mons_species( mon->type ); + if (ret == MONS_DRACONIAN && mon->type != MONS_DRACONIAN) + ret = static_cast( mon->number ); + + return (ret); +} int mons_shouts(int mc) { @@ -273,22 +477,14 @@ int mons_shouts(int mc) bool mons_is_unique( int mc ) { - if (mc <= MONS_PROGRAM_BUG - || (mc >= MONS_NAGA_MAGE && mc <= MONS_ROYAL_JELLY) - || (mc >= MONS_ANCIENT_LICH - && (mc != MONS_PLAYER_GHOST && mc != MONS_PANDEMONIUM_DEMON))) - { - return (false); - } - - return (true); + return (mons_class_flag(mc, M_UNIQUE)); } char mons_see_invis( struct monsters *mon ) { if (mon->type == MONS_PLAYER_GHOST || mon->type == MONS_PANDEMONIUM_DEMON) return (ghost.values[ GVAL_SEE_INVIS ]); - else if (((seekmonster(&mon->type))->bitfields & M_SEE_INVIS) != 0) + else if (((seekmonster(mon->type))->bitfields & M_SEE_INVIS) != 0) return (1); else if (scan_mon_inv_randarts( mon, RAP_EYESIGHT ) > 0) return (1); @@ -339,11 +535,20 @@ char mons_itemuse(int mc) return (smc->gmon_use); } // end mons_itemuse() -unsigned char mons_colour(int mc) +int mons_class_colour(int mc) { - return (smc->colour); + const monsterentry *m = smc; + if (!m) + return (BLACK); + + const int class_colour = m->colour; + return (class_colour); } // end mons_colour() +int mons_colour(const monsters *monster) +{ + return (monster->colour); +} int mons_damage(int mc, int rt) { @@ -359,9 +564,9 @@ int mons_damage(int mc, int rt) return (smc->damage[rt]); } // end mons_damage() -int mons_resist_magic( struct monsters *mon ) +int mons_resist_magic( const monsters *mon ) { - int u = (seekmonster(&mon->type))->resist_magic; + int u = (seekmonster(mon->type))->resist_magic; // negative values get multiplied with mhd if (u < 0) @@ -384,7 +589,7 @@ int mons_resist_magic( struct monsters *mon ) // Returns true if the monster made its save against hostile // enchantments/some other magics. -bool check_mons_resist_magic( struct monsters *monster, int pow ) +bool check_mons_resist_magic( const monsters *monster, int pow ) { int mrs = mons_resist_magic(monster); @@ -422,7 +627,7 @@ bool check_mons_resist_magic( struct monsters *monster, int pow ) } // end check_mons_resist_magic() -int mons_res_elec( struct monsters *mon ) +int mons_res_elec( const monsters *mon ) { int mc = mon->type; @@ -430,11 +635,10 @@ int mons_res_elec( struct monsters *mon ) return (ghost.values[ GVAL_RES_ELEC ]); /* this is a variable, not a player_xx() function, so can be above 1 */ - int u = 0, f = (seekmonster(&mc))->bitfields; + int u = 0; - // of course it makes no sense setting them both :) - if (f & M_RES_ELEC) - u++; //if(f&M_ED_ELEC) u--; + if (mons_resist(mon, MR_RES_ELEC)) + u++; // don't bother checking equipment if the monster can't use it if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) @@ -453,17 +657,26 @@ int mons_res_elec( struct monsters *mon ) return (u); } // end mons_res_elec() +bool mons_res_asphyx( const monsters *mon ) +{ + const int holiness = mons_holiness( mon ); + return (holiness == MH_UNDEAD + || holiness == MH_DEMONIC + || holiness == MH_NONLIVING + || holiness == MH_PLANT + || mons_resist(mon, MR_RES_ASPHYX)); +} -int mons_res_poison( struct monsters *mon ) +int mons_res_poison( const monsters *mon ) { int mc = mon->type; - int u = 0, f = (seekmonster(&mc))->bitfields; + int u = 0, f = get_mons_resists(mon); - if (f & M_RES_POISON) + if (f & MR_RES_POISON) u++; - if (f & M_ED_POISON) + if (f & MR_VUL_POISON) u--; if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) @@ -491,25 +704,25 @@ int mons_res_poison( struct monsters *mon ) } // end mons_res_poison() -int mons_res_fire( struct monsters *mon ) +int mons_res_fire( const monsters *mon ) { int mc = mon->type; if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON) return (ghost.values[ GVAL_RES_FIRE ]); - int u = 0, f = (seekmonster(&mc))->bitfields; + int u = 0, f = get_mons_resists(mon); // no Big Prize (tm) here either if you set all three flags. It's a pity uh? // // Note that natural monster resistance is two levels, this is duplicate // the fact that having this flag used to be a lot better than armour // for monsters (it used to make them immune in a lot of cases) -- bwr - if (f & M_RES_HELLFIRE) + if (f & MR_RES_HELLFIRE) u += 3; - else if (f & M_RES_FIRE) + else if (f & MR_RES_FIRE) u += 2; - else if (f & M_ED_FIRE) + else if (f & MR_VUL_FIRE) u--; if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) @@ -539,21 +752,21 @@ int mons_res_fire( struct monsters *mon ) } // end mons_res_fire() -int mons_res_cold( struct monsters *mon ) +int mons_res_cold( const monsters *mon ) { int mc = mon->type; if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON) return (ghost.values[ GVAL_RES_COLD ]); - int u = 0, f = (seekmonster(&mc))->bitfields; + int u = 0, f = get_mons_resists(mon); // Note that natural monster resistance is two levels, this is duplicate // the fact that having this flag used to be a lot better than armour // for monsters (it used to make them immune in a lot of cases) -- bwr - if (f & M_RES_COLD) + if (f & MR_RES_COLD) u += 2; - else if (f & M_ED_COLD) + else if (f & MR_VUL_COLD) u--; if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) @@ -582,15 +795,16 @@ int mons_res_cold( struct monsters *mon ) return (u); } // end mons_res_cold() -int mons_res_negative_energy( struct monsters *mon ) +int mons_res_negative_energy( const monsters *mon ) { int mc = mon->type; - if (mons_holiness( mon->type ) == MH_UNDEAD - || mons_holiness( mon->type ) == MH_DEMONIC - || mons_holiness( mon->type ) == MH_NONLIVING - || mons_holiness( mon->type ) == MH_PLANT - || mon->type == MONS_SHADOW_DRAGON) + if (mons_holiness(mon) == MH_UNDEAD + || mons_holiness(mon) == MH_DEMONIC + || mons_holiness(mon) == MH_NONLIVING + || mons_holiness(mon) == MH_PLANT + || mon->type == MONS_SHADOW_DRAGON + || mon->type == MONS_DEATH_DRAKE) { return (3); // to match the value for players } @@ -613,6 +827,26 @@ int mons_res_negative_energy( struct monsters *mon ) return (u); } // end mons_res_negative_energy() +bool mons_is_evil( const monsters *mon ) +{ + return (mons_class_flag( mon->type, M_EVIL )); +} + +bool mons_is_unholy( const monsters *mon ) +{ + const mon_holy_type holy = mons_holiness( mon ); + + return (holy == MH_UNDEAD || holy == MH_DEMONIC); +} + +bool mons_has_lifeforce( const monsters *mon ) +{ + const int holy = mons_holiness( mon ); + + return (holy == MH_NATURAL || holy == MH_PLANT); + // && !mons_has_ench( mon, ENCH_PETRIFY )); +} + int mons_skeleton(int mc) { if (mons_zombie_size(mc) == 0 @@ -624,7 +858,7 @@ int mons_skeleton(int mc) return (1); } // end mons_skeleton() -char mons_class_flies(int mc) +int mons_class_flies(int mc) { if (mc == MONS_PANDEMONIUM_DEMON) return (ghost.values[ GVAL_DEMONLORD_FLY ]); @@ -640,10 +874,9 @@ char mons_class_flies(int mc) return (0); } -char mons_flies( struct monsters *mon ) +int mons_flies( const monsters *mon ) { - char ret = mons_class_flies( mon->type ); - + int ret = mons_class_flies( mon->type ); return (ret ? ret : (scan_mon_inv_randarts(mon, RAP_LEVITATE) > 0) ? 2 : 0); } // end mons_flies() @@ -666,7 +899,7 @@ int hit_points(int hit_dice, int min_hp, int rand_hp) // of monster, not a pacticular monsters current hit dice. -- bwr int mons_type_hit_dice( int type ) { - struct monsterentry *mon_class = seekmonster( &type ); + struct monsterentry *mon_class = seekmonster( type ); if (mon_class) return (mon_class->hpdice[0]); @@ -675,7 +908,7 @@ int mons_type_hit_dice( int type ) } -int exper_value( struct monsters *monster ) +int exper_value( const struct monsters *monster ) { long x_val = 0; @@ -690,10 +923,15 @@ int exper_value( struct monsters *monster ) const int item_usage = mons_itemuse(mclass); // XXX: shapeshifters can qualify here, even though they can't cast: - const bool spellcaster = mons_flag( mclass, M_SPELLCASTER ); + const bool spellcaster = mons_class_flag( mclass, M_SPELLCASTER ); // early out for no XP monsters - if (mons_flag(mclass, M_NO_EXP_GAIN)) + if (mons_class_flag(mclass, M_NO_EXP_GAIN)) + return (0); + + // no experience for destroying furniture, even if the furniture started + // the fight. + if (mons_is_statue(mclass)) return (0); // These undead take damage to maxhp, so we use only HD. -- bwr @@ -718,14 +956,7 @@ int exper_value( struct monsters *monster ) // Let's look for big spells: if (spellcaster) { - const int msecc = ((mclass == MONS_HELLION) ? MST_BURNING_DEVIL : - (mclass == MONS_PANDEMONIUM_DEMON) ? MST_GHOST - : monster->number); - - int hspell_pass[6] = { MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL, - MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL }; - - mons_spell_list( msecc, hspell_pass ); + const monster_spells &hspell_pass = monster->spells; for (int i = 0; i < 6; i++) { @@ -831,35 +1062,58 @@ int exper_value( struct monsters *monster ) return (x_val); } // end exper_value() +int obsolete_mons_spell_template_index(const monsters *mon) +{ + return (mons_class_flag(mon->type, M_SPELLCASTER)? + mon->number + : MST_NO_SPELLS); +} -// this needs to be rewritten a la the monsterseek rewrite {dlb}: -void mons_spell_list( unsigned char sec, int splist[6] ) +void mons_load_spells( monsters *mon, int book ) { - unsigned int x; + int x, y; - for (x = 0; x < NUM_MSTYPES; x++) + if (book == MST_NO_SPELLS) { - if (mspell_list[x][0] == sec) - break; + for (y = 0; y < NUM_MONSTER_SPELL_SLOTS; y++) + mon->spells[y] = MS_NO_SPELL; + return; } - if (x >= NUM_MSTYPES) - return; +#if DEBUG_DIAGNOSTICS + mprf( MSGCH_DIAGNOSTICS, "%s: loading spellbook #%d", + ptr_monam( mon, DESC_PLAIN ), book ); +#endif - // I *KNOW* this can easily be done in a loop - splist[0] = mspell_list[x][1]; // bolt spell - splist[1] = mspell_list[x][2]; // enchantment - splist[2] = mspell_list[x][3]; // self_ench - splist[3] = mspell_list[x][4]; // misc - splist[4] = mspell_list[x][5]; // misc2 - splist[5] = mspell_list[x][6]; // emergency + for (x = 0; x < 6; x++) + mon->spells[x] = MS_NO_SPELL; - if (sec == MST_GHOST) /* ghost */ + if (book == MST_GHOST) + { + for (y = 0; y < NUM_MONSTER_SPELL_SLOTS; y++) + { + mon->spells[y] = ghost.values[ GVAL_SPELL_1 + y ]; +#if DEBUG_DIAGNOSTICS + mprf( MSGCH_DIAGNOSTICS, "spell #%d: %d", y, mon->spells[y] ); +#endif + } + } + else { - for (x = 0; x < 6; x++) - splist[x] = ghost.values[ GVAL_SPELL_1 + x ]; + // this needs to be rewritten a la the monsterseek rewrite {dlb}: + for (x = 0; x < NUM_MSTYPES; x++) + { + if (mspell_list[x][0] == book) + break; + } + + if (x < NUM_MSTYPES) + { + for (y = 0; y < 6; y++) + mon->spells[y] = mspell_list[x][y + 1]; + } } -} // end mons_spell_list() +} #if DEBUG_DIAGNOSTICS const char *mons_spell_name( int spell ) @@ -872,152 +1126,200 @@ const char *mons_spell_name( int spell ) #endif // generate a shiny new and unscarred monster -void define_monster(int k) +void define_monster(int index) { + monsters &mons = menv[index]; + int temp_rand = 0; // probability determination {dlb} - int m2_class = menv[k].type; - int m2_HD, m2_hp, m2_hp_max, m2_AC, m2_ev, m2_speed; - int m2_sec = menv[k].number; - struct monsterentry *m = seekmonster(&m2_class); + int mcls = mons.type; + int hd, hp, hp_max, ac, ev, speed; + int monnumber = mons.number; + const monsterentry *m = seekmonster(mcls); + int col = mons_class_colour(mons.type); + int spells = MST_NO_SPELLS; - m2_HD = m->hpdice[0]; + hd = m->hpdice[0]; // misc - m2_AC = m->AC; - m2_ev = m->ev; + ac = m->AC; + ev = m->ev; - // speed - m2_speed = m->speed; + speed = m->speed; - // some monsters are randomized: - // did I get them all? // I don't think so {dlb} - if (mons_is_mimic( m2_class )) - m2_sec = get_mimic_colour( &menv[k] ); - else + switch (mcls) { - switch (m2_class) - { - case MONS_ABOMINATION_SMALL: - m2_HD = 4 + random2(4); - m2_AC = 3 + random2(7); - m2_ev = 7 + random2(6); - m2_speed = 7 + random2avg(9, 2); - - if (m2_sec == 250) - m2_sec = random_colour(); - break; + case MONS_ABOMINATION_SMALL: + hd = 4 + random2(4); + ac = 3 + random2(7); + ev = 7 + random2(6); + speed = 7 + random2avg(9, 2); + + if (monnumber == 250) + col = random_colour(); + break; - case MONS_ZOMBIE_SMALL: - m2_HD = (coinflip() ? 1 : 2); - break; + case MONS_ZOMBIE_SMALL: + hd = (coinflip() ? 1 : 2); + break; - case MONS_ZOMBIE_LARGE: - m2_HD = 3 + random2(5); - break; + case MONS_ZOMBIE_LARGE: + hd = 3 + random2(5); + break; - case MONS_ABOMINATION_LARGE: - m2_HD = 8 + random2(4); - m2_AC = 5 + random2avg(9, 2); - m2_ev = 3 + random2(5); - m2_speed = 6 + random2avg(7, 2); + case MONS_ABOMINATION_LARGE: + hd = 8 + random2(4); + ac = 5 + random2avg(9, 2); + ev = 3 + random2(5); + speed = 6 + random2avg(7, 2); - if (m2_sec == 250) - m2_sec = random_colour(); - break; + if (monnumber == 250) + col = random_colour(); + break; - case MONS_BEAST: - m2_HD = 4 + random2(4); - m2_AC = 2 + random2(5); - m2_ev = 7 + random2(5); - m2_speed = 8 + random2(5); - break; + case MONS_BEAST: + hd = 4 + random2(4); + ac = 2 + random2(5); + ev = 7 + random2(5); + speed = 8 + random2(5); + break; - case MONS_HYDRA: - m2_sec = 4 + random2(5); - break; + case MONS_HYDRA: + monnumber = random_range(4, 8); + break; - case MONS_DEEP_ELF_FIGHTER: - case MONS_DEEP_ELF_KNIGHT: - case MONS_DEEP_ELF_SOLDIER: - case MONS_ORC_WIZARD: - m2_sec = MST_ORC_WIZARD_I + random2(3); - break; + case MONS_DEEP_ELF_FIGHTER: + case MONS_DEEP_ELF_KNIGHT: + case MONS_DEEP_ELF_SOLDIER: + case MONS_ORC_WIZARD: + spells = MST_ORC_WIZARD_I + random2(3); + break; - case MONS_LICH: - case MONS_ANCIENT_LICH: - m2_sec = MST_LICH_I + random2(4); - break; + case MONS_LICH: + case MONS_ANCIENT_LICH: + spells = MST_LICH_I + random2(4); + break; - case MONS_HELL_KNIGHT: - m2_sec = (coinflip() ? MST_HELL_KNIGHT_I : MST_HELL_KNIGHT_II); - break; + case MONS_HELL_KNIGHT: + spells = (coinflip() ? MST_HELL_KNIGHT_I : MST_HELL_KNIGHT_II); + break; - case MONS_NECROMANCER: - m2_sec = (coinflip() ? MST_NECROMANCER_I : MST_NECROMANCER_II); - break; + case MONS_NECROMANCER: + spells = (coinflip() ? MST_NECROMANCER_I : MST_NECROMANCER_II); + break; - case MONS_WIZARD: - case MONS_OGRE_MAGE: - case MONS_EROLCHA: - case MONS_DEEP_ELF_MAGE: - m2_sec = MST_WIZARD_I + random2(5); - break; + case MONS_WIZARD: + case MONS_OGRE_MAGE: + case MONS_EROLCHA: + case MONS_DEEP_ELF_MAGE: + spells = MST_WIZARD_I + random2(5); + break; - case MONS_DEEP_ELF_CONJURER: - m2_sec = (coinflip()? MST_DEEP_ELF_CONJURER_I : MST_DEEP_ELF_CONJURER_II); - break; + case MONS_DEEP_ELF_CONJURER: + spells = + (coinflip()? MST_DEEP_ELF_CONJURER_I : MST_DEEP_ELF_CONJURER_II); + break; - case MONS_BUTTERFLY: - case MONS_SPATIAL_VORTEX: - case MONS_KILLER_KLOWN: - m2_sec = random_colour(); - break; + case MONS_BUTTERFLY: + case MONS_SPATIAL_VORTEX: + case MONS_KILLER_KLOWN: + col = random_colour(); + break; - case MONS_GILA_MONSTER: - temp_rand = random2(7); + case MONS_GILA_MONSTER: + temp_rand = random2(7); - m2_sec = (temp_rand >= 5 ? LIGHTRED : // 2/7 - temp_rand >= 3 ? LIGHTMAGENTA : // 2/7 - temp_rand == 2 ? RED : // 1/7 - temp_rand == 1 ? MAGENTA // 1/7 - : YELLOW); // 1/7 - break; + col = (temp_rand >= 5 ? LIGHTRED : // 2/7 + temp_rand >= 3 ? LIGHTMAGENTA : // 2/7 + temp_rand == 2 ? RED : // 1/7 + temp_rand == 1 ? MAGENTA // 1/7 + : YELLOW); // 1/7 + break; - case MONS_HUMAN: - case MONS_ELF: - // these are supposed to only be created by polymorph - m2_HD += random2(10); - m2_AC += random2(5); - m2_ev += random2(5); - break; + case MONS_DRACONIAN: + // these are supposed to only be created by polymorph + hd += random2(10); + ac += random2(5); + ev += random2(5); - default: - break; + if (hd >= 7) + { + mons.type = MONS_BLACK_DRACONIAN + random2(8); + col = mons_class_colour( mons.type ); } + break; + + case MONS_DRACONIAN_CALLER: + case MONS_DRACONIAN_MONK: + case MONS_DRACONIAN_ZEALOT: + case MONS_DRACONIAN_SHIFTER: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_SCORCHER: + { + // Professional draconians still have a base draconian type. + // White draconians will never be draconian scorchers, but + // apart from that, anything goes. + do + monnumber = MONS_BLACK_DRACONIAN + random2(8); + while (monnumber == MONS_WHITE_DRACONIAN + && mcls == MONS_DRACONIAN_SCORCHER); + break; + } + case MONS_DRACONIAN_KNIGHT: + { + temp_rand = random2(10); + // hell knight, death knight, chaos knight... + if (temp_rand < 6) + spells = (coinflip() ? MST_HELL_KNIGHT_I : MST_HELL_KNIGHT_II); + else if (temp_rand < 9) + spells = (coinflip() ? MST_NECROMANCER_I : MST_NECROMANCER_II); + else + spells = (coinflip() ? MST_DEEP_ELF_CONJURER_I + : MST_DEEP_ELF_CONJURER_II); + + monnumber = MONS_BLACK_DRACONIAN + random2(8); + break; } + case MONS_HUMAN: + case MONS_ELF: + // these are supposed to only be created by polymorph + hd += random2(10); + ac += random2(5); + ev += random2(5); + break; + + default: + if (mons_is_mimic( mcls )) + col = get_mimic_colour( &mons ); + break; + } + + if (spells == MST_NO_SPELLS && mons_class_flag(mons.type, M_SPELLCASTER)) + spells = m->sec; // some calculations - m2_hp = hit_points(m2_HD, m->hpdice[1], m->hpdice[2]); - m2_hp += m->hpdice[3]; - m2_hp_max = m2_hp; + hp = hit_points(hd, m->hpdice[1], m->hpdice[2]); + hp += m->hpdice[3]; + hp_max = hp; - if (m2_sec == 250) - m2_sec = m->sec; + if (monnumber == 250) + monnumber = m->sec; // so let it be written, so let it be done - menv[k].hit_dice = m2_HD; - menv[k].hit_points = m2_hp; - menv[k].max_hit_points = m2_hp_max; - menv[k].armour_class = m2_AC; - menv[k].evasion = m2_ev; - menv[k].speed = m2_speed; - menv[k].speed_increment = 70; - menv[k].number = m2_sec; - menv[k].flags = 0; + mons.hit_dice = hd; + mons.hit_points = hp; + mons.max_hit_points = hp_max; + mons.armour_class = ac; + mons.evasion = ev; + mons.speed = speed; + mons.speed_increment = 70; + mons.number = monnumber; + mons.flags = 0; + mons.colour = col; + mons_load_spells( &mons, spells ); // reset monster enchantments for (int i = 0; i < NUM_MON_ENCHANTS; i++) - menv[k].enchantment[i] = ENCH_NONE; + mons.enchantment[i] = ENCH_NONE; } // end define_monster() @@ -1048,7 +1350,7 @@ const char *monam( int mons_num, int mons, bool vis, char desc, int mons_wpn ) static char gmo_n[ ITEMNAME_SIZE ]; char gmo_n2[ ITEMNAME_SIZE ] = ""; - gmo_n[0] = '\0'; + gmo_n[0] = 0; // If you can't see the critter, let moname() print [Ii]t. if (!vis) @@ -1059,7 +1361,7 @@ const char *monam( int mons_num, int mons, bool vis, char desc, int mons_wpn ) // These need their description level handled here instead of // in monam(). - if (mons == MONS_SPECTRAL_THING) + if (mons == MONS_SPECTRAL_THING || mons_genus(mons) == MONS_DRACONIAN) { switch (desc) { @@ -1107,6 +1409,33 @@ const char *monam( int mons_num, int mons, bool vis, char desc, int mons_wpn ) strcat(gmo_n, gmo_n2); break; + case MONS_DRACONIAN_CALLER: + case MONS_DRACONIAN_MONK: + case MONS_DRACONIAN_ZEALOT: + case MONS_DRACONIAN_SHIFTER: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_KNIGHT: + case MONS_DRACONIAN_SCORCHER: + if (desc != DESC_PLAIN) + strcat( gmo_n, " " ); + + switch (mons_num) + { + default: break; + case MONS_BLACK_DRACONIAN: strcat(gmo_n, "black "); break; + case MONS_MOTTLED_DRACONIAN: strcat(gmo_n, "mottled "); break; + case MONS_YELLOW_DRACONIAN: strcat(gmo_n, "yellow "); break; + case MONS_GREEN_DRACONIAN: strcat(gmo_n, "green "); break; + case MONS_PURPLE_DRACONIAN: strcat(gmo_n, "purple "); break; + case MONS_RED_DRACONIAN: strcat(gmo_n, "red "); break; + case MONS_WHITE_DRACONIAN: strcat(gmo_n, "white "); break; + case MONS_PALE_DRACONIAN: strcat(gmo_n, "pale "); break; + } + + moname( mons, vis, DESC_PLAIN, gmo_n2 ); + strcat( gmo_n, gmo_n2 ); + break; + case MONS_DANCING_WEAPON: // safety check -- if we don't have/know the weapon use default name if (mons_wpn == NON_ITEM) @@ -1136,12 +1465,12 @@ const char *monam( int mons_num, int mons, bool vis, char desc, int mons_wpn ) return (gmo_n); } // end monam() -void moname(int mons_num, bool vis, char descrip, char glog[ ITEMNAME_SIZE ]) +const char *moname(int mons_num, bool vis, char descrip, char glog[ ITEMNAME_SIZE ]) { - glog[0] = '\0'; + glog[0] = 0; char gmon_name[ ITEMNAME_SIZE ] = ""; - strcpy( gmon_name, seekmonster( &mons_num )->name ); + strcpy( gmon_name, seekmonster( mons_num )->name ); if (!vis) { @@ -1159,7 +1488,7 @@ void moname(int mons_num, bool vis, char descrip, char glog[ ITEMNAME_SIZE ]) } strcpy(gmon_name, glog); - return; + return (glog); } if (!mons_is_unique( mons_num )) @@ -1203,19 +1532,19 @@ void moname(int mons_num, bool vis, char descrip, char glog[ ITEMNAME_SIZE ]) } strcat(glog, gmon_name); + + return (glog); } // end moname() /* ********************* END PUBLIC FUNCTIONS ********************* */ // see mons_init for initialization of mon_entry array. -static struct monsterentry *seekmonster(int *p_monsterid) +static monsterentry *seekmonster(int p_monsterid) { - ASSERT(p_monsterid != 0); - - int me = mon_entry[(*p_monsterid)]; + int me = p_monsterid != -1? mon_entry[p_monsterid] : -1; if (me >= 0) // PARANOIA - return (&mondata[mon_entry[(*p_monsterid)]]); + return (&mondata[me]); else return (NULL); } // end seekmonster() @@ -1261,7 +1590,7 @@ int mons_intel_type(int mc) //jmf: new, used by my spells int mons_power(int mc) { - // for now, just return monster hit dice. + // for now, just return monster hit dice. return (smc->hpdice[0]); } @@ -1292,79 +1621,67 @@ bool mons_aligned(int m1, int m2) return (fr1 == fr2); } -bool mons_friendly(struct monsters *m) +bool mons_friendly(const monsters *m) { return (m->attitude == ATT_FRIENDLY || mons_has_ench(m, ENCH_CHARM)); } -bool mons_is_stabbable(struct monsters *m) +bool mons_is_submerged( struct monsters *mon ) { - // Make sure oklob plants are never highlighted. That'll defeat the - // point of making them look like normal plants. - return (!mons_flag(m->type, M_NO_EXP_GAIN) - && m->type != MONS_OKLOB_PLANT - && !mons_friendly(m) - && m->behaviour == BEH_SLEEP); + // FIXME, switch to 4.1's MF_SUBMERGED system which is much cleaner. + return (mons_has_ench( mon, ENCH_SUBMERGED )); } -bool mons_maybe_stabbable(struct monsters *m) +bool mons_is_paralysed(const monsters *m) { - return (!mons_flag(m->type, M_NO_EXP_GAIN) - && m->type != MONS_OKLOB_PLANT - && !mons_friendly(m) - && ((m->foe != MHITYOU && !testbits(m->flags, MF_BATTY)) - || (mons_has_ench(m, ENCH_CONFUSION) && - !mons_flag(m->type, M_CONFUSED)) - || m->behaviour == BEH_FLEE)); + // maybe this should be 70 + return (m->speed_increment <= 60); } -/* ****************************************************************** - -// In the name of England, I declare this function wasteful! {dlb} - -static monsterentry *seekmonster( int mc ) +bool mons_is_confused(const monsters *m) { + return (mons_has_ench(m, ENCH_CONFUSION) && + !mons_class_flag(m->type, M_CONFUSED)); +} - ASSERT(mc >= 0); - - int x = 0; - - while (x < mondatasize) - { - if (mondata[x].mc == mc) - return &mondata[x]; - - x++; - } - - ASSERT(false); - - return seekmonster(MONS_PROGRAM_BUG); // see the disasters coming if there is no 250? - -} // end seekmonster() -****************************************************************** */ - - -/* ****************************************************************** - -// only used once, and internal to this file, to boot {dlb}: - -// These are easy to implement here. The difficult (dull!) work of converting -// the data structures is finally finished now! -inline char *mons_name( int mc ) +bool mons_is_fleeing(const monsters *m) { + return (m->behaviour == BEH_FLEE); +} - return smc->name; - -} // end mons_name() -****************************************************************** */ +bool mons_is_sleeping(const monsters *m) +{ + return (m->behaviour == BEH_SLEEP); +} -/***************************************************************** +bool mons_is_batty(const monsters *m) +{ + return testbits(m->flags, MF_BATTY); +} - Used to determine whether or not a monster should fire a beam (MUST be - called _after_ fire_tracer() for meaningful result. +bool mons_looks_stabbable(const monsters *m) +{ + // Make sure oklob plants are never highlighted. That'll defeat the + // point of making them look like normal plants. + return (!mons_class_flag(m->type, M_NO_EXP_GAIN) + && m->type != MONS_OKLOB_PLANT + && !mons_is_mimic(m->type) + && !mons_is_statue(m->type) + && !mons_friendly(m) + && mons_is_sleeping(m)); +} -*/ +bool mons_looks_distracted(const monsters *m) +{ + return (!mons_class_flag(m->type, M_NO_EXP_GAIN) + && m->type != MONS_OKLOB_PLANT + && !mons_is_mimic(m->type) + && !mons_is_statue(m->type) + && !mons_friendly(m) + && ((m->foe != MHITYOU && !mons_is_batty(m)) + || mons_is_confused(m) + || mons_is_fleeing(m))); +} bool mons_should_fire(struct bolt &beam) { @@ -1380,19 +1697,19 @@ bool mons_should_fire(struct bolt &beam) return (false); // if we either hit no friends, or monster too dumb to care - if (beam.fr_count == 0 || !beam.smartMonster) + if (beam.fr_count == 0 || !beam.smart_monster) return (true); // only fire if they do acceptably low collateral damage // the default for this is 50%; in other words, don't // hit a foe unless you hit 2 or fewer friends. - if (beam.foe_power >= (beam.foeRatio * beam.fr_power) / 100) + if (beam.foe_power >= (beam.foe_ratio * beam.fr_power) / 100) return (true); return (false); } -int mons_has_ench(struct monsters *mon, unsigned int ench, unsigned int ench2) +int mons_has_ench(const monsters *mon, unsigned int ench, unsigned int ench2) { // silliness if (ench == ENCH_NONE) @@ -1474,7 +1791,7 @@ int mons_del_ench( struct monsters *mon, unsigned int ench, unsigned int ench2, if (ench == ENCH_INVIS) { // invisible monsters stay invisible - if (mons_flag(mon->type, M_INVIS)) + if (mons_class_flag(mon->type, M_INVIS)) { mon->enchantment[p] = ENCH_INVIS; } @@ -1487,6 +1804,7 @@ int mons_del_ench( struct monsters *mon, unsigned int ench, unsigned int ench2, strcat( info, " appears!" ); mpr( info ); } + seen_monster(mon); } } @@ -1598,6 +1916,7 @@ bool ms_requires_tracer(int monspell) case MS_IRON_BOLT: case MS_LIGHTNING_BOLT: case MS_MARSH_GAS: + case MS_MIASMA: case MS_METAL_SPLINTERS: case MS_MMISSILE: case MS_NEGATIVE_BOLT: @@ -1605,6 +1924,7 @@ bool ms_requires_tracer(int monspell) case MS_PAIN: case MS_PARALYSIS: case MS_POISON_BLAST: + case MS_POISON_ARROW: case MS_POISON_SPLASH: case MS_QUICKSILVER_BOLT: case MS_SLOW: @@ -1637,6 +1957,7 @@ bool ms_requires_tracer(int monspell) case MS_SUMMON_UFETUBUS: case MS_TELEPORT: case MS_TORMENT: + case MS_SUMMON_SMALL_MAMMALS: case MS_VAMPIRE_SUMMON: case MS_CANTRIP: @@ -1677,6 +1998,7 @@ bool ms_direct_nasty(int monspell) case MS_SUMMON_DEMON_GREATER: case MS_SUMMON_UFETUBUS: case MS_TELEPORT: + case MS_SUMMON_SMALL_MAMMALS: case MS_VAMPIRE_SUMMON: nasty = false; break; @@ -1714,6 +2036,7 @@ bool ms_useful_fleeing_out_of_sight( struct monsters *mon, int monspell ) case MS_ANIMATE_DEAD: return (true); + case MS_SUMMON_SMALL_MAMMALS: case MS_VAMPIRE_SUMMON: case MS_SUMMON_UFETUBUS: case MS_FAKE_RAKSHASA_SUMMON: @@ -1722,6 +2045,7 @@ bool ms_useful_fleeing_out_of_sight( struct monsters *mon, int monspell ) case MS_SUMMON_DEMON_LESSER: case MS_SUMMON_BEAST: case MS_SUMMON_UNDEAD: + case MS_SUMMON_MUSHROOMS: case MS_SUMMON_DEMON_GREATER: if (one_chance_in(10)) // only summon friends some of the time return (true); @@ -1763,6 +2087,7 @@ bool ms_low_hitpoint_cast( struct monsters *mon, int monspell ) ret = true; break; + case MS_SUMMON_SMALL_MAMMALS: case MS_VAMPIRE_SUMMON: case MS_SUMMON_UFETUBUS: case MS_FAKE_RAKSHASA_SUMMON: @@ -1793,7 +2118,8 @@ bool ms_waste_of_time( struct monsters *mon, int monspell ) break; case MS_INVIS: - if (mons_has_ench( mon, ENCH_INVIS )) + if (mons_has_ench( mon, ENCH_INVIS ) || + (mons_friendly(mon) && !player_see_invis(false))) ret = true; break; @@ -1902,20 +2228,11 @@ bool mons_has_ranged_spell( struct monsters *mon ) { const int mclass = mon->type; - if (mons_flag( mclass, M_SPELLCASTER )) + if (mons_class_flag( mclass, M_SPELLCASTER )) { - const int msecc = ((mclass == MONS_HELLION) ? MST_BURNING_DEVIL : - (mclass == MONS_PANDEMONIUM_DEMON) ? MST_GHOST - : mon->number); - - int hspell_pass[6] = { MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL, - MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL }; - - mons_spell_list( msecc, hspell_pass ); - - for (int i = 0; i < 6; i++) + for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++) { - if (ms_ranged_spell( hspell_pass[i] )) + if (ms_ranged_spell( mon->spells[i] )) return (true); } } @@ -2004,3 +2321,98 @@ const char *mons_pronoun(int mon_type, int variant) return (""); } + +/* + * Checks if the monster can use smiting/torment to attack without unimpeded + * LOS to the player. + */ +static bool mons_can_smite(const monsters *monster) +{ + if (monster->type == MONS_FIEND) + return (true); + + const monster_spells &hspell_pass = monster->spells; + for (unsigned i = 0; i < hspell_pass.size(); ++i) + if (hspell_pass[i] == MS_TORMENT || hspell_pass[i] == MS_SMITE) + return (true); + + return (false); +} + +/* + * Determines if a monster is smart and pushy enough to displace other monsters. + * A shover should not cause damage to the shovee by displacing it, so monsters + * that trail clouds of badness are ineligible. The shover should also benefit + * from shoving, so monsters that can smite/torment are ineligible. + * + * (Smiters would be eligible for shoving when fleeing if the AI allowed for + * smart monsters to flee.) + */ +bool monster_shover(const monsters *m) +{ + const monsterentry *me = seekmonster(m->type); + if (!me) + return (false); + + // Efreet and fire elementals are disqualified because they leave behind + // clouds of flame. Rotting devils trail clouds of miasma. + if (m->type == MONS_EFREET || m->type == MONS_FIRE_ELEMENTAL + || m->type == MONS_ROTTING_DEVIL + || m->type == MONS_CURSE_TOE) + return (false); + + // Smiters profit from staying back and smiting. + if (mons_can_smite(m)) + return (false); + + int mchar = me->showchar; + // Somewhat arbitrary: giants and dragons are too big to get past anything, + // beetles are too dumb (arguable), dancing weapons can't communicate, eyes + // aren't pushers and shovers, zombies are zombies. Worms and elementals + // are on the list because all 'w' are currently unrelated. + return (mchar != 'C' && mchar != 'B' && mchar != '(' && mchar != 'D' + && mchar != 'G' && mchar != 'Z' && mchar != 'z' + && mchar != 'w' && mchar != '#'); +} + +// Returns true if m1 and m2 are related, and m1 is higher up the totem pole +// than m2. The criteria for being related are somewhat loose, as you can see +// below. +bool monster_senior(const monsters *m1, const monsters *m2) +{ + const monsterentry *me1 = seekmonster(m1->type), + *me2 = seekmonster(m2->type); + + if (!me1 || !me2) + return (false); + + int mchar1 = me1->showchar, + mchar2 = me2->showchar; + + // If both are demons, the smaller number is the nastier demon. + if (isdigit(mchar1) && isdigit(mchar2)) + return (mchar1 < mchar2); + + // &s are the evillest demons of all, well apart from Geryon, who really + // profits from *not* pushing past beasts. + if (mchar1 == '&' && isdigit(mchar2) && m1->type != MONS_GERYON) + return (m1->hit_dice > m2->hit_dice); + + // Skeletal warriors can push past zombies large and small. + if (m1->type == MONS_SKELETAL_WARRIOR && (mchar2 == 'z' || mchar2 == 'Z')) + return (m1->hit_dice > m2->hit_dice); + + if (m1->type == MONS_QUEEN_BEE + && (m2->type == MONS_KILLER_BEE + || m2->type == MONS_KILLER_BEE_LARVA)) + return (true); + + if (m1->type == MONS_KILLER_BEE && m2->type == MONS_KILLER_BEE_LARVA) + return (true); + + // Special-case gnolls so they can't get past (hob)goblins + if (m1->type == MONS_GNOLL && m2->type != MONS_GNOLL) + return (false); + + return (mchar1 == mchar2 && m1->hit_dice > m2->hit_dice); +} diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h index 9a1fc22688..3ad17c8f11 100644 --- a/crawl-ref/source/mon-util.h +++ b/crawl-ref/source/mon-util.h @@ -15,57 +15,10 @@ #define MONUTIL_H #include "externs.h" +#include "enum.h" // ($pellbinder) (c) D.G.S.E. 1998 -// ****remember***** must make an hardcopy of this sometime - -#if defined(macintosh) || defined(__IBMCPP__) || defined(SOLARIS) || defined(__BCPLUSPLUS__) -#define PACKED -#else -#ifndef PACKED -#define PACKED __attribute__ ((packed)) -#endif -#endif - -// leaves no skeleton? ("blob" monsters?) -// if weight=0 or zombie_size=0, this is always true -#define M_NO_FLAGS 0 // clear -#define M_NO_SKELETON (1<<0) -// resistances -#define M_RES_ELEC (1<<1) -#define M_RES_POISON (1<<2) -#define M_RES_FIRE (1<<3) -#define M_RES_HELLFIRE (1<<4) -#define M_RES_COLD (1<<5) -// invisible -#define M_INVIS (1<<6) // is created with invis enchantment set, and never runs out -// vulnerabilities -//#define M_ED_ELEC (1<<6) // never used -#define M_ED_POISON (1<<7) // ??? - - This flag is now (2.50) set for insects (LRH) -#define M_ED_FIRE (1<<8) -#define M_ED_COLD (1<<9) -#define M_SPELLCASTER (1<<10) // any non-physical-attack powers -#define M_FLIES (1<<11) // will crash to ground if paralysed (wings) -#define M_LEVITATE (1<<12) // not if this is set -#define M_SEE_INVIS (1<<13) -// killing this beast only gives 10 experience (makes sense for plants/fungi) -#define M_NO_EXP_GAIN (1<<14) // must do this manually -#define M_SPEAKS (1<<15) -//jmf: M_SPELLCASTER was taken ... :-b -#define M_ACTUAL_SPELLS (1<<16) // monster is a wizard -#define M_PRIEST (1<<17) // monster is a priest of Brian's Orc God (BOG) -#define M_COLD_BLOOD (1<<18) -#define M_WARM_BLOOD (1<<19) -#define M_CONFUSED (1<<20) // monster is perma-confused -#define M_SPLITS (1<<21) // monster is perma-confused -#define M_AMPHIBIOUS (1<<22) // monster can swim in water - -//jmf: it'd be nice if these next two were implimented ... -#define M_ON_FIRE (1<<29) // flag for Hellion-like colour shift -#define M_FROZEN (1<<30) // flag for ice-like colour shift - - // zombie size #define Z_NOZOMBIE 0 // no zombie (and no skeleton) #define Z_SMALL 1 @@ -111,7 +64,9 @@ struct monsterentry unsigned char showchar, colour; const char *name /*[32]*/; //longest is 23 till now (31 is max alowed here) - int bitfields; + unsigned long bitfields; + unsigned long resists; + short weight; // experience is calculated like this: // ((((max_hp / 7) + 1) * (mHD * mHD) + 1) * exp_mod) / 10 @@ -119,9 +74,10 @@ struct monsterentry // Note that this may make draining attacks less attractive (LRH) char exp_mod; - unsigned short charclass; // + monster_type genus, // "team" the monster plays for + species; // corpse type of the monster - char holiness; // -1=holy,0=normal,1=undead,2=very very evil + mon_holy_type holiness; // -1=holy,0=normal,1=undead,2=very very evil short resist_magic; // (positive is ??) // max damage in a turn is total of these four? @@ -146,7 +102,7 @@ struct monsterentry short sec; // not used (250) most o/t time // eating the corpse: 1=clean,2=might be contaminated,3=poison,4=very bad - char corpse_thingy; + corpse_effect_type corpse_thingy; // 0=no zombie, 1=small zombie (z) 107, 2=_BIG_ zombie (Z) 108 char zombie_size; // 0-12: see above, -1=random one of (0-7) @@ -165,7 +121,7 @@ struct monsterentry /* *********************************************************************** * called from: acr * *********************************************************************** */ -void mons_init( FixedVector& colour ); +void init_monsters( FixedVector& colour ); // last updated 12may2000 {dlb} /* *********************************************************************** @@ -184,8 +140,8 @@ const char *ptr_monam( struct monsters *mon, char desc ); /* *********************************************************************** * called from: beam - direct - fight - monstuff - mstuff2 - spells4 - view * *********************************************************************** */ -char mons_class_flies( int mc ); -char mons_flies( struct monsters *mon ); +int mons_class_flies( int mc ); +int mons_flies( const monsters *mon ); // last updated XXmay2000 {dlb} @@ -218,7 +174,7 @@ bool mons_is_unique(int mclass); * called from: describe - fight * *********************************************************************** */ // int exper_value(int mclass, int mHD, int maxhp); -int exper_value( struct monsters *monster ); +int exper_value( const struct monsters *monster ); // last updated 12may2000 {dlb} @@ -234,8 +190,8 @@ int mons_type_hit_dice( int type ); /* *********************************************************************** * called from: beam - effects * *********************************************************************** */ -int mons_resist_magic( struct monsters *mon ); -int mons_resist_turn_undead( struct monsters *mon ); +int mons_resist_magic( const monsters *mon ); +int mons_resist_turn_undead( const monsters *mon ); // last updated 12may2000 {dlb} @@ -245,25 +201,17 @@ int mons_resist_turn_undead( struct monsters *mon ); int mons_damage(int mc, int rt); -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: dungeon - fight - monstuff - spells4 - * *********************************************************************** */ -int mons_charclass(int mcls); - - // last updated 12may2000 {dlb} /* *********************************************************************** * called from: food - spells4 * *********************************************************************** */ -int mons_corpse_thingy(int mclass); - +corpse_effect_type mons_corpse_effect(int mc); // last updated 12may2000 {dlb} /* *********************************************************************** * called from: dungeon - fight - monstuff - mon-util * *********************************************************************** */ -int mons_flag(int mc, int bf); +bool mons_class_flag(int mc, int bf); // last updated 12may2000 {dlb} @@ -271,10 +219,12 @@ int mons_flag(int mc, int bf); * called from: beam - effects - fight - monstuff - mstuff2 - spells2 - * spells3 - spells4 * *********************************************************************** */ -int mons_holiness(int mclass); +mon_holy_type mons_class_holiness(int mclass); +mon_holy_type mons_holiness(const monsters *); bool mons_is_mimic( int mc ); -bool mons_is_demon( int mc ); +bool mons_is_statue(int mc); +bool mons_is_demon( int mc ); bool mons_is_humanoid( int mc ); @@ -296,30 +246,32 @@ int mons_intel_type(int mclass); //jmf: added 20mar2000 /* *********************************************************************** * called from: beam - fight - monstuff * *********************************************************************** */ -int mons_res_cold( struct monsters *mon ); +int mons_res_cold( const monsters *mon ); // last updated 12may2000 {dlb} /* *********************************************************************** * called from: beam - fight - spells4 * *********************************************************************** */ -int mons_res_elec( struct monsters *mon ); +int mons_res_elec( const monsters *mon ); // last updated 12may2000 {dlb} /* *********************************************************************** * called from: beam - fight - monstuff * *********************************************************************** */ -int mons_res_fire( struct monsters *mon ); +int mons_res_fire( const monsters *mon ); // last updated 12may2000 {dlb} /* *********************************************************************** * called from: beam - monstuff - spells4 * *********************************************************************** */ -int mons_res_poison( struct monsters *mon ); +int mons_res_poison( const monsters *mon ); -int mons_res_negative_energy( struct monsters *mon ); +int mons_res_negative_energy( const monsters *mon ); + +bool mons_res_asphyx( const monsters *mon ); // last updated 12may2000 {dlb} @@ -376,8 +328,13 @@ unsigned char mons_char(int mc); /* *********************************************************************** * called from: dungeon - fight - misc * *********************************************************************** */ -unsigned char mons_colour(int mc); +int mons_class_colour(int mc); +int mons_colour(const monsters *m); + +// Only for save-compatibility. +int obsolete_mons_spell_template_index(const monsters *mon); +void mons_load_spells( monsters *mon, int book ); // last updated 12may2000 {dlb} /* *********************************************************************** @@ -390,15 +347,9 @@ void define_monster(int mid); /* *********************************************************************** * called from: debug - itemname - mon-util * *********************************************************************** */ -void moname(int mcl, bool vis, char descrip, char glog[ ITEMNAME_SIZE ]); +const char *moname(int mcl, bool vis, char descrip, char glog[ ITEMNAME_SIZE ]); -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: monstuff - * *********************************************************************** */ -void mons_spell_list(unsigned char sec, int splist[6]); - #if DEBUG_DIAGNOSTICS // last updated 25sep2001 {dlb} /* *********************************************************************** @@ -449,10 +400,19 @@ bool mons_aligned(int m1, int m2); /* *********************************************************************** * called from: monstuff acr * *********************************************************************** */ -bool mons_friendly(struct monsters *m); +bool mons_friendly(const monsters *m); +bool mons_is_confused(const monsters *m); +bool mons_is_fleeing(const monsters *m); +bool mons_is_sleeping(const monsters *m); +bool mons_is_batty(const monsters *m); +bool mons_is_evil( const monsters *mon ); +bool mons_is_unholy( const monsters *mon ); +bool mons_has_lifeforce( const monsters *mon ); +monster_type mons_genus( int mc ); +monster_type mons_species( int mc ); -int mons_has_ench( struct monsters *mon, unsigned int ench, +int mons_has_ench( const monsters *mon, unsigned int ench, unsigned int ench2 = ENCH_NONE ); int mons_del_ench( struct monsters *mon, unsigned int ench, @@ -460,10 +420,28 @@ int mons_del_ench( struct monsters *mon, unsigned int ench, bool mons_add_ench( struct monsters *mon, unsigned int ench ); -bool mons_is_stabbable(struct monsters *m); +bool mons_looks_stabbable(const monsters *m); + +bool mons_looks_distracted(const monsters *m); + +bool check_mons_resist_magic( const monsters *monster, int pow ); + +bool mons_class_is_stationary(int monsclass); +bool mons_is_stationary(const monsters *mons); +bool mons_is_submerged( struct monsters *mon ); + +bool invalid_monster(const monsters *mons); +bool invalid_monster_class(int mclass); + +bool monster_shover(const monsters *m); +bool mons_is_paralysed(const monsters *m); + +bool monster_senior(const monsters *first, const monsters *second); +monster_type draco_subspecies( const monsters *mon ); -bool mons_maybe_stabbable(struct monsters *m); +monster_type random_monster_at_grid(int x, int y); +monster_type random_monster_at_grid(int grid); -bool check_mons_resist_magic( struct monsters *monster, int pow ); +monster_type get_monster_by_name(std::string name, bool exact = false); #endif diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc index eeab5c61d7..98bb07551d 100644 --- a/crawl-ref/source/monplace.cc +++ b/crawl-ref/source/monplace.cc @@ -3,6 +3,8 @@ * Summary: Functions used when placing monsters in the dungeon. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -20,6 +22,7 @@ #include "player.h" #include "stuff.h" #include "spells4.h" +#include "view.h" // NEW place_monster -- note that power should be set to: // 51 for abyss @@ -39,6 +42,97 @@ static int place_monster_aux(int mon_type, char behaviour, int target, int px, int py, int power, int extra, bool first_band_member, int dur = 0); +// Returns whether actual_grid is compatible with grid_wanted for monster +// movement (or for monster generation, if generation is true). +bool grid_compatible(int grid_wanted, int actual_grid, bool generation) +{ + // XXX What in Xom's name is DNGN_WATER_STUCK? It looks like an artificial + // device to slow down fiery monsters flying over water. + if (grid_wanted == DNGN_FLOOR) + return actual_grid >= DNGN_FLOOR + || (!generation + && actual_grid == DNGN_SHALLOW_WATER); + + return (grid_wanted == actual_grid + || (grid_wanted == DNGN_DEEP_WATER + && (actual_grid == DNGN_SHALLOW_WATER + || actual_grid == DNGN_BLUE_FOUNTAIN))); +} + +// Can this monster happily on actual_grid? +// +// If you have an actual monster, use this instead of the overloaded function +// that uses only the monster class to make decisions. +bool monster_habitable_grid(const monsters *m, int actual_grid) +{ + return (monster_habitable_grid(m->type, actual_grid, mons_flies(m))); +} + +// Can monsters of class monster_class live happily on actual_grid? Use flies +// == true to pretend the monster can fly. +// +// [dshaligram] We're trying to harmonise the checks from various places into +// one check, so we no longer care if a water elemental springs into existence +// on dry land, because they're supposed to be able to move onto dry land +// anyway. +bool monster_habitable_grid(int monster_class, int actual_grid, bool flies) +{ + const int preferred_habitat = monster_habitat(monster_class); + return (grid_compatible(preferred_habitat, actual_grid) + // [dshaligram] Flying creatures are all DNGN_FLOOR, so we + // only have to check for the additional valid grids of deep + // water and lava. + || ((flies || mons_class_flies(monster_class)) + && (actual_grid == DNGN_LAVA + || actual_grid == DNGN_DEEP_WATER)) + + // Amphibious critters are happy in the water. + || (mons_class_flag(monster_class, M_AMPHIBIOUS) + && grid_compatible(DNGN_DEEP_WATER, actual_grid)) + + // And water elementals are native to the water but happy on land + // as well. + || (monster_class == MONS_WATER_ELEMENTAL + && grid_compatible(DNGN_FLOOR, actual_grid))); +} + +// Returns true if the monster is floundering in water and susceptible to +// extra damage from water-natives. +bool monster_floundering(const monsters *m) +{ + const int grid = grd[m->x][m->y]; + return ((grid == DNGN_DEEP_WATER || grid == DNGN_SHALLOW_WATER) + // Can't use monster_habitable_grid because that'll return true + // for non-water monsters in shallow water. + && monster_habitat(m->type) != DNGN_DEEP_WATER + && !mons_class_flag(m->type, M_AMPHIBIOUS) + && !mons_flies(m)); +} + +// Returns true if the monster can submerge in the given grid +bool monster_can_submerge(int monster_class, int grid) +{ + switch (monster_class) + { + case MONS_BIG_FISH: + case MONS_GIANT_GOLDFISH: + case MONS_ELECTRICAL_EEL: + case MONS_JELLYFISH: + case MONS_WATER_ELEMENTAL: + case MONS_SWAMP_WORM: + return (grid == DNGN_DEEP_WATER || grid == DNGN_BLUE_FOUNTAIN); + + case MONS_LAVA_WORM: + case MONS_LAVA_FISH: + case MONS_LAVA_SNAKE: + case MONS_SALAMANDER: + return (grid == DNGN_LAVA); + + default: + return (false); + } +} + bool place_monster(int &id, int mon_type, int power, char behaviour, int target, bool summoned, int px, int py, bool allow_bands, int proximity, int extra, int dur) @@ -160,7 +254,7 @@ bool place_monster(int &id, int mon_type, int power, char behaviour, } // Monsters that can't move shouldn't be taking the stairs -- bwr - if (proximity == PROX_NEAR_STAIRS && mons_speed( mon_type ) == 0) + if (proximity == PROX_NEAR_STAIRS && mons_class_is_stationary( mon_type )) proximity = PROX_AWAY_FROM_PLAYER; // (4) for first monster, choose location. This is pretty intensive. @@ -205,18 +299,10 @@ bool place_monster(int &id, int mon_type, int power, char behaviour, continue; } - // compatible - floor? - if (grid_wanted == DNGN_FLOOR && grd[px][py] < DNGN_FLOOR) + // Is the monster happy where we want to put it? + if (!grid_compatible(grid_wanted, grd[px][py], true)) continue; - // compatible - others (must match, except for deep water monsters - // generated in shallow water) - if ((grid_wanted != DNGN_FLOOR && grd[px][py] != grid_wanted) - && (grid_wanted != DNGN_DEEP_WATER || grd[px][py] != DNGN_SHALLOW_WATER)) - { - continue; - } - // don't generate monsters on top of teleport traps // (how did they get there?) int trap = trap_at_xy(px, py); @@ -259,7 +345,7 @@ bool place_monster(int &id, int mon_type, int power, char behaviour, proxOK = false; break; } - // swap the monster and the player spots, unless the + // swap the monster and the player spots, unless the // monster was generated in lava or deep water. if (grd[px][py] == DNGN_LAVA || grd[px][py] == DNGN_DEEP_WATER) { @@ -290,17 +376,17 @@ bool place_monster(int &id, int mon_type, int power, char behaviour, } id = place_monster_aux( mon_type, behaviour, target, px, py, lev_mons, - extra, true ); + extra, true); - // now, forget about banding if the first placement failed, or there's too - // many monsters already, or we successfully placed by stairs + // now, forget about banding if the first placement failed, or there's too + // many monsters already, or we successfully placed by stairs if (id < 0 || id+30 > MAX_MONSTERS) return false; // message to player from stairwell/gate appearance? if (see_grid(px, py) && proximity == PROX_NEAR_STAIRS) { - info[0] = '\0'; + info[0] = 0; if (player_monster_visible( &menv[id] )) strcpy(info, ptr_monam( &menv[id], DESC_CAP_A )); @@ -335,7 +421,7 @@ bool place_monster(int &id, int mon_type, int power, char behaviour, for(i = 1; i < band_size; i++) { place_monster_aux( band_monsters[i], behaviour, target, px, py, - lev_mons, extra, false, dur ); + lev_mons, extra, false, dur); } // placement of first monster, at least, was a success. @@ -347,7 +433,7 @@ static int place_monster_aux( int mon_type, char behaviour, int target, bool first_band_member, int dur ) { int id, i; - char grid_wanted; + unsigned char grid_wanted; int fx=0, fy=0; // final x,y // gotta be able to pick an ID @@ -388,14 +474,7 @@ static int place_monster_aux( int mon_type, char behaviour, int target, if (mgrd[fx][fy] != NON_MONSTER) continue; - // compatible - floor? - if (grid_wanted == DNGN_FLOOR && grd[fx][fy] < DNGN_FLOOR) - continue; - - // compatible - others (must match, except for deep water monsters - // generated in shallow water) - if ((grid_wanted != DNGN_FLOOR && grd[fx][fy] != grid_wanted) - && (grid_wanted != DNGN_DEEP_WATER || grd[fx][fy] != DNGN_SHALLOW_WATER)) + if (!grid_compatible(grid_wanted, grd[fx][fy], true)) continue; // don't generate monsters on top of teleport traps @@ -449,10 +528,10 @@ static int place_monster_aux( int mon_type, char behaviour, int target, if (extra != 250) menv[id].number = extra; - if (mons_flag(mon_type, M_INVIS)) + if (mons_class_flag(mon_type, M_INVIS)) mons_add_ench(&menv[id], ENCH_INVIS); - if (mons_flag(mon_type, M_CONFUSED)) + if (mons_class_flag(mon_type, M_CONFUSED)) mons_add_ench(&menv[id], ENCH_CONFUSION); if (mon_type == MONS_SHAPESHIFTER) @@ -467,28 +546,10 @@ static int place_monster_aux( int mon_type, char behaviour, int target, menv[id].flags |= MF_BATTY; } - if ((mon_type == MONS_BIG_FISH - || mon_type == MONS_GIANT_GOLDFISH - || mon_type == MONS_ELECTRICAL_EEL - || mon_type == MONS_JELLYFISH - || mon_type == MONS_WATER_ELEMENTAL - || mon_type == MONS_SWAMP_WORM) - && grd[fx][fy] == DNGN_DEEP_WATER - && !one_chance_in(5)) - { + if (monster_can_submerge(mon_type, grd[fx][fy]) + && !one_chance_in(5)) mons_add_ench( &menv[id], ENCH_SUBMERGED ); - } - - if ((mon_type == MONS_LAVA_WORM - || mon_type == MONS_LAVA_FISH - || mon_type == MONS_LAVA_SNAKE - || mon_type == MONS_SALAMANDER) - && grd[fx][fy] == DNGN_LAVA - && !one_chance_in(5)) - { - mons_add_ench( &menv[id], ENCH_SUBMERGED ); - } - + menv[id].flags |= MF_JUST_SUMMONED; if (mon_type == MONS_DANCING_WEAPON && extra != 1) // ie not from spell @@ -496,7 +557,7 @@ static int place_monster_aux( int mon_type, char behaviour, int target, give_item( id, power ); if (menv[id].inv[MSLOT_WEAPON] != NON_ITEM) - menv[id].number = mitm[ menv[id].inv[MSLOT_WEAPON] ].colour; + menv[id].colour = mitm[ menv[id].inv[MSLOT_WEAPON] ].colour; } else if (mons_itemuse(mon_type) >= MONUSE_STARTING_EQUIPMENT) { @@ -518,11 +579,15 @@ static int place_monster_aux( int mon_type, char behaviour, int target, // set attitude, behaviour and target menv[id].attitude = ATT_HOSTILE; menv[id].behaviour = behaviour; + + if (mon_type == MONS_ORANGE_STATUE || mon_type == MONS_SILVER_STATUE) + menv[id].behaviour = BEH_WANDER; + menv[id].foe_memory = 0; // setting attitude will always make the // monster wander.. if you want sleeping - // hostiles, use BEH_SLEEP since the default + // hostiles, use BEH_SLEEP since the default // attitude is hostile. if (behaviour > NUM_BEHAVIOURS) { @@ -538,6 +603,11 @@ static int place_monster_aux( int mon_type, char behaviour, int target, menv[id].foe = target; + mark_interesting_monst(&menv[id], behaviour); + + if (player_monster_visible(&menv[id]) && mons_near(&menv[id])) + seen_monster(&menv[id]); + return (id); } // end place_monster_aux() @@ -771,6 +841,34 @@ static int choose_band( int mon_type, int power, int &band_size ) band = BAND_SKELETAL_WARRIORS; // skeletal warrior band_size = 2 + random2(3); break; + // Journey -- Added Draconian Packs + case MONS_WHITE_DRACONIAN: + case MONS_RED_DRACONIAN: + case MONS_PURPLE_DRACONIAN: + case MONS_MOTTLED_DRACONIAN: + case MONS_YELLOW_DRACONIAN: + case MONS_BLACK_DRACONIAN: + case MONS_GREEN_DRACONIAN: + case MONS_PALE_DRACONIAN: + if (power > 18 && one_chance_in(3)) + { + band = BAND_DRACONIAN; + band_size = random_range(2, 4); + } + break; + case MONS_DRACONIAN_CALLER: + case MONS_DRACONIAN_MONK: + case MONS_DRACONIAN_SCORCHER: + case MONS_DRACONIAN_KNIGHT: + case MONS_DRACONIAN_ANNIHILATOR: + case MONS_DRACONIAN_ZEALOT: + case MONS_DRACONIAN_SHIFTER: + if (power > 20) + { + band = BAND_DRACONIAN; + band_size = random_range(3, 6); + } + break; } // end switch if (band != BAND_NO_BAND && band_size == 0) @@ -1002,20 +1100,81 @@ static int band_member(int band, int power) case BAND_SKELETAL_WARRIORS: mon_type = MONS_SKELETAL_WARRIOR; break; + case BAND_DRACONIAN: + { + temp_rand = random2( (power < 24) ? 24 : 37 ); + mon_type = + ((temp_rand > 35) ? MONS_DRACONIAN_CALLER : // 1 in 34 + (temp_rand > 33) ? MONS_DRACONIAN_KNIGHT : // 2 in 34 + (temp_rand > 31) ? MONS_DRACONIAN_MONK : // 2 in 34 + (temp_rand > 29) ? MONS_DRACONIAN_SHIFTER : // 2 in 34 + (temp_rand > 27) ? MONS_DRACONIAN_ANNIHILATOR :// 2 in 34 + (temp_rand > 25) ? MONS_DRACONIAN_SCORCHER : // 2 in 34 + (temp_rand > 23) ? MONS_DRACONIAN_ZEALOT : // 2 in 34 + (temp_rand > 20) ? MONS_YELLOW_DRACONIAN : // 3 in 34 + (temp_rand > 17) ? MONS_GREEN_DRACONIAN : // 3 in 34 + (temp_rand > 14) ? MONS_BLACK_DRACONIAN : // 3 in 34 + (temp_rand > 11) ? MONS_WHITE_DRACONIAN : // 3 in 34 + (temp_rand > 8) ? MONS_PALE_DRACONIAN : // 3 in 34 + (temp_rand > 5) ? MONS_PURPLE_DRACONIAN : // 3 in 34 + (temp_rand > 2) ? MONS_MOTTLED_DRACONIAN : // 3 in 34 + MONS_RED_DRACONIAN ); // 3 in 34 + break; + } } return (mon_type); } +static int ood_limit() { + return Options.ood_interesting; +} + +void mark_interesting_monst(struct monsters* monster, char behaviour) +{ + bool interesting = false; + + // Unique monsters are always intersting + if ( mons_is_unique(monster->type) ) + interesting = true; + // If it's never going to attack us, then not interesting + else if (behaviour == BEH_FRIENDLY || behaviour == BEH_GOD_GIFT) + interesting = false; + // Don't waste time on moname() if user isn't using this option + else if ( Options.note_monsters.size() > 0 ) + { + char namebuf[ITEMNAME_SIZE]; + moname(monster->type, true, DESC_NOCAP_A, namebuf); + + std::string iname = namebuf; + + for (unsigned i = 0; i < Options.note_monsters.size(); ++i) { + if (Options.note_monsters[i].matches(iname)) { + interesting = true; + break; + } + } + } + else if ( you.where_are_you == BRANCH_MAIN_DUNGEON && + you.level_type == LEVEL_DUNGEON && + mons_level(monster->type) >= you.your_level + ood_limit() && + mons_level(monster->type) < 99 && + !(monster->type >= MONS_EARTH_ELEMENTAL && + monster->type <= MONS_AIR_ELEMENTAL) ) + interesting = true; + + if ( interesting ) + monster->flags |= MF_INTERESTING; +} + // PUBLIC FUNCTION -- mons_place(). int mons_place( int mon_type, char behaviour, int target, bool summoned, int px, int py, int level_type, int proximity, int extra, - int dur ) + int dur, bool permit_bands ) { int mon_count = 0; - int temp_rand; // probabilty determination {dlb} - bool permit_bands = false; + int temp_rand; // probability determination {dlb} for (int il = 0; il < MAX_MONSTERS; il++) { @@ -1051,7 +1210,9 @@ int mons_place( int mon_type, char behaviour, int target, bool summoned, : MONS_PIT_FIEND); // 5.07% } - if (mon_type == RANDOM_MONSTER || level_type == LEVEL_PANDEMONIUM) + if (permit_bands + || mon_type == RANDOM_MONSTER + || level_type == LEVEL_PANDEMONIUM) permit_bands = true; int mid = -1; @@ -1107,26 +1268,42 @@ int mons_place( int mon_type, char behaviour, int target, bool summoned, return (mid); } // end mons_place() - -int create_monster( int cls, int dur, int beha, int cr_x, int cr_y, - int hitting, int zsec ) +coord_def find_newmons_square(int mons_class, int x, int y) { - int summd = -1; FixedVector < char, 2 > empty; - - unsigned char spcw = ((cls == RANDOM_MONSTER) ? DNGN_FLOOR - : monster_habitat( cls )); + coord_def pos = { -1, -1 }; empty[0] = 0; empty[1] = 0; + if (mons_class == WANDERING_MONSTER) + mons_class = RANDOM_MONSTER; + + int spcw = ((mons_class == RANDOM_MONSTER)? DNGN_FLOOR + : monster_habitat( mons_class )); + // Might be better if we chose a space and tried to match the monster // to it in the case of RANDOM_MONSTER, that way if the target square // is surrounded by water of lava this function would work. -- bwr - if (empty_surrounds( cr_x, cr_y, spcw, true, empty )) + if (empty_surrounds( x, y, spcw, 2, true, empty )) { - summd = mons_place( cls, beha, hitting, true, empty[0], empty[1], - you.level_type, 0, zsec, dur ); + pos.x = empty[0]; + pos.y = empty[1]; + } + + return (pos); +} + +int create_monster( int cls, int dur, int beha, int cr_x, int cr_y, + int hitting, int zsec, bool permit_bands ) +{ + int summd = -1; + coord_def pos = find_newmons_square(cls, cr_x, cr_y); + + if (pos.x != -1 && pos.y != -1) + { + summd = mons_place( cls, beha, hitting, true, pos.x, pos.y, + you.level_type, 0, zsec, dur, permit_bands ); } // determine whether creating a monster is successful (summd != -1) {dlb}: @@ -1153,6 +1330,20 @@ int create_monster( int cls, int dur, int beha, int cr_x, int cr_y, if (beha == BEH_GOD_GIFT) creation->flags |= MF_GOD_GIFT; + + // get the drawbacks, not the benefits... + // (to prevent demon-scumming) + if ( (you.religion == GOD_ZIN || + you.religion == GOD_SHINING_ONE || + you.religion == GOD_ELYVILON) && + mons_is_unholy(creation) ) + { + creation->attitude = ATT_HOSTILE; + creation->behaviour = BEH_HOSTILE; + beha = BEH_HOSTILE; + if ( see_grid(cr_x, cr_y) ) + mpr("The monster is enraged by your holy aura!"); + } if (beha == BEH_CHARMED) { @@ -1175,25 +1366,19 @@ int create_monster( int cls, int dur, int beha, int cr_x, int cr_y, bool empty_surrounds(int emx, int emy, unsigned char spc_wanted, - bool allow_centre, FixedVector < char, 2 > &empty) + int radius, bool allow_centre, + FixedVector < char, 2 > &empty) { bool success; // assume all player summoning originates from player x,y bool playerSummon = (emx == you.x_pos && emy == you.y_pos); - int xpos[25]; // good x pos - int ypos[25]; // good y pos int good_count = 0; int count_x, count_y; - char minx = -2; - char maxx = 2; - char miny = -2; - char maxy = 2; - - for (count_x = minx; count_x <= maxx; count_x++) + for (count_x = -radius; count_x <= radius; count_x++) { - for (count_y = miny; count_y <= maxy; count_y++) + for (count_y = -radius; count_y <= radius; count_y++) { success = false; @@ -1216,31 +1401,18 @@ bool empty_surrounds(int emx, int emy, unsigned char spc_wanted, if (grd[tx][ty] == spc_wanted) success = true; - // those seeking ground can stand in shallow water or better - if (spc_wanted == DNGN_FLOOR && grd[tx][ty] >= DNGN_SHALLOW_WATER) + if (grid_compatible(spc_wanted, grd[tx][ty])) success = true; - // water monsters can be created in shallow as well as deep water - if (spc_wanted == DNGN_DEEP_WATER && grd[tx][ty] == DNGN_SHALLOW_WATER) - success = true; - - if (success) + if (success && one_chance_in(++good_count)) { // add point to list of good points - xpos[good_count] = tx; - ypos[good_count] = ty; - good_count ++; + empty[0] = tx; + empty[1] = ty; } } // end "for count_y" } // end "for count_x" - if (good_count > 0) - { - int pick = random2(good_count); - empty[0] = xpos[pick]; - empty[1] = ypos[pick]; - } - return (good_count > 0); } // end empty_surrounds() @@ -1304,3 +1476,53 @@ int summon_any_demon(char demon_class) return summoned; } // end summon_any_demon() + +monster_type rand_dragon( dragon_class_type type ) +{ + monster_type summoned = MONS_PROGRAM_BUG; + int temp_rand; + + switch (type) + { + case DRAGON_LIZARD: + temp_rand = random2(100); + summoned = ((temp_rand > 85) ? MONS_GIANT_GECKO : + (temp_rand > 59) ? MONS_GIANT_LIZARD : + (temp_rand > 34) ? MONS_GIANT_IGUANA : + (temp_rand > 22) ? MONS_GILA_MONSTER : + (temp_rand > 11) ? MONS_KOMODO_DRAGON : + (temp_rand > 8) ? MONS_FIREDRAKE : + (temp_rand > 2) ? MONS_SWAMP_DRAKE + : MONS_DEATH_DRAKE ); + break; + + case DRAGON_DRACONIAN: + temp_rand = random2(70); + summoned = ((temp_rand > 60) ? MONS_YELLOW_DRACONIAN : + (temp_rand > 50) ? MONS_BLACK_DRACONIAN : + (temp_rand > 40) ? MONS_PALE_DRACONIAN : + (temp_rand > 30) ? MONS_GREEN_DRACONIAN : + (temp_rand > 20) ? MONS_PURPLE_DRACONIAN : + (temp_rand > 10) ? MONS_RED_DRACONIAN + : MONS_WHITE_DRACONIAN); + break; + + case DRAGON_DRAGON: + temp_rand = random2(90); + summoned = ((temp_rand > 80) ? MONS_MOTTLED_DRAGON : + (temp_rand > 70) ? MONS_LINDWURM : + (temp_rand > 60) ? MONS_STORM_DRAGON : + (temp_rand > 50) ? MONS_MOTTLED_DRAGON : + (temp_rand > 40) ? MONS_STEAM_DRAGON : + (temp_rand > 30) ? MONS_DRAGON : + (temp_rand > 20) ? MONS_ICE_DRAGON : + (temp_rand > 10) ? MONS_SWAMP_DRAGON + : MONS_SHADOW_DRAGON); + break; + + default: + break; + } + + return (summoned); +} diff --git a/crawl-ref/source/monplace.h b/crawl-ref/source/monplace.h index f2fde75111..5dfb00e4b7 100644 --- a/crawl-ref/source/monplace.h +++ b/crawl-ref/source/monplace.h @@ -3,6 +3,8 @@ * Summary: Functions used when placing monsters in the dungeon. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -37,7 +39,8 @@ int mons_place( int mon_type, char behaviour, int target, bool summoned, int px, int py, int level_type = LEVEL_DUNGEON, int proximity = PROX_ANYWHERE, int extra = 250, - int dur = 0 ); + int dur = 0, + bool permit_bands = false ); // last updated 12may2000 {dlb} /* *********************************************************************** @@ -46,14 +49,14 @@ int mons_place( int mon_type, char behaviour, int target, bool summoned, * spells2 - spells3 - spells4 * *********************************************************************** */ int create_monster( int cls, int dur, int beha, int cr_x, int cr_y, - int hitting, int zsec ); + int hitting, int zsec, bool permit_bands = false ); // last updated 12may2000 {dlb} /* *********************************************************************** * called from: misc - monplace - spells3 * *********************************************************************** */ -bool empty_surrounds( int emx, int emy, unsigned char spc_wanted, +bool empty_surrounds( int emx, int emy, unsigned char spc_wanted, int radius, bool allow_centre, FixedVector& empty ); @@ -78,4 +81,19 @@ bool place_monster( int &id, int mon_type, int power, char behaviour, int proximity = PROX_ANYWHERE, int extra = 250, int dur = 0 ); +monster_type rand_dragon( dragon_class_type type ); + +/* *********************************************************************** + * called from: monplace monstuff + * *********************************************************************** */ +void mark_interesting_monst(struct monsters* monster, + char behaviour = BEH_SLEEP); + +bool grid_compatible(int grid_wanted, int actual_grid, bool generation = false); +bool monster_habitable_grid(int monster_class, int actual_grid, + bool flies = false); +bool monster_habitable_grid(const monsters *m, int actual_grid); +bool monster_floundering(const monsters *m); +bool monster_can_submerge(int monster_class, int grid); + #endif // MONPLACE_H diff --git a/crawl-ref/source/monspeak.cc b/crawl-ref/source/monspeak.cc index 09b348867d..c8899f0528 100644 --- a/crawl-ref/source/monspeak.cc +++ b/crawl-ref/source/monspeak.cc @@ -2,6 +2,8 @@ * File: monspeak.cc * Summary: Functions to handle speaking monsters * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> 01/09/00 BWR Created @@ -114,7 +116,7 @@ bool mons_speaks(struct monsters *monster) if (mons_has_ench(monster, ENCH_CONFUSION)) { - if (mons_holiness( monster->type ) == MH_DEMONIC + if (mons_holiness( monster ) == MH_DEMONIC && monster->type != MONS_IMP) { return (false); @@ -259,7 +261,7 @@ bool mons_speaks(struct monsters *monster) } else if (monster->behaviour == BEH_FLEE) { - if (mons_holiness( monster->type ) == MH_DEMONIC + if (mons_holiness( monster ) == MH_DEMONIC && monster->type != MONS_IMP) { return (false); @@ -378,7 +380,7 @@ bool mons_speaks(struct monsters *monster) } else if (mons_friendly(monster)) { - if (mons_holiness( monster->type ) == MH_DEMONIC + if (mons_holiness( monster ) == MH_DEMONIC && monster->type != MONS_IMP) { return (false); @@ -1289,7 +1291,7 @@ bool mons_speaks(struct monsters *monster) break; case 11: strcat(info, - " says, \"I love to fight, but killing is better.\""); + " says, \"I love to fight, but killing is better.\""); break; } break; // end Erica @@ -2115,7 +2117,7 @@ bool mons_speaks(struct monsters *monster) switch (random2(13)) { case 0: - strcat(info, " roars, \"DIE, PUNY ONE!\""); + strcat(info, " roars, \"DIE, PUNY ONE!\""); break; case 1: strcat(info, " growls, \"YOU BORE ME SO.\""); diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index 644462b798..df7f5184b5 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -3,6 +3,8 @@ * Summary: Misc monster related functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <8> 7 Aug 2001 MV Inteligent monsters now pick up gold @@ -36,11 +38,14 @@ #include "fight.h" #include "itemname.h" #include "items.h" +#include "itemprop.h" #include "misc.h" #include "monplace.h" #include "monspeak.h" +#include "mon-pick.h" #include "mon-util.h" #include "mstuff2.h" +#include "notes.h" #include "player.h" #include "randart.h" #include "religion.h" @@ -49,6 +54,7 @@ #include "spells4.h" #include "stuff.h" #include "view.h" +#include "stash.h" static bool handle_special_ability(struct monsters *monster, bolt & beem); static bool handle_pickup(struct monsters *monster); @@ -58,14 +64,17 @@ static void monster_move(struct monsters *monster); static bool plant_spit(struct monsters *monster, struct bolt &pbolt); static int map_wand_to_mspell(int wand_type); -char mmov_x, mmov_y; +// [dshaligram] Doesn't need to be extern. +static int mmov_x, mmov_y; static int compass_x[8] = { -1, 0, 1, 1, 1, 0, -1, -1 }; static int compass_y[8] = { -1, -1, -1, 0, 1, 1, 1, 0 }; +static bool immobile_monster[MAX_MONSTERS]; + #define FAR_AWAY 1000000 // used in monster_move() -// This function creates an arteficial item to represent a mimic's appearance. +// This function creates an artificial item to represent a mimic's appearance. // Eventually, mimics could be redone to be more like Dancing wepaons... // there'd only be one type and it would look like the item it carries. -- bwr void get_mimic_item( const struct monsters *mimic, item_def &item ) @@ -114,6 +123,11 @@ void get_mimic_item( const struct monsters *mimic, item_def &item ) case MONS_ARMOUR_MIMIC: item.base_type = OBJ_ARMOUR; item.sub_type = (59 * mimic->x + 79 * mimic->y) % NUM_ARMOURS; + // FIXME: Remove fudges once these armours are completely implemented. + if (item.sub_type == ARM_STUDDED_LEATHER_ARMOUR) + item.sub_type = ARM_CRYSTAL_PLATE_MAIL; + else if (item.sub_type == ARM_CAP) + item.sub_type = ARM_MOTTLED_DRAGON_ARMOUR; prop %= 100; @@ -238,9 +252,7 @@ static void monster_drop_ething(struct monsters *monster, bool destroyed = false; bool hostile_grid = false; - if (grd[monster->x][monster->y] == DNGN_LAVA || - grd[monster->x][monster->y] == DNGN_DEEP_WATER) - { + if ( grid_destroys_items(grd[monster->x][monster->y]) ) { hostile_grid = true; } @@ -268,18 +280,18 @@ static void monster_drop_ething(struct monsters *monster, } } - if (destroyed) - { - if (grd[monster->x][monster->y] == DNGN_LAVA) - mpr("You hear a hissing sound."); - else - mpr("You hear a splashing sound."); + if (destroyed) { + mprf(MSGCH_SOUND, + grid_item_destruction_message(grd[monster->x][monster->y])); } } // end monster_drop_ething() -static void place_monster_corpse(struct monsters *monster) +static void place_monster_corpse(const monsters *monster) { - int corpse_class = mons_charclass(monster->type); + int corpse_class = mons_species(monster->type); + + if (corpse_class == MONS_DRACONIAN) + corpse_class = draco_subspecies(monster); if (mons_has_ench(monster, ENCH_SHAPESHIFTER)) corpse_class = MONS_SHAPESHIFTER; @@ -287,8 +299,8 @@ static void place_monster_corpse(struct monsters *monster) corpse_class = MONS_GLOWING_SHAPESHIFTER; if (mons_weight(corpse_class) == 0 - || grd[monster->x][monster->y] == DNGN_LAVA - || grd[monster->x][monster->y] == DNGN_DEEP_WATER || coinflip()) + || grid_destroys_items(grd[monster->x][monster->y]) + || coinflip()) { return; } @@ -303,11 +315,11 @@ static void place_monster_corpse(struct monsters *monster) mitm[o].plus2 = 0; // butcher work done mitm[o].sub_type = CORPSE_BODY; mitm[o].special = 210; // rot time - mitm[o].colour = mons_colour(corpse_class); + mitm[o].colour = mons_class_colour(corpse_class); mitm[o].quantity = 1; if (mitm[o].colour == BLACK) - mitm[o].colour = monster->number; + mitm[o].colour = monster->colour; // Don't care if 'o' is changed, and it shouldn't be (corpses don't stack) move_item_to_grid( &o, monster->x, monster->y ); @@ -316,6 +328,7 @@ static void place_monster_corpse(struct monsters *monster) void monster_die(struct monsters *monster, char killer, int i) { int dmi; // dead monster's inventory + int xom_will_act = 0; int monster_killed = monster_index(monster); bool death_message = mons_near(monster) && player_monster_visible(monster); @@ -435,42 +448,44 @@ void monster_die(struct monsters *monster, char killer, int i) // Xom doesn't care who you killed: if (you.religion == GOD_XOM - && random2(70) <= 10 + monster->hit_dice) + && random2(70) <= 10 + monster->hit_dice) { - Xom_acts(true, 1 + random2(monster->hit_dice), false); + // postpone Xom action until after the monster dies + xom_will_act = 1 + random2(monster->hit_dice); } // Trying to prevent summoning abuse here, so we're trying to - // prevent summoned creatures from being being done_good kills, + // prevent summoned creatures from being done_good kills, // Only affects monsters friendly when created. if (!created_friendly) { if (you.duration[DUR_PRAYER]) { - if (mons_holiness(monster->type) == MH_NATURAL) - done_good(GOOD_KILLED_LIVING, monster->hit_dice); + if (mons_holiness(monster) == MH_NATURAL) + did_god_conduct(DID_DEDICATED_KILL_LIVING, + monster->hit_dice); - if (mons_holiness(monster->type) == MH_UNDEAD) - done_good(GOOD_KILLED_UNDEAD, monster->hit_dice); + if (mons_holiness(monster) == MH_UNDEAD) + did_god_conduct(DID_DEDICATED_KILL_UNDEAD, + monster->hit_dice); - if (mons_holiness(monster->type) == MH_DEMONIC) - done_good(GOOD_KILLED_DEMON, monster->hit_dice); - - if (mons_holiness(monster->type) == MH_HOLY) - done_good(GOOD_KILLED_ANGEL_II, monster->hit_dice); + if (mons_holiness(monster) == MH_DEMONIC) + did_god_conduct(DID_DEDICATED_KILL_DEMON, + monster->hit_dice); //jmf: Trog hates wizards - if (mons_flag(monster->type, M_ACTUAL_SPELLS)) - done_good(GOOD_KILLED_WIZARD, monster->hit_dice); + if (mons_class_flag(monster->type, M_ACTUAL_SPELLS)) + did_god_conduct(DID_DEDICATED_KILL_WIZARD, + monster->hit_dice); //jmf: maybe someone hates priests? - if (mons_flag(monster->type, M_PRIEST)) - done_good(GOOD_KILLED_PRIEST, monster->hit_dice); - } - else if (mons_holiness(monster->type) == MH_HOLY) - { - done_good(GOOD_KILLED_ANGEL_I, monster->hit_dice); + if (mons_class_flag(monster->type, M_PRIEST)) + did_god_conduct(DID_DEDICATED_KILL_PRIEST, + monster->hit_dice); } + + if (mons_holiness(monster) == MH_HOLY) + did_god_conduct(DID_KILL_ANGEL, monster->hit_dice); } // Divine health and mp restoration doesn't happen when killing @@ -501,12 +516,12 @@ void monster_die(struct monsters *monster, char killer, int i) } if (you.duration[DUR_DEATH_CHANNEL] - && mons_holiness(monster->type) == MH_NATURAL - && mons_weight(mons_charclass(monster->type))) + && mons_holiness(monster) == MH_NATURAL + && mons_weight(mons_species(monster->type))) { if (create_monster( MONS_SPECTRAL_THING, 0, BEH_FRIENDLY, monster->x, monster->y, you.pet_target, - mons_charclass(monster->type)) != -1) + mons_species(monster->type)) != -1) { if (death_message) mpr("A glowing mist starts to gather..."); @@ -522,40 +537,83 @@ void monster_die(struct monsters *monster, char killer, int i) // no piety loss if god gifts killed by other monsters if (mons_friendly(monster) && !testbits(monster->flags,MF_GOD_GIFT)) - naughty(NAUGHTY_FRIEND_DIES, 1 + (monster->hit_dice / 2)); + did_god_conduct(DID_FRIEND_DIES, 1 + (monster->hit_dice / 2)); // Trying to prevent summoning abuse here, so we're trying to // prevent summoned creatures from being being done_good kills. // Only affects creatures which were friendly when summoned. if (!testbits(monster->flags, MF_CREATED_FRIENDLY) && pet_kill) { + bool notice = false; + gain_exp(exper_value( monster ) / 2 + 1); - if (mons_holiness(menv[i].type) == MH_UNDEAD) + int targ_holy = mons_holiness(monster), + attacker_holy = mons_holiness(&menv[i]); + + if (attacker_holy == MH_UNDEAD) { - if (mons_holiness(monster->type) == MH_NATURAL) - done_good(GOOD_SLAVES_KILL_LIVING, monster->hit_dice); - else - done_good(GOOD_SERVANTS_KILL, monster->hit_dice); + if (targ_holy == MH_NATURAL) + notice |= + did_god_conduct(DID_LIVING_KILLED_BY_UNDEAD_SLAVE, + monster->hit_dice); } - else + else if (you.religion == GOD_VEHUMET + || testbits( menv[i].flags, MF_GOD_GIFT )) { - done_good(GOOD_SERVANTS_KILL, monster->hit_dice); - - if (you.religion == GOD_VEHUMET - && (!player_under_penance() - && random2(you.piety) >= 30)) + // Yes, we are splitting undead pets from the others + // as a way to focus Necomancy vs Summoning (ignoring + // Summon Wraith here)... at least we're being nice and + // putting the natural creature Summons together with + // the Demon ones. Note that Vehumet gets a free + // pass here since those followers are assumed to + // come from Summoning spells... the others are + // from invocations (Zin, TSO, Makh, Kiku). -- bwr + + if (targ_holy == MH_NATURAL) { - /* Vehumet - only for non-undead servants (coding - convenience, no real reason except that Vehumet - prefers demons) */ - if (you.magic_points < you.max_magic_points) + notice |= did_god_conduct( DID_LIVING_KILLED_BY_SERVANT, + monster->hit_dice ); + + if (mons_class_flag( monster->type, M_EVIL )) { - mpr("You feel your power returning."); - inc_mp(1 + random2(random2(monster->hit_dice)), - false); + notice |= + did_god_conduct( + DID_NATURAL_EVIL_KILLED_BY_SERVANT, + monster->hit_dice ); } } + else if (targ_holy == MH_DEMONIC) + { + notice |= did_god_conduct( DID_DEMON_KILLED_BY_SERVANT, + monster->hit_dice ); + } + else if (targ_holy == MH_UNDEAD) + { + notice |= did_god_conduct( DID_UNDEAD_KILLED_BY_SERVANT, + monster->hit_dice ); + } + } + + // Angel kills are always noticed. + if (targ_holy == MH_HOLY) + { + notice |= did_god_conduct( DID_ANGEL_KILLED_BY_SERVANT, + monster->hit_dice ); + } + + if (you.religion == GOD_VEHUMET + && notice + && (!player_under_penance() && random2(you.piety) >= 30)) + { + /* Vehumet - only for non-undead servants (coding + convenience, no real reason except that Vehumet + prefers demons) */ + if (you.magic_points < you.max_magic_points) + { + mpr("You feel your power returning."); + inc_mp( 1 + random2(monster->hit_dice / 2), false ); + } } } break; @@ -576,6 +634,9 @@ void monster_die(struct monsters *monster, char killer, int i) place_cloud( CLOUD_GREY_SMOKE_MON + random2(3), monster->x, monster->y, 1 + random2(3) ); + // fall-through + + case KILL_DISMISSED: for (dmi = MSLOT_GOLD; dmi >= MSLOT_WEAPON; dmi--) { /* takes whatever it's carrying back home */ if (monster->inv[dmi] != NON_ITEM) @@ -625,7 +686,7 @@ void monster_die(struct monsters *monster, char killer, int i) (tmp == 1) ? " says, \"I'll get you next time!\"" : (tmp == 2) ? " says, \"This isn't over yet!\"" : (tmp == 3) ? " says, \"I'll be back!\"" : - (tmp == 4) ? " says, \"This isn't the end, its only just beginning!\"" : + (tmp == 4) ? " says, \"This isn't the end, it's only just beginning!\"" : (tmp == 5) ? " says, \"Kill me? I think not!\"" : " says, \"You cannot defeat me so easily!\"", MSGCH_TALK ); @@ -636,13 +697,31 @@ void monster_die(struct monsters *monster, char killer, int i) you.unique_creatures[ monster->type - 280 ] = 0; } - if (killer != KILL_RESET) + if (killer != KILL_RESET && killer != KILL_DISMISSED) { + if ( MONST_INTERESTING(monster) || + // XXX yucky hack + monster->type == MONS_PLAYER_GHOST || + monster->type == MONS_PANDEMONIUM_DEMON ) { + /* make a note of it */ + char namebuf[ITEMNAME_SIZE]; + if ( monster->type == MONS_PLAYER_GHOST ) { + snprintf( namebuf, sizeof(namebuf), "the ghost of %s", + ghost.name ); + } + else if ( monster->type == MONS_PANDEMONIUM_DEMON ) { + strncpy( namebuf, ghost.name, sizeof(namebuf) ); + } + else + moname(monster->type, true, DESC_NOCAP_A, namebuf); + take_note(Note(NOTE_KILL_MONSTER, monster->type, 0, namebuf)); + } + you.kills.record_kill(monster, killer, pet_kill); if (mons_has_ench(monster, ENCH_ABJ_I, ENCH_ABJ_VI)) { - if (mons_weight(mons_charclass(monster->type))) + if (mons_weight(mons_species(monster->type))) { if (monster->type == MONS_SIMULACRUM_SMALL || monster->type == MONS_SIMULACRUM_LARGE) @@ -674,9 +753,13 @@ void monster_die(struct monsters *monster, char killer, int i) || killer == KILL_YOU || pet_kill); monster_cleanup(monster); + + if ( xom_will_act ) + Xom_acts(true, xom_will_act, false); + } // end monster_die -void monster_cleanup(struct monsters *monster) +void monster_cleanup(monsters *monster) { unsigned int monster_killed = monster_index(monster); int dmi = 0; @@ -696,7 +779,8 @@ void monster_cleanup(struct monsters *monster) monster->behaviour = BEH_SLEEP; monster->foe = MHITNOT; - mgrd[monster->x][monster->y] = NON_MONSTER; + if (in_bounds(monster->x, monster->y)) + mgrd[monster->x][monster->y] = NON_MONSTER; for (dmi = MSLOT_GOLD; dmi >= MSLOT_WEAPON; dmi--) { @@ -720,7 +804,7 @@ static bool jelly_divide(struct monsters * parent) bool foundSpot = false; // to rid code of hideous goto {dlb} struct monsters *child = 0; // NULL - value determined with loop {dlb} - if (!mons_flag( parent->type, M_SPLITS ) || parent->hit_points == 1) + if (!mons_class_flag( parent->type, M_SPLITS ) || parent->hit_points == 1) return (false); // first, find a suitable spot for the child {dlb}: @@ -734,7 +818,7 @@ static bool jelly_divide(struct monsters * parent) { // 10-50 for now - must take clouds into account: if (mgrd[parent->x + jex][parent->y + jey] == NON_MONSTER - && grd[parent->x + jex][parent->y + jey] > DNGN_LAST_SOLID_TILE + && !grid_is_solid(grd[parent->x + jex][parent->y + jey]) && (parent->x + jex != you.x_pos || parent->y + jey != you.y_pos)) { foundSpot = true; @@ -779,6 +863,11 @@ static bool jelly_divide(struct monsters * parent) child->behaviour = parent->behaviour; /* Look at this! */ child->foe = parent->foe; child->attitude = parent->attitude; + child->colour = parent->colour; + + // duplicate enchantments + for ( int i = 0; i < NUM_MON_ENCHANTS; ++i ) + child->enchantment[i] = parent->enchantment[i]; child->x = parent->x + jex; child->y = parent->y + jey; @@ -788,7 +877,7 @@ static bool jelly_divide(struct monsters * parent) if (!simple_monster_message(parent, " splits in two!")) { if (!silenced(parent->x, parent->y) || !silenced(child->x, child->y)) - mpr("You hear a squelching noise."); + mpr("You hear a squelching noise.", MSGCH_SOUND); } return (true); @@ -821,13 +910,13 @@ static bool valid_morph( struct monsters *monster, int new_mclass ) { unsigned char current_tile = grd[monster->x][monster->y]; - // morph targets are _always_ "base" classes, not derived ones. - new_mclass = mons_charclass(new_mclass); + // morph targets are _always_ "base" classes, not derived ones. + new_mclass = mons_species(new_mclass); /* various inappropriate polymorph targets */ - if (mons_holiness( new_mclass ) != mons_holiness( monster->type ) - || mons_flag( new_mclass, M_NO_EXP_GAIN ) // not helpless - || new_mclass == mons_charclass( monster->type ) // must be different + if (mons_class_holiness( new_mclass ) != mons_holiness( monster ) + || mons_class_flag( new_mclass, M_NO_EXP_GAIN ) // not helpless + || new_mclass == mons_species( monster->type ) // must be different || new_mclass == MONS_PROGRAM_BUG || new_mclass == MONS_SHAPESHIFTER || new_mclass == MONS_GLOWING_SHAPESHIFTER @@ -841,30 +930,8 @@ static bool valid_morph( struct monsters *monster, int new_mclass ) return (false); } - /* Not fair to instakill a monster like this -- - order of evaluation of inner conditional important */ - if (current_tile == DNGN_LAVA || current_tile == DNGN_DEEP_WATER) - { - if (!mons_class_flies(new_mclass) - || monster_habitat(new_mclass) != current_tile) - { - return (false); - } - } - - // not fair to strand a water monster on dry land, either. :) - if (monster_habitat(new_mclass) == DNGN_DEEP_WATER - && current_tile != DNGN_DEEP_WATER - && current_tile != DNGN_SHALLOW_WATER) - { - return (false); - } - - // and putting lava monsters on non-lava sqaures is a no-no, too - if (monster_habitat(new_mclass) == DNGN_LAVA && current_tile != DNGN_LAVA) - return (false); - - return (true); + // Determine if the monster is happy on current tile + return (monster_habitable_grid(new_mclass, current_tile)); } // end valid_morph() // note that power is (as of yet) unused within this function - @@ -890,10 +957,12 @@ bool monster_polymorph( struct monsters *monster, int targetc, int power ) { do { - targetc = random2( NUM_MONSTERS ); + // Pick a monster that's guaranteed happy at this grid + targetc = random_monster_at_grid(monster->x, monster->y); - // valid targets are always base classes - targetc = mons_charclass( targetc ); + // valid targets are always base classes ([ds] which is unfortunate + // in that well-populated monster classes will dominate polymorphs) + targetc = mons_species( targetc ); target_power = mons_power( targetc ); @@ -914,9 +983,21 @@ bool monster_polymorph( struct monsters *monster, int targetc, int power ) return (player_messaged); } + // If old monster is visible to the player, and is interesting, + // then note why the interesting monster went away. + if (player_monster_visible(monster) && mons_near(monster) + && MONST_INTERESTING(monster)) { + + char namebuf[ITEMNAME_SIZE]; + moname(monster->type, true, DESC_NOCAP_A, namebuf); + take_note(Note(NOTE_POLY_MONSTER, monster->type, 0, namebuf)); + + } + // messaging: {dlb} - bool invis = mons_flag( targetc, M_INVIS ) - || mons_has_ench( monster, ENCH_INVIS ); + bool invis = (mons_class_flag( targetc, M_INVIS ) + || mons_has_ench( monster, ENCH_INVIS )) && + (!player_see_invis()); if (mons_has_ench( monster, ENCH_GLOWING_SHAPESHIFTER, ENCH_SHAPESHIFTER )) strcat( str_polymon, " changes into " ); @@ -925,11 +1006,11 @@ bool monster_polymorph( struct monsters *monster, int targetc, int power ) else strcat( str_polymon, " evaporates and reforms as " ); - if (invis && !player_see_invis()) + if (invis) strcat( str_polymon, "something you cannot see!" ); else { - strcat( str_polymon, monam( 250, targetc, !invis, DESC_NOCAP_A ) ); + strcat( str_polymon, monam( 250, targetc, true, DESC_NOCAP_A ) ); if (targetc == MONS_PULSATING_LUMP) strcat( str_polymon, " of flesh" ); @@ -961,7 +1042,7 @@ bool monster_polymorph( struct monsters *monster, int targetc, int power ) if (shifter != ENCH_NONE) mons_add_ench( monster, shifter ); - if (mons_flag( monster->type, M_INVIS )) + if (mons_class_flag( monster->type, M_INVIS )) mons_add_ench( monster, ENCH_INVIS ); monster->hit_points = monster->max_hit_points @@ -975,16 +1056,23 @@ bool monster_polymorph( struct monsters *monster, int targetc, int power ) monster_drop_ething(monster); + // New monster type might be interesting + mark_interesting_monst(monster); + + // If new monster is visible to player, then we've seen it + if (player_monster_visible(monster) && mons_near(monster)) + seen_monster(monster); + return (player_messaged); } // end monster_polymorph() -void monster_blink(struct monsters *monster) +bool monster_blink(monsters *monster) { int nx, ny; if (!random_near_space(monster->x, monster->y, nx, ny, - false, false)) - return; + false, false)) + return (false); mgrd[monster->x][monster->y] = NON_MONSTER; @@ -992,6 +1080,11 @@ void monster_blink(struct monsters *monster) monster->y = ny; mgrd[nx][ny] = monster_index(monster); + + if (player_monster_visible(monster) && mons_near(monster)) + seen_monster(monster); + + return (true); } // end monster_blink() // allow_adjacent: allow target to be adjacent to origin @@ -1025,45 +1118,7 @@ bool random_near_space(int ox, int oy, int &tx, int &ty, bool allow_adjacent, static bool habitat_okay( struct monsters *monster, int targ ) { - bool ret = false; - const int habitat = monster_habitat( monster->type ); - - if (mons_flies( monster )) - { - // flying monsters don't care - ret = true; - } - else if (mons_flag( monster->type, M_AMPHIBIOUS ) - && (targ == DNGN_DEEP_WATER || targ == DNGN_SHALLOW_WATER)) - { - // Amphibious creatures are "land" by default in mon-data, - // we allow them to swim here. -- bwr - ret = true; - } - else if (monster->type == MONS_WATER_ELEMENTAL && targ >= DNGN_DEEP_WATER) - { - // water elementals can crawl out over the land - ret = true; - } - else if (habitat == DNGN_FLOOR - && (targ >= DNGN_FLOOR || targ == DNGN_SHALLOW_WATER)) - { - // FLOOR habitat monster going to a non-bad place - ret = true; - } - else if (habitat == DNGN_DEEP_WATER - && (targ == DNGN_DEEP_WATER || targ == DNGN_SHALLOW_WATER)) - { - // Water monster to water - ret = true; - } - else if (habitat == DNGN_LAVA && targ == DNGN_LAVA) - { - // Lava monster to lava - ret = true; - } - - return (ret); + return (monster_habitable_grid(monster, targ)); } // This doesn't really swap places, it just sets the monster's @@ -1216,7 +1271,7 @@ void print_wounds(struct monsters *monster) bool wounded_damaged(int wound_class) { // this schema needs to be abstracted into real categories {dlb}: - const int holy = mons_holiness(wound_class); + const int holy = mons_class_holiness(wound_class); if (holy == MH_UNDEAD || holy == MH_NONLIVING || holy == MH_PLANT) return (true); @@ -1266,10 +1321,12 @@ void behaviour_event( struct monsters *mon, int event, int src, case ME_WHACK: case ME_ANNOY: - // will turn monster against , unless they + // will turn monster against , unless they // are BOTH friendly and stupid. Hitting someone // over the head, of course, always triggers this code. - if (isFriendly != sourceFriendly || isSmart || event == ME_WHACK) + if ( event == ME_WHACK || + ((isFriendly != sourceFriendly || isSmart) && + (mon->behaviour != BEH_FLEE && mon->behaviour != BEH_PANIC))) { mon->foe = src; @@ -1291,7 +1348,7 @@ void behaviour_event( struct monsters *mon, int event, int src, case ME_ALERT: // will alert monster to and turn them - // against them, unless they have a current foe. + // against them, unless they have a current foe. // it won't turn friends hostile either. if (mon->behaviour != BEH_CORNERED) mon->behaviour = BEH_SEEK; @@ -1362,12 +1419,7 @@ static void handle_behaviour(struct monsters *mon) bool isHealthy = (mon->hit_points > mon->max_hit_points / 2); bool isSmart = (mons_intel(mon->type) > I_ANIMAL); bool isScared = mons_has_ench(mon, ENCH_FEAR); - - // immobility logic stolen from later on in handle_monster().. argh! --gdl - bool isMobile = !(mon->type == MONS_OKLOB_PLANT - || mon->type == MONS_CURSE_SKULL - || (mon->type >= MONS_CURSE_TOE - && mon->type <= MONS_POTION_MIMIC)); + bool isMobile = !mons_is_stationary(mon); // check for confusion -- early out. if (mons_has_ench(mon, ENCH_CONFUSION)) @@ -1395,13 +1447,18 @@ static void handle_behaviour(struct monsters *mon) if (!see_grid(mon->x, mon->y)) proxPlayer = false; + const int intel = mons_intel(mon->type); // now, the corollary to that is that sometimes, if a // player is right next to a monster, they will 'see' if (grid_distance( you.x_pos, you.y_pos, mon->x, mon->y ) == 1 - && one_chance_in(3)) - { + && one_chance_in(3)) + proxPlayer = true; + + // [dshaligram] Very smart monsters have a chance of cluing in to + // invisible players in various ways. + else if ((intel == I_NORMAL && one_chance_in(10)) + || (intel == I_HIGH && one_chance_in(6))) proxPlayer = true; - } } // set friendly target, if they don't already have one @@ -1424,7 +1481,7 @@ static void handle_behaviour(struct monsters *mon) } // unfriendly monsters fighting other monsters will usually - // target the player, if they're healthy + // target the player, if they're healthy if (!isFriendly && mon->foe != MHITYOU && mon->foe != MHITNOT && proxPlayer && !one_chance_in(3) && isHealthy) { @@ -1515,7 +1572,7 @@ static void handle_behaviour(struct monsters *mon) { // if we've arrived at our target x,y // do a stealth check. If the foe - // fails, monster will then start + // fails, monster will then start // tracking foe's CURRENT position, // but only for a few moves (smell and // intuition only go so far) @@ -1583,7 +1640,7 @@ static void handle_behaviour(struct monsters *mon) // by updating target x,y if (mon->foe == MHITYOU) { - // sometimes, your friends will wander a bit. + // sometimes, your friends will wander a bit. if (isFriendly && one_chance_in(8)) { mon->target_x = 10 + random2(GXM - 10); @@ -1635,9 +1692,9 @@ static void handle_behaviour(struct monsters *mon) mon->target_y = 10 + random2(GYM - 10); } - // during their wanderings, monsters will + // during their wanderings, monsters will // eventually relax their guard (stupid - // ones will do so faster, smart monsters + // ones will do so faster, smart monsters // have longer memories if (!proxFoe && mon->foe != MHITNOT) { @@ -1652,7 +1709,7 @@ static void handle_behaviour(struct monsters *mon) new_beh = BEH_SEEK; // smart monsters flee until they can // flee no more... possible to get a - // 'CORNERED' event, at which point + // 'CORNERED' event, at which point // we can jump back to WANDER if the foe // isn't present. @@ -1727,7 +1784,6 @@ static inline int mod_speed( int val, int speed ) static bool handle_enchantment(struct monsters *monster) { - const int habitat = monster_habitat( monster->type ); bool died = false; int grid; int poisonval; @@ -1735,7 +1791,7 @@ static bool handle_enchantment(struct monsters *monster) int tmp; // Yes, this is the speed we want. This function will be called in - // two curcumstances: (1) the monster can move and have enough energy, + // two circumstances: (1) the monster can move and has enough energy, // and (2) the monster cannot move (speed == 0) and the monster loop // is running. // @@ -1787,7 +1843,7 @@ static bool handle_enchantment(struct monsters *monster) if (random2(120) < mod_speed( monster->hit_dice + 5, speed )) { // don't delete perma-confusion - if (!mons_flag(monster->type, M_CONFUSED)) + if (!mons_class_flag(monster->type, M_CONFUSED)) mons_del_ench(monster, ENCH_CONFUSION); } break; @@ -1796,7 +1852,7 @@ static bool handle_enchantment(struct monsters *monster) if (random2(1000) < mod_speed( 25, speed )) { // don't delete perma-invis - if (!mons_flag( monster->type, M_INVIS )) + if (!mons_class_flag( monster->type, M_INVIS )) mons_del_ench(monster, ENCH_INVIS); } break; @@ -1824,7 +1880,7 @@ static bool handle_enchantment(struct monsters *monster) // Badly injured monsters prefer to stay submerged... // electrical eels and lava snakes have ranged attacks // and are more likely to surface. -- bwr - if (habitat == DNGN_FLOOR || habitat != grid) + if (!monster_can_submerge(monster->type, grid)) mons_del_ench( monster, ENCH_SUBMERGED ); // forced to surface else if (monster->hit_points <= monster->max_hit_points / 2) break; @@ -1834,7 +1890,10 @@ static bool handle_enchantment(struct monsters *monster) || (mons_near(monster) && monster->hit_points == monster->max_hit_points && !one_chance_in(10)))) - || random2(5000) < mod_speed( 10, speed )) + || random2(2000) < mod_speed(10, speed) + || (mons_near(monster) + && monster->hit_points == monster->max_hit_points + && !one_chance_in(5))) { mons_del_ench( monster, ENCH_SUBMERGED ); } @@ -2133,7 +2192,7 @@ static void handle_movement(struct monsters *monster) // reproduced here is some semi-legacy code that makes monsters // move somewhat randomly along oblique paths. It is an exceedingly - // good idea, given crawl's unique line of sight properties. + // good idea, given crawl's unique line of sight properties. // // Added a check so that oblique movement paths aren't used when // close to the target square. -- bwr @@ -2172,7 +2231,7 @@ static void handle_nearby_ability(struct monsters *monster) return; } - if (mons_flag(monster->type, M_SPEAKS) && one_chance_in(21) + if (mons_class_flag(monster->type, M_SPEAKS) && one_chance_in(21) && monster->behaviour != BEH_WANDER) { mons_speaks(monster); @@ -2183,7 +2242,7 @@ static void handle_nearby_ability(struct monsters *monster) case MONS_SPATIAL_VORTEX: case MONS_KILLER_KLOWN: // used for colour (butterflies too, but they don't change) - monster->number = random_colour(); + monster->colour = random_colour(); break; case MONS_GIANT_EYEBALL: @@ -2236,10 +2295,13 @@ static void handle_nearby_ability(struct monsters *monster) mons_del_ench( monster, ENCH_SUBMERGED ); } } - else if (monster_habitat(monster->type) == grd[monster->x][monster->y] + else if (monster_can_submerge(monster->type, + grd[monster->x][monster->y]) && (one_chance_in(5) || (grid_distance( monster->x, monster->y, you.x_pos, you.y_pos ) > 1 + // FIXME This is better expressed as a function + // such as monster_has_ranged_attack: && monster->type != MONS_ELECTRICAL_EEL && monster->type != MONS_LAVA_SNAKE && !one_chance_in(20)) @@ -2257,7 +2319,7 @@ static void handle_nearby_ability(struct monsters *monster) case MONS_PANDEMONIUM_DEMON: if (ghost.values[ GVAL_DEMONLORD_CYCLE_COLOUR ]) - monster->number = random_colour(); + monster->colour = random_colour(); break; } } // end handle_nearby_ability() @@ -2275,6 +2337,10 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) FixedArray < unsigned int, 19, 19 > show; + const monster_type mclass = (mons_genus( monster->type ) == MONS_DRACONIAN) + ? draco_subspecies( monster ) + : static_cast( monster->type ); + if (!mons_near( monster ) || monster->behaviour == BEH_SLEEP || mons_has_ench( monster, ENCH_SUBMERGED )) @@ -2284,8 +2350,16 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) // losight(show, grd, you.x_pos, you.y_pos); - switch (monster->type) + switch (mclass) { + case MONS_ORANGE_STATUE: + used = orange_statue_effects(monster); + break; + + case MONS_SILVER_STATUE: + used = silver_statue_effects(monster); + break; + case MONS_BALL_LIGHTNING: if (monster->attitude == ATT_HOSTILE && distance( you.x_pos, you.y_pos, monster->x, monster->y ) <= 5) @@ -2323,7 +2397,7 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) if (tx < 0 || tx > GXM || ty < 0 || ty > GYM) continue; - if (grd[tx][ty] > DNGN_LAST_SOLID_TILE) + if (!grid_is_solid(grd[tx][ty])) { monster->hit_points = -1; used = true; @@ -2343,7 +2417,7 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) break; // setup tracer - strcpy(beem.beam_name, "glob of lava"); + beem.name = "glob of lava"; beem.range = 4; beem.rangeMax = 13; beem.damage = dice_def( 3, 10 ); @@ -2378,7 +2452,7 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) break; // setup tracer - strcpy(beem.beam_name, "bolt of electricity"); + beem.name = "bolt of electricity"; beem.damage = dice_def( 3, 6 ); beem.colour = LIGHTCYAN; beem.type = SYM_ZAP; @@ -2389,7 +2463,7 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) beem.aux_source = "bolt of electricity"; beem.range = 4; beem.rangeMax = 13; - beem.isBeam = true; + beem.is_beam = true; // fire tracer fire_tracer(monster, beem); @@ -2405,6 +2479,7 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) case MONS_ACID_BLOB: case MONS_OKLOB_PLANT: + case MONS_YELLOW_DRACONIAN: if (mons_has_ench(monster, ENCH_CONFUSION)) break; @@ -2424,7 +2499,7 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) if (mons_has_ench(monster, ENCH_CONFUSION)) break; - // friendly fiends won't use torment, preferring hellfire + // friendly fiends won't use torment, preferring hellfire // (right now there is no way a monster can predict how // badly they'll damage the player with torment) -- GDL if (one_chance_in(4)) @@ -2490,16 +2565,16 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) if (!mons_near(monster)) break; - // the fewer spikes the manticore has left, the less + // the fewer spikes the manticore has left, the less // likely it will use them. if (random2(16) >= static_cast(monster->number)) break; - // do the throwing right here, since the beam is so + // do the throwing right here, since the beam is so // easy to set up and doesn't involve inventory. // set up the beam - strcpy(beem.beam_name, "volley of spikes"); + beem.name = "volley of spikes"; beem.range = 9; beem.rangeMax = 9; beem.hit = 14; @@ -2510,7 +2585,7 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) beem.flavour = BEAM_MISSILE; beem.thrower = KILL_MON; beem.aux_source = "volley of spikes"; - beem.isBeam = false; + beem.is_beam = false; // fire tracer fire_tracer(monster, beem); @@ -2533,6 +2608,8 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) case MONS_LINDWURM: case MONS_FIREDRAKE: case MONS_XTAHUA: + case MONS_WHITE_DRACONIAN: + case MONS_RED_DRACONIAN: if (!mons_player_visible( monster )) break; @@ -2550,7 +2627,8 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) // good idea? if (mons_should_fire(beem)) { - simple_monster_message(monster, " breathes."); + simple_monster_message(monster, " breathes.", + MSGCH_MONSTER_SPELL); fire_beam(beem); mmov_x = 0; mmov_y = 0; @@ -2558,6 +2636,9 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) } } break; + + default: + break; } return (used); @@ -2590,9 +2671,9 @@ static bool handle_potion(struct monsters *monster, bolt & beem) case POT_HEALING: case POT_HEAL_WOUNDS: if (monster->hit_points <= monster->max_hit_points / 2 - && mons_holiness(monster->type) != MH_UNDEAD - && mons_holiness(monster->type) != MH_NONLIVING - && mons_holiness(monster->type) != MH_PLANT) + && mons_holiness(monster) != MH_UNDEAD + && mons_holiness(monster) != MH_NONLIVING + && mons_holiness(monster) != MH_PLANT) { simple_monster_message(monster, " drinks a potion."); @@ -2616,7 +2697,13 @@ static bool handle_potion(struct monsters *monster, bolt & beem) // intentional fall through case POT_INVISIBILITY: if (mitm[monster->inv[MSLOT_POTION]].sub_type == POT_INVISIBILITY) + { beem.colour = MAGENTA; + // Friendly monsters won't go invisible if the player + // can't see invisible. We're being nice. + if ( mons_friendly(monster) && !player_see_invis(false) ) + break; + } // why only drink these if not near player? {dlb} if (!mons_near(monster)) @@ -2797,12 +2884,12 @@ static bool handle_wand(struct monsters *monster, bolt &beem) // set up the beam int power = 30 + monster->hit_dice; - struct SBeam theBeam = mons_spells(mzap, power); + bolt theBeam = mons_spells(mzap, power); // XXX: ugly hack this: static char wand_buff[ ITEMNAME_SIZE ]; - strcpy( beem.beam_name, theBeam.name.c_str() ); + beem.name = theBeam.name; beem.beam_source = monster_index(monster); beem.source_x = monster->x; beem.source_y = monster->y; @@ -2814,8 +2901,9 @@ static bool handle_wand(struct monsters *monster, bolt &beem) beem.hit = theBeam.hit; beem.type = theBeam.type; beem.flavour = theBeam.flavour; - beem.thrower = theBeam.thrown; - beem.isBeam = theBeam.isBeam; + beem.thrower = theBeam.thrower; + beem.is_beam = theBeam.is_beam; + beem.is_explosion = theBeam.is_explosion; item_def item = mitm[ monster->inv[MSLOT_WAND] ]; @@ -2864,7 +2952,8 @@ static bool handle_wand(struct monsters *monster, bolt &beem) case WAND_INVISIBILITY: if (!mons_has_ench( monster, ENCH_INVIS ) - && !mons_has_ench( monster, ENCH_SUBMERGED )) + && !mons_has_ench( monster, ENCH_SUBMERGED ) + && (!mons_friendly(monster) || player_see_invis(false))) { beem.target_x = monster->x; beem.target_y = monster->y; @@ -2905,12 +2994,12 @@ static bool handle_wand(struct monsters *monster, bolt &beem) if (!simple_monster_message(monster, " zaps a wand.")) { if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear a zap."); + mpr("You hear a zap.", MSGCH_SOUND); } // charge expenditure {dlb} mitm[monster->inv[MSLOT_WAND]].plus--; - beem.isTracer = false; + beem.is_tracer = false; fire_beam( beem ); return (true); @@ -2920,6 +3009,69 @@ static bool handle_wand(struct monsters *monster, bolt &beem) return (false); } // end handle_wand() +// Returns a suitable breath weapon for the draconian; does not handle all +// draconians, does fire a tracer. +static int get_draconian_breath_spell( struct monsters *monster ) +{ + int draco_breath = MS_NO_SPELL; + + if (mons_genus( monster->type ) == MONS_DRACONIAN) + { + switch (draco_subspecies( monster )) + { + case MONS_BLACK_DRACONIAN: + draco_breath = MS_LIGHTNING_BOLT; + break; + + case MONS_PALE_DRACONIAN: + draco_breath = MS_STEAM_BALL; + break; + + case MONS_GREEN_DRACONIAN: + draco_breath = MS_POISON_BLAST; + break; + + case MONS_PURPLE_DRACONIAN: + draco_breath = MS_ORB_ENERGY; + break; + + case MONS_MOTTLED_DRACONIAN: + draco_breath = MS_STICKY_FLAME; + break; + + case MONS_DRACONIAN: + case MONS_YELLOW_DRACONIAN: // already handled as ability + case MONS_RED_DRACONIAN: // already handled as ability + case MONS_WHITE_DRACONIAN: // already handled as ability + default: + break; + } + + if (draco_breath != MS_NO_SPELL) + { + // [ds] Check line-of-fire here. It won't happen elsewhere. + bolt beem; + setup_mons_cast(monster, beem, draco_breath); + fire_tracer(monster, beem); + if (!mons_should_fire(beem)) + draco_breath = MS_NO_SPELL; + } + + } + + return (draco_breath); +} + +static bool is_emergency_spell(const monster_spells &msp, int spell) +{ + // If the emergency spell appears early, it's probably not a dedicated + // escape spell. + for (int i = 0; i < 5; ++i) + if (msp[i] == spell) + return (false); + return (msp[5] == spell); +} + //--------------------------------------------------------------- // // handle_spell @@ -2928,28 +3080,30 @@ static bool handle_wand(struct monsters *monster, bolt &beem) // a spell was cast. // //--------------------------------------------------------------- -static bool handle_spell( struct monsters *monster, bolt & beem ) +static bool handle_spell( monsters *monster, bolt & beem ) { bool monsterNearby = mons_near(monster); bool finalAnswer = false; // as in: "Is that your...?" {dlb} + const int draco_breath = get_draconian_breath_spell(monster); // yes, there is a logic to this ordering {dlb}: if (monster->behaviour == BEH_SLEEP - || !mons_flag( monster->type, M_SPELLCASTER ) + || (!mons_class_flag(monster->type, M_SPELLCASTER) + && draco_breath == MS_NO_SPELL) || mons_has_ench( monster, ENCH_SUBMERGED )) { return (false); } - if ((mons_flag(monster->type, M_ACTUAL_SPELLS) - || mons_flag(monster->type, M_PRIEST)) + if ((mons_class_flag(monster->type, M_ACTUAL_SPELLS) + || mons_class_flag(monster->type, M_PRIEST)) && (mons_has_ench(monster, ENCH_GLOWING_SHAPESHIFTER, ENCH_SHAPESHIFTER))) { return (false); //jmf: shapeshiftes don't get spells, just // physical powers. } else if (mons_has_ench(monster, ENCH_CONFUSION) - && !mons_flag(monster->type, M_CONFUSED)) + && !mons_class_flag(monster->type, M_CONFUSED)) { return (false); } @@ -2966,14 +3120,7 @@ static bool handle_spell( struct monsters *monster, bolt & beem ) else { int spell_cast = MS_NO_SPELL; - int hspell_pass[6] = { MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL, - MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL }; - - int msecc = ((monster->type == MONS_HELLION) ? MST_BURNING_DEVIL : - (monster->type == MONS_PANDEMONIUM_DEMON) ? MST_GHOST - : monster->number); - - mons_spell_list( msecc, hspell_pass ); + monster_spells hspell_pass = monster->spells; // forces the casting of dig when player not visible - this is EVIL! if (!monsterNearby) @@ -3055,7 +3202,7 @@ static bool handle_spell( struct monsters *monster, bolt & beem ) } // If no useful spells... cast no spell. - if (num_no_spell == 6) + if (num_no_spell == 6 && draco_breath == MS_NO_SPELL) return (false); // up to four tries to pick a spell. @@ -3129,6 +3276,17 @@ static bool handle_spell( struct monsters *monster, bolt & beem ) } } + // If there's otherwise no ranged attack use the breath weapon. + // The breath weapon is also occasionally used. + if (draco_breath != MS_NO_SPELL + && (spell_cast == MS_NO_SPELL + || (!is_emergency_spell(hspell_pass, spell_cast) + && one_chance_in(4)))) + { + spell_cast = draco_breath; + finalAnswer = true; + } + // should the monster *still* not have a spell, well, too bad {dlb}: if (spell_cast == MS_NO_SPELL) return (false); @@ -3160,10 +3318,24 @@ static bool handle_spell( struct monsters *monster, bolt & beem ) switch (monster->type) { default: + if (spell_cast == draco_breath) + { + if (!simple_monster_message(monster, " breathes.", + MSGCH_MONSTER_SPELL)) + { + if (!silenced(monster->x, monster->y) + && !silenced(you.x_pos, you.y_pos)) + { + mpr("You hear a roar.", MSGCH_SOUND); + } + } + break; + } + if (silenced(monster->x, monster->y)) return (false); - if (mons_flag(monster->type, M_PRIEST)) + if (mons_class_flag(monster->type, M_PRIEST)) { switch (random2(3)) { @@ -3193,9 +3365,10 @@ static bool handle_spell( struct monsters *monster, bolt & beem ) // XXX: could be better, chosen to match the // ones in monspeak.cc... has the problem // that it doesn't suggest a vocal component. -- bwr - simple_monster_message( monster, - " gestures wildly.", - MSGCH_MONSTER_SPELL ); + if (player_monster_visible(monster)) + simple_monster_message( monster, + " gestures wildly.", + MSGCH_MONSTER_SPELL ); break; case 1: simple_monster_message( monster, @@ -3223,6 +3396,7 @@ static bool handle_spell( struct monsters *monster, bolt & beem ) case MONS_SHADOW_DRAGON: case MONS_SWAMP_DRAGON: case MONS_SWAMP_DRAKE: + case MONS_DEATH_DRAKE: case MONS_HELL_HOG: case MONS_SERPENT_OF_HELL: case MONS_QUICKSILVER_DRAGON: @@ -3233,7 +3407,7 @@ static bool handle_spell( struct monsters *monster, bolt & beem ) if (!silenced(monster->x, monster->y) && !silenced(you.x_pos, you.y_pos)) { - mpr("You hear a roar.", MSGCH_MONSTER_SPELL); + mpr("You hear a roar.", MSGCH_SOUND); } } break; @@ -3271,16 +3445,21 @@ static bool handle_spell( struct monsters *monster, bolt & beem ) if (monster->type == MONS_GERYON && !silenced(you.x_pos, you.y_pos)) { - mpr("You hear a weird and mournful sound."); + mpr("You hear a weird and mournful sound.", MSGCH_SOUND); } } // FINALLY! determine primary spell effects {dlb}: - if (spell_cast == MS_BLINK && monsterNearby) - // why only cast blink if nearby? {dlb} + if (spell_cast == MS_BLINK) { - simple_monster_message(monster, " blinks!"); - monster_blink(monster); + // why only cast blink if nearby? {dlb} + if (monsterNearby) + { + simple_monster_message(monster, " blinks!"); + monster_blink(monster); + } + else + return (false); } else { @@ -3288,7 +3467,7 @@ static bool handle_spell( struct monsters *monster, bolt & beem ) mmov_x = 0; mmov_y = 0; } - } // end "if mons_flag(monster->type, M_SPELLCASTER) ... + } // end "if mons_class_flag(monster->type, M_SPELLCASTER) ... return (true); } // end handle_spell() @@ -3327,7 +3506,7 @@ static bool handle_throw(struct monsters *monster, bolt & beem) return (false); // recent addition {GDL} - monsters won't throw if they can do melee. - // wastes valuable ammo, and most monsters are better at melee anyway. + // wastes valuable ammo, and most monsters are better at melee anyway. if (adjacent( beem.target_x, beem.target_y, monster->x, monster->y )) return (false); @@ -3360,364 +3539,367 @@ static bool handle_throw(struct monsters *monster, bolt & beem) // good idea? if (mons_should_fire( beem )) { - beem.beam_name[0] = '\0'; + beem.name.clear(); return (mons_throw( monster, beem, mon_item )); } return (false); } // end handle_throw() - -//--------------------------------------------------------------- -// -// handle_monsters -// -// This is the routine that controls monster AI. -// -//--------------------------------------------------------------- -void handle_monsters(void) +static void handle_monster_move(int i, monsters *monster) { - bool brkk = false; + bool brkk = false; struct bolt beem; - int i; + FixedArray show; - FixedArray < unsigned int, 19, 19 > show; + if (monster->hit_points > monster->max_hit_points) + monster->hit_points = monster->max_hit_points; -// losight(show, grd, you.x_pos, you.y_pos); + // monster just summoned (or just took stairs), skip this action + if (testbits( monster->flags, MF_JUST_SUMMONED )) + { + monster->flags &= ~MF_JUST_SUMMONED; + return; + } + + monster->speed_increment += (monster->speed * you.time_taken) / 10; - for (i = 0; i < MAX_MONSTERS; i++) + if (you.slow > 0) { - struct monsters *monster = &menv[i]; + monster->speed_increment += (monster->speed * you.time_taken) / 10; + } - if (monster->type != -1) + // Handle enchantments and clouds on nonmoving monsters: + if (monster->speed == 0) + { + if (env.cgrid[monster->x][monster->y] != EMPTY_CLOUD + && !mons_has_ench( monster, ENCH_SUBMERGED )) { - if (monster->hit_points > monster->max_hit_points) - monster->hit_points = monster->max_hit_points; + mons_in_cloud( monster ); + } - // monster just summoned (or just took stairs), skip this action - if (testbits( monster->flags, MF_JUST_SUMMONED )) - { - monster->flags &= ~MF_JUST_SUMMONED; - continue; - } + handle_enchantment( monster ); + } - monster->speed_increment += (monster->speed * you.time_taken) / 10; + // memory is decremented here for a reason -- we only want it + // decrementing once per monster "move" + if (monster->foe_memory > 0) + monster->foe_memory--; + + if (monster->type == MONS_GLOWING_SHAPESHIFTER) + mons_add_ench( monster, ENCH_GLOWING_SHAPESHIFTER ); + + // otherwise there are potential problems with summonings + if (monster->type == MONS_SHAPESHIFTER) + mons_add_ench( monster, ENCH_SHAPESHIFTER ); + + // We reset batty monsters from wander to seek here, instead + // of in handle_behaviour() since that will be called with + // every single movement, and we want these monsters to + // hit and run. -- bwr + if (monster->foe != MHITNOT + && monster->behaviour == BEH_WANDER + && testbits( monster->flags, MF_BATTY )) + { + monster->behaviour = BEH_SEEK; + } - if (you.slow > 0) - { - monster->speed_increment += (monster->speed * you.time_taken) / 10; - } + while (monster->speed_increment >= 80) + { // The continues & breaks are WRT this. + if (monster->type != -1 && monster->hit_points < 1) + break; - // Handle enchantments and clouds on nonmoving monsters: - if (monster->speed == 0) - { - if (env.cgrid[monster->x][monster->y] != EMPTY_CLOUD - && !mons_has_ench( monster, ENCH_SUBMERGED )) - { - mons_in_cloud( monster ); - } + monster->speed_increment -= 10; - handle_enchantment( monster ); - } + if (env.cgrid[monster->x][monster->y] != EMPTY_CLOUD) + { + if (mons_has_ench( monster, ENCH_SUBMERGED )) + break; + + if (monster->type == -1) + break; // problem with vortices - // memory is decremented here for a reason -- we only want it - // decrementing once per monster "move" - if (monster->foe_memory > 0) - monster->foe_memory--; - - if (monster->type == MONS_GLOWING_SHAPESHIFTER) - mons_add_ench( monster, ENCH_GLOWING_SHAPESHIFTER ); - - // otherwise there are potential problems with summonings - if (monster->type == MONS_SHAPESHIFTER) - mons_add_ench( monster, ENCH_SHAPESHIFTER ); - - // We reset batty monsters from wander to seek here, instead - // of in handle_behaviour() since that will be called with - // every single movement, and we want these monsters to - // hit and run. -- bwr - if (monster->foe != MHITNOT - && monster->behaviour == BEH_WANDER - && testbits( monster->flags, MF_BATTY )) + mons_in_cloud(monster); + + if (monster->type == -1) { - monster->behaviour = BEH_SEEK; + monster->speed_increment = 1; + break; } + } - while (monster->speed_increment >= 80) - { // The continues & breaks are WRT this. - if (monster->type != -1 && monster->hit_points < 1) - break; + handle_behaviour(monster); - monster->speed_increment -= 10; + if (handle_enchantment(monster)) + continue; - if (env.cgrid[monster->x][monster->y] != EMPTY_CLOUD) - { - if (mons_has_ench( monster, ENCH_SUBMERGED )) - break; + // submerging monsters will hide from clouds + if (monster_can_submerge(monster->type, grd[monster->x][monster->y]) + && env.cgrid[monster->x][monster->y] != EMPTY_CLOUD) + { + mons_add_ench( monster, ENCH_SUBMERGED ); + } - if (monster->type == -1) - break; // problem with vortices + // regenerate: + if (monster_descriptor(monster->type, MDSC_REGENERATES) - mons_in_cloud(monster); + || (monster->type == MONS_FIRE_ELEMENTAL + && (grd[monster->x][monster->y] == DNGN_LAVA + || env.cgrid[monster->x][monster->y] == CLOUD_FIRE + || env.cgrid[monster->x][monster->y] == CLOUD_FIRE_MON)) - if (monster->type == -1) - { - monster->speed_increment = 1; - break; - } - } + || (monster->type == MONS_WATER_ELEMENTAL + && (grd[monster->x][monster->y] == DNGN_SHALLOW_WATER + || grd[monster->x][monster->y] == DNGN_DEEP_WATER)) - handle_behaviour(monster); + || (monster->type == MONS_AIR_ELEMENTAL + && env.cgrid[monster->x][monster->y] == EMPTY_CLOUD + && one_chance_in(3)) - if (handle_enchantment(monster)) - continue; + || one_chance_in(25)) + { + heal_monster(monster, 1, false); + } - // submerging monsters will hide from clouds - const int habitat = monster_habitat( monster->type ); - if (habitat != DNGN_FLOOR - && habitat == grd[monster->x][monster->y] - && env.cgrid[monster->x][monster->y] != EMPTY_CLOUD) - { - mons_add_ench( monster, ENCH_SUBMERGED ); - } + if (monster->speed >= 100) + continue; + + if (monster->type == MONS_ZOMBIE_SMALL + || monster->type == MONS_ZOMBIE_LARGE + || monster->type == MONS_SIMULACRUM_SMALL + || monster->type == MONS_SIMULACRUM_LARGE + || monster->type == MONS_SKELETON_SMALL + || monster->type == MONS_SKELETON_LARGE) + { + monster->max_hit_points = monster->hit_points; + } - // regenerate: - if (monster_descriptor(monster->type, MDSC_REGENERATES) + if (igrd[monster->x][monster->y] != NON_ITEM + && (mons_itemuse(monster->type) == MONUSE_WEAPONS_ARMOUR + || mons_itemuse(monster->type) == MONUSE_EATS_ITEMS + || monster->type == MONS_NECROPHAGE + || monster->type == MONS_GHOUL)) + { + if (handle_pickup(monster)) + continue; + } - || (monster->type == MONS_FIRE_ELEMENTAL - && (grd[monster->x][monster->y] == DNGN_LAVA - || env.cgrid[monster->x][monster->y] == CLOUD_FIRE - || env.cgrid[monster->x][monster->y] == CLOUD_FIRE_MON)) + // calculates mmov_x, mmov_y based on monster target. + handle_movement(monster); - || (monster->type == MONS_WATER_ELEMENTAL - && (grd[monster->x][monster->y] == DNGN_SHALLOW_WATER - || grd[monster->x][monster->y] == DNGN_DEEP_WATER)) + brkk = false; - || (monster->type == MONS_AIR_ELEMENTAL - && env.cgrid[monster->x][monster->y] == EMPTY_CLOUD - && one_chance_in(3)) + if (mons_has_ench( monster, ENCH_CONFUSION ) + || (monster->type == MONS_AIR_ELEMENTAL + && mons_has_ench( monster, ENCH_SUBMERGED ))) + { + mmov_x = random2(3) - 1; + mmov_y = random2(3) - 1; - || one_chance_in(25)) - { - heal_monster(monster, 1, false); - } + // bounds check: don't let confused monsters try to run + // off the map + if (monster->target_x + mmov_x < 0 + || monster->target_x + mmov_x >= GXM) + { + mmov_x = 0; + } - if (monster->speed >= 100) - continue; + if (monster->target_y + mmov_y < 0 + || monster->target_y + mmov_y >= GYM) + { + mmov_y = 0; + } - if (monster->type == MONS_ZOMBIE_SMALL - || monster->type == MONS_ZOMBIE_LARGE - || monster->type == MONS_SIMULACRUM_SMALL - || monster->type == MONS_SIMULACRUM_LARGE - || monster->type == MONS_SKELETON_SMALL - || monster->type == MONS_SKELETON_LARGE) - { - monster->max_hit_points = monster->hit_points; - } + if (mgrd[monster->x + mmov_x][monster->y + mmov_y] != NON_MONSTER + && (mmov_x != 0 || mmov_y != 0)) + { + mmov_x = 0; + mmov_y = 0; - if (igrd[monster->x][monster->y] != NON_ITEM - && (mons_itemuse(monster->type) == MONUSE_WEAPONS_ARMOUR - || mons_itemuse(monster->type) == MONUSE_EATS_ITEMS - || monster->type == MONS_NECROPHAGE - || monster->type == MONS_GHOUL)) + if (monsters_fight(i, mgrd[monster->x + mmov_x][monster->y + mmov_y])) { - if (handle_pickup(monster)) - continue; + brkk = true; } + } + } - // calculates mmov_x, mmov_y based on monster target. - handle_movement(monster); + if (brkk) + continue; - brkk = false; + handle_nearby_ability( monster ); - if (mons_has_ench( monster, ENCH_CONFUSION ) - || (monster->type == MONS_AIR_ELEMENTAL - && mons_has_ench( monster, ENCH_SUBMERGED ))) - { - mmov_x = random2(3) - 1; - mmov_y = random2(3) - 1; + beem.target_x = monster->target_x; + beem.target_y = monster->target_y; - // bounds check: don't let confused monsters try to run - // off the map - if (monster->target_x + mmov_x < 0 - || monster->target_x + mmov_x >= GXM) - { - mmov_x = 0; - } + if (monster->behaviour != BEH_SLEEP + && monster->behaviour != BEH_WANDER) + { + // prevents unfriendlies from nuking you from offscreen. + // How nice! + if (mons_friendly(monster) || mons_near(monster)) + { + if (handle_special_ability(monster, beem)) + continue; - if (monster->target_y + mmov_y < 0 - || monster->target_y + mmov_y >= GYM) - { - mmov_y = 0; - } + if (handle_potion(monster, beem)) + continue; - if (mgrd[monster->x + mmov_x][monster->y + mmov_y] != NON_MONSTER - && (mmov_x != 0 || mmov_y != 0)) - { - mmov_x = 0; - mmov_y = 0; + if (handle_scroll(monster)) + continue; - if (monsters_fight(i, mgrd[monster->x + mmov_x][monster->y + mmov_y])) - { - brkk = true; - } - } + // shapeshifters don't get spells + if (!mons_has_ench( monster, ENCH_GLOWING_SHAPESHIFTER, + ENCH_SHAPESHIFTER ) + || !mons_class_flag( monster->type, M_ACTUAL_SPELLS )) + { + if (handle_spell(monster, beem)) + continue; } - if (brkk) + if (handle_wand(monster, beem)) continue; - handle_nearby_ability( monster ); + if (handle_reaching(monster)) + continue; + } - beem.target_x = monster->target_x; - beem.target_y = monster->target_y; + if (handle_throw(monster, beem)) + continue; + } - if (monster->behaviour != BEH_SLEEP - && monster->behaviour != BEH_WANDER) + // see if we move into (and fight) an unfriendly monster + int targmon = mgrd[monster->x + mmov_x][monster->y + mmov_y]; + if (targmon != NON_MONSTER + && targmon != i + && !mons_aligned(i, targmon)) + { + // figure out if they fight + if (monsters_fight(i, targmon)) + { + if (testbits(monster->flags, MF_BATTY)) { - // prevents unfriendlies from nuking you from offscreen. - // How nice! - if (mons_friendly(monster) || mons_near(monster)) - { - if (handle_special_ability(monster, beem)) - continue; - - if (handle_potion(monster, beem)) - continue; - - if (handle_scroll(monster)) - continue; + monster->behaviour = BEH_WANDER; + monster->target_x = 10 + random2(GXM - 10); + monster->target_y = 10 + random2(GYM - 10); + // monster->speed_increment -= monster->speed; + } - // shapeshifters don't get spells - if (!mons_has_ench( monster, ENCH_GLOWING_SHAPESHIFTER, - ENCH_SHAPESHIFTER ) - || !mons_flag( monster->type, M_ACTUAL_SPELLS )) - { - if (handle_spell(monster, beem)) - continue; - } + mmov_x = 0; + mmov_y = 0; + brkk = true; + } + } - if (handle_wand(monster, beem)) - continue; + if (brkk) + continue; - if (handle_reaching(monster)) - continue; - } + if (monster->x + mmov_x == you.x_pos + && monster->y + mmov_y == you.y_pos) + { + bool isFriendly = mons_friendly(monster); + bool attacked = false; - if (handle_throw(monster, beem)) - continue; - } + if (!isFriendly) + { + monster_attack(i); + attacked = true; - // see if we move into (and fight) an unfriendly monster - int targmon = mgrd[monster->x + mmov_x][monster->y + mmov_y]; - if (targmon != NON_MONSTER - && targmon != i - && !mons_aligned(i, targmon)) + if (testbits(monster->flags, MF_BATTY)) { - // figure out if they fight - if (monsters_fight(i, targmon)) - { - if (testbits(monster->flags, MF_BATTY)) - { - monster->behaviour = BEH_WANDER; - monster->target_x = 10 + random2(GXM - 10); - monster->target_y = 10 + random2(GYM - 10); - // monster->speed_increment -= monster->speed; - } - - mmov_x = 0; - mmov_y = 0; - brkk = true; - } + monster->behaviour = BEH_WANDER; + monster->target_x = 10 + random2(GXM - 10); + monster->target_y = 10 + random2(GYM - 10); } + } - if (brkk) - continue; + if ((monster->type == MONS_GIANT_SPORE + || monster->type == MONS_BALL_LIGHTNING) + && monster->hit_points < 1) + { - if (monster->x + mmov_x == you.x_pos - && monster->y + mmov_y == you.y_pos) - { - bool isFriendly = mons_friendly(monster); - bool attacked = false; + // detach monster from the grid first, so it + // doesn't get hit by its own explosion (GDL) + mgrd[monster->x][monster->y] = NON_MONSTER; - if (!isFriendly) - { - monster_attack(i); - attacked = true; + spore_goes_pop(monster); + monster_cleanup(monster); + continue; + } - if (testbits(monster->flags, MF_BATTY)) - { - monster->behaviour = BEH_WANDER; - monster->target_x = 10 + random2(GXM - 10); - monster->target_y = 10 + random2(GYM - 10); - } - } + if (attacked) + { + mmov_x = 0; + mmov_y = 0; + continue; //break; + } + } - if ((monster->type == MONS_GIANT_SPORE - || monster->type == MONS_BALL_LIGHTNING) - && monster->hit_points < 1) - { + if (invalid_monster(monster) || mons_is_stationary(monster)) + continue; - // detach monster from the grid first, so it - // doesn't get hit by its own explosion (GDL) - mgrd[monster->x][monster->y] = NON_MONSTER; + monster_move(monster); - spore_goes_pop(monster); - monster_cleanup(monster); - continue; - } + // reevaluate behaviour, since the monster's + // surroundings have changed (it may have moved, + // or died for that matter. Don't bother for + // dead monsters. :) + if (monster->type != -1) + handle_behaviour(monster); - if (attacked) - { - mmov_x = 0; - mmov_y = 0; - continue; //break; - } - } + } // end while - if (monster->type == -1 || monster->type == MONS_OKLOB_PLANT - || monster->type == MONS_CURSE_SKULL - || (monster->type >= MONS_CURSE_TOE - && monster->type <= MONS_POTION_MIMIC)) - { - continue; - } + if (monster->type != -1 && monster->hit_points < 1) + { + if (monster->type == MONS_GIANT_SPORE + || monster->type == MONS_BALL_LIGHTNING) + { + // detach monster from the grid first, so it + // doesn't get hit by its own explosion (GDL) + mgrd[monster->x][monster->y] = NON_MONSTER; + + spore_goes_pop( monster ); + monster_cleanup( monster ); + return; + } + else + { + monster_die( monster, KILL_MISC, 0 ); + } + } +} - monster_move(monster); +//--------------------------------------------------------------- +// +// handle_monsters +// +// This is the routine that controls monster AI. +// +//--------------------------------------------------------------- +void handle_monsters(void) +{ + // Keep track of monsters that have already moved and don't allow + // them to move again. + memset(immobile_monster, 0, sizeof immobile_monster); - // reevaluate behaviour, since the monster's - // surroundings have changed (it may have moved, - // or died for that matter. Don't bother for - // dead monsters. :) - if (monster->type != -1) - handle_behaviour(monster); + for (int i = 0; i < MAX_MONSTERS; i++) + { + struct monsters *monster = &menv[i]; - } // end while + if (monster->type == -1 || immobile_monster[i]) + continue; - if (monster->type != -1 && monster->hit_points < 1) - { - if (monster->type == MONS_GIANT_SPORE - || monster->type == MONS_BALL_LIGHTNING) - { - // detach monster from the grid first, so it - // doesn't get hit by its own explosion (GDL) - mgrd[monster->x][monster->y] = NON_MONSTER; + const int mx = monster->x, + my = monster->y; + handle_monster_move(i, monster); - spore_goes_pop( monster ); - monster_cleanup( monster ); - continue; - } - else - { - monster_die( monster, KILL_MISC, 0 ); - } - } - } // end of if (mons_class != -1) + if (!invalid_monster(monster) + && (monster->x != mx || monster->y != my)) + immobile_monster[i] = true; } // end of for loop // Clear any summoning flags so that lower indiced // monsters get their actions in the next round. - for (i = 0; i < MAX_MONSTERS; i++) + for (int i = 0; i < MAX_MONSTERS; i++) { menv[i].flags &= ~MF_JUST_SUMMONED; } @@ -3744,10 +3926,7 @@ static bool handle_pickup(struct monsters *monster) if (monster->behaviour == BEH_SLEEP) return (false); - if (monster->type == MONS_JELLY - || monster->type == MONS_BROWN_OOZE - || monster->type == MONS_ACID_BLOB - || monster->type == MONS_ROYAL_JELLY) + if (mons_itemuse(monster->type) == MONUSE_EATS_ITEMS) { int hps_gained = 0; int max_eat = roll_dice( 1, 10 ); @@ -3785,7 +3964,7 @@ static bool handle_pickup(struct monsters *monster) if (quant > max_eat - eaten) quant = max_eat - eaten; - hps_gained += (quant * mass_item( mitm[item] )) / 20 + quant; + hps_gained += (quant * item_mass( mitm[item] )) / 20 + quant; eaten += quant; } else @@ -3822,10 +4001,10 @@ static bool handle_pickup(struct monsters *monster) if (!monsterNearby) strcat(info, " distant"); strcat(info, " slurping noise."); - mpr(info); + mpr(info, MSGCH_SOUND); } - if (mons_flag( monster->type, M_SPLITS )) + if (mons_class_flag( monster->type, M_SPLITS )) { const int reqd = (monster->hit_dice <= 6) ? 50 : monster->hit_dice * 8; @@ -3855,8 +4034,8 @@ static bool handle_pickup(struct monsters *monster) // wimpy monsters (Kob, gob) shouldn't pick up halberds etc // of course, this also block knives {dlb}: - if ((mons_charclass(monster->type) == MONS_KOBOLD - || mons_charclass(monster->type) == MONS_GOBLIN) + if ((mons_species(monster->type) == MONS_KOBOLD + || mons_species(monster->type) == MONS_GOBLIN) && property( mitm[item], PWPN_HIT ) <= 0) { return (false); @@ -4058,6 +4237,135 @@ static bool handle_pickup(struct monsters *monster) return (true); } // end handle_pickup() +static void jelly_grows(monsters *monster) +{ + if (!silenced(you.x_pos, you.y_pos) + && !silenced(monster->x, monster->y)) + { + strcpy(info, "You hear a"); + if (!mons_near(monster)) + strcat(info, " distant"); + strcat(info, " slurping noise."); + mpr(info, MSGCH_SOUND); + } + + monster->hit_points += 5; + + // note here, that this makes jellies "grow" {dlb}: + if (monster->hit_points > monster->max_hit_points) + monster->max_hit_points = monster->hit_points; + + if (mons_class_flag( monster->type, M_SPLITS )) + { + // and here is where the jelly might divide {dlb} + const int reqd = (monster->hit_dice < 6) ? 50 + : monster->hit_dice * 8; + + if (monster->hit_points >= reqd) + jelly_divide(monster); + } +} + +static bool mons_can_displace(const monsters *mpusher, const monsters *mpushee) +{ + if (invalid_monster(mpusher) || invalid_monster(mpushee)) + return (false); + + const int ipushee = monster_index(mpushee); + if (ipushee < 0 || ipushee >= MAX_MONSTERS) + return (false); + + if (immobile_monster[ipushee]) + return (false); + + // Confused monsters can't be pushed past, sleeping monsters + // can't push. Note that sleeping monsters can't be pushed + // past, either, but they may be woken up by a crowd trying to + // elbow past them, and the wake-up check happens downstream. + if (mons_is_confused(mpusher) || mons_is_confused(mpushee) + || mons_is_paralysed(mpusher) || mons_is_paralysed(mpushee) + || mons_is_sleeping(mpusher)) + return (false); + + // Batty monsters are unpushable + if (mons_is_batty(mpusher) || mons_is_batty(mpushee)) + return (false); + + if (!monster_shover(mpusher)) + return (false); + + if (!monster_senior(mpusher, mpushee)) + return (false); + + return (true); +} + +static bool monster_swaps_places( monsters *mon, int mx, int my ) +{ + if (!mx && !my) + return (false); + + int targmon = mgrd[mon->x + mx][mon->y + my]; + if (targmon == MHITNOT || targmon == MHITYOU) + return (false); + + monsters *m2 = &menv[targmon]; + if (!mons_can_displace(mon, m2)) + return (false); + + if (mons_is_sleeping(m2)) + { + if (one_chance_in(2)) + { +#ifdef DEBUG_DIAGNOSTICS + char mname[ITEMNAME_SIZE]; + moname(m2->type, true, DESC_PLAIN, mname); + mprf(MSGCH_DIAGNOSTICS, + "Alerting monster %s at (%d,%d)", mname, m2->x, m2->y); +#endif + behaviour_event( m2, ME_ALERT, MHITNOT ); + } + return (false); + } + + // Check that both monsters will be happy at their proposed new locations. + const int cx = mon->x, cy = mon->y, + nx = mon->x + mx, ny = mon->y + my; + if (!habitat_okay(mon, grd[nx][ny]) + || !habitat_okay(m2, grd[cx][cy])) + return (false); + + // Okay, do the swap! +#ifdef DEBUG_DIAGNOSTICS + char mname[ITEMNAME_SIZE]; + moname(mon->type, true, DESC_PLAIN, mname); + mprf(MSGCH_DIAGNOSTICS, + "Swap: %s (%d,%d)->(%d,%d) (%d;%d)", + mname, mon->x, mon->y, nx, ny, mon->speed_increment, mon->speed); +#endif + mon->x = nx; + mon->y = ny; + mgrd[nx][ny] = monster_index(mon); + +#ifdef DEBUG_DIAGNOSTICS + moname(m2->type, true, DESC_PLAIN, mname); + mprf(MSGCH_DIAGNOSTICS, + "Swap: %s (%d,%d)->(%d,%d) (%d;%d)", + mname, m2->x, m2->y, cx, cy, mon->speed_increment, mon->speed); +#endif + m2->x = cx; + m2->y = cy; + const int m2i = monster_index(m2); + ASSERT(m2i >= 0 && m2i < MAX_MONSTERS); + mgrd[cx][cy] = m2i; + immobile_monster[m2i] = true; + + mons_trap(mon); + mons_trap(m2); + + return (false); +} + static void monster_move(struct monsters *monster) { FixedArray < bool, 3, 3 > good_move; @@ -4078,7 +4386,7 @@ static void monster_move(struct monsters *monster) if (mons_flies(monster) > 0 || habitat != DNGN_FLOOR - || mons_flag( monster->type, M_AMPHIBIOUS )) + || mons_class_flag( monster->type, M_AMPHIBIOUS )) { okmove = MINMOVE; } @@ -4088,12 +4396,11 @@ static void monster_move(struct monsters *monster) for (count_y = 0; count_y < 3; count_y++) { good_move[count_x][count_y] = true; + const int targ_x = monster->x + count_x - 1; const int targ_y = monster->y + count_y - 1; - int target_grid = grd[targ_x][targ_y]; - - const int targ_cloud = env.cgrid[ targ_x ][ targ_y ]; - const int curr_cloud = env.cgrid[ monster->x ][ monster->y ]; + // [ds] Bounds check was after grd[targ_x][targ_y] which would + // trigger an ASSERT. Moved it up. // bounds check - don't consider moving out of grid! if (targ_x < 0 || targ_x >= GXM || targ_y < 0 || targ_y >= GYM) @@ -4102,6 +4409,18 @@ static void monster_move(struct monsters *monster) continue; } + int target_grid = grd[targ_x][targ_y]; + + const int targ_cloud_num = env.cgrid[ targ_x ][ targ_y ]; + const int targ_cloud_type = + targ_cloud_num == EMPTY_CLOUD? CLOUD_NONE + : env.cloud[targ_cloud_num].type; + + const int curr_cloud_num = env.cgrid[ monster->x ][ monster->y ]; + const int curr_cloud_type = + curr_cloud_num == EMPTY_CLOUD? CLOUD_NONE + : env.cloud[curr_cloud_num].type; + if (target_grid == DNGN_DEEP_WATER) deep_water_available = true; @@ -4144,10 +4463,10 @@ static void monster_move(struct monsters *monster) // Water elementals avoid fire and heat if (monster->type == MONS_WATER_ELEMENTAL && (target_grid == DNGN_LAVA - || targ_cloud == CLOUD_FIRE - || targ_cloud == CLOUD_FIRE_MON - || targ_cloud == CLOUD_STEAM - || targ_cloud == CLOUD_STEAM_MON)) + || targ_cloud_type == CLOUD_FIRE + || targ_cloud_type == CLOUD_FIRE_MON + || targ_cloud_type == CLOUD_STEAM + || targ_cloud_type == CLOUD_STEAM_MON)) { good_move[count_x][count_y] = false; continue; @@ -4158,8 +4477,8 @@ static void monster_move(struct monsters *monster) && (target_grid == DNGN_DEEP_WATER || target_grid == DNGN_SHALLOW_WATER || target_grid == DNGN_BLUE_FOUNTAIN - || targ_cloud == CLOUD_COLD - || targ_cloud == CLOUD_COLD_MON)) + || targ_cloud_type == CLOUD_COLD + || targ_cloud_type == CLOUD_COLD_MON)) { good_move[count_x][count_y] = false; continue; @@ -4167,12 +4486,14 @@ static void monster_move(struct monsters *monster) // Submerged water creatures avoid the shallows where // they would be forced to surface. -- bwr + // [dshaligram] Monsters now prefer to head for deep water only if + // they're low on hitpoints. No point in hiding if they want a + // fight. if (habitat == DNGN_DEEP_WATER && (targ_x != you.x_pos || targ_y != you.y_pos) && target_grid != DNGN_DEEP_WATER && grd[monster->x][monster->y] == DNGN_DEEP_WATER - && (mons_has_ench( monster, ENCH_SUBMERGED ) - || monster->hit_points < (monster->max_hit_points * 3) / 4)) + && monster->hit_points < (monster->max_hit_points * 3) / 4) { good_move[count_x][count_y] = false; continue; @@ -4182,11 +4503,16 @@ static void monster_move(struct monsters *monster) // we're hostile (even if we're heading somewhere // else) - // smacking another monster is good, if the monsters + // smacking another monster is good, if the monsters // are aligned differently if (mgrd[targ_x][targ_y] != NON_MONSTER) { - if (mons_aligned(monster_index(monster), mgrd[targ_x][targ_y])) + const int thismonster = monster_index(monster), + targmonster = mgrd[targ_x][targ_y]; + if (mons_aligned(thismonster, targmonster) + && targmonster != MHITNOT + && targmonster != MHITYOU + && !mons_can_displace(monster, &menv[targmonster])) { good_move[count_x][count_y] = false; continue; @@ -4205,15 +4531,15 @@ static void monster_move(struct monsters *monster) } } - if (targ_cloud != EMPTY_CLOUD) + if (targ_cloud_num != EMPTY_CLOUD) { - if (curr_cloud != EMPTY_CLOUD - && env.cloud[targ_cloud].type == env.cloud[curr_cloud].type) + if (curr_cloud_num != EMPTY_CLOUD + && targ_cloud_type == curr_cloud_type) { continue; } - switch (env.cloud[ targ_cloud ].type) + switch (targ_cloud_type) { case CLOUD_FIRE: case CLOUD_FIRE_MON: @@ -4252,7 +4578,7 @@ static void monster_move(struct monsters *monster) continue; break; - // this isn't harmful, but dumb critters might think so. + // this isn't harmful, but dumb critters might think so. case CLOUD_GREY_SMOKE: case CLOUD_GREY_SMOKE_MON: if (mons_intel(monster->type) > I_ANIMAL || coinflip()) @@ -4313,42 +4639,20 @@ static void monster_move(struct monsters *monster) { grd[monster->x + mmov_x][monster->y + mmov_y] = DNGN_FLOOR; - if (!silenced(you.x_pos, you.y_pos) - && !silenced(monster->x, monster->y)) - { - strcpy(info, "You hear a"); - if (!mons_near(monster)) - strcat(info, " distant"); - strcat(info, " slurping noise."); - mpr(info); - } - - monster->hit_points += 5; - - // note here, that this makes jellies "grow" {dlb}: - if (monster->hit_points > monster->max_hit_points) - monster->max_hit_points = monster->hit_points; - - if (mons_flag( monster->type, M_SPLITS )) - { - // and here is where the jelly might divide {dlb} - const int reqd = (monster->hit_dice < 6) ? 50 - : monster->hit_dice * 8; - - if (monster->hit_points >= reqd) - jelly_divide(monster); - } + jelly_grows(monster); } // done door-eating jellies - // water creatures have a preferance for water they can hide in -- bwr + // water creatures have a preference for water they can hide in -- bwr + // [ds] Weakened the powerful attraction to deep water if the monster + // is in good health. if (habitat == DNGN_DEEP_WATER && deep_water_available && grd[monster->x][monster->y] != DNGN_DEEP_WATER && grd[monster->x + mmov_x][monster->y + mmov_y] != DNGN_DEEP_WATER && (monster->x + mmov_x != you.x_pos || monster->y + mmov_y != you.y_pos) - && (coinflip() + && (one_chance_in(3) || monster->hit_points <= (monster->max_hit_points * 3) / 4)) { count = 0; @@ -4374,10 +4678,10 @@ static void monster_move(struct monsters *monster) } - // now, if a monster can't move in its intended direction, try - // either side. If they're both good, move in whichever dir + // now, if a monster can't move in its intended direction, try + // either side. If they're both good, move in whichever dir // gets it closer(farther for fleeing monsters) to its target. - // If neither does, do nothing. + // If neither does, do nothing. if (good_move[mmov_x + 1][mmov_y + 1] == false) { int current_distance = grid_distance( monster->x, monster->y, @@ -4401,7 +4705,7 @@ static void monster_move(struct monsters *monster) int dist[2]; - // first 1 away, then 2 (3 is silly) + // first 1 away, then 2 (3 is silly) for (int j = 1; j <= 2; j++) { int sdir, inc; @@ -4488,7 +4792,7 @@ forget_it: grd[monster->x + mmov_x][monster->y + mmov_y] = DNGN_FLOOR; if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear a grinding noise."); + mpr("You hear a grinding noise.", MSGCH_SOUND); } } @@ -4523,7 +4827,12 @@ forget_it: int targmon = mgrd[monster->x + mmov_x][monster->y + mmov_y]; if (targmon != NON_MONSTER) { - monsters_fight(monster_index(monster), targmon); + if (mons_aligned(monster_index(monster), targmon)) + monster_swaps_places(monster, mmov_x, mmov_y); + else + monsters_fight(monster_index(monster), targmon); + + // If the monster swapped places, the work's already done. mmov_x = 0; mmov_y = 0; } @@ -4535,9 +4844,24 @@ forget_it: 2 + random2(4) ); } + if (monster->type == MONS_ROTTING_DEVIL + || monster->type == MONS_CURSE_TOE) + { + place_cloud( CLOUD_MIASMA_MON, monster->x, monster->y, + 2 + random2(3) ); + } + /* this appears to be the real one, ie where the movement occurs: */ monster->x += mmov_x; monster->y += mmov_y; + + if (monster->type == MONS_CURSE_TOE) + { + // Curse toes are a special case; they can only move at half their + // attack rate. To simulate that, the toe loses another action's + // worth of energy when moving. + monster->speed_increment -= 10; + } } else { @@ -4552,9 +4876,7 @@ forget_it: mgrd[monster->x][monster->y] = monster_index(monster); // monsters stepping on traps: - if (grd[monster->x][monster->y] >= DNGN_TRAP_MECHANICAL - && grd[monster->x][monster->y] <= DNGN_UNDISCOVERED_TRAP - && (mmov_x != 0 || mmov_y != 0)) + if (mmov_x != 0 || mmov_y != 0) { mons_trap(monster); } @@ -4568,7 +4890,7 @@ static bool plant_spit(struct monsters *monster, struct bolt &pbolt) char spit_string[INFO_SIZE]; // setup plant spit - strcpy( pbolt.beam_name, "acid" ); + pbolt.name = "acid"; pbolt.type = SYM_ZAP; pbolt.range = 9; pbolt.rangeMax = 9; @@ -4578,7 +4900,7 @@ static bool plant_spit(struct monsters *monster, struct bolt &pbolt) pbolt.damage = dice_def( 3, 7 ); pbolt.hit = 20 + (3 * monster->hit_dice); pbolt.thrower = KILL_MON_MISSILE; - pbolt.aux_source = NULL; + pbolt.aux_source.clear(); // fire tracer fire_tracer(monster, pbolt); @@ -4718,7 +5040,8 @@ static void mons_in_cloud(struct monsters *monster) case CLOUD_MIASMA_MON: simple_monster_message(monster, " is engulfed in a dark miasma!"); - if (mons_holiness(monster->type) != MH_NATURAL) + if (mons_holiness(monster) != MH_NATURAL + || monster->type == MONS_DEATH_DRAKE) return; poison_monster(monster, (env.cloud[wc].type == CLOUD_MIASMA)); @@ -4891,7 +5214,7 @@ bool message_current_target(void) } // end message_current_target() // aaah, the simple joys of pointer arithmetic! {dlb}: -unsigned int monster_index(struct monsters *monster) +unsigned int monster_index(const monsters *monster) { return (monster - menv.buffer()); } // end monster_index() @@ -4912,6 +5235,9 @@ bool hurt_monster(struct monsters * victim, int damage_dealt) bool heal_monster(struct monsters * patient, int health_boost, bool permit_growth) { + if (mons_is_statue(patient->type)) + return (false); + if (health_boost < 1) return (false); else if (!permit_growth && patient->hit_points == patient->max_hit_points) @@ -4987,3 +5313,98 @@ static int map_wand_to_mspell(int wand_type) return (mzap); } + +void seen_monster(struct monsters *monster) +{ + if ( monster->flags & MF_SEEN ) + return; + + // First time we've seen this particular monster + monster->flags |= MF_SEEN; + + if ( MONST_INTERESTING(monster) && + monster->type != MONS_PANDEMONIUM_DEMON && + monster->type != MONS_PLAYER_GHOST ) + { + char namebuf[ITEMNAME_SIZE]; + moname(monster->type, true, DESC_NOCAP_A, namebuf); + take_note(Note(NOTE_SEEN_MONSTER, monster->type, 0, namebuf)); + } +} + +//--------------------------------------------------------------- +// +// shift_monster +// +// Moves a monster to approximately (x,y) and returns true +// if monster was moved. +// +//--------------------------------------------------------------- +bool shift_monster( struct monsters *mon, int x, int y ) +{ + bool found_move = false; + + int i, j; + int tx, ty; + int nx = 0, ny = 0; + + int count = 0; + + if (x == 0 && y == 0) + { + // try and find a random floor space some distance away + for (i = 0; i < 50; i++) + { + tx = 5 + random2( GXM - 10 ); + ty = 5 + random2( GYM - 10 ); + + int dist = grid_distance(x, y, tx, ty); + if (grd[tx][ty] == DNGN_FLOOR && dist > 10) + break; + } + + if (i == 50) + return (false); + } + + for (i = -1; i <= 1; i++) + { + for (j = -1; j <= 1; j++) + { + tx = x + i; + ty = y + j; + + if (tx < 5 || tx > GXM - 5 || ty < 5 || ty > GXM - 5) + continue; + + // won't drop on anything but vanilla floor right now + if (grd[tx][ty] != DNGN_FLOOR) + continue; + + if (mgrd[tx][ty] != NON_MONSTER) + continue; + + if (tx == you.x_pos && ty == you.y_pos) + continue; + + count++; + if (one_chance_in(count)) + { + nx = tx; + ny = ty; + found_move = true; + } + } + } + + if (found_move) + { + const int mon_index = mgrd[mon->x][mon->y]; + mgrd[mon->x][mon->y] = NON_MONSTER; + mgrd[nx][ny] = mon_index; + mon->x = nx; + mon->y = ny; + } + + return (found_move); +} diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h index f6202f23ae..dbbb85aeae 100644 --- a/crawl-ref/source/monstuff.h +++ b/crawl-ref/source/monstuff.h @@ -3,6 +3,8 @@ * Summary: Misc monster related functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -16,6 +18,8 @@ // useful macro #define SAME_ATTITUDE(x) (mons_friendly(x)?BEH_FRIENDLY:BEH_HOSTILE) +#define MONST_INTERESTING(x) (x->flags & MF_INTERESTING) + // for definition of type monsters {dlb} #include "externs.h" // for definition of type monsters {dlb} @@ -65,7 +69,7 @@ bool curse_an_item(char which, char power); /* *********************************************************************** * called from: fight * *********************************************************************** */ -void monster_blink(struct monsters *monster); +bool monster_blink(struct monsters *monster); /* *********************************************************************** @@ -131,7 +135,7 @@ bool message_current_target(void); /* *********************************************************************** * called from: xxx * *********************************************************************** */ -unsigned int monster_index(struct monsters *monster); +unsigned int monster_index(const monsters *monster); // last updated 08jun2000 {dlb} @@ -147,5 +151,11 @@ bool hurt_monster(struct monsters *victim, int damage_dealt); * *********************************************************************** */ bool heal_monster(struct monsters *patient, int health_boost, bool permit_growth); +/* *********************************************************************** + * called from: monplace - spells2 - view + * *********************************************************************** */ +void seen_monster(struct monsters *monster); + +bool shift_monster( struct monsters *mon, int x = 0, int y = 0 ); #endif diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc index 38d0d8c885..c47363ad7d 100644 --- a/crawl-ref/source/mstuff2.cc +++ b/crawl-ref/source/mstuff2.cc @@ -3,6 +3,8 @@ * Summary: Misc monster related functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <5> 31 July 2000 JDJ Fixed mon_throw to use lnchType. @@ -26,7 +28,9 @@ #include "beam.h" #include "debug.h" #include "effects.h" +#include "item_use.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "misc.h" #include "monplace.h" @@ -38,7 +42,6 @@ #include "spl-cast.h" #include "stuff.h" #include "view.h" -#include "wpn-misc.h" static unsigned char monster_abjuration(int pow, bool test); @@ -49,6 +52,9 @@ static unsigned char monster_abjuration(int pow, bool test); // to be some sort of trap prior to function call: {dlb} void mons_trap(struct monsters *monster) { + if (!is_trap_square(monster->x, monster->y)) + return; + int temp_rand = 0; // probability determination {dlb} // single calculation permissible {dlb} @@ -90,42 +96,42 @@ void mons_trap(struct monsters *monster) { case TRAP_DART: projectileFired = true; - strcpy(beem.beam_name, " dart"); + beem.name = " dart"; beem.damage = dice_def( 1, 4 ); beem.colour = OBJ_MISSILES; beem.type = MI_DART; break; case TRAP_NEEDLE: projectileFired = true; - strcpy(beem.beam_name, " needle"); + beem.name = " needle"; beem.damage = dice_def( 1, 0 ); beem.colour = OBJ_MISSILES; beem.type = MI_NEEDLE; break; case TRAP_ARROW: projectileFired = true; - strcpy(beem.beam_name, "n arrow"); + beem.name = "n arrow"; beem.damage = dice_def( 1, 7 ); beem.colour = OBJ_MISSILES; beem.type = MI_ARROW; break; case TRAP_SPEAR: projectileFired = true; - strcpy(beem.beam_name, " spear"); + beem.name = " spear"; beem.damage = dice_def( 1, 10 ); beem.colour = OBJ_WEAPONS; beem.type = WPN_SPEAR; break; case TRAP_BOLT: projectileFired = true; - strcpy(beem.beam_name, " bolt"); + beem.name = " bolt"; beem.damage = dice_def( 1, 13 ); beem.colour = OBJ_MISSILES; beem.type = MI_BOLT; break; case TRAP_AXE: projectileFired = true; - strcpy(beem.beam_name, "n axe"); + beem.name = "n axe"; beem.damage = dice_def( 1, 15 ); beem.colour = OBJ_WEAPONS; beem.type = WPN_HAND_AXE; @@ -229,7 +235,7 @@ void mons_trap(struct monsters *monster) temp_rand = random2(16); beem.thrower = KILL_MON; // probably unnecessary - beem.aux_source = NULL; + beem.aux_source.clear(); if (mons_friendly(monster)) { @@ -282,13 +288,11 @@ void mons_trap(struct monsters *monster) if (monsterNearby) { - snprintf( info, INFO_SIZE, "A%s %s %s%s!", - beem.beam_name, + mprf("A%s %s %s%s!", + beem.name.c_str(), (damage_taken >= 0) ? "hits" : "misses", ptr_monam( monster, DESC_NOCAP_THE ), - (damage_taken == 0) ? ", but does no damage" : "" ); - - mpr(info); + (damage_taken == 0) ? ", but does no damage" : ""); } if (apply_poison) @@ -362,8 +366,12 @@ void mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast) switch (spell_cast) { + case MS_SUMMON_SMALL_MAMMALS: case MS_VAMPIRE_SUMMON: - sumcount2 = 3 + random2(3) + monster->hit_dice / 5; + if ( spell_cast == MS_SUMMON_SMALL_MAMMALS ) + sumcount2 = 1 + random2(4); + else + sumcount2 = 3 + random2(3) + monster->hit_dice / 5; for (sumcount = 0; sumcount < sumcount2; sumcount++) { @@ -486,6 +494,28 @@ void mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast) monster->x, monster->y, monster->foe, 250 ); return; + case MS_SUMMON_MUSHROOMS: // Summon swarms of icky crawling fungi. + if (!mons_friendly(monster) && monsterNearby + && monster_abjuration(1, true) > 0 && coinflip()) + { + monster_abjuration( monster->hit_dice * 10, false ); + return; + } + + sumcount2 = 1 + random2(2) + random2( monster->hit_dice / 4 + 1 ); + + duration = ENCH_ABJ_II + monster->hit_dice / 5; + if (duration > ENCH_ABJ_VI) + duration = ENCH_ABJ_VI; + + for (int i = 0; i < sumcount2; ++i) + create_monster(MONS_WANDERING_MUSHROOM, duration, + SAME_ATTITUDE(monster), + monster->x, monster->y, + monster->foe, + 250); + return; + case MS_SUMMON_UNDEAD: // summon undead around player if (!mons_friendly(monster) && monsterNearby && monster_abjuration(1, true) > 0 && coinflip()) @@ -506,7 +536,7 @@ void mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast) { summonik = random2(241); // hmmmm ... {dlb} } - while (mons_holiness(summonik) != MH_UNDEAD); + while (mons_class_holiness(summonik) != MH_UNDEAD); create_monster(summonik, duration, SAME_ATTITUDE(monster), you.x_pos, you.y_pos, monster->foe, 250); @@ -519,7 +549,7 @@ void mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast) simple_monster_message(monster, " calls on the powers of Hell!"); - torment(monster->x, monster->y); + torment(monster_index(monster), monster->x, monster->y); return; case MS_SUMMON_DEMON_GREATER: @@ -544,6 +574,29 @@ void mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast) } return; + // Journey -- Added in Summon Lizards and Draconian + case MS_SUMMON_LIZARDS: + if (!mons_friendly( monster ) && !monsterNearby && + monster_abjuration( 1, true ) > 0 && coinflip()) + { + monster_abjuration( monster->hit_dice * 10, false ); + return; + } + + sumcount2 = 1 + random2(3) + random2( monster->hit_dice / 5 + 1 ); + + duration = ENCH_ABJ_II + monster->hit_dice / 10; + if (duration > ENCH_ABJ_VI) + duration = ENCH_ABJ_VI; + + for (sumcount = 0; sumcount < sumcount2; sumcount++) + { + create_monster( rand_dragon( DRAGON_LIZARD ), duration, + SAME_ATTITUDE(monster), + monster->x, monster->y, monster->foe, 250 ); + } + break; + case MS_CANTRIP: // Monster spell of uselessness, just prints a message. // This spell exists so that some monsters with really strong @@ -633,6 +686,7 @@ void setup_mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cas // fire_tracer, or beam. switch (spell_cast) { + case MS_SUMMON_SMALL_MAMMALS: case MS_VAMPIRE_SUMMON: case MS_LEVEL_SUMMON: // summon anything appropriate for level case MS_FAKE_RAKSHASA_SUMMON: @@ -642,6 +696,8 @@ void setup_mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cas case MS_SUMMON_UFETUBUS: case MS_SUMMON_BEAST: // Geryon case MS_SUMMON_UNDEAD: // summon undead around player + case MS_SUMMON_MUSHROOMS: + case MS_SUMMON_LIZARDS: case MS_TORMENT: case MS_SUMMON_DEMON_GREATER: case MS_CANTRIP: @@ -653,7 +709,7 @@ void setup_mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cas // Need to correct this for power of spellcaster int power = 12 * monster->hit_dice; - struct SBeam theBeam = mons_spells(spell_cast, power); + struct bolt theBeam = mons_spells(spell_cast, power); pbolt.colour = theBeam.colour; pbolt.range = theBeam.range; @@ -663,18 +719,19 @@ void setup_mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cas pbolt.ench_power = theBeam.ench_power; pbolt.type = theBeam.type; pbolt.flavour = theBeam.flavour; - pbolt.thrower = theBeam.thrown; - pbolt.aux_source = NULL; - strcpy( pbolt.beam_name, theBeam.name.c_str() ); - pbolt.isBeam = theBeam.isBeam; + pbolt.thrower = theBeam.thrower; + pbolt.aux_source.clear(); + pbolt.name = theBeam.name; + pbolt.is_beam = theBeam.is_beam; pbolt.source_x = monster->x; pbolt.source_y = monster->y; - pbolt.isTracer = false; + pbolt.is_tracer = false; + pbolt.is_explosion = theBeam.is_explosion; - if (pbolt.beam_name[0] && pbolt.beam_name[0] != '0') - pbolt.aux_source = pbolt.beam_name; + if (pbolt.name.length() && pbolt.name[0] != '0') + pbolt.aux_source = pbolt.name; else - pbolt.aux_source = NULL; + pbolt.aux_source.clear(); if (spell_cast == MS_HASTE || spell_cast == MS_INVIS @@ -683,16 +740,25 @@ void setup_mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cas pbolt.target_x = monster->x; pbolt.target_y = monster->y; } + + // Convenience for the hapless innocent who assumes that this + // damn function does all possible setup. [ds] + if (!pbolt.target_x && !pbolt.target_y) + { + pbolt.target_x = monster->target_x; + pbolt.target_y = monster->target_y; + } } // end setup_mons_cast() -void monster_teleport(struct monsters *monster, bool instan) +void monster_teleport(struct monsters *monster, bool instan, bool silent) { if (!instan) { if (mons_del_ench(monster, ENCH_TP_I, ENCH_TP_IV)) { - simple_monster_message(monster, " seems more stable."); + if (!silent) + simple_monster_message(monster, " seems more stable."); } else mons_add_ench(monster, (coinflip() ? ENCH_TP_III : ENCH_TP_IV )); @@ -700,13 +766,14 @@ void monster_teleport(struct monsters *monster, bool instan) return; } - simple_monster_message(monster, " disappears!"); + bool was_seen = player_monster_visible(monster) && mons_near(monster); + + if (!silent) + simple_monster_message(monster, " disappears!"); // pick the monster up mgrd[monster->x][monster->y] = NON_MONSTER; - char ogrid = monster_habitat(monster->type); - int newx, newy; while(true) { @@ -717,12 +784,7 @@ void monster_teleport(struct monsters *monster, bool instan) if (mgrd[newx][newy] != NON_MONSTER) continue; - // monsters going to the same habitat - if (ogrid == grd[newx][newy]) - break; - - // DEEP_WATER monsters can be teleported to SHALLOW_WATER - if (ogrid == DNGN_DEEP_WATER && grd[newx][newy] == DNGN_SHALLOW_WATER) + if (monster_habitable_grid(monster, grd[newx][newy])) break; } @@ -735,46 +797,85 @@ void monster_teleport(struct monsters *monster, bool instan) if (mons_is_mimic( monster->type )) { monster->type = MONS_GOLD_MIMIC + random2(5); - monster->number = get_mimic_colour( monster ); + monster->colour = get_mimic_colour( monster ); + } + + if (!silent) + { + if (was_seen) + simple_monster_message(monster, " reappears nearby!"); + else + simple_monster_message(monster, " appears out of thin air!"); } + + if (player_monster_visible(monster) && mons_near(monster)) + seen_monster(monster); } // end monster_teleport() void setup_dragon(struct monsters *monster, struct bolt &pbolt) { - strcpy(pbolt.beam_name, ptr_monam( monster, DESC_PLAIN )); + const int type = (mons_genus( monster->type ) == MONS_DRACONIAN) + ? draco_subspecies( monster ) : monster->type; + int scaling = 100; + + pbolt.name = ptr_monam( monster, DESC_PLAIN ); - switch (monster->type) + switch (type) { case MONS_FIREDRAKE: case MONS_HELL_HOUND: case MONS_DRAGON: case MONS_LINDWURM: case MONS_XTAHUA: - strcat(pbolt.beam_name, "'s blast of flame"); + pbolt.name += "'s blast of flame"; pbolt.flavour = BEAM_FIRE; pbolt.colour = RED; - pbolt.aux_source = "blast of flame"; + pbolt.aux_source = "blast of fiery breath"; break; case MONS_ICE_DRAGON: - strcat(pbolt.beam_name, "'s blast of cold"); + pbolt.name += "'s blast of cold"; pbolt.flavour = BEAM_COLD; pbolt.colour = WHITE; - pbolt.aux_source = "blast of cold"; + pbolt.aux_source = "blast of icy breath"; + break; + + case MONS_RED_DRACONIAN: + pbolt.name += "'s searing breath"; +#ifdef DEBUG_DIAGNOSTICS + mprf( MSGCH_DIAGNOSTICS, "bolt name: '%s'", pbolt.name.c_str() ); +#endif + pbolt.flavour = BEAM_FIRE; + pbolt.colour = RED; + pbolt.aux_source = "blast of searing breath"; + scaling = 65; break; + case MONS_WHITE_DRACONIAN: + pbolt.name += "'s chilling breath"; +#ifdef DEBUG_DIAGNOSTICS + mprf( MSGCH_DIAGNOSTICS, "bolt name: '%s'", pbolt.name.c_str() ); +#endif + pbolt.flavour = BEAM_COLD; + pbolt.colour = WHITE; + pbolt.aux_source = "blast of chilling breath"; + scaling = 65; + break; + default: DEBUGSTR("Bad monster class in setup_dragon()"); + break; } pbolt.range = 4; pbolt.rangeMax = 13; pbolt.damage = dice_def( 3, (monster->hit_dice * 2) ); + pbolt.damage.size = scaling * pbolt.damage.size / 100; pbolt.type = SYM_ZAP; pbolt.hit = 30; pbolt.beam_source = monster_index(monster); pbolt.thrower = KILL_MON; - pbolt.isBeam = true; + pbolt.is_beam = true; } // end setup_dragon(); void setup_generic_throw(struct monsters *monster, struct bolt &pbolt) @@ -786,19 +887,20 @@ void setup_generic_throw(struct monsters *monster, struct bolt &pbolt) pbolt.type = SYM_MISSILE; pbolt.flavour = BEAM_MISSILE; pbolt.thrower = KILL_MON_MISSILE; - pbolt.aux_source = NULL; - pbolt.isBeam = false; + pbolt.aux_source.clear(); + pbolt.is_beam = false; } // decide if something is launched or thrown -// pass -1 for launcher class & 0 for type if no weapon is weilded +// pass -1 for launcher class & 0 for type if no weapon is wielded void throw_type( int lnchClass, int lnchType, int wepClass, int wepType, bool &launched, bool &thrown ) { if (wepClass == OBJ_MISSILES && lnchClass == OBJ_WEAPONS - && launches_things(lnchType) && wepType == launched_by(lnchType)) + && is_range_weapon_type((weapon_type) lnchType) + && wepType == fires_ammo_type((weapon_type) lnchType)) { launched = true; } @@ -836,9 +938,11 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) int ammoHitBonus = 0, ammoDamBonus = 0; // from thrown or ammo int lnchHitBonus = 0, lnchDamBonus = 0; // special add from launcher int exHitBonus = 0, exDamBonus = 0; // 'extra' bonus from skill/dex/str + int lnchBaseDam = 0; int hitMult = 0; int damMult = 0; + int diceMult = 100; bool launched = false; // item is launched bool thrown = false; // item is sensible thrown item @@ -862,7 +966,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) pbolt.colour = item.colour; pbolt.flavour = BEAM_MISSILE; pbolt.thrower = KILL_MON_MISSILE; - pbolt.aux_source = NULL; + pbolt.aux_source.clear(); // figure out if we're thrown or launched throw_type( lnchClass, lnchType, wepClass, wepType, launched, thrown ); @@ -872,6 +976,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) { lnchHitBonus = mitm[ weapon ].plus; lnchDamBonus = mitm[ weapon ].plus2; + lnchBaseDam = property(mitm[weapon], PWPN_DAMAGE); } // extract weapon/ammo bonuses due to magic @@ -897,9 +1002,14 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) baseDam = property( item, PWPN_DAMAGE ); if (wepClass == OBJ_MISSILES) // throw missile + { // ammo damage needs adjusting here - OBJ_MISSILES // don't get separate tohit/damage bonuses! ammoDamBonus = ammoHitBonus; + // [dshaligram] Thrown stones/darts do only half the damage of + // launched stones/darts. This matches 4.0 behaviour. + baseDam = div_rand_round(baseDam, 2); + } // give monster "skill" bonuses based on HD exHitBonus = (hitMult * monster->hit_dice) / 10 + 1; @@ -917,6 +1027,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) lnchDamBonus = 0; break; case WPN_BOW: + case WPN_LONGBOW: baseHit = 0; hitMult = 60; damMult = 35; @@ -944,7 +1055,10 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) break; } - baseDam = property( item, PWPN_HIT ); + // Launcher is now more important than ammo for base damage. + baseDam = property(item, PWPN_DAMAGE); + if (lnchBaseDam) + baseDam = lnchBaseDam + random2(1 + baseDam); // missiles don't have pluses2; use hit bonus ammoDamBonus = ammoHitBonus; @@ -952,6 +1066,21 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) exHitBonus = (hitMult * monster->hit_dice) / 10 + 1; exDamBonus = (damMult * monster->hit_dice) / 10 + 1; + // monsters no longer gain unfair advantages with weapons of fire/ice + // and incorrect ammo. They now have same restriction as players. + + const int bow_brand = + get_weapon_brand(mitm[monster->inv[MSLOT_WEAPON]]); + const int ammo_brand = get_ammo_brand( item ); + + if (!baseDam && elemental_missile_beam(bow_brand, ammo_brand)) + baseDam = 4; + + // [dshaligram] This is a horrible hack - we force beam.cc to consider + // this beam "needle-like". + if (wepClass == OBJ_MISSILES && wepType == MI_NEEDLE) + pbolt.ench_power = AUTOMATIC_HIT; + // elven bow w/ elven arrow, also orcish if (get_equip_race( mitm[monster->inv[MSLOT_WEAPON]] ) == get_equip_race( mitm[monster->inv[MSLOT_MISSILE]] )) @@ -959,17 +1088,10 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) baseHit++; baseDam++; - if (cmp_equip_race(mitm[monster->inv[MSLOT_WEAPON]], ISFLAG_ELVEN)) + if (get_equip_race(mitm[monster->inv[MSLOT_WEAPON]]) == ISFLAG_ELVEN) pbolt.hit++; } - // monsters no longer gain unfair advantages with weapons of fire/ice - // and incorrect ammo. They now have same restriction as players. - - const int bow_brand = get_weapon_brand(mitm[monster->inv[MSLOT_WEAPON]]); - - const int ammo_brand = get_ammo_brand( item ); - bool poison = (ammo_brand == SPMSL_POISONED || ammo_brand == SPMSL_POISONED_II); @@ -977,6 +1099,9 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) if (bow_brand == SPWPN_VENOM && ammo_brand == SPMSL_NORMAL) set_item_ego_type( item, OBJ_MISSILES, SPMSL_POISONED ); + // Vorpal brand increases damage dice size. + if (bow_brand == SPWPN_VORPAL) + diceMult = diceMult * 130 / 100; // WEAPON or AMMO of FIRE if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME) @@ -984,13 +1109,14 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) { baseHit += 2; exDamBonus += 6; + pbolt.flavour = BEAM_FIRE; - strcpy(pbolt.beam_name, "bolt of "); + pbolt.name = "bolt of "; if (poison) - strcat(pbolt.beam_name, "poison "); + pbolt.name += "poison "; - strcat(pbolt.beam_name, "flame"); + pbolt.name += "flame"; pbolt.colour = RED; pbolt.type = SYM_ZAP; } @@ -1001,13 +1127,14 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) { baseHit += 2; exDamBonus += 6; + pbolt.flavour = BEAM_COLD; - strcpy(pbolt.beam_name, "bolt of "); + pbolt.name = "bolt of "; if (poison) - strcat(pbolt.beam_name, "poison "); + pbolt.name += "poison "; - strcat(pbolt.beam_name, "frost"); + pbolt.name += "frost"; pbolt.colour = WHITE; pbolt.type = SYM_ZAP; } @@ -1023,16 +1150,16 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) if (mons_intel(monster->type) == I_HIGH) exHitBonus += 10; - // now, if a monster is, for some reason, throwing something really - // stupid, it will have baseHit of 0 and damage of 0. Ah well. + // now, if a monster is, for some reason, throwing something really + // stupid, it will have baseHit of 0 and damage of 0. Ah well. strcpy(info, ptr_monam( monster, DESC_CAP_THE) ); strcat(info, (launched) ? " shoots " : " throws "); - if (strlen(pbolt.beam_name) > 0) + if (pbolt.name.length()) { strcat(info, "a "); - strcat(info, pbolt.beam_name); + strcat(info, pbolt.name.c_str()); } else { @@ -1043,23 +1170,24 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) // build beam name item_name( item, DESC_PLAIN, str_pass ); - strcpy(pbolt.beam_name, str_pass); + pbolt.name = str_pass; } strcat(info, "."); mpr(info); - + // [dshaligram] When changing bolt names here, you must edit + // hiscores.cc (scorefile_entry::terse_missile_cause()) to match. if (launched) { snprintf( throw_buff, sizeof(throw_buff), "Shot with a%s %s by %s", - (is_vowel(pbolt.beam_name[0]) ? "n" : ""), pbolt.beam_name, + (is_vowel(pbolt.name[0]) ? "n" : ""), pbolt.name.c_str(), ptr_monam( monster, DESC_NOCAP_A ) ); } else { snprintf( throw_buff, sizeof(throw_buff), "Hit by a%s %s thrown by %s", - (is_vowel(pbolt.beam_name[0]) ? "n" : ""), pbolt.beam_name, + (is_vowel(pbolt.name[0]) ? "n" : ""), pbolt.name.c_str(), ptr_monam( monster, DESC_NOCAP_A ) ); } @@ -1075,6 +1203,9 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) pbolt.hit += lnchHitBonus; } + pbolt.damage.size = diceMult * pbolt.damage.size / 100; + scale_dice(pbolt.damage); + // decrease inventory fire_beam( pbolt, &item ); @@ -1094,18 +1225,19 @@ void spore_goes_pop(struct monsters *monster) if (monster == NULL) return; - beam.isTracer = false; + beam.is_tracer = false; + beam.is_explosion = true; beam.beam_source = monster_index(monster); beam.type = SYM_BURST; beam.target_x = monster->x; beam.target_y = monster->y; beam.thrower = KILL_MON; // someone else's explosion - beam.aux_source = NULL; + beam.aux_source.clear(); if (type == MONS_GIANT_SPORE) { beam.flavour = BEAM_SPORE; - strcpy(beam.beam_name, "explosion of spores"); + beam.name = "explosion of spores"; beam.colour = LIGHTGREY; beam.damage = dice_def( 3, 15 ); beam.ex_size = 2; @@ -1114,7 +1246,7 @@ void spore_goes_pop(struct monsters *monster) else { beam.flavour = BEAM_ELECTRICITY; - strcpy(beam.beam_name, "blast of lightning"); + beam.name = "blast of lightning"; beam.colour = LIGHTCYAN; beam.damage = dice_def( 3, 20 ); beam.ex_size = coinflip() ? 3 : 2; @@ -1130,21 +1262,23 @@ void spore_goes_pop(struct monsters *monster) explosion(beam); } // end spore_goes_pop() -struct SBeam mons_spells( int spell_cast, int power ) +bolt mons_spells( int spell_cast, int power ) { ASSERT(power > 0); - struct SBeam beam; + bolt beam; beam.name = "****"; // initialize to some bogus values so we can catch problems beam.colour = 1000; - beam.range = -1; + beam.range = beam.rangeMax = 8; beam.hit = -1; beam.damage = dice_def( 1, 0 ); beam.ench_power = -1; beam.type = -1; beam.flavour = -1; - beam.thrown = -1; + beam.thrower = -1; + beam.is_beam = false; + beam.is_explosion = false; switch (spell_cast) { @@ -1156,9 +1290,9 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 4 + (power / 100) ); beam.hit = 1500; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_MMISSILE; - beam.isBeam = false; + beam.is_beam = false; break; case MS_FLAME: @@ -1174,9 +1308,9 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.hit = 60; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_FIRE; - beam.isBeam = false; + beam.is_beam = false; break; case MS_FROST: @@ -1191,9 +1325,9 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.hit = 60; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_COLD; - beam.isBeam = false; + beam.is_beam = false; break; case MS_PARALYSIS: @@ -1202,8 +1336,8 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 9; beam.type = 0; beam.flavour = BEAM_PARALYSIS; - beam.thrown = KILL_MON_MISSILE; - beam.isBeam = true; + beam.thrower = KILL_MON_MISSILE; + beam.is_beam = true; break; case MS_SLOW: @@ -1212,8 +1346,8 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 9; beam.type = 0; beam.flavour = BEAM_SLOW; - beam.thrown = KILL_MON_MISSILE; - beam.isBeam = true; + beam.thrower = KILL_MON_MISSILE; + beam.is_beam = true; break; case MS_HASTE: // (self) @@ -1222,8 +1356,8 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 9; beam.type = 0; beam.flavour = BEAM_HASTE; - beam.thrown = KILL_MON_MISSILE; - beam.isBeam = true; + beam.thrower = KILL_MON_MISSILE; + beam.is_beam = true; break; case MS_CONFUSE: @@ -1232,8 +1366,8 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 9; beam.type = 0; beam.flavour = BEAM_CONFUSION; - beam.thrown = KILL_MON_MISSILE; - beam.isBeam = true; + beam.thrower = KILL_MON_MISSILE; + beam.is_beam = true; break; case MS_VENOM_BOLT: @@ -1243,10 +1377,21 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 6 + power / 13 ); beam.colour = LIGHTGREEN; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.flavour = BEAM_POISON; - beam.hit = 7 + random2(power) / 80; - beam.isBeam = true; + beam.hit = 8 + power / 20; + beam.is_beam = true; + break; + + case MS_POISON_ARROW: + beam.name = "poison arrow"; + beam.damage = dice_def( 4, 5 + power / 12 ); + beam.colour = LIGHTGREEN; + beam.type = SYM_MISSILE; + beam.thrower = KILL_MON; + beam.flavour = BEAM_POISON_ARROW; + beam.hit = 14 + power / 25; + beam.range = beam.rangeMax = 8; break; case MS_FIRE_BOLT: @@ -1256,10 +1401,10 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 8 + power / 11 ); beam.colour = RED; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.flavour = BEAM_FIRE; - beam.hit = 8 + random2(power) / 80; // hit - beam.isBeam = true; + beam.hit = 12 + power / 25; + beam.is_beam = true; break; case MS_COLD_BOLT: @@ -1269,10 +1414,10 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 8 + power / 11 ); beam.colour = WHITE; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.flavour = BEAM_COLD; - beam.hit = 8 + random2(power) / 80; // hit - beam.isBeam = true; + beam.hit = 12 + power / 25; + beam.is_beam = true; break; case MS_LIGHTNING_BOLT: @@ -1282,10 +1427,10 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 10 + power / 9 ); beam.colour = LIGHTCYAN; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.flavour = BEAM_ELECTRICITY; - beam.hit = 10 + random2(power) / 40; - beam.isBeam = true; + beam.hit = 10 + power / 40; + beam.is_beam = true; break; case MS_INVIS: @@ -1294,8 +1439,8 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 9; beam.type = 0; beam.flavour = BEAM_INVISIBILITY; - beam.thrown = KILL_MON; - beam.isBeam = true; + beam.thrower = KILL_MON; + beam.is_beam = true; break; case MS_FIREBALL: @@ -1306,9 +1451,10 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 7 + power / 10 ); beam.hit = 40; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; - beam.flavour = BEAM_EXPLOSION; // why not BEAM_FIRE? {dlb} - beam.isBeam = false; + beam.thrower = KILL_MON; + beam.flavour = BEAM_FIRE; // why not BEAM_FIRE? {dlb} + beam.is_beam = false; + beam.is_explosion = true; break; case MS_HEAL: @@ -1317,9 +1463,9 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 9; beam.type = 0; beam.flavour = BEAM_HEALING; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.hit = 5 + (power / 5); - beam.isBeam = true; + beam.is_beam = true; break; case MS_TELEPORT: @@ -1328,8 +1474,8 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 9; beam.type = 0; beam.flavour = BEAM_TELEPORT; // 6 is used by digging - beam.thrown = KILL_MON; - beam.isBeam = true; + beam.thrower = KILL_MON; + beam.is_beam = true; break; case MS_TELEPORT_OTHER: @@ -1338,12 +1484,12 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 9; beam.type = 0; beam.flavour = BEAM_TELEPORT; // 6 is used by digging - beam.thrown = KILL_MON; - beam.isBeam = true; + beam.thrower = KILL_MON; + beam.is_beam = true; break; case MS_BLINK: - beam.isBeam = false; + beam.is_beam = false; break; case MS_CRYSTAL_SPEAR: // was splinters @@ -1353,10 +1499,10 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 12 + power / 10 ); beam.colour = WHITE; beam.type = SYM_MISSILE; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.flavour = BEAM_MMISSILE; - beam.hit = 6; // + random2(power) / 10; - beam.isBeam = false; + beam.hit = 14 + power / 20; + beam.is_beam = false; break; case MS_DIG: @@ -1365,8 +1511,8 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 7 + random2(power) / 10; beam.type = 0; beam.flavour = BEAM_DIGGING; - beam.thrown = KILL_MON; - beam.isBeam = true; + beam.thrower = KILL_MON; + beam.is_beam = true; break; case MS_NEGATIVE_BOLT: // negative energy @@ -1376,10 +1522,10 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 6 + power / 13 ); beam.colour = DARKGREY; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.flavour = BEAM_NEG; - beam.hit = 7 + random2(power) / 80; - beam.isBeam = true; + beam.hit = 11 + power / 35; + beam.is_beam = true; break; // 20, 21 are used @@ -1390,11 +1536,11 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.range = 6; beam.rangeMax = 10; beam.damage = dice_def( 3, 7 + (power / 14) ); - beam.hit = 10 + (power / 20); + beam.hit = 12 + (power / 20); beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_MMISSILE; - beam.isBeam = false; + beam.is_beam = false; break; // 23 is brain feed @@ -1404,12 +1550,12 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.name = "ball of steam"; beam.range = 6; beam.rangeMax = 10; - beam.damage = dice_def( 3, 6 ); - beam.hit = 11; + beam.damage = dice_def( 3, 7 + (power / 15) ); + beam.hit = 11 + power / 20; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; - beam.flavour = BEAM_FIRE; // fire - I think this is appropriate - beam.isBeam = false; + beam.thrower = KILL_MON_MISSILE; + beam.flavour = BEAM_STEAM; + beam.is_beam = false; break; // 27 is summon devils @@ -1421,11 +1567,11 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 14; beam.type = 0; beam.flavour = BEAM_PAIN; // pain - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; // beam.damage = dice_def( 1, 50 ); beam.damage = dice_def( 1, 7 + (power / 20) ); beam.ench_power = 50; - beam.isBeam = true; + beam.is_beam = true; break; // 30 is smiting @@ -1436,11 +1582,11 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.range = 6; beam.rangeMax = 10; beam.damage = dice_def( 3, 3 + power / 50 ); - beam.hit = 8 + power / 15; + beam.hit = 10 + power / 15; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_FIRE; - beam.isBeam = false; + beam.is_beam = false; break; case MS_POISON_BLAST: // demon @@ -1450,10 +1596,11 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 3 + power / 25 ); beam.colour = LIGHTGREEN; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.flavour = BEAM_POISON; - beam.hit = 7 + random2(power) / 80; - beam.isBeam = true; + beam.hit = 11 + power / 25; + beam.is_beam = true; + beam.is_big_cloud = true; break; case MS_PURPLE_BLAST: // purple bang thing @@ -1462,11 +1609,12 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.range = 6; beam.rangeMax = 10; beam.damage = dice_def( 3, 10 + power / 15 ); - beam.hit = 10 + power / 20; + beam.hit = 11 + power / 25; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; - beam.flavour = BEAM_EXPLOSION; // an exploding magical missile - beam.isBeam = false; + beam.thrower = KILL_MON_MISSILE; + beam.flavour = BEAM_MMISSILE; + beam.is_beam = false; + beam.is_explosion = true; break; case MS_ENERGY_BOLT: // eye of devastation @@ -1475,11 +1623,11 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.range = 9; beam.rangeMax = 23; beam.damage = dice_def( 3, 20 ); - beam.hit = 9; + beam.hit = 11 + power / 30; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_NUKE; // a magical missile which destroys walls - beam.isBeam = true; + beam.is_beam = true; break; case MS_STING: // sting @@ -1490,9 +1638,9 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 1, 6 + power / 25 ); beam.hit = 60; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_POISON; - beam.isBeam = false; + beam.is_beam = false; break; case MS_IRON_BOLT: @@ -1501,11 +1649,11 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.range = 4; beam.rangeMax = 8; beam.damage = dice_def( 3, 8 + (power / 9) ); - beam.hit = 6 + (power / 25); + beam.hit = 12 + (power / 25); beam.type = SYM_MISSILE; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_MMISSILE; // similarly unresisted thing - beam.isBeam = false; + beam.is_beam = false; break; case MS_STONE_ARROW: @@ -1514,11 +1662,11 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.range = 8; beam.rangeMax = 12; beam.damage = dice_def( 3, 5 + (power / 10) ); - beam.hit = 5 + power / 47; + beam.hit = 8 + power / 35; beam.type = SYM_MISSILE; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_MMISSILE; // similarly unresisted thing - beam.isBeam = false; + beam.is_beam = false; break; case MS_POISON_SPLASH: @@ -1527,11 +1675,11 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.range = 5; beam.rangeMax = 10; beam.damage = dice_def( 1, 4 + power / 10 ); - beam.hit = 9; + beam.hit = 9 + power / 20; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_POISON; - beam.isBeam = false; + beam.is_beam = false; break; case MS_DISINTEGRATE: @@ -1540,11 +1688,11 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 14; beam.type = 0; beam.flavour = BEAM_DISINTEGRATION; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.ench_power = 50; // beam.hit = 30 + (power / 10); beam.damage = dice_def( 1, 30 + (power / 10) ); - beam.isBeam = true; + beam.is_beam = true; break; case MS_MARSH_GAS: // swamp drake @@ -1554,23 +1702,37 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 2 + power / 25 ); beam.colour = GREEN; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.flavour = BEAM_POISON; - beam.hit = 7 + random2(power) / 80; - beam.isBeam = false; + beam.hit = 12 + power / 30; + beam.is_beam = true; + beam.is_big_cloud = true; break; + case MS_MIASMA: // death drake + beam.name = "foul vapour"; + beam.damage = dice_def( 3, 5 + power / 24 ); + beam.colour = DARKGREY; + beam.type = SYM_ZAP; + beam.thrower = KILL_MON; + beam.flavour = BEAM_MIASMA; + beam.hit = 12 + power / 20; + beam.is_beam = true; + beam.is_big_cloud = true; + beam.range = beam.rangeMax = 8; + break; + case MS_QUICKSILVER_BOLT: // Quicksilver dragon beam.colour = random_colour(); beam.name = "bolt of energy"; beam.range = 9; beam.rangeMax = 23; beam.damage = dice_def( 3, 25 ); - beam.hit = 9; + beam.hit = 9 + power / 25; beam.type = SYM_ZAP; - beam.thrown = KILL_MON_MISSILE; + beam.thrower = KILL_MON_MISSILE; beam.flavour = BEAM_MMISSILE; - beam.isBeam = false; + beam.is_beam = false; break; case MS_HELLFIRE: // fiend's hellfire @@ -1581,9 +1743,10 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 25 ); beam.hit = 20; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; - beam.flavour = BEAM_EXPLOSION; // hellfire - not BEAM_HELLFIRE? {dlb} - beam.isBeam = true; + beam.thrower = KILL_MON; + beam.flavour = BEAM_HELLFIRE; + beam.is_beam = true; + beam.is_explosion = true; break; case MS_METAL_SPLINTERS: @@ -1593,10 +1756,10 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.damage = dice_def( 3, 20 + power / 20 ); beam.colour = CYAN; beam.type = SYM_ZAP; - beam.thrown = KILL_MON; + beam.thrower = KILL_MON; beam.flavour = BEAM_FRAG; - beam.hit = 15 + random2(power) / 50; - beam.isBeam = true; + beam.hit = 15 + power / 30; + beam.is_beam = true; break; case MS_BANISHMENT: @@ -1605,8 +1768,19 @@ struct SBeam mons_spells( int spell_cast, int power ) beam.rangeMax = 9; beam.type = 0; beam.flavour = BEAM_BANISH; - beam.thrown = KILL_MON_MISSILE; - beam.isBeam = true; + beam.thrower = KILL_MON_MISSILE; + beam.is_beam = true; + break; + + case MS_BLINK_OTHER: + beam.name = "0"; + beam.type = 0; + beam.flavour = BEAM_BLINK; + beam.thrower = KILL_MON; + beam.is_beam = true; + beam.is_enchant = true; + beam.range = 8; + beam.rangeMax = 8; break; default: @@ -1663,3 +1837,39 @@ static unsigned char monster_abjuration(int pow, bool test) return result; } // end monster_abjuration() + +bool silver_statue_effects(monsters *mons) +{ + if ((mons_player_visible(mons) || one_chance_in(3)) + && !one_chance_in(3)) + { + char wc[30]; + + weird_colours( random2(256), wc ); + snprintf(info, INFO_SIZE, "'s eyes glow %s.", wc); + simple_monster_message(mons, info, MSGCH_WARN); + + create_monster( summon_any_demon((coinflip() ? DEMON_COMMON + : DEMON_LESSER)), + ENCH_ABJ_V, BEH_HOSTILE, + you.x_pos, you.y_pos, + MHITYOU, 250 ); + return (true); + } + return (false); +} + +bool orange_statue_effects(monsters *mons) +{ + if ((mons_player_visible(mons) || one_chance_in(3)) + && !one_chance_in(3)) + { + mpr("A hostile presence attacks your mind!", MSGCH_WARN); + + miscast_effect( SPTYP_DIVINATION, random2(15), random2(150), 100, + "an orange crystal statue" ); + return (true); + } + + return (false); +} diff --git a/crawl-ref/source/mstuff2.h b/crawl-ref/source/mstuff2.h index 1bdd23232f..aedb2cdcd0 100644 --- a/crawl-ref/source/mstuff2.h +++ b/crawl-ref/source/mstuff2.h @@ -3,6 +3,8 @@ * Summary: Misc monster related functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> 4/24/99 JDJ mons_spells returns an @@ -19,22 +21,6 @@ #include "externs.h" -struct SBeam -{ - std::string name; - int colour; - int range; - int rangeMax; - int hit; - dice_def damage; - int ench_power; - int type; - int flavour; - int thrown; - bool isBeam; -}; - - /* beam_colour = _pass[0]; beam_range = _pass[1]; @@ -50,7 +36,7 @@ struct SBeam /* *********************************************************************** * called from: monstuff - mstuff2 * *********************************************************************** */ -struct SBeam mons_spells(int spell_cast, int power); +bolt mons_spells(int spell_cast, int power); // last updated 12may2000 {dlb} @@ -95,7 +81,8 @@ void mons_trap(struct monsters *monster); /* *********************************************************************** * called from: beam - fight - files - monstuff - mstuff2 - spells4 * *********************************************************************** */ -void monster_teleport(struct monsters *monster, bool instan); +void monster_teleport(struct monsters *monster, bool instan, + bool silent = false); // last updated Dec17,2000 -- gdl @@ -113,5 +100,7 @@ void throw_type(int lnchClass, int lnchType, int wepClass, int wepType, bool &launched, bool &thrown); +bool orange_statue_effects(monsters *mons); +bool silver_statue_effects(monsters *mons); #endif diff --git a/crawl-ref/source/mutation.cc b/crawl-ref/source/mutation.cc index 027a810432..a4deade186 100644 --- a/crawl-ref/source/mutation.cc +++ b/crawl-ref/source/mutation.cc @@ -35,7 +35,9 @@ #include "defines.h" #include "effects.h" +#include "itemprop.h" #include "macro.h" +#include "notes.h" #include "ouch.h" #include "player.h" #include "skills2.h" @@ -48,6 +50,12 @@ int how_mutated(void); char body_covered(void); bool perma_mutate(int which_mut, char how_much); +const char* troll_claw_messages[3] = { + "Your claws sharpen.", + "Your claws sharpen.", + "Your claws steel!" +}; + const char *mutation_descrip[][3] = { {"You have tough skin (AC +1).", "You have very tough skin (AC +2).", "You have extremely tough skin (AC +3)."}, @@ -896,6 +904,20 @@ void display_mutations(void) j++; break; + case SP_SPRIGGAN: + cprintf("You can see invisible." EOL); + cprintf("You cover the ground extremely quickly." EOL); + j += 2; + break; + + case SP_CENTAUR: + if (!you.mutation[MUT_FAST]) + cprintf("You cover the ground quickly." EOL); + else + cprintf("You cover the ground extremely quickly." EOL); + j++; + break; + case SP_NAGA: // breathe poison replaces spit poison: if (!you.mutation[MUT_BREATHE_POISON]) @@ -905,7 +927,8 @@ void display_mutations(void) cprintf("Your system is immune to poisons." EOL); cprintf("You can see invisible." EOL); - j += 3; + cprintf("You move rather slowly." EOL); + j += 4; break; case SP_GNOME: @@ -915,7 +938,24 @@ void display_mutations(void) case SP_TROLL: cprintf("Your body regenerates quickly." EOL); - j++; + switch ( you.mutation[MUT_CLAWS] ) { + case 0: + cprintf("You have claws for hands." EOL); + break; + case 1: + cprintf("You have sharp claws for hands." EOL); + break; + case 2: + cprintf("You have very sharp claws for hands." EOL); + break; + case 3: + // literally true + cprintf("Your claws are sharper than steel." EOL); + break; + default: + break; + } + j += 2; break; case SP_GHOUL: @@ -1051,6 +1091,10 @@ void display_mutations(void) // this is already handled above: if (you.species == SP_NAGA && i == MUT_BREATHE_POISON) continue; + if (you.species == SP_CENTAUR && i == MUT_FAST) + continue; + if (you.species == SP_TROLL && i == MUT_CLAWS) + continue; j++; textcolor(LIGHTGREY); @@ -1100,9 +1144,6 @@ void display_mutations(void) puttext(1, 1, 80, 25, buffer); #endif - //cprintf("xxxxxxxxxxxxx"); - //last_requested = 0; - return; } // end display_mutations() @@ -1110,8 +1151,17 @@ bool mutate(int which_mutation, bool failMsg) { int mutat = which_mutation; bool force_mutation = false; // is mutation forced? + bool demonspawn = false; // demonspawn mutation? int i; + if (which_mutation >= 2000) + { + demonspawn = true; + force_mutation = true; + mutat -= 2000; + which_mutation -= 2000; + } + if (which_mutation >= 1000) // must give mutation without failure { force_mutation = true; @@ -1120,10 +1170,11 @@ bool mutate(int which_mutation, bool failMsg) } // Undead bodies don't mutate, they fall apart. -- bwr - if (you.is_undead) + // except for demonspawn in lichform -- haranp + if (you.is_undead && !demonspawn) { - if (force_mutation - || (wearing_amulet(AMU_RESIST_MUTATION) && coinflip())) + if ((!wearing_amulet(AMU_RESIST_MUTATION) && coinflip()) + || one_chance_in(10)) { mpr( "Your body decomposes!" ); @@ -1427,7 +1478,6 @@ bool mutate(int which_mutation, bool failMsg) break; case MUT_TELEPORT_CONTROL: - you.attribute[ATTR_CONTROL_TELEPORT]++; mpr(gain_mutation[mutat][you.mutation[mutat]], MSGCH_MUTATION); break; @@ -1449,7 +1499,10 @@ bool mutate(int which_mutation, bool failMsg) break; case MUT_CLAWS: - mpr( gain_mutation[ mutat ][ you.mutation[mutat] ], MSGCH_MUTATION ); + + mpr((you.species == SP_TROLL ? troll_claw_messages + : gain_mutation[mutat])[you.mutation[mutat]], + MSGCH_MUTATION); // gloves aren't prevented until level three if (you.mutation[ mutat ] >= 3 && you.equip[ EQ_GLOVES ] != -1) @@ -1470,8 +1523,9 @@ bool mutate(int which_mutation, bool failMsg) { mpr(gain_mutation[mutat][you.mutation[mutat]], MSGCH_MUTATION); - if (you.equip[EQ_HELMET] != -1 - && you.inv[you.equip[EQ_HELMET]].plus2 > 1) + if (you.equip[EQ_HELMET] != -1 && + (get_helmet_type(you.inv[you.equip[EQ_HELMET]]) == THELM_CAP || + get_helmet_type(you.inv[you.equip[EQ_HELMET]]) == THELM_WIZARD_HAT)) { break; // horns don't push caps/wizard hats off } @@ -1519,6 +1573,8 @@ bool mutate(int which_mutation, bool failMsg) mpr(gain_mutation[mutat][you.mutation[mutat]], MSGCH_MUTATION); you.mutation[mutat]++; calc_hp(); + /* special-case check */ + take_note(Note(NOTE_GET_MUTATION, mutat, you.mutation[mutat])); return true; case MUT_ROBUST: @@ -1530,6 +1586,8 @@ bool mutate(int which_mutation, bool failMsg) mpr(gain_mutation[mutat][you.mutation[mutat]], MSGCH_MUTATION); you.mutation[mutat]++; calc_hp(); + /* special-case check */ + take_note(Note(NOTE_GET_MUTATION, mutat, you.mutation[mutat])); return true; case MUT_BLACK_SCALES: @@ -1567,6 +1625,7 @@ bool mutate(int which_mutation, bool failMsg) you.mutation[mutat]++; + take_note(Note(NOTE_GET_MUTATION, mutat, you.mutation[mutat])); /* remember, some mutations don't get this far (eg frail) */ return true; } // end mutation() @@ -1678,7 +1737,6 @@ bool delete_mutation(int which_mutation) break; case MUT_TELEPORT_CONTROL: - you.attribute[ATTR_CONTROL_TELEPORT]--; mpr(lose_mutation[mutat][you.mutation[mutat] - 1], MSGCH_MUTATION); break; @@ -1699,6 +1757,8 @@ bool delete_mutation(int which_mutation) if (you.mutation[mutat] > 0) you.mutation[mutat]--; calc_hp(); + /* special-case check */ + take_note(Note(NOTE_LOSE_MUTATION, mutat, you.mutation[mutat])); return true; case MUT_ROBUST: @@ -1706,6 +1766,8 @@ bool delete_mutation(int which_mutation) if (you.mutation[mutat] > 0) you.mutation[mutat]--; calc_hp(); + /* special-case check */ + take_note(Note(NOTE_LOSE_MUTATION, mutat, you.mutation[mutat])); return true; case MUT_BLACK_SCALES: @@ -1766,6 +1828,7 @@ bool delete_mutation(int which_mutation) if (you.mutation[mutat] > 0) you.mutation[mutat]--; + take_note(Note(NOTE_LOSE_MUTATION, mutat, you.mutation[mutat])); return true; } // end delete_mutation() @@ -1825,7 +1888,7 @@ const char *mutation_name(int which_mutat, int level ) // Some mutations only have one "level", and it's better // to show the first level description than a blank description. - if (mutation_descrip[ which_mutat ][ level - 1 ][0] == '\0') + if (mutation_descrip[ which_mutat ][ level - 1 ][0] == 0) return (mutation_descrip[ which_mutat ][ 0 ]); else return (mutation_descrip[ which_mutat ][ level - 1 ]); @@ -1956,7 +2019,7 @@ void demonspawn(void) } } - // check here so we can see if we need to extent our options: + // check here so we can see if we need to extend our options: if (whichm != -1 && you.mutation[whichm] != 0) whichm = -1; @@ -2148,13 +2211,13 @@ bool perma_mutate(int which_mut, char how_much) { char levels = 0; - if (mutate(which_mut + 1000)) + if (mutate(which_mut + 2000)) levels++; - if (how_much >= 2 && mutate(which_mut + 1000)) + if (how_much >= 2 && mutate(which_mut + 2000)) levels++; - if (how_much >= 3 && mutate(which_mut + 1000)) + if (how_much >= 3 && mutate(which_mut + 2000)) levels++; you.demon_pow[which_mut] = levels; diff --git a/crawl-ref/source/newgame.cc b/crawl-ref/source/newgame.cc index eca23d3815..c4ffc8cbd1 100644 --- a/crawl-ref/source/newgame.cc +++ b/crawl-ref/source/newgame.cc @@ -58,6 +58,7 @@ #ifdef DOS #include +#include #endif #ifdef UNIX @@ -66,18 +67,6 @@ #include #endif -#ifdef USE_EMX -#include -#include -#include -#endif - -#ifdef OS9 -#include -#else -#include -#endif - #include "externs.h" #include "abl-show.h" @@ -86,8 +75,10 @@ #include "fight.h" #include "initfile.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "macro.h" +#include "menu.h" #include "player.h" #include "randart.h" #include "skills.h" @@ -95,8 +86,9 @@ #include "spl-util.h" #include "stuff.h" #include "version.h" -#include "wpn-misc.h" +#include "view.h" +extern std::string init_file_location; #define MIN_START_STAT 1 @@ -136,6 +128,7 @@ static char ng_race, ng_cls; static bool ng_random; static int ng_ck, ng_dk, ng_pr; static int ng_weapon; +static int ng_book; static void reset_newgame_options(void) { @@ -145,6 +138,7 @@ static void reset_newgame_options(void) ng_dk = DK_NO_SELECTION; ng_pr = GOD_NO_GOD; ng_weapon = WPN_UNKNOWN; + ng_book = SBT_NO_SELECTION; } static void save_newgame_options(void) @@ -158,6 +152,7 @@ static void save_newgame_options(void) Options.prev_dk = ng_dk; Options.prev_pr = ng_pr; Options.prev_weapon = ng_weapon; + Options.prev_book = ng_book; write_newgame_options_file(); } @@ -170,6 +165,7 @@ static void set_startup_options(void) Options.death_knight = Options.prev_dk; Options.chaos_knight = Options.prev_ck; Options.priest = Options.prev_pr; + Options.book = Options.prev_book; } static bool prev_startup_options_set(void) @@ -251,7 +247,7 @@ static void pick_random_species_and_class( void ) { // we only want draconians counted once in this loop... // we'll add the variety lower down -- bwr - if (sp >= SP_WHITE_DRACONIAN && sp <= SP_UNK2_DRACONIAN) + if (sp >= SP_WHITE_DRACONIAN && sp <= SP_BASE_DRACONIAN) continue; for (int cl = JOB_FIGHTER; cl < NUM_JOBS; cl++) @@ -283,64 +279,35 @@ static void pick_random_species_and_class( void ) static bool check_saved_game(void) { FILE *handle; - char char_fil[kFileNameSize]; - -#ifdef LOAD_UNPACKAGE_CMD - // Create the file name base - char name_buff[kFileNameLen]; - - snprintf( name_buff, sizeof(name_buff), - SAVE_DIR_PATH "%s%d", you.your_name, (int) getuid() ); - - char zip_buff[kFileNameLen]; - strcpy(zip_buff, name_buff); - strcat(zip_buff, PACKAGE_SUFFIX); + std::string basename = get_savedir_filename( you.your_name, "", ""); + std::string savename = basename + ".sav"; - // Create save dir name - strcpy(char_fil, name_buff); - strcat(char_fil, ".sav"); - - handle = fopen(zip_buff, "rb+"); +#ifdef LOAD_UNPACKAGE_CMD + std::string zipname = basename + PACKAGE_SUFFIX; + handle = fopen(zipname.c_str(), "rb+"); if (handle != NULL) { + fclose(handle); cprintf(EOL "Loading game..." EOL); // Create command char cmd_buff[1024]; - snprintf( cmd_buff, sizeof(cmd_buff), LOAD_UNPACKAGE_CMD, name_buff ); + snprintf( cmd_buff, sizeof(cmd_buff), LOAD_UNPACKAGE_CMD, + basename.c_str() ); if (system( cmd_buff ) != 0) { cprintf( EOL "Warning: Zip command (LOAD_UNPACKAGE_CMD) returned non-zero value!" EOL ); } - fclose(handle); - // Remove save game package - unlink(zip_buff); + unlink(zipname.c_str()); } - else - { -#ifdef DO_ANTICHEAT_CHECKS - // Simple security patch -- must have zip file otherwise invalidate - // the character. Right now this just renames the .sav file to - // .bak, allowing anyone with the appropriate permissions to - // fix a character in the case of a bug. This could be changed - // to unlinking the file(s) which would remove the character. - strcat(name_buff, ".bak"); - rename(char_fil, name_buff); -#endif - } - -#else - strcpy(char_fil, ""); - strncat(char_fil, you.your_name, kFileNameLen); - strcat(char_fil, ".sav"); #endif - handle = fopen(char_fil, "rb+"); + handle = fopen(savename.c_str(), "rb+"); if (handle != NULL) { @@ -350,19 +317,45 @@ static bool check_saved_game(void) return false; } +static unsigned char random_potion_description() +{ + int desc, nature, colour; + + do { + desc = random2( PDQ_NQUALS * PDC_NCOLOURS ); + + if (coinflip()) + desc %= PDC_NCOLOURS; + + nature = PQUAL(desc); + colour = PCOLOUR(desc); + + // nature and colour correspond to primary and secondary + // in itemname.cc; this check ensures clear potions don't + // get odd qualifiers. + } while ((colour == PDC_CLEAR && nature > PDQ_VISCOUS) + || desc == PDESCS(PDC_CLEAR) + || desc == PDESCQ(PDQ_GLUGGY, PDC_WHITE)); + + return (unsigned char) desc; +} + bool new_game(void) { int i, j; // loop variables {dlb} //jmf: NEW ASSERTS: we ought to do a *lot* of these ASSERT(NUM_SPELLS < SPELL_NO_SPELL); - ASSERT(NUM_DURATIONS > DUR_LAST_DUR); ASSERT(NUM_JOBS < JOB_UNKNOWN); - ASSERT(NUM_ATTRIBUTES < 30); + ASSERT(NUM_ATTRIBUTES >= 30); init_player(); - you.exp_available = 25; // now why is this all the way up here? {dlb} + if (!Options.player_name.empty()) + { + strncpy(you.your_name, Options.player_name.c_str(), kNameLen); + you.your_name[kNameLen - 1] = 0; + } textcolor(LIGHTGREY); @@ -372,21 +365,20 @@ bool new_game(void) if (SysEnv.crawl_name) { strncpy( you.your_name, SysEnv.crawl_name, kNameLen ); - you.your_name[ kNameLen - 1 ] = '\0'; + you.your_name[ kNameLen - 1 ] = 0; } openingScreen(); enterPlayerName(true); - if (you.your_name[0] != '\0') + if (you.your_name[0] != 0) { if (check_saved_game()) { textcolor( BROWN ); cprintf( EOL "Welcome back, " ); textcolor( YELLOW ); - cprintf( you.your_name ); - cprintf( "!" ); + cprintf( "%s!", you.your_name ); textcolor( LIGHTGREY ); save_player_name(); @@ -408,7 +400,7 @@ bool new_game(void) strcpy( you.class_name, get_class_name( you.char_class ) ); // new: pick name _after_ race and class choices - if (you.your_name[0] == '\0') + if (you.your_name[0] == 0) { clrscr(); @@ -432,8 +424,7 @@ bool new_game(void) textcolor( BROWN ); cprintf(EOL EOL "Welcome back, "); textcolor( YELLOW ); - cprintf(you.your_name); - cprintf("!"); + cprintf("%s!", you.your_name); textcolor( LIGHTGREY ); return (false); @@ -449,8 +440,8 @@ bool new_game(void) you.is_undead = ((you.species == SP_MUMMY) ? US_UNDEAD : (you.species == SP_GHOUL) ? US_HUNGRY_DEAD : US_ALIVE); - // before we get into the inventory init, set light radius based - // on species vision. currently, all species see out to 8 squares. + // before we get into the inventory init, set light radius based + // on species vision. currently, all species see out to 8 squares. you.normal_vision = 8; you.current_vision = 8; @@ -627,7 +618,7 @@ bool new_game(void) // don't change object type modifier unless it starts plain if (you.inv[i].base_type <= OBJ_ARMOUR - && cmp_equip_race( you.inv[i], 0 )) // == DARM_PLAIN + && get_equip_race(you.inv[i]) == 0 ) // == DARM_PLAIN { // now add appropriate species type mod: switch (you.species) @@ -664,8 +655,11 @@ bool new_game(void) } } - you.item_description[IDESC_POTIONS][POT_PORRIDGE] = 153; // "gluggy white" - you.item_description[IDESC_POTIONS][POT_WATER] = 0; // "clear" + you.item_description[IDESC_POTIONS][POT_PORRIDGE] = + PDESCQ(PDQ_GLUGGY, PDC_WHITE); + + you.item_description[IDESC_POTIONS][POT_WATER] = + PDESCS(PDC_CLEAR); int passout; @@ -689,9 +683,7 @@ bool new_game(void) break; case IDESC_POTIONS: // potions - you.item_description[i][j] = random2( 15 * 14 ); - if (coinflip()) - you.item_description[i][j] %= 14; + you.item_description[i][j] = random_potion_description(); break; case IDESC_SCROLLS: // scrolls @@ -1422,6 +1414,7 @@ bool class_allowed( unsigned char speci, int char_class ) case SP_HUMAN: case SP_DEMIGOD: case SP_DEMONSPAWN: + case SP_GHOUL: return true; } return false; @@ -1432,6 +1425,88 @@ bool class_allowed( unsigned char speci, int char_class ) } } // end class_allowed() + +static void choose_book( item_def& book, int firstbook, int numbooks ) +{ + int keyin = 0; + clrscr(); + book.base_type = OBJ_BOOKS; + book.quantity = 1; + book.plus = 0; + book.special = 1; + book.colour = CYAN; + + // using the fact that CONJ_I and MINOR_MAGIC_I are both + // fire books, CONJ_II and MINOR_MAGIC_II are both ice books + if ( Options.book && Options.book <= numbooks ) + { + book.sub_type = firstbook + Options.book - 1; + ng_book = Options.book; + return; + } + + if ( Options.prev_book > numbooks && Options.prev_book != SBT_RANDOM ) + Options.prev_book = SBT_NO_SELECTION; + + if ( !Options.random_pick ) + { + textcolor( CYAN ); + cprintf(EOL " You have a choice of books:" EOL); + textcolor( LIGHTGREY ); + + for (int i=0; i < numbooks; ++i) + { + char buf[ITEMNAME_SIZE]; + book.sub_type = firstbook + i; + item_name( book, DESC_PLAIN, buf ); + cprintf("%c - %s" EOL, 'a' + i, buf); + } + + textcolor(BROWN); + cprintf(EOL "? - Random" ); + if ( Options.prev_book != SBT_NO_SELECTION ) { + cprintf("; Enter - %s", + Options.prev_book == SBT_FIRE ? "Fire" : + Options.prev_book == SBT_COLD ? "Cold" : + Options.prev_book == SBT_SUMM ? "Summoning" : + Options.prev_book == SBT_RANDOM ? "Random" : + "Buggy Book"); + } + cprintf(EOL); + + do + { + textcolor( CYAN ); + cprintf(EOL "Which book? "); + textcolor( LIGHTGREY ); + + keyin = get_ch(); + } while (keyin != '?' && + ((keyin != '\r' && keyin != '\n') || + Options.prev_book == SBT_NO_SELECTION ) && + (keyin < 'a' || keyin > ('a' + numbooks))); + + if ( keyin == '\r' || keyin == '\n' ) + { + if ( Options.prev_book == SBT_RANDOM ) + keyin = '?'; + else + keyin = ('a' + Options.prev_book - 1); + } + } + + if (Options.random_pick || Options.book == SBT_RANDOM || keyin == '?') + ng_book = SBT_RANDOM; + else + ng_book = keyin - 'a' + 1; + + if ( Options.random_pick || keyin == '?' ) + keyin = random2(numbooks) + 'a'; + + book.sub_type = firstbook + keyin - 'a'; +} + + static char startwep[5] = { WPN_SHORT_SWORD, WPN_MACE, WPN_HAND_AXE, WPN_SPEAR, WPN_TRIDENT }; @@ -1477,10 +1552,8 @@ void choose_weapon( void ) int x = effective_stat_bonus(startwep[i]); standard_name_weap(startwep[i], wepName); - snprintf( info, INFO_SIZE, "%c - %s%s" EOL, 'a' + i, wepName, - (x <= -4) ? " (not ideal)" : "" ); - - cprintf(info); + cprintf("%c - %s%s" EOL, 'a' + i, wepName, + (x <= -4) ? " (not ideal)" : "" ); if (Options.prev_weapon == startwep[i]) prevmatch = true; @@ -1527,7 +1600,10 @@ void choose_weapon( void ) } if (keyin != '?' && effective_stat_bonus(startwep[keyin-'a']) > -4) + { cprintf(EOL "A fine choice. " EOL); + delay(1000); + } } if (Options.random_pick || Options.weapon == WPN_RANDOM || keyin == '?') @@ -1553,167 +1629,7 @@ void choose_weapon( void ) void init_player(void) { - unsigned char i = 0; // loop variable - - you.birth_time = time( NULL ); - you.real_time = 0; - you.num_turns = 0; - -#ifdef WIZARD - you.wizard = (Options.wiz_mode == WIZ_YES) ? true : false; -#else - you.wizard = false; -#endif - - you.activity = ACT_NONE; - you.berserk_penalty = 0; - you.berserker = 0; - you.conf = 0; - you.confusing_touch = 0; - you.deaths_door = 0; - you.disease = 0; - you.elapsed_time = 0; - you.exhausted = 0; - you.haste = 0; - you.invis = 0; - you.levitation = 0; - you.might = 0; - you.paralysis = 0; - you.poison = 0; - you.rotting = 0; - you.fire_shield = 0; - you.slow = 0; - you.special_wield = SPWLD_NONE; - you.sure_blade = 0; - you.synch_time = 0; - - you.base_hp = 5000; - you.base_hp2 = 5000; - you.base_magic_points = 5000; - you.base_magic_points2 = 5000; - - you.magic_points_regeneration = 0; - you.strength = 0; - you.max_strength = 0; - you.intel = 0; - you.max_intel = 0; - you.dex = 0; - you.max_dex = 0; - you.experience = 0; - you.experience_level = 1; - you.max_level = 1; - you.char_class = JOB_UNKNOWN; - - you.hunger = 6000; - you.hunger_state = HS_SATIATED; - - you.gold = 0; - // you.speed = 10; // 0.75; // unused - - you.burden = 0; - you.burden_state = BS_UNENCUMBERED; - - you.spell_no = 0; - - you.your_level = 0; - you.level_type = LEVEL_DUNGEON; - you.where_are_you = BRANCH_MAIN_DUNGEON; - you.char_direction = DIR_DESCENDING; - - you.prev_targ = MHITNOT; - you.pet_target = MHITNOT; - - you.x_pos = 0; - you.y_pos = 0; - - you.running = 0; - you.run_x = 0; - you.run_y = 0; - you.travel_x = 0; - you.travel_y = 0; - - for (i = 0; i < 3; i++) - { - you.run_check[i].grid = 0; - you.run_check[i].dx = 0; - you.run_check[i].dy = 0; - } - - you.religion = GOD_NO_GOD; - you.piety = 0; - - you.gift_timeout = 0; - - for (i = 0; i < MAX_NUM_GODS; i++) - { - you.penance[i] = 0; - you.worshipped[i] = 0; - } - - ghost.name[0] = '\0'; - - for (i = 0; i < NUM_GHOST_VALUES; i++) - ghost.values[i] = 0; - - for (i = EQ_WEAPON; i < NUM_EQUIP; i++) - you.equip[i] = -1; - - for (i = 0; i < 25; i++) - you.spells[i] = SPELL_NO_SPELL; - - for (i = 0; i < 52; i++) - { - you.spell_letter_table[i] = -1; - you.ability_letter_table[i] = ABIL_NON_ABILITY; - } - - for (i = 0; i < 100; i++) - you.mutation[i] = 0; - - for (i = 0; i < 100; i++) - you.demon_pow[i] = 0; - - for (i = 0; i < 50; i++) - you.had_book[i] = 0; - - for (i = 0; i < 50; i++) - you.unique_items[i] = UNIQ_NOT_EXISTS; - - for (i = 0; i < NO_UNRANDARTS; i++) - set_unrandart_exist(i, 0); - - for (i = 0; i < 50; i++) - { - you.skills[i] = 0; - you.skill_points[i] = 0; - you.skill_order[i] = MAX_SKILL_ORDER; - you.practise_skill[i] = 1; - } - - you.skill_cost_level = 1; - you.total_skill_points = 0; - - for (i = 0; i < 30; i++) - you.attribute[i] = 0; - - for (i = 0; i < ENDOFPACK; i++) - { - you.inv[i].quantity = 0; - you.inv[i].base_type = OBJ_WEAPONS; - you.inv[i].sub_type = WPN_CLUB; - you.inv[i].plus = 0; - you.inv[i].plus2 = 0; - you.inv[i].special = 0; - you.inv[i].colour = 0; - set_ident_flags( you.inv[i], ISFLAG_IDENT_MASK ); - - you.inv[i].x = -1; - you.inv[i].y = -1; - you.inv[i].link = i; - } - - for (i = 0; i < NUM_DURATIONS; i++) - you.duration[i] = 0; + you.init(); } void give_last_paycheck(int which_job) @@ -1803,7 +1719,7 @@ void species_stat_init(unsigned char which_species) case SP_PALE_DRACONIAN: case SP_UNK0_DRACONIAN: case SP_UNK1_DRACONIAN: - case SP_UNK2_DRACONIAN: sb = 9; ib = 6; db = 2; break; // 17 + case SP_BASE_DRACONIAN: sb = 9; ib = 6; db = 2; break; // 17 } modify_all_stats( sb, ib, db ); @@ -1993,116 +1909,202 @@ void give_basic_spells(int which_job) // eventually, this should be something more grand {dlb} void openingScreen(void) { -/* ********************************************** -// this does not work just yet ... {dlb}: - cprintf(EOL "Hello, "); - - if ( you.your_name[0] != '\0' ) - { - cprintf(you.your_name); // better be less than 31 characters :P {dlb} - // of course, invalid names will appear {dlb} - cprintf(", "); - } -********************************************** */ - textcolor( YELLOW ); - cprintf("Hello, welcome to Dungeon Crawl " VERSION "!"); + cprintf("Hello, welcome to " CRAWL " " VERSION "!"); textcolor( BROWN ); cprintf(EOL "(c) Copyright 1997-2002 Linley Henzell"); - cprintf(EOL "Please consult crawl.txt for instructions and legal details." - EOL); + cprintf(EOL + "Please consult crawl_manual.txt for instructions and legal details." + EOL); + + bool init_found = init_file_location.find("not found") == std::string::npos; + if (!init_found) + textcolor( LIGHTRED ); + else + textcolor( LIGHTGREY ); + + cprintf("Init file %s%s" EOL, + init_found? "read: " : "", + init_file_location.c_str()); textcolor( LIGHTGREY ); return; } // end openingScreen() +static void show_name_prompt(int where, bool blankOK, + const std::vector &existing_chars, + slider_menu &menu) +{ + gotoxy(1, where); + textcolor( CYAN ); + if (blankOK) + { + if (Options.prev_name.length() && Options.remember_name) + cprintf(EOL "Press for \"%s\"." EOL, + Options.prev_name.c_str()); + else + cprintf(EOL + "Press to answer this after race and " + "class are chosen." EOL); + } -void enterPlayerName(bool blankOK) + cprintf(EOL "What is your name today? "); + + if (!existing_chars.empty()) + { + const int name_x = wherex(), name_y = wherey(); + menu.set_limits(name_y + 3, get_number_of_lines()); + menu.display(); + gotoxy(name_x, name_y); + } + + textcolor( LIGHTGREY ); +} + +static void preprocess_character_name(char *name, bool blankOK) { - // temporary 'til copyover to you.your_name {dlb} - // made this rediculously long so that the game doesn't - // crash if a really really long name is entered (argh). {gdl} - char name_entered[200]; + if (!*name && blankOK && Options.prev_name.length() && + Options.remember_name) + { + strncpy(name, Options.prev_name.c_str(), kNameLen); + name[kNameLen - 1] = 0; + } - // anything to avoid goto statements {dlb} - bool acceptable_name = false; - bool first_time = true; + // '.', '?' and '*' are blanked. + if (!name[1] && (*name == '.' || + *name == '*' || + *name == '?')) + *name = 0; +} - // first time -- names set through init.txt/environment assumed ok {dlb} - if (you.your_name[0] != '\0') - acceptable_name = true; +static bool is_good_name(char *name, bool blankOK) +{ + preprocess_character_name(name, blankOK); - do + // verification begins here {dlb}: + if (you.your_name[0] == 0) { - // prompt for a new name if current one unsatisfactory {dlb}: - if (!acceptable_name) + if (blankOK) + return (true); + + cprintf(EOL "That's a silly name!" EOL); + return (false); + } + + // if MULTIUSER is defined, userid will be tacked onto the end + // of each character's files, making bones a valid player name. +#ifndef MULTIUSER + // this would cause big probs with ghosts + // what would? {dlb} + // ... having the name "bones" of course! The problem comes from + // the fact that bones files would have the exact same filename + // as level files for a character named "bones". -- bwr + if (stricmp(you.your_name, "bones") == 0) + { + cprintf(EOL "That's a silly name!" EOL); + return (false); + } +#endif + return (verifyPlayerName()); +} + +static int newname_keyfilter(int &ch) +{ + if (ch == CK_DOWN || ch == CK_PGDN || ch == '\t') + return -1; + return 1; +} + +static bool read_player_name( + char *name, + int len, + const std::vector &existing, + slider_menu &menu) +{ + const int name_x = wherex(), name_y = wherey(); + int (*keyfilter)(int &) = newname_keyfilter; + if (existing.empty()) + keyfilter = NULL; + + line_reader reader(name, len); + reader.set_keyproc(keyfilter); + + for (;;) + { + gotoxy(name_x, name_y); + if (name_x <= 80) + cprintf("%-*s", 80 - name_x + 1, ""); + + gotoxy(name_x, name_y); + int ret = reader.read_line(false); + if (!ret) + return (true); + + if (ret == CK_ESCAPE) + return (false); + + if (ret != CK_ESCAPE && existing.size()) { - textcolor( CYAN ); - if (blankOK && first_time) + menu.set_search(name); + menu.show(); + const MenuEntry *sel = menu.selected_entry(); + if (sel) { - if (Options.prev_name.length() && Options.remember_name) - cprintf(EOL "Press for \"%s\"." EOL, - Options.prev_name.c_str()); - else - cprintf(EOL - "Press to answer this after race and " - "class are chosen." EOL); + const player &p = *static_cast( sel->data ); + strncpy(name, p.your_name, kNameLen); + name[kNameLen - 1] = 0; + return (true); } + } - first_time = false; + // Go back and prompt the user. + } +} - cprintf(EOL "What is your name today? "); - textcolor( LIGHTGREY ); - get_input_line( name_entered, sizeof( name_entered ) ); - - strncpy( you.your_name, name_entered, kNameLen ); - you.your_name[ kNameLen - 1 ] = '\0'; - } +void enterPlayerName(bool blankOK) +{ + int prompt_start = wherey(); + bool ask_name = true; + char *name = you.your_name; + std::vector existing_chars; + slider_menu char_menu; - if (!*you.your_name && blankOK && Options.prev_name.length() && - Options.remember_name) - { - strncpy(you.your_name, Options.prev_name.c_str(), kNameLen); - you.your_name[kNameLen - 1] = 0; - } + if (you.your_name[0] != 0) + ask_name = false; - // '.', '?' and '*' are blanked. - if (!you.your_name[1] && (*you.your_name == '.' || - *you.your_name == '*' || - *you.your_name == '?')) - { - *you.your_name = 0; - } - - // verification begins here {dlb}: - if (you.your_name[0] == '\0') + if (blankOK) + { + existing_chars = find_saved_characters(); + + MenuEntry *title = new MenuEntry("Or choose an existing character:"); + title->colour = LIGHTCYAN; + char_menu.set_title( title ); + for (int i = 0, size = existing_chars.size(); i < size; ++i) { - if (blankOK) - return; + std::string desc = " " + existing_chars[i].short_desc(); + if ((int) desc.length() >= get_number_of_cols()) + desc = desc.substr(0, get_number_of_cols() - 1); - cprintf(EOL "That's a silly name!" EOL); - acceptable_name = false; + MenuEntry *me = new MenuEntry(desc); + me->data = &existing_chars[i]; + char_menu.add_entry(me); } + char_menu.set_flags(MF_EASY_EXIT | MF_SINGLESELECT); + } - // if SAVE_DIR_PATH is defined, userid will be tacked onto the end - // of each character's files, making bones a valid player name. -#ifndef SAVE_DIR_PATH - // this would cause big probs with ghosts - // what would? {dlb} - // ... having the name "bones" of course! The problem comes from - // the fact that bones files would have the exact same filename - // as level files for a character named "bones". -- bwr - else if (stricmp(you.your_name, "bones") == 0) + do + { + // prompt for a new name if current one unsatisfactory {dlb}: + if (ask_name) { - cprintf(EOL "That's a silly name!" EOL); - acceptable_name = false; - } -#endif - else - acceptable_name = verifyPlayerName(); + show_name_prompt(prompt_start, blankOK, existing_chars, char_menu); + // If the player wants out, we bail out. + if (!read_player_name(name, kNameLen, existing_chars, char_menu)) + end(0); + } } - while (!acceptable_name); + while (ask_name = !is_good_name(you.your_name, blankOK)); } // end enterPlayerName() bool verifyPlayerName(void) @@ -2117,7 +2119,7 @@ bool verifyPlayerName(void) return (false); } - // quick check for LPTx -- thank you, Mr. Tanksley! ;-) + // quick check for LPTx -- thank you, Mr. Tanksley! ;-) if (strnicmp(you.your_name, "LPT", 3) == 0) { switch (william_tanksley_asked_for_this) @@ -2133,7 +2135,7 @@ bool verifyPlayerName(void) return (true); } // end switch - william_tanksley_asked_for_this --; + william_tanksley_asked_for_this--; return (false); } #endif @@ -2141,14 +2143,6 @@ bool verifyPlayerName(void) const size_t len = strlen( you.your_name ); for (unsigned int i = 0; i < len; i++) { -#if MAC - // the only bad character on Macs is the path seperator - if (you.your_name[i] == ':') - { - cprintf(EOL "No colons, please." EOL); - return (false); - } -#else // Note that this includes systems which may be using the // packaging system. The packaging system is very simple // and doesn't take the time to escape every characters that @@ -2159,10 +2153,9 @@ bool verifyPlayerName(void) cprintf( EOL "Alpha-numerics and underscores only, please." EOL ); return (false); } -#endif } -#ifdef SAVE_DIR_PATH +#ifdef MULTIUSER // Until we have a better way to handle the fact that this could lead // to some confusion with where the name ends and the uid begins. -- bwr if (isdigit( you.your_name[ len - 1 ] )) @@ -2379,19 +2372,19 @@ static bool give_wanderer_weapon( int slot, int wpn_skill ) static void create_wanderer( void ) { const int util_skills[] = - { SK_DARTS, SK_THROWING, SK_ARMOUR, SK_DODGING, SK_STEALTH, + { SK_DARTS, SK_RANGED_COMBAT, SK_ARMOUR, SK_DODGING, SK_STEALTH, SK_STABBING, SK_SHIELDS, SK_TRAPS_DOORS, SK_UNARMED_COMBAT, SK_INVOCATIONS, SK_EVOCATIONS }; const int num_util_skills = sizeof(util_skills) / sizeof(int); - // Long swords is missing to increae it's rarity because we + // Long swords is missing to increase its rarity because we // can't give out a long sword to a starting character (they're // all too good)... Staves is also removed because it's not // one of the fighter options.-- bwr const int fight_util_skills[] = { SK_FIGHTING, SK_SHORT_BLADES, SK_AXES, SK_MACES_FLAILS, SK_POLEARMS, - SK_DARTS, SK_THROWING, SK_ARMOUR, SK_DODGING, SK_STEALTH, + SK_DARTS, SK_RANGED_COMBAT, SK_ARMOUR, SK_DODGING, SK_STEALTH, SK_STABBING, SK_SHIELDS, SK_TRAPS_DOORS, SK_UNARMED_COMBAT, SK_INVOCATIONS, SK_EVOCATIONS }; const int num_fight_util_skills = sizeof(fight_util_skills) / sizeof(int); @@ -2402,7 +2395,7 @@ static void create_wanderer( void ) SK_FIRE_MAGIC, SK_ICE_MAGIC, SK_AIR_MAGIC, SK_EARTH_MAGIC, SK_FIGHTING, SK_SHORT_BLADES, SK_LONG_SWORDS, SK_AXES, SK_MACES_FLAILS, SK_POLEARMS, SK_STAVES, - SK_DARTS, SK_THROWING, SK_ARMOUR, SK_DODGING, SK_STEALTH, + SK_DARTS, SK_RANGED_COMBAT, SK_ARMOUR, SK_DODGING, SK_STEALTH, SK_STABBING, SK_SHIELDS, SK_TRAPS_DOORS, SK_UNARMED_COMBAT, SK_INVOCATIONS, SK_EVOCATIONS }; const int num_not_rare_skills = sizeof(not_rare_skills) / sizeof(int); @@ -2415,7 +2408,7 @@ static void create_wanderer( void ) SK_FIRE_MAGIC, SK_ICE_MAGIC, SK_AIR_MAGIC, SK_EARTH_MAGIC, SK_FIGHTING, SK_SHORT_BLADES, SK_LONG_SWORDS, SK_AXES, SK_MACES_FLAILS, SK_POLEARMS, SK_STAVES, - SK_DARTS, SK_THROWING, SK_ARMOUR, SK_DODGING, SK_STEALTH, + SK_DARTS, SK_RANGED_COMBAT, SK_ARMOUR, SK_DODGING, SK_STEALTH, SK_STABBING, SK_SHIELDS, SK_TRAPS_DOORS, SK_UNARMED_COMBAT, SK_INVOCATIONS, SK_EVOCATIONS }; const int num_all_skills = sizeof(all_skills) / sizeof(int); @@ -2486,9 +2479,9 @@ static void create_wanderer( void ) you.skills[ skill ] = 1; } - int wpn_skill = SK_FIGHTING; // prefered weapon type - int wpn_skill_size = 0; // level of skill in prefered weapon type - int num_wpn_skills = 0; // used to choose prefered weapon + int wpn_skill = SK_FIGHTING; // preferred weapon type + int wpn_skill_size = 0; // level of skill in preferred weapon type + int num_wpn_skills = 0; // used to choose preferred weapon int total_wpn_skills = 0; // used to choose template // This algorithm is the same as the one used to pick a random @@ -2638,14 +2631,14 @@ static void create_wanderer( void ) add_spell_to_memory( spell_list[ school ] ); } } - else if (you.skills[ SK_THROWING ] && one_chance_in(3)) // these are rare + else if (you.skills[ SK_RANGED_COMBAT ] && one_chance_in(3)) // these are rare { // Ranger style wanderer // Rare since starting with a throwing weapon is very good // Create a default launcher template, but the // quantity may be reset to 0 if we don't want one -- bwr - // thorwing weapons are lowered to -1 to make them + // throwing weapons are lowered to -1 to make them // not as good as the one's hunters get, ammo is // also much smaller -- bwr you.inv[1].quantity = 1; @@ -2844,10 +2837,10 @@ static char letter_to_species(int keyn) static char species_to_letter(int spec) { - if (spec > SP_RED_DRACONIAN && spec <= SP_UNK2_DRACONIAN) + if (spec > SP_RED_DRACONIAN && spec <= SP_BASE_DRACONIAN) spec = SP_RED_DRACONIAN; - else if (spec > SP_UNK2_DRACONIAN) - spec -= SP_UNK2_DRACONIAN - SP_RED_DRACONIAN; + else if (spec > SP_BASE_DRACONIAN) + spec -= SP_BASE_DRACONIAN - SP_RED_DRACONIAN; return 'a' + spec - 1; } @@ -2891,12 +2884,12 @@ spec_query: textcolor( YELLOW ); if (strlen(you.your_name) > 0) { - cprintf(you.your_name); + cprintf("%s", you.your_name); if (you.char_class != JOB_UNKNOWN) cprintf(" the "); } if (you.char_class != JOB_UNKNOWN) - cprintf(get_class_name(you.char_class)); + cprintf("%s", get_class_name(you.char_class)); if (!shortgreet) cprintf("."); @@ -2918,7 +2911,7 @@ spec_query: *linebuf = 0; for (int i = SP_HUMAN; i < NUM_SPECIES; ++i) { - if (i > SP_RED_DRACONIAN && i <= SP_UNK2_DRACONIAN) + if (i > SP_RED_DRACONIAN && i <= SP_BASE_DRACONIAN) continue; if (you.char_class != JOB_UNKNOWN && @@ -3051,7 +3044,7 @@ spec_query: return true; } -// returns true if a class was chosen, false if we should go back to +// returns true if a class was chosen, false if we should go back to // race selection. bool choose_class(void) @@ -3089,12 +3082,12 @@ job_query: textcolor( YELLOW ); if (strlen(you.your_name) > 0) { - cprintf(you.your_name); + cprintf("%s", you.your_name); if (you.species) cprintf(" the "); } if (you.species) - cprintf(species_name(you.species,you.experience_level)); + cprintf("%s", species_name(you.species,you.experience_level)); if (!shortgreet) cprintf("."); @@ -3126,7 +3119,7 @@ job_query: putch( letter ); cprintf( " - " ); - cprintf( get_class_name(i) ); + cprintf( "%s", get_class_name(i) ); if (j % 2) cprintf(EOL); @@ -3301,7 +3294,7 @@ void give_items_skills() { you.inv[0].quantity = 1; you.inv[0].base_type = OBJ_WEAPONS; - you.inv[0].sub_type = WPN_CLUB; + you.inv[0].sub_type = WPN_ANCUS; you.inv[0].plus = 0; you.inv[0].special = 0; you.inv[0].colour = BROWN; @@ -3399,7 +3392,7 @@ void give_items_skills() if (you.species == SP_KOBOLD) { - you.skills[SK_THROWING] = 1; + you.skills[SK_RANGED_COMBAT] = 1; you.skills[SK_DARTS] = 1; you.skills[SK_DODGING] = 1; you.skills[SK_STEALTH] = 1; @@ -3419,13 +3412,13 @@ void give_items_skills() else { // Players get dodging or armour skill depending on their - // starting armour now (note: the armour has to be quiped + // starting armour now (note: the armour has to be equipped // for this function to work) you.skills[(player_light_armour()? SK_DODGING : SK_ARMOUR)] = 2; you.skills[SK_SHIELDS] = 2; - you.skills[SK_THROWING] = 2; + you.skills[SK_RANGED_COMBAT] = 2; you.skills[(coinflip() ? SK_STABBING : SK_SHIELDS)]++; } break; @@ -3485,12 +3478,7 @@ void give_items_skills() you.equip[EQ_BODY_ARMOUR] = 1; // extra items being tested: - you.inv[2].base_type = OBJ_BOOKS; - you.inv[2].sub_type = BOOK_MINOR_MAGIC_I + random2(3); - you.inv[2].quantity = 1; - you.inv[2].plus = 0; // = 127 - you.inv[2].special = 1; - you.inv[2].colour = CYAN; + choose_book( you.inv[2], BOOK_MINOR_MAGIC_I, 3 ); you.skills[SK_DODGING] = 1; you.skills[SK_STEALTH] = 1; @@ -3652,7 +3640,7 @@ void give_items_skills() you.skills[SK_STEALTH] = 2; you.skills[SK_STABBING] = 1; you.skills[SK_DODGING + random2(3)]++; - you.skills[SK_THROWING] = 1; + you.skills[SK_RANGED_COMBAT] = 1; you.skills[SK_DARTS] = 1; you.skills[SK_TRAPS_DOORS] = 2; break; @@ -3856,12 +3844,12 @@ void give_items_skills() you.skills[SK_DODGING] = 1; you.skills[SK_STEALTH] = 3; you.skills[SK_STABBING] = 2; - you.skills[SK_THROWING] = 1; + you.skills[SK_RANGED_COMBAT] = 1; you.skills[SK_DARTS] = 1; if (you.species == SP_DEEP_ELF) you.skills[SK_CROSSBOWS] = 1; else - you.skills[SK_THROWING] += 1; + you.skills[SK_RANGED_COMBAT] += 1; break; @@ -3874,7 +3862,7 @@ void give_items_skills() { you.inv[0].quantity = 1; you.inv[0].base_type = OBJ_WEAPONS; - you.inv[0].sub_type = WPN_CLUB; + you.inv[0].sub_type = WPN_ANCUS; you.inv[0].plus = 0; you.inv[0].plus2 = 0; you.inv[0].special = 0; @@ -3954,7 +3942,7 @@ void give_items_skills() you.skills[SK_POLEARMS] = 1; you.skills[SK_ARMOUR] = 2; you.skills[SK_DODGING] = 2; - you.skills[SK_THROWING] = 2; + you.skills[SK_RANGED_COMBAT] = 2; } break; @@ -4017,7 +4005,7 @@ void give_items_skills() you.equip[EQ_BODY_ARMOUR] = 4; you.skills[SK_FIGHTING] = 2; - you.skills[SK_THROWING] = 3; + you.skills[SK_RANGED_COMBAT] = 3; // Removing spellcasting -- bwr // you.skills[SK_SPELLCASTING] = 1; @@ -4064,7 +4052,7 @@ void give_items_skills() you.skills[SK_POLEARMS] = 2; you.skills[SK_DODGING] = 2; - you.skills[SK_THROWING] += 1; + you.skills[SK_RANGED_COMBAT] += 1; break; default: @@ -4108,9 +4096,15 @@ void give_items_skills() you.equip[EQ_WEAPON] = 0; you.equip[EQ_BODY_ARMOUR] = 1; - you.inv[2].base_type = OBJ_BOOKS; - you.inv[2].sub_type = give_first_conjuration_book(); - you.inv[2].plus = 0; + + if ( you.char_class == JOB_CONJURER ) + choose_book( you.inv[2], BOOK_CONJURATIONS_I, 2 ); + else + { + you.inv[2].base_type = OBJ_BOOKS; + // subtype will always be overridden + you.inv[2].plus = 0; + } switch (you.char_class) { @@ -4283,7 +4277,7 @@ void give_items_skills() you.skills[SK_STEALTH] = 1; if (you.species == SP_GNOME && you.char_class == JOB_EARTH_ELEMENTALIST) - you.skills[SK_THROWING]++; + you.skills[SK_RANGED_COMBAT]++; else you.skills[ coinflip() ? SK_DODGING : SK_STEALTH ]++; break; @@ -4332,7 +4326,7 @@ void give_items_skills() you.skills[SK_FIGHTING] = 1; you.skills[SK_UNARMED_COMBAT] = 3; - you.skills[SK_THROWING] = 2; + you.skills[SK_RANGED_COMBAT] = 2; you.skills[SK_DODGING] = 2; you.skills[SK_SPELLCASTING] = 2; you.skills[SK_TRANSMIGRATION] = 2; @@ -4419,7 +4413,7 @@ void give_items_skills() you.equip[EQ_WEAPON] = 0; you.equip[EQ_BODY_ARMOUR] = 1; - you.skills[SK_THROWING] = 1; + you.skills[SK_RANGED_COMBAT] = 1; you.skills[SK_DARTS] = 2; you.skills[SK_DODGING] = 2; you.skills[SK_STEALTH] = 1; @@ -4722,7 +4716,7 @@ void give_items_skills() you.skills[SK_FIGHTING] = 2; you.skills[SK_DODGING] = 1; you.skills[SK_SHIELDS] = 1; - you.skills[SK_THROWING] = 2; + you.skills[SK_RANGED_COMBAT] = 2; you.skills[SK_STAVES] = 3; you.skills[SK_INVOCATIONS] = 2; break; @@ -4806,7 +4800,7 @@ void give_items_skills() you.skills[SK_STEALTH] = 2; you.skills[SK_STABBING] = 2; you.skills[SK_DODGING + random2(3)]++; - //you.skills[SK_THROWING] = 1; //jmf: removed these, added magic below + //you.skills[SK_RANGED_COMBAT] = 1; //jmf: removed these, added magic below //you.skills[SK_DARTS] = 1; you.skills[SK_SPELLCASTING] = 1; you.skills[SK_ENCHANTMENTS] = 1; diff --git a/crawl-ref/source/notes.cc b/crawl-ref/source/notes.cc new file mode 100644 index 0000000000..0af29ec998 --- /dev/null +++ b/crawl-ref/source/notes.cc @@ -0,0 +1,405 @@ +/* + * Modified for Crawl Reference by $Author$ on $Date$ + */ + +#include + +#include "AppHdr.h" +#include "notes.h" + +#include "files.h" +#include "Kills.h" +#include "message.h" +#include "misc.h" +#include "mon-pick.h" +#include "mutation.h" +#include "religion.h" +#include "skills2.h" +#include "spl-util.h" + +#define NOTES_VERSION_NUMBER 1001 + +std::vector note_list; + +/* I can't believe I'm writing code this bad */ +static int real_god_power( int religion, int idx ) { + switch ( religion ) { + case GOD_NO_GOD: + case GOD_XOM: + case GOD_NEMELEX_XOBEH: + return -1; + case GOD_ZIN: + case GOD_SHINING_ONE: + case GOD_YREDELEMNUL: + case GOD_MAKHLEB: + case GOD_ELYVILON: + return idx; + case GOD_KIKUBAAQUDGHA: + if ( idx < 3 ) + return idx; + if ( idx == 3 ) + return -1; + return idx-1; + case GOD_VEHUMET: + return ( idx > 3 ? -1 : idx ); + case GOD_OKAWARU: + if ( idx < 2 ) + return idx; + if ( idx == 2 || idx == 3 ) + return -1; + return idx - 2; + case GOD_SIF_MUNA: + if ( idx == 2 || idx == 4 ) return -1; + if ( idx < 2 ) return idx; + if ( idx == 3 ) return 2; + case GOD_TROG: + if ( idx == 2 || idx == 4 ) return -1; + if ( idx < 2 ) return idx; + if ( idx == 3 ) return idx-1; + default: + return -1; + } +} + +static bool is_noteworthy_skill_level( int level ) { + unsigned i; + for ( i = 0; i < Options.note_skill_levels.size(); ++i ) + if ( level == Options.note_skill_levels[i] ) + return true; + return false; +} + +static bool is_highest_skill( int skill ) { + for ( int i = 0; i < NUM_SKILLS; ++i ) { + if ( i == skill ) + continue; + if ( you.skills[i] >= you.skills[skill] ) + return false; + } + return true; +} + +static bool is_noteworthy_hp( int hp, int maxhp ) { + return (hp > 0 && Options.note_hp_percent && + hp <= (maxhp * Options.note_hp_percent) / 100); +} + +static int dungeon_branch_depth( unsigned char branch ) { + if ( branch > BRANCH_CAVERNS ) // last branch + return -1; + switch ( branch ) { + case BRANCH_MAIN_DUNGEON: + return 27; + case BRANCH_DIS: + case BRANCH_GEHENNA: + case BRANCH_COCYTUS: + case BRANCH_TARTARUS: + return 7; + case BRANCH_VESTIBULE_OF_HELL: + return 1; + case BRANCH_INFERNO: + case BRANCH_THE_PIT: + return -1; + default: + return branch_depth( branch - 10 ); + } +} + +static bool is_noteworthy_dlevel( unsigned short place ) { + unsigned const char branch = (unsigned char) ((place >> 8) & 0xFF); + const int lev = (place & 0xFF); + + /* Special levels (Abyss, etc.) are always interesting */ + if ( lev == 0xFF ) + return true; + + if ( lev == dungeon_branch_depth(branch) || + (branch == BRANCH_MAIN_DUNGEON && (lev % 5) == 0) || + (branch != BRANCH_MAIN_DUNGEON && lev == 1) ) + return true; + + return false; +} + +/* Is a note worth taking? + This function assumes that game state has not changed since + the note was taken, e.g. you.* is valid. + */ +static bool is_noteworthy( const Note& note ) { + + /* always noteworthy */ + if ( note.type == NOTE_XP_LEVEL_CHANGE || + note.type == NOTE_GET_GOD || + note.type == NOTE_GOD_GIFT || + note.type == NOTE_GET_MUTATION || + note.type == NOTE_LOSE_MUTATION || + note.type == NOTE_SEEN_MONSTER || + note.type == NOTE_KILL_MONSTER || + note.type == NOTE_POLY_MONSTER || + note.type == NOTE_USER_NOTE || + note.type == NOTE_MESSAGE || + note.type == NOTE_LOSE_GOD ) + return true; + + /* never noteworthy, hooked up for fun or future use */ + if ( note.type == NOTE_GET_ITEM || + note.type == NOTE_MP_CHANGE || + note.type == NOTE_MAXHP_CHANGE || + note.type == NOTE_MAXMP_CHANGE ) + return false; + + /* god powers might be noteworthy if it's an actual power */ + if ( note.type == NOTE_GOD_POWER && + real_god_power(note.first, note.second) == -1 ) + return false; + + /* hp noteworthiness is handled in its own function */ + if ( note.type == NOTE_HP_CHANGE && + !is_noteworthy_hp(note.first, note.second) ) + return false; + + /* skills are noteworthy if in the skill value list or if + it's a new maximal skill (depending on options) */ + if ( note.type == NOTE_GAIN_SKILL ) { + if ( is_noteworthy_skill_level(note.second) ) + return true; + if ( Options.note_skill_max && is_highest_skill(note.first) ) + return true; + return false; + } + + if ( note.type == NOTE_DUNGEON_LEVEL_CHANGE && + !is_noteworthy_dlevel(note.packed_place) ) + return false; + + /* Learning a spell is always noteworthy if note_all_spells is set */ + if ( note.type == NOTE_LEARN_SPELL && Options.note_all_spells ) + return true; + + for ( unsigned i = 0; i < note_list.size(); ++i ) { + if ( note_list[i].type != note.type ) + continue; + const Note& rnote( note_list[i] ); + switch ( note.type ) { + case NOTE_DUNGEON_LEVEL_CHANGE: + if ( rnote.packed_place == note.packed_place ) + return false; + break; + case NOTE_LEARN_SPELL: + if (spell_difficulty(rnote.first) >= spell_difficulty(note.first)) + return false; + break; + case NOTE_GOD_POWER: + if ( rnote.first == note.first && rnote.second == note.second ) + return false; + break; + case NOTE_ID_ITEM: + /* re-id'ing an item, e.g. second copy of book, isn't + noteworthy */ + if ( rnote.name == note.name ) + return false; + break; + case NOTE_HP_CHANGE: + /* not if we have a recent warning */ + if ( (note.turn - rnote.turn < 5) && + /* unless we've lost half our HP since then */ + (note.first * 2 >= rnote.first) ) + return false; + break; + default: + mpr("Buggy note passed: unknown note type"); + // Return now, rather than give a "Buggy note passed" message + // for each note of the matching type in the note list. + return true; + } + } + return true; +} + +const char* number_to_ordinal( int number ) { + const char* ordinals[5] = { "first", "second", "third", "fourth", + "fifth" }; + if ( number < 1) + return "[unknown ordinal (too small)]"; + if ( number > 5 ) + return "[unknown ordinal (too big)]"; + return ordinals[number-1]; +} + +std::string describe_note( const Note& note ) { + char buf[200], buf2[50]; + + switch ( note.type ) { + case NOTE_HP_CHANGE: + // [ds] Shortened HP change note from "Had X hitpoints" to accommodate + // the cause for the loss of hitpoints. + snprintf(buf, sizeof buf, "HP: %d/%d [%s]", + note.first, note.second, note.name.c_str()); + break; + case NOTE_MP_CHANGE: + snprintf(buf, sizeof buf, "Mana: %d/%d", note.first, note.second); + break; + case NOTE_MAXHP_CHANGE: + snprintf(buf, sizeof buf, "Reached %d max hit points", note.first); + break; + case NOTE_MAXMP_CHANGE: + snprintf(buf, sizeof buf, "Reached %d max mana", note.first); + break; + case NOTE_XP_LEVEL_CHANGE: + snprintf(buf, sizeof buf, "Reached XP level %d. %s", note.first, + note.name.c_str()); + break; + case NOTE_DUNGEON_LEVEL_CHANGE: + snprintf(buf, sizeof buf, "Entered %s", + place_name(note.packed_place, true, true).c_str()); + break; + case NOTE_LEARN_SPELL: + snprintf(buf, sizeof buf, "Learned a level %d spell: %s", + spell_difficulty(note.first), spell_title(note.first)); + break; + case NOTE_GET_GOD: + snprintf(buf, sizeof buf, "Became a worshipper of %s", + god_name(note.first, true)); + break; + case NOTE_LOSE_GOD: + snprintf(buf, sizeof buf, "Fell from the grace of %s", + god_name(note.first)); + break; + case NOTE_GOD_GIFT: + snprintf(buf, sizeof buf, "Received a gift from %s", + god_name(note.first)); + break; + case NOTE_ID_ITEM: + if (note.desc.length() > 0) + snprintf(buf, sizeof buf, "Identified %s (%s)", note.name.c_str(), + note.desc.c_str()); + else + snprintf(buf, sizeof buf, "Identified %s", note.name.c_str()); + break; + case NOTE_GET_ITEM: + snprintf(buf, sizeof buf, "Got %s", note.name.c_str()); + break; + case NOTE_GAIN_SKILL: + snprintf(buf, sizeof buf, "Reached skill %d in %s", + note.second, skill_name(note.first)); + break; + case NOTE_SEEN_MONSTER: + snprintf(buf, sizeof buf, "Noticed %s", note.name.c_str() ); + break; + case NOTE_KILL_MONSTER: + snprintf(buf, sizeof buf, "Defeated %s", note.name.c_str()); + break; + case NOTE_POLY_MONSTER: + snprintf(buf, sizeof buf, "%s changed form", note.name.c_str() ); + break; + case NOTE_GOD_POWER: + snprintf(buf, sizeof buf, "Acquired %s's %s power", + god_name(note.first), + number_to_ordinal(real_god_power(note.first, note.second)+1)); + break; + case NOTE_GET_MUTATION: + snprintf(buf, sizeof buf, "Gained mutation: %s", + mutation_name(note.first, note.second == 0 ? 1 : note.second)); + break; + case NOTE_LOSE_MUTATION: + snprintf(buf, sizeof buf, "Lost mutation: %s", + mutation_name(note.first, + note.second == 3 ? 3 : note.second+1)); + break; + case NOTE_USER_NOTE: + snprintf(buf, sizeof buf, "%s", note.name.c_str()); + break; + case NOTE_MESSAGE: + snprintf(buf, sizeof buf, "%s", note.name.c_str()); + break; + default: + snprintf(buf, sizeof buf, "Buggy note description: unknown note type"); + break; + } + snprintf(buf2, sizeof buf2, "| %5ld | ", note.turn ); + std::string placename = short_place_name(note.packed_place); + while ( placename.length() < 7 ) + placename += ' '; + return std::string(buf2) + placename + std::string(" | ") + + std::string(buf); +} + +Note::Note() { + turn = you.num_turns; + packed_place = get_packed_place(); +} + +Note::Note( NOTE_TYPES t, int f, int s, const char* n, const char* d ) : + type(t), first(f), second(s) { + if (n) + name = std::string(n); + if (d) + desc = std::string(d); + turn = you.num_turns; + packed_place = get_packed_place(); +} + +void Note::save( FILE* fp ) const { + writeLong( fp, type ); + writeLong( fp, turn ); + writeShort( fp, packed_place ); + writeLong( fp, first ); + writeLong( fp, second ); + writeString( fp, name ); + writeString( fp, desc ); +} + +void Note::load( FILE* fp ) { + type = (NOTE_TYPES)(readLong( fp )); + turn = readLong( fp ); + packed_place = readShort( fp ); + first = readLong( fp ); + second = readLong( fp ); + name = readString( fp ); + desc = readString( fp ); +} + +bool notes_active = false; + +bool notes_are_active() { + return notes_active; +} + +void take_note( const Note& note ) { + if ( notes_active && is_noteworthy( note ) ) + note_list.push_back( note ); +} + +void activate_notes( bool active ) { + notes_active = active; +} + +void save_notes( FILE* fp ) { + writeLong( fp, NOTES_VERSION_NUMBER ); + writeLong( fp, note_list.size() ); + for ( unsigned i = 0; i < note_list.size(); ++i ) + note_list[i].save(fp); +} + +void load_notes( FILE* fp ) { + if ( readLong(fp) != NOTES_VERSION_NUMBER ) + return; + + const long num_notes = readLong(fp); + for ( long i = 0; i < num_notes; ++i ) { + Note new_note; + new_note.load(fp); + note_list.push_back(new_note); + } +} + +void make_user_note() { + mpr("Enter note: ", MSGCH_PROMPT); + char buf[400]; + bool validline = !cancelable_get_line(buf, sizeof(buf)); + if ( !validline || (!*buf) ) + return; + Note unote(NOTE_USER_NOTE); + unote.name = std::string(buf); + take_note(unote); +} diff --git a/crawl-ref/source/notes.h b/crawl-ref/source/notes.h new file mode 100644 index 0000000000..882fe81250 --- /dev/null +++ b/crawl-ref/source/notes.h @@ -0,0 +1,67 @@ +/* + * File: notes.cc + * Summary: Notetaking stuff + * Written by: Haran Pilpel + * + * Change History (most recent first): + * + * <1> -/--/-- PH Created + */ + +#ifndef NOTES_H +#define NOTES_H + +#include +#include +#include + +enum NOTE_TYPES { + NOTE_HP_CHANGE = 0, /* needs: new hp, max hp */ + NOTE_MAXHP_CHANGE, /* needs: new maxhp */ + NOTE_MP_CHANGE, /* needs: new mp, max mp */ + NOTE_MAXMP_CHANGE, /* needs: new maxmp */ + NOTE_XP_LEVEL_CHANGE, /* needs: new xplevel */ + NOTE_DUNGEON_LEVEL_CHANGE, /* needs: branch, subdepth */ + NOTE_LEARN_SPELL, /* needs: spell idx */ + NOTE_GET_GOD, /* needs: god id */ + NOTE_GOD_GIFT, /* needs: god id */ + NOTE_GOD_POWER, /* needs: god id, idx */ + NOTE_GET_MUTATION, /* needs: mutation idx */ + NOTE_LOSE_MUTATION, /* needs: mutation idx */ + NOTE_ID_ITEM, /* needs: item name (string) */ + /* NOT HOOKED YET */ + NOTE_GET_ITEM, /* needs: item name (string) */ + NOTE_GAIN_SKILL, /* needs: skill id, level */ + NOTE_SEEN_MONSTER, /* needs: monster name (string) */ + NOTE_KILL_MONSTER, /* needs: monster name (string) */ + NOTE_POLY_MONSTER, /* needs: monster name (string) */ + NOTE_USER_NOTE, /* needs: description string */ + NOTE_MESSAGE, /* needs: message string */ + NOTE_LOSE_GOD, /* needs: god id */ + NOTE_NUM_TYPES +}; + +struct Note { + Note(); + Note( NOTE_TYPES t, int f = 0, int s = 0, const char* n = 0, + const char* d = 0); + NOTE_TYPES type; + int first, second; + long turn; + unsigned short packed_place; + std::string name; + std::string desc; + void load( FILE* fp ); + void save( FILE* fp ) const; +}; + +extern std::vector note_list; +std::string describe_note( const Note& note ); +void activate_notes( bool active ); +bool notes_are_active(); +void take_note( const Note& note ); +void save_notes( FILE* fp ); +void load_notes( FILE* fp ); +void make_user_note(); + +#endif diff --git a/crawl-ref/source/ouch.cc b/crawl-ref/source/ouch.cc index f9a5f514dd..4cc78e65d8 100644 --- a/crawl-ref/source/ouch.cc +++ b/crawl-ref/source/ouch.cc @@ -44,18 +44,6 @@ #include #endif -#ifdef USE_EMX -#include -#include -#include -#endif - -#ifdef OS9 -#include -#else -#include -#endif - #include "ouch.h" #ifdef __MINGW32__ @@ -70,9 +58,11 @@ #include "hiscores.h" #include "invent.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "macro.h" #include "mon-util.h" +#include "notes.h" #include "player.h" #include "randart.h" #include "religion.h" @@ -98,8 +88,7 @@ int check_your_resists(int hurted, int flavour) #endif if (flavour == BEAM_FIRE || flavour == BEAM_LAVA - || flavour == BEAM_HELLFIRE || flavour == BEAM_EXPLOSION - || flavour == BEAM_FRAG) + || flavour == BEAM_HELLFIRE || flavour == BEAM_FRAG) { if (you.duration[DUR_CONDENSATION_SHIELD] > 0) { @@ -111,6 +100,21 @@ int check_your_resists(int hurted, int flavour) switch (flavour) { + case BEAM_STEAM: + resist = player_res_steam(); + if (resist > 0) + { + canned_msg(MSG_YOU_RESIST); + hurted /= (1 + (resist * resist)); + } + else if (resist < 0) + { + // We could use a superior message. + mpr("It burns terribly!"); + hurted = hurted * 15 / 10; + } + break; + case BEAM_FIRE: resist = player_res_fire(); if (resist > 0) @@ -169,6 +173,9 @@ int check_your_resists(int hurted, int flavour) break; case BEAM_POISON_ARROW: + // [dshaligram] NOT importing uber-poison arrow from 4.1. Giving no + // bonus to poison resistant players seems strange and unnecessarily + // arbitrary. resist = player_res_poison(); if (!resist) @@ -179,7 +186,7 @@ int check_your_resists(int hurted, int flavour) hurted /= 2; if (!you.is_undead) - poison_player( coinflip() ? 2 : 3, true ); + poison_player( 2 + random2(3), true ); } break; @@ -225,6 +232,30 @@ int check_your_resists(int hurted, int flavour) hurted /= 10; } break; + + case BEAM_ACID: + if (player_res_acid()) + { + canned_msg( MSG_YOU_RESIST ); + hurted = hurted * player_acid_resist_factor() / 100; + } + break; + + case BEAM_MIASMA: + if (player_prot_life() > random2(3)) + { + canned_msg( MSG_YOU_RESIST ); + hurted = 0; + } + break; + + case BEAM_HOLY: + if (!you.is_undead && you.species != SP_DEMONSPAWN) + { + canned_msg( MSG_YOU_RESIST ); + hurted = 0; + } + break; } /* end switch */ return (hurted); @@ -232,14 +263,10 @@ int check_your_resists(int hurted, int flavour) void splash_with_acid( char acid_strength ) { - /* affects equip only? - assume that a message has already been sent, also that damage has - already been done - */ char splc = 0; int dam = 0; - const bool wearing_cloak = (you.equip[EQ_CLOAK] == -1); + const bool wearing_cloak = (you.equip[EQ_CLOAK] != -1); for (splc = EQ_CLOAK; splc <= EQ_BODY_ARMOUR; splc++) { @@ -255,10 +282,19 @@ void splash_with_acid( char acid_strength ) item_corrode( you.equip[splc] ); } - if (dam) + if (dam > 0) { - mpr( "The acid burns!" ); - ouch( dam, 0, KILLED_BY_ACID ); + const int post_res_dam = dam * player_acid_resist_factor() / 100; + + if (post_res_dam > 0) + { + mpr( "The acid burns!" ); + + if (post_res_dam < dam) + canned_msg(MSG_YOU_RESIST); + + ouch( post_res_dam, 0, KILLED_BY_ACID ); + } } } // end splash_with_acid() @@ -313,7 +349,7 @@ void item_corrode( char itco ) suppress_msg = true; } else if ((you.inv[itco].sub_type == ARM_CRYSTAL_PLATE_MAIL - || cmp_equip_race( you.inv[itco], ISFLAG_DWARVEN )) + || get_equip_race(you.inv[itco]) == ISFLAG_DWARVEN) && !one_chance_in(5)) { it_resists = true; @@ -328,7 +364,7 @@ void item_corrode( char itco ) it_resists = true; suppress_msg = true; } - else if (cmp_equip_race( you.inv[itco], ISFLAG_DWARVEN ) + else if (get_equip_race(you.inv[itco]) == ISFLAG_DWARVEN && !one_chance_in(5)) { it_resists = true; @@ -337,12 +373,17 @@ void item_corrode( char itco ) break; case OBJ_MISSILES: - if (cmp_equip_race( you.inv[itco], ISFLAG_DWARVEN ) && !one_chance_in(5)) + if (get_equip_race(you.inv[itco]) == ISFLAG_DWARVEN + && !one_chance_in(5)) { it_resists = true; suppress_msg = false; } break; + default: + /* items which aren't missiles, etc...Could happen + if we're wielding a deck, say */ + return; } // determine chance of corrosion {dlb}: @@ -397,65 +438,145 @@ void item_corrode( char itco ) return; } // end item_corrode() -void scrolls_burn(char burn_strength, char target_class) +// Helper function for the expose functions below. +// This currently works because elements only target a single type each. +static int get_target_class( beam_type flavour ) { + int target_class = OBJ_UNASSIGNED; - unsigned char burnc; - unsigned char burn2; - unsigned char burn_no = 0; - - if (wearing_amulet(AMU_CONSERVATION) && !one_chance_in(10)) + switch (flavour) { -#if DEBUG_DIAGNOSTICS - mpr( "Amulet conserves.", MSGCH_DIAGNOSTICS ); -#endif - return; + case BEAM_FIRE: + case BEAM_LAVA: + case BEAM_NAPALM: + case BEAM_HELLFIRE: + target_class = OBJ_SCROLLS; + break; + + case BEAM_COLD: + case BEAM_FRAG: + target_class = OBJ_POTIONS; + break; + + case BEAM_SPORE: + target_class = OBJ_FOOD; + break; + + default: + break; } - for (burnc = 0; burnc < ENDOFPACK; burnc++) + return (target_class); +} + +// XXX: These expose functions could use being reworked into a real system... +// the usage and implementation is currently very hacky. +// Handles the destruction of inventory items from the elements. +static void expose_invent_to_element( beam_type flavour, int strength ) +{ + int i, j; + int num_dest = 0; + + const int target_class = get_target_class( flavour ); + if (target_class == OBJ_UNASSIGNED) + return; + + // Currently we test against each stack (and item in the stack) + // independantly at strength%... perhaps we don't want that either + // because it makes the system very fair and removes the protection + // factor of junk (which might be more desirable for game play). + for (i = 0; i < ENDOFPACK; i++) { - if (!you.inv[burnc].quantity) - continue; - if (you.inv[burnc].base_type != target_class) + if (!is_valid_item( you.inv[i] )) continue; - for (burn2 = 0; burn2 < you.inv[burnc].quantity; burn2++) + if (is_valid_item( you.inv[i] ) + && (you.inv[i].base_type == target_class + || (target_class == OBJ_FOOD + && you.inv[i].base_type == OBJ_CORPSES))) { - if (random2(70) < burn_strength) + if (player_item_conserve() && !one_chance_in(10)) + continue; + + for (j = 0; j < you.inv[i].quantity; j++) { - burn_no++; + if (random2(100) < strength) + { + num_dest++; - if (burnc == you.equip[EQ_WEAPON]) - you.wield_change = true; + if (i == you.equip[EQ_WEAPON]) + you.wield_change = true; - if (dec_inv_item_quantity( burnc, 1 )) - break; + if (dec_inv_item_quantity( i, 1 )) + break; + } } } } - if (burn_no == 1) + if (num_dest > 0) { - if (target_class == OBJ_SCROLLS) - mpr("A scroll you are carrying catches fire!"); - else if (target_class == OBJ_POTIONS) - mpr("A potion you are carrying freezes and shatters!"); - else if (target_class == OBJ_FOOD) - mpr("Some of your food is covered with spores!"); + switch (target_class) + { + case OBJ_SCROLLS: + snprintf( info, INFO_SIZE, "%s you are carrying %s fire!", + (num_dest > 1) ? "Some of the scrolls" : "A scroll", + (num_dest > 1) ? "catch" : "catches" ); + break; + + case OBJ_POTIONS: + snprintf( info, INFO_SIZE, "%s you are carrying %s and %s!", + (num_dest > 1) ? "Some of the potions" : "A potion", + (num_dest > 1) ? "freeze" : "freezes", + (num_dest > 1) ? "shatter" : "shatters" ); + break; + + case OBJ_FOOD: + snprintf( info, INFO_SIZE, "Some of your food is covered with spores!" ); + break; + + default: + snprintf( info, INFO_SIZE, "%s you are carrying %s destroyed!", + (num_dest > 1) ? "Some items" : "An item", + (num_dest > 1) ? "were" : "was" ); + break; + } + + mpr( info ); // XXX: should this be in a channel? } - else if (burn_no > 1) +} + + +// Handle side-effects for exposure to element other than damage. +// This function exists because some code calculates its own damage +// instead of using check_resists and we want to isolate all the special +// code they keep having to do... namely condensation shield checks, +// you really can't expect this function to even be called for much else. +// +// This function now calls expose_invent_to_element if strength > 0. +// +// XXX: this function is far from perfect and a work in progress. +void expose_player_to_element( beam_type flavour, int strength ) +{ + // Note that BEAM_TELEPORT is sent here when the player + // blinks or teleports. + if (flavour == BEAM_FIRE || flavour == BEAM_LAVA + || flavour == BEAM_HELLFIRE || flavour == BEAM_FRAG + || flavour == BEAM_TELEPORT || flavour == BEAM_NAPALM + || flavour == BEAM_STEAM) { - if (target_class == OBJ_SCROLLS) - mpr("Some of the scrolls you are carrying catch fire!"); - else if (target_class == OBJ_POTIONS) - mpr("Some of the potions you are carrying freeze and shatter!"); - else if (target_class == OBJ_FOOD) - mpr("Some of your food is covered with spores!"); + if (you.duration[DUR_CONDENSATION_SHIELD] > 0) + { + mprf(MSGCH_DURATION, "Your icy shield dissipates!"); + you.duration[DUR_CONDENSATION_SHIELD] = 0; + you.redraw_armour_class = true; + } } - /* burn_no could be 0 */ + + if (strength) + expose_invent_to_element( flavour, strength ); } - // end scrolls_burn() void lose_level(void) { // because you.experience is unsigned long, if it's going to be -ve @@ -481,6 +602,11 @@ void lose_level(void) calc_hp(); calc_mp(); + char buf[200]; + sprintf(buf, "HP: %d/%d MP: %d/%d", + you.hp, you.hp_max, you.magic_points, you.max_magic_points); + take_note(Note(NOTE_XP_LEVEL_CHANGE, you.experience_level, 0, buf)); + you.redraw_experience = 1; } // end lose_level() @@ -548,9 +674,6 @@ void drain_exp(void) // death_source should be set to zero for non-monsters {dlb} void ouch( int dam, int death_source, char death_type, const char *aux ) { - int d = 0; - int e = 0; - ait_hp_loss hpl(dam, death_type); interrupt_activity( AI_HP_LOSS, &hpl ); @@ -569,11 +692,6 @@ void ouch( int dam, int death_source, char death_type, const char *aux ) return; } - if (you_are_delayed()) - { - stop_delay(); - } - if (dam > -9000) // that is, a "death" caused by hp loss {dlb} { switch (you.religion) @@ -590,7 +708,6 @@ void ouch( int dam, int death_source, char death_type, const char *aux ) case GOD_ZIN: case GOD_SHINING_ONE: case GOD_ELYVILON: - case GOD_OKAWARU: case GOD_YREDELEMNUL: if (dam >= you.hp && you.duration[DUR_PRAYER] && random2(you.piety) >= 30) @@ -614,6 +731,14 @@ void ouch( int dam, int death_source, char death_type, const char *aux ) { mpr( "* * * LOW HITPOINT WARNING * * *", MSGCH_DANGER ); } + take_note( + Note( + NOTE_HP_CHANGE, + you.hp, + you.hp_max, + scorefile_entry(dam, death_source, death_type, aux, true) + .death_description(scorefile_entry::DDV_TERSE) + .c_str()) ); if (you.hp > 0) return; @@ -651,228 +776,11 @@ void ouch( int dam, int death_source, char death_type, const char *aux ) //okay, so you're dead: - // do points first. - long points = you.gold; - points += (you.experience * 7) / 10; - - //if (death_type == KILLED_BY_WINNING) points += points / 2; - //if (death_type == KILLED_BY_LEAVING) points += points / 10; - // these now handled by giving player the value of their inventory - char temp_id[4][50]; - - for (d = 0; d < 4; d++) - { - for (e = 0; e < 50; e++) - temp_id[d][e] = 1; - } + // prevent bogus notes + activate_notes(false); // CONSTRUCT SCOREFILE ENTRY - struct scorefile_entry se; - - // Score file entry version: - // - // 4.0 - original versioned entry - // 4.1 - added real_time and num_turn fields - // 4.2 - stats and god info - - se.version = 4; - se.release = 2; - - strncpy( se.name, you.your_name, kNameLen ); - se.name[ kNameLen - 1 ] = '\0'; -#ifdef MULTIUSER - se.uid = (int) getuid(); -#else - se.uid = 0; -#endif - - FixedVector< int, NUM_RUNE_TYPES > rune_array; - - se.num_runes = 0; - se.num_diff_runes = 0; - - for (int i = 0; i < NUM_RUNE_TYPES; i++) - rune_array[i] = 0; - - // Calculate value of pack and runes when character leaves dungeon - if (death_type == KILLED_BY_LEAVING || death_type == KILLED_BY_WINNING) - { - for (d = 0; d < ENDOFPACK; d++) - { - if (is_valid_item( you.inv[d] )) - { - points += item_value( you.inv[d], temp_id, true ); - - if (you.inv[d].base_type == OBJ_MISCELLANY - && you.inv[d].sub_type == MISC_RUNE_OF_ZOT) - { - if (rune_array[ you.inv[d].plus ] == 0) - se.num_diff_runes++; - - se.num_runes += you.inv[d].quantity; - rune_array[ you.inv[d].plus ] += you.inv[d].quantity; - } - } - } - - // Bonus for exploring different areas, not for collecting a - // huge stack of demonic runes in Pandemonium (gold value - // is enough for those). -- bwr - if (se.num_diff_runes >= 3) - points += ((se.num_diff_runes + 2) * (se.num_diff_runes + 2) * 1000); - } - - // Players will have a hard time getting 1/10 of this (see XP cap): - if (points > 99999999) - points = 99999999; - - se.points = points; - se.race = you.species; - se.cls = you.char_class; - - // strcpy(se.race_class_name, ""); - se.race_class_name[0] = '\0'; - - se.lvl = you.experience_level; - se.best_skill = best_skill( SK_FIGHTING, NUM_SKILLS - 1, 99 ); - se.best_skill_lvl = you.skills[ se.best_skill ]; - se.death_type = death_type; - - // for death by monster - - // Set the default aux data value... - // If aux is passed in (ie for a trap), we'll default to that. - if (aux == NULL) - se.auxkilldata[0] = '\0'; - else - { - strncpy( se.auxkilldata, aux, ITEMNAME_SIZE ); - se.auxkilldata[ ITEMNAME_SIZE - 1 ] = '\0'; - } - - if ((death_type == KILLED_BY_MONSTER || death_type == KILLED_BY_BEAM) - && death_source >= 0 && death_source < MAX_MONSTERS) - { - struct monsters *monster = &menv[death_source]; - - if (monster->type > 0 || monster->type <= NUM_MONSTERS) - { - se.death_source = monster->type; - se.mon_num = monster->number; - - // Previously the weapon was only used for dancing weapons, - // but now we pass it in as a string through the scorefile - // entry to be appended in hiscores_format_single in long or - // medium scorefile formats. - // It still isn't used in monam for anything but flying weapons - // though - if (death_type == KILLED_BY_MONSTER - && monster->inv[MSLOT_WEAPON] != NON_ITEM) - { -#if HISCORE_WEAPON_DETAIL - set_ident_flags( mitm[monster->inv[MSLOT_WEAPON]], - ISFLAG_IDENT_MASK ); -#else - // changing this to ignore the pluses to keep it short - unset_ident_flags( mitm[monster->inv[MSLOT_WEAPON]], - ISFLAG_IDENT_MASK ); - - set_ident_flags( mitm[monster->inv[MSLOT_WEAPON]], - ISFLAG_KNOW_TYPE ); - - // clear "runed" description text to make shorter yet - set_equip_desc( mitm[monster->inv[MSLOT_WEAPON]], 0 ); -#endif - - // Setting this is redundant for dancing weapons, however - // we do care about the above indentification. -- bwr - if (monster->type != MONS_DANCING_WEAPON) - { - it_name( monster->inv[MSLOT_WEAPON], DESC_NOCAP_A, info ); - strncpy( se.auxkilldata, info, ITEMNAME_SIZE ); - se.auxkilldata[ ITEMNAME_SIZE - 1 ] = '\0'; - } - } - - strcpy( info, - monam( monster->number, monster->type, true, DESC_NOCAP_A, - monster->inv[MSLOT_WEAPON] ) ); - - strncpy( se.death_source_name, info, 40 ); - se.death_source_name[39] = '\0'; - } - } - else - { - se.death_source = death_source; - se.mon_num = 0; - se.death_source_name[0] = '\0'; - } - - se.damage = dam; - se.final_hp = you.hp; - se.final_max_hp = you.hp_max; - se.final_max_max_hp = you.hp_max + player_rotted(); - se.str = you.strength; - se.intel = you.intel; - se.dex = you.dex; - - se.god = you.religion; - if (you.religion != GOD_NO_GOD) - { - se.piety = you.piety; - se.penance = you.penance[you.religion]; - } - - // main dungeon: level is simply level - se.dlvl = you.your_level + 1; - switch (you.where_are_you) - { - case BRANCH_ORCISH_MINES: - case BRANCH_HIVE: - case BRANCH_LAIR: - case BRANCH_SLIME_PITS: - case BRANCH_VAULTS: - case BRANCH_CRYPT: - case BRANCH_HALL_OF_BLADES: - case BRANCH_HALL_OF_ZOT: - case BRANCH_ECUMENICAL_TEMPLE: - case BRANCH_SNAKE_PIT: - case BRANCH_ELVEN_HALLS: - case BRANCH_TOMB: - case BRANCH_SWAMP: - se.dlvl = you.your_level - you.branch_stairs[you.where_are_you - 10]; - break; - - case BRANCH_DIS: - case BRANCH_GEHENNA: - case BRANCH_VESTIBULE_OF_HELL: - case BRANCH_COCYTUS: - case BRANCH_TARTARUS: - case BRANCH_INFERNO: - case BRANCH_THE_PIT: - se.dlvl = you.your_level - 26; - break; - } - - se.branch = you.where_are_you; // no adjustments necessary. - se.level_type = you.level_type; // pandemonium, labyrinth, dungeon.. - - se.birth_time = you.birth_time; // start time of game - se.death_time = time( NULL ); // end time of game - - if (you.real_time != -1) - se.real_time = you.real_time + (se.death_time - you.start_time); - else - se.real_time = -1; - - se.num_turns = you.num_turns; - -#ifdef WIZARD - se.wiz_mode = (you.wizard ? 1 : 0); -#else - se.wiz_mode = 0; -#endif + scorefile_entry se(dam, death_source, death_type, aux); #ifdef SCORE_WIZARD_CHARACTERS // add this highscore to the score file. @@ -897,7 +805,6 @@ void ouch( int dam, int death_source, char death_type, const char *aux ) void end_game( struct scorefile_entry &se ) { int i; - char del_file[300]; // massive overkill! bool dead = true; if (se.death_type == KILLED_BY_LEAVING || @@ -913,47 +820,34 @@ void end_game( struct scorefile_entry &se ) { if (tmp_file_pairs[level][dungeon]) { - make_filename( info, you.your_name, level, dungeon, - false, false ); - unlink(info); + unlink(make_filename( you.your_name, level, dungeon, + false, false ).c_str()); } } } // temp level, if any - make_filename( info, you.your_name, 0, 0, true, false ); - unlink(info); + unlink( make_filename( you.your_name, 0, 0, true, false ).c_str() ); // create base file name -#ifdef SAVE_DIR_PATH - snprintf( info, INFO_SIZE, SAVE_DIR_PATH "%s%d", you.your_name, (int) getuid()); -#else - strncpy(info, you.your_name, kFileNameLen); - info[kFileNameLen] = '\0'; -#endif + std::string basename = get_savedir_filename( you.your_name, "", "" ); - // this is to catch the game package if it still exists. + const char* suffixes[] = { +#ifdef CLUA_BINDINGS + ".lua", +#endif #ifdef PACKAGE_SUFFIX - strcpy(del_file, info); - strcat(del_file, "." PACKAGE_SUFFIX); - unlink(del_file); + PACKAGE_SUFFIX , #endif + ".st", ".kil", ".tc", ".nts", ".sav" + }; - // last, but not least, delete player .sav file - strcpy(del_file, info); - - std::string basefile = del_file; + const int num_suffixes = sizeof(suffixes) / sizeof(const char*); - strcat(del_file, ".sav"); - unlink(del_file); - - // Delete record of stashes, kills, travel cache and lua save. - unlink( (basefile + ".st").c_str() ); - unlink( (basefile + ".kil").c_str() ); - unlink( (basefile + ".tc").c_str() ); -#ifdef CLUA_BINDINGS - unlink( (basefile + ".lua").c_str() ); -#endif + for ( i = 0; i < num_suffixes; ++i ) { + std::string tmpname = basename + suffixes[i]; + unlink( tmpname.c_str() ); + } // death message if (dead) @@ -978,7 +872,7 @@ void end_game( struct scorefile_entry &se ) invent( -1, dead ); clrscr(); - if (!dump_char( "morgue.txt", !dead )) + if (!dump_char( "morgue", !dead, true )) mpr("Char dump unsuccessful! Sorry about that."); #if DEBUG_DIAGNOSTICS //jmf: switched logic and moved "success" message to debug-only @@ -994,17 +888,17 @@ void end_game( struct scorefile_entry &se ) #endif clrscr(); - cprintf( "Goodbye, " ); - cprintf( you.your_name ); - cprintf( "." ); + cprintf( "Goodbye, %s.", you.your_name ); cprintf( EOL EOL " " ); // Space padding where # would go in list format char scorebuff[ HIGHSCORE_SIZE ]; - const int lines = hiscores_format_single_long( scorebuff, se, true ); - + hiscores_format_single_long( scorebuff, se, true ); // truncate - scorebuff[ HIGHSCORE_SIZE - 1 ] = '\0'; + scorebuff[ HIGHSCORE_SIZE - 1 ] = 0; + + const int lines = count_occurrences(scorebuff, EOL) + 1; + cprintf( scorebuff ); cprintf( EOL "Best Crawlers -" EOL ); diff --git a/crawl-ref/source/ouch.h b/crawl-ref/source/ouch.h index 23894dade5..451d17bdcf 100644 --- a/crawl-ref/source/ouch.h +++ b/crawl-ref/source/ouch.h @@ -19,6 +19,7 @@ #define DEATH_NAME_LENGTH 10 +#include "enum.h" // last updated 12may2000 {dlb} /* *********************************************************************** @@ -70,5 +71,6 @@ void lose_level(void); * *********************************************************************** */ void drain_exp(void); +void expose_player_to_element( beam_type flavour, int strength = 0 ); #endif diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc index 415e970fc0..9d510e9216 100644 --- a/crawl-ref/source/output.cc +++ b/crawl-ref/source/output.cc @@ -245,11 +245,11 @@ void print_stats(void) gotoxy(52, 11); #if DEBUG_DIAGNOSTICS - cprintf( "%d/%d (%d/%d)", + cprintf( "%d/%lu (%d/%d)", you.experience_level, you.experience, you.skill_cost_level, you.exp_available ); #else - cprintf( "%d/%d (%d)", + cprintf( "%d/%lu (%d)", you.experience_level, you.experience, you.exp_available ); #endif @@ -283,9 +283,9 @@ void print_stats(void) in_name( you.equip[EQ_WEAPON], DESC_INVENTORY, str_pass, Options.terse_hand ); - str_pass[39] = '\0'; + str_pass[39] = 0; - cprintf(str_pass); + cprintf("%s", str_pass); textcolor(LIGHTGREY); } else @@ -511,7 +511,7 @@ void print_stats(void) cprintf( "Rot " ); } - if (you.magic_contamination > 5) + if (you.magic_contamination >= 5) { textcolor( bad_ench_colour( you.magic_contamination, 15, 25 ) ); cprintf( "Glow " ); @@ -559,10 +559,12 @@ unsigned char* itosym1(int stat) unsigned char* itosym3(int stat) { return (unsigned char*)( (stat >= 3) ? "+ + +" : - (stat == 2) ? "+ + ." : - (stat == 1) ? "+ . ." : - (stat == 0) ? ". . ." : - "x . ."); + (stat == 2) ? "+ + ." : + (stat == 1) ? "+ . ." : + (stat == 0) ? ". . ." : + (stat == -1) ? "x . ." : + (stat == -2) ? "x x ." : + "x x x"); } static const char *s_equip_slot_names[] = @@ -600,10 +602,9 @@ int equip_name_to_slot(const char *s) void get_full_detail(char* buffer, bool calc_unid) { -#define FIR_AD buffer,44 #define CUR_AD &buffer[++lines*45],44 -#define BUF_SIZE 25*3*45 - int lines = 0; +#define BUF_SIZE 24*3*45 + int lines = -1; memset(buffer, 0, BUF_SIZE); @@ -642,7 +643,7 @@ void get_full_detail(char* buffer, bool calc_unid) } else { - snprintf(CUR_AD, "HP : %3d/%d (%d)", + snprintf(CUR_AD, "HP : %3d/%d (%d)", you.hp, you.hp_max, you.hp_max + player_rotted() ); } @@ -683,7 +684,7 @@ void get_full_detail(char* buffer, bool calc_unid) snprintf(CUR_AD, "Turns : %10ld", you.num_turns ); } - lines = 27; + lines = 24 + 1; snprintf(CUR_AD, "Res.Fire : %s", itosym3( player_res_fire(calc_unid) ) ); @@ -722,15 +723,14 @@ void get_full_detail(char* buffer, bool calc_unid) const char *slot = equip_slot_to_name( eqslot ); if (eqslot == EQ_LEFT_RING || eqslot == EQ_RIGHT_RING) slot = "Ring"; - else if (eqslot == EQ_BOOTS - && (you.species == SP_CENTAUR - || you.species == SP_NAGA)) + else if (eqslot == EQ_BOOTS && + (you.species == SP_CENTAUR || you.species == SP_NAGA)) slot = "Barding"; if ( you.equip[ e_order[i] ] != -1) { in_name( you.equip[ e_order[i] ], DESC_PLAIN, - str_pass, Options.terse_hand ); + str_pass, true ); snprintf(CUR_AD, "%-7s: %s", slot, str_pass); } else @@ -752,7 +752,7 @@ void get_full_detail(char* buffer, bool calc_unid) } } - lines = 52; + lines = 24 * 2 + 1; snprintf(CUR_AD, "See Invis. : %s", itosym1( player_see_invis(calc_unid) ) ); snprintf(CUR_AD, "Warding : %s", @@ -783,11 +783,7 @@ void get_full_detail(char* buffer, bool calc_unid) case SP_OGRE: snprintf(CUR_AD, "Saprovore : %s", itosym3(1) ); break; -#ifdef V_FIX - case SP_OGRE_MAGE: - snprintf(CUR_AD, "Voracious : %s", itosym1(1) ); - break; -#endif + default: snprintf(CUR_AD, "Gourmand : %s", itosym1(0) ); break; @@ -808,7 +804,7 @@ void get_full_detail(char* buffer, bool calc_unid) snprintf(CUR_AD, "Rnd.Telep. : %s", itosym1( player_teleport(calc_unid) ) ); snprintf(CUR_AD, "Ctrl.Telep.: %s", - itosym1( you.attribute[ATTR_CONTROL_TELEPORT] ) ); + itosym1( player_control_teleport(calc_unid) ) ); snprintf(CUR_AD, "Levitation : %s", itosym1( player_is_levitating() ) ); snprintf(CUR_AD, "Ctrl.Flight: %s", itosym1( wearing_amulet(AMU_CONTROLLED_FLIGHT, calc_unid) ) ); diff --git a/crawl-ref/source/output.h b/crawl-ref/source/output.h index 1fa77fd34b..3025c8899d 100644 --- a/crawl-ref/source/output.h +++ b/crawl-ref/source/output.h @@ -3,6 +3,8 @@ * Summary: Functions used to print player related info. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created diff --git a/crawl-ref/source/overmap.cc b/crawl-ref/source/overmap.cc index e271a25fdf..2e4a236156 100644 --- a/crawl-ref/source/overmap.cc +++ b/crawl-ref/source/overmap.cc @@ -6,6 +6,8 @@ * Summary: Records location of stairs etc * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <3> 30/7/00 MV Made Over-map full-screen @@ -26,6 +28,7 @@ // for #definitions of MAX_BRANCHES & MAX_LEVELS #include "files.h" // for #definitions of MAX_BRANCHES & MAX_LEVELS +#include "misc.h" #include "religion.h" #include "stuff.h" #include "view.h" @@ -64,6 +67,22 @@ void print_one_highlighted_line( const char *pre, const char *text, static void print_level_name( int branch, int depth, bool &printed_branch, bool &printed_level ); +void seen_notable_thing( int which_thing ) +{ + // Don't record in temporary terrain + if (you.level_type != LEVEL_DUNGEON) + return; + + const god_type god = grid_altar_god(which_thing); + + if (god != GOD_NO_GOD) + seen_altar( god ); + else if (grid_is_branch_stairs( which_thing )) + seen_staircase( which_thing ); + else + seen_other_thing( which_thing ); +} + void init_overmap( void ) { for (int i = 0; i < MAX_LEVELS; i++) @@ -196,7 +215,7 @@ void display_overmap( void ) { pr_lev = false; // strcpy(info, " - a staircase leading to "); - info[0] = '\0'; + info[0] = 0; if (stair_level[k] == j) { @@ -359,7 +378,7 @@ static void print_level_name( int branch, int depth, } // we need our own buffer in here (info is used): - char buff[ INFO_SIZE ] = "\0";; + char buff[INFO_SIZE]; if (branch == BRANCH_MAIN_DUNGEON) depth += 1; @@ -424,10 +443,24 @@ void seen_staircase( unsigned char which_staircase ) case DNGN_ENTER_SWAMP: which_branch = BRANCH_SWAMP; break; + case DNGN_ENTER_DIS: + which_branch = BRANCH_DIS; + break; + case DNGN_ENTER_GEHENNA: + which_branch = BRANCH_GEHENNA; + break; + case DNGN_ENTER_COCYTUS: + which_branch = BRANCH_COCYTUS; + break; + case DNGN_ENTER_TARTARUS: + which_branch = BRANCH_TARTARUS; + break; default: - exit(-1); // shouldn't happen + break; } + ASSERT(which_branch != BRANCH_MAIN_DUNGEON); + stair_level[which_branch] = you.your_level; } // end seen_staircase() @@ -486,7 +519,7 @@ void seen_other_thing( unsigned char which_thing ) * prints "More..." message, read key, clear screen and after that prints new * line */ -void print_one_simple_line( const char *line , int colour) +void print_one_simple_line( const char *line, int colour) { if (map_lines == (get_number_of_lines() - 2)) { @@ -499,9 +532,7 @@ void print_one_simple_line( const char *line , int colour) } textcolor( colour ); - cprintf( line ); - cprintf( EOL ); - + cprintf( "%s" EOL, line ); map_lines++; } @@ -518,19 +549,19 @@ void print_one_highlighted_line( const char *pre, const char *text, map_lines = 0; } - if (pre[0] != '\0') + if (pre[0] != 0) { textcolor( LIGHTGREY ); - cprintf( pre ); + cprintf( "%s", pre ); } textcolor( colour ); - cprintf( text ); + cprintf( "%s", text ); - if (post[0] != '\0') + if (post[0] != 0) { textcolor( LIGHTGREY ); - cprintf( post ); + cprintf( "%s", post ); } cprintf( EOL ); diff --git a/crawl-ref/source/overmap.h b/crawl-ref/source/overmap.h index 9d02b320e6..4ded4e4420 100644 --- a/crawl-ref/source/overmap.h +++ b/crawl-ref/source/overmap.h @@ -13,6 +13,8 @@ #define OVERMAP_H +void seen_notable_thing( int which_thing ); + // last updated 12may2000 {dlb} /* *********************************************************************** * called from: view diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index c4d5d6c047..e44996d6f8 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -3,6 +3,8 @@ * Summary: Player related functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <6> 7/30/99 BWR Added player_spell_levels() @@ -31,23 +33,28 @@ #include "externs.h" #include "clua.h" +#include "delay.h" +#include "fight.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "macro.h" #include "misc.h" #include "mon-util.h" #include "mutation.h" +#include "notes.h" +#include "ouch.h" #include "output.h" #include "randart.h" #include "religion.h" +#include "skills.h" #include "skills2.h" #include "spl-util.h" #include "spells4.h" #include "stuff.h" +#include "transfor.h" #include "travel.h" #include "view.h" -#include "wpn-misc.h" - /* you.duration []: //jmf: obsolete, see enum.h instead @@ -105,7 +112,194 @@ int species_exp_mod(char species); void ability_increase(void); -//void priest_spells(int priest_pass[10], char religious); // see actual function for reasoning here {dlb} +// Use this function whenever the player enters (or lands and thus re-enters) +// a grid. +// +// stepped - normal walking moves +// allow_shift - allowed to scramble in any direction out of lava/water +// force - ignore safety checks, move must happen (traps, lava/water). +bool move_player_to_grid( int x, int y, bool stepped, bool allow_shift, + bool force ) +{ + ASSERT( in_bounds( x, y ) ); + + int id; + // assuming that entering the same square means coming from above (levitate) + const bool from_above = (you.x_pos == x && you.y_pos == y); + const int old_grid = (from_above) ? static_cast(DNGN_FLOOR) + : grd[you.x_pos][you.y_pos]; + const int new_grid = grd[x][y]; + + // really must be clear + ASSERT( !grid_is_solid( new_grid ) ); + + // if (grid_is_solid( new_grid )) + // return (false); + + // better not be an unsubmerged monster either: + ASSERT( mgrd[x][y] == NON_MONSTER + || mons_is_submerged( &menv[ mgrd[x][y] ] )); + + // if (mgrd[x][y] != NON_MONSTER && !mons_is_submerged( &menv[ mgrd[x][y] ] )) + // return (false); + + // if we're walking along, give a chance to avoid trap + if (stepped + && !force + && new_grid == DNGN_UNDISCOVERED_TRAP) + { + const int skill = 4 + you.skills[SK_TRAPS_DOORS] + + you.mutation[MUT_ACUTE_VISION] + - 2 * you.mutation[MUT_BLURRY_VISION]; + + if (random2( skill ) > 6) + { + mprf( MSGCH_WARN, + "Wait a moment, %s! Do you really want to step there?", + you.your_name ); + + more(); + + exercise( SK_TRAPS_DOORS, 3 ); + + you.turn_is_over = false; + + id = trap_at_xy( x, y ); + if (id != -1) + grd[x][y] = trap_category( env.trap[id].type ); + + return (false); + } + } + + // only consider terrain if player is not levitating + if (!player_is_levitating()) + { + // XXX: at some point we're going to need to fix the swimming + // code to handle burden states. + if (new_grid == DNGN_LAVA + || (new_grid == DNGN_DEEP_WATER && you.species != SP_MERFOLK)) + { + // lava and dangerous deep water (ie not merfolk) + int entry_x = (stepped) ? you.x_pos : x; + int entry_y = (stepped) ? you.y_pos : y; + + if (stepped && !force && !you.conf) + { + bool okay = yesno( "Do you really want to step there?", + false, 'n' ); + + if (!okay) + { + canned_msg(MSG_OK); + return (false); + } + } + + // have to move now so fall_into_a_pool will work + you.x_pos = x; + you.y_pos = y; + + viewwindow( true, false ); + + // if true, we were shifted and so we're done. + if (fall_into_a_pool( entry_x, entry_y, allow_shift, new_grid )) + return (true); + } + else if (new_grid == DNGN_SHALLOW_WATER || new_grid == DNGN_DEEP_WATER) + { + // safer water effects + if (you.species == SP_MERFOLK) + { + if (old_grid != DNGN_SHALLOW_WATER + && old_grid != DNGN_DEEP_WATER) + { + if (stepped) + mpr("Your legs become a tail as you enter the water."); + else + mpr("Your legs become a tail as you dive into the water."); + + merfolk_start_swimming(); + } + } + else + { + ASSERT( new_grid != DNGN_DEEP_WATER ); + + if (!stepped) + noisy(SL_SPLASH, you.x_pos, you.y_pos, "Splash!"); + + you.time_taken *= 13 + random2(8); + you.time_taken /= 10; + + if (old_grid != DNGN_SHALLOW_WATER) + { + mprf( "You %s the shallow water.", + (stepped ? "enter" : "fall into") ); + + mpr("Moving in this stuff is going to be slow."); + + if (you.invis) + mpr( "... and don't expect to remain undetected." ); + } + } + } + } + + // move the player to location + you.x_pos = x; + you.y_pos = y; + + viewwindow( true, false ); + + // Other Effects: + // clouds -- do we need this? (always seems to double up with acr.cc call) + // if (is_cloud( you.x_pos, you.y_pos )) + // in_a_cloud(); + + // icy shield goes down over lava + if (new_grid == DNGN_LAVA) + expose_player_to_element( BEAM_LAVA ); + + // traps go off: + if (new_grid >= DNGN_TRAP_MECHANICAL && new_grid <= DNGN_UNDISCOVERED_TRAP) + { + id = trap_at_xy( you.x_pos, you.y_pos ); + + if (id != -1) + { + bool trap_known = true; + + if (new_grid == DNGN_UNDISCOVERED_TRAP) + { + trap_known = false; + + const int type = trap_category( env.trap[id].type ); + + grd[you.x_pos][you.y_pos] = type; + set_envmap_char(you.x_pos, you.y_pos, get_sightmap_char(type)); + } + + // not easy to blink onto a trap without setting it off: + if (!stepped) + trap_known = false; + + if (!player_is_levitating() + || trap_category( env.trap[id].type ) != DNGN_TRAP_MECHANICAL) + { + handle_traps(env.trap[id].type, id, trap_known); + } + } + } + + return (true); +} + +bool player_in_mappable_area( void ) +{ + return (you.level_type != LEVEL_LABYRINTH && you.level_type != LEVEL_ABYSS); +} + bool player_in_branch( int branch ) { return (you.level_type == LEVEL_DUNGEON && you.where_are_you == branch); @@ -157,7 +351,7 @@ bool player_genus(unsigned char which_genus, unsigned char species) case SP_PALE_DRACONIAN: case SP_UNK0_DRACONIAN: case SP_UNK1_DRACONIAN: - case SP_UNK2_DRACONIAN: + case SP_BASE_DRACONIAN: return (which_genus == GENPC_DRACONIAN); case SP_ELF: @@ -178,6 +372,58 @@ bool player_genus(unsigned char which_genus, unsigned char species) return (false); } // end player_genus() +// Returns the item in the given equipment slot, NULL if the slot is empty. +// eq must be in [EQ_WEAPON, EQ_AMULET], or bad things will happen. +item_def *player_slot_item(equipment_type eq) +{ + ASSERT(eq >= EQ_WEAPON && eq <= EQ_AMULET); + + const int item = you.equip[eq]; + return (item == -1? NULL : &you.inv[item]); +} + +// Returns the item in the player's weapon slot. +item_def *player_weapon() +{ + return player_slot_item(EQ_WEAPON); +} + +bool player_weapon_wielded() +{ + const int wpn = you.equip[EQ_WEAPON]; + + if (wpn == -1) + return (false); + + if (you.inv[wpn].base_type != OBJ_WEAPONS + && you.inv[wpn].base_type != OBJ_STAVES) + { + return (false); + } + + // FIXME: This needs to go in eventually. + /* + // should never have a bad "shape" weapon in hand + ASSERT( check_weapon_shape( you.inv[wpn], false ) ); + + if (!check_weapon_wieldable_size( you.inv[wpn], player_size() )) + return (false); + */ + + return (true); +} + +// Returns the you.inv[] index of our wielded weapon or -1 (no item, not wield) +int get_player_wielded_item() +{ + return (you.equip[EQ_WEAPON]); +} + +int get_player_wielded_weapon() +{ + return (player_weapon_wielded()? get_player_wielded_item() : -1); +} + // Looks in equipment "slot" to see if there is an equiped "sub_type". // Returns number of matches (in the case of rings, both are checked) int player_equip( int slot, int sub_type, bool calc_unid ) @@ -201,7 +447,7 @@ int player_equip( int slot, int sub_type, bool calc_unid ) if (you.equip[EQ_WEAPON] != -1 && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_STAVES && you.inv[you.equip[EQ_WEAPON]].sub_type == sub_type - && (calc_unid || + && (calc_unid || item_ident(you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_TYPE))) { ret++; @@ -275,6 +521,8 @@ int player_equip( int slot, int sub_type, bool calc_unid ) // Looks in equipment "slot" to see if equiped item has "special" ego-type // Returns number of matches (jewellery returns zero -- no ego type). +// [ds] There's no equivalent of calc_unid or req_id because as of now, weapons +// and armour type-id on wield/wear. int player_equip_ego_type( int slot, int special ) { int ret = 0; @@ -337,13 +585,14 @@ int player_damage_type( void ) if (wpn != -1) { - return (damage_type( you.inv[wpn].base_type, you.inv[wpn].sub_type )); + return (get_vorpal_type(you.inv[wpn])); } - else if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS + else if (you.equip[EQ_GLOVES] == -1 && + (you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS || you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON || you.mutation[MUT_CLAWS] || you.species == SP_TROLL - || you.species == SP_GHOUL) + || you.species == SP_GHOUL)) { return (DVORP_SLICING); } @@ -609,6 +858,20 @@ int player_res_magic(void) return rm; } +int player_res_steam(bool calc_unid) +{ + int res = 0; + + if (you.species == SP_PALE_DRACONIAN && you.experience_level > 5) + res += 2; + + if (player_equip(EQ_BODY_ARMOUR, ARM_STEAM_DRAGON_ARMOUR)) + res += 2; + + return (res + player_res_fire(calc_unid) / 2); +} + + int player_res_fire(bool calc_unid) { int rf = 0; @@ -735,6 +998,46 @@ int player_res_cold(bool calc_unid) return (rc); } +int player_res_acid(bool consider_unidentified_gear) +{ + int res = 0; + if (!transform_changed_physiology()) + { + if (you.species == SP_GOLDEN_DRACONIAN + && you.experience_level >= 7) + res += 2; + + res += you.mutation[MUT_YELLOW_SCALES] * 2 / 3; + } + + if (wearing_amulet(AMU_RESIST_CORROSION, consider_unidentified_gear)) + res++; + + return (res); +} + +// Returns a factor X such that post-resistance acid damage can be calculated +// as pre_resist_damage * X / 100. +int player_acid_resist_factor() +{ + int res = player_res_acid(); + + int factor = 100; + + if (res == 1) + factor = 50; + else if (res == 2) + factor = 34; + else if (res > 2) + { + factor = 30; + while (res-- > 2 && factor >= 20) + factor = factor * 90 / 100; + } + + return (factor); +} + int player_res_electricity(bool calc_unid) { int re = 0; @@ -774,6 +1077,35 @@ int player_res_electricity(bool calc_unid) return (re); } // end player_res_electricity() +// Does the player resist asphyxiation? +bool player_res_asphyx() +{ + // The undead are immune to asphyxiation, or so we'll assume. + if (you.is_undead) + return (true); + + switch (you.attribute[ATTR_TRANSFORMATION]) + { + case TRAN_LICH: + case TRAN_STATUE: + case TRAN_SERPENT_OF_HELL: + case TRAN_AIR: + return (true); + } + return (false); +} + +bool player_control_teleport(bool calc_unid) { + return ( you.duration[DUR_CONTROL_TELEPORT] || + player_equip(EQ_RINGS, RING_TELEPORT_CONTROL, calc_unid) || + you.mutation[MUT_TELEPORT_CONTROL] ); +} + +int player_res_torment(bool) +{ + return (you.is_undead || you.mutation[MUT_TORMENT_RESISTANCE]); +} + // funny that no races are susceptible to poisons {dlb} int player_res_poison(bool calc_unid) { @@ -999,6 +1331,9 @@ int player_prot_life(bool calc_unid) { int pl = 0; + if (wearing_amulet(AMU_WARDING, calc_unid)) + ++pl; + // rings pl += player_equip( EQ_RINGS, RING_LIFE_PROTECTION, calc_unid ); @@ -1167,20 +1502,13 @@ int player_AC(void) // which uses "plus2"... since not all members have the same // AC value, we use special cases. -- bwr if (i == EQ_HELMET - && (cmp_helmet_type( you.inv[ item ], THELM_CAP ) - || cmp_helmet_type( you.inv[ item ], THELM_WIZARD_HAT ) - || cmp_helmet_type( you.inv[ item ], THELM_SPECIAL ))) + && (get_helmet_type(you.inv[ item ]) == THELM_CAP + || get_helmet_type(you.inv[ item ]) == THELM_WIZARD_HAT + || get_helmet_type(you.inv[ item ]) == THELM_SPECIAL)) { continue; } - if (i == EQ_BOOTS - && (you.inv[ item ].plus2 == TBOOT_NAGA_BARDING - || you.inv[ item ].plus2 == TBOOT_CENTAUR_BARDING)) - { - AC += 3; - } - int racial_bonus = 0; // additional levels of armour skill const unsigned long armour_race = get_equip_race( you.inv[ item ] ); const int ac_value = property( you.inv[ item ], PARM_AC ); @@ -1386,7 +1714,7 @@ int player_AC(void) bool is_light_armour( const item_def &item ) { - if (cmp_equip_race( item, ISFLAG_ELVEN )) + if (get_equip_race(item) == ISFLAG_ELVEN) return (true); switch (item.sub_type) @@ -1434,6 +1762,56 @@ bool player_is_shapechanged(void) return (true); } +// psize defaults to PSIZE_TORSO, which checks the part of the body +// that wears armour and wields weapons (which is different for some hybrids). +// base defaults to "false", meaning consider our current size, not our +// natural one. +size_type player_size( int psize, bool base ) +{ + size_type ret = (base) ? SIZE_CHARACTER : transform_size( psize ); + + if (ret == SIZE_CHARACTER) + { + // transformation has size of character's species: + switch (you.species) + { + case SP_OGRE: + case SP_OGRE_MAGE: + case SP_TROLL: + ret = SIZE_LARGE; + break; + + case SP_NAGA: + // Most of their body is on the ground giving them a low profile. + if (psize == PSIZE_TORSO || psize == PSIZE_PROFILE) + ret = SIZE_MEDIUM; + else + ret = SIZE_BIG; + break; + + case SP_CENTAUR: + ret = (psize == PSIZE_TORSO) ? SIZE_MEDIUM : SIZE_BIG; + break; + + case SP_SPRIGGAN: + ret = SIZE_LITTLE; + break; + + case SP_HALFLING: + case SP_GNOME: + case SP_KOBOLD: + ret = SIZE_SMALL; + break; + + default: + ret = SIZE_MEDIUM; + break; + } + } + + return (ret); +} + int player_evasion(void) { int ev = 10; @@ -1550,6 +1928,12 @@ int player_mag_abil(bool is_weighted) return ((is_weighted) ? ((ma * you.intel) / 10) : ma); } // end player_mag_abil() +// Returns the shield the player is wearing, or NULL if none. +item_def *player_shield() +{ + return player_slot_item(EQ_SHIELD); +} + int player_shield_class(void) //jmf: changes for new spell { int base_shield = 0; @@ -1619,7 +2003,7 @@ unsigned char player_see_invis(bool calc_unid) // This does NOT do line of sight! It checks the monster's visibility // with repect to the players perception, but doesn't do walls or range... // to find if the square the monster is in is visible see mons_near(). -bool player_monster_visible( struct monsters *mon ) +bool player_monster_visible( const monsters *mon ) { if (mons_has_ench( mon, ENCH_SUBMERGED ) || (mons_has_ench( mon, ENCH_INVIS ) && !player_see_invis())) @@ -1670,7 +2054,7 @@ int burden_change(void) } else { - you.burden += mass_item( you.inv[bu] ) * you.inv[bu].quantity; + you.burden += item_mass( you.inv[bu] ) * you.inv[bu].quantity; } } @@ -2072,7 +2456,7 @@ void level_change(void) case SP_PALE_DRACONIAN: case SP_UNK0_DRACONIAN: case SP_UNK1_DRACONIAN: - case SP_UNK2_DRACONIAN: + case SP_BASE_DRACONIAN: if (you.experience_level == 7) { switch (you.species) @@ -2108,7 +2492,7 @@ void level_change(void) break; case SP_UNK0_DRACONIAN: case SP_UNK1_DRACONIAN: - case SP_UNK2_DRACONIAN: + case SP_BASE_DRACONIAN: mpr(""); break; } @@ -2346,6 +2730,11 @@ void level_change(void) if (you.experience_level > you.max_level) you.max_level = you.experience_level; + + char buf[200]; + sprintf(buf, "HP: %d/%d MP: %d/%d", + you.hp, you.hp_max, you.magic_points, you.max_magic_points); + take_note(Note(NOTE_XP_LEVEL_CHANGE, you.experience_level, 0, buf)); if (you.religion == GOD_XOM) Xom_acts(true, you.experience_level, true); @@ -2359,7 +2748,7 @@ void level_change(void) // - 12mar2000 {dlb} int check_stealth(void) { - if (you.special_wield == SPWLD_SHADOW) + if (you.special_wield == SPWLD_SHADOW || you.berserker) return (0); int stealth = you.dex * 3; @@ -2408,9 +2797,9 @@ int check_stealth(void) const int boots = you.equip[EQ_BOOTS]; if (arm != -1 && !player_light_armour()) - stealth -= (mass_item( you.inv[arm] ) / 10); + stealth -= (item_mass( you.inv[arm] ) / 10); - if (cloak != -1 && cmp_equip_race( you.inv[cloak], ISFLAG_ELVEN )) + if (cloak != -1 && get_equip_race(you.inv[cloak]) == ISFLAG_ELVEN) stealth += 20; if (boots != -1) @@ -2418,7 +2807,7 @@ int check_stealth(void) if (get_armour_ego_type( you.inv[boots] ) == SPARM_STEALTH) stealth += 50; - if (cmp_equip_race( you.inv[boots], ISFLAG_ELVEN )) + if (get_equip_race(you.inv[boots]) == ISFLAG_ELVEN) stealth += 20; } @@ -2435,7 +2824,7 @@ int check_stealth(void) stealth /= 2; // splashy-splashy } - // Radiating silence is the negative compliment of shouting all the + // Radiating silence is the negative complement of shouting all the // time... a sudden change from background noise to no noise is going // to clue anything in to the fact that something is very wrong... // a personal silence spell would naturally be different, but this @@ -2580,9 +2969,6 @@ void display_char_status(void) if (you.duration[DUR_SILENCE]) //jmf: added 27mar2000 mpr( "You radiate silence." ); - if (you.duration[DUR_INFECTED_SHUGGOTH_SEED]) //jmf: added 19mar2000 - mpr( "You are infected with a shuggoth parasite." ); - if (you.duration[DUR_STONESKIN]) mpr( "Your skin is tough as stone." ); @@ -2604,9 +2990,9 @@ void display_char_status(void) if (you.slow && you.haste) mpr( "You are under both slowing and hasting effects." ); else if (you.slow) - mpr( "You are moving very slowly." ); + mpr( "You actions are slowed." ); else if (you.haste) - mpr( "You are moving very quickly." ); + mpr( "You actions are hasted." ); if (you.might) mpr( "You are mighty." ); @@ -2668,6 +3054,92 @@ void display_char_status(void) : "weak " ); mpr(info); } + + int move_cost = (player_speed() * player_movement_speed()) / 10; + if ( you.slow ) + move_cost *= 2; + + const bool water = player_in_water(); + const bool swim = player_is_swimming(); + + const bool lev = player_is_levitating(); + const bool fly = (lev && you.duration[DUR_CONTROLLED_FLIGHT]); + const bool swift = (you.duration[DUR_SWIFTNESS] > 0); + snprintf( info, INFO_SIZE, + "Your %s speed is %s%s%s.", + // order is important for these: + (swim) ? "swimming" : + (water) ? "wading" : + (fly) ? "flying" : + (lev) ? "levitating" + : "movement", + + (water && !swim) ? "uncertain and " : + (!water && swift) ? "aided by the wind" : "", + + (!water && swift) ? ((move_cost >= 10) ? ", but still " + : " and ") + : "", + + (move_cost < 8) ? "very quick" : + (move_cost < 10) ? "quick" : + (move_cost == 10) ? "average" : + (move_cost < 13) ? "slow" + : "very slow" ); + mpr(info); + + // XXX Assumes no hand-and-a-half bonus. Oh well. + const int to_hit = calc_your_to_hit( calc_heavy_armour_penalty(false), + false, false, false ) * 2; + // Messages based largely on percentage chance of missing the + // average EV 10 humanoid, and very agile EV 30 (pretty much + // max EV for monsters currently). + // + // "awkward" - need lucky hit (less than EV) + // "difficult" - worse than 2 in 3 + // "hard" - worse than fair chance + snprintf( info, INFO_SIZE, + "%s in your current equipment.", + (to_hit < 1) ? "You are completely incapable of fighting" : + (to_hit < 5) ? "Hitting even clumsy monsters is extremely awkward" : + (to_hit < 10) ? "Hitting average monsters is awkward" : + (to_hit < 15) ? "Hitting average monsters is difficult" : + (to_hit < 20) ? "Hitting average monsters is hard" : + (to_hit < 30) ? "Very agile monsters are a bit awkward to hit" : + (to_hit < 45) ? "Very agile monsters are a bit difficult to hit" : + (to_hit < 60) ? "Very agile monsters are a bit hard to hit" : + (to_hit < 100) ? "You feel comfortable with your ability to fight" + : "You feel confident with your ability to fight" ); + +#if DEBUG_DIAGNOSTICS + char str_pass[INFO_SIZE]; + snprintf( str_pass, INFO_SIZE, " (%d)", to_hit ); + strncat( info, str_pass, INFO_SIZE ); +#endif + mpr(info); + + + // character evaluates their ability to sneak around: + const int ustealth = check_stealth(); + + // XXX: made these values up, probably could be better. + snprintf( info, INFO_SIZE, "You feel %sstealthy.", + (ustealth < 10) ? "extremely un" : + (ustealth < 20) ? "very un" : + (ustealth < 30) ? "un" : + (ustealth < 50) ? "fairly " : + (ustealth < 80) ? "" : + (ustealth < 120) ? "quite " : + (ustealth < 160) ? "very " : + (ustealth < 200) ? "extremely " + : "incredibly " ); + +#if DEBUG_DIAGNOSTICS + snprintf( str_pass, INFO_SIZE, " (%d)", ustealth ); + strncat( info, str_pass, INFO_SIZE ); +#endif + + mpr( info ); } // end display_char_status() void redraw_skill(const char your_name[kNameLen], const char class_name[80]) @@ -2686,11 +3158,11 @@ void redraw_skill(const char your_name[kNameLen], const char class_name[80]) char name_buff[kNameLen]; strncpy( name_buff, your_name, kNameLen ); - // squeeze name if required, the "- 8" is too not squueze too much + // squeeze name if required, the "- 8" is too not squeeze too much if (in_len > 40 && (name_len - 8) > (in_len - 40)) - name_buff[ name_len - (in_len - 40) - 1 ] = '\0'; + name_buff[ name_len - (in_len - 40) - 1 ] = 0; else - name_buff[ kNameLen - 1 ] = '\0'; + name_buff[ kNameLen - 1 ] = 0; snprintf( print_it, sizeof(print_it), "%s, %s", name_buff, class_name ); } @@ -2698,7 +3170,7 @@ void redraw_skill(const char your_name[kNameLen], const char class_name[80]) for (int i = strlen(print_it); i < 41; i++) print_it[i] = ' '; - print_it[40] = '\0'; + print_it[40] = 0; #ifdef DOS_TERM window(1, 1, 80, 25); @@ -2706,7 +3178,7 @@ void redraw_skill(const char your_name[kNameLen], const char class_name[80]) gotoxy(40, 1); textcolor( LIGHTGREY ); - cprintf( print_it ); + cprintf( "%s", print_it ); } // end redraw_skill() // Note that this function only has the one static buffer, so if you @@ -2758,7 +3230,7 @@ char *species_name( int speci, int level, bool genus, bool adj, bool cap ) break; case SP_UNK0_DRACONIAN: case SP_UNK1_DRACONIAN: - case SP_UNK2_DRACONIAN: + case SP_BASE_DRACONIAN: default: strcpy( species_buff, "Draconian" ); break; @@ -2891,6 +3363,26 @@ char *species_name( int speci, int level, bool genus, bool adj, bool cap ) return (species_buff); } // end species_name() +bool player_res_corrosion(bool calc_unid) +{ + return (player_equip(EQ_AMULET, AMU_RESIST_CORROSION, calc_unid) + || player_equip_ego_type(EQ_CLOAK, SPARM_PRESERVATION)); +} + +bool player_item_conserve(bool calc_unid) +{ + return (player_equip(EQ_AMULET, AMU_CONSERVATION, calc_unid) + || player_equip_ego_type(EQ_CLOAK, SPARM_PRESERVATION)); +} + +int player_mental_clarity(bool calc_unid) +{ + const int ret = 3 * player_equip(EQ_AMULET, AMU_CLARITY, calc_unid) + + you.mutation[ MUT_CLARITY ]; + + return ((ret > 3) ? 3 : ret); +} + bool wearing_amulet(char amulet, bool calc_unid) { if (amulet == AMU_CONTROLLED_FLIGHT @@ -3154,7 +3646,7 @@ void modify_stat(unsigned char which_stat, char amount, bool suppress_msg) if (amount == 0) return; - // Stop running/travel if a stat drops. + // Stop delays if a stat drops. if (amount < 0) interrupt_activity( AI_STAT_CHANGE ); @@ -3204,15 +3696,24 @@ void modify_stat(unsigned char which_stat, char amount, bool suppress_msg) return; } // end modify_stat() -void dec_hp(int hp_loss, bool fatal) +void dec_hp(int hp_loss, bool fatal, const char *aux) { + if (!fatal && you.hp < 1) + you.hp = 1; + + if (!fatal && hp_loss >= you.hp) + hp_loss = you.hp - 1; + if (hp_loss < 1) return; - you.hp -= hp_loss; - - if (!fatal && you.hp < 1) - you.hp = 1; + // If it's not fatal, use ouch() so that notes can be taken. If it IS + // fatal, somebody else is doing the bookkeeping, and we don't want to mess + // with that. + if (!fatal && aux) + ouch(hp_loss, -1, KILLED_BY_SOMETHING, aux); + else + you.hp -= hp_loss; you.redraw_hit_points = 1; @@ -3229,6 +3730,7 @@ void dec_mp(int mp_loss) if (you.magic_points < 0) you.magic_points = 0; + take_note(Note(NOTE_MP_CHANGE, you.magic_points, you.max_magic_points)); you.redraw_magic_points = 1; return; @@ -3276,6 +3778,7 @@ void inc_mp(int mp_gain, bool max_too) if (you.magic_points > you.max_magic_points) you.magic_points = you.max_magic_points; + take_note(Note(NOTE_MP_CHANGE, you.magic_points, you.max_magic_points)); you.redraw_magic_points = 1; return; @@ -3296,6 +3799,9 @@ void inc_hp(int hp_gain, bool max_too) if (you.hp > you.hp_max) you.hp = you.hp_max; + // to avoid message spam, no information when HP increases + // take_note(Note(NOTE_HP_CHANGE, you.hp, you.hp_max)); + you.redraw_hit_points = 1; } // end inc_hp() @@ -3337,6 +3843,7 @@ void inc_max_hp( int hp_gain ) you.base_hp2 += hp_gain; calc_hp(); + take_note(Note(NOTE_MAXHP_CHANGE, you.hp_max)); you.redraw_hit_points = 1; } @@ -3345,6 +3852,7 @@ void dec_max_hp( int hp_loss ) you.base_hp2 -= hp_loss; calc_hp(); + take_note(Note(NOTE_MAXHP_CHANGE, you.hp_max)); you.redraw_hit_points = 1; } @@ -3353,6 +3861,7 @@ void inc_max_mp( int mp_gain ) you.base_magic_points2 += mp_gain; calc_mp(); + take_note(Note(NOTE_MAXMP_CHANGE, you.max_magic_points)); you.redraw_magic_points = 1; } @@ -3361,6 +3870,7 @@ void dec_max_mp( int mp_loss ) you.base_magic_points2 -= mp_loss; calc_mp(); + take_note(Note(NOTE_MAXMP_CHANGE, you.max_magic_points)); you.redraw_magic_points = 1; } @@ -3372,6 +3882,7 @@ void deflate_hp(int new_level, bool floor) else if (!floor && you.hp > new_level) you.hp = new_level; + // take_note(Note(NOTE_HP_CHANGE, you.hp, you.hp_max)); // must remain outside conditional, given code usage {dlb} you.redraw_hit_points = 1; @@ -3394,6 +3905,9 @@ void set_hp(int new_amount, bool max_too) if (you.hp > you.hp_max) you.hp = you.hp_max; + // take_note(Note(NOTE_HP_CHANGE, you.hp, you.hp_max)); + if ( max_too ) + take_note(Note(NOTE_MAXHP_CHANGE, you.hp_max)); // must remain outside conditional, given code usage {dlb} you.redraw_hit_points = 1; @@ -3416,7 +3930,10 @@ void set_mp(int new_amount, bool max_too) if (you.magic_points > you.max_magic_points) you.magic_points = you.max_magic_points; - + + take_note(Note(NOTE_MP_CHANGE, you.magic_points, you.max_magic_points)); + if ( max_too ) + take_note(Note(NOTE_MAXMP_CHANGE, you.max_magic_points)); // must remain outside conditional, given code usage {dlb} you.redraw_magic_points = 1; @@ -3557,65 +4074,6 @@ const char *get_class_name( int which_job ) return (Class_Name_List[ which_job ]); } -/* ****************************************************************** - -// this function is solely called by a commented out portion of -// player::level_change() and is a bit outta whack with the -// current codebase - probably should be struck as well 19may2000 {dlb} - -void priest_spells( int priest_pass[10], char religious ) -{ - - switch ( religious ) - { - case GOD_ZIN: - priest_pass[1] = SPELL_LESSER_HEALING; - priest_pass[2] = SPELL_REPEL_UNDEAD; - priest_pass[3] = SPELL_HEAL_OTHER; - priest_pass[4] = SPELL_PURIFICATION; - priest_pass[5] = SPELL_GREATER_HEALING; - priest_pass[6] = SPELL_SMITING; - priest_pass[7] = SPELL_HOLY_WORD; - priest_pass[8] = SPELL_REMOVE_CURSE; - priest_pass[9] = SPELL_GUARDIAN; - break; - - case GOD_SHINING_ONE: - priest_pass[1] = SPELL_REPEL_UNDEAD; - priest_pass[2] = SPELL_LESSER_HEALING; - priest_pass[3] = SPELL_HEAL_OTHER; - priest_pass[4] = SPELL_PURIFICATION; - priest_pass[5] = SPELL_ABJURATION_II; - priest_pass[6] = SPELL_THUNDERBOLT; - priest_pass[7] = SPELL_SHINING_LIGHT; - priest_pass[8] = SPELL_SUMMON_DAEVA; - priest_pass[9] = SPELL_FLAME_OF_CLEANSING; - break; - - case GOD_ELYVILON: - priest_pass[1] = SPELL_LESSER_HEALING; - priest_pass[2] = SPELL_HEAL_OTHER; - priest_pass[3] = SPELL_PURIFICATION; - priest_pass[4] = 93; // restore abilities - priest_pass[5] = SPELL_GREATER_HEALING; - priest_pass[6] = 94; // another healing spell - priest_pass[7] = 95; // something else - priest_pass[8] = -1; // - priest_pass[9] = -1; // - break; - } - -} - -// Spells to be added: (+ renamed!) -// holy berserker -// 87 Pestilence -// 93 Restore Abilities -// 94 something else healing -// 95 something else - -****************************************************************** */ - void contaminate_player(int change, bool statusOnly) { // get current contamination level @@ -3839,7 +4297,7 @@ void haste_player( int amount ) if (you.haste > 80 + 20 * amu_eff) you.haste = 80 + 20 * amu_eff; - naughty( NAUGHTY_STIMULANTS, 4 + random2(4) ); + did_god_conduct( DID_STIMULANTS, 4 + random2(4) ); } void dec_haste_player( void ) @@ -3911,161 +4369,198 @@ void rot_player( int amount ) } } -void run_macro(const char *macroname) +////////////////////////////////////////////////////////////////////////////// +// player + +player::player() { - if (you.activity != ACT_NONE && you.activity != ACT_MACRO) - return; + init(); +} -#ifdef CLUA_BINDINGS - if (!clua) - { - mpr("Lua not initialized", MSGCH_DIAGNOSTICS); - you.activity = ACT_NONE; - return; - } +// player struct initialization +void player::init() +{ + birth_time = time( NULL ); + real_time = 0; + num_turns = 0; - if (!clua.callbooleanfn(false, "c_macro", "s", macroname)) - { - if (clua.error.length()) - mpr(clua.error.c_str()); - you.activity = ACT_NONE; - } - else - { - you.activity = ACT_MACRO; - } +#ifdef WIZARD + wizard = (Options.wiz_mode == WIZ_YES) ? true : false; #else - you.activity = ACT_NONE; + wizard = false; #endif -} -void perform_activity() -{ - switch (you.activity) + your_name[0] = 0; + + berserk_penalty = 0; + berserker = 0; + conf = 0; + confusing_touch = 0; + deaths_door = 0; + disease = 0; + elapsed_time = 0; + exhausted = 0; + haste = 0; + invis = 0; + levitation = 0; + might = 0; + paralysis = 0; + poison = 0; + rotting = 0; + fire_shield = 0; + slow = 0; + special_wield = SPWLD_NONE; + sure_blade = 0; + synch_time = 0; + + base_hp = 5000; + base_hp2 = 5000; + base_magic_points = 5000; + base_magic_points2 = 5000; + + magic_points_regeneration = 0; + strength = 0; + max_strength = 0; + intel = 0; + max_intel = 0; + dex = 0; + max_dex = 0; + experience = 0; + experience_level = 1; + max_level = 1; + char_class = JOB_UNKNOWN; + + hunger = 6000; + hunger_state = HS_SATIATED; + + gold = 0; + // speed = 10; // 0.75; // unused + + burden = 0; + burden_state = BS_UNENCUMBERED; + + spell_no = 0; + + your_level = 0; + level_type = LEVEL_DUNGEON; + where_are_you = BRANCH_MAIN_DUNGEON; + char_direction = DIR_DESCENDING; + + prev_targ = MHITNOT; + pet_target = MHITNOT; + + x_pos = 0; + y_pos = 0; + + running.clear(); + travel_x = 0; + travel_y = 0; + + religion = GOD_NO_GOD; + piety = 0; + + gift_timeout = 0; + + for (int i = 0; i < MAX_NUM_GODS; i++) { - case ACT_MULTIDROP: - drop(); - break; - case ACT_MACRO: - run_macro(); - break; - default: - break; + penance[i] = 0; + worshipped[i] = 0; + num_gifts[i] = 0; } -} -#ifdef CLUA_BINDINGS -static const char *activity_interrupt_name(ACT_INTERRUPT ai) -{ - switch (ai) + ghost.name[0] = 0; + + for (int i = 0; i < NUM_GHOST_VALUES; i++) + ghost.values[i] = 0; + + for (int i = EQ_WEAPON; i < NUM_EQUIP; i++) + equip[i] = -1; + + for (int i = 0; i < 25; i++) + spells[i] = SPELL_NO_SPELL; + + for (int i = 0; i < 52; i++) { - case AI_FORCE_INTERRUPT: return "force"; - case AI_KEYPRESS: return "keypress"; - case AI_FULL_HP: return "full_hp"; - case AI_FULL_MP: return "full_mp"; - case AI_STATUE: return "statue"; - case AI_HUNGRY: return "hungry"; - case AI_MESSAGE: return "message"; - case AI_HP_LOSS: return "hp_loss"; - case AI_BURDEN_CHANGE: return "burden"; - case AI_STAT_CHANGE: return "stat"; - case AI_SEE_MONSTER: return "monster"; - case AI_TELEPORT: return "teleport"; - default: return "unknown"; + spell_letter_table[i] = -1; + ability_letter_table[i] = ABIL_NON_ABILITY; } -} -static const char *activity_names[] = { - "", - "multidrop", - "run", - "travel", - "macro" -}; + for (int i = 0; i < 100; i++) + mutation[i] = 0; -static const char *activity_name(int act) -{ - if (act < ACT_NONE || act >= ACT_ACTIVITY_COUNT) - return NULL; - return activity_names[act]; -} -#endif + for (int i = 0; i < 100; i++) + demon_pow[i] = 0; -static void kill_activity() -{ - if (you.running) - stop_running(); - you.activity = ACT_NONE; -} + for (int i = 0; i < 50; i++) + had_book[i] = 0; -static bool userdef_interrupt_activity( ACT_INTERRUPT ai, - const activity_interrupt_t &at ) -{ -#ifdef CLUA_BINDINGS - lua_State *ls = clua.state(); - if (!ls || ai == AI_FORCE_INTERRUPT) + for (int i = 0; i < 50; i++) + unique_items[i] = UNIQ_NOT_EXISTS; + + for (int i = 0; i < NO_UNRANDARTS; i++) + set_unrandart_exist(i, 0); + + for (int i = 0; i < 50; i++) { - if (ai == AI_FORCE_INTERRUPT || you.activity == ACT_MACRO) - kill_activity(); - return true; + skills[i] = 0; + skill_points[i] = 0; + skill_order[i] = MAX_SKILL_ORDER; + practise_skill[i] = 1; } - - const char *interrupt_name = activity_interrupt_name(ai); - const char *act_name = activity_name(you.activity); - bool ran = clua.callfn("c_interrupt_activity", "1:ssA", - act_name, interrupt_name, &at); - if (ran) - { - // If the function returned nil, we want to cease processing. - if (lua_isnil(ls, -1)) - { - lua_pop(ls, 1); - return false; - } + skill_cost_level = 1; + total_skill_points = 0; - bool stopact = lua_toboolean(ls, -1); - lua_pop(ls, 1); - if (stopact) - { - kill_activity(); - return true; - } - } + for (int i = 0; i < 30; i++) + attribute[i] = 0; - if (you.activity == ACT_MACRO && - clua.callbooleanfn(true, "c_interrupt_macro", - "sA", interrupt_name, &at)) + for (int i = 0; i < ENDOFPACK; i++) { - kill_activity(); + inv[i].quantity = 0; + inv[i].base_type = OBJ_WEAPONS; + inv[i].sub_type = WPN_CLUB; + inv[i].plus = 0; + inv[i].plus2 = 0; + inv[i].special = 0; + inv[i].colour = 0; + set_ident_flags( inv[i], ISFLAG_IDENT_MASK ); + + inv[i].x = -1; + inv[i].y = -1; + inv[i].link = i; } -#else - if (you.activity == ACT_MACRO) - kill_activity(); -#endif - return true; + for (int i = 0; i < NUM_DURATIONS; i++) + duration[i] = 0; + + exp_available = 25; } -void interrupt_activity( ACT_INTERRUPT ai, const activity_interrupt_t &at ) +bool player::is_valid() const { - if (you.running && !you.activity) - you.activity = you.running > 0? ACT_RUNNING : ACT_TRAVELING; + // Check if there's a name. + return (your_name[0] != 0); +} - if (!you.activity) - return; +std::string player::short_desc() const +{ + std::string desc; + desc += your_name; + desc += ", a level "; - if (!userdef_interrupt_activity(ai, at) || !you.activity) - { - if (you.activity == ACT_RUNNING || you.activity == ACT_TRAVELING) - you.activity = ACT_NONE; - return; - } - - if (!ai || (Options.activity_interrupts[ you.activity ] & ai)) { - kill_activity(); - } + char st_prn[20]; + itoa(experience_level, st_prn, 10); + desc += st_prn; - if (you.activity == ACT_RUNNING || you.activity == ACT_TRAVELING) - you.activity = ACT_NONE; + desc += " "; + desc += species_name(species, experience_level); + desc += " "; + desc += class_name; + + return (desc); +} + +bool player::operator < (const player &p) const +{ + return (strcmp(your_name, p.your_name) < 0); } diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h index 1d99d6b154..656a7b1faf 100644 --- a/crawl-ref/source/player.h +++ b/crawl-ref/source/player.h @@ -3,6 +3,8 @@ * Summary: Player related functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -14,9 +16,14 @@ #include "externs.h" +bool move_player_to_grid( int x, int y, bool stepped, bool allow_shift, + bool force ); + +bool player_in_mappable_area(void); bool player_in_branch( int branch ); bool player_in_hell( void ); +int get_player_wielded_weapon(); int player_equip( int slot, int sub_type, bool calc_unid = true ); int player_equip_ego_type( int slot, int sub_type ); int player_damage_type( void ); @@ -48,6 +55,7 @@ bool player_is_levitating(void); * *********************************************************************** */ bool player_under_penance(void); +int player_wielded_item(); /* *********************************************************************** * called from: ability - acr - fight - food - it_use2 - item_use - items - @@ -106,13 +114,6 @@ unsigned char player_energy(void); int player_evasion(void); -#if 0 -/* *********************************************************************** - * called from: acr - spells1 - * *********************************************************************** */ -unsigned char player_fast_run(void); -#endif - /* *********************************************************************** * called from: acr - spells1 * *********************************************************************** */ @@ -147,6 +148,14 @@ int player_regen(void); * called from: fight - files - it_use2 - misc - ouch - spells - spells2 * *********************************************************************** */ int player_res_cold(bool calc_unid = true); +int player_res_acid(bool consider_unidentified_gear = true); +int player_acid_resist_factor(); + +int player_res_torment(bool calc_unid = true); + +bool player_res_corrosion(bool calc_unid = true); +bool player_item_conserve(bool calc_unid = true); +int player_mental_clarity(bool calc_unid = true); /* *********************************************************************** @@ -159,6 +168,7 @@ int player_res_electricity(bool calc_unid = true); * called from: acr - fight - misc - ouch - spells * *********************************************************************** */ int player_res_fire(bool calc_unid = true); +int player_res_steam(bool calc_unid = true); /* *********************************************************************** @@ -167,8 +177,12 @@ int player_res_fire(bool calc_unid = true); * *********************************************************************** */ int player_res_poison(bool calc_unid = true); +bool player_control_teleport(bool calc_unid = true); + int player_res_magic(void); +bool player_res_asphyx(); + /* *********************************************************************** * called from: beam - chardump - fight - misc - output * *********************************************************************** */ @@ -278,7 +292,7 @@ int slaying_bonus(char which_affected); * spells3 * *********************************************************************** */ unsigned char player_see_invis(bool calc_unid = true); -bool player_monster_visible( struct monsters *mon ); +bool player_monster_visible( const monsters *mon ); /* *********************************************************************** @@ -339,7 +353,7 @@ bool player_genus( unsigned char which_genus, * called from: ability - effects - fight - it_use3 - ouch - spell - * spells - spells2 - spells3 - spells4 * *********************************************************************** */ -void dec_hp(int hp_loss, bool fatal); +void dec_hp(int hp_loss, bool fatal, const char *aux = NULL); /* *********************************************************************** @@ -436,16 +450,12 @@ void dec_disease_player(); void rot_player( int amount ); -void perform_activity(); - -void interrupt_activity( ACT_INTERRUPT ai, - const activity_interrupt_t &a = activity_interrupt_t() ); - -// last updated 15sep2001 {bwr} -/* *********************************************************************** - * called from: - * *********************************************************************** */ bool player_has_spell( int spell ); +size_type player_size( int psize = PSIZE_TORSO, bool base = false ); + +bool player_weapon_wielded(); +item_def *player_weapon(); +item_def *player_shield(); void run_macro(const char *macroname = NULL); diff --git a/crawl-ref/source/prebuilt/levcomp.lex.cc b/crawl-ref/source/prebuilt/levcomp.lex.cc new file mode 100644 index 0000000000..07ad28b7ac --- /dev/null +++ b/crawl-ref/source/prebuilt/levcomp.lex.cc @@ -0,0 +1,2522 @@ +#line 2 "levcomp.lex.cc" +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48 vern Exp $ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include +#include + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#ifndef _WIN32 +#include +#endif + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include +#include +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart YY_PROTO(( FILE *input_file )); + +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); +YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); +YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + + +#define YY_USES_REJECT +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern int yylineno; +int yylineno = 1; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 43 +#define YY_END_OF_BUFFER 44 +static yyconst short int yy_acclist[944] = + { 0, + 3, 3, 44, 42, 43, 41, 42, 43, 41, 43, + 37, 42, 43, 36, 42, 43, 38, 42, 43, 42, + 43, 42, 43, 42, 43, 42, 43, 42, 43, 42, + 43, 42, 43, 42, 43, 42, 43, 42, 43, 42, + 43, 42, 43, 42, 43, 42, 43, 42, 43, 5, + 42, 43, 42, 43, 42, 43, 3, 42, 43, 3, + 36, 42, 43, 3, 38, 42, 43, 3, 42, 43, + 3, 42, 43, 3, 42, 43, 3, 42, 43, 3, + 42, 43, 3, 42, 43, 3, 42, 43, 3, 42, + 43, 3, 42, 43, 3, 42, 43, 3, 42, 43, + + 3, 42, 43, 3, 42, 43, 3, 42, 43, 3, + 42, 43, 3, 42, 43, 3, 5, 42, 43, 3, + 42, 43, 3, 42, 43, 42, 43, 40, 41, 43, + 41, 42, 43, 37, 42, 43, 36, 42, 43, 38, + 42, 43, 42, 43, 42, 43, 42, 43, 42, 43, + 42, 43, 42, 43, 42, 43, 42, 43, 42, 43, + 42, 43, 42, 43, 42, 43, 42, 43, 42, 43, + 42, 43, 5, 42, 43, 42, 43, 42, 43, 42, + 43, 22, 41, 42, 43, 21, 41, 43, 41, 42, + 43, 20, 37, 42, 43, 36, 42, 43, 38, 42, + + 43, 42, 43, 42, 43, 42, 43, 42, 43, 42, + 43, 42, 43, 42, 43, 42, 43, 42, 43, 42, + 43, 42, 43, 42, 43, 42, 43, 42, 43, 42, + 43, 5, 42, 43, 42, 43, 42, 43, 17, 41, + 42, 43, 18, 41, 43, 41, 42, 43, 16, 36, + 42, 43, 16, 38, 42, 43, 16, 42, 43, 16, + 42, 43, 16, 42, 43, 16, 42, 43, 16, 42, + 43, 16, 42, 43, 16, 42, 43, 16, 42, 43, + 16, 42, 43, 16, 42, 43, 16, 42, 43, 16, + 42, 43, 16, 42, 43, 16, 42, 43, 16, 42, + + 43, 16, 42, 43, 16, 42, 43, 16, 42, 43, + 41, 38, 5, 5, 3, 3, 38, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 5, 2, 5, 3, 5, 3, + 3, 5, 3, 39, 40, 41, 38, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 5, 39, 5, 39, 5, 39, + 39, 41, 21, 41, 41, 38, 5, 5, 17, 41, + 18, 41, 41, 16, 16, 38, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + + 16, 16, 16, 16, 4, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 4, 3, 5, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 4, 39, 19, 19, 38, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 5, 19, 4, 19, + 5, 19, 19, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 4, 16, 30, 31, 3, 3, 3, 3, 3, 3, + + 3, 3, 3, 3, 3, 3, 30, 3, 3, 3, + 3, 3, 3, 3, 3, 31, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 30, 39, 39, 39, + 39, 39, 39, 39, 39, 31, 39, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 30, 19, + 19, 19, 19, 19, 19, 19, 19, 31, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 30, + 16, 16, 16, 16, 16, 16, 16, 16, 31, 15, + 6, 13, 28, 29, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 28, 3, 3, + + 29, 39, 39, 39, 15, 39, 6, 39, 39, 39, + 39, 13, 39, 39, 39, 39, 39, 39, 28, 39, + 39, 29, 39, 19, 19, 19, 15, 19, 6, 19, + 19, 19, 19, 13, 19, 19, 19, 19, 19, 19, + 19, 28, 19, 19, 29, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 28, 16, 16, + 29, 8, 12, 10, 3, 1, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 39, + 8, 39, 12, 39, 39, 10, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 19, 8, + + 19, 12, 19, 19, 10, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 11, 9, 14, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 11, 39, 9, 39, 14, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 11, + 19, 9, 19, 14, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 39, 39, 39, 39, 39, + + 39, 39, 39, 39, 39, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 27, 26, 32, 33, 34, + 35, 3, 3, 27, 3, 3, 26, 3, 3, 32, + 3, 33, 3, 3, 34, 3, 35, 39, 27, 39, + 39, 26, 39, 39, 32, 39, 33, 39, 39, 34, + 39, 35, 39, 19, 19, 27, 19, 19, 26, 19, + 19, 32, 19, 33, 19, 19, 34, 19, 35, 16, + 16, 27, 16, 16, 26, 16, 16, 32, 16, 33, + 16, 16, 34, 16, 35, 24, 25, 23, 3, 3, + + 24, 3, 25, 3, 23, 39, 24, 39, 25, 39, + 23, 39, 19, 19, 24, 19, 25, 19, 23, 16, + 16, 24, 16, 25, 16, 23, 3, 39, 19, 16, + 3, 39, 19, 16, 3, 39, 19, 16, 7, 7, + 39, 7, 19 + } ; + +static yyconst short int yy_accept[798] = + { 0, + 1, 1, 1, 2, 3, 3, 3, 3, 3, 3, + 3, 4, 6, 9, 11, 14, 17, 20, 22, 24, + 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, + 46, 48, 50, 53, 55, 57, 60, 64, 68, 71, + 74, 77, 80, 83, 86, 89, 92, 95, 98, 101, + 104, 107, 110, 113, 116, 120, 123, 126, 128, 131, + 134, 137, 140, 143, 145, 147, 149, 151, 153, 155, + 157, 159, 161, 163, 165, 167, 169, 171, 173, 176, + 178, 180, 182, 186, 189, 192, 196, 199, 202, 204, + 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, + + 226, 228, 230, 232, 235, 237, 239, 243, 246, 249, + 253, 257, 260, 263, 266, 269, 272, 275, 278, 281, + 284, 287, 290, 293, 296, 299, 302, 305, 308, 311, + 312, 313, 313, 313, 313, 313, 313, 313, 313, 313, + 313, 313, 313, 313, 313, 313, 313, 313, 314, 314, + 315, 315, 316, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 340, 341, 343, 344, 345, 345, + 347, 349, 350, 351, 352, 353, 354, 355, 356, 357, + 358, 359, 360, 361, 362, 363, 364, 365, 367, 368, + + 369, 371, 372, 372, 373, 375, 376, 377, 377, 377, + 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, + 377, 377, 377, 377, 378, 378, 379, 379, 381, 383, + 384, 385, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 406, 407, + 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 426, 428, 429, + 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, + + 440, 441, 442, 443, 444, 445, 447, 448, 450, 451, + 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, + 462, 463, 464, 465, 466, 467, 469, 471, 473, 474, + 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, + 485, 486, 487, 488, 489, 490, 491, 493, 493, 493, + 493, 493, 493, 493, 493, 493, 493, 493, 494, 494, + 494, 494, 494, 494, 494, 494, 495, 496, 497, 498, + 499, 500, 501, 502, 503, 504, 505, 506, 508, 509, + 510, 511, 512, 513, 514, 515, 517, 518, 519, 520, + 521, 522, 523, 524, 525, 526, 527, 529, 530, 531, + + 532, 533, 534, 535, 536, 538, 539, 540, 541, 542, + 543, 544, 545, 546, 547, 548, 550, 551, 552, 553, + 554, 555, 556, 557, 559, 560, 561, 562, 563, 564, + 565, 566, 567, 568, 569, 571, 572, 573, 574, 575, + 576, 577, 578, 580, 580, 580, 580, 581, 582, 582, + 582, 582, 583, 583, 583, 583, 583, 583, 584, 584, + 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, + 595, 596, 597, 599, 600, 602, 603, 604, 605, 607, + 609, 610, 611, 612, 614, 615, 616, 617, 618, 619, + 621, 622, 624, 625, 626, 627, 629, 631, 632, 633, + + 634, 636, 637, 638, 639, 640, 641, 643, 644, 646, + 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, + 657, 659, 660, 662, 662, 663, 664, 664, 665, 665, + 665, 665, 665, 665, 665, 665, 665, 665, 665, 665, + 666, 668, 669, 670, 671, 672, 673, 674, 675, 676, + 677, 678, 679, 680, 681, 683, 685, 686, 688, 689, + 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, + 700, 702, 704, 705, 707, 708, 709, 710, 711, 712, + 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, + 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, + + 733, 734, 734, 734, 734, 734, 734, 734, 734, 734, + 734, 734, 735, 736, 737, 738, 739, 740, 741, 742, + 743, 744, 746, 748, 750, 751, 752, 753, 754, 755, + 756, 757, 758, 759, 760, 762, 764, 766, 767, 768, + 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, + 779, 780, 781, 782, 783, 784, 785, 786, 786, 786, + 786, 786, 786, 786, 786, 786, 786, 786, 787, 788, + 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, + 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, + 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, + + 819, 820, 821, 822, 823, 824, 825, 826, 826, 827, + 827, 828, 828, 829, 830, 830, 831, 832, 833, 835, + 836, 838, 839, 841, 843, 844, 846, 848, 849, 851, + 852, 854, 855, 857, 859, 860, 862, 864, 865, 867, + 868, 870, 871, 873, 875, 876, 878, 880, 881, 883, + 884, 886, 887, 889, 891, 892, 894, 896, 896, 897, + 898, 899, 900, 902, 904, 906, 907, 909, 911, 913, + 914, 916, 918, 920, 921, 923, 925, 927, 927, 928, + 929, 930, 931, 931, 932, 933, 934, 935, 935, 936, + 937, 938, 939, 940, 942, 944, 944 + + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 5, 6, 6, 6, 1, 6, + 6, 6, 6, 7, 8, 6, 1, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 10, 1, 6, + 6, 6, 6, 6, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 20, 21, 22, 23, 24, 25, + 20, 26, 27, 28, 20, 20, 20, 20, 29, 20, + 6, 1, 6, 6, 30, 1, 31, 20, 32, 33, + + 34, 35, 20, 36, 37, 20, 20, 38, 39, 40, + 41, 42, 20, 43, 44, 45, 46, 47, 48, 20, + 20, 20, 6, 6, 6, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[49] = + { 0, + 1, 1, 2, 3, 4, 4, 3, 5, 5, 1, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5 + } ; + +static yyconst short int yy_base[809] = + { 0, + 0, 44, 88, 132, 176, 220, 264, 308, 352, 396, + 1529, 1530, 48, 51, 1530, 1530, 1519, 1509, 1511, 1504, + 1500, 1512, 1496, 1500, 1491, 1508, 1484, 25, 1476, 1485, + 1474, 1480, 0, 46, 397, 0, 0, 1504, 1494, 1496, + 1487, 1488, 1484, 1496, 1480, 1484, 1475, 1492, 1468, 27, + 1460, 1469, 1458, 1464, 70, 51, 398, 57, 79, 82, + 74, 136, 137, 140, 141, 145, 146, 149, 224, 153, + 225, 157, 228, 403, 404, 229, 407, 312, 160, 232, + 408, 0, 169, 235, 242, 1530, 0, 1488, 1478, 1480, + 1473, 1469, 1481, 1465, 1469, 1460, 1477, 1453, 29, 1445, + + 1454, 1443, 1449, 244, 141, 409, 255, 315, 318, 0, + 1473, 0, 1463, 1465, 1458, 1454, 1466, 1450, 1454, 1445, + 1462, 1438, 209, 1430, 1439, 1428, 1434, 304, 410, 321, + 1458, 1455, 1440, 1453, 1440, 1440, 1442, 1449, 1437, 1441, + 1422, 1412, 1423, 296, 1414, 1407, 1408, 0, 1426, 0, + 411, 0, 1441, 1438, 1423, 1433, 1435, 1422, 1422, 1424, + 1431, 1419, 1423, 1404, 1394, 1405, 297, 1396, 1389, 1390, + 60, 1530, 249, 331, 1408, 341, 412, 165, 259, 417, + 327, 420, 421, 424, 455, 425, 456, 459, 460, 463, + 464, 428, 469, 472, 477, 481, 482, 345, 485, 486, + + 489, 490, 1430, 494, 501, 504, 335, 498, 508, 510, + 512, 233, 511, 514, 516, 520, 402, 475, 518, 521, + 522, 526, 527, 537, 529, 538, 541, 545, 553, 556, + 0, 1422, 1419, 1404, 1417, 1404, 1404, 1406, 1413, 1401, + 1405, 1386, 1376, 1387, 522, 1378, 1371, 1372, 1390, 531, + 1391, 1385, 1395, 1384, 1395, 1394, 1395, 1395, 1379, 1374, + 1359, 1362, 527, 1357, 1368, 1355, 1354, 1530, 1375, 1369, + 1374, 1378, 1367, 1378, 1377, 1378, 1378, 1362, 1357, 1342, + 1345, 530, 1340, 1351, 1338, 1337, 0, 577, 565, 578, + 587, 588, 592, 593, 596, 599, 608, 612, 615, 616, + + 619, 620, 623, 624, 627, 628, 1379, 559, 530, 574, + 631, 442, 584, 632, 66, 581, 611, 637, 634, 603, + 635, 638, 640, 639, 643, 647, 1378, 657, 648, 1356, + 1350, 1360, 1349, 1360, 1359, 1360, 1360, 1344, 1339, 1324, + 1327, 627, 1322, 1333, 1320, 1319, 0, 1350, 1344, 1334, + 1350, 1349, 1335, 1342, 1332, 1345, 1308, 1530, 1314, 1313, + 1310, 1311, 1313, 1314, 1311, 1530, 1333, 1327, 1333, 1316, + 1332, 1331, 1317, 1324, 1314, 1327, 1290, 0, 1296, 1295, + 1292, 1293, 1295, 1296, 1293, 0, 673, 683, 687, 692, + 693, 696, 702, 703, 706, 707, 711, 716, 719, 720, + + 724, 728, 731, 732, 727, 735, 723, 740, 736, 737, + 644, 678, 688, 741, 738, 1326, 742, 743, 747, 748, + 750, 755, 754, 1325, 1313, 1307, 1297, 1313, 1312, 1298, + 1305, 1295, 1308, 1271, 0, 1277, 1276, 1273, 1274, 1276, + 1277, 1274, 0, 1294, 1298, 1297, 1530, 1530, 1278, 1295, + 1283, 1530, 1265, 1260, 1264, 1255, 1262, 725, 1259, 726, + 1282, 1286, 1270, 1284, 1265, 1282, 1270, 1252, 1247, 1251, + 1242, 1249, 728, 1246, 732, 768, 767, 789, 790, 793, + 794, 798, 799, 802, 803, 807, 808, 811, 814, 819, + 815, 823, 822, 824, 826, 1282, 1281, 812, 828, 827, + + 1280, 830, 831, 829, 833, 837, 841, 840, 842, 1266, + 1270, 1269, 1250, 1267, 1255, 1237, 1232, 1236, 1227, 1234, + 812, 1231, 813, 1259, 1530, 1530, 1258, 1530, 1257, 1221, + 1234, 1221, 1232, 1219, 1230, 1226, 1218, 1227, 1223, 1246, + 0, 1245, 1244, 1208, 1221, 1208, 1219, 1206, 1217, 1213, + 1205, 1214, 1210, 848, 860, 861, 878, 868, 881, 882, + 890, 891, 894, 895, 898, 899, 902, 905, 906, 867, + 1241, 1240, 903, 1239, 909, 879, 650, 656, 885, 910, + 913, 912, 915, 916, 918, 1230, 1229, 1228, 1192, 1205, + 1192, 1203, 1190, 1201, 1197, 1189, 1198, 1194, 1530, 1530, + + 1530, 1219, 1182, 1182, 1179, 1180, 1178, 1177, 1180, 1175, + 1174, 1209, 1172, 1172, 1169, 1170, 1168, 1167, 1170, 1165, + 1164, 924, 928, 933, 937, 946, 953, 947, 956, 957, + 960, 963, 964, 967, 1205, 1204, 1203, 239, 929, 920, + 940, 952, 968, 970, 973, 972, 974, 1196, 1159, 1159, + 1156, 1157, 1155, 1154, 1157, 1152, 1151, 1161, 1149, 1151, + 1157, 1149, 1144, 1143, 1150, 1141, 1140, 1151, 1139, 1141, + 1144, 1133, 1128, 1123, 1121, 1109, 1108, 976, 975, 979, + 987, 982, 996, 1003, 1013, 1020, 1023, 752, 985, 991, + 992, 995, 986, 1000, 1026, 1004, 1008, 1115, 1099, 1090, + + 1094, 1086, 1080, 1079, 1086, 1077, 1076, 1086, 1530, 1076, + 1530, 1075, 1530, 1530, 1085, 1530, 1530, 1082, 0, 1072, + 0, 1071, 0, 0, 1081, 0, 0, 1033, 1036, 1040, + 1050, 1053, 1054, 1057, 1058, 1062, 1067, 1041, 1110, 1031, + 1109, 1037, 1107, 1106, 1044, 1105, 1104, 1071, 0, 1061, + 0, 1060, 0, 0, 1070, 0, 0, 1057, 1530, 1530, + 1530, 1036, 0, 0, 0, 1068, 1075, 1080, 1083, 1049, + 1032, 980, 921, 817, 0, 0, 0, 810, 734, 1084, + 1028, 733, 739, 640, 1090, 1045, 400, 423, 340, 1091, + 1087, 165, 1530, 1096, 161, 1530, 1129, 1131, 1136, 1141, + + 1146, 1151, 1156, 82, 1161, 1166, 1171, 1176 + } ; + +static yyconst short int yy_def[809] = + { 0, + 796, 1, 796, 3, 796, 5, 796, 7, 796, 9, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 797, 796, 796, 798, 798, 798, 798, 798, + 798, 798, 798, 798, 798, 798, 798, 798, 798, 798, + 798, 798, 798, 798, 799, 798, 798, 800, 796, 796, + 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, + 800, 800, 800, 800, 800, 800, 800, 800, 801, 800, + 800, 802, 796, 796, 796, 796, 802, 802, 802, 802, + 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, + + 802, 802, 802, 803, 802, 802, 796, 796, 796, 804, + 804, 804, 804, 804, 804, 804, 804, 804, 804, 804, + 804, 804, 804, 804, 804, 804, 804, 804, 804, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 797, 796, 797, + 796, 798, 798, 798, 798, 798, 798, 798, 798, 798, + 798, 798, 798, 798, 798, 798, 798, 798, 798, 798, + 805, 796, 805, 799, 798, 806, 798, 800, 800, 796, + 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, + 800, 800, 800, 800, 800, 800, 800, 801, 801, 800, + + 801, 800, 807, 796, 796, 796, 807, 807, 807, 807, + 807, 807, 807, 807, 807, 807, 807, 807, 807, 807, + 807, 807, 807, 808, 807, 808, 807, 796, 796, 796, + 804, 804, 804, 804, 804, 804, 804, 804, 804, 804, + 804, 804, 804, 804, 804, 804, 804, 804, 804, 804, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 798, 798, + 798, 798, 798, 798, 798, 798, 798, 798, 798, 798, + 798, 798, 798, 798, 798, 798, 798, 806, 800, 800, + 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, + + 800, 800, 800, 800, 800, 800, 807, 807, 807, 807, + 807, 807, 807, 807, 807, 807, 807, 807, 807, 807, + 807, 807, 807, 807, 807, 808, 807, 808, 807, 804, + 804, 804, 804, 804, 804, 804, 804, 804, 804, 804, + 804, 804, 804, 804, 804, 804, 804, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 798, 798, 798, 798, + 798, 798, 798, 798, 798, 798, 798, 798, 798, 798, + 798, 798, 798, 798, 798, 798, 800, 800, 800, 800, + 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, + + 800, 800, 800, 800, 800, 807, 807, 807, 807, 807, + 807, 807, 807, 807, 807, 807, 807, 807, 807, 807, + 807, 807, 807, 807, 804, 804, 804, 804, 804, 804, + 804, 804, 804, 804, 804, 804, 804, 804, 804, 804, + 804, 804, 804, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 798, 798, 798, 798, 798, 798, 798, 798, 798, 798, + 798, 798, 798, 798, 798, 800, 800, 800, 800, 800, + 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, + 800, 800, 807, 807, 807, 807, 807, 807, 807, 807, + + 807, 807, 807, 807, 807, 807, 807, 807, 807, 804, + 804, 804, 804, 804, 804, 804, 804, 804, 804, 804, + 804, 804, 804, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 798, + 798, 798, 798, 798, 798, 798, 798, 798, 798, 798, + 798, 798, 798, 800, 800, 800, 800, 800, 800, 800, + 800, 800, 800, 800, 800, 800, 800, 800, 800, 807, + 807, 807, 807, 807, 807, 807, 807, 807, 807, 807, + 807, 807, 807, 807, 807, 804, 804, 804, 804, 804, + 804, 804, 804, 804, 804, 804, 804, 804, 796, 796, + + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 798, 798, 798, 798, 798, 798, 798, 798, 798, + 798, 800, 800, 800, 800, 800, 800, 800, 800, 800, + 800, 800, 800, 800, 807, 807, 807, 807, 807, 807, + 807, 807, 807, 807, 807, 807, 807, 804, 804, 804, + 804, 804, 804, 804, 804, 804, 804, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 798, 798, 798, + 798, 798, 798, 798, 798, 798, 798, 800, 800, 800, + 800, 800, 800, 800, 800, 800, 800, 807, 807, 807, + 807, 807, 807, 807, 807, 807, 807, 804, 804, 804, + + 804, 804, 804, 804, 804, 804, 804, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 798, 798, 798, + 798, 798, 798, 798, 798, 798, 798, 800, 800, 800, + 800, 800, 800, 800, 800, 800, 800, 807, 807, 807, + 807, 807, 807, 807, 807, 807, 807, 804, 804, 804, + 804, 804, 804, 804, 804, 804, 804, 796, 796, 796, + 796, 798, 798, 798, 798, 800, 800, 800, 800, 807, + 807, 807, 807, 804, 804, 804, 804, 796, 798, 800, + 807, 804, 796, 798, 800, 807, 804, 796, 798, 800, + 807, 804, 796, 800, 807, 0, 796, 796, 796, 796, + + 796, 796, 796, 796, 796, 796, 796, 796 + } ; + +static yyconst short int yy_nxt[1579] = + { 0, + 12, 13, 14, 13, 12, 12, 15, 16, 17, 12, + 12, 12, 18, 19, 12, 20, 12, 12, 12, 12, + 12, 21, 22, 23, 24, 12, 25, 26, 12, 12, + 12, 12, 27, 28, 12, 12, 12, 12, 12, 29, + 12, 30, 12, 31, 12, 12, 12, 32, 33, 130, + 130, 130, 130, 130, 130, 142, 149, 165, 179, 218, + 179, 175, 172, 173, 143, 34, 166, 203, 219, 135, + 171, 171, 172, 173, 158, 179, 171, 179, 412, 171, + 130, 130, 130, 130, 180, 130, 231, 35, 12, 13, + 14, 13, 36, 36, 15, 37, 38, 12, 36, 36, + + 39, 40, 41, 42, 36, 36, 36, 36, 36, 43, + 44, 45, 46, 36, 47, 48, 36, 36, 36, 36, + 49, 50, 36, 36, 36, 36, 36, 51, 36, 52, + 36, 53, 36, 36, 36, 54, 55, 179, 179, 179, + 179, 179, 179, 179, 179, 181, 179, 179, 179, 179, + 179, 225, 179, 56, 179, 183, 179, 182, 179, 186, + 179, 199, 203, 199, 211, 184, 179, 190, 179, 185, + 204, 205, 206, 188, 793, 57, 58, 13, 59, 60, + 58, 58, 61, 62, 63, 58, 58, 58, 64, 65, + 58, 66, 58, 58, 58, 58, 58, 67, 68, 69, + + 70, 58, 71, 72, 58, 58, 58, 58, 73, 74, + 58, 58, 58, 58, 58, 75, 58, 76, 58, 77, + 58, 58, 58, 78, 79, 179, 179, 179, 179, 179, + 179, 179, 179, 179, 203, 179, 130, 130, 130, 243, + 203, 80, 200, 130, 205, 130, 688, 148, 244, 187, + 148, 172, 173, 189, 313, 185, 228, 229, 230, 195, + 179, 191, 179, 81, 82, 83, 84, 85, 82, 82, + 86, 87, 88, 82, 82, 82, 89, 90, 82, 91, + 82, 82, 82, 82, 82, 92, 93, 94, 95, 82, + 96, 97, 82, 82, 82, 82, 98, 99, 82, 82, + + 82, 82, 82, 100, 82, 101, 82, 102, 82, 82, + 82, 103, 104, 179, 249, 179, 130, 130, 130, 130, + 229, 130, 130, 130, 130, 263, 282, 236, 179, 105, + 179, 171, 171, 172, 173, 181, 203, 171, 264, 283, + 171, 148, 148, 308, 148, 197, 199, 148, 199, 793, + 148, 106, 12, 107, 108, 109, 12, 12, 15, 110, + 111, 12, 112, 112, 113, 114, 112, 115, 112, 112, + 112, 112, 112, 116, 117, 118, 119, 112, 120, 121, + 112, 112, 112, 112, 122, 123, 112, 112, 112, 112, + 112, 124, 112, 125, 112, 126, 112, 112, 112, 127, + + 33, 150, 176, 203, 179, 179, 179, 179, 179, 179, + 179, 179, 201, 226, 150, 150, 176, 128, 130, 130, + 130, 179, 179, 179, 179, 179, 179, 179, 179, 179, + 289, 179, 793, 192, 291, 792, 318, 146, 169, 129, + 151, 177, 193, 203, 194, 290, 293, 196, 196, 222, + 247, 202, 227, 250, 151, 177, 179, 179, 179, 179, + 179, 179, 179, 179, 179, 179, 179, 179, 409, 295, + 179, 299, 179, 179, 294, 179, 203, 292, 179, 297, + 179, 296, 179, 179, 179, 179, 199, 179, 199, 179, + 199, 179, 199, 179, 201, 204, 205, 206, 298, 203, + + 300, 301, 130, 130, 130, 130, 205, 130, 309, 203, + 306, 203, 203, 203, 302, 203, 303, 203, 319, 203, + 311, 203, 203, 203, 315, 305, 304, 203, 203, 314, + 203, 203, 310, 202, 312, 150, 317, 316, 224, 224, + 148, 148, 203, 148, 148, 328, 228, 229, 230, 320, + 321, 342, 406, 327, 130, 130, 130, 130, 229, 130, + 203, 323, 360, 322, 343, 380, 179, 308, 179, 361, + 325, 324, 381, 362, 250, 203, 382, 148, 148, 179, + 148, 179, 203, 148, 329, 203, 148, 387, 179, 179, + 179, 179, 413, 179, 179, 179, 179, 179, 410, 179, + + 179, 407, 179, 389, 203, 388, 391, 392, 393, 179, + 394, 179, 203, 179, 390, 179, 179, 179, 179, 179, + 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, + 179, 179, 203, 203, 395, 203, 203, 414, 203, 203, + 203, 203, 396, 417, 203, 203, 411, 408, 224, 203, + 148, 203, 328, 148, 399, 403, 398, 203, 224, 397, + 148, 400, 437, 148, 402, 401, 498, 415, 404, 438, + 418, 405, 422, 439, 179, 789, 179, 419, 416, 203, + 639, 420, 421, 423, 179, 476, 179, 424, 179, 203, + 179, 329, 499, 179, 179, 179, 179, 179, 640, 179, + + 477, 479, 480, 179, 179, 179, 179, 179, 179, 179, + 179, 500, 179, 478, 179, 484, 482, 179, 481, 179, + 179, 179, 179, 179, 203, 179, 483, 179, 179, 179, + 179, 179, 179, 179, 179, 179, 203, 203, 203, 203, + 494, 203, 203, 203, 203, 496, 497, 493, 203, 203, + 501, 203, 485, 203, 486, 203, 203, 487, 535, 538, + 488, 549, 489, 490, 491, 552, 495, 492, 179, 179, + 179, 179, 536, 539, 788, 550, 555, 787, 784, 553, + 503, 504, 554, 502, 738, 507, 506, 505, 508, 509, + 179, 179, 179, 179, 179, 179, 179, 179, 556, 179, + + 179, 179, 179, 179, 179, 179, 179, 558, 179, 179, + 179, 179, 179, 203, 179, 179, 179, 179, 179, 559, + 179, 557, 179, 203, 179, 203, 179, 203, 203, 203, + 203, 203, 203, 571, 203, 572, 570, 574, 203, 573, + 560, 203, 203, 203, 562, 594, 597, 575, 561, 179, + 564, 179, 565, 567, 783, 563, 568, 622, 782, 595, + 598, 179, 179, 179, 179, 578, 566, 576, 203, 179, + 569, 179, 577, 580, 581, 584, 635, 579, 583, 179, + 203, 179, 179, 179, 179, 179, 203, 623, 582, 585, + 624, 179, 179, 179, 179, 179, 179, 179, 179, 179, + + 179, 179, 179, 179, 203, 179, 179, 179, 179, 179, + 203, 203, 636, 203, 203, 641, 203, 203, 637, 203, + 626, 203, 203, 638, 628, 179, 625, 179, 630, 179, + 203, 179, 631, 627, 179, 633, 179, 629, 179, 634, + 179, 203, 632, 643, 678, 644, 646, 179, 179, 179, + 179, 647, 642, 203, 179, 645, 179, 179, 179, 179, + 179, 179, 690, 179, 179, 179, 179, 179, 179, 203, + 179, 203, 689, 203, 203, 203, 179, 179, 179, 179, + 179, 203, 179, 179, 691, 179, 203, 203, 179, 679, + 179, 681, 203, 203, 692, 680, 203, 179, 682, 179, + + 683, 203, 685, 684, 179, 203, 179, 686, 728, 203, + 687, 693, 695, 694, 179, 696, 179, 697, 729, 730, + 731, 179, 732, 179, 179, 741, 179, 203, 739, 203, + 743, 740, 203, 203, 179, 742, 179, 179, 203, 179, + 733, 179, 203, 179, 744, 203, 203, 734, 746, 735, + 203, 179, 747, 179, 179, 179, 179, 179, 179, 179, + 179, 179, 745, 179, 736, 179, 766, 737, 179, 179, + 179, 179, 786, 771, 770, 773, 179, 779, 179, 772, + 791, 179, 767, 179, 179, 179, 179, 179, 203, 769, + 781, 179, 179, 179, 179, 768, 795, 179, 778, 179, + + 794, 777, 776, 775, 774, 203, 203, 203, 203, 780, + 203, 203, 765, 764, 763, 762, 761, 760, 759, 758, + 757, 756, 755, 754, 753, 790, 752, 751, 785, 148, + 750, 148, 148, 148, 152, 152, 174, 174, 174, 174, + 174, 178, 749, 178, 178, 178, 198, 748, 198, 198, + 198, 203, 727, 726, 203, 203, 224, 725, 224, 224, + 224, 171, 171, 171, 171, 171, 288, 724, 288, 288, + 288, 307, 723, 722, 307, 307, 326, 721, 326, 326, + 326, 720, 719, 718, 717, 716, 715, 714, 713, 712, + 711, 710, 709, 708, 707, 706, 705, 704, 703, 702, + + 701, 700, 699, 698, 203, 203, 203, 677, 676, 675, + 674, 673, 672, 671, 670, 669, 668, 667, 666, 665, + 664, 663, 662, 661, 660, 659, 658, 657, 656, 655, + 654, 653, 652, 651, 650, 649, 648, 601, 600, 599, + 203, 203, 203, 621, 620, 619, 618, 617, 616, 615, + 614, 613, 612, 601, 600, 599, 611, 610, 609, 608, + 607, 606, 605, 604, 603, 602, 601, 600, 599, 596, + 593, 592, 591, 590, 589, 588, 528, 587, 526, 525, + 586, 203, 203, 203, 551, 548, 547, 546, 545, 544, + 543, 528, 542, 526, 541, 525, 540, 537, 534, 533, + + 532, 531, 530, 529, 528, 527, 526, 525, 524, 523, + 522, 521, 520, 519, 518, 517, 516, 452, 515, 514, + 513, 448, 447, 512, 511, 510, 203, 203, 475, 474, + 473, 472, 471, 470, 469, 468, 452, 467, 466, 465, + 448, 447, 464, 463, 462, 461, 460, 459, 458, 457, + 456, 455, 454, 453, 452, 451, 450, 449, 448, 447, + 446, 445, 444, 443, 442, 441, 440, 436, 435, 434, + 433, 432, 431, 430, 429, 428, 427, 426, 425, 203, + 203, 386, 385, 384, 383, 379, 378, 377, 376, 375, + 374, 373, 372, 371, 370, 369, 368, 367, 366, 365, + + 364, 363, 359, 358, 357, 356, 355, 354, 353, 352, + 351, 350, 349, 348, 347, 346, 345, 344, 341, 340, + 339, 338, 337, 336, 335, 334, 333, 332, 331, 330, + 232, 203, 287, 286, 285, 284, 281, 280, 279, 278, + 277, 276, 275, 274, 273, 272, 271, 270, 269, 153, + 268, 267, 266, 265, 262, 261, 260, 259, 258, 257, + 256, 255, 254, 253, 252, 251, 131, 248, 247, 246, + 245, 242, 241, 240, 239, 238, 237, 236, 235, 234, + 233, 232, 223, 222, 221, 220, 217, 216, 215, 214, + 213, 212, 211, 210, 209, 208, 207, 170, 169, 168, + + 167, 164, 163, 162, 161, 160, 159, 158, 157, 156, + 155, 154, 153, 147, 146, 145, 144, 141, 140, 139, + 138, 137, 136, 135, 134, 133, 132, 131, 796, 11, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796 + } ; + +static yyconst short int yy_chk[1579] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 13, + 13, 13, 14, 14, 14, 28, 34, 50, 58, 99, + 58, 56, 171, 171, 28, 2, 50, 315, 99, 34, + 55, 55, 55, 55, 56, 61, 55, 61, 315, 55, + 59, 59, 59, 60, 60, 60, 804, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 4, 62, 63, 62, + 63, 64, 65, 64, 65, 63, 66, 67, 66, 67, + 68, 105, 68, 4, 70, 65, 70, 64, 72, 68, + 72, 79, 795, 79, 105, 66, 178, 72, 178, 67, + 83, 83, 83, 70, 792, 4, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 6, 69, 71, 69, 71, 73, + 76, 73, 76, 80, 212, 80, 84, 84, 84, 123, + 638, 6, 80, 85, 85, 85, 638, 104, 123, 69, + 104, 173, 173, 71, 212, 80, 107, 107, 107, 76, + 179, 73, 179, 6, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 8, 78, 128, 78, 108, 108, 108, 109, + 109, 109, 130, 130, 130, 144, 167, 128, 181, 8, + 181, 174, 174, 174, 174, 181, 207, 174, 144, 167, + 174, 176, 176, 207, 176, 78, 198, 176, 198, 789, + 176, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + + 10, 35, 57, 217, 74, 75, 74, 75, 77, 81, + 77, 81, 81, 106, 129, 151, 177, 10, 180, 180, + 180, 182, 183, 182, 183, 184, 186, 184, 186, 192, + 182, 192, 788, 74, 184, 787, 217, 35, 57, 10, + 35, 57, 74, 312, 75, 183, 186, 77, 81, 106, + 129, 81, 106, 129, 151, 177, 185, 187, 185, 187, + 188, 189, 188, 189, 190, 191, 190, 191, 312, 188, + 193, 192, 193, 194, 187, 194, 218, 185, 195, 190, + 195, 189, 196, 197, 196, 197, 199, 200, 199, 200, + 201, 202, 201, 202, 202, 204, 204, 204, 191, 208, + + 193, 194, 205, 205, 205, 206, 206, 206, 208, 209, + 200, 210, 213, 211, 194, 214, 195, 215, 218, 219, + 210, 216, 220, 221, 214, 197, 196, 222, 223, 213, + 225, 309, 209, 202, 211, 250, 216, 215, 224, 226, + 224, 226, 227, 224, 226, 227, 228, 228, 228, 219, + 220, 245, 309, 225, 229, 229, 229, 230, 230, 230, + 308, 221, 263, 220, 245, 282, 289, 308, 289, 263, + 223, 222, 282, 263, 250, 310, 282, 288, 288, 290, + 288, 290, 316, 288, 227, 313, 288, 289, 291, 292, + 291, 292, 316, 293, 294, 293, 294, 295, 313, 295, + + 296, 310, 296, 291, 320, 290, 293, 294, 295, 297, + 296, 297, 317, 298, 292, 298, 299, 300, 299, 300, + 301, 302, 301, 302, 303, 304, 303, 304, 305, 306, + 305, 306, 311, 314, 297, 319, 321, 317, 318, 322, + 324, 323, 298, 320, 325, 411, 314, 311, 326, 329, + 326, 577, 329, 326, 301, 303, 300, 578, 328, 299, + 328, 301, 342, 328, 302, 301, 411, 318, 304, 342, + 321, 305, 323, 342, 387, 784, 387, 321, 319, 412, + 577, 321, 322, 324, 388, 387, 388, 325, 389, 413, + 389, 329, 412, 390, 391, 390, 391, 392, 578, 392, + + 388, 390, 391, 393, 394, 393, 394, 395, 396, 395, + 396, 413, 397, 389, 397, 395, 393, 398, 392, 398, + 399, 400, 399, 400, 407, 401, 394, 401, 405, 402, + 405, 402, 403, 404, 403, 404, 406, 409, 410, 415, + 407, 408, 414, 417, 418, 409, 410, 406, 419, 420, + 414, 421, 396, 688, 398, 423, 422, 399, 458, 460, + 400, 473, 401, 402, 403, 475, 408, 404, 477, 476, + 477, 476, 458, 460, 783, 473, 477, 782, 779, 475, + 417, 418, 476, 415, 688, 421, 420, 419, 422, 423, + 478, 479, 478, 479, 480, 481, 480, 481, 478, 482, + + 483, 482, 483, 484, 485, 484, 485, 482, 486, 487, + 486, 487, 488, 498, 488, 489, 491, 489, 491, 483, + 490, 481, 490, 493, 492, 494, 492, 495, 500, 499, + 504, 502, 503, 494, 505, 495, 493, 499, 506, 498, + 485, 508, 507, 509, 487, 521, 523, 500, 486, 554, + 489, 554, 490, 491, 778, 488, 492, 554, 774, 521, + 523, 555, 556, 555, 556, 504, 490, 502, 570, 558, + 492, 558, 503, 506, 507, 509, 570, 505, 508, 557, + 576, 557, 559, 560, 559, 560, 579, 557, 507, 509, + 559, 561, 562, 561, 562, 563, 564, 563, 564, 565, + + 566, 565, 566, 567, 573, 567, 568, 569, 568, 569, + 575, 580, 573, 582, 581, 579, 583, 584, 575, 585, + 561, 640, 773, 576, 563, 622, 560, 622, 565, 623, + 639, 623, 566, 562, 624, 568, 624, 564, 625, 569, + 625, 641, 567, 581, 625, 582, 584, 626, 628, 626, + 628, 585, 580, 642, 627, 583, 627, 629, 630, 629, + 630, 631, 640, 631, 632, 633, 632, 633, 634, 643, + 634, 644, 639, 646, 645, 647, 679, 678, 679, 678, + 680, 772, 680, 682, 641, 682, 689, 693, 681, 626, + 681, 628, 690, 691, 642, 627, 692, 683, 629, 683, + + 630, 694, 632, 631, 684, 696, 684, 633, 678, 697, + 634, 643, 645, 644, 685, 646, 685, 647, 679, 680, + 681, 686, 682, 686, 687, 691, 687, 695, 689, 781, + 693, 690, 740, 771, 728, 692, 728, 729, 742, 729, + 683, 730, 738, 730, 694, 745, 786, 684, 696, 685, + 770, 731, 697, 731, 732, 733, 732, 733, 734, 735, + 734, 735, 695, 736, 686, 736, 728, 687, 737, 766, + 737, 766, 781, 740, 738, 745, 767, 762, 767, 742, + 786, 768, 730, 768, 769, 780, 769, 780, 791, 735, + 770, 785, 790, 785, 790, 732, 791, 794, 758, 794, + + 790, 755, 752, 750, 748, 747, 746, 744, 743, 766, + 741, 739, 725, 722, 720, 718, 715, 712, 710, 708, + 707, 706, 705, 704, 703, 785, 702, 701, 780, 797, + 700, 797, 797, 797, 798, 798, 799, 799, 799, 799, + 799, 800, 699, 800, 800, 800, 801, 698, 801, 801, + 801, 802, 677, 676, 802, 802, 803, 675, 803, 803, + 803, 805, 805, 805, 805, 805, 806, 674, 806, 806, + 806, 807, 673, 672, 807, 807, 808, 671, 808, 808, + 808, 670, 669, 668, 667, 666, 665, 664, 663, 662, + 661, 660, 659, 658, 657, 656, 655, 654, 653, 652, + + 651, 650, 649, 648, 637, 636, 635, 621, 620, 619, + 618, 617, 616, 615, 614, 613, 612, 611, 610, 609, + 608, 607, 606, 605, 604, 603, 602, 598, 597, 596, + 595, 594, 593, 592, 591, 590, 589, 588, 587, 586, + 574, 572, 571, 553, 552, 551, 550, 549, 548, 547, + 546, 545, 544, 543, 542, 540, 539, 538, 537, 536, + 535, 534, 533, 532, 531, 530, 529, 527, 524, 522, + 520, 519, 518, 517, 516, 515, 514, 513, 512, 511, + 510, 501, 497, 496, 474, 472, 471, 470, 469, 468, + 467, 466, 465, 464, 463, 462, 461, 459, 457, 456, + + 455, 454, 453, 451, 450, 449, 446, 445, 444, 442, + 441, 440, 439, 438, 437, 436, 434, 433, 432, 431, + 430, 429, 428, 427, 426, 425, 424, 416, 385, 384, + 383, 382, 381, 380, 379, 377, 376, 375, 374, 373, + 372, 371, 370, 369, 368, 367, 365, 364, 363, 362, + 361, 360, 359, 357, 356, 355, 354, 353, 352, 351, + 350, 349, 348, 346, 345, 344, 343, 341, 340, 339, + 338, 337, 336, 335, 334, 333, 332, 331, 330, 327, + 307, 286, 285, 284, 283, 281, 280, 279, 278, 277, + 276, 275, 274, 273, 272, 271, 270, 269, 267, 266, + + 265, 264, 262, 261, 260, 259, 258, 257, 256, 255, + 254, 253, 252, 251, 249, 248, 247, 246, 244, 243, + 242, 241, 240, 239, 238, 237, 236, 235, 234, 233, + 232, 203, 175, 170, 169, 168, 166, 165, 164, 163, + 162, 161, 160, 159, 158, 157, 156, 155, 154, 153, + 149, 147, 146, 145, 143, 142, 141, 140, 139, 138, + 137, 136, 135, 134, 133, 132, 131, 127, 126, 125, + 124, 122, 121, 120, 119, 118, 117, 116, 115, 114, + 113, 111, 103, 102, 101, 100, 98, 97, 96, 95, + 94, 93, 92, 91, 90, 89, 88, 54, 53, 52, + + 51, 49, 48, 47, 46, 45, 44, 43, 42, 41, + 40, 39, 38, 32, 31, 30, 29, 27, 26, 25, + 24, 23, 22, 21, 20, 19, 18, 17, 11, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, + 796, 796, 796, 796, 796, 796, 796, 796 + } ; + +static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr; +static char *yy_full_match; +static int yy_lp; +#define REJECT \ +{ \ +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \ +yy_cp = yy_full_match; /* restore poss. backed-over text */ \ +++yy_lp; \ +goto find_rule; \ +} +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "levcomp.lpp" +#define INITIAL 0 +#line 2 "levcomp.lpp" + +// levcomp.l: +// Level compiler lexer for Dungeon Crawl Stone Soup. +// +// Based loosely on NetHack's lev_comp.l + +#include "AppHdr.h" +#include "levcomp.tab.h" +#include + +static bool alloced = false; + +static void clean() +{ + if (yylval.text && alloced) + free( (void*) yylval.text); + yylval.text = NULL; + alloced = false; +} + +static void settext() +{ + clean(); + if ((yylval.text = strdup(yytext))) + alloced = true; +} + +#define MAPDEF 1 + +#define ARGUMENT 2 + +#define MNAME 3 + +#define KEYWORDS 4 + +#define YY_NEVER_INTERACTIVE 1 +#line 1130 "levcomp.lex.cc" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + } +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + yy_current_buffer->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 41 "levcomp.lpp" + + +#line 1298 "levcomp.lex.cc" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; + yy_current_state += YY_AT_BOL(); + yy_state_ptr = yy_state_buf; + *yy_state_ptr++ = yy_current_state; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 797 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *yy_state_ptr++ = yy_current_state; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 1530 ); + +yy_find_action: + yy_current_state = *--yy_state_ptr; + yy_lp = yy_accept[yy_current_state]; +find_rule: /* we branch to this label when backing up */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] ) + { + yy_act = yy_acclist[yy_lp]; + { + yy_full_match = yy_cp; + break; + } + } + --yy_cp; + yy_current_state = *--yy_state_ptr; + yy_lp = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + if ( yy_act != YY_END_OF_BUFFER ) + { + int yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + ++yylineno; + } + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +YY_RULE_SETUP +#line 43 "levcomp.lpp" +{ BEGIN(INITIAL); } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 45 "levcomp.lpp" +; + YY_BREAK +case 3: +YY_RULE_SETUP +#line 47 "levcomp.lpp" +{ + settext(); + return MAP_LINE; + } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 52 "levcomp.lpp" +{ BEGIN(MAPDEF); } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 54 "levcomp.lpp" +; + YY_BREAK +case 6: +YY_RULE_SETUP +#line 56 "levcomp.lpp" +{ BEGIN(ARGUMENT); return NAME; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 57 "levcomp.lpp" +return DEFAULT_DEPTH; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 58 "levcomp.lpp" +return DEPTH; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 59 "levcomp.lpp" +return ORIENT; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 60 "levcomp.lpp" +{ BEGIN(ARGUMENT); return PLACE; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 61 "levcomp.lpp" +return CHANCE; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 62 "levcomp.lpp" +return FLAGS; + YY_BREAK +case 13: +YY_RULE_SETUP +#line 63 "levcomp.lpp" +{ BEGIN(KEYWORDS); return TAGS; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 64 "levcomp.lpp" +{ BEGIN(ARGUMENT); return SYMBOL; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 65 "levcomp.lpp" +{ BEGIN(MNAME); return MONS; } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 67 "levcomp.lpp" +{ + settext(); + return STRING; + } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 72 "levcomp.lpp" +; + YY_BREAK +case 18: +YY_RULE_SETUP +#line 73 "levcomp.lpp" +{ BEGIN(INITIAL); } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 75 "levcomp.lpp" +{ + settext(); + return MONSTER_NAME; + } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 80 "levcomp.lpp" +return COMMA; + YY_BREAK +case 21: +YY_RULE_SETUP +#line 81 "levcomp.lpp" +{ BEGIN(INITIAL); } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 82 "levcomp.lpp" +; + YY_BREAK +case 23: +YY_RULE_SETUP +#line 84 "levcomp.lpp" +return PANDEMONIC; + YY_BREAK +case 24: +YY_RULE_SETUP +#line 85 "levcomp.lpp" +return NO_HMIRROR; + YY_BREAK +case 25: +YY_RULE_SETUP +#line 86 "levcomp.lpp" +return NO_VMIRROR; + YY_BREAK +case 26: +YY_RULE_SETUP +#line 87 "levcomp.lpp" +return NO_ROTATE; + YY_BREAK +case 27: +YY_RULE_SETUP +#line 89 "levcomp.lpp" +return ENCOMPASS; + YY_BREAK +case 28: +YY_RULE_SETUP +#line 90 "levcomp.lpp" +return NORTH; + YY_BREAK +case 29: +YY_RULE_SETUP +#line 91 "levcomp.lpp" +return SOUTH; + YY_BREAK +case 30: +YY_RULE_SETUP +#line 92 "levcomp.lpp" +return EAST; + YY_BREAK +case 31: +YY_RULE_SETUP +#line 93 "levcomp.lpp" +return WEST; + YY_BREAK +case 32: +YY_RULE_SETUP +#line 94 "levcomp.lpp" +return NORTHEAST; + YY_BREAK +case 33: +YY_RULE_SETUP +#line 95 "levcomp.lpp" +return NORTHWEST; + YY_BREAK +case 34: +YY_RULE_SETUP +#line 96 "levcomp.lpp" +return SOUTHEAST; + YY_BREAK +case 35: +YY_RULE_SETUP +#line 97 "levcomp.lpp" +return SOUTHWEST; + YY_BREAK +case 36: +YY_RULE_SETUP +#line 99 "levcomp.lpp" +return DASH; + YY_BREAK +case 37: +YY_RULE_SETUP +#line 100 "levcomp.lpp" +return COMMA; + YY_BREAK +case 38: +YY_RULE_SETUP +#line 102 "levcomp.lpp" +{ + clean(); + yylval.i = atoi(yytext); + return INTEGER; + } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 108 "levcomp.lpp" +{ + BEGIN(INITIAL); + settext(); + return STRING; + } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 114 "levcomp.lpp" +{ BEGIN(INITIAL); } + YY_BREAK +case 41: +YY_RULE_SETUP +#line 116 "levcomp.lpp" +; + YY_BREAK +case 42: +YY_RULE_SETUP +#line 118 "levcomp.lpp" +return BAD_CHARACTER; + YY_BREAK +case 43: +YY_RULE_SETUP +#line 120 "levcomp.lpp" +ECHO; + YY_BREAK +#line 1622 "levcomp.lex.cc" + case YY_STATE_EOF(INITIAL): + case YY_STATE_EOF(MAPDEF): + case YY_STATE_EOF(ARGUMENT): + case YY_STATE_EOF(MNAME): + case YY_STATE_EOF(KEYWORDS): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + yy_current_state += YY_AT_BOL(); + yy_state_ptr = yy_state_buf; + *yy_state_ptr++ = yy_current_state; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 797 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *yy_state_ptr++ = yy_current_state; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + + register YY_CHAR yy_c = 1; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 797 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 796); + if ( ! yy_is_jam ) + *yy_state_ptr++ = yy_current_state; + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + if ( c == '\n' ) + --yylineno; + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + yy_current_buffer->yy_at_bol = (c == '\n'); + if ( yy_current_buffer->yy_at_bol ) + ++yylineno; + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +#ifndef _WIN32 +#include +#else +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif +#endif + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) +#else +YY_BUFFER_STATE yy_scan_buffer( base, size ) +char *base; +yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) +#else +YY_BUFFER_STATE yy_scan_string( yy_str ) +yyconst char *yy_str; +#endif + { + int len; + for ( len = 0; yy_str[len]; ++len ) + ; + + return yy_scan_bytes( yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE yy_scan_bytes( bytes, len ) +yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( yyconst char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 120 "levcomp.lpp" + + +int yywrap() +{ + clean(); + return 1; +} diff --git a/crawl-ref/source/prebuilt/levcomp.tab.cc b/crawl-ref/source/prebuilt/levcomp.tab.cc new file mode 100644 index 0000000000..4b4d9dc3f3 --- /dev/null +++ b/crawl-ref/source/prebuilt/levcomp.tab.cc @@ -0,0 +1,1907 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + DEFAULT_DEPTH = 258, + SYMBOL = 259, + TAGS = 260, + NAME = 261, + DEPTH = 262, + ORIENT = 263, + PLACE = 264, + CHANCE = 265, + FLAGS = 266, + MONS = 267, + ENCOMPASS = 268, + NORTH = 269, + EAST = 270, + SOUTH = 271, + WEST = 272, + NORTHEAST = 273, + SOUTHEAST = 274, + SOUTHWEST = 275, + NORTHWEST = 276, + BAD_CHARACTER = 277, + NO_HMIRROR = 278, + NO_VMIRROR = 279, + NO_ROTATE = 280, + PANDEMONIC = 281, + DASH = 282, + COMMA = 283, + INTEGER = 284, + STRING = 285, + MAP_LINE = 286, + MONSTER_NAME = 287 + }; +#endif +/* Tokens. */ +#define DEFAULT_DEPTH 258 +#define SYMBOL 259 +#define TAGS 260 +#define NAME 261 +#define DEPTH 262 +#define ORIENT 263 +#define PLACE 264 +#define CHANCE 265 +#define FLAGS 266 +#define MONS 267 +#define ENCOMPASS 268 +#define NORTH 269 +#define EAST 270 +#define SOUTH 271 +#define WEST 272 +#define NORTHEAST 273 +#define SOUTHEAST 274 +#define SOUTHWEST 275 +#define NORTHWEST 276 +#define BAD_CHARACTER 277 +#define NO_HMIRROR 278 +#define NO_VMIRROR 279 +#define NO_ROTATE 280 +#define PANDEMONIC 281 +#define DASH 282 +#define COMMA 283 +#define INTEGER 284 +#define STRING 285 +#define MAP_LINE 286 +#define MONSTER_NAME 287 + + + + +/* Copy the first part of user declarations. */ +#line 1 "levcomp.ypp" + + +#include "AppHdr.h" +#include "libutil.h" +#include "levcomp.h" + +int yylex(); + +extern int yylineno; + +void yyerror(const char *e) +{ + fprintf(stderr, "%s:%d: %s\n", lc_desfile.c_str(), yylineno, e); + // Bail bail bail. + exit(1); +} + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 21 "levcomp.ypp" +{ + int i; + const char *text; +} +/* Line 193 of yacc.c. */ +#line 184 "levcomp.tab.c" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 197 "levcomp.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 13 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 41 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 33 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 28 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 57 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 70 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 287 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 6, 9, 11, 13, 15, 18, + 23, 26, 27, 30, 32, 34, 36, 38, 40, 42, + 44, 46, 49, 50, 53, 55, 58, 60, 63, 67, + 69, 71, 74, 76, 79, 83, 85, 88, 90, 93, + 95, 97, 99, 101, 103, 105, 107, 109, 111, 114, + 115, 118, 120, 122, 124, 126, 128, 131 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 34, 0, -1, 35, -1, -1, 36, 35, -1, 37, + -1, 39, -1, 38, -1, 3, 51, -1, 40, 41, + 58, 41, -1, 6, 30, -1, -1, 42, 41, -1, + 49, -1, 50, -1, 52, -1, 53, -1, 55, -1, + 46, -1, 45, -1, 43, -1, 5, 44, -1, -1, + 30, 44, -1, 4, -1, 4, 30, -1, 12, -1, + 12, 47, -1, 48, 28, 47, -1, 48, -1, 32, + -1, 9, 30, -1, 7, -1, 7, 51, -1, 29, + 27, 29, -1, 29, -1, 10, 29, -1, 8, -1, + 8, 54, -1, 13, -1, 14, -1, 15, -1, 16, + -1, 17, -1, 18, -1, 19, -1, 20, -1, 21, + -1, 11, 56, -1, -1, 57, 56, -1, 23, -1, + 24, -1, 25, -1, 59, -1, 60, -1, 60, 59, + -1, 31, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 46, 46, 49, 50, 53, 54, 57, 60, 66, + 72, 80, 81, 84, 85, 86, 87, 88, 89, 90, + 91, 94, 97, 98, 106, 107, 113, 114, 117, 118, + 121, 134, 140, 141, 147, 152, 158, 164, 165, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 182, 185, + 186, 202, 203, 204, 207, 210, 211, 214 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "DEFAULT_DEPTH", "SYMBOL", "TAGS", + "NAME", "DEPTH", "ORIENT", "PLACE", "CHANCE", "FLAGS", "MONS", + "ENCOMPASS", "NORTH", "EAST", "SOUTH", "WEST", "NORTHEAST", "SOUTHEAST", + "SOUTHWEST", "NORTHWEST", "BAD_CHARACTER", "NO_HMIRROR", "NO_VMIRROR", + "NO_ROTATE", "PANDEMONIC", "DASH", "COMMA", "INTEGER", "STRING", + "MAP_LINE", "MONSTER_NAME", "$accept", "file", "definitions", + "definition", "def", "defdepth", "level", "name", "metalines", + "metaline", "tags", "tagstrings", "symbol", "mons", "mnames", "mname", + "place", "depth", "depth_range", "chance", "orientation", "orient_name", + "flags", "flagnames", "flagname", "map_def", "map_lines", "map_line", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 33, 34, 35, 35, 36, 36, 37, 38, 39, + 40, 41, 41, 42, 42, 42, 42, 42, 42, 42, + 42, 43, 44, 44, 45, 45, 46, 46, 47, 47, + 48, 49, 50, 50, 51, 51, 52, 53, 53, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 55, 56, + 56, 57, 57, 57, 58, 59, 59, 60 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 0, 2, 1, 1, 1, 2, 4, + 2, 0, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 0, 2, 1, 2, 1, 2, 3, 1, + 1, 2, 1, 2, 3, 1, 2, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, + 2, 1, 1, 1, 1, 1, 2, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 3, 0, 0, 0, 2, 3, 5, 7, 6, 11, + 35, 8, 10, 1, 4, 24, 22, 32, 37, 0, + 0, 49, 26, 0, 11, 20, 19, 18, 13, 14, + 15, 16, 17, 0, 25, 22, 21, 33, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 38, 31, 36, + 51, 52, 53, 48, 49, 30, 27, 29, 57, 11, + 54, 55, 12, 34, 23, 50, 0, 9, 56, 28 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 3, 4, 5, 6, 7, 8, 9, 23, 24, + 25, 36, 26, 27, 56, 57, 28, 29, 11, 30, + 31, 47, 32, 53, 54, 59, 60, 61 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -29 +static const yytype_int8 yypact[] = +{ + 16, -17, -10, 21, -29, 16, -29, -29, -29, 6, + -1, -29, -29, -29, -29, -3, -2, -17, -12, 1, + 3, 0, 2, 5, 6, -29, -29, -29, -29, -29, + -29, -29, -29, 4, -29, -2, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, 0, -29, -29, 9, -29, 6, + -29, 5, -29, -29, -29, -29, 2, -29, -29, -29 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -29, -29, 24, -29, -29, -29, -29, -29, -24, -29, + -29, -5, -29, -29, -28, -29, -29, -29, 22, -29, + -29, -29, -29, -14, -29, -29, -20, -29 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 62, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 15, 16, 10, 17, 18, 19, 20, 21, 22, 1, + 12, 13, 2, 50, 51, 52, 33, 34, 35, 14, + 64, 48, 49, 63, 55, 67, 58, 66, 69, 37, + 65, 68 +}; + +static const yytype_uint8 yycheck[] = +{ + 24, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 4, 5, 29, 7, 8, 9, 10, 11, 12, 3, + 30, 0, 6, 23, 24, 25, 27, 30, 30, 5, + 35, 30, 29, 29, 32, 59, 31, 28, 66, 17, + 54, 61 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 6, 34, 35, 36, 37, 38, 39, 40, + 29, 51, 30, 0, 35, 4, 5, 7, 8, 9, + 10, 11, 12, 41, 42, 43, 45, 46, 49, 50, + 52, 53, 55, 27, 30, 30, 44, 51, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 54, 30, 29, + 23, 24, 25, 56, 57, 32, 47, 48, 31, 58, + 59, 60, 41, 29, 44, 56, 28, 41, 59, 47 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 46 "levcomp.ypp" + { } + break; + + case 3: +#line 49 "levcomp.ypp" + {} + break; + + case 4: +#line 50 "levcomp.ypp" + {} + break; + + case 5: +#line 53 "levcomp.ypp" + {} + break; + + case 6: +#line 54 "levcomp.ypp" + {} + break; + + case 8: +#line 61 "levcomp.ypp" + { + lc_default_depth = lc_range; + } + break; + + case 9: +#line 67 "levcomp.ypp" + { + add_parsed_map( lc_map ); + } + break; + + case 10: +#line 73 "levcomp.ypp" + { + lc_map.init(); + lc_map.depth = lc_default_depth; + lc_map.name = (yyvsp[(2) - (2)].text); + } + break; + + case 21: +#line 94 "levcomp.ypp" + {} + break; + + case 23: +#line 99 "levcomp.ypp" + { + lc_map.tags += " "; + lc_map.tags += (yyvsp[(1) - (2)].text); + lc_map.tags += " "; + } + break; + + case 24: +#line 106 "levcomp.ypp" + {} + break; + + case 25: +#line 108 "levcomp.ypp" + { + lc_map.random_symbols = (yyvsp[(2) - (2)].text); + } + break; + + case 26: +#line 113 "levcomp.ypp" + {} + break; + + case 27: +#line 114 "levcomp.ypp" + {} + break; + + case 30: +#line 122 "levcomp.ypp" + { + bool recognised = lc_map.mons.add_mons((yyvsp[(1) - (1)].text)); + if (!recognised) + { + char buf[300]; + snprintf(buf, sizeof buf, "unknown monster '%s'", + (yyvsp[(1) - (1)].text)); + yyerror(buf); + } + } + break; + + case 31: +#line 135 "levcomp.ypp" + { + lc_map.place = (yyvsp[(2) - (2)].text); + } + break; + + case 32: +#line 140 "levcomp.ypp" + {} + break; + + case 33: +#line 142 "levcomp.ypp" + { + lc_map.depth = lc_range; + } + break; + + case 34: +#line 148 "levcomp.ypp" + { + lc_range.set((yyvsp[(1) - (3)].i), (yyvsp[(3) - (3)].i)); + } + break; + + case 35: +#line 153 "levcomp.ypp" + { + lc_range.set((yyvsp[(1) - (1)].i)); + } + break; + + case 36: +#line 159 "levcomp.ypp" + { + lc_map.chance = (yyvsp[(2) - (2)].i); + } + break; + + case 37: +#line 164 "levcomp.ypp" + {} + break; + + case 38: +#line 166 "levcomp.ypp" + { + lc_map.orient = (map_section_type) (yyvsp[(2) - (2)].i); + } + break; + + case 39: +#line 171 "levcomp.ypp" + { (yyval.i) = MAP_ENCOMPASS; } + break; + + case 40: +#line 172 "levcomp.ypp" + { (yyval.i) = MAP_NORTH; } + break; + + case 41: +#line 173 "levcomp.ypp" + { (yyval.i) = MAP_EAST; } + break; + + case 42: +#line 174 "levcomp.ypp" + { (yyval.i) = MAP_SOUTH; } + break; + + case 43: +#line 175 "levcomp.ypp" + { (yyval.i) = MAP_WEST; } + break; + + case 44: +#line 176 "levcomp.ypp" + { (yyval.i) = MAP_NORTHEAST; } + break; + + case 45: +#line 177 "levcomp.ypp" + { (yyval.i) = MAP_SOUTHEAST; } + break; + + case 46: +#line 178 "levcomp.ypp" + { (yyval.i) = MAP_SOUTHWEST; } + break; + + case 47: +#line 179 "levcomp.ypp" + { (yyval.i) = MAP_NORTHWEST; } + break; + + case 48: +#line 182 "levcomp.ypp" + {} + break; + + case 50: +#line 187 "levcomp.ypp" + { + switch ((yyvsp[(1) - (2)].i)) { + case NO_HMIRROR: + lc_map.flags &= ~MAPF_MIRROR_HORIZONTAL; + break; + case NO_VMIRROR: + lc_map.flags &= ~MAPF_MIRROR_VERTICAL; + break; + case NO_ROTATE: + lc_map.flags &= ~MAPF_ROTATE; + break; + } + } + break; + + case 51: +#line 202 "levcomp.ypp" + { (yyval.i) = NO_HMIRROR; } + break; + + case 52: +#line 203 "levcomp.ypp" + { (yyval.i) = NO_VMIRROR; } + break; + + case 53: +#line 204 "levcomp.ypp" + { (yyval.i) = NO_ROTATE; } + break; + + case 57: +#line 215 "levcomp.ypp" + { + lc_map.map.add_line((yyvsp[(1) - (1)].text)); + } + break; + + +/* Line 1267 of yacc.c. */ +#line 1692 "levcomp.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 220 "levcomp.ypp" + + diff --git a/crawl-ref/source/prebuilt/levcomp.tab.h b/crawl-ref/source/prebuilt/levcomp.tab.h new file mode 100644 index 0000000000..64857e6606 --- /dev/null +++ b/crawl-ref/source/prebuilt/levcomp.tab.h @@ -0,0 +1,125 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + DEFAULT_DEPTH = 258, + SYMBOL = 259, + TAGS = 260, + NAME = 261, + DEPTH = 262, + ORIENT = 263, + PLACE = 264, + CHANCE = 265, + FLAGS = 266, + MONS = 267, + ENCOMPASS = 268, + NORTH = 269, + EAST = 270, + SOUTH = 271, + WEST = 272, + NORTHEAST = 273, + SOUTHEAST = 274, + SOUTHWEST = 275, + NORTHWEST = 276, + BAD_CHARACTER = 277, + NO_HMIRROR = 278, + NO_VMIRROR = 279, + NO_ROTATE = 280, + PANDEMONIC = 281, + DASH = 282, + COMMA = 283, + INTEGER = 284, + STRING = 285, + MAP_LINE = 286, + MONSTER_NAME = 287 + }; +#endif +/* Tokens. */ +#define DEFAULT_DEPTH 258 +#define SYMBOL 259 +#define TAGS 260 +#define NAME 261 +#define DEPTH 262 +#define ORIENT 263 +#define PLACE 264 +#define CHANCE 265 +#define FLAGS 266 +#define MONS 267 +#define ENCOMPASS 268 +#define NORTH 269 +#define EAST 270 +#define SOUTH 271 +#define WEST 272 +#define NORTHEAST 273 +#define SOUTHEAST 274 +#define SOUTHWEST 275 +#define NORTHWEST 276 +#define BAD_CHARACTER 277 +#define NO_HMIRROR 278 +#define NO_VMIRROR 279 +#define NO_ROTATE 280 +#define PANDEMONIC 281 +#define DASH 282 +#define COMMA 283 +#define INTEGER 284 +#define STRING 285 +#define MAP_LINE 286 +#define MONSTER_NAME 287 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 21 "levcomp.ypp" +{ + int i; + const char *text; +} +/* Line 1529 of yacc.c. */ +#line 118 "levcomp.tab.h" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +extern YYSTYPE yylval; + diff --git a/crawl-ref/source/randart.cc b/crawl-ref/source/randart.cc index b0486fe02f..2aa804e957 100644 --- a/crawl-ref/source/randart.cc +++ b/crawl-ref/source/randart.cc @@ -3,6 +3,8 @@ * Summary: Random and unrandom artifact functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <8> 19 Jun 99 GDL added IBMCPP support @@ -20,8 +22,8 @@ #include "externs.h" #include "itemname.h" +#include "itemprop.h" #include "stuff.h" -#include "wpn-misc.h" /* The initial generation of a randart is very simple - it occurs @@ -608,7 +610,7 @@ const char *rand_armour_names[] = { not randart.h because they're only used in this code module. */ -#if defined(MAC) || defined(__IBMCPP__) || defined(__BCPLUSPLUS__) +#if defined(__IBMCPP__) #define PACKED #else #ifndef PACKED @@ -814,7 +816,7 @@ void randart_wpn_properties( const item_def &item, if (proprt[RAP_BRAND] == SPWPN_SPEED && atype == WPN_QUICK_BLADE) proprt[RAP_BRAND] = SPWPN_NORMAL; - if (launches_things(atype)) + if (is_range_weapon(item)) { proprt[RAP_BRAND] = SPWPN_NORMAL; @@ -825,12 +827,14 @@ void randart_wpn_properties( const item_def &item, proprt[RAP_BRAND] = (tmp >= 18) ? SPWPN_SPEED : (tmp >= 14) ? SPWPN_PROTECTION : (tmp >= 10) ? SPWPN_VENOM - : SPWPN_FLAME + (tmp % 2); + : SPWPN_VORPAL + random2(3); + if (proprt[RAP_BRAND] == SPWPN_VORPAL && atype == WPN_BLOWGUN) + proprt[RAP_BRAND] = SPWPN_VENOM; } } - if (is_demonic(atype)) + if (is_demonic(item)) { switch (random5(9)) { @@ -1182,11 +1186,11 @@ finished_curses: if (random5(10) == 0 && (aclass != OBJ_ARMOUR || atype != ARM_CLOAK - || !cmp_equip_race( item, ISFLAG_ELVEN )) + || get_equip_race(item) != ISFLAG_ELVEN) && (aclass != OBJ_ARMOUR || atype != ARM_BOOTS - || !cmp_equip_race( item, ISFLAG_ELVEN ) - && get_armour_ego_type( item ) != SPARM_STEALTH)) + || get_equip_race(item) != ISFLAG_ELVEN) + && get_armour_ego_type( item ) != SPARM_STEALTH) { power_level++; proprt[RAP_STEALTH] = 10 + random5(70); @@ -1239,7 +1243,7 @@ const char *randart_name( const item_def &item ) push_rng_state(); seed_rng( seed ); - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) { switch (random5(21)) { @@ -1286,7 +1290,7 @@ const char *randart_name( const item_def &item ) { char st_p2[ITEMNAME_SIZE]; - make_name(random5(250), random5(250), random5(250), 3, st_p); + make_name(random_int(), false, st_p); standard_name_weap( item.sub_type, st_p2 ); strcat(art_n, st_p2); @@ -1336,7 +1340,7 @@ const char *randart_armour_name( const item_def &item ) push_rng_state(); seed_rng( seed ); - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) { switch (random5(21)) { @@ -1382,7 +1386,7 @@ const char *randart_armour_name( const item_def &item ) { char st_p2[ITEMNAME_SIZE]; - make_name(random5(250), random5(250), random5(250), 3, st_p); + make_name(random_int(), false, st_p); standard_name_armour(item, st_p2); strcat(art_n, st_p2); if (random5(3) == 0) @@ -1432,7 +1436,7 @@ const char *randart_ring_name( const item_def &item ) push_rng_state(); seed_rng( seed ); - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) { temp_rand = random5(21); @@ -1473,7 +1477,7 @@ const char *randart_ring_name( const item_def &item ) } else { - make_name(random5(250), random5(250), random5(250), 3, st_p); + make_name(random_int(), false, st_p); strcat(art_n, (item.sub_type < AMU_RAGE) ? "ring" : "amulet"); @@ -1773,6 +1777,7 @@ void standard_name_weap(unsigned char item_typ, char glorg[ITEMNAME_SIZE]) (item_typ == WPN_HALBERD) ? "halberd" : (item_typ == WPN_SLING) ? "sling" : (item_typ == WPN_BOW) ? "bow" : + (item_typ == WPN_LONGBOW) ? "longbow" : (item_typ == WPN_BLOWGUN) ? "blowgun" : (item_typ == WPN_CROSSBOW) ? "crossbow" : (item_typ == WPN_HAND_CROSSBOW) ? "hand crossbow" : @@ -1782,6 +1787,7 @@ void standard_name_weap(unsigned char item_typ, char glorg[ITEMNAME_SIZE]) (item_typ == WPN_EVENINGSTAR) ? "eveningstar" : (item_typ == WPN_QUICK_BLADE) ? "quick blade" : (item_typ == WPN_KATANA) ? "katana" : + (item_typ == WPN_LAJATANG) ? "lajatang" : (item_typ == WPN_EXECUTIONERS_AXE) ? "executioner's axe" : (item_typ == WPN_DOUBLE_SWORD) ? "double sword" : (item_typ == WPN_TRIPLE_SWORD) ? "triple sword" : @@ -1790,13 +1796,15 @@ void standard_name_weap(unsigned char item_typ, char glorg[ITEMNAME_SIZE]) (item_typ == WPN_WHIP) ? "whip" : (item_typ == WPN_SABRE) ? "sabre" : (item_typ == WPN_DEMON_BLADE) ? "demon blade" : + (item_typ == WPN_BLESSED_BLADE)? "blessed blade" : + (item_typ == WPN_LOCHABER_AXE) ? "lochaber axe" : (item_typ == WPN_DEMON_WHIP) ? "demon whip" : (item_typ == WPN_DEMON_TRIDENT) ? "demon trident" : (item_typ == WPN_BROAD_AXE) ? "broad axe" : (item_typ == WPN_WAR_AXE) ? "war axe" : (item_typ == WPN_SPIKED_FLAIL) ? "spiked flail" : (item_typ == WPN_GREAT_MACE) ? "great mace" : - (item_typ == WPN_GREAT_FLAIL) ? "great flail" : + (item_typ == WPN_DIRE_FLAIL) ? "dire flail" : (item_typ == WPN_FALCHION) ? "falchion" : (item_typ == WPN_GIANT_CLUB) @@ -1814,7 +1822,7 @@ void standard_name_armour( const item_def &item, char glorg[ITEMNAME_SIZE] ) { short helm_type; - glorg[0] = '\0'; + glorg[0] = 0; switch (item.sub_type) { @@ -1859,8 +1867,8 @@ void standard_name_armour( const item_def &item, char glorg[ITEMNAME_SIZE] ) break; case ARM_HELMET: - if (cmp_helmet_type( item, THELM_HELM ) - || cmp_helmet_type( item, THELM_HELMET )) + if (get_helmet_type(item) == THELM_HELM + || get_helmet_type(item) == THELM_HELMET) { short dhelm = get_helmet_desc( item ); @@ -1893,13 +1901,16 @@ void standard_name_armour( const item_def &item, char glorg[ITEMNAME_SIZE] ) strcat(glorg, "gloves"); break; + case ARM_NAGA_BARDING: + strcat(glorg, "naga barding"); + break; + + case ARM_CENTAUR_BARDING: + strcat(glorg, "centaur barding"); + break; + case ARM_BOOTS: - if (item.plus2 == TBOOT_NAGA_BARDING) - strcat(glorg, "naga barding"); - else if (item.plus2 == TBOOT_CENTAUR_BARDING) - strcat(glorg, "centaur barding"); - else - strcat(glorg, "boots"); + strcat(glorg, "boots"); break; case ARM_BUCKLER: diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc index fc51bc28bd..805c652d77 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -27,6 +27,10 @@ #include #include +#ifdef DOS +#include +#endif + #include "externs.h" #include "abl-show.h" @@ -39,13 +43,17 @@ #include "food.h" #include "it_use2.h" #include "itemname.h" +#include "itemprop.h" +#include "item_use.h" #include "items.h" #include "misc.h" #include "monplace.h" #include "mutation.h" #include "newgame.h" +#include "notes.h" #include "ouch.h" #include "player.h" +#include "randart.h" #include "shopping.h" #include "skills2.h" #include "spells1.h" @@ -53,6 +61,7 @@ #include "spells3.h" #include "spl-cast.h" #include "stuff.h" +#include "view.h" const char *sacrifice[] = { " glows silver and disappears.", @@ -115,9 +124,25 @@ static void inc_gift_timeout(int val) you.gift_timeout += val; } // end inc_gift_timeout() +static int random_undead_servant(int religion) +{ + // error trapping {dlb} + int thing_called = MONS_PROGRAM_BUG; + int temp_rand = random2(100); + thing_called = ((temp_rand > 66) ? MONS_WRAITH : // 33% + (temp_rand > 52) ? MONS_WIGHT : // 12% + (temp_rand > 40) ? MONS_SPECTRAL_WARRIOR : // 16% + (temp_rand > 31) ? MONS_ROTTING_HULK : // 9% + (temp_rand > 23) ? MONS_SKELETAL_WARRIOR : // 8% + (temp_rand > 16) ? MONS_VAMPIRE : // 7% + (temp_rand > 10) ? MONS_GHOUL : // 6% + (temp_rand > 4) ? MONS_MUMMY // 6% + : MONS_FLAYED_GHOST); // 5% + return (thing_called); +} + void pray(void) { - int temp_rand = 0; unsigned char was_praying = you.duration[DUR_PRAYER]; bool success = false; @@ -128,7 +153,7 @@ void pray(void) } // all prayers take time - you.turn_is_over = 1; + you.turn_is_over = true; if (you.religion != GOD_NO_GOD && grd[you.x_pos][you.y_pos] == 179 + you.religion) @@ -155,7 +180,7 @@ void pray(void) strcat(info, "un"); strcat(info, "life."); - mpr(info); + mpr(info, MSGCH_PRAY); return; } else if (you.religion == GOD_XOM) @@ -187,7 +212,7 @@ void pray(void) strcpy( info, "You offer a prayer to " ); strcat( info, god_name( you.religion ) ); strcat( info, "." ); - mpr(info); + mpr(info, MSGCH_PRAY); you.duration[DUR_PRAYER] = 9 + (random2(you.piety) / 20) + (random2(you.piety) / 20); @@ -208,7 +233,7 @@ void pray(void) : "displeased"); strcat(info, "."); - god_speaks(you.religion, info); + mpr( info, MSGCH_PRAY, you.religion ); if (you.piety > 130) you.duration[DUR_PRAYER] *= 3; @@ -226,197 +251,222 @@ void pray(void) if (!you.penance[you.religion] && !you.gift_timeout && !was_praying) { // Remember to check for water/lava - //jmf: "good" god will sometimes feed you (a la Nethack) - if (you.religion == GOD_ZIN - && you.hunger_state == HS_STARVING - && random2(250) <= you.piety) - { - god_speaks(you.religion, "Your stomach feels content."); - set_hunger(6000, true); - lose_piety(5 + random2avg(10, 2)); - inc_gift_timeout(30 + random2avg(10, 2)); - return; - } - - if (you.religion == GOD_NEMELEX_XOBEH - && random2(200) <= you.piety - && (!you.attribute[ATTR_CARD_TABLE] || one_chance_in(3)) - && !you.attribute[ATTR_CARD_COUNTDOWN] - && grd[you.x_pos][you.y_pos] != DNGN_LAVA - && grd[you.x_pos][you.y_pos] != DNGN_DEEP_WATER) + switch (you.religion) { - int thing_created = NON_ITEM; - unsigned char gift_type = MISC_DECK_OF_TRICKS; - - if (!you.attribute[ATTR_CARD_TABLE]) - { - thing_created = items( 1, OBJ_MISCELLANY, - MISC_PORTABLE_ALTAR_OF_NEMELEX, - true, 1, 250 ); + default: + break; - if (thing_created != NON_ITEM) - you.attribute[ATTR_CARD_TABLE] = 1; - } - else + case GOD_ZIN: + //jmf: "good" god will sometimes feed you (a la Nethack) + if (you.hunger_state == HS_STARVING + && random2(250) <= you.piety) { - if (random2(200) <= you.piety && one_chance_in(4)) - gift_type = MISC_DECK_OF_SUMMONINGS; - if (random2(200) <= you.piety && coinflip()) - gift_type = MISC_DECK_OF_WONDERS; - if (random2(200) <= you.piety && one_chance_in(4)) - gift_type = MISC_DECK_OF_POWER; - - thing_created = items( 1, OBJ_MISCELLANY, gift_type, - true, 1, 250 ); + god_speaks(you.religion, "Your stomach feels content."); + set_hunger(6000, true); + lose_piety(5 + random2avg(10, 2)); + inc_gift_timeout(30 + random2avg(10, 2)); } + break; - if (thing_created != NON_ITEM) + case GOD_NEMELEX_XOBEH: + if (random2(200) <= you.piety + && (!you.attribute[ATTR_CARD_TABLE] || one_chance_in(3)) + && !you.attribute[ATTR_CARD_COUNTDOWN] + && !grid_destroys_items(grd[you.x_pos][you.y_pos])) { - move_item_to_grid( &thing_created, you.x_pos, you.y_pos ); - origin_acquired(mitm[thing_created], you.religion); - - simple_god_message(" grants you a gift!"); - more(); - canned_msg(MSG_SOMETHING_APPEARS); + int thing_created = NON_ITEM; + unsigned char gift_type = MISC_DECK_OF_TRICKS; - you.attribute[ATTR_CARD_COUNTDOWN] = 10; - inc_gift_timeout(5 + random2avg(9, 2)); - } - } + if (!you.attribute[ATTR_CARD_TABLE]) + { + thing_created = items( 1, OBJ_MISCELLANY, + MISC_PORTABLE_ALTAR_OF_NEMELEX, + true, 1, 250 ); - if ((you.religion == GOD_OKAWARU || you.religion == GOD_TROG) - && you.piety > 130 - && random2(you.piety) > 120 - && grd[you.x_pos][you.y_pos] != DNGN_LAVA - && grd[you.x_pos][you.y_pos] != DNGN_DEEP_WATER - && one_chance_in(4)) - { - if (you.religion == GOD_TROG - || (you.religion == GOD_OKAWARU && coinflip())) - { - success = acquirement(OBJ_WEAPONS, you.religion); - } - else - { - success = acquirement(OBJ_ARMOUR, you.religion); - } + if (thing_created != NON_ITEM) + you.attribute[ATTR_CARD_TABLE] = 1; + } + else + { + if (random2(200) <= you.piety && one_chance_in(4)) + gift_type = MISC_DECK_OF_SUMMONINGS; + if (random2(200) <= you.piety && coinflip()) + gift_type = MISC_DECK_OF_WONDERS; + if (random2(200) <= you.piety && one_chance_in(4)) + gift_type = MISC_DECK_OF_POWER; + + thing_created = items( 1, OBJ_MISCELLANY, gift_type, + true, 1, 250 ); + } - if (success) - { - simple_god_message(" has granted you a gift!"); - more(); + if (thing_created != NON_ITEM) + { + move_item_to_grid( &thing_created, you.x_pos, you.y_pos ); + origin_acquired(mitm[thing_created], you.religion); + + simple_god_message(" grants you a gift!"); + more(); + canned_msg(MSG_SOMETHING_APPEARS); - inc_gift_timeout(30 + random2avg(19, 2)); + you.attribute[ATTR_CARD_COUNTDOWN] = 10; + inc_gift_timeout(5 + random2avg(9, 2)); + you.num_gifts[you.religion]++; + take_note(Note(NOTE_GOD_GIFT, you.religion)); + } } - } - - if (you.religion == GOD_YREDELEMNUL - && random2(you.piety) > 80 && one_chance_in(5)) - { - int thing_called = MONS_PROGRAM_BUG; // error trapping {dlb} - - temp_rand = random2(100); - thing_called = ((temp_rand > 66) ? MONS_WRAITH : // 33% - (temp_rand > 52) ? MONS_WIGHT : // 12% - (temp_rand > 40) ? MONS_SPECTRAL_WARRIOR : // 16% - (temp_rand > 31) ? MONS_ROTTING_HULK : // 9% - (temp_rand > 23) ? MONS_SKELETAL_WARRIOR : // 8% - (temp_rand > 16) ? MONS_VAMPIRE : // 7% - (temp_rand > 10) ? MONS_GHOUL : // 6% - (temp_rand > 4) ? MONS_MUMMY // 6% - : MONS_FLAYED_GHOST); // 5% - - if (create_monster( thing_called, 0, BEH_FRIENDLY, - you.x_pos, you.y_pos, - you.pet_target, 250 ) != -1) + break; + + case GOD_OKAWARU: + case GOD_TROG: + if (you.piety > 130 + && random2(you.piety) > 120 + && !grid_destroys_items(grd[you.x_pos][you.y_pos]) + && one_chance_in(4)) { - simple_god_message(" grants you an undead servant!"); - more(); - inc_gift_timeout(4 + random2avg(7, 2)); - } - } - - if ((you.religion == GOD_KIKUBAAQUDGHA - || you.religion == GOD_SIF_MUNA - || you.religion == GOD_VEHUMET) - && you.piety > 160 && random2(you.piety) > 100) - { - unsigned int gift = NUM_BOOKS; + if (you.religion == GOD_TROG + || (you.religion == GOD_OKAWARU && coinflip())) + { + success = acquirement(OBJ_WEAPONS, you.religion); + } + else + { + success = acquirement(OBJ_ARMOUR, you.religion); + } - switch (you.religion) - { - case GOD_KIKUBAAQUDGHA: // gives death books - if (!you.had_book[BOOK_NECROMANCY]) - gift = BOOK_NECROMANCY; - else if (!you.had_book[BOOK_DEATH]) - gift = BOOK_DEATH; - else if (!you.had_book[BOOK_UNLIFE]) - gift = BOOK_UNLIFE; - else if (!you.had_book[BOOK_NECRONOMICON]) - gift = BOOK_NECRONOMICON; - break; + if (success) + { + simple_god_message(" has granted you a gift!"); + more(); - case GOD_SIF_MUNA: - gift = OBJ_RANDOM; // Sif Muna - gives any + inc_gift_timeout(30 + random2avg(19, 2)); + you.num_gifts[ you.religion ]++; + take_note(Note(NOTE_GOD_GIFT, you.religion)); + } break; + } - // Vehumet - gives conj/summ. books (higher skill first) - case GOD_VEHUMET: - if (!you.had_book[BOOK_CONJURATIONS_I]) - gift = give_first_conjuration_book(); - else if (!you.had_book[BOOK_POWER]) - gift = BOOK_POWER; - else if (!you.had_book[BOOK_ANNIHILATIONS]) - gift = BOOK_ANNIHILATIONS; // conj books - - if (you.skills[SK_CONJURATIONS] < you.skills[SK_SUMMONINGS] - || gift == NUM_BOOKS) + if (you.religion == GOD_OKAWARU + && you.piety > 80 + && random2( you.piety ) > 70 + && !grid_destroys_items( grd[you.x_pos][you.y_pos] ) + && one_chance_in(8) + && you.skills[ best_skill(SK_SLINGS, SK_RANGED_COMBAT) ] >= 9) + { + success = acquirement( OBJ_MISSILES, you.religion ); + if (success) { - if (!you.had_book[BOOK_CALLINGS]) - gift = BOOK_CALLINGS; - else if (!you.had_book[BOOK_SUMMONINGS]) - gift = BOOK_SUMMONINGS; - else if (!you.had_book[BOOK_DEMONOLOGY]) - gift = BOOK_DEMONOLOGY; // summoning bks + simple_god_message( " has granted you a gift!" ); + more(); + + inc_gift_timeout( 4 + roll_dice(2,4) ); + you.num_gifts[ you.religion ]++; + take_note(Note(NOTE_GOD_GIFT, you.religion)); } break; } + break; - if (gift != NUM_BOOKS - && (grd[you.x_pos][you.y_pos] != DNGN_LAVA - && grd[you.x_pos][you.y_pos] != DNGN_DEEP_WATER)) + case GOD_YREDELEMNUL: + if (random2(you.piety) > 80 && one_chance_in(5)) { - if (gift == OBJ_RANDOM) - success = acquirement(OBJ_BOOKS, you.religion); - else + int thing_called = random_undead_servant(GOD_YREDELEMNUL); + if (create_monster( thing_called, 0, BEH_FRIENDLY, + you.x_pos, you.y_pos, + you.pet_target, 250 ) != -1) { - int thing_created = items(1, OBJ_BOOKS, gift, true, 1, 250); - if (thing_created == NON_ITEM) - return; + simple_god_message(" grants you an undead servant!"); + more(); + inc_gift_timeout(4 + random2avg(7, 2)); + you.num_gifts[you.religion]++; + take_note(Note(NOTE_GOD_GIFT, you.religion)); + } + } + break; - move_item_to_grid( &thing_created, you.x_pos, you.y_pos ); + case GOD_KIKUBAAQUDGHA: + case GOD_SIF_MUNA: + case GOD_VEHUMET: + if (you.piety > 160 && random2(you.piety) > 100) + { + unsigned int gift = NUM_BOOKS; - if (thing_created != NON_ITEM) + switch (you.religion) + { + case GOD_KIKUBAAQUDGHA: // gives death books + if (!you.had_book[BOOK_NECROMANCY]) + gift = BOOK_NECROMANCY; + else if (!you.had_book[BOOK_DEATH]) + gift = BOOK_DEATH; + else if (!you.had_book[BOOK_UNLIFE]) + gift = BOOK_UNLIFE; + else if (!you.had_book[BOOK_NECRONOMICON]) + gift = BOOK_NECRONOMICON; + break; + + case GOD_SIF_MUNA: + gift = OBJ_RANDOM; // Sif Muna - gives any + break; + + // Vehumet - gives conj/summ. books (higher skill first) + case GOD_VEHUMET: + if (!you.had_book[BOOK_CONJURATIONS_I]) + gift = give_first_conjuration_book(); + else if (!you.had_book[BOOK_POWER]) + gift = BOOK_POWER; + else if (!you.had_book[BOOK_ANNIHILATIONS]) + gift = BOOK_ANNIHILATIONS; // conj books + + if (you.skills[SK_CONJURATIONS] < + you.skills[SK_SUMMONINGS] + || gift == NUM_BOOKS) { - success = true; - origin_acquired(mitm[thing_created], you.religion); + if (!you.had_book[BOOK_CALLINGS]) + gift = BOOK_CALLINGS; + else if (!you.had_book[BOOK_SUMMONINGS]) + gift = BOOK_SUMMONINGS; + else if (!you.had_book[BOOK_DEMONOLOGY]) + gift = BOOK_DEMONOLOGY; // summoning bks } + break; } - if (success) + if (gift != NUM_BOOKS + && !(grid_destroys_items(grd[you.x_pos][you.y_pos]))) { - simple_god_message(" has granted you a gift!"); - more(); + if (gift == OBJ_RANDOM) + success = acquirement(OBJ_BOOKS, you.religion); + else + { + int thing_created = items(1, OBJ_BOOKS, gift, true, 1, 250); + if (thing_created == NON_ITEM) + return; - inc_gift_timeout(40 + random2avg(19, 2)); - } + move_item_to_grid( &thing_created, you.x_pos, you.y_pos ); + + if (thing_created != NON_ITEM) + { + success = true; + origin_acquired(mitm[thing_created], you.religion); + } + } + + if (success) + { + simple_god_message(" has granted you a gift!"); + more(); + inc_gift_timeout(40 + random2avg(19, 2)); + you.num_gifts[ you.religion ]++; + take_note(Note(NOTE_GOD_GIFT, you.religion)); + } - // Vehumet gives books less readily - if (you.religion == GOD_VEHUMET && success) - inc_gift_timeout(10 + random2(10)); - } // end of giving book - } // end of book gods + // Vehumet gives books less readily + if (you.religion == GOD_VEHUMET && success) + inc_gift_timeout(10 + random2(10)); + } // end of giving book + } // end of book gods + break; + } } // end of gift giving } // end pray() @@ -683,7 +733,7 @@ void Xom_acts(bool niceness, int sever, bool force_sever) (temp_rand == 2) ? "\"Let's see if it's strong enough to survive yet.\"" : "You hear Xom's maniacal laughter."); - if (one_chance_in(4)) + if (one_chance_in(4) && (you.equip[EQ_WEAPON] != -1) ) dancing_weapon(100, true); // nasty, but fun else { @@ -823,11 +873,10 @@ void Xom_acts(bool niceness, int sever, bool force_sever) (temp_rand == 1) ? "Xom grants you a gift!" : "Xom's generous nature manifests itself."); - if (grd[you.x_pos][you.y_pos] == DNGN_LAVA - || grd[you.x_pos][you.y_pos] == DNGN_DEEP_WATER) - { + if (grid_destroys_items(grd[you.x_pos][you.y_pos])) { // How unfortunate. I'll bet Xom feels sorry for you. - mpr("You hear a splash."); + mprf(MSGCH_SOUND, + grid_item_destruction_message(grd[you.x_pos][you.y_pos])); } else { @@ -862,6 +911,10 @@ void Xom_acts(bool niceness, int sever, bool force_sever) (temp_rand == 1) ? "Xom grants you a demonic servitor." : "Xom opens a gate."); } + else + { + god_speaks(GOD_XOM, "You hear Xom cackling."); + } done_good = true; // well, for Xom, trying == doing {dlb} } @@ -914,12 +967,13 @@ void Xom_acts(bool niceness, int sever, bool force_sever) beam.flavour = BEAM_ELECTRICITY; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "blast of lightning"); + beam.name = "blast of lightning"; beam.colour = LIGHTCYAN; - beam.thrower = KILL_YOU; // your explosion + beam.thrower = KILL_MISC; beam.aux_source = "Xom's lightning strike"; beam.ex_size = 2; - beam.isTracer = false; + beam.is_tracer = false; + beam.is_explosion = true; explosion(beam); @@ -933,160 +987,375 @@ void Xom_acts(bool niceness, int sever, bool force_sever) } } // end "Good Things" - if (done_bad || done_good || one_chance_in(4)) - return; - else - goto okay_try_again; + if (done_bad || done_good ) + return; + if ( one_chance_in(4) ) { + god_speaks(GOD_XOM, "Xom's attention is distracted from you."); + return; + } + goto okay_try_again; } // end Xom_acts() -void done_good(char thing_done, int pgain) +// This function is the merger of done_good() and naughty(). +// Returns true if god was interested (good or bad) in conduct. +bool did_god_conduct( int thing_done, int level ) { - if (you.religion == GOD_NO_GOD) - return; + bool ret = false; + int piety_change = 0; + int penance = 0; + + if (you.religion == GOD_NO_GOD || you.religion == GOD_XOM) + return (false); switch (thing_done) { - case GOOD_KILLED_LIVING: + case DID_NECROMANCY: + case DID_UNHOLY: + case DID_ATTACK_HOLY: + switch (you.religion) + { + case GOD_ZIN: + case GOD_SHINING_ONE: + case GOD_ELYVILON: + piety_change = -level; + penance = level * ((you.religion == GOD_ZIN) ? 2 : 1); + ret = true; + break; + } + break; + + case DID_STABBING: + case DID_POISON: + if (you.religion == GOD_SHINING_ONE) + { + ret = true; + piety_change = -level; + penance = level * 2; + } + break; + + case DID_ATTACK_FRIEND: + switch (you.religion) + { + case GOD_ZIN: + case GOD_SHINING_ONE: + case GOD_ELYVILON: + case GOD_OKAWARU: + piety_change = -level; + penance = level * 3; + ret = true; + break; + } + break; + + case DID_FRIEND_DIES: + switch (you.religion) + { + case GOD_ELYVILON: + penance = level; // healer god cares more about this + // fall through + case GOD_ZIN: + case GOD_SHINING_ONE: + case GOD_OKAWARU: + piety_change = -level; + ret = true; + break; + } + break; + + case DID_DEDICATED_BUTCHERY: // aka field sacrifice switch (you.religion) { case GOD_ELYVILON: simple_god_message(" did not appreciate that!"); - naughty(NAUGHTY_KILLING, 10); + ret = true; + piety_change = -10; + penance = 10; break; - case GOD_KIKUBAAQUDGHA: - case GOD_YREDELEMNUL: - case GOD_VEHUMET: + case GOD_OKAWARU: case GOD_MAKHLEB: case GOD_TROG: - simple_god_message(" accepts your kill."); - if (random2(18 + pgain) > 5) - gain_piety(1); + simple_god_message(" accepts your offering."); + ret = true; + if (random2(level + 10) > 5) + piety_change = 1; break; } break; - case GOOD_KILLED_UNDEAD: + case DID_DEDICATED_KILL_LIVING: switch (you.religion) { - case GOD_ZIN: - case GOD_SHINING_ONE: + case GOD_ELYVILON: + simple_god_message(" did not appreciate that!"); + ret = true; + piety_change = -level; + penance = level * 2; + break; + + case GOD_KIKUBAAQUDGHA: + case GOD_YREDELEMNUL: + case GOD_OKAWARU: case GOD_VEHUMET: case GOD_MAKHLEB: - case GOD_OKAWARU: + case GOD_TROG: simple_god_message(" accepts your kill."); - if (random2(18 + pgain) > 4) - gain_piety(1); + ret = true; + if (random2(level + 18) > 5) + piety_change = 1; break; } break; - case GOOD_KILLED_DEMON: + case DID_DEDICATED_KILL_UNDEAD: switch (you.religion) { case GOD_ZIN: case GOD_SHINING_ONE: + case GOD_OKAWARU: case GOD_VEHUMET: case GOD_MAKHLEB: - case GOD_OKAWARU: simple_god_message(" accepts your kill."); - if (random2(18 + pgain) > 3) - gain_piety(1); + ret = true; + if (random2(level + 18) > 4) + piety_change = 1; break; } break; - case GOOD_KILLED_ANGEL_I: - case GOOD_KILLED_ANGEL_II: + case DID_DEDICATED_KILL_DEMON: switch (you.religion) { case GOD_ZIN: case GOD_SHINING_ONE: - case GOD_ELYVILON: - simple_god_message(" did not appreciate that!"); - naughty(NAUGHTY_ATTACK_HOLY, (you.conf ? 3 : pgain * 3)); + case GOD_OKAWARU: + simple_god_message(" accepts your kill."); + ret = true; + if (random2(level + 18) > 3) + piety_change = 1; break; } break; - case GOOD_KILLED_WIZARD: - // hooking this up, but is it too good? - // enjoy it while you can -- bwr + case DID_DEDICATED_KILL_WIZARD: if (you.religion == GOD_TROG) { - simple_god_message( " appreciates your killing of a magic user." ); - - if (random2( 5 + pgain ) > 5) - gain_piety(1); + // hooking this up, but is it too good? + // enjoy it while you can -- bwr + simple_god_message(" appreciates your killing of a magic user."); + ret = true; + if (random2(level + 10) > 5) + piety_change = 1; } break; - case GOOD_HACKED_CORPSE: // NB - pgain is you.experience_level (maybe) + // Note that Angel deaths are special, they are always noticed... + // if you or any friendly kills one you'll get the credit or the blame. + case DID_ANGEL_KILLED_BY_SERVANT: + case DID_KILL_ANGEL: switch (you.religion) { - // case GOD_KIKUBAAQUDGHA: - case GOD_OKAWARU: - case GOD_MAKHLEB: - case GOD_TROG: - simple_god_message(" accepts your offering."); - if (random2(10 + pgain) > 5) - gain_piety(1); + case GOD_ZIN: + case GOD_SHINING_ONE: + case GOD_ELYVILON: + level *= 3; + piety_change = -level; + penance = level * ((you.religion == GOD_ZIN) ? 2 : 1); + ret = true; break; - // case GOD_ZIN: - // case GOD_SHINING_ONE: - case GOD_ELYVILON: - simple_god_message(" did not appreciate that!"); + case GOD_KIKUBAAQUDGHA: + case GOD_YREDELEMNUL: + case GOD_MAKHLEB: + snprintf( info, INFO_SIZE, " accepts your %skill.", + (thing_done == DID_KILL_ANGEL) ? "" : "collateral " ); + + simple_god_message( info ); - naughty(NAUGHTY_BUTCHER, 8); + ret = true; + if (random2(level + 18) > 2) + piety_change = 1; break; } break; - case GOOD_OFFER_STUFF: - simple_god_message(" is pleased with your offering."); - - gain_piety(1); - break; - - case GOOD_SLAVES_KILL_LIVING: + // Undead slave is any friendly undead... Kiku and Yred pay attention + // to the undead and both like the death of living things. + case DID_LIVING_KILLED_BY_UNDEAD_SLAVE: switch (you.religion) { case GOD_KIKUBAAQUDGHA: case GOD_YREDELEMNUL: - case GOD_VEHUMET: simple_god_message(" accepts your slave's kill."); - - if (random2(pgain + 18) > 5) - gain_piety(1); + ret = true; + if (random2(level + 10) > 5) + piety_change = 1; break; } break; - case GOOD_SERVANTS_KILL: + // Servants are currently any friendly monster under Vehumet, or + // any god given pet for everyone else (excluding undead which are + // handled above). + case DID_LIVING_KILLED_BY_SERVANT: switch (you.religion) { + case GOD_KIKUBAAQUDGHA: // note: reapers aren't undead case GOD_VEHUMET: case GOD_MAKHLEB: simple_god_message(" accepts your collateral kill."); + ret = true; + if (random2(level + 10) > 5) + piety_change = 1; + break; + } + break; - if (random2(pgain + 18) > 5) - gain_piety(1); + case DID_UNDEAD_KILLED_BY_SERVANT: + switch (you.religion) + { + case GOD_ZIN: + case GOD_SHINING_ONE: + case GOD_VEHUMET: + case GOD_MAKHLEB: + simple_god_message(" accepts your collateral kill."); + ret = true; + if (random2(level + 10) > 5) + piety_change = 1; break; } break; - case GOOD_CARDS: + case DID_DEMON_KILLED_BY_SERVANT: switch (you.religion) { - case GOD_NEMELEX_XOBEH: - gain_piety(pgain); + case GOD_ZIN: + case GOD_SHINING_ONE: + simple_god_message(" accepts your collateral kill."); + ret = true; + if (random2(level + 10) > 5) + piety_change = 1; break; } break; - // Offering at altars is covered in another function. + + case DID_SPELL_MEMORISE: + if (you.religion == GOD_TROG) + { + penance = level * 10; + piety_change = -penance; + ret = true; + } + break; + + case DID_SPELL_CASTING: + if (you.religion == GOD_TROG) + { + piety_change = -level; + penance = level * 5; + ret = true; + } + break; + + case DID_SPELL_PRACTISE: + // Like CAST, but for skill advancement. + // Level is number of skill points gained... typically 10 * exerise, + // but may be more/less if the skill is at 0 (INT adjustment), or + // if the PC's pool is low and makes change. + if (you.religion == GOD_SIF_MUNA) + { + // Old curve: random2(12) <= spell-level, this is similar, + // but faster at low levels (to help ease things for low level + // Power averages about (level * 20 / 3) + 10 / 3 now. Also + // note that spell skill practise comes just after XP gain, so + // magical kills tend to do both at the same time (unlike melee). + // This means high level spells probably work pretty much like + // they used to (use spell, get piety). + piety_change = div_rand_round( level + 10, 80 ); +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Spell practise, level: %d, dpiety: %d", + level, piety_change); +#endif + ret = true; + } + break; + + case DID_CARDS: + if (you.religion == GOD_NEMELEX_XOBEH) + { + piety_change = level; + ret = true; + } + break; + + case DID_STIMULANTS: // unused + case DID_EAT_MEAT: // unused + case DID_CREATED_LIFE: // unused + case DID_DEDICATED_KILL_NATURAL_EVIL: // unused + case DID_NATURAL_EVIL_KILLED_BY_SERVANT: // unused + case DID_SPELL_NONUTILITY: // unused + default: + break; + } + + if (piety_change > 0) + gain_piety( piety_change ); + else + { + const int piety_loss = -piety_change; + + if (piety_loss) + { + // output guilt message: + mprf( "You feel%sguilty.", + (piety_loss == 1) ? " a little " : + (piety_loss < 5) ? " " : + (piety_loss < 10) ? " very " + : " extremely " ); + + lose_piety( piety_loss ); + } + + if (you.piety < 1) + excommunication(); + else if (penance) // only if still in religion + { + god_speaks( you.religion, + "\"You will pay for your transgression, mortal!\"" ); + + inc_penance( penance ); + } + } + +#if DEBUG_DIAGNOSTICS + if (ret) + { + static const char *conducts[] = + { + "", + "Necromancy", "Unholy", "Attack Holy", "Attack Friend", + "Friend Died", "Stab", "Poison", "Field Sacrifice", + "Kill Living", "Kill Undead", "Kill Demon", "Kill Natural Evil", + "Kill Wizard", + "Kill Priest", "Kill Angel", "Undead Slave Kill Living", + "Servant Kill Living", "Servant Kill Undead", + "Servant Kill Demon", "Servant Kill Natural Evil", + "Servant Kill Angel", + "Spell Memorise", "Spell Cast", "Spell Practise", "Spell Nonutility", + "Cards", "Stimulants", "Eat Meat", "Create Life" + }; + + mprf( MSGCH_DIAGNOSTICS, + "conduct: %s; piety: %d (%+d); penance: %d (%+d)", + conducts[thing_done], + you.piety, piety_change, you.penance[you.religion], penance ); + } -} // end done_good() +#endif + + return (ret); +} void gain_piety(char pgn) { @@ -1110,10 +1379,20 @@ void gain_piety(char pgn) } // slow down gain at upper levels of piety - if (you.piety > 199 - || (you.piety > 150 && one_chance_in(3)) - || (you.piety > 100 && one_chance_in(3))) - return; + if (you.religion != GOD_SIF_MUNA) + { + if (you.piety > 199 + || (you.piety > 150 && one_chance_in(3)) + || (you.piety > 100 && one_chance_in(3))) + return; + } + else + { + // Sif Muna has a gentler taper off because training because + // naturally slower as the player gains in spell skills. + if (you.piety > 150 && one_chance_in(5)) + return; + } int old_piety = you.piety; @@ -1121,19 +1400,20 @@ void gain_piety(char pgn) if (you.piety >= 30 && old_piety < 30) { + take_note(Note(NOTE_GOD_POWER, you.religion, 0)); switch (you.religion) { case GOD_NO_GOD: case GOD_XOM: case GOD_NEMELEX_XOBEH: - case GOD_SIF_MUNA: break; default: strcpy(info, "You can now "); strcat(info, (you.religion == GOD_ZIN || you.religion == GOD_SHINING_ONE) ? "repel the undead" : - + (you.religion == GOD_SIF_MUNA) + ? "tap ambient magical fields" : (you.religion == GOD_KIKUBAAQUDGHA) ? "recall your undead slaves" : (you.religion == GOD_YREDELEMNUL) @@ -1159,6 +1439,7 @@ void gain_piety(char pgn) if (you.piety >= 50 && old_piety < 50) { + take_note(Note(NOTE_GOD_POWER, you.religion, 1)); switch (you.religion) { case GOD_NO_GOD: @@ -1204,6 +1485,7 @@ void gain_piety(char pgn) if (you.piety >= 75 && old_piety < 75) { + take_note(Note(NOTE_GOD_POWER, you.religion, 2)); switch (you.religion) { case GOD_NO_GOD: @@ -1242,6 +1524,7 @@ void gain_piety(char pgn) if (you.piety >= 100 && old_piety < 100) { + take_note(Note(NOTE_GOD_POWER, you.religion, 3)); switch (you.religion) { case GOD_NO_GOD: @@ -1249,6 +1532,7 @@ void gain_piety(char pgn) case GOD_OKAWARU: case GOD_NEMELEX_XOBEH: case GOD_KIKUBAAQUDGHA: + case GOD_VEHUMET: break; case GOD_SIF_MUNA: simple_god_message @@ -1265,8 +1549,6 @@ void gain_piety(char pgn) ? "hurl bolts of divine anger" : (you.religion == GOD_YREDELEMNUL) ? "drain ambient lifeforce" : - (you.religion == GOD_VEHUMET) - ? "tap ambient magical fields" : (you.religion == GOD_MAKHLEB) ? "hurl Makhleb's greater destruction" : (you.religion == GOD_TROG) @@ -1284,6 +1566,7 @@ void gain_piety(char pgn) if (you.piety >= 120 && old_piety < 120) { + take_note(Note(NOTE_GOD_POWER, you.religion, 4)); switch (you.religion) { case GOD_NO_GOD: @@ -1319,159 +1602,13 @@ void gain_piety(char pgn) break; } } -} // end gain_piety() - -void naughty(char type_naughty, int naughtiness) -{ - int penance = 0; - int piety_loss = 0; - - // if you currently worship no deity in particular, exit function {dlb} - if (you.religion == GOD_NO_GOD) - return; - - switch (you.religion) - { - case GOD_ZIN: - switch (type_naughty) - { - case NAUGHTY_NECROMANCY: - case NAUGHTY_UNHOLY: - case NAUGHTY_ATTACK_HOLY: - piety_loss = naughtiness; - penance = piety_loss * 2; - break; - case NAUGHTY_ATTACK_FRIEND: - piety_loss = naughtiness; - penance = piety_loss * 3; - break; - case NAUGHTY_FRIEND_DIES: - piety_loss = naughtiness; - break; - case NAUGHTY_BUTCHER: - piety_loss = naughtiness; - if (one_chance_in(3)) - penance = piety_loss; - break; - } - break; - - case GOD_SHINING_ONE: - switch (type_naughty) - { - case NAUGHTY_NECROMANCY: - case NAUGHTY_UNHOLY: - case NAUGHTY_ATTACK_HOLY: - piety_loss = naughtiness; - penance = piety_loss; - break; - case NAUGHTY_ATTACK_FRIEND: - piety_loss = naughtiness; - penance = piety_loss * 3; - break; - case NAUGHTY_FRIEND_DIES: - piety_loss = naughtiness; - break; - case NAUGHTY_BUTCHER: - piety_loss = naughtiness; - if (one_chance_in(3)) - penance = piety_loss; - break; - case NAUGHTY_STABBING: - piety_loss = naughtiness; - if (one_chance_in(5)) // can be accidental so we're nice here - penance = piety_loss; - break; - case NAUGHTY_POISON: - piety_loss = naughtiness; - penance = piety_loss * 2; - break; - } - break; - - case GOD_ELYVILON: - switch (type_naughty) - { - case NAUGHTY_NECROMANCY: - case NAUGHTY_UNHOLY: - case NAUGHTY_ATTACK_HOLY: - piety_loss = naughtiness; - penance = piety_loss; - break; - case NAUGHTY_KILLING: - piety_loss = naughtiness; - penance = piety_loss * 2; - break; - case NAUGHTY_ATTACK_FRIEND: - piety_loss = naughtiness; - penance = piety_loss * 3; - break; - // Healer god gets a bit more upset since you should have - // used your healing powers to save them. - case NAUGHTY_FRIEND_DIES: - piety_loss = naughtiness; - penance = piety_loss; - break; - case NAUGHTY_BUTCHER: - piety_loss = naughtiness; - if (one_chance_in(3)) - penance = piety_loss; - break; - } - break; - - case GOD_OKAWARU: - switch (type_naughty) - { - case NAUGHTY_ATTACK_FRIEND: - piety_loss = naughtiness; - penance = piety_loss * 3; - break; - case NAUGHTY_FRIEND_DIES: - piety_loss = naughtiness; - break; - } - break; - - case GOD_TROG: - switch (type_naughty) - { - case NAUGHTY_SPELLCASTING: - piety_loss = naughtiness; - // This penance isn't so bad since its much easier to - // gain piety with Trog than the other gods in this function. - penance = piety_loss * 10; - break; - } - break; - } - - // exit function early iff piety loss is zero: - if (piety_loss < 1) - return; - // output guilt message: - strcpy(info, "You feel"); + if ( you.piety > 160 && old_piety <= 160 && + (you.religion == GOD_SHINING_ONE || you.religion == GOD_ZIN) && + you.num_gifts[you.religion] == 0 ) + simple_god_message( " will now bless your weapon at an altar...once."); - strcat(info, (piety_loss == 1) ? " a little " : - (piety_loss < 5) ? " " : - (piety_loss < 10) ? " very " - : " extremely "); - - strcat(info, "guilty."); - mpr(info); - - lose_piety(piety_loss); - - if (you.piety < 1) - excommunication(); - else if (penance) // Don't bother unless we're not kicking them out - { - //jmf: FIXME: add randomness to following message: - god_speaks(you.religion, "\"You will pay for your transgression, mortal!\""); - inc_penance(penance); - } -} // end naughty() +} // end gain_piety() void lose_piety(char pgn) { @@ -1487,6 +1624,11 @@ void lose_piety(char pgn) // are withheld. if (!player_under_penance() && you.piety != old_piety) { + if (you.piety <= 160 && old_piety > 160 && + (you.religion == GOD_SHINING_ONE || you.religion == GOD_ZIN) && + you.num_gifts[you.religion] == 0) + simple_god_message(" is no longer ready to bless your weapon."); + if (you.piety < 120 && old_piety >= 120) { switch (you.religion) @@ -1534,6 +1676,7 @@ void lose_piety(char pgn) case GOD_OKAWARU: case GOD_NEMELEX_XOBEH: case GOD_KIKUBAAQUDGHA: + case GOD_VEHUMET: break; case GOD_SIF_MUNA: god_speaks(you.religion,"Sif Muna is no longer protecting you from miscast magic."); @@ -1549,8 +1692,6 @@ void lose_piety(char pgn) ? "hurl bolts of divine anger" : (you.religion == GOD_YREDELEMNUL) ? "drain ambient life force" : - (you.religion == GOD_VEHUMET) - ? "tap ambient magical fields" : (you.religion == GOD_MAKHLEB) ? "direct Makhleb's greater destructive powers" : (you.religion == GOD_TROG) @@ -1576,7 +1717,7 @@ void lose_piety(char pgn) case GOD_TROG: break; case GOD_VEHUMET: - simple_god_message(" will longer shield you from summoned creatures."); + simple_god_message(" will no longer shield you from summoned creatures."); break; default: strcpy(info, "You can no longer "); @@ -1654,7 +1795,6 @@ void lose_piety(char pgn) case GOD_NO_GOD: case GOD_XOM: case GOD_NEMELEX_XOBEH: - case GOD_SIF_MUNA: break; default: strcpy(info, "You can no longer "); @@ -1666,6 +1806,8 @@ void lose_piety(char pgn) ? "recall your undead slaves" : (you.religion == GOD_YREDELEMNUL) ? "animate corpses" : + (you.religion == GOD_SIF_MUNA) + ? "tap ambient magical fields" : (you.religion == GOD_VEHUMET) ? "gain power from killing in Vehumet's name" : (you.religion == GOD_MAKHLEB) @@ -1747,6 +1889,10 @@ void divine_retribution( int god ) { simple_god_message( " sends the divine host to punish you for your evil ways!", god ); } + else + { + simple_god_message("'s divine host fails to appear.", god); + } } else { @@ -1768,8 +1914,9 @@ void divine_retribution( int god ) dec_penance( GOD_SHINING_ONE, 1 ); } } + break; } - break; + return; case GOD_ZIN: // angels/creeping doom theme: @@ -1795,6 +1942,10 @@ void divine_retribution( int god ) { simple_god_message(" sends the divine host to punish you for your evil ways!", god); } + else + { + simple_god_message("'s divine host fails to appear.", god); + } } else { @@ -1802,8 +1953,9 @@ void divine_retribution( int god ) summon_swarm( you.experience_level * 20, true, false ); simple_god_message(" sends a plague down upon you!", god); } + break; } - break; + return; case GOD_MAKHLEB: // demonic servant theme @@ -1816,6 +1968,10 @@ void divine_retribution( int god ) simple_god_message(" sends a greater servant after you!", god); } + else + { + simple_god_message("'s greater servant is unavoidably detained.", god); + } } else { @@ -1833,6 +1989,8 @@ void divine_retribution( int god ) if (success) simple_god_message(" sends minions to punish you.", god); + else + simple_god_message("'s minions fail to arrive.", god); } break; @@ -1854,6 +2012,8 @@ void divine_retribution( int god ) if (success) simple_god_message(" unleashes Death upon you!", god); + else + god_speaks(god, "Death has been delayed...for now."); } else { @@ -1895,6 +2055,8 @@ void divine_retribution( int god ) if (success) simple_god_message(" sends a servant to punish you.", god); + else + simple_god_message("'s servant fails to arrive.", god); } else { @@ -1978,6 +2140,8 @@ void divine_retribution( int god ) if (success) simple_god_message(" sends monsters to punish you.", god); + else + simple_god_message(" has no time to punish you...now.", god); } break; @@ -2064,20 +2228,22 @@ void divine_retribution( int god ) if (success) simple_god_message(" sends forces against you!", god); + else + simple_god_message("'s forces are busy with other wars.", god); } break; case GOD_VEHUMET: // conjuration and summoning theme - simple_god_message("'s vengence finds you.", god); + simple_god_message("'s vengeance finds you.", god); miscast_effect( coinflip() ? SPTYP_CONJURATION : SPTYP_SUMMONING, 8 + you.experience_level, random2avg(98, 3), 100, "the wrath of Vehumet" ); break; case GOD_NEMELEX_XOBEH: - // like Xom, this might actually help the player -- bwr` - simple_god_message(" makes you to draw from the Deck of Punishment.", + // like Xom, this might actually help the player -- bwr + simple_god_message(" makes you draw from the Deck of Punishment.", god); deck_of_cards(DECK_OF_PUNISHMENT); break; @@ -2102,7 +2268,7 @@ void divine_retribution( int god ) case 5: case 6: - miscast_effect(SK_DIVINATIONS, 9, 90, 100, "the will of Sif Muna"); + miscast_effect(SPTYP_DIVINATION, 9, 90, 100, "the will of Sif Muna"); break; case 7: @@ -2130,11 +2296,13 @@ void divine_retribution( int god ) return; } - // Sometimes divine experiences are overwelming... + // Sometimes divine experiences are overwhelming... if (one_chance_in(5) && you.experience_level < random2(37)) { - if (coinflip()) + if (coinflip()) { + mpr( "The divine experience confuses you!", MSGCH_WARN); confuse_player( 3 + random2(10) ); + } else { if (you.slow < 90) @@ -2154,6 +2322,8 @@ void excommunication(void) { const int old_god = you.religion; + take_note(Note(NOTE_LOSE_GOD, old_god)); + you.duration[DUR_PRAYER] = 0; you.religion = GOD_NO_GOD; you.piety = 0; @@ -2222,6 +2392,44 @@ void excommunication(void) } } // end excommunication() +static bool bless_weapon( int god, int brand, int colour ) +{ + const int wpn = get_player_wielded_weapon(); + + // Assuming the type of weapon is correct, we only need to check + // to see if it's an artefact we can successfully clobber: + if (!is_fixed_artefact( you.inv[wpn] ) + && !is_random_artefact( you.inv[wpn] )) + { + you.duration[DUR_WEAPON_BRAND] = 0; // just in case + + set_equip_desc( you.inv[wpn], ISFLAG_GLOWING ); + set_item_ego_type( you.inv[wpn], OBJ_WEAPONS, brand ); + you.inv[wpn].colour = colour; + + do_uncurse_item( you.inv[wpn] ); + enchant_weapon( ENCHANT_TO_HIT, true ); + enchant_weapon( ENCHANT_TO_DAM, true ); + + you.wield_change = true; + you.num_gifts[god]++; + take_note(Note(NOTE_GOD_GIFT, you.religion)); + + you.flash_colour = colour; + viewwindow( true, false ); + + mprf( MSGCH_GOD, "Your weapon shines brightly!" ); + simple_god_message( " booms: Use this gift wisely!" ); + + // as currently only Zin and TSO do this is our permabrand effect: + holy_word( 100, true ); + delay(1000); + + return (true); + } + + return (false); +} void altar_prayer(void) { @@ -2239,9 +2447,48 @@ void altar_prayer(void) mpr( "You kneel at the altar and pray." ); - if (you.religion == GOD_SHINING_ONE || you.religion == GOD_XOM) + if (you.religion == GOD_XOM) return; + // TSO blesses long swords with holy wrath + if (you.religion == GOD_SHINING_ONE + && !you.num_gifts[GOD_SHINING_ONE] + && !player_under_penance() + && you.piety > 160) + { + const int wpn = get_player_wielded_weapon(); + + if (wpn != -1 + && weapon_skill( you.inv[wpn] ) == SK_LONG_SWORDS + && get_weapon_brand( you.inv[wpn] ) != SPWPN_HOLY_WRATH) + { + if (bless_weapon( GOD_SHINING_ONE, SPWPN_HOLY_WRATH, YELLOW )) + { + // convert those demon blades if blessed: + if (you.inv[wpn].sub_type == WPN_DEMON_BLADE) + you.inv[wpn].sub_type = WPN_BLESSED_BLADE; + } + } + } + + // Zin blesses maces with disruption + if (you.religion == GOD_ZIN + && !you.num_gifts[GOD_ZIN] + && !player_under_penance() + && you.piety > 160) + { + const int wpn = get_player_wielded_weapon(); + + if (wpn != -1 + && (you.inv[wpn].base_type == OBJ_WEAPONS + && (you.inv[wpn].sub_type == WPN_MACE + || you.inv[wpn].sub_type == WPN_GREAT_MACE)) + && get_weapon_brand( you.inv[wpn] ) != SPWPN_DISRUPTION) + { + bless_weapon( GOD_ZIN, SPWPN_DISRUPTION, WHITE ); + } + } + i = igrd[you.x_pos][you.y_pos]; while (i != NON_ITEM) { @@ -2263,8 +2510,12 @@ void altar_prayer(void) strcat(info, sacrifice[you.religion - 1]); mpr(info); +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Sacrifice item value: %d", value); +#endif if (mitm[i].base_type == OBJ_CORPSES || random2(value) >= 50 + || (you.religion == GOD_NEMELEX_XOBEH && one_chance_in(50)) || player_under_penance()) { gain_piety(1); @@ -2385,6 +2636,8 @@ void god_pitch(unsigned char which_god) if (you.worshipped[you.religion] < 100) you.worshipped[you.religion]++; + take_note(Note(NOTE_GET_GOD, you.religion)); + // Currently penance is just zeroed, this could be much more interesting. you.penance[you.religion] = 0; @@ -2410,7 +2663,7 @@ void offer_corpse(int corpse) strcat(info, sacrifice[you.religion - 1]); mpr(info); - done_good(GOOD_HACKED_CORPSE, 10); + did_god_conduct(DID_DEDICATED_BUTCHERY, 10); } // end offer_corpse() //jmf: moved stuff from items::handle_time() @@ -2507,8 +2760,17 @@ void handle_god_time(void) break; case GOD_SIF_MUNA: - if (one_chance_in(20)) + // [dshaligram] Sif Muna is now very patient - has to be + // to make up for the new spell training requirements, else + // it's practically impossible to get Master of Arcane status. + if (one_chance_in(100)) + { lose_piety(1); +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, + "Sif Muna piety decay (new piety = %d)", you.piety); +#endif + } if (you.piety < 1) excommunication(); break; diff --git a/crawl-ref/source/religion.h b/crawl-ref/source/religion.h index 7ca62f4e03..0e4f65a7cf 100644 --- a/crawl-ref/source/religion.h +++ b/crawl-ref/source/religion.h @@ -3,6 +3,8 @@ * Summary: Misc religion related functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -14,103 +16,20 @@ #include "enum.h" -// last updated 03jun2000 {dlb} -/* *********************************************************************** - * called from: ouch - religion - * *********************************************************************** */ void simple_god_message( const char *event, int which_deity = GOD_NO_GOD ); - - -// last updated 11jan2001 {mv} -/* *********************************************************************** - * called from: chardump - overmap - religion - * *********************************************************************** */ char *god_name(int which_god,bool long_name=false); //mv - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: religion - spell - * *********************************************************************** */ void dec_penance(int val); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: acr - decks - fight - player - religion - spell - * *********************************************************************** */ +void dec_penance(int god, int val); void Xom_acts(bool niceness, int sever, bool force_sever); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: beam - decks - fight - religion - * *********************************************************************** */ -void done_good(char thing_done, int pgain); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: ability - religion - * *********************************************************************** */ +bool did_god_conduct(int thing_done, int pgain); void excommunication(void); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: acr - religion - spell - * *********************************************************************** */ void gain_piety(char pgn); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: spell - religion - * *********************************************************************** */ void god_speaks( int god, const char *mesg ); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: ability - religion - * *********************************************************************** */ void lose_piety(char pgn); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: acr - beam - fight - it_use2 - item_use - religion - spell - - * spellbook - spells4 - * *********************************************************************** */ -void naughty(char type_naughty, int naughtiness); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: food - * *********************************************************************** */ void offer_corpse(int corpse); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: acr - * *********************************************************************** */ void pray(void); - - -// last updated 24may2000 {dlb} -/* *********************************************************************** - * called from: items - * *********************************************************************** */ void handle_god_time(void); - -// created 5jan2001 {mv} -/* *********************************************************************** - * called from: message, describe - * *********************************************************************** */ char god_colour(char god); - void god_pitch(unsigned char which_god); #endif diff --git a/crawl-ref/source/shopping.cc b/crawl-ref/source/shopping.cc index dee10a8d8c..6f80839df1 100644 --- a/crawl-ref/source/shopping.cc +++ b/crawl-ref/source/shopping.cc @@ -3,6 +3,8 @@ * Summary: Shop keeper functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <3> Jul 30 00 JDJ in_a_shop uses shoppy instead of i when calling shop_set_id. @@ -28,7 +30,9 @@ #include "invent.h" #include "items.h" #include "itemname.h" +#include "itemprop.h" #include "macro.h" +#include "notes.h" #include "player.h" #include "randart.h" #include "spl-book.h" @@ -44,6 +48,66 @@ static void shop_set_ident_type(int i, id_fix_arr &shop_id, unsigned char base_type, unsigned char sub_type); static void shop_uninit_id(int i, const id_fix_arr &shop_id); +static std::string hyphenated_suffix(char prev, char last) +{ + std::string s; + if (prev > last + 2) + s += "-"; + else if (prev == last + 2) + s += (char) (last + 1); + + if (prev != last) + s += prev; + return (s); +} + +static std::string purchase_keys(const std::string &s) +{ + if (s.empty()) + return ""; + + std::string list = "" + s.substr(0, 1); + char last = s[0]; + for (int i = 1; i < (int) s.length(); ++i) + { + if (s[i] == s[i - 1] + 1) + continue; + + char prev = s[i - 1]; + list += hyphenated_suffix(prev, last); + list += (last = s[i]); + } + + list += hyphenated_suffix( s[s.length() - 1], last ); + list += ""; + return (list); +} + +static void list_shop_keys(const std::string &purchasable) +{ + char buf[200]; + gotoxy(1, 23); + + std::string pkeys = purchase_keys(purchasable); + if (pkeys.length()) + pkeys = "[" + pkeys + "] Buy Item"; + + snprintf(buf, sizeof buf, + "[x/Esc] Exit [v] Examine Items %s", + pkeys.c_str()); + + formatted_string fs = formatted_string::parse_string(buf); + fs.cprintf("%*s", get_number_of_cols() - fs.length(), ""); + fs.display(); + gotoxy(1, 24); + + fs = formatted_string::parse_string( + "[?/*] Inventory " + "[\\] Known Items"); + fs.cprintf("%*s", get_number_of_cols() - fs.length(), ""); + fs.display(); +} + char in_a_shop( char shoppy, id_arr id ) { // easier to work with {dlb} @@ -56,6 +120,7 @@ char in_a_shop( char shoppy, id_arr id ) unsigned int gp_value = 0; char i; unsigned char ft; + std::string purchasable; #ifdef DOS_TERM char buffer[4800]; @@ -79,6 +144,8 @@ char in_a_shop( char shoppy, id_arr id ) shop_print(info, 20); more3(); + + activate_notes(false); /* should do a better job here */ shop_init_id(shoppy, shop_id); /* ************************************* @@ -119,22 +186,31 @@ char in_a_shop( char shoppy, id_arr id ) itty = igrd[0][5 + shoppy]; + purchasable.clear(); for (i = 1; i < 18; i++) { - gotoxy(1, i); + const char c = i + 96; - textcolor((i % 2) ? WHITE : LIGHTGREY); - - it_name(itty, DESC_NOCAP_A, st_pass); - putch(i + 96); - cprintf(" - "); - cprintf(st_pass); + gotoxy(1, i); gp_value = greedy * item_value( mitm[itty], id ); gp_value /= 10; if (gp_value <= 1) gp_value = 1; + bool can_afford = (you.gold >= gp_value); + textcolor( can_afford ? LIGHTGREEN : LIGHTRED ); + + if (can_afford) + purchasable += c; + + cprintf("%c - ", c); + + textcolor((i % 2) ? WHITE : LIGHTGREY); + + it_name(itty, DESC_NOCAP_A, st_pass); + cprintf("%s", st_pass); + std::string desc; if (is_dumpable_artifact(mitm[itty], Options.verbose_dump)) desc = munge_description(get_item_description(mitm[itty], @@ -145,10 +221,9 @@ char in_a_shop( char shoppy, id_arr id ) # endif gotoxy(60, i); - // cdl - itoa(gp_value, st_pass, 10); + textcolor( can_afford ? LIGHTGREEN : LIGHTRED ); snprintf(st_pass, sizeof(st_pass), "%5d", gp_value); - cprintf(st_pass); - cprintf(" gold"); + cprintf("%s gold", st_pass); if (mitm[itty].link == NON_ITEM) break; @@ -157,7 +232,7 @@ char in_a_shop( char shoppy, id_arr id ) textcolor(LIGHTGREY); - shop_print("Type letter to buy item, x/Esc to leave, ?/* for inventory, v to examine.", 23); + list_shop_keys(purchasable); purchase: snprintf( info, INFO_SIZE, "You have %d gold piece%s.", you.gold, @@ -167,7 +242,11 @@ char in_a_shop( char shoppy, id_arr id ) shop_print(info, 19); textcolor(CYAN); - shop_print("What would you like to purchase?", 20); + + snprintf(st_pass, sizeof st_pass, + "What would you like to %s?", + purchasable.length()? "purchase" : "do"); + shop_print(st_pass, 20); textcolor(LIGHTGREY); ft = get_ch(); @@ -175,6 +254,15 @@ char in_a_shop( char shoppy, id_arr id ) if (ft == 'x' || ft == ESCAPE) goto goodbye; + if (ft == '\\') + { + shop_uninit_id(shoppy, shop_id); + check_item_knowledge(); + shop_init_id(shoppy, shop_id); + + goto print_stock; + } + if (ft == 'v') { textcolor(CYAN); @@ -262,6 +350,7 @@ char in_a_shop( char shoppy, id_arr id ) #endif shop_uninit_id( shoppy, shop_id ); + activate_notes(true); return 0; } @@ -347,7 +436,7 @@ void shop_print( const char *shoppy, char sh_lines ) { gotoxy(1, sh_lines); - cprintf(shoppy); + cprintf("%s", shoppy); for (int i = strlen(shoppy); i < 80; i++) cprintf(" "); @@ -371,6 +460,21 @@ static void purchase( int shop, int item_got, int cost ) you.gold -= cost; origin_purchased(mitm[item_got]); + + if ( fully_identified(mitm[item_got]) && + is_interesting_item(mitm[item_got]) ) { + + activate_notes(true); + + char buf[ITEMNAME_SIZE]; + char buf2[ITEMNAME_SIZE]; + item_name( mitm[item_got], DESC_NOCAP_A, buf ); + strcpy(buf2, origin_desc(mitm[item_got]).c_str()); + take_note(Note(NOTE_ID_ITEM, 0, 0, buf, buf2)); + + activate_notes(false); + } + int num = move_item_to_player( item_got, mitm[item_got].quantity, true ); // Shopkeepers will now place goods you can't carry outside the shop. @@ -498,7 +602,6 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) item.flags = (ident) ? (item.flags | ISFLAG_IDENT_MASK) : (item.flags); int valued = 0; - int charge_value = 0; switch (item.base_type) { @@ -618,6 +721,7 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) break; case WPN_LONG_SWORD: + case WPN_LONGBOW: case WPN_SCIMITAR: valued += 45; break; @@ -647,8 +751,9 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) valued += 65; break; - case WPN_GREAT_FLAIL: - valued += 75; + case WPN_DIRE_FLAIL: + case WPN_LOCHABER_AXE: + valued += 90; break; case WPN_EVENINGSTAR: @@ -660,22 +765,24 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) break; case WPN_DOUBLE_SWORD: - valued += 200; + valued += 100; break; case WPN_DEMON_WHIP: - valued += 230; + valued += 130; break; case WPN_QUICK_BLADE: case WPN_DEMON_TRIDENT: - valued += 250; + valued += 150; break; case WPN_KATANA: case WPN_TRIPLE_SWORD: case WPN_DEMON_BLADE: - valued += 300; + case WPN_BLESSED_BLADE: + case WPN_LAJATANG: + valued += 200; break; } @@ -737,15 +844,15 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) } // elf/dwarf - if (cmp_equip_race( item, ISFLAG_ELVEN ) - || cmp_equip_race( item, ISFLAG_DWARVEN )) + if (get_equip_race(item) == ISFLAG_ELVEN + || get_equip_race(item) == ISFLAG_DWARVEN) { valued *= 12; valued /= 10; } // value was "6" but comment read "orc", so I went with comment {dlb} - if (cmp_equip_race( item, ISFLAG_ORCISH )) + if (get_equip_race(item) == ISFLAG_ORCISH) { valued *= 8; valued /= 10; @@ -795,7 +902,7 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) valued += 50; } else if (item_ident( item, ISFLAG_KNOW_TYPE ) - && !cmp_equip_desc( item, 0 )) + && get_equip_desc(item) != 0) { valued += 20; } @@ -823,7 +930,7 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) case MI_DART: case MI_LARGE_ROCK: case MI_STONE: - case MI_EGGPLANT: + case MI_NONE: valued++; break; case MI_ARROW: @@ -892,6 +999,8 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) break; case ARM_BANDED_MAIL: + case ARM_CENTAUR_BARDING: + case ARM_NAGA_BARDING: valued += 150; break; @@ -1006,14 +1115,14 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) valued /= 10; } - if (cmp_equip_race( item, ISFLAG_ELVEN ) - || cmp_equip_race( item, ISFLAG_DWARVEN )) + if (get_equip_race(item) == ISFLAG_ELVEN + || get_equip_race(item) == ISFLAG_DWARVEN) { valued *= 12; valued /= 10; } - if (cmp_equip_race( item, ISFLAG_ORCISH )) + if (get_equip_race(item) == ISFLAG_ORCISH) { valued *= 8; valued /= 10; @@ -1046,7 +1155,7 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) valued += 50; } else if (item_ident( item, ISFLAG_KNOW_TYPE ) - && !cmp_equip_desc( item, 0 )) + && get_equip_desc(item) != 0) { valued += 20; } @@ -1059,100 +1168,72 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) break; case OBJ_WANDS: - charge_value = 0; - if (id[0][item.sub_type]) + if (!id[ IDTYPE_WANDS ][item.sub_type]) + valued += 200; + else { switch (item.sub_type) { - case WAND_FIREBALL: - case WAND_LIGHTNING: - valued += 20; - charge_value += 5; - break; - - case WAND_DRAINING: - valued += 20; - charge_value += 4; - break; - - case WAND_DISINTEGRATION: - valued += 17; - charge_value += 4; + case WAND_HASTING: + case WAND_HEALING: + valued += 300; break; - case WAND_POLYMORPH_OTHER: - valued += 15; - charge_value += 4; + case WAND_TELEPORTATION: + valued += 250; break; case WAND_COLD: - case WAND_ENSLAVEMENT: case WAND_FIRE: - case WAND_HASTING: - valued += 15; - charge_value += 3; + case WAND_FIREBALL: + valued += 200; break; case WAND_INVISIBILITY: - valued += 15; - charge_value += 2; - break; - - case WAND_RANDOM_EFFECTS: - valued += 13; - charge_value += 3; - break; - - case WAND_PARALYSIS: - valued += 12; - charge_value += 3; + case WAND_DRAINING: + case WAND_LIGHTNING: + valued += 175; break; - case WAND_SLOWING: - valued += 10; - charge_value += 3; + case WAND_DISINTEGRATION: + valued += 120; break; - case WAND_CONFUSION: case WAND_DIGGING: - case WAND_TELEPORTATION: - valued += 10; - charge_value += 2; - break; - - case WAND_HEALING: - valued += 7; - charge_value += 3; + valued += 100; break; case WAND_FLAME: case WAND_FROST: - valued += 5; - charge_value += 2; + case WAND_PARALYSIS: + valued += 75; break; - case WAND_MAGIC_DARTS: - valued += 3; - charge_value++; + case WAND_ENSLAVEMENT: + case WAND_POLYMORPH_OTHER: + valued += 63; break; - default: // no default charge_value ??? 15jan2000 {dlb} - valued += 10; + case WAND_SLOWING: + valued += 50; + break; + + case WAND_CONFUSION: + case WAND_MAGIC_DARTS: + case WAND_RANDOM_EFFECTS: + default: + valued += 45; break; } if (item_ident( item, ISFLAG_KNOW_PLUSES )) { - valued += item.plus * charge_value; + if (item.plus == 0) + valued -= 50; + else + valued = (valued * (item.plus + 45)) / 50; } - - valued *= 3; - - if (item.plus == 0) - valued = 3; // change if wands are rechargeable! } - else - valued = 35; // = 10; break; case OBJ_POTIONS: @@ -1382,6 +1463,7 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) break; case RING_SUSTAIN_ABILITIES: case RING_SUSTENANCE: + case RING_TELEPORTATION: // usually cursed valued += 25; break; case RING_SEE_INVISIBLE: @@ -1394,9 +1476,6 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) case RING_STRENGTH: valued += 10; break; - case RING_TELEPORTATION: - valued -= 10; - break; case RING_HUNGER: valued -= 50; break; @@ -1503,7 +1582,7 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) break; case OBJ_STAVES: - if (item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (!item_ident( item, ISFLAG_KNOW_TYPE )) valued = 120; else if (item.sub_type == STAFF_SMITING || item.sub_type == STAFF_STRIKING @@ -1514,6 +1593,10 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) } else valued = 250; + + if (item_is_rod( item ) && item_ident( item, ISFLAG_KNOW_PLUSES )) + valued += 50 * (item.plus2 / ROD_CHARGE_MULT); + break; case OBJ_ORBS: @@ -1593,8 +1676,10 @@ const char *shop_name(int sx, int sy) char st_p[ITEMNAME_SIZE]; - make_name( cshop->keeper_name[0], cshop->keeper_name[1], - cshop->keeper_name[2], 3, st_p ); + unsigned long seed = static_cast( cshop->keeper_name[0] ) + | (static_cast( cshop->keeper_name[1] ) << 8) + | (static_cast( cshop->keeper_name[1] ) << 16); + make_name( seed, false, st_p ); strcpy(sh_name, st_p); strcat(sh_name, "'s "); @@ -1630,3 +1715,4 @@ const char *shop_name(int sx, int sy) return (sh_name); } + diff --git a/crawl-ref/source/shopping.h b/crawl-ref/source/shopping.h index 8891f16eb9..0de4edb2bd 100644 --- a/crawl-ref/source/shopping.h +++ b/crawl-ref/source/shopping.h @@ -3,6 +3,8 @@ * Summary: Shop keeper functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created diff --git a/crawl-ref/source/skills.cc b/crawl-ref/source/skills.cc index 9dc63d54f0..3bb6a54119 100644 --- a/crawl-ref/source/skills.cc +++ b/crawl-ref/source/skills.cc @@ -3,6 +3,8 @@ * Summary: Skill exercising functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <3> 8/08/99 BWR Increased skill cost in midgame @@ -21,6 +23,7 @@ #include "externs.h" #include "macro.h" +#include "notes.h" #include "player.h" #include "skills2.h" #include "stuff.h" @@ -38,7 +41,7 @@ #define MAX_COST_LIMIT 250 #define MAX_SPENDING_LIMIT 250 -static void exercise2( char exsk ); +static int exercise2( int exsk ); // These values were calculated by running a simulation of gaining skills. // The goal is to try and match the old cost system which used the player's @@ -146,27 +149,37 @@ static int calc_skill_cost( int skill_cost_level, int skill_level ) return (ret); } -void exercise(char exsk, int deg) +// returns total number of skill points gained +int exercise(int exsk, int deg) { - if (you.exp_available > 0 && you.skills[exsk] < 27) + int ret = 0; + + while (deg > 0) { - while (deg > 0) - { - if (!you.practise_skill[exsk] && !one_chance_in(4)) - break; + if (you.exp_available <= 0 || you.skills[exsk] >= 27) + break; - if (you.skills[exsk] >= 27) - break; + if (you.practise_skill[exsk] || one_chance_in(4)) + ret += exercise2( exsk ); - exercise2( exsk ); - deg--; - } + deg--; } - return; +#ifdef DEBUG_DIAGNOSTICS + if (ret) + { + mprf(MSGCH_DIAGNOSTICS, + "Exercised %s (deg: %d) by %d", + skill_name(exsk), + deg, + ret); + } +#endif + + return (ret); } // end exercise() -static void exercise2( char exsk ) +static int exercise2( int exsk ) { int deg = 1; int bonus = 0; @@ -237,7 +250,7 @@ static void exercise2( char exsk ) || you.skills[SK_EARTH_MAGIC] > you.skills[exsk])) { if (one_chance_in(3)) - return; + return (0); } // some are direct opposites @@ -247,7 +260,7 @@ static void exercise2( char exsk ) { // of course, this is cumulative with the one above. if (!one_chance_in(3)) - return; + return (0); } if ((exsk == SK_AIR_MAGIC || exsk == SK_EARTH_MAGIC) @@ -255,7 +268,7 @@ static void exercise2( char exsk ) || you.skills[SK_EARTH_MAGIC] > you.skills[exsk])) { if (!one_chance_in(3)) - return; + return (0); } // experimental restriction (too many spell schools) -- bwr @@ -270,7 +283,7 @@ static void exercise2( char exsk ) // Things get progressively harder, but not harder than // the Fire-Air or Ice-Earth level. if (skill_rank > 3 && one_chance_in(10 - skill_rank)) - return; + return (0); } int fraction = 0; @@ -351,6 +364,9 @@ static void exercise2( char exsk ) } } + if (skill_inc <= 0) + return (0); + you.skill_points[exsk] += skill_inc; you.exp_available -= skill_change; @@ -379,15 +395,27 @@ static void exercise2( char exsk ) */ if (you.skill_points[exsk] > - (skill_exp_needed(you.skills[exsk] + 2) - * species_skills(exsk, you.species) / 100)) + (skill_exp_needed(you.skills[exsk] + 2) + * species_skills(exsk, you.species) / 100)) { - strcpy(info, "Your "); - strcat(info, skill_name(exsk)); - strcat(info, " skill increases!"); - mpr(info, MSGCH_INTRINSIC_GAIN); - + you.skills[exsk]++; + take_note(Note(NOTE_GAIN_SKILL, exsk, you.skills[exsk])); + + if (you.skills[exsk] == 27) { + snprintf( info, INFO_SIZE, "You have mastered %s!", + skill_name( exsk ) ); + } + else if (you.skills[exsk] == 1) { + snprintf( info, INFO_SIZE, "You have gained %s skill!", + skill_name( exsk ) ); + } + else { + snprintf( info, INFO_SIZE, "Your %s skill increases to level %d!", + skill_name( exsk ), you.skills[exsk] ); + } + + mpr( info, MSGCH_INTRINSIC_GAIN ); // Recalculate this skill's order for tie breaking skills // at its new level. See skills2.cc::init_skill_order() @@ -405,8 +433,7 @@ static void exercise2( char exsk ) if (exsk == SK_FIGHTING) calc_hp(); - if (exsk == SK_INVOCATIONS || exsk == SK_EVOCATIONS - || exsk == SK_SPELLCASTING) + if (exsk == SK_INVOCATIONS || exsk == SK_SPELLCASTING) { calc_mp(); } @@ -438,4 +465,5 @@ static void exercise2( char exsk ) redraw_skill( you.your_name, player_title() ); } } + return (skill_inc); } // end exercise2() diff --git a/crawl-ref/source/skills.h b/crawl-ref/source/skills.h index 10afa1df8a..bdc8afb6c5 100644 --- a/crawl-ref/source/skills.h +++ b/crawl-ref/source/skills.h @@ -3,6 +3,8 @@ * Summary: Skill exercising functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -20,7 +22,7 @@ void calc_total_skill_points( void ); * called from: ability - bang - beam - debug - fight - it_use3 - item_use - * items - misc - spell * *********************************************************************** */ -void exercise(char exsk, int deg); +int exercise(int exsk, int deg); #endif diff --git a/crawl-ref/source/skills2.cc b/crawl-ref/source/skills2.cc index f4012d0462..42f42a1f85 100644 --- a/crawl-ref/source/skills2.cc +++ b/crawl-ref/source/skills2.cc @@ -30,11 +30,12 @@ #include "externs.h" #include "fight.h" +#include "itemprop.h" +#include "menu.h" #include "player.h" #include "randart.h" #include "religion.h" #include "stuff.h" -#include "wpn-misc.h" #include "view.h" /* jmf: some references for words I used below: @@ -61,7 +62,7 @@ const char *skills[50][6] = { {"Bows", "Shooter", "Yeoman", "Archer", "Merry %s", "Merry %s"}, {"Crossbows", "Shooter", "Sharpshooter", "Archer", "%s Ballista", "%s Ballista"}, // 10 {"Darts", "Dart Thrower", "Hurler", "Hurler, First Class", "%s Darts Champion", "Universal Darts Champion"}, - {"Throwing", "Chucker", "Thrower", "Deadly Accurate", "Hawkeye", "Sniper"}, + {"Ranged Combat", "Chucker", "Thrower", "Deadly Accurate", "Hawkeye", "Sniper"}, {"Armour", "Covered", "Protected", "Tortoise", "Impregnable", "Invulnerable"}, {"Dodging", "Ducker", "Dodger", "Nimble", "Spry", "Acrobat"}, @@ -136,7 +137,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 100, // SK_BOWS 100, // SK_CROSSBOWS 100, // SK_DARTS - 100, // SK_THROWING + 100, // SK_RANGED_COMBAT 100, // SK_ARMOUR 100, // SK_DODGING 100, // SK_STEALTH @@ -179,7 +180,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 60, // SK_BOWS 100, // SK_CROSSBOWS 90, // SK_DARTS - 80, // SK_THROWING + 80, // SK_RANGED_COMBAT 120, // SK_ARMOUR 80, // SK_DODGING 80, // SK_STEALTH @@ -222,7 +223,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 60, // SK_BOWS 100, // SK_CROSSBOWS 90, // SK_DARTS - 80, // SK_THROWING + 80, // SK_RANGED_COMBAT 110, // SK_ARMOUR 90, // SK_DODGING 90, // SK_STEALTH @@ -265,7 +266,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 70, // SK_BOWS 100, // SK_CROSSBOWS 90, // SK_DARTS - 80, // SK_THROWING + 80, // SK_RANGED_COMBAT 140, // SK_ARMOUR 75, // SK_DODGING 70, // SK_STEALTH @@ -308,7 +309,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 74, // SK_BOWS 75, // SK_CROSSBOWS 75, // SK_DARTS - 80, // SK_THROWING + 80, // SK_RANGED_COMBAT 140, // SK_ARMOUR 70, // SK_DODGING 65, // SK_STEALTH @@ -351,7 +352,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 100, // SK_BOWS 100, // SK_CROSSBOWS 100, // SK_DARTS - 70, // SK_THROWING + 70, // SK_RANGED_COMBAT 140, // SK_ARMOUR 70, // SK_DODGING 75, // SK_STEALTH @@ -394,7 +395,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 150, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 70, // SK_ARMOUR 120, // SK_DODGING 150, // SK_STEALTH @@ -437,7 +438,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 140, // SK_BOWS 100, // SK_CROSSBOWS 120, // SK_DARTS - 115, // SK_THROWING + 115, // SK_RANGED_COMBAT 60, // SK_ARMOUR 110, // SK_DODGING 140, // SK_STEALTH @@ -480,7 +481,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 70, // SK_BOWS 90, // SK_CROSSBOWS 50, // SK_DARTS - 60, // SK_THROWING + 60, // SK_RANGED_COMBAT 150, // SK_ARMOUR 70, // SK_DODGING 60, // SK_STEALTH @@ -523,7 +524,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 130, // SK_DARTS - 130, // SK_THROWING + 130, // SK_RANGED_COMBAT 90, // SK_ARMOUR 140, // SK_DODGING 150, // SK_STEALTH @@ -566,7 +567,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 80, // SK_BOWS 90, // SK_CROSSBOWS 50, // SK_DARTS - 60, // SK_THROWING + 60, // SK_RANGED_COMBAT 140, // SK_ARMOUR 70, // SK_DODGING 60, // SK_STEALTH @@ -609,7 +610,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 140, // SK_BOWS 140, // SK_CROSSBOWS 140, // SK_DARTS - 140, // SK_THROWING + 140, // SK_RANGED_COMBAT 140, // SK_ARMOUR 140, // SK_DODGING 140, // SK_STEALTH @@ -652,7 +653,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 150, // SK_ARMOUR 150, // SK_DODGING 40, // SK_STEALTH @@ -695,7 +696,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 100, // SK_BOWS 90, // SK_CROSSBOWS 60, // SK_DARTS - 100, // SK_THROWING + 100, // SK_RANGED_COMBAT 150, // SK_ARMOUR 70, // SK_DODGING 70, // SK_STEALTH @@ -738,7 +739,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 150, // SK_BOWS 180, // SK_CROSSBOWS 150, // SK_DARTS - 100, // SK_THROWING + 100, // SK_RANGED_COMBAT 140, // SK_ARMOUR 150, // SK_DODGING 200, // SK_STEALTH @@ -781,7 +782,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 180, // SK_BOWS 180, // SK_CROSSBOWS 180, // SK_DARTS - 130, // SK_THROWING + 130, // SK_RANGED_COMBAT 150, // SK_ARMOUR 130, // SK_DODGING 250, // SK_STEALTH @@ -824,7 +825,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 150, // SK_BOWS 150, // SK_CROSSBOWS 150, // SK_DARTS - 150, // SK_THROWING + 150, // SK_RANGED_COMBAT 170, // SK_ARMOUR 130, // SK_DODGING 100, // SK_STEALTH @@ -867,7 +868,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -889,7 +890,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 100, // SK_TRANSMIGRATION 100, // SK_DIVINATIONS 70, // SK_FIRE_MAGIC - 150, // SK_ICE_MAGIC + 135, // SK_ICE_MAGIC 100, // SK_AIR_MAGIC 100, // SK_EARTH_MAGIC 100, // SK_POISON_MAGIC @@ -910,7 +911,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -931,7 +932,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 100, // SK_TRANSLOCATIONS 100, // SK_TRANSMIGRATION 100, // SK_DIVINATIONS - 150, // SK_FIRE_MAGIC + 135, // SK_FIRE_MAGIC 70, // SK_ICE_MAGIC 100, // SK_AIR_MAGIC 100, // SK_EARTH_MAGIC @@ -953,7 +954,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -996,7 +997,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1039,7 +1040,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1082,7 +1083,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1106,7 +1107,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 100, // SK_FIRE_MAGIC 100, // SK_ICE_MAGIC 70, // SK_AIR_MAGIC - 150, // SK_EARTH_MAGIC + 135, // SK_EARTH_MAGIC 100, // SK_POISON_MAGIC 100, // SK_INVOCATIONS 100, // SK_EVOCATIONS @@ -1125,7 +1126,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1168,7 +1169,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1211,7 +1212,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1254,7 +1255,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1297,7 +1298,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1340,7 +1341,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1383,7 +1384,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 60, // SK_BOWS 85, // SK_CROSSBOWS 80, // SK_DARTS - 60, // SK_THROWING + 60, // SK_RANGED_COMBAT 180, // SK_ARMOUR 170, // SK_DODGING 200, // SK_STEALTH @@ -1426,7 +1427,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 110, // SK_BOWS 110, // SK_CROSSBOWS 110, // SK_DARTS - 110, // SK_THROWING + 110, // SK_RANGED_COMBAT 110, // SK_ARMOUR 110, // SK_DODGING 110, // SK_STEALTH @@ -1469,7 +1470,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 70, // SK_BOWS 100, // SK_CROSSBOWS 70, // SK_DARTS - 90, // SK_THROWING + 90, // SK_RANGED_COMBAT 170, // SK_ARMOUR 50, // SK_DODGING 50, // SK_STEALTH @@ -1512,7 +1513,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 90, // SK_BOWS 90, // SK_CROSSBOWS 90, // SK_DARTS - 90, // SK_THROWING + 90, // SK_RANGED_COMBAT 80, // SK_ARMOUR 80, // SK_DODGING 130, // SK_STEALTH @@ -1555,7 +1556,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 110, // SK_BOWS 110, // SK_CROSSBOWS 110, // SK_DARTS - 110, // SK_THROWING + 110, // SK_RANGED_COMBAT 110, // SK_ARMOUR 110, // SK_DODGING 110, // SK_STEALTH @@ -1598,7 +1599,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 130, // SK_BOWS 130, // SK_CROSSBOWS 130, // SK_DARTS - 130, // SK_THROWING + 130, // SK_RANGED_COMBAT 110, // SK_ARMOUR 110, // SK_DODGING 80, // SK_STEALTH @@ -1641,7 +1642,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 80, // SK_BOWS 80, // SK_CROSSBOWS 90, // SK_DARTS - 90, // SK_THROWING + 90, // SK_RANGED_COMBAT 90, // SK_ARMOUR 90, // SK_DODGING 100, // SK_STEALTH @@ -1684,7 +1685,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 140, // SK_BOWS 140, // SK_CROSSBOWS 100, // SK_DARTS - 100, // SK_THROWING + 100, // SK_RANGED_COMBAT 160, // SK_ARMOUR 60, // SK_DODGING 90, // SK_STEALTH @@ -1732,7 +1733,7 @@ const int spec_skills[ NUM_SPECIES ][40] = { 120, // SK_BOWS 120, // SK_CROSSBOWS 120, // SK_DARTS - 120, // SK_THROWING + 120, // SK_RANGED_COMBAT 200, // SK_ARMOUR 120, // SK_DODGING 120, // SK_STEALTH @@ -1787,11 +1788,38 @@ JOB_PALADIN: ************************************************************* */ +static const int skill_display_order[] = { + SK_FIGHTING, SK_SHORT_BLADES, SK_LONG_SWORDS, SK_AXES, + SK_MACES_FLAILS, SK_POLEARMS, SK_STAVES, SK_UNARMED_COMBAT, + + SK_BLANK_LINE, + + SK_RANGED_COMBAT, SK_SLINGS, SK_BOWS, SK_CROSSBOWS, SK_DARTS, + + SK_BLANK_LINE, + + SK_ARMOUR, SK_DODGING, SK_STEALTH, SK_STABBING, SK_SHIELDS, SK_TRAPS_DOORS, + + SK_BLANK_LINE, + SK_COLUMN_BREAK, + + SK_SPELLCASTING, SK_CONJURATIONS, SK_ENCHANTMENTS, SK_SUMMONINGS, + SK_NECROMANCY, SK_TRANSLOCATIONS, SK_TRANSMIGRATION, SK_DIVINATIONS, + SK_FIRE_MAGIC, SK_ICE_MAGIC, SK_AIR_MAGIC, SK_EARTH_MAGIC, SK_POISON_MAGIC, + + SK_BLANK_LINE, + + SK_INVOCATIONS, SK_EVOCATIONS, +}; + +static const int ndisplayed_skills = + sizeof(skill_display_order) / sizeof(*skill_display_order); + void show_skills(void) { int i; int x; - char lcount; + menu_letter lcount; const int num_lines = get_number_of_lines(); @@ -1823,21 +1851,30 @@ void show_skills(void) // Don't want the help line to appear too far down a big window. int bottom_line = ((num_lines > 30) ? 30 : num_lines); - for (x = 0; x < NUM_SKILLS; x++) + for (i = 0; i < ndisplayed_skills; ++i) { - /* spells in second column */ - if ((x == SK_SPELLCASTING && scrcol != 40) || scrln > bottom_line - 3) + x = skill_display_order[i]; + + if (scrln > bottom_line - 3 || x == SK_COLUMN_BREAK) { - scrln = 3; - scrcol = 40; + if (scrcol != 40) + { + scrln = 3; + scrcol = 40; + } + if (x == SK_COLUMN_BREAK) + continue; + } + + if (x == SK_BLANK_LINE) + { + scrln++; + continue; } gotoxy(scrcol, scrln); -#if DEBUG_DIAGNOSTICS - // In diagnostic mode we show skills at 0, but only real skills - if (x != SK_UNUSED_1 && (x <= SK_UNARMED_COMBAT || x >= SK_SPELLCASTING)) -#else +#ifndef DEBUG_DIAGNOSTICS if (you.skills[x] > 0) #endif { @@ -1853,19 +1890,9 @@ void show_skills(void) if (you.skills[x] == 0) putch(' '); else - { - putch(lcount); - if (lcount == 'z') - lcount = 'A'; - else - lcount++; - } + putch(lcount++); #else - putch(lcount); - if (lcount == 'z') - lcount = 'A'; - else - lcount++; + putch(lcount++); #endif cprintf( " %c %-14s Skill %2d", @@ -1885,18 +1912,19 @@ void show_skills(void) const int prev_needed = skill_exp_needed(you.skills[x] + 1); const int spec_abil = species_skills(x, you.species); - cprintf( " (%d)", - (((needed * spec_abil) / 100 - you.skill_points[x]) * 10) / - (((needed - prev_needed) * spec_abil) / 100) ); - } + int percent_done = ((you.skill_points[x] - (prev_needed * spec_abil) / 100) * 100) / (((needed - prev_needed) * spec_abil) / 100); - scrln++; - } + if ( percent_done == 100 ) + --percent_done; + if ( percent_done == 0 ) + ++percent_done; + + if ( !Options.increasing_skill_progress ) + cprintf( " (%d)", (100 - percent_done) / 10 ); + else + cprintf( " (%2d%%)", (percent_done / 5) * 5 ); + } - /* Extra CR between classes of weapons and such things */ - if (x == SK_STAVES || x == SK_THROWING || x == SK_TRAPS_DOORS - || x == SK_UNARMED_COMBAT || x == SK_POISON_MAGIC) - { scrln++; } } @@ -1919,21 +1947,22 @@ void show_skills(void) { lcount = 'a'; // toggle skill practise - for (i = 0; i < 50; i++) + for (i = 0; i < ndisplayed_skills; i++) { - if (you.skills[i] == 0) + x = skill_display_order[i]; + if (x == SK_BLANK_LINE || x == SK_COLUMN_BREAK) + continue; + + if (you.skills[x] == 0) continue; if (get_thing == lcount) { - you.practise_skill[i] = (you.practise_skill[i]) ? 0 : 1; + you.practise_skill[x] = !you.practise_skill[x]; break; } - if (lcount == 'z') - lcount = 'A'; - else - lcount++; + ++lcount; } goto reprint_stuff; @@ -1947,7 +1976,7 @@ void show_skills(void) } -const char *skill_name(unsigned char which_skill) +const char *skill_name(int which_skill) { return (skills[which_skill][0]); } // end skill_name() @@ -2038,10 +2067,9 @@ const char *player_title( void ) return (skill_title( best, you.skills[ best ] )); } // end player_title() -unsigned char best_skill( unsigned char min_skill, unsigned char max_skill, - unsigned char excl_skill ) +int best_skill( int min_skill, int max_skill, int excl_skill ) { - unsigned char ret = SK_FIGHTING; + int ret = SK_FIGHTING; unsigned int best_skill_level = 0; unsigned int best_position = 1000; @@ -2179,14 +2207,11 @@ int calc_mp(void) int spell_extra = (you.experience_level * you.skills[SK_SPELLCASTING]) / 4; int invoc_extra = (you.experience_level * you.skills[SK_INVOCATIONS]) / 6; - int evoc_extra = (you.experience_level * you.skills[SK_EVOCATIONS]) / 6; - if (spell_extra > invoc_extra && spell_extra > evoc_extra) + if (spell_extra > invoc_extra) enp += spell_extra; - else if (invoc_extra > evoc_extra) - enp += invoc_extra; else - enp += evoc_extra; + enp += invoc_extra; you.max_magic_points = stepdown_value( enp, 9, 18, 45, 100 ); @@ -2298,7 +2323,7 @@ void wield_warning(bool newWeapon) strcat(wepstr, wepstr2); // only warn about str/dex for non-launcher weapons - if (!launches_things(wepType)) + if (!is_range_weapon( you.inv[you.equip[EQ_WEAPON]] )) { #ifdef USE_NEW_COMBAT_STATS const int stat_bonus = effective_stat_bonus(); @@ -2334,8 +2359,10 @@ void wield_warning(bool newWeapon) return; } + // [dshaligram] No more annoying throwing skill warnings. +#ifdef OBSOLETE_THROW_SKILL_WARNING // must be a launcher - int effSkill = you.skills[SK_THROWING] * 2 + 1; + int effSkill = you.skills[SK_RANGED_COMBAT] * 2 + 1; int shoot_skill = 0; switch (wepType) @@ -2364,4 +2391,5 @@ void wield_warning(bool newWeapon) strcat( info, wepstr ); mpr( info, MSGCH_WARN ); } +#endif } diff --git a/crawl-ref/source/skills2.h b/crawl-ref/source/skills2.h index 05c5d03068..73658f31af 100644 --- a/crawl-ref/source/skills2.h +++ b/crawl-ref/source/skills2.h @@ -23,7 +23,7 @@ /* *********************************************************************** * called from: chardump - it_use3 - itemname - skills * *********************************************************************** */ -const char *skill_name(unsigned char which_skill); +const char *skill_name(int which_skill); // last_updated 24may2000 {dlb} @@ -46,7 +46,7 @@ const char *player_title( void ); * called from: acr - chardump - effects - files - player - skills - * skills2 - stuff * *********************************************************************** */ -unsigned char best_skill(unsigned char min_skill, unsigned char max_skill, unsigned char excl_skill); +int best_skill(int min_skill, int max_skill, int excl_skill = -1); void init_skill_order( void ); diff --git a/crawl-ref/source/spells1.cc b/crawl-ref/source/spells1.cc index 64bb5630f0..8e7eecc8e9 100644 --- a/crawl-ref/source/spells1.cc +++ b/crawl-ref/source/spells1.cc @@ -3,6 +3,8 @@ * Summary: Implementations of some additional spells. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <4> 06-mar-2000 bwr confusing_touch, sure_blade @@ -29,6 +31,7 @@ #include "invent.h" #include "it_use2.h" #include "itemname.h" +#include "itemprop.h" #include "misc.h" #include "monplace.h" #include "monstuff.h" @@ -40,9 +43,8 @@ #include "spl-util.h" #include "stuff.h" #include "view.h" -#include "wpn-misc.h" -void blink(void) +int blink(void) { struct dist beam; @@ -65,12 +67,12 @@ void blink(void) { mpr("Blink to where?", MSGCH_PROMPT); - direction( beam, DIR_TARGET ); + direction( beam, DIR_TARGET, TARG_ANY, true ); if (!beam.isValid) { - canned_msg(MSG_SPELL_FIZZLES); - return; // early return {dlb} + canned_msg(MSG_OK); + return (-1); // early return {dlb} } if (see_grid(beam.tx, beam.ty)) @@ -82,7 +84,7 @@ void blink(void) } } - if (grd[beam.tx][beam.ty] <= DNGN_LAST_SOLID_TILE + if (grid_is_solid(grd[beam.tx][beam.ty]) || mgrd[beam.tx][beam.ty] != NON_MONSTER) { mpr("Oops! Maybe something was there already."); @@ -109,7 +111,7 @@ void blink(void) } } - return; + return (1); } // end blink() void random_blink(bool allow_partial_control) @@ -130,7 +132,7 @@ void random_blink(bool allow_partial_control) #ifdef USE_SEMI_CONTROLLED_BLINK //jmf: add back control, but effect is cast_semi_controlled_blink(pow) - else if (you.attribute[ATTR_CONTROL_TELEPORT] && !you.conf + else if (player_control_teleport() && !you.conf && allow_partial_control && allow_control_teleport()) { mpr("You may select the general direction of your translocation."); @@ -163,7 +165,7 @@ void random_blink(bool allow_partial_control) return; } // end random_blink() -void fireball(int power) +int fireball(int power) { struct dist fire_ball; @@ -171,41 +173,42 @@ void fireball(int power) message_current_target(); - direction( fire_ball, DIR_NONE, TARG_ENEMY ); + direction( fire_ball, DIR_NONE, TARG_ENEMY, true ); if (!fire_ball.isValid) - canned_msg(MSG_SPELL_FIZZLES); + { + canned_msg(MSG_OK); + return (-1); + } else { struct bolt beam; beam.source_x = you.x_pos; beam.source_y = you.y_pos; - beam.target_x = fire_ball.tx; - beam.target_y = fire_ball.ty; + beam.set_target(fire_ball); zapping(ZAP_FIREBALL, power, beam); } - return; + return (1); } // end fireball() -void cast_fire_storm(int powc) +int cast_fire_storm(int powc) { struct bolt beam; struct dist targ; mpr("Where?"); - direction( targ, DIR_TARGET, TARG_ENEMY ); + direction( targ, DIR_TARGET, TARG_ENEMY, true ); - beam.target_x = targ.tx; - beam.target_y = targ.ty; + beam.set_target(targ); if (!targ.isValid) { - canned_msg(MSG_SPELL_FIZZLES); - return; + canned_msg(MSG_OK); + return (-1); } beam.ex_size = 2 + (random2(powc) > 75); @@ -214,12 +217,13 @@ void cast_fire_storm(int powc) beam.colour = RED; beam.beam_source = MHITYOU; beam.thrower = KILL_YOU_MISSILE; - beam.aux_source = NULL; - beam.obviousEffect = false; - beam.isBeam = false; - beam.isTracer = false; + beam.aux_source.clear(); + beam.obvious_effect = false; + beam.is_beam = false; + beam.is_tracer = false; + beam.is_explosion = true; beam.ench_power = powc; // used for radius - strcpy( beam.beam_name, "great blast of fire" ); + beam.name = "great blast of fire"; beam.hit = 20 + powc / 10; beam.damage = calc_dice( 6, 15 + powc ); @@ -227,8 +231,164 @@ void cast_fire_storm(int powc) mpr("A raging storm of fire appears!"); viewwindow(1, false); + + return (1); } // end cast_fire_storm() + +void cast_chain_lightning( int powc ) +{ + struct bolt beam; + + // initialize beam structure + beam.name = "lightning arc"; + beam.aux_source = "chain lightning"; + beam.beam_source = MHITYOU; + beam.thrower = KILL_YOU_MISSILE; + beam.range = 8; + beam.rangeMax = 8; + beam.hit = AUTOMATIC_HIT; + beam.type = SYM_ZAP; + beam.flavour = BEAM_ELECTRICITY; + beam.obvious_effect = true; + beam.is_beam = false; // since we want to stop at our target + beam.is_explosion = false; + beam.is_tracer = false; + + int sx, sy; + int tx, ty; + int i; + + for (sx = you.x_pos, sy = you.y_pos; + powc > 0; + powc -= 8 + random2(13), sx = tx, sy = ty) + { + // infinity as far as this spell is concerned + // (Range - 1) is used because the distance is randomized and + // may be shifted by one. + int min_dist = MONSTER_LOS_RANGE - 1; + + int dist; + int count = 0; + + tx = -1; + ty = -1; + + for (i = 0; i < MAX_MONSTERS; i++) + { + struct monsters *monster = &menv[i]; + + if (monster->type == -1) + continue; + + dist = grid_distance( sx, sy, monster->x, monster->y ); + + // check for the source of this arc + if (!dist) + continue; + + // randomize distance (arcs don't care about a couple of feet) + dist += (random2(3) - 1); + + // always ignore targets further than current one + if (dist > min_dist) + continue; + + if (!check_line_of_sight( sx, sy, monster->x, monster->y )) + continue; + + count++; + + if (dist < min_dist) + { + // switch to looking for closer targets (but not always) + if (!one_chance_in(10)) + { + min_dist = dist; + tx = monster->x; + ty = monster->y; + count = 0; + } + } + else if (tx == -1 || one_chance_in( count )) + { + // either first target, or new selected target at min_dist + tx = monster->x; + ty = monster->y; + + // need to set min_dist for first target case + if (dist < min_dist) + min_dist = dist; + } + } + + // now check if the player is a target: + dist = grid_distance( sx, sy, you.x_pos, you.y_pos ); + + if (dist) // ie player was not the source + { + // distance randomized (as above) + dist += (random2(3) - 1); + + // select player if only, closest, or randomly selected + if ((tx == -1 + || dist < min_dist + || (dist == min_dist && one_chance_in( count + 1 ))) + && check_line_of_sight( sx, sy, you.x_pos, you.y_pos )) + { + tx = you.x_pos; + ty = you.y_pos; + } + } + + const bool see_source = see_grid( sx, sy ); + const bool see_targ = see_grid( tx, ty ); + + if (tx == -1) + { + if (see_source) + mpr( "The lightning grounds out." ); + + break; + } + + // Trying to limit message spamming here so we'll only mention + // the thunder when it's out of LoS. + if (noisy( 25, sx, sy ) && !see_source) + mpr( "You hear a mighty clap of thunder!", MSGCH_SOUND ); + + if (see_source && !see_targ) + mpr( "The lightning arcs out of your line of sight!" ); + else if (!see_source && see_targ) + mpr( "The lightning arc suddenly appears!" ); + + if (!see_targ) + { + // It's no longer in the caster's LOS and influence. + powc = powc / 2 + 1; + } + + beam.source_x = sx; + beam.source_y = sy; + beam.target_x = tx; + beam.target_y = ty; + beam.colour = LIGHTBLUE; + beam.damage = calc_dice( 5, 12 + powc * 2 / 3 ); + + // Be kinder to the player + if (tx == you.x_pos && ty == you.y_pos) + { + if (!(beam.damage.num /= 2)) + beam.damage.num = 1; + if ((beam.damage.size /= 2) < 3) + beam.damage.size = 3; + } + fire_beam( beam ); + } + + more(); +} + void identify(int power) { int id_used = 1; @@ -241,8 +401,8 @@ void identify(int power) do { - item_slot = prompt_invent_item( "Identify which item?", -1, true, - false, false ); + item_slot = prompt_invent_item( "Identify which item?", MT_INVSELECT, + -1, true, true, false ); if (item_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -257,16 +417,18 @@ void identify(int power) // output identified item in_name( item_slot, DESC_INVENTORY_EQUIP, str_pass ); mpr( str_pass ); - if (item_slot == you.equip[EQ_WEAPON]) you.wield_change = true; id_used--; + + if (Options.auto_list && id_used > 0) + more(); } while (id_used > 0); } // end identify() -void conjure_flame(int pow) +int conjure_flame(int pow) { struct dist spelld; @@ -282,12 +444,12 @@ void conjure_flame(int pow) done_first_message = true; } - direction( spelld, DIR_TARGET, TARG_ENEMY ); + direction( spelld, DIR_TARGET, TARG_ENEMY, true ); if (!spelld.isValid) { - canned_msg(MSG_SPELL_FIZZLES); - return; + canned_msg(MSG_OK); + return (-1); } if (!see_grid(spelld.tx, spelld.ty)) @@ -296,7 +458,7 @@ void conjure_flame(int pow) continue; } - if (grd[ spelld.tx ][ spelld.ty ] <= DNGN_LAST_SOLID_TILE + if (grid_is_solid(grd[ spelld.tx ][ spelld.ty ]) || mgrd[ spelld.tx ][ spelld.ty ] != NON_MONSTER || env.cgrid[ spelld.tx ][ spelld.ty ] != EMPTY_CLOUD) { @@ -313,9 +475,10 @@ void conjure_flame(int pow) durat = 23; place_cloud( CLOUD_FIRE, spelld.tx, spelld.ty, durat ); + return (1); } // end cast_conjure_flame() -void stinking_cloud( int pow ) +int stinking_cloud( int pow ) { struct dist spelld; struct bolt beem; @@ -324,21 +487,20 @@ void stinking_cloud( int pow ) message_current_target(); - direction( spelld, DIR_NONE, TARG_ENEMY ); + direction( spelld, DIR_NONE, TARG_ENEMY, true ); if (!spelld.isValid) { - canned_msg(MSG_SPELL_FIZZLES); - return; + canned_msg(MSG_OK); + return (-1); } - beem.target_x = spelld.tx; - beem.target_y = spelld.ty; + beem.set_target(spelld); beem.source_x = you.x_pos; beem.source_y = you.y_pos; - strcpy(beem.beam_name, "ball of vapour"); + beem.name = "ball of vapour"; beem.colour = GREEN; beem.range = 6; beem.rangeMax = 6; @@ -349,27 +511,30 @@ void stinking_cloud( int pow ) beem.ench_power = pow; beem.beam_source = MHITYOU; beem.thrower = KILL_YOU; - beem.aux_source = NULL; - beem.isBeam = false; - beem.isTracer = false; + beem.aux_source.clear(); + beem.is_beam = false; + beem.is_tracer = false; fire_beam(beem); + + return (1); } // end stinking_cloud() -void cast_big_c(int pow, char cty) +int cast_big_c(int pow, char cty) { struct dist cdis; mpr("Where do you want to put it?", MSGCH_PROMPT); - direction( cdis, DIR_TARGET, TARG_ENEMY ); + direction( cdis, DIR_TARGET, TARG_ENEMY, true ); if (!cdis.isValid) { - canned_msg(MSG_SPELL_FIZZLES); - return; + canned_msg(MSG_OK); + return (-1); } big_cloud( cty, cdis.tx, cdis.ty, pow, 8 + random2(3) ); + return (1); } // end cast_big_c() void big_cloud(char clouds, char cl_x, char cl_y, int pow, int size) @@ -377,18 +542,18 @@ void big_cloud(char clouds, char cl_x, char cl_y, int pow, int size) apply_area_cloud(make_a_normal_cloud, cl_x, cl_y, pow, size, clouds); } // end big_cloud() -static char healing_spell( int healed ) +static int healing_spell( int healed ) { int mgr = 0; struct monsters *monster = 0; // NULL {dlb} struct dist bmove; mpr("Which direction?", MSGCH_PROMPT); - direction( bmove, DIR_DIR, TARG_FRIEND ); + direction( bmove, DIR_DIR, TARG_FRIEND, true ); if (!bmove.isValid) { - canned_msg( MSG_HUH ); + canned_msg( MSG_OK ); return 0; } @@ -446,7 +611,7 @@ char cast_greatest_healing( int pow ) } // end cast_greatest_healing() #endif -char cast_healing( int pow ) +int cast_healing( int pow ) { if (pow > 50) pow = 50; @@ -880,11 +1045,7 @@ void cast_swiftness(int power) if (player_in_water()) { - if (you.species == SP_MERFOLK) - mpr("This spell will not benefit you while you're swimming!"); - else - mpr("This spell will not benefit you while you're in water!"); - + mpr("The water foams!"); return; } @@ -898,11 +1059,9 @@ void cast_swiftness(int power) // dur_incr = random2(power) + random2(power) + 20; dur_incr = 20 + random2( power ); - // Centaurs do have feet and shouldn't get here anyways -- bwr - snprintf( info, INFO_SIZE, "You feel quick%s", - (you.species == SP_NAGA) ? "." : " on your feet." ); - - mpr(info); + // [dshaligram] Removed the on-your-feet bit. Sounds odd when you're + // levitating, for instance. + mpr("You feel quick."); if (dur_incr + you.duration[DUR_SWIFTNESS] > 100) you.duration[DUR_SWIFTNESS] = 100; @@ -963,9 +1122,6 @@ void cast_teleport_control(int power) { int dur_incr = 10 + random2(power); - if (you.duration[DUR_CONTROL_TELEPORT] == 0) - you.attribute[ATTR_CONTROL_TELEPORT]++; - mpr("You feel in control."); if (dur_incr + you.duration[DUR_CONTROL_TELEPORT] >= 50) @@ -1047,7 +1203,7 @@ void manage_fire_shield(void) //if ( one_chance_in(3) ) beam.range ++; - if (grd[you.x_pos + stx][you.y_pos + sty] > DNGN_LAST_SOLID_TILE + if (!grid_is_solid(grd[you.x_pos + stx][you.y_pos + sty]) && env.cgrid[you.x_pos + stx][you.y_pos + sty] == EMPTY_CLOUD) { place_cloud( CLOUD_FIRE, you.x_pos + stx, you.y_pos + sty, diff --git a/crawl-ref/source/spells1.h b/crawl-ref/source/spells1.h index dc98a1b133..64481d53bb 100644 --- a/crawl-ref/source/spells1.h +++ b/crawl-ref/source/spells1.h @@ -50,7 +50,7 @@ char cast_lesser_healing(void); /* *********************************************************************** * called from: ability - spell * *********************************************************************** */ -char cast_healing(int power); +int cast_healing(int power); // last updated 24may2000 {dlb} /* *********************************************************************** @@ -63,25 +63,25 @@ void big_cloud(char clouds, char cl_x, char cl_y, int pow, int size); /* *********************************************************************** * called from: acr (WIZARD only) - item_use - spell * *********************************************************************** */ -void blink(void); +int blink(void); /* *********************************************************************** * called from: spell * *********************************************************************** */ -void cast_big_c(int pow, char cty); +int cast_big_c(int pow, char cty); void cast_confusing_touch(int power); void cast_cure_poison(int mabil); int allowed_deaths_door_hp(void); void cast_deaths_door(int pow); -void cast_fire_storm(int powc); +int cast_fire_storm(int powc); bool cast_revivification(int power); void cast_berserk(void); void cast_ring_of_flames(int power); -void conjure_flame(int pow); +int conjure_flame(int pow); void extension(int pow); -void fireball(int power); -void stinking_cloud(int pow); +int fireball(int power); +int stinking_cloud(int pow); void abjuration(int pow); // last updated 24may2000 {dlb} diff --git a/crawl-ref/source/spells2.cc b/crawl-ref/source/spells2.cc index dbd8f00f3b..8fbd11ac3d 100644 --- a/crawl-ref/source/spells2.cc +++ b/crawl-ref/source/spells2.cc @@ -29,8 +29,10 @@ #include "beam.h" #include "cloud.h" #include "direct.h" +#include "dungeon.h" #include "effects.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "misc.h" #include "monplace.h" @@ -43,7 +45,6 @@ #include "spl-cast.h" #include "stuff.h" #include "view.h" -#include "wpn-misc.h" int raise_corpse( int corps, int corx, int cory, int corps_beh, int corps_hit, int actual ); @@ -73,7 +74,8 @@ unsigned char detect_traps( int pow ) traps_found++; grd[ etx ][ ety ] = trap_category( env.trap[count_x].type ); - env.map[etx - 1][ety - 1] = '^'; + set_envmap_char(etx, ety, get_magicmap_char(grd[etx][ety])); + set_terrain_mapped(etx, ety); } } } @@ -100,11 +102,8 @@ unsigned char detect_items( int pow ) if (igrd[i][j] != NON_ITEM) { - unsigned short flags = env.map[i - 1][j - 1]; - flags = !flags || (flags & ENVF_DETECTED)? - ENVF_DETECTED - : 0; - env.map[i - 1][j - 1] = '~' | ENVF_DETECT_ITEM | flags; + set_envmap_char(i, j, get_magicmap_char(DNGN_ITEM_DETECTED)); + set_envmap_detected_item(i, j); } } } @@ -112,14 +111,71 @@ unsigned char detect_items( int pow ) return (items_found); } // end detect_items() +static void fuzz_detect_creatures(int pow, int *fuzz_radius, int *fuzz_chance) +{ +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "dc_fuzz: Power is %d", pow); +#endif + + if (pow < 1) + pow = 1; + + *fuzz_radius = pow >= 50? 1 : 2; + + // Fuzz chance starts off at 100% and declines to a low of 10% for obscenely + // powerful castings (pow caps around the 60 mark). + *fuzz_chance = 100 - 90 * (pow - 1) / 59; + if (*fuzz_chance < 10) + *fuzz_chance = 10; +} + +static void mark_detected_creature(int gridx, int gridy, const monsters *mon, + int fuzz_chance, int fuzz_radius) +{ + if (fuzz_radius && fuzz_chance > random2(100)) + { + const int fuzz_diam = 2 * fuzz_radius + 1; + + int gx, gy; + bool found_good = false; + for (int itry = 0; itry < 5; ++itry) + { + gx = gridx + random2(fuzz_diam) - fuzz_radius; + gy = gridy + random2(fuzz_diam) - fuzz_radius; + + if (map_bounds(gx, gy) && !grid_is_solid(grd[gx][gy])) + { + found_good = true; + break; + } + } + + if (found_good) + { + gridx = gx; + gridy = gy; + } + } + + set_envmap_char(gridx, gridy, mons_char(mon->type)); + set_envmap_detected_mons(gridx, gridy); +} + unsigned char detect_creatures( int pow ) { + int fuzz_radius = 0, fuzz_chance = 0; + fuzz_detect_creatures(pow, &fuzz_radius, &fuzz_chance); + if (pow > 50) pow = 50; unsigned char creatures_found = 0; const int map_radius = 8 + random2(8) + pow; + // Clear the map so detect creatures is more useful and the detection + // fuzz is harder to analyse by averaging. + clear_map(); + mpr("You detect creatures!"); for (int i = you.x_pos - map_radius; i < you.x_pos + map_radius; i++) @@ -132,19 +188,16 @@ unsigned char detect_creatures( int pow ) if (mgrd[i][j] != NON_MONSTER) { struct monsters *mon = &menv[ mgrd[i][j] ]; + mark_detected_creature(i, j, mon, fuzz_chance, fuzz_radius); - unsigned short flags = env.map[i - 1][j - 1]; - flags = !flags || (flags & ENVF_DETECTED)? - ENVF_DETECTED - : 0; - - env.map[i - 1][j - 1] = - mons_char( mon->type ) | ENVF_DETECT_MONS | flags; + // [ds] Should we be doing this here? DC doesn't give away + // full monster identity, after all. XXX + seen_monster(mon); // Assuming that highly intelligent spellcasters can - // detect scyring. -- bwr + // detect scrying. -- bwr if (mons_intel( mon->type ) == I_HIGH - && mons_flag( mon->type, M_SPELLCASTER )) + && mons_class_flag( mon->type, M_SPELLCASTER )) { behaviour_event( mon, ME_DISTURB, MHITYOU, you.x_pos, you.y_pos ); @@ -321,7 +374,7 @@ int animate_dead( int power, int corps_beh, int corps_hit, int actual ) return number_raised; } // end animate_dead() -int animate_a_corpse( int axps, int ayps, int corps_beh, int corps_hit, +int animate_a_corpse( int axps, int ayps, int corps_beh, int corps_hit, int class_allowed ) { if (igrd[axps][ayps] == NON_ITEM) @@ -495,7 +548,7 @@ bool brand_weapon(int which_brand, int power) return false; if (you.inv[wpn].base_type != OBJ_WEAPONS - || launches_things(you.inv[wpn].sub_type)) + || is_range_weapon(you.inv[wpn])) { return false; } @@ -511,8 +564,7 @@ bool brand_weapon(int which_brand, int power) in_name( wpn, DESC_CAP_YOUR, str_pass ); strcpy( info, str_pass ); - const int wpn_type = damage_type( you.inv[wpn].base_type, - you.inv[wpn].sub_type ); + const int wpn_type = get_vorpal_type(you.inv[wpn]); switch (which_brand) // use SPECIAL_WEAPONS here? { @@ -561,9 +613,16 @@ bool brand_weapon(int which_brand, int power) strcat( info, coinflip() ? " oddly." : " strangely." ); duration_affected = 5; + // [dshaligram] Clamping power to 2. + power = 2; + // This brand is insanely powerful, this isn't even really // a start to balancing it, but it needs something. -- bwr - miscast_effect(SPTYP_TRANSLOCATION, 9, 90, 100, "a distortion effect"); + // [dshaligram] At level 7 it's costly enough to experiment + // with removing the miscast effect. We may need to revise the spell + // to level 8 or 9. XXX. + // miscast_effect(SPTYP_TRANSLOCATION, + // 9, 90, 100, "a distortion effect"); break; case SPWPN_DUMMY_CRUSHING: //jmf: added for Maxwell's Silver Hammer @@ -682,7 +741,7 @@ void turn_undead(int pow) // used to inflict random2(5) + (random2(pow) / 20) damage, // in addition {dlb} - if (mons_holiness(monster->type) == MH_UNDEAD) + if (mons_holiness(monster) == MH_UNDEAD) { if (check_mons_resist_magic( monster, pow )) { @@ -707,11 +766,12 @@ void turn_undead(int pow) } // end "for tu" } // end turn_undead() -void holy_word(int pow) +void holy_word(int pow, bool silent) { struct monsters *monster; - mpr("You speak a Word of immense power!"); + if (!silent) + mpr("You speak a Word of immense power!"); // doubt this will ever happen, but it's here as a safety -- bwr if (pow > 300) @@ -724,8 +784,8 @@ void holy_word(int pow) if (monster->type == -1 || !mons_near(monster)) continue; - if (mons_holiness(monster->type) == MH_UNDEAD - || mons_holiness(monster->type) == MH_DEMONIC) + if (mons_holiness(monster) == MH_UNDEAD + || mons_holiness(monster) == MH_DEMONIC) { simple_monster_message(monster, " convulses!"); @@ -827,7 +887,7 @@ void cast_refrigeration(int pow) // Note: this used to be 12!... and it was also applied even if // the player didn't take damage from the cold, so we're being // a lot nicer now. -- bwr - scrolls_burn( 5, OBJ_POTIONS ); + expose_player_to_element(BEAM_COLD, 5); } // Now do the monsters: @@ -859,7 +919,7 @@ void cast_refrigeration(int pow) print_wounds(monster); //jmf: "slow snakes" finally available - if (mons_flag( monster->type, M_COLD_BLOOD ) && coinflip()) + if (mons_class_flag( monster->type, M_COLD_BLOOD ) && coinflip()) mons_add_ench(monster, ENCH_SLOW); } } @@ -892,7 +952,7 @@ void drain_life(int pow) if (monster->type == -1) continue; - if (mons_holiness( monster->type ) != MH_NATURAL) + if (mons_holiness(monster) != MH_NATURAL) continue; if (mons_res_negative_energy( monster )) @@ -939,7 +999,7 @@ int vampiric_drain(int pow) dirc: mpr("Which direction?", MSGCH_PROMPT); - direction( vmove, DIR_DIR, TARG_ENEMY ); + direction( vmove, DIR_DIR, TARG_ENEMY, true ); if (!vmove.isValid) { @@ -963,12 +1023,12 @@ int vampiric_drain(int pow) monster = &menv[mgr]; - const int holy = mons_holiness(monster->type); + const int holy = mons_holiness(monster); if (holy == MH_UNDEAD || holy == MH_DEMONIC) { mpr("Aaaarggghhhhh!"); - dec_hp(random2avg(39, 2) + 10, false); + dec_hp(random2avg(39, 2) + 10, false, "vampiric drain backlash"); return -1; } @@ -1023,11 +1083,11 @@ char burn_freeze(int pow, char flavour) while (mgr == NON_MONSTER) { mpr("Which direction?", MSGCH_PROMPT); - direction( bmove, DIR_DIR, TARG_ENEMY ); + direction( bmove, DIR_DIR, TARG_ENEMY, true ); if (!bmove.isValid) { - canned_msg(MSG_SPELL_FIZZLES); + canned_msg(MSG_OK); return -1; } @@ -1043,7 +1103,7 @@ char burn_freeze(int pow, char flavour) if (mgr == NON_MONSTER) { mpr("There isn't anything close enough!"); - return -1; + return 0; } } @@ -1082,7 +1142,7 @@ char burn_freeze(int pow, char flavour) if (flavour == BEAM_COLD) { - if (mons_flag( monster->type, M_COLD_BLOOD ) && coinflip()) + if (mons_class_flag( monster->type, M_COLD_BLOOD ) && coinflip()) mons_add_ench(monster, ENCH_SLOW); const int cold_res = mons_res_cold( monster ); @@ -1123,11 +1183,11 @@ int summon_elemental(int pow, int restricted_type, { mpr("Summon from material in which direction?", MSGCH_PROMPT); - direction( smove, DIR_DIR ); + direction( smove, DIR_DIR, TARG_ANY, true ); if (!smove.isValid) { - canned_msg(MSG_NOTHING_HAPPENS); + canned_msg(MSG_OK); return (-1); } @@ -1183,7 +1243,7 @@ int summon_elemental(int pow, int restricted_type, if (type_summoned == MONS_PROGRAM_BUG) { canned_msg(MSG_NOTHING_HAPPENS); - return (-1); + return (0); } // silly - ice for water? 15jan2000 {dlb} @@ -1314,10 +1374,10 @@ void summon_scorpions(int pow) } } // end summon_scorpions() -void summon_ice_beast_etc(int pow, int ibc) +void summon_ice_beast_etc(int pow, int ibc, bool divine_gift) { int numsc = ENCH_ABJ_II + (random2(pow) / 4); - int beha = BEH_FRIENDLY; + int beha = divine_gift? BEH_GOD_GIFT : BEH_FRIENDLY; if (numsc > ENCH_ABJ_VI) numsc = ENCH_ABJ_VI; diff --git a/crawl-ref/source/spells2.h b/crawl-ref/source/spells2.h index 8c3bef71c5..409aff5a25 100644 --- a/crawl-ref/source/spells2.h +++ b/crawl-ref/source/spells2.h @@ -116,7 +116,7 @@ void drain_life(int pow); /* *********************************************************************** * called from: ability - spell * *********************************************************************** */ -void holy_word(int pow); +void holy_word(int pow, bool silent = false); // last updated 24may2000 {dlb} @@ -131,7 +131,7 @@ bool restore_stat(unsigned char which_stat, bool suppress_msg); /* *********************************************************************** * called from: ability - spell * *********************************************************************** */ -void summon_ice_beast_etc(int pow, int ibc); +void summon_ice_beast_etc(int pow, int ibc, bool divine_gift = false); // last updated 24may2000 {dlb} diff --git a/crawl-ref/source/spells3.cc b/crawl-ref/source/spells3.cc index e2a2bf325f..2d74e8a92f 100644 --- a/crawl-ref/source/spells3.cc +++ b/crawl-ref/source/spells3.cc @@ -3,6 +3,8 @@ * Summary: Implementations of some additional spells. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 9/11/99 LRH Teleportation takes longer in the Abyss @@ -26,6 +28,7 @@ #include "debug.h" #include "delay.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "it_use2.h" #include "misc.h" @@ -36,15 +39,15 @@ #include "player.h" #include "randart.h" #include "spells1.h" +#include "spells4.h" #include "spl-cast.h" #include "spl-util.h" #include "stuff.h" #include "view.h" -#include "wpn-misc.h" static bool monster_on_level(int monster); -void cast_selective_amnesia(bool force) +bool cast_selective_amnesia(bool force) { char ep_gain = 0; unsigned char keyin = 0; @@ -61,7 +64,7 @@ void cast_selective_amnesia(bool force) keyin = (unsigned char) get_ch(); if (keyin == ESCAPE) - return; // early return {dlb} + return (false); // early return {dlb} if (keyin == '?' || keyin == '*') { @@ -108,7 +111,7 @@ void cast_selective_amnesia(bool force) } } - return; + return (true); } // end cast_selective_amnesia() bool remove_curse(bool suppress_msg) @@ -164,7 +167,7 @@ bool detect_curse(bool suppress_msg) || you.inv[loopy].base_type == OBJ_ARMOUR || you.inv[loopy].base_type == OBJ_JEWELLERY)) { - if (item_not_ident( you.inv[loopy], ISFLAG_KNOW_CURSE )) + if (!item_ident( you.inv[loopy], ISFLAG_KNOW_CURSE )) success = true; set_ident_flags( you.inv[loopy], ISFLAG_KNOW_CURSE ); @@ -183,7 +186,7 @@ bool detect_curse(bool suppress_msg) return (success); } // end detect_curse() -bool cast_smiting(int power) +int cast_smiting(int power) { bool success = false; struct dist beam; @@ -191,10 +194,15 @@ bool cast_smiting(int power) mpr("Smite whom?", MSGCH_PROMPT); - direction( beam, DIR_TARGET, TARG_ENEMY ); + direction( beam, DIR_TARGET, TARG_ENEMY, true ); - if (!beam.isValid - || mgrd[beam.tx][beam.ty] == NON_MONSTER + if (!beam.isValid) + { + canned_msg(MSG_OK); + return (-1); + } + + if (mgrd[beam.tx][beam.ty] == NON_MONSTER || beam.isMe) { canned_msg(MSG_SPELL_FIZZLES); @@ -221,7 +229,7 @@ bool cast_smiting(int power) return (success); } // end cast_smiting() -bool airstrike(int power) +int airstrike(int power) { bool success = false; struct dist beam; @@ -230,10 +238,15 @@ bool airstrike(int power) mpr("Strike whom?", MSGCH_PROMPT); - direction( beam, DIR_TARGET, TARG_ENEMY ); + direction( beam, DIR_TARGET, TARG_ENEMY, true ); - if (!beam.isValid - || mgrd[beam.tx][beam.ty] == NON_MONSTER + if (!beam.isValid) + { + canned_msg(MSG_OK); + return (-1); + } + + if (mgrd[beam.tx][beam.ty] == NON_MONSTER || beam.isMe) { canned_msg(MSG_SPELL_FIZZLES); @@ -250,6 +263,10 @@ bool airstrike(int power) hurted = random2( random2(12) + (random2(power) / 6) + (random2(power) / 7) ); hurted -= random2(1 + monster->armour_class); + if ( mons_flies(monster) ) { + hurted *= 3; + hurted /= 2; + } if (hurted < 0) hurted = 0; @@ -269,7 +286,7 @@ bool airstrike(int power) return (success); } // end airstrike() -bool cast_bone_shards(int power) +int cast_bone_shards(int power) { bool success = false; struct bolt beam; @@ -282,8 +299,11 @@ bool cast_bone_shards(int power) } else if (you.inv[you.equip[EQ_WEAPON]].sub_type != CORPSE_SKELETON) mpr("The corpse collapses into a mass of pulpy flesh."); - else if (spell_direction(spelld, beam) != -1) + else { + if (spell_direction(spelld, beam) == -1) + return (-1); + // practical max of 100 * 15 + 3000 = 4500 // actual max of 200 * 15 + 3000 = 6000 power *= 15; @@ -430,10 +450,10 @@ void dancing_weapon(int pow, bool force_hostile) const int wpn = you.equip[EQ_WEAPON]; - // See if weilded item is appropriate: + // See if wielded item is appropriate: if (wpn == -1 || you.inv[wpn].base_type != OBJ_WEAPONS - || launches_things( you.inv[wpn].sub_type ) + || is_range_weapon( you.inv[wpn] ) || is_fixed_artefact( you.inv[wpn] )) { goto failed_spell; @@ -478,12 +498,15 @@ void dancing_weapon(int pow, bool force_hostile) you.equip[EQ_WEAPON] = -1; menv[summs].inv[MSLOT_WEAPON] = i; - menv[summs].number = mitm[i].colour; + menv[summs].colour = mitm[i].colour; return; failed_spell: - mpr("Your weapon vibrates crazily for a second."); + if ( wpn != -1 ) + mpr("Your weapon vibrates crazily for a second."); + else + mprf(MSGCH_PLAIN, "Your %s twitch.", your_hand(true)); } // end dancing_weapon() static bool monster_on_level(int monster) @@ -565,7 +588,7 @@ bool allow_control_teleport( bool silent ) } // Tell the player why if they have teleport control. - if (!ret && you.attribute[ATTR_CONTROL_TELEPORT] && !silent) + if (!ret && player_control_teleport() && !silent) mpr("A powerful magic prevents control of your teleportation."); return ret; @@ -599,8 +622,8 @@ void you_teleport(void) void you_teleport2( bool allow_control, bool new_abyss_area ) { bool is_controlled = (allow_control && !you.conf - && you.attribute[ATTR_CONTROL_TELEPORT] - && allow_control_teleport()); + && player_control_teleport() + && allow_control_teleport()); if (scan_randarts(RAP_PREVENT_TELEPORTATION)) { @@ -608,11 +631,8 @@ void you_teleport2( bool allow_control, bool new_abyss_area ) return; } - // after this point, we're guaranteed to teleport. Turn off auto-butcher. - // corpses still get butchered, but at least we don't get a silly message. - if (current_delay_action() == DELAY_BUTCHER) - stop_delay(); - + // after this point, we're guaranteed to teleport. Kill the appropriate + // delays. interrupt_activity( AI_TELEPORT ); if (you.duration[DUR_CONDENSATION_SHIELD] > 0) @@ -639,7 +659,7 @@ void you_teleport2( bool allow_control, bool new_abyss_area ) mpr("Expect minor deviation."); more(); - show_map(plox); + show_map(plox, false); redraw_screen(); @@ -854,7 +874,7 @@ bool project_noise(void) mpr( "Choose the noise's source (press '.' or delete to select)." ); more(); - show_map(plox); + show_map(plox, false); redraw_screen(); @@ -868,7 +888,7 @@ bool project_noise(void) // player can use this spell to "sound out" the dungeon -- bwr if (plox[0] > 1 && plox[0] < (GXM - 2) && plox[1] > 1 && plox[1] < (GYM - 2) - && grd[ plox[0] ][ plox[1] ] > DNGN_LAST_SOLID_TILE) + && !grid_is_solid(grd[ plox[0] ][ plox[1] ])) { noisy( 30, plox[0], plox[1] ); success = true; @@ -877,12 +897,12 @@ bool project_noise(void) if (!silenced( you.x_pos, you.y_pos )) { if (!success) - mpr("You hear a dull thud."); + mpr("You hear a dull thud.", MSGCH_SOUND); else { snprintf( info, INFO_SIZE, "You hear a %svoice call your name.", (see_grid( plox[0], plox[1] ) ? "distant " : "") ); - mpr( info ); + mpr( info , MSGCH_SOUND ); } } } @@ -926,7 +946,7 @@ bool recall(char type_recalled) if (!mons_friendly(monster)) continue; - if (monster_habitat(monster->type) != DNGN_FLOOR) + if (!monster_habitable_grid(monster, DNGN_FLOOR)) continue; if (type_recalled == 1) @@ -934,19 +954,19 @@ bool recall(char type_recalled) /* abomin created by twisted res, although it gets others too */ if ( !((monster->type == MONS_ABOMINATION_SMALL || monster->type == MONS_ABOMINATION_LARGE) - && (monster->number == BROWN - || monster->number == RED - || monster->number == LIGHTRED)) ) + && (monster->colour == BROWN + || monster->colour == RED + || monster->colour == LIGHTRED)) ) { if (monster->type != MONS_REAPER - && mons_holiness(monster->type) != MH_UNDEAD) + && mons_holiness(monster) != MH_UNDEAD) { continue; } } } - if (empty_surrounds(you.x_pos, you.y_pos, DNGN_FLOOR, false, empty)) + if (empty_surrounds(you.x_pos, you.y_pos, DNGN_FLOOR, 3, false, empty)) { // clear old cell pointer -- why isn't there a function for moving a monster? mgrd[monster->x][monster->y] = NON_MONSTER; @@ -972,7 +992,7 @@ bool recall(char type_recalled) return (success); } // end recall() -void portal(void) +int portal(void) { char dir_sign = 0; unsigned char keyi; @@ -982,10 +1002,12 @@ void portal(void) if (!player_in_branch( BRANCH_MAIN_DUNGEON )) { mpr("This spell doesn't work here."); + return (-1); } else if (grd[you.x_pos][you.y_pos] != DNGN_FLOOR) { mpr("You must find a clear area in which to cast this spell."); + return (-1); } else { @@ -1021,7 +1043,7 @@ void portal(void) if (keyi == 'x') { canned_msg(MSG_OK); - return; // an early return {dlb} + return (-1); // an early return {dlb} } } @@ -1035,7 +1057,7 @@ void portal(void) if (keyi == 'x') { canned_msg(MSG_OK); - return; // another early return {dlb} + return (-1); // another early return {dlb} } if (!(keyi < '1' || keyi > '9')) @@ -1065,7 +1087,7 @@ void portal(void) untag_followers(); } - return; + return (1); } // end portal() bool cast_death_channel(int power) diff --git a/crawl-ref/source/spells3.h b/crawl-ref/source/spells3.h index 5b10dd3026..9eea54e646 100644 --- a/crawl-ref/source/spells3.h +++ b/crawl-ref/source/spells3.h @@ -24,14 +24,14 @@ bool allow_control_teleport( bool silent = false ); /* *********************************************************************** * called from: spell * *********************************************************************** */ -bool airstrike(int power); +int airstrike(int power); // updated 24may2000 {dlb} /* *********************************************************************** * called from: spell * *********************************************************************** */ -bool cast_bone_shards(int power); +int cast_bone_shards(int power); // updated 24may2000 {dlb} @@ -52,14 +52,14 @@ void cast_poison_ammo(void); /* *********************************************************************** * called from: ability - spell * *********************************************************************** */ -void cast_selective_amnesia(bool force); +bool cast_selective_amnesia(bool force); // updated 24may2000 {dlb} /* *********************************************************************** * called from: ability - spell * *********************************************************************** */ -bool cast_smiting(int power); +int cast_smiting(int power); // updated 24may2000 {dlb} @@ -94,7 +94,7 @@ bool entomb(void); /* *********************************************************************** * called from: spell * *********************************************************************** */ -void portal(void); +int portal(void); // updated 24may2000 {dlb} diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc index 26b09c3901..4f005c81ac 100644 --- a/crawl-ref/source/spells4.cc +++ b/crawl-ref/source/spells4.cc @@ -4,6 +4,8 @@ * other neglected areas of Crawl magic ;^) * Written by: Copyleft Josh Fishman 1999-2000, All Rights Preserved * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 29jul2000 jdj Made a zillion functions static. @@ -28,6 +30,7 @@ #include "effects.h" #include "it_use2.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "invent.h" #include "misc.h" @@ -57,7 +60,6 @@ enum DEBRIS // jmf: add for shatter, dig, and Giants to throw NUM_DEBRIS }; // jmf: ...and I'll actually implement the items Real Soon Now... -static bool mons_can_host_shuggoth(int type); // static int make_a_random_cloud(int x, int y, int pow, int ctype); static int make_a_rot_cloud(int x, int y, int pow, int ctype); static int quadrant_blink(int x, int y, int pow, int garbage); @@ -189,6 +191,7 @@ static int shatter_monsters(int x, int y, int pow, int garbage) case MONS_ICE_BEAST: // 3/2 damage case MONS_SIMULACRUM_SMALL: case MONS_SIMULACRUM_LARGE: + case MONS_SILVER_STATUE: dam_dice.num = 4; break; @@ -199,6 +202,7 @@ static int shatter_monsters(int x, int y, int pow, int garbage) case MONS_STONE_GOLEM: case MONS_IRON_GOLEM: case MONS_CRYSTAL_GOLEM: + case MONS_ORANGE_STATUE: case MONS_EARTH_ELEMENTAL: case MONS_GARGOYLE: case MONS_SKELETAL_DRAGON: @@ -298,7 +302,7 @@ static int shatter_items(int x, int y, int pow, int garbage) if (broke_stuff) { if (!silenced(x, y) && !silenced(you.x_pos, you.y_pos)) - mpr("You hear glass break."); + mpr("You hear glass break.", MSGCH_SOUND); return 1; } @@ -394,7 +398,7 @@ void cast_shatter(int pow) noisy( 30, you.x_pos, you.y_pos ); snprintf(info, INFO_SIZE, "The dungeon %s!", (sil ? "shakes" : "rumbles")); - mpr(info); + mpr(info, (sil? MSGCH_PLAIN : MSGCH_SOUND)); switch (you.attribute[ATTR_TRANSFORMATION]) { @@ -434,7 +438,7 @@ void cast_shatter(int pow) pow, rad, 0 ); if (dest && !sil) - mpr("Ka-crash!"); + mpr("Ka-crash!", MSGCH_SOUND); } // end cast_shatter() // cast_forescry: raises evasion (by 8 currently) via divination @@ -492,7 +496,7 @@ static void cast_detect_magic(int pow) } mpr("Which direction?", MSGCH_PROMPT); - direction( bmove, DIR_DIR ); + direction( bmove, DIR_DIR, TARG_ANY, true ); if (!bmove.isValid) { @@ -708,6 +712,7 @@ void cast_sticks_to_snakes(int pow) || you.inv[ weapon ].sub_type == WPN_GIANT_CLUB || you.inv[ weapon ].sub_type == WPN_GIANT_SPIKED_CLUB || you.inv[ weapon ].sub_type == WPN_BOW + || you.inv[ weapon ].sub_type == WPN_LONGBOW || you.inv[ weapon ].sub_type == WPN_ANCUS || you.inv[ weapon ].sub_type == WPN_HALBERD || you.inv[ weapon ].sub_type == WPN_GLAIVE @@ -720,7 +725,7 @@ void cast_sticks_to_snakes(int pow) // ogres, and most importantly ogre magi). Still it's unlikely // any character is strong enough to bother lugging a few of // these around. -- bwr - if (mass_item( you.inv[ weapon ] ) < 500) + if (item_mass( you.inv[ weapon ] ) < 300) mon = MONS_SNAKE; else mon = MONS_BROWN_SNAKE; @@ -851,7 +856,7 @@ static int sleep_monsters(int x, int y, int pow, int garbage) int mnstr = mgrd[x][y]; if (mnstr == NON_MONSTER) return 0; - if (mons_holiness( menv[mnstr].type ) != MH_NATURAL) return 0; + if (mons_holiness(&menv[mnstr]) != MH_NATURAL) return 0; if (check_mons_resist_magic( &menv[mnstr], pow )) return 0; // Why shouldn't we be able to sleep friendly monsters? -- bwr @@ -864,7 +869,7 @@ static int sleep_monsters(int x, int y, int pow, int garbage) menv[mnstr].behaviour = BEH_SLEEP; mons_add_ench( &menv[mnstr], ENCH_SLEEP_WARY ); - if (mons_flag( menv[mnstr].type, M_COLD_BLOOD ) && coinflip()) + if (mons_class_flag( menv[mnstr].type, M_COLD_BLOOD ) && coinflip()) mons_add_ench( &menv[mnstr], ENCH_SLOW ); return 1; @@ -884,7 +889,7 @@ static int tame_beast_monsters(int x, int y, int pow, int garbage) struct monsters *monster = &menv[which_mons]; - if (mons_holiness(monster->type) != MH_NATURAL) return 0; + if (mons_holiness(monster) != MH_NATURAL) return 0; if (mons_intel_type(monster->type) != I_ANIMAL) return 0; if (mons_friendly(monster)) return 0; @@ -1012,7 +1017,7 @@ static int ignite_poison_monsters(int x, int y, int pow, int garbage) struct monsters *const mon = &menv[ mon_index ]; // Monsters which have poison corpses or poisonous attacks: - if (mons_corpse_thingy( mon->type ) == CE_POISONOUS + if (mons_corpse_effect( mon->type ) == CE_POISONOUS || mon->type == MONS_GIANT_ANT || mon->type == MONS_SMALL_SNAKE || mon->type == MONS_SNAKE @@ -1259,6 +1264,8 @@ static int discharge_monsters( int x, int y, int pow, int garbage ) mpr( "You are struck by lightning." ); damage = 3 + random2( 5 + pow / 10 ); damage = check_your_resists( damage, BEAM_ELECTRICITY ); + if ( player_is_levitating() ) + damage /= 2; ouch( damage, 0, KILLED_BY_WILD_MAGIC ); } else if (mon == NON_MONSTER) @@ -1707,7 +1714,7 @@ static int intoxicate_monsters(int x, int y, int pow, int garbage) return 0; if (mons_intel(menv[mon].type) < I_NORMAL) return 0; - if (mons_holiness(menv[mon].type) != MH_NATURAL) + if (mons_holiness(&menv[mon]) != MH_NATURAL) return 0; if (mons_res_poison(&menv[mon]) > 0) return 0; @@ -1746,7 +1753,7 @@ static int glamour_monsters(int x, int y, int pow, int garbage) if (mons_intel(menv[mon].type) < I_NORMAL) return (0); - if (mons_holiness(mon) != MH_NATURAL) + if (mons_class_holiness(mon) != MH_NATURAL) return (0); if (!mons_is_humanoid( menv[mon].type )) @@ -1893,7 +1900,8 @@ void cast_evaporate(int pow) struct dist spelld; struct bolt beem; - const int potion = prompt_invent_item( "Throw which potion?", OBJ_POTIONS ); + const int potion = + prompt_invent_item( "Throw which potion?", MT_INVSELECT, OBJ_POTIONS ); if (potion == -1) { @@ -1914,7 +1922,7 @@ void cast_evaporate(int pow) message_current_target(); - direction( spelld, DIR_NONE, TARG_ENEMY ); + direction( spelld, DIR_NONE, TARG_ENEMY, true ); if (!spelld.isValid) { @@ -1922,24 +1930,23 @@ void cast_evaporate(int pow) return; } - beem.target_x = spelld.tx; - beem.target_y = spelld.ty; + beem.set_target(spelld); beem.source_x = you.x_pos; beem.source_y = you.y_pos; - strcpy( beem.beam_name, "potion" ); + beem.name = "potion"; beem.colour = you.inv[potion].colour; beem.range = 9; beem.rangeMax = 9; beem.type = SYM_FLASK; beem.beam_source = MHITYOU; beem.thrower = KILL_YOU_MISSILE; - beem.aux_source = NULL; - beem.isBeam = false; - beem.isTracer = false; + beem.aux_source.clear(); + beem.is_beam = false; + beem.is_tracer = false; - beem.hit = you.dex / 2 + roll_dice( 2, you.skills[SK_THROWING] / 2 + 1 ); + beem.hit = you.dex / 2 + roll_dice( 2, you.skills[SK_RANGED_COMBAT] / 2 + 1 ); beem.damage = dice_def( 1, 0 ); // no damage, just producing clouds beem.ench_power = pow; // used for duration only? @@ -2016,7 +2023,7 @@ void cast_evaporate(int pow) } if (coinflip()) - exercise( SK_THROWING, 1 ); + exercise( SK_RANGED_COMBAT, 1 ); fire_beam(beem); @@ -2103,7 +2110,7 @@ void cast_fulsome_distillation( int powc ) break; default: - switch (mons_corpse_thingy( mitm[corpse].plus )) + switch (mons_corpse_effect( mitm[corpse].plus )) { case CE_CLEAN: potion_type = (power_up ? POT_CONFUSION : POT_WATER); @@ -2208,7 +2215,7 @@ static int rot_living(int x, int y, int pow, int message) if (mon == NON_MONSTER) return 0; - if (mons_holiness(menv[mon].type) != MH_NATURAL) + if (mons_holiness(&menv[mon]) != MH_NATURAL) return 0; if (check_mons_resist_magic(&menv[mon], pow)) @@ -2240,7 +2247,7 @@ static int rot_undead(int x, int y, int pow, int garbage) if (mon == NON_MONSTER) return 0; - if (mons_holiness(menv[mon].type) != MH_UNDEAD) + if (mons_holiness(&menv[mon]) != MH_UNDEAD) return 0; if (check_mons_resist_magic(&menv[mon], pow)) @@ -2315,7 +2322,7 @@ void do_monster_rot(int mon) { int damage = 1 + random2(3); - if (mons_holiness(menv[mon].type) == MH_UNDEAD && random2(5)) + if (mons_holiness(&menv[mon]) == MH_UNDEAD && random2(5)) { apply_area_cloud(make_a_normal_cloud, menv[mon].x, menv[mon].y, 10, 1, CLOUD_MIASMA); @@ -2361,7 +2368,7 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike const char *what = NULL; mpr("Fragment what (e.g. a wall)?", MSGCH_PROMPT); - direction( beam, DIR_TARGET, TARG_ENEMY ); + direction( beam, DIR_TARGET, TARG_ENEMY, true ); if (!beam.isValid) { @@ -2372,13 +2379,12 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike //FIXME: if (player typed '>' to attack floor) goto do_terrain; blast.beam_source = MHITYOU; blast.thrower = KILL_YOU; - blast.aux_source = NULL; + blast.aux_source.clear(); blast.ex_size = 1; // default blast.type = '#'; blast.colour = 0; - blast.target_x = beam.tx; - blast.target_y = beam.ty; - blast.isTracer = false; + blast.set_target(beam); + blast.is_tracer = false; blast.flavour = BEAM_FRAG; // Number of dice vary... 3 is easy/common, but it can get as high as 6. @@ -2405,7 +2411,7 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike case MONS_SIMULACRUM_SMALL: case MONS_SIMULACRUM_LARGE: explode = true; - strcpy(blast.beam_name, "icy blast"); + blast.name = "icy blast"; blast.colour = WHITE; blast.damage.num = 2; blast.flavour = BEAM_ICE; @@ -2421,7 +2427,7 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike snprintf( info, INFO_SIZE, "The sk%s explodes into sharp fragments of bone!", (menv[mon].type == MONS_FLYING_SKULL) ? "ull" : "eleton"); - strcpy(blast.beam_name, "blast of bone shards"); + blast.name = "blast of bone shards"; blast.colour = LIGHTGREY; @@ -2452,7 +2458,7 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike case MONS_IRON_GOLEM: case MONS_METAL_GARGOYLE: explode = true; - strcpy( blast.beam_name, "blast of metal fragments" ); + blast.name = "blast of metal fragments"; blast.colour = CYAN; blast.damage.num = 4; if (player_hurt_monster(mon, roll_dice( blast.damage ))) @@ -2465,17 +2471,38 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike case MONS_GARGOYLE: explode = true; blast.ex_size = 2; - strcpy(blast.beam_name, "blast of rock fragments"); + blast.name = "blast of rock fragments"; blast.colour = BROWN; blast.damage.num = 3; if (player_hurt_monster(mon, roll_dice( blast.damage ))) blast.damage.num += 1; break; + case MONS_SILVER_STATUE: + case MONS_ORANGE_STATUE: + explode = true; + blast.ex_size = 2; + if (menv[mon].type == MONS_SILVER_STATUE) + { + blast.name = "blast of silver fragments"; + blast.colour = WHITE; + blast.damage.num = 3; + } + else + { + blast.name = "blast of orange crystal shards"; + blast.colour = LIGHTRED; + blast.damage.num = 6; + } + + if (player_hurt_monster(mon, roll_dice( blast.damage ))) + blast.damage.num += 2; + break; + case MONS_CRYSTAL_GOLEM: explode = true; blast.ex_size = 2; - strcpy(blast.beam_name, "blast of crystal shards"); + blast.name = "blast of crystal shards"; blast.colour = WHITE; blast.damage.num = 4; if (player_hurt_monster(mon, roll_dice( blast.damage ))) @@ -2524,7 +2551,7 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike explode = true; - strcpy(blast.beam_name, "blast of rock fragments"); + blast.name = "blast of rock fragments"; blast.damage.num = 3; if (blast.colour == 0) blast.colour = LIGHTGREY; @@ -2558,7 +2585,7 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike } explode = true; - strcpy( blast.beam_name, "blast of metal fragments" ); + blast.name = "blast of metal fragments"; blast.damage.num = 4; if (okay_to_dest && pow >= 80 && random2(500) < pow / 5) @@ -2586,7 +2613,7 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike explode = true; blast.ex_size = 2; - strcpy(blast.beam_name, "blast of crystal shards"); + blast.name = "blast of crystal shards"; blast.damage.num = 5; if (okay_to_dest @@ -2619,7 +2646,7 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike explode = true; hole = false; // to hit monsters standing on traps - strcpy( blast.beam_name, "blast of fragments" ); + blast.name = "blast of fragments"; blast.colour = env.floor_colour; // in order to blend in blast.damage.num = 2; @@ -2645,7 +2672,7 @@ void cast_fragmentation(int pow) // jmf: ripped idea from airstrike case DNGN_STONE_ARCH: // floor -- small explosion explode = true; hole = false; // to hit monsters standing on doors - strcpy( blast.beam_name, "blast of rock fragments" ); + blast.name = "blast of rock fragments"; blast.colour = LIGHTGREY; blast.damage.num = 2; break; @@ -2698,7 +2725,7 @@ void cast_twist(int pow) if (pow > 25) pow = 25; - // Get target, using DIR_TARGET for targetting only, + // Get target, using DIR_TARGET for targetting only, // since we don't use fire_beam() for this spell. if (spell_direction(targ, tmp, DIR_TARGET) == -1) return; @@ -2750,7 +2777,7 @@ void cast_far_strike(int pow) struct dist targ; struct bolt tmp; // used, but ignored - // Get target, using DIR_TARGET for targetting only, + // Get target, using DIR_TARGET for targetting only, // since we don't use fire_beam() for this spell. if (spell_direction(targ, tmp, DIR_TARGET) == -1) return; @@ -2863,34 +2890,34 @@ void cast_far_strike(int pow) return; } // end cast_far_strike() -void cast_apportation(int pow) +int cast_apportation(int pow) { struct dist beam; mpr("Pull items from where?"); - direction( beam, DIR_TARGET ); + direction( beam, DIR_TARGET, TARG_ANY, true ); if (!beam.isValid) { - canned_msg(MSG_SPELL_FIZZLES); - return; + canned_msg(MSG_OK); + return (-1); } // it's already here! if (beam.isMe) { mpr( "That's just silly." ); - return; + return (-1); } // Protect the player from destroying the item const int grid = grd[ you.x_pos ][ you.y_pos ]; - if (grid == DNGN_LAVA || grid == DNGN_DEEP_WATER) + if (grid_destroys_items(grid)) { mpr( "That would be silly while over this terrain!" ); - return; + return (0); } // If this is ever changed to allow moving objects that can't @@ -2906,7 +2933,7 @@ void cast_apportation(int pow) if (!see_grid( beam.tx, beam.ty )) { mpr( "You cannot see there!" ); - return; + return (0); } // Let's look at the top item in that square... @@ -2926,11 +2953,11 @@ void cast_apportation(int pow) else mpr( "This spell does not work on creatures." ); - return; + return (0); } // mass of one unit - const int unit_mass = mass_item( mitm[ item ] ); + const int unit_mass = item_mass( mitm[ item ] ); // assume we can pull everything int max_units = mitm[ item ].quantity; @@ -2946,9 +2973,11 @@ void cast_apportation(int pow) if (max_units <= 0) { mpr( "The mass is resisting your pull." ); - return; + return (0); } + int done = 0; + // Failure should never really happen after all the above checking, // but we'll handle it anyways... if (move_top_item( beam.tx, beam.ty, you.x_pos, you.y_pos )) @@ -2965,9 +2994,12 @@ void cast_apportation(int pow) (mitm[ item ].quantity > 1) ? "s" : "" ); mpr( info ); } + done = 1; } else mpr( "The spell fails." ); + + return (done); } void cast_sandblast(int pow) @@ -3010,63 +3042,6 @@ void cast_sandblast(int pow) } } // end cast_sandblast() -static bool mons_can_host_shuggoth(int type) //jmf: simplified -{ - if (mons_holiness(type) != MH_NATURAL) - return false; - if (mons_flag(type, M_WARM_BLOOD)) - return true; - - return false; -} - -void cast_shuggoth_seed(int powc) -{ - struct dist beam; - int i; - - mpr("Sow seed in whom?", MSGCH_PROMPT); - - direction( beam, DIR_TARGET, TARG_ENEMY ); - - if (!beam.isValid) - { - mpr("You feel a distant frustration."); - return; - } - - if (beam.isMe) - { - if (!you.is_undead) - { - you.duration[DUR_INFECTED_SHUGGOTH_SEED] = 10; - mpr("A deathly dread twitches in your chest."); - } - else - mpr("You feel a distant frustration."); - } - - i = mgrd[beam.tx][beam.ty]; - - if (i == NON_MONSTER) - { - mpr("You feel a distant frustration."); - return; - } - - if (mons_can_host_shuggoth(menv[i].type)) - { - if (random2(powc) > 100) - mons_add_ench(&menv[i], ENCH_YOUR_SHUGGOTH_III); - else - mons_add_ench(&menv[i], ENCH_YOUR_SHUGGOTH_IV); - - simple_monster_message(&menv[i], " twitches."); - } - - return; -} - void cast_condensation_shield(int pow) { if (you.equip[EQ_SHIELD] != -1 || you.fire_shield) @@ -3109,8 +3084,8 @@ static int quadrant_blink(int x, int y, int pow, int garbage) // setup: Brent's new algorithm // we are interested in two things: distance of a test point from - // the ideal 'line', and the distance of a test point from two - // actual points, one in the 'correct' direction and one in the + // the ideal 'line', and the distance of a test point from two + // actual points, one in the 'correct' direction and one in the // 'incorrect' direction. // scale distance by 10 for more interesting numbers. @@ -3164,10 +3139,9 @@ static int quadrant_blink(int x, int y, int pow, int garbage) return (1); } -void cast_semi_controlled_blink(int pow) +int cast_semi_controlled_blink(int pow) { - apply_one_neighbouring_square(quadrant_blink, pow); - return; + return apply_one_neighbouring_square(quadrant_blink, pow); } void cast_stoneskin(int pow) diff --git a/crawl-ref/source/spells4.h b/crawl-ref/source/spells4.h index 94cc74fcac..29580a7b29 100644 --- a/crawl-ref/source/spells4.h +++ b/crawl-ref/source/spells4.h @@ -3,6 +3,8 @@ * Summary: Yet More Spell Function Declarations * Written by: Josh Fishman * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * <2> 12jul2000 jmf Fixed random (undocumented) damage * <1> 07jan2000 jmf Created @@ -30,7 +32,7 @@ void cast_fragmentation(int powc); void cast_twist(int powc); void cast_far_strike(int powc); void cast_swap(int powc); -void cast_apportation(int powc); +int cast_apportation(int powc); void cast_glamour(int pow); void cast_ignite_poison(int pow); void cast_intoxicate(int pow); @@ -45,6 +47,7 @@ void cast_silence(int pow); void cast_sticks_to_snakes(int pow); void cast_summon_butterflies(int pow); void cast_summon_dragon(int pow); +void cast_chain_lightning( int pow ); void cast_conjure_ball_lightning(int pow); void cast_summon_large_mammal(int pow); void cast_tame_beasts(int pow); @@ -55,7 +58,7 @@ void cast_stoneskin(int pow); void cast_shuggoth_seed(int powc); void make_shuggoth(int x, int y, int hp); -void cast_semi_controlled_blink(int pow); +int cast_semi_controlled_blink(int pow); #endif diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index c6efbae9cf..4f6e040563 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -3,6 +3,8 @@ * Summary: Spellbook/Staff contents array and management functions * Written by: Josh Fishman * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * 24jun2000 jmf Changes to many books; addition of Assassinations; @@ -26,8 +28,10 @@ #include "debug.h" #include "delay.h" +#include "food.h" #include "invent.h" #include "itemname.h" +#include "itemprop.h" #include "items.h" #include "it_use3.h" #include "player.h" @@ -144,8 +148,8 @@ static int spellbook_template_array[NUMBER_SPELLBOOKS][SPELLBOOK_SIZE] = SPELL_CONDENSATION_SHIELD, SPELL_BOLT_OF_COLD, SPELL_OZOCUBUS_REFRIGERATION, - SPELL_MASS_SLEEP, SPELL_SIMULACRUM, + SPELL_MASS_SLEEP, SPELL_NO_SPELL, }, @@ -331,15 +335,10 @@ static int spellbook_template_array[NUMBER_SPELLBOOKS][SPELLBOOK_SIZE] = SPELL_BACKLIGHT, SPELL_REPEL_MISSILES, SPELL_SLEEP, -#ifdef USE_SILENCE_CODE - SPELL_SILENCE, -#endif SPELL_CONFUSE, SPELL_ENSLAVEMENT, + SPELL_SILENCE, SPELL_INVISIBILITY, -#ifndef USE_SILENCE_CODE - SPELL_NO_SPELL, -#endif SPELL_NO_SPELL, }, // 27 - Book of Demonology -- Vehumet special @@ -382,19 +381,14 @@ static int spellbook_template_array[NUMBER_SPELLBOOKS][SPELLBOOK_SIZE] = // 29 - Book of the Sky {0, -#ifdef USE_SILENCE_CODE - SPELL_SILENCE, -#endif SPELL_SUMMON_ELEMENTAL, SPELL_INSULATION, SPELL_AIRSTRIKE, SPELL_FLY, + SPELL_SILENCE, SPELL_DEFLECT_MISSILES, SPELL_LIGHTNING_BOLT, SPELL_CONJURE_BALL_LIGHTNING, -#ifndef USE_SILENCE_CODE - SPELL_NO_SPELL, -#endif }, // 30 - Book of Divinations @@ -410,11 +404,11 @@ static int spellbook_template_array[NUMBER_SPELLBOOKS][SPELLBOOK_SIZE] = }, // 31 - Book of the Warp {0, - SPELL_CONTROLLED_BLINK, SPELL_BANISHMENT, + SPELL_WARP_BRAND, SPELL_DISPERSAL, SPELL_PORTAL, - SPELL_NO_SPELL, + SPELL_CONTROLLED_BLINK, SPELL_NO_SPELL, SPELL_NO_SPELL, SPELL_NO_SPELL, @@ -434,7 +428,7 @@ static int spellbook_template_array[NUMBER_SPELLBOOKS][SPELLBOOK_SIZE] = {0, SPELL_ISKENDERUNS_MYSTIC_BLAST, SPELL_POISON_ARROW, - SPELL_ORB_OF_ELECTROCUTION, // XXX: chain lightning? + SPELL_CHAIN_LIGHTNING, SPELL_LEHUDIBS_CRYSTAL_SPEAR, SPELL_ICE_STORM, SPELL_FIRE_STORM, @@ -520,7 +514,7 @@ static int spellbook_template_array[NUMBER_SPELLBOOKS][SPELLBOOK_SIZE] = SPELL_BOLT_OF_IRON, SPELL_TOMB_OF_DOROKLOHE, SPELL_SHATTER, - SPELL_NO_SPELL, + SPELL_NO_SPELL }, // 41 - manuals of all kinds {0, @@ -800,7 +794,7 @@ unsigned char spellbook_contents( item_def &book, int action, char str_pass[ ITEMNAME_SIZE ]; item_name( book, DESC_CAP_THE, str_pass ); - out.cprintf( str_pass ); + out.cprintf( "%s", str_pass ); out.cprintf( EOL EOL " Spells Type Level" EOL ); @@ -824,7 +818,9 @@ unsigned char spellbook_contents( item_def &book, int action, if (action == RBOOK_USE_STAFF) { if (you.experience_level >= level_diff - && you.magic_points >= level_diff) + && (book.base_type == OBJ_BOOKS? + you.magic_points >= level_diff + : book.plus >= level_diff * ROD_CHARGE_MULT)) { colour = LIGHTGREY; } @@ -850,12 +846,12 @@ unsigned char spellbook_contents( item_def &book, int action, char strng[2]; strng[0] = index_to_letter(spelcount); - strng[1] = '\0'; + strng[1] = 0; out.cprintf(strng); out.cprintf(" - "); - out.cprintf( spell_title(spell_types[j]) ); + out.cprintf( "%s", spell_title(spell_types[j]) ); out.gotoxy( 35, -1 ); @@ -872,7 +868,7 @@ unsigned char spellbook_contents( item_def &book, int action, if (already) out.cprintf( "/" ); - out.cprintf( spelltype_name( 1 << i ) ); + out.cprintf( "%s", spelltype_name( 1 << i ) ); already = true; } } @@ -897,7 +893,7 @@ unsigned char spellbook_contents( item_def &book, int action, out.cprintf( "Select a spell to cast." EOL ); break; - case RBOOK_MEMORIZE: + case RBOOK_MEMORISE: out.cprintf( "Select a spell to memorise (%d level%s available)." EOL, spell_levels, (spell_levels == 1) ? "" : "s" ); break; @@ -1063,7 +1059,8 @@ static bool which_spellbook( int &book, int &spell ) mpr( info ); - book = prompt_invent_item( "Memorise from which spellbook?", OBJ_BOOKS ); + book = prompt_invent_item("Memorise from which spellbook?", MT_INVSELECT, + OBJ_BOOKS ); if (book == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -1083,7 +1080,7 @@ static bool which_spellbook( int &book, int &spell ) return (false); } - spell = read_book( you.inv[book], RBOOK_MEMORIZE ); + spell = read_book( you.inv[book], RBOOK_MEMORISE ); clrscr(); return (true); @@ -1156,7 +1153,7 @@ unsigned char read_book( item_def &book, int action ) // reading spell descriptions doesn't take time: if (action != RBOOK_READ_SPELL) - you.turn_is_over = 1; + you.turn_is_over = true; set_ident_flags( book, ISFLAG_KNOW_TYPE ); @@ -1225,7 +1222,6 @@ bool learn_spell(void) { int chance = 0; int levels_needed = 0; - unsigned char keyin; int book, spell; int index; @@ -1247,45 +1243,47 @@ bool learn_spell(void) if (!which_spellbook( book, spell )) return (false); + mesclr(true); + redraw_screen(); + if (spell < 'A' || (spell > 'Z' && spell < 'a') || spell > 'z') { - whatt: - redraw_screen(); - mpr("What?"); + canned_msg( MSG_HUH ); return (false); } index = letter_to_index( spell ); - if (index >= SPELLBOOK_SIZE) - goto whatt; - - if (!is_valid_spell_in_book( book, index )) - goto whatt; + if (index >= SPELLBOOK_SIZE || + !is_valid_spell_in_book( book, index )) + { + canned_msg( MSG_HUH ); + return (false); + } unsigned int specspell = which_spell_in_book(you.inv[book].sub_type,index); if (specspell == SPELL_NO_SPELL) - goto whatt; + { + canned_msg( MSG_HUH ); + return (false); + } // You can always memorise selective amnesia: if (you.spell_no == 21 && specspell != SPELL_SELECTIVE_AMNESIA) { - redraw_screen(); mpr("Your head is already too full of spells!"); return (false); } if (you.is_undead && spell_typematch(specspell, SPTYP_HOLY)) { - redraw_screen(); mpr("You cannot use this type of magic!"); return (false); } if (undead_cannot_memorise(specspell, you.is_undead)) { - redraw_screen(); mpr("You cannot use this spell."); return (false); } @@ -1294,9 +1292,8 @@ bool learn_spell(void) { if (you.spells[i] == specspell) { - redraw_screen(); mpr("You already know that spell!"); - you.turn_is_over = 1; + you.turn_is_over = true; return (false); } } @@ -1305,89 +1302,56 @@ bool learn_spell(void) if (player_spell_levels() < levels_needed) { - redraw_screen(); mpr("You can't memorise that many levels of magic yet!"); - you.turn_is_over = 1; + you.turn_is_over = true; return (false); } if (you.experience_level < spell_difficulty(specspell)) { - redraw_screen(); mpr("You're too inexperienced to learn that spell!"); - you.turn_is_over = 1; + you.turn_is_over = true; return (false); } - redraw_screen(); - chance = spell_fail(specspell); - - strcpy(info, "This spell is "); - - strcat(info, (chance >= 80) ? "very" : - (chance >= 60) ? "quite" : - (chance >= 45) ? "rather" : - (chance >= 30) ? "somewhat" - : "not that"); - - strcat(info, " "); - - int temp_rand = random2(3); - - strcat(info, (temp_rand == 0) ? "difficult" : - (temp_rand == 1) ? "tricky" : - (temp_rand == 2) ? "challenging" - : ""); - - strcat(info, " to "); - - temp_rand = random2(4); - - strcat(info, (temp_rand == 0) ? "memorise" : - (temp_rand == 1) ? "commit to memory" : - (temp_rand == 2) ? "learn" : - (temp_rand == 3) ? "absorb" - : ""); - - strcat(info, "."); - + int temp_rand1 = random2(3); + int temp_rand2 = random2(4); + + snprintf(info, INFO_SIZE, "This spell is %s %s to %s.", + ((chance >= 80) ? "very" : + (chance >= 60) ? "quite" : + (chance >= 45) ? "rather" : + (chance >= 30) ? "somewhat" + : "not that"), + ((temp_rand1 == 0) ? "difficult" : + (temp_rand1 == 1) ? "tricky" : + "challenging"), + ((temp_rand2 == 0) ? "memorise" : + (temp_rand2 == 1) ? "commit to memory" : + (temp_rand2 == 2) ? "learn" : + "absorb")); mpr(info); - strcpy(info, "Memorise "); - strcat(info, spell_title(specspell)); - strcat(info, "?"); - mpr(info); - - for (;;) + snprintf(info, INFO_SIZE, "Memorise %s?", spell_title(specspell)); + if ( !yesno(info, true, 0, false) ) { - keyin = getch(); - - if (keyin == 'n' || keyin == 'N') - { - redraw_screen(); - return (false); - } - - if (keyin == 'y' || keyin == 'Y') - break; + canned_msg( MSG_OK ); + return (false); } - mesclr( true ); - if (you.mutation[MUT_BLURRY_VISION] > 0 - && random2(4) < you.mutation[MUT_BLURRY_VISION]) + && random2(4) < you.mutation[MUT_BLURRY_VISION]) { mpr("The writing blurs into unreadable gibberish."); - you.turn_is_over = 1; + you.turn_is_over = true; return (false); } if (random2(40) + random2(40) + random2(40) < chance) { - redraw_screen(); mpr("You fail to memorise the spell."); - you.turn_is_over = 1; + you.turn_is_over = true; if (you.inv[ book ].sub_type == BOOK_NECRONOMICON) { @@ -1411,19 +1375,17 @@ bool learn_spell(void) #if WIZARD if (!you.wizard) return (false); - else if (!yesno("Memorize anyway?")) + else if (!yesno("Memorise anyway?")) return (false); #else return (false); #endif } - start_delay( DELAY_MEMORIZE, spell_difficulty( specspell ), specspell ); - - you.turn_is_over = 1; - redraw_screen(); + start_delay( DELAY_MEMORISE, spell_difficulty( specspell ), specspell ); + you.turn_is_over = true; - naughty( NAUGHTY_SPELLCASTING, 2 + random2(5) ); + did_god_conduct( DID_SPELL_CASTING, 2 + random2(5) ); return (true); } // end which_spell() @@ -1433,7 +1395,7 @@ int count_staff_spells(const item_def &item, bool need_id) if (item.base_type != OBJ_STAVES) return (-1); - if (need_id && item_not_ident( item, ISFLAG_KNOW_TYPE )) + if (need_id && !item_ident( item, ISFLAG_KNOW_TYPE )) return (0); const int stype = item.sub_type; @@ -1453,11 +1415,29 @@ int count_staff_spells(const item_def &item, bool need_id) return (num_spells); } +// Returns a measure of the rod spell power disrupted by a worn shield. +int rod_shield_leakage() +{ + const item_def *shield = player_shield(); + int leakage = 100; + + if (shield) + { + const int shield_type = shield->sub_type; + leakage = shield_type == ARM_BUCKLER? 125 : + shield_type == ARM_SHIELD ? 150 : + 200; + // Adjust for shields skill. + leakage -= ((leakage - 100) * 5 / 10) * you.skills[SK_SHIELDS] / 27; + } + return (leakage); +} + int staff_spell( int staff ) { int spell; unsigned char specspell; - int mana, diff; + int mana, diff, food, energy; FixedVector< int, SPELLBOOK_SIZE > spell_list; // converting sub_type into book index type @@ -1465,18 +1445,19 @@ int staff_spell( int staff ) // Spell staves are mostly for the benefit of non-spellcasters, so we're // not going to involve INT or Spellcasting skills for power. -- bwr - const int powc = 5 + you.skills[SK_EVOCATIONS] - + roll_dice( 2, you.skills[SK_EVOCATIONS] ); + int powc = (5 + you.skills[SK_EVOCATIONS] + + roll_dice( 2, you.skills[SK_EVOCATIONS] )) + * 100 + / rod_shield_leakage(); - if (you.inv[staff].sub_type < STAFF_SMITING - || you.inv[staff].sub_type >= STAFF_AIR) + const int staff_type = you.inv[staff].sub_type; + if (staff_type < STAFF_SMITING || staff_type >= STAFF_AIR) { - //mpr("That staff has no spells in it."); canned_msg(MSG_NOTHING_HAPPENS); - return (0); + return (-1); } - if (item_not_ident( you.inv[staff], ISFLAG_KNOW_TYPE )) + if (!item_ident( you.inv[staff], ISFLAG_KNOW_TYPE )) { set_ident_flags( you.inv[staff], ISFLAG_KNOW_TYPE ); you.wield_change = true; @@ -1508,7 +1489,11 @@ int staff_spell( int staff ) spell = get_ch(); if (spell == '?' || spell == '*') + { spell = read_book( you.inv[staff], RBOOK_USE_STAFF ); + // [ds] read_book sets turn_is_over. + you.turn_is_over = false; + } } if (spell < 'A' || (spell > 'Z' && spell < 'a') || spell > 'z') @@ -1524,39 +1509,74 @@ int staff_spell( int staff ) if (!is_valid_spell_in_book( staff, spell )) goto whattt; - specspell = which_spell_in_book( type, spell ); + specspell = which_spell_in_book( type, spell ); if (specspell == SPELL_NO_SPELL) goto whattt; - mana = spell_mana( specspell ); + mana = spell_mana( specspell ) * ROD_CHARGE_MULT; diff = spell_difficulty( specspell ); + food = spell_hunger( specspell ); - if (you.magic_points < mana || you.experience_level < diff) + if (food && (you.is_undead != US_UNDEAD + && (you.hunger_state < HS_HUNGRY || you.hunger <= food))) { - mpr("Your brain hurts!"); - confuse_player( 2 + random2(4) ); - you.turn_is_over = 1; - return (0); + mpr("You don't have the energy to cast that spell."); + return (-1); } - // Exercising the spell skills doesn't make very much sense given - // that spell staves are largely intended to supply spells to - // non-spellcasters, and they don't use spell skills to determine - // power in the same way that spellcasting does. -- bwr - // - // exercise_spell(specspell, true, true); + if (staff_type == STAFF_STRIKING) + mana /= ROD_CHARGE_MULT; - your_spells(specspell, powc, false); + if ((staff_type == STAFF_STRIKING? + you.magic_points < mana + : you.inv[staff].plus < mana) + || you.experience_level < diff) + { +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, + "Mana needed: %d, Staff plus: %d, Difficulty: %d, XP: %d", + mana, you.inv[staff].plus, diff, you.experience_level); +#endif + if (you.experience_level < diff) + mprf("You need to be at least level %d to use that.", diff); + else + mprf("%s have enough magic points.", + staff_type == STAFF_STRIKING? "You don't" : "The rod doesn't"); - dec_mp(spell_mana(specspell)); + // Don't lose a turn for trying to evoke without enough MP - that's + // needlessly cruel for an honest error. + return (-1); + } + + if (your_spells(specspell, powc, false) == -1) + return (-1); + + // dec_mp(spell_mana(specspell)); + if (staff_type != STAFF_STRIKING) + you.inv[staff].plus -= mana; + else { + you.magic_points -= mana; + you.redraw_magic_points = true; + } + + energy = player_energy(); + if (energy <= 0 && you.is_undead != US_UNDEAD) + { + food -= 10 * you.skills[SK_EVOCATIONS]; + if (food < diff * 5) + food = diff * 5; + + make_hungry( food, true ); + } - you.turn_is_over = 1; + you.wield_change = true; + you.turn_is_over = true; return (roll_dice( 1, 1 + spell_difficulty(specspell) / 2 )); whattt: mpr("What?"); - return (0); + return (-1); } // end staff_spell() diff --git a/crawl-ref/source/spl-book.h b/crawl-ref/source/spl-book.h index d6aa2843bc..6c34a596e2 100644 --- a/crawl-ref/source/spl-book.h +++ b/crawl-ref/source/spl-book.h @@ -3,6 +3,8 @@ * Summary: Some spellbook related functions. * Written by: Josh Fishman * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * 22mar2000 jmf Created @@ -13,6 +15,7 @@ #define SPL_BOOK_H #include "externs.h" +#include "menu.h" #include "FixVec.h" @@ -64,5 +67,6 @@ unsigned char spellbook_contents( item_def &book, int action, formatted_string *fs = NULL ); int count_staff_spells(const item_def &item, bool need_id); +int rod_shield_leakage(); #endif diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index 6044904dc0..cc46f82a10 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -31,6 +31,7 @@ #include "food.h" #include "it_use2.h" #include "itemname.h" +#include "itemprop.h" #include "macro.h" #include "monplace.h" #include "monstuff.h" @@ -39,6 +40,7 @@ #include "player.h" #include "religion.h" #include "skills.h" +#include "skills2.h" #include "spells1.h" #include "spells2.h" #include "spells3.h" @@ -101,7 +103,7 @@ char list_spells(void) clrscr(); - cprintf( " Your Spells Type Success Level" ); + cprintf( " Your Spells Type Power Success Level" ); lines++; for (j = 0; j < 52; j++) @@ -162,13 +164,29 @@ char list_spells(void) if (already) cprintf( "/" ); - cprintf( spelltype_name( 1 << i ) ); + cprintf( "%s", spelltype_short_name( 1 << i ) ); already = true; } } char sval[16]; + // 35--48 is the spell schools + + gotoxy(51, wherey()); + int spell_p = calc_spell_power( spell, true ); + cprintf( (spell_p > 100) ? "Enormous" : + (spell_p > 90) ? "Huge" : + (spell_p > 80) ? "Massive" : + (spell_p > 70) ? "Major" : + (spell_p > 60) ? "Impressive" : + (spell_p > 50) ? "Reasonable" : + (spell_p > 40) ? "Moderate" : + (spell_p > 30) ? "Adequate" : + (spell_p > 20) ? "Mediocre" : + (spell_p > 10) ? "Minor" + : "Negligible"); + //gotoxy(58, wherey()); gotoxy(65, wherey()); @@ -225,12 +243,44 @@ char list_spells(void) return (ki); } // end list_spells() +static int apply_vehumet_wizardry_boost(int spell, int chance) +{ + int wizardry = player_mag_abil(false); + int fail_reduce = 100; + int wiz_factor = 86; + + if (you.religion == GOD_VEHUMET + && you.duration[DUR_PRAYER] + && (!player_under_penance() && you.piety >= 50) + && (spell_typematch(spell, SPTYP_CONJURATION) + || spell_typematch(spell, SPTYP_SUMMONING))) + { + // [dshaligram] Fail rate multiplier used to be .5, scaled + // back to 60%. + fail_reduce = fail_reduce * 60 / 100; + } + + // [dshaligram] Apply wizardry factor here, rather than mixed into the + // pre-scaling spell power. + while (wizardry-- > 0) + { + fail_reduce = fail_reduce * wiz_factor / 100; + wiz_factor += (100 - wiz_factor) / 5; + } + + // Hard cap on fail rate reduction. + if (fail_reduce < 40) + fail_reduce = 40; + + return (chance * fail_reduce / 100); +} + int spell_fail(int spell) { int chance = 60; int chance2 = 0, armour = 0; - chance -= 6 * calc_spell_power( spell, false ); + chance -= 6 * calc_spell_power( spell, false, true ); chance -= (you.intel * 2); //chance -= (you.intel - 10) * abs(you.intel - 10); @@ -396,15 +446,6 @@ int spell_fail(int spell) if (chance < -180) chance2 = 0; - if (you.religion == GOD_VEHUMET - && you.duration[DUR_PRAYER] - && (!player_under_penance() && you.piety >= 50) - && (spell_typematch(spell, SPTYP_CONJURATION) - || spell_typematch(spell, SPTYP_SUMMONING))) - { - chance2 /= 2; - } - if (you.duration[DUR_TRANSFORMATION] > 0) { switch (you.attribute[ATTR_TRANSFORMATION]) @@ -417,17 +458,28 @@ int spell_fail(int spell) chance2 += 10; break; } + + if (chance2 > 100) + chance2 = 100; } + // Apply the effects of Vehumet prayer and items of wizardry. + chance2 = apply_vehumet_wizardry_boost(spell, chance2); + return (chance2); } // end spell_fail() -int calc_spell_power( int spell, bool apply_intel ) +int calc_spell_power( int spell, bool apply_intel, bool fail_rate_check ) { unsigned int bit; int ndx; - int power = (you.skills[SK_SPELLCASTING] / 2) + player_mag_abil(false); + + // When checking failure rates, wizardry is handled after the various + // stepping calulations. + int power = + (you.skills[SK_SPELLCASTING] / 2) + + (fail_rate_check? 0 : player_mag_abil(false)); int enhanced = 0; unsigned int disciplines = spell_type( spell ); @@ -453,7 +505,10 @@ int calc_spell_power( int spell, bool apply_intel ) if (apply_intel) power = (power * you.intel) / 10; - enhanced = spell_enhancement( disciplines ); + // [dshaligram] Enhancers don't affect fail rates any more, only spell + // power. Note that this does not affect Vehumet's boost in castability. + if (!fail_rate_check) + enhanced = spell_enhancement( disciplines ); if (enhanced > 0) { @@ -561,7 +616,7 @@ bool cast_a_spell(void) if (unthing == 2) return (false); - if (unthing >= 'a' && unthing <= 'y') + if ( isalpha(unthing) ) { keyin = unthing; break; @@ -611,6 +666,18 @@ bool cast_a_spell(void) return (false); } + if (you.conf) + random_uselessness( 2 + random2(7), 0 ); + else + { + int cast_result = your_spells( spell ); + if (cast_result == -1) + return (false); + + exercise_spell( spell, true, cast_result ); + did_god_conduct( DID_SPELL_CASTING, 1 + random2(5) ); + } + dec_mp( spell_mana(spell) ); if (!player_energy() && you.is_undead != US_UNDEAD) @@ -626,39 +693,32 @@ bool cast_a_spell(void) make_hungry(spellh, true); } - you.turn_is_over = 1; + you.turn_is_over = true; alert_nearby_monsters(); - if (you.conf) - random_uselessness( 2 + random2(7), 0 ); - else - { - exercise_spell( spell, true, your_spells( spell ) ); - naughty( NAUGHTY_SPELLCASTING, 1 + random2(5) ); - } - return (true); } // end cast_a_spell() -// returns true if spell if spell is successfully cast for purposes of -// exercising and false otherwise (note: false == less exercise, not none). -bool your_spells( int spc2, int powc, bool allow_fail ) +// "Utility" spells for the sake of simplicity are currently ones with +// enchantments, translocations, or divinations. +bool spell_is_utility_spell( int spell_id ) { - int dem_hor = 0; - int dem_hor2 = 0; - int total_skill = 0; - struct dist spd; - struct bolt beam; + return (spell_typematch( spell_id, + SPTYP_ENCHANTMENT | SPTYP_TRANSLOCATION | SPTYP_DIVINATION )); +} - alert_nearby_monsters(); +bool spell_is_unholy( int spell_id ) +{ + return (testbits( get_spell_flags( spell_id ), SPFLAG_UNHOLY )); +} - // Added this so that the passed in powc can have meaning -- bwr - if (powc == 0) - powc = calc_spell_power( spc2, true ); +void spellcasting_side_effects(int spc2, bool idonly = false) +{ + int total_skill = 0; if (you.equip[EQ_WEAPON] != -1 && item_is_staff( you.inv[you.equip[EQ_WEAPON]] ) - && item_not_ident( you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_TYPE )) + && !item_ident( you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_TYPE )) { switch (you.inv[you.equip[EQ_WEAPON]].sub_type) { @@ -719,7 +779,8 @@ bool your_spells( int spc2, int powc, bool allow_fail ) { char str_pass[ ITEMNAME_SIZE ]; - set_ident_flags( you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_TYPE ); + // changed from ISFLAG_KNOW_TYPE + set_ident_flags( you.inv[you.equip[EQ_WEAPON]], ISFLAG_IDENT_MASK); strcpy(info, "You are wielding "); in_name(you.equip[EQ_WEAPON], DESC_NOCAP_A, str_pass); @@ -733,6 +794,53 @@ bool your_spells( int spc2, int powc, bool allow_fail ) } } + if (idonly) + return; + + if (!spell_is_utility_spell(spc2)) + did_god_conduct( DID_SPELL_NONUTILITY, 10 + spell_difficulty(spc2) ); + + if (spell_is_unholy( spc2 )) + did_god_conduct( DID_UNHOLY, 10 + spell_difficulty(spc2) ); + + // Linley says: Condensation Shield needs some disadvantages to keep + // it from being a no-brainer... this isn't much, but its a start -- bwr + if (spell_typematch(spc2, SPTYP_FIRE)) + expose_player_to_element(BEAM_FIRE, 0); + + if (spell_typematch( spc2, SPTYP_NECROMANCY )) + { + did_god_conduct( DID_NECROMANCY, 10 + spell_difficulty(spc2) ); + + if (spc2 == SPELL_NECROMUTATION + && (you.religion == GOD_ELYVILON + || you.religion == GOD_SHINING_ONE + || you.religion == GOD_ZIN)) + { + excommunication(); + } + } + + alert_nearby_monsters(); +} + +// returns 1 if spell is successfully cast for purposes of exercising and 0 +// otherwise (note: false == less exercise, not none). If the player aborts the +// spell, returns -1. +int your_spells( int spc2, int powc, bool allow_fail ) +{ + int dem_hor = 0; + int dem_hor2 = 0; + struct dist spd; + struct bolt beam; + + // [dshaligram] Any action that depends on the spellcasting attempt to have + // succeeded must be performed after the switch() + + // Added this so that the passed in powc can have meaning -- bwr + if (powc == 0) + powc = calc_spell_power( spc2, true ); + surge_power(spc2); if (allow_fail) @@ -740,21 +848,23 @@ bool your_spells( int spc2, int powc, bool allow_fail ) int spfl = random2avg(100, 3); if (you.religion != GOD_SIF_MUNA - && you.penance[GOD_SIF_MUNA] && one_chance_in(40)) + && you.penance[GOD_SIF_MUNA] && one_chance_in(20)) { god_speaks(GOD_SIF_MUNA, "You feel a surge of divine spite."); // This will cause failure and increase the miscast effect. spfl = -you.penance[GOD_SIF_MUNA]; - // Reduced penenance reduction here because casting spells + // Reduced penance reduction here because casting spells // is a player controllable act. -- bwr - if (one_chance_in(7)) - dec_penance(1); + if (one_chance_in(12)) + dec_penance(GOD_SIF_MUNA, 1); } if (spfl < spell_fail(spc2)) { + spellcasting_side_effects(spc2, true); + mpr( "You miscast the spell." ); flush_input_buffer( FLUSH_ON_FAILURE ); @@ -763,7 +873,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) && you.piety >= 100 && random2(150) <= you.piety)) { canned_msg(MSG_NOTHING_HAPPENS); - return (false); + return (0); } unsigned int sptype = 0; @@ -793,14 +903,14 @@ bool your_spells( int spc2, int powc, bool allow_fail ) if (you.religion == GOD_XOM && random2(75) < spell_mana(spc2)) Xom_acts(coinflip(), spell_mana(spc2), false); - return (false); + return (0); } } if (you.is_undead && spell_typematch( spc2, SPTYP_HOLY )) { mpr( "You can't use this type of magic!" ); - return (false); // XXX: still gets trained! + return (-1); } // Normally undead can't memorize these spells, so this check is @@ -810,43 +920,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) && undead_cannot_memorise( spc2, you.is_undead )) { mpr( "You cannot cast that spell in your current form!" ); - return (false); // XXX: still gets trained! - } - - // Linley says: Condensation Shield needs some disadvantages to keep - // it from being a no-brainer... this isn't much, but its a start -- bwr - if (you.duration[DUR_CONDENSATION_SHIELD] > 0 - && spell_typematch( spc2, SPTYP_FIRE )) - { - mpr( "Your icy shield dissipates!", MSGCH_DURATION ); - you.duration[DUR_CONDENSATION_SHIELD] = 0; - you.redraw_armour_class = 1; - } - - if (spc2 == SPELL_SUMMON_HORRIBLE_THINGS - || spc2 == SPELL_CALL_IMP - || spc2 == SPELL_SUMMON_DEMON - || spc2 == SPELL_DEMONIC_HORDE - || spc2 == SPELL_SUMMON_GREATER_DEMON || spc2 == SPELL_HELLFIRE) - { - naughty(NAUGHTY_UNHOLY, 10 + spell_difficulty(spc2)); - } - - if (spell_typematch( spc2, SPTYP_NECROMANCY )) - naughty( NAUGHTY_NECROMANCY, 10 + spell_difficulty(spc2) ); - - if (spc2 == SPELL_NECROMUTATION - && (you.religion == GOD_ELYVILON - || you.religion == GOD_SHINING_ONE - || you.religion == GOD_ZIN)) - { - excommunication(); - } - - if (you.religion == GOD_SIF_MUNA - && you.piety < 200 && random2(12) <= spell_difficulty(spc2)) - { - gain_piety(1); + return (-1); } #if DEBUG_DIAGNOSTICS @@ -854,6 +928,9 @@ bool your_spells( int spc2, int powc, bool allow_fail ) mpr( info, MSGCH_DIAGNOSTICS ); #endif +#define SPELL_DIR(s, b) if (spell_direction(spd, beam) == -1) return -1; \ + else + switch (spc2) { case SPELL_IDENTIFY: @@ -871,7 +948,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) case SPELL_CREATE_NOISE: // unused, the player can shout to do this - bwr if (!silenced(you.x_pos, you.y_pos)) { - mpr("You hear a voice call your name!"); + mpr("You hear a voice call your name!", MSGCH_SOUND); noisy( 25, you.x_pos, you.y_pos ); } break; @@ -881,14 +958,14 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_MAGIC_DART: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_MAGIC_DARTS, powc, beam); break; case SPELL_FIREBALL: - fireball(powc); + if (fireball(powc) == -1) + return (-1); break; case SPELL_DELAYED_FIREBALL: @@ -931,20 +1008,17 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_STRIKING: - if (spell_direction( spd, beam ) == -1) - return (false); - + SPELL_DIR(spd, beam); zapping( ZAP_STRIKING, powc, beam ); break; case SPELL_CONJURE_FLAME: - conjure_flame(powc); + if (conjure_flame(powc) == -1) + return (-1); break; case SPELL_DIG: - if (spell_direction(spd, beam) == -1) - return (false); - + SPELL_DIR(spd, beam); if (spd.isMe) { canned_msg(MSG_UNTHINKING_ACT); @@ -954,62 +1028,53 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_BOLT_OF_FIRE: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_FIRE, powc, beam); break; case SPELL_BOLT_OF_COLD: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_COLD, powc, beam); break; case SPELL_LIGHTNING_BOLT: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_LIGHTNING, powc, beam); break; case SPELL_BOLT_OF_MAGMA: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_MAGMA, powc, beam); break; case SPELL_POLYMORPH_OTHER: - if (spell_direction(spd, beam) == -1) - return (false); - + SPELL_DIR(spd, beam); if (spd.isMe) { mpr("Sorry, it doesn't work like that."); - return (false); + return (-1); } zapping(ZAP_POLYMORPH_OTHER, powc, beam); break; case SPELL_SLOW: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_SLOWING, powc, beam); break; case SPELL_HASTE: if (spell_direction(spd, beam, DIR_NONE, TARG_FRIEND) == -1) - return (false); + return (-1); zapping(ZAP_HASTING, powc, beam); break; case SPELL_PARALYZE: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_PARALYSIS, powc, beam); break; case SPELL_CONFUSE: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_CONFUSION, powc, beam); break; @@ -1023,32 +1088,33 @@ bool your_spells( int spc2, int powc, bool allow_fail ) case SPELL_INVISIBILITY: if (spell_direction(spd, beam, DIR_NONE, TARG_FRIEND) == -1) - return (false); + return (-1); zapping(ZAP_INVISIBILITY, powc, beam); break; case SPELL_THROW_FLAME: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_FLAME, powc, beam); break; case SPELL_THROW_FROST: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_FROST, powc, beam); break; case SPELL_CONTROLLED_BLINK: - blink(); + if (blink() == -1) + return (-1); break; case SPELL_FREEZING_CLOUD: - cast_big_c(powc, CLOUD_COLD); + if (cast_big_c(powc, CLOUD_COLD) == -1) + return (-1); break; case SPELL_MEPHITIC_CLOUD: - stinking_cloud(powc); + if (stinking_cloud(powc) == -1) + return (-1); break; case SPELL_RING_OF_FLAMES: @@ -1068,8 +1134,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_VENOM_BOLT: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_VENOM_BOLT, powc, beam); break; @@ -1078,24 +1143,25 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_TELEPORT_OTHER: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); if (spd.isMe) { mpr("Sorry, it doesn't work like that."); - return (false); + return (-1); } // teleport creature (I think) zapping(ZAP_TELEPORTATION, powc, beam); break; case SPELL_LESSER_HEALING: - cast_healing(5); + if (!cast_healing(5)) + return (-1); break; case SPELL_GREATER_HEALING: - cast_healing(25); + if (!cast_healing(25)) + return (-1); break; case SPELL_CURE_POISON_I: //jmf: `healing' version? group w/ S_C_P_II? @@ -1111,7 +1177,8 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_SELECTIVE_AMNESIA: - cast_selective_amnesia(false); + if (!cast_selective_amnesia(false)) + return (-1); break; // Sif Muna power calls with true case SPELL_MASS_CONFUSION: @@ -1119,7 +1186,8 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_SMITING: - cast_smiting(powc); + if (cast_smiting(powc) == -1) + return (-1); break; case SPELL_REPEL_UNDEAD: @@ -1135,7 +1203,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_SUMMON_SMALL_MAMMAL: - summon_small_mammals(powc); //jmf: hmm, that's definately *plural* ;-) + summon_small_mammals(powc); //jmf: hmm, that's definitely *plural* ;-) break; case SPELL_ABJURATION_I: //jmf: why not group with SPELL_ABJURATION_II? @@ -1151,36 +1219,34 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_BOLT_OF_DRAINING: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_NEGATIVE_ENERGY, powc, beam); break; case SPELL_LEHUDIBS_CRYSTAL_SPEAR: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_CRYSTAL_SPEAR, powc, beam); break; case SPELL_BOLT_OF_INACCURACY: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_BEAM_OF_ENERGY, powc, beam); break; case SPELL_POISONOUS_CLOUD: - cast_big_c(powc, CLOUD_POISON); + if (cast_big_c(powc, CLOUD_POISON) == -1) + return (-1); break; case SPELL_POISON_ARROW: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping( ZAP_POISON_ARROW, powc, beam ); break; case SPELL_FIRE_STORM: - cast_fire_storm(powc); + if (cast_fire_storm(powc) == -1) + return (-1); break; case SPELL_DETECT_TRAPS: @@ -1194,9 +1260,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_ISKENDERUNS_MYSTIC_BLAST: - if (spell_direction(spd, beam) == -1) - return (false); - + SPELL_DIR(spd, beam); zapping( ZAP_MYSTIC_BLAST, powc, beam ); break; @@ -1209,12 +1273,11 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_ENSLAVEMENT: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); if (spd.isMe) { canned_msg(MSG_UNTHINKING_ACT); - return (false); + return (-1); } zapping(ZAP_ENSLAVEMENT, powc, beam); break; @@ -1234,12 +1297,12 @@ bool your_spells( int spc2, int powc, bool allow_fail ) case SPELL_HEAL_OTHER: if (spell_direction(spd, beam, DIR_NONE, TARG_FRIEND) == -1) - return (false); + return (-1); if (spd.isMe) { mpr("Sorry, it doesn't work like that."); - return (false); + return (-1); } zapping(ZAP_HEALING, powc, beam); break; @@ -1250,8 +1313,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_PAIN: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); dec_hp(1, false); zapping(ZAP_PAIN, powc, beam); break; @@ -1287,15 +1349,18 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_BURN: - burn_freeze(powc, BEAM_FIRE); + if (burn_freeze(powc, BEAM_FIRE) == -1) + return (-1); break; case SPELL_FREEZE: - burn_freeze(powc, BEAM_COLD); + if (burn_freeze(powc, BEAM_COLD) == -1) + return (-1); break; case SPELL_SUMMON_ELEMENTAL: - summon_elemental(powc, 0, 2); + if (summon_elemental(powc, 0, 2) == -1) + return (-1); break; case SPELL_OZOCUBUS_REFRIGERATION: @@ -1303,8 +1368,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_STICKY_FLAME: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_STICKY_FLAME, powc, beam); break; @@ -1334,8 +1398,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_DISPEL_UNDEAD: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_DISPEL_UNDEAD, powc, beam); break; @@ -1343,22 +1406,16 @@ bool your_spells( int spc2, int powc, bool allow_fail ) summon_ice_beast_etc(powc, MONS_ANGEL); break; - //jmf: FIXME: SPELL_PESTILENCE? - case SPELL_THUNDERBOLT: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_LIGHTNING, powc, beam); break; case SPELL_FLAME_OF_CLEANSING: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_CLEANSING_FLAME, powc, beam); break; - //jmf: FIXME: SPELL_SHINING_LIGHT? - case SPELL_SUMMON_DAEVA: summon_ice_beast_etc(powc, MONS_DAEVA); break; @@ -1378,18 +1435,17 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_BONE_SHARDS: - cast_bone_shards(powc); + if (cast_bone_shards(powc) == -1) + return (-1); break; case SPELL_BANISHMENT: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_BANISHMENT, powc, beam); break; case SPELL_CIGOTUVIS_DEGENERATION: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); if (spd.isMe) { @@ -1400,8 +1456,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_STING: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_STING, powc, beam); break; @@ -1416,9 +1471,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) case SPELL_HELLFIRE: // should only be available from: // staff of Dispater & Sceptre of Asmodeus - if (spell_direction(spd, beam) == -1) - return (false); - + SPELL_DIR(spd, beam); zapping(ZAP_HELLFIRE, powc, beam); break; @@ -1488,14 +1541,12 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_BOLT_OF_IRON: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_IRON_BOLT, powc, beam); break; case SPELL_STONE_ARROW: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_STONE_ARROW, powc, beam); break; @@ -1508,8 +1559,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_SHOCK: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_ELECTRICITY, powc, beam); break; @@ -1526,8 +1576,7 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_ORB_OF_ELECTROCUTION: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_ORB_OF_ELECTRICITY, powc, beam); break; @@ -1583,17 +1632,16 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_PORTAL: - portal(); + if (portal() == -1) + return (-1); break; case SPELL_AGONY: - if (spell_direction(spd, beam) == -1) - return (false); - + SPELL_DIR(spd, beam); if (spd.isMe) { canned_msg(MSG_UNTHINKING_ACT); - return (false); + return (-1); } zapping(ZAP_AGONY, powc, beam); break; @@ -1603,20 +1651,16 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_DISRUPT: - if (spell_direction(spd, beam) == -1) - return (false); - + SPELL_DIR(spd, beam); zapping(ZAP_DISRUPTION, powc, beam); break; case SPELL_DISINTEGRATE: - if (spell_direction(spd, beam) == -1) - return (false); - + SPELL_DIR(spd, beam); if (spd.isMe) { canned_msg(MSG_UNTHINKING_ACT); - return (false); + return (-1); } zapping(ZAP_DISINTEGRATION, powc, beam); break; @@ -1649,9 +1693,9 @@ bool your_spells( int spc2, int powc, bool allow_fail ) if (you.is_undead || you.mutation[MUT_TORMENT_RESISTANCE]) { mpr("To torment others, one must first know what torment means. "); - return (false); + return (-1); } - torment(you.x_pos, you.y_pos); + torment(TORMENT_SPELL, you.x_pos, you.y_pos); break; case SPELL_DEFLECT_MISSILES: @@ -1659,28 +1703,27 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_ORB_OF_FRAGMENTATION: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_ORB_OF_FRAGMENTATION, powc, beam); break; case SPELL_ICE_BOLT: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_ICE_BOLT, powc, beam); break; case SPELL_ARC: - burn_freeze(powc, BEAM_ELECTRICITY); + if (burn_freeze(powc, BEAM_ELECTRICITY) == -1) + return (-1); break; case SPELL_AIRSTRIKE: - airstrike(powc); + if (airstrike(powc) == -1) + return (-1); break; case SPELL_ICE_STORM: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); zapping(ZAP_ICE_STORM, powc, beam); break; @@ -1692,22 +1735,13 @@ bool your_spells( int spc2, int powc, bool allow_fail ) //jmf: new spells 19mar2000 case SPELL_FLAME_TONGUE: - if (spell_direction(spd, beam) == -1) - return (false); - + SPELL_DIR(spd, beam); if (spd.isMe) { canned_msg(MSG_UNTHINKING_ACT); - return (false); + return (-1); } zapping(ZAP_FLAME_TONGUE, powc, beam); - - /* - // This is not the place for this sort of power adjustment, - // it just makes it harder to balance -- bwr - zapping(ZAP_FLAME_TONGUE, 2 + random2(4) + random2(4) - + random2(powc) / 25, beam); - */ break; case SPELL_PASSWALL: @@ -1735,15 +1769,12 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_SLEEP: - if (spell_direction(spd, beam) == -1) - return (false); - + SPELL_DIR(spd, beam); if (spd.isMe) { canned_msg(MSG_UNTHINKING_ACT); - return (false); + return (-1); } - zapping(ZAP_SLEEP, powc, beam); break; @@ -1797,12 +1828,11 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_BACKLIGHT: - if (spell_direction(spd, beam) == -1) - return (false); + SPELL_DIR(spd, beam); if (spd.isMe) { canned_msg(MSG_UNTHINKING_ACT); - return (false); + return (-1); } zapping(ZAP_BACKLIGHT, powc + 10, beam); break; @@ -1839,16 +1869,14 @@ bool your_spells( int spc2, int powc, bool allow_fail ) cast_rotting(powc); break; - case SPELL_SHUGGOTH_SEED: - cast_shuggoth_seed(powc); - break; - case SPELL_CONDENSATION_SHIELD: cast_condensation_shield(powc); break; case SPELL_SEMI_CONTROLLED_BLINK: - cast_semi_controlled_blink(powc); //jmf: powc is ignored + //jmf: powc is ignored + if (cast_semi_controlled_blink(powc) == -1) + return (-1); break; case SPELL_STONESKIN: @@ -1863,6 +1891,10 @@ bool your_spells( int spc2, int powc, bool allow_fail ) cast_conjure_ball_lightning(powc); break; + case SPELL_CHAIN_LIGHTNING: + cast_chain_lightning(powc); + break; + case SPELL_TWIST: cast_twist(powc); break; @@ -1876,7 +1908,8 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; case SPELL_APPORTATION: - cast_apportation(powc); + if (cast_apportation(powc) == -1) + return (-1); break; default: @@ -1884,6 +1917,8 @@ bool your_spells( int spc2, int powc, bool allow_fail ) break; } // end switch + spellcasting_side_effects(spc2); + return (true); } // end you_spells() @@ -1892,8 +1927,16 @@ void exercise_spell( int spell, bool spc, bool success ) // (!success) reduces skill increase for miscast spells int ndx = 0; int skill; + int exer = 0; + int exer_norm = 0; int workout = 0; + // This is used as a reference level to normalise spell skill training + // (for Sif Muna piety). Normalised skill training is worked out as: + // norm = actual_amount_trained * species_aptitude / ref_skill. This was + // set at 50 in stone_soup 0.1.1 (which is bad). + const int ref_skill = 80; + unsigned int disciplines = spell_type(spell); //jmf: evil evil evil -- exclude HOLY bit @@ -1904,18 +1947,22 @@ void exercise_spell( int spell, bool spc, bool success ) if (!success) skillcount += 4 + random2(10); + const int diff = spell_difficulty(spell); for (ndx = 0; ndx <= SPTYP_LAST_EXPONENT; ndx++) { if (!spell_typematch( spell, 1 << ndx )) continue; skill = spell_type2skill( 1 << ndx ); - workout = (random2(1 + spell_difficulty(spell)) / skillcount); + workout = (random2(1 + diff) / skillcount); if (!one_chance_in(5)) workout++; // most recently, this was an automatic add {dlb} - exercise( skill, workout ); + const int exercise_amount = exercise( skill, workout ); + exer += exercise_amount; + exer_norm += + exercise_amount * species_skills(skill, you.species) / ref_skill; } /* ****************************************************************** @@ -1932,21 +1979,16 @@ void exercise_spell( int spell, bool spc, bool success ) if (spc) { - exercise(SK_SPELLCASTING, one_chance_in(3) ? 1 - : random2(1 + random2(spell_difficulty(spell)))); + const int exercise_amount = + exercise(SK_SPELLCASTING, one_chance_in(3) ? 1 + : random2(1 + random2(diff))); + exer += exercise_amount; + exer_norm += exercise_amount * + species_skills(SK_SPELLCASTING, you.species) / ref_skill; } - //+ (coinflip() ? 1 : 0) + (skillcount ? 1 : 0)); - -/* ****************************************************************** - 3.02 was: - - if ( spc && spellsy ) - exercise(SK_SPELLCASTING, random2(random2(spell_difficulty(spell_ex) + 1) / spellsy)); // + 1); - else if ( spc ) - exercise(SK_SPELLCASTING, random2(random2(spell_difficulty(spell_ex)))); // + 1); -****************************************************************** */ - + if (exer_norm) + did_god_conduct( DID_SPELL_PRACTISE, exer_norm ); } // end exercise_spell() static bool send_abyss() @@ -1999,7 +2041,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, } // setup beam - beam.isTracer = false; + beam.is_tracer = false; spec_effect = spec_effect / 100; @@ -2107,12 +2149,15 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "explosion"); + beam.name = "explosion"; beam.colour = random_colour(); beam.beam_source = NON_MONSTER; beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source = cause; + beam.aux_source.clear(); + if (cause) + beam.aux_source = cause; beam.ex_size = 1; + beam.is_explosion = true; explosion(beam); break; @@ -2134,13 +2179,16 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, // BEAM_EXPLOSION instead? {dlb} beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "explosion"); + beam.name = "explosion"; beam.colour = random_colour(); beam.beam_source = NON_MONSTER; beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source = cause; + beam.aux_source.clear(); + if (cause) + beam.aux_source = cause; beam.ex_size = coinflip()?1:2; - + beam.is_explosion = true; + explosion(beam); break; } @@ -2185,7 +2233,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, break; case 9: if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear something strange."); + mpr("You hear something strange.", MSGCH_SOUND); else if (you.attribute[ATTR_TRANSFORMATION] != TRAN_AIR) mpr("Your skull vibrates slightly."); else @@ -2391,7 +2439,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, break; case 1: if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear strange voices."); + mpr("You hear strange voices.", MSGCH_SOUND); else mpr("You feel momentarily dizzy."); break; @@ -2560,7 +2608,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, break; case 1: if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear strange voices."); + mpr("You hear strange voices.", MSGCH_SOUND); else mpr("Your nose twitches."); break; @@ -2671,7 +2719,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, break; case 1: if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear strange and distant voices."); + mpr("You hear strange and distant voices.", MSGCH_SOUND); else mpr("You feel homesick."); break; @@ -2779,9 +2827,8 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, mpr("Something just walked over your grave. No, really!"); break; } - mpr("Your body is wracked with pain!"); - dec_hp((you.hp / 2) - 1, false); + torment_monsters(you.x_pos, you.y_pos, 0, TORMENT_GENERIC); break; case 1: @@ -2974,7 +3021,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, break; case 9: if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear a sizzling sound."); + mpr("You hear a sizzling sound.", MSGCH_SOUND); else mpr("You feel like you have heartburn."); break; @@ -2995,7 +3042,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, case 1: mpr("Flames sear your flesh."); - scrolls_burn(3, OBJ_SCROLLS); + expose_player_to_element(BEAM_FIRE, 3); if (player_res_fire() < 0) { @@ -3014,7 +3061,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, ouch( check_your_resists( 5 + random2avg(29, 2), 2 ), 0, KILLED_BY_WILD_MAGIC, cause ); - scrolls_burn( 5, OBJ_SCROLLS ); + expose_player_to_element(BEAM_FIRE, 5); break; case 1: @@ -3025,12 +3072,15 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, beam.flavour = BEAM_FIRE; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "explosion"); + beam.name = "explosion"; beam.colour = RED; beam.beam_source = NON_MONSTER; beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source = cause; + beam.aux_source.clear(); + if (cause) + beam.aux_source = cause; beam.ex_size = 1; + beam.is_explosion = true; explosion(beam); break; @@ -3046,7 +3096,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, ouch( check_your_resists( 9 + random2avg(33, 2), 2 ), 0, KILLED_BY_WILD_MAGIC, cause ); - scrolls_burn( 10, OBJ_SCROLLS ); + expose_player_to_element(BEAM_FIRE, 10); break; case 1: mpr("There is a sudden and violent explosion of flames!"); @@ -3056,12 +3106,15 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, beam.flavour = BEAM_FIRE; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy( beam.beam_name, "fireball" ); + beam.name = "fireball"; beam.colour = RED; beam.beam_source = NON_MONSTER; beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source = cause; + beam.aux_source.clear(); + if (cause) + beam.aux_source = cause; beam.ex_size = coinflip()?1:2; + beam.is_explosion = true; explosion(beam); break; @@ -3114,7 +3167,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, break; case 9: if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear a crackling sound."); + mpr("You hear a crackling sound.", MSGCH_SOUND); else mpr("A snowflake lands on your nose."); break; @@ -3129,7 +3182,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, break; case 1: mpr("You are covered in a thin layer of ice"); - scrolls_burn(2, OBJ_POTIONS); + expose_player_to_element(BEAM_COLD, 2); if (player_res_cold() < 0) ouch(4 + random2avg(5, 2), 0, KILLED_BY_WILD_MAGIC, cause); @@ -3146,7 +3199,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, ouch(check_your_resists(5 + random2(6) + random2(7), 3), 0, KILLED_BY_WILD_MAGIC, cause); - scrolls_burn(4, OBJ_POTIONS); + expose_player_to_element(BEAM_COLD, 4); break; case 1: @@ -3157,12 +3210,15 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, beam.flavour = BEAM_COLD; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "explosion"); + beam.name = "explosion"; beam.colour = WHITE; beam.beam_source = NON_MONSTER; beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source = cause; + beam.aux_source.clear(); + if (cause) + beam.aux_source = cause; beam.ex_size = 1; + beam.is_explosion = true; explosion(beam); break; @@ -3178,7 +3234,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, ouch(check_your_resists(9 + random2avg(23, 2), 3), 0, KILLED_BY_WILD_MAGIC, cause); - scrolls_burn(9, OBJ_POTIONS); + expose_player_to_element(BEAM_COLD, 9); break; case 1: snprintf( info, INFO_SIZE,"Freezing gasses pour from your %s!", @@ -3216,7 +3272,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, break; case 4: if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear a distant rumble."); + mpr("You hear a distant rumble.", MSGCH_SOUND); else mpr("You sympathise with the stones."); break; @@ -3279,7 +3335,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, beam.flavour = BEAM_FRAG; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "explosion"); + beam.name = "explosion"; beam.colour = CYAN; if (one_chance_in(5)) @@ -3289,8 +3345,11 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, beam.beam_source = NON_MONSTER; beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source = cause; + beam.aux_source.clear(); + if (cause) + beam.aux_source = cause; beam.ex_size = 1; + beam.is_explosion = true; explosion(beam); break; @@ -3333,7 +3392,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, case 7: // mummies cannot smell if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear a whooshing sound."); + mpr("You hear a whooshing sound.", MSGCH_SOUND); else if (you.species != SP_MUMMY) mpr("You smell ozone."); break; @@ -3343,7 +3402,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, case 9: // mummies cannot smell if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear a crackling sound."); + mpr("You hear a crackling sound.", MSGCH_SOUND); else if (you.species != SP_MUMMY) mpr("You smell something musty."); break; @@ -3394,12 +3453,15 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, beam.flavour = BEAM_ELECTRICITY; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "explosion"); + beam.name = "explosion"; beam.colour = LIGHTBLUE; beam.beam_source = NON_MONSTER; beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source = cause; + beam.aux_source.clear(); + if (cause) + beam.aux_source = cause; beam.ex_size = one_chance_in(4)?1:2; + beam.is_explosion = true; explosion(beam); break; @@ -3453,7 +3515,7 @@ bool miscast_effect( unsigned int sp_type, int mag_pow, int mag_fail, break; case 9: if (!silenced(you.x_pos, you.y_pos)) - mpr("You hear a slurping sound."); + mpr("You hear a slurping sound.", MSGCH_SOUND); else if (you.species != SP_MUMMY) mpr("You taste almonds."); break; diff --git a/crawl-ref/source/spl-cast.h b/crawl-ref/source/spl-cast.h index f517a5ff3f..b5e97d8b38 100644 --- a/crawl-ref/source/spl-cast.h +++ b/crawl-ref/source/spl-cast.h @@ -3,6 +3,8 @@ * Summary: Spell casting functions. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -14,7 +16,7 @@ char list_spells( void ); int spell_fail( int spell ); -int calc_spell_power( int spell, bool apply_intel ); +int calc_spell_power( int spell, bool apply_intel, bool fail_rate_chk = false ); int spell_enhancement( unsigned int typeflags ); // last updaetd 12may2000 {dlb} @@ -35,7 +37,7 @@ bool cast_a_spell( void ); /* *********************************************************************** * called from: ability - debug - it_use3 - spell * *********************************************************************** */ -bool your_spells( int spc2, int powc = 0, bool allow_fail = true ); +int your_spells( int spc2, int powc = 0, bool allow_fail = true ); // last updated 12may2000 {dlb} /* *********************************************************************** diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h index aee5e09bc0..ef275fccfe 100644 --- a/crawl-ref/source/spl-data.h +++ b/crawl-ref/source/spl-data.h @@ -1,3 +1,7 @@ +/* + * Modified for Crawl Reference by $Author$ on $Date$ + */ + /* In case anyone ever wants to add new spells, or just understand my reasons for putting a particular spell into a particular type, read on: @@ -135,114 +139,133 @@ { SPELL_IDENTIFY, "Identify", SPTYP_DIVINATION, + SPFLAG_NONE, 6 }, { SPELL_TELEPORT_SELF, "Teleport Self", SPTYP_TRANSLOCATION, + SPFLAG_NONE, 5 }, { SPELL_CAUSE_FEAR, "Cause Fear", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 5 }, { SPELL_CREATE_NOISE, "Create Noise", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 1 }, { SPELL_REMOVE_CURSE, "Remove Curse", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 5 }, { SPELL_MAGIC_DART, "Magic Dart", SPTYP_CONJURATION, + SPFLAG_DIR_OR_TARGET, 1 }, { SPELL_FIREBALL, "Fireball", SPTYP_CONJURATION | SPTYP_FIRE, + SPFLAG_DIR_OR_TARGET, 6 }, { SPELL_SWAP, "Swap", SPTYP_TRANSLOCATION, - 3 + SPFLAG_NONE, + 4 }, { SPELL_APPORTATION, "Apportation", SPTYP_TRANSLOCATION, + SPFLAG_NONE, 1 }, { SPELL_TWIST, "Twist", SPTYP_TRANSLOCATION, + SPFLAG_DIR_OR_TARGET, 1 }, { SPELL_CONJURE_FLAME, "Conjure Flame", SPTYP_CONJURATION | SPTYP_FIRE, + SPFLAG_GRID | SPFLAG_NOT_SELF, 3 }, { SPELL_DIG, "Dig", SPTYP_TRANSMIGRATION | SPTYP_EARTH, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 4 }, { SPELL_BOLT_OF_FIRE, "Bolt of Fire", SPTYP_CONJURATION | SPTYP_FIRE, + SPFLAG_DIR_OR_TARGET, 5 }, { SPELL_BOLT_OF_COLD, "Bolt of Cold", SPTYP_CONJURATION | SPTYP_ICE, + SPFLAG_DIR_OR_TARGET, 5 }, { SPELL_LIGHTNING_BOLT, "Lightning Bolt", SPTYP_CONJURATION | SPTYP_AIR, + SPFLAG_DIR_OR_TARGET, 6 }, { SPELL_BOLT_OF_MAGMA, "Bolt of Magma", SPTYP_CONJURATION | SPTYP_FIRE | SPTYP_EARTH, + SPFLAG_DIR_OR_TARGET, 5 }, { SPELL_POLYMORPH_OTHER, "Polymorph Other", SPTYP_TRANSMIGRATION, // removed enchantment, wasn't needed -- bwr + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 5 }, { SPELL_SLOW, "Slow", SPTYP_ENCHANTMENT, + SPFLAG_DIR_OR_TARGET, 3 }, { SPELL_HASTE, "Haste", SPTYP_ENCHANTMENT, + SPFLAG_DIR_OR_TARGET | SPFLAG_HELPFUL, 6 // lowered to 6 from 8, since its easily available from various items // and Swiftness is level 2 (and gives a similar effect). Its also // not that much better than Invisibility. -- bwr @@ -251,204 +274,238 @@ { SPELL_PARALYZE, "Paralyze", SPTYP_ENCHANTMENT, + SPFLAG_DIR_OR_TARGET, 4 }, { SPELL_CONFUSE, "Confuse", SPTYP_ENCHANTMENT, + SPFLAG_DIR_OR_TARGET, 3 }, { SPELL_INVISIBILITY, "Invisibility", SPTYP_ENCHANTMENT, + SPFLAG_DIR_OR_TARGET | SPFLAG_HELPFUL, 6 }, { SPELL_THROW_FLAME, "Throw Flame", SPTYP_CONJURATION | SPTYP_FIRE, + SPFLAG_DIR_OR_TARGET, 2 }, { SPELL_THROW_FROST, "Throw Frost", SPTYP_CONJURATION | SPTYP_ICE, + SPFLAG_DIR_OR_TARGET, 2 }, { SPELL_CONTROLLED_BLINK, "Controlled Blink", SPTYP_TRANSLOCATION, - 4 + SPFLAG_NONE, + 8 }, { SPELL_FREEZING_CLOUD, "Freezing Cloud", SPTYP_CONJURATION | SPTYP_ICE | SPTYP_AIR, + SPFLAG_GRID, 7 }, { SPELL_MEPHITIC_CLOUD, "Mephitic Cloud", SPTYP_CONJURATION | SPTYP_POISON | SPTYP_AIR, + SPFLAG_DIR_OR_TARGET, 3 }, { SPELL_RING_OF_FLAMES, "Ring of Flames", SPTYP_ENCHANTMENT | SPTYP_FIRE, + SPFLAG_NONE, 8 }, { SPELL_RESTORE_STRENGTH, "Restore Strength", SPTYP_HOLY, + SPFLAG_NONE, 2 }, { SPELL_RESTORE_INTELLIGENCE, "Restore Intelligence", SPTYP_HOLY, + SPFLAG_NONE, 2 }, { SPELL_RESTORE_DEXTERITY, "Restore Dexterity", SPTYP_HOLY, + SPFLAG_NONE, 2 }, { SPELL_VENOM_BOLT, "Venom Bolt", SPTYP_CONJURATION | SPTYP_POISON, + SPFLAG_DIR_OR_TARGET, 5 }, { SPELL_OLGREBS_TOXIC_RADIANCE, "Olgreb's Toxic Radiance", SPTYP_POISON, + SPFLAG_NONE, 4 }, { SPELL_TELEPORT_OTHER, "Teleport Other", SPTYP_TRANSLOCATION, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 4 }, { SPELL_LESSER_HEALING, "Lesser Healing", SPTYP_HOLY, + SPFLAG_NONE, 2 }, { SPELL_GREATER_HEALING, "Greater Healing", SPTYP_HOLY, + SPFLAG_NONE, 6 }, { SPELL_CURE_POISON_I, "Cure Poison", SPTYP_HOLY, + SPFLAG_NONE, 3 }, { SPELL_PURIFICATION, "Purification", SPTYP_HOLY, + SPFLAG_NONE, 5 }, { SPELL_DEATHS_DOOR, "Death's Door", SPTYP_ENCHANTMENT | SPTYP_NECROMANCY, + SPFLAG_NONE, 8 }, { SPELL_SELECTIVE_AMNESIA, "Selective Amnesia", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 3 }, { SPELL_MASS_CONFUSION, "Mass Confusion", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 6 }, { SPELL_SMITING, "Smiting", SPTYP_HOLY, + SPFLAG_NONE, 4 }, { SPELL_REPEL_UNDEAD, "Repel Undead", SPTYP_HOLY, + SPFLAG_NONE, 3 }, { SPELL_HOLY_WORD, "Holy Word", SPTYP_HOLY, + SPFLAG_NONE, 7 }, { SPELL_DETECT_CURSE, "Detect Curse", SPTYP_DIVINATION, + SPFLAG_NONE, 3 }, { SPELL_SUMMON_SMALL_MAMMAL, "Summon Small Mammals", SPTYP_SUMMONING, + SPFLAG_NONE, 1 }, { SPELL_ABJURATION_I, "Abjuration", SPTYP_SUMMONING, + SPFLAG_NONE, 3 }, { SPELL_SUMMON_SCORPIONS, "Summon Scorpions", SPTYP_SUMMONING | SPTYP_POISON, + SPFLAG_NONE, 4 }, { SPELL_LEVITATION, "Levitation", SPTYP_ENCHANTMENT | SPTYP_AIR, + SPFLAG_NONE, 2 }, { SPELL_BOLT_OF_DRAINING, "Bolt of Draining", SPTYP_CONJURATION | SPTYP_NECROMANCY, + SPFLAG_DIR_OR_TARGET, 6 }, { SPELL_LEHUDIBS_CRYSTAL_SPEAR, "Lehudib's Crystal Spear", SPTYP_CONJURATION | SPTYP_EARTH, + SPFLAG_DIR_OR_TARGET, 8 }, { SPELL_BOLT_OF_INACCURACY, "Bolt of Inaccuracy", SPTYP_CONJURATION, + SPFLAG_DIR_OR_TARGET, 2 }, { SPELL_POISONOUS_CLOUD, "Poisonous Cloud", SPTYP_CONJURATION | SPTYP_POISON | SPTYP_AIR, + SPFLAG_GRID, 6 } , @@ -456,18 +513,21 @@ { SPELL_FIRE_STORM, "Fire Storm", SPTYP_CONJURATION | SPTYP_FIRE, + SPFLAG_GRID, 9 }, { SPELL_DETECT_TRAPS, "Detect Traps", SPTYP_DIVINATION, + SPFLAG_NONE, 2 }, { SPELL_BLINK, "Blink", SPTYP_TRANSLOCATION, + SPFLAG_NONE, 2 }, @@ -477,174 +537,203 @@ { SPELL_ISKENDERUNS_MYSTIC_BLAST, "Iskenderun's Mystic Blast", SPTYP_CONJURATION, + SPFLAG_DIR_OR_TARGET, 4 }, { SPELL_SWARM, "Summon Swarm", SPTYP_SUMMONING, + SPFLAG_NONE, 6 }, { SPELL_SUMMON_HORRIBLE_THINGS, "Summon Horrible Things", SPTYP_SUMMONING, + SPFLAG_UNHOLY, 8 }, { SPELL_ENSLAVEMENT, "Enslavement", SPTYP_ENCHANTMENT, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 4 }, { SPELL_MAGIC_MAPPING, "Magic Mapping", SPTYP_DIVINATION | SPTYP_EARTH, + SPFLAG_NONE, 4 }, { SPELL_HEAL_OTHER, "Heal Other", SPTYP_HOLY, + SPFLAG_DIR_OR_TARGET | SPFLAG_HELPFUL | SPFLAG_NOT_SELF, 3 }, { SPELL_ANIMATE_DEAD, "Animate Dead", SPTYP_NECROMANCY, + SPFLAG_NONE, 4 }, { SPELL_PAIN, "Pain", SPTYP_NECROMANCY, + SPFLAG_DIR_OR_TARGET, 1 }, { SPELL_EXTENSION, "Extension", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 5 }, { SPELL_CONTROL_UNDEAD, "Control Undead", SPTYP_ENCHANTMENT | SPTYP_NECROMANCY, + SPFLAG_NONE, 6 }, { SPELL_ANIMATE_SKELETON, "Animate Skeleton", SPTYP_NECROMANCY, + SPFLAG_NONE, 1 }, { SPELL_VAMPIRIC_DRAINING, "Vampiric Draining", SPTYP_NECROMANCY, + SPFLAG_DIR | SPFLAG_NOT_SELF, 3 }, { SPELL_SUMMON_WRAITHS, "Summon Wraiths", SPTYP_NECROMANCY | SPTYP_SUMMONING, + SPFLAG_NONE, 7 }, { SPELL_DETECT_ITEMS, "Detect Items", SPTYP_DIVINATION, + SPFLAG_NONE, 2 }, { SPELL_BORGNJORS_REVIVIFICATION, "Borgnjor's Revivification", SPTYP_NECROMANCY, - 6 + SPFLAG_NONE, + 5 }, { SPELL_BURN, "Burn", // used by wanderers SPTYP_FIRE, + SPFLAG_DIR | SPFLAG_NOT_SELF, 1 }, { SPELL_FREEZE, "Freeze", SPTYP_ICE, + SPFLAG_DIR | SPFLAG_NOT_SELF, 1 }, { SPELL_SUMMON_ELEMENTAL, "Summon Elemental", SPTYP_SUMMONING, + SPFLAG_NONE, 4 }, { SPELL_OZOCUBUS_REFRIGERATION, "Ozocubu's Refrigeration", SPTYP_ICE, + SPFLAG_NONE, 5 }, { SPELL_STICKY_FLAME, "Sticky Flame", SPTYP_CONJURATION | SPTYP_FIRE, + SPFLAG_DIR_OR_TARGET, 4 }, { SPELL_SUMMON_ICE_BEAST, "Summon Ice Beast", SPTYP_ICE | SPTYP_SUMMONING, + SPFLAG_NONE, 5 }, { SPELL_OZOCUBUS_ARMOUR, "Ozocubu's Armour", SPTYP_ENCHANTMENT | SPTYP_ICE, + SPFLAG_NONE, 3 }, { SPELL_CALL_IMP, "Call Imp", SPTYP_SUMMONING, + SPFLAG_UNHOLY, 3 }, { SPELL_REPEL_MISSILES, "Repel Missiles", SPTYP_ENCHANTMENT | SPTYP_AIR, + SPFLAG_NONE, 2 }, { SPELL_BERSERKER_RAGE, "Berserker Rage", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 3 }, { SPELL_DISPEL_UNDEAD, "Dispel Undead", SPTYP_NECROMANCY, + SPFLAG_DIR_OR_TARGET, 4 }, { SPELL_GUARDIAN, "Guardian", SPTYP_HOLY, + SPFLAG_NONE, 7 }, { SPELL_PESTILENCE, "Pestilence", SPTYP_HOLY, + SPFLAG_NONE, 4 }, { SPELL_THUNDERBOLT, "Thunderbolt", SPTYP_HOLY | SPTYP_AIR, + SPFLAG_DIR_OR_TARGET, 6 // why is this the only holy spell with a secondary? {dlb} } , @@ -652,150 +741,175 @@ { SPELL_FLAME_OF_CLEANSING, "Flame of Cleansing", SPTYP_HOLY, + SPFLAG_DIR_OR_TARGET, 8 }, { SPELL_SHINING_LIGHT, "Shining Light", SPTYP_HOLY, + SPFLAG_NONE, 7 }, { SPELL_SUMMON_DAEVA, "Summon Daeva", SPTYP_HOLY, + SPFLAG_NONE, 8 }, { SPELL_ABJURATION_II, "Abjuration", SPTYP_HOLY, + SPFLAG_NONE, 4 }, { SPELL_TWISTED_RESURRECTION, "Twisted Resurrection", SPTYP_NECROMANCY, + SPFLAG_NONE, 5 }, { SPELL_REGENERATION, "Regeneration", SPTYP_ENCHANTMENT | SPTYP_NECROMANCY, + SPFLAG_NONE, 3 }, { SPELL_BONE_SHARDS, "Bone Shards", SPTYP_NECROMANCY, + SPFLAG_NONE, 3 }, { SPELL_BANISHMENT, "Banishment", SPTYP_TRANSLOCATION, + SPFLAG_DIR_OR_TARGET | SPFLAG_UNHOLY, 5 }, { SPELL_CIGOTUVIS_DEGENERATION, "Cigotuvi's Degeneration", SPTYP_TRANSMIGRATION | SPTYP_NECROMANCY, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 5 }, { SPELL_STING, "Sting", SPTYP_CONJURATION | SPTYP_POISON, + SPFLAG_DIR_OR_TARGET, 1 }, { SPELL_SUBLIMATION_OF_BLOOD, "Sublimation of Blood", SPTYP_NECROMANCY, + SPFLAG_NONE, 2 }, { SPELL_TUKIMAS_DANCE, "Tukima's Dance", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 3 }, { SPELL_HELLFIRE, "Hellfire", SPTYP_CONJURATION | SPTYP_FIRE, + SPFLAG_DIR_OR_TARGET | SPFLAG_UNHOLY, 9 }, { SPELL_SUMMON_DEMON, "Summon Demon", SPTYP_SUMMONING, + SPFLAG_UNHOLY, 5 }, { SPELL_DEMONIC_HORDE, "Demonic Horde", SPTYP_SUMMONING, + SPFLAG_UNHOLY, 6 }, { SPELL_SUMMON_GREATER_DEMON, "Summon Greater Demon", SPTYP_SUMMONING, + SPFLAG_UNHOLY, 7 }, { SPELL_CORPSE_ROT, "Corpse Rot", SPTYP_NECROMANCY, + SPFLAG_NONE, 2 }, { SPELL_TUKIMAS_VORPAL_BLADE, "Tukima's Vorpal Blade", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 2 }, { SPELL_FIRE_BRAND, "Fire Brand", SPTYP_ENCHANTMENT | SPTYP_FIRE, + SPFLAG_NONE, 2 }, { SPELL_FREEZING_AURA, "Freezing Aura", SPTYP_ENCHANTMENT | SPTYP_ICE, + SPFLAG_NONE, 2 }, { SPELL_LETHAL_INFUSION, "Lethal Infusion", SPTYP_ENCHANTMENT | SPTYP_NECROMANCY, + SPFLAG_NONE, 2 }, { - SPELL_CRUSH, "Crush", // used by wanderers + SPELL_CRUSH, "Crush", SPTYP_EARTH, + SPFLAG_DIR | SPFLAG_NOT_SELF, 1 }, { SPELL_BOLT_OF_IRON, "Bolt of Iron", SPTYP_CONJURATION | SPTYP_EARTH, + SPFLAG_DIR_OR_TARGET, 6 }, { SPELL_STONE_ARROW, "Stone Arrow", SPTYP_CONJURATION | SPTYP_EARTH, + SPFLAG_DIR_OR_TARGET, 3 }, { SPELL_TOMB_OF_DOROKLOHE, "Tomb of Doroklohe", SPTYP_CONJURATION | SPTYP_EARTH, // conj makes more sense than tmig -- bwr + SPFLAG_NONE, 7 } , @@ -803,48 +917,56 @@ { SPELL_STONEMAIL, "Stonemail", SPTYP_ENCHANTMENT | SPTYP_EARTH, + SPFLAG_NONE, 6 }, { SPELL_SHOCK, "Shock", SPTYP_CONJURATION | SPTYP_AIR, + SPFLAG_DIR_OR_TARGET, 1 }, { SPELL_SWIFTNESS, "Swiftness", SPTYP_ENCHANTMENT | SPTYP_AIR, + SPFLAG_NONE, 2 }, { SPELL_FLY, "Fly", SPTYP_ENCHANTMENT | SPTYP_AIR, + SPFLAG_NONE, 4 }, { SPELL_INSULATION, "Insulation", SPTYP_ENCHANTMENT | SPTYP_AIR, + SPFLAG_NONE, 4 }, { SPELL_ORB_OF_ELECTROCUTION, "Orb of Electrocution", SPTYP_CONJURATION | SPTYP_AIR, + SPFLAG_DIR_OR_TARGET, 7 }, { SPELL_DETECT_CREATURES, "Detect Creatures", SPTYP_DIVINATION, + SPFLAG_NONE, 2 }, { SPELL_CURE_POISON_II, "Cure Poison", SPTYP_POISON, + SPFLAG_NONE, 2 } , @@ -852,12 +974,14 @@ { SPELL_CONTROL_TELEPORT, "Control Teleport", SPTYP_ENCHANTMENT | SPTYP_TRANSLOCATION, - 6 + SPFLAG_NONE, + 5 }, { SPELL_POISON_AMMUNITION, "Poison Ammunition", SPTYP_ENCHANTMENT | SPTYP_POISON, + SPFLAG_NONE, 4 // jmf: SPTYP_TRANSMIGRATION vs SPTYP_ENCHANTMENT? } , @@ -865,150 +989,175 @@ { SPELL_POISON_WEAPON, "Poison Weapon", SPTYP_ENCHANTMENT | SPTYP_POISON, + SPFLAG_NONE, 4 }, { SPELL_RESIST_POISON, "Resist Poison", SPTYP_ENCHANTMENT | SPTYP_POISON, + SPFLAG_NONE, 4 }, { SPELL_PROJECTED_NOISE, "Projected Noise", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 2 }, { SPELL_ALTER_SELF, "Alter Self", SPTYP_TRANSMIGRATION, + SPFLAG_NONE, 7 }, { SPELL_DEBUGGING_RAY, "Debugging Ray", SPTYP_CONJURATION, + SPFLAG_DIR_OR_TARGET, 7 }, { SPELL_RECALL, "Recall", SPTYP_SUMMONING | SPTYP_TRANSLOCATION, + SPFLAG_NONE, 3 }, { SPELL_PORTAL, "Portal", SPTYP_TRANSLOCATION, - 8 + SPFLAG_NONE, + 7 }, { SPELL_AGONY, "Agony", SPTYP_NECROMANCY, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 5 }, { SPELL_SPIDER_FORM, "Spider Form", SPTYP_TRANSMIGRATION | SPTYP_POISON, + SPFLAG_NONE, 3 }, { SPELL_DISRUPT, "Disrupt", SPTYP_TRANSMIGRATION, + SPFLAG_DIR_OR_TARGET, 1 }, { SPELL_DISINTEGRATE, "Disintegrate", SPTYP_TRANSMIGRATION, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 6 }, { SPELL_BLADE_HANDS, "Blade Hands", SPTYP_TRANSMIGRATION, + SPFLAG_NONE, 5 // only removes weapon, so I raised this from 4 -- bwr }, { SPELL_STATUE_FORM, "Statue Form", SPTYP_TRANSMIGRATION | SPTYP_EARTH, + SPFLAG_NONE, 6 }, { SPELL_ICE_FORM, "Ice Form", SPTYP_ICE | SPTYP_TRANSMIGRATION, + SPFLAG_NONE, 4 // doesn't allow for equipment, so I lowered this from 5 -- bwr }, { SPELL_DRAGON_FORM, "Dragon Form", SPTYP_FIRE | SPTYP_TRANSMIGRATION, + SPFLAG_NONE, 8 }, { SPELL_NECROMUTATION, "Necromutation", SPTYP_TRANSMIGRATION | SPTYP_NECROMANCY, + SPFLAG_NONE, 8 }, { SPELL_DEATH_CHANNEL, "Death Channel", SPTYP_NECROMANCY, + SPFLAG_NONE, 9 }, { SPELL_SYMBOL_OF_TORMENT, "Symbol of Torment", SPTYP_NECROMANCY, + SPFLAG_NONE, 6 }, { SPELL_DEFLECT_MISSILES, "Deflect Missiles", SPTYP_ENCHANTMENT | SPTYP_AIR, + SPFLAG_NONE, 6 }, { SPELL_ORB_OF_FRAGMENTATION, "Orb of Fragmentation", SPTYP_CONJURATION | SPTYP_EARTH, + SPFLAG_DIR_OR_TARGET, 7 }, { SPELL_ICE_BOLT, "Ice Bolt", SPTYP_CONJURATION | SPTYP_ICE, + SPFLAG_DIR_OR_TARGET, 4 }, { SPELL_ICE_STORM, "Ice Storm", SPTYP_CONJURATION | SPTYP_ICE, + SPFLAG_DIR_OR_TARGET, 9 }, { - SPELL_ARC, "Arc", // used by wanderers + SPELL_ARC, "Arc", SPTYP_AIR, + SPFLAG_DIR | SPFLAG_NOT_SELF, 1 }, { SPELL_AIRSTRIKE, "Airstrike", SPTYP_AIR, + SPFLAG_TARGET | SPFLAG_NOT_SELF, 4 }, { SPELL_SHADOW_CREATURES, "Shadow Creatures", SPTYP_SUMMONING, // jmf: or SPTYP_SUMMONING | SPTYP_CONJURATION + SPFLAG_NONE, 5 } , @@ -1016,12 +1165,14 @@ { SPELL_CONFUSING_TOUCH, "Confusing Touch", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 1 }, { SPELL_SURE_BLADE, "Sure Blade", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 2 }, @@ -1033,246 +1184,280 @@ { SPELL_FLAME_TONGUE, "Flame Tongue", SPTYP_CONJURATION | SPTYP_FIRE, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 1 }, { SPELL_PASSWALL, "Passwall", SPTYP_TRANSMIGRATION | SPTYP_EARTH, + SPFLAG_NONE, 3 }, { SPELL_IGNITE_POISON, "Ignite Poison", SPTYP_FIRE | SPTYP_TRANSMIGRATION, + SPFLAG_NONE, 7 }, { SPELL_STICKS_TO_SNAKES, "Sticks to Snakes", SPTYP_TRANSMIGRATION | SPTYP_SUMMONING, + SPFLAG_NONE, 2 }, { SPELL_SUMMON_LARGE_MAMMAL, "Call Canine Familiar", SPTYP_SUMMONING, + SPFLAG_NONE, 3 }, { SPELL_SUMMON_DRAGON, "Summon Dragon", SPTYP_FIRE | SPTYP_SUMMONING, + SPFLAG_NONE, 9 }, { SPELL_TAME_BEASTS, "Tame Beasts", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 5 }, { SPELL_SLEEP, "Ensorcelled Hibernation", SPTYP_ENCHANTMENT | SPTYP_ICE, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 2 }, { SPELL_MASS_SLEEP, "Metabolic Englaciation", SPTYP_ENCHANTMENT | SPTYP_ICE, + SPFLAG_NONE, 7 }, { SPELL_DETECT_MAGIC, "Detect Magic", SPTYP_DIVINATION, + SPFLAG_NONE, 1 }, { SPELL_DETECT_SECRET_DOORS, "Detect Secret Doors", SPTYP_DIVINATION, + SPFLAG_NONE, 1 }, { SPELL_SEE_INVISIBLE, "See Invisible", SPTYP_ENCHANTMENT | SPTYP_DIVINATION, + SPFLAG_NONE, 4 }, { SPELL_FORESCRY, "Forescry", SPTYP_DIVINATION, + SPFLAG_NONE, 5 }, { SPELL_SUMMON_BUTTERFLIES, "Summon Butterflies", SPTYP_SUMMONING, + SPFLAG_NONE, 1 }, { SPELL_WARP_BRAND, "Warp Weapon", SPTYP_ENCHANTMENT | SPTYP_TRANSLOCATION, + SPFLAG_NONE, 7 // this is high for a reason - Warp brands are very powerful. }, { SPELL_SILENCE, "Silence", SPTYP_ENCHANTMENT | SPTYP_AIR, - 3 + SPFLAG_NONE, + 5 }, { SPELL_SHATTER, "Shatter", SPTYP_TRANSMIGRATION | SPTYP_EARTH, + SPFLAG_NONE, 9 }, { SPELL_DISPERSAL, "Dispersal", SPTYP_TRANSLOCATION, + SPFLAG_NONE, 7 }, { SPELL_DISCHARGE, "Static Discharge", SPTYP_CONJURATION | SPTYP_AIR, + SPFLAG_NONE, 4 }, { SPELL_BEND, "Bend", SPTYP_TRANSLOCATION, + SPFLAG_NONE, 1 }, { SPELL_BACKLIGHT, "Corona", SPTYP_ENCHANTMENT, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 1 }, { SPELL_INTOXICATE, "Alistair's Intoxication", SPTYP_TRANSMIGRATION | SPTYP_POISON, + SPFLAG_NONE, 4 }, { SPELL_GLAMOUR, "Glamour", SPTYP_ENCHANTMENT, + SPFLAG_NONE, 5 }, { SPELL_EVAPORATE, "Evaporate", SPTYP_FIRE | SPTYP_TRANSMIGRATION, + SPFLAG_NONE, 2 // XXX: level 2 or 3, what should it be now? -- bwr }, { SPELL_ERINGYAS_SURPRISING_BOUQUET, "Eringya's Surprising Bouquet", SPTYP_TRANSMIGRATION | SPTYP_EARTH, + SPFLAG_NONE, 4 }, { SPELL_FRAGMENTATION, "Lee's Rapid Deconstruction", SPTYP_TRANSMIGRATION | SPTYP_EARTH, + SPFLAG_NONE, 5 }, { SPELL_AIR_WALK, "Air Walk", SPTYP_TRANSMIGRATION | SPTYP_AIR, + SPFLAG_NONE, 9 }, { SPELL_SANDBLAST, "Sandblast", SPTYP_TRANSMIGRATION | SPTYP_EARTH, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 1 }, { SPELL_ROTTING, "Rotting", SPTYP_TRANSMIGRATION | SPTYP_NECROMANCY, + SPFLAG_NONE, 5 }, -{ - SPELL_SHUGGOTH_SEED, "Shuggoth Seed", - SPTYP_NECROMANCY | SPTYP_SUMMONING, - 7 -}, - { SPELL_MAXWELLS_SILVER_HAMMER, "Maxwell's Silver Hammer", - SPTYP_ENCHANTMENT | SPTYP_EARTH, + SPTYP_TRANSMIGRATION | SPTYP_EARTH, + SPFLAG_NONE, 2 }, { SPELL_CONDENSATION_SHIELD, "Condensation Shield", SPTYP_ICE | SPTYP_TRANSMIGRATION, + SPFLAG_NONE, 4 }, { SPELL_SEMI_CONTROLLED_BLINK, "Semi-Controlled Blink", SPTYP_TRANSLOCATION, + SPFLAG_NONE, 3 }, { SPELL_STONESKIN, "Stoneskin", SPTYP_EARTH | SPTYP_TRANSMIGRATION, // was ench -- bwr + SPFLAG_NONE, 2 }, { SPELL_SIMULACRUM, "Simulacrum", SPTYP_ICE | SPTYP_NECROMANCY, - 7 + SPFLAG_NONE, + 6 }, { SPELL_CONJURE_BALL_LIGHTNING, "Conjure Ball Lightning", SPTYP_AIR | SPTYP_CONJURATION, + SPFLAG_NONE, 8 }, { - SPELL_FAR_STRIKE, "Far Strike", - SPTYP_TRANSLOCATION, - 3 + SPELL_CHAIN_LIGHTNING, "Chain Lightning", + SPTYP_AIR | SPTYP_CONJURATION, + SPFLAG_NONE, + 8 }, { SPELL_DELAYED_FIREBALL, "Delayed Fireball", SPTYP_FIRE | SPTYP_CONJURATION, + SPFLAG_NONE, 7 }, { SPELL_FULSOME_DISTILLATION, "Fulsome Distillation", SPTYP_TRANSMIGRATION | SPTYP_NECROMANCY, + SPFLAG_NONE, 1 }, { SPELL_POISON_ARROW, "Poison Arrow", SPTYP_CONJURATION | SPTYP_POISON, + SPFLAG_DIR_OR_TARGET, 6 }, { SPELL_STRIKING, "Striking", 0, + SPFLAG_DIR_OR_TARGET, 1 }, @@ -1280,6 +1465,7 @@ SPELL_NO_SPELL, "nonexistent spell", 0, 0, + 0, }, diff --git a/crawl-ref/source/spl-util.cc b/crawl-ref/source/spl-util.cc index 58a9ecb2fe..020e5fa0c2 100644 --- a/crawl-ref/source/spl-util.cc +++ b/crawl-ref/source/spl-util.cc @@ -3,6 +3,8 @@ * Summary: data handlers for player-avilable spell list * * Written by: don brodale * * * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Changelog(most recent first): * * * <3> 04oct2001 bwr absorbed spells0.cc @@ -26,7 +28,9 @@ #include "stuff.h" #include "itemname.h" #include "macro.h" +#include "misc.h" #include "monstuff.h" +#include "notes.h" #include "player.h" #include "spl-book.h" #include "view.h" @@ -61,7 +65,7 @@ void init_playerspells(void) for (x = 0; x < NUM_SPELLS; x++) plyrspell_list[x] = -1; - // can only use up to PLYRSPELLDATASIZE _MINUS ONE_, or the + // can only use up to PLYRSPELLDATASIZE _MINUS ONE_, or the // last entry tries to set plyrspell_list[SPELL_NO_SPELL] // which corrupts the heap. for (x = 0; x < PLYRSPELLDATASIZE - 1; x++) @@ -121,6 +125,8 @@ bool add_spell_to_memory( int spell ) you.spell_no++; + take_note(Note(NOTE_LEARN_SPELL, spell)); + return (true); } @@ -200,6 +206,11 @@ int spell_levels_required( int which_spell ) return (levels); } +unsigned int get_spell_flags( int which_spell ) +{ + return (seekspell(which_spell)->flags); +} + bool spell_typematch(int which_spell, unsigned int which_discipline) { return (seekspell(which_spell)->disciplines & which_discipline); @@ -456,12 +467,12 @@ int apply_one_neighbouring_square(int (*func) (int, int, int, int), int power) struct dist bmove; mpr("Which direction? [ESC to cancel]", MSGCH_PROMPT); - direction( bmove, DIR_DIR, TARG_ENEMY ); + direction( bmove, DIR_DIR, TARG_ENEMY, true ); if (!bmove.isValid) { - canned_msg(MSG_SPELL_FIZZLES); - return (0); + canned_msg(MSG_OK); + return (-1); } int rv = func(you.x_pos + bmove.dx, you.y_pos + bmove.dy, power, 1); @@ -673,23 +684,56 @@ char spell_direction( struct dist &spelld, struct bolt &pbolt, message_current_target(); - direction( spelld, restrict, mode ); + direction( spelld, restrict, mode, true ); if (!spelld.isValid) { // check for user cancel - canned_msg(MSG_SPELL_FIZZLES); + canned_msg(MSG_OK); return -1; } - pbolt.target_x = spelld.tx; - pbolt.target_y = spelld.ty; + pbolt.set_target(spelld); pbolt.source_x = you.x_pos; pbolt.source_y = you.y_pos; return 1; } // end spell_direction() +const char* spelltype_short_name( int which_spelltype ) { + switch (which_spelltype) + { + case SPTYP_CONJURATION: + return ("Conj"); + case SPTYP_ENCHANTMENT: + return ("Ench"); + case SPTYP_FIRE: + return ("Fire"); + case SPTYP_ICE: + return ("Ice"); + case SPTYP_TRANSMIGRATION: + return ("Tmgr"); + case SPTYP_NECROMANCY: + return ("Necr"); + case SPTYP_HOLY: + return ("Holy"); + case SPTYP_SUMMONING: + return ("Summ"); + case SPTYP_DIVINATION: + return ("Divn"); + case SPTYP_TRANSLOCATION: + return ("Tloc"); + case SPTYP_POISON: + return ("Pois"); + case SPTYP_EARTH: + return ("Erth"); + case SPTYP_AIR: + return ("Air"); + default: + return "Bug"; + } +} + const char *spelltype_name(unsigned int which_spelltype) { static char bug_string[80]; @@ -776,7 +820,7 @@ static struct playerspell *seekspell(int spell) static bool cloud_helper( int (*func) (int, int, int, int), int x, int y, int pow, int ctype ) { - if (grd[x][y] > DNGN_LAST_SOLID_TILE && env.cgrid[x][y] == EMPTY_CLOUD) + if (!grid_is_solid(grd[x][y]) && env.cgrid[x][y] == EMPTY_CLOUD) { func(x, y, pow, ctype); return true; diff --git a/crawl-ref/source/spl-util.h b/crawl-ref/source/spl-util.h index 848530dbbf..31a3bc5102 100644 --- a/crawl-ref/source/spl-util.h +++ b/crawl-ref/source/spl-util.h @@ -3,6 +3,8 @@ * Summary: data handlers for player spell list * Written by: don brodale * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Changelog(most recent first): * * 24jun2000 jmf simplified structures @@ -20,7 +22,8 @@ struct playerspell { int id; const char *title; - unsigned int disciplines; //jmf: a bitfield + unsigned int disciplines; // bitfield + unsigned int flags; // bitfield unsigned int level; }; @@ -46,6 +49,8 @@ int spell_difficulty(int which_spell); int spell_levels_required(int which_spell); +unsigned int get_spell_flags( int which_spell ); + // * called from: chardump - spell - spl-book - spells0 bool spell_typematch(int which_spell, unsigned int which_discipline); unsigned int spell_type( int which_spell ); //jmf: simplification of above @@ -54,6 +59,8 @@ int count_bits( unsigned int bits ); // * called from: chardump - command - debug - spl-book - spells0 const char *spell_title(int which_spell); +const char* spelltype_short_name( int which_spelltype ); + //int spell_restriction(int which_spell, int which_restriction); int apply_area_visible(int (*func) (int, int, int, int), int power); diff --git a/crawl-ref/source/stash.cc b/crawl-ref/source/stash.cc index 0c7dfe6e34..b1b027fb3a 100644 --- a/crawl-ref/source/stash.cc +++ b/crawl-ref/source/stash.cc @@ -2,6 +2,8 @@ * File: stash.cc * Summary: Classes tracking player stashes * Written by: Darshan Shaligram + * + * Modified for Crawl Reference by $Author$ on $Date$ */ #include "AppHdr.h" @@ -9,12 +11,14 @@ #include "clua.h" #include "describe.h" #include "itemname.h" +#include "itemprop.h" #include "files.h" #include "invent.h" #include "items.h" #include "Kills.h" #include "libutil.h" #include "menu.h" +#include "misc.h" #include "shopping.h" #include "spl-book.h" #include "stash.h" @@ -36,12 +40,6 @@ #define LUA_DUMP_ANNOTATE "ch_stash_dump_annotate_item" #define LUA_VIEW_ANNOTATE "ch_stash_view_annotate_item" -std::string short_place_name(level_id id) -{ - return short_place_name( - get_packed_place(id.branch, id.depth, LEVEL_DUNGEON)); -} - std::string userdef_annotate_item(const char *s, const item_def *item, bool exclusive) { @@ -324,10 +322,10 @@ std::string Stash::stash_item_name(const item_def &item) return buf; } -class StashMenu : public Menu +class StashMenu : public InvMenu { public: - StashMenu() : Menu(MF_SINGLESELECT), can_travel(false) { } + StashMenu() : InvMenu(MF_SINGLESELECT), can_travel(false) { } public: bool can_travel; protected: @@ -341,7 +339,7 @@ void StashMenu::draw_title() { gotoxy(1, 1); textcolor(title->colour); - cprintf(title->text.c_str()); + cprintf( "%s", title->text.c_str()); if (title->quantity) cprintf(", %d item%s", title->quantity, title->quantity == 1? "" : "s"); @@ -362,7 +360,7 @@ bool StashMenu::process_key(int key) return Menu::process_key(key); } -static void stash_menu_fixup(MenuEntry *me) +static MenuEntry *stash_menu_fixup(MenuEntry *me) { const item_def *item = static_cast( me->data ); if (item->base_type == OBJ_GOLD) @@ -370,19 +368,20 @@ static void stash_menu_fixup(MenuEntry *me) me->quantity = 0; me->colour = DARKGREY; } + + return (me); } bool Stash::show_menu(const std::string &prefix, bool can_travel) const { StashMenu menu; - MenuEntry *mtitle = new MenuEntry(" Stash (" + prefix); + MenuEntry *mtitle = new MenuEntry("Stash (" + prefix, MEL_TITLE); menu.can_travel = can_travel; - mtitle->colour = WHITE; mtitle->quantity = items.size(); menu.set_title(mtitle); - populate_item_menu(&menu, items, stash_menu_fixup); + menu.load_items( InvMenu::xlat_itemvect(items), stash_menu_fixup); std::vector sel; while (true) { @@ -412,7 +411,9 @@ std::string Stash::description() const if (sz > 1) { char additionals[50]; - snprintf(additionals, sizeof additionals, " (+%lu)", (unsigned long) (sz - 1)); + snprintf(additionals, sizeof additionals, + " (+%lu)", + (unsigned long) (sz - 1)); desc += additionals; } return (desc); @@ -467,8 +468,6 @@ void Stash::write(std::ostream &os, bool identify) const { - int i; - if (!enabled || (items.size() == 0 && verified)) return; os << "(" << ((int) x - refx) << ", " << ((int) y - refy) << (place.length()? ", " + place : "") @@ -476,7 +475,7 @@ void Stash::write(std::ostream &os, << std::endl; char buf[ITEMNAME_SIZE]; - for (i = 0; i < (int) items.size(); ++i) + for (int i = 0; i < (int) items.size(); ++i) { item_def item = items[i]; @@ -512,9 +511,9 @@ void Stash::write(std::ostream &os, if (desc.length()) { // Walk backwards and prepend indenting spaces to \n characters - for (i = desc.length() - 1; i >= 0; --i) - if (desc[i] == '\n') - desc.insert(i + 1, " "); + for (int j = desc.length() - 1; j >= 0; --j) + if (desc[j] == '\n') + desc.insert(j + 1, " "); os << " " << desc << std::endl; } } @@ -621,14 +620,7 @@ std::string ShopInfo::shop_item_desc(const shop_item &si) const desc = munge_description(get_item_description(si.item, Options.verbose_dump, true)); - - // trim leading whitespace - std::string::size_type notwhite = desc.find_first_not_of(" \t\n"); - desc.erase(0, notwhite); - - // trim trailing whitespace - notwhite = desc.find_last_not_of(" \t\n"); - desc.erase(notwhite + 1); + trim_string(desc); // Walk backwards and prepend indenting spaces to \n characters for (int i = desc.length() - 1; i >= 0; --i) @@ -649,9 +641,8 @@ bool ShopInfo::show_menu(const std::string &place, ShopId id(shoptype); StashMenu menu; - MenuEntry *mtitle = new MenuEntry(" " + name + " (" + place); + MenuEntry *mtitle = new MenuEntry(name + " (" + place, MEL_TITLE); menu.can_travel = can_travel; - mtitle->colour = WHITE; mtitle->quantity = items.size(); menu.set_title(mtitle); @@ -701,7 +692,7 @@ std::string ShopInfo::description() const return (name); } -bool ShopInfo::matches_search(const std::string &prefix, +bool ShopInfo::matches_search(const std::string &prefix, const base_pattern &search, stash_search_result &res) const { @@ -712,12 +703,12 @@ bool ShopInfo::matches_search(const std::string &prefix, for (unsigned i = 0; i < items.size(); ++i) { - std::string name = shop_item_name(items[i]); + std::string sname = shop_item_name(items[i]); std::string ann = stash_annotate_item( LUA_SEARCH_ANNOTATE, &items[i].item, true); bool thismatch = false; - if (search.matches(prefix + " " + ann + name)) + if (search.matches(prefix + " " + ann + sname)) thismatch = true; else { @@ -729,7 +720,7 @@ bool ShopInfo::matches_search(const std::string &prefix, if (thismatch) { if (!res.count++) - res.match = name; + res.match = sname; res.matches++; } } @@ -965,7 +956,9 @@ bool LevelStashes::in_branch(int branchid) const std::string LevelStashes::level_name() const { int curr_subdungeon_level = subdungeon_depth( branch, depth ); - return (branch_level_name(branch, curr_subdungeon_level)); + return (place_name( + get_packed_place(branch, curr_subdungeon_level, LEVEL_DUNGEON), + true, true)); } std::string LevelStashes::short_level_name() const @@ -1037,11 +1030,11 @@ void LevelStashes::write(std::ostream &os, bool identify) const { const Stash &s = stashes.begin()->second; int refx = s.getX(), refy = s.getY(); - std::string level_name = short_level_name(); + std::string levname = short_level_name(); for (c_stashes::const_iterator iter = stashes.begin(); iter != stashes.end(); iter++) { - iter->second.write(os, refx, refy, level_name, identify); + iter->second.write(os, refx, refy, levname, identify); } } os << std::endl; @@ -1291,7 +1284,8 @@ void StashTracker::search_stashes() mpr("", MSGCH_PROMPT); char buf[400]; - bool validline = cancelable_get_line(buf, sizeof buf, 80, &search_history); + bool validline = + !cancelable_get_line(buf, sizeof buf, 80, &search_history); mesclr(); if (!validline || (!*buf && !lastsearch.length())) return; @@ -1381,7 +1375,7 @@ void StashSearchMenu::draw_title() { gotoxy(1, 1); textcolor(title->colour); - cprintf(" %d %s%s", title->quantity, title->text.c_str(), + cprintf("%d %s%s", title->quantity, title->text.c_str(), title->quantity > 1? "es" : ""); if (meta_key) @@ -1395,8 +1389,7 @@ bool StashSearchMenu::process_key(int key) { if (key == '?') { - if (sel) - sel->clear(); + sel.clear(); meta_key = !meta_key; update_title(); return true; @@ -1417,8 +1410,7 @@ void StashTracker::display_search_results( stashmenu.can_travel = travelable; std::string title = "matching stash"; - MenuEntry *mtitle = new MenuEntry(title); - mtitle->colour = WHITE; + MenuEntry *mtitle = new MenuEntry(title, MEL_TITLE); // Abuse of the quantity field. mtitle->quantity = results.size(); stashmenu.set_title(mtitle); @@ -1509,100 +1501,3 @@ void StashTracker::display_search_results( StashTracker stashes; #endif - -std::string branch_level_name(unsigned char branch, int sub_depth) -{ - int ltype = sub_depth == 0xFF? branch : 0; - if (ltype == LEVEL_PANDEMONIUM) - return ("Pandemonium"); - else if (ltype == LEVEL_ABYSS) - return ("The Abyss"); - else if (ltype == LEVEL_LABYRINTH) - return ("A Labyrinth"); - else - { - char buf[200]; - const char *s = NULL; - *buf = 0; - // level_type == LEVEL_DUNGEON - if (branch != BRANCH_VESTIBULE_OF_HELL - && branch != BRANCH_ECUMENICAL_TEMPLE - && branch != BRANCH_HALL_OF_BLADES) - snprintf(buf, sizeof buf, "Level %d", sub_depth); - - switch (branch) - { - case BRANCH_MAIN_DUNGEON: - s = " of the Dungeon"; - break; - case BRANCH_DIS: - s = " of Dis"; - break; - case BRANCH_GEHENNA: - s = " of Gehenna"; - break; - case BRANCH_VESTIBULE_OF_HELL: - s = "The Vestibule of Hell"; - break; - case BRANCH_COCYTUS: - s = " of Cocytus"; - break; - case BRANCH_TARTARUS: - s = " of Tartarus"; - break; - case BRANCH_INFERNO: - s = " of the Inferno"; - break; - case BRANCH_THE_PIT: - s = " of the Pit"; - break; - case BRANCH_ORCISH_MINES: - s = " of the Orcish Mines"; - break; - case BRANCH_HIVE: - s = " of the Hive"; - break; - case BRANCH_LAIR: - s = " of the Lair"; - break; - case BRANCH_SLIME_PITS: - s = " of the Slime Pits"; - break; - case BRANCH_VAULTS: - s = " of the Vaults"; - break; - case BRANCH_CRYPT: - s = " of the Crypt"; - break; - case BRANCH_HALL_OF_BLADES: - s = "The Hall of Blades"; - break; - case BRANCH_HALL_OF_ZOT: - s = " of the Realm of Zot"; - break; - case BRANCH_ECUMENICAL_TEMPLE: - s = "The Ecumenical Temple"; - break; - case BRANCH_SNAKE_PIT: - s = " of the Snake Pit"; - break; - case BRANCH_ELVEN_HALLS: - s = " of the Elven Halls"; - break; - case BRANCH_TOMB: - s = " of the Tomb"; - break; - case BRANCH_SWAMP: - s = " of the Swamp"; - break; - } - if (s) - strncat(buf, s, sizeof(buf) - 1); - return (buf); - } -} - -std::string branch_level_name(unsigned short packed_place) -{ - return branch_level_name(packed_place >> 8, packed_place & 0xFF); -} diff --git a/crawl-ref/source/stash.h b/crawl-ref/source/stash.h index 5e36eb4a61..727fb656e4 100644 --- a/crawl-ref/source/stash.h +++ b/crawl-ref/source/stash.h @@ -317,10 +317,6 @@ void describe_stash(int x, int y); #endif // STASH_TRACKING -std::string branch_level_name(unsigned char branch, int sub_depth); - -std::string branch_level_name(unsigned short packed_place); - std::string userdef_annotate_item(const char *s, const item_def *item, bool exclusive = false); diff --git a/crawl-ref/source/stuff.cc b/crawl-ref/source/stuff.cc index 3a0a281d34..e9b97624ea 100644 --- a/crawl-ref/source/stuff.cc +++ b/crawl-ref/source/stuff.cc @@ -14,7 +14,10 @@ */ #include "AppHdr.h" +#include "direct.h" +#include "monplace.h" #include "stuff.h" +#include "view.h" #include #include @@ -50,6 +53,7 @@ #include "libunix.h" #endif +#include "delay.h" #include "externs.h" #include "macro.h" @@ -57,8 +61,9 @@ #include "monstuff.h" #include "mon-util.h" #include "mt19937ar.h" -#include "player.h" +#include "notes.h" #include "output.h" +#include "player.h" #include "skills2.h" #include "view.h" @@ -133,14 +138,14 @@ void tag_followers( void ) continue; } - if (monster_habitat(fmenv->type) != DNGN_FLOOR) + if (!monster_habitable_grid(fmenv, DNGN_FLOOR)) continue; if (fmenv->speed_increment < 50) continue; - // only friendly monsters, or those actively seeking the - // player, will follow up/down stairs. + // only friendly monsters, or those actively seeking the + // player, will follow up/down stairs. if (!(mons_friendly(fmenv) || (fmenv->behaviour == BEH_SEEK && fmenv->foe == MHITYOU))) { @@ -330,6 +335,12 @@ unsigned long random_int( void ) return (genrand_int32()); } +int random_range(int low, int high) +{ + ASSERT(low <= high); + return (low + random2(high - low + 1)); +} + int random2( int max ) { if (max <= 1) @@ -355,6 +366,31 @@ void pop_rng_state() #endif // USE_SYSTEM_RAND +// Attempts to make missile weapons nicer to the player by +// reducing the extreme variance in damage done. +void scale_dice( dice_def &dice, int threshold ) +{ + while (dice.size > threshold) + { + dice.num *= 2; + // If it's an odd number, lose one; this is more than + // compensated by the increase in number of dice. + dice.size /= 2; + } +} + +int bestroll(int max, int rolls) +{ + int best = 0; + for (int i = 0; i < rolls; i++) + { + int curr = random2(max); + if (curr > best) + best = curr; + } + return (best); +} + // random2avg() returns same mean value as random2() but with a lower variance // never use with rolls < 2 as that would be silly - use random2() instead {dlb} int random2avg(int max, int rolls) @@ -438,10 +474,6 @@ void end(int end_arg) unixcurses_shutdown(); #endif -#ifdef MAC - deinit_mac(); -#endif - #ifdef WIN32CONSOLE deinit_libw32c(); #endif @@ -474,7 +506,10 @@ void redraw_screen(void) if (Options.delay_message_clear) mesclr( true ); + bool note_status = notes_are_active(); + activate_notes(false); new_level(); + activate_notes(note_status); viewwindow(1, false); #endif @@ -539,6 +574,37 @@ int stepdown_value(int base_value, int stepping, int first_step, } // end stepdown_value() +int skill_bump( int skill ) +{ + return ((you.skills[skill] < 3) ? you.skills[skill] * 2 + : you.skills[skill] + 3); +} + +// This gives (default div = 20, shift = 3): +// - shift/div% @ stat_level = 0; (default 3/20 = 15%, or 20% at stat 1) +// - even (100%) @ stat_level = div - shift; (default 17) +// - 1/div% per stat_level (default 1/20 = 5%) +int stat_mult( int stat_level, int value, int div, int shift ) +{ + return (((stat_level + shift) * value) / ((div > 1) ? div : 1)); +} + +// As above but inverted (ie 5x penalty at stat 1) +int stat_div( int stat_level, int value, int mult, int shift ) +{ + int div = stat_level + shift; + + if (div < 1) + div = 1; + + return ((mult * value) / div); +} + +// Calculates num/den and randomly adds one based on the remainder. +int div_rand_round( int num, int den ) +{ + return (num / den + (random2(den) < num % den)); +} // I got so tired of seeing: ".. && random2(foo) == 0 && .." in the code // that I broke down and wrote this little -- very little -- function. @@ -589,10 +655,9 @@ void canned_msg(unsigned char which_message) switch (which_message) { case MSG_SOMETHING_APPEARS: - strcpy(info, "Something appears "); - strcat(info, (you.species == SP_NAGA || you.species == SP_CENTAUR) - ? "before you" : "at your feet"); - strcat(info, "!"); + snprintf(info, INFO_SIZE, "Something appears %s!", + (you.species == SP_NAGA || you.species == SP_CENTAUR) + ? "before you" : "at your feet"); mpr(info); break; @@ -667,7 +732,44 @@ bool yesno( const char *str, bool safe, int safeanswer, bool clear_after ) } } // end yesno() -// More accurate than distance() given the actual movement geonmetry -- bwr +// like yesno(), but returns 0 for no, 1 for yes, and -1 for quit +int yesnoquit( const char* str, bool safe, int safeanswer, bool clear_after ) +{ + unsigned char tmp; + + interrupt_activity( AI_FORCE_INTERRUPT ); + while (1) + { + mpr(str, MSGCH_PROMPT); + + tmp = (unsigned char) getch(); + + if ( tmp == 27 || tmp == 'q' || tmp == 'Q' ) + return -1; + + if ((tmp == ' ' || tmp == '\r' || tmp == '\n') && safeanswer) + tmp = safeanswer; + + if (Options.easy_confirm == CONFIRM_ALL_EASY + || tmp == safeanswer + || (Options.easy_confirm == CONFIRM_SAFE_EASY && safe)) + { + tmp = toupper( tmp ); + } + + if (clear_after) + mesclr(); + + if (tmp == 'N') + return 0; + else if (tmp == 'Y') + return 1; + else + mpr("[Y]es or [N]o only, please."); + } +} + +// More accurate than distance() given the actual movement geometry -- bwr int grid_distance( int x, int y, int x2, int y2 ) { const int dx = abs( x - x2 ); @@ -679,7 +781,7 @@ int grid_distance( int x, int y, int x2, int y2 ) int distance( int x, int y, int x2, int y2 ) { - //jmf: now accurate, but remember to only compare vs. pre-squared distances. + //jmf: now accurate, but remember to only compare vs. pre-squared distances // thus, next to == (distance(m1.x,m1.y, m2.x,m2.y) <= 2) const int dx = x - x2; const int dy = y - y2; @@ -694,8 +796,6 @@ bool adjacent( int x, int y, int x2, int y2 ) bool silenced(char x, char y) { -#ifdef USE_SILENCE_CODE - if (you.duration[DUR_SILENCE] > 0 && distance(x, y, you.x_pos, you.y_pos) <= 36) // (6 * 6) { @@ -711,26 +811,308 @@ bool silenced(char x, char y) // } return false; } - -#else - return false; -#endif } // end silenced() bool player_can_hear(char x, char y) { -#ifdef USE_SILENCE_CODE return (!silenced(x, y) && !silenced(you.x_pos, you.y_pos)); -#else - return true; -#endif } // end player_can_hear() +// Returns true if inside the area the player can move and dig (ie exclusive) +bool in_bounds( int x, int y ) +{ + return (x > X_BOUND_1 && x < X_BOUND_2 + && y > Y_BOUND_1 && y < Y_BOUND_2); +} + +// Returns true if inside the area the player can map (ie inclusive). +// Note that terrain features should be in_bounds() leaving an outer +// ring of rock to frame the level. +bool map_bounds( int x, int y ) +{ + return (x >= X_BOUND_1 && x <= X_BOUND_2 + && y >= Y_BOUND_1 && y <= Y_BOUND_2); +} + +// Returns a random location in (x_pos, y_pos)... the grid will be +// DNGN_FLOOR if clear, and NON_MONSTER if empty. Exclusive tells +// if we're using in_bounds() or map_bounds() restriction. +void random_in_bounds( int &x_pos, int &y_pos, int terr, bool empty, bool excl ) +{ + bool done = false; + + do + { + x_pos = X_BOUND_1 + random2( X_WIDTH - 2 * excl ) + 1 * excl; + y_pos = Y_BOUND_1 + random2( Y_WIDTH - 2 * excl ) + 1 * excl; + + if (terr == DNGN_RANDOM) + done = true; + else if (terr == grd[x_pos][y_pos]) + done = true; + else if (terr == DNGN_DEEP_WATER && grd[x_pos][y_pos] == DNGN_SHALLOW_WATER) + done = true; + else if (empty + && mgrd[x_pos][y_pos] != NON_MONSTER + && (x_pos != you.x_pos || y_pos != you.y_pos)) + { + done = true; + } + } + while (!done); +} + +// takes rectangle (x1,y1)-(x2,y2) and shifts it somewhere randomly in bounds +void random_place_rectangle( int &x1, int &y1, int &x2, int &y2, bool excl ) +{ + const unsigned int dx = abs( x2 - x1 ); + const unsigned int dy = abs( y2 - y1 ); + + x1 = X_BOUND_1 + random2( X_WIDTH - dx - 2 * excl ) + excl; + y1 = Y_BOUND_1 + random2( Y_WIDTH - dy - 2 * excl ) + excl; + + x2 = x1 + dx; + y2 = y1 + dy; +} + +// returns true if point (px,py) is in rectangle (rx1, ry1) - (rx2, ry2) +bool in_rectangle( int px, int py, int rx1, int ry1, int rx2, int ry2, + bool excl ) +{ + ASSERT( rx1 < rx2 - 1 && ry1 < ry2 - 1 ); + + if (excl) + { + rx1++; + rx2--; + ry1++; + ry2--; + } + + return (px >= rx1 && px <= rx2 && py >= ry1 && py <= ry2); +} + +// XXX: this can be done better +// returns true if rectables a and b overlap +bool rectangles_overlap( int ax1, int ay1, int ax2, int ay2, + int bx1, int by1, int bx2, int by2, + bool excl ) +{ + ASSERT( ax1 < ax2 - 1 && ay1 < ay2 - 1 ); + ASSERT( bx1 < bx2 - 1 && by1 < by2 - 1 ); + + if (excl) + { + ax1++; + ax2--; + ay1++; + ay2--; + } + + return (in_rectangle( ax1, ay1, bx1, by1, bx2, by2, excl ) + || in_rectangle( ax1, ay2, bx1, by1, bx2, by2, excl ) + || in_rectangle( ax2, ay1, bx1, by1, bx2, by2, excl ) + || in_rectangle( ax2, ay2, bx1, by1, bx2, by2, excl )); +} + unsigned char random_colour(void) { return (1 + random2(15)); } // end random_colour() +// returns if a colour is one of the special element colours (ie not regular) +bool is_element_colour( int col ) +{ + // striping any COLFLAGS (just in case) + return ((col & 0x007f) >= EC_FIRE); +} + +int element_colour( int element, bool no_random ) +{ + // Doing this so that we don't have to do recursion here at all + // (these were the only cases which had possible double evaluation): + if (element == EC_FLOOR) + element = env.floor_colour; + else if (element == EC_ROCK) + element = env.rock_colour; + + // pass regular colours through for safety. + if (!is_element_colour( element )) + return (element); + + int ret = BLACK; + + // Setting no_random to true will get the first colour in the cases + // below. This is potentially useful for calls to this function + // which might want a consistant result. + int tmp_rand = (no_random ? 0 : random2(120)); + + switch (element & 0x007f) // strip COLFLAGs just in case + { + case EC_FIRE: + ret = (tmp_rand < 40) ? RED : + (tmp_rand < 80) ? YELLOW + : LIGHTRED; + break; + + case EC_ICE: + ret = (tmp_rand < 40) ? LIGHTBLUE : + (tmp_rand < 80) ? BLUE + : WHITE; + break; + + case EC_EARTH: + ret = (tmp_rand < 60) ? BROWN : LIGHTRED; + break; + + case EC_AIR: + ret = (tmp_rand < 60) ? LIGHTGREY : WHITE; + break; + + case EC_ELECTRICITY: + ret = (tmp_rand < 40) ? LIGHTCYAN : + (tmp_rand < 80) ? LIGHTBLUE + : CYAN; + break; + + case EC_POISON: + ret = (tmp_rand < 60) ? LIGHTGREEN : GREEN; + break; + + case EC_WATER: + ret = (tmp_rand < 60) ? BLUE : CYAN; + break; + + case EC_MAGIC: + ret = (tmp_rand < 30) ? LIGHTMAGENTA : + (tmp_rand < 60) ? LIGHTBLUE : + (tmp_rand < 90) ? MAGENTA + : BLUE; + break; + + case EC_MUTAGENIC: + case EC_WARP: + ret = (tmp_rand < 60) ? LIGHTMAGENTA : MAGENTA; + break; + + case EC_ENCHANT: + ret = (tmp_rand < 60) ? LIGHTBLUE : BLUE; + break; + + case EC_HEAL: + ret = (tmp_rand < 60) ? LIGHTBLUE : YELLOW; + break; + + case EC_BLOOD: + ret = (tmp_rand < 60) ? RED : DARKGREY; + break; + + case EC_DEATH: // assassin + case EC_NECRO: // necromancer + ret = (tmp_rand < 80) ? DARKGREY : MAGENTA; + break; + + case EC_UNHOLY: // ie demonology + ret = (tmp_rand < 80) ? DARKGREY : RED; + break; + + case EC_DARK: + ret = DARKGREY; + break; + + case EC_HOLY: + ret = (tmp_rand < 60) ? YELLOW : WHITE; + break; + + case EC_VEHUMET: + ret = (tmp_rand < 40) ? LIGHTRED : + (tmp_rand < 80) ? LIGHTMAGENTA + : LIGHTBLUE; + break; + + case EC_CRYSTAL: + ret = (tmp_rand < 40) ? LIGHTGREY : + (tmp_rand < 80) ? GREEN + : LIGHTRED; + break; + + case EC_SLIME: + ret = (tmp_rand < 40) ? GREEN : + (tmp_rand < 80) ? BROWN + : LIGHTGREEN; + break; + + case EC_SMOKE: + ret = (tmp_rand < 30) ? LIGHTGREY : + (tmp_rand < 60) ? DARKGREY : + (tmp_rand < 90) ? LIGHTBLUE + : MAGENTA; + break; + + case EC_JEWEL: + ret = (tmp_rand < 12) ? WHITE : + (tmp_rand < 24) ? YELLOW : + (tmp_rand < 36) ? LIGHTMAGENTA : + (tmp_rand < 48) ? LIGHTRED : + (tmp_rand < 60) ? LIGHTGREEN : + (tmp_rand < 72) ? LIGHTBLUE : + (tmp_rand < 84) ? MAGENTA : + (tmp_rand < 96) ? RED : + (tmp_rand < 108) ? GREEN + : BLUE; + break; + + case EC_ELVEN: + ret = (tmp_rand < 40) ? LIGHTGREEN : + (tmp_rand < 80) ? GREEN : + (tmp_rand < 100) ? LIGHTBLUE + : BLUE; + break; + + case EC_DWARVEN: + ret = (tmp_rand < 40) ? BROWN : + (tmp_rand < 80) ? LIGHTRED : + (tmp_rand < 100) ? LIGHTGREY + : CYAN; + break; + + case EC_ORCISH: + ret = (tmp_rand < 40) ? DARKGREY : + (tmp_rand < 80) ? RED : + (tmp_rand < 100) ? BROWN + : MAGENTA; + break; + + case EC_GILA: + ret = (tmp_rand < 30) ? LIGHTMAGENTA : + (tmp_rand < 60) ? MAGENTA : + (tmp_rand < 90) ? YELLOW : + (tmp_rand < 105) ? LIGHTRED + : RED; + break; + + case EC_STONE: + if (player_in_branch( BRANCH_HALL_OF_ZOT )) + ret = env.rock_colour; + else + ret = LIGHTGREY; + break; + + case EC_RANDOM: + ret = 1 + random2(15); // always random + break; + + case EC_FLOOR: // should alredy be handled + case EC_ROCK: // should alredy be handled + default: + break; + } + + ASSERT( !is_element_colour( ret ) ); + + return ((ret == BLACK) ? GREEN : ret); +} + char index_to_letter(int the_index) { return (the_index + ((the_index < 26) ? 'a' : ('A' - 26))); @@ -771,7 +1153,7 @@ int near_stairs(int px, int py, int max_dist, unsigned char &stair_gfx) && grd[x][y] <= DNGN_RETURN_FROM_SWAMP && grd[x][y] != DNGN_ENTER_SHOP) // silly { - stair_gfx = mapch(grd[x][y]); + stair_gfx = get_sightmap_char(grd[x][y]); return ((x == you.x_pos && y == you.y_pos) ? 2 : 1); } } @@ -779,3 +1161,48 @@ int near_stairs(int px, int py, int max_dist, unsigned char &stair_gfx) return false; } + +bool is_trap_square(int x, int y) +{ + return (grd[x][y] >= DNGN_TRAP_MECHANICAL + && grd[x][y] <= DNGN_UNDISCOVERED_TRAP); +} + +// Does the equivalent of KILL_RESET on all monsters in LOS. Should only be +// applied to new games. +void zap_los_monsters() +{ + losight(env.show, grd, you.x_pos, you.y_pos); + + for (int y = LOS_SY; y <= LOS_EY; ++y) + { + for (int x = LOS_SX; x <= LOS_EX; ++x) + { + if (!in_vlos(x, y)) + continue; + + const int gx = view2gridX(x), + gy = view2gridY(y); + + if (!map_bounds(gx, gy)) + continue; + + if (gx == you.x_pos && gy == you.y_pos) + continue; + + int imon = mgrd[gx][gy]; + if (imon == NON_MONSTER || imon == MHITYOU) + continue; + + // If we ever allow starting with a friendly monster, + // we'll have to check here. + monsters *mon = &menv[imon]; +#ifdef DEBUG_DIAGNOSTICS + char mname[ITEMNAME_SIZE]; + moname(mon->type, true, DESC_PLAIN, mname); + mprf(MSGCH_DIAGNOSTICS, "Dismissing %s", mname); +#endif + monster_die(mon, KILL_DISMISSED, 0); + } + } +} diff --git a/crawl-ref/source/stuff.h b/crawl-ref/source/stuff.h index 72c7d2dcf5..c05231ceb5 100644 --- a/crawl-ref/source/stuff.h +++ b/crawl-ref/source/stuff.h @@ -3,6 +3,8 @@ * Summary: Misc stuff. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <3> 11/14/99 cdl added random40 @@ -17,192 +19,82 @@ #include "externs.h" char *const make_time_string(time_t abs_time, char *const buff, int buff_size, bool terse = false); - void set_redraw_status( unsigned long flags ); - void tag_followers( void ); void untag_followers( void ); void seed_rng(void); - void seed_rng(long seed); - void push_rng_state(); - void pop_rng_state(); -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: acr - * *********************************************************************** */ void cf_setseed(void); - - -/* *********************************************************************** - * called from: xxx - * *********************************************************************** */ bool coinflip(void); - - -/* *********************************************************************** - * called from: xxx - * *********************************************************************** */ +int div_rand_round( int num, int den ); bool one_chance_in(int a_million); - - -/* *********************************************************************** - * called from: xxx - * *********************************************************************** */ int random2(int randmax); - +int random_range(int low, int high); unsigned long random_int(void); - -/* *********************************************************************** - * called from: xxx - * *********************************************************************** */ int random2avg( int max, int rolls ); +int bestroll(int max, int rolls); int roll_dice( int num, int size ); int roll_dice( const struct dice_def &dice ); +void scale_dice( dice_def &dice, int threshold = 24 ); -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: beam - fight - misc - * *********************************************************************** */ int random2limit(int max, int limit); - - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: bang - beam - monplace - monstuff - spells1 - spells2 - - * spells4 - view - * *********************************************************************** */ bool see_grid(unsigned char grx, unsigned char gry); - - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: spells2 - * *********************************************************************** */ int stepdown_value(int base_value, int stepping, int first_step, int last_step, int ceiling_value); - - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: ability - command - effects - food - item_use - items - - * newgame - ouch - shopping - spell - spl-book - spells1 - - * spells3 - * *********************************************************************** */ +int stat_mult( int stat_level, int value, int div = 20, int shift = 3 ); +int stat_div( int stat_level, int value, int div = 20, int shift = 3 ); +int skill_bump( int skill ); unsigned char get_ch(void); - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: files - newgame - ouch - * *********************************************************************** */ void end(int end_arg); - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: newgame - * *********************************************************************** */ void modify_all_stats(int STmod, int IQmod, int DXmod); - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: ability - acr - command - direct - invent - item_use - - * religion - shopping - spell - spl-book - spells3 - * *********************************************************************** */ void redraw_screen(void); - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: ability - acr - beam - command - decks - debug - effects - - * it_use3 - item_use - items - misc - ouch - religion - - * spell - spl-book - spells - spells1 - spells2 - spells3 - - * spells4 - * *********************************************************************** */ void canned_msg(unsigned char which_message); - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: ability - acr - command - it_use3 - item_use - items - - * misc - ouch - religion - spl-book - spells4 - * *********************************************************************** */ -// If safeanswer is nonzero, it should be a lowercase letter. bool yesno( const char * str, bool safe = true, int safeanswer = 0, bool clear_after = true ); +int yesnoquit( const char* str, bool safe = true, + int safeanswer = 0, bool clear_after = true ); + + +bool in_bounds( int x, int y ); +bool map_bounds( int x, int y ); -// last updated 21may2000 {dlb} -/* *********************************************************************** - * called from: fight - monstuff - spells4 - view - * *********************************************************************** */ int grid_distance( int x, int y, int x2, int y2 ); int distance( int x, int y, int x2, int y2); bool adjacent( int x, int y, int x2, int y2 ); - -// last updated 21may2000 {dlb} -/* *********************************************************************** - * called from: ability - acr - bang - beam - effects - fight - it_use2 - - * it_use3 - monstuff - mstuff2 - religion - spell - spells - - * spells3 - spells4 - stuff - view - * *********************************************************************** */ bool silenced(char x, char y); - -// last updated XXmay2000 {dlb} -/* *********************************************************************** - * called from: xxx - * *********************************************************************** */ bool player_can_hear(char x, char y); - -// last updated 27may2000 {dlb} -/* *********************************************************************** - * called from: dungeon - files - it_use2 - it_use3 - monstuff - mon-util - - * mstuff2 - newgame - spells - view - * *********************************************************************** */ unsigned char random_colour(void); +bool is_element_colour( int col ); +int element_colour( int element, bool no_random = false ); - -// last updated 08jun2000 {dlb} -/* *********************************************************************** - * called from: ability - chardump - command - files - invent - item_use - - * items - newgame - spl-book - spells0 - spells1 - * *********************************************************************** */ char index_to_letter (int the_index); - -// last updated 08jun2000 {dlb} -/* *********************************************************************** - * called from: ability - command - food - it_use3 - item_use - items - - * spell - spl-book - spells1 - spells3 - * *********************************************************************** */ int letter_to_index(int the_letter); - -// last updated 16mar2001 {dlb} -/* *********************************************************************** - * called from: monplace - * *********************************************************************** */ int near_stairs(int px, int py, int max_dist, unsigned char &stair_gfx); -// last updated 16mar2001 {dlb} -/* *********************************************************************** - * called from: fight - * *********************************************************************** */ -// just want to do this in a consistent fashion inline bool testbits(unsigned int flags, unsigned int test) { return ((flags & test) == test); } +bool is_trap_square(int x, int y); + +void zap_los_monsters(); + #endif diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc index 1aa6752a6b..f671197067 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -17,7 +17,7 @@ than anything else, it is not actually saved as a tag) to TAG_XXX. NUM_TAGS is equal to the actual number of defined tags. -2. Tags are created with tag_construct(), which forwards the construction +2. Tags are created with tag_construct(), which forwards the construction request appropriately. tag_write() is then used to write the tag to an output stream. @@ -25,15 +25,15 @@ forwards the request appropriately, returning the ID of the tag it found, or zero if no tag was found. -4. In order to know which tags are used by a particular file type, a client +4. In order to know which tags are used by a particular file type, a client calls tag_set_expected( fileType ), which sets up an array of chars. Within the array, a value of 1 means the tag is expected; -1 means that the tag is not expected. A client can then set values in this array to anything other than 1 to indicate a successful tag_read() of that tag. 5. A case should be provided in tag_missing() for any tag which might be - missing from a tagged save file. For example, if a developer adds - TAG_YOU_NEW_STUFF to the player save file, he would have to provide a + missing from a tagged save file. For example, if a developer adds + TAG_YOU_NEW_STUFF to the player save file, he would have to provide a case in tag_missing() for this tag since it might not be there in earlier savefiles. The tags defined with the original tag system (and so not needing cases in tag_missing()) are as follows: @@ -65,18 +65,6 @@ #include #endif -#ifdef USE_EMX -#include -#include -#include -#endif - -#ifdef OS9 -#include -#else -#include -#endif - #include "AppHdr.h" #include "abl-show.h" @@ -84,6 +72,7 @@ #include "externs.h" #include "files.h" #include "itemname.h" +#include "itemprop.h" #include "monstuff.h" #include "mon-util.h" #include "randart.h" @@ -100,9 +89,6 @@ extern FixedArray < unsigned char, MAX_LEVELS, MAX_BRANCHES > altars_present; extern FixedVector < char, MAX_BRANCHES > stair_level; extern FixedArray < unsigned char, MAX_LEVELS, MAX_BRANCHES > feature; -extern unsigned char your_sign; /* these two are defined in view.cc */ -extern unsigned char your_colour; - // temp file pairs used for file level cleanup FixedArray < bool, MAX_LEVELS, MAX_BRANCHES > tmp_file_pairs; @@ -208,23 +194,31 @@ long unmarshallLong(struct tagHeader &th) return data; } +union float_marshall_kludge +{ + // [ds] Does ANSI C guarantee that sizeof(float) == sizeof(long)? + float f_num; + long l_num; +}; + // single precision float -- marshall in network order. void marshallFloat(struct tagHeader &th, float data) { - long intBits = *((long *)(&data)); - marshallLong(th, intBits); + float_marshall_kludge k; + k.f_num = data; + marshallLong(th, k.l_num); } // single precision float -- unmarshall in network order. float unmarshallFloat(struct tagHeader &th) { - long intBits = unmarshallLong(th); - - return *((float *)(&intBits)); + float_marshall_kludge k; + k.l_num = unmarshallLong(th); + return k.f_num; } // string -- marshall length & string data -void marshallString(struct tagHeader &th, char *data, int maxSize) +void marshallString(struct tagHeader &th, const char *data, int maxSize) { // allow for very long strings. short len = strlen(data); @@ -250,7 +244,7 @@ void unmarshallString(struct tagHeader &th, char *data, int maxSize) // read the actual string and null terminate memcpy(data, &tagBuffer[th.offset], copylen); - data[copylen] = '\0'; + data[copylen] = 0; th.offset += len; } @@ -285,7 +279,7 @@ void make_date_string( time_t in_date, char buff[20] ) { if (in_date <= 0) { - buff[0] = '\0'; + buff[0] = 0; return; } @@ -481,7 +475,7 @@ int tag_read(FILE *fp, char minorVersion) // For now, none are supported. // This function will be called AFTER all other tags for -// the savefile are read, so everything that can be +// the savefile are read, so everything that can be // initialized should have been by now. // minorVersion is available for any child functions that need @@ -515,6 +509,10 @@ void tag_set_expected(char tags[], int fileType) if (i >= TAG_YOU && i <=TAG_YOU_DUNGEON) tags[i] = 1; break; + case TAGTYPE_PLAYER_NAME: + if (i == TAG_YOU) + tags[i] = 1; + break; case TAGTYPE_LEVEL: if (i >= TAG_LEVEL && i <= TAG_LEVEL_ATTITUDE) tags[i] = 1; @@ -529,12 +527,12 @@ void tag_set_expected(char tags[], int fileType) } } -// NEVER _MODIFY_ THE CONSTRUCT/READ FUNCTIONS, EVER. THAT IS THE WHOLE POINT +// NEVER _MODIFY_ THE CONSTRUCT/READ FUNCTIONS, EVER. THAT IS THE WHOLE POINT // OF USING TAGS. Apologies for the screaming. // Note anyway that the formats are somewhat flexible; you could change map -// size, the # of slots in player inventory, etc. Constants like GXM, -// NUM_EQUIP, and NUM_DURATIONS are saved, so the appropriate amount will +// size, the # of slots in player inventory, etc. Constants like GXM, +// NUM_EQUIP, and NUM_DURATIONS are saved, so the appropriate amount will // be restored even if a later version increases these constants. // --------------------------- player tags (foo.sav) -------------------- // @@ -555,8 +553,8 @@ static void tag_construct_you(struct tagHeader &th) marshallByte(th,you.rotting); marshallByte(th,you.exhausted); marshallByte(th,you.deaths_door); - marshallByte(th,your_sign); - marshallByte(th,your_colour); + marshallByte(th,you.symbol); + marshallByte(th,you.colour); marshallByte(th,you.pet_target); marshallByte(th,you.max_level); @@ -687,6 +685,10 @@ static void tag_construct_you(struct tagHeader &th) for (i = 0; i < MAX_NUM_GODS; i++) marshallByte(th, you.worshipped[i]); + // what is the extent of divine generosity? + for (i = 0; i < MAX_NUM_GODS; i++) + marshallShort(th, you.num_gifts[i]); + marshallByte(th, you.gift_timeout); marshallByte(th, you.normal_vision); marshallByte(th, you.current_vision); @@ -738,6 +740,8 @@ static void tag_construct_you_items(struct tagHeader &th) marshallShort(th,you.inv[i].plus2); marshallShort(th, you.inv[i].orig_place); marshallShort(th, you.inv[i].orig_monnum); + /*** HP CHANGE ***/ + marshallString(th, you.inv[i].inscription.c_str(), 80); } // item descrip for each type & subtype @@ -758,7 +762,7 @@ static void tag_construct_you_items(struct tagHeader &th) marshallByte(th, 50); // this is really dumb. We copy the id[] array from itemname - // to the stack, for no good reason that I can see. + // to the stack, for no good reason that I can see. char identy[4][50]; save_id(identy); @@ -833,8 +837,8 @@ static void tag_read_you(struct tagHeader &th, char minorVersion) you.rotting = unmarshallByte(th); you.exhausted = unmarshallByte(th); you.deaths_door = unmarshallByte(th); - your_sign = unmarshallByte(th); - your_colour = unmarshallByte(th); + you.symbol = unmarshallByte(th); + you.colour = unmarshallByte(th); you.pet_target = unmarshallByte(th); you.max_level = unmarshallByte(th); @@ -956,7 +960,7 @@ static void tag_read_you(struct tagHeader &th, char minorVersion) // how many durations? count_c = unmarshallByte(th); for (j = 0; j < count_c; ++j) - you.duration[j] = unmarshallByte(th); + you.duration[j] = (unsigned char) unmarshallByte(th); // how many attributes? count_c = unmarshallByte(th); @@ -981,6 +985,11 @@ static void tag_read_you(struct tagHeader &th, char minorVersion) count_c = unmarshallByte(th); for (i = 0; i < count_c; i++) you.worshipped[i] = unmarshallByte(th); + + if (minorVersion >= 5) + for (i = 0; i < count_c; i++) + you.num_gifts[i] = unmarshallShort(th); + } else { @@ -1266,6 +1275,7 @@ static void tag_read_you_items(struct tagHeader &th, char minorVersion) for (i = 0; i < count_c; ++i) { you.inv[i].orig_monnum = you.inv[i].orig_place = 0; + you.inv[i].inscription.clear(); if (minorVersion < 1) { you.inv[i].base_type = (unsigned char) unmarshallByte(th); @@ -1294,6 +1304,14 @@ static void tag_read_you_items(struct tagHeader &th, char minorVersion) { you.inv[i].orig_place = unmarshallShort(th); you.inv[i].orig_monnum = unmarshallShort(th); + + if (minorVersion >= 6) + { + /*** HP CHANGE ***/ + char insstring[80]; + unmarshallString(th, insstring, 80); + you.inv[i].inscription = std::string(insstring); + } } } @@ -1485,6 +1503,8 @@ static void tag_construct_level_items(struct tagHeader &th) marshallShort(th, mitm[i].orig_place); marshallShort(th, mitm[i].orig_monnum); + /*** HP CHANGE ***/ + marshallString(th, mitm[i].inscription.c_str(), 80); } } @@ -1506,28 +1526,34 @@ static void tag_construct_level_monsters(struct tagHeader &th) for (i = 0; i < MAX_MONSTERS; i++) { - marshallByte(th, menv[i].armour_class); - marshallByte(th, menv[i].evasion); - marshallByte(th, menv[i].hit_dice); - marshallByte(th, menv[i].speed); - marshallByte(th, menv[i].speed_increment); - marshallByte(th, menv[i].behaviour); - marshallByte(th, menv[i].x); - marshallByte(th, menv[i].y); - marshallByte(th, menv[i].target_x); - marshallByte(th, menv[i].target_y); - marshallByte(th, menv[i].flags); + const monsters &m = menv[i]; + + marshallByte(th, m.armour_class); + marshallByte(th, m.evasion); + marshallByte(th, m.hit_dice); + marshallByte(th, m.speed); + marshallByte(th, m.speed_increment); + marshallByte(th, m.behaviour); + marshallByte(th, m.x); + marshallByte(th, m.y); + marshallByte(th, m.target_x); + marshallByte(th, m.target_y); + marshallByte(th, m.flags); for (j = 0; j < NUM_MON_ENCHANTS; j++) - marshallByte(th, menv[i].enchantment[j]); + marshallByte(th, m.enchantment[j]); - marshallShort(th, menv[i].type); - marshallShort(th, menv[i].hit_points); - marshallShort(th, menv[i].max_hit_points); - marshallShort(th, menv[i].number); + marshallShort(th, m.type); + marshallShort(th, m.hit_points); + marshallShort(th, m.max_hit_points); + marshallShort(th, m.number); + marshallShort(th, m.colour); for (j = 0; j < NUM_MONSTER_SLOTS; j++) - marshallShort(th, menv[i].inv[j]); + marshallShort(th, m.inv[j]); + + for (j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j) + marshallShort(th, m.spells[j]); } } @@ -1658,6 +1684,11 @@ static void tag_read_level_items(struct tagHeader &th, char minorVersion) mitm[i].flags = (unsigned long) unmarshallLong(th); } + // [dshaligram] FIXME, remove this kludge when ARM_CAP is fully + // integrated. + if (mitm[i].base_type == OBJ_ARMOUR && mitm[i].sub_type == ARM_CAP) + mitm[i].sub_type = ARM_HELMET; + // pre 4.2 files had monster items stacked at (2,2) -- moved to (0,0) if (minorVersion < 2 && mitm[i].x == 2 && mitm[i].y == 2) { @@ -1673,10 +1704,20 @@ static void tag_read_level_items(struct tagHeader &th, char minorVersion) mitm[i].slot = unmarshallByte(th); } + mitm[i].inscription.clear(); + if (minorVersion >= 7) { mitm[i].orig_place = unmarshallShort(th); mitm[i].orig_monnum = unmarshallShort(th); + + if (minorVersion >= 9) + { + /*** HP CHANGE ***/ + char insstring[80]; + unmarshallString(th, insstring, 80); + mitm[i].inscription = std::string(insstring); + } } else { @@ -1718,8 +1759,8 @@ static void tag_read_level_monsters(struct tagHeader &th, char minorVersion) menv[i].target_y = unmarshallByte(th); menv[i].flags = unmarshallByte(th); - // VERSION NOTICE: for pre 4.2 files, flags was either 0 - // or 1. Now, we can transfer ENCH_CREATED_FRIENDLY over + // VERSION NOTICE: for pre 4.2 files, flags was either 0 + // or 1. Now, we can transfer ENCH_CREATED_FRIENDLY over // from the enchantments array to flags. // Also need to take care of ENCH_FRIEND_ABJ_xx flags @@ -1755,9 +1796,26 @@ static void tag_read_level_monsters(struct tagHeader &th, char minorVersion) menv[i].max_hit_points = unmarshallShort(th); menv[i].number = unmarshallShort(th); + if (minorVersion >= 10) + menv[i].colour = unmarshallShort(th); + else + // This will be the wrong colour for many cases, we don't care. + menv[i].colour = mons_class_colour( menv[i].type ); + for (j = 0; j < icount; j++) menv[i].inv[j] = unmarshallShort(th); + if (minorVersion >= 10) + { + for (j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j) + menv[i].spells[j] = unmarshallShort(th); + } + else if (menv[i].type != -1) + { + const int book = obsolete_mons_spell_template_index( &menv[i] ); + mons_load_spells( &menv[i], book ); + } + // place monster if (menv[i].type != -1) mgrd[menv[i].x][menv[i].y] = i; @@ -1785,7 +1843,7 @@ void tag_missing_level_attitude() // a foe first time through handle_monster() if // there's one around. - // as for attitude, a couple simple checks + // as for attitude, a couple simple checks // can be used to determine friendly/neutral/ // hostile. int i; diff --git a/crawl-ref/source/tags.h b/crawl-ref/source/tags.h index 7c84709ebf..65a00a208b 100644 --- a/crawl-ref/source/tags.h +++ b/crawl-ref/source/tags.h @@ -3,6 +3,8 @@ * Summary: Auxilary functions to make savefile versioning simpler. * Written by: Gordon Lipford * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> 27 Jan 2001 GDL Created @@ -37,7 +39,7 @@ void marshallShort(struct tagHeader &th, short data); void marshallLong(struct tagHeader &th, long data); void marshallFloat(struct tagHeader &th, float data); void marshallBoolean(struct tagHeader &th, bool data); -void marshallString(struct tagHeader &th, char *data, int maxSize = 0); +void marshallString(struct tagHeader &th, const char *data, int maxSize = 0); // last updated 22jan2001 {gdl} /* *********************************************************************** diff --git a/crawl-ref/source/transfor.cc b/crawl-ref/source/transfor.cc index 9ee38d548d..697bd4aef7 100644 --- a/crawl-ref/source/transfor.cc +++ b/crawl-ref/source/transfor.cc @@ -3,6 +3,8 @@ * Summary: Misc function related to player transformations. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 5/26/99 JDJ transform() and untransform() set you.wield_change so @@ -26,8 +28,6 @@ #include "skills2.h" #include "stuff.h" -extern unsigned char your_sign; // defined in view.cc -extern unsigned char your_colour; // defined in view.cc void drop_everything(void); void extra_hp(int amount_extra); @@ -89,6 +89,26 @@ static bool check_for_cursed_equipment( FixedVector < char, 8 > &remove_stuff ) return (false); } // end check_for_cursed_equipment() +// FIXME: Switch to 4.1 transforms handling. +size_type transform_size(int) +{ + const int transform = you.attribute[ATTR_TRANSFORMATION]; + switch (transform) + { + case TRAN_SPIDER: + return SIZE_TINY; + case TRAN_ICE_BEAST: + return SIZE_LARGE; + case TRAN_DRAGON: + case TRAN_SERPENT_OF_HELL: + return SIZE_HUGE; + case TRAN_AIR: + return SIZE_MEDIUM; + default: + return SIZE_CHARACTER; + } +} + bool transform(int pow, char which_trans) { if (you.species == SP_MERFOLK && player_is_swimming() @@ -163,8 +183,8 @@ bool transform(int pow, char which_trans) modify_stat( STAT_DEXTERITY, 5, true ); - your_sign = 's'; - your_colour = BROWN; + you.symbol = 's'; + you.colour = BROWN; return (true); case TRAN_ICE_BEAST: // also AC +3, cold +3, fire -1, pois +1 @@ -185,8 +205,8 @@ bool transform(int pow, char which_trans) if (you.duration[DUR_ICY_ARMOUR]) mpr( "Your new body merges with your icy armour." ); - your_sign = 'I'; - your_colour = WHITE; + you.symbol = 'I'; + you.colour = WHITE; return (true); case TRAN_BLADE_HANDS: @@ -237,8 +257,8 @@ bool transform(int pow, char which_trans) if (you.duration[DUR_STONEMAIL] || you.duration[DUR_STONESKIN]) mpr( "Your new body merges with your stone armour." ); - your_sign = '8'; - your_colour = LIGHTGREY; + you.symbol = '8'; + you.colour = LIGHTGREY; return (true); case TRAN_DRAGON: // also AC +10, ev -3, cold -1, fire +2, pois +1, flight @@ -258,8 +278,8 @@ bool transform(int pow, char which_trans) modify_stat( STAT_STRENGTH, 10, true ); extra_hp(16); // must occur after attribute set - your_sign = 'D'; - your_colour = GREEN; + you.symbol = 'D'; + you.colour = GREEN; return (true); case TRAN_LICH: @@ -293,8 +313,8 @@ bool transform(int pow, char which_trans) you.duration[ DUR_TRANSFORMATION ] = 100; modify_stat( STAT_STRENGTH, 3, true ); - your_sign = 'L'; - your_colour = LIGHTGREY; + you.symbol = 'L'; + you.colour = LIGHTGREY; you.is_undead = US_UNDEAD; you.hunger_state = HS_SATIATED; // no hunger effects while transformed set_redraw_status( REDRAW_HUNGER ); @@ -316,8 +336,8 @@ bool transform(int pow, char which_trans) you.duration[ DUR_TRANSFORMATION ] = 150; modify_stat( STAT_DEXTERITY, 8, true ); - your_sign = '#'; - your_colour = DARKGREY; + you.symbol = '#'; + you.colour = DARKGREY; return (true); case TRAN_SERPENT_OF_HELL: @@ -335,8 +355,8 @@ bool transform(int pow, char which_trans) modify_stat( STAT_STRENGTH, 13, true ); extra_hp(17); // must occur after attribute set - your_sign = 'S'; - your_colour = RED; + you.symbol = 'S'; + you.colour = RED; return (true); } @@ -354,8 +374,8 @@ void untransform(void) you.redraw_armour_class = 1; you.wield_change = true; - your_sign = '@'; - your_colour = LIGHTGREY; + you.symbol = '@'; + you.colour = LIGHTGREY; // must be unset first or else infinite loops might result -- bwr const int old_form = you.attribute[ ATTR_TRANSFORMATION ]; @@ -401,21 +421,8 @@ void untransform(void) mpr( "Your transformation has ended.", MSGCH_DURATION ); modify_stat(STAT_STRENGTH, -10, true); - if (!player_is_levitating() - && (grd[you.x_pos][you.y_pos] == DNGN_LAVA - || grd[you.x_pos][you.y_pos] == DNGN_DEEP_WATER - || grd[you.x_pos][you.y_pos] == DNGN_SHALLOW_WATER)) - { - if (you.species == SP_MERFOLK - && grd[you.x_pos][you.y_pos] != DNGN_LAVA) - { - mpr("You dive into the water and return to your normal form."); - merfolk_start_swimming(); - } - - if (grd[you.x_pos][you.y_pos] != DNGN_SHALLOW_WATER) - fall_into_a_pool( true, grd[you.x_pos][you.y_pos] ); - } + // re-check terrain now that be may no longer be flying. + move_player_to_grid( you.x_pos, you.y_pos, false, true, true ); break; case TRAN_LICH: @@ -440,7 +447,7 @@ void untransform(void) // probably need something better to cover all possibilities. -bwr if ((you.species == SP_NAGA || you.species == SP_CENTAUR) && you.equip[ EQ_BOOTS ] != -1 - && you.inv[ you.equip[EQ_BOOTS] ].plus2 != TBOOT_NAGA_BARDING) + && you.inv[ you.equip[EQ_BOOTS] ].sub_type != ARM_NAGA_BARDING) { rem_stuff[EQ_BOOTS] = 1; remove_equipment(rem_stuff); @@ -451,7 +458,7 @@ void untransform(void) // XXX: This whole system is a mess as it still relies on special // cases to handle a large number of things (see wear_armour()) -- bwr -bool can_equip( char use_which ) +bool can_equip( equipment_type use_which ) { // if more cases are added to this if must also change in @@ -519,6 +526,16 @@ bool can_equip( char use_which ) return (true); } // end can_equip() +// raw comparison of an item, must use check_armour_shape for full version +bool transform_can_equip_type( int eq_slot ) +{ + // FIXME FIXME FIXME + return (false); + + // const int form = you.attribute[ATTR_TRANSFORMATION]; + // return (!must_remove( Trans[form].rem_stuff, eq_slot )); +} + void extra_hp(int amount_extra) // must also set in calc_hp { calc_hp(); @@ -549,3 +566,16 @@ void drop_everything(void) return; } // end drop_everything() + +// Used to mark transformations which override species/mutation intrinsics. +// If phys_scales is true then we're checking to see if the form keeps +// the physical (AC/EV) properties from scales... the special intrinsic +// features (resistances, etc) are lost in those forms however. +bool transform_changed_physiology( bool phys_scales ) +{ + return (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE + && you.attribute[ATTR_TRANSFORMATION] != TRAN_BLADE_HANDS + && (!phys_scales + || (you.attribute[ATTR_TRANSFORMATION] != TRAN_LICH + && you.attribute[ATTR_TRANSFORMATION] != TRAN_STATUE))); +} diff --git a/crawl-ref/source/transfor.h b/crawl-ref/source/transfor.h index 5efa5a87b2..e292122559 100644 --- a/crawl-ref/source/transfor.h +++ b/crawl-ref/source/transfor.h @@ -3,6 +3,8 @@ * Summary: Misc function related to player transformations. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <1> -/--/-- LRH Created @@ -13,6 +15,7 @@ #define TRANSFOR_H #include "FixVec.h" +#include "enum.h" // last updated 12may2000 {dlb} @@ -26,8 +29,8 @@ void untransform(void); /* *********************************************************************** * called from: item_use * *********************************************************************** */ -bool can_equip(char use_which); - +bool can_equip(equipment_type use_which); +size_type transform_size(int psize = PSIZE_BODY); // last updated 12may2000 {dlb} /* *********************************************************************** @@ -42,5 +45,6 @@ bool transform(int pow, char which_trans); * *********************************************************************** */ bool remove_equipment( FixedVector& remove_stuff ); +bool transform_changed_physiology( bool phys_scales = false ); #endif diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc index f9c3d2d3d7..f5d418e8be 100644 --- a/crawl-ref/source/travel.cc +++ b/crawl-ref/source/travel.cc @@ -3,6 +3,8 @@ * Summary: Travel stuff * Written by: Darshan Shaligram * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Known issues: * Hardcoded dungeon features all over the place - this thing is a devil to * refactor. @@ -11,15 +13,20 @@ #include "files.h" #include "FixAry.h" #include "clua.h" +#include "delay.h" +#include "describe.h" +#include "misc.h" #include "mon-util.h" #include "player.h" #include "stash.h" #include "stuff.h" #include "travel.h" #include "view.h" -#include -#include -#include + +#include +#include +#include +#include #ifdef DOS #include @@ -71,14 +78,6 @@ inline int sgn(int x) return x < 0? -1 : (x > 0); } -/* These are defined in view.cc. BWR says these may become obsolete in next - * release? */ -extern unsigned char (*mapch) (unsigned char); -extern unsigned char (*mapch2) (unsigned char); -extern unsigned char mapchar(unsigned char ldfk); -extern unsigned char mapchar2(unsigned char ldfk); - - // Array of points on the map, each value being the distance the character // would have to travel to get there. Negative distances imply that the point // is a) a trap or hostile terrain or b) only reachable by crossing a trap or @@ -91,14 +90,6 @@ signed char curr_traps[GXM][GYM]; static FixedArray< unsigned short, GXM, GYM > mapshadow; -// Clockwise, around the compass from north (same order as enum RUN_DIR) -// Copied from acr.cc -static const struct coord_def Compass[8] = -{ - { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, - { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, -}; - #define TRAVERSABLE 1 #define IMPASSABLE 0 #define FORBIDDEN -1 @@ -106,12 +97,17 @@ static const struct coord_def Compass[8] = // Map of terrain types that are traversable. static signed char traversable_terrain[256]; -static int trans_negotiate_stairs(); +static command_type trans_negotiate_stairs(); static int find_transtravel_square(const level_pos &pos, bool verbose = true); static bool loadlev_populate_stair_distances(const level_pos &target); static void populate_stair_distances(const level_pos &target); +bool is_player_mapped(int grid_x, int grid_y) +{ + return (is_player_mapped( env.map[grid_x - 1][grid_y - 1] )); +} + // Determines whether the player has seen this square, given the user-visible // character. // @@ -119,13 +115,14 @@ static void populate_stair_distances(const level_pos &target); // a. The square is mapped (the env map char is not zero) // b. The square was *not* magic-mapped. // +// FIXME: There's better ways of doing this with the new view.cc. bool is_player_mapped(unsigned char envch) { // Note that we're relying here on mapch(DNGN_FLOOR) != mapch2(DNGN_FLOOR) // and that no *other* dungeon feature renders as mapch(DNGN_FLOOR). // The check for a ~ is to ensure explore stops for items turned up by // detect items. - return envch && envch != mapch(DNGN_FLOOR) && envch != '~'; + return envch && envch != get_magicmap_char(DNGN_FLOOR) && envch != '~'; } inline bool is_trap(unsigned char grid) @@ -150,12 +147,12 @@ inline int feature_traverse_cost(unsigned char feature) } // Returns true if the dungeon feature supplied is an altar. -inline bool is_altar(unsigned char grid) +bool is_altar(unsigned char grid) { - return grid >= DNGN_ALTAR_ZIN && grid <= DNGN_ALTAR_ELYVILON; + return grid_altar_god(grid) != GOD_NO_GOD; } -inline bool is_altar(const coord_def &c) +bool is_altar(const coord_def &c) { return is_altar(grd[c.x][c.y]); } @@ -194,13 +191,6 @@ static void init_traps() traps_inited = true; } -static const char *trap_names[] = -{ - "dart", "arrow", "spear", "axe", - "teleport", "amnesia", "blade", - "bolt", "zot", "needle", -}; - static const char *trap_name(int x, int y) { if (!traps_inited) @@ -211,7 +201,7 @@ static const char *trap_name(int x, int y) { int type = env.trap[ti].type; if (type >= 0 && type < NUM_TRAPS) - return (trap_names[type]); + return (trap_name(trap_type(type))); } return (""); } @@ -251,9 +241,9 @@ static bool is_exclude_root(int x, int y) const char *run_mode_name(int runmode) { - return runmode == RUN_TRAVEL? "travel" : - runmode == RUN_INTERLEVEL? "intertravel" : - runmode == RUN_EXPLORE? "explore" : + return runmode == RMODE_TRAVEL? "travel" : + runmode == RMODE_INTERLEVEL? "intertravel" : + runmode == RMODE_EXPLORE? "explore" : runmode > 0? "run" : ""; } @@ -387,7 +377,8 @@ static bool is_travel_ok(int x, int y, bool ignore_hostile) // code paths through the secret door because it looks at the actual grid, // rather than the env overmap. Hopefully there won't be any more such // cases. - if (envc == mapch2(DNGN_SECRET_DOOR)) return false; + // FIXME: is_terrain_changed ought to do this with the view.cc changes. + if (envc == get_sightmap_char(DNGN_SECRET_DOOR)) return false; unsigned char mon = mgrd[x][y]; if (mon != NON_MONSTER) @@ -400,7 +391,7 @@ static bool is_travel_ok(int x, int y, bool ignore_hostile) // the information we're giving the player for free. // Navigate around plants and fungi. Yet another tasty hack. if (player_monster_visible(&menv[mon]) && - mons_flag( menv[mon].type, M_NO_EXP_GAIN )) + mons_class_flag( menv[mon].type, M_NO_EXP_GAIN )) { extern short point_distance[GXM][GYM]; @@ -442,12 +433,12 @@ static bool is_safe(int x, int y) if (!player_monster_visible(&menv[mon]) || mons_is_mimic( menv[mon].type )) { - you.running = 0; + you.running.stop(); return true; } // Stop before wasting energy on plants and fungi. - if (mons_flag( menv[mon].type, M_NO_EXP_GAIN )) + if (mons_class_flag( menv[mon].type, M_NO_EXP_GAIN )) return false; // If this is any *other* monster, it'll be visible and @@ -506,9 +497,14 @@ static void init_terrain_check() void travel_init_new_level() { + // Clear run details, but preserve the runmode, because we might be in + // the middle of interlevel travel. + int runmode = you.running; + you.running.clear(); + you.running = runmode; + // Zero out last travel coords - you.run_x = you.run_y = 0; - you.travel_x = you.travel_y = 0; + you.travel_x = you.travel_y = 0; traps_inited = false; curr_excludes.clear(); @@ -525,7 +521,6 @@ void initialise_travel() traversable_terrain[DNGN_FLOOR] = traversable_terrain[DNGN_ENTER_HELL] = traversable_terrain[DNGN_OPEN_DOOR] = - traversable_terrain[DNGN_BRANCH_STAIRS] = traversable_terrain[DNGN_UNDISCOVERED_TRAP] = traversable_terrain[DNGN_ENTER_SHOP] = traversable_terrain[DNGN_ENTER_LABYRINTH] = @@ -628,6 +623,16 @@ void prevent_travel_to(const std::string &feature) traversable_terrain[feature_type] = FORBIDDEN; } +bool is_branch_stair(int gridx, int gridy) +{ + coord_def pos = { gridx, gridy }; + + const level_id curr = level_id::get_current_level_id(); + const level_id next = level_id::get_next_level_id(pos); + + return (next.branch != curr.branch); +} + bool is_stair(unsigned gridc) { return (is_travelable_stair(gridc) @@ -727,18 +732,27 @@ static void userdef_run_startrunning_hook(void) #endif } -/* - * Stops shift+running and all forms of travel. - */ -void stop_running(void) +bool is_resting( void ) { - userdef_run_stoprunning_hook(); - you.running = 0; + return you.running.is_rest(); } void start_running(void) { userdef_run_startrunning_hook(); + + if (you.running == RMODE_TRAVEL + || you.running == RMODE_EXPLORE + || you.running == RMODE_INTERLEVEL) + start_delay( DELAY_TRAVEL, 1 ); +} + +/* + * Stops shift+running and all forms of travel. + */ +void stop_running(void) +{ + you.running.stop(); } /* @@ -750,21 +764,23 @@ void start_running(void) * * Don't call travel() if you.running >= 0. */ -void travel(int *keyin, char *move_x, char *move_y) +command_type travel() { - *keyin = 128; + char holdx, holdy; + char *move_x = &holdx; + char *move_y = &holdy; + holdx = holdy = 0; + + command_type result = CMD_NO_CMD; // Abort travel/explore if you're confused or a key was pressed. if (kbhit() || you.conf) { stop_running(); - *keyin = 0; - if (Options.travel_delay == -1) - redraw_screen(); - return ; + return CMD_NO_CMD; } - if (Options.explore_stop && you.running == RUN_EXPLORE) + if (Options.explore_stop && you.running == RMODE_EXPLORE) { // Scan through the shadow map, compare it with the actual map, and if // there are any squares of the shadow map that have just been @@ -787,22 +803,22 @@ void travel(int *keyin, char *move_x, char *move_y) copy(env.map, mapshadow); } - if (you.running == RUN_EXPLORE) + if (you.running == RMODE_EXPLORE) { // Exploring - you.run_x = 0; + you.running.x = 0; find_travel_pos(you.x_pos, you.y_pos, NULL, NULL); // No place to go? - if (!you.run_x) + if (!you.running.x) stop_running(); } - if (you.running == RUN_INTERLEVEL && !you.run_x) + if (you.running == RMODE_INTERLEVEL && !you.running.x) { - // Interlevel travel. Since you.run_x is zero, we've either just + // Interlevel travel. Since you.running.x is zero, we've either just // initiated travel, or we've just climbed or descended a staircase, // and we need to figure out where to travel to next. - if (!find_transtravel_square(travel_target) || !you.run_x) + if (!find_transtravel_square(travel_target) || !you.running.x) stop_running(); } @@ -823,15 +839,15 @@ void travel(int *keyin, char *move_x, char *move_y) // should continue doing so (explore has its own end condition // upstairs); if we're traveling between levels and we've reached // our travel target, we're on a staircase and should take it. - if (you.x_pos == you.run_x && you.y_pos == you.run_y) + if (you.x_pos == you.running.x && you.y_pos == you.running.y) { - if (runmode == RUN_EXPLORE) - you.running = RUN_EXPLORE; // Turn explore back on + if (runmode == RMODE_EXPLORE) + you.running = RMODE_EXPLORE; // Turn explore back on // For interlevel travel, we'll want to take the stairs unless // the interlevel travel specified a destination square and // we've reached that destination square. - else if (runmode == RUN_INTERLEVEL + else if (runmode == RMODE_INTERLEVEL && (travel_target.pos.x != you.x_pos || travel_target.pos.y != you.y_pos || travel_target.id != @@ -847,10 +863,10 @@ void travel(int *keyin, char *move_x, char *move_y) // notify Lua hooks if you.running == 0. you.running = runmode; stop_running(); - return; + return CMD_NO_CMD; } - you.running = RUN_INTERLEVEL; - *keyin = trans_negotiate_stairs(); + you.running = RMODE_INTERLEVEL; + result = trans_negotiate_stairs(); // If, for some reason, we fail to use the stairs, we // need to make sure we don't go into an infinite loop @@ -860,11 +876,11 @@ void travel(int *keyin, char *move_x, char *move_y) // This is important, else we'll probably stop traveling // the moment we clear the stairs. That's because the - // (run_x, run_y) destination will no longer be valid on - // the new level. Setting run_x to zero forces us to - // recalculate our travel target next turn (see previous - // if block). - you.run_x = you.run_y = 0; + // (running.x, running.y) destination will no longer be + // valid on the new level. Setting running.x to zero forces + // us to recalculate our travel target next turn (see + // previous if block). + you.running.x = you.running.y = 0; } else { @@ -884,6 +900,51 @@ void travel(int *keyin, char *move_x, char *move_y) if (!you.running && Options.travel_delay == -1) redraw_screen(); + + if (!you.running) + return CMD_NO_CMD; + + if ( result != CMD_NO_CMD ) + return result; + + return direction_to_command( *move_x, *move_y ); +} + +command_type direction_to_command( char x, char y ) { + if ( x == -1 && y == -1 ) return CMD_MOVE_UP_LEFT; + if ( x == -1 && y == 0 ) return CMD_MOVE_LEFT; + if ( x == -1 && y == 1 ) return CMD_MOVE_DOWN_LEFT; + if ( x == 0 && y == -1 ) return CMD_MOVE_UP; + if ( x == 0 && y == 0 ) return CMD_NO_CMD; + if ( x == 0 && y == 1 ) return CMD_MOVE_DOWN; + if ( x == 1 && y == -1 ) return CMD_MOVE_UP_RIGHT; + if ( x == 1 && y == 0 ) return CMD_MOVE_RIGHT; + if ( x == 1 && y == 1 ) return CMD_MOVE_DOWN_RIGHT; + + ASSERT(0); + return CMD_NO_CMD; +} + +static void fill_exclude_radius(const coord_def &c) +{ + int radius = 0; + while (radius * radius < Options.travel_exclude_radius2) + radius++; + + for (int y = c.y - radius; y <= c.y + radius; ++y) + { + for (int x = c.x - radius; x <= c.x + radius; ++x) + { + if (!map_bounds(x, y) || !is_terrain_known(x, y) + || point_distance[x][y]) + continue; + + if (is_exclude_root(x, y)) + point_distance[x][y] = PD_EXCLUDED; + else if (is_excluded(x, y)) + point_distance[x][y] = PD_EXCLUDED_RADIUS; + } + } } /* @@ -896,7 +957,7 @@ void find_travel_pos(int youx, int youy, { init_terrain_check(); - int start_x = you.run_x, start_y = you.run_y; + int start_x = you.running.x, start_y = you.running.y; int dest_x = youx, dest_y = youy; bool floodout = false; unsigned char feature; @@ -922,14 +983,14 @@ void find_travel_pos(int youx, int youy, if (dest_x != -1 && !is_travel_ok(start_x, start_y, false) && !is_trap(start_x, start_y)) { - you.running = 0; + you.running = RMODE_NOT_RUNNING; return ; } // Abort run if we're going nowhere. if (start_x == dest_x && start_y == dest_y) { - you.running = 0; + you.running = RMODE_NOT_RUNNING; return ; } @@ -1009,13 +1070,13 @@ void find_travel_pos(int youx, int youy, unsigned char envf = env.map[dx - 1][dy - 1]; - if (floodout && you.running == RUN_EXPLORE + if (floodout && you.running == RMODE_EXPLORE && !is_player_mapped(envf)) { - // Setting run_x and run_y here is evil - this function - // should ideally not modify game state in any way. - you.run_x = x; - you.run_y = y; + // Setting running.x and running.y here is evil - this + // function should ideally not modify game state in any way. + you.running.x = x; + you.running.y = y; return; } @@ -1115,6 +1176,20 @@ void find_travel_pos(int youx, int youy, ignore_hostile = true; } } // for ( ; points > 0 ... + + if (features && floodout) + { + for (int i = 0, size = curr_excludes.size(); i < size; ++i) + { + const coord_def &exc = curr_excludes[i]; + // An exclude - wherever it is - is always a feature. + if (std::find(features->begin(), features->end(), exc) + == features->end()) + features->push_back(exc); + + fill_exclude_radius(exc); + } + } } // Mappings of which branches spring from which other branches, essential to @@ -1289,7 +1364,7 @@ static struct { { "Dungeon", "the Main Dungeon", 'D' }, { "Dis", "Dis", 'I' }, - { "Gehenna", "Gehenna", 'W' }, + { "Gehenna", "Gehenna", 'N' }, { "Hell", "Hell", 'U' }, { "Cocytus", "Cocytus", 'X' }, { "Tartarus", "Tartarus", 'Y' }, @@ -1459,14 +1534,14 @@ static std::vector get_known_branches() BRANCH_HALL_OF_ZOT }; - std::vector branches; + std::vector result; for (unsigned i = 0; i < sizeof list_order / sizeof(*list_order); ++i) { if (is_known_branch(list_order[i])) - branches.push_back(list_order[i]); + result.push_back(list_order[i]); } - return branches; + return result; } static int prompt_travel_branch() @@ -1580,7 +1655,7 @@ static int prompt_travel_depth(unsigned char branch) mesclr(); mpr(buf, MSGCH_PROMPT); - if (!cancelable_get_line( buf, sizeof buf )) + if (cancelable_get_line( buf, sizeof buf )) return 0; if (*buf) @@ -1737,56 +1812,27 @@ void start_translevel_travel(bool prompt_for_destination) if (travel_target.id.depth > -1) { - you.running = RUN_INTERLEVEL; - you.run_x = you.run_y = 0; + you.running = RMODE_INTERLEVEL; + you.running.x = you.running.y = 0; last_stair.depth = -1; start_running(); } } -int stair_direction(int stair) +command_type stair_direction(int stair) { return ((stair < DNGN_STONE_STAIRS_UP_I || stair > DNGN_ROCK_STAIRS_UP) && (stair < DNGN_RETURN_FROM_ORCISH_MINES || stair > DNGN_RETURN_FROM_SWAMP)) - ? '>' : '<'; + ? CMD_GO_DOWNSTAIRS : CMD_GO_UPSTAIRS; } -int trans_negotiate_stairs() +command_type trans_negotiate_stairs() { return stair_direction(grd[you.x_pos][you.y_pos]); } -int absdungeon_depth(unsigned char branch, int subdepth) -{ - int realdepth = subdepth - 1; - - if (branch >= BRANCH_ORCISH_MINES && branch <= BRANCH_SWAMP) - realdepth = subdepth + you.branch_stairs[branch - 10]; - - if (branch >= BRANCH_DIS && branch <= BRANCH_THE_PIT) - realdepth = subdepth + 26; - - return realdepth; -} - -int subdungeon_depth(unsigned char branch, int depth) -{ - int curr_subdungeon_level = depth + 1; - - // maybe last part better expresssed as <= PIT {dlb} - if (branch >= BRANCH_DIS && branch <= BRANCH_THE_PIT) - curr_subdungeon_level = depth - 26; - - /* Remember, must add this to the death_string in ouch */ - if (branch >= BRANCH_ORCISH_MINES && branch <= BRANCH_SWAMP) - curr_subdungeon_level = depth - - you.branch_stairs[branch - 10]; - - return curr_subdungeon_level; -} - static int target_distance_from(const coord_def &pos) { for (int i = 0, count = curr_stairs.size(); i < count; ++i) @@ -1910,8 +1956,8 @@ static int find_transtravel_stair( const level_id &cur, { si.distance = dist2stair; + // Account for the cost of taking the stairs dist2stair += Options.travel_stair_cost; - ++dist2stair; // Account for the cost of taking the stairs // Already too expensive? Short-circuit. if (local_distance != -1 && dist2stair >= local_distance) @@ -1935,7 +1981,8 @@ static int find_transtravel_stair( const level_id &cur, continue; } - if (dest.id.depth > -1) { // We have a valid level descriptor. + if (dest.id.depth > -1) // We have a valid level descriptor. + { int dist = level_distance(dest.id, target.id); if (dist != -1 && (dist < best_level_distance || @@ -2042,8 +2089,8 @@ static int find_transtravel_square(const level_pos &target, bool verbose) if (best_stair.x != -1 && best_stair.y != -1) { - you.run_x = best_stair.x; - you.run_y = best_stair.y; + you.running.x = best_stair.x; + you.running.y = best_stair.y; return 1; } else if (best_level_distance != -1 && closest_level != current @@ -2067,9 +2114,9 @@ void start_travel(int x, int y) if (x == you.x_pos && y == you.y_pos) return ; // Start running - you.running = RUN_TRAVEL; - you.run_x = x; - you.run_y = y; + you.running = RMODE_TRAVEL; + you.running.x = x; + you.running.y = y; // Remember where we're going so we can easily go back if interrupted. you.travel_x = x; @@ -2079,7 +2126,7 @@ void start_travel(int x, int y) find_travel_pos(you.x_pos, you.y_pos, NULL, NULL, NULL); if (point_distance[x][y] == 0 && - (x != you.x_pos || you.run_y != you.y_pos) && + (x != you.x_pos || you.running.y != you.y_pos) && is_travel_ok(x, y, false)) { // We'll need interlevel travel to get here. @@ -2087,8 +2134,8 @@ void start_travel(int x, int y) travel_target.pos.x = x; travel_target.pos.y = y; - you.running = RUN_INTERLEVEL; - you.run_x = you.run_y = 0; + you.running = RMODE_INTERLEVEL; + you.running.x = you.running.y = 0; last_stair.depth = -1; // We need the distance of the target from the various stairs around. @@ -2103,7 +2150,7 @@ void start_travel(int x, int y) void start_explore() { - you.running = RUN_EXPLORE; + you.running = RMODE_EXPLORE; if (Options.explore_stop) { // Clone shadow array off map @@ -2561,7 +2608,11 @@ void LevelInfo::get_stairs(std::vector &st) unsigned char grid = grd[x + 1][y + 1]; unsigned char envc = (unsigned char) env.map[x][y]; - if (envc && is_travelable_stair(grid)) + if ((x + 1 == you.x_pos && y + 1 == you.y_pos) + || (envc + && is_travelable_stair(grid) + && (is_terrain_seen(x + 1, y + 1) + || !is_branch_stair(x + 1, y + 1)))) { // Convert to grid coords, because that's what we use // everywhere else. @@ -2636,7 +2687,6 @@ void LevelInfo::save(FILE *file) const } } -#define EXCLUDE_LOAD_LIMIT 20 void LevelInfo::load(FILE *file) { stairs.clear(); @@ -2919,6 +2969,171 @@ void TravelCache::fixup_levels() bool can_travel_interlevel() { - return !(you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS - || you.level_type == LEVEL_PANDEMONIUM); + return (player_in_mappable_area() && you.level_type != LEVEL_PANDEMONIUM); +} + +///////////////////////////////////////////////////////////////////////////// +// Shift-running and resting. + +runrest::runrest() + : runmode(0), mp(0), hp(0), x(0), y(0) +{ +} + +// Initialize is only called for resting/shift-running. We should eventually +// include travel and wrap it all in. +void runrest::initialise(int dir, int mode) +{ + // Note HP and MP for reference. + hp = you.hp; + mp = you.magic_points; + + if (dir == RDIR_REST) + { + x = 0; + y = 0; + runmode = mode; + } + else + { + ASSERT( dir >= 0 && dir <= 7 ); + + x = Compass[dir].x; + y = Compass[dir].y; + runmode = mode; + + // Get the compass point to the left/right of intended travel: + const int left = (dir - 1 < 0) ? 7 : (dir - 1); + const int right = (dir + 1 > 7) ? 0 : (dir + 1); + + // Record the direction and starting tile type for later reference: + set_run_check( 0, left ); + set_run_check( 1, dir ); + set_run_check( 2, right ); + } + + if (runmode == RMODE_REST_DURATION) + start_delay(DELAY_REST, 1); + else + start_delay(DELAY_RUN, 1); +} + +runrest::operator int () const +{ + return (runmode); +} + +const runrest &runrest::operator = (int newrunmode) +{ + runmode = newrunmode; + return (*this); +} + +static char base_grid_type( char grid ) +{ + // Don't stop for undiscovered traps: + if (grid == DNGN_UNDISCOVERED_TRAP) + return (DNGN_FLOOR); + + // Or secret doors (which currently always look like rock walls): + if (grid == DNGN_SECRET_DOOR) + return (DNGN_ROCK_WALL); + + return (grid); +} + +void runrest::set_run_check(int index, int dir) +{ + run_check[index].dx = Compass[dir].x; + run_check[index].dy = Compass[dir].y; + + const int targ_x = you.x_pos + Compass[dir].x; + const int targ_y = you.y_pos + Compass[dir].y; + + run_check[index].grid = base_grid_type( grd[ targ_x ][ targ_y ] ); +} + +bool runrest::check_stop_running() +{ + if (runmode > 0 && runmode != RMODE_START && run_grids_changed()) + { + stop(); + return (true); + } + return (false); +} + +// This function creates "equivalence classes" so that undiscovered +// traps and secret doors aren't running stopping points. +bool runrest::run_grids_changed() const +{ + if (env.cgrid[you.x_pos + x][you.y_pos + y] != EMPTY_CLOUD) + return (true); + + if (mgrd[you.x_pos + x][you.y_pos + y] != NON_MONSTER) + return (true); + + for (int i = 0; i < 3; i++) + { + const int targ_x = you.x_pos + run_check[i].dx; + const int targ_y = you.y_pos + run_check[i].dy; + const int targ_grid = base_grid_type( grd[ targ_x ][ targ_y ] ); + + if (run_check[i].grid != targ_grid) + return (true); + } + + return (false); +} + +void runrest::stop() +{ + bool need_redraw = + runmode > 0 || (runmode < 0 && Options.travel_delay == -1); + userdef_run_stoprunning_hook(); + runmode = RMODE_NOT_RUNNING; + + // Kill the delay; this is fine because it's not possible to stack + // run/rest/travel on top of other delays. + stop_delay(); + + if (need_redraw) + viewwindow(true, false); +} + +bool runrest::is_rest() const +{ + return (runmode > 0 && !x && !y); +} + +void runrest::rundown() +{ + rest(); +} + +void runrest::rest() +{ + // stop_running() Lua hooks will never see rest stops. + if (runmode > 0) + --runmode; +} + +void runrest::clear() +{ + runmode = RMODE_NOT_RUNNING; + x = y = 0; + mp = hp = 0; +} + +void runrest::check_hp() +{ + if (is_rest() && you.hp == you.hp_max && you.hp > hp) + stop(); +} + +void runrest::check_mp() +{ + if (is_rest() && you.magic_points == you.max_magic_points + && you.magic_points > mp) + stop(); } diff --git a/crawl-ref/source/travel.h b/crawl-ref/source/travel.h index 30327bd46a..8c733b9af2 100644 --- a/crawl-ref/source/travel.h +++ b/crawl-ref/source/travel.h @@ -31,27 +31,14 @@ unsigned char is_waypoint(int x, int y); void update_excludes(); bool is_stair(unsigned gridc); bool is_travelable_stair(unsigned gridc); -int stair_direction(int stair_feat); +command_type stair_direction(int stair_feat); bool is_player_mapped(unsigned char envch); +command_type direction_to_command( char x, char y ); +bool is_resting( void ); +bool can_travel_interlevel(); -inline bool is_player_mapped(int grid_x, int grid_y) -{ - return (is_player_mapped( env.map[grid_x - 1][grid_y - 1] )); -} +bool is_player_mapped(int grid_x, int grid_y); -/* *********************************************************************** - * Returns the direction to take to move along the shortest path between - * (you_x, you_y) and (you.run_x, you.run_y) in (*move_x, *move_y). - * If move_x or move_y is NULL, the function explores the map outwards from - * (you_x, you_y), populating the coords vector with the coordinates - * of every dungeon feature it finds, features closest to the character - * (travel distance-wise) coming first in the vector. A 'feature' is defined - * as a trap, and any other non-floor, non-water/lava square that the character - * can step onto. - * - * *********************************************************************** - * called from: acr - view - * *********************************************************************** */ void find_travel_pos(int you_x, int you_y, char *move_x, char *move_y, std::vector* coords = NULL); @@ -72,16 +59,12 @@ void start_translevel_travel(bool prompt_for_destination = true); void start_travel(int x, int y); -void travel(int *keyin, char *move_x, char *move_y); +command_type travel(); int travel_direction(unsigned char branch, int subdungeondepth); void prevent_travel_to(const std::string &dungeon_feature_name); -int subdungeon_depth(unsigned char branch, int depth); - -int absdungeon_depth(unsigned char branch, int subdepth); - // Sort dungeon features as appropriate. void arrange_features(std::vector &features); @@ -109,14 +92,6 @@ void arrange_features(std::vector &features); * *********************************************************************** */ extern short point_distance[GXM][GYM]; -// Possible values of you.running -enum RUN_MODES -{ - RUN_TRAVEL = -1, // Classic or Plain Old travel - RUN_EXPLORE = -2, // Exploring (Ctrl+O) - RUN_INTERLEVEL = -3 // Interlevel travel (Ctrl+G) -}; - enum EXPLORE_STOP { ES_NONE = 0, @@ -129,6 +104,8 @@ enum EXPLORE_STOP //////////////////////////////////////////////////////////////////////////// // Structs for interlevel travel. +// Identifies a level. This has no meaning in the Abyss, labyrinths or +// Pandemonium. struct level_id { unsigned char branch; // The branch in which the level is. diff --git a/crawl-ref/source/unrand.h b/crawl-ref/source/unrand.h index ae38b07de8..30bfa5a1be 100644 --- a/crawl-ref/source/unrand.h +++ b/crawl-ref/source/unrand.h @@ -3,6 +3,8 @@ * Summary: Definitions for unrandom artifacts. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * <3> 7 Aug 2001 MV Added many new items * <2> 5/09/99 JDJ Cekugob no longer has fire and cold diff --git a/crawl-ref/source/util/levcomp.cc b/crawl-ref/source/util/levcomp.cc new file mode 100644 index 0000000000..5e0bb4cb64 --- /dev/null +++ b/crawl-ref/source/util/levcomp.cc @@ -0,0 +1,18 @@ +#include "AppHdr.h" +#include "levcomp.h" + +std::string lc_desfile; +map_def lc_map; +level_range lc_range; +level_range lc_default_depth; + +extern int yylineno; + +void reset_map_parser() +{ + lc_map.init(); + lc_range.reset(); + lc_default_depth.reset(); + + yylineno = 1; +} diff --git a/crawl-ref/source/util/levcomp.h b/crawl-ref/source/util/levcomp.h new file mode 100644 index 0000000000..99f0495f90 --- /dev/null +++ b/crawl-ref/source/util/levcomp.h @@ -0,0 +1,12 @@ +#include +#include +#include +#include "mapdef.h" +#include "maps.h" + +extern map_def lc_map; +extern level_range lc_range; +extern level_range lc_default_depth; +extern std::string lc_desfile; + +void reset_map_parser(); diff --git a/crawl-ref/source/util/levcomp.lpp b/crawl-ref/source/util/levcomp.lpp new file mode 100644 index 0000000000..69ad33ee23 --- /dev/null +++ b/crawl-ref/source/util/levcomp.lpp @@ -0,0 +1,155 @@ +%{ + +// levcomp.l: +// Level compiler lexer for Dungeon Crawl Stone Soup. +// +// Based loosely on NetHack's lev_comp.l + +#include "AppHdr.h" +#include "levcomp.tab.h" +#include + +static bool alloced = false; + +static void clean() +{ + if (yylval.text && alloced) + free( (void*) yylval.text); + yylval.text = NULL; + alloced = false; +} + +static void settext() +{ + clean(); + if ((yylval.text = strdup(yytext))) + alloced = true; +} + +%} + +%s MAPDEF +%s ARGUMENT +%s MNAME +%s KEYWORDS + +%option yylineno +%option never-interactive + +NSPACE [^\ \t\r\n] + +%% + +^\s*ENDMAP { BEGIN(INITIAL); } + +^#.*\r?\n ; + +[a-zA-Z_&0-9|$+.@^#()\[\]=<>{}%*\-?]* { + settext(); + return MAP_LINE; + } + +^\s*MAP { BEGIN(MAPDEF); } + +^\s*#.* ; + +NAME: { BEGIN(ARGUMENT); return NAME; } +default-depth: return DEFAULT_DEPTH; +DEPTH: return DEPTH; +ORIENT: return ORIENT; +PLACE: { BEGIN(ARGUMENT); return PLACE; } +CHANCE: return CHANCE; +FLAGS: return FLAGS; +TAGS: { BEGIN(KEYWORDS); return TAGS; } +SYMBOL: { BEGIN(ARGUMENT); return SYMBOL; } +MONS: { BEGIN(MNAME); return MONS; } + +BRANCHDEF: return BRANCH; +DEFAULT return DEFAULT; +DESC: return DESC; +BRANCH: return BRANCH; +ROOT_DEPTH: return ROOT_DEPTH; +FLOOR_COLOUR: return FLOOR_COLOUR; +ROCK_COLOUR: return ROCK_COLOUR; + +LEVEL return LEVEL; +END return END; +PVAULT: return PVAULT; +PMINIVAULT: return PMINIVAULT; + +ENTRY_MSG: { BEGIN(ARGUMENT); return ENTRY_MSG; } +EXIT_MSG: { BEGIN(ARGUMENT); return EXIT_MSG; } + +MONSTERS return MONSTERS; +ENDMONSTERS return ENDMONSTERS; + +[A-Za-z_0-9\-]+ { + settext(); + return STRING; + } + +[ \t]+ ; +[ \t]*\r?\n { BEGIN(INITIAL); } + +[^, \t\r\n][^,\r\n]+[^, \t\r\n] { + settext(); + return MONSTER_NAME; + } + +, return COMMA; +[ \t]*\r?\n { BEGIN(INITIAL); } +[ \t] ; + +pandemonic return PANDEMONIC; +no_hmirror return NO_HMIRROR; +no_vmirror return NO_VMIRROR; +no_rotate return NO_ROTATE; + +encompass return ENCOMPASS; +north return NORTH; +south return SOUTH; +east return EAST; +west return WEST; +northeast return NORTHEAST; +northwest return NORTHWEST; +southeast return SOUTHEAST; +southwest return SOUTHWEST; + +- return DASH; +, return COMMA; + +[0-9]+ { + clean(); + yylval.i = atoi(yytext); + return INTEGER; + } + +{NSPACE}.*{NSPACE} { + BEGIN(INITIAL); + settext(); + return STRING; + } + +\r?\n { BEGIN(INITIAL); } + +[\ \t\r\n]+ ; + +\( return OPAREN; +\) return CPAREN; + +\" return QUOTE; + +[a-zA-Z_][a-zA-Z_0-9]+ { + settext(); + return IDENTIFIER; + } + +. return CHARACTER; + +%% + +int yywrap() +{ + clean(); + return 1; +} diff --git a/crawl-ref/source/util/levcomp.ypp b/crawl-ref/source/util/levcomp.ypp new file mode 100644 index 0000000000..e419bdc878 --- /dev/null +++ b/crawl-ref/source/util/levcomp.ypp @@ -0,0 +1,226 @@ +%{ + +#include "AppHdr.h" +#include "libutil.h" +#include "levcomp.h" + +int yylex(); + +extern int yylineno; + +void yyerror(const char *e) +{ + fprintf(stderr, "%s:%d: %s\n", lc_desfile.c_str(), yylineno, e); + // Bail bail bail. + exit(1); +} + +%} + +%union +{ + int i; + const char *text; +} + +%token BRANCHDEF BRANCH DESC DEFAULT +%token DEFAULT_DEPTH SYMBOL TAGS +%token NAME DEPTH ORIENT PLACE CHANCE FLAGS MONS +%token ROOT_DEPTH ENTRY_MSG EXIT_MSG +%token ROCK_COLOUR FLOOR_COLOUR +%token ENCOMPASS +%token NORTH EAST SOUTH WEST +%token NORTHEAST SOUTHEAST SOUTHWEST NORTHWEST + +%token LEVEL END PVAULT PMINIVAULT MONSTERS ENDMONSTERS + +%token CHARACTER + +%token NO_HMIRROR NO_VMIRROR NO_ROTATE + +%token PANDEMONIC +%token DASH COMMA QUOTE OPAREN CPAREN +%token INTEGER + +%type orient_name flagname + +%token STRING MAP_LINE MONSTER_NAME +%token IDENTIFIER + +%% + +file : definitions { } + ; + +definitions : /* empty */ {} + | definition definitions {} + ; + +definition : def {} + | level {} + ; + +def : defdepth + ; + +defdepth : DEFAULT_DEPTH depth_range + { + lc_default_depth = lc_range; + } + ; + +level : name metalines map_def metalines + { + add_parsed_map( lc_map ); + } + ; + +name : NAME STRING + { + lc_map.init(); + lc_map.depth = lc_default_depth; + lc_map.name = $2; + } + ; + +metalines : /* no metadata */ + | metaline metalines + ; + +metaline : place + | depth + | chance + | orientation + | flags + | mons + | symbol + | tags + ; + +tags : TAGS tagstrings {} + ; + +tagstrings : /* empty */ + | STRING tagstrings + { + lc_map.tags += " "; + lc_map.tags += $1; + lc_map.tags += " "; + } + ; + +symbol : SYMBOL {} + | SYMBOL STRING + { + lc_map.random_symbols = $2; + } + ; + +mons : MONS {} + | MONS mnames {} + ; + +mnames : mname COMMA mnames + | mname + ; + +mname : MONSTER_NAME + { + bool recognised = lc_map.mons.add_mons($1); + if (!recognised) + { + char buf[300]; + snprintf(buf, sizeof buf, "unknown monster '%s'", + $1); + yyerror(buf); + } + } + ; + +place : PLACE STRING + { + lc_map.place = $2; + } + ; + +depth : DEPTH {} + | DEPTH depth_range + { + lc_map.depth = lc_range; + } + ; + +depth_range : INTEGER DASH INTEGER + { + lc_range.set($1, $3); + } + + | INTEGER + { + lc_range.set($1); + } + ; + +chance : CHANCE INTEGER + { + lc_map.chance = $2; + } + ; + +orientation : ORIENT {} + | ORIENT orient_name + { + lc_map.orient = (map_section_type) $2; + } + ; + +orient_name : ENCOMPASS { $$ = MAP_ENCOMPASS; } + | NORTH { $$ = MAP_NORTH; } + | EAST { $$ = MAP_EAST; } + | SOUTH { $$ = MAP_SOUTH; } + | WEST { $$ = MAP_WEST; } + | NORTHEAST { $$ = MAP_NORTHEAST; } + | SOUTHEAST { $$ = MAP_SOUTHEAST; } + | SOUTHWEST { $$ = MAP_SOUTHWEST; } + | NORTHWEST { $$ = MAP_NORTHWEST; } + ; + +flags : FLAGS flagnames {} + ; + +flagnames : /* empty */ + | flagname flagnames + { + switch ($1) { + case NO_HMIRROR: + lc_map.flags &= ~MAPF_MIRROR_HORIZONTAL; + break; + case NO_VMIRROR: + lc_map.flags &= ~MAPF_MIRROR_VERTICAL; + break; + case NO_ROTATE: + lc_map.flags &= ~MAPF_ROTATE; + break; + } + } + ; + +flagname : NO_HMIRROR { $$ = NO_HMIRROR; } + | NO_VMIRROR { $$ = NO_VMIRROR; } + | NO_ROTATE { $$ = NO_ROTATE; } + ; + +map_def : map_lines + ; + +map_lines : map_line + | map_line map_lines + ; + +map_line : MAP_LINE + { + lc_map.map.add_line($1); + } + ; + +%% diff --git a/crawl-ref/source/version.h b/crawl-ref/source/version.h index 583e965a6c..ce898d6603 100644 --- a/crawl-ref/source/version.h +++ b/crawl-ref/source/version.h @@ -3,6 +3,8 @@ * Summary: Contains version information * Written by: ?? * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 10/12/99 BCR Added BUILD_DATE #define @@ -33,13 +35,13 @@ #ifndef VERSION_H #define VERSION_H +#define CRAWL "Dungeon Crawl Stone Soup" // last updated 07august2001 {mv} /* *********************************************************************** * called from: chardump - command - newgame * *********************************************************************** */ -#define VERSION "4.0.0 beta 26 (crawl-ref)" - +#define VERSION "0.2-svn (crawl-ref)" // last updated 20feb2001 {GDL} /* *********************************************************************** @@ -47,5 +49,6 @@ * *********************************************************************** */ #define BUILD_DATE __DATE__ +#define SAVE_MAJOR_VERSION 0 #endif diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index adfb1a62bd..fdbe60d9f3 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -34,10 +34,16 @@ #include "externs.h" +#include "command.h" #include "clua.h" #include "debug.h" +#include "delay.h" +#include "direct.h" +#include "initfile.h" #include "insult.h" +#include "itemprop.h" #include "macro.h" +#include "misc.h" #include "monstuff.h" #include "mon-util.h" #include "overmap.h" @@ -48,37 +54,61 @@ #include "stash.h" #include "travel.h" +// These are hidden from the rest of the world... use the functions +// below to get information about the map grid. +#define MAP_MAGIC_MAPPED_FLAG 0x0100 +#define MAP_SEEN_FLAG 0x0200 +#define MAP_CHANGED_FLAG 0x0400 +#define MAP_DETECTED_MONSTER 0x0800 +#define MAP_DETECTED_ITEM 0x1000 + +#define MAP_CHARACTER_MASK 0x00ff + +struct feature_def +{ + unsigned short symbol; // symbol used for seen terrain + unsigned short magic_symbol; // symbol used for magic-mapped terrain + unsigned short colour; // normal in LoS colour + unsigned short map_colour; // colour when out of LoS on display + unsigned short seen_colour; // map_colour when is_terrain_seen() + bool notable; // gets noted when seen + bool seen_effect; // requires special handling when seen +}; + +struct feature_override +{ + dungeon_feature_type feat; + feature_def override; +}; + +static FixedVector< struct feature_def, NUM_FEATURES > Feature; +static std::vector Feature_Overrides; + #if defined(DOS_TERM) -typedef char screen_buffer_t; +// DOS functions like gettext() and puttext() can only +// work with arrays of characters, not shorts. +typedef unsigned char screen_buffer_t; #else typedef unsigned short screen_buffer_t; #endif -unsigned char your_sign; // accessed as extern in transfor.cc and acr.cc -unsigned char your_colour; // accessed as extern in transfor.cc and acr.cc - -FixedArray < unsigned int, 20, 19 > show_backup; +FixedArray < unsigned int, 20, 19 > Show_Backup; unsigned char show_green; extern int stealth; // defined in acr.cc -extern FixedVector Visible_Statue; // defined in acr.cc // char colour_code_map(unsigned char map_value); screen_buffer_t colour_code_map( int x, int y, bool item_colour = false, bool travel_colour = false ); -extern void (*viewwindow) (char, bool); -unsigned char (*mapch) (unsigned char); -unsigned char (*mapch2) (unsigned char); -unsigned char mapchar(unsigned char ldfk); -unsigned char mapchar2(unsigned char ldfk); -unsigned char mapchar3(unsigned char ldfk); -unsigned char mapchar4(unsigned char ldfk); void cloud_grid(void); void monster_grid(bool do_updates); -static int display_glyph(int env_glyph); -static int item_env_glyph(const item_def &item); +static int get_item_dngn_code(const item_def &item); +static void set_show_backup( int ex, int ey ); + +// Applies EC_ colour substitutions and brands. +static unsigned fix_colour(unsigned raw_colour); //--------------------------------------------------------------- // @@ -105,995 +135,391 @@ int get_number_of_lines(void) { #ifdef UNIX return (get_number_of_lines_from_curses()); -#elif MAC - return (MAC_NUMBER_OF_LINES); #else return (25); #endif } +int get_number_of_cols(void) +{ +#ifdef UNIX + return (get_number_of_cols_from_curses()); +#else + return (80); +#endif +} -//--------------------------------------------------------------- -// -// get_ibm_symbol -// -// Returns the DOS character code and color for everything drawn -// with the IBM graphics option. -// -//--------------------------------------------------------------- -static void get_ibm_symbol(unsigned int object, unsigned short *ch, - unsigned short *color) +unsigned get_envmap_char(int x, int y) { - ASSERT(color != NULL); - ASSERT(ch != NULL); + return static_cast( + env.map[x - 1][y - 1] & MAP_CHARACTER_MASK); +} - switch (object) - { - case DNGN_UNSEEN: - *ch = 0; - break; +void set_envmap_detected_item(int x, int y, bool detected) +{ + if (detected) + env.map[x - 1][y - 1] |= MAP_DETECTED_ITEM; + else + env.map[x - 1][y - 1] &= ~MAP_DETECTED_ITEM; +} - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - *color = env.rock_colour; - *ch = 177; - break; // remember earth elementals - - // stone in the realm of Zot is coloured the same as rock - case DNGN_STONE_WALL: - *color = (player_in_branch( BRANCH_HALL_OF_ZOT ) ? env.rock_colour - : LIGHTGREY); - *ch = 177; - break; +bool is_envmap_detected_item(int x, int y) +{ + return (env.map[x - 1][y - 1] & MAP_DETECTED_ITEM); +} - case DNGN_CLOSED_DOOR: - *ch = 254; - break; +void set_envmap_detected_mons(int x, int y, bool detected) +{ + if (detected) + env.map[x - 1][y - 1] |= MAP_DETECTED_MONSTER; + else + env.map[x - 1][y - 1] &= ~MAP_DETECTED_MONSTER; +} - case DNGN_METAL_WALL: - *ch = 177; - *color = CYAN; - break; +bool is_envmap_detected_mons(int x, int y) +{ + return (env.map[x - 1][y - 1] & MAP_DETECTED_MONSTER); +} - case DNGN_SECRET_DOOR: - *ch = 177; - *color = env.rock_colour; - break; +void set_envmap_char( int x, int y, unsigned char chr ) +{ + env.map[x - 1][y - 1] &= (~MAP_CHARACTER_MASK); // clear old first + env.map[x - 1][y - 1] |= chr; +} - case DNGN_GREEN_CRYSTAL_WALL: - *ch = 177; - *color = GREEN; - break; +bool is_terrain_known( int x, int y ) +{ + return (env.map[x - 1][y - 1] & (MAP_MAGIC_MAPPED_FLAG | MAP_SEEN_FLAG)); +} - case DNGN_ORCISH_IDOL: - *ch = '8'; - *color = DARKGREY; - break; +bool is_terrain_seen( int x, int y ) +{ + return (env.map[x - 1][y - 1] & MAP_SEEN_FLAG); +} - case DNGN_WAX_WALL: - *ch = 177; - *color = YELLOW; - break; // wax wall - /* Anything added here must also be added to the PLAIN_TERMINAL - viewwindow2 below */ - - case DNGN_SILVER_STATUE: - *ch = '8'; - *color = WHITE; - Visible_Statue[ STATUE_SILVER ] = 1; - break; +bool is_terrain_changed( int x, int y ) +{ + return (env.map[x - 1][y - 1] & MAP_CHANGED_FLAG); +} - case DNGN_GRANITE_STATUE: - *ch = '8'; - *color = LIGHTGREY; - break; +// used to mark dug out areas, unset when terrain is seen or mapped again. +void set_terrain_changed( int x, int y ) +{ + env.map[x - 1][y - 1] |= MAP_CHANGED_FLAG; +} - case DNGN_ORANGE_CRYSTAL_STATUE: - *ch = '8'; - *color = LIGHTRED; - Visible_Statue[ STATUE_ORANGE_CRYSTAL ] = 1; - break; +void set_terrain_mapped( int x, int y ) +{ + env.map[x - 1][y - 1] &= (~MAP_CHANGED_FLAG); + env.map[x - 1][y - 1] |= MAP_MAGIC_MAPPED_FLAG; +} - case DNGN_LAVA: - *ch = 247; - *color = RED; - break; +void set_terrain_seen( int x, int y ) +{ + env.map[x - 1][y - 1] &= (~MAP_CHANGED_FLAG); + env.map[x - 1][y - 1] |= MAP_SEEN_FLAG; +} - case DNGN_DEEP_WATER: - *ch = 247; // this wavy thing also used for water elemental - *color = BLUE; - break; +void clear_envmap_grid( int x, int y ) +{ + env.map[x - 1][y - 1] = 0; +} - case DNGN_SHALLOW_WATER: - *ch = 247; // this wavy thing also used for water elemental - *color = CYAN; - break; +void clear_envmap( void ) +{ + for (int i = 0; i < GXM; i++) + { + for (int j = 0; j < GYM; j++) + { + env.map[i][j] = 0; + } + } +} - case DNGN_FLOOR: - *color = env.floor_colour; - *ch = 249; - break; +#if defined(WIN32CONSOLE) || defined(DOS) || defined(DOS_TERM) +static unsigned colflag2brand(int colflag) +{ + switch (colflag) + { + case COLFLAG_ITEM_HEAP: + return (Options.heap_brand); + case COLFLAG_FRIENDLY_MONSTER: + return (Options.friend_brand); + case COLFLAG_WILLSTAB: + return (Options.stab_brand); + case COLFLAG_MAYSTAB: + return (Options.may_stab_brand); + default: + return (CHATTR_NORMAL); + } +} +#endif - case DNGN_ENTER_HELL: - *ch = 239; - *color = RED; - seen_other_thing(object); - break; +static unsigned fix_colour(unsigned raw_colour) +{ + // This order is important - is_element_colour() doesn't want to see the + // munged colours returned by dos_brand, so it should always be done + // before applying DOS brands. +#if defined(WIN32CONSOLE) || defined(DOS) || defined(DOS_TERM) + const int colflags = raw_colour & 0xFF00; +#endif - case DNGN_OPEN_DOOR: - *ch = 39; - break; + // Evaluate any elemental colours to guarantee vanilla colour is returned + if (is_element_colour( raw_colour )) + raw_colour = element_colour( raw_colour ); - case DNGN_BRANCH_STAIRS: - *ch = 240; - *color = BROWN; - break; +#if defined(WIN32CONSOLE) || defined(DOS) || defined(DOS_TERM) + if (colflags) + { + unsigned brand = colflag2brand(colflags); + raw_colour = dos_brand(raw_colour & 0xFF, brand); + } +#endif - case DNGN_TRAP_MECHANICAL: - *color = LIGHTCYAN; - *ch = 94; - break; +#ifndef USE_COLOUR_OPTS + // Strip COLFLAGs for systems that can't do anything meaningful with them. + raw_colour &= 0xFF; +#endif - case DNGN_TRAP_MAGICAL: - *color = MAGENTA; - *ch = 94; - break; + return (raw_colour); +} - case DNGN_TRAP_III: - *color = LIGHTGREY; - *ch = 94; - break; +static void get_symbol( unsigned int object, unsigned short *ch, + unsigned short *colour ) +{ + ASSERT( ch != NULL ); + ASSERT( colour != NULL ); - case DNGN_UNDISCOVERED_TRAP: - *ch = 249; - *color = env.floor_colour; - break; + if (object < NUM_FEATURES) + { + *ch = Feature[object].symbol; - case DNGN_ENTER_SHOP: - *ch = 239; - *color = YELLOW; + // Don't clobber with BLACK, because the colour should be already set. + if (Feature[object].colour != BLACK) + *colour = Feature[object].colour; - seen_other_thing(object); - break; - // if I change anything above here, must also change magic mapping! + // Note anything we see that's notable + if (Feature[object].notable) + seen_notable_thing( object ); + } + else + { + ASSERT( object >= DNGN_START_OF_MONSTERS ); + *ch = mons_char( object - DNGN_START_OF_MONSTERS ); + } - case DNGN_ENTER_LABYRINTH: - *ch = 239; - *color = LIGHTGREY; - seen_other_thing(object); - break; + *colour = fix_colour(*colour); +} - // not sure why we have "odd" here, but "ladders" are special in - // that they all lead to the first staircase of the next level - // (and returning from there will take you somewhere different) - // ... that's why they're brown... it's a warning -- bwr - case DNGN_ROCK_STAIRS_DOWN: - *color = BROWN; // ladder // odd {dlb} - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - *ch = '>'; - break; +unsigned char get_sightmap_char( int feature ) +{ + if (feature < NUM_FEATURES) + return (Feature[feature].symbol); - case DNGN_ROCK_STAIRS_UP: - *color = BROWN; // ladder // odd {dlb} - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - *ch = '<'; - break; + return (0); +} - case DNGN_ENTER_DIS: - *color = CYAN; - *ch = 239; - break; +unsigned char get_magicmap_char( int feature ) +{ + if (feature < NUM_FEATURES) + return (Feature[feature].magic_symbol); - case DNGN_ENTER_GEHENNA: - *color = RED; - *ch = 239; - break; + return (0); +} - case DNGN_ENTER_COCYTUS: - *color = LIGHTCYAN; - *ch = 239; - break; +static char get_travel_colour( int x, int y ) +{ + if (is_waypoint(x + 1, y + 1)) + return LIGHTGREEN; - case DNGN_ENTER_TARTARUS: - *color = DARKGREY; - *ch = 239; - break; + short dist = point_distance[x + 1][y + 1]; + return dist > 0? Options.tc_reachable : + dist == PD_EXCLUDED? Options.tc_excluded : + dist == PD_EXCLUDED_RADIUS? Options.tc_exclude_circle : + dist < 0? Options.tc_dangerous : + Options.tc_disconnected; +} + +#if defined(WIN32CONSOLE) || defined(DOS) +static unsigned short dos_reverse_brand(unsigned short colour) +{ + if (Options.dos_use_background_intensity) + { + // If the console treats the intensity bit on background colours + // correctly, we can do a very simple colour invert. - case DNGN_ENTER_ABYSS: - *color = random2(16); - *ch = 239; - seen_other_thing(object); - break; + // Special casery for shadows. + if (colour == BLACK) + colour = (DARKGREY << 4); + else + colour = (colour & 0xF) << 4; + } + else + { + // If we're on a console that takes its DOSness very seriously the + // background high-intensity bit is actually a blink bit. Blinking is + // evil, so we strip the background high-intensity bit. This, sadly, + // limits us to 7 background colours. + + // Strip off high-intensity bit. Special case DARKGREY, since it's the + // high-intensity counterpart of black, and we don't want black on + // black. + // + // We *could* set the foreground colour to WHITE if the background + // intensity bit is set, but I think we've carried the + // angry-fruit-salad theme far enough already. - case DNGN_EXIT_ABYSS: - *color = random2(16); - *ch = 239; - break; + if (colour == DARKGREY) + colour |= (LIGHTGREY << 4); + else if (colour == BLACK) + colour = LIGHTGREY << 4; + else + { + // Zap out any existing background colour, and the high + // intensity bit. + colour &= 7; - case DNGN_STONE_ARCH: - *color = LIGHTGREY; - *ch = 239; - break; + // And swap the foreground colour over to the background + // colour, leaving the foreground black. + colour <<= 4; + } + } - case DNGN_ENTER_PANDEMONIUM: - *color = LIGHTBLUE; - *ch = 239; - seen_other_thing(object); - break; + return (colour); +} - case DNGN_EXIT_PANDEMONIUM: - *color = LIGHTBLUE; - *ch = 239; - break; +static unsigned short dos_hilite_brand(unsigned short colour, + unsigned short hilite) +{ + if (!hilite) + return (colour); - case DNGN_TRANSIT_PANDEMONIUM: - *color = LIGHTGREEN; - *ch = 239; - break; + if (colour == hilite) + colour = 0; - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - *color = YELLOW; - *ch = '>'; - seen_staircase(object); - break; + colour |= (hilite << 4); + return (colour); +} - case DNGN_ENTER_ZOT: - *color = MAGENTA; - *ch = 239; - seen_staircase(object); - break; +unsigned short dos_brand( unsigned short colour, + unsigned brand) +{ + if ((brand & CHATTR_ATTRMASK) == CHATTR_NORMAL) + return (colour); - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - *color = YELLOW; - *ch = '<'; - break; + colour &= 0xFF; + + if ((brand & CHATTR_ATTRMASK) == CHATTR_HILITE) + return dos_hilite_brand(colour, (brand & CHATTR_COLMASK) >> 8); + else + return dos_reverse_brand(colour); + +} +#endif - case DNGN_RETURN_FROM_ZOT: - *color = MAGENTA; - *ch = 239; - break; +// FIXME: Rework this function to use the new terrain known/seen checks +// These are still env.map coordinates, NOT grid coordinates! +screen_buffer_t colour_code_map( int x, int y, bool item_colour, + bool travel_colour ) +{ + // XXX: Yes, the map array and the grid array are off by one. -- bwr + const int map_value = (unsigned char) env.map[x][y]; + const unsigned short map_flags = env.map[x][y]; + const int grid_value = grd[x + 1][y + 1]; - case DNGN_ALTAR_ZIN: - *color = WHITE; - *ch = 220; - seen_altar(GOD_ZIN); - break; + unsigned tc = travel_colour? + get_travel_colour(x, y) + : DARKGREY; - case DNGN_ALTAR_SHINING_ONE: - *color = YELLOW; - *ch = 220; - seen_altar(GOD_SHINING_ONE); - break; + if (map_flags & MAP_DETECTED_ITEM) + tc = Options.detected_item_colour; + + if (map_flags & MAP_DETECTED_MONSTER) + { + tc = Options.detected_monster_colour; + return fix_colour(tc); + } - case DNGN_ALTAR_KIKUBAAQUDGHA: - *color = DARKGREY; - *ch = 220; - seen_altar(GOD_KIKUBAAQUDGHA); - break; + // XXX: [ds] If we've an important colour, override other feature + // colouring. Yes, this is hacky. Story of my life. + if (tc == LIGHTGREEN || tc == LIGHTMAGENTA) + return fix_colour(tc); - case DNGN_ALTAR_YREDELEMNUL: - *color = ((one_chance_in(3)) ? RED : DARKGREY); - *ch = 220; - seen_altar(GOD_YREDELEMNUL); - break; + // XXX: Yeah, this is ugly, but until we have stored layers in the + // map we can't tell if we've seen a square, detected it, or just + // detected the item or monster on top... giving colour here will + // result in detect creature/item detecting features like stairs. -- bwr + if (map_value != get_sightmap_char(grid_value)) + { + // If there's an item on this square, change colour to indicate + // that, iff the item's glyph matches map_value. XXX: Potentially + // abusable? -- ds + int item = igrd[x + 1][y + 1]; + if (item_colour && item != NON_ITEM + && map_value == + get_sightmap_char(get_item_dngn_code(mitm[item]))) + { + unsigned ic = mitm[item].colour; - case DNGN_ALTAR_XOM: - *color = random_colour(); - *ch = 220; - seen_altar(GOD_XOM); - break; + if (mitm[item].link != NON_ITEM ) + ic |= COLFLAG_ITEM_HEAP; - case DNGN_ALTAR_VEHUMET: - *color = LIGHTBLUE; - if (one_chance_in(3)) - *color = LIGHTMAGENTA; - if (one_chance_in(3)) - *color = LIGHTRED; - *ch = 220; - seen_altar(GOD_VEHUMET); - break; + // If the item colour is the background colour, tweak it to WHITE + // instead to catch the player's eye. + return fix_colour( ic == tc? WHITE : ic ); + } + } - case DNGN_ALTAR_OKAWARU: - *color = CYAN; - *ch = 220; - seen_altar(GOD_OKAWARU); - break; + int feature_colour = DARKGREY; + feature_colour = + is_terrain_seen(x + 1, y + 1)? Feature[grid_value].seen_colour + : Feature[grid_value].map_colour; - case DNGN_ALTAR_MAKHLEB: - *color = RED; - if (one_chance_in(3)) - *color = LIGHTRED; - if (one_chance_in(3)) - *color = YELLOW; - *ch = 220; - seen_altar(GOD_MAKHLEB); - break; + if (feature_colour != DARKGREY) + tc = feature_colour; - case DNGN_ALTAR_SIF_MUNA: - *color = BLUE; - *ch = 220; - seen_altar(GOD_SIF_MUNA); - break; + return fix_colour(tc); +} - case DNGN_ALTAR_TROG: - *color = RED; - *ch = 220; - seen_altar(GOD_TROG); - break; +void clear_map() +{ + for (int y = Y_BOUND_1; y <= Y_BOUND_2; ++y) + { + for (int x = X_BOUND_1; x <= X_BOUND_2; ++x) + { + // Don't expose new dug out areas: + // Note: assumptions are being made here about how + // terrain can change (eg it used to be solid, and + // thus monster/item free). + if (is_terrain_changed(x, y)) + continue; - case DNGN_ALTAR_NEMELEX_XOBEH: - *color = LIGHTMAGENTA; - *ch = 220; - seen_altar(GOD_NEMELEX_XOBEH); - break; + unsigned short envc = env.map[x][y] & MAP_CHARACTER_MASK; + if (!envc) + continue; - case DNGN_ALTAR_ELYVILON: - *color = LIGHTGREY; - *ch = 220; - seen_altar(GOD_ELYVILON); - break; + const int item = igrd[x + 1][y + 1]; + if (item != NON_ITEM + && envc == + get_sightmap_char(get_item_dngn_code(mitm[item]))) + continue; - case DNGN_BLUE_FOUNTAIN: - *color = BLUE; - *ch = 159; - break; + set_envmap_char(x, y, + is_terrain_seen(x, y)? get_sightmap_char(grd[x][y]) : + is_terrain_known(x, y)? get_magicmap_char(grd[x][y]) : + 0); + set_envmap_detected_mons(x, y, false); + } + } +} - case DNGN_SPARKLING_FOUNTAIN: - *color = LIGHTBLUE; - *ch = 159; - break; +void monster_grid(bool do_updates) +{ + struct monsters *monster = 0; // NULL {dlb} - case DNGN_DRY_FOUNTAIN_I: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_PERMADRY_FOUNTAIN: - *color = LIGHTGREY; - *ch = 159; - break; - - case 256: - *ch = '0'; - break; - - case 257: - *color = CYAN; - *ch = '~'; - break; /* Invis creature walking through water */ - - case 258: - *ch = ')'; - break; // weapon ) - - case 259: - *ch = '['; - break; // armour [ - - case 260: - *ch = '/'; - break; // wands, etc. - - case 261: - *ch = '%'; - break; // food - - case 262: - *ch = '+'; - break; // books + - - case 263: - *ch = '?'; - break; // scroll ? - - case 264: - *ch = '='; - break; // ring = etc - - case 265: - *ch = '!'; - break; // potions ! - - case 266: - *ch = '('; - break; // stones - - case 267: - *ch = '+'; - break; // book + - - case 268: - *ch = '%'; - break; // corpses part 1 - - case 269: - *ch = '\\'; - break; // magical staves - - case 270: - *ch = '}'; - break; // gems - - case 271: - *ch = '%'; - break; // don't know ? - - case 272: - *ch = '$'; - *color = YELLOW; - break; // $ gold - - case 273: - *ch = '"'; - break; // amulet - - default: - *ch = ((object >= 297) ? mons_char(object - 297) : object); - break; - } -} // end get_ibm_symbol() - -//--------------------------------------------------------------- -// -// viewwindow2 -// -// Draws the main window using the extended IBM character set. -// -// This function should not interfer with the game condition, -// unless do_updates is set (ie. stealth checks for visible -// monsters). -// -//--------------------------------------------------------------- -void viewwindow2(char draw_it, bool do_updates) -{ - const long BUFFER_SIZE = 1550; -#ifdef DOS_TERM - // DOS functions like gettext() and puttext() can only - // work with arrays of characters, not shorts. - FixedVector < unsigned char, BUFFER_SIZE > buffy; //[800]; //392]; -#else - FixedVector < unsigned short, BUFFER_SIZE > buffy; //[800]; //392]; -#endif - - unsigned short ch, color; - - losight(env.show, grd, you.x_pos, you.y_pos); - - int count_x, count_y; - - for (count_x = 0; count_x < 18; count_x++) - { - for (count_y = 0; count_y < 18; count_y++) - { - env.show_col[count_x][count_y] = LIGHTGREY; - show_backup[count_x][count_y] = 0; - } - } - - item(); - cloud_grid(); - monster_grid(do_updates); - int bufcount = 0; - - if (draw_it == 1) - { - _setcursortype(_NOCURSOR); - for (count_y = you.y_pos - 8; count_y < you.y_pos + 9; count_y++) - { - bufcount += 16; - - for (count_x = you.x_pos - 8; count_x < you.x_pos + 9; count_x++) - { - // may be overriden by the code below - color = env.show_col[ count_x - you.x_pos + 9 ] - [ count_y - you.y_pos + 9 ]; - - unsigned int object = env.show[ count_x - you.x_pos + 9 ] - [ count_y - you.y_pos + 9 ]; - - get_ibm_symbol(object, &ch, &color); - - if (count_x == you.x_pos && count_y == you.y_pos) - { - ch = your_sign; - - if (player_is_swimming()) - { - color = (grd[you.x_pos][you.y_pos] == DNGN_DEEP_WATER) - ? BLUE : CYAN; - } - else - { - color = your_colour; - } - } - - ASSERT(bufcount + 1 < BUFFER_SIZE); - buffy[bufcount] = ch; - buffy[bufcount + 1] = color; - - bufcount += 2; - } - - bufcount += 16; - } - - if (you.level_type != LEVEL_LABYRINTH && you.level_type != LEVEL_ABYSS) - { - bufcount = 0; - - for (count_y = 0; count_y < 17; count_y++) - { - bufcount += 16; - - for (count_x = 0; count_x < 17; count_x++) - { - ASSERT(bufcount < BUFFER_SIZE); - - int mapx = count_x + you.x_pos - 9; - int mapy = count_y + you.y_pos - 9; - if (buffy[bufcount] != 0 && mapx >= 0 && mapx + 1 < GXM - && mapy >= 0 && mapy + 1 < GYM) - { - unsigned short bch = buffy[bufcount]; - if (mgrd[mapx + 1][mapy + 1] != NON_MONSTER) { - const monsters &m = menv[mgrd[mapx + 1][mapy + 1]]; - if (!mons_is_mimic(m.type) - && mons_char(m.type) == bch) - { - bch |= mons_colour(m.type) << 12; - } - } - env.map[mapx][mapy] = bch; - } - - if (Options.clean_map == 1 - && show_backup[count_x + 1][count_y + 1] != 0) - { - get_ibm_symbol(show_backup[count_x + 1][count_y + 1], - &ch, &color); - if (mapx >= 0 && mapx < GXM - && mapy >= 0 && mapy < GYM) - env.map[ count_x + you.x_pos - 9 ] - [ count_y + you.y_pos - 9 ] = ch; - } - bufcount += 2; - } - bufcount += 16; - } - } - - bufcount = 0; - for (count_y = 0; count_y < 17; count_y++) - { - for (count_x = 0; count_x < 33; count_x++) - { - if (count_x + you.x_pos - 17 < 3 - || count_y + you.y_pos - 9 < 3 - || count_x + you.x_pos - 14 > 77 - || count_y + you.y_pos - 9 > 67) - { - ASSERT(bufcount < BUFFER_SIZE); - buffy[bufcount] = 0; - bufcount++; - buffy[bufcount] = 0; - bufcount++; - continue; - } - - if (count_x >= 8 && count_x <= 24 && count_y >= 0 - && count_y <= 16 && buffy[bufcount] != 0) - { - bufcount += 2; - continue; - } - - ASSERT(bufcount + 1 < BUFFER_SIZE); - - buffy[bufcount] = (unsigned char) - env.map[ count_x + you.x_pos - 17 ] - [ count_y + you.y_pos - 9 ]; - - buffy[bufcount + 1] = DARKGREY; - if (Options.colour_map) - { - if (env.map[ count_x + you.x_pos - 17 ] - [ count_y + you.y_pos - 9 ] != 0) - { - buffy[bufcount + 1] - = colour_code_map( count_x + you.x_pos - 17, - count_y + you.y_pos - 9, - Options.item_colour ); - } - } - bufcount += 2; - } - } - - if (you.berserker) - { - for (count_x = 1; count_x < 1400; count_x += 2) - { - if (buffy[count_x] != DARKGREY) - buffy[count_x] = RED; - } - } - - if (show_green != BLACK) - { - for (count_x = 1; count_x < 1400; count_x += 2) - { - if (buffy[count_x] != DARKGREY) - buffy[count_x] = show_green; - } - - show_green = BLACK; - - if (you.special_wield == SPWLD_SHADOW) - show_green = DARKGREY; - } - -#ifdef DOS_TERM - puttext(2, 1, 34, 17, buffy.buffer()); -#endif - -#ifdef PLAIN_TERM - gotoxy(2, 1); - bufcount = 0; - - // following lines are purely optional. - // if used, players will 'jump' move. - // Resting will be a LOT faster too. - if (you.running == 0 || (you.running < 0 && Options.travel_delay > -1)) - { - for (count_x = 0; count_x < 1120; count_x += 2) - { // 1056 - ch = buffy[count_x]; - color = buffy[count_x + 1]; -// ASSERT(color < 16); - ASSERT(ch < 255); - - textcolor(color); - putch(ch); - - if (count_x % 66 == 64 && count_x > 0) - gotoxy(2, wherey() + 1); - } - // remember to comment out the line below if you comment out jump move. - } - _setcursortype(_NORMALCURSOR); -#endif - } -} // end viewwindow2() - -static char get_travel_colour( int x, int y ) -{ - if (is_waypoint(x + 1, y + 1)) - return LIGHTGREEN; - - short dist = point_distance[x + 1] - [y + 1]; - return dist > 0 ? BLUE : - dist == PD_EXCLUDED ? LIGHTMAGENTA : - dist == PD_EXCLUDED_RADIUS ? RED : - dist < 0 ? CYAN : - DARKGREY; -} - -#if defined(WIN32CONSOLE) || defined(DOS) -static unsigned short dos_reverse_brand(unsigned short colour) -{ - if (Options.dos_use_background_intensity) - { - // If the console treats the intensity bit on background colours - // correctly, we can do a very simple colour invert. - - // Special casery for shadows. Note this must be matched by the fix - // to libw32c.cc (the unpatched libw32c.cc does not draw spaces of any - // colour). - if (colour == BLACK) - colour = (DARKGREY << 4); - else - colour = (colour & 0xF) << 4; - } - else - { - // If we're on a console that takes its DOSness very seriously the - // background high-intensity bit is actually a blink bit. Blinking is - // evil, so we strip the background high-intensity bit. This, sadly, - // limits us to 7 background colours. - - // Strip off high-intensity bit. Special case DARKGREY, since it's the - // high-intensity counterpart of black, and we don't want black on - // black. - // - // We *could* set the foreground colour to WHITE if the background - // intensity bit is set, but I think we've carried the - // angry-fruit-salad theme far enough already. - - if (colour == DARKGREY) - colour |= (LIGHTGREY << 4); - else if (colour == BLACK) - colour = LIGHTGREY << 4; - else - { - // Zap out any existing background colour, and the high - // intensity bit. - colour &= 7; - - // And swap the foreground colour over to the background - // colour, leaving the foreground black. - colour <<= 4; - } - } - - return (colour); -} - -static unsigned short dos_hilite_brand(unsigned short colour, - unsigned short hilite) -{ - if (!hilite) - return (colour); - - if (colour == hilite) - colour = 0; - - colour |= (hilite << 4); - return (colour); -} - -static unsigned short dos_brand( unsigned short colour, - unsigned brand = CHATTR_REVERSE ) -{ - if ((brand & CHATTR_ATTRMASK) == CHATTR_NORMAL) - return (colour); - - colour &= 0xFF; - - if ((brand & CHATTR_ATTRMASK) == CHATTR_HILITE) - return dos_hilite_brand(colour, (brand & CHATTR_COLMASK) >> 8); - else - return dos_reverse_brand(colour); - -} -#endif - -screen_buffer_t colour_code_map( int x, int y, bool item_colour, - bool travel_colour ) -{ - // XXX: Yes, the map array and the grid array are off by one. -- bwr - const int map_value = (unsigned char) env.map[x][y]; - const unsigned short map_flags = env.map[x][y] & ENVF_FLAGS; - const int grid_value = grd[x + 1][y + 1]; - - char tc = travel_colour? get_travel_colour(x, y) : DARKGREY; - - if (map_flags & ENVF_DETECT_ITEM) - tc = Options.detected_item_colour; - - if (map_flags & ENVF_DETECT_MONS) { - tc = Options.detected_monster_colour; - return (tc); - } - - unsigned char ecol = ENVF_COLOR(map_flags); - if (ecol) { - unsigned rmc = Options.remembered_monster_colour & 0xFFFF; - if (rmc == 0xFFFF) // Use real colour - tc = ecol; - else if (rmc == 0) // Don't colour - ; - else - tc = rmc; - } - - // XXX: [ds] If we've an important colour, override other feature - // colouring. Yes, this is hacky. Story of my life. - if (tc == LIGHTGREEN || tc == LIGHTMAGENTA) - return tc; - - // XXX: Yeah, this is ugly, but until we have stored layers in the - // map we can't tell if we've seen a square, detected it, or just - // detected the item or monster on top... giving colour here will - // result in detect creature/item detecting features like stairs. -- bwr - if (map_value != mapch2( grid_value )) { - // If there's an item on this square, change colour to indicate - // that, iff the item's glyph matches map_value. XXX: Potentially - // abusable? -- ds - int item = igrd[x + 1][y + 1]; - if (item_colour && item != NON_ITEM - && map_value == display_glyph(item_env_glyph(mitm[item]))) - { - screen_buffer_t ic = mitm[item].colour; - -#if defined(WIN32CONSOLE) || defined(DOS) || defined(DOS_TERM) - if (mitm[item].link != NON_ITEM - && Options.heap_brand != CHATTR_NORMAL) - { - ic = dos_brand(ic, Options.heap_brand); - } -#elif defined(USE_COLOUR_OPTS) - if (mitm[item].link != NON_ITEM ) - { - ic |= COLFLAG_ITEM_HEAP; - } -#endif - // If the item colour is the background colour, tweak it to WHITE - // instead to catch the player's eye. - return ic == tc? WHITE : ic; - } - - return tc; - } - - switch (grid_value) - { - case DNGN_TRAP_MECHANICAL: - return (LIGHTCYAN); - - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - return (MAGENTA); - - case DNGN_ENTER_SHOP: - return (YELLOW); - - case DNGN_ENTER_DIS: - return (CYAN); - - case DNGN_ENTER_HELL: - case DNGN_ENTER_GEHENNA: - return (RED); - - case DNGN_ENTER_COCYTUS: - return (LIGHTCYAN); - - case DNGN_ENTER_ABYSS: - return random2(16); // so it can be black - is this right? {dlb} - - case DNGN_ENTER_LABYRINTH: - case DNGN_STONE_ARCH: - return (LIGHTGREY); - - case DNGN_ENTER_PANDEMONIUM: - return (LIGHTBLUE); - - case DNGN_EXIT_PANDEMONIUM: - // Exit pandemonium gates won't show up on the map as light blue - // unless the character has the "gate to pandemonium" demonspawn - // mutation. This is so that the player can't quickly use a - // crystal ball to find their way out. -- bwr - return (you.mutation[MUT_PANDEMONIUM] ? LIGHTBLUE : LIGHTGREEN); - - case DNGN_TRANSIT_PANDEMONIUM: - return (LIGHTGREEN); - - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - return (MAGENTA); - - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - return (RED); - - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - return (GREEN); - - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - return (LIGHTRED); - - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - return (LIGHTBLUE); - - default: - break; - } - - return tc; -} - -void clear_map() -{ - for (int y = 0; y < GYM - 1; ++y) - { - for (int x = 0; x < GXM - 1; ++x) - { - unsigned short envc = env.map[x][y]; - if (!envc) - continue; - - bool unmapped = (envc & ENVF_DETECTED) != 0; - // Discard flags at this point. - envc = (unsigned char) envc; - - const unsigned char &grdc = grd[x + 1][y + 1]; - if (envc == mapch(grdc) || envc == mapch2(grdc)) - continue; - - int item = igrd[x + 1][y + 1]; - if (item != NON_ITEM - && envc == display_glyph(item_env_glyph(mitm[item]))) - continue; - - env.map[x][y] = unmapped? 0 : mapch2(grdc); - } - } -} - -void monster_grid(bool do_updates) -{ - struct monsters *monster = 0; // NULL {dlb} - - for (int s = 0; s < MAX_MONSTERS; s++) - { - monster = &menv[s]; + for (int s = 0; s < MAX_MONSTERS; s++) + { + monster = &menv[s]; if (monster->type != -1 && mons_near(monster)) { @@ -1104,7 +530,7 @@ void monster_grid(bool do_updates) { behaviour_event( monster, ME_ALERT, MHITYOU ); - if (you.turn_is_over == 1 + if (you.turn_is_over && mons_shouts(monster->type) > 0 && random2(30) >= you.skills[SK_STEALTH]) { @@ -1194,127 +620,50 @@ void monster_grid(bool do_updates) } } + const int ex = monster->x - you.x_pos + 9; + const int ey = monster->y - you.y_pos + 9; + if (!player_monster_visible( monster )) { // ripple effect? if (grd[monster->x][monster->y] == DNGN_SHALLOW_WATER && !mons_flies(monster)) { - show_backup[ monster->x - you.x_pos + 9 ] - [ monster->y - you.y_pos + 9] - = env.show[ monster->x - you.x_pos + 9 ] - [ monster->y - you.y_pos + 9 ]; - env.show[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] = 257; + set_show_backup(ex, ey); + env.show[ex][ey] = DNGN_INVIS_EXPOSED; + env.show_col[ex][ey] = BLUE; } continue; } else if (!mons_friendly( monster ) && !mons_is_mimic( monster->type ) - && !mons_flag( monster->type, M_NO_EXP_GAIN )) + && !mons_class_flag( monster->type, M_NO_EXP_GAIN )) { - interrupt_activity( AI_SEE_MONSTER ); - if (you.running != 0 -#ifdef CLUA_BINDINGS - && clua.callbooleanfn(true, "ch_stop_run", - "M", monster) -#endif - ) - { - // Friendly monsters, mimics, or harmless monsters - // don't disturb the player's running/resting. - // - // Doing it this way causes players in run mode 2 - // to move one square, and in mode 1 to stop. This - // means that the character will run one square if - // a monster is in sight... we automatically jump - // to zero if we're resting. -- bwr - if (you.run_x == 0 && you.run_y == 0) - stop_running(); - else if (you.running > 1) - you.running--; - else - stop_running(); - } + interrupt_activity( AI_SEE_MONSTER, monster ); + seen_monster( monster ); } // mimics are always left on map if (!mons_is_mimic( monster->type )) - { - show_backup[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - = env.show[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9]; - } + set_show_backup(ex, ey); - env.show[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] = monster->type + 297; + env.show[ex][ey] = monster->type + DNGN_START_OF_MONSTERS; + env.show_col[ex][ey] = monster->colour; - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - = ((mcolour[monster->type] == BLACK) - ? monster->number : mcolour[monster->type]); -#ifdef USE_COLOUR_OPTS if (mons_friendly(monster)) { - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - |= COLFLAG_FRIENDLY_MONSTER; + env.show_col[ex][ey] |= COLFLAG_FRIENDLY_MONSTER; } else if (Options.stab_brand != CHATTR_NORMAL - && !mons_is_mimic(monster->type) - && monster->type != MONS_OKLOB_PLANT - && mons_is_stabbable(monster)) + && mons_looks_stabbable(monster)) { - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - |= COLFLAG_WILLSTAB; + env.show_col[ex][ey] |= COLFLAG_WILLSTAB; } else if (Options.may_stab_brand != CHATTR_NORMAL - && !mons_is_mimic(monster->type) - && monster->type != MONS_OKLOB_PLANT - && mons_maybe_stabbable(monster)) - { - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9] - |= COLFLAG_MAYSTAB; - } - -#elif defined(WIN32CONSOLE) || defined(DOS) - if (Options.friend_brand != CHATTR_NORMAL - && mons_friendly(monster)) + && mons_looks_distracted(monster)) { - // We munge the colours right here for DOS and Windows, because - // we know exactly how the colours will be handled, and we don't - // want to change both DOS and Windows port code to handle - // friend branding. - unsigned short &colour = - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9]; - colour = dos_brand(colour, Options.friend_brand); + env.show_col[ex][ey] |= COLFLAG_MAYSTAB; } - - if (Options.stab_brand != CHATTR_NORMAL - && !mons_is_mimic(monster->type) - && monster->type != MONS_OKLOB_PLANT - && mons_is_stabbable(monster)) - { - unsigned short &colour = - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9]; - colour = dos_brand(colour, Options.stab_brand); - } - else if (Options.may_stab_brand != CHATTR_NORMAL - && !mons_is_mimic(monster->type) - && monster->type != MONS_OKLOB_PLANT - && mons_maybe_stabbable(monster)) - { - unsigned short &colour = - env.show_col[monster->x - you.x_pos + 9] - [monster->y - you.y_pos + 9]; - colour = dos_brand(colour, Options.may_stab_brand); - } -#endif } // end "if (monster->type != -1 && mons_ner)" } // end "for s" } // end monster_grid() @@ -1324,7 +673,7 @@ bool check_awaken(int mons_aw) { int mons_perc = 0; struct monsters *monster = &menv[mons_aw]; - const int mon_holy = mons_holiness( monster->type ); + const int mon_holy = mons_holiness(monster); // berserkers aren't really concerned about stealth if (you.berserker) @@ -1344,9 +693,9 @@ bool check_awaken(int mons_aw) + mons_see_invis(monster) * 5; // critters that are wandering still have MHITYOU as their foe are - // still actively on guard for the player, even if they can't see + // still actively on guard for the player, even if they can't see // him. Give them a large bonus (handle_behaviour() will nuke 'foe' - // after a while, removing this bonus. + // after a while, removing this bonus. if (monster->behaviour == BEH_WANDER && monster->foe == MHITYOU) mons_perc += 15; @@ -1380,58 +729,52 @@ bool check_awaken(int mons_aw) return (random2(stealth) <= mons_perc); } // end check_awaken() -static int display_glyph(int env_glyph) +static void set_show_backup( int ex, int ey ) { - unsigned short ch, color; - if (viewwindow == viewwindow2) - get_ibm_symbol(env_glyph, &ch, &color); - else - get_non_ibm_symbol(env_glyph, &ch, &color); - return ch; + // Must avoid double setting it. + // We want the base terrain/item, not the cloud or monster that replaced it. + if (!Show_Backup[ex][ey]) + Show_Backup[ex][ey] = env.show[ex][ey]; } -static int item_env_glyph(const item_def &item) +static int get_item_dngn_code(const item_def &item) { switch (item.base_type) { case OBJ_ORBS: - return 256; - // need + 6 because show is 0 - 12, not -6 - +6 + return (DNGN_ITEM_ORB); case OBJ_WEAPONS: + return (DNGN_ITEM_WEAPON); case OBJ_MISSILES: - return 258; + return (DNGN_ITEM_MISSILE); case OBJ_ARMOUR: - return 259; + return (DNGN_ITEM_ARMOUR); case OBJ_WANDS: - return 260; + return (DNGN_ITEM_WAND); case OBJ_FOOD: - return 261; - case OBJ_UNKNOWN_I: - return 262; + return (DNGN_ITEM_FOOD); case OBJ_SCROLLS: - return 263; + return (DNGN_ITEM_SCROLL); case OBJ_JEWELLERY: - return item.sub_type >= AMU_RAGE? 273 : 264; + return (jewellery_is_amulet(item)? DNGN_ITEM_AMULET : DNGN_ITEM_RING); case OBJ_POTIONS: - return 265; - case OBJ_UNKNOWN_II: - return 266; + return (DNGN_ITEM_POTION); case OBJ_BOOKS: - return 267; + return (DNGN_ITEM_BOOK); case OBJ_STAVES: - return 269; + return (DNGN_ITEM_STAVE); case OBJ_MISCELLANY: - return 270; + return (DNGN_ITEM_MISCELLANY); case OBJ_CORPSES: - return 271; + return (DNGN_ITEM_CORPSE); case OBJ_GOLD: - return 272; + return (DNGN_ITEM_GOLD); default: - return '8'; + return (DNGN_ITEM_ORB); // bad item character } } -void item() +void item_grid() { char count_x, count_y; @@ -1443,34 +786,22 @@ void item() { if (igrd[count_x][count_y] != NON_ITEM) { - if (env.show[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9] != 0) + const int ix = count_x - you.x_pos + 9; + const int iy = count_y - you.y_pos + 9; + if (env.show[ix][iy]) { const item_def &eitem = mitm[igrd[count_x][count_y]]; - unsigned short &ecol = - env.show_col[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9]; + unsigned short &ecol = env.show_col[ix][iy]; ecol = (grd[count_x][count_y] == DNGN_SHALLOW_WATER)? CYAN : eitem.colour; -#ifdef USE_COLOUR_OPTS if (eitem.link != NON_ITEM) { ecol |= COLFLAG_ITEM_HEAP; } -#elif defined(WIN32CONSOLE) || defined(DOS) - if (eitem.link != NON_ITEM - && Options.heap_brand != CHATTR_NORMAL) - { - // Yes, exact same code as friend-branding. - ecol = dos_brand(ecol, Options.heap_brand); - } -#endif - env.show[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9] = - item_env_glyph( eitem ); + env.show[ix][iy] = get_item_dngn_code( eitem ); } } } @@ -1484,7 +815,7 @@ void cloud_grid(void) int mnc = 0; // btw, this is also the 'default' color {dlb} - unsigned char which_color = LIGHTGREY; + unsigned char which_colour = LIGHTGREY; for (int s = 0; s < MAX_CLOUDS; s++) { @@ -1499,94 +830,105 @@ void cloud_grid(void) if (see_grid(env.cloud[s].x, env.cloud[s].y)) { - show_backup[env.cloud[s].x - you.x_pos + 9] - [env.cloud[s].y - you.y_pos + 9] - = env.show[env.cloud[s].x - you.x_pos + 9] - [env.cloud[s].y - you.y_pos + 9]; - - env.show[env.cloud[s].x - you.x_pos + 9] - [env.cloud[s].y - you.y_pos + 9] = '#'; - + const int ex = env.cloud[s].x - you.x_pos + 9; + const int ey = env.cloud[s].y - you.y_pos + 9; + switch (env.cloud[s].type) { case CLOUD_FIRE: case CLOUD_FIRE_MON: if (env.cloud[s].decay <= 20) - which_color = RED; + which_colour = RED; else if (env.cloud[s].decay <= 40) - which_color = LIGHTRED; + which_colour = LIGHTRED; else if (one_chance_in(4)) - which_color = RED; + which_colour = RED; else if (one_chance_in(4)) - which_color = LIGHTRED; + which_colour = LIGHTRED; else - which_color = YELLOW; + which_colour = YELLOW; break; case CLOUD_STINK: case CLOUD_STINK_MON: - which_color = GREEN; + which_colour = GREEN; break; case CLOUD_COLD: case CLOUD_COLD_MON: if (env.cloud[s].decay <= 20) - which_color = BLUE; + which_colour = BLUE; else if (env.cloud[s].decay <= 40) - which_color = LIGHTBLUE; + which_colour = LIGHTBLUE; else if (one_chance_in(4)) - which_color = BLUE; + which_colour = BLUE; else if (one_chance_in(4)) - which_color = LIGHTBLUE; + which_colour = LIGHTBLUE; else - which_color = WHITE; + which_colour = WHITE; break; case CLOUD_POISON: case CLOUD_POISON_MON: - which_color = (one_chance_in(3) ? LIGHTGREEN : GREEN); + which_colour = (one_chance_in(3) ? LIGHTGREEN : GREEN); break; case CLOUD_BLUE_SMOKE: case CLOUD_BLUE_SMOKE_MON: - which_color = LIGHTBLUE; + which_colour = LIGHTBLUE; break; case CLOUD_PURP_SMOKE: case CLOUD_PURP_SMOKE_MON: - which_color = MAGENTA; + which_colour = MAGENTA; break; case CLOUD_MIASMA: case CLOUD_MIASMA_MON: case CLOUD_BLACK_SMOKE: case CLOUD_BLACK_SMOKE_MON: - which_color = DARKGREY; + which_colour = DARKGREY; break; default: - which_color = LIGHTGREY; + which_colour = LIGHTGREY; break; } - env.show_col[env.cloud[s].x - you.x_pos + 9] - [env.cloud[s].y - you.y_pos + 9] = which_color; + set_show_backup(ex, ey); + env.show[ex][ey] = DNGN_CLOUD; + env.show_col[ex][ey] = which_colour; } } // end 'if != CLOUD_NONE' } // end 'for s' loop } // end cloud_grid() - -void noisy( int loudness, int nois_x, int nois_y ) +// Noisy now has a messenging service for giving messages to the +// player is appropriate. +// +// Returns true if the PC heard the noise. +bool noisy( int loudness, int nois_x, int nois_y, const char *msg ) { int p; struct monsters *monster = 0; // NULL {dlb} + bool ret = false; + // If the origin is silenced there is no noise. if (silenced( nois_x, nois_y )) - return; + return (false); - int dist = loudness * loudness; + const int dist = loudness * loudness; + + // message the player + if (distance( you.x_pos, you.y_pos, nois_x, nois_y ) <= dist + && player_can_hear( nois_x, nois_y )) + { + if (msg) + mpr( msg, MSGCH_SOUND ); + ret = true; + } + for (p = 0; p < MAX_MONSTERS; p++) { monster = &menv[p]; @@ -1605,21 +947,19 @@ void noisy( int loudness, int nois_x, int nois_y ) behaviour_event( monster, ME_DISTURB, MHITNOT, nois_x, nois_y ); } } + + return (ret); } // end noisy() -/* ======================================================================== - * brand new LOS code - * ======================================================================== - * The new LOS works via a new (I think) shadow casting algorithm, - * plus an esthetic tweak for more pleasing corner illumination. More - * detail can be had by contacting its author, Gordon Lipford. */ +/* The LOS code now uses raycasting -- haranp */ -#define MAX_LIGHT_RADIUS 20 -#define CIRC_MAX 32000 -#define BIG_SHADOW 32000 +#define LONGSIZE (sizeof(unsigned long)*8) +#define LOS_MAX_RANGE_X 9 +#define LOS_MAX_RANGE_Y 9 +#define LOS_MAX_RANGE 9 // the following two constants represent the 'middle' of the sh array. -// since the current shown area is 19x19, centering the view at (9,9) +// since the current shown area is 19x19, centering the view at (9,9) // means it will be exactly centered. // This is done to accomodate possible future changes in viewable screen // area - simply change sh_xo and sh_yo to the new view center. @@ -1627,340 +967,648 @@ void noisy( int loudness, int nois_x, int nois_y ) const int sh_xo = 9; // X and Y origins for the sh array const int sh_yo = 9; -// the Cell class, used in the shadow-casting LOS algorithm -class Cell -{ +// Data used for the LOS algorithm +int los_radius_squared = 8*8 + 1; -public: - int up_count; - int up_max; - int low_count; - int low_max; - bool lit; - bool lit_delay; - bool visible; // for blockers only - void init(); - bool reachedLower(); - bool reachedUpper(); - - Cell() - { - init(); - }; -}; +unsigned long* los_blockrays = NULL; +unsigned long* dead_rays = NULL; +std::vector ray_coord_x; +std::vector ray_coord_y; +std::vector compressed_ray_x; +std::vector compressed_ray_y; +std::vector raylengths; +std::vector fullrays; -void Cell::init() +void setLOSRadius(int newLR) { - up_count = 0; - up_max = 0; - low_count = 0; - low_max = 0; - lit = true; - visible = true; - lit_delay = false; + los_radius_squared = newLR * newLR + 1*1; } -bool Cell::reachedLower() +bool get_bit_in_long_array( const unsigned long* data, int where ) { - // integer math: a 'step' has a value of 10 - // see if we're within a half step of the max. VERY important - // to use 'half step' or else things look really stupid. - if (low_max != 0 && low_count + 5 >= low_max && low_count - 5 < low_max) - return true; + int wordloc = where / LONGSIZE; + int bitloc = where % LONGSIZE; + return ((data[wordloc] & (1UL << bitloc)) != 0); +} - return false; +static void set_bit_in_long_array( unsigned long* data, int where ) { + int wordloc = where / LONGSIZE; + int bitloc = where % LONGSIZE; + data[wordloc] |= (1UL << bitloc); } -bool Cell::reachedUpper() +#define EPSILON_VALUE 0.00001 +bool double_is_zero( const double x ) { - // see if we're within a half step of the max. VERY important - // to use 'half step' or else things look really stupid. - if (up_max != 0 && up_count + 5 >= up_max && up_count - 5 < up_max) - return true; - - return false; + return (x > -EPSILON_VALUE) && (x < EPSILON_VALUE); } -// the cell array -static FixedVector < Cell, MAX_LIGHT_RADIUS + 1 > cells; +// note that slope must be nonnegative! +// returns 0 if the advance was in x, 1 if it was in y, 2 if it was +// the diagonal +static int find_next_intercept(double* accx, double* accy, const double slope) +{ + + // handle perpendiculars + if ( double_is_zero(slope) ) + { + *accx += 1.0; + return 0; + } + if ( slope > 100.0 ) + { + *accy += 1.0; + return 1; + } + + const double xtarget = (double)((int)(*accx) + 1); + const double ytarget = (double)((int)(*accy) + 1); + const double xdistance = xtarget - *accx; + const double ydistance = ytarget - *accy; + const double distdiff = (xdistance * slope - ydistance); + + // exact corner + if ( double_is_zero( distdiff ) ) { + // move somewhat away from the corner + if ( slope > 1.0 ) { + *accx = xtarget + EPSILON_VALUE * 2; + *accy = ytarget + EPSILON_VALUE * 2 * slope; + } + else { + *accx = xtarget + EPSILON_VALUE * 2 / slope; + *accy = ytarget + EPSILON_VALUE * 2; + } + return 2; + } + + double traveldist; + int rc = -1; + if ( distdiff > 0.0 ) { + traveldist = ydistance / slope; + rc = 1; + } + else { + traveldist = xdistance; + rc = 0; + } -// the 'circle' array. For any given row, we won't check higher than -// this given cell. -static FixedVector < int, MAX_LIGHT_RADIUS + 1 > circle; + traveldist += EPSILON_VALUE * 10.0; -// current light radius -static int LR = 0; + *accx += traveldist; + *accy += traveldist * slope; + return rc; +} -// View constant -const int view = 2; // 1=widest LOS .. 5=narrowest +void ray_def::advance_and_bounce() +{ + // 0 = down-right, 1 = down-left, 2 = up-left, 3 = up-right + int bouncequad[4][3] = { + { 1, 3, 2 }, { 0, 2, 3 }, { 3, 1, 0 }, { 2, 0, 1 } + }; + int oldx = x(), oldy = y(); + int rc = advance(); + int newx = x(), newy = y(); + ASSERT( grid_is_solid(grd[newx][newy]) ); + if ( double_is_zero(slope) || slope > 100.0 ) + quadrant = bouncequad[quadrant][2]; + else if ( rc != 2 ) + quadrant = bouncequad[quadrant][rc]; + else + { + ASSERT( (oldx != newx) && (oldy != newy) ); + bool blocked_x = grid_is_solid(grd[oldx][newy]); + bool blocked_y = grid_is_solid(grd[newx][oldy]); + if ( blocked_x && blocked_y ) + quadrant = bouncequad[quadrant][rc]; + else if ( blocked_x ) + quadrant = bouncequad[quadrant][1]; + else + quadrant = bouncequad[quadrant][0]; + } + advance(); +} -// initialize LOS code for a given light radius -extern void setLOSRadius(int newLR) +void ray_def::regress() { - int i, j; + int opp_quadrant[4] = { 2, 3, 0, 1 }; + quadrant = opp_quadrant[quadrant]; + advance(); + quadrant = opp_quadrant[quadrant]; +} - // sanity check - also allows multiple calls w/out performance loss - if (LR == newLR) - return; +int ray_def::advance() +{ + int rc; + switch ( quadrant ) + { + case 0: + // going down-right + rc = find_next_intercept( &accx, &accy, slope ); + return rc; + case 1: + // going down-left + accx = 100.0 - EPSILON_VALUE/10.0 - accx; + rc = find_next_intercept( &accx, &accy, slope ); + accx = 100.0 - EPSILON_VALUE/10.0 - accx; + return rc; + case 2: + // going up-left + accx = 100.0 - EPSILON_VALUE/10.0 - accx; + accy = 100.0 - EPSILON_VALUE/10.0 - accy; + rc = find_next_intercept( &accx, &accy, slope ); + accx = 100.0 - EPSILON_VALUE/10.0 - accx; + accy = 100.0 - EPSILON_VALUE/10.0 - accy; + return rc; + case 3: + // going up-right + accy = 100.0 - EPSILON_VALUE/10.0 - accy; + rc = find_next_intercept( &accx, &accy, slope ); + accy = 100.0 - EPSILON_VALUE/10.0 - accy; + return rc; + default: + return -1; + } +} - LR = newLR; - // cells should already be initted. calculate the circle array. +// Shoot a ray from the given start point (accx, accy) with the given +// slope, with a maximum distance (in either x or y coordinate) of +// maxrange. Store the visited cells in xpos[] and ypos[], and +// return the number of cells visited. +static int shoot_ray( double accx, double accy, const double slope, + int maxrange, int xpos[], int ypos[] ) +{ + int curx, cury; + int cellnum; + for ( cellnum = 0; true; ++cellnum ) + { + find_next_intercept( &accx, &accy, slope ); + curx = (int)(accx); + cury = (int)(accy); + if ( curx > maxrange || cury > maxrange ) + break; - // note that rows 0 and 1 will always go to infinity. - circle[0] = circle[1] = CIRC_MAX; + // work with the new square + xpos[cellnum] = curx; + ypos[cellnum] = cury; + } + return cellnum; +} - // for the rest, simply calculate max height based on light rad. - for (i = 2; i <= LR; i++) +// check if the passed ray has already been created +static bool is_duplicate_ray( int len, int xpos[], int ypos[] ) +{ + int cur_offset = 0; + for ( unsigned int i = 0; i < raylengths.size(); ++i ) { - // check top - if (2 * i * i <= LR * LR) + // only compare equal-length rays + if ( raylengths[i] != len ) { - circle[i] = CIRC_MAX; + cur_offset += raylengths[i]; continue; } - for (j = i - 1; j >= 0; j--) + int j; + for ( j = 0; j < len; ++j ) { - // check that Distance (I^2 + J^2) is no more than (R+0.5)^2 - // this rounding allows for *much* better looking circles. - if (i * i + j * j <= LR * LR + LR) - { - circle[i] = j; + if ( ray_coord_x[j + cur_offset] != xpos[j] || + ray_coord_y[j + cur_offset] != ypos[j] ) break; - } } + + // exact duplicate? + if ( j == len ) + return true; + + // move to beginning of next ray + cur_offset += raylengths[i]; } + return false; } -static int calcUpper(int bX, int bY) +// is starta...lengtha a subset of startb...lengthb? +static bool is_subset( int starta, int startb, int lengtha, int lengthb ) { - // got a blocker at row bX, cell bY. do all values - // and scale by a factor of 10 for the integer math. - int upper; + int cura = starta, curb = startb; + int enda = starta + lengtha, endb = startb + lengthb; + while ( cura < enda && curb < endb ) + { + if ( ray_coord_x[curb] > ray_coord_x[cura] ) + return false; + if ( ray_coord_y[curb] > ray_coord_y[cura] ) + return false; + if ( ray_coord_x[cura] == ray_coord_x[curb] && + ray_coord_y[cura] == ray_coord_y[curb] ) + ++cura; - upper = (10 * (10 * bX - view)) / (10 * bY + view); - if (upper < 10) // upper bound for blocker on diagonal - upper = 10; + ++curb; + } + return ( cura == enda ); +} - return upper; +// return a vector which lists all the nonduped cellrays (by index) +static std::vector find_nonduped_cellrays() +{ + // a cellray c in a fullray f is duped if there is a fullray g + // such that g contains c and g[:c] is a subset of f[:c] + int raynum, cellnum, curidx, testidx, testray, testcell; + bool is_duplicate; + + std::vector result; + for (curidx=0, raynum=0; + raynum < (int)raylengths.size(); + curidx += raylengths[raynum++]) + { + for (cellnum = 0; cellnum < raylengths[raynum]; ++cellnum) + { + // is the cellray raynum[cellnum] duplicated? + is_duplicate = false; + // XXX We should really check everything up to now + // completely, and all further rays to see if they're + // proper subsets. + const int curx = ray_coord_x[curidx + cellnum]; + const int cury = ray_coord_y[curidx + cellnum]; + for (testidx = 0, testray = 0; testray < raynum; + testidx += raylengths[testray++]) + { + // scan ahead to see if there's an intersect + for ( testcell = 0; testcell < raylengths[raynum]; ++testcell ) + { + const int testx = ray_coord_x[testidx + testcell]; + const int testy = ray_coord_y[testidx + testcell]; + // we can short-circuit sometimes + if ( testx > curx || testy > cury ) + break; + // bingo! + if ( testx == curx && testy == cury ) + { + is_duplicate = is_subset(testidx, curidx, + testcell, cellnum); + break; + } + } + if ( is_duplicate ) + break; // no point in checking further rays + } + if ( !is_duplicate ) + result.push_back(curidx + cellnum); + } + } + return result; } -static int calcLower(int bX, int bY) +// Create and register the ray defined by the arguments. +// Return true if the ray was actually registered (i.e., not a duplicate.) +static bool register_ray( double accx, double accy, double slope ) { - // got a blocker at row bX, cell bY. do all values - // and scale by a factor of 10 for the integer math. + int xpos[LOS_MAX_RANGE * 2 + 1], ypos[LOS_MAX_RANGE * 2 + 1]; + int raylen = shoot_ray( accx, accy, slope, LOS_MAX_RANGE, xpos, ypos ); - if (bY == 0) - return BIG_SHADOW; + // early out if ray already exists + if ( is_duplicate_ray(raylen, xpos, ypos) ) + return false; - return (10 * (10 * bX + view)) / (10 * bY - view); -} + // not duplicate, register + for ( int i = 0; i < raylen; ++i ) + { + // create the cellrays + ray_coord_x.push_back(xpos[i]); + ray_coord_y.push_back(ypos[i]); + } -// for easy x,y octant translation -static int xxcomp[8] = { 1, 0, 0, -1, -1, 0, 0, 1 }; -static int xycomp[8] = { 0, 1, -1, 0, 0, -1, 1, 0 }; -static int yxcomp[8] = { 0, 1, 1, 0, 0, -1, -1, 0 }; -static int yycomp[8] = { 1, 0, 0, 1, -1, 0, 0, -1 }; + // register the fullray + raylengths.push_back(raylen); + ray_def ray; + ray.accx = accx; + ray.accy = accy; + ray.slope = slope; + ray.quadrant = 0; + fullrays.push_back(ray); -static void los_octant(int o, FixedArray < unsigned int, 19, 19 > &sh, - FixedArray < unsigned char, 80, 70 > &gr, int x_p, - int y_p) + return true; +} + +static void create_blockrays() { - int row, cell, top, south; - int tx, ty; // translated x, y deltas for this octant - unsigned char gv; // grid value - bool row_dark, all_dark; - bool blocker, vis_corner; - int up_inc, low_inc; - - // leave [0,0] alone, because the old LOS code seems to. - - // init cell[0]. this is the only one that needs clearing. - cells[0].init(); - all_dark = false; - vis_corner = false; - - // loop through each row - for (row = 1; row <= LR; row++) + // determine nonduplicated rays + std::vector nondupe_cellrays = find_nonduped_cellrays(); + const unsigned int num_nondupe_rays = nondupe_cellrays.size(); + const unsigned int num_nondupe_words = + (num_nondupe_rays + LONGSIZE - 1) / LONGSIZE; + const unsigned int num_cellrays = ray_coord_x.size(); + const unsigned int num_words = (num_cellrays + LONGSIZE - 1) / LONGSIZE; + + // first build all the rays: easier to do blocking calculations there + unsigned long* full_los_blockrays; + full_los_blockrays = new unsigned long[num_words * (LOS_MAX_RANGE_X+1) * + (LOS_MAX_RANGE_Y+1)]; + memset((void*)full_los_blockrays, 0, sizeof(unsigned long) * num_words * + (LOS_MAX_RANGE_X+1) * (LOS_MAX_RANGE_Y+1)); + + int cur_offset = 0; + + for ( unsigned int ray = 0; ray < raylengths.size(); ++ray ) { - row_dark = true; - - // loop through each cell, up to the max allowed by circle[] - top = circle[row]; - if (top > row) - top = row; - - for (cell = 0; cell <= top; cell++) + for ( int i = 0; i < raylengths[ray]; ++i ) { - // translate X,Y co'ord + bounds check - tx = row * xxcomp[o] + cell * xycomp[o]; - ty = row * yxcomp[o] + cell * yycomp[o]; + // every cell blocks... + unsigned long* const inptr = full_los_blockrays + + (ray_coord_x[i + cur_offset] * (LOS_MAX_RANGE_Y + 1) + + ray_coord_y[i + cur_offset]) * num_words; - if (x_p + tx < 0 || x_p + tx > 79 || y_p + ty < 0 || y_p + ty > 69) - continue; + // ...all following cellrays + for ( int j = i+1; j < raylengths[ray]; ++j ) + set_bit_in_long_array( inptr, j + cur_offset ); - // check for all_dark - we've finished the octant but - // have yet to fill in '0' for the rest of the sight grid - if (all_dark == true) - { - sh[sh_xo + tx][sh_yo + ty] = 0; - continue; - } + } + cur_offset += raylengths[ray]; + } - // get grid value.. see if it blocks LOS - gv = gr[x_p + tx][y_p + ty]; - blocker = (gv < MINSEE); + // we've built the basic blockray array; now compress it, keeping + // only the nonduplicated cellrays. - // init some other variables - up_inc = 10; - low_inc = 10; - south = cell - 1; + // allocate and clear memory + los_blockrays = new unsigned long[num_nondupe_words * (LOS_MAX_RANGE_X+1) * (LOS_MAX_RANGE_Y + 1)]; + memset((void*)los_blockrays, 0, sizeof(unsigned long) * num_nondupe_words * + (LOS_MAX_RANGE_X+1) * (LOS_MAX_RANGE_Y+1)); - // STEP 1 - inherit values from immediate West, if possible - if (cell < row) - { - // check for delayed lighting - if (cells[cell].lit_delay) - { - if (!blocker) - { // blockers don't light up with lit_delay. - if (cells[south].lit) - { - if (cells[south].low_max != 0) - { - cells[cell].lit = false; - // steal lower values - cells[cell].low_max = cells[south].low_max; - cells[cell].low_count = cells[south].low_count; - cells[south].low_count = 0; - cells[south].low_max = 0; - low_inc = 0; // avoid double-inc. - } - else - cells[cell].lit = true; - } - } - cells[cell].lit_delay = false; - } - } - else - { - // initialize new cell. - cells[cell].init(); - } + // we want to only keep the cellrays from nondupe_cellrays. + compressed_ray_x.resize(num_nondupe_rays); + compressed_ray_y.resize(num_nondupe_rays); + for ( unsigned int i = 0; i < num_nondupe_rays; ++i ) + { + compressed_ray_x[i] = ray_coord_x[nondupe_cellrays[i]]; + compressed_ray_y[i] = ray_coord_y[nondupe_cellrays[i]]; + } + unsigned long* oldptr = full_los_blockrays; + unsigned long* newptr = los_blockrays; + for ( int x = 0; x <= LOS_MAX_RANGE_X; ++x ) + { + for ( int y = 0; y <= LOS_MAX_RANGE_Y; ++y ) + { + for ( unsigned int i = 0; i < num_nondupe_rays; ++i ) + if ( get_bit_in_long_array(oldptr, nondupe_cellrays[i]) ) + set_bit_in_long_array(newptr, i); + oldptr += num_words; + newptr += num_nondupe_words; + } + } - // STEP 2 - check for blocker - // a dark blocker in shadow's edge will be visible - if (blocker) - { - if (cells[cell].lit || (cell != 0 && cells[south].lit) - || vis_corner) - { - // hack: make 'corners' visible - vis_corner = cells[cell].lit; + // we can throw away full_los_blockrays now + delete [] full_los_blockrays; - cells[cell].lit = false; - cells[cell].visible = true; + dead_rays = new unsigned long[num_nondupe_words]; + +#ifdef DEBUG_DIAGNOSTICS + mprf( MSGCH_DIAGNOSTICS, "Cellrays: %d Fullrays: %u Compressed: %u", + num_cellrays, raylengths.size(), num_nondupe_rays ); +#endif +} - int upper = calcUpper(row, cell); - int lower = calcLower(row, cell); +static int gcd( int x, int y ) +{ + int tmp; + while ( y != 0 ) + { + x %= y; + tmp = x; + x = y; + y = tmp; + } + return x; +} - if (upper < cells[cell].up_max || cells[cell].up_max == 0) - { - // new upper shadow - cells[cell].up_max = upper; - cells[cell].up_count = 0; - up_inc = 0; - } +// Cast all rays +void raycast() +{ + static bool done_raycast = false; + if ( done_raycast ) + return; + + // Creating all rays for first quadrant + // We have a considerable amount of overkill. + done_raycast = true; + + int xangle, yangle; + + // register perpendiculars FIRST, to make them top choice + // when selecting beams + register_ray( 0.5, 0.5, 1000.0 ); + register_ray( 0.5, 0.5, 0.0 ); + + // For a slope of M = y/x, every x we move on the X axis means + // that we move y on the y axis. We want to look at the resolution + // of x/y: in that case, every step on the X axis means an increase + // of 1 in the Y axis at the intercept point. We can assume gcd(x,y)=1, + // so we look at steps of 1/y. + for ( xangle = 1; xangle <= LOS_MAX_RANGE; ++xangle ) { + for ( yangle = 1; yangle <= LOS_MAX_RANGE; ++yangle ) { + + if ( gcd(xangle, yangle) != 1 ) + continue; - if (lower > cells[cell].low_max || cells[cell].low_max == 0) - { - // new lower shadow - cells[cell].low_max = lower; - cells[cell].low_count = -10; - low_inc = 0; - if (lower <= 30) // somewhat arbitrary - cells[cell].lit_delay = true; - // set dark_delay if lower > 20?? how to decide? - } - } - else - { - cells[cell].visible = false; - } - } - else - { - cells[cell].visible = false; // special flags for blockers + const double slope = ((double)(yangle)) / xangle; + const double rslope = ((double)(xangle)) / yangle; + for ( int intercept = 0; intercept <= yangle; ++intercept ) { + double xstart = ((double)(intercept)) / yangle; + if ( intercept == 0 ) + xstart += EPSILON_VALUE / 10.0; + if ( intercept == yangle ) + xstart -= EPSILON_VALUE / 10.0; + // y should be "about to change" + register_ray( xstart, 1.0 - EPSILON_VALUE / 10.0, slope ); + // also draw the identical ray in octant 2 + register_ray( 1.0 - EPSILON_VALUE / 10.0, xstart, rslope ); } + } + } + + // Now create the appropriate blockrays array + create_blockrays(); +} + +static void set_ray_quadrant( ray_def& ray, int sx, int sy, int tx, int ty ) +{ + if ( tx >= sx && ty >= sy ) + ray.quadrant = 0; + else if ( tx < sx && ty >= sy ) + ray.quadrant = 1; + else if ( tx < sx && ty < sy ) + ray.quadrant = 2; + else if ( tx >= sx && ty < sy ) + ray.quadrant = 3; + else + mpr("Bad ray quadrant!", MSGCH_DIAGNOSTICS); +} - // STEP 3 - add increments to upper, lower counts - cells[cell].up_count += up_inc; - cells[cell].low_count += low_inc; - // STEP 4 - check south for dark - if (south >= 0) - if (cells[south].reachedUpper() == true) - { - if (cells[cell].reachedUpper() == false) +// Find a nonblocked ray from sx, sy to tx, ty. Return false if no +// such ray could be found, otherwise return true and fill ray +// appropriately. +// If allow_fallback is true, fall back to a center-to-center ray +// if range is too great or all rays are blocked. +bool find_ray( int sourcex, int sourcey, int targetx, int targety, + bool allow_fallback, ray_def& ray ) +{ + + int cellray, inray; + const int signx = ((targetx - sourcex >= 0) ? 1 : -1); + const int signy = ((targety - sourcey >= 0) ? 1 : -1); + const int absx = signx * (targetx - sourcex); + const int absy = signy * (targety - sourcey); + int cur_offset = 0; + for ( unsigned int fullray = 0; fullray < fullrays.size(); + cur_offset += raylengths[fullray++] ) { + + for ( cellray = 0; cellray < raylengths[fullray]; ++cellray ) + { + if ( ray_coord_x[cellray + cur_offset] == absx && + ray_coord_y[cellray + cur_offset] == absy ) { + + // check if we're blocked so far + bool blocked = false; + for ( inray = 0; inray < cellray; ++inray ) { + if (grid_is_solid(grd[sourcex + signx * ray_coord_x[inray + cur_offset]][sourcey + signy * ray_coord_y[inray + cur_offset]])) { - cells[cell].up_max = cells[south].up_max; - cells[cell].up_count = cells[south].up_count; - cells[cell].up_count -= cells[south].up_max; + blocked = true; + break; } - cells[cell].lit = false; - cells[cell].visible = false; - } - - // STEP 5 - nuke lower if south lower - if (south >= 0) - { - if (cells[south].reachedLower()) - { - cells[cell].low_max = cells[south].low_max; - cells[cell].low_count = cells[south].low_count; - cells[cell].low_count -= cells[south].low_max; - cells[south].low_count = cells[south].low_max = 0; } - if (cells[south].low_max != 0 - || (cells[south].lit == false - && cells[south].low_max == 0)) + if ( !blocked ) { - cells[cell].low_count = cells[cell].low_max + 10; + // success! + ray = fullrays[fullray]; + if ( sourcex > targetx ) + ray.accx = 1.0 - ray.accx; + if ( sourcey > targety ) + ray.accy = 1.0 - ray.accy; + ray.accx += sourcex; + ray.accy += sourcey; + set_ray_quadrant(ray, sourcex, sourcey, targetx, targety); + return true; } } - - // STEP 6 - light up if we've reached lower bound - if (cells[cell].reachedLower() == true) - cells[cell].lit = true; - - // now place appropriate value in sh - if (cells[cell].lit == true - || (blocker == true && cells[cell].visible == true)) - { - sh[sh_xo + tx][sh_yo + ty] = gv; - } - else - sh[sh_xo + tx][sh_yo + ty] = 0; - - if (cells[cell].lit == true) - row_dark = false; - } // end for - cells - - vis_corner = false; // don't carry over to next row. :) - if (row_dark == true) - all_dark = true; - } // end for - rows + } + } + if ( allow_fallback ) { + ray.accx = sourcex + 0.5; + ray.accy = sourcey + 0.5; + if ( targetx == sourcex ) + ray.slope = 10000.0; + else { + ray.slope = targety - sourcey; + ray.slope /= targetx - sourcex; + if ( ray.slope < 0 ) + ray.slope = -ray.slope; + } + set_ray_quadrant(ray, sourcex, sourcey, targetx, targety); + return true; + } + return false; } +// The rule behind LOS is: +// Two cells can see each other if there is any line from some point +// of the first to some point of the second ("generous" LOS.) +// +// We use raycasting. The algorithm: +// PRECOMPUTATION: +// Create a large bundle of rays and cast them. +// Mark, for each one, which cells kill it (and where.) +// Also, for each one, note which cells it passes. +// ACTUAL LOS: +// Unite the ray-killers for the given map; this tells you which rays +// are dead. +// Look up which cells the surviving rays have, and that's your LOS! +// OPTIMIZATIONS: +// WLOG, we can assume that we're in a specific quadrant - say the +// first quadrant - and just mirror everything after that. We can +// likely get away with a single octant, but we don't do that. (To +// do...) +// Rays are actually split by each cell they pass. So each "ray" only +// identifies a single cell, and we can do logical ORs. Once a cell +// kills a cellray, it will kill all remaining cellrays of that ray. +// Also, rays are checked to see if they are duplicates of each +// other. If they are, they're eliminated. +// Some cellrays can also be eliminated. In general, a cellray is +// unnecessary if there is another cellray with the same coordinates, +// and whose path (up to those coordinates) is a subset, not necessarily +// proper, of the original path. We still store the original cellrays +// fully for beam detection and such. +// PERFORMANCE: +// With reasonable values we have around 6000 cellrays, meaning +// around 600Kb (75 KB) of data. This gets cut down to 700 cellrays +// after removing duplicates. That means that we need to do +// around 22*100*4 ~ 9,000 memory reads + writes per LOS call on a +// 32-bit system. Not too bad. void losight(FixedArray < unsigned int, 19, 19 > &sh, FixedArray < unsigned char, 80, 70 > &gr, int x_p, int y_p) { - int o; + raycast(); + // go quadrant by quadrant + int quadrant_x[4] = { 1, -1, -1, 1 }; + int quadrant_y[4] = { 1, 1, -1, -1 }; + + // clear out sh + for ( int i = 0; i < 19; ++i ) + for ( int j = 0; j < 19; ++j ) + sh[i][j] = 0; + + const unsigned int num_cellrays = compressed_ray_x.size(); + const unsigned int num_words = (num_cellrays + LONGSIZE - 1) / LONGSIZE; + + for ( int quadrant = 0; quadrant < 4; ++quadrant ) { + const int xmult = quadrant_x[quadrant]; + const int ymult = quadrant_y[quadrant]; + + // clear out the dead rays array + memset( (void*)dead_rays, 0, sizeof(unsigned long) * num_words); + + // kill all blocked rays + const unsigned long* inptr = los_blockrays; + for ( int xdiff = 0; xdiff <= LOS_MAX_RANGE_X; ++xdiff ) { + for (int ydiff = 0; ydiff <= LOS_MAX_RANGE_Y; + ++ydiff, inptr += num_words ) { + + const int realx = x_p + xdiff * xmult; + const int realy = y_p + ydiff * ymult; + + if (realx < 0 || realx > 79 || realy < 0 || realy > 69) + continue; + + // if this cell is opaque... + if ( grid_is_opaque(gr[realx][realy])) { + // then block the appropriate rays + for ( unsigned int i = 0; i < num_words; ++i ) + dead_rays[i] |= inptr[i]; + } + } + } - for (o = 0; o < 8; o++) - los_octant(o, sh, gr, x_p, y_p); + // ray calculation done, now work out which cells in this + // quadrant are visible + unsigned int rayidx = 0; + for ( unsigned int wordloc = 0; wordloc < num_words; ++wordloc ) { + const unsigned long curword = dead_rays[wordloc]; + // Note: the last word may be incomplete + for ( unsigned int bitloc = 0; bitloc < LONGSIZE; ++bitloc) { + // make the cells seen by this ray at this point visible + if ( ((curword >> bitloc) & 1UL) == 0 ) { + // this ray is alive! + const int realx = xmult * compressed_ray_x[rayidx]; + const int realy = ymult * compressed_ray_y[rayidx]; + // update shadow map + if (x_p + realx >= 0 && x_p + realx < 80 && + y_p + realy >= 0 && y_p + realy < 70 && + realx * realx + realy * realy <= los_radius_squared ) + sh[sh_xo+realx][sh_yo+realy]=gr[x_p+realx][y_p+realy]; + } + ++rayidx; + if ( rayidx == num_cellrays ) + break; + } + } + } } @@ -2206,11 +1854,34 @@ static int find_feature( const std::vector& features, return 0; } +#ifdef USE_CURSES +// NOTE: This affects libunix.cc draw state; use this just before setting +// textcolour and drawing a character and call set_altcharset(false) +// after you're done drawing. +// +static int cset_adjust(int raw) +{ + if (Options.char_set != CSET_ASCII) + { + // switch to alternate char set for 8-bit characters: + set_altcharset( raw > 127 ); + + // shift the DEC line drawing set: + if (Options.char_set == CSET_DEC + && raw >= 0xE0) + { + raw &= 0x7F; + } + } + return (raw); +} +#endif + // show_map() now centers the known map along x or y. This prevents // the player from getting "artificial" location clues by using the // map to see how close to the end they are. They'll need to explore // to get that. This function is still a mess, though. -- bwr -void show_map( FixedVector &spec_place ) +void show_map( FixedVector &spec_place, bool travel_mode ) { int i, j; @@ -2226,7 +1897,8 @@ void show_map( FixedVector &spec_place ) // Vector to track all features we can travel to, in order of distance. std::vector features; - if (!spec_place[0]) { + if (travel_mode) + { travel_cache.update(); find_travel_pos(you.x_pos, you.y_pos, NULL, NULL, &features); @@ -2336,16 +2008,21 @@ void show_map( FixedVector &spec_place ) colour = colour_code_map(start_x + i, start_y + j, Options.item_colour, - !spec_place[0] && Options.travel_colour); + travel_mode && Options.travel_colour); buffer2[bufcount2 + 1] = colour; - - if (start_x + i + 1 == you.x_pos && start_y + j + 1 == you.y_pos) - buffer2[bufcount2 + 1] = WHITE; - buffer2[bufcount2] = (unsigned char) env.map[start_x + i][start_y + j]; + if (start_x + i + 1 == you.x_pos && start_y + j + 1 == you.y_pos) + { + // [dshaligram] Draw the @ symbol on the level-map. It's no + // longer saved into the env.map, so we need to draw it + // directly. + buffer2[bufcount2 + 1] = WHITE; + buffer2[bufcount2] = you.symbol; + } + // If we've a waypoint on the current square, *and* the square is // a normal floor square with nothing on it, show the waypoint // number. @@ -2355,8 +2032,8 @@ void show_map( FixedVector &spec_place ) screen_buffer_t &bc = buffer2[bufcount2]; int gridx = start_x + i + 1, gridy = start_y + j + 1; unsigned char ch = is_waypoint(gridx, gridy); - if (ch && (bc == mapch2(DNGN_FLOOR) || - bc == mapch(DNGN_FLOOR))) + if (ch && (bc == get_sightmap_char(DNGN_FLOOR) || + bc == get_magicmap_char(DNGN_FLOOR))) bc = ch; } @@ -2373,12 +2050,20 @@ void show_map( FixedVector &spec_place ) if (i == 0 && j > 0) gotoxy( 1, j + 1 ); + int ch = buffer2[bufcount2 - 2]; +#ifdef USE_CURSES + ch = cset_adjust( ch ); +#endif textcolor( buffer2[bufcount2 - 1] ); - putch( buffer2[bufcount2 - 2] ); + putch(ch); #endif } } +#ifdef USE_CURSES + set_altcharset(false); +#endif + #ifdef DOS_TERM puttext(1, 1, 80, 25, buffer2); #endif @@ -2389,7 +2074,7 @@ void show_map( FixedVector &spec_place ) gettything: getty = getchm(KC_LEVELMAP); - if (spec_place[0] == 0 && getty != 0 && getty != '+' && getty != '-' + if (travel_mode && getty != 0 && getty != '+' && getty != '-' && getty != 'h' && getty != 'j' && getty != 'k' && getty != 'l' && getty != 'y' && getty != 'u' && getty != 'b' && getty != 'n' && getty != 'H' && getty != 'J' && getty != 'K' && getty != 'L' @@ -2406,12 +2091,13 @@ void show_map( FixedVector &spec_place ) && getty != CONTROL('F') && getty != CONTROL('W') && getty != CONTROL('C') + && getty != '?' && getty != 'X' && getty != 'F' && getty != 'I' && getty != 'W') { goto putty; } - if (spec_place[0] == 1 && getty != 0 && getty != '+' && getty != '-' + if (!travel_mode && getty != 0 && getty != '+' && getty != '-' && getty != 'h' && getty != 'j' && getty != 'k' && getty != 'l' && getty != 'y' && getty != 'u' && getty != 'b' && getty != 'n' && getty != 'H' && getty != 'J' && getty != 'K' && getty != 'L' @@ -2425,9 +2111,13 @@ void show_map( FixedVector &spec_place ) } if (getty == 0) + { getty = getchm(KC_LEVELMAP); + // [dshaligram] DOS madness. + getty = dos_direction_unmunge(getty); + } -#ifdef WIN32CONSOLE +#if defined(WIN32CONSOLE) || defined(DOS) // Translate shifted numpad to shifted vi keys. Yes, // this is horribly hacky. { @@ -2441,6 +2131,10 @@ void show_map( FixedVector &spec_place ) switch (getty) { + case '?': + show_levelmap_help(); + break; + case CONTROL('C'): clear_map(); break; @@ -2591,7 +2285,7 @@ void show_map( FixedVector &spec_place ) search_feat = getty; search_found = 0; } - if (!spec_place[0]) + if (travel_mode) search_found = find_feature(features, getty, curs_x, curs_y, start_x, start_y, search_found, &move_x, &move_y); @@ -2608,7 +2302,7 @@ void show_map( FixedVector &spec_place ) case ';': { int x = start_x + curs_x, y = start_y + curs_y; - if (!spec_place[0] && x == you.x_pos && y == you.y_pos) + if (travel_mode && x == you.x_pos && y == you.y_pos) { if (you.travel_x > 0 && you.travel_y > 0) { move_x = you.travel_x - x; @@ -2701,1404 +2395,1177 @@ void magic_mapping(int map_radius, int proportion) { int i, j, k, l, empty_count; - if (map_radius > 50) + if (map_radius > 50 && map_radius != 1000) map_radius = 50; + else if (map_radius < 5) + map_radius = 5; + + // now gradually weaker with distance: + const int pfar = (map_radius * 7) / 10; + const int very_far = (map_radius * 9) / 10; for (i = you.x_pos - map_radius; i < you.x_pos + map_radius; i++) { for (j = you.y_pos - map_radius; j < you.y_pos + map_radius; j++) { - if (random2(100) > proportion) + if (proportion < 100 && random2(100) >= proportion) continue; // note that proportion can be over 100 - if (i < 5 || j < 5 || i > (GXM - 5) || j > (GYM - 5)) + if (!map_bounds(i, j)) continue; - //if (env.map[i][j] == mapch2(grd[i + 1][j + 1])) - // continue; - if (env.map[i][j]) + const int dist = grid_distance( you.x_pos, you.y_pos, i, j ); + + if (dist > pfar && one_chance_in(3)) + continue; + + if (dist > very_far && coinflip()) + continue; + + if (is_terrain_changed(i, j)) + clear_envmap_grid(i, j); + + if (is_terrain_known(i, j)) continue; empty_count = 8; - if (grd[i][j] < DNGN_LAVA && grd[i][j] != DNGN_CLOSED_DOOR) + if (grid_is_solid(grd[i][j]) && grd[i][j] != DNGN_CLOSED_DOOR) { - for (k = 0; k < 3; k++) + for (k = -1; k <= 1; k++) { - for (l = 0; l < 3; l++) + for (l = -1; l <= 1; l++) { - if (k == 1 && l == 1) + if (k == 0 && l == 0) continue; - if (grd[i + k][j + l] <= 60 - && grd[i + k][j + l] != DNGN_CLOSED_DOOR) + if (!map_bounds( i + k, j + l )) { - empty_count--; + --empty_count; + continue; } + + if (grid_is_solid( grd[i + k][j + l] ) + && grd[i + k][j + l] != DNGN_CLOSED_DOOR) + empty_count--; } } } - if (empty_count > 0) - env.map[i][j] = mapch(grd[i + 1][j + 1]); - } + if (empty_count > 0) + { + if (!get_envmap_char(i, j)) + set_envmap_char(i, j, get_magicmap_char(grd[i][j])); + + // Hack to give demonspawn Pandemonium mutation the ability + // to detect exits magically. + if ((you.mutation[MUT_PANDEMONIUM] > 1 + && grd[i][j] == DNGN_EXIT_PANDEMONIUM) + // Wizmode + || map_radius == 1000) + set_terrain_seen( i, j ); + else + set_terrain_mapped( i, j ); + } + } + } +} // end magic_mapping() + +// realize that this is simply a repackaged version of +// stuff::see_grid() -- make certain they correlate {dlb}: +bool mons_near(struct monsters *monster, unsigned int foe) +{ + // early out -- no foe! + if (foe == MHITNOT) + return (false); + + if (foe == MHITYOU) + { + if (monster->x > you.x_pos - 9 && monster->x < you.x_pos + 9 + && monster->y > you.y_pos - 9 && monster->y < you.y_pos + 9) + { + if (env.show[monster->x - you.x_pos + 9][monster->y - you.y_pos + 9]) + return (true); + } + return (false); + } + + // must be a monster + struct monsters *myFoe = &menv[foe]; + if (myFoe->type >= 0) + { + if (monster->x > myFoe->x - 9 && monster->x < myFoe->x + 9 + && monster->y > myFoe->y - 9 && monster->y < myFoe->y + 9) + { + return (true); + } + } + + return (false); +} // end mons_near() + +// answers the question: "Is a grid within character's line of sight?" +bool see_grid( int grx, int gry ) +{ + // rare case: can player see self? (of course!) + if (grx == you.x_pos && gry == you.y_pos) + return (true); + + // check env.show array + if (grid_distance( grx, gry, you.x_pos, you.y_pos ) < 9) + { + const int ex = grx - you.x_pos + 9; + const int ey = gry - you.y_pos + 9; + + if (env.show[ex][ey]) + return (true); } -} // end magic_mapping() + return (false); +} // end see_grid() -/* mapchars 3 & 4 are for non-ibm char sets */ -unsigned char mapchar(unsigned char ldfk) +static const unsigned char table[ NUM_CSET ][ NUM_DCHAR_TYPES ] = { - unsigned char showed = 0; + // CSET_ASCII + { + '#', '*', '.', ',', '\'', '+', '^', '>', '<', // wall, stairs up + '_', '\\', '}', '{', '8', '~', '~', // altar, item detect + '0', ')', '[', '/', '%', '?', '=', '!', '(', // orb, missile + ':', '|', '}', '%', '$', '"', '#', // book, cloud + }, - switch (ldfk) + // CSET_IBM - this is ANSI 437 { - case DNGN_UNSEEN: - showed = 0; - break; + 177, 176, 249, 250, '\'', 254, '^', '>', '<', // wall, stairs up + 220, 239, 244, 247, '8', '~', '~', // altar, item detect + '0', ')', '[', '/', '%', '?', '=', '!', '(', // orb, missile + '+', '\\', '}', '%', '$', '"', '#', // book, cloud + }, - case DNGN_SECRET_DOOR: - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - case DNGN_STONE_WALL: - case DNGN_METAL_WALL: - case DNGN_GREEN_CRYSTAL_WALL: - case DNGN_WAX_WALL: - showed = 176; - break; + // CSET_DEC - remember: 224-255 are mapped to shifted 96-127 + { + 225, 224, 254, ':', '\'', 238, '^', '>', '<', // wall, stairs up + 251, 182, 167, 187, '8', 171, 168, // altar, item detect + '0', ')', '[', '/', '%', '?', '=', '!', '(', // orb, missile + '+', '\\', '}', '%', '$', '"', '#', // book, cloud + }, +}; - case DNGN_CLOSED_DOOR: - showed = 206; - break; +static unsigned char cset_override[NUM_CSET][NUM_DCHAR_TYPES]; - case 20: // orcish idol - case 24: // ??? - case 25: // ??? - case DNGN_SILVER_STATUE: - case DNGN_GRANITE_STATUE: - case DNGN_ORANGE_CRYSTAL_STATUE: - showed = '8'; - break; +dungeon_char_type dchar_by_name(const std::string &name) +{ + const char *dchar_names[] = + { + "wall", "wall_magic", "floor", "floor_magic", "door_open", + "door_closed", "trap", "stairs_down", "stairs_up", "altar", "arch", + "fountain", "wavy", "statue", "invis_exposed", "item_detected", + "item_orb", "item_weapon", "item_armour", "item_wand", "item_food", + "item_scroll", "item_ring", "item_potion", "item_missile", "item_book", + "item_stave", "item_miscellany", "item_corpse", "item_gold", + "item_amulet", "cloud" + }; + for (unsigned i = 0; i < sizeof(dchar_names) / sizeof(*dchar_names); ++i) + { + if (dchar_names[i] == name) + return dungeon_char_type(i); + } + return (NUM_DCHAR_TYPES); +} - case DNGN_LAVA_X: - case DNGN_WATER_X: - case DNGN_LAVA: - case DNGN_DEEP_WATER: - case DNGN_SHALLOW_WATER: - showed = 247; - break; +void clear_cset_overrides() +{ + memset(cset_override, 0, sizeof cset_override); +} - case DNGN_FLOOR: - case DNGN_UNDISCOVERED_TRAP: - showed = 250; - break; +static unsigned short read_symbol(std::string s) +{ + if (s.empty()) + return (0); + if (s.length() == 1) + return s[0]; - //case 68: showed = '>'; break; // < (60) + if (s[0] == '\\') + s = s.substr(1); + + int feat = atoi(s.c_str()); + if (feat < 0) + feat = 0; + return static_cast(feat); +} - case DNGN_OPEN_DOOR: - showed = 39; - break; +void add_cset_override(char_set_type set, dungeon_char_type dc, + unsigned char symbol) +{ + cset_override[set][dc] = symbol; +} - //case 72: showed = '<'; break; +void add_cset_override(char_set_type set, const std::string &overrides) +{ + std::vector overs = split_string(",", overrides); + for (int i = 0, size = overs.size(); i < size; ++i) + { + std::vector mapping = split_string(":", overs[i]); + if (mapping.size() != 2) + continue; + + dungeon_char_type dc = dchar_by_name(mapping[0]); + if (dc == NUM_DCHAR_TYPES) + continue; + + unsigned char symbol = + static_cast(read_symbol(mapping[1])); - case DNGN_TRAP_MECHANICAL: - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - showed = '^'; - break; + if (set == NUM_CSET) + for (int c = 0; c < NUM_CSET; ++c) + add_cset_override(char_set_type(c), dc, symbol); + else + add_cset_override(set, dc, symbol); + } +} - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - showed = '>'; - break; +void init_char_table( char_set_type set ) +{ + for (int i = 0; i < NUM_DCHAR_TYPES; i++) + { + if (cset_override[set][i]) + Options.char_table[i] = cset_override[set][i]; + else + Options.char_table[i] = table[set][i]; + } +} - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - showed = '<'; - break; +void clear_feature_overrides() +{ + Feature_Overrides.clear(); +} - case DNGN_ENTER_HELL: - case DNGN_ENTER_LABYRINTH: - case DNGN_ENTER_SHOP: - case DNGN_ENTER_DIS: - case DNGN_ENTER_GEHENNA: - case DNGN_ENTER_COCYTUS: - case DNGN_ENTER_TARTARUS: - case DNGN_ENTER_ABYSS: - case DNGN_EXIT_ABYSS: - case DNGN_STONE_ARCH: - case DNGN_ENTER_PANDEMONIUM: - case DNGN_EXIT_PANDEMONIUM: - case DNGN_TRANSIT_PANDEMONIUM: - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - showed = 239; - break; +void add_feature_override(const std::string &text) +{ + std::string::size_type epos = text.rfind("}"); + if (epos == std::string::npos) + return; - case DNGN_ALTAR_ZIN: - case DNGN_ALTAR_SHINING_ONE: - case DNGN_ALTAR_KIKUBAAQUDGHA: - case DNGN_ALTAR_YREDELEMNUL: - case DNGN_ALTAR_XOM: - case DNGN_ALTAR_VEHUMET: - case DNGN_ALTAR_OKAWARU: - case DNGN_ALTAR_MAKHLEB: - case DNGN_ALTAR_SIF_MUNA: - case DNGN_ALTAR_TROG: - case DNGN_ALTAR_NEMELEX_XOBEH: - case DNGN_ALTAR_ELYVILON: - showed = 220; - break; + std::string::size_type spos = text.rfind("{", epos); + if (spos == std::string::npos) + return; + + std::string fname = text.substr(0, spos); + std::string props = text.substr(spos + 1, epos - spos - 1); + std::vector iprops = split_string(",", props, true, true); - case DNGN_BLUE_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_I: - case DNGN_SPARKLING_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_DRY_FOUNTAIN_III: - case DNGN_DRY_FOUNTAIN_IV: - case DNGN_DRY_FOUNTAIN_V: - case DNGN_DRY_FOUNTAIN_VI: - case DNGN_DRY_FOUNTAIN_VII: - case DNGN_DRY_FOUNTAIN_VIII: - case DNGN_PERMADRY_FOUNTAIN: - showed = 159; - break; + if (iprops.size() < 1 || iprops.size() > 5) + return; - default: - showed = 0; - break; - } + if (iprops.size() < 5) + iprops.resize(5); - return showed; -} + trim_string(fname); + std::vector feats = features_by_desc(fname); + if (feats.empty()) + return; + + for (int i = 0, size = feats.size(); i < size; ++i) + { + feature_override fov; + fov.feat = feats[i]; + + fov.override.symbol = read_symbol(iprops[0]); + fov.override.magic_symbol = read_symbol(iprops[1]); + fov.override.colour = str_to_colour(iprops[2], BLACK); + fov.override.map_colour = str_to_colour(iprops[3], BLACK); + fov.override.seen_colour = str_to_colour(iprops[4], BLACK); + Feature_Overrides.push_back(fov); + } +} -unsigned char mapchar2(unsigned char ldfk) +void apply_feature_overrides() { - unsigned char showed = 0; + for (int i = 0, size = Feature_Overrides.size(); i < size; ++i) + { + const feature_override &fov = Feature_Overrides[i]; + const feature_def &ofeat = fov.override; + feature_def &feat = Feature[fov.feat]; + + if (ofeat.symbol) + feat.symbol = ofeat.symbol; + if (ofeat.magic_symbol) + feat.magic_symbol = ofeat.magic_symbol; + if (ofeat.colour) + feat.colour = ofeat.colour; + if (ofeat.map_colour) + feat.map_colour = ofeat.map_colour; + if (ofeat.seen_colour) + feat.seen_colour = ofeat.seen_colour; + } +} - switch (ldfk) +void init_feature_table( void ) +{ + for (int i = 0; i < NUM_FEATURES; i++) { - case DNGN_UNSEEN: - showed = 0; - break; + Feature[i].symbol = 0; + Feature[i].colour = BLACK; // means must be set some other way + Feature[i].notable = false; + Feature[i].seen_effect = false; + Feature[i].magic_symbol = 0; // made equal to symbol if untouched + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = BLACK; // marks no special seen map handling + + switch (i) + { + case DNGN_UNSEEN: + default: + break; - case DNGN_SECRET_DOOR: - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - case DNGN_STONE_WALL: - case DNGN_METAL_WALL: - case DNGN_GREEN_CRYSTAL_WALL: - case DNGN_WAX_WALL: - showed = 177; - break; + case DNGN_ROCK_WALL: + case DNGN_PERMAROCK_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = EC_ROCK; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - case DNGN_CLOSED_DOOR: - showed = 254; - break; + case DNGN_STONE_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = EC_STONE; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - //case DNGN_LAVA_X: showed = 247; break; // deprecated? {dlb} - //case DNGN_WATER_X: showed = 247; break; // deprecated? {dlb} + case DNGN_OPEN_DOOR: + Feature[i].symbol = Options.char_table[ DCHAR_DOOR_OPEN ]; + Feature[i].colour = LIGHTGREY; + break; - case 20: // orcish idol - case 24: // ??? - case 25: // ??? - case DNGN_SILVER_STATUE: - case DNGN_GRANITE_STATUE: - case DNGN_ORANGE_CRYSTAL_STATUE: - showed = '8'; - break; + case DNGN_CLOSED_DOOR: + Feature[i].symbol = Options.char_table[ DCHAR_DOOR_CLOSED ]; + Feature[i].colour = LIGHTGREY; + break; - case DNGN_LAVA: - case DNGN_DEEP_WATER: - case DNGN_SHALLOW_WATER: - showed = 247; - break; + case DNGN_METAL_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = CYAN; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - case DNGN_FLOOR: - case DNGN_UNDISCOVERED_TRAP: - showed = 249; - break; + case DNGN_SECRET_DOOR: + // Note: get_secret_door_appearance means this probably isn't used + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = EC_ROCK; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - case 68: - showed = '>'; - break; // < + case DNGN_GREEN_CRYSTAL_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = GREEN; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; - case DNGN_OPEN_DOOR: - showed = 39; - break; + case DNGN_ORCISH_IDOL: + Feature[i].symbol = Options.char_table[ DCHAR_STATUE ]; + Feature[i].colour = DARKGREY; + break; - case 72: - showed = '<'; - break; // < + case DNGN_WAX_WALL: + Feature[i].symbol = Options.char_table[ DCHAR_WALL ]; + Feature[i].colour = YELLOW; + Feature[i].magic_symbol = Options.char_table[ DCHAR_WALL_MAGIC ]; + break; // wax wall - case DNGN_TRAP_MECHANICAL: - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - showed = '^'; - break; + case DNGN_SILVER_STATUE: + Feature[i].symbol = Options.char_table[ DCHAR_STATUE ]; + Feature[i].colour = WHITE; + Feature[i].seen_effect = true; + break; - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - showed = '>'; - break; + case DNGN_GRANITE_STATUE: + Feature[i].symbol = Options.char_table[ DCHAR_STATUE ]; + Feature[i].colour = LIGHTGREY; + break; - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - showed = '<'; - break; + case DNGN_ORANGE_CRYSTAL_STATUE: + Feature[i].symbol = Options.char_table[ DCHAR_STATUE ]; + Feature[i].colour = LIGHTRED; + Feature[i].seen_effect = true; + break; - case DNGN_ENTER_HELL: - case DNGN_ENTER_LABYRINTH: - case DNGN_ENTER_SHOP: - case DNGN_ENTER_DIS: - case DNGN_ENTER_GEHENNA: - case DNGN_ENTER_COCYTUS: - case DNGN_ENTER_TARTARUS: - case DNGN_ENTER_ABYSS: - case DNGN_EXIT_ABYSS: - case DNGN_STONE_ARCH: - case DNGN_ENTER_PANDEMONIUM: - case DNGN_EXIT_PANDEMONIUM: - case DNGN_TRANSIT_PANDEMONIUM: - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - showed = 239; - break; + case DNGN_LAVA: + Feature[i].symbol = Options.char_table[ DCHAR_WAVY ]; + Feature[i].colour = RED; + break; - case DNGN_ALTAR_ZIN: - case DNGN_ALTAR_SHINING_ONE: - case DNGN_ALTAR_KIKUBAAQUDGHA: - case DNGN_ALTAR_YREDELEMNUL: - case DNGN_ALTAR_XOM: - case DNGN_ALTAR_VEHUMET: - case DNGN_ALTAR_OKAWARU: - case DNGN_ALTAR_MAKHLEB: - case DNGN_ALTAR_SIF_MUNA: - case DNGN_ALTAR_TROG: - case DNGN_ALTAR_NEMELEX_XOBEH: - case DNGN_ALTAR_ELYVILON: - showed = 220; - break; + case DNGN_DEEP_WATER: + Feature[i].symbol = Options.char_table[ DCHAR_WAVY ]; + Feature[i].colour = BLUE; + break; - case DNGN_BLUE_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_I: - case DNGN_SPARKLING_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_DRY_FOUNTAIN_III: - case DNGN_DRY_FOUNTAIN_IV: - case DNGN_DRY_FOUNTAIN_V: - case DNGN_DRY_FOUNTAIN_VI: - case DNGN_DRY_FOUNTAIN_VII: - case DNGN_DRY_FOUNTAIN_VIII: - case DNGN_PERMADRY_FOUNTAIN: - showed = 159; - break; - default: - showed = 0; - break; - } + case DNGN_SHALLOW_WATER: + Feature[i].symbol = Options.char_table[ DCHAR_WAVY ]; + Feature[i].colour = CYAN; + break; - return showed; -} + case DNGN_FLOOR: + Feature[i].symbol = Options.char_table[ DCHAR_FLOOR ]; + Feature[i].colour = EC_FLOOR; + Feature[i].magic_symbol = Options.char_table[ DCHAR_FLOOR_MAGIC ]; + break; + case DNGN_EXIT_HELL: + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].colour = LIGHTRED; + Feature[i].notable = false; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTRED; + break; -// realize that this is simply a repackaged version of -// stuff::see_grid() -- make certain they correlate {dlb}: -bool mons_near(struct monsters *monster, unsigned int foe) -{ - // early out -- no foe! - if (foe == MHITNOT) - return (false); + case DNGN_ENTER_HELL: + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].colour = RED; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = RED; + break; - if (foe == MHITYOU) - { - if (monster->x > you.x_pos - 9 && monster->x < you.x_pos + 9 - && monster->y > you.y_pos - 9 && monster->y < you.y_pos + 9) - { - if (env.show[monster->x - you.x_pos + 9][monster->y - you.y_pos + 9]) - return (true); - } - return (false); - } + case DNGN_TRAP_MECHANICAL: + Feature[i].colour = LIGHTCYAN; + Feature[i].symbol = Options.char_table[ DCHAR_TRAP ]; + Feature[i].map_colour = LIGHTCYAN; + break; - // must be a monster - struct monsters *myFoe = &menv[foe]; - if (myFoe->type >= 0) - { - if (monster->x > myFoe->x - 9 && monster->x < myFoe->x + 9 - && monster->y > myFoe->y - 9 && monster->y < myFoe->y + 9) - { - return (true); - } - } + case DNGN_TRAP_MAGICAL: + Feature[i].colour = MAGENTA; + Feature[i].symbol = Options.char_table[ DCHAR_TRAP ]; + Feature[i].map_colour = MAGENTA; + break; - return (false); -} // end mons_near() + case DNGN_TRAP_III: + Feature[i].colour = LIGHTGREY; + Feature[i].symbol = Options.char_table[ DCHAR_TRAP ]; + Feature[i].map_colour = LIGHTGREY; + break; + case DNGN_UNDISCOVERED_TRAP: + Feature[i].symbol = Options.char_table[ DCHAR_FLOOR ]; + Feature[i].colour = EC_FLOOR; + Feature[i].magic_symbol = Options.char_table[ DCHAR_FLOOR_MAGIC ]; + break; -//--------------------------------------------------------------- -// -// get_non_ibm_symbol -// -// Returns the character code and color for everything drawn -// without the IBM graphics option. -// -//--------------------------------------------------------------- -void get_non_ibm_symbol(unsigned int object, unsigned short *ch, - unsigned short *color) -{ - ASSERT(color != NULL); - ASSERT(ch != NULL); + case DNGN_ENTER_SHOP: + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].colour = YELLOW; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = YELLOW; + break; - switch (object) - { + case DNGN_ENTER_LABYRINTH: + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].colour = CYAN; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = CYAN; + break; - case DNGN_UNSEEN: - *ch = 0; - break; + case DNGN_ROCK_STAIRS_DOWN: + case DNGN_STONE_STAIRS_DOWN_I: + case DNGN_STONE_STAIRS_DOWN_II: + case DNGN_STONE_STAIRS_DOWN_III: + Feature[i].symbol = Options.char_table[ DCHAR_STAIRS_DOWN ]; + Feature[i].colour = ((i == DNGN_ROCK_STAIRS_DOWN) ? BROWN + : LIGHTGREY); + Feature[i].map_colour = RED; + break; - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - *color = env.rock_colour; - *ch = '#'; - break; + case DNGN_ROCK_STAIRS_UP: + case DNGN_STONE_STAIRS_UP_I: + case DNGN_STONE_STAIRS_UP_II: + case DNGN_STONE_STAIRS_UP_III: + Feature[i].symbol = Options.char_table[ DCHAR_STAIRS_UP ]; + Feature[i].colour = ((i == DNGN_ROCK_STAIRS_UP) ? BROWN + : LIGHTGREY); + Feature[i].map_colour = GREEN; + break; - case DNGN_STONE_WALL: - if (player_in_branch( BRANCH_HALL_OF_ZOT )) - *color = env.rock_colour; - else - *color = LIGHTGREY; - *ch = '#'; - break; + case DNGN_ENTER_DIS: + Feature[i].colour = CYAN; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = CYAN; + break; - case DNGN_CLOSED_DOOR: - *ch = '+'; - break; + case DNGN_ENTER_GEHENNA: + Feature[i].colour = RED; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = RED; + break; - case DNGN_METAL_WALL: - *ch = '#'; - *color = CYAN; - break; + case DNGN_ENTER_COCYTUS: + Feature[i].colour = LIGHTCYAN; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTCYAN; + break; - case DNGN_SECRET_DOOR: - *ch = '#'; - *color = env.rock_colour; - break; + case DNGN_ENTER_TARTARUS: + Feature[i].colour = DARKGREY; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = DARKGREY; + break; - case DNGN_GREEN_CRYSTAL_WALL: - *ch = '#'; - *color = GREEN; - break; + case DNGN_ENTER_ABYSS: + Feature[i].colour = EC_RANDOM; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = EC_RANDOM; + break; - case DNGN_ORCISH_IDOL: - *ch = '8'; - *color = DARKGREY; - break; + case DNGN_EXIT_ABYSS: + Feature[i].colour = EC_RANDOM; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = EC_RANDOM; + break; - case DNGN_WAX_WALL: - *ch = '#'; - *color = YELLOW; - break; + case DNGN_STONE_ARCH: + Feature[i].colour = LIGHTGREY; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = LIGHTGREY; + break; - case DNGN_SILVER_STATUE: - *ch = '8'; - *color = WHITE; - Visible_Statue[ STATUE_SILVER ] = 1; - break; + case DNGN_ENTER_PANDEMONIUM: + Feature[i].colour = LIGHTBLUE; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTBLUE; + break; - case DNGN_GRANITE_STATUE: - *ch = '8'; - *color = LIGHTGREY; - break; + case DNGN_EXIT_PANDEMONIUM: + // Note: has special handling for colouring with mutation + Feature[i].colour = LIGHTBLUE; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTBLUE; + break; - case DNGN_ORANGE_CRYSTAL_STATUE: - *ch = '8'; - *color = LIGHTRED; - Visible_Statue[ STATUE_ORANGE_CRYSTAL ] = 1; - break; + case DNGN_TRANSIT_PANDEMONIUM: + Feature[i].colour = LIGHTGREEN; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = LIGHTGREEN; + break; - case DNGN_LAVA: - *ch = '{'; - *color = RED; - break; + case DNGN_ENTER_ORCISH_MINES: + case DNGN_ENTER_HIVE: + case DNGN_ENTER_LAIR: + case DNGN_ENTER_SLIME_PITS: + case DNGN_ENTER_VAULTS: + case DNGN_ENTER_CRYPT: + case DNGN_ENTER_HALL_OF_BLADES: + case DNGN_ENTER_TEMPLE: + case DNGN_ENTER_SNAKE_PIT: + case DNGN_ENTER_ELVEN_HALLS: + case DNGN_ENTER_TOMB: + case DNGN_ENTER_SWAMP: + case DNGN_ENTER_RESERVED_1: + case DNGN_ENTER_RESERVED_2: + case DNGN_ENTER_RESERVED_3: + case DNGN_ENTER_RESERVED_4: + Feature[i].colour = YELLOW; + Feature[i].symbol = Options.char_table[ DCHAR_STAIRS_DOWN ]; + Feature[i].notable = true; + Feature[i].map_colour = RED; + Feature[i].seen_colour = LIGHTRED; + break; - case DNGN_DEEP_WATER: - *ch = '{'; - // this wavy thing also used for water elemental - // note that some monsters which use IBM graphics aren't set - // for this function - too tricky for now. - *color = BLUE; - break; + case DNGN_ENTER_ZOT: + Feature[i].colour = MAGENTA; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].notable = true; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = MAGENTA; + break; - case DNGN_SHALLOW_WATER: - *color = CYAN; - *ch = '{'; - break; + case DNGN_RETURN_FROM_ORCISH_MINES: + case DNGN_RETURN_FROM_HIVE: + case DNGN_RETURN_FROM_LAIR: + case DNGN_RETURN_FROM_SLIME_PITS: + case DNGN_RETURN_FROM_VAULTS: + case DNGN_RETURN_FROM_CRYPT: + case DNGN_RETURN_FROM_HALL_OF_BLADES: + case DNGN_RETURN_FROM_TEMPLE: + case DNGN_RETURN_FROM_SNAKE_PIT: + case DNGN_RETURN_FROM_ELVEN_HALLS: + case DNGN_RETURN_FROM_TOMB: + case DNGN_RETURN_FROM_SWAMP: + case DNGN_RETURN_RESERVED_1: + case DNGN_RETURN_RESERVED_2: + case DNGN_RETURN_RESERVED_3: + case DNGN_RETURN_RESERVED_4: + Feature[i].colour = YELLOW; + Feature[i].symbol = Options.char_table[ DCHAR_STAIRS_UP ]; + Feature[i].map_colour = BLUE; + Feature[i].seen_colour = LIGHTBLUE; + break; - case DNGN_FLOOR: - *color = env.floor_colour; - *ch = '.'; - break; + case DNGN_RETURN_FROM_ZOT: + Feature[i].colour = MAGENTA; + Feature[i].symbol = Options.char_table[ DCHAR_ARCH ]; + Feature[i].map_colour = LIGHTGREY; + Feature[i].seen_colour = MAGENTA; + break; - case DNGN_ENTER_HELL: - *color = RED; - *ch = '\\'; - seen_other_thing(DNGN_ENTER_HELL); - break; + case DNGN_ALTAR_ZIN: + Feature[i].colour = WHITE; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = WHITE; + break; - case DNGN_OPEN_DOOR: - *ch = '\''; - break; + case DNGN_ALTAR_SHINING_ONE: + Feature[i].colour = YELLOW; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = YELLOW; + break; - case DNGN_BRANCH_STAIRS: - *color = BROWN; - *ch = '>'; - break; + case DNGN_ALTAR_KIKUBAAQUDGHA: + Feature[i].colour = DARKGREY; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = DARKGREY; + break; - case DNGN_TRAP_MECHANICAL: - *color = 11; - *ch = '^'; - break; + case DNGN_ALTAR_YREDELEMNUL: + Feature[i].colour = EC_UNHOLY; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = EC_UNHOLY; + break; - case DNGN_TRAP_MAGICAL: - *color = MAGENTA; - *ch = '^'; - break; + case DNGN_ALTAR_XOM: + Feature[i].colour = EC_RANDOM; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = EC_RANDOM; + break; - case DNGN_TRAP_III: - *color = LIGHTGREY; - *ch = '^'; - break; + case DNGN_ALTAR_VEHUMET: + Feature[i].colour = EC_VEHUMET; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = EC_VEHUMET; + break; - case DNGN_UNDISCOVERED_TRAP: - *color = env.floor_colour; - *ch = '.'; - break; + case DNGN_ALTAR_OKAWARU: + Feature[i].colour = CYAN; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = CYAN; + break; - case DNGN_ENTER_SHOP: - *color = YELLOW; - *ch = '\\'; - seen_other_thing(DNGN_ENTER_SHOP); - break; -// if I change anything above here, must also change magic mapping! + case DNGN_ALTAR_MAKHLEB: + Feature[i].colour = EC_FIRE; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = EC_FIRE; + break; - case DNGN_ENTER_LABYRINTH: - *color = LIGHTGREY; - *ch = '\\'; - seen_other_thing(DNGN_ENTER_LABYRINTH); - break; + case DNGN_ALTAR_SIF_MUNA: + Feature[i].colour = BLUE; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = BLUE; + break; - case DNGN_ROCK_STAIRS_DOWN: - *color = BROWN; // ladder - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - *ch = '>'; - break; + case DNGN_ALTAR_TROG: + Feature[i].colour = RED; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = RED; + break; - case DNGN_ROCK_STAIRS_UP: - *color = BROWN; // ladder - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - *ch = '<'; - break; + case DNGN_ALTAR_NEMELEX_XOBEH: + Feature[i].colour = LIGHTMAGENTA; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = LIGHTMAGENTA; + break; - case DNGN_ENTER_DIS: - *color = CYAN; - *ch = '\\'; - break; + case DNGN_ALTAR_ELYVILON: + Feature[i].colour = LIGHTGREY; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = LIGHTGREY; + break; - case DNGN_ENTER_GEHENNA: - *color = RED; - *ch = '\\'; - break; + case DNGN_BLUE_FOUNTAIN: + Feature[i].colour = BLUE; + Feature[i].symbol = Options.char_table[ DCHAR_FOUNTAIN ]; + break; - case DNGN_ENTER_COCYTUS: - *color = LIGHTCYAN; - *ch = '\\'; - break; + case DNGN_SPARKLING_FOUNTAIN: + Feature[i].colour = LIGHTBLUE; + Feature[i].symbol = Options.char_table[ DCHAR_FOUNTAIN ]; + break; - case DNGN_ENTER_TARTARUS: - *color = DARKGREY; - *ch = '\\'; - break; + case DNGN_DRY_FOUNTAIN_I: + case DNGN_DRY_FOUNTAIN_II: + case DNGN_PERMADRY_FOUNTAIN: + Feature[i].colour = LIGHTGREY; + Feature[i].symbol = Options.char_table[ DCHAR_FOUNTAIN ]; + break; - case DNGN_ENTER_ABYSS: - *color = random2(16); - *ch = '\\'; - seen_other_thing(DNGN_ENTER_ABYSS); - break; + case DNGN_INVIS_EXPOSED: + Feature[i].symbol = Options.char_table[ DCHAR_INVIS_EXPOSED ]; + break; - case DNGN_EXIT_ABYSS: - *color = random2(16); - *ch = '\\'; - break; + case DNGN_ITEM_DETECTED: + Feature[i].magic_symbol = Options.char_table[ DCHAR_ITEM_DETECTED ]; + break; - case DNGN_STONE_ARCH: - *color = LIGHTGREY; - *ch = '\\'; - break; + case DNGN_ITEM_ORB: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_ORB ]; + break; - case DNGN_ENTER_PANDEMONIUM: - *color = LIGHTBLUE; - *ch = '\\'; - seen_other_thing(DNGN_ENTER_PANDEMONIUM); - break; + case DNGN_ITEM_WEAPON: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_WEAPON ]; + break; - case DNGN_EXIT_PANDEMONIUM: - *color = LIGHTBLUE; - *ch = '\\'; - break; + case DNGN_ITEM_ARMOUR: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_ARMOUR ]; + break; - case DNGN_TRANSIT_PANDEMONIUM: - *color = LIGHTGREEN; - *ch = '\\'; - break; // gate to other part of pandemonium - - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - *color = YELLOW; - *ch = '>'; - seen_staircase(object); - break; + case DNGN_ITEM_WAND: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_WAND ]; + break; - case DNGN_ENTER_ZOT: - *color = MAGENTA; - *ch = '\\'; - seen_staircase(object); - break; + case DNGN_ITEM_FOOD: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_FOOD ]; + break; - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - *color = YELLOW; - *ch = '<'; - break; + case DNGN_ITEM_SCROLL: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_SCROLL ]; + break; - case DNGN_RETURN_FROM_ZOT: - *color = MAGENTA; - *ch = '\\'; - break; + case DNGN_ITEM_RING: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_RING ]; + break; - case DNGN_ALTAR_ZIN: - *color = WHITE; - *ch = '_'; - seen_altar(GOD_ZIN); - break; + case DNGN_ITEM_POTION: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_POTION ]; + break; - case DNGN_ALTAR_SHINING_ONE: - *color = YELLOW; - *ch = '_'; - seen_altar(GOD_SHINING_ONE); - break; + case DNGN_ITEM_MISSILE: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_MISSILE ]; + break; - case DNGN_ALTAR_KIKUBAAQUDGHA: - *color = DARKGREY; - *ch = '_'; - seen_altar(GOD_KIKUBAAQUDGHA); - break; + case DNGN_ITEM_BOOK: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_BOOK ]; + break; - case DNGN_ALTAR_YREDELEMNUL: - *color = DARKGREY; - if (one_chance_in(3)) - *color = RED; - *ch = '_'; - seen_altar(GOD_YREDELEMNUL); - break; + case DNGN_ITEM_STAVE: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_STAVE ]; + break; - case DNGN_ALTAR_XOM: - *color = random_colour(); - *ch = '_'; - seen_altar(GOD_XOM); - break; + case DNGN_ITEM_MISCELLANY: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_MISCELLANY ]; + break; - case DNGN_ALTAR_VEHUMET: - *color = LIGHTBLUE; - if (one_chance_in(3)) - *color = LIGHTMAGENTA; - if (one_chance_in(3)) - *color = LIGHTRED; - *ch = '_'; - seen_altar(GOD_VEHUMET); - break; + case DNGN_ITEM_CORPSE: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_CORPSE ]; + break; - case DNGN_ALTAR_OKAWARU: - *color = CYAN; - *ch = '_'; - seen_altar(GOD_OKAWARU); - break; + case DNGN_ITEM_GOLD: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_GOLD ]; + break; - case DNGN_ALTAR_MAKHLEB: - *color = RED; - if (one_chance_in(3)) - *color = LIGHTRED; - if (one_chance_in(3)) - *color = YELLOW; - *ch = '_'; - seen_altar(GOD_MAKHLEB); - break; + case DNGN_ITEM_AMULET: + Feature[i].symbol = Options.char_table[ DCHAR_ITEM_AMULET ]; + break; - case DNGN_ALTAR_SIF_MUNA: - *color = BLUE; - *ch = '_'; - seen_altar(GOD_SIF_MUNA); - break; + case DNGN_CLOUD: + Feature[i].symbol = Options.char_table[ DCHAR_CLOUD ]; + break; + } + } - case DNGN_ALTAR_TROG: - *color = RED; - *ch = '_'; - seen_altar(GOD_TROG); - break; + apply_feature_overrides(); - case DNGN_ALTAR_NEMELEX_XOBEH: - *color = LIGHTMAGENTA; - *ch = '_'; - seen_altar(GOD_NEMELEX_XOBEH); - break; + for (int i = 0; i < NUM_FEATURES; ++i) + { + if (!Feature[i].magic_symbol) + Feature[i].magic_symbol = Feature[i].symbol; - case DNGN_ALTAR_ELYVILON: - *color = LIGHTGREY; - *ch = '_'; - seen_altar(GOD_ELYVILON); - break; + if (Feature[i].seen_colour == BLACK) + Feature[i].seen_colour = Feature[i].map_colour; + } +} - case DNGN_BLUE_FOUNTAIN: - *color = BLUE; - *ch = '}'; - break; +static int get_screen_glyph( int x, int y ) +{ + const int ex = x - you.x_pos + 9; + const int ey = y - you.y_pos + 9; - case DNGN_SPARKLING_FOUNTAIN: - *color = LIGHTBLUE; - *ch = '}'; - break; + int object = env.show[ex][ey]; + unsigned short colour = env.show_col[ex][ey]; + unsigned short ch; - case DNGN_DRY_FOUNTAIN_I: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_PERMADRY_FOUNTAIN: - *color = LIGHTGREY; - *ch = '}'; - break; + if (!object) + return get_envmap_char(x, y); - case 256: - *ch = '0'; - break; + if (object == DNGN_SECRET_DOOR) + object = grid_secret_door_appearance( x, y ); - case 257: - *color = CYAN; - *ch = '~'; - break; /* Invis creature walking through water */ + get_symbol( object, &ch, &colour ); + return (ch); +} - case 258: - *ch = ')'; - break; // weapon ) +// Returns a string containing an ASCII representation of the map. If fullscreen +// is set to false, only the viewable area is returned. Leading and trailing +// spaces are trimmed from each line. Leading and trailing empty lines are also +// snipped. +std::string screenshot( bool fullscreen ) +{ + UNUSED( fullscreen ); - case 259: - *ch = '['; - break; // armour [ + const int X_SIZE = VIEW_WIDTH; + const int Y_SIZE = VIEW_HEIGHT; - case 260: - *ch = '/'; - break; // wands, etc. + // [ds] Screenshots need to be straight ASCII. We will now proceed to force + // the char and feature tables back to ASCII. + FixedVector char_table_bk; + char_table_bk = Options.char_table; - case 261: - *ch = '%'; - break; // food + init_char_table(CSET_ASCII); + init_feature_table(); + + int firstnonspace = -1; + int firstpopline = -1; + int lastpopline = -1; - case 262: - *ch = '+'; - break; // books + + char lines[Y_SIZE][X_SIZE + 1]; + for (int count_y = 0; count_y < Y_SIZE; count_y++) + { + int lastnonspace = -1; + + for (int count_x = 0; count_x < X_SIZE; count_x++) + { + // in grid coords + const int gx = count_x + you.x_pos - 16; + const int gy = count_y + you.y_pos - 8; + + int ch = (!map_bounds(gx, gy)) + ? 0 + : (count_x < 8 || count_x > 24) + ? get_envmap_char(gx, gy) + : (gx == you.x_pos && gy == you.y_pos) + ? you.symbol + : get_screen_glyph(gx, gy); + + if (ch && !isprint(ch)) + { + // [ds] Evil hack time again. Peek at grid, use that character. + int object = grd[gx][gy]; + unsigned short glych, glycol; - case 263: - *ch = '?'; - break; // scroll ? + if (object == DNGN_SECRET_DOOR) + object = grid_secret_door_appearance( gx, gy ); - case 264: - *ch = '='; - break; // ring = etc + get_symbol( object, &glych, &glycol ); + ch = glych; + } + + // More mangling to accommodate C strings. + if (!ch) + ch = ' '; - case 265: - *ch = '!'; - break; // potions ! + if (ch != ' ') + { + lastnonspace = count_x; + lastpopline = count_y; - case 266: - *ch = '('; - break; // stones + if (firstnonspace == -1 || firstnonspace > count_x) + firstnonspace = count_x; - case 267: - *ch = ':'; - break; // book + + if (firstpopline == -1) + firstpopline = count_y; + } - case 268: - *ch = '%'; - break; // corpses part 1 + lines[count_y][count_x] = ch; + } - case 269: - *ch = '|'; - break; // magical staves + lines[count_y][lastnonspace + 1] = 0; + } - case 270: - *ch = '}'; - break; // gems + // Restore char and feature tables + Options.char_table = char_table_bk; + init_feature_table(); - case 271: - *ch = '%'; - break; // don't know ? + std::string ss; + if (firstpopline != -1 && lastpopline != -1) + { + if (firstnonspace == -1) + firstnonspace = 0; - case 272: - *ch = '$'; - *color = YELLOW; - break; // $ gold + for (int i = firstpopline; i <= lastpopline; ++i) + { + char *curr = lines[i]; - case 273: - *ch = '"'; - break; // amulet + while (*curr && curr - lines[i] < firstnonspace) + curr++; - default: - int mnr = object; - *ch = ((mnr >= 297) ? mons_char(mnr - 297) : object); // yeah - break; + ss += curr; + ss += EOL; + } } -} + return (ss); +} -/* - This is the viewwindow function for computers without IBM graphic displays. - It is activated by a command line argument, which sets a function pointer. - */ -void viewwindow3(char draw_it, bool do_updates) +static int viewmap_flash_colour() { - int bufcount = 0; - FixedVector < unsigned short, 1500 > buffy; //[800]; //392]; + if (you.special_wield == SPWLD_SHADOW) + return (DARKGREY); + else if (you.berserker) + return (RED); + + return (BLACK); +} - unsigned short ch, color; +//--------------------------------------------------------------- +// +// viewwindow -- now unified and rolled into a single pass +// +// Draws the main window using the character set returned +// by get_symbol(). +// +// This function should not interfere with the game condition, +// unless do_updates is set (ie. stealth checks for visible +// monsters). +// +//--------------------------------------------------------------- +void viewwindow(bool draw_it, bool do_updates) +{ + const int X_SIZE = VIEW_WIDTH; + const int Y_SIZE = VIEW_HEIGHT; + const int BUFFER_SIZE = 1550; + FixedVector < screen_buffer_t, BUFFER_SIZE > buffy; int count_x, count_y; - losight(env.show, grd, you.x_pos, you.y_pos); + losight( env.show, grd, you.x_pos, you.y_pos ); // must be done first + + for (count_x = 0; count_x < NUM_STATUE_TYPES; count_x++) + you.visible_statue[count_x] = 0; for (count_x = 0; count_x < 18; count_x++) { for (count_y = 0; count_y < 18; count_y++) { env.show_col[count_x][count_y] = LIGHTGREY; - show_backup[count_x][count_y] = 0; + Show_Backup[count_x][count_y] = 0; } } - item(); + item_grid(); // must be done before cloud and monster cloud_grid(); - monster_grid(do_updates); - bufcount = 0; + monster_grid( do_updates ); - if (draw_it == 1) + if (draw_it) { _setcursortype(_NOCURSOR); - for (count_y = (you.y_pos - 8); (count_y < you.y_pos + 9); count_y++) - { - bufcount += 16; - for (count_x = (you.x_pos - 8); (count_x < you.x_pos + 9); count_x++) + const bool map = player_in_mappable_area(); + int bufcount = 0; + + int flash_colour = you.flash_colour; + if (flash_colour == BLACK) + flash_colour = viewmap_flash_colour(); + + for (count_y = 0; count_y < Y_SIZE; count_y++) + { + for (count_x = 0; count_x < X_SIZE; count_x++) { - color = env.show_col[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9]; + // in grid coords + const int gx = count_x + you.x_pos - 16; + const int gy = count_y + you.y_pos - 8; - if (count_x == you.x_pos && count_y == you.y_pos) + // order is important here + if (!map_bounds( gx, gy )) + { + // off the map + buffy[bufcount] = 0; + buffy[bufcount + 1] = DARKGREY; + } + else if (count_x < 8 || count_x > 24) { - ch = your_sign; + // outside the env.show area + buffy[bufcount] = get_envmap_char( gx, gy ); + buffy[bufcount + 1] = DARKGREY; + + if (Options.colour_map) + buffy[bufcount + 1] = + colour_code_map(gx - 1, gy - 1, + Options.item_colour); + } + else if (gx == you.x_pos && gy == you.y_pos) + { + // player overrides everything in cell + buffy[bufcount] = you.symbol; + buffy[bufcount + 1] = you.colour; if (player_is_swimming()) { - color = (grd[you.x_pos][you.y_pos] == DNGN_DEEP_WATER) - ? BLUE : CYAN; - } - else - { - color = your_colour; + if (grd[gx][gy] == DNGN_DEEP_WATER) + buffy[bufcount + 1] = BLUE; + else + buffy[bufcount + 1] = CYAN; } } else { - unsigned int object = env.show[count_x - you.x_pos + 9] - [count_y - you.y_pos + 9]; + // Note: env.show is set for grids in LoS + // get env coords + const int ex = gx - you.x_pos + 9; + const int ey = gy - you.y_pos + 9; - get_non_ibm_symbol(object, &ch, &color); - } + int object = env.show[ex][ey]; + unsigned short colour = env.show_col[ex][ey]; + unsigned short ch; - buffy[bufcount] = ch; //showed; - buffy[bufcount + 1] = color; - bufcount += 2; - } + if (object == DNGN_SECRET_DOOR) + object = grid_secret_door_appearance( gx, gy ); - bufcount += 16; - } + get_symbol( object, &ch, &colour ); - bufcount = 0; + buffy[bufcount] = ch; + buffy[bufcount + 1] = colour; - if (you.level_type != LEVEL_LABYRINTH - && you.level_type != LEVEL_ABYSS) - { - for (count_y = 0; count_y < 17; count_y++) - { - bufcount += 16; - for (count_x = 0; count_x < 17; count_x++) - { - int enx = count_x + you.x_pos - 9, - eny = count_y + you.y_pos - 9; - if (buffy[bufcount] != 0 && enx >= 0 && eny >= 0 - && enx + 1 < GXM && eny + 1 < GYM) + if (map) { - unsigned short bch = buffy[bufcount]; - if (mgrd[enx + 1][eny + 1] != NON_MONSTER) { - const monsters &m = menv[ mgrd[enx + 1][eny + 1] ]; - if (!mons_is_mimic(m.type) - && mons_char(m.type) == bch) - { - bch |= mons_colour(m.type) << 12; - } + // This section is very tricky because it + // duplicates the old code (which was horrid). + + // if the grid is in LoS env.show was set and + // we set the buffer already, so... + if (buffy[bufcount] != 0) + { + // ... map that we've seen this + set_envmap_char( gx, gy, buffy[bufcount] ); + set_terrain_seen( gx, gy ); + set_envmap_detected_mons(gx, gy, false); + set_envmap_detected_item(gx, gy, false); } - env.map[enx][eny] = bch; - } - - if (Options.clean_map == 1 - && show_backup[count_x + 1][count_y + 1] != 0 - && enx >= 0 - && eny >= 0) - { - get_non_ibm_symbol( show_backup[count_x + 1] - [count_y + 1], - &ch, &color ); - env.map[enx][eny] = ch; - } - bufcount += 2; - } - bufcount += 16; - } - } - bufcount = 0; - - for (count_y = 0; count_y < 17; count_y++) - { - for (count_x = 0; count_x < 33; count_x++) - { - if (count_x + you.x_pos - 17 < 3 - || count_y + you.y_pos - 9 < 3 - || count_x + you.x_pos - 14 > (GXM - 3) - || count_y + you.y_pos - 9 > (GYM - 3)) - { - buffy[bufcount] = 0; - bufcount++; - buffy[bufcount] = 0; - bufcount++; - continue; - } + // Check if we're looking to clean_map... + // but don't touch the buffer to clean it, + // instead we modify the env.map itself so + // that the map stays clean as it moves out + // of the env.show radius. + // + // Note: show_backup is 0 on every square which + // is inside the env.show radius and doesn't + // have a monster or cloud on it, and is equal + // to the grid before monsters and clouds were + // added otherwise. + if (Options.clean_map + && Show_Backup[ex][ey] + && is_terrain_seen( gx, gy )) + { + get_symbol( Show_Backup[ex][ey], &ch, &colour ); + set_envmap_char( gx, gy, ch ); + } - if (count_x >= 8 && count_x <= 24 && count_y >= 0 - && count_y <= 16 && buffy[bufcount] != 0) - { - bufcount += 2; - continue; + // Now we get to filling in both the unseen + // grids in the env.show radius area as + // well doing the clean_map. The clean_map + // is done by having the env.map set to the + // backup character above, and down here we + // procede to override that character if it's + // out of LoS! If it wasn't, buffy would have + // already been set (but we'd still have + // clobbered env.map... which is important + // to do for when we move away from the area!) + if (buffy[bufcount] == 0) + { + // show map + buffy[bufcount] = get_envmap_char( gx, gy ); + buffy[bufcount + 1] = DARKGREY; + + if (Options.colour_map) + buffy[bufcount + 1] = + colour_code_map(gx - 1, gy - 1, + Options.item_colour); + } + } } - - buffy[bufcount] = (unsigned char) - env.map[count_x + you.x_pos - 17] - [count_y + you.y_pos - 9]; - - buffy[bufcount + 1] = DARKGREY; - - if (Options.colour_map) + + // alter colour if flashing the characters vision + if (flash_colour != BLACK + && buffy[bufcount + 1] != DARKGREY) { - if (env.map[count_x + you.x_pos - 17] - [count_y + you.y_pos - 9] != 0) - { - buffy[bufcount + 1] - = colour_code_map( count_x + you.x_pos - 17, - count_y + you.y_pos - 9, - Options.item_colour ); - } + buffy[bufcount + 1] = flash_colour; } bufcount += 2; } } - if (you.berserker) - { - for (count_x = 1; count_x < 1400; count_x += 2) - { - if (buffy[count_x] != DARKGREY) - buffy[count_x] = RED; - } - } - - if (show_green != BLACK) - { - for (count_x = 1; count_x < 1400; count_x += 2) - { - if (buffy[count_x] != DARKGREY) - buffy[count_x] = show_green; - } - - show_green = ((you.special_wield == SPWLD_SHADOW) ? DARKGREY - : BLACK); - } + // Leaving it this way because short flashes can occur in long ones, + // and this simply works without requiring a stack. + you.flash_colour = BLACK; #ifdef DOS_TERM - puttext(2, 1, 34, 17, buffy.buffer()); + puttext( 2, 1, X_SIZE + 1, Y_SIZE, buffy.buffer() ); #endif #ifdef PLAIN_TERM - gotoxy(2, 1); - bufcount = 0; - - // this line is purely optional - if (you.running == 0 || (you.running < 0 && Options.travel_delay > -1)) + // avoiding unneeded draws when running + if (!you.running || (you.running < 0 && Options.travel_delay > -1)) { - for (count_x = 0; count_x < 1120; count_x += 2) // 1056 - { - textcolor(buffy[count_x + 1]); - putch(buffy[count_x]); + gotoxy( 2, 1 ); - if (count_x % 66 == 64 && count_x > 0) -#ifdef DOS_TERM - cprintf(EOL " "); + bufcount = 0; + for (count_y = 0; count_y < Y_SIZE; count_y++) + { + for (count_x = 0; count_x < X_SIZE; count_x++) + { +#ifdef USE_CURSES + buffy[bufcount] = cset_adjust( buffy[bufcount] ); #endif + textcolor( buffy[bufcount + 1] ); + putch( buffy[bufcount] ); + bufcount += 2; + } -#ifdef PLAIN_TERM - gotoxy(2, wherey() + 1); -#endif + gotoxy( 2, count_y + 2 ); } } -#endif - _setcursortype(_NORMALCURSOR); - } // end of (if brek...) -} // end viewwindow3() - - -unsigned char mapchar3(unsigned char ldfk) -{ - unsigned char showed = 0; - - switch (ldfk) - { - case DNGN_UNSEEN: - showed = 0; - break; - - case DNGN_SECRET_DOOR: - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - case DNGN_STONE_WALL: - case DNGN_METAL_WALL: - case DNGN_GREEN_CRYSTAL_WALL: - case DNGN_WAX_WALL: - showed = '*'; - break; - - case DNGN_CLOSED_DOOR: - showed = '+'; - break; - - case 20: // orcish idol - case 24: // ??? - case 25: // ??? - case DNGN_SILVER_STATUE: - case DNGN_GRANITE_STATUE: - case DNGN_ORANGE_CRYSTAL_STATUE: - showed = '8'; - break; - - case DNGN_LAVA_X: - case DNGN_WATER_X: - case DNGN_LAVA: - case DNGN_DEEP_WATER: - case DNGN_SHALLOW_WATER: - showed = '{'; - break; - - case DNGN_FLOOR: - case DNGN_UNDISCOVERED_TRAP: - showed = ','; - break; - - //case 68: showed = '>'; break; // < (60) - - case DNGN_OPEN_DOOR: - showed = 39; - break; // open door - - //case 72: showed = '<'; break; - - case DNGN_TRAP_MECHANICAL: - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - showed = '^'; - break; - - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - showed = '>'; - break; - - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - showed = '<'; - break; - - case DNGN_ENTER_HELL: - case DNGN_ENTER_LABYRINTH: - case DNGN_ENTER_SHOP: - case DNGN_ENTER_DIS: - case DNGN_ENTER_GEHENNA: - case DNGN_ENTER_COCYTUS: - case DNGN_ENTER_TARTARUS: - case DNGN_ENTER_ABYSS: - case DNGN_EXIT_ABYSS: - case DNGN_STONE_ARCH: - case DNGN_ENTER_PANDEMONIUM: - case DNGN_EXIT_PANDEMONIUM: - case DNGN_TRANSIT_PANDEMONIUM: - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - showed = '\\'; - break; - - case DNGN_ALTAR_ZIN: - case DNGN_ALTAR_SHINING_ONE: - case DNGN_ALTAR_KIKUBAAQUDGHA: - case DNGN_ALTAR_YREDELEMNUL: - case DNGN_ALTAR_XOM: - case DNGN_ALTAR_VEHUMET: - case DNGN_ALTAR_OKAWARU: - case DNGN_ALTAR_MAKHLEB: - case DNGN_ALTAR_SIF_MUNA: - case DNGN_ALTAR_TROG: - case DNGN_ALTAR_NEMELEX_XOBEH: - case DNGN_ALTAR_ELYVILON: - showed = '_'; - break; - - case DNGN_BLUE_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_I: - case DNGN_SPARKLING_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_DRY_FOUNTAIN_III: - case DNGN_DRY_FOUNTAIN_IV: - case DNGN_DRY_FOUNTAIN_V: - case DNGN_DRY_FOUNTAIN_VI: - case DNGN_DRY_FOUNTAIN_VII: - case DNGN_DRY_FOUNTAIN_VIII: - case DNGN_PERMADRY_FOUNTAIN: - showed = '}'; - break; - - default: - showed = 0; - break; - } - - return showed; -} - - -unsigned char mapchar4(unsigned char ldfk) -{ - unsigned char showed = 0; - - switch (ldfk) - { - case DNGN_UNSEEN: - showed = 0; - break; - - case DNGN_CLOSED_DOOR: - showed = '+'; - break; - case DNGN_SECRET_DOOR: - case DNGN_ROCK_WALL: - case DNGN_PERMAROCK_WALL: - case DNGN_STONE_WALL: - case DNGN_METAL_WALL: - case DNGN_GREEN_CRYSTAL_WALL: - case DNGN_WAX_WALL: - showed = '#'; - break; - - case 20: // orcish idol - case 24: // ??? - case 25: // ??? - case DNGN_SILVER_STATUE: - case DNGN_GRANITE_STATUE: - case DNGN_ORANGE_CRYSTAL_STATUE: - showed = '8'; - break; - - case DNGN_LAVA_X: - case DNGN_WATER_X: - case DNGN_LAVA: - case DNGN_DEEP_WATER: - case DNGN_SHALLOW_WATER: - showed = '{'; - break; - - case DNGN_FLOOR: - case DNGN_UNDISCOVERED_TRAP: - showed = '.'; - break; - - case 68: - showed = '>'; // < - break; - - case DNGN_OPEN_DOOR: - showed = 39; - break; - - case 72: - showed = '<'; - break; - - case DNGN_TRAP_MECHANICAL: - case DNGN_TRAP_MAGICAL: - case DNGN_TRAP_III: - showed = '^'; - break; - - case DNGN_STONE_STAIRS_DOWN_I: - case DNGN_STONE_STAIRS_DOWN_II: - case DNGN_STONE_STAIRS_DOWN_III: - case DNGN_ROCK_STAIRS_DOWN: - case DNGN_ENTER_ORCISH_MINES: - case DNGN_ENTER_HIVE: - case DNGN_ENTER_LAIR: - case DNGN_ENTER_SLIME_PITS: - case DNGN_ENTER_VAULTS: - case DNGN_ENTER_CRYPT: - case DNGN_ENTER_HALL_OF_BLADES: - case DNGN_ENTER_TEMPLE: - case DNGN_ENTER_SNAKE_PIT: - case DNGN_ENTER_ELVEN_HALLS: - case DNGN_ENTER_TOMB: - case DNGN_ENTER_SWAMP: - case 123: - case 124: - case 125: - case 126: - showed = '>'; - break; - - case DNGN_STONE_STAIRS_UP_I: - case DNGN_STONE_STAIRS_UP_II: - case DNGN_STONE_STAIRS_UP_III: - case DNGN_ROCK_STAIRS_UP: - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - case DNGN_RETURN_FROM_TEMPLE: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_ELVEN_HALLS: - case DNGN_RETURN_FROM_TOMB: - case DNGN_RETURN_FROM_SWAMP: - case 143: - case 144: - case 145: - case 146: - showed = '<'; - break; - - case DNGN_ENTER_HELL: - case DNGN_ENTER_LABYRINTH: - case DNGN_ENTER_SHOP: - case DNGN_ENTER_DIS: - case DNGN_ENTER_GEHENNA: - case DNGN_ENTER_COCYTUS: - case DNGN_ENTER_TARTARUS: - case DNGN_ENTER_ABYSS: - case DNGN_EXIT_ABYSS: - case DNGN_STONE_ARCH: - case DNGN_ENTER_PANDEMONIUM: - case DNGN_EXIT_PANDEMONIUM: - case DNGN_TRANSIT_PANDEMONIUM: - case DNGN_ENTER_ZOT: - case DNGN_RETURN_FROM_ZOT: - showed = '\\'; - break; - - case DNGN_ALTAR_ZIN: - case DNGN_ALTAR_SHINING_ONE: - case DNGN_ALTAR_KIKUBAAQUDGHA: - case DNGN_ALTAR_YREDELEMNUL: - case DNGN_ALTAR_XOM: - case DNGN_ALTAR_VEHUMET: - case DNGN_ALTAR_OKAWARU: - case DNGN_ALTAR_MAKHLEB: - case DNGN_ALTAR_SIF_MUNA: - case DNGN_ALTAR_TROG: - case DNGN_ALTAR_NEMELEX_XOBEH: - case DNGN_ALTAR_ELYVILON: - showed = '_'; - break; - - case DNGN_BLUE_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_I: - case DNGN_SPARKLING_FOUNTAIN: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_DRY_FOUNTAIN_III: - case DNGN_DRY_FOUNTAIN_IV: - case DNGN_DRY_FOUNTAIN_V: - case DNGN_DRY_FOUNTAIN_VI: - case DNGN_DRY_FOUNTAIN_VII: - case DNGN_DRY_FOUNTAIN_VIII: - case DNGN_PERMADRY_FOUNTAIN: - showed = '}'; - break; +#ifdef USE_CURSES + set_altcharset( false ); +#endif - default: - showed = 0; - break; +#endif + _setcursortype(_NORMALCURSOR); } - - return showed; -} +} // end viewwindow() diff --git a/crawl-ref/source/view.h b/crawl-ref/source/view.h index b36ff18551..26d17ada8c 100644 --- a/crawl-ref/source/view.h +++ b/crawl-ref/source/view.h @@ -3,6 +3,8 @@ * Summary: Misc function used to render the dungeon. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <2> 9/29/99 BCR Added the BORDER_COLOR define @@ -16,10 +18,13 @@ #include "externs.h" - #define BORDER_COLOR BROWN +void init_char_table(char_set_type set); +void init_feature_table( void ); + int get_number_of_lines(void); +int get_number_of_cols(void); /* *********************************************************************** * called from: dump_screenshot - chardump @@ -69,14 +74,14 @@ void magic_mapping(int map_radius, int proportion); * called from: acr - effects - it_use2 - it_use3 - item_use - spell - * spells - spells3 - spells4 * *********************************************************************** */ -void noisy( int loudness, int nois_x, int nois_y ); +bool noisy( int loudness, int nois_x, int nois_y, const char *msg = NULL ); // last updated 12may2000 {dlb} /* *********************************************************************** * called from: acr - spells3 * *********************************************************************** */ -void show_map( FixedVector& spec_place ); +void show_map( FixedVector& spec_place, bool travel_mode ); // last updated 12may2000 {dlb} @@ -108,4 +113,36 @@ void clear_map(); bool is_feature(int feature, int x, int y); +void set_envmap_char( int x, int y, unsigned char chr ); +void set_envmap_detected_item(int x, int y, bool detected = true); +void set_envmap_detected_mons(int x, int y, bool detected = true); +bool is_envmap_detected_item(int x, int y); +bool is_envmap_detected_mons(int x, int y); +void set_terrain_mapped( int x, int y ); +void set_terrain_seen( int x, int y ); +bool is_terrain_known( int x, int y ); +bool is_terrain_seen( int x, int y ); + +void clear_feature_overrides(); +void add_feature_override(const std::string &text); +void clear_cset_overrides(); +void add_cset_override(char_set_type set, const std::string &overrides); + +bool see_grid( int grx, int gry ); + +std::string screenshot(bool fullscreen = false); + +unsigned char get_sightmap_char(int feature); +unsigned char get_magicmap_char(int feature); + +void viewwindow(bool draw_it, bool do_updates); + +bool find_ray( int sourcex, int sourcey, int targetx, int targety, + bool allow_fallback, ray_def& ray ); + +#if defined(WIN32CONSOLE) || defined(DOS) +unsigned short dos_brand( unsigned short colour, + unsigned brand = CHATTR_REVERSE); +#endif + #endif diff --git a/crawl-ref/source/wpn-misc.cc b/crawl-ref/source/wpn-misc.cc deleted file mode 100644 index c6d38f98e6..0000000000 --- a/crawl-ref/source/wpn-misc.cc +++ /dev/null @@ -1,268 +0,0 @@ -/* - ********************************************************************* - * File: wpn-misc.cc * - * Summary: temporary home for weapon f(x) until struct'ed * - * Written by: don brodale * - * * - * Changelog(most recent first): * - * * - * <00> 12jun2000 dlb created after little thought * - ********************************************************************* - */ - -#include "AppHdr.h" -#include "wpn-misc.h" - -#include "externs.h" - -// all of this will be replaced by a struct and data handlers {dlb}: - -/* - ************************************************** - * * - * BEGIN PUBLIC FUNCTIONS * - * * - ************************************************** -*/ - -char damage_type(unsigned char wclass, unsigned char wtype) -{ - char type_damage = DVORP_CRUSHING; // this is the default, btw {dlb} - - if (wclass == OBJ_WEAPONS) - { - switch (wtype) - { - case WPN_DAGGER: - case WPN_DEMON_BLADE: - case WPN_DOUBLE_SWORD: - case WPN_GREAT_SWORD: - case WPN_KATANA: - case WPN_KNIFE: - case WPN_LONG_SWORD: - case WPN_QUICK_BLADE: - case WPN_SABRE: - case WPN_FALCHION: - case WPN_SCIMITAR: - case WPN_SCYTHE: - case WPN_SHORT_SWORD: - case WPN_TRIPLE_SWORD: - type_damage = DVORP_SLICING; - break; - - case WPN_DEMON_TRIDENT: - case WPN_EVENINGSTAR: - case WPN_GIANT_SPIKED_CLUB: - case WPN_MORNINGSTAR: - case WPN_SPEAR: - case WPN_SPIKED_FLAIL: - case WPN_TRIDENT: - type_damage = DVORP_PIERCING; - break; - - case WPN_WAR_AXE: - case WPN_BATTLEAXE: - case WPN_BROAD_AXE: - case WPN_EXECUTIONERS_AXE: - case WPN_GLAIVE: - case WPN_HALBERD: - case WPN_HAND_AXE: - type_damage = DVORP_CHOPPING; - break; - } - } - - return (type_damage); -} // end damage_type() - -bool can_cut_meat(unsigned char wclass, unsigned char wtype) -{ - int type = damage_type( wclass, wtype ); - - if (type == DVORP_CHOPPING || type == DVORP_SLICING) - return (true); - - return (false); -} - -int hands_reqd_for_weapon(unsigned char wclass, unsigned char wtype) -{ - int reqd_hands = HANDS_ONE_HANDED; - - switch (wclass) - { - case OBJ_WEAPONS: - switch (wtype) - { - case WPN_HALBERD: - case WPN_SCYTHE: - case WPN_GLAIVE: - case WPN_QUARTERSTAFF: - case WPN_BATTLEAXE: - case WPN_EXECUTIONERS_AXE: - case WPN_GREAT_SWORD: - case WPN_TRIPLE_SWORD: - case WPN_GREAT_MACE: - case WPN_GREAT_FLAIL: - case WPN_GIANT_CLUB: - case WPN_GIANT_SPIKED_CLUB: - reqd_hands = HANDS_TWO_HANDED; - break; - - case WPN_SPEAR: - case WPN_TRIDENT: - case WPN_DEMON_TRIDENT: - case WPN_WAR_AXE: - case WPN_BROAD_AXE: - case WPN_KATANA: - case WPN_DOUBLE_SWORD: - reqd_hands = HANDS_ONE_OR_TWO_HANDED; - break; - } - break; - - case OBJ_STAVES: - reqd_hands = HANDS_TWO_HANDED; - break; - } - - return (reqd_hands); -} // end hands_reqd_for_weapon() - -bool is_demonic(unsigned char weapon_subtype) -{ - switch (weapon_subtype) - { - case WPN_DEMON_BLADE: - case WPN_DEMON_WHIP: - case WPN_DEMON_TRIDENT: - return true; - - default: - return false; - } -} // end is_demonic() - -bool launches_things( unsigned char weapon_subtype ) -{ - switch (weapon_subtype) - { - case WPN_SLING: - case WPN_BOW: - case WPN_CROSSBOW: - case WPN_HAND_CROSSBOW: - case WPN_BLOWGUN: - return (true); - - default: - return (false); - } -} // end launches_things() - -unsigned char launched_by(unsigned char weapon_subtype) -{ - switch (weapon_subtype) - { - case WPN_BLOWGUN: - return MI_NEEDLE; - case WPN_SLING: - return MI_STONE; - case WPN_BOW: - return MI_ARROW; - case WPN_CROSSBOW: - return MI_BOLT; - case WPN_HAND_CROSSBOW: - return MI_DART; - default: - return MI_EGGPLANT; // lame debugging code :P {dlb} - } -} // end launched_by() - -// this function returns the skill that the weapon would use in melee -char weapon_skill(unsigned char wclass, unsigned char wtype) -{ - char skill2use = SK_FIGHTING; - - if (wclass == OBJ_STAVES - && (wtype < STAFF_SMITING || wtype >= STAFF_AIR)) - { - skill2use = SK_STAVES; - } - else if (wclass != OBJ_WEAPONS) - skill2use = SK_FIGHTING; - else - { - switch (wtype) - { - case WPN_CLUB: - case WPN_MACE: - case WPN_HAMMER: - case WPN_ANCUS: - case WPN_WHIP: - case WPN_FLAIL: - case WPN_MORNINGSTAR: - case WPN_GIANT_CLUB: - case WPN_GIANT_SPIKED_CLUB: - case WPN_EVENINGSTAR: - case WPN_DEMON_WHIP: - case WPN_SPIKED_FLAIL: - case WPN_GREAT_FLAIL: - case WPN_GREAT_MACE: - case WPN_BOW: - case WPN_BLOWGUN: - case WPN_CROSSBOW: - case WPN_HAND_CROSSBOW: - skill2use = SK_MACES_FLAILS; - break; - - case WPN_KNIFE: - case WPN_DAGGER: - case WPN_SHORT_SWORD: - case WPN_QUICK_BLADE: - case WPN_SABRE: - skill2use = SK_SHORT_BLADES; - break; - - case WPN_FALCHION: - case WPN_LONG_SWORD: - case WPN_SCIMITAR: - case WPN_KATANA: - case WPN_DOUBLE_SWORD: - case WPN_DEMON_BLADE: - case WPN_GREAT_SWORD: - case WPN_TRIPLE_SWORD: - skill2use = SK_LONG_SWORDS; - break; - - case WPN_HAND_AXE: - case WPN_WAR_AXE: - case WPN_BROAD_AXE: - case WPN_BATTLEAXE: - case WPN_EXECUTIONERS_AXE: - skill2use = SK_AXES; - break; - - case WPN_SPEAR: - case WPN_HALBERD: - case WPN_GLAIVE: - case WPN_SCYTHE: - case WPN_TRIDENT: - case WPN_DEMON_TRIDENT: - skill2use = SK_POLEARMS; - break; - - case WPN_QUARTERSTAFF: - skill2use = SK_STAVES; - break; - } - } - - return (skill2use); -} // end weapon_skill() -/* - ************************************************** - * * - * END PUBLIC FUNCTIONS * - * * - ************************************************** -*/ diff --git a/crawl-ref/source/wpn-misc.h b/crawl-ref/source/wpn-misc.h deleted file mode 100644 index e1d45c8d4a..0000000000 --- a/crawl-ref/source/wpn-misc.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - ********************************************************************* - * File: wpn-misc.h * - * Summary: temporary home for weapon f(x) until struct'ed * - * Written by: don brodale * - * * - * Changelog(most recent first): * - * * - * <00> 12jun2000 dlb created after little thought * - ********************************************************************* -*/ - - -#ifndef WPNMISC_H -#define WPNMISC_H - -#include "externs.h" - - -/* *********************************************************************** - * called from: food.h - * *********************************************************************** */ -bool can_cut_meat(unsigned char wclass, unsigned char wtype); - -/* *********************************************************************** - * called from: acr - fight - food - item_use - itemname - spells2 - * *********************************************************************** */ -char damage_type(unsigned char wclass, unsigned char wtype); - - -// last updated: 10jun2000 {dlb} -/* *********************************************************************** - * called from: describe - fight - item_use - * *********************************************************************** */ -int hands_reqd_for_weapon(unsigned char wclass, unsigned char wtype); - - -// last updated: 10jun2000 {dlb} -/* *********************************************************************** - * called from: dungeon - fight - item_use - randart - * *********************************************************************** */ -bool is_demonic(unsigned char weapon_subtype); - - -// last updated: 10jun2000 {dlb} -/* *********************************************************************** - * called from: dungeon - item_use - mstuff2 - * *********************************************************************** */ -unsigned char launched_by(unsigned char weapon_subtype); - - -// last updated: 10jun2000 {dlb} -/* *********************************************************************** - * called from: describe - dungeon - fight - item_use - mstuff2 - randart - - * spells2 - spells3 - * *********************************************************************** */ -bool launches_things( unsigned char weapon_subtype ); - - -// last updated: 10jun2000 {dlb} -/* *********************************************************************** - * called from: describe - fight - files - it_use3 - newgame - spells1 - * *********************************************************************** */ -char weapon_skill(unsigned char wclass, unsigned char wtype); - - -#endif -- cgit v1.2.3-54-g00ecf