summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.cvsignore8
-rw-r--r--src/CMakeLists.txt72
-rw-r--r--src/ENGLISH.txt35
-rw-r--r--src/angband.h110
-rw-r--r--src/angband.icobin0 -> 766 bytes
-rw-r--r--src/angband.rc132
-rw-r--r--src/birth.c3825
-rw-r--r--src/bldg.c2198
-rw-r--r--src/carbon/Angband.icnsbin0 -> 66668 bytes
-rw-r--r--src/carbon/Carbon.r1568
-rw-r--r--src/carbon/Data.icnsbin0 -> 36416 bytes
-rw-r--r--src/carbon/Edit.icnsbin0 -> 35735 bytes
-rw-r--r--src/carbon/Image-DS_Storebin0 -> 6148 bytes
-rw-r--r--src/carbon/Info.plist39
-rw-r--r--src/carbon/Save.icnsbin0 -> 43952 bytes
-rwxr-xr-xsrc/carbon/getversion2
-rw-r--r--src/cave.c5055
-rw-r--r--src/cmd1.c5125
-rw-r--r--src/cmd2.c5107
-rw-r--r--src/cmd3.c2331
-rw-r--r--src/cmd4.c4658
-rw-r--r--src/cmd5.c2562
-rw-r--r--src/cmd6.c7731
-rw-r--r--src/cmd7.c7652
-rw-r--r--src/cmovie.c496
-rw-r--r--src/config.h314
-rw-r--r--src/defines.h4695
-rw-r--r--src/dungeon.c5664
-rw-r--r--src/dungeon.pkg1607
-rw-r--r--src/externs.h1851
-rw-r--r--src/files.c6058
-rw-r--r--src/gen_evol.c156
-rw-r--r--src/gen_maze.c294
-rw-r--r--src/generate.c8890
-rw-r--r--src/gods.c139
-rw-r--r--src/h-basic.h25
-rw-r--r--src/h-config.h274
-rw-r--r--src/h-define.h111
-rw-r--r--src/h-system.h138
-rw-r--r--src/h-type.h178
-rw-r--r--src/help.c23
-rw-r--r--src/init1.c11819
-rw-r--r--src/init2.c2918
-rw-r--r--src/iso/.cvsignore1
-rw-r--r--src/lauxlib.h100
-rw-r--r--src/levels.c234
-rw-r--r--src/loadsave.c3288
-rw-r--r--src/lua/.cvsignore2
-rw-r--r--src/lua/.gitignore2
-rw-r--r--src/lua/CMakeLists.txt11
-rw-r--r--src/lua/array.lua203
-rw-r--r--src/lua/basic.lua190
-rw-r--r--src/lua/class.lua85
-rw-r--r--src/lua/clean.lua74
-rw-r--r--src/lua/code.lua73
-rw-r--r--src/lua/container.lua311
-rw-r--r--src/lua/declaration.lua399
-rw-r--r--src/lua/define.lua72
-rw-r--r--src/lua/doit.lua73
-rw-r--r--src/lua/enumerate.lua93
-rw-r--r--src/lua/feature.lua72
-rw-r--r--src/lua/function.lua317
-rw-r--r--src/lua/lapi.c499
-rw-r--r--src/lua/lapi.h17
-rw-r--r--src/lua/lauxlib.c216
-rw-r--r--src/lua/lauxlib.h100
-rw-r--r--src/lua/lbaselib.c651
-rw-r--r--src/lua/lcode.c701
-rw-r--r--src/lua/lcode.h70
-rw-r--r--src/lua/ldblib.c188
-rw-r--r--src/lua/ldebug.c466
-rw-r--r--src/lua/ldebug.h21
-rw-r--r--src/lua/ldo.c385
-rw-r--r--src/lua/ldo.h33
-rw-r--r--src/lua/lfunc.c109
-rw-r--r--src/lua/lfunc.h24
-rw-r--r--src/lua/lgc.c353
-rw-r--r--src/lua/lgc.h18
-rw-r--r--src/lua/liolib.c710
-rw-r--r--src/lua/llex.c411
-rw-r--r--src/lua/llex.h72
-rw-r--r--src/lua/llimits.h205
-rw-r--r--src/lua/lmem.c150
-rw-r--r--src/lua/lmem.h42
-rw-r--r--src/lua/lobject.c125
-rw-r--r--src/lua/lobject.h204
-rw-r--r--src/lua/lopcodes.h168
-rw-r--r--src/lua/lparser.c1129
-rw-r--r--src/lua/lparser.h60
-rw-r--r--src/lua/lstate.c121
-rw-r--r--src/lua/lstate.h77
-rw-r--r--src/lua/lstring.c155
-rw-r--r--src/lua/lstring.h37
-rw-r--r--src/lua/lstrlib.c621
-rw-r--r--src/lua/ltable.c303
-rw-r--r--src/lua/ltable.h34
-rw-r--r--src/lua/ltests.c543
-rw-r--r--src/lua/ltm.c163
-rw-r--r--src/lua/ltm.h59
-rw-r--r--src/lua/lua.h248
-rw-r--r--src/lua/lua2c.lua29
-rw-r--r--src/lua/luadebug.h46
-rw-r--r--src/lua/lualib.h34
-rw-r--r--src/lua/lundump.c244
-rw-r--r--src/lua/lundump.h35
-rw-r--r--src/lua/lvm.c710
-rw-r--r--src/lua/lvm.h32
-rw-r--r--src/lua/lzio.c84
-rw-r--r--src/lua/lzio.h53
-rw-r--r--src/lua/module.lua69
-rw-r--r--src/lua/operator.lua111
-rw-r--r--src/lua/package.lua222
-rw-r--r--src/lua/print.h55
-rw-r--r--src/lua/tolua.c149
-rw-r--r--src/lua/tolua.h127
-rw-r--r--src/lua/tolua_bd.c214
-rw-r--r--src/lua/tolua_eh.c66
-rw-r--r--src/lua/tolua_eh.h24
-rw-r--r--src/lua/tolua_gp.c197
-rw-r--r--src/lua/tolua_lb.c160
-rw-r--r--src/lua/tolua_rg.c243
-rw-r--r--src/lua/tolua_rg.h22
-rw-r--r--src/lua/tolua_tm.c585
-rw-r--r--src/lua/tolua_tm.h32
-rw-r--r--src/lua/tolua_tt.c316
-rw-r--r--src/lua/tolua_tt.h31
-rw-r--r--src/lua/tolualua.c2975
-rw-r--r--src/lua/tolualua.h2713
-rw-r--r--src/lua/tolualua.pkg21
-rw-r--r--src/lua/typedef.lua59
-rw-r--r--src/lua/variable.lua192
-rw-r--r--src/lua/verbatim.lua77
-rw-r--r--src/lua_bind.c691
-rwxr-xr-xsrc/maid-x11.c855
-rw-r--r--src/main-crb.c6402
-rw-r--r--src/main-gcu.c1222
-rw-r--r--src/main-gtk2.c5026
-rw-r--r--src/main-sdl.c2253
-rw-r--r--src/main-sla.c455
-rw-r--r--src/main-win.c4368
-rw-r--r--src/main-x11.c3210
-rw-r--r--src/main-xaw.c1888
-rw-r--r--src/main-xxx.c785
-rw-r--r--src/main.c691
-rw-r--r--src/melee1.c3065
-rw-r--r--src/melee2.c7591
-rw-r--r--src/modules.c274
-rw-r--r--src/monster.pkg2324
-rw-r--r--src/monster1.c1908
-rw-r--r--src/monster2.c4054
-rw-r--r--src/monster3.c706
-rw-r--r--src/notes.c188
-rw-r--r--src/object.pkg1169
-rw-r--r--src/object1.c6669
-rw-r--r--src/object2.c6617
-rw-r--r--src/player.pkg3519
-rw-r--r--src/player_c.pkg1060
-rw-r--r--src/plots.c473
-rw-r--r--src/plots.h48
-rw-r--r--src/powers.c1388
-rw-r--r--src/q_betwen.c188
-rw-r--r--src/q_dragons.c150
-rw-r--r--src/q_eol.c195
-rw-r--r--src/q_evil.c117
-rw-r--r--src/q_haunted.c147
-rw-r--r--src/q_hobbit.c195
-rw-r--r--src/q_invas.c201
-rw-r--r--src/q_main.c176
-rw-r--r--src/q_narsil.c108
-rw-r--r--src/q_nazgul.c116
-rw-r--r--src/q_nirna.c109
-rw-r--r--src/q_one.c354
-rw-r--r--src/q_poison.c238
-rw-r--r--src/q_rand.c465
-rw-r--r--src/q_shroom.c293
-rw-r--r--src/q_spider.c108
-rw-r--r--src/q_thief.c172
-rw-r--r--src/q_thrain.c230
-rw-r--r--src/q_troll.c178
-rw-r--r--src/q_ultrae.c11
-rw-r--r--src/q_ultrag.c276
-rw-r--r--src/q_wight.c156
-rw-r--r--src/q_wolves.c130
-rwxr-xr-xsrc/quest.pkg170
-rw-r--r--src/randart.c476
-rw-r--r--src/readdib.c342
-rw-r--r--src/readdib.h21
-rw-r--r--src/script.c535
-rw-r--r--src/skills.c1661
-rw-r--r--src/spells.pkg2448
-rw-r--r--src/spells1.c9327
-rw-r--r--src/spells2.c8076
-rw-r--r--src/squeltch.c553
-rw-r--r--src/status.c773
-rw-r--r--src/store.c4458
-rw-r--r--src/tables.c4792
-rw-r--r--src/traps.c3169
-rw-r--r--src/types.h2522
-rw-r--r--src/util.c4479
-rw-r--r--src/util.pkg2683
-rw-r--r--src/variable.c1604
-rw-r--r--src/wild.c1275
-rw-r--r--src/wizard1.c2756
-rw-r--r--src/wizard2.c1950
-rw-r--r--src/xtra1.c4772
-rw-r--r--src/xtra2.c6158
-rw-r--r--src/z-form.c831
-rw-r--r--src/z-form.h54
-rw-r--r--src/z-rand.c355
-rw-r--r--src/z-rand.h96
-rw-r--r--src/z-term.c2776
-rw-r--r--src/z-term.h343
-rw-r--r--src/z-util.c234
-rw-r--r--src/z-util.h91
-rw-r--r--src/z-virt.c187
-rw-r--r--src/z-virt.h168
-rw-r--r--src/z_pack.pkg398
217 files changed, 262620 insertions, 0 deletions
diff --git a/src/.cvsignore b/src/.cvsignore
new file mode 100644
index 00000000..d99b4ed2
--- /dev/null
+++ b/src/.cvsignore
@@ -0,0 +1,8 @@
+.sconsign
+.gdb_history
+makefile
+tome
+tolua
+w_*.c
+TOME.EXE
+TOLUA.EXE
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 00000000..932fef4b
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,72 @@
+# Lua support code.
+ADD_SUBDIRECTORY(lua)
+
+SET(SRCS
+ main-gcu.c main-x11.c main-xaw.c main-sdl.c main-gtk2.c
+ z-rand.c z-util.c z-form.c z-virt.c z-term.c
+ variable.c tables.c plots.c util.c cave.c dungeon.c
+ melee1.c melee2.c modules.c
+ object1.c object2.c randart.c squeltch.c traps.c
+ monster1.c monster2.c monster3.c
+ xtra1.c xtra2.c skills.c powers.c gods.c
+ spells1.c spells2.c
+ status.c files.c notes.c loadsave.c
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c
+ help.c
+ generate.c gen_maze.c gen_evol.c wild.c levels.c store.c bldg.c
+ cmovie.c
+ wizard2.c init2.c birth.c wizard1.c init1.c main.c
+ # Lua bits:
+ lua_bind.c script.c w_mnster.c w_player.c w_play_c.c w_z_pack.c
+ w_obj.c w_util.c w_spells.c w_quest.c w_dun.c
+)
+
+# Need a few additional source files for Windows.
+if(WIN32)
+ SET(SRCS ${SRCS} main-win.c readdib.c)
+ # Resource files require a little workaround.
+ if(MINGW)
+ # Workaround for resource compilation for mingw on CMake.
+ # See http://www.cmake.org/Bug/view.php?id=4068
+ ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/angband_rc.o
+ COMMAND windres.exe -I${CMAKE_CURRENT_SOURCE_DIR}
+ -i${CMAKE_CURRENT_SOURCE_DIR}/angband.rc
+ -o ${CMAKE_CURRENT_BINARY_DIR}/angband_rc.o)
+ SET(SRCS ${SRCS} ${CMAKE_CURRENT_BINARY_DIR}/angband_rc.o)
+ else(MINGW)
+ SET(SRCS ${SRCS} angband.rc)
+ endif(MINGW)
+endif(WIN32)
+
+
+# Macro for defining tolua targets.
+MACRO(TOLUA_FILE MODULE_NAME OUTPUT_FILE_NAME)
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${OUTPUT_FILE_NAME}
+ COMMAND tolua -n ${MODULE_NAME} -o ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_NAME}.pkg
+ DEPENDS tolua ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_NAME}.pkg
+ )
+ SET_SOURCE_FILES_PROPERTIES("${OUTPUT_FILE_NAME}" PROPERTIES GENERATED TRUE)
+ENDMACRO(TOLUA_FILE)
+
+# Process all the needed modules.
+TOLUA_FILE(monster w_mnster.c)
+TOLUA_FILE(player w_player.c)
+TOLUA_FILE(player_c w_play_c.c)
+TOLUA_FILE(z_pack w_z_pack.c)
+TOLUA_FILE(object w_obj.c)
+TOLUA_FILE(util w_util.c)
+TOLUA_FILE(spells w_spells.c)
+TOLUA_FILE(quest w_quest.c)
+TOLUA_FILE(dungeon w_dun.c)
+
+# tome executable
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/lua)
+ADD_EXECUTABLE(tome ${EXECUTABLE_OPTIONS} ${SRCS})
+TARGET_LINK_LIBRARIES(tome lua ${LIBS})
+
+# Installation
+INSTALL(TARGETS tome
+ RUNTIME DESTINATION games
+)
diff --git a/src/ENGLISH.txt b/src/ENGLISH.txt
new file mode 100644
index 00000000..354aa129
--- /dev/null
+++ b/src/ENGLISH.txt
@@ -0,0 +1,35 @@
+ English Text Style
+
+* Use British spelling in preference to American spelling. Quotations,
+ however, should be left intact.
+
+* Capitalize the name of spells and special powers as you would the
+ title of a book.
+
+* Avoid unnecessary modernisms. This helps preserve the mood -- an
+ item "allows", rather than "enables", you to do something.
+
+* When describing a person possessing an item, use "its wielder" or
+ "its wearer" in preference to "the wielder" or "the wearer".
+
+* When describing a gendered person of indeterminate gender, use
+ masculine terms. "This icy sword increases its wielder's statistics
+ and sustains his strength."
+
+* Use the British Imperial system for measurements. This means feet
+ and inches, not meters and centimeters.
+
+
+ Specific Issues
+
+* Use "hit points", not "hitpoints".
+
+* Use "Long Sword", not "Long-sword" or "Longsword".
+
+* Use "Middle-earth", not "middle-earth" or "Middle Earth".
+
+* Use "staves", not "staffs".
+
+* Use "dwarves", not "dwarfs".
+
+-- markrax (Mark Schreiber) <mark7@andrew.cmu.edu>
diff --git a/src/angband.h b/src/angband.h
new file mode 100644
index 00000000..cac38122
--- /dev/null
+++ b/src/angband.h
@@ -0,0 +1,110 @@
+/* File: angband.h */
+
+/* Main "Angband" header file */
+
+#ifndef INCLUDED_ANGBAND_H
+#define INCLUDED_ANGBAND_H
+
+/*
+ * Copyright (c) 1989 James E. Wilson
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+/*
+ * C++ guard.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * First, include the low-level includes. Be sure to edit "h-config.h"
+ * to reflect any hardware, operating system, or compiler nuances.
+ */
+#include "h-basic.h"
+
+
+/*
+ * Then, include the header files for the low-level code
+ */
+#include "z-util.h"
+#include "z-virt.h"
+#include "z-form.h"
+#include "z-rand.h"
+#include "z-term.h"
+
+
+/*
+ * Include the "Angband" configuration header
+ */
+#include "config.h"
+
+
+/*
+ * Now, include the define's, the type's, and the extern's
+ */
+#include "defines.h"
+#include "types.h"
+#include "externs.h"
+#include "plots.h"
+
+/***** Some copyright messages follow below *****/
+
+/*
+ * Note that these copyright messages apply to an ancient version
+ * of Angband, as in, from pre-2.4.frog-knows days, and thus the
+ * reference to "5.0" is rather misleading...
+ */
+
+/*
+ * UNIX ANGBAND Version 5.0
+ */
+
+
+/* Original copyright message follows. */
+
+/*
+ * ANGBAND Version 4.8 COPYRIGHT (c) Robert Alan Koeneke
+ *
+ * I lovingly dedicate this game to hackers and adventurers
+ * everywhere...
+ *
+ * Designer and Programmer:
+ * Robert Alan Koeneke
+ * University of Oklahoma
+ *
+ * Assistant Programmer:
+ * Jimmey Wayne Todd
+ * University of Oklahoma
+ *
+ * Assistant Programmer:
+ * Gary D. McAdoo
+ * University of Oklahoma
+ *
+ * UNIX Port:
+ * James E. Wilson
+ * UC Berkeley
+ * wilson@ernie.Berkeley.EDU
+ * ucbvax!ucbernie!wilson
+ */
+
+
+/*
+ * ANGBAND may be copied and modified freely as long as the above
+ * credits are retained. No one who-so-ever may sell or market
+ * this software in any form without the expressed written consent
+ * of the author Robert Alan Koeneke.
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+
+
+
diff --git a/src/angband.ico b/src/angband.ico
new file mode 100644
index 00000000..d20d4c7f
--- /dev/null
+++ b/src/angband.ico
Binary files differ
diff --git a/src/angband.rc b/src/angband.rc
new file mode 100644
index 00000000..00e16516
--- /dev/null
+++ b/src/angband.rc
@@ -0,0 +1,132 @@
+/* File: angband.rc */
+
+/*
+ * If the windows command to compile this file understands #ifdef's, please
+ * say #ifdef ALLOW_QUITTING to select from "A&bort" and "Sh&ow scores"
+ */
+
+ANGBAND MENU
+{
+ POPUP "&File"
+ {
+ MENUITEM "&Save", 110
+ MENUITEM SEPARATOR
+ MENUITEM "S&how score", 120
+ /*MENUITEM "A&bort", 120*/
+ MENUITEM "E&xit", 121
+ }
+
+ POPUP "&Window"
+ {
+ POPUP "&Visibility"
+ {
+ MENUITEM "ToME window", 200
+ MENUITEM "Mirror window", 201
+ MENUITEM "Recall window", 202
+ MENUITEM "Choice window", 203
+ MENUITEM "Term-4 window", 204
+ MENUITEM "Term-5 window", 205
+ MENUITEM "Term-6 window", 206
+ MENUITEM "Term-7 window", 207
+ }
+
+ POPUP "&Font"
+ {
+ MENUITEM "ToME window", 210
+ MENUITEM "Mirror window", 211
+ MENUITEM "Recall window", 212
+ MENUITEM "Choice window", 213
+ MENUITEM "Term-4 window", 214
+ MENUITEM "Term-5 window", 215
+ MENUITEM "Term-6 window", 216
+ MENUITEM "Term-7 window", 217
+ }
+
+ MENUITEM SEPARATOR
+
+ POPUP "Bizarre Display"
+ {
+ MENUITEM "ToME window", 230
+ MENUITEM "Mirror window", 231
+ MENUITEM "Recall window", 232
+ MENUITEM "Choice window", 233
+ MENUITEM "Term-4 window", 234
+ MENUITEM "Term-5 window", 235
+ MENUITEM "Term-6 window", 236
+ MENUITEM "Term-7 window", 237
+ }
+
+ POPUP "Increase Tile Width"
+ {
+ MENUITEM "ToME window", 240
+ MENUITEM "Mirror window", 241
+ MENUITEM "Recall window", 242
+ MENUITEM "Choice window", 243
+ MENUITEM "Term-4 window", 244
+ MENUITEM "Term-5 window", 245
+ MENUITEM "Term-6 window", 246
+ MENUITEM "Term-7 window", 247
+ }
+
+ POPUP "Decrease Tile Width"
+ {
+ MENUITEM "ToME window", 250
+ MENUITEM "Mirror window", 251
+ MENUITEM "Recall window", 252
+ MENUITEM "Choice window", 253
+ MENUITEM "Term-4 window", 254
+ MENUITEM "Term-5 window", 255
+ MENUITEM "Term-6 window", 256
+ MENUITEM "Term-7 window", 257
+ }
+
+ POPUP "Increase Tile Height"
+ {
+ MENUITEM "ToME window", 260
+ MENUITEM "Mirror window", 261
+ MENUITEM "Recall window", 262
+ MENUITEM "Choice window", 263
+ MENUITEM "Term-4 window", 264
+ MENUITEM "Term-5 window", 265
+ MENUITEM "Term-6 window", 266
+ MENUITEM "Term-7 window", 267
+ }
+
+ POPUP "Decrease Tile Height"
+ {
+ MENUITEM "ToME window", 270
+ MENUITEM "Mirror window", 271
+ MENUITEM "Recall window", 272
+ MENUITEM "Choice window", 273
+ MENUITEM "Term-4 window", 274
+ MENUITEM "Term-5 window", 275
+ MENUITEM "Term-6 window", 276
+ MENUITEM "Term-7 window", 277
+ }
+ }
+
+ POPUP "&Options"
+ {
+ POPUP "&Graphics"
+ {
+ MENUITEM "&Old tiles", 400
+ MENUITEM "&New tiles", 401
+ MENUITEM "ASCII &Text", 403
+ MENUITEM "&Bigtile mode", 409
+ }
+
+ MENUITEM "&Sound", 402
+ MENUITEM SEPARATOR
+ MENUITEM "Unused menu option", 410
+ MENUITEM "Activate Screensaver", 411
+ }
+
+ POPUP "&Help"
+ {
+ MENUITEM "&General", 901
+ MENUITEM "&Spoilers", 902
+ }
+}
+
+ANGBAND ICON "angband.ico"
+
diff --git a/src/birth.c b/src/birth.c
new file mode 100644
index 00000000..f073b2f6
--- /dev/null
+++ b/src/birth.c
@@ -0,0 +1,3825 @@
+/* File: birth.c */
+
+/* Purpose: create a player character */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * How often the autoroller will update the display and pause
+ * to check for user interuptions.
+ * Bigger values will make the autoroller faster, but slower
+ * system may have problems because the user can't stop the
+ * autoroller for this number of rolls.
+ */
+#define AUTOROLLER_STEP 25L
+
+/*
+ * Maximum number of tries for selection of a proper quest monster
+ */
+#define MAX_TRIES 100
+
+/* Max quests */
+static byte max_quests = 0;
+
+/*
+ * Current stats
+ */
+static s16b stat_use[6];
+
+/*
+ * Autoroll limit
+ */
+static s16b stat_limit[6];
+
+/*
+ * Autoroll matches
+ */
+static s32b stat_match[6];
+
+/*
+ * Autoroll round
+ */
+static s32b auto_round;
+
+/*
+ * Last round
+ */
+static s32b last_round;
+
+/* Human */
+static char *human_syllable1[] =
+{
+ "Ab", "Ac", "Ad", "Af", "Agr", "Ast", "As", "Al", "Adw", "Adr", "Ar",
+ "B", "Br", "C", "Cr", "Ch", "Cad", "D", "Dr", "Dw", "Ed", "Eth", "Et",
+ "Er", "El", "Eow", "F", "Fr", "G", "Gr", "Gw", "Gal", "Gl", "H", "Ha",
+ "Ib", "Jer", "K", "Ka", "Ked", "L", "Loth", "Lar", "Leg", "M", "Mir",
+ "N", "Nyd", "Ol", "Oc", "On", "P", "Pr", "R", "Rh", "S", "Sev", "T",
+ "Tr", "Th", "V", "Y", "Z", "W", "Wic",
+};
+
+static char *human_syllable2[] =
+{
+ "a", "ae", "au", "ao", "are", "ale", "ali", "ay", "ardo", "e", "ei",
+ "ea", "eri", "era", "ela", "eli", "enda", "erra", "i", "ia", "ie",
+ "ire", "ira", "ila", "ili", "ira", "igo", "o", "oa", "oi", "oe",
+ "ore", "u", "y",
+};
+
+static char *human_syllable3[] =
+{
+ "a", "and", "b", "bwyn", "baen", "bard", "c", "ctred", "cred", "ch",
+ "can", "d", "dan", "don", "der", "dric", "dfrid", "dus", "f", "g",
+ "gord", "gan", "l", "li", "lgrin", "lin", "lith", "lath", "loth",
+ "ld", "ldric", "ldan", "m", "mas", "mos", "mar", "mond", "n",
+ "nydd", "nidd", "nnon", "nwan", "nyth", "nad", "nn", "nnor", "nd",
+ "p", "r", "ron", "rd", "s", "sh", "seth", "sean", "t", "th", "tha",
+ "tlan", "trem", "tram", "v", "vudd", "w", "wan", "win", "wyn", "wyr",
+ "wyr", "wyth",
+};
+
+/*
+ * Random Name Generator
+ * based on a Javascript by Michael Hensley
+ * "http://geocities.com/timessquare/castle/6274/"
+ */
+static void create_random_name(int race, char *name)
+{
+ char *syl1, *syl2, *syl3;
+
+ int idx;
+
+
+ /* Paranoia */
+ if (!name) return;
+
+ /* Select the monster type */
+ switch (race)
+ {
+ /* Create the monster name */
+
+ /* Use human ones */
+ default:
+ {
+ idx = rand_int(sizeof(human_syllable1) / sizeof(char *));
+ syl1 = human_syllable1[idx];
+ idx = rand_int(sizeof(human_syllable2) / sizeof(char *));
+ syl2 = human_syllable2[idx];
+ idx = rand_int(sizeof(human_syllable3) / sizeof(char *));
+ syl3 = human_syllable3[idx];
+
+ break;
+ }
+ }
+
+ /* Concatenate selected syllables */
+ strnfmt(name, 32, "%s%s%s", syl1, syl2, syl3);
+}
+
+
+void print_desc_aux(cptr txt, int y, int xx)
+{
+ int i = -1, x = xx;
+
+
+ while (txt[++i] != 0)
+ {
+ if (txt[i] == '\n')
+ {
+ x = xx;
+ y++;
+ }
+ else
+ {
+ Term_putch(x++, y, TERM_YELLOW, txt[i]);
+ }
+ }
+}
+
+void print_desc(cptr txt)
+{
+ print_desc_aux(txt, 12, 1);
+}
+
+/*
+ * Save the current data for later
+ */
+static void save_prev_data(void)
+{
+ int i;
+
+
+ /*** Save the current data ***/
+
+ /* Save the data */
+ previous_char.sex = p_ptr->psex;
+ previous_char.race = p_ptr->prace;
+ previous_char.rmod = p_ptr->pracem;
+ previous_char.pclass = p_ptr->pclass;
+ previous_char.spec = p_ptr->pspec;
+
+ previous_char.quests = max_quests;
+
+ previous_char.god = p_ptr->pgod;
+ previous_char.grace = p_ptr->grace;
+
+ previous_char.age = p_ptr->age;
+ previous_char.wt = p_ptr->wt;
+ previous_char.ht = p_ptr->ht;
+ previous_char.sc = p_ptr->sc;
+ previous_char.au = p_ptr->au;
+
+ /* Save the stats */
+ for (i = 0; i < 6; i++)
+ {
+ previous_char.stat[i] = p_ptr->stat_max[i];
+ }
+ previous_char.luck = p_ptr->luck_base;
+
+ /* Save the chaos patron */
+ previous_char.chaos_patron = p_ptr->chaos_patron;
+
+ /* Save the weapon specialty */
+ previous_char.weapon = 0;
+
+ /* Save the history */
+ for (i = 0; i < 4; i++)
+ {
+ strcpy(previous_char.history[i], history[i]);
+ }
+}
+
+
+/*
+ * Load the previous data
+ */
+static void load_prev_data(bool_ save)
+{
+ int i;
+
+ birther temp;
+
+
+ /*** Save the current data ***/
+
+ /* Save the data */
+ temp.age = p_ptr->age;
+ temp.wt = p_ptr->wt;
+ temp.ht = p_ptr->ht;
+ temp.sc = p_ptr->sc;
+ temp.au = p_ptr->au;
+
+ /* Save the stats */
+ for (i = 0; i < 6; i++)
+ {
+ temp.stat[i] = p_ptr->stat_max[i];
+ }
+ temp.luck = p_ptr->luck_base;
+
+ /* Save the chaos patron */
+ temp.chaos_patron = p_ptr->chaos_patron;
+
+ /* Save the weapon specialty */
+ temp.weapon = 0;
+
+ /* Save the history */
+ for (i = 0; i < 4; i++)
+ {
+ strcpy(temp.history[i], history[i]);
+ }
+
+
+ /*** Load the previous data ***/
+
+ /* Load the data */
+ p_ptr->age = previous_char.age;
+ p_ptr->wt = previous_char.wt;
+ p_ptr->ht = previous_char.ht;
+ p_ptr->sc = previous_char.sc;
+ p_ptr->au = previous_char.au;
+
+ /* Load the stats */
+ for (i = 0; i < 6; i++)
+ {
+ p_ptr->stat_max[i] = previous_char.stat[i];
+ p_ptr->stat_cur[i] = previous_char.stat[i];
+ }
+ p_ptr->luck_base = previous_char.luck;
+ p_ptr->luck_max = previous_char.luck;
+
+ /* Load the chaos patron */
+ p_ptr->chaos_patron = previous_char.chaos_patron;
+
+ /* Load the history */
+ for (i = 0; i < 4; i++)
+ {
+ strcpy(history[i], previous_char.history[i]);
+ }
+
+
+ /*** Save the current data ***/
+ if (!save) return;
+
+ /* Save the data */
+ previous_char.age = temp.age;
+ previous_char.wt = temp.wt;
+ previous_char.ht = temp.ht;
+ previous_char.sc = temp.sc;
+ previous_char.au = temp.au;
+
+ /* Save the stats */
+ for (i = 0; i < 6; i++)
+ {
+ previous_char.stat[i] = temp.stat[i];
+ }
+ previous_char.luck = temp.luck;
+
+ /* Save the chaos patron */
+ previous_char.chaos_patron = temp.chaos_patron;
+
+ /* Save the weapon specialty */
+ previous_char.weapon = temp.weapon;
+
+ /* Save the history */
+ for (i = 0; i < 4; i++)
+ {
+ strcpy(previous_char.history[i], temp.history[i]);
+ }
+}
+
+
+
+
+/*
+ * Returns adjusted stat -JK- Algorithm by -JWT-
+ *
+ * auto_roll is boolean and states maximum changes should be used rather
+ * than random ones to allow specification of higher values to wait for
+ *
+ * The "p_ptr->maximize" code is important -BEN-
+ */
+static int adjust_stat(int value, int amount, int auto_roll)
+{
+ int i;
+
+
+ /* Negative amounts */
+ if (amount < 0)
+ {
+ /* Apply penalty */
+ for (i = 0; i < (0 - amount); i++)
+ {
+ if (value >= 18 + 10)
+ {
+ value -= 10;
+ }
+ else if (value > 18)
+ {
+ value = 18;
+ }
+ else if (value > 3)
+ {
+ value--;
+ }
+ }
+ }
+
+ /* Positive amounts */
+ else if (amount > 0)
+ {
+ /* Apply reward */
+ for (i = 0; i < amount; i++)
+ {
+ if (value < 18)
+ {
+ value++;
+ }
+ else if (p_ptr->maximize)
+ {
+ value += 10;
+ }
+ else if (value < 18 + 70)
+ {
+ value += ((auto_roll ? 15 : randint(15)) + 5);
+ }
+ else if (value < 18 + 90)
+ {
+ value += ((auto_roll ? 6 : randint(6)) + 2);
+ }
+ else if (value < 18 + 100)
+ {
+ value++;
+ }
+ }
+ }
+
+ /* Return the result */
+ return (value);
+}
+
+
+
+
+/*
+ * Roll for a characters stats
+ *
+ * For efficiency, we include a chunk of "calc_bonuses()".
+ */
+static void get_stats(void)
+{
+ int i, j;
+
+ int bonus;
+
+ int dice[18];
+
+
+ /* Roll and verify some stats */
+ while (TRUE)
+ {
+ /* Roll some dice */
+ for (j = i = 0; i < 18; i++)
+ {
+ /* Roll the dice */
+ dice[i] = randint(3 + i % 3);
+
+ /* Collect the maximum */
+ j += dice[i];
+ }
+
+ /*
+ * Verify totals
+ *
+ * 57 was 54... I hate 'magic numbers' :< TY
+ *
+ * (d3 + d4 + d5) ~= 7.5 (+- 4.5)
+ * with 5 makes avg. stat value of 12.5 (min 8, max 17)
+ *
+ * (d3 + d4 + d5) x 6 ~= 45 (+- 18)
+ *
+ * So the original value (still used by Vanilla as of 2.9.3)
+ * allows (avg - 2)..(avg + 8), while this Z version
+ * (avg - 2)..(avg + 11). I don't understand what TY meant
+ * by "magic numbers", but I like big stats :) -- pelpel
+ *
+ */
+ if ((j > 42) && (j < 57)) break;
+ }
+
+ /* Acquire the stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Extract 5 + 1d3 + 1d4 + 1d5 */
+ j = 5 + dice[3 * i] + dice[3 * i + 1] + dice[3 * i + 2];
+
+ /* Save that value */
+ p_ptr->stat_max[i] = j;
+
+ /* Obtain a "bonus" for "race" and "class" */
+ bonus = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i];
+
+ /* Variable stat maxes */
+ if (p_ptr->maximize)
+ {
+ /* Start fully healed */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i];
+
+ /* Efficiency -- Apply the racial/class bonuses */
+ stat_use[i] = modify_stat_value(p_ptr->stat_max[i], bonus);
+ }
+
+ /* Fixed stat maxes */
+ else
+ {
+ /* Apply the bonus to the stat (somewhat randomly) */
+ stat_use[i] = adjust_stat(p_ptr->stat_max[i], bonus, FALSE);
+
+ /* Save the resulting stat maximum */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stat_use[i];
+ }
+
+ /* No temporary drain (yet...) */
+ p_ptr->stat_cnt[i] = 0;
+ p_ptr->stat_los[i] = 0;
+ }
+
+ /* Get luck */
+ p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5);
+ p_ptr->luck_max = p_ptr->luck_base;
+}
+
+
+/*
+ * Roll for some info that the auto-roller ignores
+ */
+static void get_extra(void)
+{
+ int i, j, min_value, max_value;
+
+#ifdef SHOW_LIFE_RATE
+
+ int percent;
+
+#endif
+
+
+ /* Level one */
+ p_ptr->max_plv = p_ptr->lev = 1;
+
+ /* Experience factor */
+ p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp;
+
+ /* Initialize arena and rewards information -KMW- */
+ p_ptr->arena_number = 0;
+ p_ptr->inside_arena = 0;
+ p_ptr->inside_quest = 0;
+ p_ptr->exit_bldg = TRUE; /* only used for arena now -KMW- */
+
+ /* Hitdice */
+ p_ptr->hitdie = rp_ptr->r_mhp + rmp_ptr->r_mhp + cp_ptr->c_mhp;
+
+ /* Initial hitpoints */
+ p_ptr->mhp = p_ptr->hitdie;
+
+ /* Minimum hitpoints at highest level */
+ min_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 3) / 8;
+ min_value += PY_MAX_LEVEL;
+
+ /* Maximum hitpoints at highest level */
+ max_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 5) / 8;
+ max_value += PY_MAX_LEVEL;
+
+ /* Pre-calculate level 1 hitdice */
+ player_hp[0] = p_ptr->hitdie;
+
+ /* Roll out the hitpoints */
+ while (TRUE)
+ {
+ /* Roll the hitpoint values */
+ for (i = 1; i < PY_MAX_LEVEL; i++)
+ {
+ j = randint(p_ptr->hitdie);
+ player_hp[i] = player_hp[i - 1] + j;
+ }
+
+ /* XXX Could also require acceptable "mid-level" hitpoints */
+
+ /* Require "valid" hitpoints at highest level */
+ if (player_hp[PY_MAX_LEVEL - 1] < min_value) continue;
+ if (player_hp[PY_MAX_LEVEL - 1] > max_value) continue;
+
+ /* Acceptable */
+ break;
+ }
+
+ p_ptr->tactic = 4;
+ p_ptr->movement = 4;
+
+#ifdef SHOW_LIFE_RATE
+
+ percent = (int)(((long)player_hp[PY_MAX_LEVEL - 1] * 200L) /
+ (p_ptr->hitdie + ((PY_MAX_LEVEL - 1) * p_ptr->hitdie)));
+
+ msg_format("Current Life Rating is %d/100.", percent);
+ msg_print(NULL);
+
+#endif /* SHOW_LIFE_RATE */
+}
+
+
+/*
+ * Get the racial history, and social class, using the "history charts".
+ */
+static void get_history(void)
+{
+ int i, n, chart, roll, social_class;
+
+ char *s, *t;
+
+ char buf[240];
+
+
+ /* Clear the previous history strings */
+ for (i = 0; i < 4; i++) history[i][0] = '\0';
+
+ /* Clear the history text */
+ buf[0] = '\0';
+
+ /* Initial social class */
+ social_class = randint(4);
+
+ /* Starting place */
+ chart = rp_ptr->chart;
+
+ /* Process the history */
+ while (chart)
+ {
+ /* Start over */
+ i = 0;
+
+ /* Roll for nobility */
+ roll = randint(100);
+
+
+ /* Access the proper entry in the table */
+ while ((chart != bg[i].chart) || (roll > bg[i].roll)) i++;
+
+ /* Acquire the textual history */
+ (void)strcat(buf, bg[i].info + rp_text);
+
+ /* Add in the social class */
+ social_class += (int)(bg[i].bonus) - 50;
+
+ /* Enter the next chart */
+ chart = bg[i].next;
+ }
+
+
+
+ /* Verify social class */
+ if (social_class > 100) social_class = 100;
+ else if (social_class < 1) social_class = 1;
+
+ /* Save the social class */
+ p_ptr->sc = social_class;
+
+
+ /* Skip leading spaces */
+ for (s = buf; *s == ' '; s++) /* loop */;
+
+ /* Get apparent length */
+ n = strlen(s);
+
+ /* Kill trailing spaces */
+ while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0';
+
+
+ /* Start at first line */
+ i = 0;
+
+ /* Collect the history */
+ while (TRUE)
+ {
+ /* Extract remaining length */
+ n = strlen(s);
+
+ /* All done */
+ if (n < 60)
+ {
+ /* Save one line of history */
+ strcpy(history[i++], s);
+
+ /* All done */
+ break;
+ }
+
+ /* Find a reasonable break-point */
+ for (n = 60; ((n > 0) && (s[n - 1] != ' ')); n--) /* loop */;
+
+ /* Save next location */
+ t = s + n;
+
+ /* Wipe trailing spaces */
+ while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0';
+
+ /* Save one line of history */
+ strcpy(history[i++], s);
+
+ /* Start next line */
+ for (s = t; *s == ' '; s++) /* loop */;
+ }
+}
+
+
+/*
+ * Fill the random_artifacts array with relevant info.
+ */
+errr init_randart(void)
+{
+ int i;
+
+ long cost;
+
+ random_artifact* ra_ptr;
+
+ char buf[80];
+
+
+ for (i = 0; i < MAX_RANDARTS; i++)
+ {
+ ra_ptr = &random_artifacts[i];
+
+ strcpy(ra_ptr->name_short,
+ get_line("rart_s.txt", ANGBAND_DIR_FILE, buf, i));
+ strcpy(ra_ptr->name_full,
+ get_line("rart_f.txt", ANGBAND_DIR_FILE, buf, i));
+
+ ra_ptr->attr = randint(15);
+ ra_ptr->activation = rand_int(MAX_T_ACT);
+ ra_ptr->generated = FALSE;
+
+ cost = randnor(0, 250);
+
+ if (cost < 0) cost = 0;
+
+ ra_ptr->cost = cost;
+ }
+
+ return 0;
+}
+
+
+/*
+ * A helper function for get_ahw(), also called by polymorph code
+ */
+void get_height_weight(void)
+{
+ int h_mean, h_stddev;
+
+ int w_mean, w_stddev;
+
+
+ /* Extract mean and standard deviation -- Male */
+ if (p_ptr->psex == SEX_MALE)
+ {
+ h_mean = rp_ptr->m_b_ht + rmp_ptr->m_b_ht;
+ h_stddev = rp_ptr->m_m_ht + rmp_ptr->m_m_ht;
+
+ w_mean = rp_ptr->m_b_wt + rmp_ptr->m_b_wt;
+ w_stddev = rp_ptr->m_m_wt + rmp_ptr->m_m_wt;
+ }
+
+ /* Female */
+ else if (p_ptr->psex == SEX_FEMALE)
+ {
+ h_mean = rp_ptr->f_b_ht + rmp_ptr->f_b_ht;
+ h_stddev = rp_ptr->f_m_ht + rmp_ptr->f_m_ht;
+
+ w_mean = rp_ptr->f_b_wt + rmp_ptr->f_b_wt;
+ w_stddev = rp_ptr->f_m_wt + rmp_ptr->f_m_wt;
+ }
+
+ /* Neuter XXX */
+ else
+ {
+ h_mean = (rp_ptr->m_b_ht + rmp_ptr->m_b_ht +
+ rp_ptr->f_b_ht + rmp_ptr->f_b_ht) / 2,
+ h_stddev = (rp_ptr->m_m_ht + rmp_ptr->m_m_ht +
+ rp_ptr->f_m_ht + rmp_ptr->f_m_ht) / 2;
+
+ w_mean = (rp_ptr->m_b_wt + rmp_ptr->m_b_wt +
+ rp_ptr->f_b_wt + rmp_ptr->f_b_wt) / 2,
+ w_stddev = (rp_ptr->m_m_wt + rmp_ptr->m_m_wt +
+ rp_ptr->f_m_wt + rmp_ptr->f_m_wt) / 2;
+ }
+
+ /* Calculate height/weight */
+ p_ptr->ht = randnor(h_mean, h_stddev);
+ p_ptr->wt = randnor(w_mean, w_stddev);
+
+ /* Weight/height shouldn't be negative */
+ if (p_ptr->ht < 1) p_ptr->ht = 1;
+ if (p_ptr->wt < 1) p_ptr->wt = 1;
+}
+
+
+/*
+ * Computes character's age, height, and weight
+ */
+static void get_ahw(void)
+{
+ /* Calculate the age */
+ p_ptr->age = rp_ptr->b_age + rmp_ptr->b_age +
+ randint(rp_ptr->m_age + rmp_ptr->m_age);
+
+ /* Calculate the height/weight */
+ get_height_weight();
+}
+
+
+
+
+/*
+ * Get the player's starting money
+ */
+static void get_money(void)
+{
+ int i, gold;
+
+
+ /* Social Class determines starting gold */
+ gold = (p_ptr->sc * 6) + randint(100) + 300;
+
+ /* Process the stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Mega-Hack -- reduce gold for high stats */
+ if (stat_use[i] >= 18 + 50) gold -= 300;
+ else if (stat_use[i] >= 18 + 20) gold -= 200;
+ else if (stat_use[i] > 18) gold -= 150;
+ else gold -= (stat_use[i] - 8) * 10;
+ }
+
+ /* Minimum 100 gold */
+ if (gold < 100) gold = 100;
+
+ /* Save the gold */
+ p_ptr->au = gold;
+}
+
+
+
+/*
+ * Display stat values, subset of "put_stats()"
+ *
+ * See 'display_player()' for basic method.
+ */
+static void birth_put_stats(void)
+{
+ int i, p;
+
+ byte attr;
+
+ char buf[80];
+
+
+ /* Put the stats (and percents) */
+ for (i = 0; i < 6; i++)
+ {
+ /* Put the stat */
+ cnv_stat(p_ptr->stat_use[i], buf);
+ c_put_str(TERM_L_GREEN, buf, 2 + i, 66);
+
+ /* Put the percent */
+ if (stat_match[i])
+ {
+ p = 1000L * stat_match[i] / auto_round;
+ attr = (p < 100) ? TERM_YELLOW : TERM_L_GREEN;
+ strnfmt(buf, 80, "%3d.%d%%", p / 10, p % 10);
+ c_put_str(attr, buf, 2 + i, 73);
+ }
+
+ /* Never happened */
+ else
+ {
+ c_put_str(TERM_RED, "(NONE)", 2 + i, 73);
+ }
+ }
+}
+
+
+/*
+ * Clear all the global "character" data
+ */
+static void player_wipe(void)
+{
+ int i, j;
+
+ bool_ *powers;
+ bool_ *corruptions;
+
+
+ /* Wipe special levels */
+ wipe_saved();
+
+ /* Save the powers & corruptions */
+ powers = p_ptr->powers;
+ corruptions = p_ptr->corruptions;
+
+ /* Hack -- zero the struct */
+ p_ptr = WIPE(p_ptr, player_type);
+
+ /* Restore the powers & corruptions */
+ p_ptr->powers = powers;
+ p_ptr->corruptions = corruptions;
+
+ /* Not dead yet */
+ p_ptr->lives = 0;
+
+ /* Wipe the corruptions */
+ (void)C_WIPE(p_ptr->corruptions, max_corruptions, bool_);
+
+ /* Wipe the history */
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 60; j++)
+ {
+ if (j < 59) history[i][j] = ' ';
+ else history[i][j] = '\0';
+ }
+ }
+
+ /* Wipe the towns */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ for (j = 0; j < MAX_DUNGEON_DEPTH; j++)
+ {
+ special_lvl[j][i] = 0;
+ }
+ }
+
+ /* Wipe the towns */
+ for (i = max_real_towns + 1; i < max_towns; i++)
+ {
+ town_info[i].flags = 0;
+ }
+
+ /* Wipe the quests */
+ for (i = 0; i < MAX_Q_IDX_INIT; i++)
+ {
+ quest[i].status = QUEST_STATUS_UNTAKEN;
+ for (j = 0; j < 4; j++)
+ {
+ quest[i].data[j] = 0;
+ }
+ }
+
+ /* Wipe the rune spells */
+ rune_num = 0;
+ for (i = 0; i < MAX_RUNES; i++)
+ {
+ strcpy(rune_spells[i].name, "");
+ rune_spells[i].type = 0;
+ rune_spells[i].rune2 = 0;
+ rune_spells[i].mana = 0;
+ }
+
+ /* No items */
+ inven_cnt = 0;
+ equip_cnt = 0;
+
+ /* Clear the inventory */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_wipe(&p_ptr->inventory[i]);
+ }
+
+ /* Generate random artifacts */
+ init_randart();
+
+ /* Start with no artifacts made yet */
+ for (i = 0; i < max_a_idx; i++)
+ {
+ artifact_type *a_ptr = &a_info[i];
+ a_ptr->cur_num = 0;
+ }
+
+ /* Reset the "objects" */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Reset "tried" */
+ k_ptr->tried = FALSE;
+
+ /* Reset "aware" */
+ k_ptr->aware = FALSE;
+
+ /* Reset "know" */
+ k_ptr->know = FALSE;
+
+ /* Reset "artifact" */
+ k_ptr->artifact = 0;
+ }
+
+
+ /* Reset the "monsters" */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Hack -- Reset the counter */
+ r_ptr->cur_num = 0;
+
+ /* Hack -- Reset the max counter */
+ r_ptr->max_num = 100;
+
+ /* Hack -- Reset the max counter */
+ if (r_ptr->flags1 & RF1_UNIQUE) r_ptr->max_num = 1;
+ if (r_ptr->flags3 & RF3_UNIQUE_4) r_ptr->max_num = 4;
+
+ /* Clear player kills */
+ r_ptr->r_pkills = 0;
+
+ /* Clear saved flag */
+ r_ptr->on_saved = FALSE;
+ }
+
+
+ /* Hack -- Well fed player */
+ p_ptr->food = PY_FOOD_FULL - 1;
+
+ /* Wipe the alchemists' recipes */
+ for ( i = 0 ; i < 32 ; i++)
+ alchemist_known_egos[i] = 0;
+ for ( i = 0 ; i < 6 ; i++)
+ alchemist_known_artifacts[i] = 0;
+ alchemist_gained = 0;
+
+ /* Clear "cheat" options */
+ cheat_peek = FALSE;
+ cheat_hear = FALSE;
+ cheat_room = FALSE;
+ cheat_xtra = FALSE;
+ cheat_know = FALSE;
+ cheat_live = FALSE;
+
+ /* Assume no winning game */
+ total_winner = 0;
+ has_won = FALSE;
+
+ /* Assume no cheating */
+ noscore = 0;
+ wizard = 0;
+
+ /* Assume no innate spells */
+ spell_num = 0;
+
+ /* Clear the fate */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ fates[i].fate = 0;
+ }
+ p_ptr->no_mortal = FALSE;
+
+ /* Player don't have the black breath from the beginning !*/
+ p_ptr->black_breath = FALSE;
+
+ /* Default pet command settings */
+ p_ptr->pet_follow_distance = 6;
+ p_ptr->pet_open_doors = FALSE;
+ p_ptr->pet_pickup_items = FALSE;
+
+ /* Body changing initialisation */
+ p_ptr->body_monster = 0;
+ p_ptr->disembodied = FALSE;
+
+ /* Wipe the bounties */
+ total_bounties = 0;
+
+ /* Wipe spells */
+ p_ptr->xtra_spells = 0;
+
+ /* Wipe xtra hp */
+ p_ptr->hp_mod = 0;
+
+ /* Wipe the monsters */
+ wipe_m_list();
+
+ /* Wipe the doppleganger */
+ doppleganger = 0;
+
+ /* Wipe the recall depths */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ max_dlv[i] = 0;
+ }
+
+ /* Wipe the known inscription list */
+ for (i = 0; i < MAX_INSCRIPTIONS; i++)
+ {
+ inscription_info[i].know = FALSE;
+ }
+
+ /* Wipe the known traps list */
+ for (i = 0; i < max_t_idx; i++)
+ {
+ t_info[i].known = 0;
+ t_info[i].ident = FALSE;
+ }
+
+ /* Reset wild_mode to FALSE */
+ p_ptr->wild_mode = FALSE;
+ p_ptr->old_wild_mode = FALSE;
+
+ /* Initialize allow_one_death */
+ p_ptr->allow_one_death = 0;
+
+ p_ptr->loan = p_ptr->loan_time = 0;
+
+ /* Wipe the power list */
+ for (i = 0; i < POWER_MAX_INIT; i++)
+ {
+ p_ptr->powers_mod[i] = 0;
+ }
+
+ /* No companions killed */
+ p_ptr->companion_killed = 0;
+}
+
+
+/* Create an object */
+void outfit_obj(int tv, int sv, int pval, int dd, int ds)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get local object */
+ q_ptr = &forge;
+ q_ptr->pval = 0;
+ q_ptr->pval2 = 0;
+
+ /* Hack -- Give the player an object */
+ object_prep(q_ptr, lookup_kind(tv, sv));
+
+ if (pval)
+ q_ptr->pval = pval;
+
+ /* These objects are "storebought" */
+ q_ptr->ident |= IDENT_MENTAL;
+ q_ptr->number = damroll(dd, ds);
+
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+}
+
+
+/*
+ * Init players with some belongings
+ *
+ * Having an item makes the player "aware" of its purpose.
+ */
+static void player_outfit(void)
+{
+ int i;
+
+ /*
+ * Get an adventurer guide describing a bit of the
+ * wilderness.
+ */
+ {
+ object_type forge;
+ object_type *q_ptr = &forge;
+ /* Hack -- Give the player an adventurer guide */
+ object_prep(q_ptr, lookup_kind(TV_PARCHMENT, 20));
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ process_hooks(HOOK_BIRTH_OBJECTS, "()");
+
+ {
+ object_type forge;
+ object_type *q_ptr = &forge;
+ /* Hack -- Give the player some food */
+ object_prep(q_ptr, lookup_kind(TV_FOOD, SV_FOOD_RATION));
+ q_ptr->number = (byte)rand_range(3, 7);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ {
+ object_type forge;
+ object_type *q_ptr = &forge;
+ /* Hack -- Give the player some torches */
+ object_prep(q_ptr, lookup_kind(TV_LITE, SV_LITE_TORCH));
+ q_ptr->number = (byte)rand_range(3, 7);
+ q_ptr->timeout = rand_range(3, 7) * 500;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /* Rogues have a better knowledge of traps */
+ if (has_ability(AB_TRAPPING))
+ {
+ t_info[TRAP_OF_DAGGER_I].known = randint(50) + 50;
+ t_info[TRAP_OF_POISON_NEEDLE].known = randint(50) + 50;
+ t_info[TRAP_OF_FIRE_BOLT].known = randint(50) + 50;
+ t_info[TRAP_OF_DAGGER_I].ident = TRUE;
+ t_info[TRAP_OF_POISON_NEEDLE].ident = TRUE;
+ t_info[TRAP_OF_FIRE_BOLT].ident = TRUE;
+
+ /* Hack -- Give the player a some ammo for the traps */
+ object_type forge;
+ object_type *q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_SHOT, SV_AMMO_NORMAL));
+ q_ptr->number = (byte)rand_range(5, 15);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* These objects are "storebought" */
+ q_ptr->ident |= IDENT_MENTAL;
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /* Hack -- Give the player some useful objects */
+ for (i = 0; i < rp_ptr->obj_num; i++)
+ outfit_obj(rp_ptr->obj_tval[i], rp_ptr->obj_sval[i], rp_ptr->obj_pval[i], rp_ptr->obj_dd[i], rp_ptr->obj_ds[i]);
+ for (i = 0; i < rmp_ptr->obj_num; i++)
+ outfit_obj(rmp_ptr->obj_tval[i], rmp_ptr->obj_sval[i], rmp_ptr->obj_pval[i], rmp_ptr->obj_dd[i], rmp_ptr->obj_ds[i]);
+ for (i = 0; i < cp_ptr->obj_num; i++)
+ outfit_obj(cp_ptr->obj_tval[i], cp_ptr->obj_sval[i], cp_ptr->obj_pval[i], cp_ptr->obj_dd[i], cp_ptr->obj_ds[i]);
+ for (i = 0; i < cp_ptr->spec[p_ptr->pspec].obj_num; i++)
+ outfit_obj(cp_ptr->spec[p_ptr->pspec].obj_tval[i], cp_ptr->spec[p_ptr->pspec].obj_sval[i], cp_ptr->spec[p_ptr->pspec].obj_pval[i], cp_ptr->spec[p_ptr->pspec].obj_dd[i], cp_ptr->spec[p_ptr->pspec].obj_ds[i]);
+}
+
+
+/* Possible number(and layout) or random quests */
+#define MAX_RANDOM_QUESTS_TYPES ((8 * 3) + (8 * 1))
+int random_quests_types[MAX_RANDOM_QUESTS_TYPES] =
+{
+ 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */
+ 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */
+ 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */
+ 20, 13, 15, 16, 9, 17, 18, 8, /* Hero Sword Quest */
+};
+
+/* Enforce OoD monsters until this level */
+#define RQ_LEVEL_CAP 49
+
+static void gen_random_quests(int n)
+{
+ int step, lvl, i, k;
+ int old_type = dungeon_type;
+
+ /* Factor dlev value by 1000 to keep precision */
+ step = (98 * 1000) / n;
+
+ lvl = step / 2;
+
+ quest[QUEST_RANDOM].status = QUEST_STATUS_TAKEN;
+
+ for (i = 0; i < n; i++)
+ {
+ monster_race *r_ptr = &r_info[2];
+
+ int rl = (lvl / 1000) + 1;
+
+ int min_level;
+
+ int tries = 5000;
+
+ random_quest *q_ptr = &random_quests[rl];
+
+ int j;
+
+ /* Find the appropriate dungeon */
+ for (j = 0; j < max_d_idx; j++)
+ {
+ dungeon_info_type *d_ptr = &d_info[j];
+
+ if (!(d_ptr->flags1 & DF1_PRINCIPAL)) continue;
+
+ if ((d_ptr->mindepth <= rl) && (rl <= d_ptr->maxdepth))
+ {
+ dungeon_type = j;
+ break;
+ }
+ }
+
+ q_ptr->type = random_quests_types[rand_int(MAX_RANDOM_QUESTS_TYPES)];
+
+ /* XXX XXX XXX Try until valid choice is found */
+ while (tries)
+ {
+ bool_ ok;
+
+ tries--;
+
+ /* Random monster 5 - 10 levels out of depth */
+ q_ptr->r_idx = get_mon_num(rl + 4 + randint(6));
+
+ if (!q_ptr->r_idx) continue;
+
+ r_ptr = &r_info[q_ptr->r_idx];
+
+ /* Accept only monsters that can be generated */
+ if (r_ptr->flags9 & RF9_SPECIAL_GENE) continue;
+ if (r_ptr->flags9 & RF9_NEVER_GENE) continue;
+
+ /* Accept only monsters that are not breeders */
+ if (r_ptr->flags4 & RF4_MULTIPLY) continue;
+
+ /* Forbid joke monsters */
+ if (r_ptr->flags8 & RF8_JOKEANGBAND) continue;
+
+ /* Accept only monsters that are not friends */
+ if (r_ptr->flags7 & RF7_PET) continue;
+
+ /* Refuse nazguls */
+ if (r_ptr->flags7 & RF7_NAZGUL) continue;
+
+ /* Accept only monsters that are not good */
+ if (r_ptr->flags3 & RF3_GOOD) continue;
+
+ /* Assume no explosion attacks */
+ ok = TRUE;
+
+ /* Reject monsters with exploding attacks */
+ for (k = 0; k < 4; k++)
+ {
+ if (r_ptr->blow[k].method == RBM_EXPLODE) ok = FALSE;
+ }
+ if (!ok) continue;
+
+ /* No mutliple uniques */
+ if ((r_ptr->flags1 & RF1_UNIQUE) &&
+ ((q_ptr->type != 1) || (r_ptr->max_num == -1))) continue;
+
+ /* No single non uniques */
+ if ((!(r_ptr->flags1 & RF1_UNIQUE)) && (q_ptr->type == 1)) continue;
+
+ /* Level restriction */
+ min_level = (rl > RQ_LEVEL_CAP) ? RQ_LEVEL_CAP : rl;
+
+ /* Accept monsters matching the level restriction */
+ if (r_ptr->level > min_level) break;
+ }
+
+ /* Arg could not find anything ??? */
+ if (!tries)
+ {
+ if (wizard) message_add(MESSAGE_MSG, format("Could not find quest monster on lvl %d", rl), TERM_RED);
+ q_ptr->type = 0;
+ }
+ else
+ {
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ r_ptr->max_num = -1;
+ }
+
+ q_ptr->done = FALSE;
+
+ if (wizard) message_add(MESSAGE_MSG,
+ format("Quest for %d on lvl %d",
+ q_ptr->r_idx, rl), TERM_RED);
+ }
+
+ lvl += step;
+ }
+
+ dungeon_type = old_type;
+}
+
+int dump_classes(s16b *classes, int sel, u32b *restrictions)
+{
+ int n = 0;
+
+ char buf[80];
+ char *desc;
+
+ cptr str;
+
+ C_MAKE(desc, c_head->text_size, char);
+
+ /* Clean up */
+ clear_from(12);
+
+ while (classes[n] != -1)
+ {
+ cptr mod = "";
+ char p2 = ')', p1 = ' ';
+
+ /* Analyze */
+ p_ptr->pclass = classes[n];
+ cp_ptr = &class_info[p_ptr->pclass];
+ str = cp_ptr->title + c_name;
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s%s", p1,
+ (n <= 25) ? I2A(n) : I2D(n - 26), p2, str, mod);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ strnfmt(desc, c_head->text_size, "%s%s", cp_ptr->desc + c_text,
+ cp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : "");
+ print_desc(desc);
+
+ if (!(restrictions[classes[n] / 32] & BIT(classes[n])) ||
+ cp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ else
+ c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ }
+ else
+ {
+ if (!(restrictions[classes[n] / 32] & BIT(classes[n])) ||
+ cp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ else
+ put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ }
+ n++;
+ }
+
+ C_FREE(desc, c_head->text_size, char);
+
+ return (n);
+}
+
+int dump_specs(int sel)
+{
+ int n = 0;
+
+ char buf[80];
+ char *desc;
+
+ cptr str;
+
+ C_MAKE(desc, c_head->text_size, char);
+
+ /* Clean up */
+ clear_from(12);
+
+ for (n = 0; n < MAX_SPEC; n++)
+ {
+ char p2 = ')', p1 = ' ';
+
+ /* Found the last one ? */
+ if (!class_info[p_ptr->pclass].spec[n].title) break;
+
+ /* Analyze */
+ p_ptr->pspec = n;
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+ str = spp_ptr->title + c_name;
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ strnfmt(desc, c_head->text_size, "%s%s", spp_ptr->desc + c_text,
+ spp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : "");
+ print_desc(desc);
+
+ if (spp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ else
+ c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ }
+ else
+ {
+ if (spp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ else
+ put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ }
+ }
+
+ C_FREE(desc, c_head->text_size, char);
+
+ return (n);
+}
+
+int dump_races(int sel)
+{
+ int n = 0;
+
+ char buf[80];
+ char *desc;
+
+ cptr str;
+
+ C_MAKE(desc, rp_head->text_size, char);
+
+ /* Clean up */
+ clear_from(12);
+
+ for (n = 0; n < max_rp_idx; n++)
+ {
+ char p2 = ')', p1 = ' ';
+
+ /* Analyze */
+ p_ptr->prace = n;
+ rp_ptr = &race_info[p_ptr->prace];
+ str = rp_ptr->title + rp_name;
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ strnfmt(desc, rp_head->text_size, "%s%s", rp_ptr->desc + rp_text,
+ rp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : "");
+ print_desc(desc);
+
+ if (rp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ else
+ c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ }
+ else
+ {
+ if (rp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ else
+ put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ }
+ }
+
+ C_FREE(desc, rp_head->text_size, char);
+
+ return (n);
+}
+
+
+int dump_rmods(int sel, int *racem, int max)
+{
+ int n = 0;
+
+ char buf[80];
+ char *desc;
+
+ cptr str;
+
+ C_MAKE(desc, rmp_head->text_size, char);
+
+ /* Clean up */
+ clear_from(12);
+
+ /* Dump races */
+ for (n = 0; n < max; n++)
+ {
+ char p2 = ')', p1 = ' ';
+
+ /* Analyze */
+ p_ptr->pracem = racem[n];
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+ str = rmp_ptr->title + rmp_name;
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ if (racem[n])
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str);
+ else
+ strnfmt(buf, 80, "%c%c%c Classical", p1, I2A(n), p2);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ strnfmt(desc, rmp_head->text_size, "%s%s", rmp_ptr->desc + rmp_text,
+ rmp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : "");
+ print_desc(desc);
+
+ if (rmp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ else
+ c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ }
+ else
+ {
+ if (rmp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ else
+ put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ }
+ }
+
+ C_FREE(desc, rmp_head->text_size, char);
+
+ return (n);
+}
+
+int dump_gods(int sel, int *choice, int max)
+{
+ int i, j;
+ char buf[80];
+ cptr str;
+
+ /* Clean up */
+ clear_from(12);
+
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ "You can choose to worship a god, some class must start with a god.");
+
+ for (i = 0; i < max; i++)
+ {
+ char p2 = ')', p1 = ' ';
+ int n = choice[i];
+ deity_type *g_ptr = &deity_info[0];
+
+ if (!n) str = "No God";
+ else
+ {
+ g_ptr = &deity_info[n];
+ str = g_ptr->name;
+ }
+
+ if (sel == i)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(i), p2, str);
+
+ /* Print some more info */
+ if (sel == i)
+ {
+ if (n)
+ {
+ /* Display the first four lines of the god description */
+ for (j = 0; j < 4; j++)
+ if (strcmp(g_ptr->desc[j], ""))
+ print_desc_aux(g_ptr->desc[j], 12 + j, 1);
+ }
+ else print_desc("You can begin as an atheist and still convert to a god later.");
+
+ c_put_str(TERM_L_BLUE, buf, 20 + (i / 4), 1 + 20 * (i % 4));
+ }
+ else
+ {
+ put_str(buf, 20 + (i / 4), 1 + 20 * (i % 4));
+ }
+ }
+
+ return (max);
+}
+
+
+/* Ask questions */
+static bool_ do_quick_start = FALSE;
+
+static bool_ player_birth_aux_ask()
+{
+ int i, k, n, v, sel;
+
+ s32b tmp;
+
+ int racem[100], max_racem = 0;
+
+ u32b restrictions[2];
+
+ cptr str;
+
+ char c;
+
+ char p2 = ')';
+
+ char buf[200];
+ char inp[200];
+
+ s16b *class_types;
+
+ s32b allow_quest;
+
+ /*** Intro ***/
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Title everything */
+ put_str("Name :", 2, 1);
+ put_str("Sex :", 3, 1);
+ put_str("Race :", 4, 1);
+ put_str("Class :", 5, 1);
+
+ /* Dump the default name */
+ c_put_str(TERM_L_BLUE, player_name, 2, 9);
+
+
+ /*** Instructions ***/
+
+ /* Display some helpful information */
+ Term_putstr(5, 8, -1, TERM_WHITE,
+ "Please answer the following questions. Most of the questions");
+ Term_putstr(5, 9, -1, TERM_WHITE,
+ "display a set of standard answers, and many will also accept");
+ Term_putstr(5, 10, -1, TERM_WHITE,
+ "some special responses, including 'Q' to quit, 'S' to restart,");
+ Term_putstr(5, 11, -1, TERM_WHITE,
+ "and '?' for help. Note that 'Q' and 'S' must be capitalized.");
+
+
+ /*** Quick Start ***/
+
+ if (previous_char.quick_ok)
+ {
+ /* Extra info */
+ Term_putstr(1, 15, -1, TERM_WHITE,
+ "Do you want to use the quick start function(same character as your last one).");
+
+ /* Choose */
+ while (1)
+ {
+ put_str("Use quick start (y/n)?", 20, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ else if (c == 'S') return (FALSE);
+ else if ((c == 'y') || (c == 'Y'))
+ {
+ do_quick_start = TRUE;
+ break;
+ }
+ else
+ {
+ do_quick_start = FALSE;
+ break;
+ }
+ }
+ }
+
+ /* Clean up */
+ clear_from(15);
+
+ /*** Player sex ***/
+
+ if (do_quick_start)
+ {
+ k = previous_char.sex;
+ }
+ else
+ {
+ /* Extra info */
+ Term_putstr(5, 15, -1, TERM_WHITE,
+ "Your 'sex' does not have any significant gameplay effects.");
+
+ /* Prompt for "Sex" */
+ for (n = 0; n < MAX_SEXES; n++)
+ {
+ /* Analyze */
+ p_ptr->psex = n;
+ sp_ptr = &sex_info[p_ptr->psex];
+ str = sp_ptr->title;
+
+ /* Display */
+ strnfmt(buf, 200, "%c%c %s", I2A(n), p2, str);
+ put_str(buf, 21 + (n / 5), 2 + 15 * (n % 5));
+ }
+
+ /* Choose */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a sex (%c-%c), * for random, = for options: ", I2A(0), I2A(n - 1));
+ put_str(buf, 20, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = rand_int(MAX_SEXES);
+ break;
+ }
+ k = (islower(c) ? A2I(c) : -1);
+ if ((k >= 0) && (k < n)) break;
+ if (c == '?') do_cmd_help();
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else bell();
+ }
+ }
+
+ /* Set sex */
+ p_ptr->psex = k;
+ sp_ptr = &sex_info[p_ptr->psex];
+ str = sp_ptr->title;
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, str, 3, 9);
+
+ /* Clean up */
+ clear_from(15);
+
+
+ /*** Player race ***/
+
+ if (do_quick_start)
+ {
+ k = previous_char.race;
+ }
+ else
+ {
+ /* Only one choice = instant choice */
+ if (max_rp_idx == 1)
+ k = 0;
+ else
+ {
+ /* Extra info */
+ Term_putstr(5, 16, -1, TERM_WHITE,
+ "Your 'race' determines various intrinsic factors and bonuses.");
+ hack_corruption = FALSE;
+
+ /* Dump races */
+ sel = 0;
+ n = dump_races(sel);
+
+ /* Choose */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a race (%c-%c), * for a random choice, = for options, 8/2/4/6 for movement: ",
+ I2A(0), I2A(max_rp_idx - 1));
+ put_str(buf, 17, 2);
+
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = rand_int(max_rp_idx);
+ break;
+ }
+ k = (islower(c) ? A2I(c) : -1);
+ if ((k >= 0) && (k < n)) break;
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'race', '%s')", race_info[sel].title + rp_name));
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 5;
+ if (sel >= n) sel %= 5;
+ dump_races(sel);
+ }
+ else if (c == '8')
+ {
+ sel -= 5;
+ if (sel < 0) sel = n - 1 -( ( -sel) % 5);
+ /* C's modulus operator does not have defined
+ results for negative first values. Damn. */
+ dump_races(sel);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_races(sel);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_races(sel);
+ }
+ else if (c == '\r')
+ {
+ k = sel;
+ break;
+ }
+ else bell();
+ }
+ }
+ }
+ /* Set race */
+ p_ptr->prace = k;
+ rp_ptr = &race_info[p_ptr->prace];
+ str = rp_ptr->title + rp_name;
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, str, 4, 9);
+
+ /* Get a random name */
+ if (!do_quick_start) create_random_name(p_ptr->prace, player_name);
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, player_name, 2, 9);
+
+ /* Clean up */
+ clear_from(12);
+
+
+ /*** Player race mod ***/
+ if (do_quick_start)
+ {
+ k = previous_char.rmod;
+ p_ptr->pracem = k;
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+ }
+ else
+ {
+ /* Only one choice = instant choice */
+ if (max_rmp_idx == 1)
+ k = 0;
+ else
+ {
+ for (n = 0; n < 100; n++) racem[n] = 0;
+
+ max_racem = 0;
+ for (n = 0; n < max_rmp_idx; n++)
+ {
+ /* Analyze */
+ p_ptr->pracem = n;
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+
+ /* Must be an ok choice */
+ if (!(BIT(p_ptr->prace) & rmp_ptr->choice[p_ptr->prace / 32])) continue;
+
+ /* Ok thats a possibility */
+ racem[max_racem++] = n;
+ }
+
+ /* Ah ! nothing found, lets use the default */
+ if (!max_racem) p_ptr->pracem = 0;
+ /* Only one ? use it */
+ else if (max_racem == 1) p_ptr->pracem = racem[0];
+ /* We got to ask the player */
+ else
+ {
+ /* Extra info */
+ Term_putstr(5, 15, -1, TERM_WHITE,
+ "Your 'race modifier' determines various intrinsic factors and bonuses.");
+
+ /* Dump races */
+ sel = 0;
+ n = dump_rmods(sel, racem, max_racem);
+
+ /* Choose */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a race modifier (%c-%c), * for a random choice, = for options: ",
+ I2A(0), I2A(max_racem - 1));
+ put_str(buf, 17, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ do
+ {
+ k = rand_int(max_racem);
+ }
+ while (!(BIT(racem[k]) & rmp_ptr->choice[racem[k] / 32]));
+ break;
+ }
+ else if (c == '?') exec_lua(format("ingame_help('select_context', 'subrace', '%s')", race_mod_info[racem[sel]].title + rmp_name));
+
+ k = (islower(c) ? A2I(c) : -1);
+ if ((k >= 0) && (k < max_racem) &&
+ (BIT(p_ptr->prace) & race_mod_info[racem[k]].choice[p_ptr->prace / 32])) break;
+
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 5;
+ if (sel >= n) sel = sel - n + 1;
+ dump_rmods(sel, racem, max_racem);
+ }
+ else if (c == '8')
+ {
+ sel -= 5;
+ if (sel < 0) sel = n - 1 + sel;
+ dump_rmods(sel, racem, max_racem);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_rmods(sel, racem, max_racem);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_rmods(sel, racem, max_racem);
+ }
+ else if (c == '\r')
+ {
+ k = sel;
+ break;
+ }
+ else bell();
+ }
+
+ /* Set race */
+ p_ptr->pracem = racem[k];
+ }
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, get_player_race_name(p_ptr->prace, p_ptr->pracem), 4, 9);
+ }
+ }
+
+ /* Clean up */
+ clear_from(12);
+
+
+ /*** Player class ***/
+ if (do_quick_start)
+ {
+ k = previous_char.pclass;
+ p_ptr->pclass = k;
+ cp_ptr = &class_info[p_ptr->pclass];
+ k = previous_char.spec;
+ p_ptr->pspec = k;
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+ }
+ else
+ {
+ int z;
+
+ for (z = 0; z < 2; z++)
+ restrictions[z] = (rp_ptr->choice[z] | rmp_ptr->pclass[z]) & (~rmp_ptr->mclass[z]);
+
+ if (max_mc_idx > 1)
+ {
+ /* Extra info */
+ Term_putstr(5, 13, -1, TERM_WHITE,
+ "Your 'class' determines various intrinsic abilities and bonuses.");
+
+ /* Get a class type */
+ for (i = 0; i < max_mc_idx; i++)
+ c_put_str(meta_class_info[i].color, format("%c) %s", I2A(i), meta_class_info[i].name), 16 + i, 2);
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a class type (a-%c), * for random, = for options: ", I2A(max_mc_idx - 1));
+ put_str(buf, 15, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = rand_int(max_mc_idx);
+ break;
+ }
+ k = (islower(c) ? A2I(c) : (D2I(c) + 26));
+ if ((k >= 0) && (k < max_mc_idx)) break;
+ if (c == '?') do_cmd_help();
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else bell();
+ }
+ }
+ else
+ {
+ k = 0;
+ }
+ class_types = meta_class_info[k].classes;
+ clear_from(15);
+
+ /* Count classes */
+ n = 0;
+ while (class_types[n] != -1) n++;
+
+ /* Only one choice = instant choice */
+ if (n == 1)
+ k = 0;
+ else
+ {
+ /* Dump classes */
+ sel = 0;
+ n = dump_classes(class_types, sel, restrictions);
+
+ /* Get a class */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a class (%c-%c), * for random, = for options, 8/2/4 for up/down/back: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1));
+ put_str(buf, 15, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = randint(n) - 1;
+ break;
+ }
+ k = (islower(c) ? A2I(c) : (D2I(c) + 26));
+ if ((k >= 0) && (k < n)) break;
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'class', '%s')", class_info[class_types[sel]].title + c_name));
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 4;
+ if (sel >= n) sel %= 4;
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = n - 1 -( ( -sel) % 4);
+ /* C's modulus operator does not have defined
+ results for negative first values. Damn. */
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '\r')
+ {
+ k = sel;
+ break;
+ }
+ else bell();
+ }
+ }
+
+ /* Set class */
+ p_ptr->pclass = class_types[k];
+
+ /* Choose class spec */
+ clear_from(15);
+
+ /* Count choices */
+ for (n = 0; n < MAX_SPEC; n++)
+ {
+ /* Found the last one ? */
+ if (!class_info[p_ptr->pclass].spec[n].title) break;
+ }
+
+ /* Only one choice = auto choice */
+ if (n == 1)
+ k = 0;
+ else
+ {
+ /* Dump classes spec */
+ sel = 0;
+ n = dump_specs(sel);
+
+ /* Get a class */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a class specialisation (%c-%c), * for random, = for options, 8/2/4/6 for up/down/left/right: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1));
+ put_str(buf, 15, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = randint(n) - 1;
+ break;
+ }
+ k = (islower(c) ? A2I(c) : (D2I(c) + 26));
+ if ((k >= 0) && (k < n)) break;
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'class', '%s')", class_info[p_ptr->pclass].spec[sel].title + c_name));
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 4;
+ if (sel >= n) sel = sel - n + 1;
+ dump_specs(sel);
+ }
+ else if (c == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = n - 1 + sel;
+ dump_specs(sel);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_specs(sel);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_specs(sel);
+ }
+ else if (c == '\r')
+ {
+ k = sel;
+ break;
+ }
+ else bell();
+ }
+ }
+
+ /* Set class spec */
+ p_ptr->pspec = k;
+ }
+ cp_ptr = &class_info[p_ptr->pclass];
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+ str = spp_ptr->title + c_name;
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, str, 5, 9);
+
+ /* Clean up */
+ clear_from(15);
+
+ /*** Player god ***/
+ if (do_quick_start)
+ {
+ k = previous_char.god;
+ p_ptr->pgod = k;
+ set_grace(previous_char.grace);
+ }
+ else if (PRACE_FLAG(PR1_NO_GOD))
+ {
+ p_ptr->pgod = GOD_NONE;
+ }
+ else
+ {
+ int *choice;
+ int max = 0;
+
+ C_MAKE(choice, max_gods, int);
+
+ /* Get the list of possible gods */
+ for (n = 0; n < max_gods; n++)
+ {
+ if ((cp_ptr->gods | spp_ptr->gods) & BIT(n))
+ choice[max++] = n;
+ }
+
+ if (!max)
+ {
+ p_ptr->pgod = GOD_NONE;
+ }
+ else if (max == 1)
+ {
+ p_ptr->pgod = choice[0];
+ }
+ else if (max > 1)
+ {
+ sel = 0;
+ n = dump_gods(sel, choice, max);
+
+ /* Choose */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a god (%c-%c), * for a random choice, "
+ "= for options, 8/2/4/6 for movement: ",
+ I2A(0), I2A(max - 1));
+ put_str(buf, 19, 2);
+
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S')
+ {
+ C_FREE(choice, max_gods, int);
+
+ return (FALSE);
+ }
+ if (c == '*')
+ {
+ k = choice[randint(max) - 1];
+ break;
+ }
+ k = (islower(c) ? A2I(c) : -1);
+ if ((k >= 0) && (k < max))
+ {
+ k = choice[k];
+ break;
+ }
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'god', '%s')", deity_info[choice[sel]].name));
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 4;
+ if (sel >= n) sel %= 4;
+ dump_gods(sel, choice, max);
+ }
+ else if (c == '8')
+ {
+ sel -= 4;
+ /* C's modulus operator does not have defined
+ results for negative first values. Damn. */
+ if (sel < 0) sel = n - 1 -( ( -sel) % 4);
+ dump_gods(sel, choice, max);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_gods(sel, choice, max);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_gods(sel, choice, max);
+ }
+ else if (c == '\r')
+ {
+ k = choice[sel];
+ break;
+ }
+ else bell();
+ }
+
+ C_FREE(choice, max_gods, int);
+
+ /* Set god */
+ p_ptr->pgod = k;
+ p_ptr->grace = 0;
+ }
+
+ /* A god that like us ? more grace ! */
+ if (PRACE_FLAGS(PR1_GOD_FRIEND))
+ {
+ set_grace(200);
+ }
+ else
+ {
+ set_grace(100);
+ }
+ }
+
+ /* Clean up */
+ clear_from(12);
+
+ if (!do_quick_start)
+ {
+ /* Clear */
+ clear_from(15);
+
+ /* */
+ if (get_check("Do you want to modify the options"))
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ }
+
+ /* Set birth options: maximize, preserve, sepcial levels and astral */
+ p_ptr->maximize = maximize;
+ p_ptr->preserve = preserve;
+ p_ptr->special = special_lvls;
+ p_ptr->astral = (PRACE_FLAG2(PR2_ASTRAL)) ? TRUE : FALSE;
+
+ /*
+ * A note by pelpel. (remove this please)
+ * Be it the new Vanilla way (adult vs. birth options) or
+ * the old one (player_type members), it would be less confusing
+ * to handle birth-only options in a uniform fashion,the above and
+ * the following:
+ * ironman_rooms,
+ * joke_monsters,
+ * always_small_level, and
+ * fate_option
+ */
+
+
+ /* Set the recall dungeon accordingly */
+ call_lua("get_module_info", "(s)", "d", "base_dungeon", &tmp);
+ dungeon_type = tmp;
+ p_ptr->recall_dungeon = dungeon_type;
+ max_dlv[dungeon_type] = d_info[dungeon_type].mindepth;
+
+ if (p_ptr->astral)
+ {
+ s32b x, y, astral_dun;
+
+ call_lua("get_module_info", "(s)", "d", "astral_dungeon", &astral_dun);
+ dungeon_type = astral_dun;
+
+ /* Somewhere in the misty mountains */
+ call_lua("get_module_info", "(s)", "d", "astral_wild_x", &x);
+ call_lua("get_module_info", "(s)", "d", "astral_wild_y", &y);
+ p_ptr->wilderness_x = x;
+ p_ptr->wilderness_y = y;
+ }
+
+ /* Clean up */
+ clear_from(10);
+
+ /*** User enters number of quests ***/
+ /* Heino Vander Sanden and Jimmy De Laet */
+
+ call_lua("get_module_info", "(s)", "d", "rand_quest", &allow_quest);
+ if (!ironman_rooms && allow_quest)
+ {
+ if (do_quick_start)
+ {
+ v = previous_char.quests;
+ }
+ else
+ {
+ /* Extra info */
+ Term_putstr(5, 15, -1, TERM_WHITE,
+ "Select the number of optional random quests you'd like to receive.");
+ Term_putstr(5, 16, -1, TERM_WHITE,
+ "If you do not want any optional quests, enter 0.");
+
+ /* Ask the number of additional quests */
+ while (TRUE)
+ {
+ put_str(format("Number of quests? (0-%u) ",
+ MAX_RANDOM_QUEST - 1), 20, 2);
+
+ /* Get a the number of additional quest */
+ while (TRUE)
+ {
+ /* Move the cursor */
+ put_str("", 20, 27);
+
+ /* Default */
+ strcpy(inp, "20");
+
+ /* Get a response (or escape) */
+ if (!askfor_aux(inp, 2)) inp[0] = '\0';
+ if (inp[0] == '*') v = rand_int(MAX_RANDOM_QUEST);
+ else v = atoi(inp);
+
+ /* Break on valid input */
+ if ((v < MAX_RANDOM_QUEST) && ( v >= 0 )) break;
+ }
+ break;
+ }
+
+ /* Clear */
+ clear_from(15);
+ }
+ }
+ else
+ {
+ /* NO quests for ironman rooms or persistent levels, since they
+ don't work */
+ v = 0;
+ }
+
+ /* Set the quest monster hook */
+ get_mon_num_hook = monster_quest;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Generate quests */
+ for (i = 0; i < MAX_RANDOM_QUEST; i++) random_quests[i].type = 0;
+ if (v) gen_random_quests(v);
+ max_quests = v;
+
+ p_ptr->inside_quest = 0;
+
+ /* Init the plots */
+ call_lua("get_module_info", "(s)", "d", "C_quest", &allow_quest);
+ if (allow_quest)
+ {
+ plots[PLOT_MAIN] = QUEST_NECRO;
+ quest[plots[PLOT_MAIN]].status = QUEST_STATUS_TAKEN;
+
+ plots[PLOT_BREE] = QUEST_THIEVES;
+ quest[plots[PLOT_BREE]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_LORIEN] = QUEST_WOLVES;
+ quest[plots[PLOT_LORIEN]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_GONDOLIN] = QUEST_DRAGONS;
+ quest[plots[PLOT_GONDOLIN]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_MINAS] = QUEST_HAUNTED;
+ quest[plots[PLOT_MINAS]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_KHAZAD] = QUEST_EVIL;
+ quest[plots[PLOT_KHAZAD]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_OTHER] = QUEST_NULL;
+ }
+
+ quest_random_init_hook(QUEST_RANDOM);
+
+ /* Ok */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Initial stat costs (initial stats always range from 10 to 18 inclusive).
+ */
+static const int birth_stat_costs[(18-10) + 1] =
+{
+ 0, 1, 2, 4, 7, 11, 16, 22, 30
+};
+
+
+/*
+ * Helper function for 'player_birth()'.
+ *
+ * This function handles "point-based" character creation.
+ *
+ * The player selects, for each stat, a value from 10 to 18 (inclusive),
+ * each costing a certain amount of points (as above), from a pool of 48
+ * available points, to which race/class modifiers are then applied.
+ *
+ * Each unused point is converted into 100 gold pieces, with a maximum of
+ * 600 gp at birth.
+ *
+ * Taken from V 2.9.0
+ */
+static bool_ player_birth_aux_point(void)
+{
+ int i;
+
+ int row = 3;
+
+ int col = 42;
+
+ int stat = 0;
+
+ int stats[6];
+
+ int cost;
+
+ char ch;
+
+ char buf[80];
+
+ int mode = 0;
+
+
+ /* Initialize stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Initial stats */
+ stats[i] = 10;
+ }
+
+
+ /* Roll for base hitpoints */
+ get_extra();
+
+ /* Roll for age/height/weight */
+ get_ahw();
+
+ /* Roll for social class */
+ get_history();
+
+ /*** Generate ***/
+ process_hooks(HOOK_BIRTH, "()");
+
+ /* Hack -- get a chaos patron even if you are not a chaos warrior */
+ p_ptr->chaos_patron = (randint(MAX_PATRON)) - 1;
+
+ /* Get luck */
+ p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5);
+ p_ptr->luck_max = p_ptr->luck_base;
+
+ /* Interact */
+ while (1)
+ {
+ /* Reset cost */
+ cost = 0;
+
+ /* Process stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Variable stat maxes */
+ if (p_ptr->maximize)
+ {
+ /* Reset stats */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stats[i];
+
+ }
+
+ /* Fixed stat maxes */
+ else
+ {
+ /* Obtain a "bonus" for "race" and "class" */
+ int bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i];
+
+ /* Apply the racial/class bonuses */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i] =
+ modify_stat_value(stats[i], bonus);
+ }
+
+ /* Total cost */
+ cost += birth_stat_costs[stats[i] - 10];
+ }
+
+ /* Restrict cost */
+ if (cost > 48)
+ {
+ /* Warning */
+ bell();
+
+ /* Reduce stat */
+ stats[stat]--;
+
+ /* Recompute costs */
+ continue;
+ }
+
+ /* Gold is inversely proportional to cost */
+ p_ptr->au = (100 * (48 - cost)) + 100;
+
+ /* Maximum of 600 gold */
+ if (p_ptr->au > 600) p_ptr->au = 600;
+
+ /* Calculate the bonuses and hitpoints */
+ p_ptr->update |= (PU_BONUS | PU_HP);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Fully healed */
+ p_ptr->chp = p_ptr->mhp;
+
+ /* Fully rested */
+ p_ptr->csp = p_ptr->msp;
+
+ /* Display the player */
+ display_player(mode);
+
+ /* Display the costs header */
+ put_str("Cost", row - 2, col + 32);
+
+ /* Display the costs */
+ for (i = 0; i < 6; i++)
+ {
+ /* Display cost */
+ strnfmt(buf, 80, "%4d", birth_stat_costs[stats[i] - 10]);
+ put_str(buf, row + (i - 1), col + 32);
+ }
+
+
+ /* Prompt XXX XXX XXX */
+ strnfmt(buf, 80, "Total Cost %2d/48. Use 2/8 to move, 4/6 to modify, ESC to accept.", cost);
+ prt(buf, 0, 0);
+
+ /* Place cursor just after cost of current stat */
+ Term_gotoxy(col + 36, row + stat - 1);
+
+ /* Get key */
+ ch = inkey();
+
+ /* Quit */
+ if (ch == 'Q') quit(NULL);
+
+ /* Start over */
+ if (ch == 'S') return (FALSE);
+
+ /* Done */
+ if (ch == ESCAPE) break;
+
+ /* Prev stat */
+ if (ch == '8')
+ {
+ stat = (stat + 6 - 1) % 6;
+ }
+
+ /* Next stat */
+ if (ch == '2')
+ {
+ stat = (stat + 1) % 6;
+ }
+
+ /* Decrease stat */
+ if ((ch == '4') && (stats[stat] > 10))
+ {
+ stats[stat]--;
+ }
+
+ /* Increase stat */
+ if ((ch == '6') && (stats[stat] < 18))
+ {
+ stats[stat]++;
+ }
+ }
+
+
+ /* Done */
+ return (TRUE);
+}
+
+/*
+ * Use the autoroller or not to generate a char
+ */
+static bool_ player_birth_aux_auto()
+{
+ int i, j, m, v;
+
+ int mode = 0;
+
+ bool_ flag = FALSE;
+
+ bool_ prev = FALSE;
+
+ char c;
+
+ char b1 = '[';
+
+ char b2 = ']';
+
+ char buf[80];
+
+ char inp[80];
+
+
+#ifdef ALLOW_AUTOROLLER
+
+ /* Initialize */
+ if (autoroll)
+ {
+ int mval[6];
+
+
+ /* Clear fields */
+ auto_round = 0L;
+ last_round = 0L;
+
+ /* Clean up */
+ clear_from(10);
+
+ /* Prompt for the minimum stats */
+ put_str("Enter minimum attribute for: ", 15, 2);
+
+ /* Output the maximum stats */
+ for (i = 0; i < 6; i++)
+ {
+ char stat_buf[15];
+
+ /* Reset the "success" counter */
+ stat_match[i] = 0;
+
+ /* Race/Class bonus */
+ j = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i];
+
+ /* Obtain the "maximal" stat */
+ m = adjust_stat(17, j, TRUE);
+
+
+ /* Save the maximum */
+ mval[i] = m;
+
+ /* Extract a textual format */
+ cnv_stat(m, stat_buf);
+
+ strnfmt(inp, 80, "(Max of %s):", stat_buf);
+
+ /* Prepare a prompt */
+ strnfmt(buf, 80, "%-5s: %-20s", stat_names[i], inp);
+
+ /* Dump the prompt */
+ put_str(buf, 16 + i, 5);
+ }
+
+ /* Input the minimum stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Get a minimum stat */
+ while (TRUE)
+ {
+ char *s;
+
+ /* Move the cursor */
+ put_str("", 16 + i, 30);
+
+ /* Default */
+ strcpy(inp, "");
+
+ /* Get a response (or escape) */
+ if (!askfor_aux(inp, 8)) inp[0] = '\0';
+
+ /* Weirdos stat display .. erm .. I mean, original stat display */
+ if (!linear_stats)
+ {
+ /* Hack -- add a fake slash */
+ strcat(inp, "/");
+
+ /* Hack -- look for the "slash" */
+ s = strchr(inp, '/');
+
+ /* Hack -- Nuke the slash */
+ *s++ = '\0';
+
+ /* Hack -- Extract an input */
+ v = atoi(inp) + atoi(s);
+ }
+ else
+ {
+ int z = atoi(inp);
+
+ if (z <= 18)
+ v = z;
+ else
+ {
+ int extra = z - 18;
+ v = 18 + (extra * 10);
+ }
+ }
+
+ /* Break on valid input */
+ if (v <= mval[i]) break;
+ }
+
+ /* Save the minimum stat */
+ stat_limit[i] = (v > 0) ? v : 0;
+ }
+ }
+
+#endif /* ALLOW_AUTOROLLER */
+
+ /* Roll */
+ while (TRUE)
+ {
+ /* Feedback */
+ if (autoroll)
+ {
+ Term_clear();
+
+ put_str("Name :", 2, 1);
+ put_str("Sex :", 3, 1);
+ put_str("Race :", 4, 1);
+ put_str("Class:", 5, 1);
+
+ c_put_str(TERM_L_BLUE, player_name, 2, 9);
+ c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 9);
+ strnfmt(buf, 80, "%s", get_player_race_name(p_ptr->prace, p_ptr->pracem));
+ c_put_str(TERM_L_BLUE, buf, 4, 9);
+ c_put_str(TERM_L_BLUE, spp_ptr->title + c_name, 5, 9);
+
+ /* Label stats */
+ put_str("STR:", 2 + A_STR, 61);
+ put_str("INT:", 2 + A_INT, 61);
+ put_str("WIS:", 2 + A_WIS, 61);
+ put_str("DEX:", 2 + A_DEX, 61);
+ put_str("CON:", 2 + A_CON, 61);
+ put_str("CHR:", 2 + A_CHR, 61);
+
+ /* Note when we started */
+ last_round = auto_round;
+
+ /* Indicate the state */
+ put_str("(Hit ESC to abort)", 11, 61);
+
+ /* Label count */
+ put_str("Round:", 9, 61);
+ }
+
+ /* Otherwise just get a character */
+ else
+ {
+ /* Get a new character */
+ get_stats();
+ }
+
+ /* Auto-roll */
+ while (autoroll)
+ {
+ bool_ accept = TRUE;
+
+ /* Get a new character */
+ get_stats();
+
+ /* Advance the round */
+ auto_round++;
+
+ /* Hack -- Prevent overflow */
+ if (auto_round >= 1000000L) break;
+
+ /* Check and count acceptable stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* This stat is okay */
+ if (stat_use[i] >= stat_limit[i])
+ {
+ stat_match[i]++;
+ }
+
+ /* This stat is not okay */
+ else
+ {
+ accept = FALSE;
+ }
+ }
+
+ /* Break if "happy" */
+ if (accept) break;
+
+ /* Take note every 25 rolls */
+ flag = (!(auto_round % AUTOROLLER_STEP));
+
+ /* Update display occasionally */
+ if (flag || (auto_round < last_round + 100))
+ {
+ /* Dump data */
+ birth_put_stats();
+
+ /* Dump round */
+ put_str(format("%6ld", auto_round), 9, 73);
+
+ /* Make sure they see everything */
+ Term_fresh();
+
+#ifndef USE_FAST_AUTOROLLER
+
+ /* Delay 1/10 second */
+ if (fast_autoroller && flag) Term_xtra(TERM_XTRA_DELAY, 100);
+
+#endif
+ /* Do not wait for a key */
+ inkey_scan = TRUE;
+
+ /* Check for a keypress */
+ if (inkey()) break;
+ }
+ }
+
+ /* Flush input */
+ flush();
+
+
+ /*** Display ***/
+
+ /* Mode */
+ mode = 0;
+
+ /* Roll for base hitpoints */
+ get_extra();
+
+ /* Roll for age/height/weight */
+ get_ahw();
+
+ /* Roll for social class */
+ get_history();
+
+ /* Roll for gold */
+ get_money();
+
+ /*** Generate ***/
+ process_hooks(HOOK_BIRTH, "()");
+
+ /* Hack -- get a chaos patron even if you are not a chaos warrior */
+ p_ptr->chaos_patron = (randint(MAX_PATRON)) - 1;
+
+ /* Input loop */
+ while (TRUE)
+ {
+ /* Calculate the bonuses and hitpoints */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Fully healed */
+ p_ptr->chp = p_ptr->mhp;
+
+ /* Fully rested */
+ p_ptr->csp = p_ptr->msp;
+
+ /* Display the player */
+ display_player(mode);
+
+ /* Prepare a prompt (must squeeze everything in) */
+ Term_gotoxy(2, 23);
+ Term_addch(TERM_WHITE, b1);
+ Term_addstr( -1, TERM_WHITE, "'r' to reroll");
+ if (prev) Term_addstr( -1, TERM_WHITE, ", 'p' for prev");
+ if (mode) Term_addstr( -1, TERM_WHITE, ", 'h' for Misc.");
+ else Term_addstr( -1, TERM_WHITE, ", 'h' for History");
+ Term_addstr( -1, TERM_WHITE, ", or ESC to accept");
+ Term_addch(TERM_WHITE, b2);
+
+ /* Prompt and get a command */
+ c = inkey();
+
+ /* Quit */
+ if (c == 'Q') quit(NULL);
+
+ /* Start over */
+ if (c == 'S') return (FALSE);
+
+ /* Escape accepts the roll */
+ if (c == ESCAPE) break;
+
+ /* Reroll this character */
+ if ((c == ' ') || (c == 'r')) break;
+
+ /* Previous character */
+ if (prev && (c == 'p'))
+ {
+ load_prev_data(TRUE);
+ continue;
+ }
+
+ /* Toggle the display */
+ if ((c == 'H') || (c == 'h'))
+ {
+ mode = ((mode != 0) ? 0 : 1);
+ continue;
+ }
+
+ /* Help */
+ if (c == '?')
+ {
+ do_cmd_help();
+ continue;
+ }
+
+ /* Warning */
+ bell();
+ }
+
+ /* Are we done? */
+ if (c == ESCAPE) break;
+
+ /* Save this for the "previous" character */
+ save_prev_data();
+
+ /* Note that a previous roll exists */
+ prev = TRUE;
+ }
+
+ /* Clear prompt */
+ clear_from(23);
+
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for 'player_birth()'
+ *
+ * The delay may be reduced, but is recommended to keep players
+ * from continuously rolling up characters, which can be VERY
+ * expensive CPU wise. And it cuts down on player stupidity.
+ */
+static bool_ player_birth_aux()
+{
+ char c;
+
+ int i, j;
+
+ int y = 0, x = 0;
+
+ char old_history[4][60];
+
+ /* Ask */
+ if (!player_birth_aux_ask()) return (FALSE);
+
+ for (i = 1; i < max_s_idx; i++)
+ s_info[i].dev = FALSE;
+ for (i = 1; i < max_s_idx; i++)
+ {
+ s32b value = 0, mod = 0;
+
+ compute_skills(&value, &mod, i);
+
+ init_skill(value, mod, i);
+
+ /* Develop only revelant branches */
+ if (s_info[i].value || s_info[i].mod)
+ {
+ int z = s_info[i].father;
+
+ while (z != -1)
+ {
+ s_info[z].dev = TRUE;
+ z = s_info[z].father;
+ if (z == 0)
+ break;
+ }
+ }
+ }
+
+ if (do_quick_start)
+ {
+ load_prev_data(FALSE);
+
+ /* Roll for base hitpoints */
+ get_extra();
+
+ /* Calculate the bonuses and hitpoints */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Fully healed */
+ p_ptr->chp = p_ptr->mhp;
+
+ /* Fully rested */
+ p_ptr->csp = p_ptr->msp;
+ }
+ else
+ {
+ /* Point based */
+ if (point_based)
+ {
+ if (!player_birth_aux_point()) return FALSE;
+ }
+ /* Auto-roll */
+ else
+ {
+ if (!player_birth_aux_auto()) return FALSE;
+ }
+
+ /* Edit character background */
+ for (i = 0; i < 4; i++)
+ {
+ strnfmt(old_history[i], 60, "%s", history[i]);
+ }
+ /* Turn 0 to space */
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; history[i][j]; j++) /* loop */;
+
+ for (; j < 59; j++) history[i][j] = ' ';
+ }
+ display_player(1);
+ c_put_str(TERM_L_GREEN, "(Character Background - Edit Mode)", 15, 20);
+ while (TRUE)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ put_str(history[i], i + 16, 10);
+ }
+ c_put_str(TERM_L_BLUE, format("%c", history[y][x]), y + 16, x + 10);
+
+ /* Place cursor just after cost of current stat */
+ Term_gotoxy(x + 10, y + 16);
+
+ c = inkey();
+
+ if (c == '8')
+ {
+ y--;
+ if (y < 0) y = 3;
+ }
+ else if (c == '2')
+ {
+ y++;
+ if (y > 3) y = 0;
+ }
+ else if (c == '6')
+ {
+ x++;
+ if (x > 59) x = 0;
+ }
+ else if (c == '4')
+ {
+ x--;
+ if (x < 0) x = 59;
+ }
+ else if (c == '\r')
+ {
+ break;
+ }
+ else if (c == ESCAPE)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ strnfmt(history[i], 60, "%s", old_history[i]);
+ put_str(history[i], i + 16, 10);
+ }
+ break;
+ }
+ else
+ {
+ history[y][x++] = c;
+ if (x > 58)
+ {
+ x = 0;
+ y++;
+ if (y > 3) y = 0;
+ }
+ }
+ }
+
+
+ /*** Finish up ***/
+
+ /* Get a name, recolor it, prepare savefile */
+
+ get_name();
+
+
+ /* Prompt for it */
+ prt("['Q' to suicide, 'S' to start over, or ESC to continue]", 23, 10);
+
+ /* Get a key */
+ c = inkey();
+
+ /* Quit */
+ if (c == 'Q') quit(NULL);
+
+ /* Start over */
+ if (c == 'S') return (FALSE);
+ }
+
+ /* Save this for the next character */
+ previous_char.quick_ok = TRUE;
+ save_prev_data();
+
+ /* Accept */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for validate_bg().
+ */
+static void validate_bg_aux(int chart, bool_ chart_checked[], char *buf)
+{
+ char *s;
+
+ int i;
+
+
+ /* Assume the chart does not exist */
+ bool_ chart_exists = FALSE;
+
+ /* Assume the chart is not complete */
+ bool_ chart_complete = FALSE;
+
+ int bg_max = max_bg_idx;
+
+ /* No chart */
+ if (!chart) return;
+
+ /* Already saw this chart */
+ if (chart_checked[chart]) return;
+
+ /* Build a debug message */
+ s = buf + strlen(buf);
+
+ /* XXX XXX XXX */
+ (void) strnfmt(s, -1, "%d --> ", chart);
+
+ /* Check each chart */
+ for (i = 0; i < bg_max; i++)
+ {
+ /* Require same chart */
+ if (bg[i].chart != chart) continue;
+
+ /* The chart exists */
+ chart_exists = TRUE;
+
+ /* Validate the "next" chart recursively */
+ validate_bg_aux(bg[i].next, chart_checked, buf);
+
+ /* Require a terminator */
+ if (bg[i].roll != 100) continue;
+
+ /* The chart is complete */
+ chart_complete = TRUE;
+ }
+
+ /* Failed: The chart does not exist */
+ if (!chart_exists)
+ {
+ quit_fmt("birth.c: bg[] chart %d does not exist\n%s", chart, buf);
+ }
+
+ /* Failed: The chart is not complete */
+ if (!chart_complete)
+ {
+ quit_fmt("birth.c: bg[] chart %d is not complete", chart);
+ }
+
+ /* Remember we saw this chart */
+ chart_checked[chart] = TRUE;
+
+ /* Build a debug message */
+ *s = 0;
+}
+
+
+/*
+ * Verify that the bg[] table is valid.
+ */
+static void validate_bg(void)
+{
+ int i, race;
+
+ bool_ chart_checked[512];
+
+ char buf[1024];
+
+
+ for (i = 0; i < 512; i++) chart_checked[i] = FALSE;
+
+ /* Check each race */
+ for (race = 0; race < max_rp_idx; race++)
+ {
+ /* Get the first chart for this race */
+ int chart = race_info[race].chart;
+
+ (void) strcpy(buf, "");
+
+ /* Validate the chart recursively */
+ validate_bg_aux(chart, chart_checked, buf);
+ }
+}
+
+/*
+ * Initialize a random town
+ */
+void init_town(int t_idx, int level)
+{
+ town_type *t_ptr = &town_info[t_idx];
+
+ /* Mark it as existent */
+ t_ptr->flags |= (TOWN_REAL);
+
+ /* Mark it as not found */
+ t_ptr->flags &= ~(TOWN_KNOWN);
+
+ /* Generation seed for the town */
+ t_ptr->seed = randint(0x10000000);
+
+ /* Total hack and not even used */
+ t_ptr->numstores = 8;
+}
+
+/*
+ * Create a new character.
+ *
+ * Note that we may be called with "junk" leftover in the various
+ * fields, so we must be sure to clear them first.
+ */
+void player_birth(void)
+{
+ int i, j, rtown = TOWN_RANDOM;
+
+ /* Validate the bg[] table */
+ validate_bg();
+
+ /* Create a new character */
+ while (1)
+ {
+ /* Wipe the player */
+ player_wipe();
+
+ /* Roll up a new character */
+ if (player_birth_aux()) break;
+ }
+
+ /* Finish skills */
+ p_ptr->skill_points = 0;
+ p_ptr->skill_last_level = 1;
+
+ recalc_skills(FALSE);
+
+ /* grab level 1 abilities */
+ for (i = 0; i < max_ab_idx; i++)
+ ab_info[i].acquired = FALSE;
+ apply_level_abilities(1);
+
+ /* Complete the god */
+ i = p_ptr->pgod;
+ p_ptr->pgod = 0;
+ follow_god(i, TRUE);
+
+ /* Select the default melee type */
+ select_default_melee();
+
+ /* Make a note file if that option is set */
+ if (take_notes)
+ {
+ add_note_type(NOTE_BIRTH);
+ }
+
+ /* Note player birth in the message recall */
+ message_add(MESSAGE_MSG, " ", TERM_L_BLUE);
+ message_add(MESSAGE_MSG, " ", TERM_L_BLUE);
+ message_add(MESSAGE_MSG, "====================", TERM_L_BLUE);
+ message_add(MESSAGE_MSG, " ", TERM_L_BLUE);
+ message_add(MESSAGE_MSG, " ", TERM_L_BLUE);
+
+ /* Hack -- outfit the player */
+ player_outfit();
+
+ /* Initialize random towns in the dungeons */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ dungeon_info_type *d_ptr = &d_info[i];
+ int num = 0, z;
+
+ d_ptr->t_num = 0;
+ for (z = 0; z < TOWN_DUNGEON; z++)
+ {
+ d_ptr->t_idx[z] = 0;
+ d_ptr->t_level[z] = 0;
+ }
+ if (!(d_ptr->flags1 & DF1_RANDOM_TOWNS)) continue;
+
+ /* Can we add a town ? */
+ while (magik(TOWN_CHANCE - (num * 10)))
+ {
+ int lev;
+
+ d_ptr->t_idx[num] = rtown;
+ rtown++;
+
+ while (TRUE)
+ {
+ int j;
+ bool_ ok = TRUE;
+
+ lev = rand_range(d_ptr->mindepth, d_ptr->maxdepth - 1);
+
+ /* Be sure it wasnt already used */
+ for (j = 0; j < num; j++)
+ {
+ if (d_ptr->t_level[j] == lev) ok = FALSE;
+ }
+
+ /* Ok found one */
+ if (ok) break;
+ }
+ d_ptr->t_level[num] = lev;
+
+ if (wizard) message_add(MESSAGE_MSG, format("Random dungeon town: d_idx:%d, lev:%d", i, lev), TERM_WHITE);
+
+ /* Create the town */
+ init_town(d_ptr->t_idx[num], d_ptr->t_level[num]);
+
+ num++;
+
+ /* No free slots left */
+ if (num >= TOWN_DUNGEON) break;
+ }
+
+ d_ptr->t_num = num;
+ }
+
+ /* Init the towns */
+ for (i = 1; i < max_towns; i++)
+ {
+ /* Not destroyed ! yet .. ;) */
+ town_info[i].destroyed = FALSE;
+
+ /* Ignore non-existent towns */
+ if (!(town_info[i].flags & (TOWN_REAL))) continue;
+
+ create_stores_stock(i);
+
+ /* Init the stores */
+ for (j = 0; j < max_st_idx; j++)
+ {
+ /* Initialize */
+ store_init(i, j);
+ }
+ }
+
+ /* Init wilderness seeds */
+ for (i = 0; i < max_wild_x; i++)
+ {
+ for (j = 0; j < max_wild_y; j++)
+ {
+ wild_map[j][i].seed = rand_int(0x10000000);
+ wild_map[j][i].entrance = 0;
+ wild_map[j][i].known = FALSE;
+ }
+ }
+
+ /* Select bounty monsters. */
+ select_bounties();
+}
+
+
+
+
+char savefile_module[46][80];
+char savefile_names[46][30];
+char savefile_desc[46][80];
+bool_ savefile_alive[46];
+int savefile_idx[46];
+
+/*
+ * Grab all the names from an index
+ */
+int load_savefile_names()
+{
+ FILE *fff;
+ char buf[1024];
+ char tmp[50];
+ char player_base_save[32];
+ int max = 0, fd;
+
+
+ /* Build the filename */
+ strcpy(tmp, "global.svg");
+ path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Read the file */
+ fff = my_fopen(buf, "r");
+
+ /* Failure */
+ if (!fff) return (0);
+
+
+ /* Save the current 'player_base' */
+ strncpy(player_base_save, player_base, 32);
+
+
+ /*
+ * Parse, use '@' intead of ':' as a separator because it cannot exists
+ * in savefiles
+ */
+ while (0 == my_fgets(fff, buf, 1024))
+ {
+ int i = 0, start, count;
+
+ /* Check for pre-ToME 2.1.2 file */
+ count = 0;
+ i = 0;
+ while (buf[i] && buf[i] != '\n')
+ {
+ if (buf[i] == '@')
+ ++count;
+ ++i;
+ }
+
+ /* Check module if a current svg file */
+ start = 0;
+ i = 0;
+ if (count > 1)
+ {
+ while (buf[i] != '@')
+ {
+ savefile_module[max][i - start] = buf[i];
+ i++;
+ }
+ savefile_module[max][i] = '\0';
+ i++;
+ }
+ /* Default to ToME for old files */
+ else
+ {
+ savefile_module[max][0] = 'T';
+ savefile_module[max][1] = 'o';
+ savefile_module[max][2] = 'M';
+ savefile_module[max][3] = 'E';
+ savefile_module[max][4] = '\0';
+ }
+
+ if (buf[i] == '0') savefile_alive[max] = FALSE;
+ else if (buf[i] == '1') savefile_alive[max] = TRUE;
+
+ i++;
+ start = i;
+ while (buf[i] != '@')
+ {
+ savefile_names[max][i - start] = buf[i];
+ i++;
+ }
+ savefile_names[max][i - start] = '\0';
+ i++;
+ strcpy(savefile_desc[max], buf + i);
+
+ /* Build platform-dependent savefile name */
+ strncpy(player_base, savefile_names[max], 32);
+ process_player_name(TRUE);
+
+ /* File type is 'SAVE' */
+ FILE_TYPE(FILE_TYPE_SAVE);
+
+ /* Try to open the savefile */
+ fd = fd_open(savefile, O_RDONLY);
+
+ /* Still existing ? */
+ if (fd >= 0)
+ {
+ fd_close(fd);
+ max++;
+ }
+ }
+
+ my_fclose(fff);
+
+ /* Restore the values of 'player_base' and 'savefile' */
+ strncpy(player_base, player_base_save, 32);
+ process_player_name(TRUE);
+
+ return (max);
+}
+
+
+/*
+ * Save all the names from an index
+ */
+void save_savefile_names()
+{
+ FILE *fff;
+ char buf[1024];
+ char tmp[50];
+ int max = load_savefile_names(), i;
+
+
+ /* Build the filename */
+ strcpy(tmp, "global.svg");
+ path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Read the file */
+ fff = my_fopen(buf, "w");
+
+ /* Failure */
+ if (!fff) return;
+
+ /*
+ * Save, use '@' intead of ':' as a separator because it cannot exists
+ * in savefiles
+ */
+ fprintf(fff, "%s@%c%s@%s, the %s %s is %s\n", game_module,
+ (death) ? '0' : '1', player_base, player_name,
+ get_player_race_name(p_ptr->prace, p_ptr->pracem),
+ spp_ptr->title + c_name,
+ (!death) ? "alive" : "dead");
+
+ for (i = 0; i < max; i++)
+ {
+ if (!strcmp(savefile_names[i], player_base)) continue;
+ fprintf(fff, "%s@%c%s@%s\n", savefile_module[i],
+ (savefile_alive[i]) ? '1' : '0', savefile_names[i], savefile_desc[i]);
+ }
+
+ my_fclose(fff);
+}
+
+
+static void dump_savefiles(int sel, int max)
+{
+ int i;
+
+ char buf[40], pre = ' ', post = ')';
+
+ char ind;
+
+
+ for (i = 0; i < max; i++)
+ {
+ ind = I2A(i % 26);
+ if (i >= 26) ind = toupper(ind);
+
+ if (sel == i)
+ {
+ pre = '[';
+ post = ']';
+ }
+ else
+ {
+ pre = ' ';
+ post = ')';
+ }
+
+ if (i == 0) strnfmt(buf, 40, "%c%c%c New Character", pre, ind, post);
+ else if (i == 1) strnfmt(buf, 40, "%c%c%c Load Savefile", pre, ind, post);
+ else strnfmt(buf, 40, "%c%c%c %s", pre, ind, post, savefile_names[savefile_idx[i - 2]]);
+
+ if (sel == i)
+ {
+ if (i >= 2)
+ {
+ if (savefile_alive[i - 2]) c_put_str(TERM_L_GREEN, savefile_desc[savefile_idx[i - 2]], 5, 0);
+ else c_put_str(TERM_L_RED, savefile_desc[savefile_idx[i - 2]], 5, 0);
+ }
+ else if (i == 1) c_put_str(TERM_YELLOW, "Load an existing savefile that is not in the list", 5, 0);
+ else c_put_str(TERM_YELLOW, "Create a new character", 5, 0);
+ c_put_str(TERM_L_BLUE, buf, 6 + (i / 4), 20 * (i % 4));
+ }
+ else
+ put_str(buf, 6 + (i / 4), 20 * (i % 4));
+ }
+}
+
+
+/* Asks for new game or load game */
+bool_ no_begin_screen = FALSE;
+
+bool_ begin_screen()
+{
+ int m, k, sel, max;
+
+savefile_try_again:
+ sel = 0;
+
+ /* Grab the savefiles */
+ max = load_savefile_names();
+
+ /* Get only the usable savefiles */
+ for (k = 0, m = 0; k < max; k++)
+ {
+ s32b can_use;
+
+ call_lua("module_savefile_loadable", "(s)", "d", savefile_module[k], &can_use);
+ if (can_use)
+ {
+ savefile_idx[m++] = k;
+ }
+ }
+ max = m + 2;
+ if (max > 2) sel = 2;
+
+ while (TRUE)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Let the user choose */
+ c_put_str(TERM_YELLOW, format("Welcome to %s! To play you will need a character.", game_module), 1, 10);
+ put_str("Press 8/2/4/6 to move, Return to select, Backspace to delete a savefile.", 3, 3);
+ put_str("and Esc to quit.", 4, 32);
+
+ dump_savefiles(sel, max);
+
+ k = inkey();
+
+ if (k == ESCAPE)
+ {
+ quit(NULL);
+ }
+ if (k == '6')
+ {
+ sel++;
+ if (sel >= max) sel = 0;
+ continue;
+ }
+ else if (k == '4')
+ {
+ sel--;
+ if (sel < 0) sel = max - 1;
+ continue;
+ }
+ else if (k == '2')
+ {
+ sel += 4;
+ if (sel >= max) sel = sel % max;
+ continue;
+ }
+ else if (k == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = (sel + max - 1) % max;
+ continue;
+ }
+ else if (k == '\r')
+ {
+ if (sel < 26) k = I2A(sel);
+ else k = toupper(I2A(sel));
+ }
+ else if (((k == 0x7F) || (k == '\010')) && (sel >= 2))
+ {
+ char player_base_save[32];
+
+ if (!get_check(format("Really delete '%s'?", savefile_names[savefile_idx[sel - 2]]))) continue;
+
+ /* Save current 'player_base' */
+ strncpy(player_base_save, player_base, 32);
+
+ /* Build platform-dependent save file name */
+ strncpy(player_base, savefile_names[savefile_idx[sel - 2]], 32);
+ process_player_name(TRUE);
+
+ /* Remove the savefile */
+ fd_kill(savefile);
+
+ /* Restore 'player_base' and 'savefile' */
+ strncpy(player_base, player_base_save, 32);
+ process_player_name(TRUE);
+
+ /* Reload, gods I hate using goto .. */
+ goto savefile_try_again;
+
+ continue;
+ }
+
+ if (k == 'a')
+ {
+ /* Display prompt */
+ prt("Enter the name of the savefile that will hold this character: ", 23, 0);
+
+ /* Ask the user for a string */
+ if (!askfor_aux(player_base, 15)) continue;
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ return (TRUE);
+ }
+ if (k == 'b')
+ {
+ /* Display prompt */
+ prt("Enter the name of a savefile: ", 23, 0);
+
+ /* Ask the user for a string */
+ if (!askfor_aux(player_base, 15)) continue;
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ return (FALSE);
+ }
+ else
+ {
+ int x;
+
+ if (islower(k)) x = A2I(k);
+ else x = A2I(tolower(k)) + 26;
+
+ if ((x < 2) || (x >= max)) continue;
+
+ strnfmt(player_base, 32, "%s", savefile_names[savefile_idx[x - 2]]);
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ return (FALSE);
+ }
+ }
+
+ /* Shouldnt happen */
+ return (FALSE);
+}
diff --git a/src/bldg.c b/src/bldg.c
new file mode 100644
index 00000000..48e94e9f
--- /dev/null
+++ b/src/bldg.c
@@ -0,0 +1,2198 @@
+/* File: bldg.c */
+
+/*
+ * Purpose: Building commands
+ * Created by Ken Wigle for Kangband - a variant of Angband 2.8.3
+ * -KMW-
+ *
+ * Rewritten for Kangband 2.8.3i using Kamband's version of
+ * bldg.c as written by Ivan Tkatchev
+ *
+ * Changed for ZAngband by Robert Ruehlmann
+ *
+ * Heavily modified for ToME by DarkGod
+ */
+
+#include "angband.h"
+
+/* hack as in leave_store in store.c */
+static bool_ leave_bldg = FALSE;
+
+/* remember building location */
+static int building_loc = 0;
+
+
+/*
+ * A helper function for is_state
+ */
+bool_ is_state_aux(store_type *s_ptr, int state)
+{
+ owner_type *ow_ptr = &ow_info[s_ptr->owner];
+
+
+ /* Check race */
+ if (ow_ptr->races[state][p_ptr->prace / 32] & (1 << p_ptr->prace))
+ {
+ return (TRUE);
+ }
+
+ /* Check class */
+ if (ow_ptr->classes[state][p_ptr->prace / 32] & (1 << p_ptr->pclass))
+ {
+ return (TRUE);
+ }
+
+ /* All failed */
+ return (FALSE);
+}
+
+
+/*
+ * Test if the state accords with the player
+ */
+bool_ is_state(store_type *s_ptr, int state)
+{
+ if (state == STORE_NORMAL)
+ {
+ if (is_state_aux(s_ptr, STORE_LIKED)) return (FALSE);
+ if (is_state_aux(s_ptr, STORE_HATED)) return (FALSE);
+ return (TRUE);
+ }
+
+ else
+ {
+ return (is_state_aux(s_ptr, state));
+ }
+}
+
+
+/*
+ * Clear the building information
+ */
+static void clear_bldg(int min_row, int max_row)
+{
+ int i;
+
+
+ for (i = min_row; i <= max_row; i++)
+ {
+ prt("", i, 0);
+ }
+}
+
+
+/*
+ * Display a building.
+ */
+void show_building(store_type *s_ptr)
+{
+ char buff[20];
+
+ int i;
+
+ byte action_color;
+
+ char tmp_str[80];
+
+ store_info_type *st_ptr = &st_info[s_ptr->st_idx];
+
+ store_action_type *ba_ptr;
+
+
+ for (i = 0; i < 6; i++)
+ {
+ ba_ptr = &ba_info[st_ptr->actions[i]];
+
+ if (ba_ptr->letter != '.')
+ {
+ if (ba_ptr->action_restr == 0)
+ {
+ if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) ||
+ (is_state(s_ptr, STORE_HATED) && (ba_ptr->costs[STORE_HATED] == 0)) ||
+ (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0)))
+ {
+ action_color = TERM_WHITE;
+ buff[0] = '\0';
+ }
+ else if (is_state(s_ptr, STORE_LIKED))
+ {
+ action_color = TERM_L_GREEN;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]);
+ }
+ else if (is_state(s_ptr, STORE_HATED))
+ {
+ action_color = TERM_RED;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_HATED]);
+ }
+ else
+ {
+ action_color = TERM_YELLOW;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]);
+ }
+ }
+ else if (ba_ptr->action_restr == 1)
+ {
+ if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) ||
+ (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0)))
+ {
+ action_color = TERM_WHITE;
+ buff[0] = '\0';
+ }
+ else if (is_state(s_ptr, STORE_LIKED))
+ {
+ action_color = TERM_L_GREEN;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]);
+ }
+ else if (is_state(s_ptr, STORE_HATED))
+ {
+ action_color = TERM_L_DARK;
+ strnfmt(buff, 20, "(closed)");
+ }
+ else
+ {
+ action_color = TERM_YELLOW;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]);
+ }
+ }
+ else
+ {
+ if (is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0))
+ {
+ action_color = TERM_WHITE;
+ buff[0] = '\0';
+ }
+ else if (is_state(s_ptr, STORE_LIKED))
+ {
+ action_color = TERM_L_GREEN;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]);
+ }
+ else
+ {
+ action_color = TERM_L_DARK;
+ strnfmt(buff, 20, "(closed)");
+ }
+ }
+
+ strnfmt(tmp_str, 80, " %c", ba_ptr->letter);
+ c_put_str(TERM_YELLOW, tmp_str, 21 + (i / 2), 17 + (30 * (i % 2)));
+
+ strnfmt(tmp_str, 80, ") %s %s", ba_ptr->name + ba_name, buff);
+ c_put_str(action_color, tmp_str, 21 + (i / 2), 2 + 17 + (30 * (i % 2)));
+ }
+ }
+}
+
+
+/* reset timed flags */
+static void reset_tim_flags()
+{
+ p_ptr->fast = 0; /* Timed -- Fast */
+ p_ptr->slow = 0; /* Timed -- Slow */
+ p_ptr->blind = 0; /* Timed -- Blindness */
+ p_ptr->paralyzed = 0; /* Timed -- Paralysis */
+ p_ptr->confused = 0; /* Timed -- Confusion */
+ p_ptr->afraid = 0; /* Timed -- Fear */
+ p_ptr->image = 0; /* Timed -- Hallucination */
+ p_ptr->poisoned = 0; /* Timed -- Poisoned */
+ p_ptr->cut = 0; /* Timed -- Cut */
+ p_ptr->stun = 0; /* Timed -- Stun */
+
+ p_ptr->protevil = 0; /* Timed -- Protection */
+ p_ptr->protgood = 0; /* Timed -- Protection */
+ p_ptr->invuln = 0; /* Timed -- Invulnerable */
+ p_ptr->hero = 0; /* Timed -- Heroism */
+ p_ptr->shero = 0; /* Timed -- Super Heroism */
+ p_ptr->shield = 0; /* Timed -- Shield Spell */
+ p_ptr->blessed = 0; /* Timed -- Blessed */
+ p_ptr->tim_invis = 0; /* Timed -- Invisibility */
+ p_ptr->tim_infra = 0; /* Timed -- Infra Vision */
+
+ p_ptr->oppose_acid = 0; /* Timed -- oppose acid */
+ p_ptr->oppose_elec = 0; /* Timed -- oppose lightning */
+ p_ptr->oppose_fire = 0; /* Timed -- oppose heat */
+ p_ptr->oppose_cold = 0; /* Timed -- oppose cold */
+ p_ptr->oppose_pois = 0; /* Timed -- oppose poison */
+
+ p_ptr->confusing = 0; /* Touch of Confusion */
+}
+
+
+/*
+ * arena commands
+ */
+static void arena_comm(int cmd)
+{
+ char tmp_str[80];
+
+ monster_race *r_ptr;
+
+ cptr name;
+
+
+ switch (cmd)
+ {
+ case BACT_ARENA:
+ {
+ if (p_ptr->arena_number == MAX_ARENA_MONS)
+ {
+ clear_bldg(5, 19);
+ prt(" Arena Victor!", 5, 0);
+ prt("Congratulations! You have defeated all before you.", 7, 0);
+ prt("For that, receive the prize: 10,000 gold pieces", 8, 0);
+ prt("", 10, 0);
+ prt("", 11, 0);
+ p_ptr->au += 10000;
+ msg_print("Press the space bar to continue");
+ msg_print(NULL);
+ p_ptr->arena_number++;
+ }
+ else if (p_ptr->arena_number > MAX_ARENA_MONS)
+ {
+ msg_print("You enter the arena briefly and bask in your glory.");
+ msg_print(NULL);
+ }
+ else
+ {
+ p_ptr->inside_arena = TRUE;
+ p_ptr->exit_bldg = FALSE;
+ reset_tim_flags();
+ p_ptr->leaving = TRUE;
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ leave_bldg = TRUE;
+ }
+
+ break;
+ }
+
+ case BACT_POSTER:
+ {
+ if (p_ptr->arena_number == MAX_ARENA_MONS)
+ msg_print("You are victorious. Enter the arena for the ceremony.");
+ else if (p_ptr->arena_number > MAX_ARENA_MONS)
+ msg_print("You have won against all foes.");
+ else
+ {
+ r_ptr = &r_info[arena_monsters[p_ptr->arena_number]];
+ name = (r_name + r_ptr->name);
+ strnfmt(tmp_str, 80, "Do I hear any challenges against: %s", name);
+ msg_print(tmp_str);
+ msg_print(NULL);
+ }
+
+ break;
+ }
+
+ case BACT_ARENA_RULES:
+ {
+ /* Save screen */
+ screen_save();
+
+ /* Peruse the arena help file */
+ (void)show_file("arena.txt", NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * display fruit for dice slots
+ */
+static void display_fruit(int row, int col, int fruit)
+{
+ switch (fruit)
+ {
+ case 0: /* lemon */
+ {
+ c_put_str(TERM_YELLOW, " ####.", row, col);
+ c_put_str(TERM_YELLOW, " # #", row + 1, col);
+ c_put_str(TERM_YELLOW, " # #", row + 2, col);
+ c_put_str(TERM_YELLOW, "# #", row + 3, col);
+ c_put_str(TERM_YELLOW, "# #", row + 4, col);
+ c_put_str(TERM_YELLOW, "# # ", row + 5, col);
+ c_put_str(TERM_YELLOW, "# # ", row + 6, col);
+ c_put_str(TERM_YELLOW, ".#### ", row + 7, col);
+ prt(" Lemon ", row + 8, col);
+
+ break;
+ }
+
+ case 1: /* orange */
+ {
+ c_put_str(TERM_ORANGE, " ## ", row, col);
+ c_put_str(TERM_ORANGE, " #..# ", row + 1, col);
+ c_put_str(TERM_ORANGE, " #....# ", row + 2, col);
+ c_put_str(TERM_ORANGE, "#......#", row + 3, col);
+ c_put_str(TERM_ORANGE, "#......#", row + 4, col);
+ c_put_str(TERM_ORANGE, " #....# ", row + 5, col);
+ c_put_str(TERM_ORANGE, " #..# ", row + 6, col);
+ c_put_str(TERM_ORANGE, " ## ", row + 7, col);
+ prt(" Orange ", row + 8, col);
+
+ break;
+ }
+
+ case 2: /* sword */
+ {
+ c_put_str(TERM_SLATE, " /\\ ", row, col);
+ c_put_str(TERM_SLATE, " ## ", row + 1, col);
+ c_put_str(TERM_SLATE, " ## ", row + 2, col);
+ c_put_str(TERM_SLATE, " ## ", row + 3, col);
+ c_put_str(TERM_SLATE, " ## ", row + 4, col);
+ c_put_str(TERM_SLATE, " ## ", row + 5, col);
+ c_put_str(TERM_UMBER, " ###### ", row + 6, col);
+ c_put_str(TERM_UMBER, " ## ", row + 7, col);
+ prt(" Sword ", row + 8, col);
+
+ break;
+ }
+
+ case 3: /* shield */
+ {
+ c_put_str(TERM_SLATE, " ###### ", row, col);
+ c_put_str(TERM_SLATE, "# #", row + 1, col);
+ c_put_str(TERM_SLATE, "# ++++ #", row + 2, col);
+ c_put_str(TERM_SLATE, "# +==+ #", row + 3, col);
+ c_put_str(TERM_SLATE, "# ++ #", row + 4, col);
+ c_put_str(TERM_SLATE, " # # ", row + 5, col);
+ c_put_str(TERM_SLATE, " # # ", row + 6, col);
+ c_put_str(TERM_SLATE, " ## ", row + 7, col);
+ prt(" Shield ", row + 8, col);
+
+ break;
+ }
+
+ case 4: /* plum */
+ {
+ c_put_str(TERM_VIOLET, " ## ", row, col);
+ c_put_str(TERM_VIOLET, " ###### ", row + 1, col);
+ c_put_str(TERM_VIOLET, "########", row + 2, col);
+ c_put_str(TERM_VIOLET, "########", row + 3, col);
+ c_put_str(TERM_VIOLET, "########", row + 4, col);
+ c_put_str(TERM_VIOLET, " ###### ", row + 5, col);
+ c_put_str(TERM_VIOLET, " #### ", row + 6, col);
+ c_put_str(TERM_VIOLET, " ## ", row + 7, col);
+ prt(" Plum ", row + 8, col);
+
+ break;
+ }
+
+ case 5: /* cherry */
+ {
+ c_put_str(TERM_RED, " ##", row, col);
+ c_put_str(TERM_RED, " ### ", row + 1, col);
+ c_put_str(TERM_RED, " #..# ", row + 2, col);
+ c_put_str(TERM_RED, " #..# ", row + 3, col);
+ c_put_str(TERM_RED, " ###### ", row + 4, col);
+ c_put_str(TERM_RED, "#..##..#", row + 5, col);
+ c_put_str(TERM_RED, "#..##..#", row + 6, col);
+ c_put_str(TERM_RED, " ## ## ", row + 7, col);
+ prt(" Cherry ", row + 8, col);
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * gamble_comm
+ */
+static bool_ gamble_comm(int cmd)
+{
+ int roll1, roll2, roll3, choice, odds, win;
+
+ s32b wager;
+
+ s32b maxbet;
+
+ s32b oldgold;
+
+ static const char *fruit[6] =
+ {"Lemon", "Orange", "Sword", "Shield", "Plum", "Cherry"
+ };
+
+ char out_val[160], tmp_str[80], again;
+
+ cptr p;
+
+
+ screen_save();
+
+ if (cmd == BACT_GAMBLE_RULES)
+ {
+ /* Peruse the gambling help file */
+ (void)show_file("gambling.txt", NULL, 0, 0);
+ }
+ else
+ {
+ clear_bldg(5, 23);
+
+ /* Set maximum bet */
+ if (p_ptr->lev < 10)
+ maxbet = (p_ptr->lev * 100);
+ else
+ maxbet = (p_ptr->lev * 1000);
+
+ /* Get the wager */
+ strcpy(out_val, "");
+ strnfmt(tmp_str, 80, "Your wager (1-%ld) ? ", maxbet);
+ get_string(tmp_str, out_val, 32);
+
+ /* Strip spaces */
+ for (p = out_val; *p == ' '; p++);
+
+ wager = atol(p);
+
+ if (wager > p_ptr->au)
+ {
+ msg_print("Hey! You don't have the gold - get out of here!");
+ msg_print(NULL);
+ screen_load();
+ return (FALSE);
+ }
+ else if (wager > maxbet)
+ {
+ msg_format("I'll take $%ld of that. Keep the rest.", maxbet);
+ wager = maxbet;
+ }
+ else if (wager < 1)
+ {
+ msg_print("Ok, we'll start with $1.");
+
+ wager = 1;
+ }
+ msg_print(NULL);
+ win = FALSE;
+ odds = 0;
+ oldgold = p_ptr->au;
+
+ strnfmt(tmp_str, 80, "Gold before game: %9ld", oldgold);
+ prt(tmp_str, 20, 2);
+
+ strnfmt(tmp_str, 80, "Current Wager: %9ld", wager);
+ prt(tmp_str, 21, 2);
+
+ do
+ {
+ switch (cmd)
+ {
+ case BACT_IN_BETWEEN: /* Game of In-Between */
+ {
+ c_put_str(TERM_GREEN, "In Between", 5, 2);
+ odds = 3;
+ win = FALSE;
+ roll1 = randint(10);
+ roll2 = randint(10);
+ choice = randint(10);
+ strnfmt(tmp_str, 80, "Black die: %d Black Die: %d",
+ roll1, roll2);
+ prt(tmp_str, 8, 3);
+ strnfmt(tmp_str, 80, "Red die: %d", choice);
+ prt(tmp_str, 11, 14);
+ if (((choice > roll1) && (choice < roll2)) ||
+ ((choice < roll1) && (choice > roll2)))
+ win = TRUE;
+
+ break;
+ }
+ case BACT_CRAPS: /* Game of Craps */
+ {
+ c_put_str(TERM_GREEN, "Craps", 5, 2);
+ win = 3;
+ odds = 1;
+ roll1 = randint(6);
+ roll2 = randint(6);
+ roll3 = roll1 + roll2;
+ choice = roll3;
+ strnfmt(tmp_str, 80, "First roll: %d %d Total: %d", roll1,
+ roll2, roll3);
+ prt(tmp_str, 7, 5);
+ if ((roll3 == 7) || (roll3 == 11))
+ win = TRUE;
+ else if ((roll3 == 2) || (roll3 == 3) || (roll3 == 12))
+ win = FALSE;
+ else
+ {
+ do
+ {
+ msg_print("Hit any key to roll again");
+ msg_print(NULL);
+ roll1 = randint(6);
+ roll2 = randint(6);
+ roll3 = roll1 + roll2;
+
+ strnfmt(tmp_str, 80, "Roll result: %d %d Total: %d",
+ roll1, roll2, roll3);
+ prt(tmp_str, 8, 5);
+ if (roll3 == choice)
+ win = TRUE;
+ else if (roll3 == 7)
+ win = FALSE;
+ }
+ while ((win != TRUE) && (win != FALSE));
+ }
+
+ break;
+ }
+
+ case BACT_SPIN_WHEEL: /* Spin the Wheel Game */
+ {
+ win = FALSE;
+ odds = 10;
+ c_put_str(TERM_GREEN, "Wheel", 5, 2);
+ prt("0 1 2 3 4 5 6 7 8 9", 7, 5);
+ prt("--------------------------------", 8, 3);
+ strcpy(out_val, "");
+ get_string ("Pick a number (1-9): ", out_val, 32);
+ for (p = out_val; *p == ' '; p++);
+ choice = atol(p);
+ if (choice < 0)
+ {
+ msg_print("I'll put you down for 0.");
+ choice = 0;
+ }
+ else if (choice > 9)
+ {
+ msg_print("Ok, I'll put you down for 9.");
+ choice = 9;
+ }
+ msg_print(NULL);
+ roll1 = randint(10) - 1;
+ strnfmt(tmp_str, 80, "The wheel spins to a stop and the winner is %d",
+ roll1);
+ prt(tmp_str, 13, 3);
+ prt("", 9, 0);
+ prt("*", 9, (3 * roll1 + 5));
+ if (roll1 == choice)
+ win = TRUE;
+
+ break;
+ }
+
+ case BACT_DICE_SLOTS: /* The Dice Slots */
+ {
+ c_put_str(TERM_GREEN, "Dice Slots", 5, 2);
+ win = FALSE;
+ roll1 = randint(6);
+ roll2 = randint(6);
+ choice = randint(6);
+ strnfmt(tmp_str, 80, "%s %s %s",
+ fruit[roll1 - 1], fruit[roll2 - 1],
+ fruit[choice - 1]);
+ prt(tmp_str, 15, 37);
+ prt("/--------------------------\\", 7, 2);
+ prt("\\--------------------------/", 17, 2);
+ display_fruit(8, 3, roll1 - 1);
+ display_fruit(8, 12, roll2 - 1);
+ display_fruit(8, 21, choice - 1);
+ if ((roll1 == roll2) && (roll2 == choice))
+ {
+ win = TRUE;
+ if (roll1 == 1)
+ odds = 4;
+ else if (roll1 == 2)
+ odds = 6;
+ else
+ odds = roll1 * roll1;
+ }
+ else if ((roll1 == 6) && (roll2 == 6))
+ {
+ win = TRUE;
+ odds = choice + 1;
+ }
+
+ break;
+ }
+ }
+
+ if (win)
+ {
+ prt("YOU WON", 16, 37);
+ p_ptr->au = p_ptr->au + (odds * wager);
+ strnfmt(tmp_str, 80, "Payoff: %d", odds);
+ prt(tmp_str, 17, 37);
+ }
+ else
+ {
+ prt("You Lost", 16, 37);
+ p_ptr->au = p_ptr->au - wager;
+ prt("", 17, 37);
+ }
+ strnfmt(tmp_str, 80, "Current Gold: %9ld", p_ptr->au);
+ prt(tmp_str, 22, 2);
+ prt("Again(Y/N)?", 18, 37);
+ move_cursor(18, 49);
+ again = inkey();
+ if (wager > p_ptr->au)
+ {
+ msg_print("Hey! You don't have the gold - get out of here!");
+ msg_print(NULL);
+ screen_load();
+ return (FALSE);
+ /* strnfmt(tmp_str, 80, "Current Wager: %9ld",wager);
+ prt(tmp_str, 17, 2); */
+ }
+ }
+ while ((again == 'y') || (again == 'Y'));
+
+ prt("", 18, 37);
+ if (p_ptr->au >= oldgold)
+ msg_print("You came out a winner! We'll win next time, I'm sure.");
+ else
+ msg_print("You lost gold! Haha, better head home.");
+ msg_print(NULL);
+ }
+
+ screen_load();
+
+ return (TRUE);
+}
+
+
+/*
+ * inn commands
+ * Note that resting for the night was a perfect way to avoid player
+ * ghosts in the town *if* you could only make it to the inn in time (-:
+ * Now that the ghosts are temporarily disabled in 2.8.X, this function
+ * will not be that useful. I will keep it in the hopes the player
+ * ghost code does become a reality again. Does help to avoid filthy urchins.
+ * Resting at night is also a quick way to restock stores -KMW-
+ */
+static bool_ inn_comm(int cmd)
+{
+ bool_ vampire;
+
+
+ /* Extract race info */
+ vampire = ((PRACE_FLAG(PR1_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire")));
+
+ switch (cmd)
+ {
+ case BACT_FOOD: /* Buy food & drink */
+ {
+ if (!vampire)
+ {
+ msg_print("The barkeep gives you some gruel and a beer.");
+ msg_print(NULL);
+ (void) set_food(PY_FOOD_MAX - 1);
+ }
+ else
+ msg_print("You're a vampire and I don't have any food for you!");
+
+ break;
+ }
+
+ /*
+ * I revamped this... Don't know why normal races didn't get
+ * mana regenerated. It is the grand tradition of p&p games -- pelpel
+ */
+ case BACT_REST: /* Rest for the night */
+ {
+ bool_ nighttime;
+
+ /* Extract the current time */
+ nighttime = ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18));
+
+ /* Normal races rest at night */
+ if (!vampire && !nighttime)
+ {
+ msg_print("The rooms are available only at night.");
+ msg_print(NULL);
+ return (FALSE);
+ }
+
+ /* Vampires rest during daytime */
+ if (vampire && nighttime)
+ {
+ msg_print("The rooms are available only during daylight for your kind.");
+ msg_print(NULL);
+ return (FALSE);
+ }
+
+ /* Must cure HP draining status first */
+ if ((p_ptr->poisoned > 0) || (p_ptr->cut > 0))
+ {
+ msg_print("You need a healer, not a room.");
+ msg_print(NULL);
+ msg_print("Sorry, but I don't want anyone dying in here.");
+ return (FALSE);
+ }
+
+ /* Let the time pass XXX XXX XXX */
+ if (vampire)
+ {
+ /* Wait for sunset */
+ while ((bst(HOUR, turn) >= 6) && (bst(HOUR, turn) < 18))
+ {
+ turn += (10L * MINUTE);
+ }
+ }
+ else
+ {
+ /* Wait for sunrise */
+ while ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18))
+ {
+ turn += (10L * MINUTE);
+ }
+ }
+
+ /* Regen */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->csp = p_ptr->msp;
+
+ /* Restore status */
+ set_blind(0);
+ set_confused(0);
+ p_ptr->stun = 0;
+
+ /* Message */
+ if (vampire) msg_print("You awake refreshed for the new night.");
+ else msg_print("You awake refreshed for the new day.");
+
+ /* Dungeon stuff */
+ p_ptr->leaving = TRUE;
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+
+ /* Select new bounties. */
+ select_bounties();
+
+ break;
+ }
+
+ case BACT_RUMORS: /* Listen for rumors */
+ {
+ char rumor[80];
+
+ get_rnd_line("rumors.txt", rumor);
+ msg_format("%s", rumor);
+ msg_print(NULL);
+
+ break;
+ }
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Display quest information
+ */
+static void get_questinfo(int questnum)
+{
+ int i;
+
+
+ /* Print the quest info */
+ prt(format("Quest Information (Danger level: %d)", quest[questnum].level), 5, 0);
+
+ prt(quest[questnum].name, 7, 0);
+
+ i = 0;
+ while ((i < 10) && (quest[questnum].desc[i][0] != '\0'))
+ {
+ c_put_str(TERM_YELLOW, quest[questnum].desc[i], i + 8, 0);
+ i++;
+ }
+}
+
+
+/*
+ * Request a quest from the Lord.
+ */
+static bool_ castle_quest(int y, int x)
+{
+ int plot = 0;
+
+ quest_type *q_ptr;
+
+
+ clear_bldg(7, 18);
+
+ /* Current plot of the building */
+ plot = cave[y][x].special;
+
+ /* Is there a quest available at the building? */
+ if ((!plot) || (plots[plot] == QUEST_NULL))
+ {
+ put_str("I don't have a quest for you at the moment.", 8, 0);
+ return FALSE;
+ }
+
+ q_ptr = &quest[plots[plot]];
+
+ /* Quest is completed */
+ if (q_ptr->status == QUEST_STATUS_COMPLETED)
+ {
+ /* Rewarded quest */
+ q_ptr->status = QUEST_STATUS_FINISHED;
+
+ process_hooks(HOOK_QUEST_FINISH, "(d)", plots[plot]);
+
+ return (TRUE);
+ }
+
+ /* Quest is still unfinished */
+ else if (q_ptr->status == QUEST_STATUS_TAKEN)
+ {
+ put_str("You have not completed your current quest yet!", 8, 0);
+ put_str("Use CTRL-Q to check the status of your quest.", 9, 0);
+ put_str("Return when you have completed your quest.", 12, 0);
+
+ return (FALSE);
+ }
+ /* Failed quest */
+ else if (q_ptr->status == QUEST_STATUS_FAILED)
+ {
+ /* Mark quest as done (but failed) */
+ q_ptr->status = QUEST_STATUS_FAILED_DONE;
+
+ process_hooks(HOOK_QUEST_FAIL, "(d)", plots[plot]);
+
+ return (FALSE);
+ }
+ /* No quest yet */
+ else if (q_ptr->status == QUEST_STATUS_UNTAKEN)
+ {
+ if (process_hooks(HOOK_INIT_QUEST, "(d)", plots[plot])) return (FALSE);
+
+ q_ptr->status = QUEST_STATUS_TAKEN;
+
+ /* Assign a new quest */
+ get_questinfo(plots[plot]);
+
+ /* Add the hooks */
+ if (quest[plots[plot]].type == HOOK_TYPE_C) quest[plots[plot]].init(plots[plot]);
+
+ return (TRUE);
+ }
+
+ return FALSE;
+}
+
+/*
+ * Displaying town history -KMW-
+ */
+static void town_history(void)
+{
+ /* Save screen */
+ screen_save();
+
+ /* Peruse the building help file */
+ (void)show_file("bldg.txt", NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+}
+
+
+/*
+ * compare_weapon_aux2 -KMW-
+ */
+static void compare_weapon_aux2(object_type *o_ptr, int numblows, int r, int c, int mult, char attr[80], u32b f1, u32b f2, u32b f3, byte color)
+{
+ char tmp_str[80];
+
+ c_put_str(color, attr, r, c);
+ strnfmt(tmp_str, 80, "Attack: %d-%d damage",
+ numblows * ((o_ptr->dd * mult) + o_ptr->to_d),
+ numblows * ((o_ptr->ds * o_ptr->dd * mult) + o_ptr->to_d));
+ put_str(tmp_str, r, c + 8);
+ r++;
+}
+
+
+/*
+ * compare_weapon_aux1 -KMW-
+ */
+static void compare_weapon_aux1(object_type *o_ptr, int col, int r)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ if (f1 & (TR1_SLAY_ANIMAL))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Animals:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_EVIL))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Evil:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_UNDEAD))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Undead:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_DEMON))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Demons:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_ORC))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Orcs:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_TROLL))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Trolls:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_GIANT))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Giants:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_DRAGON))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Dragons:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_KILL_DRAGON))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 5, "Dragons:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_BRAND_ACID))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Acid:",
+ f1, f2, f3, TERM_RED);
+ }
+ if (f1 & (TR1_BRAND_ELEC))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Elec:",
+ f1, f2, f3, TERM_RED);
+ }
+ if (f1 & (TR1_BRAND_FIRE))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Fire:",
+ f1, f2, f3, TERM_RED);
+ }
+ if (f1 & (TR1_BRAND_COLD))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Cold:",
+ f1, f2, f3, TERM_RED);
+ }
+ if (f1 & (TR1_BRAND_POIS))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Poison:",
+ f1, f2, f3, TERM_RED);
+ }
+}
+
+
+/*
+ * list_weapon -KMW-
+ */
+static void list_weapon(object_type *o_ptr, int row, int col)
+{
+ char o_name[80];
+
+ char tmp_str[80];
+
+
+ object_desc(o_name, o_ptr, TRUE, 0);
+ c_put_str(TERM_YELLOW, o_name, row, col);
+ strnfmt(tmp_str, 80, "To Hit: %d To Damage: %d", o_ptr->to_h, o_ptr->to_d);
+ put_str(tmp_str, row + 1, col);
+ strnfmt(tmp_str, 80, "Dice: %d Sides: %d", o_ptr->dd, o_ptr->ds);
+ put_str(tmp_str, row + 2, col);
+ strnfmt(tmp_str, 80, "Number of Blows: %d", p_ptr->num_blow);
+ put_str(tmp_str, row + 3, col);
+ c_put_str(TERM_YELLOW, "Possible Damage:", row + 5, col);
+ strnfmt(tmp_str, 80, "One Strike: %d-%d damage", o_ptr->dd + o_ptr->to_d,
+ (o_ptr->ds*o_ptr->dd) + o_ptr->to_d);
+ put_str(tmp_str, row + 6, col + 1);
+ strnfmt(tmp_str, 80, "One Attack: %d-%d damage", p_ptr->num_blow*(o_ptr->dd + o_ptr->to_d),
+ p_ptr->num_blow*(o_ptr->ds*o_ptr->dd + o_ptr->to_d));
+ put_str(tmp_str, row + 7, col + 1);
+}
+
+
+/*
+ * Select melee weapons
+ */
+static bool_ item_tester_hook_melee_weapon(object_type *o_ptr)
+{
+ return (wield_slot(o_ptr) == INVEN_WIELD);
+}
+
+/*
+ * compare_weapons -KMW-
+ */
+static bool_ compare_weapons(void)
+{
+ int item, item2, i;
+
+ object_type *o1_ptr, *o2_ptr, *orig_ptr;
+
+ object_type *i_ptr;
+
+ cptr q, s;
+
+
+ clear_bldg(6, 18);
+
+ o1_ptr = NULL;
+ o2_ptr = NULL;
+ i_ptr = NULL;
+
+ /* Store copy of original wielded weapon in pack slot */
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ orig_ptr = &p_ptr->inventory[INVEN_PACK];
+ object_copy(orig_ptr, i_ptr);
+
+ i = 6;
+ /* Get first weapon */
+ /* Restrict choices to meele weapons */
+ item_tester_hook = item_tester_hook_melee_weapon;
+
+ q = "What is your first melee weapon? ";
+ s = "You have nothing to compare.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN)))
+ {
+ object_wipe(orig_ptr);
+ return (FALSE);
+ }
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ o1_ptr = &p_ptr->inventory[item];
+
+ /* Get second weapon */
+ /* Restrict choices to melee weapons */
+ item_tester_hook = item_tester_hook_melee_weapon;
+
+ q = "What is your second melee weapon? ";
+ s = "You have nothing to compare.";
+ if (!get_item(&item2, q, s, (USE_EQUIP | USE_INVEN)))
+ {
+ object_wipe(orig_ptr);
+ return (FALSE);
+ }
+
+ /* Get the item (in the pack) */
+ if (item2 >= 0) o2_ptr = &p_ptr->inventory[item2];
+
+ put_str("Based on your current abilities, here is what your weapons will do", 4, 2);
+
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ object_copy(i_ptr, o1_ptr);
+ calc_bonuses(TRUE);
+
+ list_weapon(o1_ptr, i, 2);
+ compare_weapon_aux1(o1_ptr, 2, i + 8);
+
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ if (item2 == INVEN_WIELD)
+ object_copy(i_ptr, orig_ptr);
+ else
+ object_copy(i_ptr, o2_ptr);
+ calc_bonuses(TRUE);
+
+ list_weapon(o2_ptr, i, 40);
+ compare_weapon_aux1(o2_ptr, 40, i + 8);
+
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ object_copy(i_ptr, orig_ptr);
+ calc_bonuses(TRUE);
+
+ object_wipe(orig_ptr);
+
+ put_str("(Only highest damage applies per monster. Special damage not cumulative)", 20, 0);
+
+ return (TRUE);
+}
+
+
+/*
+ * general all-purpose fixing routine for items from building personnel
+ * sharpen arrows, repair armor, repair weapon
+ * -KMW-
+ */
+static bool_ fix_item(int istart, int iend, int ispecific, bool_ iac,
+ int ireward, bool_ set_reward)
+{
+ int i;
+
+ int j = 9;
+
+ int maxenchant = (p_ptr->lev / 5);
+
+ object_type *o_ptr;
+
+ char out_val[80], tmp_str[80];
+
+ bool_ repaired = FALSE;
+
+ clear_bldg(5, 18);
+ strnfmt(tmp_str, 80, " Based on your skill, we can improve up to +%d", maxenchant);
+ prt(tmp_str, 5, 0);
+ prt("Status", 7, 30);
+
+ for (i = istart; i <= iend; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+ if (ispecific > 0)
+ {
+ if (o_ptr->tval != ispecific)
+ continue;
+ }
+
+ if (o_ptr->tval)
+ {
+ object_desc(tmp_str, o_ptr, FALSE, 1);
+
+ if ((o_ptr->name1 && (o_ptr->ident & 0x08)))
+ strnfmt(out_val, 80, "%-40s: beyond our skills!", tmp_str);
+ else if (o_ptr->name1)
+ strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str);
+ else
+ {
+ if ((iac) && (o_ptr->to_a <= -3))
+ {
+ strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str);
+ }
+ else if ((iac) && (o_ptr->to_a < maxenchant))
+ {
+ o_ptr->to_a++;
+ strnfmt(out_val, 80, "%-40s: polished -> (%d)", tmp_str, o_ptr->to_a);
+ repaired = TRUE;
+ }
+ else if ((!iac) && ((o_ptr->to_h <= -3) || (o_ptr->to_d <= -3)))
+ {
+ strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str);
+ }
+ /* Sharpen a weapon */
+ else if ((!iac) && ((o_ptr->to_h < maxenchant) ||
+ (o_ptr->to_d < maxenchant)))
+ {
+ if (o_ptr->to_h < maxenchant)
+ o_ptr->to_h++;
+ if (o_ptr->to_d < maxenchant)
+ o_ptr->to_d++;
+ strnfmt(out_val, 80, "%-40s: sharpened -> (%d,%d)", tmp_str,
+ o_ptr->to_h, o_ptr->to_d);
+ repaired = TRUE;
+ }
+ else
+ strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str);
+ }
+ prt(out_val, j++, 0);
+ }
+ }
+
+ if (!repaired)
+ {
+ msg_print("You don't have anything appropriate.");
+ msg_print(NULL);
+ }
+ else
+ {
+ msg_print("Press the spacebar to continue");
+ msg_print(NULL);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ p_ptr->update |= (PU_BONUS);
+ }
+ clear_bldg(5, 18);
+
+ return (repaired);
+}
+
+
+/*
+ * Research Item
+ */
+static bool_ research_item(void)
+{
+ clear_bldg(5, 18);
+ return (identify_fully());
+}
+
+
+/*
+ * Show the current quest monster.
+ */
+static void show_quest_monster(void)
+{
+ monster_race* r_ptr = &r_info[bounties[0][0]];
+
+
+ msg_format("Quest monster: %s. "
+ "Need to turn in %d corpse%s to receive reward.",
+ r_name + r_ptr->name, bounties[0][1],
+ (bounties[0][1] > 1 ? "s" : ""));
+ msg_print(NULL);
+}
+
+
+/*
+ * Show the current bounties.
+ */
+static void show_bounties(void)
+{
+ int i, j = 6;
+
+ monster_race* r_ptr;
+
+ char buff[80];
+
+
+ clear_bldg(7, 18);
+
+ c_prt(TERM_YELLOW, "Currently active bounties:", 4, 2);
+
+ for (i = 1; i < MAX_BOUNTIES; i++, j++)
+ {
+ r_ptr = &r_info[bounties[i][0]];
+
+ strnfmt(buff, 80, "%-30s (%d gp)", r_name + r_ptr->name, bounties[i][1]);
+
+ prt(buff, j, 2);
+
+ if (j >= 17)
+ {
+ msg_print("Press space for more.");
+ msg_print(NULL);
+
+ clear_bldg(7, 18);
+ j = 5;
+ }
+ }
+}
+
+
+/*
+ * Filter for corpses that currently have a bounty on them.
+ */
+static bool_ item_tester_hook_bounty(object_type* o_ptr)
+{
+ int i;
+
+
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ for (i = 1; i < MAX_BOUNTIES; i++)
+ {
+ if (bounties[i][0] == o_ptr->pval2) return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+/* Filter to match the quest monster's corpse. */
+static bool_ item_tester_hook_quest_monster(object_type* o_ptr)
+{
+ if ((o_ptr->tval == TV_CORPSE) &&
+ (o_ptr->pval2 == bounties[0][0])) return (TRUE);
+ return (FALSE);
+}
+
+
+/*
+ * Return the boost in the corpse's value depending on how rare the body
+ * part is.
+ */
+static int corpse_value_boost(int sval)
+{
+ switch (sval)
+ {
+ case SV_CORPSE_HEAD:
+ case SV_CORPSE_SKULL:
+ {
+ return (1);
+ }
+
+ /* Default to no boost. */
+ default:
+ {
+ return (0);
+ }
+ }
+}
+
+/*
+ * Sell a corpse, if there's currently a bounty on it.
+ */
+static void sell_corpses(void)
+{
+ object_type* o_ptr;
+
+ int i, boost = 0;
+
+ s16b value;
+
+ int item;
+
+
+ /* Set the hook. */
+ item_tester_hook = item_tester_hook_bounty;
+
+ /* Select a corpse to sell. */
+ if (!get_item(&item, "Sell which corpse",
+ "You have no corpses you can sell.", USE_INVEN)) return;
+
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Exotic body parts are worth more. */
+ boost = corpse_value_boost(o_ptr->sval);
+
+ /* Try to find a match. */
+ for (i = 1; i < MAX_BOUNTIES; i++)
+ {
+ if (o_ptr->pval2 == bounties[i][0])
+ {
+ value = bounties[i][1] + boost * (r_info[o_ptr->pval2].level);
+
+ msg_format("Sold for %ld gold pieces.", value);
+ msg_print(NULL);
+ p_ptr->au += value;
+
+ /* Increase the number of collected bounties */
+ total_bounties++;
+
+ inc_stack_size(item, -1);
+
+ return;
+ }
+ }
+
+ msg_print("Sorry, but that monster does not have a bounty on it.");
+ msg_print(NULL);
+}
+
+
+
+/*
+ * Hook for bounty monster selection.
+ */
+static bool_ mon_hook_bounty(int r_idx)
+{
+ monster_race* r_ptr = &r_info[r_idx];
+
+
+ /* Reject uniques */
+ if (r_ptr->flags1 & RF1_UNIQUE) return (FALSE);
+
+ /* Reject those who cannot leave anything */
+ if (!(r_ptr->flags9 & RF9_DROP_CORPSE) &&
+ !(r_ptr->flags9 & RF9_DROP_SKELETON)) return (FALSE);
+
+ /* Reject pets */
+ if (r_ptr->flags7 & RF7_PET) return (FALSE);
+
+ /* Reject friendly creatures */
+ if (r_ptr->flags7 & RF7_FRIENDLY) return (FALSE);
+
+ /* The rest are acceptable */
+ return (TRUE);
+}
+
+
+static void select_quest_monster(void)
+{
+ monster_race* r_ptr;
+
+ int amt;
+
+
+ /*
+ * Set up the hooks -- no bounties on uniques or monsters
+ * with no corpses
+ */
+ get_mon_num_hook = mon_hook_bounty;
+ get_mon_num_prep();
+
+ /* Set up the quest monster. */
+ bounties[0][0] = get_mon_num(p_ptr->lev);
+
+ r_ptr = &r_info[bounties[0][0]];
+
+ /*
+ * Select the number of monsters needed to kill. Groups and
+ * breeders require more
+ */
+ amt = randnor(5, 3);
+
+ if (amt < 2) amt = 2;
+
+ if (r_ptr->flags1 & RF1_FRIEND) amt *= 3; amt /= 2;
+ if (r_ptr->flags1 & RF1_FRIENDS) amt *= 2;
+ if (r_ptr->flags4 & RF4_MULTIPLY) amt *= 3;
+
+ if (r_ptr->flags7 & RF7_AQUATIC) amt /= 2;
+
+ bounties[0][1] = amt;
+
+ /* Undo the filters */
+ get_mon_num_hook = NULL;
+ get_mon_num_prep();
+}
+
+
+
+/*
+ * Sell a corpse for a reward.
+ */
+static void sell_quest_monster(void)
+{
+ object_type* o_ptr;
+
+ int item;
+
+
+ /* Set the hook. */
+ item_tester_hook = item_tester_hook_quest_monster;
+
+ /* Select a corpse to sell. */
+ if (!get_item(&item, "Sell which corpse",
+ "You have no corpses you can sell.", USE_INVEN)) return;
+
+ o_ptr = &p_ptr->inventory[item];
+
+ bounties[0][1] -= o_ptr->number;
+
+ /* Completed the quest. */
+ if (bounties[0][1] <= 0)
+ {
+ int m;
+ monster_race *r_ptr;
+
+ cmsg_print(TERM_YELLOW, "You have completed your quest!");
+ msg_print(NULL);
+
+ /* Give full knowledge */
+
+ /* Hack -- Maximal info */
+ r_ptr = &r_info[bounties[0][0]];
+
+ msg_print(format("Well done! As a reward I'll teach you everything "
+ "about the %s, (check your recall)",
+ r_name + r_ptr->name));
+
+ r_ptr->r_wake = r_ptr->r_ignore = MAX_UCHAR;
+
+ /* Observe "maximal" attacks */
+ for (m = 0; m < 4; m++)
+ {
+ /* Examine "actual" blows */
+ if (r_ptr->blow[m].effect || r_ptr->blow[m].method)
+ {
+ /* Hack -- maximal observations */
+ r_ptr->r_blows[m] = MAX_UCHAR;
+ }
+ }
+
+ /* Hack -- maximal drops */
+ r_ptr->r_drop_gold = r_ptr->r_drop_item =
+ (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0));
+
+ /* Hack -- but only "valid" drops */
+ if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0;
+ if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0;
+
+ /* Hack -- observe many spells */
+ r_ptr->r_cast_inate = MAX_UCHAR;
+ r_ptr->r_cast_spell = MAX_UCHAR;
+
+ /* Hack -- know all the flags */
+ r_ptr->r_flags1 = r_ptr->flags1;
+ r_ptr->r_flags2 = r_ptr->flags2;
+ r_ptr->r_flags3 = r_ptr->flags3;
+ r_ptr->r_flags4 = r_ptr->flags4;
+ r_ptr->r_flags5 = r_ptr->flags5;
+ r_ptr->r_flags6 = r_ptr->flags6;
+ r_ptr->r_flags4 = r_ptr->flags7;
+ r_ptr->r_flags5 = r_ptr->flags8;
+ r_ptr->r_flags6 = r_ptr->flags9;
+
+ msg_print(NULL);
+
+ select_quest_monster();
+
+ }
+ else
+ {
+ msg_format("Well done, only %d more to go.", bounties[0][1]);
+ msg_print(NULL);
+ }
+
+ inc_stack_size(item, -1);
+}
+
+
+
+/*
+ * Fill the bounty list with monsters.
+ */
+void select_bounties(void)
+{
+ int i, j;
+
+
+ select_quest_monster();
+
+ /*
+ * Set up the hooks -- no bounties on uniques or monsters
+ * with no corpses
+ */
+ get_mon_num_hook = mon_hook_bounty;
+ get_mon_num_prep();
+
+ for (i = 1; i < MAX_BOUNTIES; i++)
+ {
+ int lev = i * 5 + randnor(0, 2);
+ monster_race* r_ptr;
+ s16b r_idx;
+ s16b val;
+
+ if (lev < 1) lev = 1;
+
+ if (lev >= MAX_DEPTH) lev = MAX_DEPTH - 1;
+
+ /* We don't want to duplicate entries in the list */
+ while (TRUE)
+ {
+ r_idx = get_mon_num(lev);
+
+ for (j = 0; j < i; j++)
+ {
+ if (bounties[j][0] == r_idx) continue;
+ }
+
+ break;
+ }
+
+ bounties[i][0] = r_idx;
+
+ r_ptr = &r_info[r_idx];
+
+ val = r_ptr->mexp + r_ptr->level * 20 + randnor(0, r_ptr->level * 2);
+
+ if (val < 1) val = 1;
+
+ bounties[i][1] = val;
+ }
+
+ /* Undo the filters. */
+ get_mon_num_hook = NULL;
+ get_mon_num_prep();
+}
+
+/*
+ * Execute a building command
+ */
+bool_ bldg_process_command(store_type *s_ptr, int i)
+{
+ store_action_type *ba_ptr = &ba_info[st_info[s_ptr->st_idx].actions[i]];
+
+ int bact = ba_ptr->action;
+
+ int bcost;
+
+ bool_ paid = FALSE;
+
+ bool_ set_reward = FALSE;
+
+ bool_ recreate = FALSE;
+
+
+ if (is_state(s_ptr, STORE_LIKED))
+ {
+ bcost = ba_ptr->costs[STORE_LIKED];
+ }
+ else if (is_state(s_ptr, STORE_HATED))
+ {
+ bcost = ba_ptr->costs[STORE_HATED];
+ }
+ else
+ {
+ bcost = ba_ptr->costs[STORE_NORMAL];
+ }
+
+ /* action restrictions */
+ if (((ba_ptr->action_restr == 1) && (is_state(s_ptr, STORE_LIKED))) ||
+ ((ba_ptr->action_restr == 2) && (!is_state(s_ptr, STORE_LIKED))))
+ {
+ msg_print("You have no right to choose that!");
+ msg_print(NULL);
+ return FALSE;
+ }
+
+ /* If player has loan and the time is out, few things work in stores */
+ if (p_ptr->loan && !p_ptr->loan_time)
+ {
+ if ((bact != BACT_SELL) && (bact != BACT_VIEW_BOUNTIES) &&
+ (bact != BACT_SELL_CORPSES) &&
+ (bact != BACT_VIEW_QUEST_MON) &&
+ (bact != BACT_SELL_QUEST_MON) &&
+ (bact != BACT_EXAMINE) && (bact != BACT_STEAL) &&
+ (bact != BACT_PAY_BACK_LOAN))
+ {
+ msg_print("You are not allowed to do that until you have paid back your loan.");
+ msg_print(NULL);
+ return FALSE;
+ }
+ }
+
+ /* check gold */
+ if (bcost > p_ptr->au)
+ {
+ msg_print("You do not have the gold!");
+ msg_print(NULL);
+ return FALSE;
+ }
+
+ if (!bcost) set_reward = TRUE;
+
+ switch (bact)
+ {
+ case BACT_RESEARCH_ITEM:
+ {
+ paid = research_item();
+ break;
+ }
+
+ case BACT_TOWN_HISTORY:
+ {
+ town_history();
+ break;
+ }
+
+ case BACT_RACE_LEGENDS:
+ {
+ race_legends();
+ break;
+ }
+
+ case BACT_QUEST1:
+ case BACT_QUEST2:
+ case BACT_QUEST3:
+ case BACT_QUEST4:
+ {
+ int y = 1, x = 1;
+ bool_ ok = FALSE;
+
+ while ((x < cur_wid - 1) && !ok)
+ {
+ y = 1;
+ while ((y < cur_hgt - 1) && !ok)
+ {
+ /* Found the location of the quest info ? */
+ if (bact - BACT_QUEST1 + FEAT_QUEST1 == cave[y][x].feat)
+ {
+ /* Stop the loop */
+ ok = TRUE;
+ }
+ y++;
+ }
+ x++;
+ }
+
+ if (ok)
+ {
+ recreate = castle_quest(y - 1, x - 1);
+ ;
+ }
+ else
+ {
+ msg_format("ERROR: no quest info feature found: %d", bact - BACT_QUEST1 + FEAT_QUEST1);
+ }
+ break;
+ }
+
+ case BACT_KING_LEGENDS:
+ case BACT_ARENA_LEGENDS:
+ case BACT_LEGENDS:
+ {
+ show_highclass(building_loc);
+ break;
+ }
+
+ case BACT_POSTER:
+ case BACT_ARENA_RULES:
+ case BACT_ARENA:
+ {
+ arena_comm(bact);
+ break;
+ }
+
+ case BACT_IN_BETWEEN:
+ case BACT_CRAPS:
+ case BACT_SPIN_WHEEL:
+ case BACT_DICE_SLOTS:
+ case BACT_GAMBLE_RULES:
+ {
+ gamble_comm(bact);
+ break;
+ }
+
+ case BACT_REST:
+ case BACT_RUMORS:
+ case BACT_FOOD:
+ {
+ paid = inn_comm(bact);
+ break;
+ }
+
+ case BACT_RESEARCH_MONSTER:
+ {
+ paid = !research_mon();
+ break;
+ }
+
+ case BACT_COMPARE_WEAPONS:
+ {
+ paid = compare_weapons();
+ break;
+ }
+
+ case BACT_ENCHANT_WEAPON:
+ {
+ paid = fix_item(INVEN_WIELD, INVEN_WIELD, 0, FALSE,
+ BACT_ENCHANT_WEAPON, set_reward);
+ break;
+ }
+
+ case BACT_ENCHANT_ARMOR:
+ {
+ paid = fix_item(INVEN_BODY, INVEN_FEET, 0, TRUE,
+ BACT_ENCHANT_ARMOR, set_reward);
+ break;
+ }
+
+ /* needs work */
+ case BACT_RECHARGE:
+ {
+ if (recharge(80)) paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_IDENTS:
+ {
+ identify_pack();
+ msg_print("Your possessions have been identified.");
+ msg_print(NULL);
+ paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_STAR_HEAL:
+ {
+ hp_player(200);
+ set_poisoned(0);
+ set_blind(0);
+ set_confused(0);
+ set_cut(0);
+ set_stun(0);
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ p_ptr->black_breath = FALSE;
+ }
+ paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_HEALING:
+ {
+ hp_player(200);
+ set_poisoned(0);
+ set_blind(0);
+ set_confused(0);
+ set_cut(0);
+ set_stun(0);
+ paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_RESTORE:
+ {
+ if (do_res_stat(A_STR, TRUE)) paid = TRUE;
+ if (do_res_stat(A_INT, TRUE)) paid = TRUE;
+ if (do_res_stat(A_WIS, TRUE)) paid = TRUE;
+ if (do_res_stat(A_DEX, TRUE)) paid = TRUE;
+ if (do_res_stat(A_CON, TRUE)) paid = TRUE;
+ if (do_res_stat(A_CHR, TRUE)) paid = TRUE;
+ break;
+ }
+
+ case BACT_ENCHANT_ARROWS:
+ {
+ paid = fix_item(0, INVEN_WIELD, TV_ARROW, FALSE,
+ BACT_ENCHANT_ARROWS, set_reward);
+ break;
+ }
+
+ case BACT_ENCHANT_BOW:
+ {
+ paid = fix_item(INVEN_BOW, INVEN_BOW, TV_BOW, FALSE,
+ BACT_ENCHANT_BOW, set_reward);
+ break;
+ }
+
+ case BACT_RECALL:
+ {
+ p_ptr->word_recall = 1;
+ msg_print("The air about you becomes charged...");
+ paid = TRUE;
+ break;
+ }
+
+ case BACT_TELEPORT_LEVEL:
+ {
+ if (reset_recall(FALSE))
+ {
+ p_ptr->word_recall = 1;
+ msg_print("The air about you becomes charged...");
+ paid = TRUE;
+ }
+ break;
+ }
+
+ case BACT_MIMIC_NORMAL:
+ {
+ set_mimic(0, 0, 0);
+ paid = TRUE;
+ break;
+ }
+
+ case BACT_VIEW_BOUNTIES:
+ {
+ show_bounties();
+ break;
+ }
+
+ case BACT_VIEW_QUEST_MON:
+ {
+ show_quest_monster();
+ break;
+ }
+
+ case BACT_SELL_QUEST_MON:
+ {
+ sell_quest_monster();
+ break;
+ }
+
+ case BACT_SELL_CORPSES:
+ {
+ sell_corpses();
+ break;
+ }
+
+ case BACT_DIVINATION:
+ {
+ int i, count = 0;
+ bool_ something = FALSE;
+
+ while (count < 1000)
+ {
+ count++;
+ i = rand_int(MAX_FATES);
+ if (!fates[i].fate) continue;
+ if (fates[i].know) continue;
+ msg_print("You know a little more of your fate.");
+
+ fates[i].know = TRUE;
+ something = TRUE;
+ break;
+ }
+
+ if (!something) msg_print("Well, you have no fate, but I'll keep your money anyway!");
+
+ paid = TRUE;
+ break;
+
+ }
+
+ case BACT_BUY:
+ {
+ store_purchase();
+ break;
+ }
+
+ case BACT_SELL:
+ {
+ store_sell();
+ break;
+ }
+
+ case BACT_EXAMINE:
+ {
+ store_examine();
+ break;
+ }
+
+ case BACT_STEAL:
+ {
+ store_stole();
+ break;
+ }
+
+ case BACT_REQUEST_ITEM:
+ {
+ store_request_item();
+ paid = TRUE;
+ break;
+ }
+
+ case BACT_GET_LOAN:
+ {
+ s32b i, req;
+ char prompt[80];
+
+ if (p_ptr->loan)
+ {
+ msg_print("You already have a loan!");
+ break;
+ }
+
+ req = p_ptr->au;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ req += object_value_real(&p_ptr->inventory[i]);
+
+ if (req > 100000) req = 100000;
+ if ((req + p_ptr->au) > PY_MAX_GOLD) req = PY_MAX_GOLD - p_ptr->au;
+
+ strnfmt(prompt, sizeof (prompt),
+ "How much would you like to get (0-%ld) ?", req);
+
+ req = get_quantity(prompt, req);
+
+ if (req)
+ {
+ p_ptr->loan += req;
+ p_ptr->au += req;
+ p_ptr->loan_time += req;
+
+ msg_format("You receive %i gold pieces", req);
+
+ paid = TRUE;
+ }
+ else
+ msg_format("You did not request any money!");
+
+ break;
+ }
+
+ case BACT_PAY_BACK_LOAN:
+ {
+ s32b req;
+ char prompt[80];
+
+ if (p_ptr->loan)
+ {
+ msg_format("You have nothing to payback!");
+ break;
+ }
+
+ msg_format("You have a loan of %i.", p_ptr->loan);
+
+ req = ((p_ptr->loan + bcost) > p_ptr->au) ? p_ptr->au - bcost : p_ptr->loan;
+
+ strnfmt(prompt, sizeof (prompt),
+ "How much would you like to pay back (0-%ld) ?", req);
+
+ req = get_quantity(prompt, req);
+
+ p_ptr->loan -= req;
+ p_ptr->au -= req;
+
+ if (!p_ptr->loan) p_ptr->loan_time = 0;
+
+ msg_format("You pay back %i gold pieces", req);
+ paid = TRUE;
+ break;
+ }
+
+ default:
+ {
+ if (process_hooks_ret(HOOK_BUILDING_ACTION, "dd", "(d)", bact))
+ {
+ paid = process_hooks_return[0].num;
+ recreate = process_hooks_return[1].num;
+ }
+ break;
+ }
+ }
+
+ if (paid)
+ {
+ p_ptr->au -= bcost;
+
+ /* Display the current gold */
+ store_prt_gold();
+ }
+
+ return (recreate);
+}
+
+
+/*
+ * Enter quest level
+ */
+void enter_quest(void)
+{
+ if (!(cave[p_ptr->py][p_ptr->px].feat == FEAT_QUEST_ENTER))
+ {
+ msg_print("You see no quest level here.");
+ return;
+ }
+ else
+ {
+ /* Player enters a new quest */
+ p_ptr->oldpy = p_ptr->py;
+ p_ptr->oldpx = p_ptr->px;
+
+ leaving_quest = p_ptr->inside_quest;
+
+ p_ptr->inside_quest = cave[p_ptr->py][p_ptr->px].special;
+ dun_level = 1;
+ p_ptr->leaving = TRUE;
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ }
+}
+
+
+/*
+ * Do building commands
+ */
+void do_cmd_bldg(void)
+{
+ int i, which, x = p_ptr->px, y = p_ptr->py;
+
+ char command;
+
+ bool_ validcmd;
+
+ store_type *s_ptr;
+
+ store_action_type *ba_ptr;
+
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_SHOP)
+ {
+ msg_print("You see no building here.");
+ return;
+ }
+
+ which = cave[p_ptr->py][p_ptr->px].special;
+ building_loc = which;
+
+ s_ptr = &town_info[p_ptr->town_num].store[which];
+
+ p_ptr->oldpy = p_ptr->py;
+ p_ptr->oldpx = p_ptr->px;
+
+ /* Forget the lite */
+ /* forget_lite(); */
+
+ /* Forget the view */
+ forget_view();
+
+ /* Hack -- Increase "icky" depth */
+ character_icky++;
+
+ command_arg = 0;
+ command_rep = 0;
+ command_new = 0;
+
+ show_building(s_ptr);
+ leave_bldg = FALSE;
+
+ while (!leave_bldg)
+ {
+ validcmd = FALSE;
+ prt("", 1, 0);
+ command = inkey();
+
+ if (command == ESCAPE)
+ {
+ leave_bldg = TRUE;
+ p_ptr->inside_arena = FALSE;
+ break;
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ ba_ptr = &ba_info[st_info->actions[i]];
+
+ if (ba_ptr->letter)
+ {
+ if (ba_ptr->letter == command)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ if (ba_ptr->letter_aux)
+ {
+ if (ba_ptr->letter_aux == command)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (validcmd)
+ bldg_process_command(s_ptr, i);
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Flush messages XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Reinit wilderness to activate quests ... */
+ wilderness_gen(TRUE);
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ /* Hack -- Decrease "icky" depth */
+ character_icky--;
+
+ /* Clear the screen */
+ Term_clear();
+
+ /* Update the visuals */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE | PU_MONSTERS | PU_BONUS);
+
+ /* Redraw entire screen */
+ p_ptr->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
diff --git a/src/carbon/Angband.icns b/src/carbon/Angband.icns
new file mode 100644
index 00000000..3d775739
--- /dev/null
+++ b/src/carbon/Angband.icns
Binary files differ
diff --git a/src/carbon/Carbon.r b/src/carbon/Carbon.r
new file mode 100644
index 00000000..e3194dd2
--- /dev/null
+++ b/src/carbon/Carbon.r
@@ -0,0 +1,1568 @@
+/*
+ * Minimal Resources for T.o.M.E.
+ *
+ * Turned into human-readable and programmer-friendly format by pelpel
+ *
+ *
+ * This might help mac/non-mac coders to play, modify, hack and do
+ * whatever they like with this file.
+ *
+ *
+ * Header files and reasons for their inclusion:
+ *
+ * MacTypes.r - 'STR ' and 'STR#'
+ * Finder.r - 'BNDL' and 'FREF'
+ * Dialogs.r - 'ALRT', 'DITL' and 'DLOG'
+ * Menus.r - 'MENU' and 'MBAR'
+ * Processes.r - 'SIZE'
+ */
+
+#include <MacTypes.r>
+#include <Finder.r>
+#include <Dialogs.r>
+#include <Menus.r>
+#include <Processes.r>
+
+
+#ifndef MACH_O
+
+/*
+ * Signature - Who am I?
+ * Vanilla uses 'A271'
+ * ID should always be 0.
+ */
+#define AngbandSignature 'PrnA'
+
+type AngbandSignature as 'STR ';
+
+resource AngbandSignature (0, "Owner resource", purgeable)
+{
+ "T.o.M.E. 2.3.4"
+};
+
+
+/* OS X Finder requires this to recognise a Carbon-compatible PEF binary */
+data 'plst' (0)
+{
+ "$00";
+};
+
+
+/*
+ * Inform system of program's characteristics
+ * ID should always be -1.
+ */
+resource 'SIZE' (-1)
+{
+ /*
+ * Flags dumped = 0101 1000 1100 0000
+ */
+ reserved,
+
+ /* accepts/ignores suspend&resume events? */
+ acceptSuspendResumeEvents,
+
+ reserved,
+
+ /* can use background null events */
+ canBackground,
+
+ /* activates own windows in response to OS events */
+ doesActivateOnFGSwitch,
+
+ /* app has a user interface */
+ backgroundAndForeground,
+
+ /* don't return mouse events in front window on resume */
+ dontGetFrontClicks,
+
+ /* applications use this */
+ ignoreAppDiedEvents,
+
+ /* works with 24- or 32-bit addr */
+ is32BitCompatible,
+
+ /* can use high-level events */
+ isHighLevelEventAware,
+
+ /* only local high-level events */
+ onlyLocalHLEvents,
+
+ /* can't use stationery documents */
+ notStationeryAware,
+
+ /* can't use inline services */
+ dontUseTextEditServices,
+
+ /* all windows redrawn when monitor(s) change */
+ notDisplayManagerAware,
+
+ reserved,
+ reserved,
+
+ /* preferred memory size */
+ 16 * (1024 * 1024),
+
+ /* minimum memory size */
+ 4 * (1024 * 1024)
+};
+
+
+/*
+ * File types used by Angband
+ */
+resource 'FREF' (128, purgeable)
+{
+ /* file type */
+ 'APPL',
+
+ /* maps to icon list resource w/ local ID 0 in bundle resource */
+ 0,
+
+ /* leave empty string for name */
+ ""
+};
+
+resource 'FREF' (129, purgeable)
+{
+ /* file type */
+ 'SAVE',
+
+ /* maps to icon list resource w/ local ID 1 in bundle resource */
+ 1,
+
+ /* leave empty string for name */
+ ""
+};
+
+resource 'FREF' (130, purgeable)
+{
+ /* file type */
+ 'TEXT',
+
+ /* maps to icon list resource w/ local ID 2 in bundle resource */
+ 2,
+
+ /* leave empty string for name */
+ ""
+};
+
+resource 'FREF' (131, purgeable)
+{
+ /* file type */
+ 'DATA',
+
+ /* maps to icon list resource w/ local ID 3 in bundle resource */
+ 3,
+
+ /* leave empty string for name */
+ ""
+};
+
+
+/*
+ * Bundle information
+ */
+resource 'BNDL' (128, purgeable)
+{
+ /* Our signature */
+ AngbandSignature,
+
+ /* resource ID of signature resource: should always be 0 */
+ 0,
+
+ {
+ /* mapping local IDs in 'FREF's to 'ICN#' IDs */
+ 'ICN#',
+ {
+ /* local ID 0 -> ICN# 128 */
+ 0, 128,
+
+ /* local ID 1 -> ICN# 129 */
+ 1, 129,
+
+ /* local ID 2 -> ICN# 130 */
+ 2, 130,
+
+ /* local ID 3 -> ICN# 131 */
+ 3, 131
+ },
+
+ /* local res IDs for 'FREF's: no duplicates */
+ 'FREF',
+ {
+ /* local ID 0 -> FREF 128 */
+ 0, 128,
+
+ /* local ID 1 -> FREF 129 */
+ 1, 129,
+
+ /* local ID 2 -> FREF 130 */
+ 2, 130,
+
+ /* local ID 3 -> FREF 131 */
+ 3, 131
+ }
+ }
+};
+
+#endif /* !MACH_O */
+
+/*
+ * Menu definitions
+ */
+resource 'MENU' (128, preload)
+{
+ /* menu ID */
+ 128,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* everything but the divider is enabled */
+ 0b11111111111111111111111111111101,
+ /* or we can use 0... */
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ apple,
+
+ /* its contents */
+ {
+ /* First item */
+ "About T.o.M.E. ...", noicon, nokey, nomark, plain;
+
+ /* Second item - divider */
+ "-", noicon, nokey, nomark, plain;
+ }
+};
+
+resource 'MENU' (129, preload)
+{
+ /* menu ID */
+ 129,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000011011,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "File",
+
+ /* its contents */
+ {
+#if 0
+ /* item #1 */
+ "New", noicon, "N", nomark, plain;
+
+ /* item #2 */
+ "Open", noicon, "O", nomark, plain;
+
+ /* item #3 */
+ "Import", noicon, "I", nomark, plain;
+#endif
+ /* item #1 (was #4) */
+ "Close", noicon, "W", nomark, plain;
+
+ /* item #2 (was #5) */
+ "Save", noicon, "S", nomark, plain;
+
+ /* item #3 (was #6) */
+ "-", noicon, nokey, nomark, plain;
+
+ /* item #4 (was #7) */
+ "Score", noicon, "H", nomark, plain;
+
+ /* item #4 (was #7) */
+ "Quit", noicon, "Q", nomark, plain;
+ }
+};
+
+
+resource 'MENU' (130, preload)
+{
+ /* menu ID */
+ 130,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000111101,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Edit",
+
+ /* its contents */
+ {
+ /* item #1 */
+ "Undo", noicon, "Z", nomark, plain;
+
+ /* item #2 */
+ "-", noicon, nokey, nomark, plain;
+
+ /* item #3 */
+ "Cut", noicon, "X", nomark, plain;
+
+ /* item #4 */
+ "Copy", noicon, "C", nomark, plain;
+
+ /* item #5 */
+ "Paste", noicon, "V", nomark, plain;
+
+ /* item #6 */
+ "Clear", noicon, nokey, nomark, plain;
+ }
+};
+
+resource 'MENU' (131, preload)
+{
+ /* menu ID */
+ 131,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000011,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Font",
+
+ /* its contents */
+ {
+ /* item #1 */
+ "Bold", noicon, nokey, nomark, plain;
+
+ /* item #2 */
+ "Wide", noicon, nokey, nomark, plain;
+
+ /* item #3 */
+ "-", noicon, nokey, nomark, plain;
+
+ /* the rest are supplied by the program */
+ }
+};
+
+resource 'MENU' (132, preload)
+{
+ /* menu ID */
+ 132,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000000,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Size",
+
+ /* its contents */
+ {
+ /* Let the program fill it in */
+ }
+};
+
+resource 'MENU' (133, preload)
+{
+ /* menu ID */
+ 133,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000000,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Windows",
+
+ /* its contents */
+ {
+ /* Let the program create them for us */
+ }
+};
+
+resource 'MENU' (134, preload)
+{
+ /* menu ID */
+ 134,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000011011,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Special",
+
+ /* its contents */
+ {
+ /* item #1 */
+ "Sound", noicon, nokey, nomark, plain;
+
+ /* item #2 - 0x90 = 144 */
+ "Graphics", noicon, hierarchicalMenu, "\0x90", plain;
+
+ /* item #3 - 0x91 = 145 */
+ "TileWidth", noicon, hierarchicalMenu, "\0x91", plain;
+
+ /* item #4 - 0x92 = 146 */
+ "TileHeight", noicon, hierarchicalMenu, "\0x92", plain;
+
+ /* item #5 */
+ "-", noicon, nokey, nomark, plain;
+
+ /* item #6 */
+ "Fiddle", noicon, nokey, nomark, plain;
+
+ /* item #7 */
+ "Wizard", noicon, nokey, nomark, plain;
+ }
+};
+
+/* Graphics submenu */
+resource 'MENU' (144, preload)
+{
+ /* menu ID */
+ 144,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000111,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title (ignored) */
+ "Graphics",
+
+ /* menu items */
+ {
+ /* item #1 */
+ "None", noicon, nokey, nomark, plain;
+
+ /* item #2 */
+ "8x8", noicon, nokey, nomark, plain;
+
+ /* item #3 */
+ "16x16", noicon, nokey, nomark, plain;
+
+ /* item #4 */
+ "32x32", noicon, nokey, nomark, plain;
+
+ /* item #5 */
+ "-", noicon, nokey, nomark, plain;
+
+ /* item #6 */
+ "Enlarge tiles", noicon, nokey, nomark, plain;
+ }
+};
+
+/* Tilewidth submenu */
+resource 'MENU' (145, preload)
+{
+ /* menu ID */
+ 145,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000000,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "TileWidth",
+
+ /* its contents */
+ {
+ /* Let the program create them for us */
+ }
+};
+
+/* TileHeight submenu */
+resource 'MENU' (146, preload)
+{
+ /* menu ID */
+ 146,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000000,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "TileHeight",
+
+ /* its contents */
+ {
+ /* Let the program create them for us */
+ }
+};
+
+/* Menu bar definition */
+resource 'MBAR' (128, preload)
+{
+ { 128, 129, 130, 131, 132, 133, 134 }
+};
+
+
+/*
+ * Dialogue item lists
+ */
+resource 'DITL' (129, purgeable)
+{
+ {
+ /** item #1 **/
+
+ /* bounding rect */
+ { 45, 353, 65, 411 },
+
+ /* type */
+ Button
+ {
+ /* enable flag */
+ enabled,
+
+ /* title */
+ "OK"
+ },
+
+ /** item #2 **/
+
+ /* bounding rect */
+ { 19, 68, 90, 339 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "^0"
+ },
+
+ /** item #3 **/
+
+ /* bounding rect */
+ { 38, 21, 70, 53 },
+
+ /* type */
+ Icon
+ {
+ /* enable flag */
+ disabled,
+
+ /* 'ICON' ID */
+ 128
+ }
+ }
+};
+
+
+resource 'DITL' (128, purgeable)
+{
+ {
+ /** item #1 **/
+
+ /* bounding rect */
+ { -4, 0, 225, 308 },
+
+ /* type */
+ UserItem
+ {
+ /* enable flag */
+ enabled
+ },
+
+ /** item #2 **/
+
+ /* bounding rect */
+ { 7, 108, 24, 235 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "T.o.M.E. 2.3.3"
+ },
+
+ /** item #3 **/
+
+ /* bounding rect */
+ { 36, 80, 53, 275 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Copyright (c) 1998-2003"
+ },
+
+ /** item #4 **/
+ { 53, 122, 70, 220 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "DarkGod"
+ },
+
+ /** item #5 **/
+
+ /* bounding rect */
+ { 70, 81, 87, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "(darkgod@ifrance.com)"
+ },
+
+ /** item #6 **/
+
+ /* bounding rect */
+ { 99, 88, 116, 266 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Original Copyright by"
+ },
+
+ /** item #7 **/
+
+ /* bounding rect */
+ { 135, 92, 151, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Robert A. Koeneke"
+ },
+
+ /** item #8 **/
+
+ /* bounding rect */
+ { 119, 103, 135, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "James E. Wilson"
+ },
+
+ /** item #9 **/
+
+ /* bounding rect */
+ { 150, 112, 166, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ "Ben Harrison"
+ },
+
+ /** item #10 */
+
+ /* bounding rect */
+ { 166, 62, 182, 145 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Topi Ylinen"
+ },
+
+ /** item #11 **/
+
+ /* bounding rect */
+ { 166, 148, 182, 294 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Robert Ruehlmann"
+ },
+
+ /** item #12 **/
+
+ /* bounding rect */
+ { 190, 96, 207, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Macintosh Version"
+ }
+ }
+};
+
+resource 'ALRT' (130, purgeable)
+{
+ /* bounding rect */
+ { 144, 154, 283, 384 },
+
+ /* 'DITL' ID */
+ 130,
+
+ /* bold outline, draw alert and beeps */
+ {
+ /* stage 4 */
+ OK, visible, sound1;
+
+ /* stage 3 */
+ OK, visible, sound1;
+
+ /* stage 2 */
+ OK, visible, sound1;
+
+ /* stage 1 */
+ OK, visible, sound1;
+ },
+
+#if ALRT_RezTemplateVersion == 1
+
+ /* centered to parent window */
+ centerParentWindow
+
+#endif /* ALRT_RezTemplateVersion == 1 */
+};
+
+resource 'ALRT' (129, purgeable)
+{
+ /* bounding rect */
+ { 40, 40, 150, 471 },
+
+ /* 'DITL' ID */
+ 129,
+
+ /* bold outline, draw alert and beeps */
+ {
+ /* stage 4 */
+ OK, visible, sound1;
+
+ /* stage 3 */
+ OK, visible, sound1;
+
+ /* stage 2 */
+ OK, visible, sound1;
+
+ /* stage 1 */
+ OK, visible, sound1;
+ },
+
+#if ALRT_RezTemplateVersion == 1
+
+ /* centered to parent window */
+ centerParentWindow
+
+#endif /* ALRT_RezTemplateVersion == 1 */
+};
+
+resource 'ALRT' (128, purgeable)
+{
+ /* bounding rect */
+ { 40, 40, 150, 471 },
+
+ /* 'DITL' ID */
+ 129,
+
+ /* bold outline, draw alert and beeps */
+ {
+ /* stage 4 */
+ OK, visible, sound1;
+
+ /* stage 3 */
+ OK, visible, sound1;
+
+ /* stage 2 */
+ OK, visible, sound1;
+
+ /* stage 1 */
+ OK, visible, sound1;
+ },
+
+#if ALRT_RezTemplateVersion == 1
+
+ /* centered to parent window */
+ centerParentWindow
+
+#endif /* ALRT_RezTemplateVersion == 1 */
+};
+
+resource 'DLOG' (128, purgeable)
+{
+ /* bounding rect */
+ { 112, 202, 341, 512 },
+
+ /* procID */
+ dBoxProc,
+
+ /* visibility */
+ invisible,
+
+ /* has closebox? */
+ noGoAway,
+
+ /* refCon */
+ 0x0,
+
+ /* 'DITL' ID */
+ 128,
+
+ /* title */
+ "",
+
+#if DLOG_RezTemplateVersion == 1
+
+ /* position */
+ centerMainScreen
+
+#endif /* DLOG_RezTemplateVersion == 1 */
+};
+
+
+/*
+ * Additional resources for Carbon
+ */
+resource 'STR#' (128, purgeable)
+{
+ {
+ /* item #1 */
+ "Please select the \"lib\" folder"
+ }
+};
+
+
+/*
+ * Warning Icon (The ! one)
+ */
+data 'ICON' (128, purgeable) {
+ $"0001 8000 0003 C000 0003 C000 0006 6000"
+ $"0006 6000 000C 3000 000C 3000 0018 1800"
+ $"0019 9800 0033 CC00 0033 CC00 0063 C600"
+ $"0063 C600 00C3 C300 00C3 C300 0183 C180"
+ $"0183 C180 0303 C0C0 0303 C0C0 0603 C060"
+ $"0601 8060 0C01 8030 0C00 0030 1800 0018"
+ $"1801 8018 3003 C00C 3003 C00C 6001 8006"
+ $"6000 0006 C000 0003 FFFF FFFF 7FFF FFFE"
+};
+
+
+#ifndef MACH_O
+
+/*
+ * The JRRT icons we all know and love: you are not expected to change these,
+ * unless you are afraid of Tolkien Estate solicitors...
+ */
+
+data 'icl4' (129, purgeable) {
+ $"000F FFFF FFFF FFFF FFFF FFF0 0000 0000"
+ $"000F 0000 0000 0000 0000 0CFF 0000 0000"
+ $"000F 000F FFFF FFFF 0000 0CF0 F000 0000"
+ $"000F 000F 0F0F 0F0F 0000 0CF0 0F00 0000"
+ $"000F 0FFF FFFF FFFF FFFF FCF0 00F0 0000"
+ $"000F 0F0F 0F0F 0F0F 0F0F 0CF0 000F 0000"
+ $"000F 0FFF FFFF FFFF FFFF FCFF FFFF F000"
+ $"000F 000F 0F0F 0F0F 0000 00CC CCCC FC00"
+ $"000F 000F FFFF FFFF 0000 0000 0FF0 FC00"
+ $"000F 0000 000F 0F00 0000 0000 0F00 FC00"
+ $"000F 0000 000F FF00 0FFF FFFF FFF0 FC00"
+ $"000F 0000 000F 0F00 0F0F 0F0F 0F00 FC00"
+ $"000F 0FFF FFFF FFFF FFFF FFFF FFF0 FC00"
+ $"000F 0F0F 0F0F 0F0F 0F00 0000 0F00 FC00"
+ $"000F 0FFF FFFF FFFF F000 FFC0 0000 FC00"
+ $"000F 0F0F 0000 0000 00FF FC0F C000 FC00"
+ $"000F 0FFF 0000 00FC 000F FC0C 0FC0 FC00"
+ $"000F 0F0F 0000 00FF FFFF FFFF FFC0 FC00"
+ $"000F 0FFF FFFF 0FFC CFFF FFFC CFFC FC00"
+ $"000F 0F0F 0F0F 0CC0 FC0F FC0F CCC0 FC00"
+ $"000F 0FFF FFFF 0000 FC0F FC0F C000 FC00"
+ $"000F 0F0F 0F0F 0F00 0FFF FFFC 0000 FC00"
+ $"000F 0FFF FFFF FFF0 00FF FFC0 0000 FC00"
+ $"000F 0F0F 0F0F 0F00 0FCF FCF0 0000 FC00"
+ $"000F 0FFF FFFF FF0F FC0F FC0F FC00 FC00"
+ $"000F 0F0F 0F0F 0F0C C00F FC0C C000 FC00"
+ $"000F 0FFF FFFF FF00 0FCF FC00 0000 FC00"
+ $"000F 0F0F 0F0F 0F0F 0C0F FC00 0000 FC00"
+ $"000F 0FFF FFFF FFFF 000F CFFC 0000 FC00"
+ $"000F 0F0F 0F0F 0F0F 0FFC 0CC0 0000 FC00"
+ $"000F 0000 0000 0000 0CC0 0000 0000 FC00"
+ $"000F FFFF FFFF FFFF FFFF FFFF FFFF FC00"
+};
+
+data 'icl4' (130, purgeable) {
+ $"000F FFFF FFFF FFFF FFFF FFF0 0000 0000"
+ $"000F 0000 0000 0000 0000 0CFF 0000 0000"
+ $"000F 0000 0000 0000 0000 0CF0 F000 0000"
+ $"000F 0000 FFFF FFFF FFFF CCF0 0F00 0000"
+ $"000F 0000 CCCC CCCC CCCC CCF0 00F0 0000"
+ $"000F 0000 0000 0000 0000 0CF0 000F 0000"
+ $"000F 00FF FFFF FFFF FFFF CCFF FFFF F000"
+ $"000F 00CC CCCC CCCC CCCC C0CC CCCC FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 00FF FFFF FFFF FFFF FFFF FFFC FC00"
+ $"000F 00CC CCCC CCCC CCCC CCCC CCCC FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 00FF FFFF FFFF FFFF FFFF FFFC FC00"
+ $"000F 00CC CCCC CCCC CCCC CCCC CCCC FC00"
+ $"000F 0000 0000 0000 0000 FFC0 0000 FC00"
+ $"000F 00FF FFFF C000 00FF FC0F C000 FC00"
+ $"000F 00CC CCCC C0FC 000F FC0C 0FC0 FC00"
+ $"000F 0000 0000 00FF FFFF FFFF FFC0 FC00"
+ $"000F 00FF FFFC 0FFC CFFF FFFC CFFC FC00"
+ $"000F 00CC CCCC 0CC0 FC0F FC0F CCC0 FC00"
+ $"000F 0000 0000 0000 FC0F FC0F C000 FC00"
+ $"000F 0000 FFFF FC00 0FFF FFFC 0000 FC00"
+ $"000F 0000 CCCC CC00 00FF FFC0 0000 FC00"
+ $"000F 0000 0000 0000 0FCF FCF0 0000 FC00"
+ $"000F 00FF FFFF FC0F FC0F FC0F FC00 FC00"
+ $"000F 00CC CCCC CC0C C00F FC0C C000 FC00"
+ $"000F 0000 0000 0000 0FCF FC00 0000 FC00"
+ $"000F 00FF FFFF FFC0 0C0F FC00 0000 FC00"
+ $"000F 00CC CCCC CCC0 000F CFFC 0000 FC00"
+ $"000F 0000 0000 0000 0FFC 0CC0 0000 FC00"
+ $"000F 0000 0000 0000 0CC0 0000 0000 FC00"
+ $"000F FFFF FFFF FFFF FFFF FFFF FFFF FC00"
+};
+
+data 'icl4' (128, purgeable) {
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"F333 3333 3FFF 000F ED00 FFF3 3333 333F"
+ $"F333 333F F000 00FE D0ED 00FF F333 333F"
+ $"F333 33FF 0000 00DD 0FED 0FDF FF33 333F"
+ $"F333 3F00 0000 00FF 0ED0 00FD 00F3 333F"
+ $"F333 F000 0000 00CF FED0 00D0 000F 333F"
+ $"F33F 000F 0000 000F FED0 0000 0E00 F33F"
+ $"F3FF 00FF FFFF FFFF FFFF FFFF FFE0 FF3F"
+ $"F3F0 0FFF FFFF FFFF FFFF FFFF FFFE 0F3F"
+ $"FF00 FEED DDDD DDDF FEDD DDDD DDFF EDFF"
+ $"FF00 FDD0 000F FFFF FEFF FF00 0000 EDFF"
+ $"FF00 D000 00FF DDDF FEDD DFF0 0000 D0FF"
+ $"F000 0000 0FFD 000F FED0 00FE D000 000F"
+ $"F000 0000 0FFD 000F FED0 00FE D000 000F"
+ $"F000 0000 0FFD 000F FED0 00FE D000 000F"
+ $"F000 0000 00FF D00F FED0 0FFD 0000 000F"
+ $"F000 0000 000F FFFF FEFF FFD0 0000 000F"
+ $"F000 0000 000D DDFF FEFD DD00 0000 000F"
+ $"F000 0000 0000 0FFF FEFF 0000 0000 000F"
+ $"F000 0000 0000 FFDF FEDF F000 0000 000F"
+ $"FF00 0000 000F FD0F FED0 FF00 0000 00FF"
+ $"FF00 000F FFFF D00F FED0 0FFF FFD0 00FF"
+ $"FF00 0000 FFFD 000F FED0 00FF FD00 00FF"
+ $"F3F0 0000 DDD0 000F FED0 00DD D000 0F3F"
+ $"F3FF 0000 0000 F00F FED0 0000 0000 FF3F"
+ $"F33F 0000 000F DFDF FED0 0000 0000 F33F"
+ $"F333 F000 0000 FD0F FFD0 0000 000F 333F"
+ $"F333 3F00 0000 D00F FFF0 FFD0 00F3 333F"
+ $"F333 33FF 00F0 00FF FDFF FD00 FF33 333F"
+ $"F333 333F F00F FFFF D00D D00F F333 333F"
+ $"F333 3333 3FFF FFDD 0000 FFF3 3333 333F"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'icl4' (131, purgeable) {
+ $"000F FFFF FFFF FFFF FFFF FFF0 0000 0000"
+ $"000F 0000 0000 0000 0000 00FF 0000 0000"
+ $"000F 0F00 0F00 FF00 0FF0 00FC F000 0000"
+ $"000F 0FF0 FF0F 00F0 F00F 00FC 0F00 0000"
+ $"000F 0F0F 0F0F 00F0 F000 00FC 00F0 0000"
+ $"000F 0F00 0F0F FFF0 F000 00FC 000F 0000"
+ $"000F 0F00 0F0F 00F0 F00F 00FF FFFF F000"
+ $"000F 0F00 0F0F 00F0 0FF0 000C CCCC FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F FFFF FFFF FFFF FFFF FFFF FFFF FC00"
+ $"000F CCCC CCCC CCCC CCCC CCCC CCCC FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 0000 0000 0000 0000 FFC0 0000 FC00"
+ $"000F 0000 0000 0000 00FF FC0F C000 FC00"
+ $"000F 0000 0000 00FC 000F FC0C 0FC0 FC00"
+ $"000F 0000 0000 00FF FFFF FFFF FFC0 FC00"
+ $"000F 0000 0000 0FFC CFFF FFFC CFFC FC00"
+ $"000F 0000 0000 0CC0 FC0F FC0F CCC0 FC00"
+ $"000F 0000 0000 0000 FC0F FC0F C000 FC00"
+ $"000F 0000 0000 0000 0FFF FFFC 0000 FC00"
+ $"000F 0000 0000 0000 00FF FFC0 0000 FC00"
+ $"000F 0000 0000 0000 0FCF FCF0 0000 FC00"
+ $"000F 0000 0000 000F FC0F FC0F FC00 FC00"
+ $"000F 0000 0000 000C C00F FC0C C000 FC00"
+ $"000F 0000 0000 0000 0FCF FC00 0000 FC00"
+ $"000F 0000 0000 0000 0C0F FC00 0000 FC00"
+ $"000F 0000 0000 0000 000F CFFC 0000 FC00"
+ $"000F 0000 0000 0000 0FFC 0CC0 0000 FC00"
+ $"000F 0000 0000 0000 0CC0 0000 0000 FC00"
+ $"000F FFFF FFFF FFFF FFFF FFFF FFFF FC00"
+};
+
+data 'ICN#' (128, purgeable) {
+ $"FFFF DFFF FFF1 8FFF FF83 23FF FF00 657F"
+ $"FC03 423F F801 C01F F101 C04F F3FF FFEF"
+ $"E7FF FFF7 CE01 C03B C81F FC0B C031 C603"
+ $"8061 C301 8061 C301 8061 C301 8031 C601"
+ $"801F FC01 8003 E001 8007 F001 800D D801"
+ $"C019 CC03 C1F1 C7C3 C0E1 C383 E001 C007"
+ $"F009 C00F F015 C00F F809 C01F FC01 EC3F"
+ $"FF23 B8FF FF9F 01FF FFFC 0FFF FFF3 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'ICN#' (129, purgeable) {
+ $"1FFF FE00 1000 0300 11FF 0280 1155 0240"
+ $"17FF FA20 1555 5210 17FF FBF8 1155 0008"
+ $"11FF 0068 1014 0048 101C 7FE8 1014 5548"
+ $"17FF FFE8 1555 4008 17FF 8C08 1500 3908"
+ $"1702 1848 1503 FFC8 17F6 7E68 1550 9908"
+ $"17F0 9908 1554 7E08 17FE 3C08 1554 5A08"
+ $"17FD 9988 1554 1808 17FC 5808 1555 1808"
+ $"17FF 1608 1555 6008 1000 0008 1FFF FFF8"
+ $"1FFF FE00 1FFF FF00 1FFF FF80 1FFF FFC0"
+ $"1FFF FFE0 1FFF FFF0 1FFF FFF8 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+};
+
+data 'ICN#' (130, purgeable) {
+ $"1FFF FE00 1000 0300 1000 0280 10FF F240"
+ $"1000 0220 1000 0210 13FF F3F8 1000 0008"
+ $"1000 0008 13FF FFE8 1000 0008 1000 0008"
+ $"13FF FFE8 1000 0008 1000 0C08 13F0 3908"
+ $"1002 1848 1003 FFC8 13E6 7E68 1000 9908"
+ $"1000 9908 10F8 7E08 1000 3C08 1000 5A48"
+ $"13F9 9988 1000 1808 1000 5808 13FC 1808"
+ $"1000 1608 1000 6008 1000 0008 1FFF FFF8"
+ $"1FFF FE00 1FFF FF00 1FFF FF80 1FFF FFC0"
+ $"1FFF FFE0 1FFF FFF0 1FFF FFF8 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+};
+
+data 'ICN#' (131, purgeable) {
+ $"1FFF FE00 1000 0300 144C 6280 16D2 9240"
+ $"1552 8220 145E 8210 1452 93F8 1452 6008"
+ $"1000 0008 1FFF FFF8 1000 0008 1000 0008"
+ $"1000 0008 1000 0008 1000 0C08 1000 3908"
+ $"1002 1848 1003 FFC8 1006 7E68 1000 9908"
+ $"1000 9908 1000 7E08 1000 3C08 1000 5A48"
+ $"1001 9988 1000 1808 1000 5808 1000 1808"
+ $"1000 1608 1000 6008 1000 0008 1FFF FFF8"
+ $"1FFF FE00 1FFF FF00 1FFF FF80 1FFF FFC0"
+ $"1FFF FFE0 1FFF FFF0 1FFF FFF8 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+};
+
+data 'ics#' (128, purgeable) {
+ $"FFFF F99F F18F FFFF E7E3 8991 8991 87E1"
+ $"83C1 85A1 9999 C183 E587 F18F F97F FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'ics#' (129, purgeable) {
+ $"7FF0 4018 5FD4 555E 5FC2 4412 5FFA 5502"
+ $"5F12 557E 5F3A 5556 5F3A 551A 4022 7FFE"
+ $"7FF0 7FF8 7FFC 7FFE 7FFE 7FFE 7FFE 7FFE"
+ $"7FFE 7FFE 7FFE 7FFE 7FFE 7FFE 7FFE 7FFE"
+};
+
+data 'ics#' (130, purgeable) {
+ $"7FF0 4018 5FD4 401E 5FC2 4002 5FFA 4002"
+ $"5F12 407E 5F3A 4056 5F3A 401A 4022 7FFE"
+ $"7FF0 7FF8 7FFC 7FFE 7FFF 7FFF 7FFF 7FFF"
+ $"7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF"
+};
+
+data 'ics#' (131, purgeable) {
+ $"7FF0 4018 5FD4 5F9E 57C2 4002 7FFE 4002"
+ $"4012 407E 403A 4056 403A 401A 4022 7FFE"
+ $"7FF0 7FF8 7FFC 7FFE 7FFF 7FFF 7FFF 7FFF"
+ $"7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF"
+};
+
+data 'icl8' (129, purgeable) {
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F7 FFFF 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5FF FFFF FFFF FFFF FFFF"
+ $"F5F5 F5F5 F5F7 FFF5 FF00 0000 0000 0000"
+ $"0000 00FF F5F5 F5FF F5FF F5FF F5FF F5FF"
+ $"F5F5 F5F5 F5F7 FFF5 F5FF 0000 0000 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFF7 FFF5 F5F5 FF00 0000 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5FF"
+ $"F5FF F5FF F5F7 FFF5 F5F5 F5FF 0000 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFF7 FFFF FFFF FFFF FF00 0000"
+ $"0000 00FF F5F5 F5FF F5FF F5FF F5FF F5FF"
+ $"F5F5 F5F5 F5F5 F7F7 F7F7 F7F7 FFF7 0000"
+ $"0000 00FF F5F5 F5FF FFFF FFFF FFFF FFFF"
+ $"F5F5 F5F5 F5F5 F5F5 F5FF FFF5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5FF F5FF F5F5"
+ $"F5F5 F5F5 F5F5 F5F5 F5FF F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5FF FFFF F5F5"
+ $"F5FF FFFF FFFF FFFF FFFF FFF5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5FF F5FF F5F5"
+ $"F5FF F5FF F5FF F5FF F5FF F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFF5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5FF"
+ $"F5FF F5F5 F5F5 F5F5 F5FF F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFF5 F5F5 FFFF F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 FFFF FFF8 F7FF F8F6 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF F5F5 F5F5 F5F5 FFF8"
+ $"F6F5 F5FF FFF8 F6F8 F6FF F8F6 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5F5 F5F5 F5F5 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF F8F6 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF F5FF FFF8"
+ $"F8FF FFFF FFFF FFF8 F6FF FFF8 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5F8 F8F6"
+ $"FFF8 F6FF FFF8 F6FF F8F6 F6F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF F5F6 F6F5"
+ $"FFF8 F6FF FFF8 F6FF F8F6 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5F5"
+ $"F5FF FFFF FFFF FFF8 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFF5"
+ $"F5F5 FFFF FFFF F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5F5"
+ $"F5FF F8FF FFF8 FFF5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF F5FF"
+ $"FFF8 F6FF FFF8 F6FF FFF8 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5F8"
+ $"F8F6 F5FF FFF8 F6F8 F8F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF F5F6"
+ $"F6FF F8FF FFF8 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5FF"
+ $"F5F8 F6FF FFF8 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"F5F6 F5FF F8FF FFF8 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5FF"
+ $"F5FF FFF8 F7F8 F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F8 F8F7 F6F6 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFF7 0000"
+};
+
+data 'icl8' (128, purgeable) {
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF F9FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFD7 D7D7 D7D7 D7D7 D7FF FFFF 0000 00FF"
+ $"FCF9 F7F7 FFFF FFD7 D7D7 D7D7 D7D7 6BFF"
+ $"FFD7 4747 4747 4747 FF00 0000 F5F5 FFFC"
+ $"F9F7 FCF8 F7F5 FFFF FF47 4747 4747 6BFF"
+ $"FFD7 4747 4747 FFFF 00F5 F5F5 F5F5 F9F9"
+ $"F7FF FCF8 F7FF F7FF F9FF 4747 4747 6BFF"
+ $"FFD7 4747 47FF 0000 F5F5 F5F5 F5F5 FFFF"
+ $"F7FC F9F7 F5F5 FFF9 F7F5 FF47 4747 6BFF"
+ $"FFD7 4747 FF00 F5F5 F5F5 F5F5 F5F5 F7FF"
+ $"FFFC F9F7 F5F5 F9F7 F5F5 F5FF 4747 6BFF"
+ $"FFD7 47FF 00F5 F5FF F5F5 F5F5 F5F5 F5FF"
+ $"FFFC F9F7 F5F5 F5F5 F5FC F5F5 FF47 6BFF"
+ $"FFD7 47FF 00F5 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FCF5 FF47 6BFF"
+ $"FFD7 FF00 F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFC F5FF 6BFF"
+ $"FFFF 00F7 FFFC FCF9 F9F9 F9F9 F9F9 F9FF"
+ $"FFFC F9F9 F9F9 F9F9 F9F9 FFFF FCF9 FFFF"
+ $"FFFF 00F7 FCF9 F9F7 F7F7 F7FF FFFF FFFF"
+ $"FFFC FFFF FFFF F7F7 F7F7 F7F7 FCF9 FFFF"
+ $"FFFF 00F7 F9F7 F7F5 F5F5 FFFF F8F8 F8FF"
+ $"FFFC F9F9 F9FF FFF7 F7F5 F5F7 F9F7 FFFF"
+ $"FFF5 F5F5 F7F5 F5F5 F5FF FFF9 F7F7 F7FF"
+ $"FFFC F9F7 F7F7 FFFC F8F7 F5F5 F7F7 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5FF FFF9 F7F5 F5FF"
+ $"FFFC F9F7 F5F5 FFFC F8F7 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5FF FFF9 F7F5 F5FF"
+ $"FFFC F9F7 F5F5 FFFC F8F7 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 FFFF F9F7 F5FF"
+ $"FFFC F9F7 F5FF FFF8 F7F5 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 F5FF FFFF FFFF"
+ $"FFFC FFFF FFFF F8F7 F5F5 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 F5F9 F9F9 FFFF"
+ $"FFFC FFF9 F9F9 F7F5 F5F5 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 F5F5 F5FF FFFF"
+ $"FFFC FFFF F7F7 F5F5 F5F5 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 F5F5 FFFF F9FF"
+ $"FFFC F9FF FFF5 F5F5 F5F5 F5F5 F5F5 F8FF"
+ $"FFFF F5F5 F5F5 F5F5 F5F5 F5FF FFF9 F7FF"
+ $"FFFC F9F7 FFFF F5F5 F5F5 F5F5 F5F8 FFFF"
+ $"FFFF F5F5 F5F5 F7FF FFFF FFFF F9F7 F5FF"
+ $"FFFC F9F7 F7FF FFFF FFFF F9F5 F5F8 FFFF"
+ $"FFFF F5F5 F5F5 F5F7 FFFF FFF9 F7F5 F5FF"
+ $"FFFC F9F7 F5F7 FFFF FFF9 F7F5 F5F8 FFFF"
+ $"FFD7 FFF5 F5F5 F5F7 F9F9 F9F7 F5F5 F5FF"
+ $"FFFC F9F7 F5F5 F9F9 F9F7 F5F5 F8FF 6BFF"
+ $"FFD7 47FF F5F5 F5F5 F7F7 F7F5 FFF6 F5FF"
+ $"FFFC F9F7 F5F5 F5F7 F7F5 F5F8 FF47 6BFF"
+ $"FFD7 47FF F5F5 F5F5 F5F5 F5FF F7FF F8FF"
+ $"FFFC F9F7 F5F5 F5F5 F5F5 F5F8 FF47 6BFF"
+ $"FFD7 4747 FFF5 F5F5 F5F5 F5F5 FFF8 F7FF"
+ $"FFFF F9F7 F5F5 F5F5 F5F5 F8FF 4747 6BFF"
+ $"FFD7 4747 47FF F5F5 F5F5 F5F5 F8F7 F5FF"
+ $"FFFF FFF7 FFFF F9F7 F8F8 FF47 4747 6BFF"
+ $"FFD7 4747 4747 FFFF F5F5 FFF5 F7F5 FFFF"
+ $"FFF8 FFFF FFF9 F7F8 FFFF 4747 4747 6BFF"
+ $"FFD7 4747 4747 4747 FFF5 F5FF FFFF FFFF"
+ $"F8F6 F5F9 F9F7 F5FF 4747 4747 4747 6BFF"
+ $"FFD7 6B6B 6B6B 6B6B 6BFF FFFF FFFF F8F8"
+ $"F8F8 F8F5 FFFF FF6B 6B6B 6B6B 6B6B 6BFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF F8F8 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'icl8' (130, purgeable) {
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F8 FFFF 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F8 FFF6 FF00 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF F8F8 FFF6 F6FF 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F8 FFF6 F6F6 FF00 0000 0000"
+ $"0000 00FF F5F5 F5F5 F6F6 F6F6 F6F6 F6F6"
+ $"F6F6 F6F6 F6F8 FFF6 F6F6 F6FF 0000 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF F8F8 FFFF FFFF FFFF FF00 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F5 F8F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F6"
+ $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFF8 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F8 F8F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F6"
+ $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFF8 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F8 F8F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F6"
+ $"F6F6 F6F6 FFFF F8F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF F8F5 F5F5"
+ $"F5F5 FFFF FFF8 F6FF F8F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F5 FFF8"
+ $"F5F5 F5FF FFF8 F5F8 F5FF F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F5 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF F8F6 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFF8 F6FF FFF8"
+ $"F8FF FFFF FFFF FFF8 F8FF FFF8 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F6F8 F8F5"
+ $"FFF8 F6FF FFF8 F6FF F8F8 F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F5 F5F5"
+ $"FFF8 F6FF FFF8 F6FF F8F6 F6F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 FFFF FFFF FFF8 F6F5"
+ $"F5FF FFFF FFFF FFF8 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F8F8 F8F8 F8F8 F6F5"
+ $"F5F5 FFFF FFFF F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F6F6 F6F6 F6F6 F6F5"
+ $"F5FF F8FF FFF8 FFF6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFF8 F6FF"
+ $"FFF8 F6FF FFF8 F6FF FFF8 F6F5 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F6F8"
+ $"F8F6 F5FF FFF8 F6F8 F8F6 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F5"
+ $"F6FF F8FF FFF8 F6F6 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFFF F8F6"
+ $"F5F8 F6FF FFF8 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F8F6"
+ $"F5F6 F5FF F8FF FFF8 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F6"
+ $"F5FF FFF8 F6F8 F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F8 F8F6 F5F5 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFF7 0000"
+};
+
+data 'icl8' (131, purgeable) {
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F5 FFFF 0000 0000 0000 0000"
+ $"0000 00FF F5FF F5F5 F5FF F5F5 FFFF F5F5"
+ $"F5FF FFF5 F5F5 FFF8 FF00 0000 0000 0000"
+ $"0000 00FF F5FF FFF5 FFFF F5FF 00F5 FFF5"
+ $"FF00 00FF F5F5 FFF8 F6FF 0000 0000 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5F5 FFF5"
+ $"FF00 0000 F5F5 FFF8 F6F6 FF00 0000 0000"
+ $"0000 00FF F5FF F5F5 F5FF F5FF FFFF FFF5"
+ $"FF00 F5F5 F5F5 FFF8 F6F6 F6FF 0000 0000"
+ $"0000 00FF F5FF F5F5 F5FF F5FF F5F5 FFF5"
+ $"FF00 F5FF 00F5 FFFF FFFF FFFF FF00 0000"
+ $"0000 00FF F5FF F5F5 F5FF F5FF F5F5 FFF5"
+ $"00FF FF00 0000 F5F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F500 00F5 F5F5 F5F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFF7 0000"
+ $"0000 00FF F8F8 F8F8 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F8 F8F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F5 F5F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F5 F5F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F5 F5F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F500 FFFF F8F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 FFFF FFF8 F6FF F8F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 FFF8"
+ $"F5F5 F5FF FFF8 F5F8 F5FF F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5FF FFF8"
+ $"F8FF FFFF FFFF FFF8 F8FF FFF8 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F8 F8F5"
+ $"FFF8 F6FF FFF8 F6FF F8F8 F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"FFF8 F6FF FFF8 F6FF F8F6 F6F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5FF FFFF FFFF FFF8 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 FFFF FFFF F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5FF F8FF FFF8 FFF6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5FF"
+ $"FFF8 F6FF FFF8 F6FF FFF8 F6F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F8"
+ $"F8F6 F5FF FFF8 F6F8 F8F6 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F6FF F8FF FFF8 F6F6 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F8 F6FF FFF8 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F6 F5FF F8FF FFF8 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5FF FFF8 F6F8 F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F8 F8F6 F5F5 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFF7 0000"
+};
+
+data 'ics8' (128, purgeable) {
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FF47 4747 FFFF F6FF FFF8 FFFF 4747 47FF"
+ $"FF47 47FF F6F6 F6FF FFF8 F6F6 FF47 47FF"
+ $"FF47 FFFF FFFF FFFF FFFF FFFF FFFF 47FF"
+ $"FF47 FFF8 F8FF FFFF FFFF FFF8 F8F8 47FF"
+ $"FFFF F8F6 FFF8 F8FF FFF8 F8FF F8F6 FFFF"
+ $"FFF6 F6F6 FFF8 F6FF FFF8 F6FF F8F6 F6FF"
+ $"FFF6 F6F6 F6FF FFFF FFFF FFF8 F6F6 F6FF"
+ $"FFF6 F6F6 F6F6 FFFF FFFF F8F6 F6F6 F6FF"
+ $"FFF6 F6F6 F6FF F8FF FFF8 FFF6 F6F6 F6FF"
+ $"FFFF F6FF FFF8 F6FF FFF8 F6FF FFF8 FFFF"
+ $"FFFF F6F8 F8F6 F6FF FFF8 F6F8 F8F8 47FF"
+ $"FF47 FFF6 F6FF F8FF FFF8 F6F6 F6FF 47FF"
+ $"FF47 47FF F6F8 F6FF FFF8 F6F6 FF47 47FF"
+ $"FF47 4747 FFFF F6FF F8FF FFFF 4747 47FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'ics8' (129, purgeable) {
+ $"00FF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF FF00 0000"
+ $"00FF F5FF FFFF FFFF FFFF F5FF F5FF 0000"
+ $"00FF F5FF F5FF F5FF F5FF F5FF FFFF FF00"
+ $"00FF F5FF FFFF FFFF FFFF F5F5 F5F5 FF00"
+ $"00FF F5F5 F5FF F5F5 F5F5 F5FF F5F5 FF00"
+ $"00FF F5FF FFFF FFFF FFFF FFFF FFF5 FF00"
+ $"00FF F5FF F5FF F5FF F5F5 F5F5 F5F5 FF00"
+ $"00FF F5FF FFFF FFFF F5F5 F5FF F5F5 FF00"
+ $"00FF F5FF F5FF F5FF F5FF FFFF FFFF FF00"
+ $"00FF F5FF FFFF FFFF F5F5 FFFF FF00 FF00"
+ $"00FF F5FF F5FF F5FF F5FF 00FF 00FF FF00"
+ $"00FF F5FF FFFF FFFF F5F5 FFFF FFF5 FF00"
+ $"00FF F5FF F5FF F5FF F5F5 F5FF FFF5 FF00"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFF5 F5F5 FF00"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+};
+
+data 'ics8' (130, purgeable) {
+ $"00FF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF FF00 0000"
+ $"00FF F5FF FFFF FFFF FFFF F7FF F5FF 0000"
+ $"00FF F5F7 F7F7 F7F7 F7F7 F7FF FFFF FF00"
+ $"00FF F5FF FFFF FFFF FFFF F7F5 F5F5 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7F7 F7F5 F5F5 FFF7"
+ $"00FF F5FF FFFF FFFF FFFF FFFF FFF7 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7F7 F7F7 F7F7 FFF7"
+ $"00FF F5FF FFFF FFFF F7F5 F5FF F5F5 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7FF FFFF FFFF FFF7"
+ $"00FF F5FF FFFF FFFF F7F5 FFFF FFF5 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7FF F5FF F5FF FFF7"
+ $"00FF F5FF FFFF FFFF F7F5 FFFF FFF5 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7F5 F5FF FFF5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFF5 F5F5 FFF7"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FFF7"
+};
+
+data 'ics8' (131, purgeable) {
+ $"00FF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF FF00 0000"
+ $"00FF F5FF FFFF FFFF FFFF F5FF F5FF 0000"
+ $"00FF F5FF FFFF FFFF FF00 F5FF FFFF FF00"
+ $"00FF F5FF 00FF FFFF FFFF F5F5 F7F7 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5 FFF7"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FFF7"
+ $"00FF F7F7 F7F7 F7F7 F7F7 F7F7 F7F7 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF F5F5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5FF FFFF FFFF FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFFF FFF5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5FF F5FF F5FF FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFFF FFF5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF FFF5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFF5 F5F5 FFF7"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FFF7"
+};
+
+data 'ics4' (128, purgeable) {
+ $"FFFF FFFF FFFF FFFF F333 FFCF FCFF 333F"
+ $"F33F CCCF FCCC F33F F3FF FFFF FFFF FF3F"
+ $"FFFC CFFF FFFC CCFF FFCC FCCF FCCF CCFF"
+ $"FCCC FCCF FCCF CCCF FCCC CFFF FFFC CCCF"
+ $"FCCC CCFF FFCC CCCF FCCC CFCF FCFC CCCF"
+ $"FFCF FCCF FCCF FCFF FFCC CCCF FCCC CCFF"
+ $"F3FC CFCF FCCC CF3F F33F CCCF FCCC F33F"
+ $"F333 FFCF CFFF 333F FFFF FFFF FFFF FFFF"
+};
+
+data 'ics4' (129, purgeable) {
+ $"0FFF FFFF FFFF 0000 0F00 0000 000F F000"
+ $"0F0F FFFF FF0F 0F00 0F0F 0F0F 0F0F FFF0"
+ $"0F0F FFFF FF00 00F0 0F00 0F00 000F 00F0"
+ $"0F0F FFFF FFFF F0F0 0F0F 0F0F 0000 00F0"
+ $"0F0F FFFF 000F 00F0 0F0F 0F0F 0FFF FFF0"
+ $"0F0F FFFF 00FF F0F0 0F0F 0F0F 0F0F 0FF0"
+ $"0F0F FFFF 00FF F0F0 0F0F 0F0F 000F F0F0"
+ $"0F00 0000 00F0 00F0 0FFF FFFF FFFF FFF0"
+};
+
+data 'ics4' (130, purgeable) {
+ $"0FFF FFFF FFFF 0000 0FCC CCCC CCCF F000"
+ $"0FCF FFFF FFCF CF00 0FCC CCCC CCCF FFF0"
+ $"0FCF FFFF FFCC CCFD 0FCC CCCC CCCC CCFD"
+ $"0FCF FFFF FFFF FCFD 0FCC CCCC CCCC CCFD"
+ $"0FCF FFFF CCCF CCFD 0FCC CCCC CFFF FFFD"
+ $"0FCF FFFF CCFF FCFD 0FCC CCCC CFCF CFFD"
+ $"0FCF FFFF CCFF FCFD 0FCC CCCC CCCF FCFD"
+ $"0FCC CCCC CCFC CCFD 0FFF FFFF FFFF FFFD"
+};
+
+data 'ics4' (131, purgeable) {
+ $"0FFF FFFF FFFF 0000 0FCC CCCC CCCF F000"
+ $"0FCF FFFF FFCF CF00 0FCF FFFF FCCF FFF0"
+ $"0FCF CFFF FFCC DDFD 0FCC CCCC CCCC CCFD"
+ $"0FFF FFFF FFFF FFFD 0FCC CCCC CCCC CCFD"
+ $"0FCC CCCC CCCF CCFD 0FCC CCCC CFFF FFFD"
+ $"0FCC CCCC CCFF FCFD 0FCC CCCC CFCF CFFD"
+ $"0FCC CCCC CCFF FCFD 0FCC CCCC CCCF FCFD"
+ $"0FCC CCCC CCFC CCFD 0FFF FFFF FFFF FFFD"
+};
+
+#endif /* !MACH_O */
+
diff --git a/src/carbon/Data.icns b/src/carbon/Data.icns
new file mode 100644
index 00000000..dbaec173
--- /dev/null
+++ b/src/carbon/Data.icns
Binary files differ
diff --git a/src/carbon/Edit.icns b/src/carbon/Edit.icns
new file mode 100644
index 00000000..5e55c272
--- /dev/null
+++ b/src/carbon/Edit.icns
Binary files differ
diff --git a/src/carbon/Image-DS_Store b/src/carbon/Image-DS_Store
new file mode 100644
index 00000000..efd42e72
--- /dev/null
+++ b/src/carbon/Image-DS_Store
Binary files differ
diff --git a/src/carbon/Info.plist b/src/carbon/Info.plist
new file mode 100644
index 00000000..fff3fb15
--- /dev/null
+++ b/src/carbon/Info.plist
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plist version="1.0">
+ <dict>
+ <key>CFBundleName</key><string>T.o.M.E.</string>
+ <key>CFBundleDisplayName</key><string>T.o.M.E. (OS X)</string>
+ <key>CFBundleExecutable</key><string>tome</string>
+ <key>CFBundlePackageType</key><string>APPL</string>
+ <key>CFBundleSignature</key><string>PrnA</string>
+ <key>CFBundleVersion</key><string>2.3.4</string>
+ <key>CFBundleShortVersionString</key><string>234</string>
+ <key>CFBundleIconFile</key><string>Angband</string>
+ <key>CFBundleIdentifier</key><string>net.t-o-m-e.tome</string>
+ <key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ <key>CFBundleTypeIconFile</key><string>Save</string>
+ <key>CFBundleTypeName</key><string>Angband game data</string>
+ <key>CFBundleTypeOSTypes</key><array><string>SAVE</string></array>
+ <key>CFBundleTypeRole</key><string>Editor</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ <key>CFBundleTypeIconFile</key><string>Edit</string>
+ <key>CFBundleTypeName</key><string>Angband game data</string>
+ <key>CFBundleTypeOSTypes</key><array><string>TEXT</string></array>
+ <key>CFBundleTypeRole</key><string>Editor</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ <key>CFBundleTypeIconFile</key><string>Data</string>
+ <key>CFBundleTypeName</key><string>Angband game data</string>
+ <key>CFBundleTypeOSTypes</key><array><string>DATA</string></array>
+ <key>CFBundleTypeRole</key><string>Editor</string>
+ </dict>
+ </array>
+ </dict>
+</plist>
diff --git a/src/carbon/Save.icns b/src/carbon/Save.icns
new file mode 100644
index 00000000..362f80f6
--- /dev/null
+++ b/src/carbon/Save.icns
Binary files differ
diff --git a/src/carbon/getversion b/src/carbon/getversion
new file mode 100755
index 00000000..786fd14b
--- /dev/null
+++ b/src/carbon/getversion
@@ -0,0 +1,2 @@
+#! /bin/sh
+grep CFBundleVersion carbon/Info.plist | perl -pe "s,.*<string>([^<]*)<.*,\\1,"
diff --git a/src/cave.c b/src/cave.c
new file mode 100644
index 00000000..d23fc44c
--- /dev/null
+++ b/src/cave.c
@@ -0,0 +1,5055 @@
+/* File: cave.c */
+
+/* Purpose: low level dungeon routines -BEN- */
+
+
+#include "angband.h"
+
+
+/*
+ * Support for Adam Bolt's tileset, lighting and transparency effects
+ * by Robert Ruehlmann (rr9@angband.org)
+ */
+
+
+/*
+ * Approximate Distance between two points.
+ *
+ * When either the X or Y component dwarfs the other component,
+ * this function is almost perfect, and otherwise, it tends to
+ * over-estimate about one grid per fifteen grids of distance.
+ *
+ * Algorithm: hypot(dy,dx) = max(dy,dx) + min(dy,dx) / 2
+ */
+int distance(int y1, int x1, int y2, int x2)
+{
+ int dy, dx, d;
+
+
+ /* Find the absolute y/x distance components */
+ dy = (y1 > y2) ? (y1 - y2) : (y2 - y1);
+ dx = (x1 > x2) ? (x1 - x2) : (x2 - x1);
+
+ /* Hack -- approximate the distance */
+ d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
+
+ /* Return the distance */
+ return (d);
+}
+
+
+/*
+ * Returns TRUE if a grid is considered to be a wall for the purpose
+ * of magic mapping / clairvoyance
+ */
+static bool_ is_wall(cave_type *c_ptr)
+{
+ byte feat;
+
+
+ /* Handle feature mimics */
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else feat = c_ptr->feat;
+
+ /* Paranoia */
+ if (feat >= max_f_idx) return FALSE;
+
+ /* Vanilla floors and doors aren't considered to be walls */
+ if (feat < FEAT_SECRET) return FALSE;
+
+ /* Exception #1: a glass wall is a wall but doesn't prevent LOS */
+ if (feat == FEAT_GLASS_WALL) return FALSE;
+
+ /* Exception #2: an illusion wall is not a wall but obstructs view */
+ if (feat == FEAT_ILLUS_WALL) return TRUE;
+
+ /* Exception #3: a small tree is a floor but obstructs view */
+ if (feat == FEAT_SMALL_TREES) return TRUE;
+
+ /* Normal cases: use the WALL flag in f_info.txt */
+ return (f_info[feat].flags1 & FF1_WALL) ? TRUE : FALSE;
+}
+
+
+/*
+ * A simple, fast, integer-based line-of-sight algorithm. By Joseph Hall,
+ * 4116 Brewster Drive, Raleigh NC 27606. Email to jnh@ecemwl.ncsu.edu.
+ *
+ * Returns TRUE if a line of sight can be traced from (x1,y1) to (x2,y2).
+ *
+ * The LOS begins at the center of the tile (x1,y1) and ends at the center of
+ * the tile (x2,y2). If los() is to return TRUE, all of the tiles this line
+ * passes through must be floor tiles, except for (x1,y1) and (x2,y2).
+ *
+ * We assume that the "mathematical corner" of a non-floor tile does not
+ * block line of sight.
+ *
+ * Because this function uses (short) ints for all calculations, overflow may
+ * occur if dx and dy exceed 90.
+ *
+ * Once all the degenerate cases are eliminated, the values "qx", "qy", and
+ * "m" are multiplied by a scale factor "f1 = abs(dx * dy * 2)", so that
+ * we can use integer arithmetic.
+ *
+ * We travel from start to finish along the longer axis, starting at the border
+ * between the first and second tiles, where the y offset = .5 * slope, taking
+ * into account the scale factor. See below.
+ *
+ * Also note that this function and the "move towards target" code do NOT
+ * share the same properties. Thus, you can see someone, target them, and
+ * then fire a bolt at them, but the bolt may hit a wall, not them. However,
+ * by clever choice of target locations, you can sometimes throw a "curve".
+ *
+ * Note that "line of sight" is not "reflexive" in all cases.
+ *
+ * Use the "projectable()" routine to test "spell/missile line of sight".
+ *
+ * Use the "update_view()" function to determine player line-of-sight.
+ */
+bool_ los(int y1, int x1, int y2, int x2)
+{
+ /* Delta */
+ int dx, dy;
+
+ /* Absolute */
+ int ax, ay;
+
+ /* Signs */
+ int sx, sy;
+
+ /* Fractions */
+ int qx, qy;
+
+ /* Scanners */
+ int tx, ty;
+
+ /* Scale factors */
+ int f1, f2;
+
+ /* Slope, or 1/Slope, of LOS */
+ int m;
+
+
+ /* Extract the offset */
+ dy = y2 - y1;
+ dx = x2 - x1;
+
+ /* Extract the absolute offset */
+ ay = ABS(dy);
+ ax = ABS(dx);
+
+
+ /* Handle adjacent (or identical) grids */
+ if ((ax < 2) && (ay < 2)) return (TRUE);
+
+
+ /* Paranoia -- require "safe" origin */
+ /* if (!in_bounds(y1, x1)) return (FALSE); */
+
+
+ /* Directly South/North */
+ if (!dx)
+ {
+ /* South -- check for walls */
+ if (dy > 0)
+ {
+ for (ty = y1 + 1; ty < y2; ty++)
+ {
+ if (!cave_sight_bold(ty, x1)) return (FALSE);
+ }
+ }
+
+ /* North -- check for walls */
+ else
+ {
+ for (ty = y1 - 1; ty > y2; ty--)
+ {
+ if (!cave_sight_bold(ty, x1)) return (FALSE);
+ }
+ }
+
+ /* Assume los */
+ return (TRUE);
+ }
+
+ /* Directly East/West */
+ if (!dy)
+ {
+ /* East -- check for walls */
+ if (dx > 0)
+ {
+ for (tx = x1 + 1; tx < x2; tx++)
+ {
+ if (!cave_sight_bold(y1, tx)) return (FALSE);
+ }
+ }
+
+ /* West -- check for walls */
+ else
+ {
+ for (tx = x1 - 1; tx > x2; tx--)
+ {
+ if (!cave_sight_bold(y1, tx)) return (FALSE);
+ }
+ }
+
+ /* Assume los */
+ return (TRUE);
+ }
+
+
+ /* Extract some signs */
+ sx = (dx < 0) ? -1 : 1;
+ sy = (dy < 0) ? -1 : 1;
+
+
+ /* Vertical "knights" */
+ if (ax == 1)
+ {
+ if (ay == 2)
+ {
+ if (cave_sight_bold(y1 + sy, x1)) return (TRUE);
+ }
+ }
+
+ /* Horizontal "knights" */
+ else if (ay == 1)
+ {
+ if (ax == 2)
+ {
+ if (cave_sight_bold(y1, x1 + sx)) return (TRUE);
+ }
+ }
+
+
+ /* Calculate scale factor div 2 */
+ f2 = (ax * ay);
+
+ /* Calculate scale factor */
+ f1 = f2 << 1;
+
+
+ /* Travel horizontally */
+ if (ax >= ay)
+ {
+ /* Let m = dy / dx * 2 * (dy * dx) = 2 * dy * dy */
+ qy = ay * ay;
+ m = qy << 1;
+
+ tx = x1 + sx;
+
+ /* Consider the special case where slope == 1. */
+ if (qy == f2)
+ {
+ ty = y1 + sy;
+ qy -= f1;
+ }
+ else
+ {
+ ty = y1;
+ }
+
+ /* Note (below) the case (qy == f2), where */
+ /* the LOS exactly meets the corner of a tile. */
+ while (x2 - tx)
+ {
+ if (!cave_sight_bold(ty, tx)) return (FALSE);
+
+ qy += m;
+
+ if (qy < f2)
+ {
+ tx += sx;
+ }
+ else if (qy > f2)
+ {
+ ty += sy;
+ if (!cave_sight_bold(ty, tx)) return (FALSE);
+ qy -= f1;
+ tx += sx;
+ }
+ else
+ {
+ ty += sy;
+ qy -= f1;
+ tx += sx;
+ }
+ }
+ }
+
+ /* Travel vertically */
+ else
+ {
+ /* Let m = dx / dy * 2 * (dx * dy) = 2 * dx * dx */
+ qx = ax * ax;
+ m = qx << 1;
+
+ ty = y1 + sy;
+
+ if (qx == f2)
+ {
+ tx = x1 + sx;
+ qx -= f1;
+ }
+ else
+ {
+ tx = x1;
+ }
+
+ /* Note (below) the case (qx == f2), where */
+ /* the LOS exactly meets the corner of a tile. */
+ while (y2 - ty)
+ {
+ if (!cave_sight_bold(ty, tx)) return (FALSE);
+
+ qx += m;
+
+ if (qx < f2)
+ {
+ ty += sy;
+ }
+ else if (qx > f2)
+ {
+ tx += sx;
+ if (!cave_sight_bold(ty, tx)) return (FALSE);
+ qx -= f1;
+ ty += sy;
+ }
+ else
+ {
+ tx += sx;
+ qx -= f1;
+ ty += sy;
+ }
+ }
+ }
+
+ /* Assume los */
+ return (TRUE);
+}
+
+
+
+/*
+ * Returns true if the player's grid is dark
+ */
+bool_ no_lite(void)
+{
+ return (!player_can_see_bold(p_ptr->py, p_ptr->px));
+}
+
+
+
+/*
+ * Determine if a given location may be "destroyed"
+ *
+ * Used by destruction spells, and for placing stairs, etc.
+ */
+bool_ cave_valid_bold(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Forbid perma-grids */
+ if (cave_perma_grid(c_ptr)) return (FALSE);
+
+ /* Check objects */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Forbid artifact grids */
+ if ((o_ptr->art_name) || artifact_p(o_ptr)) return (FALSE);
+ }
+
+ /* Accept */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Hack -- Legal monster codes
+ */
+static cptr image_monster_hack = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+/*
+ * Hack -- Legal monster codes for IBM pseudo-graphics
+ *
+ * Dropped. Although this option has long left unmaintained, hardcoding
+ * code points makes it impossible to update the font and prf files
+ * flexibly. And the normal graphics code still works with it -- pelpel
+ */
+
+/*
+ * Mega-Hack -- Hallucinatory monster
+ */
+static void image_monster(byte *ap, char *cp)
+{
+ int n;
+
+ switch (graphics_mode)
+ {
+ /* Text mode */
+ case GRAPHICS_NONE:
+ {
+ n = strlen(image_monster_hack);
+
+ /* Random symbol from set above */
+ *cp = (image_monster_hack[rand_int(n)]);
+
+ /* Random color */
+ *ap = randint(15);
+
+ break;
+ }
+
+ /* Normal graphics */
+ default:
+ {
+ /* Avoid player ghost */
+ n = randint(max_r_idx);
+
+ *cp = r_info[n].x_char;
+
+ *ap = r_info[n].x_attr;
+
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Hack -- Legal object codes
+ */
+static cptr image_object_hack = "?/|\\\"!$()_-=[]{},~";
+
+/*
+ * Hardcoded IBM pseudo-graphics code points have been removed
+ * for the same reason as stated above -- pelpel
+ */
+
+/*
+ * Mega-Hack -- Hallucinatory object
+ */
+static void image_object(byte *ap, char *cp)
+{
+ int n;
+
+ switch (graphics_mode)
+ {
+ /* Text mode */
+ case GRAPHICS_NONE:
+ {
+ n = strlen(image_object_hack);
+
+ /* Random symbol from set above */
+ *cp = (image_object_hack[rand_int(n)]);
+
+ /* Random color */
+ *ap = randint(15);
+
+ /* Done */
+ break;
+ }
+
+ /* Normal graphics */
+ default:
+ {
+ n = randint(max_k_idx - 1);
+
+ *cp = k_info[n].x_char;
+ *ap = k_info[n].x_attr;
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Hack -- Random hallucination
+ */
+static void image_random(byte *ap, char *cp)
+{
+ /* Normally, assume monsters */
+ if (rand_int(100) < 75)
+ {
+ image_monster(ap, cp);
+ }
+
+ /* Otherwise, assume objects */
+ else
+ {
+ image_object(ap, cp);
+ }
+}
+
+
+/*
+ * The 16x16 tile of the terrain supports lighting
+ */
+#if 1
+
+#define feat_supports_lighting(F) \
+((f_info[F].flags1 & FF1_SUPPORT_LIGHT) != 0)
+
+#else
+
+static bool_ feat_supports_lighting(byte feat)
+{
+ if (f_info[feat].flags1 & FF1_SUPPORT_LIGHT) return TRUE;
+ else return FALSE;
+}
+
+#endif
+
+
+char get_shimmer_color()
+{
+ switch (randint(7))
+ {
+ case 1:
+ return (TERM_RED);
+ case 2:
+ return (TERM_L_RED);
+ case 3:
+ return (TERM_WHITE);
+ case 4:
+ return (TERM_L_GREEN);
+ case 5:
+ return (TERM_BLUE);
+ case 6:
+ return (TERM_L_DARK);
+ case 7:
+ return (TERM_GREEN);
+ }
+
+ return (TERM_VIOLET);
+}
+
+
+/*
+ * Table of breath colors. Must match listings in a single set of
+ * monster spell flags.
+ *
+ * The value "255" is special. Monsters with that kind of breath
+ * may be any color.
+ */
+static byte breath_to_attr[32][2] =
+{
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { TERM_SLATE, TERM_L_DARK }, /* RF4_BRTH_ACID */
+ { TERM_BLUE, TERM_L_BLUE }, /* RF4_BRTH_ELEC */
+ { TERM_RED, TERM_L_RED }, /* RF4_BRTH_FIRE */
+ { TERM_WHITE, TERM_L_WHITE }, /* RF4_BRTH_COLD */
+ { TERM_GREEN, TERM_L_GREEN }, /* RF4_BRTH_POIS */
+ { TERM_L_GREEN, TERM_GREEN }, /* RF4_BRTH_NETHR */
+ { TERM_YELLOW, TERM_ORANGE }, /* RF4_BRTH_LITE */
+ { TERM_L_DARK, TERM_SLATE }, /* RF4_BRTH_DARK */
+ { TERM_L_UMBER, TERM_UMBER }, /* RF4_BRTH_CONFU */
+ { TERM_YELLOW, TERM_L_UMBER }, /* RF4_BRTH_SOUND */
+ { 255, 255 }, /* (any color) */ /* RF4_BRTH_CHAOS */
+ { TERM_VIOLET, TERM_VIOLET }, /* RF4_BRTH_DISEN */
+ { TERM_L_RED, TERM_VIOLET }, /* RF4_BRTH_NEXUS */
+ { TERM_L_BLUE, TERM_L_BLUE }, /* RF4_BRTH_TIME */
+ { TERM_L_WHITE, TERM_SLATE }, /* RF4_BRTH_INER */
+ { TERM_L_WHITE, TERM_SLATE }, /* RF4_BRTH_GRAV */
+ { TERM_UMBER, TERM_L_UMBER }, /* RF4_BRTH_SHARD */
+ { TERM_ORANGE, TERM_RED }, /* RF4_BRTH_PLAS */
+ { TERM_UMBER, TERM_L_UMBER }, /* RF4_BRTH_FORCE */
+ { TERM_L_BLUE, TERM_WHITE }, /* RF4_BRTH_MANA */
+ { 0, 0 }, /* */
+ { TERM_GREEN, TERM_L_GREEN }, /* RF4_BRTH_NUKE */
+ { 0, 0 }, /* */
+ { TERM_WHITE, TERM_L_RED }, /* RF4_BRTH_DISINT */
+};
+
+
+/*
+ * Multi-hued monsters shimmer acording to their breaths.
+ *
+ * If a monster has only one kind of breath, it uses both colors
+ * associated with that breath. Otherwise, it just uses the first
+ * color for any of its breaths.
+ *
+ * If a monster does not breath anything, it can be any color.
+ */
+static byte multi_hued_attr(monster_race *r_ptr)
+{
+ byte allowed_attrs[15];
+
+ int i, j;
+
+ int stored_colors = 0;
+
+ int breaths = 0;
+
+ int first_color = 0;
+
+ int second_color = 0;
+
+
+ /* Monsters with no ranged attacks can be any color */
+ if (!r_ptr->freq_inate) return (get_shimmer_color());
+
+ /* Check breaths */
+ for (i = 0; i < 32; i++)
+ {
+ bool_ stored = FALSE;
+
+ /* Don't have that breath */
+ if (!(r_ptr->flags4 & (1L << i))) continue;
+
+ /* Get the first color of this breath */
+ first_color = breath_to_attr[i][0];
+
+ /* Breath has no color associated with it */
+ if (first_color == 0) continue;
+
+ /* Monster can be of any color */
+ if (first_color == 255) return (randint(15));
+
+
+ /* Increment the number of breaths */
+ breaths++;
+
+ /* Monsters with lots of breaths may be any color. */
+ if (breaths == 6) return (randint(15));
+
+
+ /* Always store the first color */
+ for (j = 0; j < stored_colors; j++)
+ {
+ /* Already stored */
+ if (allowed_attrs[j] == first_color) stored = TRUE;
+ }
+ if (!stored)
+ {
+ allowed_attrs[stored_colors] = first_color;
+ stored_colors++;
+ }
+
+ /*
+ * Remember (but do not immediately store) the second color
+ * of the first breath.
+ */
+ if (breaths == 1)
+ {
+ second_color = breath_to_attr[i][1];
+ }
+ }
+
+ /* Monsters with no breaths may be of any color. */
+ if (breaths == 0) return (get_shimmer_color());
+
+ /* If monster has one breath, store the second color too. */
+ if (breaths == 1)
+ {
+ allowed_attrs[stored_colors] = second_color;
+ stored_colors++;
+ }
+
+ /* Pick a color at random */
+ return (allowed_attrs[rand_int(stored_colors)]);
+}
+
+
+/*
+ * Extract the attr/char to display at the given (legal) map location
+ *
+ * Note that this function, since it is called by "lite_spot()" which
+ * is called by "update_view()", is a major efficiency concern.
+ *
+ * Basically, we examine each "layer" of the world (terrain, objects,
+ * monsters/players), from the bottom up, extracting a new attr/char
+ * if necessary at each layer, and defaulting to "darkness". This is
+ * not the fastest method, but it is very simple, and it is about as
+ * fast as it could be for grids which contain no "marked" objects or
+ * "visible" monsters.
+ *
+ * We apply the effects of hallucination during each layer. Objects will
+ * always appear as random "objects", monsters will always appear as random
+ * "monsters", and normal grids occasionally appear as random "monsters" or
+ * "objects", but note that these random "monsters" and "objects" are really
+ * just "colored ascii symbols" (which may look silly on some machines).
+ *
+ * The hallucination functions avoid taking any pointers to local variables
+ * because some compilers refuse to use registers for any local variables
+ * whose address is taken anywhere in the function.
+ *
+ * As an optimization, we can handle the "player" grid as a special case.
+ *
+ * Note that the memorization of "objects" and "monsters" is not related
+ * to the memorization of "terrain". This allows the player to memorize
+ * the terrain of a grid without memorizing any objects in that grid, and
+ * to detect monsters without detecting anything about the terrain of the
+ * grid containing the monster.
+ *
+ * The fact that all interesting "objects" and "terrain features" are
+ * memorized as soon as they become visible for the first time means
+ * that we only have to check the "CAVE_SEEN" flag for "boring" grids.
+ *
+ * Note that bizarre things must be done when the "attr" and/or "char"
+ * codes have the "high-bit" set, since these values are used to encode
+ * various "special" pictures in some versions, and certain situations,
+ * such as "multi-hued" or "clear" monsters, cause the attr/char codes
+ * to be "scrambled" in various ways.
+ *
+ * Note that the "zero" entry in the feature/object/monster arrays are
+ * used to provide "special" attr/char codes, with "monster zero" being
+ * used for the player attr/char, "object zero" being used for the "stack"
+ * attr/char, and "feature zero" being used for the "nothing" attr/char.
+ *
+ * Note that eventually we may want to use the "&" symbol for embedded
+ * treasure, and use the "*" symbol to indicate multiple objects, but
+ * currently, we simply use the attr/char of the first "marked" object
+ * in the stack, if any, and so "object zero" is unused. XXX XXX XXX
+ *
+ * Note the assumption that doing "x_ptr = &x_info[x]" plus a few of
+ * "x_ptr->xxx", is quicker than "x_info[x].xxx", even if "x" is a fixed
+ * constant. If this is incorrect then a lot of code should be changed.
+ *
+ *
+ * Some comments on the "terrain" layer...
+ *
+ * Note that "boring" grids (floors, invisible traps, and any illegal grids)
+ * are very different from "interesting" grids (all other terrain features),
+ * and the two types of grids are handled completely separately. The most
+ * important distinction is that "boring" grids may or may not be memorized
+ * when they are first encountered, and so we must use the "CAVE_SEEN" flag
+ * to see if they are "see-able".
+ *
+ *
+ * Some comments on the "terrain" layer (boring grids)...
+ *
+ * Note that "boring" grids are always drawn using the picture for "empty
+ * floors", which is stored in "f_info[FEAT_FLOOR]". Sometimes, special
+ * lighting effects may cause this picture to be modified.
+ *
+ * Note that "invisible traps" are always displayes exactly like "empty
+ * floors", which prevents various forms of "cheating", with no loss of
+ * efficiency. There are still a few ways to "guess" where traps may be
+ * located, for example, objects will never fall into a grid containing
+ * an invisible trap. XXX XXX
+ *
+ * To determine if a "boring" grid should be displayed, we simply check to
+ * see if it is either memorized ("CAVE_MARK"), or currently "see-able" by
+ * the player ("CAVE_SEEN"). Note that "CAVE_SEEN" is now maintained by the
+ * "update_view()" function.
+ *
+ * Note the "special lighting effects" which can be activated for "boring"
+ * grids using the "view_special_lite" option, causing certain such grids
+ * to be displayed using special colors. If the grid is "see-able" by
+ * the player, we will use the normal (except that, if the "view_yellow_lite"
+ * option is set, and the grid is *only* "see-able" because of the player's
+ * torch, then we will use "yellow"), else if the player is "blind", we will
+ * use greyscale, else if the grid is not "illuminated", we will use "dark
+ * gray", if the "view_bright_lite" option is set, we will use "darker" colour
+ * else we will use the normal colour.
+ *
+ *
+ * Some comments on the "terrain" layer (non-boring grids)...
+ *
+ * Note the use of the "mimic" field in the "terrain feature" processing,
+ * which allows any feature to "pretend" to be another feature. This is
+ * used to "hide" secret doors, and to make all "doors" appear the same,
+ * and all "walls" appear the same, and "hidden" treasure stay hidden.
+ * Note that it is possible to use this field to make a feature "look"
+ * like a floor, but the "view_special_lite" flag only affects actual
+ * "boring" grids.
+ *
+ * Since "interesting" grids are always memorized as soon as they become
+ * "see-able" by the player ("CAVE_SEEN"), such a grid only needs to be
+ * displayed if it is memorized ("CAVE_MARK"). Most "interesting" grids
+ * are in fact non-memorized, non-see-able, wall grids, so the fact that
+ * we do not have to check the "CAVE_SEEN" flag adds some efficiency, at
+ * the cost of *forcing* the memorization of all "interesting" grids when
+ * they are first seen. Since the "CAVE_SEEN" flag is now maintained by
+ * the "update_view()" function, this efficiency is not as significant as
+ * it was in previous versions, and could perhaps be removed.
+ * (so I removed this to simplify the terrain feature handling -- pelpel)
+ *
+ * Note the "special lighting effects" which can be activated for "wall"
+ * grids using the "view_granite_lite" option, causing certain such grids
+ * to be displayed using special colors.
+ * If the grid is "see-able" by the player, we will use the normal colour
+ * else if the player is "blind", we will use grey scale, else if the
+ * "view_bright_lite" option is set, we will use reduced colour, else we
+ * will use the normal one.
+ *
+ * Note that "wall" grids are more complicated than "boring" grids, due to
+ * the fact that "CAVE_GLOW" for a "wall" grid means that the grid *might*
+ * be glowing, depending on where the player is standing in relation to the
+ * wall. In particular, the wall of an illuminated room should look just
+ * like any other (dark) wall unless the player is actually inside the room.
+ *
+ * Thus, we do not support as many visual special effects for "wall" grids
+ * as we do for "boring" grids, since many of them would give the player
+ * information about the "CAVE_GLOW" flag of the wall grid, in particular,
+ * it would allow the player to notice the walls of illuminated rooms from
+ * a dark hallway that happened to run beside the room.
+ *
+ *
+ * Some comments on the "object" layer...
+ *
+ * Currently, we do nothing with multi-hued objects, because there are
+ * not any. If there were, they would have to set "shimmer_objects"
+ * when they were created, and then new "shimmer" code in "dungeon.c"
+ * would have to be created handle the "shimmer" effect, and the code
+ * in "cave.c" would have to be updated to create the shimmer effect.
+ * This did not seem worth the effort. XXX XXX
+ *
+ *
+ * Some comments on the "monster"/"player" layer...
+ *
+ * Note that monsters can have some "special" flags, including "ATTR_MULTI",
+ * which means their color changes, and "ATTR_CLEAR", which means they take
+ * the color of whatever is under them, and "CHAR_CLEAR", which means that
+ * they take the symbol of whatever is under them. Technically, the flag
+ * "CHAR_MULTI" is supposed to indicate that a monster looks strange when
+ * examined, but this flag is currently ignored. All of these flags are
+ * ignored if the "avoid_other" option is set, since checking for these
+ * conditions is expensive (and annoying) on some systems.
+ *
+ * Normally, players could be handled just like monsters, except that the
+ * concept of the "torch lite" of others player would add complications.
+ * For efficiency, however, we handle the (only) player first, since the
+ * "player" symbol always "pre-empts" any other facts about the grid.
+ *
+ * The "hidden_player" efficiency option, which only makes sense with a
+ * single player, allows the player symbol to be hidden while running.
+ */
+
+/*
+ * Alternative colours for unseen grids
+ *
+ * Reduced colours - remembered interesting grids and perma-lit floors
+ * B&W - currently only used by blindness effect
+ */
+
+/* Colour */
+static byte dark_attrs[16] =
+{
+ TERM_DARK, TERM_L_WHITE, TERM_L_DARK, TERM_ORANGE,
+ TERM_RED, TERM_GREEN, TERM_BLUE, TERM_UMBER,
+ TERM_L_DARK, TERM_SLATE, TERM_VIOLET, TERM_YELLOW,
+ TERM_RED, TERM_GREEN, TERM_BLUE, TERM_UMBER
+};
+
+/* B&W */
+static byte darker_attrs[16] =
+{
+ TERM_DARK, TERM_L_WHITE, TERM_L_DARK, TERM_SLATE,
+ TERM_L_DARK, TERM_L_DARK, TERM_L_DARK, TERM_L_DARK,
+ TERM_L_DARK, TERM_SLATE, TERM_L_DARK, TERM_SLATE,
+ TERM_SLATE, TERM_SLATE, TERM_SLATE, TERM_SLATE
+};
+
+
+void map_info(int y, int x, byte *ap, char *cp, byte *tap, char *tcp,
+ byte *eap, char *ecp)
+{
+ cave_type *c_ptr;
+
+ feature_type *f_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ u16b info;
+
+ s16b t_idx;
+
+ byte feat;
+
+ byte a;
+
+ byte c;
+
+ /*
+ * This means that a port supports graphics overlay as well as lighting
+ * effects. See the step 3 below for the detailed information about
+ * lighting. Basically, it requires "darker" tiles for those terrain
+ * features with SUPPORT_LIGHT flag set, and they must be arranged
+ * this way:
+ * col col+1 col+2
+ * row base darker brighter
+ */
+ bool_ graf_new = ((graphics_mode == GRAPHICS_ISO) ||
+ (graphics_mode == GRAPHICS_NEW));
+
+ /*
+ * I never understand why some coders like shimmering so much.
+ * It just serves to hurt my eyes, IMHO. If one feels like to show off,
+ * go for better graphics support... Anyway this means a port allows
+ * changing attr independently from its char -- pelpel
+ */
+ bool_ attr_mutable = (!use_graphics ||
+ (graphics_mode == GRAPHICS_IBM));
+
+
+ /**** Preparation ****/
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+
+ /* Cache some frequently used values */
+
+ /* Grid info */
+ info = c_ptr->info;
+
+ /* Feature code */
+ feat = c_ptr->feat;
+
+ /* Apply "mimic" field */
+ if (c_ptr->mimic)
+ {
+ feat = c_ptr->mimic;
+ }
+ else
+ {
+ feat = f_info[feat].mimic;
+ }
+
+ /* Access floor */
+ f_ptr = &f_info[feat];
+
+
+ /* Reset attr/char */
+ *eap = 0;
+ *ecp = 0;
+
+
+ /**** Layer 1 -- Terrain feature ****/
+
+ /* Only memorised or visible grids are displayed */
+ if (info & (CAVE_MARK | CAVE_SEEN))
+ {
+ /**** Step 1 -- Retrieve base attr/char ****/
+
+ /* 'Sane' terrain features */
+ if (feat != FEAT_SHOP)
+ {
+ /* Normal char */
+ c = f_ptr->x_char;
+
+ /* Normal attr */
+ a = f_ptr->x_attr;
+ }
+
+ /* Mega-Hack 1 -- Building don't conform to f_info */
+ else
+ {
+ c = st_info[c_ptr->special].x_char;
+ a = st_info[c_ptr->special].x_attr;
+ }
+
+ /* Mega-Hack 2 -- stair to dungeon branch are purple */
+ if (c_ptr->special && attr_mutable &&
+ ((feat == FEAT_MORE) || (feat == FEAT_LESS)))
+ {
+ a = TERM_VIOLET;
+ }
+
+ /* Mega-Hack 3 -- Traps don't have f_info entries either */
+ if ((info & (CAVE_TRDT)) && (feat != FEAT_ILLUS_WALL))
+ {
+ /* Trap index */
+ t_idx = c_ptr->t_idx;
+
+ if (use_graphics &&
+ (t_info[t_idx].g_attr != 0) &&
+ (t_info[t_idx].g_char != 0))
+ {
+
+ if (graf_new)
+ {
+ *eap = t_info[t_idx].g_attr;
+ *ecp = t_info[t_idx].g_char;
+ }
+ else
+ {
+ a = t_info[t_idx].g_attr;
+ c = t_info[t_idx].g_char;
+ }
+
+ }
+ else
+ {
+ /*
+ * If trap is set on a floor grid that is not
+ * one of "interesting" features, use a special
+ * symbol to display it. Check for doors is no longer
+ * necessary because they have REMEMBER flag now.
+ *
+ * Cave macros cannot be used safely here, because of
+ * c_ptr->mimic XXX XXX
+ */
+ if (!attr_mutable)
+ {
+ a = f_info[FEAT_TRAP].x_attr;
+ c = f_info[FEAT_TRAP].x_char;
+ }
+ else
+ {
+ if ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR)
+ {
+ c = f_info[FEAT_TRAP].x_char;
+ }
+
+ /* Add attr XXX XXX XXX */
+ a = t_info[t_idx].color;
+
+ /* Get a new color with a strange formula :) XXX XXX XXX */
+ if (t_info[t_idx].flags & FTRAP_CHANGE)
+ {
+ s32b tmp;
+
+ tmp = dun_level + dungeon_type + feat;
+
+ a = tmp % 16;
+ }
+ }
+ }
+ }
+
+
+ /**** Step 2 -- Apply special random effects ****/
+ if (!avoid_other && !avoid_shimmer && attr_mutable)
+ {
+ /* Special terrain effect */
+ if (c_ptr->effect)
+ {
+ a = spell_color(effects[c_ptr->effect].type);
+ }
+
+ /* Multi-hued attr */
+ else if (f_ptr->flags1 & FF1_ATTR_MULTI)
+ {
+ a = f_ptr->shimmer[rand_int(7)];
+ }
+ }
+
+
+ /*
+ * Step 3
+ *
+ * Special lighting effects, if specified and applicable
+ * This will never happen for
+ * - any grids in the overhead map
+ * - traps
+ * - (graphics modes) terrain features without corresponding
+ * "darker" tiles.
+ *
+ * Note the use of f_ptr->flags1 to avoid problems with
+ * c_ptr->mimic.
+ */
+
+ /* view_special_lite: lighting effects for boring features */
+ if (view_special_lite &&
+ ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR))
+ {
+ if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)) &&
+ (attr_mutable || (graf_new && feat_supports_lighting(feat))))
+ {
+ /* Handle "seen" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Only lit by "torch" light */
+ if (view_yellow_lite && !(info & (CAVE_GLOW)))
+ {
+ if (graf_new)
+ {
+ /* Use a brightly lit tile */
+ c += 2;
+ }
+ else
+ {
+ /* Use "yellow" */
+ a = TERM_YELLOW;
+ }
+ }
+ }
+
+ /* Handle "blind" */
+ else if (p_ptr->blind)
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darker colour */
+ a = darker_attrs[a & 0xF];
+ }
+ }
+
+ /* Handle "dark" grids */
+ else if (!(info & (CAVE_GLOW)))
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darkest colour */
+ a = TERM_L_DARK;
+ }
+ }
+
+ /* "Out-of-sight" glowing grids -- handle "view_bright_lite" */
+ else if (view_bright_lite)
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darker colour */
+ a = dark_attrs[a & 0xF];
+ }
+ }
+ }
+ }
+
+ /* view_granite_lite: lighting effects for walls and doors */
+ else if (view_granite_lite &&
+ (f_ptr->flags1 & (FF1_NO_VISION | FF1_DOOR)))
+ {
+ if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)) &&
+ (attr_mutable || (graf_new && feat_supports_lighting(feat))))
+ {
+ /* Handle "seen" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Do nothing */
+ }
+
+ /* Handle "blind" */
+ else if (p_ptr->blind)
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darker colour */
+ a = darker_attrs[a & 0xF];
+ }
+ }
+
+ /* Handle "view_bright_lite" */
+ else if (view_bright_lite)
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darker colour */
+ a = dark_attrs[a & 0xF];
+ }
+ }
+
+ else
+ {
+ if (graf_new)
+ {
+ /* Use a brightly lit tile */
+ c += 2;
+ }
+ else
+ {
+ /* Use normal colour */
+ }
+ }
+ }
+ }
+ }
+
+ /* Unknown grids */
+ else
+ {
+ /* Access darkness */
+ f_ptr = &f_info[FEAT_NONE];
+
+ /* Normal attr */
+ a = f_ptr->x_attr;
+
+ /* Normal char */
+ c = f_ptr->x_char;
+ }
+
+ /*
+ * Hack -- rare random hallucination
+ * Because we cannot be sure which is outer dungeon walls,
+ * the check for 'feat' has been removed
+ */
+ if (p_ptr->image && (rand_int(256) == 0))
+ {
+ /* Hallucinate */
+ image_random(ap, cp);
+ }
+
+ /* Save the terrain info for the transparency effects */
+ *tap = a;
+ *tcp = c;
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+
+ /**** Layer 2 -- Objects ****/
+
+ if (feat != FEAT_MON_TRAP)
+ {
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Memorized objects */
+ if (o_ptr->marked)
+ {
+ /* Normal char */
+ *cp = object_char(o_ptr);
+
+ /* Normal attr */
+ *ap = object_attr(o_ptr);
+
+ /* Multi-hued attr */
+ if (!avoid_other && attr_mutable &&
+ (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI))
+ {
+ *ap = get_shimmer_color();
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image) image_object(ap, cp);
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+
+ /**** Layer 3 -- Handle monsters ****/
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ /* Memorized objects */
+ if (o_ptr->marked)
+ {
+ /* Normal char */
+ *cp = object_char(o_ptr);
+
+ /* Normal attr */
+ *ap = object_attr(o_ptr);
+
+ /* Multi-hued attr */
+ if (!avoid_other && attr_mutable &&
+ (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI))
+ {
+ *ap = get_shimmer_color();
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image) image_object(ap, cp);
+ }
+ }
+ else
+ {
+ /* Visible monster */
+ if (m_ptr->ml)
+ {
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Reset attr/char */
+ *eap = 0;
+ *ecp = 0;
+
+ if (use_graphics)
+ {
+
+ if (graf_new)
+ {
+ monster_ego *re_ptr = &re_info[m_ptr->ego];
+
+ /* Desired attr */
+ *eap = re_ptr->g_attr;
+
+ /* Desired char */
+ *ecp = re_ptr->g_char;
+ }
+
+ /* Use base monster */
+ r_ptr = &r_info[m_ptr->r_idx];
+ }
+
+ /* Desired attr/char */
+ c = r_ptr->x_char;
+ a = r_ptr->x_attr;
+
+ /* Ignore weird codes */
+ if (avoid_other)
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Special attr/char codes */
+ else if (!attr_mutable)
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Multi-hued monster */
+ else if (r_ptr->flags1 & (RF1_ATTR_MULTI))
+ {
+ /* Is it a shapechanger? */
+ if (r_ptr->flags2 & (RF2_SHAPECHANGER))
+ {
+ image_random(ap, cp);
+ }
+ else
+ *cp = c;
+
+ /* Multi-hued attr */
+ if (r_ptr->flags2 & (RF2_ATTR_ANY))
+ {
+ *ap = randint(15);
+ }
+ else
+ {
+ *ap = multi_hued_attr(r_ptr);
+ }
+ }
+
+ /* Normal monster (not "clear" in any way) */
+ else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR | RF1_CHAR_CLEAR)))
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /*
+ * Hack -- Bizarre grid under monster
+ * WAS: else if (*ap & 0x80) || (*cp & 0x80) -- pelpel
+ */
+ else if (*ap & 0x80)
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Normal */
+ else
+ {
+ /* Normal (non-clear char) monster */
+ if (!(r_ptr->flags1 & (RF1_CHAR_CLEAR)))
+ {
+ /* Normal char */
+ *cp = c;
+ }
+
+ /* Normal (non-clear attr) monster */
+ else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR)))
+ {
+ /* Normal attr */
+ *ap = a;
+ }
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image)
+ {
+ /* Hallucinatory monster */
+ image_monster(ap, cp);
+ }
+ }
+ }
+ }
+
+ /* Handle "player" */
+ if ((y == p_ptr->py) && (x == p_ptr->px) &&
+ (!p_ptr->invis || p_ptr->see_inv))
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ /* Reset attr/char */
+ *eap = 0;
+ *ecp = 0;
+
+ /* Get the "player" attr */
+ if (!avoid_other && attr_mutable && (r_ptr->flags1 & RF1_ATTR_MULTI))
+ {
+ a = get_shimmer_color();
+ }
+ else
+ {
+ a = r_ptr->x_attr;
+ }
+
+ /* Get the "player" char */
+ c = r_ptr->x_char;
+
+
+ /* Mega-Hack -- Apply modifications to player graphics XXX XXX XXX */
+ switch (graphics_mode)
+ {
+ case GRAPHICS_NONE:
+ case GRAPHICS_IBM:
+ {
+ if (player_char_health)
+ {
+ int percent = p_ptr->chp * 10 / p_ptr->mhp;
+
+ if (percent < 7)
+ {
+ c = I2D(percent);
+ if (percent < 3) a = TERM_L_RED;
+ }
+ }
+
+ break;
+ }
+
+ case GRAPHICS_OLD:
+ {
+ if (player_symbols)
+ {
+ a = BMP_FIRST_PC_CLASS + p_ptr->pclass;
+ c = BMP_FIRST_PC_RACE + p_ptr->prace;
+ }
+
+ break;
+ }
+
+ case GRAPHICS_ISO:
+ case GRAPHICS_NEW:
+ {
+ if (p_ptr->pracem)
+ {
+ player_race_mod *rmp_ptr = &race_mod_info[p_ptr->pracem];
+
+ /* Desired attr */
+ *eap = rmp_ptr->g_attr;
+
+ /* Desired char */
+ *ecp = rmp_ptr->g_char;
+ }
+
+ /* +AKH 20020421 - Health dispay for graphics, too */
+ if (player_char_health && (graphics_mode == GRAPHICS_NEW))
+ {
+ int percent = p_ptr->chp * 14 / p_ptr->mhp;
+
+ if (percent < 10)
+ {
+ *eap = 10;
+ *ecp = 32 + 14 - percent;
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+ }
+}
+
+
+/*
+ * Special version of map_info, for use by cmovie and HTML converter
+ * to obtain pure-ASCII image of dungeon map
+ */
+void map_info_default(int y, int x, byte *ap, char *cp)
+{
+ cave_type *c_ptr;
+
+ feature_type *f_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ u16b info;
+
+ s16b t_idx;
+
+ byte feat;
+
+ byte a;
+
+ byte c;
+
+ bool_ use_graphics_hack = use_graphics;
+ byte graphics_mode_hack = graphics_mode;
+
+
+ /* Temporarily disable graphics mode -- for some random effects XXX */
+ use_graphics = FALSE;
+ graphics_mode = GRAPHICS_NONE;
+
+ /**** Preparation ****/
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+
+ /* Cache some frequently used values */
+
+ /* Grid info */
+ info = c_ptr->info;
+
+ /* Feature code */
+ feat = c_ptr->feat;
+
+ /* Apply "mimic" field */
+ if (c_ptr->mimic)
+ {
+ feat = c_ptr->mimic;
+ }
+ else
+ {
+ feat = f_info[feat].mimic;
+ }
+
+ /* Access floor */
+ f_ptr = &f_info[feat];
+
+
+ /**** Layer 1 -- Terrain feature ****/
+
+ /* Only memorised or visible grids are displayed */
+ if (info & (CAVE_MARK | CAVE_SEEN))
+ {
+ /**** Step 1 -- Retrieve base attr/char ****/
+
+ /* 'Sane' terrain features */
+ if (feat != FEAT_SHOP)
+ {
+ /* Default char */
+ c = f_ptr->d_char;
+
+ /* Default attr */
+ a = f_ptr->d_attr;
+ }
+
+ /* Mega-Hack 1 -- Building don't conform to f_info */
+ else
+ {
+ c = st_info[c_ptr->special].d_char;
+ a = st_info[c_ptr->special].d_attr;
+ }
+
+ /* Mega-Hack 2 -- stair to dungeon branch are purple */
+ if (c_ptr->special &&
+ ((feat == FEAT_MORE) || (feat == FEAT_LESS)))
+ {
+ a = TERM_VIOLET;
+ }
+
+ /* Mega-Hack 3 -- Traps don't have f_info entries either */
+ if ((info & (CAVE_TRDT)) && (feat != FEAT_ILLUS_WALL))
+ {
+ /* Trap index */
+ t_idx = c_ptr->t_idx;
+
+ /*
+ * If trap is set on a floor grid that is not
+ * one of "interesting" features, use a special
+ * symbol to display it. Check for doors is no longer
+ * necessary because they have REMEMBER flag now.
+ *
+ * Cave macros cannot be used safely here, because of
+ * c_ptr->mimic XXX XXX
+ */
+ if ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR)
+ {
+ c = f_info[FEAT_TRAP].d_char;
+ }
+
+ /* Add attr */
+ a = t_info[t_idx].color;
+
+ /* Get a new color with a strange formula :) */
+ if (t_info[t_idx].flags & FTRAP_CHANGE)
+ {
+ s32b tmp;
+
+ tmp = dun_level + dungeon_type + feat;
+
+ a = tmp % 16;
+ }
+ }
+
+
+ /**** Step 2 -- Apply special random effects ****/
+ if (!avoid_other)
+ {
+ /* Special terrain effect */
+ if (c_ptr->effect)
+ {
+ a = spell_color(effects[c_ptr->effect].type);
+ }
+
+ /* Multi-hued attr */
+ else if (f_ptr->flags1 & FF1_ATTR_MULTI)
+ {
+ a = f_ptr->shimmer[rand_int(7)];
+ }
+ }
+
+
+ /*
+ * Step 3
+ *
+ * Special lighting effects, if specified and applicable
+ * This will never happen for
+ * - any grids in the overhead map
+ * - traps
+ * - (graphics modes) terrain features without corresponding
+ * "darker" tiles.
+ *
+ * All the if's here are flag checks, so changed order shouldn't
+ * affect performance a lot, I hope...
+ */
+
+ /* view_special_lite: lighting effects for boring features */
+ if (view_special_lite &&
+ ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR))
+ {
+ if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)))
+ {
+ /* Handle "seen" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Only lit by "torch" light */
+ if (view_yellow_lite && !(info & (CAVE_GLOW)))
+ {
+ /* Use "yellow" */
+ a = TERM_YELLOW;
+ }
+ }
+
+ /* Handle "blind" */
+ else if (p_ptr->blind)
+ {
+ /* Use darker colour */
+ a = darker_attrs[a & 0xF];
+ }
+
+ /* Handle "dark" grids */
+ else if (!(info & (CAVE_GLOW)))
+ {
+ /* Use darkest colour */
+ a = TERM_L_DARK;
+ }
+
+ /* "Out-of-sight" glowing grids -- handle "view_bright_lite" */
+ else if (view_bright_lite)
+ {
+ /* Use darker colour */
+ a = dark_attrs[a & 0xF];
+ }
+ }
+ }
+
+ /* view_granite_lite: lighting effects for walls and doors */
+ else if (view_granite_lite &&
+ (f_ptr->flags1 & (FF1_NO_VISION | FF1_DOOR)))
+ {
+ if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)))
+ {
+ /* Handle "seen" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Do nothing */
+ }
+
+ /* Handle "blind" */
+ else if (p_ptr->blind)
+ {
+ /* Use darker colour */
+ a = darker_attrs[a & 0xF];
+ }
+
+ /* Handle "view_bright_lite" */
+ else if (view_bright_lite)
+ {
+ /* Use darker colour */
+ a = dark_attrs[a & 0xF];
+ }
+ }
+ }
+ }
+
+ /* Unknown grids */
+ else
+ {
+ /* Access darkness */
+ f_ptr = &f_info[FEAT_NONE];
+
+ /* Default attr */
+ a = f_ptr->d_attr;
+
+ /* Default char */
+ c = f_ptr->d_char;
+ }
+
+ /*
+ * Hack -- rare random hallucination
+ * Because we cannot be sure which is outer dungeon walls,
+ * the check for 'feat' has been removed
+ */
+ if (p_ptr->image && (rand_int(256) == 0))
+ {
+ /* Hallucinate */
+ image_random(ap, cp);
+ }
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+
+ /**** Layer 2 -- Objects ****/
+
+ if (feat != FEAT_MON_TRAP)
+ {
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Memorized objects */
+ if (o_ptr->marked)
+ {
+ /* Normal char */
+ *cp = object_char_default(o_ptr);
+
+ /* Normal attr */
+ *ap = object_attr_default(o_ptr);
+
+ /* Multi-hued attr */
+ if (!avoid_other &&
+ (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI))
+ {
+ *ap = get_shimmer_color();
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image) image_object(ap, cp);
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+
+ /**** Layer 3 -- Handle monsters ****/
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ /* Memorized objects */
+ if (o_ptr->marked)
+ {
+ /* Normal char */
+ *cp = object_char_default(o_ptr);
+
+ /* Normal attr */
+ *ap = object_attr_default(o_ptr);
+
+ /* Multi-hued attr */
+ if (!avoid_other && !use_graphics &&
+ (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI))
+ {
+ *ap = get_shimmer_color();
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image) image_object(ap, cp);
+ }
+ }
+ else
+ {
+ /* Visible monster */
+ if (m_ptr->ml)
+ {
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Default attr/char */
+ c = r_ptr->d_char;
+ a = r_ptr->d_attr;
+
+ /* Ignore weird codes */
+ if (avoid_other)
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Multi-hued monster */
+ else if (r_ptr->flags1 & (RF1_ATTR_MULTI))
+ {
+ /* Is it a shapechanger? */
+ if (r_ptr->flags2 & (RF2_SHAPECHANGER))
+ {
+ image_random(ap, cp);
+ }
+ else
+ *cp = c;
+
+ /* Multi-hued attr */
+ if (r_ptr->flags2 & (RF2_ATTR_ANY))
+ {
+ *ap = randint(15);
+ }
+ else
+ {
+ *ap = multi_hued_attr(r_ptr);
+ }
+ }
+
+ /* Normal monster (not "clear" in any way) */
+ else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR | RF1_CHAR_CLEAR)))
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Hack -- Bizarre grid under monster */
+ else if ((*ap & 0x80) || (*cp & 0x80))
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Normal */
+ else
+ {
+ /* Normal (non-clear char) monster */
+ if (!(r_ptr->flags1 & (RF1_CHAR_CLEAR)))
+ {
+ /* Normal char */
+ *cp = c;
+ }
+
+ /* Normal (non-clear attr) monster */
+ else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR)))
+ {
+ /* Normal attr */
+ *ap = a;
+ }
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image)
+ {
+ /* Hallucinatory monster */
+ image_monster(ap, cp);
+ }
+ }
+ }
+ }
+
+
+ /* Handle "player" */
+ if ((y == p_ptr->py) && (x == p_ptr->px) &&
+ (!p_ptr->invis ||
+ (p_ptr->invis && p_ptr->see_inv)))
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ /* Get the "player" attr */
+ if (!avoid_other && (r_ptr->flags1 & RF1_ATTR_MULTI))
+ {
+ a = get_shimmer_color();
+ }
+ else
+ {
+ a = r_ptr->d_attr;
+ }
+
+ /* Get the "player" char */
+ c = r_ptr->d_char;
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+ }
+
+ /* XXX Restore the graphics mode */
+ use_graphics = use_graphics_hack;
+ graphics_mode = graphics_mode_hack;
+}
+
+
+/*
+ * Calculate panel colum of a location in the map
+ */
+static int panel_col_of(int col)
+{
+ col -= panel_col_min;
+ if (use_bigtile) col *= 2;
+ return col + COL_MAP;
+}
+
+
+
+/*
+ * Moves the cursor to a given MAP (y,x) location
+ */
+void move_cursor_relative(int row, int col)
+{
+ /* Real co-ords convert to screen positions */
+ row -= panel_row_prt;
+
+ /* Go there */
+ Term_gotoxy(panel_col_of(col), row);
+}
+
+
+
+/*
+ * Place an attr/char pair at the given map coordinate, if legal.
+ */
+void print_rel(char c, byte a, int y, int x)
+{
+ /* Paranoia -- Only do "legal" locations */
+ if (!panel_contains(y, x)) return;
+
+ /* Draw the char using the attr */
+ Term_draw(panel_col_of(x), y - panel_row_prt, a, c);
+
+ if (use_bigtile)
+ {
+ char c2;
+ byte a2;
+
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_draw(panel_col_of(x) + 1, y - panel_row_prt, a2, c2);
+ }
+}
+
+
+
+
+
+/*
+ * Memorize interesting viewable object/features in the given grid
+ *
+ * This function should only be called on "legal" grids.
+ *
+ * This function will memorize the object and/or feature in the given
+ * grid, if they are (1) viewable and (2) interesting. Note that all
+ * objects are interesting, all terrain features except floors (and
+ * invisible traps) are interesting, and floors (and invisible traps)
+ * are interesting sometimes (depending on various options involving
+ * the illumination of floor grids).
+ *
+ * The automatic memorization of all objects and non-floor terrain
+ * features as soon as they are displayed allows incredible amounts
+ * of optimization in various places, especially "map_info()".
+ *
+ * Note that the memorization of objects is completely separate from
+ * the memorization of terrain features, preventing annoying floor
+ * memorization when a detected object is picked up from a dark floor,
+ * and object memorization when an object is dropped into a floor grid
+ * which is memorized but out-of-sight.
+ *
+ * This function should be called every time the "memorization" of
+ * a grid (or the object in a grid) is called into question, such
+ * as when an object is created in a grid, when a terrain feature
+ * "changes" from "floor" to "non-floor", when any grid becomes
+ * "illuminated" or "viewable", and when a "floor" grid becomes
+ * "torch-lit".
+ *
+ * Note the relatively efficient use of this function by the various
+ * "update_view()" and "update_lite()" calls, to allow objects and
+ * terrain features to be memorized (and drawn) whenever they become
+ * viewable or illuminated in any way, but not when they "maintain"
+ * or "lose" their previous viewability or illumination.
+ *
+ * Note the butchered "internal" version of "player_can_see_bold()",
+ * optimized primarily for the most common cases, that is, for the
+ * non-marked floor grids.
+ */
+void note_spot(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ u16b info = c_ptr->info;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Require "seen" flag */
+ if (!(info & (CAVE_SEEN))) return;
+
+
+ /* Hack -- memorize objects */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Memorize objects */
+ o_ptr->marked = TRUE;
+ }
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ o_ptr->marked = TRUE;
+ }
+ }
+
+
+ /* Hack -- memorize grids */
+ if (!(info & (CAVE_MARK)))
+ {
+ /* Memorise some "boring" grids */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Option -- memorise certain floors */
+ if ((info & (CAVE_TRDT)) ||
+ ((info & (CAVE_GLOW)) && view_perma_grids ) ||
+ view_torch_grids)
+ {
+ /* Memorize */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+
+ /* Memorise all "interesting" grids */
+ else
+ {
+ /* Memorize */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+}
+
+
+/*
+ * Redraw (on the screen) a given MAP location
+ *
+ * This function should only be called on "legal" grids
+ */
+void lite_spot(int y, int x)
+{
+ byte a, a2;
+ byte c, c2;
+
+ byte ta;
+ char tc;
+
+ byte ea;
+ char ec;
+
+
+ /* Redraw if on screen */
+ if (panel_contains(y, x))
+ {
+ /* Examine the grid */
+ map_info(y, x, &a, (char*)&c, &ta, &tc, &ea, &ec);
+
+ /* Hack -- Queue it */
+ Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc, ea, ec);
+ if (use_bigtile)
+ {
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0, 0, 0);
+ }
+
+ }
+}
+
+
+
+
+/*
+ * Prints the map of the dungeon
+ *
+ * Note that, for efficiency, we contain an "optimized" version
+ * of both "lite_spot()" and "print_rel()", and that we use the
+ * "lite_spot()" function to display the player grid, if needed.
+ */
+void prt_map(void)
+{
+ int x, y;
+
+ int v;
+
+ /* Access the cursor state */
+ (void)Term_get_cursor(&v);
+
+ /* Hide the cursor */
+ (void)Term_set_cursor(0);
+
+ /* Dump the map */
+ for (y = panel_row_min; y <= panel_row_max; y++)
+ {
+ /* Scan the columns of row "y" */
+ for (x = panel_col_min; x <= panel_col_max; x++)
+ {
+ byte a, a2;
+ char c, c2;
+
+ byte ta;
+ char tc;
+ byte ea;
+ char ec;
+
+ /* Determine what is there */
+ map_info(y, x, &a, &c, &ta, &tc, &ea, &ec);
+
+ /* Efficiency -- Redraw that grid of the map */
+ Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc, ea, ec);
+ if (use_bigtile)
+ {
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0, 0, 0);
+ }
+ }
+ }
+
+ /* Display player */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Restore the cursor */
+ (void)Term_set_cursor(v);
+}
+
+
+
+
+
+/*
+ * Display highest priority object in the RATIO by RATIO area
+ */
+
+/*
+ * Display the entire map
+ */
+#define MAP_HGT (MAX_HGT / RATIO)
+#define MAP_WID (MAX_WID / RATIO)
+
+/*
+ * Hack -- priority array (see below)
+ *
+ * Note that all "walls" always look like "secret doors" (see "map_info()").
+ */
+static byte priority_table[][2] =
+{
+ /* Dark */
+ { FEAT_NONE, 2 },
+
+ /* Floors */
+ { FEAT_FLOOR, 5 },
+
+ /* Walls */
+ { FEAT_SECRET, 10 },
+
+ /* Quartz */
+ { FEAT_QUARTZ, 11 },
+
+ /* Magma */
+ { FEAT_MAGMA, 12 },
+
+ /* Rubble */
+ { FEAT_RUBBLE, 13 },
+
+ /* Sandwall */
+ { FEAT_SANDWALL, 14 },
+
+ /* Open doors */
+ { FEAT_OPEN, 15 },
+ { FEAT_BROKEN, 15 },
+
+ /* Closed doors */
+ { FEAT_DOOR_HEAD + 0x00, 17 },
+
+ /* Hidden gold */
+ { FEAT_QUARTZ_K, 19 },
+ { FEAT_MAGMA_K, 19 },
+ { FEAT_SANDWALL_K, 19 },
+
+ /* water, lava, & trees oh my! -KMW- */
+ { FEAT_DEEP_WATER, 20 },
+ { FEAT_SHAL_WATER, 20 },
+ { FEAT_DEEP_LAVA, 20 },
+ { FEAT_SHAL_LAVA, 20 },
+ { FEAT_DIRT, 20 },
+ { FEAT_GRASS, 20 },
+ { FEAT_DARK_PIT, 20 },
+ { FEAT_TREES, 20 },
+ { FEAT_MOUNTAIN, 20 },
+ { FEAT_ICE, 20},
+ { FEAT_SAND, 20},
+ { FEAT_DEAD_TREE, 20},
+ { FEAT_ASH, 20},
+ { FEAT_MUD, 20},
+
+ /* Fountain */
+ { FEAT_FOUNTAIN, 22 },
+ { FEAT_EMPTY_FOUNTAIN, 22 },
+
+ /* Stairs */
+ { FEAT_LESS, 25 },
+ { FEAT_MORE, 25 },
+
+ /* Stairs */
+ { FEAT_WAY_LESS, 25 },
+ { FEAT_WAY_MORE, 25 },
+
+ { FEAT_SHAFT_UP, 25 },
+ { FEAT_SHAFT_DOWN, 25 },
+
+ /* End */
+ { 0, 0 }
+};
+
+
+/*
+ * Hack -- a priority function (see below)
+ */
+static byte priority(byte a, char c)
+{
+ int i, p0, p1;
+
+ feature_type *f_ptr;
+
+ /* Scan the table */
+ for (i = 0; TRUE; i++)
+ {
+ /* Priority level */
+ p1 = priority_table[i][1];
+
+ /* End of table */
+ if (!p1) break;
+
+ /* Feature index */
+ p0 = priority_table[i][0];
+
+ /* Access the feature */
+ f_ptr = &f_info[p0];
+
+ /* Check character and attribute, accept matches */
+ if ((f_ptr->x_char == c) && (f_ptr->x_attr == a)) return (p1);
+ }
+
+ /* Default */
+ return (20);
+}
+
+
+/*
+ * Display a "small-scale" map of the dungeon in the active Term
+ *
+ * Note that the "map_info()" function must return fully colorized
+ * data or this function will not work correctly.
+ *
+ * Note that this function must "disable" the special lighting
+ * effects so that the "priority" function will work.
+ *
+ * Note the use of a specialized "priority" function to allow this
+ * function to work with any graphic attr/char mappings, and the
+ * attempts to optimize this function where possible.
+ */
+void display_map(int *cy, int *cx)
+{
+ int i, j, x, y;
+
+ byte ta;
+ char tc;
+
+ byte tp;
+
+ byte **ma;
+ char **mc;
+
+ byte **mp;
+
+ bool_ old_view_special_lite;
+ bool_ old_view_granite_lite;
+
+ int hgt, wid, yrat, xrat, yfactor, xfactor;
+
+
+ /* Obtain current size of the Angband window */
+ Term_get_size(&wid, &hgt);
+
+ /* Use two characters as one tile in Bigtile mode */
+ if (use_bigtile) wid /= 2;
+
+ /*
+ * Calculate the size of the dungeon map area
+ */
+ hgt -= ROW_MAP + 2;
+ wid -= COL_MAP + 1;
+
+ /* Paranoia */
+ if ((hgt < 3) || (wid < 3))
+ {
+ /* Map is too small, but place the player anyway */
+ *cy = ROW_MAP;
+ *cx = COL_MAP;
+
+ return;
+ }
+
+
+ /* Save lighting effects */
+ old_view_special_lite = view_special_lite;
+ old_view_granite_lite = view_granite_lite;
+
+ /* Disable lighting effects */
+ view_special_lite = FALSE;
+ view_granite_lite = FALSE;
+
+
+ /* Allocate temporary memory for the maps */
+ C_MAKE(ma, hgt + 2, byte *);
+ C_MAKE(mc, hgt + 2, char *);
+ C_MAKE(mp, hgt + 2, byte *);
+
+ /* Allocate each line in the maps */
+ for (i = 0; i < hgt + 2; i++)
+ {
+ C_MAKE(ma[i], wid + 2, byte);
+ C_MAKE(mc[i], wid + 2, char);
+ C_MAKE(mp[i], wid + 2, byte);
+ }
+
+ /* Clear the chars and attributes */
+ for (y = 0; y < hgt + 2; ++y)
+ {
+ for (x = 0; x < wid + 2; ++x)
+ {
+ /* Nothing here */
+ ma[y][x] = TERM_WHITE;
+ mc[y][x] = ' ';
+
+ /* No priority */
+ mp[y][x] = 0;
+ }
+ }
+
+ /* Calculate scaling factors */
+ yfactor = ((cur_hgt / hgt < 4) && (cur_hgt > hgt)) ? 10 : 1;
+ xfactor = ((cur_wid / wid < 4) && (cur_wid > wid)) ? 10 : 1;
+
+ yrat = (cur_hgt * yfactor + (hgt - 1)) / hgt;
+ xrat = (cur_wid * xfactor + (wid - 1)) / wid;
+
+ /* Fill in the map */
+ for (j = 0; j < cur_hgt; ++j)
+ {
+ for (i = 0; i < cur_wid; ++i)
+ {
+ /* Location */
+ y = j * yfactor / yrat + 1;
+ x = i * xfactor / xrat + 1;
+
+ /* Extract the current attr/char at that map location */
+ map_info(j, i, &ta, &tc, &ta, &tc, &ta, &tc);
+
+ /* Extract the priority of that attr/char */
+ tp = priority(ta, tc);
+
+ /* Player location has the highest priority */
+ if ((p_ptr->py == j) && (p_ptr->px == i)) tp = 255;
+
+ /* Save "best" */
+ if (mp[y][x] < tp)
+ {
+ /* Save the char */
+ mc[y][x] = tc;
+
+ /* Save the attr */
+ ma[y][x] = ta;
+
+ /* Save priority */
+ mp[y][x] = tp;
+ }
+ }
+ }
+
+
+ /* Corners */
+ y = hgt + 1;
+ x = wid + 1;
+
+ /* Draw the corners */
+ mc[0][0] = mc[0][x] = mc[y][0] = mc[y][x] = '+';
+
+ /* Draw the horizontal edges */
+ for (x = 1; x <= wid; x++) mc[0][x] = mc[y][x] = '-';
+
+ /* Draw the vertical edges */
+ for (y = 1; y <= hgt; y++) mc[y][0] = mc[y][x] = '|';
+
+
+ /* Display each map line in order */
+ for (y = 0; y < hgt + 2; ++y)
+ {
+ /* Start a new line */
+ Term_gotoxy(COL_MAP - 1, y);
+
+ /* Display the line */
+ for (x = 0; x < wid + 2; ++x)
+ {
+ ta = ma[y][x];
+ tc = mc[y][x];
+
+ /* Add the character */
+ Term_addch(ta, tc);
+
+ /* Double width tile mode requires filler */
+ if (use_bigtile)
+ {
+ byte a2;
+ char c2;
+
+ if (ta & 0x80)
+ {
+ /* Mega-Hack */
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+
+ Term_addch(a2, c2);
+ }
+ }
+ }
+
+ /* Player location in dungeon */
+ *cy = p_ptr->py * yfactor / yrat + ROW_MAP;
+ if (!use_bigtile)
+ {
+ *cx = p_ptr->px * xfactor / xrat + COL_MAP;
+ }
+ else
+ {
+ *cx = (p_ptr->px * xfactor / xrat + 1) * 2 - 1 + COL_MAP;
+ }
+
+ /* Free each line in the maps */
+ for (i = 0; i < hgt + 2; i++)
+ {
+ C_FREE(ma[i], wid + 2, byte);
+ C_FREE(mc[i], wid + 2, char);
+ C_FREE(mp[i], wid + 2, byte);
+ }
+
+ /* Allocate temporary memory for the maps */
+ C_FREE(ma, hgt + 2, byte *);
+ C_FREE(mc, hgt + 2, char *);
+ C_FREE(mp, hgt + 2, byte *);
+
+
+ /* Restore lighting effects */
+ view_special_lite = old_view_special_lite;
+ view_granite_lite = old_view_granite_lite;
+}
+
+
+/*
+ * Display a "small-scale" map of the dungeon for the player
+ *
+ * Currently, the "player" is displayed on the map. XXX XXX XXX
+ */
+void do_cmd_view_map(void)
+{
+ int cy, cx;
+ int wid, hgt;
+
+ /* Retrive current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Note */
+ prt("Please wait...", 0, 0);
+
+ /* Flush */
+ Term_fresh();
+
+ /* Clear the screen */
+ Term_clear();
+
+ /* Display the map */
+ display_map(&cy, &cx);
+
+ /* Wait for it */
+ put_str("Hit any key to continue", hgt - 1, (wid - COL_MAP) / 2);
+
+ /* Hilite the player */
+ move_cursor(cy, cx);
+
+ /* Get any key */
+ inkey();
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+
+
+
+
+/*
+ * Some comments on the dungeon related data structures and functions...
+ *
+ * Angband is primarily a dungeon exploration game, and it should come as
+ * no surprise that the internal representation of the dungeon has evolved
+ * over time in much the same way as the game itself, to provide semantic
+ * changes to the game itself, to make the code simpler to understand, and
+ * to make the executable itself faster or more efficient in various ways.
+ *
+ * There are a variety of dungeon related data structures, and associated
+ * functions, which store information about the dungeon, and provide methods
+ * by which this information can be accessed or modified.
+ *
+ * Some of this information applies to the dungeon as a whole, such as the
+ * list of unique monsters which are still alive. Some of this information
+ * only applies to the current dungeon level, such as the current depth, or
+ * the list of monsters currently inhabiting the level. And some of the
+ * information only applies to a single grid of the current dungeon level,
+ * such as whether the grid is illuminated, or whether the grid contains a
+ * monster, or whether the grid can be seen by the player. If Angband was
+ * to be turned into a multi-player game, some of the information currently
+ * associated with the dungeon should really be associated with the player,
+ * such as whether a given grid is viewable by a given player.
+ *
+ * One of the major bottlenecks in ancient versions of Angband was in the
+ * calculation of "line of sight" from the player to various grids, such
+ * as those containing monsters, using the relatively expensive "los()"
+ * function. This was such a nasty bottleneck that a lot of silly things
+ * were done to reduce the dependancy on "line of sight", for example, you
+ * could not "see" any grids in a lit room until you actually entered the
+ * room, at which point every grid in the room became "illuminated" and
+ * all of the grids in the room were "memorized" forever. Other major
+ * bottlenecks involved the determination of whether a grid was lit by the
+ * player's torch, and whether a grid blocked the player's line of sight.
+ * These bottlenecks led to the development of special new functions to
+ * optimize issues involved with "line of sight" and "torch lit grids".
+ * These optimizations led to entirely new additions to the game, such as
+ * the ability to display the player's entire field of view using different
+ * colors than were used for the "memorized" portions of the dungeon, and
+ * the ability to memorize dark floor grids, but to indicate by the way in
+ * which they are displayed that they are not actually illuminated. And
+ * of course many of them simply made the game itself faster or more fun.
+ * Also, over time, the definition of "line of sight" has been relaxed to
+ * allow the player to see a wider "field of view", which is slightly more
+ * realistic, and only slightly more expensive to maintain.
+ *
+ * Currently, a lot of the information about the dungeon is stored in ways
+ * that make it very efficient to access or modify the information, while
+ * still attempting to be relatively conservative about memory usage, even
+ * if this means that some information is stored in multiple places, or in
+ * ways which require the use of special code idioms. For example, each
+ * monster record in the monster array contains the location of the monster,
+ * and each cave grid has an index into the monster array, or a zero if no
+ * monster is in the grid. This allows the monster code to efficiently see
+ * where the monster is located, while allowing the dungeon code to quickly
+ * determine not only if a monster is present in a given grid, but also to
+ * find out which monster. The extra space used to store the information
+ * twice is inconsequential compared to the speed increase.
+ *
+ * Some of the information about the dungeon is used by functions which can
+ * constitute the "critical efficiency path" of the game itself, and so the
+ * way in which they are stored and accessed has been optimized in order to
+ * optimize the game itself. For example, the "update_view()" function was
+ * originally created to speed up the game itself (when the player was not
+ * running), but then it took on extra responsibility as the provider of the
+ * new "special effects lighting code", and became one of the most important
+ * bottlenecks when the player was running. So many rounds of optimization
+ * were performed on both the function itself, and the data structures which
+ * it uses, resulting eventually in a function which not only made the game
+ * faster than before, but which was responsible for even more calculations
+ * (including the determination of which grids are "viewable" by the player,
+ * which grids are illuminated by the player's torch, and which grids can be
+ * "seen" in some way by the player), as well as for providing the guts of
+ * the special effects lighting code, and for the efficient redisplay of any
+ * grids whose visual representation may have changed.
+ *
+ * Several pieces of information about each cave grid are stored in various
+ * two dimensional arrays, with one unit of information for each grid in the
+ * dungeon. Some of these arrays have been intentionally expanded by a small
+ * factor to make the two dimensional array accesses faster by allowing the
+ * use of shifting instead of multiplication.
+ *
+ * Several pieces of information about each cave grid are stored in the
+ * "cave_info" array, which is a special two dimensional array of bytes,
+ * one for each cave grid, each containing eight separate "flags" which
+ * describe some property of the cave grid. These flags can be checked and
+ * modified extremely quickly, especially when special idioms are used to
+ * force the compiler to keep a local register pointing to the base of the
+ * array. Special location offset macros can be used to minimize the number
+ * of computations which must be performed at runtime. Note that using a
+ * byte for each flag set may be slightly more efficient than using a larger
+ * unit, so if another flag (or two) is needed later, and it must be fast,
+ * then the two existing flags which do not have to be fast should be moved
+ * out into some other data structure and the new flags should take their
+ * place. This may require a few minor changes in the savefile code.
+ *
+ * The "CAVE_ROOM" flag is saved in the savefile and is used to determine
+ * which grids are part of "rooms", and thus which grids are affected by
+ * "illumination" spells. This flag does not have to be very fast.
+ *
+ * The "CAVE_ICKY" flag is saved in the savefile and is used to determine
+ * which grids are part of "vaults", and thus which grids cannot serve as
+ * the destinations of player teleportation. This flag does not have to
+ * be very fast.
+ *
+ * The "CAVE_MARK" flag is saved in the savefile and is used to determine
+ * which grids have been "memorized" by the player. This flag is used by
+ * the "map_info()" function to determine if a grid should be displayed.
+ * This flag is used in a few other places to determine if the player can
+ * "know" about a given grid. This flag must be very fast.
+ *
+ * The "CAVE_GLOW" flag is saved in the savefile and is used to determine
+ * which grids are "permanently illuminated". This flag is used by the
+ * "update_view()" function to help determine which viewable flags may
+ * be "seen" by the player. This flag is used by the "map_info" function
+ * to determine if a grid is only lit by the player's torch. This flag
+ * has special semantics for wall grids (see "update_view()"). This flag
+ * must be very fast.
+ *
+ * The "CAVE_WALL" flag is used to determine which grids block the player's
+ * line of sight. This flag is used by the "update_view()" function to
+ * determine which grids block line of sight, and to help determine which
+ * grids can be "seen" by the player. This flag must be very fast.
+ *
+ * The "CAVE_VIEW" flag is used to determine which grids are currently in
+ * line of sight of the player. This flag is set by (and used by) the
+ * "update_view()" function. This flag is used by any code which needs to
+ * know if the player can "view" a given grid. This flag is used by the
+ * "map_info()" function for some optional special lighting effects. The
+ * "player_has_los_bold()" macro wraps an abstraction around this flag, but
+ * certain code idioms are much more efficient. This flag is used to check
+ * if a modification to a terrain feature might affect the player's field of
+ * view. This flag is used to see if certain monsters are "visible" to the
+ * player. This flag is used to allow any monster in the player's field of
+ * view to "sense" the presence of the player. This flag must be very fast.
+ *
+ * The "CAVE_SEEN" flag is used to determine which grids are currently in
+ * line of sight of the player and also illuminated in some way. This flag
+ * is set by the "update_view()" function, using computations based on the
+ * "CAVE_VIEW" and "CAVE_WALL" and "CAVE_GLOW" flags of various grids. This
+ * flag is used by any code which needs to know if the player can "see" a
+ * given grid. This flag is used by the "map_info()" function both to see
+ * if a given "boring" grid can be seen by the player, and for some optional
+ * special lighting effects. The "player_can_see_bold()" macro wraps an
+ * abstraction around this flag, but certain code idioms are much more
+ * efficient. This flag is used to see if certain monsters are "visible" to
+ * the player. This flag is never set for a grid unless "CAVE_VIEW" is also
+ * set for the grid. Whenever the "CAVE_WALL" or "CAVE_GLOW" flag changes
+ * for a grid which has the "CAVE_VIEW" flag set, the "CAVE_SEEN" flag must
+ * be recalculated. The simplest way to do this is to call "forget_view()"
+ * and "update_view()" whenever the "CAVE_WALL" or "CAVE_GLOW" flags change
+ * for a grid which has "CAVE_VIEW" set. This flag must be very fast.
+ *
+ * The "CAVE_TEMP" flag is used for a variety of temporary purposes. This
+ * flag is used to determine if the "CAVE_SEEN" flag for a grid has changed
+ * during the "update_view()" function. This flag is used to "spread" light
+ * or darkness through a room. This flag is used by the "monster flow code".
+ * This flag must always be cleared by any code which sets it, often, this
+ * can be optimized by the use of the special "temp_g", "temp_y", "temp_x"
+ * arrays (and the special "temp_n" global). This flag must be very fast.
+ *
+ * Note that the "CAVE_MARK" flag is used for many reasons, some of which
+ * are strictly for optimization purposes. The "CAVE_MARK" flag means that
+ * even if the player cannot "see" the grid, he "knows" about the terrain in
+ * that grid. This is used to "memorize" grids when they are first "seen" by
+ * the player, and to allow certain grids to be "detected" by certain magic.
+ * Note that most grids are always memorized when they are first "seen", but
+ * "boring" grids (floor grids) are only memorized if the "view_torch_grids"
+ * option is set, or if the "view_perma_grids" option is set, and the grid
+ * in question has the "CAVE_GLOW" flag set.
+ *
+ * Objects are "memorized" in a different way, using a special "marked" flag
+ * on the object itself, which is set when an object is observed or detected.
+ * This allows objects to be "memorized" independant of the terrain features.
+ *
+ * The "update_view()" function is an extremely important function. It is
+ * called only when the player moves, significant terrain changes, or the
+ * player's blindness or torch radius changes. Note that when the player
+ * is resting, or performing any repeated actions (like digging, disarming,
+ * farming, etc), there is no need to call the "update_view()" function, so
+ * even if it was not very efficient, this would really only matter when the
+ * player was "running" through the dungeon. It sets the "CAVE_VIEW" flag
+ * on every cave grid in the player's field of view, and maintains an array
+ * of all such grids in the global "view_g" array. It also checks the torch
+ * radius of the player, and sets the "CAVE_SEEN" flag for every grid which
+ * is in the "field of view" of the player and which is also "illuminated",
+ * either by the players torch (if any) or by any permanent light source.
+ * It could use and help maintain information about multiple light sources,
+ * which would be helpful in a multi-player version of Angband.
+ *
+ * The "update_view()" function maintains the special "view_g" array, which
+ * contains exactly those grids which have the "CAVE_VIEW" flag set. This
+ * array is used by "update_view()" to (only) memorize grids which become
+ * newly "seen", and to (only) redraw grids whose "seen" value changes, which
+ * allows the use of some interesting (and very efficient) "special lighting
+ * effects". In addition, this array could be used elsewhere to quickly scan
+ * through all the grids which are in the player's field of view.
+ *
+ * Note that the "update_view()" function allows, among other things, a room
+ * to be "partially" seen as the player approaches it, with a growing cone
+ * of floor appearing as the player gets closer to the door. Also, by not
+ * turning on the "memorize perma-lit grids" option, the player will only
+ * "see" those floor grids which are actually in line of sight. And best
+ * of all, you can now activate the special lighting effects to indicate
+ * which grids are actually in the player's field of view by using dimmer
+ * colors for grids which are not in the player's field of view, and/or to
+ * indicate which grids are illuminated only by the player's torch by using
+ * the color yellow for those grids.
+ *
+ * The old "update_view()" algorithm uses the special "CAVE_EASY" flag as a
+ * temporary internal flag to mark those grids which are not only in view,
+ * but which are also "easily" in line of sight of the player. This flag
+ * is actually just the "CAVE_SEEN" flag, and the "update_view()" function
+ * makes sure to clear it for all old "CAVE_SEEN" grids, and then use it in
+ * the algorithm as "CAVE_EASY", and then clear it for all "CAVE_EASY" grids,
+ * and then reset it as appropriate for all new "CAVE_SEEN" grids. This is
+ * kind of messy, but it works. The old algorithm may disappear eventually.
+ *
+ * The new "update_view()" algorithm uses a faster and more mathematically
+ * correct algorithm, assisted by a large machine generated static array, to
+ * determine the "CAVE_VIEW" and "CAVE_SEEN" flags simultaneously. See below.
+ *
+ * It seems as though slight modifications to the "update_view()" functions
+ * would allow us to determine "reverse" line-of-sight as well as "normal"
+ * line-of-sight", which would allow monsters to have a more "correct" way
+ * to determine if they can "see" the player, since right now, they "cheat"
+ * somewhat and assume that if the player has "line of sight" to them, then
+ * they can "pretend" that they have "line of sight" to the player. But if
+ * such a change was attempted, the monsters would actually start to exhibit
+ * some undesirable behavior, such as "freezing" near the entrances to long
+ * hallways containing the player, and code would have to be added to make
+ * the monsters move around even if the player was not detectable, and to
+ * "remember" where the player was last seen, to avoid looking stupid.
+ *
+ * Note that the "CAVE_GLOW" flag means that a grid is permanently lit in
+ * some way. However, for the player to "see" the grid, as determined by
+ * the "CAVE_SEEN" flag, the player must not be blind, the grid must have
+ * the "CAVE_VIEW" flag set, and if the grid is a "wall" grid, and it is
+ * not lit by the player's torch, then it must touch a grid which does not
+ * have the "CAVE_WALL" flag set, but which does have both the "CAVE_GLOW"
+ * and "CAVE_VIEW" flags set. This last part about wall grids is induced
+ * by the semantics of "CAVE_GLOW" as applied to wall grids, and checking
+ * the technical requirements can be very expensive, especially since the
+ * grid may be touching some "illegal" grids. Luckily, it is more or less
+ * correct to restrict the "touching" grids from the eight "possible" grids
+ * to the (at most) three grids which are touching the grid, and which are
+ * closer to the player than the grid itself, which eliminates more than
+ * half of the work, including all of the potentially "illegal" grids, if
+ * at most one of the three grids is a "diagonal" grid. In addition, in
+ * almost every situation, it is possible to ignore the "CAVE_VIEW" flag
+ * on these three "touching" grids, for a variety of technical reasons.
+ * Finally, note that in most situations, it is only necessary to check
+ * a single "touching" grid, in fact, the grid which is strictly closest
+ * to the player of all the touching grids, and in fact, it is normally
+ * only necessary to check the "CAVE_GLOW" flag of that grid, again, for
+ * various technical reasons. However, one of the situations which does
+ * not work with this last reduction is the very common one in which the
+ * player approaches an illuminated room from a dark hallway, in which the
+ * two wall grids which form the "entrance" to the room would not be marked
+ * as "CAVE_SEEN", since of the three "touching" grids nearer to the player
+ * than each wall grid, only the farthest of these grids is itself marked
+ * "CAVE_GLOW".
+ *
+ *
+ * Here are some pictures of the legal "light source" radius values, in
+ * which the numbers indicate the "order" in which the grids could have
+ * been calculated, if desired. Note that the code will work with larger
+ * radiuses, though currently yields such a radius, and the game would
+ * become slower in some situations if it did.
+ *
+ * Rad=0 Rad=1 Rad=2 Rad=3
+ * No-Lite Torch,etc Lantern Artifacts
+ *
+ * 333
+ * 333 43334
+ * 212 32123 3321233
+ * @ 1@1 31@13 331@133
+ * 212 32123 3321233
+ * 333 43334
+ * 333
+ *
+ *
+ * Here is an illustration of the two different "update_view()" algorithms,
+ * in which the grids marked "%" are pillars, and the grids marked "?" are
+ * not in line of sight of the player.
+ *
+ *
+ * Sample situation
+ *
+ * #####################
+ * ############.%.%.%.%#
+ * #...@..#####........#
+ * #............%.%.%.%#
+ * #......#####........#
+ * ############........#
+ * #####################
+ *
+ *
+ * New Algorithm Old Algorithm
+ *
+ * ########????????????? ########?????????????
+ * #...@..#????????????? #...@..#?????????????
+ * #...........????????? #.........???????????
+ * #......#####.....???? #......####??????????
+ * ########?????????...# ########?????????????
+ *
+ * ########????????????? ########?????????????
+ * #.@....#????????????? #.@....#?????????????
+ * #............%??????? #...........?????????
+ * #......#####........? #......#####?????????
+ * ########??????????..# ########?????????????
+ *
+ * ########????????????? ########?????%???????
+ * #......#####........# #......#####..???????
+ * #.@..........%??????? #.@..........%???????
+ * #......#####........# #......#####..???????
+ * ########????????????? ########?????????????
+ *
+ * ########??????????..# ########?????????????
+ * #......#####........? #......#####?????????
+ * #............%??????? #...........?????????
+ * #.@....#????????????? #.@....#?????????????
+ * ########????????????? ########?????????????
+ *
+ * ########?????????%??? ########?????????????
+ * #......#####.....???? #......####??????????
+ * #...........????????? #.........???????????
+ * #...@..#????????????? #...@..#?????????????
+ * ########????????????? ########?????????????
+ */
+
+
+
+
+/*
+ * Maximum number of grids in a single octant
+ */
+#define VINFO_MAX_GRIDS 161
+
+
+/*
+ * Maximum number of slopes in a single octant
+ */
+#define VINFO_MAX_SLOPES 126
+
+
+/*
+ * Mask of bits used in a single octant
+ */
+#define VINFO_BITS_3 0x3FFFFFFF
+#define VINFO_BITS_2 0xFFFFFFFF
+#define VINFO_BITS_1 0xFFFFFFFF
+#define VINFO_BITS_0 0xFFFFFFFF
+
+
+/*
+ * Forward declare
+ */
+typedef struct vinfo_type vinfo_type;
+
+
+/*
+ * The 'vinfo_type' structure
+ */
+struct vinfo_type
+{
+ s16b grid_y[8];
+ s16b grid_x[8];
+
+ u32b bits_3;
+ u32b bits_2;
+ u32b bits_1;
+ u32b bits_0;
+
+ vinfo_type *next_0;
+ vinfo_type *next_1;
+
+ byte y;
+ byte x;
+ byte d;
+ byte r;
+};
+
+
+
+/*
+ * The array of "vinfo" objects, initialized by "vinfo_init()"
+ */
+static vinfo_type vinfo[VINFO_MAX_GRIDS];
+
+
+
+
+/*
+ * Slope scale factor
+ */
+#define SCALE 100000L
+
+
+/*
+ * The actual slopes (for reference)
+ */
+
+/* Bit : Slope Grids */
+/* --- : ----- ----- */
+/* 0 : 2439 21 */
+/* 1 : 2564 21 */
+/* 2 : 2702 21 */
+/* 3 : 2857 21 */
+/* 4 : 3030 21 */
+/* 5 : 3225 21 */
+/* 6 : 3448 21 */
+/* 7 : 3703 21 */
+/* 8 : 4000 21 */
+/* 9 : 4347 21 */
+/* 10 : 4761 21 */
+/* 11 : 5263 21 */
+/* 12 : 5882 21 */
+/* 13 : 6666 21 */
+/* 14 : 7317 22 */
+/* 15 : 7692 20 */
+/* 16 : 8108 21 */
+/* 17 : 8571 21 */
+/* 18 : 9090 20 */
+/* 19 : 9677 21 */
+/* 20 : 10344 21 */
+/* 21 : 11111 20 */
+/* 22 : 12000 21 */
+/* 23 : 12820 22 */
+/* 24 : 13043 22 */
+/* 25 : 13513 22 */
+/* 26 : 14285 20 */
+/* 27 : 15151 22 */
+/* 28 : 15789 22 */
+/* 29 : 16129 22 */
+/* 30 : 17241 22 */
+/* 31 : 17647 22 */
+/* 32 : 17948 23 */
+/* 33 : 18518 22 */
+/* 34 : 18918 22 */
+/* 35 : 20000 19 */
+/* 36 : 21212 22 */
+/* 37 : 21739 22 */
+/* 38 : 22580 22 */
+/* 39 : 23076 22 */
+/* 40 : 23809 22 */
+/* 41 : 24137 22 */
+/* 42 : 24324 23 */
+/* 43 : 25714 23 */
+/* 44 : 25925 23 */
+/* 45 : 26315 23 */
+/* 46 : 27272 22 */
+/* 47 : 28000 23 */
+/* 48 : 29032 23 */
+/* 49 : 29411 23 */
+/* 50 : 29729 24 */
+/* 51 : 30434 23 */
+/* 52 : 31034 23 */
+/* 53 : 31428 23 */
+/* 54 : 33333 18 */
+/* 55 : 35483 23 */
+/* 56 : 36000 23 */
+/* 57 : 36842 23 */
+/* 58 : 37142 24 */
+/* 59 : 37931 24 */
+/* 60 : 38461 24 */
+/* 61 : 39130 24 */
+/* 62 : 39393 24 */
+/* 63 : 40740 24 */
+/* 64 : 41176 24 */
+/* 65 : 41935 24 */
+/* 66 : 42857 23 */
+/* 67 : 44000 24 */
+/* 68 : 44827 24 */
+/* 69 : 45454 23 */
+/* 70 : 46666 24 */
+/* 71 : 47368 24 */
+/* 72 : 47826 24 */
+/* 73 : 48148 24 */
+/* 74 : 48387 24 */
+/* 75 : 51515 25 */
+/* 76 : 51724 25 */
+/* 77 : 52000 25 */
+/* 78 : 52380 25 */
+/* 79 : 52941 25 */
+/* 80 : 53846 25 */
+/* 81 : 54838 25 */
+/* 82 : 55555 24 */
+/* 83 : 56521 25 */
+/* 84 : 57575 26 */
+/* 85 : 57894 25 */
+/* 86 : 58620 25 */
+/* 87 : 60000 23 */
+/* 88 : 61290 25 */
+/* 89 : 61904 25 */
+/* 90 : 62962 25 */
+/* 91 : 63636 25 */
+/* 92 : 64705 25 */
+/* 93 : 65217 25 */
+/* 94 : 65517 25 */
+/* 95 : 67741 26 */
+/* 96 : 68000 26 */
+/* 97 : 68421 26 */
+/* 98 : 69230 26 */
+/* 99 : 70370 26 */
+/* 100 : 71428 25 */
+/* 101 : 72413 26 */
+/* 102 : 73333 26 */
+/* 103 : 73913 26 */
+/* 104 : 74193 27 */
+/* 105 : 76000 26 */
+/* 106 : 76470 26 */
+/* 107 : 77777 25 */
+/* 108 : 78947 26 */
+/* 109 : 79310 26 */
+/* 110 : 80952 26 */
+/* 111 : 81818 26 */
+/* 112 : 82608 26 */
+/* 113 : 84000 26 */
+/* 114 : 84615 26 */
+/* 115 : 85185 26 */
+/* 116 : 86206 27 */
+/* 117 : 86666 27 */
+/* 118 : 88235 27 */
+/* 119 : 89473 27 */
+/* 120 : 90476 27 */
+/* 121 : 91304 27 */
+/* 122 : 92000 27 */
+/* 123 : 92592 27 */
+/* 124 : 93103 28 */
+/* 125 : 100000 13 */
+
+
+
+/*
+ * Forward declare
+ */
+typedef struct vinfo_hack vinfo_hack;
+
+
+/*
+ * Temporary data used by "vinfo_init()"
+ *
+ * - Number of grids
+ *
+ * - Number of slopes
+ *
+ * - Slope values
+ *
+ * - Slope range per grid
+ */
+struct vinfo_hack
+{
+
+ int num_slopes;
+
+ long slopes[VINFO_MAX_SLOPES];
+
+ long slopes_min[MAX_SIGHT + 1][MAX_SIGHT + 1];
+ long slopes_max[MAX_SIGHT + 1][MAX_SIGHT + 1];
+};
+
+
+
+/*
+ * Sorting hook -- comp function -- array of long's (see below)
+ *
+ * We use "u" to point to an array of long integers.
+ */
+static bool_ ang_sort_comp_hook_longs(vptr u, vptr v, int a, int b)
+{
+ long *x = (long*)(u);
+
+ return (x[a] <= x[b]);
+}
+
+
+/*
+ * Sorting hook -- comp function -- array of long's (see below)
+ *
+ * We use "u" to point to an array of long integers.
+ */
+static void ang_sort_swap_hook_longs(vptr u, vptr v, int a, int b)
+{
+ long *x = (long*)(u);
+
+ long temp;
+
+ /* Swap */
+ temp = x[a];
+ x[a] = x[b];
+ x[b] = temp;
+}
+
+
+
+/*
+ * Save a slope
+ */
+static void vinfo_init_aux(vinfo_hack *hack, int y, int x, long m)
+{
+ int i;
+
+ /* Handle "legal" slopes */
+ if ((m > 0) && (m <= SCALE))
+ {
+ /* Look for that slope */
+ for (i = 0; i < hack->num_slopes; i++)
+ {
+ if (hack->slopes[i] == m) break;
+ }
+
+ /* New slope */
+ if (i == hack->num_slopes)
+ {
+ /* Paranoia */
+ if (hack->num_slopes >= VINFO_MAX_SLOPES)
+ {
+ quit_fmt("Too many slopes (%d)!",
+ VINFO_MAX_SLOPES);
+ }
+
+ /* Save the slope, and advance */
+ hack->slopes[hack->num_slopes++] = m;
+ }
+ }
+
+ /* Track slope range */
+ if (hack->slopes_min[y][x] > m) hack->slopes_min[y][x] = m;
+ if (hack->slopes_max[y][x] < m) hack->slopes_max[y][x] = m;
+}
+
+
+
+/*
+ * Initialize the "vinfo" array
+ *
+ * Full Octagon (radius 20), Grids=1149
+ *
+ * Quadrant (south east), Grids=308, Slopes=251
+ *
+ * Octant (east then south), Grids=161, Slopes=126
+ *
+ * This function assumes that VINFO_MAX_GRIDS and VINFO_MAX_SLOPES
+ * have the correct values, which can be derived by setting them to
+ * a number which is too high, running this function, and using the
+ * error messages to obtain the correct values.
+ */
+errr vinfo_init(void)
+{
+ int i, y, x;
+
+ long m;
+
+ vinfo_hack *hack;
+
+ int num_grids = 0;
+
+ int queue_head = 0;
+ int queue_tail = 0;
+ vinfo_type *queue[VINFO_MAX_GRIDS*2];
+
+
+ /* Make hack */
+ MAKE(hack, vinfo_hack);
+
+
+ /* Analyze grids */
+ for (y = 0; y <= MAX_SIGHT; ++y)
+ {
+ for (x = y; x <= MAX_SIGHT; ++x)
+ {
+ /* Skip grids which are out of sight range */
+ if (distance(0, 0, y, x) > MAX_SIGHT) continue;
+
+ /* Default slope range */
+ hack->slopes_min[y][x] = 999999999;
+ hack->slopes_max[y][x] = 0;
+
+ /* Paranoia */
+ if (num_grids >= VINFO_MAX_GRIDS)
+ {
+ quit_fmt("Too many grids (%d >= %d)!",
+ num_grids, VINFO_MAX_GRIDS);
+ }
+
+ /* Count grids */
+ num_grids++;
+
+ /* Slope to the top right corner */
+ m = SCALE * (1000L * y - 500) / (1000L * x + 500);
+
+ /* Handle "legal" slopes */
+ vinfo_init_aux(hack, y, x, m);
+
+ /* Slope to top left corner */
+ m = SCALE * (1000L * y - 500) / (1000L * x - 500);
+
+ /* Handle "legal" slopes */
+ vinfo_init_aux(hack, y, x, m);
+
+ /* Slope to bottom right corner */
+ m = SCALE * (1000L * y + 500) / (1000L * x + 500);
+
+ /* Handle "legal" slopes */
+ vinfo_init_aux(hack, y, x, m);
+
+ /* Slope to bottom left corner */
+ m = SCALE * (1000L * y + 500) / (1000L * x - 500);
+
+ /* Handle "legal" slopes */
+ vinfo_init_aux(hack, y, x, m);
+ }
+ }
+
+
+ /* Enforce maximal efficiency */
+ if (num_grids < VINFO_MAX_GRIDS)
+ {
+ quit_fmt("Too few grids (%d < %d)!",
+ num_grids, VINFO_MAX_GRIDS);
+ }
+
+ /* Enforce maximal efficiency */
+ if (hack->num_slopes < VINFO_MAX_SLOPES)
+ {
+ quit_fmt("Too few slopes (%d < %d)!",
+ hack->num_slopes, VINFO_MAX_SLOPES);
+ }
+
+
+ /* Sort slopes numerically */
+ ang_sort_comp = ang_sort_comp_hook_longs;
+
+ /* Sort slopes numerically */
+ ang_sort_swap = ang_sort_swap_hook_longs;
+
+ /* Sort the (unique) slopes */
+ ang_sort(hack->slopes, NULL, hack->num_slopes);
+
+
+
+ /* Enqueue player grid */
+ queue[queue_tail++] = &vinfo[0];
+
+ /* Process queue */
+ while (queue_head < queue_tail)
+ {
+ int e;
+
+ /* Index */
+ e = queue_head;
+
+ /* Dequeue next grid */
+ queue_head++;
+
+ /* Location of main grid */
+ y = vinfo[e].grid_y[0];
+ x = vinfo[e].grid_x[0];
+
+
+ /* Compute grid offsets */
+ vinfo[e].grid_y[0] = + y;
+ vinfo[e].grid_x[0] = + x;
+ vinfo[e].grid_y[1] = + x;
+ vinfo[e].grid_x[1] = + y;
+ vinfo[e].grid_y[2] = + x;
+ vinfo[e].grid_x[2] = -y;
+ vinfo[e].grid_y[3] = + y;
+ vinfo[e].grid_x[3] = -x;
+ vinfo[e].grid_y[4] = -y;
+ vinfo[e].grid_x[4] = -x;
+ vinfo[e].grid_y[5] = -x;
+ vinfo[e].grid_x[5] = -y;
+ vinfo[e].grid_y[6] = -x;
+ vinfo[e].grid_x[6] = + y;
+ vinfo[e].grid_y[7] = -y;
+ vinfo[e].grid_x[7] = + x;
+
+
+ /* Analyze slopes */
+ for (i = 0; i < hack->num_slopes; ++i)
+ {
+ m = hack->slopes[i];
+
+ /* Memorize intersection slopes (for non-player-grids) */
+ if ((e > 0) &&
+ (hack->slopes_min[y][x] < m) &&
+ (m < hack->slopes_max[y][x]))
+ {
+ switch (i / 32)
+ {
+ case 3:
+ vinfo[e].bits_3 |= (1L << (i % 32));
+ break;
+ case 2:
+ vinfo[e].bits_2 |= (1L << (i % 32));
+ break;
+ case 1:
+ vinfo[e].bits_1 |= (1L << (i % 32));
+ break;
+ case 0:
+ vinfo[e].bits_0 |= (1L << (i % 32));
+ break;
+ }
+ }
+ }
+
+
+ /* Default */
+ vinfo[e].next_0 = &vinfo[0];
+
+ /* Grid next child */
+ if (distance(0, 0, y, x + 1) <= MAX_SIGHT)
+ {
+ if ((queue[queue_tail - 1]->grid_y[0] != y) ||
+ (queue[queue_tail - 1]->grid_x[0] != x + 1))
+ {
+ vinfo[queue_tail].grid_y[0] = y;
+ vinfo[queue_tail].grid_x[0] = x + 1;
+ queue[queue_tail] = &vinfo[queue_tail];
+ queue_tail++;
+ }
+
+ vinfo[e].next_0 = &vinfo[queue_tail - 1];
+ }
+
+
+ /* Default */
+ vinfo[e].next_1 = &vinfo[0];
+
+ /* Grid diag child */
+ if (distance(0, 0, y + 1, x + 1) <= MAX_SIGHT)
+ {
+ if ((queue[queue_tail - 1]->grid_y[0] != y + 1) ||
+ (queue[queue_tail - 1]->grid_x[0] != x + 1))
+ {
+ vinfo[queue_tail].grid_y[0] = y + 1;
+ vinfo[queue_tail].grid_x[0] = x + 1;
+ queue[queue_tail] = &vinfo[queue_tail];
+ queue_tail++;
+ }
+
+ vinfo[e].next_1 = &vinfo[queue_tail - 1];
+ }
+
+
+ /* Hack -- main diagonal has special children */
+ if (y == x) vinfo[e].next_0 = vinfo[e].next_1;
+
+
+ /* Extra values */
+ vinfo[e].y = y;
+ vinfo[e].x = x;
+ vinfo[e].d = ((y > x) ? (y + x / 2) : (x + y / 2));
+ vinfo[e].r = ((!y) ? x : (!x) ? y : (y == x) ? y : 0);
+ }
+
+
+ /* Verify maximal bits XXX XXX XXX */
+ if (((vinfo[1].bits_3 | vinfo[2].bits_3) != VINFO_BITS_3) ||
+ ((vinfo[1].bits_2 | vinfo[2].bits_2) != VINFO_BITS_2) ||
+ ((vinfo[1].bits_1 | vinfo[2].bits_1) != VINFO_BITS_1) ||
+ ((vinfo[1].bits_0 | vinfo[2].bits_0) != VINFO_BITS_0))
+ {
+ quit("Incorrect bit masks!");
+ }
+
+
+ /* Kill hack */
+ KILL(hack, vinfo_hack);
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Forget the "CAVE_VIEW" grids, redrawing as needed
+ */
+void forget_view(void)
+{
+ int i;
+
+ int fast_view_n = view_n;
+
+ cave_type *c_ptr;
+
+
+ /* None to forget */
+ if (!fast_view_n) return;
+
+ /* Clear them all */
+ for (i = 0; i < fast_view_n; i++)
+ {
+ int y = view_y[i];
+ int x = view_x[i];
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear "CAVE_VIEW", "CAVE_SEEN" and player torch flags */
+ c_ptr->info &= ~(CAVE_VIEW | CAVE_SEEN | CAVE_PLIT);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+
+ /* None left */
+ view_n = 0;
+}
+
+
+
+/*
+ * Calculate the complete field of view using a new algorithm
+ *
+ * If "view_y/x" and "temp_y/x" were global pointers to arrays of grids, as
+ * opposed to actual arrays of grids, then we could be more efficient by
+ * using "pointer swapping".
+ *
+ * Normally, vision along the major axes is more likely than vision
+ * along the diagonal axes, so we check the bits corresponding to
+ * the lines of sight near the major axes first.
+ *
+ * We use the "temp_y/x" array (and the "CAVE_TEMP" flag) to keep track of
+ * which grids were previously marked "CAVE_SEEN", since only those grids
+ * whose "CAVE_SEEN" value changes during this routine must be redrawn.
+ *
+ * This function is now responsible for maintaining the "CAVE_SEEN"
+ * flags as well as the "CAVE_VIEW" flags, which is good, because
+ * the only grids which normally need to be memorized and/or redrawn
+ * are the ones whose "CAVE_SEEN" flag changes during this routine.
+ *
+ * Basically, this function divides the "octagon of view" into octants of
+ * grids (where grids on the main axes and diagonal axes are "shared" by
+ * two octants), and processes each octant one at a time, processing each
+ * octant one grid at a time, processing only those grids which "might" be
+ * viewable, and setting the "CAVE_VIEW" flag for each grid for which there
+ * is an (unobstructed) line of sight from the center of the player grid to
+ * any internal point in the grid (and collecting these "CAVE_VIEW" grids
+ * into the "view_y/x" array), and setting the "CAVE_SEEN" flag for the grid
+ * if, in addition, the grid is "illuminated" in some way.
+ *
+ * This function relies on a theorem (suggested and proven by Mat Hostetter)
+ * which states that in each octant of a field of view, a given grid will
+ * be "intersected" by one or more unobstructed "lines of sight" from the
+ * center of the player grid if and only if it is "intersected" by at least
+ * one such unobstructed "line of sight" which passes directly through some
+ * corner of some grid in the octant which is not shared by any other octant.
+ * The proof is based on the fact that there are at least three significant
+ * lines of sight involving any non-shared grid in any octant, one which
+ * intersects the grid and passes though the corner of the grid closest to
+ * the player, and two which "brush" the grid, passing through the "outer"
+ * corners of the grid, and that any line of sight which intersects a grid
+ * without passing through the corner of a grid in the octant can be "slid"
+ * slowly towards the corner of the grid closest to the player, until it
+ * either reaches it or until it brushes the corner of another grid which
+ * is closer to the player, and in either case, the existanc of a suitable
+ * line of sight is thus demonstrated.
+ *
+ * It turns out that in each octant of the radius 20 "octagon of view",
+ * there are 161 grids (with 128 not shared by any other octant), and there
+ * are exactly 126 distinct "lines of sight" passing from the center of the
+ * player grid through any corner of any non-shared grid in the octant. To
+ * determine if a grid is "viewable" by the player, therefore, you need to
+ * simply show that one of these 126 lines of sight intersects the grid but
+ * does not intersect any wall grid closer to the player. So we simply use
+ * a bit vector with 126 bits to represent the set of interesting lines of
+ * sight which have not yet been obstructed by wall grids, and then we scan
+ * all the grids in the octant, moving outwards from the player grid. For
+ * each grid, if any of the lines of sight which intersect that grid have not
+ * yet been obstructed, then the grid is viewable. Furthermore, if the grid
+ * is a wall grid, then all of the lines of sight which intersect the grid
+ * should be marked as obstructed for future reference. Also, we only need
+ * to check those grids for whom at least one of the "parents" was a viewable
+ * non-wall grid, where the parents include the two grids touching the grid
+ * but closer to the player grid (one adjacent, and one diagonal). For the
+ * bit vector, we simply use 4 32-bit integers. All of the static values
+ * which are needed by this function are stored in the large "vinfo" array
+ * (above), which is machine generated by another program. XXX XXX XXX
+ *
+ * Hack -- The queue must be able to hold more than VINFO_MAX_GRIDS grids
+ * because the grids at the edge of the field of view use "grid zero" as
+ * their children, and the queue must be able to hold several of these
+ * special grids. Because the actual number of required grids is bizarre,
+ * we simply allocate twice as many as we would normally need. XXX XXX XXX
+ */
+void update_view(void)
+{
+ int i, o;
+ int y, x;
+
+ int radius;
+
+ int fast_view_n = view_n;
+
+ int fast_temp_n = 0;
+
+ cave_type *c_ptr;
+
+ u16b info;
+
+
+ /*** Step 0 -- Begin ***/
+
+ /* Save the old "view" grids for later */
+ for (i = 0; i < fast_view_n; i++)
+ {
+ /* Location */
+ y = view_y[i];
+ x = view_x[i];
+
+ /* Grid */
+ c_ptr = &cave[y][x];
+
+ /* Get grid info */
+ info = c_ptr->info;
+ ;
+
+ /* Save "CAVE_SEEN" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Set "CAVE_TEMP" flag */
+ info |= (CAVE_TEMP);
+
+ /* Save grid for later */
+ temp_y[fast_temp_n] = y;
+ temp_x[fast_temp_n++] = x;
+ }
+
+ /* Clear "CAVE_VIEW", "CAVE_SEEN" and player torch flags */
+ info &= ~(CAVE_VIEW | CAVE_SEEN | CAVE_PLIT);
+
+ /* Save cave info */
+ c_ptr->info = info;
+ }
+
+ /* Reset the "view" array */
+ fast_view_n = 0;
+
+ /* Extract "radius" value */
+ radius = p_ptr->cur_lite;
+
+ /* Handle real light */
+ if (radius > 0) ++radius;
+
+
+ /*** Step 1 -- player grid ***/
+
+ /* Player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ /* Get grid info */
+ info = c_ptr->info;
+
+ /* Assume viewable */
+ info |= (CAVE_VIEW);
+
+ /* Torch-lit grid */
+ if (0 < radius)
+ {
+ /* Mark as "CAVE_SEEN" and torch-lit */
+ info |= (CAVE_SEEN | CAVE_PLIT);
+ }
+
+
+ /* Perma-lit grid */
+ else if (info & (CAVE_GLOW))
+ {
+ /* Mark as "CAVE_SEEN" */
+ info |= (CAVE_SEEN);
+ }
+
+ /* Save cave info */
+ c_ptr->info = info;
+
+ /* Save in array */
+ view_y[fast_view_n] = p_ptr->py;
+ view_x[fast_view_n++] = p_ptr->px;
+
+
+ /*** Step 2 -- octants ***/
+
+ /* Scan each octant */
+ for (o = 0; o < 8; o++)
+ {
+ vinfo_type *p;
+
+ /* Last added */
+ vinfo_type *last = &vinfo[0];
+
+ /* Grid queue */
+ int queue_head = 0;
+ int queue_tail = 0;
+ vinfo_type *queue[VINFO_MAX_GRIDS*2];
+
+ /* Slope bit vector */
+ u32b bits0 = VINFO_BITS_0;
+ u32b bits1 = VINFO_BITS_1;
+ u32b bits2 = VINFO_BITS_2;
+ u32b bits3 = VINFO_BITS_3;
+
+ /* Reset queue */
+ queue_head = queue_tail = 0;
+
+ /* Initial grids */
+ queue[queue_tail++] = &vinfo[1];
+ queue[queue_tail++] = &vinfo[2];
+
+ /* Process queue */
+ while (queue_head < queue_tail)
+ {
+ /* Dequeue next grid */
+ p = queue[queue_head++];
+
+ /* Check bits */
+ if ((bits0 & (p->bits_0)) ||
+ (bits1 & (p->bits_1)) ||
+ (bits2 & (p->bits_2)) ||
+ (bits3 & (p->bits_3)))
+ {
+ /* Extract coordinate value */
+ y = p_ptr->py + p->grid_y[o];
+ x = p_ptr->px + p->grid_x[o];
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Get grid info */
+ info = c_ptr->info;
+
+ /* Handle wall */
+ if (info & (CAVE_WALL))
+ {
+ /* Clear bits */
+ bits0 &= ~(p->bits_0);
+ bits1 &= ~(p->bits_1);
+ bits2 &= ~(p->bits_2);
+ bits3 &= ~(p->bits_3);
+
+ /* Newly viewable wall */
+ if (!(info & (CAVE_VIEW)))
+ {
+ /* Mark as viewable */
+ info |= (CAVE_VIEW);
+
+ /* Torch-lit grids */
+ if (p->d < radius)
+ {
+ /* Mark as "CAVE_SEEN" and torch-lit */
+ info |= (CAVE_SEEN | CAVE_PLIT);
+ }
+
+ /* Monster-lit grids */
+ else if (info & (CAVE_MLIT))
+ {
+ /* Mark as "CAVE_SEEN" */
+ info |= (CAVE_SEEN);
+ }
+
+ /* Perma-lit grids */
+ else if (info & (CAVE_GLOW))
+ {
+ /* Hack -- move towards player */
+ int yy = (y < p_ptr->py) ? (y + 1) : (y > p_ptr->py) ? (y - 1) : y;
+ int xx = (x < p_ptr->px) ? (x + 1) : (x > p_ptr->px) ? (x - 1) : x;
+
+#ifdef UPDATE_VIEW_COMPLEX_WALL_ILLUMINATION
+
+ /* Check for "complex" illumination */
+ if ((!(cave[yy][xx].info & (CAVE_WALL)) &&
+ (cave[yy][xx].info & (CAVE_GLOW))) ||
+ (!(cave[y][xx].info & (CAVE_WALL)) &&
+ (cave[y][xx].info & (CAVE_GLOW))) ||
+ (!(cave[yy][x].info & (CAVE_WALL)) &&
+ (cave[yy][x].info & (CAVE_GLOW))))
+ {
+ /* Mark as seen */
+ info |= (CAVE_SEEN);
+ }
+
+#else /* UPDATE_VIEW_COMPLEX_WALL_ILLUMINATION */
+
+ /* Check for "simple" illumination */
+ if (cave[yy][xx].info & (CAVE_GLOW))
+ {
+ /* Mark as seen */
+ info |= (CAVE_SEEN);
+ }
+
+#endif /* UPDATE_VIEW_COMPLEX_WALL_ILLUMINATION */
+
+ }
+
+ /* Save cave info */
+ c_ptr->info = info;
+
+ /* Save in array */
+ view_y[fast_view_n] = y;
+ view_x[fast_view_n++] = x;
+ }
+ }
+
+ /* Handle non-wall */
+ else
+ {
+ /* Enqueue child */
+ if (last != p->next_0)
+ {
+ queue[queue_tail++] = last = p->next_0;
+ }
+
+ /* Enqueue child */
+ if (last != p->next_1)
+ {
+ queue[queue_tail++] = last = p->next_1;
+ }
+
+ /* Newly viewable non-wall */
+ if (!(info & (CAVE_VIEW)))
+ {
+ /* Mark as "viewable" */
+ info |= (CAVE_VIEW);
+
+ /* Torch-lit grids */
+ if (p->d < radius)
+ {
+ /* Mark as "CAVE_SEEN" and torch-lit */
+ info |= (CAVE_SEEN | CAVE_PLIT);
+ }
+
+ /* Perma-lit or monster-lit grids */
+ else if (info & (CAVE_GLOW | CAVE_MLIT))
+ {
+ /* Mark as "CAVE_SEEN" */
+ info |= (CAVE_SEEN);
+ }
+
+ /* Save cave info */
+ c_ptr->info = info;
+
+ /* Save in array */
+ view_y[fast_view_n] = y;
+ view_x[fast_view_n++] = x;
+ }
+ }
+ }
+ }
+ }
+
+
+ /*** Step 3 -- Complete the algorithm ***/
+
+ /* Handle blindness */
+ if (p_ptr->blind)
+ {
+ /* Process "new" grids */
+ for (i = 0; i < fast_view_n; i++)
+ {
+ /* Location */
+ y = view_y[i];
+ x = view_x[i];
+
+ /* Grid cannot be "CAVE_SEEN" */
+ cave[y][x].info &= ~(CAVE_SEEN);
+ }
+ }
+
+ /* Process "new" grids */
+ for (i = 0; i < fast_view_n; i++)
+ {
+ /* Location */
+ y = view_y[i];
+ x = view_x[i];
+
+ /* Get grid info */
+ info = cave[y][x].info;
+
+ /* Was not "CAVE_SEEN", is now "CAVE_SEEN" */
+ if ((info & (CAVE_SEEN)) && !(info & (CAVE_TEMP)))
+ {
+ /* Note */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+
+ /* Process "old" grids */
+ for (i = 0; i < fast_temp_n; i++)
+ {
+ /* Location */
+ y = temp_y[i];
+ x = temp_x[i];
+
+ /* Grid */
+ c_ptr = &cave[y][x];
+
+ /* Get grid info */
+ info = c_ptr->info;
+
+ /* Clear "CAVE_TEMP" flag */
+ info &= ~(CAVE_TEMP);
+
+ /* Save cave info */
+ c_ptr->info = info;
+
+ /* Was "CAVE_SEEN", is now not "CAVE_SEEN" */
+ if (!(info & (CAVE_SEEN)))
+ {
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+
+
+ /* Save 'view_n' */
+ view_n = fast_view_n;
+}
+
+
+/*
+ * Clear monster light
+ */
+void forget_mon_lite(void)
+{
+ int i, y, x;
+
+ /* Process all the monster-lit grids */
+ for (i = 0; i < lite_n; i++)
+ {
+ /* Access location */
+ y = lite_y[i];
+ x = lite_x[i];
+
+ /* Clear monster light flag */
+ cave[y][x].info &= ~(CAVE_MLIT);
+ }
+
+ /* Forget light array */
+ lite_n = 0;
+}
+
+
+/*
+ * Update squares illuminated by monsters
+ *
+ * Code taken from Steven Fuerst's work for ZAngband, without support
+ * for multiple lite radii, and with necessary modifications for different
+ * internal representation of dungeon/wilderness. Other minor changes
+ * are mine...
+ *
+ * I'm not sure if I can handle wide radius well. Consider the following
+ * example, with p carrying a radius 3 light source:
+ *
+ * ##%#
+ * .x..
+ * p##@
+ *
+ * % should be illuminated, although the beam path is entirely out of
+ * player's los (because of grid-based nature of cave representation)...
+ * And I'm extremely reluctant to introduce symmetrical los. The current
+ * asymmetrical system has its own merit, and all the rules of games are
+ * asymmetrical, in some way or another...
+ *
+ * The code below exploits special characteristics of radius one light
+ * where one can fairly safely use light source's visibility (in terms of los)
+ * to determine if we can illuminate walls XXX
+ *
+ * This function works within the current player's field of view
+ * calculated by update_view(), so it should normally be called
+ * whenever FoV is updated (== PU_VIEW | PU_MON_LITE). The other
+ * case is when RF9_HAS_LITE monsters have moved or dead. Monster
+ * creation occurs out of LoS, so I chose not to take this into
+ * consideration.
+ *
+ * The CAVE_TEMP flag is used by the function to remember "old" monster-lit
+ * grids so that it can only redraw squares whose visibility has changed.
+ *
+ * Doing this in the update_view() order (update "new" grids, then "old")
+ * would result in bizarre lighting effects XXX XXX
+ *
+ * It has been made possible again to draw torch/monster-lit grids in
+ * different colours, even when they are in permanently lit locations
+ * by using (CAVE_PLIT|CAVE_MLIT) as if it were old CAVE_LITE, but I don't
+ * think it's appropriate for torch lights to be visible under the Sun :)
+ * or brighter light, and it doesn't work well with PernAngband's already
+ * colourful terrain features in aesthetically pleasing ways... -- pelpel
+ */
+void update_mon_lite(void)
+{
+ int i, y, x, d;
+ int fy, fx;
+
+ cave_type *c_ptr;
+ u16b info;
+
+ bool_ invis;
+
+ s16b fast_lite_n = lite_n;
+ s16b fast_temp_n;
+
+
+ /* Mega-Hack -- It's unnecessary there */
+ if (p_ptr->wild_mode) return;
+
+ /* Handle special case -- Blindness */
+ if (p_ptr->blind)
+ {
+ for (i = 0; i < fast_lite_n; i++)
+ {
+ /* Light location */
+ y = lite_y[i];
+ x = lite_x[i];
+
+ /* Forget monster light and view */
+ cave[y][x].info &= ~(CAVE_MLIT | CAVE_SEEN);
+
+ /* Redraw spot */
+ /* lite_spot(y, x); */
+ }
+
+ /* Clear the light list */
+ lite_n = 0;
+
+ /* Done */
+ return;
+ }
+
+
+ /* Remember and clear all monster-lit grids */
+ for (i = 0; i < fast_lite_n; i++)
+ {
+ /* Lit location */
+ y = lite_y[i];
+ x = lite_x[i];
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+ /* Access cave info of the grid */
+ info = c_ptr->info;
+
+ /* Remember it, by setting the CAVE_TEMP flag */
+ info |= (CAVE_TEMP);
+
+ /* Forget monster light */
+ info &= ~(CAVE_MLIT);
+
+ /* Unseen unless it's glowing or illuminated by player light source */
+ if (!(info & (CAVE_GLOW | CAVE_PLIT)))
+ {
+ info &= ~(CAVE_SEEN);
+ }
+
+ /* Save cave info flags */
+ c_ptr->info = info;
+ }
+
+
+ /* Clear the temp list */
+ fast_temp_n = 0;
+
+ /* Loop through monsters, adding newly lit grids to changes list */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr;
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Skip out-of-sight monsters (MAX_SIGHT + max radius) */
+ if (m_ptr->cdis > MAX_SIGHT + 1) continue;
+
+ /* Access monster race info (with possible ego mods) */
+ r_ptr = race_info_idx(m_ptr->r_idx, m_ptr->ego);
+
+ /* Skip monsters not carrying light source */
+ if (!(r_ptr->flags9 & RF9_HAS_LITE)) continue;
+
+ /* Access the location */
+ fy = m_ptr->fy;
+ fx = m_ptr->fx;
+
+ /* Extract monster grid visibility */
+ invis = !player_has_los_bold(fy, fx);
+
+ /* Nested loops may be a bad idea here XXX */
+ for (d = 0; d < 9; d++)
+ {
+ y = fy + ddy_ddd[d];
+ x = fx + ddx_ddd[d];
+
+ /* Paranoia */
+ /* if (!in_bounds(y, x)) continue; */
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Access cave info flags */
+ info = c_ptr->info;
+
+ /* Don't care grids out of player's los */
+ if (!(info & (CAVE_VIEW))) continue;
+
+ /*
+ * Avoid processing already monster-lit grids,
+ * for efficiency and to avoid temp array overflow
+ */
+ if (info & (CAVE_MLIT)) continue;
+
+ /*
+ * Hack XXX XXX -- light shouldn't penetrate walls
+ *
+ * OK NG
+ * .#. p#. | p. .p. p..
+ * p.@ ..@ | .# .#. .#.
+ * | .@ .@. ..@
+ *
+ * So if a monster carrying light source is out of player LoS,
+ * walls aren't illuminated.
+ *
+ * CAVEAT: % will be illuminated in cases like this:
+ *
+ * #%..@
+ * p....
+ *
+ * We don't have four sides for a wall grid, so...
+ */
+ if (invis && (f_info[c_ptr->feat].flags1 & FF1_NO_VISION)) continue;
+
+ /* Give monster light to the location */
+ c_ptr->info |= (CAVE_MLIT | CAVE_SEEN);
+
+ /* Save the location */
+ temp_y[fast_temp_n] = y;
+ temp_x[fast_temp_n] = x;
+ fast_temp_n++;
+ }
+ }
+
+ /* Process old grids */
+ for (i = 0; i < fast_lite_n; i++)
+ {
+ /* Access location */
+ y = lite_y[i];
+ x = lite_x[i];
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+ /* Was lit, is no longer lit */
+ if (!(c_ptr->info & (CAVE_MLIT)))
+ {
+ /* Clear the temp flag */
+ c_ptr->info &= ~(CAVE_TEMP);
+
+ /* See if there was a visible monster */
+ if (player_has_los_bold(y, x) && c_ptr->m_idx)
+ {
+ /* Hide the monster */
+ update_mon(c_ptr->m_idx, FALSE);
+ }
+ else
+ {
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+ }
+
+ /* Copy the temp array into the light array */
+ for (i = 0; i < fast_temp_n; i++)
+ {
+ /* Access location */
+ y = temp_y[i];
+ x = temp_x[i];
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+
+ /* No changes in illumination */
+ if (c_ptr->info & (CAVE_TEMP))
+ {
+ /* Clear the temp flag */
+ c_ptr->info &= ~(CAVE_TEMP);
+ }
+
+ /* Was not lit, is now lit */
+ else
+ {
+ /* Remember the location, if appropriate */
+ note_spot(y, x);
+
+ /* See if there is a monster */
+ if (c_ptr->m_idx)
+ {
+ /* Show it */
+ update_mon(c_ptr->m_idx, FALSE);
+ }
+ else
+ {
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+
+
+ /* Save the location */
+ lite_y[i] = y;
+ lite_x[i] = x;
+ }
+
+ /* Save lite_n */
+ lite_n = fast_temp_n;
+
+ /* Forget temp */
+ temp_n = 0;
+}
+
+
+
+
+
+
+/*
+ * Hack -- provide some "speed" for the "flow" code
+ * This entry is the "current index" for the "when" field
+ * Note that a "when" value of "zero" means "not used".
+ *
+ * Note that the "cost" indexes from 1 to 127 are for
+ * "old" data, and from 128 to 255 are for "new" data.
+ *
+ * This means that as long as the player does not "teleport",
+ * then any monster up to 128 + MONSTER_FLOW_DEPTH will be
+ * able to track down the player, and in general, will be
+ * able to track down either the player or a position recently
+ * occupied by the player.
+ */
+static int flow_n = 0;
+
+
+/*
+ * Hack -- forget the "flow" information
+ */
+void forget_flow(void)
+{
+ int x, y;
+
+ /* Nothing to forget */
+ if (!flow_n) return;
+
+ /* Check the entire dungeon */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Forget the old data */
+ cave[y][x].cost = 0;
+ cave[y][x].when = 0;
+ }
+ }
+
+ /* Start over */
+ flow_n = 0;
+}
+
+
+/*
+ * Hack -- Allow us to treat the "seen" array as a queue
+ */
+static int flow_head = 0;
+static int flow_tail = 0;
+
+
+/*
+ * Take note of a reachable grid. Assume grid is legal.
+ */
+static void update_flow_aux(int y, int x, int n)
+{
+ cave_type *c_ptr;
+
+ int old_head = flow_head;
+
+
+ /* Get the grid */
+ c_ptr = &cave[y][x];
+
+ /* Ignore "pre-stamped" entries */
+ if (c_ptr->when == flow_n) return;
+
+ /* Ignore "walls" and "rubble" */
+ if (c_ptr->feat >= FEAT_RUBBLE) return;
+
+ /* Save the time-stamp */
+ c_ptr->when = flow_n;
+
+ /* Save the flow cost */
+ c_ptr->cost = n;
+
+ /* Hack -- limit flow depth */
+ if (n == MONSTER_FLOW_DEPTH) return;
+
+ /* Enqueue that entry */
+ temp_y[flow_head] = y;
+ temp_x[flow_head] = x;
+
+ /* Advance the queue */
+ if (++flow_head == TEMP_MAX) flow_head = 0;
+
+ /* Hack -- notice overflow by forgetting new entry */
+ if (flow_head == flow_tail) flow_head = old_head;
+}
+
+
+/*
+ * Hack -- fill in the "cost" field of every grid that the player
+ * can "reach" with the number of steps needed to reach that grid.
+ * This also yields the "distance" of the player from every grid.
+ *
+ * In addition, mark the "when" of the grids that can reach
+ * the player with the incremented value of "flow_n".
+ *
+ * Hack -- use the "seen" array as a "circular queue".
+ *
+ * We do not need a priority queue because the cost from grid
+ * to grid is always "one" and we process them in order.
+ */
+void update_flow(void)
+{
+ int x, y, d;
+
+ /* Hack -- disabled */
+ if (!flow_by_sound) return;
+
+ /* Paranoia -- make sure the array is empty */
+ if (temp_n) return;
+
+ /* Cycle the old entries (once per 128 updates) */
+ if (flow_n == 255)
+ {
+ /* Rotate the time-stamps */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ int w = cave[y][x].when;
+ cave[y][x].when = (w > 128) ? (w - 128) : 0;
+ }
+ }
+
+ /* Restart */
+ flow_n = 127;
+ }
+
+ /* Start a new flow (never use "zero") */
+ flow_n++;
+
+
+ /* Reset the "queue" */
+ flow_head = flow_tail = 0;
+
+ /* Add the player's grid to the queue */
+ update_flow_aux(p_ptr->py, p_ptr->px, 0);
+
+ /* Now process the queue */
+ while (flow_head != flow_tail)
+ {
+ /* Extract the next entry */
+ y = temp_y[flow_tail];
+ x = temp_x[flow_tail];
+
+ /* Forget that entry */
+ if (++flow_tail == TEMP_MAX) flow_tail = 0;
+
+ /* Add the "children" */
+ for (d = 0; d < 8; d++)
+ {
+ /* Add that child if "legal" */
+ update_flow_aux(y + ddy_ddd[d], x + ddx_ddd[d], cave[y][x].cost + 1);
+ }
+ }
+
+ /* Forget the flow info */
+ flow_head = flow_tail = 0;
+}
+
+
+
+
+
+
+
+/*
+ * Hack -- map the current panel (plus some) ala "magic mapping"
+ */
+void map_area(void)
+{
+ int i, x, y, y1, y2, x1, x2;
+
+ cave_type *c_ptr;
+
+
+ /* Pick an area to map */
+ y1 = panel_row_min - randint(10);
+ y2 = panel_row_max + randint(10);
+ x1 = panel_col_min - randint(20);
+ x2 = panel_col_max + randint(20);
+
+ /* Speed -- shrink to fit legal bounds */
+ if (y1 < 1) y1 = 1;
+ if (y2 > cur_hgt - 2) y2 = cur_hgt - 2;
+ if (x1 < 1) x1 = 1;
+ if (x2 > cur_wid - 2) x2 = cur_wid - 2;
+
+ /* Scan that area */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ c_ptr = &cave[y][x];
+
+ /* All non-walls are "checked" */
+ if (!is_wall(c_ptr))
+ {
+ /* Memorize normal features */
+ if (!cave_plain_floor_grid(c_ptr))
+ {
+ /* Memorize the object */
+ c_ptr->info |= (CAVE_MARK);
+ }
+
+ /* Memorize known walls */
+ for (i = 0; i < 8; i++)
+ {
+ c_ptr = &cave[y + ddy_ddd[i]][x + ddx_ddd[i]];
+
+ /* Memorize walls (etc) */
+ if (is_wall(c_ptr))
+ {
+ /* Memorize the walls */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+ }
+ }
+ }
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+/*
+ * Light up the dungeon using "clairvoyance"
+ *
+ * This function "illuminates" every grid in the dungeon, memorizes all
+ * "objects", memorizes all grids as with magic mapping, and, under the
+ * standard option settings (view_perma_grids but not view_torch_grids)
+ * memorizes all floor grids too.
+ *
+ * Note that if "view_perma_grids" is not set, we do not memorize floor
+ * grids, since this would defeat the purpose of "view_perma_grids", not
+ * that anyone seems to play without this option.
+ *
+ * Note that if "view_torch_grids" is set, we do not memorize floor grids,
+ * since this would prevent the use of "view_torch_grids" as a method to
+ * keep track of what grids have been observed directly.
+ */
+void wiz_lite(void)
+{
+ int i, y, x;
+
+
+ /* Memorize objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx) continue;
+
+ /* Memorize */
+ o_ptr->marked = TRUE;
+ }
+
+ /* Scan all normal grids */
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ /* Scan all normal grids */
+ for (x = 1; x < cur_wid - 1; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ o_ptr->marked = TRUE;
+ }
+ }
+
+ /* Process all non-walls */
+ /* if (c_ptr->feat < FEAT_SECRET) */
+ {
+ /* Scan all neighbors */
+ for (i = 0; i < 9; i++)
+ {
+ int yy = y + ddy_ddd[i];
+ int xx = x + ddx_ddd[i];
+
+ /* Get the grid */
+ c_ptr = &cave[yy][xx];
+
+ /* Perma-lite the grid */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Memorize normal features */
+ if (!cave_plain_floor_grid(c_ptr))
+ {
+ /* Memorize the grid */
+ c_ptr->info |= (CAVE_MARK);
+ }
+
+ /* Normally, memorize floors (see above) */
+ if (view_perma_grids && !view_torch_grids)
+ {
+ /* Memorize the grid */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+ }
+ }
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+void wiz_lite_extra(void)
+{
+ int y, x;
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave[y][x].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ wiz_lite();
+}
+
+/*
+ * Forget the dungeon map (ala "Thinking of Maud...").
+ */
+void wiz_dark(void)
+{
+ int i, y, x;
+
+
+ /* Forget every grid */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Process the grid */
+ c_ptr->info &= ~(CAVE_MARK);
+ }
+ }
+
+ /* Forget all objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx) continue;
+
+ /* Forget the object */
+ o_ptr->marked = FALSE;
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+
+
+/*
+ * Change the "feat" flag for a grid, and notice/redraw the grid
+ */
+void cave_set_feat(int y, int x, int feat)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Change the feature */
+ c_ptr->feat = feat;
+
+ /*
+ * Handle "wall/door" grids
+ *
+ * XXX XXX XXX This assumes c_ptr->mimic doesn't mimic terrain
+ * features whose LoS behaviour is different from its own, in
+ * most cases. Level boundaries are the most notable exception,
+ * where "real" terrain is always FEAT_PERM_SOLID, and the fact
+ * is (ab)used to prevent out-of-range access to the cave array.
+ * If we were going to implement an evil dungeon type in which
+ * everything is mimicked, then this function, los(), projectable(),
+ * project_path() and maybe some functions in melee2.c might
+ * better use c_ptr->mimic when it's set -- pelpel
+ */
+ if (!cave_sight_grid(c_ptr))
+ {
+ c_ptr->info |= (CAVE_WALL);
+ }
+
+ /* Handle "floor"/etc grids */
+ else
+ {
+ c_ptr->info &= ~(CAVE_WALL);
+ }
+
+ /* Notice & Redraw */
+ if (character_dungeon)
+ {
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+}
+
+
+/*
+ * Place floor terrain at (y, x) according to dungeon info
+ */
+void place_floor(int y, int x)
+{
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+}
+
+/*
+ * This routine is used when the current feature gets convert to a floor and
+ * the possible floor types include glass which is permanent. An unpassable
+ * feature is undesirable, so the glass gets convert to molten glass which
+ * is passable.
+ */
+void place_floor_convert_glass(int y, int x)
+{
+ place_floor(y, x);
+
+ if (cave[y][x].feat == 188) cave[y][x].feat = 103;
+}
+
+/*
+ * Place a cave filler at (y, x)
+ */
+void place_filler(int y, int x)
+{
+ cave_set_feat(y, x, fill_type[rand_int(100)]);
+}
+
+
+/*
+ * Calculate "incremental motion". Used by project() and shoot().
+ * Assumes that (*y,*x) lies on the path from (y1,x1) to (y2,x2).
+ */
+void mmove2(int *y, int *x, int y1, int x1, int y2, int x2)
+{
+ int dy, dx, dist, shift;
+
+ /* Extract the distance travelled */
+ dy = (*y < y1) ? y1 - *y : *y - y1;
+ dx = (*x < x1) ? x1 - *x : *x - x1;
+
+ /* Number of steps */
+ dist = (dy > dx) ? dy : dx;
+
+ /* We are calculating the next location */
+ dist++;
+
+
+ /* Calculate the total distance along each axis */
+ dy = (y2 < y1) ? (y1 - y2) : (y2 - y1);
+ dx = (x2 < x1) ? (x1 - x2) : (x2 - x1);
+
+ /* Paranoia -- Hack -- no motion */
+ if (!dy && !dx) return;
+
+
+ /* Move mostly vertically */
+ if (dy > dx)
+ {
+ /* Extract a shift factor */
+ shift = (dist * dx + (dy - 1) / 2) / dy;
+
+ /* Sometimes move along the minor axis */
+ (*x) = (x2 < x1) ? (x1 - shift) : (x1 + shift);
+
+ /* Always move along major axis */
+ (*y) = (y2 < y1) ? (y1 - dist) : (y1 + dist);
+ }
+
+ /* Move mostly horizontally */
+ else
+ {
+ /* Extract a shift factor */
+ shift = (dist * dy + (dx - 1) / 2) / dx;
+
+ /* Sometimes move along the minor axis */
+ (*y) = (y2 < y1) ? (y1 - shift) : (y1 + shift);
+
+ /* Always move along major axis */
+ (*x) = (x2 < x1) ? (x1 - dist) : (x1 + dist);
+ }
+}
+
+
+
+/*
+ * Determine if a bolt spell cast from (y1,x1) to (y2,x2) will arrive
+ * at the final destination, assuming no monster gets in the way.
+ *
+ * This is slightly (but significantly) different from "los(y1,x1,y2,x2)".
+ */
+bool_ projectable(int y1, int x1, int y2, int x2)
+{
+ int dist, y, x;
+
+ /* Start at the initial location */
+ y = y1, x = x1;
+
+ /* See "project()" */
+ for (dist = 0; dist <= MAX_RANGE; dist++)
+ {
+ /* Check for arrival at "final target" */
+ /*
+ * NB: this check was AFTER the 'never pass
+ * thru walls' clause, below. Switching them
+ * lets monsters shoot a the player if s/he is
+ * visible but in a wall
+ */
+ if ((x == x2) && (y == y2)) return (TRUE);
+
+ /* Never pass through walls */
+ if (dist && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) break;
+
+ /* Calculate the new location */
+ mmove2(&y, &x, y1, x1, y2, x2);
+ }
+
+
+ /* Assume obstruction */
+ return (FALSE);
+}
+
+
+
+
+/*
+ * Standard "find me a location" function
+ *
+ * Obtains a legal location within the given distance of the initial
+ * location, and with "los()" from the source to destination location.
+ *
+ * This function is often called from inside a loop which searches for
+ * locations while increasing the "d" distance.
+ *
+ * Currently the "m" parameter is unused.
+ */
+void scatter(int *yp, int *xp, int y, int x, int d)
+{
+ int nx, ny;
+ int attempts_left = 5000;
+
+ /* Pick a location */
+ while (--attempts_left)
+ {
+ /* Pick a new location */
+ ny = rand_spread(y, d);
+ nx = rand_spread(x, d);
+
+ /* Ignore illegal locations and outer walls */
+ if (!in_bounds(ny, nx)) continue;
+
+ /* Ignore "excessively distant" locations */
+ if ((d > 1) && (distance(y, x, ny, nx) > d)) continue;
+
+ /* Require "line of sight" */
+ if (los(y, x, ny, nx)) break;
+ }
+
+ if (attempts_left > 0)
+ {
+ /* Save the location */
+ (*yp) = ny;
+ (*xp) = nx;
+ }
+}
+
+
+
+
+/*
+ * Track a new monster
+ */
+void health_track(int m_idx)
+{
+ /* Track a new guy */
+ health_who = m_idx;
+
+ /* Redraw (later) */
+ p_ptr->redraw |= (PR_HEALTH);
+}
+
+
+
+/*
+ * Hack -- track the given monster race
+ */
+void monster_race_track(int r_idx, int ego)
+{
+ /* Save this monster ID */
+ monster_race_idx = r_idx;
+ monster_ego_idx = ego;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+}
+
+
+
+/*
+ * Hack -- track the given object kind
+ */
+void object_track(object_type *o_ptr)
+{
+ /* Save this monster ID */
+ tracked_object = o_ptr;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OBJECT);
+}
+
+
+
+/*
+ * Something has happened to disturb the player.
+ *
+ * The first arg indicates a major disturbance, which affects search.
+ *
+ * The second arg is currently unused, but could induce output flush.
+ *
+ * All disturbance cancels repeated commands, resting, and running.
+ */
+void disturb(int stop_search, int unused_flag)
+{
+ /* Unused */
+ unused_flag = unused_flag;
+
+ /* Cancel auto-commands */
+ /* command_new = 0; */
+
+ /* Cancel repeated commands */
+ if (command_rep)
+ {
+ /* Cancel */
+ command_rep = 0;
+
+ /* Redraw the state (later) */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ /* Cancel Resting */
+ if (resting)
+ {
+ /* Cancel */
+ resting = 0;
+
+ /* Redraw the state (later) */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ /* Cancel running */
+ if (running)
+ {
+ /* Cancel */
+ running = 0;
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+ }
+
+ /* Cancel searching if requested */
+ if (stop_search && p_ptr->searching)
+ {
+ /* Cancel */
+ p_ptr->searching = FALSE;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ /* Flush the input if requested */
+ if (flush_disturb) flush();
+}
+
+
+/*
+ * Hack -- Check if a level is a "quest" level
+ */
+int is_quest(int level)
+{
+ int i = random_quest_number();
+
+ /* Check quests */
+ if (p_ptr->inside_quest)
+ return (p_ptr->inside_quest);
+
+ if (i) return (QUEST_RANDOM);
+
+ /* Nope */
+ return (0);
+}
+
+
+/*
+ * Return the index of the random quest on this level
+ * (or zero)
+ */
+int random_quest_number()
+{
+ if ((dun_level >= 1) && (dun_level < MAX_RANDOM_QUEST) &&
+ (dungeon_flags1 & DF1_PRINCIPAL) &&
+ (random_quests[dun_level].type) &&
+ (!random_quests[dun_level].done) &&
+ (!is_randhero(dun_level)))
+ {
+ return dun_level;
+ }
+
+ /* Nope */
+ return 0;
+}
+
+
+/*
+ * handle spell effects
+ */
+int effect_pop()
+{
+ int i;
+
+ for (i = 1; i < MAX_EFFECTS; i++)
+ if (!effects[i].time)
+ return i;
+ return -1;
+}
+
+int new_effect(int type, int dam, int time, int cy, int cx, int rad, s32b flags)
+{
+ int i;
+
+ if ((i = effect_pop()) == -1) return -1;
+
+ effects[i].type = type;
+ effects[i].dam = dam;
+ effects[i].time = time;
+ effects[i].flags = flags;
+ effects[i].cx = cx;
+ effects[i].cy = cy;
+ effects[i].rad = rad;
+ return i;
+}
diff --git a/src/cmd1.c b/src/cmd1.c
new file mode 100644
index 00000000..49c0d38f
--- /dev/null
+++ b/src/cmd1.c
@@ -0,0 +1,5125 @@
+/* File: cmd1.c */
+
+/* Purpose: Movement commands (part 1) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+#define MAX_VAMPIRIC_DRAIN 100
+
+
+/*
+ * Determine if the player "hits" a monster (normal combat).
+ * Note -- Always miss 5%, always hit 5%, otherwise random.
+ */
+bool_ test_hit_fire(int chance, int ac, int vis)
+{
+ int k;
+
+
+ /* Percentile dice */
+ k = rand_int(100);
+
+ /* Hack -- Instant miss or hit */
+ if (k < 10) return (k < 5);
+
+ /* Never hit */
+ if (chance <= 0) return (FALSE);
+
+ /* Invisible monsters are harder to hit */
+ if (!vis) chance = (chance + 1) / 2;
+
+ /* Power competes against armor */
+ if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE);
+
+ /* Assume hit */
+ return (TRUE);
+}
+
+
+
+/*
+ * Determine if the player "hits" a monster (normal combat).
+ *
+ * Note -- Always miss 5%, always hit 5%, otherwise random.
+ */
+bool_ test_hit_norm(int chance, int ac, int vis)
+{
+ int k;
+
+
+ /* Percentile dice */
+ k = rand_int(100);
+
+ /* Hack -- Instant miss or hit */
+ if (k < 10) return (k < 5);
+
+ /* Wimpy attack never hits */
+ if (chance <= 0) return (FALSE);
+
+ /* Penalize invisible targets */
+ if (!vis) chance = (chance + 1) / 2;
+
+ /* Power must defeat armor */
+ if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE);
+
+ /* Assume hit */
+ return (TRUE);
+}
+
+
+
+/*
+ * Critical hits (from objects thrown by player)
+ * Factor in item weight, total plusses, and player level.
+ */
+s16b critical_shot(int weight, int plus, int dam, int skill)
+{
+ int i, k;
+
+
+ /* Extract "shot" power */
+ i = (weight + ((p_ptr->to_h + plus) * 4) +
+ get_skill_scale(skill, 100));
+ i += 50 * p_ptr->xtra_crit;
+ i += luck( -100, 100);
+
+ /* Critical hit */
+ if (randint(5000) <= i)
+ {
+ k = weight + randint(500);
+
+ if (k < 500)
+ {
+ msg_print("It was a good hit!");
+ dam = 2 * dam + 5;
+ }
+ else if (k < 1000)
+ {
+ msg_print("It was a great hit!");
+ dam = 2 * dam + 10;
+ }
+ else
+ {
+ msg_print("It was a superb hit!");
+ dam = 3 * dam + 15;
+ }
+ }
+
+ return (dam);
+}
+
+/*
+ * Critical hits (by player)
+ *
+ * Factor in weapon weight, total plusses, player level.
+ */
+s16b critical_norm(int weight, int plus, int dam, int weapon_tval, bool_ *done_crit)
+{
+ int i, k, num = randint(5000);
+
+ *done_crit = FALSE;
+
+ /* Extract "blow" power */
+ i = (weight + ((p_ptr->to_h + plus) * 5) +
+ get_skill_scale(p_ptr->melee_style, 150));
+ i += 50 * p_ptr->xtra_crit;
+ if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS))
+ {
+ i += get_skill_scale(SKILL_CRITS, 40 * 50);
+ }
+ i += luck( -100, 100);
+
+ /* Force good strikes */
+ if (p_ptr->tim_deadly)
+ {
+ set_tim_deadly(p_ptr->tim_deadly - 1);
+ msg_print("It was a *GREAT* hit!");
+ dam = 3 * dam + 20;
+ *done_crit = TRUE;
+ }
+
+ /* Chance */
+ else if (num <= i)
+ {
+ k = weight + randint(650);
+ if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS))
+ {
+ k += get_skill_scale(SKILL_CRITS, 400);
+ }
+
+ if (k < 400)
+ {
+ msg_print("It was a good hit!");
+ dam = 2 * dam + 5;
+ }
+ else if (k < 700)
+ {
+ msg_print("It was a great hit!");
+ dam = 2 * dam + 10;
+ }
+ else if (k < 900)
+ {
+ msg_print("It was a superb hit!");
+ dam = 3 * dam + 15;
+ }
+ else if (k < 1300)
+ {
+ msg_print("It was a *GREAT* hit!");
+ dam = 3 * dam + 20;
+ }
+ else
+ {
+ msg_print("It was a *SUPERB* hit!");
+ dam = ((7 * dam) / 2) + 25;
+ }
+ *done_crit = TRUE;
+ }
+
+ return (dam);
+}
+
+
+
+/*
+ * Extract the "total damage" from a given object hitting a given monster.
+ *
+ * Note that "flasks of oil" do NOT do fire damage, although they
+ * certainly could be made to do so. XXX XXX
+ *
+ * Note that most brands and slays are x3, except Slay Animal (x2),
+ * Slay Evil (x2), and Kill dragon (x5).
+ */
+s16b tot_dam_aux(object_type *o_ptr, int tdam, monster_type *m_ptr,
+ s32b *special)
+{
+ int mult = 1;
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Some "weapons" and "ammo" do extra damage */
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOOMERANG:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_DIGGING:
+ {
+ /* Slay Animal */
+ if ((f1 & (TR1_SLAY_ANIMAL)) && (r_ptr->flags3 & (RF3_ANIMAL)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_ANIMAL);
+ }
+
+ if (mult < 2) mult = 2;
+ }
+
+ /* Slay Evil */
+ if ((f1 & (TR1_SLAY_EVIL)) && (r_ptr->flags3 & (RF3_EVIL)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+
+ if (mult < 2) mult = 2;
+ }
+
+ /* Slay Undead */
+ if ((f1 & (TR1_SLAY_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Demon */
+ if ((f1 & (TR1_SLAY_DEMON)) && (r_ptr->flags3 & (RF3_DEMON)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_DEMON);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Orc */
+ if ((f1 & (TR1_SLAY_ORC)) && (r_ptr->flags3 & (RF3_ORC)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_ORC);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Troll */
+ if ((f1 & (TR1_SLAY_TROLL)) && (r_ptr->flags3 & (RF3_TROLL)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_TROLL);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Giant */
+ if ((f1 & (TR1_SLAY_GIANT)) && (r_ptr->flags3 & (RF3_GIANT)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_GIANT);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Dragon */
+ if ((f1 & (TR1_SLAY_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_DRAGON);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Execute Dragon */
+ if ((f1 & (TR1_KILL_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_DRAGON);
+ }
+
+ if (mult < 5) mult = 5;
+ }
+
+ /* Execute Undead */
+ if ((f5 & (TR5_KILL_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+
+ if (mult < 5) mult = 5;
+ }
+
+ /* Execute Demon */
+ if ((f5 & (TR5_KILL_DEMON)) && (r_ptr->flags3 & (RF3_DEMON)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_DEMON);
+ }
+
+ if (mult < 5) mult = 5;
+ }
+
+
+ /* Brand (Acid) */
+ if (f1 & (TR1_BRAND_ACID))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_ACID))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_ACID);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags9 & (RF9_SUSCEP_ACID))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags9 |= (RF9_SUSCEP_ACID);
+ }
+ if (mult < 6) mult = 6;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ }
+ }
+
+ /* Brand (Elec) */
+ if (f1 & (TR1_BRAND_ELEC))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_ELEC))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_ELEC);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags9 & (RF9_SUSCEP_ELEC))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags9 |= (RF9_SUSCEP_ELEC);
+ }
+ if (mult < 6) mult = 6;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ }
+ }
+
+ /* Brand (Fire) */
+ if (f1 & (TR1_BRAND_FIRE))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_FIRE))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_FIRE);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags3 & (RF3_SUSCEP_FIRE))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_SUSCEP_FIRE);
+ }
+ if (mult < 6) mult = 6;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ }
+ }
+
+ /* Brand (Cold) */
+ if (f1 & (TR1_BRAND_COLD))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_COLD))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_COLD);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags3 & (RF3_SUSCEP_COLD))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_SUSCEP_COLD);
+ }
+ if (mult < 6) mult = 6;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ }
+ }
+
+ /* Brand (Poison) */
+ if (f1 & (TR1_BRAND_POIS) || (p_ptr->tim_poison))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_POIS))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_POIS);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags9 & (RF9_SUSCEP_POIS))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags9 |= (RF9_SUSCEP_POIS);
+ }
+ if (mult < 6) mult = 6;
+ if (magik(95)) *special |= SPEC_POIS;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ if (magik(50)) *special |= SPEC_POIS;
+ }
+ }
+
+ /* Wounding */
+ if (f5 & (TR5_WOUNDING))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags8 & (RF8_NO_CUT))
+ {
+ if (m_ptr->ml)
+ {
+ r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT);
+ }
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (magik(50)) *special |= SPEC_CUT;
+ }
+ }
+ break;
+ }
+ }
+
+
+ /* Return the total damage */
+ return (tdam * mult);
+}
+
+
+/*
+ * Search for hidden things
+ */
+void search(void)
+{
+ int y, x, chance;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ cave_type *c_ptr;
+
+
+ /* Start with base search ability */
+ chance = p_ptr->skill_srh;
+
+ /* Penalize various conditions */
+ if (p_ptr->blind || no_lite()) chance = chance / 10;
+ if (p_ptr->confused || p_ptr->image) chance = chance / 10;
+
+ /* Search the nearby grids, which are always in bounds */
+ for (y = (p_ptr->py - 1); y <= (p_ptr->py + 1); y++)
+ {
+ for (x = (p_ptr->px - 1); x <= (p_ptr->px + 1); x++)
+ {
+ /* Sometimes, notice things */
+ if (rand_int(100) < chance)
+ {
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Invisible trap */
+ if ((c_ptr->t_idx != 0) && !(c_ptr->info & CAVE_TRDT))
+ {
+ /* Pick a trap */
+ pick_trap(y, x);
+
+ /* Message */
+ msg_print("You have found a trap.");
+
+ /* Disturb */
+ disturb(0, 0);
+ }
+
+ /* Secret door */
+ if (c_ptr->feat == FEAT_SECRET)
+ {
+ /* Message */
+ msg_print("You have found a secret door.");
+
+ /* Pick a door XXX XXX XXX */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+ cave[y][x].mimic = 0;
+ lite_spot(y, x);
+
+ /* Disturb */
+ disturb(0, 0);
+ }
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx;
+ this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Skip non-chests */
+ if (o_ptr->tval != TV_CHEST) continue;
+
+ /* Skip non-trapped chests */
+ if (!o_ptr->pval) continue;
+
+ /* Identify once */
+ if (!object_known_p(o_ptr))
+ {
+ /* Message */
+ msg_print("You have discovered a trap on the chest!");
+
+ /* Know the trap */
+ object_known(o_ptr);
+
+ /* Notice it */
+ disturb(0, 0);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+
+/*
+ * Player "wants" to pick up an object or gold.
+ * Note that we ONLY handle things that can be picked up.
+ * See "move_player()" for handling of other things.
+ */
+void carry(int pickup)
+{
+ if (!p_ptr->disembodied)
+ {
+ py_pickup_floor(pickup);
+ }
+}
+
+
+/*
+ * Handle player hitting a real trap
+ */
+static void hit_trap(void)
+{
+ bool_ ident = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Disturb the player */
+ disturb(0, 0);
+
+ /* Get the cave grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+ if (c_ptr->t_idx != 0)
+ {
+ ident = player_activate_trap_type(p_ptr->py, p_ptr->px, NULL, -1);
+ if (ident)
+ {
+ t_info[c_ptr->t_idx].ident = TRUE;
+ msg_format("You identified the trap as %s.",
+ t_name + t_info[c_ptr->t_idx].name);
+ }
+ }
+}
+
+
+void touch_zap_player(monster_type *m_ptr)
+{
+ int aura_damage = 0;
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+
+ if (r_ptr->flags2 & (RF2_AURA_FIRE))
+ {
+ if (!(p_ptr->immune_fire))
+ {
+ char aura_dam[80];
+
+ aura_damage =
+ damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17));
+
+ /* Hack -- Get the "died from" name */
+ monster_desc(aura_dam, m_ptr, 0x88);
+
+ msg_print("You are suddenly very hot!");
+
+ if (p_ptr->oppose_fire) aura_damage = (aura_damage + 2) / 3;
+ if (p_ptr->resist_fire) aura_damage = (aura_damage + 2) / 3;
+ if (p_ptr->sensible_fire) aura_damage = (aura_damage + 2) * 2;
+
+ take_hit(aura_damage, aura_dam);
+ r_ptr->r_flags2 |= RF2_AURA_FIRE;
+ handle_stuff();
+ }
+ }
+
+
+ if (r_ptr->flags2 & (RF2_AURA_ELEC))
+ {
+ if (!(p_ptr->immune_elec))
+ {
+ char aura_dam[80];
+
+ aura_damage =
+ damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17));
+
+ /* Hack -- Get the "died from" name */
+ monster_desc(aura_dam, m_ptr, 0x88);
+
+ if (p_ptr->oppose_elec) aura_damage = (aura_damage + 2) / 3;
+ if (p_ptr->resist_elec) aura_damage = (aura_damage + 2) / 3;
+
+ msg_print("You get zapped!");
+ take_hit(aura_damage, aura_dam);
+ r_ptr->r_flags2 |= RF2_AURA_ELEC;
+ handle_stuff();
+ }
+ }
+}
+
+
+/*
+ * Carried monster can attack too.
+ * Based on monst_attack_monst.
+ */
+static void carried_monster_attack(s16b m_idx, bool_ *fear, bool_ *mdeath,
+ int x, int y)
+{
+ monster_type *t_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr;
+
+ monster_race *tr_ptr = race_inf(t_ptr);
+
+ int ap_cnt;
+
+ int ac, rlev, pt;
+
+ char t_name[80];
+
+ cptr sym_name = symbiote_name(TRUE);
+
+ char temp[80];
+
+ bool_ blinked = FALSE, touched = FALSE;
+
+ byte y_saver = t_ptr->fy;
+
+ byte x_saver = t_ptr->fx;
+
+ object_type *o_ptr;
+
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ if (!o_ptr->k_idx) return;
+
+ r_ptr = &r_info[o_ptr->pval];
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & RF1_NEVER_BLOW) return;
+
+ /* Total armor */
+ ac = t_ptr->ac;
+
+ /* Extract the effective monster level */
+ rlev = ((o_ptr->elevel >= 1) ? o_ptr->elevel : 1);
+
+ /* Get the monster name (or "it") */
+ monster_desc(t_name, t_ptr, 0);
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ if (!t_ptr->ml)
+ {
+ msg_print("You hear noise.");
+ }
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = r_ptr->blow[ap_cnt].effect;
+ int method = r_ptr->blow[ap_cnt].method;
+ int d_dice = r_ptr->blow[ap_cnt].d_dice;
+ int d_side = r_ptr->blow[ap_cnt].d_side;
+
+ /* Stop attacking if the target dies! */
+ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver)
+ break;
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+ if (blinked) /* Stop! */
+ {
+ /* break; */
+ }
+
+ /* Extract visibility (before blink) */
+ visible = TRUE;
+
+ /* Extract the attack "power" */
+ power = get_attack_power(effect);
+
+ /* Monster hits */
+ if (!effect || check_hit2(power, rlev, ac))
+ {
+ /* Always disturbing */
+ disturb(1, 0);
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hits %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touches %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punches %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kicks %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claws %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bites %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "stings %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's %s.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butts %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crushes %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulfs %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charges %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawls on %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drools on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spits on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gazes at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wails at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "releases spores at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "projects XXX4's at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "begs %s for money.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = "insults %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ act = "moans at %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ act = "sings to %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+ }
+
+ /* Message */
+ if (act)
+ {
+ strfmt(temp, act, t_name);
+ if (t_ptr->ml)
+ msg_format("%s %s", sym_name, temp);
+
+ }
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side);
+
+ pt = GF_MISSILE;
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ damage = 0;
+ pt = 0;
+ break;
+ }
+
+ case RBE_HURT:
+ case RBE_SANITY:
+ {
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+ break;
+ }
+
+ case RBE_POISON:
+ case RBE_DISEASE:
+ {
+ pt = GF_POIS;
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ case RBE_UN_POWER:
+ case RBE_ABOMINATION:
+ {
+ pt = GF_DISENCHANT;
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ case RBE_EAT_LITE:
+ {
+ pt = damage = 0;
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ case RBE_EAT_GOLD:
+ {
+ pt = damage = 0;
+ if (randint(2) == 1) blinked = TRUE;
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ pt = GF_ACID;
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ pt = GF_ELEC;
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ pt = GF_FIRE;
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ pt = GF_COLD;
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ break;
+ }
+
+ case RBE_CONFUSE:
+ case RBE_HALLU:
+ {
+ pt = GF_CONFUSION;
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ pt = GF_TURN_ALL;
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ pt = GF_OLD_SLEEP; /* sort of close... */
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ case RBE_LOSE_INT:
+ case RBE_LOSE_WIS:
+ case RBE_LOSE_DEX:
+ case RBE_LOSE_CON:
+ case RBE_LOSE_CHR:
+ case RBE_LOSE_ALL:
+ case RBE_PARASITE:
+ {
+ break;
+ }
+
+ case RBE_SHATTER:
+ {
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 8);
+ }
+ break;
+ }
+
+ case RBE_EXP_10:
+ case RBE_EXP_20:
+ case RBE_EXP_40:
+ case RBE_EXP_80:
+ {
+ pt = GF_NETHER;
+ break;
+ }
+
+ case RBE_TIME:
+ {
+ pt = GF_TIME;
+ break;
+ }
+
+ default:
+ {
+ pt = 0;
+ break;
+ }
+ }
+
+ if (pt)
+ {
+ /* Do damage if not exploding */
+ project(0, 0, t_ptr->fy, t_ptr->fx,
+ (pt == GF_OLD_SLEEP ? r_ptr->level : damage), pt,
+ PROJECT_KILL | PROJECT_STOP);
+
+ if (touched)
+ {
+ /* Aura fire */
+ if ((tr_ptr->flags2 & RF2_AURA_FIRE) &&
+ !(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ if (t_ptr->ml)
+ {
+ blinked = FALSE;
+ msg_format("You are suddenly very hot!");
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_FIRE;
+ }
+ project(m_idx, 0, p_ptr->py, p_ptr->px,
+ damroll(1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_FIRE, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ /* Aura elec */
+ if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) &&
+ !(r_ptr->flags3 & (RF3_IM_ELEC)))
+ {
+ if (t_ptr->ml)
+ {
+ blinked = FALSE;
+ msg_format("You get zapped!");
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_ELEC;
+ }
+ project(m_idx, 0, p_ptr->py, p_ptr->px,
+ damroll(1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_ELEC, PROJECT_KILL | PROJECT_STOP);
+ }
+ }
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+ {
+ /* Disturb */
+ disturb(1, 0);
+
+ /* Message */
+ msg_format("%s misses %s.", sym_name, t_name);
+ break;
+ }
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+
+ /* Blink away */
+ if (blinked)
+ {
+ msg_format("You and %s flee laughing!", symbiote_name(FALSE));
+
+ teleport_player(MAX_SIGHT * 2 + 5);
+ }
+}
+
+/*
+ * Carried monster can attack too.
+ * Based on monst_attack_monst.
+ */
+static void incarnate_monster_attack(s16b m_idx, bool_ *fear, bool_ *mdeath,
+ int x, int y)
+{
+ monster_type *t_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr;
+
+ monster_race *tr_ptr = race_inf(t_ptr);
+
+ int ap_cnt;
+
+ int ac, rlev, pt;
+
+ char t_name[80];
+
+ char temp[80];
+
+ bool_ blinked = FALSE, touched = FALSE;
+
+ byte y_saver = t_ptr->fy;
+
+ byte x_saver = t_ptr->fx;
+
+
+ if (!p_ptr->body_monster) return;
+
+ r_ptr = race_info_idx(p_ptr->body_monster, 0);
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & RF1_NEVER_BLOW) return;
+
+ /* Total armor */
+ ac = t_ptr->ac;
+
+ /* Extract the effective monster level */
+ rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1);
+
+ /* Get the monster name (or "it") */
+ monster_desc(t_name, t_ptr, 0);
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ if (!t_ptr->ml)
+ {
+ msg_print("You hear noise.");
+ }
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < (p_ptr->num_blow > 4) ? 4 : p_ptr->num_blow;
+ ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = r_ptr->blow[ap_cnt].effect;
+ int method = r_ptr->blow[ap_cnt].method;
+ int d_dice = r_ptr->blow[ap_cnt].d_dice;
+ int d_side = r_ptr->blow[ap_cnt].d_side;
+
+ /* Stop attacking if the target dies! */
+ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver)
+ break;
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+ if (blinked) /* Stop! */
+ {
+ /* break; */
+ }
+
+ /* Extract visibility (before blink) */
+ visible = TRUE;
+
+ /* Extract the attack "power" */
+ effect = get_attack_power(effect);
+
+ /* Monster hits */
+ if (!effect || check_hit2(power, rlev, ac))
+ {
+ /* Always disturbing */
+ disturb(1, 0);
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hit %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touch %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punch %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kick %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claw %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bite %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "sting %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's %s.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butt %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crush %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulf %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charge %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawl on %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drool on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spit on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gaze at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wail at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "release spores at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "project XXX4's at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "beg %s for money.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = "insult %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ act = "moan at %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ act = "sing to %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+ }
+
+ /* Message */
+ if (act)
+ {
+ strfmt(temp, act, t_name);
+ if (t_ptr->ml)
+ msg_format("You %s", temp);
+
+ }
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side) + p_ptr->to_d;
+
+ pt = GF_MISSILE;
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ damage = 0;
+ pt = 0;
+ break;
+ }
+
+ case RBE_HURT:
+ case RBE_SANITY:
+ {
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+ break;
+ }
+
+ case RBE_POISON:
+ case RBE_DISEASE:
+ {
+ pt = GF_POIS;
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ case RBE_UN_POWER:
+ {
+ pt = GF_DISENCHANT;
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ case RBE_EAT_LITE:
+ {
+ pt = damage = 0;
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ case RBE_EAT_GOLD:
+ {
+ pt = damage = 0;
+ if (randint(2) == 1) blinked = TRUE;
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ pt = GF_ACID;
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ pt = GF_ELEC;
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ pt = GF_FIRE;
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ pt = GF_COLD;
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ break;
+ }
+
+ case RBE_HALLU:
+ case RBE_CONFUSE:
+ {
+ pt = GF_CONFUSION;
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ pt = GF_TURN_ALL;
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ pt = GF_OLD_SLEEP; /* sort of close... */
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ case RBE_LOSE_INT:
+ case RBE_LOSE_WIS:
+ case RBE_LOSE_DEX:
+ case RBE_LOSE_CON:
+ case RBE_LOSE_CHR:
+ case RBE_LOSE_ALL:
+ case RBE_PARASITE:
+ {
+ break;
+ }
+
+ case RBE_SHATTER:
+ {
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 8);
+ }
+ break;
+ }
+
+ case RBE_EXP_10:
+ case RBE_EXP_20:
+ case RBE_EXP_40:
+ case RBE_EXP_80:
+ {
+ pt = GF_NETHER;
+ break;
+ }
+
+ case RBE_TIME:
+ {
+ pt = GF_TIME;
+ break;
+ }
+
+ default:
+ {
+ pt = 0;
+ break;
+ }
+ }
+
+ if (pt)
+ {
+ /* Do damage if not exploding */
+ project(0, 0, t_ptr->fy, t_ptr->fx,
+ (pt == GF_OLD_SLEEP ? p_ptr->lev * 2 : damage), pt,
+ PROJECT_KILL | PROJECT_STOP);
+
+ if (touched)
+ {
+ /* Aura fire */
+ if ((tr_ptr->flags2 & RF2_AURA_FIRE) &&
+ !(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ if (t_ptr->ml)
+ {
+ blinked = FALSE;
+ msg_format("You are suddenly very hot!");
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_FIRE;
+ }
+ project(m_idx, 0, p_ptr->py, p_ptr->px,
+ damroll(1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_FIRE, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ /* Aura elec */
+ if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) &&
+ !(r_ptr->flags3 & (RF3_IM_ELEC)))
+ {
+ if (t_ptr->ml)
+ {
+ blinked = FALSE;
+ msg_format("You get zapped!");
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_ELEC;
+ }
+ project(m_idx, 0, p_ptr->py, p_ptr->px,
+ damroll(1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_ELEC, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ }
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+ {
+ /* Disturb */
+ disturb(1, 0);
+
+ /* Message */
+ msg_format("You miss %s.", t_name);
+
+ break;
+ }
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+
+ /* Blink away */
+ if (blinked)
+ {
+ msg_print("You flee laughing!");
+
+ teleport_player(MAX_SIGHT * 2 + 5);
+ }
+}
+
+
+/*
+ * Fetch an attack description from dam_*.txt files.
+ */
+
+static void flavored_attack(int percent, char *output)
+{
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+ bool_ insane = (rand_int(100) < insanity);
+
+ if (percent < 5)
+ {
+ if (!insane)
+ strcpy(output, "You scratch %s.");
+ else
+ get_rnd_line("dam_none.txt", output);
+
+ }
+ else if (percent < 30)
+ {
+ if (!insane)
+ strcpy(output, "You hit %s.");
+ else
+ get_rnd_line("dam_med.txt", output);
+ }
+ else if (percent < 60)
+ {
+ if (!insane)
+ strcpy(output, "You wound %s.");
+ else
+ get_rnd_line("dam_lots.txt", output);
+ }
+ else if (percent < 95)
+ {
+ if (!insane)
+ strcpy(output, "You cripple %s.");
+ else
+ get_rnd_line("dam_huge.txt", output);
+
+ }
+ else
+ {
+ if (!insane)
+ strcpy(output, "You demolish %s.");
+ else
+ get_rnd_line("dam_xxx.txt", output);
+ }
+}
+
+
+/*
+ * Apply the special effects of an attack
+ */
+void attack_special(monster_type *m_ptr, s32b special, int dam)
+{
+ char m_name[80];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+
+ /* Extract monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Special - Cut monster */
+ if (special & SPEC_CUT)
+ {
+ /* Cut the monster */
+ if (r_ptr->flags8 & (RF8_NO_CUT))
+ {
+ if (m_ptr->ml)
+ {
+ r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT);
+ }
+ }
+ else if (rand_int(100) >= r_ptr->level)
+ {
+ /* Already partially poisoned */
+ if (m_ptr->bleeding) msg_format("%^s is bleeding more strongly.",
+ m_name);
+ /* Was not poisoned */
+ else
+ msg_format("%^s is bleeding.", m_name);
+
+ m_ptr->bleeding += dam * 2;
+ }
+ }
+
+ /* Special - Poison monster */
+ if (special & SPEC_POIS)
+ {
+ /* Poison the monster */
+ if (r_ptr->flags3 & (RF3_IM_POIS))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_POIS);
+ }
+ }
+ /* Notice susceptibility */
+ else if (r_ptr->flags9 & (RF9_SUSCEP_POIS))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags9 |= (RF9_SUSCEP_POIS);
+ }
+ /* Already partially poisoned */
+ if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name);
+ /* Was not poisoned */
+ else
+ msg_format("%^s is poisoned.", m_name);
+
+ m_ptr->poisoned += dam * 2;
+ }
+ else if (rand_int(100) >= r_ptr->level)
+ {
+ /* Already partially poisoned */
+ if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name);
+ /* Was not poisoned */
+ else
+ msg_format("%^s is poisoned.", m_name);
+
+ m_ptr->poisoned += dam;
+ }
+ }
+}
+
+
+/*
+ * Bare handed attacks
+ */
+static void py_attack_hand(int *k, monster_type *m_ptr, s32b *special)
+{
+ s16b special_effect = 0, stun_effect = 0, times = 0;
+ martial_arts *ma_ptr, *old_ptr, *blow_table = ma_blows;
+ int resist_stun = 0, max = MAX_MA;
+ monster_race *r_ptr = race_inf(m_ptr);
+ char m_name[80];
+ bool_ desc = FALSE;
+ bool_ done_crit;
+ int plev = p_ptr->lev;
+
+ if ((!p_ptr->body_monster) && (p_ptr->mimic_form == resolve_mimic_name("Bear")) &&
+ (p_ptr->melee_style == SKILL_BEAR))
+ {
+ blow_table = bear_blows;
+ max = MAX_BEAR;
+ plev = get_skill(SKILL_BEAR);
+ }
+ if (p_ptr->melee_style == SKILL_HAND)
+ {
+ blow_table = ma_blows;
+ max = MAX_MA;
+ plev = get_skill(SKILL_HAND);
+ }
+ ma_ptr = &blow_table[0];
+ old_ptr = &blow_table[0];
+
+ /* Extract monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ if (r_ptr->flags1 & RF1_UNIQUE) resist_stun += 88;
+ if (r_ptr->flags3 & RF3_NO_CONF) resist_stun += 44;
+ if (r_ptr->flags3 & RF3_NO_SLEEP) resist_stun += 44;
+ if ((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_NONLIVING)) resist_stun += 88;
+
+ if (plev)
+ {
+ for (times = 0; times < (plev < 7 ? 1 : plev / 7); times++)
+ {
+ do
+ {
+ ma_ptr = &blow_table[(randint(max)) - 1];
+ }
+ while ((ma_ptr->min_level > plev) || (randint(plev) < ma_ptr->chance));
+
+ /* keep the highest level attack available we found */
+ if ((ma_ptr->min_level > old_ptr->min_level) &&
+ !(p_ptr->stun || p_ptr->confused))
+ {
+ old_ptr = ma_ptr;
+
+ if (wizard && cheat_xtra)
+ {
+ msg_print("Attack re-selected.");
+ }
+ }
+ else
+ {
+ ma_ptr = old_ptr;
+ }
+ }
+ }
+
+ *k = damroll(ma_ptr->dd, ma_ptr->ds);
+
+ if (ma_ptr->effect & MA_KNEE)
+ {
+ if (r_ptr->flags1 & RF1_MALE)
+ {
+ if (!desc) msg_format("You hit %s in the groin with your knee!",
+ m_name);
+ sound(SOUND_PAIN);
+ special_effect = MA_KNEE;
+ }
+ else if (!desc) msg_format(ma_ptr->desc, m_name);
+
+ desc = TRUE;
+ }
+ if (ma_ptr->effect & MA_FULL_SLOW)
+ {
+ special_effect = MA_SLOW;
+ if (!desc) msg_format(ma_ptr->desc, m_name);
+
+ desc = TRUE;
+ }
+ if (ma_ptr->effect & MA_SLOW)
+ {
+ if (!
+ ((r_ptr->flags1 & RF1_NEVER_MOVE) ||
+ strchr("UjmeEv$,DdsbBFIJQSXclnw!=?", r_ptr->d_char)))
+ {
+ if (!desc) msg_format("You kick %s in the ankle.", m_name);
+ special_effect = MA_SLOW;
+ }
+ else if (!desc) msg_format(ma_ptr->desc, m_name);
+
+ desc = TRUE;
+ }
+ if (ma_ptr->effect & MA_STUN)
+ {
+ if (ma_ptr->power)
+ {
+ stun_effect = (ma_ptr->power / 2) + randint(ma_ptr->power / 2);
+ }
+
+ if (!desc) msg_format(ma_ptr->desc, m_name);
+ desc = TRUE;
+ }
+ if (ma_ptr->effect & MA_WOUND)
+ {
+ if (magik(ma_ptr->power))
+ {
+ *special |= SPEC_CUT;
+ }
+ if (!desc) msg_format(ma_ptr->desc, m_name);
+ desc = TRUE;
+ }
+
+ *k = critical_norm(plev * (randint(10)), ma_ptr->min_level, *k, -1, &done_crit);
+
+ if ((special_effect & MA_KNEE) && ((*k + p_ptr->to_d) < m_ptr->hp))
+ {
+ msg_format("%^s moans in agony!", m_name);
+ stun_effect = 7 + randint(13);
+ resist_stun /= 3;
+ }
+ if (((special_effect & MA_FULL_SLOW) || (special_effect & MA_SLOW)) &&
+ ((*k + p_ptr->to_d) < m_ptr->hp))
+ {
+ if (!(r_ptr->flags1 & RF1_UNIQUE) &&
+ (randint(plev) > m_ptr->level) && m_ptr->mspeed > 60)
+ {
+ msg_format("%^s starts limping slower.", m_name);
+ m_ptr->mspeed -= 10;
+ }
+ }
+
+ if (stun_effect && ((*k + p_ptr->to_d) < m_ptr->hp))
+ {
+ if (plev > randint(m_ptr->level + resist_stun + 10))
+ {
+ if (m_ptr->stunned)
+ msg_format("%^s is still stunned.", m_name);
+ else
+ msg_format("%^s is stunned.", m_name);
+
+ m_ptr->stunned += (stun_effect);
+ }
+ }
+}
+
+
+/*
+ * Apply nazgul effects
+ */
+void do_nazgul(int *k, int *num, int num_blow, int weap, monster_race *r_ptr,
+ object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ bool_ mundane;
+ bool_ allow_shatter = TRUE;
+
+ /* Extract mundane-ness of the current weapon */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* It should be Slay Evil, Slay Undead, or *Slay Undead* */
+ mundane = !(f1 & TR1_SLAY_EVIL) && !(f1 & TR1_SLAY_UNDEAD) &&
+ !(f5 & TR5_KILL_UNDEAD);
+
+ /* Some blades can resist shattering */
+ if (f5 & TR5_RES_MORGUL)
+ allow_shatter = FALSE;
+
+ /* Mega Hack -- Hitting Nazgul is REALY dangerous (ideas from Akhronath) */
+ if (r_ptr->flags7 & RF7_NAZGUL)
+ {
+ if ((!o_ptr->name2) && (!artifact_p(o_ptr)) && allow_shatter)
+ {
+ msg_print("Your weapon *DISINTEGRATES*!");
+ *k = 0;
+
+ inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* To stop attacking */
+ *num = num_blow;
+ }
+ else if (o_ptr->name2)
+ {
+ if (mundane)
+ {
+ msg_print
+ ("The Ringwraith is IMPERVIOUS to the mundane weapon.");
+ *k = 0;
+ }
+
+ /* 25% chance of getting destroyed */
+ if (magik(25) && allow_shatter)
+ {
+ msg_print("Your weapon is destroyed!");
+
+ inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* To stop attacking */
+ *num = num_blow;
+ }
+ }
+ else if (artifact_p(o_ptr))
+ {
+ if (mundane)
+ {
+ msg_print
+ ("The Ringwraith is IMPERVIOUS to the mundane weapon.");
+ *k = 0;
+ }
+
+ apply_disenchant(INVEN_WIELD + weap);
+
+ /* 1/1000 chance of getting destroyed */
+ if (!rand_int(1000) && allow_shatter)
+ {
+ msg_print("Your weapon is destroyed!");
+
+ inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* To stop attacking */
+ *num = num_blow;
+ }
+ }
+
+ /* If any damage is done, then 25% chance of getting the Black Breath */
+ if (*k)
+ {
+ if (magik(25))
+ {
+ msg_print("Your foe calls upon your soul!");
+ msg_print
+ ("You feel the Black Breath slowly draining you of life...");
+ p_ptr->black_breath = TRUE;
+ }
+ }
+ }
+}
+
+
+/*
+ * Player attacks a (poor, defenseless) creature -RAK-
+ *
+ * If no "weapon" is available, then "punch" the monster one time.
+ */
+void py_attack(int y, int x, int max_blow)
+{
+ int num = 0, k, bonus, chance;
+
+ s32b special = 0;
+
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ object_type *o_ptr;
+
+ char m_name[80];
+
+ bool_ fear = FALSE;
+
+ bool_ mdeath = FALSE;
+
+ bool_ backstab = FALSE;
+
+ bool_ vorpal_cut = FALSE;
+
+ int chaos_effect = 0;
+
+ bool_ stab_fleeing = FALSE;
+
+ bool_ do_quake = FALSE;
+
+ bool_ done_crit = FALSE;
+
+ bool_ drain_msg = TRUE;
+
+ int drain_result = 0, drain_heal = 0;
+
+ int drain_left = MAX_VAMPIRIC_DRAIN;
+
+ /* A massive hack -- life-draining weapons */
+ u32b f1, f2, f3, f4, f5, esp;
+
+ int weap;
+
+ /* Disturb the player */
+ disturb(0, 0);
+
+ if (r_info[p_ptr->body_monster].flags1 & RF1_NEVER_BLOW)
+ {
+ msg_print("You cannot attack in this form!");
+ return;
+ }
+
+ if (get_skill(SKILL_BACKSTAB))
+ {
+ if ((m_ptr->csleep) && (m_ptr->ml))
+ {
+ /* Can't backstab creatures that we can't see, right? */
+ backstab = TRUE;
+ }
+ else if ((m_ptr->monfear) && (m_ptr->ml))
+ {
+ stab_fleeing = TRUE;
+ }
+ }
+
+ /* Disturb the monster */
+ m_ptr->csleep = 0;
+
+
+ /* Extract monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dont even bother */
+ if (r_ptr->flags7 & RF7_IM_MELEE)
+ {
+ msg_format("%^s is immune to melee attacks.");
+ return;
+ }
+
+ /* Auto-Recall if possible and visible */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Track a new monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+
+ /* Stop if friendly */
+ if ((is_friend(m_ptr) >= 0) &&
+ !(p_ptr->stun || p_ptr->confused || p_ptr->image ||
+ !(m_ptr->ml)))
+ {
+ if (!(p_ptr->inventory[INVEN_WIELD].art_name))
+ {
+ msg_format("You stop to avoid hitting %s.", m_name);
+ return;
+ }
+
+ if (!
+ (streq
+ (quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'")))
+ {
+ msg_format("You stop to avoid hitting %s.", m_name);
+ return;
+ }
+
+ msg_format("Your black blade greedily attacks %s!", m_name);
+ }
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+ /* Handle player fear */
+ if (p_ptr->afraid)
+ {
+ /* Message */
+ if (m_ptr->ml)
+ msg_format("You are too afraid to attack %s!", m_name);
+ else
+ msg_format("There is something scary in your way!");
+
+ /* Done */
+ return;
+ }
+
+ /* Monsters can use barehanded combat, but not weapon combat */
+ if ((p_ptr->body_monster) &&
+ (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON]) &&
+ !(p_ptr->melee_style == SKILL_HAND))
+ {
+ incarnate_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x);
+ }
+ /* Otherwise use your weapon(s) */
+ else
+ {
+ int weapons;
+ if (p_ptr->melee_style == SKILL_MASTERY)
+ weapons = r_info[p_ptr->body_monster].body_parts[BODY_WEAPON];
+ else /* SKILL_HAND */
+ weapons = 1;
+
+ /* Attack with ALL the weapons !!!!! -- ooh that's gonna hurt YOU */
+ for (weap = 0; weap < weapons; ++weap)
+ {
+ /* Monster is already dead ? oh :( */
+ if (mdeath) break;
+
+ /* Reset the blows counter */
+ num = 0;
+
+ /* Access the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + weap];
+
+ /* Calculate the "attack quality" */
+ bonus = p_ptr->to_h + p_ptr->to_h_melee + o_ptr->to_h;
+ chance = p_ptr->skill_thn + (bonus * BTH_PLUS_ADJ);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (!(f4 & TR4_NEVER_BLOW))
+ {
+ int num_blow = p_ptr->num_blow;
+
+ /* Restrict to max_blow(if max_blow >= 0) */
+ if ((max_blow >= 0) &&
+ (num_blow > max_blow)) num_blow = max_blow;
+
+ /* Attack once for each legal blow */
+ while (num++ < num_blow)
+ {
+ /* Test for hit */
+ if (test_hit_norm(chance, m_ptr->ac, m_ptr->ml))
+ {
+ /* Sound */
+ sound(SOUND_HIT);
+
+ /* Hack -- bare hands do one damage */
+ k = 1;
+
+ /* Select a chaotic effect (50% chance) */
+ if ((f1 & TR1_CHAOTIC) && (rand_int(2) == 0))
+ {
+ if (randint(5) < 3)
+ {
+ /* Vampiric (20%) */
+ chaos_effect = 1;
+ }
+ else if (rand_int(250) == 0)
+ {
+ /* Quake (0.12%) */
+ chaos_effect = 2;
+ }
+ else if (rand_int(10))
+ {
+ /* Confusion (26.892%) */
+ chaos_effect = 3;
+ }
+ else if (rand_int(2) == 0)
+ {
+ /* Teleport away (1.494%) */
+ chaos_effect = 4;
+ }
+ else
+ {
+ /* Polymorph (1.494%) */
+ chaos_effect = 5;
+ }
+ }
+
+ /* Vampiric drain */
+ if ((f1 & TR1_VAMPIRIC) || (chaos_effect == 1))
+ {
+ if (!
+ ((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_NONLIVING)))
+ drain_result = m_ptr->hp;
+ else
+ drain_result = 0;
+ }
+
+ if (f1 & TR1_VORPAL && (randint(6) == 1))
+ vorpal_cut = TRUE;
+ else
+ vorpal_cut = FALSE;
+
+ /* Should we attack with hands or not ? */
+ if (p_ptr->melee_style != SKILL_MASTERY)
+ {
+ py_attack_hand(&k, m_ptr, &special);
+ }
+ /* Handle normal weapon */
+ else if (o_ptr->k_idx)
+ {
+ k = damroll(o_ptr->dd, o_ptr->ds);
+ k = tot_dam_aux(o_ptr, k, m_ptr, &special);
+
+ if (backstab)
+ {
+ k += (k *
+ get_skill_scale(SKILL_BACKSTAB,
+ 100)) / 100;
+ }
+ else if (stab_fleeing)
+ {
+ k += (k * get_skill_scale(SKILL_BACKSTAB, 70)) /
+ 100;
+ }
+
+ if ((p_ptr->impact && ((k > 50) || randint(7) == 1))
+ || (chaos_effect == 2))
+ {
+ do_quake = TRUE;
+ }
+
+ k = critical_norm(o_ptr->weight, o_ptr->to_h, k, o_ptr->tval, &done_crit);
+
+ /* Stunning blow */
+ if (magik(get_skill(SKILL_STUN)) && (o_ptr->tval == TV_HAFTED) && (o_ptr->weight > 50) && done_crit)
+ {
+ if (!(r_ptr->flags4 & (RF4_BR_SOUN)) && !(r_ptr->flags4 & (RF4_BR_WALL)) && k)
+ {
+ int tmp;
+
+ /* Get stunned */
+ if (m_ptr->stunned)
+ {
+ msg_format("%^s is more dazed.", m_name);
+ tmp = m_ptr->stunned + get_skill_scale(SKILL_STUN, 30) + 10;
+ }
+ else
+ {
+ msg_format("%^s is dazed.", m_name);
+ tmp = get_skill_scale(SKILL_STUN, 60) + 20;
+ }
+
+ /* Apply stun */
+ m_ptr->stunned = (tmp < 200) ? tmp : 200;
+ }
+ }
+
+ if (vorpal_cut)
+ {
+ int step_k = k;
+
+ msg_format("Your weapon cuts deep into %s!",
+ m_name);
+ do
+ {
+ k += step_k;
+ }
+ while (randint(4) == 1);
+ }
+
+ PRAY_GOD(GOD_TULKAS)
+ {
+ if (magik(wisdom_scale(130) - m_ptr->level) && (p_ptr->grace > 1000))
+ {
+ msg_print("You feel the hand of Tulkas helping your blow.");
+ k += (o_ptr->to_d + p_ptr->to_d_melee) * 2;
+ }
+ else k += o_ptr->to_d + p_ptr->to_d_melee;
+ }
+ else k += o_ptr->to_d;
+
+ /* Project some more nasty stuff? */
+ if (p_ptr->tim_project)
+ {
+ project(0, p_ptr->tim_project_rad, y, x, p_ptr->tim_project_dam, p_ptr->tim_project_gf, p_ptr->tim_project_flag | PROJECT_JUMP);
+ if (!c_ptr->m_idx)
+ {
+ mdeath = TRUE;
+ break;
+ }
+ }
+
+ do_nazgul(&k, &num, num_blow, weap, r_ptr, o_ptr);
+
+ }
+
+ /* Melkor can cast curse for you*/
+ PRAY_GOD(GOD_MELKOR)
+ {
+ int lv = exec_lua("return get_level(MELKOR_CURSE, 100)");
+
+ if (lv >= 10)
+ {
+ int chance = (wisdom_scale(30) * lv) / ((m_ptr->level < 1) ? 1 : m_ptr->level);
+
+ if (chance < 1) chance = 1;
+ if ((p_ptr->grace > 5000) && magik(chance))
+ {
+ exec_lua(format("do_melkor_curse(%d)", c_ptr->m_idx));
+ }
+ }
+ }
+
+ /* May it clone the monster ? */
+ if ((f4 & TR4_CLONE) && magik(30))
+ {
+ msg_format("Oh no! Your weapon clones %^s!",
+ m_name);
+ multiply_monster(c_ptr->m_idx, FALSE, TRUE);
+ }
+
+ /* Apply the player damage bonuses */
+ k += p_ptr->to_d + p_ptr->to_d_melee;
+
+ /* No negative damage */
+ if (k < 0) k = 0;
+
+ /* Message */
+ if (!(backstab || stab_fleeing))
+ {
+ /* These monsters never have flavoured combat msgs */
+ if (strchr("vwjmelX,.*", r_ptr->d_char))
+ {
+ msg_format("You hit %s.", m_name);
+ }
+
+ /* Print flavoured messages if requested */
+ else
+ {
+ char buff[255];
+
+ flavored_attack((100 * k) / m_ptr->maxhp, buff);
+ msg_format(buff, m_name);
+ }
+ }
+ else if (backstab)
+ {
+ char buf[80];
+
+ monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego);
+
+ backstab = FALSE;
+
+ msg_format
+ ("You cruelly stab the helpless, sleeping %s!",
+ buf);
+ }
+ else
+ {
+ char buf[80];
+
+ monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego);
+
+ msg_format("You backstab the fleeing %s!", buf);
+ }
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.", k,
+ m_ptr->hp);
+ }
+
+ if (special) attack_special(m_ptr, special, k);
+
+ /* Damage, check for fear and death */
+ if (mon_take_hit(c_ptr->m_idx, k, &fear, NULL))
+ {
+ /* Hack -- High-level warriors can spread their attacks out
+ * among weaker foes.
+ */
+ if ((has_ability(AB_SPREAD_BLOWS)) && (num < num_blow) &&
+ (energy_use))
+ {
+ energy_use = energy_use * num / num_blow;
+ }
+ mdeath = TRUE;
+ break;
+ }
+
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+
+ touch_zap_player(m_ptr);
+
+ /* Are we draining it? A little note: If the monster is
+ dead, the drain does not work... */
+
+ if (drain_result)
+ {
+ drain_result -= m_ptr->hp; /* Calculate the difference */
+
+ if (drain_result > 0) /* Did we really hurt it? */
+ {
+ drain_heal = damroll(4, (drain_result / 6));
+
+ if (cheat_xtra)
+ {
+ msg_format("Draining left: %d", drain_left);
+ }
+
+ if (drain_left)
+ {
+ if (drain_heal < drain_left)
+ {
+ drain_left -= drain_heal;
+ }
+ else
+ {
+ drain_heal = drain_left;
+ drain_left = 0;
+ }
+
+ if (drain_msg)
+ {
+ msg_format
+ ("Your weapon drains life from %s!",
+ m_name);
+ drain_msg = FALSE;
+ }
+
+ hp_player(drain_heal);
+ /* We get to keep some of it! */
+ }
+ }
+ }
+
+ /* Confusion attack */
+ if ((p_ptr->confusing) || (chaos_effect == 3))
+ {
+ /* Cancel glowing hands */
+ if (p_ptr->confusing)
+ {
+ p_ptr->confusing = FALSE;
+ msg_print("Your hands stop glowing.");
+ }
+
+ /* Confuse the monster */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ msg_format("%^s is unaffected.", m_name);
+ }
+ else if (rand_int(100) < m_ptr->level)
+ {
+ msg_format("%^s is unaffected.", m_name);
+ }
+ else
+ {
+ msg_format("%^s appears confused.", m_name);
+ m_ptr->confused +=
+ 10 + rand_int(get_skill(SKILL_COMBAT)) / 5;
+ }
+ }
+
+ else if (chaos_effect == 4)
+ {
+ msg_format("%^s disappears!", m_name);
+ teleport_away(c_ptr->m_idx, 50);
+ num = num_blow + 1; /* Can't hit it anymore! */
+ }
+
+ else if ((chaos_effect == 5) && cave_floor_bold(y, x) &&
+ (randint(90) > m_ptr->level))
+ {
+ if (!((r_ptr->flags1 & RF1_UNIQUE) ||
+ (r_ptr->flags4 & RF4_BR_CHAO) ||
+ (m_ptr->mflag & MFLAG_QUEST)))
+ {
+ /* Handle polymorph */
+ if (do_poly_monster(y, x))
+ {
+ /* Polymorph succeeded */
+ msg_format("%^s changes!", m_name);
+
+ /* Hack -- Get new monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Oops, we need a different name... */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Hack -- Get new race */
+ r_ptr = race_inf(m_ptr);
+
+ fear = FALSE;
+ }
+ else
+ {
+ msg_format("%^s resists.", m_name);
+ }
+ }
+ else
+ {
+ msg_format("%^s is unaffected.", m_name);
+ }
+ }
+ }
+
+ /* Player misses */
+ else
+ {
+ /* Sound */
+ sound(SOUND_MISS);
+
+ backstab = FALSE; /* Clumsy! */
+
+ /* Message */
+ msg_format("You miss %s.", m_name);
+ }
+ }
+ }
+ else
+ {
+ msg_print("You can't attack with that weapon.");
+ }
+ }
+ }
+
+ /* Carried monster can attack too */
+ if ((!mdeath) && m_list[c_ptr->m_idx].hp)
+ carried_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x);
+
+ /* Hack -- delay fear messages */
+ if (fear && m_ptr->ml)
+ {
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+
+ /* Mega-Hack -- apply earthquake brand */
+ if (do_quake)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 10);
+ }
+}
+
+
+
+static bool_ pattern_tile(int y, int x)
+{
+ return ((cave[y][x].feat <= FEAT_PATTERN_XTRA2) &&
+ (cave[y][x].feat >= FEAT_PATTERN_START));
+}
+
+
+static bool_ pattern_seq(int c_y, int c_x, int n_y, int n_x)
+{
+ if (!(pattern_tile(c_y, c_x)) && !(pattern_tile(n_y, n_x)))
+ return TRUE;
+
+ if (cave[n_y][n_x].feat == FEAT_PATTERN_START)
+ {
+ if ((!(pattern_tile(c_y, c_x))) &&
+ !(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ {
+ if (get_check
+ ("If you start walking the Straight Road, you must walk the whole way. Ok? "))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else
+ return TRUE;
+ }
+ else if ((cave[n_y][n_x].feat == FEAT_PATTERN_OLD) ||
+ (cave[n_y][n_x].feat == FEAT_PATTERN_END) ||
+ (cave[n_y][n_x].feat == FEAT_PATTERN_XTRA2))
+ {
+ if (pattern_tile(c_y, c_x))
+ {
+ return TRUE;
+ }
+ else
+ {
+ msg_print
+ ("You must start walking the Straight Road from the startpoint.");
+ return FALSE;
+ }
+ }
+ else if ((cave[n_y][n_x].feat == FEAT_PATTERN_XTRA1) ||
+ (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA1))
+ {
+ return TRUE;
+ }
+ else if (cave[c_y][c_x].feat == FEAT_PATTERN_START)
+ {
+ if (pattern_tile(n_y, n_x))
+ return TRUE;
+ else
+ {
+ msg_print("You must walk the Straight Road in correct order.");
+ return FALSE;
+ }
+ }
+ else if ((cave[c_y][c_x].feat == FEAT_PATTERN_OLD) ||
+ (cave[c_y][c_x].feat == FEAT_PATTERN_END) ||
+ (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA2))
+ {
+ if (!pattern_tile(n_y, n_x))
+ {
+ msg_print("You may not step off from the Straight Road.");
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (!pattern_tile(c_y, c_x))
+ {
+ msg_print
+ ("You must start walking the Straight Road from the startpoint.");
+ return FALSE;
+ }
+ else
+ {
+ byte ok_move = FEAT_PATTERN_START;
+ switch (cave[c_y][c_x].feat)
+ {
+ case FEAT_PATTERN_1:
+ ok_move = FEAT_PATTERN_2;
+ break;
+ case FEAT_PATTERN_2:
+ ok_move = FEAT_PATTERN_3;
+ break;
+ case FEAT_PATTERN_3:
+ ok_move = FEAT_PATTERN_4;
+ break;
+ case FEAT_PATTERN_4:
+ ok_move = FEAT_PATTERN_1;
+ break;
+ default:
+ if (wizard)
+ msg_format("Funny Straight Road walking, %d.",
+ cave[c_y][c_x]);
+ return TRUE; /* Goof-up */
+ }
+
+ if ((cave[n_y][n_x].feat == ok_move) ||
+ (cave[n_y][n_x].feat == cave[c_y][c_x].feat))
+ return TRUE;
+ else
+ {
+ if (!pattern_tile(n_y, n_x))
+ msg_print("You may not step off from the Straight Road.");
+ else
+ msg_print
+ ("You must walk the Straight Road in correct order.");
+
+ return FALSE;
+ }
+ }
+ }
+}
+
+
+
+bool_ player_can_enter(byte feature)
+{
+ bool_ pass_wall;
+
+ bool_ only_wall = FALSE;
+
+
+ /* Player can not walk through "walls" unless in Shadow Form */
+ if (p_ptr->wraith_form || (PRACE_FLAG(PR1_SEMI_WRAITH)))
+ pass_wall = TRUE;
+ else
+ pass_wall = FALSE;
+
+ /* Wall mimicry force the player to stay in walls */
+ if (p_ptr->mimic_extra & CLASS_WALL)
+ {
+ only_wall = TRUE;
+ }
+
+ /* Don't let the player kill himself with one keystroke */
+ if (p_ptr->wild_mode)
+ {
+ if (feature == FEAT_DEEP_WATER)
+ {
+ int wt = weight_limit() / 2;
+
+ if ((calc_total_weight() >= wt) && !(p_ptr->ffall))
+ return (FALSE);
+ }
+ else if (feature == FEAT_SHAL_LAVA ||
+ feature == FEAT_DEEP_LAVA)
+ {
+ if (!(p_ptr->resist_fire ||
+ p_ptr->immune_fire ||
+ p_ptr->oppose_fire ||
+ p_ptr->ffall))
+ return (FALSE);
+ }
+ }
+
+ if (feature == FEAT_TREES)
+ {
+ if (p_ptr->fly ||
+ pass_wall ||
+ (has_ability(AB_TREE_WALK)) ||
+ (p_ptr->mimic_form == resolve_mimic_name("Ent")) ||
+ ((p_ptr->grace >= 9000) && (p_ptr->praying) && (p_ptr->pgod == GOD_YAVANNA)))
+ return (TRUE);
+ }
+
+ if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB))
+ return (TRUE);
+ if ((p_ptr->fly) &&
+ ((f_info[feature].flags1 & FF1_CAN_FLY) ||
+ (f_info[feature].flags1 & FF1_CAN_LEVITATE)))
+ return (TRUE);
+ else if (only_wall && (f_info[feature].flags1 & FF1_FLOOR))
+ return (FALSE);
+ else if ((p_ptr->ffall) &&
+ (f_info[feature].flags1 & FF1_CAN_LEVITATE))
+ return (TRUE);
+ else if ((pass_wall || only_wall) &&
+ (f_info[feature].flags1 & FF1_CAN_PASS))
+ return (TRUE);
+ else if (f_info[feature].flags1 & FF1_NO_WALK)
+ return (FALSE);
+ else if ((f_info[feature].flags1 & FF1_WEB) &&
+ ((!(r_info[p_ptr->body_monster].flags7 & RF7_SPIDER)) && (p_ptr->mimic_form != resolve_mimic_name("Spider"))))
+ return (FALSE);
+
+ return (TRUE);
+}
+
+/*
+ * Move player in the given direction, with the given "pickup" flag.
+ *
+ * This routine should (probably) always induce energy expenditure.
+ *
+ * Note that moving will *always* take a turn, and will *always* hit
+ * any monster which might be in the destination grid. Previously,
+ * moving into walls was "free" and did NOT hit invisible monsters.
+ */
+void move_player_aux(int dir, int do_pickup, int run, bool_ disarm)
+{
+ int y, x, tmp;
+
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ monster_type *m_ptr;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster], *mr_ptr;
+
+ char m_name[80];
+
+ bool_ stormbringer = FALSE;
+
+ bool_ old_dtrap, new_dtrap;
+
+ bool_ oktomove = TRUE;
+
+
+ /* Hack - random movement */
+ if (p_ptr->disembodied)
+ tmp = dir;
+ else if ((r_ptr->flags1 & RF1_RAND_25) && (r_ptr->flags1 & RF1_RAND_50))
+ {
+ if (randint(100) < 75)
+ tmp = randint(9);
+ else
+ tmp = dir;
+ }
+ else if (r_ptr->flags1 & RF1_RAND_50)
+ {
+ if (randint(100) < 50)
+ tmp = randint(9);
+ else
+ tmp = dir;
+ }
+ else if (r_ptr->flags1 & RF1_RAND_25)
+ {
+ if (randint(100) < 25)
+ tmp = randint(9);
+ else
+ tmp = dir;
+ }
+ else
+ {
+ tmp = dir;
+ }
+
+ if ((c_ptr->feat == FEAT_ICE) && (!p_ptr->ffall && !p_ptr->fly))
+ {
+ if (magik(70 - p_ptr->lev))
+ {
+ tmp = randint(9);
+ msg_print("You slip on the icy floor.");
+ }
+ else
+ tmp = dir;
+ }
+
+ /* Find the result of moving */
+ y = p_ptr->py + ddy[tmp];
+ x = p_ptr->px + ddx[tmp];
+
+ /* Examine the destination */
+ c_ptr = &cave[y][x];
+
+ /* Change oldpx and oldpy to place the player well when going back to big mode */
+ if (p_ptr->wild_mode)
+ {
+ if (ddy[tmp] > 0) p_ptr->oldpy = 1;
+ if (ddy[tmp] < 0) p_ptr->oldpy = MAX_HGT - 2;
+ if (ddy[tmp] == 0) p_ptr->oldpy = MAX_HGT / 2;
+ if (ddx[tmp] > 0) p_ptr->oldpx = 1;
+ if (ddx[tmp] < 0) p_ptr->oldpx = MAX_WID - 2;
+ if (ddx[tmp] == 0) p_ptr->oldpx = MAX_WID / 2;
+ }
+
+ /* Exit the area */
+ if (!dun_level && !p_ptr->wild_mode && !is_quest(dun_level) &&
+ ((x == 0) || (x == cur_wid - 1) || (y == 0) || (y == cur_hgt - 1)))
+ {
+ /* Can the player enter the grid? */
+ if (player_can_enter(c_ptr->mimic))
+ {
+ /* Hack: move to new area */
+ if ((y == 0) && (x == 0))
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->wilderness_x--;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = cur_wid - 2;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == 0) && (x == MAX_WID - 1))
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->wilderness_x++;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = 1;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == MAX_HGT - 1) && (x == 0))
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->wilderness_x--;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = cur_wid - 2;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1))
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->wilderness_x++;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = 1;
+ ambush_flag = FALSE;
+ }
+
+ else if (y == 0)
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = x;
+ ambush_flag = FALSE;
+ }
+
+ else if (y == cur_hgt - 1)
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = x;
+ ambush_flag = FALSE;
+ }
+
+ else if (x == 0)
+ {
+ p_ptr->wilderness_x--;
+ p_ptr->oldpx = cur_wid - 2;
+ p_ptr->oldpy = y;
+ ambush_flag = FALSE;
+ }
+
+ else if (x == cur_wid - 1)
+ {
+ p_ptr->wilderness_x++;
+ p_ptr->oldpx = 1;
+ p_ptr->oldpy = y;
+ ambush_flag = FALSE;
+ }
+
+ p_ptr->leaving = TRUE;
+
+ return;
+ }
+ }
+
+ /* Some hooks */
+ if (process_hooks(HOOK_MOVE, "(d,d)", y, x)) return;
+
+ /* Get the monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+ mr_ptr = race_inf(m_ptr);
+
+ if (p_ptr->inventory[INVEN_WIELD].art_name)
+ {
+ if (streq(quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'"))
+ stormbringer = TRUE;
+ }
+
+ /* Hack -- attack monsters */
+ if (c_ptr->m_idx && (m_ptr->ml || player_can_enter(c_ptr->feat)))
+ {
+
+ /* Attack -- only if we can see it OR it is not in a wall */
+ if ((is_friend(m_ptr) > 0) &&
+ !(p_ptr->confused || p_ptr->image || !(m_ptr->ml) || p_ptr->stun) &&
+ (pattern_seq(p_ptr->py, p_ptr->px, y, x)) &&
+ ((player_can_enter(cave[y][x].feat))))
+ {
+ m_ptr->csleep = 0;
+
+ /* Extract monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Auto-Recall if possible and visible */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Track a new monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+
+ /* displace? */
+ if (stormbringer && (randint(1000) > 666))
+ {
+ py_attack(y, x, -1);
+ }
+ else if (cave_floor_bold(p_ptr->py, p_ptr->px) ||
+ (mr_ptr->flags2 & RF2_PASS_WALL))
+ {
+ msg_format("You push past %s.", m_name);
+ m_ptr->fy = p_ptr->py;
+ m_ptr->fx = p_ptr->px;
+ cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx;
+ c_ptr->m_idx = 0;
+ update_mon(cave[p_ptr->py][p_ptr->px].m_idx, TRUE);
+ }
+ else
+ {
+ msg_format("%^s is in your way!", m_name);
+ energy_use = 0;
+ oktomove = FALSE;
+ }
+
+ /* now continue on to 'movement' */
+ }
+ else
+ {
+ py_attack(y, x, -1);
+ oktomove = FALSE;
+ }
+ }
+
+ else if ((c_ptr->feat == FEAT_DARK_PIT) && !p_ptr->ffall)
+ {
+ msg_print("You can't cross the chasm.");
+ running = 0;
+ oktomove = FALSE;
+ }
+
+ /* Disarm a visible trap */
+ else if (easy_disarm && disarm && (c_ptr->info & (CAVE_TRDT)))
+ {
+ (void)do_cmd_disarm_aux(y, x, tmp, do_pickup);
+ return;
+ }
+
+ /* Don't step on known traps. */
+ else if (disarm && (c_ptr->info & (CAVE_TRDT)) && !(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ {
+ msg_print("You stop to avoid triggering the trap.");
+ energy_use = 0;
+ oktomove = FALSE;
+ }
+
+ /* Player can't enter ? soo bad for him/her ... */
+ else if (!player_can_enter(c_ptr->feat))
+ {
+ oktomove = FALSE;
+
+ /* Disturb the player */
+ disturb(0, 0);
+
+ if (p_ptr->prob_travel)
+ {
+ if (passwall(tmp, TRUE)) return;
+ }
+
+ /* Notice things in the dark */
+ if (!(c_ptr->info & (CAVE_MARK)) && !(c_ptr->info & (CAVE_SEEN)))
+ {
+ /* Rubble */
+ if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ msg_print("You feel some rubble blocking your way.");
+ c_ptr->info |= (CAVE_MARK);
+ lite_spot(y, x);
+ }
+
+ /* Closed door */
+ else if (c_ptr->feat < FEAT_SECRET)
+ {
+ msg_print("You feel a closed door blocking your way.");
+ c_ptr->info |= (CAVE_MARK);
+ lite_spot(y, x);
+ }
+
+ /* Wall (or secret door) */
+ else
+ {
+ int feat;
+
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else
+ feat = f_info[c_ptr->feat].mimic;
+
+ msg_format("You feel %s.", f_text + f_info[feat].block);
+ c_ptr->info |= (CAVE_MARK);
+ lite_spot(y, x);
+ }
+ }
+
+ /* Notice things */
+ else
+ {
+ /* Rubble */
+ if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ if (!easy_tunnel)
+ {
+ msg_print("There is rubble blocking your way.");
+
+ if (!(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ energy_use = 0;
+ /*
+ * Well, it makes sense that you lose time bumping into
+ * a wall _if_ you are confused, stunned or blind; but
+ * typing mistakes should not cost you a turn...
+ */
+ }
+ else
+ {
+ do_cmd_tunnel_aux(y, x, dir);
+ return;
+ }
+ }
+ /* Closed doors */
+ else if ((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ if (easy_open)
+ {
+ if (easy_open_door(y, x)) return;
+ }
+ else
+ {
+ msg_print("There is a closed door blocking your way.");
+
+ if (!(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ energy_use = 0;
+ }
+ }
+
+ /* Wall (or secret door) */
+ else
+ {
+ if (!easy_tunnel)
+ {
+ int feat;
+
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else
+ feat = f_info[c_ptr->feat].mimic;
+
+ msg_format("There is %s.", f_text + f_info[feat].block);
+
+ if (!(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ energy_use = 0;
+ }
+ else
+ {
+ do_cmd_tunnel_aux(y, x, dir);
+ return;
+ }
+ }
+ }
+
+ /* Sound */
+ sound(SOUND_HITWALL);
+ }
+
+ /* Normal movement */
+ if (!pattern_seq(p_ptr->py, p_ptr->px, y, x))
+ {
+ if (!(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ {
+ energy_use = 0;
+ }
+
+ disturb(0, 0); /* To avoid a loop with running */
+
+ oktomove = FALSE;
+ }
+
+
+ /*
+ * Check trap detection status -- retrieve them here
+ * because they are used by the movement code as well
+ */
+ old_dtrap = ((cave[p_ptr->py][p_ptr->px].info & CAVE_DETECT) != 0);
+ new_dtrap = ((cave[y][x].info & CAVE_DETECT) != 0);
+
+ /* Normal movement */
+ if (oktomove && running && disturb_detect)
+ {
+ /*
+ * Disturb the player when about to leave the trap detected
+ * area
+ */
+ if (old_dtrap && !new_dtrap)
+ {
+ /* Disturb player */
+ disturb(0, 0);
+
+ /* but don't take a turn */
+ energy_use = 0;
+
+ /* Tell player why */
+ cmsg_print(TERM_VIOLET, "You are about to leave a trap detected zone.");
+ /* Flush */
+ /* msg_print(NULL); */
+
+ oktomove = FALSE;
+ }
+ }
+
+ /* Normal movement */
+ if (oktomove)
+ {
+ int oy, ox;
+ int feat;
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ /* Save old location */
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ if (cave[p_ptr->py][p_ptr->px].mimic) feat = cave[p_ptr->py][p_ptr->px].mimic;
+ else
+ feat = cave[p_ptr->py][p_ptr->px].feat;
+
+ /* Some hooks */
+ if (process_hooks(HOOK_MOVED, "(d,d)", oy, ox)) return;
+
+ /* Redraw new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Redraw old spot */
+ lite_spot(oy, ox);
+
+ /* Sound */
+ /* sound(SOUND_WALK); */
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Check detection status */
+ if (old_dtrap && !new_dtrap)
+ {
+ cmsg_print(TERM_VIOLET, "You leave a trap detected zone.");
+ if (running) msg_print(NULL);
+ p_ptr->redraw |= (PR_DTRAP);
+ }
+ else if (!old_dtrap && new_dtrap)
+ {
+ cmsg_print(TERM_L_BLUE, "You enter a trap detected zone.");
+ if (running) msg_print(NULL);
+ p_ptr->redraw |= (PR_DTRAP);
+ }
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Window stuff */
+ if (!run) p_ptr->window |= (PW_OVERHEAD);
+
+ /* Some feature descs */
+ if (f_info[cave[p_ptr->py][p_ptr->px].feat].text > 1)
+ {
+ /* Mega-hack for dungeon branches */
+ if ((feat == FEAT_MORE) && c_ptr->special)
+ {
+ msg_format("There is %s", d_text + d_info[c_ptr->special].text);
+ }
+ else
+ {
+ msg_print(f_text + f_info[feat].text);
+ }
+
+ /* Flush message while running */
+ if (running) msg_print(NULL);
+ }
+
+ /* Spontaneous Searching */
+ if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos)))
+ {
+ search();
+ }
+
+ /* Continuous Searching */
+ if (p_ptr->searching)
+ {
+ search();
+ }
+
+ /* Handle "objects" */
+ carry(do_pickup);
+
+ /* Handle "store doors" */
+ if (c_ptr->feat == FEAT_SHOP)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- Enter store */
+ command_new = '_';
+ }
+
+ else if (cave[y][x].feat >= FEAT_ALTAR_HEAD &&
+ cave[y][x].feat <= FEAT_ALTAR_TAIL)
+ {
+ cptr name = f_name + f_info[cave[y][x].feat].name;
+ cptr pref = (is_a_vowel(name[0])) ? "an" : "a";
+
+ msg_format("You see %s %s.", pref, name);
+
+ /* Flush message while running */
+ if (running) msg_print(NULL);
+ }
+
+ /* Discover invisible traps */
+ else if ((c_ptr->t_idx != 0) &&
+ !(f_info[cave[y][x].feat].flags1 & FF1_DOOR))
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ if (!(c_ptr->info & (CAVE_TRDT)))
+ {
+ /* Message */
+ msg_print("You found a trap!");
+
+ /* Pick a trap */
+ pick_trap(p_ptr->py, p_ptr->px);
+ }
+
+ /* Hit the trap */
+ hit_trap();
+ }
+
+ /* Execute the inscription */
+ else if (c_ptr->inscription)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ msg_format("There is an inscription here: %s",
+ inscription_info[c_ptr->inscription].text);
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_WALK)
+ {
+ execute_inscription(c_ptr->inscription, p_ptr->py, p_ptr->px);
+ }
+ }
+ }
+
+ /* Update wilderness knowledge */
+ if (p_ptr->wild_mode)
+ {
+ if (wizard) msg_format("y:%d, x:%d", p_ptr->py, p_ptr->px);
+
+ /* Update the known wilderness */
+ reveal_wilderness_around_player(p_ptr->py, p_ptr->px, 0, WILDERNESS_SEE_RADIUS);
+
+ /* Walking the wild isnt meaningfull */
+ p_ptr->did_nothing = TRUE;
+ }
+}
+
+void move_player(int dir, int do_pickup, bool_ disarm)
+{
+ move_player_aux(dir, do_pickup, 0, disarm);
+}
+
+
+/*
+ * Hack -- Grid-based version of see_obstacle
+ */
+static int see_obstacle_grid(cave_type *c_ptr)
+{
+ /*
+ * Hack -- Avoid hitting detected traps, because we cannot rely on
+ * the CAVE_MARK check below, and traps can be set to nearly
+ * everything the player can move on to XXX XXX XXX
+ */
+ if (c_ptr->info & (CAVE_TRDT)) return (TRUE);
+
+
+ /* Hack -- Handle special cases XXX XXX */
+ switch (c_ptr->feat)
+ {
+ /* Require levitation */
+ case FEAT_DARK_PIT:
+ case FEAT_DEEP_WATER:
+ case FEAT_ICE:
+ {
+ if (p_ptr->ffall || p_ptr->fly) return (FALSE);
+ }
+
+ /* Require immunity */
+ case FEAT_DEEP_LAVA:
+ case FEAT_SHAL_LAVA:
+ {
+ if (p_ptr->invuln || p_ptr->immune_fire) return (FALSE);
+ }
+ }
+
+
+ /* "Safe" floor grids aren't obstacles */
+ if (f_info[c_ptr->feat].flags1 & FF1_CAN_RUN) return (FALSE);
+
+ /* Must be known to the player */
+ if (!(c_ptr->info & (CAVE_MARK))) return (FALSE);
+
+ /* Default */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- Check for a "known wall" or "dangerous" feature (see below)
+ */
+static int see_obstacle(int dir, int y, int x)
+{
+ /* Get the new location */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Illegal grids are not known walls */
+ if (!in_bounds2(y, x)) return (FALSE);
+
+ /* Analyse the grid */
+ return (see_obstacle_grid(&cave[y][x]));
+}
+
+
+/*
+ * Hack -- Check for an "unknown corner" (see below)
+ */
+static int see_nothing(int dir, int y, int x)
+{
+ /* Get the new location */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Illegal grids are unknown */
+ if (!in_bounds2(y, x)) return (TRUE);
+
+ /* Memorized grids are always known */
+ if (cave[y][x].info & (CAVE_MARK)) return (FALSE);
+
+ /* Non-floor grids are unknown */
+ if (!cave_floor_bold(y, x)) return (TRUE);
+
+ /* Viewable door/wall grids are known */
+ if (player_can_see_bold(y, x)) return (FALSE);
+
+ /* Default */
+ return (TRUE);
+}
+
+
+
+
+
+/*
+ * The running algorithm: -CJS-
+ *
+ * In the diagrams below, the player has just arrived in the
+ * grid marked as '@', and he has just come from a grid marked
+ * as 'o', and he is about to enter the grid marked as 'x'.
+ *
+ * Of course, if the "requested" move was impossible, then you
+ * will of course be blocked, and will stop.
+ *
+ * Overview: You keep moving until something interesting happens.
+ * If you are in an enclosed space, you follow corners. This is
+ * the usual corridor scheme. If you are in an open space, you go
+ * straight, but stop before entering enclosed space. This is
+ * analogous to reaching doorways. If you have enclosed space on
+ * one side only (that is, running along side a wall) stop if
+ * your wall opens out, or your open space closes in. Either case
+ * corresponds to a doorway.
+ *
+ * What happens depends on what you can really SEE. (i.e. if you
+ * have no light, then running along a dark corridor is JUST like
+ * running in a dark room.) The algorithm works equally well in
+ * corridors, rooms, mine tailings, earthquake rubble, etc, etc.
+ *
+ * These conditions are kept in static memory:
+ * find_openarea You are in the open on at least one
+ * side.
+ * find_breakleft You have a wall on the left, and will
+ * stop if it opens
+ * find_breakright You have a wall on the right, and will
+ * stop if it opens
+ *
+ * To initialize these conditions, we examine the grids adjacent
+ * to the grid marked 'x', two on each side (marked 'L' and 'R').
+ * If either one of the two grids on a given side is seen to be
+ * closed, then that side is considered to be closed. If both
+ * sides are closed, then it is an enclosed (corridor) run.
+ *
+ * LL L
+ * @x LxR
+ * RR @R
+ *
+ * Looking at more than just the immediate squares is
+ * significant. Consider the following case. A run along the
+ * corridor will stop just before entering the center point,
+ * because a choice is clearly established. Running in any of
+ * three available directions will be defined as a corridor run.
+ * Note that a minor hack is inserted to make the angled corridor
+ * entry (with one side blocked near and the other side blocked
+ * further away from the runner) work correctly. The runner moves
+ * diagonally, but then saves the previous direction as being
+ * straight into the gap. Otherwise, the tail end of the other
+ * entry would be perceived as an alternative on the next move.
+ *
+ * #.#
+ * ##.##
+ * .@x..
+ * ##.##
+ * #.#
+ *
+ * Likewise, a run along a wall, and then into a doorway (two
+ * runs) will work correctly. A single run rightwards from @ will
+ * stop at 1. Another run right and down will enter the corridor
+ * and make the corner, stopping at the 2.
+ *
+ * #@x 1
+ * ########### ######
+ * 2 #
+ * #############
+ * #
+ *
+ * After any move, the function area_affect is called to
+ * determine the new surroundings, and the direction of
+ * subsequent moves. It examines the current player location
+ * (at which the runner has just arrived) and the previous
+ * direction (from which the runner is considered to have come).
+ *
+ * Moving one square in some direction places you adjacent to
+ * three or five new squares (for straight and diagonal moves
+ * respectively) to which you were not previously adjacent,
+ * marked as '!' in the diagrams below.
+ *
+ * ...! ...
+ * .o@! .o.!
+ * ...! ..@!
+ * !!!
+ *
+ * You STOP if any of the new squares are interesting in any way:
+ * for example, if they contain visible monsters or treasure.
+ *
+ * You STOP if any of the newly adjacent squares seem to be open,
+ * and you are also looking for a break on that side. (that is,
+ * find_openarea AND find_break).
+ *
+ * You STOP if any of the newly adjacent squares do NOT seem to be
+ * open and you are in an open area, and that side was previously
+ * entirely open.
+ *
+ * Corners: If you are not in the open (i.e. you are in a corridor)
+ * and there is only one way to go in the new squares, then turn in
+ * that direction. If there are more than two new ways to go, STOP.
+ * If there are two ways to go, and those ways are separated by a
+ * square which does not seem to be open, then STOP.
+ *
+ * Otherwise, we have a potential corner. There are two new open
+ * squares, which are also adjacent. One of the new squares is
+ * diagonally located, the other is straight on (as in the diagram).
+ * We consider two more squares further out (marked below as ?).
+ *
+ * We assign "option" to the straight-on grid, and "option2" to the
+ * diagonal grid, and "check_dir" to the grid marked 's'.
+ *
+ * .s
+ * @x?
+ * #?
+ *
+ * If they are both seen to be closed, then it is seen that no
+ * benefit is gained from moving straight. It is a known corner.
+ * To cut the corner, go diagonally, otherwise go straight, but
+ * pretend you stepped diagonally into that next location for a
+ * full view next time. Conversely, if one of the ? squares is
+ * not seen to be closed, then there is a potential choice. We check
+ * to see whether it is a potential corner or an intersection/room entrance.
+ * If the square two spaces straight ahead, and the space marked with 's'
+ * are both blank, then it is a potential corner and enter if find_examine
+ * is set, otherwise must stop because it is not a corner.
+ */
+
+
+
+
+/*
+ * Hack -- allow quick "cycling" through the legal directions
+ */
+static byte cycle[] = { 1, 2, 3, 6, 9, 8, 7, 4, 1, 2, 3, 6, 9, 8, 7, 4, 1 };
+
+/*
+ * Hack -- map each direction into the "middle" of the "cycle[]" array
+ */
+static byte chome[] = { 0, 8, 9, 10, 7, 0, 11, 6, 5, 4 };
+
+/*
+ * The direction we are running
+ */
+static byte find_current;
+
+/*
+ * The direction we came from
+ */
+static byte find_prevdir;
+
+/*
+ * We are looking for open area
+ */
+static bool_ find_openarea;
+
+/*
+ * We are looking for a break
+ */
+static bool_ find_breakright;
+static bool_ find_breakleft;
+
+
+
+/*
+ * Initialize the running algorithm for a new direction.
+ *
+ * Diagonal Corridor -- allow diaginal entry into corridors.
+ *
+ * Blunt Corridor -- If there is a wall two spaces ahead and
+ * we seem to be in a corridor, then force a turn into the side
+ * corridor, must be moving straight into a corridor here. ???
+ *
+ * Diagonal Corridor Blunt Corridor (?)
+ * # # #
+ * #x# @x#
+ * @p. p
+ */
+static void run_init(int dir)
+{
+ int row, col, deepleft, deepright;
+
+ int i, shortleft, shortright;
+
+
+ /* Save the direction */
+ find_current = dir;
+
+ /* Assume running straight */
+ find_prevdir = dir;
+
+ /* Assume looking for open area */
+ find_openarea = TRUE;
+
+ /* Assume not looking for breaks */
+ find_breakright = find_breakleft = FALSE;
+
+ /* Assume no nearby walls */
+ deepleft = deepright = FALSE;
+ shortright = shortleft = FALSE;
+
+ /* Find the destination grid */
+ row = p_ptr->py + ddy[dir];
+ col = p_ptr->px + ddx[dir];
+
+ /* Extract cycle index */
+ i = chome[dir];
+
+ /* Check for walls */
+ if (see_obstacle(cycle[i + 1], p_ptr->py, p_ptr->px))
+ {
+ find_breakleft = TRUE;
+ shortleft = TRUE;
+ }
+ else if (see_obstacle(cycle[i + 1], row, col))
+ {
+ find_breakleft = TRUE;
+ deepleft = TRUE;
+ }
+
+ /* Check for walls */
+ if (see_obstacle(cycle[i - 1], p_ptr->py, p_ptr->px))
+ {
+ find_breakright = TRUE;
+ shortright = TRUE;
+ }
+ else if (see_obstacle(cycle[i - 1], row, col))
+ {
+ find_breakright = TRUE;
+ deepright = TRUE;
+ }
+
+ /* Looking for a break */
+ if (find_breakleft && find_breakright)
+ {
+ /* Not looking for open area */
+ find_openarea = FALSE;
+
+ /* Hack -- allow angled corridor entry */
+ if (dir & 0x01)
+ {
+ if (deepleft && !deepright)
+ {
+ find_prevdir = cycle[i - 1];
+ }
+ else if (deepright && !deepleft)
+ {
+ find_prevdir = cycle[i + 1];
+ }
+ }
+
+ /* Hack -- allow blunt corridor entry */
+ else if (see_obstacle(cycle[i], row, col))
+ {
+ if (shortleft && !shortright)
+ {
+ find_prevdir = cycle[i - 2];
+ }
+ else if (shortright && !shortleft)
+ {
+ find_prevdir = cycle[i + 2];
+ }
+ }
+ }
+}
+
+
+/*
+ * Update the current "run" path
+ *
+ * Return TRUE if the running should be stopped
+ */
+static bool_ run_test(void)
+{
+ int prev_dir, new_dir, check_dir = 0;
+
+ int row, col;
+
+ int i, max, inv;
+
+ int option = 0, option2 = 0;
+
+ cave_type *c_ptr;
+
+
+ /* Where we came from */
+ prev_dir = find_prevdir;
+
+
+ /* Range of newly adjacent grids */
+ max = (prev_dir & 0x01) + 1;
+
+
+ /* Look at every newly adjacent square. */
+ for (i = -max; i <= max; i++)
+ {
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* New direction */
+ new_dir = cycle[chome[prev_dir] + i];
+
+ /* New location */
+ row = p_ptr->py + ddy[new_dir];
+ col = p_ptr->px + ddx[new_dir];
+
+ /* Access grid */
+ c_ptr = &cave[row][col];
+
+
+ /* Visible monsters abort running */
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Visible monster */
+ if (m_ptr->ml) return (TRUE);
+ }
+
+ /* Visible objects abort running */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Visible object */
+ if (o_ptr->marked) return (TRUE);
+ }
+
+
+ /* Assume unknown */
+ inv = TRUE;
+
+ /* Check memorized grids */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ bool_ notice = TRUE;
+
+ /*
+ * Examine the terrain -- conditional disturbance
+ * If we had more flags, we could make these customisable too
+ */
+ switch (c_ptr->feat)
+ {
+ case FEAT_DEEP_LAVA:
+ case FEAT_SHAL_LAVA:
+ {
+ /* Ignore */
+ if (p_ptr->invuln || p_ptr->immune_fire) notice = FALSE;
+
+ /* Done */
+ break;
+ }
+
+ case FEAT_DEEP_WATER:
+ case FEAT_ICE:
+ {
+ /* Ignore */
+ if (p_ptr->ffall || p_ptr->fly) notice = FALSE;
+
+ /* Done */
+ break;
+ }
+
+ /* Open doors */
+ case FEAT_OPEN:
+ case FEAT_BROKEN:
+ {
+ /* Option -- ignore */
+ if (find_ignore_doors) notice = FALSE;
+
+ /* Done */
+ break;
+ }
+
+ /*
+ * Stairs - too many of them, should find better ways to
+ * handle them (not scripting!, because it can be called
+ * from within the running algo) XXX XXX XXX
+ */
+ case FEAT_LESS:
+ case FEAT_MORE:
+ case FEAT_QUEST_ENTER:
+ case FEAT_QUEST_EXIT:
+ case FEAT_QUEST_DOWN:
+ case FEAT_QUEST_UP:
+ case FEAT_SHAFT_UP:
+ case FEAT_SHAFT_DOWN:
+ case FEAT_WAY_LESS:
+ case FEAT_WAY_MORE:
+ /* XXX */
+ case FEAT_BETWEEN:
+ case FEAT_BETWEEN2:
+ {
+ /* Option -- ignore */
+ if (find_ignore_stairs) notice = FALSE;
+
+ /* Done */
+ break;
+ }
+ }
+
+ /* Check the "don't notice running" flag */
+ if (f_info[c_ptr->feat].flags1 & FF1_DONT_NOTICE_RUNNING)
+ {
+ notice = FALSE;
+ }
+
+ /* A detected trap is interesting */
+ if (c_ptr->info & (CAVE_TRDT)) notice = TRUE;
+
+ /* Interesting feature */
+ if (notice) return (TRUE);
+
+ /* The grid is "visible" */
+ inv = FALSE;
+ }
+
+ /* Mega-Hack -- Maze code removes CAVE_MARK XXX XXX XXX */
+ if (c_ptr->info & (CAVE_TRDT)) return (TRUE);
+
+ /* Analyze unknown grids and floors */
+ if (inv || cave_floor_bold(row, col))
+ {
+ /* Looking for open area */
+ if (find_openarea)
+ {
+ /* Nothing */
+ }
+
+ /* The first new direction. */
+ else if (!option)
+ {
+ option = new_dir;
+ }
+
+ /* Three new directions. Stop running. */
+ else if (option2)
+ {
+ return (TRUE);
+ }
+
+ /* Two non-adjacent new directions. Stop running. */
+ else if (option != cycle[chome[prev_dir] + i - 1])
+ {
+ return (TRUE);
+ }
+
+ /* Two new (adjacent) directions (case 1) */
+ else if (new_dir & 0x01)
+ {
+ check_dir = cycle[chome[prev_dir] + i - 2];
+ option2 = new_dir;
+ }
+
+ /* Two new (adjacent) directions (case 2) */
+ else
+ {
+ check_dir = cycle[chome[prev_dir] + i + 1];
+ option2 = option;
+ option = new_dir;
+ }
+ }
+
+ /* Obstacle, while looking for open area */
+ else
+ {
+ if (find_openarea)
+ {
+ if (i < 0)
+ {
+ /* Break to the right */
+ find_breakright = TRUE;
+ }
+
+ else if (i > 0)
+ {
+ /* Break to the left */
+ find_breakleft = TRUE;
+ }
+ }
+ }
+ }
+
+
+ /* Looking for open area */
+ if (find_openarea)
+ {
+ /* Hack -- look again */
+ for (i = -max; i < 0; i++)
+ {
+ new_dir = cycle[chome[prev_dir] + i];
+
+ row = p_ptr->py + ddy[new_dir];
+ col = p_ptr->px + ddx[new_dir];
+
+ /* Access grid */
+ c_ptr = &cave[row][col];
+
+ /* Unknown grids or non-obstacle */
+ if (!see_obstacle_grid(c_ptr))
+ {
+ /* Looking to break right */
+ if (find_breakright)
+ {
+ return (TRUE);
+ }
+ }
+
+ /* Obstacle */
+ else
+ {
+ /* Looking to break left */
+ if (find_breakleft)
+ {
+ return (TRUE);
+ }
+ }
+ }
+
+ /* Hack -- look again */
+ for (i = max; i > 0; i--)
+ {
+ new_dir = cycle[chome[prev_dir] + i];
+
+ row = p_ptr->py + ddy[new_dir];
+ col = p_ptr->px + ddx[new_dir];
+
+ /* Access grid */
+ c_ptr = &cave[row][col];
+
+ /* Unknown grid or non-obstacle */
+ if (!see_obstacle_grid(c_ptr))
+ {
+ /* Looking to break left */
+ if (find_breakleft)
+ {
+ return (TRUE);
+ }
+ }
+
+ /* Obstacle */
+ else
+ {
+ /* Looking to break right */
+ if (find_breakright)
+ {
+ return (TRUE);
+ }
+ }
+ }
+ }
+
+
+ /* Not looking for open area */
+ else
+ {
+ /* No options */
+ if (!option)
+ {
+ return (TRUE);
+ }
+
+ /* One option */
+ else if (!option2)
+ {
+ /* Primary option */
+ find_current = option;
+
+ /* No other options */
+ find_prevdir = option;
+ }
+
+ /* Two options, examining corners */
+ else if (find_examine && !find_cut)
+ {
+ /* Primary option */
+ find_current = option;
+
+ /* Hack -- allow curving */
+ find_prevdir = option2;
+ }
+
+ /* Two options, pick one */
+ else
+ {
+ /* Get next location */
+ row = p_ptr->py + ddy[option];
+ col = p_ptr->px + ddx[option];
+
+ /* Don't see that it is closed off. */
+ /* This could be a potential corner or an intersection. */
+ if (!see_obstacle(option, row, col) || !see_obstacle(check_dir, row, col))
+ {
+ /* Can not see anything ahead and in the direction we */
+ /* are turning, assume that it is a potential corner. */
+ if (find_examine &&
+ see_nothing(option, row, col) &&
+ see_nothing(option2, row, col))
+ {
+ find_current = option;
+ find_prevdir = option2;
+ }
+
+ /* STOP: we are next to an intersection or a room */
+ else
+ {
+ return (TRUE);
+ }
+ }
+
+ /* This corner is seen to be enclosed; we cut the corner. */
+ else if (find_cut)
+ {
+ find_current = option2;
+ find_prevdir = option2;
+ }
+
+ /* This corner is seen to be enclosed, and we */
+ /* deliberately go the long way. */
+ else
+ {
+ find_current = option;
+ find_prevdir = option2;
+ }
+ }
+ }
+
+
+ /* About to hit a known wall, stop */
+ if (see_obstacle(find_current, p_ptr->py, p_ptr->px))
+ {
+ return (TRUE);
+ }
+
+
+ /* Failure */
+ return (FALSE);
+}
+
+
+
+/*
+ * Take one step along the current "run" path
+ */
+void run_step(int dir)
+{
+ /* Start running */
+ if (dir)
+ {
+ /* Hack -- do not start silly run */
+ if (see_obstacle(dir, p_ptr->py, p_ptr->px) &&
+ (cave[p_ptr->py + ddy[dir]][p_ptr->px + ddx[dir]].feat != FEAT_TREES))
+ {
+ /* Message */
+ msg_print("You cannot run in that direction.");
+
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Done */
+ return;
+ }
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Initialize */
+ run_init(dir);
+ }
+
+ /* Keep running */
+ else
+ {
+ /* Update run */
+ if (run_test())
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Done */
+ return;
+ }
+ }
+
+ /* Decrease the run counter */
+ if (--running <= 0) return;
+
+ /* Take time */
+ energy_use = 100;
+
+
+ /* Move the player, using the "pickup" flag */
+ move_player_aux(find_current, always_pickup, 1, TRUE);
+}
+
+
+/*
+ * Take care of the various things that can happen when you step
+ * into a space. (Objects, traps, and stores.)
+ */
+void step_effects(int y, int x, int do_pickup)
+{
+ /* Handle "objects" */
+ py_pickup_floor(do_pickup);
+
+ /* Handle "store doors" */
+ if (cave[y][x].feat == FEAT_SHOP)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- Enter store */
+ command_new = KTRL('V');
+ }
+
+ /* Discover/set off traps */
+ else if (cave[y][x].t_idx != 0)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ if (!(cave[y][x].info & CAVE_TRDT))
+ {
+ /* Message */
+ msg_print("You found a trap!");
+
+ /* Pick a trap */
+ pick_trap(y, x);
+ }
+
+ /* Hit the trap */
+ hit_trap();
+ }
+}
+
+/*
+ * Issue a pet command
+ */
+void do_cmd_pet(void)
+{
+ int i = 0;
+
+ int num = 0;
+
+ int powers[36];
+
+ char power_desc[36][80];
+
+ bool_ flag, redraw;
+
+ int ask;
+
+ char choice;
+
+ char out_val[160];
+
+ int pets = 0, pet_ctr = 0;
+
+ bool_ all_pets = FALSE;
+
+ monster_type *m_ptr;
+
+
+ for (num = 0; num < 36; num++)
+ {
+ powers[num] = 0;
+ strcpy(power_desc[num], "");
+ }
+
+ num = 0;
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused to command your pets.");
+ energy_use = 0;
+ return;
+ }
+
+ /* Calculate pets */
+ /* Process the monsters (backwards) */
+ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[pet_ctr];
+
+ if (m_ptr->status >= MSTATUS_FRIEND) pets++;
+ }
+
+ if (pets == 0)
+ {
+ msg_print("You have no pets/companions.");
+ energy_use = 0;
+ return;
+ }
+ else
+ {
+ strcpy(power_desc[num], "dismiss pets");
+ powers[num++] = 1;
+ strcpy(power_desc[num], "dismiss companions");
+ powers[num++] = 10;
+ strcpy(power_desc[num], "call pets");
+ powers[num++] = 2;
+ strcpy(power_desc[num], "follow me");
+ powers[num++] = 6;
+ strcpy(power_desc[num], "seek and destroy");
+ powers[num++] = 3;
+ if (p_ptr->pet_open_doors)
+ strcpy(power_desc[num], "disallow open doors");
+ else
+ strcpy(power_desc[num], "allow open doors");
+ powers[num++] = 4;
+ if (p_ptr->pet_pickup_items)
+ strcpy(power_desc[num], "disallow pickup items");
+ else
+ strcpy(power_desc[num], "allow pickup items");
+ powers[num++] = 5;
+ strcpy(power_desc[num], "give target to a friend");
+ powers[num++] = 7;
+ strcpy(power_desc[num], "give target to all friends");
+ powers[num++] = 8;
+ strcpy(power_desc[num], "friend forget target");
+ powers[num++] = 9;
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Build a prompt (accept all spells) */
+ if (num <= 26)
+ {
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78,
+ "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0),
+ I2A(num - 1));
+ }
+ else
+ {
+ strnfmt(out_val, 78,
+ "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0),
+ '0' + num - 27);
+ }
+
+ /* Get a command from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ byte y = 1, x = 0;
+ int ctr = 0;
+ char dummy[80];
+
+ strcpy(dummy, "");
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ prt("", y++, x);
+
+ while (ctr < num)
+ {
+ strnfmt(dummy, 80, "%c) %s", I2A(ctr), power_desc[ctr]);
+ prt(dummy, y + ctr, x);
+ ctr++;
+ }
+
+ if (ctr < 17)
+ {
+ prt("", y + ctr, x);
+ }
+ else
+ {
+ prt("", y + 17, x);
+ }
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ if (choice == '\r' && num == 1)
+ {
+ choice = 'a';
+ }
+
+ if (isalpha(choice))
+ {
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ ask = FALSE; /* Can't uppercase digits */
+
+ i = choice - '0' + 26;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Verify it */
+ if (ask)
+ {
+ char tmp_val[160];
+
+ /* Prompt */
+ strnfmt(tmp_val, 78, "Use %s? ", power_desc[i]);
+
+ /* Belay that order */
+ if (!get_check(tmp_val)) continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Abort if needed */
+ if (!flag)
+ {
+ energy_use = 0;
+ return;
+ }
+
+ switch (powers[i])
+ {
+ /* forget target */
+ case 9:
+ {
+ monster_type *m_ptr;
+ int ii, jj;
+
+ msg_print("Select the friendly monster:");
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ if (m_ptr->status < MSTATUS_PET)
+ {
+ msg_print("You cannot give orders to this monster.");
+ return;
+ }
+
+ m_ptr->target = -1;
+ }
+ break;
+ }
+ /* Give target to all */
+ case 8:
+ {
+ monster_type *m_ptr;
+ int ii, jj, i;
+
+
+ msg_print("Select the target monster:");
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ msg_print("Target selected.");
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status < MSTATUS_PET) continue;
+
+ m_ptr->target = cave[jj][ii].m_idx;
+ }
+ }
+ else
+ {
+ msg_print("This is not a correct target.");
+ return;
+ }
+ break;
+ }
+ case 1: /* Dismiss pets */
+ {
+ int Dismissed = 0;
+
+ if (get_check("Dismiss all pets? ")) all_pets = TRUE;
+
+ /* Process the monsters (backwards) */
+ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--)
+ {
+ monster_race *r_ptr;
+
+ /* Access the monster */
+ m_ptr = &m_list[pet_ctr];
+ r_ptr = &r_info[m_ptr->r_idx];
+
+ if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_PET) || (m_ptr->status == MSTATUS_FRIEND))) /* Get rid of it! */
+ {
+ bool_ checked = FALSE;
+ char command;
+ bool_ delete_this = FALSE;
+
+ if (all_pets)
+ {
+ delete_this = TRUE;
+ }
+ else
+ {
+ char friend_name[80], check_friend[80];
+ monster_desc(friend_name, m_ptr, 0x80);
+ strnfmt(check_friend, 80, "Dismiss %s? (Escape to cancel)", friend_name);
+
+ while (!checked)
+ {
+ if (!get_com(check_friend, &command))
+ {
+ /* get out of loop */
+ checked = TRUE;
+ pet_ctr = 0;
+ }
+ else switch (command)
+ {
+ case 'Y':
+ case 'y':
+ delete_this = TRUE;
+ checked = TRUE;
+ break;
+ case 'n':
+ case 'N':
+ checked = TRUE;
+ break;
+ default:
+ bell();
+ break;
+ }
+ }
+ }
+
+ if (delete_this)
+ {
+ delete_monster_idx(pet_ctr);
+ Dismissed++;
+ }
+ }
+ }
+
+ msg_format("You have dismissed %d pet%s.", Dismissed,
+ (Dismissed == 1 ? "" : "s"));
+ break;
+ }
+ case 10: /* Dismiss companions */
+ {
+ int Dismissed = 0;
+
+ if (get_check("Dismiss all companions? ")) all_pets = TRUE;
+
+ /* Process the monsters (backwards) */
+ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--)
+ {
+ monster_race *r_ptr;
+
+ /* Access the monster */
+ m_ptr = &m_list[pet_ctr];
+ r_ptr = &r_info[m_ptr->r_idx];
+
+ if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_COMPANION))) /* Get rid of it! */
+ {
+ bool_ delete_this = FALSE;
+
+ if (all_pets)
+ delete_this = TRUE;
+ else
+ {
+ char friend_name[80], check_friend[80];
+ monster_desc(friend_name, m_ptr, 0x80);
+ strnfmt(check_friend, 80, "Dismiss %s? ", friend_name);
+
+ if (get_check(check_friend))
+ delete_this = TRUE;
+ }
+
+ if (delete_this)
+ {
+ delete_monster_idx(pet_ctr);
+ Dismissed++;
+ }
+ }
+ }
+
+ msg_format("You have dismissed %d companion%s.", Dismissed,
+ (Dismissed == 1 ? "" : "s"));
+ break;
+ }
+ /* Call pets */
+ case 2:
+ {
+ p_ptr->pet_follow_distance = 1;
+ break;
+ }
+ /* "Seek and destroy" */
+ case 3:
+ {
+ p_ptr->pet_follow_distance = 255;
+ break;
+ }
+ /* flag - allow pets to open doors */
+ case 4:
+ {
+ p_ptr->pet_open_doors = !p_ptr->pet_open_doors;
+ break;
+ }
+ /* flag - allow pets to pickup items */
+ case 5:
+ {
+ p_ptr->pet_pickup_items = !p_ptr->pet_pickup_items;
+
+ /* Drop objects being carried by pets */
+ if (!p_ptr->pet_pickup_items)
+ {
+ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[pet_ctr];
+
+ if (m_ptr->status >= MSTATUS_PET)
+ {
+ monster_drop_carried_objects(m_ptr);
+ }
+ }
+ }
+
+ break;
+ }
+ /* "Follow Me" */
+ case 6:
+ {
+ p_ptr->pet_follow_distance = 6;
+ break;
+ }
+ }
+}
+
+/*
+ * Incarnate into a body
+ */
+bool_ do_cmd_integrate_body()
+{
+ cptr q, s;
+
+ int item;
+
+ object_type *o_ptr;
+
+
+ if (!p_ptr->disembodied)
+ {
+ msg_print("You are already in a body.");
+ return FALSE;
+ }
+
+ /* Restrict choices to monsters */
+ item_tester_tval = TV_CORPSE;
+
+ /* Get an item */
+ q = "Incarnate in which body? ";
+ s = "You have no corpse to incarnate in.";
+ if (!get_item(&item, q, s, (USE_FLOOR))) return FALSE;
+
+ o_ptr = &o_list[0 - item];
+
+ if (o_ptr->sval != SV_CORPSE_CORPSE)
+ {
+ msg_print("You must select a corpse.");
+ return FALSE;
+ }
+
+ p_ptr->body_monster = o_ptr->pval2;
+ p_ptr->chp = o_ptr->pval3;
+
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+
+ msg_print("Your spirit is incarnated in your new body.");
+ p_ptr->wraith_form = FALSE;
+ p_ptr->disembodied = FALSE;
+ do_cmd_redraw();
+
+ return TRUE;
+}
+
+/*
+ * Leave a body
+ */
+bool_ do_cmd_leave_body(bool_ drop_body)
+{
+ object_type *o_ptr, forge;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ int i;
+
+
+ if (p_ptr->disembodied)
+ {
+ msg_print("You are already disembodied.");
+ return FALSE;
+ }
+
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (p_ptr->body_parts[i - INVEN_WIELD] && p_ptr->inventory[i].k_idx &&
+ cursed_p(&p_ptr->inventory[i]))
+ {
+ msg_print("A cursed object is preventing you from leaving your body.");
+ return FALSE;
+ }
+ }
+
+ if (drop_body)
+ {
+ if (magik(25 + get_skill_scale(SKILL_POSSESSION, 25) + get_skill(SKILL_PRESERVATION)))
+ {
+ o_ptr = &forge;
+ object_prep(o_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE));
+ o_ptr->number = 1;
+ o_ptr->pval = 0;
+ o_ptr->pval2 = p_ptr->body_monster;
+ o_ptr->pval3 = p_ptr->chp;
+ o_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1;
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->ident |= IDENT_STOREB;
+
+ /* Unique corpses are unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ o_ptr->name1 = 201;
+ }
+
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+ }
+ else
+ msg_print
+ ("You do not manage to keep the corpse from rotting away.");
+ }
+
+ msg_print("Your spirit leaves your body.");
+ p_ptr->disembodied = TRUE;
+
+ /* Turn into a lost soul(just for the picture) */
+ p_ptr->body_monster = test_monster_name("Lost soul");
+ do_cmd_redraw();
+
+ return (TRUE);
+}
+
+
+bool_ execute_inscription(byte i, byte y, byte x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+
+ /* Not enough mana in the current grid */
+ if (c_ptr->mana < inscription_info[i].mana) return (TRUE);
+
+
+ /* Reduce the grid mana -- note: it can't be restored */
+ c_ptr->mana -= inscription_info[i].mana;
+
+ /* Analyse inscription type */
+ switch (i)
+ {
+ case INSCRIP_LIGHT:
+ {
+ msg_print("The inscription shines in a bright light!");
+ lite_room(y, x);
+
+ break;
+ }
+
+ case INSCRIP_DARK:
+ {
+ msg_print("The inscription is enveloped in a dark aura!");
+ unlite_room(y, x);
+
+ break;
+ }
+
+ case INSCRIP_STORM:
+ {
+ msg_print("The inscription releases a powerful storm!");
+ project(0, 3, y, x, damroll(10, 10),
+ GF_ELEC, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM |
+ PROJECT_KILL | PROJECT_JUMP);
+
+ break;
+ }
+
+ case INSCRIP_PROTECTION:
+ {
+ return (FALSE);
+
+ break;
+ }
+
+ case INSCRIP_DWARF_SUMMON:
+ {
+ int yy = y, xx = x;
+
+ scatter(&yy, &xx, y, x, 3);
+ place_monster_one(yy, xx, test_monster_name("Dwarven Warrior"),
+ 0, FALSE, MSTATUS_FRIEND);
+
+ break;
+ }
+
+ case INSCRIP_CHASM:
+ {
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+ cave_type *c_ptr;
+ int ii = x, ij = y;
+
+ cave_set_feat(ij, ii, FEAT_DARK_PIT);
+ msg_print("A chasm appears in the floor!");
+
+ if (cave[ij][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[ij][ii].m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ {
+ msg_print("The monster simply flies over the chasm.");
+ }
+ else
+ {
+ if (!(r_ptr->flags1 & RF1_UNIQUE))
+ {
+ msg_print("The monster falls in the chasm!");
+ delete_monster_idx(cave[ij][ii].m_idx);
+ }
+ }
+ }
+
+ if (cave[ij][ii].o_idx)
+ {
+ s16b this_o_idx, next_o_idx = 0;
+
+ c_ptr = &cave[ij][ii];
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx;
+ this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+ bool_ plural = FALSE;
+
+ char o_name[80];
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ if (o_ptr->number > 1) plural = TRUE;
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Effect "observed" */
+ if (o_ptr->marked)
+ {
+ object_desc(o_name, o_ptr, FALSE, 0);
+ }
+
+ /* Artifacts get to resist */
+ if (o_ptr->name1)
+ {
+ /* Observe the resist */
+ if (o_ptr->marked)
+ {
+ msg_format("The %s %s simply fly over the chasm!",
+ o_name, (plural ? "are" : "is"));
+ }
+ }
+
+ /* Kill it */
+ else
+ {
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ /* Redraw */
+ lite_spot(ij, ii);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case INSCRIP_BLACK_FIRE:
+ {
+ msg_print("The inscription releases a blast of hellfire!");
+ project(0, 3, y, x, 200,
+ GF_HELL_FIRE, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM |
+ PROJECT_KILL | PROJECT_JUMP);
+
+ break;
+ }
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Choose an inscription and engrave it
+ */
+void do_cmd_engrave()
+{
+ char buf[41] = "";
+
+ byte i;
+
+ strnfmt(buf, 41, "%s", inscription_info[cave[p_ptr->py][p_ptr->px].inscription].text);
+
+ get_string("Engrave what? ", buf, 40);
+
+ /* Silently do nothing when player his escape or enters an empty string */
+ if (!buf[0]) return;
+
+ for (i = 0; i < MAX_INSCRIPTIONS; i++)
+ {
+ if (!strcmp(inscription_info[i].text, buf))
+ {
+ if (inscription_info[i].know)
+ {
+ /* Save the inscription */
+ cave[p_ptr->py][p_ptr->px].inscription = i;
+ }
+ else
+ msg_print("You can't use this inscription for now.");
+ }
+ }
+
+ /* Execute the inscription */
+ if (inscription_info[cave[p_ptr->py][p_ptr->px].inscription].when & INSCRIP_EXEC_ENGRAVE)
+ {
+ execute_inscription(cave[p_ptr->py][p_ptr->px].inscription, p_ptr->py, p_ptr->px);
+ }
+
+ energy_use += 300;
+}
+
+
+/*
+ * Let's do a spinning around attack: -- DG --
+ * aDb
+ * y@k
+ * ooT
+ * Ah ... all of those will get hit.
+ */
+void do_spin()
+{
+ int i, j;
+
+
+ msg_print("You start spinning around...");
+
+ for (j = p_ptr->py - 1; j <= p_ptr->py + 1; j++)
+ {
+ for (i = p_ptr->px - 1; i <= p_ptr->px + 1; i++)
+ {
+ /* Avoid stupid bugs */
+ if (in_bounds(j, i) && cave[j][i].m_idx)
+ py_attack(j, i, 1);
+ }
+ }
+}
diff --git a/src/cmd2.c b/src/cmd2.c
new file mode 100644
index 00000000..2d05c125
--- /dev/null
+++ b/src/cmd2.c
@@ -0,0 +1,5107 @@
+/* File: cmd2.c */
+
+/* Purpose: Movement commands (part 2) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+void do_cmd_immovable_special(void);
+
+/*
+ * Try to bash an altar
+ */
+static bool_ do_cmd_bash_altar(int y, int x)
+{
+ msg_print("Are you mad? You want to anger the gods?");
+ return (FALSE);
+}
+
+
+/*
+ * Try to bash a fountain
+ */
+static bool_ do_cmd_bash_fountain(int y, int x)
+{
+ int bash, temp;
+
+ bool_ more = TRUE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR))
+ {
+ msg_print("You cannot do that.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("You smash into the fountain!");
+
+ /* Hack -- Bash power based on strength */
+ /* (Ranges from 3 to 20 to 100 to 200) */
+ bash = adj_str_blow[p_ptr->stat_ind[A_STR]];
+
+ /* Compare bash power to door power XXX XXX XXX */
+ temp = (bash - 50);
+
+ /* Hack -- always have a chance */
+ if (temp < 1) temp = 1;
+
+ /* Hack -- attempt to bash down the door */
+ if (rand_int(200) < temp)
+ {
+ /* Message */
+ msg_print("The fountain breaks!");
+
+ fire_ball(GF_WATER, 5, damroll(6, 8), 2);
+
+ cave_set_feat(y, x, FEAT_DEEP_WATER);
+ more = FALSE;
+ }
+
+ return (more);
+}
+
+
+/*
+ * Go up one level
+ */
+void do_cmd_go_up(void)
+{
+ bool_ go_up = FALSE, go_up_many = FALSE, prob_traveling = FALSE;
+
+ cave_type *c_ptr;
+
+ int oldl = dun_level;
+
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+
+ /* Player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ /* Can we ? */
+ if (process_hooks(HOOK_STAIR, "(s)", "up")) return;
+
+ /* Normal up stairs */
+ if ((c_ptr->feat == FEAT_LESS) || (c_ptr->feat == FEAT_WAY_LESS))
+ {
+ if (!dun_level)
+ {
+ go_up = TRUE;
+ }
+ else if ((dungeon_flags2 & DF2_ASK_LEAVE))
+ {
+ go_up = get_check("Leave this unique level forever? ");
+ }
+ else if (confirm_stairs)
+ {
+ go_up = get_check("Really leave the level? ");
+ }
+ else
+ {
+ go_up = TRUE;
+ }
+ }
+
+ /* Shaft up */
+ else if (c_ptr->feat == FEAT_SHAFT_UP)
+ {
+ if (dun_level == 1)
+ {
+ go_up = TRUE;
+ }
+ else if ((dungeon_flags2 & DF2_ASK_LEAVE))
+ {
+ go_up = get_check("Leave this unique level forever? ");
+ }
+ else if (confirm_stairs)
+ {
+ go_up_many = get_check("Really leave the level? ");
+ }
+ else
+ {
+ go_up_many = TRUE;
+ }
+ }
+
+ /* Quest exit */
+ else if (c_ptr->feat == FEAT_QUEST_EXIT)
+ {
+ leaving_quest = p_ptr->inside_quest;
+
+ if ((dungeon_flags2 & DF2_ASK_LEAVE) &&
+ !get_check("Leave this unique level forever? "))
+ return;
+
+ p_ptr->inside_quest = c_ptr->special;
+ dun_level = 0;
+ p_ptr->oldpx = 0;
+ p_ptr->oldpy = 0;
+ p_ptr->leaving = TRUE;
+
+ return;
+ }
+
+ /* Exits to previous area in flat terrains */
+ else if (!(dungeon_flags1 & DF1_FLAT) &&
+ p_ptr->prob_travel && !p_ptr->inside_quest)
+ {
+ if (d_ptr->mindepth == dun_level) return;
+
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerful force prevents your from teleporting.");
+ return;
+ }
+
+ prob_traveling = TRUE;
+
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_up = TRUE;
+ }
+ else
+ {
+ go_up = TRUE;
+ }
+ }
+ else
+ {
+ msg_print("I see no up staircase here.");
+ return;
+ }
+
+ if (go_up || go_up_many)
+ {
+
+ energy_use = 0;
+
+ /* Success */
+ if (c_ptr->feat == FEAT_WAY_LESS)
+ msg_print("You enter the previous area.");
+ else
+ msg_print("You enter a maze of up staircases.");
+
+ autosave_checkpoint();
+
+ if (p_ptr->inside_quest)
+ {
+ dun_level = 1;
+ leaving_quest = p_ptr->inside_quest;
+
+ p_ptr->inside_quest = c_ptr->special;
+ }
+
+ /* Create a way back */
+ if (go_up_many)
+ create_down_shaft = TRUE;
+ else
+ create_down_stair = TRUE;
+
+ /* New depth */
+ if (go_up)
+ dun_level--;
+ else
+ {
+ dun_level -= randint(3) + 1;
+ if (dun_level <= 0) dun_level = 0;
+ }
+
+ if (c_ptr->special && (!prob_traveling))
+ {
+ dun_level = oldl;
+ dun_level = get_flevel();
+ dungeon_type = c_ptr->special;
+ dun_level += d_info[dungeon_type].mindepth;
+ }
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+}
+
+
+/*
+ * Returns TRUE if we are in the Between...
+ */
+static bool_ between_effect(void)
+{
+ byte bx, by;
+
+
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN)
+ {
+
+ bx = cave[p_ptr->py][p_ptr->px].special & 255;
+ by = cave[p_ptr->py][p_ptr->px].special >> 8;
+
+ msg_print("You fall into the void.");
+ msg_print("Brrrr! It's deadly cold.");
+
+ swap_position(by, bx);
+
+ /* To avoid being teleported back */
+ energy_use = 100;
+
+ return (TRUE);
+ }
+
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN2)
+ {
+ between_exit *be_ptr = &between_exits[cave[p_ptr->py][p_ptr->px].special];
+
+ p_ptr->wild_mode = FALSE;
+ p_ptr->wilderness_x = be_ptr->wild_x;
+ p_ptr->wilderness_y = be_ptr->wild_y;
+ p_ptr->oldpx = p_ptr->px = be_ptr->px;
+ p_ptr->oldpy = p_ptr->py = be_ptr->py;
+ dungeon_type = be_ptr->d_idx;
+ dun_level = be_ptr->level;
+ p_ptr->leaving = TRUE;
+
+ return (TRUE);
+ }
+ else
+ return (FALSE);
+}
+
+/*
+ * Go down one level
+ */
+void do_cmd_go_down(void)
+{
+ cave_type *c_ptr;
+
+ bool_ go_down = FALSE, go_down_many = FALSE, prob_traveling = FALSE;
+
+ bool_ fall_trap = FALSE;
+
+ char i;
+
+ int old_dun = dun_level;
+
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+
+ /* MUST be actived now */
+ if (between_effect()) return;
+
+ /* Player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ if (p_ptr->astral && (dun_level == 98)) return;
+
+ if (c_ptr->t_idx == TRAP_OF_SINKING) fall_trap = TRUE;
+
+ /* test if on special level */
+ if ((dungeon_flags2 & DF2_ASK_LEAVE))
+ {
+ prt("Leave this unique level forever (y/n) ? ", 0, 0);
+ flush();
+ i = inkey();
+ prt("", 0, 0);
+ if (i != 'y') return;
+ }
+
+ /* Can we ? */
+ if (process_hooks(HOOK_STAIR, "(s)", "down")) return;
+
+ /* Normal up stairs */
+ if (c_ptr->feat == FEAT_SHAFT_DOWN)
+ {
+ if (!dun_level)
+ {
+ go_down = TRUE;
+
+ /* Save old player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ }
+ else
+ {
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_down_many = TRUE;
+ }
+ else
+ {
+ go_down_many = TRUE;
+ }
+ }
+ }
+
+ /* Normal stairs */
+ else if ((c_ptr->feat == FEAT_MORE) || (c_ptr->feat == FEAT_WAY_MORE))
+ {
+ if (p_ptr->prob_travel)
+ {
+ if (d_ptr->maxdepth == dun_level) return;
+ }
+ if (!dun_level)
+ {
+ go_down = TRUE;
+
+ /* Save old player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ }
+ else
+ {
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_down = TRUE;
+ }
+ else
+ {
+ go_down = TRUE;
+ }
+ }
+ }
+
+ /* Handle quest areas -KMW- */
+ else if (c_ptr->feat == FEAT_QUEST_ENTER)
+ {
+ /* Enter quest level */
+ enter_quest();
+
+ return;
+ }
+
+ else if (!(dungeon_flags1 & DF1_FLAT) &&
+ p_ptr->prob_travel && !p_ptr->inside_quest)
+ {
+ if (d_ptr->maxdepth == dun_level) return;
+
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerfull force prevents your from teleporting.");
+ return;
+ }
+
+ prob_traveling = TRUE;
+
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_down = TRUE;
+ }
+ else
+ {
+ go_down = TRUE;
+ }
+ }
+
+ else if (!(fall_trap))
+ {
+ msg_print("I see no down staircase here.");
+ return;
+ }
+
+ if (go_down || go_down_many)
+ {
+ energy_use = 0;
+
+ if (fall_trap)
+ msg_print("You deliberately jump through the trap door.");
+ else
+ {
+ if (c_ptr->feat == FEAT_WAY_MORE)
+ msg_print("You enter the next area.");
+ else
+ msg_print("You enter a maze of down staircases.");
+ }
+
+ autosave_checkpoint();
+
+ /* Go down */
+ if (go_down)
+ {
+ dun_level++;
+ }
+ else if (go_down_many)
+ {
+ int i = randint(3) + 1, j;
+
+ for (j = 1; j < i; j++)
+ {
+ dun_level++;
+ if (is_quest(dun_level + i - 1)) break;
+ if (d_ptr->maxdepth == dun_level) break;
+ }
+ }
+
+ /* We change place */
+ if (c_ptr->special && (!prob_traveling))
+ {
+ if (d_info[c_ptr->special].min_plev <= p_ptr->lev)
+ {
+ dungeon_info_type *d_ptr = &d_info[c_ptr->special];
+
+ /* Do the lua scripts refuse ? ;) */
+ if (process_hooks(HOOK_ENTER_DUNGEON, "(d)", c_ptr->special))
+ {
+ dun_level = old_dun;
+ return;
+ }
+
+ /* Ok go in the new dungeon */
+ dungeon_type = c_ptr->special;
+ d_ptr = &d_info[dungeon_type];
+
+ if ((p_ptr->wilderness_x == d_ptr->ix) &&
+ (p_ptr->wilderness_y == d_ptr->iy))
+ {
+ dun_level = d_ptr->mindepth;
+ }
+ else if ((p_ptr->wilderness_x == d_ptr->ox) &&
+ (p_ptr->wilderness_y == d_ptr->oy))
+ {
+ dun_level = d_ptr->maxdepth;
+ }
+ else
+ {
+ dun_level = d_ptr->mindepth;
+ }
+
+ msg_format("You go into %s",
+ d_text + d_info[dungeon_type].text);
+ }
+ else
+ {
+ msg_print
+ ("You don't feel yourself experienced enough to go there...");
+ dun_level = old_dun;
+ return;
+ }
+ }
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ if (!fall_trap)
+ {
+ /* Create a way back */
+ if (go_down_many)
+ create_up_shaft = TRUE;
+ else
+ create_up_stair = TRUE;
+ }
+ }
+}
+
+
+
+/*
+ * Simple command to "search" for one turn
+ */
+void do_cmd_search(void)
+{
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Search */
+ search();
+}
+
+
+/*
+ * Hack -- toggle search mode
+ */
+void do_cmd_toggle_search(void)
+{
+ /* Stop searching */
+ if (p_ptr->searching)
+ {
+ /* Clear the searching flag */
+ p_ptr->searching = FALSE;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ /* Start searching */
+ else
+ {
+ /* Set the searching flag */
+ p_ptr->searching = TRUE;
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw stuff */
+ p_ptr->redraw |= (PR_STATE | PR_SPEED);
+ }
+}
+
+
+
+/*
+ * Determine if a grid contains a chest
+ */
+static s16b chest_check(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Skip unknown chests XXX XXX */
+ /* if (!o_ptr->marked) continue; */
+
+ /* Check for chest */
+ if (o_ptr->tval == TV_CHEST) return (this_o_idx);
+ }
+
+ /* No chest */
+ return (0);
+}
+
+
+/*
+ * Allocates objects upon opening a chest -BEN-
+ *
+ * Disperse treasures from the given chest, centered at (x,y).
+ *
+ * Small chests often contain "gold", while Large chests always contain
+ * items. Wooden chests contain 2 items, Iron chests contain 4 items,
+ * and Steel chests contain 6 items. The "value" of the items in a
+ * chest is based on the "power" of the chest, which is in turn based
+ * on the level on which the chest is generated.
+ */
+static void chest_death(int y, int x, s16b o_idx)
+{
+ int number;
+
+ bool_ small;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr = &o_list[o_idx];
+
+
+ /* Small chests often hold "gold" */
+ small = (o_ptr->sval < SV_CHEST_MIN_LARGE);
+
+ /* Determine how much to drop (see above) */
+ number = (o_ptr->sval % SV_CHEST_MIN_LARGE) * 2;
+
+ /* Zero pval means empty chest */
+ if (!o_ptr->pval) number = 0;
+
+ /* Opening a chest */
+ opening_chest = TRUE;
+
+ /* Determine the "value" of the items */
+ object_level = ABS(o_ptr->pval) + 10;
+
+ /* Drop some objects (non-chests) */
+ for (; number > 0; --number)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Small chests often drop gold */
+ if (small && (rand_int(100) < 75))
+ {
+ /* Make some gold */
+ if (!make_gold(q_ptr)) continue;
+ }
+
+ /* Otherwise drop an item */
+ else
+ {
+ /* Make an object */
+ if (!make_object(q_ptr, FALSE, FALSE, d_info[dungeon_type].objs))
+ continue;
+ }
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+
+ /* Reset the object level */
+ object_level = dun_level;
+
+ /* No longer opening a chest */
+ opening_chest = FALSE;
+
+ /* Empty */
+ o_ptr->pval = 0;
+ o_ptr->pval2 = 0;
+
+ /* Known */
+ object_known(o_ptr);
+}
+
+
+/*
+ * Chests have traps too.
+ *
+ * Exploding chest destroys contents (and traps).
+ * Note that the chest itself is never destroyed.
+ */
+static void chest_trap(int y, int x, s16b o_idx)
+{
+ int trap;
+
+ object_type *o_ptr = &o_list[o_idx];
+
+ bool_ ident = FALSE;
+
+
+ /* Ignore disarmed chests */
+ if (o_ptr->pval <= 0) return;
+
+ /* Obtain the trap */
+ trap = o_ptr->pval;
+
+ /* Message */
+ msg_print("You found a trap!");
+
+ /* Set off trap */
+ ident = player_activate_trap_type(y, x, o_ptr, o_idx);
+ if (ident)
+ {
+ t_info[o_ptr->pval].ident = TRUE;
+ msg_format("You identified the trap as %s.",
+ t_name + t_info[trap].name);
+ }
+}
+
+
+/*
+ * Attempt to open the given chest at the given location
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool_ do_cmd_open_chest(int y, int x, s16b o_idx)
+{
+ int i, j;
+
+ bool_ flag = TRUE;
+
+ bool_ more = FALSE;
+
+ object_type *o_ptr = &o_list[o_idx];
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open chests.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Attempt to unlock it */
+ if (o_ptr->pval > 0)
+ {
+ /* Assume locked, and thus not open */
+ flag = FALSE;
+
+ /* Get the "disarm" factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the difficulty */
+ j = i - o_ptr->pval;
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success -- May still have traps */
+ if (rand_int(100) < j)
+ {
+ msg_print("You have picked the lock.");
+ gain_exp(1);
+ flag = TRUE;
+ }
+
+ /* Failure -- Keep trying */
+ else
+ {
+ /* We may continue repeating */
+ more = TRUE;
+
+ if (flush_failure) flush();
+
+ msg_print("You failed to pick the lock.");
+ }
+ }
+
+ /* Allowed to open */
+ if (flag)
+ {
+ /* Apply chest traps, if any */
+ chest_trap(y, x, o_idx);
+
+ /* Let the Chest drop items */
+ chest_death(y, x, o_idx);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Original code by TNB, improvement for Angband 2.9.3 by rr9
+ * Slightly modified for ToME because of its trap implementation
+ */
+
+/*
+ * Return TRUE if the given grid is an open door
+ */
+static bool_ is_open(cave_type *c_ptr)
+{
+ return (c_ptr->feat == FEAT_OPEN);
+}
+
+
+/*
+ * Return TRUE if the given grid is a closed door
+ */
+static bool_ is_closed(cave_type *c_ptr)
+{
+ byte feat;
+
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else feat = c_ptr->feat;
+
+ return ((feat >= FEAT_DOOR_HEAD) && (feat <= FEAT_DOOR_TAIL));
+}
+
+
+/*
+ * Return TRUE if the given grid has a trap
+ */
+static bool_ is_trap(cave_type *c_ptr)
+{
+ return ((c_ptr->info & (CAVE_TRDT)) != 0);
+}
+
+
+/*
+ * Return the number of doors/traps around (or under)
+ * the character using the filter function 'test'
+ */
+static int count_feats(int *y, int *x, bool_ (*test) (cave_type *c_ptr),
+ bool_ under)
+{
+ int d;
+
+ int xx, yy;
+
+ int count;
+
+
+ /* Clear match counter */
+ count = 0;
+
+ /* Check around (and under) the character */
+ for (d = 0; d < 9; d++)
+ {
+ /* Ignore current grid if told so -- See tables.c */
+ if ((d == 8) && !under) continue;
+
+ /* Extract adjacent (legal) location */
+ yy = p_ptr->py + ddy_ddd[d];
+ xx = p_ptr->px + ddx_ddd[d];
+
+ /* Paranoia */
+ if (!in_bounds(yy, xx)) continue;
+
+ /* Must have knowledge */
+ if (!(cave[yy][xx].info & (CAVE_MARK))) continue;
+
+ /* Not looking for this feature */
+ if (!(*test) (&cave[yy][xx])) continue;
+
+ /* Count it */
+ count++;
+
+ /* Remember the location. Only meaningful if there's
+ exactly one match */
+ *y = yy;
+ *x = xx;
+ }
+
+ /* All done */
+ return (count);
+}
+
+
+/*
+ * Return the number of chests around (or under) the character.
+ * If requested, count only trapped chests.
+ */
+static int count_chests(int *y, int *x, bool_ trapped)
+{
+ int d, count, o_idx;
+
+ object_type *o_ptr;
+
+
+ /* Count how many matches */
+ count = 0;
+
+ /* Check around (and under) the character */
+ for (d = 0; d < 9; d++)
+ {
+
+ /* Extract adjacent (legal) location */
+ int yy = p_ptr->py + ddy_ddd[d];
+ int xx = p_ptr->px + ddx_ddd[d];
+
+ /* No (visible) chest is there */
+ if ((o_idx = chest_check(yy, xx)) == 0) continue;
+
+ /* Grab the object */
+ o_ptr = &o_list[o_idx];
+
+ /* Already open */
+ if (o_ptr->pval == 0) continue;
+
+ /* No (known) traps here */
+ if (trapped && (!object_known_p(o_ptr) || !o_ptr->pval)) continue;
+
+ /* OK */
+ ++count;
+
+ /* Remember the location. Only useful if only one match */
+ *y = yy;
+ *x = xx;
+ }
+
+ /* All done */
+ return (count);
+}
+
+
+/*
+ * Convert an adjacent location to a direction.
+ */
+static int coords_to_dir(int y, int x)
+{
+ int d[3][3] =
+ {
+ {7, 4, 1},
+ {8, 5, 2},
+ {9, 6, 3} };
+
+ int dy, dx;
+
+
+ dy = y - p_ptr->py;
+ dx = x - p_ptr->px;
+
+ /* Paranoia */
+ if (ABS(dx) > 1 || ABS(dy) > 1) return (0);
+
+ return d[dx + 1][dy + 1];
+}
+
+
+/*
+ * Perform the basic "open" command on doors
+ *
+ * Assume destination is a closed/locked/jammed door
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool_ do_cmd_open_aux(int y, int x, int dir)
+{
+ int i, j;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open doors.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get requested grid */
+ c_ptr = &cave[y][x];
+
+ /* Jammed door */
+ if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08)
+ {
+ /* Stuck */
+ msg_print("The door appears to be stuck.");
+ }
+
+ /* Locked door */
+ else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01)
+ {
+ /* Disarm factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the lock power */
+ j = c_ptr->feat - FEAT_DOOR_HEAD;
+
+ /* Extract the difficulty XXX XXX XXX */
+ j = i - (j * 4);
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success */
+ if (rand_int(100) < j)
+ {
+ /* Message */
+ msg_print("You have picked the lock.");
+
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+
+ /* Experience */
+ gain_exp(1);
+ }
+
+ /* Failure */
+ else
+ {
+ /* Failure */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("You failed to pick the lock.");
+
+ /* We may keep trying */
+ more = TRUE;
+ }
+ }
+
+ /* Closed door */
+ else
+ {
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+
+/*
+ * Open a closed/locked/jammed door or a closed/locked chest.
+ *
+ * Unlocking a locked door/chest is worth one experience point.
+ */
+void do_cmd_open(void)
+{
+ int y, x, dir;
+
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open doors.");
+
+ return;
+ }
+
+ /* Option: Pick a direction */
+ if (easy_open)
+ {
+ int num_doors, num_chests;
+
+ /* Count closed doors (locked or jammed) */
+ num_doors = count_feats(&y, &x, is_closed, FALSE);
+
+ /* Count chests (locked) */
+ num_chests = count_chests(&y, &x, FALSE);
+
+ /* There is nothing the player can open */
+ if ((num_doors + num_chests) == 0)
+ {
+ /* Message */
+ msg_print("You see nothing there to open.");
+
+ /* Done */
+ return;
+ }
+
+ /* Set direction if there is only one target */
+ else if ((num_doors + num_chests) == 1)
+ {
+ command_dir = coords_to_dir(y, x);
+ }
+ }
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get requested location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get requested grid */
+ c_ptr = &cave[y][x];
+
+ /* Check for chest */
+ o_idx = chest_check(y, x);
+
+ /* Nothing useful */
+ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) && !o_idx)
+ {
+ /* Message */
+ msg_print("You see nothing there to open.");
+ }
+
+ /* Monster in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Handle chests */
+ else if (o_idx)
+ {
+ /* Open the chest */
+ more = do_cmd_open_chest(y, x, o_idx);
+ }
+
+ /* Handle doors */
+ else
+ {
+ /* Open the door */
+ more = do_cmd_open_aux(y, x, dir);
+ }
+ }
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_OPEN, "(d)", is_quest(dun_level));
+
+ /* Cancel repeat unless we may continue */
+ if (!more) disturb(0, 0);
+}
+
+
+
+/*
+ * Perform the basic "close" command
+ *
+ * Assume destination is an open/broken door
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool_ do_cmd_close_aux(int y, int x, int dir)
+{
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot close doors.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Broken door */
+ if (c_ptr->feat == FEAT_BROKEN)
+ {
+ /* Message */
+ msg_print("The door appears to be broken.");
+ }
+
+ /* Open door */
+ else
+ {
+ /* Close the door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_SHUTDOOR);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Close an open door.
+ */
+void do_cmd_close(void)
+{
+ int y, x, dir;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+
+ /* Option: Pick a direction */
+ if (easy_open)
+ {
+ int num_doors;
+
+ /* Count open doors */
+ num_doors = count_feats(&y, &x, is_open, FALSE);
+
+ /* There are no doors the player can close */
+ if (num_doors == 0)
+ {
+ /* Message */
+ msg_print("You see nothing there to close.");
+
+ /* Done */
+ return;
+ }
+
+ /* Exactly one closeable door */
+ else if (num_doors == 1)
+ {
+ command_dir = coords_to_dir(y, x);
+ }
+ }
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get requested location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Require open/broken door */
+ if ((c_ptr->feat != FEAT_OPEN) && (c_ptr->feat != FEAT_BROKEN))
+ {
+ /* Message */
+ msg_print("You see nothing there to close.");
+ }
+
+ /* Monster in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Close the door */
+ else
+ {
+ /* Close the door */
+ more = do_cmd_close_aux(y, x, dir);
+ }
+ }
+
+ /* Cancel repeat unless we may continue */
+ if (!more) disturb(0, 0);
+}
+
+
+/*
+ * Determine if a given grid may be "tunneled"
+ */
+static bool_ do_cmd_tunnel_test(int y, int x)
+{
+ /* Must have knowledge(execpt on "forget" levels) */
+ if (!(cave[y][x].info & (CAVE_MARK)))
+ {
+ /* Message */
+ msg_print("You see nothing there.");
+
+ /* Nope */
+ return (FALSE);
+ }
+
+ /* Must be a wall/door/etc */
+ if (cave_floor_bold(y, x))
+ {
+ /* Message */
+ msg_print("You see nothing there to tunnel.");
+
+ /* Nope */
+ return (FALSE);
+ }
+
+ /* Must be tunnelable */
+ if (!(f_info[cave[y][x].feat].flags1 & FF1_TUNNELABLE))
+ {
+ /* Message */
+ msg_print(f_text + f_info[cave[y][x].feat].tunnel);
+
+ /* Nope */
+ return (FALSE);
+ }
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+
+/*
+ * Tunnel through wall. Assumes valid location.
+ *
+ * Note that it is impossible to "extend" rooms past their
+ * outer walls (which are actually part of the room).
+ *
+ * This will, however, produce grids which are NOT illuminated
+ * (or darkened) along with the rest of the room.
+ */
+static bool_ twall(int y, int x, byte feat)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+
+ /* Paranoia -- Require a wall or door or some such */
+ if (cave_floor_bold(y, x)) return (FALSE);
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Remove the feature */
+ cave_set_feat(y, x, feat);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Result */
+ return (TRUE);
+}
+
+
+
+/*
+ * Perform the basic "tunnel" command
+ *
+ * Assumes that the destination is a wall, a vein, a secret
+ * door, or rubble.
+ *
+ * Assumes that no monster is blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+bool_ do_cmd_tunnel_aux(int y, int x, int dir)
+{
+ int skill_req = 0, skill_req_1pct = 0;
+ cave_type *c_ptr = &cave[y][x];
+
+ feature_type *f_ptr = &f_info[c_ptr->feat];
+
+ bool_ more = FALSE;
+
+
+ /* Must be have something to dig with (except for sandwalls) */
+ if ((c_ptr->feat < FEAT_SANDWALL) || (c_ptr->feat > FEAT_SANDWALL_K))
+ {
+ if (!p_ptr->inventory[INVEN_TOOL].k_idx ||
+ (p_ptr->inventory[INVEN_TOOL].tval != TV_DIGGING))
+ {
+ msg_print("You need to have a shovel or pick in your tool slot.");
+
+ return (FALSE);
+ }
+ }
+
+ /* Verify legality */
+ if (!do_cmd_tunnel_test(y, x)) return (FALSE);
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Sound */
+ sound(SOUND_DIG);
+
+ /* Titanium */
+ if (f_ptr->flags1 & FF1_PERMANENT)
+ {
+ msg_print(f_text + f_ptr->tunnel);
+ }
+
+ else if ((c_ptr->feat == FEAT_TREES) || (c_ptr->feat == FEAT_DEAD_TREE))
+ {
+ /* Chop Down */
+ skill_req = 10;
+ skill_req_1pct = 14;
+ if ((p_ptr->skill_dig > 10 + rand_int(400)) && twall(y, x, FEAT_GRASS))
+ {
+ msg_print("You have cleared away the trees.");
+ }
+
+ /* Keep trying */
+ else
+ {
+ /* We may continue chopping */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+
+ /* Occasional Search XXX XXX */
+ if (rand_int(100) < 25) search();
+ }
+ }
+
+
+ /* Granite */
+ else if ((c_ptr->feat >= FEAT_WALL_EXTRA) &&
+ (c_ptr->feat <= FEAT_WALL_SOLID))
+ {
+ /* Tunnel */
+ skill_req = 40;
+ skill_req_1pct = 56;
+ if ((p_ptr->skill_dig > 40 + rand_int(1600)) && twall(y, x, FEAT_FLOOR))
+ {
+ msg_print("You have finished the tunnel.");
+ }
+
+ /* Keep trying */
+ else
+ {
+ /* We may continue tunelling */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+ }
+ }
+
+
+ /* Quartz / Magma / Sandwall */
+ else if (((c_ptr->feat >= FEAT_MAGMA) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K)) ||
+ ((c_ptr->feat >= FEAT_SANDWALL) &&
+ (c_ptr->feat <= FEAT_SANDWALL_K)))
+ {
+ bool_ okay = FALSE;
+ bool_ gold = FALSE;
+ bool_ hard = FALSE;
+ bool_ soft = FALSE;
+
+ /* Found gold */
+ if ((c_ptr->feat >= FEAT_MAGMA_H) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K)) gold = TRUE;
+
+ if ((c_ptr->feat == FEAT_SANDWALL_H) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ gold = TRUE;
+ soft = TRUE;
+ }
+ else
+ /* Extract "quartz" flag XXX XXX XXX */
+ if ((c_ptr->feat - FEAT_MAGMA) & 0x01) hard = TRUE;
+
+ /* Quartz */
+ if (hard)
+ {
+ skill_req = 20;
+ skill_req_1pct = 28;
+ okay = (p_ptr->skill_dig > 20 + rand_int(800));
+ }
+
+ /* Sandwall */
+ else if (soft)
+ {
+ skill_req = 5;
+ skill_req_1pct = 8;
+ okay = (p_ptr->skill_dig > 5 + rand_int(250));
+ }
+
+ /* Magma */
+ else
+ {
+ skill_req = 10;
+ skill_req_1pct = 14;
+ okay = (p_ptr->skill_dig > 10 + rand_int(400));
+ }
+
+ /* Success */
+ if (okay && twall(y, x, FEAT_FLOOR))
+ {
+ /* Found treasure */
+ if (gold)
+ {
+ /* Place some gold */
+ place_gold(y, x);
+
+ /* Message */
+ msg_print("You have found something!");
+ }
+
+ /* Found nothing */
+ else
+ {
+ /* Message */
+ msg_print("You have finished the tunnel.");
+ }
+ }
+
+ /* Failure */
+ else
+ {
+ /* Message, continue digging */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+ }
+ }
+
+ /* Rubble */
+ else if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ /* Remove the rubble */
+ skill_req = 0;
+ skill_req_1pct = 2;
+ if ((p_ptr->skill_dig > rand_int(200)) &&
+ twall(y, x, d_info[dungeon_type].floor1))
+ {
+ /* Message */
+ msg_print("You have removed the rubble.");
+
+ /* Hack -- place an object */
+ if (rand_int(100) < 10)
+ {
+ /* Create a simple object */
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_RUBBLE);
+
+ /* Observe new object */
+ if (player_can_see_bold(y, x))
+ {
+ msg_print("You have found something!");
+ }
+ }
+ }
+
+ else
+ {
+ /* Message, keep digging */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+ }
+ }
+
+ /* Secret doors */
+ else if (c_ptr->feat >= FEAT_SECRET)
+ {
+ /* Tunnel */
+ skill_req = 30;
+ skill_req_1pct = 42;
+ if ((p_ptr->skill_dig > 30 + rand_int(1200)) && twall(y, x, FEAT_FLOOR))
+ {
+ msg_print("You have finished the tunnel.");
+ c_ptr->mimic = 0;
+ lite_spot(y, x);
+
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+ }
+
+ /* Keep trying */
+ else
+ {
+ int feat;
+
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else
+ feat = c_ptr->feat;
+
+ /* We may continue tunelling */
+ msg_print(f_text + f_info[feat].tunnel);
+ more = TRUE;
+
+ /* Occasional Search XXX XXX */
+ if (rand_int(100) < 25) search();
+ }
+ }
+
+ /* Doors */
+ else
+ {
+ /* Tunnel */
+ skill_req = 30;
+ skill_req_1pct = 42;
+ if ((p_ptr->skill_dig > 30 + rand_int(1200)) && twall(y, x, FEAT_FLOOR))
+ {
+ msg_print("You have finished the tunnel.");
+ }
+
+ /* Keep trying */
+ else
+ {
+ /* We may continue tunelling */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+ }
+ }
+
+ if (more && magik(2))
+ {
+ if (p_ptr->skill_dig < skill_req)
+ {
+ msg_print("You fail to make even the slightest of progress.");
+ more = FALSE;
+ }
+ else if (p_ptr->skill_dig < skill_req_1pct)
+ {
+ msg_print("This will take some time.");
+ }
+ }
+
+ /* Notice new floor grids */
+ if (!cave_floor_bold(y, x))
+ {
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Tunnels through "walls" (including rubble and closed doors)
+ *
+ * Note that you must tunnel in order to hit invisible monsters
+ * in walls, though moving into walls still takes a turn anyway.
+ *
+ * Digging is very difficult without a "digger" weapon, but can be
+ * accomplished by strong players using heavy weapons.
+ */
+void do_cmd_tunnel(void)
+{
+ int y, x, dir;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+
+ if (p_ptr->wild_mode) return;
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a direction to tunnel, or Abort */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* No tunnelling through doors */
+ if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) || (c_ptr->feat == FEAT_SHOP))
+ {
+ /* Message */
+ msg_print("You cannot tunnel through doors.");
+ }
+
+ /* No tunnelling through air */
+ else if (cave_floor_grid(c_ptr))
+ {
+ /* Message */
+ msg_print("You cannot tunnel through air.");
+ }
+
+ /* A monster is in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Try digging */
+ else
+ {
+ /* Tunnel through walls */
+ more = do_cmd_tunnel_aux(y, x, dir);
+ }
+ }
+
+ /* Cancel repetition unless we can continue */
+ if (!more) disturb(0, 0);
+}
+
+
+/*
+ * easy_open_door --
+ *
+ * If there is a jammed/closed/locked door at the given location,
+ * then attempt to unlock/open it. Return TRUE if an attempt was
+ * made (successful or not), otherwise return FALSE.
+ *
+ * The code here should be nearly identical to that in
+ * do_cmd_open_test() and do_cmd_open_aux().
+ */
+
+bool_ easy_open_door(int y, int x)
+{
+ int i, j;
+
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open doors.");
+
+ return (FALSE);
+ }
+
+ /* Must be a closed door */
+ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL)))
+ {
+ /* Nope */
+ return (FALSE);
+ }
+
+ /* Jammed door */
+ if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08)
+ {
+ /* Stuck */
+ msg_print("The door appears to be stuck.");
+ }
+
+ /* Locked door */
+ else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01)
+ {
+ /* Disarm factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the lock power */
+ j = c_ptr->feat - FEAT_DOOR_HEAD;
+
+ /* Extract the difficulty XXX XXX XXX */
+ j = i - (j * 4);
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success */
+ if (rand_int(100) < j)
+ {
+ /* Message */
+ msg_print("You have picked the lock.");
+
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_OPEN, "(d)", is_quest(dun_level));
+
+ /* Experience */
+ gain_exp(1);
+ }
+
+ /* Failure */
+ else
+ {
+ /* Failure */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("You failed to pick the lock.");
+ }
+ }
+
+ /* Closed door */
+ else
+ {
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+ }
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Perform the basic "disarm" command
+ *
+ * Assume destination is a visible trap
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool_ do_cmd_disarm_chest(int y, int x, s16b o_idx)
+{
+ int i, j;
+
+ bool_ more = FALSE;
+
+ object_type *o_ptr = &o_list[o_idx];
+
+ trap_type *t_ptr = &t_info[o_ptr->pval];
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get the "disarm" factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the difficulty */
+ j = i - t_ptr->difficulty * 3;
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Must find the trap first. */
+ if (!object_known_p(o_ptr))
+ {
+ msg_print("I don't see any traps.");
+ }
+
+ /* Already disarmed/unlocked */
+ else if (o_ptr->pval <= 0)
+ {
+ msg_print("The chest is not trapped.");
+ }
+
+ /* Success (get a lot of experience) */
+ else if (rand_int(100) < j)
+ {
+ msg_print("You have disarmed the chest.");
+ gain_exp(t_ptr->difficulty * 3);
+ o_ptr->pval = (0 - o_ptr->pval);
+ }
+
+ /* Failure -- Keep trying */
+ else if ((i > 5) && (randint(i) > 5))
+ {
+ /* We may keep trying */
+ more = TRUE;
+ if (flush_failure) flush();
+ msg_print("You failed to disarm the chest.");
+ }
+
+ /* Failure -- Set off the trap */
+ else
+ {
+ msg_print("You set off a trap!");
+ sound(SOUND_FAIL);
+ chest_trap(y, x, o_idx);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Perform the basic "disarm" command
+ *
+ * Assume destination is a visible trap
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+bool_ do_cmd_disarm_aux(int y, int x, int dir, int do_pickup)
+{
+ int i, j, power;
+
+ cave_type *c_ptr;
+
+ cptr name;
+
+ bool_ more = FALSE;
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Access trap name */
+ if (t_info[c_ptr->t_idx].ident)
+ name = (t_name + t_info[c_ptr->t_idx].name);
+ else
+ name = "unknown trap";
+
+ /* Get the "disarm" factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* XXX XXX XXX Variable power? */
+
+ /* Extract trap "power" */
+ power = t_info[c_ptr->t_idx].difficulty;
+
+ /* Extract the difficulty */
+ j = i - power;
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success */
+ if (rand_int(100) < j)
+ {
+ /* Message */
+ msg_format("You have disarmed the %s.", name);
+
+ /* Reward */
+ gain_exp(power);
+
+ /* Forget the trap */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT);
+
+ /* Remove the trap */
+ c_ptr->t_idx = 0;
+
+ /* Move the player onto the trap */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR))
+ move_player_aux(dir, do_pickup, 0, TRUE);
+
+ /* Remove trap attr from grid */
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+
+ /* Failure -- Keep trying */
+ else if ((i > 5) && (randint(i) > 5))
+ {
+ /* Failure */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_format("You failed to disarm the %s.", name);
+
+ /* We may keep trying */
+ more = TRUE;
+ }
+
+ /* Failure -- Set off the trap */
+ else
+ {
+ /* Message */
+ msg_format("You set off the %s!", name);
+
+ /* Move the player onto the trap */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR))
+ move_player_aux(dir, do_pickup, 0, FALSE);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Disamrs the monster traps(no failure)
+ */
+void do_cmd_disarm_mon_trap(int y, int x)
+{
+ msg_print("You disarm the monster trap.");
+
+ place_floor_convert_glass(y, x);
+ cave[p_ptr->py][p_ptr->px].special = cave[p_ptr->py][p_ptr->px].special2 = 0;
+}
+
+
+/*
+ * Disarms a trap, or chest
+ */
+void do_cmd_disarm(void)
+{
+ int y, x, dir;
+
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+
+ /* Option: Pick a direction */
+ if (easy_disarm)
+ {
+ int num_traps, num_chests;
+
+ /* Count visible traps */
+ num_traps = count_feats(&y, &x, is_trap, TRUE);
+
+ /* Count chests (trapped) */
+ num_chests = count_chests(&y, &x, TRUE);
+
+ /* See if only one target */
+ if (num_traps || num_chests)
+ {
+ if (num_traps + num_chests <= 1)
+ command_dir = coords_to_dir(y, x);
+ }
+ }
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a direction (or abort) */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Check for chests */
+ o_idx = chest_check(y, x);
+
+ /* Disarm a trap */
+ if (((c_ptr->t_idx == 0) || (!(c_ptr->info & CAVE_TRDT))) &&
+ !o_idx && (c_ptr->feat != FEAT_MON_TRAP))
+ {
+ /* Message */
+ msg_print("You see nothing there to disarm.");
+ }
+
+ /* Monster in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Disarm chest */
+ else if (o_idx)
+ {
+ /* Disarm the chest */
+ more = do_cmd_disarm_chest(y, x, o_idx);
+ }
+
+ /* Disarm trap */
+ else
+ {
+ /* Disarm the trap */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ do_cmd_disarm_mon_trap(y, x);
+ more = FALSE;
+ }
+ else
+ more = do_cmd_disarm_aux(y, x, dir, always_pickup);
+ }
+ }
+
+ /* Cancel repeat unless told not to */
+ if (!more) disturb(0, 0);
+}
+
+
+/*
+ * Perform the basic "bash" command
+ *
+ * Assume destination is a closed/locked/jammed door
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool_ do_cmd_bash_aux(int y, int x, int dir)
+{
+ int bash, temp;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR))
+ {
+ msg_print("You cannot do that.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Message */
+ msg_print("You smash into the door!");
+
+ /* Hack -- Bash power based on strength */
+ /* (Ranges from 3 to 20 to 100 to 200) */
+ bash = adj_str_blow[p_ptr->stat_ind[A_STR]];
+
+ /* Extract door power */
+ temp = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07);
+
+ /* Compare bash power to door power XXX XXX XXX */
+ temp = (bash - (temp * 10));
+
+ /* Hack -- always have a chance */
+ if (temp < 1) temp = 1;
+
+ /* Hack -- attempt to bash down the door */
+ if (rand_int(100) < temp)
+ {
+ /* Message */
+ msg_print("The door crashes open!");
+
+ /* Break down the door */
+ if (rand_int(100) < 50)
+ {
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ cave_set_feat(y, x, FEAT_BROKEN);
+ }
+
+ /* Open the door */
+ else
+ {
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ cave_set_feat(y, x, FEAT_OPEN);
+ }
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+
+ /* Hack -- Fall through the door. Can't disarm while falling. */
+ move_player_aux(dir, always_pickup, 0, FALSE);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE);
+ p_ptr->update |= (PU_DISTANCE);
+ }
+
+ /* Saving throw against stun */
+ else if (rand_int(100) < adj_dex_safe[p_ptr->stat_ind[A_DEX]] + p_ptr->lev)
+ {
+ /* Message */
+ msg_print("The door holds firm.");
+
+ /* Allow repeated bashing */
+ more = TRUE;
+ }
+
+ /* High dexterity yields coolness */
+ else
+ {
+ /* Message */
+ msg_print("You are off-balance.");
+
+ /* Hack -- Lose balance ala paralysis */
+ (void)set_paralyzed(p_ptr->paralyzed + 2 + rand_int(2));
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Bash open a door, success based on character strength
+ *
+ * For a closed door, pval is positive if locked; negative if stuck.
+ *
+ * For an open door, pval is positive for a broken door.
+ *
+ * A closed door can be opened - harder if locked. Any door might be
+ * bashed open (and thereby broken). Bashing a door is (potentially)
+ * faster! You move into the door way. To open a stuck door, it must
+ * be bashed. A closed door can be jammed (see do_cmd_spike()).
+ *
+ * Creatures can also open or bash doors, see elsewhere.
+ */
+void do_cmd_bash(void)
+{
+ int y, x, dir;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR))
+ {
+ msg_print("You cannot do that.");
+
+ return;
+ }
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Bash location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Nothing useful */
+ if ((c_ptr->feat < FEAT_DOOR_HEAD ||
+ c_ptr->feat > FEAT_DOOR_TAIL) &&
+ (c_ptr->feat < FEAT_ALTAR_HEAD ||
+ c_ptr->feat > FEAT_ALTAR_TAIL) && (c_ptr->feat != FEAT_FOUNTAIN))
+ {
+ /* Message */
+ msg_print("You see nothing there to bash.");
+ }
+
+ /* Monster in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ else if (c_ptr->feat >= FEAT_ALTAR_HEAD &&
+ c_ptr->feat <= FEAT_ALTAR_TAIL)
+ {
+ more = do_cmd_bash_altar(y, x);
+ }
+ /* Bash a closed door */
+ else if (c_ptr->feat == FEAT_FOUNTAIN)
+ {
+ more = do_cmd_bash_fountain(y, x);
+ }
+ else
+ {
+ /* Bash the door */
+ more = do_cmd_bash_aux(y, x, dir);
+ }
+ }
+
+ /* Unless valid action taken, cancel bash */
+ if (!more) disturb(0, 0);
+}
+
+
+
+/*
+ * Manipulate an adjacent grid in some way
+ *
+ * Attack monsters, tunnel through walls, disarm traps, open doors.
+ *
+ * Consider confusion XXX XXX XXX
+ *
+ * This command must always take a turn, to prevent free detection
+ * of invisible monsters.
+ */
+void do_cmd_alter(void)
+{
+ int y, x, dir;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Attack monsters */
+ if (c_ptr->m_idx)
+ {
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Open closed doors */
+ else if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Tunnel */
+ more = do_cmd_open_aux(y, x, dir);
+ }
+
+ /* Tunnel through walls */
+ else if (f_info[c_ptr->feat].flags1 & FF1_TUNNELABLE)
+ {
+ /* Tunnel */
+ more = do_cmd_tunnel_aux(y, x, dir);
+ }
+
+ /* Disarm traps */
+ else if (c_ptr->t_idx != 0)
+ {
+ /* Tunnel */
+ more = do_cmd_disarm_aux(y, x, dir, always_pickup);
+ }
+
+ /* Oops */
+ else
+ {
+ /* Oops */
+ msg_print("You attack the empty air.");
+ }
+ }
+
+ /* Cancel repetition unless we can continue */
+ if (!more) disturb(0, 0);
+}
+
+
+/*
+ * Find the index of some "spikes", if possible.
+ *
+ * XXX XXX XXX Let user choose a pile of spikes, perhaps?
+ */
+static bool_ get_spike(int *ip)
+{
+ int i;
+
+
+ /* Check every item in the pack */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Check the "tval" code */
+ if (o_ptr->tval == TV_SPIKE)
+ {
+ /* Save the spike index */
+ (*ip) = i;
+
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Oops */
+ return (FALSE);
+}
+
+
+
+/*
+ * Jam a closed door with a spike
+ *
+ * This command may NOT be repeated
+ */
+void do_cmd_spike(void)
+{
+ int y, x, dir, item;
+
+ cave_type *c_ptr;
+
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Require closed door */
+ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)))
+ {
+ /* Message */
+ msg_print("You see nothing there to spike.");
+ }
+
+ /* Get a spike */
+ else if (!get_spike(&item))
+ {
+ /* Message */
+ msg_print("You have no spikes!");
+ }
+
+ /* Is a monster in the way? */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Go for it */
+ else
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Successful jamming */
+ msg_print("You jam the door with a spike.");
+
+ /* Convert "locked" to "stuck" XXX XXX XXX */
+ if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08) c_ptr->feat += 0x08;
+
+ /* Add one spike to the door */
+ if (c_ptr->feat < FEAT_DOOR_TAIL) c_ptr->feat++;
+
+ /* Use up, and describe, a single spike, from the bottom */
+ inc_stack_size(item, -1);
+ }
+ }
+}
+
+
+static void do_cmd_walk_jump(int pickup, bool_ disarm)
+{
+ int dir;
+
+ bool_ more = FALSE;
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Actually move the character */
+ move_player(dir, pickup, disarm);
+
+ /* Allow more walking */
+ more = TRUE;
+ }
+
+ /* Hack -- In small scale wilderness it takes MUCH more time to move */
+ energy_use *= (p_ptr->wild_mode) ? ((MAX_HGT + MAX_WID) / 2) : 1;
+
+ /* Hack again -- Is there a special encounter ??? */
+ if (p_ptr->wild_mode &&
+ magik(wf_info[wild_map[p_ptr->py][p_ptr->px].feat].level - (p_ptr->lev * 2)))
+ {
+ /* Go into large wilderness view */
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ energy_use = 100;
+ change_wild_mode();
+
+ /* HACk -- set the encouter flag for the wilderness generation */
+ generate_encounter = TRUE;
+ p_ptr->oldpx = MAX_WID / 2;
+ p_ptr->oldpy = MAX_HGT / 2;
+
+ /* Inform the player of his horrible fate :=) */
+ msg_print("You are ambushed!");
+ }
+
+ /* Cancel repeat unless we may continue */
+ if (!more) disturb(0, 0);
+}
+
+
+/*
+ * Support code for the "Walk" and "Jump" commands
+ */
+void do_cmd_walk(int pickup, bool_ disarm)
+{
+ /* Move (usually pickup) */
+
+ if (p_ptr->immovable)
+ {
+ do_cmd_unwalk();
+ }
+ else
+ {
+ do_cmd_walk_jump(pickup, disarm);
+ }
+}
+
+
+void do_cmd_run_run()
+{
+ int dir;
+
+
+ /* Hack -- no running when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Hack -- Set the run counter */
+ running = (command_arg ? command_arg : 1000);
+
+ /* First step */
+ run_step(dir);
+ }
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+/*
+ * Start running.
+ */
+void do_cmd_run(void)
+{
+ if (p_ptr->immovable)
+ {
+ return;
+ }
+ else
+ {
+ do_cmd_run_run();
+ }
+}
+
+
+
+/*
+ * Stay still. Search. Enter stores.
+ * Pick up treasure if "pickup" is true.
+ */
+void do_cmd_stay(int pickup)
+{
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+
+ /* Spontaneous Searching */
+ if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos)))
+ {
+ search();
+ }
+
+ /* Continuous Searching */
+ if (p_ptr->searching)
+ {
+ search();
+ }
+
+
+ /* Handle "objects" */
+ carry(pickup);
+
+
+ /* Hack -- enter a store if we are on one */
+ if (c_ptr->feat == FEAT_SHOP)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- enter store */
+ command_new = '_';
+ }
+}
+
+/*
+ * Resting allows a player to safely restore his hp -RAK-
+ */
+void do_cmd_rest(void)
+{
+ /* Can't rest on a Void Jumpgate -- too dangerous */
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN)
+ {
+ /* 'R&\n' is one of our favourite macros, so we have to do this */
+ if (flush_failure) flush();
+
+ /* Tell the player why */
+ msg_print(format("Resting on a %s is too dangerous!",
+ f_name + f_info[cave[p_ptr->py][p_ptr->px].feat].name));
+
+ /* Done */
+ return;
+ }
+
+ /* Can't rest while undead, it would mean dying */
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ /* 'R&\n' is one of our favourite macros, so we have to do this */
+ if (flush_failure) flush();
+
+ /* Tell the player why */
+ msg_print("Resting is impossible while undead!");
+
+ /* Done */
+ return;
+ }
+
+ /* Prompt for time if needed */
+ if (command_arg <= 0)
+ {
+ cptr p = "Rest (0-9999, '*' for HP/SP, '&' as needed): ";
+
+ char out_val[80];
+
+ /* Default */
+ strcpy(out_val, "&");
+
+ /* Ask for duration */
+ if (!get_string(p, out_val, 4)) return;
+
+ /* Rest until done */
+ if (out_val[0] == '&')
+ {
+ command_arg = ( -2);
+ }
+
+ /* Rest a lot */
+ else if (out_val[0] == '*')
+ {
+ command_arg = ( -1);
+ }
+
+ /* Rest some */
+ else
+ {
+ command_arg = atoi(out_val);
+ if (command_arg <= 0) return;
+ }
+ }
+
+
+ /* Paranoia */
+ if (command_arg > 9999) command_arg = 9999;
+
+
+ /* Take a turn XXX XXX XXX (?) */
+ energy_use = 100;
+
+ /* Save the rest code */
+ resting = command_arg;
+
+ /* Cancel searching */
+ p_ptr->searching = FALSE;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Refresh */
+ Term_fresh();
+}
+
+
+
+
+
+
+/*
+ * Determines the odds of an object breaking when thrown at a monster
+ *
+ * Note that artifacts never break, see the "drop_near()" function.
+ */
+int breakage_chance(object_type *o_ptr)
+{
+ int reducer =
+ 1 + ((get_skill(SKILL_ARCHERY)) ? (get_skill_scale(SKILL_ARCHERY, 10)) : 0);
+
+ /* Examine the item type */
+ switch (o_ptr->tval)
+ {
+ /* Always break */
+ case TV_FLASK:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_BOTTLE:
+ case TV_FOOD:
+ {
+ return (100);
+ }
+
+ /* Often break */
+ case TV_LITE:
+ case TV_SCROLL:
+ case TV_SKELETON:
+ {
+ return (50);
+ }
+
+ case TV_ARROW:
+ {
+ return (50 / reducer);
+ }
+
+ /* Sometimes break */
+ case TV_WAND:
+ case TV_SPIKE:
+ {
+ return (25);
+ }
+
+ case TV_SHOT:
+ case TV_BOLT:
+ {
+ return (25 / reducer);
+ }
+ case TV_BOOMERANG:
+ {
+ return 1;
+ }
+ }
+
+ /* Rarely break */
+ return (10);
+}
+
+/*
+ * Return multiplier of an object
+ */
+int get_shooter_mult(object_type *o_ptr)
+{
+ /* Assume a base multiplier */
+ int tmul = 1;
+
+ /* Analyze the launcher */
+ switch (o_ptr->sval)
+ {
+ case SV_SLING:
+ {
+ /* Sling and ammo */
+ tmul = 2;
+ break;
+ }
+
+ case SV_SHORT_BOW:
+ {
+ /* Short Bow and Arrow */
+ tmul = 2;
+ break;
+ }
+
+ case SV_LONG_BOW:
+ {
+ /* Long Bow and Arrow */
+ tmul = 3;
+ break;
+ }
+
+ /* Light Crossbow and Bolt */
+ case SV_LIGHT_XBOW:
+ {
+ tmul = 3;
+ break;
+ }
+
+ /* Heavy Crossbow and Bolt */
+ case SV_HEAVY_XBOW:
+ {
+ tmul = 4;
+ break;
+ }
+ }
+ return tmul;
+}
+
+
+/*
+ * Fire an object from the pack or floor.
+ *
+ * You may only fire items that "match" your missile launcher.
+ *
+ * You must use slings + pebbles/shots, bows + arrows, xbows + bolts.
+ *
+ * See "calc_bonuses()" for more calculations and such.
+ *
+ * Note that "firing" a missile is MUCH better than "throwing" it.
+ *
+ * Note: "unseen" monsters are very hard to hit.
+ *
+ * Objects are more likely to break if they "attempt" to hit a monster.
+ *
+ * Rangers (with Bows) and Anyone (with "Extra Shots") get extra shots.
+ *
+ * The "extra shot" code works by decreasing the amount of energy
+ * required to make each shot, spreading the shots out over time.
+ *
+ * Note that when firing missiles, the launcher multiplier is applied
+ * after all the bonuses are added in, making multipliers very useful.
+ *
+ * Note that Bows of "Extra Might" get extra range and an extra bonus
+ * for the damage multiplier.
+ *
+ * Note that Bows of "Extra Shots" give an extra shot.
+ */
+void do_cmd_fire(void)
+{
+ int dir, item;
+
+ int j, y, x, ny, nx, ty, tx, by, bx;
+
+ int oldtdam, tdam, tdis, thits, tmul;
+
+ int bonus, chance;
+
+ int cur_dis, visible;
+
+ int breakage = -1, num_pierce = 0;
+
+ s32b special = 0;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ object_type *j_ptr;
+
+ bool_ hit_body = FALSE;
+
+ byte missile_attr;
+
+ char missile_char;
+
+ char o_name[80];
+
+ cptr q, s;
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+
+ /* Get the "bow" (if any) */
+ j_ptr = &p_ptr->inventory[INVEN_BOW];
+
+ /* Require a launcher */
+ if (!j_ptr->tval)
+ {
+ msg_print("You have nothing with which to fire.");
+ return;
+ }
+
+ /* XXX HACK */
+ if (j_ptr->tval == TV_INSTRUMENT)
+ {
+ msg_print("You cannot fire with an instrument.");
+ return;
+ }
+
+ /* Get the "ammo" (if any) */
+ o_ptr = &p_ptr->inventory[INVEN_AMMO];
+
+ item = INVEN_AMMO;
+
+ /* If nothing correct try to choose from the backpack */
+ if ((p_ptr->tval_ammo != o_ptr->tval) || (!o_ptr->k_idx))
+ {
+ /* Require proper missile */
+ item_tester_tval = p_ptr->tval_ammo;
+
+ /* Get an item */
+ q = "Your quiver is empty. Fire which item? ";
+ s = "You have nothing to fire.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+
+ /* Access the item */
+ o_ptr = get_object(item);
+ }
+
+
+ /* Get a direction (or cancel) */
+ if (!get_aim_dir(&dir)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Single object */
+ q_ptr->number = 1;
+
+ /* Reduce stack and describe */
+ inc_stack_size(item, -1);
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+
+ /* Sound */
+ sound(SOUND_SHOOT);
+
+
+ /* Describe the object */
+ object_desc(o_name, q_ptr, FALSE, 3);
+
+ /* Find the color and symbol for the object for throwing */
+ missile_attr = object_attr(q_ptr);
+ missile_char = object_char(q_ptr);
+
+
+ /* Use the proper number of shots */
+ thits = p_ptr->num_fire;
+
+ /* Use a base distance */
+ tdis = 10;
+
+ /* Base damage from thrown object plus launcher bonus */
+ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + j_ptr->to_d;
+
+ /* Actually "fire" the object */
+ bonus = (p_ptr->to_h + p_ptr->to_h_ranged + q_ptr->to_h + j_ptr->to_h);
+
+ chance = (p_ptr->skill_thb + (bonus * BTH_PLUS_ADJ));
+ if (chance < 5) chance = 5;
+
+ tmul = get_shooter_mult(j_ptr);
+
+ /* Get extra "power" from "extra might" */
+ tmul += p_ptr->xtra_might;
+
+ /* Boost the damage */
+ tdam *= tmul;
+
+ /* Add in the player damage */
+ tdam += p_ptr->to_d_ranged;
+
+ /* Base range */
+ tdis = 10 + 5 * tmul;
+
+
+ /* Take a (partial) turn */
+ energy_use = (100 / thits);
+
+ /* piercing shots ? */
+ if (p_ptr->use_piercing_shots)
+ {
+ num_pierce = (get_skill(SKILL_COMBAT) / 10) - 1;
+ num_pierce = (num_pierce < 0) ? 0 : num_pierce;
+ }
+
+ /* Start at the player */
+ by = p_ptr->py;
+ bx = p_ptr->px;
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Predict the "target" location */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Check for "target request" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ oldtdam = tdam;
+ while (TRUE)
+ {
+ /* Reset after a piercing shot */
+ tdam = oldtdam;
+
+ /* Travel until stopped */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == ty) && (x == tx)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, by, bx, ty, tx);
+
+ /* Stopped by walls/doors */
+ if (!cave_floor_bold(ny, nx)) break;
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+
+
+ /* Monster here, Try to hit it */
+ if (cave[y][x].m_idx)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Check the visibility */
+ visible = m_ptr->ml;
+
+ /* Note the collision */
+ hit_body = TRUE;
+
+ /* Did we hit it (penalize range) */
+ if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml))
+ {
+ bool_ fear = FALSE;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+
+ /* Handle unseen monster */
+ if (!visible)
+ {
+ /* Invisible monster */
+ msg_format("The %s finds a mark.", o_name);
+ }
+
+ /* Handle visible monster */
+ else
+ {
+ char m_name[80];
+
+ /* Get "the monster" or "it" */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("The %s hits %s.", o_name, m_name);
+
+ /* Hack -- Track this monster race */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx,
+ m_ptr->ego);
+
+ /* Hack -- Track this monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+
+ /* Anger friends */
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ {
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ }
+ case 0:
+ {
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Apply special damage XXX XXX XXX */
+ tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special);
+ tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (tdam < 0) tdam = 0;
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.",
+ tdam, m_ptr->hp);
+ }
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(c_ptr->m_idx, tdam);
+
+ if (special) attack_special(m_ptr, special, tdam);
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+ }
+
+ /* Stop looking */
+ break;
+ }
+ }
+
+ /* Exploding arrow ? */
+ if (q_ptr->pval2 != 0)
+ {
+ int rad = 0, dam =
+ (damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d) * 2;
+ int flag =
+ PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL |
+ PROJECT_JUMP;
+ switch (q_ptr->sval)
+ {
+ case SV_AMMO_LIGHT:
+ rad = 2;
+ dam /= 2;
+ break;
+ case SV_AMMO_NORMAL:
+ rad = 3;
+ break;
+ case SV_AMMO_HEAVY:
+ rad = 4;
+ dam *= 2;
+ break;
+ }
+
+ project(0, rad, y, x, dam, q_ptr->pval2, flag);
+ }
+
+ /* Chance of breakage (during attacks) */
+ j = (hit_body ? breakage_chance(q_ptr) : 0);
+
+ /* Break ? */
+ if ((q_ptr->pval2 != 0) || (rand_int(100) < j))
+ {
+ breakage = 100;
+ break;
+ }
+
+ /* If the ammo doesn't break, it can pierce through */
+ if ((num_pierce) && (hit_body) &&
+ (magik(45 + get_skill(SKILL_ARCHERY))))
+ {
+ num_pierce--;
+ hit_body = FALSE;
+
+ /* If target isn't reached, continue moving to target */
+ if ( !((tx < x && x < bx) || (bx < x && x < tx)) &&
+ !((ty < y && y < by) || (by < y && y < ty)))
+ {
+ /* Continue moving in same direction if we reached the target */
+ int dx = tx - bx;
+ int dy = ty - by;
+ tx = x + 99 * dx;
+ ty = y + 99 * dy;
+
+ /* New base location */
+ by = y;
+ bx = x;
+ }
+
+ msg_format("The %s pierces through!", o_name);
+ }
+ else
+ break;
+ }
+
+ /* Drop (or break) near that location */
+ drop_near(q_ptr, breakage, y, x);
+}
+
+
+/*
+ * Why is this here? even if it's temporary boost...
+ * Moved into player_type, hoping it might be useful in future extensions
+ * -- pelpel
+ */
+/* int throw_mult = 1; */
+
+/*
+ * Throw an object from the pack or floor.
+ *
+ * Note: "unseen" monsters are very hard to hit.
+ *
+ * Should throwing a weapon do full damage? Should it allow the magic
+ * to hit bonus of the weapon to have an effect? Should it ever cause
+ * the item to be destroyed? Should it do any damage at all?
+ */
+void do_cmd_throw(void)
+{
+ int dir, item;
+
+ s32b special = 0;
+
+ int j, y, x, ny, nx, ty, tx;
+
+ int chance, tdam, tdis;
+
+ int mul, div;
+
+ int boulder_add = 0;
+ int boulder_mult = 0;
+
+ int cur_dis, visible;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ bool_ hit_body = FALSE;
+
+ bool_ hit_wall = FALSE;
+
+ byte missile_attr;
+
+ char missile_char;
+
+ char o_name[80];
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Get an item */
+ q = "Throw which item? ";
+ s = "You have nothing to throw.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Access the item */
+ o_ptr = get_object(item);
+
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack - Cannot throw away 'no drop' cursed items */
+ if (cursed_p(o_ptr) && (f4 & TR4_CURSE_NO_DROP))
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to throw it.");
+
+ /* Nope */
+ return;
+ }
+
+ /* Boulder throwing */
+ if ((o_ptr->tval == TV_JUNK) && (o_ptr->sval == SV_BOULDER) && (get_skill(SKILL_BOULDER)))
+ {
+ boulder_add = get_skill_scale(SKILL_BOULDER, 80);
+ boulder_mult = get_skill_scale(SKILL_BOULDER, 6);
+ }
+
+ /* Get a direction (or cancel) */
+ if (!get_aim_dir(&dir)) return;
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /*
+ * Hack -- If rods or wands are thrown, the total maximum timeout or
+ * charges need to be allocated between the two stacks.
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval / o_ptr->number;
+
+ if (o_ptr->number > 1) o_ptr->pval -= q_ptr->pval;
+ }
+
+ /* Single object */
+ q_ptr->number = 1;
+
+ /* Reduce stack and describe */
+ inc_stack_size(item, -1);
+
+ /* Description */
+ object_desc(o_name, q_ptr, FALSE, 3);
+
+ /* Find the color and symbol for the object for throwing */
+ missile_attr = object_attr(q_ptr);
+ missile_char = object_char(q_ptr);
+
+ /* Extract a "distance multiplier" */
+ /* Changed for 'launcher' corruption */
+ mul = 10 + (2 * (p_ptr->throw_mult - 1)) + (2 * boulder_mult);
+
+ /* Enforce a minimum "weight" of one pound */
+ div = ((q_ptr->weight > 10) ? q_ptr->weight : 10);
+
+ /* Hack -- Distance -- Reward strength, penalize weight */
+ tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div;
+
+ /* Max distance of 10-18 */
+ if (tdis > mul) tdis = mul;
+
+ /* Hack -- Base damage from thrown object */
+ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + boulder_add;
+ tdam *= p_ptr->throw_mult + boulder_mult;
+
+ /* Chance of hitting - adjusted for Weaponmasters -- Gumby */
+ chance = (p_ptr->skill_tht + (p_ptr->to_h * BTH_PLUS_ADJ));
+
+ /* Take a turn */
+ energy_use = 100;
+
+
+ /* Start at the player */
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Predict the "target" location */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Check for "target request" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+
+ /* Travel until stopped */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == ty) && (x == tx)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx);
+
+ /* Stopped by walls/doors */
+ if (!cave_floor_bold(ny, nx))
+ {
+ hit_wall = TRUE;
+ break;
+ }
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+
+
+ /* Monster here, Try to hit it */
+ if (cave[y][x].m_idx)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Check the visibility */
+ visible = m_ptr->ml;
+
+ /* Note the collision */
+ hit_body = TRUE;
+
+ /* Did we hit it (penalize range) */
+ if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml))
+ {
+ bool_ fear = FALSE;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+
+ /* Handle unseen monster */
+ if (!visible)
+ {
+ /* Invisible monster */
+ msg_format("The %s finds a mark.", o_name);
+ }
+
+ /* Handle visible monster */
+ else
+ {
+ char m_name[80];
+
+ /* Get "the monster" or "it" */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("The %s hits %s.", o_name, m_name);
+
+ /* Hack -- Track this monster race */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack -- Track this monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+ }
+
+ /* Apply special damage XXX XXX XXX */
+ tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special);
+ tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, o_ptr->sval == SV_BOULDER ? SKILL_BOULDER : SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (tdam < 0) tdam = 0;
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.",
+ tdam, m_ptr->hp);
+ }
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(c_ptr->m_idx, tdam);
+
+ if (special) attack_special(m_ptr, special, tdam);
+
+ /* Anger friends */
+ if (!(k_info[q_ptr->k_idx].tval == TV_POTION))
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+ }
+
+ /* Stop looking */
+ break;
+ }
+ }
+
+ /* Chance of breakage (during attacks) */
+ j = (hit_body ? breakage_chance(q_ptr) : 0);
+
+ /* Potions smash open */
+ if (k_info[q_ptr->k_idx].tval == TV_POTION)
+ {
+ if ((hit_body) || (hit_wall) || (randint(100) < j))
+ {
+ /* Message */
+ msg_format("The %s shatters!", o_name);
+
+ if (potion_smash_effect(0, y, x, q_ptr->sval))
+ {
+ if (cave[y][x].m_idx)
+ {
+ char m_name[80];
+ monster_desc(m_name, &m_list[cave[y][x].m_idx], 0);
+ switch (is_friend(&m_list[cave[y][x].m_idx]))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(&m_list[cave[y][x].m_idx]);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_list[cave[y][x].m_idx].status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+ }
+
+ return;
+ }
+ else
+ {
+ j = 0;
+ }
+ }
+
+ /* Drop (or break) near that location */
+ drop_near(q_ptr, j, y, x);
+}
+
+
+/*
+ * Throw a boomerang object from the equipement(bow).
+ *
+ * Note: "unseen" monsters are very hard to hit.
+ *
+ * Should throwing a weapon do full damage? Should it allow the magic
+ * to hit bonus of the weapon to have an effect? Should it ever cause
+ * the item to be destroyed? Should it do any damage at all?
+ */
+void do_cmd_boomerang(void)
+{
+ int dir;
+
+ int j, y, x, ny, nx, ty, tx;
+
+ int chance, tdam, tdis;
+
+ int mul, div;
+
+ int cur_dis, visible;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ bool_ hit_body = FALSE;
+
+ byte missile_attr;
+
+ char missile_char;
+
+ char o_name[80];
+
+ s32b special = 0;
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+
+ /* Get the "bow" (if any) */
+ o_ptr = &p_ptr->inventory[INVEN_BOW];
+
+
+ /* Get a direction (or cancel) */
+ if (!get_aim_dir(&dir)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Single object */
+ q_ptr->number = 1;
+
+ /* Description */
+ object_desc(o_name, q_ptr, FALSE, 3);
+
+ /* Find the color and symbol for the object for throwing */
+ missile_attr = object_attr(q_ptr);
+ missile_char = object_char(q_ptr);
+
+ /* Extract a "distance multiplier" */
+ /* Changed for 'launcher' corruption */
+ mul = 10 + 2 * (p_ptr->throw_mult - 1);
+
+ /* Enforce a minimum "weight" of one pound */
+ div = ((q_ptr->weight > 10) ? q_ptr->weight : 10);
+
+ /* Hack -- Distance -- Reward strength, penalize weight */
+ tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div;
+
+ /* Max distance of 10-18 */
+ if (tdis > mul) tdis = mul;
+
+ /* Hack -- Base damage from thrown object */
+ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d;
+ tdam *= p_ptr->throw_mult;
+
+ /* Chance of hitting */
+ chance =
+ (p_ptr->skill_tht +
+ ((p_ptr->to_h + p_ptr->to_h_ranged) * BTH_PLUS_ADJ));
+
+ chance += get_skill(SKILL_BOOMERANG);
+
+ /* Take a turn */
+ energy_use = 100;
+
+
+ /* Start at the player */
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Predict the "target" location */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Check for "target request" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+
+ /* Travel until stopped */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == ty) && (x == tx)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx);
+
+ /* Stopped by walls/doors */
+ if (!cave_floor_bold(ny, nx))
+ {
+ break;
+ }
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+
+
+ /* Monster here, Try to hit it */
+ if (cave[y][x].m_idx)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Check the visibility */
+ visible = m_ptr->ml;
+
+ /* Note the collision */
+ hit_body = TRUE;
+
+ /* Did we hit it (penalize range) */
+ if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml))
+ {
+ bool_ fear = FALSE;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+
+ /* Handle unseen monster */
+ if (!visible)
+ {
+ /* Invisible monster */
+ msg_format("The %s finds a mark.", o_name);
+ }
+
+ /* Handle visible monster */
+ else
+ {
+ char m_name[80];
+
+ /* Get "the monster" or "it" */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("The %s hits %s.", o_name, m_name);
+
+ /* Hack -- Track this monster race */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack -- Track this monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+ }
+
+ /* Apply special damage XXX XXX XXX */
+ tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special);
+ tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (tdam < 0) tdam = 0;
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.",
+ tdam, m_ptr->hp);
+ }
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(c_ptr->m_idx, tdam);
+
+ if (special) attack_special(m_ptr, special, tdam);
+
+ /* Anger friends */
+ if (!(k_info[q_ptr->k_idx].tval == TV_POTION))
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+
+ /* Chance of breakage (during attacks) */
+ j = (hit_body ? breakage_chance(o_ptr) : 0);
+
+ /* Break the boomerang */
+ if (!(o_ptr->art_name || artifact_p(o_ptr)) &&
+ (rand_int(100) < j))
+ {
+ msg_print(format("Your %s is destroyed.", o_name));
+ inc_stack_size_ex(INVEN_BOW, -1, OPTIMIZE, NO_DESCRIBE);
+ }
+ }
+
+ /* Stop looking */
+ break;
+ }
+ }
+
+ /* Travel back to the player */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == p_ptr->py) && (x == p_ptr->px)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, ty, tx, p_ptr->py, p_ptr->px);
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+ }
+}
+
+
+/*
+ * Try to ``walk'' using phase door.
+ */
+void do_cmd_unwalk()
+{
+ int dir, y, x, feat;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+
+ if (!get_rep_dir(&dir)) return;
+
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ c_ptr = &cave[y][x];
+ feat = c_ptr->feat;
+
+ /* Must have knowledge to know feature XXX XXX */
+ if (!(c_ptr->info & (CAVE_MARK))) feat = FEAT_NONE;
+
+ /* Take a turn */
+ energy_use = 100;
+ energy_use *= (p_ptr->wild_mode) ? (5 * (MAX_HGT + MAX_WID) / 2) : 1;
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+
+ /* Attack monsters */
+ if (c_ptr->m_idx > 0)
+ {
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Exit the area */
+ else if ((!dun_level) && (!p_ptr->wild_mode) &&
+ ((x == 0) || (x == cur_wid - 1) || (y == 0) || (y == cur_hgt - 1)))
+ {
+ /* Can the player enter the grid? */
+ if (player_can_enter(c_ptr->mimic))
+ {
+ /* Hack: move to new area */
+ if ((y == 0) && (x == 0))
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->wilderness_x--;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = cur_wid - 2;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == 0) && (x == MAX_WID - 1))
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->wilderness_x++;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = 1;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == MAX_HGT - 1) && (x == 0))
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->wilderness_x--;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = cur_wid - 2;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1))
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->wilderness_x++;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = 1;
+ ambush_flag = FALSE;
+ }
+
+ else if (y == 0)
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = x;
+ ambush_flag = FALSE;
+ }
+
+ else if (y == cur_hgt - 1)
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = x;
+ ambush_flag = FALSE;
+ }
+
+ else if (x == 0)
+ {
+ p_ptr->wilderness_x--;
+ p_ptr->oldpx = cur_wid - 2;
+ p_ptr->oldpy = y;
+ ambush_flag = FALSE;
+ }
+
+ else if (x == cur_wid - 1)
+ {
+ p_ptr->wilderness_x++;
+ p_ptr->oldpx = 1;
+ p_ptr->oldpy = y;
+ ambush_flag = FALSE;
+ }
+
+ p_ptr->leaving = TRUE;
+
+ return;
+ }
+ }
+
+ /* Hack -- Ignore weird terrain types. */
+ else if (!cave_floor_grid(c_ptr))
+ {
+ teleport_player(10);
+ }
+
+ /* Enter quests */
+ else if (((feat >= FEAT_QUEST_ENTER) && (feat <= FEAT_QUEST_UP)) ||
+ ((feat >= FEAT_LESS) && (feat <= FEAT_MORE)))
+ {
+ move_player(dir, always_pickup, TRUE);
+ more = FALSE;
+ }
+
+ /* Hack -- Ignore wilderness mofe. */
+ else if (p_ptr->wild_mode)
+ {
+ /* Chance to not blink right */
+ if (magik(15))
+ {
+ do
+ {
+ dir = rand_range(1, 9);
+ }
+ while (dir == 5);
+ }
+
+ move_player(dir, always_pickup, TRUE);
+ }
+
+ /* Walking semantics */
+ else
+ {
+ teleport_player_directed(10, dir);
+ }
+
+ /* Cancel repetition unless we can continue */
+ if (!more) disturb(0, 0);
+}
+
+
+static bool_ tport_vertically(bool_ how)
+{
+ /* arena or quest -KMW- */
+ if ((p_ptr->inside_arena) || (p_ptr->inside_quest))
+ {
+ msg_print("There is no effect.");
+ return (FALSE);
+ }
+
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerful force prevents you from teleporting.");
+ return FALSE;
+ }
+
+ /* Go down */
+ if (how)
+ {
+ if (dun_level >= d_info[dungeon_type].maxdepth)
+ {
+ msg_print("The floor is impermeable.");
+ return (FALSE);
+ }
+
+ msg_print("You sink through the floor.");
+ dun_level++;
+ p_ptr->leaving = TRUE;
+ }
+ else
+ {
+ if (dun_level < d_info[dungeon_type].mindepth)
+ {
+ msg_print("There is nothing above you but air.");
+ return (FALSE);
+ }
+
+ msg_print("You rise through the ceiling.");
+ dun_level--;
+ p_ptr->leaving = TRUE;
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Do a special ``movement'' action. Meant to be used for ``immovable''
+ * characters.
+ */
+void do_cmd_immovable_special(void)
+{
+ int i, ii, ij, dir;
+
+ int foo = p_ptr->immov_cntr;
+
+ int lose_sp = 0;
+
+ int lose_hp = 0;
+
+ bool_ did_act = FALSE;
+
+ bool_ did_load = FALSE;
+
+
+ if (foo > 1)
+ {
+ if (p_ptr->csp > foo / 2)
+ {
+
+ msg_format("This will drain %d mana points!", foo / 2);
+ if (!get_check("Proceed? ")) return;
+
+ lose_sp = foo / 2;
+
+ }
+ else if (p_ptr->chp > foo / 2)
+ {
+
+ msg_format("Warning: This will drain %d hit points!", foo / 2);
+ if (!get_check("Proceed? ")) return;
+
+ lose_hp = foo / 2;
+
+ }
+ else
+ {
+ msg_print("You can't use your powers yet.");
+ return;
+ }
+ }
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Do what special action:", 2, 0);
+
+ /* Give some choices */
+ prt("(a) Teleport to a specific place.", 4, 5);
+ prt("(b) Fetch an item.", 5, 5);
+ prt("(c) Go up 50'", 6, 5);
+ prt("(d) Go down 50'", 7, 5);
+
+ /* Prompt */
+ prt("Command: ", 9, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ /* Tele-to */
+ if (i == 'a')
+ {
+ Term_load();
+ character_icky = FALSE;
+ did_load = TRUE;
+
+ if (!tgt_pt(&ii, &ij)) break;
+
+ /* Teleport to the target */
+ teleport_player_to(ij, ii);
+
+ did_act = TRUE;
+ break;
+ }
+
+ /* Fetch item */
+ else if (i == 'b')
+ {
+ Term_load();
+ character_icky = FALSE;
+ did_load = TRUE;
+
+ if (!get_aim_dir(&dir)) return;
+ fetch(dir, p_ptr->lev * 15, FALSE);
+ py_pickup_floor(always_pickup);
+
+ did_act = TRUE;
+ break;
+ }
+
+ /* Move up */
+ else if (i == 'c')
+ {
+ Term_load();
+ character_icky = FALSE;
+ did_load = TRUE;
+
+ if (!tport_vertically(FALSE)) return;
+
+ did_act = TRUE;
+ break;
+ }
+
+ /* Move down */
+ else if (i == 'd')
+ {
+ Term_load();
+ character_icky = FALSE;
+ did_load = TRUE;
+
+ if (!tport_vertically(TRUE)) return;
+
+ did_act = TRUE;
+ break;
+ }
+
+ /* Unknown option */
+ else
+ {
+ bell();
+ }
+
+ }
+
+ /* Check if screen was restored before */
+ if (!did_load)
+ {
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+ }
+
+ /* Apply stat losses if something was done */
+ if (did_act)
+ {
+ p_ptr->immov_cntr += 101 - (p_ptr->lev * 2);
+
+ if (lose_sp)
+ {
+ p_ptr->csp -= lose_sp;
+ p_ptr->redraw |= (PR_MANA);
+ }
+
+ if (lose_hp)
+ {
+ p_ptr->chp -= lose_hp;
+ p_ptr->redraw |= (PR_HP);
+ }
+
+ energy_use = 100;
+ }
+}
+
+/* Can we sacrifice it ? */
+static bool_ item_tester_hook_sacrifiable(object_type *o_ptr)
+{
+ GOD(GOD_MELKOR)
+ {
+ /* Corpses are */
+ if (o_ptr->tval == TV_CORPSE && o_ptr->sval == SV_CORPSE_CORPSE)
+ return (TRUE);
+
+ /* Books without any udun spells */
+ if ((o_ptr->tval == TV_BOOK) && (exec_lua(format("return udun_in_book(%d, %d)", o_ptr->sval, o_ptr->pval)) == 0))
+ return TRUE;
+ }
+
+ /* Assume not */
+ return (FALSE);
+}
+
+/*
+ * Handle sacrifices.
+ * Grace is increased by value of sacrifice.
+ */
+void do_cmd_sacrifice(void)
+{
+ byte on_what = cave[p_ptr->py][p_ptr->px].feat;
+
+ /* Check valididty */
+ if ((on_what < FEAT_ALTAR_HEAD) || (on_what > FEAT_ALTAR_TAIL))
+ {
+ show_god_info(FALSE);
+ return;
+ }
+ else
+ {
+ int agod = on_what - FEAT_ALTAR_HEAD + 1;
+
+ /* Not worshipping a god ? ahhhh! */
+ GOD(GOD_NONE)
+ {
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ if (deity_info[agod].desc[i] != NULL)
+ msg_print(deity_info[agod].desc[i]);
+ }
+ if (get_check(format("Do you want to worship %s? ", deity_info[agod].name)))
+ {
+ follow_god(agod, FALSE);
+ p_ptr->grace = -200;
+ inc_piety(p_ptr->pgod, 0);
+ }
+ }
+ else if (p_ptr->pgod == agod)
+ {
+ GOD(GOD_MELKOR)
+ {
+ /* One can sacrifice some HP for piety or damage */
+ if ((p_ptr->mhp > 10) && (p_ptr->chp > 10) && get_check("Do you want to sacrifice a part of yourself? "))
+ {
+ /* 10 HP = 300 * wis piety */
+ if (get_check("Do you want to sacrifice for more piety instead of damage? "))
+ {
+ int x = wisdom_scale(6);
+ if (x < 1) x = 1;
+
+ p_ptr->hp_mod -= 10;
+ take_hit(10, "self sacrifice to Melkor");
+ msg_print("Your life slips away, and Melkor seems happier.");
+ inc_piety(GOD_MELKOR, x * 300);
+ p_ptr->update |= (PU_HP);
+ }
+ /* 10 HP = +wis damage */
+ else
+ {
+ take_hit(10, "self sacrifice to Melkor");
+ msg_print("Your life slips away, and your arms grow stronger.");
+ p_ptr->melkor_sacrifice++;
+ p_ptr->update |= (PU_BONUS | PU_HP);
+ }
+ }
+ else
+ {
+ int item;
+ object_type *o_ptr;
+
+ /* Restrict choices to food */
+ item_tester_hook = item_tester_hook_sacrifiable;
+
+ /* Get an item */
+ if (!get_item(&item, "Sacrifice which item? ", "You have nothing to sacrifice.", (USE_INVEN))) return;
+ o_ptr = get_object(item);
+
+ /* Piety for corpses is based on monster level */
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ inc_piety(GOD_MELKOR, 2 * r_info[o_ptr->pval2].level);
+ }
+
+ /* In books it depends of the spell levels*/
+ if (o_ptr->tval == TV_BOOK)
+ {
+ int x = exec_lua(format("return levels_in_book(%d, %d)", o_ptr->sval, o_ptr->pval));
+
+ inc_piety(GOD_MELKOR, 2 * x);
+ }
+
+ /* Remove the item */
+ inc_stack_size(item, -1);
+ }
+ }
+ else
+ {
+ process_hooks(HOOK_SACRIFICE_GOD, "()", "");
+ }
+ }
+ }
+}
+
+
+/*
+ * scan_monst --
+ *
+ * Return a list of o_list[] indexes of items of the given monster
+ */
+bool_ scan_monst(int *items, int *item_num, int m_idx)
+{
+ int this_o_idx, next_o_idx;
+
+ int num = 0;
+
+
+ (*item_num) = 0;
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = m_list[m_idx].hold_o_idx; this_o_idx;
+ this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Accept this item */
+ items[num++] = this_o_idx;
+
+ /* XXX Hack -- Enforce limit */
+ if (num == 23) break;
+ }
+
+ /* Number of items */
+ (*item_num) = num;
+
+ /* Result */
+ return (num != 0);
+}
+
+
+/*
+ * Display a list of the items that the given monster carries.
+ */
+byte show_monster_inven(int m_idx, int *monst_list)
+{
+ int i, j, k, l;
+
+ int col, len, lim;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char tmp_val[80];
+
+ int out_index[23];
+
+ byte out_color[23];
+
+ char out_desc[23][80];
+
+ int monst_num;
+
+
+ /* Default length */
+ len = 79 - 50;
+
+ /* Maximum space allowed for descriptions */
+ lim = 79 - 3;
+
+ /* Require space for weight (if needed) */
+ if (show_weights) lim -= 9;
+
+ /* Scan for objects on the monster */
+ (void)scan_monst(monst_list, &monst_num, m_idx);
+
+ /* Display the p_ptr->inventory */
+ for (k = 0, i = 0; i < monst_num; i++)
+ {
+ o_ptr = &o_list[monst_list[i]];
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Hack -- enforce max length */
+ o_name[lim] = '\0';
+
+ /* Save the index */
+ out_index[k] = i;
+
+ /* Acquire p_ptr->inventory color */
+ out_color[k] = tval_to_attr[o_ptr->tval & 0x7F];
+
+ /* Save the object description */
+ strcpy(out_desc[k], o_name);
+
+ /* Find the predicted "line length" */
+ l = strlen(out_desc[k]) + 5;
+
+ /* Be sure to account for the weight */
+ if (show_weights) l += 9;
+
+ /* Maintain the maximum length */
+ if (l > len) len = l;
+
+ /* Advance to next "line" */
+ k++;
+ }
+
+ /* Find the column to start in */
+ col = (len > 76) ? 0 : (79 - len);
+
+ /* Output each entry */
+ for (j = 0; j < k; j++)
+ {
+ /* Get the index */
+ i = monst_list[out_index[j]];
+
+ /* Get the item */
+ o_ptr = &o_list[i];
+
+ /* Clear the line */
+ prt("", j + 1, col ? col - 2 : col);
+
+ /* Prepare an index --(-- */
+ strnfmt(tmp_val, 80, "%c)", index_to_label(j));
+
+ /* Clear the line with the (possibly indented) index */
+ put_str(tmp_val, j + 1, col);
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], j + 1, col + 3);
+
+ /* Display the weight if needed */
+ if (show_weights)
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ strnfmt(tmp_val, 80, "%3d.%1d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, j + 1, 71);
+ }
+ }
+
+ /* Make a "shadow" below the list (only if needed) */
+ if (j && (j < 23)) prt("", j + 1, col ? col - 2 : col);
+
+ return monst_num;
+}
+
+
+/*
+ * Steal an object from a monster
+ */
+void do_cmd_steal()
+{
+ int x, y, dir = 0, item = -1, k = -1;
+
+ cave_type *c_ptr;
+
+ monster_type *m_ptr;
+
+ object_type *o_ptr, forge;
+
+ byte num = 0;
+
+ bool_ done = FALSE;
+
+ int monst_list[23];
+
+
+ /* Only works on adjacent monsters */
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("There is no monster there!");
+ return;
+ }
+
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* There were no non-gold items */
+ if (!m_ptr->hold_o_idx)
+ {
+ msg_print("That monster has no objects!");
+ return;
+ }
+
+ /* The monster is immune */
+ if (r_info[m_ptr->r_idx].flags7 & (RF7_NO_THEFT))
+ {
+ msg_print("The monster is guarding the treasures.");
+ return;
+ }
+
+ screen_save();
+
+ num = show_monster_inven(c_ptr->m_idx, monst_list);
+
+ /* Repeat until done */
+ while (!done)
+ {
+ char tmp_val[80];
+ char which = ' ';
+
+ /* Build the prompt */
+ strnfmt(tmp_val, 80, "Choose an item to steal (a-%c) or ESC:",
+ 'a' - 1 + num);
+
+ /* Show the prompt */
+ prt(tmp_val, 0, 0);
+
+ /* Get a key */
+ which = inkey();
+
+ /* Parse it */
+ switch (which)
+ {
+ case ESCAPE:
+ {
+ done = TRUE;
+
+ break;
+ }
+
+ default:
+ {
+ int ver;
+
+ /* Extract "query" setting */
+ ver = isupper(which);
+ which = tolower(which);
+
+ k = islower(which) ? A2I(which) : -1;
+ if (k < 0 || k >= num)
+ {
+ bell();
+
+ break;
+ }
+
+ /* Verify the item */
+ if (ver && !verify("Try", 0 - monst_list[k]))
+ {
+ done = TRUE;
+
+ break;
+ }
+
+ /* Accept that choice */
+ item = monst_list[k];
+ done = TRUE;
+
+ break;
+ }
+ }
+ }
+
+ if (item != -1)
+ {
+ int chance;
+
+ chance = 40 - p_ptr->stat_ind[A_DEX];
+ chance +=
+ o_list[item].weight / (get_skill_scale(SKILL_STEALING, 19) + 1);
+ chance += get_skill_scale(SKILL_STEALING, 29) + 1;
+ chance -= (m_ptr->csleep) ? 10 : 0;
+ chance += m_ptr->level;
+
+ /* Failure check */
+ if (rand_int(chance) > 1 + get_skill_scale(SKILL_STEALING, 25))
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Wake up */
+ m_ptr->csleep = 0;
+
+ /* Speed up because monsters are ANGRY when you try to thief them */
+ m_ptr->mspeed += 5;
+
+ screen_load();
+
+ msg_print("Oops! The monster is now really *ANGRY*!");
+
+ return;
+ }
+
+ /* Reconnect the objects list */
+ if (num == 1) m_ptr->hold_o_idx = 0;
+ else
+ {
+ if (k > 0) o_list[monst_list[k - 1]].next_o_idx = monst_list[k + 1];
+ if (k + 1 >= num) o_list[monst_list[k - 1]].next_o_idx = 0;
+ if (k == 0) m_ptr->hold_o_idx = monst_list[k + 1];
+ }
+
+ /* Rogues gain some xp */
+ if (PRACE_FLAGS(PR1_EASE_STEAL))
+ {
+ s32b max_point;
+
+ /* Max XP gained from stealing */
+ max_point = (o_list[item].weight / 2) + (m_ptr->level * 10);
+
+ /* Randomise it a bit, with half a max guaranteed */
+ gain_exp((max_point / 2) + (randint(max_point) / 2));
+
+ /* Allow escape */
+ if (get_check("Phase door?")) teleport_player(10);
+ }
+
+ /* Get the item */
+ o_ptr = &forge;
+
+ /* Special handling for gold */
+ if (o_list[item].tval == TV_GOLD)
+ {
+ /* Collect the gold */
+ p_ptr->au += o_list[item].pval;
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else
+ {
+ object_copy(o_ptr, &o_list[item]);
+
+ inven_carry(o_ptr, FALSE);
+ }
+
+ /* Delete it */
+ o_list[item].k_idx = 0;
+ }
+
+ screen_load();
+
+ /* Take a turn */
+ energy_use = 100;
+}
+
+
+/*
+ * Give an item to a monster
+ */
+void do_cmd_give()
+{
+ int dir, x, y;
+
+ cave_type *c_ptr;
+
+ cptr q, s;
+
+ int item;
+
+
+ /* Get a "repeated" direction */
+ if (!get_rep_dir(&dir)) return;
+
+ /* Get requested location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get requested grid */
+ c_ptr = &cave[y][x];
+
+ /* No monster in the way */
+ if (c_ptr->m_idx == 0)
+ {
+ msg_print("There is no monster there.");
+ return;
+ }
+
+ /* Get an item */
+ q = "What item do you want to offer? ";
+ s = "You have nothing to offer.";
+ if (!get_item(&item, q, s, USE_INVEN)) return;
+
+ /* Process hooks if there are any */
+ if (!process_hooks(HOOK_GIVE, "(d,d)", c_ptr->m_idx, item))
+ {
+ msg_print("The monster does not want your item.");
+ }
+
+ /* Take a turn, even if the offer is declined */
+ energy_use = 100;
+}
+
+
+/*
+ * Chat with a monster
+ */
+void do_cmd_chat()
+{
+ int dir, x, y;
+
+ cave_type *c_ptr;
+
+
+ /* Get a "repeated" direction */
+ if (!get_rep_dir(&dir)) return;
+
+ /* Get requested location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get requested grid */
+ c_ptr = &cave[y][x];
+
+ /* No monster in the way */
+ if (c_ptr->m_idx == 0)
+ {
+ msg_print("There is no monster there.");
+ return;
+ }
+
+ /* Process hook if there are any */
+ if (!process_hooks(HOOK_CHAT, "(d)", c_ptr->m_idx))
+ {
+ msg_print("The monster does not want to chat.");
+ }
+
+ /* No energy spent */
+}
diff --git a/src/cmd3.c b/src/cmd3.c
new file mode 100644
index 00000000..02dbc1c4
--- /dev/null
+++ b/src/cmd3.c
@@ -0,0 +1,2331 @@
+/* File: cmd3.c */
+
+/* Purpose: Inventory commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Display p_ptr->inventory
+ */
+void do_cmd_inven(void)
+{
+ char out_val[160];
+
+
+ /* Note that we are in "p_ptr->inventory" mode */
+ command_wrk = FALSE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Hack -- show empty slots */
+ item_tester_full = TRUE;
+
+ /* Display the p_ptr->inventory */
+ show_inven();
+
+ /* Hack -- hide empty slots */
+ item_tester_full = FALSE;
+
+
+ {
+ s32b total_weight = calc_total_weight();
+
+ strnfmt(out_val, 160,
+ "Inventory: carrying %ld.%ld pounds (%ld%% of capacity). Command: ",
+ total_weight / 10, total_weight % 10,
+ (total_weight * 100) / ((weight_limit()) / 2));
+ }
+
+ /* Get a command */
+ prt(out_val, 0, 0);
+
+ /* Get a new command */
+ command_new = inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+
+ /* Process "Escape" */
+ if (command_new == ESCAPE)
+ {
+ /* Reset stuff */
+ command_new = 0;
+ }
+
+ /* Process normal keys */
+ else
+ {
+ /* Mega-Hack -- Don't disable keymaps for this key */
+ request_command_inven_mode = TRUE;
+ }
+}
+
+
+/*
+ * Display equipment
+ */
+void do_cmd_equip(void)
+{
+ char out_val[160];
+
+
+ /* Note that we are in "equipment" mode */
+ command_wrk = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Hack -- show empty slots */
+ item_tester_full = TRUE;
+
+ /* Display the equipment */
+ show_equip();
+
+ /* Hack -- undo the hack above */
+ item_tester_full = FALSE;
+
+ /* Build a prompt */
+ {
+ s32b total_weight = calc_total_weight();
+
+ /* Build a prompt */
+ strnfmt(out_val, 160,
+ "Equipment: carrying %ld.%ld pounds (%ld%% of capacity). Command: ",
+ total_weight / 10, total_weight % 10,
+ (total_weight * 100) / ((weight_limit()) / 2));
+ }
+
+ /* Get a command */
+ prt(out_val, 0, 0);
+
+ /* Get a new command */
+ command_new = inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+
+ /* Process "Escape" */
+ if (command_new == ESCAPE)
+ {
+ /* Reset stuff */
+ command_new = 0;
+ }
+
+ /* Process normal keys */
+ else
+ {
+ /* Mega-Hack -- Don't disable keymaps for this key */
+ request_command_inven_mode = TRUE;
+ }
+}
+
+
+/*
+ * The "wearable" tester
+ */
+static bool_ item_tester_hook_wear(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ int slot = wield_slot(o_ptr);
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Only one ultimate at a time */
+ if (f4 & TR4_ULTIMATE)
+ {
+ int i;
+
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ object_type *q_ptr = &p_ptr->inventory[i];
+
+ /* Extract the flags */
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (!q_ptr->k_idx) continue;
+
+ if (f4 & TR4_ULTIMATE) return (FALSE);
+ }
+ }
+
+ if ((slot < INVEN_WIELD) || ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_WIELD) && (p_ptr->melee_style != SKILL_MASTERY)))
+ return (FALSE);
+
+ /* Check for a usable slot */
+ if (slot >= INVEN_WIELD) return (TRUE);
+
+ /* Assume not wearable */
+ return (FALSE);
+}
+
+
+bool_ is_slot_ok(int slot)
+{
+ if ((slot >= INVEN_WIELD) && (slot < INVEN_TOTAL))
+ {
+ return (TRUE);
+ }
+ else
+ {
+ return (FALSE);
+ }
+}
+
+
+/*
+ * Wield or wear a single item from the pack or floor
+ */
+void do_cmd_wield(void)
+{
+ int item, slot, num = 1;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+ object_type *o_ptr, *i_ptr;
+
+ cptr act;
+
+ char o_name[80];
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Restrict the choices */
+ item_tester_hook = item_tester_hook_wear;
+
+ /* Get an item */
+ q = "Wear/Wield which item? ";
+ s = "You have nothing you can wear or wield.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Check the slot */
+ slot = wield_slot(o_ptr);
+
+ /* Prevent wielding into a cursed slot */
+ if (cursed_p(&p_ptr->inventory[slot]))
+ {
+ /* Describe it */
+ object_desc(o_name, &p_ptr->inventory[slot], FALSE, 0);
+
+ /* Message */
+ msg_format("The %s you are %s appears to be cursed.",
+ o_name, describe_use(slot));
+
+ /* Cancel the command */
+ return;
+ }
+
+ if ((cursed_p(o_ptr)) && (wear_confirm)
+ && (object_known_p(o_ptr) || (o_ptr->ident & (IDENT_SENSE))))
+ {
+ char dummy[512];
+
+ /* Describe it */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ strnfmt(dummy, 512, "Really use the %s {cursed}? ", o_name);
+ if (!(get_check(dummy)))
+ return;
+ }
+
+ /* Can we wield */
+ if (process_hooks(HOOK_WIELD, "(d)", item)) return;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Two handed weapons can't be wielded with a shield */
+ if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) &&
+ (f4 & TR4_MUST2H) &&
+ (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0))
+ {
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("You cannot wield your %s with a shield.", o_name);
+ return;
+ }
+
+ if (is_slot_ok(slot - INVEN_ARM + INVEN_WIELD))
+ {
+ i_ptr = &p_ptr->inventory[slot - INVEN_ARM + INVEN_WIELD];
+
+ /* Extract the flags */
+ object_flags(i_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Prevent shield from being put on if wielding 2H */
+ if ((f4 & TR4_MUST2H) && (i_ptr->k_idx) &&
+ (p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM))
+ {
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("You cannot wield your %s with a two-handed weapon.", o_name);
+ return;
+ }
+
+ if ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM) &&
+ (f4 & TR4_COULD2H))
+ {
+ if (!get_check("Are you sure you want to restrict your fighting? "))
+ {
+ return;
+ }
+ }
+ }
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) &&
+ (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0) &&
+ (f4 & TR4_COULD2H))
+ {
+ if (!get_check("Are you sure you want to use this weapon with a shield?"))
+ {
+ return;
+ }
+ }
+
+ /* Can we take off existing item */
+ if (slot != INVEN_AMMO)
+ {
+ if (p_ptr->inventory[slot].k_idx)
+ if (process_hooks(HOOK_TAKEOFF, "(d)", slot)) return;
+ }
+ else
+ {
+ if (p_ptr->inventory[slot].k_idx)
+ if (!object_similar(&p_ptr->inventory[slot], o_ptr))
+ if (process_hooks(HOOK_TAKEOFF, "(d)", slot)) return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain local object */
+ object_copy(q_ptr, o_ptr);
+
+ if (slot == INVEN_AMMO) num = o_ptr->number;
+
+ /* Modify quantity */
+ q_ptr->number = num;
+
+ /* Decrease the item */
+ inc_stack_size_ex(item, -num, OPTIMIZE, NO_DESCRIBE);
+
+ /* Access the wield slot */
+ o_ptr = &p_ptr->inventory[slot];
+
+ /* Take off existing item */
+ if (slot != INVEN_AMMO)
+ {
+ if (o_ptr->k_idx)
+ {
+ /* Take off existing item */
+ (void)inven_takeoff(slot, 255, FALSE);
+ }
+ }
+ else
+ {
+ if (o_ptr->k_idx)
+ {
+ if (!object_similar(o_ptr, q_ptr))
+ {
+ /* Take off existing item */
+ (void)inven_takeoff(slot, 255, FALSE);
+ }
+ else
+ {
+ q_ptr->number += o_ptr->number;
+ }
+ }
+ }
+
+
+ /* Wear the new stuff */
+ object_copy(o_ptr, q_ptr);
+
+ /* Increment the equip counter by hand */
+ equip_cnt++;
+
+ /* Where is the item now */
+ if (slot == INVEN_WIELD)
+ {
+ act = "You are wielding";
+ }
+ else if (( slot == INVEN_BOW ) && (o_ptr->tval == TV_INSTRUMENT))
+ {
+ act = "You are holding";
+ }
+ else if (slot == INVEN_BOW)
+ {
+ act = "You are shooting with";
+ }
+ else if (slot == INVEN_LITE)
+ {
+ act = "Your light source is";
+ }
+ else if (slot == INVEN_AMMO)
+ {
+ act = "In your quiver you have";
+ }
+ else if (slot == INVEN_TOOL)
+ {
+ act = "You are using";
+ }
+ else
+ {
+ act = "You are wearing";
+ }
+
+ /* Describe the result */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("%s %s (%c).", act, o_name, index_to_label(slot));
+
+ /* Cursed! */
+ if (cursed_p(o_ptr))
+ {
+ /* Warn the player */
+ msg_print("Oops! It feels deathly cold!");
+
+ /* Note the curse */
+ o_ptr->ident |= (IDENT_SENSE);
+ o_ptr->sense = SENSE_CURSED;
+ }
+
+ /* Take care of item sets */
+ if (o_ptr->name1)
+ {
+ wield_set(o_ptr->name1, a_info[o_ptr->name1].set, FALSE);
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Recalculate hitpoint */
+ p_ptr->update |= (PU_HP);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA | PU_SPELLS);
+
+ /* Redraw monster hitpoint */
+ p_ptr->redraw |= (PR_MH);
+
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+}
+
+
+
+/*
+ * Take off an item
+ */
+void do_cmd_takeoff(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Take off which item? ";
+ s = "You are not wearing anything to take off.";
+ if (!get_item(&item, q, s, (USE_EQUIP))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Can we take it off */
+ if (process_hooks(HOOK_TAKEOFF, "(d)", item)) return;
+
+ /* Item is cursed */
+ if (cursed_p(o_ptr) && (!wizard))
+ {
+ /* Oops */
+ msg_print("Hmmm, it seems to be cursed.");
+
+ /* Nope */
+ return;
+ }
+
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Take off the item */
+ (void)inven_takeoff(item, 255, FALSE);
+
+ /* Recalculate hitpoint */
+ p_ptr->update |= (PU_HP);
+
+ p_ptr->redraw |= (PR_MH);
+}
+
+
+/*
+ * Drop an item
+ */
+void do_cmd_drop(void)
+{
+ int item, amt = 1;
+
+ object_type *o_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Drop which item? ";
+ s = "You have nothing to drop.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Can we drop */
+ if (process_hooks(HOOK_DROP, "(d)", item)) return;
+
+ /* Hack -- Cannot remove cursed items */
+ if (cursed_p(o_ptr))
+ {
+ if (item >= INVEN_WIELD)
+ {
+ /* Oops */
+ msg_print("Hmmm, it seems to be cursed.");
+
+ /* Nope */
+ return;
+ }
+ else
+ {
+ if (f4 & TR4_CURSE_NO_DROP)
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to drop it.");
+
+ /* Nope */
+ return;
+ }
+ }
+ }
+
+
+ /* See how many items */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Drop (some of) the item */
+ inven_drop(item, amt, p_ptr->py, p_ptr->px, FALSE);
+}
+
+
+/*
+ * Destroy an item
+ */
+void do_cmd_destroy(void)
+{
+ int item, amt = 1;
+
+ int old_number;
+
+ bool_ force = FALSE;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[160];
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Hack -- force destruction */
+ if (command_arg > 0) force = TRUE;
+
+
+ /* Get an item */
+ q = "Destroy which item? ";
+ s = "You have nothing to destroy.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_AUTO))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+
+ /* See how many items */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+
+ /* Describe the object */
+ old_number = o_ptr->number;
+ o_ptr->number = amt;
+ object_desc(o_name, o_ptr, TRUE, 3);
+ o_ptr->number = old_number;
+
+ /* Verify unless quantity given */
+ if (!force)
+ {
+ if (!((auto_destroy) && (object_value(o_ptr) < 1)))
+ {
+ /* Make a verification */
+ strnfmt(out_val, 160, "Really destroy %s? ", o_name);
+ if (!get_check(out_val)) return;
+ }
+ }
+
+ /* Take no time, just like the automatizer */
+ energy_use = 0;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f4 & TR4_CURSE_NO_DROP) && cursed_p(o_ptr))
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to destroy it.");
+
+ /* Nope */
+ return;
+ }
+
+
+ /* Artifacts cannot be destroyed */
+ if (artifact_p(o_ptr) || o_ptr->art_name)
+ {
+ byte feel = SENSE_SPECIAL;
+
+ energy_use = 0;
+
+ /* Message */
+ msg_format("You cannot destroy %s.", o_name);
+
+ /* Hack -- Handle icky artifacts */
+ if (cursed_p(o_ptr)) feel = SENSE_TERRIBLE;
+
+ /* Hack -- inscribe the artifact */
+ o_ptr->sense = feel;
+
+ /* We have "felt" it (again) */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ return;
+ }
+
+ /* Message */
+ msg_format("You destroy %s.", o_name);
+ sound(SOUND_DESTITEM);
+
+ /* Create an automatizer rule */
+ if (automatizer_create)
+ {
+ automatizer_add_rule(o_ptr, TRUE);
+ }
+
+ /*
+ * Hack -- If rods or wand are destroyed, the total maximum timeout or
+ * charges of the stack needs to be reduced, unless all the items are
+ * being destroyed. -LM-
+ */
+ if ((o_ptr->tval == TV_WAND) && (amt < o_ptr->number))
+ {
+ o_ptr->pval -= o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Eru wont be happy */
+ if (f3 & TR3_BLESSED)
+ inc_piety(GOD_ERU, -10 * k_info[o_ptr->k_idx].level);
+
+ /* Eliminate the item */
+ inc_stack_size(item, -amt);
+}
+
+
+/*
+ * Observe an item which has been *identify*-ed
+ */
+void do_cmd_observe(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Examine which item? ";
+ s = "You have nothing to examine.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe */
+ cmsg_format(TERM_L_BLUE, "%s", o_name);
+
+ /* Describe it fully */
+ if (!object_out_desc(o_ptr, NULL, FALSE, TRUE)) msg_print("You see nothing special.");
+}
+
+
+
+/*
+ * Remove the inscription from an object
+ * XXX Mention item (when done)?
+ */
+void do_cmd_uninscribe(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Un-inscribe which item? ";
+ s = "You have nothing to un-inscribe.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Nothing to remove */
+ if (!o_ptr->note)
+ {
+ msg_print("That item had no inscription to remove.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Inscription removed.");
+
+ /* Remove the incription */
+ o_ptr->note = 0;
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+}
+
+
+/*
+ * Inscribe an object with a comment
+ */
+void do_cmd_inscribe(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[80];
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Inscribe which item? ";
+ s = "You have nothing to inscribe.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Describe the activity */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("Inscribing %s.", o_name);
+ msg_print(NULL);
+
+ /* Start with nothing */
+ strcpy(out_val, "");
+
+ /* Use old inscription */
+ if (o_ptr->note)
+ {
+ /* Start with the old inscription */
+ strcpy(out_val, quark_str(o_ptr->note));
+ }
+
+ /* Get a new inscription (possibly empty) */
+ if (get_string("Inscription: ", out_val, 80))
+ {
+ /* Save the inscription */
+ o_ptr->note = quark_add(out_val);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+}
+
+
+
+/*
+ * An "item_tester_hook" for refilling lanterns
+ */
+static bool_ item_tester_refill_lantern(object_type *o_ptr)
+{
+ /* Flasks of oil are okay */
+ if (o_ptr->tval == TV_FLASK) return (TRUE);
+
+ /* Lanterns are okay */
+ if ((o_ptr->tval == TV_LITE) &&
+ (o_ptr->sval == SV_LITE_LANTERN)) return (TRUE);
+
+ /* Assume not okay */
+ return (FALSE);
+}
+
+
+/*
+ * Refill the players lamp (from the pack or floor)
+ */
+static void do_cmd_refill_lamp(void)
+{
+ int item;
+
+ object_type *o_ptr;
+ object_type *j_ptr;
+
+ cptr q, s;
+
+
+ /* Restrict the choices */
+ item_tester_hook = item_tester_refill_lantern;
+
+ /* Get an item */
+ q = "Refill with which flask? ";
+ s = "You have no flasks of oil.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Access the lantern */
+ j_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Refuel */
+ if (o_ptr->tval == TV_FLASK)
+ j_ptr->timeout += o_ptr->pval;
+ else
+ j_ptr->timeout += o_ptr->timeout;
+
+ /* Message */
+ msg_print("You fuel your lamp.");
+
+ /* Comment */
+ if (j_ptr->timeout >= FUEL_LAMP)
+ {
+ j_ptr->timeout = FUEL_LAMP;
+ msg_print("Your lamp is full.");
+ }
+
+ /* Decrease the item stack */
+ inc_stack_size(item, -1);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+}
+
+
+/*
+ * An "item_tester_hook" for refilling torches
+ */
+static bool_ item_tester_refill_torch(object_type *o_ptr)
+{
+ /* Torches are okay */
+ if ((o_ptr->tval == TV_LITE) &&
+ (o_ptr->sval == SV_LITE_TORCH)) return (TRUE);
+
+ /* Assume not okay */
+ return (FALSE);
+}
+
+
+/*
+ * Refuel the players torch (from the pack or floor)
+ */
+static void do_cmd_refill_torch(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ object_type *j_ptr;
+
+ cptr q, s;
+
+
+ /* Restrict the choices */
+ item_tester_hook = item_tester_refill_torch;
+
+ /* Get an item */
+ q = "Refuel with which torch? ";
+ s = "You have no extra torches.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Access the primary torch */
+ j_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Refuel */
+ j_ptr->timeout += o_ptr->timeout + 5;
+
+ /* Message */
+ msg_print("You combine the torches.");
+
+ /* Over-fuel message */
+ if (j_ptr->timeout >= FUEL_TORCH)
+ {
+ j_ptr->timeout = FUEL_TORCH;
+ msg_print("Your torch is fully fueled.");
+ }
+
+ /* Refuel message */
+ else
+ {
+ msg_print("Your torch glows more brightly.");
+ }
+
+ /* Decrease the item stack */
+ inc_stack_size(item, -1);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+}
+
+
+/*
+ * Refill the players lamp, or restock his torches
+ */
+void do_cmd_refill(void)
+{
+ object_type *o_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Get the light */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* It is nothing */
+ if (o_ptr->tval != TV_LITE)
+ {
+ msg_print("You are not wielding a light.");
+ return;
+ }
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_FUEL_LITE)
+ {
+ /* It's a torch */
+ if (o_ptr->sval == SV_LITE_TORCH ||
+ o_ptr->sval == SV_LITE_TORCH_EVER)
+ {
+ do_cmd_refill_torch();
+ }
+
+ /* It's a lamp */
+ else if (o_ptr->sval == SV_LITE_LANTERN ||
+ o_ptr->sval == SV_LITE_DWARVEN ||
+ o_ptr->sval == SV_LITE_FEANORIAN)
+ {
+ do_cmd_refill_lamp();
+ }
+ }
+
+ /* No torch to refill */
+ else
+ {
+ msg_print("Your light cannot be refilled.");
+ }
+}
+
+
+/*
+ * Target command
+ */
+void do_cmd_target(void)
+{
+ /* Target set */
+ if (target_set(TARGET_KILL))
+ {
+ msg_print("Target Selected.");
+ }
+
+ /* Target aborted */
+ else
+ {
+ msg_print("Target Aborted.");
+ }
+}
+
+
+
+/*
+ * Look command
+ */
+void do_cmd_look(void)
+{
+ /* Look around */
+ if (target_set(TARGET_LOOK))
+ {
+ msg_print("Target Selected.");
+ }
+}
+
+
+
+/*
+ * Allow the player to examine other sectors on the map
+ */
+void do_cmd_locate(void)
+{
+ int dir, y1, x1, y2, x2;
+ int panel_hgt, panel_wid;
+ char tmp_val[80];
+ char out_val[160];
+
+
+ /* Retrieve size of the Angband window */
+ Term_get_size(&panel_wid, &panel_hgt);
+
+ /* Calcurate size of the dungeon map area */
+ panel_hgt = (panel_hgt - (ROW_MAP + 1)) / 2;
+ panel_wid = (panel_wid - (COL_MAP + 1)) / 2;
+
+ /* Start at current panel */
+ y2 = y1 = panel_row_min;
+ x2 = x1 = panel_col_min;
+
+ /* Show panels until done */
+ while (1)
+ {
+ /* Describe the location */
+ if ((y2 == y1) && (x2 == x1))
+ {
+ tmp_val[0] = '\0';
+ }
+ else
+ {
+ strnfmt(tmp_val, 80, "%s%s of",
+ ((y2 < y1) ? " North" : (y2 > y1) ? " South" : ""),
+ ((x2 < x1) ? " West" : (x2 > x1) ? " East" : ""));
+ }
+
+ /* Prepare to ask which way to look */
+ if ((panel_hgt == PANEL_HGT) && (panel_wid == PANEL_WID))
+ {
+ /* Avoid surprising the standard screen users */
+ strnfmt(out_val, 160,
+ "Map sector [%d,%d], which is%s your sector. Direction?",
+ y2 / panel_hgt, x2 / panel_wid, tmp_val);
+ }
+
+ /* Big screen */
+ else
+ {
+ /* Panels are measured by current map area size */
+ strnfmt(out_val, 160,
+ "Map sector [%d(%02d),%d(%02d)], which is%s your sector. Direction?",
+ y2 / panel_hgt, y2 % panel_hgt,
+ x2 / panel_wid, x2 % panel_wid, tmp_val);
+ }
+
+ /* Assume no direction */
+ dir = 0;
+
+ /* Get a direction */
+ while (!dir)
+ {
+ char ch;
+
+ /* Get a command (or cancel) */
+ if (!get_com(out_val, &ch)) break;
+
+ /* Extract the action (if any) */
+ dir = get_keymap_dir(ch);
+
+ /* Error */
+ if (!dir) bell();
+ }
+
+ /* No direction */
+ if (!dir) break;
+
+ /* Apply the motion */
+ if (change_panel(ddy[dir], ddx[dir]))
+ {
+ y2 = panel_row_min;
+ x2 = panel_col_min;
+ }
+ }
+
+ /* Recenter the map around the player */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+}
+
+
+
+
+
+
+/*
+ * The table of "symbol info" -- each entry is a string of the form
+ * "X:desc" where "X" is the trigger, and "desc" is the "info".
+ */
+static cptr ident_info[] =
+{
+ " :A dark grid",
+ "!:A potion (or oil)",
+ "\":An amulet (or necklace)",
+ "#:A wall (or secret door)",
+ "$:Treasure (gold or gems)",
+ "%:A vein (magma or quartz)",
+ /* "&:unused", */
+ "':An open door",
+ "(:Soft armor",
+ "):A shield",
+ "*:A vein with treasure",
+ "+:A closed door",
+ ",:Food (or mushroom patch)",
+ "-:A wand (or rod)",
+ ".:Floor",
+ "/:A polearm (Axe/Pike/etc)",
+ "0:An altar",
+ "1:Entrance to General Store",
+ "2:Entrance to Armory",
+ "3:Entrance to Weaponsmith",
+ "4:Entrance to Temple",
+ "5:Entrance to Alchemy shop",
+ "6:Entrance to Magic store",
+ "7:Entrance to Black Market",
+ "8:Entrance to your home",
+ "9:Entrance to Bookstore",
+ "::Rubble",
+ ";:A glyph of warding / explosive rune",
+ "<:An up staircase",
+ "=:A ring",
+ ">:A down staircase",
+ "?:A scroll",
+ "@:You",
+ "A:Angel",
+ "B:Bird",
+ "C:Canine",
+ "D:Ancient Dragon/Wyrm",
+ "E:Elemental",
+ "F:Dragon Fly",
+ "G:Ghost",
+ "H:Hybrid",
+ "I:Insect",
+ "J:Snake",
+ "K:Killer Beetle",
+ "L:Lich",
+ "M:Multi-Headed Reptile",
+ /* "N:unused", */
+ "O:Ogre",
+ "P:Giant Humanoid",
+ "Q:Quylthulg (Pulsing Flesh Mound)",
+ "R:Reptile/Amphibian",
+ "S:Spider/Scorpion/Tick",
+ "T:Troll",
+ "U:Major Demon",
+ "V:Vampire",
+ "W:Wight/Wraith/etc",
+ "X:Xorn/Xaren/etc",
+ "Y:Yeti",
+ "Z:Zephyr Hound",
+ "[:Hard armor",
+ "\\:A hafted weapon (mace/whip/etc)",
+ "]:Misc. armor",
+ "^:A trap",
+ "_:A staff",
+ /* "`:unused", */
+ "a:Ant",
+ "b:Bat",
+ "c:Centipede",
+ "d:Dragon",
+ "e:Floating Eye",
+ "f:Feline",
+ "g:Golem",
+ "h:Hobbit/Elf/Dwarf",
+ "i:Icky Thing",
+ "j:Jelly",
+ "k:Kobold",
+ "l:Louse",
+ "m:Mold",
+ "n:Naga",
+ "o:Orc",
+ "p:Person/Human",
+ "q:Quadruped",
+ "r:Rodent",
+ "s:Skeleton",
+ "t:Townsperson",
+ "u:Minor Demon",
+ "v:Vortex",
+ "w:Worm/Worm-Mass",
+ /* "x:unused", */
+ "y:Yeek",
+ "z:Zombie/Mummy",
+ "{:A missile (arrow/bolt/shot)",
+ "|:An edged weapon (sword/dagger/etc)",
+ "}:A launcher (bow/crossbow/sling)",
+ "~:A tool (or miscellaneous item)",
+ NULL
+};
+
+
+
+/*
+ * Sorting hook -- Comp function -- see below
+ *
+ * We use "u" to point to array of monster indexes,
+ * and "v" to select the type of sorting to perform on "u".
+ */
+static bool_ ang_sort_comp_hook(vptr u, vptr v, int a, int b)
+{
+ u16b *who = (u16b*)(u);
+
+ u16b *why = (u16b*)(v);
+
+ int w1 = who[a];
+
+ int w2 = who[b];
+
+ int z1, z2;
+
+
+ /* Sort by player kills */
+ if (*why >= 4)
+ {
+ /* Extract player kills */
+ z1 = r_info[w1].r_pkills;
+ z2 = r_info[w2].r_pkills;
+
+ /* Compare player kills */
+ if (z1 < z2) return (TRUE);
+ if (z1 > z2) return (FALSE);
+ }
+
+
+ /* Sort by total kills */
+ if (*why >= 3)
+ {
+ /* Extract total kills */
+ z1 = r_info[w1].r_tkills;
+ z2 = r_info[w2].r_tkills;
+
+ /* Compare total kills */
+ if (z1 < z2) return (TRUE);
+ if (z1 > z2) return (FALSE);
+ }
+
+
+ /* Sort by monster level */
+ if (*why >= 2)
+ {
+ /* Extract levels */
+ z1 = r_info[w1].level;
+ z2 = r_info[w2].level;
+
+ /* Compare levels */
+ if (z1 < z2) return (TRUE);
+ if (z1 > z2) return (FALSE);
+ }
+
+
+ /* Sort by monster experience */
+ if (*why >= 1)
+ {
+ /* Extract experience */
+ z1 = r_info[w1].mexp;
+ z2 = r_info[w2].mexp;
+
+ /* Compare experience */
+ if (z1 < z2) return (TRUE);
+ if (z1 > z2) return (FALSE);
+ }
+
+
+ /* Compare indexes */
+ return (w1 <= w2);
+}
+
+
+/*
+ * Sorting hook -- Swap function -- see below
+ *
+ * We use "u" to point to array of monster indexes,
+ * and "v" to select the type of sorting to perform.
+ */
+static void ang_sort_swap_hook(vptr u, vptr v, int a, int b)
+{
+ u16b *who = (u16b*)(u);
+
+ u16b holder;
+
+
+ /* XXX XXX */
+ v = v ? v : 0;
+
+ /* Swap */
+ holder = who[a];
+ who[a] = who[b];
+ who[b] = holder;
+}
+
+
+
+/*
+ * Hack -- Display the "name" and "attr/chars" of a monster race
+ */
+static void roff_top(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ byte a1, a2;
+
+ char c1, c2;
+
+
+ /* Access the chars */
+ c1 = r_ptr->d_char;
+ c2 = r_ptr->x_char;
+
+ /* Access the attrs */
+ a1 = r_ptr->d_attr;
+ a2 = r_ptr->x_attr;
+
+
+ /* Clear the top line */
+ Term_erase(0, 0, 255);
+
+ /* Reset the cursor */
+ Term_gotoxy(0, 0);
+
+ /* A title (use "The" for non-uniques) */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ Term_addstr( -1, TERM_WHITE, "The ");
+ }
+
+ /* Dump the name */
+ Term_addstr( -1, TERM_WHITE, (r_name + r_ptr->name));
+
+ /* Append the "standard" attr/char info */
+ Term_addstr( -1, TERM_WHITE, " ('");
+ Term_addch(a1, c1);
+ if (use_bigtile && (a1 & 0x80)) Term_addch(255, 255);
+ Term_addstr( -1, TERM_WHITE, "')");
+
+ /* Append the "optional" attr/char info */
+ Term_addstr( -1, TERM_WHITE, "/('");
+ Term_addch(a2, c2);
+ if (use_bigtile && (a2 & 0x80)) Term_addch(255, 255);
+ Term_addstr( -1, TERM_WHITE, "'):");
+}
+
+
+/*
+ * Identify a character, allow recall of monsters
+ *
+ * Several "special" responses recall "multiple" monsters:
+ * ^A (all monsters)
+ * ^U (all unique monsters)
+ * ^N (all non-unique monsters)
+ * ^M (case insensitive name search)
+ *
+ * The responses may be sorted in several ways, see below.
+ *
+ * Note that the player ghosts are ignored. XXX XXX XXX
+ */
+void do_cmd_query_symbol(void)
+{
+ int i, n, r_idx;
+
+ char sym, query;
+
+ char buf[128];
+
+
+ bool_ all = FALSE;
+
+ bool_ uniq = FALSE;
+
+ bool_ norm = FALSE;
+
+
+ bool_ name = FALSE;
+
+ char temp[80] = "";
+
+
+ bool_ recall = FALSE;
+
+
+ u16b why = 0;
+
+ u16b *who;
+
+
+ /* Get a character, or abort */
+ if (!get_com("Enter character to be identified, "
+ "or (Ctrl-A, Ctrl-U, Ctrl-N, Ctrl-M):", &sym)) return;
+
+ /* Find that character info, and describe it */
+ for (i = 0; ident_info[i]; ++i)
+ {
+ if (sym == ident_info[i][0]) break;
+ }
+
+ /* Describe */
+ if (sym == KTRL('A'))
+ {
+ all = TRUE;
+ strcpy(buf, "Full monster list.");
+ }
+ else if (sym == KTRL('U'))
+ {
+ all = uniq = TRUE;
+ strcpy(buf, "Unique monster list.");
+ }
+ else if (sym == KTRL('N'))
+ {
+ all = norm = TRUE;
+ strcpy(buf, "Non-unique monster list.");
+ }
+ else if (sym == KTRL('M'))
+ {
+ all = name = TRUE;
+ if (!get_string("Name:", temp, 70)) return;
+ strnfmt(buf, 128, "Monsters with a name \"%s\"", temp);
+ strlower(temp);
+ }
+ else if (ident_info[i])
+ {
+ strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2);
+ }
+ else
+ {
+ strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol");
+ }
+
+ /* Display the result */
+ prt(buf, 0, 0);
+
+ /* Allocate the "who" array */
+ C_MAKE(who, max_r_idx, u16b);
+
+ /* Collect matching monsters */
+ for (n = 0, i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Nothing to recall */
+ if (!cheat_know && !r_ptr->r_sights) continue;
+
+ /* Require non-unique monsters if needed */
+ if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue;
+
+ /* Require unique monsters if needed */
+ if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue;
+
+ /* Require monsters with the name requested if needed */
+ if (name)
+ {
+ char mon_name[80];
+
+ strcpy(mon_name, r_name + r_ptr->name);
+ strlower(mon_name);
+
+ if (!strstr(mon_name, temp)) continue;
+ }
+
+ /* Collect "appropriate" monsters */
+ if (all || (r_ptr->d_char == sym)) who[n++] = i;
+ }
+
+ /* Nothing to recall */
+ if (!n)
+ {
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+
+ return;
+ }
+
+
+ /* Prompt XXX XXX XXX */
+ put_str("Recall details? (k/p/y/n): ", 0, 40);
+
+ /* Query */
+ query = inkey();
+
+ /* Restore */
+ prt(buf, 0, 0);
+
+
+ /* Sort by kills (and level) */
+ if (query == 'k')
+ {
+ why = 4;
+ query = 'y';
+ }
+
+ /* Sort by level */
+ if (query == 'p')
+ {
+ why = 2;
+ query = 'y';
+ }
+
+ /* Catch "escape" */
+ if (query != 'y')
+ {
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+
+ return;
+ }
+
+
+ /* Sort if needed */
+ if (why)
+ {
+ /* Select the sort method */
+ ang_sort_comp = ang_sort_comp_hook;
+ ang_sort_swap = ang_sort_swap_hook;
+
+ /* Sort the array */
+ ang_sort(who, &why, n);
+ }
+
+
+ /* Start at the end */
+ i = n - 1;
+
+ /* Scan the monster memory */
+ while (1)
+ {
+ /* Extract a race */
+ r_idx = who[i];
+
+ /* Hack -- Auto-recall */
+ monster_race_track(r_idx, 0);
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ /* Hack -- Begin the prompt */
+ roff_top(r_idx);
+
+ /* Hack -- Complete the prompt */
+ Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]");
+
+ /* Interact */
+ while (1)
+ {
+ /* Recall */
+ if (recall)
+ {
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Recall on screen */
+ screen_roff(who[i], 0, 0);
+
+ /* Hack -- Complete the prompt (again) */
+ Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]");
+ }
+
+ /* Command */
+ query = inkey();
+
+ /* Unrecall */
+ if (recall)
+ {
+ /* Restore */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Normal commands */
+ if (query != 'r') break;
+
+ /* Toggle recall */
+ recall = !recall;
+ }
+
+ /* Stop scanning */
+ if (query == ESCAPE) break;
+
+ /* Move to "prev" monster */
+ if (query == '-')
+ {
+ if (++i == n)
+ {
+ i = 0;
+ if (!expand_list) break;
+ }
+ }
+
+ /* Move to "next" monster */
+ else
+ {
+ if (i-- == 0)
+ {
+ i = n - 1;
+ if (!expand_list) break;
+ }
+ }
+ }
+
+ /* Re-display the identity */
+ prt(buf, 0, 0);
+
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+}
+
+
+/*
+ * research_mon
+ * -KMW-
+ */
+bool_ research_mon()
+{
+ int i, n, r_idx;
+
+ char sym, query;
+
+ char buf[128];
+
+
+ s16b oldkills;
+
+ byte oldwake;
+
+ bool_ oldcheat;
+
+
+ bool_ all = FALSE;
+
+ bool_ uniq = FALSE;
+
+ bool_ norm = FALSE;
+
+ bool_ notpicked;
+
+
+ bool_ recall = FALSE;
+
+ u16b why = 0;
+
+ monster_race *r2_ptr;
+
+ u16b *who;
+
+
+ /* Hack -- Remember "cheat_know" flag */
+ oldcheat = cheat_know;
+
+
+ /* Get a character, or abort */
+ if (!get_com("Enter character of monster: ", &sym)) return (TRUE);
+
+ /* Allocate the "who" array */
+ C_MAKE(who, max_r_idx, u16b);
+
+ /* Find that character info, and describe it */
+ for (i = 0; ident_info[i]; ++i)
+ {
+ if (sym == ident_info[i][0]) break;
+ }
+
+ if (ident_info[i])
+ {
+ strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2);
+ }
+ else
+ {
+ strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol");
+ }
+
+ /* Display the result */
+ prt(buf, 16, 10);
+
+
+ /* Collect matching monsters */
+ for (n = 0, i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Hack -- Force "cheat_know" */
+ cheat_know = TRUE;
+
+ /* Nothing to recall */
+ if (!cheat_know && !r_ptr->r_sights) continue;
+
+ /* Require non-unique monsters if needed */
+ if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue;
+
+ /* Require unique monsters if needed */
+ if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue;
+
+ /* Collect "appropriate" monsters */
+ if (all || (r_ptr->d_char == sym)) who[n++] = i;
+ }
+
+ /* Nothing to recall */
+ if (!n)
+ {
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+
+ /* Restore the "cheat_know" flag */
+ cheat_know = oldcheat;
+
+ return (TRUE);
+ }
+
+
+ /* Sort by level */
+ why = 2;
+ query = 'y';
+
+ /* Sort if needed */
+ if (why)
+ {
+ /* Select the sort method */
+ ang_sort_comp = ang_sort_comp_hook;
+ ang_sort_swap = ang_sort_swap_hook;
+
+ /* Sort the array */
+ ang_sort(who, &why, n);
+ }
+
+
+ /* Start at the end */
+ i = n - 1;
+
+ notpicked = TRUE;
+
+ /* Scan the monster memory */
+ while (notpicked)
+ {
+ /* Extract a race */
+ r_idx = who[i];
+
+ /* Hack -- Auto-recall */
+ monster_race_track(r_idx, 0);
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ /* Hack -- Begin the prompt */
+ roff_top(r_idx);
+
+ /* Hack -- Complete the prompt */
+ Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC, space to continue]");
+
+ /* Interact */
+ while (1)
+ {
+ /* Recall */
+ if (recall)
+ {
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Recall on screen */
+ r2_ptr = &r_info[r_idx];
+
+ oldkills = r2_ptr->r_tkills;
+ oldwake = r2_ptr->r_wake;
+ screen_roff(who[i], 0, 1);
+ r2_ptr->r_tkills = oldkills;
+ r2_ptr->r_wake = oldwake;
+ r2_ptr->r_sights = 1;
+ cheat_know = oldcheat;
+ notpicked = FALSE;
+ break;
+
+ }
+
+ /* Command */
+ query = inkey();
+
+ /* Unrecall */
+ if (recall)
+ {
+ /* Restore */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Normal commands */
+ if (query != 'r') break;
+
+ /* Toggle recall */
+ recall = !recall;
+ }
+
+ /* Stop scanning */
+ if (query == ESCAPE) break;
+
+ /* Move to "prev" monster */
+ if (query == '-')
+ {
+ if (++i == n)
+ {
+ i = 0;
+ if (!expand_list) break;
+ }
+ }
+
+ /* Move to "next" monster */
+ else
+ {
+ if (i-- == 0)
+ {
+ i = n - 1;
+ if (!expand_list) break;
+ }
+ }
+ }
+
+
+ /* Re-display the identity */
+ /* prt(buf, 5, 5);*/
+
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+
+ /* Restore the "cheat_know" flag */
+ cheat_know = oldcheat;
+
+ return (notpicked);
+}
+
+
+/*
+ * Try to "sense" the grid's mana
+ */
+bool_ do_cmd_sense_grid_mana()
+{
+ int chance, i;
+
+
+ /* Take (a lot of) time */
+ energy_use = 200;
+
+ /* Base chance of success */
+ chance = p_ptr->skill_dev;
+
+ /* Confusion hurts skill */
+ if (p_ptr->confused) chance = chance / 2;
+
+ /* Hight mana grids are harder */
+ chance = chance - (cave[p_ptr->py][p_ptr->px].mana / 10);
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
+ {
+ if (flush_failure) flush();
+ msg_print("You failed to sense the grid's mana.");
+ sound(SOUND_FAIL);
+ return FALSE;
+ }
+
+ /* Try to give an "average" value */
+ i = (101 - p_ptr->skill_dev) / 2;
+ i = (i < 1) ? 1 : (i > 50) ? 50 : i;
+
+ if (wizard)
+ {
+ msg_format("Grid's mana: %d.", cave[p_ptr->py][p_ptr->px].mana);
+ msg_format("Average grid's mana: %d.", (cave[p_ptr->py][p_ptr->px].mana / i) * i);
+ }
+ else
+ {
+ msg_format("Average Area's mana: %d", (cave[p_ptr->py][p_ptr->px].mana / i) * i);
+ }
+ return TRUE;
+}
+
+
+/*
+ * Calculate the weight of the portable holes
+ */
+s32b portable_hole_weight(void)
+{
+ s32b weight, i;
+
+ store_type *st_ptr = &town_info[TOWN_RANDOM].store[STORE_HOME];
+
+
+ /* Sum the objects in the appropriate home */
+ for (i = 0, weight = 0; i < st_ptr->stock_num; i++)
+ {
+ object_type *o_ptr = &st_ptr->stock[i];
+
+ weight += (o_ptr->weight * o_ptr->number);
+ }
+
+ /* Multiply the sum with 1.5 */
+ weight = (weight * 3) / 2 + 2;
+
+ return (weight);
+}
+
+
+/*
+ * Calculate and set the weight of the portable holes
+ */
+void set_portable_hole_weight(void)
+{
+ s32b weight, i, j;
+
+ /* Calculate the weight of items in home */
+ weight = portable_hole_weight();
+
+ /* Set the weight of portable holes in the shops, ... */
+ for (i = 1; i < max_towns; i++)
+ {
+ for (j = 0; j < max_st_idx; j++)
+ {
+ store_type *st_ptr = &town_info[i].store[j];
+ int k;
+
+ for (k = 0; k < st_ptr->stock_num; k++)
+ {
+ object_type *o_ptr = &st_ptr->stock[k];
+
+ if ((o_ptr->tval == TV_TOOL) &&
+ (o_ptr->sval == SV_PORTABLE_HOLE))
+ o_ptr->weight = weight;
+ }
+ }
+ }
+
+ /* ... in the object list, ... */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ if ((o_ptr->tval == TV_TOOL) &&
+ (o_ptr->sval == SV_PORTABLE_HOLE)) o_ptr->weight = weight;
+ }
+
+ /* ... and in the p_ptr->inventory to the appropriate value */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if ((o_ptr->tval == TV_TOOL) &&
+ (o_ptr->sval == SV_PORTABLE_HOLE)) o_ptr->weight = weight;
+ }
+}
+
+
+/*
+ * Use a portable hole
+ */
+void do_cmd_portable_hole(void)
+{
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ int feat, special, town_num;
+
+ /* Is it currently wielded? */
+ if (!p_ptr->inventory[INVEN_TOOL].k_idx ||
+ (p_ptr->inventory[INVEN_TOOL].tval != TV_TOOL) ||
+ (p_ptr->inventory[INVEN_TOOL].sval != SV_PORTABLE_HOLE))
+ {
+ /* No, it isn't */
+ msg_print("You have to wield a portable hole to use your abilities");
+ return;
+ }
+
+ /* Mega-hack: Saving the old values, and then... */
+ feat = c_ptr->feat;
+ special = c_ptr->special;
+ town_num = p_ptr->town_num;
+
+ /* ... change the current grid to the home in town #1 */
+ /* DG -- use the first random town, since random towns cannot have houses */
+ /*
+ * pelpel -- This doesn't affect LoS, so we can manipulate
+ * terrain feature without calling cave_set_feat()
+ */
+ c_ptr->feat = FEAT_SHOP;
+ c_ptr->special = STORE_HOME;
+ p_ptr->town_num = TOWN_RANDOM;
+
+ /* Now use the portable hole */
+ do_cmd_store();
+
+ /* Mega-hack part II: change the current grid to the original value */
+ c_ptr->feat = feat;
+ c_ptr->special = special;
+ p_ptr->town_num = town_num;
+
+ set_portable_hole_weight();
+
+ /* Recalculate bonuses */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ p_ptr->update |= (PU_BONUS);
+}
+
+
+/*
+ * Try to add a CLI action.
+ */
+void cli_add(cptr active, cptr trigger, cptr descr)
+{
+ s16b num;
+ cli_comm *cli_ptr, *old_ptr;
+
+ /* Too many macros. */
+ if (cli_total >= CLI_MAX) return;
+
+ /* First try to read active as a number. */
+ if (strtol(active, 0, 0))
+ {
+ num = strtol(active, 0, 0);
+ }
+ /* Then try to read it as a character. */
+ else if (strlen(active) == 1)
+ {
+ num = active[0];
+ }
+ /* Give up if it doesn't work. */
+ else
+ {
+ return;
+ }
+
+ /* Dump the macro. */
+ cli_ptr = cli_info + cli_total;
+ old_ptr = cli_info + cli_total - 1;
+
+ /*
+ * Trim 's from the ends of a token. This turns '@' into @ and
+ * ''' into '. This may be the intent of the code in tokenize(),
+ * but I've left it for lack of comments to back me up.
+ */
+ if (strchr(trigger, '\''))
+ {
+ char temp[80], *t;
+ cptr s;
+ for (s = trigger, t = temp; ; s++, t++)
+ {
+ /* tokenize() causes each ' to be followed by another character,
+ * and then another '. Trim the 's here. */
+ if (*s == '\'')
+ {
+ *t = *(++s);
+ s++;
+ }
+ else
+ {
+ *t = *s;
+ }
+ if (*t == '\0') break;
+ }
+ cli_ptr->comm = string_make(temp);
+ }
+ else
+ {
+ cli_ptr->comm = string_make(trigger);
+ }
+
+ /* First try copying everything across. */
+ cli_ptr->key = num;
+ cli_ptr->descrip = string_make(descr);
+
+ /* Take description for the previous record if appropriate. */
+ if ((cli_total > 0) && (old_ptr->key == cli_ptr->key) && (cli_ptr->descrip == 0))
+ {
+ cli_ptr->descrip = old_ptr->descrip;
+ }
+
+ /* Accept the macro. */
+ if (cli_ptr->key && cli_ptr->comm && cli_ptr->descrip) cli_total++;
+}
+
+
+
+/*
+ * Get a string using CLI completion.
+ */
+bool_ get_string_cli(cptr prompt, char *buf, int len)
+{
+ bool_ res;
+
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Display prompt */
+ prt(prompt, 0, 0);
+
+ /* Ask the user for a string */
+ askfor_aux_complete = TRUE;
+ res = askfor_aux(buf, len);
+ askfor_aux_complete = FALSE;
+
+ /* Clear prompt */
+ prt("", 0, 0);
+
+ /* Result */
+ return (res);
+}
+
+
+/*
+ * Do a command line command
+ *
+ * This is a wrapper around process command to provide a "reverse keymap"
+ * whereby a set of keypresses is mapped to one.
+ *
+ * This is useful because command_cmd is a s16b, and so allows each command a
+ * unique representation.
+ *
+ * See defines.h for a list of the codes used.
+ */
+void do_cmd_cli(void)
+{
+ char buff[80];
+
+ cli_comm *cli_ptr;
+
+ /* Clear the input buffer */
+ strcpy(buff, "");
+
+ /* Accept command */
+ if (!get_string_cli("Command: ", buff, 30)) return;
+
+
+ /* Analyse the input */
+ for (cli_ptr = cli_info; cli_ptr->comm; cli_ptr++)
+ {
+ if (!strcmp(buff, cli_ptr->comm))
+ {
+ /* Process the command without keymaps or macros. */
+ command_new = cli_ptr->key;
+ return;
+ }
+ }
+
+ msg_format("No such command: %s", buff);
+}
+
+
+/*
+ * Display on-line help for the CLI commands
+ */
+void do_cmd_cli_help()
+{
+ int i, j;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ for (i = 0, j = -1; i < cli_total; i++)
+ {
+ if (j < i - 1) fprintf(fff, "/");
+ fprintf(fff, "[[[[[G%s]", cli_info[i].comm);
+ if (cli_info[i].descrip != cli_info[i + 1].descrip)
+ {
+ fprintf(fff, " %s\n", cli_info[i].descrip);
+ j = i;
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Display the file contents */
+ show_file(file_name, "Command line help", 0, 0);
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Dump screen shot in HTML
+ */
+void do_cmd_html_dump()
+{
+ char tmp_val[81];
+ bool_ html = TRUE;
+ term_win *save;
+
+ /* Save the screen */
+ save = Term_save_to();
+
+ if (wizard && get_check("WIZARD MODE: Do an help file dump?"))
+ html = FALSE;
+
+ /* Ask for a file */
+ if (html)
+ {
+ strcpy(tmp_val, "dummy.htm");
+ if (!get_string("File(you can post it to http://angband.oook.cz/): ", tmp_val, 80))
+ {
+ /* Now restore the screen to initial state */
+ Term_load_from(save, TRUE);
+ Term_fresh();
+ return;
+ }
+ }
+ else
+ {
+ strcpy(tmp_val, "dummy.txt");
+ if (!get_string("File: ", tmp_val, 80))
+ {
+ /* Now restore the screen to initial state */
+ Term_load_from(save, TRUE);
+ Term_fresh();
+ return;
+ }
+ }
+
+ /* Now restore the screen to dump it */
+ Term_load_from(save, TRUE);
+
+ if (html)
+ html_screenshot(tmp_val);
+ else
+ help_file_screenshot(tmp_val);
+
+ Term_erase(0, 0, 255);
+ msg_print("Dump saved.");
+ Term_fresh();
+ fix_message();
+}
diff --git a/src/cmd4.c b/src/cmd4.c
new file mode 100644
index 00000000..c4440428
--- /dev/null
+++ b/src/cmd4.c
@@ -0,0 +1,4658 @@
+/* File: cmd4.c */
+
+/* Purpose: Interface commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Hack -- redraw the screen
+ *
+ * This command performs various low level updates, clears all the "extra"
+ * windows, does a total redraw of the main window, and requests all of the
+ * interesting updates and redraws that I can think of.
+ *
+ * This command is also used to "instantiate" the results of the user
+ * selecting various things, such as graphics mode, so it must call
+ * the "TERM_XTRA_REACT" hook before redrawing the windows.
+ */
+void do_cmd_redraw(void)
+{
+ int j;
+
+ term *old = Term;
+
+
+ /* Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+
+ /* Combine and Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+
+ /* Update torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ /* Forget view */
+ p_ptr->update |= (PU_UN_VIEW);
+
+ /* Update view */
+ p_ptr->update |= (PU_VIEW);
+
+ /* Update monster light */
+ p_ptr->update |= (PU_MON_LITE);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw everything */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER | PW_M_LIST);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MESSAGE | PW_OVERHEAD | PW_MONSTER | PW_OBJECT);
+
+ /* Hack -- update */
+ handle_stuff();
+
+
+ /* Redraw every window */
+ for (j = 0; j < 8; j++)
+ {
+ /* Dead window */
+ if (!angband_term[j]) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Redraw */
+ Term_redraw();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- change name
+ */
+void do_cmd_change_name(void)
+{
+ char c;
+
+ int mode = 0;
+
+ char tmp[160];
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Forever */
+ while (1)
+ {
+ /* keep mode below 7 */
+ mode = (mode + 6) % 6;
+
+ /* Display the player */
+ display_player(mode);
+
+ /* Prompt */
+ if (mode == 0)
+ {
+ Term_putstr(14, 22, -1, TERM_WHITE,
+ "['t/T' to change tactics, 'e/E' to change movement]");
+ }
+
+ Term_putstr(4, 23, -1, TERM_WHITE,
+ "['c' to change name, 'f' to file, 'p' for previous, 'n' for next, or ESC]");
+
+ /* Query */
+ c = inkey();
+
+ /* Exit */
+ if (c == ESCAPE) break;
+
+ /* Change name */
+ if (c == 'c')
+ {
+ get_name();
+ }
+
+ /* File dump */
+ else if (c == 'f')
+ {
+ strnfmt(tmp, 160, "%s.txt", player_name);
+ if (get_string("Filename(you can post it to http://angband.oook.cz/): ", tmp, 80))
+ {
+ if (tmp[0] && (tmp[0] != ' '))
+ {
+ file_character(tmp, FALSE);
+ }
+ }
+ }
+
+ /* Toggle mode */
+ else if (c == 'n')
+ {
+ mode++;
+ }
+ else if (c == 'p')
+ {
+ mode--;
+ }
+
+ else if (mode == 0)
+ {
+ /* Change tactic */
+ if (c == 't')
+ {
+ (void)do_cmd_change_tactic( -1);
+ }
+ else if (c == 'T')
+ {
+ (void)do_cmd_change_tactic(1);
+ }
+
+ /* Change movement */
+ else if (c == 'e')
+ {
+ do_cmd_change_movement( -1);
+ }
+ else if (c == 'E')
+ {
+ do_cmd_change_movement(1);
+ }
+ else
+ {
+ bell();
+ }
+ }
+ /* Oops */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+
+ /* Redraw everything */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+
+ handle_stuff();
+}
+
+
+/*
+ * Recall the most recent message
+ */
+void do_cmd_message_one(void)
+{
+ cptr msg = format("> %s", message_str(0));
+
+ /* Recall one message XXX XXX XXX */
+ display_message(0, 0, strlen(msg), message_color(0), msg);
+}
+
+
+/*
+ * Show previous messages to the user -BEN-
+ *
+ * The screen format uses line 0 and (Term->hgt - 1) for headers and prompts,
+ * skips line 1 and (Term->hgt - 2), and uses line 2 thru (Term->hgt - 3) for
+ * old messages.
+ *
+ * This command shows you which commands you are viewing, and allows
+ * you to "search" for strings in the recall.
+ *
+ * Note that messages may be longer than 80 characters, but they are
+ * displayed using "infinite" length, with a special sub-command to
+ * "slide" the virtual display to the left or right.
+ *
+ * Attempt to only hilite the matching portions of the string.
+ *
+ * Now taking advantages of big-screen. -pav-
+ */
+void do_cmd_messages(void)
+{
+ int i, j, k, n;
+ u32b q;
+ int wid, hgt;
+
+ char shower[80];
+ char finder[80];
+
+ /* Wipe finder */
+ strcpy(finder, "");
+
+ /* Wipe shower */
+ strcpy(shower, "");
+
+
+ /* Total messages */
+ n = message_num();
+
+ /* Start on first message */
+ i = 0;
+
+ /* Start at leftmost edge */
+ q = 0;
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Process requests until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Retrieve current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Dump up to 20 (or more in bigscreen) lines of messages */
+ for (j = 0; (j < (hgt - 4)) && (i + j < n); j++)
+ {
+ cptr msg = message_str(i + j);
+ byte color = message_color(i + j);
+
+ /* Apply horizontal scroll */
+ msg = (strlen(msg) >= q) ? (msg + q) : "";
+
+ /* Dump the messages, bottom to top */
+ display_message(0, (hgt - 3) - j, strlen(msg), color, msg);
+
+ /* Hilite "shower" */
+ if (shower[0])
+ {
+ cptr str = msg;
+
+ /* Display matches */
+ while ((str = strstr(str, shower)) != NULL)
+ {
+ int len = strlen(shower);
+
+ /* Display the match */
+ Term_putstr(str - msg, (hgt - 3) - j, len, TERM_YELLOW, shower);
+
+ /* Advance */
+ str += len;
+ }
+ }
+ }
+
+ /* Display header XXX XXX XXX */
+ prt(format("Message Recall (%d-%d of %d), Offset %d",
+ i, i + j - 1, n, q), 0, 0);
+
+ /* Display prompt (not very informative) */
+ prt("[Press 'p' for older, 'n' for newer, ..., or ESCAPE]", hgt - 1, 0);
+
+ /* Get a command */
+ k = inkey();
+
+ /* Exit on Escape */
+ if (k == ESCAPE) break;
+
+ /* Hack -- Save the old index */
+ j = i;
+
+ /* Horizontal scroll */
+ if (k == '4')
+ {
+ /* Scroll left */
+ q = (q >= ((u32b)wid / 2)) ? (q - wid / 2) : 0;
+
+ /* Success */
+ continue;
+ }
+
+ /* Horizontal scroll */
+ if (k == '6')
+ {
+ /* Scroll right */
+ q = q + wid / 2;
+
+ /* Success */
+ continue;
+ }
+
+ /* Hack -- handle show */
+ if (k == '=')
+ {
+ /* Prompt */
+ prt("Show: ", hgt - 1, 0);
+
+ /* Get a "shower" string, or continue */
+ if (!askfor_aux(shower, 80)) continue;
+
+ /* Okay */
+ continue;
+ }
+
+ /* Hack -- handle find */
+ if (k == '/')
+ {
+ s16b z;
+
+ /* Prompt */
+ prt("Find: ", hgt - 1, 0);
+
+ /* Get a "finder" string, or continue */
+ if (!askfor_aux(finder, 80)) continue;
+
+ /* Show it */
+ strcpy(shower, finder);
+
+ /* Scan messages */
+ for (z = i + 1; z < n; z++)
+ {
+ cptr msg = message_str(z);
+
+ /* Search for it */
+ if (strstr(msg, finder))
+ {
+ /* New location */
+ i = z;
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+ /* Recall 1 older message */
+ if ((k == '8') || (k == '\n') || (k == '\r'))
+ {
+ /* Go newer if legal */
+ if (i + 1 < n) i += 1;
+ }
+
+ /* Recall 10 older messages */
+ if (k == '+')
+ {
+ /* Go older if legal */
+ if (i + 10 < n) i += 10;
+ }
+
+ /* Recall one screen of older messages */
+ if ((k == 'p') || (k == KTRL('P')) || (k == ' '))
+ {
+ /* Go older if legal */
+ if (i + (hgt - 4) < n) i += (hgt - 4);
+ }
+
+ /* Recall one screen of newer messages */
+ if ((k == 'n') || (k == KTRL('N')))
+ {
+ /* Go newer (if able) */
+ i = (i >= (hgt - 4)) ? (i - (hgt - 4)) : 0;
+ }
+
+ /* Recall 10 newer messages */
+ if (k == '-')
+ {
+ /* Go newer (if able) */
+ i = (i >= 10) ? (i - 10) : 0;
+ }
+
+ /* Recall 1 newer messages */
+ if (k == '2')
+ {
+ /* Go newer (if able) */
+ i = (i >= 1) ? (i - 1) : 0;
+ }
+
+ /* Hack -- Error of some kind */
+ if (i == j) bell();
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+
+/*
+ * Number of cheating options
+ */
+#define CHEAT_MAX 6
+
+/*
+ * Cheating options
+ */
+static option_type cheat_info[CHEAT_MAX] =
+{
+ { &cheat_peek, FALSE, 0, 0, "cheat_peek", "Peek into object creation" },
+ { &cheat_hear, FALSE, 0, 1, "cheat_hear", "Peek into monster creation" },
+ { &cheat_room, FALSE, 0, 2, "cheat_room", "Peek into dungeon creation" },
+ { &cheat_xtra, FALSE, 0, 3, "cheat_xtra", "Peek into something else" },
+ { &cheat_know, FALSE, 0, 4, "cheat_know", "Know complete monster info" },
+ { &cheat_live, FALSE, 0, 5, "cheat_live", "Allow player to avoid death" }
+};
+
+/*
+ * Interact with some options for cheating
+ */
+static void do_cmd_options_cheat(cptr info)
+{
+ char ch;
+
+ int i, k = 0, n = CHEAT_MAX;
+
+ int dir;
+
+ char buf[80];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact with the player */
+ while (TRUE)
+ {
+ /* Prompt XXX XXX XXX */
+ strnfmt(buf, 80, "%s (RET to advance, y/n to set, ESC to accept) ", info);
+ prt(buf, 0, 0);
+
+ /* Display the options */
+ for (i = 0; i < n; i++)
+ {
+ byte a = TERM_WHITE;
+
+ /* Color current option */
+ if (i == k) a = TERM_L_BLUE;
+
+ /* Display the option text */
+ strnfmt(buf, 80, "%-48s: %s (%s)",
+ cheat_info[i].o_desc,
+ (*cheat_info[i].o_var ? "yes" : "no "),
+ cheat_info[i].o_text);
+ c_prt(a, buf, i + 2, 0);
+ }
+
+ /* Hilite current option */
+ move_cursor(k + 2, 50);
+
+ /* Get a key */
+ ch = inkey();
+
+ /*
+ * Hack -- Try to translate the key into a direction
+ * to allow the use of roguelike keys for navigation
+ */
+ dir = get_keymap_dir(ch);
+ if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir);
+
+
+ /* Analyze */
+ switch (ch)
+ {
+ case ESCAPE:
+ {
+ return;
+ }
+
+ case '-':
+ case '8':
+ {
+ k = (n + k - 1) % n;
+
+ break;
+ }
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '2':
+ {
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'y':
+ case 'Y':
+ case '6':
+ {
+ noscore |= (cheat_info[k].o_page * 256 + cheat_info[k].o_bit);
+ (*cheat_info[k].o_var) = TRUE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ case '4':
+ {
+ (*cheat_info[k].o_var) = FALSE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ default:
+ {
+ bell();
+
+ break;
+ }
+ }
+ }
+}
+
+
+static option_type autosave_info[2] =
+{
+ { &autosave_l, FALSE, 0, 6, "autosave_l", "Autosave when entering new levels" },
+ { &autosave_t, FALSE, 0, 7, "autosave_t", "Timed autosave" },
+};
+
+s16b toggle_frequency(s16b current)
+{
+ if (current == 0) return (50);
+ if (current == 50) return (100);
+ if (current == 100) return (250);
+ if (current == 250) return (500);
+ if (current == 500) return (1000);
+ if (current == 1000) return (2500);
+ if (current == 2500) return (5000);
+ if (current == 5000) return (10000);
+ if (current == 10000) return (25000);
+
+ return (0);
+}
+
+
+/*
+ * Interact with some options for cheating
+ */
+static void do_cmd_options_autosave(cptr info)
+{
+ char ch;
+
+ int i, k = 0, n = 2;
+
+ int dir;
+
+ char buf[80];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact with the player */
+ while (TRUE)
+ {
+ /* Prompt XXX XXX XXX */
+ strnfmt(buf, 80,
+ "%s (RET to advance, y/n to set, 'F' for frequency, ESC to accept) ",
+ info);
+ prt(buf, 0, 0);
+
+ /* Display the options */
+ for (i = 0; i < n; i++)
+ {
+ byte a = TERM_WHITE;
+
+ /* Color current option */
+ if (i == k) a = TERM_L_BLUE;
+
+ /* Display the option text */
+ strnfmt(buf, 80, "%-48s: %s (%s)",
+ autosave_info[i].o_desc,
+ (*autosave_info[i].o_var ? "yes" : "no "),
+ autosave_info[i].o_text);
+ c_prt(a, buf, i + 2, 0);
+ }
+
+ prt(format("Timed autosave frequency: every %d turns", autosave_freq), 5, 0);
+
+
+ /* Hilite current option */
+ move_cursor(k + 2, 50);
+
+ /* Get a key */
+ ch = inkey();
+
+ /*
+ * Hack -- Try to translate the key into a direction
+ * to allow the use of roguelike keys for navigation
+ */
+ dir = get_keymap_dir(ch);
+ if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir);
+
+ /* Analyze */
+ switch (ch)
+ {
+ case ESCAPE:
+ {
+ return;
+ }
+
+ case '-':
+ case '8':
+ {
+ k = (n + k - 1) % n;
+
+ break;
+ }
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '2':
+ {
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'y':
+ case 'Y':
+ case '6':
+ {
+
+ (*autosave_info[k].o_var) = TRUE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ case '4':
+ {
+ (*autosave_info[k].o_var) = FALSE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'f':
+ case 'F':
+ {
+ autosave_freq = toggle_frequency(autosave_freq);
+ prt(format("Timed autosave frequency: every %d turns",
+ autosave_freq), 5, 0);
+
+ break;
+ }
+
+ default:
+ {
+ bell();
+
+ break;
+ }
+ }
+ }
+}
+
+/* Switch an option by only knowing its name */
+bool_ change_option(cptr name, bool_ value)
+{
+ int i;
+
+ /* Scan the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ if (!strcmp(option_info[i].o_text, name))
+ {
+ bool_ old = (*option_info[i].o_var);
+
+ (*option_info[i].o_var) = value;
+
+ return old;
+ }
+ }
+
+ cmsg_format(TERM_VIOLET, "Warning, change_option couldn't find option '%s'.", name);
+ return FALSE;
+}
+
+/*
+ * Interact with some options
+ */
+void do_cmd_options_aux(int page, cptr info, bool_ read_only)
+{
+ char ch;
+
+ int i, k = 0, n = 0;
+
+ int dir;
+
+ int opt[24];
+
+ char buf[80];
+
+
+ /* Lookup the options */
+ for (i = 0; i < 24; i++) opt[i] = 0;
+
+ /* Scan the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ /* Notice options on this "page" */
+ if (option_info[i].o_page == page) opt[n++] = i;
+ }
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact with the player */
+ while (TRUE)
+ {
+ /* Prompt XXX XXX XXX */
+ strnfmt(buf, 80, "%s (RET to advance, y/n to set, ESC to accept) ", info);
+ prt(buf, 0, 0);
+
+ /* Display the options */
+ for (i = 0; i < n; i++)
+ {
+ byte a = TERM_WHITE;
+
+ /* Color current option */
+ if (i == k) a = TERM_L_BLUE;
+
+ /* Display the option text */
+ strnfmt(buf, 80, "%-48s: %s (%s)",
+ option_info[opt[i]].o_desc,
+ (*option_info[opt[i]].o_var ? "yes" : "no "),
+ option_info[opt[i]].o_text);
+ c_prt(a, buf, i + 2, 0);
+ }
+
+ /* Hilite current option */
+ move_cursor(k + 2, 50);
+
+ /* Get a key */
+ ch = inkey();
+
+ /*
+ * Hack -- Try to translate the key into a direction
+ * to allow the use of roguelike keys for navigation
+ */
+ dir = get_keymap_dir(ch);
+ if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir);
+
+ /* Analyze */
+ switch (ch)
+ {
+ case ESCAPE:
+ {
+ return;
+ }
+
+ case '-':
+ case '8':
+ {
+ k = (n + k - 1) % n;
+
+ break;
+ }
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '2':
+ {
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'y':
+ case 'Y':
+ case '6':
+ {
+ if (read_only) break;
+
+ (*option_info[opt[k]].o_var) = TRUE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ case '4':
+ {
+ if (read_only) break;
+
+ (*option_info[opt[k]].o_var) = FALSE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ default:
+ {
+ bell();
+
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Modify the "window" options
+ */
+static void do_cmd_options_win(void)
+{
+ int i, j, d;
+
+ int y = 0;
+
+ int x = 0;
+
+ char ch;
+
+ bool_ go = TRUE;
+
+ u32b old_flag[8];
+
+
+ /* Memorize old flags */
+ for (j = 0; j < 8; j++)
+ {
+ /* Acquire current flags */
+ old_flag[j] = window_flag[j];
+ }
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact */
+ while (go)
+ {
+ /* Prompt XXX XXX XXX */
+ prt("Window Flags (<dir>, t, y, n, ESC) ", 0, 0);
+
+ /* Display the windows */
+ for (j = 0; j < 8; j++)
+ {
+ byte a = TERM_WHITE;
+
+ cptr s = angband_term_name[j];
+
+ /* Use color */
+ if (j == x) a = TERM_L_BLUE;
+
+ /* Window name, staggered, centered */
+ Term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s);
+ }
+
+ /* Display the options */
+ for (i = 0; i < 16; i++)
+ {
+ byte a = TERM_WHITE;
+
+ cptr str = window_flag_desc[i];
+
+ /* Use color */
+ if (i == y) a = TERM_L_BLUE;
+
+ /* Unused option */
+ if (!str) str = "(Unused option)";
+
+ /* Flag name */
+ Term_putstr(0, i + 5, -1, a, str);
+
+ /* Display the windows */
+ for (j = 0; j < 8; j++)
+ {
+ byte a = TERM_WHITE;
+
+ char c = '.';
+
+ /* Use color */
+ if ((i == y) && (j == x)) a = TERM_L_BLUE;
+
+ /* Active flag */
+ if (window_flag[j] & (1L << i)) c = 'X';
+
+ /* Flag value */
+ Term_putch(35 + j * 5, i + 5, a, c);
+ }
+ }
+
+ /* Place Cursor */
+ Term_gotoxy(35 + x * 5, y + 5);
+
+ /* Get key */
+ ch = inkey();
+
+ /* Analyze */
+ switch (ch)
+ {
+ case ESCAPE:
+ {
+ go = FALSE;
+
+ break;
+ }
+
+ case 'T':
+ case 't':
+ {
+ /* Clear windows */
+ for (j = 0; j < 8; j++)
+ {
+ window_flag[j] &= ~(1L << y);
+ }
+
+ /* Clear flags */
+ for (i = 0; i < 16; i++)
+ {
+ window_flag[x] &= ~(1L << i);
+ }
+
+ /* Fall through */
+ }
+
+ case 'y':
+ case 'Y':
+ {
+ /* Ignore screen */
+ if (x == 0) break;
+
+ /* Set flag */
+ window_flag[x] |= (1L << y);
+
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ {
+ /* Clear flag */
+ window_flag[x] &= ~(1L << y);
+
+ break;
+ }
+
+ default:
+ {
+ d = get_keymap_dir(ch);
+
+ x = (x + ddx[d] + 8) % 8;
+ y = (y + ddy[d] + 16) % 16;
+
+ if (!d) bell();
+
+ break;
+ }
+ }
+ }
+
+ /* Notice changes */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* Dead window */
+ if (!angband_term[j]) continue;
+
+ /* Ignore non-changes */
+ if (window_flag[j] == old_flag[j]) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Erase */
+ Term_clear();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Write all current options to the given preference file in the
+ * lib/user directory. Modified from KAmband 1.8.
+ */
+static errr option_dump(cptr fname)
+{
+ int i, j;
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+
+ /* Skip some lines */
+ fprintf(fff, "\n\n");
+
+ /* Start dumping */
+ fprintf(fff, "# Automatic option dump\n\n");
+
+ /* Dump options (skip cheat, adult, score) */
+ for (i = 0; option_info[i].o_var != NULL; i++)
+ {
+ /* Require a real option */
+ if (!option_info[i].o_text) continue;
+
+ /* No birth options */
+ if (option_info[i].o_page == 6) continue;
+
+ /* Comment */
+ fprintf(fff, "# Option '%s'\n", option_info[i].o_desc);
+
+ /* Dump the option */
+ if ((*option_info[i].o_var))
+ {
+ fprintf(fff, "Y:%s\n", option_info[i].o_text);
+ }
+ else
+ {
+ fprintf(fff, "X:%s\n", option_info[i].o_text);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+ }
+
+ /* Dump window flags */
+ for (i = 1; i < ANGBAND_TERM_MAX; i++)
+ {
+ /* Require a real window */
+ if (!angband_term[i]) continue;
+
+ /* Check each flag */
+ for (j = 0; j < 32; j++)
+ {
+ /* Require a real flag */
+ if (!window_flag_desc[j]) continue;
+
+ /* Comment */
+ fprintf(fff, "# Window '%s', Flag '%s'\n",
+ angband_term_name[i], window_flag_desc[j]);
+
+ /* Dump the flag */
+ if (window_flag[i] & (1L << j))
+ {
+ fprintf(fff, "W:%d:%d:1\n", i, j);
+ }
+ else
+ {
+ fprintf(fff, "W:%d:%d:0\n", i, j);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+ }
+ }
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Ask for a "user pref file" and process it.
+ *
+ * This function should only be used by standard interaction commands,
+ * in which a standard "Command:" prompt is present on the given row.
+ *
+ * Allow absolute file names? XXX XXX XXX
+ */
+static void do_cmd_pref_file_hack(int row)
+{
+ char ftmp[80];
+
+
+ /* Prompt */
+ prt("Command: Load a user pref file", row, 0);
+
+ /* Prompt */
+ prt("File: ", row + 2, 0);
+
+ /* Default filename */
+ strnfmt(ftmp, 80, "%s.prf", player_base);
+
+ /* Ask for a file (or cancel) */
+ if (!askfor_aux(ftmp, 80)) return;
+
+ /* Process the given filename */
+ if (process_pref_file(ftmp))
+ {
+ /* Mention failure */
+ msg_format("Failed to load '%s'!", ftmp);
+ }
+ else
+ {
+ /* Mention success */
+ msg_format("Loaded '%s'.", ftmp);
+ }
+}
+
+
+/*
+ * Set or unset various options.
+ *
+ * The user must use the "Ctrl-R" command to "adapt" to changes
+ * in any options which control "visual" aspects of the game.
+ */
+void do_cmd_options(void)
+{
+ int k;
+
+
+ /* Save the screen */
+ screen_save();
+
+ /* Interact */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Why are we here */
+ prt("Options", 2, 0);
+
+ /* Give some choices */
+ prt("(1) User Interface Options", 4, 5);
+ prt("(2) Disturbance Options", 5, 5);
+ prt("(3) Game-Play Options", 6, 5);
+ prt("(4) Efficiency Options", 7, 5);
+ prt("(5) ToME Options", 8, 5);
+ prt("(6) Birth Options(read only)", 9, 5);
+
+ /* Special choices */
+ prt("(D) Base Delay Factor", 10, 5);
+ prt("(H) Hitpoint Warning", 11, 5);
+ prt("(A) Autosave Options", 12, 5);
+
+ /* Automatizer */
+ prt("(T) Automatizer", 14, 5);
+
+
+ /* Window flags */
+ prt("(W) Window Flags", 16, 5);
+
+ /* Cheating */
+ prt("(C) Cheating Options", 18, 5);
+
+ /* Dump */
+ prt("(U) Dump Options setting", 20, 5);
+ prt("(O) Load Options setting", 21, 5);
+
+ /* Prompt */
+ prt("Command: ", 22, 0);
+
+ /* Get command */
+ k = inkey();
+
+ /* Exit */
+ if (k == ESCAPE) break;
+
+ /* Analyze */
+ switch (k)
+ {
+ /* Load a user pref file */
+ case 'o':
+ case 'O':
+ {
+ /* Ask for and load a user pref file */
+ do_cmd_pref_file_hack(21);
+
+ break;
+ }
+
+ /* Append options to a file */
+ case 'u':
+ case 'U':
+ {
+ char ftmp[80];
+
+ /* Prompt */
+ prt("Command: Append options to a file", 21, 0);
+
+ /* Prompt */
+ prt("File: ", 21, 0);
+
+ /* Default filename */
+ strnfmt(ftmp, 80, "%s.prf", player_base);
+
+ /* Ask for a file */
+ if (!askfor_aux(ftmp, 80)) continue;
+
+ /* Dump the options */
+ if (option_dump(ftmp))
+ {
+ /* Failure */
+ msg_print("Failed!");
+ }
+ else
+ {
+ /* Success */
+ msg_print("Done.");
+ }
+
+ break;
+ }
+
+ /* General Options */
+ case '1':
+ {
+ /* Process the general options */
+ do_cmd_options_aux(1, "User Interface Options", FALSE);
+
+ break;
+ }
+
+ /* Disturbance Options */
+ case '2':
+ {
+ /* Spawn */
+ do_cmd_options_aux(2, "Disturbance Options", FALSE);
+
+ break;
+ }
+
+ /* Inventory Options */
+ case '3':
+ {
+ /* Spawn */
+ do_cmd_options_aux(3, "Game-Play Options", FALSE);
+
+ break;
+ }
+
+ /* Efficiency Options */
+ case '4':
+ {
+ /* Spawn */
+ do_cmd_options_aux(4, "Efficiency Options", FALSE);
+
+ break;
+ }
+
+ /* ToME Options */
+ case '5':
+ {
+ do_cmd_options_aux(5, "ToME Options", FALSE);
+
+ break;
+ }
+
+ /* Birth Options - read only */
+ case '6':
+ {
+ do_cmd_options_aux(6, "Birth Options(read only)", TRUE);
+
+ break;
+ }
+ /* Cheating Options */
+ case 'C':
+ {
+ /* Spawn */
+ do_cmd_options_cheat("Cheaters never win");
+
+ break;
+ }
+
+ case 't':
+ case 'T':
+ {
+ do_cmd_automatizer();
+ break;
+ }
+
+ case 'a':
+ case 'A':
+ {
+ do_cmd_options_autosave("Autosave");
+
+ break;
+ }
+
+ /* Window flags */
+ case 'W':
+ case 'w':
+ {
+ /* Spawn */
+ do_cmd_options_win();
+
+ break;
+ }
+
+ /* Hack -- Delay Speed */
+ case 'D':
+ case 'd':
+ {
+ /* Prompt */
+ prt("Command: Base Delay Factor", 21, 0);
+
+ /* Get a new value */
+ while (1)
+ {
+ int msec = delay_factor * delay_factor * delay_factor;
+ prt(format("Current base delay factor: %d (%d msec)",
+ delay_factor, msec), 22, 0);
+ prt("Delay Factor (0-9 or ESC to accept): ", 23, 0);
+ k = inkey();
+ if (k == ESCAPE) break;
+ if (isdigit(k)) delay_factor = D2I(k);
+ else bell();
+ }
+
+ break;
+ }
+
+ /* Hack -- hitpoint warning factor */
+ case 'H':
+ case 'h':
+ {
+ /* Prompt */
+ prt("Command: Hitpoint Warning", 18, 0);
+
+ /* Get a new value */
+ while (1)
+ {
+ prt(format("Current hitpoint warning: %d0%%",
+ hitpoint_warn), 22, 0);
+ prt("Hitpoint Warning (0-9 or ESC to accept): ", 20, 0);
+ k = inkey();
+ if (k == ESCAPE) break;
+ if (isdigit(k)) hitpoint_warn = D2I(k);
+ else bell();
+ }
+
+ break;
+ }
+
+ /* Unknown option */
+ default:
+ {
+ /* Oops */
+ bell();
+
+ break;
+ }
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ screen_load();
+
+ /* Set the ingame help */
+ ingame_help(p_ptr->help.enabled);
+}
+
+
+
+/*
+ * Ask for a "user pref line" and process it
+ *
+ * XXX XXX XXX Allow absolute file names?
+ */
+void do_cmd_pref(void)
+{
+ char buf[80];
+
+
+ /* Default */
+ strcpy(buf, "");
+
+ /* Ask for a "user pref command" */
+ if (!get_string("Pref: ", buf, 80)) return;
+
+ /* Process that pref command */
+ (void)process_pref_file_aux(buf);
+}
+
+
+/*
+ * Hack -- append all current macros to the given file
+ */
+static errr macro_dump(cptr fname)
+{
+ int i;
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+
+ /* Skip space */
+ fprintf(fff, "\n\n");
+
+ /* Start dumping */
+ fprintf(fff, "# Automatic macro dump\n\n");
+
+ /* Dump them */
+ for (i = 0; i < macro__num; i++)
+ {
+ /* Start the macro */
+ fprintf(fff, "# Macro '%d'\n\n", i);
+
+ /* Extract the action */
+ ascii_to_text(buf, macro__act[i]);
+
+ /* Dump the macro */
+ fprintf(fff, "A:%s\n", buf);
+
+ /* Extract the action */
+ ascii_to_text(buf, macro__pat[i]);
+
+ /* Dump normal macros */
+ fprintf(fff, "P:%s\n", buf);
+
+ /* End the macro */
+ fprintf(fff, "\n\n");
+ }
+
+ /* Start dumping */
+ fprintf(fff, "\n\n\n\n");
+
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Hack -- ask for a "trigger" (see below)
+ *
+ * Note the complex use of the "inkey()" function from "util.c".
+ *
+ * Note that both "flush()" calls are extremely important.
+ */
+static void do_cmd_macro_aux(char *buf, bool_ macro_screen)
+{
+ int i, n = 0;
+
+ char tmp[1024];
+
+
+ /* Flush */
+ flush();
+
+ /* Do not process macros */
+ inkey_base = TRUE;
+
+ /* First key */
+ i = inkey();
+
+ /* Read the pattern */
+ while (i)
+ {
+ /* Save the key */
+ buf[n++] = i;
+
+ /* Do not process macros */
+ inkey_base = TRUE;
+
+ /* Do not wait for keys */
+ inkey_scan = TRUE;
+
+ /* Attempt to read a key */
+ i = inkey();
+ }
+
+ /* Terminate */
+ buf[n] = '\0';
+
+ /* Flush */
+ flush();
+
+
+ if (macro_screen)
+ {
+ /* Convert the trigger */
+ ascii_to_text(tmp, buf);
+
+ /* Hack -- display the trigger */
+ Term_addstr( -1, TERM_WHITE, tmp);
+ }
+}
+
+/*
+ * Hack -- ask for a keymap "trigger" (see below)
+ *
+ * Note that both "flush()" calls are extremely important. This may
+ * no longer be true, since "util.c" is much simpler now. XXX XXX XXX
+ */
+static void do_cmd_macro_aux_keymap(char *buf)
+{
+ char tmp[1024];
+
+
+ /* Flush */
+ flush();
+
+
+ /* Get a key */
+ buf[0] = inkey();
+ buf[1] = '\0';
+
+
+ /* Convert to ascii */
+ ascii_to_text(tmp, buf);
+
+ /* Hack -- display the trigger */
+ Term_addstr( -1, TERM_WHITE, tmp);
+
+
+ /* Flush */
+ flush();
+}
+
+
+/*
+ * Hack -- append all keymaps to the given file
+ */
+static errr keymap_dump(cptr fname)
+{
+ int i;
+
+ FILE *fff;
+
+ char key[1024];
+ char buf[1024];
+
+ int mode;
+
+
+ /* Keymap mode */
+ mode = get_keymap_mode();
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+
+ /* Skip space */
+ fprintf(fff, "\n\n");
+
+ /* Start dumping */
+ fprintf(fff, "# Automatic keymap dump\n\n");
+
+ /* Dump them */
+ for (i = 0; i < 256; i++)
+ {
+ cptr act;
+
+ /* Loop up the keymap */
+ act = keymap_act[mode][i];
+
+ /* Skip empty keymaps */
+ if (!act) continue;
+
+ /* Encode the key */
+ buf[0] = i;
+ buf[1] = '\0';
+ ascii_to_text(key, buf);
+
+ /* Encode the action */
+ ascii_to_text(buf, act);
+
+ /* Dump the macro */
+ fprintf(fff, "A:%s\n", buf);
+ fprintf(fff, "C:%d:%s\n", mode, key);
+ }
+
+ /* Start dumping */
+ fprintf(fff, "\n\n\n");
+
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Interact with "macros"
+ *
+ * Note that the macro "action" must be defined before the trigger.
+ *
+ * Could use some helpful instructions on this page. XXX XXX XXX
+ */
+void do_cmd_macros(void)
+{
+ int i;
+
+ char tmp[1024];
+
+ char buf[1024];
+
+ int mode;
+
+
+ /* Keymap mode */
+ mode = get_keymap_mode();
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save screen */
+ Term_save();
+
+
+ /* Process requests until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Describe */
+ prt("Interact with Macros", 2, 0);
+
+
+ /* Describe that action */
+ prt("Current action (if any) shown below:", 20, 0);
+
+ /* Analyze the current action */
+ ascii_to_text(buf, macro__buf);
+
+ /* Display the current action */
+ prt(buf, 22, 0);
+
+
+ /* Selections */
+ prt("(1) Load a user pref file", 4, 5);
+ prt("(2) Append macros to a file", 5, 5);
+ prt("(3) Query a macro", 6, 5);
+ prt("(4) Create a macro", 7, 5);
+ prt("(5) Remove a macro", 8, 5);
+ prt("(6) Append keymaps to a file", 9, 5);
+ prt("(7) Query a keymap", 10, 5);
+ prt("(8) Create a keymap", 11, 5);
+ prt("(9) Remove a keymap", 12, 5);
+ prt("(0) Enter a new action", 13, 5);
+
+ /* Prompt */
+ prt("Command: ", 16, 0);
+
+ /* Get a command */
+ i = inkey();
+
+ /* Leave */
+ if (i == ESCAPE) break;
+
+ /* Load a 'macro' file */
+ else if (i == '1')
+ {
+ /* Prompt */
+ prt("Command: Load a user pref file", 16, 0);
+
+ /* Prompt */
+ prt("File: ", 18, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 1024, "%s.prf", player_name);
+
+ /* Ask for a file */
+ if (!askfor_aux(tmp, 80)) continue;
+
+ /* Process the given filename */
+ if (0 != process_pref_file(tmp))
+ {
+ /* Prompt */
+ msg_print("Could not load file!");
+ }
+ }
+
+ /* Save macros */
+ else if (i == '2')
+ {
+ /* Prompt */
+ prt("Command: Append macros to a file", 16, 0);
+
+ /* Prompt */
+ prt("File: ", 18, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 1024, "%s.prf", player_name);
+
+ /* Ask for a file */
+ if (!askfor_aux(tmp, 80)) continue;
+
+ /* Dump the macros */
+ (void)macro_dump(tmp);
+
+ /* Prompt */
+ msg_print("Appended macros.");
+ }
+
+ /* Query a macro */
+ else if (i == '3')
+ {
+ int k;
+
+ /* Prompt */
+ prt("Command: Query a macro", 16, 0);
+
+ /* Prompt */
+ prt("Trigger: ", 18, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, TRUE);
+
+ /* Acquire action */
+ k = macro_find_exact(buf);
+
+ /* Nothing found */
+ if (k < 0)
+ {
+ /* Prompt */
+ msg_print("Found no macro.");
+ }
+
+ /* Found one */
+ else
+ {
+ /* Obtain the action */
+ strcpy(macro__buf, macro__act[k]);
+
+ /* Analyze the current action */
+ ascii_to_text(buf, macro__buf);
+
+ /* Display the current action */
+ prt(buf, 22, 0);
+
+ /* Prompt */
+ msg_print("Found a macro.");
+ }
+ }
+
+ /* Create a macro */
+ else if (i == '4')
+ {
+ /* Prompt */
+ prt("Command: Create a macro", 16, 0);
+
+ /* Prompt */
+ prt("Trigger: ", 18, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, TRUE);
+
+ /* Clear */
+ clear_from(20);
+
+ /* Prompt */
+ prt("Action: ", 20, 0);
+
+ /* Convert to text */
+ ascii_to_text(tmp, macro__buf);
+
+ /* Get an encoded action */
+ if (askfor_aux(tmp, 80))
+ {
+ /* Convert to ascii */
+ text_to_ascii(macro__buf, tmp);
+
+ /* Link the macro */
+ macro_add(buf, macro__buf);
+
+ /* Prompt */
+ msg_print("Added a macro.");
+ }
+ }
+
+ /* Remove a macro */
+ else if (i == '5')
+ {
+ /* Prompt */
+ prt("Command: Remove a macro", 16, 0);
+
+ /* Prompt */
+ prt("Trigger: ", 18, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, TRUE);
+
+ /* Link the macro */
+ macro_add(buf, buf);
+
+ /* Prompt */
+ msg_print("Removed a macro.");
+ }
+
+ /* Save keymaps */
+ else if (i == '6')
+ {
+ /* Prompt */
+ prt("Command: Append keymaps to a file", 16, 0);
+
+ /* Prompt */
+ prt("File: ", 18, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 1024, "%s.prf", player_name);
+
+ /* Ask for a file */
+ if (!askfor_aux(tmp, 80)) continue;
+
+ /* Dump the macros */
+ (void)keymap_dump(tmp);
+
+ /* Prompt */
+ msg_print("Appended keymaps.");
+ }
+
+ /* Query a keymap */
+ else if (i == '7')
+ {
+ cptr act;
+
+ /* Prompt */
+ prt("Command: Query a keymap", 16, 0);
+
+ /* Prompt */
+ prt("Keypress: ", 18, 0);
+
+ /* Get a keymap trigger */
+ do_cmd_macro_aux_keymap(buf);
+
+ /* Look up the keymap */
+ act = keymap_act[mode][(byte)(buf[0])];
+
+ /* Nothing found */
+ if (!act)
+ {
+ /* Prompt */
+ msg_print("Found no keymap.");
+ }
+
+ /* Found one */
+ else
+ {
+ /* Obtain the action */
+ strcpy(macro__buf, act);
+
+ /* Analyze the current action */
+ ascii_to_text(buf, macro__buf);
+
+ /* Display the current action */
+ prt(buf, 22, 0);
+
+ /* Prompt */
+ msg_print("Found a keymap.");
+ }
+ }
+
+ /* Create a keymap */
+ else if (i == '8')
+ {
+ /* Prompt */
+ prt("Command: Create a keymap", 16, 0);
+
+ /* Prompt */
+ prt("Keypress: ", 18, 0);
+
+ /* Get a keymap trigger */
+ do_cmd_macro_aux_keymap(buf);
+
+ /* Clear */
+ clear_from(20);
+
+ /* Prompt */
+ prt("Action: ", 20, 0);
+
+ /* Convert to text */
+ ascii_to_text(tmp, macro__buf);
+
+ /* Get an encoded action */
+ if (askfor_aux(tmp, 80))
+ {
+ /* Convert to ascii */
+ text_to_ascii(macro__buf, tmp);
+
+ /* Free old keymap */
+ string_free(keymap_act[mode][(byte)(buf[0])]);
+
+ /* Make new keymap */
+ keymap_act[mode][(byte)(buf[0])] = string_make(macro__buf);
+
+ /* Prompt */
+ msg_print("Added a keymap.");
+ }
+ }
+
+ /* Remove a keymap */
+ else if (i == '9')
+ {
+ /* Prompt */
+ prt("Command: Remove a keymap", 16, 0);
+
+ /* Prompt */
+ prt("Keypress: ", 18, 0);
+
+ /* Get a keymap trigger */
+ do_cmd_macro_aux_keymap(buf);
+
+ /* Free old keymap */
+ string_free(keymap_act[mode][(byte)(buf[0])]);
+
+ /* Make new keymap */
+ keymap_act[mode][(byte)(buf[0])] = NULL;
+
+ /* Prompt */
+ msg_print("Removed a keymap.");
+ }
+
+ /* Enter a new action */
+ else if (i == '0')
+ {
+ /* Prompt */
+ prt("Command: Enter a new action", 16, 0);
+
+ /* Go to the correct location */
+ Term_gotoxy(0, 22);
+
+ /* Hack -- limit the value */
+ tmp[80] = '\0';
+
+ /* Get an encoded action */
+ if (!askfor_aux(buf, 80)) continue;
+
+ /* Extract an action */
+ text_to_ascii(macro__buf, buf);
+ }
+
+ /* Oops */
+ else
+ {
+ /* Oops */
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+ /* Load screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Interact with "visuals"
+ */
+void do_cmd_visuals(void)
+{
+ int i;
+
+ FILE *fff;
+
+ char tmp[160];
+
+ char buf[1024];
+
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Interact with Visuals", 2, 0);
+
+ /* Give some choices */
+ prt("(1) Load a user pref file", 4, 5);
+ prt("(2) Dump monster attr/chars", 5, 5);
+ prt("(3) Dump object attr/chars", 6, 5);
+ prt("(4) Dump feature attr/chars", 7, 5);
+ prt("(5) (unused)", 8, 5);
+ prt("(6) Change monster attr/chars", 9, 5);
+ prt("(7) Change object attr/chars", 10, 5);
+ prt("(8) Change feature attr/chars", 11, 5);
+ prt("(9) (unused)", 12, 5);
+ prt("(0) Reset visuals", 13, 5);
+
+ /* Prompt */
+ prt("Command: ", 15, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ /* Load a 'pref' file */
+ else if (i == '1')
+ {
+ /* Prompt */
+ prt("Command: Load a user pref file", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Query */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Process the given filename */
+ (void)process_pref_file(tmp);
+ }
+
+ /* Dump monster attr/chars */
+ else if (i == '2')
+ {
+ /* Prompt */
+ prt("Command: Dump monster attr/chars", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Monster attr/char definitions\n\n");
+
+ /* Dump monsters */
+ for (i = 0; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Skip non-entries */
+ if (!r_ptr->name) continue;
+
+ /* Dump a comment */
+ fprintf(fff, "# %s\n", (r_name + r_ptr->name));
+
+ /* Dump the monster attr/char info */
+ fprintf(fff, "R:%d:0x%02X:0x%02X\n\n", i,
+ (byte)(r_ptr->x_attr), (byte)(r_ptr->x_char));
+ }
+
+ /* All done */
+ fprintf(fff, "\n\n\n\n");
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Message */
+ msg_print("Dumped monster attr/chars.");
+ }
+
+ /* Dump object attr/chars */
+ else if (i == '3')
+ {
+ /* Prompt */
+ prt("Command: Dump object attr/chars", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Object attr/char definitions\n\n");
+
+ /* Dump objects */
+ for (i = 0; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Skip non-entries */
+ if (!k_ptr->name) continue;
+
+ /* Dump a comment */
+ fprintf(fff, "# %s\n", (k_name + k_ptr->name));
+
+ /* Dump the object attr/char info */
+ fprintf(fff, "K:%d:0x%02X:0x%02X\n\n", i,
+ (byte)(k_ptr->x_attr), (byte)(k_ptr->x_char));
+ }
+
+ /* All done */
+ fprintf(fff, "\n\n\n\n");
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Message */
+ msg_print("Dumped object attr/chars.");
+ }
+
+ /* Dump feature attr/chars */
+ else if (i == '4')
+ {
+ /* Prompt */
+ prt("Command: Dump feature attr/chars", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Feature attr/char definitions\n\n");
+
+ /* Dump features */
+ for (i = 0; i < max_f_idx; i++)
+ {
+ feature_type *f_ptr = &f_info[i];
+
+ /* Skip non-entries */
+ if (!f_ptr->name) continue;
+
+ /* Dump a comment */
+ fprintf(fff, "# %s\n", (f_name + f_ptr->name));
+
+ /* Dump the feature attr/char info */
+ fprintf(fff, "F:%d:0x%02X:0x%02X\n\n", i,
+ (byte)(f_ptr->x_attr), (byte)(f_ptr->x_char));
+ }
+
+ /* All done */
+ fprintf(fff, "\n\n\n\n");
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Message */
+ msg_print("Dumped feature attr/chars.");
+ }
+
+ /* Modify monster attr/chars */
+ else if (i == '6')
+ {
+ static int r = 0;
+
+ /* Prompt */
+ prt("Command: Change monster attr/chars", 15, 0);
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ monster_race *r_ptr = &r_info[r];
+
+ byte da = (r_ptr->d_attr);
+ char dc = (r_ptr->d_char);
+ byte ca = (r_ptr->x_attr);
+ char cc = (r_ptr->x_char);
+
+ /* Label the object */
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ format("Monster = %d, Name = %-40.40s",
+ r, (r_name + r_ptr->name)));
+
+ /* Label the Default values */
+ Term_putstr(10, 19, -1, TERM_WHITE,
+ format("Default attr/char = %3u / %3u", da, (dc & 0xFF)));
+ Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 19, da, dc);
+ if (use_bigtile)
+ {
+ if (da & 0x80)
+ Term_putch(44, 19, 255, 255);
+ else
+ Term_putch(44, 19, 0, ' ');
+ }
+
+ /* Label the Current values */
+ Term_putstr(10, 20, -1, TERM_WHITE,
+ format("Current attr/char = %3u / %3u", ca, (cc & 0xFF)));
+ Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 20, ca, cc);
+ if (use_bigtile)
+ {
+ if (ca & 0x80)
+ Term_putch(44, 20, 255, 255);
+ else
+ Term_putch(44, 20, 0, ' ');
+ }
+
+ /* Prompt */
+ Term_putstr(0, 22, -1, TERM_WHITE,
+ "Command (n/N/a/A/c/C): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'n') r = (r + max_r_idx + 1) % max_r_idx;
+ if (i == 'N') r = (r + max_r_idx - 1) % max_r_idx;
+ if (i == 'a') r_ptr->x_attr = (byte)(ca + 1);
+ if (i == 'A') r_ptr->x_attr = (byte)(ca - 1);
+ if (i == 'c') r_ptr->x_char = (byte)(cc + 1);
+ if (i == 'C') r_ptr->x_char = (byte)(cc - 1);
+ }
+ }
+
+ /* Modify object attr/chars */
+ else if (i == '7')
+ {
+ static int k = 0;
+
+ /* Prompt */
+ prt("Command: Change object attr/chars", 15, 0);
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ byte da = (byte)k_ptr->d_attr;
+ char dc = (byte)k_ptr->d_char;
+ byte ca = (byte)k_ptr->x_attr;
+ char cc = (byte)k_ptr->x_char;
+
+ /* Label the object */
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ format("Object = %d, Name = %-40.40s",
+ k, (k_name + k_ptr->name)));
+
+ /* Label the Default values */
+ Term_putstr(10, 19, -1, TERM_WHITE,
+ format("Default attr/char = %3u / %3u", da, (dc & 0xFF)));
+ Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 19, da, dc);
+ if (use_bigtile)
+ {
+ if (da & 0x80)
+ Term_putch(44, 19, 255, 255);
+ else
+ Term_putch(44, 19, 0, ' ');
+ }
+
+ /* Label the Current values */
+ Term_putstr(10, 20, -1, TERM_WHITE,
+ format("Current attr/char = %3u / %3u", ca, (cc & 0xFF)));
+ Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 20, ca, cc);
+ if (use_bigtile)
+ {
+ if (ca & 0x80)
+ Term_putch(44, 20, 255, 255);
+ else
+ Term_putch(44, 20, 0, ' ');
+ }
+
+ /* Prompt */
+ Term_putstr(0, 22, -1, TERM_WHITE,
+ "Command (n/N/a/A/c/C): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'n') k = (k + max_k_idx + 1) % max_k_idx;
+ if (i == 'N') k = (k + max_k_idx - 1) % max_k_idx;
+ if (i == 'a') k_info[k].x_attr = (byte)(ca + 1);
+ if (i == 'A') k_info[k].x_attr = (byte)(ca - 1);
+ if (i == 'c') k_info[k].x_char = (byte)(cc + 1);
+ if (i == 'C') k_info[k].x_char = (byte)(cc - 1);
+ }
+ }
+
+ /* Modify feature attr/chars */
+ else if (i == '8')
+ {
+ static int f = 0;
+
+ /* Prompt */
+ prt("Command: Change feature attr/chars", 15, 0);
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ feature_type *f_ptr = &f_info[f];
+
+ byte da = (byte)f_ptr->d_attr;
+ char dc = (byte)f_ptr->d_char;
+ byte ca = (byte)f_ptr->x_attr;
+ char cc = (byte)f_ptr->x_char;
+
+ /* Label the object */
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ format("Terrain = %d, Name = %-40.40s",
+ f, (f_name + f_ptr->name)));
+
+ /* Label the Default values */
+ Term_putstr(10, 19, -1, TERM_WHITE,
+ format("Default attr/char = %3u / %3u", da, (dc & 0xFF)));
+ Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 19, da, dc);
+ if (use_bigtile)
+ {
+ if (da & 0x80)
+ Term_putch(44, 19, 255, 255);
+ else
+ Term_putch(44, 19, 0, ' ');
+ }
+
+ /* Label the Current values */
+ Term_putstr(10, 20, -1, TERM_WHITE,
+ format("Current attr/char = %3u / %3u", ca, (cc & 0xFF)));
+ Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 20, ca, cc);
+ if (use_bigtile)
+ {
+ if (ca & 0x80)
+ Term_putch(44, 20, 255, 255);
+ else
+ Term_putch(44, 20, 0, ' ');
+ }
+
+ /* Prompt */
+ Term_putstr(0, 22, -1, TERM_WHITE,
+ "Command (n/N/a/A/c/C/d): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'n') f = (f + max_f_idx + 1) % max_f_idx;
+ if (i == 'N') f = (f + max_f_idx - 1) % max_f_idx;
+ if (i == 'a') f_info[f].x_attr = (byte)(ca + 1);
+ if (i == 'A') f_info[f].x_attr = (byte)(ca - 1);
+ if (i == 'c') f_info[f].x_char = (byte)(cc + 1);
+ if (i == 'C') f_info[f].x_char = (byte)(cc - 1);
+ if (i == 'd')
+ {
+ f_info[f].x_char = f_ptr->d_char;
+ f_info[f].x_attr = f_ptr->d_attr;
+ }
+ }
+ }
+
+ /* Reset visuals */
+ else if (i == '0')
+ {
+ /* Reset */
+ reset_visuals();
+
+ /* Message */
+ msg_print("Visual attr/char tables reset.");
+ }
+
+ /* Unknown option */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Interact with "colors"
+ */
+void do_cmd_colors(void)
+{
+ int i;
+
+ FILE *fff;
+
+ char tmp[160];
+
+ char buf[1024];
+
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Interact with Colors", 2, 0);
+
+ /* Give some choices */
+ prt("(1) Load a user pref file", 4, 5);
+ prt("(2) Dump colors", 5, 5);
+ prt("(3) Modify colors", 6, 5);
+
+ /* Prompt */
+ prt("Command: ", 8, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ /* Load a 'pref' file */
+ if (i == '1')
+ {
+ /* Prompt */
+ prt("Command: Load a user pref file", 8, 0);
+
+ /* Prompt */
+ prt("File: ", 10, 0);
+
+ /* Default file */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Query */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Process the given filename */
+ (void)process_pref_file(tmp);
+
+ /* Mega-Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Mega-Hack -- redraw */
+ Term_redraw();
+ }
+
+ /* Dump colors */
+ else if (i == '2')
+ {
+ /* Prompt */
+ prt("Command: Dump colors", 8, 0);
+
+ /* Prompt */
+ prt("File: ", 10, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Color redefinitions\n\n");
+
+ /* Dump colors */
+ for (i = 0; i < 256; i++)
+ {
+ int kv = angband_color_table[i][0];
+ int rv = angband_color_table[i][1];
+ int gv = angband_color_table[i][2];
+ int bv = angband_color_table[i][3];
+
+ cptr name = "unknown";
+
+ /* Skip non-entries */
+ if (!kv && !rv && !gv && !bv) continue;
+
+ /* Extract the color name */
+ if (i < 16) name = color_names[i];
+
+ /* Dump a comment */
+ fprintf(fff, "# Color '%s'\n", name);
+
+ /* Dump the monster attr/char info */
+ fprintf(fff, "V:%d:0x%02X:0x%02X:0x%02X:0x%02X\n\n",
+ i, kv, rv, gv, bv);
+ }
+
+ /* All done */
+ fprintf(fff, "\n\n\n\n");
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Message */
+ msg_print("Dumped color redefinitions.");
+ }
+
+ /* Edit colors */
+ else if (i == '3')
+ {
+ static byte a = 0;
+
+ /* Prompt */
+ prt("Command: Modify colors", 8, 0);
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ cptr name;
+
+ /* Clear */
+ clear_from(10);
+
+ /* Exhibit the normal colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Exhibit this color */
+ Term_putstr(i*4, 20, -1, a, "###");
+
+ /* Exhibit all colors */
+ Term_putstr(i*4, 22, -1, i, format("%3d", i));
+ }
+
+ /* Describe the color */
+ name = ((a < 16) ? color_names[a] : "undefined");
+
+ /* Describe the color */
+ Term_putstr(5, 10, -1, TERM_WHITE,
+ format("Color = %d, Name = %s", a, name));
+
+ /* Label the Current values */
+ Term_putstr(5, 12, -1, TERM_WHITE,
+ format("K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x",
+ angband_color_table[a][0],
+ angband_color_table[a][1],
+ angband_color_table[a][2],
+ angband_color_table[a][3]));
+
+ /* Prompt */
+ Term_putstr(0, 14, -1, TERM_WHITE,
+ "Command (n/N/k/K/r/R/g/G/b/B): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'n') a = (byte)(a + 1);
+ if (i == 'N') a = (byte)(a - 1);
+ if (i == 'k') angband_color_table[a][0] = (byte)(angband_color_table[a][0] + 1);
+ if (i == 'K') angband_color_table[a][0] = (byte)(angband_color_table[a][0] - 1);
+ if (i == 'r') angband_color_table[a][1] = (byte)(angband_color_table[a][1] + 1);
+ if (i == 'R') angband_color_table[a][1] = (byte)(angband_color_table[a][1] - 1);
+ if (i == 'g') angband_color_table[a][2] = (byte)(angband_color_table[a][2] + 1);
+ if (i == 'G') angband_color_table[a][2] = (byte)(angband_color_table[a][2] - 1);
+ if (i == 'b') angband_color_table[a][3] = (byte)(angband_color_table[a][3] + 1);
+ if (i == 'B') angband_color_table[a][3] = (byte)(angband_color_table[a][3] - 1);
+
+ /* Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Hack -- redraw */
+ Term_redraw();
+ }
+ }
+
+ /* Unknown option */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Take notes. There are two ways this can happen, either in the message
+ * recall or a file.
+ */
+void do_cmd_note(void)
+{
+ char buf[80];
+
+
+ /* Default */
+ strcpy(buf, "");
+
+ if (!get_string("Note: ", buf, 60)) return;
+
+ /* Ignore empty notes */
+ if (!buf[0] || (buf[0] == ' ')) return;
+
+ if (take_notes)
+ {
+ /* Add note to file */
+ add_note(buf, ' ');
+ }
+ else
+ {
+ /* Add note to message recall */
+ msg_format("Note: %s", buf);
+ }
+}
+
+
+/*
+ * Mention the current version
+ */
+void do_cmd_version(void)
+{
+ cptr author, email;
+
+ call_lua("get_module_info", "(s,d)", "s", "author", 1, &author);
+ call_lua("get_module_info", "(s,d)", "s", "author", 2, &email);
+
+ /* Silly message */
+ msg_format("You are playing %s made by %s (%s).",
+ get_version_string(),
+ author, email);
+ call_lua("patchs_display", "()", "");
+}
+
+
+
+/*
+ * Array of feeling strings
+ */
+static cptr do_cmd_feeling_text[11] =
+{
+ "Looks like any other level.",
+ "You feel there is something special about this level.",
+ "You have a superb feeling about this level.",
+ "You have an excellent feeling...",
+ "You have a very good feeling...",
+ "You have a good feeling...",
+ "You feel strangely lucky...",
+ "You feel your luck is turning...",
+ "You like the look of this place...",
+ "This level can't be all bad...",
+ "What a boring place..."
+};
+
+
+/*
+ * Note that "feeling" is set to zero unless some time has passed.
+ * Note that this is done when the level is GENERATED, not entered.
+ */
+void do_cmd_feeling(void)
+{
+ /* Verify the feeling */
+ if (feeling < 0) feeling = 0;
+ if (feeling > 10) feeling = 10;
+
+ /* Feeling of the fate */
+ if (fate_flag && !(dungeon_flags2 & DF2_SPECIAL) && !p_ptr->inside_quest)
+ {
+ msg_print("You feel that you will meet your fate here.");
+ }
+
+ /* Hooked feelings ? */
+ if (process_hooks(HOOK_FEELING, "(d)", is_quest(dun_level)))
+ {
+ return;
+ }
+
+ /* No useful feeling in special levels */
+ if (dungeon_flags2 & DF2_DESC)
+ {
+ char buf[1024];
+
+ if ((get_dungeon_save(buf)) || (generate_special_feeling) || (dungeon_flags2 & DF2_DESC_ALWAYS))
+ {
+ if (!get_level_desc(buf)) msg_print("Someone forgot to describe this level!");
+ else msg_print(buf);
+ return;
+ }
+ }
+
+ /* No useful feeling in quests */
+ if (p_ptr->inside_quest)
+ {
+ msg_print("Looks like a typical quest level.");
+ return;
+ }
+
+ /* Display feelings in the dungeon, nothing on the surface */
+ if (dun_level)
+ {
+ /* This could be simplified with a correct p_ptr->town_num */
+ int i, town_level = 0;
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+ if (town_level)
+ msg_print("You hear the sound of a market.");
+ else
+ msg_print(do_cmd_feeling_text[feeling]);
+ }
+ return;
+}
+
+
+
+/*
+ * Encode the screen colors
+ */
+static char hack[17] = "dwsorgbuDWvyRGBU";
+
+
+/*
+ * Hack -- load a screen dump from a file
+ */
+void do_cmd_load_screen(void)
+{
+ int i, y, x;
+
+ int wid, hgt;
+ int len;
+
+ byte a = 0;
+ char c = ' ';
+
+ bool_ okay = TRUE;
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, "dump.txt");
+
+ /* Append to the file */
+ fff = my_fopen(buf, "r");
+
+ /* Oops */
+ if (!fff) return;
+
+
+ /* Retrieve the current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Clear the screen */
+ Term_clear();
+
+
+ /* Load the screen */
+ for (y = 0; okay; y++)
+ {
+ /* Get a line of data */
+ if (my_fgets(fff, buf, 1024)) okay = FALSE;
+
+ /* Stop on blank line */
+ if (!buf[0]) break;
+
+ /* Ignore off screen lines */
+ if (y >= hgt) continue;
+
+ /* Get width */
+ len = strlen(buf);
+
+ /* Truncate if it's longer than current screen width */
+ if (len > wid) len = wid;
+
+ /* Show each row */
+ for (x = 0; x < len; x++)
+ {
+ /* Put the attr/char */
+ Term_draw(x, y, TERM_WHITE, buf[x]);
+ }
+ }
+
+ /* Dump the screen */
+ for (y = 0; okay; y++)
+ {
+ /* Get a line of data */
+ if (my_fgets(fff, buf, 1024)) okay = FALSE;
+
+ /* Stop on blank line */
+ if (!buf[0]) break;
+
+ /* Ignore off screen lines */
+ if (y >= hgt) continue;
+
+ /* Get width */
+ len = strlen(buf);
+
+ /* Truncate if it's longer than current screen width */
+ if (len > wid) len = wid;
+
+ /* Dump each row */
+ for (x = 0; x < len; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Look up the attr */
+ for (i = 0; i < 16; i++)
+ {
+ /* Use attr matches */
+ if (hack[i] == buf[x]) a = i;
+ }
+
+ /* Put the attr/char */
+ Term_draw(x, y, a, c);
+ }
+ }
+
+
+ /* Close it */
+ my_fclose(fff);
+
+
+ /* Message */
+ msg_print("Screen dump loaded.");
+ msg_print(NULL);
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+
+/*
+ * Redefinable "save_screen" action
+ */
+void (*screendump_aux)(void) = NULL;
+
+
+
+
+
+
+/*
+ * Hack -- save a screen dump to a file
+ */
+void do_cmd_save_screen(void)
+{
+ /* Do we use a special screendump function ? */
+ if (screendump_aux)
+ {
+ /* Dump the screen to a graphics file */
+ (*screendump_aux)();
+ }
+
+ /* Dump the screen as text */
+ else
+ {
+ int y, x;
+ int wid, hgt;
+
+ byte a = 0;
+ char c = ' ';
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, "dump.txt");
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff) return;
+
+
+ /* Retrieve the current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Dump the screen */
+ for (y = 0; y < hgt; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < wid; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = c;
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+
+
+ /* Dump the screen */
+ for (y = 0; y < hgt; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < wid; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = hack[a & 0x0F];
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+
+
+ /* Close it */
+ my_fclose(fff);
+
+
+ /* Message */
+ msg_print("Screen dump saved.");
+ msg_print(NULL);
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+ }
+}
+
+
+/*
+ * Check the status of "artifacts"
+ */
+void do_cmd_knowledge_artifacts(void)
+{
+ int i, k, z, x, y;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+ char base_name[80];
+
+ bool_ *okay, *okayk;
+
+
+ /* Allocate the "okay" array */
+ C_MAKE(okay, max_a_idx, bool_);
+ C_MAKE(okayk, max_k_idx, bool_);
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Scan the artifacts */
+ for (k = 0; k < max_a_idx; k++)
+ {
+ artifact_type *a_ptr = &a_info[k];
+
+ /* Default */
+ okay[k] = FALSE;
+
+ /* Skip "empty" artifacts */
+ if (!a_ptr->name) continue;
+
+ /* Skip "uncreated" artifacts */
+ if (!a_ptr->cur_num) continue;
+
+ /* Assume okay */
+ okay[k] = TRUE;
+ }
+
+ for (k = 0; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Default */
+ okayk[k] = FALSE;
+
+ /* Skip "empty" artifacts */
+ if (!(k_ptr->flags3 & TR3_NORM_ART)) continue;
+
+ /* Skip "uncreated" artifacts */
+ if (!k_ptr->artifact) continue;
+
+ /* Assume okay */
+ okayk[k] = TRUE;
+ }
+
+ /* Check the dungeon */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Ignore random artifacts */
+ if (o_ptr->tval == TV_RANDART) continue;
+
+ /* Ignore non-artifacts */
+ if (!artifact_p(o_ptr)) continue;
+
+ /* Ignore known items */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Note the artifact */
+ if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ okayk[o_ptr->k_idx] = FALSE;
+ }
+ else
+ {
+ okay[o_ptr->name1] = FALSE;
+ }
+ }
+ }
+ }
+
+ /* Check monsters in the dungeon */
+ for (i = 0; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ /* Scan all objects the monster carries */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Ignore random artifacts */
+ if (o_ptr->tval == TV_RANDART) continue;
+
+ /* Ignore non-artifacts */
+ if (!artifact_p(o_ptr)) continue;
+
+ /* Ignore known items */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Note the artifact */
+ if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ okayk[o_ptr->k_idx] = FALSE;
+ }
+ else
+ {
+ okay[o_ptr->name1] = FALSE;
+ }
+ }
+ }
+
+ /* Check the p_ptr->inventory and equipment */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Ignore non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Ignore random artifacts */
+ if (o_ptr->tval == TV_RANDART) continue;
+
+ /* Ignore non-artifacts */
+ if (!artifact_p(o_ptr)) continue;
+
+ /* Ignore known items */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Note the artifact */
+ if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ okayk[o_ptr->k_idx] = FALSE;
+ }
+ else
+ {
+ okay[o_ptr->name1] = FALSE;
+ }
+ }
+
+ /* Scan the artifacts */
+ for (k = 0; k < max_a_idx; k++)
+ {
+ artifact_type *a_ptr = &a_info[k];
+
+ /* List "dead" ones */
+ if (!okay[k]) continue;
+
+ /* Paranoia */
+ strcpy(base_name, "Unknown Artifact");
+
+ /* Obtain the base object type */
+ z = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Real object */
+ if (z)
+ {
+ object_type forge;
+ object_type *q_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create fake object */
+ object_prep(q_ptr, z);
+
+ /* Make it an artifact */
+ q_ptr->name1 = k;
+
+ /* Spell in it ? no ! */
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f5 & TR5_SPELL_CONTAIN)
+ q_ptr->pval2 = -1;
+
+ /* Describe the artifact */
+ object_desc_store(base_name, q_ptr, FALSE, 0);
+ }
+
+ /* Hack -- Build the artifact name */
+ fprintf(fff, " The %s\n", base_name);
+ }
+
+ for (k = 0; k < max_k_idx; k++)
+ {
+ /* List "dead" ones */
+ if (!okayk[k]) continue;
+
+ /* Paranoia */
+ strcpy(base_name, "Unknown Artifact");
+
+ /* Real object */
+ if (k)
+ {
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create fake object */
+ object_prep(q_ptr, k);
+
+ /* Describe the artifact */
+ object_desc_store(base_name, q_ptr, FALSE, 0);
+ }
+
+ /* Hack -- Build the artifact name */
+ fprintf(fff, " The %s\n", base_name);
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Artifacts Seen", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+
+ C_FREE(okay, max_a_idx, bool_);
+ C_FREE(okayk, max_k_idx, bool_);
+}
+
+
+/*
+ * Check the status of traps
+ */
+void do_cmd_knowledge_traps(void)
+{
+ int k;
+
+ FILE *fff;
+
+ trap_type *t_ptr;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Scan the traps */
+ for (k = 0; k < max_t_idx; k++)
+ {
+ /* Get the trap */
+ t_ptr = &t_info[k];
+
+ /* Skip "empty" traps */
+ if (!t_ptr->name) continue;
+
+ /* Skip unidentified traps */
+ if (!t_ptr->ident) continue;
+
+ /* Hack -- Build the trap name */
+ fprintf(fff, " %s\n", t_name + t_ptr->name);
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Traps known", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Display known uniques
+ *
+ * Note that the player ghosts are ignored. XXX XXX XXX
+ */
+static void insert_sort_unique(int *sort_uniques, int *num, int r_idx)
+{
+ int i, j;
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int level = r_ptr->level;
+
+
+ /* Hack -- Morgoth is always at the bottom of the list */
+ if (r_idx == 862) level = 20000;
+
+ /* Find the place */
+ for (i = 0; i < *num; i++)
+ {
+ monster_race *r2_ptr = &r_info[sort_uniques[i]];
+ int level2 = r2_ptr->level;
+
+ if (sort_uniques[i] == 862) level2 = 20000;
+
+ if (level < level2) break;
+ }
+
+ /* Move the remaining items */
+ for (j = *num - 1; j >= i; j--)
+ {
+ sort_uniques[j + 1] = sort_uniques[j];
+ }
+
+ /* Insert it */
+ sort_uniques[i] = r_idx;
+ (*num)++;
+}
+
+
+static void do_cmd_knowledge_uniques(void)
+{
+ int k;
+
+ int *sort_uniques;
+
+ int num = 0;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ C_MAKE(sort_uniques, max_r_idx, int);
+
+ /* Sort the monster races */
+ for (k = 1; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ /* Only print Uniques */
+ if (r_ptr->flags1 & (RF1_UNIQUE) &&
+ !(r_ptr->flags7 & RF7_PET) &&
+ !(r_ptr->flags7 & RF7_NEUTRAL))
+ {
+ insert_sort_unique(sort_uniques, &num, k);
+ }
+ }
+
+ /* Scan the monster races -- sorted */
+ for (k = 0; k < num; k++)
+ {
+ monster_race *r_ptr = &r_info[sort_uniques[k]];
+
+ /* Only print Uniques */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool_ dead = (r_ptr->max_num == 0);
+
+ /* Only display "known" uniques */
+ if (dead || cheat_know || r_ptr->r_sights)
+ {
+ /* Print a message */
+ if (dead)
+ {
+ /* Don't print the unique's ASCII symbol
+ * if use_graphics is on. */
+ if (use_graphics)
+ {
+ fprintf(fff, "[[[[[R%-70s is dead]\n",
+ (r_name + r_ptr->name));
+ }
+ else
+ {
+ fprintf(fff, "[[[[[%c%c] [[[[[R%-68s is dead]\n",
+ conv_color[r_ptr->d_attr],
+ r_ptr->d_char,
+ (r_name + r_ptr->name));
+ }
+ }
+ else
+ {
+ /* Don't print the unique's ASCII symbol
+ * if use_graphics is on. */
+ if (use_graphics)
+ {
+ fprintf(fff, "[[[[[w%-70s is alive]\n",
+ (r_name + r_ptr->name));
+ }
+ else
+ {
+ fprintf(fff, "[[[[[%c%c] [[[[[w%-68s is alive]\n",
+ conv_color[r_ptr->d_attr],
+ r_ptr->d_char,
+ (r_name + r_ptr->name));
+ }
+ }
+ }
+ }
+ }
+
+ C_FREE(sort_uniques, max_r_idx, int);
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Known Uniques", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+void plural_aux(char *name)
+{
+ int name_len = strlen(name);
+
+ /* Hack -- Precedent must be pluralised for this one */
+ if (strstr(name, "Disembodied hand"))
+ {
+ strcpy(name, "Disembodied hands that strangled people");
+ }
+
+ /* "someone of something" */
+ else if (strstr(name, " of "))
+ {
+ cptr aider = strstr(name, " of ");
+ char dummy[80];
+ int i = 0;
+ cptr ctr = name;
+
+ while (ctr < aider)
+ {
+ dummy[i] = *ctr;
+ ctr++;
+ i++;
+ }
+
+ if (dummy[i - 1] == 's')
+ {
+ strcpy(&dummy[i], "es");
+ i++;
+ }
+ else
+ {
+ strcpy(&dummy[i], "s");
+ }
+
+ strcpy(&dummy[i + 1], aider);
+ strcpy(name, dummy);
+ }
+
+ /* Creeping coins */
+ else if (strstr(name, "coins"))
+ {
+ char dummy[80];
+ strcpy(dummy, "piles of ");
+ strcat(dummy, name);
+ strcpy(name, dummy);
+ return;
+ }
+
+ /* Manes stay manes */
+ else if (strstr(name, "Manes"))
+ {
+ return;
+ }
+
+ /* Broken plurals are, well, broken */
+ else if (name[name_len - 1] == 'y')
+ {
+ strcpy(&name[name_len - 1], "ies");
+ }
+ else if (streq(&name[name_len - 4], "ouse"))
+ {
+ strcpy(&name[name_len - 4], "ice");
+ }
+ else if (streq(&name[name_len - 6], "kelman"))
+ {
+ strcpy(&name[name_len - 6], "kelmen");
+ }
+ else if (streq(&name[name_len - 2], "ex"))
+ {
+ strcpy(&name[name_len - 2], "ices");
+ }
+ else if (streq(&name[name_len - 3], "olf"))
+ {
+ strcpy(&name[name_len - 3], "olves");
+ }
+
+ /* Now begins sane cases */
+ else if ((streq(&name[name_len - 2], "ch")) || (name[name_len - 1] == 's'))
+ {
+ strcpy(&name[name_len], "es");
+ }
+ else
+ {
+ strcpy(&name[name_len], "s");
+ }
+}
+
+
+/*
+ * Display current pets
+ */
+static void do_cmd_knowledge_pets(void)
+{
+ int i;
+
+ FILE *fff;
+
+ monster_type *m_ptr;
+
+ int t_friends = 0;
+
+ int t_levels = 0;
+
+ int show_upkeep = 0;
+
+ int upkeep_divider = 20;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ if (has_ability(AB_PERFECT_CASTING)) upkeep_divider = 15;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Calculate "upkeep" for friendly monsters */
+ if (m_ptr->status >= MSTATUS_PET)
+ {
+ char pet_name[80];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ t_friends++;
+ t_levels += m_ptr->level;
+ monster_desc(pet_name, m_ptr, 0x88);
+ fprintf(fff, "%s%s (%s)\n",
+ (r_ptr->flags1 & RF1_UNIQUE) ? "#####G" : "",
+ pet_name,
+ (m_ptr->status < MSTATUS_COMPANION) ? "pet" : "companion");
+ }
+ }
+
+ if (t_friends > 1 + (p_ptr->lev / (upkeep_divider)))
+ {
+ show_upkeep = (t_levels);
+
+ if (show_upkeep > 100) show_upkeep = 100;
+ else if (show_upkeep < 10) show_upkeep = 10;
+ }
+
+
+ fprintf(fff, "----------------------------------------------\n");
+ fprintf(fff, " Total: %d pet%s.\n", t_friends, (t_friends == 1 ? "" : "s"));
+ fprintf(fff, " Upkeep: %d%% mana.\n", show_upkeep);
+
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Current Pets", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+
+/*
+ * Total kill count
+ *
+ * Note that the player ghosts are ignored. XXX XXX XXX
+ */
+static void do_cmd_knowledge_kill_count(void)
+{
+ int k;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+ s32b Total = 0;
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ {
+ /* Monsters slain */
+ int kk;
+
+ /* For all monsters */
+ for (kk = 1; kk < max_r_idx; kk++)
+ {
+ monster_race *r_ptr = &r_info[kk];
+
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool_ dead = (r_ptr->max_num == 0);
+
+ if (dead)
+ {
+ Total++;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+
+ if (This > 0)
+ {
+ Total += This;
+ }
+ }
+ }
+
+ if (Total < 1)
+ {
+ fprintf(fff, "You have defeated no enemies yet.\n\n");
+ }
+ else if (Total == 1)
+ {
+ fprintf(fff, "You have defeated one enemy.\n\n");
+ }
+ else
+ {
+ fprintf(fff, "You have defeated %ld enemies.\n\n", (long int) Total);
+ }
+ }
+
+ Total = 0;
+
+ /* Scan the monster races */
+ for (k = 0; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool_ dead = (r_ptr->max_num == 0);
+
+ if (dead)
+ {
+ /* Print a message */
+ fprintf(fff, " %s\n",
+ (r_name + r_ptr->name));
+ Total++;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+
+ if (This > 0)
+ {
+ if (This < 2)
+ {
+ if (strstr(r_name + r_ptr->name, "coins"))
+ {
+ fprintf(fff, " 1 pile of %s\n", (r_name + r_ptr->name));
+ }
+ else
+ {
+ fprintf(fff, " 1 %s\n", (r_name + r_ptr->name));
+ }
+ }
+ else
+ {
+ char to_plural[80];
+ strcpy(to_plural, (r_name + r_ptr->name));
+ plural_aux(to_plural);
+ fprintf(fff, " %d %s\n", This, to_plural);
+ }
+
+ Total += This;
+ }
+ }
+ }
+
+ fprintf(fff, "----------------------------------------------\n");
+ fprintf(fff, " Total: %ld creature%s killed.\n", (long int) Total, (Total == 1 ? "" : "s"));
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Kill Count", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Display known objects
+ */
+static void do_cmd_knowledge_objects(void)
+{
+ int k;
+
+ FILE *fff;
+
+ char o_name[80];
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Scan the object kinds */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Hack -- skip artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /* List known flavored objects */
+ if (k_ptr->flavor && k_ptr->aware)
+ {
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Create fake object */
+ object_prep(i_ptr, k);
+
+ /* Describe the object */
+ object_desc_store(o_name, i_ptr, FALSE, 0);
+
+ /* Print a message */
+ fprintf(fff, " %s\n", o_name);
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Known Objects", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * List recall depths
+ */
+static void do_cmd_knowledge_dungeons(void)
+{
+ int y;
+ char file_name[1024];
+ FILE *fff;
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Oops */
+ if (fff == NULL) return;
+
+ /* Scan all dungeons */
+ for (y = 1; y < max_d_idx; y++)
+ {
+ /* The dungeon has a valid recall depth set */
+ if (max_dlv[y])
+ {
+ /* Describe the recall depth */
+ fprintf(fff, " %c%s: Level %d (%d')\n",
+ (p_ptr->recall_dungeon == y) ? '*' : ' ',
+ d_name + d_info[y].name,
+ max_dlv[y], 50 * (max_dlv[y]));
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Recall Depths", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * List known towns
+ */
+void do_cmd_knowledge_towns(void)
+{
+ int i, j;
+ char file_name[1024];
+ FILE *fff;
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Oops */
+ if (fff == NULL) return;
+
+ /* Scan all dungeons */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ dungeon_info_type *d_ptr = &d_info[i];
+
+ /* Scan all dungeon town slots */
+ for (j = 0; j < TOWN_DUNGEON; j++)
+ {
+ int town_idx = d_ptr->t_idx[j];
+
+ /* Ignore non-existent towns */
+ if (!(town_info[town_idx].flags & (TOWN_REAL))) continue;
+
+ /* Ignore unknown towns */
+ if (!(town_info[town_idx].flags & (TOWN_KNOWN))) continue;
+
+ /* Describe the dungeon town */
+ fprintf(fff, " %s: Level %d (%d')\n",
+ d_name + d_ptr->name,
+ d_ptr->t_level[j],
+ 50 * d_ptr->t_level[j]);
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Dungeon Towns", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * List corruptions
+ */
+void do_cmd_knowledge_corruptions(void)
+{
+ FILE *fff;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Dump the corruptions to file */
+ if (fff) dump_corruptions(fff, TRUE);
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Corruptions", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Helper function for do_cmd_knowledge_quests
+ */
+static void insert_sort_quest(int *order, int *num, int q_idx)
+{
+ int i, j;
+
+ quest_type *q_ptr = &quest[q_idx];
+
+ int level = q_ptr->level;
+
+
+ /* Find the place */
+ for (i = 0; i < *num; i++)
+ {
+ quest_type *q2_ptr = &quest[order[i]];
+ int level2 = q2_ptr->level;
+
+ if (level < level2) break;
+ }
+
+ /* Move the remaining items */
+ for (j = *num - 1; j >= i; j--)
+ {
+ order[j + 1] = order[j];
+ }
+
+ /* Insert it */
+ order[i] = q_idx;
+ (*num)++;
+}
+
+
+/*
+ * Print quest status of all active quests
+ */
+static void do_cmd_knowledge_quests(void)
+{
+ FILE *fff;
+
+ char file_name[1024];
+
+ int *order;
+
+ int num = 0;
+
+ int i, j, z;
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ C_MAKE(order, max_q_idx, int);
+
+ for (i = 0; i < max_q_idx; i++)
+ {
+ insert_sort_quest(order, &num, i);
+ }
+
+ for (z = 0; z < max_q_idx; z++)
+ {
+ i = order[z];
+
+ /* Dynamic quests */
+ if (quest[i].dynamic_desc)
+ {
+ /* C type quests */
+ if (quest[i].type == HOOK_TYPE_C)
+ {
+ if (!quest[i].gen_desc(fff))
+ {
+ continue;
+ }
+ }
+ /* MUST be a lua quest */
+ else
+ {
+ hook_file = fff;
+ exec_lua(format("__quest_dynamic_desc[%d]()", i));
+ }
+ }
+
+ /* Fixed quests (only known ones) */
+ else if (!quest[i].silent)
+ {
+ if (quest[i].status == QUEST_STATUS_TAKEN)
+ {
+ /* Print the quest info */
+ fprintf(fff, "#####y%s (Danger level: %d)\n",
+ quest[i].name, quest[i].level);
+
+ j = 0;
+ while ((j < 10) && (quest[i].desc[j][0] != '\0'))
+ {
+ fprintf(fff, "%s\n", quest[i].desc[j++]);
+ }
+ fprintf(fff, "\n");
+ }
+ else if (quest[i].status == QUEST_STATUS_COMPLETED)
+ {
+ fprintf(fff , "#####G%s Completed - Unrewarded\n", quest[i].name);
+ fprintf(fff, "\n");
+ }
+ }
+ }
+
+ C_FREE(order, max_q_idx, int);
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Quest status", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Print fate status
+ */
+static void do_cmd_knowledge_fates(void)
+{
+ FILE *fff;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ dump_fates(fff);
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Fate status", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Print the note file
+ */
+void do_cmd_knowledge_notes(void)
+{
+ /* Spawn */
+ show_notes_file();
+
+ /* Done */
+ return;
+}
+
+
+/*
+ * Interact with "knowledge"
+ */
+void do_cmd_knowledge(void)
+{
+ int i;
+
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Display current knowledge", 2, 0);
+
+ /* Give some choices */
+ prt("(1) Display known artifacts", 4, 5);
+ prt("(2) Display known uniques", 5, 5);
+ prt("(3) Display known objects", 6, 5);
+ prt("(4) Display kill count", 7, 5);
+ prt("(5) Display recall depths", 8, 5);
+ prt("(6) Display corruptions", 9, 5);
+ prt("(7) Display current pets", 10, 5);
+ prt("(8) Display current quests", 11, 5);
+ prt("(9) Display current fates", 12, 5);
+ prt("(0) Display known traps", 13, 5);
+ prt("(A) Display known dungeon towns", 14, 5);
+ if (take_notes) prt("(B) Display notes", 15, 5);
+
+ /* Prompt */
+ prt("Command: ", 17, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ switch (i)
+ {
+ /* Artifacts */
+ case '1':
+ {
+ do_cmd_knowledge_artifacts();
+
+ break;
+ }
+
+ /* Uniques */
+ case '2':
+ {
+ do_cmd_knowledge_uniques();
+
+ break;
+ }
+
+ /* Objects */
+ case '3':
+ {
+ do_cmd_knowledge_objects();
+
+ break;
+ }
+
+ /* Kill count */
+ case '4':
+ {
+ do_cmd_knowledge_kill_count();
+
+ break;
+ }
+
+ /* Recall depths */
+ case '5':
+ {
+ do_cmd_knowledge_dungeons();
+
+ break;
+ }
+
+ /* corruptions */
+ case '6':
+ {
+ do_cmd_knowledge_corruptions();
+
+ break;
+ }
+
+ /* Pets */
+ case '7':
+ {
+ do_cmd_knowledge_pets();
+
+ break;
+ }
+
+ /* Quests */
+ case '8':
+ {
+ do_cmd_knowledge_quests();
+
+ break;
+ }
+
+ /* Fates */
+ case '9':
+ {
+ do_cmd_knowledge_fates();
+
+ break;
+ }
+
+ /* Traps */
+ case '0':
+ {
+ do_cmd_knowledge_traps();
+
+ break;
+ }
+
+ /* Dungeon towns */
+ case 'A':
+ case 'a':
+ {
+ do_cmd_knowledge_towns();
+
+ break;
+ }
+
+ /* Notes */
+ case 'B':
+ case 'b':
+ {
+ if (take_notes) do_cmd_knowledge_notes();
+ else bell();
+
+ break;
+ }
+
+ /* Unknown option */
+ default:
+ {
+ bell();
+
+ break;
+ }
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Check on the status of an active quest -KMW-
+ * TODO: Spill out status when not a simple kill # monster.
+ */
+void do_cmd_checkquest(void)
+{
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Quest info */
+ do_cmd_knowledge_quests();
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Change player's "tactic" setting
+ */
+void do_cmd_change_tactic(int i)
+{
+ p_ptr->tactic += i;
+ if (p_ptr->tactic > 8) p_ptr->tactic = 0;
+ if (p_ptr->tactic < 0) p_ptr->tactic = 8;
+
+ p_ptr->update |= (PU_BONUS);
+ update_stuff();
+ prt("", 0, 0);
+}
+
+
+/*
+ * Change player's "movement" setting
+ */
+void do_cmd_change_movement(int i)
+{
+ p_ptr->movement += i;
+ if (p_ptr->movement > 8) p_ptr->movement = 0;
+ if (p_ptr->movement < 0) p_ptr->movement = 8;
+
+ p_ptr->update |= (PU_BONUS);
+ update_stuff();
+ prt("", 0, 0);
+}
+
+
+/*
+ * Display the time and date
+ */
+void do_cmd_time()
+{
+ int day = bst(DAY, turn);
+
+ int hour = bst(HOUR, turn);
+
+ int min = bst(MINUTE, turn);
+
+ int full = hour * 100 + min;
+
+ char buf2[20];
+
+ int start = 9999;
+
+ int end = -9999;
+
+ int num = 0;
+
+ char desc[1024];
+
+ char buf[1024];
+
+ FILE *fff;
+
+
+ /* Note */
+ strcpy(desc, "It is a strange time.");
+
+ /* Format time of the day */
+ strnfmt(buf2, 20, get_day(bst(YEAR, turn) + START_YEAR));
+
+ /* Display current date in the Elvish calendar */
+ msg_format("This is %s of the %s year of the third age.",
+ get_month_name(day, wizard, FALSE), buf2);
+
+ /* Message */
+ msg_format("The time is %d:%02d %s.",
+ (hour % 12 == 0) ? 12 : (hour % 12),
+ min, (hour < 12) ? "AM" : "PM");
+
+ /* Find the path */
+ if (!rand_int(10) || p_ptr->image)
+ {
+ path_build(buf, 1024, ANGBAND_DIR_FILE, "timefun.txt");
+ }
+ else
+ {
+ path_build(buf, 1024, ANGBAND_DIR_FILE, "timenorm.txt");
+ }
+
+ /* Open this file */
+ fff = my_fopen(buf, "rt");
+
+ /* Oops */
+ if (!fff) return;
+
+ /* Find this time */
+ while (!my_fgets(fff, buf, 1024))
+ {
+ /* Ignore comments */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Ignore invalid lines */
+ if (buf[1] != ':') continue;
+
+ /* Process 'Start' */
+ if (buf[0] == 'S')
+ {
+ /* Extract the starting time */
+ start = atoi(buf + 2);
+
+ /* Assume valid for an hour */
+ end = start + 59;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'End' */
+ if (buf[0] == 'E')
+ {
+ /* Extract the ending time */
+ end = atoi(buf + 2);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Ignore incorrect range */
+ if ((start > full) || (full > end)) continue;
+
+ /* Process 'Description' */
+ if (buf[0] == 'D')
+ {
+ num++;
+
+ /* Apply the randomizer */
+ if (!rand_int(num)) strcpy(desc, buf + 2);
+
+ /* Next... */
+ continue;
+ }
+ }
+
+ /* Message */
+ msg_print(desc);
+
+ /* Close the file */
+ my_fclose(fff);
+}
+
+/*
+ * Macro recorder!
+ * It records all keypresses and then put them in a macro
+ * Not as powerful as the macro screen, but much easier for newbies
+ */
+char *macro_recorder_current = NULL;
+void macro_recorder_start()
+{
+ msg_print("Starting macro recording, press this key again to stop. Note that if the action you want to record accepts the @ key, use it; it will remove your the need to inscribe stuff.");
+ C_MAKE(macro_recorder_current, 1, char);
+ macro_recorder_current[0] = '\0';
+}
+
+void macro_recorder_add(char c)
+{
+ char *old_macro_recorder_current = macro_recorder_current;
+
+ if (macro_recorder_current == NULL) return;
+
+ C_MAKE(macro_recorder_current, strlen(macro_recorder_current) + 1 + 1, char);
+ sprintf(macro_recorder_current, "%s%c", old_macro_recorder_current, c);
+ C_FREE(old_macro_recorder_current, strlen(old_macro_recorder_current) + 1, char);
+}
+
+void macro_recorder_stop()
+{
+ char *str, *macro;
+ char buf[1024];
+
+ /* Ok we remove the last key, because it is the key to stop recording */
+ macro_recorder_current[strlen(macro_recorder_current) - 1] = '\0';
+
+ /* Stop the recording */
+ macro = macro_recorder_current;
+ macro_recorder_current = NULL;
+
+ /* Add it */
+ if (get_check("Are you satisfied and want to create the macro? "))
+ {
+ prt("Trigger: ", 0, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, FALSE);
+
+ /* Link the macro */
+ macro_add(buf, macro);
+
+ /* Prompt */
+ C_MAKE(str, (strlen(macro) + 1) * 3, char);
+ ascii_to_text(str, macro);
+ msg_format("Added a macro '%s'. If you want it to stay permanently, press @ now and dump macros to a file.", str);
+ C_FREE(str, (strlen(macro) + 1) * 3, char);
+ }
+
+ /* Ok now rid of useless stuff */
+ C_FREE(macro, strlen(macro) + 1, char);
+}
+
+void do_cmd_macro_recorder()
+{
+ if (macro_recorder_current == NULL)
+ macro_recorder_start();
+ else
+ macro_recorder_stop();
+}
diff --git a/src/cmd5.c b/src/cmd5.c
new file mode 100644
index 00000000..b415b166
--- /dev/null
+++ b/src/cmd5.c
@@ -0,0 +1,2562 @@
+/* File: cmd5.c */
+
+/* Purpose: Class commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+#include "angband.h"
+#include "lua/lua.h"
+#include "tolua.h"
+
+extern lua_State *L;
+
+
+/* Maximum number of tries for teleporting */
+#define MAX_TRIES 300
+
+bool_ is_school_book(object_type *o_ptr)
+{
+ if (o_ptr->tval == TV_BOOK)
+ {
+ return TRUE;
+ }
+ else if (o_ptr->tval == TV_DAEMON_BOOK)
+ {
+ return TRUE;
+ }
+ else if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* Does it contains a schooled spell ? */
+static bool_ hook_school_spellable(object_type *o_ptr)
+{
+ if (is_school_book(o_ptr))
+ return TRUE;
+ else
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 != -1))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Is it a book */
+bool_ item_tester_hook_browsable(object_type *o_ptr)
+{
+ if (hook_school_spellable(o_ptr)) return TRUE;
+ if (o_ptr->tval >= TV_BOOK) return TRUE;
+ return FALSE;
+}
+
+/*
+ * Are we using a mage staff
+ */
+bool_ is_magestaff()
+{
+ int i;
+
+
+ i = 0;
+
+ while (p_ptr->body_parts[i] == INVEN_WIELD)
+ {
+ object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ /* Wielding a mage staff */
+ if ((o_ptr->k_idx) && (o_ptr->tval == TV_MSTAFF)) return (TRUE);
+
+ /* Next slot */
+ i++;
+
+ /* Paranoia */
+ if (i >= (INVEN_TOTAL - INVEN_WIELD)) break;
+ }
+
+ /* Not wielding a mage staff */
+ return (FALSE);
+}
+
+/*
+ * Peruse the spells/prayers in a book
+ *
+ * Note that *all* spells in the book are listed
+ *
+ * Note that browsing is allowed while confused or blind,
+ * and in the dark, primarily to allow browsing in stores.
+ */
+
+extern void do_cmd_browse_aux(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (is_school_book(o_ptr))
+ browse_school_spell(o_ptr->sval, o_ptr->pval, o_ptr);
+ else if (f5 & TR5_SPELL_CONTAIN && o_ptr->pval2 != -1)
+ browse_school_spell(255, o_ptr->pval2, o_ptr);
+}
+
+void do_cmd_browse(void)
+{
+ int item;
+
+ cptr q, s;
+
+ object_type *o_ptr;
+
+ /* Restrict choices to "useful" books */
+ item_tester_hook = item_tester_hook_browsable;
+
+ /* Get an item */
+ q = "Browse which book? ";
+ s = "You have no books that you can read.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_EQUIP | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ do_cmd_browse_aux(o_ptr);
+}
+
+void do_poly_wounds(void)
+{
+ /* Changed to always provide at least _some_ healing */
+ s16b wounds = p_ptr->cut;
+
+ s16b hit_p = (p_ptr->mhp - p_ptr->chp);
+
+ s16b change = damroll(p_ptr->lev, 5);
+
+ bool_ Nasty_effect = (randint(5) == 1);
+
+
+ if (!(wounds || hit_p || Nasty_effect)) return;
+
+ msg_print("Your wounds are polymorphed into less serious ones.");
+ hp_player(change);
+ if (Nasty_effect)
+ {
+ msg_print("A new wound was created!");
+ take_hit(change / 2, "a polymorphed wound");
+ set_cut(change);
+ }
+ else
+ {
+ set_cut((p_ptr->cut) - (change / 2));
+ }
+}
+
+void do_poly_self(void)
+{
+ int power = p_ptr->lev;
+ int poly_power;
+
+ msg_print("You feel a change coming over you...");
+
+ if ((power > rand_int(20)) && (rand_int(3) == 0))
+ {
+ char effect_msg[80] = "";
+ int new_race, expfact, goalexpfact;
+
+ /* Some form of racial polymorph... */
+ power -= 10;
+
+ if ((power > rand_int(5)) && (rand_int(4) == 0))
+ {
+ /* sex change */
+ power -= 2;
+
+ if (p_ptr->psex == SEX_MALE)
+ {
+ p_ptr->psex = SEX_FEMALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ strcpy(effect_msg, "female");
+ }
+ else
+ {
+ p_ptr->psex = SEX_MALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ strcpy(effect_msg, "male");
+ }
+ }
+
+ if ((power > rand_int(30)) && (rand_int(5) == 0))
+ {
+ int tmp = 0;
+
+ /* Harmful deformity */
+ power -= 15;
+
+ while (tmp < 6)
+ {
+ if ( rand_int(2) == 0)
+ {
+ (void)dec_stat(tmp, randint(6) + 6, (rand_int(3) == 0));
+ power -= 1;
+ }
+ tmp++;
+ }
+
+ /* Deformities are discriminated against! */
+ (void)dec_stat(A_CHR, randint(6), TRUE);
+
+ if (effect_msg[0])
+ {
+ char tmp_msg[10];
+ strnfmt(tmp_msg, 10, "%s", effect_msg);
+ strnfmt(effect_msg, 80, "deformed %s", tmp_msg);
+ }
+ else
+ {
+ strcpy(effect_msg, "deformed");
+ }
+ }
+
+ while ((power > rand_int(20)) && (rand_int(10) == 0))
+ {
+ /* Polymorph into a less corrupted form */
+ power -= 10;
+
+ lose_corruption(0);
+ }
+
+ /*
+ * I'm not sure 'power' is always positive, with *so* many minuses.
+ * Also, passing zero / negative numbers to randint/rand_int can
+ * cause a zero divide exception, IIRC, not to speak of its absurdity
+ * -- pelpel
+ */
+ poly_power = (power > 1) ? power : 1;
+
+ /*
+ * Restrict the race choices by exp penalty so weak polymorph
+ * always means weak race
+ */
+ goalexpfact = 100 + 3 * rand_int(poly_power);
+
+ /* Roll until an appropriate selection is made */
+ while (1)
+ {
+ new_race = rand_int(max_rp_idx);
+ expfact = race_info[new_race].r_exp;
+
+ if ((new_race != p_ptr->prace) && (expfact <= goalexpfact)) break;
+ }
+
+ if (effect_msg[0])
+ {
+ msg_format("You turn into a%s %s!",
+ ((is_a_vowel(rp_name[race_info[new_race].title])) ? "n" : ""),
+ race_info[new_race].title + rp_name);
+ }
+ else
+ {
+ msg_format("You turn into a %s %s!", effect_msg,
+ race_info[new_race].title);
+ }
+
+ p_ptr->prace = new_race;
+ rp_ptr = &race_info[p_ptr->prace];
+
+ /* Experience factor */
+ p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp;
+
+ /* Calculate the height/weight */
+ get_height_weight();
+
+
+ check_experience();
+ p_ptr->max_plv = p_ptr->lev;
+
+ p_ptr->redraw |= (PR_BASIC);
+
+ p_ptr->update |= (PU_BONUS);
+
+ handle_stuff();
+ lite_spot(p_ptr->py, p_ptr->px);
+ }
+
+ if ((power > rand_int(30)) && (rand_int(6) == 0))
+ {
+ int tmp = 0;
+
+ /* Abomination! */
+ power -= 20;
+
+ msg_print("Your internal organs are rearranged!");
+ while (tmp < 6)
+ {
+ (void)dec_stat(tmp, randint(6) + 6, (rand_int(3) == 0));
+ tmp++;
+ }
+ if (rand_int(6) == 0)
+ {
+ msg_print("You find living difficult in your present form!");
+ take_hit(damroll(randint(10), p_ptr->lev), "a lethal corruption");
+ power -= 10;
+ }
+ }
+
+ if ((power > rand_int(20)) && (rand_int(4) == 0))
+ {
+ power -= 10;
+
+ do_cmd_rerate();
+ }
+
+ while ((power > rand_int(15)) && (rand_int(3) == 0))
+ {
+ power -= 7;
+ (void) gain_random_corruption(0);
+ }
+
+ if (power > rand_int(5))
+ {
+ power -= 5;
+ do_poly_wounds();
+ }
+
+ /* Note: earlier deductions may have left power < 0 already. */
+ while (power > 0)
+ {
+ corrupt_player();
+ power--;
+ }
+}
+
+
+/*
+ * Brand the current weapon
+ */
+void brand_weapon(int brand_type)
+{
+ object_type *o_ptr;
+
+ cptr act = NULL;
+
+ char o_name[80];
+
+
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /*
+ * You can never modify artifacts / ego-items
+ * You can never modify cursed items
+ *
+ * TY: You _can_ modify broken items (if you're silly enough)
+ */
+ if (!o_ptr->k_idx || artifact_p(o_ptr) || ego_item_p(o_ptr) ||
+ o_ptr->art_name || cursed_p(o_ptr))
+ {
+ if (flush_failure) flush();
+
+ msg_print("The Branding failed.");
+
+ return;
+ }
+
+
+ /* Save the old name */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ switch (brand_type)
+ {
+ case 6:
+ {
+ act = "glows with godly power.";
+ o_ptr->name2 = EGO_BLESS_BLADE;
+ o_ptr->pval = randint(4);
+
+ break;
+ }
+ case 5:
+ {
+ act = "seems very powerful.";
+ o_ptr->name2 = EGO_EARTHQUAKES;
+ o_ptr->pval = randint(3);
+
+ break;
+ }
+ case 4:
+ {
+ act = "seems very unstable now.";
+ o_ptr->name2 = EGO_DRAGON;
+ o_ptr->pval = randint(2);
+
+ break;
+ }
+ case 3:
+ {
+ act = "thirsts for blood!";
+ o_ptr->name2 = EGO_VAMPIRIC;
+
+ break;
+ }
+ case 2:
+ {
+ act = "is coated with poison.";
+ o_ptr->name2 = EGO_BRAND_POIS;
+
+ break;
+ }
+ case 1:
+ {
+ act = "is engulfed in raw chaos!";
+ o_ptr->name2 = EGO_CHAOTIC;
+
+ break;
+ }
+ default:
+ {
+ if (rand_int(100) < 25)
+ {
+ act = "is covered in a fiery shield!";
+ o_ptr->name2 = EGO_BRAND_FIRE;
+ }
+ else
+ {
+ act = "glows deep, icy blue!";
+ o_ptr->name2 = EGO_BRAND_COLD;
+ }
+ }
+ }
+
+ /* Apply the ego */
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ o_ptr->discount = 100;
+
+ msg_format("Your %s %s", o_name, act);
+
+ enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM);
+}
+
+/*
+ * Fetch an item (teleport it right underneath the caster)
+ */
+void fetch(int dir, int wgt, bool_ require_los)
+{
+ int ty, tx, i;
+
+ cave_type *c_ptr;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Check to see if an object is already there */
+ if (cave[p_ptr->py][p_ptr->px].o_idx)
+ {
+ msg_print("You can't fetch when you're already standing on something.");
+ return;
+ }
+
+ /* Use a target */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+
+ if (distance(p_ptr->py, p_ptr->px, ty, tx) > MAX_RANGE)
+ {
+ msg_print("You can't fetch something that far away!");
+ return;
+ }
+
+ c_ptr = &cave[ty][tx];
+
+ if (!c_ptr->o_idx)
+ {
+ msg_print("There is no object at this place.");
+ return;
+ }
+
+ if (require_los && (!player_has_los_bold(ty, tx)))
+ {
+ msg_print("You have no direct line of sight to that location.");
+ return;
+ }
+ }
+ else
+ {
+ /* Use a direction */
+ ty = p_ptr->py; /* Where to drop the item */
+ tx = p_ptr->px;
+
+ while (1)
+ {
+ ty += ddy[dir];
+ tx += ddx[dir];
+ c_ptr = &cave[ty][tx];
+
+ if ((distance(p_ptr->py, p_ptr->px, ty, tx) > MAX_RANGE) ||
+ !cave_floor_bold(ty, tx)) return;
+
+ if (c_ptr->o_idx) break;
+ }
+ }
+
+ o_ptr = &o_list[c_ptr->o_idx];
+
+ if (o_ptr->weight > wgt)
+ {
+ /* Too heavy to 'fetch' */
+ msg_print("The object is too heavy.");
+ return;
+ }
+
+ i = c_ptr->o_idx;
+ c_ptr->o_idx = o_ptr->next_o_idx;
+ cave[p_ptr->py][p_ptr->px].o_idx = i; /* 'move' it */
+ o_ptr->next_o_idx = 0;
+ o_ptr->iy = p_ptr->py;
+ o_ptr->ix = p_ptr->px;
+
+ object_desc(o_name, o_ptr, TRUE, 0);
+ msg_format("%^s flies through the air to your feet.", o_name);
+
+ note_spot(p_ptr->py, p_ptr->px);
+ p_ptr->redraw |= PR_MAP;
+}
+
+
+/*
+ * Handle random effects of player shrieking
+ */
+void shriek_effect()
+{
+ switch (randint(9))
+ {
+ case 1:
+ case 5:
+ case 8:
+ case 9:
+ {
+ msg_print("You make a high-pitched shriek!");
+ aggravate_monsters(1);
+
+ break;
+ }
+ case 2:
+ case 6:
+ {
+ msg_print("Oops! You call a monster.");
+ summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0);
+
+ break;
+ }
+ case 3:
+ case 7:
+ {
+ msg_print("The dungeon collapses!");
+ earthquake(p_ptr->py, p_ptr->px, 5);
+
+ break;
+ }
+ case 4:
+ {
+ msg_print("Your shriek is so horrible that you damage your health!");
+ take_hit(damroll(p_ptr->lev / 5, 8), "inner hemorrhaging");
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Like all the random effect codes, this is *ugly*,
+ * and there is not a single line of comment, so I can't tell
+ * some fall throughs are really intended. Well, I know it's
+ * intended to be bizarre :) -- pelpel
+ */
+void wild_magic(int spell)
+{
+ int counter = 0;
+ int type = SUMMON_BIZARRE1 - 1 + randint(6);
+
+ if (type < SUMMON_BIZARRE1) type = SUMMON_BIZARRE1;
+ else if (type > SUMMON_BIZARRE6) type = SUMMON_BIZARRE6;
+
+ switch (randint(spell) + randint(8) + 1)
+ {
+ case 1:
+ case 2:
+ case 3:
+ {
+ teleport_player(10);
+
+ break;
+ }
+
+ case 4:
+ case 5:
+ case 6:
+ {
+ teleport_player(100);
+
+ break;
+ }
+
+ case 7:
+ case 8:
+ {
+ teleport_player(200);
+
+ break;
+ }
+
+ case 9:
+ case 10:
+ case 11:
+ {
+ unlite_area(10, 3);
+
+ break;
+ }
+
+ case 12:
+ case 13:
+ case 14:
+ {
+ lite_area(damroll(2, 3), 2);
+
+ break;
+ }
+
+ case 15:
+ {
+ destroy_doors_touch();
+
+ break;
+ }
+
+ case 16:
+ case 17:
+ {
+ wall_breaker();
+
+ /* I don't think this is a fall through -- pelpel */
+ break;
+ }
+
+ case 18:
+ {
+ sleep_monsters_touch();
+
+ break;
+ }
+
+ case 19:
+ case 20:
+ {
+ trap_creation();
+
+ break;
+ }
+
+ case 21:
+ case 22:
+ {
+ door_creation();
+
+ break;
+ }
+
+ case 23:
+ case 24:
+ case 25:
+ {
+ aggravate_monsters(1);
+
+ break;
+ }
+
+ case 26:
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 5);
+
+ break;
+ }
+
+ case 27:
+ case 28:
+ {
+ break;
+ }
+
+ case 29:
+ case 30:
+ {
+ apply_disenchant(0);
+
+ break;
+ }
+
+ case 31:
+ {
+ lose_all_info();
+
+ break;
+ }
+
+ case 32:
+ {
+ fire_ball(GF_CHAOS, 0, spell + 5, 1 + (spell / 10));
+
+ break;
+ }
+
+ case 33:
+ {
+ wall_stone(p_ptr->py, p_ptr->px);
+
+ break;
+ }
+
+ case 34:
+ case 35:
+ {
+ while (counter++ < 8)
+ {
+ (void) summon_specific(p_ptr->py, p_ptr->px, (dun_level * 3) / 2, type);
+ }
+
+ break;
+ }
+
+ case 36:
+ case 37:
+ {
+ activate_hi_summon();
+
+ break;
+ }
+
+ case 38:
+ {
+ summon_cyber();
+
+ /* I don't think this is a fall through -- pelpel */
+ break;
+ }
+
+ default:
+ {
+ activate_ty_curse();
+ }
+ }
+
+ return;
+}
+
+
+/*
+ * Hack -- Determine if the player is wearing an artefact ring
+ * specified by art_type, that should be an index into a_info
+ */
+bool_ check_ring(int art_type)
+{
+ int i;
+
+
+ /* We are only interested in ring slots */
+ i = INVEN_RING;
+
+ /* Scan the list of rings until we reach the end */
+ while (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_RING)
+ {
+ /* Found the ring we were looking for */
+ if (p_ptr->inventory[i].k_idx && (p_ptr->inventory[i].name1 == art_type))
+ {
+ return (TRUE);
+ }
+
+ /* Next item */
+ i++;
+ }
+
+ /* Found nothing */
+ return (FALSE);
+}
+
+/*
+ * Return the symbiote's name or description.
+ */
+cptr symbiote_name(bool_ capitalize)
+{
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ static char buf[80];
+
+ /* Make sure there actually is a symbiote there... */
+ if (!o_ptr->k_idx)
+ {
+ strcpy(buf, "A non-existent symbiote");
+ }
+ else
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval];
+ cptr s = NULL;
+
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ /* Unique monster; no preceding "your", and ignore our name. */
+ strncpy(buf, r_name + r_ptr->name, sizeof(buf));
+ }
+ else if (o_ptr->note &&
+ (s = strstr(quark_str(o_ptr->note), "#named ")) != NULL)
+ {
+ /* We've named it. */
+ strncpy(buf, s + 7, sizeof(buf));
+ }
+ else
+ {
+ /* No special cases, just return "Your <monster type>". */
+ strcpy(buf, "your ");
+ strncpy(buf + 5, r_name + r_ptr->name, sizeof(buf) - 5);
+ }
+ }
+
+ /* Just in case... */
+ buf[sizeof(buf) - 1] = '\0';
+ if (capitalize) buf[0] = toupper(buf[0]);
+ return buf;
+}
+
+/*
+ * Use a power of the monster in symbiosis
+ */
+int use_symbiotic_power(int r_idx, bool_ great, bool_ only_number, bool_ no_cost)
+{
+ int power = -1;
+
+ int num = 0, dir = 0 , i;
+
+ int powers[96];
+
+ bool_ flag, redraw;
+
+ int ask, plev = p_ptr->lev;
+
+ char choice;
+
+ char out_val[160];
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1);
+
+ int x = p_ptr->px, y = p_ptr->py, k;
+
+ int rad;
+
+ int label;
+
+
+ /* List the monster powers -- RF4_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags4 & BIT(i))
+ {
+ if (monster_powers[i].great && (!great)) continue;
+ if (!monster_powers[i].power) continue;
+ powers[num++] = i;
+ }
+ }
+
+ /* List the monster powers -- RF5_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags5 & BIT(i))
+ {
+ if (monster_powers[i + 32].great && (!great)) continue;
+ if (!monster_powers[i + 32].power) continue;
+ powers[num++] = i + 32;
+ }
+ }
+
+ /* List the monster powers -- RF6_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags6 & BIT(i))
+ {
+ if (monster_powers[i + 64].great && (!great)) continue;
+ if (!monster_powers[i + 64].power) continue;
+ powers[num++] = i + 64;
+ }
+ }
+
+ if (!num)
+ {
+ msg_print("You have no powers you can use.");
+ return (0);
+ }
+
+ if (only_number) return (num);
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Get the last label */
+ label = (num <= 26) ? I2A(num - 1) : I2D(num - 1 - 26);
+
+ /* Build a prompt (accept all spells) */
+ /* Mega Hack -- if no_cost is false, we're actually a Possessor -dsb */
+ strnfmt(out_val, 78,
+ "(Powers a-%c, *=List, ESC=exit) Use which power of your %s? ",
+ label, (no_cost ? "symbiote" : "body"));
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ byte y = 1, x = 0;
+ int ctr = 0;
+ char dummy[80];
+
+ strcpy(dummy, "");
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ prt ("", y++, x);
+
+ while (ctr < num)
+ {
+ monster_power *mp_ptr = &monster_powers[powers[ctr]];
+ int mana = mp_ptr->mana / 10;
+
+ if (mana > p_ptr->msp) mana = p_ptr->msp;
+
+ if (!mana) mana = 1;
+
+ label = (ctr < 26) ? I2A(ctr) : I2D(ctr - 26);
+
+ if (!no_cost)
+ {
+ strnfmt(dummy, 80, " %c) %2d %s",
+ label, mana, mp_ptr->name);
+ }
+ else
+ {
+ strnfmt(dummy, 80, " %c) %s",
+ label, mp_ptr->name);
+ }
+
+ if (ctr < 17)
+ {
+ prt(dummy, y + ctr, x);
+ }
+ else
+ {
+ prt(dummy, y + ctr - 17, x + 40);
+ }
+
+ ctr++;
+ }
+
+ if (ctr < 17)
+ {
+ prt ("", y + ctr, x);
+ }
+ else
+ {
+ prt ("", y + 17, x);
+ }
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ if (choice == '\r' && num == 1)
+ {
+ choice = 'a';
+ }
+
+ if (isalpha(choice))
+ {
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ /* Can't uppercase digits XXX XXX XXX */
+ ask = FALSE;
+
+ i = choice - '0' + 26;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Save the spell index */
+ power = powers[i];
+
+ /* Verify it */
+ if (ask)
+ {
+ char tmp_val[160];
+
+ /* Prompt */
+ strnfmt(tmp_val, 78, "Use %s? ", monster_powers[power].name);
+
+ /* Belay that order */
+ if (!get_check(tmp_val)) continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Abort if needed */
+ if (!flag)
+ {
+ energy_use = 0;
+ return -1;
+ }
+
+ /* 'Powerful' monsters have wider radii */
+ if (r_ptr->flags2 & RF2_POWERFUL)
+ {
+ rad = 1 + (p_ptr->lev / 15);
+ }
+ else
+ {
+ rad = 1 + (p_ptr->lev / 20);
+ }
+
+
+ /* Analyse power */
+ switch (power)
+ {
+ /**** RF4 (bit position) ****/
+
+ /* SHRIEK */
+ case 0:
+ {
+ aggravate_monsters( -1);
+
+ break;
+ }
+
+ /* MULTIPLY */
+ case 1:
+ {
+ do_cmd_wiz_named_friendly(p_ptr->body_monster, FALSE);
+
+ break;
+ }
+
+ /* S_ANIMAL */
+ case 2:
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE);
+
+ break;
+ }
+
+ /* ROCKET */
+ case 3:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ROCKET, dir, p_ptr->lev * 12, 1 + (p_ptr->lev / 20));
+
+ break;
+ }
+
+ /* ARROW_1 */
+ case 4:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ARROW, dir, damroll(1, 6));
+
+ break;
+ }
+
+ /* ARROW_2 */
+ case 5:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ARROW, dir, damroll(3, 6));
+
+ break;
+ }
+
+ /* ARROW_3 */
+ case 6:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ARROW, dir, damroll(5, 6));
+
+ break;
+ }
+
+ /* ARROW_4 */
+ case 7:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ARROW, dir, damroll(7, 6));
+
+ break;
+ }
+
+ /* BR_ACID */
+ case 8:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ACID, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_ELEC */
+ case 9:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ELEC, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_FIRE */
+ case 10:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_FIRE, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_COLD */
+ case 11:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_COLD, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_POIS */
+ case 12:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_POIS, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_NETH */
+ case 13:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NETHER, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_LITE */
+ case 14:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_LITE, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_DARK */
+ case 15:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_DARK, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_CONF */
+ case 16:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_CONFUSION, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_SOUN */
+ case 17:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_SOUND, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_CHAO */
+ case 18:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_CHAOS, dir, p_ptr->lev * 7, rad);
+
+ break;
+ }
+
+ /* BR_DISE */
+ case 19:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_DISENCHANT, dir, p_ptr->lev * 7, rad);
+
+ break;
+ }
+
+ /* BR_NEXU */
+ case 20:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NEXUS, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_TIME */
+ case 21:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_TIME, dir, p_ptr->lev * 3, rad);
+
+ break;
+ }
+
+ /* BR_INER */
+ case 22:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_INERTIA, dir, p_ptr->lev * 4, rad);
+
+ break;
+ }
+
+ /* BR_GRAV */
+ case 23:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_GRAVITY, dir, p_ptr->lev * 4, rad);
+
+ break;
+ }
+
+ /* BR_SHAR */
+ case 24:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_SHARDS, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_PLAS */
+ case 25:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_PLASMA, dir, p_ptr->lev * 3, rad);
+
+ break;
+ }
+
+ /* BR_WALL */
+ case 26:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_FORCE, dir, p_ptr->lev * 4, rad);
+
+ break;
+ }
+
+ /* BR_MANA */
+ case 27:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_MANA, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BA_NUKE */
+ case 28:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NUKE, dir, p_ptr->lev * 8, 1 + (p_ptr->lev / 20));
+
+ break;
+ }
+
+ /* BR_NUKE */
+ case 29:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NUKE, dir, p_ptr->lev * 8, 1 + (p_ptr->lev / 20));
+
+ break;
+ }
+
+ /* BA_CHAO */
+ case 30:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_CHAOS, dir, p_ptr->lev * 4, 2);
+
+ break;
+ }
+
+ /* BR_DISI */
+ case 31:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_DISINTEGRATE, dir, p_ptr->lev * 5, 1 + (p_ptr->lev / 20));
+
+ break;
+ }
+
+
+ /**** RF5 (bit position + 32) ****/
+
+ /* BA_ACID */
+ case 32:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ACID, dir, randint(p_ptr->lev * 6) + 20, 2);
+
+ break;
+ }
+
+ /* BA_ELEC */
+ case 33:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ELEC, dir, randint(p_ptr->lev * 3) + 20, 2);
+
+ break;
+ }
+
+ /* BA_FIRE */
+ case 34:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_FIRE, dir, randint(p_ptr->lev * 7) + 20, 2);
+
+ break;
+ }
+
+ /* BA_COLD */
+ case 35:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_COLD, dir, randint(p_ptr->lev * 3) + 20, 2);
+
+ break;
+ }
+
+ /* BA_POIS */
+ case 36:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_POIS, dir, damroll(12, 2), 2);
+
+ break;
+ }
+
+ /* BA_NETH */
+ case 37:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NETHER, dir, randint(p_ptr->lev * 4) + 20, 2);
+
+ break;
+ }
+
+ /* BA_WATE */
+ case 38:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_WATER, dir, randint(p_ptr->lev * 4) + 20, 2);
+
+ break;
+ }
+
+ /* BA_MANA */
+ case 39:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_MANA, dir, randint(p_ptr->lev * 3) + 20, 2);
+
+ break;
+ }
+
+ /* BA_DARK */
+ case 40:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_DARK, dir, randint(p_ptr->lev * 3) + 20, 2);
+
+ break;
+ }
+
+ /* 41 DRAIN_MANA -- Not available */
+
+ /* 42 MIND_BLAST -- Not available */
+
+ /* 43 BRAIN_SMASH -- Not available */
+
+ /* CAUSE_1 */
+ case 44:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(3, 8));
+
+ break;
+ }
+
+ /* CAUSE_2 */
+ case 45:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(8, 8));
+
+ break;
+ }
+
+ /* CAUSE_3 */
+ case 46:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(10, 15));
+
+ break;
+ }
+
+ /* CAUSE_4 */
+ case 47:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(15, 15));
+
+ break;
+ }
+
+ /* BO_ACID */
+ case 48:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ACID, dir, damroll(7, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_ELEC */
+ case 49:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ELEC, dir, damroll(4, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_FIRE */
+ case 50:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_FIRE, dir, damroll(9, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_COLD */
+ case 51:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_COLD, dir, damroll(6, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_POIS */
+ case 52:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_POIS, dir, damroll(7, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_NETH */
+ case 53:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_NETHER, dir, damroll(5, 5) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_WATE */
+ case 54:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_WATER, dir, damroll(10, 10) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_MANA */
+ case 55:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(3, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_PLAS */
+ case 56:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_PLASMA, dir, damroll(8, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_ICEE */
+ case 57:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ICE, dir, damroll(6, 6) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* MISSILE */
+ case 58:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MISSILE, dir, damroll(2, 6) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* SCARE */
+ case 59:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fear_monster(dir, plev);
+
+ break;
+ }
+
+ /* BLIND */
+ case 60:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_CONFUSION, dir, damroll(1, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* CONF */
+ case 61:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_CONFUSION, dir, damroll(7, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* SLOW */
+ case 62:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_OLD_SLOW, dir, damroll(6, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* HOLD */
+ case 63:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_OLD_SLEEP, dir, damroll(5, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+
+ /**** RF6 (bit position + 64) ****/
+
+ /* HASTE */
+ case 64:
+ {
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(20 + (plev) ) + plev, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + randint(5), 10);
+ }
+
+ break;
+ }
+
+ /* HAND_DOOM */
+ case 65:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(10, 8) + (p_ptr->lev));
+
+ break;
+ }
+
+ /* HEAL */
+ case 66:
+ {
+ hp_player(damroll(8, 5));
+
+ break;
+ }
+
+ /* S_ANIMALS */
+ case 67:
+ {
+ for (k = 0; k < 4; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE);
+ }
+
+ break;
+ }
+
+ /* BLINK */
+ case 68:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ teleport_player(10);
+
+ break;
+ }
+
+ /* TPORT */
+ case 69:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ teleport_player(plev * 5);
+
+ break;
+ }
+
+ /* TELE_TO */
+ case 70:
+ {
+ int ii, ij;
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ msg_print("You go between.");
+
+ if (!tgt_pt(&ii, &ij)) break;
+
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) ||
+ (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev * 20 + 2))
+ {
+ msg_print("You fail to show the destination correctly!");
+ p_ptr->energy -= 100;
+ teleport_player(10);
+ }
+ else teleport_player_to(ij, ii);
+
+ break;
+ }
+
+ /* TELE_AWAY */
+ case 71:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ if (!get_aim_dir(&dir)) break;
+
+ (void)fire_beam(GF_AWAY_ALL, dir, plev);
+
+ break;
+ }
+
+ /* TELE_LEVEL */
+ case 72:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ teleport_player_level();
+
+ break;
+ }
+
+ /* DARKNESS */
+ case 73:
+ {
+ (void)project( -1, 3, p_ptr->py, p_ptr->px, 0, GF_DARK_WEAK,
+ PROJECT_GRID | PROJECT_KILL);
+
+ /* Unlite the room */
+ unlite_room(p_ptr->py, p_ptr->px);
+
+ break;
+ }
+
+ /* TRAPS */
+ case 74:
+ {
+ trap_creation();
+
+ break;
+ }
+
+ /* 75 FORGET -- Not available */
+
+ /* ANIM_DEAD -- Use the same code as the nether spell */
+ case 76:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_RAISE, dir, 1, 0);
+
+ break;
+ }
+
+ /* 77 S_BUG -- Not available, well we do that anyway ;) */
+
+ /* 78 S_RNG -- Not available, who dares? */
+
+ /* S_THUNDERLORD */
+ case 79:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_THUNDERLORD, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_KIN -- Summon Kin, because we code bugs :) */
+ case 80:
+ {
+ /* Big hack */
+ summon_kin_type = r_ptr->d_char;
+
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_KIN, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HI_DEMON */
+ case 81:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HI_DEMON, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_MONSTER */
+ case 82:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, 0, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_MONSTERS */
+ case 83:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, 0, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_ANT */
+ case 84:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_ANT, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_SPIDER */
+ case 85:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_SPIDER, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HOUND */
+ case 86:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HOUND, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HYDRA */
+ case 87:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HYDRA, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_ANGEL */
+ case 88:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_ANGEL, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_DEMON */
+ case 89:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_DEMON, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_UNDEAD */
+ case 90:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_UNDEAD, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_DRAGON */
+ case 91:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_DRAGON, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HI_UNDEAD */
+ case 92:
+ {
+ for (k = 0; k < 8; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HI_UNDEAD_NO_UNIQUES, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HI_DRAGON */
+ case 93:
+ {
+ for (k = 0; k < 8; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HI_DRAGON_NO_UNIQUES, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_WRAITH */
+ case 94:
+ {
+ for (k = 0; k < 8; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_WRAITH, TRUE);
+ }
+
+ break;
+ }
+
+ /* 95 S_UNIQUE -- Not available */
+ }
+
+ /* Take some SP */
+ if (!no_cost)
+ {
+ int chance, pchance;
+
+ chance = (monster_powers[power].mana + r_ptr->level);
+ pchance = adj_str_wgt[p_ptr->stat_ind[A_WIS]] / 2 + get_skill(SKILL_POSSESSION);
+
+ if (rand_int(chance) >= pchance)
+ {
+ int m = monster_powers[power].mana / 10;
+
+ if (m > p_ptr->msp) m = p_ptr->msp;
+ if (!m) m = 1;
+
+ p_ptr->csp -= m;
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ return (num);
+}
+
+/*
+ * Schooled magic
+ */
+
+/*
+ * Find a spell in any books/objects
+ */
+static int hack_force_spell = -1;
+static object_type *hack_force_spell_obj = NULL;
+bool_ get_item_hook_find_spell(int *item)
+{
+ int i, spell;
+ char buf[80];
+ char buf2[100];
+
+ strcpy(buf, "Manathrust");
+ if (!get_string("Spell name? ", buf, 79))
+ return FALSE;
+ sprintf(buf2, "return find_spell(\"%s\")", buf);
+ spell = exec_lua(buf2);
+ if (spell == -1) return FALSE;
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Must we wield it ? */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if ((wield_slot(o_ptr) != -1) && (i < INVEN_WIELD) && (f5 & TR5_WIELD_CAST)) continue;
+
+ /* Is it a non-book? */
+ if (!is_school_book(o_ptr))
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == spell))
+ {
+ *item = i;
+ hack_force_spell = spell;
+ hack_force_spell_obj = o_ptr;
+ return TRUE;
+ }
+ }
+ /* A random book ? */
+ else if ((o_ptr->sval == 255) && (o_ptr->pval == spell))
+ {
+ *item = i;
+ hack_force_spell = spell;
+ hack_force_spell_obj = o_ptr;
+ return TRUE;
+ }
+ /* A normal book */
+ else if (o_ptr->sval != 255)
+ {
+ sprintf(buf2, "return spell_in_book(%d, %d)", o_ptr->sval, spell);
+ if (exec_lua(buf2))
+ {
+ *item = i;
+ hack_force_spell = spell;
+ hack_force_spell_obj = o_ptr;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Get a spell from a book
+ */
+s32b get_school_spell(cptr do_what, cptr check_fct, s16b force_book)
+{
+ int i, item;
+ s32b spell = -1;
+ int num = 0;
+ s32b where = 1;
+ int ask;
+ bool_ flag, redraw;
+ char choice;
+ char out_val[160];
+ char buf2[40];
+ char buf3[40];
+ object_type *o_ptr, forge;
+ int tmp;
+ int sval, pval;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ hack_force_spell = -1;
+ hack_force_spell_obj = NULL;
+
+ /* Ok do we need to ask for a book ? */
+ if (!force_book)
+ {
+ get_item_extra_hook = get_item_hook_find_spell;
+ item_tester_hook = hook_school_spellable;
+ sprintf(buf2, "You have no book to %s from", do_what);
+ sprintf(buf3, "%s from which book?", do_what);
+ if (!get_item(&item, buf3, buf2, USE_INVEN | USE_EQUIP | USE_EXTRA )) return -1;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* If it can be wielded, it must */
+ if ((wield_slot(o_ptr) != -1) && (item < INVEN_WIELD) && (f5 & TR5_WIELD_CAST))
+ {
+ msg_format("You cannot %s from that object; it must be wielded first.", do_what);
+ return -1;
+ }
+ }
+ else
+ {
+ o_ptr = &forge;
+ o_ptr->tval = TV_BOOK;
+ o_ptr->sval = force_book;
+ o_ptr->pval = 0;
+ }
+
+ if (repeat_pull(&tmp))
+ {
+ return tmp;
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Show choices */
+ if (show_choices)
+ {
+ /* Window stuff */
+ window_stuff();
+ }
+
+ /* No spell to cast by default */
+ spell = -1;
+
+ /* Is it a random book, or something else ? */
+ if (is_school_book(o_ptr))
+ {
+ sval = o_ptr->sval;
+ pval = o_ptr->pval;
+ }
+ else
+ {
+ sval = 255;
+ pval = o_ptr->pval2;
+ }
+
+ if (hack_force_spell == -1)
+ {
+ num = exec_lua(format("return book_spells_num(%d)", sval));
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78, "(Spells %c-%c, Descs %c-%c, *=List, ESC=exit) %^s which spell? ",
+ I2A(0), I2A(num - 1), I2A(0) - 'a' + 'A', I2A(num - 1) - 'a' + 'A', do_what);
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if (((choice == ' ') || (choice == '*') || (choice == '?')))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", sval, pval, o_ptr, &where);
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+ where = 1;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Verify it */
+ if (ask)
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_load();
+ Term_save();
+
+ }
+ /* Rstore the screen */
+ else
+ {
+ /* Restore the screen */
+ Term_load();
+ }
+
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", sval, pval, o_ptr, &where);
+ exec_lua(format("print_spell_desc(spell_x(%d, %d, %d), %d)", sval, pval, i, where));
+ }
+ else
+ {
+ s32b ok;
+
+ /* Save the spell index */
+ spell = exec_lua(format("return spell_x(%d, %d, %d)", sval, pval, i));
+
+ /* Do we need to do some pre test */
+ call_lua(check_fct, "(d,O)", "d", spell, o_ptr, &ok);
+
+ /* Require "okay" spells */
+ if (!ok)
+ {
+ bell();
+ msg_format("You may not %s that spell.", do_what);
+ spell = -1;
+ continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+ }
+ }
+ else
+ {
+ s32b ok;
+
+ /* Require "okay" spells */
+ call_lua(check_fct, "(d, O)", "d", hack_force_spell, hack_force_spell_obj, &ok);
+ if (ok)
+ {
+ flag = TRUE;
+ spell = hack_force_spell;
+ }
+ else
+ {
+ bell();
+ msg_format("You may not %s that spell.", do_what);
+ spell = -1;
+ }
+ }
+
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ character_icky = FALSE;
+ }
+
+
+ /* Show choices */
+ if (show_choices)
+ {
+ /* Window stuff */
+ window_stuff();
+ }
+
+
+ /* Abort if needed */
+ if (!flag) return -1;
+
+ tmp = spell;
+ repeat_push(tmp);
+ return spell;
+}
+
+void cast_school_spell()
+{
+ int spell;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ spell = get_school_spell("cast", "is_ok_spell", 0);
+
+ /* Actualy cast the choice */
+ if (spell != -1)
+ {
+ exec_lua(format("cast_school_spell(%d, spell(%d))", spell, spell));
+ }
+}
+
+void browse_school_spell(int book, int pval, object_type *o_ptr)
+{
+ int i;
+ int num = 0, where = 1;
+ int ask;
+ char choice;
+ char out_val[160];
+
+ /* Show choices */
+ if (show_choices)
+ {
+ /* Window stuff */
+ window_stuff();
+ }
+
+ num = exec_lua(format("return book_spells_num(%d)", book));
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78, "(Spells %c-%c, ESC=exit) cast which spell? ",
+ I2A(0), I2A(num - 1));
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", book, pval, o_ptr, &where);
+
+ /* Get a spell from the user */
+ while (get_com(out_val, &choice))
+ {
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", book, pval, o_ptr, &where);
+
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", book, pval, o_ptr, &where);
+ exec_lua(format("print_spell_desc(spell_x(%d, %d, %d), %d)", book, pval, i, where));
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+ /* Show choices */
+ if (show_choices)
+ {
+ /* Window stuff */
+ window_stuff();
+ }
+}
+
+/* Can it contains a schooled spell ? */
+static bool_ hook_school_can_spellable(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == -1))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Copy a spell from a bok to an object
+ */
+void do_cmd_copy_spell()
+{
+ int spell = get_school_spell("copy", "is_ok_spell", 0);
+ int item;
+ object_type *o_ptr;
+
+ if (spell == -1) return;
+
+ /* Spells that cannot be randomly created cannot be copied */
+ if (exec_lua(format("return can_spell_random(%d)", spell)) == FALSE)
+ {
+ msg_print("This spell cannot be copied.");
+ return;
+ }
+
+ item_tester_hook = hook_school_can_spellable;
+ if (!get_item(&item, "Copy to which object? ", "You have no object to copy to.", (USE_INVEN | USE_EQUIP))) return;
+ o_ptr = get_object(item);
+
+ msg_print("You copy the spell!");
+ o_ptr->pval2 = spell;
+ inven_item_describe(item);
+}
+
+/*
+ * Finds a spell by name, optimized for speed
+ */
+int find_spell(char *name)
+{
+ int oldtop, spell;
+ oldtop = lua_gettop(L);
+
+ lua_getglobal(L, "find_spell");
+ tolua_pushstring(L, name);
+
+ /* Call the function */
+ if (lua_call(L, 1, 1))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling 'find_spell'.");
+ lua_settop(L, oldtop);
+ return -1;
+ }
+
+ spell = tolua_getnumber(L, -(lua_gettop(L) - oldtop), -1);
+
+ lua_settop(L, oldtop);
+
+ return spell;
+}
diff --git a/src/cmd6.c b/src/cmd6.c
new file mode 100644
index 00000000..db89c465
--- /dev/null
+++ b/src/cmd6.c
@@ -0,0 +1,7731 @@
+/* File: cmd6.c */
+
+/* Purpose: Object commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Forward declare
+ */
+static bool_ activate_spell(object_type * o_ptr, byte choice);
+
+
+/*
+ * General function to find an item by its name
+ */
+cptr get_item_hook_find_obj_what;
+bool_ get_item_hook_find_obj(int *item)
+{
+ int i;
+ char buf[80];
+ char buf2[100];
+
+ strcpy(buf, "");
+ if (!get_string(get_item_hook_find_obj_what, buf, 79))
+ return FALSE;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!item_tester_okay(o_ptr)) continue;
+
+ object_desc(buf2, o_ptr, -1, 0);
+ if (!strcmp(buf, buf2))
+ {
+ *item = i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ * This file includes code for eating food, drinking potions,
+ * reading scrolls, aiming wands, using staffs, zapping rods,
+ * and activating artifacts.
+ *
+ * In all cases, if the player becomes "aware" of the item's use
+ * by testing it, mark it as "aware" and reward some experience
+ * based on the object's level, always rounding up. If the player
+ * remains "unaware", mark that object "kind" as "tried".
+ *
+ * This code now correctly handles the unstacking of wands, staffs,
+ * and rods. Note the overly paranoid warning about potential pack
+ * overflow, which allows the player to use and drop a stacked item.
+ *
+ * In all "unstacking" scenarios, the "used" object is "carried" as if
+ * the player had just picked it up. In particular, this means that if
+ * the use of an item induces pack overflow, that item will be dropped.
+ *
+ * For simplicity, these routines induce a full "pack reorganization"
+ * which not only combines similar items, but also reorganizes various
+ * items to obey the current "sorting" method. This may require about
+ * 400 item comparisons, but only occasionally.
+ *
+ * There may be a BIG problem with any "effect" that can cause "changes"
+ * to the p_ptr->inventory. For example, a "scroll of recharging" can cause
+ * a wand/staff to "disappear", moving the p_ptr->inventory up. Luckily, the
+ * scrolls all appear BEFORE the staffs/wands, so this is not a problem.
+ * But, for example, a "staff of recharging" could cause MAJOR problems.
+ * In such a case, it will be best to either (1) "postpone" the effect
+ * until the end of the function, or (2) "change" the effect, say, into
+ * giving a staff "negative" charges, or "turning a staff into a stick".
+ * It seems as though a "rod of recharging" might in fact cause problems.
+ * The basic problem is that the act of recharging (and destroying) an
+ * item causes the inducer of that action to "move", causing "o_ptr" to
+ * no longer point at the correct item, with horrifying results.
+ *
+ * Note that food/potions/scrolls no longer use bit-flags for effects,
+ * but instead use the "sval" (which is also used to sort the objects).
+ */
+
+
+/*
+ * Determine the effects of eating a corpse. A corpse can be
+ * eaten whole or cut into pieces for later.
+ */
+static void corpse_effect(object_type *o_ptr, bool_ cutting)
+{
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ /* Assume no bad effects */
+ bool_ harmful = FALSE;
+
+ byte method, effect, d_dice, d_side;
+
+ int i, dam, idam = 0, mdam, brpow, brdam = 0;
+
+
+ /* How much of the monster's breath attack remains */
+ if (o_ptr->pval <= r_ptr->weight)
+ {
+ brpow = 0;
+ }
+ else
+ {
+ brpow = (o_ptr->pval - r_ptr->weight) / 5;
+ if (brpow > (r_ptr->weight / 5)) brpow = r_ptr->weight / 5;
+ }
+
+ if (o_ptr->weight <= 0) o_ptr->weight = 1;
+ if (o_ptr->pval <= 0) o_ptr->pval = 1;
+
+ /*
+ * The breath is only discharged by accident or by slicing off pieces
+ * of meat, and only by corpses.
+ */
+ if ((o_ptr->sval != SV_CORPSE_CORPSE) ||
+ (rand_int(o_ptr->weight / 5) && !cutting)) brpow = 0;
+
+ /* Immediate effects - poison, acid, fire, etc. */
+ if (!cutting)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ /* skip empty blow slot */
+ if (!r_ptr->blow[i].method) continue;
+
+ method = r_ptr->blow[i].method;
+ effect = r_ptr->blow[i].effect;
+ d_dice = r_ptr->blow[i].d_dice;
+ d_side = r_ptr->blow[i].d_side;
+ dam = damroll(d_dice, d_side) * o_ptr->pval / o_ptr->weight / 2;
+ idam = damroll(d_dice, d_side) *
+ ((o_ptr->weight / o_ptr->pval > 2) ?
+ o_ptr->weight / o_ptr->pval : 2);
+ mdam = maxroll(d_dice, d_side) * 2;
+
+ /* Analyse method */
+ switch (method)
+ {
+ /* Methods that are meaningless after death */
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_ENGULF:
+ case RBM_DROOL:
+ case RBM_SPIT:
+ case RBM_GAZE:
+ case RBM_WAIL:
+ case RBM_BEG:
+ case RBM_INSULT:
+ case RBM_MOAN:
+ {
+ continue;
+ }
+ }
+
+ /* Analyse effect */
+ switch (effect)
+ {
+ /* Effects that are meaningless after death */
+ case RBE_HURT:
+ case RBE_UN_BONUS:
+ case RBE_UN_POWER:
+ case RBE_EAT_GOLD:
+ case RBE_EAT_ITEM:
+ case RBE_EAT_FOOD:
+ case RBE_EAT_LITE:
+ case RBE_ELEC:
+ case RBE_COLD:
+ case RBE_SHATTER:
+ {
+ break;
+ }
+
+ case RBE_POISON:
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + dam + idam + 10);
+ harmful = TRUE;
+ }
+
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ /* Total Immunity */
+ if (!(p_ptr->immune_acid || (dam <= 0)))
+ {
+ /* Resist the damage */
+ if (p_ptr->resist_acid) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_acid) dam = (dam + 2) / 3;
+
+ /* Take damage */
+ take_hit(dam, "acidic food");
+ harmful = TRUE;
+ }
+ else
+ {
+ set_oppose_acid(p_ptr->oppose_acid + idam);
+ }
+
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ /* Totally immune */
+ if (p_ptr->immune_fire || (dam <= 0))
+ {
+ /* Resist the damage */
+ if (p_ptr->resist_fire) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_fire) dam = (dam + 2) / 3;
+
+ /* Take damage */
+ take_hit(dam, "a fiery meal");
+ harmful = TRUE;
+ }
+ else
+ {
+ set_oppose_fire(p_ptr->oppose_fire + idam);
+ }
+
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ if (!p_ptr->resist_blind)
+ {
+ set_blind(p_ptr->blind + dam * 2 + idam * 2 + 20);
+ }
+
+ break;
+ }
+
+ case RBE_CONFUSE:
+ {
+ if (!p_ptr->resist_conf)
+ {
+ set_confused(p_ptr->confused + dam + idam + 10);
+ }
+ if (!p_ptr->resist_chaos && rand_int(mdam - dam))
+ {
+ set_image(p_ptr->image + dam * 10 + idam * 10 + 100);
+ }
+
+ break;
+ }
+
+ case RBE_HALLU:
+ {
+ if (!p_ptr->resist_chaos && rand_int(mdam - dam))
+ {
+ set_image(p_ptr->image + dam * 10 + idam * 10 + 50);
+ }
+
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ if (!p_ptr->resist_fear)
+ {
+ set_afraid(p_ptr->afraid + dam + idam + 10);
+ }
+
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ if (!p_ptr->free_act)
+ {
+ set_paralyzed(p_ptr->paralyzed + dam + idam + 10);
+ }
+
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ {
+ do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_INT:
+ {
+ do_dec_stat(A_INT, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_WIS:
+ {
+ do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_DEX:
+ {
+ do_dec_stat(A_DEX, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_CON:
+ {
+ do_dec_stat(A_CON, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_CHR:
+ {
+ do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ /* Don't eat Morgoth's corpse :) */
+ case RBE_LOSE_ALL:
+ {
+ do_dec_stat(A_STR, STAT_DEC_NORMAL);
+ do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ do_dec_stat(A_DEX, STAT_DEC_NORMAL);
+ do_dec_stat(A_CON, STAT_DEC_NORMAL);
+ do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+ o_ptr->pval = 1;
+
+ break;
+ }
+
+ case RBE_SANITY:
+ {
+ msg_print("You feel your sanity slipping away!");
+ take_sanity_hit(dam, "eating an insane monster");
+
+ break;
+ }
+
+ /* Unlife is bad to eat */
+ case RBE_EXP_10:
+ {
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(10, 6) +
+ (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+
+ o_ptr->pval = 1;
+
+ break;
+ }
+
+ case RBE_EXP_20:
+ {
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(20, 6) +
+ (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+
+ o_ptr->pval = 1;
+
+ break;
+ }
+
+ case RBE_EXP_40:
+ {
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(40, 6) +
+ (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+
+ o_ptr->pval = 1;
+
+ break;
+ }
+
+ case RBE_EXP_80:
+ {
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(80, 6) +
+ (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+
+ o_ptr->pval = 1;
+
+ break;
+ }
+ }
+ }
+ } /* if (!cutting) */
+
+
+ /*
+ * The organ that supplies breath attacks is not
+ * immediately emptied upon death, although some types
+ * of breath have no effect.
+ * AMHD's make rather risky meals, and deadly snacks.
+ */
+
+ /* Acid */
+ if (r_ptr->flags4 & RF4_BR_ACID && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));
+
+ msg_print("You are hit by a gush of acid!");
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_acid || (brdam <= 0)))
+ {
+ /* Take damage */
+ acid_dam(brdam, "a gush of acid");
+ harmful = TRUE;
+ }
+ o_ptr->pval = 1;
+ }
+ else if (r_ptr->flags4 & RF4_BR_ACID)
+ {
+ set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10);
+ }
+
+ /* Electricity */
+ if (r_ptr->flags4 & RF4_BR_ELEC && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));
+
+ msg_print("You receive a heavy shock!");
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_elec || (brdam <= 0)))
+ {
+ /* Take damage */
+ elec_dam(brdam, "an electric shock");
+ harmful = TRUE;
+ }
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ }
+ else if (r_ptr->flags4 & RF4_BR_ELEC)
+ {
+ set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10);
+ }
+
+ /* Fire */
+ if (r_ptr->flags4 & RF4_BR_FIRE && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));
+
+ msg_print("Roaring flames engulf you!");
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_fire || (brdam <= 0)))
+ {
+ /* Take damage */
+ fire_dam(brdam, "an explosion");
+ harmful = TRUE;
+ }
+ o_ptr->pval = 1;
+ }
+ else if (r_ptr->flags4 & RF4_BR_FIRE)
+ {
+ set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10);
+ }
+
+ /* Cold */
+ if (r_ptr->flags4 & RF4_BR_COLD && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));
+
+ msg_print("You are caught in a freezing liquid!");
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_cold || (brdam <= 0)))
+ {
+ /* Take damage */
+ cold_dam(brdam, "a chilling blast");
+ harmful = TRUE;
+ }
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ }
+ else if (r_ptr->flags4 & RF4_BR_COLD)
+ {
+ set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10);
+ }
+
+ /* Poison */
+ if (r_ptr->flags4 & RF4_BR_POIS && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 800 ? 800 : (brpow / 3));
+
+ msg_print("You are surrounded by toxic gases!");
+
+ /* Resist the damage */
+ if (p_ptr->resist_pois) brdam = (brdam + 2) / 3;
+ if (p_ptr->oppose_pois) brdam = (brdam + 2) / 3;
+
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ (void)set_poisoned(p_ptr->poisoned + rand_int(brdam) + 10);
+ }
+
+ /* Take damage */
+ take_hit(brdam, "toxic gases");
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ harmful = TRUE;
+ }
+
+ /* Nether */
+ if (r_ptr->flags4 & RF4_BR_NETH && brpow > 0)
+ {
+ brdam = ((brpow / 6) > 550 ? 550 : (brpow / 6));
+
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->resist_neth)
+ {
+ brdam *= 6;
+ brdam /= (randint(6) + 6);
+ }
+ else
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(200 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(200 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+
+ /* Take damage */
+ take_hit(brdam, "an unholy blast");
+ harmful = TRUE;
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ }
+
+ /* Confusion */
+ if (r_ptr->flags4 & RF4_BR_CONF && brpow > 0)
+ {
+ msg_print("A strange liquid splashes on you!");
+
+ if (!p_ptr->resist_conf)
+ {
+ set_confused(p_ptr->confused + brdam + idam + 10);
+ }
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ }
+
+ /* Chaos */
+ if (r_ptr->flags4 & RF4_BR_CHAO && brpow > 0)
+ {
+ brdam = ((brpow / 6) > 600 ? 600 : (brpow / 6));
+
+ msg_print("A swirling cloud surrounds you!");
+
+ if (p_ptr->resist_chaos)
+ {
+ brdam *= 6;
+ brdam /= (randint(6) + 6);
+ }
+
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(20) + 10);
+ }
+
+ if (!p_ptr->resist_chaos)
+ {
+ (void)set_image(p_ptr->image + randint(10));
+ }
+
+ if (!p_ptr->resist_neth && !p_ptr->resist_chaos)
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(500 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(5000 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+
+ /* Take damage */
+ take_hit(brdam, "chaotic forces");
+ o_ptr->pval = 1;
+ }
+
+ /* Disenchantment */
+ if (r_ptr->flags4 & RF4_BR_DISE && brpow > 0)
+ {
+ brdam = ((brpow / 6) > 500 ? 500 : (brpow / 6));
+
+ msg_print("You are blasted by raw mana!");
+
+ if (p_ptr->resist_disen)
+ {
+ brdam *= 6;
+ brdam /= (randint(6) + 6);
+ }
+ else
+ {
+ (void)apply_disenchant(0);
+ }
+
+ /* Take damage */
+ take_hit(brdam, "raw mana");
+ o_ptr->pval = 1;
+ }
+
+ /* Plasma */
+ if (r_ptr->flags4 & RF4_BR_PLAS && brpow > 0)
+ {
+ brdam = ((brpow / 6) > 150 ? 150 : (brpow / 6));
+
+ msg_print("Searing flames engulf the corpse!");
+
+ /* Resist the damage */
+ if (p_ptr->resist_fire || p_ptr->oppose_fire) brdam = (brdam + 2) / 3;
+
+ if (!p_ptr->resist_sound)
+ {
+ int k = (randint((brdam > 40) ? 35 : (brdam * 3 / 4 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+
+ /* Take damage */
+ take_hit(brdam, "an explosion");
+ harmful = TRUE;
+ o_ptr->pval = 1;
+ }
+
+ /* Hack -- Jellies are immune to acid only if they are already acidic */
+ if (strchr("j", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_ACID))
+ {
+ dam = damroll(8, 8);
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_acid || (dam <= 0)))
+ {
+ /* Resist the damage */
+ if (p_ptr->resist_acid) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_acid) dam = (dam + 2) / 3;
+
+ /* Take damage */
+ take_hit(dam, "acidic food");
+ }
+ harmful = TRUE;
+ }
+
+ /*
+ * Hack -- Jellies, kobolds, spiders, icky things, molds, and mushrooms
+ * are immune to poison because their body already contains
+ * poisonous chemicals.
+ */
+ if (strchr("ijkmS,", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_POIS))
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(15) + 10);
+ }
+ harmful = TRUE;
+ }
+
+ /*
+ * Bad effects override good effects
+ * and hacked-up corpses lose intrinsics.
+ */
+ if (!harmful && !cutting && (o_ptr->sval != SV_CORPSE_MEAT))
+ {
+ if (r_ptr->flags3 & RF3_IM_ACID)
+ {
+ set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_IM_ELEC)
+ {
+ set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_IM_FIRE)
+ {
+ set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_IM_COLD)
+ {
+ set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_IM_POIS)
+ {
+ set_oppose_pois(p_ptr->oppose_pois + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_RES_NETH)
+ {
+ set_protevil(p_ptr->protevil + rand_int(25) + 3 * r_ptr->level);
+ }
+ if (r_ptr->flags3 & RF3_RES_PLAS)
+ {
+ set_oppose_fire(p_ptr->oppose_fire + rand_int(20) + 20);
+ }
+ if (r_ptr->flags2 & RF2_SHAPECHANGER)
+ {
+ /* DGDGDG (void)set_mimic(20 , rand_int(MIMIC_VALAR)); */
+ }
+
+ if (r_ptr->flags3 & RF3_DEMON)
+ {
+ /* DGDGDG (void)set_mimic(30 , MIMIC_DEMON); */
+ }
+
+ if (r_ptr->flags3 & RF3_UNDEAD)
+ {
+ /* DGDGDG (void)set_mimic(30 , MIMIC_VAMPIRE); */
+ }
+
+ if (r_ptr->flags3 & RF3_NO_FEAR)
+ {
+ (void)set_afraid(0);
+ }
+ if (r_ptr->flags3 & RF3_NO_STUN)
+ {
+ (void)set_stun(0);
+ }
+ if (r_ptr->flags3 & RF3_NO_CONF)
+ {
+ (void)set_confused(0);
+ }
+ if (r_ptr->flags6 & RF6_S_THUNDERLORD)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_THUNDERLORD, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_DEMON)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DEMON, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_KIN)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_KIN, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HI_DEMON)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DEMON, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_MONSTER)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_MONSTERS)
+ {
+ int k;
+ for (k = 0; k < 8; k++)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE);
+ }
+ }
+ if (r_ptr->flags6 & RF6_S_UNDEAD)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_DRAGON)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DRAGON, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_ANT)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANT, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_SPIDER)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_SPIDER, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HOUND)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HOUND, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HYDRA)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HYDRA, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_ANGEL)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANGEL, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HI_DRAGON)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DRAGON, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HI_UNDEAD)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_UNDEAD, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_WRAITH)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_WRAITH, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_UNIQUE)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNIQUE, FALSE);
+ }
+ }
+}
+
+
+/*
+ * Hook to determine if an object is eatable
+ */
+static bool_ item_tester_hook_eatable(object_type *o_ptr)
+{
+ /* Foods and, well, corpses are edible */
+ if ((o_ptr->tval == TV_FOOD) || (o_ptr->tval == TV_CORPSE)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Eat some food (from the pack or floor)
+ */
+void do_cmd_eat_food(void)
+{
+ int item, ident, lev, fval = 0;
+
+ object_type *o_ptr;
+ object_type *q_ptr, forge;
+
+ monster_race *r_ptr;
+
+ cptr q, s;
+
+ bool_ destroy = TRUE;
+
+
+ /* Restrict choices to food */
+ item_tester_hook = item_tester_hook_eatable;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Food full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Eat which item? ";
+ s = "You have nothing to eat.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Sound */
+ sound(SOUND_EAT);
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Identity not known yet */
+ ident = FALSE;
+
+ /* Object level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Scripted foods */
+ if (process_hooks_ret(HOOK_EAT, "d", "(O)", o_ptr))
+ {
+ ident = process_hooks_return[0].num;
+ }
+ /* (not quite) Normal foods */
+ else if (o_ptr->tval == TV_FOOD)
+ {
+ /* Analyze the food */
+ switch (o_ptr->sval)
+ {
+ case SV_FOOD_GREAT_HEALTH:
+ {
+ p_ptr->hp_mod += 70;
+ msg_print("As you eat it you begin to feel your life flow getting stronger.");
+ ident = TRUE;
+ p_ptr->update |= (PU_HP);
+
+ break;
+ }
+
+ case SV_FOOD_POISON:
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + rand_int(10) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_BLINDNESS:
+ {
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + rand_int(200) + 200))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_PARANOIA:
+ {
+ if (!p_ptr->resist_fear)
+ {
+ if (set_afraid(p_ptr->afraid + rand_int(10) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_CONFUSION:
+ {
+ if (!p_ptr->resist_conf)
+ {
+ if (set_confused(p_ptr->confused + rand_int(10) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_HALLUCINATION:
+ {
+ if (!p_ptr->resist_chaos)
+ {
+ if (set_image(p_ptr->image + rand_int(250) + 250))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_PARALYSIS:
+ {
+ if (!p_ptr->free_act)
+ {
+ if (set_paralyzed(p_ptr->paralyzed + rand_int(10) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_WEAKNESS:
+ {
+ take_hit(damroll(6, 6), "poisonous food");
+ (void)do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_SICKNESS:
+ {
+ take_hit(damroll(6, 6), "poisonous food");
+ (void)do_dec_stat(A_CON, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_STUPIDITY:
+ {
+ take_hit(damroll(8, 8), "poisonous food");
+ (void)do_dec_stat(A_INT, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_NAIVETY:
+ {
+ take_hit(damroll(8, 8), "poisonous food");
+ (void)do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_UNHEALTH:
+ {
+ take_hit(damroll(10, 10), "poisonous food");
+ (void)do_dec_stat(A_CON, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_DISEASE:
+ {
+ take_hit(damroll(10, 10), "poisonous food");
+ (void)do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_POISON:
+ {
+ if (set_poisoned(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_BLINDNESS:
+ {
+ if (set_blind(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_PARANOIA:
+ {
+ if (set_afraid(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_CONFUSION:
+ {
+ if (set_confused(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_SERIOUS:
+ {
+ if (hp_player(damroll(4, 8))) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_RESTORE_STR:
+ {
+ if (do_res_stat(A_STR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_RESTORE_CON:
+ {
+ if (do_res_stat(A_CON, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_RESTORING:
+ {
+ if (do_res_stat(A_STR, TRUE)) ident = TRUE;
+ if (do_res_stat(A_INT, TRUE)) ident = TRUE;
+ if (do_res_stat(A_WIS, TRUE)) ident = TRUE;
+ if (do_res_stat(A_DEX, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CON, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CHR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_FORTUNE_COOKIE:
+ {
+ char rumour[80];
+
+ msg_print("That tastes good.");
+ msg_print("There is message in the cookie. It says:");
+ msg_print(NULL);
+
+ switch (randint(20))
+ {
+ case 1:
+ {
+ get_rnd_line("chainswd.txt", rumour);
+ break;
+ }
+
+ case 2:
+ {
+ get_rnd_line("error.txt", rumour);
+ break;
+ }
+
+ case 3:
+ case 4:
+ case 5:
+ {
+ get_rnd_line("death.txt", rumour);
+ break;
+ }
+
+ default:
+ {
+ get_rnd_line("rumors.txt", rumour);
+ break;
+ }
+ }
+
+ msg_format("%s", rumour);
+ msg_print(NULL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+
+ case SV_FOOD_RATION:
+ case SV_FOOD_BISCUIT:
+ case SV_FOOD_JERKY:
+ {
+ msg_print("That tastes good.");
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_SLIME_MOLD:
+ {
+ msg_print("That tastes good.");
+
+ /* 2% chance of getting the mold power */
+ if (magik(2))
+ {
+ ADD_POWER(p_ptr->powers_mod, PWR_GROW_MOLD);
+ p_ptr->update |= PU_POWERS;
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_WAYBREAD:
+ {
+ msg_print("That tastes very good.");
+ (void)set_poisoned(0);
+ (void)hp_player(damroll(4, 8));
+ set_food(PY_FOOD_MAX - 1);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_PINT_OF_ALE:
+ case SV_FOOD_PINT_OF_WINE:
+ {
+ msg_print("That tastes good.");
+
+ ident = TRUE;
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1));
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ break;
+ }
+
+ case SV_FOOD_ATHELAS:
+ {
+ msg_print("A fresh, clean essence rises, driving away wounds and poison.");
+
+ (void)set_poisoned(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ p_ptr->black_breath = FALSE;
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+ }
+ }
+
+ /* Corpses... */
+ else
+ {
+ r_ptr = &r_info[o_ptr->pval2];
+
+ /* Analyse the corpse */
+ switch (o_ptr->sval)
+ {
+ case SV_CORPSE_CORPSE:
+ {
+ bool_ no_meat = FALSE;
+
+ /* Not all is edible. Apologies if messy. */
+
+ /* Check weight -- they have to have some meat left */
+ if (r_ptr->flags9 & RF9_DROP_SKELETON)
+ {
+ if (o_ptr->weight <= (r_ptr->weight * 3) / 5)
+ {
+ no_meat = TRUE;
+ }
+ }
+
+ /* Non-skeletons are naturally have more allowances */
+ else
+ {
+ if (o_ptr->weight <= (r_ptr->weight * 7) / 20)
+ {
+ no_meat = TRUE;
+ }
+ }
+
+ /* Nothing left to eat */
+ if (no_meat)
+ {
+ msg_print("There is not enough meat.");
+ return;
+ }
+
+
+ /* Check freshness */
+ if (!o_ptr->timeout) msg_print("Ugh! Raw meat!");
+ else msg_print("That tastes good.");
+
+
+ /* A pound of raw meat */
+ o_ptr->pval -= 10;
+ o_ptr->weight -= 10;
+
+ /* Corpses still have meat on them */
+ destroy = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_CORPSE_HEAD:
+ {
+ msg_print("You feel rather sick.");
+
+ /* A pound of raw meat */
+ o_ptr->pval -= 10;
+ o_ptr->weight -= 10;
+
+ /* Corpses still have meat on them */
+ destroy = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_CORPSE_MEAT:
+ {
+ /* Just meat */
+ if (!o_ptr->timeout) msg_print("You quickly swallow the meat.");
+ else msg_print("That tastes good.");
+
+ ident = TRUE;
+
+ /* Those darn microorganisms */
+ if (!o_ptr->timeout && (o_ptr->weight > o_ptr->pval) &&
+ !(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) +
+ (o_ptr->weight - o_ptr->pval));
+ }
+
+ break;
+ }
+ }
+
+ corpse_effect(o_ptr, FALSE);
+
+ /* Less nutritious than food rations, but much more of it. */
+ fval = (o_ptr->timeout) ? 2000 : 2500;
+
+ /* Those darn microorganisms */
+ if (!o_ptr->timeout && (o_ptr->weight - o_ptr->pval > 10) &&
+ !(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) +
+ (o_ptr->weight - o_ptr->pval));
+ }
+
+ /* Partially cured */
+ if (o_ptr->weight > o_ptr->timeout)
+ {
+ /* Adjust the "timeout" without overflowing */
+ o_ptr->timeout = (o_ptr->timeout * ((100 * o_ptr->timeout) / o_ptr->weight)) / 100;
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* We have tried it */
+ object_tried(o_ptr);
+
+ /* The player is now aware of the object */
+ if (ident && !object_aware_p(o_ptr))
+ {
+ object_aware(o_ptr);
+ gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ if (!fval) fval = o_ptr->pval;
+
+ /* Food can feed the player, in a different ways */
+
+ /* Vampires */
+ if ((PRACE_FLAG(PR1_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire")))
+ {
+ /* Reduced nutritional benefit */
+ /* (void)set_food(p_ptr->food + (fval / 10)); -- No more */
+ msg_print("Mere victuals hold scant sustenance for a being such as yourself.");
+
+ /* Hungry */
+ if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ msg_print("Your hunger can only be satisfied with fresh blood!");
+ }
+ }
+
+ else if (PRACE_FLAG(PR1_NO_FOOD))
+ {
+ if (PRACE_FLAG(PR1_UNDEAD))
+ {
+ msg_print("The food of mortals is poor sustenance for you.");
+ }
+ else
+ {
+ msg_print("Food is poor sustenance for you.");
+ }
+ set_food(p_ptr->food + ((fval) / 40));
+ }
+
+ /* Those living in fresh */
+ else
+ {
+ (void)set_food(p_ptr->food + fval);
+ }
+
+
+ /* Destroy food? */
+ if (destroy)
+ {
+ inc_stack_size(item, -1);
+ }
+}
+
+
+/*
+ * Cut a corpse up for convenient storage
+ */
+void do_cmd_cut_corpse(void)
+{
+ int item, meat = 0, not_meat = 0;
+
+ object_type *o_ptr;
+
+ object_type *i_ptr;
+
+ object_type object_type_body;
+
+ monster_race *r_ptr;
+
+ cptr q, s;
+
+
+ /* Restrict choices to corpses */
+ item_tester_tval = TV_CORPSE;
+
+ /* Get an item */
+ q = "Hack up which corpse? ";
+ s = "You have no corpses.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ r_ptr = &r_info[o_ptr->pval2];
+
+ if ((o_ptr->sval != SV_CORPSE_CORPSE) && (o_ptr->sval != SV_CORPSE_HEAD))
+ {
+ msg_print ("You cannot split that.");
+ return;
+ }
+
+ switch (o_ptr->sval)
+ {
+ case SV_CORPSE_CORPSE:
+ {
+ if (r_ptr->flags9 & RF9_DROP_SKELETON)
+ {
+ not_meat = (r_ptr->weight * 3) / 5;
+ }
+ else
+ {
+ not_meat = (r_ptr->weight * 7) / 20;
+ }
+ meat = r_ptr->weight + r_ptr->weight / 10 - not_meat;
+
+ break;
+ }
+
+ case SV_CORPSE_HEAD:
+ {
+ not_meat = r_ptr->weight / 150;
+ meat = r_ptr->weight / 30 + r_ptr->weight / 300 - not_meat;
+
+ break;
+ }
+ }
+
+ if ((o_ptr->weight <= not_meat) || (meat < 10))
+ {
+ msg_print("There is not enough meat.");
+ return;
+ }
+
+ /* Hacking 10 pounds off */
+ if (meat > 100) meat = 100;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ o_ptr->pval -= meat;
+ o_ptr->weight -= meat;
+
+ msg_print("You hack some meat off the corpse.");
+
+ corpse_effect(o_ptr, TRUE);
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Make some meat */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_MEAT));
+
+ i_ptr->number = meat / 10;
+ i_ptr->pval2 = o_ptr->pval2;
+
+ /* Length of time before decay */
+ i_ptr->pval = 1000 + rand_int(1000);
+
+ if (inven_carry_okay(i_ptr))
+ {
+ inven_carry(i_ptr, TRUE);
+ }
+ else
+ {
+ drop_near(i_ptr, 0, p_ptr->py, p_ptr->px);
+ }
+}
+
+
+/*
+ * Use a potion to cure some meat
+ *
+ * Salt water works well.
+ */
+void do_cmd_cure_meat(void)
+{
+ int item, num, cure;
+
+ object_type *o_ptr;
+
+ object_type *i_ptr;
+
+ cptr q, s;
+
+
+ /* Restrict choices to corpses */
+ item_tester_tval = TV_CORPSE;
+ item_tester_hook = item_tester_hook_eatable;
+
+ /* Get some meat */
+ q = "Cure which meat? ";
+ s = "You have no meat to cure.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Restrict choices to potions */
+ item_tester_tval = TV_POTION;
+
+ /* Get a potion */
+ q = "Use which potion? ";
+ s = "You have no potions to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ i_ptr = get_object(item);
+
+ if (i_ptr->number > 1)
+ {
+ /* Get a number */
+ get_count(1, i_ptr->number);
+
+ /* Save it */
+ num = command_arg;
+ }
+ else
+ {
+ num = 1;
+ }
+
+ if (num == 0) return;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ q = "You soak the meat.";
+ s = "You soak the meat.";
+
+ switch (i_ptr->sval)
+ {
+ case SV_POTION_SALT_WATER:
+ {
+ q = "You salt the meat.";
+ cure = 200 * num;
+
+ break;
+ }
+
+ case SV_POTION_POISON:
+ {
+ q = "You poison the meat.";
+ cure = 0;
+ o_ptr->pval /= 2;
+ if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight;
+
+ break;
+ }
+
+ case SV_POTION_CONFUSION:
+ {
+ cure = 80 * num;
+
+ break;
+ }
+
+ case SV_POTION_SLOW_POISON:
+ {
+ cure = 20 * num;
+
+ break;
+ }
+
+ case SV_POTION_CURE_POISON:
+ {
+ cure = 45 * num;
+
+ break;
+ }
+
+ case SV_POTION_DEATH:
+ {
+ q = "You ruin the meat.";
+ cure = 0;
+ o_ptr->pval /= 10;
+ if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight / 2;
+
+ break;
+ }
+
+ default:
+ {
+ cure = 0;
+
+ break;
+ }
+ }
+
+ /* Message */
+ if (object_known_p(i_ptr)) msg_print(q);
+ else msg_print(s);
+
+ /* The meat is already spoiling */
+ if (((o_ptr->sval == SV_CORPSE_MEAT) && (o_ptr->weight > o_ptr->pval)) ||
+ (o_ptr->weight - o_ptr->pval > 10))
+ {
+ cure = (cure * o_ptr->pval) / (o_ptr->weight * 20);
+ }
+
+ /* Cure the meat */
+ o_ptr->timeout += cure / o_ptr->number;
+
+ if (o_ptr->timeout > o_ptr->pval) o_ptr->timeout = o_ptr->pval;
+
+ /* Use up the potions */
+ inc_stack_size(item, -num);
+}
+
+
+/*
+ * Hook to determine if an object is quaffable
+ */
+static bool_ item_tester_hook_quaffable(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_POTION) || (o_ptr->tval == TV_POTION2)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+static bool_ quaff_potion(int tval, int sval, int pval, int pval2)
+{
+ int ident = FALSE;
+
+
+ /* "Traditional" potions */
+ if (tval == TV_POTION)
+ {
+ switch (sval)
+ {
+ case SV_POTION_WATER:
+ case SV_POTION_APPLE_JUICE:
+ case SV_POTION_SLIME_MOLD:
+ {
+ msg_print("You feel less thirsty.");
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_SLOWNESS:
+ {
+ if (set_slow(p_ptr->slow + randint(25) + 15)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_SALT_WATER:
+ {
+ msg_print("The potion makes you vomit!");
+ (void)set_food(PY_FOOD_STARVE - 1);
+ (void)set_poisoned(0);
+ (void)set_paralyzed(p_ptr->paralyzed + 4);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_POISON:
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + rand_int(15) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_POTION_BLINDNESS:
+ {
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + rand_int(100) + 100))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ /* Booze */
+ case SV_POTION_CONFUSION:
+ {
+ if (!((p_ptr->resist_conf) || (p_ptr->resist_chaos)))
+ {
+ if (set_confused(p_ptr->confused + rand_int(20) + 15))
+ {
+ ident = TRUE;
+ }
+ if (randint(2) == 1)
+ {
+ if (set_image(p_ptr->image + rand_int(150) + 150))
+ {
+ ident = TRUE;
+ }
+ }
+ if (randint(13) == 1)
+ {
+ ident = TRUE;
+ if (randint(3) == 1) lose_all_info();
+ else wiz_dark();
+ teleport_player(100);
+ wiz_dark();
+ msg_print("You wake up elsewhere with a sore head...");
+ msg_print("You can't remember a thing, or how you got here!");
+ }
+ }
+
+ break;
+ }
+
+ case SV_POTION_SLEEP:
+ {
+ if (!p_ptr->free_act)
+ {
+ if (set_paralyzed(p_ptr->paralyzed + rand_int(4) + 4))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_POTION_LOSE_MEMORIES:
+ {
+ if (!p_ptr->hold_life && (p_ptr->exp > 0))
+ {
+ msg_print("You feel your memories fade.");
+ lose_exp(p_ptr->exp / 4);
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_RUINATION:
+ {
+ msg_print("Your nerves and muscles feel weak and lifeless!");
+ take_hit(damroll(10, 10), "a potion of Ruination");
+ (void)dec_stat(A_DEX, 25, TRUE);
+ (void)dec_stat(A_WIS, 25, TRUE);
+ (void)dec_stat(A_CON, 25, TRUE);
+ (void)dec_stat(A_STR, 25, TRUE);
+ (void)dec_stat(A_CHR, 25, TRUE);
+ (void)dec_stat(A_INT, 25, TRUE);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_STR:
+ {
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_INT:
+ {
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_WIS:
+ {
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_DEX:
+ {
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_CON:
+ {
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_CHR:
+ {
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DETONATIONS:
+ {
+ msg_print("Massive explosions rupture your body!");
+ take_hit(damroll(50, 20), "a potion of Detonation");
+ (void)set_stun(p_ptr->stun + 75);
+ (void)set_cut(p_ptr->cut + 5000);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEATH:
+ {
+ msg_print("A feeling of Death flows through your body.");
+ take_hit(5000, "a potion of Death");
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INFRAVISION:
+ {
+ if (set_tim_infra(p_ptr->tim_infra + 100 + randint(100)))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_DETECT_INVIS:
+ {
+ if (set_tim_invis(p_ptr->tim_invis + 12 + randint(12)))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_SLOW_POISON:
+ {
+ if (set_poisoned(p_ptr->poisoned / 2)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURE_POISON:
+ {
+ if (set_poisoned(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_BOLDNESS:
+ {
+ if (set_afraid(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_SPEED:
+ {
+ if (!p_ptr->fast)
+ {
+ if (set_fast(randint(25) + 15, 10)) ident = TRUE;
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ break;
+ }
+
+ case SV_POTION_RESIST_HEAT:
+ {
+ if (set_oppose_fire(p_ptr->oppose_fire + randint(10) + 10))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_RESIST_COLD:
+ {
+ if (set_oppose_cold(p_ptr->oppose_cold + randint(10) + 10))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_HEROISM:
+ {
+ if (set_afraid(0)) ident = TRUE;
+ if (set_hero(p_ptr->hero + randint(25) + 25)) ident = TRUE;
+ if (hp_player(10)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_BESERK_STRENGTH:
+ {
+ if (set_afraid(0)) ident = TRUE;
+ if (set_shero(p_ptr->shero + randint(25) + 25)) ident = TRUE;
+ if (hp_player(30)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURE_LIGHT:
+ {
+ if (hp_player(damroll(2, 8))) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_cut(p_ptr->cut - 10)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURE_SERIOUS:
+ {
+ if (hp_player(damroll(4, 8))) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_cut((p_ptr->cut / 2) - 50)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURE_CRITICAL:
+ {
+ if (hp_player(damroll(6, 8))) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_HEALING:
+ {
+ if (hp_player(300)) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_STAR_HEALING:
+ {
+ if (hp_player(1200)) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_LIFE:
+ {
+ msg_print("You feel life flow through your body!");
+ restore_level();
+ hp_player(5000);
+ (void)set_poisoned(0);
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ (void)do_res_stat(A_STR, TRUE);
+ (void)do_res_stat(A_CON, TRUE);
+ (void)do_res_stat(A_DEX, TRUE);
+ (void)do_res_stat(A_WIS, TRUE);
+ (void)do_res_stat(A_INT, TRUE);
+ (void)do_res_stat(A_CHR, TRUE);
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ }
+ p_ptr->black_breath = FALSE;
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RESTORE_MANA:
+ {
+ if (p_ptr->csp < p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+ msg_print("Your feel your head clear.");
+ p_ptr->redraw |= (PR_MANA);
+ p_ptr->window |= (PW_PLAYER);
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_RESTORE_EXP:
+ {
+ if (restore_level()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_STR:
+ {
+ if (do_res_stat(A_STR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_INT:
+ {
+ if (do_res_stat(A_INT, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_WIS:
+ {
+ if (do_res_stat(A_WIS, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_DEX:
+ {
+ if (do_res_stat(A_DEX, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_CON:
+ {
+ if (do_res_stat(A_CON, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_CHR:
+ {
+ if (do_res_stat(A_CHR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_STR:
+ {
+ if (do_inc_stat(A_STR)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_INT:
+ {
+ if (do_inc_stat(A_INT)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_WIS:
+ {
+ if (do_inc_stat(A_WIS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_DEX:
+ {
+ if (do_inc_stat(A_DEX)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_CON:
+ {
+ if (do_inc_stat(A_CON)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_CHR:
+ {
+ if (do_inc_stat(A_CHR)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_AUGMENTATION:
+ {
+ if (do_inc_stat(A_STR)) ident = TRUE;
+ if (do_inc_stat(A_INT)) ident = TRUE;
+ if (do_inc_stat(A_WIS)) ident = TRUE;
+ if (do_inc_stat(A_DEX)) ident = TRUE;
+ if (do_inc_stat(A_CON)) ident = TRUE;
+ if (do_inc_stat(A_CHR)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_ENLIGHTENMENT:
+ {
+ msg_print("An image of your surroundings forms in your mind...");
+ wiz_lite();
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_STAR_ENLIGHTENMENT:
+ {
+ msg_print("You begin to feel more enlightened...");
+ msg_print(NULL);
+ wiz_lite_extra();
+ (void)do_inc_stat(A_INT);
+ (void)do_inc_stat(A_WIS);
+ (void)detect_traps(DEFAULT_RADIUS);
+ (void)detect_doors(DEFAULT_RADIUS);
+ (void)detect_stairs(DEFAULT_RADIUS);
+ (void)detect_treasure(DEFAULT_RADIUS);
+ (void)detect_objects_gold(DEFAULT_RADIUS);
+ (void)detect_objects_normal(DEFAULT_RADIUS);
+ identify_pack();
+ self_knowledge(NULL);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_SELF_KNOWLEDGE:
+ {
+ msg_print("You begin to know yourself a little better...");
+ msg_print(NULL);
+ self_knowledge(NULL);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_EXPERIENCE:
+ {
+ if (p_ptr->exp < PY_MAX_EXP)
+ {
+ s32b ee = (p_ptr->exp / 2) + 10;
+ if (ee > 100000L) ee = 100000L;
+ msg_print("You feel more experienced.");
+ gain_exp(ee);
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_RESISTANCE:
+ {
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURING:
+ {
+ if (hp_player(50)) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+ if (set_image(0)) ident = TRUE;
+ if (heal_insanity(50)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INVULNERABILITY:
+ {
+ (void)set_invuln(p_ptr->invuln + randint(7) + 7);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_NEW_LIFE:
+ {
+ do_cmd_rerate();
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_BLOOD:
+ {
+ msg_print("You feel the blood of life running through your veins!");
+ ident = TRUE;
+ p_ptr->allow_one_death++;
+
+ break;
+ }
+
+ case SV_POTION_MUTATION:
+ {
+ msg_print("You feel the dark corruptions of Morgoth coming over you!");
+ gain_random_corruption(0);
+ ident = TRUE;
+ break;
+ }
+
+ case SV_POTION_INVIS:
+ {
+ int t = 30 + randint(30);
+
+ if (set_invis(p_ptr->tim_invis + t, 35))
+ {
+ ident = TRUE;
+ }
+ set_tim_invis(p_ptr->tim_invis + t);
+
+ break;
+ }
+
+ case SV_POTION_LEARNING:
+ {
+ p_ptr->skill_points += rand_range(4, 10 + luck( -4, 4));
+ cmsg_format(TERM_L_GREEN, "You can increase %d more skills.", p_ptr->skill_points);
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ /* "Duplicate" potions */
+ else
+ {
+ switch (sval)
+ {
+ case SV_POTION2_MIMIC:
+ {
+ if (!p_ptr->mimic_form)
+ {
+ s32b time;
+
+ call_lua("get_mimic_rand_dur", "(d)", "d", pval2, &time);
+
+ set_mimic(time, pval2, (p_ptr->lev * 2) / 3);
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION2_CURE_LIGHT_SANITY:
+ {
+ if (heal_insanity(damroll(4, 8))) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION2_CURE_SERIOUS_SANITY:
+ {
+ if (heal_insanity(damroll(8, 8))) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION2_CURE_CRITICAL_SANITY:
+ {
+ if (heal_insanity(damroll(12, 8))) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION2_CURE_SANITY:
+ {
+ if (heal_insanity(damroll(10, 100))) ident = TRUE;
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ return (ident);
+}
+
+
+/*
+ * Quaff a potion (from the pack or the floor)
+ */
+void do_cmd_quaff_potion(void)
+{
+ int item, ident, lev;
+
+ object_type *o_ptr;
+
+ object_type *q_ptr, forge;
+
+ cptr q, s;
+
+
+ /* Restrict choices to potions */
+ item_tester_hook = item_tester_hook_quaffable;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Potion full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Quaff which potion? ";
+ s = "You have no potions to quaff.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+
+ /* Sound */
+ sound(SOUND_QUAFF);
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Object level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Analyze the potion */
+ if (process_hooks_ret(HOOK_QUAFF, "d", "(O)", o_ptr))
+ {
+ ident = process_hooks_return[0].num;
+ }
+ else
+ {
+ ident = quaff_potion(o_ptr->tval, o_ptr->sval, o_ptr->pval, o_ptr->pval2);
+ }
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* The item has been tried */
+ object_tried(o_ptr);
+
+ /* An identification was made */
+ if (ident && !object_aware_p(o_ptr))
+ {
+ object_aware(o_ptr);
+ gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
+ }
+
+ if (get_skill(SKILL_ALCHEMY))
+ {
+ if (item >= 0)
+ {
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1));
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ q_ptr->ident |= IDENT_STOREB;
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Potions can feed the player */
+ (void)set_food(p_ptr->food + o_ptr->pval);
+
+
+ /* Destroy potion */
+ inc_stack_size(item, -1);
+}
+
+
+/*
+ * Drink from a fountain
+ */
+void do_cmd_drink_fountain(void)
+{
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ bool_ ident;
+
+ int tval, sval, pval = 0;
+
+ int i;
+
+ char ch;
+
+
+ /* Is the fountain empty? */
+ if (c_ptr->special2 <= 0)
+ {
+ msg_print("The fountain is dried out.");
+ return;
+ }
+
+ /* We quaff or we fill ? */
+ if (!get_com("Do you want to [Q]uaff or [F]ill from the fountain? ", &ch))
+ {
+ return;
+ }
+
+ if ((ch == 'F') || (ch == 'f'))
+ {
+ do_cmd_fill_bottle();
+
+ return;
+ }
+
+ else if ((ch == 'Q') || (ch == 'q'))
+ {
+ if (c_ptr->special <= SV_POTION_LAST)
+ {
+ tval = TV_POTION;
+ sval = c_ptr->special;
+ }
+ else
+ {
+ tval = TV_POTION2;
+ sval = c_ptr->special - SV_POTION_LAST;
+ }
+
+ for (i = 0; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ if (k_ptr->tval != tval) continue;
+ if (k_ptr->sval != sval) continue;
+
+ pval = k_ptr->pval;
+
+ break;
+ }
+
+ ident = quaff_potion(tval, sval, pval, 0);
+
+ c_ptr->special2--;
+
+ if (c_ptr->special2 <= 0)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN);
+ }
+
+ if (ident) c_ptr->info |= CAVE_IDNT;
+ }
+}
+
+
+/*
+ * Fill an empty bottle
+ */
+void do_cmd_fill_bottle(void)
+{
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ int tval, sval, item, amt = 1;
+
+ object_type *q_ptr, *o_ptr, forge;
+
+ cptr q, s;
+
+ /* Is the fountain empty? */
+ /*
+ * This check is redundant as it is done in do_cmd_drink_fountain()
+ * but I keep this because someone might want to call this directly.
+ * -- Kusunose
+ */
+ if (c_ptr->special2 <= 0)
+ {
+ msg_print("The fountain has dried up.");
+ return;
+ }
+
+ /* Determine the tval/sval of the potion */
+ if (c_ptr->special <= SV_POTION_LAST)
+ {
+ tval = TV_POTION;
+ sval = c_ptr->special;
+ }
+ else
+ {
+ tval = TV_POTION2;
+ sval = c_ptr->special - SV_POTION_LAST;
+ }
+
+ /* Restrict choices to bottles */
+ item_tester_tval = TV_BOTTLE;
+
+ /* Get an item */
+ q = "Fill which bottle? ";
+ s = "You have no bottles to fill.";
+ if (!get_item(&item, q, s, (USE_INVEN))) return;
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Find out how many the player wants */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ if (amt > c_ptr->special2) amt = c_ptr->special2;
+
+ /* Destroy bottles */
+ inc_stack_size(item, -amt);
+
+ /* Create the potion */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(tval, sval));
+ q_ptr->number = amt;
+
+ if (c_ptr->info & CAVE_IDNT)
+ {
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ }
+
+ inven_carry(q_ptr, TRUE);
+
+ c_ptr->special2 -= amt;
+
+ if (c_ptr->special2 <= 0)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN);
+ }
+
+ return;
+}
+
+
+/*
+ * Curse the players armor
+ */
+bool_ curse_armor(void)
+{
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Curse the body armor */
+ o_ptr = &p_ptr->inventory[INVEN_BODY];
+
+ /* Nothing to curse */
+ if (!o_ptr->k_idx) return (FALSE);
+
+
+ /* Describe */
+ object_desc(o_name, o_ptr, FALSE, 3);
+
+ /* Attempt a saving throw for artifacts */
+ if (((o_ptr->art_name) || artifact_p(o_ptr)) && (rand_int(100) < 50))
+ {
+ /* Cool */
+ msg_format("A terrible black aura tries to surround your armour, "
+ "but your %s resists the effects!", o_name);
+ }
+
+ /* not artifact or failed save... */
+ else
+ {
+ /* Oops */
+ msg_format("A terrible black aura blasts your %s!", o_name);
+
+ /* Blast the armor */
+ o_ptr->name1 = 0;
+ o_ptr->name2 = EGO_BLASTED;
+ o_ptr->to_a = 0 - randint(5) - randint(5);
+ o_ptr->to_h = 0;
+ o_ptr->to_d = 0;
+ o_ptr->ac = 0;
+ o_ptr->dd = 0;
+ o_ptr->ds = 0;
+ o_ptr->art_flags1 = 0;
+ o_ptr->art_flags2 = 0;
+ o_ptr->art_flags3 = 0;
+ o_ptr->art_flags4 = 0;
+
+ /* Curse it */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Curse the players weapon
+ */
+bool_ curse_weapon(void)
+{
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Curse the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Nothing to curse */
+ if (!o_ptr->k_idx) return (FALSE);
+
+
+ /* Describe */
+ object_desc(o_name, o_ptr, FALSE, 3);
+
+ /* Attempt a saving throw */
+ if ((artifact_p(o_ptr) || o_ptr->art_name) && (rand_int(100) < 50))
+ {
+ /* Cool */
+ msg_format("A terrible black aura tries to surround your weapon, "
+ "but your %s resists the effects!", o_name);
+ }
+
+ /* not artifact or failed save... */
+ else
+ {
+ /* Oops */
+ msg_format("A terrible black aura blasts your %s!", o_name);
+
+ /* Shatter the weapon */
+ o_ptr->name1 = 0;
+ o_ptr->name2 = EGO_SHATTERED;
+ o_ptr->to_h = 0 - randint(5) - randint(5);
+ o_ptr->to_d = 0 - randint(5) - randint(5);
+ o_ptr->to_a = 0;
+ o_ptr->ac = 0;
+ o_ptr->dd = 0;
+ o_ptr->ds = 0;
+ o_ptr->art_flags1 = 0;
+ o_ptr->art_flags2 = 0;
+ o_ptr->art_flags3 = 0;
+ o_ptr->art_flags4 = 0;
+
+
+ /* Curse it */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+
+ /* Notice */
+ return (TRUE);
+}
+
+
+/*
+ * Hook to determine if an object is readable
+ */
+static bool_ item_tester_hook_readable(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_SCROLL) || (o_ptr->tval == TV_PARCHMENT)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Read a scroll (from the pack or floor).
+ *
+ * Certain scrolls can be "aborted" without losing the scroll. These
+ * include scrolls with no effects but recharge or identify, which are
+ * cancelled before use. XXX Reading them still takes a turn, though.
+ */
+void do_cmd_read_scroll(void)
+{
+ int item, k, used_up, ident, lev;
+
+ object_type *o_ptr;
+
+ object_type *q_ptr, forge;
+
+ cptr q, s;
+
+
+ /* Check some conditions */
+ if (p_ptr->blind)
+ {
+ msg_print("You can't see anything.");
+ return;
+ }
+
+ if (no_lite())
+ {
+ msg_print("You have no light by which to read.");
+ return;
+ }
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+
+ /* Restrict choices to scrolls */
+ item_tester_hook = item_tester_hook_readable;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Scroll full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Read which scroll? ";
+ s = "You have no scrolls to read.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Object level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Assume the scroll will get used up */
+ used_up = TRUE;
+
+ /* New scripts, can override the ingame code */
+ if (process_hooks_ret(HOOK_READ, "dd", "(O)", o_ptr))
+ {
+ used_up = process_hooks_return[0].num;
+ ident = process_hooks_return[1].num;
+ }
+ /* Traditional scrolls */
+ else if (o_ptr->tval == TV_SCROLL)
+ {
+ /* Analyze the scroll */
+ switch (o_ptr->sval)
+ {
+ case SV_SCROLL_MASS_RESURECTION:
+ {
+ int k;
+
+ ident = TRUE;
+ msg_print("You feel the souls of the dead coming back "
+ "from the Halls of Mandos.");
+
+ for (k = 0; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ if (r_ptr->flags1 & RF1_UNIQUE &&
+ !(r_ptr->flags9 & RF9_SPECIAL_GENE))
+ {
+ r_ptr->max_num = 1;
+ }
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_DEINCARNATION:
+ {
+ if (!get_check("Do you really want to leave your body? "
+ "(beware, it'll be destroyed!) "))
+ {
+ used_up = FALSE;
+ break;
+ }
+
+ do_cmd_leave_body(FALSE);
+
+ ident = TRUE;
+ used_up = TRUE;
+
+ break;
+ }
+
+ /* original didn't set used_up flag ??? -- pelpel */
+ case SV_SCROLL_RESET_RECALL:
+ {
+ if (!reset_recall(TRUE))
+ {
+ used_up = FALSE;
+ break;
+ }
+
+ msg_format("Recall reset to %s at level %d.",
+ d_info[p_ptr->recall_dungeon].name + d_name,
+ max_dlv[p_ptr->recall_dungeon]);
+
+ ident = TRUE;
+ used_up = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DIVINATION:
+ {
+ int i, count = 0;
+ char buf[120];
+
+ while (count < 1000)
+ {
+ count++;
+ i = rand_int(MAX_FATES);
+ if (!fates[i].fate) continue;
+ if (fates[i].know) continue;
+
+ msg_print("A message appears on the scroll. It says:");
+ msg_print(NULL);
+
+ fate_desc(buf, i);
+ msg_format("%s", buf);
+
+ msg_print(NULL);
+ msg_print("The scroll disappears in a puff of smoke!");
+
+ fates[i].know = TRUE;
+ ident = TRUE;
+
+ break;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_DARKNESS:
+ {
+ if (!(p_ptr->resist_blind) && !(p_ptr->resist_dark))
+ {
+ (void)set_blind(p_ptr->blind + 3 + randint(5));
+ }
+ if (unlite_area(10, 3)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_AGGRAVATE_MONSTER:
+ {
+ msg_print("There is a high-pitched humming noise.");
+ aggravate_monsters(1);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_CURSE_ARMOR:
+ {
+ if (curse_armor()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_CURSE_WEAPON:
+ {
+ if (curse_weapon()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_SUMMON_MONSTER:
+ {
+ for (k = 0; k < randint(3); k++)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, dun_level, 0))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_SUMMON_MINE:
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_SUMMON_UNDEAD:
+ {
+ for (k = 0; k < randint(3); k++)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_TRAP_CREATION:
+ {
+ if (trap_creation()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_PHASE_DOOR:
+ {
+ teleport_player(10);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_TELEPORT:
+ {
+ teleport_player(100);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_TELEPORT_LEVEL:
+ {
+ (void)teleport_player_level();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_WORD_OF_RECALL:
+ {
+ if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? "))
+ {
+ used_up = FALSE;
+ }
+ else
+ {
+ recall_player(21, 15);
+
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_IDENTIFY:
+ {
+ ident = TRUE;
+
+ if (!ident_spell()) used_up = FALSE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_IDENTIFY:
+ {
+ ident = TRUE;
+
+ if (!identify_fully()) used_up = FALSE;
+
+ break;
+ }
+
+ case SV_SCROLL_REMOVE_CURSE:
+ {
+ if (remove_curse())
+ {
+ msg_print("You feel as if someone is watching over you.");
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_REMOVE_CURSE:
+ {
+ remove_all_curse();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ENCHANT_ARMOR:
+ {
+ ident = TRUE;
+
+ if (!enchant_spell(0, 0, 1, 0)) used_up = FALSE;
+
+ break;
+ }
+
+ case SV_SCROLL_ENCHANT_WEAPON_TO_HIT:
+ {
+ if (!enchant_spell(1, 0, 0, 0)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ENCHANT_WEAPON_TO_DAM:
+ {
+ if (!enchant_spell(0, 1, 0, 0)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ENCHANT_WEAPON_PVAL:
+ {
+ if (!enchant_spell(0, 0, 0, 1)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_ENCHANT_ARMOR:
+ {
+ if (!enchant_spell(0, 0, randint(3) + 2, 0)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_ENCHANT_WEAPON:
+ {
+ if (!enchant_spell(randint(3), randint(3), 0, 0)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_RECHARGING:
+ {
+ if (!recharge(60)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_LIGHT:
+ {
+ if (lite_area(damroll(2, 8), 2)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_MAPPING:
+ {
+ map_area();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_GOLD:
+ {
+ if (detect_treasure(DEFAULT_RADIUS)) ident = TRUE;
+ if (detect_objects_gold(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_ITEM:
+ {
+ if (detect_objects_normal(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_TRAP:
+ {
+ if (detect_traps(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_DOOR:
+ {
+ if (detect_doors(DEFAULT_RADIUS)) ident = TRUE;
+ if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_INVIS:
+ {
+ if (detect_monsters_invis(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_SATISFY_HUNGER:
+ {
+ if (set_food(PY_FOOD_MAX - 1)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_BLESSING:
+ {
+ if (set_blessed(p_ptr->blessed + randint(12) + 6)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_HOLY_CHANT:
+ {
+ if (set_blessed(p_ptr->blessed + randint(24) + 12)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_HOLY_PRAYER:
+ {
+ if (set_blessed(p_ptr->blessed + randint(48) + 24)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_MONSTER_CONFUSION:
+ {
+ if (p_ptr->confusing == 0)
+ {
+ msg_print("Your hands begin to glow.");
+ p_ptr->confusing = TRUE;
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_PROTECTION_FROM_EVIL:
+ {
+ k = 3 * p_ptr->lev;
+ if (set_protevil(p_ptr->protevil + randint(25) + k)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_RUNE_OF_PROTECTION:
+ {
+ warding_glyph();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_TRAP_DOOR_DESTRUCTION:
+ {
+ if (destroy_doors_touch()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_DESTRUCTION:
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ {
+ destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE);
+ }
+ else
+ {
+ msg_print("The dungeon trembles...");
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DISPEL_UNDEAD:
+ {
+ if (dispel_undead(60)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_GENOCIDE:
+ {
+ (void)genocide(TRUE);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_MASS_GENOCIDE:
+ {
+ (void)mass_genocide(TRUE);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ACQUIREMENT:
+ {
+ acquirement(p_ptr->py, p_ptr->px, 1, TRUE, FALSE);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_ACQUIREMENT:
+ {
+ acquirement(p_ptr->py, p_ptr->px, randint(2) + 1, TRUE, FALSE);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ /* ZAngband scrolls */
+ case SV_SCROLL_FIRE:
+ {
+ fire_ball(GF_FIRE, 0, 150, 4);
+
+ /*
+ * Note: "Double" damage since it is centered on
+ * the player ...
+ */
+ if (!p_ptr->oppose_fire && !p_ptr->resist_fire &&
+ !p_ptr->immune_fire)
+ {
+ take_hit(50 + randint(50) + (p_ptr->sensible_fire) ? 20 : 0,
+ "a Scroll of Fire");
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+
+ case SV_SCROLL_ICE:
+ {
+ fire_ball(GF_ICE, 0, 175, 4);
+
+ if (!p_ptr->oppose_cold && !p_ptr->resist_cold &&
+ !p_ptr->immune_cold)
+ {
+ take_hit(100 + randint(100), "a Scroll of Ice");
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_CHAOS:
+ {
+ fire_ball(GF_CHAOS, 0, 222, 4);
+
+ if (!p_ptr->resist_chaos)
+ {
+ take_hit(111 + randint(111), "a Scroll of Chaos");
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_RUMOR:
+ {
+ char rumour[80];
+
+ msg_print("There is message on the scroll. It says:");
+ msg_print(NULL);
+
+ /* Pick random text */
+ switch (randint(20))
+ {
+ case 1:
+ {
+ get_rnd_line("chainswd.txt", rumour);
+
+ break;
+ }
+
+ case 2:
+ {
+ get_rnd_line("error.txt", rumour);
+
+ break;
+ }
+
+ case 3:
+ case 4:
+ case 5:
+ {
+ get_rnd_line("death.txt", rumour);
+
+ break;
+ }
+
+ default:
+ {
+ get_rnd_line("rumors.txt", rumour);
+
+ break;
+ }
+ }
+
+ msg_format("%s", rumour);
+ msg_print(NULL);
+
+ msg_print("The scroll disappears in a puff of smoke!");
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ARTIFACT:
+ {
+ ident = TRUE;
+
+ if (!artifact_scroll()) used_up = FALSE;
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ /* Other readable items */
+ else
+ {
+ /* Maps */
+ if (o_ptr->sval >= 200)
+ {
+ int i, n;
+ char buf[80], fil[20];
+
+ strnfmt(fil, 20, "book-%d.txt", o_ptr->sval);
+
+ n = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, -1));
+
+ /* Parse all the fields */
+ for (i = 0; i < n; i += 4)
+ {
+ /* Grab the fields */
+ int x = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 0));
+ int y = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 1));
+ int w = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 2));
+ int h = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 3));
+
+ reveal_wilderness_around_player(y, x, h, w);
+ }
+ }
+
+ /* Normal parchements */
+ else
+ {
+ /* Save screen */
+ screen_save();
+
+ /* Get the filename */
+ q = format("book-%d.txt", o_ptr->sval);
+
+ /* Peruse the help file */
+ (void)show_file(q, NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+
+ if (o_ptr->sval >= 100)
+ {
+ inscription_info[o_ptr->sval - 100].know = TRUE;
+ }
+
+ used_up = FALSE;
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* The item was tried */
+ object_tried(o_ptr);
+
+ /* An identification was made */
+ if (ident && !object_aware_p(o_ptr))
+ {
+ object_aware(o_ptr);
+ gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Hack -- allow certain scrolls to be "preserved" */
+ if (!used_up) return;
+
+ sound(SOUND_SCROLL);
+
+ /* Destroy scroll */
+ inc_stack_size(item, -1);
+
+ if (get_skill(SKILL_ALCHEMY))
+ {
+ if (item >= 0)
+ {
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_SCROLL, SV_SCROLL_NOTHING));
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ q_ptr->ident |= IDENT_STOREB;
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+ }
+}
+
+
+
+/* Set the 'stick mode' on */
+void set_stick_mode(object_type *o_ptr)
+{
+ s32b bonus = o_ptr->pval3 & 0xFFFF;
+ s32b max = o_ptr->pval3 >> 16;
+
+ exec_lua(format("get_level_use_stick = %d; get_level_max_stick = %d", bonus, max));
+}
+/* Remove 'stick mode' */
+void unset_stick_mode()
+{
+ exec_lua("get_level_use_stick = -1; get_level_max_stick = -1");
+}
+
+
+/*
+ * Use a staff. -RAK-
+ *
+ * One charge of one staff disappears.
+ *
+ * Hack -- staffs of identify can be "cancelled".
+ */
+void do_cmd_use_staff(void)
+{
+ int item, ident, chance;
+
+ s32b obvious, use_charge;
+
+ object_type *o_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr q, s;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* Restrict choices to wands */
+ item_tester_tval = TV_STAFF;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Staff full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Use which staff? ";
+ s = "You have no staff to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Mega-Hack -- refuse to use a pile from the ground */
+ if ((item < 0) && (o_ptr->number > 1))
+ {
+ msg_print("You must first pick up the staffs.");
+ return;
+ }
+
+ /* Enter device mode */
+ set_stick_mode(o_ptr);
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* get the chance */
+ chance = exec_lua(format("return spell_chance(%d)", o_ptr->pval2));
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance /= 3;
+ }
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if (magik(chance))
+ {
+ if (flush_failure) flush();
+ msg_print("You failed to use the staff properly.");
+ sound(SOUND_FAIL);
+
+ /* Leave device mode */
+ unset_stick_mode();
+ return;
+ }
+
+ /* Notice empty staffs */
+ if (o_ptr->pval <= 0)
+ {
+ if (flush_failure) flush();
+ msg_print("The staff has no charges left.");
+ o_ptr->ident |= (IDENT_EMPTY);
+
+ /* Leave device mode */
+ unset_stick_mode();
+ return;
+ }
+
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+
+ /* Analyze the staff */
+ call_lua("activate_stick", "(d)", "dd", o_ptr->pval2, &obvious, &use_charge);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Tried the item */
+ object_tried(o_ptr);
+
+ /* An identification was made */
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Hack -- some uses are "free" */
+ if (!use_charge)
+ {
+ /* Leave device mode */
+ unset_stick_mode();
+
+ return;
+ }
+
+ /* An identification was made */
+ if (obvious)
+ {
+ object_aware(o_ptr);
+ }
+
+ /* Use a single charge */
+ o_ptr->pval--;
+
+ /* XXX Hack -- unstack if necessary */
+ if ((item >= 0) && (o_ptr->number > 1))
+ {
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = 1;
+
+ /* Restore the charges */
+ o_ptr->pval++;
+
+ /* Unstack the used item */
+ o_ptr->number--;
+ item = inven_carry(q_ptr, FALSE);
+
+ /* Message */
+ msg_print("You unstack your staff.");
+ }
+
+ /* Describe charges in the pack */
+ if (item >= 0)
+ {
+ inven_item_charges(item);
+ }
+
+ /* Describe charges on the floor */
+ else
+ {
+ floor_item_charges(0 - item);
+ }
+
+ /* Leave device mode */
+ unset_stick_mode();
+}
+
+
+/*
+ * Aim a wand (from the pack or floor).
+ *
+ * Use a single charge from a single item.
+ * Handle "unstacking" in a logical manner.
+ *
+ * For simplicity, you cannot use a stack of items from the
+ * ground. This would require too much nasty code.
+ *
+ * There are no wands which can "destroy" themselves, in the p_ptr->inventory
+ * or on the ground, so we can ignore this possibility. Note that this
+ * required giving "wand of wonder" the ability to ignore destruction
+ * by electric balls.
+ *
+ * All wands can be "cancelled" at the "Direction?" prompt for free.
+ *
+ * Note that the basic "bolt" wands do slightly less damage than the
+ * basic "bolt" rods, but the basic "ball" wands do the same damage
+ * as the basic "ball" rods.
+ */
+void do_cmd_aim_wand(void)
+{
+ s32b obvious, use_charge;
+
+ int item, ident, chance, sval;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* Restrict choices to wands */
+ item_tester_tval = TV_WAND;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Wand full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Aim which wand? ";
+ s = "You have no wand to aim.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+
+ /* Mega-Hack -- refuse to aim a pile from the ground */
+ if ((item < 0) && (o_ptr->number > 1))
+ {
+ msg_print("You must first pick up the wands.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Enter device mode */
+ set_stick_mode(o_ptr);
+
+ /* get the chance */
+ chance = exec_lua(format("return spell_chance(%d)", o_ptr->pval2));
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance /= 3;
+ }
+
+ /* Roll for usage */
+ if (magik(chance))
+ {
+ if (flush_failure) flush();
+ msg_print("You failed to use the wand properly.");
+ sound(SOUND_FAIL);
+
+ /* Leave device mode */
+ unset_stick_mode();
+ return;
+ }
+
+ /* The wand is already empty! */
+ if (o_ptr->pval <= 0)
+ {
+ if (flush_failure) flush();
+ msg_print("The wand has no charges left.");
+ o_ptr->ident |= (IDENT_EMPTY);
+
+ /* Leave device mode */
+ unset_stick_mode();
+ return;
+ }
+
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+
+ /* XXX Hack -- Extract the "sval" effect */
+ sval = o_ptr->sval;
+
+ /* Analyze the wand */
+ call_lua("activate_stick", "(d)", "dd", o_ptr->pval2, &obvious, &use_charge);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Mark it as tried */
+ object_tried(o_ptr);
+
+ /* Hack -- some uses are "free" */
+ if (!use_charge)
+ {
+ /* Leave device mode */
+ unset_stick_mode();
+
+ return;
+ }
+
+ /* An identification was made */
+ if (obvious)
+ {
+ object_aware(o_ptr);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Use a single charge */
+ o_ptr->pval--;
+
+ /* Describe the charges in the pack */
+ if (item >= 0)
+ {
+ inven_item_charges(item);
+ }
+
+ /* Describe the charges on the floor */
+ else
+ {
+ floor_item_charges(0 - item);
+ }
+
+ /* Leave device mode */
+ unset_stick_mode();
+}
+
+
+
+
+
+
+/*
+ * Activate (zap) a Rod
+ *
+ * Unstack fully charged rods as needed.
+ *
+ * Hack -- rods of perception/genocide can be "cancelled"
+ * All rods can be cancelled at the "Direction?" prompt
+ */
+
+
+/*
+ * Hook to determine if an object is zapable
+ */
+static bool_ item_tester_hook_zapable(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_ROD) || (o_ptr->tval == TV_ROD_MAIN)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Hook to determine if an object is attachable
+ */
+static bool_ item_tester_hook_attachable(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_ROD_MAIN) &&
+ (o_ptr->pval == SV_ROD_NOTHING)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Combine a rod and a rod tip
+ */
+void zap_combine_rod_tip(object_type *q_ptr, int tip_item)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+ s32b cost;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* Restrict choices to rods */
+ item_tester_hook = item_tester_hook_attachable;
+
+ /* Get an item */
+ q = "Attach the rod tip with which rod? ";
+ s = "You have no rod to attach to.";
+ if (!get_item(&item, q, s, (USE_INVEN))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Calculate rod tip's mana cost */
+ cost = q_ptr->pval;
+
+ if (f4 & TR4_CHEAPNESS)
+ {
+ cost /= 2;
+ }
+
+ /*
+ * The rod must have at least the same mana capacity as the
+ * rod tip spell needs
+ */
+ if (o_ptr->pval2 < cost)
+ {
+ msg_print("This rod doesn't have enough mana for the rod tip.");
+ return;
+ }
+
+ /* Attach the tip to the rod */
+ o_ptr->pval = q_ptr->sval;
+
+ /* Destroy rod tip */
+ inc_stack_size(tip_item, -1);
+}
+
+
+/*
+ * Zap a rod, or attack a rod tip to a rod
+ */
+void do_cmd_zap_rod(void)
+{
+ int item, ident, chance, dir, lev;
+
+ int cost;
+
+ bool_ require_dir;
+
+ object_type *o_ptr;
+
+ object_kind *tip_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr q, s;
+
+ /* Hack -- let perception get aborted */
+ bool_ use_charge = TRUE;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+
+ /* Restrict choices to rods */
+ item_tester_hook = item_tester_hook_zapable;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Rod full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Zap which rod? ";
+ s = "You have no rod to zap.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+
+ /* "Zapping" a Rod Tip on rod of nothing will attach it */
+ if (o_ptr->tval == TV_ROD)
+ {
+ if (item >= 0)
+ {
+ zap_combine_rod_tip(o_ptr, item);
+ return;
+ }
+ else
+ {
+ msg_print("You can't zap a rod tip that's on the floor.");
+ return;
+ }
+ }
+
+
+ /* Non-directed rods */
+ if (o_ptr->pval < SV_ROD_MIN_DIRECTION)
+ {
+ require_dir = FALSE;
+ }
+
+ /* Some rods always require direction */
+ else
+ {
+ switch (o_ptr->pval)
+ {
+ case SV_ROD_DETECT_TRAP:
+ case SV_ROD_HAVOC:
+ case SV_ROD_HOME:
+ {
+ require_dir = FALSE;
+ break;
+ }
+
+ default:
+ {
+ require_dir = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Get a direction (unless KNOWN not to need it) */
+ if (!object_aware_p(o_ptr) || require_dir)
+ {
+ /* Get a direction, allow cancel */
+ if (!get_aim_dir(&dir)) return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_FAST_CAST) energy_use /= 2;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Extract the item level */
+ tip_ptr = &k_info[lookup_kind(TV_ROD, o_ptr->pval)];
+ lev = k_info[lookup_kind(TV_ROD, o_ptr->pval)].level;
+
+ /* Base chance of success */
+ chance = p_ptr->skill_dev;
+
+ /* Confusion hurts skill */
+ if (p_ptr->confused) chance = chance / 2;
+
+ /* High level objects are harder */
+ chance = chance - ((lev > 50) ? 50 : lev);
+
+ if (chance <= 0)
+ {
+ chance = 1;
+ }
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance *= 10;
+ }
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
+ {
+ /* Flush input if necessary */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("You failed to use the rod properly.");
+
+ sound(SOUND_FAIL);
+
+ return;
+ }
+
+ /* Extract mana cost */
+ cost = tip_ptr->pval;
+
+ /* "Cheapness" ego halven the cost */
+ if (f4 & TR4_CHEAPNESS) cost = cost / 2;
+
+ /* A single rod is still charging */
+ if (o_ptr->timeout < cost)
+ {
+ /* Flush input if necessary */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("The rod does not have enough mana yet.");
+
+ return;
+ }
+
+ /* Increase the timeout by the rod kind's pval. */
+ o_ptr->timeout -= cost;
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+ /* Analyze the rod */
+ switch (o_ptr->pval)
+ {
+ case SV_ROD_HOME:
+ {
+ ident = TRUE;
+
+ do_cmd_home_trump();
+
+ break;
+ }
+
+ case SV_ROD_DETECT_TRAP:
+ {
+ if (detect_traps(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DETECT_DOOR:
+ {
+ if (detect_doors(DEFAULT_RADIUS)) ident = TRUE;
+ if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_IDENTIFY:
+ {
+ ident = TRUE;
+
+ if (!ident_spell()) use_charge = FALSE;
+
+ break;
+ }
+
+ case SV_ROD_RECALL:
+ {
+ if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? "))
+ {
+ use_charge = FALSE;
+ }
+ else
+ {
+ recall_player(21, 15);
+
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_ROD_ILLUMINATION:
+ {
+ if (lite_area(damroll(2, 8), 2)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_MAPPING:
+ {
+ map_area();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DETECTION:
+ {
+ detect_all(DEFAULT_RADIUS);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_PROBING:
+ {
+ probing();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_CURING:
+ {
+ if (set_blind(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+ if (set_image(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_HEALING:
+ {
+ if (hp_player(500)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_RESTORATION:
+ {
+ if (restore_level()) ident = TRUE;
+ if (do_res_stat(A_STR, TRUE)) ident = TRUE;
+ if (do_res_stat(A_INT, TRUE)) ident = TRUE;
+ if (do_res_stat(A_WIS, TRUE)) ident = TRUE;
+ if (do_res_stat(A_DEX, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CON, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CHR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_SPEED:
+ {
+ if (!p_ptr->fast)
+ {
+ if (set_fast(randint(30) + 15, 10)) ident = TRUE;
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ break;
+ }
+
+ case SV_ROD_TELEPORT_AWAY:
+ {
+ if (teleport_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DISARMING:
+ {
+ if (disarm_trap(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_LITE:
+ {
+ msg_print("A line of blue shimmering light appears.");
+ lite_line(dir);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_SLEEP_MONSTER:
+ {
+ if (sleep_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_SLOW_MONSTER:
+ {
+ if (slow_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DRAIN_LIFE:
+ {
+ if (drain_life(dir, 75)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_POLYMORPH:
+ {
+ if (poly_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ACID_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_ACID, dir, damroll(6, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ELEC_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_ELEC, dir, damroll(3, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_FIRE_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_FIRE, dir, damroll(8, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_COLD_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_COLD, dir, damroll(5, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ACID_BALL:
+ {
+ fire_ball(GF_ACID, dir, 60, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ELEC_BALL:
+ {
+ fire_ball(GF_ELEC, dir, 32, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_FIRE_BALL:
+ {
+ fire_ball(GF_FIRE, dir, 72, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_COLD_BALL:
+ {
+ fire_ball(GF_COLD, dir, 48, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_HAVOC:
+ {
+ call_chaos();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ default:
+ {
+ process_hooks(HOOK_ZAP, "(d,d)", o_ptr->tval, o_ptr->sval);
+
+ break;
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Tried the object */
+ object_tried(o_ptr);
+
+ /* Successfully determined the object function */
+ if (ident && !object_aware_p(o_ptr))
+ {
+ object_aware(o_ptr);
+ gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Hack -- deal with cancelled zap */
+ if (!use_charge)
+ {
+ o_ptr->timeout += cost;
+
+ return;
+ }
+}
+
+
+
+
+/*
+ * Hook to determine if an object is activable
+ */
+static bool_ item_tester_hook_activate(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Not known */
+ if (!object_known_p(o_ptr)) return (FALSE);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Check activation flag */
+ if (f3 & (TR3_ACTIVATE)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+
+/*
+ * Hack -- activate the ring of power
+ */
+int ring_of_power()
+{
+ char ch = 0, p = 0;
+
+ int plev = p_ptr->lev;
+
+ int timeout = 0;
+
+
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("[S]ummon a wraith, [R]ule the world or "
+ "[C]ast a powerful attack? ", &ch))
+ {
+ return (0);
+ }
+
+ if (ch == 'S' || ch == 's')
+ {
+ p = 1;
+ break;
+ }
+ if (ch == 'R' || ch == 'r')
+ {
+ p = 2;
+ break;
+ }
+ if (ch == 'C' || ch == 'c')
+ {
+ p = 3;
+ break;
+ }
+ }
+
+ /* Summon a Wraith */
+ if (p == 1)
+ {
+ /* Rewrite this -- pelpel */
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD),
+ (bool_)(((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE)))
+ {
+ msg_print("Cold winds begin to blow around you, "
+ "carrying with them the stench of decay...");
+ msg_print("Ancient, long-dead forms arise from the ground "
+ "to serve you!");
+ }
+ timeout = 200 + rand_int(200);
+ }
+
+ /* Rule the World -- only if we can really do so */
+ else if (p == 2)
+ {
+ msg_print("The power of the ring destroys the world!");
+ msg_print("The world changes!");
+
+ autosave_checkpoint();
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ timeout = 250 + rand_int(250);
+ }
+
+ /* Cast a powerful spell */
+ else if (p == 3)
+ {
+ int dir;
+
+ if (!get_aim_dir(&dir)) return (0);
+
+ if (rand_int(3) == 0)
+ {
+ msg_print("You call the fire of Mount Doom!");
+ fire_ball(GF_METEOR, dir, 600, 4);
+ }
+ else
+ {
+ msg_print("Your ring tries to take possession of your enemy's mind!");
+ fire_bolt(GF_CHARM, dir, 600);
+ }
+ timeout = 300 + rand_int(300);
+ }
+
+ return (timeout);
+}
+
+
+
+
+/*
+ * Enchant some bolts
+ */
+bool_ brand_bolts(void)
+{
+ int i;
+
+
+ /* Use the first acceptable bolts */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-bolts */
+ if (o_ptr->tval != TV_BOLT) continue;
+
+ /* Skip artifacts and ego-items */
+ if (o_ptr->art_name || artifact_p(o_ptr) || ego_item_p(o_ptr)) continue;
+
+ /* Skip cursed/broken items */
+ if (cursed_p(o_ptr)) continue;
+
+ /* Randomize */
+ if (rand_int(100) < 75) continue;
+
+ /* Message */
+ msg_print("Your bolts are covered in a fiery aura!");
+
+ /* Ego-item */
+ o_ptr->name2 = EGO_FLAME;
+
+ /* Apply the ego */
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Enchant */
+ enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM);
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Flush */
+ if (flush_failure) flush();
+
+ /* Fail */
+ msg_print("The fiery enchantment failed.");
+
+ /* Notice */
+ return (TRUE);
+}
+
+
+/*
+ * Objects in the p_ptr->inventory can now be activated, and
+ * SOME of those may be able to stack (ego wands or something)
+ * in any case, we can't know that it's impossible. *BUT* we'll
+ * ignore it for now, and the timeout will be set on the entire stack
+ * of objects. Reduces their utility, but oh well.
+ *
+ * Note that it always takes a turn to activate an object, even if
+ * the user hits "escape" at the "direction" prompt.
+ */
+void do_cmd_activate(void)
+{
+ int item, lev, chance;
+
+ char ch, spell_choice;
+
+ object_type *o_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr q, s;
+
+
+ /* Prepare the hook */
+ item_tester_hook = item_tester_hook_activate;
+
+ /* Get an item */
+ command_wrk = USE_EQUIP;
+ q = "Activate which item? ";
+ s = "You have nothing to activate.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Wearable items have to be worn */
+ if (!(f5 & TR5_ACTIVATE_NO_WIELD))
+ {
+ if (item < INVEN_WIELD)
+ {
+ msg_print("You must wear it to activate it.");
+ return;
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Extract the item level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Hack -- Use artifact level instead */
+ if (artifact_p(o_ptr))
+ {
+ if (o_ptr->tval == TV_RANDART)
+ {
+ lev = random_artifacts[o_ptr->sval].level;
+ }
+ else
+ {
+ lev = a_info[o_ptr->name1].level;
+ }
+ }
+
+ /* Base chance of success */
+ chance = p_ptr->skill_dev;
+
+ /* Confusion hurts skill */
+ if (p_ptr->confused) chance = chance / 2;
+
+ /* Hight level objects are harder */
+ chance = chance - ((lev > 50) ? 50 : lev);
+
+ if (chance <= 0)
+ {
+ chance = 1;
+ }
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance *= 10;
+ }
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
+ {
+ if (flush_failure) flush();
+ msg_print("You failed to activate it properly.");
+ sound(SOUND_FAIL);
+ return;
+ }
+
+ /* Check the recharge */
+ if (o_ptr->timeout)
+ {
+ /* Mage Staff of Spells -- Have another timeout in xtra2 */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && o_ptr->xtra2)
+ {
+ msg_print("It whines, glows and fades...");
+ return;
+ }
+
+ /* Monster eggs */
+ else if (o_ptr->tval == TV_EGG)
+ {
+ msg_print("You resume the development of the egg.");
+ o_ptr->timeout = 0;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Success */
+ return;
+ }
+
+ /* Normal activatable items */
+ else
+ {
+ msg_print("It whines, glows and fades...");
+ return;
+ }
+ }
+
+
+ /* Activate the item */
+ msg_print("You activate it...");
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+ /* Lua hook ? -- go first to allow lua to override */
+ if (process_hooks(HOOK_ACTIVATE, "(d)", item))
+ {
+ return;
+ }
+
+ /* New mostly unified activation code
+ This has to be early to allow artifacts to override normal items -- neil */
+
+ if ( activation_aux(o_ptr, TRUE, item) == NULL )
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Success */
+ return;
+ }
+
+ /* Mage Staff of Spells */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ while (TRUE)
+ {
+ if (!get_com("Use Spell [1] or [2]?", &ch))
+ {
+ return;
+ }
+
+ if (ch == '1')
+ {
+ spell_choice = 1;
+ break;
+ }
+
+ if (ch == '2')
+ {
+ spell_choice = 2;
+ break;
+ }
+ }
+
+ if (spell_choice == 1)
+ {
+ /* Still need to check timeouts because there is another counter */
+ if (o_ptr->timeout)
+ {
+ msg_print("The first spell is still charging!");
+ return;
+ }
+
+ /* Cast spell 1 */
+ activate_spell(o_ptr, spell_choice);
+ }
+ else if (spell_choice == 2)
+ {
+ /* Still need to check timeouts because there is another counter */
+ if (o_ptr->xtra2)
+ {
+ msg_print("The second spell is still charging!");
+ return;
+ }
+
+ /* Cast spell 2 */
+ activate_spell(o_ptr, spell_choice);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Success */
+ return;
+ }
+
+ /* Monster eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ msg_print("You stop the development of the egg.");
+ o_ptr->timeout = -1;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Success */
+ return;
+ }
+
+ /* Musical instruments */
+ if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ /* Horns */
+ if (o_ptr->sval == SV_HORN)
+ {
+ msg_format("Your instrument emits a loud sound!");
+
+ aggravate_monsters(1);
+
+ o_ptr->timeout = 100;
+ }
+
+ /* Success */
+ return;
+ }
+
+ /* Mistake */
+ msg_print("Oops. That object cannot be activated.");
+}
+
+
+
+const char *activation_aux(object_type * o_ptr, bool_ doit, int item)
+{
+ int plev = get_skill(SKILL_DEVICE);
+
+ int i = 0, ii = 0, ij = 0, k, dir, dummy = 0;
+ int chance;
+ bool_ is_junkart = (o_ptr->tval == TV_RANDART);
+
+ int spell = 0;
+
+ /* Junkarts */
+ if (is_junkart)
+ spell = activation_info[o_ptr->pval2].spell;
+
+ /* True Actifacts */
+ if (!spell && o_ptr->name1)
+ spell = a_info[o_ptr->name1].activate;
+
+ /* Random and Alchemist Artifacts */
+ if (!spell && o_ptr->art_name)
+ spell = o_ptr->xtra2;
+
+ /* Ego Items */
+ if (!spell && o_ptr->name2)
+ spell = e_info[o_ptr->name2].activate;
+
+ /* Dual egos with the second ego having the activation */
+ if (!spell && o_ptr->name2b)
+ spell = e_info[o_ptr->name2b].activate;
+
+ /* Intrinsic to item type (rings of Ice, etc) */
+ if (!spell)
+ spell = k_info[o_ptr->k_idx].activate;
+
+ /* Complain about mis-configured .txt files? */
+ if (!spell)
+ return "Unknown!";
+
+ /* Negative means a unified spell index */
+ if (spell < 0)
+ {
+ if (doit)
+ {
+ call_lua("activate_activation", "(d,d)", "", -spell, item);
+ o_ptr->timeout = exec_lua(format("return get_activation_timeout(%d)", -spell));
+ }
+ else
+ {
+ return string_exec_lua(format("return get_activation_desc(%d)", -spell));
+ }
+ }
+ else
+ {
+ /* Activate for attack */
+ switch (spell)
+ {
+ case ACT_GILGALAD:
+ {
+ if (!doit) return "starlight (75) every 75+d75 turns";
+ for (k = 1; k < 10; k++)
+ {
+ if (k - 5) fire_beam(GF_LITE, k, 75);
+ }
+
+ o_ptr->timeout = rand_int(75) + 75;
+
+ break;
+ }
+
+ case ACT_CELEBRIMBOR:
+ {
+ if (!doit) return "temporary ESP (dur 20+d20) every 20+d50 turns";
+ set_tim_esp(p_ptr->tim_esp + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 20;
+
+ break;
+ }
+
+ case ACT_SKULLCLEAVER:
+ {
+ if (!doit) return "destruction every 200+d200 turns";
+ destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE);
+
+ o_ptr->timeout = rand_int(200) + 200;
+
+ break;
+ }
+
+ case ACT_HARADRIM:
+ {
+ if (!doit) return "berserk strength every 50+d50 turns";
+ set_afraid(0);
+ set_shero(p_ptr->shero + randint(25) + 25);
+ hp_player(30);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_FUNDIN:
+ {
+ if (!doit) return "dispel evil (x4) every 100+d100 turns";
+ dispel_evil(p_ptr->lev * 4);
+
+ o_ptr->timeout = rand_int(100) + 100;
+
+ break;
+ }
+
+ case ACT_EOL:
+ {
+ if (!doit) return "mana bolt (9d8) 7+d7 turns";
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_MANA, dir, damroll(9, 8));
+
+ o_ptr->timeout = rand_int(7) + 7;
+
+ break;
+ }
+
+ case ACT_UMBAR:
+ {
+ if (!doit) return "magic arrow (10d10) every 20+d20 turns";
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_MISSILE, dir, damroll(10, 10));
+
+ o_ptr->timeout = rand_int(20) + 20;
+
+ break;
+ }
+
+ case ACT_NUMENOR:
+ {
+ /* Give full knowledge */
+ /* Hack -- Maximal info */
+ monster_race *r_ptr;
+ cave_type *c_ptr;
+ int x, y, m;
+
+ if (!doit) return "analyze monster every 500+d200 turns";
+
+ if (!tgt_pt(&x, &y)) break;
+
+ c_ptr = &cave[y][x];
+ if (!c_ptr->m_idx) break;
+
+ r_ptr = &r_info[c_ptr->m_idx];
+
+ /* Observe "maximal" attacks */
+ for (m = 0; m < 4; m++)
+ {
+ /* Examine "actual" blows */
+ if (r_ptr->blow[m].effect || r_ptr->blow[m].method)
+ {
+ /* Hack -- maximal observations */
+ r_ptr->r_blows[m] = MAX_UCHAR;
+ }
+ }
+
+ /* Hack -- maximal drops */
+ r_ptr->r_drop_gold = r_ptr->r_drop_item =
+ (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0));
+
+ /* Hack -- but only "valid" drops */
+ if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0;
+ if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0;
+
+ /* Hack -- observe many spells */
+ r_ptr->r_cast_inate = MAX_UCHAR;
+ r_ptr->r_cast_spell = MAX_UCHAR;
+
+ /* Hack -- know all the flags */
+ r_ptr->r_flags1 = r_ptr->flags1;
+ r_ptr->r_flags2 = r_ptr->flags2;
+ r_ptr->r_flags3 = r_ptr->flags3;
+ r_ptr->r_flags4 = r_ptr->flags4;
+ r_ptr->r_flags5 = r_ptr->flags5;
+ r_ptr->r_flags6 = r_ptr->flags6;
+ r_ptr->r_flags7 = r_ptr->flags7;
+ r_ptr->r_flags8 = r_ptr->flags8;
+ r_ptr->r_flags9 = r_ptr->flags9;
+
+ o_ptr->timeout = rand_int(200) + 500;
+
+ break;
+ }
+
+ case ACT_KNOWLEDGE:
+ {
+ if (!doit) return "whispers from beyond(sanity drain) 100+d200 turns";
+ identify_fully();
+ take_sanity_hit(damroll(10, 7), "the sounds of the dead");
+
+ o_ptr->timeout = rand_int(200) + 100;
+
+ break;
+ }
+
+ case ACT_UNDEATH:
+ {
+ if (!doit) return "ruination every 10+d10 turns";
+ msg_print("The phial wells with dark light...");
+ unlite_area(damroll(2, 15), 3);
+ take_hit(damroll(10, 10), "activating The Phial of Undeath");
+ (void)dec_stat(A_DEX, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_WIS, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_CON, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_STR, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_CHR, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_INT, 25, STAT_DEC_PERMANENT);
+
+ o_ptr->timeout = rand_int(10) + 10;
+
+ break;
+ }
+
+ case ACT_THRAIN:
+ {
+ if (!doit) return "detection every 30+d30 turns";
+ msg_print("The stone glows a deep green...");
+ detect_all(DEFAULT_RADIUS);
+
+ o_ptr->timeout = rand_int(30) + 30;
+
+ break;
+ }
+
+ case ACT_BARAHIR:
+ {
+ if (!doit) return "dispel small life every 55+d55 turns";
+ msg_print("You exterminate small life.");
+ (void)dispel_monsters(4);
+
+ o_ptr->timeout = rand_int(55) + 55;
+
+ break;
+ }
+
+ case ACT_TULKAS:
+ {
+ if (!doit) return "haste self (75+d75 turns) every 150+d150 turns";
+ msg_print("The ring glows brightly...");
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(75) + 75, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ o_ptr->timeout = rand_int(150) + 150;
+
+ break;
+ }
+
+ case ACT_NARYA:
+ {
+ if (!doit) return "healing (500) every 200+d100 turns";
+ msg_print("The ring glows deep red...");
+ hp_player(500);
+ set_blind(0);
+ set_confused(0);
+ set_poisoned(0);
+ set_stun(0);
+ set_cut(0);
+
+ o_ptr->timeout = rand_int(100) + 200;
+
+ break;
+ }
+
+ case ACT_NENYA:
+ {
+ if (!doit) return "healing (800) every 100+d200 turns";
+ msg_print("The ring glows bright white...");
+ hp_player(800);
+ set_blind(0);
+ set_confused(0);
+ set_poisoned(0);
+ set_stun(0);
+ set_cut(0);
+
+ o_ptr->timeout = rand_int(200) + 100;
+
+ break;
+ }
+
+ case ACT_VILYA:
+ {
+ if (!doit) return "greater healing (900) every 200+d200 turns";
+ msg_print("The ring glows deep blue...");
+ hp_player(900);
+ set_blind(0);
+ set_confused(0);
+ set_poisoned(0);
+ set_stun(0);
+ set_cut(0);
+ if (p_ptr->black_breath)
+ {
+ p_ptr->black_breath = FALSE;
+ msg_print("The hold of the Black Breath on you is broken!");
+ }
+
+ o_ptr->timeout = rand_int(200) + 200;
+
+ break;
+ }
+
+ case ACT_POWER:
+ {
+ if (!doit) return "powerful things";
+ msg_print("The ring glows intensely black...");
+
+ o_ptr->timeout = ring_of_power();
+
+ break;
+ }
+
+
+ /* The Stone of Lore is perilous, for the sake of game balance. */
+ case ACT_STONE_LORE:
+ {
+ if (!doit) return "perilous identify every turn";
+ msg_print("The stone reveals hidden mysteries...");
+ if (!ident_spell()) break;
+
+ if (has_ability(AB_PERFECT_CASTING))
+ {
+ /* Sufficient mana */
+ if (20 <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= 20;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = 20 - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You are too weak to control the stone!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed +
+ randint(5 * oops + 1));
+
+ /* Confusing. */
+ (void)set_confused(p_ptr->confused +
+ randint(5 * oops + 1));
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+ }
+
+ take_hit(damroll(1, 12), "perilous secrets");
+
+ /* Confusing. */
+ if (rand_int(5) == 0)
+ {
+ (void)set_confused(p_ptr->confused + randint(10));
+ }
+
+ /* Exercise a little care... */
+ if (rand_int(20) == 0)
+ {
+ take_hit(damroll(4, 10), "perilous secrets");
+ }
+
+ o_ptr->timeout = 1;
+
+ break;
+ }
+
+ case ACT_RAZORBACK:
+ {
+ if (!doit) return "star ball (150) every 1000 turns";
+ msg_print("Your armor is surrounded by lightning...");
+ for (i = 0; i < 8; i++) fire_ball(GF_ELEC, ddd[i], 150, 3);
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_BLADETURNER:
+ {
+ if (!doit) return "invulnerability (4+d8) every 800 turns";
+ set_invuln(p_ptr->invuln + randint(8) + 4);
+
+ o_ptr->timeout = 800;
+
+ break;
+ }
+
+ case ACT_MEDIATOR:
+ {
+ if (!doit) return "breathe elements (300), berserk rage, bless, and resistance every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, 300, 4);
+ msg_print("Your armor glows many colours...");
+ (void)set_afraid(0);
+ (void)set_shero(p_ptr->shero + randint(50) + 50);
+ (void)hp_player(30);
+ (void)set_blessed(p_ptr->blessed + randint(50) + 50);
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(50) + 50);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(50) + 50);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(50) + 50);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(50) + 50);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(50) + 50);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_BELEGENNON:
+ {
+ if (!doit) return ("heal (777), curing and heroism every 300 turns");
+ msg_print("A heavenly choir sings...");
+ (void)set_poisoned(0);
+ (void)set_cut(0);
+ (void)set_stun(0);
+ (void)set_confused(0);
+ (void)set_blind(0);
+ (void)set_hero(p_ptr->hero + randint(25) + 25);
+ (void)hp_player(777);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_GORLIM:
+ {
+ if (!doit) return "rays of fear in every direction";
+ turn_monsters(40 + p_ptr->lev);
+
+ o_ptr->timeout = 3 * (p_ptr->lev + 10);
+
+ break;
+ }
+
+ case ACT_COLLUIN:
+ {
+ if (!doit) return "resistance (20+d20 turns) every 111 turns";
+ msg_print("Your cloak glows many colours...");
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20);
+
+ o_ptr->timeout = 111;
+
+ break;
+ }
+
+
+ case ACT_BELANGIL:
+ {
+ if (!doit) return "frost ball (48) every 5+d5 turns";
+ msg_print("Your dagger is covered in frost...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 48, 2);
+
+ o_ptr->timeout = rand_int(5) + 5;
+
+ break;
+ }
+
+ case ACT_ANGUIREL:
+ {
+ if (!doit) return "a getaway every 35 turns";
+ switch (randint(13))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ teleport_player(10);
+
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ {
+ teleport_player(222);
+
+ break;
+ }
+
+ case 11:
+ case 12:
+ {
+ (void)stair_creation();
+
+ break;
+ }
+
+ default:
+ {
+ if (get_check("Leave this level? "))
+ {
+ autosave_checkpoint();
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+
+ break;
+ }
+ }
+
+ o_ptr->timeout = 35;
+
+ break;
+ }
+
+ case ACT_ERU:
+ {
+ if (!doit) return "healing(7000), curing every 500 turns";
+ msg_print("Your sword glows an intense white...");
+ hp_player(7000);
+ heal_insanity(50);
+ set_blind(0);
+ set_poisoned(0);
+ set_confused(0);
+ set_stun(0);
+ set_cut(0);
+ set_image(0);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_DAWN:
+ {
+ if (!doit) return "summon the Legion of the Dawn every 500+d500 turns";
+ msg_print("You summon the Legion of the Dawn.");
+ (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DAWN, TRUE);
+
+ o_ptr->timeout = 500 + randint(500);
+
+ break;
+ }
+
+ case ACT_FIRESTAR:
+ {
+ if (!doit) return "large fire ball (72) every 100 turns";
+ msg_print("Your morning star rages in fire...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 72, 3);
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_TURMIL:
+ {
+ if (!doit) return "drain life (90) every 70 turns";
+ msg_print("Your hammer glows white...");
+ if (!get_aim_dir(&dir)) break;
+ drain_life(dir, 90);
+
+ o_ptr->timeout = 70;
+
+ break;
+ }
+
+ case ACT_CUBRAGOL:
+ {
+ if (!doit) return "fire branding of bolts every 999 turns";
+ msg_print("Your crossbow glows deep red...");
+ (void)brand_bolts();
+
+ o_ptr->timeout = 999;
+
+ break;
+ }
+
+ case ACT_ELESSAR:
+ {
+ if (!doit) return "heal and cure black breath every 200 turns";
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ }
+ p_ptr->black_breath = FALSE;
+ hp_player(100);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_GANDALF:
+ {
+ if (!doit) return "restore mana every 666 turns";
+ msg_print("Your mage staff glows deep blue...");
+ if (p_ptr->csp < p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+ msg_print("Your feel your head clear.");
+ p_ptr->redraw |= (PR_MANA);
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ o_ptr->timeout = 666;
+
+ break;
+ }
+
+ case ACT_MARDA:
+ {
+ if (!doit) return "summon a thunderlord every 1000 turns";
+ if (randint(3) == 1)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_THUNDERLORD))
+ {
+ msg_print("A Thunderlord comes from thin air!");
+ msg_print("'I will burn you!'");
+ }
+ }
+ else
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ SUMMON_THUNDERLORD, (bool_)(plev == 50 ? TRUE : FALSE)))
+ {
+ msg_print("A Thunderlord comes from thin air!");
+ msg_print("'I will help you in your difficult task.'");
+ }
+ }
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_PALANTIR:
+ {
+ if (!doit) return "clairvoyance every 100+d100 turns";
+ msg_print("The stone glows a deep green...");
+ wiz_lite_extra();
+ (void)detect_traps(DEFAULT_RADIUS);
+ (void)detect_doors(DEFAULT_RADIUS);
+ (void)detect_stairs(DEFAULT_RADIUS);
+
+ o_ptr->timeout = rand_int(100) + 100;
+
+ break;
+ }
+
+ case ACT_EREBOR:
+ {
+ if (!doit) return "open a secret passage every 75 turns";
+ msg_print("Your pick twists in your hands.");
+
+ if (!get_aim_dir(&dir)) break;
+ if (passwall(dir, TRUE))
+ {
+ msg_print("A passage opens, and you step through.");
+ }
+ else
+ {
+ msg_print("There is no wall there!");
+ }
+
+ o_ptr->timeout = 75;
+
+ break;
+ }
+
+ case ACT_DRUEDAIN:
+ {
+ if (!doit) return "detection every 99 turns";
+ msg_print("Your drum shows you the world.");
+ detect_all(DEFAULT_RADIUS);
+
+ o_ptr->timeout = 99;
+
+ break;
+ }
+
+ case ACT_ROHAN:
+ {
+ if (!doit) return "heroism, berserker, and haste every 250 turns";
+ msg_print("Your horn glows deep red.");
+ set_afraid(0);
+ set_shero(p_ptr->shero + damroll(5, 10) + 30);
+ set_afraid(0);
+ set_hero(p_ptr->hero + damroll(5, 10) + 30);
+ set_fast(p_ptr->fast + damroll(5, 10) + 30, 10);
+ hp_player(30);
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_HELM:
+ {
+ if (!doit) return "sound ball (300) every 300 turns";
+ msg_print("Your horn emits a loud sound.");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_SOUND, dir, 300, 6);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_BOROMIR:
+ {
+ if (!doit) return "mass human summoning every 1000 turns";
+ msg_print("Your horn calls for help.");
+ for (i = 0; i < 15; i++)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_HUMAN, TRUE);
+ }
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_HURIN:
+ {
+ if (!doit) return "berserker and +10 to speed (50) every 100+d200 turns";
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(50) + 50, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+ hp_player(30);
+ set_afraid(0);
+ set_shero(p_ptr->shero + randint(50) + 50);
+
+ o_ptr->timeout = rand_int(200) + 100;
+
+ break;
+ }
+
+ case ACT_AXE_GOTHMOG:
+ {
+ if (!doit) return "fire ball (300) every 200+d200 turns";
+ msg_print("Your lochaber axe erupts in fire...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 300, 4);
+
+ o_ptr->timeout = 200 + rand_int(200);
+
+ break;
+ }
+
+ case ACT_MELKOR:
+ {
+ if (!doit) return "darkness ball (150) every 100 turns";
+ msg_print("Your spear is covered of darkness...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_DARK, dir, 150, 3);
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_GROND:
+ {
+ if (!doit) return "alter reality every 100 turns";
+ msg_print("Your hammer hits the floor...");
+ alter_reality();
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_NATUREBANE:
+ {
+ if (!doit) return "dispel monsters (300) every 200+d200 turns";
+ msg_print("Your axe glows blood red...");
+ dispel_monsters(300);
+
+ o_ptr->timeout = 200 + randint(200);
+
+ break;
+ }
+
+ case ACT_NIGHT:
+ {
+ if (!doit) return "vampiric drain (3*100) every 250 turns";
+ msg_print("Your axe emits a black aura...");
+ if (!get_aim_dir(&dir)) break;
+ for (i = 0; i < 3; i++)
+ {
+ if (drain_life(dir, 100)) hp_player(100);
+ }
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_ORCHAST:
+ {
+ if (!doit) return "detect orcs every 10 turns";
+ msg_print("Your weapon glows brightly...");
+ (void)detect_monsters_xxx(RF3_ORC, DEFAULT_RADIUS);
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+ case ACT_SUNLIGHT:
+ {
+ if (!doit) return "beam of sunlight every 10 turns";
+
+ if (!get_aim_dir(&dir)) break;
+ msg_print("A line of sunlight appears.");
+ lite_line(dir);
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+
+ case ACT_BO_MISS_1:
+ {
+ if (!doit) return "magic missile (2d6) every 2 turns";
+ msg_print("It glows extremely brightly...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_MISSILE, dir, damroll(2, 6));
+
+ o_ptr->timeout = 2;
+
+ break;
+ }
+
+ case ACT_BA_POIS_1:
+ {
+ if (!doit) return "stinking cloud (12), rad. 3, every 4+d4 turns";
+ msg_print("It throbs deep green...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_POIS, dir, 12, 3);
+
+ o_ptr->timeout = rand_int(4) + 4;
+
+ break;
+ }
+
+ case ACT_BO_ELEC_1:
+ {
+ if (!doit) return "lightning bolt (4d8) every 6+d6 turns";
+ msg_print("It is covered in sparks...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_ELEC, dir, damroll(4, 8));
+
+ o_ptr->timeout = rand_int(6) + 6;
+
+ break;
+ }
+
+ case ACT_BO_ACID_1:
+ {
+ if (!doit) return "acid bolt (5d8) every 5+d5 turns";
+ msg_print("It is covered in acid...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_ACID, dir, damroll(5, 8));
+
+ o_ptr->timeout = rand_int(5) + 5;
+
+ break;
+ }
+
+ case ACT_BO_COLD_1:
+ {
+ if (!doit) return "frost bolt (6d8) every 7+d7 turns";
+ msg_print("It is covered in frost...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_COLD, dir, damroll(6, 8));
+
+ o_ptr->timeout = rand_int(7) + 7;
+
+ break;
+ }
+
+ case ACT_BO_FIRE_1:
+ {
+ if (!doit) return "fire bolt (9d8) every 8+d8 turns";
+ msg_print("It is covered in fire...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_FIRE, dir, damroll(9, 8));
+
+ o_ptr->timeout = rand_int(8) + 8;
+
+ break;
+ }
+
+ case ACT_BA_COLD_1:
+ {
+ if (!doit) return "ball of cold (48) every 400 turns";
+ msg_print("It is covered in frost...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 48, 2);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_BA_FIRE_1:
+ {
+ if (!doit) return "ball of fire (72) every 400 turns";
+ msg_print("It glows an intense red...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 72, 2);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_DRAIN_1:
+ {
+ if (!doit) return "drain life (100) every 100+d100 turns";
+ msg_print("It glows black...");
+ if (!get_aim_dir(&dir)) break;
+ if (drain_life(dir, 100))
+
+ o_ptr->timeout = rand_int(100) + 100;
+
+ break;
+ }
+
+ case ACT_BA_COLD_2:
+ {
+ if (!doit) return "ball of cold (100) every 300 turns";
+ msg_print("It glows an intense blue...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 100, 2);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_BA_ELEC_2:
+ {
+ if (!doit) return "ball of lightning (100) every 500 turns";
+ msg_print("It crackles with electricity...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_ELEC, dir, 100, 3);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_DRAIN_2:
+ {
+ if (!doit) return "drain life (120) every 400 turns";
+ msg_print("It glows black...");
+ if (!get_aim_dir(&dir)) break;
+ drain_life(dir, 120);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_VAMPIRE_1:
+ {
+ if (!doit) return "vampiric drain (3*50) every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ for (dummy = 0; dummy < 3; dummy++)
+ {
+ if (drain_life(dir, 50))
+ hp_player(50);
+ }
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_BO_MISS_2:
+ {
+ if (!doit) return "arrows (150) every 90+d90 turns";
+ msg_print("It grows magical spikes...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_ARROW, dir, 150);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BA_FIRE_2:
+ {
+ if (!doit) return "fire ball (120) every 225+d225 turns";
+ msg_print("It glows deep red...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 120, 3);
+
+ o_ptr->timeout = rand_int(225) + 225;
+
+ break;
+ }
+
+ case ACT_BA_COLD_3:
+ {
+ if (!doit) return "ball of cold (200) every 325+d325 turns";
+ msg_print("It glows bright white...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 200, 3);
+
+ o_ptr->timeout = rand_int(325) + 325;
+
+ break;
+ }
+
+ case ACT_BA_ELEC_3:
+ {
+ if (!doit) return "Lightning Ball (250) every 425+d425 turns";
+ msg_print("It glows deep blue...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_ELEC, dir, 250, 3);
+
+ o_ptr->timeout = rand_int(425) + 425;
+
+ break;
+ }
+
+ case ACT_WHIRLWIND:
+ {
+ int y = 0, x = 0;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ if (!doit) return "whirlwind attack every 250 turns";
+
+ for (dir = 0; dir <= 9; dir++)
+ {
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ /* Get the monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Hack -- attack monsters */
+ if (c_ptr->m_idx && (m_ptr->ml || cave_floor_bold(y, x)))
+ {
+ py_attack(y, x, -1);
+ }
+ }
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_VAMPIRE_2:
+ {
+ if (!doit) return "vampiric drain (3*100) every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ for (dummy = 0; dummy < 3; dummy++)
+ {
+ if (drain_life(dir, 100))
+ hp_player(100);
+ }
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+
+ case ACT_CALL_CHAOS:
+ {
+ if (!doit) return "call chaos every 350 turns";
+ msg_print("It glows in scintillating colours...");
+ call_chaos();
+
+ o_ptr->timeout = 350;
+
+ break;
+ }
+
+ case ACT_ROCKET:
+ {
+ if (!doit) return "launch rocket (120+level) every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You launch a rocket!");
+ fire_ball(GF_ROCKET, dir, 120 + (plev), 2);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_DISP_EVIL:
+ {
+ if (!doit) return "dispel evil (level*5) every 300+d300 turns";
+ msg_print("It floods the area with goodness...");
+ dispel_evil(p_ptr->lev * 5);
+
+ o_ptr->timeout = rand_int(300) + 300;
+
+ break;
+ }
+
+ case ACT_DISP_GOOD:
+ {
+ if (!doit) return "dispel good (level*5) every 300+d300 turns";
+ msg_print("It floods the area with evil...");
+ dispel_good(p_ptr->lev * 5);
+
+ o_ptr->timeout = rand_int(300) + 300;
+
+ break;
+ }
+
+ case ACT_BA_MISS_3:
+ {
+ if (!doit) return "elemental breath (300) every 500 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, 300, 4);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ /* Activate for other offensive action */
+
+ case ACT_CONFUSE:
+ {
+ if (!doit) return "confuse monster every 15 turns";
+ msg_print("It glows in scintillating colours...");
+ if (!get_aim_dir(&dir)) break;
+ confuse_monster(dir, 20);
+
+ o_ptr->timeout = 15;
+
+ break;
+ }
+
+ case ACT_SLEEP:
+ {
+ if (!doit) return "sleep nearby monsters every 55 turns";
+ msg_print("It glows deep blue...");
+ sleep_monsters_touch();
+
+ o_ptr->timeout = 55;
+
+ break;
+ }
+
+ case ACT_QUAKE:
+ {
+ if (!doit) return "earthquake (rad 10) every 50 turns";
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ {
+ earthquake(p_ptr->py, p_ptr->px, 10);
+ o_ptr->timeout = 50;
+ }
+
+ break;
+ }
+
+ case ACT_TERROR:
+ {
+ if (!doit) return "terror every 3 * (level+10) turns";
+ turn_monsters(40 + p_ptr->lev);
+
+ o_ptr->timeout = 3 * (p_ptr->lev + 10);
+
+ break;
+ }
+
+ case ACT_TELE_AWAY:
+ {
+ if (!doit) return "teleport away every 200 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void)fire_beam(GF_AWAY_ALL, dir, plev);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_BANISH_EVIL:
+ {
+ if (!doit) return "banish evil every 250+d250 turns";
+ if (banish_evil(100))
+ {
+ msg_print("The power of the artifact banishes evil!");
+ }
+
+ o_ptr->timeout = 250 + randint(250);
+
+ break;
+ }
+
+ case ACT_GENOCIDE:
+ {
+ if (!doit) return "genocide every 500 turns";
+ msg_print("It glows deep blue...");
+ (void)genocide(TRUE);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_MASS_GENO:
+ {
+ if (!doit) return "mass genocide every 1000 turns";
+ msg_print("It lets out a long, shrill note...");
+ (void)mass_genocide(TRUE);
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ /* Activate for summoning / charming */
+
+ case ACT_CHARM_ANIMAL:
+ {
+ if (!doit) return "charm animal every 300 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void) charm_animal(dir, plev);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_CHARM_UNDEAD:
+ {
+ if (!doit) return "enslave undead every 333 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void)control_one_undead(dir, plev);
+
+ o_ptr->timeout = 333;
+
+ break;
+ }
+
+ case ACT_CHARM_OTHER:
+ {
+ if (!doit) return "charm monster every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void) charm_monster(dir, plev);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_CHARM_ANIMALS:
+ {
+ if (!doit) return "animal friendship every 500 turns";
+ (void) charm_animals(plev * 2);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_CHARM_OTHERS:
+ {
+ if (!doit) return "mass charm every 750 turns";
+ charm_monsters(plev * 2);
+
+ o_ptr->timeout = 750;
+
+ break;
+ }
+
+ case ACT_SUMMON_ANIMAL:
+ {
+ if (!doit) return "summon animal every 200+d300 turns";
+ (void)summon_specific_friendly(p_ptr->py, p_ptr->px, plev, SUMMON_ANIMAL_RANGER, TRUE);
+
+ o_ptr->timeout = 200 + randint(300);
+
+ break;
+ }
+
+ case ACT_SUMMON_PHANTOM:
+ {
+ if (!doit) return "summon phantasmal servant every 200+d200 turns";
+ msg_print("You summon a phantasmal servant.");
+ (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_PHANTOM, TRUE);
+
+ o_ptr->timeout = 200 + randint(200);
+
+ break;
+ }
+
+ case ACT_SUMMON_ELEMENTAL:
+ {
+ if (!doit) return "summon elemental every 750 turns";
+ if (randint(3) == 1)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_ELEMENTAL))
+ {
+ msg_print("An elemental materialises...");
+ msg_print("You fail to control it!");
+ }
+ }
+ else
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ SUMMON_ELEMENTAL, (bool_)(plev == 50 ? TRUE : FALSE)))
+ {
+ msg_print("An elemental materialises...");
+ msg_print("It seems obedient to you.");
+ }
+ }
+
+ o_ptr->timeout = 750;
+
+ break;
+ }
+
+ case ACT_SUMMON_DEMON:
+ {
+ if (!doit) return "summon demon every 666+d333 turns";
+ if (randint(3) == 1)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_DEMON))
+ {
+ msg_print("The area fills with a stench of sulphur and brimstone.");
+ msg_print("'NON SERVIAM! Wretch! I shall feast on thy mortal soul!'");
+ }
+ }
+ else
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ SUMMON_DEMON, (bool_)(plev == 50 ? TRUE : FALSE)))
+ {
+ msg_print("The area fills with a stench of sulphur and brimstone.");
+ msg_print("'What is thy bidding... Master?'");
+ }
+ }
+
+ o_ptr->timeout = 666 + randint(333);
+
+ break;
+ }
+
+ case ACT_SUMMON_UNDEAD:
+ {
+ if (!doit) return "summon undead every 666+d333 turns";
+ if (randint(3) == 1)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ (plev > 47 ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD)))
+ {
+ msg_print("Cold winds begin to blow around you, carrying with them the stench of decay...");
+ msg_print("'The dead arise... to punish you for disturbing them!'");
+ }
+ }
+ else
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD),
+ (bool_)(((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE)))
+ {
+ msg_print("Cold winds begin to blow around you, carrying with them the stench of decay...");
+ msg_print("Ancient, long-dead forms arise from the ground to serve you!");
+ }
+ }
+
+ o_ptr->timeout = 666 + randint(333);
+
+ break;
+ }
+
+ /* Activate for healing */
+
+ case ACT_CURE_LW:
+ {
+ if (!doit) return format("cure light wounds every %d turns", (is_junkart ? 50 : 10));
+ (void)set_afraid(0);
+ (void)hp_player(30);
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+
+ case ACT_CURE_MW:
+ {
+ if (!doit) return format("cure serious wounds every %s turns", (is_junkart? "75" : "3+d3"));
+ msg_print("It radiates deep purple...");
+ hp_player(damroll(4, 8));
+ (void)set_cut((p_ptr->cut / 2) - 50);
+
+ o_ptr->timeout = rand_int(3) + 3;
+
+ break;
+ }
+
+ case ACT_CURE_POISON:
+ {
+ if (!doit) return "remove fear and cure poison every 5 turns";
+ msg_print("It glows deep blue...");
+ (void)set_afraid(0);
+ (void)set_poisoned(0);
+
+ o_ptr->timeout = 5;
+
+ break;
+ }
+
+ case ACT_REST_LIFE:
+ {
+ if (!doit) return "restore life levels every 450 turns";
+ msg_print("It glows a deep red...");
+ restore_level();
+
+ o_ptr->timeout = 450;
+
+ break;
+ }
+
+ case ACT_REST_ALL:
+ {
+ if (!doit) return format("restore stats and life levels every %d turns", (is_junkart ? 200 : 750));
+ msg_print("It glows a deep green...");
+ (void)do_res_stat(A_STR, TRUE);
+ (void)do_res_stat(A_INT, TRUE);
+ (void)do_res_stat(A_WIS, TRUE);
+ (void)do_res_stat(A_DEX, TRUE);
+ (void)do_res_stat(A_CON, TRUE);
+ (void)do_res_stat(A_CHR, TRUE);
+ (void)restore_level();
+
+ o_ptr->timeout = 750;
+
+ break;
+ }
+
+ case ACT_CURE_700:
+ {
+ if (!doit) return format("heal 700 hit points every %d turns", (is_junkart ? 100 : 250));
+ msg_print("It glows deep blue...");
+ msg_print("You feel a warm tingling inside...");
+ (void)hp_player(700);
+ (void)set_cut(0);
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_CURE_1000:
+ {
+ if (!doit) return "heal 1000 hit points every 888 turns";
+ msg_print("It glows a bright white...");
+ msg_print("You feel much better...");
+ (void)hp_player(1000);
+ (void)set_cut(0);
+
+ o_ptr->timeout = 888;
+
+ break;
+ }
+
+ case ACT_ESP:
+ {
+ if (!doit) return "temporary ESP (dur 25+d30) every 200 turns";
+ (void)set_tim_esp(p_ptr->tim_esp + randint(30) + 25);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_BERSERK:
+ {
+ if (!doit) return "heroism and berserk (dur 50+d50) every 100+d100 turns";
+ (void)set_shero(p_ptr->shero + randint(50) + 50);
+ (void)set_blessed(p_ptr->blessed + randint(50) + 50);
+
+ o_ptr->timeout = 100 + randint(100);
+
+ break;
+ }
+
+ case ACT_PROT_EVIL:
+ {
+ if (!doit) return "protection from evil (dur level*3 + d25) every 225+d225 turns";
+ msg_print("It lets out a shrill wail...");
+ k = 3 * p_ptr->lev;
+ (void)set_protevil(p_ptr->protevil + randint(25) + k);
+
+ o_ptr->timeout = rand_int(225) + 225;
+
+ break;
+ }
+
+ case ACT_RESIST_ALL:
+ {
+ if (!doit) return "resist elements (dur 40+d40) every 200 turns";
+ msg_print("It glows many colours...");
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(40) + 40);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(40) + 40);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(40) + 40);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(40) + 40);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(40) + 40);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_SPEED:
+ {
+ if (!doit) return "speed (dur 20+d20) every 250 turns";
+ msg_print("It glows bright green...");
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(20) + 20, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_XTRA_SPEED:
+ {
+ if (!doit) return "speed (dur 75+d75) every 200+d200 turns";
+ msg_print("It glows brightly...");
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(75) + 75, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ o_ptr->timeout = rand_int(200) + 200;
+
+ break;
+ }
+
+ case ACT_WRAITH:
+ {
+ if (!doit) return "wraith form (level/2 + d(level/2)) every 1000 turns";
+ set_shadow(p_ptr->tim_wraith + randint(plev / 2) + (plev / 2));
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_INVULN:
+ {
+ if (!doit) return "invulnerability (dur 8+d8) every 1000 turns";
+ (void)set_invuln(p_ptr->invuln + randint(8) + 8);
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ /* Activate for general purpose effect (detection etc.) */
+
+ case ACT_LIGHT:
+ {
+ if (!doit) return format("light area (dam 2d15) every %s turns", (is_junkart ? "100" : "10+d10"));
+ msg_print("It wells with clear light...");
+ lite_area(damroll(2, 15), 3);
+
+ o_ptr->timeout = rand_int(10) + 10;
+
+ break;
+ }
+
+ case ACT_MAP_LIGHT:
+ {
+ if (!doit) return "light (dam 2d15) & map area every 50+d50 turns";
+ msg_print("It shines brightly...");
+ map_area();
+ lite_area(damroll(2, 15), 3);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_DETECT_ALL:
+ {
+ if (!doit) return "detection every 55+d55 turns";
+ msg_print("It glows bright white...");
+ msg_print("An image forms in your mind...");
+ detect_all(DEFAULT_RADIUS);
+
+ o_ptr->timeout = rand_int(55) + 55;
+
+ break;
+ }
+
+ case ACT_DETECT_XTRA:
+ {
+ if (!doit) return "detection, probing and identify true every 1000 turns";
+ msg_print("It glows brightly...");
+ detect_all(DEFAULT_RADIUS);
+ probing();
+ identify_fully();
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_ID_FULL:
+ {
+ if (!doit) return "identify true every 750 turns";
+ msg_print("It glows yellow...");
+ identify_fully();
+
+ o_ptr->timeout = 750;
+
+ break;
+ }
+
+ case ACT_ID_PLAIN:
+ {
+ if (!doit) return "identify spell every 10 turns";
+ if (!ident_spell()) break;
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+
+ case ACT_RUNE_EXPLO:
+ {
+ if (!doit) return "explosive rune every 200 turns";
+ msg_print("It glows bright red...");
+ explosive_rune();
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_RUNE_PROT:
+ {
+ if (!doit) return "rune of protection every 400 turns";
+ msg_print("It glows light blue...");
+ warding_glyph();
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_SATIATE:
+ {
+ if (!doit) return "satisfy hunger every 200 turns";
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_DEST_DOOR:
+ {
+ if (!doit) return "destroy doors and traps every 10 turns";
+ msg_print("It glows bright red...");
+ destroy_doors_touch();
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+
+ case ACT_STONE_MUD:
+ {
+ if (!doit) return "stone to mud every 5 turns";
+ msg_print("It pulsates...");
+ if (!get_aim_dir(&dir)) break;
+ wall_to_mud(dir);
+
+ o_ptr->timeout = 5;
+
+ break;
+ }
+
+ case ACT_RECHARGE:
+ {
+ if (!doit) return "recharging every 70 turns";
+ recharge(60);
+
+ o_ptr->timeout = 70;
+
+ break;
+ }
+
+ case ACT_ALCHEMY:
+ {
+ if (!doit) return "alchemy every 500 turns";
+ msg_print("It glows bright yellow...");
+ (void) alchemy();
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_DIM_DOOR:
+ {
+ if (!doit) return "dimension door every 100 turns";
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("Not on special levels!");
+ break;
+ }
+
+ msg_print("You open a Void Jumpgate. Choose a destination.");
+ if (!tgt_pt(&ii, &ij)) break;
+
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) || (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2) ||
+ (!rand_int(plev * plev / 2)))
+ {
+ msg_print("You fail to exit the void correctly!");
+ p_ptr->energy -= 100;
+ get_pos_player(10, &ij, &ii);
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN);
+ cave_set_feat(ij, ii, FEAT_BETWEEN);
+ cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8);
+ cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8);
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_TELEPORT:
+ {
+ if (!doit) return format("teleport (range 100) every %d turns", (is_junkart? 100 : 45));
+ msg_print("It twists space around you...");
+ teleport_player(100);
+
+ o_ptr->timeout = 45;
+
+ break;
+ }
+
+ case ACT_RECALL:
+ {
+ if (!(dungeon_flags2 & DF2_ASK_LEAVE) || ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")))
+ {
+ if (!doit) return "word of recall every 200 turns";
+ msg_print("It glows soft white...");
+ recall_player(20,15);
+
+ o_ptr->timeout = 200;
+ }
+
+ break;
+ }
+
+ case ACT_DEATH:
+ {
+ if (!doit) return "death";
+ take_hit(5000, "activating a death spell");
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_RUINATION:
+ {
+ if (!doit) return "Ruination";
+ msg_print("Your nerves and muscles feel weak and lifeless!");
+
+ take_hit(damroll(10, 10), "activating Ruination");
+ (void)dec_stat(A_DEX, 25, TRUE);
+ (void)dec_stat(A_WIS, 25, TRUE);
+ (void)dec_stat(A_CON, 25, TRUE);
+ (void)dec_stat(A_STR, 25, TRUE);
+ (void)dec_stat(A_CHR, 25, TRUE);
+ (void)dec_stat(A_INT, 25, TRUE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_DESTRUC:
+ {
+ if (!doit) return "Destruction every 100 turns";
+ earthquake(p_ptr->py, p_ptr->px, 12);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNINT:
+ {
+ if (!doit) return "decreasing Intelligence";
+ (void)dec_stat(A_INT, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNSTR:
+ {
+ if (!doit) return "decreasing Strength";
+ (void)dec_stat(A_STR, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNCON:
+ {
+ if (!doit) return "decreasing Constitution";
+ (void)dec_stat(A_CON, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNCHR:
+ {
+ if (!doit) return "decreasing Charisma";
+ (void)dec_stat(A_CHR, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNDEX:
+ {
+ if (!doit) return "decreasing Dexterity";
+ (void)dec_stat(A_DEX, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNWIS:
+ {
+ if (!doit) return "decreasing Wisdom";
+ (void)dec_stat(A_WIS, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_STATLOSS:
+ {
+ if (!doit) return "stat loss";
+ (void)dec_stat(A_STR, 15, FALSE);
+ (void)dec_stat(A_INT, 15, FALSE);
+ (void)dec_stat(A_WIS, 15, FALSE);
+ (void)dec_stat(A_DEX, 15, FALSE);
+ (void)dec_stat(A_CON, 15, FALSE);
+ (void)dec_stat(A_CHR, 15, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_HISTATLOSS:
+ {
+ if (!doit) return "high stat loss";
+ (void)dec_stat(A_STR, 25, FALSE);
+ (void)dec_stat(A_INT, 25, FALSE);
+ (void)dec_stat(A_WIS, 25, FALSE);
+ (void)dec_stat(A_DEX, 25, FALSE);
+ (void)dec_stat(A_CON, 25, FALSE);
+ (void)dec_stat(A_CHR, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_EXPLOSS:
+ {
+ if (!doit) return "experience loss";
+ lose_exp(p_ptr->exp / 20);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_HIEXPLOSS:
+ {
+ if (!doit) return "high experience loss";
+ lose_exp(p_ptr->exp / 10);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_SUMMON_MONST:
+ {
+ if (!doit) return "summon monster";
+ summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_PARALYZE:
+ {
+ if (!doit) return "paralyze";
+ set_paralyzed(p_ptr->paralyzed + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_HALLU:
+ {
+ if (!doit) return "hallucination every 10 turns";
+ set_image(p_ptr->image + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_POISON:
+ {
+ if (!doit) return "poison";
+ set_poisoned(p_ptr->poisoned + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_HUNGER:
+ {
+ if (!doit) return "create hunger";
+ (void)set_food(PY_FOOD_WEAK);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_STUN:
+ {
+ if (!doit) return "stun";
+ set_stun(p_ptr->stun + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CUTS:
+ {
+ if (!doit) return "cuts";
+ set_cut(p_ptr->cut + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_PARANO:
+ {
+ if (!doit) return "confusion";
+ set_confused(p_ptr->confused + 30 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CONFUSION:
+ {
+ if (!doit) return "confusion";
+ set_confused(p_ptr->confused + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_BLIND:
+ {
+ if (!doit) return "blindness";
+ set_blind(p_ptr->blind + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_PET_SUMMON:
+ {
+ if (!doit) return "summon pet every 101 turns";
+ summon_specific_friendly(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0, FALSE);
+
+ /* Timeout is set before return */
+ /*FINDME*/
+
+ break;
+ }
+
+ case ACT_CURE_PARA:
+ {
+ if (!doit) return "cure confusion every 500 turns";
+ set_confused(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_HALLU:
+ {
+ if (!doit) return "cure hallucination every 100 turns";
+ set_image(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_POIS:
+ {
+ if (!doit) return "cure poison every 100 turns";
+ set_poisoned(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_HUNGER:
+ {
+ if (!doit) return "satisfy hunger every 100 turns";
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_STUN:
+ {
+ if (!doit) return "cure stun every 100 turns";
+ set_stun(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_CUTS:
+ {
+ if (!doit) return "cure cuts every 100 turns";
+ set_cut(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_FEAR:
+ {
+ if (!doit) return "cure fear every 100 turns";
+ set_afraid(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_CONF:
+ {
+ if (!doit) return "cure confusion every 100 turns";
+ set_confused(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_BLIND:
+ {
+ if (!doit) return "cure blindness every 100 turns";
+ set_blind(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURING:
+ {
+ if (!doit) return "curing every 110 turns";
+ set_blind(0);
+ set_poisoned(0);
+ set_confused(0);
+ set_stun(0);
+ set_cut(0);
+ set_image(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_DARKNESS:
+ {
+ if (!doit) return "darkness";
+ unlite_area(damroll(2, 10), 10);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_LEV_TELE:
+ {
+ if (!doit) return "teleport level every 50 turns";
+ teleport_player_level();
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_ACQUIREMENT:
+ {
+ if (!doit) return "acquirement every 3000 turns";
+ acquirement(p_ptr->py, p_ptr->px, 1, FALSE, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_WEIRD:
+ {
+ if (!doit) return "something weird every 5 turns";
+ /* It doesn't do anything */
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_AGGRAVATE:
+ {
+ if (!doit) return "aggravate";
+ aggravate_monsters(1);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_MUT:
+ {
+ if (!doit) return "gain corruption every 10 turns";
+ gain_random_corruption(0);
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_INSANITY:
+ {
+ if (!doit) return "cure insanity every 200 turns";
+ heal_insanity(damroll(10, 10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_MUT:
+ {
+ msg_print("Ahah, you wish.");
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_LIGHT_ABSORBTION:
+ {
+ int y, x, light = 0, dir;
+ cave_type *c_ptr;
+
+ if (!doit) return "light absorption every 80 turns";
+
+ for (y = p_ptr->py - 6; y <= p_ptr->py + 6; y++)
+ {
+ for (x = p_ptr->px - 6; x <= p_ptr->px + 6; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ c_ptr = &cave[y][x];
+
+ if (distance(y, x, p_ptr->py, p_ptr->px) > 6) continue;
+
+ if (c_ptr->info & CAVE_GLOW)
+ {
+ light++;
+
+ /* No longer in the array */
+ c_ptr->info &= ~(CAVE_TEMP);
+
+ /* Darken the grid */
+ c_ptr->info &= ~(CAVE_GLOW);
+
+ /* Hack -- Forget "boring" grids */
+ if (cave_plain_floor_grid(c_ptr) &&
+ !(c_ptr->info & (CAVE_TRDT)))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ note_spot(y, x);
+ }
+
+ /* Process affected monsters */
+ if (c_ptr->m_idx)
+ {
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, FALSE);
+ }
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+ }
+
+ if (!get_aim_dir(&dir)) return (FALSE);
+
+ msg_print("The light around you is absorbed... "
+ "and released in a powerful bolt!");
+ fire_bolt(GF_LITE, dir, damroll(light, p_ptr->lev));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+ /* Horns of DragonKind (Note that these are new egos)*/
+ case ACT_BA_FIRE_H:
+ {
+ if (!doit) return "large fire ball (300) every 100 turns";
+ fire_ball(GF_FIRE, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+ case ACT_BA_COLD_H:
+ {
+ if (!doit) return "large cold ball (300) every 100 turns";
+ fire_ball(GF_COLD, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+ case ACT_BA_ELEC_H:
+ {
+ if (!doit) return "large lightning ball (300) every 100 turns";
+ fire_ball(GF_ELEC, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+ case ACT_BA_ACID_H:
+ {
+ if (!doit) return "large acid ball (300) every 100 turns";
+ fire_ball(GF_ACID, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+
+ case ACT_SPIN:
+ {
+ if (!doit) return "spinning around every 50+d25 turns";
+ do_spin();
+
+ o_ptr->timeout = 50 + randint(25);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ break;
+ }
+ case ACT_NOLDOR:
+ {
+ if (!doit) return "detect treasure every 10+d20 turns";
+ detect_treasure(DEFAULT_RADIUS);
+
+ o_ptr->timeout = 10 + randint(20);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ break;
+ }
+ case ACT_SPECTRAL:
+ {
+ if (!doit) return "wraith-form every 50+d50 turns";
+ if (!p_ptr->wraith_form)
+ {
+ set_shadow(20 + randint(20));
+ }
+ else
+ {
+ set_shadow(p_ptr->tim_wraith + randint(20));
+ }
+
+ o_ptr->timeout = 50 + randint(50);
+
+ /* Window stuff */
+ p_ptr->window |= PW_INVEN | PW_EQUIP;
+
+ /* Done */
+ break;
+ }
+ case ACT_JUMP:
+ {
+ if (!doit) return "phasing every 10+d10 turns";
+ teleport_player(10);
+ o_ptr->timeout = 10 + randint(10);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ break;
+ }
+
+ case ACT_DEST_TELE:
+ {
+ if (!doit) return "teleportation and destruction of the ring";
+ if (!item)
+ {
+ msg_print("You can't activate this when it's there!");
+ }
+ if (get_check("This will destroy the ring. Do you wish to continue? "))
+ {
+ msg_print("The ring explodes into a space distortion.");
+ teleport_player(200);
+
+ /* It explodes, doesn't it ? */
+ take_hit(damroll(2, 10), "an exploding ring");
+
+ inc_stack_size_ex(item, -255, OPTIMIZE, NO_DESCRIBE);
+ }
+
+ break;
+ }
+ /*amulet of serpents dam 100, rad 2 timeout 40+d60 */
+ case ACT_BA_POIS_4:
+ {
+ if (!doit) return "venom breathing every 40+d60 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ msg_print("You breathe venom...");
+ fire_ball(GF_POIS, dir, 100, 2);
+
+ o_ptr->timeout = rand_int(60) + 40;
+
+ /* Window stuff */
+ p_ptr->window |= PW_INVEN | PW_EQUIP;
+
+ /* Done */
+ break;
+ }
+ /*rings of X 50,50+d50 dur 20+d20 */
+ case ACT_BA_COLD_4:
+ {
+ if (!doit) return "ball of cold and resist cold every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_COLD, dir, 50, 2);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_BA_FIRE_4:
+ {
+ if (!doit) return "ball of fire and resist fire every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_FIRE, dir, 50, 2);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+ case ACT_BA_ACID_4:
+ {
+ if (!doit) return "ball of acid and resist acid every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ACID, dir, 50, 2);
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_BA_ELEC_4:
+ {
+ if (!doit) return "ball of lightning and resist lightning every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ELEC, dir, 50, 2);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_BR_ELEC:
+ {
+ if (!doit) return "breathe lightning (100) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe lightning.");
+ fire_ball(GF_ELEC, dir, 100, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_COLD:
+ {
+ if (!doit) return "breathe frost (110) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe frost.");
+ fire_ball(GF_COLD, dir, 110, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_FIRE:
+ {
+ if (!doit) return "breathe fire (200) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe fire.");
+ fire_ball(GF_FIRE, dir, 200, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_ACID:
+ {
+ if (!doit) return "breathe acid (130) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe acid.");
+ fire_ball(GF_ACID, dir, 130, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_POIS:
+ {
+ if (!doit) return "breathe poison gas (150) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe poison gas.");
+ fire_ball(GF_POIS, dir, 150, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_MANY:
+ {
+ if (!doit) return "breathe multi-hued (250) every 60+d60 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(5);
+ msg_format("You breathe %s.",
+ ((chance == 1) ? "lightning" :
+ ((chance == 2) ? "frost" :
+ ((chance == 3) ? "acid" :
+ ((chance == 4) ? "poison gas" : "fire")))));
+ fire_ball(((chance == 1) ? GF_ELEC :
+ ((chance == 2) ? GF_COLD :
+ ((chance == 3) ? GF_ACID :
+ ((chance == 4) ? GF_POIS : GF_FIRE)))),
+ dir, 250, 2);
+
+ o_ptr->timeout = rand_int(60) + 60;
+
+ break;
+ }
+
+ case ACT_BR_CONF:
+ {
+ if (!doit) return "breathe confusion (120) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe confusion.");
+ fire_ball(GF_CONFUSION, dir, 120, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_SOUND:
+ {
+ if (!doit) return "breathe sound (130) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe sound.");
+ fire_ball(GF_SOUND, dir, 130, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_CHAOS:
+ {
+ if (!doit) return "breathe chaos/disenchant (220) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(2);
+ msg_format("You breathe %s.",
+ ((chance == 1 ? "chaos" : "disenchantment")));
+ fire_ball((chance == 1 ? GF_CHAOS : GF_DISENCHANT),
+ dir, 220, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+
+ case ACT_BR_SHARD:
+ {
+ if (!doit) return "breathe sound/shards (230) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(2);
+ msg_format("You breathe %s.",
+ ((chance == 1 ? "sound" : "shards")));
+ fire_ball((chance == 1 ? GF_SOUND : GF_SHARDS),
+ dir, 230, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+
+ case ACT_BR_BALANCE:
+ {
+ if (!doit) return "breathe balance (250) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(4);
+ msg_format("You breathe %s.",
+ ((chance == 1) ? "chaos" :
+ ((chance == 2) ? "disenchantment" :
+ ((chance == 3) ? "sound" : "shards"))));
+ fire_ball(((chance == 1) ? GF_CHAOS :
+ ((chance == 2) ? GF_DISENCHANT :
+ ((chance == 3) ? GF_SOUND : GF_SHARDS))),
+ dir, 250, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+
+ case ACT_BR_LIGHT:
+ {
+ if (!doit) return "breathe light/darkness (200) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(2);
+ msg_format("You breathe %s.",
+ ((chance == 0 ? "light" : "darkness")));
+ fire_ball((chance == 0 ? GF_LITE : GF_DARK), dir, 200, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+ case ACT_BR_POWER:
+ {
+ if (!doit) return "breathe the elements (300) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, 300, 3);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+ case ACT_GROW_MOLD:
+ {
+ if (!doit) return "grow mushrooms every 50+d50 turns";
+ msg_print("You twirl and spores fly everywhere!");
+ for (i = 0; i < 8; i++)
+ summon_specific_friendly(p_ptr->py, p_ptr->px, p_ptr->lev, SUMMON_BIZARRE1, FALSE);
+
+ o_ptr->timeout = randint(50) + 50;
+
+ break;
+ }
+ case ACT_MUSIC:
+ /*
+ fall through to unknown, as music should be
+ handled by calling procedure.
+ */
+
+ default:
+ {
+ msg_format("Unknown activation effect: %d.", spell);
+ if ( !doit ) return "Unknown Activation";
+ return NULL;
+ }
+ }
+ }
+
+ /* Set timeout for junkarts
+ * Note that I still need to set the timeouts for other
+ * (non-random) artifacts above
+ */
+ if (is_junkart && doit)
+ o_ptr->timeout = activation_info[o_ptr->pval2].cost / 10;
+
+ return NULL;
+}
+
+
+static bool_ activate_spell(object_type * o_ptr, byte choice)
+{
+ int mana = 0, gf = 0, mod = 0;
+
+ rune_spell s_ptr;
+
+
+ if (choice == 1)
+ {
+ gf = o_ptr->pval & 0xFFFF;
+ mod = o_ptr->pval3 & 0xFFFF;
+ mana = o_ptr->pval2 & 0xFF;
+ }
+ else if (choice == 2)
+ {
+ gf = o_ptr->pval >> 16;
+ mod = o_ptr->pval3 >> 16;
+ mana = o_ptr->pval2 >> 8;
+ }
+
+ s_ptr.type = gf;
+ s_ptr.rune2 = 1 << mod;
+ s_ptr.mana = mana;
+
+ /* Execute */
+ rune_exec(&s_ptr, 0);
+
+ if (choice == 1) o_ptr->timeout = mana * 5;
+ if (choice == 2) o_ptr->xtra2 = mana * 5;
+
+ return (TRUE);
+}
diff --git a/src/cmd7.c b/src/cmd7.c
new file mode 100644
index 00000000..aca14dcd
--- /dev/null
+++ b/src/cmd7.c
@@ -0,0 +1,7652 @@
+/* File: cmd7.c */
+
+/* Purpose: More Class commands */
+
+/*
+ * Copyright (c) 1999 Dark God
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+#include "angband.h"
+
+
+/*
+ * Describe class powers of Mindcrafters
+ *
+ * 'p' points to a 80 byte long buffer
+ */
+void mindcraft_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_MINDCRAFT);
+
+
+ /* Clear buffer */
+ strcpy(p, "");
+
+ /* Fill the buffer with requested power description */
+ switch (power)
+ {
+ case 0:
+ strnfmt(p, 80, " rad %d", DEFAULT_RADIUS);
+ break;
+ case 1:
+ strnfmt(p, 80, " dam %dd%d", 3 + ((plev - 1) / 4), 3 + plev / 15);
+ break;
+ case 2:
+ strnfmt(p, 80, " range %d", (plev < 25 ? 10 : plev + 2 + p_ptr->to_s * 3));
+ break;
+ case 3:
+ strnfmt(p, 80, " range %d", plev * 5);
+ break;
+ case 4:
+ strnfmt(p, 80, " power %d", plev * (plev < 30 ? 1 : 2));
+ break;
+ case 5:
+ if (plev > 20)
+ strnfmt(p, 80, " dam %dd8 rad %d", 8 + ((plev - 5) / 4), (plev - 20)/8 + 1);
+ else
+ strnfmt(p, 80, " dam %dd8", 8 + ((plev - 5) / 4));
+ break;
+ case 6:
+ strnfmt(p, 80, " dur %d", plev);
+ break;
+ case 7:
+ break;
+ case 8:
+ if (plev < 25)
+ strnfmt(p, 80, " dam %d rad %d", (3 * plev) / 2, 2 + (plev / 10));
+ else
+ strnfmt(p, 80, " dam %d", plev * ((plev - 5) / 10 + 1));
+ break;
+ case 9:
+ strnfmt(p, 80, " dur 11-%d", 10 + plev + plev / 2);
+ break;
+ case 10:
+ strnfmt(p, 80, " dam %dd6 rad %d", plev / 2, 0 + (plev - 25) / 10);
+ break;
+ case 11:
+ strnfmt(p, 80, " dam %d rad %d", plev * (plev > 39 ? 4 : 3), 3 + plev / 10);
+ break;
+ }
+}
+
+
+/*
+ * Describe class powers of Mimics
+ *
+ * 'p' points to a 80 byte long buffer
+ */
+void mimic_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_MIMICRY);
+ object_type *o_ptr = &p_ptr->inventory[INVEN_OUTER];
+
+ /* Clear the buffer */
+ strcpy(p, "");
+
+ /* Fill the buffer with requested power description */
+ switch (power)
+ {
+ case 0:
+ strnfmt(p, 80, " dur %d", k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000));
+ break;
+ case 1:
+ strnfmt(p, 80, " dur %d+d20", 10 + plev);
+ break;
+ case 2:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ case 3:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ case 4:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ }
+}
+
+
+/*
+ * Allow user to choose a magic power.
+ *
+ * If a valid spell is chosen, saves it in '*sn' and returns TRUE
+ * If the user hits escape, returns FALSE, and set '*sn' to -1
+ * If there are no legal choices, returns FALSE, and sets '*sn' to -2
+ *
+ * The "prompt" should be "cast", "recite", or "study"
+ * The "known" should be TRUE for cast/pray, FALSE for study
+ *
+ * nb: This function has a (trivial) display bug which will be obvious
+ * when you run it. It's probably easy to fix but I haven't tried,
+ * sorry.
+ */
+bool_ get_magic_power(int *sn, magic_power *powers, int max_powers,
+ void (*power_info)(char *p, int power), int plev, int cast_stat)
+{
+ int i;
+
+ int num = 0;
+
+ int y = 2;
+
+ int x = 18;
+
+ int minfail = 0;
+
+ int chance = 0;
+
+ int info;
+
+ char choice;
+
+ char out_val[160];
+
+ char comment[80];
+
+ cptr p = "power";
+
+ magic_power spell;
+
+ bool_ flag, redraw;
+
+
+ /* Assume cancelled */
+ *sn = ( -1);
+
+ /* Get the spell, if available */
+ if (repeat_pull(sn))
+ {
+ /* Verify the spell */
+ if (powers[*sn].min_lev <= plev)
+ {
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Count number of powers that satisfies minimum plev requirement */
+ for (i = 0; i < max_powers; i++)
+ {
+ if (powers[i].min_lev <= plev)
+ {
+ num++;
+ }
+ }
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78, "(%^ss %c-%c, *=List, ESC=exit, %c-%c=Info) Use which %s? ",
+ p, I2A(0), I2A(num - 1), toupper(I2A(0)), toupper(I2A(num - 1)), p);
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ char psi_desc[80];
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Display a list of spells */
+ prt("", 1, x);
+ prt("", y, x);
+ put_str("Name", y, x + 5);
+ put_str("Lv Mana Fail Info", y, x + 35);
+
+ /* Dump the spells */
+ for (i = 0; i < max_powers; i++)
+ {
+ /* Access the spell */
+ spell = powers[i];
+ if (spell.min_lev > plev) break;
+
+ chance = spell.fail;
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[cast_stat]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[cast_stat]];
+
+ /* Failure rate */
+ chance = clamp_failure_chance(chance, minfail);
+
+ /* Get info */
+ power_info(comment, i);
+
+ /* Dump the spell --(-- */
+ strnfmt(psi_desc, 80, " %c) %-30s%2d %4d %3d%%%s",
+ I2A(i), spell.name,
+ spell.min_lev, spell.mana_cost, chance, comment);
+ prt(psi_desc, y + i + 1, x);
+ }
+
+ /* Clear the bottom line */
+ prt("", y + i + 1, x);
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ /* Note verify */
+ info = (isupper(choice));
+
+ /* Lowercase */
+ if (info) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Save the spell index */
+ spell = powers[i];
+
+ /* Provides info */
+ if (info)
+ {
+ c_prt(TERM_L_BLUE, spell.desc, 1, 0);
+
+ /* Restore the screen */
+ inkey();
+ Term_load();
+ character_icky = FALSE;
+ continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ }
+ character_icky = FALSE;
+
+ /* Abort if needed */
+ if (!flag) return (FALSE);
+
+ /* Save the choice */
+ (*sn) = i;
+
+
+ repeat_push(*sn);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'mindcrafter'.
+ */
+void do_cmd_mindcraft(void)
+{
+ int n = 0, b = 0;
+
+ int chance;
+
+ int dir;
+
+ int minfail = 0;
+
+ int plev = get_skill(SKILL_MINDCRAFT);
+
+ magic_power spell;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, mindcraft_powers, MAX_MINDCRAFT_POWERS,
+ mindcraft_info, plev, A_WIS)) return;
+
+ spell = mindcraft_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (get_skill(SKILL_MINDCRAFT) - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_WIS]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_WIS]];
+
+ /* Failure rate */
+ chance = clamp_failure_chance(chance, minfail);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+
+ msg_format("You failed to concentrate hard enough!");
+
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (chance / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+ if (b < 5)
+ {
+ msg_print("Oh, no! Your mind has gone blank!");
+ lose_all_info();
+ }
+ else if (b < 15)
+ {
+ msg_print("Weird visions seem to dance before your eyes...");
+ set_image(p_ptr->image + 5 + randint(10));
+ }
+ else if (b < 45)
+ {
+ msg_print("Your brain is addled!");
+ set_confused(p_ptr->confused + randint(8));
+ }
+ else if (b < 90)
+ {
+ set_stun(p_ptr->stun + randint(8));
+ }
+ else
+ {
+ /* Mana storm */
+ msg_print("Your mind unleashes its power in an uncontrollable storm!");
+ project(1, 2 + plev / 10, p_ptr->py, p_ptr->px, plev * 2,
+ GF_MANA, PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM);
+ p_ptr->csp = MAX(0, p_ptr->csp - plev * MAX(1, plev / 10));
+ }
+ }
+ }
+
+ /* Successful spells */
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Precog */
+ case 0:
+ {
+ /* Magic mapping */
+ if (plev > 44)
+ {
+ wiz_lite();
+ }
+ else if (plev > 19)
+ {
+ map_area();
+ }
+
+ /* Detection */
+ if (plev < 30)
+ {
+ b = detect_monsters_normal(DEFAULT_RADIUS);
+ if (plev > 14) b |= detect_monsters_invis(DEFAULT_RADIUS);
+ if (plev > 4) b |= detect_traps(DEFAULT_RADIUS);
+ }
+ else
+ {
+ b = detect_all(DEFAULT_RADIUS);
+ }
+
+ /* Telepathy */
+ if (plev > 24)
+ {
+ set_tim_esp(p_ptr->tim_esp + plev);
+
+ /* If plvl >= 40, we should have permanent ESP */
+ }
+
+ if (!b) msg_print("You feel safe.");
+
+ break;
+ }
+
+ /* Mindblast */
+ case 1:
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ if (randint(100) < plev * 2)
+ {
+ fire_beam(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)));
+ }
+ else
+ {
+ fire_ball(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)), 0);
+ }
+
+ break;
+ }
+
+ /* Minor displace */
+ case 2:
+ {
+ if (plev < 25)
+ {
+ teleport_player(10);
+ }
+ else
+ {
+ int ii, ij;
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("Not on special levels!");
+ break;
+ }
+
+ msg_print("You open a Void Jumpgate. Choose a destination.");
+
+ if (!tgt_pt(&ii, &ij)) return;
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) ||
+ (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2 + (p_ptr->to_s*3)) ||
+ (rand_int(plev * plev / 2) == 0))
+ {
+ msg_print("You fail to exit the void correctly!");
+ p_ptr->energy -= 100;
+ get_pos_player(10 + p_ptr->to_s / 2, &ij, &ii);
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN);
+ cave_set_feat(ij, ii, FEAT_BETWEEN);
+ cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8);
+ cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8);
+ }
+
+ break;
+ }
+
+ /* Major displace */
+ case 3:
+ {
+ if (plev > 29) banish_monsters(plev);
+ teleport_player(plev * 5);
+
+ break;
+ }
+
+ /* Domination */
+ case 4:
+ {
+ if (plev < 30)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_DOMINATION, dir, plev, 0);
+ }
+ else
+ {
+ charm_monsters(plev * 2);
+ }
+
+ break;
+ }
+
+ /* Fist of Force --- not 'true' TK */
+ case 5:
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_SOUND, dir, damroll(8 + ((plev - 5) / 4), 8),
+ (plev > 20 ? (plev - 20) / 8 + 1 : 0));
+
+ break;
+ }
+
+ /* Character Armour */
+ case 6:
+ {
+ set_shield(p_ptr->shield + plev, plev, 0, 0, 0);
+ if (plev > 14) set_oppose_acid(p_ptr->oppose_acid + plev);
+ if (plev > 19) set_oppose_fire(p_ptr->oppose_fire + plev);
+ if (plev > 24) set_oppose_cold(p_ptr->oppose_cold + plev);
+ if (plev > 29) set_oppose_elec(p_ptr->oppose_elec + plev);
+ if (plev > 34) set_oppose_pois(p_ptr->oppose_pois + plev);
+
+ break;
+ }
+
+ /* Psychometry */
+ case 7:
+ {
+ ident_spell();
+ break;
+ }
+
+ /* Mindwave */
+ case 8:
+ {
+ msg_print("Mind-warping forces emanate from your brain!");
+ if (plev < 25)
+ {
+ project(0, 2 + plev / 10, p_ptr->py, p_ptr->px,
+ (plev*3) / 2, GF_PSI, PROJECT_KILL);
+ }
+ else
+ {
+ (void)mindblast_monsters(plev * ((plev - 5) / 10 + 1));
+ }
+
+ break;
+ }
+
+ /* Adrenaline */
+ case 9:
+ {
+ set_afraid(0);
+ set_stun(0);
+ hp_player(plev);
+
+ b = 10 + randint((plev * 3) / 2);
+
+ if (plev < 35)
+ {
+ set_hero(p_ptr->hero + b);
+ }
+ else
+ {
+ set_shero(p_ptr->shero + b);
+ }
+
+ if (!p_ptr->fast)
+ {
+ /* Haste */
+ (void)set_fast(b, plev / 5);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + b, plev / 5);
+ }
+
+ break;
+ }
+
+ /* Psychic Drain */
+ case 10:
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ b = damroll(plev / 2, 6);
+
+ if (fire_ball(GF_PSI_DRAIN, dir, b, 0 + (plev - 25) / 10))
+ {
+ p_ptr->energy -= randint(150);
+ }
+
+ break;
+ }
+
+ /* Telekinesis */
+ case 11:
+ {
+ msg_print("A wave of pure physical force radiates out from your body!");
+ project(0, 3 + plev / 10, p_ptr->py, p_ptr->px,
+ plev * (plev > 39 ? 4 : 3), GF_TELEKINESIS,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID);
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage WIS (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool_ perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your mind!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_WIS, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+static int get_mimic_chance(int mimic)
+{
+ s32b chance;
+
+ call_lua("get_mimic_info", "(d,s)", "d", mimic, "level", &chance);
+ chance *= 3;
+
+ chance -= get_skill_scale(SKILL_MIMICRY, 150);
+ chance -= 3 * adj_mag_stat[p_ptr->stat_ind[A_DEX]];
+
+ /* Return the chance */
+ return clamp_failure_chance(chance, 2);
+}
+
+
+void do_cmd_mimic_lore()
+{
+ int fail;
+
+ object_type *o_ptr;
+
+
+ /* Player has to be able to see */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* No transformations when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+
+ /* Already in a mimic form -- Allow cancelling */
+ if (p_ptr->mimic_form)
+ {
+ msg_print("You morph back to your natural form!");
+
+ set_mimic(0, 0, 0);
+ }
+
+ /* Not in mimic forms -- Allow transformations */
+ else
+ {
+ o_ptr = &p_ptr->inventory[INVEN_OUTER];
+
+ if ((o_ptr->tval != TV_CLOAK) || (o_ptr->sval != SV_MIMIC_CLOAK))
+ {
+ msg_print("You are not wearing any cloaks of mimicry.");
+ return;
+ }
+
+ /* Calculate failure rate */
+ fail = get_mimic_chance(o_ptr->pval2);
+
+ if (fail > 75)
+ {
+ msg_print("You feel uneasy with this shape-change.");
+
+ if (!get_check("Try it anyway? ")) return;
+ }
+
+ /* Fumble */
+ if (randint(100) < fail)
+ {
+ msg_print("Your shape-change goes horribly wrong!");
+
+ if (randint(100) < p_ptr->skill_sav)
+ {
+ msg_print("You manage to wrest your body back under control.");
+ return;
+ }
+
+ set_mimic(30, resolve_mimic_name("Abomination"), get_skill(SKILL_MIMICRY));
+ }
+
+ /* Success */
+ else
+ {
+ set_mimic(k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000), o_ptr->pval2, get_skill(SKILL_MIMICRY));
+ }
+ }
+
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+}
+
+static bool_ mimic_forbid_travel(char *fmt)
+{
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ if(value > 0 && (att & CLASS_ARMS || att & CLASS_LEGS))
+ {
+ msg_print("You had best not travel with your extra limbs.");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'mimic'.
+ */
+void do_cmd_mimic(void)
+{
+ int n = 0, b = 0;
+
+ int fail;
+
+ int minfail = 0;
+
+ int plev = get_skill(SKILL_MIMICRY);
+
+ magic_power spell;
+
+ static bool_ added_hooks = FALSE;
+ if(!added_hooks)
+ {
+ add_hook(HOOK_FORBID_TRAVEL, mimic_forbid_travel, "mimic_forbid_travel");
+ added_hooks = TRUE;
+ }
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, mimic_powers, MAX_MIMIC_POWERS, mimic_info,
+ plev, A_DEX)) return;
+
+ spell = mimic_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ fail = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ fail -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ fail -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ fail += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]];
+
+ /* Minimum failure rate */
+ if (fail < minfail) fail = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) fail += 25;
+ else if (p_ptr->stun) fail += 15;
+
+ /* Always a 5 percent chance of working */
+ if (fail > 95) fail = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < fail)
+ {
+ if (flush_failure) flush();
+
+ msg_format("You failed to concentrate hard enough!");
+
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (fail / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+
+ if (b < 5)
+ {
+ msg_print("Oh, no! Your mind has gone blank!");
+ lose_all_info();
+ }
+ else if (b < 15)
+ {
+ msg_print("Weird visions seem to dance before your eyes...");
+ set_image(p_ptr->image + 5 + randint(10));
+ }
+ else if (b < 45)
+ {
+ msg_print("Your brain is addled!");
+ set_confused(p_ptr->confused + randint(8));
+ }
+ else
+ {
+ set_stun(p_ptr->stun + randint(8));
+ }
+ }
+ }
+
+ /* Successful spells */
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Mimic */
+ case 0:
+ {
+ do_cmd_mimic_lore();
+
+ break;
+ }
+
+ /* Invisibility */
+ case 1:
+ {
+ int ii = 10 + plev + randint(20) + p_ptr->to_s;
+
+ set_invis(p_ptr->tim_invisible + ii, 50);
+ set_tim_invis(p_ptr->tim_invisible + ii);
+
+ break;
+ }
+
+ /* Legs Mimicry */
+ case 2:
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ /* Clear useless things */
+ att &= ~(CLASS_ARMS);
+ att &= ~(CLASS_WALL);
+
+ if (att & CLASS_LEGS)
+ {
+ value += 50 + randint(50 + (2 * plev));
+ }
+ else
+ {
+ msg_print("You mimic a new pair of legs.");
+
+ value = 50 + randint(50 + (2 * plev));
+ att |= (CLASS_LEGS);
+ }
+
+ if (value > 10000) value = 10000;
+
+ p_ptr->mimic_extra = att + (value << 16);
+ p_ptr->update |= (PU_BODY);
+
+ break;
+ }
+
+ /* Wall Mimicry */
+ case 3:
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ /* Clear useless things */
+ att &= ~(CLASS_ARMS);
+ att &= ~(CLASS_LEGS);
+
+ if (att & CLASS_WALL)
+ {
+ value += 50 + randint(50 + (2 * plev));
+ }
+ else
+ {
+ msg_print("You grow an affinity for walls.");
+
+ value = 50 + randint(50 + (2 * plev));
+ att |= (CLASS_WALL);
+ }
+
+ if (value > 10000) value = 10000;
+
+ p_ptr->mimic_extra = att + (value << 16);
+ p_ptr->update |= (PU_BODY);
+
+ break;
+ }
+
+ case 4: /* Arms Mimicry */
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ /* Clear useless things */
+ att &= ~(CLASS_LEGS);
+ att &= ~(CLASS_WALL);
+
+ if (att & CLASS_ARMS)
+ {
+ value += 50 + randint(50 + (2 * plev));
+ }
+ else
+ {
+ msg_print("You mimic a new pair of arms.");
+
+ value = 50 + randint(50 + (2 * plev));
+ att |= (CLASS_ARMS);
+ }
+
+ if (value > 10000) value = 10000;
+
+ p_ptr->mimic_extra = att + (value << 16);
+ p_ptr->update |= (PU_BODY);
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage WIS (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool_ perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your mind!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_DEX, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'beastmaster'.
+ */
+void do_cmd_beastmaster(void)
+{
+ int plev = p_ptr->lev, i, num;
+
+ monster_type *m_ptr;
+
+
+ /* Process the monsters (backwards) */
+ num = 0;
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+
+ if (m_ptr->status == MSTATUS_PET)
+ {
+ num++;
+ }
+ }
+
+ if (num < plev * 2)
+ {
+ /* XXX XXX */
+ if (rand_int(80-(plev) - p_ptr->stat_use[5]-p_ptr->to_s) < 20)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, plev, rand_int(plev / 2), FALSE);
+ }
+ }
+ else msg_print("You can't summon more pets");
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+/*
+ * Set of variables and functions to create an artifact
+ */
+
+
+/* LOG2 is a constant (compile-time) method of converting a single
+ * set bit into a number. Works well, but for variable (runtime)
+ * expressions, use a loop instead.. much smaller code*/
+#define LOG2(x) ( (x) & 0xFFFF? BLOG16(x) : BLOG16((x)>>16) + 16 )
+#define BLOG16(x) ( (x) & 0xFF ? BLOG8(x) : BLOG8 ((x)>>8 ) + 8 )
+#define BLOG8(x) ( (x) & 0xF ? BLOG4(x) : BLOG4 ((x)>>4 ) + 4 )
+#define BLOG4(x) ( (x) & 0x3 ? BLOG2(x) : BLOG2 ((x)>>2 ) + 2 )
+#define BLOG2(x) ( (x) & 0x1 ? 0 : 1 )
+
+int flags_select[32*5];
+int activation_select;
+
+/* Return true if the player is wielding the philosopher's stone
+ */
+bool_ alchemist_has_stone(void)
+{
+ if (p_ptr->inventory[INVEN_LITE].name1 == 209)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*
+ Display a group of flags from a_select flags, and return
+ the number of flags displayed (even invisible ones)
+ */
+int show_flags(byte group, int pval)
+{
+ int i, x, color = TERM_WHITE;
+ int items = 0;
+
+ char ttt[80];
+
+ Term_clear();
+
+ group++; /* Adjust - no zero group */
+
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ {
+ if (a_select_flags[i].group != group)
+ continue;
+
+ if (a_select_flags[i].xp == 0)
+ break;
+ else
+ {
+ sprintf(ttt, "%c) %s",
+ (items < 26) ? I2A(items) : ('0' + items - 26),
+ al_name + a_select_flags[i].desc);
+ if ( wizard || alchemist_has_stone())
+ sprintf(ttt, "%c) %s (exp %ld)",
+ (items < 26) ? I2A(items) : ('0' + items - 26),
+ al_name + a_select_flags[i].desc,
+ (long int) a_select_flags[i].xp);
+
+ /* Note: Somebody is VERY clever, and it wasn't me. Text printed as
+ * TERM_DARK is actually printed as TERM_BLUE *SPACES* to prevent the
+ * player from using a 'cut-and-paste' enabled terminal to see
+ * what he shouldn't. Thus, simply setting the color to TERM_DARK
+ * will entirely prevent the unspoiled player from knowing that it's
+ * even possible. */
+
+ switch (flags_select[i])
+ {
+ case 1:
+ color = TERM_YELLOW;
+ break; /* Flag was set by the player (just now)*/
+ case 0:
+ color = TERM_WHITE;
+ break; /* This flag can be set, player is 'aware' of it*/
+ case - 1:
+ color = TERM_L_GREEN;
+ break; /* Flag is already set*/
+ case - 2:
+ color = TERM_DARK;
+ break; /* Invisible option */
+ case - 3:
+ color = TERM_RED;
+ break; /* Flag is set, but player isn't 'aware' of it */
+ case - 4:
+ color = TERM_L_DARK;
+ break; /* Flag is not set, player is 'aware', but it's beyond thier skill */
+ default:
+ color = TERM_DARK;
+ break; /* Just in Case*/
+ }
+ }
+ /* For alchemists who have the stone, at least show all the flags... */
+ if ((alchemist_has_stone() || wizard) && color == TERM_DARK)
+ color = TERM_BLUE;
+
+ if (items < 16) x = 5;
+ else x = 45;
+ c_prt(color, ttt, ((items < 16) ? items : items - 16) + 5, x);
+ items++;
+
+ }
+ return items;
+}
+
+void show_levels(void)
+{
+ Term_clear();
+ c_prt(TERM_WHITE, "[a] Stats, sustains, luck, speed, vision, etc. ", 3, 10);
+ c_prt(TERM_WHITE, "[b] Misc. (Auras, light, see invis, etc) ", 4, 10);
+ c_prt(TERM_WHITE, "[c] Weapon Branding ", 5, 10);
+ c_prt(TERM_WHITE, "[d] Resistances and Immunities ", 6, 10);
+ c_prt(TERM_WHITE, "[e] ESP and Curses ", 7, 10);
+ c_prt(TERM_WHITE, "[f] Activation ", 8, 10);
+ c_prt(TERM_DARK , "[g] Abilities Gained ", 9, 10);
+ c_prt(TERM_WHITE, "[h] Display Required Essences and items ", 10, 10);
+ c_prt(TERM_WHITE, "[i] Done! Finalize and commit changes. ", 11, 10);
+ /*No need to return anything - if the valid selections change, it'll be a code level change.*/
+}
+
+s32b get_flags_exp(int pval, int oldpval)
+{
+ int i;
+ s32b exp = 0;
+
+ for (i = 0 ; a_select_flags[i].group ; i++ )
+ {
+ if (a_select_flags[i].xp == 0)
+ break;
+ else
+ {
+ if ( a_select_flags[i].group <= 5 && flags_select[i] )
+ {
+ s32b xp = a_select_flags[i].xp;
+ int factor = 1, oldfactor = 0;
+
+ /* don't even look at flags which the user can't set
+ * because they also can't change the pval when a pval-
+ * dependant flag is set, flags which they can't set
+ * cannot effect the exp in any way, whether their set or not
+ */
+ if ( flags_select[i] < -1 )
+ continue;
+ if ( flags_select[i] == -1 )
+ oldfactor = 1;
+
+ if (a_select_flags[i].pval)
+ {
+ /* (1/4)x^2 + x
+ * I wanted something smaller than x^2 or x^x
+ * this is because although a ring of speed +10 is
+ * more than 10 times better than a ring of speed +1,
+ * I don't think it's 100 times better. More like 30.
+ * this function yields:
+ * 1=1 * 2=3 * 3=5 * 4=8 * 5=11 * 6=15 * 7=21
+ * 8=24 * 9=29 * 10=35 * 11=41 * 12=48 * 13=55
+ * 14=63 * 15=71 * 20=120 * 25=181 * 30=255
+ * which I think is acceptable.
+ * briefly, to get a +30 speed ring, it would be:
+ * 255*50000 or over 12 million experience
+ * points. For reference, a level 50 human requires
+ * 5 million xp. I'm sure it's doable, but it'd be
+ * *HARD*
+ * a speed+10 artifact would require 1.75 million.
+ * much more doable, but not too easily.
+ */
+ factor = (pval * pval / 4 + pval);
+ if ( flags_select[i] == -1 )
+ {
+ oldfactor = oldpval * oldpval / 4 + oldpval;
+ }
+ }
+ exp += xp * factor - xp * oldfactor;
+ }
+ if ( a_select_flags[i].group == 88 && a_select_flags[i].flag == -activation_select )
+ {
+ exp += a_select_flags[i].xp;
+ }
+ }
+ }
+ if ( alchemist_has_stone() ) exp = exp / 4;
+ return exp;
+}
+
+/* returns the 'real quantity' of items needed to empower
+ * a particular flag to a particular pval.
+ * Note that this routine returns zero for any flag that
+ * doesn't require some sort of action.
+ */
+int calc_rqty(int i, int pval, int oldpval)
+{
+ /* return 0 if flag is greater than size of flags_select && ! activation */
+ if ( a_select_flags[i].group > 5 )
+ {
+ if ( activation_select == a_select_flags[i].flag)
+ return 1;
+ else
+ return 0;
+ }
+
+ /* return 0 if the flag wasn't set */
+ if ( flags_select[i] < -1 || flags_select[i] == 0 )
+ return 0;
+
+ /* Return change in pval if the flag was already set */
+ if ( flags_select[i] == -1 && a_select_flags[i].pval)
+ return pval - oldpval;
+
+ /* Return pval if the flag will be set this time */
+ else if ( a_select_flags[i].pval )
+ return pval;
+
+ /* Return 0 if the flag is unknown */
+ else if ( flags_select[i] == -1 )
+ return 0;
+ return 1;
+}
+
+/* Handle the various items that creating artifacts requires.
+ * Mode = 0 to print a description,
+ * 1 to use up the items
+ * -1 to check to see if the items exist
+ * Note that this function is called ONLY from the
+ * other artifact item helper function.
+ */
+
+
+int check_artifact_items(int pval, int oldpval, int mode)
+{
+ int i, j, k, row = 1 , col = 15, rqty, orqty, trqty;
+ bool_ good = TRUE;
+ int temporary = -1;
+ char ch;
+
+ /* For temporary items, waive the item requirements,
+ * except for the corpse... */
+ for ( j = 0 ; a_select_flags[j].group ; j++)
+ if (a_select_flags[j].flag == 4*32 && flags_select[j] == 1 )
+ temporary = j;
+ /* Check for enough items */
+ for (i = 0; a_select_flags[i].group ; i++)
+ {
+ /* For temporary items, ignore
+ everything except the one item
+ */
+ if (temporary != -1 && i != temporary)
+ continue;
+
+ /* Calc quantity is done per flag, because
+ some have a pval, some don't, some where already
+ set at pval=2, etc
+ */
+ rqty = orqty = calc_rqty(i, pval, oldpval);
+
+ /* If no item is associated with this flag,
+ or this flag wasn't set or didn't change */
+ if ( !a_select_flags[i].rtval || !rqty)
+ continue;
+
+ for ( k = 0 ; k < INVEN_WIELD ; k++ )
+ {
+ object_type *o_ptr = &p_ptr->inventory[k];
+
+ /* Note here that an rsval of -1 (which is read is 0xff
+ for a byte..) matches anything. */
+ if (o_ptr->tval == a_select_flags[i].rtval
+ && (o_ptr->sval == a_select_flags[i].rsval
+ || a_select_flags[i].rsval == (byte) - 1 ) )
+ {
+ /* Corpse validation is COMPLICATED!
+ * But at least we don't have to do this twice.
+ */
+ if ( a_select_flags[i].rtval == TV_CORPSE )
+ {
+ bool_ itemgood = TRUE;
+
+ /*Specified race not this one */
+ if ( o_ptr->pval2 != a_select_flags[i].rpval && a_select_flags[i].rpval)
+ continue;
+
+ /* Race flag (any monster who...)*/
+ for ( j = 0 ; !a_select_flags[i].rpval && a_select_flags[i].rflag[j] && j < 6 && itemgood ; j++)
+ {
+ int flag = a_select_flags[i].rflag[j] / 32;
+ u32b mask = 1 << (a_select_flags[i].rflag[j] % 32);
+
+ switch (flag)
+ {
+ case 0:
+ if ( !(r_info[o_ptr->pval2].flags1 & mask) ) itemgood = FALSE;
+ break;
+ case 1:
+ if ( !(r_info[o_ptr->pval2].flags2 & mask) ) itemgood = FALSE;
+ break;
+ case 2:
+ if ( !(r_info[o_ptr->pval2].flags3 & mask) ) itemgood = FALSE;
+ break;
+ case 3:
+ if ( !(r_info[o_ptr->pval2].flags4 & mask) ) itemgood = FALSE;
+ break;
+ case 4:
+ if ( !(r_info[o_ptr->pval2].flags5 & mask) ) itemgood = FALSE;
+ break;
+ case 5:
+ if ( !(r_info[o_ptr->pval2].flags6 & mask) ) itemgood = FALSE;
+ break;
+ case 6:
+ if ( !(r_info[o_ptr->pval2].flags7 & mask) ) itemgood = FALSE;
+ break;
+ case 7:
+ if ( !(r_info[o_ptr->pval2].flags8 & mask) ) itemgood = FALSE;
+ break;
+ case 8:
+ if ( !(r_info[o_ptr->pval2].flags9 & mask) ) itemgood = FALSE;
+ break;
+ default:
+ msg_print("This code should never be hit!");
+ }
+ }
+ if ( ! itemgood )
+ continue;
+
+ }
+ /* Validate pval of good item */
+ else if ( a_select_flags[i].rpval)
+ {
+ /* Must have matching signs */
+ if ( (o_ptr->pval < 0) != (a_select_flags[i].rpval < 0))
+ continue;
+ /* Must be greater than */
+ if ( abs(o_ptr->pval) < abs(a_select_flags[i].rpval))
+ continue;
+ }
+
+ trqty = MIN(o_ptr->number, rqty);
+ rqty -= trqty;
+
+ if ( mode == 1 )
+ {
+ inc_stack_size_ex(k, -trqty, NO_OPTIMIZE, DESCRIBE);
+ }
+ }/* if p_ptr->inventory item is acceptable */
+
+ } /*end of looping through the p_ptr->inventory*/
+
+ if (rqty)
+ {
+ good = FALSE;
+ /* Oops, we didn't have enough of this object
+ when actually creating the artifact.
+ unset this flag
+ */
+ if ( mode == 1 )
+ {
+ flags_select[i] = -4;
+ }
+ /* we only return false for mode -1,
+ * for mode 0 we display stuff, and for
+ * mode 1 we want to continue destroying things
+ * even if the player is missing one small item,
+ * because there's no way to change things now.
+ * We may have already destroyed a unique corpse,
+ * or some other hard-to-find item.
+ */
+ if ( mode == -1 )
+ return FALSE;
+ }
+
+ /* Display a description of the required object, if needed */
+ /* Note that the tests for good items HAVE to be in a different
+ place, because otherwise we don't know how many the player
+ has, as opposed to how many they need.
+ */
+ if ( mode == 0 )
+ {
+ char *o_name = al_name + a_select_flags[i].item_desc;
+ if (orqty > 1 && a_select_flags[i].pval && a_select_flags[i].item_descp)
+ o_name = al_name + a_select_flags[i].item_descp;
+
+ if ( rqty )
+ {
+ if ( orqty > 1 )
+ c_prt(TERM_RED, format(" you are missing %d of the %d %s", rqty, orqty, o_name), row++, col);
+ else if ( is_a_vowel(o_name[0]))
+ c_prt(TERM_RED, format(" you are missing an %s", o_name), row++, col);
+ else
+ c_prt(TERM_RED, format(" you are missing a %s", o_name), row++, col);
+ }
+ else
+ {
+ if ( orqty > 1 )
+ c_prt(TERM_GREEN, format(" you have the %d %s", orqty, o_name), row++, col);
+ else if ( is_a_vowel(o_name[0]))
+ c_prt(TERM_GREEN, format(" you have an %s", o_name), row++, col);
+ else
+ c_prt(TERM_GREEN, format(" you have a %s", o_name), row++, col);
+ }
+
+ if ( row > 21 )
+ {
+ row = 1;
+ if (!good)
+ (void)get_com("You are missing some items:", &ch);
+ else
+ (void)get_com("You have these needed items on hand:", &ch);
+ }
+
+ }
+
+ } /* End of group associated with this a_select_flags entry */
+
+ if ( mode == 0 )
+ {
+ while ( row < 22 )
+ c_prt(TERM_GREEN, " ", row++, col);
+ if (!good)
+ (void)get_com("You are missing some items:", &ch);
+ else
+ (void)get_com("You have these needed items on hand:", &ch);
+ }
+ return good;
+}
+
+/* Display a list of required essences,
+ * and/or use up the essences. */
+bool_ artifact_display_or_use(int pval, int oldpval, bool_ use)
+{
+ int essence[MAX_BATERIE_SVAL];
+ int essenceh[MAX_BATERIE_SVAL];
+ int al_idx, i, j, k;
+ bool_ enough;
+
+ /* Temporary Items require only one item, and no essences. */
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ if ( a_select_flags[i].flag == 32*4)
+ {
+ if ( use )
+ return check_artifact_items(pval, oldpval, 1);
+ else
+ return check_artifact_items(pval, oldpval, 0);
+ }
+
+ for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++ )
+ essence[i] = essenceh[i] = 0;
+
+ /* Accumulate a list of required essences */
+ for ( al_idx = 0; al_idx < max_al_idx ; al_idx++ )
+ if ( alchemist_recipes[al_idx].tval == 0 )
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ {
+ int rqty = calc_rqty(i, pval, oldpval);
+
+ /* If the flag isn't being set, rqty will be zero */
+ if ( !rqty)
+ continue;
+
+ if ( alchemist_recipes[al_idx].sval == a_select_flags[i].flag )
+ essence[alchemist_recipes[al_idx].sval_essence] +=
+ alchemist_recipes[al_idx].qty * rqty;
+ }
+
+ /* The essence array now contains a list of all essences
+ * that will be consumed in the creation of this artifact */
+
+ /* Check for existence of required quatities of essences. */
+ for ( i = 0 ; i < INVEN_WIELD ; i++ )
+ {
+ for ( j = 0 ; j < MAX_BATERIE_SVAL ; j++)
+ if ( p_ptr->inventory[i].tval == TV_BATERIE && p_ptr->inventory[i].sval == j + 1)
+ {
+ essenceh[j] += p_ptr->inventory[i].number;
+ }
+ }
+
+ /* Check for enough essences */
+ enough = TRUE;
+ for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++)
+ if ( essenceh[i] < essence[i] )
+ {
+ enough = FALSE;
+ break;
+ }
+
+ /* Check for items */
+ if ( enough )
+ enough = check_artifact_items(pval, oldpval, -1);
+
+
+ /* Display recipe list if they don't have enough, or not enough exp */
+ if (!enough || !use )
+ {
+ int row = 1 , col = 15;
+ bool_ good = FALSE;
+ char ch;
+
+ /* display of list of required essences */
+ /* Note: there are only 12 or so essences, so this list
+ * will ALWAYS fit on the screen */
+ for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++)
+ if ( essence[i] )
+ {
+ int missing = -MIN(essenceh[i] - essence[i], 0);
+ good = TRUE;
+ if ( missing )
+ c_prt(TERM_RED, format("%d of the required %d essences of %s",
+ missing, essence[i],
+ k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ),
+ row++, col);
+ else
+ c_prt(TERM_GREEN, format("you have the needed %d essences of %s",
+ essence[i],
+ k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ),
+ row++, col);
+ }
+
+ if (good)
+ {
+ /* blank the bottom row */
+ c_prt(TERM_WHITE, " ", row++, col);
+
+ /* and wait for a key */
+ (void)get_com("You are currently missing:", &ch);
+ }
+
+ /* Display a list of needed items as well */
+ check_artifact_items(pval, oldpval, 0);
+
+ return FALSE;
+ }
+
+ /* If we get to this point in the code, then the player
+ * has the required essences and items in their p_ptr->inventory */
+
+ /* If they do have enough, and they have enough exp, consume them */
+ for (i = 0 ; i < MAX_BATERIE_SVAL ; i++)
+ for ( k = 0 ; k < INVEN_WIELD && essence[i] > 0 ; k++)
+ if (p_ptr->inventory[k].tval == TV_BATERIE
+ && p_ptr->inventory[k].sval == i + 1
+ && essence[i])
+ {
+ int num = p_ptr->inventory[k].number;
+
+ inc_stack_size_ex(k, MAX( -essence[i], -num), NO_OPTIMIZE, DESCRIBE);
+
+ essence[i] -= MIN(num, essence[i]);
+ }
+
+ /* Destroy the items needed */
+ check_artifact_items(pval, oldpval, 1);
+
+ return TRUE;
+}
+
+
+void display_activation_info(int num)
+{
+ object_type forge;
+ int i;
+
+
+ /* find the a_select_flags number of this activation type... */
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ if (a_select_flags[i].group == 88 && a_select_flags[i].flag == -num )
+ break;
+
+ object_wipe(&forge);
+ forge.xtra2 = num;
+ /* Print out various information about this activation... */
+ /* min level, experience, required items (and essences)
+ full description (from activation_aux) */
+ if (wizard)
+ c_prt(TERM_WHITE, format(" number:%d ", num), 5, 5);
+ else
+ c_prt(TERM_WHITE, " ", 5, 5);
+ c_prt(TERM_WHITE, format(" Level:%d ", a_select_flags[i].level), 6, 5);
+ c_prt(TERM_WHITE, format(" Exp :%d ", a_select_flags[i].xp), 7, 5);
+ c_prt(TERM_WHITE, format(" Item :%s ", al_name + a_select_flags[i].item_desc), 8, 5);
+ c_prt(TERM_WHITE, " ", 9, 5);
+ c_prt(TERM_WHITE, format(" %s ", activation_aux(&forge, 0, 0)), 9, 5);
+ c_prt(TERM_WHITE, " ", 10, 5);
+ inkey();
+}
+
+void select_an_activation(void)
+{
+ int i, lev, wid, hgt, begin = 0, sel = 0;
+ u32b max;
+ cptr act_list[150]; /* currently, ~127 hardcoded activations */
+ int act_ref[150];
+ char c;
+ /* How do we want to do this? */
+ /* Ideally, we let them select from a list, which includes all the activations that they've ecountered in any form.
+ Problems with this idea include mainly the lack of any (current) place to store which activations they've seen, and
+ that they'll not get credit for any seen before we start tracking it.
+
+ So - list is everything. If they select one which they're to low-level for
+ or if the explicitly request it, we'll display info about this item.
+ We'll also get our descriptions from the activation_aux(ACT_CONSTANT)
+ function, because they are more complete, and include even lua-scripted ones.
+ msg_print("Since the code to actually let you select one isn't here");
+ msg_print("You will automatically get the activation 'Dawn'");
+ activation_select = ACT_DAWN;
+ */
+
+ /* Build a list of available activations at the player's level */
+ lev = get_skill(SKILL_ALCHEMY);
+ for ( i = max = 0 ; max < (sizeof(act_list) / sizeof(cptr)) && a_select_flags[i].group ; i++)
+ if (a_select_flags[i].group == 88 && a_select_flags[i].level <= lev )
+ {
+ act_ref[max] = -a_select_flags[i].flag; /* Activation number */
+ act_list[max++] = al_name + a_select_flags[i].desc; /* Description */
+ }
+
+ /* Select from that list, using the util.c function display_list to display the scrolled list */
+ /* Note: I think that there is only one other place that uses this function. Should be more! */
+ while (1)
+ {
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ c_prt(TERM_WHITE, "Enter to select, ? for more information, 2 and 8 to scroll ", 0, 0);
+ display_list(1, 0, hgt - 2, wid - 2, "Select an Activation", act_list, max, begin, sel, TERM_L_GREEN);
+
+ c = inkey();
+
+ if (c == ESCAPE) break;
+ else if (c == '8')
+ {
+ sel--;
+ if (sel < 0)
+ {
+ sel = max - 1;
+ begin = max - hgt;
+ if (begin < 0) begin = 0;
+ }
+ if (sel < begin) begin = sel;
+ }
+ else if (c == '2')
+ {
+ sel++;
+ if (sel >= (s32b)max)
+ {
+ sel = 0;
+ begin = 0;
+ }
+ if (sel >= begin + hgt - 1) begin++;
+ }
+ else if (c == '?')
+ {
+ display_activation_info(act_ref[sel]);
+ }
+ else if (c == '\r')
+ {
+ display_activation_info(act_ref[sel]);
+ activation_select = act_ref[sel];
+ return;
+ }
+ }
+ activation_select = 0;
+}
+
+
+/* Consume 'num' magic essences and return true.
+ * If there aren't enough essences, return false */
+
+bool_ magic_essence(int num)
+{
+ int i;
+ int j = 0;
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Count the magic essences */
+ if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC)) j += o_ptr->number;
+ }
+
+ /* Abort if not enough essences. */
+ if (j < num) return FALSE;
+
+ /* Consume them */
+ i = 0;
+ j = num;
+ while (i < INVEN_WIELD)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC))
+ {
+ /* This can lead to invalid object pointer for objects
+ * that come after the magic essences. Therefore, every
+ * artifactable object should come before the essences.
+ */
+ j -= o_ptr->number;
+ inc_stack_size(i, -num);
+ num = j;
+ if (num <= 0) break;
+ /* Stay on this slot; do not increment i. */
+ }
+ else
+ {
+ /* Move on to the next slot. */
+ i++;
+ }
+ }
+
+ /* Sanity check. */
+ if (num > 0)
+ {
+ msg_format("ERROR: Couldn't destroy %d essences!", num);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void do_cmd_create_artifact(object_type *q_ptr)
+{
+ int max, i = 0, j, cur_set = 0, abord = FALSE, done = FALSE;
+ int skill;
+ s32b exp = 0;
+
+ char out_val[160];
+ char choice = 0;
+ bool_ lockpval = FALSE;
+ int pval;
+ int oldpval;
+ energy_use = 100;
+
+ pval = q_ptr->pval;
+ oldpval = pval;
+ skill = get_skill(SKILL_ALCHEMY);
+
+ if ( !pval )
+ pval = 1;
+ /* No activation added on this round */
+ activation_select = 0;
+
+ /* Save the current flags */
+ for (i = 0 ; a_select_flags[i].group ; i++)
+ {
+ if ( a_select_flags[i].flag < 0 || a_select_flags[i].group > 5)
+ continue;
+
+ flags_select[i] = 0;
+
+ switch (a_select_flags[i].flag / 32)
+ {
+ case 0:
+ if (q_ptr->art_flags1 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 1:
+ if (q_ptr->art_flags2 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 2:
+ if (q_ptr->art_flags3 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 3:
+ if (q_ptr->art_flags4 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 4:
+ if (q_ptr->art_flags5 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 5:
+ if (q_ptr->art_esp & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ default:
+ /*This will not be hit, inspite of activations, because of the <= 5 above...*/
+ break;
+ }
+ /*
+ this would learn about ALL flags....
+ if(wizard)
+ alchemist_known_artifacts[a_select_flags[i].flag/32] = 0xffffffffL;
+ */
+
+ /* Set various flags if they haven't *ID*'d an artifact with this flag set.*/
+ if ( !(alchemist_known_artifacts[a_select_flags[i].flag / 32] & (1 << (a_select_flags[i].flag % 32)) ))
+ {
+ /* If this item has an ability that depends on pval which the player
+ * cannot set, don't allow them to change the pval either. */
+ if ( a_select_flags[i].pval && flags_select[i])
+ lockpval = TRUE;
+
+ /* Set the color and set-ablitity of this flag */
+ if ( flags_select[i] )
+ flags_select[i] = -3;
+ else
+ flags_select[i] = -2;
+ continue;
+ }
+ else if ( skill < a_select_flags[i].level )
+ {
+ /* If the alchemist has not passed the skill level for this flag,
+ Set this flag as unsettable.
+ */
+ if ( flags_select[i])
+ lockpval = TRUE;
+ else
+ flags_select[i] = -4;
+ }
+ }
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+
+ /* Everlasting love ... ... nevermind :) */
+ while ( !done && !abord)
+ {
+ c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0);
+
+ /* Display the menu, but don't display it if we just
+ * displayed a message (it erases the screen, creating a blink message */
+ if ( cur_set < 6 || cur_set == 7 )
+ show_levels();
+
+ c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0);
+
+ prt("Enter to accept, Escape to abort", 1, 0);
+
+ abord = !get_com("Play around with which group of powers?[a-g]", &choice);
+
+ if ( choice == ESCAPE)
+ abord = TRUE;
+
+ if ( abord )
+ continue; /*or break, same diff */
+
+ if ( isalpha(choice))
+ {
+ if (isupper(choice))
+ choice = tolower(choice);
+ cur_set = A2I(choice);
+ }
+ else
+ {
+ bell();
+ continue;
+ }
+
+ if ( cur_set == 5 )
+ {
+ if (q_ptr->xtra2 && !activation_select
+ && !get_check("This item already activates! Choose a different activation?")) continue;
+ select_an_activation();
+ exp = get_flags_exp(pval, oldpval);
+ continue;
+ }
+ if ( cur_set == 6 )
+ {
+ msg_print("This option is not available");
+ continue;
+ }
+ if ( cur_set == 7 )
+ {
+ artifact_display_or_use(pval, oldpval, FALSE);
+ continue;
+ }
+ if ( cur_set == 8 )
+ {
+ if (q_ptr->exp - exp < 0)
+ msg_print("Not enough experience for the flags you've selected.");
+ else
+ done = TRUE;
+ continue;
+ }
+
+ if (cur_set < 0 || cur_set > 4 )
+ {
+ bell();
+ continue;
+ }
+
+
+ while (!done && !abord)
+ {
+ /* Chose the flags */
+ exp = 0;
+ max = show_flags(cur_set, pval);
+ exp = get_flags_exp(pval, oldpval);
+ c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0);
+
+ /* Build a prompt (accept all flags) */
+ if (max <= 26)
+ {
+ /* Build a prompt (accept all flags) */
+ strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ",
+ I2A(0), I2A(max - 1));
+ }
+ else
+ {
+ strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ",
+ I2A(0), '0' + max - 27);
+ }
+ c_prt(TERM_L_BLUE, format("Power(I/D to increase/decrease): %d", pval), 3, 0);
+
+ /* Get a spell from the user */
+ while (!(done = !get_com(out_val, &choice)))
+ {
+ if (choice == 'I')
+ {
+ if ( lockpval )
+ {
+ msg_print("You cannot do that - you don't know how!");
+ continue;
+ }
+ if (q_ptr->exp - exp < 0)
+ {
+ msg_print("Not enough experience. Decrease power or deselect flags.");
+ continue;
+ }
+ pval++;
+ break;
+ }
+ else if (choice == 'D')
+ {
+ if ( lockpval )
+ {
+ msg_print("You cannot do that - you don't know how!");
+ continue;
+ }
+ pval--;
+ if (pval < oldpval) pval = oldpval;
+ break;
+ }
+ else if (choice == '\r' || choice == ESCAPE || choice == ' ')
+ {
+ done = TRUE;
+ break;
+ }
+ else if (isalpha(choice))
+ {
+ /* Lowercase */
+ if (isupper(choice)) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ i = D2I(choice) + 26;
+
+ /* Illegal */
+ if (i < 26) i = -1;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= max))
+ {
+ bell();
+ continue;
+ }
+ else
+ {
+ /*Find the i'th flag in group cur_set...*/
+ for ( j = 0 ; a_select_flags[j].group ; j++)
+ if (a_select_flags[j].group == cur_set + 1)
+ if (!i--) break;
+
+ if ( flags_select[j] == -4 )
+ {
+ msg_format("You need at least %d skill in alchemy.",
+ a_select_flags[j].level);
+ continue;
+ }
+ if ( flags_select[j] != 0 && flags_select[j] != 1)
+ {
+ bell();
+ continue;
+ }
+ if (flags_select[j]) flags_select[j] = 0;
+ else if (!flags_select[j])
+ {
+ if (q_ptr->exp - exp < 0)
+ {
+ msg_print("Not enough experience. Decrease power or deselect flags.");
+ continue;
+ }
+ flags_select[j] = 1;
+ }
+ break;
+ }
+ }
+ }/*sub-screen select and redraw loop*/
+ done = FALSE;
+ Term_clear();
+ }/* main screen (flag select screen) select and redraw loop*/
+
+ /* Abort if not enough experience, or no flags added */
+ if ( q_ptr->exp - exp < 0 || exp == 0 )
+ abord = TRUE;
+
+ /* Display the recipe, or use up the essences.
+ * Note that this has to be done before the screen
+ * is restored. This is because it's also called from
+ * within the loop to display the required items. */
+ if ( !abord )
+ if (!artifact_display_or_use(pval, oldpval, TRUE))
+ abord = TRUE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+ /* Return if abort, or missing ingredients */
+ if ( abord )
+ return;
+
+ /* Actually create the artifact */
+ q_ptr->exp -= exp;
+ q_ptr->art_flags4 &= ~TR4_ART_EXP;
+ q_ptr->pval = pval;
+
+ /* Just to be sure */
+ q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD );
+
+ {
+ int now = 0, before = 0;
+ char dummy_name[80];
+ char new_name[80];
+
+ /* Apply the flags */
+ for (i = 0; a_select_flags[i].group ; i++)
+ {
+ if (flags_select[i] < 0)
+ before++;
+ else if ( flags_select[i] == 1)
+ {
+ now++;
+ switch (a_select_flags[i].flag / 32)
+ {
+ case 0:
+ q_ptr->art_flags1 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 1:
+ q_ptr->art_flags2 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 2:
+ q_ptr->art_flags3 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 3:
+ q_ptr->art_flags4 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 4:
+ q_ptr->art_flags5 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 5:
+ q_ptr->art_esp |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ default:
+ msg_print("error: this code can't ever be hit!");
+ }
+ }
+ }
+
+ if ( activation_select )
+ {
+ q_ptr->art_flags3 |= TR3_ACTIVATE;
+ q_ptr->xtra2 = activation_select;
+ }
+
+
+ /* Set the 'show modifier' flag */
+ q_ptr->art_flags3 |= TR3_SHOW_MODS;
+
+ /* For temporary items, set a timeout.
+ * alchemist_skill^2 for now */
+ if ( q_ptr->art_flags5 & TR5_TEMPORARY )
+ {
+ int lev = get_skill(SKILL_ALCHEMY);
+ q_ptr->timeout = lev * lev * 3;
+ }
+
+ /* Describe the new artifact */
+ object_out_desc(q_ptr, NULL, FALSE, TRUE);
+
+
+ /* Name the new artifact */
+ strcpy(dummy_name, "of an Alchemist");
+ if (!(get_string("What do you want to call the artifact? ", dummy_name, 80)))
+ strcpy(new_name, "of an Alchemist");
+ else
+ {
+ if ((strncmp(dummy_name, "of ", 3) == 0) ||
+ (strncmp(dummy_name, "Of ", 3) == 0) ||
+ ((dummy_name[0] == '\'') &&
+ (dummy_name[strlen(dummy_name) - 1] == '\'')))
+ {
+ strcpy(new_name, dummy_name);
+ }
+ else
+ {
+ strcpy(new_name, "called '");
+ strcat(new_name, dummy_name);
+ strcat(new_name, "'");
+ }
+ }
+ /* Identify it fully */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+ q_ptr->ident |= IDENT_STOREB; /* This will be used later on... */
+
+ /* Save the inscription */
+ q_ptr->art_name = quark_add(new_name);
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ done = FALSE;
+ while (!done && get_com("Do you want to let this item continue to gain experience?", &choice))
+ {
+ switch (choice)
+ {
+ case 'y':
+ case 'Y':
+ if (magic_essence(get_skill(SKILL_ALCHEMY)))
+ q_ptr->art_flags4 |= TR4_ART_EXP;
+ else
+ msg_format("Oh, NO! You don't have enough magic essences. You needed %d.", get_skill(SKILL_ALCHEMY));
+ done = TRUE;
+ break;
+ case 'n':
+ case 'N':
+ q_ptr->exp = 0;
+ done = TRUE;
+ break;
+ }
+ }
+
+ /* Cycle through the p_ptr->inventory, and optimize everything.
+ * This wasn't done earlier, because if we had, then
+ * things in the p_ptr->inventory would shift around, and q_ptr
+ * wouldn't point to the right thing. BUT, at this point
+ * we don't need q_ptr anymore, so optimizing the p_ptr->inventory
+ * becomes sane. Sticky bug to figure out, let me tell you.
+ * Note also that this is cycling backwards - this is so
+ * that the same effect doesn't cause us to skip items. */
+ for ( i = INVEN_WIELD - 1 ; i >= 0 ; i-- )
+ inven_item_optimize(i);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+}
+
+/*
+ * Test to see if this tval/sval combo is in the alchemists'
+ * recipes as a createable item. Used to determine if we
+ * should extract from it.
+ */
+bool_ alchemist_exists(int tval, int sval, int ego, int artifact)
+{
+ int al_idx;
+
+ /* To prevent conflicts with recipes for ego-items.
+ * artifact not used, simplifies the loop below. */
+ if ((tval == 1) || artifact)
+ return FALSE;
+
+ /*Search for recipes with this tval/sval combo as the final result*/
+ for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++)
+ {
+ int rtval = alchemist_recipes[al_idx].tval;
+ int rsval = alchemist_recipes[al_idx].sval;
+
+ /* Accept ego wands and staves since ego is extracted last */
+ if (((!ego || tval == TV_WAND || tval == TV_STAFF) && rtval == tval && rsval == sval) ||
+ ( ego && rtval == 1 && rsval == ego))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ * Hook to determine if an object can have things extracted from it.
+ */
+bool_ item_tester_hook_extractable(object_type *o_ptr)
+{
+
+ /* No artifacts */
+ if (artifact_p(o_ptr)) return (FALSE);
+
+ /* No cursed things */
+ if (cursed_p(o_ptr)) return (FALSE);
+
+ /* If we REALLY wanted to rebalance alchemists,
+ * we'd test for 'fully identified this object kind' here.
+ */
+
+ return ((o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != 0)
+ || alchemist_exists(o_ptr->tval, o_ptr->sval, o_ptr->name2, o_ptr->name1));
+}
+
+/*
+ * Hook to determine if an object is empowerable (NOT rechargeable)
+ */
+bool_ item_tester_hook_empower(object_type *o_ptr)
+{
+ int sval = -1;
+ int lev = get_skill(SKILL_ALCHEMY);
+ /* after level 25, can empower ego items to create artifacts
+ * and double ego items.
+ * after level 50, can empower artifacts to create powerful artifacts
+ */
+
+ /* Never Empower a cursed item */
+ if ( cursed_p(o_ptr))
+ {
+ return FALSE;
+ }
+
+ /* Allow finalizing a self created artifact */
+ if (artifact_p(o_ptr)
+ && (o_ptr->art_flags4 & TR4_ART_EXP)
+ && !(o_ptr->art_flags4 & TR4_ULTIMATE))
+ return TRUE;
+
+ switch ( o_ptr->tval)
+ {
+ /* Empowerable objects: Traditional alchemist stuff */
+ case TV_WAND:
+ sval = SV_WAND_NOTHING;
+ break;
+ case TV_RING:
+ sval = SV_RING_NOTHING;
+ break;
+ case TV_STAFF:
+ sval = SV_STAFF_NOTHING;
+ break;
+ case TV_BOTTLE:
+ sval = 1;
+ break;
+ case TV_AMULET:
+ sval = SV_AMULET_NOTHING;
+ break;
+ case TV_SCROLL:
+ sval = SV_SCROLL_NOTHING;
+ break;
+ case TV_ROD:
+ sval = SV_ROD_NOTHING;
+ break;
+ case TV_ROD_MAIN:
+ sval = -1;
+ break;
+ case TV_BOOK:
+ sval = -1;
+ break;
+
+ /* Ego item stuff */
+ /* Disallow ego dragon armour before you can create artifacts.*/
+ case TV_DRAG_ARMOR:
+ if ( lev < 25)
+ return FALSE;
+ /* FALL THROUGH! no break here. */
+
+ /* weapons */
+
+ case TV_DAEMON_BOOK:
+ case TV_SWORD:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_AXE:
+ case TV_MSTAFF:
+
+ /* misc other items */
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_INSTRUMENT:
+ case TV_DIGGING:
+ case TV_LITE:
+
+ /* Ammo */
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+
+ /* Armor of various sorts */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+
+ /* Disallow ANY creation of ego items below level 5*/
+ if ( lev < 5)
+ return FALSE;
+
+ /* empowering an ego item creates an artifact or a
+ * double ego item, disallow below level 25 */
+ if ( lev < 25 && o_ptr->name2)
+ return FALSE;
+
+ /* Disallow double-ego and artifact unless the character has
+ * the artifact creation ability. */
+ if (!has_ability(AB_CREATE_ART) &&
+ (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b)))
+ return FALSE;
+
+ /* Otherwise... */
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+ /* Return to the traditional alchemist objects.
+ * All ego items and artifacts returning TRUE are accepted as artifactable
+ * at level 25. If we want double ego non wieldable items (Fireproof Staff
+ * of Plenty) the artifactable test in do_cmd_alchemist() must be changed,
+ * e.g. checking if the item is wearable.
+ * For now, we disallow non-wearable ego-items and artifacts here.
+ */
+
+ if ((o_ptr->name2 || artifact_p(o_ptr)) &&
+ o_ptr->tval != TV_RING && o_ptr->tval != TV_AMULET)
+ return FALSE;
+
+ /* return true if it's a 'of nothing' item;
+ * does nothing for TV_ROD_MAIN and TV_BOOK
+ */
+ return (sval == o_ptr->sval
+
+ /* or if it's artifactable */
+ || ((lev >= 50 || (lev >= 25 && !artifact_p(o_ptr))) &&
+ (o_ptr->tval == TV_RING || o_ptr->tval == TV_AMULET))
+
+ /* or if it's egoable (note that normal egos start at level 5, wands and such start at 15) */
+ || (!o_ptr->name2 && lev >= 15));
+}
+
+/* Extract a rod tip from a rod */
+void rod_tip_extract(object_type *o_ptr)
+{
+ object_type *q_ptr;
+ object_type forge;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Paranoia, return if it's a rod of nothing */
+ if (o_ptr->pval == SV_ROD_NOTHING)
+ return;
+
+ /* Extract the rod tip */
+ object_prep(q_ptr, lookup_kind(TV_ROD, o_ptr->pval));
+
+ q_ptr->number = o_ptr->number;
+
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Remove it from the rod */
+ o_ptr->pval = SV_ROD_NOTHING;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+}
+
+
+/* Begin & finish an art */
+void do_cmd_toggle_artifact(object_type *o_ptr)
+{
+ char o_name[80];
+
+ if (!(o_ptr->art_flags4 & TR4_ART_EXP))
+ {
+ bool_ okay = TRUE;
+
+ if ( !alchemist_has_stone())
+ {
+ msg_print("Creating an artifact will result into a permanent loss of 10 hp.");
+ if (!get_check("Are you sure you want to do that?")) return;
+ }
+
+ if (!magic_essence(get_skill(SKILL_ALCHEMY)))
+ {
+ msg_format("You need %d magic essences.", get_skill(SKILL_ALCHEMY));
+ return;
+ }
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ if (o_ptr->number > 1)
+ {
+ msg_print("Not enough energy to enchant more than one object!");
+ msg_format("%d of your %s %s destroyed!", (o_ptr->number) - 1, o_name, (o_ptr->number > 2 ? "were" : "was"));
+ o_ptr->number = 1;
+ }
+ okay = TRUE;
+
+ if (!okay) return;
+
+ /* he/she got warned */
+ p_ptr->hp_mod -= 10;
+
+ /* Ok toggle it */
+ o_ptr->art_flags4 |= TR4_ART_EXP;
+ o_ptr->name2 = 0;
+ o_ptr->name2b = 0;
+ o_ptr->art_name = quark_add("Becoming");
+
+ /* Copy the object_kind flags to the artifact flags.
+ * Note that this is only needed so that flags set in the
+ * 'kind' area are visible when finalizing the artifact.
+ */
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ o_ptr->art_flags1 |= f1;
+ o_ptr->art_flags2 |= f2;
+ o_ptr->art_flags3 |= f3;
+ o_ptr->art_flags4 |= f4;
+ o_ptr->art_flags5 |= f5;
+ o_ptr->art_esp |= esp;
+ }
+
+ p_ptr->update |= (PU_HP);
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+ else
+ {
+ do_cmd_create_artifact(o_ptr);
+ }
+}
+
+/*
+ * Test to see if they have all the ingredients to create an item.
+ * (doesn't count base item)
+ * creates 'tocreate' items (may be -1, but no more than that!)
+ * if tocreate=0, will return true if the player has enough
+ * in their p_ptr->inventory to empower that item.
+ */
+bool_ alchemist_items_check(int tval, int sval, int ego, int tocreate, bool_ message)
+{
+ int al_idx, j;
+ bool_ exists = FALSE;
+
+
+ for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ )
+ if ((ego && alchemist_recipes[al_idx].sval == ego
+ && alchemist_recipes[al_idx].tval == 1 )
+ || (!ego && alchemist_recipes[al_idx].sval == sval
+ && alchemist_recipes[al_idx].tval == tval))
+ {
+ exists = TRUE;
+ /* Create the essences */
+ if (tocreate > 0)
+ {
+ object_type forge;
+ object_type *o_ptr = &forge;
+
+ object_wipe(o_ptr);
+ object_prep(o_ptr, lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence));
+ o_ptr->number = alchemist_recipes[al_idx].qty * tocreate;
+ /* Don't bother with apply_magic */
+
+ /* Randomly decrease the number of essences created */
+ if ( randint(3) == 1
+ && randint(52) > get_skill(SKILL_ALCHEMY)
+ && !alchemist_has_stone())
+ o_ptr->number /= randint(2) + 1;
+ if ( o_ptr->number == 0)
+ continue;
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ if (inven_carry_okay(o_ptr))
+ {
+ int i;
+ inven_carry(o_ptr, FALSE);
+ for (i = 0; i < INVEN_WIELD ; i++)
+ if (p_ptr->inventory[i].tval == o_ptr->tval && p_ptr->inventory[i].sval == o_ptr->sval)
+ {
+ if ( message )
+ inven_item_describe(i);
+ break;
+ }
+
+ }
+ else
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+
+ o_ptr->ident |= IDENT_STOREB;
+ }
+ else if ( tocreate < -1)
+ {
+ /*It's not valid to create more than one
+ * thing at a time, so if it's less than -1,
+ * it must be time to display a recipe
+ */
+ msg_format("%d essences of %d",
+ alchemist_recipes[al_idx].qty,
+ al_idx);
+ }
+ else /* Destroy the essences (tocreate == -1)
+ * or check for existence(tocreate == 0)*/
+ {
+ int rqty = alchemist_recipes[al_idx].qty;
+ for (j = 0; j < INVEN_WIELD; j++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[j];
+ if (o_ptr->k_idx
+ && (o_ptr->tval == TV_BATERIE )
+ && (o_ptr->sval == alchemist_recipes[al_idx].sval_essence )
+ && (o_ptr->number >= rqty ))
+ {
+ /* At this point, the item is required, destroy it. */
+ if ( tocreate )
+ {
+ inc_stack_size_ex(j, 0 - rqty, OPTIMIZE, message ? DESCRIBE : NO_DESCRIBE);
+ }
+
+ /* When we find enough of the item, break out of the
+ * 'search through the p_ptr->inventory' loop */
+ break;
+ }
+ }
+ if ( j == INVEN_WIELD)
+ /* This ingredient was not found, cannot do recipe */
+ return FALSE;
+ }/*destroying items, or just checking for existence */
+ }
+ return exists;
+}
+
+/* This function lists all the ingredients
+ * needed to create something.
+ */
+void alchemist_display_recipe(int tval, int sval, int ego)
+{
+ int al_idx;
+ int row = 1, col = 15;
+ char o_name[80];
+ char ch;
+ object_type *o_ptr, forge;
+
+ /* Display the ingredients for a recipe */
+ for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ )
+ if ((ego && alchemist_recipes[al_idx].sval == ego
+ && alchemist_recipes[al_idx].tval == 1 )
+ || (!ego && alchemist_recipes[al_idx].sval == sval
+ && alchemist_recipes[al_idx].tval == tval))
+ {
+ int qty = alchemist_recipes[al_idx].qty;
+ c_prt(TERM_GREEN,
+ format(" %d essence%s %s ", qty,
+ qty > 1 ? "s" : "",
+ k_name + k_info[lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence)].name ),
+ row++, col);
+ }
+
+ c_prt(TERM_WHITE, " ", row++, col);
+
+ if (!ego)
+ {
+ /* Find the name of that object */
+ o_ptr = &forge;
+ object_prep(o_ptr, lookup_kind(tval, sval));
+ o_ptr->name2 = ego;
+ hack_apply_magic_power = -99;
+ apply_magic(o_ptr, get_skill(SKILL_ALCHEMY) * 2, FALSE, FALSE, FALSE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ /* the 0 mode means only the text, leaving off any numbers */
+ object_desc(o_name, o_ptr, FALSE, 0);
+ }
+ else
+ {
+ /* Display the ego item name */
+ strcpy(o_name, e_name + e_info[ego].name);
+ }
+
+ /* Display a short message about it, and wait for a key. */
+ (void)get_com(format("ingredients needed to create a %s", o_name), &ch);
+
+}
+
+/*
+ *
+ * The alchemist_recipe_select was copied from
+ * wiz_create_itemtype
+ * and then changed quite a bit.
+ *
+ */
+
+/*
+ The select array is a simple array of 'use this char to select item x'
+ It has 88 items (three columns of 20 each)
+ selectitem is initilized with the reverse mappings:
+ selectitem[selectchar[x]] == x is always true.
+ */
+char selectchar[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*():;,.<=>[]{}/=?+'~";
+byte selectitem[256];
+
+void strip_and_print(char *str, int color, int num)
+{
+ int row = 2 + (num % 20), col = 40 * (num / 20);
+ int ch, max_len = 0;
+ char buf[80];
+ char *string;
+
+ if (num > 60)
+ {
+ msg_print("Attempting to display too many items!");
+ return;
+ }
+ ch = selectchar[num];
+ if (selectitem[ch] != num)
+ {
+ int i;
+ for ( i = 0 ; i < 256 ; i++)
+ selectitem[i] = 0xff;
+ for ( i = 0 ; selectchar[i] ; i++)
+ selectitem[(byte)selectchar[i]] = i;
+ }
+
+ /* Skip past leading characters */
+ while ((*str == ' ') || (*str == '&')) str++;
+
+ /* Copy useful chars */
+ for (string = buf; *str; str++)
+ if (*str != '~') *string++ = *str;
+
+ /* Terminate the new name */
+ *string = '\0';
+
+ /* strip the name down to size
+ if (76-col < (signed)max_len)
+ max_len = 76-col;
+ else
+ max_len = 30-6;*/
+ max_len = 39;
+
+ string = buf;
+ if (strlen(string) > (unsigned)max_len)
+ string = string + (strlen(string) - max_len);
+
+ /* Print it */
+ c_prt(color, format("[%c] %s", ch, string), row, col);
+}
+
+/* Display a list of recipes that need a particular essence.
+ * Note that we display a list of essences first,
+ * so in effect, this is the alchemist's recipe book.
+ */
+void alchemist_recipe_book(void)
+{
+ int num, max_num, i, al_idx, bat, kidx;
+ int choice[61], choice2[61];
+ int mod40;
+ bool_ essence[MAX_BATERIE_SVAL + 1];
+ char ch;
+
+ /* Save and clear the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ while ( TRUE )
+ {
+ Term_clear();
+
+ num = 0;
+
+ /* Display bateries */
+
+ /* start with assumption that the alchemist knows about no recipes */
+ for (i = 0; i < MAX_BATERIE_SVAL + 1 ; i++)
+ essence[i] = FALSE;
+
+ /* cycle through all alchemist recipes */
+ for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++)
+ /* if we aren't already going to display this essence */
+ if (!essence[alchemist_recipes[al_idx].sval_essence])
+ {
+
+ /*Note that we don't display artifact recipes here...*/
+ /*This is partially because artifacts often require exotic
+ ingredients as well */
+
+ if (!alchemist_recipes[al_idx].tval)
+ continue;
+
+ if (alchemist_recipes[al_idx].tval == 1)
+ {
+ if (alchemist_known_egos[alchemist_recipes[al_idx].sval / 32]
+ & (1 << (alchemist_recipes[al_idx].sval % 32)) )
+ essence[alchemist_recipes[al_idx].sval_essence] = TRUE;
+ continue;
+ }
+
+ kidx = lookup_kind(alchemist_recipes[al_idx].tval, alchemist_recipes[al_idx].sval);
+ if (alchemist_recipes[al_idx].tval != 1 && k_info[kidx].know)
+ essence[alchemist_recipes[al_idx].sval_essence] = TRUE;
+
+ }
+ for (num = 0, i = 0; i < MAX_BATERIE_SVAL + 7 ; i++)
+ if (essence[i] || i > MAX_BATERIE_SVAL)
+ {
+ int kidx = lookup_kind(TV_BATERIE, i);
+ if (i > MAX_BATERIE_SVAL)
+ {
+ switch (i)
+ {
+ case (MAX_BATERIE_SVAL + 1): strip_and_print("Scrolls", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 2): strip_and_print("Potions", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 3): strip_and_print("Wands", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 4): strip_and_print("Rings", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 5): strip_and_print("Staves", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 6): strip_and_print("Amulets", TERM_WHITE, num);
+ break;
+ default:
+ continue;
+ }
+ }
+ else
+ /* add this essence to the list*/
+ strip_and_print(k_name + k_info[kidx].name, TERM_WHITE, num);
+
+ choice[num++] = i;
+ }
+ max_num = num;
+ if ( max_num == 0)
+ {
+ /*Note that this should never actually happen, as any skill
+ at alchemy automatically gets you some recipes, and this
+ procedure shouldn't be called for players without alchemist skill
+ */
+ msg_print("You don't know any recipes!");
+ msg_print("You can't be an alchemist without recipes!");
+ break;
+ }
+
+ while (num == 0xff || num >= max_num)
+ {
+ ch = selectchar[max_num - 1];
+ /* Choose! */
+ if ( max_num == 0 ||
+ !get_com(format("Which Type of Recipe?[a-%c]", selectchar[max_num - 1]), &ch))
+ break;
+
+ /* Analyze choice - note that the cast to byte prevents overflow*/
+ num = selectitem[(byte)ch];
+
+ }
+ /* This break, and the break for no recipes above,
+ are the only exits from this procedure.
+ */
+ if ( num == 0xff || num >= max_num)
+ break;
+
+ /* Save the baterie index */
+ bat = choice[num];
+ num = 0;
+
+ /*Display the 'type of object' recipe screen*/
+ if (bat > MAX_BATERIE_SVAL)
+ {
+ int tval;
+ switch (bat)
+ {
+ case MAX_BATERIE_SVAL + 1:
+ tval = TV_SCROLL;
+ break;
+ case MAX_BATERIE_SVAL + 2:
+ tval = TV_POTION;
+ break;
+ case MAX_BATERIE_SVAL + 3:
+ tval = TV_WAND;
+ break;
+ case MAX_BATERIE_SVAL + 4:
+ tval = TV_RING;
+ break;
+ case MAX_BATERIE_SVAL + 5:
+ tval = TV_STAFF;
+ break;
+ case MAX_BATERIE_SVAL + 6:
+ tval = TV_AMULET;
+ break;
+ }
+ Term_load();
+ alchemist_recipe_select(&tval, 0, FALSE, TRUE);
+ Term_save();
+ continue;
+ }
+ mod40 = 0;
+ while ( TRUE )
+ {
+ int skipped;
+
+ Term_clear();
+ num = 0;
+
+ if (mod40)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ choice[num] = -2;
+ choice2[num++] = 0;
+ }
+
+ /* Display all items made with this essence */
+ for ( al_idx = 0 , skipped = 0 ; al_idx < max_al_idx ; al_idx++)
+ if ( alchemist_recipes[al_idx].sval_essence == bat)
+ {
+ int sval = alchemist_recipes[al_idx].sval;
+ int tval = alchemist_recipes[al_idx].tval;
+ char names[200] = "";
+
+ if (alchemist_recipes[al_idx].tval == 1)
+ {
+ /* Ego items */
+ ego_item_type *e_ptr = &e_info[sval];
+ int j, k;
+
+ if ( !(alchemist_known_egos[sval / 32] & (1 << (sval % 32))))
+ continue;
+
+ for ( j = 0 ; j < 6 && e_ptr->tval[j] ; j ++ )
+ {
+ if ( j > 0 && e_ptr->tval[j] == e_ptr->tval[j - 1])
+ continue;
+ for ( k = 0; tvals[k].tval; k++)
+ if (tvals[k].tval == e_ptr->tval[j])
+ {
+ strcat(names, tvals[k].desc);
+ strcat(names, ", ");
+ break;
+ }
+ }
+ strcat(names, e_name + e_ptr->name);
+ }
+ else
+ {
+ /* Normal Items */
+ int kidx = lookup_kind(tval, sval);
+ int k;
+ if ( !k_info[kidx].know )
+ continue;
+
+ for ( k = 0; tvals[k].tval; k++)
+ if (tvals[k].tval == tval)
+ {
+ strcat(names, tvals[k].desc);
+ break;
+ }
+ strcat(names, " of ");
+ strcat(names, k_name + k_info[kidx].name);
+
+ }
+
+ /*Skip the first mod40 pages of recipes*/
+ if (skipped++ < mod40*38)
+ continue;
+
+ /* add this object kind to the list*/
+ strip_and_print(names, TERM_WHITE, num);
+ choice[num] = tval;
+ choice2[num++] = sval;
+ if (num > 38)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ choice[num] = -1;
+ choice2[num++] = 0;
+ break;
+ }
+
+ }/*Loop through tidx/sidx*/
+
+ max_num = num;
+ while (num == 0xff || num >= max_num)
+ {
+ ch = selectchar[max_num - 1];
+ /* Choose! */
+ if ( max_num == 0 || !get_com(
+ format("Examine which recipe?[%c-%c]", selectchar[0], ch)
+ , &ch))
+ {
+ break;
+ }
+
+ /* Analyze choice */
+ num = selectitem[(byte)ch];
+ }
+
+ if ( choice[num] < 0)
+ {
+ if (choice[num] < -1)
+ mod40--;
+ else
+ mod40++;
+ continue;
+ }
+
+ if ( num == 0xff || num >= max_num)
+ break;
+
+ /* Display the recipe */
+ if (choice[num] == 1)
+ alchemist_display_recipe(0, 0, choice2[num]);
+ else
+ alchemist_display_recipe(choice[num], choice2[num], 0);
+ }
+ /*
+ break is at top of loop, after essence list
+ if( num < 0 || num >= max_num)
+ break;
+ */
+
+ }/*show recipes*/
+
+ /* Restore screen contents */
+ Term_load();
+ character_icky = FALSE;
+}
+
+/* Display a list of known recipies that can be made with
+ * materials on hand (including the passed tval). Also
+ * calls the recipe_display function, if requested by the
+ * player or there aren't enough essences to make the
+ * requested object.
+ *
+ * Note: sval is ignored if !ego, tval is the only determinant
+ * of what recipies are available otherwise.
+ *
+ * This function needs to be able to scroll a list, because
+ * there are SO MANY potions. :)
+ */
+int alchemist_recipe_select(int *tval, int sval, int ego, bool_ recipe)
+{
+ int i, mod40 = 0, num, max_num = 0;
+
+ cptr tval_desc2 = "";
+ char ch;
+ bool_ done = FALSE;
+
+ int choice[60];
+ int validc[60];
+
+ char *string;
+
+
+ /* Save and clear the screen */
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+ /* Base object type chosen, fill in tval */
+ for ( num = 0 ; num < 40 ; num ++)
+ if (tvals[num].tval == *tval)
+ {
+ tval_desc2 = tvals[num].desc;
+ }
+
+ while (!done)
+ {
+ Term_clear();
+ if (ego)
+ {
+ /* Find matching ego items */
+ for (num = 0, i = 1; (num < 40) && (i < max_e_idx) ; i++)
+ {
+ int j;
+ ego_item_type *e_ptr = &e_info[i];
+
+ /* Skip if unknown ego type */
+ if ( !(alchemist_known_egos[i / 32] & (1 << (i % 32))))
+ continue;
+
+ /* search in permitted tvals/svals for allowed egos */
+ for ( j = 0 ; j < 6 ; j ++ )
+ if ( e_ptr->tval[j] == *tval
+ && sval >= e_ptr->min_sval[j]
+ && sval <= e_ptr->max_sval[j])
+ {
+ int color = TERM_GREEN;
+
+ /*Reject if not opposite end of name
+ prefixes only on postfix egos,
+ postfixes only on prefix egos.
+ */
+ if (ego != -1 && e_ptr->before == e_info[ego].before)
+ continue;
+
+ /*Color it red of the alchemist doesn't have the essences to create it*/
+ if (!alchemist_items_check(*tval, 0, i, 0, TRUE))
+ color = TERM_RED;
+
+ /* add this ego to the list*/
+ strip_and_print(e_name + e_info[i].name, color, num);
+ validc[num] = color;
+ choice[num++] = i;
+ break;
+ }
+ }
+ }
+ else
+ {
+ char skipped = 0;
+ num = 0;
+ if (mod40 != 0)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ validc[num] = TERM_WHITE;
+ choice[num++] = -1;
+ }
+
+ for (i = 1; (num < 39) && (i < max_k_idx); i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Analyze matching items */
+ if (k_ptr->tval == *tval || (k_ptr->tval == TV_POTION2 && *tval == TV_POTION))
+ {
+ char color = TERM_GREEN;
+ /* Hack -- Skip instant artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /*Don't display recipes that the alchemist doesn't know about*/
+ if (!k_ptr->know && !wizard) continue;
+
+ /*Skip recipes that are somehow known, but don't exist*/
+ if (!alchemist_exists(k_ptr->tval, k_ptr->sval, 0, 0))
+ continue;
+
+ /* Skip the first 39 if they hit 'more' */
+ if (skipped++ < mod40*39)
+ continue;
+
+ /* Color 'unable to create' items different */
+ if (!alchemist_items_check(k_ptr->tval, k_ptr->sval, 0, 0, TRUE))
+ color = TERM_RED;
+
+ /* Acquire the "name" of object "i" */
+ /* and print it in it's place */
+ strip_and_print(k_name + k_ptr->name, color, num);
+
+ /* Remember the object index */
+ validc[num] = color;
+ choice[num++] = i;
+ }
+ }
+ if (num == 39)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ validc[num] = TERM_WHITE;
+ choice[num++] = -1;
+ }
+ }
+
+ /* We need to know the maximal possible remembered object_index */
+ max_num = num;
+ string = "What Kind of %s? (* to see recipe) [%c-%c,*]";
+ num = 0xff;
+
+ /* Pretend they're all undoable if we where called to display recipes */
+ if (recipe)
+ {
+ for ( num = 0 ; num < max_num ; num++)
+ if (validc[num] != TERM_WHITE) validc[num] = TERM_RED;
+ string = "show which %s recipe? [%c-%c]";
+ }
+
+ while (num == 0xff || num >= max_num)
+ {
+ ch = selectchar[max_num - 1];
+ /* Choose! */
+ if ( max_num == 0 || !get_com(format(string, tval_desc2, selectchar[0], ch), &ch))
+ {
+ break;
+ }
+
+ /* Extra breaks for recipe */
+ if (recipe && (ch == '\r' || ch == ' ' || ch == ESCAPE ))
+ break;
+
+ /* Analyze choice */
+ num = selectitem[(byte)ch];
+
+ /* Pretend that we don't have enough essences for anything */
+ if (ch == '*' )
+ {
+ for ( num = 0 ; num < max_num ; num++)
+ if (validc[num] != TERM_WHITE) validc[num] = TERM_RED;
+ string = "Show which %s recipe? [%c-%c]";
+ }
+ }
+ if ( num == 0xff || max_num == 0 || num >= max_num)
+ break;
+
+ if ( validc[num] == TERM_WHITE )
+ {
+ if (num == 0)
+ mod40--;
+ else
+ mod40++;
+ if ( mod40 < 0)
+ mod40 = 0;
+ continue;
+ }
+
+ /* If we don't have enough essences, or user asked for recipes */
+ if ( validc[num] != TERM_GREEN )
+ {
+ /* Display the recipe */
+ if (ego)
+ alchemist_display_recipe(*tval, sval, choice[num]);
+ else
+ alchemist_display_recipe(k_info[choice[num]].tval, k_info[choice[num]].sval, 0);
+ }
+ else
+ done = TRUE;
+
+ }/*while(!done)*/
+
+ /* Restore screen contents */
+ Term_load();
+ character_icky = FALSE;
+
+ /* User abort, or no choices */
+ if (max_num == 0 || num == 0xff || num >= max_num)
+ {
+ if (max_num == 0)
+ msg_print("You don't know of anything you can make using that.");
+ return ( -1);
+ }
+ if ( validc[num] != TERM_GREEN )
+ return ( -1);
+
+ /* And return successful */
+ if ( ego )
+ return choice[num];
+
+ /* Set the tval, should be the same unless they selected a potion2 */
+ if (*tval != k_info[choice[num]].tval && *tval != TV_POTION)
+ msg_print("Coding error: tval != TV_POTION");
+ *tval = k_info[choice[num]].tval;
+ return ( k_info[choice[num]].sval );
+}
+
+/* Set the 'known' flags for all objects with a level <= lev
+ * This lets the budding alchemist create basic items.
+ */
+void alchemist_learn_all(int lev)
+{
+ int i;
+
+ if ( !get_skill(SKILL_ALCHEMY) )
+ return;
+
+ /* msg_format("You learn about level %d items",lev); */
+
+ for ( i = 0 ; i < max_k_idx ; i++ )
+ if ( k_info[i].level <= lev )
+ if (alchemist_exists(k_info[i].tval, k_info[i].sval, 0, 0))
+ k_info[i].know = TRUE;
+}
+
+void alchemist_learn_ego(int ego)
+{
+ char *name;
+ int i;
+
+ /* some Paranoia*/
+ if ( !ego || ego >= max_e_idx )
+ return;
+
+ /* Get the ego items name */
+ name = e_name + e_info[ego].name;
+ while (strchr(name, ' '))
+ name = strchr(name, ' ') + 1;
+
+ /* Don't learn about egos without recipes, and
+ * always learn about the passed ego item. */
+ if (alchemist_exists(0, 0, ego, 0))
+ {
+ alchemist_known_egos[ego / 32] |= (1 << (ego % 32));
+ /* msg_format("You learn about '%s' ego items.",e_name+e_info[ego].name); */
+ }
+ else
+ {
+ return;
+ }
+
+ /* Don't mass learn about egos that have no name. */
+ if ( name[0] == 0 )
+ {
+ return;
+ }
+
+ /* Look through all ego's for matching name */
+ /* Note that the original ego is marked here too */
+ for ( i = 0 ; i < max_e_idx ; i++ )
+ if ( strstr(e_name + e_info[i].name, name) != NULL /*Last word of name exists in this ego's name*/
+ && alchemist_exists(0, 0, i, 0) /*There exists a recipe for this*/
+ && !(alchemist_known_egos[i / 32] & (1 << (i % 32)) ) ) /*Not already known*/
+ /*&& (e_name+e_info[i].name)[0])non-blank name*/
+ {
+ alchemist_known_egos[i / 32] |= (1 << (i % 32));
+ /* msg_format("You learn about '%s' ego items.",e_name+e_info[i].name); */
+ }
+
+ return;
+}
+
+/* Alchemist has learned about a new item.
+ * Learn about not only it, but ALL egos with the
+ * same name.
+ */
+int alchemist_learn_object(object_type *o_ptr)
+{
+
+ /* Allow alchemist to create this item,
+ and.. learn about it even if the player
+ doesn't currently have the alchemy skill
+ */
+ k_info[o_ptr->k_idx].know = TRUE;
+
+ /* Not Paranoia, identify_fully calls this always */
+ if ( !get_skill(SKILL_ALCHEMY) )
+ return FALSE;
+
+ if ( artifact_p(o_ptr) )
+ {
+ char o_name[80];
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Randarts and normal artifacts both*/
+ alchemist_known_artifacts[0] |= f1;
+ alchemist_known_artifacts[1] |= f2;
+ alchemist_known_artifacts[2] |= f3;
+ alchemist_known_artifacts[3] |= f4;
+ alchemist_known_artifacts[4] |= f5;
+ alchemist_known_artifacts[5] |= esp;
+
+ object_desc(o_name, o_ptr, 1, 0);
+ msg_format("You learn all about the abilities of %s!", o_name);
+ }
+ if (o_ptr->name2)
+ alchemist_learn_ego(o_ptr->name2);
+
+ if (o_ptr->name2b)
+ alchemist_learn_ego(o_ptr->name2b);
+
+ return (TRUE);
+}
+
+/* Alchemist has gained a level - set the ego flags
+ * for all egos <= lev/4.
+ */
+void alchemist_gain_level(int lev)
+{
+ object_type forge;
+ object_type *o_ptr = &forge;
+
+ if ( lev == 0)
+ {
+ /* Learn about potions of Detonation */
+ k_info[417].know = TRUE;
+ }
+ if ( lev == 5)
+ {
+ int ego;
+ int egos[] = {
+ 7/*armor of resist fire*/
+ , 18/*shield of resist fire*/
+ , 74/*shocking weapon*/
+ , 75/*fiery weapon*/
+ , 76/*frozen weapon*/
+ , 77/*Venomous weapon*/
+ , 78/*Chaotic weapon*/
+ , 115/*projectile of venom*/
+ , 116/*projectile of Acid*/
+ , 122/*projectile of flame*/
+ , 123/*projectile of frost*/
+ , 137/*Lite of fearlessness*/
+ , 0 /*terminator*/
+ };
+ object_wipe(o_ptr);
+ /* learn about some basic ego items */
+ /* Note that this is just to get you started. */
+ for ( ego = 0 ; egos[ego] ; ego++)
+ {
+ o_ptr->name2 = egos[ego];
+ alchemist_learn_object(o_ptr);
+ }
+ msg_print("You recall your old master teaching you about elemental item infusing.");
+ }
+ if ( lev == 10)
+ {
+ /*For 'hard rooms' Players only, learn about diggers.*/
+ if (ironman_rooms)
+ {
+ msg_print("There's gotta be an easier way to get into all these vaults!");
+ object_wipe(o_ptr);
+ o_ptr->name2 = 101; /* Ego item, 'of digging' */
+ alchemist_learn_object(o_ptr);
+ }
+ }
+ if ( lev == 25)
+ {
+ msg_print("You recall your old master reminiscing about legendary infusings");
+ msg_print("and the Philosophers' stone.");
+
+ /* No auto-learn on artifacts - by this level, you'll have *ID*'d several */
+ }
+ if ( lev == 25)
+ {
+ msg_print("You wonder about shocking daggers of slay evil.");
+ }
+ if ( lev == 50)
+ {
+ /* learn about Temporary item creation */
+ /* Note that this is the ONLY way to learn this,
+ because spells which create a temporary item
+ also fully ID it. */
+ alchemist_known_artifacts[4] |= TR5_TEMPORARY;
+ msg_print("It suddenly occurs to you that artifacts don't *HAVE* to be permanent...");
+ }
+
+ /* Every Four Levels, learn about items that are
+ * less than that.
+ * Note that this isn't a significant effect after the
+ * first few levels, as the level at which you are learning
+ * things here quickly drops behind the level at which you
+ * are finding items.
+ */
+ if ( (lev & 0x3) != 0 )
+ return;
+ lev = (lev >> 2) + 1;
+ alchemist_learn_all(lev);
+
+}
+
+/* This, in combination with some code in loadsave.c,
+ insures that alchemist_gain_level is called EXACTLY
+ once with each possible value during the characters
+ lifetime.
+ */
+void alchemist_check_level()
+{
+ u32b lev = get_skill(SKILL_ALCHEMY);
+ if ( alchemist_gained > lev )
+ return;
+ /*Paranoia*/
+ if ( !lev )
+ return;
+ while ( alchemist_gained <= lev )
+ alchemist_gain_level(alchemist_gained++);
+}
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'alchemist'.
+ */
+void do_cmd_alchemist(void)
+{
+ int item, ext = 0;
+ int value, basechance;
+ int askill;
+ bool_ repeat = 0;
+ char ch;
+
+ object_type *o_ptr, *q_ptr;
+ object_type forge, forge2;
+ byte carry_o_ptr = FALSE;
+
+ cptr q, s;
+
+ /* With the new skill system, we can no longer depend on
+ * check_exp to handle the changes and learning involved in
+ * gaining levels.
+ * So we'll have to check for it here.
+ */
+ alchemist_check_level();
+ askill = get_skill(SKILL_ALCHEMY);
+
+
+ q_ptr = &forge;
+
+ o_ptr = &p_ptr->inventory[INVEN_HANDS];
+ if ((o_ptr->tval != TV_GLOVES) || (o_ptr->sval != SV_SET_OF_LEATHER_GLOVES))
+ {
+ msg_print("You must wear gloves in order to do alchemy.");
+ return;
+ }
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ while (TRUE)
+ {
+ if (!get_com("[P]ower, [R]echarge or [L]eech an item, [E]xtract essences, or recipe [B]ook?", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if (ch == ' ' )
+ {
+ ext = 0;
+ break;
+ }
+ if (ch == 'P' || ch == 'p')
+ {
+ ext = 1;
+ break;
+ }
+ if (ch == 'E' || ch == 'e')
+ {
+ ext = 2;
+ break;
+ }
+ if (ch == 'R' || ch == 'r')
+ {
+ ext = 3;
+ break;
+ }
+ if (ch == 'L' || ch == 'l')
+ {
+ ext = 2;
+ repeat = 1;
+ break;
+ }
+ if (ch == 'B' || ch == 'b')
+ {
+ ext = 4;
+ break;
+ }
+ }
+
+ /**********Add a power*********/
+ if (ext == 1)
+ {
+ int i, qty, tval, sval = 0, ego = 0;
+ char o_name[200];
+
+ /* Get an item */
+ q = "Empower which item? ";
+ s = "You have no empowerable items.";
+ item_tester_hook = item_tester_hook_empower;
+
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Create an artifact from an ego or double ego item,
+ * from a previous artifact, or finish an artifact
+ */
+ if ((askill >= 25) && (artifact_p(o_ptr) || o_ptr->name2) && has_ability(AB_CREATE_ART))
+ {
+ if (get_check("Create an artifact?"))
+ {
+ do_cmd_toggle_artifact(o_ptr);
+ return;
+ }
+ /* Don't change artifacts or double ego items further */
+ else if (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b))
+ return;
+ }
+ /*Ok, now we have the item, so we can now pick recipes.
+ Note: No recipe is known unless we have 'extracted' from
+ that object type. I.E. the 'know' flag (also greater identify)
+ is set.
+ */
+
+ /* Here we're not setting what kind of ego item it IS,
+ * where' just deciding that it CAN be an ego item */
+ if ( o_ptr->name2 ) /* creating a DUAL ego */
+ ego = TRUE;
+ if ( o_ptr->tval < 40 && o_ptr->tval != TV_BOTTLE)
+ ego = TRUE;
+ if ( o_ptr->tval == TV_ROD_MAIN || o_ptr->tval == TV_DAEMON_BOOK || o_ptr->tval == TV_BOOK)
+ ego = TRUE;
+
+ sval = o_ptr->sval;
+ if (!ego)
+ {
+ switch ( o_ptr->tval)
+ {
+ case TV_WAND:
+ sval = SV_WAND_NOTHING;
+ break;
+ case TV_RING:
+ sval = SV_RING_NOTHING;
+ break;
+ case TV_STAFF:
+ sval = SV_STAFF_NOTHING;
+ break;
+ case TV_BOTTLE:
+ sval = 1;
+ break;
+ case TV_AMULET:
+ sval = SV_AMULET_NOTHING;
+ break;
+ case TV_SCROLL:
+ sval = SV_SCROLL_NOTHING;
+ break;
+ case TV_ROD:
+ sval = SV_ROD_NOTHING;
+ break;
+ }
+ }
+ if ( o_ptr->sval != sval )
+ ego = TRUE;
+
+ tval = o_ptr->tval;
+ sval = o_ptr->sval;
+
+ /*HACK - bottles don't have the same tval as potions*/
+ /*Everything else will have the same tval after empowering*/
+ if (tval == TV_BOTTLE) tval = TV_POTION;
+ if (ego)
+ if (o_ptr->name2)
+ ego = alchemist_recipe_select(&tval, sval, o_ptr->name2, FALSE);
+ else
+ ego = alchemist_recipe_select(&tval, sval, -1, FALSE);
+ else
+ sval = alchemist_recipe_select(&tval, 0, 0, FALSE);
+
+ if ( sval < 0 || ego < 0)
+ return;
+
+ /* Check to make sure we have enough essences */
+ /* theoretically this is taken care of by recipe_select*/
+ /* but we'll double check just for paranoia. */
+ if (!alchemist_items_check(tval, sval, ego, 0, TRUE))
+ {
+ msg_print("You do not have enough essences.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Use up the essences */
+ (void)alchemist_items_check(tval, sval, ego, -1, TRUE);
+
+ /* Enchant stacks of ammunition at a time */
+ if ( o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW || o_ptr->tval == TV_BOLT )
+ {
+ qty = 1;
+ while (qty < o_ptr->number && alchemist_items_check(tval, sval, ego, -1, FALSE))
+ qty++;
+ }
+ else
+ qty = 1;
+
+ /* Copy the object */
+ q_ptr = &forge;
+ object_copy(q_ptr, o_ptr);
+
+ if ( o_ptr->tval == TV_WAND)
+ {
+ /* distribute charges on wands */
+ q_ptr->pval = o_ptr->pval / o_ptr->number;
+ o_ptr->pval -= q_ptr->pval;
+ }
+
+ o_ptr = q_ptr;
+ o_ptr->number = qty;
+ carry_o_ptr = TRUE;
+
+ /* Destroy the initial object */
+ inc_stack_size(item, -qty);
+
+
+ if ( ego )
+ {
+ int pval, pval2;
+ s32b pval3;
+
+ pval = o_ptr->pval;
+ pval2 = o_ptr->pval2;
+ pval3 = o_ptr->pval3;
+
+ if (o_ptr->name2)
+ o_ptr->name2b = ego;
+ else
+ o_ptr->name2 = ego;
+ o_ptr->pval = randint(e_info[ego].max_pval - 1) + 1;
+ /* dilemma - how to prevent creation of cursed items,
+ * without allowing the creation of artifacts?
+ * We can't, unless we want to finalize the ego flags ourselves.
+ */
+ apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE);
+ /* Remember what the old pval was, so that we can re-apply it. */
+ if ( o_ptr->tval == TV_WAND
+ || o_ptr->tval == TV_RING
+ || o_ptr->tval == TV_AMULET
+ || o_ptr->tval == TV_STAFF)
+ {
+ o_ptr->pval = pval;
+ o_ptr->pval2 = pval2;
+ o_ptr->pval3 = pval3;
+ }
+ else if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ o_ptr->pval = pval;
+ }
+ else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255))
+ {
+ o_ptr->pval = pval;
+ }
+ else if (o_ptr->tval == TV_SHOT
+ || o_ptr->tval == TV_ARROW
+ || o_ptr->tval == TV_BOLT)
+ {
+ o_ptr->pval2 = pval2;
+ }
+ else if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ o_ptr->pval2 = pval2;
+ }
+
+ /* Calculate failure rate, lev=val/2500+5 */
+ value = MIN(e_info[o_ptr->name2].cost, 50000);
+ if (o_ptr->name2b) value += MIN(e_info[o_ptr->name2b].cost, 50000);
+ basechance = (value / 1000 + 5 - get_skill_scale(SKILL_ALCHEMY, 100) ) * 10;
+ if ( basechance < 0) basechance = 0;
+ if ( basechance > 100) basechance = 100;
+
+ value = object_value_real(o_ptr);
+
+ }
+ else /* not an ego item */
+ {
+ o_ptr = &forge;
+ object_wipe(o_ptr);
+ object_prep(o_ptr, lookup_kind(tval, sval));
+ hack_apply_magic_power = -99;
+ apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE);
+ if ( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF)
+ o_ptr->pval = 0;
+ value = object_value_real(o_ptr);
+
+ basechance = k_info[o_ptr->k_idx].level - askill * 2;
+ basechance *= 10;
+
+ /* Can't fail more that 100% of the time... */
+ if (basechance > 100)
+ basechance = 100;
+ /* Always success in creation of potion of detonations */
+ if (o_ptr->tval == TV_POTION && o_ptr->sval == SV_POTION_DETONATIONS)
+ {
+ basechance /= 10;
+ }
+ }
+
+ /* Use up gold to create items */
+ /* this has the effect of making the alchemist
+ chronically short of funds, unless he finds the
+ philosopher's stone. It also means the easiest
+ things to make are 'bad', like a potion of
+ detonations...
+ */
+ /* Problem - to restrictive. We need something
+ which requires less money. But at the same time,
+ we don't want an 'easy cash' situation. Maybe something
+ like '10% * level difference', meaning at skill level 5,
+ level one items are free? But egos are frequently level
+ zero! Maybe egos are forced to level 25? with a cost ceiling?
+ I mean, Potions and scrolls are really the problem causing the
+ 'easy cash' situation, it's ego items. Ego items require
+ relatively few essences, and the rewards are HUGE. Most powerful
+ potions and scrolls require rare essences. Maybe force all egos
+ to require a magic essence? But then you'd get lots of magic
+ from distilling them. Maybe consumed in the creation? then when
+ you got a powerful item, you could make one ego item...
+ But if making things doesn't take gold, what about the cash
+ does the Philosopher's stone do?
+ Time*/
+
+ /* 0% failure if you have the stone */
+ if ( alchemist_has_stone())
+ basechance = 0;
+
+ if (basechance > 0 && value)
+ {
+ char string[80];
+ string[0] = '0';
+ string[1] = 0;
+
+ msg_format("The chance of success is only %d%%!", 100-basechance);
+ get_string("How much gold do you want to add?", string, 50);
+ i = atoi(string);
+ /* Note: don't trust the user to enter a positive number... */
+ if ( i < 0)
+ i = 0;
+ if ( i > p_ptr->au)
+ i = p_ptr->au;
+
+ if (i)
+ {
+ basechance = basechance - (i * 20) / value;
+ msg_format("The chance of success improved to %d%%.", 100-basechance);
+ }
+
+ if (randint(100) < basechance )
+ /*creation failed, even with the extra gold...*/
+ carry_o_ptr = FALSE;
+
+ /* Redraw gold */
+ p_ptr->au -= i;
+ p_ptr->redraw |= (PR_GOLD);
+ }
+
+ /* Set fully identified
+ * After all, the player just made it...
+ */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->ident |= IDENT_MENTAL;
+ o_ptr->found = OBJ_FOUND_SELFMADE;
+
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ if ( carry_o_ptr)
+ {
+ msg_format("You have successfully created %s %s",
+ (o_ptr->number > 1 ? "some" : (is_a_vowel(o_name[0]) ? "an" : "a")),
+ o_name);
+
+ if (inven_carry_okay(o_ptr))
+ inven_carry(o_ptr, FALSE);
+ else
+ {
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+ msg_format("You drop the %s", o_name);
+ }
+ carry_o_ptr = FALSE;
+ }
+ else /* don't carry, or in other words... */
+ {
+ int level = k_info[o_ptr->k_idx].level;
+ if (o_ptr->name1) /* created ego item */
+ level += e_info[o_ptr->name2].level;
+
+ msg_format("Your attempt backfires! Your %s explodes!", o_name);
+ take_hit(damroll(3, level - askill ) , "Alchemical Explosion");
+ p_ptr->redraw |= (PR_HP);
+ }
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Optimize the entire p_ptr->inventory - needed because we
+ don't know how many essences where used, and we may
+ have 'used up' a wielded item as well.
+ */
+ for ( item = 0 ; item < INVEN_TOTAL ; item++ )
+ inven_item_optimize(item);
+
+ /**********Extract a power*********/
+ }
+ else if (ext == 2)
+ {
+ int ego;
+ bool_ discharge_stick = FALSE;
+
+ /* s_ptr holds the empty items */
+ object_type *s_ptr = NULL;
+ bool_ carry_s_ptr = FALSE;
+
+ item_tester_hook = item_tester_hook_extractable;
+
+ /* Get an item */
+ q = "Extract from which item? ";
+ s = "You have no item to extract power from.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* This is to prevent creating magic essences by extracting
+ * from a recharged wand of dragon breath or something.
+ */
+ if (( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF )
+ && o_ptr->art_flags4 & TR4_RECHARGED)
+ {
+ msg_print("You cannot extract essences after it's been magically recharged.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Handle Rods before the loop, since they don't stack */
+ if (o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != SV_ROD_NOTHING)
+ {
+ rod_tip_extract(o_ptr);
+ return;
+ }
+
+ do
+ { /* Repeat (for leech command) */
+
+ /* Create the items.
+ * we don't care if they drop to the ground,
+ * and if no action was taken, return
+ */
+ ego = 0;
+ if ( o_ptr->name2)
+ ego = o_ptr->name2;
+
+ /* For ego staves and wands (not of nothing), discharge before extracting the ego */
+ discharge_stick = (o_ptr->pval > 0 &&
+ ((o_ptr->tval == TV_STAFF && o_ptr->sval != SV_STAFF_NOTHING) ||
+ (o_ptr->tval == TV_WAND && o_ptr->sval != SV_WAND_NOTHING)));
+ if (discharge_stick)
+ ego = 0;
+
+ if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, ego, 1, TRUE))
+ {
+ msg_print("You cannot extract anything from that item.");
+ return;
+ }
+
+ if (o_ptr->name2b && !alchemist_items_check(o_ptr->tval, o_ptr->sval, o_ptr->name2b, 1, TRUE))
+ {
+ /* do nothing - if the second ego can't be extracted
+ because there is no recipe for it, simply destroy it
+ */
+ }
+
+ /* Once in three times, learn how to make the item */
+ /* Sorry for the complicated if! Basically, if it's an
+ * unknown regular item or an unknown ego item, there's
+ * a one in 3 chance that it'll be id'd */
+ if (((!ego && !k_info[o_ptr->k_idx].know)
+ || (ego && !(alchemist_known_egos[ego / 32] & (1 << (ego % 32)))))
+ && randint(3) == 1)
+ {
+ msg_print("While destroying it, you gain insight into this item.");
+ /* If over level 10, the player has a chance of 'greater ID'
+ * on extracted items
+ */
+ if (askill > 9)
+ object_out_desc(o_ptr, NULL, FALSE, TRUE);
+ alchemist_learn_object(o_ptr);
+ }
+
+ /* Always learn what kind of thing it is */
+ object_known(o_ptr);
+ object_aware(o_ptr);
+
+ /* If it's a wand or staff with charges (but not of nothing),
+ * decrease number of charges, unstacking if needed.
+ * Otherwise, create the 'of nothing' item and destroy the old one.
+ */
+ if (discharge_stick)
+ {
+ /* Unstack staves */
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1))
+ {
+ /* Create one local copy of the staff */
+ q_ptr = &forge2;
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = 1;
+
+ /* Unstack the copied staff */
+ o_ptr->number--;
+
+ /* Use the local copy of the staff */
+ o_ptr = q_ptr;
+ carry_o_ptr = TRUE;
+ }
+ /* remove one charge */
+ o_ptr->pval--;
+ }
+ else
+ {
+ /* Create the empty, plain item */
+ /* If the item was already created, increase the number */
+ if (carry_s_ptr)
+ {
+ s_ptr->number++;
+ }
+ else
+ {
+ /* Otherwise we must create a local copy of the empty item */
+ int tval, sval;
+ bool_ create_item = TRUE;
+
+ tval = o_ptr->tval;
+ if ( !ego && (tval == TV_POTION || tval == TV_POTION2))
+ tval = TV_BOTTLE;
+
+ sval = o_ptr->sval;
+
+ if (!ego)
+ {
+ switch ( tval)
+ {
+ case TV_WAND:
+ sval = SV_WAND_NOTHING;
+ break;
+ case TV_RING:
+ sval = SV_RING_NOTHING;
+ break;
+ case TV_STAFF:
+ sval = SV_STAFF_NOTHING;
+ break;
+ case TV_BOTTLE:
+ sval = 1;
+ break;
+ case TV_AMULET:
+ sval = SV_AMULET_NOTHING;
+ break;
+ case TV_SCROLL:
+ sval = SV_SCROLL_NOTHING;
+ break;
+ case TV_ROD:
+ sval = SV_ROD_NOTHING;
+ break;
+ default:
+ create_item = FALSE;
+ }
+ }
+
+ if (create_item)
+ {
+ /* Create the empty item */
+ s_ptr = &forge;
+ object_wipe(s_ptr);
+ object_prep(s_ptr, lookup_kind(tval, sval));
+ s_ptr->number = 1;
+
+ /* Force creation of non ego non cursed */
+ hack_apply_magic_power = -99;
+ apply_magic(s_ptr, 0, FALSE, FALSE, FALSE);
+
+ /* Hack -- remove possible curse */
+ if (cursed_p(s_ptr))
+ {
+ s_ptr->art_flags3 &= ~(TR3_CURSED | TR3_HEAVY_CURSE);
+ s_ptr->ident &= ~(IDENT_CURSED);
+ }
+
+ /* Restore pvals (e.g. charges ==0) of the item */
+ if (ego && ((tval == TV_WAND) || (tval == TV_STAFF) ||
+ (tval == TV_RING) || (tval == TV_AMULET)))
+ {
+ s_ptr->pval = o_ptr->pval;
+ s_ptr->pval2 = o_ptr->pval2;
+ s_ptr->pval3 = o_ptr->pval3;
+ }
+ /* Restore the spell stored in a random book */
+ else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255))
+ {
+ s_ptr->pval = o_ptr->pval;
+ }
+ /* Restore the type of explosive ammo */
+ else if (o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW
+ || o_ptr->tval == TV_BOLT)
+ {
+ s_ptr->pval2 = o_ptr->pval2;
+ }
+ /* Restore the music stored in an instrument */
+ else if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ s_ptr->pval2 = o_ptr->pval2;
+ }
+
+ object_aware(s_ptr);
+ object_known(s_ptr);
+ s_ptr->ident |= IDENT_STOREB;
+
+ /* The empty item will be added to the p_ptr->inventory later */
+ carry_s_ptr = TRUE;
+ }
+ }
+
+ /* Now, we can delete the original (leeched) object.
+ * Is o_ptr an p_ptr->inventory / floor item or a local copy?
+ */
+ if (!carry_o_ptr)
+ {
+ /* Break the leech-loop if it was the last item */
+ if (o_ptr->number == 1)
+ repeat = 0;
+
+ inc_stack_size(item, -1);
+ }
+ else
+ {
+ /* Forget the local object */
+ carry_o_ptr = FALSE;
+
+ /* reset o_ptr to the original stack,
+ * which contains at least another item */
+ o_ptr = get_object(item);
+ }
+ }
+ }
+ while ( repeat == 1);
+
+ /* If we carry empty items, add them to the p_ptr->inventory */
+ if (carry_s_ptr)
+ inven_carry(s_ptr, TRUE);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /******* Recharge an item *******/
+ }
+ else if (ext == 3)
+ {
+ int item;
+
+ cptr q, s;
+
+ item_tester_hook = item_tester_hook_recharge;
+
+ /* Get an item */
+ q = "Recharge which item? ";
+ s = "You have no rechargable items.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR ))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Make sure we have enough essences to recharge this */
+ if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, 0, TRUE))
+ {
+ msg_print("You don't have the essences to recharge this item.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Destroy the essences */
+ (void)alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, -1, TRUE);
+
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1))
+ {
+ /* Unstack staves */
+ /* Get local object */
+ q_ptr = &forge2;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = 1;
+
+ /* Unstack the used item */
+ o_ptr->number--;
+
+ o_ptr = q_ptr;
+ carry_o_ptr = TRUE;
+ }
+ o_ptr->pval++;
+ }
+ else if ( ext == 4)
+ {
+ alchemist_recipe_book();
+ }
+ /* Just in case - */
+ if (carry_o_ptr)
+ {
+ /* the o_ptr item was probably an unstacked staff
+ * Anyway, we need to add it to the p_ptr->inventory */
+ if (inven_carry_okay(o_ptr))
+ inven_carry(o_ptr, TRUE);
+ else
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+ }
+}
+
+
+/*
+ * Command to ask favors from your god.
+ */
+void do_cmd_pray(void)
+{
+ if (p_ptr->pgod == GOD_NONE)
+ {
+ msg_print("Pray hard enough and your prayers might be answered.");
+ return;
+ }
+ else
+ {
+ if (!p_ptr->praying)
+ msg_format("You start praying to %s.", deity_info[p_ptr->pgod].name);
+ else
+ msg_format("You stop praying to %s.", deity_info[p_ptr->pgod].name);
+ p_ptr->praying = !p_ptr->praying;
+
+ /* Update stuffs */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ p_ptr->redraw |= PR_PIETY | PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP;
+ energy_use = 100;
+ }
+}
+
+
+/*
+ * Return percentage chance of spell failure.
+ */
+int spell_chance_random(random_spell* rspell)
+{
+ int chance, minfail;
+
+
+ /* Extract the base spell failure rate */
+ chance = rspell->level + 10;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (get_skill(SKILL_THAUMATURGY) - rspell->level);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1);
+
+ /* Not enough mana to cast */
+ if (rspell->mana > p_ptr->csp)
+ {
+ chance += 5 * (rspell->mana - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]];
+
+ /* Failure rate */
+ return clamp_failure_chance(chance, minfail);
+}
+
+
+
+
+/*
+ * Print a batch of spells.
+ */
+static void print_spell_batch(int batch, int max)
+{
+ char buff[80];
+
+ random_spell* rspell;
+
+ int i;
+
+
+ prt(format(" %-30s Lev Fail Mana Damage ", "Name"), 1, 20);
+
+ for (i = 0; i < max; i++)
+ {
+ rspell = &random_spells[batch * 10 + i];
+
+ if (rspell->untried)
+ {
+ strnfmt(buff, 80, " %c) %-30s (Spell untried) ",
+ I2A(i), rspell->name);
+
+ }
+ else
+ {
+ strnfmt(buff, 80, " %c) %-30s %3d %4d%% %3d %3dd%d ",
+ I2A(i), rspell->name,
+ rspell->level, spell_chance_random(rspell), rspell->mana,
+ rspell->dam_dice, rspell->dam_sides);
+ }
+
+ prt(buff, 2 + i, 20);
+ }
+
+ prt("", 2 + i, 20);
+}
+
+
+
+/*
+ * List ten random spells and ask to pick one.
+ */
+static random_spell* select_spell_from_batch(int batch, bool_ quick)
+{
+ char tmp[160];
+
+ char out_val[30];
+
+ char which;
+
+ int mut_max = 10;
+
+ random_spell* ret;
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ if (spell_num < (batch + 1) * 10)
+ {
+ mut_max = spell_num - batch * 10;
+ }
+
+ strnfmt(tmp, 160, "(a-%c, * to list, A-%cto browse, / to rename, - to comment) Select a power: ",
+ I2A(mut_max - 1), I2A(mut_max - 1) - 'a' + 'A');
+
+ prt(tmp, 0, 0);
+
+ if (quick)
+ {
+ print_spell_batch(batch, mut_max);
+ }
+
+ while (1)
+ {
+ /* Get a command */
+ which = inkey();
+
+ /* Abort */
+ if (which == ESCAPE)
+ {
+ /* No selection */
+ ret = NULL;
+
+ /* Leave the command loop */
+ break;
+
+ }
+
+ /* List */
+ if (which == '*' || which == '?' || which == ' ')
+ {
+ /* Print power list */
+ print_spell_batch(batch, mut_max);
+
+ /* Wait for next command */
+ continue;
+ }
+
+ /* Accept default */
+ if (which == '\r')
+ {
+ /* There are no other choices */
+ if (mut_max == 1)
+ {
+ ret = &random_spells[batch * 10];
+
+ /* Leave the command loop */
+ break;
+ }
+
+ /* Wait for next command */
+ continue;
+ }
+
+ /* Rename */
+ if (which == '/')
+ {
+ prt("Rename which power: ", 0, 0);
+ which = tolower(inkey());
+
+ if (isalpha(which) && (A2I(which) <= mut_max))
+ {
+ strcpy(out_val, random_spells[batch*10 + A2I(which)].name);
+ if (get_string("Name this power: ", out_val, 29))
+ {
+ strcpy(random_spells[batch*10 + A2I(which)].name, out_val);
+ }
+ prt(tmp, 0, 0);
+ }
+ else
+ {
+ bell();
+ prt(tmp, 0, 0);
+ }
+
+ /* Wait for next command */
+ continue;
+ }
+
+ /* Comment */
+ if (which == '-')
+ {
+ prt("Comment which power: ", 0, 0);
+ which = tolower(inkey());
+
+ if (isalpha(which) && (A2I(which) <= mut_max))
+ {
+ strcpy(out_val, random_spells[batch*10 + A2I(which)].desc);
+ if (get_string("Comment this power: ", out_val, 29))
+ {
+ strcpy(random_spells[batch*10 + A2I(which)].desc, out_val);
+ }
+ prt(tmp, 0, 0);
+ }
+ else
+ {
+ bell();
+ prt(tmp, 0, 0);
+ }
+
+ /* Wait for next command */
+ continue;
+ }
+
+ if (isalpha(which) && isupper(which))
+ {
+ which = tolower(which);
+ c_prt(TERM_L_BLUE, format("%s : %s", random_spells[batch*10 + A2I(which)].name, random_spells[batch*10 + A2I(which)].desc), 0, 0);
+ inkey();
+ prt(tmp, 0, 0);
+ continue;
+ }
+ else if (isalpha(which) && (A2I(which) < mut_max))
+ {
+ /* Pick the power */
+ ret = &random_spells[batch * 10 + A2I(which)];
+
+ /* Leave the command loop */
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ /* Return selection */
+ return (ret);
+}
+
+
+/*
+ * Pick a random spell from a menu
+ */
+random_spell* select_spell(bool_ quick)
+{
+ char tmp[160];
+
+ char which;
+
+ int batch_max = (spell_num - 1) / 10;
+
+ random_spell *ret;
+
+
+ /* Too confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You can't use your powers while confused!");
+ return NULL;
+ }
+
+ /* No spells available */
+ if (spell_num == 0)
+ {
+ msg_print("There are no spells you can cast.");
+ return NULL;
+ }
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max));
+
+ prt(tmp, 0, 0);
+
+ while (1)
+ {
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ ret = NULL;
+
+ break;
+ }
+
+ if (which == '\r')
+ {
+ if (batch_max == 0)
+ {
+ ret = select_spell_from_batch(0, quick);
+
+ break;
+ }
+
+ continue;
+ }
+
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) <= batch_max))
+ {
+ ret = select_spell_from_batch(A2I(which), quick);
+
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ return (ret);
+}
+
+
+void do_cmd_powermage(void)
+{
+ random_spell *s_ptr;
+
+ u32b proj_flags;
+
+ int dir, chance;
+
+ int ty = 0, tx = 0;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ s_ptr = select_spell(FALSE);
+
+ if (s_ptr == NULL) return;
+
+ if (p_ptr->csp < s_ptr->mana)
+ {
+ msg_print("You do not have enough mana.");
+ return;
+ }
+
+ /* Spell failure chance */
+ chance = spell_chance_random(s_ptr);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+ char sfail[80];
+
+ /* Flush input if told so */
+ if (flush_failure) flush();
+
+ /* Insane players can see something strange */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("sfail.txt", sfail);
+ msg_format("A cloud of %s appears above you.", sfail);
+ }
+
+ /* Normal failure messages */
+ else
+ {
+ msg_print("You failed to get the spell off!");
+ }
+
+ sound(SOUND_FAIL);
+
+ /* Let time pass */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Mana is spent anyway */
+ p_ptr->csp -= s_ptr->mana;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+
+ return;
+ }
+
+
+ p_ptr->csp -= s_ptr->mana;
+
+ s_ptr->untried = FALSE;
+ proj_flags = s_ptr->proj_flags;
+
+ /* Hack -- Spell needs a target */
+ if ((s_ptr->proj_flags & PROJECT_BEAM) ||
+ (s_ptr->proj_flags & PROJECT_STOP))
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+
+ /* Mega-Hack -- Beam spells should continue through
+ * the target; bolt spells should stop at the
+ * target. --dsb */
+ if (s_ptr->proj_flags & PROJECT_BEAM)
+ proj_flags |= PROJECT_THRU;
+ }
+ else
+ {
+ /* Use the given direction */
+ ty = p_ptr->py + ddy[dir];
+ tx = p_ptr->px + ddx[dir];
+
+ /* Mega-Hack -- Both beam and bolt spells should
+ * continue through this fake target. --dsb */
+ proj_flags |= PROJECT_THRU;
+ }
+ }
+
+ if (s_ptr->proj_flags & PROJECT_BLAST)
+ {
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (s_ptr->proj_flags & PROJECT_VIEWABLE)
+ {
+ project_hack(s_ptr->GF, damroll(s_ptr->dam_dice, s_ptr->dam_sides));
+ }
+ else if (s_ptr->proj_flags & PROJECT_METEOR_SHOWER)
+ {
+ project_meteor(s_ptr->radius, s_ptr->GF,
+ damroll(s_ptr->dam_dice, s_ptr->dam_sides),
+ s_ptr->proj_flags);
+ }
+ else
+ {
+ project(0, s_ptr->radius, ty, tx,
+ damroll(s_ptr->dam_dice, s_ptr->dam_sides),
+ s_ptr->GF, proj_flags);
+ }
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Brand some ammunition. Used by Cubragol and a mage spell. The spell was
+ * moved here from cmd6.c where it used to be for Cubragol only. I've also
+ * expanded it to do either frost, fire or venom, at random. -GJW -KMW-
+ */
+void brand_ammo(int brand_type, int bolts_only)
+{
+ int a;
+
+ for (a = 0; a < INVEN_PACK; a++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[a];
+
+ if (bolts_only && (o_ptr->tval != TV_BOLT)) continue;
+
+ if (!bolts_only && (o_ptr->tval != TV_BOLT) &&
+ (o_ptr->tval != TV_ARROW) && (o_ptr->tval != TV_SHOT))
+ continue;
+
+ if (!artifact_p(o_ptr) && !ego_item_p(o_ptr) &&
+ !cursed_p(o_ptr))
+ break;
+ }
+
+ /* Enchant the ammo (or fail) */
+ if ((a < INVEN_PACK) && (rand_int(100) < 50))
+ {
+ object_type *o_ptr = &p_ptr->inventory[a];
+ char *ammo_name;
+ char *aura_name;
+ char msg[48];
+ int aura_type, r;
+
+ /* fire only */
+ if (brand_type == 1) r = 0;
+
+ /* cold only */
+ else if (brand_type == 2) r = 99;
+
+ /* No bias */
+ else r = rand_int(100);
+
+ if (r < 50)
+ {
+ aura_name = "fiery";
+ aura_type = EGO_FLAME;
+ }
+ else
+ {
+ aura_name = "frosty";
+ aura_type = EGO_FROST;
+ }
+
+ if (o_ptr->tval == TV_BOLT)
+ {
+ ammo_name = "bolts";
+ }
+ else if (o_ptr->tval == TV_ARROW)
+ {
+ ammo_name = "arrows";
+ }
+ else
+ {
+ ammo_name = "shots";
+ }
+
+ strnfmt(msg, 48, "Your %s are covered in a %s aura!",
+ ammo_name, aura_name);
+ msg_print(msg);
+
+ o_ptr->name2 = aura_type;
+
+ /* Apply the ego */
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ o_ptr->discount = 100;
+
+ enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM);
+ }
+ else
+ {
+ if (flush_failure) flush();
+ msg_print("The enchantment failed.");
+ }
+}
+
+
+/*
+ * From Kamband by Ivan Tkatchev
+ */
+void summon_monster(int sumtype)
+{
+ /* Take a turn */
+ energy_use = 100;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("This place seems devoid of life.");
+ msg_print(NULL);
+ return;
+ }
+
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level + randint(5), sumtype, TRUE))
+ {
+ msg_print("You summon some help.");
+ }
+ else
+ {
+ msg_print("You called, but no help came.");
+ }
+}
+
+
+
+/*
+ * Use a class power of Possessor
+ */
+void do_cmd_possessor()
+{
+ char ch, ext;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ while (TRUE)
+ {
+ if (!get_com("Use your [R]ace powers or your [I]ncarnating powers?", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'R') || (ch == 'r'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'I') || (ch == 'i'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ if (ext == 1)
+ {
+ bool_ use_great = FALSE;
+
+ if (p_ptr->disembodied)
+ {
+ msg_print("You don't currently own a body to use.");
+ return;
+ }
+
+ /* Do we have access to all the powers ? */
+ if (get_skill_scale(SKILL_POSSESSION, 100) >= r_info[p_ptr->body_monster].level)
+ use_great = TRUE;
+
+ use_symbiotic_power(p_ptr->body_monster, use_great, FALSE, FALSE);
+
+ if (p_ptr->csp < 0)
+ {
+ msg_print("You lose control of your body!");
+ if (!do_cmd_leave_body(FALSE))
+ {
+ cmsg_print(TERM_VIOLET,
+ "You are forced back into your body by your cursed items, "
+ "you suffer a system shock!");
+
+ p_ptr->chp = 1;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+ }
+ }
+ }
+ else if (ext == 2)
+ {
+ if (p_ptr->disembodied)
+ {
+ do_cmd_integrate_body();
+ }
+ else
+ {
+ do_cmd_leave_body(TRUE);
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+}
+
+
+/*
+ * Hook to determine if an object is contertible in an arrow/bolt
+ */
+static bool_ item_tester_hook_convertible(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_JUNK) || (o_ptr->tval == TV_SKELETON)) return TRUE;
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'archer'.
+ */
+void do_cmd_archer(void)
+{
+ int ext = 0;
+ char ch;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char com[80];
+
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ if (p_ptr->blind)
+ {
+ msg_print("You are blind!");
+ return;
+ }
+
+
+ if (get_skill(SKILL_ARCHERY) >= 20)
+ {
+ strnfmt(com, 80, "Create [S]hots, [A]rrows or [B]olts? ");
+ }
+ else if (get_skill(SKILL_ARCHERY) >= 10)
+ {
+ strnfmt(com, 80, "Create [S]hots or [A]rrows? ");
+ }
+ else
+ {
+ strnfmt(com, 80, "Create [S]hots? ");
+ }
+
+ while (TRUE)
+ {
+ if (!get_com(com, &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'S') || (ch == 's'))
+ {
+ ext = 1;
+ break;
+ }
+ if (((ch == 'A') || (ch == 'a')) && (get_skill(SKILL_ARCHERY) >= 10))
+ {
+ ext = 2;
+ break;
+ }
+ if (((ch == 'B') || (ch == 'b')) && (get_skill(SKILL_ARCHERY) >= 20))
+ {
+ ext = 3;
+ break;
+ }
+ }
+
+ /* Prepare for object creation */
+ q_ptr = &forge;
+
+ /**********Create shots*********/
+ if (ext == 1)
+ {
+ int x, y, dir;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some shots */
+ object_prep(q_ptr, lookup_kind(TV_SHOT, m_bonus(2, dun_level)));
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You make some ammo.");
+
+ (void)wall_to_mud(dir);
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+
+ /**********Create arrows*********/
+ else if (ext == 2)
+ {
+ int item;
+
+ cptr q, s;
+
+ item_tester_hook = item_tester_hook_convertible;
+
+ /* Get an item */
+ q = "Convert which item? ";
+ s = "You have no item to convert.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some arrows */
+ object_prep(q_ptr, lookup_kind(TV_ARROW, m_bonus(1, dun_level) + 1));
+ q_ptr->number = (byte)rand_range(15, 25);
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ msg_print("You make some ammo.");
+
+ inc_stack_size(item, -1);
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /**********Create bolts*********/
+ else if (ext == 3)
+ {
+ int item;
+
+ cptr q, s;
+
+ item_tester_hook = item_tester_hook_convertible;
+
+ /* Get an item */
+ q = "Convert which item? ";
+ s = "You have no item to convert.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some bolts */
+ object_prep(q_ptr, lookup_kind(TV_BOLT, m_bonus(1, dun_level) + 1));
+ q_ptr->number = (byte)rand_range(15, 25);
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ msg_print("You make some ammo.");
+
+ inc_stack_size(item, -1);
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+}
+
+/*
+ * Control whether shots are allowed to pierce
+ */
+void do_cmd_set_piercing(void)
+{
+ char ch;
+ char com[80];
+
+ if ((get_skill(SKILL_BOW) <= 25) && (get_skill(SKILL_XBOW) <= 25) &&
+ (get_skill(SKILL_SLING) <= 25))
+ {
+ msg_print("You can't fire piercing shots yet.");
+ return;
+ }
+
+ strnfmt(com, 80, "Allow shots to pierce? ");
+
+ while (TRUE)
+ {
+ if (!get_com(com, &ch))
+ {
+ break;
+ }
+ if ((ch == 'Y') || (ch == 'y'))
+ {
+ p_ptr->use_piercing_shots = 1;
+ msg_print("Piercing shots activated.");
+ break;
+ }
+ if ((ch == 'N') || (ch == 'n'))
+ {
+ p_ptr->use_piercing_shots = 0;
+ msg_print("Piercing shots deactivated.");
+ break;
+ }
+ }
+}
+/*
+ * Helper function to describe necro powers
+ */
+void necro_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_NECROMANCY);
+
+ strcpy(p, "");
+
+ switch (power)
+ {
+ case 0:
+ {
+ if (p_ptr->to_s)
+ strnfmt(p, 80, " power %dd%d+%d", 2 + (plev * 2 / 3), 4, (p_ptr->to_s * 2));
+ else
+ strnfmt(p, 80, " power %dd%d", 2 + (plev * 2 / 3), 4);
+ break;
+ }
+ case 2:
+ {
+ strnfmt(p, 80, " dur d%d+%d", 100 + (plev * 4), 200 + (plev * 3));
+ break;
+ }
+ case 3:
+ {
+ strnfmt(p, 80, " dur d%d+%d", 30 + (plev * 2), 50 + plev);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Cast a Necromancy spell
+ */
+void do_cmd_necromancer(void)
+{
+ int n = 0, b = 0;
+ int chance;
+ int dir;
+ int minfail = 0;
+ int plev = get_skill(SKILL_NECROMANCY);
+ magic_power spell;
+ int to_s2 = p_ptr->to_s / 2;
+ int mto_s2 = p_ptr->to_s / 2;
+
+
+ if (mto_s2 == 0) mto_s2 = 1;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, necro_powers, MAX_NECRO_POWERS, necro_info,
+ get_skill(SKILL_NECROMANCY), A_CON)) return;
+
+ spell = necro_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_CON]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_CON]];
+
+ /* Failure rate */
+ chance = clamp_failure_chance(chance, minfail);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (chance / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+ if (b < 10)
+ {
+ msg_print("Oh, no! You become undead!");
+
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ p_ptr->necro_extra2 = 2 * plev;
+ msg_format("You have to kill %d monster%s to be brought back to life.",
+ p_ptr->necro_extra2,
+ (p_ptr->necro_extra2 == 1) ? "" : "s");
+
+ /* MEGA-HACK !!! */
+ calc_hitpoints();
+
+ /* Enforce maximum */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else if (b < 40)
+ {
+ msg_print("Suddenly you feel that you're in a bad situation...");
+ summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type],
+ (plev >= 30) ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD);
+ }
+ else
+ {
+ msg_print("Your body is damaged by the horrible forces of the spell!");
+ take_hit(damroll(5, plev), "using necromancy unwisely");
+ }
+ }
+ }
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Horrify */
+ case 0:
+ {
+ int dam = damroll(2 + (plev * 2 / 3), 4) + (p_ptr->to_s * 2);
+
+ if (plev > 45)
+ {
+ project_hack(GF_STUN, dam);
+ project_hack(GF_TURN_ALL, dam);
+ }
+ else if (plev > 35)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_STUN, dir, dam, 3 + (plev / 10));
+ fire_ball(GF_TURN_ALL, dir, dam, 3 + (plev / 10));
+ }
+ else if (plev > 20)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_beam(GF_STUN, dir, dam);
+ fire_beam(GF_TURN_ALL, dir, dam);
+ }
+ else
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_bolt(GF_STUN, dir, dam);
+ fire_bolt(GF_TURN_ALL, dir, dam);
+ }
+
+ break;
+ }
+
+ /* Raise Death */
+ case 1:
+ {
+ fire_ball(GF_RAISE, 0, plev * 3, 1 + to_s2 + (plev / 10));
+
+ break;
+ }
+
+ /* Conjures temporary weapon */
+ case 2:
+ {
+ int dur = randint(100 + (plev * 4)) + 200 + (plev * 3);
+ object_type forge, *o_ptr = &forge;
+ int k_idx = test_item_name("& Necromantic Teeth~");
+
+ k_allow_special[k_idx] = TRUE;
+
+ object_prep(o_ptr, k_idx);
+ apply_magic(o_ptr, plev * 2, TRUE, TRUE, TRUE);
+
+ o_ptr->art_flags5 |= TR5_TEMPORARY;
+ o_ptr->timeout = dur;
+
+ /* These objects are "storebought" */
+ o_ptr->ident |= IDENT_MENTAL;
+ o_ptr->number = 1;
+
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ (void)inven_carry(o_ptr, FALSE);
+
+ k_allow_special[k_idx] = FALSE;
+
+ break;
+ }
+
+ /* Absorb souls */
+ case 3:
+ {
+ set_absorb_soul(randint(30 + (plev * 2)) + 50 + plev);
+ break;
+ }
+
+ /* Vampirism */
+ case 4:
+ {
+ int i;
+ if (!get_aim_dir(&dir)) return;
+ for (i = 0; i < 1 + to_s2 + (plev / 15); i++)
+ {
+ if (drain_life(dir, 100))
+ hp_player(100);
+ }
+
+ break;
+ }
+
+ /* Death */
+ case 5:
+ {
+ if (get_check("Using the Death word will leave you undead, with 1 DP. Do you *really* want to use it? "))
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_bolt(GF_DEATH, dir, 1);
+
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ p_ptr->necro_extra2 = plev + (rand_int(plev / 2) - (plev / 4));
+ msg_format("You have to kill %d monster%s to be brought back to life.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s");
+
+ /* MEGA-HACK !!! */
+ calc_hitpoints();
+
+ /* Enforce 1 DP */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage CON (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool_ perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your body!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_CON, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/* Runecrafters -- Move this into variable.c XXX XXX XXX */
+static s32b rune_combine = 0;
+
+/*
+ * Hook to determine if an object is "runestone"
+ */
+static bool_ item_tester_hook_runestone(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE2) return (FALSE);
+
+ if (o_ptr->sval != RUNE_STONE) return (FALSE);
+
+ if (o_ptr->pval != 0) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+static bool_ item_tester_hook_runestone_full(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE2) return (FALSE);
+
+ if (o_ptr->sval != RUNE_STONE) return (FALSE);
+
+ if (o_ptr->pval == 0) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+/*
+ * Hook to determine if an object is "rune-able"
+ */
+static bool_ item_tester_hook_runeable1(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE1) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+/*
+ * Hook to determine if an object is "rune-able"
+ */
+static bool_ item_tester_hook_runeable2(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE2) return (FALSE);
+
+ if (o_ptr->sval == RUNE_STONE) return (FALSE);
+
+ if (rune_combine & BIT(o_ptr->sval)) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+/*
+ * math.h(sqrt) is banned of angband so ... :)
+ */
+s32b sroot(s32b n)
+{
+ s32b i = n / 2;
+
+ if (n < 2) return (n);
+
+ while (1)
+ {
+ s32b err = (i - n / (i + 1)) / 2;
+
+ if (!err) break;
+
+ i -= err;
+ }
+
+ return ((n / i < i) ? (i - 1) : i);
+}
+
+
+/*
+ * Damage formula, for runes
+ */
+void rune_calc_power(s32b *power, s32b *powerdiv)
+{
+ /* Not too weak power(paranoia) */
+ *power = (*power < 1) ? 1 : *power;
+ *power += 3;
+
+ *power = 37 * sroot(*power) / 10;
+
+ /* To reduce the high level power, while increasing the low levels */
+ *powerdiv = *power / 3;
+ if (*powerdiv < 1) *powerdiv = 1;
+
+ /* Use the spell multiplicator */
+ *power *= (p_ptr->to_s / 2) ? (p_ptr->to_s / 2) : 1;
+}
+
+
+/*
+ * Return percentage chance of runespell failure.
+ */
+int spell_chance_rune(rune_spell* spell)
+{
+ int chance, minfail;
+
+ s32b power = spell->mana, power_rune = 0, powerdiv = 0;
+
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ power_rune += 4;
+ }
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ power_rune += 3;
+ }
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ power_rune += 2;
+ }
+ if (spell->rune2 & RUNE_RAY)
+ {
+ power_rune += 1;
+ }
+
+ rune_calc_power(&power, &powerdiv);
+
+ chance = (5 * power_rune) + (power);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1);
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]];
+
+ /* Return the chance */
+ return clamp_failure_chance(chance, minfail);
+}
+
+
+/*
+ * Combine the Runes
+ */
+int rune_exec(rune_spell *spell, int cost)
+{
+ int dir, power_rune = 0, mana_used, plev = get_skill(SKILL_RUNECRAFT);
+
+ int chance;
+
+ s32b power, powerdiv;
+
+ int rad = 0, ty = -1, tx = -1, dam = 0, flg = 0;
+
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ power_rune += 4;
+ }
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ power_rune += 3;
+ }
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ power_rune += 2;
+ }
+ if (spell->rune2 & RUNE_RAY)
+ {
+ power_rune += 1;
+ }
+
+
+ power = spell->mana;
+
+ if (cost && ((power * cost / 100) > p_ptr->csp - (power_rune * (plev / 5))))
+ {
+ power = p_ptr->csp - (power_rune * (plev / 5));
+ mana_used = power + (power_rune * (plev / 5));
+ }
+ else
+ {
+ mana_used = (power * cost / 100) + (power_rune * (plev / 5));
+ }
+
+ rune_calc_power(&power, &powerdiv);
+
+ dam = damroll(powerdiv, power);
+
+ if (wizard) msg_format("Rune %dd%d = dam %d", powerdiv, power, dam);
+
+ /* Extract the base spell failure rate */
+ chance = spell_chance_rune(spell);
+
+ /* Failure ? */
+ if (rand_int(100) < chance)
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+ char sfail[80];
+
+ /* Flush input if told so */
+ if (flush_failure) flush();
+
+ /* Insane players can see something strange */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("sfail.txt", sfail);
+ msg_format("A cloud of %s appears above you.", sfail);
+ }
+
+ /* Normal failure messages */
+ else
+ {
+ msg_print("You failed to get the spell off!");
+ }
+
+ sound(SOUND_FAIL);
+
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+ return (mana_used);
+ }
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ flg |= (PROJECT_VIEWABLE);
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_KILL);
+ flg |= (PROJECT_ITEM);
+ flg |= (PROJECT_GRID);
+ flg |= (PROJECT_METEOR_SHOWER);
+ rad = (power / 8 == 0) ? 1 : power / 8;
+ rad = (rad > 10) ? 10 : rad;
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_KILL);
+ flg |= (PROJECT_ITEM);
+ flg |= (PROJECT_GRID);
+ rad = (power / 8 == 0) ? 1 : power / 8;
+ rad = (rad > 10) ? 10 : rad;
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (spell->rune2 & RUNE_RAY)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_KILL);
+ flg |= (PROJECT_BEAM);
+ ty = -1;
+ tx = -1;
+ }
+ if (spell->rune2 & RUNE_ARROW)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_STOP);
+ flg |= (PROJECT_KILL);
+ ty = -1;
+ tx = -1;
+ }
+ if (spell->rune2 & RUNE_SELF)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_STOP);
+ flg |= (PROJECT_KILL);
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ unsafe = TRUE;
+ }
+
+ if ((ty == -1) && (tx == -1))
+ {
+ if (!get_aim_dir(&dir)) return (mana_used);
+
+ /* Use the given direction */
+ tx = p_ptr->px + ddx[dir];
+ ty = p_ptr->py + ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+ }
+
+ if (flg & PROJECT_VIEWABLE)
+ {
+ project_hack(spell->type, dam);
+ }
+ else if (flg & PROJECT_METEOR_SHOWER)
+ {
+ project_meteor(rad, spell->type, dam, flg);
+ }
+ else project(0, rad, ty, tx, dam, spell->type, flg);
+
+ if (unsafe) unsafe = FALSE;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+
+ return (mana_used);
+}
+
+
+/*
+ * Test if all runes needed at in the player p_ptr->inventory
+ */
+bool_ test_runespell(rune_spell *spell)
+{
+ int i;
+
+ object_type *o_ptr;
+
+ bool_ typeok = FALSE;
+
+ int rune2 = 0;
+
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+
+ /* Does the rune1(type) match ? */
+ if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == spell->type))
+ {
+ typeok = TRUE;
+ }
+
+ if ((o_ptr->tval == TV_RUNE2) && (o_ptr->sval != RUNE_STONE))
+ {
+ /* Add it to the list */
+ rune2 |= 1 << o_ptr->sval;
+ }
+ }
+
+ /* Need all runes to be present */
+ return (typeok && ((rune2 & spell->rune2) == spell->rune2));
+}
+
+
+/*
+ * Ask for rune, rune2 and mana
+ */
+bool_ get_runespell(rune_spell *spell)
+{
+ int item, power_rune = 0, rune2 = 0, plev = get_skill(SKILL_RUNECRAFT);
+
+ s32b power;
+
+ int type = 0;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ bool_ OK = FALSE;
+
+
+ rune_combine = 0;
+
+ /* Restrict choices to unused runes */
+ item_tester_hook = item_tester_hook_runeable1;
+
+ /* Get an item */
+ q = "Use which rune? ";
+ s = "You have no rune to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return FALSE;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+ type = o_ptr->sval;
+
+ while (1)
+ {
+ /* Restrict choices to unused secondary runes */
+ item_tester_hook = item_tester_hook_runeable2;
+
+ OK = !get_item(&item, q, s, (USE_INVEN | USE_FLOOR));
+
+ if (OK) break;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ rune_combine |= 1 << o_ptr->sval;
+ rune2 |= 1 << o_ptr->sval;
+ }
+
+ if (!rune2)
+ {
+ msg_print("You have not selected a second rune!");
+ return (FALSE);
+ }
+
+ power = get_quantity("Which amount of Mana?",
+ p_ptr->csp - (power_rune * (plev / 5)));
+ if (power < 1) power = 1;
+
+ spell->mana = power;
+ spell->type = type;
+ spell->rune2 = rune2;
+
+ return (TRUE);
+}
+
+
+void do_cmd_rune(void)
+{
+ rune_spell spell;
+
+
+ /* Require some mana */
+ if (p_ptr->csp <= 0)
+ {
+ msg_print("You have no mana!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ if (!get_runespell(&spell)) return;
+
+ /* Execute at normal mana cost */
+ p_ptr->csp -= rune_exec(&spell, 100);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Print a batch of runespells.
+ */
+static void print_runespell_batch(int batch, int max)
+{
+ char buff[80];
+
+ rune_spell* spell;
+
+ int i;
+
+ s32b power, powerdiv;
+
+ int p, dp;
+
+
+ prt(format(" %-30s Fail Mana Power", "Name"), 1, 20);
+
+ for (i = 0; i < max; i++)
+ {
+ spell = &rune_spells[batch * 10 + i];
+
+ power = spell->mana;
+ rune_calc_power(&power, &powerdiv);
+ p = power;
+ dp = powerdiv;
+
+ strnfmt(buff, 80, " %c) %-30s %4d%% %4d %dd%d ", I2A(i), spell->name,
+ spell_chance_rune(spell), spell->mana, dp, p);
+
+ prt(buff, 2 + i, 20);
+ }
+ prt("", 2 + i, 20);
+}
+
+
+
+/*
+ * List ten random spells and ask to pick one.
+ */
+
+static rune_spell* select_runespell_from_batch(int batch, bool_ quick,
+ int *s_idx)
+{
+ char tmp[160];
+
+ char out_val[30];
+
+ char which;
+
+ int mut_max = 10;
+
+ rune_spell* ret;
+
+
+ character_icky = TRUE;
+ Term_save();
+
+ if (rune_num < (batch + 1) * 10)
+ {
+ mut_max = rune_num - batch * 10;
+ }
+
+ strnfmt(tmp, 160, "(a-%c, * to list, / to rename, - to comment) Select a power: ",
+ I2A(mut_max - 1));
+
+ prt(tmp, 0, 0);
+
+ if (quick)
+ {
+ print_runespell_batch(batch, mut_max);
+ }
+
+ while (1)
+ {
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ *s_idx = -1;
+ ret = NULL;
+ break;
+ }
+ else if ((which == '*') || (which == '?') || (which == ' '))
+ {
+ print_runespell_batch(batch, mut_max);
+ }
+ else if ((which == '\r') && (mut_max == 1))
+ {
+ *s_idx = batch * 10;
+ ret = &rune_spells[batch * 10];
+ break;
+ }
+ else if (which == '/')
+ {
+ prt("Rename which power: ", 0, 0);
+ which = tolower(inkey());
+
+ if (isalpha(which) && (A2I(which) <= mut_max))
+ {
+ strcpy(out_val, rune_spells[batch*10 + A2I(which)].name);
+ if (get_string("Name this power: ", out_val, 29))
+ {
+ strcpy(rune_spells[batch*10 + A2I(which)].name, out_val);
+ }
+ prt(tmp, 0, 0);
+ }
+ else
+ {
+ bell();
+ prt(tmp, 0, 0);
+ }
+ }
+ else
+ {
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) < mut_max))
+ {
+ *s_idx = batch * 10 + A2I(which);
+ ret = &rune_spells[batch * 10 + A2I(which)];
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+ }
+
+ Term_load();
+ character_icky = FALSE;
+
+ return (ret);
+}
+
+
+/*
+ * Pick a random spell from a menu
+ */
+
+rune_spell* select_runespell(bool_ quick, int *s_idx)
+{
+ char tmp[160];
+
+ char which;
+
+ int batch_max = (rune_num - 1) / 10;
+
+ if (rune_num == 0)
+ {
+ msg_print("There are no runespells you can cast.");
+ return (NULL);
+ }
+
+ character_icky = TRUE;
+ Term_save();
+
+ strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max));
+
+ prt(tmp, 0, 0);
+
+ while (1)
+ {
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ Term_load();
+ character_icky = FALSE;
+ return (NULL);
+ }
+ else if ((which == '\r') && (batch_max == 0))
+ {
+ Term_load();
+ character_icky = FALSE;
+ return (select_runespell_from_batch(0, quick, s_idx));
+
+ }
+ else
+ {
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) <= batch_max))
+ {
+ Term_load();
+ character_icky = FALSE;
+ return (select_runespell_from_batch(A2I(which), quick, s_idx));
+ }
+ else
+ {
+ bell();
+ }
+ }
+ }
+}
+
+
+/*
+ * Cast a memorized runespell
+ * Note that the only limits are antimagic & conf, NOT blind
+ */
+void do_cmd_rune_cast()
+{
+ rune_spell *s_ptr;
+
+ int s_idx;
+
+
+ /* Require some mana */
+ if (p_ptr->csp <= 0)
+ {
+ msg_print("You have no mana!");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ s_ptr = select_runespell(FALSE, &s_idx);
+
+ if (s_ptr == NULL) return;
+
+ /* Need the runes */
+ if (!test_runespell(s_ptr))
+ {
+ msg_print("You lack some essential rune(s) for this runespell!");
+ return;
+ }
+
+ /* Execute at normal mana cost */
+ p_ptr->csp -= rune_exec(s_ptr, 100);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Cast a runespell from a carved runestone
+ */
+void do_cmd_runestone()
+{
+ rune_spell s_ptr;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ int item;
+
+
+ /* Require some mana */
+ if (p_ptr->csp <= 0)
+ {
+ msg_print("You have no mana!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* Restrict choices to unused runes */
+ item_tester_hook = item_tester_hook_runestone_full;
+
+ /* Get an item */
+ q = "Cast from which runestone? ";
+ s = "You have no runestone to cast from.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ s_ptr.type = o_ptr->pval;
+ s_ptr.rune2 = o_ptr->pval2;
+ s_ptr.mana = o_ptr->pval3;
+
+ /* Execute less mana */
+ p_ptr->csp -= rune_exec(&s_ptr, 75);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Add a runespell to the list
+ */
+void do_cmd_rune_add_mem()
+{
+ rune_spell s_ptr;
+
+ rune_spell *ds_ptr = &rune_spells[rune_num];
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+
+ if (rune_num >= MAX_RUNES)
+ {
+ msg_print("You have already learn the maximun number of runespells!");
+ return;
+ }
+
+ if (!get_runespell(&s_ptr)) return;
+
+ ds_ptr->type = s_ptr.type;
+ ds_ptr->rune2 = s_ptr.rune2;
+ ds_ptr->mana = s_ptr.mana;
+ strcpy(ds_ptr->name, "Unnamed Runespell");
+
+ get_string("Name this runespell: ", ds_ptr->name, 29);
+
+ rune_num++;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Carve a runespell onto a Runestone
+ */
+void do_cmd_rune_carve()
+{
+ rune_spell s_ptr;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ int item, i;
+
+ char out_val[80];
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ if (!get_check("Beware, this will destroy the involved runes, continue?"))
+ {
+ return;
+ }
+
+ if (!get_runespell(&s_ptr)) return;
+
+ /* Restrict choices to unused runes */
+ item_tester_hook = item_tester_hook_runestone;
+
+ /* Get an item */
+ q = "Use which runestone? ";
+ s = "You have no runestone to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ o_ptr->pval = s_ptr.type;
+ o_ptr->pval2 = s_ptr.rune2;
+ o_ptr->pval3 = s_ptr.mana;
+
+ /* Start with nothing */
+ strcpy(out_val, "");
+
+ /* Use old inscription */
+ if (o_ptr->note)
+ {
+ /* Start with the old inscription */
+ strcpy(out_val, quark_str(o_ptr->note));
+ }
+
+ /* Get a new inscription (possibly empty) */
+ if (get_string("Name this runestone: ", out_val, 80))
+ {
+ /* Save the inscription */
+ o_ptr->note = quark_add(out_val);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+
+ /* Delete the runes */
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ if (o_ptr->k_idx)
+ {
+ bool_ do_del = FALSE;
+
+ if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == s_ptr.type)) do_del = TRUE;
+ if ((o_ptr->tval == TV_RUNE2) && (BIT(o_ptr->sval) & s_ptr.rune2)) do_del = TRUE;
+
+ if (do_del)
+ {
+ inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE);
+ }
+ }
+ }
+
+ /* Take a turn -- Carving takes a LONG time */
+ energy_use = 400;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Remove a runespell
+ */
+void do_cmd_rune_del()
+{
+ rune_spell *s_ptr;
+
+ int s_idx;
+
+ int i;
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ s_ptr = select_runespell(FALSE, &s_idx);
+
+ if (s_ptr == NULL) return;
+
+ /* Delete and move */
+ for (i = s_idx + 1; i < rune_num; i++)
+ {
+ rune_spells[i - 1].type = rune_spells[i].type;
+ rune_spells[i - 1].rune2 = rune_spells[i].rune2;
+ rune_spells[i - 1].mana = rune_spells[i].mana;
+ strcpy(rune_spells[i - 1].name, rune_spells[i].name);
+ }
+ rune_num--;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+void do_cmd_rune_add()
+{
+ int ext = 0;
+
+ char ch;
+
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("Add to [M]emory(need runes to cast) or "
+ "Carve a [R]unestone(less mana to cast)", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'M') || (ch == 'm'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'R') || (ch == 'r'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ /* Create a Spell in memory */
+ case 1:
+ {
+ do_cmd_rune_add_mem();
+ break;
+ }
+
+ /* Carve a Runestone */
+ case 2:
+ {
+ do_cmd_rune_carve();
+ break;
+ }
+ }
+}
+
+
+void do_cmd_runecrafter()
+{
+ int ext = 0;
+
+ char ch;
+
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("Rune Spell:[C]reate, [D]elete, C[a]st, D[i]rectly Cast "
+ "or Use [R]unestone", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'C') || (ch == 'c'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'D') || (ch == 'd'))
+ {
+ ext = 2;
+ break;
+ }
+ if ((ch == 'A') || (ch == 'a'))
+ {
+ ext = 3;
+ break;
+ }
+ if ((ch == 'I') || (ch == 'i'))
+ {
+ ext = 4;
+ break;
+ }
+ if ((ch == 'R') || (ch == 'r'))
+ {
+ ext = 5;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ /* Create a Spell */
+ case 1:
+ {
+ do_cmd_rune_add();
+ break;
+ }
+
+ /* Delete a Spell */
+ case 2:
+ {
+ do_cmd_rune_del();
+ break;
+ }
+
+ /* Cast a Spell */
+ case 3:
+ {
+ do_cmd_rune_cast();
+ break;
+ }
+
+ /* Directly Cast a Spell */
+ case 4:
+ {
+ do_cmd_rune();
+ break;
+ }
+
+ /* Cast a Runestone */
+ case 5:
+ {
+ do_cmd_runestone();
+ break;
+ }
+ }
+}
+
+
+void do_cmd_unbeliever_antimagic()
+{
+ if (get_skill(SKILL_ANTIMAGIC) < 20)
+ {
+ msg_print("You must have at least a level 20 antimagic skill "
+ "to be able to disrupt the magic continuum.");
+ return;
+ }
+
+ if (p_ptr->antimagic_extra & CLASS_ANTIMAGIC)
+ {
+ p_ptr->antimagic_extra &= ~CLASS_ANTIMAGIC;
+ msg_print("You stop disrupting the magic continuum.");
+ }
+ else
+ {
+ p_ptr->antimagic_extra |= CLASS_ANTIMAGIC;
+ msg_print("You start disrupting the magic continuum.");
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+}
+
+
+/*
+ * Detect traps + kill traps
+ */
+void do_cmd_unbeliever()
+{
+ int ext = 0;
+
+ char ch;
+
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("Disrupt [C]ontinuum or [D]etect Traps", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'C') || (ch == 'c'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'D') || (ch == 'd'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ /* Disrupt Continuum */
+ case 1:
+ {
+ do_cmd_unbeliever_antimagic();
+ break;
+ }
+
+ /* Detect Traps */
+ case 2:
+ {
+ s16b skill = get_skill(SKILL_ANTIMAGIC);
+
+ if (skill < 25)
+ {
+ msg_print("You cannot use your detection abilities yet.");
+ break;
+ }
+
+ detect_traps(DEFAULT_RADIUS);
+
+ if (skill >= 35) destroy_doors_touch();
+
+ break;
+ }
+ }
+}
+
+/*
+ * Hook to determine if an object is totemable
+ */
+static bool_ item_tester_hook_totemable(object_type *o_ptr)
+{
+ /* Only full corpse */
+ if ((o_ptr->tval == TV_CORPSE) &&
+ ((o_ptr->sval == SV_CORPSE_CORPSE) || (o_ptr->sval == SV_CORPSE_SKELETON)))
+ {
+ return (TRUE);
+ }
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Summoners
+ */
+void do_cmd_summoner_extract()
+{
+ object_type *o_ptr, forge, *q_ptr;
+
+ cptr q, s;
+
+ int item, r;
+
+ bool_ partial;
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ item_tester_hook = item_tester_hook_totemable;
+
+ /* Get an item */
+ q = "Use which corpse? ";
+ s = "You have no corpse to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+
+ if (r_info[o_ptr->pval2].flags1 & RF1_UNIQUE)
+ {
+ partial = FALSE;
+ }
+ else
+ {
+ partial = get_check("Do you want to create a partial totem?");
+ }
+
+ r = o_ptr->pval2;
+
+ inc_stack_size(item, -1);
+
+ if (magik(r_info[o_ptr->pval2].level - get_skill(SKILL_SUMMON)))
+ {
+ msg_print("You failed to extract a totem.");
+ energy_use += 100;
+ return;
+ }
+
+ /* Prepare for object creation */
+ q_ptr = &forge;
+
+ /* Create the object */
+ object_prep(q_ptr, lookup_kind(TV_TOTEM, partial ? 1 : 2));
+ q_ptr->pval = r;
+ q_ptr->pval2 = 0;
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You extract a totem from the dead corpse.");
+ energy_use += 100;
+}
+
+
+void summon_true(int r_idx, int item)
+{
+ int i, status, x = 1, y = 1, rx, ry = 0, chance;
+
+ bool_ used;
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+
+ /* Uniques are less likely to be nice */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ /* Because it's unique, it will always be destroyed */
+ used = TRUE;
+
+ /* About twice as hard as non-uniques */
+ chance = (get_skill(SKILL_SUMMON) * 70 / (r_ptr->level + 1));
+
+ if (magik(chance))
+ {
+ status = MSTATUS_PET;
+ }
+ else
+ {
+ status = MSTATUS_ENEMY;
+ }
+ }
+
+ /* Non-uniques are easier to handle */
+ else
+ {
+ if (get_skill(SKILL_SUMMON) == 0)
+ {
+ used = TRUE;
+ }
+ else
+ {
+ /* It can be used multiple times */
+ used = FALSE;
+
+ /* But it is not 100% sure (note: skill > 0) */
+ chance = (r_ptr->level * 25 / get_skill(SKILL_SUMMON));
+ if (magik(chance)) used = TRUE;
+ }
+
+ chance = (get_skill(SKILL_SUMMON) * 130 / (r_ptr->level + 1));
+
+ if (magik(chance))
+ {
+ status = MSTATUS_PET;
+ }
+ else
+ {
+ status = MSTATUS_ENEMY;
+ }
+ }
+
+ /* Find a grid where the monster is summoned */
+ for (i = 0; i < 40; i++)
+ {
+ rx = (rand_int(8) - 4) + p_ptr->px;
+ ry = (rand_int(8) - 4) + p_ptr->py;
+ if (in_bounds(ry, rx) && cave_empty_bold(ry, rx))
+ {
+ x = rx;
+ y = ry;
+ break;
+ }
+ }
+
+ /* No room found */
+ if (i == 40)
+ {
+ msg_print("The summoning fails due to lack of room.");
+ return;
+ }
+
+ /* Summon the monster */
+ bypass_r_ptr_max_num = TRUE;
+ if (!(i = place_monster_one (y, x, r_idx, 0, 0, status)))
+ {
+ msg_print("The summoning fails.");
+ }
+ else
+ {
+ m_list[i].status = status;
+ m_list[i].mflag |= MFLAG_NO_DROP;
+ }
+ bypass_r_ptr_max_num = FALSE;
+
+ /* Destroy the totem if the used flag is set */
+ if (used)
+ {
+ /* Eliminate the totem */
+ inc_stack_size(item, -1);
+ }
+
+ /* Done */
+ return;
+}
+
+
+void do_cmd_summoner_summon()
+{
+ int item, x = 1, y = 1, rx, ry, m_idx = 0, i;
+
+ cptr q, s;
+
+ object_type *o_ptr;
+
+ monster_type *m_ptr;
+
+
+ /* Which Totem? */
+ item_tester_tval = TV_TOTEM;
+
+ q = "Summon from which Totem?";
+ s = "There are no totems to summon from!";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Access the item */
+ o_ptr = get_object(item);
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* True Totems have their own function. */
+ if (o_ptr->sval == 2)
+ {
+ summon_true(o_ptr->pval, item);
+ return;
+ }
+
+ /* Handle partial totems */
+
+ /* Find a grid where the monster is summoned */
+ for (i = 0; i < 40; i++)
+ {
+ rx = (rand_int(8) - 4) + p_ptr->px;
+ ry = (rand_int(8) - 4) + p_ptr->py;
+ if (in_bounds(ry, rx) && cave_empty_bold(ry, rx))
+ {
+ x = rx;
+ y = ry;
+ break;
+ }
+ }
+
+ /* No room found */
+ if (i == 40)
+ {
+ msg_print("The summoning fails due to lack of room.");
+ return;
+ }
+
+ /* Summon the monster */
+ bypass_r_ptr_max_num = TRUE;
+ place_monster_one_no_drop = TRUE;
+ m_idx = place_monster_one(y, x, o_ptr->pval, 0, 0, MSTATUS_PET);
+ bypass_r_ptr_max_num = FALSE;
+
+ /* Failure. */
+ if (!m_idx)
+ {
+ msg_print("The summoning fails.");
+ }
+
+ /* Mark the monster as a "partial" ally */
+ m_ptr = &m_list[m_idx];
+ m_ptr->mflag |= MFLAG_PARTIAL | MFLAG_NO_DROP;
+}
+
+
+void do_cmd_summoner(void)
+{
+ int ext = 0;
+
+ char ch;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* not if blind */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("[E]xtract a totem, [S]ummon", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'E') || (ch == 'e'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 's') || (ch == 'S'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ case 1:
+ {
+ do_cmd_summoner_extract();
+ break;
+ }
+
+ case 2:
+ {
+ do_cmd_summoner_summon();
+ break;
+ }
+ }
+}
+
+
+/*
+ * Fighters may invoke The Rush.
+ */
+void do_cmd_blade(void)
+{
+ /* Are we already Rushed? */
+ if (p_ptr->rush)
+ {
+ msg_format("You have %d turns of The Rush remaining", p_ptr->rush);
+ return;
+ }
+
+ /* Are you sure? */
+ if (!get_check("Are you sure you want to invoke The Rush?")) return;
+
+ /* Let's Rush! */
+ set_rush(2 + p_ptr->lev / 2 + randint(p_ptr->lev / 2));
+}
+
+
+/*
+ * Dodge Chance Feedback.
+ */
+void use_ability_blade(void)
+{
+ int chance = p_ptr->dodge_chance - ((dun_level * 5) / 6);
+
+ if (chance < 0) chance = 0;
+ if (wizard)
+ {
+ msg_format("You have exactly %d chances of dodging a level %d monster.", chance, dun_level);
+ }
+
+ if (chance < 5)
+ {
+ msg_format("You have almost no chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 10)
+ {
+ msg_format("You have a slight chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 20)
+ {
+ msg_format("You have a significant chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 40)
+ {
+ msg_format("You have a large chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 70)
+ {
+ msg_format("You have a high chance of dodging a level %d monster.", dun_level);
+ }
+ else
+ {
+ msg_format("You will usually dodge successfully a level %d monster.", dun_level);
+ }
+
+ return;
+}
+
+/*
+ * Helper function to describe symbiotic powers
+ */
+void symbiotic_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_SYMBIOTIC);
+
+ strcpy(p, "");
+
+ switch (power)
+ {
+ case 2:
+ {
+ strnfmt(p, 80, " power %d", plev * 3);
+ break;
+ }
+ case 5:
+ {
+ strnfmt(p, 80, " heal %d%%", 15 + get_skill_scale(SKILL_SYMBIOTIC, 35));
+ break;
+ }
+ }
+}
+
+
+/*
+ * Cast a symbiotic spell
+ */
+void do_cmd_symbiotic(void)
+{
+ int n = 0;
+ int chance;
+ int minfail = 0;
+ int plev = get_skill(SKILL_SYMBIOTIC);
+ magic_power spell;
+
+ /* Get the carried monster */
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info,
+ get_skill(SKILL_SYMBIOTIC), A_INT)) return;
+
+ spell = symbiotic_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]];
+
+ /* Failure rate */
+ chance = clamp_failure_chance(chance, minfail);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+ }
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ case 0:
+ {
+ int dir, x, y;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+ object_type *q_ptr;
+ object_type forge;
+
+ msg_print("Hypnotise which pet?");
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (c_ptr->m_idx)
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if (!(r_ptr->flags1 & RF1_NEVER_MOVE))
+ {
+ msg_print("You can only hypnotise monsters that cannot move.");
+ }
+ else if (m_ptr->status < MSTATUS_PET)
+ {
+ msg_print("You can only hypnotise pets and companions.");
+ }
+ else if (r_ptr->flags9 & RF9_SPECIAL_GENE)
+ {
+ msg_print("You cannot hypnotise this monster.");
+ }
+ else
+ {
+ /* TODO fix this hack hack hack hackity hack with ToME 3 flags */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_HYPNOS, 1));
+ q_ptr->number = 1;
+ q_ptr->pval = m_ptr->r_idx;
+ q_ptr->pval2 = m_ptr->hp;
+ q_ptr->pval3 = m_ptr->maxhp;
+ /* overflow alert */
+ q_ptr->exp = m_ptr->exp;
+ q_ptr->elevel = m_ptr->level;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ q_ptr->ident |= IDENT_STOREB;
+
+ drop_near(q_ptr, 0, y, x);
+
+ delete_monster(y, x);
+ health_who = 0;
+ }
+ }
+ else
+ {
+ msg_print("There is no pet here !");
+ }
+
+ break;
+ }
+
+ case 1:
+ {
+ monster_type *m_ptr;
+ int m_idx;
+ int item, x, y, d;
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ /* Restrict choices to monsters */
+ item_tester_tval = TV_HYPNOS;
+
+ /* Get an item */
+ q = "Awaken which monster? ";
+ s = "You have no monster to awaken.";
+ if (!get_item(&item, q, s, (USE_FLOOR))) return;
+
+ o_ptr = &o_list[0 - item];
+
+ d = 2;
+ while (d < 100)
+ {
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ if (cave_floor_bold(y, x) && (!cave[y][x].m_idx)) break;
+
+ d++;
+ }
+
+ if (d >= 100) return;
+
+ if ((m_idx = place_monster_one(y, x, o_ptr->pval, 0, FALSE, MSTATUS_PET)) == 0) return;
+
+ /* TODO fix this hack hack hack hackity hack with ToME 3 flags */
+ /* Have to be careful here; releasing the symbiote into a
+ * dungeon with leveled monsters will level the symbiote
+ * before we can get hold of it. We'll be nice and use the
+ * larger of the saved exp and the exp that the newly-generated
+ * monster starts with. */
+ m_ptr = &m_list[m_idx];
+ if (m_ptr->exp < o_ptr->exp)
+ {
+ m_ptr->exp = o_ptr->exp;
+ monster_check_experience(m_idx, TRUE);
+ if (m_ptr->level != o_ptr->elevel)
+ cmsg_format(TERM_VIOLET, "ERROR: level-%d HYPNOS becomes level-%d symbiote", o_ptr->elevel, m_ptr->level);
+ }
+ m_ptr->hp = o_ptr->pval2;
+ m_ptr->maxhp = o_ptr->pval3;
+
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ break;
+ }
+
+ /* Charm */
+ case 2:
+ {
+ int dir;
+
+ if (!get_aim_dir(&dir)) return;
+
+ fire_bolt(GF_CHARM_UNMOVING, dir, plev * 3);
+
+ break;
+ }
+
+ /* Life Share */
+ case 3:
+ {
+ s32b percent1, percent2;
+
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ percent1 = p_ptr->chp;
+ percent1 = (percent1 * 100) / p_ptr->mhp;
+
+ percent2 = o_ptr->pval2;
+ percent2 = (percent2 * 100) / o_ptr->pval3;
+
+ /* Now get the average */
+ percent1 = (percent1 + percent2) / 2;
+
+ /* And set the hp of monster & player to it */
+ p_ptr->chp = (percent1 * p_ptr->mhp) / 100;
+ o_ptr->pval2 = (percent1 * o_ptr->pval3) / 100;
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_MH);
+
+ break;
+ }
+
+ /* Minor Symbiotic Powers */
+ case 4:
+ {
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ if (0 > use_symbiotic_power(o_ptr->pval, FALSE, FALSE, TRUE))
+ return;
+
+ break;
+ }
+
+ /* Heal Symbiote */
+ case 5:
+ {
+ int hp;
+
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ hp = o_ptr->pval3 * (15 + get_skill_scale(SKILL_SYMBIOTIC, 35)) / 100;
+ o_ptr->pval2 += hp;
+ if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3;
+
+ msg_format("%s is healed.", symbiote_name(TRUE));
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_MH);
+
+ break;
+ }
+
+
+ /* Major Symbiotic Powers */
+ case 6:
+ {
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ if(0 > use_symbiotic_power(o_ptr->pval, TRUE, FALSE, TRUE))
+ return;
+
+ break;
+ }
+
+ /* Summon never-moving pet */
+ case 7:
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE);
+
+ break;
+ }
+
+ /* Force Symbiosis */
+ case 8:
+ {
+ int y, x;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+
+ if (!tgt_pt(&x, &y)) return;
+
+ c_ptr = &cave[y][x];
+
+ if (!c_ptr->m_idx) break;
+
+ m_ptr = &m_list[c_ptr->m_idx];
+ use_symbiotic_power(m_ptr->r_idx, TRUE, FALSE, TRUE);
+
+ break;
+ }
+
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage CON (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool_ perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your body!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_CHR, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/*
+ * Boulder creation .. sorry :)
+ */
+void do_cmd_create_boulder()
+{
+ int x, y, dir;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ /* Granite -- How about other wall types? */
+ if (((c_ptr->feat >= FEAT_WALL_EXTRA) && (c_ptr->feat <= FEAT_WALL_SOLID)) ||
+ ((c_ptr->feat >= FEAT_MAGMA_H) && (c_ptr->feat <= FEAT_QUARTZ_K)) ||
+ ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_QUARTZ)))
+ {
+ object_type forge;
+ object_type *q_ptr;
+
+ (void)wall_to_mud(dir);
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some shots */
+ object_prep(q_ptr, lookup_kind(TV_JUNK, SV_BOULDER));
+ q_ptr->number = (byte)rand_range(2, 5);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You make some boulders.");
+
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Take a turn */
+ energy_use = 100;
+ }
+}
+
+/*
+ * Clamp failure chance
+ */
+extern int clamp_failure_chance(int chance, int minfail)
+{
+ if (minfail < 0) minfail = 0;
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ return chance;
+}
diff --git a/src/cmovie.c b/src/cmovie.c
new file mode 100644
index 00000000..d1459e02
--- /dev/null
+++ b/src/cmovie.c
@@ -0,0 +1,496 @@
+/* File: cmovie.c */
+
+/* Purpose: play cmovie files -DarkGod-Improv- */
+
+#include "angband.h"
+
+/*
+ * Play a given cmovie
+ */
+s16b do_play_cmovie(cptr cmov_file)
+{
+ FILE *fff;
+
+ int y, line = 0, x;
+ int delay;
+
+ char *s;
+
+ char buf[1024];
+ char cbuf[90];
+ char ch;
+
+ char mode = 0;
+
+
+ /* Cmovie files are moved to the user directory on the multiuser systems */
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_CMOV, cmov_file);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Read the file */
+ fff = my_fopen(buf, "r");
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+ /* Save screen */
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+ /* Give some usefull info */
+ prt("While viewing the movie you can press Escape to exit, t/Space to switch between", 0, 0);
+ prt("fluid more and step by step mode and any other key to step a frame in step by", 1, 0);
+ prt("step mode.", 2, 0);
+ prt("You can press D to do an html screenshot of the current frame.", 3, 0);
+ prt("You can also use + and - to speed up/down the playing speed.", 5, 0);
+ prt("Press any key when ready.", 8, 0);
+
+ inkey();
+
+ Term_clear();
+
+ line = -1;
+
+ delay = 1;
+
+ /* Init to white */
+ for (x = 0; x < 80; x++)
+ {
+ cbuf[x] = 'w';
+ }
+
+ /* Parse */
+ while (0 == my_fgets(fff, buf, 1024))
+ {
+ /* Do not wait */
+ inkey_scan = TRUE;
+ ch = inkey();
+
+ /* Stop */
+ if (ch == ESCAPE) break;
+
+ /* Change mode */
+ else if (ch == 't')
+ {
+ mode = FALSE;
+ }
+ else if (ch == ' ')
+ {
+ mode = TRUE;
+ }
+
+ /* Change speed */
+ else if (ch == '+')
+ {
+ delay--;
+ if (delay < 0) delay = 0;
+ }
+ else if (ch == '-')
+ {
+ delay++;
+ if (delay > 5) delay = 5;
+ }
+ else if (ch == 'D')
+ {
+ do_cmd_html_dump();
+ }
+
+ line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') break;
+
+ /* Clean screen */
+ if (buf[0] == 'C')
+ {
+ Term_clear();
+
+ /* Next */
+ continue;
+ }
+
+ /* Displays a textbox */
+ if (buf[0] == 'B')
+ {
+ int len = strlen(buf + 2);
+
+ /* Clear the line */
+ Term_erase(0, 0, 255);
+
+ /* Display the message */
+ c_put_str(TERM_VIOLET, "###", 0, 0);
+ c_put_str(TERM_ORANGE, buf + 2, 0, 3);
+ c_put_str(TERM_VIOLET, "###", 0, 3 + len);
+ c_put_str(TERM_WHITE, "(more)", 0, 6 + len);
+
+ /* Next */
+ continue;
+ }
+
+ /* Wait a key */
+ if (buf[0] == 'W')
+ {
+ inkey();
+
+ /* Next */
+ continue;
+ }
+
+ /* Sleep */
+ if (buf[0] == 'S')
+ {
+ long msec;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%ld:", &msec))
+ {
+ return ( -2);
+ }
+
+ if (!mode)
+ {
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+ else
+ {
+ bool_ stop = FALSE;
+
+ while (TRUE)
+ {
+ ch = inkey();
+
+ /* Stop */
+ if (ch == ESCAPE)
+ {
+ stop = TRUE;
+ break;
+ }
+ /* Change mode */
+ else if (ch == 't')
+ {
+ mode = FALSE;
+ break;
+ }
+ /* Change mode */
+ else if (ch == ' ')
+ {
+ if (mode) continue;
+ mode = TRUE;
+ break;
+ }
+ /* Change speed */
+ else if (ch == '+')
+ {
+ delay--;
+ if (delay < 0) delay = 0;
+ }
+ else if (ch == '-')
+ {
+ delay++;
+ if (delay > 5) delay = 5;
+ }
+ else if (ch == 'D')
+ {
+ do_cmd_html_dump();
+ }
+ else break;
+ }
+ if (stop) break;
+ }
+
+ /* Next */
+ continue;
+ }
+
+ /* Get color for the NEXT L line */
+ if (buf[0] == 'E')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return ( -2);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return ( -2);
+
+ /* Get the index */
+ y = atoi(buf + 2);
+
+ C_COPY(cbuf, s, 80, char);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Print a line */
+ if (buf[0] == 'L')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return ( -2);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return ( -2);
+
+ /* Get the index */
+ y = atoi(buf + 2);
+
+ for (x = 0; x < 80; x++)
+ {
+ Term_putch(x, y, color_char_to_attr(cbuf[x]), s[x]);
+
+ /* Reinit to white */
+ cbuf[x] = 'w';
+ }
+ Term_redraw_section(0, y, 79, y);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Update 1 char */
+ if (buf[0] == 'P')
+ {
+ int x, y, a, c;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &x, &y, &c, &a))
+ {
+ a = 'w';
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &x, &y, &c)) return ( -2);
+ }
+
+ Term_putch(x, y, color_char_to_attr(cbuf[x]), c);
+ Term_redraw_section(x, y, x + 1, y + 1);
+
+ /* Next... */
+ continue;
+ }
+ }
+
+ /* Load screen */
+ Term_load();
+ character_icky = FALSE;
+
+ /* Close */
+ my_fclose(fff);
+
+ return (0);
+}
+
+
+/*
+ * Start the recording of a cmovie
+ */
+void do_record_cmovie(cptr cmovie)
+{
+ char buf[1024];
+ int fd = -1;
+ int y;
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_CMOV, cmovie);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Check for existing file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Existing file */
+ if (fd >= 0)
+ {
+ char out_val[160];
+
+ /* Close the file */
+ (void)fd_close(fd);
+
+ /* Build query */
+ (void)sprintf(out_val, "Replace existing file %s? ", cmovie);
+
+ /* Ask */
+ if (get_check(out_val)) fd = -1;
+ }
+
+ /* Be sure */
+ if (!get_check("Ready to record(Press ctrl+D to enter a textual note while recording)?")) return;
+
+ /* Open the non-existing file */
+ if (fd < 0) movfile = my_fopen(buf, "w");
+
+ /* Invalid file */
+ if (movfile == NULL)
+ {
+ msg_format("Cmovie recording failed!");
+
+ return;
+ }
+
+ /* First thing: Record clear screen then enable the recording */
+ fprintf(movfile, "# Generated by %s\n",
+ get_version_string());
+ fprintf(movfile, "C:\n");
+ last_paused = 0;
+ do_movies = 1;
+ cmovie_init_second();
+
+ /* Mega Hack, get the screen */
+ for (y = 0; y < Term->hgt; y++)
+ {
+ cmovie_record_line(y);
+ }
+}
+
+
+/*
+ * Stop the recording
+ */
+void do_stop_cmovie()
+{
+ if (do_movies == 1)
+ {
+ do_movies = 0;
+ my_fclose(movfile);
+ }
+}
+
+
+/*
+ * Start a cmovie
+ */
+void do_start_cmovie()
+{
+ char name[90], rname[94];
+
+
+ /* Should never happen */
+ if (do_movies == 1) return;
+
+ /* Default */
+ sprintf(name, "%s", player_base);
+
+ if (get_string("Cmovie name: ", name, 80))
+ {
+ if (name[0] && (name[0] != ' '))
+ {
+ sprintf(rname, "%s.cmv", name);
+
+ if (get_check("Record(y), Play(n)?")) do_record_cmovie(rname);
+ else do_play_cmovie(rname);
+ }
+ }
+}
+
+
+void cmovie_clean_line(int y, char *abuf, char *cbuf)
+{
+ const byte *ap = Term->scr->a[y];
+ const char *cp = Term->scr->c[y];
+
+ byte a;
+ char c;
+
+ int x;
+ int wid, hgt;
+ int screen_wid, screen_hgt;
+
+
+ /* Retrieve current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Calculate the size of dungeon map area */
+ screen_wid = wid - (COL_MAP + 1);
+ screen_hgt = hgt - (ROW_MAP + 1);
+
+ /* For the time being, assume 80 column display XXX XXX XXX */
+ for (x = 0; x < wid; x++)
+ {
+ /* Convert dungeon map into default attr/chars */
+ if (!character_icky &&
+ ((x - COL_MAP) >= 0) &&
+ ((x - COL_MAP) < screen_wid) &&
+ ((y - ROW_MAP) >= 0) &&
+ ((y - ROW_MAP) < screen_hgt))
+ {
+ /* Retrieve default attr/char */
+ map_info_default(y + panel_row_prt, x + panel_col_prt, &a, &c);
+
+ abuf[x] = conv_color[a & 0xf];
+
+ if (c == '\0') cbuf[x] = ' ';
+ else cbuf[x] = c;
+ }
+
+ else
+ {
+ abuf[x] = conv_color[ap[x] & 0xf];
+ cbuf[x] = cp[x];
+ }
+ }
+
+ /* Null-terminate the prepared strings */
+ abuf[x] = '\0';
+ cbuf[x] = '\0';
+}
+
+
+/*
+ * Write a record of a screen row into a cmovie file
+ */
+void cmovie_record_line(int y)
+{
+ char abuf[256];
+ char cbuf[256];
+
+ cmovie_clean_line(y, abuf, cbuf);
+
+ /* Write a colour record */
+ fprintf(movfile, "E:%d:%.80s\n", y, abuf);
+
+ /* Write a char record */
+ fprintf(movfile, "L:%d:%.80s\n", y, cbuf);
+}
+
+
+/*
+ * Record a "text box"
+ */
+void do_cmovie_insert()
+{
+ char buf[81] = "";
+
+ /* Dont record */
+ do_movies = 2;
+
+ while (get_string("Textbox(ESC to end): ", buf, 80))
+ {
+ fprintf(movfile, "B:%s\nW:\n", buf);
+ buf[0] = '\0';
+ }
+
+ /* We reinit the time as to not count the time the user needed ot enter the text */
+ cmovie_init_second();
+
+ /* Continue recording */
+ do_movies = 1;
+}
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 00000000..c9f460e7
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,314 @@
+/* File: config.h */
+
+/* Purpose: Angband specific configuration stuff */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+/*
+ * Look through the following lines, and where a comment includes the
+ * tag "OPTION:", examine the associated "#define" statements, and decide
+ * whether you wish to keep, comment, or uncomment them. You should not
+ * have to modify any lines not indicated by "OPTION".
+ *
+ * Note: Also examine the "system" configuration file "h-config.h"
+ * and the variable initialization file "variable.c". If you change
+ * anything in "variable.c", you only need to recompile that file.
+ *
+ * And finally, remember that the "Makefile" will specify some rather
+ * important compile time options, like what visual module to use.
+ */
+
+
+/*
+ * OPTION: See the Makefile(s), where several options may be declared.
+ *
+ * Some popular options include "USE_GCU" (allow use with Unix "curses"),
+ * "USE_X11" (allow basic use with Unix X11) and "USE_XAW" (allow use with
+ * Unix X11 plus the Athena Widget set).
+ *
+ * The old "USE_NCU" option has been replaced with "USE_GCU".
+ *
+ * Several other such options are available for non-unix machines,
+ * such as "MACINTOSH", "WINDOWS".
+ *
+ * You may also need to specify the "system", using defines such as
+ * "SOLARIS" (for Solaris), etc, see "h-config.h" for more info.
+ */
+
+
+/*
+ * OPTION: define "SPECIAL_BSD" for using certain versions of UNIX
+ * that use the 4.4BSD Lite version of Curses in "main-gcu.c"
+ */
+/* #define SPECIAL_BSD */
+
+
+/*
+ * OPTION: Use the POSIX "termios" methods in "main-gcu.c"
+ */
+/* #define USE_TPOSIX */
+
+/*
+ * OPTION: Use the "termio" methods in "main-gcu.c"
+ */
+/* #define USE_TERMIO */
+
+/*
+ * OPTION: Use the icky BSD "tchars" methods in "main-gcu.c"
+ */
+/* #define USE_TCHARS */
+
+
+/*
+ * OPTION: Use "blocking getch() calls" in "main-gcu.c".
+ * Hack -- Note that this option will NOT work on many BSD machines
+ * Currently used whenever available, if you get a warning about
+ * "nodelay()" undefined, then make sure to undefine this.
+ */
+#if defined(SYS_V)
+# define USE_GETCH
+#endif
+
+
+/*
+ * OPTION: Use the "curs_set()" call in "main-gcu.c".
+ * Hack -- This option will not work on most BSD machines
+ */
+#ifdef SYS_V
+# define USE_CURS_SET
+#endif
+
+
+/*
+ * OPTION: Include "ncurses.h" instead of "curses.h" in "main-gcu.c"
+ */
+#define USE_NCURSES
+
+
+/*
+ * OPTION: for multi-user machines running the game setuid to some other
+ * user (like 'games') this SAFE_SETUID option allows the program to drop
+ * its privileges when saving files that allow for user specified pathnames.
+ * This lets the game be installed system wide without major security
+ * concerns. There should not be any side effects on any machines.
+ *
+ * This will handle "gids" correctly once the permissions are set right.
+ */
+#define SAFE_SETUID
+
+
+/*
+ * This flag enables the "POSIX" methods for "SAFE_SETUID".
+ */
+#if defined(_POSIX_SAVED_IDS) && !(defined(SUNOS) && !defined(SOLARIS)) && !defined(__APPLE__)
+# define SAFE_SETUID_POSIX
+#endif
+
+
+/*
+ * OPTION: Allow characteres to be "auto-rolled"
+ */
+#define ALLOW_AUTOROLLER
+
+
+/*
+ * OPTION: Allow monsters to "flee" when hit hard
+ */
+#define ALLOW_FEAR
+
+/*
+ * OPTION: Allow monsters to "flee" from strong players
+ */
+#define ALLOW_TERROR
+
+
+/*
+ * OPTION: Handle signals
+ */
+#define HANDLE_SIGNALS
+
+
+/*
+ * Allow "Wizards" to yield "high scores"
+ */
+/* #define SCORE_WIZARDS */
+
+/*
+ * Allow "Borgs" to yield "high scores"
+ */
+/*#define SCORE_BORGS*/
+
+/*
+ * Allow "Cheaters" to yield "high scores"
+ */
+/* #define SCORE_CHEATERS */
+
+
+
+/*
+ * OPTION: Maximum flow depth
+ */
+#define MONSTER_FLOW_DEPTH 32
+
+
+
+/*
+ * OPTION: Allow use of extended spell info -DRS-
+ */
+#define DRS_SHOW_SPELL_INFO
+
+/*
+ * OPTION: Allow use of the monster health bar -DRS-
+ */
+#define DRS_SHOW_HEALTH_BAR
+
+
+
+/*
+ * OPTION: Allow the use of "sound" in various places.
+ */
+#define USE_SOUND
+
+/*
+ * OPTION: Allow the use of "graphics" in various places
+ */
+#define USE_GRAPHICS
+
+
+/*
+ * OPTION: Hack -- Macintosh stuff
+ */
+#ifdef MACINTOSH
+
+/* Do not handle signals */
+# undef HANDLE_SIGNALS
+
+#endif
+
+
+/*
+ * OPTION: Hack -- Windows stuff
+ */
+#ifdef WINDOWS
+
+/* Do not handle signals */
+# undef HANDLE_SIGNALS
+
+#endif
+
+
+/*
+ * OPTION: Set the "default" path to the angband "lib" directory.
+ *
+ * See "main.c" for usage, and note that this value is only used on
+ * certain machines, primarily Unix machines. If this value is used,
+ * it will be over-ridden by the "ANGBAND_PATH" environment variable,
+ * if that variable is defined and accessable. The final slash is
+ * optional, but it may eventually be required.
+ *
+ * Using the value "./lib/" below tells Angband that, by default,
+ * the user will run "angband" from the same directory that contains
+ * the "lib" directory. This is a reasonable (but imperfect) default.
+ *
+ * If at all possible, you should change this value to refer to the
+ * actual location of the "lib" folder, for example, "/tmp/angband/lib/"
+ * or "/usr/games/lib/angband/", or "/pkg/angband/lib".
+ *
+ * Additional note -- if you are planning to use makefile.org, don't bother
+ * setting this variable, as it is overridden by a value set near the top of
+ * that file.
+ */
+#ifndef DEFAULT_PATH
+# define DEFAULT_PATH "/var/games/tome"
+#endif
+
+
+/*
+ * Where to put the user's files.
+ */
+#if defined(MACH_O_CARBON)
+#define PRIVATE_USER_PATH "~/Library/Application Support/ToME"
+#define PRIVATE_USER_PATH_DATA
+#define PRIVATE_USER_PATH_APEX
+#define PRIVATE_USER_PATH_MODULES
+#else
+#define PRIVATE_USER_PATH "~/.tome"
+#define PRIVATE_USER_PATH_APEX
+#endif
+
+/*
+ * OPTION: For some brain-dead computers with no command line interface,
+ * namely Macintosh, there has to be some way of "naming" your savefiles.
+ * The current "Macintosh" hack is to make it so whenever the character
+ * name changes, the savefile is renamed accordingly. But on normal
+ * machines, once you manage to "load" a savefile, it stays that way.
+ * Macintosh is particularly weird because you can load savefiles that
+ * are not contained in the "lib:save:" folder, and if you change the
+ * player's name, it will then save the savefile elsewhere.
+ */
+#if defined(MACINTOSH) || defined(WINDOWS)
+/* #define SAVEFILE_MUTABLE */
+#endif
+
+
+/*
+ * OPTION: Person to bother if something goes wrong.
+ */
+#define MAINTAINER "tome@packages.debian.org"
+
+
+/*
+ * OPTION: Default font (when using X11).
+ */
+#define DEFAULT_X11_FONT "9x15"
+
+/*
+ * OPTION: Default fonts (when using X11)
+ */
+#define DEFAULT_X11_FONT_SCREEN DEFAULT_X11_FONT
+#define DEFAULT_X11_FONT_MIRROR DEFAULT_X11_FONT
+#define DEFAULT_X11_FONT_RECALL DEFAULT_X11_FONT
+#define DEFAULT_X11_FONT_CHOICE DEFAULT_X11_FONT
+
+
+
+/* ToME options: */
+
+/* Should the player know his / her starting life rate? */
+#define SHOW_LIFE_RATE
+
+/* Allow hordes of 'similar' monsters */
+#define MONSTER_HORDES
+
+/* Wizard mode testing options: */
+
+/* For testing the vaults */
+/* # define FORCE_V_IDX 20 */
+
+/* Testing upkeep */
+/* # define TRACK_FRIENDS */
+
+/*
+ * Using the fast autoroller can be considered as cheating
+ */
+#define USE_FAST_AUTOROLLER
+
+/*
+ * Enable the CTRL + L command to quit without saving
+ * Only use for debugging purpose, otherwise you are a CHEATER
+ */
+/* #define ALLOW_QUITTING */
+
+/*
+ * Allow makefiles to override the default file mode
+ */
+#ifndef FILE_MODE
+#define FILE_MODE 0644
+#endif
diff --git a/src/defines.h b/src/defines.h
new file mode 100644
index 00000000..850bbc7a
--- /dev/null
+++ b/src/defines.h
@@ -0,0 +1,4695 @@
+/* File: defines.h */
+
+/* Purpose: global constants and macro definitions */
+
+
+/*
+ * Do not edit this file unless you know *exactly* what you are doing.
+ *
+ * Some of the values in this file were chosen to preserve game balance,
+ * while others are hard-coded based on the format of old save-files, the
+ * definition of arrays in various places, mathematical properties, fast
+ * computation, storage limits, or the format of external text files.
+ *
+ * Changing some of these values will induce crashes or memory errors or
+ * savefile mis-reads. Most of the comments in this file are meant as
+ * reminders, not complete descriptions, and even a complete knowledge
+ * of the source may not be sufficient to fully understand the effects
+ * of changing certain definitions.
+ *
+ * Lastly, note that the code does not always use the symbolic constants
+ * below, and sometimes uses various hard-coded values that may not even
+ * be defined in this file, but which may be related to definitions here.
+ * This is of course bad programming practice, but nobody is perfect...
+ *
+ * For example, there are MANY things that depend on the screen being
+ * 80x24, with the top line used for messages, the bottom line being
+ * used for status, and exactly 22 lines used to show the dungeon.
+ * Just because your screen can hold 46 lines does not mean that the
+ * game will work if you try to use 44 lines to show the dungeon.
+ *
+ * You have been warned.
+ */
+
+
+/*
+ * Release state, CVS or not, remember to switch it when making releases
+ */
+
+#ifndef IS_CVS
+#define IS_CVS " (ah)"
+/* #define IS_CVS " (ah, git)" */
+#endif
+
+#define USER_PATH_VERSION "/2.3"
+
+#define ANGBAND_2_8_1
+
+#define SAVEFILE_VERSION 104
+
+/*
+ * This value is not currently used
+ */
+#define VERSION_EXTRA 0
+
+/*
+ * Maximum amount of Angband windows.
+ */
+#define ANGBAND_TERM_MAX 8
+
+/*
+ * Number of grids in each block (vertically)
+ * Probably hard-coded to 11, see "generate.c"
+ */
+#define BLOCK_HGT 11
+
+/*
+ * Number of grids in each block (horizontally)
+ * Probably hard-coded to 11, see "generate.c"
+ */
+#define BLOCK_WID 11
+
+
+/*
+ * Number of grids in each panel (vertically)
+ * Must be a multiple of BLOCK_HGT
+ */
+#define PANEL_HGT 11
+
+/*
+ * Number of grids in each panel (horizontally)
+ * Must be a multiple of BLOCK_WID
+ */
+#define PANEL_WID 33
+
+
+/*
+ * Number of grids used to display the dungeon (vertically).
+ * Must be a multiple of 11, probably hard-coded to 22.
+ */
+#define SCREEN_HGT 22
+
+/*
+ * Number of grids used to display the dungeon (horizontally).
+ * Must be a multiple of 33, probably hard-coded to 66.
+ */
+#define SCREEN_WID 66
+
+
+/*
+ * Maximum dungeon height in grids, must be a multiple of SCREEN_HGT,
+ * probably hard-coded to SCREEN_HGT * 3.
+ */
+#define MAX_HGT 66
+
+/*
+ * Maximum dungeon width in grids, must be a multiple of SCREEN_WID,
+ * probably hard-coded to SCREEN_WID * 3.
+ */
+#define MAX_WID 198
+
+/* Used only in object3.c / trap effects */
+#if ((MAX_HGT / SCREEN_HGT) < (MAX_WID / SCREEN_WID))
+ #define RATIO (MAX_WID / SCREEN_WID)
+#else
+ #define RATIO (MAX_HGT / SCREEN_HGT)
+#endif
+
+/*
+ * Default radius of detection spells
+ * Its area must be at least as large as SCREEN_WID * SCREEN_HGT
+ */
+#define DEFAULT_RADIUS 25
+
+
+#define CHANCE_TRAP_JAMMED_DOOR 2500
+#define CHANCE_TRAP_SECRET_DOOR 1500
+#define CHANCE_TRAP_LOCKED_DOOR 1000
+#define CHANCE_TRAP_DOOR 500 /* in 10000 */
+#define CHANCE_TRAP_FLOOR 4 /* in 10000 chance of placing a trap */
+
+#define MAX_BOUNTIES 24
+
+#define MAX_SPELLS 100
+#define MAX_RUNES 100
+
+/*
+ * Arena constants
+ */
+#define MAX_ARENA_MONS 29 /* -KMW- */
+
+/*
+ * Total number of stores (see "store.c", etc)
+ */
+#define STORE_GENERAL 0
+#define STORE_ARMOURY 1
+#define STORE_WEAPON 2
+#define STORE_TEMPLE 3
+#define STORE_ALCHEMIST 4
+#define STORE_MAGIC 5
+#define STORE_BLACK 6
+#define STORE_HOME 7
+#define STORE_BOOK 8
+#define STORE_PET 9
+
+/*
+ * Maximum number of player "sex" types (see "table.c", etc)
+ */
+#define MAX_SEXES 3
+
+/* The number of "patrons" available (for Chaos Warriors) */
+#define MAX_PATRON 16
+
+/* Number of Random Artifacts */
+#define MAX_RANDARTS 84
+#define MAX_T_ACT 51
+
+/* Chaos Warrior: Reward types: */
+#define REW_POLY_SLF 0
+#define REW_GAIN_EXP 1
+#define REW_LOSE_EXP 2
+#define REW_GOOD_OBJ 3
+#define REW_GREA_OBJ 4
+#define REW_CHAOS_WP 5
+#define REW_GOOD_OBS 6
+#define REW_GREA_OBS 7
+#define REW_TY_CURSE 8
+#define REW_SUMMON_M 9
+#define REW_H_SUMMON 10
+#define REW_DO_HAVOC 11
+#define REW_GAIN_ABL 12
+#define REW_LOSE_ABL 13
+#define REW_RUIN_ABL 14
+#define REW_AUGM_ABL 15
+#define REW_POLY_WND 16
+#define REW_HEAL_FUL 17
+#define REW_HURT_LOT 18
+#define REW_CURSE_WP 19
+#define REW_CURSE_AR 20
+#define REW_PISS_OFF 21
+#define REW_WRATH 22
+#define REW_DESTRUCT 23
+#define REW_GENOCIDE 24
+#define REW_MASS_GEN 25
+#define REW_DISPEL_C 26
+#define REW_UNUSED_1 27
+#define REW_UNUSED_2 28
+#define REW_UNUSED_3 29
+#define REW_UNUSED_4 30
+#define REW_UNUSED_5 31
+#define REW_IGNORE 32
+#define REW_SER_UNDE 33
+#define REW_SER_DEMO 34
+#define REW_SER_MONS 35
+
+
+/* bear barehanded attacks ... */
+#define MAX_BEAR 8
+
+/* Monk martial arts... */
+#define MAX_MA 17
+
+#define MA_KNEE 0x0001
+#define MA_SLOW 0x0002
+#define MA_WOUND 0x0004
+#define MA_STUN 0x0008
+#define MA_FULL_SLOW 0x0010
+
+/* Mindcraft */
+#define MAX_MINDCRAFT_POWERS 12
+
+/* Necromancy */
+#define MAX_NECRO_POWERS 6
+
+/* Mimicry */
+#define MAX_MIMIC_POWERS 5
+
+/* Symbiosis */
+#define MAX_SYMBIOTIC_POWERS 9
+
+
+/* A hack for cave.c */
+#define BMP_FIRST_PC_CLASS 164
+#define BMP_FIRST_PC_RACE 128
+
+
+/*
+ * Size of memory reserved for initialization of some arrays
+ */
+#define FAKE_NAME_SIZE 40 * 1024L
+#define FAKE_TEXT_SIZE 120 * 1024L
+
+
+/*
+ * Maximum number of high scores in the high score file
+ */
+#define MAX_HISCORES 100
+
+
+/*
+ * Maximum dungeon level. The player can never reach this level
+ * in the dungeon, and this value is used for various calculations
+ * involving object and monster creation. It must be at least 100.
+ * Setting it below 128 may prevent the creation of some objects.
+ */
+#define MAX_DEPTH 128
+#define MAX_DEPTH_MONSTER 200
+
+
+/*
+ * Maximum size of the "lite" array (see "cave.c")
+ * Note that the "lite radius" will NEVER exceed 5, and even if the "lite"
+ * was rectangular, we would never require more than 128 entries in the array.
+ */
+#define LITE_MAX 1536
+
+/*
+ * Maximum size of the "view" array (see "cave.c")
+ * Note that the "view radius" will NEVER exceed 20, and even if the "view"
+ * was octagonal, we would never require more than 1520 entries in the array.
+ */
+#define VIEW_MAX 1536
+
+/*
+ * Maximum size of the "temp" array (see "cave.c")
+ * We must be as large as "VIEW_MAX" and "LITE_MAX" for proper functioning
+ * of "update_view()" and "update_lite()". We must also be as large as the
+ * largest illuminatable room, but no room is larger than 800 grids. We
+ * must also be large enough to allow "good enough" use as a circular queue,
+ * to calculate monster flow, but note that the flow code is "paranoid".
+ */
+#define TEMP_MAX 16384
+
+
+/*
+ * Number of keymap modes
+ */
+#define KEYMAP_MODES 2
+
+/*
+ * Mode for original keyset commands
+ */
+#define KEYMAP_MODE_ORIG 0
+
+/*
+ * Mode for roguelike keyset commands
+ */
+#define KEYMAP_MODE_ROGUE 1
+
+
+/*
+ * OPTION: Maximum number of macros (see "io.c")
+ * Default: assume at most 256 macros are used
+ */
+#define MACRO_MAX 256
+
+/*
+ * OPTION: Maximum number of "quarks" (see "io.c")
+ * Default: assume at most 512 different inscriptions are used
+ */
+#define QUARK_MAX 768
+ /* Was 512... 256 quarks added for random artifacts */
+
+/*
+ * OPTION: Maximum number of messages to remember (see "io.c")
+ * Default: assume maximal memorization of 2048 total messages
+ */
+#define MESSAGE_MAX 2048
+
+#define MESSAGE_NONE 0
+#define MESSAGE_MSG 1
+
+/*
+ * OPTION: Maximum space for the message text buffer (see "io.c")
+ * Default: assume that each of the 2048 messages is repeated an
+ * average of three times, and has an average length of 48
+ */
+#define MESSAGE_BUF 32768
+
+
+/*
+ * Maximum value storable in a "byte" (hard-coded)
+ */
+#define MAX_UCHAR 255
+
+/*
+ * Maximum value storable in a "s16b" (hard-coded)
+ */
+#define MAX_SHORT 32767
+
+
+/*
+ * Store constants
+ */
+#define STORE_INVEN_MAX 255 /* Max number of discrete objs in inven */
+#define STORE_CHOICES 56 /* Number of items to choose stock from */
+#define STORE_OBJ_LEVEL 5 /* Magic Level for normal stores */
+#define STORE_TURNOVER 9 /* Normal shop turnover, per day */
+#define STORE_MIN_KEEP 6 /* Min slots to "always" keep full */
+#define STORE_MAX_KEEP 18 /* Max slots to "always" keep full */
+#define STORE_SHUFFLE 21 /* 1/Chance (per day) of an owner changing */
+#define STORE_TURNS 1000 /* Number of turns between turnovers */
+
+/*
+ * Misc constants
+ */
+#define DAY 11520 /* Number of turns per day */
+#define YEAR (DAY * 365) /* Number of turns per year */
+#define HOUR (DAY / 24) /* Number of turns per hour */
+#define MINUTE (HOUR / 60) /* Number of turns per minute */
+#define DAY_START (HOUR * 6) /* Sunrise */
+#define START_YEAR 2890 /* Bilbo birthday year */
+#define START_DAY (DAY * (42 + 127)) /* Bilbo birthday */
+
+#define BREAK_GLYPH 550 /* Rune of protection resistance */
+#define BREAK_MINOR_GLYPH 99 /* For explosive runes */
+#define BTH_PLUS_ADJ 3 /* Adjust BTH per plus-to-hit */
+#define MON_MULT_ADJ 10 /* High value slows multiplication */
+#define MON_SUMMON_ADJ 2 /* Adjust level of summoned creatures */
+#define MON_DRAIN_LIFE 2 /* Percent of player exp drained per hit */
+#define USE_DEVICE 3 /* x> Harder devices x< Easier devices */
+
+#define BIAS_ELEC 1 /* "Biases" for random artifact gen */
+#define BIAS_POIS 2
+#define BIAS_FIRE 3
+#define BIAS_COLD 4
+#define BIAS_ACID 5
+#define BIAS_STR 6
+#define BIAS_INT 7
+#define BIAS_WIS 8
+#define BIAS_DEX 9
+#define BIAS_CON 10
+#define BIAS_CHR 11
+#define BIAS_CHAOS 12
+#define BIAS_PRIESTLY 13
+#define BIAS_NECROMANTIC 14
+#define BIAS_LAW 15
+#define BIAS_ROGUE 16
+#define BIAS_MAGE 17
+#define BIAS_WARRIOR 18
+#define BIAS_RANGER 19
+
+/*
+ * Location of objects when they were found
+ */
+#define OBJ_FOUND_MONSTER 1
+#define OBJ_FOUND_FLOOR 2
+#define OBJ_FOUND_VAULT 3
+#define OBJ_FOUND_SPECIAL 4
+#define OBJ_FOUND_RUBBLE 5
+#define OBJ_FOUND_REWARD 6
+#define OBJ_FOUND_STORE 7
+#define OBJ_FOUND_STOLEN 8
+#define OBJ_FOUND_SELFMADE 9
+/*
+ * There is a 1/20 (5%) chance of inflating the requested object_level
+ * during the creation of an object (see "get_obj_num()" in "object.c").
+ * Lower values yield better objects more often.
+ */
+#define GREAT_OBJ 20
+
+#define GREAT_EGO 20
+
+/*
+ * There is a 1/50 (2%) chance of inflating the requested monster_level
+ * during the creation of a monsters (see "get_mon_num()" in "monster.c").
+ * Lower values yield harder monsters more often.
+ */
+#define NASTY_MON 50 /* 1/chance of inflated monster level */
+
+
+
+/*
+ * Refueling constants
+ */
+#define FUEL_TORCH 5000 /* Maximum amount of fuel in a torch */
+#define FUEL_LAMP 15000 /* Maximum amount of fuel in a lantern */
+
+
+/*
+ * More maximum values
+ */
+#define MAX_SIGHT 20 /* Maximum view distance */
+#define MAX_RANGE 18 /* Maximum range (spells, etc) */
+
+
+
+/*
+ * The town starts out with 4 residents during the day
+ */
+#define MIN_M_ALLOC_TD 4
+
+/*
+ * The town starts out with 8 residents during the night
+ */
+#define MIN_M_ALLOC_TN 8
+
+
+/*
+ * A monster can only "multiply" (reproduce) if there are fewer than 100
+ * monsters on the level capable of such spontaneous reproduction. This
+ * is a hack which prevents the "m_list[]" array from exploding due to
+ * reproducing monsters. Messy, but necessary.
+ */
+#define MAX_REPRO 100
+
+
+/*
+ * Player constants
+ */
+#define SUBRACE_SAVE 9 /* Ugly hack, should be in foo-info, the subrace saved to the savefile */
+#define PY_MAX_EXP 99999999L /* Maximum exp */
+#define PY_MAX_GOLD 999999999L /* Maximum gold */
+#define PY_MAX_LEVEL 50 /* Maximum level */
+
+/*
+ * Player "food" crucial values
+ */
+#define PY_FOOD_MAX 15000 /* Food value (Bloated) */
+#define PY_FOOD_FULL 10000 /* Food value (Normal) */
+#define PY_FOOD_ALERT 2000 /* Food value (Hungry) */
+#define PY_FOOD_WEAK 1000 /* Food value (Weak) */
+#define PY_FOOD_FAINT 500 /* Food value (Fainting) */
+#define PY_FOOD_STARVE 100 /* Food value (Starving) */
+
+/*
+ * Player regeneration constants
+ */
+#define PY_REGEN_NORMAL 197 /* Regen factor*2^16 when full */
+#define PY_REGEN_WEAK 98 /* Regen factor*2^16 when weak */
+#define PY_REGEN_FAINT 33 /* Regen factor*2^16 when fainting */
+#define PY_REGEN_HPBASE 1442 /* Min amount hp regen*2^16 */
+#define PY_REGEN_MNBASE 524 /* Min amount mana regen*2^16 */
+
+/*
+ * Maximum number of "normal" pack slots, and the index of the "overflow"
+ * slot, which can hold an item, but only temporarily, since it causes the
+ * pack to "overflow", dropping the "last" item onto the ground. Since this
+ * value is used as an actual slot, it must be less than "INVEN_WIELD" (below).
+ * Note that "INVEN_PACK" is probably hard-coded by its use in savefiles, and
+ * by the fact that the screen can only show 23 items plus a one-line prompt.
+ */
+#define INVEN_PACK 23
+
+/*
+ * Body parts
+ */
+#define BODY_WEAPON 0
+#define BODY_TORSO 1
+#define BODY_ARMS 2
+#define BODY_FINGER 3
+#define BODY_HEAD 4
+#define BODY_LEGS 5
+#define BODY_MAX 6
+
+/*
+ * Indexes used for various "equipment" slots (hard-coded by savefiles, etc).
+ */
+#define INVEN_WIELD 24 /* 3 weapons -- WEAPONS */
+#define INVEN_BOW 27 /* 1 bow -- WEAPON */
+#define INVEN_RING 28 /* 6 rings -- FINGER */
+#define INVEN_NECK 34 /* 2 amulets -- HEAD */
+#define INVEN_LITE 36 /* 1 lite -- TORSO */
+#define INVEN_BODY 37 /* 1 body -- TORSO */
+#define INVEN_OUTER 38 /* 1 cloak -- TORSO */
+#define INVEN_ARM 39 /* 3 arms -- ARMS */
+#define INVEN_HEAD 42 /* 2 heads -- HEAD */
+#define INVEN_HANDS 44 /* 3 hands -- ARMS */
+#define INVEN_FEET 47 /* 2 feets -- LEGS */
+#define INVEN_CARRY 49 /* 1 carried monster -- TORSO */
+#define INVEN_AMMO 50 /* 1 quiver -- TORSO */
+#define INVEN_TOOL 51 /* 1 tool -- ARMS */
+
+/*
+ * Total number of inventory slots (hard-coded).
+ */
+#define INVEN_TOTAL 52
+#define INVEN_EQ (INVEN_TOTAL - INVEN_WIELD)
+
+/*
+ * A "stack" of items is limited to less than 100 items (hard-coded).
+ */
+#define MAX_STACK_SIZE 100
+
+
+
+/*
+ * Indexes of the various "stats" (hard-coded by savefiles, etc).
+ */
+#define A_STR 0
+#define A_INT 1
+#define A_WIS 2
+#define A_DEX 3
+#define A_CON 4
+#define A_CHR 5
+
+/*
+ * Player sex constants (hard-coded by save-files, arrays, etc)
+ */
+#define SEX_FEMALE 0
+#define SEX_MALE 1
+#define SEX_NEUTER 2
+
+
+/* Race flags */
+#define PR1_EXPERIMENTAL 0x00000001L /* Is still under developemnt */
+/* XXX */
+#define PR1_RESIST_BLACK_BREATH 0x00000004L /* Resist black breath */
+#define PR1_NO_STUN 0x00000008L /* Never stunned */
+#define PR1_XTRA_MIGHT_BOW 0x00000010L /* Xtra might with bows */
+#define PR1_XTRA_MIGHT_XBOW 0x00000020L /* Xtra might with xbows */
+#define PR1_XTRA_MIGHT_SLING 0x00000040L /* Xtra might with slings */
+#define PR1_AC_LEVEL 0x00000080L /* More AC with levels */
+#define PR1_HURT_LITE 0x00000100L /* Hurt by light */
+#define PR1_VAMPIRE 0x00000200L /* Vampire */
+#define PR1_UNDEAD 0x00000400L /* Undead */
+#define PR1_NO_CUT 0x00000800L /* no cuts */
+#define PR1_CORRUPT 0x00001000L /* hack-- corrupted */
+#define PR1_NO_FOOD 0x00002000L /* little gain from food */
+#define PR1_NO_GOD 0x00004000L /* cannot worship */
+/* XXX */
+#define PR1_ELF 0x00010000L /* Is an elf */
+#define PR1_SEMI_WRAITH 0x00020000L /* Takes damage when going in walls */
+#define PR1_NO_SUBRACE_CHANGE 0x00040000L /* Impossible to change subrace */
+/* XXX */
+#define PR1_ANTIMAGIC 0x00100000L /* antimagic ... hack */
+#define PR1_MOLD_FRIEND 0x00200000L /* Not attacked by molds wielded */
+#define PR1_GOD_FRIEND 0x00400000L /* Better grace */
+/* XXX */
+#define PR1_INNATE_SPELLS 0x01000000L /* KNown all spells, only need books */
+/* XXX */
+/* XXX */
+#define PR1_EASE_STEAL 0x08000000L /* Gain xp by stealing */
+/* XXX */
+/* XXX */
+/* XXX */
+/* XXX */
+
+/* XXX */
+#define PR2_ASTRAL 0x00000002L /* Is it an astral being coming from th halls of mandos ? */
+/* XXX */
+
+#define PRACE_FLAG2(f) ((rp_ptr->flags2 | rmp_ptr->flags2 | cp_ptr->flags2 | spp_ptr->flags2) & (f))
+#define PRACE_FLAG(f) ((rp_ptr->flags1 | rmp_ptr->flags1 | cp_ptr->flags1 | spp_ptr->flags1) & (f))
+#define PRACE_FLAGS(f) PRACE_FLAG(f)
+
+/* XXX */
+#define MKEY_MINDCRAFT 2
+#define MKEY_ANTIMAGIC 3
+#define MKEY_BLADE 4
+#define MKEY_ALCHEMY 5
+#define MKEY_MIMIC 6
+#define MKEY_NECRO 7
+#define MKEY_POWER_MAGE 8
+#define MKEY_RUNE 9
+#define MKEY_FORGING 10
+#define MKEY_INCARNATION 11
+#define MKEY_TELEKINESIS 12
+#define MKEY_SUMMON 13
+#define MKEY_TRAP 14
+#define MKEY_STEAL 15
+#define MKEY_DODGE 16
+#define MKEY_SCHOOL 17
+#define MKEY_LEARN 18
+#define MKEY_COPY 19
+#define MKEY_SYMBIOTIC 20
+#define MKEY_BOULDER 21
+#define MKEY_COMPANION 22
+#define MKEY_PIERCING 23
+
+
+/*** Screen Locations ***/
+
+/*
+ * Some screen locations for various display routines
+ * Currently, row 8 and 15 are the only "blank" rows.
+ * That leaves a "border" around the "stat" values.
+ */
+
+#define ROW_MAP 1
+#define COL_MAP 13 /* Dungeon map */
+
+#define ROW_RACE 1
+#define COL_RACE 0 /* <race name> */
+
+#define ROW_CLASS 2
+#define COL_CLASS 0 /* <class name> */
+
+#define ROW_TITLE 3
+#define COL_TITLE 0 /* <title> or <mode> */
+
+#define ROW_LEVEL 4
+#define COL_LEVEL 0 /* "LEVEL xxxxxx" */
+
+#define ROW_EXP 5
+#define COL_EXP 0 /* "EXP xxxxxxxx" */
+
+#define ROW_GOLD 6
+#define COL_GOLD 0 /* "AU xxxxxxxxx" */
+
+#define ROW_STAT 8
+#define COL_STAT 0 /* "xxx xxxxxx" */
+
+#define ROW_AC 14
+#define COL_AC 0 /* "Cur AC xxxxx" */
+
+#define ROW_HP 15
+#define COL_HP 0 /* "HP xxxxx/xxxxx" */
+
+#define ROW_SANITY 16 /* "Sanity 100%" */
+#define COL_SANITY 0
+
+#define ROW_SP 17
+#define COL_SP 0 /* "SP xxxxx/xxxxx" */
+
+#define ROW_PIETY 18
+#define COL_PIETY 0 /* "Pt xxxxx/xxxxx" */
+
+#define ROW_MH 19
+#define COL_MH 0 /* "MH xxxxx/xxxxx" */
+
+#define ROW_INFO (Term->hgt - 4)
+#define COL_INFO 0 /* "xxxxxxxxxxxx" */
+
+#define ROW_CUT (Term->hgt - 3)
+#define COL_CUT 0 /* <cut> */
+
+#define ROW_STUN (Term->hgt - 2)
+#define COL_STUN 0 /* <stun> */
+
+#define ROW_HUNGRY (Term->hgt - 1)
+#define COL_HUNGRY 0 /* "Weak" / "Hungry" / "Full" / "Gorged" */
+
+#define ROW_BLIND (Term->hgt - 1)
+#define COL_BLIND 7 /* "Blind" */
+
+#define ROW_CONFUSED (Term->hgt - 1)
+#define COL_CONFUSED 13 /* "Conf" */
+
+#define ROW_AFRAID (Term->hgt - 1)
+#define COL_AFRAID 18 /* "Afraid" */
+
+#define ROW_POISONED (Term->hgt - 1)
+#define COL_POISONED 25 /* "Poison" */
+
+#define ROW_DTRAP (Term->hgt - 1)
+#define COL_DTRAP 32 /* "DTrap" */
+
+#define ROW_STATE (Term->hgt - 1)
+#define COL_STATE 38 /* <state> */
+
+#define ROW_SPEED (Term->hgt - 1)
+#define COL_SPEED 49 /* "Slow (-NN)" or "Fast (+NN)" */
+
+#define ROW_STUDY (Term->hgt - 1)
+#define COL_STUDY 60 /* "Study" */
+
+#define ROW_DEPTH (Term->hgt - 1)
+#define COL_DEPTH (Term->wid - 14) /* "Lev NNN" / "NNNN ft" */
+
+
+
+/*** Terrain Feature Indexes (see "lib/edit/f_info.txt") ***/
+
+/* Nothing */
+#define FEAT_NONE 0x00
+
+/* Basic features */
+#define FEAT_FLOOR 0x01
+#define FEAT_FOUNTAIN 0x02
+#define FEAT_GLYPH 0x03
+#define FEAT_OPEN 0x04
+#define FEAT_BROKEN 0x05
+#define FEAT_LESS 0x06
+#define FEAT_MORE 0x07
+
+/* Quest features -KMW- */
+#define FEAT_QUEST_ENTER 0x08
+#define FEAT_QUEST_EXIT 0x09
+#define FEAT_QUEST_DOWN 0x0A
+#define FEAT_QUEST_UP 0x0B
+
+/* Shafts -GSN- */
+#define FEAT_SHAFT_DOWN 0x0D
+#define FEAT_SHAFT_UP 0x0E
+
+/* Basic feature */
+#define FEAT_EMPTY_FOUNTAIN 0x0F
+
+/* Feature 0x10 -- web */
+
+/* Traps */
+#define FEAT_TRAP 0x11
+
+/* Features 0x12 - 0x1F -- unused */
+
+/* Doors */
+#define FEAT_DOOR_HEAD 0x20
+#define FEAT_DOOR_TAIL 0x2F
+
+/* Extra */
+#define FEAT_SECRET 0x30
+#define FEAT_RUBBLE 0x31
+
+/* Seams */
+#define FEAT_MAGMA 0x32
+#define FEAT_QUARTZ 0x33
+#define FEAT_MAGMA_H 0x34
+#define FEAT_QUARTZ_H 0x35
+#define FEAT_MAGMA_K 0x36
+#define FEAT_QUARTZ_K 0x37
+
+/* Walls */
+#define FEAT_WALL_EXTRA 0x38
+#define FEAT_WALL_INNER 0x39
+#define FEAT_WALL_OUTER 0x3A
+#define FEAT_WALL_SOLID 0x3B
+#define FEAT_PERM_EXTRA 0x3C
+#define FEAT_PERM_INNER 0x3D
+#define FEAT_PERM_OUTER 0x3E
+#define FEAT_PERM_SOLID 0x3F
+
+/* Explosive rune */
+#define FEAT_MINOR_GLYPH 0x40
+
+/* Pattern */
+#define FEAT_PATTERN_START 0x41
+#define FEAT_PATTERN_1 0x42
+#define FEAT_PATTERN_2 0x43
+#define FEAT_PATTERN_3 0x44
+#define FEAT_PATTERN_4 0x45
+#define FEAT_PATTERN_END 0x46
+#define FEAT_PATTERN_OLD 0x47
+#define FEAT_PATTERN_XTRA1 0x48
+#define FEAT_PATTERN_XTRA2 0x49
+
+/* Shops */
+#define FEAT_SHOP 0x4A
+
+/* Permanent walls for quests */
+#define FEAT_QUEST1 0x4B
+#define FEAT_QUEST2 0x4C
+#define FEAT_QUEST3 0x4D
+#define FEAT_QUEST4 0x4E
+
+/* Features 0x4F - 0x53 -- unused */
+
+/* Additional terrains */
+#define FEAT_SHAL_WATER 0x54
+#define FEAT_DEEP_LAVA 0x55
+#define FEAT_SHAL_LAVA 0x56
+#define FEAT_DARK_PIT 0x57
+#define FEAT_DIRT 0x58
+#define FEAT_GRASS 0x59
+#define FEAT_ICE 0x5A
+#define FEAT_SAND 0x5B
+#define FEAT_DEAD_TREE 0x5C
+#define FEAT_DEAD_SMALL_TREE 212
+#define FEAT_ASH 0x5D
+#define FEAT_MUD 0x5E
+#define FEAT_ICE_WALL 0x5F
+#define FEAT_TREES 0x60
+#define FEAT_MOUNTAIN 0x61
+#define FEAT_SANDWALL 0x62
+#define FEAT_SANDWALL_H 0x63
+#define FEAT_SANDWALL_K 0x64
+/* Feature 0x65 -- high mountain chain */
+/* Feature 0x66 -- nether mist */
+
+/* Features 0x67 - 0x9F -- unused */
+
+#define FEAT_BETWEEN 0xA0 /* 160 */
+
+/* Altars */
+#define FEAT_ALTAR_HEAD 0xA1 /* 161 */
+#define FEAT_ALTAR_TAIL 0xAB /* 171 */
+
+#define FEAT_MARKER 0xAC /* 172 */
+/* Feature 0xAD -- Underground Tunnel */
+#define FEAT_TAINTED_WATER 0xAE /* 174 */
+#define FEAT_MON_TRAP 0xAF /* 175 */
+#define FEAT_BETWEEN2 0xB0 /* 176 */
+#define FEAT_LAVA_WALL 0xB1 /* 177 */
+#define FEAT_GREAT_FIRE 0xB2 /* 178 */
+#define FEAT_WAY_MORE 0xB3 /* 179 */
+#define FEAT_WAY_LESS 0xB4 /* 180 */
+/* Feature 0xB5 -- field */
+#define FEAT_EKKAIA 0xB6 /* 182 */
+
+/* Features 0xB7 - 0xBA -- unused */
+
+#define FEAT_DEEP_WATER 0xBB /* 187 */
+#define FEAT_GLASS_WALL 0xBC /* 188 */
+#define FEAT_ILLUS_WALL 0xBD /* 189 */
+/* Feature 0xBE -- grass roof */
+/* Feature 0xBF -- grass roof top */
+/* Feature 0xC0 -- grass roof chimney */
+/* Feature 0xC1 -- brick roof */
+/* Feature 0xC2 -- brick roof top */
+/* Feature 0xC3 -- brick roof chimney */
+/* Feature 0xC4 -- window */
+/* Feature 0xC5 -- small window */
+/* Feature 0xC6 -- rain barrel */
+#define FEAT_FLOWER 0xC7 /* 199 */
+/* Feature 0xC8 -- cobblestone road */
+/* Feature 0xC9 -- cobblestone with outlet */
+#define FEAT_SMALL_TREES 0xCA /* 202 */
+#define FEAT_TOWN 0xCB /* 203 */
+/* Feature 0xCC -- Underground Tunnel */
+#define FEAT_FIRE 0xCD /* 205 */
+/* Feature 0xCE -- pile of rubble (permanent) */
+
+/* Features 0xCF - 0xFF -- unused */
+
+
+#define MAX_BETWEEN_EXITS 2
+
+/*
+ * Number of effects
+ */
+#define MAX_EFFECTS 128
+#define EFF_WAVE 0x00000001 /* A circle whose radius increase */
+#define EFF_LAST 0x00000002 /* The wave lasts */
+#define EFF_STORM 0x00000004 /* The area follows the player */
+#define EFF_DIR1 0x00000008 /* Directed effect */
+#define EFF_DIR2 0x00000010 /* Directed effect */
+#define EFF_DIR3 0x00000020 /* Directed effect */
+#define EFF_DIR4 0x00000040 /* Directed effect */
+#define EFF_DIR6 0x00000080 /* Directed effect */
+#define EFF_DIR7 0x00000100 /* Directed effect */
+#define EFF_DIR8 0x00000200 /* Directed effect */
+#define EFF_DIR9 0x00000400 /* Directed effect */
+
+/*
+ * Wilderness terrains
+ */
+#define TERRAIN_EDGE 0 /* Edge of the World */
+#define TERRAIN_TOWN 1 /* Town */
+#define TERRAIN_DEEP_WATER 2 /* Deep water */
+#define TERRAIN_SHALLOW_WATER 3 /* Shallow water */
+#define TERRAIN_SWAMP 4 /* Swamp */
+#define TERRAIN_DIRT 5 /* Dirt */
+#define TERRAIN_GRASS 6 /* Grass */
+#define TERRAIN_TREES 7 /* Trees */
+#define TERRAIN_DESERT 8 /* Desert */
+#define TERRAIN_SHALLOW_LAVA 9 /* Shallow lava */
+#define TERRAIN_DEEP_LAVA 10 /* Deep lava */
+#define TERRAIN_MOUNTAIN 11 /* Mountain */
+
+#define MAX_WILD_TERRAIN 18
+
+/*** Artifact indexes (see "lib/edit/a_info.txt") ***/
+
+
+/* Lites */
+#define ART_GALADRIEL 1
+#define ART_ELENDIL 2
+#define ART_THRAIN 3
+#define ART_PALANTIR 202
+#define ART_UNDEATH 200
+#define ART_STONE_LORE 15
+#define ART_PALANTIR_ITHIL 208
+
+/* Amulets */
+#define ART_CARLAMMAS 4
+#define ART_INGWE 5
+#define ART_DWARVES 6
+#define ART_ANCHOR 14
+#define ART_ELESSAR 206
+#define ART_EVENSTAR 207
+
+/* Rings */
+#define ART_FLAR 7
+#define ART_BARAHIR 8
+#define ART_TULKAS 9
+#define ART_NARYA 10
+#define ART_NENYA 11
+#define ART_VILYA 12
+#define ART_POWER 13
+/* 14 used by the anchor of space-time */
+/* 15 used by the stone of lore */
+
+/* Dragon Scale */
+#define ART_RAZORBACK 16
+#define ART_BLADETURNER 17
+#define ART_MEDIATOR 166
+
+/* Hard Armour */
+#define ART_HIMRING 167
+#define ART_SOULKEEPER 19
+#define ART_ISILDUR 20
+#define ART_ROHIRRIM 21
+#define ART_BELEGENNON 22
+#define ART_CELEBORN 23
+#define ART_ARVEDUI 24
+#define ART_CASPANION 25
+
+/* Thunderlord flying suit */
+#define ART_MARDA 26
+#define ART_TRON 27
+
+/* Soft Armour */
+#define ART_THALKETTOTH 28
+
+/* Shields */
+#define ART_THORIN 30
+#define ART_CELEGORM 31
+#define ART_ANARION 32
+#define ART_GILGALAD 169
+#define ART_HARADRIM 176
+
+/* Helms and Crowns */
+#define ART_MORGOTH 34
+#define ART_BERUTHIEL 35
+#define ART_THRANDUIL 36
+#define ART_THENGEL 37
+#define ART_HAMMERHAND 38
+#define ART_DOR 39
+#define ART_HOLHENNETH 40
+#define ART_GORLIM 41
+#define ART_GONDOR 42
+#define ART_KNOWLEDGE 160
+#define ART_NUMENOR 43
+#define ART_CELEBRIMBOR 170
+
+/* Cloaks */
+#define ART_COLLUIN 44
+#define ART_HOLCOLLETH 45
+#define ART_THINGOL 46
+#define ART_THORONGIL 47
+#define ART_COLANNON 48
+#define ART_LUTHIEN 49
+#define ART_TUOR 50
+
+/* Gloves */
+#define ART_CAMBELEG 52
+#define ART_CAMMITHRIM 53
+#define ART_PAURHACH 54
+#define ART_PAURNIMMEN 55
+#define ART_PAURAEGEN 56
+#define ART_PAURNEN 57
+#define ART_CAMLOST 58
+#define ART_FINGOLFIN 59
+#define ART_EOL 178
+
+/* Boots */
+#define ART_FEANOR 60
+#define ART_DAL 61
+#define ART_THROR 62
+
+/* Swords */
+#define ART_NARSIL 164
+#define ART_MAEDHROS 64
+#define ART_ANGRIST 65
+#define ART_NARTHANC 66
+#define ART_NIMTHANC 67
+#define ART_DETHANC 68
+#define ART_RILIA 69
+#define ART_BELANGIL 70
+#define ART_CALRIS 71
+#define ART_ARUNRUTH 72
+#define ART_GLAMDRING 73
+#define ART_AEGLIN 74
+#define ART_ORCRIST 75
+#define ART_GURTHANG 76
+#define ART_ZARCUTHRA 77
+#define ART_MORMEGIL 78
+#define ART_GONDRICAM 79
+#define ART_CRISDURIAN 80
+#define ART_AGLARANG 81
+#define ART_RINGIL 82
+#define ART_ANDURIL 83
+#define ART_ANGUIREL 84
+#define ART_ELVAGIL 85
+#define ART_FORASGIL 86
+#define ART_CARETH 87
+#define ART_STING 88
+#define ART_HARADEKKET 89
+#define ART_GILETTAR 90
+#define ART_DOOMCALLER 91
+#define ART_VORPAL_BLADE 92
+#define ART_ERU 147
+
+/* Polearms */
+#define ART_THEODEN 93
+#define ART_PAIN 94
+#define ART_OSONDIR 95
+#define ART_TIL 96
+#define ART_AEGLOS 97
+#define ART_OROME 98
+#define ART_NIMLOTH 99
+#define ART_EORLINGAS 100
+#define ART_DURIN 101
+#define ART_EONWE 102
+#define ART_BALLI 103
+#define ART_LOTHARANG 104
+#define ART_MUNDWINE 105
+#define ART_BARUKKHELED 106
+#define ART_WRATH 107
+#define ART_ULMO 108
+#define ART_AVAVIR 109
+#define ART_FUNDIN 175
+
+/* The sword of the Dawn */
+#define ART_DAWN 110
+
+/* Hafted */
+#define ART_MELKOR 18
+#define ART_HURIN 33
+#define ART_GROND 111
+#define ART_TOTILA 112
+#define ART_THUNDERFIST 113
+#define ART_BLOODSPIKE 114
+#define ART_FIRESTAR 115
+#define ART_TARATOL 116
+#define ART_AULE 117
+#define ART_NAR 118
+#define ART_ERIRIL 119
+#define ART_OLORIN 120
+#define ART_DEATHWREAKER 121
+#define ART_TURMIL 122
+#define ART_GOTHMOG 123
+#define ART_AXE_GOTHMOG 145
+#define ART_SKULLCLEAVER 177
+
+#define ART_NAIN 174
+
+/* Bows */
+#define ART_BELTHRONDING 124
+#define ART_BARD 125
+#define ART_CUBRAGOL 126
+#define ART_UMBAR 171
+
+/* Mage Staffs */
+#define ART_GANDALF 127
+
+/* Boomerangs */
+#define ART_BEOR 128
+#define ART_GLIMDRIR 129
+
+/* Musical Instrument */
+#define ART_MAGLOR 137
+#define ART_SKY 138
+#define ART_DAERON 139
+#define ART_DRUEDAIN 141
+#define ART_ROHAN 142
+#define ART_HELM 143
+#define ART_BOROMIR 144
+
+/* Diggers */
+#define ART_EREBOR 140
+
+#define ART_ORCHAST 156
+#define ART_NIGHT 157
+#define ART_NATUREBANE 158
+
+/* Spell for various object */
+#define SPELL_ID_PLAIN 1
+#define SPELL_BO_FIRE 2
+#define SPELL_BO_COLD 3
+#define SPELL_BO_ELEC 4
+#define SPELL_BO_POIS 5
+#define SPELL_BO_ACID 6
+#define SPELL_MAPPING 7
+#define SPELL_CURE 8
+#define SPELL_ID_FULL 9
+#define SPELL_WRAITH 10
+#define SPELL_INVIS 11
+
+
+/*** Ego-Item indexes (see "lib/edit/e_info.txt") ***/
+
+#define EGO_MANA 1
+#define EGO_POWER 2
+#define EGO_MANA_POWER 3
+#define EGO_MSTAFF_SPELL 4
+#define EGO_BRAND_POIS 77
+#define EGO_BRAND_ELEC 74
+#define EGO_BRAND_FIRE 75
+#define EGO_BRAND_COLD 76
+#define EGO_BRAND_ACID 73
+#define EGO_EARTHQUAKES 80
+#define EGO_BLESS_BLADE 67
+#define EGO_VAMPIRIC 97
+#define EGO_CHAOTIC 78
+#define EGO_BLASTED 127
+#define EGO_SHATTERED 126
+#define EGO_FLAME 122
+#define EGO_FROST 123
+#define EGO_LIGHTNING_BOLT 121
+#define EGO_FREE_ACTION 49
+#define EGO_DF 66
+#define EGO_MOTION 59
+#define EGO_SPECTRAL 102
+#define EGO_NOLDOR 23
+#define EGO_JUMP 15
+#define EGO_SPINING 72
+#define EGO_DRAGON 99
+#define EGO_INST_DRAGONKIND 130
+#define EGO_ENDURE_ELEC 17
+#define EGO_ENDURE_ACID 16
+#define EGO_ENDURE_FIRE 18
+#define EGO_ENDURE_COLD 19
+#define EGO_QUIET 58
+#define EGO_WISDOM 25
+#define EGO_RESIST_ELEC 6
+#define EGO_RESIST_ACID 5
+#define EGO_RESIST_FIRE 7
+#define EGO_RESIST_COLD 8
+#define EGO_LIFE 68
+#define EGO_STEALTH 42
+#define EGO_PROTECTION 41
+#define EGO_MAGI 27
+#define EGO_SPEED 60
+#define EGO_RESISTANCE 9
+#define EGO_LITE 32
+#define EGO_AURA_FIRE 44
+#define EGO_SLAYING_WEAPON 71
+#define EGO_SLAYING 50
+#define EGO_TELEPORTATION 35
+#define EGO_ELVENKIND 10
+#define EGO_LITE_MAGI 163
+#define EGO_HA 65
+
+
+/* Activation effects for random artifacts */
+#define ACT_SUNLIGHT 1
+#define ACT_BO_MISS_1 2
+#define ACT_BA_POIS_1 3
+#define ACT_BO_ELEC_1 4
+#define ACT_BO_ACID_1 5
+#define ACT_BO_COLD_1 6
+#define ACT_BO_FIRE_1 7
+#define ACT_BA_COLD_1 8
+#define ACT_BA_FIRE_1 9
+#define ACT_DRAIN_1 10
+#define ACT_BA_COLD_2 11
+#define ACT_BA_ELEC_2 12
+#define ACT_DRAIN_2 13
+#define ACT_VAMPIRE_1 14
+#define ACT_BO_MISS_2 15
+#define ACT_BA_FIRE_2 16
+#define ACT_BA_COLD_3 17
+#define ACT_BA_ELEC_3 18
+#define ACT_WHIRLWIND 19
+#define ACT_VAMPIRE_2 20
+#define ACT_CALL_CHAOS 21
+#define ACT_ROCKET 22
+#define ACT_DISP_EVIL 23
+#define ACT_BA_MISS_3 24
+#define ACT_DISP_GOOD 25
+#define ACT_GILGALAD 26
+#define ACT_CELEBRIMBOR 27
+#define ACT_SKULLCLEAVER 28
+#define ACT_HARADRIM 29
+#define ACT_FUNDIN 30
+#define ACT_EOL 31
+#define ACT_UMBAR 32
+#define ACT_NUMENOR 33
+#define ACT_KNOWLEDGE 34
+#define ACT_UNDEATH 35
+#define ACT_THRAIN 36
+#define ACT_BARAHIR 37
+#define ACT_TULKAS 38
+#define ACT_NARYA 39
+#define ACT_NENYA 40
+#define ACT_VILYA 41
+#define ACT_POWER 42
+#define ACT_STONE_LORE 43
+#define ACT_RAZORBACK 44
+#define ACT_BLADETURNER 45
+#define ACT_MEDIATOR 46
+#define ACT_BELEGENNON 47
+#define ACT_GORLIM 48
+#define ACT_COLLUIN 49
+#define ACT_BELANGIL 50
+#define ACT_CONFUSE 51
+#define ACT_SLEEP 52
+#define ACT_QUAKE 53
+#define ACT_TERROR 54
+#define ACT_TELE_AWAY 55
+#define ACT_BANISH_EVIL 56
+#define ACT_GENOCIDE 57
+#define ACT_MASS_GENO 58
+#define ACT_ANGUIREL 59
+#define ACT_ERU 60
+#define ACT_DAWN 61
+#define ACT_FIRESTAR 62
+#define ACT_TURMIL 63
+#define ACT_CUBRAGOL 64
+#define ACT_CHARM_ANIMAL 65
+#define ACT_CHARM_UNDEAD 66
+#define ACT_CHARM_OTHER 67
+#define ACT_CHARM_ANIMALS 68
+#define ACT_CHARM_OTHERS 69
+#define ACT_SUMMON_ANIMAL 70
+#define ACT_SUMMON_PHANTOM 71
+#define ACT_SUMMON_ELEMENTAL 72
+#define ACT_SUMMON_DEMON 73
+#define ACT_SUMMON_UNDEAD 74
+#define ACT_ELESSAR 75
+#define ACT_GANDALF 76
+#define ACT_MARDA 77
+#define ACT_PALANTIR 78
+/*
+ 79
+ 80
+*/
+#define ACT_CURE_LW 81
+#define ACT_CURE_MW 82
+#define ACT_CURE_POISON 83
+#define ACT_REST_LIFE 84
+#define ACT_REST_ALL 85
+#define ACT_CURE_700 86
+#define ACT_CURE_1000 87
+/*
+ 88
+*/
+#define ACT_EREBOR 89
+#define ACT_DRUEDAIN 90
+#define ACT_ESP 91
+#define ACT_BERSERK 92
+#define ACT_PROT_EVIL 93
+#define ACT_RESIST_ALL 94
+#define ACT_SPEED 95
+#define ACT_XTRA_SPEED 96
+#define ACT_WRAITH 97
+#define ACT_INVULN 98
+#define ACT_ROHAN 99
+#define ACT_HELM 100
+#define ACT_BOROMIR 101
+#define ACT_HURIN 102
+#define ACT_AXE_GOTHMOG 103
+#define ACT_MELKOR 104
+#define ACT_GROND 105
+#define ACT_NATUREBANE 106
+#define ACT_NIGHT 107
+#define ACT_ORCHAST 108
+#define ACT_LIGHT 111
+#define ACT_MAP_LIGHT 112
+#define ACT_DETECT_ALL 113
+#define ACT_DETECT_XTRA 114
+#define ACT_ID_FULL 115
+#define ACT_ID_PLAIN 116
+#define ACT_RUNE_EXPLO 117
+#define ACT_RUNE_PROT 118
+#define ACT_SATIATE 119
+#define ACT_DEST_DOOR 120
+#define ACT_STONE_MUD 121
+#define ACT_RECHARGE 122
+#define ACT_ALCHEMY 123
+#define ACT_DIM_DOOR 124
+#define ACT_TELEPORT 125
+#define ACT_RECALL 126
+
+#define ACT_DEATH 127
+#define ACT_RUINATION 128
+#define ACT_DESTRUC 129
+#define ACT_UNINT 130
+#define ACT_UNSTR 131
+#define ACT_UNCON 132
+#define ACT_UNCHR 133
+#define ACT_UNDEX 134
+#define ACT_UNWIS 135
+#define ACT_STATLOSS 136
+#define ACT_HISTATLOSS 137
+#define ACT_EXPLOSS 138
+#define ACT_HIEXPLOSS 139
+#define ACT_SUMMON_MONST 140
+#define ACT_PARALYZE 141
+#define ACT_HALLU 142
+#define ACT_POISON 143
+#define ACT_HUNGER 144
+#define ACT_STUN 145
+#define ACT_CUTS 146
+#define ACT_PARANO 147
+#define ACT_CONFUSION 148
+#define ACT_BLIND 149
+#define ACT_PET_SUMMON 150
+#define ACT_CURE_PARA 151
+#define ACT_CURE_HALLU 152
+#define ACT_CURE_POIS 153
+#define ACT_CURE_HUNGER 154
+#define ACT_CURE_STUN 155
+#define ACT_CURE_CUTS 156
+#define ACT_CURE_FEAR 157
+#define ACT_CURE_CONF 158
+#define ACT_CURE_BLIND 159
+#define ACT_CURING 160
+#define ACT_DARKNESS 161
+#define ACT_LEV_TELE 162
+#define ACT_ACQUIREMENT 163
+#define ACT_WEIRD 164
+#define ACT_AGGRAVATE 165
+#define ACT_MUT 166
+#define ACT_CURE_INSANITY 167
+#define ACT_CURE_MUT 168
+#define ACT_LIGHT_ABSORBTION 169
+/* of dragonkind?*/
+#define ACT_BA_FIRE_H 170
+#define ACT_BA_COLD_H 171
+#define ACT_BA_ELEC_H 172
+#define ACT_BA_ACID_H 173
+#define ACT_SPIN 174
+#define ACT_NOLDOR 175
+#define ACT_SPECTRAL 176
+#define ACT_JUMP 177
+#define ACT_DEST_TELE 178
+/*amulet of serpents dam 100, rad 2 timout 40+d60 */
+#define ACT_BA_POIS_4 179
+/*rings of X 50,50+d50 dur 20+d20 */
+#define ACT_BA_COLD_4 180
+#define ACT_BA_FIRE_4 181
+#define ACT_BA_ACID_4 182
+#define ACT_BA_ELEC_4 183
+#define ACT_BR_ELEC 184
+#define ACT_BR_COLD 185
+#define ACT_BR_FIRE 186
+#define ACT_BR_ACID 187
+#define ACT_BR_POIS 188
+#define ACT_BR_MANY 189
+#define ACT_BR_CONF 190
+#define ACT_BR_SOUND 191
+#define ACT_BR_CHAOS 192
+#define ACT_BR_SHARD 193
+#define ACT_BR_BALANCE 194
+#define ACT_BR_LIGHT 195
+#define ACT_BR_POWER 196
+#define ACT_GROW_MOLD 197
+#define ACT_MUSIC 200
+/* 170 -> unused */
+
+/*** Object "tval" and "sval" codes ***/
+
+
+/*
+ * The values for the "tval" field of various objects.
+ *
+ * This value is the primary means by which items are sorted in the
+ * player inventory, followed by "sval" and "cost".
+ *
+ * Note that a "BOW" with tval = 19 and sval S = 10*N+P takes a missile
+ * weapon with tval = 16+N, and does (xP) damage when so combined. This
+ * fact is not actually used in the source, but it kind of interesting.
+ *
+ * Note that as of 2.7.8, the "item flags" apply to all items, though
+ * only armor and weapons and a few other items use any of these flags.
+ */
+
+#define TV_SKELETON 1 /* Skeletons ('s') */
+#define TV_BOTTLE 2 /* Empty bottles ('!') */
+/* XXX */
+#define TV_BATERIE 4 /* For the Alchemists */
+#define TV_SPIKE 5 /* Spikes ('~') */
+#define TV_MSTAFF 6 /* Mage Staffs */
+#define TV_CHEST 7 /* Chests ('~') */
+#define TV_PARCHMENT 8 /* Parchments from Kamband */
+#define TV_PARCHEMENT 8 /* compatibility define */
+#define TV_CORPSE 9 /* Monster corpses */
+#define TV_EGG 10 /* Monster Eggs */
+#define TV_JUNK 11 /* Sticks, Pottery, etc ('~') */
+#define TV_TOOL 12 /* Tools */
+#define TV_INSTRUMENT 14 /* Musical instruments */
+#define TV_BOOMERANG 15 /* Boomerangs */
+#define TV_SHOT 16 /* Ammo for slings */
+#define TV_ARROW 17 /* Ammo for bows */
+#define TV_BOLT 18 /* Ammo for x-bows */
+#define TV_BOW 19 /* Slings/Bows/Xbows */
+#define TV_DIGGING 20 /* Shovels/Picks */
+#define TV_HAFTED 21 /* Priest Weapons */
+#define TV_POLEARM 22 /* Pikes/Glaives/Spears/etc. */
+#define TV_SWORD 23 /* Edged Weapons */
+#define TV_AXE 24 /* Axes/Cleavers */
+#define TV_BOOTS 30 /* Boots */
+#define TV_GLOVES 31 /* Gloves */
+#define TV_HELM 32 /* Helms */
+#define TV_CROWN 33 /* Crowns */
+#define TV_SHIELD 34 /* Shields */
+#define TV_CLOAK 35 /* Cloaks */
+#define TV_SOFT_ARMOR 36 /* Soft Armor */
+#define TV_HARD_ARMOR 37 /* Hard Armor */
+#define TV_DRAG_ARMOR 38 /* Dragon Scale Mail */
+#define TV_LITE 39 /* Lites (including Specials) */
+#define TV_AMULET 40 /* Amulets (including Specials) */
+#define TV_RING 45 /* Rings (including Specials) */
+#define TV_TRAPKIT 46 /* Trapkits */
+#define TV_TOTEM 54 /* Summoner totems */
+#define TV_STAFF 55
+#define TV_WAND 65
+#define TV_ROD 66
+#define TV_ROD_MAIN 67
+#define TV_SCROLL 70
+#define TV_POTION 71
+#define TV_POTION2 72 /* Second set of potion */
+#define TV_FLASK 77
+#define TV_FOOD 80
+#define TV_HYPNOS 99 /* To wield monsters !:) */
+#define TV_GOLD 100 /* Gold can only be picked up by players */
+#define TV_RANDART 102 /* Random Artifacts */
+#define TV_RUNE1 104 /* Base runes */
+#define TV_RUNE2 105 /* Modifier runes */
+
+#define TV_BOOK 111
+#define TV_SYMBIOTIC_BOOK 112
+#define TV_MUSIC_BOOK 113
+#define TV_DRUID_BOOK 114
+#define TV_DAEMON_BOOK 115
+
+
+/* The "sval" codes for TV_JUNK */
+#define SV_BOULDER 1
+
+
+/* The "sval" codes for TV_TOOL */
+#define SV_TOOL_CLIMB 0
+#define SV_PORTABLE_HOLE 1
+
+/* The "sval" codes for TV_MSTAFF */
+#define SV_MSTAFF 1
+
+/* The "sval" codes for TV_SHOT/TV_ARROW/TV_BOLT */
+#define SV_AMMO_LIGHT 0 /* pebbles */
+#define SV_AMMO_NORMAL 1 /* shots, arrows, bolts */
+#define SV_AMMO_HEAVY 2 /* seeker arrows and bolts, mithril shots */
+
+/* The "sval" codes for TV_INSTRUMENT */
+#define SV_DRUM 58
+#define SV_HARP 59
+#define SV_HORN 60
+
+/* The "sval" codes for TV_TRAPKIT */
+#define SV_TRAPKIT_SLING 1
+#define SV_TRAPKIT_BOW 2
+#define SV_TRAPKIT_XBOW 3
+#define SV_TRAPKIT_POTION 4
+#define SV_TRAPKIT_SCROLL 5
+#define SV_TRAPKIT_DEVICE 6
+
+/* The "sval" codes for TV_BOOMERANG */
+#define SV_BOOM_S_WOOD 1 /* 1d4 */
+#define SV_BOOM_WOOD 2 /* 1d8 */
+#define SV_BOOM_S_METAL 3 /* 3d4 */
+#define SV_BOOM_METAL 4 /* 4d5 */
+
+/* The "sval" codes for TV_BOW (note information in "sval") */
+#define SV_SLING 2 /* (x2) */
+#define SV_SHORT_BOW 12 /* (x2) */
+#define SV_LONG_BOW 13 /* (x3) */
+#define SV_LIGHT_XBOW 23 /* (x3) */
+#define SV_HEAVY_XBOW 24 /* (x4) */
+
+/* The "sval" codes for TV_DIGGING */
+#define SV_SHOVEL 1
+#define SV_GNOMISH_SHOVEL 2
+#define SV_DWARVEN_SHOVEL 3
+#define SV_PICK 4
+#define SV_ORCISH_PICK 5
+#define SV_DWARVEN_PICK 6
+#define SV_MATTOCK 7
+
+/* The "sval" values for TV_HAFTED */
+#define SV_CLUB 1 /* 1d4 */
+#define SV_WHIP 2 /* 1d6 */
+#define SV_QUARTERSTAFF 3 /* 1d9 */
+#define SV_NUNCHAKU 4 /* 2d3 */
+#define SV_MACE 5 /* 2d4 */
+#define SV_BALL_AND_CHAIN 6 /* 2d4 */
+#define SV_WAR_HAMMER 8 /* 3d3 */
+#define SV_LUCERN_HAMMER 10 /* 2d5 */
+#define SV_THREE_PIECE_ROD 11 /* 3d3 */
+#define SV_MORNING_STAR 12 /* 2d6 */
+#define SV_FLAIL 13 /* 2d6 */
+#define SV_LEAD_FILLED_MACE 15 /* 3d4 */
+#define SV_TWO_HANDED_FLAIL 18 /* 3d6 */
+#define SV_GREAT_HAMMER 19 /* 4d6 */
+#define SV_MACE_OF_DISRUPTION 20 /* 5d8 */
+#define SV_GROND 50 /* 3d4 */
+
+/* The "sval" values for TV_AXE */
+#define SV_HATCHET 1 /* 1d5 */
+#define SV_CLEAVER 2 /* 2d4 */
+#define SV_LIGHT_WAR_AXE 8 /* 2d5 */
+#define SV_BEAKED_AXE 10 /* 2d6 */
+#define SV_BROAD_AXE 11 /* 2d6 */
+#define SV_BATTLE_AXE 22 /* 2d8 */
+#define SV_GREAT_AXE 25 /* 4d4 */
+#define SV_LOCHABER_AXE 28 /* 3d8 */
+#define SV_SLAUGHTER_AXE 30 /* 5d7 */
+
+/* The "sval" values for TV_POLEARM */
+#define SV_SPEAR 2 /* 1d6 */
+#define SV_SICKLE 3 /* 2d3 */
+#define SV_AWL_PIKE 4 /* 1d8 */
+#define SV_TRIDENT 5 /* 1d9 */
+#define SV_FAUCHARD 6 /* 1d10 */
+#define SV_BROAD_SPEAR 7 /* 1d9 */
+#define SV_PIKE 8 /* 2d5 */
+#define SV_GLAIVE 13 /* 2d6 */
+#define SV_HALBERD 15 /* 3d4 */
+#define SV_GUISARME 16 /* 2d5 */
+#define SV_SCYTHE 17 /* 5d3 */
+#define SV_LANCE 20 /* 2d8 */
+#define SV_TRIFURCATE_SPEAR 26 /* 2d9 */
+#define SV_HEAVY_LANCE 29 /* 4d8 */
+#define SV_SCYTHE_OF_SLICING 30 /* 8d4 */
+
+/* The "sval" codes for TV_SWORD */
+#define SV_BROKEN_DAGGER 1 /* 1d1 */
+#define SV_BROKEN_SWORD 2 /* 1d2 */
+#define SV_DAGGER 4 /* 1d4 */
+#define SV_MAIN_GAUCHE 5 /* 1d5 */
+#define SV_RAPIER 7 /* 1d6 */
+#define SV_SMALL_SWORD 8 /* 1d6 */
+#define SV_BASILLARD 9 /* 1d8 */
+#define SV_SHORT_SWORD 10 /* 1d7 */
+#define SV_SABRE 11 /* 1d7 */
+#define SV_CUTLASS 12 /* 1d7 */
+#define SV_KHOPESH 14 /* 2d4 */
+#define SV_TULWAR 15 /* 2d4 */
+#define SV_BROAD_SWORD 16 /* 2d5 */
+#define SV_LONG_SWORD 17 /* 2d5 */
+#define SV_SCIMITAR 18 /* 2d5 */
+#define SV_KATANA 20 /* 3d4 */
+#define SV_BASTARD_SWORD 21 /* 3d4 */
+#define SV_GREAT_SCIMITAR 22 /* 4d5 */
+#define SV_CLAYMORE 23 /* 2d8 */
+#define SV_ESPADON 24 /* 2d9 */
+#define SV_TWO_HANDED_SWORD 25 /* 3d6 */
+#define SV_FLAMBERGE 26 /* 3d7 */
+#define SV_EXECUTIONERS_SWORD 28 /* 4d5 */
+#define SV_ZWEIHANDER 29 /* 4d6 */
+#define SV_BLADE_OF_CHAOS 30 /* 6d5 */
+#define SV_BLUESTEEL_BLADE 31 /* 3d9 */
+#define SV_SHADOW_BLADE 32 /* 4d4 */
+#define SV_DARK_SWORD 33 /* 3d7 */
+
+/* The "sval" codes for TV_SHIELD */
+#define SV_SMALL_LEATHER_SHIELD 2
+#define SV_SMALL_METAL_SHIELD 3
+#define SV_LARGE_LEATHER_SHIELD 4
+#define SV_LARGE_METAL_SHIELD 5
+#define SV_DRAGON_SHIELD 6
+#define SV_SHIELD_OF_DEFLECTION 10
+
+/* The "sval" codes for TV_HELM */
+#define SV_HARD_LEATHER_CAP 2
+#define SV_METAL_CAP 3
+#define SV_IRON_HELM 5
+#define SV_STEEL_HELM 6
+#define SV_DRAGON_HELM 7
+#define SV_IRON_CROWN 10
+#define SV_GOLDEN_CROWN 11
+#define SV_JEWELED_CROWN 12
+#define SV_MORGOTH 50
+
+/* The "sval" codes for TV_BOOTS */
+#define SV_PAIR_OF_SOFT_LEATHER_BOOTS 2
+#define SV_PAIR_OF_HARD_LEATHER_BOOTS 3
+#define SV_PAIR_OF_METAL_SHOD_BOOTS 6
+
+/* The "sval" codes for TV_CLOAK */
+#define SV_CLOAK 1
+#define SV_ELVEN_CLOAK 2
+#define SV_FUR_CLOAK 3
+#define SV_SHADOW_CLOAK 6
+#define SV_MIMIC_CLOAK 100
+
+/* The "sval" codes for TV_GLOVES */
+#define SV_SET_OF_LEATHER_GLOVES 1
+#define SV_SET_OF_GAUNTLETS 2
+#define SV_SET_OF_CESTI 5
+
+/* The "sval" codes for TV_SOFT_ARMOR */
+#define SV_FILTHY_RAG 1
+#define SV_ROBE 2
+#define SV_PAPER_ARMOR 3 /* 4 */
+#define SV_SOFT_LEATHER_ARMOR 4
+#define SV_SOFT_STUDDED_LEATHER 5
+#define SV_HARD_LEATHER_ARMOR 6
+#define SV_HARD_STUDDED_LEATHER 7
+#define SV_RHINO_HIDE_ARMOR 8
+#define SV_CORD_ARMOR 9 /* 6 */
+#define SV_PADDED_ARMOR 10 /* 4 */
+#define SV_LEATHER_SCALE_MAIL 11
+#define SV_LEATHER_JACK 12
+#define SV_STONE_AND_HIDE_ARMOR 15 /* 15 */
+#define SV_THUNDERLORD_SUIT 16
+
+/* The "sval" codes for TV_HARD_ARMOR */
+#define SV_RUSTY_CHAIN_MAIL 1 /* 14- */
+#define SV_RING_MAIL 2 /* 12 */
+#define SV_METAL_SCALE_MAIL 3 /* 13 */
+#define SV_CHAIN_MAIL 4 /* 14 */
+#define SV_DOUBLE_RING_MAIL 5 /* 15 */
+#define SV_AUGMENTED_CHAIN_MAIL 6 /* 16 */
+#define SV_DOUBLE_CHAIN_MAIL 7 /* 16 */
+#define SV_BAR_CHAIN_MAIL 8 /* 18 */
+#define SV_METAL_BRIGANDINE_ARMOUR 9 /* 19 */
+#define SV_SPLINT_MAIL 10 /* 19 */
+#define SV_PARTIAL_PLATE_ARMOUR 12 /* 22 */
+#define SV_METAL_LAMELLAR_ARMOUR 13 /* 23 */
+#define SV_FULL_PLATE_ARMOUR 15 /* 25 */
+#define SV_RIBBED_PLATE_ARMOUR 18 /* 28 */
+#define SV_MITHRIL_CHAIN_MAIL 20 /* 28+ */
+#define SV_MITHRIL_PLATE_MAIL 25 /* 35+ */
+#define SV_ADAMANTITE_PLATE_MAIL 30 /* 40+ */
+
+/* The "sval" codes for TV_DRAG_ARMOR */
+#define SV_DRAGON_BLACK 1
+#define SV_DRAGON_BLUE 2
+#define SV_DRAGON_WHITE 3
+#define SV_DRAGON_RED 4
+#define SV_DRAGON_GREEN 5
+#define SV_DRAGON_MULTIHUED 6
+#define SV_DRAGON_SHINING 10
+#define SV_DRAGON_LAW 12
+#define SV_DRAGON_BRONZE 14
+#define SV_DRAGON_GOLD 16
+#define SV_DRAGON_CHAOS 18
+#define SV_DRAGON_BALANCE 20
+#define SV_DRAGON_POWER 30
+
+/* The sval codes for TV_LITE */
+#define SV_LITE_TORCH 0
+#define SV_LITE_LANTERN 1
+#define SV_LITE_TORCH_EVER 2
+#define SV_LITE_DWARVEN 3
+#define SV_LITE_FEANORIAN 4
+#define SV_LITE_GALADRIEL 100
+#define SV_LITE_ELENDIL 101
+#define SV_LITE_THRAIN 102
+#define SV_LITE_UNDEATH 103
+#define SV_LITE_PALANTIR 104
+#define SV_ANCHOR_SPACETIME 105
+#define SV_STONE_LORE 106
+
+
+/* The "sval" codes for TV_AMULET */
+#define SV_AMULET_DOOM 0
+#define SV_AMULET_TELEPORT 1
+#define SV_AMULET_ADORNMENT 2
+#define SV_AMULET_SLOW_DIGEST 3
+#define SV_AMULET_RESIST_ACID 4
+#define SV_AMULET_SEARCHING 5
+#define SV_AMULET_BRILLANCE 6
+#define SV_AMULET_CHARISMA 7
+#define SV_AMULET_THE_MAGI 8
+#define SV_AMULET_REFLECTION 9
+#define SV_AMULET_CARLAMMAS 10
+#define SV_AMULET_INGWE 11
+#define SV_AMULET_DWARVES 12
+#define SV_AMULET_NO_MAGIC 13
+#define SV_AMULET_NO_TELE 14
+#define SV_AMULET_RESISTANCE 15
+#define SV_AMULET_NOTHING 16
+#define SV_AMULET_SERPENT 17
+#define SV_AMULET_TORIS_MEJISTOS 18
+#define SV_AMULET_ELESSAR 19
+#define SV_AMULET_EVENSTAR 20
+#define SV_AMULET_SUSTENANCE 21
+#define SV_AMULET_TELEPATHY 22
+#define SV_AMULET_TRICKERY 23
+#define SV_AMULET_WEAPONMASTERY 24
+#define SV_AMULET_DEVOTION 25
+#define SV_AMULET_INFRA 26
+#define SV_AMULET_SPELL 27
+#define SV_AMULET_WISDOM 28
+#define SV_AMULET_RESIST_ELEC 29
+#define SV_AMULET_REGEN 30
+
+/* The sval codes for TV_RING */
+#define SV_RING_WOE 0
+#define SV_RING_AGGRAVATION 1
+#define SV_RING_WEAKNESS 2
+#define SV_RING_STUPIDITY 3
+#define SV_RING_TELEPORTATION 4
+#define SV_RING_SPECIAL 5
+#define SV_RING_SLOW_DIGESTION 6
+#define SV_RING_FEATHER_FALL 7
+#define SV_RING_RESIST_FIRE 8
+#define SV_RING_RESIST_COLD 9
+#define SV_RING_SUSTAIN_STR 10
+#define SV_RING_SUSTAIN_INT 11
+#define SV_RING_SUSTAIN_WIS 12
+#define SV_RING_SUSTAIN_CON 13
+#define SV_RING_SUSTAIN_DEX 14
+#define SV_RING_SUSTAIN_CHR 15
+#define SV_RING_PROTECTION 16
+#define SV_RING_ACID 17
+#define SV_RING_FLAMES 18
+#define SV_RING_ICE 19
+#define SV_RING_RESIST_POIS 20
+#define SV_RING_FREE_ACTION 21
+#define SV_RING_SEE_INVIS 22
+#define SV_RING_SEARCHING 23
+#define SV_RING_STR 24
+#define SV_RING_INT 25
+#define SV_RING_DEX 26
+#define SV_RING_CON 27
+#define SV_RING_ACCURACY 28
+#define SV_RING_DAMAGE 29
+#define SV_RING_SLAYING 30
+#define SV_RING_SPEED 31
+#define SV_RING_BARAHIR 32
+#define SV_RING_TULKAS 33
+#define SV_RING_NARYA 34
+#define SV_RING_NENYA 35
+#define SV_RING_VILYA 36
+#define SV_RING_POWER 37
+#define SV_RING_RES_FEAR 38
+#define SV_RING_RES_LD 39
+#define SV_RING_RES_NETHER 40
+#define SV_RING_RES_NEXUS 41
+#define SV_RING_RES_SOUND 42
+#define SV_RING_RES_CONFUSION 43
+#define SV_RING_RES_SHARDS 44
+#define SV_RING_RES_DISENCHANT 45
+#define SV_RING_RES_CHAOS 46
+#define SV_RING_RES_BLINDNESS 47
+#define SV_RING_LORDLY 48
+#define SV_RING_ATTACKS 49
+#define SV_RING_NOTHING 50
+#define SV_RING_PRECONITION 51
+#define SV_RING_FLAR 52
+#define SV_RING_INVIS 53
+#define SV_RING_FLYING 54
+#define SV_RING_WRAITH 55
+#define SV_RING_ELEC 56
+#define SV_RING_DURIN 57
+#define SV_RING_SPELL 58
+#define SV_RING_CRIT 59
+
+/* The "sval" codes for TV_STAFF */
+#define SV_STAFF_SCHOOL 1
+#define SV_STAFF_NOTHING 2
+
+/* needed for monster traps */
+#define SV_STAFF_LIGHT 3
+#define SV_STAFF_FIERY_SHIELD 4
+#define SV_STAFF_REMOVE_CURSES 5
+#define SV_STAFF_WINGS_WIND 6
+#define SV_STAFF_SHAKE 7
+#define SV_STAFF_DISARM 8
+#define SV_STAFF_TELEPORTATION 9
+#define SV_STAFF_PROBABILITY_TRAVEL 10
+#define SV_STAFF_RECOVERY 11
+#define SV_STAFF_HEALING 12
+#define SV_STAFF_VISION 13
+#define SV_STAFF_IDENTIFY 14
+#define SV_STAFF_SENSE_HIDDEN 15
+#define SV_STAFF_REVEAL_WAYS 16
+#define SV_STAFF_SENSE_MONSTER 17
+#define SV_STAFF_GENOCIDE 18
+#define SV_STAFF_SUMMON 19
+#define SV_STAFF_WISH 20
+#define SV_STAFF_MANA 21
+#define SV_STAFF_MITHRANDIR 22
+
+/* The "sval" codes for TV_WAND */
+#define SV_WAND_SCHOOL 1
+#define SV_WAND_NOTHING 2
+
+/* needed for monster traps */
+#define SV_WAND_MANATHRUST 3
+#define SV_WAND_FIREFLASH 4
+#define SV_WAND_FIREWALL 5
+#define SV_WAND_TIDAL_WAVE 6
+#define SV_WAND_ICE_STORM 7
+#define SV_WAND_NOXIOUS_CLOUD 8
+#define SV_WAND_POISON_BLOOD 9
+#define SV_WAND_THUNDERSTORM 10
+#define SV_WAND_DIG 11
+#define SV_WAND_STONE_PRISON 12
+#define SV_WAND_STRIKE 13
+#define SV_WAND_TELEPORT_AWAY 14
+#define SV_WAND_SUMMON_ANIMAL 15
+#define SV_WAND_MAGELOCK 16
+#define SV_WAND_SLOW_MONSTER 17
+#define SV_WAND_SPEED 18
+#define SV_WAND_BANISHMENT 19
+#define SV_WAND_DISPERSE_MAGIC 20
+#define SV_WAND_CHARM 21
+#define SV_WAND_CONFUSE 22
+#define SV_WAND_DEMON_BLADE 23
+#define SV_WAND_HEAL_MONSTER 24
+#define SV_WAND_HASTE_MONSTER 25
+#define SV_WAND_THRAIN 26
+
+/* The "sval" codes for TV_ROD(Rod Tips) */
+#define SV_ROD_NOTHING 0
+#define SV_ROD_DETECT_DOOR 1
+#define SV_ROD_IDENTIFY 2
+#define SV_ROD_RECALL 3
+#define SV_ROD_ILLUMINATION 4
+#define SV_ROD_MAPPING 5
+#define SV_ROD_DETECTION 6
+#define SV_ROD_PROBING 7
+#define SV_ROD_CURING 8
+#define SV_ROD_HEALING 9
+#define SV_ROD_RESTORATION 10
+#define SV_ROD_SPEED 11
+/* xxx (aimed) */
+#define SV_ROD_TELEPORT_AWAY 13
+#define SV_ROD_DISARMING 14
+#define SV_ROD_LITE 15
+#define SV_ROD_SLEEP_MONSTER 16
+#define SV_ROD_SLOW_MONSTER 17
+#define SV_ROD_DRAIN_LIFE 18
+#define SV_ROD_POLYMORPH 19
+#define SV_ROD_ACID_BOLT 20
+#define SV_ROD_ELEC_BOLT 21
+#define SV_ROD_FIRE_BOLT 22
+#define SV_ROD_COLD_BOLT 23
+#define SV_ROD_ACID_BALL 24
+#define SV_ROD_ELEC_BALL 25
+#define SV_ROD_FIRE_BALL 26
+#define SV_ROD_COLD_BALL 27
+#define SV_ROD_HAVOC 28
+#define SV_ROD_DETECT_TRAP 29
+#define SV_ROD_HOME 30
+
+
+/* The "sval" codes for TV_ROD_MAIN(Rods) */
+/* Note that the sval is the max mana capacity of the rod */
+
+#define SV_ROD_WOODEN 10
+#define SV_ROD_COPPER 20
+#define SV_ROD_IRON 50
+#define SV_ROD_ALUMINIUM 75
+#define SV_ROD_SILVER 100
+#define SV_ROD_GOLDEN 125
+#define SV_ROD_MITHRIL 160
+#define SV_ROD_ADMANTITE 200
+
+
+/* The "sval" codes for TV_SCROLL */
+
+#define SV_SCROLL_DARKNESS 0
+#define SV_SCROLL_AGGRAVATE_MONSTER 1
+#define SV_SCROLL_CURSE_ARMOR 2
+#define SV_SCROLL_CURSE_WEAPON 3
+#define SV_SCROLL_SUMMON_MONSTER 4
+#define SV_SCROLL_SUMMON_UNDEAD 5
+#define SV_SCROLL_SUMMON_MINE 6
+#define SV_SCROLL_TRAP_CREATION 7
+#define SV_SCROLL_PHASE_DOOR 8
+#define SV_SCROLL_TELEPORT 9
+#define SV_SCROLL_TELEPORT_LEVEL 10
+#define SV_SCROLL_WORD_OF_RECALL 11
+#define SV_SCROLL_IDENTIFY 12
+#define SV_SCROLL_STAR_IDENTIFY 13
+#define SV_SCROLL_REMOVE_CURSE 14
+#define SV_SCROLL_STAR_REMOVE_CURSE 15
+#define SV_SCROLL_ENCHANT_ARMOR 16
+#define SV_SCROLL_ENCHANT_WEAPON_TO_HIT 17
+#define SV_SCROLL_ENCHANT_WEAPON_TO_DAM 18
+#define SV_SCROLL_ENCHANT_WEAPON_PVAL 19
+#define SV_SCROLL_STAR_ENCHANT_ARMOR 20
+#define SV_SCROLL_STAR_ENCHANT_WEAPON 21
+#define SV_SCROLL_RECHARGING 22
+#define SV_SCROLL_RESET_RECALL 23
+#define SV_SCROLL_LIGHT 24
+#define SV_SCROLL_MAPPING 25
+#define SV_SCROLL_DETECT_GOLD 26
+#define SV_SCROLL_DETECT_ITEM 27
+#define SV_SCROLL_DETECT_TRAP 28
+#define SV_SCROLL_DETECT_DOOR 29
+#define SV_SCROLL_DETECT_INVIS 30
+#define SV_SCROLL_DIVINATION 31
+#define SV_SCROLL_SATISFY_HUNGER 32
+#define SV_SCROLL_BLESSING 33
+#define SV_SCROLL_HOLY_CHANT 34
+#define SV_SCROLL_HOLY_PRAYER 35
+#define SV_SCROLL_MONSTER_CONFUSION 36
+#define SV_SCROLL_PROTECTION_FROM_EVIL 37
+#define SV_SCROLL_RUNE_OF_PROTECTION 38
+#define SV_SCROLL_TRAP_DOOR_DESTRUCTION 39
+#define SV_SCROLL_DEINCARNATION 40
+#define SV_SCROLL_STAR_DESTRUCTION 41
+#define SV_SCROLL_DISPEL_UNDEAD 42
+#define SV_SCROLL_MASS_RESURECTION 43
+#define SV_SCROLL_GENOCIDE 44
+#define SV_SCROLL_MASS_GENOCIDE 45
+#define SV_SCROLL_ACQUIREMENT 46
+#define SV_SCROLL_STAR_ACQUIREMENT 47
+#define SV_SCROLL_FIRE 48
+#define SV_SCROLL_ICE 49
+#define SV_SCROLL_CHAOS 50
+#define SV_SCROLL_RUMOR 51
+#define SV_SCROLL_ARTIFACT 52
+#define SV_SCROLL_NOTHING 53
+
+/* The "sval" codes for TV_POTION */
+#define SV_POTION_WATER 0
+#define SV_POTION_APPLE_JUICE 1
+#define SV_POTION_SLIME_MOLD 2
+#define SV_POTION_BLOOD 3
+#define SV_POTION_SLOWNESS 4
+#define SV_POTION_SALT_WATER 5
+#define SV_POTION_POISON 6
+#define SV_POTION_BLINDNESS 7
+#define SV_POTION_INVIS 8
+#define SV_POTION_CONFUSION 9
+#define SV_POTION_MUTATION 10
+#define SV_POTION_SLEEP 11
+#define SV_POTION_LEARNING 12
+#define SV_POTION_LOSE_MEMORIES 13
+/* xxx */
+#define SV_POTION_RUINATION 15
+#define SV_POTION_DEC_STR 16
+#define SV_POTION_DEC_INT 17
+#define SV_POTION_DEC_WIS 18
+#define SV_POTION_DEC_DEX 19
+#define SV_POTION_DEC_CON 20
+#define SV_POTION_DEC_CHR 21
+#define SV_POTION_DETONATIONS 22
+#define SV_POTION_DEATH 23
+#define SV_POTION_INFRAVISION 24
+#define SV_POTION_DETECT_INVIS 25
+#define SV_POTION_SLOW_POISON 26
+#define SV_POTION_CURE_POISON 27
+#define SV_POTION_BOLDNESS 28
+#define SV_POTION_SPEED 29
+#define SV_POTION_RESIST_HEAT 30
+#define SV_POTION_RESIST_COLD 31
+#define SV_POTION_HEROISM 32
+#define SV_POTION_BESERK_STRENGTH 33
+#define SV_POTION_CURE_LIGHT 34
+#define SV_POTION_CURE_SERIOUS 35
+#define SV_POTION_CURE_CRITICAL 36
+#define SV_POTION_HEALING 37
+#define SV_POTION_STAR_HEALING 38
+#define SV_POTION_LIFE 39
+#define SV_POTION_RESTORE_MANA 40
+#define SV_POTION_RESTORE_EXP 41
+#define SV_POTION_RES_STR 42
+#define SV_POTION_RES_INT 43
+#define SV_POTION_RES_WIS 44
+#define SV_POTION_RES_DEX 45
+#define SV_POTION_RES_CON 46
+#define SV_POTION_RES_CHR 47
+#define SV_POTION_INC_STR 48
+#define SV_POTION_INC_INT 49
+#define SV_POTION_INC_WIS 50
+#define SV_POTION_INC_DEX 51
+#define SV_POTION_INC_CON 52
+#define SV_POTION_INC_CHR 53
+/* xxx */
+#define SV_POTION_AUGMENTATION 55
+#define SV_POTION_ENLIGHTENMENT 56
+#define SV_POTION_STAR_ENLIGHTENMENT 57
+#define SV_POTION_SELF_KNOWLEDGE 58
+#define SV_POTION_EXPERIENCE 59
+#define SV_POTION_RESISTANCE 60
+#define SV_POTION_CURING 61
+#define SV_POTION_INVULNERABILITY 62
+#define SV_POTION_NEW_LIFE 63
+
+#define SV_POTION_LAST 63
+
+/* The "sval" codes for TV_POTION2 */
+#define SV_POTION2_MIMIC 1
+#define SV_POTION2_CURE_LIGHT_SANITY 14
+#define SV_POTION2_CURE_SERIOUS_SANITY 15
+#define SV_POTION2_CURE_CRITICAL_SANITY 16
+#define SV_POTION2_CURE_SANITY 17
+#define SV_POTION2_CURE_WATER 18
+
+#define SV_POTION2_LAST 18
+
+/* The "sval" codes for TV_FOOD */
+#define SV_FOOD_POISON 0
+#define SV_FOOD_BLINDNESS 1
+#define SV_FOOD_PARANOIA 2
+#define SV_FOOD_CONFUSION 3
+#define SV_FOOD_HALLUCINATION 4
+#define SV_FOOD_PARALYSIS 5
+#define SV_FOOD_WEAKNESS 6
+#define SV_FOOD_SICKNESS 7
+#define SV_FOOD_STUPIDITY 8
+#define SV_FOOD_NAIVETY 9
+#define SV_FOOD_UNHEALTH 10
+#define SV_FOOD_DISEASE 11
+#define SV_FOOD_CURE_POISON 12
+#define SV_FOOD_CURE_BLINDNESS 13
+#define SV_FOOD_CURE_PARANOIA 14
+#define SV_FOOD_CURE_CONFUSION 15
+#define SV_FOOD_CURE_SERIOUS 16
+#define SV_FOOD_RESTORE_STR 17
+#define SV_FOOD_RESTORE_CON 18
+#define SV_FOOD_RESTORING 19
+/* many missing mushrooms */
+#define SV_FOOD_BISCUIT 32
+#define SV_FOOD_JERKY 33
+#define SV_FOOD_RATION 35
+#define SV_FOOD_SLIME_MOLD 36
+#define SV_FOOD_WAYBREAD 37
+#define SV_FOOD_PINT_OF_ALE 38
+#define SV_FOOD_PINT_OF_WINE 39
+#define SV_FOOD_ATHELAS 40
+#define SV_FOOD_GREAT_HEALTH 41
+#define SV_FOOD_FORTUNE_COOKIE 42
+
+/* The "sval" codes for TV_BATERIE */
+#define SV_BATERIE_POISON 1
+#define SV_BATERIE_EXPLOSION 2
+#define SV_BATERIE_TELEPORT 3
+#define SV_BATERIE_COLD 4
+#define SV_BATERIE_FIRE 5
+#define SV_BATERIE_ACID 6
+#define SV_BATERIE_LIFE 7
+#define SV_BATERIE_CONFUSION 8
+#define SV_BATERIE_LITE 9
+#define SV_BATERIE_CHAOS 10
+#define SV_BATERIE_TIME 11
+#define SV_BATERIE_MAGIC 12
+#define SV_BATERIE_XTRA_LIFE 13
+#define SV_BATERIE_DARKNESS 14
+#define SV_BATERIE_KNOWLEDGE 15
+#define SV_BATERIE_FORCE 16
+#define SV_BATERIE_LIGHTNING 17
+#define SV_BATERIE_MANA 18
+#define MAX_BATERIE_SVAL 18
+
+/* The "sval" codes for TV_CORPSE */
+#define SV_CORPSE_CORPSE 1
+#define SV_CORPSE_SKELETON 2
+#define SV_CORPSE_HEAD 3
+#define SV_CORPSE_SKULL 4
+#define SV_CORPSE_MEAT 5
+
+/* The "sval" codes for TV_DAEMON_BOOK */
+#define SV_DEMONBLADE 55
+#define SV_DEMONSHIELD 56
+#define SV_DEMONHORN 57
+
+/*
+ * Special "sval" limit -- first "normal" food
+ */
+#define SV_FOOD_MIN_FOOD 32
+
+/*
+ * Special "sval" limit -- first "aimed" rod
+ */
+#define SV_ROD_MIN_DIRECTION 12
+
+/*
+ * Special "sval" limit -- first "large" chest
+ */
+#define SV_CHEST_MIN_LARGE 4
+
+/*
+ * Special "sval" limit -- last "good" magic/prayer book
+ */
+#define SV_BOOK_MAX_GOOD 49
+
+/* flags for operation in get_random_trap in object3.c */
+
+#define TRAP_EXISTS 0x00000001L
+#define TRAP_FOUND 0x00000002L
+#define TRAP_NOTFOUND 0x00000004L
+#define TRAP_IDENTIFIED 0x00000008L
+
+/*** General flag values ***/
+
+
+/*
+ * Special cave grid flags
+ */
+#define CAVE_MARK 0x0001 /* memorized feature */
+#define CAVE_GLOW 0x0002 /* self-illuminating */
+#define CAVE_ICKY 0x0004 /* part of a vault */
+#define CAVE_ROOM 0x0008 /* part of a room */
+#define CAVE_SEEN 0x0010 /* seen flag */
+#define CAVE_VIEW 0x0020 /* view flag */
+#define CAVE_TEMP 0x0040 /* temp flag */
+#define CAVE_WALL 0x0080 /* wall flag */
+#define CAVE_TRDT 0x0100 /* trap detected */
+#define CAVE_IDNT 0x0200 /* grid identified (fountains) */
+#define CAVE_SPEC 0x0400 /* special mark(quests) */
+#define CAVE_FREE 0x0800 /* no random generation on it */
+#define CAVE_DETECT 0x1000 /* Traps detected here */
+#define CAVE_PLIT 0x2000 /* Player lit grid */
+#define CAVE_MLIT 0x4000 /* Monster lit grid */
+
+/*
+ * Bit flags for the "project()" function
+ *
+ * JUMP: Jump directly to the target location (this is a hack)
+ * BEAM: Work as a beam weapon (affect every grid passed through)
+ * THRU: Continue "through" the target (used for "bolts"/"beams")
+ * WALL: Continue "through" the walls
+ * STOP: Stop as soon as we hit a monster (used for "bolts")
+ * GRID: Affect each grid in the "blast area" in some way
+ * ITEM: Affect each object in the "blast area" in some way
+ * KILL: Affect each monster in the "blast area" in some way
+ * HIDE: Hack -- disable "visual" feedback from projection
+ */
+#define PROJECT_JUMP 0x00000001
+#define PROJECT_BEAM 0x00000002
+#define PROJECT_THRU 0x00000004
+#define PROJECT_STOP 0x00000008
+#define PROJECT_GRID 0x00000010
+#define PROJECT_ITEM 0x00000020
+#define PROJECT_KILL 0x00000040
+#define PROJECT_HIDE 0x00000080
+#define PROJECT_VIEWABLE 0x00000100 /* Affect monsters in LOS */
+#define PROJECT_METEOR_SHOWER 0x00000200 /* Affect random grids */
+#define PROJECT_BLAST 0x00000400 /* Like Mega_blast, but will only affect viewable grids */
+#define PROJECT_PANEL 0x00000800 /* Affect everything in the panel. */
+#define PROJECT_ALL 0x00001000 /* Affect every single grid. */
+#define PROJECT_WALL 0x00002000
+#define PROJECT_MANA_PATH 0x00004000 /* Follow a mana path. */
+#define PROJECT_ABSORB_MANA 0x00008000 /* The spell increase in power as it absord grid's mana. */
+#define PROJECT_STAY 0x00010000
+
+/*
+ * Bit flags for the "enchant()" function
+ */
+#define ENCH_TOHIT 0x01
+#define ENCH_TODAM 0x02
+#define ENCH_TOAC 0x04
+#define ENCH_PVAL 0x08
+
+/*
+ * Bit flags for the "target_set" function XXX XXX XXX
+ *
+ * KILL: Target monsters
+ * LOOK: Describe grid fully
+ * XTRA: Currently unused flag
+ * GRID: Select from all grids
+ */
+#define TARGET_KILL 0x01
+#define TARGET_LOOK 0x02
+#define TARGET_XTRA 0x04
+#define TARGET_GRID 0x08
+
+
+/*
+ * Some bit-flags for the "smart" field
+ */
+#define SM_RES_ACID 0x00000001
+#define SM_RES_ELEC 0x00000002
+#define SM_RES_FIRE 0x00000004
+#define SM_RES_COLD 0x00000008
+#define SM_RES_POIS 0x00000010
+#define SM_RES_NETH 0x00000020
+#define SM_RES_LITE 0x00000040
+#define SM_RES_DARK 0x00000080
+#define SM_RES_FEAR 0x00000100
+#define SM_RES_CONF 0x00000200
+#define SM_RES_CHAOS 0x00000400
+#define SM_RES_DISEN 0x00000800
+#define SM_RES_BLIND 0x00001000
+#define SM_RES_NEXUS 0x00002000
+#define SM_RES_SOUND 0x00004000
+#define SM_RES_SHARD 0x00008000
+#define SM_OPP_ACID 0x00010000
+#define SM_OPP_ELEC 0x00020000
+#define SM_OPP_FIRE 0x00040000
+#define SM_OPP_COLD 0x00080000
+#define SM_OPP_POIS 0x00100000
+#define SM_OPP_XXX1 0x00200000
+#define SM_CLONED 0x00400000
+#define SM_NOTE_TRAP 0x00800000
+#define SM_IMM_ACID 0x01000000
+#define SM_IMM_ELEC 0x02000000
+#define SM_IMM_FIRE 0x04000000
+#define SM_IMM_COLD 0x08000000
+#define SM_IMM_XXX5 0x10000000
+#define SM_IMM_REFLECT 0x20000000
+#define SM_IMM_FREE 0x40000000
+#define SM_IMM_MANA 0x80000000
+
+/*
+ * Monster status(Player POV)
+ */
+#define MSTATUS_ENEMY -2
+#define MSTATUS_NEUTRAL_M -1
+#define MSTATUS_NEUTRAL 0
+#define MSTATUS_NEUTRAL_P 1
+#define MSTATUS_FRIEND 2
+#define MSTATUS_PET 3
+#define MSTATUS_COMPANION 4
+
+/*
+ * Bit flags for the "get_item" function
+ */
+#define USE_EQUIP 0x01 /* Allow equip items */
+#define USE_INVEN 0x02 /* Allow inven items */
+#define USE_FLOOR 0x04 /* Allow floor items */
+#define USE_EXTRA 0x08 /* Allow extra items */
+#define USE_AUTO 0x10 /* Allow creation of automatizer rule */
+/*
+ * Bit flags for the "p_ptr->notice" variable
+ */
+#define PN_COMBINE 0x00000001L /* Combine the pack */
+#define PN_REORDER 0x00000002L /* Reorder the pack */
+/* xxx (many) */
+
+
+/*
+ * Bit flags for the "p_ptr->update" variable
+ */
+#define PU_BONUS 0x00000001L /* Calculate bonuses */
+#define PU_TORCH 0x00000002L /* Calculate torch radius */
+#define PU_BODY 0x00000004L /* Calculate body parts */
+#define PU_SANITY 0x00000008L /* Calculate csan and msan */
+#define PU_HP 0x00000010L /* Calculate chp and mhp */
+#define PU_MANA 0x00000020L /* Calculate csp and msp */
+#define PU_SPELLS 0x00000040L /* Calculate spells */
+#define PU_POWERS 0x00000080L /* Calculate powers */
+/* xxx (many) */
+#define PU_UN_VIEW 0x00010000L /* Forget view */
+/* xxx (many) */
+#define PU_VIEW 0x00100000L /* Update view */
+#define PU_MON_LITE 0x00200000L /* Update monster light */
+/* xxx */
+#define PU_MONSTERS 0x01000000L /* Update monsters */
+#define PU_DISTANCE 0x02000000L /* Update distances */
+/* xxx */
+#define PU_FLOW 0x10000000L /* Update flow */
+/* xxx (many) */
+
+
+/*
+ * Bit flags for the "p_ptr->redraw" variable
+ */
+#define PR_MISC 0x00000001L /* Display Race/Class */
+#define PR_TITLE 0x00000002L /* Display Title */
+#define PR_LEV 0x00000004L /* Display Level */
+#define PR_EXP 0x00000008L /* Display Experience */
+#define PR_STATS 0x00000010L /* Display Stats */
+#define PR_ARMOR 0x00000020L /* Display Armor */
+#define PR_HP 0x00000040L /* Display Hitpoints */
+#define PR_MANA 0x00000080L /* Display Mana */
+#define PR_GOLD 0x00000100L /* Display Gold */
+#define PR_DEPTH 0x00000200L /* Display Depth */
+/****/
+#define PR_HEALTH 0x00000800L /* Display Health Bar */
+#define PR_CUT 0x00001000L /* Display Extra (Cut) */
+#define PR_STUN 0x00002000L /* Display Extra (Stun) */
+#define PR_HUNGER 0x00004000L /* Display Extra (Hunger) */
+#define PR_PIETY 0x00008000L /* Display Piety */
+#define PR_BLIND 0x00010000L /* Display Extra (Blind) */
+#define PR_CONFUSED 0x00020000L /* Display Extra (Confused) */
+#define PR_AFRAID 0x00040000L /* Display Extra (Afraid) */
+#define PR_POISONED 0x00080000L /* Display Extra (Poisoned) */
+#define PR_STATE 0x00100000L /* Display Extra (State) */
+#define PR_SPEED 0x00200000L /* Display Extra (Speed) */
+#define PR_STUDY 0x00400000L /* Display Extra (Study) */
+#define PR_SANITY 0x00800000L /* Display Sanity */
+#define PR_EXTRA 0x01000000L /* Display Extra Info */
+#define PR_BASIC 0x02000000L /* Display Basic Info */
+#define PR_MAP 0x04000000L /* Display Map */
+#define PR_WIPE 0x08000000L /* Hack -- Total Redraw */
+#define PR_MH 0x10000000L /* Display Monster hitpoints */
+#define PR_DTRAP 0x20000000L /* Display Extra (DTrap) */
+/* xxx */
+/* xxx */
+
+/*
+ * Bit flags for the "p_ptr->window" variable (etc)
+ */
+#define PW_INVEN 0x00000001L /* Display inven/equip */
+#define PW_EQUIP 0x00000002L /* Display equip/inven */
+/* xxx */
+#define PW_PLAYER 0x00000008L /* Display character */
+#define PW_M_LIST 0x00000010L /* Show monster list */
+/* xxx */
+#define PW_MESSAGE 0x00000040L /* Display messages */
+#define PW_OVERHEAD 0x00000080L /* Display overhead view */
+#define PW_MONSTER 0x00000100L /* Display monster recall */
+#define PW_OBJECT 0x00000200L /* Display object recall */
+/* xxx */
+#define PW_SNAPSHOT 0x00000800L /* Display snap-shot */
+/* xxx */
+/* xxx */
+#define PW_BORG_1 0x00004000L /* Display borg messages */
+#define PW_BORG_2 0x00008000L /* Display borg status */
+
+
+/* jk */
+#define FTRAP_CHEST 0x000000001 /* may appear on chests */
+#define FTRAP_DOOR 0x000000002 /* may appear on doors/floors */
+#define FTRAP_FLOOR 0x000000004 /* may appear on floor */
+#define FTRAP_CHANGE 0x000000008 /* Color changing */
+#define FTRAP_XXX5 0x000000010
+#define FTRAP_XXX6 0x000000020
+#define FTRAP_XXX7 0x000000040
+#define FTRAP_XXX8 0x000000080
+#define FTRAP_XXX9 0x000000100
+#define FTRAP_XXX10 0x000000200
+#define FTRAP_XXX11 0x000000400
+#define FTRAP_XXX12 0x000000800
+#define FTRAP_XXX13 0x000001000
+#define FTRAP_XXX14 0x000002000
+#define FTRAP_XXX15 0x000004000
+#define FTRAP_XXX16 0x000008000
+#define FTRAP_LEVEL1 0x000010000 /* low level ball/bolt trap */
+#define FTRAP_LEVEL2 0x000020000 /* medium level ball/bolt trap */
+#define FTRAP_LEVEL3 0x000040000 /* high level ball/bolt trap */
+#define FTRAP_LEVEL4 0x000080000 /* oops level ball/bolt trap */
+#define FTRAP_XXX21 0x000100000
+#define FTRAP_XXX22 0x000200000
+#define FTRAP_XXX23 0x000400000
+#define FTRAP_XXX24 0x000800000
+#define FTRAP_XXX25 0x001000000
+#define FTRAP_XXX26 0x002000000
+#define FTRAP_XXX27 0x004000000
+#define FTRAP_XXX28 0x008000000
+#define FTRAP_XXX29 0x010000000
+#define FTRAP_XXX30 0x020000000
+#define FTRAP_XXX31 0x040000000
+#define FTRAP_XXX32 0x080000000
+
+/* jk */
+#define STAT_DEC_TEMPORARY 1
+#define STAT_DEC_NORMAL 2
+#define STAT_DEC_PERMANENT 3
+
+/* jk - which trap is which number */
+#define TRAP_OF_WEAKNESS_I 1
+#define TRAP_OF_WEAKNESS_II 2
+#define TRAP_OF_WEAKNESS_III 3
+#define TRAP_OF_INTELLIGENCE_I 4
+#define TRAP_OF_INTELLIGENCE_II 5
+#define TRAP_OF_INTELLIGENCE_III 6
+#define TRAP_OF_WISDOM_I 7
+#define TRAP_OF_WISDOM_II 8
+#define TRAP_OF_WISDOM_III 9
+#define TRAP_OF_FUMBLING_I 10
+#define TRAP_OF_FUMBLING_II 11
+#define TRAP_OF_FUMBLING_III 12
+#define TRAP_OF_WASTING_I 13
+#define TRAP_OF_WASTING_II 14
+#define TRAP_OF_WASTING_III 15
+#define TRAP_OF_BEAUTY_I 16
+#define TRAP_OF_BEAUTY_II 17
+#define TRAP_OF_BEAUTY_III 18
+
+#define TRAP_OF_CURSE_WEAPON 20
+#define TRAP_OF_CURSE_ARMOR 21
+#define TRAP_OF_EARTHQUAKE 22
+#define TRAP_OF_POISON_NEEDLE 23
+#define TRAP_OF_SUMMON_MONSTER 24
+#define TRAP_OF_SUMMON_UNDEAD 25
+#define TRAP_OF_SUMMON_GREATER_UNDEAD 26
+#define TRAP_OF_TELEPORT 27
+#define TRAP_OF_PARALYZING 28
+#define TRAP_OF_EXPLOSIVE_DEVICE 29
+#define TRAP_OF_TELEPORT_AWAY 30
+#define TRAP_OF_LOSE_MEMORY 31
+#define TRAP_OF_BITTER_REGRET 32
+#define TRAP_OF_BOWEL_CRAMPS 33
+#define TRAP_OF_BLINDNESS_CONFUSION 34
+#define TRAP_OF_AGGRAVATION 35
+#define TRAP_OF_MULTIPLICATION 36
+#define TRAP_OF_STEAL_ITEM 37
+#define TRAP_OF_SUMMON_FAST_QUYLTHULGS 38
+#define TRAP_OF_SINKING 39
+#define TRAP_OF_MANA_DRAIN 40
+#define TRAP_OF_MISSING_MONEY 41
+#define TRAP_OF_NO_RETURN 42
+#define TRAP_OF_SILENT_SWITCHING 43
+#define TRAP_OF_WALLS 44
+#define TRAP_OF_CALLING_OUT 45
+#define TRAP_OF_SLIDING 46
+#define TRAP_OF_CHARGES_DRAIN 47
+#define TRAP_OF_STAIR_MOVEMENT 48
+#define TRAP_OF_NEW 49
+#define TRAP_OF_SCATTER_ITEMS 50
+#define TRAP_OF_DECAY 51
+#define TRAP_OF_WASTING_WANDS 52
+#define TRAP_OF_FILLING 53
+#define TRAP_OF_DRAIN_SPEED 54
+
+#define TRAP_OF_ELEC_BOLT 60
+#define TRAP_OF_POIS_BOLT 61
+#define TRAP_OF_ACID_BOLT 62
+#define TRAP_OF_COLD_BOLT 63
+#define TRAP_OF_FIRE_BOLT 64
+#define TRAP_OF_PLASMA_BOLT 65
+#define TRAP_OF_WATER_BOLT 66
+#define TRAP_OF_LITE_BOLT 67
+#define TRAP_OF_DARK_BOLT 68
+#define TRAP_OF_SHARDS_BOLT 69
+#define TRAP_OF_SOUND_BOLT 70
+#define TRAP_OF_CONFUSION_BOLT 71
+#define TRAP_OF_FORCE_BOLT 72
+#define TRAP_OF_INERTIA_BOLT 73
+#define TRAP_OF_MANA_BOLT 74
+#define TRAP_OF_ICE_BOLT 75
+#define TRAP_OF_CHAOS_BOLT 76
+#define TRAP_OF_NETHER_BOLT 77
+#define TRAP_OF_DISENCHANT_BOLT 78
+#define TRAP_OF_NEXUS_BOLT 79
+#define TRAP_OF_TIME_BOLT 80
+#define TRAP_OF_GRAVITY_BOLT 81
+
+#define TRAP_OF_ELEC_BALL 82
+#define TRAP_OF_POIS_BALL 83
+#define TRAP_OF_ACID_BALL 84
+#define TRAP_OF_COLD_BALL 85
+#define TRAP_OF_FIRE_BALL 86
+#define TRAP_OF_PLASMA_BALL 87
+#define TRAP_OF_WATER_BALL 88
+#define TRAP_OF_LITE_BALL 89
+#define TRAP_OF_DARK_BALL 90
+#define TRAP_OF_SHARDS_BALL 91
+#define TRAP_OF_SOUND_BALL 92
+#define TRAP_OF_CONFUSION_BALL 93
+#define TRAP_OF_FORCE_BALL 94
+#define TRAP_OF_INERTIA_BALL 95
+#define TRAP_OF_MANA_BALL 96
+#define TRAP_OF_ICE_BALL 97
+#define TRAP_OF_CHAOS_BALL 98
+#define TRAP_OF_NETHER_BALL 99
+#define TRAP_OF_DISENCHANT_BALL 100
+#define TRAP_OF_NEXUS_BALL 101
+#define TRAP_OF_TIME_BALL 102
+#define TRAP_OF_GRAVITY_BALL 103
+
+#define TRAP_OF_ARROW_I 110
+#define TRAP_OF_ARROW_II 111
+#define TRAP_OF_ARROW_III 112
+#define TRAP_OF_ARROW_IV 113
+#define TRAP_OF_POISON_ARROW_I 114
+#define TRAP_OF_POISON_ARROW_II 115
+#define TRAP_OF_POISON_ARROW_III 116
+#define TRAP_OF_POISON_ARROW_IV 117
+#define TRAP_OF_DAGGER_I 118
+#define TRAP_OF_DAGGER_II 119
+#define TRAP_OF_POISON_DAGGER_I 120
+#define TRAP_OF_POISON_DAGGER_II 121
+#define TRAP_OF_ARROWS_I 122
+#define TRAP_OF_ARROWS_II 123
+#define TRAP_OF_ARROWS_III 124
+#define TRAP_OF_ARROWS_IV 125
+#define TRAP_OF_POISON_ARROWS_I 126
+#define TRAP_OF_POISON_ARROWS_II 127
+#define TRAP_OF_POISON_ARROWS_III 128
+#define TRAP_OF_POISON_ARROWS_IV 129
+#define TRAP_OF_DAGGERS_I 130
+#define TRAP_OF_DAGGERS_II 131
+#define TRAP_OF_POISON_DAGGERS_I 132
+#define TRAP_OF_POISON_DAGGERS_II 133
+
+#define TRAP_OF_DROP_ITEMS 140
+#define TRAP_OF_DROP_ALL_ITEMS 141
+#define TRAP_OF_DROP_EVERYTHING 142
+
+/* -SC- */
+#define TRAP_OF_FEMINITY 150
+#define TRAP_OF_MASCULINITY 151
+#define TRAP_OF_NEUTRALITY 152
+#define TRAP_OF_AGING 153
+#define TRAP_OF_GROWING 154
+#define TRAP_OF_SHRINKING 155
+#define TRAP_OF_ELDRITCH_HORROR 156
+/* XXX */
+#define TRAP_OF_DIVINE_ANGER 158
+#define TRAP_OF_DIVINE_WRATH 159
+#define TRAP_OF_HALLUCINATION 160
+
+#define TRAP_OF_ROCKET 161
+#define TRAP_OF_NUKE_BOLT 162
+#define TRAP_OF_DEATH_RAY 163
+#define TRAP_OF_HOLY_FIRE 164
+#define TRAP_OF_HELL_FIRE 165
+#define TRAP_OF_PSI_BOLT 166
+#define TRAP_OF_PSI_DRAIN 167
+#define TRAP_OF_NUKE_BALL 168
+#define TRAP_OF_PSI_BALL 169
+
+/* DG */
+#define TRAP_OF_ACQUIREMENT 170
+
+/* Runescrye */
+#define TRAP_G_ELEC_BOLT 171
+#define TRAP_G_POIS_BOLT 172
+#define TRAP_G_ACID_BOLT 173
+#define TRAP_G_COLD_BOLT 174
+#define TRAP_G_FIRE_BOLT 175
+
+/*** General index values ***/
+
+
+/*
+ * Legal restrictions for "summon_specific()"
+ */
+#define SUMMON_ANT 11
+#define SUMMON_SPIDER 12
+#define SUMMON_HOUND 13
+#define SUMMON_HYDRA 14
+#define SUMMON_ANGEL 15
+#define SUMMON_DEMON 16
+#define SUMMON_UNDEAD 17
+#define SUMMON_DRAGON 18
+#define SUMMON_HI_UNDEAD 21
+#define SUMMON_HI_DRAGON 22
+#define SUMMON_WRAITH 31
+#define SUMMON_UNIQUE 32
+#define SUMMON_BIZARRE1 33
+#define SUMMON_BIZARRE2 34
+#define SUMMON_BIZARRE3 35
+#define SUMMON_BIZARRE4 36
+#define SUMMON_BIZARRE5 37
+#define SUMMON_BIZARRE6 38
+#define SUMMON_HI_DEMON 39
+#define SUMMON_KIN 40
+#define SUMMON_DAWN 41
+#define SUMMON_ANIMAL 42
+#define SUMMON_ANIMAL_RANGER 43
+#define SUMMON_HI_UNDEAD_NO_UNIQUES 44
+#define SUMMON_HI_DRAGON_NO_UNIQUES 45
+#define SUMMON_NO_UNIQUES 46
+#define SUMMON_PHANTOM 47
+#define SUMMON_ELEMENTAL 48
+#define SUMMON_THUNDERLORD 49
+#define SUMMON_BLUE_HORROR 50
+#define SUMMON_BUG 51
+#define SUMMON_RNG 52
+#define SUMMON_MINE 53
+#define SUMMON_HUMAN 54
+#define SUMMON_SHADOWS 55
+#define SUMMON_GHOST 56
+#define SUMMON_QUYLTHULG 57
+#define SUMMON_LUA 58
+
+
+/*
+ * Spell types used by project(), and related functions.
+ */
+#define GF_ELEC 1
+#define GF_POIS 2
+#define GF_ACID 3
+#define GF_COLD 4
+#define GF_FIRE 5
+#define GF_UNBREATH 6
+#define GF_CORPSE_EXPL 7
+#define GF_MISSILE 10
+#define GF_ARROW 11
+#define GF_PLASMA 12
+#define GF_WAVE 13
+#define GF_WATER 14
+#define GF_LITE 15
+#define GF_DARK 16
+#define GF_LITE_WEAK 17
+#define GF_DARK_WEAK 18
+#define GF_SHARDS 20
+#define GF_SOUND 21
+#define GF_CONFUSION 22
+#define GF_FORCE 23
+#define GF_INERTIA 24
+#define GF_MANA 26
+#define GF_METEOR 27
+#define GF_ICE 28
+#define GF_CHAOS 30
+#define GF_NETHER 31
+#define GF_DISENCHANT 32
+#define GF_NEXUS 33
+#define GF_TIME 34
+#define GF_GRAVITY 35
+#define GF_KILL_WALL 40
+#define GF_KILL_DOOR 41
+#define GF_KILL_TRAP 42
+#define GF_MAKE_WALL 45
+#define GF_MAKE_DOOR 46
+#define GF_MAKE_TRAP 47
+#define GF_OLD_CLONE 51
+#define GF_OLD_POLY 52
+#define GF_OLD_HEAL 53
+#define GF_OLD_SPEED 54
+#define GF_OLD_SLOW 55
+#define GF_OLD_CONF 56
+#define GF_OLD_SLEEP 57
+#define GF_OLD_DRAIN 58
+#define GF_AWAY_UNDEAD 61
+#define GF_AWAY_EVIL 62
+#define GF_AWAY_ALL 63
+#define GF_TURN_UNDEAD 64
+#define GF_TURN_EVIL 65
+#define GF_TURN_ALL 66
+#define GF_DISP_UNDEAD 67
+#define GF_DISP_EVIL 68
+#define GF_DISP_ALL 69
+#define GF_DISP_DEMON 70 /* New types for Zangband begin here... */
+#define GF_DISP_LIVING 71
+#define GF_ROCKET 72
+#define GF_NUKE 73
+#define GF_MAKE_GLYPH 74
+#define GF_STASIS 75
+#define GF_STONE_WALL 76
+#define GF_DEATH_RAY 77
+#define GF_STUN 78
+#define GF_HOLY_FIRE 79
+#define GF_HELL_FIRE 80
+#define GF_DISINTEGRATE 81
+#define GF_CHARM 82
+#define GF_CONTROL_UNDEAD 83
+#define GF_CONTROL_ANIMAL 84
+#define GF_PSI 85
+#define GF_PSI_DRAIN 86
+#define GF_TELEKINESIS 87
+#define GF_JAM_DOOR 88
+#define GF_DOMINATION 89
+#define GF_DISP_GOOD 90
+#define GF_IDENTIFY 91
+#define GF_RAISE 92
+#define GF_STAR_IDENTIFY 93
+#define GF_DESTRUCTION 94
+#define GF_STUN_CONF 95
+#define GF_STUN_DAM 96
+#define GF_CONF_DAM 98
+#define GF_STAR_CHARM 99
+#define GF_IMPLOSION 100
+#define GF_LAVA_FLOW 101
+#define GF_FEAR 102
+#define GF_BETWEEN_GATE 103
+#define GF_WINDS_MANA 104
+#define GF_DEATH 105
+#define GF_CONTROL_DEMON 106
+#define GF_RAISE_DEMON 107
+#define GF_TRAP_DEMONSOUL 108
+#define GF_ATTACK 109
+#define GF_CHARM_UNMOVING 110
+#define MAX_GF 111
+
+/*
+ * Some things which induce learning
+ */
+#define DRS_ACID 1
+#define DRS_ELEC 2
+#define DRS_FIRE 3
+#define DRS_COLD 4
+#define DRS_POIS 5
+#define DRS_NETH 6
+#define DRS_LITE 7
+#define DRS_DARK 8
+#define DRS_FEAR 9
+#define DRS_CONF 10
+#define DRS_CHAOS 11
+#define DRS_DISEN 12
+#define DRS_BLIND 13
+#define DRS_NEXUS 14
+#define DRS_SOUND 15
+#define DRS_SHARD 16
+#define DRS_FREE 30
+#define DRS_MANA 31
+#define DRS_REFLECT 32
+
+
+
+/*
+ * Hack -- first "normal" artifact in the artifact list. All of
+ * the artifacts with indexes from 1 to 15 are "special" (lights,
+ * rings, amulets), and the ones from 16 to 127 are "normal".
+ */
+#define ART_MIN_NORMAL 16
+#define ART_MIN_SPECIAL 200
+
+
+/*
+ * Hack -- special "xtra" object powers
+ */
+
+/* Sustain one stat */
+#define EGO_XTRA_SUSTAIN 1
+
+/* High resist */
+#define EGO_XTRA_POWER 2
+
+/* Special ability */
+#define EGO_XTRA_ABILITY 3
+
+/*** Object flag values ***/
+
+
+/*
+ * Special Object Flags
+ */
+#define IDENT_SENSE 0x01 /* Item has been "sensed" */
+#define IDENT_FIXED 0x02 /* Item has been "haggled" */
+#define IDENT_EMPTY 0x04 /* Item charges are known */
+#define IDENT_KNOWN 0x08 /* Item abilities are known */
+#define IDENT_STOREB 0x10 /* Item is storebought !!!! */
+#define IDENT_MENTAL 0x20 /* Item information is known */
+#define IDENT_CURSED 0x40 /* Item is temporarily cursed */
+
+
+
+/*
+ * Special Monster Flags
+ */
+#define MFLAG_VIEW 0x00000001 /* Monster is in line of sight */
+#define MFLAG_QUEST 0x00000002 /* Monster is subject to a quest */
+#define MFLAG_PARTIAL 0x00000004 /* Monster is a partial summon */
+#define MFLAG_CONTROL 0x00000008 /* Monster is controlled */
+#define MFLAG_BORN 0x00000010 /* Monster is still being born */
+#define MFLAG_NICE 0x00000020 /* Monster is still being nice */
+#define MFLAG_SHOW 0x00000040 /* Monster is recently memorized */
+#define MFLAG_MARK 0x00000080 /* Monster is currently memorized */
+#define MFLAG_NO_DROP 0x00000100 /* Monster wont drop obj/corpse */
+#define MFLAG_QUEST2 0x00000200 /* Monster is subject to a quest */
+#define PERM_MFLAG_MASK ( \
+ MFLAG_QUEST | MFLAG_QUEST2 | \
+ MFLAG_PARTIAL | MFLAG_CONTROL | MFLAG_NO_DROP \
+ )
+
+
+/*
+ * As of 2.7.8, the "object flags" are valid for all objects, and as
+ * of 2.7.9, these flags are not actually stored with the object.
+ *
+ * Note that "flags1" contains all flags dependant on "pval" (including
+ * stat bonuses, but NOT stat sustainers), plus all "extra attack damage"
+ * flags (SLAY_XXX and BRAND_XXX).
+ *
+ * Note that "flags2" contains all "resistances" (including "Stat Sustainers",
+ * actual immunities, and resistances). Note that "Hold Life" is really an
+ * "immunity" to ExpLoss, and "Free Action" is "immunity to paralysis".
+ *
+ * Note that "flags3" contains everything else -- including the three "CURSED"
+ * flags, and the "BLESSED" flag, several "item display" parameters, some new
+ * flags for powerful Bows, and flags which affect the player in a "general"
+ * way (LITE, TELEPATHY, SEE_INVIS, SLOW_DIGEST, REGEN, FEATHER), including
+ * all the "general" curses (TELEPORT, AGGRAVATE, EXP_DRAIN). It also has
+ * four new flags called "ITEM_IGNORE_XXX" which lets an item specify that
+ * it can not be affected by various forms of destruction. This is NOT as
+ * powerful as actually granting resistance/immunity to the wearer.
+ */
+
+#define TR1_STR 0x00000001L /* STR += "pval" */
+#define TR1_INT 0x00000002L /* INT += "pval" */
+#define TR1_WIS 0x00000004L /* WIS += "pval" */
+#define TR1_DEX 0x00000008L /* DEX += "pval" */
+#define TR1_CON 0x00000010L /* CON += "pval" */
+#define TR1_CHR 0x00000020L /* CHR += "pval" */
+#define TR1_MANA 0x00000040L /* Mana multipler */
+#define TR1_SPELL 0x00000080L /* Spell power increase */
+#define TR1_STEALTH 0x00000100L /* Stealth += "pval" */
+#define TR1_SEARCH 0x00000200L /* Search += "pval" */
+#define TR1_INFRA 0x00000400L /* Infra += "pval" */
+#define TR1_TUNNEL 0x00000800L /* Tunnel += "pval" */
+#define TR1_SPEED 0x00001000L /* Speed += "pval" */
+#define TR1_BLOWS 0x00002000L /* Blows += "pval" */
+#define TR1_CHAOTIC 0x00004000L
+#define TR1_VAMPIRIC 0x00008000L
+#define TR1_SLAY_ANIMAL 0x00010000L
+#define TR1_SLAY_EVIL 0x00020000L
+#define TR1_SLAY_UNDEAD 0x00040000L
+#define TR1_SLAY_DEMON 0x00080000L
+#define TR1_SLAY_ORC 0x00100000L
+#define TR1_SLAY_TROLL 0x00200000L
+#define TR1_SLAY_GIANT 0x00400000L
+#define TR1_SLAY_DRAGON 0x00800000L
+#define TR1_KILL_DRAGON 0x01000000L /* Execute Dragon */
+#define TR1_VORPAL 0x02000000L /* Later */
+#define TR1_IMPACT 0x04000000L /* Cause Earthquakes */
+#define TR1_BRAND_POIS 0x08000000L
+#define TR1_BRAND_ACID 0x10000000L
+#define TR1_BRAND_ELEC 0x20000000L
+#define TR1_BRAND_FIRE 0x40000000L
+#define TR1_BRAND_COLD 0x80000000L
+#define TR1_NULL_MASK 0x00000000L
+
+#define TRAP2_AUTOMATIC_5 0x00000001L /* Trap automatically rearms itself, 1 in 5 failure */
+#define TRAP2_AUTOMATIC_99 0x00000002L /* Trap automatically rearms itself */
+#define TRAP2_KILL_GHOST 0x00000004L /* Trap also affects PASS_WALL creatures */
+#define TRAP2_TELEPORT_TO 0x00000008L /* After everything else, teleport to player */
+#define TRAP2_ONLY_DRAGON 0x00000010L /* Affect only dragons & other AFFECTed creatures */
+#define TRAP2_ONLY_DEMON 0x00000020L /* Affect only demons & other AFFECTed creatures */
+#define TRAP2_ONLY_ANIMAL 0x00000100L /* Affect only animals & other AFFECTed creatures */
+#define TRAP2_ONLY_UNDEAD 0x00000200L /* Affect only undead & others */
+#define TRAP2_ONLY_EVIL 0x00000400L /* Affect only evil creatures &c. */
+
+#define TRAP2_ONLY_MASK (TRAP2_ONLY_DRAGON | TRAP2_ONLY_DEMON | TRAP2_ONLY_ANIMAL | \
+ TRAP2_ONLY_UNDEAD | TRAP2_ONLY_EVIL )
+
+#define TR2_SUST_STR 0x00000001L
+#define TR2_SUST_INT 0x00000002L
+#define TR2_SUST_WIS 0x00000004L
+#define TR2_SUST_DEX 0x00000008L
+#define TR2_SUST_CON 0x00000010L
+#define TR2_SUST_CHR 0x00000020L
+#define TR2_INVIS 0x00000040L /* Invisibility */
+#define TR2_LIFE 0x00000080L /* Life multiplier */
+#define TR2_IM_ACID 0x00000100L
+#define TR2_IM_ELEC 0x00000200L
+#define TR2_IM_FIRE 0x00000400L
+#define TR2_IM_COLD 0x00000800L
+#define TR2_SENS_FIRE 0x00001000L /* Sensibility to fire */
+#define TR2_REFLECT 0x00002000L /* Reflect 'bolts' */
+#define TR2_FREE_ACT 0x00004000L /* Free Action */
+#define TR2_HOLD_LIFE 0x00008000L /* Hold Life */
+#define TR2_RES_ACID 0x00010000L
+#define TR2_RES_ELEC 0x00020000L
+#define TR2_RES_FIRE 0x00040000L
+#define TR2_RES_COLD 0x00080000L
+#define TR2_RES_POIS 0x00100000L
+#define TR2_RES_FEAR 0x00200000L
+#define TR2_RES_LITE 0x00400000L
+#define TR2_RES_DARK 0x00800000L
+#define TR2_RES_BLIND 0x01000000L
+#define TR2_RES_CONF 0x02000000L
+#define TR2_RES_SOUND 0x04000000L
+#define TR2_RES_SHARDS 0x08000000L
+#define TR2_RES_NETHER 0x10000000L
+#define TR2_RES_NEXUS 0x20000000L
+#define TR2_RES_CHAOS 0x40000000L
+#define TR2_RES_DISEN 0x80000000L
+#define TR2_NULL_MASK 0x00000000L
+
+#define TR3_SH_FIRE 0x00000001L /* Immolation (Fire) */
+#define TR3_SH_ELEC 0x00000002L /* Electric Sheath */
+#define TR3_AUTO_CURSE 0x00000004L /* The obj will recurse itself */
+#define TR3_DECAY 0x00000008L /* Decay */
+#define TR3_NO_TELE 0x00000010L /* Anti-teleportation */
+#define TR3_NO_MAGIC 0x00000020L /* Anti-magic */
+#define TR3_WRAITH 0x00000040L /* Wraithform */
+#define TR3_TY_CURSE 0x00000080L /* The Ancient Curse */
+#define TR3_EASY_KNOW 0x00000100L /* Aware -> Known */
+#define TR3_HIDE_TYPE 0x00000200L /* Hide "pval" description */
+#define TR3_SHOW_MODS 0x00000400L /* Always show Tohit/Todam */
+#define TR3_INSTA_ART 0x00000800L /* Item must be an artifact */
+#define TR3_FEATHER 0x00001000L /* Feather Falling */
+#define TR3_LITE1 0x00002000L /* lite radius 1 */
+#define TR3_SEE_INVIS 0x00004000L /* See Invisible */
+#define TR3_NORM_ART 0x00008000L /* Artifact in k_info */
+#define TR3_SLOW_DIGEST 0x00010000L /* Item slows down digestion */
+#define TR3_REGEN 0x00020000L /* Item induces regeneration */
+#define TR3_XTRA_MIGHT 0x00040000L /* Bows get extra multiplier */
+#define TR3_XTRA_SHOTS 0x00080000L /* Bows get extra shots */
+#define TR3_IGNORE_ACID 0x00100000L /* Item ignores Acid Damage */
+#define TR3_IGNORE_ELEC 0x00200000L /* Item ignores Elec Damage */
+#define TR3_IGNORE_FIRE 0x00400000L /* Item ignores Fire Damage */
+#define TR3_IGNORE_COLD 0x00800000L /* Item ignores Cold Damage */
+#define TR3_ACTIVATE 0x01000000L /* Item can be activated */
+#define TR3_DRAIN_EXP 0x02000000L /* Item drains Experience */
+#define TR3_TELEPORT 0x04000000L /* Item teleports player */
+#define TR3_AGGRAVATE 0x08000000L /* Item aggravates monsters */
+#define TR3_BLESSED 0x10000000L /* Item is Blessed */
+#define TR3_CURSED 0x20000000L /* Item is Cursed */
+#define TR3_HEAVY_CURSE 0x40000000L /* Item is Heavily Cursed */
+#define TR3_PERMA_CURSE 0x80000000L /* Item is Perma Cursed */
+#define TR3_NULL_MASK 0x00000000L
+
+
+#define TR4_NEVER_BLOW 0x00000001L /* Weapon can't attack */
+#define TR4_PRECOGNITION 0x00000002L /* Like activating the cheat mode */
+#define TR4_BLACK_BREATH 0x00000004L /* Tolkien's Black Breath */
+#define TR4_RECHARGE 0x00000008L /* For artifact Wands and Staffs */
+#define TR4_FLY 0x00000010L /* This one and ONLY this one allow you to fly over trees */
+#define TR4_DG_CURSE 0x00000020L /* The Ancient Morgothian Curse */
+#define TR4_COULD2H 0x00000040L /* Can wield it 2 Handed */
+#define TR4_MUST2H 0x00000080L /* Must wield it 2 Handed */
+#define TR4_LEVELS 0x00000100L /* Can gain exp/exp levels !! */
+#define TR4_CLONE 0x00000200L /* Can clone monsters */
+#define TR4_SPECIAL_GENE 0x00000400L /* The object can only be generated in special conditions like quests, special dungeons, ... */
+#define TR4_CLIMB 0x00000800L /* Allow climbing mountains */
+#define TR4_FAST_CAST 0x00001000L /* Rod is x2 time faster to use */
+#define TR4_CAPACITY 0x00002000L /* Rod can take x2 mana */
+#define TR4_CHARGING 0x00004000L /* Rod recharge faster */
+#define TR4_CHEAPNESS 0x00008000L /* Rod spells are cheaper(in mana cost) to cast */
+#define TR4_FOUNTAIN 0x00010000L /* Available as fountain (for potions) */
+#define TR4_ANTIMAGIC_50 0x00020000L /* Forbid magic */
+#define TR4_ANTIMAGIC_30 0x00040000L /* Forbid magic */
+#define TR4_ANTIMAGIC_20 0x00080000L /* Forbid magic */
+#define TR4_ANTIMAGIC_10 0x00100000L /* Forbid magic */
+#define TR4_EASY_USE 0x00200000L /* Easily activable */
+#define TR4_IM_NETHER 0x00400000L /* Immunity to nether */
+#define TR4_RECHARGED 0x00800000L /* Object has been recharged once */
+#define TR4_ULTIMATE 0x01000000L /* ULTIMATE artifact */
+#define TR4_AUTO_ID 0x02000000L /* Id stuff on floor */
+#define TR4_LITE2 0x04000000L /* lite radius 2 */
+#define TR4_LITE3 0x08000000L /* lite radius 3 */
+#define TR4_FUEL_LITE 0x10000000L /* fuelable lite */
+#define TR4_ART_EXP 0x20000000L /* Will accumulate xp */
+#define TR4_CURSE_NO_DROP 0x40000000L /* The obj wont be dropped */
+#define TR4_NO_RECHARGE 0x80000000L /* Object Cannot be recharged */
+#define TR4_NULL_MASK 0xFFFFFFFCL
+
+#define TR5_TEMPORARY 0x00000001L /* In timeout turns it is destroyed */
+#define TR5_DRAIN_MANA 0x00000002L /* Drains mana */
+#define TR5_DRAIN_HP 0x00000004L /* Drains hp */
+#define TR5_KILL_DEMON 0x00000008L /* Execute Demon */
+#define TR5_KILL_UNDEAD 0x00000010L /* Execute Undead */
+#define TR5_CRIT 0x00000020L /* More critical hits */
+#define TR5_ATTR_MULTI 0x00000040L /* Object shimmer -- only allowed in k_info */
+#define TR5_WOUNDING 0x00000080L /* Wounds monsters */
+#define TR5_FULL_NAME 0x00000100L /* Uses direct name from k_info */
+#define TR5_LUCK 0x00000200L /* Luck += pval */
+#define TR5_IMMOVABLE 0x00000400L /* Cannot move */
+#define TR5_SPELL_CONTAIN 0x00000800L /* Can contain a spell */
+#define TR5_RES_MORGUL 0x00001000L /* Is not shattered by morgul fiends(nazguls) */
+#define TR5_ACTIVATE_NO_WIELD 0x00002000L /* Can be 'A'ctivated without being wielded */
+#define TR5_MAGIC_BREATH 0x00004000L /* Can breath anywere */
+#define TR5_WATER_BREATH 0x00008000L /* Can breath underwater */
+#define TR5_WIELD_CAST 0x00010000L /* Need to be wielded to cast spelsl fomr it(if it can be wiekded) */
+
+/* ESP defines */
+#define ESP_ORC 0x00000001L
+#define ESP_TROLL 0x00000002L
+#define ESP_DRAGON 0x00000004L
+#define ESP_GIANT 0x00000008L
+#define ESP_DEMON 0x00000010L
+#define ESP_UNDEAD 0x00000020L
+#define ESP_EVIL 0x00000040L
+#define ESP_ANIMAL 0x00000080L
+#define ESP_THUNDERLORD 0x00000100L
+#define ESP_GOOD 0x00000200L
+#define ESP_NONLIVING 0x00000400L
+#define ESP_UNIQUE 0x00000800L
+#define ESP_SPIDER 0x00001000L
+#define ESP_ALL 0x80000000L
+
+/* Number of group of flags to choose from */
+#define MAX_FLAG_GROUP 12
+#define NEW_GROUP_CHANCE 40 /* Chance to get a new group */
+
+/*
+ * Hack masks for "pval-dependant" flags.
+ */
+#define TR1_PVAL_MASK \
+ (TR1_STR | TR1_INT | TR1_WIS | TR1_DEX | \
+ TR1_CON | TR1_CHR | \
+ TR1_STEALTH | TR1_SEARCH | TR1_INFRA | TR1_TUNNEL | \
+ TR1_SPEED | TR1_BLOWS | TR1_SPELL)
+
+#define TR5_PVAL_MASK \
+ (TR5_CRIT | TR5_LUCK)
+
+
+/*** Ego flags ***/
+#define ETR4_SUSTAIN 0x00000001L /* Ego-Item gives a Random Sustain */
+#define ETR4_OLD_RESIST 0x00000002L /* The old "extra power" random high resist */
+#define ETR4_ABILITY 0x00000004L /* Ego-Item has a random Sustain */
+#define ETR4_R_ELEM 0x00000008L /* Item resists Acid/Fire/Cold/Elec or Poison */
+#define ETR4_R_LOW 0x00000010L /* Item has a random low resist */
+#define ETR4_R_HIGH 0x00000020L /* Item has a random high resist */
+#define ETR4_R_ANY 0x00000040L /* Item has one additional resist */
+#define ETR4_R_DRAGON 0x00000080L /* Item gets "Dragon" Resist */
+#define ETR4_SLAY_WEAP 0x00000100L /* Special 'Slaying' bonus */
+#define ETR4_DAM_DIE 0x00000200L /* Item has an additional dam die */
+#define ETR4_DAM_SIZE 0x00000400L /* Item has greater damage dice */
+#define ETR4_PVAL_M1 0x00000800L /* Item has +1 to pval */
+#define ETR4_PVAL_M2 0x00001000L /* Item has +(up to 2) to pval */
+#define ETR4_PVAL_M3 0x00002000L /* Item has +(up to 3) to pval */
+#define ETR4_PVAL_M5 0x00004000L /* Item has +(up to 5) to pval */
+#define ETR4_AC_M1 0x00008000L /* Item has +1 to AC */
+#define ETR4_AC_M2 0x00010000L /* Item has +(up to 2) to AC */
+#define ETR4_AC_M3 0x00020000L /* Item has +(up to 3) to AC */
+#define ETR4_AC_M5 0x00040000L /* Item has +(up to 5) to AC */
+#define ETR4_TH_M1 0x00080000L /* Item has +1 to hit */
+#define ETR4_TH_M2 0x00100000L /* Item has +(up to 2) to hit */
+#define ETR4_TH_M3 0x00200000L /* Item has +(up to 3) to hit */
+#define ETR4_TH_M5 0x00400000L /* Item has +(up to 5) to hit */
+#define ETR4_TD_M1 0x00800000L /* Item has +1 to dam */
+#define ETR4_TD_M2 0x01000000L /* Item has +(up to 2) to dam */
+#define ETR4_TD_M3 0x02000000L /* Item has +(up to 3) to dam */
+#define ETR4_TD_M5 0x04000000L /* Item has +(up to 5) to dam */
+#define ETR4_R_P_ABILITY 0x08000000L /* Item has a random pval-affected ability */
+#define ETR4_R_STAT 0x10000000L /* Item affects a random stat */
+#define ETR4_R_STAT_SUST 0x20000000L /* Item affects a random stat & sustains it */
+#define ETR4_R_IMMUNITY 0x40000000L /* Item gives a random immunity */
+#define ETR4_LIMIT_BLOWS 0x80000000L /* switch the "limit blows" feature */
+
+/*** Features flags -- DG ***/
+#define FF1_NO_WALK 0x00000001L
+#define FF1_NO_VISION 0x00000002L
+#define FF1_CAN_LEVITATE 0x00000004L
+#define FF1_CAN_PASS 0x00000008L
+#define FF1_FLOOR 0x00000010L
+#define FF1_WALL 0x00000020L
+#define FF1_PERMANENT 0x00000040L
+#define FF1_CAN_FLY 0x00000080L
+#define FF1_REMEMBER 0x00000100L
+#define FF1_NOTICE 0x00000200L
+#define FF1_DONT_NOTICE_RUNNING 0x00000400L
+#define FF1_CAN_RUN 0x00000800L
+#define FF1_DOOR 0x00001000L
+#define FF1_SUPPORT_LIGHT 0x00002000L
+#define FF1_CAN_CLIMB 0x00004000L
+#define FF1_TUNNELABLE 0x00008000L
+#define FF1_WEB 0x00010000L
+#define FF1_ATTR_MULTI 0x00020000L
+#define FF1_SUPPORT_GROWTH 0x00040000L
+
+/*** Dungeon type flags -- DG ***/
+#define DF1_PRINCIPAL 0x00000001L /* Is a principal dungeon */
+#define DF1_MAZE 0x00000002L /* Is a maze-type dungeon */
+#define DF1_SMALLEST 0x00000004L /* Creates VERY small levels like The Maze */
+#define DF1_SMALL 0x00000008L /* Creates small levels like Dol Goldor */
+#define DF1_BIG 0x00000010L /* Creates big levels like Moria, and Angband dungeons */
+#define DF1_NO_DOORS 0x00000020L /* No doors on rooms, like Barrowdowns, Old Forest etc) */
+#define DF1_WATER_RIVER 0x00000040L /* Allow a single water streamer on a level */
+#define DF1_LAVA_RIVER 0x00000080L /* Allow a single lava streamer on a level */
+#define DF1_WATER_RIVERS 0x00000100L /* Allow multiple water streamers on a level */
+#define DF1_LAVA_RIVERS 0x00000200L /* Allow multiple lava streamers on a level */
+#define DF1_CAVE 0x00000400L /* Allow rooms */
+#define DF1_CAVERN 0x00000800L /* Allow cavern rooms */
+#define DF1_NO_UP 0x00001000L /* Disallow up stairs */
+#define DF1_HOT 0x00002000L /* Corpses on ground and in pack decay quicker through heat */
+#define DF1_COLD 0x00004000L /* Corpses on ground and in pack decay quicker through cold */
+#define DF1_FORCE_DOWN 0x00008000L /* No up stairs generated */
+#define DF1_FORGET 0x00010000L /* Features are forgotten, like the Maze and Illusory Castle */
+#define DF1_NO_DESTROY 0x00020000L /* No destroyed levels in dungeon */
+#define DF1_SAND_VEIN 0x00040000L /* Like in the sandworm lair */
+#define DF1_CIRCULAR_ROOMS 0x00080000L /* Allow circular rooms */
+#define DF1_EMPTY 0x00100000L /* Allow arena levels */
+#define DF1_DAMAGE_FEAT 0x00200000L
+#define DF1_FLAT 0x00400000L /* Creates paths to next areas at edge of level, like Barrowdowns */
+#define DF1_TOWER 0x00800000L /* You start at bottom and go up rather than the reverse */
+#define DF1_RANDOM_TOWNS 0x01000000L /* Allow random towns */
+#define DF1_DOUBLE 0x02000000L /* Creates double-walled dungeon like Helcaraxe and Erebor */
+#define DF1_LIFE_LEVEL 0x04000000L /* Creates dungeon level on modified 'game of life' algorithm */
+#define DF1_EVOLVE 0x08000000L /* Evolving, pulsing levels like Heart of the Earth */
+#define DF1_ADJUST_LEVEL_1 0x10000000L /* Minimum monster level will be equal to dungeon level */
+#define DF1_ADJUST_LEVEL_2 0x20000000L /* Minimum monster level will be double the dungeon level */
+#define DF1_NO_RECALL 0x40000000L /* No recall allowed */
+#define DF1_NO_STREAMERS 0x80000000L /* No streamers */
+
+#define DF2_ADJUST_LEVEL_1_2 0x00000001L /* Minimum monster level will be half the dungeon level */
+#define DF2_NO_SHAFT 0x00000002L /* No shafts */
+#define DF2_ADJUST_LEVEL_PLAYER 0x00000004L /* Uses player level*2 instead of dungeon level for other ADJUST_LEVEL flags */
+#define DF2_NO_TELEPORT 0x00000008L
+#define DF2_ASK_LEAVE 0x00000010L
+#define DF2_NO_STAIR 0x00000020L
+#define DF2_SPECIAL 0x00000040L
+#define DF2_NO_NEW_MONSTER 0x00000080L
+#define DF2_DESC 0x00000100L
+#define DF2_NO_GENO 0x00000200L
+#define DF2_NO_BREATH 0x00000400L /* Oups, cannot breath here */
+#define DF2_WATER_BREATH 0x00000800L /* Oups, cannot breath here, need water breathing */
+#define DF2_ELVEN 0x00001000L /* Try to create elven monster ego */
+#define DF2_DWARVEN 0x00002000L /* Try to create dwarven monster ego */
+#define DF2_NO_EASY_MOVE 0x00004000L /* Forbid stuff like teleport level, probability travel, ... */
+#define DF2_NO_RECALL_OUT 0x00008000L /* Cannot recall out of the place */
+#define DF2_DESC_ALWAYS 0x00010000L /* Always shows the desc */
+
+/*** Town flags ***/
+#define TOWN_REAL 0x01 /* Town is really present */
+#define TOWN_KNOWN 0x02 /* Town is found by the player */
+
+
+
+/*** Monster blow constants ***/
+
+#define MODIFY_AUX(o, n) ((o) = modify_aux((o), (n) >> 2, (n) & 3))
+#define MODIFY(o, n, min) MODIFY_AUX(o, n); (o) = ((o) < (min))?(min):(o)
+
+/*
+ * New monster blow methods
+ */
+#define RBM_ANY 0
+#define RBM_HIT 1
+#define RBM_TOUCH 2
+#define RBM_PUNCH 3
+#define RBM_KICK 4
+#define RBM_CLAW 5
+#define RBM_BITE 6
+#define RBM_STING 7
+#define RBM_XXX1 8
+#define RBM_BUTT 9
+#define RBM_CRUSH 10
+#define RBM_ENGULF 11
+#define RBM_CHARGE 12
+#define RBM_CRAWL 13
+#define RBM_DROOL 14
+#define RBM_SPIT 15
+#define RBM_EXPLODE 16
+#define RBM_GAZE 17
+#define RBM_WAIL 18
+#define RBM_SPORE 19
+#define RBM_XXX4 20
+#define RBM_BEG 21
+#define RBM_INSULT 22
+#define RBM_MOAN 23
+#define RBM_SHOW 24
+
+
+/*
+ * New monster blow effects
+ */
+#define RBE_ANY 0
+#define RBE_HURT 1
+#define RBE_POISON 2
+#define RBE_UN_BONUS 3
+#define RBE_UN_POWER 4
+#define RBE_EAT_GOLD 5
+#define RBE_EAT_ITEM 6
+#define RBE_EAT_FOOD 7
+#define RBE_EAT_LITE 8
+#define RBE_ACID 9
+#define RBE_ELEC 10
+#define RBE_FIRE 11
+#define RBE_COLD 12
+#define RBE_BLIND 13
+#define RBE_CONFUSE 14
+#define RBE_TERRIFY 15
+#define RBE_PARALYZE 16
+#define RBE_LOSE_STR 17
+#define RBE_LOSE_INT 18
+#define RBE_LOSE_WIS 19
+#define RBE_LOSE_DEX 20
+#define RBE_LOSE_CON 21
+#define RBE_LOSE_CHR 22
+#define RBE_LOSE_ALL 23
+#define RBE_SHATTER 24
+#define RBE_EXP_10 25
+#define RBE_EXP_20 26
+#define RBE_EXP_40 27
+#define RBE_EXP_80 28
+#define RBE_DISEASE 29
+#define RBE_TIME 30
+#define RBE_SANITY 31
+#define RBE_HALLU 32
+#define RBE_PARASITE 33
+#define RBE_ABOMINATION 34
+
+
+/*** Monster flag values (hard-coded) ***/
+
+#define MONSTER_LEVEL_MAX 150
+#define MONSTER_EXP(level) ((((level) > MONSTER_LEVEL_MAX)?MONSTER_LEVEL_MAX:(level)) * (((level) > MONSTER_LEVEL_MAX)?MONSTER_LEVEL_MAX:(level)) * (((level) > MONSTER_LEVEL_MAX)?MONSTER_LEVEL_MAX:(level)) * 6)
+
+/*
+ * New monster race bit flags
+ */
+#define RF1_UNIQUE 0x00000001 /* Unique Monster */
+#define RF1_QUESTOR 0x00000002 /* Quest Monster */
+#define RF1_MALE 0x00000004 /* Male gender */
+#define RF1_FEMALE 0x00000008 /* Female gender */
+#define RF1_CHAR_CLEAR 0x00000010 /* Absorbs symbol */
+#define RF1_CHAR_MULTI 0x00000020 /* Changes symbol */
+#define RF1_ATTR_CLEAR 0x00000040 /* Absorbs color */
+#define RF1_ATTR_MULTI 0x00000080 /* Changes color */
+#define RF1_FORCE_DEPTH 0x00000100 /* Start at "correct" depth */
+#define RF1_FORCE_MAXHP 0x00000200 /* Start with max hitpoints */
+#define RF1_FORCE_SLEEP 0x00000400 /* Start out sleeping */
+#define RF1_FORCE_EXTRA 0x00000800 /* Start out something */
+#define RF1_FRIEND 0x00001000 /* Arrive with a friend */
+#define RF1_FRIENDS 0x00002000 /* Arrive with some friends */
+#define RF1_ESCORT 0x00004000 /* Arrive with an escort */
+#define RF1_ESCORTS 0x00008000 /* Arrive with some escorts */
+#define RF1_NEVER_BLOW 0x00010000 /* Never make physical blow */
+#define RF1_NEVER_MOVE 0x00020000 /* Never make physical move */
+#define RF1_RAND_25 0x00040000 /* Moves randomly (25%) */
+#define RF1_RAND_50 0x00080000 /* Moves randomly (50%) */
+#define RF1_ONLY_GOLD 0x00100000 /* Drop only gold */
+#define RF1_ONLY_ITEM 0x00200000 /* Drop only items */
+#define RF1_DROP_60 0x00400000 /* Drop an item/gold (60%) */
+#define RF1_DROP_90 0x00800000 /* Drop an item/gold (90%) */
+#define RF1_DROP_1D2 0x01000000 /* Drop 1d2 items/gold */
+#define RF1_DROP_2D2 0x02000000 /* Drop 2d2 items/gold */
+#define RF1_DROP_3D2 0x04000000 /* Drop 3d2 items/gold */
+#define RF1_DROP_4D2 0x08000000 /* Drop 4d2 items/gold */
+#define RF1_DROP_GOOD 0x10000000 /* Drop good items */
+#define RF1_DROP_GREAT 0x20000000 /* Drop great items */
+#define RF1_DROP_USEFUL 0x40000000 /* Drop "useful" items */
+#define RF1_DROP_CHOSEN 0x80000000 /* Drop "chosen" items */
+
+/*
+ * New monster race bit flags
+ */
+#define RF2_STUPID 0x00000001 /* Monster is stupid */
+#define RF2_SMART 0x00000002 /* Monster is smart */
+#define RF2_CAN_SPEAK 0x00000004 /* TY: can speak */
+#define RF2_REFLECTING 0x00000008 /* Reflects bolts */
+#define RF2_INVISIBLE 0x00000010 /* Monster avoids vision */
+#define RF2_COLD_BLOOD 0x00000020 /* Monster avoids infra */
+#define RF2_EMPTY_MIND 0x00000040 /* Monster avoids telepathy */
+#define RF2_WEIRD_MIND 0x00000080 /* Monster avoids telepathy? */
+#define RF2_DEATH_ORB 0x00000100 /* Death Orb */
+#define RF2_REGENERATE 0x00000200 /* Monster regenerates */
+#define RF2_SHAPECHANGER 0x00000400 /* TY: shapechanger */
+#define RF2_ATTR_ANY 0x00000800 /* TY: Attr_any */
+#define RF2_POWERFUL 0x00001000 /* Monster has strong breath */
+#define RF2_ELDRITCH_HORROR 0x00002000 /* Sanity-blasting horror */
+#define RF2_AURA_FIRE 0x00004000 /* Burns in melee */
+#define RF2_AURA_ELEC 0x00008000 /* Shocks in melee */
+#define RF2_OPEN_DOOR 0x00010000 /* Monster can open doors */
+#define RF2_BASH_DOOR 0x00020000 /* Monster can bash doors */
+#define RF2_PASS_WALL 0x00040000 /* Monster can pass walls */
+#define RF2_KILL_WALL 0x00080000 /* Monster can destroy walls */
+#define RF2_MOVE_BODY 0x00100000 /* Monster can move monsters */
+#define RF2_KILL_BODY 0x00200000 /* Monster can kill monsters */
+#define RF2_TAKE_ITEM 0x00400000 /* Monster can pick up items */
+#define RF2_KILL_ITEM 0x00800000 /* Monster can crush items */
+#define RF2_BRAIN_1 0x01000000
+#define RF2_BRAIN_2 0x02000000
+#define RF2_BRAIN_3 0x04000000
+#define RF2_BRAIN_4 0x08000000
+#define RF2_BRAIN_5 0x10000000
+#define RF2_BRAIN_6 0x20000000
+#define RF2_BRAIN_7 0x40000000
+#define RF2_BRAIN_8 0x80000000
+
+/*
+ * New monster race bit flags
+ */
+#define RF3_ORC 0x00000001 /* Orc */
+#define RF3_TROLL 0x00000002 /* Troll */
+#define RF3_GIANT 0x00000004 /* Giant */
+#define RF3_DRAGON 0x00000008 /* Dragon */
+#define RF3_DEMON 0x00000010 /* Demon */
+#define RF3_UNDEAD 0x00000020 /* Undead */
+#define RF3_EVIL 0x00000040 /* Evil */
+#define RF3_ANIMAL 0x00000080 /* Animal */
+#define RF3_THUNDERLORD 0x00000100 /* DG: Thunderlord */
+#define RF3_GOOD 0x00000200 /* Good */
+#define RF3_AURA_COLD 0x00000400 /* Freezes in melee */
+#define RF3_NONLIVING 0x00000800 /* TY: Non-Living (?) */
+#define RF3_HURT_LITE 0x00001000 /* Hurt by lite */
+#define RF3_HURT_ROCK 0x00002000 /* Hurt by rock remover */
+#define RF3_SUSCEP_FIRE 0x00004000 /* Hurt badly by fire */
+#define RF3_SUSCEP_COLD 0x00008000 /* Hurt badly by cold */
+#define RF3_IM_ACID 0x00010000 /* Resist acid a lot */
+#define RF3_IM_ELEC 0x00020000 /* Resist elec a lot */
+#define RF3_IM_FIRE 0x00040000 /* Resist fire a lot */
+#define RF3_IM_COLD 0x00080000 /* Resist cold a lot */
+#define RF3_IM_POIS 0x00100000 /* Resist poison a lot */
+#define RF3_RES_TELE 0x00200000 /* Resist teleportation */
+#define RF3_RES_NETH 0x00400000 /* Resist nether a lot */
+#define RF3_RES_WATE 0x00800000 /* Resist water */
+#define RF3_RES_PLAS 0x01000000 /* Resist plasma */
+#define RF3_RES_NEXU 0x02000000 /* Resist nexus */
+#define RF3_RES_DISE 0x04000000 /* Resist disenchantment */
+#define RF3_UNIQUE_4 0x08000000 /* Is a "Nazgul" unique */
+#define RF3_NO_FEAR 0x10000000 /* Cannot be scared */
+#define RF3_NO_STUN 0x20000000 /* Cannot be stunned */
+#define RF3_NO_CONF 0x40000000 /* Cannot be confused */
+#define RF3_NO_SLEEP 0x80000000 /* Cannot be slept */
+
+/*
+ * New monster race bit flags
+ */
+#define RF4_SHRIEK 0x00000001 /* Shriek for help */
+#define RF4_MULTIPLY 0x00000002 /* Monster reproduces */
+#define RF4_S_ANIMAL 0x00000004 /* Summon animals */
+#define RF4_ROCKET 0x00000008 /* TY: Rocket */
+#define RF4_ARROW_1 0x00000010 /* Fire an arrow (light) */
+#define RF4_ARROW_2 0x00000020 /* Fire an arrow (heavy) */
+#define RF4_ARROW_3 0x00000040 /* Fire missiles (light) */
+#define RF4_ARROW_4 0x00000080 /* Fire missiles (heavy) */
+#define RF4_BR_ACID 0x00000100 /* Breathe Acid */
+#define RF4_BR_ELEC 0x00000200 /* Breathe Elec */
+#define RF4_BR_FIRE 0x00000400 /* Breathe Fire */
+#define RF4_BR_COLD 0x00000800 /* Breathe Cold */
+#define RF4_BR_POIS 0x00001000 /* Breathe Poison */
+#define RF4_BR_NETH 0x00002000 /* Breathe Nether */
+#define RF4_BR_LITE 0x00004000 /* Breathe Lite */
+#define RF4_BR_DARK 0x00008000 /* Breathe Dark */
+#define RF4_BR_CONF 0x00010000 /* Breathe Confusion */
+#define RF4_BR_SOUN 0x00020000 /* Breathe Sound */
+#define RF4_BR_CHAO 0x00040000 /* Breathe Chaos */
+#define RF4_BR_DISE 0x00080000 /* Breathe Disenchant */
+#define RF4_BR_NEXU 0x00100000 /* Breathe Nexus */
+#define RF4_BR_TIME 0x00200000 /* Breathe Time */
+#define RF4_BR_INER 0x00400000 /* Breathe Inertia */
+#define RF4_BR_GRAV 0x00800000 /* Breathe Gravity */
+#define RF4_BR_SHAR 0x01000000 /* Breathe Shards */
+#define RF4_BR_PLAS 0x02000000 /* Breathe Plasma */
+#define RF4_BR_WALL 0x04000000 /* Breathe Force */
+#define RF4_BR_MANA 0x08000000 /* Breathe Mana */
+#define RF4_BA_NUKE 0x10000000 /* TY: Nuke Ball */
+#define RF4_BR_NUKE 0x20000000 /* TY: Toxic Breath */
+#define RF4_BA_CHAO 0x40000000 /* Chaos Ball */
+#define RF4_BR_DISI 0x80000000 /* Breathe Disintegration */
+
+/*
+ * New monster race bit flags
+ */
+#define RF5_BA_ACID 0x00000001 /* Acid Ball */
+#define RF5_BA_ELEC 0x00000002 /* Elec Ball */
+#define RF5_BA_FIRE 0x00000004 /* Fire Ball */
+#define RF5_BA_COLD 0x00000008 /* Cold Ball */
+#define RF5_BA_POIS 0x00000010 /* Poison Ball */
+#define RF5_BA_NETH 0x00000020 /* Nether Ball */
+#define RF5_BA_WATE 0x00000040 /* Water Ball */
+#define RF5_BA_MANA 0x00000080 /* Mana Storm */
+#define RF5_BA_DARK 0x00000100 /* Darkness Storm */
+#define RF5_DRAIN_MANA 0x00000200 /* Drain Mana */
+#define RF5_MIND_BLAST 0x00000400 /* Blast Mind */
+#define RF5_BRAIN_SMASH 0x00000800 /* Smash Brain */
+#define RF5_CAUSE_1 0x00001000 /* Cause Light Wound */
+#define RF5_CAUSE_2 0x00002000 /* Cause Serious Wound */
+#define RF5_CAUSE_3 0x00004000 /* Cause Critical Wound */
+#define RF5_CAUSE_4 0x00008000 /* Cause Mortal Wound */
+#define RF5_BO_ACID 0x00010000 /* Acid Bolt */
+#define RF5_BO_ELEC 0x00020000 /* Elec Bolt (unused) */
+#define RF5_BO_FIRE 0x00040000 /* Fire Bolt */
+#define RF5_BO_COLD 0x00080000 /* Cold Bolt */
+#define RF5_BO_POIS 0x00100000 /* Poison Bolt (unused) */
+#define RF5_BO_NETH 0x00200000 /* Nether Bolt */
+#define RF5_BO_WATE 0x00400000 /* Water Bolt */
+#define RF5_BO_MANA 0x00800000 /* Mana Bolt */
+#define RF5_BO_PLAS 0x01000000 /* Plasma Bolt */
+#define RF5_BO_ICEE 0x02000000 /* Ice Bolt */
+#define RF5_MISSILE 0x04000000 /* Magic Missile */
+#define RF5_SCARE 0x08000000 /* Frighten Player */
+#define RF5_BLIND 0x10000000 /* Blind Player */
+#define RF5_CONF 0x20000000 /* Confuse Player */
+#define RF5_SLOW 0x40000000 /* Slow Player */
+#define RF5_HOLD 0x80000000 /* Paralyze Player */
+
+/*
+ * New monster race bit flags
+ */
+#define RF6_HASTE 0x00000001 /* Speed self */
+#define RF6_HAND_DOOM 0x00000002 /* Hand of Doom */
+#define RF6_HEAL 0x00000004 /* Heal self */
+#define RF6_S_ANIMALS 0x00000008 /* Summon animals */
+#define RF6_BLINK 0x00000010 /* Teleport Short */
+#define RF6_TPORT 0x00000020 /* Teleport Long */
+#define RF6_TELE_TO 0x00000040 /* Move player to monster */
+#define RF6_TELE_AWAY 0x00000080 /* Move player far away */
+#define RF6_TELE_LEVEL 0x00000100 /* Move player vertically */
+#define RF6_DARKNESS 0x00000200 /* Create Darkness */
+#define RF6_TRAPS 0x00000400 /* Create Traps */
+#define RF6_FORGET 0x00000800 /* Cause amnesia */
+#define RF6_RAISE_DEAD 0x00001000 /* Raise Dead */
+#define RF6_S_BUG 0x00002000 /* Summon Software bug */
+#define RF6_S_RNG 0x00004000 /* Summon RNG */
+#define RF6_S_THUNDERLORD 0x00008000 /* Summon Thunderlords */
+#define RF6_S_KIN 0x00010000 /* Summon "kin" */
+#define RF6_S_HI_DEMON 0x00020000 /* Summon greater demons! */
+#define RF6_S_MONSTER 0x00040000 /* Summon Monster */
+#define RF6_S_MONSTERS 0x00080000 /* Summon Monsters */
+#define RF6_S_ANT 0x00100000 /* Summon Ants */
+#define RF6_S_SPIDER 0x00200000 /* Summon Spiders */
+#define RF6_S_HOUND 0x00400000 /* Summon Hounds */
+#define RF6_S_HYDRA 0x00800000 /* Summon Hydras */
+#define RF6_S_ANGEL 0x01000000 /* Summon Angel */
+#define RF6_S_DEMON 0x02000000 /* Summon Demon */
+#define RF6_S_UNDEAD 0x04000000 /* Summon Undead */
+#define RF6_S_DRAGON 0x08000000 /* Summon Dragon */
+#define RF6_S_HI_UNDEAD 0x10000000 /* Summon Greater Undead */
+#define RF6_S_HI_DRAGON 0x20000000 /* Summon Ancient Dragon */
+#define RF6_S_WRAITH 0x40000000 /* Summon Unique Wraith */
+#define RF6_S_UNIQUE 0x80000000 /* Summon Unique Monster */
+
+/*
+ * New monster race bit flags
+ */
+#define RF7_AQUATIC 0x00000001 /* Aquatic monster */
+#define RF7_CAN_SWIM 0x00000002 /* Monster can swim */
+#define RF7_CAN_FLY 0x00000004 /* Monster can fly */
+#define RF7_FRIENDLY 0x00000008 /* Monster is friendly */
+#define RF7_PET 0x00000010 /* Monster is a pet */
+#define RF7_MORTAL 0x00000020 /* Monster is a mortal being */
+#define RF7_SPIDER 0x00000040 /* Monster is a spider (can pass webs) */
+#define RF7_NAZGUL 0x00000080 /* Monster is a Nazgul */
+#define RF7_DG_CURSE 0x00000100 /* If killed the monster grant a DG Curse to the player */
+#define RF7_POSSESSOR 0x00000200 /* Is it a dreaded possessor monster ? */
+#define RF7_NO_DEATH 0x00000400 /* Cannot be killed */
+#define RF7_NO_TARGET 0x00000800 /* Cannot be targeted */
+#define RF7_AI_ANNOY 0x00001000 /* Try to tease the player */
+#define RF7_AI_SPECIAL 0x00002000 /* For quests */
+#define RF7_NEUTRAL 0x00004000 /* Monster is neutral */
+#define RF7_DROP_ART 0x00008000 /* Monster drop one art */
+#define RF7_DROP_RANDART 0x00010000 /* Monster drop one randart */
+#define RF7_AI_PLAYER 0x00020000 /* Controlled by the player */
+#define RF7_NO_THEFT 0x00040000 /* Monster is immune to theft */
+#define RF7_SPIRIT 0x00080000 /* This is a Spirit, coming from the Void */
+#define RF7_IM_MELEE 0x00100000 /* IM melee */
+
+
+/*
+ * Monster race flags
+ */
+#define RF8_DUNGEON 0x00000001
+#define RF8_WILD_TOWN 0x00000002
+#define RF8_XXX8X02 0x00000004
+#define RF8_WILD_SHORE 0x00000008
+#define RF8_WILD_OCEAN 0x00000010
+#define RF8_WILD_WASTE 0x00000020
+#define RF8_WILD_WOOD 0x00000040
+#define RF8_WILD_VOLCANO 0x00000080
+#define RF8_XXX8X08 0x00000100
+#define RF8_WILD_MOUNTAIN 0x00000200
+#define RF8_WILD_GRASS 0x00000400
+#define RF8_NO_CUT 0x00000800
+#define RF8_CTHANGBAND 0x00001000 /* Not used in ToME */
+/* XXX */
+#define RF8_ZANGBAND 0x00004000 /* Not used in ToME */
+#define RF8_JOKEANGBAND 0x00008000
+#define RF8_ANGBAND 0x00010000
+
+#define RF8_WILD_TOO 0x80000000
+
+
+/*
+ * Monster race flags
+ */
+#define RF9_DROP_CORPSE 0x00000001
+#define RF9_DROP_SKELETON 0x00000002
+#define RF9_HAS_LITE 0x00000004 /* Carries a lite */
+#define RF9_MIMIC 0x00000008 /* *REALLY* looks like an object ... only nastier */
+#define RF9_HAS_EGG 0x00000010 /* Can be monster's eggs */
+#define RF9_IMPRESED 0x00000020 /* The monster can follow you on each level until he dies */
+#define RF9_SUSCEP_ACID 0x00000040 /* Susceptible to acid */
+#define RF9_SUSCEP_ELEC 0x00000080 /* Susceptible to lightning */
+#define RF9_SUSCEP_POIS 0x00000100 /* Susceptible to poison */
+#define RF9_KILL_TREES 0x00000200 /* Monster can eat trees */
+#define RF9_WYRM_PROTECT 0x00000400 /* The monster is protected by great wyrms of power: They'll be summoned if it's killed */
+#define RF9_DOPPLEGANGER 0x00000800 /* The monster looks like you */
+#define RF9_ONLY_DEPTH 0x00001000 /* The monster can only be generated at the GIVEN depth */
+#define RF9_SPECIAL_GENE 0x00002000 /* The monster can only be generated in special conditions like quests, special dungeons, ... */
+#define RF9_NEVER_GENE 0x00004000 /* The monster cannot be normaly generated */
+
+
+/*
+ * Hack -- choose "intelligent" spells when desperate
+ */
+
+#define RF4_INT_MASK \
+ (RF4_S_ANIMAL)
+
+#define RF5_INT_MASK \
+ (RF5_HOLD | RF5_SLOW | RF5_CONF | RF5_BLIND | RF5_SCARE)
+
+#define RF6_INT_MASK \
+ (RF6_BLINK | RF6_TPORT | RF6_TELE_LEVEL | RF6_TELE_AWAY | \
+ RF6_HEAL | RF6_HASTE | RF6_TRAPS | \
+ RF6_S_KIN | RF6_S_HI_DEMON | RF6_S_MONSTER | RF6_S_MONSTERS | \
+ RF6_S_ANT | RF6_S_SPIDER | RF6_S_HOUND | RF6_S_HYDRA | \
+ RF6_S_ANGEL | RF6_S_DRAGON | RF6_S_UNDEAD | RF6_S_DEMON | \
+ RF6_S_HI_DRAGON | RF6_S_HI_UNDEAD | RF6_S_WRAITH | RF6_S_UNIQUE | \
+ RF6_S_THUNDERLORD | RF6_S_BUG | RF6_S_RNG | RF6_S_ANIMALS)
+
+
+/*
+ * Hack -- "bolt" spells that may hurt fellow monsters
+ */
+#define RF4_BOLT_MASK \
+ (RF4_ARROW_1 | RF4_ARROW_2 | RF4_ARROW_3 | RF4_ARROW_4)
+
+#define RF5_BOLT_MASK \
+ (RF5_BO_ACID | RF5_BO_ELEC | RF5_BO_FIRE | RF5_BO_COLD | \
+ RF5_BO_POIS | RF5_BO_NETH | RF5_BO_WATE | RF5_BO_MANA | \
+ RF5_BO_PLAS | RF5_BO_ICEE | RF5_MISSILE)
+
+#define RF6_BOLT_MASK \
+ 0L
+
+
+/* Hack -- summon spells */
+
+#define RF4_SUMMON_MASK \
+ (RF4_S_ANIMAL)
+
+#define RF5_SUMMON_MASK \
+ 0L
+
+#define RF6_SUMMON_MASK \
+ (RF6_S_KIN | RF6_S_HI_DEMON | RF6_S_MONSTER | RF6_S_MONSTERS | RF6_S_ANT | \
+ RF6_S_SPIDER | RF6_S_HOUND | RF6_S_HYDRA | RF6_S_ANGEL | RF6_S_DEMON | \
+ RF6_S_UNDEAD | RF6_S_DRAGON | RF6_S_HI_UNDEAD | RF6_S_HI_DRAGON | \
+ RF6_S_WRAITH | RF6_S_UNIQUE | RF6_S_THUNDERLORD | RF6_S_BUG | RF6_S_RNG | \
+ RF6_S_ANIMALS)
+
+
+/*** Macro Definitions ***/
+
+
+/*
+ * Hack -- The main "screen"
+ */
+#define term_screen (angband_term[0])
+
+
+/*
+ * Determine if a given inventory item is "aware"
+ */
+#define object_aware_p(T) \
+ (k_info[(T)->k_idx].aware)
+
+/*
+ * Determine if a given inventory item is "tried"
+ */
+#define object_tried_p(T) \
+ (k_info[(T)->k_idx].tried)
+
+
+/*
+ * Determine if a given inventory item is "known"
+ * Test One -- Check for special "known" tag
+ * Test Two -- Check for "Easy Know" + "Aware"
+ */
+#define object_known_p(T) \
+ (((T)->ident & (IDENT_KNOWN)) || \
+ (k_info[(T)->k_idx].easy_know && k_info[(T)->k_idx].aware))
+
+
+/*
+ * Return the "attr" for a given item.
+ * Use "flavor" if available.
+ * Default to user definitions.
+ */
+#define object_attr(T) \
+ (((T)->tval == TV_RANDART) ? \
+ random_artifacts[(T)->sval].attr : \
+ (k_info[(T)->k_idx].flavor) ? \
+ misc_to_attr[k_info[(T)->k_idx].flavor] : \
+ k_info[(T)->k_idx].x_attr)
+
+#define object_attr_default(T) \
+ (((T)->tval == TV_RANDART) ? \
+ random_artifacts[(T)->sval].attr : \
+ (k_info[(T)->k_idx].flavor) ? \
+ misc_to_attr[k_info[(T)->k_idx].flavor] : \
+ k_info[(T)->k_idx].d_attr)
+
+/*
+ * Return the "char" for a given item.
+ * Use "flavor" if available.
+ * Default to user definitions.
+ */
+#define object_char(T) \
+ ((k_info[(T)->k_idx].flavor) ? \
+ misc_to_char[k_info[(T)->k_idx].flavor] : \
+ k_info[(T)->k_idx].x_char)
+
+#define object_char_default(T) \
+ ((k_info[(T)->k_idx].flavor) ? \
+ misc_to_char[k_info[(T)->k_idx].flavor] : \
+ k_info[(T)->k_idx].d_char)
+
+
+
+/*
+ * Artifacts use the "name1" field
+ */
+#define artifact_p(T) \
+ ( \
+ ((T)->tval == TV_RANDART || \
+ ((T)->name1 ? TRUE : FALSE) || \
+ ((T)->art_name ? TRUE : FALSE) || \
+ ((k_info[(T)->k_idx].flags3 & TR3_NORM_ART)? TRUE : FALSE)) \
+ )
+
+/*
+ * Ego-Items use the "name2" field
+ */
+#define ego_item_p(T) \
+ ((T)->name2 || (T)->name2b ? TRUE : FALSE)
+
+/*
+ * Ego-Items use the "name2" field
+ */
+#define is_ego_p(T, e) \
+ (((T)->name2 == (e)) || ((T)->name2b == (e)))
+
+
+
+/*
+ * Cursed items.
+ */
+#define cursed_p(T) \
+ ((T)->ident & (IDENT_CURSED))
+
+
+/*
+ * Convert an "attr"/"char" pair into a "pict" (P)
+ */
+#define PICT(A,C) \
+ ((((u16b)(A)) << 8) | ((byte)(C)))
+
+/*
+ * Convert a "pict" (P) into an "attr" (A)
+ */
+#define PICT_A(P) \
+ ((byte)((P) >> 8))
+
+/*
+ * Convert a "pict" (P) into an "char" (C)
+ */
+#define PICT_C(P) \
+ ((char)((byte)(P)))
+
+
+/*
+ * Convert a "location" (Y,X) into a "grid" (G)
+ */
+#define GRID(Y,X) \
+ (256 * (Y) + (X))
+
+/*
+ * Convert a "grid" (G) into a "location" (Y)
+ */
+#define GRID_Y(G) \
+ ((int)((G) / 256U))
+
+/*
+ * Convert a "grid" (G) into a "location" (X)
+ */
+#define GRID_X(G) \
+ ((int)((G) % 256U))
+
+
+/*
+ * Determines if a map location is fully inside the outer walls
+ */
+#define in_bounds(Y,X) \
+ (((Y) > 0) && ((X) > 0) && ((Y) < cur_hgt-1) && ((X) < cur_wid-1))
+
+/*
+ * Determines if a map location is on or inside the outer walls
+ */
+#define in_bounds2(Y,X) \
+ (((Y) >= 0) && ((X) >= 0) && ((Y) < cur_hgt) && ((X) < cur_wid))
+
+
+/*
+ * Determines if a map location is currently "on screen" -RAK-
+ * Note that "panel_contains(Y,X)" always implies "in_bounds2(Y,X)".
+ */
+#define panel_contains(Y,X) \
+ (((Y) >= panel_row_min) && ((Y) <= panel_row_max) && \
+ ((X) >= panel_col_min) && ((X) <= panel_col_max))
+
+
+
+/*
+ * Determine if a "legal" grid is a "floor" grid
+ *
+ * Line 1 -- forbid doors, rubble, seams, walls
+ *
+ * Note that the terrain features are split by a one bit test
+ * into those features which block line of sight and those that
+ * do not, allowing an extremely fast single bit check below.
+ *
+ * Add in the fact that some new terrain (water & lava) do NOT block sight
+ * -KMW-
+ */
+#define cave_floor_bold(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ (cave[Y][X].feat != FEAT_MON_TRAP))
+
+
+/*
+ * Determine if a "legal" grid is floor without the REMEMBER flag set
+ * Sometimes called "boring" grid
+ */
+#define cave_plain_floor_bold(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ !(f_info[cave[Y][X].feat].flags1 & FF1_REMEMBER))
+
+
+/*
+ * Determine if a "legal" grid isn't a "blocking line of sight" grid
+ *
+ * Line 1 -- forbid doors, rubble, seams, walls
+ *
+ * Note that the terrain features are split by a one bit test
+ * into those features which block line of sight and those that
+ * do not, allowing an extremely fast single bit check below.
+ *
+ * Add in the fact that some new terrain (water & lava) do NOT block sight
+ * -KMW-
+ */
+#define cave_sight_bold(Y,X) \
+ (!(f_info[cave[Y][X].feat].flags1 & FF1_NO_VISION))
+
+
+/*
+ * Determine if a "legal" grid is a "clean" floor grid
+ *
+ * Line 1 -- forbid non-floors
+ * Line 2 -- forbid deep water -KMW-
+ * Line 3 -- forbid deep lava -KMW-
+ * Line 4 -- forbid normal objects
+ */
+#define cave_clean_bold(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ (cave[Y][X].feat != FEAT_MON_TRAP) && \
+ (cave[Y][X].o_idx == 0) && \
+ !(f_info[cave[Y][X].feat].flags1 & FF1_PERMANENT))
+
+
+/*
+ * Determine if a "legal" grid is an "empty" floor grid
+ *
+ * Line 1 -- forbid doors, rubble, seams, walls
+ * Line 2 -- forbid normal monsters
+ * Line 3 -- forbid the player
+ */
+#define cave_empty_bold(Y,X) \
+ (cave_floor_bold(Y,X) && \
+ !(cave[Y][X].m_idx) && \
+ !(((Y) == p_ptr->py) && ((X) == p_ptr->px)))
+
+
+/*
+ * Determine if a "legal" grid is an "naked" floor grid
+ *
+ * Line 1 -- forbid non-floors, non-shallow water & lava -KMW-
+ * Line 2 -- forbid normal objects
+ * Line 3 -- forbid player/monsters
+ */
+#define cave_naked_bold(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ (cave[Y][X].feat != FEAT_MON_TRAP) && \
+ !(f_info[cave[Y][X].feat].flags1 & FF1_PERMANENT) && \
+ (cave[Y][X].o_idx == 0) && \
+ (cave[Y][X].m_idx == 0))
+
+#define cave_naked_bold2(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ (cave[Y][X].feat != FEAT_MON_TRAP) && \
+ (cave[Y][X].o_idx == 0) && \
+ (cave[Y][X].m_idx == 0))
+
+
+
+/*
+ * Determine if a "legal" grid is "permanent"
+ *
+ * Line 1 -- perma-walls
+ * Line 2-3 -- stairs
+ * Line 4-5 -- building doors -KMW-
+ * Line 6-7 -- shop doors
+ */
+#define cave_perma_bold(Y,X) \
+ (f_info[cave[Y][X].feat].flags1 & FF1_PERMANENT)
+
+
+/*
+ * Grid based version of "cave_floor_bold()"
+ */
+#define cave_floor_grid(C) \
+ ((f_info[(C)->feat].flags1 & FF1_FLOOR) && ((C)->feat != FEAT_MON_TRAP))
+
+
+/*
+ * Grid based version of "cave_plain_floor_bold()"
+ */
+#define cave_plain_floor_grid(C) \
+ ((f_info[(C)->feat].flags1 & FF1_FLOOR) && \
+ !(f_info[(C)->feat].flags1 & FF1_REMEMBER))
+
+
+/*
+ * Grid based version of "cave_clean_bold()"
+ */
+#define cave_clean_grid(C) \
+ ((f_info[(C)->feat].flags1 & FF1_FLOOR) && ((C)->feat != FEAT_MON_TRAP) && \
+ (!(C)->o_idx))
+
+/*
+ * Grid based version of "cave_sight_bold()"
+ */
+#define cave_sight_grid(C) \
+ (!(f_info[(C)->feat].flags1 & FF1_NO_VISION))
+
+/*
+ * Grid based version of "cave_empty_bold()"
+ */
+#define cave_empty_grid(C) \
+ (cave_floor_grid(C) && \
+ !((C)->m_idx) && \
+ !((C) == &cave[p_ptr->py][p_ptr->px]))
+
+/*
+ * Grid based version of "cave_empty_bold()"
+ */
+#define cave_naked_grid(C) \
+ ((f_info[(C)->feat].flags1 & FF1_FLOOR) && ((C)->feat != FEAT_MON_TRAP) && \
+ !((C)->o_idx) && \
+ !((C)->m_idx) && \
+ !((C) == &cave[p_ptr->py][p_ptr->px]))
+
+
+/*
+ * Grid based version of "cave_perma_bold()"
+ */
+#define cave_perma_grid(C) \
+ (f_info[(C)->feat].flags1 & FF1_PERMANENT)
+
+
+
+/*
+ * Determine if a "legal" grid is within "los" of the player
+ *
+ * Note the use of comparison to zero to force a "boolean" result
+ */
+#define player_has_los_bold(Y,X) \
+ ((cave[Y][X].info & (CAVE_VIEW)) != 0)
+
+
+
+/*
+ * Determine if a "legal" grid can be "seen" by the player
+ *
+ * Note the use of comparison to zero to force a "boolean" result
+ */
+#define player_can_see_bold(Y,X) \
+ ((cave[Y][X].info & (CAVE_SEEN)) != 0)
+
+
+
+/*** Color constants ***/
+
+
+/*
+ * Angband "attributes" (with symbols, and base (R,G,B) codes)
+ *
+ * The "(R,G,B)" codes are given in "fourths" of the "maximal" value,
+ * and should "gamma corrected" on most (non-Macintosh) machines.
+ */
+#define TERM_DARK 0 /* 'd' */ /* 0,0,0 */
+#define TERM_WHITE 1 /* 'w' */ /* 4,4,4 */
+#define TERM_SLATE 2 /* 's' */ /* 2,2,2 */
+#define TERM_ORANGE 3 /* 'o' */ /* 4,2,0 */
+#define TERM_RED 4 /* 'r' */ /* 3,0,0 */
+#define TERM_GREEN 5 /* 'g' */ /* 0,2,1 */
+#define TERM_BLUE 6 /* 'b' */ /* 0,0,4 */
+#define TERM_UMBER 7 /* 'u' */ /* 2,1,0 */
+#define TERM_L_DARK 8 /* 'D' */ /* 1,1,1 */
+#define TERM_L_WHITE 9 /* 'W' */ /* 3,3,3 */
+#define TERM_VIOLET 10 /* 'v' */ /* 4,0,4 */
+#define TERM_YELLOW 11 /* 'y' */ /* 4,4,0 */
+#define TERM_L_RED 12 /* 'R' */ /* 4,0,0 */
+#define TERM_L_GREEN 13 /* 'G' */ /* 0,4,0 */
+#define TERM_L_BLUE 14 /* 'B' */ /* 0,4,4 */
+#define TERM_L_UMBER 15 /* 'U' */ /* 3,2,1 */
+
+
+/*** Graphics constants ***/
+
+/*
+ * Possible values of graphics_mode
+ * Good only when use_graphics is set to TRUE
+ * Set by reset_visuals() and used by map_info()
+ */
+#define GRAPHICS_NONE 0
+#define GRAPHICS_UNKNOWN 1
+#define GRAPHICS_IBM 2
+#define GRAPHICS_OLD 3
+#define GRAPHICS_NEW 4
+#define GRAPHICS_ISO 5
+
+
+/*** Sound constants ***/
+
+
+/*
+ * Mega-Hack -- some primitive sound support (see "main-win.c")
+ *
+ * Some "sound" constants for "Term_xtra(TERM_XTRA_SOUND, val)"
+ */
+#define SOUND_HIT 1
+#define SOUND_MISS 2
+#define SOUND_FLEE 3
+#define SOUND_DROP 4
+#define SOUND_KILL 5
+#define SOUND_LEVEL 6
+#define SOUND_DEATH 7
+#define SOUND_STUDY 8
+#define SOUND_TELEPORT 9
+#define SOUND_SHOOT 10
+#define SOUND_QUAFF 11
+#define SOUND_ZAP 12
+#define SOUND_WALK 13
+#define SOUND_TPOTHER 14
+#define SOUND_HITWALL 15
+#define SOUND_EAT 16
+#define SOUND_STORE1 17
+#define SOUND_STORE2 18
+#define SOUND_STORE3 19
+#define SOUND_STORE4 20
+#define SOUND_DIG 21
+#define SOUND_OPENDOOR 22
+#define SOUND_SHUTDOOR 23
+#define SOUND_TPLEVEL 24
+#define SOUND_SCROLL 25
+#define SOUND_BUY 26
+#define SOUND_SELL 27
+#define SOUND_WARN 28
+#define SOUND_ROCKET 29 /* Somebody's shooting rockets */
+#define SOUND_N_KILL 30 /* The player kills a non-living/undead monster */
+#define SOUND_U_KILL 31 /* The player kills a unique */
+#define SOUND_QUEST 32 /* The player has just completed a quest */
+#define SOUND_HEAL 33 /* The player was healed a little bit */
+#define SOUND_X_HEAL 34 /* The player was healed full health */
+#define SOUND_BITE 35 /* A monster bites you */
+#define SOUND_CLAW 36 /* A monster claws you */
+#define SOUND_M_SPELL 37 /* A monster casts a miscellaneous spell */
+#define SOUND_SUMMON 38 /* A monster casts a summoning spell */
+#define SOUND_BREATH 39 /* A monster breathes */
+#define SOUND_BALL 40 /* A monster casts a ball / bolt spell */
+#define SOUND_M_HEAL 41 /* A monster heals itself somehow */
+#define SOUND_ATK_SPELL 42 /* A monster casts a misc. offensive spell */
+#define SOUND_EVIL 43 /* Something nasty has just happened! */
+#define SOUND_TOUCH 44 /* A monster touches you */
+#define SOUND_STING 45 /* A monster stings you */
+#define SOUND_CRUSH 46 /* A monster crushes / envelopes you */
+#define SOUND_SLIME 47 /* A monster drools/spits/etc on you */
+#define SOUND_WAIL 48 /* A monster wails */
+#define SOUND_WINNER 49 /* Just won the game! */
+#define SOUND_FIRE 50 /* An item was burned */
+#define SOUND_ACID 51 /* An item was destroyed by acid */
+#define SOUND_ELEC 52 /* An item was destroyed by electricity */
+#define SOUND_COLD 53 /* An item was shattered */
+#define SOUND_ILLEGAL 54 /* Illegal command attempted */
+#define SOUND_FAIL 55 /* Fail to get a spell off / activate an item */
+#define SOUND_WAKEUP 56 /* A monster wakes up */
+#define SOUND_INVULN 57 /* Invulnerability! */
+#define SOUND_FALL 58 /* Falling through a trapdoor... */
+#define SOUND_PAIN 59 /* A monster is in pain! */
+#define SOUND_DESTITEM 60 /* An item was destroyed by misc. means */
+#define SOUND_MOAN 61 /* A monster makes a moan/beg/insult attack */
+#define SOUND_SHOW 62 /* A monster makes a "show" attack */
+#define SOUND_UNUSED 63 /* (no sound for gaze attacks) */
+#define SOUND_EXPLODE 64 /* Something (or somebody) explodes */
+
+/*
+ * Mega-Hack -- maximum known sounds
+ */
+#define SOUND_MAX 65
+
+
+
+/*** Hack ***/
+
+
+/*
+ * Road flags
+ */
+#define ROAD_NORTH 1
+#define ROAD_SOUTH 2
+#define ROAD_EAST 4
+#define ROAD_WEST 8
+
+
+/*
+ * Buildings actions
+ */
+#define BACT_RESEARCH_ITEM 1
+#define BACT_TOWN_HISTORY 2
+#define BACT_RACE_LEGENDS 3
+#define BACT_GREET_KING 4
+#define BACT_KING_LEGENDS 5
+#define BACT_QUEST1 6
+#define BACT_POSTER 8
+#define BACT_ARENA_RULES 9
+#define BACT_ARENA 10
+#define BACT_ARENA_LEGENDS 11
+#define BACT_IN_BETWEEN 12
+#define BACT_GAMBLE_RULES 13
+#define BACT_CRAPS 14
+#define BACT_SPIN_WHEEL 15
+#define BACT_DICE_SLOTS 16
+#define BACT_REST 17
+#define BACT_FOOD 18
+#define BACT_RUMORS 19
+#define BACT_RESEARCH_MONSTER 20
+#define BACT_COMPARE_WEAPONS 21
+#define BACT_LEGENDS 22
+#define BACT_ENCHANT_WEAPON 23
+#define BACT_ENCHANT_ARMOR 24
+#define BACT_RECHARGE 25
+#define BACT_IDENTS 26
+#define BACT_LEARN 27
+#define BACT_HEALING 28
+#define BACT_RESTORE 29
+#define BACT_ENCHANT_ARROWS 30
+#define BACT_ENCHANT_BOW 31
+#define BACT_GREET 32
+#define BACT_RECALL 33
+#define BACT_TELEPORT_LEVEL 34
+/* XXX */
+/* XXX */
+#define BACT_MIMIC_NORMAL 37
+#define BACT_VIEW_BOUNTIES 38
+#define BACT_SELL_CORPSES 39
+#define BACT_VIEW_QUEST_MON 40
+#define BACT_SELL_QUEST_MON 41
+#define BACT_DIVINATION 42
+#define BACT_SELL 43
+#define BACT_BUY 44
+#define BACT_EXAMINE 45
+#define BACT_STEAL 46
+#define BACT_QUEST2 47
+#define BACT_QUEST3 48
+#define BACT_QUEST4 49
+#define BACT_STAR_HEAL 50
+#define BACT_REQUEST_ITEM 51
+#define BACT_GET_LOAN 52
+#define BACT_PAY_BACK_LOAN 53
+/* If one adds new BACT_ do NOT forget to increase max_bact in variables.c */
+
+
+/*
+ * Quest status
+ */
+#define QUEST_STATUS_IGNORED -1
+#define QUEST_STATUS_UNTAKEN 0
+#define QUEST_STATUS_TAKEN 1
+#define QUEST_STATUS_COMPLETED 2
+#define QUEST_STATUS_REWARDED 3
+#define QUEST_STATUS_FAILED 4
+#define QUEST_STATUS_FINISHED 5
+#define QUEST_STATUS_FAILED_DONE 6
+
+/*
+ * Quest flags
+ */
+#define QUEST_FLAG_SILENT 0x01 /* no messages for completion */
+#define QUEST_FLAG_PRESET 0x02 /* quest is outside the main dungeon */
+#define QUEST_FLAG_ONCE 0x04 /* quest is marked finished after leaving */
+
+/*
+ * Initialization flags
+ */
+#define INIT_SHOW_TEXT 0x01
+#define INIT_ASSIGN 0x02
+#define INIT_CREATE_DUNGEON 0x04
+#define INIT_GET_SIZE 0x08
+#define INIT_POSITION 0x10
+
+/*
+ * Alchemists defines
+ */
+#define MAX_ALCHEMIST_RECIPES 20
+#define ALCHEMIST_ENCHANT_DAM 0x01
+#define ALCHEMIST_ENCHANT_PVAL 0x02
+#define ALCHEMIST_ENCHANT_AC 0x04
+
+/*
+ * Music songs
+ */
+#define MUSIC_NONE 0
+#define MUSIC_SLOW 1
+#define MUSIC_CONF 2
+#define MUSIC_STUN 3
+#define MUSIC_LIFE 4
+#define MUSIC_MIND 5
+#define MUSIC_LITE 6
+#define MUSIC_FURY 7
+#define MUSIC_AWARE 8
+#define MUSIC_ID 9
+#define MUSIC_ILLUSION 10
+#define MUSIC_WALL 11
+#define MUSIC_RESIST 12
+#define MUSIC_TIME 13
+#define MUSIC_BETWEEN 14
+#define MUSIC_CHARME 15
+#define MUSIC_VIBRA 16
+#define MUSIC_HOLY 17
+#define MUSIC_HIDE 18
+#define MUSIC_LIBERTY 19
+#define MUSIC_RAISE 20
+#define MUSIC_SHADOW 21
+#define MUSIC_STAR_ID 22
+
+#define MAX_MUSIC 23
+#define MAX_MUSICS 11
+
+/*
+ * Fate
+ */
+#define MAX_FATES 200
+
+#define FATE_NONE 0
+#define FATE_FIND_O 1
+#define FATE_NO_DIE_MORTAL 2
+#define FATE_FIND_A 3
+#define FATE_FIND_R 4
+#define FATE_FIND_V 5
+#define FATE_DIE 6
+
+/*
+ * Runes definition
+ */
+#define RUNE_SELF 0x00000001
+#define RUNE_ARROW 0x00000002
+#define RUNE_RAY 0x00000004
+#define RUNE_SPHERE 0x00000008
+#define RUNE_POWER_SURGE 0x00000010
+#define RUNE_ARMAGEDDON 0x00000020
+#define RUNE_MOD_MAX 6
+#define RUNE_STONE 0x000000FF
+
+
+/*
+ * Defines of the different dungeon types
+ */
+#define DUNGEON_WILDERNESS 0
+#define DUNGEON_MIRKWOOD 1
+#define DUNGEON_MORDOR 2
+#define DUNGEON_ANGBAND 3
+#define DUNGEON_BARROW_DOWNS 4
+#define DUNGEON_MOUNT_DOOM 5
+#define DUNGEON_NETHER_REALM 6
+#define DUNGEON_NUMENOR 7
+#define DUNGEON_MANDOS 8
+#define DUNGEON_VOID 11
+#define DUNGEON_MAZE 18
+#define DUNGEON_DOL_GULDUR 23
+
+/* Max depth of each dungeon(max_depth - min_depth) */
+#define MAX_DUNGEON_DEPTH 128
+
+#define DUNGEON_MODE_NONE 0
+#define DUNGEON_MODE_AND 1
+#define DUNGEON_MODE_NAND 2
+#define DUNGEON_MODE_OR 3
+#define DUNGEON_MODE_NOR 4
+
+
+/*
+ * Returns the dungeon level or the feat,
+ * if the player is not in a dungeon
+ */
+#define level_or_feat(DTYPE, DLEVEL) \
+ ((DTYPE) == DUNGEON_WILDERNESS ? \
+ wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat : \
+ (DLEVEL) )
+
+
+/*
+ * Defines for the inscriptions
+ */
+#define INSCRIP_EXEC_ENGRAVE 0x01
+#define INSCRIP_EXEC_WALK 0x02
+#define INSCRIP_EXEC_MONST_WALK 0x04
+
+#define INSCRIP_NONE 0
+#define INSCRIP_LIGHT 1
+#define INSCRIP_DARK 2
+#define INSCRIP_STORM 3
+#define INSCRIP_PROTECTION 4
+#define INSCRIP_DWARF_SUMMON 5
+#define INSCRIP_CHASM 6
+#define INSCRIP_BLACK_FIRE 7
+#define MAX_INSCRIPTIONS 8
+
+/*
+ * Various class dependant defines
+ */
+#define CLASS_NONE 0
+#define CLASS_MANA_PATH 1
+#define CLASS_CANALIZE_MANA 2
+#define CLASS_WINDS_MANA 3
+
+#define CLASS_MANA_PATH_ERASE 0x0001
+#define CLASS_FLOOD_LEVEL 0x0002
+#define CLASS_CANALIZE_MANA_EXTRA 0x0004
+#define CLASS_UNDEAD 0x0008
+#define CLASS_ANTIMAGIC 0x0010
+#define CLASS_LEGS 0x0020
+#define CLASS_ARMS 0x0040
+#define CLASS_WALL 0x0080
+
+/*
+ * Types of birth presents
+ */
+#define BIRTH_NONE 0
+#define BIRTH_RING 1
+#define BIRTH_AMULET 2
+
+/*
+ * Automatic note taking types
+ */
+#define NOTE_BIRTH 1
+#define NOTE_WINNER 2
+#define NOTE_SAVE_GAME 3
+#define NOTE_ENTER_DUNGEON 4
+
+/*
+ * Player monsters & ghost defines
+ * NO MORE USED but for savefile compatibility
+ */
+#define GHOST_R_IDX_HEAD 967
+#define GHOST_R_IDX_TAIL 977
+#define MAX_GHOSTS (GHOST_R_IDX_TAIL - GHOST_R_IDX_HEAD)
+
+/* Stores/buildings defines */
+#define STORE_HATED 0
+#define STORE_LIKED 1
+#define STORE_NORMAL 2
+
+/* Pseudo-id defines */
+#define SENSE_NONE 0
+#define SENSE_CURSED 1
+#define SENSE_AVERAGE 2
+#define SENSE_GOOD_LIGHT 3
+#define SENSE_GOOD_HEAVY 4
+#define SENSE_EXCELLENT 5
+#define SENSE_WORTHLESS 6
+#define SENSE_TERRIBLE 7
+#define SENSE_SPECIAL 8
+#define SENSE_BROKEN 9
+#define SENSE_UNCURSED 10
+
+/* Wilderness map related */
+#define WILDERNESS_SEE_RADIUS 3 /* The amount of wilderness seen around the player */
+
+/* Ego monsters defines */
+#define MEGO_CHAR_ANY 127
+#define MEGO_ADD 0
+#define MEGO_SUB 1
+#define MEGO_FIX 2
+#define MEGO_PRC 3
+
+#define MEGO_CHANCE 18 /* % chances of getting ego monsters */
+
+#define race_inf(m_ptr) (((m_ptr)->sr_ptr) ? (m_ptr)->sr_ptr : race_info_idx((m_ptr)->r_idx, (m_ptr)->ego))
+
+/* Object generation */
+#define OBJ_GENE_TREASURE 20
+#define OBJ_GENE_COMBAT 20
+#define OBJ_GENE_MAGIC 20
+#define OBJ_GENE_TOOL 20
+
+/*
+ * Used (or should be) by various functions and tables needing access to
+ * single bits
+ */
+#define BIT(x) (1L << (x))
+
+/* Town defines */
+#define TOWN_RANDOM 20 /* First random town */
+#define TOWN_DUNGEON 4 /* Maximun number of towns per dungeon */
+#define TOWN_CHANCE 50 /* Chance of 1 town */
+
+
+/*
+ * Store flags
+ */
+#define SF1_DEPEND_LEVEL 0x00000001L
+#define SF1_SHALLOW_LEVEL 0x00000002L
+#define SF1_MEDIUM_LEVEL 0x00000004L
+#define SF1_DEEP_LEVEL 0x00000008L
+#define SF1_RARE 0x00000010L
+#define SF1_VERY_RARE 0x00000020L
+#define SF1_COMMON 0x00000040L
+#define SF1_ALL_ITEM 0x00000080L /* Works as the BM */
+#define SF1_RANDOM 0x00000100L
+#define SF1_FORCE_LEVEL 0x00000200L
+#define SF1_MUSEUM 0x00000400L
+
+/*
+ * Powers (mutation, activations, ...)
+ */
+#define POWER_MAX_INIT 62
+
+#define PWR_SPIT_ACID 0
+#define PWR_BR_FIRE 1
+#define PWR_HYPN_GAZE 2
+#define PWR_TELEKINES 3
+#define PWR_VTELEPORT 4
+#define PWR_MIND_BLST 5
+#define PWR_RADIATION 6
+#define PWR_VAMPIRISM 7
+#define PWR_SMELL_MET 8
+#define PWR_SMELL_MON 9
+#define PWR_BLINK 10
+#define PWR_EAT_ROCK 11
+#define PWR_SWAP_POS 12
+#define PWR_SHRIEK 13
+#define PWR_ILLUMINE 14
+#define PWR_DET_CURSE 15
+#define PWR_BERSERK 16
+#define PWR_POLYMORPH 17
+#define PWR_MIDAS_TCH 18
+#define PWR_GROW_MOLD 19
+#define PWR_RESIST 20
+#define PWR_EARTHQUAKE 21
+#define PWR_EAT_MAGIC 22
+#define PWR_WEIGH_MAG 23
+#define PWR_STERILITY 24
+#define PWR_PANIC_HIT 25
+#define PWR_DAZZLE 26
+#define PWR_DARKRAY 27
+#define PWR_RECALL 28
+#define PWR_BANISH 29
+#define PWR_COLD_TOUCH 30
+#define PWR_LAUNCHER 31
+
+#define PWR_PASSWALL 32
+#define PWR_DETECT_TD 33
+#define PWR_COOK_FOOD 34
+#define PWR_UNFEAR 35
+#define PWR_EXPL_RUNE 36
+#define PWR_STM 37
+#define PWR_POIS_DART 38
+#define PWR_MAGIC_MISSILE 39
+#define PWR_GROW_TREE 40
+#define PWR_BR_COLD 41
+#define PWR_BR_CHAOS 42
+#define PWR_BR_ELEM 43
+#define PWR_WRECK_WORLD 44
+#define PWR_SCARE 45
+#define PWR_REST_LIFE 46
+#define PWR_SUMMON_MONSTER 47
+#define PWR_NECRO 48
+#define PWR_ROHAN 49
+#define PWR_THUNDER 50
+#define PWR_DEATHMOLD 51
+#define PWR_HYPNO 52
+#define PWR_UNHYPNO 53
+#define PWR_INCARNATE 54
+#define PWR_MAGIC_MAP 55
+#define PWR_LAY_TRAP 56
+#define PWR_MERCHANT 57
+#define PWR_COMPANION 58
+#define PWR_BEAR 59
+#define PWR_DODGE 60
+#define PWR_BALROG 61
+
+#define ADD_POWER(pow, p) ((pow)[(p)] = TRUE)
+
+/*
+ * Shield effect options
+ */
+#define SHIELD_NONE 0x0000
+#define SHIELD_COUNTER 0x0001
+#define SHIELD_FIRE 0x0002
+#define SHIELD_GREAT_FIRE 0x0004
+#define SHIELD_FEAR 0x0008
+
+/*
+ * Quest constants
+ */
+#define MAX_MON_QUEST 10
+#define MAX_ITEM_QUEST 5
+
+#define MAX_RANDOM_QUEST 99
+
+#define QUEST_NULL 0
+#define QUEST_NECRO 1
+#define QUEST_SAURON 2
+#define QUEST_MORGOTH 3
+#define QUEST_THIEVES 4
+#define QUEST_RANDOM 5
+#define QUEST_HOBBIT 6
+#define QUEST_NAZGUL 7
+#define QUEST_TROLL 8
+#define QUEST_WIGHT 9
+#define QUEST_SPIDER 10
+#define QUEST_POISON 11
+#define QUEST_NARSIL 12
+#define QUEST_EOL 13
+#define QUEST_NIRNAETH 14
+#define QUEST_INVASION 15
+#define QUEST_BETWEEN 16
+#define QUEST_ONE 17
+#define QUEST_SHROOM 18
+#define QUEST_THRAIN 19
+#define QUEST_ULTRA_GOOD 20
+#define QUEST_ULTRA_EVIL 21
+#define QUEST_WOLVES 22
+#define QUEST_DRAGONS 23
+#define QUEST_HAUNTED 24
+#define QUEST_EVIL 25
+#define MAX_Q_IDX_INIT 26
+
+#define PLOT_MAIN 0
+#define PLOT_BREE 1
+#define PLOT_LORIEN 2
+#define PLOT_OTHER 3
+#define PLOT_GONDOLIN 4
+#define PLOT_MINAS 5
+#define PLOT_KHAZAD 6
+#define MAX_PLOTS 7
+
+/*
+ * Hooks
+ */
+#define HOOK_MONSTER_DEATH 0
+#define HOOK_OPEN 1
+#define HOOK_GEN_QUEST 2
+#define HOOK_END_TURN 3
+#define HOOK_FEELING 4
+#define HOOK_NEW_MONSTER 5
+#define HOOK_GEN_LEVEL 6
+#define HOOK_BUILD_ROOM1 7
+#define HOOK_NEW_LEVEL 8
+#define HOOK_QUEST_FINISH 9
+#define HOOK_QUEST_FAIL 10
+#define HOOK_GIVE 11
+#define HOOK_CHAR_DUMP 12
+#define HOOK_INIT_QUEST 13
+#define HOOK_WILD_GEN 14
+#define HOOK_DROP 15
+#define HOOK_IDENTIFY 16
+#define HOOK_MOVE 17
+#define HOOK_STAIR 18
+#define HOOK_MONSTER_AI 19
+#define HOOK_PLAYER_LEVEL 20
+#define HOOK_WIELD 21
+#define HOOK_INIT 22
+#define HOOK_QUAFF 23
+#define HOOK_AIM 24
+#define HOOK_USE 25
+#define HOOK_ACTIVATE 26
+#define HOOK_ZAP 27
+#define HOOK_READ 28
+#define HOOK_CALC_BONUS 29
+#define HOOK_CALC_POWERS 30
+#define HOOK_KEYPRESS 31
+#define HOOK_CHAT 32
+#define HOOK_MON_SPEAK 33
+#define HOOK_MKEY 34
+#define HOOK_BIRTH_OBJECTS 35
+#define HOOK_ACTIVATE_DESC 36
+#define HOOK_INIT_GAME 37
+#define HOOK_ACTIVATE_POWER 38
+#define HOOK_ITEM_NAME 39
+#define HOOK_SAVE_GAME 40
+#define HOOK_LOAD_GAME 41
+#define HOOK_LEVEL_REGEN 42
+#define HOOK_LEVEL_END_GEN 43
+#define HOOK_BUILDING_ACTION 44
+#define HOOK_PROCESS_WORLD 45
+#define HOOK_WIELD_SLOT 46
+#define HOOK_STORE_STOCK 47
+#define HOOK_STORE_BUY 48
+#define HOOK_GEN_LEVEL_BEGIN 49
+#define HOOK_GET 50
+#define HOOK_REDRAW 51
+#define HOOK_RECALC_SKILLS 52
+#define HOOK_ENTER_DUNGEON 53
+#define HOOK_FIRE 54
+#define HOOK_EAT 55
+#define HOOK_DIE 56
+#define HOOK_CALC_HP 57
+#define HOOK_GF_COLOR 58
+#define HOOK_GF_EXEC 59
+#define HOOK_CALC_MANA 60
+#define HOOK_LOAD_END 61
+#define HOOK_RECALL 62
+#define HOOK_FOLLOW_GOD 63
+#define HOOK_SACRIFICE_GOD 64
+#define HOOK_BODY_PARTS 65
+#define HOOK_APPLY_MAGIC 66
+#define HOOK_PLAYER_EXP 67
+#define HOOK_BIRTH 68
+#define HOOK_CALC_LITE 69
+#define HOOK_LEARN_ABILITY 70
+#define HOOK_MOVED 71
+#define HOOK_GAME_START 72
+#define HOOK_TAKEOFF 73
+#define HOOK_CALC_WEIGHT 74
+#define HOOK_FORBID_TRAVEL 75
+#define HOOK_DEBUG_COMMAND 76
+#define HOOK_CALC_BONUS_END 77
+#define MAX_HOOKS 78
+
+#define HOOK_TYPE_C 0
+#define HOOK_TYPE_LUA 1
+
+/*
+ * Defines for loadsave.c
+ * Why 3 and 7? So if it's uninitialized, the code will be able to catch it, as
+ * 0 is an invalid flag. Also, having them apart means that it being accidentally
+ * modified will also result in an invalid value -- Improv
+ */
+#define LS_LOAD 3
+#define LS_SAVE 7
+
+/*
+ * In game help
+ */
+#define HELP1_BETWEEN 0x00000001
+#define HELP1_ALTAR 0x00000002
+#define HELP1_FOUNTAIN 0x00000004
+#define HELP1_IDENTIFY 0x00000008
+#define HELP1_WILD_MODE 0x00000010
+
+/*
+ * Special weapon effects
+ */
+#define SPEC_POIS 0x00000001L
+#define SPEC_CUT 0x00000002L
+
+/*
+ * Ambushes in the wild
+ */
+#define AMBUSH_RACE 1
+#define AMBUSH_MIX 2
+
+/*
+ * Macro trigger
+ */
+#define MAX_MACRO_MOD 12
+#define MAX_MACRO_TRIG 200
+
+
+/*
+ * Skills !
+ */
+#define SKILL_MAX 50000 /* Maximun skill value */
+#define SKILL_STEP 1000 /* 1 skill point */
+
+#define SKILL_EXCLUSIVE 9999 /* Flag to tell exclusive skills */
+
+#define SKILL_CONVEYANCE 1
+#define SKILL_MANA 2
+#define SKILL_FIRE 3
+#define SKILL_AIR 4
+#define SKILL_WATER 5
+#define SKILL_NATURE 6
+#define SKILL_EARTH 7
+#define SKILL_SYMBIOTIC 8
+#define SKILL_MUSIC 9
+#define SKILL_DIVINATION 10
+#define SKILL_TEMPORAL 11
+#define SKILL_DRUID 12
+#define SKILL_DAEMON 13
+#define SKILL_META 14
+#define SKILL_MAGIC 15
+#define SKILL_COMBAT 16
+#define SKILL_MASTERY 17
+#define SKILL_SWORD 18
+#define SKILL_AXE 19
+#define SKILL_POLEARM 20
+#define SKILL_HAFTED 21
+#define SKILL_BACKSTAB 22
+#define SKILL_ARCHERY 23
+#define SKILL_SLING 24
+#define SKILL_BOW 25
+#define SKILL_XBOW 26
+#define SKILL_BOOMERANG 27
+#define SKILL_SPIRITUALITY 28
+#define SKILL_MINDCRAFT 29
+#define SKILL_MISC 30
+#define SKILL_NECROMANCY 31
+#define SKILL_MIMICRY 32
+#define SKILL_ANTIMAGIC 33
+#define SKILL_RUNECRAFT 34
+#define SKILL_SNEAK 35
+#define SKILL_STEALTH 36
+#define SKILL_DISARMING 37
+/* XXX */
+#define SKILL_ALCHEMY 39
+#define SKILL_STEALING 40
+#define SKILL_SORCERY 41
+#define SKILL_HAND 42
+#define SKILL_THAUMATURGY 43
+#define SKILL_SUMMON 44
+#define SKILL_SPELL 45
+#define SKILL_DODGE 46
+#define SKILL_BEAR 47
+#define SKILL_LORE 48
+#define SKILL_PRESERVATION 49
+#define SKILL_POSSESSION 50
+#define SKILL_MIND 51
+#define SKILL_CRITS 52
+#define SKILL_PRAY 53
+#define SKILL_LEARN 54
+#define SKILL_UDUN 55
+#define SKILL_DEVICE 56
+#define SKILL_STUN 57
+#define SKILL_BOULDER 58
+#define SKILL_GEOMANCY 59
+
+/* Ugly but needed */
+#define MAX_SKILLS 200
+
+/* Number of skill choices for Lost Sword quests. */
+#define LOST_SWORD_NSKILLS 4
+
+/* SKill flags */
+#define SKF1_HIDDEN 0x00000001 /* Starts hidden */
+#define SKF1_AUTO_HIDE 0x00000002 /* Tries to rehide at calc_bonus */
+#define SKF1_RANDOM_GAIN 0x00000004 /* Can be randomly gained by certain quests & such */
+
+#define MAX_MELEE 3
+
+/*
+ * Player specialities, should be external but ti would be a mess
+ */
+#define MAX_SPEC 20
+
+
+/*
+ * Spellbinder triggers
+ */
+#define SPELLBINDER_HP75 1
+#define SPELLBINDER_HP50 2
+#define SPELLBINDER_HP25 3
+
+/*
+ * God's defines
+ */
+#define GOD_ALL -1
+#define GOD_NONE 0
+#define GOD_ERU 1
+#define GOD_MANWE 2
+#define GOD_TULKAS 3
+#define GOD_MELKOR 4
+#define GOD_YAVANNA 5
+#define MAX_GODS_INIT 6
+
+#define GOD(g) if (p_ptr->pgod == (g))
+#define PRAY_GOD(g) if ((p_ptr->pgod == (g)) && (p_ptr->praying))
+#define NOT_PRAY_GOD(g) if ((p_ptr->pgod == (g)) && (!p_ptr->praying))
+
+/*
+ * Command numbers for do_cmd_cli().
+ *
+ * As the user is not intended to have a way to enter these codes directly
+ * (doing so isn't harmful, but these codes are not intended as mnemonics),
+ * only codes in the range 0xE000 - 0xF8FF (the private area of Unicode 3.0)
+ * should be used.
+ *
+ * In addition, values at the lower end of this range are preferred as the upper
+ * end may have a system-specific encoding
+ */
+#define CMD_CLI_HELP -8192
+#define CMD_SHOW_TIME -8188
+#define CMD_SHOW_SKILL -8187
+#define CMD_DUMP_HTML -8186
+#define CMD_MACRO -8185
+#define CMD_QUEST -8184
+#define CMD_BLUNDER -8183
+#define CMD_SHOW_ABILITY -8182
+
+#define CLI_MAX 128
+
+
+/*
+ * The various winner state
+ */
+#define WINNER_NORMAL 1
+#define WINNER_ULTRA 2
+
+/*
+ * The abilities
+ */
+#define AB_SPREAD_BLOWS 0
+#define AB_TREE_WALK 1
+#define AB_PERFECT_CASTING 2
+#define AB_MAX_BLOW1 3
+#define AB_MAX_BLOW2 4
+#define AB_AMMO_CREATION 5
+#define AB_DEATH_TOUCH 6
+#define AB_CREATE_ART 7
+#define AB_FAR_REACHING 8
+#define AB_TRAPPING 9
+#define AB_UNDEAD_FORM 10
diff --git a/src/dungeon.c b/src/dungeon.c
new file mode 100644
index 00000000..6d732f00
--- /dev/null
+++ b/src/dungeon.c
@@ -0,0 +1,5664 @@
+/* File: dungeon.c */
+
+/* Purpose: Angband game engine */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+#include "lua/lua.h"
+#include "tolua.h"
+extern lua_State* L;
+
+#define TY_CURSE_CHANCE 100
+#define DG_CURSE_CHANCE 50
+#define AUTO_CURSE_CHANCE 15
+#define CHAINSWORD_NOISE 100
+
+
+/*
+ * Return a "feeling" (or NULL) about an item. Method 1 (Heavy).
+ */
+byte value_check_aux1(object_type *o_ptr)
+{
+ /* Artifacts */
+ if (artifact_p(o_ptr))
+ {
+ /* Cursed/Broken */
+ if (cursed_p(o_ptr)) return (SENSE_TERRIBLE);
+
+ /* Normal */
+ return (SENSE_SPECIAL);
+ }
+
+ /* Ego-Items */
+ if (ego_item_p(o_ptr))
+ {
+ /* Cursed/Broken */
+ if (cursed_p(o_ptr)) return (SENSE_WORTHLESS);
+
+ /* Normal */
+ return (SENSE_EXCELLENT);
+ }
+
+ /* Cursed items */
+ if (cursed_p(o_ptr)) return (SENSE_CURSED);
+
+ /* Good "armor" bonus */
+ if (o_ptr->to_a > 0) return (SENSE_GOOD_HEAVY);
+
+ /* Good "weapon" bonus */
+ if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_HEAVY);
+
+ /* Default to "average" */
+ return (SENSE_AVERAGE);
+}
+
+byte value_check_aux1_magic(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+
+ switch (o_ptr->tval)
+ {
+ /* Scrolls, Potions, Wands, Staves and Rods */
+ case TV_SCROLL:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ /* "Cursed" scrolls/potions have a cost of 0 */
+ if (k_ptr->cost == 0) return (SENSE_TERRIBLE);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_SPECIAL);
+
+ /* Scroll of Nothing, Apple Juice, etc. */
+ if (k_ptr->cost < 3) return (SENSE_WORTHLESS);
+
+ /*
+ * Identify, Phase Door, Cure Light Wounds, etc. are
+ * just average
+ */
+ if (k_ptr->cost < 100) return (SENSE_AVERAGE);
+
+ /* Enchant Armor, *Identify*, Restore Stat, etc. */
+ if (k_ptr->cost < 10000) return (SENSE_GOOD_HEAVY);
+
+ /* Acquirement, Deincarnation, Strength, Blood of Life, ... */
+ if (k_ptr->cost >= 10000) return (SENSE_EXCELLENT);
+
+ break;
+ }
+
+ /* Food */
+ case TV_FOOD:
+ {
+ /* "Cursed" food */
+ if (k_ptr->cost == 0) return (SENSE_TERRIBLE);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_SPECIAL);
+
+ /* Normal food (no magical properties) */
+ if (k_ptr->cost <= 10) return (SENSE_AVERAGE);
+
+ /* Everything else is good */
+ if (k_ptr->cost > 10) return (SENSE_GOOD_HEAVY);
+
+ break;
+ }
+ }
+
+ /* No feeling */
+ return (SENSE_NONE);
+}
+
+
+/*
+ * Return a "feeling" (or NULL) about an item. Method 2 (Light).
+ */
+byte value_check_aux2(object_type *o_ptr)
+{
+ /* Cursed items (all of them) */
+ if (cursed_p(o_ptr)) return (SENSE_CURSED);
+
+ /* Artifacts -- except cursed/broken ones */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Ego-Items -- except cursed/broken ones */
+ if (ego_item_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Good armor bonus */
+ if (o_ptr->to_a > 0) return (SENSE_GOOD_LIGHT);
+
+ /* Good weapon bonuses */
+ if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_LIGHT);
+
+ /* Default to "average" */
+ return (SENSE_AVERAGE);
+}
+
+
+byte value_check_aux2_magic(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+
+ switch (o_ptr->tval)
+ {
+ /* Scrolls, Potions, Wands, Staves and Rods */
+ case TV_SCROLL:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ /* "Cursed" scrolls/potions have a cost of 0 */
+ if (k_ptr->cost == 0) return (SENSE_CURSED);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Scroll of Nothing, Apple Juice, etc. */
+ if (k_ptr->cost < 3) return (SENSE_AVERAGE);
+
+ /*
+ * Identify, Phase Door, Cure Light Wounds, etc. are
+ * just average
+ */
+ if (k_ptr->cost < 100) return (SENSE_AVERAGE);
+
+ /* Enchant Armor, *Identify*, Restore Stat, etc. */
+ if (k_ptr->cost < 10000) return (SENSE_GOOD_LIGHT);
+
+ /* Acquirement, Deincarnation, Strength, Blood of Life, ... */
+ if (k_ptr->cost >= 10000) return (SENSE_GOOD_LIGHT);
+
+ break;
+ }
+
+ /* Food */
+ case TV_FOOD:
+ {
+ /* "Cursed" food */
+ if (k_ptr->cost == 0) return (SENSE_CURSED);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Normal food (no magical properties) */
+ if (k_ptr->cost <= 10) return (SENSE_AVERAGE);
+
+ /* Everything else is good */
+ if (k_ptr->cost > 10) return (SENSE_GOOD_LIGHT);
+
+ break;
+ }
+ }
+
+ /* No feeling */
+ return (SENSE_NONE);
+}
+
+
+/*
+ * Can a player be resurrected?
+ */
+static bool_ granted_resurrection(void)
+{
+ PRAY_GOD(GOD_ERU)
+ {
+ if (p_ptr->grace > 100000)
+ {
+ if (magik(70)) return (TRUE);
+ else return (FALSE);
+ }
+ }
+ return (FALSE);
+}
+
+static byte select_sense(object_type *o_ptr)
+{
+ /* Valid "tval" codes */
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOW:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_MSTAFF:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_BOOMERANG:
+ case TV_TRAPKIT:
+ {
+ return 1;
+ break;
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_SCROLL:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ return 2;
+ break;
+ }
+
+ /* Dual use? */
+ case TV_DAEMON_BOOK:
+ {
+ return 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Sense the inventory
+ *
+ * Combat items (weapons and armour) - Fast, weak if combat skill < 10, strong
+ * otherwise.
+ *
+ * Magic items (scrolls, staffs, wands, potions etc) - Slow, weak if
+ * magic skill < 10, strong otherwise.
+ *
+ * It shouldn't matter a lot to discriminate against magic users, because
+ * they learn one form of ID or another, and because most magic items are
+ * easy_know.
+ */
+void sense_inventory(void)
+{
+ int i, combat_lev, magic_lev;
+
+ bool_ heavy_combat, heavy_magic;
+
+ byte feel;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /*** Check for "sensing" ***/
+
+ /* No sensing when confused */
+ if (p_ptr->confused) return;
+
+ /*
+ * In Angband, the chance of pseudo-id uses two different formulae:
+ *
+ * (1) Fast. 0 == rand_int(BASE / (plev * plev + 40)
+ * (2) Slow. 0 == rand_int(BASE / (plev + 5)
+ *
+ * Warriors: Fase with BASE == 9000
+ * Magi: Slow with BASE == 240000
+ * Priests: Fast with BASE == 10000
+ * Rogues: Fase with BASE == 20000
+ * Rangers: Slow with BASE == 120000
+ * Paladins: Fast with BASE == 80000
+ *
+ * In other words, those who have identify spells are penalised.
+ * The problems with Pern/Tome since it externalised player classes
+ * is that it uses the same and slow formula for spellcasters and
+ * fighters.
+ *
+ * In the following code, combat item pseudo-ID improves exponentially,
+ * (fast with BASE 9000) and magic one linear (slow with base 60000 --
+ * twice faster than V rangers).
+ *
+ * I hope this makes it closer to the original model -- pelpel
+ */
+
+ /* The combat skill affects weapon/armour pseudo-ID */
+ combat_lev = get_skill(SKILL_COMBAT);
+
+ /* The magic skill affects magic item pseudo-ID */
+ magic_lev = get_skill(SKILL_MAGIC);
+
+ /* Higher skill levels give the player better sense of items */
+ heavy_combat = (combat_lev > 10) ? TRUE : FALSE;
+ heavy_magic = (magic_lev > 10) ? TRUE : FALSE;
+
+
+ /*** Sense everything ***/
+
+ /* Check everything */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ byte okay = 0;
+
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty slots */
+ if (!o_ptr->k_idx) continue;
+
+ /* We know about it already, do not tell us again */
+ if (o_ptr->ident & (IDENT_SENSE)) continue;
+
+ /* It is fully known, no information needed */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Valid "tval" codes */
+ okay = select_sense(o_ptr);
+
+ /* Skip non-sense machines */
+ if (!okay) continue;
+
+ /* Check for a feeling */
+ if (okay == 1)
+ {
+ feel = (heavy_combat ? value_check_aux1(o_ptr) : value_check_aux2(o_ptr));
+ }
+ else
+ {
+ feel = (heavy_magic ? value_check_aux1_magic(o_ptr) : value_check_aux2_magic(o_ptr));
+ }
+
+ /* Skip non-feelings */
+ if (feel == SENSE_NONE) continue;
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Message (equipment) */
+ if (i >= INVEN_WIELD)
+ {
+ msg_format("You feel the %s (%c) you are %s %s %s...",
+ o_name, index_to_label(i), describe_use(i),
+ ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]);
+ }
+
+ /* Message (inventory) */
+ else
+ {
+ msg_format("You feel the %s (%c) in your pack %s %s...",
+ o_name, index_to_label(i),
+ ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]);
+ }
+
+ /* We have "felt" it */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Set sense property */
+ o_ptr->sense = feel;
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+
+ /* Squelch ! */
+ squeltch_inventory();
+}
+
+
+/*
+ * Go to any level (ripped off from wiz_jump)
+ */
+static void pattern_teleport(void)
+{
+ /* Ask for level */
+ if (get_check("Teleport level? "))
+ {
+ char ppp[80];
+
+ char tmp_val[160];
+
+ /* Prompt */
+ sprintf(ppp, "Teleport to level (0-%d): ", 99);
+
+ /* Default */
+ sprintf(tmp_val, "%d", dun_level);
+
+ /* Ask for a level */
+ if (!get_string(ppp, tmp_val, 10)) return;
+
+ /* Extract request */
+ command_arg = atoi(tmp_val);
+ }
+ else if (get_check("Normal teleport? "))
+ {
+ teleport_player(200);
+ return;
+ }
+ else
+ {
+ return;
+ }
+
+ /* Paranoia */
+ if (command_arg < 0) command_arg = 0;
+
+ /* Paranoia */
+ if (command_arg > 99) command_arg = 99;
+
+ /* Accept request */
+ msg_format("You teleport to dungeon level %d.", command_arg);
+
+ autosave_checkpoint();
+
+ /* Change level */
+ dun_level = command_arg;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Returns TRUE if we are on the Straight Road...
+ */
+static bool_ pattern_effect(void)
+{
+ if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_START) ||
+ (cave[p_ptr->py][p_ptr->px].feat > FEAT_PATTERN_XTRA2)) return (FALSE);
+
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_END)
+ {
+ (void)set_poisoned(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ (void)set_blind(0);
+ (void)set_afraid(0);
+ (void)do_res_stat(A_STR, TRUE);
+ (void)do_res_stat(A_INT, TRUE);
+ (void)do_res_stat(A_WIS, TRUE);
+ (void)do_res_stat(A_DEX, TRUE);
+ (void)do_res_stat(A_CON, TRUE);
+ (void)do_res_stat(A_CHR, TRUE);
+ (void)restore_level();
+ (void)hp_player(1000);
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_PATTERN_OLD);
+ msg_print("This section of the Straight Road looks less powerful.");
+ }
+
+
+ /*
+ * We could make the healing effect of the
+ * Pattern center one-time only to avoid various kinds
+ * of abuse, like luring the win monster into fighting you
+ * in the middle of the pattern...
+ */
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_OLD)
+ {
+ /* No effect */
+ }
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA1)
+ {
+ pattern_teleport();
+ }
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA2)
+ {
+ if (!(p_ptr->invuln))
+ take_hit(200, "walking the corrupted Straight Road");
+ }
+
+ else
+ {
+ if (!(p_ptr->invuln))
+ take_hit(damroll(1, 3), "walking the Straight Road");
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * If player has inscribed the object with "!!", let him know when it's
+ * recharged. -LM-
+ */
+static void recharged_notice(object_type *o_ptr)
+{
+ char o_name[80];
+
+ cptr s;
+
+
+ /* No inscription */
+ if (!o_ptr->note) return;
+
+ /* Find a '!' */
+ s = strchr(quark_str(o_ptr->note), '!');
+
+ /* Process notification request. */
+ while (s)
+ {
+ /* Find another '!' */
+ if (s[1] == '!')
+ {
+ /* Describe (briefly) */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Notify the player */
+ if (o_ptr->number > 1)
+ {
+ msg_format("Your %s are recharged.", o_name);
+ }
+ else
+ {
+ msg_format("Your %s is recharged.", o_name);
+ }
+
+ /* Done. */
+ return;
+ }
+
+ /* Keep looking for '!'s */
+ s = strchr(s + 1, '!');
+ }
+}
+
+
+
+/*
+ * Regenerate hit points -RAK-
+ */
+static void regenhp(int percent)
+{
+ s32b new_chp, new_chp_frac;
+
+ int old_chp;
+
+
+ /* Only if alive */
+ if (!(p_ptr->necro_extra & CLASS_UNDEAD))
+ {
+ /* Save the old hitpoints */
+ old_chp = p_ptr->chp;
+
+ /* Extract the new hitpoints */
+ new_chp = ((long)p_ptr->mhp) * percent + PY_REGEN_HPBASE;
+
+ /* div 65536 */
+ p_ptr->chp += new_chp >> 16;
+
+ /* check for overflow */
+ if ((p_ptr->chp < 0) && (old_chp > 0)) p_ptr->chp = MAX_SHORT;
+
+ /* mod 65536 */
+ new_chp_frac = (new_chp & 0xFFFF) + p_ptr->chp_frac;
+
+ if (new_chp_frac >= 0x10000L)
+ {
+ p_ptr->chp_frac = new_chp_frac - 0x10000L;
+ p_ptr->chp++;
+ }
+ else
+ {
+ p_ptr->chp_frac = new_chp_frac;
+ }
+
+ /* Fully healed */
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+ }
+
+ /* Notice changes */
+ if (old_chp != p_ptr->chp)
+ {
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ }
+}
+
+
+/*
+ * Regenerate mana points -RAK-
+ */
+static void regenmana(int percent)
+{
+ s32b new_mana, new_mana_frac;
+
+ int old_csp;
+
+ /* Incraese regen with int */
+ percent += adj_str_blow[p_ptr->stat_ind[A_INT]] * 3;
+
+ old_csp = p_ptr->csp;
+ new_mana = ((long)p_ptr->msp) * percent + PY_REGEN_MNBASE;
+
+ /* div 65536 */
+ p_ptr->csp += new_mana >> 16;
+
+ /* check for overflow */
+ if ((p_ptr->csp < 0) && (old_csp > 0))
+ {
+ p_ptr->csp = MAX_SHORT;
+ }
+
+ /* mod 65536 */
+ new_mana_frac = (new_mana & 0xFFFF) + p_ptr->csp_frac;
+
+ if (new_mana_frac >= 0x10000L)
+ {
+ p_ptr->csp_frac = new_mana_frac - 0x10000L;
+ p_ptr->csp++;
+ }
+ else
+ {
+ p_ptr->csp_frac = new_mana_frac;
+ }
+
+ /* Must set frac to zero even if equal */
+ if (p_ptr->csp >= p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+ }
+
+ /* Redraw mana */
+ if (old_csp != p_ptr->csp)
+ {
+ /* Redraw */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+
+
+
+
+/*
+ * Regenerate the monsters (once per 100 game turns)
+ *
+ * XXX XXX XXX Should probably be done during monster turns.
+ */
+static void regen_monsters(void)
+{
+ int i, frac;
+
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+
+ if (o_ptr->k_idx)
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval];
+
+ /* Allow regeneration (if needed) */
+ if (o_ptr->pval2 < o_ptr->pval3)
+ {
+ /* Hack -- Base regeneration */
+ frac = o_ptr->pval3 / 100;
+
+ /* Hack -- Minimal regeneration rate */
+ if (!frac) frac = 1;
+
+ /* Hack -- Some monsters regenerate quickly */
+ if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2;
+
+
+ /* Hack -- Regenerate */
+ o_ptr->pval2 += frac;
+
+ /* Do not over-regenerate */
+ if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3;
+
+ /* Redraw (later) */
+ p_ptr->redraw |= (PR_MH);
+ }
+ }
+
+ /* Regenerate everyone */
+ for (i = 1; i < m_max; i++)
+ {
+ /* Check the i'th monster */
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Dont regen bleeding/poisonned monsters */
+ if (m_ptr->bleeding || m_ptr->poisoned) continue;
+
+ /* Allow regeneration (if needed) */
+ if (m_ptr->hp < m_ptr->maxhp)
+ {
+ /* Hack -- Base regeneration */
+ frac = m_ptr->maxhp / 100;
+
+ /* Hack -- Minimal regeneration rate */
+ if (!frac) frac = 1;
+
+ /* Hack -- Some monsters regenerate quickly */
+ if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2;
+
+
+ /* Hack -- Regenerate */
+ m_ptr->hp += frac;
+
+ /* Do not over-regenerate */
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == i) p_ptr->redraw |= (PR_HEALTH);
+ }
+ }
+}
+
+
+/*
+ * Does an object decay?
+ *
+ * Should belong to object1.c, renamed to object_decays() -- pelpel
+ */
+bool_ decays(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f3 & TR3_DECAY) return (TRUE);
+
+ return (FALSE);
+}
+
+
+static int process_lasting_spell(s16b music)
+{
+ int oldtop, use_mana;
+
+ if (music > 0) return FALSE;
+
+ oldtop = lua_gettop(L);
+
+ music = -music;
+
+ /* Push the function */
+ lua_getglobal(L, "exec_lasting_spell");
+
+ /* Push the spell */
+ tolua_pushnumber(L, music);
+
+ /* Call the function */
+ if (lua_call(L, 1, 1))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling lasting spell");
+ return 0;
+ }
+
+ use_mana = tolua_getnumber(L, -(lua_gettop(L) - oldtop), 0);
+ lua_settop(L, oldtop);
+ return use_mana;
+}
+
+static void gere_class_special()
+{
+ switch (p_ptr->druid_extra2)
+ {
+ /* Lay a path of mana on the floor */
+ case CLASS_MANA_PATH:
+ {
+ /* Does the player have enought mana ? */
+ if (p_ptr->csp < (s32b)(p_ptr->druid_extra & 255))
+ {
+ p_ptr->druid_extra = 0;
+ p_ptr->druid_extra2 = CLASS_NONE;
+ msg_print("You stop laying a mana path.");
+ }
+ else
+ {
+ /* Use some mana */
+ p_ptr->csp -= (p_ptr->druid_extra & 255);
+
+ if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE)
+ {
+ /* Absorb some of the mana of the grid */
+ p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 50;
+ if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp;
+
+ /* Set the new grid mana */
+ cave[p_ptr->py][p_ptr->px].mana = p_ptr->druid_extra & 255;
+ }
+ else
+ {
+ int m = cave[p_ptr->py][p_ptr->px].mana;
+
+ if (m + (p_ptr->druid_extra & 255) > 255)
+ {
+ cave[p_ptr->py][p_ptr->px].mana = 255;
+ }
+ else
+ {
+ cave[p_ptr->py][p_ptr->px].mana += p_ptr->druid_extra & 255;
+ }
+ }
+ }
+
+ break;
+ }
+
+ /* Lay a path of mana on the floor */
+ case CLASS_WINDS_MANA:
+ {
+ /* Does the player have enought mana ? */
+ if (p_ptr->csp < (s32b)(p_ptr->druid_extra & 255))
+ {
+ p_ptr->druid_extra = CLASS_NONE;
+ msg_print("You stop expulsing mana winds.");
+ }
+ else
+ {
+ int dam = 0;
+
+ /* Use some mana */
+ p_ptr->csp -= (p_ptr->druid_extra & 255);
+
+ if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE)
+ {
+ dam = (p_ptr->druid_extra & 255) + 256;
+ }
+ else
+ {
+ dam = (p_ptr->druid_extra & 255);
+ }
+
+ fire_explosion(p_ptr->py, p_ptr->px, GF_WINDS_MANA, 2, dam);
+ }
+
+ break;
+ }
+
+ case CLASS_CANALIZE_MANA:
+ {
+ if (p_ptr->druid_extra & CLASS_CANALIZE_MANA_EXTRA)
+ {
+ p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 10;
+ }
+ else
+ {
+ p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 20;
+ }
+
+ if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp;
+
+ cave[p_ptr->py][p_ptr->px].mana = 0;
+
+ break;
+ }
+
+ /* CLASS_NONE, possibly others? */
+ default:
+ {
+ /* No mana update */
+ return;
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+static void check_music()
+{
+ int use_mana;
+
+ /* Music sung by player */
+ if (!p_ptr->music_extra) return;
+
+ use_mana = process_lasting_spell(p_ptr->music_extra);
+
+ if (p_ptr->csp < use_mana)
+ {
+ msg_print("You stop your spell.");
+ p_ptr->music_extra = MUSIC_NONE;
+ p_ptr->music_extra2 = MUSIC_NONE;
+ }
+ else
+ {
+ p_ptr->csp -= use_mana;
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+/*
+ * Generate the feature effect
+ */
+void apply_effect(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ feature_type *f_ptr = &f_info[c_ptr->feat];
+
+
+ if (f_ptr->d_frequency[0] != 0)
+ {
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* Check the frequency */
+ if (f_ptr->d_frequency[i] == 0) continue;
+
+ if (((turn % f_ptr->d_frequency[i]) == 0) &&
+ ((f_ptr->d_side[i] != 0) || (f_ptr->d_dice[i] != 0)))
+ {
+ int l, dam = 0;
+ int d = f_ptr->d_dice[i], s = f_ptr->d_side[i];
+
+ if (d == -1) d = p_ptr->lev;
+ if (s == -1) s = p_ptr->lev;
+
+ /* Roll damage */
+ for (l = 0; l < d; l++)
+ {
+ dam += randint(s);
+ }
+
+ /* Apply damage */
+ project( -100, 0, y, x, dam, f_ptr->d_type[i],
+ PROJECT_KILL | PROJECT_HIDE);
+
+ /* Hack -- notice death */
+ if (!alive || death) return;
+ }
+ }
+ }
+}
+
+
+
+/* XXX XXX XXX */
+bool_ is_recall = FALSE;
+
+
+/*
+ * Handle certain things once every 10 game turns
+ *
+ * Note that a single movement in the overhead wilderness mode
+ * consumes 132 times as much energy as a normal one...
+ */
+static void process_world(void)
+{
+ timer_type *t_ptr;
+
+ int x, y, i, j;
+
+ int regen_amount;
+ bool_ cave_no_regen = FALSE;
+ int upkeep_factor = 0;
+
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ cave_type *c_ptr;
+
+ object_type *o_ptr;
+ u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0;
+
+
+ /*
+ * Every 10 game turns -- which means this section is invoked once
+ * in a player turn under the normal speed, and 132 times in a move
+ * in the reduced map mode.
+ */
+ if (turn % 10) return;
+
+ /*
+ * I don't know if this is the right thing to do because I'm totally
+ * ignorant (yes, I must admit that...) about the scripting part of
+ * the game, but since there have been complaints telling us it
+ * runs terribly slow in the reduced map mode... -- pelpel
+ *
+ * Note to coders: if it is desirable to make this active in the
+ * reduced map mode, remove the if condition surrounding the line
+ * and move the code inside into every 1000 game turns section.
+ */
+ if (dun_level || (!p_ptr->wild_mode))
+ {
+ /* Let the script live! */
+ process_hooks(HOOK_PROCESS_WORLD, "()");
+
+ /* Handle the player song */
+ check_music();
+ }
+
+ /* Handle the timers */
+ for (t_ptr = gl_timers; t_ptr != NULL; t_ptr = t_ptr->next)
+ {
+ if (!t_ptr->enabled) continue;
+
+ t_ptr->countdown--;
+ if (!t_ptr->countdown)
+ {
+ t_ptr->countdown = t_ptr->delay;
+ call_lua(t_ptr->callback, "()", "");
+ }
+ }
+
+ /* Handle class special actions */
+ gere_class_special();
+
+ /* Check the fate */
+ if (fate_option && (p_ptr->lev > 10))
+ {
+ /*
+ * WAS: == 666 against randint(50000).
+ * Since CPU's don't know Judeo-Christian / Cabalistic traditions,
+ * and since comparisons with zero is more efficient in many
+ * architectures...
+ */
+ if (rand_int(50000) == 0) gain_fate(0);
+ }
+
+ /*** Is the wielded monsters still hypnotised ***/
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (o_ptr->k_idx)
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval];
+
+ if ((randint(1000) < r_ptr->level - ((p_ptr->lev * 2) + get_skill(SKILL_SYMBIOTIC))))
+ {
+ msg_format("%s breaks free from hypnosis!",
+ symbiote_name(TRUE));
+ carried_make_attack_normal(o_ptr->pval);
+ }
+ }
+
+ /*** Attempt timed autosave ***/
+ if (autosave_t && autosave_freq)
+ {
+ if ((turn % ((s32b)autosave_freq * 10)) == 0)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+ }
+
+
+ /*** Handle the wilderness/town (sunshine) ***/
+
+ /* While in town/wilderness and not in the overworld mode */
+ if (!dun_level && !p_ptr->wild_mode)
+ {
+ /* Hack -- Daybreak/Nighfall in town */
+ if ((turn % ((10L * DAY) / 2)) == 0)
+ {
+ bool_ dawn;
+
+ /* Check for dawn */
+ dawn = ((turn % (10L * DAY)) == 0);
+
+ /* Day breaks */
+ if (dawn)
+ {
+ /* Message */
+ msg_print("The sun has risen.");
+
+ /* Hack -- Scan the town */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Assume lit */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Hack -- Memorize lit grids if allowed */
+ if (view_perma_grids) c_ptr->info |= (CAVE_MARK);
+
+ /* Hack -- Notice spot */
+ note_spot(y, x);
+ }
+ }
+ }
+
+ /* Night falls */
+ else
+ {
+ /* Message */
+ msg_print("The sun has set.");
+
+ /* Hack -- Scan the town */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Darken "boring" features */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Hack -- Notice spot */
+ note_spot(y, x);
+ }
+ }
+ }
+ }
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+
+ /* Tell a day passed */
+ if (((turn + (DAY_START * 10L)) % (10L * DAY)) == 0)
+ {
+ char buf[20];
+
+ sprintf(buf, "%s", get_day(bst(YEAR, turn) + START_YEAR));
+ cmsg_format(TERM_L_GREEN,
+ "Today it is %s of the %s year of the third age.",
+ get_month_name(bst(DAY, turn), wizard, FALSE), buf);
+ }
+
+ /* Set back the rewards once a day */
+ if ((turn % (10L * STORE_TURNS)) == 0)
+ {
+ /* Select new bounties. */
+ if (magik(20)) select_bounties();
+ }
+
+ /* Modify loan */
+ if (p_ptr->loan)
+ {
+ if (p_ptr->loan_time) p_ptr->loan_time--;
+
+ if (((turn % 5000) == 0) && !p_ptr->loan_time)
+ {
+ cmsg_print(TERM_RED, "You should pay your loan...");
+
+ p_ptr->loan += p_ptr->loan / 12;
+
+ if (p_ptr->loan > PY_MAX_GOLD) p_ptr->loan = PY_MAX_GOLD;
+
+ /* Do a nasty stuff */
+ if (p_ptr->wild_mode && rand_int(2))
+ {
+ /* Discount player items */
+ int z = 0, tries = 200;
+ object_type *o_ptr = NULL;
+
+ while (tries--)
+ {
+ z = rand_int(INVEN_TOTAL);
+ o_ptr = &p_ptr->inventory[z];
+
+ if (!o_ptr->k_idx) continue;
+
+ if (o_ptr->discount >= 100) continue;
+
+ break;
+ }
+
+ if (tries)
+ {
+ o_ptr->discount += 70;
+ if (o_ptr->discount >= 100) o_ptr->discount = 100;
+
+ inven_item_optimize(z);
+ inven_item_describe(z);
+
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+ }
+
+ else
+ {
+ int merc = test_monster_name("Mean-looking mercenary");
+ int agent = test_monster_name("Agent of the black market");
+ int num = 5 + (p_ptr->lev / 3), z;
+
+ for (z = 0; z < num; z++)
+ {
+ int yy, xx, attempts = 200, m_idx;
+
+ /* Summon */
+ while (1)
+ {
+ scatter(&yy, &xx, p_ptr->py, p_ptr->px, 6);
+
+ /* Accept an empty grid within the boundary */
+ if (in_bounds(yy, xx) && cave_floor_bold(yy, xx)) break;
+
+ /* Max number of retries reached */
+ if (--attempts == 0) break;
+ }
+
+ /* All the attempts failed */
+ if (attempts == 0) continue;
+
+ /* Summon a monster */
+ m_idx = place_monster_one(yy, xx, magik(80) ? merc : agent,
+ 0, FALSE, MSTATUS_ENEMY);
+
+ /* Level it */
+ if (m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+
+ m_ptr->exp = MONSTER_EXP(p_ptr->lev * 2);
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+ }
+ }
+
+ /*** Process the monsters ***/
+
+ /* Check for creature generation. */
+ if (!p_ptr->wild_mode &&
+ !p_ptr->inside_arena &&
+ !p_ptr->inside_quest &&
+ (rand_int(d_info[(dun_level) ? dungeon_type : DUNGEON_WILDERNESS].max_m_alloc_chance) == 0))
+ {
+ /* Make a new monster */
+ if (!(dungeon_flags2 & DF2_NO_NEW_MONSTER))
+ {
+ (void)alloc_monster(MAX_SIGHT + 5, FALSE);
+ }
+ }
+
+ /* Hack -- Check for creature regeneration */
+ if (!p_ptr->wild_mode && ((turn % 100) == 0)) regen_monsters();
+
+
+ /*** Damage over Time ***/
+
+ /* Take damage from poison */
+ if (p_ptr->poisoned && !p_ptr->invuln)
+ {
+ /* Take damage */
+ take_hit(1, "poison");
+ }
+
+
+ /* Vampires take damage from sunlight */
+ if (p_ptr->sensible_lite)
+ {
+ if ((!dun_level) && (((turn / ((10L * DAY) / 2)) % 2) == 0))
+ {
+ if (cave[p_ptr->py][p_ptr->px].info & (CAVE_GLOW))
+ {
+ /* Take damage */
+ msg_print("The sun's rays scorch your undead flesh!");
+ take_hit(1, "sunlight");
+ cave_no_regen = TRUE;
+ drop_from_wild();
+ }
+ }
+
+ if ((p_ptr->inventory[INVEN_LITE].tval != 0) &&
+ (p_ptr->inventory[INVEN_LITE].sval >= SV_LITE_GALADRIEL) &&
+ (p_ptr->inventory[INVEN_LITE].sval <= SV_STONE_LORE) &&
+ (p_ptr->inventory[INVEN_LITE].sval != SV_LITE_UNDEATH))
+ {
+ object_type * o_ptr = &p_ptr->inventory[INVEN_LITE];
+ char o_name [80];
+ char ouch [80];
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ msg_format("The %s scorches your undead flesh!", o_name);
+
+ cave_no_regen = TRUE;
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, TRUE, 0);
+
+ sprintf(ouch, "wielding %s", o_name);
+ take_hit(1, ouch);
+ }
+ }
+
+ /* Drown in deep water unless the player have levitation, water walking
+ water breathing, or magic breathing.*/
+ if (!p_ptr->ffall && !p_ptr->walk_water && !p_ptr->magical_breath &&
+ !p_ptr->water_breath &&
+ (cave[p_ptr->py][p_ptr->px].feat == FEAT_DEEP_WATER))
+ {
+ if (calc_total_weight() > ((weight_limit()) / 2))
+ {
+ /* Take damage */
+ msg_print("You are drowning!");
+ take_hit(randint(p_ptr->lev), "drowning");
+ cave_no_regen = TRUE;
+ }
+ }
+
+
+ /* Spectres -- take damage when moving through walls */
+
+ /*
+ * Added: ANYBODY takes damage if inside through walls
+ * without wraith form -- NOTE: Spectres will never be
+ * reduced below 0 hp by being inside a stone wall; others
+ * WILL BE!
+ */
+ if (!cave_floor_bold(p_ptr->py, p_ptr->px))
+ {
+ int feature = cave[p_ptr->py][p_ptr->px].feat;
+
+ /* Player can walk through or fly over trees */
+ if ((has_ability(AB_TREE_WALK) || p_ptr->fly) && (feature == FEAT_TREES))
+ {
+ /* Do nothing */
+ }
+ /* Player can climb over mountains */
+ else if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB))
+ {
+ /* Do nothing */
+ }
+ else if (PRACE_FLAG(PR1_SEMI_WRAITH) && (!p_ptr->wraith_form) && (f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_CAN_PASS))
+ {
+ int amt = 1 + ((p_ptr->lev) / 5);
+
+ cave_no_regen = TRUE;
+ if (amt > p_ptr->chp - 1) amt = p_ptr->chp - 1;
+ take_hit(amt, " walls ...");
+ }
+ }
+
+
+ /* Take damage from cuts */
+ if ((p_ptr->cut) && !(p_ptr->invuln))
+ {
+ /* Mortal wound or Deep Gash */
+ if (p_ptr->cut > 200)
+ {
+ i = 3;
+ }
+
+ /* Severe cut */
+ else if (p_ptr->cut > 100)
+ {
+ i = 2;
+ }
+
+ /* Other cuts */
+ else
+ {
+ i = 1;
+ }
+
+ /* Take damage */
+ take_hit(i, "a fatal wound");
+ }
+
+
+ /*** Check the Food, and Regenerate ***/
+
+ /* Digest normally */
+ if (p_ptr->food < PY_FOOD_MAX)
+ {
+ /* Every 100 game turns */
+ if ((turn % 100) == 0)
+ {
+ int speed_use = p_ptr->pspeed;
+
+ /* Maximum */
+ if (speed_use > 199)
+ {
+ speed_use = 199;
+ }
+
+ /* Minimum */
+ else if (speed_use < 0)
+ {
+ speed_use = 0;
+ }
+
+ /* Basic digestion rate based on speed */
+ i = extract_energy[speed_use] * 2;
+
+ /* Regeneration takes more food */
+ if (p_ptr->regenerate) i += 30;
+
+ /* Regeneration takes more food */
+ if (p_ptr->tim_regen) i += p_ptr->tim_regen_pow / 10;
+
+ /* Invisibility consume a lot of food */
+ i += p_ptr->invis / 2;
+
+ /* Invulnerability consume a lot of food */
+ if (p_ptr->invuln) i += 40;
+
+ /* Wraith Form consume a lot of food */
+ if (p_ptr->wraith_form) i += 30;
+
+ /* Get the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Examine the sword */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hitpoints multiplier consume a lot of food */
+ if (o_ptr->k_idx && (f2 & (TR2_LIFE))) i += o_ptr->pval * 5;
+
+ /* Slow digestion takes less food */
+ if (p_ptr->slow_digest) i -= 10;
+
+ /* Minimal digestion */
+ if (i < 1) i = 1;
+
+ /* Digest some food */
+ (void)set_food(p_ptr->food - i);
+ }
+ }
+
+ /* Digest quickly when gorged */
+ else
+ {
+ /* Digest a lot of food */
+ (void)set_food(p_ptr->food - 100);
+ }
+
+ /* Starve to death (slowly) */
+ if (p_ptr->food < PY_FOOD_STARVE)
+ {
+ /* Calculate damage */
+ i = (PY_FOOD_STARVE - p_ptr->food) / 10;
+
+ /* Take damage */
+ if (!(p_ptr->invuln)) take_hit(i, "starvation");
+ }
+
+ /* Default regeneration */
+ regen_amount = PY_REGEN_NORMAL;
+
+ /* Getting Weak */
+ if (p_ptr->food < PY_FOOD_WEAK)
+ {
+ /* Lower regeneration */
+ if (p_ptr->food < PY_FOOD_STARVE)
+ {
+ regen_amount = 0;
+ }
+ else if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ regen_amount = PY_REGEN_FAINT;
+ }
+ else
+ {
+ regen_amount = PY_REGEN_WEAK;
+ }
+
+ /* Getting Faint */
+ if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ /* Faint occasionally */
+ if (!p_ptr->paralyzed && (rand_int(100) < 10))
+ {
+ /* Message */
+ msg_print("You faint from the lack of food.");
+ disturb(1, 0);
+
+ /* Hack -- faint (bypass free action) */
+ (void)set_paralyzed(p_ptr->paralyzed + 1 + rand_int(5));
+ }
+ }
+ }
+
+ /* Are we walking the pattern? */
+ if (!p_ptr->wild_mode && pattern_effect())
+ {
+ cave_no_regen = TRUE;
+ }
+ else
+ {
+ /* Regeneration ability */
+ if (p_ptr->regenerate)
+ {
+ regen_amount = regen_amount * 2;
+ }
+ }
+
+
+ /* Searching or Resting */
+ if (p_ptr->searching || resting)
+ {
+ regen_amount = regen_amount * 2;
+ }
+
+ if (total_friends)
+ {
+ int upkeep_divider = 20;
+
+ if (has_ability(AB_PERFECT_CASTING))
+ upkeep_divider = 15;
+
+#ifdef TRACK_FRIENDS
+
+ if (wizard) msg_format("Total friends: %d.", total_friends);
+
+#endif /* TRACK_FRIENDS */
+
+ if (total_friends > 1 + (p_ptr->lev / (upkeep_divider)))
+ {
+ upkeep_factor = (total_friend_levels);
+
+ if (upkeep_factor > 100) upkeep_factor = 100;
+ else if (upkeep_factor < 10) upkeep_factor = 10;
+
+#ifdef TRACK_FRIENDS
+
+ if (wizard) msg_format("Levels %d, upkeep %d", total_friend_levels,
+ upkeep_factor);
+
+#endif /* TRACK_FRIENDS */
+ }
+ }
+
+ /* Regenerate the mana */
+ if (p_ptr->csp < p_ptr->msp)
+ {
+ if (upkeep_factor)
+ {
+ s16b upkeep_regen = (((100 - upkeep_factor) * regen_amount) / 100);
+ regenmana(upkeep_regen);
+
+#ifdef TRACK_FRIENDS
+
+ if (wizard)
+ {
+ msg_format("Regen: %d/%d", upkeep_regen, regen_amount);
+ }
+
+#endif /* TRACK_FRIENDS */
+ }
+ else
+ {
+ regenmana(regen_amount);
+ }
+ }
+
+ /* Eru piety incraese with time */
+ if (((turn % 100) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode))
+ {
+ NOT_PRAY_GOD(GOD_ERU)
+ {
+ int inc = wisdom_scale(10);
+
+ /* Increase by wisdom/4 */
+ if (!inc) inc = 1;
+ inc_piety(GOD_ERU, inc);
+ }
+ }
+ /* Most gods piety decrease with time */
+ if (((turn % 300) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level))
+ {
+ GOD(GOD_MANWE)
+ {
+ int dec = 4 - wisdom_scale(3);
+
+ PRAY_GOD(GOD_MANWE)
+ dec++;
+ if (PRACE_FLAG(PR1_ELF))
+ dec -= wisdom_scale(2);
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_MANWE, -dec);
+ }
+ GOD(GOD_MELKOR)
+ {
+ int dec = 8 - wisdom_scale(6);
+
+ PRAY_GOD(GOD_MELKOR)
+ dec++;
+ if (PRACE_FLAG(PR1_ELF))
+ dec += 5 - wisdom_scale(4);
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_MELKOR, -dec);
+ }
+ PRAY_GOD(GOD_TULKAS)
+ {
+ int dec = 4 - wisdom_scale(3);
+
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_TULKAS, -dec);
+ }
+ }
+ /* Yavanna piety decrease with time */
+ if (((turn % 400) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level))
+ {
+ GOD(GOD_YAVANNA)
+ {
+ int dec = 5 - wisdom_scale(3);
+
+ /* Blech what an hideous hack */
+ if (!strcmp(rp_ptr->title + rp_name, "Ent"))
+ dec -= wisdom_scale(2);
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_YAVANNA, -dec);
+ }
+ }
+ p_ptr->did_nothing = FALSE;
+
+ /* Increase regen by tim regen */
+ if (p_ptr->tim_regen) regen_amount += p_ptr->tim_regen_pow;
+
+ /* Poisoned or cut yields no healing */
+ if (p_ptr->poisoned) regen_amount = 0;
+ if (p_ptr->cut) regen_amount = 0;
+
+ /* Special floor -- Pattern, in a wall -- yields no healing */
+ if (cave_no_regen) regen_amount = 0;
+
+ /* Being over grass allows Yavanna to regen you */
+ PRAY_GOD(GOD_YAVANNA)
+ {
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_GRASS)
+ {
+ regen_amount += 200 + wisdom_scale(800);
+ }
+ }
+
+ /* Regenerate Hit Points if needed */
+ if ((p_ptr->chp < p_ptr->mhp) && !cave_no_regen)
+ {
+ if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_END) &&
+ (cave[p_ptr->py][p_ptr->px].feat >= FEAT_PATTERN_START))
+ {
+ /* Hmmm. this should never happen? */
+ regenhp(regen_amount / 5);
+ }
+ else
+ {
+ regenhp(regen_amount);
+ }
+ }
+
+
+ /*** Timeout Various Things ***/
+
+ /* Handle temporary stat drains */
+ for (i = 0; i < 6; i++)
+ {
+ if (p_ptr->stat_cnt[i] > 0)
+ {
+ p_ptr->stat_cnt[i]--;
+ if (p_ptr->stat_cnt[i] == 0)
+ {
+ do_res_stat(i, FALSE);
+ }
+ }
+ }
+
+ /* Hack -- Hallucinating */
+ if (p_ptr->image)
+ {
+ (void)set_image(p_ptr->image - 1);
+ }
+
+ /* Holy Aura */
+ if (p_ptr->holy)
+ {
+ (void)set_holy(p_ptr->holy - 1);
+ }
+
+ /* Soul absorbtion */
+ if (p_ptr->absorb_soul)
+ {
+ (void)set_absorb_soul(p_ptr->absorb_soul - 1);
+ }
+
+ /* Undead loose Death Points */
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ int old_chp = p_ptr->chp;
+ int warning = (p_ptr->mhp * hitpoint_warn / 10);
+
+ /* Bypass invulnerability and wraithform */
+ p_ptr->chp--;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Dead player */
+ if (p_ptr->chp < 0)
+ {
+ bool_ old_quick = quick_messages;
+
+ /* Sound */
+ sound(SOUND_DEATH);
+
+ /* Hack -- Note death */
+ if (!last_words)
+ {
+ msg_print("You die.");
+ msg_print(NULL);
+ }
+ else
+ {
+ char death_message[80];
+
+ (void)get_rnd_line("death.txt", death_message);
+ msg_print(death_message);
+ }
+
+ /* Note cause of death */
+ (void)strcpy(died_from, "being undead too long");
+
+ if (p_ptr->image) strcat(died_from, "(?)");
+
+ /* No longer a winner */
+ total_winner = FALSE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Note death */
+ death = TRUE;
+
+ quick_messages = FALSE;
+ if (get_check("Make a last screenshot? "))
+ {
+ do_cmd_html_dump();
+ }
+ quick_messages = old_quick;
+
+ /* Dead */
+ return;
+ }
+
+ /* Hitpoint warning */
+ if (p_ptr->chp < warning)
+ {
+ /* Hack -- bell on first notice */
+ if (alert_hitpoint && (old_chp > warning)) bell();
+
+ sound(SOUND_WARN);
+
+ /* Message */
+ msg_print("*** LOW DEATHPOINT WARNING! ***");
+ msg_print(NULL);
+ }
+ }
+
+ /* Walk water */
+ if (p_ptr->walk_water)
+ {
+ (void)set_walk_water(p_ptr->walk_water - 1);
+ }
+
+ /* True Strike */
+ if (p_ptr->strike)
+ {
+ (void)set_strike(p_ptr->strike - 1);
+ }
+
+ /* Meditation */
+ if (p_ptr->meditation)
+ {
+ (void)set_meditation(p_ptr->meditation - 1);
+ }
+
+ /* Timed project */
+ if (p_ptr->tim_project)
+ {
+ (void)set_project(p_ptr->tim_project - 1, p_ptr->tim_project_gf, p_ptr->tim_project_dam, p_ptr->tim_project_rad, p_ptr->tim_project_flag);
+ }
+
+ /* Timed roots */
+ if (p_ptr->tim_roots)
+ {
+ (void)set_roots(p_ptr->tim_roots - 1, p_ptr->tim_roots_ac, p_ptr->tim_roots_dam);
+ }
+
+ /* Timed breath */
+ if (p_ptr->tim_water_breath)
+ {
+ (void)set_tim_breath(p_ptr->tim_water_breath - 1, FALSE);
+ }
+ if (p_ptr->tim_magic_breath)
+ {
+ (void)set_tim_breath(p_ptr->tim_magic_breath - 1, TRUE);
+ }
+
+ /* Timed regen */
+ if (p_ptr->tim_regen)
+ {
+ (void)set_tim_regen(p_ptr->tim_regen - 1, p_ptr->tim_regen_pow);
+ }
+
+ /* Timed Disrupt shield */
+ if (p_ptr->disrupt_shield)
+ {
+ (void)set_disrupt_shield(p_ptr->disrupt_shield - 1);
+ }
+
+ /* Timed Parasite */
+ if (p_ptr->parasite)
+ {
+ (void)set_parasite(p_ptr->parasite - 1, p_ptr->parasite_r_idx);
+ }
+
+ /* Timed Reflection */
+ if (p_ptr->tim_reflect)
+ {
+ (void)set_tim_reflect(p_ptr->tim_reflect - 1);
+ }
+
+ /* Timed Prob Travel */
+ if (p_ptr->prob_travel)
+ {
+ (void)set_prob_travel(p_ptr->prob_travel - 1);
+ }
+
+ /* Timed Time Resistance */
+ if (p_ptr->tim_res_time)
+ {
+ (void)set_tim_res_time(p_ptr->tim_res_time - 1);
+ }
+
+ /* Timed Levitation */
+ if (p_ptr->tim_ffall)
+ {
+ (void)set_tim_ffall(p_ptr->tim_ffall - 1);
+ }
+ if (p_ptr->tim_fly)
+ {
+ (void)set_tim_fly(p_ptr->tim_fly - 1);
+ }
+
+ /* Thunderstorm */
+ if (p_ptr->tim_thunder)
+ {
+ int dam = damroll(p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2);
+ int i, tries = 600;
+ monster_type *m_ptr = NULL;
+
+ while (tries)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i = rand_range(1, m_max - 1)];
+
+ tries--;
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Cant see ? cant hit */
+ if (!los(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx)) continue;
+
+ /* Do not hurt friends! */
+ if (is_friend(m_ptr) >= 0) continue;
+ break;
+ }
+
+ if (tries)
+ {
+ char m_name[80];
+
+ monster_desc(m_name, m_ptr, 0);
+ msg_format("Lightning strikes %s.", m_name);
+ project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_ELEC,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_LITE,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_SOUND,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+
+ (void)set_tim_thunder(p_ptr->tim_thunder - 1, p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2);
+ }
+
+ /* Poisonned hands */
+ if (p_ptr->tim_poison)
+ {
+ (void)set_poison(p_ptr->tim_poison - 1);
+ }
+
+ /* Timed Fire Aura */
+ if (p_ptr->tim_fire_aura)
+ {
+ (void)set_tim_fire_aura(p_ptr->tim_fire_aura - 1);
+ }
+
+ /* Brightness */
+ if (p_ptr->tim_lite)
+ {
+ (void)set_lite(p_ptr->tim_lite - 1);
+ }
+
+ /* Blindness */
+ if (p_ptr->blind)
+ {
+ (void)set_blind(p_ptr->blind - 1);
+ }
+
+ /* Timed no_breeds */
+ if (no_breeds)
+ {
+ (void)set_no_breeders(no_breeds - 1);
+ }
+
+ /* Timed mimic */
+ if (p_ptr->tim_mimic)
+ {
+ (void)set_mimic(p_ptr->tim_mimic - 1, p_ptr->mimic_form, p_ptr->mimic_level);
+ }
+
+ /* Timed special move commands */
+ if (p_ptr->immov_cntr)
+ {
+ p_ptr->immov_cntr--;
+ }
+
+ /* Timed invisibility */
+ if (p_ptr->tim_invisible)
+ {
+ (void)set_invis(p_ptr->tim_invisible - 1, p_ptr->tim_inv_pow);
+ }
+
+ /* Times see-invisible */
+ if (p_ptr->tim_invis)
+ {
+ (void)set_tim_invis(p_ptr->tim_invis - 1);
+ }
+
+ if (multi_rew)
+ {
+ multi_rew = FALSE;
+ }
+
+ /* Timed esp */
+ if (p_ptr->tim_esp)
+ {
+ (void)set_tim_esp(p_ptr->tim_esp - 1);
+ }
+
+ /* Timed infra-vision */
+ if (p_ptr->tim_infra)
+ {
+ (void)set_tim_infra(p_ptr->tim_infra - 1);
+ }
+
+ /* Paralysis */
+ if (p_ptr->paralyzed)
+ {
+ (void)set_paralyzed(p_ptr->paralyzed - 1);
+ }
+
+ /* Confusion */
+ if (p_ptr->confused)
+ {
+ (void)set_confused(p_ptr->confused - 1);
+ }
+
+ /* Afraid */
+ if (p_ptr->afraid)
+ {
+ (void)set_afraid(p_ptr->afraid - 1);
+ }
+
+ /* Fast */
+ if (p_ptr->fast)
+ {
+ (void)set_fast(p_ptr->fast - 1, p_ptr->speed_factor);
+ }
+
+ /* Light speed */
+ if (p_ptr->lightspeed)
+ {
+ (void)set_light_speed(p_ptr->lightspeed - 1);
+ }
+
+ /* Slow */
+ if (p_ptr->slow)
+ {
+ (void)set_slow(p_ptr->slow - 1);
+ }
+
+ /* Protection from evil */
+ if (p_ptr->protevil)
+ {
+ (void)set_protevil(p_ptr->protevil - 1);
+ }
+
+ /* Protection from good */
+ if (p_ptr->protgood)
+ {
+ (void)set_protgood(p_ptr->protgood - 1);
+ }
+
+ /* Protection from undead */
+ if (p_ptr->protundead)
+ {
+ (void)set_protundead(p_ptr->protundead - 1);
+ }
+
+ /* Invulnerability */
+ if (p_ptr->invuln)
+ {
+ (void)set_invuln(p_ptr->invuln - 1);
+ }
+
+ /* Wraith form */
+ if (p_ptr->tim_wraith)
+ {
+ (void)set_shadow(p_ptr->tim_wraith - 1);
+ }
+
+ /* Heroism */
+ if (p_ptr->hero)
+ {
+ (void)set_hero(p_ptr->hero - 1);
+ }
+
+ /* Super Heroism */
+ if (p_ptr->shero)
+ {
+ (void)set_shero(p_ptr->shero - 1);
+ }
+
+ /* Blessed */
+ if (p_ptr->blessed)
+ {
+ (void)set_blessed(p_ptr->blessed - 1);
+ }
+
+ /* Shield */
+ if (p_ptr->shield)
+ {
+ (void)set_shield(p_ptr->shield - 1, p_ptr->shield_power, p_ptr->shield_opt, p_ptr->shield_power_opt, p_ptr->shield_power_opt2);
+ }
+
+ /* Oppose Acid */
+ if (p_ptr->oppose_acid)
+ {
+ (void)set_oppose_acid(p_ptr->oppose_acid - 1);
+ }
+
+ /* Oppose Lightning */
+ if (p_ptr->oppose_elec)
+ {
+ (void)set_oppose_elec(p_ptr->oppose_elec - 1);
+ }
+
+ /* Oppose Fire */
+ if (p_ptr->oppose_fire)
+ {
+ (void)set_oppose_fire(p_ptr->oppose_fire - 1);
+ }
+
+ /* Oppose Cold */
+ if (p_ptr->oppose_cold)
+ {
+ (void)set_oppose_cold(p_ptr->oppose_cold - 1);
+ }
+
+ /* Oppose Poison */
+ if (p_ptr->oppose_pois)
+ {
+ (void)set_oppose_pois(p_ptr->oppose_pois - 1);
+ }
+
+ /* Oppose Light & Dark */
+ if (p_ptr->oppose_ld)
+ {
+ (void)set_oppose_ld(p_ptr->oppose_ld - 1);
+ }
+
+ /* Oppose Chaos & Confusion */
+ if (p_ptr->oppose_cc)
+ {
+ (void)set_oppose_cc(p_ptr->oppose_cc - 1);
+ }
+
+ /* Oppose Sound & Shards */
+ if (p_ptr->oppose_ss)
+ {
+ (void)set_oppose_ss(p_ptr->oppose_ss - 1);
+ }
+
+ /* Oppose Nexus */
+ if (p_ptr->oppose_nex)
+ {
+ (void)set_oppose_nex(p_ptr->oppose_nex - 1);
+ }
+
+ /* Mental Barrier */
+ if (p_ptr->tim_mental_barrier)
+ {
+ (void)set_mental_barrier(p_ptr->tim_mental_barrier - 1);
+ }
+
+ /* The rush */
+ if (p_ptr->rush)
+ {
+ (void)set_rush(p_ptr->rush - 1);
+ }
+
+
+ /* Timed mimicry */
+ if (get_skill(SKILL_MIMICRY))
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ if ((att & CLASS_LEGS) || (att & CLASS_WALL) || (att & CLASS_ARMS))
+ {
+ value--;
+
+ if (!value)
+ {
+ if (att & CLASS_LEGS) msg_print("You lose your extra pair of legs.");
+ if (att & CLASS_ARMS) msg_print("You lose your extra pair of arms.");
+ if (att & CLASS_WALL) msg_print("You lose your affinity for walls.");
+
+ att &= ~(CLASS_ARMS);
+ att &= ~(CLASS_LEGS);
+ att &= ~(CLASS_WALL);
+
+ if (disturb_state) disturb(0, 0);
+ }
+
+ p_ptr->update |= (PU_BODY);
+ p_ptr->mimic_extra = att + (value << 16);
+ }
+ }
+
+
+ /*** Poison and Stun and Cut ***/
+
+ /* Poison */
+ if (p_ptr->poisoned)
+ {
+ int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1);
+
+ /* Apply some healing */
+ (void)set_poisoned(p_ptr->poisoned - adjust);
+ }
+
+ /* Stun */
+ if (p_ptr->stun)
+ {
+ int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1);
+
+ /* Apply some healing */
+ (void)set_stun(p_ptr->stun - adjust);
+ }
+
+ /* Cut */
+ if (p_ptr->cut)
+ {
+ int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1);
+
+ /* Hack -- Truly "mortal" wound */
+ if (p_ptr->cut > 1000) adjust = 0;
+
+ /* Apply some healing */
+ (void)set_cut(p_ptr->cut - adjust);
+ }
+
+ /* Hack - damage done by the dungeon -SC- */
+ if ((dun_level != 0) && (d_ptr->d_frequency[0] != 0))
+ {
+ int i, j, k;
+
+ /* Apply damage to every grid in the dungeon */
+ for (i = 0; i < 4; i++)
+ {
+ /* Check the frequency */
+ if (d_ptr->d_frequency[i] == 0) continue;
+
+ if (((turn % d_ptr->d_frequency[i]) == 0) &&
+ ((d_ptr->d_side[i] != 0) || (d_ptr->d_dice[i] != 0)))
+ {
+ for (j = 0; j < cur_hgt - 1; j++)
+ {
+ for (k = 0; k < cur_wid - 1; k++)
+ {
+ int l, dam = 0;
+
+ if (!(dungeon_flags1 & DF1_DAMAGE_FEAT))
+ {
+ /* If the grid is empty, skip it */
+ if ((cave[j][k].o_idx == 0) &&
+ ((j != p_ptr->py) && (i != p_ptr->px))) continue;
+ }
+
+ /* Let's not hurt poor monsters */
+ if (cave[j][k].m_idx) continue;
+
+ /* Roll damage */
+ for (l = 0; l < d_ptr->d_dice[i]; l++)
+ {
+ dam += randint(d_ptr->d_side[i]);
+ }
+
+ /* Apply damage */
+ project( -100, 0, j, k, dam, d_ptr->d_type[i],
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ }
+ }
+ }
+ }
+
+ /* handle spell effects */
+ if (!p_ptr->wild_mode)
+ {
+ /*
+ * I noticed significant performance degrade after the introduction
+ * of staying spell effects. I believe serious optimisation effort
+ * is required before another release.
+ *
+ * More important is to fix that display weirdness...
+ *
+ * It seems that the game never expects that monster deaths and
+ * terrain feature changes should happen here... Moving these
+ * to process_player() [before resting code, with "every 10 game turn"
+ * 'if'] may or may not fix the problem... -- pelpel to DG
+ */
+ for (j = 0; j < cur_hgt - 1; j++)
+ {
+ for (i = 0; i < cur_wid - 1; i++)
+ {
+ int e = cave[j][i].effect;
+
+ if (e)
+ {
+ effect_type *e_ptr = &effects[e];
+
+ if (e_ptr->time)
+ {
+ /* Apply damage */
+ project(0, 0, j, i, e_ptr->dam, e_ptr->type,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ else
+ {
+ cave[j][i].effect = 0;
+ }
+
+ if ((e_ptr->flags & EFF_WAVE) && !(e_ptr->flags & EFF_LAST))
+ {
+ if (distance(e_ptr->cy, e_ptr->cx, j, i) < e_ptr->rad - 1)
+ cave[j][i].effect = 0;
+ }
+ else if ((e_ptr->flags & EFF_STORM) && !(e_ptr->flags & EFF_LAST))
+ {
+ cave[j][i].effect = 0;
+ }
+
+ lite_spot(j, i);
+ }
+ }
+ }
+
+ /* Reduce & handle effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ /* Skip empty slots */
+ if (effects[i].time == 0) continue;
+
+ /* Reduce duration */
+ effects[i].time--;
+
+ /* Creates a "wave" effect*/
+ if (effects[i].flags & EFF_WAVE)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y, z;
+
+ e_ptr->rad++;
+
+ /* What a frelling ugly line of ifs ... */
+ if (effects[i].flags & EFF_DIR8)
+ for (y = e_ptr->cy - e_ptr->rad, z = 0; y <= e_ptr->cy; y++, z++)
+ {
+ for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR2)
+ for (y = e_ptr->cy, z = e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++, z--)
+ {
+ for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR6)
+ for (x = e_ptr->cx, z = e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++, z--)
+ {
+ for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR4)
+ for (x = e_ptr->cx - e_ptr->rad, z = 0; x <= e_ptr->cx; x++, z++)
+ {
+ for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR9)
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++)
+ {
+ for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR1)
+ for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR7)
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR3)
+ for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ /* This is *slow* -- pelpel */
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ }
+ /* Creates a "storm" effect*/
+ else if (effects[i].flags & EFF_STORM)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y;
+
+ e_ptr->cy = p_ptr->py;
+ e_ptr->cx = p_ptr->px;
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) <= e_ptr->rad))
+ {
+ cave[y][x].effect = i;
+ lite_spot(y, x);
+ }
+ }
+ }
+ }
+ }
+
+ apply_effect(p_ptr->py, p_ptr->px);
+ }
+
+ /* Arg cannot breath? */
+ if ((dungeon_flags2 & DF2_WATER_BREATH) && (!p_ptr->water_breath))
+ {
+ cmsg_print(TERM_L_RED, "You cannot breathe water! You suffocate!");
+ take_hit(damroll(3, p_ptr->lev), "suffocating");
+ }
+ if ((dungeon_flags2 & DF2_NO_BREATH) && (!p_ptr->magical_breath))
+ {
+ cmsg_print(TERM_L_RED, "There is no air there! You suffocate!");
+ take_hit(damroll(3, p_ptr->lev), "suffocating");
+ }
+
+ /*
+ * Every 1500 turns, warn about any Black Breath not gotten from
+ * an equipped object, and stop any resting. -LM-
+ *
+ * It's apparent that someone has halved the frequency... -- pelpel
+ */
+ if (((turn % 3000) == 0) && p_ptr->black_breath)
+ {
+ u32b f1, f2, f3, f4, f5;
+
+ bool_ be_silent = FALSE;
+
+ /* check all equipment for the Black Breath flag. */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* No messages if object has the flag, to avoid annoyance. */
+ if (f4 & (TR4_BLACK_BREATH)) be_silent = TRUE;
+
+ }
+ /* If we are allowed to speak, warn and disturb. */
+
+ if (!be_silent)
+ {
+ cmsg_print(TERM_L_DARK, "The Black Breath saps your soul!");
+ disturb(0, 0);
+ }
+ }
+
+
+ /*** Process Light ***/
+
+ /* Check for light being wielded */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Burn some fuel in the current lite */
+ if (o_ptr->tval == TV_LITE)
+ {
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- Use some fuel */
+ if ((f4 & TR4_FUEL_LITE) && (o_ptr->timeout > 0))
+ {
+ /* Decrease life-span */
+ o_ptr->timeout--;
+
+ /* Hack -- notice interesting fuel steps */
+ if ((o_ptr->timeout < 100) || ((o_ptr->timeout % 100) == 0))
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Hack -- Special treatment when blind */
+ if (p_ptr->blind)
+ {
+ /* Hack -- save some light for later */
+ if (o_ptr->timeout == 0) o_ptr->timeout++;
+ }
+
+ /* The light is now out */
+ else if (o_ptr->timeout < 1)
+ {
+ disturb(0, 0);
+ cmsg_print(TERM_YELLOW, "Your light has gone out!");
+ }
+
+ /* The light is getting dim */
+ else if ((o_ptr->timeout < 100) && (o_ptr->timeout % 10 == 0))
+ {
+ if (disturb_minor) disturb(0, 0);
+ cmsg_print(TERM_YELLOW, "Your light is growing faint.");
+ }
+ }
+ }
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+
+ /*** Process Inventory ***/
+
+ /*
+ * Handle experience draining. In Oangband, the effect is worse,
+ * especially for high-level characters. As per Tolkien, hobbits
+ * are resistant.
+ */
+ if (p_ptr->black_breath)
+ {
+ byte chance = 0;
+ int plev = p_ptr->lev;
+
+ if (PRACE_FLAG(PR1_RESIST_BLACK_BREATH)) chance = 2;
+ else chance = 5;
+
+ if ((rand_int(100) < chance) && (p_ptr->exp > 0))
+ {
+ p_ptr->exp -= 1 + plev / 5;
+ p_ptr->max_exp -= 1 + plev / 5;
+ (void)do_dec_stat(rand_int(6), STAT_DEC_NORMAL);
+ check_experience();
+ }
+ }
+
+ /* Drain Mana */
+ if (p_ptr->drain_mana && p_ptr->csp)
+ {
+ p_ptr->csp -= p_ptr->drain_mana;
+ if (magik(30)) p_ptr->csp -= p_ptr->drain_mana;
+
+ if (p_ptr->csp < 0)
+ {
+ p_ptr->csp = 0;
+ disturb(0, 0);
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ /* Partial summons drain mana */
+ if (p_ptr->maintain_sum)
+ {
+ u32b oldcsp = p_ptr->csp;
+ p_ptr->csp -= p_ptr->maintain_sum / 10000;
+
+ if (p_ptr->csp < 0)
+ {
+ p_ptr->csp = 0;
+ disturb(0, 0);
+
+ p_ptr->maintain_sum = 0;
+ }
+ else
+ {
+ /* Leave behind any fractional sp */
+ p_ptr->maintain_sum -= (oldcsp - p_ptr->csp) * 10000;
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ }
+
+ /* Drain Hitpoints */
+ if (p_ptr->drain_life)
+ {
+ int drain = p_ptr->drain_life + rand_int(p_ptr->mhp / 100);
+
+ p_ptr->chp -= (drain < p_ptr->chp ? drain : p_ptr->chp);
+
+ if (p_ptr->chp == 0)
+ {
+ disturb(0, 0);
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ }
+
+ /* Handle experience draining */
+ if (p_ptr->exp_drain)
+ {
+ if ((rand_int(100) < 10) && (p_ptr->exp > 0))
+ {
+ p_ptr->exp--;
+ p_ptr->max_exp--;
+ check_experience();
+ }
+ }
+
+ /* Process equipment */
+ for (j = 0, i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ /* Get the object */
+ o_ptr = &p_ptr->inventory[i];
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ /* TY Curse */
+ if ((f3 & TR3_TY_CURSE) && (rand_int(TY_CURSE_CHANCE) == 0))
+ {
+ activate_ty_curse();
+ }
+
+ /* DG Curse */
+ if ((f4 & TR4_DG_CURSE) && (rand_int(DG_CURSE_CHANCE) == 0))
+ {
+ activate_dg_curse();
+
+ /* The object recurse itself ! */
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ /* Auto Curse */
+ if ((f3 & TR3_AUTO_CURSE) && (rand_int(AUTO_CURSE_CHANCE) == 0))
+ {
+ /* The object recurse itself ! */
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ /*
+ * Hack: Uncursed teleporting items (e.g. Dragon Weapons)
+ * can actually be useful!
+ */
+ if ((f3 & TR3_TELEPORT) && (rand_int(100) < 1))
+ {
+ if ((o_ptr->ident & IDENT_CURSED) && !p_ptr->anti_tele)
+ {
+ disturb(0, 0);
+
+ /* Teleport player */
+ teleport_player(40);
+ }
+ else
+ {
+ if (p_ptr->wild_mode ||
+ (o_ptr->note && strchr(quark_str(o_ptr->note), '.')))
+ {
+ /* Do nothing */
+ /* msg_print("Teleport aborted.") */;
+ }
+ else if (get_check("Teleport? "))
+ {
+ disturb(0, 0);
+ teleport_player(50);
+ }
+ }
+ }
+
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Hack: Skip wielded lights that need fuel (already handled above) */
+ if ((i == INVEN_LITE) && (o_ptr->tval == TV_LITE) && (f4 & TR4_FUEL_LITE)) continue;
+
+ /* Recharge activatable objects */
+ if (o_ptr->timeout > 0)
+ {
+ /* Recharge */
+ o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->timeout == 0)
+ {
+ recharged_notice(o_ptr);
+ j++;
+ }
+ }
+
+ /* Recharge second spell in Mage Staffs of Spells */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && (o_ptr->xtra2 > 0))
+ {
+ /* Recharge */
+ o_ptr->xtra2--;
+
+ /* Notice changes */
+ if (o_ptr->xtra2 == 0) j++;
+ }
+ }
+
+ /* Notice changes */
+ if (j)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Recharge rods */
+ for (j = 0, i = 0; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Temporary items are destroyed */
+ if (f5 & TR5_TEMPORARY)
+ {
+ o_ptr->timeout--;
+
+ if (o_ptr->timeout <= 0)
+ {
+ inc_stack_size(i, -99);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+
+ /* Examine all charging rods or stacks of charging rods. */
+ if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2))
+ {
+ /* Increase the rod's mana. */
+ o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1;
+
+ /* Always notice */
+ j++;
+
+ /* Notice changes, provide message if object is inscribed. */
+ if (o_ptr->timeout >= o_ptr->pval2)
+ {
+ o_ptr->timeout = o_ptr->pval2;
+ recharged_notice(o_ptr);
+ }
+ }
+
+ /* Examine all charging random artifacts */
+ if ((f5 & TR5_ACTIVATE_NO_WIELD) && (o_ptr->timeout > 0))
+ {
+ /* Charge it */
+ o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->timeout == 0)
+ {
+ j++;
+ recharged_notice(o_ptr);
+ }
+ }
+
+ /* Decay objects in pack */
+ if (decays(o_ptr))
+ {
+ /* Decay it */
+ if (o_ptr->pval != 0)
+ {
+ if (o_ptr->timeout > 0)
+ {
+ if (dungeon_flags1 & DF1_HOT)
+ {
+ o_ptr->pval -= 2;
+ }
+ else if ((dungeon_flags1 & DF1_COLD) && rand_int(2))
+ {
+ if (magik(50)) o_ptr->pval--;
+ }
+ else
+ {
+ o_ptr->pval--;
+ }
+ }
+
+ if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ pack_decay(i);
+ j++;
+ }
+ }
+ }
+
+ /* Hatch eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ int mx, my;
+
+ if (o_ptr->timeout == 0)
+ {
+ o_ptr->pval--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ mx = p_ptr->px;
+ my = p_ptr->py + 1;
+ get_pos_player(5, &my, &mx);
+ msg_print("Your egg hatches!");
+ place_monster_aux(my, mx, o_ptr->pval2, FALSE, FALSE, MSTATUS_PET);
+
+ m_ptr = &m_list[cave[my][mx].m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if ((r_ptr->flags9 & RF9_IMPRESED) && can_create_companion())
+ {
+ msg_format("And you have given the imprint to your %s!",
+ r_name + r_ptr->name);
+ m_ptr->status = MSTATUS_COMPANION;
+ }
+
+ inc_stack_size(i, -1);
+
+ j++;
+ }
+ }
+ }
+ }
+
+ /* Notice changes */
+ if (j)
+ {
+ /* Combine pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /*** Process Objects ***/
+
+ /* Process objects */
+ for (i = 1; i < o_max; i++)
+ {
+ /* Access object */
+ o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Temporary items are destroyed */
+ if (f5 & TR5_TEMPORARY)
+ {
+ o_ptr->timeout--;
+
+ if (o_ptr->timeout <= 0)
+ {
+ floor_item_increase(i, -99);
+ floor_item_optimize(i);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+
+ /* Recharge rods on the ground. No messages. */
+ if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2))
+ {
+ /* Increase the rod's mana. */
+ o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1;
+
+ /* Do not overflow */
+ if (o_ptr->timeout >= o_ptr->pval2)
+ {
+ o_ptr->timeout = o_ptr->pval2;
+ }
+ }
+
+ /* Decay objects on the ground*/
+ if (decays(o_ptr))
+ {
+ /* Decay it */
+ if (o_ptr->pval != 0)
+ {
+ if (o_ptr->timeout > 0)
+ {
+ if (dungeon_flags1 & DF1_HOT)
+ {
+ o_ptr->pval -= 2;
+ }
+ else if ((dungeon_flags1 & DF1_COLD) && rand_int(2))
+ {
+ if (magik(50)) o_ptr->pval--;
+ }
+ else
+ {
+ o_ptr->pval--;
+ }
+ }
+
+ if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--;
+
+ /* Turn it into a skeleton */
+ if (o_ptr->pval <= 0)
+ {
+ floor_decay(i);
+ }
+ }
+ }
+
+ /* Hatch eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ int mx, my;
+ if (o_ptr->timeout > 0) o_ptr->pval--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ mx = o_ptr->ix;
+ my = o_ptr->iy;
+ get_pos_player(5, &my, &mx);
+ msg_print("An egg hatches!");
+ place_monster_one(my, mx, o_ptr->pval2, 0, FALSE, MSTATUS_ENEMY);
+ floor_item_increase(i, -1);
+ floor_item_describe(i);
+ floor_item_optimize(i);
+ }
+ }
+ }
+
+
+ /*** Involuntary Movement ***/
+
+ /* Delayed Word-of-Recall */
+ if (p_ptr->word_recall)
+ {
+ /* Can we ? */
+ if (process_hooks(HOOK_RECALL, "()", ""))
+ {
+ p_ptr->word_recall = 0;
+ }
+
+ /* No recall. sorry */
+ else if (dungeon_flags2 & DF2_NO_RECALL_OUT)
+ {
+ cmsg_print(TERM_L_DARK, "You cannot recall from here.");
+ p_ptr->word_recall = 0;
+ }
+
+ /* Cannot WoR out of death fate levels */
+ else if (dungeon_type == DUNGEON_DEATH)
+ {
+ cmsg_print(TERM_L_DARK, "You are fated to die here. FIGHT for your life!");
+ p_ptr->word_recall = 0;
+ }
+
+ /* I think the 'inside_quest' code belongs here -- pelpel */
+
+ /* They cannot use word of recall until reaching surface */
+ else if (p_ptr->astral)
+ {
+ msg_print("As an astral being you can't recall.");
+ p_ptr->word_recall = 0;
+ }
+
+ /* Normal WoR */
+ else
+ {
+ /*
+ * HACK: Autosave BEFORE resetting the recall counter (rr9)
+ * The player is yanked up/down as soon as
+ * he loads the autosaved game.
+ */
+ if (p_ptr->word_recall == 1)
+ {
+ autosave_checkpoint();
+ }
+
+ /* Make SURE that persistent levels are saved
+ * I don't know if this is needed, but I'm getting reports,
+ * so I'm adding this extra save -- Neil
+ */
+ save_dungeon();
+
+ /* Count down towards recall */
+ p_ptr->word_recall--;
+
+ /* Activate the recall */
+ if (p_ptr->word_recall == 0)
+ {
+ /* Disturbing! */
+ disturb(0, 0);
+
+ /* Determine the level */
+ if (p_ptr->inside_quest)
+ {
+ msg_print("The recall is cancelled by a powerful magic force!");
+ }
+ else if (dun_level)
+ {
+ msg_print("You feel yourself yanked upwards!");
+
+ p_ptr->recall_dungeon = dungeon_type;
+ dungeon_type = DUNGEON_WILDERNESS;
+ dun_level = 0;
+
+ is_recall = TRUE;
+
+ p_ptr->inside_quest = 0;
+ p_ptr->leaving = TRUE;
+ }
+ else
+ {
+ msg_print("You feel yourself yanked downwards!");
+
+ /* New depth */
+ dungeon_type = p_ptr->recall_dungeon;
+ dun_level = max_dlv[dungeon_type];
+ if (dun_level < 1) dun_level = 1;
+
+ /* Reset player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+
+ /* Leaving */
+ is_recall = TRUE;
+
+ p_ptr->leaving = TRUE;
+ p_ptr->wild_mode = FALSE;
+ }
+
+ /* Sound */
+ sound(SOUND_TPLEVEL);
+ }
+ }
+ }
+}
+
+
+/*
+ * Verify use of "wizard" mode
+ */
+static bool_ enter_wizard_mode(void)
+{
+ /* Ask first time, but not while loading a dead char with the -w option */
+ if (!noscore && !(p_ptr->chp < 0))
+ {
+ /* Mention effects */
+ msg_print("Wizard mode is for debugging and experimenting.");
+ msg_print("The game will not be scored if you enter wizard mode.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to enter wizard mode? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0002;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Verify use of "debug" commands
+ */
+static bool_ enter_debug_mode(void)
+{
+ /* Ask first time */
+ if (!noscore && !wizard)
+ {
+ /* Mention effects */
+ msg_print("The debug commands are for debugging and experimenting.");
+ msg_print("The game will not be scored if you use debug commands.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to use debug commands? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0008;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- Declare the Debug Routines
+ */
+extern void do_cmd_debug(void);
+
+
+/*
+ * Parse and execute the current command
+ * Give "Warning" on illegal commands.
+ *
+ * XXX XXX XXX Make some "blocks"
+ */
+static void process_command(void)
+{
+ char error_m[80];
+
+ /* Handle repeating the last command */
+ repeat_check();
+
+ /* Process the appropriate hooks */
+ if (process_hooks(HOOK_KEYPRESS, "(d)", command_cmd)) return;
+
+ /* Parse the command */
+ switch (command_cmd)
+ {
+ /* Ignore */
+ case ESCAPE:
+ case ' ':
+ case 0:
+ {
+ break;
+ }
+
+ /* Ignore return */
+ case '\r':
+ {
+ break;
+ }
+
+#ifdef ALLOW_QUITTING
+
+ case KTRL('L'):
+ {
+ quit("CHEATER");
+ break;
+ }
+
+#endif
+
+
+ /*** Wizard Commands ***/
+
+ /* Toggle Wizard Mode */
+ case KTRL('W'):
+ {
+ if (wizard)
+ {
+ wizard = FALSE;
+ msg_print("Wizard mode off.");
+ }
+ else if (enter_wizard_mode())
+ {
+ wizard = TRUE;
+ msg_print("Wizard mode on.");
+ }
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw "title" */
+ p_ptr->redraw |= (PR_TITLE);
+
+ break;
+ }
+
+ /* Special "debug" commands */
+ case KTRL('A'):
+ {
+ /* Enter debug mode */
+ if (enter_debug_mode())
+ {
+ do_cmd_debug();
+ }
+ break;
+ }
+
+
+ /*** Inventory Commands ***/
+
+ /* Wear/wield equipment */
+ case 'w':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_wield();
+ break;
+ }
+
+ /* Take off equipment */
+ case 't':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_takeoff();
+ p_ptr->redraw |= (PR_MH);
+ break;
+ }
+
+ /* Drop an item */
+ case 'd':
+ {
+ if (do_control_drop()) break;
+ if (!p_ptr->wild_mode) do_cmd_drop();
+ break;
+ }
+
+ /* Destroy an item */
+ case 'k':
+ {
+ if (p_ptr->control) break;
+ do_cmd_destroy();
+ break;
+ }
+
+ /* Equipment list */
+ case 'e':
+ {
+ if (p_ptr->control) break;
+ do_cmd_equip();
+ break;
+ }
+
+ /* Inventory list */
+ case 'i':
+ {
+ if (do_control_inven()) break;
+ do_cmd_inven();
+ break;
+ }
+
+
+ /*** Various commands ***/
+
+ /* Identify an object */
+ case 'I':
+ {
+ do_cmd_observe();
+ break;
+ }
+
+ /* Hack -- toggle windows */
+ case KTRL('I'):
+ {
+ toggle_inven_equip();
+ break;
+ }
+
+
+ /*** Standard "Movement" Commands ***/
+
+ /* Alter a grid */
+ case '+':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_alter();
+ break;
+ }
+
+ /* Dig a tunnel */
+ case 'T':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_tunnel();
+ break;
+ }
+
+ /* Move (usually pick up things) */
+ case ';':
+ {
+ if (do_control_walk()) break;
+
+ do_cmd_walk(always_pickup, TRUE);
+
+ break;
+ }
+
+ /* Move (usually do not pick up) */
+ case '-':
+ {
+ if (do_control_walk()) break;
+
+ do_cmd_walk(!always_pickup, TRUE);
+
+ break;
+ }
+
+
+ /*** Running, Resting, Searching, Staying */
+
+ /* Begin Running -- Arg is Max Distance */
+ case '.':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_run();
+ break;
+ }
+
+ /* Stay still (usually pick things up) */
+ case ',':
+ {
+ if (do_control_pickup()) break;
+ do_cmd_stay(always_pickup);
+ break;
+ }
+
+ /* Stay still (usually do not pick up) */
+ case 'g':
+ {
+ if (p_ptr->control) break;
+ do_cmd_stay(!always_pickup);
+ break;
+ }
+
+ /* Rest -- Arg is time */
+ case 'R':
+ {
+ if (p_ptr->control) break;
+ do_cmd_rest();
+ break;
+ }
+
+ /* Search for traps/doors */
+ case 's':
+ {
+ if (p_ptr->control) break;
+ do_cmd_search();
+ break;
+ }
+
+ /* Toggle search mode */
+ case 'S':
+ {
+ if (p_ptr->control) break;
+ do_cmd_toggle_search();
+ break;
+ }
+
+
+ /*** Stairs and Doors and Chests and Traps ***/
+
+ /* Enter store */
+ case '_':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_store();
+ break;
+ }
+
+ /* Go up staircase */
+ case '<':
+ {
+ object_type *o_ptr;
+ u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0;
+
+
+ /* Check for light being wielded */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+ /* Burn some fuel in the current lite */
+ if (o_ptr->tval == TV_LITE)
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Cannot move if rooted in place */
+ if (p_ptr->tim_roots) break;
+
+ if (p_ptr->control) break;
+ /* Normal cases */
+ if (p_ptr->wild_mode || dun_level || is_quest(dun_level))
+ {
+ do_cmd_go_up();
+ }
+ /* Don't let the player < when he'd just drop right back down */
+ else if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ msg_print("You are too hungry to travel.");
+ }
+ else if (p_ptr->sensible_lite &&
+ (((turn / ((10L * DAY) / 2)) % 2) == 0))
+ {
+ /* Burn vampires! burn! */
+ msg_print("You can't travel during the day!");
+ }
+ else if (p_ptr->sensible_lite &&
+ (o_ptr->tval != 0) &&
+ (o_ptr->sval >= SV_LITE_GALADRIEL) &&
+ (o_ptr->sval <= SV_STONE_LORE) &&
+ (o_ptr->sval != SV_LITE_UNDEATH))
+ {
+ msg_print("Travel with your present light would be unsafe.");
+ }
+ else if (p_ptr->cut || p_ptr->poisoned)
+ {
+ /* I actually died this way once -- neil */
+ msg_print("You are too injured to travel.");
+ }
+ else if (ambush_flag)
+ {
+ msg_print("To flee the ambush you have to reach the edge of the map.");
+ }
+ /* TODO: make the above stuff use this hook */
+ else if (!process_hooks(HOOK_FORBID_TRAVEL, "()"))
+ {
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ change_wild_mode();
+
+ /* Update the known wilderness */
+ reveal_wilderness_around_player(p_ptr->wilderness_y,
+ p_ptr->wilderness_x,
+ 0, WILDERNESS_SEE_RADIUS);
+ }
+
+ break;
+ }
+
+ /* Go down staircase */
+ case '>':
+ {
+ /* Cannot move if rooted in place */
+ if (p_ptr->tim_roots) break;
+
+ if (p_ptr->control) break;
+ /* Normal cases */
+ if (!p_ptr->wild_mode)
+ {
+ do_cmd_go_down();
+ }
+
+ /* Special cases */
+ else
+ {
+ if ((wf_info[wild_map[p_ptr->py][p_ptr->px].feat].entrance >= 1000) ||
+ (wild_map[p_ptr->py][p_ptr->px].entrance > 1000))
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ p_ptr->wild_mode = !p_ptr->wild_mode;
+ do_cmd_go_down();
+
+ if (dun_level == 0)
+ {
+ p_ptr->wild_mode = !p_ptr->wild_mode;
+ }
+ else
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ change_wild_mode();
+ }
+ }
+ else
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ change_wild_mode();
+ }
+ }
+
+ break;
+ }
+
+ /* Open a door or chest */
+ case 'o':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_open();
+ break;
+ }
+
+ /* Close a door */
+ case 'c':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_close();
+ break;
+ }
+
+ /* Give an item */
+ case 'y':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_give();
+ break;
+ }
+
+ /* Chat */
+ case 'Y':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_chat();
+ break;
+ }
+
+ /* Jam a door with spikes */
+ case 'j':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_spike();
+ break;
+ }
+
+ /* Bash a door */
+ case 'B':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_bash();
+ break;
+ }
+
+ /* Disarm a trap or chest */
+ case 'D':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_disarm();
+ break;
+ }
+
+
+ /*** Magic and Prayers ***/
+
+ /* Interact with skills */
+ case 'G':
+ {
+ if (p_ptr->control) break;
+ do_cmd_skill();
+ break;
+ }
+
+ /* Interact with abilities */
+ case 'N':
+ {
+ if (p_ptr->control) break;
+ do_cmd_ability();
+ break;
+ }
+
+ /* Browse a book */
+ case 'b':
+ {
+ if (p_ptr->control) break;
+ do_cmd_browse();
+ break;
+ }
+
+ /* Cast a spell */
+ case 'm':
+ {
+ if (do_control_magic()) break;
+
+ /* No magic in the overworld map */
+ if (p_ptr->wild_mode) break;
+
+ /* Neither in the Arena */
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+
+ break;
+ }
+ do_cmd_activate_skill();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Pray a prayer */
+ case 'p':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_pray();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Issue commands to pets */
+ case 'P':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_pet();
+ break;
+ }
+
+ /* Cut up a corpse */
+ case 'h':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_cut_corpse();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Cure some meat */
+ case 'K':
+ {
+ if (p_ptr->control) break;
+ do_cmd_cure_meat();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Steal an item form a monster */
+ case 'Z':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_steal();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /*** Use various objects ***/
+
+ /* Inscribe an object */
+ case '{':
+ {
+ if (p_ptr->control) break;
+ do_cmd_inscribe();
+ break;
+ }
+
+ /* Uninscribe an object */
+ case '}':
+ {
+ if (p_ptr->control) break;
+ do_cmd_uninscribe();
+ break;
+ }
+
+ /* Activate an artifact */
+ case 'A':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_activate();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Eat some food */
+ case 'E':
+ {
+ if (p_ptr->control) break;
+ do_cmd_eat_food();
+ break;
+ }
+
+ /* Fuel your lantern/torch */
+ case 'F':
+ {
+ if (p_ptr->control) break;
+ do_cmd_refill();
+ break;
+ }
+
+ /* Fire an item */
+ case 'f':
+ {
+ object_type *j_ptr;
+
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("You're in the arena now. This is hand-to-hand!");
+ msg_print(NULL);
+ break;
+ }
+
+ j_ptr = &p_ptr->inventory[INVEN_BOW];
+
+ if (process_hooks(HOOK_FIRE, "(O)", j_ptr))
+ break;
+
+ if (j_ptr->tval == TV_BOOMERANG)
+ {
+ do_cmd_boomerang();
+ }
+ else
+ {
+ do_cmd_fire();
+ }
+
+ break;
+ }
+
+ /* Throw an item */
+ case 'v':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("You're in the arena now. This is hand-to-hand!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_throw();
+ break;
+ }
+
+ /* Aim a wand */
+ case 'a':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_aim_wand();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Zap a rod */
+ case 'z':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_zap_rod();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Quaff a potion */
+ case 'q':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_quaff_potion();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Drink from a fountain -SC- */
+ case 'H':
+ {
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ if (p_ptr->control) break;
+ if ((c_ptr->feat == FEAT_FOUNTAIN) ||
+ (c_ptr->feat == FEAT_EMPTY_FOUNTAIN))
+ {
+ do_cmd_drink_fountain();
+ squeltch_inventory();
+ squeltch_grid();
+ }
+ else
+ {
+ msg_print("You see no fountain here.");
+ }
+
+ break;
+ }
+
+ /* Read a scroll */
+ case 'r':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_read_scroll();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Use a staff */
+ case 'u':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_use_staff();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Use racial power */
+ case 'U':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_power();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Sacrifice at an altar */
+ case 'O':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (PRACE_FLAG(PR1_NO_GOD))
+ {
+ msg_print("You cannot worship gods.");
+ }
+ else
+ {
+ do_cmd_sacrifice();
+ }
+
+ break;
+ }
+
+ /*** Looking at Things (nearby or on map) ***/
+
+ /* Full dungeon map */
+ case 'M':
+ {
+ if (!p_ptr->wild_mode) do_cmd_view_map();
+ break;
+ }
+
+ /* Locate player on map */
+ case 'L':
+ {
+ do_cmd_locate();
+ break;
+ }
+
+ /* Look around */
+ case 'l':
+ {
+ do_cmd_look();
+ break;
+ }
+
+ /* Target monster or location */
+ case '*':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_target();
+ break;
+ }
+
+ /* Engrave the floor */
+ case 'x':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ /* No point in engraving if there isn't any mana on this grid. */
+ /* DG - actualy there is, it doesnt break macros */
+ do_cmd_sense_grid_mana();
+ do_cmd_engrave();
+
+ break;
+ }
+
+ /*** Help and Such ***/
+
+ /* Help */
+ case '?':
+ {
+ do_cmd_help();
+ break;
+ }
+
+ /* Identify symbol */
+ case '/':
+ {
+ do_cmd_query_symbol();
+ break;
+ }
+
+ /* Character description */
+ case 'C':
+ {
+ do_cmd_change_name();
+ break;
+ }
+
+
+ /*** System Commands ***/
+
+ /* Hack -- User interface */
+ case '!':
+ {
+ (void)Term_user(0);
+ break;
+ }
+
+ /* Single line from a pref file */
+ case '"':
+ {
+ do_cmd_pref();
+ break;
+ }
+
+ /* Interact with macros */
+ case '@':
+ {
+ do_cmd_macros();
+ break;
+ }
+
+ /* Interact with visuals */
+ case '%':
+ {
+ do_cmd_visuals();
+ break;
+ }
+
+ /* Interact with colors */
+ case '&':
+ {
+ do_cmd_colors();
+ break;
+ }
+
+ /* Interact with options */
+ case '=':
+ {
+ do_cmd_options();
+ break;
+ }
+
+
+ /*** Misc Commands ***/
+
+ /* Take notes */
+ case ':':
+ {
+ do_cmd_note();
+ break;
+ }
+
+ /* Version info */
+ case 'V':
+ {
+ do_cmd_version();
+ break;
+ }
+
+ /* Repeat level feeling */
+ case KTRL('F'):
+ {
+ if (!p_ptr->wild_mode)
+ do_cmd_feeling();
+ break;
+ }
+
+ /* Show previous message */
+ case KTRL('O'):
+ {
+ do_cmd_message_one();
+ break;
+ }
+
+ /* Show previous messages */
+ case KTRL('P'):
+ {
+ do_cmd_messages();
+ break;
+ }
+
+ /* Show quest status -KMW- */
+ case KTRL('Q'):
+ case CMD_QUEST:
+{
+ do_cmd_checkquest();
+ break;
+ }
+
+ /* Redraw the screen */
+ case KTRL('R'):
+ {
+ do_cmd_redraw();
+ break;
+ }
+
+ /* Hack -- Save and don't quit */
+ case KTRL('S'):
+ {
+ is_autosave = FALSE;
+ do_cmd_save_game();
+ break;
+ }
+
+ case KTRL('T'):
+ {
+ do_cmd_time();
+ }
+ break;
+
+ /* Save and quit */
+ case KTRL('X'):
+ {
+ alive = FALSE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ break;
+ }
+
+ /* Quit (commit suicide) */
+ case 'Q':
+ {
+ do_cmd_suicide();
+ break;
+ }
+
+ /* Activate cmovie */
+ case '|':
+ {
+ /* Stop ? */
+ if (do_movies == 1)
+ {
+ do_stop_cmovie();
+ msg_print("Cmovie recording stopped.");
+ }
+ else
+ {
+ do_start_cmovie();
+ }
+ break;
+ }
+
+ /* Extended command */
+ case '#':
+ {
+ do_cmd_cli();
+ break;
+ }
+
+ /* Check artifacts, uniques, objects */
+ case '~':
+ {
+ do_cmd_knowledge();
+ break;
+ }
+
+ /* Commands only available as extended commands: */
+
+ /* Extended command help. */
+ case CMD_CLI_HELP:
+ {
+ do_cmd_cli_help();
+ break;
+ }
+
+ /* Game time. */
+ case CMD_SHOW_TIME:
+ {
+ do_cmd_time();
+ break;
+ }
+
+ /* Check skills. */
+ case CMD_SHOW_SKILL:
+ {
+ do_cmd_skill();
+ break;
+ }
+
+ /* Check abilities. */
+ case CMD_SHOW_ABILITY:
+ {
+ do_cmd_ability();
+ break;
+ }
+
+ /* Save a html screenshot. */
+ case CMD_DUMP_HTML:
+ {
+ do_cmd_html_dump();
+ break;
+ }
+
+ /* Record a macro. */
+ case '$':
+ case CMD_MACRO:
+ {
+ do_cmd_macro_recorder();
+ break;
+ }
+ case CMD_BLUNDER:
+ {
+ if (do_control_walk()) break;
+ do_cmd_walk(always_pickup, FALSE);
+ break;
+ }
+ /* Hack -- Unknown command */
+ default:
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+
+ /* Would like to have an option disabling this -- pelpel */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("error.txt", error_m);
+ sound(SOUND_ILLEGAL);
+ msg_print(error_m);
+ }
+ else
+ {
+ prt("Type '?' for help.", 0, 0);
+ }
+
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Process the player
+ *
+ * Notice the annoying code to handle "pack overflow", which
+ * must come first just in case somebody manages to corrupt
+ * the savefiles by clever use of menu commands or something.
+ */
+void process_player(void)
+{
+ int i, j;
+
+ int speed_use;
+
+
+ /*** Apply energy ***/
+
+ if (hack_corruption)
+ {
+ msg_print("You feel different!");
+ (void)gain_random_corruption(0);
+ hack_corruption = FALSE;
+ }
+
+ /* Obtain current speed */
+ speed_use = p_ptr->pspeed;
+
+ /* Maximum value */
+ if (speed_use > 199)
+ {
+ speed_use = 199;
+ }
+
+ /* Minimum value */
+ else if (speed_use < 0)
+ {
+ speed_use = 0;
+ }
+
+ /* Give the player some energy */
+ p_ptr->energy += extract_energy[speed_use];
+
+ /* No turn yet */
+ if (p_ptr->energy < 100) return;
+
+
+ /*** Check for interupts ***/
+
+ /* Complete resting */
+ if (resting < 0)
+ {
+ /* Basic resting */
+ if (resting == -1)
+ {
+ /* Stop resting */
+ if ((p_ptr->chp == p_ptr->mhp) && (p_ptr->csp >= p_ptr->msp))
+ {
+ disturb(0, 0);
+ }
+ }
+
+ /* Complete resting */
+ else if (resting == -2)
+ {
+ bool_ stop = TRUE;
+ object_type *o_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ /* Stop resting */
+ if ((!p_ptr->drain_life) && (p_ptr->chp != p_ptr->mhp)) stop = FALSE;
+ if ((!p_ptr->drain_mana) && (p_ptr->csp != p_ptr->msp)) stop = FALSE;
+ if (o_ptr->pval2 < o_ptr->pval3) stop = FALSE;
+ if (p_ptr->blind || p_ptr->confused) stop = FALSE;
+ if (p_ptr->poisoned || p_ptr->afraid) stop = FALSE;
+ if (p_ptr->stun || p_ptr->cut) stop = FALSE;
+ if (p_ptr->slow || p_ptr->paralyzed) stop = FALSE;
+ if (p_ptr->image || p_ptr->word_recall) stop = FALSE;
+ if (p_ptr->immov_cntr != 0) stop = FALSE;
+
+ for (i = 0; i < 6; i++)
+ {
+ if (p_ptr->stat_cnt[i] > 0) stop = FALSE;
+ }
+
+ if (stop)
+ {
+ disturb(0, 0);
+ }
+ p_ptr->redraw |= (PR_STATE);
+ }
+ }
+
+ /* Handle "abort" */
+ if (!avoid_abort)
+ {
+ /* Check for "player abort" (semi-efficiently for resting) */
+ if (running || command_rep || (resting && !(resting & 0x0F)))
+ {
+ /* Do not wait */
+ inkey_scan = TRUE;
+
+ /* Check for a key */
+ if (inkey())
+ {
+ /* Flush input */
+ flush();
+
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- Show a Message */
+ msg_print("Cancelled.");
+ }
+ }
+ }
+
+
+ /*** Handle actual user input ***/
+
+ /* Repeat until out of energy */
+ while (p_ptr->energy >= 100)
+ {
+ /* Notice stuff (if needed) */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff (if needed) */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- mark current wilderness location as known */
+ if (!p_ptr->wild_mode && dun_level == 0)
+ wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].known = TRUE;
+
+
+ /* Place the cursor on the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Refresh (optional) */
+ if (fresh_before) Term_fresh();
+
+ /* Hack -- Pack Overflow */
+ if (p_ptr->inventory[INVEN_PACK].k_idx)
+ {
+ int item = INVEN_PACK;
+
+ char o_name[80];
+
+ object_type *o_ptr;
+
+ /* Access the slot to be dropped */
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Disturbing */
+ disturb(0, 0);
+
+ /* Warning */
+ msg_print("Your pack overflows!");
+
+ /* Describe */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Drop it (carefully) near the player */
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+
+ /* Modify, Describe, Optimize */
+ inc_stack_size(item, -255);
+
+ /* Notice stuff (if needed) */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff (if needed) */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->window) window_stuff();
+ }
+
+
+ /* Assume free turn */
+ energy_use = 0;
+
+
+ /* Paralyzed or Knocked Out */
+ if ((p_ptr->paralyzed) || (p_ptr->stun >= 100))
+ {
+ /* Take a turn */
+ energy_use = 100;
+ }
+
+ /* Resting */
+ else if (resting)
+ {
+ /* Timed rest */
+ if (resting > 0)
+ {
+ /* Reduce rest count */
+ resting--;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ p_ptr->did_nothing = TRUE;
+
+ /* Take a turn */
+ energy_use = 100;
+ }
+
+ /* Running */
+ else if (running)
+ {
+ /* Take a step */
+ run_step(0);
+
+ /*
+ * Commented out because it doesn't make any sense
+ * to require a player holding down direction keys
+ * instead of using running commands when s/he follows
+ * Eru and do the opposite for the other deities -- pelpel
+ */
+ /* p_ptr->did_nothing = TRUE; */
+ }
+
+ /* Repeated command */
+ else if (command_rep)
+ {
+ /* Count this execution */
+ command_rep--;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Hack -- Assume messages were seen */
+ msg_flag = FALSE;
+
+ /* Clear the top line */
+ prt("", 0, 0);
+
+ /* Process the command */
+ process_command();
+
+ p_ptr->did_nothing = TRUE;
+ }
+
+ /* Normal command */
+ else
+ {
+ /* Place the cursor on the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Get a command (normal) */
+ request_command(FALSE);
+
+ /* Process the command */
+ process_command();
+ }
+
+
+ /*** Clean up ***/
+
+ /* Significant */
+ if (energy_use)
+ {
+ /* Use some energy */
+ p_ptr->energy -= energy_use;
+
+
+ /* Hack -- constant hallucination */
+ if (p_ptr->image) p_ptr->redraw |= (PR_MAP);
+
+
+ /* Shimmer monsters if needed */
+ if (!avoid_other && !use_graphics && shimmer_monsters)
+ {
+ /* Clear the flag */
+ shimmer_monsters = FALSE;
+
+ /* Shimmer multi-hued monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ /* Access monster */
+ m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Access the monster race */
+ r_ptr = race_inf(m_ptr);
+
+ /* Skip non-multi-hued monsters */
+ if (!(r_ptr->flags1 & (RF1_ATTR_MULTI))) continue;
+
+ /* Reset the flag */
+ shimmer_monsters = TRUE;
+
+ /* Redraw regardless */
+ lite_spot(m_ptr->fy, m_ptr->fx);
+ }
+ }
+
+ /* Shimmer objects if needed and requested */
+ if (!avoid_other && !avoid_shimmer && !use_graphics &&
+ shimmer_objects)
+ {
+ /* Clear the flag */
+ shimmer_objects = FALSE;
+
+ /* Shimmer multi-hued objects */
+ for (i = 1; i < o_max; i++)
+ {
+ /* Acquire object -- for speed only base items are allowed to shimmer */
+ object_type *o_ptr = &o_list[i];
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Skip dead or carried objects */
+ if ((!o_ptr->k_idx) || (!o_ptr->ix)) continue;
+
+ /* Skip non-multi-hued monsters */
+ if (!(k_ptr->flags5 & (TR5_ATTR_MULTI))) continue;
+
+ /* Reset the flag */
+ shimmer_objects = TRUE;
+
+ /* Redraw regardless */
+ lite_spot(o_ptr->iy, o_ptr->ix);
+ }
+ }
+
+ /*
+ * Shimmer features if needed and requested
+ *
+ * Note: this can be unbearably slow when a player chooses
+ * to use a REALLY big screen in levels filled with shallow
+ * water. I believe this also hurts a lot on multiuser systems.
+ * However fast modern processors are, I/O cannot be made that
+ * fast, and that's why shimmering has been limited to small
+ * number of monsters -- pelpel
+ */
+ if (!avoid_other && !avoid_shimmer && !use_graphics &&
+ !resting && !running)
+ {
+ for (j = panel_row_min; j <= panel_row_max; j++)
+ {
+ for (i = panel_col_min; i <= panel_col_max; i++)
+ {
+ cave_type *c_ptr = &cave[j][i];
+ feature_type *f_ptr;
+
+ /* Apply terrain feature mimics */
+ if (c_ptr->mimic)
+ {
+ f_ptr = &f_info[c_ptr->mimic];
+ }
+ else
+ {
+ f_ptr = &f_info[f_info[c_ptr->feat].mimic];
+ }
+
+ /* Skip normal features */
+ if (!(f_ptr->flags1 & (FF1_ATTR_MULTI))) continue;
+
+ /* Redraw a shimmering spot */
+ lite_spot(j, i);
+ }
+ }
+ }
+
+
+ /* Handle monster detection */
+ if (repair_monsters)
+ {
+ /* Reset the flag */
+ repair_monsters = FALSE;
+
+ /* Rotate detection flags */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr;
+
+ /* Access monster */
+ m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Nice monsters get mean */
+ if (m_ptr->mflag & (MFLAG_NICE))
+ {
+ /* Nice monsters get mean */
+ m_ptr->mflag &= ~(MFLAG_NICE);
+ }
+
+ /* Handle memorized monsters */
+ if (m_ptr->mflag & (MFLAG_MARK))
+ {
+ /* Maintain detection */
+ if (m_ptr->mflag & (MFLAG_SHOW))
+ {
+ /* Forget flag */
+ m_ptr->mflag &= ~(MFLAG_SHOW);
+
+ /* Still need repairs */
+ repair_monsters = TRUE;
+ }
+
+ /* Remove detection */
+ else
+ {
+ /* Forget flag */
+ m_ptr->mflag &= ~(MFLAG_MARK);
+
+ /* Assume invisible */
+ m_ptr->ml = FALSE;
+
+ /* Update the monster */
+ update_mon(i, FALSE);
+
+ /* Redraw regardless */
+ lite_spot(m_ptr->fy, m_ptr->fx);
+ }
+ }
+ }
+ }
+
+ /*
+ * Moved from dungeon() -- It'll get called whenever player
+ * spends energy, so that maze isn't incredibly easy for
+ * Sorcerors and alike any longer -- pelpel
+ *
+ * Forget everything when requested hehe I'm *NASTY*
+ */
+ if (dun_level && (dungeon_flags1 & DF1_FORGET))
+ {
+ wiz_dark();
+ }
+ }
+
+
+ /* Hack -- notice death */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+ }
+}
+
+
+
+/*
+ * Interact with the current dungeon level.
+ *
+ * This function will not exit until the level is completed,
+ * the user dies, or the game is terminated.
+ */
+static void dungeon(void)
+{
+ /* Reset various flags */
+ hack_mind = FALSE;
+
+ /* Not leaving */
+ p_ptr->leaving = FALSE;
+
+ /* Reset the "command" vars */
+ command_cmd = 0;
+ command_new = 0;
+ command_rep = 0;
+ command_arg = 0;
+ command_dir = 0;
+
+ /* Make sure partial summoning counter is initialized. */
+ p_ptr->maintain_sum = 0;
+
+ /* Cancel the target */
+ target_who = 0;
+
+ /* Cancel the health bar */
+ health_track(0);
+
+
+ /* Check visual effects */
+ shimmer_monsters = TRUE;
+ shimmer_objects = TRUE;
+ repair_monsters = TRUE;
+ repair_objects = TRUE;
+
+
+ /* Disturb */
+ disturb(1, 0);
+
+ /* Track maximum player level */
+ if (p_ptr->max_plv < p_ptr->lev)
+ {
+ p_ptr->max_plv = p_ptr->lev;
+ }
+
+ /* Track maximum dungeon level (if not in quest -KMW-) */
+ if ((max_dlv[dungeon_type] < dun_level) && !p_ptr->inside_quest)
+ {
+ max_dlv[dungeon_type] = dun_level;
+ }
+
+ /* No stairs down from Quest */
+ if (is_quest(dun_level) && !p_ptr->astral)
+ {
+ create_down_stair = FALSE;
+ create_down_shaft = FALSE;
+ }
+
+ /* Paranoia -- no stairs from town or wilderness */
+ if (!dun_level) create_down_stair = create_up_stair = FALSE;
+ if (!dun_level) create_down_shaft = create_up_shaft = FALSE;
+
+ /* Option -- no connected stairs */
+ if (!dungeon_stair) create_down_stair = create_up_stair = FALSE;
+ if (!dungeon_stair) create_down_shaft = create_up_shaft = FALSE;
+
+ /* no connecting stairs on special levels */
+ if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_stair = create_up_stair = FALSE;
+ if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_shaft = create_up_shaft = FALSE;
+
+ /* Make a stairway. */
+ if ((create_up_stair || create_down_stair ||
+ create_up_shaft || create_down_shaft) &&
+ !get_fbranch())
+ {
+ /* Place a stairway */
+ if (cave_valid_bold(p_ptr->py, p_ptr->px))
+ {
+ /* XXX XXX XXX */
+ delete_object(p_ptr->py, p_ptr->px);
+
+ /* Make stairs */
+ if (create_down_stair)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_MORE);
+ }
+ else if (create_down_shaft)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_SHAFT_DOWN);
+ }
+ else if (create_up_shaft)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_SHAFT_UP);
+ }
+ else
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_LESS);
+ }
+ }
+
+ /* Cancel the stair request */
+ create_down_stair = create_up_stair = FALSE;
+ create_down_shaft = create_up_shaft = FALSE;
+ }
+
+ /* Hack - Assume invalid panel */
+ panel_row_min = cur_hgt;
+ panel_row_max = 0;
+ panel_col_min = cur_wid;
+ panel_col_max = 0;
+
+ /* Center the panel */
+ verify_panel();
+
+ /* Flush messages */
+ msg_print(NULL);
+
+
+ /* Enter "xtra" mode */
+ character_xtra = TRUE;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+
+ /* Redraw dungeon */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_SANITY | PU_BODY);
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Redraw stuff */
+ window_stuff();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_DISTANCE | PU_MON_LITE);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Leave "xtra" mode */
+ character_xtra = FALSE;
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_BODY);
+
+ /* Combine / Reorder the pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Window stuff */
+ window_stuff();
+
+ /* Refresh */
+ Term_fresh();
+
+
+ /* Announce (or repeat) the feeling */
+ if (dun_level) do_cmd_feeling();
+
+
+ /* Hack -- notice death or departure */
+ if (!alive || death) return;
+
+ /*** Process this dungeon level ***/
+
+ /* Reset the monster generation level */
+ monster_level = dun_level;
+
+ /* Reset the object generation level */
+ object_level = dun_level;
+
+ hack_mind = TRUE;
+
+ /* Mega Hack, if needed wipe all stairs */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ int i, j;
+
+ for (i = 0; i < cur_wid; i++)
+ {
+ for (j = 0; j < cur_hgt; j++)
+ {
+ cave_type *c_ptr = &cave[j][i];
+
+ switch (c_ptr->feat)
+ {
+ case FEAT_MORE:
+ case FEAT_LESS:
+ case FEAT_SHAFT_UP:
+ case FEAT_SHAFT_DOWN:
+ {
+ cave_set_feat(j, i, FEAT_FLOOR);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Reset the monster generation level */
+ monster_level = 127;
+
+ /* Reset the object generation level */
+ object_level = 0;
+ }
+
+ /* Main loop */
+ while (TRUE)
+ {
+ /* Hack -- Compact the monster list occasionally */
+ if (m_cnt + 32 > max_m_idx) compact_monsters(64);
+
+ /* Hack -- Compress the monster list occasionally */
+ if (m_cnt + 32 < m_max) compact_monsters(0);
+
+
+ /* Hack -- Compact the object list occasionally */
+ if (o_cnt + 32 > max_o_idx) compact_objects(64);
+
+ /* Hack -- Compress the object list occasionally */
+ if (o_cnt + 32 < o_max) compact_objects(0);
+
+
+
+ /* Process the player */
+ process_player();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+
+ total_friends = 0;
+ total_friend_levels = 0;
+
+ /* Process all of the monsters */
+ process_monsters();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+
+ /* Process the world */
+ process_world();
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_END_TURN, "(d)", is_quest(dun_level));
+
+ /* Make it pulsate and live !!!! */
+ if ((dungeon_flags1 & DF1_EVOLVE) && dun_level)
+ {
+ if (!(turn % 10)) evolve_level(TRUE);
+ }
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Count game turns */
+ turn++;
+ }
+
+ /* Did we leave a dungeon ? */
+ if ((dun_level < d_info[dungeon_type].mindepth) && !is_recall)
+ {
+ dun_level = 0;
+
+ if (d_info[dungeon_type].ix > -1)
+ {
+ p_ptr->wilderness_x = d_info[dungeon_type].ix;
+ p_ptr->wilderness_y = d_info[dungeon_type].iy;
+ }
+
+ dungeon_type = DUNGEON_WILDERNESS;
+ }
+
+ if (dun_level > d_info[dungeon_type].maxdepth)
+ {
+ dun_level = 0;
+
+ if (d_info[dungeon_type].ox > -1)
+ {
+ p_ptr->wilderness_x = d_info[dungeon_type].ox;
+ p_ptr->wilderness_y = d_info[dungeon_type].oy;
+ }
+
+ dungeon_type = DUNGEON_WILDERNESS;
+ }
+
+ is_recall = FALSE;
+}
+
+
+
+
+/*
+ * Load some "user pref files"
+ */
+static void load_all_pref_files(void)
+{
+ char buf[1024];
+
+
+ /* Access the "race" pref file */
+ sprintf(buf, "%s.prf", rp_ptr->title + rp_name);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "class" pref file */
+ sprintf(buf, "%s.prf", spp_ptr->title + c_name);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "character" pref file */
+ sprintf(buf, "%s.prf", player_name);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Process player specific automatizer sets */
+ tome_dofile_anywhere(ANGBAND_DIR_USER, format("%s.atm", player_name), FALSE);
+}
+
+/*
+ * Actually play a game
+ *
+ * If the "new_game" parameter is true, then, after loading the
+ * savefile, we will commit suicide, if necessary, to allow the
+ * player to start a new game.
+ */
+void play_game(bool_ new_game)
+{
+ int i, tmp_dun;
+
+ bool_ cheat_death = FALSE;
+
+ hack_corruption = FALSE;
+
+ /* Hack -- Character is "icky" */
+ character_icky = TRUE;
+
+
+ /* Make sure main term is active */
+ Term_activate(angband_term[0]);
+
+ /* Initialise the resize hook XXX XXX XXX */
+ angband_term[0]->resize_hook = resize_map;
+
+ /* XXX XXX XXX hardcoded number of terms */
+ for (i = 1; i < 8; i++)
+ {
+ if (angband_term[i])
+ {
+ /* Add redraw hook */
+ angband_term[i]->resize_hook = resize_window;
+ }
+ }
+
+
+ /* Hack -- turn off the cursor */
+ (void)Term_set_cursor(0);
+
+ /* Character list */
+ if (!new_game && !no_begin_screen) new_game = begin_screen();
+ no_begin_screen = FALSE;
+
+ /* Attempt to load */
+ if (!load_player())
+ {
+ /* Oops */
+ quit("broken savefile");
+ }
+
+ /* Nothing loaded */
+ if (!character_loaded)
+ {
+ /* Make new player */
+ new_game = TRUE;
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+ }
+ else
+ {
+ int i;
+
+ /* Init new skills to their defaults */
+ for (i = old_max_s_idx; i < max_s_idx; i++)
+ {
+ s32b value = 0, mod = 0;
+
+ compute_skills(&value, &mod, i);
+
+ init_skill(value, mod, i);
+ }
+ }
+
+#if 1
+
+ /* Process old character */
+ if (!new_game)
+ {
+ /* Process the player name */
+ process_player_name(FALSE);
+ }
+
+#endif
+
+ /* Init the RNG */
+ if (Rand_quick)
+ {
+ u32b seed;
+
+ /* Basic seed */
+ seed = (time(NULL));
+
+#ifdef SET_UID
+
+ /* Mutate the seed on Unix machines */
+ seed = ((seed >> 3) * (getpid() << 1));
+
+#endif
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+
+ /* Seed the "complex" RNG */
+ Rand_state_init(seed);
+ }
+
+ /* Extract the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ int os = option_info[i].o_page;
+ int ob = option_info[i].o_bit;
+
+ /* Set the "default" options */
+ if (option_info[i].o_var)
+ {
+ /* Set */
+ if (option_flag[os] & (1L << ob))
+ {
+ /* Set */
+ (*option_info[i].o_var) = TRUE;
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ (*option_info[i].o_var) = FALSE;
+ }
+ }
+ }
+
+ /* Roll new character */
+ if (new_game)
+ {
+ s32b ret;
+
+ /* Are we authorized to create new chars? */
+ call_lua("get_module_info", "(s)", "d", "allow_birth", &ret);
+
+ if (!ret)
+ {
+ msg_box("Sorry, this module does not allow character creation.", -1, -1);
+
+ /* Close stuff */
+ close_game();
+
+ /* Quit */
+ quit(NULL);
+ }
+
+ process_hooks(HOOK_INIT, "()");
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+
+ /* Hack -- seed for flavors */
+ seed_flavor = rand_int(0x10000000);
+
+ /* Roll up a new character */
+ player_birth();
+
+ /* Start in town, or not */
+ if (p_ptr->astral) dun_level = 98;
+ else dun_level = 0;
+ p_ptr->inside_quest = 0;
+ p_ptr->inside_arena = 0;
+
+ /* Hack -- enter the world */
+ /* Mega-hack Vampires and Spectres start in the dungeon */
+ if (PRACE_FLAG(PR1_UNDEAD))
+ {
+ turn = (10L * DAY / 2) + (START_DAY * 10) + 1;
+ }
+ else
+ {
+ turn = (START_DAY * 10) + 1;
+ }
+ }
+
+ /* Flash a message */
+ prt("Please wait...", 0, 0);
+
+ /* Flush the message */
+ Term_fresh();
+
+ /* Be sure to not bother the player */
+ calc_powers_silent = TRUE;
+
+ /* Hack -- Enter wizard mode */
+ if (arg_wizard && enter_wizard_mode()) wizard = TRUE;
+
+ /* Flavor the objects */
+ flavor_init();
+
+ /* Reset the visual mappings */
+ reset_visuals();
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+
+ /* Window stuff */
+ window_stuff();
+
+ /* load user file */
+ process_pref_file("user.prf");
+
+ /* Load the "pref" files */
+ load_all_pref_files();
+
+ /* Set or clear "rogue_like_commands" if requested */
+ if (arg_force_original) rogue_like_commands = FALSE;
+ if (arg_force_roguelike) rogue_like_commands = TRUE;
+
+ /* Initialize vault info */
+ if (init_v_info()) quit("Cannot initialize vaults");
+
+ /* Initialize hooks */
+ init_hooks();
+ ingame_help(p_ptr->help.enabled);
+
+ /* React to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Mega hack, prevent lots of bugs */
+ if ((p_ptr->px == 0) || (p_ptr->py == 0))
+ {
+ p_ptr->px = 1;
+ p_ptr->py = 1;
+ };
+
+ /* Hack - if note file exists, load it */
+ if (!new_game && take_notes)
+ {
+ add_note_type(NOTE_ENTER_DUNGEON);
+ }
+
+ /* Generate a dungeon level if needed */
+ if (!character_dungeon) generate_cave();
+
+ /* Ok tell the scripts that the game is about to start */
+ process_hooks(HOOK_GAME_START, "()");
+
+ /* Character is now "complete" */
+ character_generated = TRUE;
+
+
+ /* Hack -- Character is no longer "icky" */
+ character_icky = FALSE;
+
+
+ /* Start game */
+ alive = TRUE;
+
+ /* Hack -- Enforce "delayed death" */
+ if (p_ptr->chp < 0) death = TRUE;
+
+ /* Should we use old colors */
+ if (autoload_old_colors)
+ {
+ process_pref_file("422color.prf");
+ }
+
+
+ /* Process */
+ while (TRUE)
+ {
+ /* Save the level */
+ old_dun_level = dun_level;
+ p_ptr->old_wild_mode = p_ptr->wild_mode;
+
+ /* We reached surface ? good, lets go down again !! */
+ if (p_ptr->astral && !dun_level)
+ {
+ p_ptr->astral = FALSE;
+ cmsg_print(TERM_L_GREEN,
+ "Well done ! You reached the town ! "
+ "You can now go down again.");
+ }
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Process the level */
+ dungeon();
+
+ /* Save the current level if in a persistent level */
+ tmp_dun = dun_level;
+ dun_level = old_dun_level;
+ save_dungeon();
+ dun_level = tmp_dun;
+
+ /* A death fate affects level generation */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ /* Ignore empty slots */
+ if (!fates[i].fate) continue;
+
+ /* Ignore non-applicable fates */
+ if (fates[i].level != dun_level) continue;
+
+ /* Non-serious fate fails to fire 50% of time */
+ if (!fates[i].serious && (rand_int(2) == 0)) continue;
+
+ /* Analyse fate */
+ switch (fates[i].fate)
+ {
+ /* You are doomed */
+ case FATE_DIE:
+ {
+ cmsg_print(TERM_L_DARK, "You were fated to die here. DIE!");
+
+ /* You shall perish there */
+ dungeon_type = DUNGEON_DEATH;
+ dun_level = d_info[dungeon_type].mindepth; /* was 1 */
+
+ fates[i].fate = FATE_NONE;
+ break;
+ }
+ }
+ }
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Cancel the target */
+ target_who = 0;
+
+ /* Cancel the health bar */
+ health_track(0);
+
+
+ /* Forget the lite */
+ forget_mon_lite();
+
+ /* Forget the view */
+ forget_view();
+
+ /* Handle "quit and save" */
+ if (!alive && !death) break;
+
+
+ /* Erase the old cave */
+ wipe_o_list();
+
+
+ /* XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Accidental Death */
+ if (alive && death)
+ {
+ cheat_death = FALSE;
+
+ /* Can we die ? please let us die ! */
+ if (process_hooks(HOOK_DIE, "()"))
+ {
+ cheat_death = TRUE;
+ }
+
+ /* Deus ex machina */
+ else if (granted_resurrection())
+ {
+ cheat_death = TRUE;
+ p_ptr->grace = -200000;
+ cmsg_format(TERM_L_GREEN,
+ "The power of %s raises you back from the grave!",
+ deity_info[p_ptr->pgod].name);
+ msg_print(NULL);
+ }
+
+ /* Blood of life */
+ else if (p_ptr->allow_one_death > 0)
+ {
+ cheat_death = TRUE;
+
+ /* Lose one extra life */
+ p_ptr->allow_one_death--;
+
+ cmsg_print(TERM_L_GREEN,
+ "You have been saved by the Blood of Life!");
+ msg_print(NULL);
+ }
+
+ /* Cheat death option */
+ else if ((wizard || cheat_live) && !get_check("Die? "))
+ {
+ cheat_death = TRUE;
+
+ /* Mark social class, reset age, if needed */
+ if (p_ptr->sc) p_ptr->sc = p_ptr->age = 0;
+
+ /* Increase age */
+ p_ptr->age++;
+
+ /* Mark savefile */
+ noscore |= 0x0001;
+ msg_print("You invoke wizard mode and cheat death.");
+ msg_print(NULL);
+ }
+
+ if (cheat_death)
+ {
+ /* Restore the winner status */
+ total_winner = has_won;
+
+ /* One more life spent */
+ p_ptr->lives++;
+
+ /* Restore hit points */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Heal sanity */
+ p_ptr->csane = p_ptr->msane;
+ p_ptr->csane_frac = 0;
+
+ /* Restore spell points */
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+
+ /* Hack -- Healing */
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_poisoned(0);
+ (void)set_afraid(0);
+ (void)set_paralyzed(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+
+ /* accounting for a new ailment. -LM- */
+ p_ptr->black_breath = FALSE;
+
+ /* Hack -- don't go to undead form */
+ p_ptr->necro_extra &= ~CLASS_UNDEAD;
+
+ /* Hack -- Prevent starvation */
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ /* Hack -- cancel recall */
+ if (p_ptr->word_recall)
+ {
+ /* Message */
+ msg_print("A tension leaves the air around you...");
+ msg_print(NULL);
+
+ /* Hack -- Prevent recall */
+ p_ptr->word_recall = 0;
+ }
+
+ /* Note cause of death XXX XXX XXX */
+ (void)strcpy(died_from, "Cheating death");
+
+ /* Do not die */
+ death = FALSE;
+
+ /* New depth -KMW- */
+ /* dun_level = 0; */
+ p_ptr->inside_arena = 0;
+ leaving_quest = 0;
+ p_ptr->inside_quest = 0;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ }
+
+ /* Handle "death" */
+ if (death)
+ {
+ break;
+ }
+
+ /* Mega hack */
+ if (dun_level) p_ptr->wild_mode = FALSE;
+
+ /* Make a new level */
+ process_hooks(HOOK_NEW_LEVEL, "(d)", is_quest(dun_level));
+ generate_cave();
+ }
+
+ /* Close stuff */
+ close_game();
+
+ /* Quit */
+ quit(NULL);
+}
+
diff --git a/src/dungeon.pkg b/src/dungeon.pkg
new file mode 100644
index 00000000..f5e4045f
--- /dev/null
+++ b/src/dungeon.pkg
@@ -0,0 +1,1607 @@
+/* File: dungeon.pkg */
+
+/*
+ * Purpose: Lua interface defitions for dungeon routines.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @name Cave Grid
+ * @note Special cave grid flags
+ * @{
+ */
+
+/** @def CAVE_MARK
+ * @note memorized feature
+ */
+#define CAVE_MARK 0x0001
+
+/** @def CAVE_GLOW
+ * @note self-illuminating
+ */
+#define CAVE_GLOW 0x0002
+
+/** @def CAVE_ICKY
+ * @note part of a vault
+ */
+#define CAVE_ICKY 0x0004
+
+/** @def CAVE_ROOM
+ * @note part of a room
+ */
+#define CAVE_ROOM 0x0008
+
+/** @def CAVE_SEEN
+ * @note seen flag
+ */
+#define CAVE_SEEN 0x0010
+
+/** @def CAVE_VIEW
+ * @note view flag
+ */
+#define CAVE_VIEW 0x0020
+
+/** @def CAVE_TEMP
+ * @note temp flag
+ */
+#define CAVE_TEMP 0x0040
+
+/** @def CAVE_WALL
+ * @note wall flag
+ */
+#define CAVE_WALL 0x0080
+
+/** @def CAVE_TRDT
+ * @note trap detected
+ */
+#define CAVE_TRDT 0x0100
+
+/** @def CAVE_IDNT
+ * @note grid identified (fountains)
+ */
+#define CAVE_IDNT 0x0200
+
+/** @def CAVE_SPEC
+ * @note special mark(quests)
+ */
+#define CAVE_SPEC 0x0400
+
+/** @def CAVE_FREE
+ * @note no random generation on it
+ */
+#define CAVE_FREE 0x0800
+
+/** @def CAVE_DETECT
+ * @note Traps detected here
+ */
+#define CAVE_DETECT 0x1000
+
+/** @def CAVE_PLIT
+ * @note Player lit grid
+ */
+#define CAVE_PLIT 0x2000
+
+/** @def CAVE_MLIT
+ * @note Monster lit grid
+ */
+#define CAVE_MLIT 0x4000
+/** @} */
+
+/** @name Terrain Feature Indexes
+ * @note (see "lib/edit/f_info.txt")
+ * @{
+ */
+
+/* Nothing */
+/** @def FEAT_NONE */
+#define FEAT_NONE 0x00
+
+
+/* Basic features */
+/** @def FEAT_FLOOR */
+#define FEAT_FLOOR 0x01
+
+/** @def FEAT_FOUNTAIN */
+#define FEAT_FOUNTAIN 0x02
+
+/** @def FEAT_GLYPH */
+#define FEAT_GLYPH 0x03
+
+/** @def FEAT_OPEN */
+#define FEAT_OPEN 0x04
+
+/** @def FEAT_BROKEN */
+#define FEAT_BROKEN 0x05
+
+/** @def FEAT_LESS */
+#define FEAT_LESS 0x06
+
+/** @def FEAT_MORE */
+#define FEAT_MORE 0x07
+
+
+/* Quest features -KMW- */
+/** @def FEAT_QUEST_ENTER */
+#define FEAT_QUEST_ENTER 0x08
+
+/** @def FEAT_QUEST_EXIT */
+#define FEAT_QUEST_EXIT 0x09
+
+/** @def FEAT_QUEST_DOWN */
+#define FEAT_QUEST_DOWN 0x0A
+
+/** @def FEAT_QUEST_UP */
+#define FEAT_QUEST_UP 0x0B
+
+
+/* Shafts -GSN- */
+/** @def FEAT_SHAFT_DOWN */
+#define FEAT_SHAFT_DOWN 0x0D
+
+/** @def FEAT_SHAFT_UP */
+#define FEAT_SHAFT_UP 0x0E
+
+
+/* Basic feature */
+/** @def FEAT_EMPTY_FOUNTAIN */
+#define FEAT_EMPTY_FOUNTAIN 0x0F
+
+
+/* Feature 0x10 -- web */
+
+/* Traps */
+/** @def FEAT_TRAP */
+#define FEAT_TRAP 0x11
+
+
+/* Features 0x12 - 0x1F -- unused */
+
+/* Doors */
+/** @def FEAT_DOOR_HEAD */
+#define FEAT_DOOR_HEAD 0x20
+
+/** @def FEAT_DOOR_TAIL */
+#define FEAT_DOOR_TAIL 0x2F
+
+
+/* Extra */
+/** @def FEAT_SECRET */
+#define FEAT_SECRET 0x30
+
+/** @def FEAT_RUBBLE */
+#define FEAT_RUBBLE 0x31
+
+
+/* Seams */
+/** @def FEAT_MAGMA */
+#define FEAT_MAGMA 0x32
+
+/** @def FEAT_QUARTZ */
+#define FEAT_QUARTZ 0x33
+
+/** @def FEAT_MAGMA_H */
+#define FEAT_MAGMA_H 0x34
+
+/** @def FEAT_QUARTZ_H */
+#define FEAT_QUARTZ_H 0x35
+
+/** @def FEAT_MAGMA_K */
+#define FEAT_MAGMA_K 0x36
+
+/** @def FEAT_QUARTZ_K */
+#define FEAT_QUARTZ_K 0x37
+
+
+/* Walls */
+/** @def FEAT_WALL_EXTRA */
+#define FEAT_WALL_EXTRA 0x38
+
+/** @def FEAT_WALL_INNER */
+#define FEAT_WALL_INNER 0x39
+
+/** @def FEAT_WALL_OUTER */
+#define FEAT_WALL_OUTER 0x3A
+
+/** @def FEAT_WALL_SOLID */
+#define FEAT_WALL_SOLID 0x3B
+
+/** @def FEAT_PERM_EXTRA */
+#define FEAT_PERM_EXTRA 0x3C
+
+/** @def FEAT_PERM_INNER */
+#define FEAT_PERM_INNER 0x3D
+
+/** @def FEAT_PERM_OUTER */
+#define FEAT_PERM_OUTER 0x3E
+
+/** @def FEAT_PERM_SOLID */
+#define FEAT_PERM_SOLID 0x3F
+
+
+/* Explosive rune */
+/** @def FEAT_MINOR_GLYPH */
+#define FEAT_MINOR_GLYPH 0x40
+
+
+/* Pattern */
+/** @def FEAT_PATTERN_START */
+#define FEAT_PATTERN_START 0x41
+
+/** @def FEAT_PATTERN_1 */
+#define FEAT_PATTERN_1 0x42
+
+/** @def FEAT_PATTERN_2 */
+#define FEAT_PATTERN_2 0x43
+
+/** @def FEAT_PATTERN_3 */
+#define FEAT_PATTERN_3 0x44
+
+/** @def FEAT_PATTERN_4 */
+#define FEAT_PATTERN_4 0x45
+
+/** @def FEAT_PATTERN_END */
+#define FEAT_PATTERN_END 0x46
+
+/** @def FEAT_PATTERN_OLD */
+#define FEAT_PATTERN_OLD 0x47
+
+/** @def FEAT_PATTERN_XTRA1 */
+#define FEAT_PATTERN_XTRA1 0x48
+
+/** @def FEAT_PATTERN_XTRA2 */
+#define FEAT_PATTERN_XTRA2 0x49
+
+
+/* Shops */
+/** @def FEAT_SHOP */
+#define FEAT_SHOP 0x4A
+
+
+/* Permanent walls for quests */
+/** @def FEAT_QUEST1 */
+#define FEAT_QUEST1 0x4B
+
+/** @def FEAT_QUEST2 */
+#define FEAT_QUEST2 0x4C
+
+/** @def FEAT_QUEST3 */
+#define FEAT_QUEST3 0x4D
+
+/** @def FEAT_QUEST4 */
+#define FEAT_QUEST4 0x4E
+
+
+/* Features 0x4F - 0x53 -- unused */
+
+/* Additional terrains */
+/** @def FEAT_SHAL_WATER */
+#define FEAT_SHAL_WATER 0x54
+
+/** @def FEAT_DEEP_LAVA */
+#define FEAT_DEEP_LAVA 0x55
+
+/** @def FEAT_SHAL_LAVA */
+#define FEAT_SHAL_LAVA 0x56
+
+/** @def FEAT_DARK_PIT */
+#define FEAT_DARK_PIT 0x57
+
+/** @def FEAT_DIRT */
+#define FEAT_DIRT 0x58
+
+/** @def FEAT_GRASS */
+#define FEAT_GRASS 0x59
+
+/** @def FEAT_ICE */
+#define FEAT_ICE 0x5A
+
+/** @def FEAT_SAND */
+#define FEAT_SAND 0x5B
+
+/** @def FEAT_DEAD_TREE */
+#define FEAT_DEAD_TREE 0x5C
+
+/** @def FEAT_ASH */
+#define FEAT_ASH 0x5D
+
+/** @def FEAT_MUD */
+#define FEAT_MUD 0x5E
+
+/** @def FEAT_ICE_WALL */
+#define FEAT_ICE_WALL 0x5F
+
+/** @def FEAT_TREES */
+#define FEAT_TREES 0x60
+
+/** @def FEAT_MOUNTAIN */
+#define FEAT_MOUNTAIN 0x61
+
+/** @def FEAT_SANDWALL */
+#define FEAT_SANDWALL 0x62
+
+/** @def FEAT_SANDWALL_H */
+#define FEAT_SANDWALL_H 0x63
+
+/** @def FEAT_SANDWALL_K */
+#define FEAT_SANDWALL_K 0x64
+
+/* Feature 0x65 -- high mountain chain */
+/* Feature 0x66 -- nether mist */
+
+/* Features 0x67 - 0x9F -- unused */
+
+/** @def FEAT_BETWEEN
+ * @note 160
+ */
+#define FEAT_BETWEEN 0xA0
+
+/* Altars */
+/** @def FEAT_ALTAR_HEAD
+ * @note 161
+ */
+#define FEAT_ALTAR_HEAD 0xA1
+
+/** @def FEAT_ALTAR_TAIL
+ * @note 171
+ */
+#define FEAT_ALTAR_TAIL 0xAB
+
+/** @def FEAT_MARKER
+ * @note 172
+ */
+#define FEAT_MARKER 0xAC
+
+/* Feature 0xAD -- Underground Tunnel */
+/** @def FEAT_TAINTED_WATER
+ * @note 174
+ */
+#define FEAT_TAINTED_WATER 0xAE
+
+/** @def FEAT_MON_TRAP
+ * @note 175
+ */
+#define FEAT_MON_TRAP 0xAF
+
+/** @def FEAT_BETWEEN2
+ * @note 176
+ */
+#define FEAT_BETWEEN2 0xB0
+
+/** @def FEAT_LAVA_WALL
+ * @note 177
+ */
+#define FEAT_LAVA_WALL 0xB1
+
+/** @def FEAT_GREAT_FIRE
+ * @note 178
+ */
+#define FEAT_GREAT_FIRE 0xB2
+
+/** @def FEAT_WAY_MORE
+ * @note 179
+ */
+#define FEAT_WAY_MORE 0xB3
+
+/** @def FEAT_WAY_LESS
+ * @note 180
+ */
+#define FEAT_WAY_LESS 0xB4
+
+/* Feature 0xB5 -- field */
+
+/** @def FEAT_EKKAIA
+ * @note 182
+ */
+#define FEAT_EKKAIA 0xB6
+
+/* Features 0xB7 - 0xBA -- unused */
+
+/** @def FEAT_DEEP_WATER
+ * @note 187
+ */
+#define FEAT_DEEP_WATER 0xBB
+
+/** @def FEAT_GLASS_WALL
+ * @note 188
+ */
+#define FEAT_GLASS_WALL 0xBC
+
+/** @def FEAT_ILLUS_WALL
+ * @note 189
+ */
+#define FEAT_ILLUS_WALL 0xBD
+
+/* Feature 0xBE -- grass roof */
+/* Feature 0xBF -- grass roof top */
+/* Feature 0xC0 -- grass roof chimney */
+/* Feature 0xC1 -- brick roof */
+/* Feature 0xC2 -- brick roof top */
+/* Feature 0xC3 -- brick roof chimney */
+/* Feature 0xC4 -- window */
+/* Feature 0xC5 -- small window */
+/* Feature 0xC6 -- rain barrel */
+
+/** @def FEAT_FLOWER
+ * @note 199
+ */
+#define FEAT_FLOWER 0xC7
+
+/* Feature 0xC8 -- cobblestone road */
+/* Feature 0xC9 -- cobblestone with outlet */
+
+/** @def FEAT_SMALL_TREES
+ * @note 202
+ */
+#define FEAT_SMALL_TREES 0xCA
+
+/** @def FEAT_TOWN
+ * @note 203
+ */
+#define FEAT_TOWN 0xCB
+
+/* Feature 0xCC -- Underground Tunnel */
+
+/** @def FEAT_FIRE
+ * @note 205
+ */
+#define FEAT_FIRE 0xCD
+
+/* Feature 0xCE -- pile of rubble (permanent) */
+/* Features 0xCF - 0xFF -- unused */
+/** @} */
+
+/** @name Dungeon Type Flags (part 1)
+ * @{ */
+
+/** @def DF1_PRINCIPAL
+ * @note Is a principal dungeon
+ */
+#define DF1_PRINCIPAL 0x00000001L
+/** @def DF1_MAZE
+ * @note Is a maze-type dungeon
+ */
+#define DF1_MAZE 0x00000002L
+/** @def DF1_SMALLEST
+ * @note Creates VERY small levels like The Maze
+ */
+#define DF1_SMALLEST 0x00000004L
+/** @def DF1_SMALL
+ * @note Creates small levels like Dol Goldor
+ */
+#define DF1_SMALL 0x00000008L
+/** @def DF1_BIG
+ * @note Creates big levels like Moria, and Angband dungeons
+ */
+#define DF1_BIG 0x00000010L
+/** @def DF1_NO_DOORS
+ * @note No doors on rooms, like Barrowdowns, Old Forest etc)
+ */
+#define DF1_NO_DOORS 0x00000020L
+/** @def DF1_WATER_RIVER
+ * @note Allow a single water streamer on a level
+ */
+#define DF1_WATER_RIVER 0x00000040L
+/** @def DF1_LAVA_RIVER
+ * @note Allow a single lava streamer on a level
+ */
+#define DF1_LAVA_RIVER 0x00000080L
+/** @def DF1_WATER_RIVERS
+ * @note Allow multiple water streamers on a level
+ */
+#define DF1_WATER_RIVERS 0x00000100L
+/** @def DF1_LAVA_RIVERS
+ * @note Allow multiple lava streamers on a level
+ */
+#define DF1_LAVA_RIVERS 0x00000200L
+/** @def DF1_CAVE
+ * @note Allow orc-cave like 'fractal' rooms
+ */
+#define DF1_CAVE 0x00000400L
+/** @def DF1_CAVERN
+ * @note Allow cavern rooms
+ */
+#define DF1_CAVERN 0x00000800L
+/** @def DF1_NO_UP
+ * @note Disallow up stairs
+ */
+#define DF1_NO_UP 0x00001000L
+/** @def DF1_HOT
+ * @note Corpses on ground and in pack decay quicker through heat
+ */
+#define DF1_HOT 0x00002000L
+/** @def DF1_COLD
+ * @note Corpses on ground and in pack decay quicker through cold
+ */
+#define DF1_COLD 0x00004000L
+/** @def DF1_FORCE_DOWN
+ * @note No up stairs generated
+ */
+#define DF1_FORCE_DOWN 0x00008000L
+/** @def DF1_FORGET
+ * @note Features are forgotten, like the Maze and Illusory Castle
+ */
+#define DF1_FORGET 0x00010000L
+/** @def DF1_NO_DESTROY
+ * @note No destroyed levels in dungeon
+ */
+#define DF1_NO_DESTROY 0x00020000L
+/** @def DF1_SAND_VEIN
+ * @note Like in the sandworm lair
+ */
+#define DF1_SAND_VEIN 0x00040000L
+/** @def DF1_CIRCULAR_ROOMS
+ * @note Allow circular rooms
+ */
+#define DF1_CIRCULAR_ROOMS 0x00080000L
+/** @def DF1_EMPTY
+ * @note Allow arena levels
+ */
+#define DF1_EMPTY 0x00100000L
+/** @def DF1_DAMAGE_FEAT
+ * @note Effect specified in will affect all grids incl. terrain and monsters
+ */
+#define DF1_DAMAGE_FEAT 0x00200000L
+/** @def DF1_FLAT
+ * @note Creates paths to next areas at edge of level, like Barrowdowns
+ */
+#define DF1_FLAT 0x00400000L
+/** @def DF1_TOWER
+ * @note You start at bottom and go up rather than the reverse
+ */
+#define DF1_TOWER 0x00800000L
+/** @def DF1_RANDOM_TOWNS
+ * @note Allow random towns
+ */
+#define DF1_RANDOM_TOWNS 0x01000000L
+/** @def DF1_DOUBLE
+ * @note Generates everything at double size like Helcaraxe and Erebor
+ */
+#define DF1_DOUBLE 0x02000000L
+/** @def DF1_LIFE_LEVEL
+ * @note Creates dungeon level on modified 'game of life' algorithm
+ */
+#define DF1_LIFE_LEVEL 0x04000000L
+/** @def DF1_EVOLVE
+ * @note Evolving, pulsing levels like Heart of the Earth
+ */
+#define DF1_EVOLVE 0x08000000L
+/** @def DF1_ADJUST_LEVEL_1
+ * @note Minimum monster level will be equal to dungeon level
+ */
+#define DF1_ADJUST_LEVEL_1 0x10000000L
+/** @def DF1_ADJUST_LEVEL_2
+ * @note Minimum monster level will be double the dungeon level
+ */
+#define DF1_ADJUST_LEVEL_2 0x20000000L
+/** @def DF1_NO_RECALL
+ * @note No recall allowed
+ */
+#define DF1_NO_RECALL 0x40000000L
+/** @def DF1_NO_STREAMERS
+ * @note No streamers
+ */
+#define DF1_NO_STREAMERS 0x80000000L
+/** @} */
+
+/** @name Dungeon Type Flags (part 2)
+ * @{ */
+
+/** @def DF2_ADJUST_LEVEL_1_2
+ * @note Minimum monster level will be half the dungeon level
+ */
+#define DF2_ADJUST_LEVEL_1_2 0x00000001L
+
+/** @def DF2_NO_SHAFT
+ * @note No shafts
+ */
+#define DF2_NO_SHAFT 0x00000002L
+
+/** @def DF2_ADJUST_LEVEL_PLAYER
+ * @note Uses player level*2 instead of dungeon level for other ADJUST_LEVEL flags
+ */
+#define DF2_ADJUST_LEVEL_PLAYER 0x00000004L
+
+/** @def DF2_NO_TELEPORT */
+#define DF2_NO_TELEPORT 0x00000008L
+
+/** @def DF2_ASK_LEAVE */
+#define DF2_ASK_LEAVE 0x00000010L
+
+/** @def DF2_NO_STAIR */
+#define DF2_NO_STAIR 0x00000020L
+
+/** @def DF2_SPECIAL */
+#define DF2_SPECIAL 0x00000040L
+
+/** @def DF2_NO_NEW_MONSTER */
+#define DF2_NO_NEW_MONSTER 0x00000080L
+
+/** @def DF2_DESC */
+#define DF2_DESC 0x00000100L
+
+/** @def DF2_NO_GENO */
+#define DF2_NO_GENO 0x00000200L
+
+/** @def DF2_NO_BREATH
+ * @note Oups, cannot breath here
+ */
+#define DF2_NO_BREATH 0x00000400L
+
+/** @def DF2_WATER_BREATH
+ * @note Oups, cannot breath here, need water breathing
+ */
+#define DF2_WATER_BREATH 0x00000800L
+
+/** @def DF2_ELVEN
+ * @note Try to create elven monster ego
+ */
+#define DF2_ELVEN 0x00001000L
+
+/** @def DF2_DWARVEN
+ * @note Try to create dwarven monster ego
+ */
+#define DF2_DWARVEN 0x00002000L
+
+/** @def DF2_NO_EASY_MOVE
+ * @note Forbid stuff like teleport level, probability travel, ...
+ */
+#define DF2_NO_EASY_MOVE 0x00004000L
+
+/** @def DF2_NO_RECALL_OUT
+ * @note Cannot recall out of the place
+ */
+#define DF2_NO_RECALL_OUT 0x00008000L
+
+/** @def DF2_DESC_ALWAYS
+ * @note Always shows the desc
+ */
+#define DF2_DESC_ALWAYS 0x00010000L
+/** @} */
+
+/** @var level_flags1;
+ * @brief Number
+ */
+extern u32b dungeon_flags1@level_flags1;
+
+/** @var level_flags2;
+ * @brief Number
+ */
+extern u32b dungeon_flags2@level_flags2;
+
+/** @def MAX_HGT
+ * @note Maximum dungeon height in grids, must be a multiple of SCREEN_HGT,
+ * probably hard-coded to SCREEN_HGT * 3.
+ */
+#define MAX_HGT 66
+
+
+/** @def MAX_WID
+ * @note Maximum dungeon width in grids, must be a multiple of SCREEN_WID,
+ * probably hard-coded to SCREEN_WID * 3.
+ */
+#define MAX_WID 198
+
+
+/** @name Town Defines
+ * @{ */
+
+/** @def TOWN_RANDOM
+ * @note First random town
+ */
+#define TOWN_RANDOM 20
+
+/** @def TOWN_DUNGEON
+ * @note Maximun number of towns per dungeon
+ */
+#define TOWN_DUNGEON 4
+
+/** @def TOWN_CHANCE
+ * @note Chance of 1 town
+ */
+#define TOWN_CHANCE 50
+
+/** @} */
+
+/** @name Wilderness Terrains
+ * @{
+ */
+
+/** @def TERRAIN_EDGE
+ * @note Edge of the World
+ */
+#define TERRAIN_EDGE 0
+
+/** @def TERRAIN_TOWN
+ * @note Town
+ */
+#define TERRAIN_TOWN 1
+
+/** @def TERRAIN_DEEP_WATER
+ * @note Deep water
+ */
+#define TERRAIN_DEEP_WATER 2
+
+/** @def TERRAIN_SHALLOW_WATER
+ * @note Shallow water
+ */
+#define TERRAIN_SHALLOW_WATER 3
+
+/** @def TERRAIN_SWAMP
+ * @note Swamp
+ */
+#define TERRAIN_SWAMP 4
+
+/** @def TERRAIN_DIRT
+ * @note Dirt
+ */
+#define TERRAIN_DIRT 5
+
+/** @def TERRAIN_GRASS
+ * @note Grass
+ */
+#define TERRAIN_GRASS 6
+
+/** @def TERRAIN_TREES
+ * @note Trees
+ */
+#define TERRAIN_TREES 7
+
+/** @def TERRAIN_DESERT
+ * @note Desert
+ */
+#define TERRAIN_DESERT 8
+
+/** @def TERRAIN_SHALLOW_LAVA
+ * @note Shallow lava
+ */
+#define TERRAIN_SHALLOW_LAVA 9
+
+/** @def TERRAIN_DEEP_LAVA
+ * @note Deep lava
+ */
+#define TERRAIN_DEEP_LAVA 10
+
+/** @def TERRAIN_MOUNTAIN
+ * @note Mountain
+ */
+#define TERRAIN_MOUNTAIN 11
+
+/** @def MAX_WILD_TERRAIN */
+#define MAX_WILD_TERRAIN 18
+/** @} */
+
+/** @struct border_type
+ * @note Border
+ */
+struct border_type
+{
+ /** @structvar north[MAX_WID]
+ * @brief Number
+ */
+ byte north[MAX_WID];
+
+ /** @structvar south[MAX_WID]
+ * @brief Number
+ */
+ byte south[MAX_WID];
+
+ /** @structvar east[MAX_HGT]
+ * @brief Number
+ */
+ byte east[MAX_HGT];
+
+ /** @structvar west[MAX_HGT]
+ * @brief Number
+ */
+ byte west[MAX_HGT];
+
+ /** @structvar north_west
+ * @brief Number
+ */
+ byte north_west;
+
+ /** @structvar north_east
+ * @brief Number
+ */
+ byte north_east;
+
+ /** @structvar south_west
+ * @brief Number
+ */
+ byte south_west;
+
+ /** @structvar south_east
+ * @brief Number
+ */
+ byte south_east;
+};
+
+
+/** @struct wilderness_type_info
+ * @note A structure describing a wilderness area
+ * with a terrain, a town or a dungeon entrance
+ */
+struct wilderness_type_info
+{
+ /** @structvar name
+ * @brief Number
+ * @note Name (offset)
+ */
+ u32b name;
+
+ /** @structvar text
+ * @brief Number
+ * @note Text (offset)
+ */
+ u32b text;
+
+ /** @structvar entrance
+ * @brief Number
+ * @note Which town is there(<1000 i's a town, >=1000 it a dungeon)
+ */
+ u16b entrance;
+
+ /** @structvar road
+ * @brief Number
+ * @note Flags of road
+ */
+ byte road;
+
+ /** @structvar level
+ * @brief Number
+ * @note Difficulty level
+ */
+ int level;
+
+ /** @structvar flags1
+ * @brief Number
+ * @note Some flags
+ */
+ u32b flags1;
+
+ /** @structvar feat
+ * @brief Number
+ * @note The feature of f_info.txt that is used to allow passing, ... and to get a char/color/graph
+ */
+ byte feat;
+
+ /** @structvar terrain_idx
+ * @brief Number
+ * @note Terrain index(defined in defines.h)
+ */
+ byte terrain_idx;
+
+ /** @structvar terrain[MAX_WILD_TERRAIN]
+ * @brief Number
+ * @note Feature types for the plasma generator
+ */
+ byte terrain[MAX_WILD_TERRAIN];
+};
+
+/** @struct wilderness_map
+ * @note A structure describing a wilderness map
+ */
+struct wilderness_map
+{
+ /** @structvar feat
+ * @brief Number
+ * @note Wilderness feature
+ */
+ int feat;
+
+ /** @structvar seed
+ * @brief Number
+ * @note Seed for the RNG
+ */
+ u32b seed;
+
+ /** @structvar entrance
+ * @brief Number
+ * @note Entrance for dungeons
+ */
+ u16b entrance;
+
+ /** @structvar known
+ * @brief Boolean
+ * @note Is it seen by the player ?
+ */
+ bool known;
+};
+
+
+/** @struct town_type
+ * @note A structure describing a town with
+ * stores and buildings
+ */
+struct town_type
+{
+ /** @structvar name
+ * @brief String
+ */
+ cptr name;
+
+ /** @structvar seed
+ * @brief Number
+ * @note Seed for RNG
+ */
+ u32b seed;
+
+ /** @structvar *store
+ * @brief store_type
+ * @note The stores [max_st_idx]
+ */
+ store_type store[max_st_idx];
+
+ /** @structvar numstores
+ * @brief Number
+ */
+ byte numstores;
+
+ /** @structvar flags
+ * @brief Number
+ * @note Town flags
+ */
+ byte flags;
+
+ /** @structvar stocked
+ * @brief Boolean
+ * @note Is the town actually stocked ?
+ * Left this for the sake of compatibility
+ */
+ bool stocked;
+
+ /** @structvar destroyed
+ * @brief Boolean
+ * @note Is the town destroyed?
+ */
+ bool destroyed;
+};
+
+/** @var max_towns
+ * @brief Number
+ */
+extern u16b max_towns;
+
+/** @var town_info[max_towns]
+ * @brief town_type
+ */
+extern town_type town_info[max_towns];
+
+/** @struct rule_type
+ * Define monster generation rules
+ */
+struct rule_type
+{
+ /** @structvar mode
+ * @brief Number
+ * @note Mode of combination of the monster flags
+ */
+ byte mode;
+
+ /** @structvar percent
+ * @brief Number
+ * @note Percent of monsters affected by the rule
+ */
+ byte percent;
+
+ /** @structvar mflags1
+ * @brief Number
+ * @note The monster flags that are allowed
+ */
+ u32b mflags1;
+
+ /** @structvar mflags2
+ * @brief Number
+ */
+ u32b mflags2;
+
+ /** @structvar mflags3
+ * @brief Number
+ */
+ u32b mflags3;
+
+ /** @structvar mflags4
+ * @brief Number
+ */
+ u32b mflags4;
+
+ /** @structvar mflags5
+ * @brief Number
+ */
+ u32b mflags5;
+
+ /** @structvar mflags6
+ * @brief Number
+ */
+ u32b mflags6;
+
+ /** @structvar mflags7
+ * @brief Number
+ */
+ u32b mflags7;
+
+ /** @structvar mflags8
+ * @brief Number
+ */
+ u32b mflags8;
+
+ /** @structvar mflags9
+ * @brief Number
+ */
+ u32b mflags9;
+
+ /** @structvar r_char[5]
+ * @brief String
+ * @note Monster race allowed
+ */
+ char r_char[5];
+};
+
+/** @struct obj_theme
+ * @brief "Themed" objects.
+ * @note Probability in percent for each class of objects to be dropped.
+ * This could perhaps be an array - but that wouldn't be as clear.
+ */
+struct obj_theme
+{
+ /** @structvar treasure
+ * @brief Number
+ */
+ byte treasure;
+
+ /** @structvar combat
+ * @brief Number
+ */
+ byte combat;
+
+ /** @structvar magic
+ * @brief Number
+ */
+ byte magic;
+
+ /** @structvar tools
+ * @brief Number
+ */
+ byte tools;
+};
+
+/** @struct dungeon_info_type
+ * A structure for the != dungeon types
+ */
+struct dungeon_info_type
+{
+ /** @structvar name
+ * @brief Number
+ * @note Name
+ */
+ u32b name;
+
+ /** @structvar text
+ * @brief Number
+ * @note Description
+ */
+ u32b text;
+
+ /** @structvar short_name[3]
+ * @brief String
+ * @note Short name
+ */
+ char short_name[3];
+
+ /** @structvar floor1
+ * @brief Number
+ * @note Floor tile 1
+ */
+ s16b floor1;
+
+ /** @structvar floor_percent1[2]
+ * @brief Number
+ * @note Chance of type 1
+ */
+ byte floor_percent1[2];
+
+ /** @structvar floor2
+ * @brief Number
+ * @note Floor tile 2
+ */
+ s16b floor2;
+
+ /** @structvar floor_percent2[2]
+ * @brief Number
+ * @note Chance of type 2
+ */
+ byte floor_percent2[2];
+
+ /** @structvar floor3
+ * @brief Number
+ * @note Floor tile 3
+ */
+ s16b floor3;
+
+ /** @structvar floor_percent3[2]
+ * @brief Number
+ * @note Chance of type 3
+ */
+ byte floor_percent3[2];
+
+ /** @structvar outer_wall
+ * @brief Number
+ * @note Outer wall tile
+ */
+ s16b outer_wall;
+
+ /** @structvar inner_wall
+ * @brief Number
+ * @note Inner wall tile
+ */
+ s16b inner_wall;
+
+ /** @structvar fill_type1
+ * @brief Number
+ * @note Cave tile 1
+ */
+ s16b fill_type1;
+
+ /** @structvar fill_percent1[2]
+ * @brief Number
+ * @note Chance of type 1
+ */
+ byte fill_percent1[2];
+
+ /** @structvar fill_type2
+ * @brief Number
+ * @note Cave tile 2
+ */
+ s16b fill_type2;
+
+ /** @structvar fill_percent2[2]
+ * @brief Number
+ * @note Chance of type 2
+ */
+ byte fill_percent2[2];
+
+ /** @structvar fill_type3
+ * @brief Number
+ * @note Cave tile 3
+ */
+ s16b fill_type3;
+
+ /** @structvar fill_percent3[2]
+ * @brief Number
+ * @note Chance of type 3
+ */
+ byte fill_percent3[2];
+
+ /** @structvar fill_method
+ * @brief Number
+ * @note Smoothing parameter for the above
+ */
+ byte fill_method;
+
+ /** @structvar mindepth
+ * @brief Number
+ * @note Minimal depth
+ */
+ s16b mindepth;
+
+ /** @structvar maxdepth
+ * @brief Number
+ * @note Maximal depth
+ */
+ s16b maxdepth;
+
+ /** @structvar principal
+ * @brief Boolean
+ * @note If it's a part of the main dungeon
+ */
+ bool principal;
+
+ /** @structvar next
+ * @brief Number
+ * @note The next part of the main dungeon
+ */
+ byte next;
+
+ /** @structvar min_plev
+ * @brief Number
+ * @note Minimal plev needed to enter -- it's an anti-cheating mesure
+ */
+ byte min_plev;
+
+ /** @structvar min_m_alloc_level
+ * @brief Number
+ * @note Minimal number of monsters per level
+ */
+ int min_m_alloc_level;
+
+ /** @structvar max_m_alloc_chance
+ * @brief Number
+ * @note There is a 1/max_m_alloc_chance chance per round of creating a new monster
+ */
+ int max_m_alloc_chance;
+
+ /** @structvar flags1
+ * @brief Number
+ * @note Flags 1
+ */
+ u32b flags1;
+
+ /** @structvar flags2
+ * @brief Number
+ * @note Flags 1
+ */
+ u32b flags2;
+
+ /** @structvar size_x
+ * @brief Number
+ */
+ int size_x;
+
+ /** @structvar size_y
+ * @brief Number
+ * @note Desired numers of panels
+ */
+ int size_y;
+
+ /** @structvar rule_percents[100]
+ * @brief Number
+ * @note Flat rule percents
+ */
+ byte rule_percents[100];
+
+ /** @structvar rules[5]
+ * @brief rule_type
+ * @note Monster generation rules
+ */
+ rule_type rules[5];
+
+ /** @structvar final_object
+ * @brief Number
+ * @note The object you'll find at the bottom
+ */
+ int final_object;
+
+ /** @structvar final_artifact
+ * @brief Number
+ * @note The artifact you'll find at the bottom
+ */
+ int final_artifact;
+
+ /** @structvar final_guardian
+ * @brief Number
+ * @note The artifact's guardian. If an artifact is specified, then it's NEEDED
+ */
+ int final_guardian;
+
+ /** @structvar ix
+ * @brief Number
+ */
+ int ix;
+
+ /** @structvar iy
+ * @brief Number
+ */
+ int iy;
+
+ /** @structvar ox
+ * @brief Number
+ */
+ int ox;
+
+ /** @structvar oy
+ * @brief Number
+ * @note Wilderness coordinates of the entrance/output of the dungeon
+ */
+ int oy;
+
+ /** @structvar objs
+ * @brief obj_theme
+ * @note The drops type
+ */
+ obj_theme objs;
+
+ /** @structvar d_dice[4]
+ * @brief Number
+ * @note Number of dices
+ */
+ int d_dice[4];
+
+ /** @structvar d_side[4]
+ * @brief Number
+ * @note Number of sides
+ */
+ int d_side[4];
+
+ /** @structvar d_frequency[4]
+ * @brief Number
+ * @note Frequency of damage (1 is the minimum)
+ */
+ int d_frequency[4];
+
+ /** @structvar d_type[4]
+ * @brief Number
+ * @note Type of damage
+ */
+ int d_type[4];
+
+ /** @structvar t_idx[TOWN_DUNGEON]
+ * @brief Number
+ * @note The towns
+ */
+ s16b t_idx[TOWN_DUNGEON];
+
+ /** @structvar t_level[TOWN_DUNGEON]
+ * @brief Number
+ * @note The towns levels
+ */
+ s16b t_level[TOWN_DUNGEON];
+
+ /** @structvar t_num
+ * @brief Number
+ * @note Number of towns
+ */
+ s16b t_num;
+};
+
+/** @var max_d_idx
+ * @brief Number
+ */
+extern u16b max_d_idx;
+
+/** @var d_info[max_d_idx]
+ * @brief dungeon_info_type
+ */
+extern dungeon_info_type d_info[max_d_idx];
+
+/** @var *d_name
+ * @brief String
+ */
+extern char *d_name;
+
+/** @var *d_text
+ * @brief String
+ */
+extern char *d_text;
+
+/** @var max_wild_x
+ * @brief Number
+ */
+extern u16b max_wild_x;
+
+/** @var max_wild_y
+ * @brief Number
+ */
+extern u16b max_wild_y;
+
+/** @var max_wf_idx
+ * @brief Number
+ */
+extern u16b max_wf_idx;
+
+/** @var wf_info[max_wf_idx]
+ * @brief wilderness_type_info
+ */
+extern wilderness_type_info wf_info[max_wf_idx];
+
+/** @var *wf_name
+ * @brief String
+ */
+extern char *wf_name;
+
+/** @var *wf_text
+ * @brief String
+ */
+extern char *wf_text;
+
+/** @var DUNGEON_DEATH
+ * @brief Number
+ */
+extern s32b DUNGEON_DEATH;
+
+/** @var current_dungeon_idx;
+ * @brief Number
+ */
+extern byte dungeon_type@current_dungeon_idx;
+
+/*
+ * tolua doesnt like wierd arraysn, I'll use accessing functions
+ * extern wilderness_map wild_map[max_wild_y][max_wild_x];
+ */
+$static wilderness_map* lua_get_wild_map(int y, int x) { return &wild_map[y][x]; }
+
+/** @fn wild_map(int y, int x);
+ * @brief Return a map of the wilderness at coordinate (y,x).\n
+ * @param y Number \n y coordinate of wilderness map
+ * @brief Y-coordinate
+ * @param x Number \n x coordinate of wilderness map
+ * @brief X-coordinate
+ * @return wilderness_map \n map of wilderness at coordinate (y,x)
+ * @note (see file w_dun.c)
+ */
+wilderness_map* lua_get_wild_map@wild_map(int y, int x);
+
+/** @fn place_trap(int y, int x)
+ * @brief Place a random trap at the given location.\n
+ * @param y Number \n y coordinate of dungeon
+ * @brief Y-coordinate
+ * @param x Number \n x coordinate of dungeon
+ * @brief X-coordinate
+ * @note
+ * Places a random trap at the given location.\n
+ * The location must be a valid, empty, clean, floor grid.
+ * @note (see file traps.c)
+ */
+extern void place_trap(int y, int x);
+
+/** @fn place_floor(int y, int x)
+ * @brief Place floor terrain at (y, x).\n
+ * @param y Number \n y coordinate of dungeon
+ * @brief Y-coordinate
+ * @param x Number \n x coordinate of dungeon
+ * @brief X-coordinate
+ * @note
+ * Place floor terrain at (y, x) according to dungeon info.
+ * @note (see file cave.c)
+ */
+extern void place_floor(int y, int x);
+
+/** @fn place_filler(int y, int x)
+ * @brief Place a cave filler at (y, x).\n
+ * @param y Number \n y coordinate of dungeon
+ * @brief Y-coordinate
+ * @param x Number \n x coordinate of dungeon
+ * @brief X-coordinate
+ * @note (see file generate.c)
+ */
+extern void place_filler(int y, int x);
+
+/** @fn new_player_spot(int branch)
+ * @brief Places player in a new location.\n
+ * @param branch Number \n branch is the dungeon branch (if any).
+ * @brief Dungeon branch
+ * @return Boolean \n TRUE if player was placed successfully, otherwise FALSE.
+ * The global values py and px are updated.
+ * @note
+ * Up to 5000 attempts are made to place the player in the dungeon. The grid
+ * must be a naked floor and not an anti-teleport grid. In some circumstances
+ * stairs or ways in/out may be created under the player.
+ * @note (see file generate.c)
+ */
+extern bool new_player_spot(int branch);
+
+/** @fn get_level_desc(char *buf)
+ * @brief Return the special level desc.\n
+ * @param *buf String
+ * @brief Description
+ * @return *buf String \n The level description
+ * @return Boolean \n TRUE if a level description was returned, otherwise FALSE
+ * @note
+ * This is the 'D' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern bool get_level_desc(char *buf);
+
+/** @fn get_level_flags()
+ * These are the 'F' lines in the dngn files.
+ * @note (see file levels.c)
+ */
+extern void get_level_flags();
+
+/** @fn get_dungeon_name(char *buf)
+ * @brief Return the special level name.\n
+ * @param *buf String
+ * @brief Name
+ * @return *buf String \n The level name
+ * @return Boolean \n TRUE if a level name was returned, otherwise FALSE
+ * @note
+ * This is the 'N' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern bool get_dungeon_name(char *buf);
+
+/** @fn get_dungeon_special(char *buf)
+ * @brief Return the map filename.\n
+ * @param *buf String
+ * @brief Map filename
+ * @return *buf String \n The map filename
+ * @return Boolean \n TRUE if a map filename was returned, otherwise FALSE
+ * @note
+ * This is the 'S' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern bool get_dungeon_special(char *buf);
+
+/** @fn get_command(const char *file, char comm, char *param)
+ * @brief Return the parameter of command "comm" in file "*file".\n
+ * @param *file String \n name of the dungeon file.
+ * @brief Dungeon file
+ * @param comm String \n The command \n
+ * 'A' = father branch, 'B' = branch, 'D' = desccription, 'L' = father level,
+ * 'N' = name, 'S' = savefile extension, 'U' = map filename
+ * @brief Command
+ * @param *param String
+ * @brief Parameter
+ * @return *param String \n The result of the command
+ * @return Boolean \n TRUE if a result is returned, otherwise FALSE
+ * @note (see file levels.c)
+ */
+extern bool get_command(const char *file, char comm, char *param);
+
+/** @fn get_branch()
+ * @brief return the dungeon branch starting form the current dungeon/level.
+ * @return Number \n The branch
+ * @note
+ * This is the 'B' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern int get_branch();
+
+/** @fn get_fbranch()
+ * @brief Return the father dungeon branch.
+ * @return Number \n The father branch
+ * @note
+ * This is the 'A' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern int get_fbranch();
+
+/** @fn get_flevel()
+ * @brief Return the father dungeon level.
+ * @return Number \n The father level
+ * @note
+ * This is the 'L' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern int get_flevel();
+
+/** @fn get_dungeon_save(char *buf)
+ * @brief Return the extension of the savefile for the level.\n
+ * @param *buf String
+ * @brief Savefile extension
+ * @return *buf String \n The savefile extension
+ * @return Boolean \n TRUE if a savefile extension was returned, otherwise FALSE
+ * This is the 'S' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern bool get_dungeon_save(char *buf);
diff --git a/src/externs.h b/src/externs.h
new file mode 100644
index 00000000..197c1c8a
--- /dev/null
+++ b/src/externs.h
@@ -0,0 +1,1851 @@
+/* File: externs.h */
+
+/* Purpose: extern declarations (variables and functions) */
+
+/*
+ * Note that some files have their own header files
+ * (z-virt.h, z-util.h, z-form.h, term.h, random.h)
+ */
+
+/*
+ * Options for inc_stack_size_ex
+ */
+typedef enum { OPTIMIZE, NO_OPTIMIZE } optimize_flag;
+typedef enum { DESCRIBE, NO_DESCRIBE } describe_flag;
+
+
+/*
+ * Automatically generated "variable" declarations
+ */
+
+extern int max_macrotrigger;
+extern char *macro_template;
+extern char *macro_modifier_chr;
+extern char *macro_modifier_name[MAX_MACRO_MOD];
+extern char *macro_trigger_name[MAX_MACRO_TRIG];
+extern char *macro_trigger_keycode[2][MAX_MACRO_TRIG];
+
+/* tables.c */
+extern s16b ddd[9];
+extern s16b ddx[10];
+extern s16b ddy[10];
+extern s16b ddx_ddd[9];
+extern s16b ddy_ddd[9];
+extern char hexsym[16];
+extern byte adj_val_min[];
+extern byte adj_val_max[];
+extern byte adj_mag_study[];
+extern byte adj_mag_mana[];
+extern byte adj_mag_fail[];
+extern byte adj_mag_stat[];
+extern byte adj_chr_gold[];
+extern byte adj_int_dev[];
+extern byte adj_wis_sav[];
+extern byte adj_dex_dis[];
+extern byte adj_int_dis[];
+extern byte adj_dex_ta[];
+extern byte adj_str_td[];
+extern byte adj_dex_th[];
+extern byte adj_str_th[];
+extern byte adj_str_wgt[];
+extern byte adj_str_hold[];
+extern byte adj_str_dig[];
+extern byte adj_str_blow[];
+extern byte adj_dex_blow[];
+extern byte adj_dex_safe[];
+extern byte adj_con_fix[];
+extern byte adj_con_mhp[];
+extern byte blows_table[12][12];
+extern s16b arena_monsters[MAX_ARENA_MONS];
+extern byte extract_energy[300];
+extern s32b player_exp[PY_MAX_LEVEL];
+extern player_sex sex_info[MAX_SEXES];
+extern deity_type deity_info_init[MAX_GODS_INIT];
+extern cptr color_names[16];
+extern cptr stat_names[6];
+extern cptr stat_names_reduced[6];
+extern cptr window_flag_desc[32];
+extern option_type option_info[];
+extern cptr chaos_patrons[MAX_PATRON];
+extern int chaos_stats[MAX_PATRON];
+extern int chaos_rewards[MAX_PATRON][20];
+extern martial_arts bear_blows[MAX_BEAR];
+extern martial_arts ma_blows[MAX_MA];
+extern magic_power mindcraft_powers[MAX_MINDCRAFT_POWERS];
+extern magic_power necro_powers[MAX_NECRO_POWERS];
+extern magic_power mimic_powers[MAX_MIMIC_POWERS];
+extern magic_power symbiotic_powers[MAX_SYMBIOTIC_POWERS];
+extern cptr deity_rarity[2];
+extern cptr deity_niceness[10];
+extern cptr deity_standing[11];
+extern move_info_type move_info[9];
+extern tactic_info_type tactic_info[9];
+extern activation activation_info[MAX_T_ACT];
+extern inscription_info_type inscription_info[MAX_INSCRIPTIONS];
+extern cptr sense_desc[];
+extern flags_group flags_groups[MAX_FLAG_GROUP];
+extern power_type powers_type_init[POWER_MAX_INIT];
+extern quest_type quest_info[MAX_Q_IDX_INIT];
+extern cptr artifact_names_list;
+extern monster_power monster_powers[96];
+extern tval_desc tval_descs[];
+extern between_exit between_exits[MAX_BETWEEN_EXITS];
+extern int month_day[9];
+extern cptr month_name[9];
+extern cli_comm *cli_info;
+extern int cli_total;
+extern quest_type quest_init_tome[MAX_Q_IDX_INIT];
+extern int max_body_part[BODY_MAX];
+extern gf_name_type gf_names[];
+
+
+/* variable.c */
+extern cptr copyright[5];
+extern byte version_major;
+extern byte version_minor;
+extern byte version_patch;
+extern byte version_extra;
+extern byte sf_major;
+extern byte sf_minor;
+extern byte sf_patch;
+extern byte sf_extra;
+extern u32b sf_xtra;
+extern u32b sf_when;
+extern u16b sf_lives;
+extern u16b sf_saves;
+extern u32b vernum; /* Version flag */
+extern bool_ arg_fiddle;
+extern bool_ arg_wizard;
+extern bool_ arg_sound;
+extern bool_ arg_graphics;
+extern bool_ arg_force_original;
+extern bool_ arg_force_roguelike;
+extern bool_ arg_bigtile;
+extern bool_ character_generated;
+extern bool_ character_dungeon;
+extern bool_ character_loaded;
+extern bool_ character_saved;
+extern bool_ character_icky;
+extern bool_ character_xtra;
+extern u32b seed_flavor;
+extern s16b command_cmd;
+extern s16b command_arg;
+extern s16b command_rep;
+extern s16b command_dir;
+extern s16b command_wrk;
+extern s16b command_new;
+extern s32b energy_use;
+extern s16b choose_default;
+extern bool_ create_up_stair;
+extern bool_ create_down_stair;
+extern bool_ create_up_shaft;
+extern bool_ create_down_shaft;
+extern bool_ msg_flag;
+extern bool_ alive;
+extern bool_ death;
+extern s16b running;
+extern s16b resting;
+extern s16b cur_hgt;
+extern s16b cur_wid;
+extern s16b dun_level;
+extern s16b old_dun_level;
+extern s16b num_repro;
+extern s16b object_level;
+extern s16b monster_level;
+extern s32b turn;
+extern s32b old_turn;
+extern bool_ wizard;
+extern bool_ use_sound;
+extern bool_ use_graphics;
+extern bool_ use_bigtile;
+extern byte graphics_mode;
+extern u16b total_winner;
+extern u16b has_won;
+extern u16b noscore;
+extern s16b signal_count;
+extern bool_ inkey_base;
+extern bool_ inkey_xtra;
+extern bool_ inkey_scan;
+extern bool_ inkey_flag;
+extern s16b coin_type;
+extern bool_ opening_chest;
+extern bool_ shimmer_monsters;
+extern bool_ shimmer_objects;
+extern bool_ repair_monsters;
+extern bool_ repair_objects;
+extern s16b inven_nxt;
+extern s16b inven_cnt;
+extern s16b equip_cnt;
+extern s16b o_max;
+extern s16b o_cnt;
+extern s16b m_max;
+extern s16b m_cnt;
+extern s16b hack_m_idx;
+extern s16b hack_m_idx_ii;
+extern int total_friends;
+extern s32b total_friend_levels;
+extern int leaving_quest;
+extern bool_ multi_rew;
+extern char summon_kin_type;
+extern bool_ hack_mind;
+extern bool_ hack_corruption;
+extern bool_ is_autosave;
+extern int artifact_bias;
+extern FILE *text_out_file;
+extern void (*text_out_hook)(byte a, cptr str);
+extern int text_out_wrap;
+extern int text_out_indent;
+extern int highscore_fd;
+extern bool_ show_inven_graph;
+extern bool_ show_store_graph;
+extern bool_ show_equip_graph;
+extern bool_ rogue_like_commands;
+extern bool_ quick_messages;
+extern bool_ other_query_flag;
+extern bool_ carry_query_flag;
+extern bool_ always_pickup;
+extern bool_ prompt_pickup_heavy;
+extern bool_ always_repeat;
+extern bool_ use_old_target;
+extern bool_ depth_in_feet;
+extern bool_ hilite_player;
+extern bool_ ring_bell;
+extern bool_ find_ignore_stairs;
+extern bool_ find_ignore_doors;
+extern bool_ find_cut;
+extern bool_ find_examine;
+extern bool_ disturb_near;
+extern bool_ disturb_move;
+extern bool_ disturb_panel;
+extern bool_ disturb_detect;
+extern bool_ disturb_state;
+extern bool_ disturb_minor;
+extern bool_ disturb_other;
+extern bool_ avoid_abort;
+extern bool_ avoid_shimmer;
+extern bool_ avoid_other;
+extern bool_ flush_disturb;
+extern bool_ flush_failure;
+extern bool_ flush_command;
+extern bool_ fresh_before;
+extern bool_ fresh_after;
+extern bool_ fresh_message;
+extern bool_ alert_hitpoint;
+extern bool_ alert_failure;
+extern bool_ view_yellow_lite;
+extern bool_ view_bright_lite;
+extern bool_ view_granite_lite;
+extern bool_ view_special_lite;
+extern bool_ plain_descriptions;
+extern bool_ stupid_monsters;
+extern bool_ auto_destroy;
+extern bool_ wear_confirm;
+extern bool_ confirm_stairs;
+extern bool_ disturb_pets;
+extern bool_ view_perma_grids;
+extern bool_ view_torch_grids;
+extern bool_ monster_lite;
+extern bool_ flow_by_sound;
+extern bool_ track_follow;
+extern bool_ track_target;
+extern bool_ stack_allow_items;
+extern bool_ stack_allow_wands;
+extern bool_ stack_force_notes;
+extern bool_ stack_force_costs;
+extern bool_ view_reduce_lite;
+extern bool_ view_reduce_view;
+extern bool_ auto_haggle;
+extern bool_ auto_scum;
+extern bool_ expand_look;
+extern bool_ expand_list;
+extern bool_ dungeon_align;
+extern bool_ dungeon_stair;
+extern bool_ smart_learn;
+extern bool_ smart_cheat;
+extern bool_ show_labels;
+extern bool_ show_weights;
+extern bool_ show_choices;
+extern bool_ show_details;
+extern bool_ testing_stack;
+extern bool_ testing_carry;
+extern bool_ cheat_peek;
+extern bool_ cheat_hear;
+extern bool_ cheat_room;
+extern bool_ cheat_xtra;
+extern bool_ cheat_know;
+extern bool_ cheat_live;
+extern bool_ last_words; /* Zangband options */
+extern bool_ speak_unique;
+extern bool_ small_levels;
+extern bool_ empty_levels;
+extern bool_ always_small_level;
+extern bool_ player_symbols;
+extern byte hitpoint_warn;
+extern byte delay_factor;
+extern s16b autosave_freq;
+extern bool_ autosave_t;
+extern bool_ autosave_l;
+extern s16b feeling;
+extern s16b rating;
+extern bool_ good_item_flag;
+extern bool_ closing_flag;
+extern s16b max_panel_rows, max_panel_cols;
+extern s16b panel_row_min, panel_row_max;
+extern s16b panel_col_min, panel_col_max;
+extern s16b panel_col_prt, panel_row_prt;
+extern byte feat_wall_outer;
+extern byte feat_wall_inner;
+extern s16b floor_type[100];
+extern s16b fill_type[100];
+extern s16b py;
+extern s16b px;
+extern s16b target_who;
+extern s16b target_col;
+extern s16b target_row;
+extern s16b health_who;
+extern s16b monster_race_idx;
+extern s16b monster_ego_idx;
+extern object_type *tracked_object;
+extern int player_uid;
+extern char player_name[32];
+extern char player_base[32];
+extern char died_from[80];
+extern char history[4][60];
+extern char savefile[1024];
+extern s16b lite_n;
+extern s16b lite_y[LITE_MAX];
+extern s16b lite_x[LITE_MAX];
+extern s16b view_n;
+extern byte view_y[VIEW_MAX];
+extern byte view_x[VIEW_MAX];
+extern s16b temp_n;
+extern byte temp_y[TEMP_MAX];
+extern byte temp_x[TEMP_MAX];
+extern s16b macro__num;
+extern cptr *macro__pat;
+extern cptr *macro__act;
+extern bool_ *macro__cmd;
+extern char *macro__buf;
+extern s16b quark__num;
+extern cptr *quark__str;
+extern u16b message__next;
+extern u16b message__last;
+extern u16b message__head;
+extern u16b message__tail;
+extern u16b *message__ptr;
+extern byte *message__color;
+extern byte *message__type;
+extern u16b *message__count;
+extern char *message__buf;
+extern u32b option_flag[8];
+extern u32b option_mask[8];
+extern u32b window_flag[ANGBAND_TERM_MAX];
+extern u32b window_mask[ANGBAND_TERM_MAX];
+extern term *angband_term[ANGBAND_TERM_MAX];
+extern char angband_term_name[ANGBAND_TERM_MAX][80];
+extern byte angband_color_table[256][4];
+extern char angband_sound_name[SOUND_MAX][16];
+extern cave_type *cave[MAX_HGT];
+extern object_type *o_list;
+extern monster_type *m_list;
+extern monster_type *km_list;
+extern u16b max_real_towns;
+extern u16b max_towns;
+extern town_type *town_info;
+extern s16b alloc_kind_size;
+extern alloc_entry *alloc_kind_table;
+extern bool_ alloc_kind_table_valid;
+extern s16b alloc_race_size;
+extern alloc_entry *alloc_race_table;
+extern byte misc_to_attr[256];
+extern char misc_to_char[256];
+extern byte tval_to_attr[128];
+extern char tval_to_char[128];
+extern cptr keymap_act[KEYMAP_MODES][256];
+extern player_type p_body;
+extern player_type *p_ptr;
+extern player_sex *sp_ptr;
+extern player_race *rp_ptr;
+extern player_race_mod *rmp_ptr;
+extern player_class *cp_ptr;
+extern player_spec *spp_ptr;
+extern u32b alchemist_known_egos[32];
+extern alchemist_recipe *alchemist_recipes;
+extern u32b alchemist_known_artifacts[6];
+extern u32b alchemist_gained;
+extern s16b player_hp[PY_MAX_LEVEL];
+extern header *al_head;
+extern char *al_name;
+extern artifact_select_flag *a_select_flags;
+
+extern header *ab_head;
+extern ability_type *ab_info;
+extern char *ab_name;
+extern char *ab_text;
+
+extern header *s_head;
+extern skill_type *s_info;
+extern char *s_name;
+extern char *s_text;
+
+extern header *v_head;
+extern vault_type *v_info;
+extern char *v_name;
+extern char *v_text;
+extern header *f_head;
+extern feature_type *f_info;
+extern char *f_name;
+extern char *f_text;
+extern header *k_head;
+extern object_kind *k_info;
+extern char *k_name;
+extern char *k_text;
+extern header *a_head;
+extern artifact_type *a_info;
+extern char *a_name;
+extern char *a_text;
+extern header *e_head;
+extern ego_item_type *e_info;
+extern char *e_name;
+extern char *e_text;
+extern header *ra_head;
+extern randart_part_type *ra_info;
+extern randart_gen_type ra_gen[30];
+extern header *r_head;
+extern monster_race *r_info;
+extern char *r_name;
+extern char *r_text;
+extern header *re_head;
+extern monster_ego *re_info;
+extern char *re_name;
+extern header *d_head;
+extern dungeon_info_type *d_info;
+extern char *d_name;
+extern char *d_text;
+extern header *c_head;
+extern player_class *class_info;
+extern char *c_name;
+extern char *c_text;
+extern meta_class_type *meta_class_info;
+extern header *rp_head;
+extern player_race *race_info;
+extern char *rp_name;
+extern char *rp_text;
+extern header *rmp_head;
+extern player_race_mod *race_mod_info;
+extern char *rmp_name;
+extern char *rmp_text;
+extern header *t_head;
+extern trap_type *t_info;
+extern char *t_name;
+extern char *t_text;
+extern header *wf_head;
+extern wilderness_type_info *wf_info;
+extern char *wf_name;
+extern char *wf_text;
+extern int wildc2i[256];
+extern header *st_head;
+extern store_info_type *st_info;
+extern char *st_name;
+extern header *ba_head;
+extern store_action_type *ba_info;
+extern char *ba_name;
+extern header *ow_head;
+extern owner_type *ow_info;
+extern char *ow_name;
+extern header *set_head;
+extern set_type *set_info;
+extern char *set_name;
+extern char *set_text;
+extern cptr ANGBAND_SYS;
+extern cptr ANGBAND_KEYBOARD;
+extern cptr ANGBAND_GRAF;
+extern cptr ANGBAND_DIR;
+extern cptr ANGBAND_DIR_APEX;
+extern cptr ANGBAND_DIR_CORE;
+extern cptr ANGBAND_DIR_DNGN;
+extern cptr ANGBAND_DIR_DATA;
+extern cptr ANGBAND_DIR_EDIT;
+extern cptr ANGBAND_DIR_FILE;
+extern cptr ANGBAND_DIR_HELP;
+extern cptr ANGBAND_DIR_INFO;
+extern cptr ANGBAND_DIR_MODULES;
+extern cptr ANGBAND_DIR_NOTE;
+extern cptr ANGBAND_DIR_SAVE;
+extern cptr ANGBAND_DIR_SCPT;
+extern cptr ANGBAND_DIR_PATCH;
+extern cptr ANGBAND_DIR_PREF;
+extern cptr ANGBAND_DIR_USER;
+extern cptr ANGBAND_DIR_XTRA;
+extern cptr ANGBAND_DIR_CMOV;
+extern char pref_tmp_value[8];
+extern bool_ item_tester_full;
+extern byte item_tester_tval;
+extern bool_ (*item_tester_hook)(object_type *o_ptr);
+extern bool_ (*ang_sort_comp)(vptr u, vptr v, int a, int b);
+extern void (*ang_sort_swap)(vptr u, vptr v, int a, int b);
+extern bool_ (*get_mon_num_hook)(int r_idx);
+extern bool_ (*get_mon_num2_hook)(int r_idx);
+extern bool_ (*get_obj_num_hook)(int k_idx);
+extern bool_ monk_armour_aux;
+extern bool_ monk_notify_aux;
+extern u16b max_wild_x;
+extern u16b max_wild_y;
+extern wilderness_map **wild_map;
+extern u16b old_max_s_idx;
+extern u16b max_ab_idx;
+extern u16b max_s_idx;
+extern u16b max_al_idx;
+extern u16b max_r_idx;
+extern u16b max_re_idx;
+extern u16b max_k_idx;
+extern u16b max_v_idx;
+extern u16b max_f_idx;
+extern u16b max_a_idx;
+extern u16b max_e_idx;
+extern u16b max_ra_idx;
+extern u16b max_d_idx;
+extern u16b max_o_idx;
+extern u16b max_m_idx;
+extern u16b max_t_idx;
+extern u16b max_rp_idx;
+extern u16b max_c_idx;
+extern u16b max_mc_idx;
+extern u16b max_rmp_idx;
+extern u16b max_st_idx;
+extern u16b max_ba_idx;
+extern u16b max_ow_idx;
+extern u16b max_wf_idx;
+extern s16b max_set_idx;
+extern int init_flags;
+extern bool_ ambush_flag;
+extern bool_ fate_flag;
+extern s16b no_breeds;
+extern bool_ carried_monster_hit;
+extern random_artifact random_artifacts[MAX_RANDARTS];
+extern s32b RANDART_WEAPON;
+extern s32b RANDART_ARMOR;
+extern s32b RANDART_JEWEL;
+extern s16b bounties[MAX_BOUNTIES][2];
+extern random_spell random_spells[MAX_SPELLS];
+extern s16b spell_num;
+extern rune_spell rune_spells[MAX_RUNES];
+extern s16b rune_num;
+extern fate fates[MAX_FATES];
+extern byte dungeon_type;
+extern s16b *max_dlv;
+extern u32b total_bounties;
+extern s16b doppleganger;
+extern bool_ generate_encounter;
+extern bool_ autoroll;
+extern bool_ point_based;
+extern bool_ maximize, preserve, special_lvls, ironman_rooms;
+extern bool_ inventory_no_move;
+extern bool_ take_notes, auto_notes;
+extern bool_ *m_allow_special;
+extern bool_ *k_allow_special;
+extern bool_ *a_allow_special;
+extern bool_ rand_birth;
+extern bool_ fast_autoroller;
+extern bool_ joke_monsters;
+extern bool_ munchkin_multipliers;
+extern bool_ center_player;
+extern s16b plots[MAX_PLOTS];
+extern random_quest random_quests[MAX_RANDOM_QUEST];
+extern bool_ exp_need;
+extern bool_ autoload_old_colors;
+extern bool_ fate_option;
+extern bool_ *special_lvl[MAX_DUNGEON_DEPTH];
+extern bool_ generate_special_feeling;
+extern bool_ auto_more;
+extern u32b dungeon_flags1;
+extern u32b dungeon_flags2;
+extern birther previous_char;
+extern hist_type *bg;
+extern int max_bg_idx;
+extern power_type *powers_type;
+extern s16b power_max;
+extern s32b extra_savefile_parts;
+extern s16b max_q_idx;
+extern quest_type *quest;
+extern bool_ player_char_health;
+extern s16b max_spells;
+extern spell_type *school_spells;
+extern s16b max_schools;
+extern school_type *schools;
+extern int project_time;
+extern s32b project_time_effect;
+extern effect_type effects[MAX_EFFECTS];
+extern char gen_skill_basem[MAX_SKILLS];
+extern u32b gen_skill_base[MAX_SKILLS];
+extern char gen_skill_modm[MAX_SKILLS];
+extern s16b gen_skill_mod[MAX_SKILLS];
+extern bool_ linear_stats;
+extern int max_bact;
+extern s16b max_corruptions;
+extern bool_ option_ingame_help;
+extern bool_ automatizer_enabled;
+extern s16b last_teleportation_y;
+extern s16b last_teleportation_x;
+extern cptr game_module;
+extern s32b VERSION_MAJOR;
+extern s32b VERSION_MINOR;
+extern s32b VERSION_PATCH;
+extern s32b max_plev;
+extern s32b DUNGEON_DEATH;
+extern deity_type *deity_info;
+extern s32b max_gods;
+extern timer_type *gl_timers;
+extern const char *get_version_string();
+
+/* plots.c */
+extern FILE *hook_file;
+extern bool_ check_hook(int h_idx);
+extern void wipe_hooks(void);
+extern void dump_hooks(int h_idx);
+extern void init_hooks(void);
+extern hooks_chain* add_hook(int h_idx, hook_type hook, cptr name);
+extern void add_hook_script(int h_idx, char *script, cptr name);
+extern void del_hook(int h_idx, hook_type hook);
+extern void del_hook_name(int h_idx, cptr name);
+extern s32b get_next_arg(char *fmt);
+extern int process_hooks_restart;
+extern hook_return process_hooks_return[20];
+extern bool_ process_hooks_ret(int h_idx, char *ret, char *fmt, ...);
+extern bool_ process_hooks(int h_idx, char *fmt, ...);
+
+/* help.c */
+extern void ingame_help(bool_ enable);
+
+/* birth.c */
+extern void print_desc_aux(cptr txt, int y, int x);
+extern void save_savefile_names(void);
+extern bool_ no_begin_screen;
+extern bool_ begin_screen(void);
+extern errr init_randart(void);
+extern void get_height_weight(void);
+extern void player_birth(void);
+
+/* cave.c */
+extern int distance(int y1, int x1, int y2, int x2);
+extern bool_ los(int y1, int x1, int y2, int x2);
+extern bool_ cave_valid_bold(int y, int x);
+extern bool_ no_lite(void);
+extern void map_info(int y, int x, byte *ap, char *cp, byte *tap, char *tcp, byte *eap, char *ecp);
+extern void map_info_default(int y, int x, byte *ap, char *cp);
+extern void move_cursor_relative(int row, int col);
+extern void print_rel(char c, byte a, int y, int x);
+extern void note_spot(int y, int x);
+extern void lite_spot(int y, int x);
+extern void prt_map(void);
+extern void display_map(int *cy, int *cx);
+extern void do_cmd_view_map(void);
+extern errr vinfo_init(void);
+extern void forget_view(void);
+extern void update_view(void);
+extern void forget_mon_lite(void);
+extern void update_mon_lite(void);
+extern void forget_flow(void);
+extern void update_flow(void);
+extern void map_area(void);
+extern void wiz_lite(void);
+extern void wiz_lite_extra(void);
+extern void wiz_dark(void);
+extern void cave_set_feat(int y, int x, int feat);
+extern void place_floor(int y, int x);
+extern void place_floor_convert_glass(int y, int x);
+extern void place_filler(int y, int x);
+extern void mmove2(int *y, int *x, int y1, int x1, int y2, int x2);
+extern bool_ projectable(int y1, int x1, int y2, int x2);
+extern void scatter(int *yp, int *xp, int y, int x, int d);
+extern void health_track(int m_idx);
+extern void monster_race_track(int r_idx, int ego);
+extern void object_track(object_type *o_ptr);
+extern void disturb(int stop_search, int flush_output);
+extern int is_quest(int level);
+extern int random_quest_number(void);
+extern int new_effect(int type, int dam, int time, int cy, int cx, int rad, s32b flags);
+
+/* cmovie.c */
+extern void cmovie_init_second(void);
+extern s16b do_play_cmovie(cptr cmov_file);
+extern void do_record_cmovie(cptr cmovie);
+extern void do_stop_cmovie(void);
+extern void do_start_cmovie(void);
+extern void cmovie_clean_line(int y, char *abuf, char *cbuf);
+extern void cmovie_record_line(int y);
+extern void do_cmovie_insert(void);
+
+/* cmd1.c */
+extern void attack_special(monster_type *m_ptr, s32b special, int dam);
+extern bool_ test_hit_fire(int chance, int ac, int vis);
+extern bool_ test_hit_norm(int chance, int ac, int vis);
+extern s16b critical_shot(int weight, int plus, int dam, int skill);
+extern s16b critical_norm(int weight, int plus, int dam, int weapon_tval, bool_ *done_crit);
+extern s16b tot_dam_aux(object_type *o_ptr, int tdam, monster_type *m_ptr, s32b *special);
+extern void search(void);
+extern void carry(int pickup);
+extern void py_attack(int y, int x, int max_blow);
+extern bool_ player_can_enter(byte feature);
+extern void move_player(int dir, int do_pickup, bool_ disarm);
+extern void move_player_aux(int dir, int do_pickup, int run, bool_ disarm);
+extern void run_step(int dir);
+extern void step_effects(int y, int x, int do_pickup);
+extern void do_cmd_pet(void);
+extern bool_ do_cmd_integrate_body(void);
+extern bool_ do_cmd_leave_body(bool_ drop_body);
+extern bool_ execute_inscription(byte i, byte y, byte x);
+extern void do_cmd_engrave(void);
+extern void do_spin(void);
+
+/* cmd2.c */
+extern byte show_monster_inven(int m_idx, int *monst_list);
+extern int breakage_chance(object_type *o_ptr);
+extern void do_cmd_go_up(void);
+extern void do_cmd_go_down(void);
+extern void do_cmd_search(void);
+extern void do_cmd_toggle_search(void);
+extern void do_cmd_open(void);
+extern void do_cmd_close(void);
+extern void do_cmd_chat(void);
+extern void do_cmd_give(void);
+extern bool_ do_cmd_tunnel_aux(int y, int x, int dir);
+extern void do_cmd_tunnel(void);
+extern void do_cmd_disarm(void);
+extern void do_cmd_disarm(void);
+extern void do_cmd_bash(void);
+extern void do_cmd_alter(void);
+extern void do_cmd_spike(void);
+extern void do_cmd_walk(int pickup, bool_ disarm);
+extern void do_cmd_stay(int pickup);
+extern void do_cmd_run(void);
+extern void do_cmd_rest(void);
+extern int get_shooter_mult(object_type *o_ptr);
+extern void do_cmd_fire(void);
+extern void do_cmd_throw(void);
+extern void do_cmd_boomerang(void);
+extern void do_cmd_unwalk(void);
+extern void do_cmd_immovable_special(void);
+extern void fetch(int dir, int wgt, bool_ require_los);
+extern void do_cmd_sacrifice(void);
+extern void do_cmd_create_artifact(object_type *q_ptr);
+extern void do_cmd_steal(void);
+extern void do_cmd_racial_power(void);
+
+/* cmd3.c */
+extern void do_cmd_html_dump(void);
+extern void cli_add(cptr active, cptr trigger, cptr descr);
+extern void do_cmd_cli(void);
+extern void do_cmd_cli_help(void);
+extern void do_cmd_inven(void);
+extern void do_cmd_equip(void);
+extern void do_cmd_wield(void);
+extern void do_cmd_takeoff(void);
+extern void do_cmd_drop(void);
+extern void do_cmd_destroy(void);
+extern void do_cmd_observe(void);
+extern void do_cmd_uninscribe(void);
+extern void do_cmd_inscribe(void);
+extern void do_cmd_refill(void);
+extern void do_cmd_target(void);
+extern void do_cmd_look(void);
+extern void do_cmd_locate(void);
+extern void do_cmd_query_symbol(void);
+extern bool_ do_cmd_sense_grid_mana(void);
+extern bool_ research_mon(void);
+extern s32b portable_hole_weight(void);
+extern void set_portable_hole_weight(void);
+extern void do_cmd_portable_hole(void);
+
+/* cmd4.c */
+extern bool_ change_option(cptr name, bool_ value);
+extern void macro_recorder_start(void);
+extern void macro_recorder_add(char c);
+extern void macro_recorder_stop(void);
+extern void do_cmd_macro_recorder(void);
+extern void do_cmd_redraw(void);
+extern void do_cmd_change_name(void);
+extern void do_cmd_message_one(void);
+extern void do_cmd_messages(void);
+extern void do_cmd_options(void);
+extern void do_cmd_pref(void);
+extern void do_cmd_macros(void);
+extern void do_cmd_visuals(void);
+extern void do_cmd_colors(void);
+extern void do_cmd_note(void);
+extern void do_cmd_version(void);
+extern void do_cmd_feeling(void);
+extern void do_cmd_load_screen(void);
+extern void do_cmd_save_screen(void);
+extern void do_cmd_knowledge(void);
+extern void plural_aux(char * Name);
+extern void do_cmd_checkquest(void);
+extern void do_cmd_change_tactic(int i);
+extern void do_cmd_change_movement(int i);
+extern void do_cmd_time(void);
+extern void do_cmd_options_aux(int page, cptr info, bool_ read_only);
+
+
+/* cmd5.c */
+extern bool_ is_magestaff(void);
+extern void calc_magic_bonus(void);
+extern void do_cmd_browse_aux(object_type *o_ptr);
+extern void do_cmd_browse(void);
+extern void do_cmd_cast(void);
+extern void do_cmd_pray(void);
+extern void do_cmd_rerate(void);
+extern void corrupt_player(void);
+extern bool_ item_tester_hook_armour(object_type *o_ptr);
+extern void fetch(int dir, int wgt, bool_ require_los);
+extern void do_poly_self(void);
+extern void brand_weapon(int brand_type);
+extern cptr symbiote_name(bool_ capitalize);
+extern int use_symbiotic_power(int r_idx, bool_ great, bool_ only_number, bool_ no_cost);
+extern s32b get_school_spell(cptr do_what, cptr check_fct, s16b force_book);
+extern void do_cmd_copy_spell(void);
+extern void cast_school_spell(void);
+extern void browse_school_spell(int book, int pval, object_type *o_ptr);
+extern int find_spell(char *name);
+extern bool_ is_school_book(object_type *o_ptr);
+
+/* cmd6.c */
+extern void set_stick_mode(object_type *o_ptr);
+extern void unset_stick_mode(void);
+extern void do_cmd_eat_food(void);
+extern void do_cmd_quaff_potion(void);
+extern void do_cmd_read_scroll(void);
+extern void do_cmd_aim_wand(void);
+extern void do_cmd_use_staff(void);
+extern void do_cmd_zap_rod(void);
+extern const char *activation_aux(object_type *o_ptr, bool_ desc, int item);
+extern void do_cmd_activate(void);
+extern void do_cmd_rerate(void);
+extern void do_cmd_cut_corpse(void);
+extern void do_cmd_cure_meat(void);
+extern void do_cmd_drink_fountain(void);
+extern void do_cmd_fill_bottle(void);
+
+/* cmd7.c */
+extern void do_cmd_create_boulder(void);
+extern int rune_exec(rune_spell *spell, int cost);
+extern void necro_info(char *p, int power);
+extern void mindcraft_info(char *p, int power);
+extern void symbiotic_info(char *p, int power);
+extern void mimic_info(char *p, int power);
+extern random_spell* select_spell(bool_ quick);
+extern void cast_magic_spell(int spell, byte level);
+extern void do_cmd_summoner(void);
+extern void do_cmd_mindcraft(void);
+extern void do_cmd_mimic(void);
+extern void do_cmd_blade(void);
+extern void use_ability_blade(void);
+extern bool_ alchemist_exists(int tval, int sval, int ego, int artifact);
+extern void rod_tip_extract(object_type *o_ptr);
+extern void do_cmd_toggle_artifact(object_type *o_ptr);
+extern bool_ alchemist_items_check(int tval, int sval, int ego, int tocreate, bool_ message);
+extern void alchemist_display_recipe(int tval, int sval, int ego);
+extern void alchemist_recipe_book(void);
+extern int alchemist_recipe_select(int *tval, int sval, int ego, bool_ recipe);
+extern int alchemist_learn_object(object_type *o_ptr);
+extern void alchemist_gain_level(int lev);
+extern void do_cmd_alchemist(void);
+extern void do_cmd_beastmaster(void);
+extern void do_cmd_powermage(void);
+extern void do_cmd_possessor(void);
+extern void do_cmd_archer(void);
+extern void do_cmd_set_piercing(void);
+extern void do_cmd_necromancer(void);
+extern void do_cmd_unbeliever(void);
+extern void cast_daemon_spell(int spell, byte level);
+extern void do_cmd_unbeliever(void);
+extern void do_cmd_runecrafter(void);
+extern void do_cmd_symbiotic(void);
+extern s32b sroot(s32b n);
+extern int clamp_failure_chance(int chance, int minfail);
+
+/* dungeon.c */
+extern byte value_check_aux1(object_type *o_ptr);
+extern byte value_check_aux1_magic(object_type *o_ptr);
+extern byte value_check_aux2(object_type *o_ptr);
+extern byte value_check_aux2_magic(object_type *o_ptr);
+extern void play_game(bool_ new_game);
+extern void sense_inventory();
+
+/* files.c */
+extern void html_screenshot(cptr name);
+extern void help_file_screenshot(cptr name);
+extern void player_flags(u32b* f1, u32b* f2, u32b* f3, u32b* f4, u32b* f5, u32b* esp);
+extern void wipe_saved(void);
+extern s16b tokenize(char *buf, s16b num, char **tokens, char delim1, char delim2);
+extern void display_player(int mode);
+extern cptr describe_player_location(void);
+extern errr file_character(cptr name, bool_ full);
+extern errr process_pref_file_aux(char *buf);
+extern errr process_pref_file(cptr name);
+extern void read_times(void);
+extern bool_ txt_to_html(cptr head, cptr food, cptr base, cptr ext, bool_ force, bool_ recur);
+extern bool_ show_file(cptr name, cptr what, int line, int mode);
+extern void do_cmd_help(void);
+extern void process_player_base(void);
+extern void process_player_name(bool_ sf);
+extern void get_name(void);
+extern void do_cmd_suicide(void);
+extern void do_cmd_save_game(void);
+extern void autosave_checkpoint();
+extern long total_points(void);
+extern void display_scores(int from, int to);
+extern errr predict_score(void);
+extern void close_game(void);
+extern void signals_ignore_tstp(void);
+extern void signals_handle_tstp(void);
+extern void signals_init(void);
+extern errr get_rnd_line(char * file_name, char * output);
+extern char *get_line(char* fname, cptr fdir, char *linbuf, int line);
+extern void do_cmd_knowledge_corruptions(void);
+extern void race_legends(void);
+extern void show_highclass(int building);
+extern errr get_xtra_line(char * file_name, monster_type *m_ptr, char * output);
+
+/* gen_maze.c */
+extern bool_ level_generate_maze();
+
+/* gen_life.c */
+extern bool_ level_generate_life();
+extern void evolve_level(bool_ noise);
+
+/* generate.c */
+extern bool_ new_player_spot(int branch);
+extern void add_level_generator(cptr name, bool_ (*generator)(), bool_ stairs, bool_ monsters, bool_ objects, bool_ miscs);
+extern bool_ level_generate_dungeon();
+extern bool_ generate_fracave(int y0, int x0,int xsize,int ysize,int cutoff,bool_ light,bool_ room);
+extern void generate_hmap(int y0, int x0,int xsiz,int ysiz,int grd,int roug,int cutoff);
+extern bool_ room_alloc(int x,int y,bool_ crowded,int by0,int bx0,int *xx,int *yy);
+extern void generate_grid_mana(void);
+extern byte calc_dungeon_type(void);
+extern void generate_cave(void);
+extern void build_rectangle(int y1, int x1, int y2, int x2, int feat, int info);
+
+
+/* wild.c */
+extern int generate_area(int y, int x, bool_ border, bool_ corner, bool_ refresh);
+extern void wilderness_gen(int refresh);
+extern void wilderness_gen_small(void);
+extern void reveal_wilderness_around_player(int y, int x, int h, int w);
+extern void town_gen(int t_idx);
+
+
+/* init1.c */
+extern int color_char_to_attr(char c);
+extern byte conv_color[16];
+extern errr init_player_info_txt(FILE *fp, char *buf);
+extern errr init_ab_info_txt(FILE *fp, char *buf);
+extern errr init_s_info_txt(FILE *fp, char *buf);
+extern errr init_set_info_txt(FILE *fp, char *buf);
+extern errr init_v_info_txt(FILE *fp, char *buf, bool_ start);
+extern errr init_f_info_txt(FILE *fp, char *buf);
+extern errr init_k_info_txt(FILE *fp, char *buf);
+extern errr init_a_info_txt(FILE *fp, char *buf);
+extern errr init_al_info_txt(FILE *fp, char *buf);
+extern errr init_ra_info_txt(FILE *fp, char *buf);
+extern errr init_e_info_txt(FILE *fp, char *buf);
+extern errr init_r_info_txt(FILE *fp, char *buf);
+extern errr init_re_info_txt(FILE *fp, char *buf);
+extern errr grab_one_dungeon_flag(u32b *flags1, u32b *flags2, cptr what);
+extern errr init_d_info_txt(FILE *fp, char *buf);
+extern errr init_t_info_txt(FILE *fp, char *buf);
+extern errr init_ba_info_txt(FILE *fp, char *buf);
+extern errr init_st_info_txt(FILE *fp, char *buf);
+extern errr init_ow_info_txt(FILE *fp, char *buf);
+extern errr init_wf_info_txt(FILE *fp, char *buf);
+extern errr process_dungeon_file(cptr name, int *yval, int *xval, int ymax, int xmax, bool_ init, bool_ full);
+
+/* init2.c */
+extern void init_corruptions(s16b new_size);
+extern void init_spells(s16b new_size);
+extern void init_schools(s16b new_size);
+extern void reinit_gods(s16b new_size);
+extern void reinit_quests(s16b new_size);
+extern void reinit_powers_type(s16b new_size);
+extern void create_stores_stock(int t);
+extern errr init_v_info(void);
+extern void init_file_paths(char *path);
+extern void init_angband(void);
+extern errr init_buildings(void);
+extern s16b error_idx;
+extern s16b error_line;
+extern u32b fake_name_size;
+extern u32b fake_text_size;
+
+/* loadsave.c */
+extern void register_savefile(int num);
+extern bool_ file_exist(char *buf);
+extern s16b rd_variable(void);
+extern void wr_variable(s16b *var);
+extern void wr_scripts(void);
+extern bool_ load_dungeon(char *ext);
+extern void save_dungeon(void);
+extern bool_ save_player(void);
+extern bool_ load_player(void);
+extern errr rd_savefile_new(void);
+extern void load_number_key(char *key, u32b *val);
+extern void save_number_key(char *key, u32b val);
+
+/* melee1.c */
+/* melee2.c */
+extern int monst_spell_monst_spell;
+extern bool_ mon_take_hit_mon(int s_idx, int m_idx, int dam, bool_ *fear, cptr note);
+extern int check_hit2(int power, int level, int ac);
+extern int get_attack_power(int effect);
+extern bool_ carried_make_attack_normal(int r_idx);
+extern bool_ make_attack_normal(int m_idx, byte divis);
+extern bool_ make_attack_spell(int m_idx);
+extern void process_monsters(void);
+extern void curse_equipment(int chance, int heavy_chance);
+extern void curse_equipment_dg(int chance, int heavy_chance);
+
+/* monster1.c */
+extern void screen_roff(int r_idx, int ego, int remember);
+extern void display_roff(int r_idx, int ego);
+extern void monster_description_out(int r_idx, int ego);
+
+/* monster2.c */
+extern void monster_set_level(int m_idx, int level);
+extern s32b modify_aux(s32b a, s32b b, char mod);
+extern void monster_msg(cptr fmt, ...);
+extern void cmonster_msg(char a, cptr fmt, ...);
+extern bool_ mego_ok(int r_idx, int ego);
+extern void monster_check_experience(int m_idx, bool_ silent);
+extern void monster_gain_exp(int m_idx, u32b exp, bool_ silent);
+extern monster_race* race_info_idx(int r_idx, int ego);
+extern int get_wilderness_flag(void);
+extern void sanity_blast(monster_type * m_ptr, bool_ necro);
+extern void delete_monster_idx(int i);
+extern void delete_monster(int y, int x);
+extern void compact_monsters(int size);
+extern void wipe_m_list(void);
+extern s16b m_pop(void);
+extern errr get_mon_num_prep(void);
+extern s16b get_mon_num(int level);
+extern void monster_desc(char *desc, monster_type *m_ptr, int mode);
+extern void monster_race_desc(char *desc, int r_idx, int ego);
+extern void lore_do_probe(int m_idx);
+extern void lore_treasure(int m_idx, int num_item, int num_gold);
+extern void update_mon(int m_idx, bool_ full);
+extern void update_monsters(bool_ full);
+extern void monster_carry(monster_type *m_ptr, int m_idx, object_type *q_ptr);
+extern bool_ bypass_r_ptr_max_num ;
+extern bool_ place_monster_aux(int y, int x, int r_idx, bool_ slp, bool_ grp, int status);
+extern bool_ place_monster(int y, int x, bool_ slp, bool_ grp);
+extern bool_ alloc_horde(int y, int x);
+extern bool_ alloc_monster(int dis, bool_ slp);
+extern bool_ summon_specific_okay(int r_idx);
+extern int summon_specific_level;
+extern bool_ summon_specific(int y1, int x1, int lev, int type);
+extern void monster_swap(int y1, int x1, int y2, int x2);
+extern bool_ multiply_monster(int m_idx, bool_ charm, bool_ clone);
+extern bool_ hack_message_pain_may_silent;
+extern void message_pain(int m_idx, int dam);
+extern void update_smart_learn(int m_idx, int what);
+extern bool_ summon_specific_friendly(int y1, int x1, int lev, int type, bool_ Group_ok);
+extern bool_ place_monster_one_no_drop;
+extern monster_race *place_monster_one_race;
+extern s16b place_monster_one(int y, int x, int r_idx, int ego, bool_ slp, int status);
+extern s16b player_place(int y, int x);
+extern void monster_drop_carried_objects(monster_type *m_ptr);
+extern bool_ monster_dungeon(int r_idx);
+extern bool_ monster_quest(int r_idx);
+extern bool_ monster_ocean(int r_idx);
+extern bool_ monster_shore(int r_idx);
+extern bool_ monster_town(int r_idx);
+extern bool_ monster_wood(int r_idx);
+extern bool_ monster_volcano(int r_idx);
+extern bool_ monster_mountain(int r_idx);
+extern bool_ monster_grass(int r_idx);
+extern bool_ monster_deep_water(int r_idx);
+extern bool_ monster_shallow_water(int r_idx);
+extern bool_ monster_lava(int r_idx);
+extern void set_mon_num_hook(void);
+extern void set_mon_num2_hook(int y, int x);
+extern bool_ monster_can_cross_terrain(byte feat, monster_race *r_ptr);
+extern void corrupt_corrupted(void);
+
+/* monster3.c */
+extern void dump_companions(FILE *outfile);
+extern void do_cmd_companion(void);
+extern bool_ do_control_reconnect(void);
+extern bool_ do_control_drop(void);
+extern bool_ do_control_magic(void);
+extern bool_ do_control_pickup(void);
+extern bool_ do_control_inven(void);
+extern bool_ do_control_walk(void);
+extern bool_ can_create_companion(void);
+extern void ai_deincarnate(int m_idx);
+extern bool_ ai_possessor(int m_idx, int o_idx);
+extern bool_ ai_multiply(int m_idx);
+extern bool_ change_side(monster_type *m_ptr);
+extern int is_friend(monster_type *m_ptr);
+extern bool_ is_enemy(monster_type *m_ptr, monster_type *t_ptr);
+
+/* object1.c */
+/* object2.c */
+extern byte get_item_letter_color(object_type *o_ptr);
+extern void describe_device(object_type *o_ptr);
+extern void inc_stack_size(int item, int delta);
+extern void inc_stack_size_ex(int item, int delta, optimize_flag opt, describe_flag desc);
+extern void object_pickup(int this_o_idx);
+extern int get_slot(int slot);
+extern bool_ apply_flags_set(s16b a_idx, s16b set_idx, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp);
+extern bool_ apply_set(s16b a_idx, s16b set_idx);
+extern bool_ takeoff_set(s16b a_idx, s16b set_idx);
+extern bool_ wield_set(s16b a_idx, s16b set_idx, bool_ silent);
+extern object_type *get_object(int item);
+extern s32b calc_total_weight(void);
+extern void add_random_ego_flag(object_type *o_ptr, int fego, bool_ *limit_blows);
+extern bool_ info_spell;
+extern char spell_txt[50];
+extern bool_ grab_tval_desc(int tval);
+extern void init_match_theme(obj_theme theme);
+extern bool_ kind_is_artifactable(int k_idx);
+extern bool_ kind_is_good(int k_idx);
+extern int kind_is_legal_special;
+extern bool_ kind_is_legal(int k_idx);
+extern bool_ verify(cptr prompt, int item);
+extern void flavor_init(void);
+extern void reset_visuals(void);
+extern int object_power(object_type *o_ptr);
+extern bool_ object_flags_no_set;
+extern void object_flags(object_type *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp);
+extern void object_flags_known(object_type *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp);
+extern void object_desc(char *buf, object_type *o_ptr, int pref, int mode);
+extern void object_desc_store(char *buf, object_type *o_ptr, int pref, int mode);
+extern bool_ object_out_desc(object_type *o_ptr, FILE *fff, bool_ trim_down, bool_ wait_for_it);
+extern char index_to_label(int i);
+extern s16b label_to_inven(int c);
+extern s16b label_to_equip(int c);
+extern s16b wield_slot_ideal(object_type *o_ptr, bool_ ideal);
+extern s16b wield_slot(object_type *o_ptr);
+extern cptr mention_use(int i);
+extern cptr describe_use(int i);
+extern void inven_item_charges(int item);
+extern void inven_item_describe(int item);
+extern void inven_item_increase(int item, int num);
+extern bool_ inven_item_optimize(int item);
+extern void floor_item_charges(int item);
+extern void floor_item_describe(int item);
+extern void floor_item_increase(int item, int num);
+extern void floor_item_optimize(int item);
+extern bool_ inven_carry_okay(object_type *o_ptr);
+extern s16b inven_carry(object_type *o_ptr, bool_ final);
+extern s16b inven_takeoff(int item, int amt, bool_ force_drop);
+extern void inven_drop(int item, int amt, int dy, int dx, bool_ silent);
+extern bool_ item_tester_okay(object_type *o_ptr);
+extern void display_inven(void);
+extern void display_equip(void);
+extern void show_inven();
+extern void show_equip();
+extern void toggle_inven_equip(void);
+extern bool_ (*get_item_extra_hook)(int *cp);
+extern bool_ get_item(int *cp, cptr pmt, cptr str, int mode);
+extern void excise_object_idx(int o_idx);
+extern void delete_object_idx(int o_idx);
+extern void delete_object(int y, int x);
+extern void compact_objects(int size);
+extern void wipe_o_list(void);
+extern s16b o_pop(void);
+extern errr get_obj_num_prep(void);
+extern s16b get_obj_num(int level);
+extern void object_known(object_type *o_ptr);
+extern void object_aware(object_type *o_ptr);
+extern void object_tried(object_type *o_ptr);
+extern s32b object_value(object_type *o_ptr);
+extern s32b object_value_real(object_type *o_ptr);
+extern bool_ object_similar(object_type *o_ptr, object_type *j_ptr);
+extern void object_absorb(object_type *o_ptr, object_type *j_ptr);
+extern s16b lookup_kind(int tval, int sval);
+extern void object_wipe(object_type *o_ptr);
+extern void object_prep(object_type *o_ptr, int k_idx);
+extern void object_copy(object_type *o_ptr, object_type *j_ptr);
+extern int hack_apply_magic_power;
+extern void apply_magic(object_type *o_ptr, int lev, bool_ okay, bool_ good, bool_ great);
+extern bool_ make_object(object_type *j_ptr, bool_ good, bool_ great, obj_theme theme);
+extern void place_object(int y, int x, bool_ good, bool_ great, int where);
+extern bool_ make_gold(object_type *j_ptr);
+extern void place_gold(int y, int x);
+extern void process_objects(void);
+extern s16b drop_near(object_type *o_ptr, int chance, int y, int x);
+extern void acquirement(int y1, int x1, int num, bool_ great, bool_ known);
+extern void pick_trap(int y, int x);
+extern cptr item_activation(object_type *o_ptr,byte num);
+extern void combine_pack(void);
+extern void reorder_pack(void);
+extern void display_koff(int k_idx);
+extern void random_artifact_resistance (object_type * o_ptr);
+extern void random_resistance (object_type * o_ptr, bool_ is_scroll, int specific);
+extern s16b floor_carry(int y, int x, object_type *j_ptr);
+extern void pack_decay(int item);
+extern void floor_decay(int item);
+extern bool_ scan_floor(int *items, int *item_num, int y, int x, int mode);
+extern void show_floor(int y, int x);
+extern bool_ get_item_floor(int *cp, cptr pmt, cptr str, int mode);
+extern void py_pickup_floor(int pickup);
+extern s16b m_bonus(int max, int level);
+extern void object_gain_level(object_type *o_ptr);
+extern void gain_flag_group_flag(object_type *o_ptr, bool_ silent);
+extern void gain_flag_group(object_type *o_ptr, bool_ silent);
+extern void get_table_name(char * out_string);
+extern s32b flag_cost(object_type * o_ptr, int plusses);
+
+/* powers.c */
+extern void do_cmd_power(void);
+
+/* traps.c */
+extern bool_ player_activate_trap_type(s16b y, s16b x, object_type *i_ptr, s16b item);
+extern void player_activate_door_trap(s16b y, s16b x);
+extern void place_trap(int y, int x);
+extern void place_trap_object(object_type *o_ptr);
+extern void wiz_place_trap(int y, int x, int idx);
+extern void do_cmd_set_trap(void);
+extern bool_ mon_hit_trap(int);
+
+/* spells1.c */
+extern byte spell_color(int type);
+extern s16b poly_r_idx(int r_idx);
+extern void get_pos_player(int dis, int *ny, int *nx);
+extern bool_ teleport_player_bypass;
+extern void teleport_to_player(int m_idx);
+extern void teleport_player_directed(int rad, int dir);
+extern void teleport_away(int m_idx, int dis);
+extern void teleport_player(int dis);
+extern void teleport_player_to(int ny, int nx);
+extern void teleport_monster_to(int m_idx, int ny, int nx);
+extern void teleport_player_level(void);
+extern void recall_player(int d, int f);
+extern void take_hit(int damage, cptr kb_str);
+extern void take_sanity_hit(int damage, cptr hit_from);
+extern void acid_dam(int dam, cptr kb_str);
+extern void elec_dam(int dam, cptr kb_str);
+extern void fire_dam(int dam, cptr kb_str);
+extern void cold_dam(int dam, cptr kb_str);
+extern bool_ inc_stat(int stat);
+extern bool_ dec_stat(int stat, int amount, int mode);
+extern bool_ res_stat(int stat, bool_ full);
+extern bool_ apply_disenchant(int mode);
+extern bool_ project_m(int who, int r, int y, int x, int dam, int typ);
+extern sint project_path(u16b *gp, int range, int y1, int x1, int y2, int x2, int flg);
+extern bool_ project(int who, int rad, int y, int x, int dam, int typ, int flg);
+extern bool_ potion_smash_effect(int who, int y, int x, int o_sval);
+extern void do_poly_self(void);
+extern void corrupt_player(void);
+extern void generate_spell(int plev);
+extern bool_ unsafe;
+extern void describe_attack_fully(int type, char* r);
+extern s16b do_poly_monster(int y, int x);
+
+
+/* spells2.c */
+extern bool_ remove_curse_object(object_type *o_ptr, bool_ all);
+extern void curse_artifact(object_type * o_ptr);
+extern void grow_things(s16b type, int rad);
+extern void grow_grass(int rad);
+extern void grow_trees(int rad);
+extern bool_ hp_player(int num);
+extern bool_ heal_insanity(int val);
+extern void warding_glyph(void);
+extern void explosive_rune(void);
+extern bool_ do_dec_stat(int stat, int mode);
+extern bool_ do_res_stat(int stat, bool_ full);
+extern bool_ do_inc_stat(int stat);
+extern void identify_pack(void);
+extern void identify_pack_fully(void);
+extern bool_ remove_curse(void);
+extern bool_ remove_all_curse(void);
+extern bool_ restore_level(void);
+extern void self_knowledge(FILE *fff);
+extern bool_ lose_all_info(void);
+extern bool_ detect_traps(int rad);
+extern bool_ detect_doors(int rad);
+extern bool_ detect_stairs(int rad);
+extern bool_ detect_treasure(int rad);
+extern bool_ hack_no_detect_message;
+extern bool_ detect_objects_gold(int rad);
+extern bool_ detect_objects_normal(int rad);
+extern bool_ detect_objects_magic(int rad);
+extern bool_ detect_monsters_normal(int rad);
+extern bool_ detect_monsters_invis(int rad);
+extern bool_ detect_monsters_evil(int rad);
+extern bool_ detect_monsters_good(int rad);
+extern bool_ detect_monsters_xxx(u32b match_flag, int rad);
+extern bool_ detect_monsters_string(cptr chars, int rad);
+extern bool_ detect_monsters_nonliving(int rad);
+extern bool_ detect_all(int rad);
+extern void stair_creation(void);
+extern bool_ wall_stone(int y, int x);
+extern bool_ enchant(object_type *o_ptr, int n, int eflag);
+extern bool_ enchant_spell(int num_hit, int num_dam, int num_ac, int num_pval);
+extern bool_ ident_spell(void);
+extern bool_ ident_all(void);
+extern bool_ identify_fully(void);
+extern bool_ recharge(int num);
+extern bool_ speed_monsters(void);
+extern bool_ slow_monsters(void);
+extern bool_ sleep_monsters(void);
+extern bool_ conf_monsters(void);
+extern void aggravate_monsters(int who);
+extern bool_ genocide_aux(bool_ player_cast, char typ);
+extern bool_ genocide(bool_ player_cast);
+extern bool_ mass_genocide(bool_ player_cast);
+extern void do_probe(int m_idx);
+extern bool_ probing(void);
+extern void change_wild_mode(void);
+extern bool_ banish_evil(int dist);
+extern bool_ dispel_evil(int dam);
+extern bool_ dispel_good(int dam);
+extern bool_ dispel_undead(int dam);
+extern bool_ dispel_monsters(int dam);
+extern bool_ dispel_living(int dam);
+extern bool_ dispel_demons(int dam);
+extern bool_ turn_undead(void);
+extern void wipe(int y1, int x1, int r);
+extern void destroy_area(int y1, int x1, int r, bool_ full, bool_ bypass);
+extern void earthquake(int cy, int cx, int r);
+extern void lite_room(int y1, int x1);
+extern void unlite_room(int y1, int x1);
+extern bool_ lite_area(int dam, int rad);
+extern bool_ unlite_area(int dam, int rad);
+extern bool_ fire_ball_beam(int typ, int dir, int dam, int rad);
+extern bool_ fire_cloud(int typ, int dir, int dam, int rad, int time);
+extern bool_ fire_wave(int typ, int dir, int dam, int rad, int time, s32b eff);
+extern bool_ fire_wall(int typ, int dir, int dam, int time);
+extern bool_ fire_ball(int typ, int dir, int dam, int rad);
+extern bool_ fire_bolt(int typ, int dir, int dam);
+extern bool_ fire_beam(int typ, int dir, int dam);
+extern bool_ fire_druid_ball(int typ, int dir, int dam, int rad);
+extern bool_ fire_druid_bolt(int typ, int dir, int dam);
+extern bool_ fire_druid_beam(int typ, int dir, int dam);
+extern void call_chaos(void);
+extern bool_ fire_bolt_or_beam(int prob, int typ, int dir, int dam);
+extern bool_ lite_line(int dir);
+extern bool_ drain_life(int dir, int dam);
+extern bool_ death_ray(int dir, int plev);
+extern bool_ wall_to_mud(int dir);
+extern bool_ destroy_door(int dir);
+extern bool_ disarm_trap(int dir);
+extern bool_ wizard_lock(int dir);
+extern bool_ heal_monster(int dir);
+extern bool_ speed_monster(int dir);
+extern bool_ slow_monster(int dir);
+extern bool_ sleep_monster(int dir);
+extern bool_ stasis_monster(int dir);
+extern bool_ confuse_monster(int dir, int plev);
+extern bool_ stun_monster(int dir, int plev);
+extern bool_ fear_monster(int dir, int plev);
+extern bool_ scare_monsters(void);
+extern bool_ poly_monster(int dir);
+extern bool_ clone_monster(int dir);
+extern bool_ teleport_monster(int dir);
+extern bool_ door_creation(void);
+extern bool_ trap_creation(void);
+extern bool_ glyph_creation(void);
+extern bool_ destroy_doors_touch(void);
+extern bool_ destroy_traps_touch(void);
+extern bool_ sleep_monsters_touch(void);
+extern bool_ alchemy(void);
+extern void activate_ty_curse(void);
+extern void activate_dg_curse(void);
+extern void activate_hi_summon(void);
+extern void summon_cyber(void);
+extern void wall_breaker(void);
+extern void bless_weapon(void);
+extern bool_ confuse_monsters(int dam);
+extern bool_ charm_monsters(int dam);
+extern bool_ charm_animals(int dam);
+extern bool_ charm_demons(int dam);
+extern bool_ stun_monsters(int dam);
+extern bool_ stasis_monsters(int dam);
+extern bool_ banish_monsters(int dist);
+extern bool_ turn_monsters(int dam);
+extern bool_ turn_evil(int dam);
+extern bool_ deathray_monsters(void);
+extern bool_ charm_monster(int dir, int plev);
+extern bool_ star_charm_monster(int dir, int plev);
+extern bool_ control_one_undead(int dir, int plev);
+extern bool_ charm_animal(int dir, int plev);
+extern bool_ mindblast_monsters(int dam);
+extern void alter_reality(void);
+extern void report_magics(void);
+extern void teleport_swap(int dir);
+extern void swap_position(int lty, int ltx);
+extern bool_ item_tester_hook_recharge(object_type *o_ptr);
+extern bool_ fire_explosion(int y, int x, int typ, int rad, int dam);
+extern bool_ fire_godly_wrath(int y, int x, int typ, int dir, int dam);
+extern bool_ invoke(int dam, int typ);
+extern bool_ project_hack(int typ, int dam);
+extern void project_meteor(int radius, int typ, int dam, u32b flg);
+extern bool_ item_tester_hook_artifactable(object_type *o_ptr);
+extern bool_ passwall(int dir, bool_ safe);
+extern bool_ project_hook(int typ, int dir, int dam, int flg);
+extern void random_misc (object_type * o_ptr, bool_ is_scroll);
+extern void random_plus(object_type * o_ptr, bool_ is_scroll);
+extern bool_ reset_recall(bool_ no_trepas_max_depth);
+extern void remove_dg_curse(void);
+
+/* randart.c */
+extern int get_activation_power(void);
+extern void build_prob(cptr learn);
+extern bool_ create_artifact(object_type *o_ptr, bool_ a_scroll, bool_ get_name);
+extern bool_ artifact_scroll(void);
+
+/* store.c */
+extern bool_ is_blessed(object_type *o_ptr);
+extern void do_cmd_store(void);
+extern void store_shuffle(int which);
+extern void store_maint(int town_num, int store_num);
+extern void store_init(int town_num, int store_num);
+extern void move_to_black_market(object_type * o_ptr);
+extern void do_cmd_home_trump(void);
+extern void store_sell(void);
+extern void store_purchase(void);
+extern void store_examine(void);
+extern void store_stole(void);
+extern void store_prt_gold(void);
+extern void store_request_item(void);
+
+/* bldg.c -KMW- */
+extern bool_ bldg_process_command(store_type *s_ptr, int i);
+extern void show_building(store_type *s_ptr);
+extern bool_ is_state(store_type *s_ptr, int state);
+extern void do_cmd_bldg(void);
+extern bool_ show_god_info(bool_ ext);
+extern void enter_quest(void);
+extern void select_bounties(void);
+
+/* util.c */
+extern void scansubdir(cptr dir);
+extern s32b rescale(s32b x, s32b max, s32b new_max);
+extern bool_ input_box(cptr text, int y, int x, char *buf, int max);
+extern void draw_box(int y, int x, int h, int w);
+extern void display_list(int y, int x, int h, int w, cptr title, cptr *list, int max, int begin, int sel, byte sel_color);
+extern s32b value_scale(int value, int vmax, int max, int min);
+extern int ask_menu(cptr ask, char **items, int max);
+extern cptr get_player_race_name(int pr, int ps);
+extern cptr get_month_name(int month, bool_ full, bool_ compact);
+extern cptr get_day(int day);
+extern s32b bst(s32b what, s32b t);
+extern errr path_parse(char *buf, int max, cptr file);
+extern errr path_temp(char *buf, int max);
+extern errr path_build(char *buf, int max, cptr path, cptr file);
+extern FILE *my_fopen(cptr file, cptr mode);
+extern errr my_fgets(FILE *fff, char *buf, huge n);
+extern errr my_fputs(FILE *fff, cptr buf, huge n);
+extern errr my_fclose(FILE *fff);
+extern errr fd_kill(cptr file);
+extern errr fd_move(cptr file, cptr what);
+extern errr fd_copy(cptr file, cptr what);
+extern int fd_make(cptr file, int mode);
+extern int fd_open(cptr file, int flags);
+extern errr fd_lock(int fd, int what);
+extern errr fd_seek(int fd, huge n);
+extern errr fd_read(int fd, char *buf, huge n);
+extern errr fd_write(int fd, cptr buf, huge n);
+extern errr fd_close(int fd);
+extern void flush(void);
+extern void bell(void);
+extern void sound(int num);
+extern void move_cursor(int row, int col);
+extern void text_to_ascii(char *buf, cptr str);
+extern void ascii_to_text(char *buf, cptr str);
+extern void keymap_init(void);
+extern errr macro_add(cptr pat, cptr act);
+extern sint macro_find_exact(cptr pat);
+extern char inkey(void);
+extern cptr quark_str(s16b num);
+extern s16b quark_add(cptr str);
+extern s16b message_num(void);
+extern cptr message_str(int age);
+extern byte message_color(int age);
+extern byte message_type(int age);
+extern void message_add(byte type, cptr msg, byte color);
+extern void display_message(int x, int y, int split, byte color, cptr t);
+extern void cmsg_print(byte color, cptr msg);
+extern void msg_print(cptr msg);
+extern void cmsg_format(byte color, cptr fmt, ...);
+extern void msg_format(cptr fmt, ...);
+extern void screen_save(void);
+extern void screen_load(void);
+extern void c_put_str(byte attr, cptr str, int row, int col);
+extern void put_str(cptr str, int row, int col);
+extern void c_prt(byte attr, cptr str, int row, int col);
+extern void prt(cptr str, int row, int col);
+extern void text_out_to_screen(byte a, cptr str);
+extern void text_out_to_file(byte a, cptr str);
+extern void text_out(cptr str);
+extern void text_out_c(byte a, cptr str);
+extern void clear_screen(void);
+extern void clear_from(int row);
+extern bool_ askfor_aux_complete;
+extern bool_ askfor_aux(char *buf, int len);
+extern bool_ get_string(cptr prompt, char *buf, int len);
+extern bool_ get_check(cptr prompt);
+extern bool_ get_com(cptr prompt, char *command);
+extern s32b get_quantity(cptr prompt, s32b max);
+extern void pause_line(int row);
+extern char request_command_ignore_keymaps[];
+extern bool_ request_command_inven_mode;
+extern void request_command(int shopping);
+extern bool_ is_a_vowel(int ch);
+extern int get_keymap_dir(char ch);
+extern byte count_bits(u32b array);
+extern void strlower(char *buf);
+extern int test_monster_name(cptr name);
+extern int test_mego_name(cptr name);
+extern int test_item_name(cptr name);
+extern char msg_box(cptr text, int y, int x);
+extern timer_type *new_timer(cptr callback, s32b delay);
+extern void del_timer(timer_type *t_ptr);
+extern int get_keymap_mode();
+
+/* main.c */
+extern bool_ private_check_user_directory(cptr dirpath);
+
+/* xtra1.c */
+extern void fix_message(void);
+extern void apply_flags(u32b f1, u32b f2, u32b f3, u32b f4, u32b f5, u32b esp, s16b pval, s16b tval, s16b to_h, s16b to_d, s16b to_a);
+extern int luck(int min, int max);
+extern int weight_limit(void);
+extern bool_ calc_powers_silent;
+extern void cnv_stat(int i, char *out_val);
+extern s16b modify_stat_value(int value, int amount);
+extern void calc_hitpoints(void);
+extern void notice_stuff(void);
+extern void update_stuff(void);
+extern void redraw_stuff(void);
+extern void window_stuff(void);
+extern void handle_stuff(void);
+extern bool_ monk_empty_hands(void);
+extern bool_ monk_heavy_armor(void);
+extern bool_ beastmaster_whip(void);
+extern void calc_bonuses(bool_ silent);
+extern void calc_sanity(void);
+extern void gain_fate(byte fate);
+extern void fate_desc(char *desc, int fate);
+extern void dump_fates(FILE *OutFile);
+
+/* xtra2.c */
+extern int resolve_mimic_name(cptr name);
+extern void do_rebirth(void);
+extern cptr get_subrace_title(int racem);
+extern void set_subrace_title(int racem, cptr name);
+extern void switch_subrace(int racem, bool_ copy_old);
+extern void switch_class(int sclass);
+extern void switch_subclass(int sclass);
+extern void drop_from_wild(void);
+extern void clean_wish_name(char *buf, char *name);
+extern bool_ test_object_wish(char *name, object_type *o_ptr, object_type *forge, char *what);
+extern bool_ set_roots(int v, s16b ac, s16b dam);
+extern bool_ set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag);
+extern bool_ set_rush(int v);
+extern bool_ set_parasite(int v, int r);
+extern bool_ set_disrupt_shield(int v);
+extern bool_ set_prob_travel(int v);
+extern bool_ set_absorb_soul(int v);
+extern bool_ set_tim_breath(int v, bool_ magical);
+extern bool_ set_tim_deadly(int v);
+extern bool_ set_tim_res_time(int v);
+extern bool_ set_tim_reflect(int v);
+extern bool_ set_tim_thunder(int v, int p1, int p2);
+extern bool_ set_meditation(int v);
+extern bool_ set_strike(int v);
+extern bool_ set_walk_water(int v);
+extern bool_ set_tim_regen(int v, int p);
+extern bool_ set_tim_ffall(int v);
+extern bool_ set_tim_fly(int v);
+extern bool_ set_poison(int v);
+extern bool_ set_tim_fire_aura(int v);
+extern bool_ set_holy(int v);
+extern void set_grace(s32b v);
+extern bool_ set_mimic(int v, int p, int level);
+extern bool_ set_no_breeders(int v);
+extern bool_ set_invis(int v,int p);
+extern bool_ set_lite(int v);
+extern bool_ set_blind(int v);
+extern bool_ set_confused(int v);
+extern bool_ set_poisoned(int v);
+extern bool_ set_afraid(int v);
+extern bool_ set_paralyzed(int v);
+extern bool_ set_image(int v);
+extern bool_ set_fast(int v, int p);
+extern bool_ set_light_speed(int v);
+extern bool_ set_slow(int v);
+extern bool_ set_shield(int v, int p, s16b o, s16b d1, s16b d2);
+extern bool_ set_blessed(int v);
+extern bool_ set_hero(int v);
+extern bool_ set_shero(int v);
+extern bool_ set_protevil(int v);
+extern bool_ set_protgood(int v);
+extern bool_ set_protundead(int v);
+extern bool_ set_invuln(int v);
+extern bool_ set_tim_invis(int v);
+extern bool_ set_tim_infra(int v);
+extern bool_ set_mental_barrier(int v);
+extern bool_ set_oppose_acid(int v);
+extern bool_ set_oppose_elec(int v);
+extern bool_ set_oppose_fire(int v);
+extern bool_ set_oppose_cold(int v);
+extern bool_ set_oppose_pois(int v);
+extern bool_ set_oppose_ld(int v);
+extern bool_ set_oppose_cc(int v);
+extern bool_ set_oppose_ss(int v);
+extern bool_ set_oppose_nex(int v);
+extern bool_ set_stun(int v);
+extern bool_ set_cut(int v);
+extern bool_ set_food(int v);
+extern void check_experience(void);
+extern void check_experience_obj(object_type *o_ptr);
+extern void gain_exp(s32b amount);
+extern void lose_exp(s32b amount);
+extern int get_coin_type(monster_race *r_ptr);
+extern void monster_death(int m_idx);
+extern bool_ mon_take_hit(int m_idx, int dam, bool_ *fear, cptr note);
+extern bool_ change_panel(int dy, int dx);
+extern void verify_panel(void);
+extern void resize_map(void);
+extern void resize_window(void);
+extern cptr look_mon_desc(int m_idx);
+extern void ang_sort_aux(vptr u, vptr v, int p, int q);
+extern void ang_sort(vptr u, vptr v, int n);
+extern bool_ target_able(int m_idx);
+extern bool_ target_okay(void);
+extern bool_ target_set(int mode);
+extern bool_ get_aim_dir(int *dp);
+extern bool_ get_hack_dir(int *dp);
+extern bool_ get_rep_dir(int *dp);
+extern int get_chaos_patron(void);
+extern void gain_level_reward(int chosen_reward);
+extern bool_ set_shadow(int v);
+extern bool_ set_tim_esp(int v);
+extern bool_ tgp_pt(int *x, int * y);
+extern bool_ tgt_pt (int *x, int *y);
+extern bool_ gain_random_corruption(int choose_mut);
+extern bool_ got_corruptions(void);
+extern void dump_corruptions(FILE *OutFile, bool_ color);
+extern void do_poly_self(void);
+extern void do_poly_wounds(void);
+extern bool_ curse_weapon(void);
+extern bool_ curse_armor(void);
+extern void random_resistance(object_type * q_ptr, bool_ is_scroll, int specific);
+extern bool_ lose_corruption(int choose_mut);
+extern bool_ lose_all_corruptions(void);
+extern void great_side_effect(void);
+extern void nasty_side_effect(void);
+extern void deadly_side_effect(bool_ god);
+extern void godly_wrath_blast(void);
+extern int interpret_grace(void);
+extern int interpret_favor(void);
+extern void make_wish(void);
+extern bool_ set_sliding(s16b v);
+extern void create_between_gate(int dist, int y, int x);
+
+/* levels.c */
+extern bool_ get_dungeon_generator(char *buf);
+extern bool_ get_level_desc(char *buf);
+extern void get_level_flags(void);
+extern bool_ get_dungeon_name(char *buf);
+extern bool_ get_dungeon_special(char *buf);
+extern bool_ get_command(const char *file, char comm, char *param);
+extern int get_branch(void);
+extern int get_fbranch(void);
+extern int get_flevel(void);
+extern bool_ get_dungeon_save(char *buf);
+
+/* wizard2.c */
+extern void do_cmd_wiz_cure_all(void);
+extern void do_cmd_wiz_named_friendly(int r_idx, bool_ slp);
+extern tval_desc2 tvals[];
+
+/* notes.c */
+extern void show_notes_file(void);
+extern void output_note(char *final_note);
+extern void add_note(char *note, char code);
+extern void add_note_type(int note_number);
+
+/* squeltch.c */
+extern void squeltch_inventory(void);
+extern void squeltch_grid(void);
+extern void do_cmd_automatizer(void);
+extern void automatizer_add_rule(object_type *o_ptr, bool_ destroy);
+extern bool_ automatizer_create;
+
+
+
+/*
+ * Hack -- conditional (or "bizarre") externs
+ */
+
+#ifdef SET_UID
+/* util.c */
+extern void user_name(char *buf, int id);
+#endif
+
+#ifndef HAS_MEMSET
+/* util.c */
+extern char *memset(char*, int, huge);
+#endif
+
+#ifndef HAS_STRICMP
+/* util.c */
+extern int stricmp(cptr a, cptr b);
+#endif
+
+#ifndef HAS_USLEEP
+/* util.c */
+extern int usleep(huge usecs);
+#endif
+
+#ifdef MACINTOSH
+/* main-mac.c */
+/* extern void main(void); */
+#endif
+
+#ifdef MACH_O_CARBON
+/* main-crb.c */
+extern int fsetfileinfo(char *path, u32b fcreator, u32b ftype);
+extern u32b _fcreator;
+extern u32b _ftype;
+#endif /* MACH_O_CARBON */
+
+#ifdef WINDOWS
+/* main-win.c */
+/* extern int FAR PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, ...); */
+#endif
+
+#if !defined(WINDOWS) && !defined(MACINTOSH)
+/* files.c */
+extern bool_ chg_to_txt(cptr base, cptr newname);
+#endif /* !WINDOWS && !MACINTOSH */
+
+/* util.c */
+extern void repeat_push(int what);
+extern bool_ repeat_pull(int *what);
+extern void repeat_check(void);
+extern void get_count(int number, int max);
+
+/* variable.c */
+extern bool_ easy_open;
+extern bool_ easy_tunnel;
+extern bool_ easy_disarm;
+
+/* cmd2.c */
+extern bool_ easy_open_door(int y, int x);
+
+/* cmd2.c */
+extern bool_ do_cmd_disarm_aux(int y, int x, int dir, int do_pickup);
+
+extern bool_ easy_floor;
+
+
+/* script.c */
+extern void init_lua(void);
+extern void init_lua_init(void);
+extern int exec_lua(char *file);
+extern cptr string_exec_lua(char *file);
+extern bool_ tome_dofile(char *file);
+extern bool_ tome_dofile_anywhere(cptr dir, char *file, bool_ test_exist);
+extern void dump_lua_stack(int min, int max);
+extern bool_ call_lua(cptr function, cptr args, cptr ret, ...);
+extern bool_ get_lua_var(cptr name, char type, void *arg);
+
+/* modules.c */
+extern void module_reset_dir(cptr dir, cptr new_path);
+extern cptr force_module;
+extern bool_ select_module(void);
+
+
+/* lua_bind.c */
+extern magic_power *grab_magic_power(magic_power *m_ptr, int num);
+extern bool_ get_magic_power(int *sn, magic_power *powers, int max_powers, void (*power_info)(char *p, int power), int plev, int cast_stat);
+extern bool_ lua_spell_success(magic_power *spell, int stat, char *oups_fct);
+
+extern object_type *new_object(void);
+extern void end_object(object_type *o_ptr);
+extern void lua_set_item_tester(int tval, char *fct);
+extern char *lua_object_desc(object_type *o_ptr, int pref, int mode);
+
+extern s16b add_new_power(cptr name, cptr desc, cptr gain, cptr lose, byte level, byte cost, byte stat, byte diff);
+
+extern void find_position(int y, int x, int *yy, int *xx);
+extern bool_ summon_lua_okay(int r_idx);
+extern bool_ lua_summon_monster(int y, int x, int lev, bool_ ffriend, char *fct);
+
+extern s16b add_new_quest(char *name);
+extern void desc_quest(int q_idx, int d, char *desc);
+
+extern s16b add_new_gods(char *name);
+extern void desc_god(int g_idx, int d, char *desc);
+
+extern bool_ get_com_lua(cptr promtp, int *com);
+
+extern s16b new_school(int i, cptr name, s16b skill);
+extern s16b new_spell(int i, cptr name);
+extern spell_type *grab_spell_type(s16b num);
+extern school_type *grab_school_type(s16b num);
+extern s32b lua_get_level(s32b s, s32b lvl, s32b max, s32b min, s32b bonus);
+extern s32b lua_spell_chance(s32b chance, int level, int skill_level, int mana, int cur_mana, int stat);
+extern s32b lua_spell_device_chance(s32b chance, int level, int base_level);
+
+extern cave_type *lua_get_cave(int y, int x);
+extern void set_target(int y, int x);
+extern void get_target(int dir, int *y, int *x);
+
+extern void get_map_size(char *name, int *ysize, int *xsize);
+extern void load_map(char *name, int *y, int *x);
+extern bool_ alloc_room(int by0, int bx0, int ysize, int xsize, int *y1, int *x1, int *y2, int *x2);
+
+extern void lua_print_hook(cptr str);
+
+extern int lua_get_new_bounty_monster(int lev);
+
+extern char *lua_input_box(cptr title, int max);
+extern char lua_msg_box(cptr title);
+
+extern list_type *lua_create_list(int size);
+extern void lua_delete_list(list_type *, int size);
+extern void lua_add_to_list(list_type *, int idx, cptr str);
+extern void lua_display_list(int y, int x, int h, int w, cptr title, list_type *list, int max, int begin, int sel, byte sel_color);
+
+extern cptr compass(int y, int x, int y2, int x2);
+extern cptr approximate_distance(int y, int x, int y2, int x2);
+
+extern bool_ drop_text_left(byte c, cptr s, int y, int o);
+extern bool_ drop_text_right(byte c, cptr s, int y, int o);
+
+/* skills.c */
+extern void dump_skills(FILE *fff);
+extern s16b find_skill(cptr name);
+extern s16b find_skill_i(cptr name);
+extern s16b get_skill(int skill);
+extern s16b get_skill_scale(int skill, u32b scale);
+extern void do_cmd_skill(void);
+extern void do_cmd_activate_skill(void);
+extern s16b melee_skills[MAX_MELEE];
+extern char *melee_names[MAX_MELEE];
+extern s16b get_melee_skills(void);
+extern s16b get_melee_skill(void);
+extern bool_ forbid_gloves(void);
+extern bool_ forbid_non_blessed(void);
+extern void compute_skills(s32b *v, s32b *m, int i);
+extern void select_default_melee(void);
+extern void do_get_new_skill(void);
+extern void init_skill(s32b value, s32b mod, int i);
+extern s16b find_ability(cptr name);
+extern void dump_abilities(FILE *fff);
+extern void do_cmd_ability(void);
+extern bool_ has_ability(int ab);
+extern void apply_level_abilities(int level);
+extern void recalc_skills(bool_ init);
+
+/* gods.c */
+extern void inc_piety(int god, s32b amt);
+extern void abandon_god(int god);
+extern int wisdom_scale(int max);
+extern int find_god(cptr name);
+extern void follow_god(int god, bool_ silent);
diff --git a/src/files.c b/src/files.c
new file mode 100644
index 00000000..56e57975
--- /dev/null
+++ b/src/files.c
@@ -0,0 +1,6058 @@
+/* File: files.c */
+
+/* Purpose: code dealing with files (and death) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Extract the first few "tokens" from a buffer
+ *
+ * This function uses "colon" and "slash" and delim arg as the delimeter characters.
+ *
+ * We never extract more than "num" tokens. The "last" token may include
+ * "delimeter" characters, allowing the buffer to include a "string" token.
+ *
+ * We save pointers to the tokens in "tokens", and return the number found.
+ *
+ * Hack -- Attempt to handle the 'c' character formalism
+ *
+ * Hack -- An empty buffer, or a final delimeter, yields an "empty" token.
+ *
+ * Hack -- We will always extract at least one token
+ */
+s16b tokenize(char *buf, s16b num, char **tokens, char delim1, char delim2)
+{
+ int i = 0;
+
+ char *s = buf;
+
+
+ /* Process */
+ while (i < num - 1)
+ {
+ char *t;
+
+ /* Scan the string */
+ for (t = s; *t; t++)
+ {
+ /* Found a delimiter */
+ if ((*t == delim1) || (*t == delim2)) break;
+
+ /* Handle single quotes */
+ if (*t == '\'')
+ {
+ /* Advance */
+ t++;
+
+ /* Handle backslash */
+ if (*t == '\\') t++;
+
+ /* Require a character */
+ if (!*t) break;
+
+ /* Advance */
+ t++;
+
+ /* Hack -- Require a close quote */
+ if (*t != '\'') *t = '\'';
+ }
+
+ /* Handle back-slash */
+ if (*t == '\\') t++;
+ }
+
+ /* Nothing left */
+ if (!*t) break;
+
+ /* Nuke and advance */
+ *t++ = '\0';
+
+ /* Save the token */
+ tokens[i++] = s;
+
+ /* Advance */
+ s = t;
+ }
+
+ /* Save the token */
+ tokens[i++] = s;
+
+ /* Number found */
+ return (i);
+}
+
+
+
+/*
+ * Parse a sub-file of the "extra info" (format shown below)
+ *
+ * Each "action" line has an "action symbol" in the first column,
+ * followed by a colon, followed by some command specific info,
+ * usually in the form of "tokens" separated by colons or slashes.
+ *
+ * Blank lines, lines starting with white space, and lines starting
+ * with pound signs ("#") are ignored (as comments).
+ *
+ * Note the use of "tokenize()" to allow the use of both colons and
+ * slashes as delimeters, while still allowing final tokens which
+ * may contain any characters including "delimiters".
+ *
+ * Note the use of "strtol()" to allow all "integers" to be encoded
+ * in decimal, hexidecimal, or octal form.
+ *
+ * Note that "monster zero" is used for the "player" attr/char, "object
+ * zero" will be used for the "stack" attr/char, and "feature zero" is
+ * used for the "nothing" attr/char.
+ *
+ * Parse another file recursively, see below for details
+ * %:<filename>
+ *
+ * Specify the attr/char values for "monsters" by race index
+ * R:<num>:<a>:<c>
+ *
+ * Specify the attr/char values for "objects" by kind index
+ * K:<num>:<a>:<c>
+ *
+ * Specify the attr/char values for "features" by feature index
+ * F:<num>:<a>:<c>
+ *
+ * Specify the attr/char values for "stores" by store index
+ * B:<num>:<a>:<c>
+ *
+ * Specify the attr/char values for unaware "objects" by kind tval
+ * U:<tv>:<a>:<c>
+ *
+ * Specify the attr/char values for inventory "objects" by kind tval
+ * E:<tv>:<a>:<c>
+ *
+ * Define a macro action, given an encoded macro action
+ * A:<str>
+ *
+ * Create a normal macro, given an encoded macro trigger
+ * P:<str>
+ *
+ * Create a command macro, given an encoded macro trigger
+ * C:<str>
+ *
+ * Create a keyset mapping
+ * S:<key>:<key>:<dir>
+ *
+ * Turn an option off, given its name
+ * X:<str>
+ *
+ * Turn an option on, given its name
+ * Y:<str>
+ *
+ * Specify visual information, given an index, and some data
+ * V:<num>:<kv>:<rv>:<gv>:<bv>
+ *
+ * Specify squelch settings
+ * Q:<num>:<squelch>
+ */
+errr process_pref_file_aux(char *buf)
+{
+ int i, j, n1, n2;
+
+ char *zz[16];
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) return (0);
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) return (0);
+
+ /* Skip comments */
+ if (buf[0] == '#') return (0);
+
+ /* Require "?:*" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Process "%:<fname>" */
+ if (buf[0] == '%')
+ {
+ /* Attempt to Process the given file */
+ return (process_pref_file(buf + 2));
+ }
+
+
+ /* Process "R:<num>:<a>/<c>" -- attr/char for monster races */
+ if (buf[0] == 'R')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ monster_race *r_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_r_idx) return (1);
+ r_ptr = &r_info[i];
+ if (n1) r_ptr->x_attr = n1;
+ if (n2)
+ {
+ r_ptr->x_char = n2;
+ }
+ return (0);
+ }
+ }
+
+
+ /* Process "G:<type>:<num>:<a>/<c>" -- attr/char for overlay graphics */
+ if (buf[0] == 'G')
+ {
+ /* Process "G:M:<num>:<a>/<c>" -- attr/char for ego monsters */
+ if (buf[2] == 'M')
+ {
+ if (tokenize(buf + 4, 3, zz, ':', '/') == 3)
+ {
+ monster_ego *re_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_re_idx) return (1);
+ re_ptr = &re_info[i];
+ if (n1) re_ptr->g_attr = n1;
+ if (n2)
+ {
+ re_ptr->g_char = n2;
+ }
+ return (0);
+ }
+ }
+
+ /* Process "G:P:<num>:<a>/<c>" -- attr/char for race modifiers */
+ if (buf[2] == 'P')
+ {
+ if (tokenize(buf + 4, 3, zz, ':', '/') == 3)
+ {
+ player_race_mod *rmp_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_rmp_idx) return (1);
+ rmp_ptr = &race_mod_info[i];
+ if (n1) rmp_ptr->g_attr = n1;
+ if (n2)
+ {
+ rmp_ptr->g_char = n2;
+ }
+ return (0);
+ }
+ }
+
+ /* Process "G:T:<num>:<a>/<c>" -- attr/char for traps */
+ if (buf[2] == 'T')
+ {
+ if (tokenize(buf + 4, 3, zz, ':', '/') == 3)
+ {
+ trap_type *t_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_t_idx) return (1);
+ t_ptr = &t_info[i];
+ if (n1) t_ptr->g_attr = n1;
+ if (n2)
+ {
+ t_ptr->g_char = n2;
+ }
+ return (0);
+ }
+ }
+ }
+
+
+ /* Process "K:<num>:<a>/<c>" -- attr/char for object kinds */
+ else if (buf[0] == 'K')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ object_kind *k_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_k_idx) return (1);
+ k_ptr = &k_info[i];
+ if (n1) k_ptr->x_attr = n1;
+ if (n2)
+ {
+ k_ptr->x_char = n2;
+ }
+ return (0);
+ }
+ }
+
+
+ /* Process "F:<num>:<a>/<c>" -- attr/char for terrain features */
+ else if (buf[0] == 'F')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ feature_type *f_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_f_idx) return (1);
+ f_ptr = &f_info[i];
+ if (n1) f_ptr->x_attr = n1;
+ if (n2)
+ {
+ f_ptr->x_char = n2;
+ }
+ return (0);
+ }
+ }
+
+ /* Process "B:<num>:<a>/<c>" -- attr/char for stores */
+ else if (buf[0] == 'B')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ store_info_type *st_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_st_idx) return (1);
+ st_ptr = &st_info[i];
+ if (n1) st_ptr->x_attr = n1;
+ if (n2) st_ptr->x_char = n2;
+ return (0);
+ }
+ }
+
+ /* Process "S:<num>:<a>/<c>" -- attr/char for special things */
+ else if (buf[0] == 'S')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ j = (byte)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ misc_to_attr[j] = n1;
+ misc_to_char[j] = n2;
+ return (0);
+ }
+ }
+
+ /* Process "U:<tv>:<a>/<c>" -- attr/char for unaware items */
+ else if (buf[0] == 'U')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ j = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+ if (k_ptr->tval == j)
+ {
+ if (n1) k_ptr->d_attr = n1;
+ if (n2) k_ptr->d_char = n2;
+ }
+ }
+ return (0);
+ }
+ }
+
+
+ /* Process "E:<tv>:<a>" -- attribute for inventory objects */
+ else if (buf[0] == 'E')
+ {
+ if (tokenize(buf + 2, 2, zz, ':', '/') == 2)
+ {
+ j = (byte)strtol(zz[0], NULL, 0) % 128;
+ n1 = strtol(zz[1], NULL, 0);
+ if (n1) tval_to_attr[j] = n1;
+ return (0);
+ }
+ }
+
+
+ /* Process "A:<str>" -- save an "action" for later */
+ else if (buf[0] == 'A')
+ {
+ text_to_ascii(macro__buf, buf + 2);
+ return (0);
+ }
+
+ /* Process "P:<str>" -- normal macro */
+ else if (buf[0] == 'P')
+ {
+ char tmp[1024];
+ text_to_ascii(tmp, buf + 2);
+ macro_add(tmp, macro__buf);
+ return (0);
+ }
+
+ /* Process "L:<num>:<trigger>:<descr> -- extended command macro */
+ else if (buf[0] == 'L')
+ {
+ switch (tokenize(buf + 2, 3, zz, ':', 0))
+ {
+ case 3:
+ cli_add(zz[0], zz[1], zz[2]);
+ return 0;
+ case 2:
+ cli_add(zz[0], zz[1], 0);
+ return 0;
+ default:
+ return 1;
+ }
+ }
+
+ /* Process "C:<str>" -- create keymap */
+ else if (buf[0] == 'C')
+ {
+ int mode;
+
+ char tmp[1024];
+
+ if (tokenize(buf + 2, 2, zz, ':', '/') != 2) return (1);
+
+ mode = strtol(zz[0], NULL, 0);
+ if ((mode < 0) || (mode >= KEYMAP_MODES)) return (1);
+
+ text_to_ascii(tmp, zz[1]);
+ if (!tmp[0] || tmp[1]) return (1);
+ i = (byte)(tmp[0]);
+
+ string_free(keymap_act[mode][i]);
+
+ keymap_act[mode][i] = string_make(macro__buf);
+
+ return (0);
+ }
+
+
+ /* Process "V:<num>:<kv>:<rv>:<gv>:<bv>" -- visual info */
+ else if (buf[0] == 'V')
+ {
+ if (tokenize(buf + 2, 5, zz, ':', '/') == 5)
+ {
+ i = (byte)strtol(zz[0], NULL, 0);
+ angband_color_table[i][0] = (byte)strtol(zz[1], NULL, 0);
+ angband_color_table[i][1] = (byte)strtol(zz[2], NULL, 0);
+ angband_color_table[i][2] = (byte)strtol(zz[3], NULL, 0);
+ angband_color_table[i][3] = (byte)strtol(zz[4], NULL, 0);
+ return (0);
+ }
+ }
+ /* set macro trigger names and a template */
+ /* Process "T:<trigger>:<keycode>:<shift-keycode>" */
+ /* Process "T:<template>:<modifier chr>:<modifier name>:..." */
+ else if (buf[0] == 'T')
+ {
+ int len, tok;
+ tok = tokenize(buf + 2, 2 + MAX_MACRO_MOD, zz, ':', '/');
+ if (tok >= 4)
+ {
+ int i;
+ int num;
+
+ if (macro_template != NULL)
+ {
+ free(macro_template);
+ macro_template = NULL;
+ for (i = 0; i < max_macrotrigger; i++)
+ free(macro_trigger_name[i]);
+ max_macrotrigger = 0;
+ }
+
+ if (*zz[0] == '\0') return 0; /* clear template */
+ num = strlen(zz[1]);
+ if (2 + num != tok) return 1; /* error */
+
+ len = strlen(zz[0]) + 1 + num + 1;
+ for (i = 0; i < num; i++)
+ len += strlen(zz[2 + i]) + 1;
+ macro_template = malloc(len);
+
+ strcpy(macro_template, zz[0]);
+ macro_modifier_chr =
+ macro_template + strlen(macro_template) + 1;
+ strcpy(macro_modifier_chr, zz[1]);
+ macro_modifier_name[0] =
+ macro_modifier_chr + strlen(macro_modifier_chr) + 1;
+ for (i = 0; i < num; i++)
+ {
+ strcpy(macro_modifier_name[i], zz[2 + i]);
+ macro_modifier_name[i + 1] = macro_modifier_name[i] +
+ strlen(macro_modifier_name[i]) + 1;
+ }
+ }
+ else if (tok >= 2)
+ {
+ int m;
+ char *t, *s;
+ if (max_macrotrigger >= MAX_MACRO_TRIG)
+ {
+ msg_print("Too many macro triggers!");
+ return 1;
+ }
+ m = max_macrotrigger;
+ max_macrotrigger++;
+
+ len = strlen(zz[0]) + 1 + strlen(zz[1]) + 1;
+ if (tok == 3)
+ len += strlen(zz[2]) + 1;
+ macro_trigger_name[m] = malloc(len);
+
+ t = macro_trigger_name[m];
+ s = zz[0];
+ while (*s)
+ {
+ if ('\\' == *s) s++;
+ *t++ = *s++;
+ }
+ *t = '\0';
+
+ macro_trigger_keycode[0][m] = macro_trigger_name[m] +
+ strlen(macro_trigger_name[m]) + 1;
+ strcpy(macro_trigger_keycode[0][m], zz[1]);
+ if (tok == 3)
+ {
+ macro_trigger_keycode[1][m] = macro_trigger_keycode[0][m] +
+ strlen(macro_trigger_keycode[0][m]) + 1;
+ strcpy(macro_trigger_keycode[1][m], zz[2]);
+ }
+ else
+ {
+ macro_trigger_keycode[1][m] = macro_trigger_keycode[0][m];
+ }
+ }
+ return 0;
+ }
+
+ /* Process "X:<str>" -- turn option off */
+ else if (buf[0] == 'X')
+ {
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ if (option_info[i].o_var &&
+ option_info[i].o_text &&
+ streq(option_info[i].o_text, buf + 2))
+ {
+ (*option_info[i].o_var) = FALSE;
+ return (0);
+ }
+ }
+ }
+
+ /* Process "Y:<str>" -- turn option on */
+ else if (buf[0] == 'Y')
+ {
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ if (option_info[i].o_var &&
+ option_info[i].o_text &&
+ streq(option_info[i].o_text, buf + 2))
+ {
+ (*option_info[i].o_var) = TRUE;
+ return (0);
+ }
+ }
+ }
+
+ /* Process "W:<win>:<flag>:<value>" -- window flags */
+ else if (buf[0] == 'W')
+ {
+ int win, flag, value;
+
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ win = strtol(zz[0], NULL, 0);
+ flag = strtol(zz[1], NULL, 0);
+ value = strtol(zz[2], NULL, 0);
+
+ /* Ignore illegal windows */
+ /* Hack -- Ignore the main window */
+ if ((win <= 0) || (win >= ANGBAND_TERM_MAX)) return (1);
+
+ /* Ignore illegal flags */
+ if ((flag < 0) || (flag >= 32)) return (1);
+
+ /* Require a real flag */
+ if (window_flag_desc[flag])
+ {
+ if (value)
+ {
+ /* Turn flag on */
+ window_flag[win] |= (1L << flag);
+ }
+ else
+ {
+ /* Turn flag off */
+ window_flag[win] &= ~(1L << flag);
+ }
+ }
+
+ /* Success */
+ return (0);
+ }
+ }
+
+ /* Process "Q:<num>:<squelch>" -- item squelch flags */
+ else if (buf[0] == 'Q')
+ {
+ /* This option isn't used anymore */
+ return (0);
+ }
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Helper function for "process_pref_file()"
+ *
+ * Input:
+ * v: output buffer array
+ * f: final character
+ *
+ * Output:
+ * result
+ */
+static cptr process_pref_file_expr(char **sp, char *fp)
+{
+ cptr v;
+
+ char *b;
+ char *s;
+
+ char b1 = '[';
+ char b2 = ']';
+
+ char f = ' ';
+
+ /* Initial */
+ s = (*sp);
+
+ /* Skip spaces */
+ while (isspace(*s)) s++;
+
+ /* Save start */
+ b = s;
+
+ /* Default */
+ v = "?o?o?";
+
+ /* Analyze */
+ if (*s == b1)
+ {
+ const char *p;
+ const char *t;
+
+ /* Skip b1 */
+ s++;
+
+ /* First */
+ t = process_pref_file_expr(&s, &f);
+
+ /* Oops */
+ if (!*t)
+ {
+ /* Nothing */
+ }
+
+ /* Function: IOR */
+ else if (streq(t, "IOR"))
+ {
+ v = "0";
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ if (*t && !streq(t, "0")) v = "1";
+ }
+ }
+
+ /* Function: AND */
+ else if (streq(t, "AND"))
+ {
+ v = "1";
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ if (*t && streq(t, "0")) v = "0";
+ }
+ }
+
+ /* Function: NOT */
+ else if (streq(t, "NOT"))
+ {
+ v = "1";
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ if (*t && !streq(t, "0")) v = "0";
+ }
+ }
+
+ /* Function: EQU */
+ else if (streq(t, "EQU"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && !streq(p, t)) v = "0";
+ }
+ }
+
+ /* Function: LEQ */
+ else if (streq(t, "LEQ"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && (strcmp(p, t) > 0)) v = "0";
+ }
+ }
+
+ /* Function: GEQ */
+ else if (streq(t, "GEQ"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && (strcmp(p, t) < 0)) v = "0";
+ }
+ }
+
+ /* Function: LEQN */
+ else if (streq(t, "LEQN"))
+ {
+ int n = 0;
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ n = atoi(t);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && (atoi(t) < n)) v = "0";
+ }
+ }
+
+ /* Function: GEQN */
+ else if (streq(t, "GEQN"))
+ {
+ int n = 0;
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ n = atoi(t);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && (atoi(t) > n)) v = "0";
+ }
+ }
+
+ /* Function SKILL */
+ else if (streq(t, "SKILL"))
+ {
+ static char skill_val[4*sizeof(int) + 1];
+ s16b skill = -1;
+ v = "0";
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ if (*t) skill = find_skill_i(t);
+ }
+ if (skill > 0)
+ {
+ sprintf(skill_val, "%d", (int)get_skill(skill));
+ v = skill_val;
+ }
+ }
+
+ /* Oops */
+ else
+ {
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ }
+ }
+
+ /* Verify ending */
+ if (f != b2) v = "?x?x?";
+
+ /* Extract final and Terminate */
+ if ((f = *s) != '\0') * s++ = '\0';
+ }
+
+ /* Other */
+ else
+ {
+ /* Accept all printables except spaces and brackets */
+ while (isprint(*s) && !strchr(" []", *s)) ++s;
+
+ /* Extract final and Terminate */
+ if ((f = *s) != '\0') * s++ = '\0';
+
+ /* Variable */
+ if (*b == '$')
+ {
+ /* System */
+ if (streq(b + 1, "SYS"))
+ {
+ v = ANGBAND_SYS;
+ }
+
+ else if (streq(b + 1, "KEYBOARD"))
+ {
+ v = ANGBAND_KEYBOARD;
+ }
+
+ /* Graphics */
+ if (streq(b + 1, "GRAF"))
+ {
+ v = ANGBAND_GRAF;
+ }
+
+ /* Race */
+ else if (streq(b + 1, "RACE"))
+ {
+ v = rp_ptr->title + rp_name;
+ }
+
+ /* Race */
+ else if (streq(b + 1, "RACEMOD"))
+ {
+ v = rmp_ptr->title + rmp_name;
+ }
+
+ /* Class */
+ else if (streq(b + 1, "CLASS"))
+ {
+ v = spp_ptr->title + c_name;
+ }
+
+ /* Player */
+ else if (streq(b + 1, "PLAYER"))
+ {
+ v = player_base;
+ }
+ }
+
+ /* Constant */
+ else
+ {
+ v = b;
+ }
+ }
+
+ /* Save */
+ (*fp) = f;
+
+ /* Save */
+ (*sp) = s;
+
+ /* Result */
+ return (v);
+}
+
+
+
+
+/*
+ * Process the "user pref file" with the given name
+ *
+ * See the function above for a list of legal "commands".
+ *
+ * We also accept the special "?" and "%" directives, which
+ * allow conditional evaluation and filename inclusion.
+ */
+errr process_pref_file(cptr name)
+{
+ FILE *fp;
+
+ char buf[1024];
+
+ int num = -1;
+
+ errr err = 0;
+
+ bool_ bypass = FALSE;
+
+ /* Build the filename -- Allow users to override system pref files */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* No such file -- Try system pref file */
+ if (!fp)
+ {
+ /* Build the pathname, this time using the system pref directory */
+ path_build(buf, 1024, ANGBAND_DIR_PREF, name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Failed again */
+ if (!fp) return ( -1);
+ }
+
+
+ /* Process the file */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Count lines */
+ num++;
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) continue;
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) continue;
+
+ /* Skip comments */
+ if (buf[0] == '#') continue;
+
+
+ /* Process "?:<expr>" */
+ if ((buf[0] == '?') && (buf[1] == ':'))
+ {
+ char f;
+ cptr v;
+ char *s;
+
+ /* Start */
+ s = buf + 2;
+
+ /* Parse the expr */
+ v = process_pref_file_expr(&s, &f);
+
+ /* Set flag */
+ bypass = (streq(v, "0") ? TRUE : FALSE);
+
+ /* Continue */
+ continue;
+ }
+
+ /* Apply conditionals */
+ if (bypass) continue;
+
+
+ /* Process "%:<file>" */
+ if (buf[0] == '%')
+ {
+ /* Process that file if allowed */
+ (void)process_pref_file(buf + 2);
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Process the line */
+ err = process_pref_file_aux(buf);
+
+ /* Oops */
+ if (err) break;
+ }
+
+
+ /* Error */
+ if (err)
+ {
+ /* Useful error message */
+ msg_format("Error %d in line %d of file '%s'.", err, num, name);
+ msg_format("Parsing '%s'", buf);
+ }
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Result */
+ return (err);
+}
+
+
+
+
+/*
+ * Print long number with header at given row, column
+ * Use the color for the number, not the header
+ */
+static void prt_lnum(cptr header, s32b num, int row, int col, byte color)
+{
+ int len = strlen(header);
+ char out_val[32];
+
+ put_str(header, row, col);
+ (void)sprintf(out_val, "%9ld", (long)num);
+ c_put_str(color, out_val, row, col + len);
+}
+
+
+/*
+ * Print number with header at given row, column
+ */
+static void prt_num(cptr header, int num, int row, int col, byte color,
+ char *space)
+{
+ int len = strlen(header);
+ char out_val[32];
+
+ put_str(header, row, col);
+ put_str(space, row, col + len);
+ (void)sprintf(out_val, "%6ld", (long)num);
+ c_put_str(color, out_val, row, col + len + strlen(space));
+}
+
+
+/*
+ * Print str with header at given row, column
+ */
+static void prt_str(cptr header, cptr str, int row, int col, byte color)
+{
+ int len = strlen(header);
+ char out_val[32];
+
+ put_str(header, row, col);
+ put_str(" ", row, col + len);
+ (void)sprintf(out_val, "%6s", str);
+ c_put_str(color, out_val, row, col + len + 3);
+}
+
+
+/*
+ * Prints the following information on the screen.
+ *
+ * For this to look right, the following should be spaced the
+ * same as in the prt_lnum code... -CFT
+ */
+static void display_player_middle(void)
+{
+ int show_tohit = p_ptr->dis_to_h;
+ int show_todam = p_ptr->dis_to_d;
+
+ object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD];
+ char num[7];
+ byte color;
+ int speed;
+
+
+ /* Hack -- add in weapon info if known */
+ if (object_known_p(o_ptr)) show_tohit = p_ptr->dis_to_h + p_ptr->to_h_melee + o_ptr->to_h;
+ else show_tohit = p_ptr->dis_to_h + p_ptr->to_h_melee;
+ if (object_known_p(o_ptr)) show_todam = p_ptr->dis_to_d + p_ptr->to_d_melee + o_ptr->to_d;
+ else show_todam = p_ptr->dis_to_d + p_ptr->to_d_melee;
+
+ /* Dump the bonuses to hit/dam */
+ prt_num("+ To Melee Hit ", show_tohit, 9, 1, TERM_L_BLUE, " ");
+ prt_num("+ To Melee Damage", show_todam, 10, 1, TERM_L_BLUE, " ");
+
+ o_ptr = &p_ptr->inventory[INVEN_BOW];
+
+ /* Hack -- add in weapon info if known */
+ if (object_known_p(o_ptr)) show_tohit = p_ptr->dis_to_h + p_ptr->to_h_ranged + o_ptr->to_h;
+ else show_tohit = p_ptr->dis_to_h + p_ptr->to_h_ranged;
+ if (object_known_p(o_ptr)) show_todam = p_ptr->to_d_ranged + o_ptr->to_d;
+ else show_todam = p_ptr->to_d_ranged;
+
+ prt_num("+ To Ranged Hit ", show_tohit, 11, 1, TERM_L_BLUE, " ");
+ prt_num("+ To Ranged Damage", show_todam, 12, 1, TERM_L_BLUE, " ");
+
+ /* Dump the total armor class */
+ prt_str(" AC ", format("%d+%d", p_ptr->ac, p_ptr->dis_to_a), 13, 1, TERM_L_BLUE);
+
+ prt_num("Level ", (int)p_ptr->lev, 9, 28, TERM_L_GREEN, " ");
+
+ if (p_ptr->exp >= p_ptr->max_exp)
+ {
+ prt_lnum("Experience ", p_ptr->exp, 10, 28, TERM_L_GREEN);
+ }
+ else
+ {
+ prt_lnum("Experience ", p_ptr->exp, 10, 28, TERM_YELLOW);
+ }
+
+ prt_lnum("Max Exp ", p_ptr->max_exp, 11, 28, TERM_L_GREEN);
+
+ if ((p_ptr->lev >= PY_MAX_LEVEL) || (p_ptr->lev >= max_plev))
+ {
+ put_str("Exp to Adv.", 12, 28);
+ c_put_str(TERM_L_GREEN, " *****", 12, 28 + 11);
+ }
+ else
+ {
+ prt_lnum("Exp to Adv.",
+ (s32b)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L),
+ 12, 28, TERM_L_GREEN);
+ }
+
+ prt_lnum("Gold ", p_ptr->au, 13, 28, TERM_L_GREEN);
+
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ put_str("Death Points ", 9, 52);
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_BLUE;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_VIOLET;
+ }
+ else
+ {
+ color = TERM_L_RED;
+ }
+ (void)sprintf(num, "%6ld", (long)p_ptr->chp);
+ c_put_str(color, num, 9, 65);
+ put_str("/", 9, 71);
+ (void)sprintf(num, "%6ld", (long)p_ptr->mhp);
+ c_put_str(TERM_L_BLUE, num, 9, 72);
+ }
+ else
+ {
+ put_str("Hit Points ", 9, 52);
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ (void)sprintf(num, "%6ld", (long)p_ptr->chp);
+ c_put_str(color, num, 9, 65);
+ put_str("/", 9, 71);
+ (void)sprintf(num, "%6ld", (long)p_ptr->mhp);
+ c_put_str(TERM_L_GREEN, num, 9, 72);
+ }
+
+ put_str("Spell Points ", 10, 52);
+ if (p_ptr->csp >= p_ptr->msp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->csp > (p_ptr->msp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ (void)sprintf(num, "%6ld", (long)p_ptr->csp);
+ c_put_str(color, num, 10, 65);
+ put_str("/", 10, 71);
+ (void)sprintf(num, "%6ld", (long)p_ptr->msp);
+ c_put_str(TERM_L_GREEN, num, 10, 72);
+
+ put_str("Sanity ", 11, 52);
+ if (p_ptr->csane >= p_ptr->msane)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->csane > (p_ptr->msane * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ (void)sprintf(num, "%6ld", (long)p_ptr->csane);
+ c_put_str(color, num, 11, 65);
+ put_str("/", 11, 71);
+ (void)sprintf(num, "%6ld", (long)p_ptr->msane);
+ c_put_str(TERM_L_GREEN, num, 11, 72);
+
+ if (p_ptr->pgod != GOD_NONE)
+ {
+ prt_num("Piety ", p_ptr->grace, 12, 52, TERM_L_GREEN, " ");
+ }
+
+ put_str("Speed ", 13, 52);
+ speed = p_ptr->pspeed;
+ /* Hack -- Visually "undo" the Search Mode Slowdown */
+ if (p_ptr->searching) speed += 10;
+ if (speed > 110)
+ {
+ char s[11];
+ (void)sprintf(s, "Fast (+%d)", speed - 110);
+ c_put_str(TERM_L_GREEN, s, 13, (speed >= 120) ? 68 : 69);
+ }
+ else if (speed < 110)
+ {
+ char s[11];
+ (void)sprintf(s, "Slow (-%d)", 110 - speed);
+ c_put_str(TERM_L_UMBER, s, 13, (speed <= 100) ? 68 : 69);
+ }
+ else
+ {
+ put_str("Normal", 13, 72);
+ }
+}
+
+
+
+
+/*
+ * Hack -- pass color info around this file
+ */
+static byte likert_color = TERM_WHITE;
+
+
+/*
+ * Returns a "rating" of x depending on y
+ */
+static cptr likert(int x, int y)
+{
+ static char dummy[20] = "";
+
+ /* Paranoia */
+ if (y <= 0) y = 1;
+
+ /* Negative value */
+ if (x < 0)
+ {
+ likert_color = TERM_L_DARK;
+ return ("Very Bad");
+ }
+
+ /* Analyze the value */
+ switch ((x / y))
+ {
+ case 0:
+ case 1:
+ {
+ likert_color = TERM_RED;
+ return ("Bad");
+ }
+ case 2:
+ {
+ likert_color = TERM_L_RED;
+ return ("Poor");
+ }
+ case 3:
+ case 4:
+ {
+ likert_color = TERM_ORANGE;
+ return ("Fair");
+ }
+ case 5:
+ {
+ likert_color = TERM_YELLOW;
+ return ("Good");
+ }
+ case 6:
+ {
+ likert_color = TERM_YELLOW;
+ return ("Very Good");
+ }
+ case 7:
+ case 8:
+ {
+ likert_color = TERM_L_GREEN;
+ return ("Excellent");
+ }
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ {
+ likert_color = TERM_GREEN;
+ return ("Superb");
+ }
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ {
+ likert_color = TERM_L_GREEN;
+ return ("Heroic");
+ }
+ default:
+ {
+ likert_color = TERM_L_GREEN;
+ sprintf(dummy, "Legendary[%d]", (int)((((x / y) - 17) * 5) / 2));
+ return dummy;
+ }
+ }
+}
+
+
+/*
+ * Prints ratings on certain abilities
+ *
+ * This code is "imitated" elsewhere to "dump" a character sheet.
+ */
+static void display_player_various(void)
+{
+ int tmp, tmp2, damdice, damsides, dambonus, blows;
+ int xthn, xthb, xfos, xsrh;
+ int xdis, xdev, xsav, xstl;
+ cptr desc;
+ int i;
+
+ object_type *o_ptr;
+
+
+ /* Fighting Skill (with current weapon) */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+ tmp = p_ptr->to_h + o_ptr->to_h + p_ptr->to_h_melee;
+ xthn = p_ptr->skill_thn + (tmp * BTH_PLUS_ADJ);
+
+ /* Shooting Skill (with current bow and normal missile) */
+ o_ptr = &p_ptr->inventory[INVEN_BOW];
+ tmp = p_ptr->to_h + o_ptr->to_h + p_ptr->to_h_ranged;
+ xthb = p_ptr->skill_thb + (tmp * BTH_PLUS_ADJ);
+
+ /* variables for all types of melee damage */
+ dambonus = p_ptr->dis_to_d;
+ blows = p_ptr->num_blow;
+
+ /* Basic abilities */
+ xdis = p_ptr->skill_dis;
+ xdev = p_ptr->skill_dev;
+ xsav = p_ptr->skill_sav;
+ xstl = p_ptr->skill_stl;
+ xsrh = p_ptr->skill_srh;
+ xfos = p_ptr->skill_fos;
+
+
+ put_str("Fighting :", 16, 1);
+ desc = likert(xthn, 12);
+ c_put_str(likert_color, desc, 16, 15);
+
+ put_str("Bows/Throw :", 17, 1);
+ desc = likert(xthb, 12);
+ c_put_str(likert_color, desc, 17, 15);
+
+ put_str("Saving Throw:", 18, 1);
+ desc = likert(xsav, 6);
+ c_put_str(likert_color, desc, 18, 15);
+
+ put_str("Stealth :", 19, 1);
+ desc = likert(xstl, 1);
+ c_put_str(likert_color, desc, 19, 15);
+
+
+ put_str("Perception :", 16, 28);
+ desc = likert(xfos, 6);
+ c_put_str(likert_color, desc, 16, 42);
+
+ put_str("Searching :", 17, 28);
+ desc = likert(xsrh, 6);
+ c_put_str(likert_color, desc, 17, 42);
+
+ put_str("Disarming :", 18, 28);
+ desc = likert(xdis, 8);
+ c_put_str(likert_color, desc, 18, 42);
+
+ put_str("Magic Device:", 19, 28);
+ desc = likert(xdev, 6);
+ c_put_str(likert_color, desc, 19, 42);
+
+
+ put_str("Blows/Round:", 16, 55);
+ put_str(format("%d", p_ptr->num_blow), 16, 69);
+
+ put_str("Shots/Round:", 17, 55);
+ put_str(format("%d", p_ptr->num_fire), 17, 69);
+
+ put_str("Mel.dmg/Rnd:", 18, 55); /* From PsiAngband */
+
+ if (p_ptr->melee_style == SKILL_HAND || p_ptr->melee_style == SKILL_BEAR)
+ {
+ /* This is all based on py_attack_hand */
+ martial_arts *blow_table, *min_attack, *max_attack;
+ int max_blow, plev, i;
+
+ if (p_ptr->melee_style == SKILL_HAND)
+ {
+ blow_table = ma_blows;
+ max_blow = MAX_MA;
+ plev = get_skill(SKILL_HAND);
+ }
+ else /* SKILL_BEAR */
+ {
+ blow_table = bear_blows;
+ max_blow = MAX_BEAR;
+ plev = get_skill(SKILL_BEAR);
+ }
+ min_attack = blow_table;
+ i = max_blow - 1;
+ while (blow_table[i].min_level > plev && i != 0)
+ --i;
+ max_attack = &blow_table[i];
+
+ dambonus += p_ptr->to_d_melee;
+ tmp = min_attack->dd + dambonus;
+ if (tmp < 0) tmp = 0;
+ tmp2 = maxroll(max_attack->dd, max_attack->ds) + dambonus;
+ if (tmp2 < 0) tmp2 = 0;
+ if (!tmp && !tmp2)
+ desc = "0";
+ else
+ desc = format("%d-%d", blows * tmp, blows * tmp2);
+ }
+ else if (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON])
+ {
+ if (r_info[p_ptr->body_monster].flags1 & RF1_NEVER_BLOW)
+ desc = "nil!";
+ else
+ {
+ tmp = tmp2 = 0;
+ for (i = 0; i < blows; i++)
+ {
+ tmp += r_info[p_ptr->body_monster].blow[i].d_dice;
+ tmp2 += maxroll(r_info[p_ptr->body_monster].blow[i].d_dice,
+ r_info[p_ptr->body_monster].blow[i].d_side);
+ }
+ if (dambonus > 0)
+ {
+ tmp += dambonus;
+ tmp2 += dambonus;
+ }
+ desc = format("%d-%d", tmp, tmp2);
+ }
+ }
+ else
+ {
+ /* Increase the bonus to damage for weapon combat */
+ dambonus += p_ptr->to_d_melee;
+
+ /* Access the first weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ if (object_known_p(o_ptr)) dambonus += o_ptr->to_d;
+
+ damdice = o_ptr->dd;
+ damsides = o_ptr->ds;
+
+ if ((damdice == 0) || (damsides == 0))
+ {
+ if (dambonus <= 0)
+ desc = "nil!";
+ else
+ desc = format("%d", blows * dambonus);
+ }
+ else
+ {
+ if (dambonus == 0)
+ desc = format("%dd%d", blows * damdice, damsides);
+ else
+ desc = format("%dd%d%c%d", blows * damdice, damsides,
+ ( dambonus > 0 ? '+' : '\0' ), blows * dambonus );
+ }
+ }
+ put_str(desc, 18, 69);
+
+
+ put_str("Infra-Vision:", 19, 55);
+ put_str(format("%d feet", p_ptr->see_infra * 10), 19, 69);
+
+ /* jk - add tactic */
+ put_str("Tactic:", 20, 55);
+ c_put_str(TERM_L_BLUE, tactic_info[(byte)p_ptr->tactic].name, 20, 69);
+
+ /* jk - add movement */
+ put_str("Explor:", 21, 55);
+ c_put_str(TERM_L_BLUE, move_info[(byte)p_ptr->movement].name, 21, 69);
+}
+
+
+
+/*
+ * Obtain the "flags" of the wielded symbiote
+ */
+
+void wield_monster_flags(u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ object_type *o_ptr;
+ monster_race *r_ptr;
+
+ /* Clear */
+ (*f1) = (*f2) = (*f3) = (*f4) = (*f5) = (*esp) = 0L;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (o_ptr->k_idx)
+ {
+ r_ptr = &r_info[o_ptr->pval];
+
+ if (r_ptr->flags2 & RF2_INVISIBLE)
+ (*f2) |= TR2_INVIS;
+ if (r_ptr->flags2 & RF2_REFLECTING)
+ (*f2) |= TR2_REFLECT;
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ (*f3) |= TR3_FEATHER;
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ (*f5) |= TR5_WATER_BREATH;
+ }
+}
+
+
+/*
+ * Obtain the "flags" for the player as if he was an item
+ */
+void player_flags(u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ int i;
+
+ /* Clear */
+ (*f1) = (*f2) = (*f3) = (*f4) = (*f5) = (*esp) = 0L;
+
+ /* Astral chars */
+ if (p_ptr->astral)
+ {
+ (*f3) |= TR3_WRAITH;
+ }
+
+/* Skills */
+ if (get_skill(SKILL_DAEMON) > 20) (*f2) |= TR2_RES_CONF;
+ if (get_skill(SKILL_DAEMON) > 30) (*f2) |= TR2_RES_FEAR;
+ if (get_skill(SKILL_MINDCRAFT) >= 40) (*esp) |= ESP_ALL;
+ if (p_ptr->melee_style == SKILL_HAND && get_skill(SKILL_HAND) > 24 && !monk_heavy_armor())
+ (*f2) |= TR2_FREE_ACT;
+/* Hack - from Lua */
+ if (get_skill(SKILL_MANA) >= 35) (*f1) |= TR1_MANA;
+ if (get_skill(SKILL_AIR) >= 50) (*f5) |= (TR5_MAGIC_BREATH | TR5_WATER_BREATH);
+ if (get_skill(SKILL_WATER) >= 30) (*f5) |= TR5_WATER_BREATH;
+
+/* Gods */
+ GOD(GOD_ERU)
+ {
+ if ((p_ptr->grace >= 100) || (p_ptr->grace <= -100)) (*f1) |= TR1_MANA;
+ if (p_ptr->grace > 10000) (*f1) |= TR1_WIS;
+ }
+
+ GOD(GOD_MELKOR)
+ {
+ (*f2) |= TR2_RES_FIRE;
+ if (p_ptr->melkor_sacrifice > 0) (*f2) |= TR2_LIFE;
+ if (p_ptr->grace > 10000) (*f1) |= (TR1_STR | TR1_CON | TR1_INT | TR1_WIS | TR1_CHR);
+ PRAY_GOD(GOD_MELKOR)
+ {
+ if (p_ptr->grace > 5000) (*f2) |= TR2_INVIS;
+ if (p_ptr->grace > 15000) (*f2) |= TR2_IM_FIRE;
+ }
+ }
+
+ GOD(GOD_MANWE)
+ {
+ if (p_ptr->grace >= 2000) (*f3) |= TR3_FEATHER;
+ PRAY_GOD(GOD_MANWE)
+ {
+ if (p_ptr->grace >= 7000) (*f2) |= TR2_FREE_ACT;
+ if (p_ptr->grace >= 15000) (*f4) |= TR4_FLY;
+ if ((p_ptr->grace >= 5000) || (p_ptr->grace <= -5000)) (*f1) |= TR1_SPEED;
+ }
+ }
+
+ GOD(GOD_TULKAS)
+ {
+ if (p_ptr->grace > 5000) (*f1) |= TR1_CON;
+ if (p_ptr->grace > 10000) (*f1) |= TR1_STR;
+ }
+
+ /* Classes */
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ (*f1) |= cp_ptr->oflags1[i];
+ (*f2) |= cp_ptr->oflags2[i];
+ (*f3) |= cp_ptr->oflags3[i];
+ (*f4) |= cp_ptr->oflags4[i];
+ (*f5) |= cp_ptr->oflags5[i];
+ (*esp) |= cp_ptr->oesp[i];
+ }
+
+ /* Races */
+ if ((!p_ptr->mimic_form) && (!p_ptr->body_monster))
+ {
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ (*f1) |= rp_ptr->oflags1[i];
+ (*f2) |= rp_ptr->oflags2[i];
+ (*f3) |= rp_ptr->oflags3[i];
+ (*f4) |= rp_ptr->oflags4[i];
+ (*f5) |= rp_ptr->oflags5[i];
+ (*esp) |= rp_ptr->oesp[i];
+
+ (*f1) |= rmp_ptr->oflags1[i];
+ (*f2) |= rmp_ptr->oflags2[i];
+ (*f3) |= rmp_ptr->oflags3[i];
+ (*f4) |= rmp_ptr->oflags4[i];
+ (*f5) |= rmp_ptr->oflags5[i];
+ (*esp) |= rmp_ptr->oesp[i];
+ }
+ }
+ else
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ if (r_ptr->flags2 & RF2_REFLECTING) (*f2) |= TR2_REFLECT;
+ if (r_ptr->flags2 & RF2_REGENERATE) (*f3) |= TR3_REGEN;
+ if (r_ptr->flags2 & RF2_AURA_FIRE) (*f3) |= TR3_SH_FIRE;
+ if (r_ptr->flags2 & RF2_AURA_ELEC) (*f3) |= TR3_SH_ELEC;
+ if (r_ptr->flags2 & RF2_PASS_WALL) (*f3) |= TR3_WRAITH;
+ if (r_ptr->flags3 & RF3_SUSCEP_FIRE) (*f2) |= TR2_SENS_FIRE;
+ if (r_ptr->flags3 & RF3_IM_ACID) (*f2) |= TR2_RES_ACID;
+ if (r_ptr->flags3 & RF3_IM_ELEC) (*f2) |= TR2_RES_ELEC;
+ if (r_ptr->flags3 & RF3_IM_FIRE) (*f2) |= TR2_RES_FIRE;
+ if (r_ptr->flags3 & RF3_IM_POIS) (*f2) |= TR2_RES_POIS;
+ if (r_ptr->flags3 & RF3_IM_COLD) (*f2) |= TR2_RES_COLD;
+ if (r_ptr->flags3 & RF3_RES_NETH) (*f2) |= TR2_RES_NETHER;
+ if (r_ptr->flags3 & RF3_RES_NEXU) (*f2) |= TR2_RES_NEXUS;
+ if (r_ptr->flags3 & RF3_RES_DISE) (*f2) |= TR2_RES_DISEN;
+ if (r_ptr->flags3 & RF3_NO_FEAR) (*f2) |= TR2_RES_FEAR;
+ if (r_ptr->flags3 & RF3_NO_SLEEP) (*f2) |= TR2_FREE_ACT;
+ if (r_ptr->flags3 & RF3_NO_CONF) (*f2) |= TR2_RES_CONF;
+ if (r_ptr->flags7 & RF7_CAN_FLY) (*f3) |= TR3_FEATHER;
+ }
+
+ (*f1) |= p_ptr->xtra_f1;
+ (*f2) |= p_ptr->xtra_f2;
+ (*f3) |= p_ptr->xtra_f3;
+ (*f4) |= p_ptr->xtra_f4;
+ (*f5) |= p_ptr->xtra_f5;
+ (*esp) |= p_ptr->xtra_esp;
+
+ if (p_ptr->black_breath)
+ {
+ (*f4) |= TR4_BLACK_BREATH;
+ }
+
+ if (p_ptr->hp_mod != 0)
+ {
+ (*f2) |= TR2_LIFE;
+ }
+}
+
+/*
+ * Object flag names
+ */
+static cptr object_flag_names[192] =
+{
+ "Add Str",
+ "Add Int",
+ "Add Wis",
+ "Add Dex",
+ "Add Con",
+ "Add Chr",
+ "Mul Mana",
+ "Mul SPower",
+ "Add Stea.",
+ "Add Sear.",
+ "Add Infra",
+ "Add Tun..",
+ "Add Speed",
+ "Add Blows",
+ "Chaotic",
+ "Vampiric",
+ "Slay Anim.",
+ "Slay Evil",
+ "Slay Und.",
+ "Slay Demon",
+ "Slay Orc",
+ "Slay Troll",
+ "Slay Giant",
+ "Slay Drag.",
+ "Kill Drag.",
+ "Sharpness",
+ "Impact",
+ "Poison Brd",
+ "Acid Brand",
+ "Elec Brand",
+ "Fire Brand",
+ "Cold Brand",
+
+ "Sust Str",
+ "Sust Int",
+ "Sust Wis",
+ "Sust Dex",
+ "Sust Con",
+ "Sust Chr",
+ "Invisible",
+ "Mul life",
+ "Imm Acid",
+ "Imm Elec",
+ "Imm Fire",
+ "Imm Cold",
+ "Sens Fire",
+ "Reflect",
+ "Free Act",
+ "Hold Life",
+ "Res Acid",
+ "Res Elec",
+ "Res Fire",
+ "Res Cold",
+ "Res Pois",
+ "Res Fear",
+ "Res Light",
+ "Res Dark",
+ "Res Blind",
+ "Res Conf",
+ "Res Sound",
+ "Res Shard",
+ "Res Neth",
+ "Res Nexus",
+ "Res Chaos",
+ "Res Disen",
+
+
+
+ "Aura Fire",
+ "Aura Elec",
+ "Auto Curse",
+ NULL,
+ "NoTeleport",
+ "AntiMagic",
+ "WraithForm",
+ "EvilCurse",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Levitate",
+ "Lite",
+ "See Invis",
+ NULL,
+ "Digestion",
+ "Regen",
+ "Xtra Might",
+ "Xtra Shots",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Activate",
+ "Drain Exp",
+ "Teleport",
+ "Aggravate",
+ "Blessed",
+ "Cursed",
+ "Hvy Curse",
+ "Prm Curse",
+
+ "No blows",
+ "Precogn.",
+ "B.Breath",
+ "Recharge",
+ "Fly",
+ "Mrg.Curse",
+ NULL,
+ NULL,
+ "Sentient",
+ "Clone",
+ NULL,
+ "Climb",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Imm Neth",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ "Orc.ESP",
+ "Troll.ESP",
+ "Dragon.ESP",
+ "Giant.ESP",
+ "Demon.ESP",
+ "Undead.ESP",
+ "Evil.ESP",
+ "Animal.ESP",
+ "TLord.ESP",
+ "Good.ESP",
+ "Nlive.ESP",
+ "Unique.ESP",
+ "Spider ESP",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Full ESP",
+};
+
+/*
+ * Summarize resistances
+ */
+static void display_player_ben_one(int mode)
+{
+ int i, n, x, y, z, dispx, modetemp, xtemp;
+
+ object_type *o_ptr;
+
+ char dummy[80], c;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ u16b b[INVEN_TOTAL - INVEN_WIELD + 1][10];
+
+ int d[INVEN_TOTAL - INVEN_WIELD + 1];
+
+ bool_ got;
+
+ byte a;
+
+ cptr name;
+
+ /* Scan equipment */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ /* Index */
+ n = (i - INVEN_WIELD);
+
+ /* Object */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Known object flags */
+ object_flags_known(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Incorporate */
+ b[n][0] = (u16b)(f1 & 0xFFFF);
+ b[n][1] = (u16b)(f1 >> 16);
+ b[n][2] = (u16b)(f2 & 0xFFFF);
+ b[n][3] = (u16b)(f2 >> 16);
+ b[n][4] = (u16b)(f3 & 0xFFFF);
+ b[n][5] = (u16b)(f3 >> 16);
+ b[n][6] = (u16b)(f4 & 0xFFFF);
+ b[n][7] = (u16b)(f4 >> 16);
+ b[n][8] = (u16b)(esp & 0xFFFF);
+ b[n][9] = (u16b)(esp >> 16);
+ d[n] = o_ptr->pval;
+ }
+
+ /* Carried symbiote */
+ n = INVEN_CARRY - INVEN_WIELD;
+
+ /* Player flags */
+ wield_monster_flags(&f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Incorporate */
+ b[n][0] = (u16b)(f1 & 0xFFFF);
+ b[n][1] = (u16b)(f1 >> 16);
+ b[n][2] = (u16b)(f2 & 0xFFFF);
+ b[n][3] = (u16b)(f2 >> 16);
+ b[n][4] = (u16b)(f3 & 0xFFFF);
+ b[n][5] = (u16b)(f3 >> 16);
+ b[n][6] = (u16b)(f4 & 0xFFFF);
+ b[n][7] = (u16b)(f4 >> 16);
+ b[n][8] = (u16b)(esp & 0xFFFF);
+ b[n][9] = (u16b)(esp >> 16);
+
+ /* Index */
+ n = INVEN_TOTAL - INVEN_WIELD;
+
+ /* Player flags */
+ player_flags(&f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Incorporate */
+ b[n][0] = (u16b)(f1 & 0xFFFF);
+ b[n][1] = (u16b)(f1 >> 16);
+ b[n][2] = (u16b)(f2 & 0xFFFF);
+ b[n][3] = (u16b)(f2 >> 16);
+ b[n][4] = (u16b)(f3 & 0xFFFF);
+ b[n][5] = (u16b)(f3 >> 16);
+ b[n][6] = (u16b)(f4 & 0xFFFF);
+ b[n][7] = (u16b)(f4 >> 16);
+ b[n][8] = (u16b)(esp & 0xFFFF);
+ b[n][9] = (u16b)(esp >> 16);
+
+ /* Generate the equip chars */
+ sprintf(dummy, " ");
+ for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++)
+ {
+ /* If you have that body part then show it */
+ if (p_ptr->body_parts[i])
+ {
+ strcat(dummy, format("%c", i + 'a'));
+ }
+ }
+ strcat(dummy, "@");
+
+ /* Scan cols */
+ for (x = 1; x > -1; x--)
+ {
+ /* Label */
+ Term_putstr(x * 40 + 11, 3, -1, TERM_WHITE, dummy);
+
+ /* Scan rows */
+ for (y = 0; y < 16; y++)
+ {
+ if (mode == 3 && x == 1)
+ {
+ modetemp = 4;
+ xtemp = 0;
+ }
+ else
+ {
+ modetemp = mode;
+ xtemp = x;
+ }
+
+ for (z = mode; z <= modetemp; z++)
+ {
+ if (mode == 3 && x == 1 && z == modetemp) xtemp = 1;
+ name = object_flag_names[32 * modetemp + 16 * xtemp + y];
+ got = FALSE;
+
+ /* No name */
+ if (!name) continue;
+
+ /* Dump colon */
+ if (!(modetemp == 1 && x == 0 && y > 7 && y < 12))
+ {
+ Term_putch(x * 40 + 10, y + 4, TERM_WHITE, ':');
+ }
+
+ /* Check flags */
+ dispx = 0;
+ for (n = 0; n < INVEN_TOTAL - INVEN_WIELD + 1; n++)
+ {
+ /* Change colour every two columns */
+ bool_ is_green = (dispx & 0x02);
+ a = (is_green ? TERM_GREEN : TERM_SLATE);
+ c = '.';
+
+ /* If the body part doesn't exists then skip it :) */
+ if ((n < INVEN_TOTAL - INVEN_WIELD) && (!p_ptr->body_parts[n])) continue;
+
+ /* Increment the drawing coordinates */
+ dispx++;
+
+ /* Check flag */
+ if (b[n][2 * modetemp + xtemp] & (1 << y))
+ {
+ a = (is_green ? TERM_L_GREEN : TERM_WHITE);
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ c = '*';
+ }
+ else if (modetemp == 0 && x == 0 && y < 14 && (y < 6 || y > 7))
+ {
+ if (n == INVEN_TOTAL - INVEN_WIELD)
+ {
+ c = '+';
+ }
+ else
+ {
+ c = d[n];
+ if (c < 0)
+ {
+ c = -c;
+ a = TERM_RED;
+ }
+ c = (c > 9 ? '*' : I2D(c));
+ }
+ }
+ else
+ {
+ c = '+';
+ }
+ got = TRUE;
+ }
+
+ /* HACK - Check for nether immunity and
+ apply to Res Neth line */
+ if (modetemp == 1 && x == 1 && y == 12)
+ {
+ if (b[n][7] & (1 << 6))
+ {
+ a = (is_green ? TERM_L_GREEN : TERM_WHITE);
+ c = '*';
+ got = TRUE;
+ }
+ }
+
+ /* Dump flag */
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ if (c == '*') Term_putch(40 + 11 + dispx, y - 4, a, c);
+ }
+ else
+ {
+ Term_putch(x * 40 + 11 + dispx, y + 4, a, c);
+ }
+ }
+
+ a = TERM_WHITE;
+ if (got)
+ {
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ a = TERM_L_GREEN;
+ }
+ else if (modetemp != 0)
+ {
+ a = TERM_GREEN;
+ }
+ }
+
+ /* HACK - Check for nether immunity and change "Res Neth" */
+ if (modetemp == 1 && x == 1 && y == 12 && p_ptr->immune_neth)
+ {
+ name = "Imm Neth";
+ a = TERM_L_GREEN;
+ }
+
+ /* Dump name */
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ if (got) Term_putstr(40, y - 4, -1, a, name);
+ }
+ else
+ {
+ Term_putstr(x * 40, y + 4, -1, a, name);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Display the character on the screen (various modes)
+ *
+ * The top two and bottom two lines are left blank.
+ *
+ * Mode 0 = standard display with skills
+ * Mode 1 = standard display with history
+ * Mode 2 = current flags (part 1)
+ * Mode 3 = current flags (part 2)
+ * Mode 4 = current flags (part 3)
+ * Mode 5 = current flags (part 4)
+ * Mode 6 = current flags (part 5 -- esp)
+ */
+void display_player(int mode)
+{
+ int i;
+
+ char buf[80];
+
+
+ /* Erase screen */
+ clear_from(0);
+
+ /* Standard */
+ if ((mode == 0) || (mode == 1))
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ /* Name, Sex, Race, Class */
+ put_str("Name :", 2, 1);
+ put_str("Sex :", 3, 1);
+ put_str("Race :", 4, 1);
+ put_str("Class :", 5, 1);
+ put_str("Body :", 6, 1);
+ put_str("God :", 7, 1);
+ c_put_str(TERM_L_BLUE, player_name, 2, 9);
+ if (p_ptr->body_monster != 0)
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ char tmp[12];
+
+ if ((r_ptr->flags1 & RF1_MALE) != 0)
+ strcpy(tmp, "Male");
+ else if ((r_ptr->flags1 & RF1_FEMALE) != 0)
+ strcpy(tmp, "Female");
+ else
+ strcpy(tmp, "Neuter");
+ c_put_str(TERM_L_BLUE, tmp, 3, 9);
+ }
+ else
+ c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 9);
+ sprintf(buf, "%s", get_player_race_name(p_ptr->prace, p_ptr->pracem));
+ c_put_str(TERM_L_BLUE, buf, 4, 9);
+ c_put_str(TERM_L_BLUE, spp_ptr->title + c_name, 5, 9);
+ c_put_str(TERM_L_BLUE, r_name + r_ptr->name, 6, 9);
+ c_put_str(TERM_L_BLUE, deity_info[p_ptr->pgod].name, 7, 9);
+
+ /* Age, Height, Weight, Social */
+ prt_num("Age ", (int)p_ptr->age + bst(YEAR, turn - (START_DAY * 10)), 2, 32, TERM_L_BLUE, " ");
+ prt_num("Height ", (int)p_ptr->ht, 3, 32, TERM_L_BLUE, " ");
+ prt_num("Weight ", (int)p_ptr->wt, 4, 32, TERM_L_BLUE, " ");
+ prt_num("Social Class ", (int)p_ptr->sc, 5, 32, TERM_L_BLUE, " ");
+
+ /* Display the stats */
+ for (i = 0; i < 6; i++)
+ {
+ char punctuation = p_ptr->stat_max[i] == 18 + 100 ? '!' : ':';
+ /* Special treatment of "injured" stats */
+ if (p_ptr->stat_cur[i] < p_ptr->stat_max[i])
+ {
+ int value;
+ int colour;
+
+ if (p_ptr->stat_cnt[i])
+ colour = TERM_ORANGE;
+ else
+ colour = TERM_YELLOW;
+
+ /* Use lowercase stat name */
+ put_str(format("%s%c ", stat_names_reduced[i], punctuation), 2 + i, 61);
+
+ /* Get the current stat */
+ value = p_ptr->stat_use[i];
+
+ /* Obtain the current stat (modified) */
+ cnv_stat(value, buf);
+
+ /* Display the current stat (modified) */
+ c_put_str(colour, buf, 2 + i, 66);
+
+ /* Acquire the max stat */
+ value = p_ptr->stat_top[i];
+
+ /* Obtain the maximum stat (modified) */
+ cnv_stat(value, buf);
+
+ /* Display the maximum stat (modified) */
+ c_put_str(TERM_L_GREEN, buf, 2 + i, 73);
+ }
+
+ /* Normal treatment of "normal" stats */
+ else
+ {
+ /* Assume uppercase stat name */
+ put_str(format("%s%c ", stat_names[i], punctuation), 2 + i, 61);
+
+ /* Obtain the current stat (modified) */
+ cnv_stat(p_ptr->stat_use[i], buf);
+
+ /* Display the current stat (modified) */
+ c_put_str(TERM_L_GREEN, buf, 2 + i, 66);
+ }
+ }
+
+ /* Extra info */
+ display_player_middle();
+
+ /* Display "history" info */
+ if (mode == 1)
+ {
+ put_str("(Character Background)", 15, 25);
+
+ for (i = 0; i < 4; i++)
+ {
+ put_str(history[i], i + 16, 10);
+ }
+ }
+
+ /* Display "various" info */
+ else
+ {
+ put_str("(Miscellaneous Abilities)", 15, 25);
+
+ display_player_various();
+ }
+ }
+
+ /* Special */
+ else
+ {
+ display_player_ben_one(mode - 2);
+ }
+}
+
+/*
+ * Utility function; should probably be in some other file...
+ *
+ * Describe the player's location -- either by dungeon level, town, or in
+ * wilderness with landmark reference.
+ */
+cptr describe_player_location()
+{
+ int i;
+ static char desc[80];
+ int pwx = (p_ptr->wild_mode ? p_ptr->px : p_ptr->wilderness_x);
+ int pwy = (p_ptr->wild_mode ? p_ptr->py : p_ptr->wilderness_y);
+ int feat = wild_map[pwy][pwx].feat;
+
+ if (dungeon_type != DUNGEON_WILDERNESS && dun_level > 0)
+ sprintf(desc, "on level %d of %s", dun_level, d_info[dungeon_type].name + d_name);
+ else if (wf_info[feat].terrain_idx == TERRAIN_TOWN)
+ sprintf(desc, "in the town of %s", wf_info[feat].name + wf_name);
+ else if (wf_info[feat].entrance)
+ sprintf(desc, "near %s", wf_info[feat].name + wf_name);
+ else
+ {
+ /*
+ * The complicated case. Find the nearest known landmark,
+ * and describe our position relative to that. Note that
+ * we may not even have any known landmarks (for instance,
+ * a Lost Soul character just after escaping the Halls of
+ * Mandos).
+ */
+ int landmark = 0, lwx = 0, lwy = 0;
+ int l_dist = -1;
+ int i;
+
+ for (i = 0; i < max_wf_idx; i++)
+ {
+ int wx = wf_info[i].wild_x;
+ int wy = wf_info[i].wild_y;
+ int dist;
+
+ /* Skip if not a landmark */
+ if (!wf_info[i].entrance) continue;
+
+ /* Skip if we haven't seen it */
+ if (!wild_map[wy][wx].known) continue;
+
+ dist = distance(wy, wx, pwy, pwx);
+ if (dist < l_dist || l_dist < 0)
+ {
+ landmark = i;
+ l_dist = dist;
+ lwx = wx;
+ lwy = wy;
+ }
+ }
+
+ if (!landmark)
+ sprintf(desc, "in %s", wf_info[feat].text + wf_text);
+ else if (pwx == lwx && pwy == lwy)
+ /* Paranoia; this should have been caught above */
+ sprintf(desc, "near %s", wf_info[feat].name + wf_name);
+ else
+ {
+ /*
+ * We split the circle into eight equal octants of
+ * size pi/4 radians; the "east" octant, for
+ * instance, is defined as due east plus or minus
+ * pi/8 radians. Now sin(pi/8) ~= 0.3826 ~= 31/81,
+ * so we check |dx|/|dy| and |dy|/|dx| against that
+ * ratio to determine which octant we're in.
+ */
+ int dx = pwx - lwx;
+ int dy = pwy - lwy;
+ cptr ns = (dy > 0 ? "south" : "north");
+ cptr ew = (dx > 0 ? "east" : "west");
+
+ dx = (dx < 0 ? -dx : dx);
+ dy = (dy < 0 ? -dy : dy);
+ if (dy * 81 < dx * 31) ns = "";
+ if (dx * 81 < dy * 31) ew = "";
+
+ sprintf(desc, "in %s %s%s of %s",
+ wf_info[feat].text + wf_text, ns, ew,
+ wf_info[landmark].name + wf_name);
+ }
+ }
+
+ /* strip trailing whitespace */
+ for (i = 0; desc[i]; ++i);
+ while (desc[--i] == ' ')
+ desc[i] = 0;
+
+ return desc;
+}
+
+/*
+ * Helper function or file_character_print_grid
+ *
+ * Figure out if a row on the grid is empty
+ */
+static bool_ file_character_print_grid_check_row(const char *buf)
+{
+ if (strstr(buf + 12, "+")) return TRUE;
+ if (strstr(buf + 12, "*")) return TRUE;
+ if (strstr(buf + 12, "1")) return TRUE;
+ if (strstr(buf + 12, "2")) return TRUE;
+ if (strstr(buf + 12, "3")) return TRUE;
+ if (strstr(buf + 12, "4")) return TRUE;
+ if (strstr(buf + 12, "5")) return TRUE;
+ if (strstr(buf + 12, "6")) return TRUE;
+ if (strstr(buf + 12, "7")) return TRUE;
+ if (strstr(buf + 12, "8")) return TRUE;
+ if (strstr(buf + 12, "9")) return TRUE;
+ return FALSE;
+}
+
+/*
+ * Helper function for file_character
+ *
+ * Prints the big ugly grid
+ */
+static void file_character_print_grid(FILE *fff, bool_ show_gaps, bool_ show_legend)
+{
+ static cptr blank_line = " ";
+ static char buf[1024];
+ byte a;
+ char c;
+ int x, y;
+
+ y = show_legend ? 3 : 4;
+ for (; y < 23; y++)
+ {
+ for (x = 0; x < 40; x++)
+ {
+ (void)(Term_what(x, y, &a, &c));
+ buf[x] = c;
+ }
+
+ buf[x] = '\0';
+ if (strcmp(buf, blank_line) &&
+ (y == 3 || show_gaps || file_character_print_grid_check_row(buf)))
+ fprintf (fff, " %s\n", buf);
+ }
+ for (y = 4; y < 23; y++)
+ {
+ for (x = 40; x < 80; x++)
+ {
+ (void)(Term_what(x, y, &a, &c));
+ buf[x - 40] = c;
+ }
+
+ buf[x] = '\0';
+ if (strcmp(buf, blank_line) &&
+ (show_gaps || file_character_print_grid_check_row(buf)))
+ fprintf (fff, " %s\n", buf);
+ }
+}
+
+/*
+ * Helper function for file_character
+ *
+ * Outputs one item (for Inventory, Equipment, Home, and Mathom-house)
+ */
+void file_character_print_item(FILE *fff, char label, object_type *obj, bool_ full)
+{
+ static char o_name[80];
+ static cptr paren = ")";
+ object_desc(o_name, obj, TRUE, 3);
+ fprintf(fff, "%c%s %s\n", label, paren, o_name);
+
+ if ((artifact_p(obj) || ego_item_p(obj) || obj->tval == TV_RING || obj->tval == TV_AMULET || full) &&
+ (obj->ident & IDENT_MENTAL))
+ {
+ object_out_desc(obj, fff, TRUE, TRUE);
+ }
+}
+
+/*
+ * Helper function for file_character
+ *
+ * Prints out one "store" (for Home and Mathom-house)
+ */
+void file_character_print_store(FILE *fff, wilderness_type_info *place, int store, bool_ full)
+{
+ int i;
+ town_type *town = &town_info[place->entrance];
+ store_type *st_ptr = &town->store[store];
+
+ if (st_ptr->stock_num)
+ {
+ /* Header with name of the town */
+ fprintf(fff, " [%s Inventory - %s]\n\n", st_name + st_info[store].name, wf_name + place->name);
+
+ /* Dump all available items */
+ for (i = 0; i < st_ptr->stock_num; i++)
+ {
+ file_character_print_item(fff, I2A(i%24), &st_ptr->stock[i], full);
+ }
+
+ /* Add an empty line */
+ fprintf(fff, "\n\n");
+ }
+}
+
+/*
+ * Helper function for file_character
+ *
+ * Checks if the store hasn't been added to the list yet, and then adds it if it
+ * was not already there. XXX This is an ugly workaround for the double Gondolin
+ * problem.
+ *
+ * Beware of the ugly pointer gymnastics.
+ */
+bool_ file_character_check_stores(store_type ***store_list, int *store_list_count, wilderness_type_info *place, int store)
+{
+ town_type *town = &town_info[place->entrance];
+ store_type *st_ptr = &town->store[store];
+ store_type **head = *store_list;
+ int i;
+
+ /* check the list for this store */
+ for (i = 0; i < *store_list_count; ++i)
+ {
+ if (*head == st_ptr) return FALSE;
+ ++head;
+ }
+
+ /* make room for the new one in the array */
+ if (*store_list)
+ {
+ head = *store_list;
+ *store_list = C_RNEW(*store_list_count + 1, store_type *);
+ C_COPY(*store_list, head, *store_list_count, store_type *);
+ C_FREE(head, *store_list_count, store_type *);
+ }
+ else
+ {
+ *store_list = RNEW(store_type *);
+ }
+
+ /* update data */
+ (*store_list)[*store_list_count] = st_ptr;
+ ++*store_list_count;
+
+ return TRUE;
+}
+
+/*
+ * Hack -- Dump a character description file
+ *
+ * XXX XXX XXX Allow the "full" flag to dump additional info,
+ * and trigger its usage from various places in the code.
+ */
+errr file_character(cptr name, bool_ full)
+{
+ int i, j, x, y;
+ byte a;
+ char c;
+ int fd = -1;
+ FILE *fff = NULL;
+ char buf[1024];
+ store_type **store_list = NULL;
+ int store_list_count = 0;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Check for existing file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Existing file */
+ if (fd >= 0)
+ {
+ char out_val[160];
+
+ /* Close the file */
+ (void)fd_close(fd);
+
+ /* Build query */
+ (void)sprintf(out_val, "Replace existing file %s? ", buf);
+
+ /* Ask */
+ if (get_check(out_val)) fd = -1;
+ }
+
+ /* Open the non-existing file */
+ if (fd < 0) fff = my_fopen(buf, "w");
+
+ /* Invalid file */
+ if (!fff)
+ {
+ /* Message */
+ msg_format("Character sheet creation failed!");
+ msg_print(NULL);
+
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Begin dump */
+ fprintf(fff, " [%s Character Sheet]\n\n", get_version_string());
+
+
+ /* Display player */
+ display_player(0);
+
+ /* Dump part of the screen */
+ for (y = 2; y < 22; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < 79; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = c;
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* Display history */
+ display_player(1);
+
+ /* Dump part of the screen */
+ for (y = 15; y < 20; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < 79; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = c;
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* List the patches */
+ hook_file = fff;
+ exec_lua("patchs_list()");
+
+ fprintf(fff, "\n\n [Miscellaneous information]\n");
+ if (joke_monsters)
+ fprintf(fff, "\n Joke monsters: ON");
+ else
+ fprintf(fff, "\n Joke monsters: OFF");
+
+ if (p_ptr->maximize)
+ fprintf(fff, "\n Maximize mode: ON");
+ else
+ fprintf(fff, "\n Maximize mode: OFF");
+
+ if (p_ptr->preserve)
+ fprintf(fff, "\n Preserve Mode: ON");
+ else
+ fprintf(fff, "\n Preserve Mode: OFF");
+
+ if (auto_scum)
+ fprintf(fff, "\n Autoscum: ON");
+ else
+ fprintf(fff, "\n Autoscum: OFF");
+
+ if (always_small_level)
+ fprintf(fff, "\n Small Levels: ALWAYS");
+ else if (small_levels)
+ fprintf(fff, "\n Small Levels: ON");
+ else
+ fprintf(fff, "\n Small Levels: OFF");
+
+ if (empty_levels)
+ fprintf(fff, "\n Arena Levels: ON");
+ else
+ fprintf(fff, "\n Arena Levels: OFF");
+
+ if (ironman_rooms)
+ fprintf(fff, "\n Always unusual rooms: ON");
+ else
+ fprintf(fff, "\n Always unusual rooms: OFF");
+
+ fprintf(fff, "\n\n Recall Depth:");
+ for (y = 1; y < max_d_idx; y++)
+ {
+ if (max_dlv[y])
+ fprintf(fff, "\n %s: Level %d (%d')",
+ d_name + d_info[y].name,
+ max_dlv[y], 50 * (max_dlv[y]));
+ }
+ fprintf(fff, "\n");
+
+ if (noscore)
+ fprintf(fff, "\n You have done something illegal.");
+
+ if (PRACE_FLAGS(PR1_EXPERIMENTAL))
+ fprintf(fff, "\n You have done something experimental.");
+
+ if (stupid_monsters)
+ fprintf(fff, "\n Your opponents are behaving stupidly.");
+
+ {
+ char desc[80];
+ cptr mimic;
+
+ monster_race_desc(desc, p_ptr->body_monster, 0);
+ fprintf(fff, "\n Your body %s %s.", (death ? "was" : "is"), desc);
+
+ if (p_ptr->tim_mimic)
+ {
+ call_lua("get_mimic_info", "(d,s)", "s", p_ptr->mimic_form, "name", &mimic);
+ fprintf(fff, "\n You %s disguised as a %s.", (death ? "were" : "are"), mimic);
+ }
+ }
+
+ /* Where we are, if we're alive */
+ if (!death) fprintf(fff, "\n You are currently %s.", describe_player_location());
+
+ /* Monsters slain */
+ {
+ int k;
+ s32b Total = 0;
+
+ for (k = 1; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool_ dead = (r_ptr->max_num == 0);
+ if (dead)
+ {
+ Total++;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+ if (This > 0)
+ {
+ Total += This;
+ }
+ }
+ }
+
+ if (Total < 1)
+ fprintf(fff, "\n You have defeated no enemies yet.");
+ else if (Total == 1)
+ fprintf(fff, "\n You have defeated one enemy.");
+ else
+ fprintf(fff, "\n You have defeated %ld enemies.", (long int) Total);
+ }
+
+ hook_file = fff;
+ process_hooks(HOOK_CHAR_DUMP, "()");
+
+ /* Date */
+ {
+ char buf2[20];
+ u32b days = bst(DAY, turn - (START_DAY * 10));
+
+ strnfmt(buf2, 20, get_day(bst(YEAR, START_DAY * 10) + START_YEAR));
+ fprintf(fff, "\n\n You started your adventure the %s of the %s year of the third age.",
+ get_month_name(bst(DAY, START_DAY * 10), wizard, FALSE), buf2);
+
+ strnfmt(buf2, 20, get_day(bst(YEAR, turn) + START_YEAR));
+ fprintf(fff, "\n %s the %s of the %s year of the third age.",
+ (death ? "You ended your adventure" : "It is currently"),
+ get_month_name(bst(DAY, turn), wizard, FALSE), buf2);
+ fprintf(fff,
+ (death ? "\n Your adventure lasted %ld day%s." : "\n You have been adventuring for %ld day%s."),
+ (long int) days, (days == 1) ? "" : "s");
+ }
+
+ fprintf (fff, "\n\n");
+
+ /* Emit the self-knowledge lines, even though they duplicate the
+ information in the grids (below), because they contain information
+ that's not in the grids (racial abilities, luck, etc.). */
+ if (full)
+ {
+ self_knowledge(fff);
+ fprintf(fff, "\n\n");
+ }
+
+ /* adds and slays */
+ display_player (2);
+ file_character_print_grid(fff, FALSE, TRUE);
+
+ /* sustains and resistances */
+ display_player (3);
+ file_character_print_grid(fff, TRUE, FALSE);
+
+ /* stuff */
+ display_player (4);
+ file_character_print_grid(fff, FALSE, FALSE);
+
+ /* a little bit of stuff */
+ display_player (5);
+ file_character_print_grid(fff, FALSE, FALSE);
+
+ /* Dump corruptions */
+ if (got_corruptions())
+ {
+ fprintf(fff, "\n Corruption list:\n");
+ dump_corruptions(fff, FALSE);
+ }
+
+ /* Dump skills */
+ dump_skills(fff);
+ dump_abilities(fff);
+
+ /* Dump companions. */
+ dump_companions(fff);
+
+ if (p_ptr->companion_killed)
+ {
+ if (p_ptr->companion_killed == 1)
+ fprintf(fff, "\n One of your companion(s) has been killed.");
+ else
+ fprintf(fff, "\n %d of your companions have been killed.", p_ptr->companion_killed);
+ }
+
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if ((fates[i].fate) && (fates[i].know))
+ {
+ fprintf(fff, "\n\n [Fates]\n\n");
+ dump_fates(fff);
+ break;
+ }
+ }
+
+ /* Skip some lines */
+ fprintf(fff, "\n\n");
+
+
+ /* Dump the equipment */
+ text_out_indent = 4;
+ if (equip_cnt)
+ {
+ fprintf(fff, " [Character Equipment]\n\n");
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (!p_ptr->body_parts[i - INVEN_WIELD]) continue;
+
+ file_character_print_item(fff, index_to_label(i), &p_ptr->inventory[i], full);
+ }
+ fprintf(fff, "\n\n");
+ }
+
+ /* Dump the inventory */
+ fprintf(fff, " [Character Inventory]\n\n");
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ file_character_print_item(fff, index_to_label(i), &p_ptr->inventory[i], full);
+ }
+ fprintf(fff, "\n\n");
+
+ /* Print all homes in the different towns */
+ for (j = 0; j < max_wf_idx; j++)
+ {
+ if (wf_info[j].feat == FEAT_TOWN &&
+ file_character_check_stores(&store_list, &store_list_count, &wf_info[j], 7))
+ file_character_print_store(fff, &wf_info[j], 7, full);
+ }
+ store_list = C_FREE(store_list, store_list_count, store_type *);
+ store_list_count = 0;
+
+ /* Print all Mathom-houses in the different towns */
+ for (j = 0; j < max_wf_idx; j++)
+ {
+ if (wf_info[j].feat == FEAT_TOWN &&
+ file_character_check_stores(&store_list, &store_list_count, &wf_info[j], 57))
+ file_character_print_store(fff, &wf_info[j], 57, full);
+ }
+ store_list = C_FREE(store_list, store_list_count, store_type *);
+ store_list_count = 0;
+
+ text_out_indent = 0;
+
+ /* Close it */
+ my_fclose(fff);
+
+
+ /* Message */
+ msg_print("Character sheet creation successful.");
+ msg_print(NULL);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Recursive file perusal.
+ *
+ * Return FALSE on "ESCAPE", otherwise TRUE.
+ *
+ * Process various special text in the input file, including
+ * the "menu" structures used by the "help file" system.
+ *
+ * XXX XXX XXX Consider using a temporary file.
+ *
+ * XXX XXX XXX Allow the user to "save" the current file.
+ */
+
+/*
+ * A structure to hold (some of == XXX) the hyperlink information.
+ * This prevents excessive use of stack.
+ */
+#define MAX_LINKS 1024
+struct hyperlink
+{
+ /* Path buffer */
+ char path[1024];
+
+ /* General buffer */
+ char rbuf[1024];
+
+ /* Hold a string to find */
+ char finder[81];
+
+ /* Hold a string to show */
+ char shower[81];
+
+ /* Describe this thing */
+ char caption[128];
+
+ /* Hypertext info */
+ char link[MAX_LINKS][32], link_key[MAX_LINKS];
+ int link_x[MAX_LINKS], link_y[MAX_LINKS], link_line[MAX_LINKS];
+};
+
+typedef struct hyperlink hyperlink_type;
+
+bool_ show_file(cptr name, cptr what, int line, int mode)
+{
+ int i, k, x;
+
+ byte link_color = TERM_ORANGE, link_color_sel = TERM_YELLOW;
+
+ /* Number of "real" lines passed by */
+ int next = 0;
+
+ /* Number of "real" lines in the file */
+ int size = 0;
+
+ /* Backup value for "line" */
+ int back = 0;
+
+ /* Color of the next line */
+ byte color = TERM_WHITE;
+
+ /* This screen has sub-screens */
+ bool_ menu = FALSE;
+
+ /* Current help file */
+ FILE *fff = NULL;
+
+ /* Find this string (if any) */
+ cptr find = NULL;
+
+ /* Char array type of hyperlink info */
+ hyperlink_type *h_ptr;
+
+ /* Pointer to general buffer in the above */
+ char *buf;
+
+ int cur_link = 0, max_link = 0;
+
+ /* Read size of screen for big-screen stuff -pav- */
+ int wid, hgt;
+
+ /* Allocate hyperlink data */
+ MAKE(h_ptr, hyperlink_type);
+
+ /* Setup buffer pointer */
+ buf = h_ptr->rbuf;
+
+ /* Wipe the links */
+ for (i = 0; i < MAX_LINKS; i++)
+ {
+ h_ptr->link_x[i] = -1;
+ }
+
+ /* Hack XXX XXX XXX */
+ if (what)
+ {
+ /* h_ptr->caption */
+ strcpy(h_ptr->caption, what);
+
+ /* Access the "file" */
+ strcpy(h_ptr->path, name);
+
+ /* Open */
+ fff = my_fopen(h_ptr->path, "r");
+ }
+
+ /* Look in "help" */
+ if (!fff)
+ {
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "Help file '%s'", name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, name);
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+ }
+
+ /* Look in "info" */
+ if (!fff)
+ {
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "Info file '%s'", name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_INFO, name);
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+ }
+
+ /* Look in "file" */
+ if (!fff)
+ {
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "File '%s'", name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_FILE, name);
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+ }
+
+ /* Oops */
+ if (!fff)
+ {
+ /* Message */
+ msg_format("Cannot open '%s'.", name);
+ msg_print(NULL);
+
+ /* Free hyperlink info */
+ KILL(h_ptr, hyperlink_type);
+
+ /* Oops */
+ return (TRUE);
+ }
+
+
+ /* Pre-Parse the file */
+ while (TRUE)
+ {
+ /* Read a line or stop */
+ if (my_fgets(fff, h_ptr->rbuf, 1024)) break;
+
+ /* Get a color */
+ if (prefix(h_ptr->rbuf, "#####"))
+ {
+ buf = &h_ptr->rbuf[6];
+ }
+ else buf = h_ptr->rbuf;
+
+ /* Get the link colors */
+ if (prefix(buf, "|||||"))
+ {
+ link_color = color_char_to_attr(buf[5]);
+ link_color_sel = color_char_to_attr(buf[6]);
+ }
+
+ /* Tag ? */
+ if (prefix(buf, "~~~~~"))
+ {
+ if (line < 0)
+ {
+ int i;
+ char old_c;
+
+ for (i = 5; (buf[i] >= '0') && (buf[i] <= '9'); i++)
+ ;
+ old_c = buf[i];
+ buf[i] = '\0';
+
+ if (atoi(buf + 5) == -line)
+ {
+ line = next + 1;
+ }
+ buf[i] = old_c;
+ }
+ }
+
+ x = 0;
+ while (buf[x])
+ {
+ /* Hyperlink ? */
+ if (prefix(buf + x, "*****"))
+ {
+ int xx = x + 5, stmp, xdeb = x + 5, z;
+ char tmp[20];
+
+ for (z = 0; z < 20; z++) tmp[z] = '\0';
+
+ h_ptr->link_x[max_link] = x;
+ h_ptr->link_y[max_link] = next;
+
+ if (buf[xx] == '/')
+ {
+ xx++;
+ h_ptr->link_key[max_link] = buf[xx];
+ xx++;
+ xdeb += 2;
+ }
+ else
+ {
+ h_ptr->link_key[max_link] = 0;
+ }
+
+ /* Zap the link info */
+ while (buf[xx] != '*')
+ {
+ h_ptr->link[max_link][xx - xdeb] = buf[xx];
+ xx++;
+ }
+ h_ptr->link[max_link][xx - xdeb] = '\0';
+ xx++;
+ stmp = xx;
+ while (buf[xx] != '[')
+ {
+ tmp[xx - stmp] = buf[xx];
+ xx++;
+ }
+ xx++;
+ tmp[xx - stmp] = '\0';
+ h_ptr->link_line[max_link] = -atoi(tmp);
+ max_link++;
+ }
+ x++;
+ }
+
+ /* Count the "real" lines */
+ next++;
+ }
+
+ /* Save the number of "real" lines */
+ size = next;
+
+
+
+ /* Display the file */
+ while (TRUE)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ Term_get_size(&wid, &hgt);
+
+ /* Restart when necessary */
+ if (line >= size) line = 0;
+
+
+ /* Re-open the file if needed */
+ if (next > line)
+ {
+ /* Close it */
+ my_fclose(fff);
+
+ /* Hack -- Re-Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Oops */
+ if (!fff)
+ {
+ /* Free hyperlink info */
+ KILL(h_ptr, hyperlink_type);
+
+ return (FALSE);
+ }
+
+ /* File has been restarted */
+ next = 0;
+ }
+
+ /* Skip lines if needed */
+ for (; next < line; next++)
+ {
+ /* Skip a line */
+ if (my_fgets(fff, buf, 1024)) break;
+ }
+
+
+ /* Dump the next 20 (or more in bigscreen) lines of the file */
+ for (i = 0; i < (hgt - 4); )
+ {
+ int print_x;
+
+ /* Hack -- track the "first" line */
+ if (!i) line = next;
+
+ /* Get a line of the file or stop */
+ if (my_fgets(fff, h_ptr->rbuf, 1024)) break;
+
+ /* Get a color */
+ if (prefix(h_ptr->rbuf, "#####"))
+ {
+ color = color_char_to_attr(h_ptr->rbuf[5]);
+ buf = &h_ptr->rbuf[6];
+ }
+ else buf = h_ptr->rbuf;
+
+ /* Count the "real" lines */
+ next++;
+
+ /* Skip link colors */
+ if (prefix(buf, "|||||")) continue;
+
+ /* Skip tags */
+ if (prefix(buf, "~~~~~"))
+ {
+ i++;
+ continue;
+ }
+
+ /* Hack -- keep searching */
+ if (find && !i && !strstr(buf, find)) continue;
+
+ /* Hack -- stop searching */
+ find = NULL;
+
+ /* Be sure to get a correct cur_link */
+ if (h_ptr->link_y[cur_link] >= line + (hgt - 4))
+ {
+ while ((cur_link > 0) && (h_ptr->link_y[cur_link] >= line + (hgt - 4)))
+ {
+ cur_link--;
+ }
+ }
+ if (h_ptr->link_y[cur_link] < line)
+ {
+ while ((cur_link < max_link) && (h_ptr->link_y[cur_link] < line))
+ {
+ cur_link++;
+ }
+ }
+
+ /* Dump the line */
+ print_x = 0;
+ if (!prefix(buf, "&&&&&"))
+ {
+ x = 0;
+ while (buf[x])
+ {
+ /* Hyperlink ? */
+ if (prefix(buf + x, "*****"))
+ {
+ int xx = x + 5;
+
+ /* Zap the link info */
+ while (buf[xx] != '[')
+ {
+ xx++;
+ }
+ xx++;
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ byte color = link_color;
+
+ if ((h_ptr->link_x[cur_link] == x) && (h_ptr->link_y[cur_link] == line + i))
+ color = link_color_sel;
+
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+
+ Term_putch(print_x, i + 2, color, buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x = xx;
+ }
+ /* Color ? */
+ else if (prefix(buf + x, "[[[[["))
+ {
+ int xx = x + 6;
+
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+ Term_putch(print_x, i + 2, color_char_to_attr(buf[x + 5]), buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x = xx;
+ }
+ /* Remove HTML ? */
+ else if (prefix(buf + x, "{{{{{"))
+ {
+ int xx = x + 6;
+
+ /* Ok remove this section */
+ while (buf[xx] != '}')
+ {
+ xx++;
+ }
+ x = xx;
+ }
+ else
+ {
+ Term_putch(print_x, i + 2, color, buf[x]);
+ print_x++;
+ }
+
+ x++;
+ }
+ }
+ /* Verbatim mode: i.e: acacacac */
+ else
+ {
+ x = 5;
+ while (buf[x])
+ {
+ Term_putch(print_x, i + 2, color_char_to_attr(buf[x]), buf[x + 1]);
+ print_x++;
+ x += 2;
+ }
+ }
+ color = TERM_WHITE;
+
+ /* Hilite "h_ptr->shower" */
+ if (h_ptr->shower[0])
+ {
+ cptr str = buf;
+
+ /* Display matches */
+ while ((str = strstr(str, h_ptr->shower)) != NULL)
+ {
+ int len = strlen(h_ptr->shower);
+
+ /* Display the match */
+ Term_putstr(str - buf, i + 2, len, TERM_YELLOW, h_ptr->shower);
+
+ /* Advance */
+ str += len;
+ }
+ }
+
+ /* Count the printed lines */
+ i++;
+ }
+
+ /* Hack -- failed search */
+ if (find)
+ {
+ bell();
+ line = back;
+ find = NULL;
+ continue;
+ }
+
+
+ /* Show a general "title" */
+ prt(format("[%s, %s, Line %d/%d]", get_version_string(),
+ h_ptr->caption, line, size), 0, 0);
+
+ /* Prompt -- menu screen */
+ if (menu)
+ {
+ /* Wait for it */
+ prt("[Press a Number, or ESC to exit.]", hgt - 1, 0);
+ }
+
+ /* Prompt -- small files */
+ else if (size <= (hgt - 4))
+ {
+ /* Wait for it */
+ prt("[Press ESC to exit.]", hgt - 1, 0);
+ }
+
+ /* Prompt -- large files */
+ else
+ {
+ /* Wait for it */
+ prt("[Press 2, 8, 4, 6, /, =, #, %, backspace, or ESC to exit.]", hgt - 1, 0);
+ }
+
+ /* Get a keypress */
+ k = inkey();
+
+ /* Hack -- return to last screen */
+ if ((k == '?') || (k == 0x7F) || (k == '\010')) break;
+
+ /* Hack -- try showing */
+ if (k == '=')
+ {
+ /* Get "h_ptr->shower" */
+ prt("Show: ", hgt - 1, 0);
+ (void)askfor_aux(h_ptr->shower, 80);
+ }
+
+ /* Hack -- try finding */
+ if (k == '/')
+ {
+ /* Get "h_ptr->finder" */
+ prt("Find: ", hgt - 1, 0);
+ if (askfor_aux(h_ptr->finder, 80))
+ {
+ /* Find it */
+ find = h_ptr->finder;
+ back = line;
+ line = line + 1;
+
+ /* Show it */
+ strcpy(h_ptr->shower, h_ptr->finder);
+ }
+ }
+
+ /* Hack -- go to a specific line */
+ if (k == '#')
+ {
+ char tmp[81];
+ prt("Goto Line: ", hgt - 1, 0);
+ strcpy(tmp, "0");
+ if (askfor_aux(tmp, 80))
+ {
+ line = atoi(tmp);
+ }
+ }
+
+ /* Hack -- go to a specific file */
+ if (k == '%')
+ {
+ char tmp[81];
+ prt("Goto File: ", hgt - 1, 0);
+ strcpy(tmp, "help.hlp");
+ if (askfor_aux(tmp, 80))
+ {
+ if (!show_file(tmp, NULL, 0, mode)) k = ESCAPE;
+ }
+ }
+
+ /* Hack -- Allow backing up */
+ if (k == '-')
+ {
+ line = line - (hgt - 4);
+ if (line < 0) line = 0;
+ }
+
+ if (k == '8')
+ {
+ line--;
+ if (line < 0) line = 0;
+ }
+
+ /* Hack -- Advance a single line */
+ if (k == '2')
+ {
+ line = line + 1;
+ }
+
+ /* Advance one page */
+ if (k == ' ')
+ {
+ line = line + (hgt - 4);
+ }
+
+ /* Advance one link */
+ if ((k == '6') || (k == '\t'))
+ {
+ cur_link++;
+ if (cur_link >= max_link) cur_link = max_link - 1;
+
+ if (h_ptr->link_y[cur_link] < line) line = h_ptr->link_y[cur_link];
+ if (h_ptr->link_y[cur_link] >= line + (hgt - 4)) line = h_ptr->link_y[cur_link] - (hgt - 4);
+ }
+ /* Return one link */
+ if (k == '4')
+ {
+ cur_link--;
+ if (cur_link < 0) cur_link = 0;
+
+ if (h_ptr->link_y[cur_link] < line) line = h_ptr->link_y[cur_link];
+ if (h_ptr->link_y[cur_link] >= line + (hgt - 4)) line = h_ptr->link_y[cur_link] - (hgt - 4);
+ }
+
+ /* Recurse on numbers */
+ if (k == '\r')
+ {
+ if (h_ptr->link_x[cur_link] != -1)
+ {
+ /* Recurse on that file */
+ if (!show_file(h_ptr->link[cur_link], NULL, h_ptr->link_line[cur_link], mode)) k = ESCAPE;
+ }
+ }
+
+ /* Exit on escape */
+ if (k == ESCAPE) break;
+
+ /* No other key ? lets look for a shortcut */
+ for (i = 0; i < max_link; i++)
+ {
+ if (h_ptr->link_key[i] == k)
+ {
+ /* Recurse on that file */
+ if (!show_file(h_ptr->link[i], NULL, h_ptr->link_line[i], mode)) k = ESCAPE;
+ break;
+ }
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Free hyperlink buffers */
+ KILL(h_ptr, hyperlink_type);
+
+ /* Escape */
+ if (k == ESCAPE) return (FALSE);
+
+ /* Normal return */
+ return (TRUE);
+}
+
+bool_ txt_to_html(cptr head, cptr foot, cptr base, cptr ext, bool_ force, bool_ recur)
+{
+ int i, x;
+
+ /* Number of "real" lines passed by */
+ int next = 0;
+
+ char buf_name[80];
+
+ /* Color of the next line */
+ byte color = TERM_WHITE;
+
+ /* Current help file */
+ FILE *fff = NULL;
+
+ /* Current aux file */
+ FILE *aux = NULL;
+
+ /* Current html file */
+ FILE *htm = NULL;
+
+ /* Char array type of hyperlink info */
+ hyperlink_type *h_ptr;
+
+ cptr file_ext;
+ cptr link_prefix;
+ cptr link_suffix;
+
+ /* Pointer to general buffer in the above */
+ char *buf;
+
+ /* Allocate hyperlink data */
+ MAKE(h_ptr, hyperlink_type);
+
+ /* Setup buffer pointer */
+ buf = h_ptr->rbuf;
+
+ /* Wipe the links */
+ for (i = 0; i < MAX_LINKS; i++)
+ {
+ h_ptr->link_x[i] = -1;
+ }
+
+ /* Parse it(yeah lua is neat :) */
+ tome_dofile_anywhere(ANGBAND_DIR_HELP, "def.aux", TRUE);
+
+ /* Ok now get the parameters */
+ file_ext = string_exec_lua("return file_ext");
+ link_prefix = string_exec_lua("return link_prefix");
+ link_suffix = string_exec_lua("return link_suffix");
+
+ sprintf(buf_name, "%s.%s", base, file_ext);
+
+ if ((!force) && file_exist(buf_name)) return FALSE;
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, buf_name);
+
+ /* Open the file */
+ htm = my_fopen(h_ptr->path, "w");
+
+ sprintf(buf_name, "%s.%s", base, ext);
+
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "Help file '%s'", buf_name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, buf_name);
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Oops */
+ if (!fff || !htm)
+ {
+ /* Free hyperlink info */
+ KILL(h_ptr, hyperlink_type);
+
+ my_fclose(fff);
+ my_fclose(htm);
+
+ /* Oops */
+ return (TRUE);
+ }
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, head);
+
+ /* Open the file */
+ aux = my_fopen(h_ptr->path, "r");
+
+ /* Copy the header */
+ if (aux)
+ {
+ while (TRUE)
+ {
+ char *find;
+
+ if (my_fgets(aux, h_ptr->rbuf, 1024)) break;
+ find = strstr(h_ptr->rbuf, "%t");
+ if (find != NULL)
+ {
+ *find = '\0';
+ find += 2;
+ fprintf(htm, "%s", h_ptr->rbuf);
+ fprintf(htm, "%s", base);
+ fprintf(htm, "%s\n", find);
+ }
+ else
+ fprintf(htm, "%s\n", h_ptr->rbuf);
+ }
+ my_fclose(aux);
+ }
+
+ /* Display the file */
+ while (TRUE)
+ {
+ bool_ do_color = FALSE;
+
+ /* Skip a line */
+ if (my_fgets(fff, h_ptr->rbuf, 1024)) break;
+
+ color = TERM_WHITE;
+
+ {
+ int print_x;
+
+ /* Get a color */
+ if (prefix(h_ptr->rbuf, "#####"))
+ {
+ color = color_char_to_attr(h_ptr->rbuf[5]);
+ do_color = TRUE;
+ fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+ buf = &h_ptr->rbuf[6];
+ }
+ else buf = h_ptr->rbuf;
+
+ /* Count the "real" lines */
+ next++;
+
+ /* Skip link colors */
+ if (prefix(buf, "|||||")) continue;
+
+ /* Skip tags */
+ if (prefix(buf, "~~~~~"))
+ {
+ int i;
+
+ for (i = 5; (buf[i] >= '0') && (buf[i] <= '9'); i++)
+ ;
+ buf[i] = '\0';
+ fprintf(htm, "<A NAME=\"%s\"></A>", buf + 5);
+ continue;
+ }
+
+ /* Dump the line */
+ print_x = 0;
+ if (!prefix(buf, "&&&&&"))
+ {
+ x = 0;
+ while (buf[x])
+ {
+ /* Hyperlink ? */
+ if (prefix(buf + x, "*****"))
+ {
+ int xx = x + 5, z = 0;
+ char buff[80];
+ char link_line[80], *s;
+
+ if (buf[xx] == '/') xx += 2;
+
+ /* Zap the link info */
+ while (buf[xx] != '*')
+ {
+ buff[z++] = buf[xx];
+ xx++;
+ }
+ xx++;
+ buff[z] = '\0';
+
+ /* Zap the link info */
+ z = 0;
+ while (buf[xx] != '[')
+ {
+ link_line[z++] = buf[xx];
+ xx++;
+ }
+ xx++;
+ link_line[z] = '\0';
+
+ /* parse it */
+ s = buff;
+ while (*s != '.') s++;
+ *s = '\0';
+ s++;
+ if (recur) txt_to_html(head, foot, buff, s, FALSE, recur);
+
+ if (atoi(link_line)) fprintf(htm, "<A HREF=\"%s%s.%s%s#%d\">", link_prefix, buff, file_ext, link_suffix, atoi(link_line));
+ else fprintf(htm, "<A HREF=\"%s%s.%s%s\">", link_prefix, buff, file_ext, link_suffix);
+
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+ fprintf(htm, "%c", buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x = xx;
+
+ fprintf(htm, "</A>");
+ }
+ /* Color ? */
+ else if (prefix(buf + x, "[[[[["))
+ {
+ int xx = x + 6;
+
+ color = color_char_to_attr(buf[x + 5]);
+ fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+ fprintf(htm, "%c", buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x++;
+ x = xx;
+
+ fprintf(htm, "</FONT>");
+ }
+ /* Hidden HTML tag? */
+ else if (prefix(buf + x, "{{{{{"))
+ {
+ int xx = x + 5;
+
+ /* Ok output the tag inside */
+ while (buf[xx] != '}')
+ {
+ fprintf(htm, "%c", buf[xx]);
+ xx++;
+ }
+ x++;
+ x = xx;
+ }
+ else
+ {
+ fprintf(htm, "%c", buf[x]);
+ print_x++;
+ }
+
+ x++;
+ }
+ }
+ /* Verbatim mode: i.e: acacacac */
+ else
+ {
+ byte old_color;
+
+ x = 5;
+ old_color = color_char_to_attr(buf[x]);
+ fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+ while (buf[x])
+ {
+ color = color_char_to_attr(buf[x]);
+ if (color != old_color)
+ fprintf(htm, "</FONT><FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+
+ fprintf(htm, "%c", buf[x + 1]);
+ print_x++;
+ x += 2;
+ }
+ fprintf(htm, "</FONT>");
+ }
+ }
+ if (do_color)
+ {
+ fprintf(htm, "</FONT>");
+ }
+ fprintf(htm, "\n");
+ }
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, foot);
+
+ /* Open the file */
+ aux = my_fopen(h_ptr->path, "r");
+
+ /* Copy the footer */
+ if (aux)
+ {
+ while (TRUE)
+ {
+ if (my_fgets(aux, h_ptr->rbuf, 1024)) break;
+ fprintf(htm, "%s\n", h_ptr->rbuf);
+ }
+ my_fclose(aux);
+ }
+
+ /* Close the file */
+ my_fclose(htm);
+ my_fclose(fff);
+
+ /* Free hyperlink buffers */
+ KILL(h_ptr, hyperlink_type);
+
+ /* Normal return */
+ return (TRUE);
+}
+
+/* Take an help file screenshot(yes yes I know..) */
+void help_file_screenshot(cptr name)
+{
+ int y, x;
+ int wid, hgt;
+
+ byte a = 0;
+ char c = ' ';
+
+ FILE *htm;
+
+ char buf[1024];
+
+ /* The terms package supports up to 255x255 screen size */
+ char abuf[256];
+ char cbuf[256];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ htm = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!htm) return;
+
+ /* Retrieve current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Dump the screen */
+ for (y = 0; y < hgt; y++)
+ {
+ cmovie_clean_line(y, abuf, cbuf);
+
+ /* Dump each row */
+ fprintf(htm, "&&&&&");
+ for (x = 0; x < wid; x++)
+ {
+ a = abuf[x];
+ c = cbuf[x];
+
+ fprintf(htm, "%c%c", a, c);
+ }
+
+ /* End the row */
+ fprintf(htm, "\n");
+ }
+
+ /* Close it */
+ my_fclose(htm);
+}
+
+/* Take an html screenshot */
+void html_screenshot(cptr name)
+{
+ int y, x;
+ int wid, hgt;
+
+ byte a = 0, oa = TERM_WHITE;
+ char c = ' ';
+
+ FILE *htm;
+
+ char buf[1024];
+
+ /* The terms package supports up to 255x255 screen size */
+ char abuf[256];
+ char cbuf[256];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ htm = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!htm) return;
+
+ /* Retrieve current screen size */
+ Term_get_size(&wid, &hgt);
+
+ fprintf(htm, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
+ "<head>\n");
+ fprintf(htm, "<meta name=\"GENERATOR\" content=\"%s\"/>\n",
+ get_version_string());
+ fprintf(htm, "<title>%s</title>\n", name);
+ fprintf(htm, "</head>\n"
+ "<body>\n"
+ "<pre style=\"color: #ffffff; background-color: #000000; font-family: monospace\">\n");
+ fprintf(htm, "<span style=\"color: #%02X%02X%02X\">\n",
+ angband_color_table[TERM_WHITE][1],
+ angband_color_table[TERM_WHITE][2],
+ angband_color_table[TERM_WHITE][3]);
+
+ /* Dump the screen */
+ for (y = 0; y < hgt; y++)
+ {
+ cmovie_clean_line(y, abuf, cbuf);
+
+ /* Dump each row */
+ for (x = 0; x < wid; x++)
+ {
+ a = color_char_to_attr(abuf[x]);
+ c = cbuf[x];
+
+ if (oa != a)
+ {
+ fprintf(htm, "</span><span style=\"color: #%02X%02X%02X\">", angband_color_table[a][1], angband_color_table[a][2], angband_color_table[a][3]);
+ oa = a;
+ }
+ if (c == '<')
+ fprintf(htm, "&lt;");
+ else if (c == '>')
+ fprintf(htm, "&gt;");
+ else if (c == '&')
+ fprintf(htm, "&amp;");
+ else
+ fprintf(htm, "%c", c);
+ }
+
+ /* End the row */
+ fprintf(htm, "\n");
+ }
+ fprintf(htm, "</span>\n"
+ "</pre>\n"
+ "</body>\n"
+ "</html>\n");
+
+ /* Close it */
+ my_fclose(htm);
+}
+
+
+/*
+ * Peruse the On-Line-Help
+ */
+void do_cmd_help(void)
+{
+ /* Save screen */
+ screen_save();
+
+ /* Peruse the main help file */
+ (void)show_file("help.hlp", NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+}
+
+
+
+
+/*
+ * Process the player name.
+ * Extract a clean "base name".
+ * Build the savefile name if needed.
+ */
+void process_player_base()
+{
+ char temp[128];
+
+ /* Rename the savefile, using the player_base */
+ (void)sprintf(temp, "%s", player_base);
+
+ /* Build the filename */
+ path_build(savefile, 1024, ANGBAND_DIR_SAVE, temp);
+}
+
+void process_player_name(bool_ sf)
+{
+ int i, k = 0;
+ char tmp[50];
+
+ /* Cannot be too long */
+ if (strlen(player_base) > 15)
+ {
+ /* Name too long */
+ quit_fmt("The name '%s' is too long!", player_base);
+ }
+
+ /* Cannot contain "icky" characters */
+ for (i = 0; player_base[i]; i++)
+ {
+ /* No control characters */
+ if (iscntrl(player_base[i]))
+ {
+ /* Illegal characters */
+ quit_fmt("The name '%s' contains control chars!", player_base);
+ }
+ }
+
+
+#ifdef MACINTOSH
+
+ /* Extract "useful" letters */
+ for (i = 0; player_base[i]; i++)
+ {
+ char c = player_base[i];
+
+ /* Convert "dot" to "underscore" */
+ if (c == '@.') c = '_';
+
+ /* Accept all the letters */
+ tmp[k++] = c;
+ }
+
+#else
+
+ /* Extract "useful" letters */
+ for (i = 0; player_base[i]; i++)
+ {
+ char c = player_base[i];
+
+ /* Accept some letters */
+ if (isalpha(c) || isdigit(c)) tmp[k++] = c;
+
+ /* Convert space, dot, and underscore to underscore */
+ else if (strchr("@. _", c)) tmp[k++] = '_';
+ }
+
+#endif
+
+
+#if defined(WINDOWS) || defined(MSDOS)
+
+ /* Hack -- max length */
+ if (k > 8) k = 8;
+
+#endif
+
+ /* Terminate */
+ tmp[k] = '\0';
+ sprintf(player_base, "%s", tmp);
+
+ /* Require a "base" name */
+ if (!player_base[0]) strcpy(player_base, "PLAYER");
+
+
+#ifdef SAVEFILE_MUTABLE
+
+ /* Accept */
+ sf = TRUE;
+
+#endif
+
+ /* Change the savefile name */
+ if (sf)
+ {
+ process_player_base();
+ }
+}
+
+
+/*
+ * Gets a name for the character, reacting to name changes.
+ *
+ * Assumes that "display_player(0)" has just been called
+ *
+ * Perhaps we should NOT ask for a name (at "birth()") on
+ * Unix machines? XXX XXX
+ *
+ * What a horrible name for a global function. XXX XXX XXX
+ */
+void get_name(void)
+{
+ char tmp[32];
+
+ /* Clear last line */
+ clear_from(22);
+
+ /* Prompt and ask */
+ prt("[Enter your player's name above, or hit ESCAPE]", 23, 2);
+
+ /* Ask until happy */
+ while (1)
+ {
+ /* Go to the "name" field */
+ move_cursor(2, 9);
+
+ /* Save the player name */
+ strcpy(tmp, player_name);
+
+ /* Get an input, ignore "Escape" */
+ if (askfor_aux(tmp, 31)) strcpy(player_name, tmp);
+
+ /* Process the player name */
+ process_player_name(FALSE);
+
+ /* All done */
+ break;
+ }
+
+ /* Pad the name (to clear junk) */
+ sprintf(tmp, "%-31.31s", player_name);
+
+ /* Re-Draw the name (in light blue) */
+ c_put_str(TERM_L_BLUE, tmp, 2, 9);
+
+ /* Erase the prompt, etc */
+ clear_from(22);
+}
+
+
+
+/*
+ * Hack -- commit suicide
+ */
+void do_cmd_suicide(void)
+{
+ int i;
+
+ /* Flush input */
+ flush();
+
+ /* Verify Retirement */
+ if (total_winner)
+ {
+ /* Verify */
+ if (!get_check("Do you want to retire? ")) return;
+ }
+
+ /* Verify Suicide */
+ else
+ {
+ /* Verify */
+ if (!get_check("Do you really want to quit? ")) return;
+
+ if (!noscore)
+ {
+ /* Special Verification for suicide */
+ prt("Please verify QUITTING by typing the '@' sign: ", 0, 0);
+ flush();
+ i = inkey();
+ prt("", 0, 0);
+ if (i != '@') return;
+ }
+ }
+
+ /* Stop playing */
+ alive = FALSE;
+
+ /* Kill the player */
+ death = TRUE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Cause of death */
+ (void)strcpy(died_from, "Quitting");
+}
+
+
+ /* HACK - Remove / set the CAVE_VIEW flag, since view_x / view_y
+ * is not saved, and the visible locations are not lighted correctly
+ * when the game is loaded again
+ * Alternatively forget_view() and update_view() can be used
+ */
+void remove_cave_view(bool_ remove)
+{
+ int i;
+ cave_type *c_ptr;
+
+ if (view_n)
+ {
+ /* Clear them all */
+ for (i = 0; i < view_n; i++)
+ {
+ int y = view_y[i];
+ int x = view_x[i];
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ if (remove)
+ c_ptr->info &= ~(CAVE_VIEW);
+ else
+ c_ptr->info |= (CAVE_VIEW);
+ }
+ }
+}
+
+/*
+ * Save the game
+ */
+void do_cmd_save_game(void)
+{
+ remove_cave_view(TRUE);
+
+ /* Save the current level if in a persistent level */
+ save_dungeon();
+
+ /* Autosaves do not disturb */
+ if (!is_autosave)
+ {
+ /* Disturb the player */
+ disturb(1, 0);
+ }
+
+ /* Clear messages */
+ msg_print(NULL);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Message */
+ prt("Saving game...", 0, 0);
+
+ /* Refresh */
+ Term_fresh();
+
+ /* The player is not dead */
+ (void)strcpy(died_from, "(saved)");
+
+ /* Forbid suspend */
+ signals_ignore_tstp();
+
+ /* Save the player */
+ if (save_player())
+ {
+ prt("Saving game... done.", 0, 0);
+ }
+
+ /* Save failed (oops) */
+ else
+ {
+ prt("Saving game... failed!", 0, 0);
+ }
+
+ remove_cave_view(FALSE);
+
+ /* Allow suspend again */
+ signals_handle_tstp();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Note that the player is not dead */
+ (void)strcpy(died_from, "(alive and well)");
+}
+
+/*
+ * Auto-save depending on whether the auto save flag is set.
+ */
+void autosave_checkpoint()
+{
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+}
+
+/*
+ * Hack -- Calculates the total number of points earned -JWT-
+ */
+long total_points(void)
+{
+ s16b max_dl = 0, i, k;
+ long temp, Total = 0;
+ long mult = 20; /* was 100. Divided values by 5 because of an overflow error */
+ long comp_death = (p_ptr->companion_killed * 2 / 5);
+
+ if (!comp_death) comp_death = 1;
+
+ if (p_ptr->preserve) mult -= 1; /* Penalize preserve, maximize modes */
+ if (p_ptr->maximize) mult -= 1;
+ if (auto_scum) mult -= 4;
+ if (stupid_monsters) mult -= 10;
+ if (small_levels) mult += ((always_small_level) ? 4 : 10);
+ if (empty_levels) mult += 2;
+ if (smart_learn) mult += 4;
+ if (smart_cheat) mult += 4;
+
+ if (mult < 2) mult = 2; /* At least 10% of the original score */
+ /* mult is now between 2 and 40, i.e. 10% and 200% */
+
+ for (i = 0; i < max_d_idx; i++)
+ if (max_dlv[i] > max_dl)
+ max_dl = max_dlv[i];
+
+ temp = p_ptr->lev * p_ptr->lev * p_ptr->lev * p_ptr->lev + (100 * max_dl);
+
+ temp += p_ptr->max_exp / 5;
+
+ temp = (temp * mult / 20);
+
+ /* Gold increases score */
+ temp += p_ptr->au / 5;
+
+ /* Completing quest increase score */
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (quest[i].status >= QUEST_STATUS_COMPLETED)
+ {
+ temp += 2000;
+ temp += quest[i].level * 100;
+ }
+ }
+
+ /* Death of a companion is BAD */
+ temp /= comp_death;
+
+ /* The know objects increase the score */
+ /* Scan the object kinds */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Hack -- skip artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /* List known flavored objects */
+ if (k_ptr->flavor && k_ptr->aware)
+ {
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Create fake object */
+ object_prep(i_ptr, k);
+
+ temp += object_value_real(i_ptr);
+ }
+ }
+
+ for (k = 1; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool_ dead = (r_ptr->max_num == 0);
+
+ if (dead)
+ {
+ /* Uniques are supposed to be harder */
+ Total += 50;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+
+ if (This > 0)
+ {
+ Total += This;
+ }
+ }
+ }
+ temp += Total * 50;
+
+ temp += total_bounties * 100;
+
+ if (total_winner) temp += 1000000;
+
+
+
+ return (temp);
+}
+
+
+
+/*
+ * Centers a string within a 31 character string -JWT-
+ */
+static void center_string(char *buf, cptr str)
+{
+ int i, j;
+
+ /* Total length */
+ i = strlen(str);
+
+ /* Necessary border */
+ j = 15 - i / 2;
+
+ /* Mega-Hack */
+ (void)sprintf(buf, "%*s%s%*s", j, "", str, 31 - i - j, "");
+}
+
+
+/*
+ * Redefinable "print_tombstone" action
+ */
+bool_ (*tombstone_aux)(void) = NULL;
+
+
+/*
+ * Display a "tomb-stone"
+ */
+static void print_tomb(void)
+{
+ bool_ done = FALSE;
+
+ /* Do we use a special tombstone ? */
+ if (tombstone_aux)
+ {
+ /* Use tombstone hook */
+ done = (*tombstone_aux)();
+ }
+
+ /* Print the text-tombstone */
+ if (!done)
+ {
+ cptr p;
+
+ char tmp[160];
+
+ char buf[1024];
+ char dummy[80];
+
+ FILE *fp;
+
+ time_t ct = time((time_t)0);
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, "dead.txt");
+
+ /* Open the News file */
+ fp = my_fopen(buf, "r");
+
+ /* Dump */
+ if (fp)
+ {
+ int i = 0;
+
+ /* Dump the file to the screen */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Display and advance */
+ display_message(0, i++, strlen(buf), TERM_WHITE, buf);
+ }
+
+ /* Close */
+ my_fclose(fp);
+ }
+
+
+ /* King or Queen */
+ if (total_winner || (p_ptr->lev > PY_MAX_LEVEL))
+ {
+ p = "Magnificent";
+ }
+
+ /* Normal */
+ else
+ {
+ p = cp_ptr->titles[(p_ptr->lev - 1) / 5] + c_text;
+ }
+
+ center_string(buf, player_name);
+ put_str(buf, 6, 11);
+
+ center_string(buf, "the");
+ put_str(buf, 7, 11);
+
+ center_string(buf, p);
+ put_str(buf, 8, 11);
+
+
+ center_string(buf, spp_ptr->title + c_name);
+ put_str(buf, 10, 11);
+
+ (void)sprintf(tmp, "Level: %d", (int)p_ptr->lev);
+ center_string(buf, tmp);
+ put_str(buf, 11, 11);
+
+ (void)sprintf(tmp, "Exp: %ld", (long)p_ptr->exp);
+ center_string(buf, tmp);
+ put_str(buf, 12, 11);
+
+ (void)sprintf(tmp, "AU: %ld", (long)p_ptr->au);
+ center_string(buf, tmp);
+ put_str(buf, 13, 11);
+
+ (void)sprintf(tmp, "Killed on Level %d", dun_level);
+ center_string(buf, tmp);
+ put_str(buf, 14, 11);
+
+
+ if (strlen(died_from) > 24)
+ {
+ strncpy(dummy, died_from, 24);
+ dummy[24] = '\0';
+ (void)sprintf(tmp, "by %s.", dummy);
+ }
+ else
+ (void)sprintf(tmp, "by %s.", died_from);
+
+ center_string(buf, tmp);
+ put_str(buf, 15, 11);
+
+
+ (void)sprintf(tmp, "%-.24s", ctime(&ct));
+ center_string(buf, tmp);
+ put_str(buf, 17, 11);
+ }
+}
+
+
+/*
+ * Display some character info
+ */
+static void show_info(void)
+{
+ int i, j, k;
+ object_type *o_ptr;
+ store_type *st_ptr;
+
+ /* Hack -- Know everything in the inven/equip */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Aware and Known */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ }
+
+ for (i = 1; i < max_towns; i++)
+ {
+ st_ptr = &town_info[i].store[7];
+
+ /* Hack -- Know everything in the home */
+ for (j = 0; j < st_ptr->stock_num; j++)
+ {
+ o_ptr = &st_ptr->stock[j];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Aware and Known */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ }
+ }
+
+ /* Hack -- Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Flush all input keys */
+ flush();
+
+ /* Flush messages */
+ msg_print(NULL);
+
+
+ /* Describe options */
+ prt("You may now dump a character record to one or more files.", 21, 0);
+ prt("Then, hit RETURN to see the character, or ESC to abort.", 22, 0);
+
+ /* Dump character records as requested */
+ while (TRUE)
+ {
+ char out_val[160];
+
+ /* Prompt */
+ put_str("Filename(you can post it to http://angband.oook.cz/): ", 23, 0);
+
+ /* Default */
+ strcpy(out_val, "");
+
+ /* Ask for filename (or abort) */
+ if (!askfor_aux(out_val, 60)) return;
+
+ /* Return means "show on screen" */
+ if (!out_val[0]) break;
+
+ /* Save screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Dump a character file */
+ (void)file_character(out_val, TRUE);
+
+ /* Load screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+
+ /* Display player */
+ display_player(0);
+
+ /* Prompt for p_ptr->inventory */
+ prt("Hit any key to see more information (ESC to abort): ", 23, 0);
+
+ /* Allow abort at this point */
+ if (inkey() == ESCAPE) return;
+
+
+ /* Show equipment and inventory */
+
+ /* Equipment -- if any */
+ if (equip_cnt)
+ {
+ Term_clear();
+ item_tester_full = TRUE;
+ show_equip();
+ prt("You are using: -more-", 0, 0);
+ if (inkey() == ESCAPE) return;
+ }
+
+ /* Inventory -- if any */
+ if (inven_cnt)
+ {
+ Term_clear();
+ item_tester_full = TRUE;
+ show_inven();
+ prt("You are carrying: -more-", 0, 0);
+ if (inkey() == ESCAPE) return;
+ }
+
+ /* Homes in the different towns */
+ for (k = 1; k < max_towns; k++)
+ {
+ st_ptr = &town_info[k].store[7];
+
+ /* Home -- if anything there */
+ if (st_ptr->stock_num)
+ {
+ /* Display contents of the home */
+ for (k = 0, i = 0; i < st_ptr->stock_num; k++)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Show 12 items */
+ for (j = 0; (j < 12) && (i < st_ptr->stock_num); j++, i++)
+ {
+ char o_name[80];
+ char tmp_val[80];
+
+ /* Acquire item */
+ o_ptr = &st_ptr->stock[i];
+
+ /* Print header, clear line */
+ sprintf(tmp_val, "%c) ", I2A(j));
+ prt(tmp_val, j + 2, 4);
+
+ /* Display object description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+ c_put_str(tval_to_attr[o_ptr->tval], o_name, j + 2, 7);
+ }
+
+ /* h_ptr->caption */
+ prt(format("Your home contains (page %d): -more-", k + 1), 0, 0);
+
+ /* Wait for it */
+ if (inkey() == ESCAPE) return;
+ }
+ }
+ }
+}
+
+
+
+
+
+/*
+ * Semi-Portable High Score List Entry (128 bytes) -- BEN
+ *
+ * All fields listed below are null terminated ascii strings.
+ *
+ * In addition, the "number" fields are right justified, and
+ * space padded, to the full available length (minus the "null").
+ *
+ * Note that "string comparisons" are thus valid on "pts".
+ */
+
+typedef struct high_score high_score;
+
+struct high_score
+{
+ char what[8]; /* Version info (string) */
+
+ char pts[10]; /* Total Score (number) */
+
+ char gold[10]; /* Total Gold (number) */
+
+ char turns[10]; /* Turns Taken (number) */
+
+ char day[10]; /* Time stamp (string) */
+
+ char who[16]; /* Player Name (string) */
+
+ char uid[8]; /* Player UID (number) */
+
+ char sex[2]; /* Player Sex (string) */
+ char p_r[3]; /* Player Race (number) */
+ char p_s[3]; /* Player Subrace (number) */
+ char p_c[3]; /* Player Class (number) */
+ char p_cs[3]; /* Player Class spec (number) */
+
+ char cur_lev[4]; /* Current Player Level (number) */
+ char cur_dun[4]; /* Current Dungeon Level (number) */
+ char max_lev[4]; /* Max Player Level (number) */
+ char max_dun[4]; /* Max Dungeon Level (number) */
+
+ char arena_number[4]; /* Arena level attained -KMW- */
+ char inside_arena[4]; /* Did the player die in the arena? */
+ char inside_quest[4]; /* Did the player die in a quest? */
+ char exit_bldg[4]; /* Can the player exit arena? Goal obtained? -KMW- */
+
+ char how[32]; /* Method of death (string) */
+};
+
+
+
+/*
+ * Seek score 'i' in the highscore file
+ */
+static int highscore_seek(int i)
+{
+ /* Seek for the requested record */
+ return (fd_seek(highscore_fd, (huge)(i) * sizeof(high_score)));
+}
+
+
+/*
+ * Read one score from the highscore file
+ */
+static errr highscore_read(high_score *score)
+{
+ /* Read the record, note failure */
+ return (fd_read(highscore_fd, (char*)(score), sizeof(high_score)));
+}
+
+
+/*
+ * Write one score to the highscore file
+ */
+static int highscore_write(high_score *score)
+{
+ /* Write the record, note failure */
+ return (fd_write(highscore_fd, (char*)(score), sizeof(high_score)));
+}
+
+
+
+
+/*
+ * Just determine where a new score *would* be placed
+ * Return the location (0 is best) or -1 on failure
+ */
+static int highscore_where(high_score *score)
+{
+ int i;
+
+ high_score the_score;
+
+ /* Paranoia -- it may not have opened */
+ if (highscore_fd < 0) return ( -1);
+
+ /* Go to the start of the highscore file */
+ if (highscore_seek(0)) return ( -1);
+
+ /* Read until we get to a higher score */
+ for (i = 0; i < MAX_HISCORES; i++)
+ {
+ if (highscore_read(&the_score)) return (i);
+ if (strcmp(the_score.pts, score->pts) < 0) return (i);
+ }
+
+ /* The "last" entry is always usable */
+ return (MAX_HISCORES - 1);
+}
+
+
+/*
+ * Actually place an entry into the high score file
+ * Return the location (0 is best) or -1 on "failure"
+ */
+static int highscore_add(high_score *score)
+{
+ int i, slot;
+ bool_ done = FALSE;
+
+ high_score the_score, tmpscore;
+
+
+ /* Paranoia -- it may not have opened */
+ if (highscore_fd < 0) return ( -1);
+
+ /* Determine where the score should go */
+ slot = highscore_where(score);
+
+ /* Hack -- Not on the list */
+ if (slot < 0) return ( -1);
+
+ /* Hack -- prepare to dump the new score */
+ the_score = (*score);
+
+ /* Slide all the scores down one */
+ for (i = slot; !done && (i < MAX_HISCORES); i++)
+ {
+ /* Read the old guy, note errors */
+ if (highscore_seek(i)) return ( -1);
+ if (highscore_read(&tmpscore)) done = TRUE;
+
+ /* Back up and dump the score we were holding */
+ if (highscore_seek(i)) return ( -1);
+ if (highscore_write(&the_score)) return ( -1);
+
+ /* Hack -- Save the old score, for the next pass */
+ the_score = tmpscore;
+ }
+
+ /* Return location used */
+ return (slot);
+}
+
+
+
+/*
+ * Display the scores in a given range.
+ * Assumes the high score list is already open.
+ * Only five entries per line, too much info.
+ *
+ * Mega-Hack -- allow "fake" entry at the given position.
+ */
+static void display_scores_aux(int from, int to, int note, high_score *score)
+{
+ int i, j, k, n, place;
+ byte attr;
+ char out_val[256];
+ char tmp_val[160];
+ high_score the_score;
+
+
+ /* Paranoia -- it may not have opened */
+ if (highscore_fd < 0) return;
+
+
+ /* Assume we will show the first 10 */
+ if (from < 0) from = 0;
+ if (to < 0) to = 10;
+ if (to > MAX_HISCORES) to = MAX_HISCORES;
+
+
+ /* Seek to the beginning */
+ if (highscore_seek(0)) return;
+
+ /* Hack -- Count the high scores */
+ for (i = 0; i < MAX_HISCORES; i++)
+ {
+ if (highscore_read(&the_score)) break;
+ }
+
+ /* Hack -- allow "fake" entry to be last */
+ if ((note == i) && score) i++;
+
+ /* Forget about the last entries */
+ if (i > to) i = to;
+
+
+ /* Show 5 per page, until "done" */
+ for (k = from, place = k + 1; k < i; k += 5)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Title */
+ put_str(format(" %s Hall of Fame", game_module), 0, 0);
+
+ /* Indicate non-top scores */
+ if (k > 0)
+ {
+ sprintf(tmp_val, "(from position %d)", k + 1);
+ put_str(tmp_val, 0, 40);
+ }
+
+ /* Dump 5 entries */
+ for (j = k, n = 0; j < i && n < 5; place++, j++, n++)
+ {
+ int pcs, pr, ps, pc, clev, mlev, cdun, mdun;
+
+ cptr user, gold, when, aged;
+
+ int in_arena, in_quest;
+
+ /* Hack -- indicate death in yellow */
+ attr = (j == note) ? TERM_YELLOW : TERM_WHITE;
+
+
+ /* Mega-Hack -- insert a "fake" record */
+ if ((note == j) && score)
+ {
+ the_score = (*score);
+ attr = TERM_L_GREEN;
+ score = NULL;
+ note = -1;
+ j--;
+ }
+
+ /* Read a normal record */
+ else
+ {
+ /* Read the proper record */
+ if (highscore_seek(j)) break;
+ if (highscore_read(&the_score)) break;
+ }
+
+ /* Extract the race/class */
+ pr = atoi(the_score.p_r);
+ ps = atoi(the_score.p_s);
+ pc = atoi(the_score.p_c);
+ pcs = atoi(the_score.p_cs);
+
+ /* Extract the level info */
+ clev = atoi(the_score.cur_lev);
+ mlev = atoi(the_score.max_lev);
+ cdun = atoi(the_score.cur_dun);
+ mdun = atoi(the_score.max_dun);
+
+ in_arena = atoi(the_score.inside_arena);
+ in_quest = atoi(the_score.inside_quest);
+
+ /* Hack -- extract the gold and such */
+ for (user = the_score.uid; isspace(*user); user++) /* loop */;
+ for (when = the_score.day; isspace(*when); when++) /* loop */;
+ for (gold = the_score.gold; isspace(*gold); gold++) /* loop */;
+ for (aged = the_score.turns; isspace(*aged); aged++) /* loop */;
+
+ /* Dump some info */
+ sprintf(out_val, "%3d.%9s %s the %s %s, Level %d",
+ place, the_score.pts, the_score.who,
+ get_player_race_name(pr, ps), class_info[pc].spec[pcs].title + c_name,
+ clev);
+
+ /* Append a "maximum level" */
+ if (mlev > clev) strcat(out_val, format(" (Max %d)", mlev));
+
+ /* Dump the first line */
+ c_put_str(attr, out_val, n*4 + 2, 0);
+
+ /* Another line of info */
+ if (in_arena)
+ {
+ sprintf(out_val, " Killed by %s in the Arena",
+ the_score.how);
+ }
+ else if (in_quest)
+ {
+ sprintf(out_val, " Killed by %s while questing",
+ the_score.how);
+ }
+ /* Hack -- some people die in the town */
+ else if (!cdun)
+ {
+ sprintf(out_val, " Killed by %s in the Town",
+ the_score.how);
+ }
+ else
+ {
+ sprintf(out_val, " Killed by %s on %s %d",
+ the_score.how, "Dungeon Level", cdun);
+ }
+
+ /* Append a "maximum level" */
+ if (mdun > cdun) strcat(out_val, format(" (Max %d)", mdun));
+
+ /* Dump the info */
+ c_put_str(attr, out_val, n*4 + 3, 0);
+
+ /* And still another line of info */
+ sprintf(out_val,
+ " (User %s, Date %s, Gold %s, Turn %s).",
+ user, when, gold, aged);
+ c_put_str(attr, out_val, n*4 + 4, 0);
+ }
+
+
+ /* Wait for response */
+ prt("[Press ESC to quit, any other key to continue.]", 23, 17);
+ j = inkey();
+ prt("", 23, 0);
+
+ /* Hack -- notice Escape */
+ if (j == ESCAPE) break;
+ }
+}
+
+
+/*
+ * Hack -- Display the scores in a given range and quit.
+ *
+ * This function is only called from "main.c" when the user asks
+ * to see the "high scores".
+ */
+void display_scores(int from, int to)
+{
+ char buf[1024];
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Open the binary high score file, for reading */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ /* Paranoia -- No score file */
+ if (highscore_fd < 0) quit("Score file unavailable.");
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Display the scores */
+ display_scores_aux(from, to, -1, NULL);
+
+ /* Shut the high score file */
+ (void)fd_close(highscore_fd);
+
+ /* Forget the high score fd */
+ highscore_fd = -1;
+
+ /* Quit */
+ quit(NULL);
+}
+
+
+/*
+ * show_highclass - selectively list highscores based on class
+ * -KMW-
+ */
+void show_highclass(int building)
+{
+
+ register int i = 0, j, m = 0;
+ int pr, pc, clev, al;
+ high_score the_score;
+ char buf[1024], out_val[256];
+
+ switch (building)
+ {
+ case 1:
+ prt(" Busts of Greatest Kings", 5, 0);
+ break;
+ case 2:
+ prt(" Plaque - Greatest Arena Champions", 5, 0);
+ break;
+ case 10:
+ prt(" Plaque - Greatest Fighters", 5, 0);
+ break;
+ case 11:
+ prt(" Spires of the Greatest Magic-Users", 5, 0);
+ break;
+ case 12:
+ prt(" Busts of Greatest Priests", 5, 0);
+ break;
+ case 13:
+ prt(" Wall Inscriptions - Greatest Thieves", 5, 0);
+ break;
+ case 14:
+ prt(" Plaque - Greatest Rangers", 5, 0);
+ break;
+ case 15:
+ prt(" Plaque - Greatest Paladins", 5, 0);
+ break;
+ case 16:
+ prt(" Spires of the Greatest Illusionists", 5, 0);
+ break;
+ default:
+ bell();
+ break;
+ }
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Open file */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return;
+ }
+
+ if (highscore_seek(0)) return;
+
+ for (i = 0; i < MAX_HISCORES; i++)
+ if (highscore_read(&the_score)) break;
+
+ m = 0;
+ j = 0;
+ clev = 0;
+
+ while ((m < 9) || (j < MAX_HISCORES))
+ {
+ if (highscore_seek(j)) break;
+ if (highscore_read(&the_score)) break;
+ pr = atoi(the_score.p_r);
+ pc = atoi(the_score.p_c);
+ clev = atoi(the_score.cur_lev);
+ al = atoi(the_score.arena_number);
+ if (((pc == (building - 10)) && (building != 1) && (building != 2)) ||
+ ((building == 1) && (clev >= PY_MAX_LEVEL)) ||
+ ((building == 2) && (al > MAX_ARENA_MONS)))
+ {
+ sprintf(out_val, "%3d) %s the %s (Level %2d)",
+ (m + 1), the_score.who, rp_name + race_info[pr].title, clev);
+ prt(out_val, (m + 7), 0);
+ m++;
+ }
+ j++;
+ }
+
+ /* Now, list the active player if they qualify */
+ if ((building == 1) && (p_ptr->lev >= PY_MAX_LEVEL))
+ {
+ sprintf(out_val, "You) %s the %s (Level %2d)",
+ player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+ else if ((building == 2) && (p_ptr->arena_number > MAX_ARENA_MONS))
+ {
+ sprintf(out_val, "You) %s the %s (Level %2d)",
+ player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+ else if ((building != 1) && (building != 2))
+ {
+ if ((p_ptr->lev > clev) && (p_ptr->pclass == (building - 10)))
+ {
+ sprintf(out_val, "You) %s the %s (Level %2d)",
+ player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+ }
+
+ (void)fd_close(highscore_fd);
+ highscore_fd = -1;
+ msg_print("Hit any key to continue");
+ msg_print(NULL);
+ for (j = 5; j < 18; j++)
+ prt("", j, 0);
+}
+
+
+/*
+ * Race Legends
+ * -KMW-
+ */
+void race_score(int race_num)
+{
+ register int i = 0, j, m = 0;
+ int pr, clev, lastlev;
+ high_score the_score;
+ char buf[1024], out_val[256], tmp_str[80];
+
+ lastlev = 0;
+
+ /* rr9: TODO - pluralize the race */
+ sprintf(tmp_str, "The Greatest of all the %s", rp_name + race_info[race_num].title);
+ prt(tmp_str, 5, 3);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Open the highscore file */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return;
+ }
+
+ if (highscore_seek(0)) return;
+
+ for (i = 0; i < MAX_HISCORES; i++)
+ {
+ if (highscore_read(&the_score)) break;
+ }
+
+ m = 0;
+ j = 0;
+
+ while ((m < 10) && (j < i))
+ {
+ if (highscore_seek(j)) break;
+ if (highscore_read(&the_score)) break;
+ pr = atoi(the_score.p_r);
+ clev = atoi(the_score.cur_lev);
+ if (pr == race_num)
+ {
+ sprintf(out_val, "%3d) %s the %s (Level %3d)",
+ (m + 1), the_score.who,
+ rp_name + race_info[pr].title, clev);
+ prt(out_val, (m + 7), 0);
+ m++;
+ lastlev = clev;
+ }
+ j++;
+ }
+
+ /* add player if qualified */
+ if ((p_ptr->prace == race_num) && (p_ptr->lev >= lastlev))
+ {
+ sprintf(out_val, "You) %s the %s (Level %3d)",
+ player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+
+ (void)fd_close(highscore_fd);
+ highscore_fd = -1;
+}
+
+
+/*
+ * Race Legends
+ * -KMW-
+ */
+void race_legends(void)
+{
+ int i, j;
+
+ for (i = 0; i < max_rp_idx; i++)
+ {
+ race_score(i);
+ msg_print("Hit any key to continue");
+ msg_print(NULL);
+ for (j = 5; j < 19; j++)
+ prt("", j, 0);
+ }
+}
+
+
+
+
+/*
+ * Enters a players name on a hi-score table, if "legal", and in any
+ * case, displays some relevant portion of the high score list.
+ *
+ * Assumes "signals_ignore_tstp()" has been called.
+ */
+static errr top_twenty(void)
+{
+ int j;
+
+ high_score the_score;
+
+ time_t ct = time((time_t*)0);
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* No score file */
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return (0);
+ }
+
+#ifndef SCORE_WIZARDS
+ /* Wizard-mode pre-empts scoring */
+ if (noscore & 0x000F)
+ {
+ msg_print("Score not registered for wizards.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+#endif
+
+#ifndef SCORE_BORGS
+ /* Borg-mode pre-empts scoring */
+ if (noscore & 0x00F0)
+ {
+ msg_print("Score not registered for borgs.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+#endif
+
+#ifndef SCORE_CHEATERS
+ /* Cheaters are not scored */
+ if (noscore & 0xFF00)
+ {
+ msg_print("Score not registered for cheaters.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+#endif
+
+ /* Interupted */
+ if (!total_winner && streq(died_from, "Interrupting"))
+ {
+ msg_print("Score not registered due to interruption.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+
+ /* Quitter */
+ if (!total_winner && streq(died_from, "Quitting"))
+ {
+ msg_print("Score not registered due to quitting.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+
+
+ /* Clear the record */
+ WIPE(&the_score, high_score);
+
+ /* Save the version */
+ sprintf(the_score.what, "%ld.%ld.%ld",
+ (long int) VERSION_MAJOR, (long int) VERSION_MINOR, (long int) VERSION_PATCH);
+
+ /* Calculate and save the points */
+ sprintf(the_score.pts, "%9lu", (long)total_points());
+ the_score.pts[9] = '\0';
+
+ /* Save the current gold */
+ sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
+ the_score.gold[9] = '\0';
+
+ /* Save the current turn */
+ sprintf(the_score.turns, "%9lu", (long)turn - (START_DAY * 10L));
+ the_score.turns[9] = '\0';
+
+#ifdef HIGHSCORE_DATE_HACK
+ /* Save the date in a hacked up form (9 chars) */
+ sprintf(the_score.day, "%-.6s %-.2s", ctime(&ct) + 4, ctime(&ct) + 22);
+#else
+ /* Save the date in standard form (8 chars) */
+ strftime(the_score.day, 9, "%m/%d/%y", localtime(&ct));
+#endif
+
+ /* Save the player name (15 chars) */
+ sprintf(the_score.who, "%-.15s", player_name);
+
+ /* Save the player info XXX XXX XXX */
+ sprintf(the_score.uid, "%7u", player_uid);
+ sprintf(the_score.sex, "%c", (p_ptr->psex ? 'm' : 'f'));
+ sprintf(the_score.p_r, "%2d", p_ptr->prace);
+ sprintf(the_score.p_s, "%2d", p_ptr->pracem);
+ sprintf(the_score.p_c, "%2d", p_ptr->pclass);
+ sprintf(the_score.p_cs, "%2d", p_ptr->pspec);
+
+ /* Save the level and such */
+ sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
+ sprintf(the_score.cur_dun, "%3d", dun_level);
+ sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
+ sprintf(the_score.max_dun, "%3d", max_dlv[dungeon_type]);
+
+ sprintf(the_score.arena_number, "%3d", p_ptr->arena_number); /* -KMW- */
+ sprintf(the_score.inside_arena, "%3d", p_ptr->inside_arena);
+ sprintf(the_score.inside_quest, "%3d", p_ptr->inside_quest);
+ sprintf(the_score.exit_bldg, "%3d", p_ptr->exit_bldg); /* -KMW- */
+
+ /* Save the cause of death (31 chars) */
+ sprintf(the_score.how, "%-.31s", died_from);
+
+
+ /* Lock (for writing) the highscore file, or fail */
+ if (fd_lock(highscore_fd, F_WRLCK)) return (1);
+
+ /* Add a new entry to the score list, see where it went */
+ j = highscore_add(&the_score);
+
+ /* Unlock the highscore file, or fail */
+ if (fd_lock(highscore_fd, F_UNLCK)) return (1);
+
+
+ /* Hack -- Display the top fifteen scores */
+ if (j < 10)
+ {
+ display_scores_aux(0, 15, j, NULL);
+ }
+
+ /* Display the scores surrounding the player */
+ else
+ {
+ display_scores_aux(0, 5, j, NULL);
+ display_scores_aux(j - 2, j + 7, j, NULL);
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Predict the players location, and display it.
+ */
+errr predict_score(void)
+{
+ int j;
+
+ high_score the_score;
+
+
+ /* No score file */
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return (0);
+ }
+
+
+ /* Save the version */
+ sprintf(the_score.what, "%ld.%ld.%ld",
+ (long int) VERSION_MAJOR, (long int) VERSION_MINOR, (long int) VERSION_PATCH);
+
+ /* Calculate and save the points */
+ sprintf(the_score.pts, "%9lu", (long)total_points());
+ the_score.pts[9] = '\0';
+
+ /* Save the current gold */
+ sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
+ the_score.gold[9] = '\0';
+
+ /* Save the current turn */
+ sprintf(the_score.turns, "%9lu", (long)turn - (START_DAY * 10L));
+ the_score.turns[9] = '\0';
+
+ /* Hack -- no time needed */
+ strcpy(the_score.day, "TODAY");
+
+ /* Save the player name (15 chars) */
+ sprintf(the_score.who, "%-.15s", player_name);
+
+ /* Save the player info XXX XXX XXX */
+ sprintf(the_score.uid, "%7u", player_uid);
+ sprintf(the_score.sex, "%c", (p_ptr->psex ? 'm' : 'f'));
+ sprintf(the_score.p_r, "%2d", p_ptr->prace);
+ sprintf(the_score.p_s, "%2d", p_ptr->pracem);
+ sprintf(the_score.p_c, "%2d", p_ptr->pclass);
+ sprintf(the_score.p_cs, "%2d", p_ptr->pspec);
+
+ /* Save the level and such */
+ sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
+ sprintf(the_score.cur_dun, "%3d", dun_level);
+ sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
+ sprintf(the_score.max_dun, "%3d", max_dlv[dungeon_type]);
+
+ sprintf(the_score.arena_number, "%3d", p_ptr->arena_number); /* -KMW- */
+ sprintf(the_score.inside_arena, "%3d", p_ptr->inside_arena);
+ sprintf(the_score.inside_quest, "%3d", p_ptr->inside_quest);
+ sprintf(the_score.exit_bldg, "%3d", p_ptr->exit_bldg); /* -KMW- */
+
+ /* Hack -- no cause of death */
+ strcpy(the_score.how, "nobody (yet!)");
+
+
+ /* See where the entry would be placed */
+ j = highscore_where(&the_score);
+
+
+ /* Hack -- Display the top fifteen scores */
+ if (j < 10)
+ {
+ display_scores_aux(0, 15, j, &the_score);
+ }
+
+ /* Display some "useful" scores */
+ else
+ {
+ display_scores_aux(0, 5, -1, NULL);
+ display_scores_aux(j - 2, j + 7, j, &the_score);
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Change the player into a King! -RAK-
+ */
+static void kingly(void)
+{
+ /* Hack -- retire in town */
+ dun_level = 0;
+
+ /* Fake death */
+ (void)strcpy(died_from, "Ripe Old Age");
+
+ /* Restore the experience */
+ p_ptr->exp = p_ptr->max_exp;
+
+ /* Restore the level */
+ p_ptr->lev = p_ptr->max_plv;
+
+ /* Hack -- Instant Gold */
+ p_ptr->au += 10000000L;
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Would like to see something more Tolkienian here... */
+
+ /* Display a crown */
+ put_str("#", 1, 34);
+ put_str("#####", 2, 32);
+ put_str("#", 3, 34);
+ put_str(",,, $$$ ,,,", 4, 28);
+ put_str(",,=$ \"$$$$$\" $=,,", 5, 24);
+ put_str(",$$ $$$ $$,", 6, 22);
+ put_str("*> <*> <*", 7, 22);
+ put_str("$$ $$$ $$", 8, 22);
+ put_str("\"$$ $$$ $$\"", 9, 22);
+ put_str("\"$$ $$$ $$\"", 10, 23);
+ put_str("*#########*#########*", 11, 24);
+ put_str("*#########*#########*", 12, 24);
+
+ /* Display a message */
+ put_str("Veni, Vidi, Vici!", 15, 26);
+ put_str("I came, I saw, I conquered!", 16, 21);
+ put_str(format("All Hail the Mighty %s!", sp_ptr->winner), 17, 22);
+
+ /* Flush input */
+ flush();
+
+ /* Wait for response */
+ pause_line(23);
+}
+
+
+/*
+ * Wipe the saved levels
+ */
+void wipe_saved()
+{
+ int d, l, od = dungeon_type, ol = dun_level;
+
+ for (d = 0; d < max_d_idx; d++)
+ {
+ dungeon_info_type *d_ptr = &d_info[d];
+
+ for (l = d_ptr->mindepth; l <= d_ptr->maxdepth; l++)
+ {
+ char buf[10];
+
+ dun_level = l;
+ dungeon_type = d;
+ if (get_dungeon_save(buf))
+ {
+ char tmp[80], name[1024];
+
+ sprintf(tmp, "%s.%s", player_base, buf);
+ path_build(name, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Remove the dungeon save file */
+ fd_kill(name);
+ }
+ }
+ }
+
+ dungeon_type = od;
+ dun_level = ol;
+}
+
+
+/*
+ * Close up the current game (player may or may not be dead)
+ *
+ * This function is called only from "main.c" and "signals.c".
+ */
+void close_game(void)
+{
+ char buf[1024];
+
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Flush the messages */
+ msg_print(NULL);
+
+ /* Flush the input */
+ flush();
+
+
+ /* No suspending now */
+ signals_ignore_tstp();
+
+ /* Hack -- Character is now "icky" */
+ character_icky = TRUE;
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Open the high score file, for reading/writing */
+ highscore_fd = fd_open(buf, O_RDWR);
+
+ /* Handle death */
+ if (death)
+ {
+ /* Handle retirement */
+ if (total_winner)
+ {
+ /* Write a note, if that option is on */
+ if (take_notes)
+ {
+ add_note_type(NOTE_WINNER);
+ }
+
+ kingly();
+ }
+
+ /* Wipe the saved levels */
+ wipe_saved();
+
+ /* Save memories */
+ if (!save_player()) msg_print("Death save failed!");
+
+ /* You are dead */
+ print_tomb();
+
+ /* Show more info */
+ show_info();
+
+ /* Write a note */
+ if (take_notes)
+ {
+ char long_day[30];
+ char buf[80];
+ time_t ct = time((time_t*)NULL);
+
+ /* Get the date */
+ strftime(long_day, 30,
+ "%Y-%m-%d at %H:%M:%S", localtime(&ct));
+
+ /* Create string */
+ sprintf(buf, "\n%s was killed by %s on %s\n", player_name,
+ died_from, long_day);
+
+ /* Output to the notes file */
+ output_note(buf);
+ }
+
+ /* Handle score, show Top scores */
+ top_twenty();
+ }
+
+ /* Still alive */
+ else
+ {
+ is_autosave = FALSE;
+
+ /* Save the game */
+ do_cmd_save_game();
+
+ /* If note-taking enabled, write session end to notes file */
+ if (take_notes)
+ {
+ add_note_type(NOTE_SAVE_GAME);
+ }
+
+ /* Prompt for scores XXX XXX XXX */
+ prt("Press Return (or Escape).", 0, 40);
+
+ /* Predict score (or ESCAPE) */
+ if (inkey() != ESCAPE) predict_score();
+ }
+
+
+ /* Shut the high score file */
+ (void)fd_close(highscore_fd);
+
+ /* Forget the high score fd */
+ highscore_fd = -1;
+
+ /* Allow suspending now */
+ signals_handle_tstp();
+}
+
+
+/*
+ * Grab a randomly selected line in lib/file/file_name
+ */
+errr get_rnd_line(char *file_name, char *output)
+{
+ FILE *fp;
+
+ char buf[1024];
+
+ int lines = 0;
+
+ int line;
+
+ int i;
+
+
+ /* Clear the output buffer */
+ strcpy(output, "");
+
+ /* test hack */
+ if (wizard && cheat_xtra) msg_print(file_name);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, file_name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Failed */
+ if (!fp) return ( -1);
+
+ /* Read the first line */
+ if (0 != my_fgets(fp, buf, 80))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Retrieve number of valid lines in the file */
+ lines = atoi(buf);
+
+ /* Pick a line in the file */
+ line = randint(lines);
+
+ /*
+ * Scan through the file XXX XXX XXX
+ * Seemingly wrong use of the counter is justified by the
+ * stupid 'buffer' lines in the random text files.
+ */
+ for (i = 0; i <= line; i++)
+ {
+ if (0 != my_fgets(fp, buf, 80))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Found the line */
+ if (i == line) break;
+ }
+
+ /* Copy the line to the output buffer */
+ strcpy(output, buf);
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Read line'th line file the file
+ * and return pointer to it, or NULL if it fails.
+ *
+ * Nuked the static buffer. Caller should provide one. -- pelpel
+ *
+ * Caution: 'linbuf' should be at least 80 byte long.
+ */
+char *get_line(char* fname, cptr fdir, char *linbuf, int line)
+{
+ FILE* fp;
+ int i;
+ char buf[1024];
+
+
+ /* Don't count the first line in the file, which is a comment line */
+ line++;
+
+ /* Build the filename */
+ path_build(buf, 1024, fdir, fname);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Failed */
+ if (!fp) return (NULL);
+
+ /* Read past specified number of lines */
+ for (i = 0; i <= line; i++)
+ {
+ /* Oops */
+ if (my_fgets(fp, linbuf, 80) != 0)
+ {
+ my_fclose(fp);
+ return (NULL);
+ }
+ }
+
+ my_fclose(fp);
+
+ return (linbuf);
+}
+
+
+/*
+ * Return a line for a speaking unique, by Matt G.
+ *
+ * XXX XXX XXX Opening a file and scanning it through whenever a unique
+ * tries to say something? Something like DELAY_LOAD_?_TEXT would be
+ * much better -- pelpel
+ *
+ * XXX XXX XXX I must say the original is an extremely poor and unreliable
+ * implementation... I removed noxious flag -- I'm too stupid to
+ * understand such complexities -- and added extra error checkings
+ * and made sure fd is always closed -- pelpel
+ */
+errr get_xtra_line(char *file_name, monster_type *m_ptr, char *output)
+{
+ FILE *fp;
+ char buf[1024];
+ int line;
+ int num_entries;
+ int i;
+ int mnum;
+
+
+ /* Clear the message buffer */
+ strcpy(output, "");
+
+ /* test and DEBUG hack */
+ if (wizard && cheat_xtra)
+ {
+ msg_print(file_name);
+ }
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, file_name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Failed */
+ if (!fp) return ( -1);
+
+ /* Monster number we are looking for */
+ mnum = m_ptr->r_idx;
+
+ /* Find matching N: line */
+ while (1)
+ {
+ int n;
+
+ /* Read a line */
+ if (my_fgets(fp, buf, 90) != 0)
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Not a N: line */
+ if (buf[0] != 'N') continue;
+
+ /* Skip "N:" and parse off a number */
+ sscanf(buf + 2, "%d", &n);
+
+ /* Match found */
+ if (n == mnum) break;
+ }
+
+ /* Retrieve number of normal messages */
+ while (1)
+ {
+ /* Read next line */
+ if (my_fgets(fp, buf, 90) != 0)
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* The first line not beginning with 'N:' holds number of lines */
+ if (buf[0] != 'N')
+ {
+ num_entries = atoi(buf);
+ break;
+ }
+ }
+
+ /* The monster is afraid */
+ if (m_ptr->monfear)
+ {
+ /* Read past normal lines */
+ for (line = 0; line < num_entries + 1; line++)
+ {
+ if (my_fgets(fp, buf, 90))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+ }
+
+ /* Retrieve number of 'afraid' lines */
+ num_entries = atoi(buf);
+ }
+
+
+ /* Pick a random line */
+ line = rand_int(num_entries);
+
+ /* test and DEBUG hack */
+ if (wizard && cheat_xtra)
+ {
+ sprintf(buf, "Line number %d", line);
+ msg_print(buf);
+ }
+
+ /* Find the selected line */
+ for (i = 0; i <= line; i++)
+ {
+ /* Oops */
+ if (0 != my_fgets(fp, buf, 90))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Found it */
+ if (i == line) break;
+ }
+
+ /* Copy it to the output buffer */
+ strcpy(output, buf);
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef HANDLE_SIGNALS
+
+
+#include <signal.h>
+
+
+/*
+ * Handle signals -- suspend
+ *
+ * Actually suspend the game, and then resume cleanly
+ */
+static void handle_signal_suspend(int sig)
+{
+ /* Disable handler */
+ (void)signal(sig, SIG_IGN);
+
+#ifdef SIGSTOP
+
+ /* Flush output */
+ Term_fresh();
+
+ /* Suspend the "Term" */
+ Term_xtra(TERM_XTRA_ALIVE, 0);
+
+ /* Suspend ourself */
+ (void)kill(0, SIGSTOP);
+
+ /* Resume the "Term" */
+ Term_xtra(TERM_XTRA_ALIVE, 1);
+
+ /* Redraw the term */
+ Term_redraw();
+
+ /* Flush the term */
+ Term_fresh();
+
+#endif
+
+ /* Restore handler */
+ (void)signal(sig, handle_signal_suspend);
+}
+
+
+/*
+ * Ignore SIGTSTP signals (keyboard suspend)
+ */
+void signals_ignore_tstp(void)
+{
+
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, SIG_IGN);
+#endif
+
+}
+
+/*
+ * Handle SIGTSTP signals (keyboard suspend)
+ */
+void signals_handle_tstp(void)
+{
+
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, handle_signal_suspend);
+#endif
+
+}
+
+
+/*
+ * Prepare to handle the relevant signals
+ */
+void signals_init(void)
+{
+
+#ifdef SIGHUP
+ (void)signal(SIGHUP, SIG_IGN);
+#endif
+
+
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, handle_signal_suspend);
+#endif
+
+}
+
+
+#else /* HANDLE_SIGNALS */
+
+
+/*
+* Do nothing
+*/
+void signals_ignore_tstp(void)
+{}
+
+/*
+* Do nothing
+*/
+void signals_handle_tstp(void)
+{}
+
+/*
+* Do nothing
+*/
+void signals_init(void)
+{}
+
+
+#endif /* HANDLE_SIGNALS */
diff --git a/src/gen_evol.c b/src/gen_evol.c
new file mode 100644
index 00000000..bfdfbd68
--- /dev/null
+++ b/src/gen_evol.c
@@ -0,0 +1,156 @@
+/*
+ * Generate a game of life level and make it evolve
+ */
+
+/*
+ * Copyright (c) 2003 DarkGod.
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Generate a game of life level :) and make it evolve
+ */
+void evolve_level(bool_ noise)
+{
+ int i, j;
+
+ int cw = 0, cf = 0;
+
+
+ /* Add a bit of noise */
+ if (noise)
+ {
+ for (i = 1; i < cur_wid - 1; i++)
+ {
+ for (j = 1; j < cur_hgt - 1; j++)
+ {
+ if (f_info[cave[j][i].feat].flags1 & FF1_WALL) cw++;
+ if (f_info[cave[j][i].feat].flags1 & FF1_FLOOR) cf++;
+ }
+ }
+
+ for (i = 1; i < cur_wid - 1; i++)
+ {
+ for (j = 1; j < cur_hgt - 1; j++)
+ {
+ cave_type *c_ptr;
+
+ /* Access the grid */
+ c_ptr = &cave[j][i];
+
+ /* Permanent features should stay */
+ if (f_info[c_ptr->feat].flags1 & FF1_PERMANENT) continue;
+
+ /* Avoid evolving grids with object or monster */
+ if (c_ptr->o_idx || c_ptr->m_idx) continue;
+
+ /* Avoid evolving player grid */
+ if ((j == p_ptr->py) && (i == p_ptr->px)) continue;
+
+ if (magik(7))
+ {
+ if (cw > cf)
+ {
+ place_floor(j, i);
+ }
+ else
+ {
+ place_filler(j, i);
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 1; i < cur_wid - 1; i++)
+ {
+ for (j = 1; j < cur_hgt - 1; j++)
+ {
+ int x, y, c;
+ cave_type *c_ptr;
+
+ /* Access the grid */
+ c_ptr = &cave[j][i];
+
+ /* Permanent features should stay */
+ if (f_info[c_ptr->feat].flags1 & FF1_PERMANENT) continue;
+
+ /* Avoid evolving grids with object or monster */
+ if (c_ptr->o_idx || c_ptr->m_idx) continue;
+
+ /* Avoid evolving player grid */
+ if ((j == p_ptr->py) && (i == p_ptr->px)) continue;
+
+
+ /* Reset tally */
+ c = 0;
+
+ /* Count number of surrounding walls */
+ for (x = i - 1; x <= i + 1; x++)
+ {
+ for (y = j - 1; y <= j + 1; y++)
+ {
+ if ((x == i) && (y == j)) continue;
+ if (f_info[cave[y][x].feat].flags1 & FF1_WALL) c++;
+ }
+ }
+
+ /*
+ * Changed these parameters a bit, so that it doesn't produce
+ * too open or too narrow levels -- pelpel
+ */
+ /* Starved or suffocated */
+ if ((c < 4) || (c >= 7))
+ {
+ if (f_info[c_ptr->feat].flags1 & FF1_WALL)
+ {
+ place_floor(j, i);
+ }
+ }
+
+ /* Spawned */
+ else if ((c == 4) || (c == 5))
+ {
+ if (!(f_info[c_ptr->feat].flags1 & FF1_WALL))
+ {
+ place_filler(j, i);
+ }
+ }
+ }
+ }
+
+ /* Notice changes */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_FLOW | PU_MON_LITE);
+}
+
+
+bool_ level_generate_life()
+{
+ int i, j;
+
+ for (i = 1; i < cur_wid - 1; i++)
+ {
+ for (j = 1; j < cur_hgt - 1; j++)
+ {
+ cave[j][i].info = (CAVE_ROOM | CAVE_GLOW | CAVE_MARK);
+ if (magik(45)) place_floor(j, i);
+ else place_filler(j, i);
+ }
+ }
+
+ evolve_level(FALSE);
+ evolve_level(FALSE);
+ evolve_level(FALSE);
+
+ /* Determine the character location */
+ if (!new_player_spot(get_branch()))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/gen_maze.c b/src/gen_maze.c
new file mode 100644
index 00000000..92cb482f
--- /dev/null
+++ b/src/gen_maze.c
@@ -0,0 +1,294 @@
+/*
+ * Maze dungeon generator
+ */
+
+/*
+ * Copyright (c) 2003 DarkGod. And somebody who posted the algorith on
+ * rec.games.roguelike.development. I can't remember teh name :( please mail me
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * If we wasted static memory for this, it would look like:
+ *
+ * static char maze[(MAX_HGT / 2) + 2][(MAX_WID / 2) + 2];
+ */
+typedef signed char maze_row[(MAX_WID / 2) + 2];
+
+void dig(maze_row *maze, int y, int x, int d)
+{
+ int k;
+ int dy = 0, dx = 0;
+
+ /*
+ * first, open the wall of the new cell
+ * in the direction we come from.
+ */
+ switch (d)
+ {
+ case 0:
+ {
+ maze[y][x] |= 4;
+ break;
+ }
+
+ case 1:
+ {
+ maze[y][x] |= 8;
+ break;
+ }
+
+ case 2:
+ {
+ maze[y][x] |= 1;
+ break;
+ }
+
+ case 3:
+ {
+ maze[y][x] |= 2;
+ break;
+ }
+ }
+
+ /*
+ * try to chage direction, here 50% times.
+ * with smaller values (say 25%) the maze
+ * is made of long straight corridors. with
+ * greaters values (say 75%) the maze is
+ * very "turny".
+ */
+ if (rand_range(1, 100) < 50) d = rand_range(0, 3);
+
+ for (k = 1; k <= 4; k++)
+ {
+ switch (d)
+ {
+ case 0:
+ {
+ dy = 0;
+ dx = 1;
+ break;
+ }
+
+ case 1:
+ {
+ dy = -1;
+ dx = 0;
+ break;
+ }
+
+ case 2:
+ {
+ dy = 0;
+ dx = -1;
+ break;
+ }
+
+ case 3:
+ {
+ dy = 1;
+ dx = 0;
+ break;
+ }
+ }
+
+ if (maze[y + dy][x + dx] == 0)
+ {
+ /*
+ * now, open the wall of the new cell
+ * in the direction we go to.
+ */
+ switch (d)
+ {
+ case 0:
+ {
+ maze[y][x] |= 1;
+ break;
+ }
+
+ case 1:
+ {
+ maze[y][x] |= 2;
+ break;
+ }
+
+ case 2:
+ {
+ maze[y][x] |= 4;
+ break;
+ }
+
+ case 3:
+ {
+ maze[y][x] |= 8;
+ break;
+ }
+ }
+
+ dig(maze, y + dy, x + dx, d);
+ }
+
+ d = (d + 1) % 4;
+ }
+}
+
+
+bool_ level_generate_maze()
+{
+ int i, j, d;
+ int y, dy = 0;
+ int x, dx = 0;
+ int m_1 = 0, m_2 = 0;
+ maze_row *maze;
+
+ /* Allocate temporary memory */
+ C_MAKE(maze, (MAX_HGT / 2) + 2, maze_row);
+
+ /*
+ * the empty maze is:
+ *
+ * -1 -1 ... -1 -1
+ * -1 0 0 -1
+ * . .
+ * . .
+ * -1 0 0 -1
+ * -1 -1 ... -1 -1
+ *
+ * -1 are so-called "sentinel value".
+ * 0 are empty cells.
+ *
+ * walls are not represented, only cells.
+ * at the end of the algorithm each cell
+ * contains a value that is bit mask
+ * representing surrounding walls:
+ *
+ * bit #1
+ *
+ * +------+
+ * | |
+ * bit #2 | | bit #0
+ * | |
+ * +------+
+ *
+ * bit #3
+ *
+ * d is the direction you are digging
+ * to. d value is the bit number:
+ * d=0 --> go east
+ * d=1 --> go north
+ * etc
+ *
+ * you need only 4 bits per cell.
+ * this gives you a very compact
+ * maze representation.
+ *
+ */
+ for (j = 0; j <= (cur_hgt / 2) + 1; j++)
+ {
+ for (i = 0; i <= (cur_wid / 2) + 1; i++)
+ {
+ maze[j][i] = -1;
+ }
+ }
+
+ for (j = 1; j <= (cur_hgt / 2); j++)
+ {
+ for (i = 1; i <= (cur_wid / 2); i++)
+ {
+ maze[j][i] = 0;
+ }
+ }
+
+ y = rand_range(1, (cur_hgt / 2));
+ x = rand_range(1, (cur_wid / 2));
+ d = rand_range(0, 3);
+
+ dig(maze, y, x, d);
+
+ maze[y][x] = 0;
+
+ for (d = 0; d <= 3; d++)
+ {
+ switch (d)
+ {
+ case 0:
+ {
+ dy = 0;
+ dx = 1;
+ m_1 = 1;
+ m_2 = 4;
+ break;
+ }
+
+ case 1:
+ {
+ dy = -1;
+ dx = 0;
+ m_1 = 2;
+ m_2 = 8;
+ break;
+ }
+
+ case 2:
+ {
+ dy = 0;
+ dx = -1;
+ m_1 = 4;
+ m_2 = 1;
+ break;
+ }
+
+ case 3:
+ {
+ dy = 1;
+ dx = 0;
+ m_1 = 8;
+ m_2 = 2;
+ break;
+ }
+ }
+
+ if ((maze[y + dy][x + dx] != -1) &&
+ ((maze[y + dy][x + dx] & m_2) != 0))
+ {
+ maze[y][x] |= m_1;
+ }
+ }
+
+ /* Translate the maze bit array into a real dungeon map -- DG */
+ for (j = 1; j <= (cur_hgt / 2) - 2; j++)
+ {
+ for (i = 1; i <= (cur_wid / 2) - 2; i++)
+ {
+ if (maze[j][i])
+ {
+ place_floor(j * 2, i * 2);
+ }
+
+ if (maze[j][i] & 1)
+ {
+ place_floor(j * 2, i * 2 + 1);
+ }
+
+ if (maze[j][i] & 8)
+ {
+ place_floor(j * 2 + 1, i * 2);
+ }
+ }
+ }
+
+ /* Free temporary memory */
+ C_FREE(maze, (MAX_HGT / 2) + 2, maze_row);
+
+ /* Determine the character location */
+ if (!new_player_spot(get_branch()))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/generate.c b/src/generate.c
new file mode 100644
index 00000000..6d83c321
--- /dev/null
+++ b/src/generate.c
@@ -0,0 +1,8890 @@
+/* File: generate.c */
+
+/* Purpose: Dungeon generation */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+#define SAFE_MAX_ATTEMPTS 5000
+
+/*
+ * Note that Level generation is *not* an important bottleneck,
+ * though it can be annoyingly slow on older machines... Thus
+ * we emphasize "simplicity" and "correctness" over "speed".
+ *
+ * This entire file is only needed for generating levels.
+ * This may allow smart compilers to only load it when needed.
+ *
+ * Consider the "v_info.txt" file for vault generation.
+ *
+ * In this file, we use the "special" granite and perma-wall sub-types,
+ * where "basic" is normal, "inner" is inside a room, "outer" is the
+ * outer wall of a room, and "solid" is the outer wall of the dungeon
+ * or any walls that may not be pierced by corridors. Thus the only
+ * wall type that may be pierced by a corridor is the "outer granite"
+ * type. The "basic granite" type yields the "actual" corridors.
+ *
+ * Note that we use the special "solid" granite wall type to prevent
+ * multiple corridors from piercing a wall in two adjacent locations,
+ * which would be messy, and we use the special "outer" granite wall
+ * to indicate which walls "surround" rooms, and may thus be "pierced"
+ * by corridors entering or leaving the room.
+ *
+ * Note that a tunnel which attempts to leave a room near the "edge"
+ * of the dungeon in a direction toward that edge will cause "silly"
+ * wall piercings, but will have no permanently incorrect effects,
+ * as long as the tunnel can *eventually* exit from another side.
+ * And note that the wall may not come back into the room by the
+ * hole it left through, so it must bend to the left or right and
+ * then optionally re-enter the room (at least 2 grids away). This
+ * is not a problem since every room that is large enough to block
+ * the passage of tunnels is also large enough to allow the tunnel
+ * to pierce the room itself several times.
+ *
+ * Note that no two corridors may enter a room through adjacent grids,
+ * they must either share an entryway or else use entryways at least
+ * two grids apart. This prevents "large" (or "silly") doorways.
+ *
+ * To create rooms in the dungeon, we first divide the dungeon up
+ * into "blocks" of 11x11 grids each, and require that all rooms
+ * occupy a rectangular group of blocks. As long as each room type
+ * reserves a sufficient number of blocks, the room building routines
+ * will not need to check bounds. Note that most of the normal rooms
+ * actually only use 23x11 grids, and so reserve 33x11 grids.
+ *
+ * Note that the use of 11x11 blocks (instead of the old 33x11 blocks)
+ * allows more variability in the horizontal placement of rooms, and
+ * at the same time has the disadvantage that some rooms (two thirds
+ * of the normal rooms) may be "split" by panel boundaries. This can
+ * induce a situation where a player is in a room and part of the room
+ * is off the screen. It may be annoying enough to go back to 33x11
+ * blocks to prevent this visual situation.
+ *
+ * Note that the dungeon generation routines are much different (2.7.5)
+ * and perhaps "DUN_ROOMS" should be less than 50.
+ *
+ * XXX XXX XXX Note that it is possible to create a room which is only
+ * connected to itself, because the "tunnel generation" code allows a
+ * tunnel to leave a room, wander around, and then re-enter the room.
+ *
+ * XXX XXX XXX Note that it is possible to create a set of rooms which
+ * are only connected to other rooms in that set, since there is nothing
+ * explicit in the code to prevent this from happening. But this is less
+ * likely than the "isolated room" problem, because each room attempts to
+ * connect to another room, in a giant cycle, thus requiring at least two
+ * bizarre occurances to create an isolated section of the dungeon.
+ *
+ * Note that (2.7.9) monster pits have been split into monster "nests"
+ * and monster "pits". The "nests" have a collection of monsters of a
+ * given type strewn randomly around the room (jelly, animal, or undead),
+ * while the "pits" have a collection of monsters of a given type placed
+ * around the room in an organized manner (orc, troll, giant, dragon, or
+ * demon). Note that both "nests" and "pits" are now "level dependant",
+ * and both make 16 "expensive" calls to the "get_mon_num()" function.
+ *
+ * Note that the cave grid flags changed in a rather drastic manner
+ * for Angband 2.8.0 (and 2.7.9+), in particular, dungeon terrain
+ * features, such as doors and stairs and traps and rubble and walls,
+ * are all handled as a set of 64 possible "terrain features", and
+ * not as "fake" objects (440-479) as in pre-2.8.0 versions.
+ *
+ * The 64 new "dungeon features" will also be used for "visual display"
+ * but we must be careful not to allow, for example, the user to display
+ * hidden traps in a different way from floors, or secret doors in a way
+ * different from granite walls, or even permanent granite in a different
+ * way from granite. XXX XXX XXX
+ */
+
+
+/*
+ * Dungeon generation values
+ */
+#define DUN_ROOMS 50 /* Number of rooms to attempt */
+#define DUN_UNUSUAL 194 /* Level/chance of unusual room (was 200) */
+#define DUN_DEST 18 /* 1/chance of having a destroyed level */
+#define SMALL_LEVEL 6 /* 1/chance of smaller size (3->6) */
+#define EMPTY_LEVEL 15 /* 1/chance of being 'empty' (15)*/
+#define DARK_EMPTY 5 /* 1/chance of arena level NOT being lit (2)*/
+#define XTRA_MAGIC 10 /* 1/chance of having a level with more magic (10)*/
+#define DUN_WILD_VAULT 50 /* Chance of finding a wilderness vault. */
+#define DUN_WAT_RNG 2 /* Width of rivers */
+#define DUN_WAT_CHG 50 /* 1 in 50 chance of junction in river */
+#define DUN_CAVERN 30 /* 1/chance of having a cavern level */
+
+/*
+ * Dungeon tunnel generation values
+ */
+#define DUN_TUN_RND 10 /* Chance of random direction */
+#define DUN_TUN_CHG 30 /* Chance of changing direction */
+#define DUN_TUN_CON 15 /* Chance of extra tunneling */
+#define DUN_TUN_PEN 25 /* Chance of doors at room entrances */
+#define DUN_TUN_JCT 90 /* Chance of doors at tunnel junctions */
+
+/*
+ * Dungeon streamer generation values
+ */
+#define DUN_STR_DEN 5 /* Density of streamers */
+#define DUN_STR_RNG 2 /* Width of streamers */
+#define DUN_STR_MAG 3 /* Number of magma streamers */
+#define DUN_STR_MC 90 /* 1/chance of treasure per magma */
+#define DUN_STR_QUA 2 /* Number of quartz streamers */
+#define DUN_STR_QC 40 /* 1/chance of treasure per quartz */
+#define DUN_STR_SAN 1 /* Number of sand streamers */
+#define DUN_STR_SC 10 /* 1/chance of treasure per sandwall */
+#define DUN_STR_WLW 1 /* Width of lava & water streamers -KMW- */
+#define DUN_STR_DWLW 8 /* Density of water & lava streams -KMW- */
+
+
+/*
+ * Dungeon treausre allocation values
+ */
+#define DUN_AMT_ROOM 9 /* Amount of objects for rooms */
+#define DUN_AMT_ITEM 3 /* Amount of objects for rooms/corridors */
+#define DUN_AMT_GOLD 3 /* Amount of treasure for rooms/corridors */
+#define DUN_AMT_ALTAR 1 /* Amount of altars */
+#define DUN_AMT_BETWEEN 2 /* Amount of between gates */
+#define DUN_AMT_FOUNTAIN 1 /* Amount of fountains */
+
+/*
+ * Hack -- Dungeon allocation "places"
+ */
+#define ALLOC_SET_CORR 1 /* Hallway */
+#define ALLOC_SET_ROOM 2 /* Room */
+#define ALLOC_SET_BOTH 3 /* Anywhere */
+
+/*
+ * Hack -- Dungeon allocation "types"
+ */
+#define ALLOC_TYP_RUBBLE 1 /* Rubble */
+#define ALLOC_TYP_TRAP 3 /* Trap */
+#define ALLOC_TYP_GOLD 4 /* Gold */
+#define ALLOC_TYP_OBJECT 5 /* Object */
+#define ALLOC_TYP_ALTAR 6 /* Altar */
+#define ALLOC_TYP_BETWEEN 7 /* Between */
+#define ALLOC_TYP_FOUNTAIN 8 /* Fountain */
+
+
+/*
+ * The "size" of a "generation block" in grids
+ */
+#define BLOCK_HGT 11
+#define BLOCK_WID 11
+
+/*
+ * Maximum numbers of rooms along each axis (currently 6x6)
+ */
+#define MAX_ROOMS_ROW (MAX_HGT / BLOCK_HGT)
+#define MAX_ROOMS_COL (MAX_WID / BLOCK_WID)
+
+
+/*
+ * Bounds on some arrays used in the "dun_data" structure.
+ * These bounds are checked, though usually this is a formality.
+ */
+#define CENT_MAX 100
+#define DOOR_MAX 200
+#define WALL_MAX 500
+#define TUNN_MAX 900
+
+
+/*
+ * Maximal number of room types
+ */
+#define ROOM_MAX 12
+
+
+
+/*
+ * Simple structure to hold a map location
+ */
+
+
+typedef struct coord coord;
+
+struct coord
+{
+ byte y;
+ byte x;
+};
+
+
+/*
+ * Room type information
+ */
+
+typedef struct room_data room_data;
+
+struct room_data
+{
+ /* Required size in blocks */
+ s16b dy1, dy2, dx1, dx2;
+
+ /* Hack -- minimum level */
+ s16b level;
+};
+
+
+/*
+ * Structure to hold all "dungeon generation" data
+ */
+
+typedef struct dun_data dun_data;
+
+struct dun_data
+{
+ /* Array of centers of rooms */
+ int cent_n;
+ coord cent[CENT_MAX];
+
+ /* Array of possible door locations */
+ int door_n;
+ coord door[DOOR_MAX];
+
+ /* Array of wall piercing locations */
+ int wall_n;
+ coord wall[WALL_MAX];
+
+ /* Array of tunnel grids */
+ int tunn_n;
+ coord tunn[TUNN_MAX];
+
+ /* Number of blocks along each axis */
+ int row_rooms;
+ int col_rooms;
+
+ /* Array of which blocks are used */
+ bool_ room_map[MAX_ROOMS_ROW][MAX_ROOMS_COL];
+
+ /* Hack -- there is a pit/nest on this level */
+ bool_ crowded;
+};
+
+/*
+ * Level generator type
+ */
+
+typedef struct level_generator_type level_generator_type;
+struct level_generator_type
+{
+ cptr name;
+ bool_ (*generator)(cptr);
+
+ bool_ default_stairs;
+ bool_ default_monsters;
+ bool_ default_objects;
+ bool_ default_miscs;
+
+ struct level_generator_type *next;
+};
+
+static level_generator_type *level_generators = NULL;
+
+/*
+ * Add a new generator
+ */
+void add_level_generator(cptr name, bool_ (*generator)(), bool_ stairs, bool_ monsters, bool_ objects, bool_ miscs)
+{
+ level_generator_type *g;
+
+ MAKE(g, level_generator_type);
+ g->name = string_make(name);
+ g->generator = generator;
+
+ g->default_stairs = stairs;
+ g->default_monsters = monsters;
+ g->default_objects = objects;
+ g->default_miscs = miscs;
+
+ g->next = level_generators;
+ level_generators = g;
+}
+
+
+/*
+ * Dungeon generation data -- see "cave_gen()"
+ */
+static dun_data *dun;
+
+/*
+ * ???
+ */
+static int template_race;
+
+
+
+/*
+ * Array of room types depths
+ */
+static s16b roomdep[] =
+{
+ 0, /* 0 = Nothing */
+ 1, /* 1 = Simple (33x11) */
+ 1, /* 2 = Overlapping (33x11) */
+ 3, /* 3 = Crossed (33x11) */
+ 3, /* 4 = Large (33x11) */
+ 5, /* 5 = Monster nest (33x11) */
+ 5, /* 6 = Monster pit (33x11) */
+ 5, /* 7 = Lesser vault (33x22) */
+ 10, /* 8 = Greater vault (66x44) */
+ 1, /* 9 = Circular rooms (22x22) */
+ 3, /* 10 = Fractal cave (42x24) */
+ 10, /* 11 = Random vault (44x22) */
+ 10, /* 12 = Crypts (22x22) */
+};
+
+
+/*
+ * Always picks a correct direction
+ */
+static void correct_dir(int *rdir, int *cdir, int y1, int x1, int y2, int x2)
+{
+ /* Extract vertical and horizontal directions */
+ *rdir = (y1 == y2) ? 0 : (y1 < y2) ? 1 : -1;
+ *cdir = (x1 == x2) ? 0 : (x1 < x2) ? 1 : -1;
+
+ /* Never move diagonally */
+ if (*rdir && *cdir)
+ {
+ if (rand_int(100) < 50)
+ {
+ *rdir = 0;
+ }
+ else
+ {
+ *cdir = 0;
+ }
+ }
+}
+
+
+/*
+ * Pick a random direction
+ */
+static void rand_dir(int *rdir, int *cdir)
+{
+ /* Pick a random direction */
+ int i = rand_int(4);
+
+ /* Extract the dy/dx components */
+ *rdir = ddy_ddd[i];
+ *cdir = ddx_ddd[i];
+}
+
+
+/*
+ * Convert existing terrain type to "up stairs"
+ */
+static void place_up_stairs(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Create up stairs */
+ if ((rand_int(3) != 0) || (dungeon_flags2 & DF2_NO_SHAFT))
+ {
+ cave_set_feat(y, x, FEAT_LESS);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_UP);
+ }
+
+ c_ptr->special = 0;
+}
+
+
+/*
+ * Convert existing terrain type to "down stairs" with dungeon changing.
+ */
+static void place_magical_stairs(int y, int x, byte next)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Create up stairs */
+ cave_set_feat(y, x, FEAT_MORE);
+ c_ptr->special = next;
+}
+
+
+/*
+ * Convert existing terrain type to "down stairs"
+ */
+static void place_down_stairs(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /*
+ * Create down stairs
+ * All thoses tests are necesary because a shaft can jump up to 4 levels
+ */
+ if ((dun_level + 4 > d_info[dungeon_type].maxdepth) ||
+ (rand_int(3) != 0) || (dungeon_flags2 & DF2_NO_SHAFT))
+ {
+ cave_set_feat(y, x, FEAT_MORE);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_DOWN);
+ }
+
+ c_ptr->special = 0;
+}
+
+
+/*
+ * Helper function for place_new_way. Determine if y, x is one of
+ * floor features of the current dungeon
+ */
+static bool_ is_safe_floor(int y, int x)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ byte feat = cave[y][x].feat;
+
+ /* One of the legal floor types */
+ if (feat == d_ptr->floor1) return (TRUE);
+ if (feat == d_ptr->floor2) return (TRUE);
+ if (feat == d_ptr->floor3) return (TRUE);
+
+ /* Assume non-floor */
+ return (FALSE);
+}
+
+
+/*
+ * Place a way to next / previoous level on flat places
+ */
+void place_new_way(int *y, int *x)
+{
+ int xx, yy;
+ int x0, x1, x2;
+ int y0, y1, y2;
+ cave_type *c_ptr;
+ bool_ ok;
+ int i, way_n;
+ byte way_x[MAX_WID], way_y[MAX_WID];
+
+
+ /* Find valid location XXX XXX XXX */
+ while (TRUE)
+ {
+ /* A way on vertical edge */
+ if (rand_int(cur_hgt + cur_wid) < cur_hgt)
+ {
+ /* Pick a random grid */
+ yy = *y = rand_int(cur_hgt - 2) + 1;
+ xx = *x = 1 + (rand_int(2) * (cur_wid - 3));
+
+ /* Pick a direction */
+ if (xx == 1)
+ {
+ /* Left */
+ x0 = + 1;
+ y0 = 0;
+
+ /* Sides */
+ x1 = 0;
+ y1 = -1;
+ x2 = 0;
+ y2 = + 1;
+ }
+ else
+ {
+ /* Right */
+ x0 = -1;
+ y0 = 0;
+
+ /* Sides */
+ x1 = 0;
+ y1 = -1;
+ x2 = 0;
+ y2 = + 1;
+ }
+ }
+
+ /* A way on horizontal edge */
+ else
+ {
+ /* Pick a random grid */
+ xx = *x = rand_int(cur_wid - 2) + 1;
+ yy = *y = 1 + (rand_int(2) * (cur_hgt - 3));
+
+ /* Pick a direction */
+ if (yy == 1)
+ {
+ /* Down */
+ x0 = 0;
+ y0 = + 1;
+
+ /* Sides */
+ x1 = -1;
+ y1 = 0;
+ x2 = + 1;
+ y2 = 0;
+ }
+ else
+ {
+ /* Up */
+ x0 = 0;
+ y0 = -1;
+
+ /* Sides */
+ x1 = -1;
+ y1 = 0;
+ x2 = + 1;
+ y2 = 0;
+ }
+ }
+
+
+ /* Look at the starting location */
+ c_ptr = &cave[yy][xx];
+
+ /* Reject locations inside vaults */
+ if (c_ptr->info & (CAVE_ICKY)) continue;
+
+ /* Reject permanent features */
+ if ((f_info[c_ptr->feat].flags1 & (FF1_PERMANENT)) &&
+ (f_info[c_ptr->feat].flags1 & (FF1_FLOOR))) continue;
+
+ /* Reject room walls */
+ if ((c_ptr->info & (CAVE_ROOM)) &&
+ (c_ptr->feat == feat_wall_outer)) continue;
+
+ /* Look at a neighbouring edge */
+ c_ptr = &cave[yy + y1][xx + x1];
+
+ /* Reject two adjacent ways */
+ if ((c_ptr->feat == FEAT_WAY_MORE) ||
+ (c_ptr->feat == FEAT_WAY_LESS)) continue;
+
+ /* Look at the other neighbouring edge */
+ c_ptr = &cave[yy + y2][xx + x2];
+
+ /* Reject two adjacent ways */
+ if ((c_ptr->feat == FEAT_WAY_MORE) ||
+ (c_ptr->feat == FEAT_WAY_LESS)) continue;
+
+ /* Look ahead */
+ c_ptr = &cave[yy + y0][xx + x0];
+
+ /* Reject two adjacent ways -- relatively rare, but this can happen */
+ if ((c_ptr->feat == FEAT_WAY_MORE) ||
+ (c_ptr->feat == FEAT_WAY_LESS)) continue;
+
+
+ /* Reset counter */
+ way_n = 0;
+
+ /* Assume bad location */
+ ok = FALSE;
+
+ /* Check if it connects to current dungeon */
+ while (in_bounds(yy, xx))
+ {
+#if 1
+
+ /* Check grids ahead */
+ if (is_safe_floor(yy + y0, xx + x0)) ok = TRUE;
+
+ /* Check side grids */
+ if (is_safe_floor(yy + y1, xx + x1)) ok = TRUE;
+ if (is_safe_floor(yy + y2, xx + x2)) ok = TRUE;
+
+#else
+
+ /*
+ * This can create unconnected sections if it bumps into a
+ * non-penetrating streamer
+ */
+ /* Check grids ahead */
+ if (cave_floor_bold(yy + y0, xx + x0)) ok = TRUE;
+
+ /* Check side grids */
+ if (cave_floor_bold(yy + y1, xx + x1)) ok = TRUE;
+ if (cave_floor_bold(yy + y2, xx + x2)) ok = TRUE;
+
+#endif
+
+ /* Connected */
+ if (ok) break;
+
+ /* Access grid (ahead) */
+ c_ptr = &cave[yy + y0][xx + x0];
+
+ /* Avoid opening vaults */
+ if (c_ptr->feat == FEAT_PERM_OUTER)
+ {
+ /* Comment this out if you find any problems... */
+ ok = TRUE;
+
+ break;
+ }
+
+ /* Paranoia */
+ if (c_ptr->feat == FEAT_PERM_SOLID) break;
+
+ /*
+ * Hack -- Avoid digging room corner
+ *
+ * CAVEAT: Can't handle situations like this:
+ *
+ * .....########
+ * .....########
+ * ######.....>#
+ * #############
+ * .....#
+ * .....#
+ */
+ if (c_ptr->info & (CAVE_ROOM))
+ {
+ cave_type *c1_ptr = &cave[yy + y0 + y1][xx + x0 + x1];
+ cave_type *c2_ptr = &cave[yy + y0 + y2][xx + x0 + x2];
+
+ /* Bend the way */
+ if ((c1_ptr->info & (CAVE_ROOM)) &&
+ !(c2_ptr->info & (CAVE_ROOM)))
+ {
+ way_x[way_n] = xx + x1;
+ way_y[way_n] = yy + y1;
+ way_n++;
+ way_x[way_n] = xx + x1 + x0;
+ way_y[way_n] = yy + y1 + y0;
+ way_n++;
+ }
+
+ /* Bend the way -- the other direction */
+ else if ((c2_ptr->info & (CAVE_ROOM)) &&
+ !(c1_ptr->info & (CAVE_ROOM)))
+ {
+ way_x[way_n] = xx + x2;
+ way_y[way_n] = yy + y2;
+ way_n++;
+ way_x[way_n] = xx + x2 + x0;
+ way_y[way_n] = yy + y2 + y0;
+ way_n++;
+ }
+
+ else
+ {
+ way_x[way_n] = xx + x0;
+ way_y[way_n] = yy + y0;
+ way_n++;
+ }
+
+ ok = TRUE;
+ break;
+ }
+
+ /* Remember the location */
+ way_x[way_n] = xx + x0;
+ way_y[way_n] = yy + y0;
+ way_n++;
+
+ /* Advance */
+ xx += x0;
+ yy += y0;
+ }
+
+ /* Accept connected corridor */
+ if (ok) break;
+
+ /* Try again, until valid location is found */
+ }
+
+
+ /* Actually dig a corridor connecting the way to dungeon */
+ for (i = 0; i < way_n; i++)
+ {
+ /* Dig */
+ place_floor(way_y[i], way_x[i]);
+ }
+}
+
+
+/*
+ * Returns random co-ordinates for player/monster/object
+ */
+bool_ new_player_spot(int branch)
+{
+ int y, x;
+ int max_attempts = 5000;
+
+ /* Place the player */
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ place_new_way(&y, &x);
+ }
+ else
+ {
+ while (max_attempts--)
+ {
+ /* Pick a legal spot */
+ y = rand_range(1, cur_hgt - 2);
+ x = rand_range(1, cur_wid - 2);
+
+ /* Must be a "naked" floor grid */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* Refuse to start on anti-teleport grids */
+ if (cave[y][x].info & (CAVE_ICKY)) continue;
+
+ /* Done */
+ break;
+
+ }
+ }
+
+ /* Should be -1, actually if we failed... */
+ if (max_attempts < 1) return (FALSE);
+
+
+ /* Save the new player grid */
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ /* XXX XXX XXX */
+ if (dungeon_stair && !(dungeon_flags2 & DF2_NO_STAIR) && dun_level &&
+ (!is_quest(dun_level) || (old_dun_level < dun_level)) && !branch)
+ {
+ if (old_dun_level < dun_level)
+ {
+ place_up_stairs(p_ptr->py , p_ptr->px);
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_WAY_LESS);
+ }
+ }
+ else
+ {
+ place_down_stairs(p_ptr->py , p_ptr->px);
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_WAY_MORE);
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+
+
+/*
+ * Count the number of walls adjacent to the given grid.
+ *
+ * Note -- Assumes "in_bounds(y, x)"
+ *
+ * We count only granite walls and permanent walls.
+ */
+static int next_to_walls(int y, int x)
+{
+ int k = 0;
+
+ if (f_info[cave[y + 1][x].feat].flags1 & FF1_WALL) k++;
+ if (f_info[cave[y - 1][x].feat].flags1 & FF1_WALL) k++;
+ if (f_info[cave[y][x + 1].feat].flags1 & FF1_WALL) k++;
+ if (f_info[cave[y][x - 1].feat].flags1 & FF1_WALL) k++;
+
+ return (k);
+}
+
+
+
+/*
+ * Convert existing terrain type to rubble
+ */
+static void place_rubble(int y, int x)
+{
+ /* Create rubble */
+ cave_set_feat(y, x, FEAT_RUBBLE);
+}
+
+
+/*
+ * Place an altar at the given location
+ */
+static void place_altar(int y, int x)
+{
+ if (magik(10))
+ cave_set_feat(y, x, 164);
+}
+
+
+/*
+ * Place a fountain at the given location
+ */
+static void place_fountain(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+ int svals[SV_POTION_LAST + SV_POTION2_LAST + 1], maxsval = 0, k;
+
+ /* List of usable svals */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ if (((k_ptr->tval == TV_POTION) || (k_ptr->tval == TV_POTION2)) &&
+ (k_ptr->level <= dun_level) && (k_ptr->flags4 & TR4_FOUNTAIN))
+ {
+ if (k_ptr->tval == TV_POTION2) svals[maxsval] = k_ptr->sval + SV_POTION_LAST;
+ else svals[maxsval] = k_ptr->sval;
+ maxsval++;
+ }
+ }
+
+ if (maxsval == 0) return;
+
+ /* Place the fountain */
+ if (randint(100) < 30)
+ {
+ cave_set_feat(y, x, FEAT_EMPTY_FOUNTAIN);
+ c_ptr->special2 = 0;
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_FOUNTAIN);
+ c_ptr->special2 = damroll(3, 4);
+ }
+
+ c_ptr->special = svals[rand_int(maxsval)];
+}
+
+
+/*
+ * Place a between gate at the given location
+ */
+static void place_between(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x], *c1_ptr;
+ int gx, gy;
+
+ while (TRUE)
+ {
+ /* Location */
+ gy = rand_int(cur_hgt);
+ gx = rand_int(cur_wid);
+
+ /* Require "naked" floor grid */
+ if (cave_naked_bold(gy, gx)) break;
+ }
+
+ /* Access the target grid */
+ c1_ptr = &cave[gy][gx];
+
+ /* Place a pair of between gates */
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ c_ptr->special = gx + (gy << 8);
+ cave_set_feat(gy, gx, FEAT_BETWEEN);
+ c1_ptr->special = x + (y << 8);
+}
+
+
+/*
+ * Place an up/down staircase at given location
+ */
+static void place_random_stairs(int y, int x)
+{
+ /* Paranoia */
+ if (!cave_clean_bold(y, x)) return;
+
+ /* Choose a staircase */
+ if (!dun_level)
+ {
+ place_down_stairs(y, x);
+ }
+ else if (is_quest(dun_level) && (dun_level > 1))
+ {
+ place_up_stairs(y, x);
+ }
+ else if (dun_level >= d_info[dungeon_type].maxdepth)
+ {
+ if (d_info[dungeon_type].next)
+ {
+ place_magical_stairs(y, x, d_info[dungeon_type].next);
+ }
+ else
+ {
+ place_up_stairs(y, x);
+ }
+ }
+ else if (rand_int(100) < 50)
+ {
+ place_down_stairs(y, x);
+ }
+ else
+ {
+ place_up_stairs(y, x);
+ }
+}
+
+
+/*
+ * Place a locked door at the given location
+ */
+static void place_locked_door(int y, int x)
+{
+ /* Create locked door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + randint(7));
+}
+
+
+/*
+ * Place a secret door at the given location
+ */
+static void place_secret_door(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Vaults */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ c_ptr->mimic = FEAT_WALL_INNER;
+ }
+
+ /* Ordinary room -- use current outer or inner wall */
+ else if (c_ptr->info & CAVE_ROOM)
+ {
+ /* Determine if it's inner or outer XXX XXX XXX */
+ if ((cave[y - 1][x].info & CAVE_ROOM) &&
+ (cave[y + 1][x].info & CAVE_ROOM) &&
+ (cave[y][x - 1].info & CAVE_ROOM) &&
+ (cave[y][x + 1].info & CAVE_ROOM))
+ {
+ c_ptr->mimic = feat_wall_inner;
+ }
+ else
+ {
+ c_ptr->mimic = feat_wall_outer;
+ }
+ }
+ else
+ {
+ c_ptr->mimic = fill_type[rand_int(100)];
+ }
+
+ /* Create secret door */
+ cave_set_feat(y, x, FEAT_SECRET);
+}
+
+
+/*
+ * Place a random type of door at the given location
+ */
+static void place_random_door(int y, int x)
+{
+ int tmp;
+
+ /* Choose an object */
+ tmp = rand_int(1000);
+
+ /* Open doors (300/1000) */
+ if (tmp < 300)
+ {
+ /* Create open door */
+ cave_set_feat(y, x, FEAT_OPEN);
+ }
+
+ /* Broken doors (100/1000) */
+ else if (tmp < 400)
+ {
+ /* Create broken door */
+ cave_set_feat(y, x, FEAT_BROKEN);
+ }
+
+ /* Secret doors (200/1000) */
+ else if (tmp < 600)
+ {
+ /* Create secret door */
+ place_secret_door(y, x);
+ }
+
+ /* Closed doors (300/1000) */
+ else if (tmp < 900)
+ {
+ /* Create closed door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+ }
+
+ /* Locked doors (99/1000) */
+ else if (tmp < 999)
+ {
+ /* Create locked door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + randint(7));
+ }
+
+ /* Stuck doors (1/1000) */
+ else
+ {
+ /* Create jammed door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x08 + (byte)rand_int(8));
+ }
+}
+
+
+
+/*
+ * Places some staircases near walls
+ */
+static void alloc_stairs(int feat, int num, int walls, int branch)
+{
+ int y, x, i, j, cnt;
+
+ /* Place "num" stairs */
+ for (cnt = 0, i = 0; i < num || (cnt < 1 && num > 1); i++)
+ {
+ /* Try several times, then decrease "walls" */
+ for (j = 0; j <= SAFE_MAX_ATTEMPTS; j++)
+ {
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ place_new_way(&y, &x);
+ }
+ else
+ {
+ /* Pick a random grid */
+ y = rand_int(cur_hgt);
+ x = rand_int(cur_wid);
+
+ /* Require "naked" floor grid */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* Require a certain number of adjacent walls */
+ if (next_to_walls(y, x) < walls) continue;
+ }
+
+ /* Town -- must go down */
+ if (!dun_level)
+ {
+ /* Clear previous contents, add down stairs */
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(y, x, FEAT_WAY_MORE);
+ }
+ else if ((rand_int(3) == 0) && (!(dungeon_flags2 & DF2_NO_SHAFT)))
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_DOWN);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_MORE);
+ }
+ }
+
+ /* Quest -- must go up */
+ else if ((is_quest(dun_level) && (dun_level >= 1)) ||
+ ((dun_level >= d_info[dungeon_type].maxdepth) &&
+ (!(dungeon_flags1 & DF1_FORCE_DOWN))))
+ {
+ /* Clear previous contents, add up stairs */
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(y, x, FEAT_WAY_LESS);
+ }
+ else if ((rand_int(3) == 0) && (!(dungeon_flags2 & DF2_NO_SHAFT)))
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_UP);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_LESS);
+ }
+ }
+
+ /* Requested type */
+ else
+ {
+ /* Clear previous contents, add stairs */
+ cave_set_feat(y, x, feat);
+ }
+
+ cave[y][x].special = branch;
+
+ /* Count the number of stairs we've actually managed to place. */
+ cnt++;
+
+ /* All done */
+ break;
+ }
+
+ /* Require fewer walls */
+ if (walls) walls--;
+ }
+}
+
+
+
+
+/*
+ * Allocates some objects (using "place" and "type")
+ */
+static void alloc_object(int set, int typ, int num)
+{
+ int y = 1, x = 1, k;
+ int dummy = 0;
+
+ /* Place some objects */
+ for (k = 0; k < num; k++)
+ {
+ /* Pick a "legal" spot */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ bool_ room;
+
+ dummy++;
+
+ /* Location */
+ y = rand_int(cur_hgt);
+ x = rand_int(cur_wid);
+
+ /* Require "naked" floor grid */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* Check for "room" */
+ room = (cave[y][x].info & (CAVE_ROOM)) ? TRUE : FALSE;
+
+ /* Require corridor? */
+ if ((set == ALLOC_SET_CORR) && room) continue;
+
+ /* Require room? */
+ if ((set == ALLOC_SET_ROOM) && !room) continue;
+
+ /* Accept it */
+ break;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_format("Warning! Could not place object, type : %d!", typ);
+ }
+
+ return;
+ }
+
+
+ /* Place something */
+ switch (typ)
+ {
+ case ALLOC_TYP_RUBBLE:
+ {
+ place_rubble(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_TRAP:
+ {
+ place_trap(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_GOLD:
+ {
+ place_gold(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_OBJECT:
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ break;
+ }
+
+ case ALLOC_TYP_ALTAR:
+ {
+ place_altar(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_BETWEEN:
+ {
+ place_between(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_FOUNTAIN:
+ {
+ place_fountain(y, x);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * The following functions create a rectangle (e.g. outer wall of rooms)
+ */
+void build_rectangle(int y1, int x1, int y2, int x2, int feat, int info)
+{
+ int y, x;
+
+ /* Top and bottom boundaries */
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y1, x, feat);
+ cave[y1][x].info |= (info);
+
+ cave_set_feat(y2, x, feat);
+ cave[y2][x].info |= (info);
+ }
+
+ /* Top and bottom boundaries */
+ for (y = y1; y <= y2; y++)
+ {
+ cave_set_feat(y, x1, feat);
+ cave[y][x1].info |= (info);
+
+ cave_set_feat(y, x2, feat);
+ cave[y][x2].info |= (info);
+ }
+}
+
+
+/*
+ * Place water through the dungeon using recursive fractal algorithm
+ *
+ * Why do those good at math and/or algorithms tend *not* to
+ * place any spaces around binary operators? I've been always
+ * wondering. This seems almost a unversal phenomenon...
+ * Tried to make those conform to the rule, but there may still
+ * some left untouched...
+ */
+static void recursive_river(int x1, int y1, int x2, int y2,
+ int feat1, int feat2, int width)
+{
+ int dx, dy, length, l, x, y;
+ int changex, changey;
+ int ty, tx;
+
+
+ length = distance(x1, y1, x2, y2);
+
+ if (length > 4)
+ {
+ /*
+ * Divide path in half and call routine twice.
+ * There is a small chance of splitting the river
+ */
+ dx = (x2 - x1) / 2;
+ dy = (y2 - y1) / 2;
+
+ if (dy != 0)
+ {
+ /* perturbation perpendicular to path */
+ changex = randint(abs(dy)) * 2 - abs(dy);
+ }
+ else
+ {
+ changex = 0;
+ }
+
+ if (dx != 0)
+ {
+ /* perturbation perpendicular to path */
+ changey = randint(abs(dx)) * 2 - abs(dx);
+ }
+ else
+ {
+ changey = 0;
+ }
+
+
+
+ /* construct river out of two smaller ones */
+ recursive_river(x1, y1, x1 + dx + changex, y1 + dy + changey,
+ feat1, feat2, width);
+ recursive_river(x1 + dx + changex, y1 + dy + changey, x2, y2,
+ feat1, feat2, width);
+
+ /* Split the river some of the time -junctions look cool */
+ if ((width > 0) && (rand_int(DUN_WAT_CHG) == 0))
+ {
+ recursive_river(x1 + dx + changex, y1 + dy + changey,
+ x1 + 8 * (dx + changex), y1 + 8 * (dy + changey),
+ feat1, feat2, width - 1);
+ }
+ }
+
+ /* Actually build the river */
+ else
+ {
+ for (l = 0; l < length; l++)
+ {
+ x = x1 + l * (x2 - x1) / length;
+ y = y1 + l * (y2 - y1) / length;
+
+ for (ty = y - width - 1; ty <= y + width + 1; ty++)
+ {
+ for (tx = x - width - 1; tx <= x + width + 1; tx++)
+ {
+ if (!in_bounds(ty, tx)) continue;
+
+ if (cave[ty][tx].feat == feat1) continue;
+ if (cave[ty][tx].feat == feat2) continue;
+
+ if (distance(ty, tx, y, x) > rand_spread(width, 1)) continue;
+
+ /* Do not convert permanent features */
+ if (cave_perma_bold(ty, tx)) continue;
+
+ /*
+ * Clear previous contents, add feature
+ * The border mainly gets feat2, while the center
+ * gets feat1
+ */
+ if (distance(ty, tx, y, x) > width)
+ {
+ cave_set_feat(ty, tx, feat2);
+ }
+ else
+ {
+ cave_set_feat(ty, tx, feat1);
+ }
+
+ /* Lava terrain glows */
+ if ((feat1 == FEAT_DEEP_LAVA) ||
+ (feat1 == FEAT_SHAL_LAVA))
+ {
+ cave[ty][tx].info |= CAVE_GLOW;
+ }
+
+ /* Hack -- don't teleport here */
+ cave[ty][tx].info |= CAVE_ICKY;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Places water through dungeon.
+ */
+static void add_river(int feat1, int feat2)
+{
+ int y2, x2;
+ int y1 = 0, x1 = 0, wid;
+
+
+ /* Hack -- Choose starting point */
+ y2 = randint(cur_hgt / 2 - 2) + cur_hgt / 2;
+ x2 = randint(cur_wid / 2 - 2) + cur_wid / 2;
+
+ /* Hack -- Choose ending point somewhere on boundary */
+ switch (randint(4))
+ {
+ case 1:
+ {
+ /* top boundary */
+ x1 = randint(cur_wid - 2) + 1;
+ y1 = 1;
+ break;
+ }
+ case 2:
+ {
+ /* left boundary */
+ x1 = 1;
+ y1 = randint(cur_hgt - 2) + 1;
+ break;
+ }
+ case 3:
+ {
+ /* right boundary */
+ x1 = cur_wid - 1;
+ y1 = randint(cur_hgt - 2) + 1;
+ break;
+ }
+ case 4:
+ {
+ /* bottom boundary */
+ x1 = randint(cur_wid - 2) + 1;
+ y1 = cur_hgt - 1;
+ break;
+ }
+ }
+ wid = randint(DUN_WAT_RNG);
+ recursive_river(x1, y1, x2, y2, feat1, feat2, wid);
+}
+
+
+/*
+ * Places "streamers" of rock through dungeon
+ *
+ * Note that their are actually six different terrain features used
+ * to represent streamers. Three each of magma and quartz, one for
+ * basic vein, one with hidden gold, and one with known gold. The
+ * hidden gold types are currently unused.
+ */
+static void build_streamer(int feat, int chance)
+{
+ int i, tx, ty;
+ int y, x, dir;
+ int dummy = 0;
+ cave_type *c_ptr;
+
+
+ /* Hack -- Choose starting point */
+ y = rand_spread(cur_hgt / 2, 10);
+ x = rand_spread(cur_wid / 2, 15);
+
+ /* Choose a random compass direction */
+ dir = ddd[rand_int(8)];
+
+ /* Place streamer into dungeon */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ dummy++;
+
+ /* One grid per density */
+ for (i = 0; i < DUN_STR_DEN; i++)
+ {
+ int d = DUN_STR_RNG;
+
+ /* Pick a nearby grid */
+ while (1)
+ {
+ ty = rand_spread(y, d);
+ tx = rand_spread(x, d);
+ if (!in_bounds2(ty, tx)) continue;
+ break;
+ }
+
+ /* Access the grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Only convert "granite" walls */
+ if ((c_ptr->feat != feat_wall_inner) &&
+ (c_ptr->feat != feat_wall_outer) &&
+ (c_ptr->feat != d_info[dungeon_type].fill_type1) &&
+ (c_ptr->feat != d_info[dungeon_type].fill_type2) &&
+ (c_ptr->feat != d_info[dungeon_type].fill_type3)) continue;
+
+ /* Clear mimic feature to avoid nasty consequences */
+ c_ptr->mimic = 0;
+
+ /* Clear previous contents, add proper vein type */
+ cave_set_feat(ty, tx, feat);
+
+ /* Hack -- Add some (known) treasure */
+ if (rand_int(chance) == 0)
+ {
+ cave_set_feat(ty, tx, c_ptr->feat + 0x04);
+ }
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place streamer!");
+ }
+ return;
+ }
+
+
+ /* Advance the streamer */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Quit before leaving the dungeon */
+ if (!in_bounds(y, x)) break;
+ }
+}
+
+
+
+/*
+ * Place streams of water, lava, & trees -KMW-
+ * This routine varies the placement based on dungeon level
+ * otherwise is similar to build_streamer
+ */
+static void build_streamer2(int feat, int killwall)
+{
+ int i, j, mid, tx, ty;
+ int y, x, dir;
+ int poolchance;
+ int poolsize;
+ cave_type *c_ptr;
+
+ poolchance = randint(10);
+
+ /* Hack -- Choose starting point */
+ y = rand_spread(cur_hgt / 2, 10);
+ x = rand_spread(cur_wid / 2, 15);
+
+ /* Choose a random compass direction */
+ dir = ddd[rand_int(8)];
+
+ /* Place streamer into dungeon */
+ if (poolchance > 2)
+ {
+ while (TRUE)
+ {
+ /* One grid per density */
+ for (i = 0; i < (DUN_STR_DWLW + 1); i++)
+ {
+ int d = DUN_STR_WLW;
+
+ /* Pick a nearby grid */
+ while (1)
+ {
+ ty = rand_spread(y, d);
+ tx = rand_spread(x, d);
+ if (in_bounds(ty, tx)) break;
+ }
+
+ /* Access grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Never convert vaults */
+ if (c_ptr->info & (CAVE_ICKY)) continue;
+
+ /* Reject permanent features */
+ if ((f_info[c_ptr->feat].flags1 & (FF1_PERMANENT)) &&
+ (f_info[c_ptr->feat].flags1 & (FF1_FLOOR))) continue;
+
+ /* Avoid converting walls when told so */
+ if (killwall == 0)
+ {
+ if (f_info[c_ptr->feat].flags1 & FF1_WALL) continue;
+ }
+
+ /* Clear mimic feature to avoid nasty consequences */
+ c_ptr->mimic = 0;
+
+ /* Clear previous contents, add proper vein type */
+ cave_set_feat(ty, tx, feat);
+ }
+
+ /* Advance the streamer */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Change direction */
+ if (rand_int(20) == 0) dir = ddd[rand_int(8)];
+
+ /* Stop at dungeon edge */
+ if (!in_bounds(y, x)) break;
+ }
+ }
+
+ /* Create pool */
+ else if ((feat == FEAT_DEEP_WATER) || (feat == FEAT_DEEP_LAVA))
+ {
+ poolsize = 5 + randint(10);
+ mid = poolsize / 2;
+
+ /* One grid per density */
+ for (i = 0; i < poolsize; i++)
+ {
+ for (j = 0; j < poolsize; j++)
+ {
+ tx = x + j;
+ ty = y + i;
+
+ if (!in_bounds(ty, tx)) continue;
+
+ if (i < mid)
+ {
+ if (j < mid)
+ {
+ if ((i + j + 1) < mid)
+ continue;
+ }
+ else if (j > (mid + i))
+ continue;
+ }
+ else if (j < mid)
+ {
+ if (i > (mid + j))
+ continue;
+ }
+ else if ((i + j) > ((mid * 3)-1))
+ continue;
+
+ /* Only convert non-permanent features */
+ if (f_info[cave[ty][tx].feat].flags1 & FF1_PERMANENT) continue;
+
+ /* Clear mimic feature to avoid nasty consequences */
+ cave[ty][tx].mimic = 0;
+
+ /* Clear previous contents, add proper vein type */
+ cave_set_feat(ty, tx, feat);
+ }
+ }
+ }
+}
+
+
+
+/*
+ * Build a destroyed level
+ */
+static void destroy_level(void)
+{
+ int y1, x1, y, x, k, t, n;
+
+ cave_type *c_ptr;
+
+ /* Note destroyed levels */
+ if ((cheat_room) || (p_ptr->precognition)) msg_print("Destroyed Level");
+
+ /* Drop a few epi-centers (usually about two) */
+ for (n = 0; n < randint(5); n++)
+ {
+ /* Pick an epi-center */
+ x1 = rand_range(5, cur_wid - 1 - 5);
+ y1 = rand_range(5, cur_hgt - 1 - 5);
+
+ /* Big area of affect */
+ for (y = (y1 - 15); y <= (y1 + 15); y++)
+ {
+ for (x = (x1 - 15); x <= (x1 + 15); x++)
+ {
+ /* Skip illegal grids */
+ if (!in_bounds(y, x)) continue;
+
+ /* Extract the distance */
+ k = distance(y1, x1, y, x);
+
+ /* Stay in the circle of death */
+ if (k >= 16) continue;
+
+ /* Delete the monster (if any) */
+ delete_monster(y, x);
+
+ /* Destroy valid grids */
+ if (cave_valid_bold(y, x))
+ {
+ /* Delete objects */
+ delete_object(y, x);
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Wall (or floor) type */
+ t = rand_int(200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 60)
+ {
+ /* Create quartz vein */
+ cave_set_feat(y, x, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 90)
+ {
+ /* Create magma vein */
+ cave_set_feat(y, x, FEAT_MAGMA);
+ }
+
+ /* Sand */
+ else if (t < 110)
+ {
+ /* Create sand vein */
+ cave_set_feat(y, x, FEAT_SANDWALL);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ place_floor(y, x);
+ }
+
+ /* No longer part of a room or vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* No longer illuminated or known */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Function that sees if a square is a floor (Includes range checking)
+ */
+static bool_ get_is_floor(int x, int y)
+{
+ /* Out of bounds */
+ if (!in_bounds(y, x)) return (FALSE);
+
+ /* Do the real check: */
+ if (f_info[cave[y][x].feat].flags1 & FF1_FLOOR) return (TRUE);
+
+ return (FALSE);
+}
+
+
+/*
+ * Tunnel around a room if it will cut off part of a cave system
+ */
+static void check_room_boundary(int x1, int y1, int x2, int y2)
+{
+ int count, x, y;
+ bool_ old_is_floor, new_is_floor;
+
+ /* Avoid doing this in irrelevant places -- pelpel */
+ if (!(dungeon_flags1 & DF1_CAVERN)) return;
+
+ /* Initialize */
+ count = 0;
+
+ old_is_floor = get_is_floor(x1 - 1, y1);
+
+ /*
+ * Count the number of floor-wall boundaries around the room
+ * Note: diagonal squares are ignored since the player can move diagonally
+ * to bypass these if needed.
+ */
+
+ /* Above the top boundary */
+ for (x = x1; x <= x2; x++)
+ {
+ new_is_floor = get_is_floor(x, y1 - 1);
+
+ /* increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+ /* Right boundary */
+ for (y = y1; y <= y2; y++)
+ {
+ new_is_floor = get_is_floor(x2 + 1, y);
+
+ /* increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+ /* Bottom boundary*/
+ for (x = x2; x >= x1; x--)
+ {
+ new_is_floor = get_is_floor(x, y2 + 1);
+
+ /* Increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+ /* Left boundary */
+ for (y = y2; y >= y1; y--)
+ {
+ new_is_floor = get_is_floor(x1 - 1, y);
+
+ /* Increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+
+ /* If all the same, or only one connection exit */
+ if ((count == 0) || (count == 2)) return;
+
+
+ /* Tunnel around the room so to prevent problems with caves */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ if (in_bounds(y, x)) place_floor(y, x);
+ }
+ }
+}
+
+
+/*
+ * Create up to "num" objects near the given coordinates
+ * Only really called by some of the "vault" routines.
+ */
+static void vault_objects(int y, int x, int num)
+{
+ int dummy = 0;
+ int i = 0, j = y, k = x;
+
+
+ /* Attempt to place 'num' objects */
+ for (; num > 0; --num)
+ {
+ /* Try up to 11 spots looking for empty space */
+ for (i = 0; i < 11; ++i)
+ {
+ /* Pick a random location */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ j = rand_spread(y, 2);
+ k = rand_spread(x, 3);
+ dummy++;
+ if (in_bounds(j, k)) break;
+ }
+
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place vault object!");
+ }
+ }
+
+
+ /* Require "clean" floor space */
+ if (!cave_clean_bold(j, k)) continue;
+
+ /* Place an item */
+ if (rand_int(100) < 75)
+ {
+ place_object(j, k, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+
+ /* Place gold */
+ else
+ {
+ place_gold(j, k);
+ }
+
+ /* Placement accomplished */
+ break;
+ }
+ }
+}
+
+
+/*
+ * Place a trap with a given displacement of point
+ */
+static void vault_trap_aux(int y, int x, int yd, int xd)
+{
+ int count = 0, y1 = y, x1 = x;
+ int dummy = 0;
+
+ /* Place traps */
+ for (count = 0; count <= 5; count++)
+ {
+ /* Get a location */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ y1 = rand_spread(y, yd);
+ x1 = rand_spread(x, xd);
+ dummy++;
+ if (in_bounds(y1, x1)) break;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place vault trap!");
+ }
+ }
+
+
+ /* Require "naked" floor grids */
+ if (!cave_naked_bold(y1, x1)) continue;
+
+ /* Place the trap */
+ place_trap(y1, x1);
+
+ /* Done */
+ break;
+ }
+}
+
+
+/*
+ * Place some traps with a given displacement of given location
+ */
+static void vault_traps(int y, int x, int yd, int xd, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ {
+ vault_trap_aux(y, x, yd, xd);
+ }
+}
+
+
+/*
+ * Hack -- Place some sleeping monsters near the given location
+ */
+static void vault_monsters(int y1, int x1, int num)
+{
+ int k, i, y, x;
+
+ /* Try to summon "num" monsters "near" the given location */
+ for (k = 0; k < num; k++)
+ {
+ /* Try nine locations */
+ for (i = 0; i < 9; i++)
+ {
+ int d = 1;
+
+ /* Pick a nearby location */
+ scatter(&y, &x, y1, x1, d);
+
+ /* Require "empty" floor grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Place the monster (allow groups) */
+ monster_level = dun_level + 2;
+ (void)place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ }
+}
+
+/*
+ * Allocate the space needed by a room in the room_map array.
+ *
+ * width, height represent the size of the room (0...x-1) by (0...y-1).
+ * crowded is used to denote a monset nest.
+ * by0, bx0 are the positions in the room_map array given to the build_type'x'
+ * function.
+ * cx, cy are the returned center of the allocated room in coordinates for
+ * cave.feat and cave.info etc.
+ */
+bool_ room_alloc(int width, int height, bool_ crowded, int by0, int bx0, int *cx, int *cy)
+{
+ int temp, eby, ebx, by, bx;
+
+ /* Calculate number of room_map squares to allocate */
+
+ /* Total number along width */
+ temp = ((width - 1) / BLOCK_WID) + 1;
+
+ for (ebx = bx0 + temp; bx0 > 0 && ebx > dun->col_rooms; bx0--, ebx--);
+
+ if (ebx > dun->col_rooms) return (FALSE);
+
+ /* Total number along height */
+ temp = ((height - 1) / BLOCK_HGT) + 1;
+
+ for (eby = by0 + temp; by0 > 0 && eby > dun->row_rooms; by0--, eby--);
+
+ /* Never run off the screen */
+ if (eby > dun->row_rooms) return (FALSE);
+
+ /* Verify open space */
+ for (by = by0; by < eby; by++)
+ {
+ for (bx = bx0; bx < ebx; bx++)
+ {
+ if (dun->room_map[by][bx]) return (FALSE);
+ }
+ }
+
+ /*
+ * It is *extremely* important that the following calculation
+ * be *exactly* correct to prevent memory errors XXX XXX XXX
+ */
+
+ /* Acquire the location of the room */
+ *cy = ((by0 + eby) * BLOCK_HGT) / 2;
+ *cx = ((bx0 + ebx) * BLOCK_WID) / 2;
+
+ /* Save the room location */
+ if (dun->cent_n < CENT_MAX)
+ {
+ dun->cent[dun->cent_n].y = *cy;
+ dun->cent[dun->cent_n].x = *cx;
+ dun->cent_n++;
+ }
+
+ /* Reserve some blocks */
+ for (by = by0; by < eby; by++)
+ {
+ for (bx = bx0; bx < ebx; bx++)
+ {
+ dun->room_map[by][bx] = TRUE;
+ }
+ }
+
+ /* Count "crowded" rooms */
+ if (crowded) dun->crowded = TRUE;
+
+ /*
+ * Hack -- See if room will cut off a cavern.
+ * If so, fix by tunneling outside the room in such a way as
+ * to conect the caves.
+ */
+ check_room_boundary(*cx - width / 2 - 1, *cy - height / 2 - 1,
+ *cx + width / 2 + 1, *cy + height / 2 + 1);
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * Room building routines.
+ *
+ * Room types:
+ * 1 -- normal
+ * 2 -- overlapping
+ * 3 -- cross shaped
+ * 4 -- large room with features
+ * 5 -- monster nests
+ * 6 -- monster pits
+ * 7 -- simple vaults
+ * 8 -- greater vaults
+ * 9 -- circular rooms
+ */
+
+/*
+ * Type 1 -- normal rectangular rooms
+ */
+static void build_type1(int by0, int bx0)
+{
+ u16b info;
+ int y, x = 1, y2, x2, yval, xval;
+ int y1, x1, xsize, ysize;
+
+ /* Pick a room size */
+ y1 = rand_range(1, 4);
+ x1 = rand_range(1, 10);
+ y2 = rand_range(1, 3);
+ x2 = rand_range(1, 9);
+
+ xsize = x1 + x2;
+ ysize = y1 + y2;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* Get corner values */
+ y1 = yval - ysize / 2;
+ x1 = xval - xsize / 2;
+ y2 = y1 + ysize - 1;
+ x2 = x1 + xsize - 1;
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place a full floor under the room */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Walls around the room */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, info);
+
+ /* Hack -- Occasional pillar room */
+ if (ysize > 2 && xsize > 2)
+ {
+ if (rand_int(20) == 0)
+ {
+ for (y = y1; y <= y2; y += 2)
+ {
+ for (x = x1; x <= x2; x += 2)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Hack -- Occasional ragged-edge room */
+ else if (rand_int(50) == 0)
+ {
+ for (y = y1 + 2; y <= y2 - 2; y += 2)
+ {
+ cave_set_feat(y, x1, feat_wall_inner);
+ cave_set_feat(y, x2, feat_wall_inner);
+ }
+ for (x = x1 + 2; x <= x2 - 2; x += 2)
+ {
+ cave_set_feat(y1, x, feat_wall_inner);
+ cave_set_feat(y2, x, feat_wall_inner);
+ }
+ }
+ }
+}
+
+/*
+ * Type 2 -- Overlapping rectangular rooms
+ */
+static void build_type2(int by0, int bx0)
+{
+ u16b info;
+ int y, x, yval, xval;
+ int y1a, x1a, y2a, x2a;
+ int y1b, x1b, y2b, x2b;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* Determine extents of the first room */
+ y1a = yval - randint(4);
+ y2a = yval + randint(3);
+ x1a = xval - randint(14);
+ x2a = xval + randint(6);
+
+ /* Determine extents of the second room */
+ y1b = yval - randint(3);
+ y2b = yval + randint(4);
+ x1b = xval - randint(6);
+ x2b = xval + randint(14);
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1a - 1, x1a - 1, y2a + 1, x2a + 1, feat_wall_outer, info);
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1b - 1, x1b - 1, y2b + 1, x2b + 1, feat_wall_outer, info);
+
+ /* Replace the floor for room "a" */
+ for (y = y1a; y <= y2a; y++)
+ {
+ for (x = x1a; x <= x2a; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Replace the floor for room "b" */
+ for (y = y1b; y <= y2b; y++)
+ {
+ for (x = x1b; x <= x2b; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+}
+
+/*
+ * Type 3 -- Cross shaped rooms
+ *
+ * Builds a room at a row, column coordinate
+ *
+ * Room "a" runs north/south, and Room "b" runs east/east
+ * So the "central pillar" runs from x1a,y1b to x2a,y2b.
+ *
+ * Note that currently, the "center" is always 3x3, but I think that
+ * the code below will work (with "bounds checking") for 5x5, or even
+ * for unsymetric values like 4x3 or 5x3 or 3x4 or 3x5, or even larger.
+ */
+static void build_type3(int by0, int bx0)
+{
+ u16b info;
+ int y, x, dy, dx, wy, wx;
+ int y1a, x1a, y2a, x2a;
+ int y1b, x1b, y2b, x2b;
+ int yval, xval;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* For now, always 3x3 */
+ wx = wy = 1;
+
+ /* Pick max vertical size (at most 4) */
+ dy = rand_range(3, 4);
+
+ /* Pick max horizontal size (at most 11) */
+ dx = rand_range(3, 11);
+
+ /* Determine extents of the north/south room */
+ y1a = yval - dy;
+ y2a = yval + dy;
+ x1a = xval - wx;
+ x2a = xval + wx;
+
+ /* Determine extents of the east/west room */
+ y1b = yval - wy;
+ y2b = yval + wy;
+ x1b = xval - dx;
+ x2b = xval + dx;
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1a - 1, x1a - 1, y2a + 1, x2a + 1, feat_wall_outer, info);
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1b - 1, x1b - 1, y2b + 1, x2b + 1, feat_wall_outer, info);
+
+ /* Replace the floor for room "a" */
+ for (y = y1a; y <= y2a; y++)
+ {
+ for (x = x1a; x <= x2a; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Replace the floor for room "b" */
+ for (y = y1b; y <= y2b; y++)
+ {
+ for (x = x1b; x <= x2b; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Special features (3/4) */
+ switch (rand_int(4))
+ {
+ /* Large solid middle pillar */
+ case 1:
+ {
+ for (y = y1b; y <= y2b; y++)
+ {
+ for (x = x1a; x <= x2a; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ break;
+ }
+
+ /* Inner treasure vault */
+ case 2:
+ {
+ /* Build the vault */
+ build_rectangle(y1b, x1a, y2b, x2a, feat_wall_inner, info);
+
+ /* Place a secret door on the inner room */
+ switch (rand_int(4))
+ {
+ case 0:
+ place_secret_door(y1b, xval);
+ break;
+ case 1:
+ place_secret_door(y2b, xval);
+ break;
+ case 2:
+ place_secret_door(yval, x1a);
+ break;
+ case 3:
+ place_secret_door(yval, x2a);
+ break;
+ }
+
+ /* Place a treasure in the vault */
+ place_object(yval, xval, FALSE, FALSE, OBJ_FOUND_FLOOR);
+
+ /* Let's guard the treasure well */
+ vault_monsters(yval, xval, rand_int(2) + 3);
+
+ /* Traps naturally */
+ vault_traps(yval, xval, 4, 4, rand_int(3) + 2);
+
+ break;
+ }
+
+ /* Something else */
+ case 3:
+ {
+ /* Occasionally pinch the center shut */
+ if (rand_int(3) == 0)
+ {
+ /* Pinch the east/west sides */
+ for (y = y1b; y <= y2b; y++)
+ {
+ if (y == yval) continue;
+ cave_set_feat(y, x1a - 1, feat_wall_inner);
+ cave_set_feat(y, x2a + 1, feat_wall_inner);
+ }
+
+ /* Pinch the north/south sides */
+ for (x = x1a; x <= x2a; x++)
+ {
+ if (x == xval) continue;
+ cave_set_feat(y1b - 1, x, feat_wall_inner);
+ cave_set_feat(y2b + 1, x, feat_wall_inner);
+ }
+
+ /* Sometimes shut using secret doors */
+ if (rand_int(3) == 0)
+ {
+ place_secret_door(yval, x1a - 1);
+ place_secret_door(yval, x2a + 1);
+ place_secret_door(y1b - 1, xval);
+ place_secret_door(y2b + 1, xval);
+ }
+ }
+
+ /* Occasionally put a "plus" in the center */
+ else if (rand_int(3) == 0)
+ {
+ cave_set_feat(yval, xval, feat_wall_inner);
+ cave_set_feat(y1b, xval, feat_wall_inner);
+ cave_set_feat(y2b, xval, feat_wall_inner);
+ cave_set_feat(yval, x1a, feat_wall_inner);
+ cave_set_feat(yval, x2a, feat_wall_inner);
+ }
+
+ /* Occasionally put a pillar in the center */
+ else if (rand_int(3) == 0)
+ {
+ cave_set_feat(yval, xval, feat_wall_inner);
+ }
+
+ break;
+ }
+ }
+}
+
+/*
+ * Type 4 -- Large room with inner features
+ *
+ * Possible sub-types:
+ * 1 - Just an inner room with one door
+ * 2 - An inner room within an inner room
+ * 3 - An inner room with pillar(s)
+ * 4 - Inner room has a maze
+ * 5 - A set of four inner rooms
+ */
+static void build_type4(int by0, int bx0)
+{
+ u16b info;
+ int y, x, y1, x1;
+ int y2, x2, tmp, yval, xval;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* Large room */
+ y1 = yval - 4;
+ y2 = yval + 4;
+ x1 = xval - 11;
+ x2 = xval + 11;
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place a full floor under the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Outer Walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, info);
+
+ /* The inner room */
+ y1 = y1 + 2;
+ y2 = y2 - 2;
+ x1 = x1 + 2;
+ x2 = x2 - 2;
+
+ /* The inner walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_inner, info);
+
+ /* Inner room variations */
+ switch (randint(5))
+ {
+ /* Just an inner room with a monster */
+ case 1:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Place a monster in the room */
+ vault_monsters(yval, xval, 1);
+
+ break;
+ }
+
+ /* Treasure Vault (with a door) */
+ case 2:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Place another inner room */
+ build_rectangle(yval - 1, xval - 1, yval + 1, xval + 1,
+ feat_wall_inner, info);
+
+ /* Place a locked door on the inner room */
+ switch (randint(4))
+ {
+ case 1:
+ place_locked_door(yval - 1, xval);
+ break;
+ case 2:
+ place_locked_door(yval + 1, xval);
+ break;
+ case 3:
+ place_locked_door(yval, xval - 1);
+ break;
+ case 4:
+ place_locked_door(yval, xval + 1);
+ break;
+ }
+
+ /* Monsters to guard the "treasure" */
+ vault_monsters(yval, xval, randint(3) + 2);
+
+ /* Object (80%) */
+ if (rand_int(100) < 80)
+ {
+ place_object(yval, xval, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+
+ /* Stairs (20%) */
+ else
+ {
+ place_random_stairs(yval, xval);
+ }
+
+ /* Traps to protect the treasure */
+ vault_traps(yval, xval, 4, 10, 2 + randint(3));
+
+ break;
+ }
+
+ /* Inner pillar(s). */
+ case 3:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Large Inner Pillar */
+ for (y = yval - 1; y <= yval + 1; y++)
+ {
+ for (x = xval - 1; x <= xval + 1; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+
+ /* Occasionally, two more Large Inner Pillars */
+ if (rand_int(2) == 0)
+ {
+ tmp = randint(2);
+ for (y = yval - 1; y <= yval + 1; y++)
+ {
+ for (x = xval - 5 - tmp; x <= xval - 3 - tmp; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ for (x = xval + 3 + tmp; x <= xval + 5 + tmp; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Occasionally, some Inner rooms */
+ if (rand_int(3) == 0)
+ {
+ /* Long horizontal walls */
+ for (x = xval - 5; x <= xval + 5; x++)
+ {
+ cave_set_feat(yval - 1, x, feat_wall_inner);
+ cave_set_feat(yval + 1, x, feat_wall_inner);
+ }
+
+ /* Close off the left/right edges */
+ cave_set_feat(yval, xval - 5, feat_wall_inner);
+ cave_set_feat(yval, xval + 5, feat_wall_inner);
+
+ /* Secret doors (random top/bottom) */
+ place_secret_door(yval - 3 + (randint(2) * 2), xval - 3);
+ place_secret_door(yval - 3 + (randint(2) * 2), xval + 3);
+
+ /* Monsters */
+ vault_monsters(yval, xval - 2, randint(2));
+ vault_monsters(yval, xval + 2, randint(2));
+
+ /* Objects */
+ if (rand_int(3) == 0) place_object(yval, xval - 2, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ if (rand_int(3) == 0) place_object(yval, xval + 2, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+
+ break;
+ }
+
+ /* Maze inside. */
+ case 4:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Maze (really a checkerboard) */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ if (0x1 & (x + y))
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Monsters just love mazes. */
+ vault_monsters(yval, xval - 5, randint(3));
+ vault_monsters(yval, xval + 5, randint(3));
+
+ /* Traps make them entertaining. */
+ vault_traps(yval, xval - 3, 2, 8, randint(3));
+ vault_traps(yval, xval + 3, 2, 8, randint(3));
+
+ /* Mazes should have some treasure too. */
+ vault_objects(yval, xval, 3);
+
+ break;
+ }
+
+ /* Four small rooms. */
+ case 5:
+ {
+ /* Inner "cross" */
+ for (y = y1; y <= y2; y++)
+ {
+ cave_set_feat(y, xval, feat_wall_inner);
+ }
+
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(yval, x, feat_wall_inner);
+ }
+
+ /* Doors into the rooms */
+ if (rand_int(100) < 50)
+ {
+ int i = randint(10);
+ place_secret_door(y1 - 1, xval - i);
+ place_secret_door(y1 - 1, xval + i);
+ place_secret_door(y2 + 1, xval - i);
+ place_secret_door(y2 + 1, xval + i);
+ }
+ else
+ {
+ int i = randint(3);
+ place_secret_door(yval + i, x1 - 1);
+ place_secret_door(yval - i, x1 - 1);
+ place_secret_door(yval + i, x2 + 1);
+ place_secret_door(yval - i, x2 + 1);
+ }
+
+ /* Treasure, centered at the center of the cross */
+ vault_objects(yval, xval, 2 + randint(2));
+
+ /* Gotta have some monsters. */
+ vault_monsters(yval + 1, xval - 4, randint(4));
+ vault_monsters(yval + 1, xval + 4, randint(4));
+ vault_monsters(yval - 1, xval - 4, randint(4));
+ vault_monsters(yval - 1, xval + 4, randint(4));
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Determine if the given monster is appropriate for inclusion in
+ * a monster nest or monster pit or the given type.
+ *
+ * None of the pits/nests are allowed to include "unique" monsters,
+ * or monsters which can "multiply".
+ *
+ * Some of the pits/nests are asked to avoid monsters which can blink
+ * away or which are invisible. This is probably a hack.
+ *
+ * The old method made direct use of monster "names", which is bad.
+ *
+ * Note the use of Angband 2.7.9 monster race pictures in various places.
+ */
+
+
+/*
+ * Helper function for "monster nest (jelly)"
+ */
+static bool_ vault_aux_jelly(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Also decline evil jellies (like death molds and shoggoths) */
+ if (r_ptr->flags3 & (RF3_EVIL)) return (FALSE);
+
+ /* Require icky thing, jelly, mold, or mushroom */
+ if (!strchr("ijm,", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (animal)"
+ */
+static bool_ vault_aux_animal(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require "animal" flag */
+ if (!(r_ptr->flags3 & (RF3_ANIMAL))) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (undead)"
+ */
+static bool_ vault_aux_undead(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require Undead */
+ if (!(r_ptr->flags3 & (RF3_UNDEAD))) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (chapel)"
+ */
+static bool_ vault_aux_chapel(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require "priest" or Angel */
+ if (!((r_ptr->d_char == 'A') ||
+ strstr((r_name + r_ptr->name), "riest")))
+ {
+ return (FALSE);
+ }
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (kennel)"
+ */
+static bool_ vault_aux_kennel(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require a Zephyr Hound or a dog */
+ return ((r_ptr->d_char == 'Z') || (r_ptr->d_char == 'C'));
+
+}
+
+
+/*
+ * Helper function for "monster nest (treasure)"
+ */
+static bool_ vault_aux_treasure(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require "priest" or Angel */
+ if (!((r_ptr->d_char == '!') || (r_ptr->d_char == '|') ||
+ (r_ptr->d_char == '$') || (r_ptr->d_char == '?') ||
+ (r_ptr->d_char == '=')))
+ {
+ return (FALSE);
+ }
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (clone)"
+ */
+static bool_ vault_aux_clone(int r_idx)
+{
+ return (r_idx == template_race);
+}
+
+
+/*
+ * Helper function for "monster nest (symbol clone)"
+ */
+static bool_ vault_aux_symbol(int r_idx)
+{
+ return ((r_info[r_idx].d_char == (r_info[template_race].d_char))
+ && !(r_info[r_idx].flags1 & RF1_UNIQUE));
+}
+
+
+/*
+ * Helper function for "monster pit (orc)"
+ */
+static bool_ vault_aux_orc(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "o" monsters */
+ if (!strchr("o", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+
+/*
+ * Helper function for "monster pit (troll)"
+ */
+static bool_ vault_aux_troll(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "T" monsters */
+ if (!strchr("T", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster pit (giant)"
+ */
+static bool_ vault_aux_giant(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "P" monsters */
+ if (!strchr("P", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- breath type for "vault_aux_dragon()"
+ */
+static u32b vault_aux_dragon_mask4;
+
+
+/*
+ * Helper function for "monster pit (dragon)"
+ */
+static bool_ vault_aux_dragon(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "d" or "D" monsters */
+ if (!strchr("Dd", r_ptr->d_char)) return (FALSE);
+
+ /* Hack -- Require correct "breath attack" */
+ if (r_ptr->flags4 != vault_aux_dragon_mask4) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster pit (demon)"
+ */
+static bool_ vault_aux_demon(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "U" monsters */
+ if (!strchr("U", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Type 5 -- Monster nests
+ *
+ * A monster nest is a "big" room, with an "inner" room, containing
+ * a "collection" of monsters of a given type strewn about the room.
+ *
+ * The monsters are chosen from a set of 64 randomly selected monster
+ * races, to allow the nest creation to fail instead of having "holes".
+ *
+ * Note the use of the "get_mon_num_prep()" function, and the special
+ * "get_mon_num_hook()" restriction function, to prepare the "monster
+ * allocation table" in such a way as to optimize the selection of
+ * "appropriate" non-unique monsters for the nest.
+ *
+ * Currently, a monster nest is one of
+ * a nest of "jelly" monsters (Dungeon level 5 and deeper)
+ * a nest of "animal" monsters (Dungeon level 30 and deeper)
+ * a nest of "undead" monsters (Dungeon level 50 and deeper)
+ *
+ * Note that the "get_mon_num()" function may (rarely) fail, in which
+ * case the nest will be empty, and will not affect the level rating.
+ *
+ * Note that "monster nests" will never contain "unique" monsters.
+ */
+static void build_type5(int by0, int bx0)
+{
+ int y, x, y1, x1, y2, x2, xval, yval;
+ int tmp, i;
+ cptr name;
+ bool_ empty = FALSE;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+ s16b what[64];
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, TRUE, by0, bx0, &xval, &yval)) return;
+
+ /* Large room */
+ y1 = yval - 4;
+ y2 = yval + 4;
+ x1 = xval - 11;
+ x2 = xval + 11;
+
+ /* Place the floor area */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= (CAVE_ROOM);
+ }
+ }
+
+ /* Place the outer walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM);
+
+ /* Advance to the center room */
+ y1 = y1 + 2;
+ y2 = y2 - 2;
+ x1 = x1 + 2;
+ x2 = x2 - 2;
+
+ /* The inner walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_inner, CAVE_ROOM);
+
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Hack -- Choose a nest type */
+ tmp = randint(dun_level);
+
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ if ((tmp < 25) && (rand_int(2) != 0))
+ {
+ while (1)
+ {
+ template_race = randint(max_r_idx - 2);
+
+ /* Reject uniques */
+ if (r_info[template_race].flags1 & RF1_UNIQUE) continue;
+
+ /* Reject OoD monsters in a loose fashion */
+ if (((r_info[template_race].level) + randint(5)) >
+ (dun_level + randint(5))) continue;
+
+ /* Don't like 'break's like this, but this cannot be made better */
+ break;
+ }
+
+ if ((dun_level >= (25 + randint(15))) && (rand_int(2) != 0))
+ {
+ name = "symbol clone";
+ get_mon_num_hook = vault_aux_symbol;
+ }
+ else
+ {
+ name = "clone";
+ get_mon_num_hook = vault_aux_clone;
+ }
+ }
+ else if (tmp < 25)
+ /* Monster nest (jelly) */
+ {
+ /* Describe */
+ name = "jelly";
+
+ /* Restrict to jelly */
+ get_mon_num_hook = vault_aux_jelly;
+ }
+
+ else if (tmp < 50)
+ {
+ name = "treasure";
+ get_mon_num_hook = vault_aux_treasure;
+ }
+
+ /* Monster nest (animal) */
+ else if (tmp < 65)
+ {
+ if (rand_int(3) == 0)
+ {
+ name = "kennel";
+ get_mon_num_hook = vault_aux_kennel;
+ }
+ else
+ {
+ /* Describe */
+ name = "animal";
+
+ /* Restrict to animal */
+ get_mon_num_hook = vault_aux_animal;
+ }
+ }
+
+ /* Monster nest (undead) */
+ else
+ {
+ if (rand_int(3) == 0)
+ {
+ name = "chapel";
+ get_mon_num_hook = vault_aux_chapel;
+ }
+ else
+ {
+ /* Describe */
+ name = "undead";
+
+ /* Restrict to undead */
+ get_mon_num_hook = vault_aux_undead;
+ }
+ }
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick some monster types */
+ for (i = 0; i < 64; i++)
+ {
+ /* Get a (hard) monster type */
+ what[i] = get_mon_num(dun_level + 10);
+
+ /* Notice failure */
+ if (!what[i]) empty = TRUE;
+ }
+
+ /* Remove restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Oops */
+ if (empty) return;
+
+ /* Describe */
+ if (cheat_room || p_ptr->precognition)
+ {
+ /* Room type */
+ msg_format("Monster nest (%s)", name);
+ }
+
+ /* Increase the level rating */
+ rating += 10;
+
+ /* (Sometimes) Cause a "special feeling" (for "Monster Nests") */
+ if ((dun_level <= 40) && (randint(dun_level * dun_level + 50) < 300))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Place some monsters */
+ for (y = yval - 2; y <= yval + 2; y++)
+ {
+ for (x = xval - 9; x <= xval + 9; x++)
+ {
+ int r_idx = what[rand_int(64)];
+
+ /* Place that "random" monster (no groups) */
+ (void)place_monster_aux(y, x, r_idx, FALSE, FALSE, MSTATUS_ENEMY);
+ }
+ }
+}
+
+
+
+/*
+ * Type 6 -- Monster pits
+ *
+ * A monster pit is a "big" room, with an "inner" room, containing
+ * a "collection" of monsters of a given type organized in the room.
+ *
+ * Monster types in the pit (list out of date...)
+ * orc pit (Dungeon Level 5 and deeper)
+ * troll pit (Dungeon Level 20 and deeper)
+ * giant pit (Dungeon Level 40 and deeper)
+ * dragon pit (Dungeon Level 60 and deeper)
+ * demon pit (Dungeon Level 80 and deeper)
+ *
+ * The inside room in a monster pit appears as shown below, where the
+ * actual monsters in each location depend on the type of the pit
+ *
+ * #####################
+ * #0000000000000000000#
+ * #0112233455543322110#
+ * #0112233467643322110#
+ * #0112233455543322110#
+ * #0000000000000000000#
+ * #####################
+ *
+ * Note that the monsters in the pit are now chosen by using "get_mon_num()"
+ * to request 16 "appropriate" monsters, sorting them by level, and using
+ * the "even" entries in this sorted list for the contents of the pit.
+ *
+ * Hack -- all of the "dragons" in a "dragon" pit must be the same "color",
+ * which is handled by requiring a specific "breath" attack for all of the
+ * dragons. This may include "multi-hued" breath. Note that "wyrms" may
+ * be present in many of the dragon pits, if they have the proper breath.
+ *
+ * Note the use of the "get_mon_num_prep()" function, and the special
+ * "get_mon_num_hook()" restriction function, to prepare the "monster
+ * allocation table" in such a way as to optimize the selection of
+ * "appropriate" non-unique monsters for the pit.
+ *
+ * Note that the "get_mon_num()" function may (rarely) fail, in which case
+ * the pit will be empty, and will not effect the level rating.
+ *
+ * Note that "monster pits" will never contain "unique" monsters.
+ */
+static void build_type6(int by0, int bx0)
+{
+ int tmp, what[16];
+ int i, j, y, x, y1, x1, y2, x2, xval, yval;
+ bool_ empty = FALSE;
+ cptr name;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, TRUE, by0, bx0, &xval, &yval)) return;
+
+ /* Large room */
+ y1 = yval - 4;
+ y2 = yval + 4;
+ x1 = xval - 11;
+ x2 = xval + 11;
+
+ /* Place the floor area */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= (CAVE_ROOM);
+ }
+ }
+
+ /* Place the outer walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM);
+
+ /* Advance to the center room */
+ y1 = y1 + 2;
+ y2 = y2 - 2;
+ x1 = x1 + 2;
+ x2 = x2 - 2;
+
+ /* The inner walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM);
+
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Choose a pit type */
+ tmp = randint(dun_level);
+
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Orc pit */
+ if (tmp < 20)
+ {
+ /* Message */
+ name = "orc";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_orc;
+ }
+
+ /* Troll pit */
+ else if (tmp < 40)
+ {
+ /* Message */
+ name = "troll";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_troll;
+ }
+
+ /* Giant pit */
+ else if (tmp < 55)
+ {
+ /* Message */
+ name = "giant";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_giant;
+ }
+
+ else if (tmp < 70)
+ {
+ if (randint(4) != 1)
+ {
+ /* Message */
+ name = "ordered clones";
+
+ do
+ {
+ template_race = randint(max_r_idx - 2);
+ }
+ while ((r_info[template_race].flags1 & RF1_UNIQUE)
+ || (((r_info[template_race].level) + randint(5)) >
+ (dun_level + randint(5))));
+
+ /* Restrict selection */
+ get_mon_num_hook = vault_aux_symbol;
+ }
+ else
+ {
+ name = "ordered chapel";
+ get_mon_num_hook = vault_aux_chapel;
+ }
+
+ }
+
+ /* Dragon pit */
+ else if (tmp < 80)
+ {
+ /* Pick dragon type */
+ switch (rand_int(6))
+ {
+ /* Black */
+ case 0:
+ {
+ /* Message */
+ name = "acid dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_ACID;
+
+ /* Done */
+ break;
+ }
+
+ /* Blue */
+ case 1:
+ {
+ /* Message */
+ name = "electric dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_ELEC;
+
+ /* Done */
+ break;
+ }
+
+ /* Red */
+ case 2:
+ {
+ /* Message */
+ name = "fire dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_FIRE;
+
+ /* Done */
+ break;
+ }
+
+ /* White */
+ case 3:
+ {
+ /* Message */
+ name = "cold dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_COLD;
+
+ /* Done */
+ break;
+ }
+
+ /* Green */
+ case 4:
+ {
+ /* Message */
+ name = "poison dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_POIS;
+
+ /* Done */
+ break;
+ }
+
+ /* Multi-hued */
+ default:
+ {
+ /* Message */
+ name = "multi-hued dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = (RF4_BR_ACID | RF4_BR_ELEC |
+ RF4_BR_FIRE | RF4_BR_COLD |
+ RF4_BR_POIS);
+
+ /* Done */
+ break;
+ }
+
+ }
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_dragon;
+ }
+
+ /* Demon pit */
+ else
+ {
+ /* Message */
+ name = "demon";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_demon;
+ }
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick some monster types */
+ for (i = 0; i < 16; i++)
+ {
+ /* Get a (hard) monster type */
+ what[i] = get_mon_num(dun_level + 10);
+
+ /* Notice failure */
+ if (!what[i]) empty = TRUE;
+ }
+
+ /* Remove restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Oops */
+ if (empty) return;
+
+ /* XXX XXX XXX */
+ /* Sort the entries */
+ for (i = 0; i < 16 - 1; i++)
+ {
+ /* Sort the entries */
+ for (j = 0; j < 16 - 1; j++)
+ {
+ int i1 = j;
+ int i2 = j + 1;
+
+ int p1 = r_info[what[i1]].level;
+ int p2 = r_info[what[i2]].level;
+
+ /* Bubble */
+ if (p1 > p2)
+ {
+ int tmp = what[i1];
+ what[i1] = what[i2];
+ what[i2] = tmp;
+ }
+ }
+ }
+
+ /* Select the entries */
+ for (i = 0; i < 8; i++)
+ {
+ /* Every other entry */
+ what[i] = what[i * 2];
+ }
+
+ /* Message */
+ if (cheat_room || p_ptr->precognition)
+ {
+ /* Room type */
+ msg_format("Monster pit (%s)", name);
+
+ if (cheat_hear || p_ptr->precognition)
+ {
+ /* Contents */
+ for (i = 0; i < 8; i++)
+ {
+ /* Message */
+ msg_print(r_name + r_info[what[i]].name);
+ }
+ }
+ }
+
+ /* Increase the level rating */
+ rating += 10;
+
+ /* (Sometimes) Cause a "special feeling" (for "Monster Pits") */
+ if ((dun_level <= 40) && (randint(dun_level * dun_level + 50) < 300))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Top and bottom rows */
+ for (x = xval - 9; x <= xval + 9; x++)
+ {
+ place_monster_aux(yval - 2, x, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(yval + 2, x, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+ }
+
+ /* Middle columns */
+ for (y = yval - 1; y <= yval + 1; y++)
+ {
+ place_monster_aux(y, xval - 9, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 9, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 8, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 8, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 7, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 7, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 6, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 6, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 5, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 5, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 4, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 4, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 3, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 3, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 2, what[4], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 2, what[4], FALSE, FALSE, MSTATUS_ENEMY);
+ }
+
+ /* Above/Below the center monster */
+ for (x = xval - 1; x <= xval + 1; x++)
+ {
+ place_monster_aux(yval + 1, x, what[5], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(yval - 1, x, what[5], FALSE, FALSE, MSTATUS_ENEMY);
+ }
+
+ /* Next to the center monster */
+ place_monster_aux(yval, xval + 1, what[6], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(yval, xval - 1, what[6], FALSE, FALSE, MSTATUS_ENEMY);
+
+ /* Center monster */
+ place_monster_aux(yval, xval, what[7], FALSE, FALSE, MSTATUS_ENEMY);
+}
+
+/*
+ * Hack -- fill in "vault" rooms
+ */
+static void build_vault(int yval, int xval, int ymax, int xmax, cptr data)
+{
+ int dx, dy, x, y, bwy[8], bwx[8], i;
+
+ cptr t;
+
+ cave_type *c_ptr;
+
+ /* Clean the between gates arrays */
+ for (i = 0; i < 8; i++)
+ {
+ bwy[i] = bwx[i] = 9999;
+ }
+
+ /* Place dungeon features and objects */
+ for (t = data, dy = 0; dy < ymax; dy++)
+ {
+ for (dx = 0; dx < xmax; dx++, t++)
+ {
+ /* Extract the location */
+ x = xval - (xmax / 2) + dx;
+ y = yval - (ymax / 2) + dy;
+
+ /* Hack -- skip "non-grids" */
+ if (*t == ' ') continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Lay down a floor */
+ place_floor(y, x);
+
+ /* Part of a vault */
+ c_ptr->info |= (CAVE_ROOM | CAVE_ICKY);
+
+ /* Analyze the grid */
+ switch (*t)
+ {
+ /* Granite wall (outer) */
+ case '%':
+ {
+ cave_set_feat(y, x, FEAT_WALL_OUTER);
+ break;
+ }
+
+ /* Granite wall (inner) */
+ case '#':
+ {
+ cave_set_feat(y, x, FEAT_WALL_INNER);
+ break;
+ }
+
+ /* Permanent wall (inner) */
+ case 'X':
+ {
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+ break;
+ }
+
+ /* Treasure/trap */
+ case '*':
+ {
+ if (rand_int(100) < 75)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_VAULT);
+ }
+ else
+ {
+ place_trap(y, x);
+ }
+ break;
+ }
+
+ /* Secret doors */
+ case '+':
+ {
+ place_secret_door(y, x);
+ break;
+ }
+
+ /* Trap */
+ case '^':
+ {
+ place_trap(y, x);
+ break;
+ }
+
+ /* Glass wall */
+ case 'G':
+ {
+ cave_set_feat(y, x, FEAT_GLASS_WALL);
+ break;
+ }
+
+ /* Illusion wall */
+ case 'I':
+ {
+ cave_set_feat(y, x, FEAT_ILLUS_WALL);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Place dungeon monsters and objects */
+ for (t = data, dy = 0; dy < ymax; dy++)
+ {
+ for (dx = 0; dx < xmax; dx++, t++)
+ {
+ /* Extract the grid */
+ x = xval - (xmax / 2) + dx;
+ y = yval - (ymax / 2) + dy;
+
+ /* Hack -- skip "non-grids" */
+ if (*t == ' ') continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Analyze the symbol */
+ switch (*t)
+ {
+ /* Monster */
+ case '&':
+ {
+ monster_level = dun_level + 5;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ break;
+ }
+
+ /* Meaner monster */
+ case '@':
+ {
+ monster_level = dun_level + 11;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ break;
+ }
+
+ /* Meaner monster, plus treasure */
+ case '9':
+ {
+ monster_level = dun_level + 9;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 7;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ break;
+ }
+
+ /* Nasty monster and treasure */
+ case '8':
+ {
+ monster_level = dun_level + 40;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 20;
+ place_object(y, x, TRUE, TRUE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ break;
+ }
+
+ /* Monster and/or object */
+ case ',':
+ {
+ if (rand_int(100) < 50)
+ {
+ monster_level = dun_level + 3;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ if (rand_int(100) < 50)
+ {
+ object_level = dun_level + 7;
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ }
+ break;
+ }
+
+ case 'p':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_START);
+ break;
+ }
+
+ case 'a':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_1);
+ break;
+ }
+
+ case 'b':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_2);
+ break;
+ }
+
+ case 'c':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_3);
+ break;
+ }
+
+ case 'd':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_4);
+ break;
+ }
+
+ case 'P':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_END);
+ break;
+ }
+
+ case 'B':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_XTRA1);
+ break;
+ }
+
+ case 'A':
+ {
+ object_level = dun_level + 12;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ break;
+ }
+
+
+ /* Between gates */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ /* Not found before */
+ if (bwy[*t - '0'] == 9999)
+ {
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ bwy[*t - '0'] = y;
+ bwx[*t - '0'] = x;
+ }
+ /* The second time */
+ else
+ {
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ c_ptr->special = bwx[*t - '0'] + (bwy[*t - '0'] << 8);
+ cave[bwy[*t - '0']][bwx[*t - '0']].special = x + (y << 8);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Type 7 -- simple vaults (see "v_info.txt")
+ */
+static void build_type7(int by0, int bx0)
+{
+ vault_type *v_ptr = NULL;
+ int dummy = 0, xval, yval;
+
+ /* Pick a lesser vault */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ dummy++;
+
+ /* Access a random vault record */
+ v_ptr = &v_info[rand_int(max_v_idx)];
+
+ /* Accept the first lesser vault */
+ if (v_ptr->typ == 7) break;
+ }
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(v_ptr->wid, v_ptr->hgt, FALSE, by0, bx0, &xval, &yval))
+ {
+ if (cheat_room) msg_print("Could not allocate this vault here");
+ return;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place lesser vault!");
+ }
+ return;
+ }
+
+
+#ifdef FORCE_V_IDX
+ v_ptr = &v_info[FORCE_V_IDX];
+#endif
+
+ /* Message */
+ if (cheat_room || p_ptr->precognition) msg_print("Lesser Vault");
+
+ /* Boost the rating */
+ rating += v_ptr->rat;
+
+ /* (Sometimes) Cause a special feeling */
+ if ((dun_level <= 50) ||
+ (randint((dun_level - 40) * (dun_level - 40) + 50) < 400))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Hack -- Build the vault */
+ build_vault(yval, xval, v_ptr->hgt, v_ptr->wid, v_text + v_ptr->text);
+}
+
+
+
+/*
+ * Type 8 -- greater vaults (see "v_info.txt")
+ */
+static void build_type8(int by0, int bx0)
+{
+ vault_type *v_ptr = NULL;
+ int dummy = 0, xval, yval;
+
+ /* Pick a lesser vault */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ dummy++;
+
+ /* Access a random vault record */
+ v_ptr = &v_info[rand_int(max_v_idx)];
+
+ /* Accept the first greater vault */
+ if (v_ptr->typ == 8) break;
+ }
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(v_ptr->wid, v_ptr->hgt, FALSE, by0, bx0, &xval, &yval))
+ {
+ if (cheat_room) msg_print("Could not allocate this vault here");
+ return;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place greater vault!");
+ }
+ return;
+ }
+
+
+#ifdef FORCE_V_IDX
+ v_ptr = &v_info[FORCE_V_IDX];
+#endif
+
+ /* Message */
+ if (cheat_room || p_ptr->precognition) msg_print("Greater Vault");
+
+ /* Boost the rating */
+ rating += v_ptr->rat;
+
+ /* (Sometimes) Cause a special feeling */
+ if ((dun_level <= 50) ||
+ (randint((dun_level - 40) * (dun_level - 40) + 50) < 400))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Hack -- Build the vault */
+ build_vault(yval, xval, v_ptr->hgt, v_ptr->wid, v_text + v_ptr->text);
+}
+
+/*
+ * DAG:
+ * Build an vertical oval room.
+ * For every grid in the possible square, check the distance.
+ * If it's less than or == than the radius, make it a room square.
+ * If its less, make it a normal grid. If it's == make it an outer
+ * wall.
+ */
+static void build_type9(int by0, int bx0)
+{
+ u16b info;
+ int rad, x, y, x0, y0;
+
+ rad = 2 + rand_int(8);
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(rad*2 + 1, rad*2 + 1, FALSE, by0, bx0, &x0, &y0)) return;
+
+ info = (randint(dun_level) <= 5) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ if (distance(y0, x0, y, x) == rad)
+ {
+ cave[y][x].info |= info;
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+
+ if (distance(y0, x0, y, x) < rad)
+ {
+ cave[y][x].info |= info;
+ place_floor(y, x);
+ }
+ }
+ }
+}
+
+
+/*
+ * Store routine for the fractal cave generator
+ * this routine probably should be an inline function or a macro
+ */
+static void store_height(int x, int y, int x0, int y0, byte val,
+ int xhsize, int yhsize, int cutoff)
+{
+ /* Only write to points that are "blank" */
+ if (cave[y + y0 - yhsize][x + x0 - xhsize].feat != 255) return;
+
+ /* If on boundary set val > cutoff so walls are not as square */
+ if (((x == 0) || (y == 0) || (x == xhsize * 2) || (y == yhsize * 2)) &&
+ (val <= cutoff)) val = cutoff + 1;
+
+ /* Store the value in height-map format */
+ /* Meant to be temporary, hence no cave_set_feat */
+ cave[y + y0 - yhsize][x + x0 - xhsize].feat = val;
+
+ return;
+}
+
+
+
+/*
+ * Explanation of the plasma fractal algorithm:
+ *
+ * A grid of points is created with the properties of a 'height-map'
+ * This is done by making the corners of the grid have a random value.
+ * The grid is then subdivided into one with twice the resolution.
+ * The new points midway between two 'known' points can be calculated
+ * by taking the average value of the 'known' ones and randomly adding
+ * or subtracting an amount proportional to the distance between those
+ * points. The final 'middle' points of the grid are then calculated
+ * by averaging all four of the originally 'known' corner points. An
+ * random amount is added or subtracted from this to get a value of the
+ * height at that point. The scaling factor here is adjusted to the
+ * slightly larger distance diagonally as compared to orthogonally.
+ *
+ * This is then repeated recursively to fill an entire 'height-map'
+ * A rectangular map is done the same way, except there are different
+ * scaling factors along the x and y directions.
+ *
+ * A hack to change the amount of correlation between points is done using
+ * the grd variable. If the current step size is greater than grd then
+ * the point will be random, otherwise it will be calculated by the
+ * above algorithm. This makes a maximum distance at which two points on
+ * the height map can affect each other.
+ *
+ * How fractal caves are made:
+ *
+ * When the map is complete, a cut-off value is used to create a cave.
+ * Heights below this value are "floor", and heights above are "wall".
+ * This also can be used to create lakes, by adding more height levels
+ * representing shallow and deep water/ lava etc.
+ *
+ * The grd variable affects the width of passages.
+ * The roug variable affects the roughness of those passages
+ *
+ * The tricky part is making sure the created cave is connected. This
+ * is done by 'filling' from the inside and only keeping the 'filled'
+ * floor. Walls bounding the 'filled' floor are also kept. Everything
+ * else is converted to the normal granite FEAT_WALL_EXTRA.
+ */
+
+
+/*
+ * Note that this uses the cave.feat array in a very hackish way
+ * the values are first set to zero, and then each array location
+ * is used as a "heightmap"
+ * The heightmap then needs to be converted back into the "feat" format.
+ *
+ * grd=level at which fractal turns on. smaller gives more mazelike caves
+ * roug=roughness level. 16=normal. higher values make things more
+ * convoluted small values are good for smooth walls.
+ * size=length of the side of the square cave system.
+ */
+
+void generate_hmap(int y0, int x0, int xsiz, int ysiz, int grd,
+ int roug, int cutoff)
+{
+ int xhsize, yhsize, xsize, ysize, maxsize;
+
+ /*
+ * fixed point variables- these are stored as 256 x normal value
+ * this gives 8 binary places of fractional part + 8 places of normal part
+ */
+ u16b xstep, xhstep, ystep, yhstep, i, j, diagsize, xxsize, yysize;
+
+
+ /* Redefine size so can change the value if out of range */
+ xsize = xsiz;
+ ysize = ysiz;
+
+ /* Paranoia about size of the system of caves*/
+ if (xsize > 254) xsize = 254;
+ if (xsize < 4) xsize = 4;
+ if (ysize > 254) ysize = 254;
+ if (ysize < 4) ysize = 4;
+
+ /* Get offsets to middle of array */
+ xhsize = xsize / 2;
+ yhsize = ysize / 2;
+
+ /* Fix rounding problem */
+ xsize = xhsize * 2;
+ ysize = yhsize * 2;
+
+ /*
+ * Scale factor for middle points:
+ * About sqrt(2)*256 - correct for a square lattice
+ * approximately correct for everything else.
+ */
+ diagsize = 362;
+
+ /* Maximum of xsize and ysize */
+ maxsize = (xsize > ysize) ? xsize : ysize;
+
+ /* Clear the section */
+ for (i = 0; i <= xsize; i++)
+ {
+ for (j = 0; j <= ysize; j++)
+ {
+ cave_type *c_ptr;
+
+ /* Access the grid */
+ c_ptr = &cave[j + y0 - yhsize][i + x0 - xhsize];
+
+ /* 255 is a flag for "not done yet" */
+ c_ptr->feat = 255;
+
+ /* Clear icky flag because may be redoing the cave */
+ c_ptr->info &= ~(CAVE_ICKY);
+ }
+ }
+
+ /* Set the corner values just in case grd>size. */
+ store_height(0, 0, x0, y0, maxsize, xhsize, yhsize, cutoff);
+ store_height(0, ysize, x0, y0, maxsize, xhsize, yhsize, cutoff);
+ store_height(xsize, 0, x0, y0, maxsize, xhsize, yhsize, cutoff);
+ store_height(xsize, ysize, x0, y0, maxsize, xhsize, yhsize, cutoff);
+
+ /* Set the middle square to be an open area. */
+ store_height(xhsize, yhsize, x0, y0, 0, xhsize, yhsize, cutoff);
+
+
+ /* Initialise the step sizes */
+ xstep = xhstep = xsize * 256;
+ ystep = yhstep = ysize * 256;
+ xxsize = xsize * 256;
+ yysize = ysize * 256;
+
+ /*
+ * Fill in the rectangle with fractal height data - like the
+ * 'plasma fractal' in fractint
+ */
+ while ((xstep / 256 > 1) || (ystep / 256 > 1))
+ {
+ /* Halve the step sizes */
+ xstep = xhstep;
+ xhstep /= 2;
+ ystep = yhstep;
+ yhstep /= 2;
+
+ /* Middle top to bottom */
+ for (i = xhstep; i <= xxsize - xhstep; i += xstep)
+ {
+ for (j = 0; j <= yysize; j += ystep)
+ {
+ /* If greater than 'grid' level then is random */
+ if (xhstep / 256 > grd)
+ {
+ store_height(i / 256, j / 256, x0, y0, randint(maxsize),
+ xhsize, yhsize, cutoff);
+ }
+ else
+ {
+ cave_type *l, *r;
+ byte val;
+
+ /* Left point */
+ l = &cave[j / 256 + y0 - yhsize][(i - xhstep) / 256 + x0 - xhsize];
+
+ /* Right point */
+ r = &cave[j / 256 + y0 - yhsize][(i + xhstep) / 256 + x0 - xhsize];
+
+ /* Average of left and right points + random bit */
+ val = (l->feat + r->feat) / 2 +
+ (randint(xstep / 256) - xhstep / 256) * roug / 16;
+
+ store_height(i / 256, j / 256, x0, y0, val,
+ xhsize, yhsize, cutoff);
+ }
+ }
+ }
+
+
+ /* Middle left to right */
+ for (j = yhstep; j <= yysize - yhstep; j += ystep)
+ {
+ for (i = 0; i <= xxsize; i += xstep)
+ {
+ /* If greater than 'grid' level then is random */
+ if (xhstep / 256 > grd)
+ {
+ store_height(i / 256, j / 256, x0, y0, randint(maxsize),
+ xhsize, yhsize, cutoff);
+ }
+ else
+ {
+ cave_type *u, *d;
+ byte val;
+
+ /* Up point */
+ u = &cave[(j - yhstep) / 256 + y0 - yhsize][i / 256 + x0 - xhsize];
+
+ /* Down point */
+ d = &cave[(j + yhstep) / 256 + y0 - yhsize][i / 256 + x0 - xhsize];
+
+ /* Average of up and down points + random bit */
+ val = (u->feat + d->feat) / 2 +
+ (randint(ystep / 256) - yhstep / 256) * roug / 16;
+
+ store_height(i / 256, j / 256, x0, y0, val,
+ xhsize, yhsize, cutoff);
+ }
+ }
+ }
+
+ /* Center */
+ for (i = xhstep; i <= xxsize - xhstep; i += xstep)
+ {
+ for (j = yhstep; j <= yysize - yhstep; j += ystep)
+ {
+ /* If greater than 'grid' level then is random */
+ if (xhstep / 256 > grd)
+ {
+ store_height(i / 256, j / 256, x0, y0, randint(maxsize),
+ xhsize, yhsize, cutoff);
+ }
+ else
+ {
+ cave_type *ul, *dl, *ur, *dr;
+ byte val;
+
+ /* Up-left point */
+ ul = &cave[(j - yhstep) / 256 + y0 - yhsize][(i - xhstep) / 256 + x0 - xhsize];
+
+ /* Down-left point */
+ dl = &cave[(j + yhstep) / 256 + y0 - yhsize][(i - xhstep) / 256 + x0 - xhsize];
+
+ /* Up-right point */
+ ur = &cave[(j - yhstep) / 256 + y0 - yhsize][(i + xhstep) / 256 + x0 - xhsize];
+
+ /* Down-right point */
+ dr = &cave[(j + yhstep) / 256 + y0 - yhsize][(i + xhstep) / 256 + x0 - xhsize];
+
+ /*
+ * average over all four corners + scale by diagsize to
+ * reduce the effect of the square grid on the shape
+ * of the fractal
+ */
+ val = (ul->feat + dl->feat + ur->feat + dr->feat) / 4 +
+ (randint(xstep / 256) - xhstep / 256) *
+ (diagsize / 16) / 256 * roug;
+
+ store_height(i / 256, j / 256, x0, y0, val,
+ xhsize, yhsize , cutoff);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Convert from height-map back to the normal Angband cave format
+ */
+static bool_ hack_isnt_wall(int y, int x, int cutoff)
+{
+ /* Already done */
+ if (cave[y][x].info & CAVE_ICKY)
+ {
+ return (FALSE);
+ }
+
+ else
+ {
+ /* Show that have looked at this square */
+ cave[y][x].info |= (CAVE_ICKY);
+
+ /* If less than cutoff then is a floor */
+ if (cave[y][x].feat <= cutoff)
+ {
+ place_floor(y, x);
+ return (TRUE);
+ }
+
+ /* If greater than cutoff then is a wall */
+ else
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ return (FALSE);
+ }
+ }
+}
+
+
+/*
+ * Quick and nasty fill routine used to find the connected region
+ * of floor in the middle of the cave
+ */
+static void fill_hack(int y0, int x0, int y, int x, int xsize, int ysize,
+ int cutoff, int *amount)
+{
+ int i, j;
+
+ /* check 8 neighbours +self (self is caught in the isnt_wall function) */
+ for (i = -1; i <= 1; i++)
+ {
+ for (j = -1; j <= 1; j++)
+ {
+ /* If within bounds */
+ if ((x + i > 0) && (x + i < xsize) &&
+ (y + j > 0) && (y + j < ysize))
+ {
+ /* If not a wall or floor done before */
+ if (hack_isnt_wall(y + j + y0 - ysize / 2,
+ x + i + x0 - xsize / 2, cutoff))
+ {
+ /* then fill from the new point*/
+ fill_hack(y0, x0, y + j, x + i, xsize, ysize,
+ cutoff, amount);
+
+ /* keep tally of size of cave system */
+ (*amount)++;
+ }
+ }
+
+ /* Affect boundary */
+ else
+ {
+ cave[y0 + y + j - ysize / 2][x0 + x + i - xsize / 2].info |= (CAVE_ICKY);
+ }
+ }
+ }
+}
+
+
+bool_ generate_fracave(int y0, int x0, int xsize, int ysize,
+ int cutoff, bool_ light, bool_ room)
+{
+ int x, y, i, amount, xhsize, yhsize;
+ cave_type *c_ptr;
+
+ /* Offsets to middle from corner */
+ xhsize = xsize / 2;
+ yhsize = ysize / 2;
+
+ /* Reset tally */
+ amount = 0;
+
+ /*
+ * Select region connected to center of cave system
+ * this gets rid of alot of isolated one-sqaures that
+ * can make teleport traps instadeaths...
+ */
+ fill_hack(y0, x0, yhsize, xhsize, xsize, ysize, cutoff, &amount);
+
+ /* If tally too small, try again */
+ if (amount < 10)
+ {
+ /* Too small -- clear area and try again later */
+ for (x = 0; x <= xsize; ++x)
+ {
+ for (y = 0; y < ysize; ++y)
+ {
+ place_filler(y0 + y - yhsize, x0 + x - xhsize);
+ cave[y0 + y - yhsize][x0 + x - xhsize].info &= ~(CAVE_ICKY | CAVE_ROOM);
+ }
+ }
+ return FALSE;
+ }
+
+
+ /*
+ * Do boundaries -- check to see if they are next to a filled region
+ * If not then they are set to normal granite
+ * If so then they are marked as room walls
+ */
+ for (i = 0; i <= xsize; ++i)
+ {
+ /* Access top boundary grid */
+ c_ptr = &cave[0 + y0 - yhsize][i + x0 - xhsize];
+
+ /* Next to a 'filled' region? -- set to be room walls */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ cave_set_feat(0 + y0 - yhsize, i + x0 - xhsize, feat_wall_outer);
+
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(0 + y0 - yhsize, i + x0 - xhsize);
+ }
+ }
+
+ /* Outside of the room -- set to be normal granite */
+ else
+ {
+ place_filler(0 + y0 - yhsize, i + x0 - xhsize);
+ }
+
+ /* Clear the icky flag -- don't need it any more */
+ c_ptr->info &= ~(CAVE_ICKY);
+
+
+ /* Access bottom boundary grid */
+ c_ptr = &cave[ysize + y0 - yhsize][i + x0 - xhsize];
+
+ /* Next to a 'filled' region? -- set to be room walls */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ cave_set_feat(ysize + y0 - yhsize, i + x0 - xhsize, feat_wall_outer);
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(ysize + y0 - yhsize, i + x0 - xhsize);
+ }
+ }
+
+ /* Outside of the room -- set to be normal granite */
+ else
+ {
+ place_filler(ysize + y0 - yhsize, i + x0 - xhsize);
+ }
+
+ /* Clear the icky flag -- don't need it any more */
+ c_ptr->info &= ~(CAVE_ICKY);
+ }
+
+
+ /* Do the left and right boundaries minus the corners (done above) */
+ for (i = 1; i < ysize; ++i)
+ {
+ /* Access left boundary grid */
+ c_ptr = &cave[i + y0 - yhsize][0 + x0 - xhsize];
+
+ /* Next to a 'filled' region? -- set to be room walls */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ cave_set_feat(i + y0 - yhsize, 0 + x0 - xhsize, feat_wall_outer);
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(i + y0 - yhsize, 0 + x0 - xhsize);
+ }
+ }
+
+ /* Outside of the room -- set to be normal granite */
+ else
+ {
+ place_filler(i + y0 - yhsize, 0 + x0 - xhsize);
+ }
+
+ /* Clear the icky flag -- don't need it any more */
+ c_ptr->info &= ~(CAVE_ICKY);
+
+
+ /* Access left boundary grid */
+ c_ptr = &cave[i + y0 - yhsize][xsize + x0 - xhsize];
+
+ /* Next to a 'filled' region? -- set to be room walls */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ cave_set_feat(i + y0 - yhsize, xsize + x0 - xhsize, feat_wall_outer);
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(i + y0 - yhsize, xsize + x0 - xhsize);
+ }
+ }
+
+ /* Outside of the room -- set to be normal granite */
+ else
+ {
+ place_filler(i + y0 - yhsize, xsize + x0 - xhsize);
+ }
+
+ /* Clear the icky flag -- don't need it any more */
+ c_ptr->info &= ~(CAVE_ICKY);
+ }
+
+
+ /*
+ * Do the rest: convert back to the normal format
+ * In other variants, may want to check to see if cave.feat< some value
+ * if so, set to be water:- this will make interesting pools etc.
+ * (I don't do this for standard Angband.)
+ */
+ for (x = 1; x < xsize; ++x)
+ {
+ for (y = 1; y < ysize; ++y)
+ {
+ /* Access the grid */
+ c_ptr = &cave[y + y0 - yhsize][x + x0 - xhsize];
+
+ /* A floor grid to be converted */
+ if ((f_info[c_ptr->feat].flags1 & FF1_FLOOR) &&
+ (c_ptr->info & CAVE_ICKY))
+
+ {
+ /* Clear the icky flag in the filled region */
+ c_ptr->info &= ~(CAVE_ICKY);
+
+ /* Set appropriate flags */
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room) c_ptr->info |= (CAVE_ROOM);
+ }
+
+ /* A wall grid to be convereted */
+ else if ((c_ptr->feat == feat_wall_outer) &&
+ (c_ptr->info & CAVE_ICKY))
+ {
+ /* Clear the icky flag in the filled region */
+ c_ptr->info &= ~(CAVE_ICKY);
+
+ /* Set appropriate flags */
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(y + y0 - yhsize, x + x0 - xhsize);
+ }
+ }
+
+ /* None of the above -- clear the unconnected regions */
+ else
+ {
+ place_filler(y + y0 - yhsize, x + x0 - xhsize);
+ c_ptr->info &= ~(CAVE_ICKY | CAVE_ROOM);
+ }
+ }
+ }
+
+ /*
+ * XXX XXX XXX There is a slight problem when tunnels pierce the caves:
+ * Extra doors appear inside the system. (Its not very noticeable though.)
+ * This can be removed by "filling" from the outside in. This allows
+ * a separation from FEAT_WALL_OUTER with FEAT_WALL_INNER. (Internal
+ * walls are F.W.OUTER instead.)
+ * The extra effort for what seems to be only a minor thing (even
+ * non-existant if you think of the caves not as normal rooms, but as
+ * holes in the dungeon), doesn't seem worth it.
+ */
+
+ return (TRUE);
+}
+
+
+/*
+ * Makes a cave system in the center of the dungeon
+ */
+static void build_cavern(void)
+{
+ int grd, roug, cutoff, xsize, ysize, x0, y0;
+ bool_ done, light, room;
+
+ light = done = room = FALSE;
+ if (dun_level <= randint(25)) light = TRUE;
+
+ /* Make a cave the size of the dungeon */
+ xsize = cur_wid - 1;
+ ysize = cur_hgt - 1;
+ x0 = xsize / 2;
+ y0 = ysize / 2;
+
+ /* Paranoia: make size even */
+ xsize = x0 * 2;
+ ysize = y0 * 2;
+
+ while (!done)
+ {
+ /* Testing values for these parameters: feel free to adjust */
+ grd = 1 << (randint(4) + 4);
+
+ /* Want average of about 16 */
+ roug = randint(8) * randint(4);
+
+ /* About size/2 */
+ cutoff = xsize / 2;
+
+ /* Make it */
+ generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff);
+
+ /* Convert to normal format+ clean up*/
+ done = generate_fracave(y0, x0, xsize, ysize, cutoff, light, room);
+ }
+}
+
+/*
+ * Driver routine to create fractal cave system
+ */
+static void build_type10(int by0, int bx0)
+{
+ int grd, roug, cutoff, xsize, ysize, y0, x0;
+
+ bool_ done, light, room;
+
+ /* Get size: note 'Evenness'*/
+ xsize = randint(22) * 2 + 6;
+ ysize = randint(15) * 2 + 6;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 1, ysize + 1, FALSE, by0, bx0, &x0, &y0)) return;
+
+ light = done = FALSE;
+ room = TRUE;
+
+ if (dun_level <= randint(25)) light = TRUE;
+
+ while (!done)
+ {
+ /*
+ * Note: size must be even or there are rounding problems
+ * This causes the tunnels not to connect properly to the room
+ */
+
+ /* Testing values for these parameters feel free to adjust */
+ grd = 1 << (randint(4));
+
+ /* Want average of about 16 */
+ roug = randint(8) * randint(4);
+
+ /* About size/2 */
+ cutoff = randint(xsize / 4) + randint(ysize / 4) +
+ randint(xsize / 4) + randint(ysize / 4);
+
+ /* Make it */
+ generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff);
+
+ /* Convert to normal format + clean up*/
+ done = generate_fracave(y0, x0, xsize, ysize, cutoff, light, room);
+ }
+}
+
+
+/*
+ * Random vault generation from Z 2.5.1
+ */
+
+/*
+ * Make a very small room centred at (x0, y0)
+ *
+ * This is used in crypts, and random elemental vaults.
+ *
+ * Note - this should be used only on allocated regions
+ * within another room.
+ */
+static void build_small_room(int x0, int y0)
+{
+ build_rectangle(y0 - 1, x0 - 1, y0 + 1, x0 + 1, feat_wall_inner, CAVE_ROOM);
+
+ /* Place a secret door on one side */
+ switch (rand_int(4))
+ {
+ case 0:
+ {
+ place_secret_door(y0, x0 - 1);
+ break;
+ }
+
+ case 1:
+ {
+ place_secret_door(y0, x0 + 1);
+ break;
+ }
+
+ case 2:
+ {
+ place_secret_door(y0 - 1, x0);
+ break;
+ }
+
+ case 3:
+ {
+ place_secret_door(y0 + 1, x0);
+ break;
+ }
+ }
+
+ /* Add inner open space */
+ place_floor(y0, x0);
+}
+
+
+/*
+ * Add a door to a location in a random vault
+ *
+ * Note that range checking has to be done in the calling routine.
+ *
+ * The doors must be INSIDE the allocated region.
+ */
+static void add_door(int x, int y)
+{
+ /* Need to have a wall in the center square */
+ if (cave[y][x].feat != feat_wall_outer) return;
+
+ /*
+ * Look at:
+ * x#x
+ * .#.
+ * x#x
+ *
+ * where x=don't care
+ * .=floor, #=wall
+ */
+
+ if (get_is_floor(x, y - 1) && get_is_floor(x, y + 1) &&
+ (cave[y][x - 1].feat == feat_wall_outer) &&
+ (cave[y][x + 1].feat == feat_wall_outer))
+ {
+ /* secret door */
+ place_secret_door(y, x);
+
+ /* set boundarys so don't get wide doors */
+ place_filler(y, x - 1);
+ place_filler(y, x + 1);
+ }
+
+
+ /*
+ * Look at:
+ * x#x
+ * .#.
+ * x#x
+ *
+ * where x = don't care
+ * .=floor, #=wall
+ */
+ if ((cave[y - 1][x].feat == feat_wall_outer) &&
+ (cave[y + 1][x].feat == feat_wall_outer) &&
+ get_is_floor(x - 1, y) && get_is_floor(x + 1, y))
+ {
+ /* secret door */
+ place_secret_door(y, x);
+
+ /* set boundarys so don't get wide doors */
+ place_filler(y - 1, x);
+ place_filler(y + 1, x);
+ }
+}
+
+
+/*
+ * Fill the empty areas of a room with treasure and monsters.
+ */
+static void fill_treasure(int x1, int x2, int y1, int y2, int difficulty)
+{
+ int x, y, cx, cy, size;
+ s32b value;
+
+ /* center of room:*/
+ cx = (x1 + x2) / 2;
+ cy = (y1 + y2) / 2;
+
+ /* Rough measure of size of vault= sum of lengths of sides */
+ size = abs(x2 - x1) + abs(y2 - y1);
+
+ for (x = x1; x <= x2; x++)
+ {
+ for (y = y1; y <= y2; y++)
+ {
+ /*
+ * Thing added based on distance to center of vault
+ * Difficulty is 1-easy to 10-hard
+ */
+ value = (((s32b)distance(cx, cy, x, y) * 100) / size) +
+ randint(10) - difficulty;
+
+ /* Hack -- Empty square part of the time */
+ if ((randint(100) - difficulty * 3) > 50) value = 20;
+
+ /* If floor, shallow water or lava */
+ if (get_is_floor(x, y) ||
+ (cave[y][x].feat == FEAT_SHAL_WATER) ||
+ (cave[y][x].feat == FEAT_SHAL_LAVA))
+ {
+ /* The smaller 'value' is, the better the stuff */
+ if (value < 0)
+ {
+ /* Meanest monster + treasure */
+ monster_level = dun_level + 40;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 20;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_FLOOR);
+ object_level = dun_level;
+ }
+ else if (value < 5)
+ {
+ /* Mean monster +treasure */
+ monster_level = dun_level + 20;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 10;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_FLOOR);
+ object_level = dun_level;
+ }
+ else if (value < 10)
+ {
+ /* Monster */
+ monster_level = dun_level + 9;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ else if (value < 17)
+ {
+ /* Intentional Blank space */
+
+ /*
+ * (Want some of the vault to be empty
+ * so have room for group monsters.
+ * This is used in the hack above to lower
+ * the density of stuff in the vault.)
+ */
+ }
+ else if (value < 23)
+ {
+ /* Object or trap */
+ if (rand_int(100) < 25)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+ else
+ {
+ place_trap(y, x);
+ }
+ }
+ else if (value < 30)
+ {
+ /* Monster and trap */
+ monster_level = dun_level + 5;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ place_trap(y, x);
+ }
+ else if (value < 40)
+ {
+ /* Monster or object */
+ if (rand_int(100) < 50)
+ {
+ monster_level = dun_level + 3;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ if (rand_int(100) < 50)
+ {
+ object_level = dun_level + 7;
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ object_level = dun_level;
+ }
+ }
+ else if (value < 50)
+ {
+ /* Trap */
+ place_trap(y, x);
+ }
+ else
+ {
+ /* Various Stuff */
+
+ /* 20% monster, 40% trap, 20% object, 20% blank space */
+ if (rand_int(100) < 20)
+ {
+ place_monster(y, x, TRUE, TRUE);
+ }
+ else if (rand_int(100) < 50)
+ {
+ place_trap(y, x);
+ }
+ else if (rand_int(100) < 50)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+ }
+
+ }
+ }
+ }
+}
+
+
+/*
+ * Creates a random vault that looks like a collection of bubbles
+ *
+ * It works by getting a set of coordinates that represent the center of
+ * each bubble. The entire room is made by seeing which bubble center is
+ * closest. If two centers are equidistant then the square is a wall,
+ * otherwise it is a floor. The only exception is for squares really
+ * near a center, these are always floor.
+ * (It looks better than without this check.)
+ *
+ * Note: If two centers are on the same point then this algorithm will create a
+ * blank bubble filled with walls. - This is prevented from happening.
+ */
+
+#define BUBBLENUM 10 /* number of bubbles */
+
+static void build_bubble_vault(int x0, int y0, int xsize, int ysize)
+{
+ /* array of center points of bubbles */
+ coord center[BUBBLENUM];
+
+ int i, j, k, x = 0, y = 0;
+ u16b min1, min2, temp;
+ bool_ done;
+
+ /* Offset from center to top left hand corner */
+ int xhsize = xsize / 2;
+ int yhsize = ysize / 2;
+
+ if (cheat_room) msg_print("Bubble Vault");
+
+ /* Allocate center of bubbles */
+ center[0].x = randint(xsize - 3) + 1;
+ center[0].y = randint(ysize - 3) + 1;
+
+ for (i = 1; i < BUBBLENUM; i++)
+ {
+ done = FALSE;
+
+ /* Get center and check to see if it is unique */
+ for (k = 0; !done && (k < 2000); k++)
+ {
+ done = TRUE;
+
+ x = randint(xsize - 3) + 1;
+ y = randint(ysize - 3) + 1;
+
+ for (j = 0; j < i; j++)
+ {
+ /* Rough test to see if there is an overlap */
+ if ((x == center[j].x) || (y == center[j].y)) done = FALSE;
+ }
+ }
+
+ /* Too many failures */
+ if (k >= 2000) return;
+
+ center[i].x = x;
+ center[i].y = y;
+ }
+
+ build_rectangle(y0 - yhsize, x0 - xhsize,
+ y0 - yhsize + ysize - 1, x0 - xhsize + xsize - 1,
+ feat_wall_outer, CAVE_ROOM | CAVE_ICKY);
+
+ /* Fill in middle with bubbles */
+ for (x = 1; x < xsize - 1; x++)
+ {
+ for (y = 1; y < ysize - 1; y++)
+ {
+ cave_type *c_ptr;
+
+ /* Get distances to two closest centers */
+
+ /* Initialise */
+ min1 = distance(x, y, center[0].x, center[0].y);
+ min2 = distance(x, y, center[1].x, center[1].y);
+
+ if (min1 > min2)
+ {
+ /* Swap if in wrong order */
+ temp = min1;
+ min1 = min2;
+ min2 = temp;
+ }
+
+ /* Scan the rest */
+ for (i = 2; i < BUBBLENUM; i++)
+ {
+ temp = distance(x, y, center[i].x, center[i].y);
+
+ if (temp < min1)
+ {
+ /* Smallest */
+ min2 = min1;
+ min1 = temp;
+ }
+ else if (temp < min2)
+ {
+ /* Second smallest */
+ min2 = temp;
+ }
+ }
+
+ /* Access the grid */
+ c_ptr = &cave[y + y0 - yhsize][x + x0 - xhsize];
+
+ /*
+ * Boundary at midpoint+ not at inner region of bubble
+ *
+ * SCSCSC: was feat_wall_outer
+ */
+ if (((min2 - min1) <= 2) && (!(min1 < 3)))
+ {
+ place_filler(y + y0 - yhsize, x + x0 - xhsize);
+ }
+
+ /* Middle of a bubble */
+ else
+ {
+ place_floor(y + y0 - yhsize, x + x0 - xhsize);
+ }
+
+ /* Clean up rest of flags */
+ c_ptr->info |= (CAVE_ROOM | CAVE_ICKY);
+ }
+ }
+
+ /* Try to add some random doors */
+ for (i = 0; i < 500; i++)
+ {
+ x = randint(xsize - 3) - xhsize + x0 + 1;
+ y = randint(ysize - 3) - yhsize + y0 + 1;
+ add_door(x, y);
+ }
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x0 - xhsize + 1, x0 - xhsize + xsize - 2,
+ y0 - yhsize + 1, y0 - yhsize + ysize - 2, randint(5));
+}
+
+
+/*
+ * Convert FEAT_WALL_EXTRA (used by random vaults) to normal dungeon wall
+ */
+static void convert_extra(int y1, int x1, int y2, int x2)
+{
+ int x, y;
+
+ for (x = x1; x <= x2; x++)
+ {
+ for (y = y1; y <= y2; y++)
+ {
+ if (cave[y][x].feat == FEAT_WALL_OUTER)
+ {
+ place_filler(y, x);
+ }
+ }
+ }
+}
+
+
+/*
+ * Overlay a rectangular room given its bounds
+ *
+ * This routine is used by build_room_vault (hence FEAT_WALL_OUTER)
+ * The area inside the walls is not touched: only granite is removed
+ * and normal walls stay
+ */
+static void build_room(int x1, int x2, int y1, int y2)
+{
+ int x, y, xsize, ysize, temp;
+
+ /* Check if rectangle has no width */
+ if ((x1 == x2) || (y1 == y2)) return;
+
+ /* initialize */
+ if (x1 > x2)
+ {
+ /* Swap boundaries if in wrong order */
+ temp = x1;
+ x1 = x2;
+ x2 = temp;
+ }
+
+ if (y1 > y2)
+ {
+ /* Swap boundaries if in wrong order */
+ temp = y1;
+ y1 = y2;
+ y2 = temp;
+ }
+
+ /* Get total widths */
+ xsize = x2 - x1;
+ ysize = y2 - y1;
+
+ build_rectangle(y1, x1, y2, x2, feat_wall_outer, CAVE_ROOM | CAVE_ICKY);
+
+ /* Middle */
+ for (x = 1; x < xsize; x++)
+ {
+ for (y = 1; y < ysize; y++)
+ {
+ if (cave[y1 + y][x1 + x].feat == FEAT_WALL_OUTER)
+ {
+ /* Clear the untouched region */
+ place_floor(y1 + y, x1 + x);
+ cave[y1 + y][x1 + x].info |= (CAVE_ROOM | CAVE_ICKY);
+ }
+ else
+ {
+ /* Make it a room -- but don't touch */
+ cave[y1 + y][x1 + x].info |= (CAVE_ROOM | CAVE_ICKY);
+ }
+ }
+ }
+}
+
+
+/*
+ * Create a random vault that looks like a collection of overlapping rooms
+ */
+static void build_room_vault(int x0, int y0, int xsize, int ysize)
+{
+ int i, x1, x2, y1, y2, xhsize, yhsize;
+
+ /* Get offset from center */
+ xhsize = xsize / 2;
+ yhsize = ysize / 2;
+
+ if (cheat_room) msg_print("Room Vault");
+
+ /* Fill area so don't get problems with arena levels */
+ for (x1 = 0; x1 <= xsize; x1++)
+ {
+ int x = x0 - xhsize + x1;
+
+ for (y1 = 0; y1 <= ysize; y1++)
+ {
+ int y = y0 - yhsize + y1;
+
+ cave_set_feat(y, x, FEAT_WALL_OUTER);
+ cave[y][x].info &= ~(CAVE_ICKY);
+ }
+ }
+
+ /* Add ten random rooms */
+ for (i = 0; i < 10; i++)
+ {
+ x1 = randint(xhsize) * 2 + x0 - xhsize;
+ x2 = randint(xhsize) * 2 + x0 - xhsize;
+ y1 = randint(yhsize) * 2 + y0 - yhsize;
+ y2 = randint(yhsize) * 2 + y0 - yhsize;
+
+ build_room(x1, x2, y1, y2);
+ }
+
+ convert_extra(y0 - yhsize, x0 - xhsize, y0 - yhsize + ysize,
+ x0 - xhsize + xsize);
+
+ /* Add some random doors */
+ for (i = 0; i < 500; i++)
+ {
+ x1 = randint(xsize - 2) - xhsize + x0 + 1;
+ y1 = randint(ysize - 2) - yhsize + y0 + 1;
+ add_door(x1, y1);
+ }
+
+ /* Fill with monsters and treasure, high difficulty */
+ fill_treasure(x0 - xhsize + 1, x0 - xhsize + xsize - 1,
+ y0 - yhsize + 1, y0 - yhsize + ysize - 1, randint(5) + 5);
+}
+
+
+/*
+ * Create a random vault out of a fractal cave
+ */
+static void build_cave_vault(int x0, int y0, int xsiz, int ysiz)
+{
+ int grd, roug, cutoff, xhsize, yhsize, xsize, ysize, x, y;
+ bool_ done, light, room;
+
+ /* Round to make sizes even */
+ xhsize = xsiz / 2;
+ yhsize = ysiz / 2;
+ xsize = xhsize * 2;
+ ysize = yhsize * 2;
+
+ if (cheat_room) msg_print("Cave Vault");
+
+ light = done = FALSE;
+ room = TRUE;
+
+ while (!done)
+ {
+ /* Testing values for these parameters feel free to adjust */
+ grd = 1 << rand_int(4);
+
+ /* Want average of about 16 */
+ roug = randint(8) * randint(4);
+
+ /* About size/2 */
+ cutoff = randint(xsize / 4) + randint(ysize / 4) +
+ randint(xsize / 4) + randint(ysize / 4);
+
+ /* Make it */
+ generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff);
+
+ /* Convert to normal format + clean up */
+ done = generate_fracave(y0, x0, xsize, ysize, cutoff, light, room);
+ }
+
+ /* Set icky flag because is a vault */
+ for (x = 0; x <= xsize; x++)
+ {
+ for (y = 0; y <= ysize; y++)
+ {
+ cave[y0 - yhsize + y][x0 - xhsize + x].info |= CAVE_ICKY;
+ }
+ }
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x0 - xhsize + 1, x0 - xhsize + xsize - 1,
+ y0 - yhsize + 1, y0 - yhsize + ysize - 1, randint(5));
+}
+
+
+/*
+ * Maze vault -- rectangular labyrinthine rooms
+ *
+ * maze vault uses two routines:
+ * r_visit - a recursive routine that builds the labyrinth
+ * build_maze_vault - a driver routine that calls r_visit and adds
+ * monsters, traps and treasure
+ *
+ * The labyrinth is built by creating a spanning tree of a graph.
+ * The graph vertices are at
+ * (x, y) = (2j + x1, 2k + y1) j = 0,...,m-1 k = 0,...,n-1
+ * and the edges are the vertical and horizontal nearest neighbors.
+ *
+ * The spanning tree is created by performing a suitably randomized
+ * depth-first traversal of the graph. The only adjustable parameter
+ * is the rand_int(3) below; it governs the relative density of
+ * twists and turns in the labyrinth: smaller number, more twists.
+ */
+static void r_visit(int y1, int x1, int y2, int x2,
+ int node, int dir, int *visited)
+{
+ int i, j, m, n, temp, x, y, adj[4];
+
+ /* Dimensions of vertex array */
+ m = (x2 - x1) / 2 + 1;
+ n = (y2 - y1) / 2 + 1;
+
+ /* Mark node visited and set it to a floor */
+ visited[node] = 1;
+ x = 2 * (node % m) + x1;
+ y = 2 * (node / m) + y1;
+ place_floor(y, x);
+
+ /* Setup order of adjacent node visits */
+ if (rand_int(3) == 0)
+ {
+ /* Pick a random ordering */
+ for (i = 0; i < 4; i++)
+ {
+ adj[i] = i;
+ }
+ for (i = 0; i < 4; i++)
+ {
+ j = rand_int(4);
+ temp = adj[i];
+ adj[i] = adj[j];
+ adj[j] = temp;
+ }
+ dir = adj[0];
+ }
+ else
+ {
+ /* Pick a random ordering with dir first */
+ adj[0] = dir;
+ for (i = 1; i < 4; i++)
+ {
+ adj[i] = i;
+ }
+ for (i = 1; i < 4; i++)
+ {
+ j = 1 + rand_int(3);
+ temp = adj[i];
+ adj[i] = adj[j];
+ adj[j] = temp;
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ switch (adj[i])
+ {
+ /* (0,+) - check for bottom boundary */
+ case 0:
+ {
+ if ((node / m < n - 1) && (visited[node + m] == 0))
+ {
+ place_floor(y + 1, x);
+ r_visit(y1, x1, y2, x2, node + m, dir, visited);
+ }
+ break;
+ }
+
+ /* (0,-) - check for top boundary */
+ case 1:
+ {
+ if ((node / m > 0) && (visited[node - m] == 0))
+ {
+ place_floor(y - 1, x);
+ r_visit(y1, x1, y2, x2, node - m, dir, visited);
+ }
+ break;
+ }
+
+ /* (+,0) - check for right boundary */
+ case 2:
+ {
+ if ((node % m < m - 1) && (visited[node + 1] == 0))
+ {
+ place_floor(y, x + 1);
+ r_visit(y1, x1, y2, x2, node + 1, dir, visited);
+ }
+ break;
+ }
+
+ /* (-,0) - check for left boundary */
+ case 3:
+ {
+ if ((node % m > 0) && (visited[node - 1] == 0))
+ {
+ place_floor(y, x - 1);
+ r_visit(y1, x1, y2, x2, node - 1, dir, visited);
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+static void build_maze_vault(int x0, int y0, int xsize, int ysize)
+{
+ int y, x, dy, dx;
+ int y1, x1, y2, x2;
+ int i, m, n, num_vertices, *visited;
+ bool_ light;
+ cave_type *c_ptr;
+
+
+ if (cheat_room) msg_print("Maze Vault");
+
+ /* Choose lite or dark */
+ light = (dun_level <= randint(25));
+
+ /* Pick a random room size - randomized by calling routine */
+ dy = ysize / 2 - 1;
+ dx = xsize / 2 - 1;
+
+ y1 = y0 - dy;
+ x1 = x0 - dx;
+ y2 = y0 + dy;
+ x2 = x0 + dx;
+
+ /* Generate the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ c_ptr = &cave[y][x];
+
+ c_ptr->info |= (CAVE_ROOM | CAVE_ICKY);
+
+ if ((x == x1 - 1) || (x == x2 + 1) ||
+ (y == y1 - 1) || (y == y2 + 1))
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ else
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ }
+ }
+
+ /* Dimensions of vertex array */
+ m = dx + 1;
+ n = dy + 1;
+ num_vertices = m * n;
+
+ /* Allocate an array for visited vertices */
+ C_MAKE(visited, num_vertices, int);
+
+ /* Initialise array of visited vertices */
+ for (i = 0; i < num_vertices; i++)
+ {
+ visited[i] = 0;
+ }
+
+ /* Traverse the graph to create a spaning tree, pick a random root */
+ r_visit(y1, x1, y2, x2, rand_int(num_vertices), 0, visited);
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x1, x2, y1, y2, randint(5));
+
+ /* Free the array for visited vertices */
+ C_FREE(visited, num_vertices, int);
+}
+
+
+/*
+ * Build a "mini" checkerboard vault
+ *
+ * This is done by making a permanent wall maze and setting
+ * the diagonal sqaures of the checker board to be granite.
+ * The vault has two entrances on opposite sides to guarantee
+ * a way to get in even if the vault abuts a side of the dungeon.
+ */
+static void build_mini_c_vault(int x0, int y0, int xsize, int ysize)
+{
+ int dy, dx;
+ int y1, x1, y2, x2, y, x, total;
+ int i, m, n, num_vertices;
+ int *visited;
+
+ if (cheat_room) msg_print("Mini Checker Board Vault");
+
+ /* Pick a random room size */
+ dy = ysize / 2 - 1;
+ dx = xsize / 2 - 1;
+
+ y1 = y0 - dy;
+ x1 = x0 - dx;
+ y2 = y0 + dy;
+ x2 = x0 + dx;
+
+
+ /* Generate the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ cave[y][x].info |= (CAVE_ROOM | CAVE_ICKY);
+
+ /* Permanent walls */
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+ }
+ }
+
+
+ /* Dimensions of vertex array */
+ m = dx + 1;
+ n = dy + 1;
+ num_vertices = m * n;
+
+ /* Allocate an array for visited vertices */
+ C_MAKE(visited, num_vertices, int);
+
+ /* Initialise array of visited vertices */
+ for (i = 0; i < num_vertices; i++)
+ {
+ visited[i] = 0;
+ }
+
+ /* Traverse the graph to create a spannng tree, pick a random root */
+ r_visit(y1, x1, y2, x2, rand_int(num_vertices), 0, visited);
+
+ /* Make it look like a checker board vault */
+ for (x = x1; x <= x2; x++)
+ {
+ for (y = y1; y <= y2; y++)
+ {
+ total = x - x1 + y - y1;
+
+ /* If total is odd and is a floor, then make a wall */
+ if ((total % 2 == 1) && get_is_floor(x, y))
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Make a couple of entrances */
+ if (rand_int(2) == 0)
+ {
+ /* Left and right */
+ y = randint(dy) + dy / 2;
+ cave_set_feat(y1 + y, x1 - 1, feat_wall_outer);
+ cave_set_feat(y1 + y, x2 + 1, feat_wall_outer);
+ }
+ else
+ {
+ /* Top and bottom */
+ x = randint(dx) + dx / 2;
+ cave_set_feat(y1 - 1, x1 + x, feat_wall_outer);
+ cave_set_feat(y2 + 1, x1 + x, feat_wall_outer);
+ }
+
+ /* Fill with monsters and treasure, highest difficulty */
+ fill_treasure(x1, x2, y1, y2, 10);
+
+ /* Free the array for visited vertices */
+ C_FREE(visited, num_vertices, int);
+}
+
+
+/*
+ * Build a town/ castle by using a recursive algorithm.
+ * Basically divide each region in a probalistic way to create
+ * smaller regions. When the regions get too small stop.
+ *
+ * The power variable is a measure of how well defended a region is.
+ * This alters the possible choices.
+ */
+static void build_recursive_room(int x1, int y1, int x2, int y2, int power)
+{
+ int xsize, ysize;
+ int x, y;
+ int choice;
+
+ /* Temp variables */
+ int t1, t2, t3, t4;
+
+ xsize = x2 - x1;
+ ysize = y2 - y1;
+
+ if ((power < 3) && (xsize > 12) && (ysize > 12))
+ {
+ /* Need outside wall +keep */
+ choice = 1;
+ }
+ else
+ {
+ if (power < 10)
+ {
+ /* Make rooms + subdivide */
+ if ((randint(10) > 2) && (xsize < 8) && (ysize < 8))
+ {
+ choice = 4;
+ }
+ else
+ {
+ choice = randint(2) + 1;
+ }
+ }
+ else
+ {
+ /* Mostly subdivide */
+ choice = randint(3) + 1;
+ }
+ }
+
+ /* Based on the choice made above, do something */
+ switch (choice)
+ {
+ /* Outer walls */
+ case 1:
+ {
+ /* Top and bottom */
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y1, x, feat_wall_outer);
+ cave_set_feat(y2, x, feat_wall_outer);
+ }
+
+ /* Left and right */
+ for (y = y1 + 1; y < y2; y++)
+ {
+ cave_set_feat(y, x1, feat_wall_outer);
+ cave_set_feat(y, x2, feat_wall_outer);
+ }
+
+ /* Make a couple of entrances */
+ if (rand_int(2) == 0)
+ {
+ /* Left and right */
+ y = randint(ysize) + y1;
+ place_floor(y, x1);
+ place_floor(y, x2);
+ }
+ else
+ {
+ /* Top and bottom */
+ x = randint(xsize) + x1;
+ place_floor(y1, x);
+ place_floor(y2, x);
+ }
+
+ /* Select size of keep */
+ t1 = randint(ysize / 3) + y1;
+ t2 = y2 - randint(ysize / 3);
+ t3 = randint(xsize / 3) + x1;
+ t4 = x2 - randint(xsize / 3);
+
+ /* Do outside areas */
+
+ /* Above and below keep */
+ build_recursive_room(x1 + 1, y1 + 1, x2 - 1, t1, power + 1);
+ build_recursive_room(x1 + 1, t2, x2 - 1, y2, power + 1);
+
+ /* Left and right of keep */
+ build_recursive_room(x1 + 1, t1 + 1, t3, t2 - 1, power + 3);
+ build_recursive_room(t4, t1 + 1, x2 - 1, t2 - 1, power + 3);
+
+ /* Make the keep itself: */
+ x1 = t3;
+ x2 = t4;
+ y1 = t1;
+ y2 = t2;
+ xsize = x2 - x1;
+ ysize = y2 - y1;
+ power += 2;
+
+ /* Fall through */
+ }
+
+ /* Try to build a room */
+ case 4:
+ {
+ if ((xsize < 3) || (ysize < 3))
+ {
+ for (y = y1; y < y2; y++)
+ {
+ for (x = x1; x < x2; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+
+ /* Too small */
+ return;
+ }
+
+ /* Make outside walls */
+
+ /* Top and bottom */
+ for (x = x1 + 1; x <= x2 - 1; x++)
+ {
+ cave_set_feat(y1 + 1, x, feat_wall_inner);
+ cave_set_feat(y2 - 1, x, feat_wall_inner);
+ }
+
+ /* Left and right */
+ for (y = y1 + 1; y <= y2 - 1; y++)
+ {
+ cave_set_feat(y, x1 + 1, feat_wall_inner);
+ cave_set_feat(y, x2 - 1, feat_wall_inner);
+ }
+
+ /* Make a door */
+ y = randint(ysize - 3) + y1 + 1;
+
+ if (rand_int(2) == 0)
+ {
+ /* Left */
+ place_floor(y, x1 + 1);
+ }
+ else
+ {
+ /* Right */
+ place_floor(y, x2 - 1);
+ }
+
+ /* Build the room */
+ build_recursive_room(x1 + 2, y1 + 2, x2 - 2, y2 - 2, power + 3);
+
+ break;
+ }
+
+ /* Try and divide vertically */
+ case 2:
+ {
+ if (xsize < 3)
+ {
+ /* Too small */
+ for (y = y1; y < y2; y++)
+ {
+ for (x = x1; x < x2; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ return;
+ }
+
+ t1 = randint(xsize - 2) + x1 + 1;
+ build_recursive_room(x1, y1, t1, y2, power - 2);
+ build_recursive_room(t1 + 1, y1, x2, y2, power - 2);
+
+ break;
+ }
+
+ /* Try and divide horizontally */
+ case 3:
+ {
+ if (ysize < 3)
+ {
+ /* Too small */
+ for (y = y1; y < y2; y++)
+ {
+ for (x = x1; x < x2; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ return;
+ }
+
+ t1 = randint(ysize - 2) + y1 + 1;
+ build_recursive_room(x1, y1, x2, t1, power - 2);
+ build_recursive_room(x1, t1 + 1, x2, y2, power - 2);
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Build a castle
+ *
+ * Clear the region and call the recursive room routine.
+ *
+ * This makes a vault that looks like a castle or city in the dungeon.
+ */
+static void build_castle_vault(int x0, int y0, int xsize, int ysize)
+{
+ int dy, dx;
+ int y1, x1, y2, x2;
+ int y, x;
+
+ /* Pick a random room size */
+ dy = ysize / 2 - 1;
+ dx = xsize / 2 - 1;
+
+ y1 = y0 - dy;
+ x1 = x0 - dx;
+ y2 = y0 + dy;
+ x2 = x0 + dx;
+
+ if (cheat_room) msg_print("Castle Vault");
+
+ /* Generate the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ cave[y][x].info |= (CAVE_ROOM | CAVE_ICKY);
+
+ /* Make everything a floor */
+ place_floor(y, x);
+ }
+ }
+
+ /* Make the castle */
+ build_recursive_room(x1, y1, x2, y2, randint(5));
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x1, x2, y1, y2, randint(3));
+}
+
+
+/*
+ * Add outer wall to a floored region
+ *
+ * Note: no range checking is done so must be inside dungeon
+ * This routine also stomps on doors
+ */
+static void add_outer_wall(int x, int y, int light, int x1, int y1,
+ int x2, int y2)
+{
+ int i, j;
+
+ if (!in_bounds(y, x)) return;
+
+ /*
+ * Hack -- Check to see if square has been visited before
+ * if so, then exit (use room flag to do this)
+ */
+ if (cave[y][x].info & CAVE_ROOM) return;
+
+ /* Set room flag */
+ cave[y][x].info |= (CAVE_ROOM);
+
+ if (get_is_floor(x, y))
+ {
+ for (i = -1; i <= 1; i++)
+ {
+ for (j = -1; j <= 1; j++)
+ {
+ if ((x + i >= x1) && (x + i <= x2) &&
+ (y + j >= y1) && (y + j <= y2))
+ {
+ add_outer_wall(x + i, y + j, light, x1, y1, x2, y2);
+ if (light) cave[y][x].info |= CAVE_GLOW;
+ }
+ }
+ }
+ }
+
+ /* Set bounding walls */
+ else if (cave[y][x].feat == FEAT_WALL_EXTRA)
+ {
+ cave[y][x].feat = feat_wall_outer;
+ if (light == TRUE) cave[y][x].info |= CAVE_GLOW;
+ }
+
+ /* Set bounding walls */
+ else if (cave[y][x].feat == FEAT_PERM_OUTER)
+ {
+ if (light == TRUE) cave[y][x].info |= CAVE_GLOW;
+ }
+}
+
+
+/*
+ * Hacked distance formula - gives the 'wrong' answer
+ *
+ * Used to build crypts
+ */
+static int dist2(int x1, int y1, int x2, int y2,
+ int h1, int h2, int h3, int h4)
+{
+ int dx, dy;
+ dx = abs(x2 - x1);
+ dy = abs(y2 - y1);
+
+ /*
+ * Basically this works by taking the normal pythagorean formula
+ * and using an expansion to express this in a way without the
+ * square root. This approximate formula is then perturbed to give
+ * the distorted results. (I found this by making a mistake when I was
+ * trying to fix the circular rooms.)
+ */
+
+ /* h1-h4 are constants that describe the metric */
+ if (dx >= 2 * dy) return (dx + (dy * h1) / h2);
+ if (dy >= 2 * dx) return (dy + (dx * h1) / h2);
+
+ /* 128/181 is approx. 1/sqrt(2) */
+ return (((dx + dy) * 128) / 181 +
+ (dx * dx / (dy * h3) + dy * dy / (dx * h3)) * h4);
+}
+
+
+/*
+ * Build target vault
+ *
+ * This is made by two concentric "crypts" with perpendicular
+ * walls creating the cross-hairs.
+ */
+static void build_target_vault(int x0, int y0, int xsize, int ysize)
+{
+ int rad, x, y;
+
+ int h1, h2, h3, h4;
+
+
+ /* Make a random metric */
+ h1 = randint(32) - 16;
+ h2 = randint(16);
+ h3 = randint(32);
+ h4 = randint(32) - 16;
+
+ if (cheat_room) msg_print("Target Vault");
+
+ /* Work out outer radius */
+ if (xsize > ysize)
+ {
+ rad = ysize / 2;
+ }
+ else
+ {
+ rad = xsize / 2;
+ }
+
+ /* Make floor */
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ cave_type *c_ptr;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear room flag */
+ c_ptr->info &= ~(CAVE_ROOM);
+
+ /* Grids in vaults are required to be "icky" */
+ c_ptr->info |= (CAVE_ICKY);
+
+ /* Inside -- floor */
+ if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1)
+ {
+ place_floor(y, x);
+ }
+
+ /* Outside -- make it granite so that arena works */
+ else
+ {
+ c_ptr->feat = FEAT_WALL_EXTRA;
+ }
+
+ /* Proper boundary for arena */
+ if (((y + rad) == y0) || ((y - rad) == y0) ||
+ ((x + rad) == x0) || ((x - rad) == x0))
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ }
+ }
+
+ /* Find visible outer walls and set to be FEAT_OUTER */
+ add_outer_wall(x0, y0, FALSE, x0 - rad - 1, y0 - rad - 1,
+ x0 + rad + 1, y0 + rad + 1);
+
+ /* Add inner wall */
+ for (x = x0 - rad / 2; x <= x0 + rad / 2; x++)
+ {
+ for (y = y0 - rad / 2; y <= y0 + rad / 2; y++)
+ {
+ if (dist2(y0, x0, y, x, h1, h2, h3, h4) == rad / 2)
+ {
+ /* Make an internal wall */
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Add perpendicular walls */
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ cave_set_feat(y0, x, feat_wall_inner);
+ }
+
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ cave_set_feat(y, x0, feat_wall_inner);
+ }
+
+ /* Make inner vault */
+ for (y = y0 - 1; y <= y0 + 1; y++)
+ {
+ cave_set_feat(y, x0 - 1, feat_wall_inner);
+ cave_set_feat(y, x0 + 1, feat_wall_inner);
+ }
+ for (x = x0 - 1; x <= x0 + 1; x++)
+ {
+ cave_set_feat(y0 - 1, x, feat_wall_inner);
+ cave_set_feat(y0 + 1, x, feat_wall_inner);
+ }
+
+ place_floor(y0, x0);
+
+
+ /*
+ * Add doors to vault
+ *
+ * Get two distances so can place doors relative to centre
+ */
+ x = (rad - 2) / 4 + 1;
+ y = rad / 2 + x;
+
+ add_door(x0 + x, y0);
+ add_door(x0 + y, y0);
+ add_door(x0 - x, y0);
+ add_door(x0 - y, y0);
+ add_door(x0, y0 + x);
+ add_door(x0, y0 + y);
+ add_door(x0, y0 - x);
+ add_door(x0, y0 - y);
+
+ /* Fill with stuff - medium difficulty */
+ fill_treasure(x0 - rad, x0 + rad, y0 - rad, y0 + rad, randint(3) + 3);
+}
+
+
+/*
+ * Random vaults
+ */
+static void build_type11(int by0, int bx0)
+{
+ int y0, x0, xsize, ysize, vtype;
+
+ /* Get size -- gig enough to look good, small enough to be fairly common */
+ xsize = randint(22) + 22;
+ ysize = randint(11) + 11;
+
+ /* Allocate in room_map. If will not fit, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &x0, &y0)) return;
+
+ /*
+ * Boost the rating -- Higher than lesser vaults and lower than
+ * greater vaults
+ */
+ rating += 10;
+
+ /* (Sometimes) Cause a special feeling */
+ if ((dun_level <= 50) ||
+ (randint((dun_level - 40) * (dun_level - 40) + 1) < 400))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Select type of vault */
+ vtype = randint(8);
+
+ switch (vtype)
+ {
+ /* Build an appropriate room */
+ case 1:
+ {
+ build_bubble_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 2:
+ {
+ build_room_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 3:
+ {
+ build_cave_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 4:
+ {
+ build_maze_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 5:
+ {
+ build_mini_c_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 6:
+ {
+ build_castle_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 7:
+ {
+ build_target_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ /* I know how to add a few more... give me some time. */
+
+ /* Paranoia */
+ default:
+ {
+ return;
+ }
+ }
+}
+
+/*
+ * Crypt room generation from Z 2.5.1
+ */
+
+/*
+ * Build crypt room.
+ * For every grid in the possible square, check the (fake) distance.
+ * If it's less than the radius, make it a room square.
+ *
+ * When done fill from the inside to find the walls,
+ */
+static void build_type12(int by0, int bx0)
+{
+ int light, rad, x, y, x0, y0;
+ bool_ emptyflag = TRUE;
+ int h1, h2, h3, h4;
+
+ /* Make a random metric */
+ h1 = randint(32) - 16;
+ h2 = randint(16);
+ h3 = randint(32);
+ h4 = randint(32) - 16;
+
+ /* Occasional light */
+ light = (randint(dun_level) <= 5) ? TRUE : FALSE;
+
+ rad = randint(9);
+
+ /* Allocate in room_map. If will not fit, exit */
+ if (!room_alloc(rad * 2 + 3, rad * 2 + 3, FALSE, by0, bx0, &x0, &y0)) return;
+
+ /* Make floor */
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ /* Clear room flag */
+ cave[y][x].info &= ~(CAVE_ROOM);
+
+ /* Inside -- floor */
+ if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1)
+ {
+ place_floor(y, x);
+ }
+ else if (distance(y0, x0, y, x) < 3)
+ {
+ place_floor(y, x);
+ }
+
+ /* Outside -- make it granite so that arena works */
+ else
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+
+ /* Proper boundary for arena */
+ if (((y + rad) == y0) || ((y - rad) == y0) ||
+ ((x + rad) == x0) || ((x - rad) == x0))
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ }
+ }
+
+ /* Find visible outer walls and set to be FEAT_OUTER */
+ add_outer_wall(x0, y0, light, x0 - rad - 1, y0 - rad - 1,
+ x0 + rad + 1, y0 + rad + 1);
+
+ /* Check to see if there is room for an inner vault */
+ for (x = x0 - 2; x <= x0 + 2; x++)
+ {
+ for (y = y0 - 2; y <= y0 + 2; y++)
+ {
+ if (!get_is_floor(x, y))
+ {
+ /* Wall in the way */
+ emptyflag = FALSE;
+ }
+ }
+ }
+
+ if (emptyflag && (rand_int(2) == 0))
+ {
+ /* Build the vault */
+ build_small_room(x0, y0);
+
+ /* Place a treasure in the vault */
+ place_object(y0, x0, FALSE, FALSE, OBJ_FOUND_FLOOR);
+
+ /* Let's guard the treasure well */
+ vault_monsters(y0, x0, rand_int(2) + 3);
+
+ /* Traps naturally */
+ vault_traps(y0, x0, 4, 4, rand_int(3) + 2);
+ }
+}
+
+
+/*
+ * Constructs a tunnel between two points
+ *
+ * This function must be called BEFORE any streamers are created,
+ * since we use the special "granite wall" sub-types to keep track
+ * of legal places for corridors to pierce rooms.
+ *
+ * We use "door_flag" to prevent excessive construction of doors
+ * along overlapping corridors.
+ *
+ * We queue the tunnel grids to prevent door creation along a corridor
+ * which intersects itself.
+ *
+ * We queue the wall piercing grids to prevent a corridor from leaving
+ * a room and then coming back in through the same entrance.
+ *
+ * We "pierce" grids which are "outer" walls of rooms, and when we
+ * do so, we change all adjacent "outer" walls of rooms into "solid"
+ * walls so that no two corridors may use adjacent grids for exits.
+ *
+ * The "solid" wall check prevents corridors from "chopping" the
+ * corners of rooms off, as well as "silly" door placement, and
+ * "excessively wide" room entrances.
+ *
+ * Useful "feat" values:
+ * FEAT_WALL_EXTRA -- granite walls
+ * FEAT_WALL_INNER -- inner room walls
+ * FEAT_WALL_OUTER -- outer room walls
+ * FEAT_WALL_SOLID -- solid room walls
+ * FEAT_PERM_EXTRA -- shop walls (perma)
+ * FEAT_PERM_INNER -- inner room walls (perma)
+ * FEAT_PERM_OUTER -- outer room walls (perma)
+ * FEAT_PERM_SOLID -- dungeon border (perma)
+ */
+static void build_tunnel(int row1, int col1, int row2, int col2, bool_ water)
+{
+ int i, y, x;
+ int tmp_row, tmp_col;
+ int row_dir, col_dir;
+ int start_row, start_col;
+ int main_loop_count = 0;
+
+ bool_ door_flag = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Reset the arrays */
+ dun->tunn_n = 0;
+ dun->wall_n = 0;
+
+ /* Save the starting location */
+ start_row = row1;
+ start_col = col1;
+
+ /* Start out in the correct direction */
+ correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
+
+ /* Keep going until done (or bored) */
+ while ((row1 != row2) || (col1 != col2))
+ {
+ /* Mega-Hack -- Paranoia -- prevent infinite loops */
+ if (main_loop_count++ > 2000) break;
+
+ /* Allow bends in the tunnel */
+ if (rand_int(100) < DUN_TUN_CHG)
+ {
+ /* Acquire the correct direction */
+ correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
+
+ /* Random direction */
+ if (rand_int(100) < DUN_TUN_RND)
+ {
+ rand_dir(&row_dir, &col_dir);
+ }
+ }
+
+ /* Get the next location */
+ tmp_row = row1 + row_dir;
+ tmp_col = col1 + col_dir;
+
+
+ /* Extremely Important -- do not leave the dungeon */
+ while (!in_bounds(tmp_row, tmp_col))
+ {
+ /* Acquire the correct direction */
+ correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
+
+ /* Random direction */
+ if (rand_int(100) < DUN_TUN_RND)
+ {
+ rand_dir(&row_dir, &col_dir);
+ }
+
+ /* Get the next location */
+ tmp_row = row1 + row_dir;
+ tmp_col = col1 + col_dir;
+ }
+
+
+ /* Access the location */
+ c_ptr = &cave[tmp_row][tmp_col];
+
+
+ /* Avoid the edge of the dungeon */
+ if (c_ptr->feat == FEAT_PERM_SOLID) continue;
+
+ /* Avoid the edge of vaults */
+ if (c_ptr->feat == FEAT_PERM_OUTER) continue;
+
+ /* Avoid "solid" granite walls */
+ if (c_ptr->feat == FEAT_WALL_SOLID) continue;
+
+ /*
+ * Pierce "outer" walls of rooms
+ * Cannot trust feat code any longer...
+ */
+ if ((c_ptr->feat == feat_wall_outer) &&
+ (c_ptr->info & CAVE_ROOM))
+ {
+ /* Acquire the "next" location */
+ y = tmp_row + row_dir;
+ x = tmp_col + col_dir;
+
+ /* Hack -- Avoid outer/solid permanent walls */
+ if (cave[y][x].feat == FEAT_PERM_SOLID) continue;
+ if (cave[y][x].feat == FEAT_PERM_OUTER) continue;
+
+ /* Hack -- Avoid outer/solid granite walls */
+ if ((cave[y][x].feat == feat_wall_outer) &&
+ (cave[y][x].info & CAVE_ROOM)) continue;
+ if (cave[y][x].feat == FEAT_WALL_SOLID) continue;
+
+ /* Accept this location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+
+ /* Save the wall location */
+ if (dun->wall_n < WALL_MAX)
+ {
+ dun->wall[dun->wall_n].y = row1;
+ dun->wall[dun->wall_n].x = col1;
+ dun->wall_n++;
+ }
+
+ /* Forbid re-entry near this piercing */
+ for (y = row1 - 1; y <= row1 + 1; y++)
+ {
+ for (x = col1 - 1; x <= col1 + 1; x++)
+ {
+ /* Convert adjacent "outer" walls as "solid" walls */
+ if ((cave[y][x].feat == feat_wall_outer) &&
+ (cave[y][x].info & CAVE_ROOM))
+ {
+ /* Change the wall to a "solid" wall */
+ /* Mega-Hack -- to be brought back later... */
+ cave_set_feat(y, x, FEAT_WALL_SOLID);
+ }
+ }
+ }
+ }
+
+ /* Travel quickly through rooms */
+ else if (c_ptr->info & (CAVE_ROOM))
+ {
+ /* Accept the location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+ }
+
+ /* Tunnel through all other walls */
+ else if ((c_ptr->feat == d_info[dungeon_type].fill_type1) ||
+ (c_ptr->feat == d_info[dungeon_type].fill_type2) ||
+ (c_ptr->feat == d_info[dungeon_type].fill_type3))
+ {
+ /* Accept this location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+
+ /* Save the tunnel location */
+ if (dun->tunn_n < TUNN_MAX)
+ {
+ dun->tunn[dun->tunn_n].y = row1;
+ dun->tunn[dun->tunn_n].x = col1;
+ dun->tunn_n++;
+ }
+
+ /* Allow door in next grid */
+ door_flag = FALSE;
+ }
+
+ /* Handle corridor intersections or overlaps */
+ else
+ {
+ /* Accept the location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+
+ /* Collect legal door locations */
+ if (!door_flag)
+ {
+ /* Save the door location */
+ if (dun->door_n < DOOR_MAX)
+ {
+ dun->door[dun->door_n].y = row1;
+ dun->door[dun->door_n].x = col1;
+ dun->door_n++;
+ }
+
+ /* No door in next grid */
+ door_flag = TRUE;
+ }
+
+ /* Hack -- allow pre-emptive tunnel termination */
+ if (rand_int(100) >= DUN_TUN_CON)
+ {
+ /* Distance between row1 and start_row */
+ tmp_row = row1 - start_row;
+ if (tmp_row < 0) tmp_row = ( -tmp_row);
+
+ /* Distance between col1 and start_col */
+ tmp_col = col1 - start_col;
+ if (tmp_col < 0) tmp_col = ( -tmp_col);
+
+ /* Terminate the tunnel */
+ if ((tmp_row > 10) || (tmp_col > 10)) break;
+ }
+ }
+ }
+
+
+ /* Turn the tunnel into corridor */
+ for (i = 0; i < dun->tunn_n; i++)
+ {
+ /* Access the grid */
+ y = dun->tunn[i].y;
+ x = dun->tunn[i].x;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear previous contents, add a floor */
+ if (!water)
+ {
+ place_floor(y, x);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_SHAL_WATER);
+ }
+ }
+
+
+ /* Apply the piercings that we found */
+ for (i = 0; i < dun->wall_n; i++)
+ {
+ /* Access the grid */
+ y = dun->wall[i].y;
+ x = dun->wall[i].x;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear previous contents, add up floor */
+ place_floor(y, x);
+
+ /* Occasional doorway */
+ if (!(dungeon_flags1 & DF1_NO_DOORS) &&
+ (rand_int(100) < DUN_TUN_PEN))
+ {
+ /* Place a random door */
+ place_random_door(y, x);
+ }
+ }
+}
+
+
+
+
+/*
+ * Count the number of "corridor" grids adjacent to the given grid.
+ *
+ * Note -- Assumes "in_bounds(y1, x1)"
+ *
+ * XXX XXX This routine currently only counts actual "empty floor"
+ * grids which are not in rooms. We might want to also count stairs,
+ * open doors, closed doors, etc.
+ */
+static int next_to_corr(int y1, int x1)
+{
+ int i, y, x, k = 0;
+
+ cave_type *c_ptr;
+
+ /* Scan adjacent grids */
+ for (i = 0; i < 4; i++)
+ {
+ /* Extract the location */
+ y = y1 + ddy_ddd[i];
+ x = x1 + ddx_ddd[i];
+
+ /* Skip non floors */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Skip non "empty floor" grids */
+ if ((c_ptr->feat != d_info[dungeon_type].floor1) &&
+ (c_ptr->feat != d_info[dungeon_type].floor2) &&
+ (c_ptr->feat != d_info[dungeon_type].floor3))
+ {
+ continue;
+ }
+
+ /* Skip grids inside rooms */
+ if (c_ptr->info & (CAVE_ROOM)) continue;
+
+ /* Count these grids */
+ k++;
+ }
+
+ /* Return the number of corridors */
+ return (k);
+}
+
+
+/*
+ * Determine if the given location is "between" two walls,
+ * and "next to" two corridor spaces. XXX XXX XXX
+ *
+ * Assumes "in_bounds(y,x)"
+ */
+static bool_ possible_doorway(int y, int x)
+{
+ /* Count the adjacent corridors */
+ if (next_to_corr(y, x) >= 2)
+ {
+ /* Check Vertical */
+ if ((f_info[cave[y - 1][x].feat].flags1 & FF1_WALL) &&
+ (f_info[cave[y + 1][x].feat].flags1 & FF1_WALL))
+ {
+ return (TRUE);
+ }
+
+ /* Check Horizontal */
+ if ((f_info[cave[y][x - 1].feat].flags1 & FF1_WALL) &&
+ (f_info[cave[y][x + 1].feat].flags1 & FF1_WALL))
+ {
+ return (TRUE);
+ }
+ }
+
+ /* No doorway */
+ return (FALSE);
+}
+
+
+/*
+ * Places doors around y, x position
+ */
+static void try_doors(int y, int x)
+{
+ bool_ dir_ok[4];
+ int i, k, n;
+ int yy, xx;
+
+ /* Paranoia */
+ /* if (!in_bounds(y, x)) return; */
+
+ /* Some dungeons don't have doors at all */
+ if (dungeon_flags1 & (DF1_NO_DOORS)) return;
+
+ /* Reset tally */
+ n = 0;
+
+ /* Look four cardinal directions */
+ for (i = 0; i < 4; i++)
+ {
+ /* Assume NG */
+ dir_ok[i] = FALSE;
+
+ /* Access location */
+ yy = y + ddy_ddd[i];
+ xx = x + ddx_ddd[i];
+
+ /* Out of level boundary */
+ if (!in_bounds(yy, xx)) continue;
+
+ /* Ignore walls */
+ if (f_info[cave[yy][xx].feat].flags1 & (FF1_WALL)) continue;
+
+ /* Ignore room grids */
+ if (cave[yy][xx].info & (CAVE_ROOM)) continue;
+
+ /* Not a doorway */
+ if (!possible_doorway(yy, xx)) continue;
+
+ /* Accept the direction */
+ dir_ok[i] = TRUE;
+
+ /* Count good spots */
+ n++;
+ }
+
+ /* Use the traditional method 75% of time */
+ if (rand_int(100) < 75)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ /* Bad locations */
+ if (!dir_ok[i]) continue;
+
+ /* Place one of various kinds of doors */
+ if (rand_int(100) < DUN_TUN_JCT)
+ {
+ /* Access location */
+ yy = y + ddy_ddd[i];
+ xx = x + ddx_ddd[i];
+
+ /* Place a door */
+ place_random_door(yy, xx);
+ }
+ }
+ }
+
+ /* Use alternative method */
+ else
+ {
+ /* A crossroad */
+ if (n == 4)
+ {
+ /* Clear OK flags XXX */
+ for (i = 0; i < 4; i++) dir_ok[i] = FALSE;
+
+ /* Put one or two secret doors */
+ dir_ok[rand_int(4)] = TRUE;
+ dir_ok[rand_int(4)] = TRUE;
+ }
+
+ /* A T-shaped intersection or two possible doorways */
+ else if ((n == 3) || (n == 2))
+ {
+ /* Pick one random location from the list */
+ k = rand_int(n);
+
+ for (i = 0; i < 4; i++)
+ {
+ /* Reject all but k'th OK direction */
+ if (dir_ok[i] && (k-- != 0)) dir_ok[i] = FALSE;
+ }
+ }
+
+ /* Place secret door(s) */
+ for (i = 0; i < 4; i++)
+ {
+ /* Bad location */
+ if (!dir_ok[i]) continue;
+
+ /* Access location */
+ yy = y + ddy_ddd[i];
+ xx = x + ddx_ddd[i];
+
+ /* Place a secret door */
+ place_secret_door(yy, xx);
+ }
+ }
+}
+
+
+/*
+ * Attempt to build a room of the given type at the given block
+ *
+ * Note that we restrict the number of "crowded" rooms to reduce
+ * the chance of overflowing the monster list during level creation.
+ */
+static bool_ room_build(int y, int x, int typ)
+{
+ /* Restrict level */
+ if ((dun_level < roomdep[typ]) && !ironman_rooms) return (FALSE);
+
+ /* Restrict "crowded" rooms */
+ if (dun->crowded && ((typ == 5) || (typ == 6))) return (FALSE);
+
+ /* Build a room */
+ switch (typ)
+ {
+ /* Build an appropriate room */
+ case 12:
+ build_type12(y, x);
+ break;
+ case 11:
+ build_type11(y, x);
+ break;
+ case 10:
+ build_type10(y, x);
+ break;
+ case 9:
+ build_type9 (y, x);
+ break;
+ case 8:
+ build_type8 (y, x);
+ break;
+ case 7:
+ build_type7 (y, x);
+ break;
+ case 6:
+ build_type6 (y, x);
+ break;
+ case 5:
+ build_type5 (y, x);
+ break;
+ case 4:
+ build_type4 (y, x);
+ break;
+ case 3:
+ build_type3 (y, x);
+ break;
+ case 2:
+ build_type2 (y, x);
+ break;
+ case 1:
+ build_type1 (y, x);
+ break;
+
+ /* Paranoia */
+ default:
+ return (FALSE);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * Set level boundaries
+ */
+void set_bounders(bool_ empty_level)
+{
+ int y, x;
+
+ /* Special boundary walls -- Top */
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[0][x].mimic = fill_type[rand_int(100)];
+ else cave[0][x].mimic = cave[0][x].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(0, x, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- Bottom */
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[cur_hgt - 1][x].mimic = fill_type[rand_int(100)];
+ else cave[cur_hgt - 1][x].mimic = cave[cur_hgt - 1][x].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(cur_hgt - 1, x, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- Left */
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[y][0].mimic = fill_type[rand_int(100)];
+ else cave[y][0].mimic = cave[y][0].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(y, 0, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- Right */
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[y][cur_wid - 1].mimic = fill_type[rand_int(100)];
+ else cave[y][cur_wid - 1].mimic = cave[y][cur_wid - 1].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(y, cur_wid - 1, FEAT_PERM_SOLID);
+ }
+}
+
+/* Needed to refill empty levels */
+static void fill_level(bool_ use_floor, byte smooth);
+
+/*
+ * Generate a normal dungeon level
+ */
+bool_ level_generate_dungeon()
+{
+ int i, k, y, x, y1, x1, branch = get_branch();
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ int max_vault_ok = 2;
+
+ bool_ destroyed = FALSE;
+ bool_ empty_level = FALSE;
+ bool_ cavern = FALSE;
+ s16b town_level = 0;
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+ /* Check for arena level */
+ if ((dungeon_flags1 & (DF1_EMPTY)) ||
+ (empty_levels && (rand_int(EMPTY_LEVEL) == 0)))
+ {
+ empty_level = TRUE;
+
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print("Arena level.");
+ }
+
+ /* Refill the level with floor tiles */
+ fill_level(empty_level, d_ptr->fill_method);
+ }
+
+ /* Possible cavern */
+ if ((dungeon_flags1 & DF1_CAVERN) && (rand_int(dun_level / 2) > DUN_CAVERN))
+ {
+ cavern = TRUE;
+
+ /* Make a large fractal cave in the middle of the dungeon */
+ if (cheat_room)
+ {
+ msg_print("Cavern on level.");
+ }
+
+ build_cavern();
+ }
+
+ /* Possible "destroyed" level */
+ if ((dun_level > 10) && (rand_int(DUN_DEST) == 0))
+ {
+ destroyed = TRUE;
+ }
+
+ /* Hack -- No destroyed "quest" levels */
+ if (is_quest(dun_level)) destroyed = FALSE;
+
+ /* Hack -- No destroyed "small" levels */
+ if ((cur_wid != MAX_WID) || (cur_hgt != MAX_HGT)) destroyed = FALSE;
+
+ /* Hack -- No destroyed levels */
+ if (dungeon_flags1 & DF1_NO_DESTROY) destroyed = FALSE;
+
+ /* Actual maximum number of rooms on this level */
+ dun->row_rooms = cur_hgt / BLOCK_HGT;
+ dun->col_rooms = cur_wid / BLOCK_WID;
+
+
+ /* Initialize the room table */
+ for (y = 0; y < dun->row_rooms; y++)
+ {
+ for (x = 0; x < dun->col_rooms; x++)
+ {
+ dun->room_map[y][x] = FALSE;
+ }
+ }
+
+ /* No "crowded" rooms yet */
+ dun->crowded = FALSE;
+
+ /* No rooms yet */
+ dun->cent_n = 0;
+
+ /* Pick a block for the room */
+ y = rand_int(dun->row_rooms);
+ x = rand_int(dun->col_rooms);
+
+ /* Align dungeon rooms */
+ if (dungeon_align)
+ {
+ /* Slide some rooms right */
+ if ((x % 3) == 0) x++;
+
+ /* Slide some rooms left */
+ if ((x % 3) == 2) x--;
+ }
+
+ /* Ugly */
+ process_hooks(HOOK_BUILD_ROOM1, "(d,d)", y, x);
+
+ /* Build some rooms */
+ for (i = 0; i < DUN_ROOMS; i++)
+ {
+ /* Pick a block for the room */
+ y = rand_int(dun->row_rooms);
+ x = rand_int(dun->col_rooms);
+
+ /* Align dungeon rooms */
+ if (dungeon_align)
+ {
+ /* Slide some rooms right */
+ if ((x % 3) == 0) x++;
+
+ /* Slide some rooms left */
+ if ((x % 3) == 2) x--;
+ }
+
+ /* Destroyed levels are boring */
+ if (destroyed)
+ {
+ /* The deeper you are, the more cavelike the rooms are */
+
+ /* no caves when cavern exists: they look bad */
+ k = randint(100);
+
+ if (!cavern && (k < dun_level))
+ {
+ /* Type 10 -- Fractal cave */
+ if (room_build(y, x, 10)) continue;
+ }
+ else
+ {
+ /* Attempt a "trivial" room */
+ if ((dungeon_flags1 & DF1_CIRCULAR_ROOMS) &&
+ room_build(y, x, 9))
+ {
+ continue;
+ }
+ else if (room_build(y, x, 1)) continue;
+ }
+
+ /* Never mind */
+ continue;
+ }
+
+ /* Attempt an "unusual" room -- no vaults on town levels */
+ if (!town_level &&
+ (ironman_rooms || (rand_int(DUN_UNUSUAL) < dun_level)))
+ {
+ /* Roll for room type */
+ k = (ironman_rooms ? 0 : rand_int(100));
+
+ /* Attempt a very unusual room */ /* test hack */
+ if (ironman_rooms || (rand_int(DUN_UNUSUAL) < dun_level))
+ {
+#ifdef FORCE_V_IDX
+ if (room_build(y, x, 8)) continue;
+#else
+/* Type 8 -- Greater vault (10%) */
+ if (k < 10)
+ {
+ if (max_vault_ok > 1)
+ {
+ if (room_build(y, x, 8)) continue;
+ }
+ else
+ {
+ if (cheat_room) msg_print("Refusing a greater vault.");
+ }
+ }
+
+ /* Type 7 -- Lesser vault (15%) */
+ if (k < 25)
+ {
+ if (max_vault_ok > 0)
+ {
+ if (room_build(y, x, 7)) continue;
+ }
+ else
+ {
+ if (cheat_room) msg_print("Refusing a lesser vault.");
+ }
+ }
+
+
+ /* Type 5 -- Monster nest (15%) */
+ if ((k < 40) && room_build(y, x, 5)) continue;
+
+ /* Type 6 -- Monster pit (15%) */
+ if ((k < 55) && room_build(y, x, 6)) continue;
+
+ /* Type 11 -- Random vault (5%) */
+ if ((k < 60) && room_build(y, x, 11)) continue;
+#endif
+ }
+
+ /* Type 4 -- Large room (25%) */
+ if ((k < 25) && room_build(y, x, 4)) continue;
+
+ /* Type 3 -- Cross room (20%) */
+ if ((k < 45) && room_build(y, x, 3)) continue;
+
+ /* Type 2 -- Overlapping (20%) */
+ if ((k < 65) && room_build(y, x, 2)) continue;
+
+ /* Type 10 -- Fractal cave (15%) */
+ if ((k < 80) && room_build(y, x, 10)) continue;
+
+ /* Type 9 -- Circular (10%) */
+ /* Hack - build standard rectangular rooms if needed */
+ if (k < 90)
+ {
+ if ((dungeon_flags1 & DF1_CIRCULAR_ROOMS) && room_build(y, x, 1)) continue;
+ else if (room_build(y, x, 9)) continue;
+ }
+
+ /* Type 12 -- Crypt (10%) */
+ if ((k < 100) && room_build(y, x, 12)) continue;
+ }
+
+ /* Attempt a trivial room */
+ if (dungeon_flags1 & DF1_CAVE)
+ {
+ if (room_build(y, x, 10)) continue;
+ }
+ else
+ {
+ if ((dungeon_flags1 & DF1_CIRCULAR_ROOMS) && room_build(y, x, 9)) continue;
+ else if (room_build(y, x, 1)) continue;
+ }
+ }
+
+ /* If no rooms are allocated... */
+ while (dun->cent_n == 0)
+ {
+ /* ...force the creation of a small rectangular room */
+ (void)room_build(0, 0, 1);
+ }
+
+ /* Hack -- Scramble the room order */
+ for (i = 0; i < dun->cent_n; i++)
+ {
+ int pick1 = rand_int(dun->cent_n);
+ int pick2 = rand_int(dun->cent_n);
+ y1 = dun->cent[pick1].y;
+ x1 = dun->cent[pick1].x;
+ dun->cent[pick1].y = dun->cent[pick2].y;
+ dun->cent[pick1].x = dun->cent[pick2].x;
+ dun->cent[pick2].y = y1;
+ dun->cent[pick2].x = x1;
+ }
+
+ /* Start with no tunnel doors */
+ dun->door_n = 0;
+
+ /* Hack -- connect the first room to the last room */
+ y = dun->cent[dun->cent_n - 1].y;
+ x = dun->cent[dun->cent_n - 1].x;
+
+ /* Connect all the rooms together */
+ for (i = 0; i < dun->cent_n; i++)
+ {
+ /* Connect the room to the previous room */
+ build_tunnel(dun->cent[i].y, dun->cent[i].x, y, x, FALSE);
+
+ /* Remember the "previous" room */
+ y = dun->cent[i].y;
+ x = dun->cent[i].x;
+ }
+
+ /* Mega-Hack -- Convert FEAT_WALL_SOLID back into outer walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ if (cave[y][x].feat == FEAT_WALL_SOLID)
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ }
+ }
+
+ /* Place intersection doors */
+ for (i = 0; i < dun->door_n; i++)
+ {
+ /* Extract junction location */
+ y = dun->door[i].y;
+ x = dun->door[i].x;
+
+ /* Try placing doors */
+ try_doors(y, x);
+ }
+
+ if (strcmp(game_module, "ToME") == 0)
+ {
+ /* Hack -- Add some magma streamers */
+ if ((dungeon_type == DUNGEON_MORDOR) || (dungeon_type == DUNGEON_ANGBAND))
+ for (i = 0; i < DUN_STR_MAG; i++)
+ {
+ build_streamer(FEAT_MAGMA, DUN_STR_MC);
+ }
+
+ /* Hack -- Add some quartz streamers */
+ if ((dungeon_type == DUNGEON_MORDOR) || (dungeon_type == DUNGEON_ANGBAND))
+ for (i = 0; i < DUN_STR_QUA; i++)
+ {
+ build_streamer(FEAT_QUARTZ, DUN_STR_QC);
+ }
+ }
+
+ /* Add some sand streamers */
+ if ((dungeon_flags1 & DF1_SAND_VEIN) && !rand_int(4))
+ {
+ if ((cheat_room) || (p_ptr->precognition)) msg_print("Sand vein.");
+ build_streamer(FEAT_SANDWALL, DUN_STR_SC);
+ }
+
+ /* Destroy the level if necessary */
+ if (destroyed) destroy_level();
+
+ /* Create the town if needed */
+ if (town_level)
+ {
+ town_gen(town_level);
+ }
+
+ /* Hack -- Add some rivers if requested */
+ if ((dungeon_flags1 & DF1_WATER_RIVER) && !rand_int(4))
+ {
+ if (cheat_room || p_ptr->precognition) msg_print("River of water.");
+ add_river(FEAT_DEEP_WATER, FEAT_SHAL_WATER);
+ }
+ if ((dungeon_flags1 & DF1_LAVA_RIVER) && !rand_int(4))
+ {
+ if ((cheat_room) || (p_ptr->precognition)) msg_print("River of lava.");
+ add_river(FEAT_DEEP_LAVA, FEAT_SHAL_LAVA);
+ }
+
+ if (dungeon_flags1 & DF1_WATER_RIVERS)
+ {
+ int max = 3 + rand_int(2);
+ bool_ said = FALSE;
+
+ for (i = 0; i < max; i++)
+ {
+ if (rand_int(3) == 0)
+ {
+ add_river(FEAT_DEEP_WATER, FEAT_SHAL_WATER);
+ if (!said && ((cheat_room) || (p_ptr->precognition))) msg_print("Rivers of water.");
+ said = TRUE;
+ }
+ }
+ }
+
+ if (dungeon_flags1 & DF1_LAVA_RIVERS)
+ {
+ int max = 2 + rand_int(2);
+ bool_ said = FALSE;
+
+ for (i = 0; i < max; i++)
+ {
+ if (rand_int(3) == 0)
+ {
+ add_river(FEAT_DEEP_LAVA, FEAT_SHAL_LAVA);
+ if (!said && ((cheat_room) || (p_ptr->precognition))) msg_print("Rivers of lava.");
+ said = TRUE;
+ }
+ }
+ }
+
+ /* Add streamers of trees, water, or lava -KMW- */
+ if (!(dungeon_flags1 & DF1_NO_STREAMERS))
+ {
+ int num;
+
+ /*
+ * Flat levels (was: levels 1--2)
+ *
+ * Small trees (penetrate walls)
+ */
+ if ((dungeon_flags1 & DF1_FLAT) && (randint(20) > 15))
+ {
+ num = randint(DUN_STR_QUA);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_SMALL_TREES, 1);
+ }
+ }
+
+ /*
+ * Levels 1 -- 33 (was: 1 -- 19)
+ *
+ * Shallow water (preserve walls)
+ * Deep water (penetrate walls)
+ */
+ if (!(dun_level <= 33) && (randint(20) > 15))
+ {
+ num = randint(DUN_STR_QUA - 1);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_SHAL_WATER, 0);
+ }
+
+ if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_DEEP_WATER, 1);
+ }
+ }
+ }
+
+ /*
+ * Levels 34 -- (was: 20 --)
+ */
+ else if (dun_level > 33)
+ {
+ /*
+ * Shallow lava (preserve walls)
+ * Deep lava (penetrate walls)
+ */
+ if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_SHAL_LAVA, 0);
+ }
+
+ if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA - 1);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_DEEP_LAVA, 1);
+ }
+ }
+ }
+
+ /*
+ * Shallow water (preserve walls)
+ * Deep water (penetrate walls)
+ */
+ else if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA - 1);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_SHAL_WATER, 0);
+ }
+
+ if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_DEEP_WATER, 1);
+ }
+ }
+ }
+ }
+ }
+
+ /* Hack, seems like once a room overrode the level boundaries, this is BAD */
+ set_bounders(empty_level);
+
+ /* Determine the character location */
+ if (!new_player_spot(branch))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Bring the imprinted pets from the old level
+ */
+void replace_all_friends()
+{
+ int i;
+
+ if (p_ptr->wild_mode) return;
+
+ /* Scan every saved pet */
+ for (i = 0; i < max_m_idx; i++)
+ {
+ if ((km_list[i].r_idx) && (km_list[i].status == MSTATUS_COMPANION))
+ {
+ int y = p_ptr->py, x = p_ptr->px;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+
+ /* Find a suitable location */
+ get_pos_player(5, &y, &x);
+ c_ptr = &cave[y][x];
+
+ /* Get a m_idx to use */
+ c_ptr->m_idx = m_pop();
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Actualy place the monster */
+ m_list[c_ptr->m_idx] = km_list[i];
+ m_ptr->fy = y;
+ m_ptr->fx = x;
+ m_ptr->hold_o_idx = 0;
+ }
+ }
+}
+
+/*
+ * Save the imprinted pets from the old level
+ */
+void save_all_friends()
+{
+ if (p_ptr->old_wild_mode) return;
+
+ C_COPY(km_list, m_list, max_m_idx, monster_type);
+}
+
+
+
+/*
+ * Return the dungeon type of the current level(it can only return the
+ * principal dungeons)
+ */
+byte calc_dungeon_type()
+{
+ int i;
+
+ for (i = 0; i < max_d_idx; i++)
+ {
+ if ((dun_level >= d_info[i].mindepth) &&
+ (dun_level <= d_info[i].maxdepth) &&
+ (d_info[i].flags1 & DF1_PRINCIPAL))
+ return (i);
+ }
+ return (0);
+}
+
+
+/*
+ * Build probability tables for walls and floors and set feat_wall_outer
+ * and feat_wall_inner according to the current information in d_info.txt
+ *
+ * *hint* *hint* with this made extern, and we no longer have to
+ * store fill_type and floor_type in the savefile...
+ */
+static void init_feat_info(void)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ int i;
+ int cur_depth, max_depth;
+ int p1, p2;
+ int floor_lim1, floor_lim2;
+ int fill_lim1, fill_lim2;
+
+
+ /* Retrieve dungeon depth info (base 1, to avoid zero divide errors) */
+ cur_depth = (dun_level - d_ptr->mindepth) + 1;
+ max_depth = (d_ptr->maxdepth - d_ptr->mindepth) + 1;
+
+
+ /* Set room wall types */
+ feat_wall_outer = d_ptr->outer_wall;
+ feat_wall_inner = d_ptr->inner_wall;
+
+
+ /* Setup probability info -- Floors */
+ p1 = d_ptr->floor_percent1[0];
+ p2 = d_ptr->floor_percent1[1];
+ floor_lim1 = p1 + (p2 - p1) * cur_depth / max_depth;
+
+ p1 = d_ptr->floor_percent2[0];
+ p2 = d_ptr->floor_percent2[1];
+ floor_lim2 = floor_lim1 + p1 + (p2 - p1) * cur_depth / max_depth;
+
+ /* Setup probability info -- Fillers */
+ p1 = d_ptr->fill_percent1[0];
+ p2 = d_ptr->fill_percent1[1];
+ fill_lim1 = p1 + (p2 - p1) * cur_depth / max_depth;
+
+ p1 = d_ptr->fill_percent2[0];
+ p2 = d_ptr->fill_percent2[1];
+ fill_lim2 = fill_lim1 + p1 + (p2 - p1) * cur_depth / max_depth;
+
+
+ /* Fill the arrays of floors and walls in the good proportions */
+ for (i = 0; i < 100; i++)
+ {
+ if (i < floor_lim1)
+ {
+ floor_type[i] = d_ptr->floor1;
+ }
+ else if (i < floor_lim2)
+ {
+ floor_type[i] = d_ptr->floor2;
+ }
+ else
+ {
+ floor_type[i] = d_ptr->floor3;
+ }
+
+ if (i < fill_lim1)
+ {
+ fill_type[i] = d_ptr->fill_type1;
+ }
+ else if (i < fill_lim2)
+ {
+ fill_type[i] = d_ptr->fill_type2;
+ }
+ else
+ {
+ fill_type[i] = d_ptr->fill_type3;
+ }
+ }
+}
+
+
+/*
+ * Fill a level with wall type specified in A: or L: line of d_info.txt
+ *
+ * 'use_floor', when it is TRUE, tells the function to use floor type
+ * terrains (L:) instead of walls (A:).
+ *
+ * Filling behaviour can be controlled by the second parameter 'smooth',
+ * with the following options available:
+ *
+ * smooth behaviour
+ * ------ ------------------------------------------------------------
+ * 0 Fill the entire level with fill_type1 / floor1
+ * 1 All the grids are randomly selected (== --P5.1.2)
+ * 2 Slightly smoothed -- look like scattered patches
+ * 3 More smoothed -- tend to look like caverns / small scale map
+ * 4-- Max smoothing -- tend to look like landscape/island/
+ * continent etc.
+ *
+ * I put it here, because there's another filler generator in
+ * wild.c, but it works better there, in fact...
+ *
+ * CAVEAT: smoothness of 3 or greater doesn't work well with the
+ * current secret door implementation. Outer walls also need some
+ * rethinking.
+ *
+ * -- pelpel
+ */
+
+/*
+ * Thou shalt not invoke the name of thy RNG in vain.
+ * The Angband RNG generates 28 bit pseudo-random number, hence
+ * 28 / 2 = 14
+ */
+#define MAX_SHIFTS 14
+
+static void fill_level(bool_ use_floor, byte smooth)
+{
+ int y, x;
+ int step;
+ int shift;
+
+
+ /* Convert smoothness to initial step */
+ if (smooth == 0) step = 0;
+ else if (smooth == 1) step = 1;
+ else if (smooth == 2) step = 2;
+ else if (smooth == 3) step = 4;
+ else step = 8;
+
+ /*
+ * Paranoia -- step must be less than or equal to a half of
+ * width or height, whichever shorter
+ */
+ if ((cur_hgt < 16) && (step > 4)) step = 4;
+ if ((cur_wid < 16) && (step > 4)) step = 4;
+
+
+ /* Special case -- simple fill */
+ if (step == 0)
+ {
+ byte filler;
+
+ /* Pick a filler XXX XXX XXX */
+ if (use_floor) filler = d_info[dungeon_type].floor1;
+ else filler = d_info[dungeon_type].fill_type1;
+
+ /* Fill the level with the filler without calling RNG */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, filler);
+ }
+ }
+
+ /* Done */
+ return;
+ }
+
+
+ /*
+ * Fill starting positions -- every 'step' grids horizontally and
+ * vertically
+ */
+ for (y = 0; y < cur_hgt; y += step)
+ {
+ for (x = 0; x < cur_wid; x += step)
+ {
+ /*
+ * Place randomly selected terrain feature using the prebuilt
+ * probability table
+ *
+ * By slightly modifying this, you can build streamers as
+ * well as normal fillers all at once, but this calls for
+ * modifications to the other part of the dungeon generator.
+ */
+ if (use_floor) place_floor(y, x);
+ else place_filler(y, x);
+ }
+ }
+
+
+ /*
+ * Fill spaces between, randomly picking one of their neighbours
+ *
+ * This simple yet powerful algorithm was described by Mike Anderson:
+ *
+ * A B A | B A a B
+ * -> --+-- -> d e b
+ * D C D | C D c C
+ *
+ * a can be either A or B, b B or C, c C or D and d D or A.
+ * e is chosen from A, B, C and D.
+ * Subdivide and repeat the process as many times as you like.
+ *
+ * All the nasty tricks that obscure this simplicity are mine (^ ^;)
+ */
+
+ /* Initialise bit shift counter */
+ shift = MAX_SHIFTS;
+
+ /* Repeat subdivision until all the grids are filled in */
+ while ((step = step >> 1) > 0)
+ {
+ bool_ y_even, x_even;
+ s16b y_wrap, x_wrap;
+ s16b y_sel, x_sel;
+ u32b selector = 0;
+
+ /* Hacklette -- Calculate wrap-around locations */
+ y_wrap = ((cur_hgt - 1) / (step * 2)) * (step * 2);
+ x_wrap = ((cur_wid - 1) / (step * 2)) * (step * 2);
+
+ /* Initialise vertical phase */
+ y_even = 0;
+
+ for (y = 0; y < cur_hgt; y += step)
+ {
+ /* Flip vertical phase */
+ y_even = !y_even;
+
+ /* Initialise horizontal phase */
+ x_even = 0;
+
+ for (x = 0; x < cur_wid; x += step)
+ {
+ /* Flip horizontal phase */
+ x_even = !x_even;
+
+ /* Already filled in by previous iterations */
+ if (y_even && x_even) continue;
+
+ /*
+ * Retrieve next two bits from pseudo-random bit sequence
+ *
+ * You can do well not caring so much about their randomness.
+ *
+ * This is not really necessary, but I don't like to invoke
+ * relatively expensive RNG when we can do with much smaller
+ * number of calls.
+ */
+ if (shift >= MAX_SHIFTS)
+ {
+ selector = rand_int(0x10000000L);
+ shift = 0;
+ }
+ else
+ {
+ selector >>= 2;
+ shift++;
+ }
+
+ /* Vertically in sync */
+ if (y_even) y_sel = y;
+
+ /* Bit 1 selects neighbouring y */
+ else y_sel = (selector & 2) ? y + step : y - step;
+
+ /* Horizontally in sync */
+ if (x_even) x_sel = x;
+
+ /* Bit 0 selects neighbouring x */
+ else x_sel = (selector & 1) ? x + step : x - step;
+
+ /* Hacklette -- Fix out of range indices by wrapping around */
+ if (y_sel >= cur_hgt) y_sel = 0;
+ else if (y_sel < 0) y_sel = y_wrap;
+ if (x_sel >= cur_wid) x_sel = 0;
+ else if (x_sel < 0) x_sel = x_wrap;
+
+ /*
+ * Fill the grid with terrain feature of the randomly
+ * picked up neighbour
+ */
+ cave_set_feat(y, x, cave[y_sel][x_sel].feat);
+ }
+ }
+ }
+}
+
+
+/*
+ * Generate a new dungeon level
+ *
+ * Note that "dun_body" adds about 4000 bytes of memory to the stack.
+ */
+static bool_ cave_gen(void)
+{
+ int i, k, y, x, y1, x1, branch;
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ int max_vault_ok = 2;
+
+ bool_ empty_level = FALSE;
+
+ level_generator_type *generator;
+
+ dun_data dun_body;
+
+ char generator_name[100];
+
+ if (!get_dungeon_generator(generator_name))
+ strnfmt(generator_name, 99, "%s", d_ptr->generator);
+
+ /*
+ * We generate a double dungeon. First we should halve the desired
+ * width/height, generate the dungeon normally, then double it
+ * in both directions
+ */
+ if (dungeon_flags1 & DF1_DOUBLE)
+ {
+ cur_wid /= 2;
+ cur_hgt /= 2;
+ }
+
+ /* Fill the arrays of floors and walls in the good proportions */
+ init_feat_info();
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Global data */
+ dun = &dun_body;
+
+ if (!(max_panel_rows)) max_vault_ok--;
+ if (!(max_panel_cols)) max_vault_ok--;
+
+ /*
+ * Hack -- Start with fill_type's
+ *
+ * Need a way to know appropriate smoothing factor for the current
+ * dungeon. Maybe we need another d_info flag/value.
+ */
+ fill_level(empty_level, d_ptr->fill_method);
+
+ set_bounders(empty_level);
+
+ /*
+ * Call the good level generator
+ */
+ generator = level_generators;
+ while (generator)
+ {
+ if (!strcmp(generator->name, generator_name))
+ {
+ if (!generator->generator(generator->name))
+ return FALSE;
+ break;
+ }
+
+ generator = generator->next;
+ }
+
+ /* Only if requested */
+ if (generator->default_stairs)
+ {
+ /* Is there a dungeon branch ? */
+ if ((branch = get_branch()))
+ {
+ /* Place 5 down stair some walls */
+ alloc_stairs(FEAT_MORE, 5, 3, branch);
+ }
+
+ /* Is there a father dungeon branch ? */
+ if ((branch = get_fbranch()))
+ {
+ /* Place 1 down stair some walls */
+ alloc_stairs(FEAT_LESS, 5, 3, branch);
+ }
+
+ if ((dun_level < d_ptr->maxdepth) || ((dun_level == d_ptr->maxdepth) && (dungeon_flags1 & DF1_FORCE_DOWN)))
+ {
+ /* Place 3 or 4 down stairs near some walls */
+ alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_MORE, rand_range(3, 4), 3, 0);
+
+ /* Place 0 or 1 down shafts near some walls */
+ if (!(dungeon_flags2 & DF2_NO_SHAFT)) alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_SHAFT_DOWN, rand_range(0, 1), 3, 0);
+ }
+
+ if ((dun_level > d_ptr->mindepth) || ((dun_level == d_ptr->mindepth) && (!(dungeon_flags1 & DF1_NO_UP))))
+ {
+ /* Place 1 or 2 up stairs near some walls */
+ alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_LESS, rand_range(1, 2), 3, 0);
+
+ /* Place 0 or 1 up shafts near some walls */
+ if (!(dungeon_flags2 & DF2_NO_SHAFT)) alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_SHAFT_UP, rand_range(0, 1), 3, 0);
+ }
+ }
+
+ process_hooks(HOOK_GEN_LEVEL, "(d)", is_quest(dun_level));
+
+ /* Basic "amount" */
+ k = (dun_level / 3);
+ if (k > 10) k = 10;
+ if (k < 2) k = 2;
+
+ /* Only if requested */
+ if (generator->default_monsters)
+ {
+
+ /*
+ * Pick a base number of monsters
+ */
+ i = d_ptr->min_m_alloc_level;
+
+ /* To make small levels a bit more playable */
+ if ((cur_hgt < MAX_HGT) || (cur_wid < MAX_WID))
+ {
+ int small_tester = i;
+
+ i = (i * cur_hgt) / MAX_HGT;
+ i = (i * cur_wid) / MAX_WID;
+ i += 1;
+
+ if (i > small_tester) i = small_tester;
+ else if (cheat_hear)
+ {
+ msg_format("Reduced monsters base from %d to %d", small_tester, i);
+ }
+ }
+
+ i += randint(8);
+
+ /* Put some monsters in the dungeon */
+ for (i = i + k; i > 0; i--)
+ {
+ (void)alloc_monster(0, TRUE);
+ }
+ }
+
+ /* Check fates */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ /* Ignore empty slots */
+ if (fates[i].fate == FATE_NONE) continue;
+
+ /* Check dungeon depth */
+ if (fates[i].level != dun_level) continue;
+
+ /* Non-serious fates don't always fire */
+ if ((!fates[i].serious) && (randint(2) != 1)) continue;
+
+ /* Player meets his/her fate now... */
+ fate_flag = TRUE;
+
+ switch (fates[i].fate)
+ {
+ case FATE_FIND_O:
+ {
+ int oy = p_ptr->py + 1;
+ int ox = p_ptr->px;
+ object_type *q_ptr, forge;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Mega-Hack */
+ object_prep(q_ptr, fates[i].o_idx);
+
+ /* Mega-Hack */
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, fates[i].serious);
+
+ get_pos_player(10, &oy, &ox);
+
+ /* Drop it from the heaven */
+ drop_near(q_ptr, -1, oy, ox);
+
+ /* Make it icky */
+ fates[i].icky = TRUE;
+ break;
+ }
+ case FATE_FIND_R:
+ {
+ int oy = p_ptr->py + 1;
+ int ox = p_ptr->px;
+
+ get_pos_player(10, &oy, &ox);
+
+ place_monster_one(oy, ox, fates[i].r_idx, 0, fates[i].serious, MSTATUS_ENEMY);
+
+ fates[i].icky = TRUE;
+ break;
+ }
+ case FATE_FIND_A:
+ {
+ int oy = p_ptr->py + 1;
+ int ox = p_ptr->px;
+ object_type *q_ptr = NULL, forge;
+
+ get_pos_player(10, &oy, &ox);
+
+ /* XXX XXX XXX Grant a randart */
+ if (fates[i].a_idx == 0)
+ {
+ int obj_lev;
+ s16b k_idx;
+
+ /* Apply restriction */
+ get_obj_num_hook = kind_is_artifactable;
+
+ /* Object level a la find object fates */
+ obj_lev = max_dlv[dungeon_type] + randint(10);
+
+ /* Rebuild allocation table */
+ get_obj_num_prep();
+
+ /* Roll for an object */
+ k_idx = get_obj_num(obj_lev);
+
+ /* Reset restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Invalidate the allocation table */
+ alloc_kind_table_valid = FALSE;
+
+ /* Get a local object */
+ q_ptr = &forge;
+
+ /* Wipe it */
+ object_wipe(q_ptr);
+
+ /* Create the object */
+ object_prep(q_ptr, k_idx);
+
+ /* SoAC it */
+ create_artifact(q_ptr, FALSE, TRUE);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, oy, ox);
+ }
+
+ /* Grant a normal artefact */
+ else if (a_info[fates[i].a_idx].cur_num == 0)
+ {
+ artifact_type *a_ptr = &a_info[fates[i].a_idx];
+ s16b I_kind;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = fates[i].a_idx;
+
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, oy, ox);
+ }
+
+ fates[i].icky = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Re scan the list to eliminate the inutile fate */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ switch (fates[i].fate)
+ {
+ case FATE_FIND_A:
+ {
+ if (a_info[fates[i].a_idx].cur_num == 1) fates[i].icky = TRUE;
+ break;
+ }
+ case FATE_FIND_R:
+ {
+ if ((r_info[fates[i].r_idx].cur_num == 1) && (r_info[fates[i].r_idx].flags1 & RF1_UNIQUE)) fates[i].icky = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Only if requested */
+ if (generator->default_miscs)
+ {
+ /* Place some traps in the dungeon */
+ alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_TRAP, randint(k * 2));
+
+ /* Put some rubble in corridors */
+ alloc_object(ALLOC_SET_CORR, ALLOC_TYP_RUBBLE, randint(k));
+ }
+
+ /* Only if requested */
+ if (generator->default_objects)
+ {
+ /* Put some objects in rooms */
+ if (dungeon_type != DUNGEON_DEATH) alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_OBJECT, randnor(DUN_AMT_ROOM, 3));
+
+ /* Put some objects/gold in the dungeon */
+ if (dungeon_type != DUNGEON_DEATH) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_OBJECT, randnor(DUN_AMT_ITEM, 3));
+ if (dungeon_type != DUNGEON_DEATH) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_GOLD, randnor(DUN_AMT_GOLD, 3));
+ }
+
+ /* Only if requested */
+ if (generator->default_miscs)
+ {
+ /* Put some altars */
+ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_ALTAR, randnor(DUN_AMT_ALTAR, 3));
+
+ /* Put some between gates */
+ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_BETWEEN, randnor(DUN_AMT_BETWEEN, 3));
+
+ /* Put some fountains */
+ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_FOUNTAIN, randnor(DUN_AMT_FOUNTAIN, 3));
+ }
+
+ /* Put an Artifact and Artifact Guardian is requested */
+ if (d_ptr->final_guardian && (d_ptr->maxdepth == dun_level))
+ {
+ int oy;
+ int ox;
+ int m_idx, tries = 10000;
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ oy = randint(cur_hgt - 4) + 2;
+ ox = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(oy, ox)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place the guardian */
+ m_allow_special[d_ptr->final_guardian] = TRUE;
+ place_monster_one(oy, ox, d_ptr->final_guardian, 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[d_ptr->final_guardian] = FALSE;
+
+
+ m_idx = cave[oy][ox].m_idx;
+
+ if (!m_idx && wizard) cmsg_print(TERM_L_RED, "WARNING: Could not place guardian.");
+
+ /*
+ * If guardian is successfully created and his/her/its
+ * treasure hasn't been found, let him/her/it own that
+ */
+ if (m_idx && d_ptr->final_artifact &&
+ (a_info[d_ptr->final_artifact].cur_num == 0))
+ {
+ artifact_type *a_ptr = &a_info[d_ptr->final_artifact];
+ object_type *q_ptr, forge, *o_ptr;
+ int I_kind, o_idx;
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ /* Proceed only if there's an object slot available */
+ if (o_idx)
+ {
+ a_allow_special[d_ptr->final_artifact] = TRUE;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = d_ptr->final_artifact;
+
+ /* Actually create it */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Where it is found ? */
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = d_ptr->final_guardian;
+ q_ptr->found_aux2 = 0;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ a_allow_special[d_ptr->final_artifact] = FALSE;
+
+ /* Get the item */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_list[m_idx].hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idx = o_idx;
+ }
+ }
+
+ if (m_idx && d_ptr->final_object &&
+ (k_info[d_ptr->final_object].artifact == FALSE))
+ {
+ object_type *q_ptr, forge, *o_ptr;
+ int o_idx;
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ /* Proceed only if there's an object slot available */
+ if (o_idx)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ k_allow_special[d_ptr->final_object] = TRUE;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Create the final object */
+ object_prep(q_ptr, d_ptr->final_object);
+ apply_magic(q_ptr, 1, FALSE, FALSE, FALSE);
+
+ /* Where it is found ? */
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = d_ptr->final_guardian;
+ q_ptr->found_aux2 = 0;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ k_allow_special[d_ptr->final_object] = FALSE;
+
+ k_info[d_ptr->final_object].artifact = TRUE;
+
+ /* Get the item */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_list[m_idx].hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idx = o_idx;
+ }
+ }
+ }
+
+ if ((empty_level) && (randint(DARK_EMPTY) != 1 || (randint(100) > dun_level)))
+ wiz_lite();
+
+ /* Now double the generated dungeon */
+ if (dungeon_flags1 & DF1_DOUBLE)
+ {
+ /* We begin at the bottom-right corner and from there move
+ * up/left (this way we don't need another array for the
+ * dungeon data) */
+ /* Note: we double the border permanent walls, too. It is
+ * easier this way and I think it isn't too ugly */
+ for (y = cur_hgt - 1, y1 = y * 2; y >= 0; y--, y1 -= 2)
+ for (x = cur_wid - 1, x1 = x * 2; x >= 0; x--, x1 -= 2)
+ {
+ int disp[4][2] = {{0, 0}, {0, + 1}, { + 1, 0}, { + 1, + 1}};
+
+ cave_type *c_ptr[4], *cc_ptr = &cave[y][x];
+ object_type *o_ptr = &o_list[cc_ptr->o_idx];
+ monster_type *m_ptr = &m_list[cc_ptr->m_idx];
+
+ /*
+ * Now we copy the generated data to the
+ * appropriate grids
+ */
+ for (i = 0; i < 4; i++)
+ {
+ c_ptr[i] = &cave[y1 + disp[i][0]][x1 + disp[i][1]];
+ *c_ptr[i] = *cc_ptr;
+ c_ptr[i]->o_idx = 0;
+ c_ptr[i]->m_idx = 0;
+
+ if (cc_ptr->feat == FEAT_BETWEEN)
+ {
+ int xxx = cc_ptr->special & 0xFF;
+ int yyy = cc_ptr->special >> 8;
+
+ xxx *= 2;
+ yyy *= 2;
+ xxx += disp[i][1];
+ yyy += disp[i][0];
+ c_ptr[i]->special = xxx + (yyy << 8);
+ }
+ }
+
+ /* Objects should be put only in 1 of the
+ * new grids (otherwise we would segfault
+ * a lot) ... */
+ if (cc_ptr->o_idx != 0)
+ {
+ i = rand_int(4);
+ c_ptr[i]->o_idx = cc_ptr->o_idx;
+ o_ptr->iy = y1 + disp[i][0];
+ o_ptr->ix = x1 + disp[i][1];
+ }
+
+ /* ..just like monsters */
+ if (cc_ptr->m_idx != 0)
+ {
+ i = rand_int(4);
+ c_ptr[i]->m_idx = cc_ptr->m_idx;
+ m_ptr->fy = y1 + disp[i][0];
+ m_ptr->fx = x1 + disp[i][1];
+ }
+ }
+
+ /* Set the width/height ... */
+ cur_wid *= 2;
+ cur_hgt *= 2;
+
+ /* ... and player position to the right place */
+ p_ptr->py *= 2;
+ p_ptr->px *= 2;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Builds the arena after it is entered -KMW-
+ */
+static void build_arena(void)
+{
+ int yval, y_height, y_depth, xval, x_left, x_right;
+ register int i, j;
+
+ yval = SCREEN_HGT / 2;
+ xval = SCREEN_WID / 2;
+ y_height = yval - 10 + SCREEN_HGT;
+ y_depth = yval + 10 + SCREEN_HGT;
+ x_left = xval - 32 + SCREEN_WID;
+ x_right = xval + 32 + SCREEN_WID;
+
+ for (i = y_height; i <= y_height + 5; i++)
+ {
+ for (j = x_left; j <= x_right; j++)
+ {
+ cave_set_feat(i, j, FEAT_PERM_EXTRA);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ for (i = y_depth; i >= y_depth - 5; i--)
+ {
+ for (j = x_left; j <= x_right; j++)
+ {
+ cave_set_feat(i, j, FEAT_PERM_EXTRA);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ for (j = x_left; j <= x_left + 17; j++)
+ {
+ for (i = y_height; i <= y_depth; i++)
+ {
+ cave_set_feat(i, j, FEAT_PERM_EXTRA);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ for (j = x_right; j >= x_right - 17; j--)
+ {
+ for (i = y_height; i <= y_depth; i++)
+ {
+ cave_set_feat(i, j, FEAT_PERM_EXTRA);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+
+ cave_set_feat(y_height + 6, x_left + 18, FEAT_PERM_EXTRA);
+ cave[y_height + 6][x_left + 18].info |= (CAVE_GLOW | CAVE_MARK);
+ cave_set_feat(y_depth - 6, x_left + 18, FEAT_PERM_EXTRA);
+ cave[y_depth - 6][x_left + 18].info |= (CAVE_GLOW | CAVE_MARK);
+ cave_set_feat(y_height + 6, x_right - 18, FEAT_PERM_EXTRA);
+ cave[y_height + 6][x_right - 18].info |= (CAVE_GLOW | CAVE_MARK);
+ cave_set_feat(y_depth - 6, x_right - 18, FEAT_PERM_EXTRA);
+ cave[y_depth - 6][x_right - 18].info |= (CAVE_GLOW | CAVE_MARK);
+
+ i = y_height + 5;
+ j = xval + SCREEN_WID;
+ cave_set_feat(i, j, FEAT_SHOP);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ player_place(i + 1, j);
+}
+
+
+/*
+ * Town logic flow for generation of arena -KMW-
+ */
+static void arena_gen(void)
+{
+ int y, x;
+ int qy = SCREEN_HGT;
+ int qx = SCREEN_WID;
+ bool_ daytime;
+
+ /* Day time */
+ if ((turn % (10L * DAY)) < ((10L * DAY) / 2))
+ daytime = TRUE;
+
+ /* Night time */
+ else
+ daytime = FALSE;
+
+ /* Start with solid walls */
+ for (y = 0; y < MAX_HGT; y++)
+ {
+ for (x = 0; x < MAX_WID; x++)
+ {
+ /* Create "solid" perma-wall */
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+
+ /* Illuminate and memorize the walls */
+ cave[y][x].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+
+ /* Then place some floors */
+ for (y = qy + 1; y < qy + SCREEN_HGT - 1; y++)
+ {
+ for (x = qx + 1; x < qx + SCREEN_WID - 1; x++)
+ {
+ /* Create empty floor */
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Darken and forget the floors */
+ cave[y][x].info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Day time */
+ if (daytime)
+ {
+ /* Perma-Lite */
+ cave[y][x].info |= (CAVE_GLOW);
+
+ /* Memorize */
+ if (view_perma_grids) cave[y][x].info |= (CAVE_MARK);
+ }
+ }
+ }
+
+ build_arena();
+
+ place_monster_aux(p_ptr->py + 5, p_ptr->px, arena_monsters[p_ptr->arena_number],
+ FALSE, FALSE, MSTATUS_ENEMY);
+}
+
+
+/*
+ * Generate a quest level
+ */
+static void quest_gen(void)
+{
+ process_hooks(HOOK_GEN_QUEST, "(d)", is_quest(dun_level));
+}
+
+/*
+ * Creates a special level
+ */
+
+/* Mega-Hack */
+#define REGEN_HACK 0x02
+
+bool_ build_special_level(void)
+{
+ char buf[80];
+ int y, x, ystart = 2, xstart = 2;
+ s16b level;
+
+ /* No special levels on the surface */
+ if (!dun_level) return FALSE;
+
+ level = dun_level - d_info[dungeon_type].mindepth;
+ if ((!get_dungeon_save(buf)) && (special_lvl[level][dungeon_type])) return FALSE;
+ if (!get_dungeon_special(buf)) return FALSE;
+
+ /* Big town */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON | INIT_POSITION;
+ process_dungeon_file(buf, &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ special_lvl[level][dungeon_type] = REGEN_HACK;
+ generate_special_feeling = TRUE;
+
+ /* Special feeling because it's special */
+ good_item_flag = TRUE;
+
+ /*
+ * Hack -- It's better/more dangerous than a greater vault.
+ * Better to have a rating field in special level description.
+ */
+ rating += 40;
+
+ return TRUE;
+}
+
+/*
+ * Prepare regeneration of a special level, which should not happen,
+ * but just in case...
+ */
+static void wipe_special_level(void)
+{
+ s16b level;
+ char buf[80];
+
+ /* No special levels on the surface */
+ if (!dun_level) return;
+
+ process_hooks(HOOK_LEVEL_REGEN, "()");
+
+ /* Calculate relative depth */
+ level = dun_level - d_info[dungeon_type].mindepth;
+
+ /* No special level at this depth */
+ if ((!get_dungeon_save(buf)) &&
+ special_lvl[level][dungeon_type]) return;
+ if (!get_dungeon_special(buf)) return;
+
+ /* Clear the Mega-Hack flag */
+ if (special_lvl[level][dungeon_type] == REGEN_HACK)
+ special_lvl[level][dungeon_type] = FALSE;
+}
+
+/*
+ * Finalise generation of a special level
+ */
+static void finalise_special_level(void)
+{
+ s16b level;
+ char buf[80];
+
+ /* No special levels on the surface */
+ if (!dun_level) return;
+
+ process_hooks(HOOK_LEVEL_END_GEN, "()");
+
+ /* Calculate relative depth */
+ level = dun_level - d_info[dungeon_type].mindepth;
+
+ /* No special level at this depth */
+ if ((!get_dungeon_save(buf)) &&
+ special_lvl[level][dungeon_type]) return;
+ if (!get_dungeon_special(buf)) return;
+
+ /* Set the "generated" flag */
+ if (special_lvl[level][dungeon_type] == REGEN_HACK)
+ special_lvl[level][dungeon_type] = TRUE;
+}
+
+/*
+ * Give some magical energy to the each grid of the level
+ */
+void generate_grid_mana()
+{
+ int y, x, mana, mult;
+ bool_ xtra_magic = FALSE;
+
+ if (randint(XTRA_MAGIC) == 1)
+ {
+ xtra_magic = TRUE;
+
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print("Magical level");
+ }
+ }
+
+ mult = ((xtra_magic) ? 3 : 2);
+
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Calculate the amount of mana in each grid */
+ mana = mult * m_bonus(255, dun_level) / 2;
+ if (xtra_magic) mana += 10 + rand_int(10);
+
+ /* Never more than 255 or less than 0(paranoia) */
+ if (mana < 0) mana = 0;
+ if (mana > 255) mana = 255;
+
+ c_ptr->mana = mana;
+ }
+ }
+}
+
+
+/*
+ * Generates a random dungeon level -RAK-
+ *
+ * Hack -- regenerate any "overflow" levels
+ *
+ * Hack -- allow auto-scumming via a gameplay option.
+ */
+void generate_cave(void)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ int tester_1, tester_2;
+ int y, x, num, i;
+ bool_ loaded = FALSE;
+ char buf[80];
+ s16b town_level = 0;
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+ generate_special_feeling = FALSE;
+
+ /* Initialize the flags with the basic dungeon flags */
+ if (!dun_level)
+ {
+ dungeon_flags1 = d_info[DUNGEON_WILDERNESS].flags1;
+ dungeon_flags2 = d_info[DUNGEON_WILDERNESS].flags2;
+ }
+ else
+ {
+ dungeon_flags1 = d_ptr->flags1;
+ dungeon_flags2 = d_ptr->flags2;
+ }
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+ /* Save the imprinted monsters */
+ save_all_friends();
+ wipe_m_list();
+
+ /* Seed the RNG if appropriate */
+ if (town_level)
+ {
+ Rand_quick = TRUE;
+ Rand_value = town_info[town_level].seed;
+ }
+
+ process_hooks(HOOK_GEN_LEVEL_BEGIN, "");
+
+ /* Try to load a saved level */
+ if (get_dungeon_save(buf))
+ {
+ /* No effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ effects[i].time = 0;
+ }
+
+ /* Start with a blank cave */
+ for (y = 0; y < MAX_HGT; y++)
+ {
+ for (x = 0; x < MAX_WID; x++)
+ {
+ /* No flags */
+ cave[y][x].info = 0;
+
+ /* No features */
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+
+ /* No objects */
+ cave[y][x].o_idx = 0;
+
+ /* No monsters */
+ cave[y][x].m_idx = 0;
+
+ /* No traps */
+ cave[y][x].t_idx = 0;
+
+ /* No mimic */
+ cave[y][x].mimic = 0;
+
+ /* No effects */
+ cave[y][x].effect = 0;
+
+ /* No inscription */
+ cave[y][x].inscription = 0;
+
+ /* No flow */
+ cave[y][x].cost = 0;
+ cave[y][x].when = 0;
+ }
+ }
+
+ loaded = load_dungeon(buf);
+ }
+
+ /* No saved level -- generate new one */
+ if (!loaded)
+ {
+ if (!get_dungeon_special(buf) ||
+ !special_lvl[dun_level - d_info[dungeon_type].mindepth][dungeon_type])
+ {
+ get_level_flags();
+ }
+
+ /* Generate */
+ for (num = 0; TRUE; num++)
+ {
+ bool_ okay = TRUE;
+
+ cptr why = NULL;
+
+ /* No effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ effects[i].time = 0;
+ }
+
+ /* Start with a blank cave */
+ for (y = 0; y < MAX_HGT; y++)
+ {
+ for (x = 0; x < MAX_WID; x++)
+ {
+ /* No flags */
+ cave[y][x].info = 0;
+
+ /* No features */
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+
+ /* No objects */
+ cave[y][x].o_idx = 0;
+
+ /* No monsters */
+ cave[y][x].m_idx = 0;
+
+ /* No traps */
+ cave[y][x].t_idx = 0;
+
+ /* No mimic */
+ cave[y][x].mimic = 0;
+
+ /* No effect */
+ cave[y][x].effect = 0;
+
+ /* No inscription */
+ cave[y][x].inscription = 0;
+
+ /* No flow */
+ cave[y][x].cost = 0;
+ cave[y][x].when = 0;
+ }
+ }
+
+
+ /* XXX XXX XXX XXX */
+ o_max = 1;
+
+
+ /* Mega-Hack -- no player yet */
+ p_ptr->px = p_ptr->py = 0;
+
+
+ /* Mega-Hack -- no panel yet */
+ panel_row_min = 0;
+ panel_row_max = 0;
+ panel_col_min = 0;
+ panel_col_max = 0;
+
+
+ /* Reset the monster generation level */
+ if (dungeon_type != DUNGEON_DEATH) monster_level = dun_level;
+ else monster_level = (p_ptr->lev * 2) + 10 + rand_int(40);
+
+ /* Reset the object generation level */
+ object_level = dun_level;
+
+ /* Nothing special here yet */
+ good_item_flag = FALSE;
+
+ /* Nothing good here yet */
+ rating = 0;
+
+ /* No ambush here yet */
+ ambush_flag = FALSE;
+
+ /* No fated level here yet */
+ fate_flag = FALSE;
+
+ /* Build the arena -KMW- */
+ if (p_ptr->inside_arena)
+ {
+ /* Small arena */
+ arena_gen();
+ }
+
+ /* Quest levels -KMW- */
+ else if (p_ptr->inside_quest)
+ {
+ quest_gen();
+ }
+
+ /* Special levels */
+ else if (build_special_level())
+ {
+ /* nothing */
+ }
+
+ /* Build the town */
+ else if (!dun_level)
+ {
+ /* Big town */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ /* Big wilderness mode */
+ if (!p_ptr->wild_mode)
+ {
+ /* Make the wilderness */
+ wilderness_gen(0);
+ }
+
+ /* Small wilderness mode */
+ else
+ {
+ /* Make the wilderness */
+ wilderness_gen_small();
+ }
+
+
+ okay = TRUE;
+ }
+
+ /* Build a dungeon level */
+ else
+ {
+ /* Requested size level */
+ if (d_ptr->size_x != -1)
+ {
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print ("A 'size' dungeon level.");
+ }
+
+ cur_hgt = d_ptr->size_y * SCREEN_HGT;
+ cur_wid = d_ptr->size_x * SCREEN_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ if (cheat_room)
+ {
+ msg_format("X:%d, Y:%d.", max_panel_cols, max_panel_rows);
+ }
+ }
+ /* Very small (1 x 1 panel) level */
+ else if (!(dungeon_flags1 & DF1_BIG) &&
+ (dungeon_flags1 & DF1_SMALLEST))
+ {
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print ("A 'small' dungeon level.");
+ }
+
+ cur_hgt = SCREEN_HGT;
+ cur_wid = SCREEN_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = 1;
+ max_panel_cols = 1;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ if (cheat_room)
+ {
+ msg_format("X:1, Y:1.");
+ }
+ }
+
+ /* Small level */
+ else if (!(dungeon_flags1 & DF1_BIG) &&
+ (always_small_level ||
+ (dungeon_flags1 & DF1_SMALL) ||
+ (small_levels && rand_int(SMALL_LEVEL) == 0)))
+ {
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print ("A 'small' dungeon level.");
+ }
+
+ tester_1 = rand_range(1, (MAX_HGT / SCREEN_HGT));
+ tester_2 = rand_range(1, (MAX_WID / SCREEN_WID) - 1);
+
+ cur_hgt = tester_1 * SCREEN_HGT;
+ cur_wid = tester_2 * SCREEN_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ if (cheat_room)
+ {
+ msg_format("X:%d, Y:%d.", max_panel_cols, max_panel_rows);
+ }
+ }
+
+ /* Normal level */
+ else
+ {
+ /* Use full panels */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+ }
+
+ /* Generate a level */
+ if (!cave_gen())
+ {
+ why = "could not place player";
+ okay = FALSE;
+ }
+ }
+
+ /* Extract the feeling */
+ if (rating > 100) feeling = 2;
+ else if (rating > 80) feeling = 3;
+ else if (rating > 60) feeling = 4;
+ else if (rating > 40) feeling = 5;
+ else if (rating > 30) feeling = 6;
+ else if (rating > 20) feeling = 7;
+ else if (rating > 10) feeling = 8;
+ else if (rating > 0) feeling = 9;
+ else feeling = 10;
+
+ /* Hack -- Have a special feeling sometimes */
+ if (good_item_flag && !p_ptr->preserve) feeling = 1;
+
+ /* It takes 1000 game turns for "feelings" to recharge */
+ if ((turn - old_turn) < 1000) feeling = 0;
+
+ /* Hack -- no feeling in the town */
+ if (!dun_level) feeling = 0;
+
+
+ /* Prevent object over-flow */
+ if (o_max >= max_o_idx)
+ {
+ /* Message */
+ why = "too many objects";
+
+ /* Message */
+ okay = FALSE;
+ }
+
+ /* Prevent monster over-flow */
+ if (m_max >= max_m_idx)
+ {
+ /* Message */
+ why = "too many monsters";
+
+ /* Message */
+ okay = FALSE;
+ }
+
+ /* Mega-Hack -- "auto-scum" */
+ if (auto_scum && (num < 100) && !p_ptr->inside_quest && dun_level)
+ {
+ /* Require "goodness" */
+ if ((feeling > 9) ||
+ ((dun_level >= 5) && (feeling > 8)) ||
+ ((dun_level >= 10) && (feeling > 7)) ||
+ ((dun_level >= 20) && (feeling > 6)) ||
+ ((dun_level >= 40) && (feeling > 5)))
+ {
+ /* Give message to cheaters */
+ if (cheat_room || cheat_hear ||
+ cheat_peek || cheat_xtra || p_ptr->precognition)
+ {
+ /* Message */
+ why = "boring level";
+ }
+
+ /* Try again */
+ okay = FALSE;
+ }
+ }
+
+ /* Accept */
+ if (okay || town_level) break;
+
+ /* Message */
+ if (why) msg_format("Generation restarted (%s)", why);
+
+ /* Wipe the objects */
+ wipe_o_list();
+
+ /* Wipe the monsters */
+ wipe_m_list();
+
+ /* Clear the fate icky flags */
+ for (i = 0; i < MAX_FATES; i++) fates[i].icky = FALSE;
+
+ /*
+ * Mega-Hack -- Reset special level flag if necessary
+ * XXX XXX XXX
+ */
+ wipe_special_level();
+ }
+
+ /* Give some mana to each grid -- DG */
+ generate_grid_mana();
+ }
+
+ /* Put the kept monsters -- DG */
+ if (!p_ptr->wild_mode) replace_all_friends();
+
+ /* Hack -- Clear used up fates */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if (fates[i].icky)
+ {
+ /* Mark the artefact as generated */
+ if ((fates[i].fate == FATE_FIND_A) && fates[i].a_idx)
+ {
+ a_info[fates[i].a_idx].cur_num = 1;
+ }
+ fates[i].fate = FATE_NONE;
+ fates[i].icky = FALSE;
+ }
+ }
+
+ /* Set special level generated flag if applicable */
+ finalise_special_level();
+
+ /* No teleporatations yet */
+ last_teleportation_y = -1;
+ last_teleportation_x = -1;
+
+ /* Mark the dungeon town as found */
+ if (town_level)
+ {
+ /* Set the known flag */
+ town_info[town_level].flags |= (TOWN_KNOWN);
+ }
+
+ /* The dungeon is ready */
+ character_dungeon = TRUE;
+
+ /* Remember when this level was "created" */
+ old_turn = turn;
+
+ /* Provide astral chars with the full map */
+ if (p_ptr->astral && dun_level)
+ {
+ wiz_lite_extra();
+ }
+
+ /* Player should get the first move upon entering the dungeon */
+ p_ptr->energy = 100;
+}
diff --git a/src/gods.c b/src/gods.c
new file mode 100644
index 00000000..b8b8fd3a
--- /dev/null
+++ b/src/gods.c
@@ -0,0 +1,139 @@
+/* File: gods.c */
+
+/* Purpose: Deities code */
+
+/*
+ * Copyright (c) 2002 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Add amt piety is god is god
+ */
+void inc_piety(int god, s32b amt)
+{
+ s32b old = p_ptr->grace;
+
+ if ((god == GOD_ALL) || (god == p_ptr->pgod))
+ {
+ set_grace(p_ptr->grace + amt);
+
+ if(amt > 0 && p_ptr->grace <= old)
+ set_grace(300000);
+
+ if(amt < 0 && p_ptr->grace >= old)
+ set_grace(-300000);
+ }
+}
+
+/*
+ * Renounce to religion
+ */
+void abandon_god(int god)
+{
+ if ((god == GOD_ALL) || (god == p_ptr->pgod))
+ {
+ p_ptr->pgod = GOD_NONE;
+ set_grace(0);
+ }
+}
+
+/*
+ * Get a religion
+ */
+void follow_god(int god, bool_ silent)
+{
+ /* Poor unbelievers, i'm so mean ... BOUHAHAHA */
+ if (get_skill(SKILL_ANTIMAGIC))
+ {
+ msg_print("Don't be silly; you don't believe in gods.");
+ return;
+ }
+
+ /* Are we allowed ? */
+ if (process_hooks(HOOK_FOLLOW_GOD, "(d,s)", god, "ask"))
+ return;
+
+ if (p_ptr->pgod == GOD_NONE)
+ {
+ p_ptr->pgod = god;
+
+ /* Melkor offer Udun magic */
+ GOD(GOD_MELKOR)
+ {
+ s_info[SKILL_UDUN].hidden = FALSE;
+ if (!silent) msg_print("You feel the dark powers of Melkor in you. You can now use the Udun skill.");
+ }
+
+ /* Anything to be done? */
+ process_hooks(HOOK_FOLLOW_GOD, "(d,s)", god, "done");
+ }
+}
+
+/*
+ * Show religious info.
+ */
+bool_ show_god_info(bool_ ext)
+{
+ int pgod = p_ptr->pgod;
+
+ deity_type *d_ptr;
+
+ if (pgod < 0)
+ {
+ msg_print("You don't worship anyone.");
+ msg_print(NULL);
+ return FALSE;
+ }
+ else
+ {
+ int i;
+
+ d_ptr = &deity_info[pgod];
+
+ msg_print(NULL);
+
+ character_icky = TRUE;
+ Term_save();
+
+ text_out(format("You worship %s. ", d_ptr->name));
+ for (i = 0; (i < 10) && (strcmp(d_ptr->desc[i], "")); i++)
+ text_out(d_ptr->desc[i]);
+ text_out("\n");
+
+ inkey();
+
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Rescale the wisdom value to a 0 <-> max range
+ */
+int wisdom_scale(int max)
+{
+ int i = p_ptr->stat_ind[A_WIS];
+
+ return (i * max) / 37;
+}
+
+/* Find a god by name */
+int find_god(cptr name)
+{
+ int i;
+
+ for (i = 0; i < max_gods; i++)
+ {
+ /* The name matches */
+ if (streq(deity_info[i].name, name)) return (i);
+ }
+ return -1;
+}
diff --git a/src/h-basic.h b/src/h-basic.h
new file mode 100644
index 00000000..b5ea5d87
--- /dev/null
+++ b/src/h-basic.h
@@ -0,0 +1,25 @@
+/* File: h-basic.h */
+
+#ifndef INCLUDED_H_BASIC_H
+#define INCLUDED_H_BASIC_H
+
+/*
+ * The most basic "include" file.
+ *
+ * This file simply includes other low level header files.
+ */
+
+/* System Configuration */
+#include "h-config.h"
+
+/* System includes/externs */
+#include "h-system.h"
+
+/* Basic types */
+#include "h-type.h"
+
+/* Basic constants and macros */
+#include "h-define.h"
+
+#endif
+
diff --git a/src/h-config.h b/src/h-config.h
new file mode 100644
index 00000000..53670e8f
--- /dev/null
+++ b/src/h-config.h
@@ -0,0 +1,274 @@
+/* File: h-config.h */
+
+#ifndef INCLUDED_H_CONFIG_H
+#define INCLUDED_H_CONFIG_H
+
+/*
+ * Choose the hardware, operating system, and compiler.
+ * Also, choose various "system level" compilation options.
+ * A lot of these definitions take effect in "h-system.h"
+ *
+ * Note that you may find it simpler to define some of these
+ * options in the "Makefile", especially any options describing
+ * what "system" is being used.
+ */
+
+
+/*
+ * no system definitions are needed for 4.3BSD, SUN OS, DG/UX
+ */
+
+/*
+ * OPTION: Compile on a Macintosh (see "A-mac-h" or "A-mac-pch")
+ */
+#ifndef MACINTOSH
+/* #define MACINTOSH */
+#endif
+
+/*
+ * OPTION: Compile on Windows (automatic)
+ */
+#ifndef WINDOWS
+/* #define WINDOWS */
+#endif
+
+/*
+ * OPTION: Compile on a SYS III version of UNIX
+ */
+#ifndef SYS_III
+/* #define SYS_III */
+#endif
+
+/*
+ * OPTION: Compile on a SYS V version of UNIX (not Solaris)
+ */
+#ifndef SYS_V
+/* #define SYS_V */
+#endif
+
+/*
+ * OPTION: Compile on a HPUX version of UNIX
+ */
+#ifndef HPUX
+/* #define HPUX */
+#endif
+
+/*
+ * OPTION: Compile on an SGI running IRIX
+ */
+#ifndef SGI
+/* #define SGI */
+#endif
+
+/*
+ * OPTION: Compile on a SunOS machine
+ */
+#ifndef SUNOS
+/* #define SUNOS */
+#endif
+
+/*
+ * OPTION: Compile on a Solaris machine
+ */
+#ifndef SOLARIS
+/* #define SOLARIS */
+#endif
+
+/*
+ * OPTION: Compile on an ultrix/4.2BSD/Dynix/etc. version of UNIX,
+ * Do not define this if you are on any kind of SunOS.
+ */
+#ifndef ULTRIX
+/* #define ULTRIX */
+#endif
+
+
+
+/*
+ * Extract the "SUNOS" flag from the compiler
+ */
+#if defined(sun)
+# ifndef SUNOS
+# define SUNOS
+# endif
+#endif
+
+/*
+ * Extract the "ULTRIX" flag from the compiler
+ */
+#if defined(ultrix) || defined(Pyramid)
+# ifndef ULTRIX
+# define ULTRIX
+# endif
+#endif
+
+/*
+ * Extract the "ATARI" flag from the compiler [cjh]
+ */
+#if defined(__atarist) || defined(__atarist__)
+# ifndef ATARI
+# define ATARI
+# endif
+#endif
+
+/*
+ * Extract the "SGI" flag from the compiler
+ */
+#ifdef sgi
+# ifndef SGI
+# define SGI
+# endif
+#endif
+
+/*
+ * Extract the "MSDOS" flag from the compiler
+ */
+#ifdef __MSDOS__
+# ifndef MSDOS
+# define MSDOS
+# endif
+#endif
+
+/*
+ * Extract the "WINDOWS" flag from the compiler
+ */
+#if defined(_Windows) || defined(__WINDOWS__) || \
+ defined(__WIN32__) || defined(WIN32) || \
+ defined(__WINNT__) || defined(__NT__)
+# ifndef WINDOWS
+# define WINDOWS
+# endif
+#endif
+
+
+
+/*
+ * OPTION: Define "L64" if a "long" is 64-bits. See "h-types.h".
+ * The only such platform that angband is ported to is currently
+ * DEC Alpha AXP running OSF/1 (OpenVMS uses 32-bit longs).
+ */
+#if defined(__alpha) && defined(__osf__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__ia64) || defined(__ia64__) || defined(__mips64) || defined(__ppc64__) || defined(__PPC64__) || defined(__powerpc64__) || defined(__64BIT__) || defined(__sparc64__) || defined(__LP64__)
+# define L64
+#endif
+
+
+
+/*
+ * OPTION: set "SET_UID" if the machine is a "multi-user" machine.
+ * This option is used to verify the use of "uids" and "gids" for
+ * various "Unix" calls, and of "pids" for getting a random seed,
+ * and of the "umask()" call for various reasons, and to guess if
+ * the "kill()" function is available, and for permission to use
+ * functions to extract user names and expand "tildes" in filenames.
+ * It is also used for "locking" and "unlocking" the score file.
+ * Basically, SET_UID should *only* be set for "Unix" machines,
+ * or for the "Atari" platform which is Unix-like, apparently
+ */
+#if !defined(MACINTOSH) && !defined(WINDOWS) && \
+ !defined(MSDOS)
+# define SET_UID
+#endif
+
+
+/*
+ * OPTION: Set "USG" for "System V" versions of Unix
+ * This is used to choose a "lock()" function, and to choose
+ * which header files ("string.h" vs "strings.h") to include.
+ * It is also used to allow certain other options, such as options
+ * involving userid's, or multiple users on a single machine, etc.
+ */
+#ifdef SET_UID
+# if defined(SYS_III) || defined(SYS_V) || defined(SOLARIS) || \
+ defined(HPUX) || defined(SGI) || defined(ATARI)
+# ifndef USG
+# define USG
+# endif
+# endif
+#endif
+
+
+/*
+ * Every system seems to use its own symbol as a path separator.
+ * Default to the standard Unix slash, but attempt to change this
+ * for various other systems. Note that any system that uses the
+ * "period" as a separator (i.e. ACORN) will have to pretend that
+ * it uses the slash, and do its own mapping of period <-> slash.
+ */
+#undef PATH_SEP
+#define PATH_SEP "/"
+#ifdef MACINTOSH
+# undef PATH_SEP
+# define PATH_SEP ":"
+#endif
+#if defined(WINDOWS) || defined(WINNT)
+# undef PATH_SEP
+# define PATH_SEP "\\"
+#endif
+#if defined(MSDOS) || defined(OS2)
+# undef PATH_SEP
+# define PATH_SEP "\\"
+#endif
+#ifdef __GO32__
+# undef PATH_SEP
+# define PATH_SEP "/"
+#endif
+
+
+/*
+ * The Macintosh allows the use of a "file type" when creating a file
+ */
+#if defined(MACINTOSH) && !defined(applec) || defined(MACH_O_CARBON)
+# define FILE_TYPE_TEXT 'TEXT'
+# define FILE_TYPE_DATA 'DATA'
+# define FILE_TYPE_SAVE 'SAVE'
+# define FILE_TYPE(X) (_ftype = (X))
+#else
+# define FILE_TYPE(X) ((void)0)
+#endif
+
+
+/*
+ * OPTION: Hack -- Make sure "strchr()" and "strrchr()" will work
+ */
+#if defined(SYS_III) || defined(SYS_V) || defined(MSDOS)
+# if !defined(__TURBOC__) && !defined(__WATCOMC__)
+# define strchr index
+# define strrchr rindex
+# endif
+#endif
+
+
+/*
+ * OPTION: Define "HAS_STRICMP" only if "stricmp()" exists.
+ * Note that "stricmp()" is not actually used by Angband.
+ */
+/* #define HAS_STRICMP */
+
+/*
+ * Linux has "stricmp()" with a different name
+ */
+#if defined(linux)
+# define HAS_STRICMP
+# define stricmp strcasecmp
+#endif
+
+
+/*
+ * OPTION: Define "HAS_MEMSET" only if "memset()" exists.
+ * Note that the "memset()" routines are used in "z-virt.h"
+ */
+#define HAS_MEMSET
+
+
+/*
+ * OPTION: Define "HAS_USLEEP" only if "usleep()" exists.
+ * Note that this is only relevant for "SET_UID" machines
+ */
+#ifdef SET_UID
+# if !defined(HPUX) && !defined(ULTRIX) && !defined(SOLARIS) && \
+ !defined(SGI) && !defined(ISC)
+# define HAS_USLEEP
+# endif
+#endif
+
+#endif
diff --git a/src/h-define.h b/src/h-define.h
new file mode 100644
index 00000000..cb36b189
--- /dev/null
+++ b/src/h-define.h
@@ -0,0 +1,111 @@
+/* File: h-define.h */
+
+#ifndef INCLUDED_H_DEFINE_H
+#define INCLUDED_H_DEFINE_H
+
+/*
+ * Define some simple constants
+ */
+
+
+/*
+ * Hack -- Define NULL
+ */
+#ifndef NULL
+# ifdef __STDC__
+# define NULL ((void*)0)
+# else
+# define NULL ((char*)0)
+# endif /* __STDC__ */
+#endif /* NULL */
+
+
+/*
+ * Hack -- force definitions -- see fd_seek()
+ */
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+# define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+# define SEEK_END 2
+#endif
+
+/*
+ * Hack -- force definitions -- see fd_lock() XXX XXX XXX
+ */
+#ifndef F_UNLCK
+# define F_UNLCK 0
+#endif
+#ifndef F_RDLCK
+# define F_RDLCK 1
+#endif
+#ifndef F_WRLCK
+# define F_WRLCK 2
+#endif
+
+
+/*
+ * The constants "TRUE" and "FALSE"
+ */
+
+#undef TRUE
+#define TRUE 1
+
+#undef FALSE
+#define FALSE 0
+
+
+
+
+/**** Simple "Macros" ****/
+
+/*
+ * Force a character to lowercase/uppercase
+ */
+#define FORCELOWER(A) ((isupper((A))) ? tolower((A)) : (A))
+#define FORCEUPPER(A) ((islower((A))) ? toupper((A)) : (A))
+
+
+/*
+ * Non-typed minimum value macro
+ */
+#undef MIN
+#define MIN(a,b) (((a) > (b)) ? (b) : (a))
+
+/*
+ * Non-typed maximum value macro
+ */
+#undef MAX
+#define MAX(a,b) (((a) < (b)) ? (b) : (a))
+
+/*
+ * Non-typed absolute value macro
+ */
+#undef ABS
+#define ABS(a) (((a) < 0) ? (-(a)) : (a))
+
+/*
+ * Non-typed sign extractor macro
+ */
+#undef SGN
+#define SGN(a) (((a) < 0) ? (-1) : ((a) != 0))
+
+
+/*
+ * Note that all "index" values must be "lowercase letters", while
+ * all "digits" must be "digits". Control characters can be made
+ * from any legal characters. XXX XXX XXX
+ */
+#define A2I(X) ((X) - 'a')
+#define I2A(X) ((X) + 'a')
+#define D2I(X) ((X) - '0')
+#define I2D(X) ((X) + '0')
+#define KTRL(X) ((X) & 0x1F)
+#define ESCAPE '\033'
+
+
+#endif
+
diff --git a/src/h-system.h b/src/h-system.h
new file mode 100644
index 00000000..50cc0af8
--- /dev/null
+++ b/src/h-system.h
@@ -0,0 +1,138 @@
+/* File: h-system.h */
+
+#ifndef INCLUDED_H_SYSTEM_H
+#define INCLUDED_H_SYSTEM_H
+
+/*
+ * Include the basic "system" files.
+ *
+ * Make sure all "system" constants/macros are defined.
+ * Make sure all "system" functions have "extern" declarations.
+ *
+ * This file is a big hack to make other files less of a hack.
+ * This file has been rebuilt -- it may need a little more work.
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#if defined(NeXT)
+# include <libc.h>
+#else
+# include <stdlib.h>
+#endif
+
+
+#ifdef SET_UID
+
+# include <sys/types.h>
+
+# if defined(Pyramid) || defined(NeXT) || defined(SUNOS) || \
+ defined(NCR3K) || defined(SUNOS) || defined(ibm032) || \
+ defined(__osf__) || defined(ISC) || defined(SGI) || \
+ defined(linux)
+# include <sys/time.h>
+# endif
+
+# if !defined(SGI) && !defined(ULTRIX)
+# include <sys/timeb.h>
+# endif
+
+#endif
+
+
+#include <time.h>
+
+
+
+#ifdef MACINTOSH
+# include <unix.h>
+#endif
+
+#if defined(WINDOWS) || defined(MSDOS)
+# include <io.h>
+#endif
+
+#if !defined(MACINTOSH) && \
+ !defined(__MWERKS__)
+# if defined(__TURBOC__) || defined(__WATCOMC__)
+# include <mem.h>
+# else
+# include <memory.h>
+# endif
+#endif
+
+
+#if !defined(NeXT) && !defined(__MWERKS__)
+# include <fcntl.h>
+#endif
+
+
+#ifdef SET_UID
+
+# ifndef USG
+# include <sys/param.h>
+# include <sys/file.h>
+# endif
+
+# ifdef linux
+# include <sys/file.h>
+# endif
+
+# include <pwd.h>
+
+# include <unistd.h>
+
+# include <sys/stat.h>
+
+# if defined(SOLARIS)
+# include <netdb.h>
+# endif
+
+#endif
+
+#ifdef __DJGPP__
+#include <unistd.h>
+#endif /* __DJGPP__ */
+
+#ifdef SET_UID
+
+#ifdef USG
+# include <string.h>
+#else
+# include <strings.h>
+# ifndef strstr
+extern char *strstr();
+# endif
+# ifndef strchr
+extern char *strchr();
+# endif
+# ifndef strrchr
+extern char *strrchr();
+# endif
+#endif
+
+#else
+
+# include <string.h>
+
+#endif
+
+
+
+#if !defined(linux) && !defined(__MWERKS__)
+extern long atol();
+#endif
+
+
+#include <stdarg.h>
+
+
+#endif
+
+/* There was a bug introduced in 10.4.11; working around it */
+#ifdef __APPLE__
+#define GETLOGIN_BROKEN
+#endif
diff --git a/src/h-type.h b/src/h-type.h
new file mode 100644
index 00000000..5dbb4975
--- /dev/null
+++ b/src/h-type.h
@@ -0,0 +1,178 @@
+/* File: h-type.h */
+
+#ifndef INCLUDED_H_TYPE_H
+#define INCLUDED_H_TYPE_H
+
+/*
+ * Basic "types".
+ *
+ * Note the attempt to make all basic types have 4 letters.
+ * This improves readibility and standardizes the code.
+ *
+ * Likewise, all complex types are at least 4 letters.
+ * Thus, almost every three letter word is a legal variable.
+ * But beware of certain reserved words ('for' and 'if' and 'do').
+ *
+ * Note that the type used in structures for bit flags should be uint.
+ * As long as these bit flags are sequential, they will be space smart.
+ *
+ * Note that on some machines, apparently "signed char" is illegal.
+ *
+ * It must be true that char/byte takes exactly 1 byte
+ * It must be true that sind/uind takes exactly 2 bytes
+ * It must be true that sbig/ubig takes exactly 4 bytes
+ *
+ * On Sparc's, a sint takes 4 bytes (2 is legal)
+ * On Sparc's, a uint takes 4 bytes (2 is legal)
+ * On Sparc's, a long takes 4 bytes (8 is legal)
+ * On Sparc's, a huge takes 4 bytes (8 is legal)
+ * On Sparc's, a vptr takes 4 bytes (8 is legal)
+ * On Sparc's, a real takes 8 bytes (4 is legal)
+ *
+ * Note that some files have already been included by "h-include.h"
+ * These include <stdio.h> and <sys/types>, which define some types
+ * In particular, uint is defined so we do not have to define it
+ *
+ * Also, see <limits.h> for min/max values for sind, uind, long, huge
+ * (SHRT_MIN, SHRT_MAX, USHRT_MAX, LONG_MIN, LONG_MAX, ULONG_MAX)
+ * These limits should be verified and coded into "h-constant.h".
+ */
+
+
+
+/*** Special 4 letter names for some standard types ***/
+
+
+/* A standard pointer (to "void" because ANSI C says so) */
+typedef void *vptr;
+
+/* A simple pointer (to unmodifiable strings) */
+typedef const char *cptr;
+typedef char *mcptr;
+
+
+/* Since float's are silly, hard code real numbers as doubles */
+typedef double real;
+
+
+/* Error codes for function return values */
+/* Success = 0, Failure = -N, Problem = +N */
+typedef int errr;
+
+
+/*
+ * Hack -- prevent problems with non-MACINTOSH
+ */
+#undef uint
+#define uint uint_hack
+
+/*
+ * Hack -- prevent problems with MSDOS and WINDOWS
+ */
+#undef huge
+#define huge huge_hack
+
+/*
+ * Hack -- prevent problems with AMIGA
+ */
+#undef byte
+#define byte byte_hack
+
+/* Note that "signed char" is not always "defined" */
+/* So always use "s16b" to hold small signed values */
+/* A signed byte of memory */
+/* typedef signed char syte; */
+
+/* Note that unsigned values can cause math problems */
+/* An unsigned byte of memory */
+typedef unsigned char byte;
+
+/* Note that a bool is smaller than a full "int" */
+/* Simple True/False type */
+typedef char bool_;
+
+
+/* A signed, standard integer (at least 2 bytes) */
+typedef int sint;
+
+/* An unsigned, "standard" integer (often pre-defined) */
+typedef unsigned int uint;
+
+
+/* The largest possible signed integer (pre-defined) */
+/* typedef long long; */
+
+/* The largest possible unsigned integer */
+typedef unsigned long huge;
+
+
+/* Signed/Unsigned 16 bit value */
+typedef signed short s16b;
+typedef unsigned short u16b;
+
+/* Signed/Unsigned 32 bit value */
+#ifdef L64 /* 64 bit longs */
+typedef signed int s32b;
+typedef unsigned int u32b;
+#else
+typedef signed long s32b;
+typedef unsigned long u32b;
+#endif
+
+
+/*** Pointers to all the basic types defined above ***/
+
+typedef real *real_ptr;
+typedef errr *errr_ptr;
+typedef char *char_ptr;
+typedef byte *byte_ptr;
+typedef bool_ *bool_ptr;
+typedef sint *sint_ptr;
+typedef uint *uint_ptr;
+typedef long *long_ptr;
+typedef huge *huge_ptr;
+typedef s16b *s16b_ptr;
+typedef u16b *u16b_ptr;
+typedef s32b *s32b_ptr;
+typedef u32b *u32b_ptr;
+typedef vptr *vptr_ptr;
+typedef cptr *cptr_ptr;
+
+
+
+/*** Pointers to Functions with simple return types and any args ***/
+
+typedef void (*func_void)();
+typedef errr (*func_errr)();
+typedef char (*func_char)();
+typedef byte (*func_byte)();
+typedef bool_ (*func_bool)();
+typedef sint (*func_sint)();
+typedef uint (*func_uint)();
+typedef real (*func_real)();
+typedef vptr (*func_vptr)();
+typedef cptr (*func_cptr)();
+
+
+
+/*** Pointers to Functions of special types (for various purposes) ***/
+
+/* A generic function takes a user data and a special data */
+typedef errr (*func_gen)(vptr, vptr);
+
+/* An equality testing function takes two things to compare (bool) */
+typedef bool_ (*func_eql)(vptr, vptr);
+
+/* A comparison function takes two things and to compare (-1,0,+1) */
+typedef sint (*func_cmp)(vptr, vptr);
+
+/* A hasher takes a thing (and a max hash size) to hash (0 to siz - 1) */
+typedef uint (*func_hsh)(vptr, uint);
+
+/* A key extractor takes a thing and returns (a pointer to) some key */
+typedef vptr (*func_key)(vptr);
+
+
+
+#endif
+
diff --git a/src/help.c b/src/help.c
new file mode 100644
index 00000000..d0bdbedf
--- /dev/null
+++ b/src/help.c
@@ -0,0 +1,23 @@
+/* File: help.c */
+
+/* Purpose: ingame help */
+/*
+ * Actually this is now handled by lua,
+ * I'll remove this file when I feel un-lazy
+ */
+
+/*
+ * Copyright (c) 2001 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Driver for the context-sensitive help system
+ */
+void ingame_help(bool_ enable)
+{}
diff --git a/src/init1.c b/src/init1.c
new file mode 100644
index 00000000..9715831d
--- /dev/null
+++ b/src/init1.c
@@ -0,0 +1,11819 @@
+/* File: init1.c */
+
+/* Purpose: Initialization (part 1) -BEN- */
+
+#include "angband.h"
+
+
+/*
+ * This file is used to initialize various variables and arrays for the
+ * Angband game. Note the use of "fd_read()" and "fd_write()" to bypass
+ * the common limitation of "read()" and "write()" to only 32767 bytes
+ * at a time.
+ *
+ * Several of the arrays for Angband are built from "template" files in
+ * the "lib/file" directory, from which quick-load binary "image" files
+ * are constructed whenever they are not present in the "lib/data"
+ * directory, or if those files become obsolete, if we are allowed.
+ *
+ * Warning -- the "ascii" file parsers use a minor hack to collect the
+ * name and text information in a single pass. Thus, the game will not
+ * be able to load any template file with more than 20K of names or 60K
+ * of text, even though technically, up to 64K should be legal.
+ */
+
+
+/*** Helper arrays for parsing ascii template files ***/
+
+/*
+ * Monster Blow Methods
+ */
+static cptr r_info_blow_method[] =
+{
+ "*",
+ "HIT",
+ "TOUCH",
+ "PUNCH",
+ "KICK",
+ "CLAW",
+ "BITE",
+ "STING",
+ "XXX1",
+ "BUTT",
+ "CRUSH",
+ "ENGULF",
+ "CHARGE",
+ "CRAWL",
+ "DROOL",
+ "SPIT",
+ "EXPLODE",
+ "GAZE",
+ "WAIL",
+ "SPORE",
+ "XXX4",
+ "BEG",
+ "INSULT",
+ "MOAN",
+ "SHOW",
+ NULL
+};
+
+
+/*
+ * Monster Blow Effects
+ */
+static cptr r_info_blow_effect[] =
+{
+ "*",
+ "HURT",
+ "POISON",
+ "UN_BONUS",
+ "UN_POWER",
+ "EAT_GOLD",
+ "EAT_ITEM",
+ "EAT_FOOD",
+ "EAT_LITE",
+ "ACID",
+ "ELEC",
+ "FIRE",
+ "COLD",
+ "BLIND",
+ "CONFUSE",
+ "TERRIFY",
+ "PARALYZE",
+ "LOSE_STR",
+ "LOSE_INT",
+ "LOSE_WIS",
+ "LOSE_DEX",
+ "LOSE_CON",
+ "LOSE_CHR",
+ "LOSE_ALL",
+ "SHATTER",
+ "EXP_10",
+ "EXP_20",
+ "EXP_40",
+ "EXP_80",
+ "DISEASE",
+ "TIME",
+ "INSANITY",
+ "HALLU",
+ "PARASITE",
+ "ABOMINATION",
+ NULL
+};
+
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags1[] =
+{
+ "UNIQUE",
+ "QUESTOR",
+ "MALE",
+ "FEMALE",
+ "CHAR_CLEAR",
+ "CHAR_MULTI",
+ "ATTR_CLEAR",
+ "ATTR_MULTI",
+ "FORCE_DEPTH",
+ "FORCE_MAXHP",
+ "FORCE_SLEEP",
+ "FORCE_EXTRA",
+ "FRIEND",
+ "FRIENDS",
+ "ESCORT",
+ "ESCORTS",
+ "NEVER_BLOW",
+ "NEVER_MOVE",
+ "RAND_25",
+ "RAND_50",
+ "ONLY_GOLD",
+ "ONLY_ITEM",
+ "DROP_60",
+ "DROP_90",
+ "DROP_1D2",
+ "DROP_2D2",
+ "DROP_3D2",
+ "DROP_4D2",
+ "DROP_GOOD",
+ "DROP_GREAT",
+ "DROP_USEFUL",
+ "DROP_CHOSEN"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags2[] =
+{
+ "STUPID",
+ "SMART",
+ "CAN_SPEAK",
+ "REFLECTING",
+ "INVISIBLE",
+ "COLD_BLOOD",
+ "EMPTY_MIND",
+ "WEIRD_MIND",
+ "DEATH_ORB",
+ "REGENERATE",
+ "SHAPECHANGER",
+ "ATTR_ANY",
+ "POWERFUL",
+ "ELDRITCH_HORROR",
+ "AURA_FIRE",
+ "AURA_ELEC",
+ "OPEN_DOOR",
+ "BASH_DOOR",
+ "PASS_WALL",
+ "KILL_WALL",
+ "MOVE_BODY",
+ "KILL_BODY",
+ "TAKE_ITEM",
+ "KILL_ITEM",
+ "BRAIN_1",
+ "BRAIN_2",
+ "BRAIN_3",
+ "BRAIN_4",
+ "BRAIN_5",
+ "BRAIN_6",
+ "BRAIN_7",
+ "BRAIN_8"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags3[] =
+{
+ "ORC",
+ "TROLL",
+ "GIANT",
+ "DRAGON",
+ "DEMON",
+ "UNDEAD",
+ "EVIL",
+ "ANIMAL",
+ "THUNDERLORD",
+ "GOOD",
+ "AURA_COLD", /* TODO: Implement aura_cold */
+ "NONLIVING",
+ "HURT_LITE",
+ "HURT_ROCK",
+ "SUSCEP_FIRE",
+ "SUSCEP_COLD",
+ "IM_ACID",
+ "IM_ELEC",
+ "IM_FIRE",
+ "IM_COLD",
+ "IM_POIS",
+ "RES_TELE",
+ "RES_NETH",
+ "RES_WATE",
+ "RES_PLAS",
+ "RES_NEXU",
+ "RES_DISE",
+ "UNIQUE_4",
+ "NO_FEAR",
+ "NO_STUN",
+ "NO_CONF",
+ "NO_SLEEP"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags4[] =
+{
+ "SHRIEK",
+ "MULTIPLY",
+ "S_ANIMAL",
+ "ROCKET",
+ "ARROW_1",
+ "ARROW_2",
+ "ARROW_3",
+ "ARROW_4",
+ "BR_ACID",
+ "BR_ELEC",
+ "BR_FIRE",
+ "BR_COLD",
+ "BR_POIS",
+ "BR_NETH",
+ "BR_LITE",
+ "BR_DARK",
+ "BR_CONF",
+ "BR_SOUN",
+ "BR_CHAO",
+ "BR_DISE",
+ "BR_NEXU",
+ "BR_TIME",
+ "BR_INER",
+ "BR_GRAV",
+ "BR_SHAR",
+ "BR_PLAS",
+ "BR_WALL",
+ "BR_MANA",
+ "BA_NUKE",
+ "BR_NUKE",
+ "BA_CHAO",
+ "BR_DISI",
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags5[] =
+{
+ "BA_ACID",
+ "BA_ELEC",
+ "BA_FIRE",
+ "BA_COLD",
+ "BA_POIS",
+ "BA_NETH",
+ "BA_WATE",
+ "BA_MANA",
+ "BA_DARK",
+ "DRAIN_MANA",
+ "MIND_BLAST",
+ "BRAIN_SMASH",
+ "CAUSE_1",
+ "CAUSE_2",
+ "CAUSE_3",
+ "CAUSE_4",
+ "BO_ACID",
+ "BO_ELEC",
+ "BO_FIRE",
+ "BO_COLD",
+ "BO_POIS",
+ "BO_NETH",
+ "BO_WATE",
+ "BO_MANA",
+ "BO_PLAS",
+ "BO_ICEE",
+ "MISSILE",
+ "SCARE",
+ "BLIND",
+ "CONF",
+ "SLOW",
+ "HOLD"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags6[] =
+{
+ "HASTE",
+ "HAND_DOOM",
+ "HEAL",
+ "S_ANIMALS",
+ "BLINK",
+ "TPORT",
+ "TELE_TO",
+ "TELE_AWAY",
+ "TELE_LEVEL",
+ "DARKNESS",
+ "TRAPS",
+ "FORGET",
+ "ANIM_DEAD", /* ToDo: Implement ANIM_DEAD */
+ "S_BUG",
+ "S_RNG",
+ "S_THUNDERLORD", /* DG : Summon Thunderlord */
+ "S_KIN",
+ "S_HI_DEMON",
+ "S_MONSTER",
+ "S_MONSTERS",
+ "S_ANT",
+ "S_SPIDER",
+ "S_HOUND",
+ "S_HYDRA",
+ "S_ANGEL",
+ "S_DEMON",
+ "S_UNDEAD",
+ "S_DRAGON",
+ "S_HI_UNDEAD",
+ "S_HI_DRAGON",
+ "S_WRAITH",
+ "S_UNIQUE"
+};
+
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags7[] =
+{
+ "AQUATIC",
+ "CAN_SWIM",
+ "CAN_FLY",
+ "FRIENDLY",
+ "PET",
+ "MORTAL",
+ "SPIDER",
+ "NAZGUL",
+ "DG_CURSE",
+ "POSSESSOR",
+ "NO_DEATH",
+ "NO_TARGET",
+ "AI_ANNOY",
+ "AI_SPECIAL",
+ "NEUTRAL",
+ "DROP_ART",
+ "DROP_RANDART",
+ "AI_PLAYER",
+ "NO_THEFT",
+ "SPIRIT",
+ "IM_MELEE",
+ "XXX7X21",
+ "XXX7X22",
+ "XXX7X23",
+ "XXX7X24",
+ "XXX7X25",
+ "XXX7X26",
+ "XXX7X27",
+ "XXX7X28",
+ "XXX7X29",
+ "XXX7X30",
+ "XXX7X31",
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags8[] =
+{
+ "WILD_ONLY",
+ "WILD_TOWN",
+ "XXX8X02",
+ "WILD_SHORE",
+ "WILD_OCEAN",
+ "WILD_WASTE",
+ "WILD_WOOD",
+ "WILD_VOLCANO",
+ "XXX8X08",
+ "WILD_MOUNTAIN",
+ "WILD_GRASS",
+ "NO_CUT",
+ "CTHANGBAND",
+ "XXX8X13",
+ "ZANGBAND",
+ "JOKEANGBAND",
+ "BASEANGBAND",
+ "XXX8X17",
+ "XXX8X18",
+ "XXX8X19",
+ "XXX8X20",
+ "XXX8X21",
+ "XXX8X22",
+ "XXX8X23",
+ "XXX8X24",
+ "XXX8X25",
+ "XXX8X26",
+ "XXX8X27",
+ "XXX8X28",
+ "XXX8X29",
+ "WILD_SWAMP", /* ToDo: Implement Swamp */
+ "WILD_TOO",
+};
+
+
+/*
+ * Monster race flags - Drops
+ */
+static cptr r_info_flags9[] =
+{
+ "DROP_CORPSE",
+ "DROP_SKELETON",
+ "HAS_LITE",
+ "MIMIC",
+ "HAS_EGG",
+ "IMPRESED",
+ "SUSCEP_ACID",
+ "SUSCEP_ELEC",
+ "SUSCEP_POIS",
+ "KILL_TREES",
+ "WYRM_PROTECT",
+ "DOPPLEGANGER",
+ "ONLY_DEPTH",
+ "SPECIAL_GENE",
+ "NEVER_GENE",
+ "XXX9X15",
+ "XXX9X16",
+ "XXX9X17",
+ "XXX9X18",
+ "XXX9X19",
+ "XXX9X20",
+ "XXX9X21",
+ "XXX9X22",
+ "XXX9X23",
+ "XXX9X24",
+ "XXX9X25",
+ "XXX9X26",
+ "XXX9X27",
+ "XXX9X28",
+ "XXX9X29",
+ "XXX9X30",
+ "XXX9X31",
+};
+
+
+/*
+ * Object flags
+ */
+cptr k_info_flags1[] =
+{
+ "STR",
+ "INT",
+ "WIS",
+ "DEX",
+ "CON",
+ "CHR",
+ "MANA",
+ "SPELL",
+ "STEALTH",
+ "SEARCH",
+ "INFRA",
+ "TUNNEL",
+ "SPEED",
+ "BLOWS",
+ "CHAOTIC",
+ "VAMPIRIC",
+ "SLAY_ANIMAL",
+ "SLAY_EVIL",
+ "SLAY_UNDEAD",
+ "SLAY_DEMON",
+ "SLAY_ORC",
+ "SLAY_TROLL",
+ "SLAY_GIANT",
+ "SLAY_DRAGON",
+ "KILL_DRAGON",
+ "VORPAL",
+ "IMPACT",
+ "BRAND_POIS",
+ "BRAND_ACID",
+ "BRAND_ELEC",
+ "BRAND_FIRE",
+ "BRAND_COLD"
+};
+
+/*
+ * Object flags
+ */
+cptr k_info_flags2[] =
+{
+ "SUST_STR",
+ "SUST_INT",
+ "SUST_WIS",
+ "SUST_DEX",
+ "SUST_CON",
+ "SUST_CHR",
+ "INVIS",
+ "LIFE",
+ "IM_ACID",
+ "IM_ELEC",
+ "IM_FIRE",
+ "IM_COLD",
+ "SENS_FIRE",
+ "REFLECT",
+ "FREE_ACT",
+ "HOLD_LIFE",
+ "RES_ACID",
+ "RES_ELEC",
+ "RES_FIRE",
+ "RES_COLD",
+ "RES_POIS",
+ "RES_FEAR",
+ "RES_LITE",
+ "RES_DARK",
+ "RES_BLIND",
+ "RES_CONF",
+ "RES_SOUND",
+ "RES_SHARDS",
+ "RES_NETHER",
+ "RES_NEXUS",
+ "RES_CHAOS",
+ "RES_DISEN"
+};
+
+/*
+ * Trap flags
+ */
+cptr k_info_flags2_trap[] =
+{
+ "AUTOMATIC_5",
+ "AUTOMATIC_99",
+ "KILL_GHOST",
+ "TELEPORT_TO",
+ "ONLY_DRAGON",
+ "ONLY_DEMON",
+ "XXX3",
+ "XXX3",
+ "ONLY_ANIMAL",
+ "ONLY_UNDEAD",
+ "ONLY_EVIL",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+};
+
+
+/*
+ * Object flags
+ */
+cptr k_info_flags3[] =
+{
+ "SH_FIRE",
+ "SH_ELEC",
+ "AUTO_CURSE",
+ "DECAY",
+ "NO_TELE",
+ "NO_MAGIC",
+ "WRAITH",
+ "TY_CURSE",
+ "EASY_KNOW",
+ "HIDE_TYPE",
+ "SHOW_MODS",
+ "INSTA_ART",
+ "FEATHER",
+ "LITE1",
+ "SEE_INVIS",
+ "NORM_ART",
+ "SLOW_DIGEST",
+ "REGEN",
+ "XTRA_MIGHT",
+ "XTRA_SHOTS",
+ "IGNORE_ACID",
+ "IGNORE_ELEC",
+ "IGNORE_FIRE",
+ "IGNORE_COLD",
+ "ACTIVATE",
+ "DRAIN_EXP",
+ "TELEPORT",
+ "AGGRAVATE",
+ "BLESSED",
+ "CURSED",
+ "HEAVY_CURSE",
+ "PERMA_CURSE"
+};
+
+/*
+ * Object flags
+ */
+cptr k_info_flags4[] =
+{
+ "NEVER_BLOW",
+ "PRECOGNITION",
+ "BLACK_BREATH",
+ "RECHARGE",
+ "FLY",
+ "DG_CURSE",
+ "COULD2H",
+ "MUST2H",
+ "LEVELS",
+ "CLONE",
+ "SPECIAL_GENE",
+ "CLIMB",
+ "FAST_CAST",
+ "CAPACITY",
+ "CHARGING",
+ "CHEAPNESS",
+ "FOUNTAIN",
+ "ANTIMAGIC_50",
+ "ANTIMAGIC_30",
+ "ANTIMAGIC_20",
+ "ANTIMAGIC_10",
+ "EASY_USE",
+ "IM_NETHER",
+ "RECHARGED",
+ "ULTIMATE",
+ "AUTO_ID",
+ "LITE2",
+ "LITE3",
+ "FUEL_LITE",
+ "ART_EXP",
+ "CURSE_NO_DROP",
+ "NO_RECHARGE"
+};
+
+/*
+ * Object flags
+ */
+cptr k_info_flags5[] =
+{
+ "TEMPORARY",
+ "DRAIN_MANA",
+ "DRAIN_HP",
+ "KILL_DEMON",
+ "KILL_UNDEAD",
+ "CRIT",
+ "ATTR_MULTI",
+ "WOUNDING",
+ "FULL_NAME",
+ "LUCK",
+ "IMMOVABLE",
+ "SPELL_CONTAIN",
+ "RES_MORGUL",
+ "ACTIVATE_NO_WIELD",
+ "MAGIC_BREATH",
+ "WATER_BREATH",
+ "WIELD_CAST",
+ "XXX8X17",
+ "XXX8X18",
+ "XXX8X19",
+ "XXX8X20",
+ "XXX8X21",
+ "XXX8X22",
+ "XXX8X23",
+ "XXX8X24",
+ "XXX8X25",
+ "XXX8X26",
+ "XXX8X27",
+ "XXX8X28",
+ "XXX8X29",
+ "XXX8X02",
+ "XXX8X22",
+};
+
+/*
+ * ESP flags
+ */
+cptr esp_flags[] =
+{
+ "ESP_ORC",
+ "ESP_TROLL",
+ "ESP_DRAGON",
+ "ESP_GIANT",
+ "ESP_DEMON",
+ "ESP_UNDEAD",
+ "ESP_EVIL",
+ "ESP_ANIMAL",
+ "ESP_THUNDERLORD",
+ "ESP_GOOD",
+ "ESP_NONLIVING",
+ "ESP_UNIQUE",
+ "ESP_SPIDER",
+ "XXX8X02",
+ "XXX8X02",
+ "XXX8X02",
+ "XXX8X02",
+ "XXX8X17",
+ "XXX8X18",
+ "XXX8X19",
+ "XXX8X20",
+ "XXX8X21",
+ "XXX8X22",
+ "XXX8X23",
+ "XXX8X24",
+ "XXX8X25",
+ "XXX8X26",
+ "XXX8X27",
+ "XXX8X28",
+ "XXX8X29",
+ "XXX8X02",
+ "ESP_ALL",
+};
+
+/* Specially handled properties for ego-items */
+
+static cptr ego_flags[] =
+{
+ "SUSTAIN",
+ "OLD_RESIST",
+ "ABILITY",
+ "R_ELEM",
+ "R_LOW",
+ "R_HIGH",
+ "R_ANY",
+ "R_DRAGON",
+ "SLAY_WEAP",
+ "DAM_DIE",
+ "DAM_SIZE",
+ "PVAL_M1",
+ "PVAL_M2",
+ "PVAL_M3",
+ "PVAL_M5",
+ "AC_M1",
+ "AC_M2",
+ "AC_M3",
+ "AC_M5",
+ "TH_M1",
+ "TH_M2",
+ "TH_M3",
+ "TH_M5",
+ "TD_M1",
+ "TD_M2",
+ "TD_M3",
+ "TD_M5",
+ "R_P_ABILITY",
+ "R_STAT",
+ "R_STAT_SUST",
+ "R_IMMUNITY",
+ "LIMIT_BLOWS"
+};
+
+/*
+ * Feature flags
+ */
+static cptr f_info_flags1[] =
+{
+ "NO_WALK",
+ "NO_VISION",
+ "CAN_LEVITATE",
+ "CAN_PASS",
+ "FLOOR",
+ "WALL",
+ "PERMANENT",
+ "CAN_FLY",
+ "REMEMBER",
+ "NOTICE",
+ "DONT_NOTICE_RUNNING",
+ "CAN_RUN",
+ "DOOR",
+ "SUPPORT_LIGHT",
+ "CAN_CLIMB",
+ "TUNNELABLE",
+ "WEB",
+ "ATTR_MULTI",
+ "SUPPORT_GROWTH",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Dungeon flags
+ */
+static cptr d_info_flags1[] =
+{
+ "PRINCIPAL",
+ "MAZE",
+ "SMALLEST",
+ "SMALL",
+ "BIG",
+ "NO_DOORS",
+ "WATER_RIVER",
+ "LAVA_RIVER",
+ "WATER_RIVERS",
+ "LAVA_RIVERS",
+ "CAVE",
+ "CAVERN",
+ "NO_UP",
+ "HOT",
+ "COLD",
+ "FORCE_DOWN",
+ "FORGET",
+ "NO_DESTROY",
+ "SAND_VEIN",
+ "CIRCULAR_ROOMS",
+ "EMPTY",
+ "DAMAGE_FEAT",
+ "FLAT",
+ "TOWER",
+ "RANDOM_TOWNS",
+ "DOUBLE",
+ "LIFE_LEVEL",
+ "EVOLVE",
+ "ADJUST_LEVEL_1",
+ "ADJUST_LEVEL_2",
+ "NO_RECALL",
+ "NO_STREAMERS"
+};
+
+static cptr d_info_flags2[] =
+{
+ "ADJUST_LEVEL_1_2",
+ "NO_SHAFT",
+ "ADJUST_LEVEL_PLAYER",
+ "NO_TELEPORT",
+ "ASK_LEAVE",
+ "NO_STAIR",
+ "SPECIAL",
+ "NO_NEW_MONSTER",
+ "DESC",
+ "NO_GENO",
+ "NO_BREATH",
+ "WATER_BREATH",
+ "ELVEN",
+ "DWARVEN",
+ "NO_EASY_MOVE",
+ "NO_RECALL_OUT",
+ "DESC_ALWAYS",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Trap flags
+ */
+static cptr t_info_flags[] =
+{
+ "CHEST",
+ "DOOR",
+ "FLOOR",
+ "XXX4",
+ "XXX5",
+ "XXX6",
+ "XXX7",
+ "XXX8",
+ "XXX9",
+ "XXX10",
+ "XXX11",
+ "XXX12",
+ "XXX13",
+ "XXX14",
+ "XXX15",
+ "XXX16",
+ "LEVEL1",
+ "LEVEL2",
+ "LEVEL3",
+ "LEVEL4",
+ "XXX21",
+ "XXX22",
+ "XXX23",
+ "XXX24",
+ "XXX25",
+ "XXX26",
+ "XXX27",
+ "XXX28",
+ "XXX29",
+ "XXX30",
+ "XXX31",
+ "XXX32"
+};
+
+/*
+ * Wilderness feature flags
+ */
+static cptr wf_info_flags1[] =
+{
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Stores flags
+ */
+static cptr st_info_flags1[] =
+{
+ "DEPEND_LEVEL",
+ "SHALLOW_LEVEL",
+ "MEDIUM_LEVEL",
+ "DEEP_LEVEL",
+ "RARE",
+ "VERY_RARE",
+ "COMMON",
+ "ALL_ITEM",
+ "RANDOM",
+ "FORCE_LEVEL",
+ "MUSEUM",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Race flags
+ */
+cptr rp_info_flags1[] =
+{
+ "EXPERIMENTAL",
+ "XXX",
+ "RESIST_BLACK_BREATH",
+ "NO_STUN",
+ "XTRA_MIGHT_BOW",
+ "XTRA_MIGHT_XBOW",
+ "XTRA_MIGHT_SLING",
+ "AC_LEVEL",
+ "HURT_LITE",
+ "VAMPIRE",
+ "UNDEAD",
+ "NO_CUT",
+ "CORRUPT",
+ "NO_FOOD",
+ "NO_GOD",
+ "XXX",
+ "ELF",
+ "SEMI_WRAITH",
+ "NO_SUBRACE_CHANGE",
+ "XXX",
+ "XXX",
+ "MOLD_FRIEND",
+ "GOD_FRIEND",
+ "XXX",
+ "INNATE_SPELLS",
+ "XXX",
+ "XXX",
+ "EASE_STEAL",
+ "XXX",
+ "XXX",
+ "XXX",
+ "XXX"
+};
+
+/*
+ * Race flags
+ */
+cptr rp_info_flags2[] =
+{
+ "XXX",
+ "ASTRAL",
+ "XXX",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/* Skill flags */
+static cptr s_info_flags1[] =
+{
+ "HIDDEN",
+ "AUTO_HIDE",
+ "RANDOM_GAIN",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Dungeon effect types (used in E:damage:frequency:type entry in d_info.txt)
+ */
+static struct
+{
+cptr name;
+int feat;
+}
+d_info_dtypes[] =
+{
+ {"ELEC", GF_ELEC},
+ {"POISON", GF_POIS},
+ {"ACID", GF_ACID},
+ {"COLD", GF_COLD},
+ {"FIRE", GF_FIRE},
+ {"MISSILE", GF_MISSILE},
+ {"ARROW", GF_ARROW},
+ {"PLASMA", GF_PLASMA},
+ {"WATER", GF_WATER},
+ {"LITE", GF_LITE},
+ {"DARK", GF_DARK},
+ {"LITE_WEAK", GF_LITE_WEAK},
+ {"LITE_DARK", GF_DARK_WEAK},
+ {"SHARDS", GF_SHARDS},
+ {"SOUND", GF_SOUND},
+ {"CONFUSION", GF_CONFUSION},
+ {"FORCE", GF_FORCE},
+ {"INERTIA", GF_INERTIA},
+ {"MANA", GF_MANA},
+ {"METEOR", GF_METEOR},
+ {"ICE", GF_ICE},
+ {"CHAOS", GF_CHAOS},
+ {"NETHER", GF_NETHER},
+ {"DISENCHANT", GF_DISENCHANT},
+ {"NEXUS", GF_NEXUS},
+ {"TIME", GF_TIME},
+ {"GRAVITY", GF_GRAVITY},
+ {"ROCKET", GF_ROCKET},
+ {"NUKE", GF_NUKE},
+ {"HOLY_FIRE", GF_HOLY_FIRE},
+ {"HELL_FIRE", GF_HELL_FIRE},
+ {"DISINTEGRATE", GF_DISINTEGRATE},
+ {"DESTRUCTION", GF_DESTRUCTION},
+ {"RAISE", GF_RAISE},
+ {NULL, 0}
+};
+
+/* Essence names for al_info.txt */
+static const char *essence_names[] =
+{
+ "No name here", /* can't be matched, sscanf stops at spaces */
+ "POISON",
+ "EXPLOSION",
+ "TELEPORT",
+ "COLD",
+ "FIRE",
+ "ACID",
+ "LIFE",
+ "CONFUSION",
+ "LITE",
+ "CHAOS",
+ "TIME",
+ "MAGIC",
+ "EXTRALIFE",
+ "DARKNESS",
+ "KNOWLEDGE",
+ "FORCE",
+ "LIGHTNING",
+ "MANA",
+ ""
+};
+static const char *activation_names[] =
+{
+ "NO_ACTIVATION", /* 0*/
+ "SUNLIGHT", /* 1*/
+ "BO_MISS_1", /* 2*/
+ "BA_POIS_1", /* 3*/
+ "BO_ELEC_1", /* 4*/
+ "BO_ACID_1", /* 5*/
+ "BO_COLD_1", /* 6*/
+ "BO_FIRE_1", /* 7*/
+ "BA_COLD_1", /* 8*/
+ "BA_FIRE_1", /* 9*/
+ "DRAIN_1", /* 10*/
+ "BA_COLD_2", /* 11*/
+ "BA_ELEC_2", /* 12*/
+ "DRAIN_2", /* 13*/
+ "VAMPIRE_1", /* 14*/
+ "BO_MISS_2", /* 15*/
+ "BA_FIRE_2", /* 16*/
+ "BA_COLD_3", /* 17*/
+ "BA_ELEC_3", /* 18*/
+ "WHIRLWIND", /* 19*/
+ "VAMPIRE_2", /* 20*/
+ "CALL_CHAOS", /* 21*/
+ "ROCKET", /* 22*/
+ "DISP_EVIL", /* 23*/
+ "BA_MISS_3", /* 24*/
+ "DISP_GOOD", /* 25*/
+ "GILGALAD", /* 26*/
+ "CELEBRIMBOR", /* 27*/
+ "SKULLCLEAVER", /* 28*/
+ "HARADRIM", /* 29*/
+ "FUNDIN", /* 30*/
+ "EOL", /* 31*/
+ "UMBAR", /* 32*/
+ "NUMENOR", /* 33*/
+ "KNOWLEDGE", /* 34*/
+ "UNDEATH", /* 35*/
+ "THRAIN", /* 36*/
+ "BARAHIR", /* 37*/
+ "TULKAS", /* 38*/
+ "NARYA", /* 39*/
+ "NENYA", /* 40*/
+ "VILYA", /* 41*/
+ "POWER", /* 42*/
+ "STONE_LORE", /* 43*/
+ "RAZORBACK", /* 44*/
+ "BLADETURNER", /* 45*/
+ "MEDIATOR", /* 46*/
+ "BELEGENNON", /* 47*/
+ "GORLIM", /* 48*/
+ "COLLUIN", /* 49*/
+ "BELANGIL", /* 50*/
+ "CONFUSE", /* 51*/
+ "SLEEP", /* 52*/
+ "QUAKE", /* 53*/
+ "TERROR", /* 54*/
+ "TELE_AWAY", /* 55*/
+ "BANISH_EVIL", /* 56*/
+ "GENOCIDE", /* 57*/
+ "MASS_GENO", /* 58*/
+ "ANGUIREL", /* 59*/
+ "ERU", /* 60*/
+ "DAWN", /* 61*/
+ "FIRESTAR", /* 62*/
+ "TURMIL", /* 63*/
+ "CUBRAGOL", /* 64*/
+ "CHARM_ANIMAL", /* 65*/
+ "CHARM_UNDEAD", /* 66*/
+ "CHARM_OTHER", /* 67*/
+ "CHARM_ANIMALS", /* 68*/
+ "CHARM_OTHERS", /* 69*/
+ "SUMMON_ANIMAL", /* 70*/
+ "SUMMON_PHANTOM", /* 71*/
+ "SUMMON_ELEMENTAL", /* 72*/
+ "SUMMON_DEMON", /* 73*/
+ "SUMMON_UNDEAD", /* 74*/
+ "ELESSAR", /* 75*/
+ "GANDALF", /* 76*/
+ "MARDA", /* 77*/
+ "PALANTIR", /* 78*/
+ "XXX79",
+ "XXX80",
+ "CURE_LW", /* 81*/
+ "CURE_MW", /* 82*/
+ "CURE_POISON", /* 83*/
+ "REST_LIFE", /* 84*/
+ "REST_ALL", /* 85*/
+ "CURE_700", /* 86*/
+ "CURE_1000", /* 87*/
+ "XXX88",
+ "EREBOR", /* 89*/
+ "DRUEDAIN", /* 90*/
+ "ESP", /* 91*/
+ "BERSERK", /* 92*/
+ "PROT_EVIL", /* 93*/
+ "RESIST_ALL", /* 94*/
+ "SPEED", /* 95*/
+ "XTRA_SPEED", /* 96*/
+ "WRAITH", /* 97*/
+ "INVULN", /* 98*/
+ "ROHAN", /* 99*/
+ "HELM", /* 100*/
+ "BOROMIR", /* 101*/
+ "HURIN", /* 102*/
+ "AXE_GOTHMOG", /* 103*/
+ "MELKOR", /* 104*/
+ "GROND", /* 105*/
+ "NATUREBANE", /* 106*/
+ "NIGHT", /* 107*/
+ "ORCHAST", /* 108*/
+ "XXX109",
+ "XXX110",
+ "LIGHT", /* 111*/
+ "MAP_LIGHT", /* 112*/
+ "DETECT_ALL", /* 113*/
+ "DETECT_XTRA", /* 114*/
+ "ID_FULL", /* 115*/
+ "ID_PLAIN", /* 116*/
+ "RUNE_EXPLO", /* 117*/
+ "RUNE_PROT", /* 118*/
+ "SATIATE", /* 119*/
+ "DEST_DOOR", /* 120*/
+ "STONE_MUD", /* 121*/
+ "RECHARGE", /* 122*/
+ "ALCHEMY", /* 123*/
+ "DIM_DOOR", /* 124*/
+ "TELEPORT", /* 125*/
+ "RECALL", /* 126*/
+ "DEATH", /* 127*/
+ "RUINATION", /* 128*/
+ "DESTRUC", /* 129*/
+ "UNINT", /* 130*/
+ "UNSTR", /* 131*/
+ "UNCON", /* 132*/
+ "UNCHR", /* 133*/
+ "UNDEX", /* 134*/
+ "UNWIS", /* 135*/
+ "STATLOSS", /* 136*/
+ "HISTATLOSS", /* 137*/
+ "EXPLOSS", /* 138*/
+ "HIEXPLOSS", /* 139*/
+ "SUMMON_MONST", /* 140*/
+ "PARALYZE", /* 141*/
+ "HALLU", /* 142*/
+ "POISON", /* 143*/
+ "HUNGER", /* 144*/
+ "STUN", /* 145*/
+ "CUTS", /* 146*/
+ "PARANO", /* 147*/
+ "CONFUSION", /* 148*/
+ "BLIND", /* 149*/
+ "PET_SUMMON", /* 150*/
+ "CURE_PARA", /* 151*/
+ "CURE_HALLU", /* 152*/
+ "CURE_POIS", /* 153*/
+ "CURE_HUNGER", /* 154*/
+ "CURE_STUN", /* 155*/
+ "CURE_CUTS", /* 156*/
+ "CURE_FEAR", /* 157*/
+ "CURE_CONF", /* 158*/
+ "CURE_BLIND", /* 159*/
+ "CURING", /* 160*/
+ "DARKNESS", /* 161*/
+ "LEV_TELE", /* 162*/
+ "ACQUIREMENT", /* 163*/
+ "WEIRD", /* 164*/
+ "AGGRAVATE", /* 165*/
+ "MUT", /* 166*/
+ "CURE_INSANITY", /* 167*/
+ "CURE_MUT", /* 168*/
+ "LIGHT_ABSORBTION", /* 169*/
+ "BA_FIRE_H", /* 170*/
+ "BA_COLD_H", /* 171*/
+ "BA_ELEC_H", /* 172*/
+ "BA_ACID_H", /* 173*/
+ "SPIN", /* 174*/
+ "NOLDOR", /* 175*/
+ "SPECTRAL", /* 176*/
+ "JUMP", /* 177*/
+ "DEST_TELE", /* 178*/
+ "BA_POIS_4", /* 179*/
+ "BA_COLD_4", /* 180*/
+ "BA_FIRE_4", /* 181*/
+ "BA_ACID_4", /* 182*/
+ "BA_ELEC_4", /* 183*/
+ "BR_ELEC", /* 184*/
+ "BR_COLD", /* 185*/
+ "BR_FIRE", /* 186*/
+ "BR_ACID", /* 187*/
+ "BR_POIS", /* 188*/
+ "BR_MANY", /* 189*/
+ "BR_CONF", /* 190*/
+ "BR_SOUND", /* 191*/
+ "BR_CHAOS", /* 192*/
+ "BR_SHARD", /* 193*/
+ "BR_BALANCE", /* 194*/
+ "BR_LIGHT", /* 195*/
+ "BR_POWER", /* 196*/
+ "GROW_MOLD", /* 197*/
+ "XXX198",
+ "XXX199",
+ "MUSIC", /* 200*/
+ ""
+};
+
+/*
+ * Convert a "color letter" into an "actual" color
+ * The colors are: dwsorgbuDWvyRGBU, as shown below
+ */
+int color_char_to_attr(char c)
+{
+ switch (c)
+ {
+ case 'd':
+ return (TERM_DARK);
+ case 'w':
+ return (TERM_WHITE);
+ case 's':
+ return (TERM_SLATE);
+ case 'o':
+ return (TERM_ORANGE);
+ case 'r':
+ return (TERM_RED);
+ case 'g':
+ return (TERM_GREEN);
+ case 'b':
+ return (TERM_BLUE);
+ case 'u':
+ return (TERM_UMBER);
+
+ case 'D':
+ return (TERM_L_DARK);
+ case 'W':
+ return (TERM_L_WHITE);
+ case 'v':
+ return (TERM_VIOLET);
+ case 'y':
+ return (TERM_YELLOW);
+ case 'R':
+ return (TERM_L_RED);
+ case 'G':
+ return (TERM_L_GREEN);
+ case 'B':
+ return (TERM_L_BLUE);
+ case 'U':
+ return (TERM_L_UMBER);
+ }
+
+ return ( -1);
+}
+
+/*
+ * Attr value-to-char convertion table
+ */
+byte conv_color[16] =
+{
+ 'd',
+ 'w',
+ 's',
+ 'o',
+ 'r',
+ 'g',
+ 'b',
+ 'u',
+ 'D',
+ 'W',
+ 'v',
+ 'y',
+ 'R',
+ 'G',
+ 'B',
+ 'U',
+};
+
+
+/* Values in re_info can be fixed, added, substracted or percented */
+static byte monster_ego_modify(char c)
+{
+ switch (c)
+ {
+ case '+':
+ return MEGO_ADD;
+ case '-':
+ return MEGO_SUB;
+ case '=':
+ return MEGO_FIX;
+ case '%':
+ return MEGO_PRC;
+ default:
+ {
+ msg_format("Unknown mego value modifier %c.", c);
+ return MEGO_ADD;
+ }
+ }
+}
+
+/*
+ * Implements fp stacks, for included files
+ */
+static FILE *fp_stack[10];
+static int fp_stack_idx = 0;
+
+/*
+ * Must be caleld before the main loop
+ */
+static void fp_stack_init(FILE *fp)
+{
+ fp_stack[0] = fp;
+ fp_stack_idx = 0;
+}
+
+static void fp_stack_push(cptr name)
+{
+ if (fp_stack_idx < 9)
+ {
+ char buf[1024];
+ FILE *fp;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit(format("Cannot open '%s' file.", name));
+
+ printf("ibncluding %s\n", name);
+
+ fp_stack[++fp_stack_idx] = fp;
+ }
+}
+
+static bool_ fp_stack_pop()
+{
+ if (fp_stack_idx > 0)
+ {
+ FILE *fp = fp_stack[fp_stack_idx--];
+ my_fclose(fp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/*
+ * Must be used instead of my_fgets for teh main loop
+ */
+static int my_fgets_dostack(char *buf, int len)
+{
+ // End of a file
+ if (0 != my_fgets(fp_stack[fp_stack_idx], buf, len))
+ {
+ // If any left, use them
+ if (fp_stack_pop())
+ return my_fgets_dostack(buf, len);
+ // If not, this is the end
+ else
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/*** Initialize from ascii template files ***/
+
+/*
+ * Grab one race flag from a textual string
+ */
+static bool_ unknown_shut_up = FALSE;
+static errr grab_one_class_flag(u32b *choice, cptr what)
+{
+ int i;
+ cptr s;
+
+ /* Scan classes flags */
+ for (i = 0; i < max_c_idx && (s = class_info[i].title + c_name); i++)
+ {
+ if (streq(what, s))
+ {
+ (choice[i / 32]) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ if (!unknown_shut_up) msg_format("Unknown class flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+static errr grab_one_race_allow_flag(u32b *choice, cptr what)
+{
+ int i;
+ cptr s;
+
+ /* Scan classes flags */
+ for (i = 0; i < max_rp_idx && (s = race_info[i].title + rp_name); i++)
+ {
+ if (streq(what, s))
+ {
+ (choice[i / 32]) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ if (!unknown_shut_up) msg_format("(1)Unknown race flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Grab one flag from a textual string
+ */
+static errr grab_one_skill_flag(u32b *f1, cptr what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, s_info_flags1[i]))
+ {
+ (*f1) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("(2)Unknown skill flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+/*
+ * Grab one flag from a textual string
+ */
+static errr grab_one_player_race_flag(u32b *f1, u32b *f2, cptr what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, rp_info_flags1[i]))
+ {
+ (*f1) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, rp_info_flags2[i]))
+ {
+ (*f2) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("(2)Unknown race flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+/* Get an activation number (good for artifacts, recipes, egos, and object kinds) */
+int get_activation(char *activation)
+{
+ int i;
+ for ( i = 0 ; activation_names[i][0] ; i++)
+ if (!strncmp(activation_names[i], activation, 19))
+ {
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * Grab one flag in an object_kind from a textual string
+ */
+static errr grab_one_race_kind_flag(u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp, cptr what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ (*f1) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ (*f2) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps*/
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ (*f3) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ (*f3) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ (*f4) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ (*f5) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ (*esp) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown object flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+/*
+ * Initialize the "player" arrays, by parsing an ascii "template" file
+ */
+errr init_player_info_txt(FILE *fp, char *buf)
+{
+ int i = 0, z;
+ int powers = 0;
+ int lev = 1;
+ int tit_idx = 0;
+ int spec_idx = 0;
+ int cur_ab = -1;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ player_race *rp_ptr = NULL;
+ player_race_mod *rmp_ptr = NULL;
+ player_class *c_ptr = NULL;
+ player_spec *s_ptr = NULL;
+ meta_class_type *mc_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ rp_head->name_size = 0;
+ rp_head->text_size = 0;
+ rmp_head->name_size = 0;
+ rmp_head->text_size = 0;
+ c_head->name_size = 0;
+ c_head->text_size = 0;
+
+ /* Init general skills */
+ for (z = 0; z < MAX_SKILLS; z++)
+ {
+ gen_skill_basem[z] = 0;
+ gen_skill_base[z] = 0;
+ gen_skill_modm[z] = 0;
+ gen_skill_mod[z] = 0;
+ }
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Reinit error_idx */
+ if (buf[0] == 'I')
+ {
+ error_idx = -1;
+ continue;
+ }
+
+ /* Process 'H' for "History" */
+ if (buf[0] == 'H')
+ {
+ int idx;
+ char *zz[6];
+
+ /* Scan for the values */
+ if (tokenize(buf + 2, 6, zz, ':', ':') != 6) return (1);
+
+ idx = atoi(zz[0]);
+ bg[idx].roll = atoi(zz[1]);
+ bg[idx].chart = atoi(zz[2]);
+ bg[idx].next = atoi(zz[3]);
+ bg[idx].bonus = atoi(zz[4]);
+
+ bg[idx].info = ++rp_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(rp_text + rp_head->text_size, zz[5]);
+
+ /* Advance the index */
+ rp_head->text_size += strlen(zz[5]);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G:k' for "General skills" */
+ if ((buf[0] == 'G') && (buf[2] == 'k'))
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ gen_skill_basem[i] = monster_ego_modify(v);
+ gen_skill_base[i] = val;
+ gen_skill_modm[i] = monster_ego_modify(m);
+ gen_skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if ((buf[0] == 'R') && (buf[2] == 'N'))
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 4, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 4);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= rp_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ rp_ptr = &race_info[i];
+
+ /* Hack -- Verify space */
+ if (rp_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!rp_ptr->title) rp_ptr->title = ++rp_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(rp_name + rp_head->name_size, s);
+
+ /* Advance the index */
+ rp_head->name_size += strlen(s);
+
+ rp_ptr->powers[0] = rp_ptr->powers[1] = rp_ptr->powers[2] = rp_ptr->powers[3] = -1;
+ powers = 0;
+ lev = 1;
+ cur_ab = 0;
+ for (z = 0; z < 10; z++)
+ rp_ptr->abilities[z].level = -1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if ((buf[0] == 'R') && (buf[2] == 'D'))
+ {
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Hack -- Verify space */
+ if (rp_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!rp_ptr->desc)
+ {
+ rp_ptr->desc = ++rp_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(rp_text + rp_head->text_size, s);
+
+ /* Advance the index */
+ rp_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(rp_text + rp_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ rp_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "body parts" */
+ if ((buf[0] == 'R') && (buf[2] == 'E'))
+ {
+ int s[BODY_MAX], z;
+
+ /* Scan for the values */
+ if (BODY_MAX != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5])) return (1);
+
+ for (z = 0; z < BODY_MAX; z++)
+ rp_ptr->body_parts[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "flag level" */
+ if ((buf[0] == 'R') && (buf[2] == 'R'))
+ {
+ int s[2];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 4, "%d:%d",
+ &s[0], &s[1])) return (1);
+
+ lev = s[0];
+ rp_ptr->opval[lev] = s[1];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Stats" */
+ if ((buf[0] == 'R') && (buf[2] == 'S'))
+ {
+ int s[7], z;
+
+ /* Scan for the values */
+ if (7 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6])) return (1);
+
+ rp_ptr->luck = s[6];
+ for (z = 0; z < 6; z++)
+ rp_ptr->r_adj[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "powers" */
+ if ((buf[0] == 'R') && (buf[2] == 'Z'))
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ rp_ptr->powers[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'K' for "sKills" */
+ if ((buf[0] == 'R') && (buf[2] == 'K'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ rp_ptr->r_dis = s[0];
+ rp_ptr->r_dev = s[1];
+ rp_ptr->r_sav = s[2];
+ rp_ptr->r_stl = s[3];
+ rp_ptr->r_srh = s[4];
+ rp_ptr->r_fos = s[5];
+ rp_ptr->r_thn = s[6];
+ rp_ptr->r_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "skills" */
+ if ((buf[0] == 'R') && (buf[2] == 'k'))
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ rp_ptr->skill_basem[i] = monster_ego_modify(v);
+ rp_ptr->skill_base[i] = val;
+ rp_ptr->skill_modm[i] = monster_ego_modify(m);
+ rp_ptr->skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'b' for "abilities" */
+ if ((buf[0] == 'R') && (buf[2] == 'b'))
+ {
+ char *sec;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 4, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ if ((i = find_ability(sec)) == -1) return (1);
+
+ rp_ptr->abilities[cur_ab].ability = i;
+ rp_ptr->abilities[cur_ab].level = atoi(buf + 4);
+ cur_ab++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'M' for "Mods" */
+ if ((buf[0] == 'R') && (buf[2] == 'M'))
+ {
+ int s[10];
+
+ /* Scan for the values */
+ if (10 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7], &s[8], &s[9])) return (1);
+
+ rp_ptr->b_age = s[0];
+ rp_ptr->m_age = s[1];
+ rp_ptr->m_b_ht = s[2];
+ rp_ptr->m_m_ht = s[3];
+ rp_ptr->m_b_wt = s[4];
+ rp_ptr->m_m_wt = s[5];
+ rp_ptr->f_b_ht = s[6];
+ rp_ptr->f_m_ht = s[7];
+ rp_ptr->f_b_wt = s[8];
+ rp_ptr->f_m_wt = s[9];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'P' for "xtra" */
+ if ((buf[0] == 'R') && (buf[2] == 'P'))
+ {
+ int s[4];
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 4, "%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3])) return (1);
+
+ rp_ptr->r_mhp = s[0];
+ rp_ptr->r_exp = s[1];
+ rp_ptr->infra = s[2];
+ rp_ptr->chart = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Player flags" (multiple lines) */
+ if ((buf[0] == 'R') && (buf[2] == 'G'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_player_race_flag(&rp_ptr->flags1, &rp_ptr->flags2, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "level Flags" (multiple lines) */
+ if ((buf[0] == 'R') && (buf[2] == 'F'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_kind_flag(&rp_ptr->oflags1[lev], &rp_ptr->oflags2[lev], &rp_ptr->oflags3[lev], &rp_ptr->oflags4[lev], &rp_ptr->oflags5[lev], &rp_ptr->oesp[lev], s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object birth" */
+ if ((buf[0] == 'R') && (buf[2] == 'O'))
+ {
+ int s[5];
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%d:%d:%d:%dd%d",
+ &s[0], &s[1], &s[4], &s[2], &s[3]))
+ {
+ s[4] = 0;
+
+ if (4 != sscanf(buf + 4, "%d:%d:%dd%d",
+ &s[0], &s[1], &s[2], &s[3]))
+ {
+ return (1);
+ }
+ }
+
+ rp_ptr->obj_pval[rp_ptr->obj_num] = s[4];
+ rp_ptr->obj_tval[rp_ptr->obj_num] = s[0];
+ rp_ptr->obj_sval[rp_ptr->obj_num] = s[1];
+ rp_ptr->obj_dd[rp_ptr->obj_num] = s[2];
+ rp_ptr->obj_ds[rp_ptr->obj_num++] = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "Class choice flags" (multiple lines) */
+ if ((buf[0] == 'R') && (buf[2] == 'C'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_class_flag(rp_ptr->choice, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if ((buf[0] == 'S') && (buf[2] == 'N'))
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 4, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 4);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= rmp_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ rmp_ptr = &race_mod_info[i];
+
+ /* Hack -- Verify space */
+ if (rmp_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!rmp_ptr->title) rmp_ptr->title = ++rmp_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(rmp_name + rmp_head->name_size, s);
+
+ /* Advance the index */
+ rmp_head->name_size += strlen(s);
+
+ rmp_ptr->powers[0] = rmp_ptr->powers[1] = rmp_ptr->powers[2] = rmp_ptr->powers[3] = -1;
+ powers = 0;
+ lev = 1;
+ cur_ab = 0;
+ for (z = 0; z < 10; z++)
+ rmp_ptr->abilities[z].level = -1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if ((buf[0] == 'S') && (buf[2] == 'D'))
+ {
+ /* Acquire the text */
+ s = buf + 6;
+
+ if (buf[4] == 'A') rmp_ptr->place = TRUE;
+ else rmp_ptr->place = FALSE;
+
+ /* Hack -- Verify space */
+ if (rmp_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!rmp_ptr->desc)
+ {
+ rmp_ptr->desc = ++rmp_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(rmp_text + rmp_head->text_size, s);
+
+ /* Advance the index */
+ rmp_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(rmp_text + rmp_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ rmp_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "body parts" */
+ if ((buf[0] == 'S') && (buf[2] == 'E'))
+ {
+ int s[BODY_MAX], z;
+
+ /* Scan for the values */
+ if (BODY_MAX != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5])) return (1);
+
+ for (z = 0; z < BODY_MAX; z++)
+ rmp_ptr->body_parts[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "flag level" */
+ if ((buf[0] == 'S') && (buf[2] == 'R'))
+ {
+ int s[2];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 4, "%d:%d",
+ &s[0], &s[1])) return (1);
+
+ lev = s[0];
+ rmp_ptr->opval[lev] = s[1];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Stats" */
+ if ((buf[0] == 'S') && (buf[2] == 'S'))
+ {
+ int s[8], z;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ rmp_ptr->mana = s[7];
+ rmp_ptr->luck = s[6];
+ for (z = 0; z < 6; z++)
+ rmp_ptr->r_adj[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "powers" */
+ if ((buf[0] == 'S') && (buf[2] == 'Z'))
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ rmp_ptr->powers[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "skills" */
+ if ((buf[0] == 'S') && (buf[2] == 'k'))
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ rmp_ptr->skill_basem[i] = monster_ego_modify(v);
+ rmp_ptr->skill_base[i] = val;
+ rmp_ptr->skill_modm[i] = monster_ego_modify(m);
+ rmp_ptr->skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'b' for "abilities" */
+ if ((buf[0] == 'S') && (buf[2] == 'b'))
+ {
+ char *sec;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 4, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ if ((i = find_ability(sec)) == -1) return (1);
+
+ rmp_ptr->abilities[cur_ab].ability = i;
+ rmp_ptr->abilities[cur_ab].level = atoi(buf + 4);
+ cur_ab++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'K' for "sKills" */
+ if ((buf[0] == 'S') && (buf[2] == 'K'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ rmp_ptr->r_dis = s[0];
+ rmp_ptr->r_dev = s[1];
+ rmp_ptr->r_sav = s[2];
+ rmp_ptr->r_stl = s[3];
+ rmp_ptr->r_srh = s[4];
+ rmp_ptr->r_fos = s[5];
+ rmp_ptr->r_thn = s[6];
+ rmp_ptr->r_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'M' for "Mods" */
+ if ((buf[0] == 'S') && (buf[2] == 'M'))
+ {
+ int s[10];
+
+ /* Scan for the values */
+ if (10 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7], &s[8], &s[9])) return (1);
+
+ rmp_ptr->b_age = s[0];
+ rmp_ptr->m_age = s[1];
+ rmp_ptr->m_b_ht = s[2];
+ rmp_ptr->m_m_ht = s[3];
+ rmp_ptr->m_b_wt = s[4];
+ rmp_ptr->m_m_wt = s[5];
+ rmp_ptr->f_b_ht = s[6];
+ rmp_ptr->f_m_ht = s[7];
+ rmp_ptr->f_b_wt = s[8];
+ rmp_ptr->f_m_wt = s[9];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'P' for "xtra" */
+ if ((buf[0] == 'S') && (buf[2] == 'P'))
+ {
+ int s[3];
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 4, "%d:%d:%d",
+ &s[0], &s[1], &s[2])) return (1);
+
+ rmp_ptr->r_mhp = s[0];
+ rmp_ptr->r_exp = s[1];
+ rmp_ptr->infra = s[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Player flags" (multiple lines) */
+ if ((buf[0] == 'S') && (buf[2] == 'G'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_player_race_flag(&rmp_ptr->flags1, &rmp_ptr->flags2, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "level Flags" (multiple lines) */
+ if ((buf[0] == 'S') && (buf[2] == 'F'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_kind_flag(&rmp_ptr->oflags1[lev], &rmp_ptr->oflags2[lev], &rmp_ptr->oflags3[lev], &rmp_ptr->oflags4[lev], &rmp_ptr->oflags5[lev], &rmp_ptr->oesp[lev], s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object birth" */
+ if ((buf[0] == 'S') && (buf[2] == 'O'))
+ {
+ int s[5];
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%d:%d:%d:%dd%d",
+ &s[0], &s[1], &s[4], &s[2], &s[3]))
+ {
+ s[4] = 0;
+
+ if (4 != sscanf(buf + 4, "%d:%d:%dd%d",
+ &s[0], &s[1], &s[2], &s[3]))
+ {
+ return (1);
+ }
+ }
+
+ rmp_ptr->obj_pval[rmp_ptr->obj_num] = s[4];
+ rmp_ptr->obj_tval[rmp_ptr->obj_num] = s[0];
+ rmp_ptr->obj_sval[rmp_ptr->obj_num] = s[1];
+ rmp_ptr->obj_dd[rmp_ptr->obj_num] = s[2];
+ rmp_ptr->obj_ds[rmp_ptr->obj_num++] = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Allowed races" (multiple lines) */
+ if ((buf[0] == 'S') && (buf[2] == 'A'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_allow_flag(rmp_ptr->choice, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "Class choice flags" (multiple lines) */
+ if ((buf[0] == 'S') && (buf[2] == 'C'))
+ {
+ u32b choice[2] = {0, 0}, z;
+
+ /* Parse every entry */
+ for (s = buf + 6; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_class_flag(choice, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ for (z = 0; z < 2; z++)
+ {
+ if (buf[4] == 'A') rmp_ptr->pclass[z] |= choice[z];
+ else rmp_ptr->mclass[z] |= choice[z];
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if ((buf[0] == 'C') && (buf[2] == 'N'))
+ {
+ int z;
+
+ /* Find the colon before the name */
+ s = strchr(buf + 4, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 4);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= c_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ c_ptr = &class_info[i];
+
+ /* Hack -- Verify space */
+ if (c_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!c_ptr->title) c_ptr->title = ++c_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(c_name + c_head->name_size, s);
+
+ /* Advance the index */
+ c_head->name_size += strlen(s);
+
+ c_ptr->powers[0] = c_ptr->powers[1] = c_ptr->powers[2] = c_ptr->powers[3] = -1;
+ powers = 0;
+ lev = 1;
+ for (z = 0; z < 10; z++)
+ c_ptr->abilities[z].level = -1;
+ cur_ab = 0;
+ c_ptr->obj_num = 0;
+ tit_idx = 0;
+ spec_idx = -1;
+ for (z = 0; z < MAX_SPEC; z++)
+ c_ptr->spec[z].title = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if ((buf[0] == 'C') && (buf[2] == 'D'))
+ {
+ /* Acquire the text */
+ s = buf + 6;
+
+ /* Hack -- Verify space */
+ if (c_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ switch (buf[4])
+ {
+ case '0':
+ /* Advance and Save the text index */
+ if (!c_ptr->desc)
+ {
+ c_ptr->desc = ++c_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, s);
+
+ /* Advance the index */
+ c_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ c_head->text_size += strlen(s) + 1;
+ }
+ break;
+ case '1':
+ /* Advance and Save the text index */
+ c_ptr->titles[tit_idx++] = ++c_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, s);
+
+ /* Advance the index */
+ c_head->text_size += strlen(s);
+ break;
+ default:
+ return (6);
+ break;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object birth" */
+ if ((buf[0] == 'C') && (buf[2] == 'O'))
+ {
+ int s[5];
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%d:%d:%d:%dd%d",
+ &s[0], &s[1], &s[4], &s[2], &s[3]))
+ {
+ s[4] = 0;
+
+ if (4 != sscanf(buf + 4, "%d:%d:%dd%d",
+ &s[0], &s[1], &s[2], &s[3]))
+ {
+ return (1);
+ }
+ }
+
+ c_ptr->obj_pval[c_ptr->obj_num] = s[4];
+ c_ptr->obj_tval[c_ptr->obj_num] = s[0];
+ c_ptr->obj_sval[c_ptr->obj_num] = s[1];
+ c_ptr->obj_dd[c_ptr->obj_num] = s[2];
+ c_ptr->obj_ds[c_ptr->obj_num++] = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "body parts" */
+ if ((buf[0] == 'C') && (buf[2] == 'E'))
+ {
+ int s[BODY_MAX], z;
+
+ /* Scan for the values */
+ if (BODY_MAX != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5])) return (1);
+
+ for (z = 0; z < BODY_MAX; z++)
+ c_ptr->body_parts[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "flag level" */
+ if ((buf[0] == 'C') && (buf[2] == 'R'))
+ {
+ int s[2];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 4, "%d:%d",
+ &s[0], &s[1])) return (1);
+
+ lev = s[0];
+ c_ptr->opval[lev] = s[1];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "Stats" */
+ if ((buf[0] == 'C') && (buf[2] == 'S'))
+ {
+ int s[8], z;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ c_ptr->mana = s[6];
+ c_ptr->extra_blows = s[7];
+ for (z = 0; z < 6; z++)
+ c_ptr->c_adj[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "skills" */
+ if ((buf[0] == 'C') && (buf[2] == 'k'))
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ c_ptr->skill_basem[i] = monster_ego_modify(v);
+ c_ptr->skill_base[i] = val;
+ c_ptr->skill_modm[i] = monster_ego_modify(m);
+ c_ptr->skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'b' for "abilities" */
+ if ((buf[0] == 'C') && (buf[2] == 'b'))
+ {
+ char *sec;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 4, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ if ((i = find_ability(sec)) == -1) return (1);
+
+ c_ptr->abilities[cur_ab].ability = i;
+ c_ptr->abilities[cur_ab].level = atoi(buf + 4);
+ cur_ab++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'g' for "gods" */
+ if ((buf[0] == 'C') && (buf[2] == 'g'))
+ {
+ int i;
+
+ if (streq(buf + 4, "All Gods"))
+ c_ptr->gods = 0xFFFFFFFF;
+ else
+ {
+ if ((i = find_god(buf + 4)) == -1) return (1);
+ c_ptr->gods |= BIT(i);
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "powers" */
+ if ((buf[0] == 'C') && (buf[2] == 'Z'))
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ c_ptr->powers[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'K' for "sKills" */
+ if ((buf[0] == 'C') && (buf[2] == 'K'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ c_ptr->c_dis = s[0];
+ c_ptr->c_dev = s[1];
+ c_ptr->c_sav = s[2];
+ c_ptr->c_stl = s[3];
+ c_ptr->c_srh = s[4];
+ c_ptr->c_fos = s[5];
+ c_ptr->c_thn = s[6];
+ c_ptr->c_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'x' for "Xtra skills" */
+ if ((buf[0] == 'C') && (buf[2] == 'X'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ c_ptr->x_dis = s[0];
+ c_ptr->x_dev = s[1];
+ c_ptr->x_sav = s[2];
+ c_ptr->x_stl = s[3];
+ c_ptr->x_srh = s[4];
+ c_ptr->x_fos = s[5];
+ c_ptr->x_thn = s[6];
+ c_ptr->x_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'P' for "xtra" */
+ if ((buf[0] == 'C') && (buf[2] == 'P'))
+ {
+ int s[2];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 4, "%d:%d",
+ &s[0], &s[1])) return (1);
+
+ c_ptr->c_mhp = s[0];
+ c_ptr->c_exp = s[1];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "sensing" */
+ if ((buf[0] == 'C') && (buf[2] == 'C'))
+ {
+ long int s[3];
+ char h, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c:%c:%ld:%ld:%ld",
+ &h, &m, &s[0], &s[1], &s[2])) return (1);
+
+ c_ptr->sense_heavy = (h == 'H') ? TRUE : FALSE;
+ c_ptr->sense_heavy_magic = (m == 'H') ? TRUE : FALSE;
+ c_ptr->sense_base = s[0];
+ c_ptr->sense_pl = s[1];
+ c_ptr->sense_plus = s[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'B' for "blows" */
+ if ((buf[0] == 'C') && (buf[2] == 'B'))
+ {
+ int s[3];
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 4, "%d:%d:%d",
+ &s[0], &s[1], &s[2])) return (1);
+
+ c_ptr->blow_num = s[0];
+ c_ptr->blow_wgt = s[1];
+ c_ptr->blow_mul = s[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Player flags" (multiple lines) */
+ if ((buf[0] == 'C') && (buf[2] == 'G'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_player_race_flag(&c_ptr->flags1, &c_ptr->flags2, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "level Flags" (multiple lines) */
+ if ((buf[0] == 'C') && (buf[2] == 'F'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_kind_flag(&c_ptr->oflags1[lev], &c_ptr->oflags2[lev], &c_ptr->oflags3[lev], &c_ptr->oflags4[lev], &c_ptr->oflags5[lev], &c_ptr->oesp[lev], s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Specialities */
+ if ((buf[0] == 'C') && (buf[2] == 'a'))
+ {
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[4] == 'N')
+ {
+ /* Find the colon before the name */
+ s = buf + 6;
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+ /* Get the index */
+ spec_idx++;
+
+ /* Verify information */
+ if (spec_idx >= MAX_SPEC) return (2);
+
+ /* Point at the "info" */
+ s_ptr = &c_ptr->spec[spec_idx];
+
+ /* Hack -- Verify space */
+ if (c_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!s_ptr->title) s_ptr->title = ++c_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(c_name + c_head->name_size, s);
+
+ /* Advance the index */
+ c_head->name_size += strlen(s);
+
+ s_ptr->obj_num = 0;
+ cur_ab = 0;
+ for (z = 0; z < 10; z++)
+ s_ptr->abilities[z].level = -1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if (buf[4] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 6;
+
+ /* Hack -- Verify space */
+ if (c_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!s_ptr->desc)
+ {
+ s_ptr->desc = ++c_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, s);
+
+ /* Advance the index */
+ c_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ c_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object birth" */
+ if (buf[4] == 'O')
+ {
+ int s[5];
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 6, "%d:%d:%d:%dd%d",
+ &s[0], &s[1], &s[4], &s[2], &s[3]))
+ {
+ s[4] = 0;
+
+ if (4 != sscanf(buf + 6, "%d:%d:%dd%d",
+ &s[0], &s[1], &s[2], &s[3]))
+ {
+ return (1);
+ }
+ }
+
+ s_ptr->obj_pval[s_ptr->obj_num] = s[4];
+ s_ptr->obj_tval[s_ptr->obj_num] = s[0];
+ s_ptr->obj_sval[s_ptr->obj_num] = s[1];
+ s_ptr->obj_dd[s_ptr->obj_num] = s[2];
+ s_ptr->obj_ds[s_ptr->obj_num++] = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'g' for "gods" */
+ if (buf[4] == 'g')
+ {
+ int i;
+
+ if (streq(buf + 6, "All Gods"))
+ s_ptr->gods = 0xFFFFFFFF;
+ else
+ {
+ if ((i = find_god(buf + 6)) == -1) return (1);
+ s_ptr->gods |= BIT(i);
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "skills" */
+ if (buf[4] == 'k')
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 6, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ s_ptr->skill_basem[i] = monster_ego_modify(v);
+ s_ptr->skill_base[i] = val;
+ s_ptr->skill_modm[i] = monster_ego_modify(m);
+ s_ptr->skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'b' for "abilities" */
+ if (buf[4] == 'b')
+ {
+ char *sec;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 6, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ if ((i = find_ability(sec)) == -1) return (1);
+
+ s_ptr->abilities[cur_ab].ability = i;
+ s_ptr->abilities[cur_ab].level = atoi(buf + 6);
+ cur_ab++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Player flags" (multiple lines) */
+ if (buf[4] == 'G')
+ {
+ /* Parse every entry */
+ for (s = buf + 6; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_player_race_flag(&s_ptr->flags1, &s_ptr->flags2, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'K' for "desired skills" */
+ if (buf[4] == 'K')
+ {
+ long val;
+ char name[200];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 6, "%ld:%s",
+ &val, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ s_ptr->skill_ideal[i] = val;
+
+ /* Next... */
+ continue;
+ }
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if ((buf[0] == 'M') && (buf[2] == 'N'))
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 4, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 4);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= max_mc_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ mc_ptr = &meta_class_info[i];
+
+ /* Append chars to the name */
+ strcpy(mc_ptr->name, s + 2);
+ mc_ptr->color = color_char_to_attr(s[0]);
+ for (powers = 0; powers < max_c_idx; powers++)
+ mc_ptr->classes[powers] = -1;
+ powers = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "Classes" */
+ if ((buf[0] == 'M') && (buf[2] == 'C'))
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Find it in the list */
+ for (i = 0; i < max_c_idx; i++)
+ {
+ if (!stricmp(s, class_info[i].title + c_name)) break;
+ }
+
+ if (i == max_c_idx) return (6);
+
+ mc_ptr->classes[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+ /* Complete the "name" and "text" sizes */
+ ++rp_head->name_size;
+ ++rp_head->text_size;
+ ++rmp_head->name_size;
+ ++rmp_head->text_size;
+ ++c_head->name_size;
+ ++c_head->text_size;
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize the "v_info" array, by parsing an ascii "template" file
+ */
+errr init_v_info_txt(FILE *fp, char *buf, bool_ start)
+{
+ int i;
+ char *s;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ vault_type *v_ptr = NULL;
+
+ if (start)
+ {
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Prepare the "fake" stuff */
+ v_head->name_size = 0;
+ v_head->text_size = 0;
+ }
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+ if ((buf[0] == 'Q') || (buf[0] == 'T')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf, "V:%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i <= error_idx) return (4);
+
+ /* Verify information */
+ if (i >= v_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ v_ptr = &v_info[i];
+
+ /* Hack -- Verify space */
+ if (v_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!v_ptr->name) v_ptr->name = ++v_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(v_name + v_head->name_size, s);
+
+ /* Advance the index */
+ v_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current v_ptr */
+ if (!v_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (v_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!v_ptr->text) v_ptr->text = ++v_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(v_text + v_head->text_size, s);
+
+ /* Advance the index */
+ v_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'X' for "Extra info" (one line only) */
+ if (buf[0] == 'X')
+ {
+ int typ, rat, hgt, wid;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &typ, &rat, &hgt, &wid)) return (1);
+
+ /* Save the values */
+ v_ptr->typ = typ;
+ v_ptr->rat = rat;
+ v_ptr->hgt = hgt;
+ v_ptr->wid = wid;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current v_ptr */
+ if (!v_ptr) return (3);
+
+ /* Process monster, item and level info for special levels */
+ if (buf[0] == 'Y')
+ {
+
+ int mon1, mon2, mon3, mon4, mon5, mon6, mon7, mon8, mon9;
+ int mon10, item1, item2, item3, lvl, dun_type;
+
+ /* Scan for the values */
+ if (15 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &mon1, &mon2, &mon3, &mon4, &mon5, &mon6, &mon7, &mon8, &mon9, &mon10, &item1, &item2, &item3, &lvl, &dun_type)) return (1);
+
+ /* Save the values */
+ v_ptr->mon[0] = mon1;
+ v_ptr->mon[1] = mon2;
+ v_ptr->mon[2] = mon3;
+ v_ptr->mon[3] = mon4;
+ v_ptr->mon[4] = mon5;
+ v_ptr->mon[5] = mon6;
+ v_ptr->mon[6] = mon7;
+ v_ptr->mon[7] = mon8;
+ v_ptr->mon[8] = mon9;
+ v_ptr->mon[9] = mon10;
+ v_ptr->item[0] = item1;
+ v_ptr->item[1] = item2;
+ v_ptr->item[2] = item3;
+ v_ptr->lvl = lvl;
+ v_ptr->dun_type = dun_type;
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ if (!start)
+ {
+ ++v_head->name_size;
+ ++v_head->text_size;
+ }
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one flag in an feature_type from a textual string
+ */
+static errr grab_one_feature_flag(feature_type *f_ptr, cptr what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, f_info_flags1[i]))
+ {
+ f_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown object flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+/*
+ * Initialize the "f_info" array, by parsing an ascii "template" file
+ */
+errr init_f_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+ u32b default_desc = 0, default_tunnel = 0, default_block = 0;
+
+ /* Current entry */
+ feature_type *f_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Prepare the "fake" stuff */
+ f_head->name_size = 0;
+ f_head->text_size = 0;
+
+ /* Add some fake descs */
+ default_desc = ++f_head->text_size;
+ strcpy(f_text + f_head->text_size, "a wall blocking your way");
+ f_head->text_size += strlen("a wall blocking your way");
+
+ default_tunnel = ++f_head->text_size;
+ strcpy(f_text + f_head->text_size, "You cannot tunnel through that.");
+ f_head->text_size += strlen("You cannot tunnel through that.");
+
+ default_block = ++f_head->text_size;
+ strcpy(f_text + f_head->text_size, "a wall blocking your way");
+ f_head->text_size += strlen("a wall blocking your way");
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i <= error_idx) return (4);
+
+ /* Verify information */
+ if (i >= f_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ f_ptr = &f_info[i];
+
+ /* Hack -- Verify space */
+ if (f_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!f_ptr->name) f_ptr->name = ++f_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(f_name + f_head->name_size, s);
+
+ /* Advance the index */
+ f_head->name_size += strlen(s);
+
+ /* Default "mimic" */
+ f_ptr->mimic = i;
+ f_ptr->text = default_desc;
+ f_ptr->block = default_desc;
+ f_ptr->tunnel = default_tunnel;
+ f_ptr->block = default_block;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current f_ptr */
+ if (!f_ptr) return (3);
+
+
+ /* Process 'D' for "Descriptions" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Hack -- Verify space */
+ if (f_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ switch (buf[2])
+ {
+ case '0':
+ /* Advance and Save the text index */
+ f_ptr->text = ++f_head->text_size;
+ break;
+ case '1':
+ /* Advance and Save the text index */
+ f_ptr->tunnel = ++f_head->text_size;
+ break;
+ case '2':
+ /* Advance and Save the text index */
+ f_ptr->block = ++f_head->text_size;
+ break;
+ default:
+ return (6);
+ break;
+ }
+
+ /* Append chars to the name */
+ strcpy(f_text + f_head->text_size, s);
+
+ /* Advance the index */
+ f_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'M' for "Mimic" (one line only) */
+ if (buf[0] == 'M')
+ {
+ int mimic;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d",
+ &mimic)) return (1);
+
+ /* Save the values */
+ f_ptr->mimic = mimic;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Shimmer" (one line only) */
+ if (buf[0] == 'S')
+ {
+ char s0, s1, s2, s3, s4, s5, s6;
+
+ /* Scan for the values */
+ if (7 != sscanf(buf + 2, "%c:%c:%c:%c:%c:%c:%c",
+ &s0, &s1, &s2, &s3, &s4, &s5, &s6)) return (1);
+
+ /* Save the values */
+ f_ptr->shimmer[0] = color_char_to_attr(s0);
+ f_ptr->shimmer[1] = color_char_to_attr(s1);
+ f_ptr->shimmer[2] = color_char_to_attr(s2);
+ f_ptr->shimmer[3] = color_char_to_attr(s3);
+ f_ptr->shimmer[4] = color_char_to_attr(s4);
+ f_ptr->shimmer[5] = color_char_to_attr(s5);
+ f_ptr->shimmer[6] = color_char_to_attr(s6);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'G' for "Graphics" (one line only) */
+ if (buf[0] == 'G')
+ {
+ int tmp;
+
+ /* Paranoia */
+ if (!buf[2]) return (1);
+ if (!buf[3]) return (1);
+ if (!buf[4]) return (1);
+
+ /* Extract the color */
+ tmp = color_char_to_attr(buf[4]);
+
+ /* Paranoia */
+ if (tmp < 0) return (1);
+
+ /* Save the values */
+ f_ptr->d_attr = tmp;
+ f_ptr->d_char = buf[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Effects" (up to four lines) -SC- */
+ if (buf[0] == 'E')
+ {
+ int side, dice, freq, type;
+ cptr tmp;
+
+ /* Find the next empty blow slot (if any) */
+ for (i = 0; i < 4; i++) if ((!f_ptr->d_side[i]) &&
+ (!f_ptr->d_dice[i])) break;
+
+ /* Oops, no more slots */
+ if (i == 4) return (1);
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%dd%d:%d:%d",
+ &dice, &side, &freq, &type))
+ {
+ int j;
+
+ if (3 != sscanf(buf + 2, "%dd%d:%d",
+ &dice, &side, &freq)) return (1);
+
+ tmp = buf + 2;
+ for (j = 0; j < 2; j++)
+ {
+ tmp = strchr(tmp, ':');
+ if (tmp == NULL) return (1);
+ tmp++;
+ }
+
+ j = 0;
+
+ while (d_info_dtypes[j].name != NULL)
+ if (strcmp(d_info_dtypes[j].name, tmp) == 0)
+ {
+ f_ptr->d_type[i] = d_info_dtypes[j].feat;
+ break;
+ }
+ else j++;
+
+ if (d_info_dtypes[j].name == NULL) return (1);
+ }
+ else
+ f_ptr->d_type[i] = type;
+
+ freq *= 10;
+ /* Save the values */
+ f_ptr->d_side[i] = side;
+ f_ptr->d_dice[i] = dice;
+ f_ptr->d_frequency[i] = freq;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_feature_flag(f_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++f_head->name_size;
+ ++f_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one flag in an object_kind from a textual string
+ */
+static errr grab_one_kind_flag(object_kind *k_ptr, cptr what, bool_ obvious)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ if (obvious)
+ k_ptr->oflags1 |= (1L << i);
+ else
+ k_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ if (obvious)
+ k_ptr->oflags2 |= (1L << i);
+ else
+ k_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps*/
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ if (obvious)
+ k_ptr->oflags2 |= (1L << i);
+ else
+ k_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ if (obvious)
+ k_ptr->oflags3 |= (1L << i);
+ else
+ k_ptr->flags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ if (obvious)
+ k_ptr->oflags4 |= (1L << i);
+ else
+ k_ptr->flags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ if (obvious)
+ k_ptr->oflags5 |= (1L << i);
+ else
+ k_ptr->flags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ if (obvious)
+ k_ptr->oesp |= (1L << i);
+ else
+ k_ptr->esp |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown object flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+/*
+ * Initialize the "k_info" array, by parsing an ascii "template" file
+ */
+errr init_k_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ object_kind *k_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Prepare the "fake" stuff */
+ k_head->name_size = 0;
+ k_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i <= error_idx) return (4);
+
+ /* Verify information */
+ if (i >= k_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ k_ptr = &k_info[i];
+
+ /* Hack -- Verify space */
+ if (k_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!k_ptr->name) k_ptr->name = ++k_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(k_name + k_head->name_size, s);
+
+ /* Advance the index */
+ k_head->name_size += strlen(s);
+
+ /* Needed hack */
+ k_ptr->esp = 0;
+ k_ptr->power = -1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current k_ptr */
+ if (!k_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (k_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!k_ptr->text) k_ptr->text = ++k_head->text_size;
+
+ /* Append a space if needed */
+ else if (k_text[k_head->text_size - 1] != ' ')
+ {
+ /* Append chars to the name */
+ strcpy(k_text + k_head->text_size, " ");
+
+ /* Advance the index */
+ k_head->text_size += 1;
+ }
+
+ /* Append chars to the name */
+ strcpy(k_text + k_head->text_size, s);
+
+ /* Advance the index */
+ k_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Graphics" (one line only) */
+ if (buf[0] == 'G')
+ {
+ char sym;
+ int tmp;
+
+ /* Paranoia */
+ if (!buf[2]) return (1);
+ if (!buf[3]) return (1);
+ if (!buf[4]) return (1);
+
+ /* Extract the char */
+ sym = buf[2];
+
+ /* Extract the attr */
+ tmp = color_char_to_attr(buf[4]);
+
+ /* Paranoia */
+ if (tmp < 0) return (1);
+
+ /* Save the values */
+ k_ptr->d_attr = tmp;
+ k_ptr->d_char = sym;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int tval, sval, pval, pval2 = 0;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &tval, &sval, &pval, &pval2))
+ {
+ char spl[70];
+
+ if (4 != sscanf(buf + 2, "%d:%d:%d:SPELL=%s",
+ &tval, &sval, &pval, spl))
+ {
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tval, &sval, &pval))
+ return (1);
+ }
+ else
+ {
+ char *spl = strchr(buf + 2, '=') + 1;
+
+ pval2 = find_spell(spl);
+ }
+ }
+
+ /* Save the values */
+ k_ptr->tval = tval;
+ k_ptr->sval = sval;
+ k_ptr->pval = pval;
+ k_ptr->pval2 = pval2;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int level, extra, wgt;
+ long cost;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld",
+ &level, &extra, &wgt, &cost)) return (1);
+
+ /* Save the values */
+ k_ptr->level = level;
+ k_ptr->extra = extra;
+ k_ptr->weight = wgt;
+ k_ptr->cost = cost;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'T' for "arTifact Info" (one line only) */
+ if (buf[0] == 'T')
+ {
+ int btval, bsval;
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 2, "%d:%d",
+ &btval, &bsval)) return (1);
+
+ /* Save the values */
+ k_ptr->btval = btval;
+ k_ptr->bsval = bsval;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "Granted power" */
+ if (buf[0] == 'Z')
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ k_ptr->power = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'a' for Activation */
+ if ( buf[0] == 'a')
+ {
+ if (prefix(buf + 2, "HARDCORE="))
+ {
+ k_ptr->activate = get_activation(buf + 11);
+ if (k_ptr->activate == -1)
+ return 1;
+ }
+ else if (prefix(buf + 2, "SPELL="))
+ {
+ k_ptr->activate = -find_spell(buf + 8);
+ if (k_ptr->activate == -( -1))
+ return 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Allocation" (one line only) */
+ if (buf[0] == 'A')
+ {
+ int i;
+
+ /* XXX XXX XXX Simply read each number following a colon */
+ for (i = 0, s = buf + 1; s && (s[0] == ':') && s[1]; ++i)
+ {
+ /* Default chance */
+ k_ptr->chance[i] = 1;
+
+ /* Store the level */
+ k_ptr->locale[i] = atoi(s + 1);
+
+ /* Find the slash */
+ t = strchr(s + 1, '/');
+
+ /* Find the next colon */
+ s = strchr(s + 1, ':');
+
+ /* If the slash is "nearby", use it */
+ if (t && (!s || t < s))
+ {
+ int chance = atoi(t + 1);
+ if (chance > 0) k_ptr->chance[i] = chance;
+ }
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'P' for "power" and such */
+ if (buf[0] == 'P')
+ {
+ int ac, hd1, hd2, th, td, ta;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d",
+ &ac, &hd1, &hd2, &th, &td, &ta)) return (1);
+
+ k_ptr->ac = ac;
+ k_ptr->dd = hd1;
+ k_ptr->ds = hd2;
+ k_ptr->to_h = th;
+ k_ptr->to_d = td;
+ k_ptr->to_a = ta;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_kind_flag(k_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'f' for obvious flags */
+ if (buf[0] == 'f')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_kind_flag(k_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++k_head->name_size;
+ ++k_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*Get a kind flag, return flag*32+bit number or -1 for unknown*/
+
+int get_k_flag(char *what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ return i;
+ if (streq(what, k_info_flags2[i]))
+ return 1*32 + i;
+ if (streq(what, k_info_flags2_trap[i]))
+ return 1*32 + i;
+ if (streq(what, k_info_flags3[i]))
+ return 2*32 + i;
+ if (streq(what, k_info_flags4[i]))
+ return 3*32 + i;
+ if (streq(what, k_info_flags5[i]))
+ return 4*32 + i;
+ if (streq(what, esp_flags[i]))
+ return 5*32 + i;
+ }
+
+ /* Oops */
+ msg_format("Unknown object flag '%s'.", what);
+
+ /* Error */
+ return ( -1);
+
+}
+
+int get_r_flag(char *what)
+{
+ int i;
+
+ /* Check flags */
+ /* this processes all r_info_flag arrays in parallel.
+ Seemed like a good idea at the time..
+ */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ return i;
+ if (streq(what, r_info_flags2[i]))
+ return 1*32 + i;
+ if (streq(what, r_info_flags3[i]))
+ return 2*32 + i;
+ if (streq(what, r_info_flags4[i]))
+ return 3*32 + i;
+ if (streq(what, r_info_flags5[i]))
+ return 4*32 + i;
+ if (streq(what, r_info_flags6[i]))
+ return 5*32 + i;
+ if (streq(what, r_info_flags7[i]))
+ return 6*32 + i;
+ if (streq(what, r_info_flags8[i]))
+ return 7*32 + i;
+ if (streq(what, r_info_flags9[i]))
+ return 8*32 + i;
+ }
+
+ /* Oops */
+ msg_format("Unknown race flag '%s'.", what);
+
+ /* Error */
+ return ( -1);
+}
+int init_al_info_essence(char *essence)
+{
+ int i;
+ for ( i = 0 ; essence_names[i][0] ; i++)
+ if (!strncmp(essence_names[i], essence, 9))
+ {
+ return i;
+ }
+ return -1;
+}
+/*
+ * Initialize the "al_info" array, by parsing an ascii "template" file
+ */
+errr init_al_info_txt(FILE *fp, char *buf)
+{
+ int al_idx = 0, a_idx = 0;
+ char *s, *t;
+ struct artifact_select_flag *a_ptr = NULL;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Fun! */
+ al_head->name_size = 0;
+ *al_name = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int tval, sval, qty;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tval, &sval, &qty))
+ {
+ return (1);
+ }
+
+ /* ignore everything after the first space. */
+ s = strchr(buf, ' ');
+ if (s != NULL)
+ *s = 0;
+
+ /* Save the values */
+ alchemist_recipes[al_idx].tval = tval;
+ alchemist_recipes[al_idx].sval = sval;
+ alchemist_recipes[al_idx].qty = qty;
+ alchemist_recipes[al_idx].sval_essence = init_al_info_essence(strrchr(buf, ':') + 1);
+ if (alchemist_recipes[al_idx].sval_essence < 0)
+ return 5;
+
+ al_idx++;
+ if (al_idx >= max_al_idx)
+ return 7;
+ /* Next... */
+ continue;
+ }
+ if (buf[0] == 'a')
+ {
+ int qty;
+ if ( 1 != sscanf(buf + 2, "%d", &qty))
+ {
+ return (1);
+ }
+ s = strrchr(buf, ':');
+ *(s++) = 0;
+ t = strchr(s, ' ');
+ *(t++) = 0;
+ alchemist_recipes[al_idx].tval = 0;
+ alchemist_recipes[al_idx].sval = get_k_flag(s);
+ alchemist_recipes[al_idx].qty = qty;
+ alchemist_recipes[al_idx].sval_essence = init_al_info_essence(t);
+ if (alchemist_recipes[al_idx].sval_essence < 0)
+ return 1;
+
+ al_idx++;
+ if (al_idx >= max_al_idx)
+ return 7; /* 7 is an 'out of memory' error */
+
+ continue;
+ }
+ if (buf[0] == 'A')
+ {
+ int group, level, pval, rtval, rsval, rpval;
+ long xp;
+ /*Verify that complete description information is
+ Recorded for previous Artifact flag
+ */
+ if (a_ptr
+ && (!a_ptr->group || !a_ptr->desc || !a_ptr->item_desc != !a_ptr->rtval)
+ )
+ return (1);
+
+ a_ptr = &a_select_flags[a_idx++];
+
+ if ( 7 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%ld",
+ &group, &rtval, &rsval, &rpval, &pval, &level, &xp))
+ return (1);
+ a_ptr->group = group;
+ a_ptr->rtval = rtval;
+ a_ptr->rsval = rsval;
+ a_ptr->rpval = rpval;
+ a_ptr->pval = pval;
+ a_ptr->level = level;
+ a_ptr->xp = xp;
+ continue;
+ }
+
+ /*Anything else here MUST be a artifact flag line*/
+ if ( !a_ptr)
+ return (3);
+
+ if (buf[0] == 'F')
+ {
+ /* Get the Item flag associated with this */
+ a_ptr->flag = get_k_flag(buf + 2);
+ if (a_ptr->flag == -1)
+ return (1);
+ continue;
+ }
+ if (buf[0] == 'x')
+ {
+ /* Get the activation name associated with this */
+ a_ptr->flag = -get_activation(buf + 2);
+ if (a_ptr->flag == 1)
+ return (1);
+ a_ptr->group = 88;
+ a_ptr->pval = 0;
+ continue;
+ }
+ /* Get the race flags associated with this */
+ if (buf[0] == 'f')
+ {
+ char *s, *t;
+ int idx = 0;
+
+ if ( a_ptr->rflag[0] )
+ {
+ msg_print("duplicate f: entries for one corpse");
+ return (5);
+ }
+
+ if ( a_ptr->rtval != TV_CORPSE )
+ {
+ msg_print("f: section for corpse flags only");
+ return (5);
+ }
+ if ( a_ptr->rpval )
+ {
+ msg_print("Can't specify r_info.txt index with f: section");
+ return (5);
+ }
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ if ( idx > 5 )
+ {
+ msg_print("Limit on race flags is currently 6");
+ return (5);
+ }
+
+ /* Parse this entry */
+ a_ptr->rflag[idx] = get_r_flag(s);
+ if (a_ptr->rflag[idx++] == -1)
+ return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'p' for "Plural Description" */
+ /* Only valid for flag which depend on pval */
+ if (buf[0] == 'p')
+ {
+ /* Reject if doesn't depend on pval */
+
+ if (!a_ptr->pval)
+ return (1);
+
+ /* Acquire the description */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (al_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ a_ptr->item_descp = ++al_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(al_name + al_head->name_size, s);
+
+ /* Advance the index */
+ al_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the description */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (al_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ a_ptr->desc = ++al_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(al_name + al_head->name_size, s);
+
+ /* Advance the index */
+ al_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'd' for "Item Description" */
+ if (buf[0] == 'd')
+ {
+ /* Acquire the name */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (al_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ if (a_ptr->item_desc)
+ return (7);
+
+ /* Advance and Save the name index */
+ a_ptr->item_desc = ++al_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(al_name + al_head->name_size, s);
+
+ /* Advance the index */
+ al_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Hack - set the al_head->text_size to byte size of array */
+ al_head->text_size = (a_idx + 1) * sizeof(artifact_select_flag);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one flag in an artifact_type from a textual string
+ */
+static errr grab_one_artifact_flag(artifact_type *a_ptr, cptr what, bool_ obvious)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ if (obvious)
+ a_ptr->oflags1 |= (1L << i);
+ else
+ a_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ if (obvious)
+ a_ptr->oflags2 |= (1L << i);
+ else
+ a_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps*/
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ if (obvious)
+ a_ptr->oflags2 |= (1L << i);
+ else
+ a_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ if (obvious)
+ a_ptr->oflags3 |= (1L << i);
+ else
+ a_ptr->flags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ if (obvious)
+ a_ptr->oflags4 |= (1L << i);
+ else
+ a_ptr->flags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ if (obvious)
+ a_ptr->oflags5 |= (1L << i);
+ else
+ a_ptr->flags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ if (obvious)
+ a_ptr->oesp |= (1L << i);
+ else
+ a_ptr->esp |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown artifact flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+
+
+/*
+ * Initialize the "a_info" array, by parsing an ascii "template" file
+ */
+errr init_a_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ artifact_type *a_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= a_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ a_ptr = &a_info[i];
+
+ /* Hack -- Verify space */
+ if (a_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!a_ptr->name) a_ptr->name = ++a_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(a_name + a_head->name_size, s);
+
+ /* Advance the index */
+ a_head->name_size += strlen(s);
+
+ /* Ignore everything */
+ a_ptr->flags3 |= (TR3_IGNORE_ACID);
+ a_ptr->flags3 |= (TR3_IGNORE_ELEC);
+ a_ptr->flags3 |= (TR3_IGNORE_FIRE);
+ a_ptr->flags3 |= (TR3_IGNORE_COLD);
+
+ /* Needed hack */
+ a_ptr->esp = 0;
+ a_ptr->power = -1;
+
+ /*Require activating artifacts to have a activation type */
+ if (a_ptr && a_ptr->flags3 & TR3_ACTIVATE && !a_ptr->activate)
+ {
+ msg_print("Activate flag without activate type");
+ return 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current a_ptr */
+ if (!a_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (a_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!a_ptr->text) a_ptr->text = ++a_head->text_size;
+
+ /* Append a space at the end of the line, if needed */
+ else if (a_text[a_head->text_size - 1] != ' ')
+ {
+ /* Append the space */
+ strcpy(a_text + a_head->text_size, " ");
+
+ /* Advance the index */
+ a_head->text_size += 1;
+ }
+
+ /* Append chars to the name */
+ strcpy(a_text + a_head->text_size, s);
+
+ /* Advance the index */
+ a_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int tval, sval, pval;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tval, &sval, &pval))
+ {
+ return (1);
+ }
+
+ /* Save the values */
+ a_ptr->tval = tval;
+ a_ptr->sval = sval;
+ a_ptr->pval = pval;
+
+ /* Verify */
+ if (!lookup_kind(tval, sval)) return (6);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int level, rarity, wgt;
+ long cost;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld",
+ &level, &rarity, &wgt, &cost)) return (1);
+
+ /* Save the values */
+ a_ptr->level = level;
+ a_ptr->rarity = rarity;
+ a_ptr->weight = wgt;
+ a_ptr->cost = cost;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'P' for "power" and such */
+ if (buf[0] == 'P')
+ {
+ int ac, hd1, hd2, th, td, ta;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d",
+ &ac, &hd1, &hd2, &th, &td, &ta)) return (1);
+
+ a_ptr->ac = ac;
+ a_ptr->dd = hd1;
+ a_ptr->ds = hd2;
+ a_ptr->to_h = th;
+ a_ptr->to_d = td;
+ a_ptr->to_a = ta;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "Granted power" */
+ if (buf[0] == 'Z')
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ a_ptr->power = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_artifact_flag(a_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'f' for obvious flags */
+ if (buf[0] == 'f')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_artifact_flag(a_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Read activation type. */
+ if (buf[0] == 'a')
+ {
+ if (prefix(buf + 2, "HARDCORE="))
+ {
+ a_ptr->activate = get_activation(buf + 11);
+ if (a_ptr->activate == -1)
+ return 1;
+ }
+ else if (prefix(buf + 2, "SPELL="))
+ {
+ a_ptr->activate = -find_spell(buf + 8);
+ if (a_ptr->activate == -( -1))
+ return 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++a_head->name_size;
+ ++a_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+* Initialize the "set_info" array, by parsing an ascii "template" file
+*/
+errr init_set_info_txt(FILE *fp, char *buf)
+{
+ int i;
+ int cur_art = 0, cur_num = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ set_type *set_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ int z, y;
+
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= set_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ set_ptr = &set_info[i];
+
+ /* Hack -- Verify space */
+ if (set_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!set_ptr->name) set_ptr->name = ++set_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(set_name + set_head->name_size, s);
+
+ /* Advance the index */
+ set_head->name_size += strlen(s);
+
+ /* Needed hack */
+ set_ptr->num = 0;
+ set_ptr->num_use = 0;
+ for (z = 0; z < 6; z++)
+ {
+ set_ptr->arts[z].a_idx = 0;
+ set_ptr->arts[z].present = FALSE;
+ for (y = 0; y < 6; y++)
+ {
+ set_ptr->arts[z].flags1[y] = 0;
+ set_ptr->arts[z].flags2[y] = 0;
+ set_ptr->arts[z].flags3[y] = 0;
+ set_ptr->arts[z].flags4[y] = 0;
+ set_ptr->arts[z].flags5[y] = 0;
+ set_ptr->arts[z].esp[y] = 0;
+ set_ptr->arts[z].pval[y] = 0;
+ }
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current set_ptr */
+ if (!set_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (set_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!set_ptr->desc) set_ptr->desc = ++set_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(set_text + set_head->text_size, s);
+
+ /* Advance the index */
+ set_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'P' for "Power" (up to 6) */
+ if (buf[0] == 'P')
+ {
+ int a_idx, num, pval;
+ int z;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &a_idx, &num, &pval))
+ {
+ return (1);
+ }
+
+ for (z = 0; z < set_ptr->num; z++)
+ if (set_ptr->arts[z].a_idx == a_idx) break;
+ if (z == set_ptr->num)
+ {
+ set_ptr->num++;
+ set_ptr->arts[z].a_idx = a_idx;
+ }
+
+ /* Save the values */
+ set_ptr->arts[z].pval[num - 1] = pval;
+ cur_art = z;
+ cur_num = num - 1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_kind_flag(&set_ptr->arts[cur_art].flags1[cur_num],
+ &set_ptr->arts[cur_art].flags2[cur_num],
+ &set_ptr->arts[cur_art].flags3[cur_num],
+ &set_ptr->arts[cur_art].flags4[cur_num],
+ &set_ptr->arts[cur_art].flags5[cur_num],
+ &set_ptr->arts[cur_art].esp[cur_num],
+ s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++set_head->name_size;
+ ++set_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize the "s_info" array, by parsing an ascii "template" file
+ */
+errr init_s_info_txt(FILE *fp, char *buf)
+{
+ int i, z, order = 1;
+
+ char *s;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ skill_type *s_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'T' for "skill Tree" */
+ if (buf[0] == 'T')
+ {
+ char *sec;
+ s16b s1, s2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ s1 = find_skill(buf + 2);
+ s2 = find_skill(sec);
+ if (s2 == -1) return (1);
+
+ s_info[s2].father = s1;
+ s_info[s2].order = order++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Exclusive" */
+ if (buf[0] == 'E')
+ {
+ char *sec;
+ s16b s1, s2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ s1 = find_skill(buf + 2);
+ s2 = find_skill(sec);
+ if ((s1 == -1) || (s2 == -1)) return (1);
+
+ s_info[s1].action[s2] = SKILL_EXCLUSIVE;
+ s_info[s2].action[s1] = SKILL_EXCLUSIVE;
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'O' for "Opposite" */
+ if (buf[0] == 'O')
+ {
+ char *sec, *cval;
+ s16b s1, s2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+ if (NULL == (cval = strchr(sec, '%')))
+ {
+ return (1);
+ }
+ *cval = '\0';
+ cval++;
+ if (!*cval) return (1);
+
+ s1 = find_skill(buf + 2);
+ s2 = find_skill(sec);
+ if ((s1 == -1) || (s2 == -1)) return (1);
+
+ s_info[s1].action[s2] = -atoi(cval);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Amical/friendly" */
+ if (buf[0] == 'f')
+ {
+ char *sec, *cval;
+ s16b s1, s2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+ if (NULL == (cval = strchr(sec, '%')))
+ {
+ return (1);
+ }
+ *cval = '\0';
+ cval++;
+ if (!*cval) return (1);
+
+ s1 = find_skill(buf + 2);
+ s2 = find_skill(sec);
+ if ((s1 == -1) || (s2 == -1)) return (1);
+
+ s_info[s1].action[s2] = atoi(cval);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i >= s_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ s_ptr = &s_info[i];
+
+ /* Hack -- Verify space */
+ if (s_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!s_ptr->name) s_ptr->name = ++s_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(s_name + s_head->name_size, s);
+
+ /* Advance the index */
+ s_head->name_size += strlen(s);
+
+ /* Init */
+ s_ptr->action_mkey = 0;
+ s_ptr->dev = FALSE;
+ s_ptr->random_gain_chance = 100;
+ for (z = 0; z < max_s_idx; z++)
+ {
+ s_ptr->action[z] = 0;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current s_ptr */
+ if (!s_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (s_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!s_ptr->desc)
+ {
+ s_ptr->desc = ++s_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(s_text + s_head->text_size, s);
+
+ /* Advance the index */
+ s_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(s_text + s_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ s_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Activation Description" */
+ if (buf[0] == 'A')
+ {
+ char *txt;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ if (NULL == (txt = strchr(s, ':'))) return (1);
+ *txt = '\0';
+ txt++;
+
+ /* Hack -- Verify space */
+ if (s_head->text_size + strlen(txt) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!s_ptr->action_desc) s_ptr->action_desc = ++s_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(s_text + s_head->text_size, txt);
+ s_ptr->action_mkey = atoi(s);
+
+ /* Advance the index */
+ s_head->text_size += strlen(txt);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int rate;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d", &rate))
+ {
+ return (1);
+ }
+
+ /* Save the values */
+ s_ptr->rate = rate;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "random Gain" (one line only) */
+ if (buf[0] == 'G')
+ {
+ int chance;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d", &chance))
+ {
+ return (1);
+ }
+
+ /* Save the values */
+ s_ptr->random_gain_chance = chance;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ char *t;
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_skill_flag(&(s_ptr->flags1), s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++s_head->name_size;
+ ++s_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "ab_info" array, by parsing an ascii "template" file
+ */
+errr init_ab_info_txt(FILE *fp, char *buf)
+{
+ int i, z;
+
+ char *s;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ ability_type *ab_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i >= ab_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ab_ptr = &ab_info[i];
+
+ /* Hack -- Verify space */
+ if (ab_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!ab_ptr->name) ab_ptr->name = ++ab_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(ab_name + ab_head->name_size, s);
+
+ /* Advance the index */
+ ab_head->name_size += strlen(s);
+
+ /* Init */
+ ab_ptr->action_mkey = 0;
+ ab_ptr->acquired = FALSE;
+ for (z = 0; z < 10; z++)
+ {
+ ab_ptr->skills[z] = -1;
+ ab_ptr->need_abilities[z] = -1;
+ ab_ptr->forbid_abilities[z] = -1;
+ }
+ for (z = 0; z < 6; z++)
+ {
+ ab_ptr->stat[z] = -1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current ab_ptr */
+ if (!ab_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (ab_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!ab_ptr->desc)
+ {
+ ab_ptr->desc = ++ab_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(ab_text + ab_head->text_size, s);
+
+ /* Advance the index */
+ ab_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(ab_text + ab_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ ab_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Activation Description" */
+ if (buf[0] == 'A')
+ {
+ char *txt;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ if (NULL == (txt = strchr(s, ':'))) return (1);
+ *txt = '\0';
+ txt++;
+
+ /* Hack -- Verify space */
+ if (ab_head->text_size + strlen(txt) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!ab_ptr->action_desc) ab_ptr->action_desc = ++ab_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(ab_text + ab_head->text_size, txt);
+ ab_ptr->action_mkey = atoi(s);
+
+ /* Advance the index */
+ ab_head->text_size += strlen(txt);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int cost;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d", &cost))
+ {
+ return (1);
+ }
+
+ /* Save the values */
+ ab_ptr->cost = cost;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "Skill" */
+ if (buf[0] == 'k')
+ {
+ char *sec;
+ s16b level, skill;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ level = atoi(buf + 2);
+ skill = find_skill(sec);
+
+ if (skill == -1) return (1);
+
+ for (z = 0; z < 10; z++)
+ if (ab_ptr->skills[z] == -1) break;
+
+ if (z < 10)
+ {
+ ab_ptr->skills[z] = skill;
+ ab_ptr->skill_levels[z] = level;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'a' for "needed ability" */
+ if (buf[0] == 'a')
+ {
+ s16b ab;
+
+ ab = find_ability(buf + 2);
+
+ if (ab == -1) return (1);
+
+ for (z = 0; z < 10; z++)
+ if (ab_ptr->need_abilities[z] == -1) break;
+
+ if (z < 10)
+ {
+ ab_ptr->need_abilities[z] = ab;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Stat" */
+ if (buf[0] == 'S')
+ {
+ char *sec;
+ s16b stat;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ for (stat = 0; stat < 6; stat++)
+ {
+ if (!strcmp(stat_names[stat], sec))
+ break;
+ }
+
+ if (stat == 6) return (1);
+
+ ab_ptr->stat[stat] = atoi(buf + 2);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Excluding ability" */
+ if (buf[0] == 'E')
+ {
+ char *sec;
+ s16b ab1, ab2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ ab1 = find_ability(buf + 2);
+ ab2 = find_ability(sec);
+
+ if ((ab1 == -1) || (ab2 == -1)) return (1);
+
+ for (z = 0; z < 10; z++)
+ if (ab_info[ab1].forbid_abilities[z] == -1) break;
+ if (z < 10)
+ {
+ ab_info[ab1].forbid_abilities[z] = ab2;
+ }
+
+ for (z = 0; z < 10; z++)
+ if (ab_info[ab2].forbid_abilities[z] == -1) break;
+ if (z < 10)
+ {
+ ab_info[ab2].forbid_abilities[z] = ab1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++ab_head->name_size;
+ ++ab_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one flag in a ego-item_type from a textual string
+ */
+static bool_ grab_one_ego_item_flag(ego_item_type *e_ptr, cptr what, int n, bool_ obvious)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ if (obvious)
+ e_ptr->oflags1[n] |= (1L << i);
+ else
+ e_ptr->flags1[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ if (obvious)
+ e_ptr->oflags2[n] |= (1L << i);
+ else
+ e_ptr->flags2[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ if (obvious)
+ e_ptr->oflags2[n] |= (1L << i);
+ else
+ e_ptr->flags2[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ if (obvious)
+ e_ptr->oflags3[n] |= (1L << i);
+ else
+ e_ptr->flags3[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ if (obvious)
+ e_ptr->oflags4[n] |= (1L << i);
+ else
+ e_ptr->flags4[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ if (obvious)
+ e_ptr->oflags5[n] |= (1L << i);
+ else
+ e_ptr->flags5[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ if (obvious)
+ e_ptr->oesp[n] |= (1L << i);
+ else
+ e_ptr->esp[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check ego_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, ego_flags[i]))
+ {
+ e_ptr->fego[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown ego-item flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+static bool_ grab_one_ego_item_flag_restrict(ego_item_type *e_ptr, cptr what, bool_ need)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ if (need)
+ e_ptr->need_flags1 |= (1L << i);
+ else
+ e_ptr->forbid_flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ if (need)
+ e_ptr->need_flags2 |= (1L << i);
+ else
+ e_ptr->forbid_flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ if (need)
+ e_ptr->need_flags2 |= (1L << i);
+ else
+ e_ptr->forbid_flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ if (need)
+ e_ptr->need_flags3 |= (1L << i);
+ else
+ e_ptr->forbid_flags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ if (need)
+ e_ptr->need_flags4 |= (1L << i);
+ else
+ e_ptr->forbid_flags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ if (need)
+ e_ptr->need_flags5 |= (1L << i);
+ else
+ e_ptr->forbid_flags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ if (need)
+ e_ptr->need_esp |= (1L << i);
+ else
+ e_ptr->forbid_esp |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown ego-item restrict flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+
+
+/*
+ * Initialize the "e_info" array, by parsing an ascii "template" file
+ */
+errr init_e_info_txt(FILE *fp, char *buf)
+{
+ int i, cur_r = -1, cur_t = 0, j;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ ego_item_type *e_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= e_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ e_ptr = &e_info[i];
+
+ /* Hack -- Verify space */
+ if (e_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!e_ptr->name) e_ptr->name = ++e_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(e_name + e_head->name_size, s);
+
+ /* Advance the index */
+ e_head->name_size += strlen(s);
+
+ /* Needed hack */
+ e_ptr->power = -1;
+ cur_r = -1;
+ cur_t = 0;
+
+ for (j = 0; j < 10; j++)
+ {
+ e_ptr->tval[j] = 255;
+ }
+ for (j = 0; j < 5; j++)
+ {
+ e_ptr->rar[j] = 0;
+ e_ptr->flags1[j] = 0;
+ e_ptr->flags2[j] = 0;
+ e_ptr->flags3[j] = 0;
+ e_ptr->flags4[j] = 0;
+ e_ptr->flags5[j] = 0;
+ e_ptr->esp[j] = 0;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current e_ptr */
+ if (!e_ptr) return (3);
+
+
+ /* Process 'T' for "Tval/Sval" (up to 5 lines) */
+ if (buf[0] == 'T')
+ {
+ int tv, minsv, maxsv;
+
+ if (cur_t == 10) return 1;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tv, &minsv, &maxsv)) return (1);
+
+ /* Save the values */
+ e_ptr->tval[cur_t] = tv;
+ e_ptr->min_sval[cur_t] = minsv;
+ e_ptr->max_sval[cur_t] = maxsv;
+
+ cur_t++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "flags rarity" (up to 5 lines) */
+ if (buf[0] == 'R')
+ {
+ int rar;
+
+ if (cur_r == 5) return 1;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d",
+ &rar)) return (1);
+
+ cur_r++;
+
+ /* Save the values */
+ e_ptr->rar[cur_r] = rar;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'X' for "Xtra" (one line only) */
+ if (buf[0] == 'X')
+ {
+ int slot, rating;
+ char pos;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%c:%d:%d",
+ &pos, &slot, &rating)) return (1);
+
+ /* Save the values */
+ /* e_ptr->slot = slot; */
+ e_ptr->rating = rating;
+ e_ptr->before = (pos == 'B') ? TRUE : FALSE;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int level, rarity, rarity2;
+ long cost;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld",
+ &level, &rarity, &rarity2, &cost)) return (1);
+
+ /* Save the values */
+ e_ptr->level = level;
+ e_ptr->rarity = rarity;
+ e_ptr->mrarity = rarity2;
+ e_ptr->cost = cost;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'C' for "creation" */
+ if (buf[0] == 'C')
+ {
+ int th, td, ta, pv;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &th, &td, &ta, &pv)) return (1);
+
+ e_ptr->max_to_h = th;
+ e_ptr->max_to_d = td;
+ e_ptr->max_to_a = ta;
+ e_ptr->max_pval = pv;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "Granted power" */
+ if (buf[0] == 'Z')
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ e_ptr->power = i;
+
+ /* Next... */
+ continue;
+ }
+
+ if (buf[0] == 'a')
+ {
+ if (prefix(buf + 2, "HARDCORE="))
+ {
+ e_ptr->activate = get_activation(buf + 11);
+ if (e_ptr->activate == -1)
+ return 1;
+ }
+ else if (prefix(buf + 2, "SPELL="))
+ {
+ e_ptr->activate = -find_spell(buf + 8);
+ if (e_ptr->activate == -( -1))
+ return 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'r:N' for needed flags */
+ if ((buf[0] == 'r') && (buf[2] == 'N'))
+ {
+ /* Parse every entry textually */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_item_flag_restrict(e_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'r:F' for forbidden flags */
+ if ((buf[0] == 'r') && (buf[2] == 'F'))
+ {
+ /* Parse every entry textually */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_item_flag_restrict(e_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ if (cur_r == -1) return (6);
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_item_flag(e_ptr, s, cur_r, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'f' for obvious flags */
+ if (buf[0] == 'f')
+ {
+ if (cur_r == -1) return (6);
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_item_flag(e_ptr, s, cur_r, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++e_head->name_size;
+ ++e_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one flag in a randart_part_type from a textual string
+ */
+static bool_ grab_one_randart_item_flag(randart_part_type *ra_ptr, cptr what, char c)
+{
+ int i;
+ u32b *f1, *f2, *f3, *f4, *f5, *esp;
+
+ if (c == 'F')
+ {
+ f1 = &ra_ptr->flags1;
+ f2 = &ra_ptr->flags2;
+ f3 = &ra_ptr->flags3;
+ f4 = &ra_ptr->flags4;
+ f5 = &ra_ptr->flags5;
+ esp = &ra_ptr->esp;
+ }
+ else
+ {
+ f1 = &ra_ptr->aflags1;
+ f2 = &ra_ptr->aflags2;
+ f3 = &ra_ptr->aflags3;
+ f4 = &ra_ptr->aflags4;
+ f5 = &ra_ptr->aflags5;
+ esp = &ra_ptr->aesp;
+ }
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ *f1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ *f2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ *f2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ *f3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ *f4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ *f5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ *esp |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check ego_flags */
+ if (c == 'F')
+ {
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, ego_flags[i]))
+ {
+ ra_ptr->fego |= (1L << i);
+ return (0);
+ }
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown ego-item flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+
+
+/*
+ * Initialize the "ra_info" array, by parsing an ascii "template" file
+ */
+errr init_ra_info_txt(FILE *fp, char *buf)
+{
+ int i, cur_t = 0, j, cur_g = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ randart_part_type *ra_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'G' for "General" (up to 30 lines) */
+ if (buf[0] == 'G')
+ {
+ int chance, dd, ds, plus;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%dd%d:%d",
+ &chance, &dd, &ds, &plus)) return (1);
+
+ /* Save the values */
+ ra_gen[cur_g].chance = chance;
+ ra_gen[cur_g].dd = dd;
+ ra_gen[cur_g].ds = ds;
+ ra_gen[cur_g].plus = plus;
+ cur_g++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number" */
+ if (buf[0] == 'N')
+ {
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= ra_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ra_ptr = &ra_info[i];
+
+ /* Needed hack */
+ ra_ptr->power = -1;
+ cur_t = 0;
+
+ for (j = 0; j < 20; j++)
+ {
+ ra_ptr->tval[j] = 255;
+ }
+ ra_ptr->flags1 = 0;
+ ra_ptr->flags2 = 0;
+ ra_ptr->flags3 = 0;
+ ra_ptr->flags4 = 0;
+ ra_ptr->flags5 = 0;
+ ra_ptr->esp = 0;
+ ra_ptr->fego = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current ra_ptr */
+ if (!ra_ptr) return (3);
+
+ /* Process 'T' for "Tval/Sval" (up to 5 lines) */
+ if (buf[0] == 'T')
+ {
+ int tv, minsv, maxsv;
+
+ if (cur_t == 20) return 1;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tv, &minsv, &maxsv)) return (1);
+
+ /* Save the values */
+ ra_ptr->tval[cur_t] = tv;
+ ra_ptr->min_sval[cur_t] = minsv;
+ ra_ptr->max_sval[cur_t] = maxsv;
+
+ cur_t++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'X' for "Xtra" (one line only) */
+ if (buf[0] == 'X')
+ {
+ int power, max;
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 2, "%d:%d",
+ &power, &max)) return (1);
+
+ /* Save the values */
+ ra_ptr->value = power;
+ ra_ptr->max = max;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int level, rarity, rarity2;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &level, &rarity, &rarity2)) return (1);
+
+ /* Save the values */
+ ra_ptr->level = level;
+ ra_ptr->rarity = rarity;
+ ra_ptr->mrarity = rarity2;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'C' for "creation" */
+ if (buf[0] == 'C')
+ {
+ int th, td, ta, pv;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &th, &td, &ta, &pv)) return (1);
+
+ ra_ptr->max_to_h = th;
+ ra_ptr->max_to_d = td;
+ ra_ptr->max_to_a = ta;
+ ra_ptr->max_pval = pv;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "Granted power" */
+ if (buf[0] == 'Z')
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ ra_ptr->power = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_randart_item_flag(ra_ptr, s, 'F')) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'A' for antagonic flags */
+ if (buf[0] == 'A')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_randart_item_flag(ra_ptr, s, 'A')) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one (basic) flag in a monster_race from a textual string
+ */
+static errr grab_one_basic_flag(monster_race *r_ptr, cptr what)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ {
+ r_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags2[i]))
+ {
+ r_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags3[i]))
+ {
+ r_ptr->flags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags7 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags7[i]))
+ {
+ r_ptr->flags7 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags8 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags8[i]))
+ {
+ r_ptr->flags8 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags9 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags9[i]))
+ {
+ r_ptr->flags9 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Grab one (spell) flag in a monster_race from a textual string
+ */
+static errr grab_one_spell_flag(monster_race *r_ptr, cptr what)
+{
+ int i;
+
+ /* Scan flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags4[i]))
+ {
+ r_ptr->flags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags5[i]))
+ {
+ r_ptr->flags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags6 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags6[i]))
+ {
+ r_ptr->flags6 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Initialize the "r_info" array, by parsing an ascii "template" file
+ */
+errr init_r_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ monster_race *r_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ r_head->name_size = 0;
+ r_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= r_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ r_ptr = &r_info[i];
+
+ /* Hack -- Verify space */
+ if (r_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!r_ptr->name) r_ptr->name = ++r_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(r_name + r_head->name_size, s);
+
+ /* Advance the index */
+ r_head->name_size += strlen(s);
+
+ /* HACK -- Those ones HAVE to have a set default value */
+ r_ptr->drops.treasure = OBJ_GENE_TREASURE;
+ r_ptr->drops.combat = OBJ_GENE_COMBAT;
+ r_ptr->drops.magic = OBJ_GENE_MAGIC;
+ r_ptr->drops.tools = OBJ_GENE_TOOL;
+ r_ptr->freq_inate = r_ptr->freq_spell = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current r_ptr */
+ if (!r_ptr) return (3);
+
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (r_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!r_ptr->text) r_ptr->text = ++r_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(r_text + r_head->text_size, s);
+
+ /* Advance the index */
+ r_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Graphics" (one line only) */
+ if (buf[0] == 'G')
+ {
+ char sym;
+ int tmp;
+
+ /* Paranoia */
+ if (!buf[2]) return (1);
+ if (!buf[3]) return (1);
+ if (!buf[4]) return (1);
+
+ /* Extract the char */
+ sym = buf[2];
+
+ /* Extract the attr */
+ tmp = color_char_to_attr(buf[4]);
+
+ /* Paranoia */
+ if (tmp < 0) return (1);
+
+ /* Save the values */
+ r_ptr->d_char = sym;
+ r_ptr->d_attr = tmp;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int spd, hp1, hp2, aaf, ac, slp;
+
+ /* Scan for the other values */
+ if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d",
+ &spd, &hp1, &hp2, &aaf, &ac, &slp)) return (1);
+
+ /* Save the values */
+ r_ptr->speed = spd;
+ r_ptr->hdice = hp1;
+ r_ptr->hside = hp2;
+ r_ptr->aaf = aaf;
+ r_ptr->ac = ac;
+ r_ptr->sleep = slp;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Body Parts" (one line only) */
+ if (buf[0] == 'E')
+ {
+ int weap, tors, fing, head, arms, legs;
+
+ /* Scan for the other values */
+ if (BODY_MAX != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d",
+ &weap, &tors, &arms, &fing, &head, &legs)) return (1);
+
+ /* Save the values */
+ r_ptr->body_parts[BODY_WEAPON] = weap;
+ r_ptr->body_parts[BODY_TORSO] = tors;
+ r_ptr->body_parts[BODY_ARMS] = arms;
+ r_ptr->body_parts[BODY_FINGER] = fing;
+ r_ptr->body_parts[BODY_HEAD] = head;
+ r_ptr->body_parts[BODY_LEGS] = legs;
+
+ /* Mega debugging hack */
+ if (weap > arms) quit(format("monster %d, %d weapon(s), %d arm(s) !", error_idx, weap, arms));
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object type" (one line only) */
+ if (buf[0] == 'O')
+ {
+ int treasure, combat, magic, tools;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &treasure, &combat, &magic, &tools)) return (1);
+
+ /* Save the values */
+ r_ptr->drops.treasure = treasure;
+ r_ptr->drops.combat = combat;
+ r_ptr->drops.magic = magic;
+ r_ptr->drops.tools = tools;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int lev, rar, wt;
+ long exp;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld",
+ &lev, &rar, &wt, &exp)) return (1);
+
+ /* Save the values */
+ r_ptr->level = lev;
+ r_ptr->rarity = rar;
+ /* MEGA HACK */
+ if (!wt) wt = 100;
+ r_ptr->weight = wt;
+ r_ptr->mexp = exp;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'B' for "Blows" (up to four lines) */
+ if (buf[0] == 'B')
+ {
+ int n1, n2;
+
+ /* Find the next empty blow slot (if any) */
+ for (i = 0; i < 4; i++) if (!r_ptr->blow[i].method) break;
+
+ /* Oops, no more slots */
+ if (i == 4) return (1);
+
+ /* Analyze the first field */
+ for (s = t = buf + 2; *t && (*t != ':'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == ':') *t++ = '\0';
+
+ /* Analyze the method */
+ for (n1 = 0; r_info_blow_method[n1]; n1++)
+ {
+ if (streq(s, r_info_blow_method[n1])) break;
+ }
+
+ /* Invalid method */
+ if (!r_info_blow_method[n1]) return (1);
+
+ /* Analyze the second field */
+ for (s = t; *t && (*t != ':'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == ':') *t++ = '\0';
+
+ /* Analyze effect */
+ for (n2 = 0; r_info_blow_effect[n2]; n2++)
+ {
+ if (streq(s, r_info_blow_effect[n2])) break;
+ }
+
+ /* Invalid effect */
+ if (!r_info_blow_effect[n2]) return (1);
+
+ /* Analyze the third field */
+ for (s = t; *t && (*t != 'd'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == 'd') *t++ = '\0';
+
+ /* Save the method */
+ r_ptr->blow[i].method = n1;
+
+ /* Save the effect */
+ r_ptr->blow[i].effect = n2;
+
+ /* Extract the damage dice and sides */
+ r_ptr->blow[i].d_dice = atoi(s);
+ r_ptr->blow[i].d_side = atoi(t);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "Basic Flags" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_basic_flag(r_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Spell Flags" (multiple lines) */
+ if (buf[0] == 'S')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read spell frequency */
+ if (1 == sscanf(s, "1_IN_%d", &i))
+ {
+ /* Extract a "frequency" */
+ r_ptr->freq_spell = r_ptr->freq_inate = 100 / i;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_spell_flag(r_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++r_head->name_size;
+ ++r_head->text_size;
+
+ for (i = 1; i < max_r_idx; i++)
+ {
+ /* Invert flag WILD_ONLY <-> RF8_DUNGEON */
+ r_info[i].flags8 ^= 1L;
+
+ /* WILD_TOO without any other wilderness flags enables all flags */
+ if ((r_info[i].flags8 & RF8_WILD_TOO) && !(r_info[i].flags8 & 0x7FFFFFFE))
+ r_info[i].flags8 = 0x0463;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one (basic) flag in a monster_race from a textual string
+ */
+static errr grab_one_basic_ego_flag(monster_ego *re_ptr, cptr what, bool_ add)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ {
+ if (add)
+ re_ptr->mflags1 |= (1L << i);
+ else
+ re_ptr->nflags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags2[i]))
+ {
+ if (add)
+ re_ptr->mflags2 |= (1L << i);
+ else
+ re_ptr->nflags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags3[i]))
+ {
+ if (add)
+ re_ptr->mflags3 |= (1L << i);
+ else
+ re_ptr->nflags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags7 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags7[i]))
+ {
+ if (add)
+ re_ptr->mflags7 |= (1L << i);
+ else
+ re_ptr->nflags7 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags8 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags8[i]))
+ {
+ if (add)
+ re_ptr->mflags8 |= (1L << i);
+ else
+ re_ptr->nflags8 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags9 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags9[i]))
+ {
+ if (add)
+ re_ptr->mflags9 |= (1L << i);
+ else
+ re_ptr->nflags9 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Grab one (spell) flag in a monster_race from a textual string
+ */
+static errr grab_one_spell_ego_flag(monster_ego *re_ptr, cptr what, bool_ add)
+{
+ int i;
+
+ /* Scan flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags4[i]))
+ {
+ if (add)
+ re_ptr->mflags4 |= (1L << i);
+ else
+ re_ptr->nflags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags5[i]))
+ {
+ if (add)
+ re_ptr->mflags5 |= (1L << i);
+ else
+ re_ptr->nflags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags6 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags6[i]))
+ {
+ if (add)
+ re_ptr->mflags6 |= (1L << i);
+ else
+ re_ptr->nflags6 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Grab one (basic) flag in a monster_race from a textual string
+ */
+static errr grab_one_ego_flag(monster_ego *re_ptr, cptr what, bool_ must)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ {
+ if (must) re_ptr->flags1 |= (1L << i);
+ else re_ptr->hflags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags2[i]))
+ {
+ if (must) re_ptr->flags2 |= (1L << i);
+ else re_ptr->hflags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags3[i]))
+ {
+ if (must) re_ptr->flags3 |= (1L << i);
+ else re_ptr->hflags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags7 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags7[i]))
+ {
+ if (must) re_ptr->flags7 |= (1L << i);
+ else re_ptr->hflags7 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags8 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags8[i]))
+ {
+ if (must) re_ptr->flags8 |= (1L << i);
+ else re_ptr->hflags8 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags9 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags9[i]))
+ {
+ if (must) re_ptr->flags9 |= (1L << i);
+ else re_ptr->hflags9 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Initialize the "re_info" array, by parsing an ascii "template" file
+ */
+errr init_re_info_txt(FILE *fp, char *buf)
+{
+ int i, j;
+
+ byte blow_num = 0;
+ int r_char_number = 0, nr_char_number = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ monster_ego *re_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ re_head->name_size = 0;
+ re_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= re_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ re_ptr = &re_info[i];
+
+ /* Hack -- Verify space */
+ if (re_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!re_ptr->name) re_ptr->name = ++re_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(re_name + re_head->name_size, s);
+
+ /* Advance the index */
+ re_head->name_size += strlen(s);
+
+ /* Some inits */
+ blow_num = 0;
+ r_char_number = 0;
+ nr_char_number = 0;
+ for (j = 0; j < 5; j++) re_ptr->r_char[j] = 0;
+ for (j = 0; j < 5; j++) re_ptr->nr_char[j] = 0;
+ for (j = 0; j < 4; j++)
+ {
+ re_ptr->blow[j].method = 0;
+ re_ptr->blow[j].effect = 0;
+ re_ptr->blow[j].d_dice = 0;
+ re_ptr->blow[j].d_side = 0;
+ re_ptr->blowm[j][0] = MEGO_ADD;
+ re_ptr->blowm[j][1] = MEGO_ADD;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current re_ptr */
+ if (!re_ptr) return (3);
+
+ /* Process 'G' for "Graphics" (one line only) */
+ if (buf[0] == 'G')
+ {
+ char sym;
+ int tmp;
+
+ /* Paranoia */
+ if (!buf[2]) return (1);
+ if (!buf[3]) return (1);
+ if (!buf[4]) return (1);
+
+ /* Extract the char */
+ if (buf[2] != '*') sym = buf[2];
+ else sym = MEGO_CHAR_ANY;
+
+ /* Extract the attr */
+ if (buf[4] != '*') tmp = color_char_to_attr(buf[4]);
+ else tmp = MEGO_CHAR_ANY;
+
+ /* Paranoia */
+ if (tmp < 0) return (1);
+
+ /* Save the values */
+ re_ptr->d_char = sym;
+ re_ptr->d_attr = tmp;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int spd, hp1, hp2, aaf, ac, slp;
+ char mspd, mhp1, mhp2, maaf, mac, mslp;
+
+ /* Scan for the other values */
+ if (12 != sscanf(buf + 2, "%c%d:%c%dd%c%d:%c%d:%c%d:%c%d",
+ &mspd, &spd, &mhp1, &hp1, &mhp2, &hp2, &maaf, &aaf, &mac, &ac, &mslp, &slp)) return (1);
+
+ /* Save the values */
+ re_ptr->speed = (spd << 2) + monster_ego_modify(mspd);
+ re_ptr->hdice = (hp1 << 2) + monster_ego_modify(mhp1);
+ re_ptr->hside = (hp2 << 2) + monster_ego_modify(mhp2);
+ re_ptr->aaf = (aaf << 2) + monster_ego_modify(maaf);
+ re_ptr->ac = (ac << 2) + monster_ego_modify(mac);
+ re_ptr->sleep = (slp << 2) + monster_ego_modify(mslp);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int lev, rar, wt;
+ char mlev, mwt, mexp, pos;
+ long exp;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 2, "%c%d:%d:%c%d:%c%ld:%c",
+ &mlev, &lev, &rar, &mwt, &wt, &mexp, &exp, &pos)) return (1);
+
+ /* Save the values */
+ re_ptr->level = (lev << 2) + monster_ego_modify(mlev);
+ re_ptr->rarity = rar;
+ re_ptr->weight = (wt << 2) + monster_ego_modify(mwt);
+ re_ptr->mexp = (exp << 2) + monster_ego_modify(mexp);
+ re_ptr->before = (pos == 'B') ? TRUE : FALSE;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'B' for "Blows" (up to four lines) */
+ if (buf[0] == 'B')
+ {
+ int n1, n2, dice, side;
+ char mdice, mside;
+
+ /* Oops, no more slots */
+ if (blow_num == 4) return (1);
+
+ /* Analyze the first field */
+ for (s = t = buf + 2; *t && (*t != ':'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == ':') *t++ = '\0';
+
+ /* Analyze the method */
+ for (n1 = 0; r_info_blow_method[n1]; n1++)
+ {
+ if (streq(s, r_info_blow_method[n1])) break;
+ }
+
+ /* Invalid method */
+ if (!r_info_blow_method[n1]) return (1);
+
+ /* Analyze the second field */
+ for (s = t; *t && (*t != ':'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == ':') *t++ = '\0';
+
+ /* Analyze effect */
+ for (n2 = 0; r_info_blow_effect[n2]; n2++)
+ {
+ if (streq(s, r_info_blow_effect[n2])) break;
+ }
+
+ /* Invalid effect */
+ if (!r_info_blow_effect[n2]) return (1);
+
+ /* Save the method */
+ re_ptr->blow[blow_num].method = n1;
+
+ /* Save the effect */
+ re_ptr->blow[blow_num].effect = n2;
+
+ /* Extract the damage dice and sides */
+ if (4 != sscanf(t, "%c%dd%c%d",
+ &mdice, &dice, &mside, &side)) return (1);
+
+ re_ptr->blow[blow_num].d_dice = dice;
+ re_ptr->blow[blow_num].d_side = side;
+ re_ptr->blowm[blow_num][0] = monster_ego_modify(mdice);
+ re_ptr->blowm[blow_num][1] = monster_ego_modify(mside);
+ blow_num++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "Flags monster must have" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ char r_char;
+
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read monster symbols */
+ if (1 == sscanf(s, "R_CHAR_%c", &r_char))
+ {
+ /* Limited to 5 races */
+ if (r_char_number >= 5) continue;
+
+ /* Extract a "frequency" */
+ re_ptr->r_char[r_char_number++] = r_char;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_flag(re_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'H' for "Flags monster must not have" (multiple lines) */
+ if (buf[0] == 'H')
+ {
+ char r_char;
+
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read monster symbols */
+ if (1 == sscanf(s, "R_CHAR_%c", &r_char))
+ {
+ /* Limited to 5 races */
+ if (nr_char_number >= 5) continue;
+
+ /* Extract a "frequency" */
+ re_ptr->nr_char[nr_char_number++] = r_char;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_flag(re_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'M' for "Basic Monster Flags" (multiple lines) */
+ if (buf[0] == 'M')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_basic_ego_flag(re_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Basic Monster -Flags" (multiple lines) */
+ if (buf[0] == 'O')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read no flags */
+ if (!strcmp(s, "MF_ALL"))
+ {
+ /* No flags */
+ re_ptr->nflags1 = re_ptr->nflags2 = re_ptr->nflags3 = re_ptr->nflags7 = re_ptr->nflags8 = re_ptr->nflags9 = 0xFFFFFFFF;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_basic_ego_flag(re_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Spell Flags" (multiple lines) */
+ if (buf[0] == 'S')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read spell frequency */
+ if (1 == sscanf(s, "1_IN_%d", &i))
+ {
+ /* Extract a "frequency" */
+ re_ptr->freq_spell = re_ptr->freq_inate = 100 / i;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_spell_ego_flag(re_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'T' for "Spell -Flags" (multiple lines) */
+ if (buf[0] == 'T')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read no flags */
+ if (!strcmp(s, "MF_ALL"))
+ {
+ /* No flags */
+ re_ptr->nflags4 = re_ptr->nflags5 = re_ptr->nflags6 = 0xFFFFFFFF;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_spell_ego_flag(re_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++re_head->name_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one flag in an trap_type from a textual string
+ */
+static errr grab_one_trap_type_flag(trap_type *t_ptr, cptr what)
+{
+ s16b i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, t_info_flags[i]))
+ {
+ t_ptr->flags |= (1L << i);
+ return (0);
+ }
+ }
+ /* Oops */
+ msg_format("Unknown trap_type flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+/*
+ * Initialize the "tr_info" array, by parsing an ascii "template" file
+ */
+errr init_t_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ trap_type *t_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Prepare the "fake" stuff */
+ t_head->name_size = 0;
+ t_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i <= error_idx) return (4);
+
+ /* Verify information */
+ if (i >= t_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ t_ptr = &t_info[i];
+
+ /* Hack -- Verify space */
+ if (t_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!t_ptr->name) t_ptr->name = ++t_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(t_name + t_head->name_size, s);
+
+ /* Advance the index */
+ t_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current t_ptr */
+ if (!t_ptr) return (3);
+
+
+ /* Process 'I' for "Information" */
+ if (buf[0] == 'I')
+ {
+ int probability, another, p1valinc, difficulty;
+ int minlevel;
+ int dd, ds;
+ char color;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%dd%d:%c",
+ &difficulty, &probability, &another,
+ &p1valinc, &minlevel, &dd, &ds,
+ &color)) return (1);
+
+ t_ptr->difficulty = (byte)difficulty;
+ t_ptr->probability = (s16b)probability;
+ t_ptr->another = (s16b)another;
+ t_ptr->p1valinc = (s16b)p1valinc;
+ t_ptr->minlevel = (byte)minlevel;
+ t_ptr->dd = (s16b)dd;
+ t_ptr->ds = (s16b)ds;
+ t_ptr->color = color_char_to_attr(color);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (t_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!t_ptr->text) t_ptr->text = ++t_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(t_text + t_head->text_size, s);
+
+ /* Advance the index */
+ t_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+
+ t_ptr->flags = 0;
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_trap_type_flag(t_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++t_head->name_size;
+ ++t_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one flag for a dungeon type from a textual string
+ */
+errr grab_one_dungeon_flag(u32b *flags1, u32b *flags2, cptr what)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, d_info_flags1[i]))
+ {
+ *flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, d_info_flags2[i]))
+ {
+ *flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown dungeon type flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Grab one (basic) flag in a monster_race from a textual string
+ */
+static errr grab_one_basic_monster_flag(dungeon_info_type *d_ptr, cptr what, byte rule)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ {
+ d_ptr->rules[rule].mflags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags2[i]))
+ {
+ d_ptr->rules[rule].mflags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags3[i]))
+ {
+ d_ptr->rules[rule].mflags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags7 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags7[i]))
+ {
+ d_ptr->rules[rule].mflags7 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags8 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags8[i]))
+ {
+ d_ptr->rules[rule].mflags8 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags9 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags9[i]))
+ {
+ d_ptr->rules[rule].mflags9 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Grab one (spell) flag in a monster_race from a textual string
+ */
+static errr grab_one_spell_monster_flag(dungeon_info_type *d_ptr, cptr what, byte rule)
+{
+ int i;
+
+ /* Scan flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags4[i]))
+ {
+ d_ptr->rules[rule].mflags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags5[i]))
+ {
+ d_ptr->rules[rule].mflags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags6 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags6[i]))
+ {
+ d_ptr->rules[rule].mflags6 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Initialize the "d_info" array, by parsing an ascii "template" file
+ */
+errr init_d_info_txt(FILE *fp, char *buf)
+{
+ int i, j;
+
+ s16b rule_num = 0;
+
+ byte r_char_number = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ dungeon_info_type *d_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ d_head->name_size = 0;
+ d_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= d_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ d_ptr = &d_info[i];
+
+ /* Hack -- Verify space */
+ if (d_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!d_ptr->name) d_ptr->name = ++d_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(d_name + d_head->name_size, s);
+
+ /* Advance the index */
+ d_head->name_size += strlen(s);
+
+ /* HACK -- Those ones HAVE to have a set default value */
+ d_ptr->size_x = -1;
+ d_ptr->size_y = -1;
+ d_ptr->ix = -1;
+ d_ptr->iy = -1;
+ d_ptr->ox = -1;
+ d_ptr->oy = -1;
+ d_ptr->fill_method = 1;
+ rule_num = -1;
+ r_char_number = 0;
+ for (j = 0; j < 5; j++)
+ {
+ int k;
+
+ d_ptr->rules[j].mode = DUNGEON_MODE_NONE;
+ d_ptr->rules[j].percent = 0;
+
+ for (k = 0; k < 5; k++) d_ptr->rules[j].r_char[k] = 0;
+ }
+
+ /* HACK -- Those ones HAVE to have a set default value */
+ d_ptr->objs.treasure = OBJ_GENE_TREASURE;
+ d_ptr->objs.combat = OBJ_GENE_COMBAT;
+ d_ptr->objs.magic = OBJ_GENE_MAGIC;
+ d_ptr->objs.tools = OBJ_GENE_TOOL;
+
+ /* The default generator */
+ strcpy(d_ptr->generator, "dungeon");
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current d_ptr */
+ if (!d_ptr) return (3);
+
+ /* Process 'D' for "Description */
+ if (buf[0] == 'D')
+ {
+ /* Acquire short name */
+ d_ptr->short_name[0] = buf[2];
+ d_ptr->short_name[1] = buf[3];
+ d_ptr->short_name[2] = buf[4];
+
+ /* Acquire the text */
+ s = buf + 6;
+
+ /* Hack -- Verify space */
+ if (d_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!d_ptr->text) d_ptr->text = ++d_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(d_text + d_head->text_size, s);
+
+ /* Advance the index */
+ d_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int min_lev, max_lev;
+ int min_plev, next;
+ int min_alloc, max_chance;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d",
+ &min_lev, &max_lev, &min_plev, &next, &min_alloc, &max_chance)) return (1);
+
+ /* Save the values */
+ d_ptr->mindepth = min_lev;
+ d_ptr->maxdepth = max_lev;
+ d_ptr->min_plev = min_plev;
+ d_ptr->next = next;
+ d_ptr->min_m_alloc_level = min_alloc;
+ d_ptr->max_m_alloc_chance = max_chance;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'L' for "fLoor type" (one line only) */
+ if (buf[0] == 'L')
+ {
+ int f1, f2, f3;
+ int p1, p2, p3;
+ int i;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d",
+ &f1, &p1, &f2, &p2, &f3, &p3))
+ {
+ /* Scan for the values - part ii*/
+ if (3 != sscanf(buf + 2, "%d:%d:%d", &p1, &p2,
+ &p3)) return (1);
+
+ /* Save the values */
+ d_ptr->floor_percent1[1] = p1;
+ d_ptr->floor_percent2[1] = p2;
+ d_ptr->floor_percent3[1] = p3;
+
+ continue;
+ }
+
+ /* Save the values */
+ d_ptr->floor1 = f1;
+ d_ptr->floor2 = f2;
+ d_ptr->floor3 = f3;
+
+ for (i = 0; i < 2; i++)
+ {
+ d_ptr->floor_percent1[i] = p1;
+ d_ptr->floor_percent2[i] = p2;
+ d_ptr->floor_percent3[i] = p3;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object type" (one line only) */
+ if (buf[0] == 'O')
+ {
+ int treasure, combat, magic, tools;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &treasure, &combat, &magic, &tools)) return (1);
+
+ /* Save the values */
+ d_ptr->objs.treasure = treasure;
+ d_ptr->objs.combat = combat;
+ d_ptr->objs.magic = magic;
+ d_ptr->objs.tools = tools;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Generator" (one line only) */
+ if (buf[0] == 'G')
+ {
+ strnfmt(d_ptr->generator, 30, "%s", buf + 2);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "wAll type" (one line only) */
+ if (buf[0] == 'A')
+ {
+ int w1, w2, w3, outer, inner;
+ int p1, p2, p3;
+ int i;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &w1, &p1, &w2, &p2, &w3, &p3, &outer, &inner))
+ {
+ /* Scan for the values - part ii*/
+ if (3 != sscanf(buf + 2, "%d:%d:%d", &p1, &p2,
+ &p3)) return (1);
+
+ /* Save the values */
+ d_ptr->fill_percent1[1] = p1;
+ d_ptr->fill_percent2[1] = p2;
+ d_ptr->fill_percent3[1] = p3;
+ continue;
+ }
+
+ /* Save the values */
+ d_ptr->fill_type1 = w1;
+ d_ptr->fill_type2 = w2;
+ d_ptr->fill_type3 = w3;
+
+ for (i = 0; i < 2; i++)
+ {
+ d_ptr->fill_percent1[i] = p1;
+ d_ptr->fill_percent2[i] = p2;
+ d_ptr->fill_percent3[i] = p3;
+ }
+
+ d_ptr->outer_wall = outer;
+ d_ptr->inner_wall = inner;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Effects" (up to four lines) -SC- */
+ if (buf[0] == 'E')
+ {
+ int side, dice, freq, type;
+ cptr tmp;
+
+ /* Find the next empty blow slot (if any) */
+ for (i = 0; i < 4; i++) if ((!d_ptr->d_side[i]) &&
+ (!d_ptr->d_dice[i])) break;
+
+ /* Oops, no more slots */
+ if (i == 4) return (1);
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%dd%d:%d:%d",
+ &dice, &side, &freq, &type))
+ {
+ int j;
+
+ if (3 != sscanf(buf + 2, "%dd%d:%d",
+ &dice, &side, &freq)) return (1);
+
+ tmp = buf + 2;
+ for (j = 0; j < 2; j++)
+ {
+ tmp = strchr(tmp, ':');
+ if (tmp == NULL) return (1);
+ tmp++;
+ }
+
+ j = 0;
+
+ while (d_info_dtypes[j].name != NULL)
+ if (strcmp(d_info_dtypes[j].name, tmp) == 0)
+ {
+ d_ptr->d_type[i] = d_info_dtypes[j].feat;
+ break;
+ }
+ else j++;
+
+ if (d_info_dtypes[j].name == NULL) return (1);
+ }
+ else
+ d_ptr->d_type[i] = type;
+
+ freq *= 10;
+ /* Save the values */
+ d_ptr->d_side[i] = side;
+ d_ptr->d_dice[i] = dice;
+ d_ptr->d_frequency[i] = freq;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "Dungeon Flags" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ int artif = 0, monst = 0, obj = 0;
+ int ix = -1, iy = -1, ox = -1, oy = -1;
+ int fill_method;
+
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Read dungeon in/out coords */
+ if (4 == sscanf(s, "WILD_%d_%d__%d_%d", &ix, &iy, &ox, &oy))
+ {
+ d_ptr->ix = ix;
+ d_ptr->iy = iy;
+ d_ptr->ox = ox;
+ d_ptr->oy = oy;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read dungeon size */
+ if (2 == sscanf(s, "SIZE_%d_%d", &ix, &iy))
+ {
+ d_ptr->size_x = ix;
+ d_ptr->size_y = iy;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read dungeon fill method */
+ if (1 == sscanf(s, "FILL_METHOD_%d", &fill_method))
+ {
+ d_ptr->fill_method = fill_method;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read Final Object */
+ if (1 == sscanf(s, "FINAL_OBJECT_%d", &obj))
+ {
+ /* Extract a "Final Artifact" */
+ d_ptr->final_object = obj;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read Final Artifact */
+ if (1 == sscanf(s, "FINAL_ARTIFACT_%d", &artif ))
+ {
+ /* Extract a "Final Artifact" */
+ d_ptr->final_artifact = artif ;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read Artifact Guardian */
+ if (1 == sscanf(s, "FINAL_GUARDIAN_%d", &monst))
+ {
+ /* Extract a "Artifact Guardian" */
+ d_ptr->final_guardian = monst;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_dungeon_flag(&(d_ptr->flags1), &(d_ptr->flags2), s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "monster generation Rule" (up to 5 lines) */
+ if (buf[0] == 'R')
+ {
+ int percent, mode;
+ int z, y, lims[5];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 2, "%d:%d",
+ &percent, &mode)) return (1);
+
+ /* Save the values */
+ r_char_number = 0;
+ rule_num++;
+
+ d_ptr->rules[rule_num].percent = percent;
+ d_ptr->rules[rule_num].mode = mode;
+
+ /* Lets remap the flat percents */
+ lims[0] = d_ptr->rules[0].percent;
+ for (y = 1; y <= rule_num; y++)
+ {
+ lims[y] = lims[y - 1] + d_ptr->rules[y].percent;
+ }
+ for (z = 0; z < 100; z++)
+ {
+ for (y = rule_num; y >= 0; y--)
+ {
+ if (z < lims[y]) d_ptr->rule_percents[z] = y;
+ }
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'M' for "Basic Flags" (multiple lines) */
+ if (buf[0] == 'M')
+ {
+ byte r_char;
+
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Read monster symbols */
+ if (1 == sscanf(s, "R_CHAR_%c", &r_char))
+ {
+ /* Limited to 5 races */
+ if (r_char_number >= 5) continue;
+
+ /* Extract a "frequency" */
+ d_ptr->rules[rule_num].r_char[r_char_number++] = r_char;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_basic_monster_flag(d_ptr, s, rule_num)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Spell Flags" (multiple lines) */
+ if (buf[0] == 'S')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_spell_monster_flag(d_ptr, s, rule_num)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++d_head->name_size;
+ ++d_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one race flag from a textual string
+ */
+static errr grab_one_race_flag(owner_type *ow_ptr, int state, cptr what)
+{
+ /* int i;
+ cptr s; */
+
+ /* Scan race flags */
+ unknown_shut_up = TRUE;
+ if (!grab_one_race_allow_flag(ow_ptr->races[state], what))
+ {
+ unknown_shut_up = FALSE;
+ return (0);
+ }
+
+ /* Scan classes flags */
+ if (!grab_one_class_flag(ow_ptr->classes[state], what))
+ {
+ unknown_shut_up = FALSE;
+ return (0);
+ }
+
+ /* Oops */
+ unknown_shut_up = FALSE;
+ msg_format("Unknown race/class flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Grab one store flag from a textual string
+ */
+static errr grab_one_store_flag(store_info_type *st_ptr, cptr what)
+{
+ int i;
+
+ /* Scan store flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, st_info_flags1[i]))
+ {
+ st_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown store flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Initialize the "st_info" array, by parsing an ascii "template" file
+ */
+errr init_st_info_txt(FILE *fp, char *buf)
+{
+ int i = 0, item_idx = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ store_info_type *st_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ st_head->name_size = 0;
+ st_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= st_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ st_ptr = &st_info[i];
+
+ /* Hack -- Verify space */
+ if (st_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!st_ptr->name) st_ptr->name = ++st_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(st_name + st_head->name_size, s);
+
+ /* Advance the index */
+ st_head->name_size += strlen(s);
+
+ /* We are ready for a new set of objects */
+ item_idx = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current st_ptr */
+ if (!st_ptr) return (3);
+
+ /* Process 'I' for "Items" (multiple lines) */
+ if (buf[0] == 'I')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ st_ptr->table[item_idx][1] = atoi(buf + 2);
+
+ /* Append chars to the name */
+ st_ptr->table[item_idx++][0] = test_item_name(s);
+
+ st_ptr->table_num = item_idx;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'T' for "Tval/sval" */
+ if (buf[0] == 'T')
+ {
+ int tv1, sv1, rar1;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &rar1, &tv1, &sv1)) return (1);
+
+ /* Get the index */
+ st_ptr->table[item_idx][1] = rar1;
+ /* Hack -- 256 as a sval means all possible items */
+ st_ptr->table[item_idx++][0] = (sv1 < 256) ? lookup_kind(tv1, sv1) : tv1 + 10000;
+
+ st_ptr->table_num = item_idx;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Graphics" one line only) */
+ if (buf[0] == 'G')
+ {
+ char c, a;
+ int attr;
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 2, "%c:%c",
+ &c, &a)) return (1);
+
+ /* Extract the color */
+ attr = color_char_to_attr(a);
+
+ /* Paranoia */
+ if (attr < 0) return (1);
+
+ /* Save the values */
+ st_ptr->d_char = c;
+ st_ptr->d_attr = attr;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Actions" (one line only) */
+ if (buf[0] == 'A')
+ {
+ int a1, a2, a3, a4, a5, a6;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d",
+ &a1, &a2, &a3, &a4, &a5, &a6)) return (1);
+
+ /* Save the values */
+ st_ptr->actions[0] = a1;
+ st_ptr->actions[1] = a2;
+ st_ptr->actions[2] = a3;
+ st_ptr->actions[3] = a4;
+ st_ptr->actions[4] = a5;
+ st_ptr->actions[5] = a6;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "store Flags" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_store_flag(st_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Owners" (one line only) */
+ if (buf[0] == 'O')
+ {
+ int a1, a2, a3, a4;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &a1, &a2, &a3, &a4)) return (1);
+
+ /* Save the values */
+ st_ptr->owners[0] = a1;
+ st_ptr->owners[1] = a2;
+ st_ptr->owners[2] = a3;
+ st_ptr->owners[3] = a4;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "Extra info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int max_obj;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d",
+ &max_obj)) return (1);
+
+ /* Save the values */
+ if (max_obj > STORE_INVEN_MAX) max_obj = STORE_INVEN_MAX;
+ st_ptr->max_obj = max_obj;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++st_head->name_size;
+ ++st_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "ba_info" array, by parsing an ascii "template" file
+ */
+errr init_ba_info_txt(FILE *fp, char *buf)
+{
+ int i = 0;
+
+ char *s;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ store_action_type *ba_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ ba_head->name_size = 0;
+ ba_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= ba_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ba_ptr = &ba_info[i];
+
+ /* Hack -- Verify space */
+ if (ba_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!ba_ptr->name) ba_ptr->name = ++ba_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(ba_name + ba_head->name_size, s);
+
+ /* Advance the index */
+ ba_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current ba_ptr */
+ if (!ba_ptr) return (3);
+
+ /* Process 'C' for "Costs" (one line only) */
+ if (buf[0] == 'C')
+ {
+ int ch, cn, cl;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &ch, &cn, &cl)) return (1);
+
+ /* Save the values */
+ ba_ptr->costs[STORE_HATED] = ch;
+ ba_ptr->costs[STORE_NORMAL] = cn;
+ ba_ptr->costs[STORE_LIKED] = cl;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Infos" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int act, act_res;
+ char letter, letter_aux = 0;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%c:%c", &act, &act_res, &letter, &letter_aux))
+ if (3 != sscanf(buf + 2, "%d:%d:%c", &act, &act_res, &letter))
+ return (1);
+
+ /* Save the values */
+ ba_ptr->action = act;
+ ba_ptr->action_restr = act_res;
+ ba_ptr->letter = letter;
+ ba_ptr->letter_aux = letter_aux;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++ba_head->name_size;
+ ++ba_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "ow_info" array, by parsing an ascii "template" file
+ */
+errr init_ow_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ owner_type *ow_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ ow_head->name_size = 0;
+ ow_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= ow_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ow_ptr = &ow_info[i];
+
+ /* Hack -- Verify space */
+ if (ow_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!ow_ptr->name) ow_ptr->name = ++ow_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(ow_name + ow_head->name_size, s);
+
+ /* Advance the index */
+ ow_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current ow_ptr */
+ if (!ow_ptr) return (3);
+
+
+ /* Process 'C' for "Costs" (one line only) */
+ if (buf[0] == 'C')
+ {
+ int ch, cn, cl;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &ch, &cn, &cl)) return (1);
+
+ /* Save the values */
+ ow_ptr->costs[STORE_HATED] = ch;
+ ow_ptr->costs[STORE_NORMAL] = cn;
+ ow_ptr->costs[STORE_LIKED] = cl;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (multiple lines line only) */
+ if (buf[0] == 'I')
+ {
+ int cost, max_inf, min_inf, haggle, insult;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 2, "%d:%d:%d:%d:%d",
+ &cost, &max_inf, &min_inf, &haggle, &insult)) return (1);
+
+ /* Save the values */
+ ow_ptr->max_cost = cost;
+ ow_ptr->max_inflate = max_inf;
+ ow_ptr->min_inflate = min_inf;
+ ow_ptr->haggle_per = haggle;
+ ow_ptr->insult_max = insult;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'L' for "Liked races/classes" (multiple lines) */
+ if (buf[0] == 'L')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_flag(ow_ptr, STORE_LIKED, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+ /* Process 'H' for "Hated races/classes" (multiple lines) */
+ if (buf[0] == 'H')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_flag(ow_ptr, STORE_HATED, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++ow_head->name_size;
+ ++ow_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one flag for a dungeon type from a textual string
+ */
+static errr grab_one_wf_info_flag(wilderness_type_info *wf_ptr, cptr what)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, wf_info_flags1[i]))
+ {
+ wf_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Initialize the "wf_info" array, by parsing an ascii "template" file
+ */
+errr init_wf_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool_ okay = FALSE;
+
+ /* Current entry */
+ wilderness_type_info *wf_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ wf_head->name_size = 0;
+ wf_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= wf_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ wf_ptr = &wf_info[i];
+
+ /* Hack -- Verify space */
+ if (wf_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!wf_ptr->name) wf_ptr->name = ++wf_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(wf_name + wf_head->name_size, s);
+
+ /* Advance the index */
+ wf_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current wf_ptr */
+ if (!wf_ptr) return (3);
+
+ /* Process 'D' for "Description */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (wf_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!wf_ptr->text) wf_ptr->text = ++wf_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(wf_text + wf_head->text_size, s);
+
+ /* Advance the index */
+ wf_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int entrance, level;
+ int road, feat, ter_idx;
+ char car;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%c",
+ &level, &entrance, &road, &feat, &ter_idx, &car)) return (1);
+
+ /* Save the values */
+ wf_ptr->level = level;
+ wf_ptr->entrance = entrance;
+ wf_ptr->road = road;
+ wf_ptr->feat = feat;
+ wf_ptr->terrain_idx = ter_idx;
+
+ /* To acces it easily from the map structure */
+ wildc2i[(int)car] = error_idx;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'X' for "More Info" (one line only) */
+ if (buf[0] == 'X')
+ {
+ int terrain[MAX_WILD_TERRAIN], i;
+
+ /* Scan for the values */
+ if (MAX_WILD_TERRAIN != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &terrain[0], &terrain[1], &terrain[2],
+ &terrain[3], &terrain[4], &terrain[5],
+ &terrain[6], &terrain[7], &terrain[8],
+ &terrain[9], &terrain[10], &terrain[11],
+ &terrain[12], &terrain[13], &terrain[14],
+ &terrain[15], &terrain[16], &terrain[17])) return (1);
+
+ /* Save the values */
+ for (i = 0; i < MAX_WILD_TERRAIN; i++)
+ {
+ wf_ptr->terrain[i] = terrain[i];
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "Wilderness feature Flags" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_wf_info_flag(wf_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++wf_head->name_size;
+ ++wf_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+
+/* Random dungeon grid effects */
+#define RANDOM_NONE 0x00
+#define RANDOM_FEATURE 0x01
+#define RANDOM_MONSTER 0x02
+#define RANDOM_OBJECT 0x04
+#define RANDOM_EGO 0x08
+#define RANDOM_ARTIFACT 0x10
+#define RANDOM_TRAP 0x20
+
+
+typedef struct dungeon_grid dungeon_grid;
+
+struct dungeon_grid
+{
+ int feature; /* Terrain feature */
+ int monster; /* Monster */
+ int object; /* Object */
+ int ego; /* Ego-Item */
+ int artifact; /* Artifact */
+ int trap; /* Trap */
+ int cave_info; /* Flags for CAVE_MARK, CAVE_GLOW, CAVE_ICKY, CAVE_ROOM */
+ int special; /* Reserved for special terrain info */
+ int random; /* Number of the random effect */
+ int bx, by; /* For between gates */
+ int mimic; /* Mimiced features */
+ s32b mflag; /* monster's mflag */
+ bool_ ok;
+ bool_ defined;
+};
+static bool_ meta_sleep = TRUE;
+
+static dungeon_grid letter[255];
+
+/*
+ * Parse a sub-file of the "extra info"
+ */
+static errr process_dungeon_file_aux(char *buf, int *yval, int *xval, int xvalstart, int ymax, int xmax, bool_ full)
+{
+ int i;
+
+ char *zz[33];
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) return (0);
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) return (0);
+
+ /* Skip comments */
+ if (buf[0] == '#') return (0);
+
+ /* Require "?:*" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Process "%:<fname>" */
+ if (buf[0] == '%')
+ {
+ /* Attempt to Process the given file */
+ return (process_dungeon_file(buf + 2, yval, xval, ymax, xmax, FALSE, full));
+ }
+
+ /* Process "N:<sleep>" */
+ if (buf[0] == 'N')
+ {
+ int num;
+
+ if ((num = tokenize(buf + 2, 1, zz, ':', '/')) > 0)
+ {
+ meta_sleep = atoi(zz[0]);
+ }
+
+ return (0);
+ }
+
+ /* Process "F:<letter>:<terrain>:<cave_info>:<monster>:<object>:<ego>:<artifact>:<trap>:<special>:<mimic>:<mflag>" -- info for dungeon grid */
+ if (buf[0] == 'F')
+ {
+ int num;
+
+ if ((num = tokenize(buf + 2, 11, zz, ':', '/')) > 1)
+ {
+ int index = zz[0][0];
+
+ /* Reset the feature */
+ letter[index].feature = 0;
+ letter[index].monster = 0;
+ letter[index].object = 0;
+ letter[index].ego = 0;
+ letter[index].artifact = 0;
+ letter[index].trap = 0;
+ letter[index].cave_info = 0;
+ letter[index].special = 0;
+ letter[index].random = 0;
+ letter[index].mimic = 0;
+ letter[index].mflag = 0;
+ letter[index].ok = TRUE;
+ letter[index].defined = TRUE;
+
+ if (num > 1)
+ {
+ if (zz[1][0] == '*')
+ {
+ letter[index].random |= RANDOM_FEATURE;
+ if (zz[1][1])
+ {
+ zz[1]++;
+ letter[index].feature = atoi(zz[1]);
+ }
+ }
+ else
+ {
+ letter[index].feature = atoi(zz[1]);
+ }
+ }
+
+ if (num > 2)
+ letter[index].cave_info = atoi(zz[2]);
+
+ /* Monster */
+ if (num > 3)
+ {
+ if (zz[3][0] == '*')
+ {
+ letter[index].random |= RANDOM_MONSTER;
+ if (zz[3][1])
+ {
+ zz[3]++;
+ letter[index].monster = atoi(zz[3]);
+ }
+ }
+ else
+ {
+ letter[index].monster = atoi(zz[3]);
+ }
+ }
+
+ /* Object */
+ if (num > 4)
+ {
+ if (zz[4][0] == '*')
+ {
+ letter[index].random |= RANDOM_OBJECT;
+
+ if (zz[4][1])
+ {
+ zz[4]++;
+ letter[index].object = atoi(zz[4]);
+ }
+ }
+ else
+ {
+ letter[index].object = atoi(zz[4]);
+ }
+ }
+
+ /* Ego-Item */
+ if (num > 5)
+ {
+ if (zz[5][0] == '*')
+ {
+ letter[index].random |= RANDOM_EGO;
+
+ if (zz[5][1])
+ {
+ zz[5]++;
+ letter[index].ego = atoi(zz[5]);
+ }
+ }
+ else
+ {
+ letter[index].ego = atoi(zz[5]);
+ }
+ }
+
+ /* Artifact */
+ if (num > 6)
+ {
+ if (zz[6][0] == '*')
+ {
+ letter[index].random |= RANDOM_ARTIFACT;
+
+ if (zz[6][1])
+ {
+ zz[6]++;
+ letter[index].artifact = atoi(zz[6]);
+ }
+ }
+ else
+ {
+ letter[index].artifact = atoi(zz[6]);
+ }
+ }
+
+ if (num > 7)
+ {
+ if (zz[7][0] == '*')
+ {
+ letter[index].random |= RANDOM_TRAP;
+
+ if (zz[7][1])
+ {
+ zz[7]++;
+ letter[index].trap = atoi(zz[7]);
+ }
+ }
+ else
+ letter[index].trap = atoi(zz[7]);
+ }
+
+ if (num > 8)
+ {
+ /* Quests can be defined by name only */
+ if (zz[8][0] == '"')
+ {
+ int i;
+
+ /* Hunt & shoot the ending " */
+ i = strlen(zz[8]) - 1;
+ if (zz[8][i] == '"') zz[8][i] = '\0';
+ letter[index].special = 0;
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (!strcmp(&zz[8][1], quest[i].name))
+ {
+ letter[index].special = i;
+ break;
+ }
+ }
+ }
+ else
+ letter[index].special = atoi(zz[8]);
+ }
+
+ if (num > 9)
+ {
+ letter[index].mimic = atoi(zz[9]);
+ }
+
+ if (num > 10)
+ {
+ letter[index].mflag = atoi(zz[10]);
+ }
+
+ return (0);
+ }
+ }
+
+ /* Process "f:flags" -- level flags */
+ else if (buf[0] == 'f')
+ {
+ char *s, *t;
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_dungeon_flag(&dungeon_flags1, &dungeon_flags2, s)) return 1;
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ return 0;
+ }
+
+ /* Process "D:<dungeon>" -- info for the cave grids */
+ else if (buf[0] == 'D')
+ {
+ int x, m_idx = 0;
+
+ object_type object_type_body;
+
+ /* Acquire the text */
+ char *s = buf + 2;
+
+ /* Length of the text */
+ int len = strlen(s);
+
+ int y = *yval;
+ *xval = xvalstart;
+ for (x = *xval, i = 0; ((x < xmax) && (i < len)); x++, s++, i++)
+ {
+ /* Access the grid */
+ cave_type *c_ptr = &cave[y][x];
+
+ int idx = s[0];
+
+ int object_index = letter[idx].object;
+ int monster_index = letter[idx].monster;
+ int random = letter[idx].random;
+ int artifact_index = letter[idx].artifact;
+
+ if (!letter[idx].ok) msg_format("Warning '%c' not defined but used.", idx);
+
+ if (init_flags & INIT_GET_SIZE) continue;
+
+ /* use the plasma generator wilderness */
+ if (((!dun_level) || (!letter[idx].defined)) && (idx == ' ')) continue;
+
+ /* Clear some info */
+ c_ptr->info = 0;
+
+ /* Lay down a floor */
+ c_ptr->mimic = letter[idx].mimic;
+ cave_set_feat(y, x, letter[idx].feature);
+
+ /* Cave info */
+ c_ptr->info |= letter[idx].cave_info;
+
+ /* Create a monster */
+ if (random & RANDOM_MONSTER)
+ {
+ int level = monster_level;
+
+ monster_level = quest[p_ptr->inside_quest].level + monster_index;
+
+ m_idx = place_monster(y, x, meta_sleep, FALSE);
+
+ monster_level = level;
+ }
+ else if (monster_index)
+ {
+ /* Place it */
+ m_allow_special[monster_index] = TRUE;
+ m_idx = place_monster_aux(y, x, monster_index, meta_sleep, FALSE, MSTATUS_ENEMY);
+ m_allow_special[monster_index] = FALSE;
+ }
+
+ /* Set the mflag of the monster */
+ if (m_idx) m_list[m_idx].mflag |= letter[idx].mflag;
+
+ /* Object (and possible trap) */
+ if ((random & RANDOM_OBJECT) && (random & RANDOM_TRAP))
+ {
+ int level = object_level;
+
+ object_level = quest[p_ptr->inside_quest].level;
+
+ /*
+ * Random trap and random treasure defined
+ * 25% chance for trap and 75% chance for object
+ */
+ if (rand_int(100) < 75)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_SPECIAL);
+ }
+ else
+ {
+ place_trap(y, x);
+ }
+
+ object_level = level;
+ }
+ else if (random & RANDOM_OBJECT)
+ {
+ /* Create an out of deep object */
+ if (object_index)
+ {
+ int level = object_level;
+
+ object_level = quest[p_ptr->inside_quest].level + object_index;
+ if (rand_int(100) < 75)
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_SPECIAL);
+ else if (rand_int(100) < 80)
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_SPECIAL);
+ else
+ place_object(y, x, TRUE, TRUE, OBJ_FOUND_SPECIAL);
+
+ object_level = level;
+ }
+ else if (rand_int(100) < 75)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_SPECIAL);
+ }
+ else if (rand_int(100) < 80)
+ {
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_SPECIAL);
+ }
+ else
+ {
+ place_object(y, x, TRUE, TRUE, OBJ_FOUND_SPECIAL);
+ }
+ }
+ /* Random trap */
+ else if (random & RANDOM_TRAP)
+ {
+ place_trap(y, x);
+ }
+ else if (object_index)
+ {
+ /* Get local object */
+ object_type *o_ptr = &object_type_body;
+
+ k_allow_special[object_index] = TRUE;
+
+ /* Create the item */
+ object_prep(o_ptr, object_index);
+
+ /* Apply magic (no messages, no artifacts) */
+ apply_magic(o_ptr, dun_level, FALSE, TRUE, FALSE);
+
+ o_ptr->found = OBJ_FOUND_SPECIAL;
+
+ k_allow_special[object_index] = FALSE;
+
+ drop_near(o_ptr, -1, y, x);
+ }
+
+ /* Artifact */
+ if (artifact_index)
+ {
+ int I_kind = 0;
+
+ artifact_type *a_ptr = &a_info[artifact_index];
+
+ object_type forge;
+
+ /* Get local object */
+ object_type *q_ptr = &forge;
+
+ a_allow_special[artifact_index] = TRUE;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = artifact_index;
+
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+ q_ptr->found = OBJ_FOUND_SPECIAL;
+
+ random_artifact_resistance(q_ptr);
+
+ a_info[artifact_index].cur_num = 1;
+
+ a_allow_special[artifact_index] = FALSE;
+
+ /* It's amazing that this "creating objects anywhere"
+ junk ever worked.
+ Let's just HACK around one observed bug: Shadow Cloak
+ of Luthien [Globe of Light] */
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f5 & TR5_SPELL_CONTAIN)
+ q_ptr->pval2 = -1;
+ }
+
+ /* Drop the artifact */
+ drop_near(q_ptr, -1, y, x);
+
+ }
+
+ /* Terrain special */
+ if (letter[idx].special == -1)
+ {
+ if (!letter[idx].bx)
+ {
+ letter[idx].bx = x;
+ letter[idx].by = y;
+ }
+ else
+ {
+ c_ptr->special = (letter[idx].by << 8) + letter[idx].bx;
+ cave[letter[idx].by][letter[idx].bx].special = (y << 8) + x;
+ }
+ }
+ else
+ {
+ c_ptr->special = letter[idx].special;
+ }
+ }
+ if (full && (*xval < x)) *xval = x;
+ (*yval)++;
+
+ return (0);
+ }
+
+ /* Process "W:<command>: ..." -- info for the wilderness */
+ else if (buf[0] == 'W')
+ {
+ /* Process "W:D:<layout> */
+ /* Layout of the wilderness */
+ if (buf[2] == 'D')
+ {
+ int x;
+ char i;
+
+ /* Acquire the text */
+ char *s = buf + 4;
+
+ int y = *yval;
+
+ for (x = 0; x < max_wild_x; x++)
+ {
+ if (1 != sscanf(s + x, "%c", &i)) return (1);
+ wild_map[y][x].feat = wildc2i[(int)i];
+
+ /*
+ * If this is a town/dungeon entrance, note
+ * its coordinates. (Have to check for
+ * duplicate Morias...)
+ */
+ if (wf_info[wildc2i[(int)i]].entrance &&
+ wf_info[wildc2i[(int)i]].wild_x == 0)
+ {
+ wf_info[wildc2i[(int)i]].wild_x = x;
+ wf_info[wildc2i[(int)i]].wild_y = y;
+ }
+ }
+
+ (*yval)++;
+
+ return (0);
+ }
+ /* Process "M:<plus>:<line>" -- move line lines */
+ else if (buf[2] == 'M')
+ {
+ if (tokenize(buf + 4, 2, zz, ':', '/') == 2)
+ {
+ if (atoi(zz[0]))
+ {
+ (*yval) += atoi(zz[1]);
+ }
+ else
+ {
+ (*yval) -= atoi(zz[1]);
+ }
+ }
+ else
+ {
+ return (1);
+ }
+ return (0);
+ }
+ /* Process "W:P:<x>:<y> - starting position in the wilderness */
+ else if (buf[2] == 'P')
+ {
+ if ((p_ptr->wilderness_x == 0) &&
+ (p_ptr->wilderness_y == 0))
+ {
+ if (tokenize(buf + 4, 2, zz, ':', '/') == 2)
+ {
+ p_ptr->wilderness_x = atoi(zz[0]);
+ p_ptr->wilderness_y = atoi(zz[1]);
+ }
+ else
+ {
+ return (1);
+ }
+ }
+
+ return (0);
+ }
+ /* Process "W:E:<dungeon>:<y>:<x> - entrance to the dungeon <dungeon> */
+ else if (buf[2] == 'E')
+ {
+ if (tokenize(buf + 4, 3, zz, ':', '/') == 3)
+ {
+ wild_map[atoi(zz[1])][atoi(zz[2])].entrance = 1000 + atoi(zz[0]);
+ }
+ else
+ {
+ return (1);
+ }
+
+ return (0);
+ }
+ }
+
+ /* Process "P:<y>:<x>" -- player position */
+ else if (buf[0] == 'P')
+ {
+ if (init_flags & INIT_CREATE_DUNGEON)
+ {
+ if (tokenize(buf + 2, 2, zz, ':', '/') == 2)
+ {
+ /* Place player in a quest level */
+ if (p_ptr->inside_quest || (init_flags & INIT_POSITION))
+ {
+ p_ptr->py = atoi(zz[0]);
+ p_ptr->px = atoi(zz[1]);
+ }
+ /* Place player in the town */
+ else if ((p_ptr->oldpx == 0) && (p_ptr->oldpy == 0))
+ {
+ p_ptr->oldpy = atoi(zz[0]);
+ p_ptr->oldpx = atoi(zz[1]);
+ }
+ }
+ }
+
+ return (0);
+ }
+
+ /* Process "M:<type>:<maximum>" -- set maximum values */
+ else if (buf[0] == 'M')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') >= 2)
+ {
+ /* Maximum towns */
+ if (zz[0][0] == 'T')
+ {
+ max_towns = atoi(zz[1]);
+ }
+
+ /* Maximum real towns */
+ if (zz[0][0] == 't')
+ {
+ max_real_towns = atoi(zz[1]);
+ }
+
+ /* Maximum r_idx */
+ else if (zz[0][0] == 'R')
+ {
+ max_r_idx = atoi(zz[1]);
+ }
+
+ /* Maximum re_idx */
+ else if (zz[0][0] == 'r')
+ {
+ max_re_idx = atoi(zz[1]);
+ }
+
+ /* Maximum s_idx */
+ else if (zz[0][0] == 'k')
+ {
+ max_s_idx = atoi(zz[1]);
+ if (max_s_idx > MAX_SKILLS) return (1);
+ }
+
+ /* Maximum ab_idx */
+ else if (zz[0][0] == 'b')
+ {
+ max_ab_idx = atoi(zz[1]);
+ }
+
+ /* Maximum k_idx */
+ else if (zz[0][0] == 'K')
+ {
+ max_k_idx = atoi(zz[1]);
+ }
+
+ /* Maximum v_idx */
+ else if (zz[0][0] == 'V')
+ {
+ max_v_idx = atoi(zz[1]);
+ }
+
+ /* Maximum f_idx */
+ else if (zz[0][0] == 'F')
+ {
+ max_f_idx = atoi(zz[1]);
+ }
+
+ /* Maximum a_idx */
+ else if (zz[0][0] == 'A')
+ {
+ max_a_idx = atoi(zz[1]);
+ }
+
+ /* Maximum al_idx */
+ else if (zz[0][0] == 'a')
+ {
+ max_al_idx = atoi(zz[1]);
+ }
+
+ /* Maximum e_idx */
+ else if (zz[0][0] == 'E')
+ {
+ max_e_idx = atoi(zz[1]);
+ }
+
+ /* Maximum ra_idx */
+ else if (zz[0][0] == 'Z')
+ {
+ max_ra_idx = atoi(zz[1]);
+ }
+
+ /* Maximum o_idx */
+ else if (zz[0][0] == 'O')
+ {
+ max_o_idx = atoi(zz[1]);
+ }
+
+ /* Maximum player types */
+ else if (zz[0][0] == 'P')
+ {
+ if (zz[1][0] == 'R')
+ {
+ max_rp_idx = atoi(zz[2]);
+ }
+ else if (zz[1][0] == 'S')
+ {
+ max_rmp_idx = atoi(zz[2]);
+ }
+ else if (zz[1][0] == 'C')
+ {
+ max_c_idx = atoi(zz[2]);
+ }
+ else if (zz[1][0] == 'M')
+ {
+ max_mc_idx = atoi(zz[2]);
+ }
+ else if (zz[1][0] == 'H')
+ {
+ max_bg_idx = atoi(zz[2]);
+ }
+ }
+
+ /* Maximum m_idx */
+ else if (zz[0][0] == 'M')
+ {
+ max_m_idx = atoi(zz[1]);
+ }
+
+ /* Maximum tr_idx */
+ else if (zz[0][0] == 'U')
+ {
+ max_t_idx = atoi(zz[1]);
+ }
+
+ /* Maximum wf_idx */
+ else if (zz[0][0] == 'W')
+ {
+ max_wf_idx = atoi(zz[1]);
+ }
+
+ /* Maximum ba_idx */
+ else if (zz[0][0] == 'B')
+ {
+ max_ba_idx = atoi(zz[1]);
+ }
+
+ /* Maximum st_idx */
+ else if (zz[0][0] == 'S')
+ {
+ max_st_idx = atoi(zz[1]);
+ }
+
+ /* Maximum set_idx */
+ else if (zz[0][0] == 's')
+ {
+ max_set_idx = atoi(zz[1]);
+ }
+
+ /* Maximum ow_idx */
+ else if (zz[0][0] == 'N')
+ {
+ max_ow_idx = atoi(zz[1]);
+ }
+
+ /* Maximum wilderness x size */
+ else if (zz[0][0] == 'X')
+ {
+ max_wild_x = atoi(zz[1]);
+ }
+
+ /* Maximum wilderness y size */
+ else if (zz[0][0] == 'Y')
+ {
+ max_wild_y = atoi(zz[1]);
+ }
+
+ /* Maximum d_idx */
+ else if (zz[0][0] == 'D')
+ {
+ max_d_idx = atoi(zz[1]);
+ }
+
+ return (0);
+ }
+ }
+
+ /* Failure */
+ return (1);
+}
+
+
+
+
+/*
+ * Helper function for "process_dungeon_file()"
+ */
+static cptr process_dungeon_file_expr(char **sp, char *fp)
+{
+ cptr v;
+
+ char *b;
+ char *s;
+
+ char b1 = '[';
+ char b2 = ']';
+
+ char f = ' ';
+
+ /* Initial */
+ s = (*sp);
+
+ /* Skip spaces */
+ while (isspace(*s)) s++;
+
+ /* Save start */
+ b = s;
+
+ /* Default */
+ v = "?o?o?";
+
+ /* Analyze */
+ if (*s == b1)
+ {
+ const char *p;
+ const char *t;
+
+ /* Skip b1 */
+ s++;
+
+ /* First */
+ t = process_dungeon_file_expr(&s, &f);
+
+ /* Oops */
+ if (!*t)
+ {
+ /* Nothing */
+ }
+
+ /* Function: IOR */
+ else if (streq(t, "IOR"))
+ {
+ v = "0";
+ while (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && !streq(t, "0")) v = "1";
+ }
+ }
+
+ /* Function: AND */
+ else if (streq(t, "AND"))
+ {
+ v = "1";
+ while (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && streq(t, "0")) v = "0";
+ }
+ }
+
+ /* Function: NOT */
+ else if (streq(t, "NOT"))
+ {
+ v = "1";
+ while (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && streq(t, "1")) v = "0";
+ }
+ }
+
+ /* Function: EQU */
+ else if (streq(t, "EQU"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && !streq(p, t)) v = "0";
+ }
+ }
+
+ /* Function: LEQ */
+ else if (streq(t, "LEQ"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && (strcmp(p, t) > 0)) v = "0";
+ }
+ }
+
+ /* Function: GEQ */
+ else if (streq(t, "GEQ"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && (strcmp(p, t) < 0)) v = "0";
+ }
+ }
+
+ /* Oops */
+ else
+ {
+ while (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ }
+ }
+
+ /* Verify ending */
+ if (f != b2) v = "?x?x?";
+
+ /* Extract final and Terminate */
+ if ((f = *s) != '\0') * s++ = '\0';
+ }
+
+ /* Other */
+ else
+ {
+ bool_ text_mode = FALSE;
+
+ /* Accept all printables except spaces and brackets */
+ while (isprint(*s))
+ {
+ if (*s == '"') text_mode = !text_mode;
+ if (!text_mode)
+ {
+ if (strchr(" []", *s))
+ break;
+ }
+ else
+ {
+ if (strchr("[]", *s))
+ break;
+ }
+
+ ++s;
+ }
+
+ /* Extract final and Terminate */
+ if ((f = *s) != '\0') * s++ = '\0';
+
+ /* Variable */
+ if (*b == '$')
+ {
+ /* System */
+ if (streq(b + 1, "SYS"))
+ {
+ v = ANGBAND_SYS;
+ }
+
+ /* Graphics */
+ else if (streq(b + 1, "GRAF"))
+ {
+ v = ANGBAND_GRAF;
+ }
+
+ /* Race */
+ else if (streq(b + 1, "RACE"))
+ {
+ v = rp_ptr->title + rp_name;
+ }
+
+ /* Race Mod */
+ else if (streq(b + 1, "RACEMOD"))
+ {
+ v = rmp_ptr->title + rmp_name;
+ }
+
+ /* Class */
+ else if (streq(b + 1, "CLASS"))
+ {
+ v = cp_ptr->title + c_name;
+ }
+
+ /* Player */
+ else if (streq(b + 1, "PLAYER"))
+ {
+ v = player_base;
+ }
+
+ /* Town */
+ else if (streq(b + 1, "TOWN"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", p_ptr->town_num);
+ v = pref_tmp_value;
+ }
+
+ /* Town destroyed */
+ else if (prefix(b + 1, "TOWN_DESTROY"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", town_info[atoi(b + 13)].destroyed);
+ v = pref_tmp_value;
+ }
+
+ /* Current quest number */
+ else if (streq(b + 1, "QUEST_NUMBER"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", p_ptr->inside_quest);
+ v = pref_tmp_value;
+ }
+
+ /* Number of last quest */
+ else if (streq(b + 1, "LEAVING_QUEST"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", leaving_quest);
+ v = pref_tmp_value;
+ }
+
+ /* DAYTIME status */
+ else if (prefix(b + 1, "DAYTIME"))
+ {
+ if ((bst(HOUR, turn) >= 6) && (bst(HOUR, turn) < 18))
+ v = "1";
+ else
+ v = "0";
+ }
+
+ /* Quest status */
+ else if (prefix(b + 1, "QUEST"))
+ {
+ /* "QUEST" uses a special parameter to determine the number of the quest */
+ if (*(b + 6) != '"')
+ strnfmt(pref_tmp_value, 8, "%d", quest[atoi(b + 6)].status);
+ else
+ {
+ char c[80];
+ int i;
+
+ /* Copy it to temp array, so that we can modify it safely */
+ strcpy(c, b + 7);
+
+ /* Hunt & shoot the ending " */
+ for (i = 0; (c[i] != '"') && (c[i] != '\0'); i++);
+ if (c[i] == '"') c[i] = '\0';
+ strcpy(pref_tmp_value, "-1");
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (streq(c, quest[i].name))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", quest[i].status);
+ break;
+ }
+ }
+ }
+ v = pref_tmp_value;
+ }
+
+ /* Variant name */
+ else if (streq(b + 1, "VARIANT"))
+ {
+ v = "ToME";
+ }
+
+ /* Wilderness */
+ else if (streq(b + 1, "WILDERNESS"))
+ {
+ v = "NORMAL";
+ }
+ }
+
+ /* Constant */
+ else
+ {
+ v = b;
+ }
+ }
+
+ /* Save */
+ (*fp) = f;
+
+ /* Save */
+ (*sp) = s;
+
+ /* Result */
+ return (v);
+}
+
+
+errr process_dungeon_file(cptr name, int *yval, int *xval, int ymax, int xmax, bool_ init, bool_ full)
+{
+ FILE *fp = 0;
+
+ char buf[1024];
+
+ int num = -1, i;
+
+ errr err = 0;
+
+ bool_ bypass = FALSE;
+
+ /* Save the start since it ought to be modified */
+ int xmin = *xval;
+
+ if (init)
+ {
+ meta_sleep = TRUE;
+ for (i = 0; i < 255; i++)
+ {
+ letter[i].defined = FALSE;
+ if (i == ' ') letter[i].ok = TRUE;
+ else letter[i].ok = FALSE;
+ letter[i].bx = 0;
+ letter[i].by = 0;
+ }
+ }
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* No such file */
+ if (!fp)
+ {
+ msg_format("Cannot find file %s at %s", name, buf);
+ return ( -1);
+ }
+
+ /* Process the file */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Count lines */
+ num++;
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) continue;
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) continue;
+
+ /* Skip comments */
+ if (buf[0] == '#') continue;
+
+
+ /* Process "?:<expr>" */
+ if ((buf[0] == '?') && (buf[1] == ':'))
+ {
+ char f;
+ cptr v;
+ char *s;
+
+ /* Start */
+ s = buf + 2;
+
+ /* Parse the expr */
+ v = process_dungeon_file_expr(&s, &f);
+
+ /* Set flag */
+ bypass = (streq(v, "0") ? TRUE : FALSE);
+
+ /* Continue */
+ continue;
+ }
+
+ /* Apply conditionals */
+ if (bypass) continue;
+
+
+ /* Process "%:<file>" */
+ if (buf[0] == '%')
+ {
+ /* Process that file if allowed */
+ (void)process_dungeon_file(buf + 2, yval, xval, ymax, xmax, FALSE, full);
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Process the line */
+ err = process_dungeon_file_aux(buf, yval, xval, xmin, ymax, xmax, full);
+
+ /* Oops */
+ if (err) break;
+ }
+
+
+ /* Error */
+ if (err)
+ {
+ /* Useful error message */
+ msg_format("Error %d in line %d of file '%s'.", err, num, name);
+ msg_format("Parsing '%s'", buf);
+ }
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Result */
+ return (err);
+}
diff --git a/src/init2.c b/src/init2.c
new file mode 100644
index 00000000..5239426c
--- /dev/null
+++ b/src/init2.c
@@ -0,0 +1,2918 @@
+/* File: init2.c */
+
+/* Purpose: Initialisation (part 2) -BEN- */
+
+#include "angband.h"
+
+
+/*
+ * This file is used to initialise various variables and arrays for the
+ * Angband game. Note the use of "fd_read()" and "fd_write()" to bypass
+ * the common limitation of "read()" and "write()" to only 32767 bytes
+ * at a time.
+ *
+ * Several of the arrays for Angband are built from "template" files in
+ * the "lib/file" directory, from which quick-load binary "image" files
+ * are constructed whenever they are not present in the "lib/data"
+ * directory, or if those files become obsolete, if we are allowed.
+ *
+ * Warning -- the "ascii" file parsers use a minor hack to collect the
+ * name and text information in a single pass. Thus, the game will not
+ * be able to load any template file with more than 20K of names or 60K
+ * of text, even though technically, up to 64K should be legal.
+ *
+ * The "init1.c" file is used only to parse the ascii template files.
+ */
+
+
+
+/*
+ * Find the default paths to all of our important sub-directories.
+ *
+ * The purpose of each sub-directory is described in "variable.c".
+ *
+ * All of the sub-directories should, by default, be located inside
+ * the main "lib" directory, whose location is very system dependant.
+ *
+ * This function takes a writable buffer, initially containing the
+ * "path" to the "lib" directory, for example, "/pkg/lib/angband/",
+ * or a system dependant string, for example, ":lib:". The buffer
+ * must be large enough to contain at least 32 more characters.
+ *
+ * Various command line options may allow some of the important
+ * directories to be changed to user-specified directories, most
+ * importantly, the "info" and "user" and "save" directories,
+ * but this is done after this function, see "main.c".
+ *
+ * In general, the initial path should end in the appropriate "PATH_SEP"
+ * string. All of the "sub-directory" paths (created below or supplied
+ * by the user) will NOT end in the "PATH_SEP" string, see the special
+ * "path_build()" function in "util.c" for more information.
+ *
+ * Mega-Hack -- support fat raw files under NEXTSTEP, using special
+ * "suffixed" directories for the "ANGBAND_DIR_DATA" directory, but
+ * requiring the directories to be created by hand by the user.
+ *
+ * Hack -- first we free all the strings, since this is known
+ * to succeed even if the strings have not been allocated yet,
+ * as long as the variables start out as "NULL". This allows
+ * this function to be called multiple times, for example, to
+ * try several base "path" values until a good one is found.
+ */
+void init_file_paths(char *path)
+{
+ char *tail;
+ int pathlen;
+
+ /*** Free everything ***/
+
+ /* Free the main path */
+ string_free(ANGBAND_DIR);
+
+ /* Free the sub-paths */
+ string_free(ANGBAND_DIR_APEX);
+ string_free(ANGBAND_DIR_CORE);
+ string_free(ANGBAND_DIR_DNGN);
+ string_free(ANGBAND_DIR_DATA);
+ string_free(ANGBAND_DIR_EDIT);
+ string_free(ANGBAND_DIR_FILE);
+ string_free(ANGBAND_DIR_HELP);
+ string_free(ANGBAND_DIR_INFO);
+ string_free(ANGBAND_DIR_MODULES);
+ string_free(ANGBAND_DIR_NOTE);
+ string_free(ANGBAND_DIR_SAVE);
+ string_free(ANGBAND_DIR_SCPT);
+ string_free(ANGBAND_DIR_PREF);
+ string_free(ANGBAND_DIR_PATCH);
+ string_free(ANGBAND_DIR_USER);
+ string_free(ANGBAND_DIR_XTRA);
+ string_free(ANGBAND_DIR_CMOV);
+
+
+ /*** Prepare the "path" ***/
+
+ pathlen = strlen(path);
+
+ /* Hack -- save the main directory without trailing PATH_SEP if present */
+ if (strlen(PATH_SEP) > 0 && pathlen > 0)
+ {
+ int seplen = strlen(PATH_SEP);
+
+ if (strcmp(path + pathlen - seplen, PATH_SEP) == 0)
+ {
+ path[pathlen - seplen] = '\0';
+ ANGBAND_DIR = string_make(path);
+ path[pathlen - seplen] = *PATH_SEP;
+ }
+ else
+ {
+ ANGBAND_DIR = string_make(path);
+ }
+ }
+ else
+ {
+ ANGBAND_DIR = string_make(path);
+ }
+
+ /* Prepare to append to the Base Path */
+ tail = path + pathlen;
+
+
+
+ /*** Build the sub-directory names ***/
+
+ /* Build a path name */
+ strcpy(tail, "apex");
+ ANGBAND_DIR_APEX = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "core");
+ ANGBAND_DIR_CORE = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "dngn");
+ ANGBAND_DIR_DNGN = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "data");
+ ANGBAND_DIR_DATA = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "edit");
+ ANGBAND_DIR_EDIT = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "file");
+ ANGBAND_DIR_FILE = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "help");
+ ANGBAND_DIR_HELP = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "info");
+ ANGBAND_DIR_INFO = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "mods");
+ ANGBAND_DIR_MODULES = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "patch");
+ ANGBAND_DIR_PATCH = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "scpt");
+ ANGBAND_DIR_SCPT = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "pref");
+ ANGBAND_DIR_PREF = string_make(path);
+
+ /* synchronize with module_reset_dir */
+ {
+ char user_path[1024];
+
+ /* Get an absolute path from the file name */
+ path_parse(user_path, 1024, PRIVATE_USER_PATH);
+ strcat(user_path, USER_PATH_VERSION);
+ ANGBAND_DIR_USER = string_make(user_path);
+ ANGBAND_DIR_NOTE = string_make(user_path);
+ ANGBAND_DIR_CMOV = string_make(user_path);
+#ifdef PRIVATE_USER_PATH_MODULES
+ ANGBAND_DIR_MODULES = string_make(user_path);
+#endif
+#ifdef PRIVATE_USER_PATH_APEX
+ ANGBAND_DIR_APEX = string_make(user_path);
+#endif
+#ifdef PRIVATE_USER_PATH_DATA
+ {
+ char user_path_data[1024];
+ strcpy(user_path_data, user_path);
+ strcat(user_path_data, "/data");
+ ANGBAND_DIR_DATA = string_make(user_path_data);
+ }
+#endif
+
+ /* Savefiles are in user directory */
+ strcat(user_path, "/save");
+ ANGBAND_DIR_SAVE = string_make(user_path);
+ }
+
+ /* Build a path name */
+ strcpy(tail, "xtra");
+ ANGBAND_DIR_XTRA = string_make(path);
+
+#ifdef NeXT
+
+ /* Allow "fat binary" usage with NeXT */
+ if (TRUE)
+ {
+ cptr next = NULL;
+
+# if defined(m68k)
+ next = "m68k";
+# endif
+
+# if defined(i386)
+ next = "i386";
+# endif
+
+# if defined(sparc)
+ next = "sparc";
+# endif
+
+# if defined(hppa)
+ next = "hppa";
+# endif
+
+ /* Use special directory */
+ if (next)
+ {
+ /* Forget the old path name */
+ string_free(ANGBAND_DIR_DATA);
+
+ /* Build a new path name */
+ sprintf(tail, "data-%s", next);
+ ANGBAND_DIR_DATA = string_make(path);
+ }
+ }
+
+#endif /* NeXT */
+
+}
+
+
+/**
+ * Realloc the given character array.
+ */
+static void z_realloc(char **p, size_t n) {
+ /* realloc doesn't really support size 0, but we want to shrink the allocated area regardless. */
+ if (n == 0) {
+ n = 1;
+ }
+ /* do the reallocation */
+ *p = realloc(*p, n);
+ if (*p == NULL) {
+ quit("Error during realloc.");
+ }
+}
+
+/*
+ * Hack -- help give useful error messages
+ */
+s16b error_idx;
+s16b error_line;
+
+
+/*
+ * Hack -- help initialise the fake "name" and "text" arrays when
+ * parsing an "ascii" template file.
+ */
+u32b fake_name_size;
+u32b fake_text_size;
+
+
+/*
+ * Standard error message text
+ */
+static cptr err_str[9] =
+{
+ NULL,
+ "parse error",
+ "obsolete file",
+ "missing record header",
+ "non-sequential records",
+ "invalid flag specification",
+ "undefined directive",
+ "out of memory",
+ "invalid skill chart"
+};
+
+
+/*
+ * Hack -- take notes on line 23
+ */
+static void note(cptr str)
+{
+ Term_erase(0, 23, 255);
+ Term_putstr(20, 23, -1, TERM_WHITE, str);
+ Term_fresh();
+}
+
+
+
+/*
+ * Initialise the "f_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_f_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(f_head, header);
+
+ /* Save the "record" information */
+ f_head->info_num = max_f_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "f_name" and "f_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "f_info" array */
+ C_MAKE(f_info, f_head->info_num, feature_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(f_name, fake_name_size, char);
+ C_MAKE(f_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "f_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'f_info.txt' file.");
+
+ /* Parse the file */
+ err = init_f_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'f_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'f_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&f_name, f_head->name_size);
+ z_realloc(&f_text, f_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "k_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_k_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(k_head, header);
+
+ /* Save the "record" information */
+ k_head->info_num = max_k_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "k_name" and "k_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "k_info" array */
+ C_MAKE(k_info, k_head->info_num, object_kind);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(k_name, fake_name_size, char);
+ C_MAKE(k_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "k_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'k_info.txt' file.");
+
+ /* Parse the file */
+ err = init_k_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'k_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'k_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&k_name, k_head->name_size);
+ z_realloc(&k_text, k_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "set_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_set_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(set_head, header);
+
+ /* Save the "record" information */
+ set_head->info_num = max_set_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "set_name" and "set_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "set_info" array */
+ C_MAKE(set_info, set_head->info_num, set_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(set_name, fake_name_size, char);
+ C_MAKE(set_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "set_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'set_info.txt' file.");
+
+ /* Parse the file */
+ err = init_set_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'set_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'set_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&set_name, set_head->name_size);
+ z_realloc(&set_text, set_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "a_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_a_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(a_head, header);
+
+ /* Save the "record" information */
+ a_head->info_num = max_a_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "a_name" and "a_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "a_info" array */
+ C_MAKE(a_info, a_head->info_num, artifact_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(a_name, fake_name_size, char);
+ C_MAKE(a_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "a_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'a_info.txt' file.");
+
+ /* Parse the file */
+ err = init_a_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'a_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'a_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&a_name, a_head->name_size);
+ z_realloc(&a_text, a_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "s_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_s_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(s_head, header);
+
+ /* Save the "record" information */
+ s_head->info_num = max_s_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "a_name" and "a_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "s_info" array */
+ C_MAKE(s_info, s_head->info_num, skill_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(s_name, fake_name_size, char);
+ C_MAKE(s_text, fake_text_size, char);
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "s_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 's_info.txt' file.");
+
+ /* Parse the file */
+ err = init_s_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 's_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 's_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&s_name, s_head->name_size);
+ z_realloc(&s_text, s_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "ab_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_ab_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(ab_head, header);
+
+ /* Save the "record" information */
+ ab_head->info_num = max_ab_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "a_name" and "a_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "ab_info" array */
+ C_MAKE(ab_info, ab_head->info_num, ability_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(ab_name, fake_name_size, char);
+ C_MAKE(ab_text, fake_text_size, char);
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "ab_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'ab_info.txt' file.");
+
+ /* Parse the file */
+ err = init_ab_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'ab_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'ab_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&ab_name, ab_head->name_size);
+ z_realloc(&ab_text, ab_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "e_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_e_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(e_head, header);
+
+ /* Save the "record" information */
+ e_head->info_num = max_e_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "e_name" and "e_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "e_info" array */
+ C_MAKE(e_info, e_head->info_num, ego_item_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(e_name, fake_name_size, char);
+ C_MAKE(e_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "e_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'e_info.txt' file.");
+
+ /* Parse the file */
+ err = init_e_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'e_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'e_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&e_name, e_head->name_size);
+ z_realloc(&e_text, e_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "ra_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_ra_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(ra_head, header);
+
+ /* Save the "record" information */
+ ra_head->info_num = max_ra_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "ra_name" and "ra_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "ra_info" array */
+ C_MAKE(ra_info, ra_head->info_num, randart_part_type);
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "ra_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'ra_info.txt' file.");
+
+ /* Parse the file */
+ err = init_ra_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'ra_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'ra_info.txt' file.");
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "r_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_r_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(r_head, header);
+
+ /* Save the "record" information */
+ r_head->info_num = max_r_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "r_name" and "r_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "r_info" array */
+ C_MAKE(r_info, r_head->info_num, monster_race);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(r_name, fake_name_size, char);
+ C_MAKE(r_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "r_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'r_info.txt' file.");
+
+ /* Parse the file */
+ err = init_r_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'r_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'r_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&r_name, r_head->name_size);
+ z_realloc(&r_text, r_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "re_info" array
+ *
+ * Note that we let each entry have a unique "name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_re_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(re_head, header);
+
+ /* Save the "record" information */
+ re_head->info_num = max_re_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "re_name" */
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "re_info" array */
+ C_MAKE(re_info, re_head->info_num, monster_ego);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(re_name, fake_name_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "re_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 're_info.txt' file.");
+
+ /* Parse the file */
+ err = init_re_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 're_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 're_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&re_name, re_head->name_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "d_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_d_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(d_head, header);
+
+ /* Save the "record" information */
+ d_head->info_num = max_d_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "d_name" and "d_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "d_info" array */
+ C_MAKE(d_info, d_head->info_num, dungeon_info_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(d_name, fake_name_size, char);
+ C_MAKE(d_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "d_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'd_info.txt' file.");
+
+ /* Parse the file */
+ err = init_d_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'd_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'd_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&d_name, d_head->name_size);
+ z_realloc(&d_text, d_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "player" arrays
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_player_info(void)
+{
+ int i;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(rp_head, header);
+
+ /* Save the "record" information */
+ rp_head->info_num = max_rp_idx;
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(rmp_head, header);
+
+ /* Save the "record" information */
+ rmp_head->info_num = max_rmp_idx;
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(c_head, header);
+
+ /* Save the "record" information */
+ c_head->info_num = max_c_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "rp_name" and "rp_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "rp_info" array */
+ C_MAKE(race_info, rp_head->info_num, player_race);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(rp_name, fake_name_size, char);
+ C_MAKE(rp_text, fake_text_size, char);
+
+ /* Allocate the "rmp_info" array */
+ C_MAKE(race_mod_info, rmp_head->info_num, player_race_mod);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(rmp_name, fake_name_size, char);
+ C_MAKE(rmp_text, fake_text_size, char);
+
+ /* Allocate the "c_info" array */
+ C_MAKE(class_info, c_head->info_num, player_class);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(c_name, fake_name_size, char);
+ C_MAKE(c_text, fake_text_size, char);
+
+ /* Allocate the "bg" array */
+ C_MAKE(bg, max_bg_idx, hist_type);
+
+ /* Allocate the "meta_class" array */
+ C_MAKE(meta_class_info, max_mc_idx, meta_class_type);
+ for (i = 0; i < max_mc_idx; i++)
+ {
+ C_MAKE(meta_class_info[i].classes, max_c_idx, s16b);
+ }
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "p_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'p_info.txt' file.");
+
+ /* Parse the file */
+ err = init_player_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'p_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'p_info.txt' file.");
+ }
+
+ /* Reallocate arrays. */
+ z_realloc(&rp_name, rp_head->name_size);
+ z_realloc(&rp_text, rp_head->text_size);
+ z_realloc(&rmp_name, rmp_head->name_size);
+ z_realloc(&rmp_text, rmp_head->text_size);
+ z_realloc(&c_name, c_head->name_size);
+ z_realloc(&c_text, c_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "st_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_st_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(st_head, header);
+
+ /* Save the "record" information */
+ st_head->info_num = max_st_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "st_name" and "st_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "st_info" array */
+ C_MAKE(st_info, st_head->info_num, store_info_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(st_name, fake_name_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "st_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'st_info.txt' file.");
+
+ /* Parse the file */
+ err = init_st_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'st_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'st_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&st_name, st_head->name_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "ow_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_ow_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(ow_head, header);
+
+ /* Save the "record" information */
+ ow_head->info_num = max_ow_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "ow_name" and "ow_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "ow_info" array */
+ C_MAKE(ow_info, ow_head->info_num, owner_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(ow_name, fake_name_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "ow_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'ow_info.txt' file.");
+
+ /* Parse the file */
+ err = init_ow_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'ow_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'ow_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&ow_name, ow_head->name_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "ba_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_ba_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(ba_head, header);
+
+ /* Save the "record" information */
+ ba_head->info_num = max_ba_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "ba_name" and "ba_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "ba_info" array */
+ C_MAKE(ba_info, ba_head->info_num, store_action_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(ba_name, fake_name_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "ba_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'ba_info.txt' file.");
+
+ /* Parse the file */
+ err = init_ba_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'ba_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'ba_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&ba_name, ba_head->name_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "wf_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_wf_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(wf_head, header);
+
+ /* Save the "record" information */
+ wf_head->info_num = max_wf_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "wf_name" and "wf_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "r_info" array */
+ C_MAKE(wf_info, wf_head->info_num, wilderness_type_info);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(wf_name, fake_name_size, char);
+ C_MAKE(wf_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "wf_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'wf_info.txt' file.");
+
+ /* Parse the file */
+ err = init_wf_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'wf_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'wf_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&wf_name, wf_head->name_size);
+ z_realloc(&wf_text, wf_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "t_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_t_info(void)
+{
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(t_head, header);
+
+ /* Save the "record" information */
+ t_head->info_num = max_t_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "t_name" and "t_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "t_info" array */
+ C_MAKE(t_info, t_head->info_num, trap_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(t_name, fake_name_size, char);
+ C_MAKE(t_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "tr_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'tr_info.txt' file.");
+
+ /* Parse the file */
+ err = init_t_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'tr_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'tr_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&t_name, t_head->name_size);
+ z_realloc(&t_text, t_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "al_info" array
+ *
+ * Not a flat array, but an array none the less
+ */
+errr init_al_info(void)
+{
+ errr err;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(al_head, header);
+
+ /* Save the "record" information */
+ al_head->info_num = max_al_idx;
+
+
+
+
+ fake_text_size = FAKE_TEXT_SIZE;
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "al_info" array */
+ C_MAKE(alchemist_recipes, al_head->info_num, alchemist_recipe);
+
+ /* Allocate the fake arrays */
+ /* ok, so we fudge a bit, but
+ fake text size will ALWAYS be larger
+ than 32*5*sizeof(artifact_select_flag) = 10 int and 5 bytes
+ which is the maximum size of the a_select_flags array
+ */
+ C_MAKE(al_name, fake_name_size, char);
+
+ {
+ char *hack;
+ C_MAKE(hack, fake_text_size, char);
+ a_select_flags = (artifact_select_flag *) hack;
+ }
+
+ /*** Load the ascii template file ***/
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "al_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'al_info.txt' file.");
+
+ /* Parse the file */
+ err = init_al_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'al_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'al_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&al_name, al_head->name_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "v_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+errr init_v_info(void)
+{
+ errr err;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(v_head, header);
+
+ /* Save the "record" information */
+ v_head->info_num = max_v_idx;
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "v_name" and "v_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "k_info" array */
+ C_MAKE(v_info, v_head->info_num, vault_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(v_name, fake_name_size, char);
+ C_MAKE(v_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "v_info.txt");
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'v_info.txt' file.");
+
+ /* Parse the file */
+ err = init_v_info_txt(fp, buf, TRUE);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'v_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'v_info.txt' file.");
+ }
+
+ /* Reduce sizes of the arrays */
+ z_realloc(&v_name, v_head->name_size);
+ z_realloc(&v_text, v_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the very basic arrays
+ */
+static void init_basic()
+{
+ int i;
+
+ /* Macro variables */
+ C_MAKE(macro__pat, MACRO_MAX, cptr);
+ C_MAKE(macro__act, MACRO_MAX, cptr);
+ C_MAKE(macro__cmd, MACRO_MAX, bool_);
+
+ /* Macro action buffer */
+ C_MAKE(macro__buf, 1024, char);
+
+ /* Extended trigger macros */
+ C_MAKE(cli_info, CLI_MAX, cli_comm);
+
+ /* Wipe the directory list */
+ for (i = 0; i < 255; i++)
+ {
+ scansubdir_result[i] = NULL;
+ }
+}
+
+/*
+ * Pseudo, dummy quest initializer, to actualy disable them
+ */
+static bool_ quest_disable_init_hook(int q_idx)
+{
+ q_idx = q_idx;
+ return FALSE;
+}
+
+
+/*
+ * Initialise misc. values
+ */
+static errr init_misc(void)
+{
+ int xstart = 0;
+ int ystart = 0;
+ int i;
+ s32b allow_quest;
+ s32b allow_rquest;
+
+ /*** Prepare the various "bizarre" arrays ***/
+
+ /* Quark variables */
+ C_MAKE(quark__str, QUARK_MAX, cptr);
+
+ /* Message variables */
+ C_MAKE(message__ptr, MESSAGE_MAX, u16b);
+ C_MAKE(message__color, MESSAGE_MAX, byte);
+ C_MAKE(message__type, MESSAGE_MAX, byte);
+ C_MAKE(message__count, MESSAGE_MAX, u16b);
+ C_MAKE(message__buf, MESSAGE_BUF, char);
+
+ /* Hack -- No messages yet */
+ message__tail = MESSAGE_BUF;
+
+ /* Prepare powers */
+ p_ptr->powers = NULL;
+ powers_type = NULL;
+ power_max = POWER_MAX_INIT;
+ reinit_powers_type(power_max);
+ C_COPY(powers_type, powers_type_init, POWER_MAX_INIT, power_type);
+
+ /* Prepare quests */
+ call_lua("get_module_info", "(s)", "d", "C_quest", &allow_quest);
+ call_lua("get_module_info", "(s)", "d", "rand_quest", &allow_rquest);
+
+ quest = NULL;
+ max_q_idx = MAX_Q_IDX_INIT;
+ reinit_quests(max_q_idx);
+
+ C_COPY(quest, quest_init_tome, MAX_Q_IDX_INIT, quest_type);
+
+ /* If we dont allow C quests, we dont let them init */
+ if (!allow_quest)
+ {
+ for (i = 0; i < MAX_Q_IDX_INIT; i++)
+ {
+ if (allow_rquest && (i == QUEST_RANDOM))
+ continue;
+ quest[i].init = quest_disable_init_hook;
+ }
+ }
+
+ /* Prepare gods */
+ deity_info = NULL;
+ max_gods = MAX_GODS_INIT;
+ reinit_gods(max_gods);
+
+ C_COPY(deity_info, deity_info_init, MAX_GODS_INIT, deity_type);
+
+ /* Prepare schools */
+ max_spells = 0;
+ max_schools = 0;
+ schools = NULL;
+ school_spells = NULL;
+
+ process_hooks(HOOK_INIT_GAME, "(s)", "begin");
+
+ /* Initialise the values */
+ process_dungeon_file("misc.txt", &ystart, &xstart, 0, 0, TRUE, FALSE);
+
+ /* Init the spell effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ effects[i].time = 0;
+
+ return 0;
+}
+
+
+/*
+ * Initialise town array
+ */
+static errr init_towns(void)
+{
+ int i = 0, j = 0;
+
+ /*** Prepare the Towns ***/
+
+ /* Allocate the towns */
+ C_MAKE(town_info, max_towns, town_type);
+
+ for (i = 1; i < max_towns; i++)
+ {
+ if (i <= max_real_towns) town_info[i].flags |= (TOWN_REAL);
+
+ /* Allocate the stores */
+ C_MAKE(town_info[i].store, max_st_idx, store_type);
+
+ /* Fill in each store */
+ for (j = 0; j < max_st_idx; j++)
+ {
+ /* Access the store */
+ store_type *st_ptr = &town_info[i].store[j];
+
+ /* Know who we are */
+ st_ptr->st_idx = j;
+
+ /* Assume full stock */
+ st_ptr->stock_size = 0;
+ }
+ }
+ return 0;
+}
+
+void create_stores_stock(int t)
+{
+ int j;
+ town_type *t_ptr = &town_info[t];
+
+ if (t_ptr->stocked) return;
+
+ for (j = 0; j < max_st_idx; j++)
+ {
+ store_type *st_ptr = &t_ptr->store[j];
+
+ /* Assume full stock */
+ st_ptr->stock_size = st_info[j].max_obj;
+
+ /* Allocate the stock */
+ C_MAKE(st_ptr->stock, st_ptr->stock_size, object_type);
+ }
+ t_ptr->stocked = TRUE;
+}
+
+/*
+ * Pointer to wilderness_map
+ */
+typedef wilderness_map *wilderness_map_ptr;
+
+/*
+ * Initialise wilderness map array
+ */
+static errr init_wilderness(void)
+{
+ int i;
+
+ /* Allocate the wilderness (two-dimension array) */
+ C_MAKE(wild_map, max_wild_y, wilderness_map_ptr);
+ C_MAKE(wild_map[0], max_wild_x * max_wild_y, wilderness_map);
+
+ /* Init the other pointers */
+ for (i = 1; i < max_wild_y; i++)
+ wild_map[i] = wild_map[0] + i * max_wild_x;
+
+ /* No encounter right now */
+ generate_encounter = FALSE;
+
+ return 0;
+}
+
+/*
+ * XXX XXX XXX XXX XXX Realloc is not guaranteed to work (see main-gtk.c
+ * and main-mac.c.
+ */
+void reinit_powers_type(s16b new_size)
+{
+ power_type *new_powers_type;
+ bool_ *new_powers;
+
+ C_MAKE(new_powers_type, new_size, power_type);
+ C_MAKE(new_powers, new_size, bool_);
+
+ /* Reallocate the extra memory */
+ if (powers_type && p_ptr->powers)
+ {
+ C_COPY(new_powers_type, powers_type, power_max, power_type);
+ C_COPY(new_powers, p_ptr->powers, power_max, bool_);
+
+ C_FREE(powers_type, power_max, power_type);
+ C_FREE(p_ptr->powers, power_max, bool_);
+ }
+
+ powers_type = new_powers_type;
+ p_ptr->powers = new_powers;
+
+ power_max = new_size;
+}
+
+void reinit_quests(s16b new_size)
+{
+ quest_type *new_quest;
+
+ C_MAKE(new_quest, new_size, quest_type);
+
+ /* Reallocate the extra memory */
+ if (quest)
+ {
+ C_COPY(new_quest, quest, max_q_idx, quest_type);
+
+ C_FREE(quest, max_q_idx, quest_type);
+ }
+
+ quest = new_quest;
+
+ max_q_idx = new_size;
+}
+
+void reinit_gods(s16b new_size)
+{
+ deity_type *new_deity;
+
+ C_MAKE(new_deity, new_size, deity_type);
+
+ /* Reallocate the extra memory */
+ if (deity_info)
+ {
+ C_COPY(new_deity, deity_info, max_gods, deity_type);
+
+ C_FREE(deity_info, max_gods, deity_type);
+ }
+
+ deity_info = new_deity;
+
+ max_gods = new_size;
+}
+
+void init_spells(s16b new_size)
+{
+ /* allocate the extra memory */
+ C_MAKE(school_spells, new_size, spell_type);
+ max_spells = new_size;
+}
+
+void init_schools(s16b new_size)
+{
+ /* allocate the extra memory */
+ C_MAKE(schools, new_size, school_type);
+ max_schools = new_size;
+}
+
+void init_corruptions(s16b new_size)
+{
+ /* allocate the extra memory */
+ C_MAKE(p_ptr->corruptions, new_size, bool_);
+ max_corruptions = new_size;
+}
+
+/*
+ * Initialise some other arrays
+ */
+static errr init_other(void)
+{
+ int i, n;
+
+ /*** Prepare the "dungeon" information ***/
+
+ /* Allocate and Wipe the special gene flags */
+ C_MAKE(m_allow_special, max_r_idx, bool_);
+ C_MAKE(k_allow_special, max_k_idx, bool_);
+ C_MAKE(a_allow_special, max_a_idx, bool_);
+
+
+ /*** Prepare "vinfo" array ***/
+
+ /* Used by "update_view()" */
+ (void)vinfo_init();
+
+
+ /* Allocate and Wipe the object list */
+ C_MAKE(o_list, max_o_idx, object_type);
+
+ /* Allocate and Wipe the monster list */
+ C_MAKE(m_list, max_m_idx, monster_type);
+
+ /* Allocate and Wipe the to keep monster list */
+ C_MAKE(km_list, max_m_idx, monster_type);
+
+ /* Allocate and Wipe the max dungeon level */
+ C_MAKE(max_dlv, max_d_idx, s16b);
+
+ /* Allocate and Wipe the special levels */
+ for (i = 0; i < MAX_DUNGEON_DEPTH; i++)
+ {
+ C_MAKE(special_lvl[i], max_d_idx, bool_);
+ }
+
+ /* Allocate and wipe each line of the cave */
+ for (i = 0; i < MAX_HGT; i++)
+ {
+ /* Allocate one row of the cave */
+ C_MAKE(cave[i], MAX_WID, cave_type);
+ }
+
+ /*** Pre-allocate the basic "auto-inscriptions" ***/
+
+ /* The "basic" feelings */
+ (void)quark_add("cursed");
+ (void)quark_add("broken");
+ (void)quark_add("average");
+ (void)quark_add("good");
+
+ /* The "extra" feelings */
+ (void)quark_add("excellent");
+ (void)quark_add("worthless");
+ (void)quark_add("special");
+ (void)quark_add("terrible");
+
+ /* Some extra strings */
+ (void)quark_add("uncursed");
+ (void)quark_add("on sale");
+
+
+ /*** Prepare the options ***/
+
+ /* Scan the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ int os = option_info[i].o_page;
+ int ob = option_info[i].o_bit;
+
+ /* Set the "default" options */
+ if (option_info[i].o_var)
+ {
+ /* Accept */
+ option_mask[os] |= (1L << ob);
+
+ /* Set */
+ if (option_info[i].o_norm)
+ {
+ /* Set */
+ option_flag[os] |= (1L << ob);
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ option_flag[os] &= ~(1L << ob);
+ }
+ }
+ }
+
+ /* Analyze the windows */
+ for (n = 0; n < 8; n++)
+ {
+ /* Analyze the options */
+ for (i = 0; i < 32; i++)
+ {
+ /* Accept */
+ if (window_flag_desc[i])
+ {
+ /* Accept */
+ window_mask[n] |= (1L << i);
+ }
+ }
+ }
+
+
+ /*
+ * Install the various level generators
+ */
+ add_level_generator("dungeon", level_generate_dungeon, TRUE, TRUE, TRUE, TRUE);
+ add_level_generator("maze", level_generate_maze, TRUE, TRUE, TRUE, TRUE);
+ add_level_generator("life", level_generate_life, TRUE, TRUE, TRUE, TRUE);
+
+ /*** Pre-allocate space for the "format()" buffer ***/
+
+ /* Hack -- Just call the "format()" function */
+ (void)format("%s (%s).", "Dark God <darkgod@t-o-m-e.net>", MAINTAINER);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise some other arrays
+ */
+static errr init_alloc(void)
+{
+ int i, j;
+
+ object_kind *k_ptr;
+
+ monster_race *r_ptr;
+
+ alloc_entry *table;
+
+ s16b num[MAX_DEPTH_MONSTER];
+
+ s16b aux[MAX_DEPTH_MONSTER];
+
+ /*** Analyze object allocation info ***/
+
+ /* Clear the "aux" array */
+ C_WIPE(&aux, MAX_DEPTH_MONSTER, s16b);
+
+ /* Clear the "num" array */
+ C_WIPE(&num, MAX_DEPTH_MONSTER, s16b);
+
+ /* Size of "alloc_kind_table" */
+ alloc_kind_size = 0;
+
+ /* Scan the objects */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ k_ptr = &k_info[i];
+
+ /* Scan allocation pairs */
+ for (j = 0; j < 4; j++)
+ {
+ /* Count the "legal" entries */
+ if (k_ptr->chance[j])
+ {
+ /* Count the entries */
+ alloc_kind_size++;
+
+ /* Group by level */
+ num[k_ptr->locale[j]]++;
+ }
+ }
+ }
+
+ /* Collect the level indexes */
+ for (i = 1; i < MAX_DEPTH_MONSTER; i++)
+ {
+ /* Group by level */
+ num[i] += num[i - 1];
+ }
+
+ /* Paranoia */
+ if (!num[0]) quit("No town objects!");
+
+
+ /*** Initialise object allocation info ***/
+
+ /* Allocate the alloc_kind_table */
+ C_MAKE(alloc_kind_table, alloc_kind_size, alloc_entry);
+
+ /* Access the table entry */
+ table = alloc_kind_table;
+
+ /* Scan the objects */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ k_ptr = &k_info[i];
+
+ /* Scan allocation pairs */
+ for (j = 0; j < 4; j++)
+ {
+ /* Count the "legal" entries */
+ if (k_ptr->chance[j])
+ {
+ int p, x, y, z;
+
+ /* Extract the base level */
+ x = k_ptr->locale[j];
+
+ /* Extract the base probability */
+ p = (100 / k_ptr->chance[j]);
+
+ /* Skip entries preceding our locale */
+ y = (x > 0) ? num[x - 1] : 0;
+
+ /* Skip previous entries at this locale */
+ z = y + aux[x];
+
+ /* Load the entry */
+ table[z].index = i;
+ table[z].level = x;
+ table[z].prob1 = p;
+ table[z].prob2 = p;
+ table[z].prob3 = p;
+
+ /* Another entry complete for this locale */
+ aux[x]++;
+ }
+ }
+ }
+
+
+ /*** Analyze monster allocation info ***/
+
+ /* Clear the "aux" array */
+ C_WIPE(&aux, MAX_DEPTH_MONSTER, s16b);
+
+ /* Clear the "num" array */
+ C_WIPE(&num, MAX_DEPTH_MONSTER, s16b);
+
+ /* Size of "alloc_race_table" */
+ alloc_race_size = 0;
+
+ /* Scan the monsters */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ /* Get the i'th race */
+ r_ptr = &r_info[i];
+
+ /* Legal monsters */
+ if (r_ptr->rarity)
+ {
+ /* Count the entries */
+ alloc_race_size++;
+
+ /* Group by level */
+ num[r_ptr->level]++;
+ }
+ }
+
+ /* Collect the level indexes */
+ for (i = 1; i < MAX_DEPTH_MONSTER; i++)
+ {
+ /* Group by level */
+ num[i] += num[i - 1];
+ }
+
+ /* Paranoia */
+ if (!num[0]) quit("No town monsters!");
+
+
+ /*** Initialise monster allocation info ***/
+
+ /* Allocate the alloc_race_table */
+ C_MAKE(alloc_race_table, alloc_race_size, alloc_entry);
+
+ /* Access the table entry */
+ table = alloc_race_table;
+
+ /* Scan the monsters */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ /* Get the i'th race */
+ r_ptr = &r_info[i];
+
+ /* Count valid pairs */
+ if (r_ptr->rarity)
+ {
+ int p, x, y, z;
+
+ /* Extract the base level */
+ x = r_ptr->level;
+
+ /* Extract the base probability */
+ p = (100 / r_ptr->rarity);
+
+ /* Skip entries preceding our locale */
+ y = (x > 0) ? num[x - 1] : 0;
+
+ /* Skip previous entries at this locale */
+ z = y + aux[x];
+
+ /* Load the entry */
+ table[z].index = i;
+ table[z].level = x;
+ table[z].prob1 = p;
+ table[z].prob2 = p;
+ table[z].prob3 = p;
+
+ /* Another entry complete for this locale */
+ aux[x]++;
+ }
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+/* Init the sets in a_info */
+void init_sets_aux()
+{
+ int i, j;
+
+ for (i = 0; i < max_a_idx; i++)
+ a_info[i].set = -1;
+ for (i = 0; i < max_set_idx; i++)
+ {
+ for (j = 0; j < set_info[i].num; j++)
+ {
+ a_info[set_info[i].arts[j].a_idx].set = i;
+ }
+ }
+}
+
+/*
+ * Mark guardians and their artifacts with SPECIAL_GENE flag
+ */
+static void init_guardians(void)
+{
+ int i;
+
+ /* Scan dungeons */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ dungeon_info_type *d_ptr = &d_info[i];
+
+ /* Mark the guadian monster */
+ if (d_ptr->final_guardian)
+ {
+ monster_race *r_ptr = &r_info[d_ptr->final_guardian];
+
+ r_ptr->flags9 |= RF9_SPECIAL_GENE;
+
+ /* Mark the final artifact */
+ if (d_ptr->final_artifact)
+ {
+ artifact_type *a_ptr = &a_info[d_ptr->final_artifact];
+
+ a_ptr->flags4 |= TR4_SPECIAL_GENE;
+ }
+
+ /* Mark the final object */
+ if (d_ptr->final_object)
+ {
+ object_kind *k_ptr = &k_info[d_ptr->final_object];
+
+ k_ptr->flags4 |= TR4_SPECIAL_GENE;
+ }
+
+ /* Give randart if there are no final artifacts */
+ if (!(d_ptr->final_artifact) && !(d_ptr->final_object))
+ {
+ r_ptr->flags7 |= RF7_DROP_RANDART;
+ }
+ }
+ }
+}
+
+/*
+ * Hack -- Explain a broken "lib" folder and quit (see below).
+ *
+ * XXX XXX XXX This function is "messy" because various things
+ * may or may not be initialised, but the "plog()" and "quit()"
+ * functions are "supposed" to work under any conditions.
+ */
+static void init_angband_aux(cptr why)
+{
+ /* Why */
+ plog(why);
+
+ /* Explain */
+ plog("The 'lib' directory is probably missing or broken.");
+
+ /* More details */
+ plog("Perhaps the archive was not extracted correctly.");
+
+ /* Explain */
+ plog("See the 'README' file for more information.");
+
+ /* Quit with error */
+ quit("Fatal Error.");
+}
+
+/*
+ * Hack -- main Angband initialisation entry point
+ *
+ * Verify some files, display the "news.txt" file, create
+ * the high score file, initialise all internal arrays, and
+ * load the basic "user pref files".
+ *
+ * Note that we blindly assume that "news2.txt" exists. XXX
+ *
+ * Be very careful to keep track of the order in which things
+ * are initialised, in particular, the only thing *known* to
+ * be available when this function is called is the "z-term.c"
+ * package, and that may not be fully initialised until the
+ * end of this function, when the default "user pref files"
+ * are loaded and "Term_xtra(TERM_XTRA_REACT,0)" is called.
+ *
+ * Note that this function attempts to verify the "news" file,
+ * and the game aborts (cleanly) on failure, since without the
+ * "news" file, it is likely that the "lib" folder has not been
+ * correctly located. Otherwise, the news file is displayed for
+ * the user.
+ *
+ * Note that this function attempts to verify (or create) the
+ * "high score" file, and the game aborts (cleanly) on failure,
+ * since one of the most common "extraction" failures involves
+ * failing to extract all sub-directories (even empty ones), such
+ * as by failing to use the "-d" option of "pkunzip", or failing
+ * to use the "save empty directories" option with "Compact Pro".
+ * This error will often be caught by the "high score" creation
+ * code below, since the "lib/apex" directory, being empty in the
+ * standard distributions, is most likely to be "lost", making it
+ * impossible to create the high score file.
+ *
+ * Note that various things are initialised by this function,
+ * including everything that was once done by "init_some_arrays".
+ *
+ * This initialisation involves the parsing of special files
+ * in the "lib/data" and sometimes the "lib/edit" directories.
+ *
+ * Note that the "template" files are initialised first, since they
+ * often contain errors. This means that macros and message recall
+ * and things like that are not available until after they are done.
+ *
+ * We load the default "user pref files" here in case any "color"
+ * changes are needed before character creation.
+ *
+ * Note that the "graf-xxx.prf" file must be loaded separately,
+ * if needed, in the first (?) pass through "TERM_XTRA_REACT".
+ */
+void init_angband(void)
+{
+ int fd = -1;
+
+ int mode = FILE_MODE;
+
+ FILE *fp;
+
+ char *news_file;
+
+ char buf[1024];
+
+ /* Init some VERY basic stuff, like macro arrays */
+ init_basic();
+
+ /* Select & init a module if needed */
+ select_module();
+
+ /*** Choose which news.txt file to use ***/
+
+ /* Choose the news file */
+ switch (time(NULL) % 2)
+ {
+ default:
+ {
+ news_file = "news.txt";
+ break;
+ }
+
+ case 0:
+ {
+ news_file = "news2.txt";
+ break;
+ }
+ }
+
+ /*** Verify the "news" file ***/
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, news_file);
+
+ /* Attempt to open the file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Failure */
+ if (fd < 0)
+ {
+ char why[1024];
+
+ /* Message */
+ sprintf(why, "Cannot access the '%s' file!", buf);
+
+ /* Crash and burn */
+ init_angband_aux(why);
+ }
+
+ /* Close it */
+ (void)fd_close(fd);
+
+
+ /*** Display the "news" file ***/
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, news_file);
+
+ /* Open the News file */
+ fp = my_fopen(buf, "r");
+
+ /* Dump */
+ if (fp)
+ {
+ int i = 0;
+
+ /* Dump the file to the screen */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Display and advance - we use display_message to parse colour codes XXX */
+ display_message(0, i++, strlen(buf), TERM_WHITE, buf);
+ }
+
+ /* Close */
+ my_fclose(fp);
+ }
+
+ /* Flush it */
+ Term_fresh();
+
+
+ /*** Verify (or create) the "high score" file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Attempt to open the high score file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Failure */
+ if (fd < 0)
+ {
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Create a new high score file */
+ fd = fd_make(buf, mode);
+
+ /* Failure */
+ if (fd < 0)
+ {
+ char why[1024];
+
+ /* Message */
+ sprintf(why, "Cannot create the '%s' file!", buf);
+
+ /* Crash and burn */
+ init_angband_aux(why);
+ }
+ }
+
+ /* Close it */
+ (void)fd_close(fd);
+
+
+ /*** Initialise some arrays ***/
+
+ /* Initialise misc. values */
+ note("[Initialising values... (misc)]");
+ if (init_misc()) quit("Cannot initialise misc. values");
+
+ wipe_hooks();
+
+ /* Initialise some other arrays */
+ note("[Initialising lua scripting... (lua)]");
+ init_lua();
+ init_lua_init();
+
+ /* Initialise skills info */
+ note("[Initialising arrays... (skills)]");
+ if (init_s_info()) quit("Cannot initialise skills");
+
+ /* Initialise abilities info */
+ note("[Initialising arrays... (abilities)]");
+ if (init_ab_info()) quit("Cannot initialise abilities");
+
+ /* Initialise alchemy info */
+ note("[Initialising arrays... (alchemy)]");
+ if (init_al_info()) quit("Cannot initialise alchemy");
+
+ /* Initialise player info */
+ note("[Initialising arrays... (players)]");
+ if (init_player_info()) quit("Cannot initialise players");
+
+ /* Initialise feature info */
+ note("[Initialising arrays... (features)]");
+ if (init_f_info()) quit("Cannot initialise features");
+
+ /* Initialise object info */
+ note("[Initialising arrays... (objects)]");
+ if (init_k_info()) quit("Cannot initialise objects");
+
+ /* Initialise artifact info */
+ note("[Initialising arrays... (artifacts)]");
+ if (init_a_info()) quit("Cannot initialise artifacts");
+
+ /* Initialise set info */
+ note("[Initialising item sets... (sets)]");
+ if (init_set_info()) quit("Cannot initialise item sets");
+ init_sets_aux();
+
+ /* Initialise ego-item info */
+ note("[Initialising arrays... (ego-items)]");
+ if (init_e_info()) quit("Cannot initialise ego-items");
+
+ /* Initialise randart parts info */
+ note("[Initialising arrays... (randarts)]");
+ if (init_ra_info()) quit("Cannot initialise randarts");
+
+ /* Initialise monster info */
+ note("[Initialising arrays... (monsters)]");
+ if (init_r_info()) quit("Cannot initialise monsters");
+
+ /* Initialise ego monster info */
+ note("[Initialising arrays... (ego monsters)]");
+ if (init_re_info()) quit("Cannot initialise ego monsters");
+
+ /* Initialise dungeon type info */
+ note("[Initialising arrays... (dungeon types)]");
+ if (init_d_info()) quit("Cannot initialise dungeon types");
+ init_guardians();
+
+ /* Initialise actions type info */
+ note("[Initialising arrays... (action types)]");
+ if (init_ba_info()) quit("Cannot initialise action types");
+
+ /* Initialise owners type info */
+ note("[Initialising arrays... (owners types)]");
+ if (init_ow_info()) quit("Cannot initialise owners types");
+
+ /* Initialise stores type info */
+ note("[Initialising arrays... (stores types)]");
+ if (init_st_info()) quit("Cannot initialise stores types");
+
+ /* Initialise wilderness features array */
+ note("[Initialising arrays... (wilderness features)]");
+ if (init_wf_info()) quit("Cannot initialise wilderness features");
+
+ /* Initialise wilderness map array */
+ note("[Initialising arrays... (wilderness map)]");
+ if (init_wilderness()) quit("Cannot initialise wilderness map");
+
+ /* Initialise town array */
+ note("[Initialising arrays... (towns)]");
+ if (init_towns()) quit("Cannot initialise towns");
+
+ /* Initialise trap info */
+ note("[Initialising arrays... (traps)]");
+ if (init_t_info()) quit("Cannot initialise traps");
+
+ /* Initialise some other arrays */
+ note("[Initialising arrays... (other)]");
+ if (init_other()) quit("Cannot initialise other stuff");
+
+ /* Initialise some other arrays */
+ note("[Initialising arrays... (alloc)]");
+ if (init_alloc()) quit("Cannot initialise alloc stuff");
+
+ /* Init random artifact names */
+ build_prob(artifact_names_list);
+
+ /*** Load default user pref files ***/
+
+ /* Initialise feature info */
+ note("[Initialising user pref files...]");
+
+ /* Access the "basic" pref file */
+ strcpy(buf, "pref.prf");
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "basic" system pref file */
+ sprintf(buf, "pref-%s.prf", ANGBAND_SYS);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "user" pref file */
+ sprintf(buf, "user.prf");
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "user" system pref file */
+ sprintf(buf, "user-%s.prf", ANGBAND_SYS);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Initialise the automatizer */
+ tome_dofile_anywhere(ANGBAND_DIR_CORE, "auto.lua", TRUE);
+ tome_dofile_anywhere(ANGBAND_DIR_USER, "automat.atm", FALSE);
+
+ /* Done */
+ note("[Initialisation complete]");
+
+ process_hooks(HOOK_INIT_GAME, "(s)", "end");
+}
diff --git a/src/iso/.cvsignore b/src/iso/.cvsignore
new file mode 100644
index 00000000..f8d9fd47
--- /dev/null
+++ b/src/iso/.cvsignore
@@ -0,0 +1 @@
+.sconsign
diff --git a/src/lauxlib.h b/src/lauxlib.h
new file mode 100644
index 00000000..8b6db343
--- /dev/null
+++ b/src/lauxlib.h
@@ -0,0 +1,100 @@
+/*
+** $Id: lauxlib.h,v 1.4 2002/01/04 03:31:23 pelpel Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lua.h"
+
+
+#ifndef LUALIB_API
+#define LUALIB_API extern
+#endif
+
+
+struct luaL_reg {
+ const char *name;
+ lua_CFunction func;
+};
+
+
+LUALIB_API void luaL_openlib (lua_State *L, const struct luaL_reg *l, int n);
+LUALIB_API void luaL_argerror (lua_State *L, int numarg, const char *extramsg);
+LUALIB_API const char *luaL_check_lstr (lua_State *L, int numArg, size_t *len);
+LUALIB_API const char *luaL_opt_lstr (lua_State *L, int numArg, const char *def, size_t *len);
+LUALIB_API long luaL_check_number (lua_State *L, int numArg);
+LUALIB_API long luaL_opt_number (lua_State *L, int numArg, long def);
+
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg);
+LUALIB_API void luaL_checktype (lua_State *L, int narg, int t);
+LUALIB_API void luaL_checkany (lua_State *L, int narg);
+
+LUALIB_API void luaL_verror (lua_State *L, const char *fmt, ...);
+LUALIB_API int luaL_findstring (const char *name, const char *const list[]);
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define luaL_arg_check(L, cond,numarg,extramsg) if (!(cond)) \
+ luaL_argerror(L, numarg,extramsg)
+#define luaL_check_string(L,n) (luaL_check_lstr(L, (n), NULL))
+#define luaL_opt_string(L,n,d) (luaL_opt_lstr(L, (n), (d), NULL))
+#define luaL_check_int(L,n) ((int)luaL_check_number(L, n))
+#define luaL_check_long(L,n) ((long)luaL_check_number(L, n))
+#define luaL_opt_int(L,n,d) ((int)luaL_opt_number(L, n,d))
+#define luaL_opt_long(L,n,d) ((long)luaL_opt_number(L, n,d))
+#define luaL_openl(L,a) luaL_openlib(L, a, (sizeof(a)/sizeof(a[0])))
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+#ifndef LUAL_BUFFERSIZE
+#define LUAL_BUFFERSIZE BUFSIZ
+#endif
+
+
+typedef struct luaL_Buffer {
+ char *p; /* current position in buffer */
+ int level;
+ lua_State *L;
+ char buffer[LUAL_BUFFERSIZE];
+} luaL_Buffer;
+
+#define luaL_putchar(B,c) \
+ ((void)((B)->p < &(B)->buffer[LUAL_BUFFERSIZE] || luaL_prepbuffer(B)), \
+ (*(B)->p++ = (char)(c)))
+
+#define luaL_addsize(B,n) ((B)->p += (n))
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B);
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s);
+LUALIB_API void luaL_addvalue (luaL_Buffer *B);
+LUALIB_API void luaL_pushresult (luaL_Buffer *B);
+
+
+/* }====================================================== */
+
+
+#endif
+
+
diff --git a/src/levels.c b/src/levels.c
new file mode 100644
index 00000000..71148fc5
--- /dev/null
+++ b/src/levels.c
@@ -0,0 +1,234 @@
+/* File: levels.c */
+
+/* Purpose: Levels functions */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Return the parameter of the given command in the given file
+ */
+static int start_line = 0;
+bool_ get_command(const char *file, char comm, char *param)
+{
+ char buf[1024];
+ int i = -1;
+ FILE *fp;
+ char *s;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DNGN, file);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* The file exists ? */
+ /* no ? then command not found */
+ if (!fp) return FALSE;
+
+ /* Parse to the end of the file or when the command is found */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Advance the line number */
+ i++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Is it the command we are looking for ? */
+ if ((i > start_line) && (buf[0] == comm))
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ start_line = i;
+
+ /* Get the parameter */
+ strcpy(param, s);
+
+ /* Close it */
+ my_fclose(fp);
+
+ return TRUE;
+ }
+
+ }
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Assume command not found */
+ return FALSE;
+}
+
+
+/*
+ * Return the dungeon branch starting form the current dungeon/level
+ */
+int get_branch()
+{
+ char file[20], buf[5];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the branch */
+ start_line = -1;
+ if (get_command(file, 'B', buf)) return (atoi(buf));
+
+ /* No branch ? return 0 */
+ else return 0;
+}
+
+/*
+ * Return the father dungeon branch
+ */
+int get_fbranch()
+{
+ char file[20], buf[5];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the branch */
+ start_line = -1;
+ if (get_command(file, 'A', buf)) return (atoi(buf));
+
+ /* No branch ? return 0 */
+ else return 0;
+}
+
+/*
+ * Return the father dungeon level
+ */
+int get_flevel()
+{
+ char file[20], buf[5];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'L', buf)) return (atoi(buf));
+
+ /* No level ? return 0 */
+ else return 0;
+}
+
+/*
+ * Return the extension of the savefile for the level
+ */
+bool_ get_dungeon_save(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'S', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the level generator
+ */
+bool_ get_dungeon_generator(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'G', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the special level
+ */
+bool_ get_dungeon_special(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'U', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the special level name
+ */
+bool_ get_dungeon_name(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'N', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the special level name
+ */
+void get_level_flags()
+{
+ char file[20];
+ char buf[1024], *s, *t;
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ start_line = -1;
+
+ /* Parse until done */
+ while (get_command(file, 'F', buf))
+ {
+ /* Parse every entry textually */
+ for (s = buf; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_dungeon_flag(&dungeon_flags1, &dungeon_flags2, s)) return;
+
+ /* Start the next entry */
+ s = t;
+ }
+ }
+}
+
+/*
+ * Return the special level desc
+ */
+bool_ get_level_desc(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'D', buf)) return (TRUE);
+ else return FALSE;
+}
diff --git a/src/loadsave.c b/src/loadsave.c
new file mode 100644
index 00000000..8cdb9f72
--- /dev/null
+++ b/src/loadsave.c
@@ -0,0 +1,3288 @@
+/* File: loadsave.c */
+
+/* Purpose: interact with savefiles. This file was made by
+ unifying load2.c and save.c from the old codebase. Doing it
+ this way makes maintenance easier and lets us share code. */
+
+#include "angband.h"
+
+static void do_byte(byte *, int);
+static void do_u16b(u16b *, int);
+static void do_s16b(s16b *, int);
+static void do_u32b(u32b *, int);
+static void do_s32b(s32b *, int);
+static void do_string(char *, int, int);
+static void do_lore(int, int);
+static void do_monster(monster_type *, int);
+static void do_randomizer(int flag);
+static void do_spells(int, int);
+static void note(cptr);
+static void do_fate(int, int);
+static void do_item(object_type *, int);
+static void do_options(int);
+static bool_ do_store(store_type *, int);
+static void do_messages(int flag);
+static void do_xtra(int, int);
+static bool_ do_savefile_aux(int);
+static void junkinit(void);
+static void morejunk(void);
+static bool_ do_inventory(int);
+static bool_ do_dungeon(int, bool_);
+static void do_grid(int);
+static void my_sentinel(char *, u16b, int);
+
+static void do_ver_s16b(s16b *, u32b, s16b, int);
+
+static void skip_ver_byte(u32b, int);
+
+errr rd_savefile(void);
+
+static FILE *fff; /* Local savefile ptr */
+
+/*
+ * Basic byte-level reading from savefile. This provides a single point
+ * of interface to the pseudoencryption that ToME (and Angband)
+ * uses. I'm thinking about if it might be faster/better to modify all
+ * the do_* functions to directly do this stuff -- it'd make the code
+ * somewhat uglier to maintain, but concievably might be much faster. Or
+ * is it better maybe to scrap the pseudoencryption entirely and adopt
+ * some other means of obfuscation, should it still prove useful in any
+ * way? -- Improv
+ *
+ * What's the point of encryption on savefiles anyway? If I wanted to
+ * make a cheater savefile, I'd activate debug mode, and hack the game
+ * not to save it. There's no point. -- takkaria
+ */
+
+static byte sf_get(void)
+{
+ byte c;
+
+ /* Get a character, decode the value */
+ c = getc(fff) & 0xFF;
+
+ /* Return the value */
+ return (c);
+}
+
+
+static void sf_put(byte v)
+{
+ (void)putc((int)v, fff);
+}
+
+/*
+ * Do object memory and similar stuff
+ */
+static void do_xtra(int k_idx, int flag)
+{
+ byte tmp8u = 0;
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (flag == LS_SAVE)
+ {
+ if (k_ptr->aware) tmp8u |= 0x01;
+ if (k_ptr->tried) tmp8u |= 0x02;
+ if (k_ptr->know) tmp8u |= 0x04;
+ if (k_ptr->artifact) tmp8u |= 0x80;
+
+ do_byte(&tmp8u, flag);
+ }
+ if (flag == LS_LOAD)
+ {
+ do_byte(&tmp8u, flag);
+ k_ptr->aware = ((tmp8u & 0x01) ? TRUE : FALSE);
+ k_ptr->tried = ((tmp8u & 0x02) ? TRUE : FALSE);
+ k_ptr->know = ((tmp8u & 0x04) ? TRUE : FALSE);
+ k_ptr->artifact = ((tmp8u & 0x80) ? TRUE : FALSE);
+ }
+}
+
+/*
+ * Load/Save quick start data
+ */
+void do_quick_start(int flag)
+{
+ int i;
+
+ do_s16b(&previous_char.sex, flag);
+ do_s16b(&previous_char.race, flag);
+ do_s16b(&previous_char.rmod, flag);
+ do_s16b(&previous_char.pclass, flag);
+ do_s16b(&previous_char.spec, flag);
+ do_byte(&previous_char.quests, flag);
+ do_byte(&previous_char.god, flag);
+ do_s32b(&previous_char.grace, flag);
+ do_s16b(&previous_char.age, flag);
+ do_s16b(&previous_char.wt, flag);
+ do_s16b(&previous_char.ht, flag);
+ do_s16b(&previous_char.sc, flag);
+ do_s32b(&previous_char.au, flag);
+
+ for (i = 0; i < 6; i++) do_s16b(&(previous_char.stat[i]), flag);
+ do_s16b(&previous_char.luck, flag);
+
+ do_s16b(&previous_char.chaos_patron, flag);
+ do_u32b(&previous_char.weapon, flag);
+ do_byte((byte*)&previous_char.quick_ok, flag);
+
+ for (i = 0; i < 4; i++) do_string(previous_char.history[i], 60, flag);
+}
+
+/*
+ * The special saved subrace
+ */
+static void do_subrace(int flag)
+{
+ player_race_mod *sr_ptr = &race_mod_info[SUBRACE_SAVE];
+ int i;
+ char buf[81];
+
+ if (flag == LS_SAVE)
+ strncpy(buf, sr_ptr->title + rmp_name, 80);
+ do_string(buf, 80, flag);
+ if (flag == LS_LOAD)
+ strncpy(sr_ptr->title + rmp_name, buf, 80);
+
+ if (flag == LS_SAVE)
+ strncpy(buf, sr_ptr->desc + rmp_text, 80);
+ do_string(buf, 80, flag);
+ if (flag == LS_LOAD)
+ strncpy(sr_ptr->desc + rmp_text, buf, 80);
+
+ do_byte((byte*)&sr_ptr->place, flag);
+
+ for (i = 0; i < 6; i++)
+ do_s16b(&sr_ptr->r_adj[i], flag);
+
+ do_byte((byte*)&sr_ptr->luck, flag);
+ do_s16b(&sr_ptr->mana, flag);
+
+ do_s16b(&sr_ptr->r_dis, flag);
+ do_s16b(&sr_ptr->r_dev, flag);
+ do_s16b(&sr_ptr->r_sav, flag);
+ do_s16b(&sr_ptr->r_stl, flag);
+ do_s16b(&sr_ptr->r_srh, flag);
+ do_s16b(&sr_ptr->r_fos, flag);
+ do_s16b(&sr_ptr->r_thn, flag);
+ do_s16b(&sr_ptr->r_thb, flag);
+
+ do_byte((byte*)&sr_ptr->r_mhp, flag);
+ do_s16b(&sr_ptr->r_exp, flag);
+
+ do_byte((byte*)&sr_ptr->b_age, flag);
+ do_byte((byte*)&sr_ptr->m_age, flag);
+
+ do_byte((byte*)&sr_ptr->m_b_ht, flag);
+ do_byte((byte*)&sr_ptr->m_m_ht, flag);
+ do_byte((byte*)&sr_ptr->f_b_ht, flag);
+ do_byte((byte*)&sr_ptr->f_m_ht, flag);
+
+ do_byte((byte*)&sr_ptr->m_b_wt, flag);
+ do_byte((byte*)&sr_ptr->m_m_wt, flag);
+ do_byte((byte*)&sr_ptr->f_b_wt, flag);
+ do_byte((byte*)&sr_ptr->f_m_wt, flag);
+
+ do_byte((byte*)&sr_ptr->infra, flag);
+
+ for (i = 0; i < 4; i++)
+ do_s16b(&sr_ptr->powers[i], flag);
+
+ for (i = 0; i < BODY_MAX; i++)
+ do_byte((byte*)&sr_ptr->body_parts[i], flag);
+
+ do_u32b(&sr_ptr->flags1, flag);
+ do_u32b(&sr_ptr->flags2, flag);
+
+ for (i = 0; i < PY_MAX_LEVEL + 1; i++)
+ {
+ do_u32b(&sr_ptr->oflags1[i], flag);
+ do_u32b(&sr_ptr->oflags2[i], flag);
+ do_u32b(&sr_ptr->oflags3[i], flag);
+ do_u32b(&sr_ptr->oflags4[i], flag);
+ do_u32b(&sr_ptr->oflags5[i], flag);
+ do_u32b(&sr_ptr->oesp[i], flag);
+ do_s16b(&sr_ptr->opval[i], flag);
+ }
+
+ do_byte(&sr_ptr->g_attr, flag);
+ do_byte((byte*)&sr_ptr->g_char, flag);
+
+ for (i = 0; i < MAX_SKILLS; i++)
+ {
+ do_byte((byte*)&sr_ptr->skill_basem[i], flag);
+ do_u32b(&sr_ptr->skill_base[i], flag);
+ do_byte((byte*)&sr_ptr->skill_modm[i], flag);
+ do_s16b(&sr_ptr->skill_mod[i], flag);
+ }
+}
+
+/*
+ * Misc. other data
+ */
+static char loaded_game_module[80];
+static bool_ do_extra(int flag)
+{
+ int i, j;
+ byte tmp8u;
+ s16b tmp16s;
+ u32b tmp32u;
+ u16b tmp16b;
+ u32b dummy32u = 0;
+
+ do_string(player_name, 32, flag);
+
+ do_string(died_from, 80, flag);
+
+ for (i = 0; i < 4; i++)
+ {
+ do_string(history[i], 60, flag);
+ }
+
+ /* Handle the special levels info */
+ if (flag == LS_SAVE)
+ {
+ tmp8u = max_d_idx;
+ tmp16s = MAX_DUNGEON_DEPTH;
+ }
+ do_byte(&tmp8u, flag);
+
+ if (flag == LS_LOAD)
+ {
+ if (tmp8u > max_d_idx)
+ {
+ note(format("Too many (%d) dungeon types!", tmp8u));
+ }
+ }
+
+ do_s16b(&tmp16s, flag);
+
+ if (flag == LS_LOAD)
+ {
+ if (tmp16s > MAX_DUNGEON_DEPTH)
+ {
+ note(format("Too many (%d) max level by dungeon type!", tmp16s));
+ }
+ }
+
+ /* Load the special levels history */
+ for (i = 0; i < tmp8u; i++)
+ {
+ for (j = 0; j < tmp16s; j++)
+ {
+ do_byte((byte*)&special_lvl[j][i], flag);
+ }
+ }
+
+ do_byte((byte*)&generate_special_feeling, flag);
+
+ /* Load the quick start data */
+ do_quick_start(flag);
+
+ /* Load/save the special subrace */
+ do_subrace(flag);
+
+ /* Race/Class/Gender/Spells */
+ do_s32b(&p_ptr->lives, flag);
+ do_byte(&p_ptr->prace, flag);
+ do_byte(&p_ptr->pracem, flag);
+ do_byte(&p_ptr->pclass, flag);
+ do_byte(&p_ptr->pspec, flag);
+ do_byte(&p_ptr->psex, flag);
+ do_u16b(&tmp16b, flag);
+ do_u16b(&tmp16b, flag);
+ do_byte(&p_ptr->mimic_form, flag);
+ do_s16b(&p_ptr->mimic_level, flag);
+ if (flag == LS_SAVE) tmp8u = 0;
+
+ do_byte(&p_ptr->hitdie, flag);
+ do_u16b(&p_ptr->expfact, flag);
+
+ do_s16b(&p_ptr->age, flag);
+ do_s16b(&p_ptr->ht, flag);
+ do_s16b(&p_ptr->wt, flag);
+
+ /* Dump the stats (maximum and current) */
+ for (i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_max[i], flag);
+ for (i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_cur[i], flag);
+ for (i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_cnt[i], flag);
+ for (i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_los[i], flag);
+
+ /* Dump the skills */
+ do_s16b(&p_ptr->skill_points, flag);
+ do_s16b(&p_ptr->skill_last_level, flag);
+ do_s16b(&p_ptr->melee_style, flag);
+ do_s16b(&p_ptr->use_piercing_shots, flag);
+
+ tmp16s = MAX_SKILLS;
+ do_s16b(&tmp16s, flag);
+
+ if ((flag == LS_LOAD) && (tmp16s > MAX_SKILLS))
+ {
+ quit("Too many skills");
+ }
+
+ if (flag == LS_SAVE) old_max_s_idx = max_s_idx;
+ do_u16b(&old_max_s_idx, flag);
+ for (i = 0; i < tmp16s; ++i)
+ {
+ if (i < old_max_s_idx)
+ {
+ do_s32b(&s_info[i].value, flag);
+ do_s32b(&s_info[i].mod, flag);
+ do_byte((byte*)&s_info[i].dev, flag);
+ do_byte((byte*)&s_info[i].hidden, flag);
+ do_u32b(&s_info[i].uses, flag);
+ }
+ else
+ {
+ do_u32b(&tmp32u, flag);
+ do_s16b(&tmp16s, flag);
+ do_byte(&tmp8u, flag);
+ do_byte(&tmp8u, flag);
+ do_u32b(&tmp32u, flag);
+ }
+ }
+
+ tmp16s = max_ab_idx;
+ do_s16b(&tmp16s, flag);
+
+ if ((flag == LS_LOAD) && (tmp16s > max_ab_idx))
+ {
+ quit("Too many abilities");
+ }
+
+ for (i = 0; i < tmp16s; ++i)
+ {
+ do_byte((byte*)&ab_info[i].acquired, flag);
+ }
+
+ do_s16b(&p_ptr->luck_base, flag);
+ do_s16b(&p_ptr->luck_max, flag);
+
+ /* Found 24 unused bytes here...
+ Converted it to be the alchemist's
+ known artifact flags.
+ Note that the ego flags and the gained levels
+ record are recorded here too, but we use the
+ _ver_ format to protect save file compatablity.
+ Note that the other alchemist knowledge (item types known)
+ is stored in do_aux, and is a bit flag in a previously
+ unused bit.
+ */
+ for (i = 0; i < 6 ; ++i)
+ do_u32b(&alchemist_known_artifacts[i], flag);
+
+ for (i = 0; i < 32 ; ++i)
+ do_u32b(&alchemist_known_egos[i], flag);
+
+ do_u32b(&alchemist_gained, flag);
+
+ do_s32b(&p_ptr->au, flag);
+
+ do_s32b(&p_ptr->max_exp, flag);
+ do_s32b(&p_ptr->exp, flag);
+ do_u16b(&p_ptr->exp_frac, flag);
+ do_s16b(&p_ptr->lev, flag);
+
+ do_s16b(&p_ptr->town_num, flag); /* -KMW- */
+
+ /* Write arena and rewards information -KMW- */
+ do_s16b(&p_ptr->arena_number, flag);
+ do_s16b(&p_ptr->inside_arena, flag);
+ do_s16b(&p_ptr->inside_quest, flag);
+ do_byte((byte*)&p_ptr->exit_bldg, flag);
+
+
+ /* Save/load spellbinder */
+ do_byte(&p_ptr->spellbinder_num, flag);
+ do_byte(&p_ptr->spellbinder_trigger, flag);
+ for (i = 0; i < 4; i++)
+ do_u32b(&p_ptr->spellbinder[i], flag);
+
+
+ do_byte(&tmp8u, flag); /* tmp8u should be 0 at this point */
+
+ if (flag == LS_SAVE) tmp8u = MAX_PLOTS;
+ do_byte(&tmp8u, flag);
+
+ if ((flag == LS_LOAD) && (tmp8u > MAX_PLOTS))
+ {
+ quit(format("Too many plots, %d %d", tmp8u, MAX_PLOTS));
+ }
+
+ for (i = 0; i < tmp8u; i++)
+ {
+ do_s16b(&plots[i], flag);
+ }
+
+ if (flag == LS_SAVE)
+ {
+ tmp8u = MAX_RANDOM_QUEST;
+ }
+ do_byte(&tmp8u, flag);
+
+ if ((flag == LS_LOAD) &&
+ (tmp8u > MAX_RANDOM_QUEST)) quit("Too many random quests");
+ for (i = 0; i < tmp8u; i++)
+ {
+ do_byte(&random_quests[i].type, flag);
+ do_s16b(&random_quests[i].r_idx, flag);
+ do_byte((byte*)&random_quests[i].done, flag);
+ }
+
+ do_s16b(&p_ptr->oldpx, flag);
+ do_s16b(&p_ptr->oldpy, flag);
+
+ do_s16b(&p_ptr->mhp, flag);
+ do_s16b(&p_ptr->chp, flag);
+ do_u16b(&p_ptr->chp_frac, flag);
+ do_s16b(&p_ptr->hp_mod, flag);
+
+ do_s16b(&p_ptr->msane, flag);
+ do_s16b(&p_ptr->csane, flag);
+ do_u16b(&p_ptr->csane_frac, flag);
+
+ do_s16b(&p_ptr->msp, flag);
+ do_s16b(&p_ptr->csp, flag);
+ do_u16b(&p_ptr->csp_frac, flag);
+
+ /* XXX
+ Here's where tank points were.
+ Those who run the estate of you-know-who is really stupid.
+ I'll never even consider reading her books now. -- neil */
+ do_s16b(&tmp16s, flag);
+ do_s16b(&tmp16s, flag);
+ do_s16b(&tmp16s, flag);
+ do_s16b(&tmp16s, flag);
+
+ /* Gods */
+ do_s32b(&p_ptr->grace, flag);
+ do_byte((byte*)&p_ptr->praying, flag);
+ do_s16b(&p_ptr->melkor_sacrifice, flag);
+ do_byte(&p_ptr->pgod, flag);
+
+ /* Max Player and Dungeon Levels */
+ do_s16b(&p_ptr->max_plv, flag);
+
+ if (flag == LS_SAVE)
+ tmp8u = max_d_idx;
+ do_byte(&tmp8u, flag);
+ for (i = 0; i < tmp8u; i++)
+ {
+ if (flag == LS_SAVE)
+ tmp16s = max_dlv[i];
+ do_s16b(&tmp16s, flag);
+ if ((flag == LS_LOAD) && (i <= max_d_idx))
+ max_dlv[i] = tmp16s;
+ }
+ /* Repair max player level??? */
+ if ((flag == LS_LOAD) && (p_ptr->max_plv < p_ptr->lev))
+ p_ptr->max_plv = p_ptr->lev;
+
+ do_byte((byte*)&(p_ptr->help.enabled), flag);
+ do_u32b(&(p_ptr->help.help1), flag);
+
+ /* More info */
+ tmp16s = 0;
+ do_s16b(&p_ptr->sc, flag);
+ do_s16b(&p_ptr->blind, flag);
+ do_s16b(&p_ptr->paralyzed, flag);
+ do_s16b(&p_ptr->confused, flag);
+ do_s16b(&p_ptr->food, flag);
+ do_s32b(&p_ptr->energy, flag);
+ do_s16b(&p_ptr->fast, flag);
+ do_s16b(&p_ptr->speed_factor, flag);
+ do_s16b(&p_ptr->slow, flag);
+ do_s16b(&p_ptr->afraid, flag);
+ do_s16b(&p_ptr->cut, flag);
+ do_s16b(&p_ptr->stun, flag);
+ do_s16b(&p_ptr->poisoned, flag);
+ do_s16b(&p_ptr->image, flag);
+ do_s16b(&p_ptr->protevil, flag);
+ do_s16b(&p_ptr->protundead, flag);
+ do_s16b(&p_ptr->invuln, flag);
+ do_s16b(&p_ptr->hero, flag);
+ do_s16b(&p_ptr->shero, flag);
+ do_s16b(&p_ptr->shield, flag);
+ do_s16b(&p_ptr->shield_power, flag);
+ do_s16b(&p_ptr->shield_power_opt, flag);
+ do_s16b(&p_ptr->shield_power_opt2, flag);
+ do_s16b(&p_ptr->shield_opt, flag);
+ do_s16b(&p_ptr->blessed, flag);
+ do_s16b(&p_ptr->control, flag);
+ do_byte(&p_ptr->control_dir, flag);
+ do_s16b(&p_ptr->tim_thunder, flag);
+ do_s16b(&p_ptr->tim_thunder_p1, flag);
+ do_s16b(&p_ptr->tim_thunder_p2, flag);
+ do_s16b(&p_ptr->tim_project, flag);
+ do_s16b(&p_ptr->tim_project_dam, flag);
+ do_s16b(&p_ptr->tim_project_gf, flag);
+ do_s16b(&p_ptr->tim_project_rad, flag);
+ do_s16b(&p_ptr->tim_project_flag, flag);
+
+ do_s16b(&p_ptr->tim_magic_breath, flag);
+ do_s16b(&p_ptr->tim_water_breath, flag);
+
+ do_s16b(&p_ptr->tim_roots, flag);
+ do_s16b(&p_ptr->tim_roots_ac, flag);
+ do_s16b(&p_ptr->tim_roots_dam, flag);
+
+ do_s16b(&p_ptr->tim_invis, flag);
+ do_s16b(&p_ptr->word_recall, flag);
+ do_s16b(&p_ptr->recall_dungeon, flag);
+ do_s16b(&p_ptr->see_infra, flag);
+ do_s16b(&p_ptr->tim_infra, flag);
+ do_s16b(&p_ptr->oppose_fire, flag);
+ do_s16b(&p_ptr->oppose_cold, flag);
+ do_s16b(&p_ptr->oppose_acid, flag);
+ do_s16b(&p_ptr->oppose_elec, flag);
+ do_s16b(&p_ptr->oppose_pois, flag);
+ do_s16b(&p_ptr->oppose_ld, flag);
+ do_s16b(&p_ptr->oppose_cc, flag);
+ do_s16b(&p_ptr->oppose_ss, flag);
+ do_s16b(&p_ptr->oppose_nex, flag);
+
+ do_s16b(&p_ptr->tim_esp, flag);
+ do_s16b(&p_ptr->tim_wraith, flag);
+ do_s16b(&p_ptr->tim_ffall, flag);
+ do_ver_s16b(&p_ptr->tim_fly, SAVEFILE_VERSION, 0, flag);
+ do_s16b(&p_ptr->tim_fire_aura, flag);
+ do_ver_s16b(&p_ptr->tim_poison, SAVEFILE_VERSION, 0, flag);
+ do_s16b(&p_ptr->resist_magic, flag);
+ do_s16b(&p_ptr->tim_invisible, flag);
+ do_s16b(&p_ptr->tim_inv_pow, flag);
+ do_s16b(&p_ptr->tim_mimic, flag);
+ do_s16b(&p_ptr->lightspeed, flag);
+ do_s16b(&p_ptr->tim_lite, flag);
+ do_ver_s16b(&p_ptr->tim_regen, SAVEFILE_VERSION, 0, flag);
+ do_ver_s16b(&p_ptr->tim_regen_pow, SAVEFILE_VERSION, 0, flag);
+ do_s16b(&p_ptr->holy, flag);
+ do_s16b(&p_ptr->walk_water, flag);
+ do_s16b(&p_ptr->tim_mental_barrier, flag);
+ do_s16b(&p_ptr->immov_cntr, flag);
+ do_s16b(&p_ptr->strike, flag);
+ do_s16b(&p_ptr->meditation, flag);
+ do_s16b(&p_ptr->tim_reflect, flag);
+ do_s16b(&p_ptr->tim_res_time, flag);
+ do_s16b(&p_ptr->tim_deadly, flag);
+ do_s16b(&p_ptr->prob_travel, flag);
+ do_s16b(&p_ptr->disrupt_shield, flag);
+ do_s16b(&p_ptr->parasite, flag);
+ do_s16b(&p_ptr->parasite_r_idx, flag);
+ do_s32b(&p_ptr->loan, flag);
+ do_s32b(&p_ptr->loan_time, flag);
+ do_s16b(&p_ptr->absorb_soul, flag);
+
+ do_s16b(&p_ptr->chaos_patron, flag);
+
+ if (flag == LS_SAVE) tmp16s = max_corruptions;
+ do_s16b(&tmp16s, flag);
+
+ for (i = 0; i < tmp16s; i++)
+ {
+ if ((flag == LS_SAVE) && (i < max_corruptions))
+ tmp8u = p_ptr->corruptions[i];
+
+ do_byte(&tmp8u, flag);
+
+ if ((flag == LS_LOAD) && (i < max_corruptions))
+ p_ptr->corruptions[i] = tmp8u;
+ }
+
+ do_byte(&p_ptr->confusing, flag);
+ do_byte((byte*)&p_ptr->black_breath, flag);
+ do_byte((byte*)&fate_flag, flag);
+ do_byte(&p_ptr->searching, flag);
+ do_byte(&p_ptr->maximize, flag);
+ do_byte(&p_ptr->preserve, flag);
+ do_byte(&p_ptr->special, flag);
+ do_byte((byte*)&ambush_flag, flag);
+ do_byte(&p_ptr->allow_one_death, flag);
+ do_s16b(&p_ptr->xtra_spells, flag);
+
+ do_byte(&tmp8u, flag);
+
+ do_s16b(&no_breeds, flag);
+ do_s16b(&p_ptr->protgood, flag);
+
+ /* Auxilliary variables */
+ do_u32b(&p_ptr->mimic_extra, flag);
+ do_u32b(&p_ptr->antimagic_extra, flag);
+ do_u32b(&p_ptr->druid_extra, flag);
+ do_u32b(&p_ptr->druid_extra2, flag);
+ do_u32b(&p_ptr->druid_extra3, flag);
+ do_u32b(&p_ptr->music_extra, flag);
+ do_u32b(&p_ptr->music_extra2, flag);
+ do_u32b(&p_ptr->necro_extra, flag);
+ do_u32b(&p_ptr->necro_extra2, flag);
+
+ do_u32b(&p_ptr->race_extra1, flag);
+ do_u32b(&p_ptr->race_extra2, flag);
+ do_u32b(&p_ptr->race_extra3, flag);
+ do_u32b(&p_ptr->race_extra4, flag);
+ do_u32b(&p_ptr->race_extra5, flag);
+ do_u32b(&p_ptr->race_extra6, flag);
+ do_u32b(&p_ptr->race_extra7, flag);
+
+ do_u16b(&p_ptr->body_monster, flag);
+ do_byte((byte*)&p_ptr->disembodied, flag);
+
+ /* Are we in astral mode? */
+ do_byte((byte*)&p_ptr->astral, flag);
+
+ if (flag == LS_SAVE) tmp16s = POWER_MAX_INIT;
+ do_s16b(&tmp16s, flag);
+ if ((flag == LS_LOAD) && (tmp16s > POWER_MAX_INIT))
+ note(format("Too many (%u) powers!", tmp16s));
+ if (flag == LS_SAVE) tmp16s = POWER_MAX_INIT;
+ for (i = 0; i < tmp16s; i++)
+ do_byte((byte*)&p_ptr->powers_mod[i], flag);
+
+ skip_ver_byte(100, flag);
+
+ /* The tactic */
+ do_byte((byte*)&p_ptr->tactic, flag);
+
+ /* The movement */
+ do_byte((byte*)&p_ptr->movement, flag);
+
+ /* The comapnions killed */
+ do_s16b(&p_ptr->companion_killed, flag);
+
+ /* The fate */
+ do_byte((byte*)&p_ptr->no_mortal, flag);
+
+ /* The bounties */
+ for (i = 0; i < MAX_BOUNTIES; i++)
+ {
+ do_s16b(&bounties[i][0], flag);
+ do_s16b(&bounties[i][1], flag);
+ }
+ do_u32b(&total_bounties, flag);
+ do_s16b(&spell_num, flag);
+ for (i = 0; i < MAX_SPELLS; i++)
+ do_spells(i, flag);
+ do_s16b(&rune_num, flag);
+ for (i = 0; i < MAX_RUNES; i++)
+ {
+ do_string(rune_spells[i].name, 30, flag);
+ do_s16b(&rune_spells[i].type, flag);
+ do_s16b(&rune_spells[i].rune2, flag);
+ do_s16b(&rune_spells[i].mana, flag);
+ }
+
+ /* Load random seeds */
+ do_u32b(&dummy32u, flag); /* Load-compatibility with old savefiles. */
+ do_u32b(&seed_flavor, flag); /* For consistent object flavors. */
+ do_u32b(&dummy32u, flag); /* Load-compatibility with old savefiles. */
+
+ /* Special stuff */
+ do_u16b(&tmp16b, flag); /* Dummy */
+ do_u16b(&total_winner, flag);
+ do_u16b(&has_won, flag);
+ do_u16b(&noscore, flag);
+
+ /* Write death */
+ if (flag == LS_SAVE) tmp8u = death;
+ do_byte(&tmp8u, flag);
+ if (flag == LS_LOAD) death = tmp8u;
+
+ /* Incompatible module? */
+ if (flag == LS_LOAD)
+ {
+ s32b ok;
+
+ call_lua("module_savefile_loadable", "(s,d)", "d", loaded_game_module, death, &ok);
+
+ /* Argh bad game module! */
+ if (!ok)
+ {
+ note(format("Bad game module. Savefile was saved with module '%s' but game is '%s'.", loaded_game_module, game_module));
+ return (FALSE);
+ }
+ }
+
+ /* Write feeling */
+ if (flag == LS_SAVE) tmp8u = feeling;
+ do_byte(&tmp8u, flag);
+ if (flag == LS_LOAD) feeling = tmp8u;
+
+ /* Turn of last "feeling" */
+ do_s32b(&old_turn, flag);
+
+ /* Current turn */
+ do_s32b(&turn, flag);
+
+ return TRUE;
+}
+
+/* Save the current persistent dungeon -SC- */
+void save_dungeon(void)
+{
+ char tmp[16];
+ char name[1024], buf[5];
+
+ /* Save only persistent dungeons */
+ if (!get_dungeon_save(buf) || (!dun_level)) return;
+
+ /* Construct filename */
+ sprintf(tmp, "%s.%s", player_base, buf);
+ path_build(name, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Open the file */
+ fff = my_fopen(name, "wb");
+
+ /* Save the dungeon */
+ do_dungeon(LS_SAVE, TRUE);
+
+ my_fclose(fff);
+}
+
+/*
+ * Medium level player saver
+ */
+static bool_ save_player_aux(char *name)
+{
+ bool_ ok = FALSE;
+ int fd = -1;
+ int mode = 0644;
+
+ /* No file yet */
+ fff = NULL;
+
+ /* File type is "SAVE" */
+ FILE_TYPE(FILE_TYPE_SAVE);
+
+ /* Create the savefile */
+ fd = fd_make(name, mode);
+
+ /* File is okay */
+ if (fd >= 0)
+ {
+ /* Close the "fd" */
+ (void)fd_close(fd);
+
+ /* Open the savefile */
+ fff = my_fopen(name, "wb");
+
+ /* Successful open */
+ if (fff)
+ {
+ /* Write the savefile */
+ if (do_savefile_aux(LS_SAVE)) ok = TRUE;
+
+ /* Attempt to close it */
+ if (my_fclose(fff)) ok = FALSE;
+ }
+
+ /* "broken" savefile */
+ if (!ok)
+ {
+ /* Remove "broken" files */
+ (void)fd_kill(name);
+ }
+ }
+
+ /* Failure */
+ if (!ok) return (FALSE);
+
+ /* Successful save */
+ character_saved = TRUE;
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * Attempt to save the player in a savefile
+ */
+bool_ save_player(void)
+{
+ int result = FALSE;
+ char safe[1024];
+
+ /* New savefile */
+ strcpy(safe, savefile);
+ strcat(safe, ".new");
+
+ /* Remove it */
+ fd_kill(safe);
+
+ /* Attempt to save the player */
+ if (save_player_aux(safe))
+ {
+ char temp[1024];
+
+ /* Old savefile */
+ strcpy(temp, savefile);
+ strcat(temp, ".old");
+
+ /* Remove it */
+ fd_kill(temp);
+
+ /* Preserve old savefile */
+ fd_move(savefile, temp);
+
+ /* Activate new savefile */
+ fd_move(safe, savefile);
+
+ /* Remove preserved savefile */
+ fd_kill(temp);
+
+ /* Hack -- Pretend the character was loaded */
+ character_loaded = TRUE;
+
+ /* Success */
+ result = TRUE;
+ }
+
+ save_savefile_names();
+
+ /* Return the result */
+ return (result);
+}
+
+bool_ file_exist(char *buf)
+{
+ int fd;
+ bool_ result;
+
+ /* Open savefile */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* File exists */
+ if (fd >= 0)
+ {
+ fd_close(fd);
+ result = TRUE;
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+/*
+ * Attempt to Load a "savefile"
+ *
+ * On multi-user systems, you may only "read" a savefile if you will be
+ * allowed to "write" it later, this prevents painful situations in which
+ * the player loads a savefile belonging to someone else, and then is not
+ * allowed to save his game when he quits.
+ *
+ * We return "TRUE" if the savefile was usable, and we set the global
+ * flag "character_loaded" if a real, living, character was loaded.
+ *
+ * Note that we always try to load the "current" savefile, even if
+ * there is no such file, so we must check for "empty" savefile names.
+ */
+bool_ load_player(void)
+{
+ int fd = -1;
+
+ errr err = 0;
+
+ cptr what = "generic";
+
+ /* Paranoia */
+ turn = 0;
+
+ /* Paranoia */
+ death = FALSE;
+
+
+ /* Allow empty savefile name */
+ if (!savefile[0]) return (TRUE);
+
+
+ /* XXX XXX XXX Fix this */
+
+ /* Verify the existance of the savefile */
+ if (!file_exist(savefile))
+ {
+ /* Give a message */
+ msg_format("Savefile does not exist: %s", savefile);
+ msg_print(NULL);
+
+ /* Allow this */
+ return (TRUE);
+ }
+
+ /* Okay */
+ if (!err)
+ {
+ /* Open the savefile */
+ fd = fd_open(savefile, O_RDONLY);
+
+ /* No file */
+ if (fd < 0) err = -1;
+
+ /* Message (below) */
+ if (err) what = "Cannot open savefile";
+ }
+
+ /* Process file */
+ if (!err)
+ {
+ /* Open the file XXX XXX XXX XXX Should use Angband file interface */
+ fff = my_fopen(savefile, "rb");
+/* fff = fdopen(fd, "r"); */
+
+ /* Read the first four bytes */
+ do_u32b(&vernum, LS_LOAD);
+ do_byte(&sf_extra, LS_LOAD);
+
+ /* XXX XXX XXX XXX Should use Angband file interface */
+ my_fclose(fff);
+ /* fclose(fff) */
+
+ /* Close the file */
+ fd_close(fd);
+ }
+
+ /* Process file */
+ if (!err)
+ {
+
+ /* Extract version */
+ sf_major = VERSION_MAJOR;
+ sf_minor = VERSION_MINOR;
+ sf_patch = VERSION_PATCH;
+ sf_extra = VERSION_EXTRA;
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Attempt to load */
+ err = rd_savefile();
+
+ /* Message (below) */
+ if (err) what = "Cannot parse savefile";
+ }
+
+ /* Paranoia */
+ if (!err)
+ {
+ /* Invalid turn */
+ if (!turn) err = -1;
+
+ /* Message (below) */
+ if (err) what = "Broken savefile";
+ }
+
+
+ /* Okay */
+ if (!err)
+ {
+ /* Maybe the scripts want to resurrect char */
+ if (process_hooks_ret(HOOK_LOAD_END, "d", "(d)", death))
+ {
+ character_loaded = process_hooks_return[0].num;
+ death = process_hooks_return[1].num;
+ return TRUE;
+ }
+
+ /* Player is dead */
+ if (death)
+ {
+ /* Player is no longer "dead" */
+ death = FALSE;
+
+ /* Cheat death (unless the character retired) */
+ if (arg_wizard && !total_winner)
+ {
+ /* A character was loaded */
+ character_loaded = TRUE;
+
+ /* Done */
+ return (TRUE);
+ }
+
+ /* Count lives */
+ sf_lives++;
+
+ /* Forget turns */
+ turn = old_turn = 0;
+
+ /* Done */
+ return (TRUE);
+ }
+
+ /* A character was loaded */
+ character_loaded = TRUE;
+
+ /* Still alive */
+ if (p_ptr->chp >= 0)
+ {
+ /* Reset cause of death */
+ (void)strcpy(died_from, "(alive and well)");
+ }
+
+ /* Success */
+ return (TRUE);
+ }
+
+
+ /* Message */
+ msg_format("Error (%s) reading %d.%d.%d savefile.",
+ what, sf_major, sf_minor, sf_patch);
+ msg_print(NULL);
+
+ /* Oops */
+ return (FALSE);
+}
+
+
+/*
+ * Size-aware read/write routines for the savefile, do all their
+ * work through sf_get and sf_put.
+ */
+
+static void do_byte(byte *v, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ *v = sf_get();
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ byte val = *v;
+ sf_put(val);
+ return;
+ }
+ /* We should never reach here, so bail out */
+ printf("FATAL: do_byte passed %d\n", flag);
+ exit(0);
+}
+
+static void do_u16b(u16b *v, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ (*v) = sf_get();
+ (*v) |= ((u16b)(sf_get()) << 8);
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ u16b val;
+ val = *v;
+ sf_put((byte)(val & 0xFF));
+ sf_put((byte)((val >> 8) & 0xFF));
+ return;
+ }
+ /* Never should reach here, bail out */
+ printf("FATAL: do_u16b passed %d\n", flag);
+ exit(0);
+}
+
+static void do_s16b(s16b *ip, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ do_u16b((u16b *)ip, flag);
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ do_u16b((u16b *)ip, flag);
+ return;
+ }
+ /* Blah blah, never should reach here, die */
+ printf("FATAL: do_s16b passed %d\n", flag);
+ exit(0);
+}
+
+static void do_u32b(u32b *ip, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ (*ip) = sf_get();
+ (*ip) |= ((u32b)(sf_get()) << 8);
+ (*ip) |= ((u32b)(sf_get()) << 16);
+ (*ip) |= ((u32b)(sf_get()) << 24);
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ u32b val = *ip;
+ sf_put((byte)(val & 0xFF));
+ sf_put((byte)((val >> 8) & 0xFF));
+ sf_put((byte)((val >> 16) & 0xFF));
+ sf_put((byte)((val >> 24) & 0xFF));
+ return;
+ }
+ /* Bad news if you're here, because you're going down */
+ printf("FATAL: do_u32b passed %d\n", flag);
+ exit(0);
+}
+
+static void do_s32b(s32b *ip, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ do_u32b((u32b *)ip, flag);
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ do_u32b((u32b *)ip, flag);
+ return;
+ }
+ /* Raus! Schnell! */
+ printf("FATAL: do_s32b passed %d\n", flag);
+ exit(0);
+}
+
+static void do_string(char *str, int max, int flag)
+/* Max is ignored for writing */
+{
+ if (flag == LS_LOAD)
+ {
+ int i;
+
+ /* Read the string */
+ for (i = 0; TRUE; i++)
+ {
+ byte tmp8u;
+
+ /* Read a byte */
+ do_byte(&tmp8u, LS_LOAD);
+
+ /* Collect string while legal */
+ if (i < max) str[i] = tmp8u;
+
+ /* End of string */
+ if (!tmp8u) break;
+ }
+ /* Terminate */
+ str[max - 1] = '\0';
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ while (*str)
+ {
+ do_byte((byte*)str, flag);
+ str++;
+ }
+ do_byte((byte*)str, flag); /* Output a terminator */
+ return;
+ }
+ printf("FATAL: do_string passed flag %d\n", flag);
+ exit(0);
+}
+
+static void skip_ver_byte(u32b version, int flag)
+/* Reads and discards a byte if the savefile is as old as/older than version */
+{
+ if ((flag == LS_LOAD) && (vernum <= version))
+ {
+ byte forget;
+ do_byte(&forget, flag);
+ }
+ return;
+}
+
+static void do_ver_s16b(s16b *v, u32b version, s16b defval, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum < version))
+ {
+ *v = defval;
+ return;
+ }
+ do_s16b(v, flag);
+}
+
+/*
+ * Show information on the screen, one line at a time.
+ *
+ * Avoid the top two lines, to avoid interference with "msg_print()".
+ */
+static void note(cptr msg)
+{
+ static int y = 2;
+
+ /* Draw the message */
+ prt(msg, y, 0);
+
+ /* Advance one line (wrap if needed) */
+ if (++y >= 24) y = 2;
+
+ /* Flush it */
+ Term_fresh();
+}
+
+
+/*
+ * Determine if an item can be wielded/worn (e.g. helmet, sword, bow, arrow)
+ */
+static bool_ wearable_p(object_type *o_ptr)
+{
+ /* Valid "tval" codes */
+ switch (o_ptr->tval)
+ {
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_SCROLL:
+ case TV_LITE:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_AMULET:
+ case TV_RING:
+ case TV_HYPNOS:
+ case TV_INSTRUMENT:
+ case TV_DAEMON_BOOK:
+ case TV_TRAPKIT:
+ case TV_TOOL:
+ {
+ return (TRUE);
+ }
+ }
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+/*
+ * rd/wr an object
+ *
+ * FIXME! This code probably has a lot of cruft from the old Z/V codebase.
+ *
+ */
+static void do_item(object_type *o_ptr, int flag)
+{
+ byte old_dd;
+ byte old_ds;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_kind *k_ptr;
+
+ /* Kind */
+ do_s16b(&o_ptr->k_idx, flag);
+
+ /* Location */
+ do_byte(&o_ptr->iy, flag);
+ do_byte(&o_ptr->ix, flag);
+
+ /* Type/Subtype */
+ do_byte(&o_ptr->tval, flag);
+ do_byte(&o_ptr->sval, flag);
+
+ /* Special pval */
+ do_s32b(&o_ptr->pval, flag);
+
+ /* Special pval */
+ do_s16b(&o_ptr->pval2, flag);
+
+ /* Special pval */
+ do_s32b(&o_ptr->pval3, flag);
+
+ do_byte(&o_ptr->discount, flag);
+ do_byte(&o_ptr->number, flag);
+ do_s32b(&o_ptr->weight, flag);
+
+ do_byte(&o_ptr->name1, flag);
+ do_s16b(&o_ptr->name2, flag);
+ do_s16b(&o_ptr->name2b, flag);
+ do_s16b(&o_ptr->timeout, flag);
+
+ do_s16b(&o_ptr->to_h, flag);
+ do_s16b(&o_ptr->to_d, flag);
+ do_s16b(&o_ptr->to_a, flag);
+
+ do_s16b(&o_ptr->ac, flag);
+
+ /* We do special processing of this flag when reading */
+ if (flag == LS_LOAD)
+ {
+ do_byte(&old_dd, LS_LOAD);
+ do_byte(&old_ds, LS_LOAD);
+ }
+ if (flag == LS_SAVE)
+ {
+ do_byte(&o_ptr->dd, LS_SAVE);
+ do_byte(&o_ptr->ds, LS_SAVE);
+ }
+
+ do_byte(&o_ptr->ident, flag);
+
+ do_byte(&o_ptr->marked, flag);
+
+ /* flags */
+ do_u32b(&o_ptr->art_flags1, flag);
+ do_u32b(&o_ptr->art_flags2, flag);
+ do_u32b(&o_ptr->art_flags3, flag);
+ do_u32b(&o_ptr->art_flags4, flag);
+ do_u32b(&o_ptr->art_flags5, flag);
+ do_u32b(&o_ptr->art_esp, flag);
+
+ /* obvious flags */
+ do_u32b(&o_ptr->art_oflags1, flag);
+ do_u32b(&o_ptr->art_oflags2, flag);
+ do_u32b(&o_ptr->art_oflags3, flag);
+ do_u32b(&o_ptr->art_oflags4, flag);
+ do_u32b(&o_ptr->art_oflags5, flag);
+ do_u32b(&o_ptr->art_oesp, flag);
+
+ /* Monster holding object */
+ do_s16b(&o_ptr->held_m_idx, flag);
+
+ /* Special powers */
+ do_byte(&o_ptr->xtra1, flag);
+ do_s16b(&o_ptr->xtra2, flag);
+
+ do_byte(&o_ptr->elevel, flag);
+ do_s32b(&o_ptr->exp, flag);
+
+ /* Read the pseudo-id */
+ do_byte(&o_ptr->sense, flag);
+
+ /* Read the found info */
+ do_byte(&o_ptr->found, flag);
+ do_s16b(&o_ptr->found_aux1, flag);
+ do_s16b(&o_ptr->found_aux2, flag);
+ do_s16b(&o_ptr->found_aux3, flag);
+ do_s16b(&o_ptr->found_aux4, flag);
+
+ if (flag == LS_LOAD)
+ {
+ char buf[128];
+ /* Inscription */
+ do_string(buf, 128, LS_LOAD);
+ /* Save the inscription */
+ if (buf[0]) o_ptr->note = quark_add(buf);
+
+ do_string(buf, 128, LS_LOAD);
+ if (buf[0]) o_ptr->art_name = quark_add(buf);
+ }
+ if (flag == LS_SAVE)
+ {
+ /* Save the inscription (if any) */
+ if (o_ptr->note)
+ {
+ do_string((char *)quark_str(o_ptr->note), 0, LS_SAVE);
+ }
+ else
+ {
+ do_string("", 0, LS_SAVE);
+ }
+ if (o_ptr->art_name)
+ {
+ do_string((char *)quark_str(o_ptr->art_name), 0, LS_SAVE);
+ }
+ else
+ {
+ do_string("", 0, LS_SAVE);
+ }
+ }
+
+ if (flag == LS_SAVE) return ; /* Stick any more shared code before this. The rest
+ of this function is reserved for LS_LOAD's
+ cleanup functions */
+ /*********** END OF LS_SAVE ***************/
+
+ /* Obtain the "kind" template */
+ k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Obtain tval/sval from k_info */
+ o_ptr->tval = k_ptr->tval;
+ if (o_ptr->tval != TV_RANDART) o_ptr->sval = k_ptr->sval;
+
+
+ /* Repair non "wearable" items */
+ if (!wearable_p(o_ptr))
+ {
+ /* Acquire correct fields */
+ o_ptr->to_h = k_ptr->to_h;
+ o_ptr->to_d = k_ptr->to_d;
+ o_ptr->to_a = k_ptr->to_a;
+
+ /* Acquire correct fields */
+ o_ptr->ac = k_ptr->ac;
+ o_ptr->dd = k_ptr->dd;
+ o_ptr->ds = k_ptr->ds;
+
+ /* All done */
+ return;
+ }
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Paranoia */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr;
+
+ /* Obtain the artifact info */
+ a_ptr = &a_info[o_ptr->name1];
+
+ /* Verify that artifact */
+ if (!a_ptr->name) o_ptr->name1 = 0;
+ }
+
+ /* Paranoia */
+ if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr;
+
+ /* Obtain the ego-item info */
+ e_ptr = &e_info[o_ptr->name2];
+
+ /* Verify that ego-item */
+ if (!e_ptr->name) o_ptr->name2 = 0;
+ }
+
+
+ /* Acquire standard fields */
+ o_ptr->ac = k_ptr->ac;
+ o_ptr->dd = k_ptr->dd;
+ o_ptr->ds = k_ptr->ds;
+
+ /* Artifacts */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr;
+
+ /* Obtain the artifact info */
+ a_ptr = &a_info[o_ptr->name1];
+
+ /* Acquire new artifact fields */
+ o_ptr->ac = a_ptr->ac;
+ o_ptr->dd = a_ptr->dd;
+ o_ptr->ds = a_ptr->ds;
+
+ /* Acquire new artifact weight */
+ o_ptr->weight = a_ptr->weight;
+ }
+
+ /* Ego items */
+ if (o_ptr->name2)
+ {
+ o_ptr->dd = old_dd;
+ o_ptr->ds = old_ds;
+ }
+
+ if (o_ptr->art_name) /* A random artifact */
+ {
+ o_ptr->dd = old_dd;
+ o_ptr->ds = old_ds;
+ }
+}
+
+
+
+
+/*
+ * Read a monster
+ */
+static void do_monster(monster_type *m_ptr, int flag)
+{
+ int i;
+ bool_ tmp;
+
+ /* Read the monster race */
+ do_s16b(&m_ptr->r_idx, flag);
+
+ do_u16b(&m_ptr->ego, flag);
+
+ /* Read the other information */
+ do_byte(&m_ptr->fy, flag);
+ do_byte(&m_ptr->fx, flag);
+
+ do_s32b(&m_ptr->hp, flag);
+ do_s32b(&m_ptr->maxhp, flag);
+
+ do_s16b(&m_ptr->csleep, flag);
+ do_byte(&m_ptr->mspeed, flag);
+ do_byte(&m_ptr->energy, flag);
+ do_byte(&m_ptr->stunned, flag);
+ do_byte(&m_ptr->confused, flag);
+ do_byte(&m_ptr->monfear, flag);
+ do_u32b(&m_ptr->smart, flag);
+ do_s16b(&m_ptr->status, flag);
+ do_s16b(&m_ptr->possessor, flag);
+ do_byte(&m_ptr->speed, flag);
+ do_byte(&m_ptr->level, flag);
+ do_s16b(&m_ptr->ac, flag);
+ do_u32b(&m_ptr->exp, flag);
+ do_s16b(&m_ptr->target, flag);
+
+ do_s16b(&m_ptr->bleeding, flag);
+ do_s16b(&m_ptr->poisoned, flag);
+
+ do_s32b(&m_ptr->mflag, flag);
+
+ if (flag == LS_LOAD) m_ptr->mflag &= PERM_MFLAG_MASK;
+
+ /* Attacks */
+ for (i = 0; i < 4; i++)
+ {
+ do_byte(&m_ptr->blow[i].method, flag);
+ do_byte(&m_ptr->blow[i].effect, flag);
+ do_byte(&m_ptr->blow[i].d_dice, flag);
+ do_byte(&m_ptr->blow[i].d_side, flag);
+ }
+
+ /* Mind */
+ tmp = (m_ptr->mind) ? TRUE : FALSE;
+ do_byte((byte*)&tmp, flag);
+ if (tmp)
+ {
+ if (flag == LS_LOAD)
+ {
+ MAKE(m_ptr->mind, monster_mind);
+ }
+ }
+
+ /* Special race */
+ tmp = (m_ptr->sr_ptr) ? TRUE : FALSE;
+ do_byte((byte*)&tmp, flag);
+ if (tmp)
+ {
+ if (flag == LS_LOAD)
+ {
+ MAKE(m_ptr->sr_ptr, monster_race);
+ }
+ do_u32b(&m_ptr->sr_ptr->name, flag);
+ do_u32b(&m_ptr->sr_ptr->text, flag);
+
+ do_u16b(&m_ptr->sr_ptr->hdice, flag);
+ do_u16b(&m_ptr->sr_ptr->hside, flag);
+
+ do_s16b(&m_ptr->sr_ptr->ac, flag);
+
+ do_s16b(&m_ptr->sr_ptr->sleep, flag);
+ do_byte(&m_ptr->sr_ptr->aaf, flag);
+ do_byte(&m_ptr->sr_ptr->speed, flag);
+
+ do_s32b(&m_ptr->sr_ptr->mexp, flag);
+
+ do_s32b(&m_ptr->sr_ptr->weight, flag);
+
+ do_byte(&m_ptr->sr_ptr->freq_inate, flag);
+ do_byte(&m_ptr->sr_ptr->freq_spell, flag);
+
+ do_u32b(&m_ptr->sr_ptr->flags1, flag);
+ do_u32b(&m_ptr->sr_ptr->flags2, flag);
+ do_u32b(&m_ptr->sr_ptr->flags3, flag);
+ do_u32b(&m_ptr->sr_ptr->flags4, flag);
+ do_u32b(&m_ptr->sr_ptr->flags5, flag);
+ do_u32b(&m_ptr->sr_ptr->flags6, flag);
+ do_u32b(&m_ptr->sr_ptr->flags7, flag);
+ do_u32b(&m_ptr->sr_ptr->flags8, flag);
+ do_u32b(&m_ptr->sr_ptr->flags9, flag);
+
+ /* Attacks */
+ for (i = 0; i < 4; i++)
+ {
+ do_byte(&m_ptr->sr_ptr->blow[i].method, flag);
+ do_byte(&m_ptr->sr_ptr->blow[i].effect, flag);
+ do_byte(&m_ptr->sr_ptr->blow[i].d_dice, flag);
+ do_byte(&m_ptr->sr_ptr->blow[i].d_side, flag);
+ }
+
+ for (i = 0; i < BODY_MAX; i++)
+ do_byte(&m_ptr->sr_ptr->body_parts[i], flag);
+
+ do_byte(&m_ptr->sr_ptr->level, flag);
+ do_byte(&m_ptr->sr_ptr->rarity, flag);
+
+ do_byte((byte*)&m_ptr->sr_ptr->d_char, flag);
+ do_byte(&m_ptr->sr_ptr->d_attr, flag);
+
+ do_byte((byte*)&m_ptr->sr_ptr->x_char, flag);
+ do_byte(&m_ptr->sr_ptr->x_attr, flag);
+
+ do_s16b(&m_ptr->sr_ptr->max_num, flag);
+ do_byte(&m_ptr->sr_ptr->cur_num, flag);
+ }
+}
+
+
+
+
+
+/*
+ * Handle monster lore
+ */
+static void do_lore(int r_idx, int flag)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Count sights/deaths/kills */
+ do_s16b(&r_ptr->r_sights, flag);
+ do_s16b(&r_ptr->r_deaths, flag);
+ do_s16b(&r_ptr->r_pkills, flag);
+ do_s16b(&r_ptr->r_tkills, flag);
+
+ /* Count wakes and ignores */
+ do_byte(&r_ptr->r_wake, flag);
+ do_byte(&r_ptr->r_ignore, flag);
+
+ /* Extra stuff */
+ do_byte(&r_ptr->r_xtra1, flag);
+ do_byte(&r_ptr->r_xtra2, flag);
+
+ /* Count drops */
+ do_byte(&r_ptr->r_drop_gold, flag);
+ do_byte(&r_ptr->r_drop_item, flag);
+
+ /* Count spells */
+ do_byte(&r_ptr->r_cast_inate, flag);
+ do_byte(&r_ptr->r_cast_spell, flag);
+
+ /* Count blows of each type */
+ do_byte(&r_ptr->r_blows[0], flag);
+ do_byte(&r_ptr->r_blows[1], flag);
+ do_byte(&r_ptr->r_blows[2], flag);
+ do_byte(&r_ptr->r_blows[3], flag);
+
+ /* Memorize flags */
+ do_u32b(&r_ptr->r_flags1, flag); /* Just to remind you */
+ do_u32b(&r_ptr->r_flags2, flag); /* flag is unrelated to */
+ do_u32b(&r_ptr->r_flags3, flag); /* the other argument */
+ do_u32b(&r_ptr->r_flags4, flag);
+ do_u32b(&r_ptr->r_flags5, flag);
+ do_u32b(&r_ptr->r_flags6, flag);
+ do_u32b(&r_ptr->r_flags7, flag);
+ do_u32b(&r_ptr->r_flags8, flag);
+ do_u32b(&r_ptr->r_flags9, flag);
+
+ /* Read the "Racial" monster tmp16b per level */
+ do_s16b(&r_ptr->max_num, flag);
+
+ do_byte((byte*)&r_ptr->on_saved, flag);
+
+ if (flag == LS_LOAD)
+ {
+ /* Lore flag repair? */
+ r_ptr->r_flags1 &= r_ptr->flags1;
+ r_ptr->r_flags2 &= r_ptr->flags2;
+ r_ptr->r_flags3 &= r_ptr->flags3;
+ r_ptr->r_flags4 &= r_ptr->flags4;
+ r_ptr->r_flags5 &= r_ptr->flags5;
+ r_ptr->r_flags6 &= r_ptr->flags6;
+ }
+}
+
+
+
+
+/*
+ * Read a store
+ */
+static bool_ do_store(store_type *str, int flag)
+/* FIXME! Why does this return anything when
+ it always returns the same thing? */
+{
+ int j;
+
+ byte num;
+
+ byte store_inven_max = STORE_INVEN_MAX;
+
+ /* Some basic info */
+ do_s32b(&str->store_open, flag);
+ do_s16b(&str->insult_cur, flag);
+ do_u16b(&str->owner, flag);
+ if (flag == LS_SAVE) num = str->stock_num;
+
+ /* Could be cleaner, done this way for benefit of the for loop later on */
+ do_byte(&num, flag);
+
+ do_s16b(&str->good_buy, flag);
+ do_s16b(&str->bad_buy, flag);
+
+ /* Last visit */
+ do_s32b(&str->last_visit, flag);
+
+ /* Items */
+ for (j = 0; j < num; j++)
+ {
+ if (flag == LS_LOAD)
+ /* Can't this be cleaner? */
+ {
+ object_type forge;
+ /* Wipe the object */
+ object_wipe(&forge);
+ /* Read the item */
+ do_item(&forge, LS_LOAD);
+ /* Acquire valid items */
+ if ((str->stock_num < store_inven_max) && (str->stock_num < str->stock_size))
+ {
+ int k = str->stock_num++;
+
+ /* Acquire the item */
+ object_copy(&str->stock[k], &forge);
+ }
+ }
+ if (flag == LS_SAVE) do_item(&str->stock[j], flag);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * RNG state
+ */
+static void do_randomizer(int flag)
+{
+ int i;
+
+ u16b tmp16u = 0;
+
+ /* Tmp */
+ do_u16b(&tmp16u, flag);
+
+ /* Place */
+ do_u16b(&Rand_place, flag);
+
+ /* State */
+ for (i = 0; i < RAND_DEG; i++)
+ {
+ do_u32b(&Rand_state[i], flag);
+ }
+
+ /* Accept */
+ if (flag == LS_LOAD)
+ {
+ Rand_quick = FALSE;
+ }
+}
+
+/*
+ * Handle options
+ *
+ * Normal options are stored as a set of 256 bit flags,
+ * plus a set of 256 bit masks to indicate which bit flags were defined
+ * at the time the savefile was created. This will allow new options
+ * to be added, and old options to be removed, at any time, without
+ * hurting old savefiles.
+ *
+ * The window options are stored in the same way, but note that each
+ * window gets 32 options, and their order is fixed by certain defines.
+ */
+static void do_options(int flag)
+{
+ int i, n;
+
+ u32b oflag[8];
+ u32b mask[8];
+
+ /*** Special info */
+
+ /* Read "delay_factor" */
+ do_byte(&delay_factor, flag);
+
+ /* Read "hitpoint_warn" */
+ do_byte(&hitpoint_warn, flag);
+
+ /*** Cheating options ***/
+ if (flag == LS_LOAD) /* There *MUST* be some nice way to unify this! */
+ {
+ u16b c;
+ do_u16b(&c, LS_LOAD);
+ if (c & 0x0002) wizard = TRUE;
+ cheat_peek = (c & 0x0100) ? TRUE : FALSE;
+ cheat_hear = (c & 0x0200) ? TRUE : FALSE;
+ cheat_room = (c & 0x0400) ? TRUE : FALSE;
+ cheat_xtra = (c & 0x0800) ? TRUE : FALSE;
+ cheat_know = (c & 0x1000) ? TRUE : FALSE;
+ cheat_live = (c & 0x2000) ? TRUE : FALSE;
+ }
+ if (flag == LS_SAVE)
+ {
+ u16b c = 0;
+ if (wizard) c |= 0x0002;
+ if (cheat_peek) c |= 0x0100;
+ if (cheat_hear) c |= 0x0200;
+ if (cheat_room) c |= 0x0400;
+ if (cheat_xtra) c |= 0x0800;
+ if (cheat_know) c |= 0x1000;
+ if (cheat_live) c |= 0x2000;
+ do_u16b(&c, LS_SAVE);
+ }
+
+ do_byte((byte*)&autosave_l, flag);
+ do_byte((byte*)&autosave_t, flag);
+ do_s16b(&autosave_freq, flag);
+
+ if (flag == LS_LOAD)
+ {
+ /* Read the option flags */
+ for (n = 0; n < 8; n++) do_u32b(&oflag[n], flag);
+
+ /* Read the option masks */
+ for (n = 0; n < 8; n++) do_u32b(&mask[n], flag);
+
+ /* Analyze the options */
+ for (n = 0; n < 8; n++)
+ {
+ /* Analyze the options */
+ for (i = 0; i < 32; i++)
+ {
+ /* Process valid flags */
+ if (mask[n] & (1L << i))
+ {
+ /* Process valid flags */
+ if (option_mask[n] & (1L << i))
+ {
+ /* Set */
+ if (oflag[n] & (1L << i))
+ {
+ /* Set */
+ option_flag[n] |= (1L << i);
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ option_flag[n] &= ~(1L << i);
+ }
+ }
+ }
+ }
+ }
+
+
+ /*** Window Options ***/
+
+ /* Read the window flags */
+ for (n = 0; n < 8; n++) do_u32b(&oflag[n], flag);
+
+ /* Read the window masks */
+ for (n = 0; n < 8; n++) do_u32b(&mask[n], flag);
+
+ /* Analyze the options */
+ for (n = 0; n < 8; n++)
+ {
+ /* Analyze the options */
+ for (i = 0; i < 32; i++)
+ {
+ /* Process valid flags */
+ if (mask[n] & (1L << i))
+ {
+ /* Process valid flags */
+ if (window_mask[n] & (1L << i))
+ {
+ /* Set */
+ if (oflag[n] & (1L << i))
+ {
+ /* Set */
+ window_flag[n] |= (1L << i);
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ window_flag[n] &= ~(1L << i);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (flag == LS_SAVE)
+ {
+ /* Analyze the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ int os = option_info[i].o_page;
+ int ob = option_info[i].o_bit;
+
+ /* Process real entries */
+ if (option_info[i].o_var)
+ {
+ /* Set */
+ if (*option_info[i].o_var)
+ {
+ /* Set */
+ option_flag[os] |= (1L << ob);
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ option_flag[os] &= ~(1L << ob);
+ }
+ }
+ }
+
+
+ /*** Normal options ***/
+
+ /* Dump the flags */
+ for (i = 0; i < 8; i++) do_u32b(&option_flag[i], flag);
+
+ /* Dump the masks */
+ for (i = 0; i < 8; i++) do_u32b(&option_mask[i], flag);
+
+ /*** Window options ***/
+
+ /* Dump the flags */
+ for (i = 0; i < 8; i++) do_u32b(&window_flag[i], flag);
+
+ /* Dump the masks */
+ for (i = 0; i < 8; i++) do_u32b(&window_mask[i], flag);
+ }
+}
+
+
+/* Load/Save the random spells info */
+static void do_spells(int i, int flag)
+{
+ random_spell *s_ptr = &random_spells[i];
+ do_string(s_ptr->name, 30, flag);
+ do_string(s_ptr->desc, 30, flag);
+ do_s16b(&s_ptr->mana, flag);
+ do_s16b(&s_ptr->fail, flag);
+ do_u32b(&s_ptr->proj_flags, flag);
+ do_byte(&s_ptr->GF, flag);
+ do_byte(&s_ptr->radius, flag);
+ do_byte(&s_ptr->dam_sides, flag);
+ do_byte(&s_ptr->dam_dice, flag);
+ do_byte(&s_ptr->level, flag);
+ do_byte((byte*)&s_ptr->untried, flag);
+}
+
+
+/*
+ * Handle player inventory
+ *
+ * FIXME! This function probably could be unified better
+ * Note that the inventory is "re-sorted" later by "dungeon()".
+ */
+static bool_ do_inventory(int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ int slot = 0;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* No items */
+ inven_cnt = 0;
+ equip_cnt = 0;
+
+ /* Read until done */
+ while (1)
+ {
+ u16b n;
+
+ /* Get the next item index */
+ do_u16b(&n, LS_LOAD);
+
+ /* Nope, we reached the end */
+ if (n == 0xFFFF) break;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Read the item */
+ do_item(q_ptr, LS_LOAD);
+
+ /* Hack -- verify item */
+ if (!q_ptr->k_idx) return (FALSE);
+
+ /* Wield equipment */
+ if (n >= INVEN_WIELD)
+ {
+ /* Copy object */
+ object_copy(&p_ptr->inventory[n], q_ptr);
+
+ /* Take care of item sets */
+ if (q_ptr->name1)
+ {
+ wield_set(q_ptr->name1, a_info[q_ptr->name1].set, TRUE);
+ }
+
+ /* One more item */
+ equip_cnt++;
+ }
+
+ /* Warning -- backpack is full */
+ else if (inven_cnt == INVEN_PACK)
+ {
+ /* Oops */
+ note("Too many items in the inventory!");
+
+ /* Fail */
+ return (FALSE);
+ }
+
+ /* Carry inventory */
+ else
+ {
+ /* Get a slot */
+ n = slot++;
+
+ /* Copy object */
+ object_copy(&p_ptr->inventory[n], q_ptr);
+
+ /* One more item */
+ inven_cnt++;
+ }
+ }
+ }
+ if (flag == LS_SAVE)
+ {
+ u16b i;
+ u16b sent = 0xFFFF;
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+ if (!o_ptr->k_idx) continue;
+ do_u16b(&i, flag);
+ do_item(o_ptr, flag);
+ }
+ do_u16b(&sent, LS_SAVE); /* Sentinel */
+ }
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Read the saved messages
+ */
+static void do_messages(int flag) /* FIXME! We should be able to unify this better */
+{
+ int i;
+ char buf[128];
+ byte color, type;
+
+ s16b num;
+
+ if (flag == LS_SAVE) num = message_num();
+
+ /* Total */
+ do_s16b(&num, flag);
+
+ /* Read the messages */
+ if (flag == LS_LOAD)
+ {
+ for (i = 0; i < num; i++)
+ {
+ /* Read the message */
+ do_string(buf, 128, LS_LOAD);
+ do_byte(&color, flag);
+ do_byte(&type, flag);
+
+ /* Save the message */
+ message_add(type, buf, color);
+ }
+ }
+ if (flag == LS_SAVE)
+ {
+ byte holder;
+ for (i = num - 1; i >= 0; i--)
+ {
+ do_string((char *)message_str((s16b)i), 0, LS_SAVE);
+ holder = message_color((s16b)i);
+ do_byte(&holder, flag);
+ holder = message_type((s16b)i);
+ do_byte(&holder, flag);
+ }
+ }
+}
+
+/*
+ * Handle dungeon
+ *
+ * The monsters/objects must be loaded in the same order
+ * that they were stored, since the actual indexes matter.
+ */
+static bool_ do_dungeon(int flag, bool_ no_companions)
+{
+ int i;
+
+ cave_type *c_ptr;
+
+ /* Read specific */
+ u16b tmp16b = 0;
+
+ my_sentinel("Before do_dungeon", 324, flag);
+
+ /* Header info */
+ do_s16b(&dun_level, flag);
+ do_byte(&dungeon_type, flag);
+ do_s16b(&num_repro, flag);
+ do_s16b(&p_ptr->py, flag);
+ do_s16b(&p_ptr->px, flag);
+ do_s16b(&cur_hgt, flag);
+ do_s16b(&cur_wid, flag);
+ do_s16b(&max_panel_rows, flag);
+ do_s16b(&max_panel_cols, flag);
+
+ do_u32b(&dungeon_flags1, flag);
+ do_u32b(&dungeon_flags2, flag);
+
+ /* Last teleportation */
+ do_s16b(&last_teleportation_y, flag);
+ do_s16b(&last_teleportation_y, flag);
+
+ /* Spell effects */
+ tmp16b = MAX_EFFECTS;
+ do_u16b(&tmp16b, flag);
+
+ if ((flag == LS_LOAD) && (tmp16b > MAX_EFFECTS))
+ {
+ quit("Too many spell effects");
+ }
+
+ for (i = 0; i < tmp16b; ++i)
+ {
+ do_s16b(&effects[i].type, flag);
+ do_s16b(&effects[i].dam, flag);
+ do_s16b(&effects[i].time, flag);
+ do_u32b(&effects[i].flags, flag);
+ do_s16b(&effects[i].cx, flag);
+ do_s16b(&effects[i].cy, flag);
+ do_s16b(&effects[i].rad, flag);
+ }
+
+ /* TO prevent bugs with evolving dungeons */
+ for (i = 0; i < 100; i++)
+ {
+ do_s16b(&floor_type[i], flag);
+ do_s16b(&fill_type[i], flag);
+ }
+
+ if ((flag == LS_LOAD) && (!dun_level && !p_ptr->inside_quest))
+ {
+ int xstart = 0;
+ int ystart = 0;
+ /* Init the wilderness */
+ process_dungeon_file("w_info.txt", &ystart, &xstart, cur_hgt, cur_wid,
+ TRUE, FALSE);
+
+ /* Init the town */
+ xstart = 0;
+ ystart = 0;
+ init_flags = 0;
+ process_dungeon_file("t_info.txt", &ystart, &xstart, cur_hgt, cur_wid,
+ TRUE, FALSE);
+ }
+
+ do_grid(flag);
+
+ /*** Objects ***/
+
+ if (flag == LS_SAVE) compact_objects(0);
+ if (flag == LS_SAVE) compact_monsters(0);
+ if (flag == LS_SAVE)
+ {
+ tmp16b = o_max;
+
+ if (no_companions)
+ {
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ if (o_ptr->held_m_idx && (m_list[o_ptr->held_m_idx].status == MSTATUS_COMPANION)) tmp16b--;
+ }
+ }
+
+ /* Item count */
+ do_u16b(&tmp16b, flag);
+
+ tmp16b = o_max;
+ }
+ else
+ /* Read item count */
+ do_u16b(&tmp16b, flag);
+
+ /* Verify maximum */
+ if ((flag == LS_LOAD) && (tmp16b > max_o_idx))
+ {
+ note(format("Too many (%d) object entries!", tmp16b));
+ return (FALSE);
+ }
+
+ /* Dungeon items */
+ for (i = 1; i < tmp16b; i++)
+ {
+ int o_idx;
+
+ object_type *o_ptr;
+
+ if (flag == LS_SAVE)
+ {
+ o_ptr = &o_list[i];
+ /* Don't save objects held by companions when no_companions is set */
+ if (no_companions && o_ptr->held_m_idx && (m_list[o_ptr->held_m_idx].status == MSTATUS_COMPANION)) continue;
+
+ do_item(o_ptr, LS_SAVE);
+ continue; /* Saving is easy */
+ }
+ /* Until the end of the loop, this is all LS_LOAD */
+
+ /* Get a new record */
+ o_idx = o_pop();
+
+ /* Oops */
+ if (i != o_idx)
+ {
+ note(format("Object allocation error (%d <> %d)", i, o_idx));
+ return (FALSE);
+ }
+
+
+ /* Acquire place */
+ o_ptr = &o_list[o_idx];
+
+ /* Read the item */
+ do_item(o_ptr, LS_LOAD);
+
+ /* Monster */
+ if (o_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Monster */
+ m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ /* Place the object */
+ m_ptr->hold_o_idx = o_idx;
+ }
+
+ /* Dungeon */
+ else
+ {
+ /* Access the item location */
+ c_ptr = &cave[o_ptr->iy][o_ptr->ix];
+
+ /* Build a stack */
+ o_ptr->next_o_idx = c_ptr->o_idx;
+
+ /* Place the object */
+ c_ptr->o_idx = o_idx;
+ }
+ }
+
+ /*** Monsters ***/
+
+ if (flag == LS_SAVE)
+ {
+ tmp16b = m_max;
+
+ if (no_companions)
+ {
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ if (m_ptr->status == MSTATUS_COMPANION) tmp16b--;
+ }
+ }
+
+ /* Write the monster count */
+ do_u16b(&tmp16b, flag);
+
+ tmp16b = m_max;
+ }
+ else
+ /* Read the monster count */
+ do_u16b(&tmp16b, flag);
+
+ /* Validate */
+ if ((flag == LS_LOAD) && (tmp16b > max_m_idx))
+ {
+ note(format("Too many (%d) monster entries!", tmp16b));
+ return (FALSE);
+ }
+
+ /* Read the monsters */
+ for (i = 1; i < tmp16b; i++)
+ {
+ int m_idx;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ if (flag == LS_SAVE)
+ {
+ m_ptr = &m_list[i];
+
+ /* Don't save companions when no_companions is set */
+ if (no_companions && m_ptr->status == MSTATUS_COMPANION) continue;
+
+ do_monster(m_ptr, LS_SAVE);
+ continue; /* Easy to save a monster */
+ }
+ /* From here on, it's all LS_LOAD */
+ /* Get a new record */
+ m_idx = m_pop();
+
+ /* Oops */
+ if (i != m_idx)
+ {
+ note(format("Monster allocation error (%d <> %d)", i, m_idx));
+ return (FALSE);
+ }
+
+ /* Acquire monster */
+ m_ptr = &m_list[m_idx];
+
+ /* Read the monster */
+ do_monster(m_ptr, LS_LOAD);
+
+ /* Access grid */
+ c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+
+ /* Mark the location */
+ c_ptr->m_idx = m_idx;
+
+ /* Controlled ? */
+ if (m_ptr->mflag & MFLAG_CONTROL)
+ p_ptr->control = m_idx;
+
+ /* Access race */
+ r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Count XXX XXX XXX */
+ r_ptr->cur_num++;
+ }
+
+ /* Read the kept monsters */
+
+ tmp16b = (flag == LS_SAVE && !no_companions) ? max_m_idx : 0;
+
+ /* Read the monster count */
+ do_u16b(&tmp16b, flag);
+
+ /* Hack -- verify */
+ if ((flag == LS_LOAD) && (tmp16b > max_m_idx))
+ {
+ note(format("Too many (%d) monster entries!", tmp16b));
+ return (FALSE);
+ }
+ for (i = 1; i < tmp16b; i++)
+ {
+ monster_type *m_ptr;
+
+ /* Acquire monster */
+ m_ptr = &km_list[i];
+
+ /* Read the monster */
+ do_monster(m_ptr, flag);
+ }
+
+ /*** Success ***/
+
+ /* The dungeon is ready */
+ if (flag == LS_LOAD) character_dungeon = TRUE;
+
+ /* Success */
+ return (TRUE);
+}
+
+/* Returns TRUE if we successfully load the dungeon */
+bool_ load_dungeon(char *ext)
+{
+ char tmp[16];
+ char name[1024];
+ byte old_dungeon_type = dungeon_type;
+ s16b old_dun = dun_level;
+
+ /* Construct name */
+ sprintf(tmp, "%s.%s", player_base, ext);
+ path_build(name, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Open the file */
+ fff = my_fopen(name, "rb");
+
+ if (fff == NULL)
+ {
+ dun_level = old_dun;
+ dungeon_type = old_dungeon_type;
+
+ my_fclose(fff);
+ return (FALSE);
+ }
+
+ /* Read the dungeon */
+ if (!do_dungeon(LS_LOAD, FALSE))
+ {
+ dun_level = old_dun;
+ dungeon_type = old_dungeon_type;
+
+ my_fclose(fff);
+ return (FALSE);
+ }
+
+ dun_level = old_dun;
+ dungeon_type = old_dungeon_type;
+
+ /* Done */
+ my_fclose(fff);
+ return (TRUE);
+}
+
+void do_blocks(int flag)
+/* Handle blocked-allocation stuff for quests and lua stuff
+ This depends on a dyn_tosave array of s32b's. Adjust as needed
+ if other data structures are desirable. This also is not hooked
+ in yet. Ideally, plug it near the end of the savefile.
+ */
+{
+ s16b numblks, n_iter = 0; /* How many blocks do we have? */
+ do_s16b(&numblks, flag);
+ while (n_iter < numblks)
+ {
+ /* do_s32b(dyn_tosave[n_iter], flag); */
+ n_iter++;
+ }
+ my_sentinel("In blocked-allocation area", 37, flag);
+}
+
+void do_fate(int i, int flag)
+{
+ if ((flag == LS_LOAD) && (i >= MAX_FATES)) i = MAX_FATES - 1;
+
+ do_byte(&fates[i].fate, flag);
+ do_byte(&fates[i].level, flag);
+ do_byte(&fates[i].serious, flag);
+ do_s16b(&fates[i].o_idx, flag);
+ do_s16b(&fates[i].e_idx, flag);
+ do_s16b(&fates[i].a_idx, flag);
+ do_s16b(&fates[i].v_idx, flag);
+ do_s16b(&fates[i].r_idx, flag);
+ do_s16b(&fates[i].count, flag);
+ do_s16b(&fates[i].time, flag);
+ do_byte((byte*)&fates[i].know, flag);
+}
+
+/*
+ * Actually read the savefile
+ */
+static bool_ do_savefile_aux(int flag)
+{
+ int i, j;
+
+ byte tmp8u;
+ u16b tmp16u;
+ u32b tmp32u;
+
+ bool_ *reals;
+ u16b real_max = 0;
+
+ /* Mention the savefile version */
+ if (flag == LS_LOAD)
+ {
+ if (vernum < 100)
+ {
+ note(format("Savefile version %lu too old! ", vernum));
+ return FALSE;
+ }
+ else
+ {
+ note(format("Loading version %lu savefile... ", vernum));
+ }
+ }
+ if (flag == LS_SAVE)
+ {
+ sf_when = time((time_t *) 0); /* Note when file was saved */
+ sf_xtra = 0L; /* What the hell is this? */
+ sf_saves++; /* Increment the saves ctr */
+ }
+
+ /* Handle version bytes. FIXME! DG wants me to change this all around */
+ if (flag == LS_LOAD)
+ {
+ u32b mt32b;
+ byte mtbyte;
+
+ /* Discard all this, we've already read it */
+ do_u32b(&mt32b, flag);
+ do_byte(&mtbyte, flag);
+ }
+ if (flag == LS_SAVE)
+ {
+ u32b saver;
+ saver = SAVEFILE_VERSION;
+ do_u32b(&saver, flag);
+ tmp8u = (byte)rand_int(256);
+ do_byte(&tmp8u, flag); /* 'encryption' */
+ }
+
+ /* Operating system info? Not really. This is just set to 0L */
+ do_u32b(&sf_xtra, flag);
+
+ /* Time of last save */
+ do_u32b(&sf_when, flag);
+
+ /* Number of past lives */
+ do_u16b(&sf_lives, flag);
+
+ /* Number of times saved */
+ do_u16b(&sf_saves, flag);
+
+ /* Game module */
+ if (flag == LS_SAVE)
+ strcpy(loaded_game_module, game_module);
+ do_string(loaded_game_module, 80, flag);
+
+ /* Read RNG state */
+ do_randomizer(flag);
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Randomizer Info");
+
+ /* Automatizer state */
+ do_byte((byte*)&automatizer_enabled, flag);
+
+ /* Then the options */
+ do_options(flag);
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Option Flags");
+
+ /* Then the "messages" */
+ do_messages(flag);
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Messages");
+
+ /* Monster Memory */
+ if (flag == LS_SAVE) tmp16u = max_r_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > max_r_idx))
+ {
+ note(format("Too many (%u) monster races!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the available records */
+ for (i = 0; i < tmp16u; i++)
+ {
+ /* Read the lore */
+ do_lore(i, flag);
+ }
+
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Monster Memory");
+ /* Object Memory */
+ if (flag == LS_SAVE) tmp16u = max_k_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > max_k_idx))
+ {
+ note(format("Too many (%u) object kinds!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the object memory */
+ for (i = 0; i < tmp16u; i++) do_xtra(i, flag);
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Object Memory");
+ if (flag == LS_LOAD) junkinit();
+
+ {
+ u16b max_towns_ldsv;
+ u16b max_quests_ldsv;
+ if (flag == LS_SAVE) max_towns_ldsv = max_towns;
+ /* Number of towns */
+ do_u16b(&max_towns_ldsv, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_towns_ldsv > max_towns))
+ {
+ note(format("Too many (%u) towns!", max_towns_ldsv));
+ return (FALSE);
+ }
+ /* Min of random towns */
+ if (flag == LS_SAVE) max_towns_ldsv = TOWN_RANDOM;
+ do_u16b(&max_towns_ldsv, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_towns_ldsv != TOWN_RANDOM))
+ {
+ note(format("Different random towns base (%u)!", max_towns_ldsv));
+ return (FALSE);
+ }
+
+ for (i = 0; i < max_towns; i++)
+ {
+ do_byte((byte*)&town_info[i].destroyed, flag);
+
+ if (i >= TOWN_RANDOM)
+ {
+ do_u32b(&town_info[i].seed, flag);
+ do_byte(&town_info[i].numstores, flag);
+ do_byte(&town_info[i].flags, flag);
+
+ /* If the town is realy used create a sock */
+ if ((town_info[i].flags & (TOWN_REAL)) && (flag == LS_LOAD))
+ {
+ create_stores_stock(i);
+ }
+ }
+ }
+
+ /* Number of dungeon */
+ if (flag == LS_SAVE) max_towns_ldsv = max_d_idx;
+ do_u16b(&max_towns_ldsv, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_towns_ldsv > max_d_idx))
+ {
+ note(format("Too many dungeon types (%u)!", max_towns_ldsv));
+ return (FALSE);
+ }
+
+ /* Number of towns per dungeon */
+ if (flag == LS_SAVE) max_quests_ldsv = TOWN_DUNGEON;
+ do_u16b(&max_quests_ldsv, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_quests_ldsv > TOWN_DUNGEON))
+ {
+ note(format("Too many town per dungeons (%u)!", max_quests_ldsv));
+ return (FALSE);
+ }
+
+ for (i = 0; i < max_towns_ldsv; i++)
+ {
+ for (j = 0; j < max_quests_ldsv; j++)
+ {
+ do_s16b(&(d_info[i].t_idx[j]), flag);
+ do_s16b(&(d_info[i].t_level[j]), flag);
+ }
+ do_s16b(&(d_info[i].t_num), flag);
+ }
+
+ if (flag == LS_SAVE) max_quests_ldsv = MAX_Q_IDX_INIT;
+ /* Number of quests */
+ do_u16b(&max_quests_ldsv, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_quests_ldsv > MAX_Q_IDX_INIT))
+ {
+ note(format("Too many (%u) quests!", max_quests_ldsv));
+ return (FALSE);
+ }
+
+ for (i = 0; i < max_quests_ldsv; i++)
+ {
+ do_s16b(&quest[i].status, flag);
+ for (j = 0; j < 4; j++)
+ {
+ do_s32b(&(quest[i].data[j]), flag);
+ }
+
+ /* Init the hooks */
+ if ((flag == LS_LOAD) && (quest[i].type == HOOK_TYPE_C)) quest[i].init(i);
+ }
+
+ /* Position in the wilderness */
+ do_s32b(&p_ptr->wilderness_x, flag);
+ do_s32b(&p_ptr->wilderness_y, flag);
+ do_byte((byte*)&p_ptr->wild_mode, flag);
+ do_byte((byte*)&p_ptr->old_wild_mode, flag);
+
+ {
+ s32b wild_x_size, wild_y_size;
+ if (flag == LS_SAVE)
+ {
+ wild_x_size = max_wild_x;
+ wild_y_size = max_wild_y;
+ }
+ /* Size of the wilderness */
+ do_s32b(&wild_x_size, flag);
+ do_s32b(&wild_y_size, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) &&
+ ((wild_x_size > max_wild_x) || (wild_y_size > max_wild_y)))
+ {
+ note(format("Wilderness is too big (%u/%u)!",
+ wild_x_size, wild_y_size));
+ return (FALSE);
+ }
+ /* Wilderness seeds */
+ for (i = 0; i < wild_x_size; i++)
+ {
+ for (j = 0; j < wild_y_size; j++)
+ {
+ do_u32b(&wild_map[j][i].seed, flag);
+ do_u16b(&wild_map[j][i].entrance, flag);
+ do_byte((byte*)&wild_map[j][i].known, flag);
+ }
+ }
+ }
+ }
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Quests");
+
+ /* Load the random artifacts. */
+ if (flag == LS_SAVE) tmp16u = MAX_RANDARTS;
+ do_u16b(&tmp16u, flag);
+ if ((flag == LS_LOAD) && (tmp16u > MAX_RANDARTS))
+ {
+ note(format("Too many (%u) random artifacts!", tmp16u));
+ return (FALSE);
+ }
+ for (i = 0; i < tmp16u; i++)
+ {
+ random_artifact *ra_ptr = &random_artifacts[i];
+
+ do_string(ra_ptr->name_full, 80, flag);
+ do_string(ra_ptr->name_short, 80, flag);
+ do_byte(&ra_ptr->level, flag);
+ do_byte(&ra_ptr->attr, flag);
+ do_u32b(&ra_ptr->cost, flag);
+ do_byte(&ra_ptr->activation, flag);
+ do_byte(&ra_ptr->generated, flag);
+ }
+
+ /* Load the Artifacts */
+ if (flag == LS_SAVE) tmp16u = max_a_idx;
+ do_u16b(&tmp16u, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > max_a_idx))
+ {
+ note(format("Too many (%u) artifacts!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the artifact flags */
+ for (i = 0; i < tmp16u; i++)
+ {
+ do_byte(&(&a_info[i])->cur_num, flag);
+ }
+ if ((flag == LS_LOAD) && arg_fiddle) note("Loaded Artifacts");
+
+ /* Fates */
+ if (flag == LS_SAVE) tmp16u = MAX_FATES;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > MAX_FATES))
+ {
+ note(format("Too many (%u) fates!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the fate flags */
+ for (i = 0; i < tmp16u; i++)
+ {
+ do_fate(i, flag);
+ }
+ if ((flag == LS_LOAD) && arg_fiddle) note("Loaded Fates");
+
+ /* Load the Traps */
+ if (flag == LS_SAVE) tmp16u = max_t_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > max_t_idx))
+ {
+ note(format("Too many (%u) traps!", tmp16u));
+ return (FALSE);
+ }
+
+ /* fate flags */
+ for (i = 0; i < tmp16u; i++)
+ {
+ do_byte((byte*)&t_info[i].ident, flag);
+ }
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Traps");
+
+ /* inscription knowledge */
+ if (flag == LS_SAVE) tmp16u = MAX_INSCRIPTIONS;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > MAX_INSCRIPTIONS))
+ {
+ note(format("Too many (%u) inscriptions!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the inscription flag */
+ for (i = 0; i < tmp16u; i++)
+ do_byte((byte*)&inscription_info[i].know, flag);
+ if ((flag == LS_LOAD) && arg_fiddle) note("Loaded Inscriptions");
+
+
+ /* Read the extra stuff */
+ if (!do_extra(flag))
+ return FALSE;
+ if ((flag == LS_LOAD) && arg_fiddle) note("Loaded extra information");
+
+
+ /* player_hp array */
+ if (flag == LS_SAVE) tmp16u = PY_MAX_LEVEL;
+ do_u16b(&tmp16u, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > PY_MAX_LEVEL))
+ {
+ note(format("Too many (%u) hitpoint entries!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the player_hp array */
+ for (i = 0; i < tmp16u; i++)
+ {
+ do_s16b(&player_hp[i], flag);
+ }
+
+ if (flag == LS_LOAD) morejunk();
+
+ /* Read the pet command settings */
+ do_byte(&p_ptr->pet_follow_distance, flag);
+ do_byte(&p_ptr->pet_open_doors, flag);
+ do_byte(&p_ptr->pet_pickup_items, flag);
+
+ /* Read the inventory */
+ if (!do_inventory(flag) && (flag == LS_LOAD)) /* do NOT reverse this ordering */
+ {
+ note("Unable to read inventory");
+ return (FALSE);
+ }
+
+ /* Note that this forbids max_towns from shrinking, but that is fine */
+ C_MAKE(reals, max_towns, bool_);
+
+ /* Find the real towns */
+ if (flag == LS_SAVE)
+ {
+ for (i = 1; i < max_towns; i++)
+ {
+ if (!(town_info[i].flags & (TOWN_REAL))) continue;
+ reals[real_max++] = i;
+ }
+ }
+ do_u16b(&real_max, flag);
+ for (i = 0; i < real_max; i++)
+ {
+ do_byte((byte*)&reals[i], flag);
+ }
+
+ /* Read the stores */
+ if (flag == LS_SAVE) tmp16u = max_st_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Ok now read the real towns */
+ for (i = 0; i < real_max; i++)
+ {
+ int z = reals[i];
+
+ /* Ultra paranoia */
+ if (!town_info[z].stocked) create_stores_stock(z);
+
+ for (j = 0; j < tmp16u; j++)
+ do_store(&town_info[z].store[j], flag);
+ }
+
+ C_FREE(reals, max_towns, bool_);
+
+ if (flag == LS_SAVE) tmp32u = extra_savefile_parts;
+ do_u32b(&tmp32u, flag);
+ if (flag == LS_SAVE)
+ {
+ /* Save the stuff */
+ process_hooks(HOOK_SAVE_GAME, "()");
+ }
+
+ if (flag == LS_LOAD)
+ {
+ u32b len = tmp32u;
+
+ while (len)
+ {
+ char key_buf[100];
+
+ /* Load a key */
+ load_number_key(key_buf, &tmp32u);
+
+ /* Process it -- the hooks can use it or ignore it */
+ process_hooks(HOOK_LOAD_GAME, "(s,l)", key_buf, tmp32u);
+ len--;
+ }
+ }
+
+ /* I'm not dead yet... */
+ if (!death)
+ {
+ /* Dead players have no dungeon */
+ if (flag == LS_LOAD) note("Restoring Dungeon...");
+ if ((flag == LS_LOAD) && (!do_dungeon(LS_LOAD, FALSE)))
+ {
+ note("Error reading dungeon data");
+ return (FALSE);
+ }
+ if (flag == LS_SAVE) do_dungeon(LS_SAVE, FALSE);
+ my_sentinel("Before ghost data", 435, flag);
+ my_sentinel("After ghost data", 320, flag);
+ }
+
+ {
+ byte foo = 0;
+ if (flag == LS_SAVE)
+ {
+ /*
+ * Safety Padding. It's there
+ * for a good reason. Trust me on
+ * this. Keep this at the *END*
+ * of the file, and do *NOT* try to
+ * read it. Insert any new stuff before
+ * this position.
+ */
+ do_byte(&foo, LS_SAVE);
+ }
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Actually read the savefile
+ */
+errr rd_savefile(void)
+{
+ errr err = 0;
+
+ /* The savefile is a binary file */
+ fff = my_fopen(savefile, "rb");
+
+ /* Paranoia */
+ if (!fff) return ( -1);
+
+ /* Call the sub-function */
+ err = !do_savefile_aux(LS_LOAD);
+
+ /* Check for errors */
+ if (ferror(fff)) err = -1;
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Result */
+ return (err);
+}
+
+/*
+ * Note that this function may not be needed at all.
+ * It was taken out of load_player_aux(). Do we need it?
+ */
+static void junkinit(void)
+{
+ int i, j;
+ p_ptr->arena_number = 0;
+ p_ptr->inside_arena = 0;
+ p_ptr->inside_quest = 0;
+ p_ptr->exit_bldg = TRUE;
+ p_ptr->exit_bldg = TRUE;
+ p_ptr->town_num = 1;
+ p_ptr->wilderness_x = 4;
+ p_ptr->wilderness_y = 4;
+ for (i = 0; i < max_wild_x; i++)
+ {
+ for (j = 0; j < max_wild_y; j++)
+ {
+ wild_map[j][i].seed = rand_int(0x10000000);
+ }
+ }
+}
+
+static void morejunk(void)
+{
+ sp_ptr = &sex_info[p_ptr->psex]; /* Sex */
+ rp_ptr = &race_info[p_ptr->prace]; /* Raceclass */
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+ cp_ptr = &class_info[p_ptr->pclass];
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+}
+
+static void do_grid(int flag)
+/* Does the grid, RLE, blahblah. RLE sucks. I hate it. */
+{
+ int i = 0, y = 0, x = 0;
+ byte count = 0;
+ byte tmp8u = 0;
+ s16b tmp16s = 0;
+ cave_type *c_ptr;
+ byte prev_char = 0;
+ s16b prev_s16b = 0;
+ int ymax = cur_hgt, xmax = cur_wid;
+
+ int part; /* Which section of the grid we're on */
+
+ for (part = 0; part < 9; part++) /* There are 8 fields to the grid, each stored
+ in a seperate RLE data structure */
+ {
+ if (flag == LS_SAVE)
+ {
+ count = 0;
+ prev_s16b = 0;
+ prev_char = 0; /* Clear, prepare for RLE */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ c_ptr = &cave[y][x];
+ switch (part)
+ {
+ case 0:
+ tmp16s = c_ptr->info;
+ break;
+
+ case 1:
+ tmp8u = c_ptr->feat;
+ break;
+
+ case 2:
+ tmp8u = c_ptr->mimic;
+ break;
+
+ case 3:
+ tmp16s = c_ptr->special;
+ break;
+
+ case 4:
+ tmp16s = c_ptr->special2;
+ break;
+
+ case 5:
+ tmp16s = c_ptr->t_idx;
+ break;
+
+ case 6:
+ tmp16s = c_ptr->inscription;
+ break;
+
+ case 7:
+ tmp8u = c_ptr->mana;
+ break;
+
+ case 8:
+ tmp16s = c_ptr->effect;
+ break;
+ }
+ /* Flush a full run */
+ if ((((part != 1) && (part != 2) && (part != 7)) &&
+ (tmp16s != prev_s16b)) || (((part == 1) || (part == 2)
+ || (part == 7)) &&
+ (tmp8u != prev_char)) ||
+ (count == MAX_UCHAR))
+ {
+ do_byte(&count, LS_SAVE);
+ switch (part)
+ {
+ case 0:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ do_s16b(&prev_s16b, LS_SAVE);
+ prev_s16b = tmp16s;
+ break;
+
+ case 1:
+ case 2:
+ case 7:
+ do_byte(&prev_char, LS_SAVE);
+ prev_char = tmp8u;
+ break;
+ }
+ count = 1; /* Reset RLE */
+ }
+ else
+ count++; /* Otherwise, keep going */
+ }
+ }
+ /* Fallen off the end of the world, flush anything left */
+ if (count)
+ {
+ do_byte(&count, LS_SAVE);
+ switch (part)
+ {
+ case 0:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ do_s16b(&prev_s16b, LS_SAVE);
+ break;
+
+ case 1:
+ case 2:
+ case 7:
+ do_byte(&prev_char, LS_SAVE);
+ break;
+ }
+ }
+ }
+ if (flag == LS_LOAD)
+ {
+ x = 0;
+ for (y = 0; y < ymax; )
+ {
+ do_byte(&count, LS_LOAD);
+ switch (part)
+ {
+ case 0:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ do_s16b(&tmp16s, LS_LOAD);
+ break;
+
+ case 1:
+ case 2:
+ case 7:
+ do_byte(&tmp8u, LS_LOAD);
+ break;
+ }
+ for (i = count; i > 0; i--) /* RLE */
+ {
+ c_ptr = &cave[y][x];
+ switch (part)
+ {
+ case 0:
+ c_ptr->info = tmp16s;
+ break;
+
+ case 1:
+ c_ptr->feat = tmp8u;
+ break;
+
+ case 2:
+ c_ptr->mimic = tmp8u;
+ break;
+
+ case 3:
+ c_ptr->special = tmp16s;
+ break;
+
+ case 4:
+ c_ptr->special2 = tmp16s;
+ break;
+
+ case 5:
+ c_ptr->t_idx = tmp16s;
+ break;
+
+ case 6:
+ c_ptr->inscription = tmp16s;
+ break;
+
+ case 7:
+ c_ptr->mana = tmp8u;
+ break;
+
+ case 8:
+ c_ptr->effect = tmp16s;
+ break;
+ }
+ if (++x >= xmax)
+ {
+ /* Wrap */
+ x = 0;
+ if ((++y) >= ymax) break;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void my_sentinel(char *place, u16b value, int flag)
+/* This function lets us know exactly where a savefile is
+ broken by reading/writing conveniently a sentinel at this
+ spot */
+{
+ if (flag == LS_SAVE)
+ {
+ do_u16b(&value, flag);
+ return;
+ }
+ if (flag == LS_LOAD)
+ {
+ u16b found;
+ do_u16b(&found, flag);
+ if (found == value) /* All is good */
+ return;
+ /* All is bad */
+ note(format("Savefile broken %s", place));
+ return;
+ }
+ note(format("Impossible has occurred")); /* Programmer error */
+ exit(0);
+}
+
+/********** Variable savefile stuff **************/
+
+/*
+ * Add num slots to the savefile
+ */
+void register_savefile(int num)
+{
+ extra_savefile_parts += (num > 0) ? num : 0;
+}
+
+void save_number_key(char *key, u32b val)
+{
+ byte len = strlen(key);
+
+ do_byte(&len, LS_SAVE);
+ while (*key)
+ {
+ do_byte((byte*)key, LS_SAVE);
+ key++;
+ }
+ do_u32b(&val, LS_SAVE);
+}
+
+void load_number_key(char *key, u32b *val)
+{
+ byte len, i = 0;
+
+ do_byte(&len, LS_LOAD);
+ while (i < len)
+ {
+ do_byte((byte*)&key[i], LS_LOAD);
+ i++;
+ }
+ key[i] = '\0';
+ do_u32b(val, LS_LOAD);
+}
diff --git a/src/lua/.cvsignore b/src/lua/.cvsignore
new file mode 100644
index 00000000..0b086004
--- /dev/null
+++ b/src/lua/.cvsignore
@@ -0,0 +1,2 @@
+tolua
+host
diff --git a/src/lua/.gitignore b/src/lua/.gitignore
new file mode 100644
index 00000000..26548e17
--- /dev/null
+++ b/src/lua/.gitignore
@@ -0,0 +1,2 @@
+/liblua.a
+/tolua
diff --git a/src/lua/CMakeLists.txt b/src/lua/CMakeLists.txt
new file mode 100644
index 00000000..df2b30e7
--- /dev/null
+++ b/src/lua/CMakeLists.txt
@@ -0,0 +1,11 @@
+ADD_LIBRARY (lua STATIC
+ lapi.c lcode.c ldebug.c ldo.c lfunc.c lgc.c
+ llex.c lmem.c lobject.c lparser.c lstate.c lstring.c
+ ltable.c ltests.c ltm.c lundump.c lvm.c lzio.c
+ lauxlib.c lbaselib.c ldblib.c liolib.c lstrlib.c
+ tolua_lb.c tolua_rg.c tolua_tt.c tolua_tm.c tolua_gp.c
+ tolua_eh.c tolua_bd.c)
+
+ADD_EXECUTABLE(tolua tolua.c tolualua.c lua)
+
+TARGET_LINK_LIBRARIES(tolua lua)
diff --git a/src/lua/array.lua b/src/lua/array.lua
new file mode 100644
index 00000000..7929f8cd
--- /dev/null
+++ b/src/lua/array.lua
@@ -0,0 +1,203 @@
+-- tolua: array class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1999
+-- $Id: array.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Array class
+-- Represents a extern array variable or a public member of a class.
+-- Stores all fields present in a declaration.
+classArray = {
+ _base = classDeclaration,
+}
+
+settag(classArray,tolua_tag)
+
+-- Print method
+function classArray:print (ident,close)
+ print(ident.."Array{")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." def = '"..self.def.."',")
+ print(ident.." dim = '"..self.dim.."',")
+ print(ident.." ret = '"..self.ret.."',")
+ print(ident.."}"..close)
+end
+
+-- get variable value
+function classArray:getvalue (class,static)
+ if class and static then
+ return class..'::'..self.name..'[toluaI_index]'
+ elseif class then
+ return 'self->'..self.name..'[toluaI_index]'
+ else
+ return self.name..'[toluaI_index]'
+ end
+end
+
+-- Write binding functions
+function classArray:supcode ()
+ local class = self:inclass()
+
+ -- get function ------------------------------------------------
+ if class then
+ output("/* get function:",self.name," of class ",class," */")
+ else
+ output("/* get function:",self.name," */")
+ end
+ self.cgetname = self:cfuncname("toluaI_get")
+ output("static int",self.cgetname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- declare index
+ output(' int toluaI_index;')
+
+ -- declare self, if the case
+ local _,_,static = strfind(self.mod,'^%s*(static)')
+ if class and static==nil then
+ output(' ',class,'*','self;')
+ output(' lua_pushstring(tolua_S,".self");')
+ output(' lua_rawget(tolua_S,1);')
+ output(' self = ')
+ output('(',class,'*) ')
+ output('lua_touserdata(tolua_S,-1);')
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ end
+
+ -- check index
+ output(' if (!tolua_istype(tolua_S,2,LUA_TNUMBER,0))')
+ output(' tolua_error(tolua_S,"invalid type in array indexing.");')
+ output(' toluaI_index = (int)tolua_getnumber(tolua_S,2,0)-1;')
+ output(' if (toluaI_index<0 || toluaI_index>='..self.dim..')')
+ output(' tolua_error(tolua_S,"array indexing out of range.");')
+
+ -- return value
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_push'..t..'(tolua_S,(',ct,')'..self:getvalue(class,static)..');')
+ else
+ if self.ptr == '&' or self.ptr == '' then
+ output(' tolua_pushusertype(tolua_S,(void*)&'..self:getvalue(class,static)..',',self.tag,');')
+ else
+ output(' tolua_pushusertype(tolua_S,(void*)'..self:getvalue(class,static)..',',self.tag,');')
+ end
+ end
+ output(' return 1;')
+ output('}')
+ output('\n')
+
+ -- set function ------------------------------------------------
+ if not strfind(self.mod,'const') then
+ if class then
+ output("/* set function:",self.name," of class ",class," */")
+ else
+ output("/* set function:",self.name," */")
+ end
+ self.csetname = self:cfuncname("toluaI_set")
+ output("static int",self.csetname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- declare index
+ output(' int toluaI_index;')
+
+ -- declare self, if the case
+ local _,_,static = strfind(self.mod,'^%s*(static)')
+ if class and static==nil then
+ output(' ',class,'*','self;')
+ output(' lua_pushstring(tolua_S,".self");')
+ output(' lua_rawget(tolua_S,1);')
+ output(' self = ')
+ output('(',class,'*) ')
+ output('lua_touserdata(tolua_S,-1);')
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ end
+
+ -- check index
+ output(' if (!tolua_istype(tolua_S,2,LUA_TNUMBER,0))')
+ output(' tolua_error(tolua_S,"invalid type in array indexing.");')
+ output(' toluaI_index = (int)tolua_getnumber(tolua_S,2,0)-1;')
+ output(' if (toluaI_index<0 || toluaI_index>='..self.dim..')')
+ output(' tolua_error(tolua_S,"array indexing out of range.");')
+
+ -- assign value
+ local ptr = ''
+ if self.ptr~='' then ptr = '*' end
+ output(' ')
+ if class and static then
+ output(class..'::'..self.name..'[toluaI_index]')
+ elseif class then
+ output('self->'..self.name..'[toluaI_index]')
+ else
+ output(self.name..'[toluaI_index]')
+ end
+ local t = isbasic(self.type)
+ output(' = ')
+ if not t and ptr=='' then output('*') end
+ output('((',self.mod,self.type)
+ if not t then
+ output('*')
+ end
+ output(') ')
+ local def = 0
+ if self.def ~= '' then def = self.def end
+ if t then
+ output('tolua_get'..t,'(tolua_S,3,',def,'));')
+ else
+ output('tolua_getusertype(tolua_S,3,',def,'));')
+ end
+ output(' return 0;')
+ output('}')
+ output('\n')
+ end
+
+end
+
+function classArray:register ()
+ local parent = self:inclass() or self:inmodule()
+ if parent then
+ if self.csetname then
+ output(' tolua_tablearray(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..','..self.csetname..');')
+ else
+ output(' tolua_tablearray(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..',NULL);')
+ end
+ else
+ if self.csetname then
+ output(' tolua_globalarray(tolua_S,"'..self.lname..'",'..self.cgetname..','..self.csetname..');')
+ else
+ output(' tolua_globalarray(tolua_S,"'..self.lname..'",'..self.cgetname..',NULL);')
+ end
+ end
+end
+
+function classArray:unregister ()
+ if self:inclass()==nil and self:inmodule()==nil then
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lname..'");')
+ end
+end
+
+
+-- Internal constructor
+function _Array (t)
+ t._base = classArray
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the variable declaration.
+function Array (s)
+ return _Array (Declaration(s,'var'))
+end
+
+
diff --git a/src/lua/basic.lua b/src/lua/basic.lua
new file mode 100644
index 00000000..2bac463f
--- /dev/null
+++ b/src/lua/basic.lua
@@ -0,0 +1,190 @@
+-- tolua: basic utility functions
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: basic.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Basic C types and their corresponding Lua types
+-- All occurrences of "char*" will be replaced by "_cstring",
+-- and all occurrences of "void*" will be replaced by "_userdata"
+_basic = {
+ ['void'] = '',
+ ['char'] = 'number',
+ ['int'] = 'number',
+ ['short'] = 'number',
+ ['long'] = 'number',
+ ['_cstring'] = 'string',
+ ['_userdata'] = 'userdata',
+ ['char*'] = 'string',
+ ['void*'] = 'userdata',
+ ['bool'] = 'bool',
+ ['LUA_VALUE'] = 'value',
+ ['byte'] = 'number',
+ ['s16b'] = 'number',
+ ['u16b'] = 'number',
+ ['s32b'] = 'number',
+ ['u32b'] = 'number',
+}
+
+_basic_tag = {
+ ['void'] = '',
+ ['char'] = 'LUA_TNUMBER',
+ ['int'] = 'LUA_TNUMBER',
+ ['short'] = 'LUA_TNUMBER',
+ ['long'] = 'LUA_TNUMBER',
+ ['_cstring'] = 'LUA_TSTRING',
+ ['_userdata'] = 'LUA_TUSERDATA',
+ ['char*'] = 'LUA_TSTRING',
+ ['void*'] = 'LUA_TUSERDATA',
+ ['bool'] = 'tolua_tag(tolua_S,"bool")',
+ ['byte'] = 'LUA_TNUMBER',
+ ['s16b'] = 'LUA_TNUMBER',
+ ['u16b'] = 'LUA_TNUMBER',
+ ['s32b'] = 'LUA_TNUMBER',
+ ['u32b'] = 'LUA_TNUMBER',
+}
+
+_basic_ctype = {
+ number = "long",
+ string = "const char*",
+ userdata = "void*",
+ bool = "int",
+}
+
+-- List of user defined types
+-- Each type corresponds to a variable name that stores its tag value.
+_usertype = {}
+
+-- Tag method to provide inheritance
+function tolua_index (t,f)
+ if f == '_base' then -- to avoid loop
+ return tolua_old_index(t,f)
+ else
+ return t._base[f]
+ end
+end
+
+tolua_tag = newtag()
+tolua_old_index = settagmethod(tolua_tag,"index",tolua_index)
+
+-- Error handler
+function tolua_error (s)
+ local out = _OUTPUT
+ _OUTPUT = _STDERR
+ if strsub(s,1,1) == '#' then
+ write("\n** tolua: "..strsub(s,2)..".\n\n")
+ else
+ write("\n** tolua internal error: "..s..".\n\n")
+ return
+ end
+
+ if _curr_code then
+ local _,_,s = strfind(_curr_code,"^%s*(.-\n)") -- extract first line
+ if s==nil then s = _curr_code end
+ s = gsub(s,"_userdata","void*") -- return with 'void*'
+ s = gsub(s,"_cstring","char*") -- return with 'char*'
+ write("Code being processed:\n"..s.."\n")
+ end
+ _OUTPUT = out
+end
+
+
+_ERRORMESSAGE = tolua_error
+
+-- register an user defined type
+function regtype (t)
+ if not istype(t) then
+ _usertype[t] = t
+ end
+ return t
+end
+
+-- return tag name
+function tagvar(type,const)
+ if type == '' or type == 'void' then
+ return type,0
+ else
+ local m,t = findtypedef(type)
+ if isbasic(t) then
+ return t, _basic_tag[t]
+ end
+ if strfind(m,'const') then const = 'const' end
+ regtype(t)
+ if const and const ~= '' then
+ t = 'const '..t
+ end
+ return t,'tolua_tag(tolua_S,"'..t..'")'
+ end
+end
+
+-- check if basic type
+function isbasic (type)
+ local m,t = findtypedef(type)
+ local b = _basic[t]
+ if b then
+ return b,_basic_ctype[b]
+ end
+ return nil
+end
+
+-- check if type
+function istype (t)
+ return _basic[t] or _usertype[t] or istypedef(t)
+end
+
+
+-- split string using a token
+function split (s,t)
+ local l = {n=0}
+ local f = function (s)
+ %l.n = %l.n + 1
+ %l[%l.n] = s
+ end
+ local p = "%s*(.-)%s*"..t.."%s*"
+ s = gsub(s,"^%s+","")
+ s = gsub(s,"%s+$","")
+ s = gsub(s,p,f)
+ l.n = l.n + 1
+ l[l.n] = gsub(s,"(%s%s*)$","")
+ return l
+end
+
+
+-- concatenate strings of a table
+function concat (t,f,l)
+ local s = ''
+ local i=f
+ while i<=l do
+ s = s..t[i]
+ i = i+1
+ if i <= l then s = s..' ' end
+ end
+ return s
+end
+
+-- output line
+function output (...)
+ local i=1
+ while i<=arg.n do
+ if _cont and not strfind(_cont,'[%(,"]') and
+ strfind(arg[i],"^[%a_~]") then
+ write(' ')
+ end
+ write(arg[i])
+ if arg[i] ~= '' then
+ _cont = strsub(arg[i],-1,-1)
+ end
+ i = i+1
+ end
+ if strfind(arg[arg.n],"[%/%)%;%{%}]$") then
+ _cont=nil write('\n')
+ end
+end
+
+
diff --git a/src/lua/class.lua b/src/lua/class.lua
new file mode 100644
index 00000000..01385178
--- /dev/null
+++ b/src/lua/class.lua
@@ -0,0 +1,85 @@
+-- tolua: class class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: class.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Class class
+-- Represents a class definition.
+-- Stores the following fields:
+-- name = class name
+-- base = class base, if any (only single inheritance is supported)
+-- {i} = list of members
+classClass = {
+ _base = classContainer,
+ type = 'class',
+ name = '',
+ base = '',
+}
+settag(classClass,tolua_tag)
+
+
+-- register class
+function classClass:register ()
+ output(' tolua_cclass(tolua_S,"'..self.name..'","'..self.base..'");')
+ local i=1
+ while self[i] do
+ self[i]:register()
+ i = i+1
+ end
+end
+
+-- unregister class
+function classClass:unregister ()
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.name..'");')
+end
+
+-- output tags
+function classClass:decltag ()
+ self.itype,self.tag = tagvar(self.name);
+ self.citype,self.ctag = tagvar(self.name,'const');
+ local i=1
+ while self[i] do
+ self[i]:decltag()
+ i = i+1
+ end
+end
+
+
+-- Print method
+function classClass:print (ident,close)
+ print(ident.."Class{")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." base = '"..self.base.."';")
+ local i=1
+ while self[i] do
+ self[i]:print(ident.." ",",")
+ i = i+1
+ end
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Class (t)
+ t._base = classClass
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects the name, the base and the body of the class.
+function Class (n,p,b)
+ local c = _Class(_Container{name=n, base=p})
+ push(c)
+ c:parse(strsub(b,2,strlen(b)-1)) -- eliminate braces
+ pop()
+end
+
+
diff --git a/src/lua/clean.lua b/src/lua/clean.lua
new file mode 100644
index 00000000..ba08d534
--- /dev/null
+++ b/src/lua/clean.lua
@@ -0,0 +1,74 @@
+-- mark up comments and strings
+STR1 = "\001"
+STR2 = "\002"
+STR3 = "\003"
+STR4 = "\004"
+REM = "\005"
+ANY = "([\001-\005])"
+ESC1 = "\006"
+ESC2 = "\007"
+
+MASK = { -- the substitution order is important
+ {ESC1, "\\'"},
+ {ESC2, '\\"'},
+ {STR1, "'"},
+ {STR2, '"'},
+ {STR3, "%[%["},
+ {STR4, "%]%]"},
+ {REM , "%-%-"},
+}
+
+function mask (s)
+ for i = 1,getn(MASK) do
+ s = gsub(s,MASK[i][2],MASK[i][1])
+ end
+ return s
+end
+
+function unmask (s)
+ for i = 1,getn(MASK) do
+ s = gsub(s,MASK[i][1],MASK[i][2])
+ end
+ return s
+end
+
+function clean (s)
+ -- check for compilation error
+ local code = "return function () " .. s .. " end"
+ if not dostring(code) then
+ return nil
+ end
+
+ local S = "" -- saved string
+
+ s = mask(s)
+
+ -- remove blanks and comments
+ while 1 do
+ local b,e,d = strfind(s,ANY)
+ if b then
+ S = S..strsub(s,1,b-1)
+ s = strsub(s,b+1)
+ if d==STR1 or d==STR2 then
+ e = strfind(s,d)
+ S = S ..d..strsub(s,1,e)
+ s = strsub(s,e+1)
+ elseif d==STR3 then
+ e = strfind(s,STR4)
+ S = S..d..strsub(s,1,e)
+ s = strsub(s,e+1)
+ elseif d==REM then
+ s = gsub(s,"[^\n]*(\n?)","%1",1)
+ end
+ else
+ S = S..s
+ break
+ end
+ end
+ -- eliminate unecessary spaces
+ S = gsub(S,"[ \t]+"," ")
+ S = gsub(S,"[ \t]*\n[ \t]*","\n")
+ S = unmask(S)
+ return S
+end
+
diff --git a/src/lua/code.lua b/src/lua/code.lua
new file mode 100644
index 00000000..08f38ad2
--- /dev/null
+++ b/src/lua/code.lua
@@ -0,0 +1,73 @@
+-- tolua: code class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1999
+-- $Id: code.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Code class
+-- Represents Lua code to be compiled and included
+-- in the initialization function.
+-- The following fields are stored:
+-- text = text code
+classCode = {
+ text = '',
+ _base = classFeature,
+}
+settag(classCode,tolua_tag)
+
+-- register code
+function classCode:register ()
+ -- clean Lua code
+ local s = clean(self.text)
+ if not s then
+ error("parser error in embedded code")
+ end
+
+ -- convert to C
+ output('\n { /* begin embedded lua code */\n')
+ output(' static unsigned char B[] = {\n ')
+ local t={n=0}
+ local b = gsub(s,'(.)',function (c)
+ local e = ''
+ %t.n=%t.n+1 if %t.n==15 then %t.n=0 e='\n ' end
+ return format('%3u,%s',strbyte(c),e)
+ end
+ )
+ output(b..strbyte(" "))
+ output('\n };\n')
+ output(' lua_dobuffer(tolua_S,(char*)B,sizeof(B),"tolua: embedded Lua code");')
+ output(' } /* end of embedded lua code */\n\n')
+end
+
+
+-- Print method
+function classCode:print (ident,close)
+ print(ident.."Code{")
+ print(ident.." text = [["..self.text.."]],")
+ print(ident.."}"..close)
+end
+
+
+-- Internal constructor
+function _Code (t)
+ t._base = classCode
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the code text
+function Code (l)
+ return _Code {
+ text = l
+ }
+end
+
+
diff --git a/src/lua/container.lua b/src/lua/container.lua
new file mode 100644
index 00000000..cbbf11c1
--- /dev/null
+++ b/src/lua/container.lua
@@ -0,0 +1,311 @@
+-- tolua: container abstract class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: container.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Container class
+-- Represents a container of features to be bound
+-- to lua.
+classContainer =
+{
+ curr = nil,
+ _base = classFeature,
+}
+settag(classContainer,tolua_tag)
+
+-- output tags
+function classContainer:decltag ()
+ push(self)
+ local i=1
+ while self[i] do
+ self[i]:decltag()
+ i = i+1
+ end
+ pop()
+end
+
+
+-- write support code
+function classContainer:supcode ()
+ push(self)
+ local i=1
+ while self[i] do
+ self[i]:supcode()
+ i = i+1
+ end
+ pop()
+end
+
+
+-- Internal container constructor
+function _Container (self)
+ self._base = classContainer
+ settag(self,tolua_tag)
+ self.n = 0
+ self.typedefs = {n=0}
+ self.lnames = {}
+ return self
+end
+
+-- push container
+function push (t)
+ classContainer.curr = t
+end
+
+-- pop container
+function pop ()
+ classContainer.curr = classContainer.curr.parent
+end
+
+-- append to current container
+function append (t)
+ return classContainer.curr:append(t)
+end
+
+-- append typedef to current container
+function appendtypedef (t)
+ return classContainer.curr:appendtypedef(t)
+end
+
+-- substitute typedef
+function findtypedef (type)
+ return classContainer.curr:findtypedef(type)
+end
+
+-- check if is typedef
+function istypedef (type)
+ return classContainer.curr:istypedef(type)
+end
+
+-- append feature to container
+function classContainer:append (t)
+ self.n = self.n + 1
+ self[self.n] = t
+ t.parent = self
+end
+
+-- append typedef
+function classContainer:appendtypedef (t)
+ self.typedefs.n = self.typedefs.n + 1
+ self.typedefs[self.typedefs.n] = t
+end
+
+-- determine lua function name overload
+function classContainer:overload (lname)
+ if not self.lnames[lname] then
+ self.lnames[lname] = 0
+ else
+ self.lnames[lname] = self.lnames[lname] + 1
+ end
+ return format("%02d",self.lnames[lname])
+end
+
+function classContainer:findtypedef (type)
+ local env = self
+ while env do
+ if env.typedefs then
+ local i=1
+ while env.typedefs[i] do
+ if env.typedefs[i].utype == type then
+ local mod1,type1 = env.typedefs[i].mod,env.typedefs[i].type
+ local mod2,type2 = findtypedef(type1)
+ return mod2..' '..mod1,type2
+ end
+ i = i+1
+ end
+ end
+ env = env.parent
+ end
+ return '',type
+end
+
+function classContainer:istypedef (type)
+ local env = self
+ while env do
+ if env.typedefs then
+ local i=1
+ while env.typedefs[i] do
+ if env.typedefs[i].utype == type then
+ return 1
+ end
+ i = i+1
+ end
+ end
+ env = env.parent
+ end
+ return nil
+end
+
+-- parse chunk
+function classContainer:doparse (s)
+
+ -- try module
+ do
+ local b,e,name,body = strfind(s,"^%s*module%s%s*([_%w][_%w]*)%s*(%b{})%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Module(name,body)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try define
+ do
+ local b,e,name = strfind(s,"^%s*#define%s%s*([^%s]*)[^\n]*\n%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Define(name)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try enumerates
+ do
+ local b,e,body = strfind(s,"^%s*enum[^{]*(%b{})%s*;?%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Enumerate(body)
+ return strsub(s,e+1)
+ end
+ end
+
+ do
+ local b,e,body,name = strfind(s,"^%s*typedef%s%s*enum[^{]*(%b{})%s*([%w_][^%s]*)%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Enumerate(body)
+ Typedef("int "..name)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try operator
+ do
+ local b,e,decl,kind,arg,const = strfind(s,"^%s*([_%w][_%w%s%*&]*operator)%s*([^%s][^%s]*)%s*(%b())%s*(c?o?n?s?t?)%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Operator(decl,kind,arg,const)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try function
+ do
+ local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&]*[_%w])%s*(%b())%s*(c?o?n?s?t?)%s*=?%s*0?%s*;%s*")
+ if not b then
+ -- try a single letter function name
+ b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*;%s*")
+ end
+ if b then
+ _curr_code = strsub(s,b,e)
+ Function(decl,arg,const)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try inline function
+ do
+ local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&]*[_%w])%s*(%b())%s*(c?o?n?s?t?)%s*%b{}%s*")
+ if not b then
+ -- try a single letter function name
+ b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*%b{}%s*")
+ end
+ if b then
+ _curr_code = strsub(s,b,e)
+ Function(decl,arg,const)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try class
+ do
+ local b,e,name,base,body = strfind(s,"^%s*class%s*([_%w][_%w]*)%s*(.-)%s*(%b{})%s*;%s*")
+ if not b then
+ b,e,name,base,body = strfind(s,"^%s*struct%s*([_%w][_%w]*)%s*(.-)%s*(%b{})%s*;%s*")
+ if not b then
+ base = ''
+ b,e,body,name = strfind(s,"^%s*typedef%s%s*struct%s%s*[_%w]*%s*(%b{})%s*([_%w][_%w]*)%s*;%s*")
+ end
+ end
+ if b then
+ if base ~= '' then
+ local b,e
+ b,e,base = strfind(base,".-([_%w][_%w]*)$")
+ end
+ _curr_code = strsub(s,b,e)
+ Class(name,base,body)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try typedef
+ do
+ local b,e,types = strfind(s,"^%s*typedef%s%s*(.-)%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Typedef(types)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try variable
+ do
+ local b,e,decl = strfind(s,"^%s*([_%w][_@%s%w%d%*&]*[_%w%d])%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Variable(decl)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try array
+ do
+ local b,e,decl = strfind(s,"^%s*([_%w][][_@%s%w%d%*&%-%>]*[]_%w%d])%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Array(decl)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try code
+ do
+ local b,e,code = strfind(s,"^%s*(%b\1\2)")
+ if b then
+ Code(strsub(code,2,-2))
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try verbatim
+ do
+ local b,e,line = strfind(s,"^%s*%$(.-\n)")
+ if b then
+ Verbatim(line)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- no matching
+ if gsub(s,"%s%s*","") ~= "" then
+ _curr_code = s
+ error("#parse error")
+ else
+ return ""
+ end
+end
+
+function classContainer:parse (s)
+ while s ~= '' do
+ s = self:doparse(s)
+ end
+end
+
+
diff --git a/src/lua/declaration.lua b/src/lua/declaration.lua
new file mode 100644
index 00000000..e4d5c688
--- /dev/null
+++ b/src/lua/declaration.lua
@@ -0,0 +1,399 @@
+-- tolua: declaration class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: declaration.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Declaration class
+-- Represents variable, function, or argument declaration.
+-- Stores the following fields:
+-- mod = type modifiers
+-- type = type
+-- ptr = "*" or "&", if representing a pointer or a reference
+-- name = name
+-- dim = dimension, if a vector
+-- def = default value, if any (only for arguments)
+-- ret = "*" or "&", if value is to be returned (only for arguments)
+classDeclaration = {
+ _base = classFeature,
+ mod = '',
+ type = '',
+ ptr = '',
+ name = '',
+ dim = '',
+ ret = '',
+ def = ''
+}
+settag(classDeclaration,tolua_tag)
+
+-- Create an unique variable name
+function create_varname ()
+ if not _varnumber then _varnumber = 0 end
+ _varnumber = _varnumber + 1
+ return "tolua_var_".._varnumber
+end
+
+-- Check declaration name
+-- It also identifies default values
+function classDeclaration:checkname ()
+
+ if strsub(self.name,1,1) == '[' and not istype(self.type) then
+ self.name = self.type..self.name
+ local m = split(self.mod,'%s%s*')
+ self.type = m[m.n]
+ self.mod = concat(m,1,m.n-1)
+ end
+
+ local t = split(self.name,'=')
+ if t.n==2 then
+ self.name = t[1]
+ self.def = t[t.n]
+ end
+
+ local b,e,d = strfind(self.name,"%[(.-)%]")
+ if b then
+ self.name = strsub(self.name,1,b-1)
+ self.dim = d
+ end
+
+
+ if self.type ~= '' and self.type ~= 'void' and self.name == '' then
+ self.name = create_varname()
+ elseif self.kind=='var' then
+ if self.type=='' and self.name~='' then
+ self.type = self.type..self.name
+ self.name = create_varname()
+ elseif istype(self.name) then
+ if self.type=='' then self.type = self.name
+ else self.type = self.type..' '..self.name end
+ self.name = create_varname()
+ end
+ end
+
+end
+
+-- Check declaration type
+-- Substitutes typedef's.
+function classDeclaration:checktype ()
+
+ -- check if there is a pointer to basic type
+ if isbasic(self.type) and self.ptr~='' then
+ self.ret = self.ptr
+ self.ptr = nil
+ end
+
+ -- check if there is array to be returned
+ if self.dim~='' and self.ret~='' then
+ error('#invalid parameter: cannot return an array of values')
+ end
+
+ -- register type
+ if self.type~='' then
+ regtype(self.type)
+ end
+
+ -- restore 'void*' and 'string*'
+ if self.type == '_userdata' then self.type = 'void*'
+ elseif self.type == '_cstring' then self.type = 'char*'
+ end
+
+--
+-- -- if returning value, automatically set default value
+-- if self.ret ~= '' and self.def == '' then
+-- self.def = '0'
+-- end
+--
+
+end
+
+-- Print method
+function classDeclaration:print (ident,close)
+ print(ident.."Declaration{")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." dim = '"..self.dim.."',")
+ print(ident.." def = '"..self.def.."',")
+ print(ident.." ret = '"..self.ret.."',")
+ print(ident.."}"..close)
+end
+
+-- declare tag
+function classDeclaration:decltag ()
+ self.itype, self.tag = tagvar(self.type,strfind(self.mod,'const'))
+end
+
+
+-- output type checking
+function classDeclaration:outchecktype (narg)
+ local tag, def
+ if self.dim ~= '' then
+ tag = 'LUA_TTABLE'
+ def = 0
+ else
+ tag = self.tag
+ def = self.def~='' or 0
+ end
+ return 'tolua_istype(tolua_S,'..narg..','..tag..','..def..')'
+end
+
+-- Declare variable
+function classDeclaration:declare (narg)
+ local ptr = ''
+ if self.ptr~='' then ptr = '*' end
+ output(" ",self.mod,self.type,ptr)
+ if self.dim ~= '' and tonumber(self.dim)==nil then
+ output('*')
+ end
+ output(self.name)
+ if self.dim ~= '' then
+ if tonumber(self.dim)~=nil then
+ output('[',self.dim,'];')
+ else
+ output(' = (',self.mod,self.type,ptr,'*)',
+ 'malloc(',self.dim,'*sizeof(',self.type,ptr,'));')
+ end
+ else
+ local t = isbasic(self.type)
+ output(' = ')
+ if not t and ptr=='' then output('*') end
+ output('((',self.mod,self.type)
+ if not t then
+ output('*')
+ end
+ output(') ')
+ local def = 0
+ if self.def ~= '' then def = self.def end
+ if t then
+ output('tolua_get'..t,'(tolua_S,',narg,',',def,'));')
+ else
+ output('tolua_getusertype(tolua_S,',narg,',',def,'));')
+ end
+ end
+end
+
+-- Get parameter value
+function classDeclaration:getarray (narg)
+ if self.dim ~= '' then
+ output(' {')
+ local def = self.def~='' or 0
+ output(' if (!tolua_arrayistype(tolua_S,',narg,',',self.tag,',',self.dim,',',def,'))')
+ output(' goto tolua_lerror;')
+ output(' else\n')
+ output(' {')
+ output(' int i;')
+ output(' for(i=0; i<'..self.dim..';i++)')
+ local t = isbasic(self.type)
+ local ptr = ''
+ if self.ptr~='' then ptr = '*' end
+ output(' ',self.name..'[i] = ')
+ if not t and ptr=='' then output('*') end
+ output('((',self.mod,self.type)
+ if not t then
+ output('*')
+ end
+ output(') ')
+ local def = 0
+ if self.def ~= '' then def = self.def end
+ if t then
+ output('tolua_getfield'..t..'(tolua_S,',narg,',i+1,',def,'));')
+ else
+ output('tolua_getfieldusertype(tolua_S,',narg,',i+1,',def,'));')
+ end
+ output(' }')
+ output(' }')
+ end
+end
+
+-- Get parameter value
+function classDeclaration:setarray (narg)
+ if self.dim ~= '' then
+ output(' {')
+ output(' int i;')
+ output(' for(i=0; i<'..self.dim..';i++)')
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_pushfield'..t..'(tolua_S,',narg,',i+1,(',ct,')',self.name,'[i]);')
+ else
+ if self.ptr == '' then
+ output(' {')
+ output('#ifdef __cplusplus\n')
+ output(' void* toluaI_clone = new',self.type,'(',self.name,'[i]);')
+ output('#else\n')
+ output(' void* toluaI_clone = tolua_copy(tolua_S,(void*)&',self.name,'[i],sizeof(',self.type,'));')
+ output('#endif\n')
+ output(' tolua_pushfieldusertype(tolua_S,',narg,',i+1,tolua_doclone(tolua_S,toluaI_clone,',self.tag,'),',self.tag,');')
+ output(' }')
+
+ --output(' tolua_pushfieldclone(tolua_S,',narg,',i+1,(void*)&',self.name,'[i],sizeof(',self.type,'),',self.tag,');')
+ else
+ output(' tolua_pushfieldusertype(tolua_S,',narg,',i+1,(void*)',self.name,'[i],',self.tag,');')
+ end
+ end
+ output(' }')
+ end
+end
+
+-- Free dynamically allocated array
+function classDeclaration:freearray ()
+ if self.dim ~= '' and tonumber(self.dim)==nil then
+ output(' free(',self.name,');')
+ end
+end
+
+-- Pass parameter
+function classDeclaration:passpar ()
+ if self.ptr=='&' then
+ output('*'..self.name)
+ elseif self.ret=='*' then
+ output('&'..self.name)
+ else
+ output(self.name)
+ end
+end
+
+-- Return parameter value
+function classDeclaration:retvalue ()
+ if self.ret ~= '' then
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_push'..t..'(tolua_S,(',ct,')'..self.name..');')
+ else
+ output(' tolua_pushusertype(tolua_S,(void*)'..self.name..',',self.tag,');')
+ end
+ return 1
+ end
+ return 0
+end
+
+-- Internal constructor
+function _Declaration (t)
+ if t.name and t.name~='' then
+ local n = split(t.name,'@')
+ t.name = n[1]
+ t.lname = gsub(n[2] or n[1],"%[.-%]","")
+ end
+ t._base = classDeclaration
+ settag(t,tolua_tag)
+ t:checkname()
+ t:checktype()
+ return t
+end
+
+-- Constructor
+-- Expects the string declaration.
+-- The kind of declaration can be "var" or "func".
+function Declaration (s,kind)
+ -- eliminate spaces if default value is provided
+ s = gsub(s,"%s*=%s*","=")
+
+ if kind == "var" then
+ -- check the form: void
+ if s == '' or s == 'void' then
+ return _Declaration{type = 'void', kind = kind}
+ end
+ end
+
+ -- check the form: mod type*& name
+ local t = split(s,'%*%s*&')
+ if t.n == 2 then
+ if kind == 'func' then
+ error("#invalid function return type: "..s)
+ end
+ local m = split(t[1],'%s%s*')
+ return _Declaration{
+ name = t[2],
+ ptr = '*',
+ ret = '&',
+ type = m[m.n],
+ mod = concat(m,1,m.n-1),
+ kind = kind
+ }
+ end
+
+ -- check the form: mod type** name
+ t = split(s,'%*%s*%*')
+ if t.n == 2 then
+ if kind == 'func' then
+ error("#invalid function return type: "..s)
+ end
+ local m = split(t[1],'%s%s*')
+ return _Declaration{
+ name = t[2],
+ ptr = '*',
+ ret = '*',
+ type = m[m.n],
+ mod = concat(m,1,m.n-1),
+ kind = kind
+ }
+ end
+
+ -- check the form: mod type& name
+ t = split(s,'&')
+ if t.n == 2 then
+ local m = split(t[1],'%s%s*')
+ return _Declaration{
+ name = t[2],
+ ptr = '&',
+ type = m[m.n],
+ mod = concat(m,1,m.n-1) ,
+ kind = kind
+ }
+ end
+
+ -- check the form: mod type* name
+ local s1 = gsub(s,"(%b\[\])",function (n) return gsub(n,'%*','\1') end)
+ t = split(s1,'%*')
+ if t.n == 2 then
+ t[2] = gsub(t[2],'\1','%*') -- restore * in dimension expression
+ local m = split(t[1],'%s%s*')
+ return _Declaration{
+ name = t[2],
+ ptr = '*',
+ type = m[m.n],
+ mod = concat(m,1,m.n-1) ,
+ kind = kind
+ }
+ end
+
+ if kind == 'var' then
+ -- check the form: mod type name
+ t = split(s,'%s%s*')
+ local v
+ if istype(t[t.n]) then v = '' else v = t[t.n]; t.n = t.n-1 end
+ return _Declaration{
+ name = v,
+ type = t[t.n],
+ mod = concat(t,1,t.n-1),
+ kind = kind
+ }
+
+ else -- kind == "func"
+
+ -- check the form: mod type name
+ t = split(s,'%s%s*')
+ local v = t[t.n] -- last word is the function name
+ local tp,md
+ if t.n>1 then
+ tp = t[t.n-1]
+ md = concat(t,1,t.n-2)
+ end
+ return _Declaration{
+ name = v,
+ type = tp,
+ mod = md,
+ kind = kind
+ }
+ end
+
+end
+
+
+
diff --git a/src/lua/define.lua b/src/lua/define.lua
new file mode 100644
index 00000000..db64db50
--- /dev/null
+++ b/src/lua/define.lua
@@ -0,0 +1,72 @@
+-- tolua: define class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: define.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Define class
+-- Represents a numeric const definition
+-- The following filds are stored:
+-- name = constant name
+classDefine = {
+ name = '',
+ _base = classFeature,
+}
+settag(classDefine,tolua_tag)
+
+-- register define
+function classDefine:register ()
+ local p = self:inmodule()
+ if p then
+ output(' tolua_constant(tolua_S,"'..p..'","'..self.lname..'",'..self.name..');')
+ else
+ output(' tolua_constant(tolua_S,NULL,"'..self.lname..'",'..self.name..');')
+ end
+end
+
+-- unregister define
+function classDefine:unregister ()
+ if not self:inmodule() then
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lname..'");')
+ end
+end
+
+-- Print method
+function classDefine:print (ident,close)
+ print(ident.."Define{")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." lname = '"..self.lname.."',")
+ print(ident.."}"..close)
+end
+
+
+-- Internal constructor
+function _Define (t)
+ t._base = classDefine
+ settag(t,tolua_tag)
+
+ if t.name == '' then
+ error("#invalid define")
+ end
+
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the constant name
+function Define (n)
+ local t = split(n,'@')
+ return _Define {
+ name = t[1],
+ lname = t[2] or t[1]
+ }
+end
+
+
diff --git a/src/lua/doit.lua b/src/lua/doit.lua
new file mode 100644
index 00000000..aa184d62
--- /dev/null
+++ b/src/lua/doit.lua
@@ -0,0 +1,73 @@
+-- Generate binding code
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: doit.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- open input file, if any
+if flags.f then
+ local st, msg = readfrom(flags.f)
+ if not st then
+ error('#'..msg)
+ end
+end
+
+-- define package name, if not provided
+if not flags.n then
+ if flags.f then
+ flags.n = gsub(flags.f,"%..*","")
+ else
+ error("#no package name nor input file provided")
+ end
+end
+
+local p = Package(flags.n)
+
+if flags.f then
+ readfrom()
+end
+
+if flags.p then
+ return -- only parse
+end
+
+if flags.o then
+ local st,msg = writeto(flags.o)
+ if not st then
+ error('#'..msg)
+ end
+end
+
+if flags.P then
+ p:print()
+else
+ p:decltag()
+ p:preamble()
+ p:supcode()
+ p:register()
+ p:unregister()
+end
+
+if flags.o then
+ writeto()
+end
+
+-- write header file
+if not flags.P then
+ if flags.H then
+ local st,msg = writeto(flags.H)
+ if not st then
+ error('#'..msg)
+ end
+ p:header()
+ writeto()
+ end
+end
+
diff --git a/src/lua/enumerate.lua b/src/lua/enumerate.lua
new file mode 100644
index 00000000..6b2b7466
--- /dev/null
+++ b/src/lua/enumerate.lua
@@ -0,0 +1,93 @@
+-- tolua: enumerate class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: enumerate.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Enumerate class
+-- Represents enumeration
+-- The following fields are stored:
+-- {i} = list of constant names
+classEnumerate = {
+ _base = classFeature,
+}
+settag(classEnumerate,tolua_tag)
+
+-- register enumeration
+function classEnumerate:register ()
+ local p = self:inclass() or self:inmodule()
+ local i=1
+ while self[i] do
+ if p then
+ if self:inclass() then
+ output(' tolua_constant(tolua_S,"'..p..'","'..self.lnames[i]..'",'..p..'::'..self[i]..');')
+ else
+ output(' tolua_constant(tolua_S,"'..p..'","'..self.lnames[i]..'",'..self[i]..');')
+ end
+ else
+ output(' tolua_constant(tolua_S,NULL,"'..self.lnames[i]..'",'..self[i]..');')
+ end
+ i = i+1
+ end
+end
+-- register enumeration
+function classEnumerate:unregister ()
+ if self:inclass()==nil and self:inmodule()==nil then
+ local i=1
+ while self[i] do
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lnames[i]..'");')
+ i = i+1
+ end
+ end
+end
+
+-- Print method
+function classEnumerate:print (ident,close)
+ print(ident.."Enumerate{")
+ local i=1
+ while self[i] do
+ print(ident.." '"..self[i].."'("..self.lnames[i].."),")
+ i = i+1
+ end
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Enumerate (t)
+ t._base = classEnumerate
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the enumerate body
+function Enumerate (b)
+ local t = split(strsub(b,2,-2),',') -- eliminate braces
+ local i = 1
+ local e = {n=0}
+ while t[i] do
+ local tt = split(t[i],'=') -- discard initial value
+ e.n = e.n + 1
+ e[e.n] = tt[1]
+ i = i+1
+ end
+ -- set lua names
+ i = 1
+ e.lnames = {}
+ while e[i] do
+ local t = split(e[i],'@')
+ e[i] = t[1]
+ e.lnames[i] = t[2] or t[1]
+ i = i+1
+ end
+ return _Enumerate(e)
+end
+
+
diff --git a/src/lua/feature.lua b/src/lua/feature.lua
new file mode 100644
index 00000000..4a4379e1
--- /dev/null
+++ b/src/lua/feature.lua
@@ -0,0 +1,72 @@
+-- tolua: abstract feature class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: feature.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Feature class
+-- Represents the base class of all mapped feature.
+classFeature = {
+}
+
+-- write support code
+function classFeature:supcode ()
+end
+
+-- output tag
+function classFeature:decltag ()
+end
+
+-- register feature
+function classFeature:register ()
+end
+
+-- unregister feature
+function classFeature:unregister ()
+end
+
+-- translate verbatim
+function classFeature:preamble ()
+end
+
+-- check if feature is inside a class definition
+-- it returns the feature class name or nil.
+function classFeature:inclass ()
+ if self.parent and self.parent.type == 'class' then
+ return self.parent.name
+ else
+ return nil
+ end
+end
+
+-- check if feature is inside a module
+-- it returns the feature module name or nil.
+function classFeature:inmodule ()
+ if self.parent and self.parent.type == 'module' then
+ return self.parent.name
+ else
+ return nil
+ end
+end
+
+-- return C binding function name based on name
+-- the client specifies a prefix
+-- return C binding function name
+-- the client specifies a prefix
+function classFeature:cfuncname (n)
+ if self.parent then
+ n = self.parent:cfuncname(n)
+ end
+ if self.lname then
+ return n..'_'..self.lname
+ else
+ return n..'_'..self.name
+ end
+end
+
diff --git a/src/lua/function.lua b/src/lua/function.lua
new file mode 100644
index 00000000..b87e3488
--- /dev/null
+++ b/src/lua/function.lua
@@ -0,0 +1,317 @@
+-- tolua: function class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: function.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Function class
+-- Represents a function or a class method.
+-- The following fields are stored:
+-- mod = type modifiers
+-- type = type
+-- ptr = "*" or "&", if representing a pointer or a reference
+-- name = name
+-- args = list of argument declarations
+-- const = if it is a method receiving a const "this".
+classFunction = {
+ mod = '',
+ type = '',
+ ptr = '',
+ name = '',
+ args = {n=0},
+ const = '',
+ _base = classFeature,
+}
+settag(classFunction,tolua_tag)
+
+-- declare tags
+function classFunction:decltag ()
+ self.itype,self.tag = tagvar(self.type,strfind(self.mod,'const'))
+ local i=1
+ while self.args[i] do
+ self.args[i]:decltag()
+ i = i+1
+ end
+end
+
+
+-- Write binding function
+-- Outputs C/C++ binding function.
+function classFunction:supcode ()
+ local nret = 0 -- number of returned values
+ local class = self:inclass()
+ local _,_,static = strfind(self.mod,'^%s*(static)')
+
+ if class then
+ output("/* method:",self.name," of class ",class," */")
+ else
+ output("/* function:",self.name," */")
+ end
+ output("static int",self.cname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- check types
+ output(' if (\n')
+ -- check self
+ local narg
+ if class then narg=2 else narg=1 end
+ if class and self.name~='new' and static==nil then
+ if self.const == 'const' then
+ output(' !tolua_istype(tolua_S,1,',self.parent.ctag,',0) ||\n')
+ else
+ output(' !tolua_istype(tolua_S,1,',self.parent.tag,',0) ||\n')
+ end
+ end
+ -- check args
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ if isbasic(self.args[i].type) ~= 'value' then
+ output(' !'..self.args[i]:outchecktype(narg)..' ||\n')
+ end
+ narg = narg+1
+ i = i+1
+ end
+ end
+ -- check end of list
+ output(' !tolua_isnoobj(tolua_S,'..narg..')\n )\n goto tolua_lerror;')
+
+ output(' else\n {')
+
+ -- declare self, if the case
+ local narg
+ if class then narg=2 else narg=1 end
+ if class and self.name~='new' and static==nil then
+ output(' ',self.const,class,'*','self = ')
+ output('(',self.const,class,'*) ')
+ output('tolua_getusertype(tolua_S,1,0);')
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ end
+ -- declare parameters
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ self.args[i]:declare(narg)
+ narg = narg+1
+ i = i+1
+ end
+ end
+
+ -- check self
+ if class and self.name~='new' and static==nil then
+ output(' if (!self) tolua_error(tolua_S,"invalid \'self\' in function \''..self.name..'\'");');
+ end
+
+ -- get array element values
+ if class then narg=2 else narg=1 end
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ self.args[i]:getarray(narg)
+ narg = narg+1
+ i = i+1
+ end
+ end
+
+ -- call function
+ if class and self.name=='delete' then
+ output(' delete self;')
+ elseif class and self.name == 'operator&[]' then
+ output(' self->operator[](',self.args[1].name,') = ',self.args[2].name,';')
+ else
+ output(' {')
+ if self.type ~= '' and self.type ~= 'void' then
+ output(' ',self.mod,self.type,self.ptr,'toluaI_ret = ')
+ output('(',self.mod,self.type,self.ptr,') ')
+ else
+ output(' ')
+ end
+ if class and self.name=='new' then
+ output('new',class,'(')
+ elseif class and static then
+ output(class..'::'..self.name,'(')
+ elseif class then
+ output('self->'..self.name,'(')
+ else
+ output(self.name,'(')
+ end
+
+ -- write parameters
+ local i=1
+ while self.args[i] do
+ self.args[i]:passpar()
+ i = i+1
+ if self.args[i] then
+ output(',')
+ end
+ end
+
+ output(');')
+
+ -- return values
+ if self.type ~= '' and self.type ~= 'void' then
+ nret = nret + 1
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_push'..t..'(tolua_S,(',ct,')toluaI_ret);')
+ else
+ if self.ptr == '' then
+ output(' {')
+ output('#ifdef __cplusplus\n')
+ output(' void* toluaI_clone = new',self.type,'(toluaI_ret);')
+ output('#else\n')
+ output(' void* toluaI_clone = tolua_copy(tolua_S,(void*)&toluaI_ret,sizeof(',self.type,'));')
+ output('#endif\n')
+ output(' tolua_pushusertype(tolua_S,tolua_doclone(tolua_S,toluaI_clone,',self.tag,'),',self.tag,');')
+ output(' }')
+ --output(' tolua_pushclone((void*)&toluaI_ret,sizeof(',self.type,'),',self.tag,');')
+ elseif self.ptr == '&' then
+ output(' tolua_pushusertype(tolua_S,(void*)&toluaI_ret,',self.tag,');')
+ else
+ output(' tolua_pushusertype(tolua_S,(void*)toluaI_ret,',self.tag,');')
+ end
+ end
+ end
+ local i=1
+ while self.args[i] do
+ nret = nret + self.args[i]:retvalue()
+ i = i+1
+ end
+ output(' }')
+
+ -- set array element values
+ if class then narg=2 else narg=1 end
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ self.args[i]:setarray(narg)
+ narg = narg+1
+ i = i+1
+ end
+ end
+
+ -- free dynamically allocated array
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ self.args[i]:freearray()
+ i = i+1
+ end
+ end
+ end
+
+ output(' }')
+ output(' return '..nret..';')
+
+ -- call overloaded function or generate error
+ output('tolua_lerror:\n')
+ local overload = strsub(self.cname,-2,-1) - 1
+ if overload >= 0 then
+ output(' return '..strsub(self.cname,1,-3)..format("%02d",overload)..'(tolua_S);')
+ else
+ output(' tolua_error(tolua_S,"#ferror in function \''..self.lname..'\'.");')
+ output(' return 0;')
+ end
+
+ output('}')
+ output('\n')
+end
+
+-- register function
+function classFunction:register ()
+ local parent = self:inclass() or self:inmodule()
+ if parent then
+ output(' tolua_function(tolua_S,"'..parent..'","'..self.lname..'",'..self.cname..');')
+ else
+ output(' tolua_function(tolua_S,NULL,"'..self.lname..'",'..self.cname..');')
+ end
+end
+
+-- unregister function
+function classFunction:unregister ()
+ if self:inclass()==nil and self:inmodule()==nil then
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lname..'");')
+ end
+end
+
+
+-- Print method
+function classFunction:print (ident,close)
+ print(ident.."Function{")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." const = '"..self.const.."',")
+ print(ident.." cname = '"..self.cname.."',")
+ print(ident.." lname = '"..self.lname.."',")
+ print(ident.." args = {")
+ local i=1
+ while self.args[i] do
+ self.args[i]:print(ident.." ",",")
+ i = i+1
+ end
+ print(ident.." }")
+ print(ident.."}"..close)
+end
+
+-- determine lua function name overload
+function classFunction:overload ()
+ return self.parent:overload(self.lname)
+end
+
+
+
+-- Internal constructor
+function _Function (t)
+ t._base = classFunction
+ settag(t,tolua_tag)
+
+ if t.const ~= 'const' and t.const ~= '' then
+ error("#invalid 'const' specification")
+ end
+
+ append(t)
+ if t:inclass() then
+ if t.name == t.parent.name then
+ t.name = 'new'
+ t.lname = 'new'
+ t.type = t.parent.name
+ t.ptr = '*'
+ elseif t.name == '~'..t.parent.name then
+ t.name = 'delete'
+ t.lname = 'delete'
+ end
+ end
+ t.cname = t:cfuncname("toluaI")..t:overload(t)
+ return t
+end
+
+-- Constructor
+-- Expects three strings: one representing the function declaration,
+-- another representing the argument list, and the third representing
+-- the "const" or empty string.
+function Function (d,a,c)
+ local t = split(strsub(a,2,-2),',') -- eliminate braces
+ local i=1
+ local l = {n=0}
+ while t[i] do
+ l.n = l.n+1
+ l[l.n] = Declaration(t[i],'var')
+ i = i+1
+ end
+ local f = Declaration(d,'func')
+ f.args = l
+ f.const = c
+ return _Function(f)
+end
+
+
diff --git a/src/lua/lapi.c b/src/lua/lapi.c
new file mode 100644
index 00000000..b597e00a
--- /dev/null
+++ b/src/lua/lapi.c
@@ -0,0 +1,499 @@
+/*
+** $Id: lapi.c,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Lua API
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+const char lua_ident[] = "$Lua: " LUA_VERSION " " LUA_COPYRIGHT " $\n"
+ "$Authors: " LUA_AUTHORS " $";
+
+
+
+#define Index(L,i) ((i) >= 0 ? (L->Cbase+((i)-1)) : (L->top+(i)))
+
+#define api_incr_top(L) incr_top
+
+
+
+
+TObject *luaA_index (lua_State *L, int index) {
+ return Index(L, index);
+}
+
+
+static TObject *luaA_indexAcceptable (lua_State *L, int index) {
+ if (index == 0) {
+ return NULL;
+ } else if (index > 0) {
+ TObject *o = L->Cbase+(index-1);
+ if (o >= L->top) return NULL;
+ else return o;
+ } else {
+ TObject *o = L->top+index;
+ if(o < L->Cbase) return NULL;
+ else return o;
+ }
+}
+
+
+void luaA_pushobject (lua_State *L, const TObject *o) {
+ *L->top = *o;
+ incr_top;
+}
+
+LUA_API int lua_stackspace (lua_State *L) {
+ return (L->stack_last - L->top);
+}
+
+
+
+/*
+** basic stack manipulation
+*/
+
+
+LUA_API int lua_gettop (lua_State *L) {
+ return (L->top - L->Cbase);
+}
+
+
+LUA_API void lua_settop (lua_State *L, int index) {
+ if (index >= 0)
+ luaD_adjusttop(L, L->Cbase, index);
+ else
+ L->top = L->top+index+1; /* index is negative */
+}
+
+
+LUA_API void lua_remove (lua_State *L, int index) {
+ StkId p = luaA_index(L, index);
+ while (++p < L->top) *(p-1) = *p;
+ L->top--;
+}
+
+
+LUA_API void lua_insert (lua_State *L, int index) {
+ StkId p = luaA_index(L, index);
+ StkId q;
+ for (q = L->top; q>p; q--)
+ *q = *(q-1);
+ *p = *L->top;
+}
+
+
+LUA_API void lua_pushvalue (lua_State *L, int index) {
+ *L->top = *luaA_index(L, index);
+ api_incr_top(L);
+}
+
+
+
+/*
+** access functions (stack -> C)
+*/
+
+
+LUA_API int lua_type (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL) ? LUA_TNONE : ttype(o);
+}
+
+LUA_API const char *lua_typename (lua_State *L, int t) {
+ UNUSED(L);
+ return (t == LUA_TNONE) ? "no value" : luaO_typenames[t];
+}
+
+
+LUA_API int lua_iscfunction (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL) ? 0 : iscfunction(o);
+}
+
+LUA_API int lua_isnumber (lua_State *L, int index) {
+ TObject *o = luaA_indexAcceptable(L, index);
+ return (o == NULL) ? 0 : (tonumber(o) == 0);
+}
+
+LUA_API int lua_isstring (lua_State *L, int index) {
+ int t = lua_type(L, index);
+ return (t == LUA_TSTRING || t == LUA_TNUMBER);
+}
+
+
+LUA_API int lua_tag (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL) ? LUA_NOTAG : luaT_tag(o);
+}
+
+LUA_API int lua_equal (lua_State *L, int index1, int index2) {
+ StkId o1 = luaA_indexAcceptable(L, index1);
+ StkId o2 = luaA_indexAcceptable(L, index2);
+ if (o1 == NULL || o2 == NULL) return 0; /* index out-of-range */
+ else return luaO_equalObj(o1, o2);
+}
+
+LUA_API int lua_lessthan (lua_State *L, int index1, int index2) {
+ StkId o1 = luaA_indexAcceptable(L, index1);
+ StkId o2 = luaA_indexAcceptable(L, index2);
+ if (o1 == NULL || o2 == NULL) return 0; /* index out-of-range */
+ else return luaV_lessthan(L, o1, o2, L->top);
+}
+
+
+
+LUA_API long lua_tonumber (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || tonumber(o)) ? 0 : nvalue(o);
+}
+
+LUA_API const char *lua_tostring (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || tostring(L, o)) ? NULL : svalue(o);
+}
+
+LUA_API size_t lua_strlen (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || tostring(L, o)) ? 0 : tsvalue(o)->len;
+}
+
+LUA_API lua_CFunction lua_tocfunction (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || !iscfunction(o)) ? NULL : clvalue(o)->f.c;
+}
+
+LUA_API void *lua_touserdata (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || ttype(o) != LUA_TUSERDATA) ? NULL :
+ tsvalue(o)->u.d.value;
+}
+
+LUA_API const void *lua_topointer (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ if (o == NULL) return NULL;
+ switch (ttype(o)) {
+ case LUA_TTABLE:
+ return hvalue(o);
+ case LUA_TFUNCTION:
+ return clvalue(o);
+ default: return NULL;
+ }
+}
+
+
+
+/*
+** push functions (C -> stack)
+*/
+
+
+LUA_API void lua_pushnil (lua_State *L) {
+ ttype(L->top) = LUA_TNIL;
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_pushnumber (lua_State *L, long n) {
+ nvalue(L->top) = n;
+ ttype(L->top) = LUA_TNUMBER;
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) {
+ tsvalue(L->top) = luaS_newlstr(L, s, len);
+ ttype(L->top) = LUA_TSTRING;
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_pushstring (lua_State *L, const char *s) {
+ if (s == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushlstring(L, s, strlen(s));
+}
+
+
+LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
+ luaV_Cclosure(L, fn, n);
+}
+
+
+LUA_API void lua_pushusertag (lua_State *L, void *u, int tag) {
+ /* ORDER LUA_T */
+ if (!(tag == LUA_ANYTAG || tag == LUA_TUSERDATA || validtag(tag)))
+ luaO_verror(L, "invalid tag for a userdata (%d)", tag);
+ tsvalue(L->top) = luaS_createudata(L, u, tag);
+ ttype(L->top) = LUA_TUSERDATA;
+ api_incr_top(L);
+}
+
+
+
+/*
+** get functions (Lua -> stack)
+*/
+
+
+LUA_API void lua_getglobal (lua_State *L, const char *name) {
+ StkId top = L->top;
+ *top = *luaV_getglobal(L, luaS_new(L, name));
+ L->top = top;
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_gettable (lua_State *L, int index) {
+ StkId t = Index(L, index);
+ StkId top = L->top;
+ *(top-1) = *luaV_gettable(L, t);
+ L->top = top; /* tag method may change top */
+}
+
+
+LUA_API void lua_rawget (lua_State *L, int index) {
+ StkId t = Index(L, index);
+ LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected");
+ *(L->top - 1) = *luaH_get(L, hvalue(t), L->top - 1);
+}
+
+
+LUA_API void lua_rawgeti (lua_State *L, int index, int n) {
+ StkId o = Index(L, index);
+ LUA_ASSERT(ttype(o) == LUA_TTABLE, "table expected");
+ *L->top = *luaH_getnum(hvalue(o), n);
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_getglobals (lua_State *L) {
+ hvalue(L->top) = L->gt;
+ ttype(L->top) = LUA_TTABLE;
+ api_incr_top(L);
+}
+
+
+LUA_API int lua_getref (lua_State *L, int ref) {
+ if (ref == LUA_REFNIL)
+ ttype(L->top) = LUA_TNIL;
+ else if (0 <= ref && ref < L->refSize &&
+ (L->refArray[ref].st == LOCK || L->refArray[ref].st == HOLD))
+ *L->top = L->refArray[ref].o;
+ else
+ return 0;
+ api_incr_top(L);
+ return 1;
+}
+
+
+LUA_API void lua_newtable (lua_State *L) {
+ hvalue(L->top) = luaH_new(L, 0);
+ ttype(L->top) = LUA_TTABLE;
+ api_incr_top(L);
+}
+
+
+
+/*
+** set functions (stack -> Lua)
+*/
+
+
+LUA_API void lua_setglobal (lua_State *L, const char *name) {
+ StkId top = L->top;
+ luaV_setglobal(L, luaS_new(L, name));
+ L->top = top-1; /* remove element from the top */
+}
+
+
+LUA_API void lua_settable (lua_State *L, int index) {
+ StkId t = Index(L, index);
+ StkId top = L->top;
+ luaV_settable(L, t, top-2);
+ L->top = top-2; /* pop index and value */
+}
+
+
+LUA_API void lua_rawset (lua_State *L, int index) {
+ StkId t = Index(L, index);
+ LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected");
+ *luaH_set(L, hvalue(t), L->top-2) = *(L->top-1);
+ L->top -= 2;
+}
+
+
+LUA_API void lua_rawseti (lua_State *L, int index, int n) {
+ StkId o = Index(L, index);
+ LUA_ASSERT(ttype(o) == LUA_TTABLE, "table expected");
+ *luaH_setint(L, hvalue(o), n) = *(L->top-1);
+ L->top--;
+}
+
+
+LUA_API void lua_setglobals (lua_State *L) {
+ StkId newtable = --L->top;
+ LUA_ASSERT(ttype(newtable) == LUA_TTABLE, "table expected");
+ L->gt = hvalue(newtable);
+}
+
+
+LUA_API int lua_ref (lua_State *L, int lock) {
+ int ref;
+ if (ttype(L->top-1) == LUA_TNIL)
+ ref = LUA_REFNIL;
+ else {
+ if (L->refFree != NONEXT) { /* is there a free place? */
+ ref = L->refFree;
+ L->refFree = L->refArray[ref].st;
+ }
+ else { /* no more free places */
+ luaM_growvector(L, L->refArray, L->refSize, 1, struct Ref,
+ "reference table overflow", MAX_INT);
+ L->nblocks += sizeof(struct Ref);
+ ref = L->refSize++;
+ }
+ L->refArray[ref].o = *(L->top-1);
+ L->refArray[ref].st = lock ? LOCK : HOLD;
+ }
+ L->top--;
+ return ref;
+}
+
+
+/*
+** "do" functions (run Lua code)
+** (most of them are in ldo.c)
+*/
+
+LUA_API void lua_rawcall (lua_State *L, int nargs, int nresults) {
+ luaD_call(L, L->top-(nargs+1), nresults);
+}
+
+
+/*
+** Garbage-collection functions
+*/
+
+/* GC values are expressed in Kbytes: #bytes/2^10 */
+#define GCscale(x) ((int)((x)>>10))
+#define GCunscale(x) ((unsigned long)(x)<<10)
+
+LUA_API int lua_getgcthreshold (lua_State *L) {
+ return GCscale(L->GCthreshold);
+}
+
+LUA_API int lua_getgccount (lua_State *L) {
+ return GCscale(L->nblocks);
+}
+
+LUA_API void lua_setgcthreshold (lua_State *L, int newthreshold) {
+ if (newthreshold > GCscale(ULONG_MAX))
+ L->GCthreshold = ULONG_MAX;
+ else
+ L->GCthreshold = GCunscale(newthreshold);
+ luaC_checkGC(L);
+}
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API void lua_settag (lua_State *L, int tag) {
+ luaT_realtag(L, tag);
+ switch (ttype(L->top-1)) {
+ case LUA_TTABLE:
+ hvalue(L->top-1)->htag = tag;
+ break;
+ case LUA_TUSERDATA:
+ tsvalue(L->top-1)->u.d.tag = tag;
+ break;
+ default:
+ luaO_verror(L, "cannot change the tag of a %.20s",
+ luaO_typename(L->top-1));
+ }
+}
+
+
+LUA_API void lua_unref (lua_State *L, int ref) {
+ if (ref >= 0) {
+ LUA_ASSERT(ref < L->refSize && L->refArray[ref].st < 0, "invalid ref");
+ L->refArray[ref].st = L->refFree;
+ L->refFree = ref;
+ }
+}
+
+
+LUA_API int lua_next (lua_State *L, int index) {
+ StkId t = luaA_index(L, index);
+ Node *n;
+ LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected");
+ n = luaH_next(L, hvalue(t), luaA_index(L, -1));
+ if (n) {
+ *(L->top-1) = *key(n);
+ *L->top = *val(n);
+ api_incr_top(L);
+ return 1;
+ }
+ else { /* no more elements */
+ L->top -= 1; /* remove key */
+ return 0;
+ }
+}
+
+
+LUA_API int lua_getn (lua_State *L, int index) {
+ Hash *h = hvalue(luaA_index(L, index));
+ const TObject *value = luaH_getstr(h, luaS_new(L, "n")); /* value = h.n */
+ if (ttype(value) == LUA_TNUMBER)
+ return (int)nvalue(value);
+ else {
+ Number max = 0;
+ int i = h->size;
+ Node *n = h->node;
+ while (i--) {
+ if (ttype(key(n)) == LUA_TNUMBER &&
+ ttype(val(n)) != LUA_TNIL &&
+ nvalue(key(n)) > max)
+ max = nvalue(key(n));
+ n++;
+ }
+ return (int)max;
+ }
+}
+
+
+LUA_API void lua_concat (lua_State *L, int n) {
+ StkId top = L->top;
+ luaV_strconc(L, n, top);
+ L->top = top-(n-1);
+ luaC_checkGC(L);
+}
+
+
+LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
+ TString *ts = luaS_newudata(L, size, NULL);
+ tsvalue(L->top) = ts;
+ ttype(L->top) = LUA_TUSERDATA;
+ api_incr_top(L);
+ return ts->u.d.value;
+}
+
diff --git a/src/lua/lapi.h b/src/lua/lapi.h
new file mode 100644
index 00000000..d6e1c44f
--- /dev/null
+++ b/src/lua/lapi.h
@@ -0,0 +1,17 @@
+/*
+** $Id: lapi.h,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions from Lua API
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lapi_h
+#define lapi_h
+
+
+#include "lobject.h"
+
+
+TObject *luaA_index (lua_State *L, int index);
+void luaA_pushobject (lua_State *L, const TObject *o);
+
+#endif
diff --git a/src/lua/lauxlib.c b/src/lua/lauxlib.c
new file mode 100644
index 00000000..810bca20
--- /dev/null
+++ b/src/lua/lauxlib.c
@@ -0,0 +1,216 @@
+/*
+** $Id: lauxlib.c,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+/* This file uses only the official API of Lua.
+** Any function declared here could be written as an application function.
+** With care, these functions can be used by other libraries.
+*/
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "luadebug.h"
+
+
+
+LUALIB_API int luaL_findstring (const char *name, const char *const list[]) {
+ int i;
+ for (i=0; list[i]; i++)
+ if (strcmp(list[i], name) == 0)
+ return i;
+ return -1; /* name not found */
+}
+
+LUALIB_API void luaL_argerror (lua_State *L, int narg, const char *extramsg) {
+ lua_Debug ar;
+ lua_getstack(L, 0, &ar);
+ lua_getinfo(L, "n", &ar);
+ if (ar.name == NULL)
+ ar.name = "?";
+ luaL_verror(L, "bad argument #%d to `%.50s' (%.100s)",
+ narg, ar.name, extramsg);
+}
+
+
+static void type_error (lua_State *L, int narg, int t) {
+ char buff[50];
+ sprintf(buff, "%.8s expected, got %.8s", lua_typename(L, t),
+ lua_typename(L, lua_type(L, narg)));
+ luaL_argerror(L, narg, buff);
+}
+
+
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) {
+ if (space > lua_stackspace(L))
+ luaL_verror(L, "stack overflow (%.30s)", mes);
+}
+
+
+LUALIB_API void luaL_checktype(lua_State *L, int narg, int t) {
+ if (lua_type(L, narg) != t)
+ type_error(L, narg, t);
+}
+
+
+LUALIB_API void luaL_checkany (lua_State *L, int narg) {
+ if (lua_type(L, narg) == LUA_TNONE)
+ luaL_argerror(L, narg, "value expected");
+}
+
+
+LUALIB_API const char *luaL_check_lstr (lua_State *L, int narg, size_t *len) {
+ const char *s = lua_tostring(L, narg);
+ if (!s) type_error(L, narg, LUA_TSTRING);
+ if (len) *len = lua_strlen(L, narg);
+ return s;
+}
+
+
+LUALIB_API const char *luaL_opt_lstr (lua_State *L, int narg, const char *def, size_t *len) {
+ if (lua_isnull(L, narg)) {
+ if (len)
+ *len = (def ? strlen(def) : 0);
+ return def;
+ }
+ else return luaL_check_lstr(L, narg, len);
+}
+
+
+LUALIB_API long luaL_check_number (lua_State *L, int narg) {
+ long d = lua_tonumber(L, narg);
+ if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
+ type_error(L, narg, LUA_TNUMBER);
+ return d;
+}
+
+
+LUALIB_API long luaL_opt_number (lua_State *L, int narg, long def) {
+ if (lua_isnull(L, narg)) return def;
+ else return luaL_check_number(L, narg);
+}
+
+
+LUALIB_API void luaL_openlib (lua_State *L, const struct luaL_reg *l, int n) {
+ int i;
+ for (i=0; i<n; i++)
+ lua_register(L, l[i].name, l[i].func);
+}
+
+
+LUALIB_API void luaL_verror (lua_State *L, const char *fmt, ...) {
+ char buff[500];
+ va_list argp;
+ va_start(argp, fmt);
+ vsprintf(buff, fmt, argp);
+ va_end(argp);
+ lua_error(L, buff);
+}
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+#define buffempty(B) ((B)->p == (B)->buffer)
+#define bufflen(B) ((B)->p - (B)->buffer)
+#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B)))
+
+#define LIMIT (LUA_MINSTACK/2)
+
+
+static int emptybuffer (luaL_Buffer *B) {
+ size_t l = bufflen(B);
+ if (l == 0) return 0; /* put nothing on stack */
+ else {
+ lua_pushlstring(B->L, B->buffer, l);
+ B->p = B->buffer;
+ B->level++;
+ return 1;
+ }
+}
+
+
+static void adjuststack (luaL_Buffer *B) {
+ if (B->level > 1) {
+ lua_State *L = B->L;
+ int toget = 1; /* number of levels to concat */
+ size_t toplen = lua_strlen(L, -1);
+ do {
+ size_t l = lua_strlen(L, -(toget+1));
+ if (B->level - toget + 1 >= LIMIT || toplen > l) {
+ toplen += l;
+ toget++;
+ }
+ else break;
+ } while (toget < B->level);
+ if (toget >= 2) {
+ lua_concat(L, toget);
+ B->level = B->level - toget + 1;
+ }
+ }
+}
+
+
+LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) {
+ if (emptybuffer(B))
+ adjuststack(B);
+ return B->buffer;
+}
+
+
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
+ while (l--)
+ luaL_putchar(B, *s++);
+}
+
+
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
+ luaL_addlstring(B, s, strlen(s));
+}
+
+
+LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
+ emptybuffer(B);
+ if (B->level == 0)
+ lua_pushlstring(B->L, NULL, 0);
+ else if (B->level > 1)
+ lua_concat(B->L, B->level);
+ B->level = 1;
+}
+
+
+LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
+ lua_State *L = B->L;
+ size_t vl = lua_strlen(L, -1);
+ if (vl <= bufffree(B)) { /* fit into buffer? */
+ memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */
+ B->p += vl;
+ lua_pop(L, 1); /* remove from stack */
+ }
+ else {
+ if (emptybuffer(B))
+ lua_insert(L, -2); /* put buffer before new value */
+ B->level++; /* add new value into B stack */
+ adjuststack(B);
+ }
+}
+
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
+ B->L = L;
+ B->p = B->buffer;
+ B->level = 0;
+}
+
+/* }====================================================== */
diff --git a/src/lua/lauxlib.h b/src/lua/lauxlib.h
new file mode 100644
index 00000000..a8d35aff
--- /dev/null
+++ b/src/lua/lauxlib.h
@@ -0,0 +1,100 @@
+/*
+** $Id: lauxlib.h,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lua.h"
+
+
+#ifndef LUALIB_API
+#define LUALIB_API extern
+#endif
+
+
+struct luaL_reg {
+ const char *name;
+ lua_CFunction func;
+};
+
+
+LUALIB_API void luaL_openlib (lua_State *L, const struct luaL_reg *l, int n);
+LUALIB_API void luaL_argerror (lua_State *L, int numarg, const char *extramsg);
+LUALIB_API const char *luaL_check_lstr (lua_State *L, int numArg, size_t *len);
+LUALIB_API const char *luaL_opt_lstr (lua_State *L, int numArg, const char *def, size_t *len);
+LUALIB_API long luaL_check_number (lua_State *L, int numArg);
+LUALIB_API long luaL_opt_number (lua_State *L, int numArg, long def);
+
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg);
+LUALIB_API void luaL_checktype (lua_State *L, int narg, int t);
+LUALIB_API void luaL_checkany (lua_State *L, int narg);
+
+LUALIB_API void luaL_verror (lua_State *L, const char *fmt, ...);
+LUALIB_API int luaL_findstring (const char *name, const char *const list[]);
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define luaL_arg_check(L, cond,numarg,extramsg) if (!(cond)) \
+ luaL_argerror(L, numarg,extramsg)
+#define luaL_check_string(L,n) (luaL_check_lstr(L, (n), NULL))
+#define luaL_opt_string(L,n,d) (luaL_opt_lstr(L, (n), (d), NULL))
+#define luaL_check_int(L,n) ((int)luaL_check_number(L, n))
+#define luaL_check_long(L,n) ((long)luaL_check_number(L, n))
+#define luaL_opt_int(L,n,d) ((int)luaL_opt_number(L, n,d))
+#define luaL_opt_long(L,n,d) ((long)luaL_opt_number(L, n,d))
+#define luaL_openl(L,a) luaL_openlib(L, a, (sizeof(a)/sizeof(a[0])))
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+#ifndef LUAL_BUFFERSIZE
+#define LUAL_BUFFERSIZE BUFSIZ
+#endif
+
+
+typedef struct luaL_Buffer {
+ char *p; /* current position in buffer */
+ int level;
+ lua_State *L;
+ char buffer[LUAL_BUFFERSIZE];
+} luaL_Buffer;
+
+#define luaL_putchar(B,c) \
+ ((void)((B)->p < &(B)->buffer[LUAL_BUFFERSIZE] || luaL_prepbuffer(B)), \
+ (*(B)->p++ = (char)(c)))
+
+#define luaL_addsize(B,n) ((B)->p += (n))
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B);
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s);
+LUALIB_API void luaL_addvalue (luaL_Buffer *B);
+LUALIB_API void luaL_pushresult (luaL_Buffer *B);
+
+
+/* }====================================================== */
+
+
+#endif
+
+
diff --git a/src/lua/lbaselib.c b/src/lua/lbaselib.c
new file mode 100644
index 00000000..71c643aa
--- /dev/null
+++ b/src/lua/lbaselib.c
@@ -0,0 +1,651 @@
+/*
+** $Id: lbaselib.c,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Basic library
+** See Copyright Notice in lua.h
+*/
+
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "luadebug.h"
+#include "lualib.h"
+
+
+
+/*
+** If your system does not support `stderr', redefine this function, or
+** redefine _ERRORMESSAGE so that it won't need _ALERT.
+*/
+static int luaB__ALERT (lua_State *L) {
+ fputs(luaL_check_string(L, 1), stderr);
+ return 0;
+}
+
+
+/*
+** Basic implementation of _ERRORMESSAGE.
+** The library `liolib' redefines _ERRORMESSAGE for better error information.
+*/
+static int luaB__ERRORMESSAGE (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TSTRING);
+ lua_getglobal(L, LUA_ALERT);
+ if (lua_isfunction(L, -1)) { /* avoid error loop if _ALERT is not defined */
+ lua_Debug ar;
+ lua_pushstring(L, "error: ");
+ lua_pushvalue(L, 1);
+ if (lua_getstack(L, 1, &ar)) {
+ lua_getinfo(L, "Sl", &ar);
+ if (ar.source && ar.currentline > 0) {
+ char buff[100];
+ sprintf(buff, "\n <%.70s: line %d>", ar.short_src, ar.currentline);
+ lua_pushstring(L, buff);
+ lua_concat(L, 2);
+ }
+ }
+ lua_pushstring(L, "\n");
+ lua_concat(L, 3);
+ lua_rawcall(L, 1, 0);
+ }
+ return 0;
+}
+
+
+/*
+** If your system does not support `stdout', you can just remove this function.
+** If you need, you can define your own `print' function, following this
+** model but changing `fputs' to put the strings at a proper place
+** (a console window or a log file, for instance).
+*/
+static int luaB_print (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ lua_getglobal(L, "tostring");
+ for (i=1; i<=n; i++) {
+ const char *s;
+ lua_pushvalue(L, -1); /* function to be called */
+ lua_pushvalue(L, i); /* value to print */
+ lua_rawcall(L, 1, 1);
+ s = lua_tostring(L, -1); /* get result */
+ if (s == NULL)
+ lua_error(L, "`tostring' must return a string to `print'");
+ if (i>1) fputs("\t", stdout);
+ fputs(s, stdout);
+ lua_pop(L, 1); /* pop result */
+ }
+ fputs("\n", stdout);
+ return 0;
+}
+
+
+static int luaB_tonumber (lua_State *L) {
+ int base = luaL_opt_int(L, 2, 10);
+ if (base == 10) { /* standard conversion */
+ luaL_checkany(L, 1);
+ if (lua_isnumber(L, 1)) {
+ lua_pushnumber(L, lua_tonumber(L, 1));
+ return 1;
+ }
+ }
+ else {
+ const char *s1 = luaL_check_string(L, 1);
+ char *s2;
+ unsigned long n;
+ luaL_arg_check(L, 2 <= base && base <= 36, 2, "base out of range");
+ n = strtoul(s1, &s2, base);
+ if (s1 != s2) { /* at least one valid digit? */
+ while (isspace((unsigned char)*s2)) s2++; /* skip trailing spaces */
+ if (*s2 == '\0') { /* no invalid trailing characters? */
+ lua_pushnumber(L, n);
+ return 1;
+ }
+ }
+ }
+ lua_pushnil(L); /* else not a number */
+ return 1;
+}
+
+
+static int luaB_error (lua_State *L) {
+ lua_error(L, luaL_opt_string(L, 1, NULL));
+ return 0; /* to avoid warnings */
+}
+
+static int luaB_setglobal (lua_State *L) {
+ luaL_checkany(L, 2);
+ lua_setglobal(L, luaL_check_string(L, 1));
+ return 0;
+}
+
+static int luaB_getglobal (lua_State *L) {
+ lua_getglobal(L, luaL_check_string(L, 1));
+ return 1;
+}
+
+static int luaB_tag (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushnumber(L, lua_tag(L, 1));
+ return 1;
+}
+
+static int luaB_settag (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushvalue(L, 1); /* push table */
+ lua_settag(L, luaL_check_int(L, 2));
+ return 1; /* return table */
+}
+
+static int luaB_newtag (lua_State *L) {
+ lua_pushnumber(L, lua_newtag(L));
+ return 1;
+}
+
+static int luaB_copytagmethods (lua_State *L) {
+ lua_pushnumber(L, lua_copytagmethods(L, luaL_check_int(L, 1),
+ luaL_check_int(L, 2)));
+ return 1;
+}
+
+static int luaB_globals (lua_State *L) {
+ lua_getglobals(L); /* value to be returned */
+ if (!lua_isnull(L, 1)) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushvalue(L, 1); /* new table of globals */
+ lua_setglobals(L);
+ }
+ return 1;
+}
+
+static int luaB_rawget (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ lua_rawget(L, -2);
+ return 1;
+}
+
+static int luaB_rawset (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ luaL_checkany(L, 3);
+ lua_rawset(L, -3);
+ return 1;
+}
+
+static int luaB_settagmethod (lua_State *L) {
+ int tag = luaL_check_int(L, 1);
+ const char *event = luaL_check_string(L, 2);
+ luaL_arg_check(L, lua_isfunction(L, 3) || lua_isnil(L, 3), 3,
+ "function or nil expected");
+ if (strcmp(event, "gc") == 0)
+ lua_error(L, "deprecated use: cannot set the `gc' tag method from Lua");
+ lua_gettagmethod(L, tag, event);
+ lua_pushvalue(L, 3);
+ lua_settagmethod(L, tag, event);
+ return 1;
+}
+
+
+static int luaB_gettagmethod (lua_State *L) {
+ int tag = luaL_check_int(L, 1);
+ const char *event = luaL_check_string(L, 2);
+ if (strcmp(event, "gc") == 0)
+ lua_error(L, "deprecated use: cannot get the `gc' tag method from Lua");
+ lua_gettagmethod(L, tag, event);
+ return 1;
+}
+
+
+static int luaB_gcinfo (lua_State *L) {
+ lua_pushnumber(L, lua_getgccount(L));
+ lua_pushnumber(L, lua_getgcthreshold(L));
+ return 2;
+}
+
+
+static int luaB_collectgarbage (lua_State *L) {
+ lua_setgcthreshold(L, luaL_opt_int(L, 1, 0));
+ return 0;
+}
+
+
+static int luaB_type (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
+ return 1;
+}
+
+
+static int luaB_next (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 2); /* create a 2nd argument if there isn't one */
+ if (lua_next(L, 1))
+ return 2;
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int passresults (lua_State *L, int status, int oldtop) {
+ static const char *const errornames[] =
+ {"ok", "run-time error", "file error", "syntax error",
+ "memory error", "error in error handling"};
+ if (status == 0) {
+ int nresults = lua_gettop(L) - oldtop;
+ if (nresults > 0)
+ return nresults; /* results are already on the stack */
+ else {
+ lua_pushuserdata(L, NULL); /* at least one result to signal no errors */
+ return 1;
+ }
+ }
+ else { /* error */
+ lua_pushnil(L);
+ lua_pushstring(L, errornames[status]); /* error code */
+ return 2;
+ }
+}
+
+static int luaB_dostring (lua_State *L) {
+ int oldtop = lua_gettop(L);
+ size_t l;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ if (*s == '\27') /* binary files start with ESC... */
+ lua_error(L, "`dostring' cannot run pre-compiled code");
+ return passresults(L, lua_dobuffer(L, s, l, luaL_opt_string(L, 2, s)), oldtop);
+}
+
+
+static int luaB_dofile (lua_State *L) {
+ int oldtop = lua_gettop(L);
+ const char *fname = luaL_opt_string(L, 1, NULL);
+ return passresults(L, lua_dofile(L, fname), oldtop);
+}
+
+
+static int luaB_call (lua_State *L) {
+ int oldtop;
+ const char *options = luaL_opt_string(L, 3, "");
+ int err = 0; /* index of old error method */
+ int i, status;
+ int n;
+ luaL_checktype(L, 2, LUA_TTABLE);
+ n = lua_getn(L, 2);
+ if (!lua_isnull(L, 4)) { /* set new error method */
+ lua_getglobal(L, LUA_ERRORMESSAGE);
+ err = lua_gettop(L); /* get index */
+ lua_pushvalue(L, 4);
+ lua_setglobal(L, LUA_ERRORMESSAGE);
+ }
+ oldtop = lua_gettop(L); /* top before function-call preparation */
+ /* push function */
+ lua_pushvalue(L, 1);
+ luaL_checkstack(L, n, "too many arguments");
+ for (i=0; i<n; i++) /* push arg[1...n] */
+ lua_rawgeti(L, 2, i+1);
+ status = lua_call(L, n, LUA_MULTRET);
+ if (err != 0) { /* restore old error method */
+ lua_pushvalue(L, err);
+ lua_setglobal(L, LUA_ERRORMESSAGE);
+ }
+ if (status != 0) { /* error in call? */
+ if (strchr(options, 'x'))
+ lua_pushnil(L); /* return nil to signal the error */
+ else
+ lua_error(L, NULL); /* propagate error without additional messages */
+ return 1;
+ }
+ if (strchr(options, 'p')) /* pack results? */
+ lua_error(L, "deprecated option `p' in `call'");
+ return lua_gettop(L) - oldtop; /* results are already on the stack */
+}
+
+
+static int luaB_tostring (lua_State *L) {
+ char buff[64];
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ lua_pushstring(L, lua_tostring(L, 1));
+ return 1;
+ case LUA_TSTRING:
+ lua_pushvalue(L, 1);
+ return 1;
+ case LUA_TTABLE:
+ sprintf(buff, "table: %p", lua_topointer(L, 1));
+ break;
+ case LUA_TFUNCTION:
+ sprintf(buff, "function: %p", lua_topointer(L, 1));
+ break;
+ case LUA_TUSERDATA:
+ sprintf(buff, "userdata(%d): %p", lua_tag(L, 1), lua_touserdata(L, 1));
+ break;
+ case LUA_TNIL:
+ lua_pushstring(L, "nil");
+ return 1;
+ default:
+ luaL_argerror(L, 1, "value expected");
+ }
+ lua_pushstring(L, buff);
+ return 1;
+}
+
+
+static int luaB_foreachi (lua_State *L) {
+ int n, i;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ n = lua_getn(L, 1);
+ for (i=1; i<=n; i++) {
+ lua_pushvalue(L, 2); /* function */
+ lua_pushnumber(L, i); /* 1st argument */
+ lua_rawgeti(L, 1, i); /* 2nd argument */
+ lua_rawcall(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 1); /* remove nil result */
+ }
+ return 0;
+}
+
+
+static int luaB_foreach (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ lua_pushnil(L); /* first index */
+ for (;;) {
+ if (lua_next(L, 1) == 0)
+ return 0;
+ lua_pushvalue(L, 2); /* function */
+ lua_pushvalue(L, -3); /* key */
+ lua_pushvalue(L, -3); /* value */
+ lua_rawcall(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 2); /* remove value and result */
+ }
+}
+
+
+static int luaB_assert (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (lua_isnil(L, 1))
+ luaL_verror(L, "assertion failed! %.90s", luaL_opt_string(L, 2, ""));
+ return 0;
+}
+
+
+static int luaB_getn (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushnumber(L, lua_getn(L, 1));
+ return 1;
+}
+
+
+static int luaB_tinsert (lua_State *L) {
+ int v = lua_gettop(L); /* last argument: to be inserted */
+ int n, pos;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ n = lua_getn(L, 1);
+ if (v == 2) /* called with only 2 arguments */
+ pos = n+1;
+ else
+ pos = luaL_check_int(L, 2); /* 2nd argument is the position */
+ lua_pushstring(L, "n");
+ lua_pushnumber(L, n+1);
+ lua_rawset(L, 1); /* t.n = n+1 */
+ for (; n>=pos; n--) {
+ lua_rawgeti(L, 1, n);
+ lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */
+ }
+ lua_pushvalue(L, v);
+ lua_rawseti(L, 1, pos); /* t[pos] = v */
+ return 0;
+}
+
+
+static int luaB_tremove (lua_State *L) {
+ int pos, n;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ n = lua_getn(L, 1);
+ pos = luaL_opt_int(L, 2, n);
+ if (n <= 0) return 0; /* table is "empty" */
+ lua_rawgeti(L, 1, pos); /* result = t[pos] */
+ for ( ;pos<n; pos++) {
+ lua_rawgeti(L, 1, pos+1);
+ lua_rawseti(L, 1, pos); /* a[pos] = a[pos+1] */
+ }
+ lua_pushstring(L, "n");
+ lua_pushnumber(L, n-1);
+ lua_rawset(L, 1); /* t.n = n-1 */
+ lua_pushnil(L);
+ lua_rawseti(L, 1, n); /* t[n] = nil */
+ return 1;
+}
+
+
+
+
+/*
+** {======================================================
+** Quicksort
+** (based on `Algorithms in MODULA-3', Robert Sedgewick;
+** Addison-Wesley, 1993.)
+*/
+
+
+static void set2 (lua_State *L, int i, int j) {
+ lua_rawseti(L, 1, i);
+ lua_rawseti(L, 1, j);
+}
+
+static int sort_comp (lua_State *L, int a, int b) {
+ /* WARNING: the caller (auxsort) must ensure stack space */
+ if (!lua_isnil(L, 2)) { /* function? */
+ int res;
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, a-1); /* -1 to compensate function */
+ lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */
+ lua_rawcall(L, 2, 1);
+ res = !lua_isnil(L, -1);
+ lua_pop(L, 1);
+ return res;
+ }
+ else /* a < b? */
+ return lua_lessthan(L, a, b);
+}
+
+static void auxsort (lua_State *L, int l, int u) {
+ while (l < u) { /* for tail recursion */
+ int i, j;
+ /* sort elements a[l], a[(l+u)/2] and a[u] */
+ lua_rawgeti(L, 1, l);
+ lua_rawgeti(L, 1, u);
+ if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
+ set2(L, l, u); /* swap a[l] - a[u] */
+ else
+ lua_pop(L, 2);
+ if (u-l == 1) break; /* only 2 elements */
+ i = (l+u)/2;
+ lua_rawgeti(L, 1, i);
+ lua_rawgeti(L, 1, l);
+ if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */
+ set2(L, i, l);
+ else {
+ lua_pop(L, 1); /* remove a[l] */
+ lua_rawgeti(L, 1, u);
+ if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */
+ set2(L, i, u);
+ else
+ lua_pop(L, 2);
+ }
+ if (u-l == 2) break; /* only 3 elements */
+ lua_rawgeti(L, 1, i); /* Pivot */
+ lua_pushvalue(L, -1);
+ lua_rawgeti(L, 1, u-1);
+ set2(L, i, u-1);
+ /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
+ i = l; j = u-1;
+ for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
+ /* repeat ++i until a[i] >= P */
+ while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
+ if (i>u) lua_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[i] */
+ }
+ /* repeat --j until a[j] <= P */
+ while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
+ if (j<l) lua_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[j] */
+ }
+ if (j<i) {
+ lua_pop(L, 3); /* pop pivot, a[i], a[j] */
+ break;
+ }
+ set2(L, i, j);
+ }
+ lua_rawgeti(L, 1, u-1);
+ lua_rawgeti(L, 1, i);
+ set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */
+ /* a[l..i-1] <= a[i] == P <= a[i+1..u] */
+ /* adjust so that smaller "half" is in [j..i] and larger one in [l..u] */
+ if (i-l < u-i) {
+ j=l; i=i-1; l=i+2;
+ }
+ else {
+ j=i+1; i=u; u=j-2;
+ }
+ auxsort(L, j, i); /* call recursively the smaller one */
+ } /* repeat the routine for the larger one */
+}
+
+static int luaB_sort (lua_State *L) {
+ int n;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ n = lua_getn(L, 1);
+ if (!lua_isnull(L, 2)) /* is there a 2nd argument? */
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ lua_settop(L, 2); /* make sure there is two arguments */
+ auxsort(L, 1, n);
+ return 0;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Deprecated functions to manipulate global environment.
+** =======================================================
+*/
+
+
+#define num_deprecated 4
+
+static const struct luaL_reg deprecated_names [num_deprecated] = {
+ {"foreachvar", luaB_foreach},
+ {"nextvar", luaB_next},
+ {"rawgetglobal", luaB_rawget},
+ {"rawsetglobal", luaB_rawset}
+};
+
+
+#ifdef LUA_DEPRECATEDFUNCS
+
+/*
+** call corresponding function inserting `globals' as first argument
+*/
+static int deprecated_func (lua_State *L) {
+ lua_insert(L, 1); /* upvalue is the function to be called */
+ lua_getglobals(L);
+ lua_insert(L, 2); /* table of globals is 1o argument */
+ lua_rawcall(L, lua_gettop(L)-1, LUA_MULTRET);
+ return lua_gettop(L); /* return all results */
+}
+
+
+static void deprecated_funcs (lua_State *L) {
+ int i;
+ for (i=0; i<num_deprecated; i++) {
+ lua_pushcfunction(L, deprecated_names[i].func);
+ lua_pushcclosure(L, deprecated_func, 1);
+ lua_setglobal(L, deprecated_names[i].name);
+ }
+}
+
+
+#else
+
+/*
+** gives an explicit error in any attempt to call a deprecated function
+*/
+static int deprecated_func (lua_State *L) {
+ luaL_verror(L, "function `%.20s' is deprecated", lua_tostring(L, -1));
+ return 0; /* to avoid warnings */
+}
+
+
+static void deprecated_funcs (lua_State *L) {
+ int i;
+ for (i=0; i<num_deprecated; i++) {
+ lua_pushstring(L, deprecated_names[i].name);
+ lua_pushcclosure(L, deprecated_func, 1);
+ lua_setglobal(L, deprecated_names[i].name);
+ }
+}
+
+#endif
+
+/* }====================================================== */
+
+static const struct luaL_reg base_funcs[] = {
+ {LUA_ALERT, luaB__ALERT},
+ {LUA_ERRORMESSAGE, luaB__ERRORMESSAGE},
+ {"call", luaB_call},
+ {"collectgarbage", luaB_collectgarbage},
+ {"copytagmethods", luaB_copytagmethods},
+ {"dofile", luaB_dofile},
+ {"dostring", luaB_dostring},
+ {"error", luaB_error},
+ {"foreach", luaB_foreach},
+ {"foreachi", luaB_foreachi},
+ {"gcinfo", luaB_gcinfo},
+ {"getglobal", luaB_getglobal},
+ {"gettagmethod", luaB_gettagmethod},
+ {"globals", luaB_globals},
+ {"newtag", luaB_newtag},
+ {"next", luaB_next},
+ {"print", luaB_print},
+ {"rawget", luaB_rawget},
+ {"rawset", luaB_rawset},
+ {"rawgettable", luaB_rawget}, /* for compatibility */
+ {"rawsettable", luaB_rawset}, /* for compatibility */
+ {"setglobal", luaB_setglobal},
+ {"settag", luaB_settag},
+ {"settagmethod", luaB_settagmethod},
+ {"tag", luaB_tag},
+ {"tonumber", luaB_tonumber},
+ {"tostring", luaB_tostring},
+ {"type", luaB_type},
+ {"assert", luaB_assert},
+ {"getn", luaB_getn},
+ {"sort", luaB_sort},
+ {"tinsert", luaB_tinsert},
+ {"tremove", luaB_tremove}
+};
+
+
+
+LUALIB_API void lua_baselibopen (lua_State *L) {
+ luaL_openl(L, base_funcs);
+ lua_pushstring(L, LUA_VERSION);
+ lua_setglobal(L, "_VERSION");
+ deprecated_funcs(L);
+}
+
diff --git a/src/lua/lcode.c b/src/lua/lcode.c
new file mode 100644
index 00000000..89de4a55
--- /dev/null
+++ b/src/lua/lcode.c
@@ -0,0 +1,701 @@
+/*
+** $Id: lcode.c,v 1.4 2004/06/04 13:42:10 neil Exp $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include "stdlib.h"
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldo.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+
+
+void luaK_error (LexState *ls, const char *msg) {
+ luaX_error(ls, msg, ls->t.token);
+}
+
+
+/*
+** Returns the the previous instruction, for optimizations.
+** If there is a jump target between this and the current instruction,
+** returns a dummy instruction to avoid wrong optimizations.
+*/
+static Instruction previous_instruction (FuncState *fs) {
+ if (fs->pc > fs->lasttarget) /* no jumps to current position? */
+ return fs->f->code[fs->pc-1]; /* returns previous instruction */
+ else
+ return CREATE_0(OP_END); /* no optimizations after an `END' */
+}
+
+
+int luaK_jump (FuncState *fs) {
+ int j = luaK_code1(fs, OP_JMP, NO_JUMP);
+ if (j == fs->lasttarget) { /* possible jumps to this jump? */
+ luaK_concat(fs, &j, fs->jlt); /* keep them on hold */
+ fs->jlt = NO_JUMP;
+ }
+ return j;
+}
+
+
+static void luaK_fixjump (FuncState *fs, int pc, int dest) {
+ Instruction *jmp = &fs->f->code[pc];
+ if (dest == NO_JUMP)
+ SETARG_S(*jmp, NO_JUMP); /* point to itself to represent end of list */
+ else { /* jump is relative to position following jump instruction */
+ int offset = dest-(pc+1);
+ if (abs(offset) > MAXARG_S)
+ luaK_error(fs->ls, "control structure too long");
+ SETARG_S(*jmp, offset);
+ }
+}
+
+
+static int luaK_getjump (FuncState *fs, int pc) {
+ int offset = GETARG_S(fs->f->code[pc]);
+ if (offset == NO_JUMP) /* point to itself represents end of list */
+ return NO_JUMP; /* end of list */
+ else
+ return (pc+1)+offset; /* turn offset into absolute position */
+}
+
+
+/*
+** returns current `pc' and marks it as a jump target (to avoid wrong
+** optimizations with consecutive instructions not in the same basic block).
+** discharge list of jumps to last target.
+*/
+int luaK_getlabel (FuncState *fs) {
+ if (fs->pc != fs->lasttarget) {
+ int lasttarget = fs->lasttarget;
+ fs->lasttarget = fs->pc;
+ luaK_patchlist(fs, fs->jlt, lasttarget); /* discharge old list `jlt' */
+ fs->jlt = NO_JUMP; /* nobody jumps to this new label (yet) */
+ }
+ return fs->pc;
+}
+
+
+void luaK_deltastack (FuncState *fs, int delta) {
+ fs->stacklevel += delta;
+ if (fs->stacklevel > fs->f->maxstacksize) {
+ if (fs->stacklevel > MAXSTACK)
+ luaK_error(fs->ls, "function or expression too complex");
+ fs->f->maxstacksize = fs->stacklevel;
+ }
+}
+
+
+void luaK_kstr (LexState *ls, int c) {
+ luaK_code1(ls->fs, OP_PUSHSTRING, c);
+}
+
+
+static int number_constant (FuncState *fs, Number r) {
+ /* check whether `r' has appeared within the last LOOKBACKNUMS entries */
+ Proto *f = fs->f;
+ int c = f->nknum;
+ int lim = c < LOOKBACKNUMS ? 0 : c-LOOKBACKNUMS;
+ while (--c >= lim)
+ if (f->knum[c] == r) return c;
+ /* not found; create a new entry */
+ luaM_growvector(fs->L, f->knum, f->nknum, 1, Number,
+ "constant table overflow", MAXARG_U);
+ c = f->nknum++;
+ f->knum[c] = r;
+ return c;
+}
+
+
+void luaK_number (FuncState *fs, Number f) {
+ if (f <= (Number)MAXARG_S && (Number)(int)f == f)
+ luaK_code1(fs, OP_PUSHINT, (int)f); /* f has a short integer value */
+ else
+ luaK_code1(fs, OP_PUSHNUM, number_constant(fs, f));
+}
+
+
+void luaK_adjuststack (FuncState *fs, int n) {
+ if (n > 0)
+ luaK_code1(fs, OP_POP, n);
+ else
+ luaK_code1(fs, OP_PUSHNIL, -n);
+}
+
+
+int luaK_lastisopen (FuncState *fs) {
+ /* check whether last instruction is an open function call */
+ Instruction i = previous_instruction(fs);
+ if (GET_OPCODE(i) == OP_CALL && GETARG_B(i) == MULT_RET)
+ return 1;
+ else return 0;
+}
+
+
+void luaK_setcallreturns (FuncState *fs, int nresults) {
+ if (luaK_lastisopen(fs)) { /* expression is an open function call? */
+ SETARG_B(fs->f->code[fs->pc-1], nresults); /* set number of results */
+ luaK_deltastack(fs, nresults); /* push results */
+ }
+}
+
+
+static int discharge (FuncState *fs, expdesc *var) {
+ switch (var->k) {
+ case VLOCAL:
+ luaK_code1(fs, OP_GETLOCAL, var->u.index);
+ break;
+ case VGLOBAL:
+ luaK_code1(fs, OP_GETGLOBAL, var->u.index);
+ break;
+ case VINDEXED:
+ luaK_code0(fs, OP_GETTABLE);
+ break;
+ case VEXP:
+ return 0; /* nothing to do */
+ }
+ var->k = VEXP;
+ var->u.l.t = var->u.l.f = NO_JUMP;
+ return 1;
+}
+
+
+static void discharge1 (FuncState *fs, expdesc *var) {
+ discharge(fs, var);
+ /* if it has jumps then it is already discharged */
+ if (var->u.l.t == NO_JUMP && var->u.l.f == NO_JUMP)
+ luaK_setcallreturns(fs, 1); /* call must return 1 value */
+}
+
+
+void luaK_storevar (LexState *ls, const expdesc *var) {
+ FuncState *fs = ls->fs;
+ switch (var->k) {
+ case VLOCAL:
+ luaK_code1(fs, OP_SETLOCAL, var->u.index);
+ break;
+ case VGLOBAL:
+ luaK_code1(fs, OP_SETGLOBAL, var->u.index);
+ break;
+ case VINDEXED: /* table is at top-3; pop 3 elements after operation */
+ luaK_code2(fs, OP_SETTABLE, 3, 3);
+ break;
+ default:
+ LUA_INTERNALERROR("invalid var kind to store");
+ }
+}
+
+
+static OpCode invertjump (OpCode op) {
+ switch (op) {
+ case OP_JMPNE: return OP_JMPEQ;
+ case OP_JMPEQ: return OP_JMPNE;
+ case OP_JMPLT: return OP_JMPGE;
+ case OP_JMPLE: return OP_JMPGT;
+ case OP_JMPGT: return OP_JMPLE;
+ case OP_JMPGE: return OP_JMPLT;
+ case OP_JMPT: case OP_JMPONT: return OP_JMPF;
+ case OP_JMPF: case OP_JMPONF: return OP_JMPT;
+ default:
+ LUA_INTERNALERROR("invalid jump instruction");
+ return OP_END; /* to avoid warnings */
+ }
+}
+
+
+static void luaK_patchlistaux (FuncState *fs, int list, int target,
+ OpCode special, int special_target) {
+ Instruction *code = fs->f->code;
+ while (list != NO_JUMP) {
+ int next = luaK_getjump(fs, list);
+ Instruction *i = &code[list];
+ OpCode op = GET_OPCODE(*i);
+ if (op == special) /* this `op' already has a value */
+ luaK_fixjump(fs, list, special_target);
+ else {
+ luaK_fixjump(fs, list, target); /* do the patch */
+ if (op == OP_JMPONT) /* remove eventual values */
+ SET_OPCODE(*i, OP_JMPT);
+ else if (op == OP_JMPONF)
+ SET_OPCODE(*i, OP_JMPF);
+ }
+ list = next;
+ }
+}
+
+
+void luaK_patchlist (FuncState *fs, int list, int target) {
+ if (target == fs->lasttarget) /* same target that list `jlt'? */
+ luaK_concat(fs, &fs->jlt, list); /* delay fixing */
+ else
+ luaK_patchlistaux(fs, list, target, OP_END, 0);
+}
+
+
+static int need_value (FuncState *fs, int list, OpCode hasvalue) {
+ /* check whether list has a jump without a value */
+ for (; list != NO_JUMP; list = luaK_getjump(fs, list))
+ if (GET_OPCODE(fs->f->code[list]) != hasvalue) return 1;
+ return 0; /* not found */
+}
+
+
+void luaK_concat (FuncState *fs, int *l1, int l2) {
+ if (*l1 == NO_JUMP)
+ *l1 = l2;
+ else {
+ int list = *l1;
+ for (;;) { /* traverse `l1' */
+ int next = luaK_getjump(fs, list);
+ if (next == NO_JUMP) { /* end of list? */
+ luaK_fixjump(fs, list, l2);
+ return;
+ }
+ list = next;
+ }
+ }
+}
+
+
+static void luaK_testgo (FuncState *fs, expdesc *v, int invert, OpCode jump) {
+ int prevpos; /* position of last instruction */
+ Instruction *previous;
+ int *golist, *exitlist;
+ if (!invert) {
+ golist = &v->u.l.f; /* go if false */
+ exitlist = &v->u.l.t; /* exit if true */
+ }
+ else {
+ golist = &v->u.l.t; /* go if true */
+ exitlist = &v->u.l.f; /* exit if false */
+ }
+ discharge1(fs, v);
+ prevpos = fs->pc-1;
+ previous = &fs->f->code[prevpos];
+ LUA_ASSERT(*previous==previous_instruction(fs), "no jump allowed here");
+ if (!ISJUMP(GET_OPCODE(*previous)))
+ prevpos = luaK_code1(fs, jump, NO_JUMP);
+ else { /* last instruction is already a jump */
+ if (invert)
+ SET_OPCODE(*previous, invertjump(GET_OPCODE(*previous)));
+ }
+ luaK_concat(fs, exitlist, prevpos); /* insert last jump in `exitlist' */
+ luaK_patchlist(fs, *golist, luaK_getlabel(fs));
+ *golist = NO_JUMP;
+}
+
+
+void luaK_goiftrue (FuncState *fs, expdesc *v, int keepvalue) {
+ luaK_testgo(fs, v, 1, keepvalue ? OP_JMPONF : OP_JMPF);
+}
+
+
+static void luaK_goiffalse (FuncState *fs, expdesc *v, int keepvalue) {
+ luaK_testgo(fs, v, 0, keepvalue ? OP_JMPONT : OP_JMPT);
+}
+
+
+static int code_label (FuncState *fs, OpCode op, int arg) {
+ luaK_getlabel(fs); /* those instructions may be jump targets */
+ return luaK_code1(fs, op, arg);
+}
+
+
+void luaK_tostack (LexState *ls, expdesc *v, int onlyone) {
+ FuncState *fs = ls->fs;
+ if (!discharge(fs, v)) { /* `v' is an expression? */
+ OpCode previous = GET_OPCODE(fs->f->code[fs->pc-1]);
+ if (!ISJUMP(previous) && v->u.l.f == NO_JUMP && v->u.l.t == NO_JUMP) {
+ /* expression has no jumps */
+ if (onlyone)
+ luaK_setcallreturns(fs, 1); /* call must return 1 value */
+ }
+ else { /* expression has jumps */
+ int final; /* position after whole expression */
+ int j = NO_JUMP; /* eventual jump over values */
+ int p_nil = NO_JUMP; /* position of an eventual PUSHNIL */
+ int p_1 = NO_JUMP; /* position of an eventual PUSHINT */
+ if (ISJUMP(previous) || need_value(fs, v->u.l.f, OP_JMPONF)
+ || need_value(fs, v->u.l.t, OP_JMPONT)) {
+ /* expression needs values */
+ if (ISJUMP(previous))
+ luaK_concat(fs, &v->u.l.t, fs->pc-1); /* put `previous' in t. list */
+ else {
+ j = code_label(fs, OP_JMP, NO_JUMP); /* to jump over both pushes */
+ /* correct stack for compiler and symbolic execution */
+ luaK_adjuststack(fs, 1);
+ }
+ p_nil = code_label(fs, OP_PUSHNILJMP, 0);
+ p_1 = code_label(fs, OP_PUSHINT, 1);
+ luaK_patchlist(fs, j, luaK_getlabel(fs));
+ }
+ final = luaK_getlabel(fs);
+ luaK_patchlistaux(fs, v->u.l.f, p_nil, OP_JMPONF, final);
+ luaK_patchlistaux(fs, v->u.l.t, p_1, OP_JMPONT, final);
+ v->u.l.f = v->u.l.t = NO_JUMP;
+ }
+ }
+}
+
+
+void luaK_prefix (LexState *ls, UnOpr op, expdesc *v) {
+ FuncState *fs = ls->fs;
+ if (op == OPR_MINUS) {
+ luaK_tostack(ls, v, 1);
+ luaK_code0(fs, OP_MINUS);
+ }
+ else { /* op == NOT */
+ Instruction *previous;
+ discharge1(fs, v);
+ previous = &fs->f->code[fs->pc-1];
+ if (ISJUMP(GET_OPCODE(*previous)))
+ SET_OPCODE(*previous, invertjump(GET_OPCODE(*previous)));
+ else
+ luaK_code0(fs, OP_NOT);
+ /* interchange true and false lists */
+ { int temp = v->u.l.f; v->u.l.f = v->u.l.t; v->u.l.t = temp; }
+ }
+}
+
+
+void luaK_infix (LexState *ls, BinOpr op, expdesc *v) {
+ FuncState *fs = ls->fs;
+ switch (op) {
+ case OPR_AND:
+ luaK_goiftrue(fs, v, 1);
+ break;
+ case OPR_OR:
+ luaK_goiffalse(fs, v, 1);
+ break;
+ default:
+ luaK_tostack(ls, v, 1); /* all other binary operators need a value */
+ }
+}
+
+
+
+static const struct {
+ OpCode opcode; /* opcode for each binary operator */
+ int arg; /* default argument for the opcode */
+} codes[] = { /* ORDER OPR */
+ {OP_ADD, 0}, {OP_SUB, 0}, {OP_MULT, 0}, {OP_DIV, 0},
+ {OP_POW, 0}, {OP_CONCAT, 2},
+ {OP_JMPNE, NO_JUMP}, {OP_JMPEQ, NO_JUMP},
+ {OP_JMPLT, NO_JUMP}, {OP_JMPLE, NO_JUMP},
+ {OP_JMPGT, NO_JUMP}, {OP_JMPGE, NO_JUMP}
+};
+
+
+void luaK_posfix (LexState *ls, BinOpr op, expdesc *v1, expdesc *v2) {
+ FuncState *fs = ls->fs;
+ switch (op) {
+ case OPR_AND: {
+ LUA_ASSERT(v1->u.l.t == NO_JUMP, "list must be closed");
+ discharge1(fs, v2);
+ v1->u.l.t = v2->u.l.t;
+ luaK_concat(fs, &v1->u.l.f, v2->u.l.f);
+ break;
+ }
+ case OPR_OR: {
+ LUA_ASSERT(v1->u.l.f == NO_JUMP, "list must be closed");
+ discharge1(fs, v2);
+ v1->u.l.f = v2->u.l.f;
+ luaK_concat(fs, &v1->u.l.t, v2->u.l.t);
+ break;
+ }
+ default: {
+ luaK_tostack(ls, v2, 1); /* `v2' must be a value */
+ luaK_code1(fs, codes[op].opcode, codes[op].arg);
+ }
+ }
+}
+
+
+static void codelineinfo (FuncState *fs) {
+ Proto *f = fs->f;
+ LexState *ls = fs->ls;
+ if (ls->lastline > fs->lastline) {
+ luaM_growvector(fs->L, f->lineinfo, f->nlineinfo, 2, int,
+ "line info overflow", MAX_INT);
+ if (ls->lastline > fs->lastline+1)
+ f->lineinfo[f->nlineinfo++] = -(ls->lastline - (fs->lastline+1));
+ f->lineinfo[f->nlineinfo++] = fs->pc;
+ fs->lastline = ls->lastline;
+ }
+}
+
+
+int luaK_code0 (FuncState *fs, OpCode o) {
+ return luaK_code2(fs, o, 0, 0);
+}
+
+
+int luaK_code1 (FuncState *fs, OpCode o, int arg1) {
+ return luaK_code2(fs, o, arg1, 0);
+}
+
+
+int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2) {
+ Instruction i = previous_instruction(fs);
+ int delta = luaK_opproperties[o].push - luaK_opproperties[o].pop;
+ int optm = 0; /* 1 when there is an optimization */
+ switch (o) {
+ case OP_CLOSURE: {
+ delta = -arg2+1;
+ break;
+ }
+ case OP_SETTABLE: {
+ delta = -arg2;
+ break;
+ }
+ case OP_SETLIST: {
+ if (arg2 == 0) return NO_JUMP; /* nothing to do */
+ delta = -arg2;
+ break;
+ }
+ case OP_SETMAP: {
+ if (arg1 == 0) return NO_JUMP; /* nothing to do */
+ delta = -2*arg1;
+ break;
+ }
+ case OP_RETURN: {
+ if (GET_OPCODE(i) == OP_CALL && GETARG_B(i) == MULT_RET) {
+ SET_OPCODE(i, OP_TAILCALL);
+ SETARG_B(i, arg1);
+ optm = 1;
+ }
+ break;
+ }
+ case OP_PUSHNIL: {
+ if (arg1 == 0) return NO_JUMP; /* nothing to do */
+ delta = arg1;
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHNIL: SETARG_U(i, GETARG_U(i)+arg1); optm = 1; break;
+ default: break;
+ }
+ break;
+ }
+ case OP_POP: {
+ if (arg1 == 0) return NO_JUMP; /* nothing to do */
+ delta = -arg1;
+ switch(GET_OPCODE(i)) {
+ case OP_SETTABLE: SETARG_B(i, GETARG_B(i)+arg1); optm = 1; break;
+ default: break;
+ }
+ break;
+ }
+ case OP_GETTABLE: {
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHSTRING: /* `t.x' */
+ SET_OPCODE(i, OP_GETDOTTED);
+ optm = 1;
+ break;
+ case OP_GETLOCAL: /* `t[i]' */
+ SET_OPCODE(i, OP_GETINDEXED);
+ optm = 1;
+ break;
+ default: break;
+ }
+ break;
+ }
+ case OP_ADD: {
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHINT: SET_OPCODE(i, OP_ADDI); optm = 1; break; /* `a+k' */
+ default: break;
+ }
+ break;
+ }
+ case OP_SUB: {
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHINT: /* `a-k' */
+ i = CREATE_S(OP_ADDI, -GETARG_S(i));
+ optm = 1;
+ break;
+ default: break;
+ }
+ break;
+ }
+ case OP_CONCAT: {
+ delta = -arg1+1;
+ switch(GET_OPCODE(i)) {
+ case OP_CONCAT: /* `a..b..c' */
+ SETARG_U(i, GETARG_U(i)+1);
+ optm = 1;
+ break;
+ default: break;
+ }
+ break;
+ }
+ case OP_MINUS: {
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHINT: /* `-k' */
+ SETARG_S(i, -GETARG_S(i));
+ optm = 1;
+ break;
+ case OP_PUSHNUM: /* `-k' */
+ SET_OPCODE(i, OP_PUSHNEGNUM);
+ optm = 1;
+ break;
+ default: break;
+ }
+ break;
+ }
+ case OP_JMPNE: {
+ if (i == CREATE_U(OP_PUSHNIL, 1)) { /* `a~=nil' */
+ i = CREATE_S(OP_JMPT, NO_JUMP);
+ optm = 1;
+ }
+ break;
+ }
+ case OP_JMPEQ: {
+ if (i == CREATE_U(OP_PUSHNIL, 1)) { /* `a==nil' */
+ i = CREATE_0(OP_NOT);
+ delta = -1; /* just undo effect of previous PUSHNIL */
+ optm = 1;
+ }
+ break;
+ }
+ case OP_JMPT:
+ case OP_JMPONT: {
+ switch (GET_OPCODE(i)) {
+ case OP_NOT: {
+ i = CREATE_S(OP_JMPF, NO_JUMP);
+ optm = 1;
+ break;
+ }
+ case OP_PUSHINT: {
+ if (o == OP_JMPT) { /* JMPONT must keep original integer value */
+ i = CREATE_S(OP_JMP, NO_JUMP);
+ optm = 1;
+ }
+ break;
+ }
+ case OP_PUSHNIL: {
+ if (GETARG_U(i) == 1) {
+ fs->pc--; /* erase previous instruction */
+ luaK_deltastack(fs, -1); /* correct stack */
+ return NO_JUMP;
+ }
+ break;
+ }
+ default: break;
+ }
+ break;
+ }
+ case OP_JMPF:
+ case OP_JMPONF: {
+ switch (GET_OPCODE(i)) {
+ case OP_NOT: {
+ i = CREATE_S(OP_JMPT, NO_JUMP);
+ optm = 1;
+ break;
+ }
+ case OP_PUSHINT: { /* `while 1 do ...' */
+ fs->pc--; /* erase previous instruction */
+ luaK_deltastack(fs, -1); /* correct stack */
+ return NO_JUMP;
+ }
+ case OP_PUSHNIL: { /* `repeat ... until nil' */
+ if (GETARG_U(i) == 1) {
+ i = CREATE_S(OP_JMP, NO_JUMP);
+ optm = 1;
+ }
+ break;
+ }
+ default: break;
+ }
+ break;
+ }
+ case OP_GETDOTTED:
+ case OP_GETINDEXED:
+ case OP_TAILCALL:
+ case OP_ADDI: {
+ LUA_INTERNALERROR("instruction used only for optimizations");
+ break;
+ }
+ default: {
+ LUA_ASSERT(delta != VD, "invalid delta");
+ break;
+ }
+ }
+ luaK_deltastack(fs, delta);
+ if (optm) { /* optimize: put instruction in place of last one */
+ fs->f->code[fs->pc-1] = i; /* change previous instruction */
+ return fs->pc-1; /* do not generate new instruction */
+ }
+ /* else build new instruction */
+ switch ((enum Mode)luaK_opproperties[o].mode) {
+ case iO: i = CREATE_0(o); break;
+ case iU: i = CREATE_U(o, arg1); break;
+ case iS: i = CREATE_S(o, arg1); break;
+ case iAB: i = CREATE_AB(o, arg1, arg2); break;
+ }
+ codelineinfo(fs);
+ /* put new instruction in code array */
+ luaM_growvector(fs->L, fs->f->code, fs->pc, 1, Instruction,
+ "code size overflow", MAX_INT);
+ fs->f->code[fs->pc] = i;
+ return fs->pc++;
+}
+
+
+const struct OpProperties luaK_opproperties[NUM_OPCODES] = {
+ {iO, 0, 0}, /* OP_END */
+ {iU, 0, 0}, /* OP_RETURN */
+ {iAB, 0, 0}, /* OP_CALL */
+ {iAB, 0, 0}, /* OP_TAILCALL */
+ {iU, VD, 0}, /* OP_PUSHNIL */
+ {iU, VD, 0}, /* OP_POP */
+ {iS, 1, 0}, /* OP_PUSHINT */
+ {iU, 1, 0}, /* OP_PUSHSTRING */
+ {iU, 1, 0}, /* OP_PUSHNUM */
+ {iU, 1, 0}, /* OP_PUSHNEGNUM */
+ {iU, 1, 0}, /* OP_PUSHUPVALUE */
+ {iU, 1, 0}, /* OP_GETLOCAL */
+ {iU, 1, 0}, /* OP_GETGLOBAL */
+ {iO, 1, 2}, /* OP_GETTABLE */
+ {iU, 1, 1}, /* OP_GETDOTTED */
+ {iU, 1, 1}, /* OP_GETINDEXED */
+ {iU, 2, 1}, /* OP_PUSHSELF */
+ {iU, 1, 0}, /* OP_CREATETABLE */
+ {iU, 0, 1}, /* OP_SETLOCAL */
+ {iU, 0, 1}, /* OP_SETGLOBAL */
+ {iAB, VD, 0}, /* OP_SETTABLE */
+ {iAB, VD, 0}, /* OP_SETLIST */
+ {iU, VD, 0}, /* OP_SETMAP */
+ {iO, 1, 2}, /* OP_ADD */
+ {iS, 1, 1}, /* OP_ADDI */
+ {iO, 1, 2}, /* OP_SUB */
+ {iO, 1, 2}, /* OP_MULT */
+ {iO, 1, 2}, /* OP_DIV */
+ {iO, 1, 2}, /* OP_POW */
+ {iU, VD, 0}, /* OP_CONCAT */
+ {iO, 1, 1}, /* OP_MINUS */
+ {iO, 1, 1}, /* OP_NOT */
+ {iS, 0, 2}, /* OP_JMPNE */
+ {iS, 0, 2}, /* OP_JMPEQ */
+ {iS, 0, 2}, /* OP_JMPLT */
+ {iS, 0, 2}, /* OP_JMPLE */
+ {iS, 0, 2}, /* OP_JMPGT */
+ {iS, 0, 2}, /* OP_JMPGE */
+ {iS, 0, 1}, /* OP_JMPT */
+ {iS, 0, 1}, /* OP_JMPF */
+ {iS, 0, 1}, /* OP_JMPONT */
+ {iS, 0, 1}, /* OP_JMPONF */
+ {iS, 0, 0}, /* OP_JMP */
+ {iO, 0, 0}, /* OP_PUSHNILJMP */
+ {iS, 0, 0}, /* OP_FORPREP */
+ {iS, 0, 3}, /* OP_FORLOOP */
+ {iS, 2, 0}, /* OP_LFORPREP */
+ {iS, 0, 3}, /* OP_LFORLOOP */
+ {iAB, VD, 0} /* OP_CLOSURE */
+};
+
diff --git a/src/lua/lcode.h b/src/lua/lcode.h
new file mode 100644
index 00000000..c413c897
--- /dev/null
+++ b/src/lua/lcode.h
@@ -0,0 +1,70 @@
+/*
+** $Id: lcode.h,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lcode_h
+#define lcode_h
+
+#include "llex.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+
+
+/*
+** Marks the end of a patch list. It is an invalid value both as an absolute
+** address, and as a list link (would link an element to itself).
+*/
+#define NO_JUMP (-1)
+
+
+/*
+** grep "ORDER OPR" if you change these enums
+*/
+typedef enum BinOpr {
+ OPR_ADD, OPR_SUB, OPR_MULT, OPR_DIV, OPR_POW,
+ OPR_CONCAT,
+ OPR_NE, OPR_EQ, OPR_LT, OPR_LE, OPR_GT, OPR_GE,
+ OPR_AND, OPR_OR,
+ OPR_NOBINOPR
+} BinOpr;
+
+typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_NOUNOPR } UnOpr;
+
+
+enum Mode {iO, iU, iS, iAB}; /* instruction format */
+
+#define VD 100 /* flag for variable delta */
+
+extern const struct OpProperties {
+ char mode;
+ unsigned char push;
+ unsigned char pop;
+} luaK_opproperties[NUM_OPCODES];
+
+
+void luaK_error (LexState *ls, const char *msg);
+int luaK_code0 (FuncState *fs, OpCode o);
+int luaK_code1 (FuncState *fs, OpCode o, int arg1);
+int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2);
+int luaK_jump (FuncState *fs);
+void luaK_patchlist (FuncState *fs, int list, int target);
+void luaK_concat (FuncState *fs, int *l1, int l2);
+void luaK_goiftrue (FuncState *fs, expdesc *v, int keepvalue);
+int luaK_getlabel (FuncState *fs);
+void luaK_deltastack (FuncState *fs, int delta);
+void luaK_kstr (LexState *ls, int c);
+void luaK_number (FuncState *fs, Number f);
+void luaK_adjuststack (FuncState *fs, int n);
+int luaK_lastisopen (FuncState *fs);
+void luaK_setcallreturns (FuncState *fs, int nresults);
+void luaK_tostack (LexState *ls, expdesc *v, int onlyone);
+void luaK_storevar (LexState *ls, const expdesc *var);
+void luaK_prefix (LexState *ls, UnOpr op, expdesc *v);
+void luaK_infix (LexState *ls, BinOpr op, expdesc *v);
+void luaK_posfix (LexState *ls, BinOpr op, expdesc *v1, expdesc *v2);
+
+
+#endif
diff --git a/src/lua/ldblib.c b/src/lua/ldblib.c
new file mode 100644
index 00000000..481f1d6f
--- /dev/null
+++ b/src/lua/ldblib.c
@@ -0,0 +1,188 @@
+/*
+** $Id: ldblib.c,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Interface from Lua to its debug API
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "luadebug.h"
+#include "lualib.h"
+
+
+
+static void settabss (lua_State *L, const char *i, const char *v) {
+ lua_pushstring(L, i);
+ lua_pushstring(L, v);
+ lua_settable(L, -3);
+}
+
+
+static void settabsi (lua_State *L, const char *i, int v) {
+ lua_pushstring(L, i);
+ lua_pushnumber(L, v);
+ lua_settable(L, -3);
+}
+
+
+static int getinfo (lua_State *L) {
+ lua_Debug ar;
+ const char *options = luaL_opt_string(L, 2, "flnSu");
+ char buff[20];
+ if (lua_isnumber(L, 1)) {
+ if (!lua_getstack(L, (int)lua_tonumber(L, 1), &ar)) {
+ lua_pushnil(L); /* level out of range */
+ return 1;
+ }
+ }
+ else if (lua_isfunction(L, 1)) {
+ lua_pushvalue(L, 1);
+ sprintf(buff, ">%.10s", options);
+ options = buff;
+ }
+ else
+ luaL_argerror(L, 1, "function or level expected");
+ if (!lua_getinfo(L, options, &ar))
+ luaL_argerror(L, 2, "invalid option");
+ lua_newtable(L);
+ for (; *options; options++) {
+ switch (*options) {
+ case 'S':
+ settabss(L, "source", ar.source);
+ if (ar.source)
+ settabss(L, "short_src", ar.short_src);
+ settabsi(L, "linedefined", ar.linedefined);
+ settabss(L, "what", ar.what);
+ break;
+ case 'l':
+ settabsi(L, "currentline", ar.currentline);
+ break;
+ case 'u':
+ settabsi(L, "nups", ar.nups);
+ break;
+ case 'n':
+ settabss(L, "name", ar.name);
+ settabss(L, "namewhat", ar.namewhat);
+ break;
+ case 'f':
+ lua_pushstring(L, "func");
+ lua_pushvalue(L, -3);
+ lua_settable(L, -3);
+ break;
+ }
+ }
+ return 1; /* return table */
+}
+
+
+static int getlocal (lua_State *L) {
+ lua_Debug ar;
+ const char *name;
+ if (!lua_getstack(L, luaL_check_int(L, 1), &ar)) /* level out of range? */
+ luaL_argerror(L, 1, "level out of range");
+ name = lua_getlocal(L, &ar, luaL_check_int(L, 2));
+ if (name) {
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+ return 2;
+ }
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int setlocal (lua_State *L) {
+ lua_Debug ar;
+ if (!lua_getstack(L, luaL_check_int(L, 1), &ar)) /* level out of range? */
+ luaL_argerror(L, 1, "level out of range");
+ luaL_checkany(L, 3);
+ lua_pushstring(L, lua_setlocal(L, &ar, luaL_check_int(L, 2)));
+ return 1;
+}
+
+
+
+/* dummy variables (to define unique addresses) */
+static char key1, key2;
+#define KEY_CALLHOOK (&key1)
+#define KEY_LINEHOOK (&key2)
+
+
+static void hookf (lua_State *L, void *key) {
+ lua_getregistry(L);
+ lua_pushuserdata(L, key);
+ lua_gettable(L, -2);
+ if (lua_isfunction(L, -1)) {
+ lua_pushvalue(L, 1);
+ lua_rawcall(L, 1, 0);
+ }
+ else
+ lua_pop(L, 1); /* pop result from gettable */
+ lua_pop(L, 1); /* pop table */
+}
+
+
+static void callf (lua_State *L, lua_Debug *ar) {
+ lua_pushstring(L, ar->event);
+ hookf(L, KEY_CALLHOOK);
+}
+
+
+static void linef (lua_State *L, lua_Debug *ar) {
+ lua_pushnumber(L, ar->currentline);
+ hookf(L, KEY_LINEHOOK);
+}
+
+
+static void sethook (lua_State *L, void *key, lua_Hook hook,
+ lua_Hook (*sethookf)(lua_State * L, lua_Hook h)) {
+ lua_settop(L, 1);
+ if (lua_isnil(L, 1))
+ (*sethookf)(L, NULL);
+ else if (lua_isfunction(L, 1))
+ (*sethookf)(L, hook);
+ else
+ luaL_argerror(L, 1, "function expected");
+ lua_getregistry(L);
+ lua_pushuserdata(L, key);
+ lua_pushvalue(L, -1); /* dup key */
+ lua_gettable(L, -3); /* get old value */
+ lua_pushvalue(L, -2); /* key (again) */
+ lua_pushvalue(L, 1);
+ lua_settable(L, -5); /* set new value */
+}
+
+
+static int setcallhook (lua_State *L) {
+ sethook(L, KEY_CALLHOOK, callf, lua_setcallhook);
+ return 1;
+}
+
+
+static int setlinehook (lua_State *L) {
+ sethook(L, KEY_LINEHOOK, linef, lua_setlinehook);
+ return 1;
+}
+
+
+static const struct luaL_reg dblib[] = {
+ {"getlocal", getlocal},
+ {"getinfo", getinfo},
+ {"setcallhook", setcallhook},
+ {"setlinehook", setlinehook},
+ {"setlocal", setlocal}
+};
+
+
+LUALIB_API void lua_dblibopen (lua_State *L) {
+ luaL_openl(L, dblib);
+}
+
diff --git a/src/lua/ldebug.c b/src/lua/ldebug.c
new file mode 100644
index 00000000..02481b4c
--- /dev/null
+++ b/src/lua/ldebug.c
@@ -0,0 +1,466 @@
+/*
+** $Id: ldebug.c,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Debug Interface
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "luadebug.h"
+
+
+
+static const char *getfuncname (lua_State *L, StkId f, const char **name);
+
+
+static void setnormalized (TObject *d, const TObject *s) {
+ if (ttype(s) == LUA_TMARK) {
+ clvalue(d) = infovalue(s)->func;
+ ttype(d) = LUA_TFUNCTION;
+ }
+ else *d = *s;
+}
+
+
+static int isLmark (StkId o) {
+ return (o && ttype(o) == LUA_TMARK && !infovalue(o)->func->isC);
+}
+
+
+LUA_API lua_Hook lua_setcallhook (lua_State *L, lua_Hook func) {
+ lua_Hook oldhook = L->callhook;
+ L->callhook = func;
+ return oldhook;
+}
+
+
+LUA_API lua_Hook lua_setlinehook (lua_State *L, lua_Hook func) {
+ lua_Hook oldhook = L->linehook;
+ L->linehook = func;
+ return oldhook;
+}
+
+
+static StkId aux_stackedfunction (lua_State *L, int level, StkId top) {
+ int i;
+ for (i = (top-1) - L->stack; i>=0; i--) {
+ if (is_T_MARK(L->stack[i].ttype)) {
+ if (level == 0)
+ return L->stack+i;
+ level--;
+ }
+ }
+ return NULL;
+}
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
+ StkId f = aux_stackedfunction(L, level, L->top);
+ if (f == NULL) return 0; /* there is no such level */
+ else {
+ ar->_func = f;
+ return 1;
+ }
+}
+
+
+static int nups (StkId f) {
+ switch (ttype(f)) {
+ case LUA_TFUNCTION:
+ return clvalue(f)->nupvalues;
+ case LUA_TMARK:
+ return infovalue(f)->func->nupvalues;
+ default:
+ return 0;
+ }
+}
+
+
+int luaG_getline (int *lineinfo, int pc, int refline, int *prefi) {
+ int refi;
+ if (lineinfo == NULL || pc == -1)
+ return -1; /* no line info or function is not active */
+ refi = prefi ? *prefi : 0;
+ if (lineinfo[refi] < 0)
+ refline += -lineinfo[refi++];
+ LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info");
+ while (lineinfo[refi] > pc) {
+ refline--;
+ refi--;
+ if (lineinfo[refi] < 0)
+ refline -= -lineinfo[refi--];
+ LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info");
+ }
+ for (;;) {
+ int nextline = refline + 1;
+ int nextref = refi + 1;
+ if (lineinfo[nextref] < 0)
+ nextline += -lineinfo[nextref++];
+ LUA_ASSERT(lineinfo[nextref] >= 0, "invalid line info");
+ if (lineinfo[nextref] > pc)
+ break;
+ refline = nextline;
+ refi = nextref;
+ }
+ if (prefi) *prefi = refi;
+ return refline;
+}
+
+
+static int currentpc (StkId f) {
+ CallInfo *ci = infovalue(f);
+ LUA_ASSERT(isLmark(f), "function has no pc");
+ if (ci->pc)
+ return (*ci->pc - ci->func->f.l->code) - 1;
+ else
+ return -1; /* function is not active */
+}
+
+
+static int currentline (StkId f) {
+ if (!isLmark(f))
+ return -1; /* only active lua functions have current-line information */
+ else {
+ CallInfo *ci = infovalue(f);
+ int *lineinfo = ci->func->f.l->lineinfo;
+ return luaG_getline(lineinfo, currentpc(f), 1, NULL);
+ }
+}
+
+
+
+static Proto *getluaproto (StkId f) {
+ return (isLmark(f) ? infovalue(f)->func->f.l : NULL);
+}
+
+
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
+ const char *name;
+ StkId f = ar->_func;
+ Proto *fp = getluaproto(f);
+ if (!fp) return NULL; /* `f' is not a Lua function? */
+ name = luaF_getlocalname(fp, n, currentpc(f));
+ if (!name) return NULL;
+ luaA_pushobject(L, (f+1)+(n-1)); /* push value */
+ return name;
+}
+
+
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
+ const char *name;
+ StkId f = ar->_func;
+ Proto *fp = getluaproto(f);
+ L->top--; /* pop new value */
+ if (!fp) return NULL; /* `f' is not a Lua function? */
+ name = luaF_getlocalname(fp, n, currentpc(f));
+ if (!name || name[0] == '(') return NULL; /* `(' starts private locals */
+ *((f+1)+(n-1)) = *L->top;
+ return name;
+}
+
+
+static void infoLproto (lua_Debug *ar, Proto *f) {
+ ar->source = f->source->str;
+ ar->linedefined = f->lineDefined;
+ ar->what = "Lua";
+}
+
+
+static void funcinfo (lua_State *L, lua_Debug *ar, StkId func) {
+ Closure *cl = NULL;
+ switch (ttype(func)) {
+ case LUA_TFUNCTION:
+ cl = clvalue(func);
+ break;
+ case LUA_TMARK:
+ cl = infovalue(func)->func;
+ break;
+ default:
+ lua_error(L, "value for `lua_getinfo' is not a function");
+ }
+ if (cl->isC) {
+ ar->source = "=C";
+ ar->linedefined = -1;
+ ar->what = "C";
+ }
+ else
+ infoLproto(ar, cl->f.l);
+ luaO_chunkid(ar->short_src, ar->source, sizeof(ar->short_src));
+ if (ar->linedefined == 0)
+ ar->what = "main";
+}
+
+
+static const char *travtagmethods (lua_State *L, const TObject *o) {
+ if (ttype(o) == LUA_TFUNCTION) {
+ int e;
+ for (e=0; e<TM_N; e++) {
+ int t;
+ for (t=0; t<=L->last_tag; t++)
+ if (clvalue(o) == luaT_gettm(L, t, e))
+ return luaT_eventname[e];
+ }
+ }
+ return NULL;
+}
+
+
+static const char *travglobals (lua_State *L, const TObject *o) {
+ Hash *g = L->gt;
+ int i;
+ for (i=0; i<g->size; i++) {
+ if (luaO_equalObj(o, val(node(g, i))) &&
+ ttype(key(node(g, i))) == LUA_TSTRING)
+ return tsvalue(key(node(g, i)))->str;
+ }
+ return NULL;
+}
+
+
+static void getname (lua_State *L, StkId f, lua_Debug *ar) {
+ TObject o;
+ setnormalized(&o, f);
+ /* try to find a name for given function */
+ if ((ar->name = travglobals(L, &o)) != NULL)
+ ar->namewhat = "global";
+ /* not found: try tag methods */
+ else if ((ar->name = travtagmethods(L, &o)) != NULL)
+ ar->namewhat = "tag-method";
+ else ar->namewhat = ""; /* not found at all */
+}
+
+
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
+ StkId func;
+ int isactive = (*what != '>');
+ if (isactive)
+ func = ar->_func;
+ else {
+ what++; /* skip the '>' */
+ func = L->top - 1;
+ }
+ for (; *what; what++) {
+ switch (*what) {
+ case 'S': {
+ funcinfo(L, ar, func);
+ break;
+ }
+ case 'l': {
+ ar->currentline = currentline(func);
+ break;
+ }
+ case 'u': {
+ ar->nups = nups(func);
+ break;
+ }
+ case 'n': {
+ ar->namewhat = (isactive) ? getfuncname(L, func, &ar->name) : NULL;
+ if (ar->namewhat == NULL)
+ getname(L, func, ar);
+ break;
+ }
+ case 'f': {
+ setnormalized(L->top, func);
+ incr_top; /* push function */
+ break;
+ }
+ default: return 0; /* invalid option */
+ }
+ }
+ if (!isactive) L->top--; /* pop function */
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Symbolic Execution
+** =======================================================
+*/
+
+
+static int pushpc (int *stack, int pc, int top, int n) {
+ while (n--)
+ stack[top++] = pc-1;
+ return top;
+}
+
+
+static Instruction luaG_symbexec (const Proto *pt, int lastpc, int stackpos) {
+ int stack[MAXSTACK]; /* stores last instruction that changed a stack entry */
+ const Instruction *code = pt->code;
+ int top = pt->numparams;
+ int pc = 0;
+ if (pt->is_vararg) /* varargs? */
+ top++; /* `arg' */
+ while (pc < lastpc) {
+ const Instruction i = code[pc++];
+ LUA_ASSERT(0 <= top && top <= pt->maxstacksize, "wrong stack");
+ switch (GET_OPCODE(i)) {
+ case OP_RETURN: {
+ LUA_ASSERT(top >= GETARG_U(i), "wrong stack");
+ top = GETARG_U(i);
+ break;
+ }
+ case OP_TAILCALL: {
+ LUA_ASSERT(top >= GETARG_A(i), "wrong stack");
+ top = GETARG_B(i);
+ break;
+ }
+ case OP_CALL: {
+ int nresults = GETARG_B(i);
+ if (nresults == MULT_RET) nresults = 1;
+ LUA_ASSERT(top >= GETARG_A(i), "wrong stack");
+ top = pushpc(stack, pc, GETARG_A(i), nresults);
+ break;
+ }
+ case OP_PUSHNIL: {
+ top = pushpc(stack, pc, top, GETARG_U(i));
+ break;
+ }
+ case OP_POP: {
+ top -= GETARG_U(i);
+ break;
+ }
+ case OP_SETTABLE:
+ case OP_SETLIST: {
+ top -= GETARG_B(i);
+ break;
+ }
+ case OP_SETMAP: {
+ top -= 2*GETARG_U(i);
+ break;
+ }
+ case OP_CONCAT: {
+ top -= GETARG_U(i);
+ stack[top++] = pc-1;
+ break;
+ }
+ case OP_CLOSURE: {
+ top -= GETARG_B(i);
+ stack[top++] = pc-1;
+ break;
+ }
+ case OP_JMPONT:
+ case OP_JMPONF: {
+ int newpc = pc + GETARG_S(i);
+ /* jump is forward and do not skip `lastpc'? */
+ if (pc < newpc && newpc <= lastpc) {
+ stack[top-1] = pc-1; /* value comes from `and'/`or' */
+ pc = newpc; /* do the jump */
+ }
+ else
+ top--; /* do not jump; pop value */
+ break;
+ }
+ default: {
+ OpCode op = GET_OPCODE(i);
+ LUA_ASSERT(luaK_opproperties[op].push != VD,
+ "invalid opcode for default");
+ top -= luaK_opproperties[op].pop;
+ LUA_ASSERT(top >= 0, "wrong stack");
+ top = pushpc(stack, pc, top, luaK_opproperties[op].push);
+ }
+ }
+ }
+ return code[stack[stackpos]];
+}
+
+
+static const char *getobjname (lua_State *L, StkId obj, const char **name) {
+ StkId func = aux_stackedfunction(L, 0, obj);
+ if (!isLmark(func))
+ return NULL; /* not an active Lua function */
+ else {
+ Proto *p = infovalue(func)->func->f.l;
+ int pc = currentpc(func);
+ int stackpos = obj - (func+1); /* func+1 == function base */
+ Instruction i = luaG_symbexec(p, pc, stackpos);
+ LUA_ASSERT(pc != -1, "function must be active");
+ switch (GET_OPCODE(i)) {
+ case OP_GETGLOBAL: {
+ *name = p->kstr[GETARG_U(i)]->str;
+ return "global";
+ }
+ case OP_GETLOCAL: {
+ *name = luaF_getlocalname(p, GETARG_U(i)+1, pc);
+ LUA_ASSERT(*name, "local must exist");
+ return "local";
+ }
+ case OP_PUSHSELF:
+ case OP_GETDOTTED: {
+ *name = p->kstr[GETARG_U(i)]->str;
+ return "field";
+ }
+ default:
+ return NULL; /* no useful name found */
+ }
+ }
+}
+
+
+static const char *getfuncname (lua_State *L, StkId f, const char **name) {
+ StkId func = aux_stackedfunction(L, 0, f); /* calling function */
+ if (!isLmark(func))
+ return NULL; /* not an active Lua function */
+ else {
+ Proto *p = infovalue(func)->func->f.l;
+ int pc = currentpc(func);
+ Instruction i;
+ if (pc == -1) return NULL; /* function is not activated */
+ i = p->code[pc];
+ switch (GET_OPCODE(i)) {
+ case OP_CALL: case OP_TAILCALL:
+ return getobjname(L, (func+1)+GETARG_A(i), name);
+ default:
+ return NULL; /* no useful name found */
+ }
+ }
+}
+
+
+/* }====================================================== */
+
+
+void luaG_typeerror (lua_State *L, StkId o, const char *op) {
+ const char *name;
+ const char *kind = getobjname(L, o, &name);
+ const char *t = luaO_typename(o);
+ if (kind)
+ luaO_verror(L, "attempt to %.30s %.20s `%.40s' (a %.10s value)",
+ op, kind, name, t);
+ else
+ luaO_verror(L, "attempt to %.30s a %.10s value", op, t);
+}
+
+
+void luaG_binerror (lua_State *L, StkId p1, int t, const char *op) {
+ if (ttype(p1) == t) p1++;
+ LUA_ASSERT(ttype(p1) != t, "must be an error");
+ luaG_typeerror(L, p1, op);
+}
+
+
+void luaG_ordererror (lua_State *L, StkId top) {
+ const char *t1 = luaO_typename(top-2);
+ const char *t2 = luaO_typename(top-1);
+ if (t1[2] == t2[2])
+ luaO_verror(L, "attempt to compare two %.10s values", t1);
+ else
+ luaO_verror(L, "attempt to compare %.10s with %.10s", t1, t2);
+}
+
diff --git a/src/lua/ldebug.h b/src/lua/ldebug.h
new file mode 100644
index 00000000..76865616
--- /dev/null
+++ b/src/lua/ldebug.h
@@ -0,0 +1,21 @@
+/*
+** $Id: ldebug.h,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions from Debug Interface module
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldebug_h
+#define ldebug_h
+
+
+#include "lstate.h"
+#include "luadebug.h"
+
+
+void luaG_typeerror (lua_State *L, StkId o, const char *op);
+void luaG_binerror (lua_State *L, StkId p1, int t, const char *op);
+int luaG_getline (int *lineinfo, int pc, int refline, int *refi);
+void luaG_ordererror (lua_State *L, StkId top);
+
+
+#endif
diff --git a/src/lua/ldo.c b/src/lua/ldo.c
new file mode 100644
index 00000000..5f23bfd9
--- /dev/null
+++ b/src/lua/ldo.c
@@ -0,0 +1,385 @@
+/*
+** $Id: ldo.c,v 1.7 2004/06/04 13:42:10 neil Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+#include "lzio.h"
+
+
+/* space to handle stack overflow errors */
+#define EXTRA_STACK (2*LUA_MINSTACK)
+
+
+void luaD_init (lua_State *L, int stacksize) {
+ L->stack = luaM_newvector(L, stacksize+EXTRA_STACK, TObject);
+ L->nblocks += stacksize*sizeof(TObject);
+ L->stack_last = L->stack+(stacksize-1);
+ L->stacksize = stacksize;
+ L->Cbase = L->top = L->stack;
+}
+
+
+void luaD_checkstack (lua_State *L, int n) {
+ if (L->stack_last - L->top <= n) { /* stack overflow? */
+ if (L->stack_last-L->stack > (L->stacksize-1)) {
+ /* overflow while handling overflow */
+ luaD_breakrun(L, LUA_ERRERR); /* break run without error message */
+ }
+ else {
+ L->stack_last += EXTRA_STACK; /* to be used by error message */
+ lua_error(L, "stack overflow");
+ }
+ }
+}
+
+
+static void restore_stack_limit (lua_State *L) {
+ if (L->top - L->stack < L->stacksize - 1)
+ L->stack_last = L->stack + (L->stacksize-1);
+}
+
+
+/*
+** Adjust stack. Set top to base+extra, pushing NILs if needed.
+** (we cannot add base+extra unless we are sure it fits in the stack;
+** otherwise the result of such operation on pointers is undefined)
+*/
+void luaD_adjusttop (lua_State *L, StkId base, int extra) {
+ int diff = extra-(L->top-base);
+ if (diff <= 0)
+ L->top = base+extra;
+ else {
+ luaD_checkstack(L, diff);
+ while (diff--)
+ ttype(L->top++) = LUA_TNIL;
+ }
+}
+
+
+/*
+** Open a hole inside the stack at `pos'
+*/
+static void luaD_openstack (lua_State *L, StkId pos) {
+ int i = L->top-pos;
+ while (i--) pos[i+1] = pos[i];
+ incr_top;
+}
+
+
+static void dohook (lua_State *L, lua_Debug *ar, lua_Hook hook) {
+ StkId old_Cbase = L->Cbase;
+ StkId old_top = L->Cbase = L->top;
+ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
+ L->allowhooks = 0; /* cannot call hooks inside a hook */
+ (*hook)(L, ar);
+ LUA_ASSERT(L->allowhooks == 0, "invalid allow");
+ L->allowhooks = 1;
+ L->top = old_top;
+ L->Cbase = old_Cbase;
+}
+
+
+void luaD_lineHook (lua_State *L, StkId func, int line, lua_Hook linehook) {
+ if (L->allowhooks) {
+ lua_Debug ar;
+ ar._func = func;
+ ar.event = "line";
+ ar.currentline = line;
+ dohook(L, &ar, linehook);
+ }
+}
+
+
+static void luaD_callHook (lua_State *L, StkId func, lua_Hook callhook,
+ const char *event) {
+ if (L->allowhooks) {
+ lua_Debug ar;
+ ar._func = func;
+ ar.event = event;
+ infovalue(func)->pc = NULL; /* function is not active */
+ dohook(L, &ar, callhook);
+ }
+}
+
+
+static StkId callCclosure (lua_State *L, const struct Closure *cl, StkId base) {
+ int nup = cl->nupvalues; /* number of upvalues */
+ StkId old_Cbase = L->Cbase;
+ int n;
+ L->Cbase = base; /* new base for C function */
+ luaD_checkstack(L, nup+LUA_MINSTACK); /* ensure minimum stack size */
+ for (n=0; n<nup; n++) /* copy upvalues as extra arguments */
+ *(L->top++) = cl->upvalue[n];
+ n = (*cl->f.c)(L); /* do the actual call */
+ L->Cbase = old_Cbase; /* restore old C base */
+ return L->top - n; /* return index of first result */
+}
+
+
+void luaD_callTM (lua_State *L, Closure *f, int nParams, int nResults) {
+ StkId base = L->top - nParams;
+ luaD_openstack(L, base);
+ clvalue(base) = f;
+ ttype(base) = LUA_TFUNCTION;
+ luaD_call(L, base, nResults);
+}
+
+
+/*
+** Call a function (C or Lua). The function to be called is at *func.
+** The arguments are on the stack, right after the function.
+** When returns, the results are on the stack, starting at the original
+** function position.
+** The number of results is nResults, unless nResults=LUA_MULTRET.
+*/
+void luaD_call (lua_State *L, StkId func, int nResults) {
+ lua_Hook callhook;
+ StkId firstResult;
+ CallInfo ci;
+ Closure *cl;
+ if (ttype(func) != LUA_TFUNCTION) {
+ /* `func' is not a function; check the `function' tag method */
+ Closure *tm = luaT_gettmbyObj(L, func, TM_FUNCTION);
+ if (tm == NULL)
+ luaG_typeerror(L, func, "call");
+ luaD_openstack(L, func);
+ clvalue(func) = tm; /* tag method is the new function to be called */
+ ttype(func) = LUA_TFUNCTION;
+ }
+ cl = clvalue(func);
+ ci.func = cl;
+ infovalue(func) = &ci;
+ ttype(func) = LUA_TMARK;
+ callhook = L->callhook;
+ if (callhook)
+ luaD_callHook(L, func, callhook, "call");
+ firstResult = (cl->isC ? callCclosure(L, cl, func+1) :
+ luaV_execute(L, cl, func+1));
+ if (callhook) /* same hook that was active at entry */
+ luaD_callHook(L, func, callhook, "return");
+ LUA_ASSERT(ttype(func) == LUA_TMARK, "invalid tag");
+ /* move results to `func' (to erase parameters and function) */
+ if (nResults == LUA_MULTRET) {
+ while (firstResult < L->top) /* copy all results */
+ *func++ = *firstResult++;
+ L->top = func;
+ }
+ else { /* copy at most `nResults' */
+ for (; nResults > 0 && firstResult < L->top; nResults--)
+ *func++ = *firstResult++;
+ L->top = func;
+ for (; nResults > 0; nResults--) { /* if there are not enough results */
+ ttype(L->top) = LUA_TNIL; /* adjust the stack */
+ incr_top; /* must check stack space */
+ }
+ }
+ luaC_checkGC(L);
+}
+
+
+/*
+** Execute a protected call.
+*/
+struct CallS { /* data to `f_call' */
+ StkId func;
+ int nresults;
+};
+
+static void f_call (lua_State *L, void *ud) {
+ struct CallS *c = (struct CallS *)ud;
+ luaD_call(L, c->func, c->nresults);
+}
+
+
+LUA_API int lua_call (lua_State *L, int nargs, int nresults) {
+ StkId func = L->top - (nargs+1); /* function to be called */
+ struct CallS c;
+ int status;
+ c.func = func; c.nresults = nresults;
+ status = luaD_runprotected(L, f_call, &c);
+ if (status != 0) /* an error occurred? */
+ L->top = func; /* remove parameters from the stack */
+ return status;
+}
+
+
+/*
+** Execute a protected parser.
+*/
+struct ParserS { /* data to `f_parser' */
+ ZIO *z;
+ int bin;
+};
+
+static void f_parser (lua_State *L, void *ud) {
+ struct ParserS *p = (struct ParserS *)ud;
+ Proto *tf = p->bin ? luaU_undump(L, p->z) : luaY_parser(L, p->z);
+ luaV_Lclosure(L, tf, 0);
+}
+
+
+static int protectedparser (lua_State *L, ZIO *z, int bin) {
+ struct ParserS p;
+ unsigned long old_blocks;
+ int status;
+ p.z = z; p.bin = bin;
+ luaC_checkGC(L);
+ old_blocks = L->nblocks;
+ status = luaD_runprotected(L, f_parser, &p);
+ if (status == 0) {
+ /* add new memory to threshold (as it probably will stay) */
+ L->GCthreshold += (L->nblocks - old_blocks);
+ }
+ else if (status == LUA_ERRRUN) /* an error occurred: correct error code */
+ status = LUA_ERRSYNTAX;
+ return status;
+}
+
+
+static int parse_file (lua_State *L, const char *filename) {
+ ZIO z;
+ int status;
+ int bin; /* flag for file mode */
+ int c; /* look ahead char */
+ FILE *f = (filename == NULL) ? stdin : fopen(filename, "r");
+ if (f == NULL) return LUA_ERRFILE; /* unable to open file */
+ c = fgetc(f);
+ ungetc(c, f);
+ bin = (c == ID_CHUNK);
+ if (bin && f != stdin) {
+ f = freopen(filename, "rb", f); /* set binary mode */
+ if (f == NULL) return LUA_ERRFILE; /* unable to reopen file */
+ }
+ lua_pushstring(L, "@");
+ lua_pushstring(L, (filename == NULL) ? "(stdin)" : filename);
+ lua_concat(L, 2);
+ filename = lua_tostring(L, -1); /* filename = '@'..filename */
+ lua_pop(L, 1); /* OK: there is no GC during parser */
+ luaZ_Fopen(&z, f, filename);
+ status = protectedparser(L, &z, bin);
+ if (f != stdin)
+ fclose(f);
+ return status;
+}
+
+
+LUA_API int lua_dofile (lua_State *L, const char *filename) {
+ int status = parse_file(L, filename);
+ if (status == 0) /* parse OK? */
+ status = lua_call(L, 0, LUA_MULTRET); /* call main */
+ return status;
+}
+
+
+static int parse_buffer (lua_State *L, const char *buff, size_t size,
+ const char *name) {
+ ZIO z;
+ if (!name) name = "?";
+ luaZ_mopen(&z, buff, size, name);
+ return protectedparser(L, &z, buff[0]==ID_CHUNK);
+}
+
+
+LUA_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, const char *name) {
+ int status = parse_buffer(L, buff, size, name);
+ if (status == 0) /* parse OK? */
+ status = lua_call(L, 0, LUA_MULTRET); /* call main */
+ return status;
+}
+
+
+LUA_API int lua_dostring (lua_State *L, const char *str) {
+ return lua_dobuffer(L, str, strlen(str), str);
+}
+
+
+/*
+** {======================================================
+** Error-recover functions (based on long jumps)
+** =======================================================
+*/
+
+/* chain list of long jump buffers */
+struct lua_longjmp {
+ jmp_buf b;
+ struct lua_longjmp *previous;
+ volatile int status; /* error code */
+};
+
+
+static void message (lua_State *L, const char *s) {
+ const TObject *em = luaH_getglobal(L, LUA_ERRORMESSAGE);
+ if (ttype(em) == LUA_TFUNCTION) {
+ *L->top = *em;
+ incr_top;
+ lua_pushstring(L, s);
+ luaD_call(L, L->top-2, 0);
+ }
+}
+
+
+/*
+** Reports an error, and jumps up to the available recovery label
+*/
+LUA_API void lua_error (lua_State *L, const char *s) {
+ if (s) message(L, s);
+ luaD_breakrun(L, LUA_ERRRUN);
+}
+
+
+void luaD_breakrun (lua_State *L, int errcode) {
+ if (L->errorJmp) {
+ L->errorJmp->status = errcode;
+ longjmp(L->errorJmp->b, 1);
+ }
+ else {
+ if (errcode != LUA_ERRMEM)
+ message(L, "unable to recover; exiting\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+int luaD_runprotected (lua_State *L, void (*f)(lua_State *, void *), void *ud) {
+ StkId oldCbase = L->Cbase;
+ StkId oldtop = L->top;
+ struct lua_longjmp lj;
+ int allowhooks = L->allowhooks;
+ lj.status = 0;
+ lj.previous = L->errorJmp; /* chain new error handler */
+ L->errorJmp = &lj;
+ if (setjmp(lj.b) == 0)
+ (*f)(L, ud);
+ else { /* an error occurred: restore the state */
+ L->allowhooks = allowhooks;
+ L->Cbase = oldCbase;
+ L->top = oldtop;
+ restore_stack_limit(L);
+ }
+ L->errorJmp = lj.previous; /* restore old error handler */
+ return lj.status;
+}
+
+/* }====================================================== */
+
diff --git a/src/lua/ldo.h b/src/lua/ldo.h
new file mode 100644
index 00000000..d948ad35
--- /dev/null
+++ b/src/lua/ldo.h
@@ -0,0 +1,33 @@
+/*
+** $Id: ldo.h,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldo_h
+#define ldo_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+
+/*
+** macro to increment stack top.
+** There must be always an empty slot at the L->stack.top
+*/
+#define incr_top {if (L->top == L->stack_last) luaD_checkstack(L, 1); L->top++;}
+
+
+void luaD_init (lua_State *L, int stacksize);
+void luaD_adjusttop (lua_State *L, StkId base, int extra);
+void luaD_lineHook (lua_State *L, StkId func, int line, lua_Hook linehook);
+void luaD_call (lua_State *L, StkId func, int nResults);
+void luaD_callTM (lua_State *L, Closure *f, int nParams, int nResults);
+void luaD_checkstack (lua_State *L, int n);
+
+void luaD_breakrun (lua_State *L, int errcode);
+int luaD_runprotected (lua_State *L, void (*f)(lua_State *, void *), void *ud);
+
+
+#endif
diff --git a/src/lua/lfunc.c b/src/lua/lfunc.c
new file mode 100644
index 00000000..d3427653
--- /dev/null
+++ b/src/lua/lfunc.c
@@ -0,0 +1,109 @@
+/*
+** $Id: lfunc.c,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "lfunc.h"
+#include "lmem.h"
+#include "lstate.h"
+
+
+#define sizeclosure(n) ((int)sizeof(Closure) + (int)sizeof(TObject)*((n)-1))
+
+
+Closure *luaF_newclosure (lua_State *L, int nelems) {
+ int size = sizeclosure(nelems);
+ Closure *c = (Closure *)luaM_malloc(L, size);
+ c->next = L->rootcl;
+ L->rootcl = c;
+ c->mark = c;
+ c->nupvalues = nelems;
+ L->nblocks += size;
+ return c;
+}
+
+
+Proto *luaF_newproto (lua_State *L) {
+ Proto *f = luaM_new(L, Proto);
+ f->knum = NULL;
+ f->nknum = 0;
+ f->kstr = NULL;
+ f->nkstr = 0;
+ f->kproto = NULL;
+ f->nkproto = 0;
+ f->code = NULL;
+ f->ncode = 0;
+ f->numparams = 0;
+ f->is_vararg = 0;
+ f->maxstacksize = 0;
+ f->marked = 0;
+ f->lineinfo = NULL;
+ f->nlineinfo = 0;
+ f->nlocvars = 0;
+ f->locvars = NULL;
+ f->lineDefined = 0;
+ f->source = NULL;
+ f->next = L->rootproto; /* chain in list of protos */
+ L->rootproto = f;
+ return f;
+}
+
+
+static size_t protosize (Proto *f) {
+ return sizeof(Proto)
+ + f->nknum*sizeof(Number)
+ + f->nkstr*sizeof(TString *)
+ + f->nkproto*sizeof(Proto *)
+ + f->ncode*sizeof(Instruction)
+ + f->nlocvars*sizeof(struct LocVar)
+ + f->nlineinfo*sizeof(int);
+}
+
+
+void luaF_protook (lua_State *L, Proto *f, int pc) {
+ f->ncode = pc; /* signal that proto was properly created */
+ L->nblocks += protosize(f);
+}
+
+
+void luaF_freeproto (lua_State *L, Proto *f) {
+ if (f->ncode > 0) /* function was properly created? */
+ L->nblocks -= protosize(f);
+ luaM_free(L, f->code);
+ luaM_free(L, f->locvars);
+ luaM_free(L, f->kstr);
+ luaM_free(L, f->knum);
+ luaM_free(L, f->kproto);
+ luaM_free(L, f->lineinfo);
+ luaM_free(L, f);
+}
+
+
+void luaF_freeclosure (lua_State *L, Closure *c) {
+ L->nblocks -= sizeclosure(c->nupvalues);
+ luaM_free(L, c);
+}
+
+
+/*
+** Look for n-th local variable at line `line' in function `func'.
+** Returns NULL if not found.
+*/
+const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
+ int i;
+ for (i = 0; i<f->nlocvars && f->locvars[i].startpc <= pc; i++) {
+ if (pc < f->locvars[i].endpc) { /* is variable active? */
+ local_number--;
+ if (local_number == 0)
+ return f->locvars[i].varname->str;
+ }
+ }
+ return NULL; /* not found */
+}
+
diff --git a/src/lua/lfunc.h b/src/lua/lfunc.h
new file mode 100644
index 00000000..1bd9722d
--- /dev/null
+++ b/src/lua/lfunc.h
@@ -0,0 +1,24 @@
+/*
+** $Id: lfunc.h,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lfunc_h
+#define lfunc_h
+
+
+#include "lobject.h"
+
+
+
+Proto *luaF_newproto (lua_State *L);
+void luaF_protook (lua_State *L, Proto *f, int pc);
+Closure *luaF_newclosure (lua_State *L, int nelems);
+void luaF_freeproto (lua_State *L, Proto *f);
+void luaF_freeclosure (lua_State *L, Closure *c);
+
+const char *luaF_getlocalname (const Proto *func, int local_number, int pc);
+
+
+#endif
diff --git a/src/lua/lgc.c b/src/lua/lgc.c
new file mode 100644
index 00000000..4e8b234d
--- /dev/null
+++ b/src/lua/lgc.c
@@ -0,0 +1,353 @@
+/*
+** $Id: lgc.c,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+typedef struct GCState {
+ Hash *tmark; /* list of marked tables to be visited */
+ Closure *cmark; /* list of marked closures to be visited */
+} GCState;
+
+
+
+static void markobject (GCState *st, TObject *o);
+
+
+/* mark a string; marks larger than 1 cannot be changed */
+#define strmark(s) {if ((s)->marked == 0) (s)->marked = 1;}
+
+
+
+static void protomark (Proto *f) {
+ if (!f->marked) {
+ int i;
+ f->marked = 1;
+ strmark(f->source);
+ for (i=0; i<f->nkstr; i++)
+ strmark(f->kstr[i]);
+ for (i=0; i<f->nkproto; i++)
+ protomark(f->kproto[i]);
+ for (i=0; i<f->nlocvars; i++) /* mark local-variable names */
+ strmark(f->locvars[i].varname);
+ }
+}
+
+
+static void markstack (lua_State *L, GCState *st) {
+ StkId o;
+ for (o=L->stack; o<L->top; o++)
+ markobject(st, o);
+}
+
+
+static void marklock (lua_State *L, GCState *st) {
+ int i;
+ for (i=0; i<L->refSize; i++) {
+ if (L->refArray[i].st == LOCK)
+ markobject(st, &L->refArray[i].o);
+ }
+}
+
+
+static void markclosure (GCState *st, Closure *cl) {
+ if (!ismarked(cl)) {
+ if (!cl->isC)
+ protomark(cl->f.l);
+ cl->mark = st->cmark; /* chain it for later traversal */
+ st->cmark = cl;
+ }
+}
+
+
+static void marktagmethods (lua_State *L, GCState *st) {
+ int e;
+ for (e=0; e<TM_N; e++) {
+ int t;
+ for (t=0; t<=L->last_tag; t++) {
+ Closure *cl = luaT_gettm(L, t, e);
+ if (cl) markclosure(st, cl);
+ }
+ }
+}
+
+
+static void markobject (GCState *st, TObject *o) {
+ switch (ttype(o)) {
+ case LUA_TUSERDATA: case LUA_TSTRING:
+ strmark(tsvalue(o));
+ break;
+ case LUA_TMARK:
+ markclosure(st, infovalue(o)->func);
+ break;
+ case LUA_TFUNCTION:
+ markclosure(st, clvalue(o));
+ break;
+ case LUA_TTABLE: {
+ if (!ismarked(hvalue(o))) {
+ hvalue(o)->mark = st->tmark; /* chain it in list of marked */
+ st->tmark = hvalue(o);
+ }
+ break;
+ }
+ default: break; /* numbers, etc */
+ }
+}
+
+
+static void markall (lua_State *L) {
+ GCState st;
+ st.cmark = NULL;
+ st.tmark = L->gt; /* put table of globals in mark list */
+ L->gt->mark = NULL;
+ marktagmethods(L, &st); /* mark tag methods */
+ markstack(L, &st); /* mark stack objects */
+ marklock(L, &st); /* mark locked objects */
+ for (;;) { /* mark tables and closures */
+ if (st.cmark) {
+ int i;
+ Closure *f = st.cmark; /* get first closure from list */
+ st.cmark = f->mark; /* remove it from list */
+ for (i=0; i<f->nupvalues; i++) /* mark its upvalues */
+ markobject(&st, &f->upvalue[i]);
+ }
+ else if (st.tmark) {
+ int i;
+ Hash *h = st.tmark; /* get first table from list */
+ st.tmark = h->mark; /* remove it from list */
+ for (i=0; i<h->size; i++) {
+ Node *n = node(h, i);
+ if (ttype(key(n)) != LUA_TNIL) {
+ if (ttype(val(n)) == LUA_TNIL)
+ luaH_remove(h, key(n)); /* dead element; try to remove it */
+ markobject(&st, &n->key);
+ markobject(&st, &n->val);
+ }
+ }
+ }
+ else break; /* nothing else to mark */
+ }
+}
+
+
+static int hasmark (const TObject *o) {
+ /* valid only for locked objects */
+ switch (o->ttype) {
+ case LUA_TSTRING: case LUA_TUSERDATA:
+ return tsvalue(o)->marked;
+ case LUA_TTABLE:
+ return ismarked(hvalue(o));
+ case LUA_TFUNCTION:
+ return ismarked(clvalue(o));
+ default: /* number */
+ return 1;
+ }
+}
+
+
+/* macro for internal debugging; check if a link of free refs is valid */
+#define VALIDLINK(L, st,n) (NONEXT <= (st) && (st) < (n))
+
+static void invalidaterefs (lua_State *L) {
+ int n = L->refSize;
+ int i;
+ for (i=0; i<n; i++) {
+ struct Ref *r = &L->refArray[i];
+ if (r->st == HOLD && !hasmark(&r->o))
+ r->st = COLLECTED;
+ LUA_ASSERT((r->st == LOCK && hasmark(&r->o)) ||
+ (r->st == HOLD && hasmark(&r->o)) ||
+ r->st == COLLECTED ||
+ r->st == NONEXT ||
+ (r->st < n && VALIDLINK(L, L->refArray[r->st].st, n)),
+ "inconsistent ref table");
+ }
+ LUA_ASSERT(VALIDLINK(L, L->refFree, n), "inconsistent ref table");
+}
+
+
+
+static void collectproto (lua_State *L) {
+ Proto **p = &L->rootproto;
+ Proto *next;
+ while ((next = *p) != NULL) {
+ if (next->marked) {
+ next->marked = 0;
+ p = &next->next;
+ }
+ else {
+ *p = next->next;
+ luaF_freeproto(L, next);
+ }
+ }
+}
+
+
+static void collectclosure (lua_State *L) {
+ Closure **p = &L->rootcl;
+ Closure *next;
+ while ((next = *p) != NULL) {
+ if (ismarked(next)) {
+ next->mark = next; /* unmark */
+ p = &next->next;
+ }
+ else {
+ *p = next->next;
+ luaF_freeclosure(L, next);
+ }
+ }
+}
+
+
+static void collecttable (lua_State *L) {
+ Hash **p = &L->roottable;
+ Hash *next;
+ while ((next = *p) != NULL) {
+ if (ismarked(next)) {
+ next->mark = next; /* unmark */
+ p = &next->next;
+ }
+ else {
+ *p = next->next;
+ luaH_free(L, next);
+ }
+ }
+}
+
+
+static void checktab (lua_State *L, stringtable *tb) {
+ if (tb->nuse < (lint32)(tb->size/4) && tb->size > 10)
+ luaS_resize(L, tb, tb->size/2); /* table is too big */
+}
+
+
+static void collectstrings (lua_State *L, int all) {
+ int i;
+ for (i=0; i<L->strt.size; i++) { /* for each list */
+ TString **p = &L->strt.hash[i];
+ TString *next;
+ while ((next = *p) != NULL) {
+ if (next->marked && !all) { /* preserve? */
+ if (next->marked < FIXMARK) /* does not change FIXMARKs */
+ next->marked = 0;
+ p = &next->nexthash;
+ }
+ else { /* collect */
+ *p = next->nexthash;
+ L->strt.nuse--;
+ L->nblocks -= sizestring(next->len);
+ luaM_free(L, next);
+ }
+ }
+ }
+ checktab(L, &L->strt);
+}
+
+
+static void collectudata (lua_State *L, int all) {
+ int i;
+ for (i=0; i<L->udt.size; i++) { /* for each list */
+ TString **p = &L->udt.hash[i];
+ TString *next;
+ while ((next = *p) != NULL) {
+ LUA_ASSERT(next->marked <= 1, "udata cannot be fixed");
+ if (next->marked && !all) { /* preserve? */
+ next->marked = 0;
+ p = &next->nexthash;
+ }
+ else { /* collect */
+ int tag = next->u.d.tag;
+ *p = next->nexthash;
+ next->nexthash = L->TMtable[tag].collected; /* chain udata */
+ L->TMtable[tag].collected = next;
+ L->nblocks -= sizestring(next->len);
+ L->udt.nuse--;
+ }
+ }
+ }
+ checktab(L, &L->udt);
+}
+
+
+#define MINBUFFER 256
+static void checkMbuffer (lua_State *L) {
+ if (L->Mbuffsize > MINBUFFER*2) { /* is buffer too big? */
+ size_t newsize = L->Mbuffsize/2; /* still larger than MINBUFFER */
+ L->nblocks += (newsize - L->Mbuffsize)*sizeof(char);
+ L->Mbuffsize = newsize;
+ luaM_reallocvector(L, L->Mbuffer, newsize, char);
+ }
+}
+
+
+static void callgcTM (lua_State *L, const TObject *o) {
+ Closure *tm = luaT_gettmbyObj(L, o, TM_GC);
+ if (tm != NULL) {
+ int oldah = L->allowhooks;
+ L->allowhooks = 0; /* stop debug hooks during GC tag methods */
+ luaD_checkstack(L, 2);
+ clvalue(L->top) = tm;
+ ttype(L->top) = LUA_TFUNCTION;
+ *(L->top+1) = *o;
+ L->top += 2;
+ luaD_call(L, L->top-2, 0);
+ L->allowhooks = oldah; /* restore hooks */
+ }
+}
+
+
+static void callgcTMudata (lua_State *L) {
+ int tag;
+ TObject o;
+ ttype(&o) = LUA_TUSERDATA;
+ L->GCthreshold = 2*L->nblocks; /* avoid GC during tag methods */
+ for (tag=L->last_tag; tag>=0; tag--) { /* for each tag (in reverse order) */
+ TString *udata;
+ while ((udata = L->TMtable[tag].collected) != NULL) {
+ L->TMtable[tag].collected = udata->nexthash; /* remove it from list */
+ tsvalue(&o) = udata;
+ callgcTM(L, &o);
+ luaM_free(L, udata);
+ }
+ }
+}
+
+
+void luaC_collect (lua_State *L, int all) {
+ collectudata(L, all);
+ callgcTMudata(L);
+ collectstrings(L, all);
+ collecttable(L);
+ collectproto(L);
+ collectclosure(L);
+}
+
+
+static void luaC_collectgarbage (lua_State *L) {
+ markall(L);
+ invalidaterefs(L); /* check unlocked references */
+ luaC_collect(L, 0);
+ checkMbuffer(L);
+ L->GCthreshold = 2*L->nblocks; /* set new threshold */
+ callgcTM(L, &luaO_nilobject);
+}
+
+
+void luaC_checkGC (lua_State *L) {
+ if (L->nblocks >= L->GCthreshold)
+ luaC_collectgarbage(L);
+}
+
diff --git a/src/lua/lgc.h b/src/lua/lgc.h
new file mode 100644
index 00000000..2dea9e4d
--- /dev/null
+++ b/src/lua/lgc.h
@@ -0,0 +1,18 @@
+/*
+** $Id: lgc.h,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lgc_h
+#define lgc_h
+
+
+#include "lobject.h"
+
+
+void luaC_collect (lua_State *L, int all);
+void luaC_checkGC (lua_State *L);
+
+
+#endif
diff --git a/src/lua/liolib.c b/src/lua/liolib.c
new file mode 100644
index 00000000..4fb385f4
--- /dev/null
+++ b/src/lua/liolib.c
@@ -0,0 +1,710 @@
+/*
+** $Id: liolib.c,v 1.5 2004/06/04 13:42:10 neil Exp $
+** Standard I/O (and system) library
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "luadebug.h"
+#include "lualib.h"
+
+
+#ifndef OLD_ANSI
+#include <errno.h>
+#include <locale.h>
+#define realloc(b,s) ((b) == NULL ? malloc(s) : (realloc)(b, s))
+#define free(b) if (b) (free)(b)
+#else
+/* no support for locale and for strerror: fake them */
+#define setlocale(a,b) ((void)a, strcmp((b),"C")==0?"C":NULL)
+#define LC_ALL 0
+#define LC_COLLATE 0
+#define LC_CTYPE 0
+#define LC_MONETARY 0
+#define LC_NUMERIC 0
+#define LC_TIME 0
+#define strerror(e) "generic I/O error"
+#define errno (-1)
+#endif
+
+
+
+#ifdef POPEN
+/* FILE *popen();
+int pclose(); */
+#define CLOSEFILE(L, f) ((pclose(f) == -1) ? fclose(f) : 0)
+#else
+/* no support for popen */
+#define popen(x,y) NULL /* that is, popen always fails */
+#define CLOSEFILE(L, f) (fclose(f))
+#endif
+
+
+#define INFILE 0
+#define OUTFILE 1
+
+typedef struct IOCtrl {
+ int ref[2]; /* ref for strings _INPUT/_OUTPUT */
+ int iotag; /* tag for file handles */
+ int closedtag; /* tag for closed handles */
+} IOCtrl;
+
+
+
+static const char *const filenames[] = {"_INPUT", "_OUTPUT"};
+
+
+static int pushresult (lua_State *L, int i) {
+ if (i) {
+ lua_pushuserdata(L, NULL);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushnumber(L, errno);
+ return 3;;
+ }
+}
+
+
+/*
+** {======================================================
+** FILE Operations
+** =======================================================
+*/
+
+
+static FILE *gethandle (lua_State *L, IOCtrl *ctrl, int f) {
+ void *p = lua_touserdata(L, f);
+ if (p != NULL) { /* is `f' a userdata ? */
+ int ftag = lua_tag(L, f);
+ if (ftag == ctrl->iotag) /* does it have the correct tag? */
+ return (FILE *)p;
+ else if (ftag == ctrl->closedtag)
+ lua_error(L, "cannot access a closed file");
+ /* else go through */
+ }
+ return NULL;
+}
+
+
+static FILE *getnonullfile (lua_State *L, IOCtrl *ctrl, int arg) {
+ FILE *f = gethandle(L, ctrl, arg);
+ luaL_arg_check(L, f, arg, "invalid file handle");
+ return f;
+}
+
+
+static FILE *getfilebyref (lua_State *L, IOCtrl *ctrl, int inout) {
+ FILE *f;
+ lua_getglobals(L);
+ lua_getref(L, ctrl->ref[inout]);
+ lua_rawget(L, -2);
+ f = gethandle(L, ctrl, -1);
+ if (f == NULL)
+ luaL_verror(L, "global variable `%.10s' is not a file handle",
+ filenames[inout]);
+ return f;
+}
+
+
+static void setfilebyname (lua_State *L, IOCtrl *ctrl, FILE *f,
+ const char *name) {
+ lua_pushusertag(L, f, ctrl->iotag);
+ lua_setglobal(L, name);
+}
+
+
+#define setfile(L,ctrl,f,inout) (setfilebyname(L,ctrl,f,filenames[inout]))
+
+
+static int setreturn (lua_State *L, IOCtrl *ctrl, FILE *f, int inout) {
+ if (f == NULL)
+ return pushresult(L, 0);
+ else {
+ setfile(L, ctrl, f, inout);
+ lua_pushusertag(L, f, ctrl->iotag);
+ return 1;
+ }
+}
+
+
+static int closefile (lua_State *L, IOCtrl *ctrl, FILE *f) {
+ if (f == stdin || f == stdout || f == stderr)
+ return 1;
+ else {
+ lua_pushusertag(L, f, ctrl->iotag);
+ lua_settag(L, ctrl->closedtag);
+ return (CLOSEFILE(L, f) == 0);
+ }
+}
+
+
+static int io_close (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ lua_pop(L, 1); /* remove upvalue */
+ return pushresult(L, closefile(L, ctrl, getnonullfile(L, ctrl, 1)));
+}
+
+
+static int file_collect (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *f = getnonullfile(L, ctrl, 1);
+ if (f != stdin && f != stdout && f != stderr)
+ CLOSEFILE(L, f);
+ return 0;
+}
+
+
+static int io_open (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *f;
+ lua_pop(L, 1); /* remove upvalue */
+ f = fopen(luaL_check_string(L, 1), luaL_check_string(L, 2));
+ if (f) {
+ lua_pushusertag(L, f, ctrl->iotag);
+ return 1;
+ }
+ else
+ return pushresult(L, 0);
+}
+
+
+
+static int io_fromto (lua_State *L, int inout, const char *mode) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *current;
+ lua_pop(L, 1); /* remove upvalue */
+ if (lua_isnull(L, 1)) {
+ closefile(L, ctrl, getfilebyref(L, ctrl, inout));
+ current = (inout == 0) ? stdin : stdout;
+ }
+ else if (lua_tag(L, 1) == ctrl->iotag) /* deprecated option */
+ current = (FILE *)lua_touserdata(L, 1);
+ else {
+ const char *s = luaL_check_string(L, 1);
+ current = (*s == '|') ? popen(s+1, mode) : fopen(s, mode);
+ }
+ return setreturn(L, ctrl, current, inout);
+}
+
+
+static int io_readfrom (lua_State *L) {
+ return io_fromto(L, INFILE, "r");
+}
+
+
+static int io_writeto (lua_State *L) {
+ return io_fromto(L, OUTFILE, "w");
+}
+
+
+static int io_appendto (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *current;
+ lua_pop(L, 1); /* remove upvalue */
+ current = fopen(luaL_check_string(L, 1), "a");
+ return setreturn(L, ctrl, current, OUTFILE);
+}
+
+
+
+/*
+** {======================================================
+** READ
+** =======================================================
+*/
+
+
+
+#ifdef LUA_COMPAT_READPATTERN
+
+/*
+** We cannot lookahead without need, because this can lock stdin.
+** This flag signals when we need to read a next char.
+*/
+#define NEED_OTHER (EOF-1) /* just some flag different from EOF */
+
+
+static int read_pattern (lua_State *L, FILE *f, const char *p) {
+ int inskip = 0; /* {skip} level */
+ int c = NEED_OTHER;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (*p != '\0') {
+ switch (*p) {
+ case '{':
+ inskip++;
+ p++;
+ continue;
+ case '}':
+ if (!inskip) lua_error(L, "unbalanced braces in read pattern");
+ inskip--;
+ p++;
+ continue;
+ default: {
+ const char *ep = luaI_classend(L, p); /* get what is next */
+ int m; /* match result */
+ if (c == NEED_OTHER) c = getc(f);
+ m = (c==EOF) ? 0 : luaI_singlematch(c, p, ep);
+ if (m) {
+ if (!inskip) luaL_putchar(&b, c);
+ c = NEED_OTHER;
+ }
+ switch (*ep) {
+ case '+': /* repetition (1 or more) */
+ if (!m) goto break_while; /* pattern fails? */
+ /* else go through */
+ case '*': /* repetition (0 or more) */
+ while (m) { /* reads the same item until it fails */
+ c = getc(f);
+ m = (c==EOF) ? 0 : luaI_singlematch(c, p, ep);
+ if (m && !inskip) luaL_putchar(&b, c);
+ }
+ /* go through to continue reading the pattern */
+ case '?': /* optional */
+ p = ep+1; /* continues reading the pattern */
+ continue;
+ default:
+ if (!m) goto break_while; /* pattern fails? */
+ p = ep; /* else continues reading the pattern */
+ }
+ }
+ }
+ } break_while:
+ if (c != NEED_OTHER) ungetc(c, f);
+ luaL_pushresult(&b); /* close buffer */
+ return (*p == '\0');
+}
+
+#else
+
+#define read_pattern(L, f, p) (lua_error(L, "read patterns are deprecated"), 0)
+
+#endif
+
+
+static int read_number (lua_State *L, FILE *f) {
+ long d;
+ if (fscanf(f, "%ld", &d) == 1) {
+ lua_pushnumber(L, d);
+ return 1;
+ }
+ else return 0; /* read fails */
+}
+
+
+static int read_word (lua_State *L, FILE *f) {
+ int c;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ do { c = fgetc(f); } while (isspace(c)); /* skip spaces */
+ while (c != EOF && !isspace(c)) {
+ luaL_putchar(&b, c);
+ c = fgetc(f);
+ }
+ ungetc(c, f);
+ luaL_pushresult(&b); /* close buffer */
+ return (lua_strlen(L, -1) > 0);
+}
+
+
+static int read_line (lua_State *L, FILE *f) {
+ int n = 0;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (;;) {
+ char *p = luaL_prepbuffer(&b);
+ if (!fgets(p, LUAL_BUFFERSIZE, f)) /* read fails? */
+ break;
+ n = strlen(p);
+ if (p[n-1] != '\n')
+ luaL_addsize(&b, n);
+ else {
+ luaL_addsize(&b, n-1); /* do not add the `\n' */
+ break;
+ }
+ }
+ luaL_pushresult(&b); /* close buffer */
+ return (n > 0); /* read something? */
+}
+
+
+static void read_file (lua_State *L, FILE *f) {
+ size_t len = 0;
+ size_t size = BUFSIZ;
+ char *buffer = NULL;
+ for (;;) {
+ char *newbuffer = (char *)realloc(buffer, size);
+ if (newbuffer == NULL) {
+ free(buffer);
+ lua_error(L, "not enough memory to read a file");
+ }
+ buffer = newbuffer;
+ len += fread(buffer+len, sizeof(char), size-len, f);
+ if (len < size) break; /* did not read all it could */
+ size *= 2;
+ }
+ lua_pushlstring(L, buffer, len);
+ free(buffer);
+}
+
+
+static int read_chars (lua_State *L, FILE *f, size_t n) {
+ char *buffer;
+ size_t n1;
+ char statbuff[BUFSIZ];
+ if (n <= BUFSIZ)
+ buffer = statbuff;
+ else {
+ buffer = (char *)malloc(n);
+ if (buffer == NULL)
+ lua_error(L, "not enough memory to read a file");
+ }
+ n1 = fread(buffer, sizeof(char), n, f);
+ lua_pushlstring(L, buffer, n1);
+ if (buffer != statbuff) free(buffer);
+ return (n1 > 0 || n == 0);
+}
+
+
+static int io_read (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ int lastarg = lua_gettop(L) - 1;
+ int firstarg = 1;
+ FILE *f = gethandle(L, ctrl, firstarg);
+ int n;
+ if (f) firstarg++;
+ else f = getfilebyref(L, ctrl, INFILE); /* get _INPUT */
+ lua_pop(L, 1);
+ if (firstarg > lastarg) { /* no arguments? */
+ lua_settop(L, 0); /* erase upvalue and other eventual garbage */
+ firstarg = lastarg = 1; /* correct indices */
+ lua_pushstring(L, "*l"); /* push default argument */
+ }
+ else /* ensure stack space for all results and for auxlib's buffer */
+ luaL_checkstack(L, lastarg-firstarg+1+LUA_MINSTACK, "too many arguments");
+ for (n = firstarg; n<=lastarg; n++) {
+ int success;
+ if (lua_isnumber(L, n))
+ success = read_chars(L, f, (size_t)lua_tonumber(L, n));
+ else {
+ const char *p = luaL_check_string(L, n);
+ if (p[0] != '*')
+ success = read_pattern(L, f, p); /* deprecated! */
+ else {
+ switch (p[1]) {
+ case 'n': /* number */
+ if (!read_number(L, f)) goto endloop; /* read fails */
+ continue; /* number is already pushed; avoid the "pushstring" */
+ case 'l': /* line */
+ success = read_line(L, f);
+ break;
+ case 'a': /* file */
+ read_file(L, f);
+ success = 1; /* always success */
+ break;
+ case 'w': /* word */
+ success = read_word(L, f);
+ break;
+ default:
+ luaL_argerror(L, n, "invalid format");
+ success = 0; /* to avoid warnings */
+ }
+ }
+ }
+ if (!success) {
+ lua_pop(L, 1); /* remove last result */
+ break; /* read fails */
+ }
+ } endloop:
+ return n - firstarg;
+}
+
+/* }====================================================== */
+
+
+static int io_write (lua_State *L) {
+ int lastarg = lua_gettop(L) - 1;
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ int arg = 1;
+ int status = 1;
+ FILE *f = gethandle(L, ctrl, arg);
+ if (f) arg++;
+ else f = getfilebyref(L, ctrl, OUTFILE); /* get _OUTPUT */
+ for (; arg <= lastarg; arg++) {
+ if (lua_type(L, arg) == LUA_TNUMBER) { /* LUA_NUMBER */
+ /* optimization: could be done exactly as for strings */
+ status = status && fprintf(f, "%ld", lua_tonumber(L, arg)) > 0;
+ }
+ else {
+ size_t l;
+ const char *s = luaL_check_lstr(L, arg, &l);
+ status = status && (fwrite(s, sizeof(char), l, f) == l);
+ }
+ }
+ pushresult(L, status);
+ return 1;
+}
+
+
+static int io_seek (lua_State *L) {
+ static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
+ static const char *const modenames[] = {"set", "cur", "end", NULL};
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *f;
+ int op;
+ long offset;
+ lua_pop(L, 1); /* remove upvalue */
+ f = getnonullfile(L, ctrl, 1);
+ op = luaL_findstring(luaL_opt_string(L, 2, "cur"), modenames);
+ offset = luaL_opt_long(L, 3, 0);
+ luaL_arg_check(L, op != -1, 2, "invalid mode");
+ op = fseek(f, offset, mode[op]);
+ if (op)
+ return pushresult(L, 0); /* error */
+ else {
+ lua_pushnumber(L, ftell(f));
+ return 1;
+ }
+}
+
+
+static int io_flush (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *f;
+ lua_pop(L, 1); /* remove upvalue */
+ f = gethandle(L, ctrl, 1);
+ luaL_arg_check(L, f || lua_isnull(L, 1), 1, "invalid file handle");
+ return pushresult(L, fflush(f) == 0);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Other O.S. Operations
+** =======================================================
+*/
+
+static int io_execute (lua_State *L) {
+ lua_pushnumber(L, system(luaL_check_string(L, 1)));
+ return 1;
+}
+
+
+static int io_remove (lua_State *L) {
+ return pushresult(L, remove(luaL_check_string(L, 1)) == 0);
+}
+
+
+static int io_rename (lua_State *L) {
+ return pushresult(L, rename(luaL_check_string(L, 1),
+ luaL_check_string(L, 2)) == 0);
+}
+
+
+static int io_getenv (lua_State *L) {
+ lua_pushstring(L, getenv(luaL_check_string(L, 1))); /* if NULL push nil */
+ return 1;
+}
+
+
+static int io_clock (lua_State *L) {
+ lua_pushnumber(L, ((long)clock())/CLOCKS_PER_SEC);
+ return 1;
+}
+
+
+static int io_date (lua_State *L) {
+ char b[256];
+ const char *s = luaL_opt_string(L, 1, "%c");
+ struct tm *stm;
+ time_t t;
+ time(&t); stm = localtime(&t);
+ if (strftime(b, sizeof(b), s, stm))
+ lua_pushstring(L, b);
+ else
+ lua_error(L, "invalid `date' format");
+ return 1;
+}
+
+
+static int setloc (lua_State *L) {
+ static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
+ LC_NUMERIC, LC_TIME};
+ static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
+ "numeric", "time", NULL};
+ int op = luaL_findstring(luaL_opt_string(L, 2, "all"), catnames);
+ luaL_arg_check(L, op != -1, 2, "invalid option");
+ lua_pushstring(L, setlocale(cat[op], luaL_check_string(L, 1)));
+ return 1;
+}
+
+
+static int io_exit (lua_State *L) {
+ exit(luaL_opt_int(L, 1, EXIT_SUCCESS));
+ return 0; /* to avoid warnings */
+}
+
+/* }====================================================== */
+
+
+
+static int io_debug (lua_State *L) {
+ for (;;) {
+ char buffer[250];
+ fprintf(stderr, "lua_debug> ");
+ if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
+ strcmp(buffer, "cont\n") == 0)
+ return 0;
+ lua_dostring(L, buffer);
+ lua_settop(L, 0); /* remove eventual returns */
+ }
+}
+
+
+#define LEVELS1 12 /* size of the first part of the stack */
+#define LEVELS2 10 /* size of the second part of the stack */
+
+static int errorfb (lua_State *L) {
+ int level = 1; /* skip level 0 (it's this function) */
+ int firstpart = 1; /* still before eventual `...' */
+ lua_Debug ar;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ luaL_addstring(&b, "error: ");
+ luaL_addstring(&b, luaL_check_string(L, 1));
+ luaL_addstring(&b, "\n");
+ while (lua_getstack(L, level++, &ar)) {
+ char buff[120]; /* enough to fit following `sprintf's */
+ if (level == 2)
+ luaL_addstring(&b, "stack traceback:\n");
+ else if (level > LEVELS1 && firstpart) {
+ /* no more than `LEVELS2' more levels? */
+ if (!lua_getstack(L, level+LEVELS2, &ar))
+ level--; /* keep going */
+ else {
+ luaL_addstring(&b, " ...\n"); /* too many levels */
+ while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */
+ level++;
+ }
+ firstpart = 0;
+ continue;
+ }
+ sprintf(buff, "%4d: ", level-1);
+ luaL_addstring(&b, buff);
+ lua_getinfo(L, "Snl", &ar);
+ switch (*ar.namewhat) {
+ case 'g': case 'l': /* global, local */
+ sprintf(buff, "function `%.50s'", ar.name);
+ break;
+ case 'f': /* field */
+ sprintf(buff, "method `%.50s'", ar.name);
+ break;
+ case 't': /* tag method */
+ sprintf(buff, "`%.50s' tag method", ar.name);
+ break;
+ default: {
+ if (*ar.what == 'm') /* main? */
+ sprintf(buff, "main of %.70s", ar.short_src);
+ else if (*ar.what == 'C') /* C function? */
+ sprintf(buff, "%.70s", ar.short_src);
+ else
+ sprintf(buff, "function <%d:%.70s>", ar.linedefined, ar.short_src);
+ ar.source = NULL; /* do not print source again */
+ }
+ }
+ luaL_addstring(&b, buff);
+ if (ar.currentline > 0) {
+ sprintf(buff, " at line %d", ar.currentline);
+ luaL_addstring(&b, buff);
+ }
+ if (ar.source) {
+ sprintf(buff, " [%.70s]", ar.short_src);
+ luaL_addstring(&b, buff);
+ }
+ luaL_addstring(&b, "\n");
+ }
+ luaL_pushresult(&b);
+ lua_getglobal(L, LUA_ALERT);
+ if (lua_isfunction(L, -1)) { /* avoid loop if _ALERT is not defined */
+ lua_pushvalue(L, -2); /* error message */
+ lua_rawcall(L, 1, 0);
+ }
+ return 0;
+}
+
+
+
+static const struct luaL_reg iolib[] = {
+ {LUA_ERRORMESSAGE, errorfb},
+ {"clock", io_clock},
+ {"date", io_date},
+ {"debug", io_debug},
+ {"execute", io_execute},
+ {"exit", io_exit},
+ {"getenv", io_getenv},
+ {"remove", io_remove},
+ {"rename", io_rename},
+ {"setlocale", setloc},
+};
+
+
+static const struct luaL_reg iolibtag[] = {
+ {"appendto", io_appendto},
+ {"closefile", io_close},
+ {"flush", io_flush},
+ {"openfile", io_open},
+ {"read", io_read},
+ {"readfrom", io_readfrom},
+ {"seek", io_seek},
+ {"write", io_write},
+ {"writeto", io_writeto}
+};
+
+
+static void openwithcontrol (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_newuserdata(L, sizeof(IOCtrl));
+ unsigned int i;
+ ctrl->iotag = lua_newtag(L);
+ ctrl->closedtag = lua_newtag(L);
+ for (i=0; i<sizeof(iolibtag)/sizeof(iolibtag[0]); i++) {
+ /* put `ctrl' as upvalue for these functions */
+ lua_pushvalue(L, -1);
+ lua_pushcclosure(L, iolibtag[i].func, 1);
+ lua_setglobal(L, iolibtag[i].name);
+ }
+ /* create references to variable names */
+ lua_pushstring(L, filenames[INFILE]);
+ ctrl->ref[INFILE] = lua_ref(L, 1);
+ lua_pushstring(L, filenames[OUTFILE]);
+ ctrl->ref[OUTFILE] = lua_ref(L, 1);
+ /* predefined file handles */
+ setfile(L, ctrl, stdin, INFILE);
+ setfile(L, ctrl, stdout, OUTFILE);
+ setfilebyname(L, ctrl, stdin, "_STDIN");
+ setfilebyname(L, ctrl, stdout, "_STDOUT");
+ setfilebyname(L, ctrl, stderr, "_STDERR");
+ /* close files when collected */
+ lua_pushcclosure(L, file_collect, 1); /* pops `ctrl' from stack */
+ lua_settagmethod(L, ctrl->iotag, "gc");
+}
+
+
+LUALIB_API void lua_iolibopen (lua_State *L) {
+ luaL_openl(L, iolib);
+ openwithcontrol(L);
+}
+
diff --git a/src/lua/llex.c b/src/lua/llex.c
new file mode 100644
index 00000000..86fb69ab
--- /dev/null
+++ b/src/lua/llex.c
@@ -0,0 +1,411 @@
+/*
+** $Id: llex.c,v 1.6 2004/06/04 13:42:10 neil Exp $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "luadebug.h"
+#include "lzio.h"
+
+
+
+#define next(LS) (LS->current = zgetc(LS->z))
+
+
+
+/* ORDER RESERVED */
+static const char *const token2string [] = {
+ "and", "break", "do", "else", "elseif", "end", "for",
+ "function", "if", "local", "nil", "not", "or", "repeat", "return", "then",
+ "until", "while", "", "..", "...", "==", ">=", "<=", "~=", "", "", "<eof>"};
+
+
+void luaX_init (lua_State *L) {
+ int i;
+ for (i=0; i<NUM_RESERVED; i++) {
+ TString *ts = luaS_new(L, token2string[i]);
+ ts->marked = (unsigned char)(RESERVEDMARK+i); /* reserved word */
+ }
+}
+
+
+#define MAXSRC 80
+
+
+void luaX_checklimit (LexState *ls, int val, int limit, const char *msg) {
+ if (val > limit) {
+ char buff[100];
+ sprintf(buff, "too many %.50s (limit=%d)", msg, limit);
+ luaX_error(ls, buff, ls->t.token);
+ }
+}
+
+
+void luaX_syntaxerror (LexState *ls, const char *s, const char *token) {
+ char buff[MAXSRC];
+ luaO_chunkid(buff, ls->source->str, sizeof(buff));
+ luaO_verror(ls->L, "%.99s;\n last token read: `%.30s' at line %d in %.80s",
+ s, token, ls->linenumber, buff);
+}
+
+
+void luaX_error (LexState *ls, const char *s, int token) {
+ char buff[TOKEN_LEN];
+ luaX_token2str(token, buff);
+ if (buff[0] == '\0')
+ luaX_syntaxerror(ls, s, ls->L->Mbuffer);
+ else
+ luaX_syntaxerror(ls, s, buff);
+}
+
+
+void luaX_token2str (int token, char *s) {
+ if (token < 256) {
+ s[0] = (char)token;
+ s[1] = '\0';
+ }
+ else
+ strcpy(s, token2string[token-FIRST_RESERVED]);
+}
+
+
+static void luaX_invalidchar (LexState *ls, int c) {
+ char buff[8];
+ sprintf(buff, "0x%02X", c);
+ luaX_syntaxerror(ls, "invalid control char", buff);
+}
+
+
+static void inclinenumber (LexState *LS) {
+ next(LS); /* skip '\n' */
+ ++LS->linenumber;
+ luaX_checklimit(LS, LS->linenumber, MAX_INT, "lines in a chunk");
+}
+
+
+void luaX_setinput (lua_State *L, LexState *LS, ZIO *z, TString *source) {
+ LS->L = L;
+ LS->lookahead.token = TK_EOS; /* no look-ahead token */
+ LS->z = z;
+ LS->fs = NULL;
+ LS->linenumber = 1;
+ LS->lastline = 1;
+ LS->source = source;
+ next(LS); /* read first char */
+ if (LS->current == '#') {
+ do { /* skip first line */
+ next(LS);
+ } while (LS->current != '\n' && LS->current != '\r' && LS->current != EOZ);
+ }
+}
+
+
+
+/*
+** =======================================================
+** LEXICAL ANALYZER
+** =======================================================
+*/
+
+
+/* use Mbuffer to store names, literal strings and numbers */
+
+#define EXTRABUFF 128
+#define checkbuffer(L, n, len) if ((len)+(n) > L->Mbuffsize) \
+ luaO_openspace(L, (len)+(n)+EXTRABUFF)
+
+#define save(L, c, l) (L->Mbuffer[l++] = (char)c)
+#define save_and_next(L, LS, l) (save(L, LS->current, l), next(LS))
+
+
+static const char *readname (LexState *LS) {
+ lua_State *L = LS->L;
+ size_t l = 0;
+ checkbuffer(L, 10, l);
+ do {
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ } while (isalnum(LS->current) || LS->current == '_');
+ save(L, '\0', l);
+ return L->Mbuffer;
+}
+
+
+/* LUA_NUMBER */
+static void read_number (LexState *LS, int comma, SemInfo *seminfo) {
+ lua_State *L = LS->L;
+ size_t l = 0;
+ checkbuffer(L, 10, l);
+ if (comma) save(L, '.', l);
+ while (isdigit(LS->current)) {
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ }
+ if (LS->current == '.') {
+ save_and_next(L, LS, l);
+ if (LS->current == '.') {
+ save_and_next(L, LS, l);
+ save(L, '\0', l);
+ luaX_error(LS, "ambiguous syntax"
+ " (decimal point x string concatenation)", TK_NUMBER);
+ }
+ }
+ while (isdigit(LS->current)) {
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ }
+ if (LS->current == 'e' || LS->current == 'E') {
+ save_and_next(L, LS, l); /* read 'E' */
+ if (LS->current == '+' || LS->current == '-')
+ save_and_next(L, LS, l); /* optional exponent sign */
+ while (isdigit(LS->current)) {
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ }
+ }
+ save(L, '\0', l);
+ if (!luaO_str2d(L->Mbuffer, &seminfo->r))
+ luaX_error(LS, "malformed number", TK_NUMBER);
+}
+
+
+static void read_long_string (LexState *LS, SemInfo *seminfo) {
+ lua_State *L = LS->L;
+ int cont = 0;
+ size_t l = 0;
+ checkbuffer(L, 10, l);
+ save(L, '[', l); /* save first '[' */
+ save_and_next(L, LS, l); /* pass the second '[' */
+ for (;;) {
+ checkbuffer(L, 10, l);
+ switch (LS->current) {
+ case EOZ:
+ save(L, '\0', l);
+ if (seminfo)
+ luaX_error(LS, "unfinished long string", TK_STRING);
+ else
+ luaX_error(LS, "unfinished comment", TK_EOS);
+ break; /* to avoid warnings */
+ case '[':
+ save_and_next(L, LS, l);
+ if (LS->current == '[') {
+ cont++;
+ save_and_next(L, LS, l);
+ }
+ continue;
+ case ']':
+ save_and_next(L, LS, l);
+ if (LS->current == ']') {
+ if (cont == 0) goto endloop;
+ cont--;
+ save_and_next(L, LS, l);
+ }
+ continue;
+ case '\n':
+ save(L, '\n', l);
+ inclinenumber(LS);
+ if (LS->current == '\r') next(LS);
+ continue;
+ case '\r':
+ save(L, '\n', l);
+ inclinenumber(LS);
+ if (LS->current == '\n') next(LS);
+ continue;
+ default:
+ if (seminfo) /* no need to save complete comment */
+ save(L, LS->current, l);
+ next(LS);
+ }
+ } endloop:
+ save_and_next(L, LS, l); /* skip the second ']' */
+ save(L, '\0', l);
+ if (seminfo)
+ seminfo->ts = luaS_newlstr(L, L->Mbuffer+2, l-5);
+}
+
+
+static void read_string (LexState *LS, int del, SemInfo *seminfo) {
+ lua_State *L = LS->L;
+ size_t l = 0;
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ while (LS->current != del) {
+ checkbuffer(L, 10, l);
+ switch (LS->current) {
+ case EOZ: case '\n': case '\r':
+ save(L, '\0', l);
+ luaX_error(LS, "unfinished string", TK_STRING);
+ break; /* to avoid warnings */
+ case '\\':
+ next(LS); /* do not save the '\' */
+ switch (LS->current) {
+ case 'a': save(L, '\a', l); next(LS); break;
+ case 'b': save(L, '\b', l); next(LS); break;
+ case 'f': save(L, '\f', l); next(LS); break;
+ case 'n': save(L, '\n', l); next(LS); break;
+ case 'r': save(L, '\r', l); next(LS); break;
+ case 't': save(L, '\t', l); next(LS); break;
+ case 'v': save(L, '\v', l); next(LS); break;
+ case '\n':
+ save(L, '\n', l);
+ inclinenumber(LS);
+ if (LS->current == '\r') next(LS);
+ break;
+ case '\r':
+ save(L, '\n', l);
+ inclinenumber(LS);
+ if (LS->current == '\n') next(LS);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ int c = 0;
+ int i = 0;
+ do {
+ c = 10*c + (LS->current-'0');
+ next(LS);
+ } while (++i<3 && isdigit(LS->current));
+ if (c != (unsigned char)c) {
+ save(L, '\0', l);
+ luaX_error(LS, "escape sequence too large", TK_STRING);
+ }
+ save(L, c, l);
+ break;
+ }
+ default: /* handles \\, \", \', and \? */
+ save_and_next(L, LS, l);
+ }
+ break;
+ default:
+ save_and_next(L, LS, l);
+ }
+ }
+ save_and_next(L, LS, l); /* skip delimiter */
+ save(L, '\0', l);
+ seminfo->ts = luaS_newlstr(L, L->Mbuffer+1, l-3);
+}
+
+
+int luaX_lex (LexState *LS, SemInfo *seminfo) {
+ for (;;) {
+ switch (LS->current) {
+
+ case ' ': case '\t':
+ next(LS);
+ continue;
+
+ case '\n':
+ inclinenumber(LS);
+ if (LS->current == '\r') next(LS);
+ continue;
+
+ case '\r':
+ inclinenumber(LS);
+ if (LS->current == '\n') next(LS);
+ continue;
+
+ case '$':
+ luaX_error(LS, "unexpected `$' (pragmas are no longer supported)", '$');
+ break;
+
+ case '-':
+ next(LS);
+ if (LS->current != '-') return '-';
+ if (next(LS) == '[' && next(LS) == '[')
+ read_long_string(LS, NULL);
+ else
+ while (LS->current != '\n' && LS->current != '\r' && LS->current != EOZ)
+ next(LS);
+ continue;
+
+ case '[':
+ next(LS);
+ if (LS->current != '[') return '[';
+ else {
+ read_long_string(LS, seminfo);
+ return TK_STRING;
+ }
+
+ case '=':
+ next(LS);
+ if (LS->current != '=') return '=';
+ else { next(LS); return TK_EQ; }
+
+ case '<':
+ next(LS);
+ if (LS->current != '=') return '<';
+ else { next(LS); return TK_LE; }
+
+ case '>':
+ next(LS);
+ if (LS->current != '=') return '>';
+ else { next(LS); return TK_GE; }
+
+ case '~':
+ next(LS);
+ if (LS->current != '=') return '~';
+ else { next(LS); return TK_NE; }
+
+ case '"':
+ case '\'':
+ read_string(LS, LS->current, seminfo);
+ return TK_STRING;
+
+ case '.':
+ next(LS);
+ if (LS->current == '.') {
+ next(LS);
+ if (LS->current == '.') {
+ next(LS);
+ return TK_DOTS; /* ... */
+ }
+ else return TK_CONCAT; /* .. */
+ }
+ else if (!isdigit(LS->current)) return '.';
+ else {
+ read_number(LS, 1, seminfo);
+ return TK_NUMBER;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ read_number(LS, 0, seminfo);
+ return TK_NUMBER;
+
+ case EOZ:
+ return TK_EOS;
+
+ case '_': goto tname;
+
+ default:
+ if (!isalpha(LS->current)) {
+ int c = LS->current;
+ if (iscntrl(c))
+ luaX_invalidchar(LS, c);
+ next(LS);
+ return c;
+ }
+ tname: { /* identifier or reserved word */
+ TString *ts = luaS_new(LS->L, readname(LS));
+ if (ts->marked >= RESERVEDMARK) /* reserved word? */
+ return ts->marked-RESERVEDMARK+FIRST_RESERVED;
+ seminfo->ts = ts;
+ return TK_NAME;
+ }
+ }
+ }
+}
+
diff --git a/src/lua/llex.h b/src/lua/llex.h
new file mode 100644
index 00000000..5fb13c88
--- /dev/null
+++ b/src/lua/llex.h
@@ -0,0 +1,72 @@
+/*
+** $Id: llex.h,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llex_h
+#define llex_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+
+#define FIRST_RESERVED 257
+
+/* maximum length of a reserved word (+1 for final 0) */
+#define TOKEN_LEN 15
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER RESERVED"
+*/
+enum RESERVED {
+ /* terminal symbols denoted by reserved words */
+ TK_AND = FIRST_RESERVED, TK_BREAK,
+ TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FOR, TK_FUNCTION, TK_IF, TK_LOCAL,
+ TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_UNTIL, TK_WHILE,
+ /* other terminal symbols */
+ TK_NAME, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
+ TK_STRING, TK_EOS
+};
+
+/* number of reserved words */
+#define NUM_RESERVED ((int)(TK_WHILE-FIRST_RESERVED+1))
+
+
+typedef union {
+ Number r;
+ TString *ts;
+} SemInfo; /* semantics information */
+
+
+typedef struct Token {
+ int token;
+ SemInfo seminfo;
+} Token;
+
+
+typedef struct LexState {
+ int current; /* current character */
+ Token t; /* current token */
+ Token lookahead; /* look ahead token */
+ struct FuncState *fs; /* `FuncState' is private to the parser */
+ struct lua_State *L;
+ struct zio *z; /* input stream */
+ int linenumber; /* input line counter */
+ int lastline; /* line of last token `consumed' */
+ TString *source; /* current source name */
+} LexState;
+
+
+void luaX_init (lua_State *L);
+void luaX_setinput (lua_State *L, LexState *LS, ZIO *z, TString *source);
+int luaX_lex (LexState *LS, SemInfo *seminfo);
+void luaX_checklimit (LexState *ls, int val, int limit, const char *msg);
+void luaX_syntaxerror (LexState *ls, const char *s, const char *token);
+void luaX_error (LexState *ls, const char *s, int token);
+void luaX_token2str (int token, char *s);
+
+
+#endif
diff --git a/src/lua/llimits.h b/src/lua/llimits.h
new file mode 100644
index 00000000..39a1e0df
--- /dev/null
+++ b/src/lua/llimits.h
@@ -0,0 +1,205 @@
+/*
+** $Id: llimits.h,v 1.2 2001/11/26 23:00:24 darkgod Exp $
+** Limits, basic types, and some other "installation-dependent" definitions
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llimits_h
+#define llimits_h
+
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+
+
+
+/*
+** try to find number of bits in an integer
+*/
+#ifndef BITS_INT
+/* avoid overflows in comparison */
+#if INT_MAX-20 < 32760
+#define BITS_INT 16
+#else
+#if INT_MAX > 2147483640L
+/* machine has at least 32 bits */
+#define BITS_INT 32
+#else
+#error "you must define BITS_INT with number of bits in an integer"
+#endif
+#endif
+#endif
+
+
+/*
+** Define the type `number' of Lua
+** GREP LUA_NUMBER to change that
+*/
+#ifndef LUA_NUM_TYPE
+#define LUA_NUM_TYPE int32_t
+#endif
+
+typedef LUA_NUM_TYPE Number;
+
+/* function to convert a Number to a string */
+#define NUMBER_FMT "%ld" /* LUA_NUMBER */
+#define lua_number2str(s,n) sprintf((s), NUMBER_FMT, (n))
+
+/* function to convert a string to a Number */
+#define lua_str2number(s,p) strtol((s), (p), 10)
+
+
+
+typedef int_least32_t lint32; /* unsigned int with at least 32 bits */
+
+
+#define MAX_SIZET ((size_t)(~(size_t)0)-2)
+
+
+#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
+
+/*
+** conversion of pointer to int (for hashing only)
+** (the shift removes bits that are usually 0 because of alignment)
+*/
+#define IntPoint(p) (((unsigned long)(p)) >> 3)
+
+
+
+#define MINPOWER2 4 /* minimum size for "growing" vectors */
+
+
+
+#ifndef DEFAULT_STACK_SIZE
+#define DEFAULT_STACK_SIZE 1024
+#endif
+
+
+
+/* type to ensure maximum alignment */
+union L_Umaxalign { long d; char *s; long l; };
+
+
+
+/*
+** type for virtual-machine instructions
+** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
+** For a very small machine, you may change that to 2 bytes (and adjust
+** the following limits accordingly)
+*/
+typedef unsigned long Instruction;
+
+
+/*
+** size and position of opcode arguments.
+** For an instruction with 2 bytes, size is 16, and size_b can be 5
+** (accordingly, size_u will be 10, and size_a will be 5)
+*/
+#define SIZE_INSTRUCTION 32
+#define SIZE_B 9
+
+#define SIZE_OP 6
+#define SIZE_U (SIZE_INSTRUCTION-SIZE_OP)
+#define POS_U SIZE_OP
+#define POS_B SIZE_OP
+#define SIZE_A (SIZE_INSTRUCTION-(SIZE_OP+SIZE_B))
+#define POS_A (SIZE_OP+SIZE_B)
+
+
+/*
+** limits for opcode arguments.
+** we use (signed) int to manipulate most arguments,
+** so they must fit in BITS_INT-1 bits (-1 for sign)
+*/
+#if SIZE_U < BITS_INT-1
+#define MAXARG_U ((1<<SIZE_U)-1)
+#define MAXARG_S (MAXARG_U>>1) /* `S' is signed */
+#else
+#define MAXARG_U MAX_INT
+#define MAXARG_S MAX_INT
+#endif
+
+#if SIZE_A < BITS_INT-1
+#define MAXARG_A ((1<<SIZE_A)-1)
+#else
+#define MAXARG_A MAX_INT
+#endif
+
+#if SIZE_B < BITS_INT-1
+#define MAXARG_B ((1<<SIZE_B)-1)
+#else
+#define MAXARG_B MAX_INT
+#endif
+
+
+/* maximum stack size in a function */
+#ifndef MAXSTACK
+#define MAXSTACK 250
+#endif
+
+#if MAXSTACK > MAXARG_B
+#undef MAXSTACK
+#define MAXSTACK MAXARG_B
+#endif
+
+
+/* maximum number of local variables */
+#ifndef MAXLOCALS
+#define MAXLOCALS 200 /* arbitrary limit (<MAXSTACK) */
+#endif
+#if MAXLOCALS>=MAXSTACK
+#undef MAXLOCALS
+#define MAXLOCALS (MAXSTACK-1)
+#endif
+
+
+/* maximum number of upvalues */
+#ifndef MAXUPVALUES
+#define MAXUPVALUES 32 /* arbitrary limit (<=MAXARG_B) */
+#endif
+#if MAXUPVALUES>MAXARG_B
+#undef MAXUPVALUES
+#define MAXUPVALUES MAXARG_B
+#endif
+
+
+/* maximum number of variables in the left side of an assignment */
+#ifndef MAXVARSLH
+#define MAXVARSLH 100 /* arbitrary limit (<MULT_RET) */
+#endif
+#if MAXVARSLH>=MULT_RET
+#undef MAXVARSLH
+#define MAXVARSLH (MULT_RET-1)
+#endif
+
+
+/* maximum number of parameters in a function */
+#ifndef MAXPARAMS
+#define MAXPARAMS 100 /* arbitrary limit (<MAXLOCALS) */
+#endif
+#if MAXPARAMS>=MAXLOCALS
+#undef MAXPARAMS
+#define MAXPARAMS (MAXLOCALS-1)
+#endif
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 64
+#if LFIELDS_PER_FLUSH>(MAXSTACK/4)
+#undef LFIELDS_PER_FLUSH
+#define LFIELDS_PER_FLUSH (MAXSTACK/4)
+#endif
+
+/* number of record items to accumulate before a SETMAP instruction */
+/* (each item counts 2 elements on the stack: an index and a value) */
+#define RFIELDS_PER_FLUSH (LFIELDS_PER_FLUSH/2)
+
+
+/* maximum lookback to find a real constant (for code generation) */
+#ifndef LOOKBACKNUMS
+#define LOOKBACKNUMS 20 /* arbitrary constant */
+#endif
+
+
+#endif
diff --git a/src/lua/lmem.c b/src/lua/lmem.c
new file mode 100644
index 00000000..8fdecef3
--- /dev/null
+++ b/src/lua/lmem.c
@@ -0,0 +1,150 @@
+/*
+** $Id: lmem.c,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+
+#ifdef LUA_DEBUG
+/*
+** {======================================================================
+** Controlled version for realloc.
+** =======================================================================
+*/
+
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+
+#define realloc(b, s) debug_realloc(b, s)
+#define malloc(b) debug_realloc(NULL, b)
+#define free(b) debug_realloc(b, 0)
+
+
+/* ensures maximum alignment for HEADER */
+#define HEADER (sizeof(union L_Umaxalign))
+
+#define MARKSIZE 16
+#define MARK 0x55 /* 01010101 (a nice pattern) */
+
+
+#define blocksize(b) ((unsigned long *)((char *)(b) - HEADER))
+
+unsigned long memdebug_numblocks = 0;
+unsigned long memdebug_total = 0;
+unsigned long memdebug_maxmem = 0;
+unsigned long memdebug_memlimit = LONG_MAX;
+
+
+static void *checkblock (void *block) {
+ unsigned long *b = blocksize(block);
+ unsigned long size = *b;
+ int i;
+ for (i=0;i<MARKSIZE;i++)
+ assert(*(((char *)b)+HEADER+size+i) == MARK+i); /* corrupted block? */
+ memdebug_numblocks--;
+ memdebug_total -= size;
+ return b;
+}
+
+
+static void freeblock (void *block) {
+ if (block) {
+ size_t size = *blocksize(block);
+ block = checkblock(block);
+ memset(block, -1, size+HEADER+MARKSIZE); /* erase block */
+ (free)(block); /* free original block */
+ }
+}
+
+
+static void *debug_realloc (void *block, size_t size) {
+ if (size == 0) {
+ freeblock(block);
+ return NULL;
+ }
+ else if (memdebug_total+size > memdebug_memlimit)
+ return NULL; /* to test memory allocation errors */
+ else {
+ size_t realsize = HEADER+size+MARKSIZE;
+ char *newblock = (char *)(malloc)(realsize); /* alloc a new block */
+ int i;
+ if (realsize < size) return NULL; /* overflow! */
+ if (newblock == NULL) return NULL;
+ if (block) {
+ size_t oldsize = *blocksize(block);
+ if (oldsize > size) oldsize = size;
+ memcpy(newblock+HEADER, block, oldsize);
+ freeblock(block); /* erase (and check) old copy */
+ }
+ memdebug_total += size;
+ if (memdebug_total > memdebug_maxmem) memdebug_maxmem = memdebug_total;
+ memdebug_numblocks++;
+ *(unsigned long *)newblock = size;
+ for (i=0;i<MARKSIZE;i++)
+ *(newblock+HEADER+size+i) = (char)(MARK+i);
+ return newblock+HEADER;
+ }
+}
+
+
+/* }====================================================================== */
+#endif
+
+
+
+/*
+** Real ISO (ANSI) systems do not need these tests;
+** but some systems (Sun OS) are not that ISO...
+*/
+#ifdef OLD_ANSI
+#define realloc(b,s) ((b) == NULL ? malloc(s) : (realloc)(b, s))
+#define free(b) if (b) (free)(b)
+#endif
+
+
+void *luaM_growaux (lua_State *L, void *block, size_t nelems,
+ int inc, size_t size, const char *errormsg, size_t limit) {
+ size_t newn = nelems+inc;
+ if (nelems >= limit-inc) lua_error(L, errormsg);
+ if ((newn ^ nelems) <= nelems || /* still the same power-of-2 limit? */
+ (nelems > 0 && newn < MINPOWER2)) /* or block already is MINPOWER2? */
+ return block; /* do not need to reallocate */
+ else /* it crossed a power-of-2 boundary; grow to next power */
+ return luaM_realloc(L, block, luaO_power2(newn)*size);
+}
+
+
+/*
+** generic allocation routine.
+*/
+void *luaM_realloc (lua_State *L, void *block, lint32 size) {
+ if (size == 0) {
+ free(block); /* block may be NULL; that is OK for free */
+ return NULL;
+ }
+ else if (size >= MAX_SIZET)
+ lua_error(L, "memory allocation error: block too big");
+ block = realloc(block, size);
+ if (block == NULL) {
+ if (L)
+ luaD_breakrun(L, LUA_ERRMEM); /* break run without error message */
+ else return NULL; /* error before creating state! */
+ }
+ return block;
+}
+
+
diff --git a/src/lua/lmem.h b/src/lua/lmem.h
new file mode 100644
index 00000000..0d27c336
--- /dev/null
+++ b/src/lua/lmem.h
@@ -0,0 +1,42 @@
+/*
+** $Id: lmem.h,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lmem_h
+#define lmem_h
+
+
+#include <stddef.h>
+
+#include "llimits.h"
+#include "lua.h"
+
+void *luaM_realloc (lua_State *L, void *oldblock, lint32 size);
+void *luaM_growaux (lua_State *L, void *block, size_t nelems,
+ int inc, size_t size, const char *errormsg,
+ size_t limit);
+
+#define luaM_free(L, b) luaM_realloc(L, (b), 0)
+#define luaM_malloc(L, t) luaM_realloc(L, NULL, (t))
+#define luaM_new(L, t) ((t *)luaM_malloc(L, sizeof(t)))
+#define luaM_newvector(L, n,t) ((t *)luaM_malloc(L, (n)*(lint32)sizeof(t)))
+
+#define luaM_growvector(L, v,nelems,inc,t,e,l) \
+ ((v)=(t *)luaM_growaux(L, v,nelems,inc,sizeof(t),e,l))
+
+#define luaM_reallocvector(L, v,n,t) \
+ ((v)=(t *)luaM_realloc(L, v,(n)*(lint32)sizeof(t)))
+
+
+#ifdef LUA_DEBUG
+extern unsigned long memdebug_numblocks;
+extern unsigned long memdebug_total;
+extern unsigned long memdebug_maxmem;
+extern unsigned long memdebug_memlimit;
+#endif
+
+
+#endif
+
diff --git a/src/lua/lobject.c b/src/lua/lobject.c
new file mode 100644
index 00000000..cd9d1f0b
--- /dev/null
+++ b/src/lua/lobject.c
@@ -0,0 +1,125 @@
+/*
+** $Id: lobject.c,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Some generic functions over Lua objects
+** See Copyright Notice in lua.h
+*/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+const TObject luaO_nilobject = {LUA_TNIL, {NULL}};
+
+
+const char *const luaO_typenames[] = {
+ "userdata", "nil", "number", "string", "table", "function"
+};
+
+
+
+/*
+** returns smaller power of 2 larger than `n' (minimum is MINPOWER2)
+*/
+lint32 luaO_power2 (lint32 n) {
+ lint32 p = MINPOWER2;
+ while (p<=n) p<<=1;
+ return p;
+}
+
+
+int luaO_equalObj (const TObject *t1, const TObject *t2) {
+ if (ttype(t1) != ttype(t2)) return 0;
+ switch (ttype(t1)) {
+ case LUA_TNUMBER:
+ return nvalue(t1) == nvalue(t2);
+ case LUA_TSTRING: case LUA_TUSERDATA:
+ return tsvalue(t1) == tsvalue(t2);
+ case LUA_TTABLE:
+ return hvalue(t1) == hvalue(t2);
+ case LUA_TFUNCTION:
+ return clvalue(t1) == clvalue(t2);
+ default:
+ LUA_ASSERT(ttype(t1) == LUA_TNIL, "invalid type");
+ return 1; /* LUA_TNIL */
+ }
+}
+
+
+char *luaO_openspace (lua_State *L, size_t n) {
+ if (n > L->Mbuffsize) {
+ luaM_reallocvector(L, L->Mbuffer, n, char);
+ L->nblocks += (n - L->Mbuffsize)*sizeof(char);
+ L->Mbuffsize = n;
+ }
+ return L->Mbuffer;
+}
+
+
+int luaO_str2d (const char *s, Number *result) { /* LUA_NUMBER */
+ char *endptr;
+ Number res = lua_str2number(s, &endptr);
+ if (endptr == s) return 0; /* no conversion */
+ while (isspace((unsigned char)*endptr)) endptr++;
+ if (*endptr != '\0') return 0; /* invalid trailing characters? */
+ *result = res;
+ return 1;
+}
+
+
+/* maximum length of a string format for `luaO_verror' */
+#define MAX_VERROR 280
+
+/* this function needs to handle only '%d' and '%.XXs' formats */
+void luaO_verror (lua_State *L, const char *fmt, ...) {
+ va_list argp;
+ char buff[MAX_VERROR]; /* to hold formatted message */
+ va_start(argp, fmt);
+ vsprintf(buff, fmt, argp);
+ va_end(argp);
+ lua_error(L, buff);
+}
+
+
+void luaO_chunkid (char *out, const char *source, int bufflen) {
+ if (*source == '=') {
+ strncpy(out, source+1, bufflen); /* remove first char */
+ out[bufflen-1] = '\0'; /* ensures null termination */
+ }
+ else {
+ if (*source == '@') {
+ int l;
+ source++; /* skip the `@' */
+ bufflen -= sizeof("file `...%s'");
+ l = strlen(source);
+ if (l>bufflen) {
+ source += (l-bufflen); /* get last part of file name */
+ sprintf(out, "file `...%.99s'", source);
+ }
+ else
+ sprintf(out, "file `%.99s'", source);
+ }
+ else {
+ int len = strcspn(source, "\n"); /* stop at first newline */
+ bufflen -= sizeof("string \"%.*s...\"");
+ if (len > bufflen) len = bufflen;
+ if (source[len] != '\0') { /* must truncate? */
+ strcpy(out, "string \"");
+ out += strlen(out);
+ strncpy(out, source, len);
+ strcpy(out+len, "...\"");
+ }
+ else
+ sprintf(out, "string \"%.99s\"", source);
+ }
+ }
+}
diff --git a/src/lua/lobject.h b/src/lua/lobject.h
new file mode 100644
index 00000000..ce978205
--- /dev/null
+++ b/src/lua/lobject.h
@@ -0,0 +1,204 @@
+/*
+** $Id: lobject.h,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Type definitions for Lua objects
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lobject_h
+#define lobject_h
+
+
+#include "llimits.h"
+#include "lua.h"
+
+
+#ifdef LUA_DEBUG
+#undef NDEBUG
+#include <assert.h>
+#define LUA_INTERNALERROR(s) assert(((void)s,0))
+#define LUA_ASSERT(c,s) assert(((void)s,(c)))
+#else
+#define LUA_INTERNALERROR(s) /* empty */
+#define LUA_ASSERT(c,s) /* empty */
+#endif
+
+
+#ifdef LUA_DEBUG
+/* to avoid warnings, and make sure value is really unused */
+#define UNUSED(x) (x=0, (void)(x))
+#else
+#define UNUSED(x) ((void)(x)) /* to avoid warnings */
+#endif
+
+
+/* mark for closures active in the stack */
+#define LUA_TMARK 6
+
+
+/* tags for values visible from Lua == first user-created tag */
+#define NUM_TAGS 6
+
+
+/* check whether `t' is a mark */
+#define is_T_MARK(t) ((t) == LUA_TMARK)
+
+
+typedef union {
+ struct TString *ts; /* LUA_TSTRING, LUA_TUSERDATA */
+ struct Closure *cl; /* LUA_TFUNCTION */
+ struct Hash *a; /* LUA_TTABLE */
+ struct CallInfo *i; /* LUA_TLMARK */
+ Number n; /* LUA_TNUMBER */
+} Value;
+
+
+/* Macros to access values */
+#define ttype(o) ((o)->ttype)
+#define nvalue(o) ((o)->value.n)
+#define tsvalue(o) ((o)->value.ts)
+#define clvalue(o) ((o)->value.cl)
+#define hvalue(o) ((o)->value.a)
+#define infovalue(o) ((o)->value.i)
+#define svalue(o) (tsvalue(o)->str)
+
+
+typedef struct lua_TObject {
+ int ttype;
+ Value value;
+} TObject;
+
+
+/*
+** String headers for string table
+*/
+
+/*
+** most `malloc' libraries allocate memory in blocks of 8 bytes. TSPACK
+** tries to make sizeof(TString) a multiple of this granularity, to reduce
+** waste of space.
+*/
+#define TSPACK ((int)sizeof(int))
+
+typedef struct TString {
+ union {
+ struct { /* for strings */
+ unsigned long hash;
+ int constindex; /* hint to reuse constants */
+ } s;
+ struct { /* for userdata */
+ int tag;
+ void *value;
+ } d;
+ } u;
+ size_t len;
+ struct TString *nexthash; /* chain for hash table */
+ int marked;
+ char str[TSPACK]; /* variable length string!! must be the last field! */
+} TString;
+
+
+/*
+** Function Prototypes
+*/
+typedef struct Proto {
+ Number *knum; /* Number numbers used by the function */
+ int nknum; /* size of `knum' */
+ struct TString **kstr; /* strings used by the function */
+ int nkstr; /* size of `kstr' */
+ struct Proto **kproto; /* functions defined inside the function */
+ int nkproto; /* size of `kproto' */
+ Instruction *code;
+ int ncode; /* size of `code'; when 0 means an incomplete `Proto' */
+ short numparams;
+ short is_vararg;
+ short maxstacksize;
+ short marked;
+ struct Proto *next;
+ /* debug information */
+ int *lineinfo; /* map from opcodes to source lines */
+ int nlineinfo; /* size of `lineinfo' */
+ int nlocvars;
+ struct LocVar *locvars; /* information about local variables */
+ int lineDefined;
+ TString *source;
+} Proto;
+
+
+typedef struct LocVar {
+ TString *varname;
+ int startpc; /* first point where variable is active */
+ int endpc; /* first point where variable is dead */
+} LocVar;
+
+
+/*
+** Closures
+*/
+typedef struct Closure {
+ union {
+ lua_CFunction c; /* C functions */
+ struct Proto *l; /* Lua functions */
+ } f;
+ struct Closure *next;
+ struct Closure *mark; /* marked closures (point to itself when not marked) */
+ short isC; /* 0 for Lua functions, 1 for C functions */
+ short nupvalues;
+ TObject upvalue[1];
+} Closure;
+
+
+#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->isC)
+
+
+typedef struct Node {
+ TObject key;
+ TObject val;
+ struct Node *next; /* for chaining */
+} Node;
+
+typedef struct Hash {
+ Node *node;
+ int htag;
+ int size;
+ Node *firstfree; /* this position is free; all positions after it are full */
+ struct Hash *next;
+ struct Hash *mark; /* marked tables (point to itself when not marked) */
+} Hash;
+
+
+/* unmarked tables and closures are represented by pointing `mark' to
+** themselves
+*/
+#define ismarked(x) ((x)->mark != (x))
+
+
+/*
+** informations about a call (for debugging)
+*/
+typedef struct CallInfo {
+ struct Closure *func; /* function being called */
+ const Instruction **pc; /* current pc of called function */
+ int lastpc; /* last pc traced */
+ int line; /* current line */
+ int refi; /* current index in `lineinfo' */
+} CallInfo;
+
+
+extern const TObject luaO_nilobject;
+extern const char *const luaO_typenames[];
+
+
+#define luaO_typename(o) (luaO_typenames[ttype(o)])
+
+
+lint32 luaO_power2 (lint32 n);
+char *luaO_openspace (lua_State *L, size_t n);
+
+int luaO_equalObj (const TObject *t1, const TObject *t2);
+int luaO_str2d (const char *s, Number *result);
+
+void luaO_verror (lua_State *L, const char *fmt, ...);
+void luaO_chunkid (char *out, const char *source, int len);
+
+
+#endif
diff --git a/src/lua/lopcodes.h b/src/lua/lopcodes.h
new file mode 100644
index 00000000..59740896
--- /dev/null
+++ b/src/lua/lopcodes.h
@@ -0,0 +1,168 @@
+/*
+** $Id: lopcodes.h,v 1.5 2004/06/04 13:42:10 neil Exp $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lopcodes_h
+#define lopcodes_h
+
+#include "llimits.h"
+
+
+/*===========================================================================
+ We assume that instructions are unsigned numbers.
+ All instructions have an opcode in the first 6 bits. Moreover,
+ an instruction can have 0, 1, or 2 arguments. Instructions can
+ have the following types:
+ type 0: no arguments
+ type 1: 1 unsigned argument in the higher bits (called `U')
+ type 2: 1 signed argument in the higher bits (`S')
+ type 3: 1st unsigned argument in the higher bits (`A')
+ 2nd unsigned argument in the middle bits (`B')
+
+ A signed argument is represented in excess K; that is, the number
+ value is the unsigned value minus K. K is exactly the maximum value
+ for that argument (so that -max is represented by 0, and +max is
+ represented by 2*max), which is half the maximum for the corresponding
+ unsigned argument.
+
+ The size of each argument is defined in `llimits.h'. The usual is an
+ instruction with 32 bits, U arguments with 26 bits (32-6), B arguments
+ with 9 bits, and A arguments with 17 bits (32-6-9). For small
+ installations, the instruction size can be 16, so U has 10 bits,
+ and A and B have 5 bits each.
+===========================================================================*/
+
+
+
+
+/* creates a mask with `n' 1 bits at position `p' */
+#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p)
+
+/* creates a mask with `n' 0 bits at position `p' */
+#define MASK0(n,p) (~MASK1(n,p))
+
+/*
+** the following macros help to manipulate instructions
+*/
+
+#define CREATE_0(o) ((Instruction)(o))
+#define GET_OPCODE(i) ((OpCode)((i)&MASK1(SIZE_OP,0)))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,0)) | (Instruction)(o)))
+
+#define CREATE_U(o,u) ((Instruction)(o) | ((Instruction)(u)<<POS_U))
+#define GETARG_U(i) ((int)((i)>>POS_U))
+#define SETARG_U(i,u) ((i) = (((i)&MASK0(SIZE_U,POS_U)) | \
+ ((Instruction)(u)<<POS_U)))
+
+#define CREATE_S(o,s) CREATE_U((o),(s)+MAXARG_S)
+#define GETARG_S(i) (GETARG_U(i)-MAXARG_S)
+#define SETARG_S(i,s) SETARG_U((i),(s)+MAXARG_S)
+
+
+#define CREATE_AB(o,a,b) ((Instruction)(o) | ((Instruction)(a)<<POS_A) \
+ | ((Instruction)(b)<<POS_B))
+#define GETARG_A(i) ((int)((i)>>POS_A))
+#define SETARG_A(i,a) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
+ ((Instruction)(a)<<POS_A)))
+#define GETARG_B(i) ((int)(((i)>>POS_B) & MASK1(SIZE_B,0)))
+#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
+ ((Instruction)(b)<<POS_B)))
+
+
+/*
+** K = U argument used as index to `kstr'
+** J = S argument used as jump offset (relative to pc of next instruction)
+** L = unsigned argument used as index of local variable
+** N = U argument used as index to `knum'
+*/
+
+typedef enum {
+/*----------------------------------------------------------------------
+name args stack before stack after side effects
+------------------------------------------------------------------------*/
+OP_END,/* - - (return) no results */
+OP_RETURN,/* U v_n-v_x(at u) (return) returns v_x-v_n */
+
+OP_CALL,/* A B v_n-v_1 f(at a) r_b-r_1 f(v1,...,v_n) */
+OP_TAILCALL,/* A B v_n-v_1 f(at a) (return) f(v1,...,v_n) */
+
+OP_PUSHNIL,/* U - nil_1-nil_u */
+OP_POP,/* U a_u-a_1 - */
+
+OP_PUSHINT,/* S - (Number)s */
+OP_PUSHSTRING,/* K - KSTR[k] */
+OP_PUSHNUM,/* N - KNUM[n] */
+OP_PUSHNEGNUM,/* N - -KNUM[n] */
+
+OP_PUSHUPVALUE,/* U - Closure[u] */
+
+OP_GETLOCAL,/* L - LOC[l] */
+OP_GETGLOBAL,/* K - VAR[KSTR[k]] */
+
+OP_GETTABLE,/* - i t t[i] */
+OP_GETDOTTED,/* K t t[KSTR[k]] */
+OP_GETINDEXED,/* L t t[LOC[l]] */
+OP_PUSHSELF,/* K t t t[KSTR[k]] */
+
+OP_CREATETABLE,/* U - newarray(size = u) */
+
+OP_SETLOCAL,/* L x - LOC[l]=x */
+OP_SETGLOBAL,/* K x - VAR[KSTR[k]]=x */
+OP_SETTABLE,/* A B v a_a-a_1 i t (pops b values) t[i]=v */
+
+OP_SETLIST,/* A B v_b-v_1 t t t[i+a*FPF]=v_i */
+OP_SETMAP,/* U v_u k_u - v_1 k_1 t t t[k_i]=v_i */
+
+OP_ADD,/* - y x x+y */
+OP_ADDI,/* S x x+s */
+OP_SUB,/* - y x x-y */
+OP_MULT,/* - y x x*y */
+OP_DIV,/* - y x x/y */
+OP_POW,/* - y x x^y */
+OP_CONCAT,/* U v_u-v_1 v1..-..v_u */
+OP_MINUS,/* - x -x */
+OP_NOT,/* - x (x==nil)? 1 : nil */
+
+OP_JMPNE,/* J y x - (x~=y)? PC+=s */
+OP_JMPEQ,/* J y x - (x==y)? PC+=s */
+OP_JMPLT,/* J y x - (x<y)? PC+=s */
+OP_JMPLE,/* J y x - (x<y)? PC+=s */
+OP_JMPGT,/* J y x - (x>y)? PC+=s */
+OP_JMPGE,/* J y x - (x>=y)? PC+=s */
+
+OP_JMPT,/* J x - (x~=nil)? PC+=s */
+OP_JMPF,/* J x - (x==nil)? PC+=s */
+OP_JMPONT,/* J x (x~=nil)? x : - (x~=nil)? PC+=s */
+OP_JMPONF,/* J x (x==nil)? x : - (x==nil)? PC+=s */
+OP_JMP,/* J - - PC+=s */
+
+OP_PUSHNILJMP,/* - - nil PC++; */
+
+OP_FORPREP,/* J */
+OP_FORLOOP,/* J */
+
+OP_LFORPREP,/* J */
+OP_LFORLOOP,/* J */
+
+OP_CLOSURE/* A B v_b-v_1 closure(KPROTO[a], v_1-v_b) */
+
+} OpCode;
+
+#define NUM_OPCODES ((int)OP_CLOSURE+1)
+
+
+#define ISJUMP(o) (OP_JMPNE <= (o) && (o) <= OP_JMP)
+
+
+
+/* special code to fit a LUA_MULTRET inside an argB */
+#define MULT_RET 255 /* (<=MAXARG_B) */
+#if MULT_RET>MAXARG_B
+#undef MULT_RET
+#define MULT_RET MAXARG_B
+#endif
+
+
+#endif
diff --git a/src/lua/lparser.c b/src/lua/lparser.c
new file mode 100644
index 00000000..1ac1f37b
--- /dev/null
+++ b/src/lua/lparser.c
@@ -0,0 +1,1129 @@
+/*
+** $Id: lparser.c,v 1.8 2004/06/04 13:42:10 neil Exp $
+** LL(1) Parser and code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "lfunc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+
+#ifdef __MWERKS__
+/* To avoid name conflict resulting from the use of prefix header */
+#define stat lua_hack_stat
+#endif /* __MWERKS__ */
+
+
+/*
+** Constructors descriptor:
+** `n' indicates number of elements, and `k' signals whether
+** it is a list constructor (k = 0) or a record constructor (k = 1)
+** or empty (k = ';' or '}')
+*/
+typedef struct Constdesc {
+ int n;
+ int k;
+} Constdesc;
+
+
+typedef struct Breaklabel {
+ struct Breaklabel *previous; /* chain */
+ int breaklist;
+ int stacklevel;
+} Breaklabel;
+
+
+
+
+/*
+** prototypes for recursive non-terminal functions
+*/
+static void body (LexState *ls, int needself, int line);
+static void chunk (LexState *ls);
+static void constructor (LexState *ls);
+static void expr (LexState *ls, expdesc *v);
+static void exp1 (LexState *ls);
+
+
+
+static void next (LexState *ls) {
+ ls->lastline = ls->linenumber;
+ if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
+ ls->t = ls->lookahead; /* use this one */
+ ls->lookahead.token = TK_EOS; /* and discharge it */
+ }
+ else
+ ls->t.token = luaX_lex(ls, &ls->t.seminfo); /* read next token */
+}
+
+
+static void lookahead (LexState *ls) {
+ LUA_ASSERT(ls->lookahead.token == TK_EOS, "two look-aheads");
+ ls->lookahead.token = luaX_lex(ls, &ls->lookahead.seminfo);
+}
+
+
+static void error_expected (LexState *ls, int token) {
+ char buff[100], t[TOKEN_LEN];
+ luaX_token2str(token, t);
+ sprintf(buff, "`%.20s' expected", t);
+ luaK_error(ls, buff);
+}
+
+
+static void check (LexState *ls, int c) {
+ if (ls->t.token != c)
+ error_expected(ls, c);
+ next(ls);
+}
+
+
+static void check_condition (LexState *ls, int c, const char *msg) {
+ if (!c) luaK_error(ls, msg);
+}
+
+
+static int optional (LexState *ls, int c) {
+ if (ls->t.token == c) {
+ next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+static void check_match (LexState *ls, int what, int who, int where) {
+ if (ls->t.token != what) {
+ if (where == ls->linenumber)
+ error_expected(ls, what);
+ else {
+ char buff[100];
+ char t_what[TOKEN_LEN], t_who[TOKEN_LEN];
+ luaX_token2str(what, t_what);
+ luaX_token2str(who, t_who);
+ sprintf(buff, "`%.20s' expected (to close `%.20s' at line %d)",
+ t_what, t_who, where);
+ luaK_error(ls, buff);
+ }
+ }
+ next(ls);
+}
+
+
+static int string_constant (FuncState *fs, TString *s) {
+ Proto *f = fs->f;
+ int c = s->u.s.constindex;
+ if (c >= f->nkstr || f->kstr[c] != s) {
+ luaM_growvector(fs->L, f->kstr, f->nkstr, 1, TString *,
+ "constant table overflow", MAXARG_U);
+ c = f->nkstr++;
+ f->kstr[c] = s;
+ s->u.s.constindex = c; /* hint for next time */
+ }
+ return c;
+}
+
+
+static void code_string (LexState *ls, TString *s) {
+ luaK_kstr(ls, string_constant(ls->fs, s));
+}
+
+
+static TString *str_checkname (LexState *ls) {
+ TString *ts;
+ check_condition(ls, (ls->t.token == TK_NAME), "<name> expected");
+ ts = ls->t.seminfo.ts;
+ next(ls);
+ return ts;
+}
+
+
+static int checkname (LexState *ls) {
+ return string_constant(ls->fs, str_checkname(ls));
+}
+
+
+static int luaI_registerlocalvar (LexState *ls, TString *varname) {
+ Proto *f = ls->fs->f;
+ luaM_growvector(ls->L, f->locvars, f->nlocvars, 1, LocVar, "", MAX_INT);
+ f->locvars[f->nlocvars].varname = varname;
+ return f->nlocvars++;
+}
+
+
+static void new_localvar (LexState *ls, TString *name, int n) {
+ FuncState *fs = ls->fs;
+ luaX_checklimit(ls, fs->nactloc+n+1, MAXLOCALS, "local variables");
+ fs->actloc[fs->nactloc+n] = luaI_registerlocalvar(ls, name);
+}
+
+
+static void adjustlocalvars (LexState *ls, int nvars) {
+ FuncState *fs = ls->fs;
+ while (nvars--)
+ fs->f->locvars[fs->actloc[fs->nactloc++]].startpc = fs->pc;
+}
+
+
+static void removelocalvars (LexState *ls, int nvars) {
+ FuncState *fs = ls->fs;
+ while (nvars--)
+ fs->f->locvars[fs->actloc[--fs->nactloc]].endpc = fs->pc;
+}
+
+
+static void new_localvarstr (LexState *ls, const char *name, int n) {
+ new_localvar(ls, luaS_newfixed(ls->L, name), n);
+}
+
+
+static int search_local (LexState *ls, TString *n, expdesc *var) {
+ FuncState *fs;
+ int level = 0;
+ for (fs=ls->fs; fs; fs=fs->prev) {
+ int i;
+ for (i=fs->nactloc-1; i >= 0; i--) {
+ if (n == fs->f->locvars[fs->actloc[i]].varname) {
+ var->k = VLOCAL;
+ var->u.index = i;
+ return level;
+ }
+ }
+ level++; /* `var' not found; check outer level */
+ }
+ var->k = VGLOBAL; /* not found in any level; must be global */
+ return -1;
+}
+
+
+static void singlevar (LexState *ls, TString *n, expdesc *var) {
+ int level = search_local(ls, n, var);
+ if (level >= 1) /* neither local (0) nor global (-1)? */
+ luaX_syntaxerror(ls, "cannot access a variable in outer scope", n->str);
+ else if (level == -1) /* global? */
+ var->u.index = string_constant(ls->fs, n);
+}
+
+
+static int indexupvalue (LexState *ls, expdesc *v) {
+ FuncState *fs = ls->fs;
+ int i;
+ for (i=0; i<fs->nupvalues; i++) {
+ if (fs->upvalues[i].k == v->k && fs->upvalues[i].u.index == v->u.index)
+ return i;
+ }
+ /* new one */
+ luaX_checklimit(ls, fs->nupvalues+1, MAXUPVALUES, "upvalues");
+ fs->upvalues[fs->nupvalues] = *v;
+ return fs->nupvalues++;
+}
+
+
+static void pushupvalue (LexState *ls, TString *n) {
+ FuncState *fs = ls->fs;
+ expdesc v;
+ int level = search_local(ls, n, &v);
+ if (level == -1) { /* global? */
+ if (fs->prev == NULL)
+ luaX_syntaxerror(ls, "cannot access upvalue in main", n->str);
+ v.u.index = string_constant(fs->prev, n);
+ }
+ else if (level != 1)
+ luaX_syntaxerror(ls,
+ "upvalue must be global or local to immediately outer scope", n->str);
+ luaK_code1(fs, OP_PUSHUPVALUE, indexupvalue(ls, &v));
+}
+
+
+static void adjust_mult_assign (LexState *ls, int nvars, int nexps) {
+ FuncState *fs = ls->fs;
+ int diff = nexps - nvars;
+ if (nexps > 0 && luaK_lastisopen(fs)) { /* list ends in a function call */
+ diff--; /* do not count function call itself */
+ if (diff <= 0) { /* more variables than values? */
+ luaK_setcallreturns(fs, -diff); /* function call provide extra values */
+ diff = 0; /* no more difference */
+ }
+ else /* more values than variables */
+ luaK_setcallreturns(fs, 0); /* call should provide no value */
+ }
+ /* push or pop eventual difference between list lengths */
+ luaK_adjuststack(fs, diff);
+}
+
+
+static void code_params (LexState *ls, int nparams, int dots) {
+ FuncState *fs = ls->fs;
+ adjustlocalvars(ls, nparams);
+ luaX_checklimit(ls, fs->nactloc, MAXPARAMS, "parameters");
+ fs->f->numparams = fs->nactloc; /* `self' could be there already */
+ fs->f->is_vararg = dots;
+ if (dots) {
+ new_localvarstr(ls, "arg", 0);
+ adjustlocalvars(ls, 1);
+ }
+ luaK_deltastack(fs, fs->nactloc); /* count parameters in the stack */
+}
+
+
+static void enterbreak (FuncState *fs, Breaklabel *bl) {
+ bl->stacklevel = fs->stacklevel;
+ bl->breaklist = NO_JUMP;
+ bl->previous = fs->bl;
+ fs->bl = bl;
+}
+
+
+static void leavebreak (FuncState *fs, Breaklabel *bl) {
+ fs->bl = bl->previous;
+ LUA_ASSERT(bl->stacklevel == fs->stacklevel, "wrong levels");
+ luaK_patchlist(fs, bl->breaklist, luaK_getlabel(fs));
+}
+
+
+static void pushclosure (LexState *ls, FuncState *func) {
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int i;
+ for (i=0; i<func->nupvalues; i++)
+ luaK_tostack(ls, &func->upvalues[i], 1);
+ luaM_growvector(ls->L, f->kproto, f->nkproto, 1, Proto *,
+ "constant table overflow", MAXARG_A);
+ f->kproto[f->nkproto++] = func->f;
+ luaK_code2(fs, OP_CLOSURE, f->nkproto-1, func->nupvalues);
+}
+
+
+static void open_func (LexState *ls, FuncState *fs) {
+ Proto *f = luaF_newproto(ls->L);
+ fs->prev = ls->fs; /* linked list of funcstates */
+ fs->ls = ls;
+ fs->L = ls->L;
+ ls->fs = fs;
+ fs->stacklevel = 0;
+ fs->nactloc = 0;
+ fs->nupvalues = 0;
+ fs->bl = NULL;
+ fs->f = f;
+ f->source = ls->source;
+ fs->pc = 0;
+ fs->lasttarget = 0;
+ fs->lastline = 0;
+ fs->jlt = NO_JUMP;
+ f->code = NULL;
+ f->maxstacksize = 0;
+ f->numparams = 0; /* default for main chunk */
+ f->is_vararg = 0; /* default for main chunk */
+}
+
+
+static void close_func (LexState *ls) {
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ luaK_code0(fs, OP_END);
+ luaK_getlabel(fs); /* close eventual list of pending jumps */
+ luaM_reallocvector(L, f->code, fs->pc, Instruction);
+ luaM_reallocvector(L, f->kstr, f->nkstr, TString *);
+ luaM_reallocvector(L, f->knum, f->nknum, Number);
+ luaM_reallocvector(L, f->kproto, f->nkproto, Proto *);
+ removelocalvars(ls, fs->nactloc);
+ luaM_reallocvector(L, f->locvars, f->nlocvars, LocVar);
+ luaM_reallocvector(L, f->lineinfo, f->nlineinfo+1, int);
+ f->lineinfo[f->nlineinfo++] = MAX_INT; /* end flag */
+ luaF_protook(L, f, fs->pc); /* proto is ok now */
+ ls->fs = fs->prev;
+ LUA_ASSERT(fs->bl == NULL, "wrong list end");
+}
+
+
+Proto *luaY_parser (lua_State *L, ZIO *z) {
+ struct LexState lexstate;
+ struct FuncState funcstate;
+ luaX_setinput(L, &lexstate, z, luaS_new(L, zname(z)));
+ open_func(&lexstate, &funcstate);
+ next(&lexstate); /* read first token */
+ chunk(&lexstate);
+ check_condition(&lexstate, (lexstate.t.token == TK_EOS), "<eof> expected");
+ close_func(&lexstate);
+ LUA_ASSERT(funcstate.prev == NULL, "wrong list end");
+ LUA_ASSERT(funcstate.nupvalues == 0, "no upvalues in main");
+ return funcstate.f;
+}
+
+
+
+/*============================================================*/
+/* GRAMMAR RULES */
+/*============================================================*/
+
+
+static int explist1 (LexState *ls) {
+ /* explist1 -> expr { ',' expr } */
+ int n = 1; /* at least one expression */
+ expdesc v;
+ expr(ls, &v);
+ while (ls->t.token == ',') {
+ luaK_tostack(ls, &v, 1); /* gets only 1 value from previous expression */
+ next(ls); /* skip comma */
+ expr(ls, &v);
+ n++;
+ }
+ luaK_tostack(ls, &v, 0); /* keep open number of values of last expression */
+ return n;
+}
+
+
+static void funcargs (LexState *ls, int slf) {
+ FuncState *fs = ls->fs;
+ int slevel = fs->stacklevel - slf - 1; /* where is func in the stack */
+ switch (ls->t.token) {
+ case '(': { /* funcargs -> '(' [ explist1 ] ')' */
+ int line = ls->linenumber;
+ int nargs = 0;
+ next(ls);
+ if (ls->t.token != ')') /* arg list not empty? */
+ nargs = explist1(ls);
+ check_match(ls, ')', '(', line);
+#ifdef LUA_COMPAT_ARGRET
+ if (nargs > 0) /* arg list is not empty? */
+ luaK_setcallreturns(fs, 1); /* last call returns only 1 value */
+#else
+ UNUSED(nargs); /* to avoid warnings */
+#endif
+ break;
+ }
+ case '{': { /* funcargs -> constructor */
+ constructor(ls);
+ break;
+ }
+ case TK_STRING: { /* funcargs -> STRING */
+ code_string(ls, ls->t.seminfo.ts); /* must use `seminfo' before `next' */
+ next(ls);
+ break;
+ }
+ default: {
+ luaK_error(ls, "function arguments expected");
+ break;
+ }
+ }
+ fs->stacklevel = slevel; /* call will remove function and arguments */
+ luaK_code2(fs, OP_CALL, slevel, MULT_RET);
+}
+
+
+static void var_or_func_tail (LexState *ls, expdesc *v) {
+ for (;;) {
+ switch (ls->t.token) {
+ case '.': { /* var_or_func_tail -> '.' NAME */
+ next(ls);
+ luaK_tostack(ls, v, 1); /* `v' must be on stack */
+ luaK_kstr(ls, checkname(ls));
+ v->k = VINDEXED;
+ break;
+ }
+ case '[': { /* var_or_func_tail -> '[' exp1 ']' */
+ next(ls);
+ luaK_tostack(ls, v, 1); /* `v' must be on stack */
+ v->k = VINDEXED;
+ exp1(ls);
+ check(ls, ']');
+ break;
+ }
+ case ':': { /* var_or_func_tail -> ':' NAME funcargs */
+ int name;
+ next(ls);
+ name = checkname(ls);
+ luaK_tostack(ls, v, 1); /* `v' must be on stack */
+ luaK_code1(ls->fs, OP_PUSHSELF, name);
+ funcargs(ls, 1);
+ v->k = VEXP;
+ v->u.l.t = v->u.l.f = NO_JUMP;
+ break;
+ }
+ case '(': case TK_STRING: case '{': { /* var_or_func_tail -> funcargs */
+ luaK_tostack(ls, v, 1); /* `v' must be on stack */
+ funcargs(ls, 0);
+ v->k = VEXP;
+ v->u.l.t = v->u.l.f = NO_JUMP;
+ break;
+ }
+ default: return; /* should be follow... */
+ }
+ }
+}
+
+
+static void var_or_func (LexState *ls, expdesc *v) {
+ /* var_or_func -> ['%'] NAME var_or_func_tail */
+ if (optional(ls, '%')) { /* upvalue? */
+ pushupvalue(ls, str_checkname(ls));
+ v->k = VEXP;
+ v->u.l.t = v->u.l.f = NO_JUMP;
+ }
+ else /* variable name */
+ singlevar(ls, str_checkname(ls), v);
+ var_or_func_tail(ls, v);
+}
+
+
+
+/*
+** {======================================================================
+** Rules for Constructors
+** =======================================================================
+*/
+
+
+static void recfield (LexState *ls) {
+ /* recfield -> (NAME | '['exp1']') = exp1 */
+ switch (ls->t.token) {
+ case TK_NAME: {
+ luaK_kstr(ls, checkname(ls));
+ break;
+ }
+ case '[': {
+ next(ls);
+ exp1(ls);
+ check(ls, ']');
+ break;
+ }
+ default: luaK_error(ls, "<name> or `[' expected");
+ }
+ check(ls, '=');
+ exp1(ls);
+}
+
+
+static int recfields (LexState *ls) {
+ /* recfields -> recfield { ',' recfield } [','] */
+ FuncState *fs = ls->fs;
+ int n = 1; /* at least one element */
+ recfield(ls);
+ while (ls->t.token == ',') {
+ next(ls);
+ if (ls->t.token == ';' || ls->t.token == '}')
+ break;
+ recfield(ls);
+ n++;
+ if (n%RFIELDS_PER_FLUSH == 0)
+ luaK_code1(fs, OP_SETMAP, RFIELDS_PER_FLUSH);
+ }
+ luaK_code1(fs, OP_SETMAP, n%RFIELDS_PER_FLUSH);
+ return n;
+}
+
+
+static int listfields (LexState *ls) {
+ /* listfields -> exp1 { ',' exp1 } [','] */
+ FuncState *fs = ls->fs;
+ int n = 1; /* at least one element */
+ exp1(ls);
+ while (ls->t.token == ',') {
+ next(ls);
+ if (ls->t.token == ';' || ls->t.token == '}')
+ break;
+ exp1(ls);
+ n++;
+ luaX_checklimit(ls, n/LFIELDS_PER_FLUSH, MAXARG_A,
+ "`item groups' in a list initializer");
+ if (n%LFIELDS_PER_FLUSH == 0)
+ luaK_code2(fs, OP_SETLIST, n/LFIELDS_PER_FLUSH - 1, LFIELDS_PER_FLUSH);
+ }
+ luaK_code2(fs, OP_SETLIST, n/LFIELDS_PER_FLUSH, n%LFIELDS_PER_FLUSH);
+ return n;
+}
+
+
+
+static void constructor_part (LexState *ls, Constdesc *cd) {
+ switch (ls->t.token) {
+ case ';': case '}': { /* constructor_part -> empty */
+ cd->n = 0;
+ cd->k = ls->t.token;
+ break;
+ }
+ case TK_NAME: { /* may be listfields or recfields */
+ lookahead(ls);
+ if (ls->lookahead.token != '=') /* expression? */
+ goto case_default;
+ /* else go through to recfields */
+ }
+ case '[': { /* constructor_part -> recfields */
+ cd->n = recfields(ls);
+ cd->k = 1; /* record */
+ break;
+ }
+ default: { /* constructor_part -> listfields */
+ case_default:
+ cd->n = listfields(ls);
+ cd->k = 0; /* list */
+ break;
+ }
+ }
+}
+
+
+static void constructor (LexState *ls) {
+ /* constructor -> '{' constructor_part [';' constructor_part] '}' */
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ int pc = luaK_code1(fs, OP_CREATETABLE, 0);
+ int nelems;
+ Constdesc cd;
+ check(ls, '{');
+ constructor_part(ls, &cd);
+ nelems = cd.n;
+ if (optional(ls, ';')) {
+ Constdesc other_cd;
+ constructor_part(ls, &other_cd);
+ check_condition(ls, (cd.k != other_cd.k), "invalid constructor syntax");
+ nelems += other_cd.n;
+ }
+ check_match(ls, '}', '{', line);
+ luaX_checklimit(ls, nelems, MAXARG_U, "elements in a table constructor");
+ SETARG_U(fs->f->code[pc], nelems); /* set initial table size */
+}
+
+/* }====================================================================== */
+
+
+
+
+/*
+** {======================================================================
+** Expression parsing
+** =======================================================================
+*/
+
+
+static void simpleexp (LexState *ls, expdesc *v) {
+ FuncState *fs = ls->fs;
+ switch (ls->t.token) {
+ case TK_NUMBER: { /* simpleexp -> NUMBER */
+ Number r = ls->t.seminfo.r;
+ next(ls);
+ luaK_number(fs, r);
+ break;
+ }
+ case TK_STRING: { /* simpleexp -> STRING */
+ code_string(ls, ls->t.seminfo.ts); /* must use `seminfo' before `next' */
+ next(ls);
+ break;
+ }
+ case TK_NIL: { /* simpleexp -> NIL */
+ luaK_adjuststack(fs, -1);
+ next(ls);
+ break;
+ }
+ case '{': { /* simpleexp -> constructor */
+ constructor(ls);
+ break;
+ }
+ case TK_FUNCTION: { /* simpleexp -> FUNCTION body */
+ next(ls);
+ body(ls, 0, ls->linenumber);
+ break;
+ }
+ case '(': { /* simpleexp -> '(' expr ')' */
+ next(ls);
+ expr(ls, v);
+ check(ls, ')');
+ return;
+ }
+ case TK_NAME: case '%': {
+ var_or_func(ls, v);
+ return;
+ }
+ default: {
+ luaK_error(ls, "<expression> expected");
+ return;
+ }
+ }
+ v->k = VEXP;
+ v->u.l.t = v->u.l.f = NO_JUMP;
+}
+
+
+static void exp1 (LexState *ls) {
+ expdesc v;
+ expr(ls, &v);
+ luaK_tostack(ls, &v, 1);
+}
+
+
+static UnOpr getunopr (int op) {
+ switch (op) {
+ case TK_NOT: return OPR_NOT;
+ case '-': return OPR_MINUS;
+ default: return OPR_NOUNOPR;
+ }
+}
+
+
+static BinOpr getbinopr (int op) {
+ switch (op) {
+ case '+': return OPR_ADD;
+ case '-': return OPR_SUB;
+ case '*': return OPR_MULT;
+ case '/': return OPR_DIV;
+ case '^': return OPR_POW;
+ case TK_CONCAT: return OPR_CONCAT;
+ case TK_NE: return OPR_NE;
+ case TK_EQ: return OPR_EQ;
+ case '<': return OPR_LT;
+ case TK_LE: return OPR_LE;
+ case '>': return OPR_GT;
+ case TK_GE: return OPR_GE;
+ case TK_AND: return OPR_AND;
+ case TK_OR: return OPR_OR;
+ default: return OPR_NOBINOPR;
+ }
+}
+
+
+static const struct {
+ char left; /* left priority for each binary operator */
+ char right; /* right priority */
+} priority[] = { /* ORDER OPR */
+ {5, 5}, {5, 5}, {6, 6}, {6, 6}, /* arithmetic */
+ {9, 8}, {4, 3}, /* power and concat (right associative) */
+ {2, 2}, {2, 2}, /* equality */
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}, /* order */
+ {1, 1}, {1, 1} /* logical */
+};
+
+#define UNARY_PRIORITY 7 /* priority for unary operators */
+
+
+/*
+** subexpr -> (simplexep | unop subexpr) { binop subexpr }
+** where `binop' is any binary operator with a priority higher than `limit'
+*/
+static BinOpr subexpr (LexState *ls, expdesc *v, int limit) {
+ BinOpr op;
+ UnOpr uop = getunopr(ls->t.token);
+ if (uop != OPR_NOUNOPR) {
+ next(ls);
+ subexpr(ls, v, UNARY_PRIORITY);
+ luaK_prefix(ls, uop, v);
+ }
+ else simpleexp(ls, v);
+ /* expand while operators have priorities higher than `limit' */
+ op = getbinopr(ls->t.token);
+ while (op != OPR_NOBINOPR && priority[op].left > limit) {
+ expdesc v2;
+ BinOpr nextop;
+ next(ls);
+ luaK_infix(ls, op, v);
+ /* read sub-expression with higher priority */
+ nextop = subexpr(ls, &v2, priority[op].right);
+ luaK_posfix(ls, op, v, &v2);
+ op = nextop;
+ }
+ return op; /* return first untreated operator */
+}
+
+
+static void expr (LexState *ls, expdesc *v) {
+ subexpr(ls, v, -1);
+}
+
+/* }==================================================================== */
+
+
+/*
+** {======================================================================
+** Rules for Statements
+** =======================================================================
+*/
+
+
+static int block_follow (int token) {
+ switch (token) {
+ case TK_ELSE: case TK_ELSEIF: case TK_END:
+ case TK_UNTIL: case TK_EOS:
+ return 1;
+ default: return 0;
+ }
+}
+
+
+static void block (LexState *ls) {
+ /* block -> chunk */
+ FuncState *fs = ls->fs;
+ int nactloc = fs->nactloc;
+ chunk(ls);
+ luaK_adjuststack(fs, fs->nactloc - nactloc); /* remove local variables */
+ removelocalvars(ls, fs->nactloc - nactloc);
+}
+
+
+static int assignment (LexState *ls, expdesc *v, int nvars) {
+ int left = 0; /* number of values left in the stack after assignment */
+ luaX_checklimit(ls, nvars, MAXVARSLH, "variables in a multiple assignment");
+ if (ls->t.token == ',') { /* assignment -> ',' NAME assignment */
+ expdesc nv;
+ next(ls);
+ var_or_func(ls, &nv);
+ check_condition(ls, (nv.k != VEXP), "syntax error");
+ left = assignment(ls, &nv, nvars+1);
+ }
+ else { /* assignment -> '=' explist1 */
+ int nexps;
+ check(ls, '=');
+ nexps = explist1(ls);
+ adjust_mult_assign(ls, nvars, nexps);
+ }
+ if (v->k != VINDEXED)
+ luaK_storevar(ls, v);
+ else { /* there may be garbage between table-index and value */
+ luaK_code2(ls->fs, OP_SETTABLE, left+nvars+2, 1);
+ left += 2;
+ }
+ return left;
+}
+
+
+static void cond (LexState *ls, expdesc *v) {
+ /* cond -> exp */
+ expr(ls, v); /* read condition */
+ luaK_goiftrue(ls->fs, v, 0);
+}
+
+
+static void whilestat (LexState *ls, int line) {
+ /* whilestat -> WHILE cond DO block END */
+ FuncState *fs = ls->fs;
+ int while_init = luaK_getlabel(fs);
+ expdesc v;
+ Breaklabel bl;
+ enterbreak(fs, &bl);
+ next(ls);
+ cond(ls, &v);
+ check(ls, TK_DO);
+ block(ls);
+ luaK_patchlist(fs, luaK_jump(fs), while_init);
+ luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs));
+ check_match(ls, TK_END, TK_WHILE, line);
+ leavebreak(fs, &bl);
+}
+
+
+static void repeatstat (LexState *ls, int line) {
+ /* repeatstat -> REPEAT block UNTIL cond */
+ FuncState *fs = ls->fs;
+ int repeat_init = luaK_getlabel(fs);
+ expdesc v;
+ Breaklabel bl;
+ enterbreak(fs, &bl);
+ next(ls);
+ block(ls);
+ check_match(ls, TK_UNTIL, TK_REPEAT, line);
+ cond(ls, &v);
+ luaK_patchlist(fs, v.u.l.f, repeat_init);
+ leavebreak(fs, &bl);
+}
+
+
+static void forbody (LexState *ls, int nvar, OpCode prepfor, OpCode loopfor) {
+ /* forbody -> DO block END */
+ FuncState *fs = ls->fs;
+ int prep = luaK_code1(fs, prepfor, NO_JUMP);
+ int blockinit = luaK_getlabel(fs);
+ check(ls, TK_DO);
+ adjustlocalvars(ls, nvar); /* scope for control variables */
+ block(ls);
+ luaK_patchlist(fs, luaK_code1(fs, loopfor, NO_JUMP), blockinit);
+ luaK_patchlist(fs, prep, luaK_getlabel(fs));
+ removelocalvars(ls, nvar);
+}
+
+
+static void fornum (LexState *ls, TString *varname) {
+ /* fornum -> NAME = exp1,exp1[,exp1] forbody */
+ FuncState *fs = ls->fs;
+ check(ls, '=');
+ exp1(ls); /* initial value */
+ check(ls, ',');
+ exp1(ls); /* limit */
+ if (optional(ls, ','))
+ exp1(ls); /* optional step */
+ else
+ luaK_code1(fs, OP_PUSHINT, 1); /* default step */
+ new_localvar(ls, varname, 0);
+ new_localvarstr(ls, "(limit)", 1);
+ new_localvarstr(ls, "(step)", 2);
+ forbody(ls, 3, OP_FORPREP, OP_FORLOOP);
+}
+
+
+static void forlist (LexState *ls, TString *indexname) {
+ /* forlist -> NAME,NAME IN exp1 forbody */
+ TString *valname;
+ check(ls, ',');
+ valname = str_checkname(ls);
+ /* next test is dirty, but avoids `in' being a reserved word */
+ check_condition(ls,
+ (ls->t.token == TK_NAME && ls->t.seminfo.ts == luaS_new(ls->L, "in")),
+ "`in' expected");
+ next(ls); /* skip `in' */
+ exp1(ls); /* table */
+ new_localvarstr(ls, "(table)", 0);
+ new_localvar(ls, indexname, 1);
+ new_localvar(ls, valname, 2);
+ forbody(ls, 3, OP_LFORPREP, OP_LFORLOOP);
+}
+
+
+static void forstat (LexState *ls, int line) {
+ /* forstat -> fornum | forlist */
+ FuncState *fs = ls->fs;
+ TString *varname;
+ Breaklabel bl;
+ enterbreak(fs, &bl);
+ next(ls); /* skip `for' */
+ varname = str_checkname(ls); /* first variable name */
+ switch (ls->t.token) {
+ case '=': fornum(ls, varname); break;
+ case ',': forlist(ls, varname); break;
+ default: luaK_error(ls, "`=' or `,' expected");
+ }
+ check_match(ls, TK_END, TK_FOR, line);
+ leavebreak(fs, &bl);
+}
+
+
+static void test_then_block (LexState *ls, expdesc *v) {
+ /* test_then_block -> [IF | ELSEIF] cond THEN block */
+ next(ls); /* skip IF or ELSEIF */
+ cond(ls, v);
+ check(ls, TK_THEN);
+ block(ls); /* `then' part */
+}
+
+
+static void ifstat (LexState *ls, int line) {
+ /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
+ FuncState *fs = ls->fs;
+ expdesc v;
+ int escapelist = NO_JUMP;
+ test_then_block(ls, &v); /* IF cond THEN block */
+ while (ls->t.token == TK_ELSEIF) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs));
+ test_then_block(ls, &v); /* ELSEIF cond THEN block */
+ }
+ if (ls->t.token == TK_ELSE) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs));
+ next(ls); /* skip ELSE */
+ block(ls); /* `else' part */
+ }
+ else
+ luaK_concat(fs, &escapelist, v.u.l.f);
+ luaK_patchlist(fs, escapelist, luaK_getlabel(fs));
+ check_match(ls, TK_END, TK_IF, line);
+}
+
+
+static void localstat (LexState *ls) {
+ /* stat -> LOCAL NAME {',' NAME} ['=' explist1] */
+ int nvars = 0;
+ int nexps;
+ do {
+ next(ls); /* skip LOCAL or ',' */
+ new_localvar(ls, str_checkname(ls), nvars++);
+ } while (ls->t.token == ',');
+ if (optional(ls, '='))
+ nexps = explist1(ls);
+ else
+ nexps = 0;
+ adjust_mult_assign(ls, nvars, nexps);
+ adjustlocalvars(ls, nvars);
+}
+
+
+static int funcname (LexState *ls, expdesc *v) {
+ /* funcname -> NAME [':' NAME | '.' NAME] */
+ int needself = 0;
+ singlevar(ls, str_checkname(ls), v);
+ if (ls->t.token == ':' || ls->t.token == '.') {
+ needself = (ls->t.token == ':');
+ next(ls);
+ luaK_tostack(ls, v, 1);
+ luaK_kstr(ls, checkname(ls));
+ v->k = VINDEXED;
+ }
+ return needself;
+}
+
+
+static void funcstat (LexState *ls, int line) {
+ /* funcstat -> FUNCTION funcname body */
+ int needself;
+ expdesc v;
+ next(ls); /* skip FUNCTION */
+ needself = funcname(ls, &v);
+ body(ls, needself, line);
+ luaK_storevar(ls, &v);
+}
+
+
+static void namestat (LexState *ls) {
+ /* stat -> func | ['%'] NAME assignment */
+ FuncState *fs = ls->fs;
+ expdesc v;
+ var_or_func(ls, &v);
+ if (v.k == VEXP) { /* stat -> func */
+ check_condition(ls, luaK_lastisopen(fs), "syntax error"); /* an upvalue? */
+ luaK_setcallreturns(fs, 0); /* call statement uses no results */
+ }
+ else { /* stat -> ['%'] NAME assignment */
+ int left = assignment(ls, &v, 1);
+ luaK_adjuststack(fs, left); /* remove eventual garbage left on stack */
+ }
+}
+
+
+static void retstat (LexState *ls) {
+ /* stat -> RETURN explist */
+ FuncState *fs = ls->fs;
+ next(ls); /* skip RETURN */
+ if (!block_follow(ls->t.token))
+ explist1(ls); /* optional return values */
+ luaK_code1(fs, OP_RETURN, ls->fs->nactloc);
+ fs->stacklevel = fs->nactloc; /* removes all temp values */
+}
+
+
+static void breakstat (LexState *ls) {
+ /* stat -> BREAK [NAME] */
+ FuncState *fs = ls->fs;
+ int currentlevel = fs->stacklevel;
+ Breaklabel *bl = fs->bl;
+ if (!bl)
+ luaK_error(ls, "no loop to break");
+ next(ls); /* skip BREAK */
+ luaK_adjuststack(fs, currentlevel - bl->stacklevel);
+ luaK_concat(fs, &bl->breaklist, luaK_jump(fs));
+ /* correct stack for compiler and symbolic execution */
+ luaK_adjuststack(fs, bl->stacklevel - currentlevel);
+}
+
+
+static int stat (LexState *ls) {
+ int line = ls->linenumber; /* may be needed for error messages */
+ switch (ls->t.token) {
+ case TK_IF: { /* stat -> ifstat */
+ ifstat(ls, line);
+ return 0;
+ }
+ case TK_WHILE: { /* stat -> whilestat */
+ whilestat(ls, line);
+ return 0;
+ }
+ case TK_DO: { /* stat -> DO block END */
+ next(ls); /* skip DO */
+ block(ls);
+ check_match(ls, TK_END, TK_DO, line);
+ return 0;
+ }
+ case TK_FOR: { /* stat -> forstat */
+ forstat(ls, line);
+ return 0;
+ }
+ case TK_REPEAT: { /* stat -> repeatstat */
+ repeatstat(ls, line);
+ return 0;
+ }
+ case TK_FUNCTION: { /* stat -> funcstat */
+ funcstat(ls, line);
+ return 0;
+ }
+ case TK_LOCAL: { /* stat -> localstat */
+ localstat(ls);
+ return 0;
+ }
+ case TK_NAME: case '%': { /* stat -> namestat */
+ namestat(ls);
+ return 0;
+ }
+ case TK_RETURN: { /* stat -> retstat */
+ retstat(ls);
+ return 1; /* must be last statement */
+ }
+ case TK_BREAK: { /* stat -> breakstat */
+ breakstat(ls);
+ return 1; /* must be last statement */
+ }
+ default: {
+ luaK_error(ls, "<statement> expected");
+ return 0; /* to avoid warnings */
+ }
+ }
+}
+
+
+static void parlist (LexState *ls) {
+ /* parlist -> [ param { ',' param } ] */
+ int nparams = 0;
+ int dots = 0;
+ if (ls->t.token != ')') { /* is `parlist' not empty? */
+ do {
+ switch (ls->t.token) {
+ case TK_DOTS: next(ls); dots = 1; break;
+ case TK_NAME: new_localvar(ls, str_checkname(ls), nparams++); break;
+ default: luaK_error(ls, "<name> or `...' expected");
+ }
+ } while (!dots && optional(ls, ','));
+ }
+ code_params(ls, nparams, dots);
+}
+
+
+static void body (LexState *ls, int needself, int line) {
+ /* body -> '(' parlist ')' chunk END */
+ FuncState new_fs;
+ open_func(ls, &new_fs);
+ new_fs.f->lineDefined = line;
+ check(ls, '(');
+ if (needself) {
+ new_localvarstr(ls, "self", 0);
+ adjustlocalvars(ls, 1);
+ }
+ parlist(ls);
+ check(ls, ')');
+ chunk(ls);
+ check_match(ls, TK_END, TK_FUNCTION, line);
+ close_func(ls);
+ pushclosure(ls, &new_fs);
+}
+
+
+/* }====================================================================== */
+
+
+static void chunk (LexState *ls) {
+ /* chunk -> { stat [';'] } */
+ int islast = 0;
+ while (!islast && !block_follow(ls->t.token)) {
+ islast = stat(ls);
+ optional(ls, ';');
+ LUA_ASSERT(ls->fs->stacklevel == ls->fs->nactloc,
+ "stack size != # local vars");
+ }
+}
+
diff --git a/src/lua/lparser.h b/src/lua/lparser.h
new file mode 100644
index 00000000..d83fb5f1
--- /dev/null
+++ b/src/lua/lparser.h
@@ -0,0 +1,60 @@
+/*
+** $Id: lparser.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** LL(1) Parser and code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lparser_h
+#define lparser_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+
+/*
+** Expression descriptor
+*/
+
+typedef enum {
+ VGLOBAL,
+ VLOCAL,
+ VINDEXED,
+ VEXP
+} expkind;
+
+typedef struct expdesc {
+ expkind k;
+ union {
+ int index; /* VGLOBAL: `kstr' index of global name; VLOCAL: stack index */
+ struct {
+ int t; /* patch list of `exit when true' */
+ int f; /* patch list of `exit when false' */
+ } l;
+ } u;
+} expdesc;
+
+
+
+/* state needed to generate code for a given function */
+typedef struct FuncState {
+ Proto *f; /* current function header */
+ struct FuncState *prev; /* enclosing function */
+ struct LexState *ls; /* lexical state */
+ struct lua_State *L; /* copy of the Lua state */
+ int pc; /* next position to code */
+ int lasttarget; /* `pc' of last `jump target' */
+ int jlt; /* list of jumps to `lasttarget' */
+ short stacklevel; /* number of values on activation register */
+ short nactloc; /* number of active local variables */
+ short nupvalues; /* number of upvalues */
+ int lastline; /* line where last `lineinfo' was generated */
+ struct Breaklabel *bl; /* chain of breakable blocks */
+ expdesc upvalues[MAXUPVALUES]; /* upvalues */
+ int actloc[MAXLOCALS]; /* local-variable stack (indices to locvars) */
+} FuncState;
+
+
+Proto *luaY_parser (lua_State *L, ZIO *z);
+
+
+#endif
diff --git a/src/lua/lstate.c b/src/lua/lstate.c
new file mode 100644
index 00000000..6310cb7e
--- /dev/null
+++ b/src/lua/lstate.c
@@ -0,0 +1,121 @@
+/*
+** $Id: lstate.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+#ifdef LUA_DEBUG
+static lua_State *lua_state = NULL;
+void luaB_opentests (lua_State *L);
+#endif
+
+
+/*
+** built-in implementation for ERRORMESSAGE. In a "correct" environment
+** ERRORMESSAGE should have an external definition, and so this function
+** would not be used.
+*/
+static int errormessage (lua_State *L) {
+ const char *s = lua_tostring(L, 1);
+ if (s == NULL) s = "(no message)";
+ fprintf(stderr, "error: %s\n", s);
+ return 0;
+}
+
+
+/*
+** open parts that may cause memory-allocation errors
+*/
+static void f_luaopen (lua_State *L, void *ud) {
+ int stacksize = *(int *)ud;
+ if (stacksize == 0)
+ stacksize = DEFAULT_STACK_SIZE;
+ else
+ stacksize += LUA_MINSTACK;
+ L->gt = luaH_new(L, 10); /* table of globals */
+ luaD_init(L, stacksize);
+ luaS_init(L);
+ luaX_init(L);
+ luaT_init(L);
+ lua_newtable(L);
+ lua_ref(L, 1); /* create registry */
+ lua_register(L, LUA_ERRORMESSAGE, errormessage);
+#ifdef LUA_DEBUG
+ luaB_opentests(L);
+ if (lua_state == NULL) lua_state = L; /* keep first state to be opened */
+#endif
+ LUA_ASSERT(lua_gettop(L) == 0, "wrong API stack");
+}
+
+
+LUA_API lua_State *lua_open (int stacksize) {
+ lua_State *L = luaM_new(NULL, lua_State);
+ if (L == NULL) return NULL; /* memory allocation error */
+ L->stack = NULL;
+ L->strt.size = L->udt.size = 0;
+ L->strt.nuse = L->udt.nuse = 0;
+ L->strt.hash = NULL;
+ L->udt.hash = NULL;
+ L->Mbuffer = NULL;
+ L->Mbuffsize = 0;
+ L->rootproto = NULL;
+ L->rootcl = NULL;
+ L->roottable = NULL;
+ L->TMtable = NULL;
+ L->last_tag = -1;
+ L->refArray = NULL;
+ L->refSize = 0;
+ L->refFree = NONEXT;
+ L->nblocks = sizeof(lua_State);
+ L->GCthreshold = MAX_INT; /* to avoid GC during pre-definitions */
+ L->callhook = NULL;
+ L->linehook = NULL;
+ L->allowhooks = 1;
+ L->errorJmp = NULL;
+ if (luaD_runprotected(L, f_luaopen, &stacksize) != 0) {
+ /* memory allocation error: free partial state */
+ lua_close(L);
+ return NULL;
+ }
+ L->GCthreshold = 2*L->nblocks;
+ return L;
+}
+
+
+LUA_API void lua_close (lua_State *L) {
+ LUA_ASSERT(L != lua_state || lua_gettop(L) == 0, "garbage in C stack");
+ luaC_collect(L, 1); /* collect all elements */
+ LUA_ASSERT(L->rootproto == NULL, "list should be empty");
+ LUA_ASSERT(L->rootcl == NULL, "list should be empty");
+ LUA_ASSERT(L->roottable == NULL, "list should be empty");
+ luaS_freeall(L);
+ if (L->stack)
+ L->nblocks -= (L->stack_last - L->stack + 1)*sizeof(TObject);
+ luaM_free(L, L->stack);
+ L->nblocks -= (L->last_tag+1)*sizeof(struct TM);
+ luaM_free(L, L->TMtable);
+ L->nblocks -= (L->refSize)*sizeof(struct Ref);
+ luaM_free(L, L->refArray);
+ L->nblocks -= (L->Mbuffsize)*sizeof(char);
+ luaM_free(L, L->Mbuffer);
+ LUA_ASSERT(L->nblocks == sizeof(lua_State), "wrong count for nblocks");
+ luaM_free(L, L);
+ LUA_ASSERT(L != lua_state || memdebug_numblocks == 0, "memory leak!");
+ LUA_ASSERT(L != lua_state || memdebug_total == 0,"memory leak!");
+}
+
diff --git a/src/lua/lstate.h b/src/lua/lstate.h
new file mode 100644
index 00000000..ee02db01
--- /dev/null
+++ b/src/lua/lstate.h
@@ -0,0 +1,77 @@
+/*
+** $Id: lstate.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstate_h
+#define lstate_h
+
+#include "lobject.h"
+#include "lua.h"
+#include "luadebug.h"
+
+
+
+typedef TObject *StkId; /* index to stack elements */
+
+
+/*
+** marks for Reference array
+*/
+#define NONEXT -1 /* to end the free list */
+#define HOLD -2
+#define COLLECTED -3
+#define LOCK -4
+
+
+struct Ref {
+ TObject o;
+ int st; /* can be LOCK, HOLD, COLLECTED, or next (for free list) */
+};
+
+
+struct lua_longjmp; /* defined in ldo.c */
+struct TM; /* defined in ltm.h */
+
+
+typedef struct stringtable {
+ int size;
+ lint32 nuse; /* number of elements */
+ TString **hash;
+} stringtable;
+
+
+
+struct lua_State {
+ /* thread-specific state */
+ StkId top; /* first free slot in the stack */
+ StkId stack; /* stack base */
+ StkId stack_last; /* last free slot in the stack */
+ int stacksize;
+ StkId Cbase; /* base for current C function */
+ struct lua_longjmp *errorJmp; /* current error recover point */
+ char *Mbuffer; /* global buffer */
+ size_t Mbuffsize; /* size of Mbuffer */
+ /* global state */
+ Proto *rootproto; /* list of all prototypes */
+ Closure *rootcl; /* list of all closures */
+ Hash *roottable; /* list of all tables */
+ stringtable strt; /* hash table for strings */
+ stringtable udt; /* hash table for udata */
+ Hash *gt; /* table for globals */
+ struct TM *TMtable; /* table for tag methods */
+ int last_tag; /* last used tag in TMtable */
+ struct Ref *refArray; /* locked objects */
+ int refSize; /* size of refArray */
+ int refFree; /* list of free positions in refArray */
+ unsigned long GCthreshold;
+ unsigned long nblocks; /* number of `bytes' currently allocated */
+ lua_Hook callhook;
+ lua_Hook linehook;
+ int allowhooks;
+};
+
+
+#endif
+
diff --git a/src/lua/lstring.c b/src/lua/lstring.c
new file mode 100644
index 00000000..7293e195
--- /dev/null
+++ b/src/lua/lstring.c
@@ -0,0 +1,155 @@
+/*
+** $Id: lstring.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** String table (keeps all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+
+
+/*
+** type equivalent to TString, but with maximum alignment requirements
+*/
+union L_UTString {
+ TString ts;
+ union L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
+};
+
+
+
+void luaS_init (lua_State *L) {
+ L->strt.hash = luaM_newvector(L, 1, TString *);
+ L->udt.hash = luaM_newvector(L, 1, TString *);
+ L->nblocks += 2*sizeof(TString *);
+ L->strt.size = L->udt.size = 1;
+ L->strt.nuse = L->udt.nuse = 0;
+ L->strt.hash[0] = L->udt.hash[0] = NULL;
+}
+
+
+void luaS_freeall (lua_State *L) {
+ LUA_ASSERT(L->strt.nuse==0, "non-empty string table");
+ L->nblocks -= (L->strt.size + L->udt.size)*sizeof(TString *);
+ luaM_free(L, L->strt.hash);
+ LUA_ASSERT(L->udt.nuse==0, "non-empty udata table");
+ luaM_free(L, L->udt.hash);
+}
+
+
+static unsigned long hash_s (const char *s, size_t l) {
+ unsigned long h = l; /* seed */
+ size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */
+ for (; l>=step; l-=step)
+ h = h ^ ((h<<5)+(h>>2)+(unsigned char)*(s++));
+ return h;
+}
+
+
+void luaS_resize (lua_State *L, stringtable *tb, int newsize) {
+ TString **newhash = luaM_newvector(L, newsize, TString *);
+ int i;
+ for (i=0; i<newsize; i++) newhash[i] = NULL;
+ /* rehash */
+ for (i=0; i<tb->size; i++) {
+ TString *p = tb->hash[i];
+ while (p) { /* for each node in the list */
+ TString *next = p->nexthash; /* save next */
+ unsigned long h = (tb == &L->strt) ? p->u.s.hash : IntPoint(p->u.d.value);
+ int h1 = h&(newsize-1); /* new position */
+ LUA_ASSERT(h%newsize == (h&(newsize-1)),
+ "a&(x-1) == a%x, for x power of 2");
+ p->nexthash = newhash[h1]; /* chain it in new position */
+ newhash[h1] = p;
+ p = next;
+ }
+ }
+ luaM_free(L, tb->hash);
+ L->nblocks += (newsize - tb->size)*sizeof(TString *);
+ tb->size = newsize;
+ tb->hash = newhash;
+}
+
+
+static void newentry (lua_State *L, stringtable *tb, TString *ts, int h) {
+ ts->nexthash = tb->hash[h]; /* chain new entry */
+ tb->hash[h] = ts;
+ tb->nuse++;
+ if (tb->nuse > (lint32)tb->size && tb->size < MAX_INT/2) /* too crowded? */
+ luaS_resize(L, tb, tb->size*2);
+}
+
+
+
+TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
+ unsigned long h = hash_s(str, l);
+ int h1 = h & (L->strt.size-1);
+ TString *ts;
+ for (ts = L->strt.hash[h1]; ts; ts = ts->nexthash) {
+ if (ts->len == l && (memcmp(str, ts->str, l) == 0))
+ return ts;
+ }
+ /* not found */
+ ts = (TString *)luaM_malloc(L, sizestring(l));
+ ts->marked = 0;
+ ts->nexthash = NULL;
+ ts->len = l;
+ ts->u.s.hash = h;
+ ts->u.s.constindex = 0;
+ memcpy(ts->str, str, l);
+ ts->str[l] = 0; /* ending 0 */
+ L->nblocks += sizestring(l);
+ newentry(L, &L->strt, ts, h1); /* insert it on table */
+ return ts;
+}
+
+
+TString *luaS_newudata (lua_State *L, size_t s, void *udata) {
+ union L_UTString *uts = (union L_UTString *)luaM_malloc(L,
+ (lint32)sizeof(union L_UTString)+s);
+ TString *ts = &uts->ts;
+ ts->marked = 0;
+ ts->nexthash = NULL;
+ ts->len = s;
+ ts->u.d.tag = 0;
+ ts->u.d.value = (udata == NULL) ? uts+1 : udata;
+ L->nblocks += sizestring(s);
+ /* insert it on table */
+ newentry(L, &L->udt, ts, IntPoint(ts->u.d.value) & (L->udt.size-1));
+ return ts;
+}
+
+
+TString *luaS_createudata (lua_State *L, void *udata, int tag) {
+ int h1 = IntPoint(udata) & (L->udt.size-1);
+ TString *ts;
+ for (ts = L->udt.hash[h1]; ts; ts = ts->nexthash) {
+ if (udata == ts->u.d.value && (tag == ts->u.d.tag || tag == LUA_ANYTAG))
+ return ts;
+ }
+ /* not found */
+ ts = luaS_newudata(L, 0, udata);
+ if (tag != LUA_ANYTAG)
+ ts->u.d.tag = tag;
+ return ts;
+}
+
+
+TString *luaS_new (lua_State *L, const char *str) {
+ return luaS_newlstr(L, str, strlen(str));
+}
+
+
+TString *luaS_newfixed (lua_State *L, const char *str) {
+ TString *ts = luaS_new(L, str);
+ if (ts->marked == 0) ts->marked = FIXMARK; /* avoid GC */
+ return ts;
+}
+
diff --git a/src/lua/lstring.h b/src/lua/lstring.h
new file mode 100644
index 00000000..f23159ec
--- /dev/null
+++ b/src/lua/lstring.h
@@ -0,0 +1,37 @@
+/*
+** $Id: lstring.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** String table (keep all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstring_h
+#define lstring_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+
+/*
+** any TString with mark>=FIXMARK is never collected.
+** Marks>=RESERVEDMARK are used to identify reserved words.
+*/
+#define FIXMARK 2
+#define RESERVEDMARK 3
+
+
+#define sizestring(l) ((long)sizeof(TString) + \
+ ((long)(l+1)-TSPACK)*(long)sizeof(char))
+
+
+void luaS_init (lua_State *L);
+void luaS_resize (lua_State *L, stringtable *tb, int newsize);
+TString *luaS_newudata (lua_State *L, size_t s, void *udata);
+TString *luaS_createudata (lua_State *L, void *udata, int tag);
+void luaS_freeall (lua_State *L);
+TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+TString *luaS_new (lua_State *L, const char *str);
+TString *luaS_newfixed (lua_State *L, const char *str);
+
+
+#endif
diff --git a/src/lua/lstrlib.c b/src/lua/lstrlib.c
new file mode 100644
index 00000000..051eccf7
--- /dev/null
+++ b/src/lua/lstrlib.c
@@ -0,0 +1,621 @@
+/*
+** $Id: lstrlib.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Standard library for string operations and pattern-matching
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+static int str_len (lua_State *L) {
+ size_t l;
+ luaL_check_lstr(L, 1, &l);
+ lua_pushnumber(L, l);
+ return 1;
+}
+
+
+static long posrelat (long pos, size_t len) {
+ /* relative string position: negative means back from end */
+ return (pos>=0) ? pos : (long)len+pos+1;
+}
+
+
+static int str_sub (lua_State *L) {
+ size_t l;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ long start = posrelat(luaL_check_long(L, 2), l);
+ long end = posrelat(luaL_opt_long(L, 3, -1), l);
+ if (start < 1) start = 1;
+ if (end > (long)l) end = l;
+ if (start <= end)
+ lua_pushlstring(L, s+start-1, end-start+1);
+ else lua_pushstring(L, "");
+ return 1;
+}
+
+
+static int str_lower (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ luaL_buffinit(L, &b);
+ for (i=0; i<l; i++)
+ luaL_putchar(&b, tolower((unsigned char)(s[i])));
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_upper (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ luaL_buffinit(L, &b);
+ for (i=0; i<l; i++)
+ luaL_putchar(&b, toupper((unsigned char)(s[i])));
+ luaL_pushresult(&b);
+ return 1;
+}
+
+static int str_rep (lua_State *L) {
+ size_t l;
+ luaL_Buffer b;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ int n = luaL_check_int(L, 2);
+ luaL_buffinit(L, &b);
+ while (n-- > 0)
+ luaL_addlstring(&b, s, l);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_byte (lua_State *L) {
+ size_t l;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ long pos = posrelat(luaL_opt_long(L, 2, 1), l);
+ luaL_arg_check(L, 0<pos && (size_t)pos<=l, 2, "out of range");
+ lua_pushnumber(L, (unsigned char)s[pos-1]);
+ return 1;
+}
+
+
+static int str_char (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (i=1; i<=n; i++) {
+ int c = luaL_check_int(L, i);
+ luaL_arg_check(L, (unsigned char)c == c, i, "invalid value");
+ luaL_putchar(&b, (unsigned char)c);
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+
+/*
+** {======================================================
+** PATTERN MATCHING
+** =======================================================
+*/
+
+#ifndef MAX_CAPTURES
+#define MAX_CAPTURES 32 /* arbitrary limit */
+#endif
+
+
+struct Capture {
+ const char *src_end; /* end ('\0') of source string */
+ int level; /* total number of captures (finished or unfinished) */
+ struct {
+ const char *init;
+ long len; /* -1 signals unfinished capture */
+ } capture[MAX_CAPTURES];
+};
+
+
+#define ESC '%'
+#define SPECIALS "^$*+?.([%-"
+
+
+static int check_capture (lua_State *L, int l, struct Capture *cap) {
+ l -= '1';
+ if (!(0 <= l && l < cap->level && cap->capture[l].len != -1))
+ lua_error(L, "invalid capture index");
+ return l;
+}
+
+
+static int capture_to_close (lua_State *L, struct Capture *cap) {
+ int level = cap->level;
+ for (level--; level>=0; level--)
+ if (cap->capture[level].len == -1) return level;
+ lua_error(L, "invalid pattern capture");
+ return 0; /* to avoid warnings */
+}
+
+
+const char *luaI_classend (lua_State *L, const char *p) {
+ switch (*p++) {
+ case ESC:
+ if (*p == '\0') lua_error(L, "malformed pattern (ends with `%')");
+ return p+1;
+ case '[':
+ if (*p == '^') p++;
+ do { /* look for a ']' */
+ if (*p == '\0') lua_error(L, "malformed pattern (missing `]')");
+ if (*(p++) == ESC && *p != '\0') p++; /* skip escapes (e.g. '%]') */
+ } while (*p != ']');
+ return p+1;
+ default:
+ return p;
+ }
+}
+
+
+static int match_class (int c, int cl) {
+ int res;
+ switch (tolower(cl)) {
+ case 'a' : res = isalpha(c); break;
+ case 'c' : res = iscntrl(c); break;
+ case 'd' : res = isdigit(c); break;
+ case 'l' : res = islower(c); break;
+ case 'p' : res = ispunct(c); break;
+ case 's' : res = isspace(c); break;
+ case 'u' : res = isupper(c); break;
+ case 'w' : res = isalnum(c); break;
+ case 'x' : res = isxdigit(c); break;
+ case 'z' : res = (c == '\0'); break;
+ default: return (cl == c);
+ }
+ return (islower(cl) ? res : !res);
+}
+
+
+
+static int matchbracketclass (int c, const char *p, const char *endclass) {
+ int sig = 1;
+ if (*(p+1) == '^') {
+ sig = 0;
+ p++; /* skip the '^' */
+ }
+ while (++p < endclass) {
+ if (*p == ESC) {
+ p++;
+ if (match_class(c, (unsigned char)*p))
+ return sig;
+ }
+ else if ((*(p+1) == '-') && (p+2 < endclass)) {
+ p+=2;
+ if ((int)(unsigned char)*(p-2) <= c && c <= (int)(unsigned char)*p)
+ return sig;
+ }
+ else if ((int)(unsigned char)*p == c) return sig;
+ }
+ return !sig;
+}
+
+
+
+int luaI_singlematch (int c, const char *p, const char *ep) {
+ switch (*p) {
+ case '.': /* matches any char */
+ return 1;
+ case ESC:
+ return match_class(c, (unsigned char)*(p+1));
+ case '[':
+ return matchbracketclass(c, p, ep-1);
+ default:
+ return ((unsigned char)*p == c);
+ }
+}
+
+
+static const char *match (lua_State *L, const char *s, const char *p,
+ struct Capture *cap);
+
+
+static const char *matchbalance (lua_State *L, const char *s, const char *p,
+ struct Capture *cap) {
+ if (*p == 0 || *(p+1) == 0)
+ lua_error(L, "unbalanced pattern");
+ if (*s != *p) return NULL;
+ else {
+ int b = *p;
+ int e = *(p+1);
+ int cont = 1;
+ while (++s < cap->src_end) {
+ if (*s == e) {
+ if (--cont == 0) return s+1;
+ }
+ else if (*s == b) cont++;
+ }
+ }
+ return NULL; /* string ends out of balance */
+}
+
+
+static const char *max_expand (lua_State *L, const char *s, const char *p,
+ const char *ep, struct Capture *cap) {
+ long i = 0; /* counts maximum expand for item */
+ while ((s+i)<cap->src_end && luaI_singlematch((unsigned char)*(s+i), p, ep))
+ i++;
+ /* keeps trying to match with the maximum repetitions */
+ while (i>=0) {
+ const char *res = match(L, (s+i), ep+1, cap);
+ if (res) return res;
+ i--; /* else didn't match; reduce 1 repetition to try again */
+ }
+ return NULL;
+}
+
+
+static const char *min_expand (lua_State *L, const char *s, const char *p,
+ const char *ep, struct Capture *cap) {
+ for (;;) {
+ const char *res = match(L, s, ep+1, cap);
+ if (res != NULL)
+ return res;
+ else if (s<cap->src_end && luaI_singlematch((unsigned char)*s, p, ep))
+ s++; /* try with one more repetition */
+ else return NULL;
+ }
+}
+
+
+static const char *start_capture (lua_State *L, const char *s, const char *p,
+ struct Capture *cap) {
+ const char *res;
+ int level = cap->level;
+ if (level >= MAX_CAPTURES) lua_error(L, "too many captures");
+ cap->capture[level].init = s;
+ cap->capture[level].len = -1;
+ cap->level = level+1;
+ if ((res=match(L, s, p+1, cap)) == NULL) /* match failed? */
+ cap->level--; /* undo capture */
+ return res;
+}
+
+
+static const char *end_capture (lua_State *L, const char *s, const char *p,
+ struct Capture *cap) {
+ int l = capture_to_close(L, cap);
+ const char *res;
+ cap->capture[l].len = s - cap->capture[l].init; /* close capture */
+ if ((res = match(L, s, p+1, cap)) == NULL) /* match failed? */
+ cap->capture[l].len = -1; /* undo capture */
+ return res;
+}
+
+
+static const char *match_capture (lua_State *L, const char *s, int level,
+ struct Capture *cap) {
+ int l = check_capture(L, level, cap);
+ size_t len = cap->capture[l].len;
+ if ((size_t)(cap->src_end-s) >= len &&
+ memcmp(cap->capture[l].init, s, len) == 0)
+ return s+len;
+ else return NULL;
+}
+
+
+static const char *match (lua_State *L, const char *s, const char *p,
+ struct Capture *cap) {
+ init: /* using goto's to optimize tail recursion */
+ switch (*p) {
+ case '(': /* start capture */
+ return start_capture(L, s, p, cap);
+ case ')': /* end capture */
+ return end_capture(L, s, p, cap);
+ case ESC: /* may be %[0-9] or %b */
+ if (isdigit((unsigned char)(*(p+1)))) { /* capture? */
+ s = match_capture(L, s, *(p+1), cap);
+ if (s == NULL) return NULL;
+ p+=2; goto init; /* else return match(L, s, p+2, cap) */
+ }
+ else if (*(p+1) == 'b') { /* balanced string? */
+ s = matchbalance(L, s, p+2, cap);
+ if (s == NULL) return NULL;
+ p+=4; goto init; /* else return match(L, s, p+4, cap); */
+ }
+ else goto dflt; /* case default */
+ case '\0': /* end of pattern */
+ return s; /* match succeeded */
+ case '$':
+ if (*(p+1) == '\0') /* is the '$' the last char in pattern? */
+ return (s == cap->src_end) ? s : NULL; /* check end of string */
+ else goto dflt;
+ default: dflt: { /* it is a pattern item */
+ const char *ep = luaI_classend(L, p); /* points to what is next */
+ int m = s<cap->src_end && luaI_singlematch((unsigned char)*s, p, ep);
+ switch (*ep) {
+ case '?': { /* optional */
+ const char *res;
+ if (m && ((res=match(L, s+1, ep+1, cap)) != NULL))
+ return res;
+ p=ep+1; goto init; /* else return match(L, s, ep+1, cap); */
+ }
+ case '*': /* 0 or more repetitions */
+ return max_expand(L, s, p, ep, cap);
+ case '+': /* 1 or more repetitions */
+ return (m ? max_expand(L, s+1, p, ep, cap) : NULL);
+ case '-': /* 0 or more repetitions (minimum) */
+ return min_expand(L, s, p, ep, cap);
+ default:
+ if (!m) return NULL;
+ s++; p=ep; goto init; /* else return match(L, s+1, ep, cap); */
+ }
+ }
+ }
+}
+
+
+
+static const char *lmemfind (const char *s1, size_t l1,
+ const char *s2, size_t l2) {
+ if (l2 == 0) return s1; /* empty strings are everywhere */
+ else if (l2 > l1) return NULL; /* avoids a negative `l1' */
+ else {
+ const char *init; /* to search for a `*s2' inside `s1' */
+ l2--; /* 1st char will be checked by `memchr' */
+ l1 = l1-l2; /* `s2' cannot be found after that */
+ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
+ init++; /* 1st char is already checked */
+ if (memcmp(init, s2+1, l2) == 0)
+ return init-1;
+ else { /* correct `l1' and `s1' to try again */
+ l1 -= init-s1;
+ s1 = init;
+ }
+ }
+ return NULL; /* not found */
+ }
+}
+
+
+static int push_captures (lua_State *L, struct Capture *cap) {
+ int i;
+ luaL_checkstack(L, cap->level, "too many captures");
+ for (i=0; i<cap->level; i++) {
+ int l = cap->capture[i].len;
+ if (l == -1) lua_error(L, "unfinished capture");
+ lua_pushlstring(L, cap->capture[i].init, l);
+ }
+ return cap->level; /* number of strings pushed */
+}
+
+
+static int str_find (lua_State *L) {
+ size_t l1, l2;
+ const char *s = luaL_check_lstr(L, 1, &l1);
+ const char *p = luaL_check_lstr(L, 2, &l2);
+ long init = posrelat(luaL_opt_long(L, 3, 1), l1) - 1;
+ struct Capture cap;
+ luaL_arg_check(L, 0 <= init && (size_t)init <= l1, 3, "out of range");
+ if (lua_gettop(L) > 3 || /* extra argument? */
+ strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */
+ const char *s2 = lmemfind(s+init, l1-init, p, l2);
+ if (s2) {
+ lua_pushnumber(L, s2-s+1);
+ lua_pushnumber(L, s2-s+l2);
+ return 2;
+ }
+ }
+ else {
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ const char *s1=s+init;
+ cap.src_end = s+l1;
+ do {
+ const char *res;
+ cap.level = 0;
+ if ((res=match(L, s1, p, &cap)) != NULL) {
+ lua_pushnumber(L, s1-s+1); /* start */
+ lua_pushnumber(L, res-s); /* end */
+ return push_captures(L, &cap) + 2;
+ }
+ } while (s1++<cap.src_end && !anchor);
+ }
+ lua_pushnil(L); /* not found */
+ return 1;
+}
+
+
+static void add_s (lua_State *L, luaL_Buffer *b, struct Capture *cap) {
+ if (lua_isstring(L, 3)) {
+ const char *news = lua_tostring(L, 3);
+ size_t l = lua_strlen(L, 3);
+ size_t i;
+ for (i=0; i<l; i++) {
+ if (news[i] != ESC)
+ luaL_putchar(b, news[i]);
+ else {
+ i++; /* skip ESC */
+ if (!isdigit((unsigned char)news[i]))
+ luaL_putchar(b, news[i]);
+ else {
+ int level = check_capture(L, news[i], cap);
+ luaL_addlstring(b, cap->capture[level].init, cap->capture[level].len);
+ }
+ }
+ }
+ }
+ else { /* is a function */
+ int n;
+ lua_pushvalue(L, 3);
+ n = push_captures(L, cap);
+ lua_rawcall(L, n, 1);
+ if (lua_isstring(L, -1))
+ luaL_addvalue(b); /* add return to accumulated result */
+ else
+ lua_pop(L, 1); /* function result is not a string: pop it */
+ }
+}
+
+
+static int str_gsub (lua_State *L) {
+ size_t srcl;
+ const char *src = luaL_check_lstr(L, 1, &srcl);
+ const char *p = luaL_check_string(L, 2);
+ int max_s = luaL_opt_int(L, 4, srcl+1);
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ int n = 0;
+ struct Capture cap;
+ luaL_Buffer b;
+ luaL_arg_check(L,
+ lua_gettop(L) >= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)),
+ 3, "string or function expected");
+ luaL_buffinit(L, &b);
+ cap.src_end = src+srcl;
+ while (n < max_s) {
+ const char *e;
+ cap.level = 0;
+ e = match(L, src, p, &cap);
+ if (e) {
+ n++;
+ add_s(L, &b, &cap);
+ }
+ if (e && e>src) /* non empty match? */
+ src = e; /* skip it */
+ else if (src < cap.src_end)
+ luaL_putchar(&b, *src++);
+ else break;
+ if (anchor) break;
+ }
+ luaL_addlstring(&b, src, cap.src_end-src);
+ luaL_pushresult(&b);
+ lua_pushnumber(L, n); /* number of substitutions */
+ return 2;
+}
+
+/* }====================================================== */
+
+
+static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) {
+ size_t l;
+ const char *s = luaL_check_lstr(L, arg, &l);
+ luaL_putchar(b, '"');
+ while (l--) {
+ switch (*s) {
+ case '"': case '\\': case '\n':
+ luaL_putchar(b, '\\');
+ luaL_putchar(b, *s);
+ break;
+ case '\0': luaL_addlstring(b, "\\000", 4); break;
+ default: luaL_putchar(b, *s);
+ }
+ s++;
+ }
+ luaL_putchar(b, '"');
+}
+
+/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
+#define MAX_ITEM 512
+/* maximum size of each format specification (such as '%-099.99d') */
+#define MAX_FORMAT 20
+
+static int str_format (lua_State *L) {
+ int arg = 1;
+ const char *strfrmt = luaL_check_string(L, arg);
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (*strfrmt) {
+ if (*strfrmt != '%')
+ luaL_putchar(&b, *strfrmt++);
+ else if (*++strfrmt == '%')
+ luaL_putchar(&b, *strfrmt++); /* %% */
+ else { /* format item */
+ struct Capture cap;
+ char form[MAX_FORMAT]; /* to store the format ('%...') */
+ char buff[MAX_ITEM]; /* to store the formatted item */
+ const char *initf = strfrmt;
+ form[0] = '%';
+ if (isdigit((unsigned char)*initf) && *(initf+1) == '$') {
+ arg = *initf - '0';
+ initf += 2; /* skip the 'n$' */
+ }
+ arg++;
+ cap.src_end = strfrmt+strlen(strfrmt)+1;
+ cap.level = 0;
+ strfrmt = match(L, initf, "[-+ #0]*(%d*)%.?(%d*)", &cap);
+ if (cap.capture[0].len > 2 || cap.capture[1].len > 2 || /* < 100? */
+ strfrmt-initf > MAX_FORMAT-2)
+ lua_error(L, "invalid format (width or precision too long)");
+ strncpy(form+1, initf, strfrmt-initf+1); /* +1 to include conversion */
+ form[strfrmt-initf+2] = 0;
+ switch (*strfrmt++) {
+ case 'c': case 'd': case 'i':
+ sprintf(buff, form, luaL_check_int(L, arg));
+ break;
+ case 'o': case 'u': case 'x': case 'X':
+ sprintf(buff, form, (unsigned int)luaL_check_number(L, arg));
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ sprintf(buff, form, luaL_check_number(L, arg));
+ break;
+ case 'q':
+ luaI_addquoted(L, &b, arg);
+ continue; /* skip the "addsize" at the end */
+ case 's': {
+ size_t l;
+ const char *s = luaL_check_lstr(L, arg, &l);
+ if (cap.capture[1].len == 0 && l >= 100) {
+ /* no precision and string is too long to be formatted;
+ keep original string */
+ lua_pushvalue(L, arg);
+ luaL_addvalue(&b);
+ continue; /* skip the "addsize" at the end */
+ }
+ else {
+ sprintf(buff, form, s);
+ break;
+ }
+ }
+ default: /* also treat cases 'pnLlh' */
+ lua_error(L, "invalid option in `format'");
+ }
+ luaL_addlstring(&b, buff, strlen(buff));
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static const struct luaL_reg strlib[] = {
+{"strlen", str_len},
+{"strsub", str_sub},
+{"strlower", str_lower},
+{"strupper", str_upper},
+{"strchar", str_char},
+{"strrep", str_rep},
+{"ascii", str_byte}, /* for compatibility with 3.0 and earlier */
+{"strbyte", str_byte},
+{"format", str_format},
+{"strfind", str_find},
+{"gsub", str_gsub}
+};
+
+
+/*
+** Open string library
+*/
+LUALIB_API void lua_strlibopen (lua_State *L) {
+ luaL_openl(L, strlib);
+}
diff --git a/src/lua/ltable.c b/src/lua/ltable.c
new file mode 100644
index 00000000..1e3eb4f5
--- /dev/null
+++ b/src/lua/ltable.c
@@ -0,0 +1,303 @@
+/*
+** $Id: ltable.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+
+/*
+** Implementation of tables (aka arrays, objects, or hash tables);
+** uses a mix of chained scatter table with Brent's variation.
+** A main invariant of these tables is that, if an element is not
+** in its main position (i.e. the `original' position that its hash gives
+** to it), then the colliding element is in its own main position.
+** In other words, there are collisions only when two elements have the
+** same main position (i.e. the same hash values for that table size).
+** Because of that, the load factor of these tables can be 100% without
+** performance penalties.
+*/
+
+
+#include "lua.h"
+
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+
+
+#define gcsize(L, n) (sizeof(Hash)+(n)*sizeof(Node))
+
+
+
+#define TagDefault LUA_TTABLE
+
+
+
+/*
+** returns the `main' position of an element in a table (that is, the index
+** of its hash value)
+*/
+Node *luaH_mainposition (const Hash *t, const TObject *key) {
+ unsigned long h;
+ switch (ttype(key)) {
+ case LUA_TNUMBER:
+ h = (unsigned long)(long)nvalue(key);
+ break;
+ case LUA_TSTRING:
+ h = tsvalue(key)->u.s.hash;
+ break;
+ case LUA_TUSERDATA:
+ h = IntPoint(tsvalue(key));
+ break;
+ case LUA_TTABLE:
+ h = IntPoint(hvalue(key));
+ break;
+ case LUA_TFUNCTION:
+ h = IntPoint(clvalue(key));
+ break;
+ default:
+ return NULL; /* invalid key */
+ }
+ LUA_ASSERT(h%(unsigned int)t->size == (h&((unsigned int)t->size-1)),
+ "a&(x-1) == a%x, for x power of 2");
+ return &t->node[h&(t->size-1)];
+}
+
+
+static const TObject *luaH_getany (lua_State *L, const Hash *t,
+ const TObject *key) {
+ Node *n = luaH_mainposition(t, key);
+ if (!n)
+ lua_error(L, "table index is nil");
+ else do {
+ if (luaO_equalObj(key, &n->key))
+ return &n->val;
+ n = n->next;
+ } while (n);
+ return &luaO_nilobject; /* key not found */
+}
+
+
+/* specialized version for numbers */
+const TObject *luaH_getnum (const Hash *t, Number key) {
+ Node *n = &t->node[(unsigned long)(long)key&(t->size-1)];
+ do {
+ if (ttype(&n->key) == LUA_TNUMBER && nvalue(&n->key) == key)
+ return &n->val;
+ n = n->next;
+ } while (n);
+ return &luaO_nilobject; /* key not found */
+}
+
+
+/* specialized version for strings */
+const TObject *luaH_getstr (const Hash *t, TString *key) {
+ Node *n = &t->node[key->u.s.hash&(t->size-1)];
+ do {
+ if (ttype(&n->key) == LUA_TSTRING && tsvalue(&n->key) == key)
+ return &n->val;
+ n = n->next;
+ } while (n);
+ return &luaO_nilobject; /* key not found */
+}
+
+
+const TObject *luaH_get (lua_State *L, const Hash *t, const TObject *key) {
+ switch (ttype(key)) {
+ case LUA_TNUMBER: return luaH_getnum(t, nvalue(key));
+ case LUA_TSTRING: return luaH_getstr(t, tsvalue(key));
+ default: return luaH_getany(L, t, key);
+ }
+}
+
+
+Node *luaH_next (lua_State *L, const Hash *t, const TObject *key) {
+ int i;
+ if (ttype(key) == LUA_TNIL)
+ i = 0; /* first iteration */
+ else {
+ const TObject *v = luaH_get(L, t, key);
+ if (v == &luaO_nilobject)
+ lua_error(L, "invalid key for `next'");
+ i = (int)(((const char *)v -
+ (const char *)(&t->node[0].val)) / sizeof(Node)) + 1;
+ }
+ for (; i<t->size; i++) {
+ Node *n = node(t, i);
+ if (ttype(val(n)) != LUA_TNIL)
+ return n;
+ }
+ return NULL; /* no more elements */
+}
+
+
+/*
+** try to remove a key without value from a table. To avoid problems with
+** hash, change `key' for a number with the same hash.
+*/
+void luaH_remove (Hash *t, TObject *key) {
+ if (ttype(key) == LUA_TNUMBER ||
+ (ttype(key) == LUA_TSTRING && tsvalue(key)->len <= 30))
+ return; /* do not remove numbers nor small strings */
+ else {
+ /* try to find a number `n' with the same hash as `key' */
+ Node *mp = luaH_mainposition(t, key);
+ int n = mp - &t->node[0];
+ /* make sure `n' is not in `t' */
+ while (luaH_getnum(t, n) != &luaO_nilobject) {
+ if (n >= MAX_INT - t->size)
+ return; /* give up; (to avoid overflow) */
+ n += t->size;
+ }
+ ttype(key) = LUA_TNUMBER;
+ nvalue(key) = n;
+ LUA_ASSERT(luaH_mainposition(t, key) == mp, "cannot change hash");
+ }
+}
+
+
+static void setnodevector (lua_State *L, Hash *t, lint32 size) {
+ int i;
+ if (size > MAX_INT)
+ lua_error(L, "table overflow");
+ t->node = luaM_newvector(L, size, Node);
+ for (i=0; i<(int)size; i++) {
+ ttype(&t->node[i].key) = ttype(&t->node[i].val) = LUA_TNIL;
+ t->node[i].next = NULL;
+ }
+ L->nblocks += gcsize(L, size) - gcsize(L, t->size);
+ t->size = size;
+ t->firstfree = &t->node[size-1]; /* first free position to be used */
+}
+
+
+Hash *luaH_new (lua_State *L, int size) {
+ Hash *t = luaM_new(L, Hash);
+ t->htag = TagDefault;
+ t->next = L->roottable;
+ L->roottable = t;
+ t->mark = t;
+ t->size = 0;
+ L->nblocks += gcsize(L, 0);
+ t->node = NULL;
+ setnodevector(L, t, luaO_power2(size));
+ return t;
+}
+
+
+void luaH_free (lua_State *L, Hash *t) {
+ L->nblocks -= gcsize(L, t->size);
+ luaM_free(L, t->node);
+ luaM_free(L, t);
+}
+
+
+static int numuse (const Hash *t) {
+ Node *v = t->node;
+ int size = t->size;
+ int realuse = 0;
+ int i;
+ for (i=0; i<size; i++) {
+ if (ttype(&v[i].val) != LUA_TNIL)
+ realuse++;
+ }
+ return realuse;
+}
+
+
+static void rehash (lua_State *L, Hash *t) {
+ int oldsize = t->size;
+ Node *nold = t->node;
+ int nelems = numuse(t);
+ int i;
+ LUA_ASSERT(nelems<=oldsize, "wrong count");
+ if (nelems >= oldsize-oldsize/4) /* using more than 3/4? */
+ setnodevector(L, t, (lint32)oldsize*2);
+ else if (nelems <= oldsize/4 && /* less than 1/4? */
+ oldsize > MINPOWER2)
+ setnodevector(L, t, oldsize/2);
+ else
+ setnodevector(L, t, oldsize);
+ for (i=0; i<oldsize; i++) {
+ Node *old = nold+i;
+ if (ttype(&old->val) != LUA_TNIL)
+ *luaH_set(L, t, &old->key) = old->val;
+ }
+ luaM_free(L, nold); /* free old array */
+}
+
+
+/*
+** inserts a key into a hash table; first, check whether key is
+** already present; if not, check whether key's main position is free;
+** if not, check whether colliding node is in its main position or not;
+** if it is not, move colliding node to an empty place and put new key
+** in its main position; otherwise (colliding node is in its main position),
+** new key goes to an empty position.
+*/
+TObject *luaH_set (lua_State *L, Hash *t, const TObject *key) {
+ Node *mp = luaH_mainposition(t, key);
+ Node *n = mp;
+ if (!mp)
+ lua_error(L, "table index is nil");
+ do { /* check whether `key' is somewhere in the chain */
+ if (luaO_equalObj(key, &n->key))
+ return &n->val; /* that's all */
+ else n = n->next;
+ } while (n);
+ /* `key' not found; must insert it */
+ if (ttype(&mp->key) != LUA_TNIL) { /* main position is not free? */
+ Node *othern; /* main position of colliding node */
+ n = t->firstfree; /* get a free place */
+ /* is colliding node out of its main position? (can only happens if
+ its position is after "firstfree") */
+ if (mp > n && (othern=luaH_mainposition(t, &mp->key)) != mp) {
+ /* yes; move colliding node into free position */
+ while (othern->next != mp) othern = othern->next; /* find previous */
+ othern->next = n; /* redo the chain with `n' in place of `mp' */
+ *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */
+ mp->next = NULL; /* now `mp' is free */
+ }
+ else { /* colliding node is in its own main position */
+ /* new node will go into free position */
+ n->next = mp->next; /* chain new position */
+ mp->next = n;
+ mp = n;
+ }
+ }
+ mp->key = *key;
+ for (;;) { /* correct `firstfree' */
+ if (ttype(&t->firstfree->key) == LUA_TNIL)
+ return &mp->val; /* OK; table still has a free place */
+ else if (t->firstfree == t->node) break; /* cannot decrement from here */
+ else (t->firstfree)--;
+ }
+ rehash(L, t); /* no more free places */
+ return luaH_set(L, t, key); /* `rehash' invalidates this insertion */
+}
+
+
+TObject *luaH_setint (lua_State *L, Hash *t, int key) {
+ TObject index;
+ ttype(&index) = LUA_TNUMBER;
+ nvalue(&index) = key;
+ return luaH_set(L, t, &index);
+}
+
+
+void luaH_setstrnum (lua_State *L, Hash *t, TString *key, Number val) {
+ TObject *value, index;
+ ttype(&index) = LUA_TSTRING;
+ tsvalue(&index) = key;
+ value = luaH_set(L, t, &index);
+ ttype(value) = LUA_TNUMBER;
+ nvalue(value) = val;
+}
+
+
+const TObject *luaH_getglobal (lua_State *L, const char *name) {
+ return luaH_getstr(L->gt, luaS_new(L, name));
+}
+
diff --git a/src/lua/ltable.h b/src/lua/ltable.h
new file mode 100644
index 00000000..3bc2a5df
--- /dev/null
+++ b/src/lua/ltable.h
@@ -0,0 +1,34 @@
+/*
+** $Id: ltable.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltable_h
+#define ltable_h
+
+#include "lobject.h"
+
+
+#define node(t,i) (&(t)->node[i])
+#define key(n) (&(n)->key)
+#define val(n) (&(n)->val)
+
+Hash *luaH_new (lua_State *L, int nhash);
+void luaH_free (lua_State *L, Hash *t);
+const TObject *luaH_get (lua_State *L, const Hash *t, const TObject *key);
+const TObject *luaH_getnum (const Hash *t, Number key);
+const TObject *luaH_getstr (const Hash *t, TString *key);
+void luaH_remove (Hash *t, TObject *key);
+TObject *luaH_set (lua_State *L, Hash *t, const TObject *key);
+Node * luaH_next (lua_State *L, const Hash *t, const TObject *r);
+TObject *luaH_setint (lua_State *L, Hash *t, int key);
+void luaH_setstrnum (lua_State *L, Hash *t, TString *key, Number val);
+unsigned long luaH_hash (lua_State *L, const TObject *key);
+const TObject *luaH_getglobal (lua_State *L, const char *name);
+
+/* exported only for debugging */
+Node *luaH_mainposition (const Hash *t, const TObject *key);
+
+
+#endif
diff --git a/src/lua/ltests.c b/src/lua/ltests.c
new file mode 100644
index 00000000..06e08f5a
--- /dev/null
+++ b/src/lua/ltests.c
@@ -0,0 +1,543 @@
+/*
+** $Id: ltests.c,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+** Internal Module for Debugging of the Lua Implementation
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "lauxlib.h"
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "luadebug.h"
+#include "lualib.h"
+
+
+void luaB_opentests (lua_State *L);
+
+
+/*
+** The whole module only makes sense with LUA_DEBUG on
+*/
+#ifdef LUA_DEBUG
+
+
+
+static void setnameval (lua_State *L, const char *name, int val) {
+ lua_pushstring(L, name);
+ lua_pushnumber(L, val);
+ lua_settable(L, -3);
+}
+
+
+/*
+** {======================================================
+** Disassembler
+** =======================================================
+*/
+
+
+static const char *const instrname[NUM_OPCODES] = {
+ "END", "RETURN", "CALL", "TAILCALL", "PUSHNIL", "POP", "PUSHINT",
+ "PUSHSTRING", "PUSHNUM", "PUSHNEGNUM", "PUSHUPVALUE", "GETLOCAL",
+ "GETGLOBAL", "GETTABLE", "GETDOTTED", "GETINDEXED", "PUSHSELF",
+ "CREATETABLE", "SETLOCAL", "SETGLOBAL", "SETTABLE", "SETLIST", "SETMAP",
+ "ADD", "ADDI", "SUB", "MULT", "DIV", "POW", "CONCAT", "MINUS", "NOT",
+ "JMPNE", "JMPEQ", "JMPLT", "JMPLE", "JMPGT", "JMPGE", "JMPT", "JMPF",
+ "JMPONT", "JMPONF", "JMP", "PUSHNILJMP", "FORPREP", "FORLOOP", "LFORPREP",
+ "LFORLOOP", "CLOSURE"
+};
+
+
+static int pushop (lua_State *L, Proto *p, int pc) {
+ char buff[100];
+ Instruction i = p->code[pc];
+ OpCode o = GET_OPCODE(i);
+ const char *name = instrname[o];
+ sprintf(buff, "%5d - ", luaG_getline(p->lineinfo, pc, 1, NULL));
+ switch ((enum Mode)luaK_opproperties[o].mode) {
+ case iO:
+ sprintf(buff+8, "%-12s", name);
+ break;
+ case iU:
+ sprintf(buff+8, "%-12s%4u", name, GETARG_U(i));
+ break;
+ case iS:
+ sprintf(buff+8, "%-12s%4d", name, GETARG_S(i));
+ break;
+ case iAB:
+ sprintf(buff+8, "%-12s%4d %4d", name, GETARG_A(i), GETARG_B(i));
+ break;
+ }
+ lua_pushstring(L, buff);
+ return (o != OP_END);
+}
+
+
+static int listcode (lua_State *L) {
+ int pc;
+ Proto *p;
+ int res;
+ luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1),
+ 1, "Lua function expected");
+ p = clvalue(luaA_index(L, 1))->f.l;
+ lua_newtable(L);
+ setnameval(L, "maxstack", p->maxstacksize);
+ setnameval(L, "numparams", p->numparams);
+ pc = 0;
+ do {
+ lua_pushnumber(L, pc+1);
+ res = pushop(L, p, pc++);
+ lua_settable(L, -3);
+ } while (res);
+ return 1;
+}
+
+
+static int liststrings (lua_State *L) {
+ Proto *p;
+ int i;
+ luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1),
+ 1, "Lua function expected");
+ p = clvalue(luaA_index(L, 1))->f.l;
+ lua_newtable(L);
+ for (i=0; i<p->nkstr; i++) {
+ lua_pushnumber(L, i+1);
+ lua_pushstring(L, p->kstr[i]->str);
+ lua_settable(L, -3);
+ }
+ return 1;
+}
+
+
+static int listlocals (lua_State *L) {
+ Proto *p;
+ int pc = luaL_check_int(L, 2) - 1;
+ int i = 0;
+ const char *name;
+ luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1),
+ 1, "Lua function expected");
+ p = clvalue(luaA_index(L, 1))->f.l;
+ while ((name = luaF_getlocalname(p, ++i, pc)) != NULL)
+ lua_pushstring(L, name);
+ return i-1;
+}
+
+/* }====================================================== */
+
+
+
+static int get_limits (lua_State *L) {
+ lua_newtable(L);
+ setnameval(L, "BITS_INT", BITS_INT);
+ setnameval(L, "LFPF", LFIELDS_PER_FLUSH);
+ setnameval(L, "MAXARG_A", MAXARG_A);
+ setnameval(L, "MAXARG_B", MAXARG_B);
+ setnameval(L, "MAXARG_S", MAXARG_S);
+ setnameval(L, "MAXARG_U", MAXARG_U);
+ setnameval(L, "MAXLOCALS", MAXLOCALS);
+ setnameval(L, "MAXPARAMS", MAXPARAMS);
+ setnameval(L, "MAXSTACK", MAXSTACK);
+ setnameval(L, "MAXUPVALUES", MAXUPVALUES);
+ setnameval(L, "MAXVARSLH", MAXVARSLH);
+ setnameval(L, "RFPF", RFIELDS_PER_FLUSH);
+ setnameval(L, "SIZE_A", SIZE_A);
+ setnameval(L, "SIZE_B", SIZE_B);
+ setnameval(L, "SIZE_OP", SIZE_OP);
+ setnameval(L, "SIZE_U", SIZE_U);
+ return 1;
+}
+
+
+static int mem_query (lua_State *L) {
+ if (lua_isnull(L, 1)) {
+ lua_pushnumber(L, memdebug_total);
+ lua_pushnumber(L, memdebug_numblocks);
+ lua_pushnumber(L, memdebug_maxmem);
+ return 3;
+ }
+ else {
+ memdebug_memlimit = luaL_check_int(L, 1);
+ return 0;
+ }
+}
+
+
+static int hash_query (lua_State *L) {
+ if (lua_isnull(L, 2)) {
+ luaL_arg_check(L, lua_tag(L, 1) == LUA_TSTRING, 1, "string expected");
+ lua_pushnumber(L, tsvalue(luaA_index(L, 1))->u.s.hash);
+ }
+ else {
+ Hash *t;
+ luaL_checktype(L, 2, LUA_TTABLE);
+ t = hvalue(luaA_index(L, 2));
+ lua_pushnumber(L, luaH_mainposition(t, luaA_index(L, 1)) - t->node);
+ }
+ return 1;
+}
+
+
+static int table_query (lua_State *L) {
+ const Hash *t;
+ int i = luaL_opt_int(L, 2, -1);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ t = hvalue(luaA_index(L, 1));
+ if (i == -1) {
+ lua_pushnumber(L, t->size);
+ lua_pushnumber(L, t->firstfree - t->node);
+ return 2;
+ }
+ else if (i < t->size) {
+ luaA_pushobject(L, &t->node[i].key);
+ luaA_pushobject(L, &t->node[i].val);
+ if (t->node[i].next) {
+ lua_pushnumber(L, t->node[i].next - t->node);
+ return 3;
+ }
+ else
+ return 2;
+ }
+ return 0;
+}
+
+
+static int string_query (lua_State *L) {
+ stringtable *tb = (*luaL_check_string(L, 1) == 's') ? &L->strt : &L->udt;
+ int s = luaL_opt_int(L, 2, 0) - 1;
+ if (s==-1) {
+ lua_pushnumber(L ,tb->nuse);
+ lua_pushnumber(L ,tb->size);
+ return 2;
+ }
+ else if (s < tb->size) {
+ TString *ts;
+ int n = 0;
+ for (ts = tb->hash[s]; ts; ts = ts->nexthash) {
+ ttype(L->top) = LUA_TSTRING;
+ tsvalue(L->top) = ts;
+ incr_top;
+ n++;
+ }
+ return n;
+ }
+ return 0;
+}
+
+
+static int tref (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushvalue(L, 1);
+ lua_pushnumber(L, lua_ref(L, luaL_opt_int(L, 2, 1)));
+ return 1;
+}
+
+static int getref (lua_State *L) {
+ if (lua_getref(L, luaL_check_int(L, 1)))
+ return 1;
+ else
+ return 0;
+}
+
+static int unref (lua_State *L) {
+ lua_unref(L, luaL_check_int(L, 1));
+ return 0;
+}
+
+static int newuserdata (lua_State *L) {
+ if (lua_isnumber(L, 2))
+ lua_pushusertag(L, (void *)luaL_check_int(L, 1), luaL_check_int(L, 2));
+ else
+ lua_newuserdata(L, luaL_check_int(L, 1));
+ return 1;
+}
+
+static int udataval (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ lua_pushnumber(L, (int)lua_touserdata(L, 1));
+ return 1;
+}
+
+static int newstate (lua_State *L) {
+ lua_State *L1 = lua_open(luaL_check_int(L, 1));
+ if (L1)
+ lua_pushuserdata(L, L1);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static int loadlib (lua_State *L) {
+ lua_State *L1 = (lua_State *)lua_touserdata(L, 1);
+ switch (*luaL_check_string(L, 2)) {
+ case 'm': lua_mathlibopen(L1); break;
+ case 's': lua_strlibopen(L1); break;
+ case 'i': lua_iolibopen(L1); break;
+ case 'd': lua_dblibopen(L1); break;
+ case 'b': lua_baselibopen(L1); break;
+ default: luaL_argerror(L, 2, "invalid option");
+ }
+ return 0;
+}
+
+static int closestate (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ lua_close((lua_State *)lua_touserdata(L, 1));
+ return 0;
+}
+
+static int doremote (lua_State *L) {
+ lua_State *L1;
+ const char *code = luaL_check_string(L, 2);
+ int status;
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ L1 = (lua_State *)lua_touserdata(L, 1);
+ status = lua_dostring(L1, code);
+ if (status != 0) {
+ lua_pushnil(L);
+ lua_pushnumber(L, status);
+ return 2;
+ }
+ else {
+ int i = 0;
+ while (!lua_isnull(L1, ++i))
+ lua_pushstring(L, lua_tostring(L1, i));
+ return i-1;
+ }
+}
+
+static int settagmethod (lua_State *L) {
+ int tag = luaL_check_int(L, 1);
+ const char *event = luaL_check_string(L, 2);
+ luaL_checkany(L, 3);
+ lua_gettagmethod(L, tag, event);
+ lua_pushvalue(L, 3);
+ lua_settagmethod(L, tag, event);
+ return 1;
+}
+
+static int pushbool (lua_State *L, int b) {
+ if (b) lua_pushnumber(L, 1);
+ else lua_pushnil(L);
+ return 1;
+}
+
+static int equal (lua_State *L) {
+ return pushbool(L, lua_equal(L, 1, 2));
+}
+
+
+
+/*
+** {======================================================
+** function to test the API with C. It interprets a kind of "assembler"
+** language with calls to the API, so the test can be driven by Lua code
+** =======================================================
+*/
+
+static const char *const delimits = " \t\n,;";
+
+static void skip (const char **pc) {
+ while (**pc != '\0' && strchr(delimits, **pc)) (*pc)++;
+}
+
+static int getnum (lua_State *L, const char **pc) {
+ int res = 0;
+ int sig = 1;
+ skip(pc);
+ if (**pc == '.') {
+ res = (int)lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ (*pc)++;
+ return res;
+ }
+ else if (**pc == '-') {
+ sig = -1;
+ (*pc)++;
+ }
+ while (isdigit(**pc)) res = res*10 + (*(*pc)++) - '0';
+ return sig*res;
+}
+
+static const char *getname (char *buff, const char **pc) {
+ int i = 0;
+ skip(pc);
+ while (**pc != '\0' && !strchr(delimits, **pc))
+ buff[i++] = *(*pc)++;
+ buff[i] = '\0';
+ return buff;
+}
+
+
+#define EQ(s1) (strcmp(s1, inst) == 0)
+
+#define getnum ((getnum)(L, &pc))
+#define getname ((getname)(buff, &pc))
+
+
+static int testC (lua_State *L) {
+ char buff[30];
+ const char *pc = luaL_check_string(L, 1);
+ for (;;) {
+ const char *inst = getname;
+ if EQ("") return 0;
+ else if EQ("isnumber") {
+ lua_pushnumber(L, lua_isnumber(L, getnum));
+ }
+ else if EQ("isstring") {
+ lua_pushnumber(L, lua_isstring(L, getnum));
+ }
+ else if EQ("istable") {
+ lua_pushnumber(L, lua_istable(L, getnum));
+ }
+ else if EQ("iscfunction") {
+ lua_pushnumber(L, lua_iscfunction(L, getnum));
+ }
+ else if EQ("isfunction") {
+ lua_pushnumber(L, lua_isfunction(L, getnum));
+ }
+ else if EQ("isuserdata") {
+ lua_pushnumber(L, lua_isuserdata(L, getnum));
+ }
+ else if EQ("isnil") {
+ lua_pushnumber(L, lua_isnil(L, getnum));
+ }
+ else if EQ("isnull") {
+ lua_pushnumber(L, lua_isnull(L, getnum));
+ }
+ else if EQ("tonumber") {
+ lua_pushnumber(L, lua_tonumber(L, getnum));
+ }
+ else if EQ("tostring") {
+ lua_pushstring(L, lua_tostring(L, getnum));
+ }
+ else if EQ("tonumber") {
+ lua_pushnumber(L, lua_tonumber(L, getnum));
+ }
+ else if EQ("strlen") {
+ lua_pushnumber(L, lua_strlen(L, getnum));
+ }
+ else if EQ("tocfunction") {
+ lua_pushcfunction(L, lua_tocfunction(L, getnum));
+ }
+ else if EQ("return") {
+ return getnum;
+ }
+ else if EQ("gettop") {
+ lua_pushnumber(L, lua_gettop(L));
+ }
+ else if EQ("settop") {
+ lua_settop(L, getnum);
+ }
+ else if EQ("pop") {
+ lua_pop(L, getnum);
+ }
+ else if EQ("pushnum") {
+ lua_pushnumber(L, getnum);
+ }
+ else if EQ("pushvalue") {
+ lua_pushvalue(L, getnum);
+ }
+ else if EQ("remove") {
+ lua_remove(L, getnum);
+ }
+ else if EQ("insert") {
+ lua_insert(L, getnum);
+ }
+ else if EQ("gettable") {
+ lua_gettable(L, getnum);
+ }
+ else if EQ("settable") {
+ lua_settable(L, getnum);
+ }
+ else if EQ("next") {
+ lua_next(L, -2);
+ }
+ else if EQ("concat") {
+ lua_concat(L, getnum);
+ }
+ else if EQ("rawcall") {
+ int narg = getnum;
+ int nres = getnum;
+ lua_rawcall(L, narg, nres);
+ }
+ else if EQ("call") {
+ int narg = getnum;
+ int nres = getnum;
+ lua_call(L, narg, nres);
+ }
+ else if EQ("dostring") {
+ lua_dostring(L, luaL_check_string(L, getnum));
+ }
+ else if EQ("settagmethod") {
+ int tag = getnum;
+ const char *event = getname;
+ lua_settagmethod(L, tag, event);
+ }
+ else if EQ("gettagmethod") {
+ int tag = getnum;
+ const char *event = getname;
+ lua_gettagmethod(L, tag, event);
+ }
+ else if EQ("type") {
+ lua_pushstring(L, lua_typename(L, lua_type(L, getnum)));
+ }
+ else luaL_verror(L, "unknown instruction %.30s", buff);
+ }
+ return 0;
+}
+
+/* }====================================================== */
+
+
+
+static const struct luaL_reg tests_funcs[] = {
+ {"hash", hash_query},
+ {"limits", get_limits},
+ {"listcode", listcode},
+ {"liststrings", liststrings},
+ {"listlocals", listlocals},
+ {"loadlib", loadlib},
+ {"querystr", string_query},
+ {"querytab", table_query},
+ {"testC", testC},
+ {"ref", tref},
+ {"getref", getref},
+ {"unref", unref},
+ {"newuserdata", newuserdata},
+ {"udataval", udataval},
+ {"newstate", newstate},
+ {"closestate", closestate},
+ {"doremote", doremote},
+ {"settagmethod", settagmethod},
+ {"equal", equal},
+ {"totalmem", mem_query}
+};
+
+
+void luaB_opentests (lua_State *L) {
+ lua_newtable(L);
+ lua_getglobals(L);
+ lua_pushvalue(L, -2);
+ lua_setglobals(L);
+ luaL_openl(L, tests_funcs); /* open functions inside new table */
+ lua_setglobals(L); /* restore old table of globals */
+ lua_setglobal(L, "T"); /* set new table as global T */
+}
+
+#endif
diff --git a/src/lua/ltm.c b/src/lua/ltm.c
new file mode 100644
index 00000000..3f69a6ca
--- /dev/null
+++ b/src/lua/ltm.c
@@ -0,0 +1,163 @@
+/*
+** $Id: ltm.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "ltm.h"
+
+
+const char *const luaT_eventname[] = { /* ORDER TM */
+ "gettable", "settable", "index", "getglobal", "setglobal", "add", "sub",
+ "mul", "div", "pow", "unm", "lt", "concat", "gc", "function",
+ "le", "gt", "ge", /* deprecated options!! */
+ NULL
+};
+
+
+static int findevent (const char *name) {
+ int i;
+ for (i=0; luaT_eventname[i]; i++)
+ if (strcmp(luaT_eventname[i], name) == 0)
+ return i;
+ return -1; /* name not found */
+}
+
+
+static int luaI_checkevent (lua_State *L, const char *name, int t) {
+ int e = findevent(name);
+ if (e >= TM_N)
+ luaO_verror(L, "event `%.50s' is deprecated", name);
+ if (e == TM_GC && t == LUA_TTABLE)
+ luaO_verror(L, "event `gc' for tables is deprecated");
+ if (e < 0)
+ luaO_verror(L, "`%.50s' is not a valid event name", name);
+ return e;
+}
+
+
+
+/* events in LUA_TNIL are all allowed, since this is used as a
+* 'placeholder' for "default" fallbacks
+*/
+/* ORDER LUA_T, ORDER TM */
+static const char luaT_validevents[NUM_TAGS][TM_N] = {
+ {1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, /* LUA_TUSERDATA */
+ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* LUA_TNIL */
+ {1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, /* LUA_TNUMBER */
+ {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, /* LUA_TSTRING */
+ {0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, /* LUA_TTABLE */
+ {1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0} /* LUA_TFUNCTION */
+};
+
+int luaT_validevent (int t, int e) { /* ORDER LUA_T */
+ return (t >= NUM_TAGS) ? 1 : luaT_validevents[t][e];
+}
+
+
+static void init_entry (lua_State *L, int tag) {
+ int i;
+ for (i=0; i<TM_N; i++)
+ luaT_gettm(L, tag, i) = NULL;
+ L->TMtable[tag].collected = NULL;
+}
+
+
+void luaT_init (lua_State *L) {
+ int t;
+ luaM_growvector(L, L->TMtable, 0, NUM_TAGS, struct TM, "", MAX_INT);
+ L->nblocks += NUM_TAGS*sizeof(struct TM);
+ L->last_tag = NUM_TAGS-1;
+ for (t=0; t<=L->last_tag; t++)
+ init_entry(L, t);
+}
+
+
+LUA_API int lua_newtag (lua_State *L) {
+ luaM_growvector(L, L->TMtable, L->last_tag, 1, struct TM,
+ "tag table overflow", MAX_INT);
+ L->nblocks += sizeof(struct TM);
+ L->last_tag++;
+ init_entry(L, L->last_tag);
+ return L->last_tag;
+}
+
+
+static void checktag (lua_State *L, int tag) {
+ if (!(0 <= tag && tag <= L->last_tag))
+ luaO_verror(L, "%d is not a valid tag", tag);
+}
+
+void luaT_realtag (lua_State *L, int tag) {
+ if (!validtag(tag))
+ luaO_verror(L, "tag %d was not created by `newtag'", tag);
+}
+
+
+LUA_API int lua_copytagmethods (lua_State *L, int tagto, int tagfrom) {
+ int e;
+ checktag(L, tagto);
+ checktag(L, tagfrom);
+ for (e=0; e<TM_N; e++) {
+ if (luaT_validevent(tagto, e))
+ luaT_gettm(L, tagto, e) = luaT_gettm(L, tagfrom, e);
+ }
+ return tagto;
+}
+
+
+int luaT_tag (const TObject *o) {
+ int t = ttype(o);
+ switch (t) {
+ case LUA_TUSERDATA: return tsvalue(o)->u.d.tag;
+ case LUA_TTABLE: return hvalue(o)->htag;
+ default: return t;
+ }
+}
+
+
+LUA_API void lua_gettagmethod (lua_State *L, int t, const char *event) {
+ int e;
+ e = luaI_checkevent(L, event, t);
+ checktag(L, t);
+ if (luaT_validevent(t, e) && luaT_gettm(L, t, e)) {
+ clvalue(L->top) = luaT_gettm(L, t, e);
+ ttype(L->top) = LUA_TFUNCTION;
+ }
+ else
+ ttype(L->top) = LUA_TNIL;
+ incr_top;
+}
+
+
+LUA_API void lua_settagmethod (lua_State *L, int t, const char *event) {
+ int e = luaI_checkevent(L, event, t);
+ checktag(L, t);
+ if (!luaT_validevent(t, e))
+ luaO_verror(L, "cannot change `%.20s' tag method for type `%.20s'%.20s",
+ luaT_eventname[e], luaO_typenames[t],
+ (t == LUA_TTABLE || t == LUA_TUSERDATA) ?
+ " with default tag" : "");
+ switch (ttype(L->top - 1)) {
+ case LUA_TNIL:
+ luaT_gettm(L, t, e) = NULL;
+ break;
+ case LUA_TFUNCTION:
+ luaT_gettm(L, t, e) = clvalue(L->top - 1);
+ break;
+ default:
+ lua_error(L, "tag method must be a function (or nil)");
+ }
+ L->top--;
+}
+
diff --git a/src/lua/ltm.h b/src/lua/ltm.h
new file mode 100644
index 00000000..f6be13ed
--- /dev/null
+++ b/src/lua/ltm.h
@@ -0,0 +1,59 @@
+/*
+** $Id: ltm.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltm_h
+#define ltm_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER TM"
+*/
+typedef enum {
+ TM_GETTABLE = 0,
+ TM_SETTABLE,
+ TM_INDEX,
+ TM_GETGLOBAL,
+ TM_SETGLOBAL,
+ TM_ADD,
+ TM_SUB,
+ TM_MUL,
+ TM_DIV,
+ TM_POW,
+ TM_UNM,
+ TM_LT,
+ TM_CONCAT,
+ TM_GC,
+ TM_FUNCTION,
+ TM_N /* number of elements in the enum */
+} TMS;
+
+
+struct TM {
+ Closure *method[TM_N];
+ TString *collected; /* list of garbage-collected udata with this tag */
+};
+
+
+#define luaT_gettm(L,tag,event) (L->TMtable[tag].method[event])
+#define luaT_gettmbyObj(L,o,e) (luaT_gettm((L),luaT_tag(o),(e)))
+
+
+#define validtag(t) (NUM_TAGS <= (t) && (t) <= L->last_tag)
+
+extern const char *const luaT_eventname[];
+
+
+void luaT_init (lua_State *L);
+void luaT_realtag (lua_State *L, int tag);
+int luaT_tag (const TObject *o);
+int luaT_validevent (int t, int e); /* used by compatibility module */
+
+
+#endif
diff --git a/src/lua/lua.h b/src/lua/lua.h
new file mode 100644
index 00000000..87d64e71
--- /dev/null
+++ b/src/lua/lua.h
@@ -0,0 +1,248 @@
+/*
+** $Id: lua.h,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+** Lua - An Extensible Extension Language
+** TeCGraf: Grupo de Tecnologia em Computacao Grafica, PUC-Rio, Brazil
+** e-mail: lua@tecgraf.puc-rio.br
+** www: http://www.tecgraf.puc-rio.br/lua/
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+
+/* definition of `size_t' */
+#include <stddef.h>
+
+
+/* mark for all API functions */
+#ifndef LUA_API
+#define LUA_API extern
+#endif
+
+
+#define LUA_VERSION "Lua 4.0"
+#define LUA_COPYRIGHT "Copyright (C) 1994-2000 TeCGraf, PUC-Rio"
+#define LUA_AUTHORS "W. Celes, R. Ierusalimschy & L. H. de Figueiredo"
+
+
+/* name of global variable with error handler */
+#define LUA_ERRORMESSAGE "_ERRORMESSAGE"
+
+
+/* pre-defined references */
+#define LUA_NOREF (-2)
+#define LUA_REFNIL (-1)
+#define LUA_REFREGISTRY 0
+
+/* pre-defined tags */
+#define LUA_ANYTAG (-1)
+#define LUA_NOTAG (-2)
+
+
+/* option for multiple returns in lua_call */
+#define LUA_MULTRET (-1)
+
+
+/* minimum stack available for a C function */
+#define LUA_MINSTACK 20
+
+
+/* error codes for lua_do* */
+#define LUA_ERRRUN 1
+#define LUA_ERRFILE 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRERR 5
+
+
+typedef struct lua_State lua_State;
+
+typedef int (*lua_CFunction) (lua_State *L);
+
+/*
+** types returned by `lua_type'
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TUSERDATA 0
+#define LUA_TNIL 1
+#define LUA_TNUMBER 2
+#define LUA_TSTRING 3
+#define LUA_TTABLE 4
+#define LUA_TFUNCTION 5
+
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *lua_open (int stacksize);
+LUA_API void lua_close (lua_State *L);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int lua_gettop (lua_State *L);
+LUA_API void lua_settop (lua_State *L, int index);
+LUA_API void lua_pushvalue (lua_State *L, int index);
+LUA_API void lua_remove (lua_State *L, int index);
+LUA_API void lua_insert (lua_State *L, int index);
+LUA_API int lua_stackspace (lua_State *L);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int lua_type (lua_State *L, int index);
+LUA_API const char *lua_typename (lua_State *L, int t);
+LUA_API int lua_isnumber (lua_State *L, int index);
+LUA_API int lua_isstring (lua_State *L, int index);
+LUA_API int lua_iscfunction (lua_State *L, int index);
+LUA_API int lua_tag (lua_State *L, int index);
+
+LUA_API int lua_equal (lua_State *L, int index1, int index2);
+LUA_API int lua_lessthan (lua_State *L, int index1, int index2);
+
+LUA_API long lua_tonumber (lua_State *L, int index);
+LUA_API const char *lua_tostring (lua_State *L, int index);
+LUA_API size_t lua_strlen (lua_State *L, int index);
+LUA_API lua_CFunction lua_tocfunction (lua_State *L, int index);
+LUA_API void *lua_touserdata (lua_State *L, int index);
+LUA_API const void *lua_topointer (lua_State *L, int index);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void lua_pushnil (lua_State *L);
+LUA_API void lua_pushnumber (lua_State *L, long n);
+LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len);
+LUA_API void lua_pushstring (lua_State *L, const char *s);
+LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
+LUA_API void lua_pushusertag (lua_State *L, void *u, int tag);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API void lua_getglobal (lua_State *L, const char *name);
+LUA_API void lua_gettable (lua_State *L, int index);
+LUA_API void lua_rawget (lua_State *L, int index);
+LUA_API void lua_rawgeti (lua_State *L, int index, int n);
+LUA_API void lua_getglobals (lua_State *L);
+LUA_API void lua_gettagmethod (lua_State *L, int tag, const char *event);
+LUA_API int lua_getref (lua_State *L, int ref);
+LUA_API void lua_newtable (lua_State *L);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void lua_setglobal (lua_State *L, const char *name);
+LUA_API void lua_settable (lua_State *L, int index);
+LUA_API void lua_rawset (lua_State *L, int index);
+LUA_API void lua_rawseti (lua_State *L, int index, int n);
+LUA_API void lua_setglobals (lua_State *L);
+LUA_API void lua_settagmethod (lua_State *L, int tag, const char *event);
+LUA_API int lua_ref (lua_State *L, int lock);
+
+
+/*
+** "do" functions (run Lua code)
+*/
+LUA_API int lua_call (lua_State *L, int nargs, int nresults);
+LUA_API void lua_rawcall (lua_State *L, int nargs, int nresults);
+LUA_API int lua_dofile (lua_State *L, const char *filename);
+LUA_API int lua_dostring (lua_State *L, const char *str);
+LUA_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, const char *name);
+
+/*
+** Garbage-collection functions
+*/
+LUA_API int lua_getgcthreshold (lua_State *L);
+LUA_API int lua_getgccount (lua_State *L);
+LUA_API void lua_setgcthreshold (lua_State *L, int newthreshold);
+
+/*
+** miscellaneous functions
+*/
+LUA_API int lua_newtag (lua_State *L);
+LUA_API int lua_copytagmethods (lua_State *L, int tagto, int tagfrom);
+LUA_API void lua_settag (lua_State *L, int tag);
+
+LUA_API void lua_error (lua_State *L, const char *s);
+
+LUA_API void lua_unref (lua_State *L, int ref);
+
+LUA_API int lua_next (lua_State *L, int index);
+LUA_API int lua_getn (lua_State *L, int index);
+
+LUA_API void lua_concat (lua_State *L, int n);
+
+LUA_API void *lua_newuserdata (lua_State *L, size_t size);
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, f), lua_setglobal(L, n))
+#define lua_pushuserdata(L,u) lua_pushusertag(L, u, 0)
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, f, 0)
+#define lua_clonetag(L,t) lua_copytagmethods(L, lua_newtag(L), (t))
+
+#define lua_isfunction(L,n) (lua_type(L,n) == LUA_TFUNCTION)
+#define lua_istable(L,n) (lua_type(L,n) == LUA_TTABLE)
+#define lua_isuserdata(L,n) (lua_type(L,n) == LUA_TUSERDATA)
+#define lua_isnil(L,n) (lua_type(L,n) == LUA_TNIL)
+#define lua_isnull(L,n) (lua_type(L,n) == LUA_TNONE)
+
+#define lua_getregistry(L) lua_getref(L, LUA_REFREGISTRY)
+
+#endif
+
+
+
+/******************************************************************************
+* Copyright (C) 1994-2000 TeCGraf, PUC-Rio. All rights reserved.
+*
+* Permission is hereby granted, without written agreement and without license
+* or royalty fees, to use, copy, modify, and distribute this software and its
+* documentation for any purpose, including commercial applications, subject to
+* the following conditions:
+*
+* - The above copyright notice and this permission notice shall appear in all
+* copies or substantial portions of this software.
+*
+* - The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software in a
+* product, an acknowledgment in the product documentation would be greatly
+* appreciated (but it is not required).
+*
+* - Altered source versions must be plainly marked as such, and must not be
+* misrepresented as being the original software.
+*
+* The authors specifically disclaim any warranties, including, but not limited
+* to, the implied warranties of merchantability and fitness for a particular
+* purpose. The software provided hereunder is on an "as is" basis, and the
+* authors have no obligation to provide maintenance, support, updates,
+* enhancements, or modifications. In no event shall TeCGraf, PUC-Rio, or the
+* authors be held liable to any party for direct, indirect, special,
+* incidental, or consequential damages arising out of the use of this software
+* and its documentation.
+*
+* The Lua language and this implementation have been entirely designed and
+* written by Waldemar Celes Filho, Roberto Ierusalimschy and
+* Luiz Henrique de Figueiredo at TeCGraf, PUC-Rio.
+*
+* This implementation contains no third-party code.
+******************************************************************************/
+
diff --git a/src/lua/lua2c.lua b/src/lua/lua2c.lua
new file mode 100644
index 00000000..3f8d1716
--- /dev/null
+++ b/src/lua/lua2c.lua
@@ -0,0 +1,29 @@
+-- lua2c.lua
+-- embed lua code into C source
+-- celetecgraf.puc-rio.br
+-- dez 2000
+
+function embed (code)
+
+ -- clean Lua code
+ local s = clean(code)
+ if not s then
+ error("parser error in embedded code")
+ end
+
+ -- convert to C
+ output('\n { /* begin embedded lua code */\n')
+ output(' static unsigned char B[] = {\n ')
+ local t={n=0}
+ local b = gsub(s,'(.)',function (c)
+ local e = ''
+ %t.n=%t.n+1 if %t.n==15 then %t.n=0 e='\n ' end
+ return format('%3u,%s',strbyte(c),e)
+ end
+ )
+ output(b..strbyte(" "))
+ output('\n };\n')
+ output(' lua_dobuffer(tolua_S,(char*)B,sizeof(B),"'..fn..': embedded Lua code");')
+ output(' } /* end of embedded lua code */\n\n')
+end
+
diff --git a/src/lua/luadebug.h b/src/lua/luadebug.h
new file mode 100644
index 00000000..21522445
--- /dev/null
+++ b/src/lua/luadebug.h
@@ -0,0 +1,46 @@
+/*
+** $Id: luadebug.h,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+** Debugging API
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef luadebug_h
+#define luadebug_h
+
+
+#include "lua.h"
+
+typedef struct lua_Debug lua_Debug; /* activation record */
+typedef struct lua_Localvar lua_Localvar;
+
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
+
+LUA_API lua_Hook lua_setcallhook (lua_State *L, lua_Hook func);
+LUA_API lua_Hook lua_setlinehook (lua_State *L, lua_Hook func);
+
+
+#define LUA_IDSIZE 60
+
+struct lua_Debug {
+ const char *event; /* `call', `return' */
+ int currentline; /* (l) */
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) `global', `tag method', `local', `field' */
+ int nups; /* (u) number of upvalues */
+ int linedefined; /* (S) */
+ const char *what; /* (S) `Lua' function, `C' function, Lua `main' */
+ const char *source; /* (S) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ struct lua_TObject *_func; /* active function */
+};
+
+
+#endif
diff --git a/src/lua/lualib.h b/src/lua/lualib.h
new file mode 100644
index 00000000..89f5519f
--- /dev/null
+++ b/src/lua/lualib.h
@@ -0,0 +1,34 @@
+/*
+** $Id: lualib.h,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+** Lua standard libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lualib_h
+#define lualib_h
+
+#include "lua.h"
+
+
+#ifndef LUALIB_API
+#define LUALIB_API extern
+#endif
+
+
+#define LUA_ALERT "_ALERT"
+
+LUALIB_API void lua_baselibopen (lua_State *L);
+LUALIB_API void lua_iolibopen (lua_State *L);
+LUALIB_API void lua_strlibopen (lua_State *L);
+LUALIB_API void lua_mathlibopen (lua_State *L);
+LUALIB_API void lua_dblibopen (lua_State *L);
+
+
+
+/* Auxiliary functions (private) */
+
+const char *luaI_classend (lua_State *L, const char *p);
+int luaI_singlematch (int c, const char *p, const char *ep);
+
+#endif
diff --git a/src/lua/lundump.c b/src/lua/lundump.c
new file mode 100644
index 00000000..7f69573e
--- /dev/null
+++ b/src/lua/lundump.c
@@ -0,0 +1,244 @@
+/*
+** $Id: lundump.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** load bytecodes from files
+** See Copyright Notice in lua.h
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lfunc.h"
+#include "lmem.h"
+#include "lopcodes.h"
+#include "lstring.h"
+#include "lundump.h"
+
+#define LoadByte ezgetc
+
+static const char* ZNAME (ZIO* Z)
+{
+ const char* s=zname(Z);
+ return (*s=='@') ? s+1 : s;
+}
+
+static void unexpectedEOZ (lua_State* L, ZIO* Z)
+{
+ luaO_verror(L,"unexpected end of file in `%.99s'",ZNAME(Z));
+}
+
+static int ezgetc (lua_State* L, ZIO* Z)
+{
+ int c=zgetc(Z);
+ if (c==EOZ) unexpectedEOZ(L,Z);
+ return c;
+}
+
+static void ezread (lua_State* L, ZIO* Z, void* b, int n)
+{
+ int r=zread(Z,b,n);
+ if (r!=0) unexpectedEOZ(L,Z);
+}
+
+static void LoadBlock (lua_State* L, void* b, size_t size, ZIO* Z, int swap)
+{
+ if (swap)
+ {
+ char *p=(char *) b+size-1;
+ int n=size;
+ while (n--) *p--=(char)ezgetc(L,Z);
+ }
+ else
+ ezread(L,Z,b,size);
+}
+
+static void LoadVector (lua_State* L, void* b, int m, size_t size, ZIO* Z, int swap)
+{
+ if (swap)
+ {
+ char *q=(char *) b;
+ while (m--)
+ {
+ char *p=q+size-1;
+ int n=size;
+ while (n--) *p--=(char)ezgetc(L,Z);
+ q+=size;
+ }
+ }
+ else
+ ezread(L,Z,b,m*size);
+}
+
+static int LoadInt (lua_State* L, ZIO* Z, int swap)
+{
+ int x;
+ LoadBlock(L,&x,sizeof(x),Z,swap);
+ return x;
+}
+
+static size_t LoadSize (lua_State* L, ZIO* Z, int swap)
+{
+ size_t x;
+ LoadBlock(L,&x,sizeof(x),Z,swap);
+ return x;
+}
+
+static Number LoadNumber (lua_State* L, ZIO* Z, int swap)
+{
+ Number x;
+ LoadBlock(L,&x,sizeof(x),Z,swap);
+ return x;
+}
+
+static TString* LoadString (lua_State* L, ZIO* Z, int swap)
+{
+ size_t size=LoadSize(L,Z,swap);
+ if (size==0)
+ return NULL;
+ else
+ {
+ char* s=luaO_openspace(L,size);
+ LoadBlock(L,s,size,Z,0);
+ return luaS_newlstr(L,s,size-1); /* remove trailing '\0' */
+ }
+}
+
+static void LoadCode (lua_State* L, Proto* tf, ZIO* Z, int swap)
+{
+ int size=LoadInt(L,Z,swap);
+ tf->code=luaM_newvector(L,size,Instruction);
+ LoadVector(L,tf->code,size,sizeof(*tf->code),Z,swap);
+ if (tf->code[size-1]!=OP_END) luaO_verror(L,"bad code in `%.99s'",ZNAME(Z));
+ luaF_protook(L,tf,size);
+}
+
+static void LoadLocals (lua_State* L, Proto* tf, ZIO* Z, int swap)
+{
+ int i,n;
+ tf->nlocvars=n=LoadInt(L,Z,swap);
+ tf->locvars=luaM_newvector(L,n,LocVar);
+ for (i=0; i<n; i++)
+ {
+ tf->locvars[i].varname=LoadString(L,Z,swap);
+ tf->locvars[i].startpc=LoadInt(L,Z,swap);
+ tf->locvars[i].endpc=LoadInt(L,Z,swap);
+ }
+}
+
+static void LoadLines (lua_State* L, Proto* tf, ZIO* Z, int swap)
+{
+ int n;
+ tf->nlineinfo=n=LoadInt(L,Z,swap);
+ tf->lineinfo=luaM_newvector(L,n,int);
+ LoadVector(L,tf->lineinfo,n,sizeof(*tf->lineinfo),Z,swap);
+}
+
+static Proto* LoadFunction (lua_State* L, ZIO* Z, int swap);
+
+static void LoadConstants (lua_State* L, Proto* tf, ZIO* Z, int swap)
+{
+ int i,n;
+ tf->nkstr=n=LoadInt(L,Z,swap);
+ tf->kstr=luaM_newvector(L,n,TString*);
+ for (i=0; i<n; i++)
+ tf->kstr[i]=LoadString(L,Z,swap);
+ tf->nknum=n=LoadInt(L,Z,swap);
+ tf->knum=luaM_newvector(L,n,Number);
+ LoadVector(L,tf->knum,n,sizeof(*tf->knum),Z,swap);
+ tf->nkproto=n=LoadInt(L,Z,swap);
+ tf->kproto=luaM_newvector(L,n,Proto*);
+ for (i=0; i<n; i++)
+ tf->kproto[i]=LoadFunction(L,Z,swap);
+}
+
+static Proto* LoadFunction (lua_State* L, ZIO* Z, int swap)
+{
+ Proto* tf=luaF_newproto(L);
+ tf->source=LoadString(L,Z,swap);
+ tf->lineDefined=LoadInt(L,Z,swap);
+ tf->numparams=LoadInt(L,Z,swap);
+ tf->is_vararg=LoadByte(L,Z);
+ tf->maxstacksize=LoadInt(L,Z,swap);
+ LoadLocals(L,tf,Z,swap);
+ LoadLines(L,tf,Z,swap);
+ LoadConstants(L,tf,Z,swap);
+ LoadCode(L,tf,Z,swap);
+ return tf;
+}
+
+static void LoadSignature (lua_State* L, ZIO* Z)
+{
+ const char* s=SIGNATURE;
+ while (*s!=0 && ezgetc(L,Z)==*s)
+ ++s;
+ if (*s!=0) luaO_verror(L,"bad signature in `%.99s'",ZNAME(Z));
+}
+
+static void TestSize (lua_State* L, int s, const char* what, ZIO* Z)
+{
+ int r=ezgetc(L,Z);
+ if (r!=s)
+ luaO_verror(L,"virtual machine mismatch in `%.99s':\n"
+ " %.20s is %d but read %d",ZNAME(Z),what,s,r);
+}
+
+#define TESTSIZE(s) TestSize(L,s,#s,Z)
+#define V(v) v/16,v%16
+
+static int LoadHeader (lua_State* L, ZIO* Z)
+{
+ int version,swap;
+ Number f=0,tf=TEST_NUMBER;
+ LoadSignature(L,Z);
+ version=ezgetc(L,Z);
+ if (version>VERSION)
+ luaO_verror(L,"`%.99s' too new:\n"
+ " read version %d.%d; expected at most %d.%d",
+ ZNAME(Z),V(version),V(VERSION));
+ if (version<VERSION0) /* check last major change */
+ luaO_verror(L,"`%.99s' too old:\n"
+ " read version %d.%d; expected at least %d.%d",
+ ZNAME(Z),V(version),V(VERSION));
+ swap=(luaU_endianess()!=ezgetc(L,Z)); /* need to swap bytes? */
+ TESTSIZE(sizeof(int));
+ TESTSIZE(sizeof(size_t));
+ TESTSIZE(sizeof(Instruction));
+ TESTSIZE(SIZE_INSTRUCTION);
+ TESTSIZE(SIZE_OP);
+ TESTSIZE(SIZE_B);
+ TESTSIZE(sizeof(Number));
+ f=LoadNumber(L,Z,swap);
+ if ((long)f!=(long)tf) /* disregard errors in last bit of fraction */
+ luaO_verror(L,"unknown number format in `%.99s':\n"
+ " read " NUMBER_FMT "; expected " NUMBER_FMT, ZNAME(Z),f,tf);
+ return swap;
+}
+
+static Proto* LoadChunk (lua_State* L, ZIO* Z)
+{
+ return LoadFunction(L,Z,LoadHeader(L,Z));
+}
+
+/*
+** load one chunk from a file or buffer
+** return main if ok and NULL at EOF
+*/
+Proto* luaU_undump (lua_State* L, ZIO* Z)
+{
+ Proto* tf=NULL;
+ int c=zgetc(Z);
+ if (c==ID_CHUNK)
+ tf=LoadChunk(L,Z);
+ c=zgetc(Z);
+ if (c!=EOZ)
+ luaO_verror(L,"`%.99s' apparently contains more than one chunk",ZNAME(Z));
+ return tf;
+}
+
+/*
+** find byte order
+*/
+int luaU_endianess (void)
+{
+ int x=1;
+ return *(char*)&x;
+}
diff --git a/src/lua/lundump.h b/src/lua/lundump.h
new file mode 100644
index 00000000..ec394f46
--- /dev/null
+++ b/src/lua/lundump.h
@@ -0,0 +1,35 @@
+/*
+** $Id: lundump.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** load pre-compiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lundump_h
+#define lundump_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+/* load one chunk */
+Proto* luaU_undump (lua_State* L, ZIO* Z);
+
+/* find byte order */
+int luaU_endianess (void);
+
+/* definitions for headers of binary files */
+#define VERSION 0x40 /* last format change was in 4.0 */
+#define VERSION0 0x40 /* last major change was in 4.0 */
+#define ID_CHUNK 27 /* binary files start with ESC... */
+#define SIGNATURE "Lua" /* ...followed by this signature */
+
+/* formats for error messages */
+#define SOURCE_FMT "<%d:%.99s>"
+#define SOURCE tf->lineDefined,tf->source->str
+#define IN_FMT " in %p " SOURCE_FMT
+#define IN tf,SOURCE
+
+/* a multiple of PI for testing native format */
+/* multiplying by 1E8 gives non-trivial integer values */
+#define TEST_NUMBER 3
+
+#endif
diff --git a/src/lua/lvm.c b/src/lua/lvm.c
new file mode 100644
index 00000000..e304e11e
--- /dev/null
+++ b/src/lua/lvm.c
@@ -0,0 +1,710 @@
+/*
+** $Id: lvm.c,v 1.5 2004/06/04 13:42:10 neil Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+#ifdef OLD_ANSI
+#define strcoll(a,b) strcmp(a,b)
+#endif
+
+
+
+/*
+** Extra stack size to run a function:
+** TAG_LINE(1), NAME(1), TM calls(3) (plus some extra...)
+*/
+#define EXTRA_STACK 8
+
+
+
+int luaV_tonumber (TObject *obj) {
+ if (ttype(obj) != LUA_TSTRING)
+ return 1;
+ else {
+ if (!luaO_str2d(svalue(obj), &nvalue(obj)))
+ return 2;
+ ttype(obj) = LUA_TNUMBER;
+ return 0;
+ }
+}
+
+
+int luaV_tostring (lua_State *L, TObject *obj) { /* LUA_NUMBER */
+ if (ttype(obj) != LUA_TNUMBER)
+ return 1;
+ else {
+ char s[32]; /* 16 digits, sign, point and \0 (+ some extra...) */
+ lua_number2str(s, nvalue(obj)); /* convert `s' to number */
+ tsvalue(obj) = luaS_new(L, s);
+ ttype(obj) = LUA_TSTRING;
+ return 0;
+ }
+}
+
+
+static void traceexec (lua_State *L, StkId base, StkId top, lua_Hook linehook) {
+ CallInfo *ci = infovalue(base-1);
+ int *lineinfo = ci->func->f.l->lineinfo;
+ int pc = (*ci->pc - ci->func->f.l->code) - 1;
+ int newline;
+ if (pc == 0) { /* may be first time? */
+ ci->line = 1;
+ ci->refi = 0;
+ ci->lastpc = pc+1; /* make sure it will call linehook */
+ }
+ newline = luaG_getline(lineinfo, pc, ci->line, &ci->refi);
+ /* calls linehook when enters a new line or jumps back (loop) */
+ if (newline != ci->line || pc <= ci->lastpc) {
+ ci->line = newline;
+ L->top = top;
+ luaD_lineHook(L, base-2, newline, linehook);
+ }
+ ci->lastpc = pc;
+}
+
+
+static Closure *luaV_closure (lua_State *L, int nelems) {
+ Closure *c = luaF_newclosure(L, nelems);
+ L->top -= nelems;
+ while (nelems--)
+ c->upvalue[nelems] = *(L->top+nelems);
+ clvalue(L->top) = c;
+ ttype(L->top) = LUA_TFUNCTION;
+ incr_top;
+ return c;
+}
+
+
+void luaV_Cclosure (lua_State *L, lua_CFunction c, int nelems) {
+ Closure *cl = luaV_closure(L, nelems);
+ cl->f.c = c;
+ cl->isC = 1;
+}
+
+
+void luaV_Lclosure (lua_State *L, Proto *l, int nelems) {
+ Closure *cl = luaV_closure(L, nelems);
+ cl->f.l = l;
+ cl->isC = 0;
+}
+
+
+/*
+** Function to index a table.
+** Receives the table at `t' and the key at top.
+*/
+const TObject *luaV_gettable (lua_State *L, StkId t) {
+ Closure *tm;
+ int tg;
+ if (ttype(t) == LUA_TTABLE && /* `t' is a table? */
+ ((tg = hvalue(t)->htag) == LUA_TTABLE || /* with default tag? */
+ luaT_gettm(L, tg, TM_GETTABLE) == NULL)) { /* or no TM? */
+ /* do a primitive get */
+ const TObject *h = luaH_get(L, hvalue(t), L->top-1);
+ /* result is no nil or there is no `index' tag method? */
+ if (ttype(h) != LUA_TNIL || ((tm=luaT_gettm(L, tg, TM_INDEX)) == NULL))
+ return h; /* return result */
+ /* else call `index' tag method */
+ }
+ else { /* try a `gettable' tag method */
+ tm = luaT_gettmbyObj(L, t, TM_GETTABLE);
+ }
+ if (tm != NULL) { /* is there a tag method? */
+ luaD_checkstack(L, 2);
+ *(L->top+1) = *(L->top-1); /* key */
+ *L->top = *t; /* table */
+ clvalue(L->top-1) = tm; /* tag method */
+ ttype(L->top-1) = LUA_TFUNCTION;
+ L->top += 2;
+ luaD_call(L, L->top - 3, 1);
+ return L->top - 1; /* call result */
+ }
+ else { /* no tag method */
+ luaG_typeerror(L, t, "index");
+ return NULL; /* to avoid warnings */
+ }
+}
+
+
+/*
+** Receives table at `t', key at `key' and value at top.
+*/
+void luaV_settable (lua_State *L, StkId t, StkId key) {
+ int tg;
+ if (ttype(t) == LUA_TTABLE && /* `t' is a table? */
+ ((tg = hvalue(t)->htag) == LUA_TTABLE || /* with default tag? */
+ luaT_gettm(L, tg, TM_SETTABLE) == NULL)) /* or no TM? */
+ *luaH_set(L, hvalue(t), key) = *(L->top-1); /* do a primitive set */
+ else { /* try a `settable' tag method */
+ Closure *tm = luaT_gettmbyObj(L, t, TM_SETTABLE);
+ if (tm != NULL) {
+ luaD_checkstack(L, 3);
+ *(L->top+2) = *(L->top-1);
+ *(L->top+1) = *key;
+ *(L->top) = *t;
+ clvalue(L->top-1) = tm;
+ ttype(L->top-1) = LUA_TFUNCTION;
+ L->top += 3;
+ luaD_call(L, L->top - 4, 0); /* call `settable' tag method */
+ }
+ else /* no tag method... */
+ luaG_typeerror(L, t, "index");
+ }
+}
+
+
+const TObject *luaV_getglobal (lua_State *L, TString *s) {
+ const TObject *value = luaH_getstr(L->gt, s);
+ Closure *tm = luaT_gettmbyObj(L, value, TM_GETGLOBAL);
+ if (tm == NULL) /* is there a tag method? */
+ return value; /* default behavior */
+ else { /* tag method */
+ luaD_checkstack(L, 3);
+ clvalue(L->top) = tm;
+ ttype(L->top) = LUA_TFUNCTION;
+ tsvalue(L->top+1) = s; /* global name */
+ ttype(L->top+1) = LUA_TSTRING;
+ *(L->top+2) = *value;
+ L->top += 3;
+ luaD_call(L, L->top - 3, 1);
+ return L->top - 1;
+ }
+}
+
+
+void luaV_setglobal (lua_State *L, TString *s) {
+ const TObject *oldvalue = luaH_getstr(L->gt, s);
+ Closure *tm = luaT_gettmbyObj(L, oldvalue, TM_SETGLOBAL);
+ if (tm == NULL) { /* is there a tag method? */
+ if (oldvalue != &luaO_nilobject) {
+ /* cast to remove `const' is OK, because `oldvalue' != luaO_nilobject */
+ *(TObject *)oldvalue = *(L->top - 1);
+ }
+ else {
+ TObject key;
+ ttype(&key) = LUA_TSTRING;
+ tsvalue(&key) = s;
+ *luaH_set(L, L->gt, &key) = *(L->top - 1);
+ }
+ }
+ else {
+ luaD_checkstack(L, 3);
+ *(L->top+2) = *(L->top-1); /* new value */
+ *(L->top+1) = *oldvalue;
+ ttype(L->top) = LUA_TSTRING;
+ tsvalue(L->top) = s;
+ clvalue(L->top-1) = tm;
+ ttype(L->top-1) = LUA_TFUNCTION;
+ L->top += 3;
+ luaD_call(L, L->top - 4, 0);
+ }
+}
+
+
+static int call_binTM (lua_State *L, StkId top, TMS event) {
+ /* try first operand */
+ Closure *tm = luaT_gettmbyObj(L, top-2, event);
+ L->top = top;
+ if (tm == NULL) {
+ tm = luaT_gettmbyObj(L, top-1, event); /* try second operand */
+ if (tm == NULL) {
+ tm = luaT_gettm(L, 0, event); /* try a `global' method */
+ if (tm == NULL)
+ return 0; /* error */
+ }
+ }
+ lua_pushstring(L, luaT_eventname[event]);
+ luaD_callTM(L, tm, 3, 1);
+ return 1;
+}
+
+
+static void call_arith (lua_State *L, StkId top, TMS event) {
+ if (!call_binTM(L, top, event))
+ luaG_binerror(L, top-2, LUA_TNUMBER, "perform arithmetic on");
+}
+
+
+static int luaV_strcomp (const TString *ls, const TString *rs) {
+ const char *l = ls->str;
+ size_t ll = ls->len;
+ const char *r = rs->str;
+ size_t lr = rs->len;
+ for (;;) {
+ int temp = strcoll(l, r);
+ if (temp != 0) return temp;
+ else { /* strings are equal up to a '\0' */
+ size_t len = strlen(l); /* index of first '\0' in both strings */
+ if (len == ll) /* l is finished? */
+ return (len == lr) ? 0 : -1; /* l is equal or smaller than r */
+ else if (len == lr) /* r is finished? */
+ return 1; /* l is greater than r (because l is not finished) */
+ /* both strings longer than `len'; go on comparing (after the '\0') */
+ len++;
+ l += len; ll -= len; r += len; lr -= len;
+ }
+ }
+}
+
+
+int luaV_lessthan (lua_State *L, const TObject *l, const TObject *r, StkId top) {
+ if (ttype(l) == LUA_TNUMBER && ttype(r) == LUA_TNUMBER)
+ return (nvalue(l) < nvalue(r));
+ else if (ttype(l) == LUA_TSTRING && ttype(r) == LUA_TSTRING)
+ return (luaV_strcomp(tsvalue(l), tsvalue(r)) < 0);
+ else { /* call TM */
+ luaD_checkstack(L, 2);
+ *top++ = *l;
+ *top++ = *r;
+ if (!call_binTM(L, top, TM_LT))
+ luaG_ordererror(L, top-2);
+ L->top--;
+ return (ttype(L->top) != LUA_TNIL);
+ }
+}
+
+
+void luaV_strconc (lua_State *L, int total, StkId top) {
+ do {
+ int n = 2; /* number of elements handled in this pass (at least 2) */
+ if (tostring(L, top-2) || tostring(L, top-1)) {
+ if (!call_binTM(L, top, TM_CONCAT))
+ luaG_binerror(L, top-2, LUA_TSTRING, "concat");
+ }
+ else if (tsvalue(top-1)->len > 0) { /* if len=0, do nothing */
+ /* at least two string values; get as many as possible */
+ lint32 tl = (lint32)tsvalue(top-1)->len +
+ (lint32)tsvalue(top-2)->len;
+ char *buffer;
+ int i;
+ while (n < total && !tostring(L, top-n-1)) { /* collect total length */
+ tl += tsvalue(top-n-1)->len;
+ n++;
+ }
+ if (tl > MAX_SIZET) lua_error(L, "string size overflow");
+ buffer = luaO_openspace(L, tl);
+ tl = 0;
+ for (i=n; i>0; i--) { /* concat all strings */
+ size_t l = tsvalue(top-i)->len;
+ memcpy(buffer+tl, tsvalue(top-i)->str, l);
+ tl += l;
+ }
+ tsvalue(top-n) = luaS_newlstr(L, buffer, tl);
+ }
+ total -= n-1; /* got `n' strings to create 1 new */
+ top -= n-1;
+ } while (total > 1); /* repeat until only 1 result left */
+}
+
+
+static void luaV_pack (lua_State *L, StkId firstelem) {
+ int i;
+ Hash *htab = luaH_new(L, 0);
+ for (i=0; firstelem+i<L->top; i++)
+ *luaH_setint(L, htab, i+1) = *(firstelem+i);
+ /* store counter in field `n' */
+ luaH_setstrnum(L, htab, luaS_new(L, "n"), i);
+ L->top = firstelem; /* remove elements from the stack */
+ ttype(L->top) = LUA_TTABLE;
+ hvalue(L->top) = htab;
+ incr_top;
+}
+
+
+static void adjust_varargs (lua_State *L, StkId base, int nfixargs) {
+ int nvararg = (L->top-base) - nfixargs;
+ if (nvararg < 0)
+ luaD_adjusttop(L, base, nfixargs);
+ luaV_pack(L, base+nfixargs);
+}
+
+
+
+#define dojump(pc, i) { int d = GETARG_S(i); pc += d; }
+
+/*
+** Executes the given Lua function. Parameters are between [base,top).
+** Returns n such that the the results are between [n,top).
+*/
+StkId luaV_execute (lua_State *L, const Closure *cl, StkId base) {
+ const Proto *const tf = cl->f.l;
+ StkId top; /* keep top local, for performance */
+ const Instruction *pc = tf->code;
+ TString **const kstr = tf->kstr;
+ const lua_Hook linehook = L->linehook;
+ infovalue(base-1)->pc = &pc;
+ luaD_checkstack(L, tf->maxstacksize+EXTRA_STACK);
+ if (tf->is_vararg) /* varargs? */
+ adjust_varargs(L, base, tf->numparams);
+ else
+ luaD_adjusttop(L, base, tf->numparams);
+ top = L->top;
+ /* main loop of interpreter */
+ for (;;) {
+ const Instruction i = *pc++;
+ if (linehook)
+ traceexec(L, base, top, linehook);
+ switch (GET_OPCODE(i)) {
+ case OP_END: {
+ L->top = top;
+ return top;
+ }
+ case OP_RETURN: {
+ L->top = top;
+ return base+GETARG_U(i);
+ }
+ case OP_CALL: {
+ int nres = GETARG_B(i);
+ if (nres == MULT_RET) nres = LUA_MULTRET;
+ L->top = top;
+ luaD_call(L, base+GETARG_A(i), nres);
+ top = L->top;
+ break;
+ }
+ case OP_TAILCALL: {
+ L->top = top;
+ luaD_call(L, base+GETARG_A(i), LUA_MULTRET);
+ return base+GETARG_B(i);
+ }
+ case OP_PUSHNIL: {
+ int n = GETARG_U(i);
+ LUA_ASSERT(n>0, "invalid argument");
+ do {
+ ttype(top++) = LUA_TNIL;
+ } while (--n > 0);
+ break;
+ }
+ case OP_POP: {
+ top -= GETARG_U(i);
+ break;
+ }
+ case OP_PUSHINT: {
+ ttype(top) = LUA_TNUMBER;
+ nvalue(top) = (Number)GETARG_S(i);
+ top++;
+ break;
+ }
+ case OP_PUSHSTRING: {
+ ttype(top) = LUA_TSTRING;
+ tsvalue(top) = kstr[GETARG_U(i)];
+ top++;
+ break;
+ }
+ case OP_PUSHNUM: {
+ ttype(top) = LUA_TNUMBER;
+ nvalue(top) = tf->knum[GETARG_U(i)];
+ top++;
+ break;
+ }
+ case OP_PUSHNEGNUM: {
+ ttype(top) = LUA_TNUMBER;
+ nvalue(top) = -tf->knum[GETARG_U(i)];
+ top++;
+ break;
+ }
+ case OP_PUSHUPVALUE: {
+ *top++ = cl->upvalue[GETARG_U(i)];
+ break;
+ }
+ case OP_GETLOCAL: {
+ *top++ = *(base+GETARG_U(i));
+ break;
+ }
+ case OP_GETGLOBAL: {
+ L->top = top;
+ *top = *luaV_getglobal(L, kstr[GETARG_U(i)]);
+ top++;
+ break;
+ }
+ case OP_GETTABLE: {
+ L->top = top;
+ top--;
+ *(top-1) = *luaV_gettable(L, top-1);
+ break;
+ }
+ case OP_GETDOTTED: {
+ ttype(top) = LUA_TSTRING;
+ tsvalue(top) = kstr[GETARG_U(i)];
+ L->top = top+1;
+ *(top-1) = *luaV_gettable(L, top-1);
+ break;
+ }
+ case OP_GETINDEXED: {
+ *top = *(base+GETARG_U(i));
+ L->top = top+1;
+ *(top-1) = *luaV_gettable(L, top-1);
+ break;
+ }
+ case OP_PUSHSELF: {
+ TObject receiver;
+ receiver = *(top-1);
+ ttype(top) = LUA_TSTRING;
+ tsvalue(top++) = kstr[GETARG_U(i)];
+ L->top = top;
+ *(top-2) = *luaV_gettable(L, top-2);
+ *(top-1) = receiver;
+ break;
+ }
+ case OP_CREATETABLE: {
+ L->top = top;
+ luaC_checkGC(L);
+ hvalue(top) = luaH_new(L, GETARG_U(i));
+ ttype(top) = LUA_TTABLE;
+ top++;
+ break;
+ }
+ case OP_SETLOCAL: {
+ *(base+GETARG_U(i)) = *(--top);
+ break;
+ }
+ case OP_SETGLOBAL: {
+ L->top = top;
+ luaV_setglobal(L, kstr[GETARG_U(i)]);
+ top--;
+ break;
+ }
+ case OP_SETTABLE: {
+ StkId t = top-GETARG_A(i);
+ L->top = top;
+ luaV_settable(L, t, t+1);
+ top -= GETARG_B(i); /* pop values */
+ break;
+ }
+ case OP_SETLIST: {
+ int aux = GETARG_A(i) * LFIELDS_PER_FLUSH;
+ int n = GETARG_B(i);
+ Hash *arr = hvalue(top-n-1);
+ L->top = top-n; /* final value of `top' (in case of errors) */
+ for (; n; n--)
+ *luaH_setint(L, arr, n+aux) = *(--top);
+ break;
+ }
+ case OP_SETMAP: {
+ int n = GETARG_U(i);
+ StkId finaltop = top-2*n;
+ Hash *arr = hvalue(finaltop-1);
+ L->top = finaltop; /* final value of `top' (in case of errors) */
+ for (; n; n--) {
+ top-=2;
+ *luaH_set(L, arr, top) = *(top+1);
+ }
+ break;
+ }
+ case OP_ADD: {
+ if (tonumber(top-2) || tonumber(top-1))
+ call_arith(L, top, TM_ADD);
+ else
+ nvalue(top-2) += nvalue(top-1);
+ top--;
+ break;
+ }
+ case OP_ADDI: {
+ if (tonumber(top-1)) {
+ ttype(top) = LUA_TNUMBER;
+ nvalue(top) = (Number)GETARG_S(i);
+ call_arith(L, top+1, TM_ADD);
+ }
+ else
+ nvalue(top-1) += (Number)GETARG_S(i);
+ break;
+ }
+ case OP_SUB: {
+ if (tonumber(top-2) || tonumber(top-1))
+ call_arith(L, top, TM_SUB);
+ else
+ nvalue(top-2) -= nvalue(top-1);
+ top--;
+ break;
+ }
+ case OP_MULT: {
+ if (tonumber(top-2) || tonumber(top-1))
+ call_arith(L, top, TM_MUL);
+ else
+ nvalue(top-2) *= nvalue(top-1);
+ top--;
+ break;
+ }
+ case OP_DIV: {
+ if (tonumber(top-2) || tonumber(top-1))
+ call_arith(L, top, TM_DIV);
+ else
+ nvalue(top-2) /= nvalue(top-1);
+ top--;
+ break;
+ }
+ case OP_POW: {
+ if (!call_binTM(L, top, TM_POW))
+ lua_error(L, "undefined operation");
+ top--;
+ break;
+ }
+ case OP_CONCAT: {
+ int n = GETARG_U(i);
+ luaV_strconc(L, n, top);
+ top -= n-1;
+ L->top = top;
+ luaC_checkGC(L);
+ break;
+ }
+ case OP_MINUS: {
+ if (tonumber(top-1)) {
+ ttype(top) = LUA_TNIL;
+ call_arith(L, top+1, TM_UNM);
+ }
+ else
+ nvalue(top-1) = -nvalue(top-1);
+ break;
+ }
+ case OP_NOT: {
+ ttype(top-1) =
+ (ttype(top-1) == LUA_TNIL) ? LUA_TNUMBER : LUA_TNIL;
+ nvalue(top-1) = 1;
+ break;
+ }
+ case OP_JMPNE: {
+ top -= 2;
+ if (!luaO_equalObj(top, top+1)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPEQ: {
+ top -= 2;
+ if (luaO_equalObj(top, top+1)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPLT: {
+ top -= 2;
+ if (luaV_lessthan(L, top, top+1, top+2)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPLE: { /* a <= b === !(b<a) */
+ top -= 2;
+ if (!luaV_lessthan(L, top+1, top, top+2)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPGT: { /* a > b === (b<a) */
+ top -= 2;
+ if (luaV_lessthan(L, top+1, top, top+2)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPGE: { /* a >= b === !(a<b) */
+ top -= 2;
+ if (!luaV_lessthan(L, top, top+1, top+2)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPT: {
+ if (ttype(--top) != LUA_TNIL) dojump(pc, i);
+ break;
+ }
+ case OP_JMPF: {
+ if (ttype(--top) == LUA_TNIL) dojump(pc, i);
+ break;
+ }
+ case OP_JMPONT: {
+ if (ttype(top-1) == LUA_TNIL) top--;
+ else dojump(pc, i);
+ break;
+ }
+ case OP_JMPONF: {
+ if (ttype(top-1) != LUA_TNIL) top--;
+ else dojump(pc, i);
+ break;
+ }
+ case OP_JMP: {
+ dojump(pc, i);
+ break;
+ }
+ case OP_PUSHNILJMP: {
+ ttype(top++) = LUA_TNIL;
+ pc++;
+ break;
+ }
+ case OP_FORPREP: {
+ if (tonumber(top-1))
+ lua_error(L, "`for' step must be a number");
+ if (tonumber(top-2))
+ lua_error(L, "`for' limit must be a number");
+ if (tonumber(top-3))
+ lua_error(L, "`for' initial value must be a number");
+ if (nvalue(top-1) > 0 ?
+ nvalue(top-3) > nvalue(top-2) :
+ nvalue(top-3) < nvalue(top-2)) { /* `empty' loop? */
+ top -= 3; /* remove control variables */
+ dojump(pc, i); /* jump to loop end */
+ }
+ break;
+ }
+ case OP_FORLOOP: {
+ LUA_ASSERT(ttype(top-1) == LUA_TNUMBER, "invalid step");
+ LUA_ASSERT(ttype(top-2) == LUA_TNUMBER, "invalid limit");
+ if (ttype(top-3) != LUA_TNUMBER)
+ lua_error(L, "`for' index must be a number");
+ nvalue(top-3) += nvalue(top-1); /* increment index */
+ if (nvalue(top-1) > 0 ?
+ nvalue(top-3) > nvalue(top-2) :
+ nvalue(top-3) < nvalue(top-2))
+ top -= 3; /* end loop: remove control variables */
+ else
+ dojump(pc, i); /* repeat loop */
+ break;
+ }
+ case OP_LFORPREP: {
+ Node *node;
+ if (ttype(top-1) != LUA_TTABLE)
+ lua_error(L, "`for' table must be a table");
+ node = luaH_next(L, hvalue(top-1), &luaO_nilobject);
+ if (node == NULL) { /* `empty' loop? */
+ top--; /* remove table */
+ dojump(pc, i); /* jump to loop end */
+ }
+ else {
+ top += 2; /* index,value */
+ *(top-2) = *key(node);
+ *(top-1) = *val(node);
+ }
+ break;
+ }
+ case OP_LFORLOOP: {
+ Node *node;
+ LUA_ASSERT(ttype(top-3) == LUA_TTABLE, "invalid table");
+ node = luaH_next(L, hvalue(top-3), top-2);
+ if (node == NULL) /* end loop? */
+ top -= 3; /* remove table, key, and value */
+ else {
+ *(top-2) = *key(node);
+ *(top-1) = *val(node);
+ dojump(pc, i); /* repeat loop */
+ }
+ break;
+ }
+ case OP_CLOSURE: {
+ L->top = top;
+ luaV_Lclosure(L, tf->kproto[GETARG_A(i)], GETARG_B(i));
+ top = L->top;
+ luaC_checkGC(L);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/lua/lvm.h b/src/lua/lvm.h
new file mode 100644
index 00000000..ac95ae41
--- /dev/null
+++ b/src/lua/lvm.h
@@ -0,0 +1,32 @@
+/*
+** $Id: lvm.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lvm_h
+#define lvm_h
+
+
+#include "ldo.h"
+#include "lobject.h"
+#include "ltm.h"
+
+
+#define tonumber(o) ((ttype(o) != LUA_TNUMBER) && (luaV_tonumber(o) != 0))
+#define tostring(L,o) ((ttype(o) != LUA_TSTRING) && (luaV_tostring(L, o) != 0))
+
+
+int luaV_tonumber (TObject *obj);
+int luaV_tostring (lua_State *L, TObject *obj);
+const TObject *luaV_gettable (lua_State *L, StkId t);
+void luaV_settable (lua_State *L, StkId t, StkId key);
+const TObject *luaV_getglobal (lua_State *L, TString *s);
+void luaV_setglobal (lua_State *L, TString *s);
+StkId luaV_execute (lua_State *L, const Closure *cl, StkId base);
+void luaV_Cclosure (lua_State *L, lua_CFunction c, int nelems);
+void luaV_Lclosure (lua_State *L, Proto *l, int nelems);
+int luaV_lessthan (lua_State *L, const TObject *l, const TObject *r, StkId top);
+void luaV_strconc (lua_State *L, int total, StkId top);
+
+#endif
diff --git a/src/lua/lzio.c b/src/lua/lzio.c
new file mode 100644
index 00000000..84d4a35c
--- /dev/null
+++ b/src/lua/lzio.c
@@ -0,0 +1,84 @@
+/*
+** $Id: lzio.c,v 1.5 2004/06/04 13:42:10 neil Exp $
+** a generic input stream interface
+** See Copyright Notice in lua.h
+*/
+
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lzio.h"
+
+
+
+/* ----------------------------------------------------- memory buffers --- */
+
+static int zmfilbuf (ZIO* z) {
+ (void)z; /* to avoid warnings */
+ return EOZ;
+}
+
+
+ZIO* zmopen (ZIO* z, const char* b, size_t size, const char *name) {
+ if (b==NULL) return NULL;
+ z->n = size;
+ z->p = (const unsigned char *)b;
+ z->filbuf = zmfilbuf;
+ z->u = NULL;
+ z->name = name;
+ return z;
+}
+
+/* ------------------------------------------------------------ strings --- */
+
+ZIO* zsopen (ZIO* z, const char* s, const char *name) {
+ if (s==NULL) return NULL;
+ return zmopen(z, s, strlen(s), name);
+}
+
+/* -------------------------------------------------------------- FILEs --- */
+
+static int zffilbuf (ZIO* z) {
+ size_t n;
+ if (feof((FILE *)z->u)) return EOZ;
+ n = fread(z->buffer, 1, ZBSIZE, (FILE *)z->u);
+ if (n==0) return EOZ;
+ z->n = n-1;
+ z->p = z->buffer;
+ return *(z->p++);
+}
+
+
+ZIO* zFopen (ZIO* z, FILE* f, const char *name) {
+ if (f==NULL) return NULL;
+ z->n = 0;
+ z->p = z->buffer;
+ z->filbuf = zffilbuf;
+ z->u = f;
+ z->name = name;
+ return z;
+}
+
+
+/* --------------------------------------------------------------- read --- */
+size_t zread (ZIO *z, void *b, size_t n) {
+ while (n) {
+ size_t m;
+ if (z->n == 0) {
+ if (z->filbuf(z) == EOZ)
+ return n; /* return number of missing bytes */
+ zungetc(z); /* put result from `filbuf' in the buffer */
+ }
+ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
+ memcpy(b, z->p, m);
+ z->n -= m;
+ z->p += m;
+ b = (char *)b + m;
+ n -= m;
+ }
+ return 0;
+}
diff --git a/src/lua/lzio.h b/src/lua/lzio.h
new file mode 100644
index 00000000..45feeee3
--- /dev/null
+++ b/src/lua/lzio.h
@@ -0,0 +1,53 @@
+/*
+** $Id: lzio.h,v 1.5 2004/06/04 13:42:10 neil Exp $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lzio_h
+#define lzio_h
+
+#include <stdio.h>
+
+
+
+/* For Lua only */
+#define zFopen luaZ_Fopen
+#define zsopen luaZ_sopen
+#define zmopen luaZ_mopen
+#define zread luaZ_read
+
+#define EOZ (-1) /* end of stream */
+
+typedef struct zio ZIO;
+
+ZIO* zFopen (ZIO* z, FILE* f, const char *name); /* open FILEs */
+ZIO* zsopen (ZIO* z, const char* s, const char *name); /* string */
+ZIO* zmopen (ZIO* z, const char* b, size_t size, const char *name); /* memory */
+
+size_t zread (ZIO* z, void* b, size_t n); /* read next n bytes */
+
+#define zgetc(z) (((z)->n--)>0 ? ((int)*(z)->p++): (z)->filbuf(z))
+#define zungetc(z) (++(z)->n,--(z)->p)
+#define zname(z) ((z)->name)
+
+
+
+/* --------- Private Part ------------------ */
+
+#ifndef ZBSIZE
+#define ZBSIZE 256 /* buffer size */
+#endif
+
+struct zio {
+ size_t n; /* bytes still unread */
+ const unsigned char* p; /* current position in buffer */
+ int (*filbuf)(ZIO* z);
+ void* u; /* additional data */
+ const char *name;
+ unsigned char buffer[ZBSIZE]; /* buffer */
+};
+
+
+#endif
diff --git a/src/lua/module.lua b/src/lua/module.lua
new file mode 100644
index 00000000..98dffe6e
--- /dev/null
+++ b/src/lua/module.lua
@@ -0,0 +1,69 @@
+-- tolua: module class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: module.lua,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Module class
+-- Represents module.
+-- The following fields are stored:
+-- {i} = list of objects in the module.
+classModule = {
+ _base = classContainer,
+ type = 'module'
+}
+settag(classModule,tolua_tag)
+
+-- register module
+function classModule:register ()
+ output(' tolua_module(tolua_S,"'..self.name..'");')
+ local i=1
+ while self[i] do
+ self[i]:register()
+ i = i+1
+ end
+end
+
+-- unregister module
+function classModule:unregister ()
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.name..'");')
+end
+
+-- Print method
+function classModule:print (ident,close)
+ print(ident.."Module{")
+ print(ident.." name = '"..self.name.."';")
+ local i=1
+ while self[i] do
+ self[i]:print(ident.." ",",")
+ i = i+1
+ end
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Module (t)
+ t._base = classModule
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects two string representing the module name and body.
+function Module (n,b)
+ local t = _Module(_Container{name=n})
+ push(t)
+ t:parse(strsub(b,2,strlen(b)-1)) -- eliminate braces
+ pop()
+ return t
+end
+
+
diff --git a/src/lua/operator.lua b/src/lua/operator.lua
new file mode 100644
index 00000000..7a42cf1b
--- /dev/null
+++ b/src/lua/operator.lua
@@ -0,0 +1,111 @@
+-- tolua: operator class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: operator.lua,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Operator class
+-- Represents an operator function or a class operator method.
+-- It stores the same fields as functions do plus:
+-- kind = set of character representing the operator (as it appers in C++ code)
+classOperator = {
+ kind = '',
+ _base = classFunction,
+}
+settag(classOperator,tolua_tag)
+
+-- table to transform operator kind into the appropriate tag method name
+_TM = {['+'] = 'operator_add',
+ ['-'] = 'operator_sub',
+ ['*'] = 'operator_mul',
+ ['/'] = 'operator_div',
+ ['<'] = 'operator_lt',
+ ['[]'] = 'operator_get',
+ ['&[]'] = 'operator_set',
+ }
+
+
+-- Print method
+function classOperator:print (ident,close)
+ print(ident.."Operator{")
+ print(ident.." kind = '"..self.kind.."',")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." const = '"..self.const.."',")
+ print(ident.." cname = '"..self.cname.."',")
+ print(ident.." lname = '"..self.lname.."',")
+ print(ident.." args = {")
+ local i=1
+ while self.args[i] do
+ self.args[i]:print(ident.." ",",")
+ i = i+1
+ end
+ print(ident.." }")
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Operator (t)
+ t._base = classOperator
+ settag(t,tolua_tag)
+
+ if t.const ~= 'const' and t.const ~= '' then
+ error("#invalid 'const' specification")
+ end
+
+ append(t)
+ if not t:inclass() then
+ error("#operator can only be defined as class member")
+ end
+
+ t.cname = t:cfuncname("toluaI")..t:overload(t)
+ t.name = t.name..t.kind
+ return t
+end
+
+-- Constructor
+-- Expects three strings: one representing the function declaration,
+-- another representing the argument list, and the third representing
+-- the "const" or empty string.
+function Operator (d,k,a,c)
+ local t = split(strsub(a,2,strlen(a)-1),',') -- eliminate braces
+ local i=1
+ local l = {n=0}
+ while t[i] do
+ l.n = l.n+1
+ l[l.n] = Declaration(t[i],'var')
+ i = i+1
+ end
+ if k == '[]' then
+ d = gsub(d,'&','')
+ elseif k=='&[]' then
+ l.n = l.n+1
+ l[l.n] = Declaration(d,'var')
+ l[l.n].name = 'toluaI_value'
+ end
+ local f = Declaration(d,'func')
+ if k == '[]' and (l[1]==nil or isbasic(l[1].type)~='number') then
+ error('operator[] can only be defined for numeric index.')
+ end
+ f.args = l
+ f.const = c
+ f.kind = gsub(k,"%s","")
+ f.lname = _TM[f.kind]
+ if not f.lname then
+ error("tolua: no support for operator" .. f.kind)
+ end
+ if f.kind == '[]' and not strfind(f.mod,'const') then
+ Operator(d,'&'..k,a,c) -- create correspoding set operator
+ end
+ return _Operator(f)
+end
+
+
diff --git a/src/lua/package.lua b/src/lua/package.lua
new file mode 100644
index 00000000..42dbfaac
--- /dev/null
+++ b/src/lua/package.lua
@@ -0,0 +1,222 @@
+-- tolua: package class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: package.lua,v 1.4 2002/01/03 13:45:08 takkaria Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Package class
+-- Represents the whole package being bound.
+-- The following fields are stored:
+-- {i} = list of objects in the package.
+classPackage = {
+ _base = classContainer,
+ type = 'package'
+}
+settag(classPackage,tolua_tag)
+
+-- Print method
+function classPackage:print ()
+ print("Package: "..self.name)
+ local i=1
+ while self[i] do
+ self[i]:print("","")
+ i = i+1
+ end
+end
+
+function classPackage:preprocess ()
+ self.code = "\n"..self.code -- add a blank sentinel line
+ -- avoid preprocessing verbatim lines
+ local V = {}
+ self.code = gsub(self.code,"\n(%s*%$[^%[%]][^\n]*)",function (v)
+ tinsert(%V,v)
+ return "\n$"..getn(%V).."$"
+ end)
+ -- avoid preprocessing embedded lua code
+ local C = {}
+ self.code = gsub(self.code,"\n%s*%$%[","\1") -- deal with embedded Lua code
+ self.code = gsub(self.code,"\n%s*%$%]","\2")
+ self.code = gsub(self.code,"(%b\1\2)", function (c)
+ tinsert(%C,c)
+ return "\n$["..getn(%C).."]$"
+ end)
+ -- perform global substitution
+
+ self.code = gsub(self.code,"(//[^\n]*)","") -- eliminate C++ comments
+ self.code = gsub(self.code,"/%*","\1")
+ self.code = gsub(self.code,"%*/","\2")
+ self.code = gsub(self.code,"%b\1\2","")
+ self.code = gsub(self.code,"\1","/%*")
+ self.code = gsub(self.code,"\2","%*/")
+ self.code = gsub(self.code,"%s*@%s*","@") -- eliminate spaces beside @
+ self.code = gsub(self.code,"%s?inline(%s)","%1") -- eliminate 'inline' keyword
+ self.code = gsub(self.code,"%s?extern(%s)","%1") -- eliminate 'extern' keyword
+ self.code = gsub(self.code,"%s?virtual(%s)","%1") -- eliminate 'virtual' keyword
+ self.code = gsub(self.code,"public:","") -- eliminate 'public:' keyword
+ self.code = gsub(self.code,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
+ self.code = gsub(self.code,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
+ self.code = gsub(self.code,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*'
+
+ -- restore embedded code
+ self.code = gsub(self.code,"%$%[(%d+)%]%$",function (n)
+ return %C[tonumber(n)]
+ end)
+ -- restore verbatim lines
+ self.code = gsub(self.code,"%$(%d+)%$",function (n)
+ return %V[tonumber(n)]
+ end)
+end
+
+-- translate verbatim
+function classPackage:preamble ()
+ output('/*\n')
+ output('** Lua binding: '..self.name..'\n')
+ output('** Generated automatically by '..TOLUA_VERSION..'\n')
+ output('*/\n\n')
+
+ output('#include "lua/tolua.h"\n\n')
+
+ if not flags.h then
+ output('/* Exported function */')
+ output('int tolua_'..self.name..'_open (lua_State* tolua_S);')
+ output('void tolua_'..self.name..'_close (lua_State* tolua_S);')
+ output('\n')
+ end
+
+ local i=1
+ while self[i] do
+ self[i]:preamble()
+ i = i+1
+ end
+ output('\n')
+ output('/* function to register type */')
+ output('static void toluaI_reg_types (lua_State* tolua_S)')
+ output('{')
+ foreach(_usertype,function(n,v) output(' tolua_usertype(tolua_S,"',v,'");') end)
+ output('}')
+ output('\n')
+
+ output('/* error messages */')
+ output('#define TOLUA_ERR_SELF tolua_error(tolua_S,\"invalid \'self\'\")')
+ output('#define TOLUA_ERR_ASSIGN tolua_error(tolua_S,\"#vinvalid type in variable assignment.\")')
+ output('\n')
+end
+
+-- register package
+-- write package open function
+function classPackage:register ()
+ output("/* Open function */")
+ output("int tolua_"..self.name.."_open (lua_State* tolua_S)")
+ output("{")
+ output(" tolua_open(tolua_S);")
+ output(" toluaI_reg_types(tolua_S);")
+ local i=1
+ while self[i] do
+ self[i]:register()
+ i = i+1
+ end
+ output(" return 1;")
+ output("}")
+end
+
+-- unregister package
+-- write package close function
+function classPackage:unregister ()
+ output("/* Close function */")
+ output("void tolua_"..self.name.."_close (lua_State* tolua_S)")
+ output("{")
+ local i=1
+ while self[i] do
+ self[i]:unregister()
+ i = i+1
+ end
+ output("}")
+end
+
+-- write header file
+function classPackage:header ()
+ output('/*\n') output('** Lua binding: '..self.name..'\n')
+ output('** Generated automatically by '..TOLUA_VERSION..'.\n')
+ output('*/\n\n')
+
+ if not flags.h then
+ output('/* Exported function */')
+ output('int tolua_'..self.name..'_open (lua_State* tolua_S);')
+ output('void tolua_'..self.name..'_close (lua_State* tolua_S);')
+ output('\n')
+ end
+end
+
+-- Internal constructor
+function _Package (t)
+ t._base = classPackage
+ settag(t,tolua_tag)
+ return t
+end
+
+-- Constructor
+-- Expects the base file name.
+-- It assumes the file has extension ".pkg".
+function Package (name)
+ -- read file
+ local code = read("*a")
+ code = "\n" .. code -- add sentinel
+ -- deal with include directive
+ local nsubst
+ repeat
+ code,nsubst = gsub(code,"\n%s*%$<(.-)>%s*\n",function (fn)
+ local fp,msg = openfile(fn,'r')
+ if not fp then
+ error('#'..msg..': '..fn)
+ end
+ local s = read(fp,'*a')
+ closefile(fp)
+ return "\n" .. s
+ end)
+ until nsubst==0
+
+ -- deal with include directive for C/C++ header files
+ local nsubst
+ repeat
+ code,nsubst =
+ gsub(code,"\n%s*%${(.-)}%s*\n",
+ function (fn)
+ local fp,msg = openfile(fn,'r')
+ if not fp then
+ error('#'..msg..': '..fn)
+ end
+ local s = read(fp,'*a')
+ closefile(fp)
+ -- extract marked code
+ local T = {code="\n"}
+ s= "\n" .. s .. "\n" -- add blank lines as sentinels
+ -- extract one-line statments
+ gsub(s,"\n(.-)[Tt][Oo][Ll][Uu][Aa]_[Ee][Xx][Pp][Oo][Rr][Tt][^\n]*\n",
+ function (c) %T.code = %T.code .. c .. "\n" end
+ )
+ -- extract multiline statments
+ gsub(s,"\n[^\n]*[Tt][Oo][Ll][Uu][Aa]_[Bb][Ee][Gg][Ii][Nn][^\n]*"..
+ "(.-)" ..
+ "\n[^\n]*[Tt][Oo][Ll][Uu][Aa]_[Ee][Nn][Dd][^\n]*\n",
+ function (c) %T.code = %T.code .. c .. "\n" end
+ )
+ return T.code
+ end)
+ until nsubst==0
+
+ local t = _Package(_Container{name=name, code=code})
+ push(t)
+ t:preprocess()
+ t:parse(t.code)
+ pop()
+ return t
+end
+
+
diff --git a/src/lua/print.h b/src/lua/print.h
new file mode 100644
index 00000000..49f4c4cb
--- /dev/null
+++ b/src/lua/print.h
@@ -0,0 +1,55 @@
+/*
+** $Id: print.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+** extracted automatically from lopcodes.h by mkprint.lua -- DO NOT EDIT
+** See Copyright Notice in lua.h
+*/
+
+ case OP_END: P_OP("END"); P_NONE; break;
+ case OP_RETURN: P_OP("RETURN"); P_U; break;
+ case OP_CALL: P_OP("CALL"); P_AB; break;
+ case OP_TAILCALL: P_OP("TAILCALL"); P_AB; break;
+ case OP_PUSHNIL: P_OP("PUSHNIL"); P_U; break;
+ case OP_POP: P_OP("POP"); P_U; break;
+ case OP_PUSHINT: P_OP("PUSHINT"); P_S; break;
+ case OP_PUSHSTRING: P_OP("PUSHSTRING"); P_Q; break;
+ case OP_PUSHNUM: P_OP("PUSHNUM"); P_N; break;
+ case OP_PUSHNEGNUM: P_OP("PUSHNEGNUM"); P_N; break;
+ case OP_PUSHUPVALUE: P_OP("PUSHUPVALUE"); P_U; break;
+ case OP_GETLOCAL: P_OP("GETLOCAL"); P_L; break;
+ case OP_GETGLOBAL: P_OP("GETGLOBAL"); P_K; break;
+ case OP_GETTABLE: P_OP("GETTABLE"); P_NONE; break;
+ case OP_GETDOTTED: P_OP("GETDOTTED"); P_K; break;
+ case OP_GETINDEXED: P_OP("GETINDEXED"); P_L; break;
+ case OP_PUSHSELF: P_OP("PUSHSELF"); P_K; break;
+ case OP_CREATETABLE: P_OP("CREATETABLE"); P_U; break;
+ case OP_SETLOCAL: P_OP("SETLOCAL"); P_L; break;
+ case OP_SETGLOBAL: P_OP("SETGLOBAL"); P_K; break;
+ case OP_SETTABLE: P_OP("SETTABLE"); P_AB; break;
+ case OP_SETLIST: P_OP("SETLIST"); P_AB; break;
+ case OP_SETMAP: P_OP("SETMAP"); P_U; break;
+ case OP_ADD: P_OP("ADD"); P_NONE; break;
+ case OP_ADDI: P_OP("ADDI"); P_S; break;
+ case OP_SUB: P_OP("SUB"); P_NONE; break;
+ case OP_MULT: P_OP("MULT"); P_NONE; break;
+ case OP_DIV: P_OP("DIV"); P_NONE; break;
+ case OP_POW: P_OP("POW"); P_NONE; break;
+ case OP_CONCAT: P_OP("CONCAT"); P_U; break;
+ case OP_MINUS: P_OP("MINUS"); P_NONE; break;
+ case OP_NOT: P_OP("NOT"); P_NONE; break;
+ case OP_JMPNE: P_OP("JMPNE"); P_J; break;
+ case OP_JMPEQ: P_OP("JMPEQ"); P_J; break;
+ case OP_JMPLT: P_OP("JMPLT"); P_J; break;
+ case OP_JMPLE: P_OP("JMPLE"); P_J; break;
+ case OP_JMPGT: P_OP("JMPGT"); P_J; break;
+ case OP_JMPGE: P_OP("JMPGE"); P_J; break;
+ case OP_JMPT: P_OP("JMPT"); P_J; break;
+ case OP_JMPF: P_OP("JMPF"); P_J; break;
+ case OP_JMPONT: P_OP("JMPONT"); P_J; break;
+ case OP_JMPONF: P_OP("JMPONF"); P_J; break;
+ case OP_JMP: P_OP("JMP"); P_J; break;
+ case OP_PUSHNILJMP: P_OP("PUSHNILJMP"); P_NONE; break;
+ case OP_FORPREP: P_OP("FORPREP"); P_J; break;
+ case OP_FORLOOP: P_OP("FORLOOP"); P_J; break;
+ case OP_LFORPREP: P_OP("LFORPREP"); P_J; break;
+ case OP_LFORLOOP: P_OP("LFORLOOP"); P_J; break;
+ case OP_CLOSURE: P_OP("CLOSURE"); P_F; break;
diff --git a/src/lua/tolua.c b/src/lua/tolua.c
new file mode 100644
index 00000000..3cb09291
--- /dev/null
+++ b/src/lua/tolua.c
@@ -0,0 +1,149 @@
+/* tolua
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua.c,v 1.4 2004/06/04 13:42:10 neil Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "lua.h"
+#include "lualib.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+static void help (void)
+{
+ fprintf(stderr,"\n"
+ "usage: tolua [options] input_file\n"
+ "\n"
+ "Command line options are:\n"
+ " -v : print version information.\n"
+ " -o file : set output file; default is stdout.\n"
+ " -H file : create include file.\n"
+ " -n name : set package name; default is input file root name.\n"
+ " -p : parse only.\n"
+ " -P : parse and print structure information (for debug).\n"
+ " -h : print this message.\n"
+ "Should the input file be omitted, stdin is assumed;\n"
+ "in that case, the package name must be explicitly set.\n\n"
+ );
+}
+
+static void version (void)
+{
+ fprintf(stderr, "%s (written by W. Celes)\n",TOLUA_VERSION);
+}
+
+static void setfield (lua_State* L, int table, char* f, char* v)
+{
+ lua_pushstring(L,f);
+ lua_pushstring(L,v);
+ lua_settable(L,table);
+}
+
+static void error (char* o)
+{
+ fprintf(stderr,"tolua: unknown option '%s'\n",o);
+ help();
+ exit(1);
+}
+
+int main (int argc, char* argv[])
+{
+ lua_State* L = lua_open(0);
+ lua_baselibopen(L);
+ lua_iolibopen(L);
+ lua_strlibopen(L);
+ lua_pushstring(L,TOLUA_VERSION); lua_setglobal(L,"TOLUA_VERSION");
+
+ if (argc==1)
+ {
+ help();
+ return 0;
+ }
+ else
+ {
+ int i, t;
+ lua_newtable(L);
+ lua_pushvalue(L,-1);
+ lua_setglobal(L,"flags");
+ t = lua_gettop(L);
+ for (i=1; i<argc; ++i)
+ {
+ if (*argv[i] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'v': version(); return 0;
+ case 'h': help(); return 0;
+ case 'p': setfield(L,t,"p",""); break;
+ case 'P': setfield(L,t,"P",""); break;
+ case 'o': setfield(L,t,"o",argv[++i]); break;
+ case 'n': setfield(L,t,"n",argv[++i]); break;
+ case 'H': setfield(L,t,"H",argv[++i]); break;
+ default: error(argv[i]); break;
+ }
+ }
+ else
+ {
+ setfield(L,t,"f",argv[i]);
+ break;
+ }
+ }
+ lua_pop(L,1);
+ }
+
+#if 1
+ {
+ int tolua_tolualua_open(lua_State* L);
+ tolua_tolualua_open(L);
+ }
+#else
+ {
+ int i;
+ char* p;
+ char path[BUFSIZ];
+ char* files[] = {
+ "basic.lua",
+ "feature.lua",
+ "verbatim.lua",
+ "code.lua",
+ "typedef.lua",
+ "container.lua",
+ "package.lua",
+ "module.lua",
+ "define.lua",
+ "enumerate.lua",
+ "declaration.lua",
+ "variable.lua",
+ "array.lua",
+ "function.lua",
+ "operator.lua",
+ "class.lua",
+ "clean.lua",
+ "doit.lua",
+ NULL
+ };
+ strcpy(path,argv[0]);
+ p = strrchr(path,'/');
+ p = (p==NULL) ? path : p+1;
+ for (i=0; files[i]; ++i)
+ {
+ sprintf(p,"%s",files[i]);
+ lua_dofile(L,path);
+ }
+ }
+
+#endif
+ return 0;
+}
diff --git a/src/lua/tolua.h b/src/lua/tolua.h
new file mode 100644
index 00000000..ab86976c
--- /dev/null
+++ b/src/lua/tolua.h
@@ -0,0 +1,127 @@
+/* tolua - Support code for Lua bindings.
+** Written by Waldemar Celes (celes@tecgraf.puc-rio.br)
+** TeCGraf/PUC-Rio
+** http://www.tecgraf.puc-rio.br/~celes/tolua
+** Jul 1998
+** $Id: tolua.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+#ifndef tolua_h
+#define tolua_h
+
+#define TOLUA_VERSION "tolua 4.0a - angband"
+
+
+#include <stdlib.h> /* NULL, malloc, free */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lua.h"
+
+/* Evil hack for C++ bool_ vs. C bool. */
+#ifndef __cplusplus
+typedef unsigned char bool;
+#endif
+
+/*************************************** Exported functions */
+
+int tolua_open (lua_State* L);
+void tolua_using (lua_State* L, int module);
+void tolua_class (lua_State* L, int derived, int base);
+void tolua_instance (lua_State* L, int instance, int classobj);
+void tolua_foreach (lua_State* L, int lo, int f);
+int tolua_tag (lua_State* L, char* type);
+const char* tolua_type (lua_State* L, int lo);
+int tolua_base (lua_State* L, int lo);
+int tolua_cast (lua_State* L, int lo, char* type);
+void tolua_takeownership (lua_State* L, int lo);
+
+
+
+/*************************************** Support functions for binding code */
+
+#define LUA_VALUE int
+#define LUA_NIL 0 /* TODO */
+/*#define TOLUA_NIL (lua_pushnil(),lua_pop())*/
+
+/* Register functions */
+void tolua_globalvar (lua_State* L, char* name, lua_CFunction get, lua_CFunction set);
+void tolua_globalarray (lua_State* L, char* name, lua_CFunction get, lua_CFunction set);
+void tolua_module (lua_State* L, char* name);
+void tolua_cclass (lua_State* L, char* name, char* base);
+void tolua_function (lua_State* L, char* parent, char* name, lua_CFunction func);
+void tolua_constant (lua_State* L, char* parent, char* name, long value);
+void tolua_tablevar
+(lua_State* L, char* table, char* name, lua_CFunction get, lua_CFunction set);
+void tolua_tablearray
+(lua_State* L, char* table, char* name, lua_CFunction get, lua_CFunction set);
+
+
+/* Get and push functions */
+long tolua_getnumber (lua_State* L, int narg, long def);
+const char* tolua_getstring (lua_State* L, int narg, const char* def);
+void* tolua_getuserdata (lua_State* L, int narg, void* def);
+void* tolua_getusertype (lua_State* L, int narg, void* def);
+int tolua_getvalue (lua_State* L, int narg, int def);
+int tolua_getbool (lua_State* L, int narg, int def);
+long tolua_getfieldnumber (lua_State* L, int lo, int index, long def);
+const char* tolua_getfieldstring (lua_State* L, int lo, int index, const char* def);
+void* tolua_getfielduserdata (lua_State* L, int lo, int index, void* def);
+void* tolua_getfieldusertype (lua_State* L, int lo, int index, void* def);
+int tolua_getfieldvalue (lua_State* L, int lo, int index, int def);
+int tolua_getfieldbool (lua_State* L, int lo, int index, int def);
+
+void tolua_pushnumber (lua_State* L, long value);
+void tolua_pushstring (lua_State* L, const char* value);
+void tolua_pushuserdata (lua_State* L, void* value);
+void tolua_pushusertype (lua_State* L, void* value, int tag);
+void tolua_pushvalue (lua_State* L, int lo);
+void tolua_pushbool (lua_State* L, int value);
+void tolua_pushfieldnumber (lua_State* L, int lo, int index, long v);
+void tolua_pushfieldstring (lua_State* L, int lo, int index, char* v);
+void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v);
+void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, int tag);
+void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v);
+void tolua_pushfieldbool (lua_State* L, int lo, int index, int v);
+
+
+/* Type & tag manipulation */
+void tolua_usertype (lua_State* L, char* type);
+#if 0
+void tolua_settag (lua_State* L, char* type, int* tag);
+#endif
+int tolua_istype (lua_State* L, int narg, int tag, int dim);
+int tolua_arrayistype (lua_State* L, int narg, int tag, int dim, int def);
+
+int tolua_isnoobj (lua_State* L, int narg);
+
+/* Tag method manipulation */
+void* tolua_doclone (lua_State* L, void* value, int tag);
+void* tolua_copy (lua_State* L, void* value, unsigned int size);
+
+/* Error handling */
+void tolua_error (lua_State* L, char* msg);
+
+/* Exported variables */
+extern int tolua_tag_nil;
+extern int tolua_tag_number;
+extern int tolua_tag_string;
+extern int tolua_tag_userdata;
+extern int tolua_tag_table;
+extern int tolua_tag_function;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/lua/tolua_bd.c b/src/lua/tolua_bd.c
new file mode 100644
index 00000000..d36a5ce9
--- /dev/null
+++ b/src/lua/tolua_bd.c
@@ -0,0 +1,214 @@
+/*
+** Lua binding: tolua
+** Generated automatically by tolua 4.0b on Tue Nov 14 14:18:50 2000.
+*/
+
+#include "tolua.h"
+
+/* Exported function */
+int tolua_tolua_open (lua_State* tolua_S);
+void tolua_tolua_close (lua_State* tolua_S);
+
+#define tolua_using(module) (tolua_using)(tolua_S,module)
+#define tolua_type(lo) (tolua_type)(tolua_S,lo)
+#define tolua_foreach(lo,f) (tolua_foreach)(tolua_S,lo,f)
+#define tolua_class(derived,base) (tolua_class)(tolua_S,derived,base)
+#define tolua_instance(inst,cobj) (tolua_instance)(tolua_S,inst,cobj)
+#define tolua_base(lo) (tolua_base)(tolua_S,lo)
+#define tolua_cast(lo,type) (tolua_cast)(tolua_S,lo,type)
+#define tolua_takeownership(lo) (tolua_takeownership)(tolua_S,lo)
+
+/* function to register type */
+static void toluaI_reg_types (lua_State* tolua_S)
+{
+}
+
+/* function: tolua_using */
+static int toluaI_tolua_tolua_using00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,2)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE module = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ {
+ tolua_using(module);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'using'.");
+ return 0;
+}
+
+/* function: tolua_type */
+static int toluaI_tolua_tolua_type00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,2)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ {
+ char* toluaI_ret = (char*) tolua_type(lo);
+ tolua_pushstring(tolua_S,toluaI_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'type'.");
+ return 0;
+}
+
+/* function: tolua_foreach */
+static int toluaI_tolua_tolua_foreach00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,3)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ LUA_VALUE f = ((LUA_VALUE) tolua_getvalue(tolua_S,2,0));
+ {
+ tolua_foreach(lo,f);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'foreach'.");
+ return 0;
+}
+
+/* function: tolua_class */
+static int toluaI_tolua_tolua_class00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,3)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE derived = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ LUA_VALUE base = ((LUA_VALUE) tolua_getvalue(tolua_S,2,0));
+ {
+ tolua_class(derived,base);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'class'.");
+ return 0;
+}
+
+/* function: tolua_instance */
+static int toluaI_tolua_tolua_instance00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,3)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE instance = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ LUA_VALUE classobj = ((LUA_VALUE) tolua_getvalue(tolua_S,2,0));
+ {
+ tolua_instance(instance,classobj);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'instance'.");
+ return 0;
+}
+
+/* function: tolua_base */
+static int toluaI_tolua_tolua_base00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,2)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ {
+ LUA_VALUE toluaI_ret = (LUA_VALUE) tolua_base(lo);
+ tolua_pushvalue(tolua_S,toluaI_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'base'.");
+ return 0;
+}
+
+/* function: tolua_cast */
+static int toluaI_tolua_tolua_cast00(lua_State* tolua_S)
+{
+ if (
+ !tolua_istype(tolua_S,2,LUA_TSTRING,0) ||
+ !tolua_isnoobj(tolua_S,3)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ char* type = ((char*) tolua_getstring(tolua_S,2,0));
+ {
+ LUA_VALUE toluaI_ret = (LUA_VALUE) tolua_cast(lo,type);
+ tolua_pushvalue(tolua_S,toluaI_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'cast'.");
+ return 0;
+}
+
+/* function: tolua_takeownership */
+static int toluaI_tolua_tolua_takeownership00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,2)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ {
+ tolua_takeownership(lo);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'takeownership'.");
+ return 0;
+}
+
+/* Open function */
+int tolua_tolua_open (lua_State* tolua_S)
+{
+ tolua_open(tolua_S);
+ toluaI_reg_types(tolua_S);
+ tolua_module(tolua_S,"tolua");
+ tolua_function(tolua_S,"tolua","using",toluaI_tolua_tolua_using00);
+ tolua_function(tolua_S,"tolua","type",toluaI_tolua_tolua_type00);
+ tolua_function(tolua_S,"tolua","foreach",toluaI_tolua_tolua_foreach00);
+ tolua_function(tolua_S,"tolua","class",toluaI_tolua_tolua_class00);
+ tolua_function(tolua_S,"tolua","instance",toluaI_tolua_tolua_instance00);
+ tolua_function(tolua_S,"tolua","base",toluaI_tolua_tolua_base00);
+ tolua_function(tolua_S,"tolua","cast",toluaI_tolua_tolua_cast00);
+ tolua_function(tolua_S,"tolua","takeownership",toluaI_tolua_tolua_takeownership00);
+ return 1;
+}
+/* Close function */
+void tolua_tolua_close (lua_State* tolua_S)
+{
+ lua_pushnil(tolua_S); lua_setglobal(tolua_S,"tolua");
+}
diff --git a/src/lua/tolua_eh.c b/src/lua/tolua_eh.c
new file mode 100644
index 00000000..0709cb4c
--- /dev/null
+++ b/src/lua/tolua_eh.c
@@ -0,0 +1,66 @@
+/* tolua: error handling
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_eh.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_eh.h"
+#include "tolua_rg.h"
+
+#include <stdio.h>
+
+/* registry fiels used to hold current error info
+ - tolua_err_narg: number of wrong argument
+ - tolua_err_provided: provided type
+ - tolua_err_expected: expected type
+*/
+
+void toluaI_eh_set
+(lua_State* L, int narg, const char* provided, const char* expected)
+{
+ lua_pushnumber(L,narg);
+ toluaI_setregistry(L,"tolua_err_narg");
+ lua_pushstring(L,provided);
+ toluaI_setregistry(L,"tolua_err_provided");
+ lua_pushstring(L,expected);
+ toluaI_setregistry(L,"tolua_err_expected");
+}
+
+void tolua_error (lua_State* L, char* msg)
+{
+ if (msg[0]=='#')
+ {
+ static char buffer[BUFSIZ];
+ const char* err_provided;
+ const char* err_expected;
+ toluaI_getregistry(L,"tolua_err_provided");
+ err_provided = lua_tostring(L,-1);
+ toluaI_getregistry(L,"tolua_err_expected");
+ err_expected = lua_tostring(L,-1);
+ lua_pop(L,2);
+ if (msg[1]=='f')
+ {
+ int err_narg;
+ toluaI_getregistry(L,"tolua_err_narg");
+ err_narg = (int)lua_tonumber(L,-1);
+ lua_pop(L,1);
+ sprintf(buffer,"%s\n argument #%d is '%s'; '%s' expected.\n",
+ msg+2,err_narg,err_provided,err_expected);
+ }
+ else if (msg[1]=='v')
+ sprintf(buffer,"%s\n value is '%s'; '%s' expected.\n",
+ msg+2,err_provided,err_expected);
+ lua_error(L,buffer);
+ }
+ else
+ lua_error(L,msg);
+}
diff --git a/src/lua/tolua_eh.h b/src/lua/tolua_eh.h
new file mode 100644
index 00000000..168ba122
--- /dev/null
+++ b/src/lua/tolua_eh.h
@@ -0,0 +1,24 @@
+/* tolua: error handling
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_eh.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+
+#ifndef tolua_eh_h
+#define tolua_eh_h
+
+void toluaI_eh_set
+(lua_State* L, int narg, const char* provided, const char* expected);
+
+
+#endif
diff --git a/src/lua/tolua_gp.c b/src/lua/tolua_gp.c
new file mode 100644
index 00000000..77ff0c26
--- /dev/null
+++ b/src/lua/tolua_gp.c
@@ -0,0 +1,197 @@
+/* tolua: get & push functions.
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_gp.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_tm.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+long tolua_getnumber (lua_State* L, int narg, long def)
+{
+ return lua_gettop(L)<abs(narg) ? def : lua_tonumber(L,narg);
+}
+
+const char* tolua_getstring (lua_State* L, int narg, const char* def)
+{
+ return lua_gettop(L)<abs(narg) ? def : lua_tostring(L,narg);
+}
+
+void* tolua_getuserdata (lua_State* L, int narg, void* def)
+{
+ return lua_gettop(L)<abs(narg) ? def : lua_touserdata(L,narg);
+}
+
+void* tolua_getusertype (lua_State* L, int narg, void* def)
+{
+ return lua_gettop(L)<abs(narg) ? def : lua_touserdata(L,narg);
+}
+
+int tolua_getvalue (lua_State* L, int narg, int def)
+{
+ return lua_gettop(L)<abs(narg) ? def : narg;
+}
+
+int tolua_getbool (lua_State* L, int narg, int def)
+{
+ return lua_gettop(L)<abs(narg) ?
+ def :
+ lua_isnil(L,narg) ? 0 : lua_tonumber(L,narg)!=0;
+}
+
+long tolua_getfieldnumber (lua_State* L, int lo, int index, long def)
+{
+ long v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lua_tonumber(L,-1);
+ lua_pop(L,1);
+ return v;
+}
+
+const char* tolua_getfieldstring
+(lua_State* L, int lo, int index, const char* def)
+{
+ const char* v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lua_tostring(L,-1);
+ lua_pop(L,1);
+ return v;
+}
+
+void* tolua_getfielduserdata (lua_State* L, int lo, int index, void* def)
+{
+ void* v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lua_touserdata(L,-1);
+ lua_pop(L,1);
+ return v;
+}
+
+void* tolua_getfieldusertype (lua_State* L, int lo, int index, void* def)
+{
+ void* v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lua_touserdata(L,-1);
+ lua_pop(L,1);
+ return v;
+}
+
+int tolua_getfieldvalue (lua_State* L, int lo, int index, int def)
+{
+ int v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lo;
+ lua_pop(L,1);
+ return v;
+}
+
+int tolua_getfieldbool (lua_State* L, int lo, int index, int def)
+{
+ int v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? 0 : lua_tonumber(L,-1)!=0;
+ lua_pop(L,1);
+ return v;
+}
+
+void tolua_pushnumber (lua_State* L, long value)
+{
+ lua_pushnumber(L,value);
+}
+
+void tolua_pushstring (lua_State* L, const char* value)
+{
+ if (value == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushstring(L,value);
+}
+
+void tolua_pushuserdata (lua_State* L, void* value)
+{
+ if (value == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushuserdata(L,value);
+}
+
+void tolua_pushusertype (lua_State* L, void* value, int tag)
+{
+ if (value == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushusertag(L,value,tag);
+}
+
+void tolua_pushvalue (lua_State* L, int lo)
+{
+ lua_pushvalue(L,lo);
+}
+
+void tolua_pushbool (lua_State* L, int value)
+{
+ if (value)
+ lua_pushnumber(L,(long)value);
+ else
+ lua_pushnil(L);
+}
+
+void tolua_pushfieldnumber (lua_State* L, int lo, int index, long v)
+{
+ lua_pushnumber(L,index);
+ tolua_pushnumber(L,v);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfieldstring (lua_State* L, int lo, int index, char* v)
+{
+ lua_pushnumber(L,index);
+ tolua_pushstring(L,v);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v)
+{
+ lua_pushnumber(L,index);
+ tolua_pushuserdata(L,v);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, int tag)
+{
+ lua_pushnumber(L,index);
+ tolua_pushusertype(L,v,tag);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v)
+{
+ lua_pushnumber(L,index);
+ lua_pushvalue(L,v);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfieldbool (lua_State* L, int lo, int index, int v)
+{
+ lua_pushnumber(L,index);
+ tolua_pushbool(L,v);
+ lua_settable(L,lo);
+}
+
diff --git a/src/lua/tolua_lb.c b/src/lua/tolua_lb.c
new file mode 100644
index 00000000..5fd4c337
--- /dev/null
+++ b/src/lua/tolua_lb.c
@@ -0,0 +1,160 @@
+/* tolua
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_lb.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_rg.h"
+#include "tolua_tm.h"
+#include "tolua_tt.h"
+
+
+int tolua_open (lua_State* L)
+{
+ int tolua_tolua_open (lua_State* L);
+ /* check if alread opened */
+ toluaI_getregistry(L,"TOLUA");
+ if (lua_isnil(L,-1))
+ {
+ lua_pushnumber(L,1);
+ toluaI_setregistry(L,"TOLUA");
+ toluaI_tt_init(L);
+ toluaI_tm_init(L);
+ lua_getglobal(L,"foreach");
+ toluaI_setregistry(L,"tolua_orig_foreach");
+ tolua_tolua_open(L); /* opens tolua own binding */
+ }
+ lua_pop(L,1);
+ return 1;
+}
+
+void tolua_using (lua_State* L, int module)
+{
+ toluaI_tm_using(L,module);
+}
+
+void tolua_class (lua_State* L, int derived, int base)
+{
+ int tag = lua_newtag(L); /* new tag of instances of that class */
+ toluaI_tm_setclass(L,derived);
+ toluaI_tm_linstance(L,tag,derived);
+ lua_pushvalue(L,derived);
+ lua_pushstring(L,".base");
+ lua_pushvalue(L,base);
+ lua_rawset(L,-3);
+ lua_pushstring(L,".itag");
+ lua_pushnumber(L,tag);
+ lua_rawset(L,-3);
+ lua_pop(L,1);
+}
+
+void tolua_instance (lua_State* L, int instance, int classobj)
+{
+ int tag;
+ lua_pushvalue(L,classobj);
+ lua_pushstring(L,".itag");
+ lua_gettable(L,-2);
+ tag = (int) lua_tonumber(L,-1);
+ lua_pop(L,2); /* number and table */
+ if (tag==0)
+ tolua_error(L,"unregistered 'classobj' in function 'tolua_instance'.");
+ lua_pushvalue(L,instance);
+ lua_settag(L,tag);
+ lua_pop(L,1);
+}
+
+static int filter (lua_State* L)
+{
+ int n = 1; /* name */
+ int v = 2; /* value */
+ int f = lua_gettop(L); /* function */
+ /* do not pass string fields starting with a dot */
+ if (!lua_isstring(L,n) || *lua_tostring(L,n)!='.')
+ {
+ lua_pushvalue(L,f);
+ lua_pushvalue(L,n);
+ lua_pushvalue(L,v);
+ lua_call(L,2,1);
+ }
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+void tolua_foreach (lua_State* L, int lo, int f)
+{
+ if (toluaI_tt_isusertype(L,lo))
+ {
+ toluaI_tm_pushmate(L,lo);
+ if (lua_isnil(L,-1))
+ return; /* no field in mate table */
+ else
+ lo = lua_gettop(L);
+ }
+ toluaI_getregistry(L,"tolua_orig_foreach");
+ lua_pushvalue(L,lo);
+ lua_pushvalue(L,f);
+ lua_pushcclosure(L,filter,1);
+ lua_call(L,2,1);
+}
+
+const char* tolua_type (lua_State* L, int lo)
+{
+ return toluaI_tt_getobjtype(L,lo);
+}
+
+int tolua_tag (lua_State* L, char* type)
+{
+ return toluaI_tt_gettag(L,type);
+}
+
+int tolua_base (lua_State* L, int lo)
+{
+ if (toluaI_tt_isusertype(L,lo))
+ {
+ toluaI_tm_pushclass(L,lo);
+ return lua_gettop(L);
+ }
+ else if (lua_istable(L,lo))
+ {
+ lua_pushvalue(L,lo);
+ lua_pushstring(L,".base");
+ lua_rawget(L,-2);
+ return -1;
+ }
+ else
+ return 0;
+}
+
+int tolua_cast (lua_State* L, int lo, char* type)
+{
+ if (lua_isuserdata(L,lo))
+ {
+ tolua_pushusertype(L,lua_touserdata(L,lo),toluaI_tt_gettag(L,type));
+ return -1;
+ }
+ else
+ return 0;
+}
+
+void tolua_takeownership (lua_State* L, int lo)
+{
+ if (toluaI_tt_isusertype(L,lo))
+ {
+ /* force garbage collection to avoid C to reuse a to-be-collected address */
+ lua_setgcthreshold(L,0);
+ tolua_doclone(L,lua_touserdata(L,lo),lua_tag(L,lo));
+ }
+ else
+ tolua_error(L,"cannot take ownership of specified obejct.");
+}
+
diff --git a/src/lua/tolua_rg.c b/src/lua/tolua_rg.c
new file mode 100644
index 00000000..4337e9f9
--- /dev/null
+++ b/src/lua/tolua_rg.c
@@ -0,0 +1,243 @@
+/* tolua: register functions
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_rg.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include <stdio.h>
+
+#include "tolua.h"
+#include "tolua_rg.h"
+#include "tolua_tm.h"
+#include "tolua_tt.h"
+
+void tolua_globalvar (lua_State* L, char* name, lua_CFunction get, lua_CFunction set)
+{
+ lua_newtable(L);
+ lua_pushstring(L,".get");
+ lua_pushcfunction(L,get);
+ lua_settable(L,-3);
+ if (set)
+ {
+ lua_pushstring(L,".set");
+ lua_pushcfunction(L,set);
+ lua_settable(L,-3);
+ }
+ lua_pushvalue(L,-1); /* duplicate top */
+ lua_setglobal(L,name);
+ toluaI_tm_global(L,lua_gettop(L));
+ lua_pop(L,1);
+}
+
+static int toluaI_const_global_array (lua_State* L)
+{
+ lua_error(L,"value of const array cannot be changed");
+ return 0;
+}
+
+
+void tolua_globalarray (lua_State* L,char* name, lua_CFunction get, lua_CFunction set)
+{
+ int tag = lua_newtag(L);
+ lua_newtable(L);
+ lua_settag(L,tag);
+ lua_setglobal(L,name);
+
+ lua_pushcfunction(L,get);
+ lua_settagmethod(L,tag,"gettable");
+ if (set)
+ lua_pushcfunction(L,set);
+ else
+ lua_pushcfunction(L,toluaI_const_global_array);
+ lua_settagmethod(L,tag,"settable");
+}
+
+void tolua_tablevar
+(lua_State* L, char* table, char* name, lua_CFunction get, lua_CFunction set)
+{
+ lua_getglobal(L,table);
+
+ lua_pushstring(L,".get");
+ lua_gettable(L,-2);
+ lua_pushstring(L,name);
+ lua_pushcfunction(L,get);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ if (set)
+ {
+ lua_pushstring(L,".set");
+ lua_gettable(L,-2);
+ lua_pushstring(L,name);
+ lua_pushcfunction(L,set);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ }
+
+ lua_pop(L,1);
+}
+
+static int toluaI_get_array (lua_State* L)
+{
+ void* self = tolua_getuserdata(L,1,0);
+ const char* field = tolua_getstring(L,2,0);
+
+ if (!field)
+ tolua_error(L,"invalid 'field' in accessing array");
+ if (!self)
+ {
+ static char msg[BUFSIZ];
+ sprintf(msg,"invalid 'self' in accessing array '%s'",field);
+ tolua_error(L,msg);
+ }
+ toluaI_getregistry(L,"tolua_tbl_itype");
+ lua_pushnumber(L,lua_tag(L,1));
+ lua_gettable(L,-2);
+ lua_getglobal(L,lua_tostring(L,-1));
+ lua_pushstring(L,".array");
+ lua_gettable(L,-2);
+ lua_pushvalue(L,2); /* field */
+ lua_gettable(L,-2);
+ lua_pushstring(L,".self");
+ lua_pushvalue(L,1); /* self */
+ lua_rawset(L,-3);
+ return 1;
+}
+
+static int toluaI_const_array (lua_State* L)
+{
+ lua_error(L,"value of const field cannot be changed");
+ return 0;
+}
+
+void tolua_tablearray
+(lua_State* L, char* table, char* name, lua_CFunction get, lua_CFunction set)
+{
+ int tag = lua_newtag(L);
+ lua_getglobal(L,table);
+ lua_pushstring(L,".array");
+ lua_rawget(L,-2);
+ lua_pushstring(L,name);
+ lua_newtable(L);
+ lua_settag(L,tag);
+ lua_settable(L,-3);
+ lua_pop(L,2);
+
+ lua_pushcfunction(L,get);
+ lua_settagmethod(L,tag,"gettable");
+ if (set)
+ lua_pushcfunction(L,set);
+ else
+ lua_pushcfunction(L,toluaI_const_array);
+ lua_settagmethod(L,tag,"settable");
+
+ tolua_tablevar(L,table,name,toluaI_get_array,NULL);
+}
+
+void tolua_module (lua_State* L, char* name)
+{
+ lua_getglobal(L,name);
+ if (!lua_istable(L,-1))
+ {
+ lua_newtable(L);
+ lua_pushstring(L,".get");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ lua_pushstring(L,".set");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ lua_pushvalue(L,-1); /* duplicate top */
+ lua_setglobal(L,name);
+ toluaI_tm_module(L,lua_gettop(L));
+ lua_pop(L,1);
+ }
+ lua_pop(L,1);
+}
+
+void tolua_cclass (lua_State* L, char* name, char* base)
+{
+ int t;
+ lua_newtable(L);
+ lua_pushstring(L,".get");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ lua_pushstring(L,".set");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ lua_pushstring(L,".array");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ if (*base != 0)
+ {
+ lua_pushstring(L,".base");
+ lua_getglobal(L,base);
+ lua_rawset(L,-3);
+ }
+ lua_pushvalue(L,-1); /* duplicate top */
+ lua_setglobal(L,name);
+ t = lua_gettop(L);
+ toluaI_tm_class(L,t,name);
+ toluaI_tt_class(L,t,name,base);
+ lua_pop(L,1);
+}
+
+
+void tolua_function (lua_State* L, char* parent, char* name, lua_CFunction func)
+{
+ if (parent==NULL)
+ {
+ lua_pushcfunction(L,func);
+ lua_setglobal(L,name);
+ }
+ else
+ {
+ lua_getglobal(L,parent);
+ lua_pushstring(L,name);
+ lua_pushcfunction(L,func);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ }
+}
+
+void tolua_constant (lua_State* L, char* parent, char* name, long value)
+{
+ if (parent==NULL)
+ {
+ lua_pushnumber(L,value);
+ lua_setglobal(L,name);
+ }
+ else
+ {
+ lua_getglobal(L,parent);
+ lua_pushstring(L,name);
+ lua_pushnumber(L,value);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ }
+}
+
+void toluaI_setregistry (lua_State* L, char* field)
+{
+ lua_getregistry(L);
+ lua_insert(L,-2);
+ lua_pushstring(L,field);
+ lua_insert(L,-2);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+}
+
+void toluaI_getregistry (lua_State* L, char* field)
+{
+ lua_getregistry(L);
+ lua_pushstring(L,field);
+ lua_gettable(L,-2);
+ lua_insert(L,-2);
+ lua_pop(L,1);
+}
diff --git a/src/lua/tolua_rg.h b/src/lua/tolua_rg.h
new file mode 100644
index 00000000..0feb6078
--- /dev/null
+++ b/src/lua/tolua_rg.h
@@ -0,0 +1,22 @@
+/* tolua: register functions
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Nov 200
+** $Id: tolua_rg.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+#ifndef tolua_rg_h
+#define tolua_rg_h
+
+void toluaI_setregistry (lua_State* L, char* field);
+void toluaI_getregistry (lua_State* L, char* field);
+
+#endif
diff --git a/src/lua/tolua_tm.c b/src/lua/tolua_tm.c
new file mode 100644
index 00000000..8fd7b28d
--- /dev/null
+++ b/src/lua/tolua_tm.c
@@ -0,0 +1,585 @@
+/* tolua: tag methods
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_tm.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_tm.h"
+#include "tolua_tt.h"
+#include "tolua_rg.h"
+
+#include <string.h>
+
+
+
+/* Global tables created in Lua registry:
+ tolua_tbl_class: indexed by instance tags, stores the class tables.
+ tolua_tbl_clone: indexed by memory address, stores the tag indicanting
+ it is a clone.
+ tolua_tbl_mate: indexed by memory address, stores the associate instance
+ table.
+
+ General tags stored in Lua registry:
+ tolua_tag_global;
+ tolua_tag_module;
+ tolua_tag_class;
+ tolua_tag_instance;
+ tolua_tag_linstance;
+ tolua_tag_indirect;
+*/
+
+/* internal function prototype */
+static void setmethods (lua_State* L);
+
+static void settag (lua_State* L, int lo, char* tag_registry_field)
+{
+ toluaI_getregistry(L,tag_registry_field);
+ lua_pushvalue(L,lo);
+ lua_settag(L,(int)lua_tonumber(L,-2));
+ lua_pop(L,2);
+}
+
+void toluaI_tm_global (lua_State* L, int lo)
+{
+ settag(L,lo,"tolua_tag_global");
+}
+
+void toluaI_tm_module (lua_State* L, int lo)
+{
+ settag(L,lo,"tolua_tag_module");
+}
+
+void toluaI_tm_setclass (lua_State* L, int lo)
+{
+ settag(L,lo,"tolua_tag_class");
+}
+
+void toluaI_tm_class (lua_State* L, int lo, char* name)
+{
+ int tag_class;
+ int tag = lua_newtag(L);
+ char* type = toluaI_tt_concat("class ",name);
+ toluaI_getregistry(L,"tolua_tag_class");
+ tag_class = (int)lua_tonumber(L,-1);
+ lua_copytagmethods(L,tag,tag_class);
+ toluaI_tt_register(L,tag,type);
+ toluaI_tt_sethierarchy(L,tag,tag_class);
+ lua_pushvalue(L,lo);
+ lua_settag(L,tag);
+ lua_pop(L,2); /* tag_class and lo */
+}
+
+void toluaI_tm_instance (lua_State* L, int tag, int lo)
+{
+ toluaI_getregistry(L,"tolua_tbl_class");
+ lua_pushnumber(L,tag);
+ lua_pushvalue(L,lo);
+ lua_settable(L,-3);
+ toluaI_getregistry(L,"tolua_tag_instance");
+ lua_copytagmethods(L,tag,(int)lua_tonumber(L,-1));
+ lua_pop(L,2); /* tbl_class and tag_instance */
+}
+
+void toluaI_tm_linstance (lua_State* L, int tag, int lo)
+{
+ toluaI_getregistry(L,"tolua_tbl_class");
+ lua_pushnumber(L,tag);
+ lua_pushvalue(L,lo);
+ lua_settable(L,-3);
+ toluaI_getregistry(L,"tolua_tag_linstance");
+ lua_copytagmethods(L,tag,(int)lua_tonumber(L,-1));
+ lua_pop(L,2); /* tbl_class and tag_linstance */
+}
+
+void* tolua_doclone (lua_State* L, void* value, int tag)
+{
+ toluaI_getregistry(L,"tolua_tbl_clone");
+ lua_pushuserdata(L,value);
+ lua_pushnumber(L,tag);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ return value;
+}
+
+void* tolua_copy (lua_State* L, void* value, unsigned int size)
+{
+ void* clone = (void*)malloc(size);
+ if (clone)
+ memcpy(clone,value,size);
+ else
+ tolua_error(L,"insuficient memory");
+ return clone;
+}
+
+static void toluaI_tm_undoclone (lua_State* L, int tag, void* clone)
+{
+ toluaI_getregistry(L,"tolua_tbl_clone");
+ lua_pushuserdata(L,clone);
+ lua_gettable(L,-2);
+ if (lua_isnumber(L,-1) && lua_tonumber(L,-1)==tag)
+ {
+ lua_pushuserdata(L,clone);
+ lua_pushnil(L);
+ lua_settable(L,-4);
+
+ /* get base class */
+ toluaI_getregistry(L,"tolua_tbl_class");
+ lua_pushnumber(L,tag);
+ lua_rawget(L,-2);
+
+ /* look for destructor */
+ lua_pushstring(L,"delete");
+ lua_gettable(L,-2);
+ if (lua_iscfunction(L,-1))
+ {
+ lua_pushusertag(L,clone,tag);
+ lua_call(L,1,0);
+ }
+ else
+ {
+ free(clone); /* no destructor: use raw free */
+ lua_pop(L,1); /* the nil function value */
+ }
+ lua_pop(L,2); /* tbl_class and class method table */
+ }
+ lua_pop(L,2); /* table and value */
+}
+
+void toluaI_tm_pushmate (lua_State* L, int lo)
+{
+ toluaI_getregistry(L,"tolua_tbl_mate");
+ lua_pushvalue(L,lo);
+ lua_rawget(L,-2);
+ lua_insert(L,-2);
+ lua_pop(L,1);
+}
+
+void toluaI_tm_pushclass (lua_State* L, int lo)
+{
+ toluaI_getregistry(L,"tolua_tbl_class");
+ lua_pushnumber(L,lua_tag(L,lo));
+ lua_rawget(L,-2);
+ lua_insert(L,-2);
+ lua_pop(L,1);
+}
+
+int toluaI_gettag (lua_State* L, char* tagname)
+{
+ int tag;
+ toluaI_getregistry(L,tagname);
+ tag = (int)lua_tonumber(L,-1);
+ lua_pop(L,1);
+ return tag;
+}
+
+void toluaI_tm_init (lua_State* L)
+{
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_class");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_clone");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_mate");
+
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_global");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_module");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_class");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_instance");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_linstance");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_indirect");
+
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_global"),"generic variable");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_module"),"generic module");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_class"),"generic class");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_indirect"),"generic indirect");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_instance"),"generic instance");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_linstance"),"generic lua instance");
+
+ /* allows modules and classes to be used as ordinary tables */
+ toluaI_tt_sethierarchy(L,toluaI_gettag(L,"tolua_tag_module"),tolua_tag_table);
+ toluaI_tt_sethierarchy(L,toluaI_gettag(L,"tolua_tag_class"),tolua_tag_table);
+
+ setmethods(L);
+}
+
+static int map (lua_State* L)
+{
+ int m = lua_gettop(L);
+ /* do not pass string fields starting with a dot */
+ if (!lua_isstring(L,1) || *lua_tostring(L,1)!='.')
+ {
+ lua_getglobals(L);
+ lua_pushvalue(L,1);
+ lua_pushvalue(L,m);
+ lua_rawset(L,-3);
+ lua_pop(L,1);
+ }
+ return 0;
+}
+
+void toluaI_tm_using (lua_State* L, int module)
+{
+ lua_newtable(L);
+ lua_settag(L,toluaI_gettag(L,"tolua_tag_indirect"));
+ lua_pushstring(L,".module");
+ lua_pushvalue(L,module);
+ lua_settable(L,-3);
+
+ lua_getglobal(L,"foreach");
+ lua_pushvalue(L,module);
+ lua_pushvalue(L,-3);
+ lua_pushcclosure(L,map,1);
+ lua_call(L,2,0);
+
+ lua_getglobal(L,"foreach");
+ lua_pushvalue(L,module);
+ lua_pushstring(L,".get");
+ lua_gettable(L,-2);
+ lua_insert(L,-2);
+ lua_pop(L,1); /* module table */
+ lua_pushvalue(L,-3);
+ lua_pushcclosure(L,map,1);
+ lua_call(L,2,0);
+ lua_pop(L,1); /* indirect table */
+}
+
+/********************************************************** tag methods */
+
+/* tag methods coded in C */
+
+/* generic gettable */
+static void oo_gettable (lua_State* L, int table, int base, int index)
+{
+ while (lua_istable(L,base))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,base);
+ if (!lua_isnil(L,-1))
+ return; /* returned value already on the top */
+ else if (lua_isnumber(L,index))
+ {
+ lua_pushstring(L,"operator_get");
+ lua_rawget(L,base);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,table);
+ lua_pushvalue(L,index);
+ lua_call(L,2,1);
+ return;
+ }
+ }
+ else
+ {
+ lua_pushstring(L,".get");
+ lua_rawget(L,base);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,table);
+ lua_pushvalue(L,index); /* need to access array field (?) */
+ lua_call(L,2,1);
+ return;
+ }
+ }
+ }
+ lua_pushstring(L,".base"); lua_rawget(L,base);
+ base = lua_gettop(L);
+ }
+ lua_pushnil(L);
+}
+
+/* generic settable */
+static int oo_settable (lua_State* L, int table, int base, int index, int value)
+{
+ while (lua_istable(L,base))
+ {
+ lua_pushstring(L,".set");
+ lua_rawget(L,base);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,table);
+ lua_pushvalue(L,value);
+ lua_call(L,2,0);
+ return 1;
+ }
+ }
+ lua_pushstring(L,".base"); lua_rawget(L,base);
+ base = lua_gettop(L);
+ }
+ return 0;
+}
+
+/* class tag methods */
+static int class_index (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ oo_gettable(L,table,table,index);
+ return 1;
+}
+static int class_settable (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ int value = 3;
+ if (oo_settable(L,table,table,index,value) == 0)
+ {
+ lua_pushvalue(L,table);
+ lua_pushvalue(L,index);
+ lua_pushvalue(L,value);
+ lua_rawset(L,-3);
+ }
+ return 0;
+}
+
+/* instance tags */
+static int instance_gettable (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ toluaI_tm_pushmate(L,table); /* pushes mate */
+ if (!lua_isnil(L,-1)) /* if there's a mate table */
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1)) /* if field in mate table exists */
+ return 1;
+ }
+ toluaI_tm_pushclass(L,table); /* pushes base */
+ oo_gettable(L,table,lua_gettop(L),index);
+ return 1;
+}
+static int instance_settable (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ int value = 3;
+ toluaI_tm_pushclass(L,table); /* pushes base */
+ if (lua_isnumber(L,index))
+ {
+ lua_pushstring(L,"operator_set");
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {/* the stack here is: table,index,value,base,operator */
+ /* call operator passing table, index, and value */
+ lua_insert(L,1);
+ lua_pop(L,1); /* base */
+ lua_call(L,3,0);
+ return 0;
+ }
+ }
+ if (oo_settable(L,table,4,index,value) == 0)
+ {
+ toluaI_tm_pushmate(L,table); /* pushes mate */
+ if (lua_isnil(L,-1))
+ {
+ /* creates mate table */
+ lua_newtable(L);
+ toluaI_getregistry(L,"tolua_tbl_mate");
+ lua_pushvalue(L,table); /* that is the userdata */
+ lua_pushvalue(L,-3);
+ lua_rawset(L,-3);
+ lua_pop(L,1); /* tbl_mate */
+ }
+ /* the mate table is on the top */
+ lua_pushvalue(L,index);
+ lua_pushvalue(L,value);
+ lua_rawset(L,-3);
+ }
+ return 0;
+}
+static int instance_gc (lua_State* L)
+{
+ toluaI_tm_undoclone(L,lua_tag(L,1),lua_touserdata(L,1));
+ return 0;
+}
+static int gen_operator (lua_State* L)
+{
+ int op1 = 1;
+ int op2 = 2;
+ int event = 3;
+ char* name = toluaI_tt_concat("operator_",lua_tostring(L,event));
+ lua_pushstring(L,name);
+ lua_gettable(L,op1);
+ lua_pushvalue(L,op1);
+ lua_pushvalue(L,op2);
+ lua_call(L,2,1);
+ return 1;
+}
+static int instance_operator (lua_State* L)
+{
+ return gen_operator(L);
+}
+static int instance_relational (lua_State* L)
+{
+ gen_operator(L);
+ if ((int)lua_tonumber(L,-1)==0) lua_pushnil(L);
+ return 1;
+}
+
+/* lua instance tags */
+static int linstance_index (lua_State* L)
+{
+ toluaI_tm_pushclass(L,1);
+ oo_gettable(L,1,3,2); /* table,base,index */
+ return 1;
+}
+
+
+/* module tag methods */
+static int module_index (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ lua_pushstring(L,".get");
+ lua_rawget(L,table);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {
+ lua_call(L,0,1);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+static int module_settable (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ int value = 3;
+ lua_pushstring(L,".set");
+ lua_rawget(L,table);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,value);
+ lua_call(L,1,0);
+ return 0;
+ }
+ }
+ lua_pushvalue(L,index);
+ lua_pushvalue(L,value);
+ lua_rawset(L,table);
+ return 0;
+}
+
+/* global variable tag methods */
+static int global_getglobal (lua_State* L)
+{
+ int value = 2;
+ lua_pushstring(L,".get");
+ lua_rawget(L,value);
+ lua_call(L,0,1);
+ return 1;
+}
+static int global_setglobal (lua_State* L)
+{
+ int value = 2;
+ int newvalue = 3;
+ lua_pushstring(L,".set");
+ lua_rawget(L,value);
+ if (lua_isnil(L,-1))
+ lua_error(L,"value of const variable cannot be changed");
+ else
+ {
+ lua_pushvalue(L,newvalue);
+ lua_call(L,1,0);
+ }
+ return 0;
+}
+
+/* indirect tag methods */
+static int indirect_getglobal (lua_State* L)
+{
+ int varname = 1;
+ int value = 2;
+ lua_pushstring(L,".module");
+ lua_gettable(L,value);
+ lua_pushvalue(L,varname);
+ lua_gettable(L,-2);
+ return 1;
+}
+static int indirect_setglobal (lua_State* L)
+{
+ int varname = 1;
+ int value = 2;
+ int newvalue = 3;
+ lua_pushstring(L,".module");
+ lua_gettable(L,value);
+ lua_pushvalue(L,varname);
+ lua_pushvalue(L,newvalue);
+ lua_settable(L,-3);
+ return 0;
+}
+
+static void setmethods (lua_State* L)
+{
+ /* global variable */
+ lua_pushcfunction(L,global_getglobal);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_global"),"getglobal");
+ lua_pushcfunction(L,global_setglobal);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_global"),"setglobal");
+
+ /* module */
+ lua_pushcfunction(L,module_index);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_module"),"index");
+ lua_pushcfunction(L,module_settable);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_module"),"settable");
+
+ /* class */
+ lua_pushcfunction(L,class_index);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_class"),"index");
+ lua_pushcfunction(L,class_settable);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_class"),"settable");
+
+ /* instance */
+ lua_pushcfunction(L,instance_gettable);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"gettable");
+ lua_pushcfunction(L,instance_settable);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"settable");
+ lua_pushcfunction(L,instance_operator);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"add");
+ lua_pushcfunction(L,instance_operator);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"sub");
+ lua_pushcfunction(L,instance_operator);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"mul");
+ lua_pushcfunction(L,instance_operator);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"div");
+ lua_pushcfunction(L,instance_relational);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"lt");
+ lua_pushcfunction(L,instance_gc);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"gc");
+
+ /* lua instance */
+ lua_pushcfunction(L,linstance_index);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_linstance"),"index");
+
+ /* indirect */
+ lua_pushcfunction(L,indirect_getglobal);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_indirect"),"getglobal");
+ lua_pushcfunction(L,indirect_setglobal);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_indirect"),"setglobal");
+}
+
+
+
diff --git a/src/lua/tolua_tm.h b/src/lua/tolua_tm.h
new file mode 100644
index 00000000..c1bf06dc
--- /dev/null
+++ b/src/lua/tolua_tm.h
@@ -0,0 +1,32 @@
+/* tolua: tag methods
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_tm.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+
+#ifndef tolua_tm_h
+#define tolua_tm_h
+
+void toluaI_tm_init (lua_State* L);
+void toluaI_tm_global (lua_State* L, int lo);
+void toluaI_tm_module (lua_State* L, int lo);
+void toluaI_tm_class (lua_State* L, int lo, char* name);
+void toluaI_tm_instance (lua_State* L, int tag, int lo);
+void toluaI_tm_linstance (lua_State* L, int tag, int lo);
+void toluaI_tm_using (lua_State* L, int module);
+void toluaI_tm_setclass (lua_State* L, int lo);
+void toluaI_tm_pushmate (lua_State* L, int lo);
+void toluaI_tm_pushclass (lua_State* L, int lo);
+
+
+#endif
diff --git a/src/lua/tolua_tt.c b/src/lua/tolua_tt.c
new file mode 100644
index 00000000..33c384c6
--- /dev/null
+++ b/src/lua/tolua_tt.c
@@ -0,0 +1,316 @@
+/* tolua: type & tag manipulation.
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_tt.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_tt.h"
+#include "tolua_tm.h"
+#include "tolua_eh.h"
+#include "tolua_rg.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/* Global tables created in Lua registry:
+ tolua_tbl_itype: indexed by instance tags, stores the instance types.
+ tolua_tbl_itag: indexed by instance types, stores the instance tags.
+ tolua_tbl_const: indexed by constant tags, stores the tags.
+ tolua_tbl_hierarchy: indexed by instance tags, stores the base tags.
+*/
+
+/* exported basic type tags */
+int tolua_tag_nil;
+int tolua_tag_number;
+int tolua_tag_string;
+int tolua_tag_userdata;
+int tolua_tag_table;
+int tolua_tag_function;
+
+
+static const char* gettype (lua_State* L, int tag)
+{
+ const char* type;
+ toluaI_getregistry(L,"tolua_tbl_itype");
+ lua_pushnumber(L,tag);
+ lua_gettable(L,-2);
+ type = lua_tostring(L,-1);
+ if (type==NULL) type = "[undefined]";
+ lua_pop(L,2);
+ return type;
+}
+
+const char* toluaI_tt_getobjtype (lua_State* L, int lo)
+{
+ if (lua_gettop(L)<abs(lo))
+ return "[no object]";
+ else
+ return gettype(L,lua_tag(L,lo));
+}
+
+int toluaI_tt_gettag (lua_State* L, char* type)
+{
+ int tag;
+ toluaI_getregistry(L,"tolua_tbl_itag");
+ lua_pushstring(L,type);
+ lua_gettable(L,-2);
+ tag = (int)lua_tonumber(L,-1);
+ lua_pop(L,2);
+ return tag;
+}
+
+static int basetag (lua_State* L, int hierarchy, int tag)
+{
+ int btag;
+ lua_pushnumber(L,tag);
+ lua_gettable(L,hierarchy);
+ btag = (int)lua_tonumber(L,-1);
+ lua_pop(L,1);
+ return btag;
+}
+
+static int istype (lua_State* L, int lo, int tag)
+{
+ int otag = lua_tag(L,lo);
+ if (tag==otag) /* check simplest case */
+ return 1;
+ else if (lua_isnil(L,lo) &&
+ tag!=LUA_TNUMBER &&
+ tag!=LUA_TTABLE &&
+ tag!=LUA_TFUNCTION
+ )
+ return 1;
+ else if ((tag==LUA_TSTRING && lua_isstring(L,lo)) || /* check convertions */
+ (tag==LUA_TNUMBER && lua_isnumber(L,lo))
+ )
+ return 1;
+ else if (tag==LUA_TUSERDATA && lua_isuserdata(L,lo)) /* pointer to void* */
+ return 1;
+ else if (tag==toluaI_tt_gettag(L,"bool") && otag==LUA_TNUMBER)
+ return 1;
+ else
+ {
+ /* if requested type is const, the non-const is an alternative type */
+ int tag2;
+ int tbl_hierarchy;
+ toluaI_getregistry(L,"tolua_tbl_const");
+ lua_pushnumber(L,tag);
+ lua_gettable(L,-2);
+ tag2 = (int)lua_tonumber(L,-1);
+ lua_pop(L,2);
+ if (tag2 && tag2==otag)
+ return 1;
+ /* check for base classes */
+ toluaI_getregistry(L,"tolua_tbl_hierarchy");
+ tbl_hierarchy = lua_gettop(L);
+ otag = basetag(L,tbl_hierarchy,otag);
+ while (otag)
+ {
+ if (tag==otag || (tag2 && tag2==otag))
+ break;
+ otag = basetag(L,tbl_hierarchy,otag);
+ }
+ lua_pop(L,1);
+ return otag!=0;
+ }
+}
+
+void toluaI_tt_init (lua_State* L)
+{
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_itype");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_itag");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_const");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_hierarchy");
+
+ /* set and register basic Lua type tag */
+#if 0
+ lua_pushnumber(L,LUA_TNIL); toluaI_setregistry(L,"tolua_tag_nil");
+ lua_pushnumber(L,LUA_TNUMBER); toluaI_setregistry(L,"tolua_tag_number");
+ lua_pushnumber(L,LUA_TSTRING); toluaI_setregistry(L,"tolua_tag_string");
+ lua_pushnumber(L,LUA_TUSERDATA); toluaI_setregistry(L,"tolua_tag_userdata");
+ lua_pushnumber(L,LUA_TTABLE); toluaI_setregistry(L,"tolua_tag_table");
+ lua_pushnumber(L,LUA_TFUNCTION); toluaI_setregistry(L,"tolua_tag_function");
+
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_nil"),"nil");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_number"),"number");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_string"),"string");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_userdata"),"userdata");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_table"),"table");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_function"),"function");
+#else
+ toluaI_tt_register(L,LUA_TNIL,"nil");
+ toluaI_tt_register(L,LUA_TNUMBER,"number");
+ toluaI_tt_register(L,LUA_TSTRING,"string");
+ toluaI_tt_register(L,LUA_TUSERDATA,"userdata");
+ toluaI_tt_register(L,LUA_TTABLE,"table");
+ toluaI_tt_register(L,LUA_TFUNCTION,"function");
+ toluaI_tt_register(L,lua_newtag(L),"bool");
+#endif
+}
+
+
+void toluaI_tt_register (lua_State* L, int tag, char* type)
+{
+ toluaI_getregistry(L,"tolua_tbl_itype");
+ lua_pushnumber(L,tag);
+ lua_pushstring(L,type);
+ lua_settable(L,-3);
+
+ toluaI_getregistry(L,"tolua_tbl_itag");
+ lua_pushstring(L,type);
+ lua_pushnumber(L,tag);
+ lua_settable(L,-3);
+
+ lua_pop(L,2);
+}
+
+
+void toluaI_tt_class (lua_State* L, int lo, char* derived, char* base)
+{
+ char* cderived = toluaI_tt_concat("const ",derived);
+ int tag = toluaI_tt_gettag(L,derived);
+ int ctag = toluaI_tt_gettag(L,cderived);
+ toluaI_tm_instance(L,tag,lo);
+ toluaI_tm_instance(L,ctag,lo);
+ if (*base != 0)
+ {
+ char* cbase = toluaI_tt_concat("const ",base);
+ int btag = toluaI_tt_gettag(L,base);
+ int cbtag = toluaI_tt_gettag(L,cbase);
+ toluaI_tt_sethierarchy(L,tag,btag);
+ toluaI_tt_sethierarchy(L,ctag,cbtag);
+ }
+}
+
+void toluaI_tt_sethierarchy (lua_State* L, int tag, int btag)
+{
+ toluaI_getregistry(L,"tolua_tbl_hierarchy");
+ lua_pushnumber(L,tag);
+ lua_pushnumber(L,btag);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+}
+
+char* toluaI_tt_concat (const char* s1, const char* s2)
+{
+ static char s[BUFSIZ];
+ assert(strlen(s1)+strlen(s2)<BUFSIZ);
+ return strcat(strcpy(s,s1),s2);
+}
+
+void tolua_usertype (lua_State* L, char* type)
+{
+ /* check if type is already registered */
+ toluaI_getregistry(L,"tolua_tbl_itag");
+ lua_pushstring(L,type);
+ lua_gettable(L,-2);
+ if (lua_isnil(L,-1))
+ {
+ char *ctype = toluaI_tt_concat("const ",type);
+ int tag = lua_newtag(L);
+ int ctag = lua_newtag(L);
+ toluaI_tt_register(L,tag,type);
+ toluaI_tt_register(L,ctag,ctype);
+ /* set const table */
+ toluaI_getregistry(L,"tolua_tbl_const");
+ lua_pushnumber(L,ctag);
+ lua_pushnumber(L,tag);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ }
+ lua_pop(L,2);
+}
+
+int toluaI_tt_isusertype (lua_State* L, int lo)
+{
+ if (lua_isuserdata(L,lo) &&
+ toluaI_tt_gettag(L,"tolua_tag_userdata")!=lua_tag(L,lo)
+ )
+ {
+ int status;
+ toluaI_getregistry(L,"tolua_tbl_itype");
+ lua_pushnumber(L,lua_tag(L,lo));
+ lua_gettable(L,-2);
+ status = !lua_isnil(L,-1);
+ lua_pop(L,2);
+ return status;
+ }
+ return 0;
+}
+
+#if 0
+void tolua_settag (lua_State* L, char* type, int* tag)
+{
+ toluaI_getregistry(L,"tolua_tbl_itag");
+ lua_pushstring(L,type);
+ lua_gettable(L,-2);
+ *tag = (int) lua_tonumber(L,-1);
+ lua_pop(L,2);
+}
+#endif
+
+int tolua_istype (lua_State* L, int narg, int tag, int def)
+{
+ if (lua_gettop(L)<abs(narg))
+ {
+ if (def==0)
+ {
+ toluaI_eh_set(L,narg,toluaI_tt_getobjtype(L,narg),gettype(L,tag));
+ return 0;
+ }
+ }
+ else
+ {
+ if (!istype(L,narg,tag))
+ {
+ toluaI_eh_set(L,narg,toluaI_tt_getobjtype(L,narg),gettype(L,tag));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int tolua_arrayistype (lua_State* L, int narg, int tag, int dim, int def)
+{
+ int i;
+ for (i=0; i<dim; ++i)
+ {
+ int tf;
+ lua_pushnumber(L,i+1);
+ lua_gettable(L,narg);
+ tf = lua_gettop(L);
+ if (!istype(L,tf,tag) && (!def || !lua_isnil(L,tf)))
+ {
+ static char t1[BUFSIZ], t2[BUFSIZ];
+ sprintf(t1,"array of %s",toluaI_tt_getobjtype(L,tf));
+ sprintf(t2,"array of %s (dimension=%d)",gettype(L,tag),dim);
+ toluaI_eh_set(L,narg,t1,t2);
+ return 0;
+ }
+ lua_pop(L,1);
+ }
+ return 1;
+}
+
+int tolua_isnoobj (lua_State* L, int narg)
+{
+ if (lua_gettop(L)>=abs(narg))
+ {
+ toluaI_eh_set(L,narg,toluaI_tt_getobjtype(L,narg),
+ toluaI_tt_getobjtype(L,lua_gettop(L)+1));
+ return 0;
+ }
+ return 1;
+}
+
+
diff --git a/src/lua/tolua_tt.h b/src/lua/tolua_tt.h
new file mode 100644
index 00000000..941a2b02
--- /dev/null
+++ b/src/lua/tolua_tt.h
@@ -0,0 +1,31 @@
+/* tolua: type & tag manipulation.
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_tt.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+#ifndef tolua_tt_h
+#define tolua_tt_h
+
+void toluaI_tt_init (lua_State* L);
+void toluaI_tt_register (lua_State* L, int tag, char* type);
+void toluaI_tt_class (lua_State* L, int lo, char* derived, char* base);
+void toluaI_tt_sethierarchy (lua_State* L, int tag, int btag);
+int toluaI_tt_isusertype (lua_State* L, int lo);
+int toluaI_tt_gettag (lua_State* L, char* type);
+const char* toluaI_tt_getobjtype (lua_State* L, int lo);
+char* toluaI_tt_concat (const char* s1, const char* s2);
+
+
+
+
+#endif
diff --git a/src/lua/tolualua.c b/src/lua/tolualua.c
new file mode 100644
index 00000000..adbb8635
--- /dev/null
+++ b/src/lua/tolualua.c
@@ -0,0 +1,2975 @@
+/*
+** Lua binding: tolualua
+** Generated automatically by tolua 4.0a - angband on Sun Nov 11 22:59:08 2001.
+*/
+
+#include "tolua.h"
+
+/* Exported function */
+int tolua_tolualua_open (lua_State* tolua_S);
+void tolua_tolualua_close (lua_State* tolua_S);
+
+
+/* function to register type */
+static void toluaI_reg_types (lua_State* tolua_S)
+{
+}
+
+/* error messages */
+#define TOLUA_ERR_SELF tolua_error(tolua_S,"invalid 'self'")
+#define TOLUA_ERR_ASSIGN tolua_error(tolua_S,"#vinvalid type in variable assignment.")
+
+/* Open function */
+int tolua_tolualua_open (lua_State* tolua_S)
+{
+ tolua_open(tolua_S);
+ toluaI_reg_types(tolua_S);
+
+ { /* begin embedded lua code */
+ static unsigned char B[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 95, 98, 97,115,105, 99, 32, 61, 32,123, 10, 91, 39,118,
+ 111,105,100, 39, 93, 32, 61, 32, 39, 39, 44, 10, 91, 39, 99,
+ 104, 97,114, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114,
+ 39, 44, 10, 91, 39,105,110,116, 39, 93, 32, 61, 32, 39,110,
+ 117,109, 98,101,114, 39, 44, 10, 91, 39,115,104,111,114,116,
+ 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10,
+ 91, 39,108,111,110,103, 39, 93, 32, 61, 32, 39,110,117,109,
+ 98,101,114, 39, 44, 10, 91, 39, 95, 99,115,116,114,105,110,
+ 103, 39, 93, 32, 61, 32, 39,115,116,114,105,110,103, 39, 44,
+ 10, 91, 39, 95,117,115,101,114,100, 97,116, 97, 39, 93, 32,
+ 61, 32, 39,117,115,101,114,100, 97,116, 97, 39, 44, 10, 91,
+ 39, 99,104, 97,114, 42, 39, 93, 32, 61, 32, 39,115,116,114,
+ 105,110,103, 39, 44, 10, 91, 39,118,111,105,100, 42, 39, 93,
+ 32, 61, 32, 39,117,115,101,114,100, 97,116, 97, 39, 44, 10,
+ 91, 39, 98,111,111,108, 39, 93, 32, 61, 32, 39, 98,111,111,
+ 108, 39, 44, 10, 91, 39, 76, 85, 65, 95, 86, 65, 76, 85, 69,
+ 39, 93, 32, 61, 32, 39,118, 97,108,117,101, 39, 44, 10, 91,
+ 39, 98,121,116,101, 39, 93, 32, 61, 32, 39,110,117,109, 98,
+ 101,114, 39, 44, 10, 91, 39,115, 49, 54, 98, 39, 93, 32, 61,
+ 32, 39,110,117,109, 98,101,114, 39, 44, 10, 91, 39,117, 49,
+ 54, 98, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39,
+ 44, 10, 91, 39,115, 51, 50, 98, 39, 93, 32, 61, 32, 39,110,
+ 117,109, 98,101,114, 39, 44, 10, 91, 39,117, 51, 50, 98, 39,
+ 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10,125,
+ 10, 10, 95, 98, 97,115,105, 99, 95,116, 97,103, 32, 61, 32,
+ 123, 10, 91, 39,118,111,105,100, 39, 93, 32, 61, 32, 39, 39,
+ 44, 10, 91, 39, 99,104, 97,114, 39, 93, 32, 61, 32, 39, 76,
+ 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,
+ 105,110,116, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78,
+ 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,115,104,111,114,116,
+ 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66,
+ 69, 82, 39, 44, 10, 91, 39,108,111,110,103, 39, 93, 32, 61,
+ 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44,
+ 10, 91, 39, 95, 99,115,116,114,105,110,103, 39, 93, 32, 61,
+ 32, 39, 76, 85, 65, 95, 84, 83, 84, 82, 73, 78, 71, 39, 44,
+ 10, 91, 39, 95,117,115,101,114,100, 97,116, 97, 39, 93, 32,
+ 61, 32, 39, 76, 85, 65, 95, 84, 85, 83, 69, 82, 68, 65, 84,
+ 65, 39, 44, 10, 91, 39, 99,104, 97,114, 42, 39, 93, 32, 61,
+ 32, 39, 76, 85, 65, 95, 84, 83, 84, 82, 73, 78, 71, 39, 44,
+ 10, 91, 39,118,111,105,100, 42, 39, 93, 32, 61, 32, 39, 76,
+ 85, 65, 95, 84, 85, 83, 69, 82, 68, 65, 84, 65, 39, 44, 10,
+ 91, 39, 98,111,111,108, 39, 93, 32, 61, 32, 39,116,111,108,
+ 117, 97, 95,116, 97,103, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 98,111,111,108, 34, 41, 39, 44, 10, 91, 39, 98,121,116,
+ 101, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77,
+ 66, 69, 82, 39, 44, 10, 91, 39,115, 49, 54, 98, 39, 93, 32,
+ 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39,
+ 44, 10, 91, 39,117, 49, 54, 98, 39, 93, 32, 61, 32, 39, 76,
+ 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,
+ 115, 51, 50, 98, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84,
+ 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,117, 51, 50, 98,
+ 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66,
+ 69, 82, 39, 44, 10,125, 10, 10, 95, 98, 97,115,105, 99, 95,
+ 99,116,121,112,101, 32, 61, 32,123, 10,110,117,109, 98,101,
+ 114, 32, 61, 32, 34,108,111,110,103, 34, 44, 10,115,116,114,
+ 105,110,103, 32, 61, 32, 34, 99,111,110,115,116, 32, 99,104,
+ 97,114, 42, 34, 44, 10,117,115,101,114,100, 97,116, 97, 32,
+ 61, 32, 34,118,111,105,100, 42, 34, 44, 10, 98,111,111,108,
+ 32, 61, 32, 34,105,110,116, 34, 44, 10,125, 10, 10, 10, 10,
+ 95,117,115,101,114,116,121,112,101, 32, 61, 32,123,125, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32,116,111,108,117,
+ 97, 95,105,110,100,101,120, 32, 40,116, 44,102, 41, 10,105,
+ 102, 32,102, 32, 61, 61, 32, 39, 95, 98, 97,115,101, 39, 32,
+ 116,104,101,110, 10,114,101,116,117,114,110, 32,116,111,108,
+ 117, 97, 95,111,108,100, 95,105,110,100,101,120, 40,116, 44,
+ 102, 41, 10,101,108,115,101, 10,114,101,116,117,114,110, 32,
+ 116, 46, 95, 98, 97,115,101, 91,102, 93, 10,101,110,100, 10,
+ 101,110,100, 10, 10,116,111,108,117, 97, 95,116, 97,103, 32,
+ 61, 32,110,101,119,116, 97,103, 40, 41, 10,116,111,108,117,
+ 97, 95,111,108,100, 95,105,110,100,101,120, 32, 61, 32,115,
+ 101,116,116, 97,103,109,101,116,104,111,100, 40,116,111,108,
+ 117, 97, 95,116, 97,103, 44, 34,105,110,100,101,120, 34, 44,
+ 116,111,108,117, 97, 95,105,110,100,101,120, 41, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32,116,111,108,117, 97, 95,
+ 101,114,114,111,114, 32, 40,115, 41, 10,108,111, 99, 97,108,
+ 32,111,117,116, 32, 61, 32, 95, 79, 85, 84, 80, 85, 84, 10,
+ 95, 79, 85, 84, 80, 85, 84, 32, 61, 32, 95, 83, 84, 68, 69,
+ 82, 82, 10,105,102, 32,115,116,114,115,117, 98, 40,115, 44,
+ 49, 44, 49, 41, 32, 61, 61, 32, 39, 35, 39, 32,116,104,101,
+ 110, 10,119,114,105,116,101, 40, 34, 92,110, 42, 42, 32,116,
+ 111,108,117, 97, 58, 32, 34, 46, 46,115,116,114,115,117, 98,
+ 40,115, 44, 50, 41, 46, 46, 34, 46, 92,110, 92,110, 34, 41,
+ 10,101,108,115,101, 10,119,114,105,116,101, 40, 34, 92,110,
+ 42, 42, 32,116,111,108,117, 97, 32,105,110,116,101,114,110,
+ 97,108, 32,101,114,114,111,114, 58, 32, 34, 46, 46,115, 46,
+ 46, 34, 46, 92,110, 92,110, 34, 41, 10,114,101,116,117,114,
+ 110, 10,101,110,100, 10, 10,105,102, 32, 95, 99,117,114,114,
+ 95, 99,111,100,101, 32,116,104,101,110, 10,108,111, 99, 97,
+ 108, 32, 95, 44, 95, 44,115, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40, 95, 99,117,114,114, 95, 99,111,100,101, 44, 34,
+ 94, 37,115, 42, 40, 46, 45, 92,110, 41, 34, 41, 10,105,102,
+ 32,115, 61, 61,110,105,108, 32,116,104,101,110, 32,115, 32,
+ 61, 32, 95, 99,117,114,114, 95, 99,111,100,101, 32,101,110,
+ 100, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 95,
+ 117,115,101,114,100, 97,116, 97, 34, 44, 34,118,111,105,100,
+ 42, 34, 41, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44,
+ 34, 95, 99,115,116,114,105,110,103, 34, 44, 34, 99,104, 97,
+ 114, 42, 34, 41, 10,119,114,105,116,101, 40, 34, 67,111,100,
+ 101, 32, 98,101,105,110,103, 32,112,114,111, 99,101,115,115,
+ 101,100, 58, 92,110, 34, 46, 46,115, 46, 46, 34, 92,110, 34,
+ 41, 10,101,110,100, 10, 95, 79, 85, 84, 80, 85, 84, 32, 61,
+ 32,111,117,116, 10,101,110,100, 10, 10, 10, 95, 69, 82, 82,
+ 79, 82, 77, 69, 83, 83, 65, 71, 69, 32, 61, 32,116,111,108,
+ 117, 97, 95,101,114,114,111,114, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32,114,101,103,116,121,112,101, 32, 40,116,
+ 41, 10,105,102, 32,110,111,116, 32,105,115,116,121,112,101,
+ 40,116, 41, 32,116,104,101,110, 10, 95,117,115,101,114,116,
+ 121,112,101, 91,116, 93, 32, 61, 32,116, 10,101,110,100, 10,
+ 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32,116, 97,103,118, 97,114,
+ 40,116,121,112,101, 44, 99,111,110,115,116, 41, 10,105,102,
+ 32,116,121,112,101, 32, 61, 61, 32, 39, 39, 32,111,114, 32,
+ 116,121,112,101, 32, 61, 61, 32, 39,118,111,105,100, 39, 32,
+ 116,104,101,110, 10,114,101,116,117,114,110, 32,116,121,112,
+ 101, 44, 48, 10,101,108,115,101, 10,108,111, 99, 97,108, 32,
+ 109, 44,116, 32, 61, 32,102,105,110,100,116,121,112,101,100,
+ 101,102, 40,116,121,112,101, 41, 10,105,102, 32,105,115, 98,
+ 97,115,105, 99, 40,116, 41, 32,116,104,101,110, 10,114,101,
+ 116,117,114,110, 32,116, 44, 32, 95, 98, 97,115,105, 99, 95,
+ 116, 97,103, 91,116, 93, 10,101,110,100, 10,105,102, 32,115,
+ 116,114,102,105,110,100, 40,109, 44, 39, 99,111,110,115,116,
+ 39, 41, 32,116,104,101,110, 32, 99,111,110,115,116, 32, 61,
+ 32, 39, 99,111,110,115,116, 39, 32,101,110,100, 10,114,101,
+ 103,116,121,112,101, 40,116, 41, 10,105,102, 32, 99,111,110,
+ 115,116, 32, 97,110,100, 32, 99,111,110,115,116, 32,126, 61,
+ 32, 39, 39, 32,116,104,101,110, 10,116, 32, 61, 32, 39, 99,
+ 111,110,115,116, 32, 39, 46, 46,116, 10,101,110,100, 10,114,
+ 101,116,117,114,110, 32,116, 44, 39,116,111,108,117, 97, 95,
+ 116, 97,103, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46,
+ 46,116, 46, 46, 39, 34, 41, 39, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,105,115,
+ 98, 97,115,105, 99, 32, 40,116,121,112,101, 41, 10,108,111,
+ 99, 97,108, 32,109, 44,116, 32, 61, 32,102,105,110,100,116,
+ 121,112,101,100,101,102, 40,116,121,112,101, 41, 10,108,111,
+ 99, 97,108, 32, 98, 32, 61, 32, 95, 98, 97,115,105, 99, 91,
+ 116, 93, 10,105,102, 32, 98, 32,116,104,101,110, 10,114,101,
+ 116,117,114,110, 32, 98, 44, 95, 98, 97,115,105, 99, 95, 99,
+ 116,121,112,101, 91, 98, 93, 10,101,110,100, 10,114,101,116,
+ 117,114,110, 32,110,105,108, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32,105,115,116,121,112,101, 32,
+ 40,116, 41, 10,114,101,116,117,114,110, 32, 95, 98, 97,115,
+ 105, 99, 91,116, 93, 32,111,114, 32, 95,117,115,101,114,116,
+ 121,112,101, 91,116, 93, 32,111,114, 32,105,115,116,121,112,
+ 101,100,101,102, 40,116, 41, 10,101,110,100, 10, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32,115,112,108,105,116, 32,
+ 40,115, 44,116, 41, 10,108,111, 99, 97,108, 32,108, 32, 61,
+ 32,123,110, 61, 48,125, 10,108,111, 99, 97,108, 32,102, 32,
+ 61, 32,102,117,110, 99,116,105,111,110, 32, 40,115, 41, 10,
+ 37,108, 46,110, 32, 61, 32, 37,108, 46,110, 32, 43, 32, 49,
+ 10, 37,108, 91, 37,108, 46,110, 93, 32, 61, 32,115, 10,101,
+ 110,100, 10,108,111, 99, 97,108, 32,112, 32, 61, 32, 34, 37,
+ 115, 42, 40, 46, 45, 41, 37,115, 42, 34, 46, 46,116, 46, 46,
+ 34, 37,115, 42, 34, 10,115, 32, 61, 32,103,115,117, 98, 40,
+ 115, 44, 34, 94, 37,115, 43, 34, 44, 34, 34, 41, 10,115, 32,
+ 61, 32,103,115,117, 98, 40,115, 44, 34, 37,115, 43, 36, 34,
+ 44, 34, 34, 41, 10,115, 32, 61, 32,103,115,117, 98, 40,115,
+ 44,112, 44,102, 41, 10,108, 46,110, 32, 61, 32,108, 46,110,
+ 32, 43, 32, 49, 10,108, 91,108, 46,110, 93, 32, 61, 32,103,
+ 115,117, 98, 40,115, 44, 34, 40, 37,115, 37,115, 42, 41, 36,
+ 34, 44, 34, 34, 41, 10,114,101,116,117,114,110, 32,108, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,111,110, 99, 97,116, 32, 40,116, 44,102, 44,108, 41,
+ 10,108,111, 99, 97,108, 32,115, 32, 61, 32, 39, 39, 10,108,
+ 111, 99, 97,108, 32,105, 61,102, 10,119,104,105,108,101, 32,
+ 105, 60, 61,108, 32,100,111, 10,115, 32, 61, 32,115, 46, 46,
+ 116, 91,105, 93, 10,105, 32, 61, 32,105, 43, 49, 10,105,102,
+ 32,105, 32, 60, 61, 32,108, 32,116,104,101,110, 32,115, 32,
+ 61, 32,115, 46, 46, 39, 32, 39, 32,101,110,100, 10,101,110,
+ 100, 10,114,101,116,117,114,110, 32,115, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32,111,117,116,112,
+ 117,116, 32, 40, 46, 46, 46, 41, 10,108,111, 99, 97,108, 32,
+ 105, 61, 49, 10,119,104,105,108,101, 32,105, 60, 61, 97,114,
+ 103, 46,110, 32,100,111, 10,105,102, 32, 95, 99,111,110,116,
+ 32, 97,110,100, 32,110,111,116, 32,115,116,114,102,105,110,
+ 100, 40, 95, 99,111,110,116, 44, 39, 91, 37, 40, 44, 34, 93,
+ 39, 41, 32, 97,110,100, 10,115,116,114,102,105,110,100, 40,
+ 97,114,103, 91,105, 93, 44, 34, 94, 91, 37, 97, 95,126, 93,
+ 34, 41, 32,116,104,101,110, 10,119,114,105,116,101, 40, 39,
+ 32, 39, 41, 10,101,110,100, 10,119,114,105,116,101, 40, 97,
+ 114,103, 91,105, 93, 41, 10,105,102, 32, 97,114,103, 91,105,
+ 93, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10, 95, 99,
+ 111,110,116, 32, 61, 32,115,116,114,115,117, 98, 40, 97,114,
+ 103, 91,105, 93, 44, 45, 49, 44, 45, 49, 41, 10,101,110,100,
+ 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,105,102,
+ 32,115,116,114,102,105,110,100, 40, 97,114,103, 91, 97,114,
+ 103, 46,110, 93, 44, 34, 91, 37, 47, 37, 41, 37, 59, 37,123,
+ 37,125, 93, 36, 34, 41, 32,116,104,101,110, 10, 95, 99,111,
+ 110,116, 61,110,105,108, 32,119,114,105,116,101, 40, 39, 92,
+ 110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,
+ 115, 70,101, 97,116,117,114,101, 32, 61, 32,123, 10,125, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 70,101, 97,116,117,114,101, 58,115,117,112, 99,111,100,
+ 101, 32, 40, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 70,101, 97,116,117,
+ 114,101, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,101,
+ 110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 70,101, 97,116,117,114,101, 58,114,101,103,
+ 105,115,116,101,114, 32, 40, 41, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,
+ 101, 97,116,117,114,101, 58,117,110,114,101,103,105,115,116,
+ 101,114, 32, 40, 41, 10,101,110,100, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 70,101, 97,116,
+ 117,114,101, 58,112,114,101, 97,109, 98,108,101, 32, 40, 41,
+ 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 58,
+ 105,110, 99,108, 97,115,115, 32, 40, 41, 10,105,102, 32,115,
+ 101,108,102, 46,112, 97,114,101,110,116, 32, 97,110,100, 32,
+ 115,101,108,102, 46,112, 97,114,101,110,116, 46,116,121,112,
+ 101, 32, 61, 61, 32, 39, 99,108, 97,115,115, 39, 32,116,104,
+ 101,110, 10,114,101,116,117,114,110, 32,115,101,108,102, 46,
+ 112, 97,114,101,110,116, 46,110, 97,109,101, 10,101,108,115,
+ 101, 10,114,101,116,117,114,110, 32,110,105,108, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101,
+ 58,105,110,109,111,100,117,108,101, 32, 40, 41, 10,105,102,
+ 32,115,101,108,102, 46,112, 97,114,101,110,116, 32, 97,110,
+ 100, 32,115,101,108,102, 46,112, 97,114,101,110,116, 46,116,
+ 121,112,101, 32, 61, 61, 32, 39,109,111,100,117,108,101, 39,
+ 32,116,104,101,110, 10,114,101,116,117,114,110, 32,115,101,
+ 108,102, 46,112, 97,114,101,110,116, 46,110, 97,109,101, 10,
+ 101,108,115,101, 10,114,101,116,117,114,110, 32,110,105,108,
+ 10,101,110,100, 10,101,110,100, 10, 10, 10, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,101,
+ 97,116,117,114,101, 58, 99,102,117,110, 99,110, 97,109,101,
+ 32, 40,110, 41, 10,105,102, 32,115,101,108,102, 46,112, 97,
+ 114,101,110,116, 32,116,104,101,110, 10,110, 32, 61, 32,115,
+ 101,108,102, 46,112, 97,114,101,110,116, 58, 99,102,117,110,
+ 99,110, 97,109,101, 40,110, 41, 10,101,110,100, 10,105,102,
+ 32,115,101,108,102, 46,108,110, 97,109,101, 32,116,104,101,
+ 110, 10,114,101,116,117,114,110, 32,110, 46, 46, 39, 95, 39,
+ 46, 46,115,101,108,102, 46,108,110, 97,109,101, 10,101,108,
+ 115,101, 10,114,101,116,117,114,110, 32,110, 46, 46, 39, 95,
+ 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 86,
+ 101,114, 98, 97,116,105,109, 32, 61, 32,123, 10,108,105,110,
+ 101, 32, 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101, 32, 61,
+ 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 44, 10,
+ 125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115, 86,
+ 101,114, 98, 97,116,105,109, 44,116,111,108,117, 97, 95,116,
+ 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 99,108, 97,115,115, 86,101,114, 98, 97,116,105,109, 58,112,
+ 114,101, 97,109, 98,108,101, 32, 40, 41, 10,105,102, 32,110,
+ 111,116, 32,115,101,108,102, 46, 99,111,110,100, 32,116,104,
+ 101,110, 10,119,114,105,116,101, 40,115,101,108,102, 46,108,
+ 105,110,101, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86,
+ 101,114, 98, 97,116,105,109, 58,115,117,112, 99,111,100,101,
+ 32, 40, 41, 10,105,102, 32,115,101,108,102, 46, 99,111,110,
+ 100, 32,116,104,101,110, 10,119,114,105,116,101, 40,115,101,
+ 108,102, 46,108,105,110,101, 41, 10,119,114,105,116,101, 40,
+ 39, 92,110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115,
+ 86,101,114, 98, 97,116,105,109, 58,114,101,103,105,115,116,
+ 101,114, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46, 99,
+ 111,110,100, 32,116,104,101,110, 10,119,114,105,116,101, 40,
+ 115,101,108,102, 46,108,105,110,101, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,108, 97,115,115, 86,101,114, 98, 97,116,105,109, 58,
+ 112,114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,
+ 111,115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 86,101,114, 98, 97,116,105,109,123, 34, 41,
+ 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,
+ 32,108,105,110,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,
+ 102, 46,108,105,110,101, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,125, 34,
+ 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 95, 86,101,114, 98,
+ 97,116,105,109, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,
+ 101, 32, 61, 32, 99,108, 97,115,115, 86,101,114, 98, 97,116,
+ 105,109, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108,
+ 117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,110,100, 40,
+ 116, 41, 10,114,101,116,117,114,110, 32,116, 10,101,110,100,
+ 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 86,101,
+ 114, 98, 97,116,105,109, 32, 40,108, 41, 10,108,111, 99, 97,
+ 108, 32, 99, 10,105,102, 32,115,116,114,115,117, 98, 40,108,
+ 44, 49, 44, 49, 41, 32, 61, 61, 32, 39, 36, 39, 32,116,104,
+ 101,110, 10, 99, 32, 61, 32, 49, 10,108, 32, 61, 32,115,116,
+ 114,115,117, 98, 40,108, 44, 50, 41, 10,101,110,100, 10,114,
+ 101,116,117,114,110, 32, 95, 86,101,114, 98, 97,116,105,109,
+ 32,123, 10,108,105,110,101, 32, 61, 32,108, 44, 10, 99,111,
+ 110,100, 32, 61, 32, 99, 10,125, 10,101,110,100, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 99,108, 97,115,115, 67,111,100,101, 32, 61, 32,123, 10,116,
+ 101,120,116, 32, 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101,
+ 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101,
+ 44, 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,
+ 115, 67,111,100,101, 44,116,111,108,117, 97, 95,116, 97,103,
+ 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 67,111,100,101, 58,114,101,103,105,115,116,101,
+ 114, 32, 40, 41, 10, 10,108,111, 99, 97,108, 32,115, 32, 61,
+ 32, 99,108,101, 97,110, 40,115,101,108,102, 46,116,101,120,
+ 116, 41, 10,105,102, 32,110,111,116, 32,115, 32,116,104,101,
+ 110, 10,101,114,114,111,114, 40, 34,112, 97,114,115,101,114,
+ 32,101,114,114,111,114, 32,105,110, 32,101,109, 98,101,100,
+ 100,101,100, 32, 99,111,100,101, 34, 41, 10,101,110,100, 10,
+ 10, 10,111,117,116,112,117,116, 40, 39, 92,110, 32,123, 32,
+ 47, 42, 32, 98,101,103,105,110, 32,101,109, 98,101,100,100,
+ 101,100, 32,108,117, 97, 32, 99,111,100,101, 32, 42, 47, 92,
+ 110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,115,116,
+ 97,116,105, 99, 32,117,110,115,105,103,110,101,100, 32, 99,
+ 104, 97,114, 32, 66, 91, 93, 32, 61, 32,123, 92,110, 32, 39,
+ 41, 10,108,111, 99, 97,108, 32,116, 61,123,110, 61, 48,125,
+ 10,108,111, 99, 97,108, 32, 98, 32, 61, 32,103,115,117, 98,
+ 40,115, 44, 39, 40, 46, 41, 39, 44,102,117,110, 99,116,105,
+ 111,110, 32, 40, 99, 41, 10,108,111, 99, 97,108, 32,101, 32,
+ 61, 32, 39, 39, 10, 37,116, 46,110, 61, 37,116, 46,110, 43,
+ 49, 32,105,102, 32, 37,116, 46,110, 61, 61, 49, 53, 32,116,
+ 104,101,110, 32, 37,116, 46,110, 61, 48, 32,101, 61, 39, 92,
+ 110, 32, 39, 32,101,110,100, 10,114,101,116,117,114,110, 32,
+ 102,111,114,109, 97,116, 40, 39, 37, 51,117, 44, 37,115, 39,
+ 44,115,116,114, 98,121,116,101, 40, 99, 41, 44,101, 41, 10,
+ 101,110,100, 10, 41, 10,111,117,116,112,117,116, 40, 98, 46,
+ 46,115,116,114, 98,121,116,101, 40, 34, 32, 34, 41, 41, 10,
+ 111,117,116,112,117,116, 40, 39, 92,110, 32,125, 59, 92,110,
+ 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97,
+ 95,100,111, 98,117,102,102,101,114, 40,116,111,108,117, 97,
+ 95, 83, 44, 40, 99,104, 97,114, 42, 41, 66, 44,115,105,122,
+ 101,111,102, 40, 66, 41, 44, 34,116,111,108,117, 97, 58, 32,
+ 101,109, 98,101,100,100,101,100, 32, 76,117, 97, 32, 99,111,
+ 100,101, 34, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32,125, 32, 47, 42, 32,101,110,100, 32,111,102, 32,101,
+ 109, 98,101,100,100,101,100, 32,108,117, 97, 32, 99,111,100,
+ 101, 32, 42, 47, 92,110, 92,110, 39, 41, 10,101,110,100, 10,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,
+ 115,115, 67,111,100,101, 58,112,114,105,110,116, 32, 40,105,
+ 100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,114,105,
+ 110,116, 40,105,100,101,110,116, 46, 46, 34, 67,111,100,101,
+ 123, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34, 32,116,101,120,116, 32, 61, 32, 91, 91, 34, 46,
+ 46,115,101,108,102, 46,116,101,120,116, 46, 46, 34, 93, 93,
+ 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,
+ 110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 95, 67,111,100,101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,
+ 115,101, 32, 61, 32, 99,108, 97,115,115, 67,111,100,101, 10,
+ 115,101,116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95,
+ 116, 97,103, 41, 10, 97,112,112,101,110,100, 40,116, 41, 10,
+ 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 67,111,100,101, 32,
+ 40,108, 41, 10,114,101,116,117,114,110, 32, 95, 67,111,100,
+ 101, 32,123, 10,116,101,120,116, 32, 61, 32,108, 10,125, 10,
+ 101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,
+ 115,115, 84,121,112,101,100,101,102, 32, 61, 32,123, 10,117,
+ 116,121,112,101, 32, 61, 32, 39, 39, 44, 10,109,111,100, 32,
+ 61, 32, 39, 39, 44, 10,116,121,112,101, 32, 61, 32, 39, 39,
+ 10,125, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 84,121,112,101,100,101,102, 58,112,114,105,
+ 110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,115,101,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 84,121,112,101,100,101,102,123, 34, 41, 10,112,114,105,
+ 110,116, 40,105,100,101,110,116, 46, 46, 34, 32,117,116,121,
+ 112,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,117,
+ 116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,
+ 110,116, 40,105,100,101,110,116, 46, 46, 34, 32,109,111,100,
+ 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100,
+ 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46,
+ 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41,
+ 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 95, 84,121,112,101,100,101,102, 32, 40,116, 41, 10,116,
+ 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 84,
+ 121,112,101,100,101,102, 10,115,101,116,116, 97,103, 40,116,
+ 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,
+ 101,110,100,116,121,112,101,100,101,102, 40,116, 41, 10,114,
+ 101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 84,121,112,101,100,101,
+ 102, 32, 40,115, 41, 10,105,102, 32,115,116,114,102,105,110,
+ 100, 40,115, 44, 39, 91, 37, 42, 38, 93, 39, 41, 32,116,104,
+ 101,110, 10,116,111,108,117, 97, 95,101,114,114,111,114, 40,
+ 34, 35,105,110,118, 97,108,105,100, 32,116,121,112,101,100,
+ 101,102, 58, 32,112,111,105,110,116,101,114,115, 32, 40, 97,
+ 110,100, 32,114,101,102,101,114,101,110, 99,101,115, 41, 32,
+ 97,114,101, 32,110,111,116, 32,115,117,112,112,111,114,116,
+ 101,100, 34, 41, 10,101,110,100, 10,108,111, 99, 97,108, 32,
+ 116, 32, 61, 32,115,112,108,105,116, 40,103,115,117, 98, 40,
+ 115, 44, 34, 37,115, 37,115, 42, 34, 44, 34, 32, 34, 41, 44,
+ 34, 32, 34, 41, 10,114,101,116,117,114,110, 32, 95, 84,121,
+ 112,101,100,101,102, 32,123, 10,117,116,121,112,101, 32, 61,
+ 32,116, 91,116, 46,110, 93, 44, 10,116,121,112,101, 32, 61,
+ 32,116, 91,116, 46,110, 45, 49, 93, 44, 10,109,111,100, 32,
+ 61, 32, 99,111,110, 99, 97,116, 40,116, 44, 49, 44,116, 46,
+ 110, 45, 50, 41, 10,125, 10,101,110,100, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,
+ 115, 67,111,110,116, 97,105,110,101,114, 32, 61, 10,123, 10,
+ 99,117,114,114, 32, 61, 32,110,105,108, 44, 10, 95, 98, 97,
+ 115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,117,
+ 114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40, 99,108,
+ 97,115,115, 67,111,110,116, 97,105,110,101,114, 44,116,111,
+ 108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,
+ 105,110,101,114, 58,100,101, 99,108,116, 97,103, 32, 40, 41,
+ 10,112,117,115,104, 40,115,101,108,102, 41, 10,108,111, 99,
+ 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,
+ 108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105,
+ 93, 58,100,101, 99,108,116, 97,103, 40, 41, 10,105, 32, 61,
+ 32,105, 43, 49, 10,101,110,100, 10,112,111,112, 40, 41, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114,
+ 58,115,117,112, 99,111,100,101, 32, 40, 41, 10,112,117,115,
+ 104, 40,115,101,108,102, 41, 10,108,111, 99, 97,108, 32,105,
+ 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91,105,
+ 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,115,117,
+ 112, 99,111,100,101, 40, 41, 10,105, 32, 61, 32,105, 43, 49,
+ 10,101,110,100, 10,112,111,112, 40, 41, 10,101,110,100, 10,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 67,111,
+ 110,116, 97,105,110,101,114, 32, 40,115,101,108,102, 41, 10,
+ 115,101,108,102, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108,
+ 97,115,115, 67,111,110,116, 97,105,110,101,114, 10,115,101,
+ 116,116, 97,103, 40,115,101,108,102, 44,116,111,108,117, 97,
+ 95,116, 97,103, 41, 10,115,101,108,102, 46,110, 32, 61, 32,
+ 48, 10,115,101,108,102, 46,116,121,112,101,100,101,102,115,
+ 32, 61, 32,123,110, 61, 48,125, 10,115,101,108,102, 46,108,
+ 110, 97,109,101,115, 32, 61, 32,123,125, 10,114,101,116,117,
+ 114,110, 32,115,101,108,102, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32,112,117,115,104, 32, 40,116,
+ 41, 10, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,
+ 114, 46, 99,117,114,114, 32, 61, 32,116, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32,112,111,112, 32,
+ 40, 41, 10, 99,108, 97,115,115, 67,111,110,116, 97,105,110,
+ 101,114, 46, 99,117,114,114, 32, 61, 32, 99,108, 97,115,115,
+ 67,111,110,116, 97,105,110,101,114, 46, 99,117,114,114, 46,
+ 112, 97,114,101,110,116, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 97,112,112,101,110,100, 32, 40,
+ 116, 41, 10,114,101,116,117,114,110, 32, 99,108, 97,115,115,
+ 67,111,110,116, 97,105,110,101,114, 46, 99,117,114,114, 58,
+ 97,112,112,101,110,100, 40,116, 41, 10,101,110,100, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 97,112,112,101,110,
+ 100,116,121,112,101,100,101,102, 32, 40,116, 41, 10,114,101,
+ 116,117,114,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,
+ 105,110,101,114, 46, 99,117,114,114, 58, 97,112,112,101,110,
+ 100,116,121,112,101,100,101,102, 40,116, 41, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32,102,105,110,
+ 100,116,121,112,101,100,101,102, 32, 40,116,121,112,101, 41,
+ 10,114,101,116,117,114,110, 32, 99,108, 97,115,115, 67,111,
+ 110,116, 97,105,110,101,114, 46, 99,117,114,114, 58,102,105,
+ 110,100,116,121,112,101,100,101,102, 40,116,121,112,101, 41,
+ 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32,105,115,116,121,112,101,100,101,102, 32, 40,116,121,112,
+ 101, 41, 10,114,101,116,117,114,110, 32, 99,108, 97,115,115,
+ 67,111,110,116, 97,105,110,101,114, 46, 99,117,114,114, 58,
+ 105,115,116,121,112,101,100,101,102, 40,116,121,112,101, 41,
+ 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114,
+ 58, 97,112,112,101,110,100, 32, 40,116, 41, 10,115,101,108,
+ 102, 46,110, 32, 61, 32,115,101,108,102, 46,110, 32, 43, 32,
+ 49, 10,115,101,108,102, 91,115,101,108,102, 46,110, 93, 32,
+ 61, 32,116, 10,116, 46,112, 97,114,101,110,116, 32, 61, 32,
+ 115,101,108,102, 10,101,110,100, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,
+ 105,110,101,114, 58, 97,112,112,101,110,100,116,121,112,101,
+ 100,101,102, 32, 40,116, 41, 10,115,101,108,102, 46,116,121,
+ 112,101,100,101,102,115, 46,110, 32, 61, 32,115,101,108,102,
+ 46,116,121,112,101,100,101,102,115, 46,110, 32, 43, 32, 49,
+ 10,115,101,108,102, 46,116,121,112,101,100,101,102,115, 91,
+ 115,101,108,102, 46,116,121,112,101,100,101,102,115, 46,110,
+ 93, 32, 61, 32,116, 10,101,110,100, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116,
+ 97,105,110,101,114, 58,111,118,101,114,108,111, 97,100, 32,
+ 40,108,110, 97,109,101, 41, 10,105,102, 32,110,111,116, 32,
+ 115,101,108,102, 46,108,110, 97,109,101,115, 91,108,110, 97,
+ 109,101, 93, 32,116,104,101,110, 10,115,101,108,102, 46,108,
+ 110, 97,109,101,115, 91,108,110, 97,109,101, 93, 32, 61, 32,
+ 48, 10,101,108,115,101, 10,115,101,108,102, 46,108,110, 97,
+ 109,101,115, 91,108,110, 97,109,101, 93, 32, 61, 32,115,101,
+ 108,102, 46,108,110, 97,109,101,115, 91,108,110, 97,109,101,
+ 93, 32, 43, 32, 49, 10,101,110,100, 10,114,101,116,117,114,
+ 110, 32,102,111,114,109, 97,116, 40, 34, 37, 48, 50,100, 34,
+ 44,115,101,108,102, 46,108,110, 97,109,101,115, 91,108,110,
+ 97,109,101, 93, 41, 10,101,110,100, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,
+ 105,110,101,114, 58,102,105,110,100,116,121,112,101,100,101,
+ 102, 32, 40,116,121,112,101, 41, 10,108,111, 99, 97,108, 32,
+ 101,110,118, 32, 61, 32,115,101,108,102, 10,119,104,105,108,
+ 101, 32,101,110,118, 32,100,111, 10,105,102, 32,101,110,118,
+ 46,116,121,112,101,100,101,102,115, 32,116,104,101,110, 10,
+ 108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101,
+ 32,101,110,118, 46,116,121,112,101,100,101,102,115, 91,105,
+ 93, 32,100,111, 10,105,102, 32,101,110,118, 46,116,121,112,
+ 101,100,101,102,115, 91,105, 93, 46,117,116,121,112,101, 32,
+ 61, 61, 32,116,121,112,101, 32,116,104,101,110, 10,108,111,
+ 99, 97,108, 32,109,111,100, 49, 44,116,121,112,101, 49, 32,
+ 61, 32,101,110,118, 46,116,121,112,101,100,101,102,115, 91,
+ 105, 93, 46,109,111,100, 44,101,110,118, 46,116,121,112,101,
+ 100,101,102,115, 91,105, 93, 46,116,121,112,101, 10,108,111,
+ 99, 97,108, 32,109,111,100, 50, 44,116,121,112,101, 50, 32,
+ 61, 32,102,105,110,100,116,121,112,101,100,101,102, 40,116,
+ 121,112,101, 49, 41, 10,114,101,116,117,114,110, 32,109,111,
+ 100, 50, 46, 46, 39, 32, 39, 46, 46,109,111,100, 49, 44,116,
+ 121,112,101, 50, 10,101,110,100, 10,105, 32, 61, 32,105, 43,
+ 49, 10,101,110,100, 10,101,110,100, 10,101,110,118, 32, 61,
+ 32,101,110,118, 46,112, 97,114,101,110,116, 10,101,110,100,
+ 10,114,101,116,117,114,110, 32, 39, 39, 44,116,121,112,101,
+ 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 58,
+ 105,115,116,121,112,101,100,101,102, 32, 40,116,121,112,101,
+ 41, 10,108,111, 99, 97,108, 32,101,110,118, 32, 61, 32,115,
+ 101,108,102, 10,119,104,105,108,101, 32,101,110,118, 32,100,
+ 111, 10,105,102, 32,101,110,118, 46,116,121,112,101,100,101,
+ 102,115, 32,116,104,101,110, 10,108,111, 99, 97,108, 32,105,
+ 61, 49, 10,119,104,105,108,101, 32,101,110,118, 46,116,121,
+ 112,101,100,101,102,115, 91,105, 93, 32,100,111, 10,105,102,
+ 32,101,110,118, 46,116,121,112,101,100,101,102,115, 91,105,
+ 93, 46,117,116,121,112,101, 32, 61, 61, 32,116,121,112,101,
+ 32,116,104,101,110, 10,114,101,116,117,114,110, 32, 49, 10,
+ 101,110,100, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100,
+ 10,101,110,100, 10,101,110,118, 32, 61, 32,101,110,118, 46,
+ 112, 97,114,101,110,116, 10,101,110,100, 10,114,101,116,117,
+ 114,110, 32,110,105,108, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,
+ 116, 97,105,110,101,114, 58,100,111,112, 97,114,115,101, 32,
+ 40,115, 41, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32,
+ 98, 44,101, 44,110, 97,109,101, 44, 98,111,100,121, 32, 61,
+ 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115,
+ 42,109,111,100,117,108,101, 37,115, 37,115, 42, 40, 91, 95,
+ 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40, 37,
+ 98,123,125, 41, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,
+ 116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,101,
+ 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101,
+ 41, 10, 77,111,100,117,108,101, 40,110, 97,109,101, 44, 98,
+ 111,100,121, 41, 10,114,101,116,117,114,110, 32,115,116,114,
+ 115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32,
+ 98, 44,101, 44,110, 97,109,101, 32, 61, 32,115,116,114,102,
+ 105,110,100, 40,115, 44, 34, 94, 37,115, 42, 35,100,101,102,
+ 105,110,101, 37,115, 37,115, 42, 40, 91, 94, 37,115, 93, 42,
+ 41, 91, 94, 92,110, 93, 42, 92,110, 37,115, 42, 34, 41, 10,
+ 105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,114,114,
+ 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40,
+ 115, 44, 98, 44,101, 41, 10, 68,101,102,105,110,101, 40,110,
+ 97,109,101, 41, 10,114,101,116,117,114,110, 32,115,116,114,
+ 115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32,
+ 98, 44,101, 44, 98,111,100,121, 32, 61, 32,115,116,114,102,
+ 105,110,100, 40,115, 44, 34, 94, 37,115, 42,101,110,117,109,
+ 91, 94,123, 93, 42, 40, 37, 98,123,125, 41, 37,115, 42, 59,
+ 63, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,116,104,101,
+ 110, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32, 61, 32,
+ 115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41, 10, 69,
+ 110,117,109,101,114, 97,116,101, 40, 98,111,100,121, 41, 10,
+ 114,101,116,117,114,110, 32,115,116,114,115,117, 98, 40,115,
+ 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,100, 10, 10,
+ 100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44, 98,111,
+ 100,121, 44,110, 97,109,101, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115, 44, 34, 94, 37,115, 42,116,121,112,101,100,
+ 101,102, 37,115, 37,115, 42,101,110,117,109, 91, 94,123, 93,
+ 42, 40, 37, 98,123,125, 41, 37,115, 42, 40, 91, 37,119, 95,
+ 93, 91, 94, 37,115, 93, 42, 41, 37,115, 42, 59, 37,115, 42,
+ 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,
+ 117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,
+ 117, 98, 40,115, 44, 98, 44,101, 41, 10, 69,110,117,109,101,
+ 114, 97,116,101, 40, 98,111,100,121, 41, 10, 84,121,112,101,
+ 100,101,102, 40, 34,105,110,116, 32, 34, 46, 46,110, 97,109,
+ 101, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117,
+ 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,
+ 101, 44,100,101, 99,108, 44,107,105,110,100, 44, 97,114,103,
+ 44, 99,111,110,115,116, 32, 61, 32,115,116,114,102,105,110,
+ 100, 40,115, 44, 34, 94, 37,115, 42, 40, 91, 95, 37,119, 93,
+ 91, 95, 37,119, 37,115, 37, 42, 38, 93, 42,111,112,101,114,
+ 97,116,111,114, 41, 37,115, 42, 40, 91, 94, 37,115, 93, 91,
+ 94, 37,115, 93, 42, 41, 37,115, 42, 40, 37, 98, 40, 41, 41,
+ 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41,
+ 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,
+ 116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,101,
+ 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101,
+ 41, 10, 79,112,101,114, 97,116,111,114, 40,100,101, 99,108,
+ 44,107,105,110,100, 44, 97,114,103, 44, 99,111,110,115,116,
+ 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117, 98,
+ 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101,
+ 44,100,101, 99,108, 44, 97,114,103, 44, 99,111,110,115,116,
+ 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94,
+ 37,115, 42, 40, 91,126, 95, 37,119, 93, 91, 95, 64, 37,119,
+ 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93, 41, 37,115,
+ 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,
+ 110, 63,115, 63,116, 63, 41, 37,115, 42, 61, 63, 37,115, 42,
+ 48, 63, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32,
+ 110,111,116, 32, 98, 32,116,104,101,110, 10, 10, 98, 44,101,
+ 44,100,101, 99,108, 44, 97,114,103, 44, 99,111,110,115,116,
+ 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94,
+ 37,115, 42, 40, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37,
+ 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115,
+ 63,116, 63, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,101,
+ 110,100, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,
+ 117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,
+ 117, 98, 40,115, 44, 98, 44,101, 41, 10, 70,117,110, 99,116,
+ 105,111,110, 40,100,101, 99,108, 44, 97,114,103, 44, 99,111,
+ 110,115,116, 41, 10,114,101,116,117,114,110, 32,115,116,114,
+ 115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32,
+ 98, 44,101, 44,100,101, 99,108, 44, 97,114,103, 44, 99,111,
+ 110,115,116, 32, 61, 32,115,116,114,102,105,110,100, 40,115,
+ 44, 34, 94, 37,115, 42, 40, 91,126, 95, 37,119, 93, 91, 95,
+ 64, 37,119, 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93,
+ 41, 37,115, 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99,
+ 63,111, 63,110, 63,115, 63,116, 63, 41, 37,115, 42, 37, 98,
+ 123,125, 37,115, 42, 34, 41, 10,105,102, 32,110,111,116, 32,
+ 98, 32,116,104,101,110, 10, 10, 98, 44,101, 44,100,101, 99,
+ 108, 44, 97,114,103, 44, 99,111,110,115,116, 32, 61, 32,115,
+ 116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 40,
+ 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37, 98, 40, 41, 41,
+ 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41,
+ 37,115, 42, 37, 98,123,125, 37,115, 42, 34, 41, 10,101,110,
+ 100, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,
+ 114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117,
+ 98, 40,115, 44, 98, 44,101, 41, 10, 70,117,110, 99,116,105,
+ 111,110, 40,100,101, 99,108, 44, 97,114,103, 44, 99,111,110,
+ 115,116, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,
+ 117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98,
+ 44,101, 44,110, 97,109,101, 44, 98, 97,115,101, 44, 98,111,
+ 100,121, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44,
+ 34, 94, 37,115, 42, 99,108, 97,115,115, 37,115, 42, 40, 91,
+ 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40,
+ 46, 45, 41, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42,
+ 59, 37,115, 42, 34, 41, 10,105,102, 32,110,111,116, 32, 98,
+ 32,116,104,101,110, 10, 98, 44,101, 44,110, 97,109,101, 44,
+ 98, 97,115,101, 44, 98,111,100,121, 32, 61, 32,115,116,114,
+ 102,105,110,100, 40,115, 44, 34, 94, 37,115, 42,115,116,114,
+ 117, 99,116, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,
+ 119, 93, 42, 41, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 40,
+ 37, 98,123,125, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,
+ 105,102, 32,110,111,116, 32, 98, 32,116,104,101,110, 10, 98,
+ 97,115,101, 32, 61, 32, 39, 39, 10, 98, 44,101, 44, 98,111,
+ 100,121, 44,110, 97,109,101, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115, 44, 34, 94, 37,115, 42,116,121,112,101,100,
+ 101,102, 37,115, 37,115, 42,115,116,114,117, 99,116, 37,115,
+ 37,115, 42, 91, 95, 37,119, 93, 42, 37,115, 42, 40, 37, 98,
+ 123,125, 41, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,
+ 119, 93, 42, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,101,
+ 110,100, 10,101,110,100, 10,105,102, 32, 98, 32,116,104,101,
+ 110, 10,105,102, 32, 98, 97,115,101, 32,126, 61, 32, 39, 39,
+ 32,116,104,101,110, 10,108,111, 99, 97,108, 32, 98, 44,101,
+ 10, 98, 44,101, 44, 98, 97,115,101, 32, 61, 32,115,116,114,
+ 102,105,110,100, 40, 98, 97,115,101, 44, 34, 46, 45, 40, 91,
+ 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 36, 34, 41, 10,
+ 101,110,100, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32,
+ 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41,
+ 10, 67,108, 97,115,115, 40,110, 97,109,101, 44, 98, 97,115,
+ 101, 44, 98,111,100,121, 41, 10,114,101,116,117,114,110, 32,
+ 115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,
+ 110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108,111, 99,
+ 97,108, 32, 98, 44,101, 44,116,121,112,101,115, 32, 61, 32,
+ 115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42,
+ 116,121,112,101,100,101,102, 37,115, 37,115, 42, 40, 46, 45,
+ 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32, 98,
+ 32,116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,
+ 101, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,
+ 101, 41, 10, 84,121,112,101,100,101,102, 40,116,121,112,101,
+ 115, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117,
+ 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,
+ 101, 44,100,101, 99,108, 32, 61, 32,115,116,114,102,105,110,
+ 100, 40,115, 44, 34, 94, 37,115, 42, 40, 91, 95, 37,119, 93,
+ 91, 95, 64, 37,115, 37,119, 37,100, 37, 42, 38, 93, 42, 91,
+ 95, 37,119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42, 34,
+ 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,
+ 114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117,
+ 98, 40,115, 44, 98, 44,101, 41, 10, 86, 97,114,105, 97, 98,
+ 108,101, 40,100,101, 99,108, 41, 10,114,101,116,117,114,110,
+ 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,
+ 101,110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108,111,
+ 99, 97,108, 32, 98, 44,101, 44,100,101, 99,108, 32, 61, 32,
+ 115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42,
+ 40, 91, 95, 37,119, 93, 91, 93, 91, 95, 64, 37,115, 37,119,
+ 37,100, 37, 42, 38, 37, 45, 37, 62, 93, 42, 91, 93, 95, 37,
+ 119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,
+ 105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,114,114,
+ 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40,
+ 115, 44, 98, 44,101, 41, 10, 65,114,114, 97,121, 40,100,101,
+ 99,108, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,
+ 117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98,
+ 44,101, 44, 99,111,100,101, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115, 44, 34, 94, 37,115, 42, 40, 37, 98, 92, 49,
+ 92, 50, 41, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110,
+ 10, 67,111,100,101, 40,115,116,114,115,117, 98, 40, 99,111,
+ 100,101, 44, 50, 44, 45, 50, 41, 41, 10,114,101,116,117,114,
+ 110, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41,
+ 10,101,110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108,
+ 111, 99, 97,108, 32, 98, 44,101, 44,108,105,110,101, 32, 61,
+ 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115,
+ 42, 37, 36, 40, 46, 45, 92,110, 41, 34, 41, 10,105,102, 32,
+ 98, 32,116,104,101,110, 10, 86,101,114, 98, 97,116,105,109,
+ 40,108,105,110,101, 41, 10,114,101,116,117,114,110, 32,115,
+ 116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10,105,102, 32,103,115,117, 98,
+ 40,115, 44, 34, 37,115, 37,115, 42, 34, 44, 34, 34, 41, 32,
+ 126, 61, 32, 34, 34, 32,116,104,101,110, 10, 95, 99,117,114,
+ 114, 95, 99,111,100,101, 32, 61, 32,115, 10,101,114,114,111,
+ 114, 40, 34, 35,112, 97,114,115,101, 32,101,114,114,111,114,
+ 34, 41, 10,101,108,115,101, 10,114,101,116,117,114,110, 32,
+ 34, 34, 10,101,110,100, 10,101,110,100, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116,
+ 97,105,110,101,114, 58,112, 97,114,115,101, 32, 40,115, 41,
+ 10,119,104,105,108,101, 32,115, 32,126, 61, 32, 39, 39, 32,
+ 100,111, 10,115, 32, 61, 32,115,101,108,102, 58,100,111,112,
+ 97,114,115,101, 40,115, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 99,108, 97,115,115, 80, 97, 99,107, 97,
+ 103,101, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32, 61, 32,
+ 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 44,
+ 10,116,121,112,101, 32, 61, 32, 39,112, 97, 99,107, 97,103,
+ 101, 39, 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,
+ 115,115, 80, 97, 99,107, 97,103,101, 44,116,111,108,117, 97,
+ 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 58,
+ 112,114,105,110,116, 32, 40, 41, 10,112,114,105,110,116, 40,
+ 34, 80, 97, 99,107, 97,103,101, 58, 32, 34, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 41, 10,108,111, 99, 97,108, 32,
+ 105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91,
+ 105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,112,
+ 114,105,110,116, 40, 34, 34, 44, 34, 34, 41, 10,105, 32, 61,
+ 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97,
+ 99,107, 97,103,101, 58,112,114,101,112,114,111, 99,101,115,
+ 115, 32, 40, 41, 10,115,101,108,102, 46, 99,111,100,101, 32,
+ 61, 32, 34, 92,110, 34, 46, 46,115,101,108,102, 46, 99,111,
+ 100,101, 10, 10,108,111, 99, 97,108, 32, 86, 32, 61, 32,123,
+ 125, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,
+ 115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34,
+ 92,110, 40, 37,115, 42, 37, 36, 91, 94, 37, 91, 37, 93, 93,
+ 91, 94, 92,110, 93, 42, 41, 34, 44,102,117,110, 99,116,105,
+ 111,110, 32, 40,118, 41, 10,116,105,110,115,101,114,116, 40,
+ 37, 86, 44,118, 41, 10,114,101,116,117,114,110, 32, 34, 92,
+ 110, 36, 34, 46, 46,103,101,116,110, 40, 37, 86, 41, 46, 46,
+ 34, 36, 34, 10,101,110,100, 41, 10, 10,108,111, 99, 97,108,
+ 32, 67, 32, 61, 32,123,125, 10,115,101,108,102, 46, 99,111,
+ 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46,
+ 99,111,100,101, 44, 34, 92,110, 37,115, 42, 37, 36, 37, 91,
+ 34, 44, 34, 92, 49, 34, 41, 10,115,101,108,102, 46, 99,111,
+ 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46,
+ 99,111,100,101, 44, 34, 92,110, 37,115, 42, 37, 36, 37, 93,
+ 34, 44, 34, 92, 50, 34, 41, 10,115,101,108,102, 46, 99,111,
+ 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46,
+ 99,111,100,101, 44, 34, 40, 37, 98, 92, 49, 92, 50, 41, 34,
+ 44, 32,102,117,110, 99,116,105,111,110, 32, 40, 99, 41, 10,
+ 116,105,110,115,101,114,116, 40, 37, 67, 44, 99, 41, 10,114,
+ 101,116,117,114,110, 32, 34, 92,110, 36, 91, 34, 46, 46,103,
+ 101,116,110, 40, 37, 67, 41, 46, 46, 34, 93, 36, 34, 10,101,
+ 110,100, 41, 10, 10, 10,115,101,108,102, 46, 99,111,100,101,
+ 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,
+ 100,101, 44, 34, 40, 47, 47, 91, 94, 92,110, 93, 42, 41, 34,
+ 44, 34, 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32,
+ 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,
+ 101, 44, 34, 47, 37, 42, 34, 44, 34, 92, 49, 34, 41, 10,115,
+ 101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98,
+ 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 37, 42, 47,
+ 34, 44, 34, 92, 50, 34, 41, 10,115,101,108,102, 46, 99,111,
+ 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46,
+ 99,111,100,101, 44, 34, 37, 98, 92, 49, 92, 50, 34, 44, 34,
+ 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,
+ 103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44,
+ 34, 92, 49, 34, 44, 34, 47, 37, 42, 34, 41, 10,115,101,108,
+ 102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,
+ 101,108,102, 46, 99,111,100,101, 44, 34, 92, 50, 34, 44, 34,
+ 37, 42, 47, 34, 41, 10,115,101,108,102, 46, 99,111,100,101,
+ 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,
+ 100,101, 44, 34, 37,115, 42, 64, 37,115, 42, 34, 44, 34, 64,
+ 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,
+ 103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44,
+ 34, 37,115, 63,105,110,108,105,110,101, 40, 37,115, 41, 34,
+ 44, 34, 37, 49, 34, 41, 10,115,101,108,102, 46, 99,111,100,
+ 101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,
+ 111,100,101, 44, 34, 37,115, 63,101,120,116,101,114,110, 40,
+ 37,115, 41, 34, 44, 34, 37, 49, 34, 41, 10,115,101,108,102,
+ 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,101,
+ 108,102, 46, 99,111,100,101, 44, 34, 37,115, 63,118,105,114,
+ 116,117, 97,108, 40, 37,115, 41, 34, 44, 34, 37, 49, 34, 41,
+ 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,
+ 117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34,112,
+ 117, 98,108,105, 99, 58, 34, 44, 34, 34, 41, 10,115,101,108,
+ 102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,
+ 101,108,102, 46, 99,111,100,101, 44, 34, 40, 91, 94, 37,119,
+ 95, 93, 41,118,111,105,100, 37,115, 42, 37, 42, 34, 44, 34,
+ 37, 49, 95,117,115,101,114,100, 97,116, 97, 32, 34, 41, 10,
+ 115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117,
+ 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 40, 91,
+ 94, 37,119, 95, 93, 41,118,111,105,100, 37,115, 42, 37, 42,
+ 34, 44, 34, 37, 49, 95,117,115,101,114,100, 97,116, 97, 32,
+ 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,
+ 103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44,
+ 34, 40, 91, 94, 37,119, 95, 93, 41, 99,104, 97,114, 37,115,
+ 42, 37, 42, 34, 44, 34, 37, 49, 95, 99,115,116,114,105,110,
+ 103, 32, 34, 41, 10, 10, 10,115,101,108,102, 46, 99,111,100,
+ 101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,
+ 111,100,101, 44, 34, 37, 36, 37, 91, 40, 37,100, 43, 41, 37,
+ 93, 37, 36, 34, 44,102,117,110, 99,116,105,111,110, 32, 40,
+ 110, 41, 10,114,101,116,117,114,110, 32, 37, 67, 91,116,111,
+ 110,117,109, 98,101,114, 40,110, 41, 93, 10,101,110,100, 41,
+ 10, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,
+ 115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34,
+ 37, 36, 40, 37,100, 43, 41, 37, 36, 34, 44,102,117,110, 99,
+ 116,105,111,110, 32, 40,110, 41, 10,114,101,116,117,114,110,
+ 32, 37, 86, 91,116,111,110,117,109, 98,101,114, 40,110, 41,
+ 93, 10,101,110,100, 41, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97, 99,
+ 107, 97,103,101, 58,112,114,101, 97,109, 98,108,101, 32, 40,
+ 41, 10,111,117,116,112,117,116, 40, 39, 47, 42, 92,110, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 42, 42, 32, 76,117,
+ 97, 32, 98,105,110,100,105,110,103, 58, 32, 39, 46, 46,115,
+ 101,108,102, 46,110, 97,109,101, 46, 46, 39, 92,110, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 42, 42, 32, 71,101,110,
+ 101,114, 97,116,101,100, 32, 97,117,116,111,109, 97,116,105,
+ 99, 97,108,108,121, 32, 98,121, 32, 39, 46, 46, 84, 79, 76,
+ 85, 65, 95, 86, 69, 82, 83, 73, 79, 78, 46, 46, 39, 32,111,
+ 110, 32, 39, 46, 46,100, 97,116,101, 40, 41, 46, 46, 39, 46,
+ 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 42, 47,
+ 92,110, 92,110, 39, 41, 10, 10,111,117,116,112,117,116, 40,
+ 39, 35,105,110, 99,108,117,100,101, 32, 34,108,117, 97, 47,
+ 116,111,108,117, 97, 46,104, 34, 92,110, 92,110, 39, 41, 10,
+ 10,105,102, 32,110,111,116, 32,102,108, 97,103,115, 46,104,
+ 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 47,
+ 42, 32, 69,120,112,111,114,116,101,100, 32,102,117,110, 99,
+ 116,105,111,110, 32, 42, 47, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39,105,110,116, 32,116,111,108,117, 97, 95, 39, 46,
+ 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 95,111,
+ 112,101,110, 32, 40,108,117, 97, 95, 83,116, 97,116,101, 42,
+ 32,116,111,108,117, 97, 95, 83, 41, 59, 39, 41, 10,111,117,
+ 116,112,117,116, 40, 39,118,111,105,100, 32,116,111,108,117,
+ 97, 95, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46,
+ 46, 39, 95, 99,108,111,115,101, 32, 40,108,117, 97, 95, 83,
+ 116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 59,
+ 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41,
+ 10,101,110,100, 10, 10,108,111, 99, 97,108, 32,105, 61, 49,
+ 10,119,104,105,108,101, 32,115,101,108,102, 91,105, 93, 32,
+ 100,111, 10,115,101,108,102, 91,105, 93, 58,112,114,101, 97,
+ 109, 98,108,101, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,
+ 101,110,100, 10,111,117,116,112,117,116, 40, 39, 92,110, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 47, 42, 32,102,117,
+ 110, 99,116,105,111,110, 32,116,111, 32,114,101,103,105,115,
+ 116,101,114, 32,116,121,112,101, 32, 42, 47, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39,115,116, 97,116,105, 99, 32,118,
+ 111,105,100, 32,116,111,108,117, 97, 73, 95,114,101,103, 95,
+ 116,121,112,101,115, 32, 40,108,117, 97, 95, 83,116, 97,116,
+ 101, 42, 32,116,111,108,117, 97, 95, 83, 41, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39,123, 39, 41, 10,102,111,114,101,
+ 97, 99,104, 40, 95,117,115,101,114,116,121,112,101, 44,102,
+ 117,110, 99,116,105,111,110, 40,110, 44,118, 41, 32,111,117,
+ 116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,117,115,
+ 101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 39, 44,118, 44, 39, 34, 41, 59, 39, 41, 32,101,110,100,
+ 41, 10,111,117,116,112,117,116, 40, 39,125, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 92,110, 39, 41, 10, 10,111,117,
+ 116,112,117,116, 40, 39, 47, 42, 32,101,114,114,111,114, 32,
+ 109,101,115,115, 97,103,101,115, 32, 42, 47, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 35,100,101,102,105,110,101, 32,
+ 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 83, 69, 76, 70, 32,
+ 116,111,108,117, 97, 95,101,114,114,111,114, 40,116,111,108,
+ 117, 97, 95, 83, 44, 92, 34,105,110,118, 97,108,105,100, 32,
+ 92, 39,115,101,108,102, 92, 39, 92, 34, 41, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 35,100,101,102,105,110,101, 32,
+ 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 65, 83, 83, 73, 71,
+ 78, 32,116,111,108,117, 97, 95,101,114,114,111,114, 40,116,
+ 111,108,117, 97, 95, 83, 44, 92, 34, 35,118,105,110,118, 97,
+ 108,105,100, 32,116,121,112,101, 32,105,110, 32,118, 97,114,
+ 105, 97, 98,108,101, 32, 97,115,115,105,103,110,109,101,110,
+ 116, 46, 92, 34, 41, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 92,110, 39, 41, 10,101,110,100, 10, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97, 99,
+ 107, 97,103,101, 58,114,101,103,105,115,116,101,114, 32, 40,
+ 41, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32, 79,112,
+ 101,110, 32,102,117,110, 99,116,105,111,110, 32, 42, 47, 34,
+ 41, 10,111,117,116,112,117,116, 40, 34,105,110,116, 32,116,
+ 111,108,117, 97, 95, 34, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 46, 46, 34, 95,111,112,101,110, 32, 40,108,117, 97,
+ 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83,
+ 41, 34, 41, 10,111,117,116,112,117,116, 40, 34,123, 34, 41,
+ 10,111,117,116,112,117,116, 40, 34, 32,116,111,108,117, 97,
+ 95,111,112,101,110, 40,116,111,108,117, 97, 95, 83, 41, 59,
+ 34, 41, 10,111,117,116,112,117,116, 40, 34, 32,116,111,108,
+ 117, 97, 73, 95,114,101,103, 95,116,121,112,101,115, 40,116,
+ 111,108,117, 97, 95, 83, 41, 59, 34, 41, 10,108,111, 99, 97,
+ 108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,
+ 102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93,
+ 58,114,101,103,105,115,116,101,114, 40, 41, 10,105, 32, 61,
+ 32,105, 43, 49, 10,101,110,100, 10,111,117,116,112,117,116,
+ 40, 34, 32,114,101,116,117,114,110, 32, 49, 59, 34, 41, 10,
+ 111,117,116,112,117,116, 40, 34,125, 34, 41, 10,101,110,100,
+ 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 80, 97, 99,107, 97,103,101, 58,117,110,114,101,
+ 103,105,115,116,101,114, 32, 40, 41, 10,111,117,116,112,117,
+ 116, 40, 34, 47, 42, 32, 67,108,111,115,101, 32,102,117,110,
+ 99,116,105,111,110, 32, 42, 47, 34, 41, 10,111,117,116,112,
+ 117,116, 40, 34,118,111,105,100, 32,116,111,108,117, 97, 95,
+ 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34,
+ 95, 99,108,111,115,101, 32, 40,108,117, 97, 95, 83,116, 97,
+ 116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10,
+ 111,117,116,112,117,116, 40, 34,123, 34, 41, 10,108,111, 99,
+ 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,
+ 108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105,
+ 93, 58,117,110,114,101,103,105,115,116,101,114, 40, 41, 10,
+ 105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,111,117,116,
+ 112,117,116, 40, 34,125, 34, 41, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80,
+ 97, 99,107, 97,103,101, 58,104,101, 97,100,101,114, 32, 40,
+ 41, 10,111,117,116,112,117,116, 40, 39, 47, 42, 92,110, 39,
+ 41, 32,111,117,116,112,117,116, 40, 39, 42, 42, 32, 76,117,
+ 97, 32, 98,105,110,100,105,110,103, 58, 32, 39, 46, 46,115,
+ 101,108,102, 46,110, 97,109,101, 46, 46, 39, 92,110, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 42, 42, 32, 71,101,110,
+ 101,114, 97,116,101,100, 32, 97,117,116,111,109, 97,116,105,
+ 99, 97,108,108,121, 32, 98,121, 32, 39, 46, 46, 84, 79, 76,
+ 85, 65, 95, 86, 69, 82, 83, 73, 79, 78, 46, 46, 39, 32,111,
+ 110, 32, 39, 46, 46,100, 97,116,101, 40, 41, 46, 46, 39, 46,
+ 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 42, 47,
+ 92,110, 92,110, 39, 41, 10, 10,105,102, 32,110,111,116, 32,
+ 102,108, 97,103,115, 46,104, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 47, 42, 32, 69,120,112,111,114,116,
+ 101,100, 32,102,117,110, 99,116,105,111,110, 32, 42, 47, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39,105,110,116, 32,116,
+ 111,108,117, 97, 95, 39, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 46, 46, 39, 95,111,112,101,110, 32, 40,108,117, 97,
+ 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83,
+ 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,118,111,
+ 105,100, 32,116,111,108,117, 97, 95, 39, 46, 46,115,101,108,
+ 102, 46,110, 97,109,101, 46, 46, 39, 95, 99,108,111,115,101,
+ 32, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,
+ 108,117, 97, 95, 83, 41, 59, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39, 92,110, 39, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 80, 97,
+ 99,107, 97,103,101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,
+ 115,101, 32, 61, 32, 99,108, 97,115,115, 80, 97, 99,107, 97,
+ 103,101, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108,
+ 117, 97, 95,116, 97,103, 41, 10,114,101,116,117,114,110, 32,
+ 116, 10,101,110,100, 10, 10, 10, 10, 10,102,117,110, 99,116,
+ 105,111,110, 32, 80, 97, 99,107, 97,103,101, 32, 40,110, 97,
+ 109,101, 41, 10, 10,108,111, 99, 97,108, 32, 99,111,100,101,
+ 32, 61, 32,114,101, 97,100, 40, 34, 42, 97, 34, 41, 10, 99,
+ 111,100,101, 32, 61, 32, 34, 92,110, 34, 32, 46, 46, 32, 99,
+ 111,100,101, 10, 10,108,111, 99, 97,108, 32,110,115,117, 98,
+ 115,116, 10,114,101,112,101, 97,116, 10, 99,111,100,101, 44,
+ 110,115,117, 98,115,116, 32, 61, 32,103,115,117, 98, 40, 99,
+ 111,100,101, 44, 34, 92,110, 37,115, 42, 37, 36, 60, 40, 46,
+ 45, 41, 62, 37,115, 42, 92,110, 34, 44,102,117,110, 99,116,
+ 105,111,110, 32, 40,102,110, 41, 10,108,111, 99, 97,108, 32,
+ 102,112, 44,109,115,103, 32, 61, 32,111,112,101,110,102,105,
+ 108,101, 40,102,110, 44, 39,114, 39, 41, 10,105,102, 32,110,
+ 111,116, 32,102,112, 32,116,104,101,110, 10,101,114,114,111,
+ 114, 40, 39, 35, 39, 46, 46,109,115,103, 46, 46, 39, 58, 32,
+ 39, 46, 46,102,110, 41, 10,101,110,100, 10,108,111, 99, 97,
+ 108, 32,115, 32, 61, 32,114,101, 97,100, 40,102,112, 44, 39,
+ 42, 97, 39, 41, 10, 99,108,111,115,101,102,105,108,101, 40,
+ 102,112, 41, 10,114,101,116,117,114,110, 32, 34, 92,110, 34,
+ 32, 46, 46, 32,115, 10,101,110,100, 41, 10,117,110,116,105,
+ 108, 32,110,115,117, 98,115,116, 61, 61, 48, 10, 10, 10,108,
+ 111, 99, 97,108, 32,110,115,117, 98,115,116, 10,114,101,112,
+ 101, 97,116, 10, 99,111,100,101, 44,110,115,117, 98,115,116,
+ 32, 61, 10,103,115,117, 98, 40, 99,111,100,101, 44, 34, 92,
+ 110, 37,115, 42, 37, 36,123, 40, 46, 45, 41,125, 37,115, 42,
+ 92,110, 34, 44, 10,102,117,110, 99,116,105,111,110, 32, 40,
+ 102,110, 41, 10,108,111, 99, 97,108, 32,102,112, 44,109,115,
+ 103, 32, 61, 32,111,112,101,110,102,105,108,101, 40,102,110,
+ 44, 39,114, 39, 41, 10,105,102, 32,110,111,116, 32,102,112,
+ 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35, 39,
+ 46, 46,109,115,103, 46, 46, 39, 58, 32, 39, 46, 46,102,110,
+ 41, 10,101,110,100, 10,108,111, 99, 97,108, 32,115, 32, 61,
+ 32,114,101, 97,100, 40,102,112, 44, 39, 42, 97, 39, 41, 10,
+ 99,108,111,115,101,102,105,108,101, 40,102,112, 41, 10, 10,
+ 108,111, 99, 97,108, 32, 84, 32, 61, 32,123, 99,111,100,101,
+ 61, 34, 92,110, 34,125, 10,115, 61, 32, 34, 92,110, 34, 32,
+ 46, 46, 32,115, 32, 46, 46, 32, 34, 92,110, 34, 10, 10,103,
+ 115,117, 98, 40,115, 44, 34, 92,110, 40, 46, 45, 41, 91, 84,
+ 116, 93, 91, 79,111, 93, 91, 76,108, 93, 91, 85,117, 93, 91,
+ 65, 97, 93, 95, 91, 69,101, 93, 91, 88,120, 93, 91, 80,112,
+ 93, 91, 79,111, 93, 91, 82,114, 93, 91, 84,116, 93, 91, 94,
+ 92,110, 93, 42, 92,110, 34, 44, 10,102,117,110, 99,116,105,
+ 111,110, 32, 40, 99, 41, 32, 37, 84, 46, 99,111,100,101, 32,
+ 61, 32, 37, 84, 46, 99,111,100,101, 32, 46, 46, 32, 99, 32,
+ 46, 46, 32, 34, 92,110, 34, 32,101,110,100, 10, 41, 10, 10,
+ 103,115,117, 98, 40,115, 44, 34, 92,110, 91, 94, 92,110, 93,
+ 42, 91, 84,116, 93, 91, 79,111, 93, 91, 76,108, 93, 91, 85,
+ 117, 93, 91, 65, 97, 93, 95, 91, 66, 98, 93, 91, 69,101, 93,
+ 91, 71,103, 93, 91, 73,105, 93, 91, 78,110, 93, 91, 94, 92,
+ 110, 93, 42, 34, 46, 46, 10, 34, 40, 46, 45, 41, 34, 32, 46,
+ 46, 10, 34, 92,110, 91, 94, 92,110, 93, 42, 91, 84,116, 93,
+ 91, 79,111, 93, 91, 76,108, 93, 91, 85,117, 93, 91, 65, 97,
+ 93, 95, 91, 69,101, 93, 91, 78,110, 93, 91, 68,100, 93, 91,
+ 94, 92,110, 93, 42, 92,110, 34, 44, 10,102,117,110, 99,116,
+ 105,111,110, 32, 40, 99, 41, 32, 37, 84, 46, 99,111,100,101,
+ 32, 61, 32, 37, 84, 46, 99,111,100,101, 32, 46, 46, 32, 99,
+ 32, 46, 46, 32, 34, 92,110, 34, 32,101,110,100, 10, 41, 10,
+ 114,101,116,117,114,110, 32, 84, 46, 99,111,100,101, 10,101,
+ 110,100, 41, 10,117,110,116,105,108, 32,110,115,117, 98,115,
+ 116, 61, 61, 48, 10, 10,108,111, 99, 97,108, 32,116, 32, 61,
+ 32, 95, 80, 97, 99,107, 97,103,101, 40, 95, 67,111,110,116,
+ 97,105,110,101,114,123,110, 97,109,101, 61,110, 97,109,101,
+ 44, 32, 99,111,100,101, 61, 99,111,100,101,125, 41, 10,112,
+ 117,115,104, 40,116, 41, 10,116, 58,112,114,101,112,114,111,
+ 99,101,115,115, 40, 41, 10,116, 58,112, 97,114,115,101, 40,
+ 116, 46, 99,111,100,101, 41, 10,112,111,112, 40, 41, 10,114,
+ 101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,
+ 108, 97,115,115, 77,111,100,117,108,101, 32, 61, 32,123, 10,
+ 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 67,111,
+ 110,116, 97,105,110,101,114, 44, 10,116,121,112,101, 32, 61,
+ 32, 39,109,111,100,117,108,101, 39, 10,125, 10,115,101,116,
+ 116, 97,103, 40, 99,108, 97,115,115, 77,111,100,117,108,101,
+ 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 77,111,
+ 100,117,108,101, 58,114,101,103,105,115,116,101,114, 32, 40,
+ 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95,109,111,100,117,108,101, 40,116,111,108,117, 97, 95,
+ 83, 44, 34, 39, 46, 46,115,101,108,102, 46,110, 97,109,101,
+ 46, 46, 39, 34, 41, 59, 39, 41, 10,108,111, 99, 97,108, 32,
+ 105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91,
+ 105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,114,
+ 101,103,105,115,116,101,114, 40, 41, 10,105, 32, 61, 32,105,
+ 43, 49, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 77,111,100,
+ 117,108,101, 58,117,110,114,101,103,105,115,116,101,114, 32,
+ 40, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97,
+ 95,112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95,
+ 83, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98,
+ 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,
+ 115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 34, 41, 59,
+ 39, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 77,111,100,117,108,101, 58,
+ 112,114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,
+ 111,115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 77,111,100,117,108,101,123, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,110,
+ 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,
+ 110, 97,109,101, 46, 46, 34, 39, 59, 34, 41, 10,108,111, 99,
+ 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,
+ 108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105,
+ 93, 58,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 32, 34, 44, 34, 44, 34, 41, 10,105, 32, 61, 32,105, 43,
+ 49, 10,101,110,100, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41,
+ 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 95, 77,111,100,117,108,101, 32, 40,116, 41, 10,116, 46,
+ 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 77,111,
+ 100,117,108,101, 10,115,101,116,116, 97,103, 40,116, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,110,
+ 100, 40,116, 41, 10,114,101,116,117,114,110, 32,116, 10,101,
+ 110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 77,111,100,117,108,101, 32, 40,110, 44, 98, 41, 10,108,111,
+ 99, 97,108, 32,116, 32, 61, 32, 95, 77,111,100,117,108,101,
+ 40, 95, 67,111,110,116, 97,105,110,101,114,123,110, 97,109,
+ 101, 61,110,125, 41, 10,112,117,115,104, 40,116, 41, 10,116,
+ 58,112, 97,114,115,101, 40,115,116,114,115,117, 98, 40, 98,
+ 44, 50, 44,115,116,114,108,101,110, 40, 98, 41, 45, 49, 41,
+ 41, 10,112,111,112, 40, 41, 10,114,101,116,117,114,110, 32,
+ 116, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 68,
+ 101,102,105,110,101, 32, 61, 32,123, 10,110, 97,109,101, 32,
+ 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101, 32, 61, 32, 99,
+ 108, 97,115,115, 70,101, 97,116,117,114,101, 44, 10,125, 10,
+ 115,101,116,116, 97,103, 40, 99,108, 97,115,115, 68,101,102,
+ 105,110,101, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 68,101,102,105,110,101, 58,114,101,103,105,115,116,101,
+ 114, 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 32, 61, 32,
+ 115,101,108,102, 58,105,110,109,111,100,117,108,101, 40, 41,
+ 10,105,102, 32,112, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115,
+ 116, 97,110,116, 40,116,111,108,117, 97, 95, 83, 44, 34, 39,
+ 46, 46,112, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,
+ 102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,
+ 115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 41, 59, 39,
+ 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,110,116,
+ 40,116,111,108,117, 97, 95, 83, 44, 78, 85, 76, 76, 44, 34,
+ 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46,
+ 39, 34, 44, 39, 46, 46,115,101,108,102, 46,110, 97,109,101,
+ 46, 46, 39, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,
+ 115,115, 68,101,102,105,110,101, 58,117,110,114,101,103,105,
+ 115,116,101,114, 32, 40, 41, 10,105,102, 32,110,111,116, 32,
+ 115,101,108,102, 58,105,110,109,111,100,117,108,101, 40, 41,
+ 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,
+ 108,117, 97, 95,112,117,115,104,110,105,108, 40,116,111,108,
+ 117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,101,116,103,
+ 108,111, 98, 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34,
+ 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46,
+ 39, 34, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 68,101,102,105,110,101, 58,112,114,105,110,116, 32, 40,
+ 105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,114,
+ 105,110,116, 40,105,100,101,110,116, 46, 46, 34, 68,101,102,
+ 105,110,101,123, 34, 41, 10,112,114,105,110,116, 40,105,100,
+ 101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39,
+ 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 32,108,110, 97,109,101, 32, 61, 32, 39, 34,
+ 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 95, 68,101,102,105,110,101, 32, 40,116, 41, 10,116, 46,
+ 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 68,101,
+ 102,105,110,101, 10,115,101,116,116, 97,103, 40,116, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 10,105,102, 32,116,
+ 46,110, 97,109,101, 32, 61, 61, 32, 39, 39, 32,116,104,101,
+ 110, 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97,108,
+ 105,100, 32,100,101,102,105,110,101, 34, 41, 10,101,110,100,
+ 10, 10, 97,112,112,101,110,100, 40,116, 41, 10,114,101,116,
+ 117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 68,101,102,105,110,101, 32, 40,
+ 110, 41, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,
+ 108,105,116, 40,110, 44, 39, 64, 39, 41, 10,114,101,116,117,
+ 114,110, 32, 95, 68,101,102,105,110,101, 32,123, 10,110, 97,
+ 109,101, 32, 61, 32,116, 91, 49, 93, 44, 10,108,110, 97,109,
+ 101, 32, 61, 32,116, 91, 50, 93, 32,111,114, 32,116, 91, 49,
+ 93, 10,125, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 69,
+ 110,117,109,101,114, 97,116,101, 32, 61, 32,123, 10, 95, 98,
+ 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,
+ 117,114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40, 99,
+ 108, 97,115,115, 69,110,117,109,101,114, 97,116,101, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 69,110,117,109,
+ 101,114, 97,116,101, 58,114,101,103,105,115,116,101,114, 32,
+ 40, 41, 10,108,111, 99, 97,108, 32,112, 32, 61, 32,115,101,
+ 108,102, 58,105,110, 99,108, 97,115,115, 40, 41, 32,111,114,
+ 32,115,101,108,102, 58,105,110,109,111,100,117,108,101, 40,
+ 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,
+ 108,101, 32,115,101,108,102, 91,105, 93, 32,100,111, 10,105,
+ 102, 32,112, 32,116,104,101,110, 10,105,102, 32,115,101,108,
+ 102, 58,105,110, 99,108, 97,115,115, 40, 41, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95, 99,111,110,115,116, 97,110,116, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 39, 46, 46,112, 46, 46, 39, 34, 44, 34,
+ 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101,115, 91,
+ 105, 93, 46, 46, 39, 34, 44, 39, 46, 46,112, 46, 46, 39, 58,
+ 58, 39, 46, 46,115,101,108,102, 91,105, 93, 46, 46, 39, 41,
+ 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116,
+ 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,
+ 110,116, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,
+ 112, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46,
+ 108,110, 97,109,101,115, 91,105, 93, 46, 46, 39, 34, 44, 39,
+ 46, 46,115,101,108,102, 91,105, 93, 46, 46, 39, 41, 59, 39,
+ 41, 10,101,110,100, 10,101,108,115,101, 10,111,117,116,112,
+ 117,116, 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115,
+ 116, 97,110,116, 40,116,111,108,117, 97, 95, 83, 44, 78, 85,
+ 76, 76, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,
+ 109,101,115, 91,105, 93, 46, 46, 39, 34, 44, 39, 46, 46,115,
+ 101,108,102, 91,105, 93, 46, 46, 39, 41, 59, 39, 41, 10,101,
+ 110,100, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,
+ 101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 69,110,117,109,101,114, 97,116,101, 58,117,
+ 110,114,101,103,105,115,116,101,114, 32, 40, 41, 10,105,102,
+ 32,115,101,108,102, 58,105,110, 99,108, 97,115,115, 40, 41,
+ 61, 61,110,105,108, 32, 97,110,100, 32,115,101,108,102, 58,
+ 105,110,109,111,100,117,108,101, 40, 41, 61, 61,110,105,108,
+ 32,116,104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49,
+ 10,119,104,105,108,101, 32,115,101,108,102, 91,105, 93, 32,
+ 100,111, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97,
+ 95,112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95,
+ 83, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98,
+ 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,
+ 115,101,108,102, 46,108,110, 97,109,101,115, 91,105, 93, 46,
+ 46, 39, 34, 41, 59, 39, 41, 10,105, 32, 61, 32,105, 43, 49,
+ 10,101,110,100, 10,101,110,100, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 69,
+ 110,117,109,101,114, 97,116,101, 58,112,114,105,110,116, 32,
+ 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 69,110,
+ 117,109,101,114, 97,116,101,123, 34, 41, 10,108,111, 99, 97,
+ 108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,
+ 102, 91,105, 93, 32,100,111, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32, 39, 34, 46, 46,115,101,108,
+ 102, 91,105, 93, 46, 46, 34, 39, 40, 34, 46, 46,115,101,108,
+ 102, 46,108,110, 97,109,101,115, 91,105, 93, 46, 46, 34, 41,
+ 44, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100,
+ 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,
+ 125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 69,110,117,
+ 109,101,114, 97,116,101, 32, 40,116, 41, 10,116, 46, 95, 98,
+ 97,115,101, 32, 61, 32, 99,108, 97,115,115, 69,110,117,109,
+ 101,114, 97,116,101, 10,115,101,116,116, 97,103, 40,116, 44,
+ 116,111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,
+ 110,100, 40,116, 41, 10,114,101,116,117,114,110, 32,116, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 69,110,117,109,101,114, 97,116,101, 32, 40, 98, 41, 10,
+ 108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116,
+ 40,115,116,114,115,117, 98, 40, 98, 44, 50, 44, 45, 50, 41,
+ 44, 39, 44, 39, 41, 10,108,111, 99, 97,108, 32,105, 32, 61,
+ 32, 49, 10,108,111, 99, 97,108, 32,101, 32, 61, 32,123,110,
+ 61, 48,125, 10,119,104,105,108,101, 32,116, 91,105, 93, 32,
+ 100,111, 10,108,111, 99, 97,108, 32,116,116, 32, 61, 32,115,
+ 112,108,105,116, 40,116, 91,105, 93, 44, 39, 61, 39, 41, 10,
+ 101, 46,110, 32, 61, 32,101, 46,110, 32, 43, 32, 49, 10,101,
+ 91,101, 46,110, 93, 32, 61, 32,116,116, 91, 49, 93, 10,105,
+ 32, 61, 32,105, 43, 49, 10,101,110,100, 10, 10,105, 32, 61,
+ 32, 49, 10,101, 46,108,110, 97,109,101,115, 32, 61, 32,123,
+ 125, 10,119,104,105,108,101, 32,101, 91,105, 93, 32,100,111,
+ 10,108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,
+ 116, 40,101, 91,105, 93, 44, 39, 64, 39, 41, 10,101, 91,105,
+ 93, 32, 61, 32,116, 91, 49, 93, 10,101, 46,108,110, 97,109,
+ 101,115, 91,105, 93, 32, 61, 32,116, 91, 50, 93, 32,111,114,
+ 32,116, 91, 49, 93, 10,105, 32, 61, 32,105, 43, 49, 10,101,
+ 110,100, 10,114,101,116,117,114,110, 32, 95, 69,110,117,109,
+ 101,114, 97,116,101, 40,101, 41, 10,101,110,100, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 68,101, 99,
+ 108, 97,114, 97,116,105,111,110, 32, 61, 32,123, 10, 95, 98,
+ 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,
+ 117,114,101, 44, 10,109,111,100, 32, 61, 32, 39, 39, 44, 10,
+ 116,121,112,101, 32, 61, 32, 39, 39, 44, 10,112,116,114, 32,
+ 61, 32, 39, 39, 44, 10,110, 97,109,101, 32, 61, 32, 39, 39,
+ 44, 10,100,105,109, 32, 61, 32, 39, 39, 44, 10,114,101,116,
+ 32, 61, 32, 39, 39, 44, 10,100,101,102, 32, 61, 32, 39, 39,
+ 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115,
+ 68,101, 99,108, 97,114, 97,116,105,111,110, 44,116,111,108,
+ 117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,
+ 105,111,110, 32, 99,114,101, 97,116,101, 95,118, 97,114,110,
+ 97,109,101, 32, 40, 41, 10,105,102, 32,110,111,116, 32, 95,
+ 118, 97,114,110,117,109, 98,101,114, 32,116,104,101,110, 32,
+ 95,118, 97,114,110,117,109, 98,101,114, 32, 61, 32, 48, 32,
+ 101,110,100, 10, 95,118, 97,114,110,117,109, 98,101,114, 32,
+ 61, 32, 95,118, 97,114,110,117,109, 98,101,114, 32, 43, 32,
+ 49, 10,114,101,116,117,114,110, 32, 34,116,111,108,117, 97,
+ 95,118, 97,114, 95, 34, 46, 46, 95,118, 97,114,110,117,109,
+ 98,101,114, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,
+ 114, 97,116,105,111,110, 58, 99,104,101, 99,107,110, 97,109,
+ 101, 32, 40, 41, 10, 10,105,102, 32,115,116,114,115,117, 98,
+ 40,115,101,108,102, 46,110, 97,109,101, 44, 49, 44, 49, 41,
+ 32, 61, 61, 32, 39, 91, 39, 32, 97,110,100, 32,110,111,116,
+ 32,105,115,116,121,112,101, 40,115,101,108,102, 46,116,121,
+ 112,101, 41, 32,116,104,101,110, 10,115,101,108,102, 46,110,
+ 97,109,101, 32, 61, 32,115,101,108,102, 46,116,121,112,101,
+ 46, 46,115,101,108,102, 46,110, 97,109,101, 10,108,111, 99,
+ 97,108, 32,109, 32, 61, 32,115,112,108,105,116, 40,115,101,
+ 108,102, 46,109,111,100, 44, 39, 37,115, 37,115, 42, 39, 41,
+ 10,115,101,108,102, 46,116,121,112,101, 32, 61, 32,109, 91,
+ 109, 46,110, 93, 10,115,101,108,102, 46,109,111,100, 32, 61,
+ 32, 99,111,110, 99, 97,116, 40,109, 44, 49, 44,109, 46,110,
+ 45, 49, 41, 10,101,110,100, 10, 10,108,111, 99, 97,108, 32,
+ 116, 32, 61, 32,115,112,108,105,116, 40,115,101,108,102, 46,
+ 110, 97,109,101, 44, 39, 61, 39, 41, 10,105,102, 32,116, 46,
+ 110, 61, 61, 50, 32,116,104,101,110, 10,115,101,108,102, 46,
+ 110, 97,109,101, 32, 61, 32,116, 91, 49, 93, 10,115,101,108,
+ 102, 46,100,101,102, 32, 61, 32,116, 91,116, 46,110, 93, 10,
+ 101,110,100, 10, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,
+ 100, 32, 61, 32,115,116,114,102,105,110,100, 40,115,101,108,
+ 102, 46,110, 97,109,101, 44, 34, 37, 91, 40, 46, 45, 41, 37,
+ 93, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10,115,
+ 101,108,102, 46,110, 97,109,101, 32, 61, 32,115,116,114,115,
+ 117, 98, 40,115,101,108,102, 46,110, 97,109,101, 44, 49, 44,
+ 98, 45, 49, 41, 10,115,101,108,102, 46,100,105,109, 32, 61,
+ 32,100, 10,101,110,100, 10, 10, 10,105,102, 32,115,101,108,
+ 102, 46,116,121,112,101, 32,126, 61, 32, 39, 39, 32, 97,110,
+ 100, 32,115,101,108,102, 46,116,121,112,101, 32,126, 61, 32,
+ 39,118,111,105,100, 39, 32, 97,110,100, 32,115,101,108,102,
+ 46,110, 97,109,101, 32, 61, 61, 32, 39, 39, 32,116,104,101,
+ 110, 10,115,101,108,102, 46,110, 97,109,101, 32, 61, 32, 99,
+ 114,101, 97,116,101, 95,118, 97,114,110, 97,109,101, 40, 41,
+ 10,101,108,115,101,105,102, 32,115,101,108,102, 46,107,105,
+ 110,100, 61, 61, 39,118, 97,114, 39, 32,116,104,101,110, 10,
+ 105,102, 32,115,101,108,102, 46,116,121,112,101, 61, 61, 39,
+ 39, 32, 97,110,100, 32,115,101,108,102, 46,110, 97,109,101,
+ 126, 61, 39, 39, 32,116,104,101,110, 10,115,101,108,102, 46,
+ 116,121,112,101, 32, 61, 32,115,101,108,102, 46,116,121,112,
+ 101, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,115,101,
+ 108,102, 46,110, 97,109,101, 32, 61, 32, 99,114,101, 97,116,
+ 101, 95,118, 97,114,110, 97,109,101, 40, 41, 10,101,108,115,
+ 101,105,102, 32,105,115,116,121,112,101, 40,115,101,108,102,
+ 46,110, 97,109,101, 41, 32,116,104,101,110, 10,105,102, 32,
+ 115,101,108,102, 46,116,121,112,101, 61, 61, 39, 39, 32,116,
+ 104,101,110, 32,115,101,108,102, 46,116,121,112,101, 32, 61,
+ 32,115,101,108,102, 46,110, 97,109,101, 10,101,108,115,101,
+ 32,115,101,108,102, 46,116,121,112,101, 32, 61, 32,115,101,
+ 108,102, 46,116,121,112,101, 46, 46, 39, 32, 39, 46, 46,115,
+ 101,108,102, 46,110, 97,109,101, 32,101,110,100, 10,115,101,
+ 108,102, 46,110, 97,109,101, 32, 61, 32, 99,114,101, 97,116,
+ 101, 95,118, 97,114,110, 97,109,101, 40, 41, 10,101,110,100,
+ 10,101,110,100, 10, 10,101,110,100, 10, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,
+ 108, 97,114, 97,116,105,111,110, 58, 99,104,101, 99,107,116,
+ 121,112,101, 32, 40, 41, 10, 10, 10,105,102, 32,105,115, 98,
+ 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41,
+ 32, 97,110,100, 32,115,101,108,102, 46,112,116,114,126, 61,
+ 39, 39, 32,116,104,101,110, 10,115,101,108,102, 46,114,101,
+ 116, 32, 61, 32,115,101,108,102, 46,112,116,114, 10,115,101,
+ 108,102, 46,112,116,114, 32, 61, 32,110,105,108, 10,101,110,
+ 100, 10, 10, 10,105,102, 32,115,101,108,102, 46,100,105,109,
+ 126, 61, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,114,
+ 101,116,126, 61, 39, 39, 32,116,104,101,110, 10,101,114,114,
+ 111,114, 40, 39, 35,105,110,118, 97,108,105,100, 32,112, 97,
+ 114, 97,109,101,116,101,114, 58, 32, 99, 97,110,110,111,116,
+ 32,114,101,116,117,114,110, 32, 97,110, 32, 97,114,114, 97,
+ 121, 32,111,102, 32,118, 97,108,117,101,115, 39, 41, 10,101,
+ 110,100, 10, 10, 10,105,102, 32,115,101,108,102, 46,116,121,
+ 112,101,126, 61, 39, 39, 32,116,104,101,110, 10,114,101,103,
+ 116,121,112,101, 40,115,101,108,102, 46,116,121,112,101, 41,
+ 10,101,110,100, 10, 10, 10,105,102, 32,115,101,108,102, 46,
+ 116,121,112,101, 32, 61, 61, 32, 39, 95,117,115,101,114,100,
+ 97,116, 97, 39, 32,116,104,101,110, 32,115,101,108,102, 46,
+ 116,121,112,101, 32, 61, 32, 39,118,111,105,100, 42, 39, 10,
+ 101,108,115,101,105,102, 32,115,101,108,102, 46,116,121,112,
+ 101, 32, 61, 61, 32, 39, 95, 99,115,116,114,105,110,103, 39,
+ 32,116,104,101,110, 32,115,101,108,102, 46,116,121,112,101,
+ 32, 61, 32, 39, 99,104, 97,114, 42, 39, 10,101,110,100, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 58,112,114,105,110,116,
+ 32, 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110,123, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,109,
+ 111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,
+ 111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116,
+ 40,105,100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32,
+ 61, 32, 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101,
+ 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39,
+ 34, 46, 46,115,101,108,102, 46,112,116,114, 46, 46, 34, 39,
+ 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 32,100,105,109, 32, 61, 32, 39, 34, 46, 46,115,101,108,
+ 102, 46,100,105,109, 46, 46, 34, 39, 44, 34, 41, 10,112,114,
+ 105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,100,101,
+ 102, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,100,101,
+ 102, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,
+ 105,100,101,110,116, 46, 46, 34, 32,114,101,116, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,114,101,116, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,
+ 101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,
+ 110, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,115,101,
+ 108,102, 46,105,116,121,112,101, 44, 32,115,101,108,102, 46,
+ 116, 97,103, 32, 61, 32,116, 97,103,118, 97,114, 40,115,101,
+ 108,102, 46,116,121,112,101, 44,115,116,114,102,105,110,100,
+ 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111,110,115,
+ 116, 39, 41, 41, 10,101,110,100, 10, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108,
+ 97,114, 97,116,105,111,110, 58,111,117,116, 99,104,101, 99,
+ 107,116,121,112,101, 32, 40,110, 97,114,103, 41, 10,108,111,
+ 99, 97,108, 32,116, 97,103, 44, 32,100,101,102, 10,105,102,
+ 32,115,101,108,102, 46,100,105,109, 32,126, 61, 32, 39, 39,
+ 32,116,104,101,110, 10,116, 97,103, 32, 61, 32, 39, 76, 85,
+ 65, 95, 84, 84, 65, 66, 76, 69, 39, 10,100,101,102, 32, 61,
+ 32, 48, 10,101,108,115,101, 10,116, 97,103, 32, 61, 32,115,
+ 101,108,102, 46,116, 97,103, 10,100,101,102, 32, 61, 32,115,
+ 101,108,102, 46,100,101,102,126, 61, 39, 39, 32,111,114, 32,
+ 48, 10,101,110,100, 10,114,101,116,117,114,110, 32, 39,116,
+ 111,108,117, 97, 95,105,115,116,121,112,101, 40,116,111,108,
+ 117, 97, 95, 83, 44, 39, 46, 46,110, 97,114,103, 46, 46, 39,
+ 44, 39, 46, 46,116, 97,103, 46, 46, 39, 44, 39, 46, 46,100,
+ 101,102, 46, 46, 39, 41, 39, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 58,100,101, 99,108, 97,
+ 114,101, 32, 40,110, 97,114,103, 41, 10,108,111, 99, 97,108,
+ 32,112,116,114, 32, 61, 32, 39, 39, 10,105,102, 32,115,101,
+ 108,102, 46,112,116,114,126, 61, 39, 39, 32,116,104,101,110,
+ 32,112,116,114, 32, 61, 32, 39, 42, 39, 32,101,110,100, 10,
+ 111,117,116,112,117,116, 40, 34, 32, 34, 44,115,101,108,102,
+ 46,109,111,100, 44,115,101,108,102, 46,116,121,112,101, 44,
+ 112,116,114, 41, 10,105,102, 32,115,101,108,102, 46,100,105,
+ 109, 32,126, 61, 32, 39, 39, 32, 97,110,100, 32,116,111,110,
+ 117,109, 98,101,114, 40,115,101,108,102, 46,100,105,109, 41,
+ 61, 61,110,105,108, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117,116,
+ 112,117,116, 40,115,101,108,102, 46,110, 97,109,101, 41, 10,
+ 105,102, 32,115,101,108,102, 46,100,105,109, 32,126, 61, 32,
+ 39, 39, 32,116,104,101,110, 10,105,102, 32,116,111,110,117,
+ 109, 98,101,114, 40,115,101,108,102, 46,100,105,109, 41,126,
+ 61,110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39, 91, 39, 44,115,101,108,102, 46,100,105,109, 44,
+ 39, 93, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,
+ 117,116, 40, 39, 32, 61, 32, 40, 39, 44,115,101,108,102, 46,
+ 109,111,100, 44,115,101,108,102, 46,116,121,112,101, 44,112,
+ 116,114, 44, 39, 42, 41, 39, 44, 10, 39,109, 97,108,108,111,
+ 99, 40, 39, 44,115,101,108,102, 46,100,105,109, 44, 39, 42,
+ 115,105,122,101,111,102, 40, 39, 44,115,101,108,102, 46,116,
+ 121,112,101, 44,112,116,114, 44, 39, 41, 41, 59, 39, 41, 10,
+ 101,110,100, 10,101,108,115,101, 10,108,111, 99, 97,108, 32,
+ 116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,108,
+ 102, 46,116,121,112,101, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32, 61, 32, 39, 41, 10,105,102, 32,110,111,116, 32,116,
+ 32, 97,110,100, 32,112,116,114, 61, 61, 39, 39, 32,116,104,
+ 101,110, 32,111,117,116,112,117,116, 40, 39, 42, 39, 41, 32,
+ 101,110,100, 10,111,117,116,112,117,116, 40, 39, 40, 40, 39,
+ 44,115,101,108,102, 46,109,111,100, 44,115,101,108,102, 46,
+ 116,121,112,101, 41, 10,105,102, 32,110,111,116, 32,116, 32,
+ 116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 42, 39,
+ 41, 10,101,110,100, 10,111,117,116,112,117,116, 40, 39, 41,
+ 32, 39, 41, 10,108,111, 99, 97,108, 32,100,101,102, 32, 61,
+ 32, 48, 10,105,102, 32,115,101,108,102, 46,100,101,102, 32,
+ 126, 61, 32, 39, 39, 32,116,104,101,110, 32,100,101,102, 32,
+ 61, 32,115,101,108,102, 46,100,101,102, 32,101,110,100, 10,
+ 105,102, 32,116, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39,116,111,108,117, 97, 95,103,101,116, 39, 46, 46,
+ 116, 44, 39, 40,116,111,108,117, 97, 95, 83, 44, 39, 44,110,
+ 97,114,103, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41, 41,
+ 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116,
+ 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 39, 44,
+ 110, 97,114,103, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41,
+ 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58,
+ 103,101,116, 97,114,114, 97,121, 32, 40,110, 97,114,103, 41,
+ 10,105,102, 32,115,101,108,102, 46,100,105,109, 32,126, 61,
+ 32, 39, 39, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 39, 32,123, 39, 41, 10,108,111, 99, 97,108, 32,100,101,
+ 102, 32, 61, 32,115,101,108,102, 46,100,101,102,126, 61, 39,
+ 39, 32,111,114, 32, 48, 10,111,117,116,112,117,116, 40, 39,
+ 32,105,102, 32, 40, 33,116,111,108,117, 97, 95, 97,114,114,
+ 97,121,105,115,116,121,112,101, 40,116,111,108,117, 97, 95,
+ 83, 44, 39, 44,110, 97,114,103, 44, 39, 44, 39, 44,115,101,
+ 108,102, 46,116, 97,103, 44, 39, 44, 39, 44,115,101,108,102,
+ 46,100,105,109, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41,
+ 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,103,111,
+ 116,111, 32,116,111,108,117, 97, 95,108,101,114,114,111,114,
+ 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,101,108,
+ 115,101, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,123, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,105,
+ 110,116, 32,105, 59, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32,102,111,114, 40,105, 61, 48, 59, 32,105, 60, 39, 46,
+ 46,115,101,108,102, 46,100,105,109, 46, 46, 39, 59,105, 43,
+ 43, 41, 39, 41, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,
+ 105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,
+ 112,101, 41, 10,108,111, 99, 97,108, 32,112,116,114, 32, 61,
+ 32, 39, 39, 10,105,102, 32,115,101,108,102, 46,112,116,114,
+ 126, 61, 39, 39, 32,116,104,101,110, 32,112,116,114, 32, 61,
+ 32, 39, 42, 39, 32,101,110,100, 10,111,117,116,112,117,116,
+ 40, 39, 32, 39, 44,115,101,108,102, 46,110, 97,109,101, 46,
+ 46, 39, 91,105, 93, 32, 61, 32, 39, 41, 10,105,102, 32,110,
+ 111,116, 32,116, 32, 97,110,100, 32,112,116,114, 61, 61, 39,
+ 39, 32,116,104,101,110, 32,111,117,116,112,117,116, 40, 39,
+ 42, 39, 41, 32,101,110,100, 10,111,117,116,112,117,116, 40,
+ 39, 40, 40, 39, 44,115,101,108,102, 46,109,111,100, 44,115,
+ 101,108,102, 46,116,121,112,101, 41, 10,105,102, 32,110,111,
+ 116, 32,116, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117,116,112,117,
+ 116, 40, 39, 41, 32, 39, 41, 10,108,111, 99, 97,108, 32,100,
+ 101,102, 32, 61, 32, 48, 10,105,102, 32,115,101,108,102, 46,
+ 100,101,102, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 32,
+ 100,101,102, 32, 61, 32,115,101,108,102, 46,100,101,102, 32,
+ 101,110,100, 10,105,102, 32,116, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,
+ 116,102,105,101,108,100, 39, 46, 46,116, 46, 46, 39, 40,116,
+ 111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39,
+ 44,105, 43, 49, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59,
+ 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40,
+ 39,116,111,108,117, 97, 95,103,101,116,102,105,101,108,100,
+ 117,115,101,114,116,121,112,101, 40,116,111,108,117, 97, 95,
+ 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49, 44,
+ 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,110,
+ 100, 10,111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10,
+ 111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,
+ 105,111,110, 58,115,101,116, 97,114,114, 97,121, 32, 40,110,
+ 97,114,103, 41, 10,105,102, 32,115,101,108,102, 46,100,105,
+ 109, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112,
+ 117,116, 40, 39, 32,105,110,116, 32,105, 59, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 32,102,111,114, 40,105, 61, 48,
+ 59, 32,105, 60, 39, 46, 46,115,101,108,102, 46,100,105,109,
+ 46, 46, 39, 59,105, 43, 43, 41, 39, 41, 10,108,111, 99, 97,
+ 108, 32,116, 44, 99,116, 32, 61, 32,105,115, 98, 97,115,105,
+ 99, 40,115,101,108,102, 46,116,121,112,101, 41, 10,105,102,
+ 32,116, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39, 32,116,111,108,117, 97, 95,112,117,115,104,102,105,101,
+ 108,100, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117, 97,
+ 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49,
+ 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 44,115,101,108,102,
+ 46,110, 97,109,101, 44, 39, 91,105, 93, 41, 59, 39, 41, 10,
+ 101,108,115,101, 10,105,102, 32,115,101,108,102, 46,112,116,
+ 114, 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112,
+ 117,116, 40, 39, 35,105,102,100,101,102, 32, 95, 95, 99,112,
+ 108,117,115,112,108,117,115, 92,110, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 32,118,111,105,100, 42, 32,116,111,108,
+ 117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,110,101,119,
+ 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39, 40, 39,
+ 44,115,101,108,102, 46,110, 97,109,101, 44, 39, 91,105, 93,
+ 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 35,101,
+ 108,115,101, 92,110, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32,118,111,105,100, 42, 32,116,111,108,117, 97, 73, 95,
+ 99,108,111,110,101, 32, 61, 32,116,111,108,117, 97, 95, 99,
+ 111,112,121, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,
+ 105,100, 42, 41, 38, 39, 44,115,101,108,102, 46,110, 97,109,
+ 101, 44, 39, 91,105, 93, 44,115,105,122,101,111,102, 40, 39,
+ 44,115,101,108,102, 46,116,121,112,101, 44, 39, 41, 41, 59,
+ 39, 41, 10,111,117,116,112,117,116, 40, 39, 35,101,110,100,
+ 105,102, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95,112,117,115,104,102,105,101,108,
+ 100,117,115,101,114,116,121,112,101, 40,116,111,108,117, 97,
+ 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49,
+ 44,116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40,
+ 116,111,108,117, 97, 95, 83, 44,116,111,108,117, 97, 73, 95,
+ 99,108,111,110,101, 44, 39, 44,115,101,108,102, 46,116, 97,
+ 103, 44, 39, 41, 44, 39, 44,115,101,108,102, 46,116, 97,103,
+ 44, 39, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,125, 39, 41, 10, 10, 10,101,108,115,101, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,
+ 104,102,105,101,108,100,117,115,101,114,116,121,112,101, 40,
+ 116,111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44,
+ 39, 44,105, 43, 49, 44, 40,118,111,105,100, 42, 41, 39, 44,
+ 115,101,108,102, 46,110, 97,109,101, 44, 39, 91,105, 93, 44,
+ 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39,
+ 41, 10,101,110,100, 10,101,110,100, 10,111,117,116,112,117,
+ 116, 40, 39, 32,125, 39, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,
+ 115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58,102,
+ 114,101,101, 97,114,114, 97,121, 32, 40, 41, 10,105,102, 32,
+ 115,101,108,102, 46,100,105,109, 32,126, 61, 32, 39, 39, 32,
+ 97,110,100, 32,116,111,110,117,109, 98,101,114, 40,115,101,
+ 108,102, 46,100,105,109, 41, 61, 61,110,105,108, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 39, 32,102,114,101,
+ 101, 40, 39, 44,115,101,108,102, 46,110, 97,109,101, 44, 39,
+ 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110, 58,112, 97,115,115,
+ 112, 97,114, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46,
+ 112,116,114, 61, 61, 39, 38, 39, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 39, 42, 39, 46, 46,115,101,108,102,
+ 46,110, 97,109,101, 41, 10,101,108,115,101,105,102, 32,115,
+ 101,108,102, 46,114,101,116, 61, 61, 39, 42, 39, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 39, 38, 39, 46, 46,
+ 115,101,108,102, 46,110, 97,109,101, 41, 10,101,108,115,101,
+ 10,111,117,116,112,117,116, 40,115,101,108,102, 46,110, 97,
+ 109,101, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 58,114,101,116,118, 97,
+ 108,117,101, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46,
+ 114,101,116, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,
+ 108,111, 99, 97,108, 32,116, 44, 99,116, 32, 61, 32,105,115,
+ 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101,
+ 41, 10,105,102, 32,116, 32,116,104,101,110, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,
+ 104, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117, 97, 95,
+ 83, 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,
+ 101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32,116,
+ 111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,
+ 112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,105,
+ 100, 42, 41, 39, 46, 46,115,101,108,102, 46,110, 97,109,101,
+ 46, 46, 39, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44,
+ 39, 41, 59, 39, 41, 10,101,110,100, 10,114,101,116,117,114,
+ 110, 32, 49, 10,101,110,100, 10,114,101,116,117,114,110, 32,
+ 48, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 95, 68,101, 99,108, 97,114, 97,116,105,111,110, 32,
+ 40,116, 41, 10,105,102, 32,116, 46,110, 97,109,101, 32, 97,
+ 110,100, 32,116, 46,110, 97,109,101,126, 61, 39, 39, 32,116,
+ 104,101,110, 10,108,111, 99, 97,108, 32,110, 32, 61, 32,115,
+ 112,108,105,116, 40,116, 46,110, 97,109,101, 44, 39, 64, 39,
+ 41, 10,116, 46,110, 97,109,101, 32, 61, 32,110, 91, 49, 93,
+ 10,116, 46,108,110, 97,109,101, 32, 61, 32,103,115,117, 98,
+ 40,110, 91, 50, 93, 32,111,114, 32,110, 91, 49, 93, 44, 34,
+ 37, 91, 46, 45, 37, 93, 34, 44, 34, 34, 41, 10,101,110,100,
+ 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,
+ 115, 68,101, 99,108, 97,114, 97,116,105,111,110, 10,115,101,
+ 116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95,116, 97,
+ 103, 41, 10,116, 58, 99,104,101, 99,107,110, 97,109,101, 40,
+ 41, 10,116, 58, 99,104,101, 99,107,116,121,112,101, 40, 41,
+ 10,114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 68,101, 99,
+ 108, 97,114, 97,116,105,111,110, 32, 40,115, 44,107,105,110,
+ 100, 41, 10, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44,
+ 34, 37,115, 42, 61, 37,115, 42, 34, 44, 34, 61, 34, 41, 10,
+ 10,105,102, 32,107,105,110,100, 32, 61, 61, 32, 34,118, 97,
+ 114, 34, 32,116,104,101,110, 10, 10,105,102, 32,115, 32, 61,
+ 61, 32, 39, 39, 32,111,114, 32,115, 32, 61, 61, 32, 39,118,
+ 111,105,100, 39, 32,116,104,101,110, 10,114,101,116,117,114,
+ 110, 32, 95, 68,101, 99,108, 97,114, 97,116,105,111,110,123,
+ 116,121,112,101, 32, 61, 32, 39,118,111,105,100, 39, 44, 32,
+ 107,105,110,100, 32, 61, 32,107,105,110,100,125, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10,108,111, 99, 97,108, 32,116,
+ 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37, 42, 37,
+ 115, 42, 38, 39, 41, 10,105,102, 32,116, 46,110, 32, 61, 61,
+ 32, 50, 32,116,104,101,110, 10,105,102, 32,107,105,110,100,
+ 32, 61, 61, 32, 39,102,117,110, 99, 39, 32,116,104,101,110,
+ 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97,108,105,
+ 100, 32,102,117,110, 99,116,105,111,110, 32,114,101,116,117,
+ 114,110, 32,116,121,112,101, 58, 32, 34, 46, 46,115, 41, 10,
+ 101,110,100, 10,108,111, 99, 97,108, 32,109, 32, 61, 32,115,
+ 112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115, 37,115,
+ 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68,101, 99,
+ 108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,101, 32,
+ 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61, 32, 39,
+ 42, 39, 44, 10,114,101,116, 32, 61, 32, 39, 38, 39, 44, 10,
+ 116,121,112,101, 32, 61, 32,109, 91,109, 46,110, 93, 44, 10,
+ 109,111,100, 32, 61, 32, 99,111,110, 99, 97,116, 40,109, 44,
+ 49, 44,109, 46,110, 45, 49, 41, 44, 10,107,105,110,100, 32,
+ 61, 32,107,105,110,100, 10,125, 10,101,110,100, 10, 10, 10,
+ 116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37, 42,
+ 37,115, 42, 37, 42, 39, 41, 10,105,102, 32,116, 46,110, 32,
+ 61, 61, 32, 50, 32,116,104,101,110, 10,105,102, 32,107,105,
+ 110,100, 32, 61, 61, 32, 39,102,117,110, 99, 39, 32,116,104,
+ 101,110, 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97,
+ 108,105,100, 32,102,117,110, 99,116,105,111,110, 32,114,101,
+ 116,117,114,110, 32,116,121,112,101, 58, 32, 34, 46, 46,115,
+ 41, 10,101,110,100, 10,108,111, 99, 97,108, 32,109, 32, 61,
+ 32,115,112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115,
+ 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,
+ 101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61,
+ 32, 39, 42, 39, 44, 10,114,101,116, 32, 61, 32, 39, 42, 39,
+ 44, 10,116,121,112,101, 32, 61, 32,109, 91,109, 46,110, 93,
+ 44, 10,109,111,100, 32, 61, 32, 99,111,110, 99, 97,116, 40,
+ 109, 44, 49, 44,109, 46,110, 45, 49, 41, 44, 10,107,105,110,
+ 100, 32, 61, 32,107,105,110,100, 10,125, 10,101,110,100, 10,
+ 10, 10,116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39,
+ 38, 39, 41, 10,105,102, 32,116, 46,110, 32, 61, 61, 32, 50,
+ 32,116,104,101,110, 10,108,111, 99, 97,108, 32,109, 32, 61,
+ 32,115,112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115,
+ 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,
+ 101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61,
+ 32, 39, 38, 39, 44, 10,116,121,112,101, 32, 61, 32,109, 91,
+ 109, 46,110, 93, 44, 10,109,111,100, 32, 61, 32, 99,111,110,
+ 99, 97,116, 40,109, 44, 49, 44,109, 46,110, 45, 49, 41, 32,
+ 44, 10,107,105,110,100, 32, 61, 32,107,105,110,100, 10,125,
+ 10,101,110,100, 10, 10, 10,108,111, 99, 97,108, 32,115, 49,
+ 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 40, 37, 98, 92,
+ 91, 92, 93, 41, 34, 44,102,117,110, 99,116,105,111,110, 32,
+ 40,110, 41, 32,114,101,116,117,114,110, 32,103,115,117, 98,
+ 40,110, 44, 39, 37, 42, 39, 44, 39, 92, 49, 39, 41, 32,101,
+ 110,100, 41, 10,116, 32, 61, 32,115,112,108,105,116, 40,115,
+ 49, 44, 39, 37, 42, 39, 41, 10,105,102, 32,116, 46,110, 32,
+ 61, 61, 32, 50, 32,116,104,101,110, 10,116, 91, 50, 93, 32,
+ 61, 32,103,115,117, 98, 40,116, 91, 50, 93, 44, 39, 92, 49,
+ 39, 44, 39, 37, 42, 39, 41, 10,108,111, 99, 97,108, 32,109,
+ 32, 61, 32,115,112,108,105,116, 40,116, 91, 49, 93, 44, 39,
+ 37,115, 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32,
+ 95, 68,101, 99,108, 97,114, 97,116,105,111,110,123, 10,110,
+ 97,109,101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114,
+ 32, 61, 32, 39, 42, 39, 44, 10,116,121,112,101, 32, 61, 32,
+ 109, 91,109, 46,110, 93, 44, 10,109,111,100, 32, 61, 32, 99,
+ 111,110, 99, 97,116, 40,109, 44, 49, 44,109, 46,110, 45, 49,
+ 41, 32, 44, 10,107,105,110,100, 32, 61, 32,107,105,110,100,
+ 10,125, 10,101,110,100, 10, 10,105,102, 32,107,105,110,100,
+ 32, 61, 61, 32, 39,118, 97,114, 39, 32,116,104,101,110, 10,
+ 10,116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37,
+ 115, 37,115, 42, 39, 41, 10,108,111, 99, 97,108, 32,118, 10,
+ 105,102, 32,105,115,116,121,112,101, 40,116, 91,116, 46,110,
+ 93, 41, 32,116,104,101,110, 32,118, 32, 61, 32, 39, 39, 32,
+ 101,108,115,101, 32,118, 32, 61, 32,116, 91,116, 46,110, 93,
+ 59, 32,116, 46,110, 32, 61, 32,116, 46,110, 45, 49, 32,101,
+ 110,100, 10,114,101,116,117,114,110, 32, 95, 68,101, 99,108,
+ 97,114, 97,116,105,111,110,123, 10,110, 97,109,101, 32, 61,
+ 32,118, 44, 10,116,121,112,101, 32, 61, 32,116, 91,116, 46,
+ 110, 93, 44, 10,109,111,100, 32, 61, 32, 99,111,110, 99, 97,
+ 116, 40,116, 44, 49, 44,116, 46,110, 45, 49, 41, 44, 10,107,
+ 105,110,100, 32, 61, 32,107,105,110,100, 10,125, 10, 10,101,
+ 108,115,101, 10, 10, 10,116, 32, 61, 32,115,112,108,105,116,
+ 40,115, 44, 39, 37,115, 37,115, 42, 39, 41, 10,108,111, 99,
+ 97,108, 32,118, 32, 61, 32,116, 91,116, 46,110, 93, 10,108,
+ 111, 99, 97,108, 32,116,112, 44,109,100, 10,105,102, 32,116,
+ 46,110, 62, 49, 32,116,104,101,110, 10,116,112, 32, 61, 32,
+ 116, 91,116, 46,110, 45, 49, 93, 10,109,100, 32, 61, 32, 99,
+ 111,110, 99, 97,116, 40,116, 44, 49, 44,116, 46,110, 45, 50,
+ 41, 10,101,110,100, 10,114,101,116,117,114,110, 32, 95, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,
+ 101, 32, 61, 32,118, 44, 10,116,121,112,101, 32, 61, 32,116,
+ 112, 44, 10,109,111,100, 32, 61, 32,109,100, 44, 10,107,105,
+ 110,100, 32, 61, 32,107,105,110,100, 10,125, 10,101,110,100,
+ 10, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 86, 97,114,105,
+ 97, 98,108,101, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32,
+ 61, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,
+ 105,111,110, 44, 10,125, 10, 10,115,101,116,116, 97,103, 40,
+ 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 86, 97,114,105,
+ 97, 98,108,101, 58,112,114,105,110,116, 32, 40,105,100,101,
+ 110,116, 44, 99,108,111,115,101, 41, 10,112,114,105,110,116,
+ 40,105,100,101,110,116, 46, 46, 34, 86, 97,114,105, 97, 98,
+ 108,101,123, 34, 41, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34, 32,109,111,100, 32, 61, 32, 39, 34, 46,
+ 46,115,101,108,102, 46,109,111,100, 46, 46, 34, 39, 44, 34,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 32,116,121,112,101, 32, 61, 32, 39, 34, 46, 46,115,101,
+ 108,102, 46,116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,
+ 112,116,114, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,
+ 112,116,114, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,
+ 116, 40,105,100,101,110,116, 46, 46, 34, 32,110, 97,109,101,
+ 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,110, 97,109,
+ 101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,
+ 105,100,101,110,116, 46, 46, 34, 32,100,101,102, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,100,101,102, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 32,114,101,116, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46,114,101,116, 46, 46, 34, 39, 44, 34, 41,
+ 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,
+ 125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 86, 97,114,105, 97, 98,108,101, 58,103,101,116,118, 97,
+ 108,117,101, 32, 40, 99,108, 97,115,115, 44,115,116, 97,116,
+ 105, 99, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,
+ 100, 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10,114,
+ 101,116,117,114,110, 32, 99,108, 97,115,115, 46, 46, 39, 58,
+ 58, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,101,
+ 108,115,101,105,102, 32, 99,108, 97,115,115, 32,116,104,101,
+ 110, 10,114,101,116,117,114,110, 32, 39,115,101,108,102, 45,
+ 62, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,101,
+ 108,115,101, 10,114,101,116,117,114,110, 32,115,101,108,102,
+ 46,110, 97,109,101, 10,101,110,100, 10,101,110,100, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115,
+ 86, 97,114,105, 97, 98,108,101, 58,115,117,112, 99,111,100,
+ 101, 32, 40, 41, 10,108,111, 99, 97,108, 32, 99,108, 97,115,
+ 115, 32, 61, 32,115,101,108,102, 58,105,110, 99,108, 97,115,
+ 115, 40, 41, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32,
+ 116,104,101,110, 10,111,117,116,112,117,116, 40, 34, 47, 42,
+ 32,103,101,116, 32,102,117,110, 99,116,105,111,110, 58, 34,
+ 44,115,101,108,102, 46,110, 97,109,101, 44, 34, 32,111,102,
+ 32, 99,108, 97,115,115, 32, 34, 44, 99,108, 97,115,115, 44,
+ 34, 32, 42, 47, 34, 41, 10,101,108,115,101, 10,111,117,116,
+ 112,117,116, 40, 34, 47, 42, 32,103,101,116, 32,102,117,110,
+ 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46,110, 97,
+ 109,101, 44, 34, 32, 42, 47, 34, 41, 10,101,110,100, 10,115,
+ 101,108,102, 46, 99,103,101,116,110, 97,109,101, 32, 61, 32,
+ 115,101,108,102, 58, 99,102,117,110, 99,110, 97,109,101, 40,
+ 34,116,111,108,117, 97, 73, 95,103,101,116, 34, 41, 10,111,
+ 117,116,112,117,116, 40, 34,115,116, 97,116,105, 99, 32,105,
+ 110,116, 34, 44,115,101,108,102, 46, 99,103,101,116,110, 97,
+ 109,101, 44, 34, 40,108,117, 97, 95, 83,116, 97,116,101, 42,
+ 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116,
+ 112,117,116, 40, 34,123, 34, 41, 10, 10, 10,108,111, 99, 97,
+ 108, 32, 95, 44, 95, 44,115,116, 97,116,105, 99, 32, 61, 32,
+ 115,116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,
+ 100, 44, 39, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41,
+ 39, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100,
+ 32,115,116, 97,116,105, 99, 61, 61,110,105,108, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 39, 32, 39, 44, 99,
+ 108, 97,115,115, 44, 39, 42, 39, 44, 39,115,101,108,102, 32,
+ 61, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39,
+ 44, 99,108, 97,115,115, 44, 39, 42, 41, 32, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,
+ 116,117,115,101,114,116,121,112,101, 40,116,111,108,117, 97,
+ 95, 83, 44, 49, 44, 48, 41, 59, 39, 41, 10,101,108,115,101,
+ 105,102, 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10,
+ 95, 44, 95, 44,115,101,108,102, 46,109,111,100, 32, 61, 32,
+ 115,116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,
+ 100, 44, 39, 94, 37,115, 42,115,116, 97,116,105, 99, 37,115,
+ 37,115, 42, 40, 46, 42, 41, 39, 41, 10,101,110,100, 10, 10,
+ 10, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,
+ 115,116, 97,116,105, 99, 61, 61,110,105,108, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40,
+ 33,115,101,108,102, 41, 32, 84, 79, 76, 85, 65, 95, 69, 82,
+ 82, 95, 83, 69, 76, 70, 59, 39, 41, 59, 10,101,110,100, 10,
+ 10, 10,108,111, 99, 97,108, 32,116, 44, 99,116, 32, 61, 32,
+ 105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,
+ 112,101, 41, 10,105,102, 32,116, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,
+ 117,115,104, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117,
+ 97, 95, 83, 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 46, 46,
+ 115,101,108,102, 58,103,101,116,118, 97,108,117,101, 40, 99,
+ 108, 97,115,115, 44,115,116, 97,116,105, 99, 41, 46, 46, 39,
+ 41, 59, 39, 41, 10,101,108,115,101, 10,105,102, 32,115,101,
+ 108,102, 46,112,116,114, 32, 61, 61, 32, 39, 38, 39, 32,111,
+ 114, 32,115,101,108,102, 46,112,116,114, 32, 61, 61, 32, 39,
+ 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,
+ 111,105,100, 42, 41, 38, 39, 46, 46,115,101,108,102, 58,103,
+ 101,116,118, 97,108,117,101, 40, 99,108, 97,115,115, 44,115,
+ 116, 97,116,105, 99, 41, 46, 46, 39, 44, 39, 44,115,101,108,
+ 102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95,112,117,115,104,117,115,101,114,116,121,112,101, 40,
+ 116,111,108,117, 97, 95, 83, 44, 40,118,111,105,100, 42, 41,
+ 39, 46, 46,115,101,108,102, 58,103,101,116,118, 97,108,117,
+ 101, 40, 99,108, 97,115,115, 44,115,116, 97,116,105, 99, 41,
+ 46, 46, 39, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44,
+ 39, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,111,
+ 117,116,112,117,116, 40, 39, 32,114,101,116,117,114,110, 32,
+ 49, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,125, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41, 10,
+ 10, 10,105,102, 32,110,111,116, 32,115,116,114,102,105,110,
+ 100, 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111,110,
+ 115,116, 39, 41, 32,116,104,101,110, 10,105,102, 32, 99,108,
+ 97,115,115, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 34, 47, 42, 32,115,101,116, 32,102,117,110, 99,116,105,
+ 111,110, 58, 34, 44,115,101,108,102, 46,110, 97,109,101, 44,
+ 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44, 99,108,
+ 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,115,101,
+ 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,115,101,116,
+ 32,102,117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,
+ 102, 46,110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10,101,
+ 110,100, 10,115,101,108,102, 46, 99,115,101,116,110, 97,109,
+ 101, 32, 61, 32,115,101,108,102, 58, 99,102,117,110, 99,110,
+ 97,109,101, 40, 34,116,111,108,117, 97, 73, 95,115,101,116,
+ 34, 41, 10,111,117,116,112,117,116, 40, 34,115,116, 97,116,
+ 105, 99, 32,105,110,116, 34, 44,115,101,108,102, 46, 99,115,
+ 101,116,110, 97,109,101, 44, 34, 40,108,117, 97, 95, 83,116,
+ 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41,
+ 10,111,117,116,112,117,116, 40, 34,123, 34, 41, 10, 10, 10,
+ 108,111, 99, 97,108, 32,110, 97,114,103, 61, 49, 10,105,102,
+ 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116,
+ 105, 99, 61, 61,110,105,108, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32, 39, 44, 99,108, 97,115,115, 44,
+ 39, 42, 39, 44, 39,115,101,108,102, 32, 61, 32, 39, 41, 10,
+ 111,117,116,112,117,116, 40, 39, 40, 39, 44, 99,108, 97,115,
+ 115, 44, 39, 42, 41, 32, 39, 41, 10,111,117,116,112,117,116,
+ 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 49, 44,
+ 48, 41, 59, 39, 41, 10, 10,111,117,116,112,117,116, 40, 39,
+ 32,105,102, 32, 40, 33,115,101,108,102, 41, 32, 84, 79, 76,
+ 85, 65, 95, 69, 82, 82, 95, 83, 69, 76, 70, 59, 39, 41, 59,
+ 10,110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,
+ 101,108,115,101,105,102, 32,115,116, 97,116,105, 99, 32,116,
+ 104,101,110, 10, 95, 44, 95, 44,115,101,108,102, 46,109,111,
+ 100, 32, 61, 32,115,116,114,102,105,110,100, 40,115,101,108,
+ 102, 46,109,111,100, 44, 39, 94, 37,115, 42,115,116, 97,116,
+ 105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 39, 41, 10,110,
+ 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,101,110,
+ 100, 10, 10, 10,111,117,116,112,117,116, 40, 39, 32,105,102,
+ 32, 40, 33, 39, 46, 46,115,101,108,102, 58,111,117,116, 99,
+ 104,101, 99,107,116,121,112,101, 40,110, 97,114,103, 41, 46,
+ 46, 39, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,
+ 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 65, 83, 83, 73, 71,
+ 78, 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32,112,116,
+ 114, 32, 61, 32, 39, 39, 10,105,102, 32,115,101,108,102, 46,
+ 112,116,114,126, 61, 39, 39, 32,116,104,101,110, 32,112,116,
+ 114, 32, 61, 32, 39, 42, 39, 32,101,110,100, 10,111,117,116,
+ 112,117,116, 40, 39, 32, 39, 41, 10,105,102, 32, 99,108, 97,
+ 115,115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 99,108, 97,115,
+ 115, 46, 46, 39, 58, 58, 39, 46, 46,115,101,108,102, 46,110,
+ 97,109,101, 41, 10,101,108,115,101,105,102, 32, 99,108, 97,
+ 115,115, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39,115,101,108,102, 45, 62, 39, 46, 46,115,101,108,102, 46,
+ 110, 97,109,101, 41, 10,101,108,115,101, 10,111,117,116,112,
+ 117,116, 40,115,101,108,102, 46,110, 97,109,101, 41, 10,101,
+ 110,100, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,105,115,
+ 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101,
+ 41, 10,111,117,116,112,117,116, 40, 39, 32, 61, 32, 39, 41,
+ 10,105,102, 32,110,111,116, 32,116, 32, 97,110,100, 32,112,
+ 116,114, 61, 61, 39, 39, 32,116,104,101,110, 32,111,117,116,
+ 112,117,116, 40, 39, 42, 39, 41, 32,101,110,100, 10,111,117,
+ 116,112,117,116, 40, 39, 40, 40, 39, 44,115,101,108,102, 46,
+ 109,111,100, 44,115,101,108,102, 46,116,121,112,101, 41, 10,
+ 105,102, 32,110,111,116, 32,116, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,
+ 111,117,116,112,117,116, 40, 39, 41, 32, 39, 41, 10,108,111,
+ 99, 97,108, 32,100,101,102, 32, 61, 32, 48, 10,105,102, 32,
+ 115,101,108,102, 46,100,101,102, 32,126, 61, 32, 39, 39, 32,
+ 116,104,101,110, 32,100,101,102, 32, 61, 32,115,101,108,102,
+ 46,100,101,102, 32,101,110,100, 10,105,102, 32,116, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 39,116,111,108,
+ 117, 97, 95,103,101,116, 39, 46, 46,116, 44, 39, 40,116,111,
+ 108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,
+ 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,108,
+ 115,101, 10,111,117,116,112,117,116, 40, 39,116,111,108,117,
+ 97, 95,103,101,116,117,115,101,114,116,121,112,101, 40,116,
+ 111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39,
+ 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,
+ 110,100, 10,111,117,116,112,117,116, 40, 39, 32,114,101,116,
+ 117,114,110, 32, 48, 59, 39, 41, 10,111,117,116,112,117,116,
+ 40, 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,
+ 110, 39, 41, 10,101,110,100, 10, 10,101,110,100, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86, 97,
+ 114,105, 97, 98,108,101, 58,114,101,103,105,115,116,101,114,
+ 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 97,114,101,110,
+ 116, 32, 61, 32,115,101,108,102, 58,105,110, 99,108, 97,115,
+ 115, 40, 41, 32,111,114, 32,115,101,108,102, 58,105,110,109,
+ 111,100,117,108,101, 40, 41, 10,105,102, 32,112, 97,114,101,
+ 110,116, 32,116,104,101,110, 10,105,102, 32,115,101,108,102,
+ 46, 99,115,101,116,110, 97,109,101, 32,116,104,101,110, 10,
+ 111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,
+ 116, 97, 98,108,101,118, 97,114, 40,116,111,108,117, 97, 95,
+ 83, 44, 34, 39, 46, 46,112, 97,114,101,110,116, 46, 46, 39,
+ 34, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,
+ 101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,
+ 103,101,116,110, 97,109,101, 46, 46, 39, 44, 39, 46, 46,115,
+ 101,108,102, 46, 99,115,101,116,110, 97,109,101, 46, 46, 39,
+ 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,
+ 116, 40, 39, 32,116,111,108,117, 97, 95,116, 97, 98,108,101,
+ 118, 97,114, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46,
+ 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46,
+ 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34,
+ 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,116,110, 97,
+ 109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59, 39, 41, 10,
+ 101,110,100, 10,101,108,115,101, 10,105,102, 32,115,101,108,
+ 102, 46, 99,115,101,116,110, 97,109,101, 32,116,104,101,110,
+ 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97,
+ 95,103,108,111, 98, 97,108,118, 97,114, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110,
+ 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102,
+ 46, 99,103,101,116,110, 97,109,101, 46, 46, 39, 44, 39, 46,
+ 46,115,101,108,102, 46, 99,115,101,116,110, 97,109,101, 46,
+ 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,103,108,111,
+ 98, 97,108,118, 97,114, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46,
+ 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,
+ 116,110, 97,109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59,
+ 39, 41, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115,
+ 86, 97,114,105, 97, 98,108,101, 58,117,110,114,101,103,105,
+ 115,116,101,114, 32, 40, 41, 10,105,102, 32,115,101,108,102,
+ 58,105,110, 99,108, 97,115,115, 40, 41, 61, 61,110,105,108,
+ 32, 97,110,100, 32,115,101,108,102, 58,105,110,109,111,100,
+ 117,108,101, 40, 41, 61, 61,110,105,108, 32,116,104,101,110,
+ 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,103,
+ 101,116,103,108,111, 98, 97,108,115, 40,116,111,108,117, 97,
+ 95, 83, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,108,117, 97, 95,112,117,115,104,115,116,114,105,110,103,
+ 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 44,115,101,108,
+ 102, 46,108,110, 97,109,101, 44, 39, 34, 41, 59, 32,108,117,
+ 97, 95,112,117,115,104,110,105,108, 40,116,111,108,117, 97,
+ 95, 83, 41, 59, 32,108,117, 97, 95,114, 97,119,115,101,116,
+ 40,116,111,108,117, 97, 95, 83, 44, 45, 51, 41, 59, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,112,
+ 111,112, 40,116,111,108,117, 97, 95, 83, 44, 49, 41, 59, 39,
+ 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 95, 86, 97,114,105, 97, 98,108,
+ 101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61,
+ 32, 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 10,
+ 115,101,116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95,
+ 116, 97,103, 41, 10, 97,112,112,101,110,100, 40,116, 41, 10,
+ 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 86, 97,114,105, 97,
+ 98,108,101, 32, 40,115, 41, 10,114,101,116,117,114,110, 32,
+ 95, 86, 97,114,105, 97, 98,108,101, 32, 40, 68,101, 99,108,
+ 97,114, 97,116,105,111,110, 40,115, 44, 39,118, 97,114, 39,
+ 41, 41, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 65,
+ 114,114, 97,121, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32,
+ 61, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,
+ 105,111,110, 44, 10,125, 10, 10,115,101,116,116, 97,103, 40,
+ 99,108, 97,115,115, 65,114,114, 97,121, 44,116,111,108,117,
+ 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 65,114,114, 97,121, 58,112,
+ 114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,
+ 115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34, 65,114,114, 97,121,123, 34, 41, 10,112,114,105,
+ 110,116, 40,105,100,101,110,116, 46, 46, 34, 32,109,111,100,
+ 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100,
+ 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46,
+ 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39, 34, 46,
+ 46,115,101,108,102, 46,112,116,114, 46, 46, 34, 39, 44, 34,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,
+ 100,101,102, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,
+ 100,101,102, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,
+ 116, 40,105,100,101,110,116, 46, 46, 34, 32,100,105,109, 32,
+ 61, 32, 39, 34, 46, 46,115,101,108,102, 46,100,105,109, 46,
+ 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,
+ 101,110,116, 46, 46, 34, 32,114,101,116, 32, 61, 32, 39, 34,
+ 46, 46,115,101,108,102, 46,114,101,116, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,
+ 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 65,114,114, 97,121, 58,103,101,116,118, 97,108,
+ 117,101, 32, 40, 99,108, 97,115,115, 44,115,116, 97,116,105,
+ 99, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100,
+ 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10,114,101,
+ 116,117,114,110, 32, 99,108, 97,115,115, 46, 46, 39, 58, 58,
+ 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39,
+ 91,116,111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39,
+ 10,101,108,115,101,105,102, 32, 99,108, 97,115,115, 32,116,
+ 104,101,110, 10,114,101,116,117,114,110, 32, 39,115,101,108,
+ 102, 45, 62, 39, 46, 46,115,101,108,102, 46,110, 97,109,101,
+ 46, 46, 39, 91,116,111,108,117, 97, 73, 95,105,110,100,101,
+ 120, 93, 39, 10,101,108,115,101, 10,114,101,116,117,114,110,
+ 32,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,
+ 111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39, 10,101,
+ 110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 65,114,114, 97,121, 58,115,
+ 117,112, 99,111,100,101, 32, 40, 41, 10,108,111, 99, 97,108,
+ 32, 99,108, 97,115,115, 32, 61, 32,115,101,108,102, 58,105,
+ 110, 99,108, 97,115,115, 40, 41, 10, 10, 10,105,102, 32, 99,
+ 108, 97,115,115, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 34, 47, 42, 32,103,101,116, 32,102,117,110, 99,116,
+ 105,111,110, 58, 34, 44,115,101,108,102, 46,110, 97,109,101,
+ 44, 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44, 99,
+ 108, 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,103,101,
+ 116, 32,102,117,110, 99,116,105,111,110, 58, 34, 44,115,101,
+ 108,102, 46,110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10,
+ 101,110,100, 10,115,101,108,102, 46, 99,103,101,116,110, 97,
+ 109,101, 32, 61, 32,115,101,108,102, 58, 99,102,117,110, 99,
+ 110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 95,103,101,
+ 116, 34, 41, 10,111,117,116,112,117,116, 40, 34,115,116, 97,
+ 116,105, 99, 32,105,110,116, 34, 44,115,101,108,102, 46, 99,
+ 103,101,116,110, 97,109,101, 44, 34, 40,108,117, 97, 95, 83,
+ 116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34,
+ 41, 10,111,117,116,112,117,116, 40, 34,123, 34, 41, 10, 10,
+ 10,111,117,116,112,117,116, 40, 39, 32,105,110,116, 32,116,
+ 111,108,117, 97, 73, 95,105,110,100,101,120, 59, 39, 41, 10,
+ 10, 10,108,111, 99, 97,108, 32, 95, 44, 95, 44,115,116, 97,
+ 116,105, 99, 32, 61, 32,115,116,114,102,105,110,100, 40,115,
+ 101,108,102, 46,109,111,100, 44, 39, 94, 37,115, 42, 40,115,
+ 116, 97,116,105, 99, 41, 39, 41, 10,105,102, 32, 99,108, 97,
+ 115,115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,
+ 110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 39, 32, 39, 44, 99,108, 97,115,115, 44, 39, 42, 39, 44,
+ 39,115,101,108,102, 59, 39, 41, 10,111,117,116,112,117,116,
+ 40, 39, 32,108,117, 97, 95,112,117,115,104,115,116,114,105,
+ 110,103, 40,116,111,108,117, 97, 95, 83, 44, 34, 46,115,101,
+ 108,102, 34, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32,108,117, 97, 95,114, 97,119,103,101,116, 40,116,111,
+ 108,117, 97, 95, 83, 44, 49, 41, 59, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 32,115,101,108,102, 32, 61, 32, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 40, 39, 44, 99,108, 97,
+ 115,115, 44, 39, 42, 41, 32, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39,108,117, 97, 95,116,111,117,115,101,114,100, 97,
+ 116, 97, 40,116,111,108,117, 97, 95, 83, 44, 45, 49, 41, 59,
+ 39, 41, 10,101,108,115,101,105,102, 32,115,116, 97,116,105,
+ 99, 32,116,104,101,110, 10, 95, 44, 95, 44,115,101,108,102,
+ 46,109,111,100, 32, 61, 32,115,116,114,102,105,110,100, 40,
+ 115,101,108,102, 46,109,111,100, 44, 39, 94, 37,115, 42,115,
+ 116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 39,
+ 41, 10,101,110,100, 10, 10, 10,111,117,116,112,117,116, 40,
+ 39, 32,105,102, 32, 40, 33,116,111,108,117, 97, 95,105,115,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 50, 44,
+ 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 44, 48, 41, 41,
+ 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,
+ 117, 97, 95,101,114,114,111,114, 40,116,111,108,117, 97, 95,
+ 83, 44, 34,105,110,118, 97,108,105,100, 32,116,121,112,101,
+ 32,105,110, 32, 97,114,114, 97,121, 32,105,110,100,101,120,
+ 105,110,103, 46, 34, 41, 59, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39, 32,116,111,108,117, 97, 73, 95,105,110,100,101,
+ 120, 32, 61, 32, 40,105,110,116, 41,116,111,108,117, 97, 95,
+ 103,101,116,110,117,109, 98,101,114, 40,116,111,108,117, 97,
+ 95, 83, 44, 50, 44, 48, 41, 45, 49, 59, 39, 41, 10,111,117,
+ 116,112,117,116, 40, 39, 32,105,102, 32, 40,116,111,108,117,
+ 97, 73, 95,105,110,100,101,120, 60, 48, 32,124,124, 32,116,
+ 111,108,117, 97, 73, 95,105,110,100,101,120, 62, 61, 39, 46,
+ 46,115,101,108,102, 46,100,105,109, 46, 46, 39, 41, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97,
+ 95,101,114,114,111,114, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 97,114,114, 97,121, 32,105,110,100,101,120,105,110,103,
+ 32,111,117,116, 32,111,102, 32,114, 97,110,103,101, 46, 34,
+ 41, 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32,116, 44,
+ 99,116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,
+ 108,102, 46,116,121,112,101, 41, 10,105,102, 32,116, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,
+ 108,117, 97, 95,112,117,115,104, 39, 46, 46,116, 46, 46, 39,
+ 40,116,111,108,117, 97, 95, 83, 44, 40, 39, 44, 99,116, 44,
+ 39, 41, 39, 46, 46,115,101,108,102, 58,103,101,116,118, 97,
+ 108,117,101, 40, 99,108, 97,115,115, 44,115,116, 97,116,105,
+ 99, 41, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,
+ 105,102, 32,115,101,108,102, 46,112,116,114, 32, 61, 61, 32,
+ 39, 38, 39, 32,111,114, 32,115,101,108,102, 46,112,116,114,
+ 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,
+ 104,117,115,101,114,116,121,112,101, 40,116,111,108,117, 97,
+ 95, 83, 44, 40,118,111,105,100, 42, 41, 38, 39, 46, 46,115,
+ 101,108,102, 58,103,101,116,118, 97,108,117,101, 40, 99,108,
+ 97,115,115, 44,115,116, 97,116,105, 99, 41, 46, 46, 39, 44,
+ 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39,
+ 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,
+ 111,105,100, 42, 41, 39, 46, 46,115,101,108,102, 58,103,101,
+ 116,118, 97,108,117,101, 40, 99,108, 97,115,115, 44,115,116,
+ 97,116,105, 99, 41, 46, 46, 39, 44, 39, 44,115,101,108,102,
+ 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,110,100, 10,
+ 101,110,100, 10,111,117,116,112,117,116, 40, 39, 32,114,101,
+ 116,117,114,110, 32, 49, 59, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 92,110, 39, 41, 10, 10, 10,105,102, 32,110,111,116, 32,115,
+ 116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,100,
+ 44, 39, 99,111,110,115,116, 39, 41, 32,116,104,101,110, 10,
+ 105,102, 32, 99,108, 97,115,115, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 34, 47, 42, 32,115,101,116, 32,102,
+ 117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46,
+ 110, 97,109,101, 44, 34, 32,111,102, 32, 99,108, 97,115,115,
+ 32, 34, 44, 99,108, 97,115,115, 44, 34, 32, 42, 47, 34, 41,
+ 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 34, 47,
+ 42, 32,115,101,116, 32,102,117,110, 99,116,105,111,110, 58,
+ 34, 44,115,101,108,102, 46,110, 97,109,101, 44, 34, 32, 42,
+ 47, 34, 41, 10,101,110,100, 10,115,101,108,102, 46, 99,115,
+ 101,116,110, 97,109,101, 32, 61, 32,115,101,108,102, 58, 99,
+ 102,117,110, 99,110, 97,109,101, 40, 34,116,111,108,117, 97,
+ 73, 95,115,101,116, 34, 41, 10,111,117,116,112,117,116, 40,
+ 34,115,116, 97,116,105, 99, 32,105,110,116, 34, 44,115,101,
+ 108,102, 46, 99,115,101,116,110, 97,109,101, 44, 34, 40,108,
+ 117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97,
+ 95, 83, 41, 34, 41, 10,111,117,116,112,117,116, 40, 34,123,
+ 34, 41, 10, 10, 10,111,117,116,112,117,116, 40, 39, 32,105,
+ 110,116, 32,116,111,108,117, 97, 73, 95,105,110,100,101,120,
+ 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32, 95, 44, 95,
+ 44,115,116, 97,116,105, 99, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94, 37,
+ 115, 42, 40,115,116, 97,116,105, 99, 41, 39, 41, 10,105,102,
+ 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116,
+ 105, 99, 61, 61,110,105,108, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32, 39, 44, 99,108, 97,115,115, 44,
+ 39, 42, 39, 44, 39,115,101,108,102, 59, 39, 41, 10,111,117,
+ 116,112,117,116, 40, 39, 32,108,117, 97, 95,112,117,115,104,
+ 115,116,114,105,110,103, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 46,115,101,108,102, 34, 41, 59, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 32,108,117, 97, 95,114, 97,119,103,101,
+ 116, 40,116,111,108,117, 97, 95, 83, 44, 49, 41, 59, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 32,115,101,108,102, 32,
+ 61, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39,
+ 44, 99,108, 97,115,115, 44, 39, 42, 41, 32, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39,108,117, 97, 95,116,111,117,115,
+ 101,114,100, 97,116, 97, 40,116,111,108,117, 97, 95, 83, 44,
+ 45, 49, 41, 59, 39, 41, 10,101,108,115,101,105,102, 32,115,
+ 116, 97,116,105, 99, 32,116,104,101,110, 10, 95, 44, 95, 44,
+ 115,101,108,102, 46,109,111,100, 32, 61, 32,115,116,114,102,
+ 105,110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94,
+ 37,115, 42,115,116, 97,116,105, 99, 37,115, 37,115, 42, 40,
+ 46, 42, 41, 39, 41, 10,101,110,100, 10, 10, 10,111,117,116,
+ 112,117,116, 40, 39, 32,105,102, 32, 40, 33,116,111,108,117,
+ 97, 95,105,115,116,121,112,101, 40,116,111,108,117, 97, 95,
+ 83, 44, 50, 44, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82,
+ 44, 48, 41, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95,101,114,114,111,114, 40,116,111,
+ 108,117, 97, 95, 83, 44, 34,105,110,118, 97,108,105,100, 32,
+ 116,121,112,101, 32,105,110, 32, 97,114,114, 97,121, 32,105,
+ 110,100,101,120,105,110,103, 46, 34, 41, 59, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 73, 95,
+ 105,110,100,101,120, 32, 61, 32, 40,105,110,116, 41,116,111,
+ 108,117, 97, 95,103,101,116,110,117,109, 98,101,114, 40,116,
+ 111,108,117, 97, 95, 83, 44, 50, 44, 48, 41, 45, 49, 59, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40,
+ 116,111,108,117, 97, 73, 95,105,110,100,101,120, 60, 48, 32,
+ 124,124, 32,116,111,108,117, 97, 73, 95,105,110,100,101,120,
+ 62, 61, 39, 46, 46,115,101,108,102, 46,100,105,109, 46, 46,
+ 39, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,
+ 111,108,117, 97, 95,101,114,114,111,114, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 97,114,114, 97,121, 32,105,110,100,101,
+ 120,105,110,103, 32,111,117,116, 32,111,102, 32,114, 97,110,
+ 103,101, 46, 34, 41, 59, 39, 41, 10, 10, 10,108,111, 99, 97,
+ 108, 32,112,116,114, 32, 61, 32, 39, 39, 10,105,102, 32,115,
+ 101,108,102, 46,112,116,114,126, 61, 39, 39, 32,116,104,101,
+ 110, 32,112,116,114, 32, 61, 32, 39, 42, 39, 32,101,110,100,
+ 10,111,117,116,112,117,116, 40, 39, 32, 39, 41, 10,105,102,
+ 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116,
+ 105, 99, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 99,108, 97,115,115, 46, 46, 39, 58, 58, 39, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,111,108,117,
+ 97, 73, 95,105,110,100,101,120, 93, 39, 41, 10,101,108,115,
+ 101,105,102, 32, 99,108, 97,115,115, 32,116,104,101,110, 10,
+ 111,117,116,112,117,116, 40, 39,115,101,108,102, 45, 62, 39,
+ 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 91,
+ 116,111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39, 41,
+ 10,101,108,115,101, 10,111,117,116,112,117,116, 40,115,101,
+ 108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,111,108,117,
+ 97, 73, 95,105,110,100,101,120, 93, 39, 41, 10,101,110,100,
+ 10,108,111, 99, 97,108, 32,116, 32, 61, 32,105,115, 98, 97,
+ 115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41, 10,
+ 111,117,116,112,117,116, 40, 39, 32, 61, 32, 39, 41, 10,105,
+ 102, 32,110,111,116, 32,116, 32, 97,110,100, 32,112,116,114,
+ 61, 61, 39, 39, 32,116,104,101,110, 32,111,117,116,112,117,
+ 116, 40, 39, 42, 39, 41, 32,101,110,100, 10,111,117,116,112,
+ 117,116, 40, 39, 40, 40, 39, 44,115,101,108,102, 46,109,111,
+ 100, 44,115,101,108,102, 46,116,121,112,101, 41, 10,105,102,
+ 32,110,111,116, 32,116, 32,116,104,101,110, 10,111,117,116,
+ 112,117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117,
+ 116,112,117,116, 40, 39, 41, 32, 39, 41, 10,108,111, 99, 97,
+ 108, 32,100,101,102, 32, 61, 32, 48, 10,105,102, 32,115,101,
+ 108,102, 46,100,101,102, 32,126, 61, 32, 39, 39, 32,116,104,
+ 101,110, 32,100,101,102, 32, 61, 32,115,101,108,102, 46,100,
+ 101,102, 32,101,110,100, 10,105,102, 32,116, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39,116,111,108,117, 97,
+ 95,103,101,116, 39, 46, 46,116, 44, 39, 40,116,111,108,117,
+ 97, 95, 83, 44, 51, 44, 39, 44,100,101,102, 44, 39, 41, 41,
+ 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116,
+ 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 51, 44,
+ 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,110,
+ 100, 10,111,117,116,112,117,116, 40, 39, 32,114,101,116,117,
+ 114,110, 32, 48, 59, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,110,
+ 39, 41, 10,101,110,100, 10, 10,101,110,100, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 65,114,114,
+ 97,121, 58,114,101,103,105,115,116,101,114, 32, 40, 41, 10,
+ 108,111, 99, 97,108, 32,112, 97,114,101,110,116, 32, 61, 32,
+ 115,101,108,102, 58,105,110, 99,108, 97,115,115, 40, 41, 32,
+ 111,114, 32,115,101,108,102, 58,105,110,109,111,100,117,108,
+ 101, 40, 41, 10,105,102, 32,112, 97,114,101,110,116, 32,116,
+ 104,101,110, 10,105,102, 32,115,101,108,102, 46, 99,115,101,
+ 116,110, 97,109,101, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39, 32,116,111,108,117, 97, 95,116, 97, 98,108,
+ 101, 97,114,114, 97,121, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 39, 46, 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44,
+ 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46,
+ 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,
+ 116,110, 97,109,101, 46, 46, 39, 44, 39, 46, 46,115,101,108,
+ 102, 46, 99,115,101,116,110, 97,109,101, 46, 46, 39, 41, 59,
+ 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40,
+ 39, 32,116,111,108,117, 97, 95,116, 97, 98,108,101, 97,114,
+ 114, 97,121, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46,
+ 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46,
+ 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34,
+ 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,116,110, 97,
+ 109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59, 39, 41, 10,
+ 101,110,100, 10,101,108,115,101, 10,105,102, 32,115,101,108,
+ 102, 46, 99,115,101,116,110, 97,109,101, 32,116,104,101,110,
+ 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97,
+ 95,103,108,111, 98, 97,108, 97,114,114, 97,121, 40,116,111,
+ 108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,
+ 108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,
+ 108,102, 46, 99,103,101,116,110, 97,109,101, 46, 46, 39, 44,
+ 39, 46, 46,115,101,108,102, 46, 99,115,101,116,110, 97,109,
+ 101, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,
+ 117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,103,
+ 108,111, 98, 97,108, 97,114,114, 97,121, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110,
+ 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102,
+ 46, 99,103,101,116,110, 97,109,101, 46, 46, 39, 44, 78, 85,
+ 76, 76, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,
+ 101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 65,114,114, 97,121, 58,117,110,114,101,103,
+ 105,115,116,101,114, 32, 40, 41, 10,105,102, 32,115,101,108,
+ 102, 58,105,110, 99,108, 97,115,115, 40, 41, 61, 61,110,105,
+ 108, 32, 97,110,100, 32,115,101,108,102, 58,105,110,109,111,
+ 100,117,108,101, 40, 41, 61, 61,110,105,108, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,
+ 112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95, 83,
+ 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98, 97,
+ 108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,
+ 101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 41, 59,
+ 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 95, 65,114,114, 97,121, 32,
+ 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,
+ 108, 97,115,115, 65,114,114, 97,121, 10,115,101,116,116, 97,
+ 103, 40,116, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10,
+ 97,112,112,101,110,100, 40,116, 41, 10,114,101,116,117,114,
+ 110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 65,114,114, 97,121, 32, 40,115, 41, 10,
+ 114,101,116,117,114,110, 32, 95, 65,114,114, 97,121, 32, 40,
+ 68,101, 99,108, 97,114, 97,116,105,111,110, 40,115, 44, 39,
+ 118, 97,114, 39, 41, 41, 10,101,110,100, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 99,108, 97,115,115, 70,117,110, 99,116,105,111,
+ 110, 32, 61, 32,123, 10,109,111,100, 32, 61, 32, 39, 39, 44,
+ 10,116,121,112,101, 32, 61, 32, 39, 39, 44, 10,112,116,114,
+ 32, 61, 32, 39, 39, 44, 10,110, 97,109,101, 32, 61, 32, 39,
+ 39, 44, 10, 97,114,103,115, 32, 61, 32,123,110, 61, 48,125,
+ 44, 10, 99,111,110,115,116, 32, 61, 32, 39, 39, 44, 10, 95,
+ 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,
+ 116,117,114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40,
+ 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 70,117,110, 99,
+ 116,105,111,110, 58,100,101, 99,108,116, 97,103, 32, 40, 41,
+ 10,115,101,108,102, 46,105,116,121,112,101, 44,115,101,108,
+ 102, 46,116, 97,103, 32, 61, 32,116, 97,103,118, 97,114, 40,
+ 115,101,108,102, 46,116,121,112,101, 44,115,116,114,102,105,
+ 110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111,
+ 110,115,116, 39, 41, 41, 10,108,111, 99, 97,108, 32,105, 61,
+ 49, 10,119,104,105,108,101, 32,115,101,108,102, 46, 97,114,
+ 103,115, 91,105, 93, 32,100,111, 10,115,101,108,102, 46, 97,
+ 114,103,115, 91,105, 93, 58,100,101, 99,108,116, 97,103, 40,
+ 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 58,
+ 115,117,112, 99,111,100,101, 32, 40, 41, 10,108,111, 99, 97,
+ 108, 32,110,114,101,116, 32, 61, 32, 48, 10,108,111, 99, 97,
+ 108, 32, 99,108, 97,115,115, 32, 61, 32,115,101,108,102, 58,
+ 105,110, 99,108, 97,115,115, 40, 41, 10,108,111, 99, 97,108,
+ 32, 95, 44, 95, 44,115,116, 97,116,105, 99, 32, 61, 32,115,
+ 116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,100,
+ 44, 39, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41, 39,
+ 41, 10, 10,105,102, 32, 99,108, 97,115,115, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,109,101,
+ 116,104,111,100, 58, 34, 44,115,101,108,102, 46,110, 97,109,
+ 101, 44, 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44,
+ 99,108, 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,
+ 115,101, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,102,
+ 117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46,
+ 110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10,101,110,100,
+ 10,111,117,116,112,117,116, 40, 34,115,116, 97,116,105, 99,
+ 32,105,110,116, 34, 44,115,101,108,102, 46, 99,110, 97,109,
+ 101, 44, 34, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,
+ 116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116,112,
+ 117,116, 40, 34,123, 34, 41, 10, 10, 10,111,117,116,112,117,
+ 116, 40, 39, 32,105,102, 32, 40, 92,110, 39, 41, 10, 10,108,
+ 111, 99, 97,108, 32,110, 97,114,103, 10,105,102, 32, 99,108,
+ 97,115,115, 32,116,104,101,110, 32,110, 97,114,103, 61, 50,
+ 32,101,108,115,101, 32,110, 97,114,103, 61, 49, 32,101,110,
+ 100, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,
+ 115,101,108,102, 46,110, 97,109,101,126, 61, 39,110,101,119,
+ 39, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110,
+ 105,108, 32,116,104,101,110, 10,105,102, 32,115,101,108,102,
+ 46, 99,111,110,115,116, 32, 61, 61, 32, 39, 99,111,110,115,
+ 116, 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39, 32, 33,116,111,108,117, 97, 95,105,115,116,121,112,101,
+ 40,116,111,108,117, 97, 95, 83, 44, 49, 44, 39, 44,115,101,
+ 108,102, 46,112, 97,114,101,110,116, 46, 99,116, 97,103, 44,
+ 39, 44, 48, 41, 32,124,124, 92,110, 39, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 39, 32, 33,116,111,108,
+ 117, 97, 95,105,115,116,121,112,101, 40,116,111,108,117, 97,
+ 95, 83, 44, 49, 44, 39, 44,115,101,108,102, 46,112, 97,114,
+ 101,110,116, 46,116, 97,103, 44, 39, 44, 48, 41, 32,124,124,
+ 92,110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10,105,
+ 102, 32,115,101,108,102, 46, 97,114,103,115, 91, 49, 93, 46,
+ 116,121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,
+ 116,104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,
+ 119,104,105,108,101, 32,115,101,108,102, 46, 97,114,103,115,
+ 91,105, 93, 32,100,111, 10,105,102, 32,105,115, 98, 97,115,
+ 105, 99, 40,115,101,108,102, 46, 97,114,103,115, 91,105, 93,
+ 46,116,121,112,101, 41, 32,126, 61, 32, 39,118, 97,108,117,
+ 101, 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39, 32, 33, 39, 46, 46,115,101,108,102, 46, 97,114,103,115,
+ 91,105, 93, 58,111,117,116, 99,104,101, 99,107,116,121,112,
+ 101, 40,110, 97,114,103, 41, 46, 46, 39, 32,124,124, 92,110,
+ 39, 41, 10,101,110,100, 10,110, 97,114,103, 32, 61, 32,110,
+ 97,114,103, 43, 49, 10,105, 32, 61, 32,105, 43, 49, 10,101,
+ 110,100, 10,101,110,100, 10, 10,111,117,116,112,117,116, 40,
+ 39, 32, 33,116,111,108,117, 97, 95,105,115,110,111,111, 98,
+ 106, 40,116,111,108,117, 97, 95, 83, 44, 39, 46, 46,110, 97,
+ 114,103, 46, 46, 39, 41, 92,110, 32, 41, 92,110, 32,103,111,
+ 116,111, 32,116,111,108,117, 97, 95,108,101,114,114,111,114,
+ 59, 39, 41, 10, 10,111,117,116,112,117,116, 40, 39, 32,101,
+ 108,115,101, 92,110, 32,123, 39, 41, 10, 10, 10,108,111, 99,
+ 97,108, 32,110, 97,114,103, 10,105,102, 32, 99,108, 97,115,
+ 115, 32,116,104,101,110, 32,110, 97,114,103, 61, 50, 32,101,
+ 108,115,101, 32,110, 97,114,103, 61, 49, 32,101,110,100, 10,
+ 105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,101,
+ 108,102, 46,110, 97,109,101,126, 61, 39,110,101,119, 39, 32,
+ 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110,105,108,
+ 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,
+ 39, 44,115,101,108,102, 46, 99,111,110,115,116, 44, 99,108,
+ 97,115,115, 44, 39, 42, 39, 44, 39,115,101,108,102, 32, 61,
+ 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39, 44,
+ 115,101,108,102, 46, 99,111,110,115,116, 44, 99,108, 97,115,
+ 115, 44, 39, 42, 41, 32, 39, 41, 10,111,117,116,112,117,116,
+ 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 49, 44,
+ 48, 41, 59, 39, 41, 10,101,108,115,101,105,102, 32,115,116,
+ 97,116,105, 99, 32,116,104,101,110, 10, 95, 44, 95, 44,115,
+ 101,108,102, 46,109,111,100, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94, 37,
+ 115, 42,115,116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46,
+ 42, 41, 39, 41, 10,101,110,100, 10, 10,105,102, 32,115,101,
+ 108,102, 46, 97,114,103,115, 91, 49, 93, 46,116,121,112,101,
+ 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104,101,110,
+ 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,
+ 101, 32,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,
+ 100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93,
+ 58,100,101, 99,108, 97,114,101, 40,110, 97,114,103, 41, 10,
+ 110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,105,
+ 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10,
+ 10, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,
+ 115,101,108,102, 46,110, 97,109,101,126, 61, 39,110,101,119,
+ 39, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110,
+ 105,108, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39, 32,105,102, 32, 40, 33,115,101,108,102, 41, 32,116,111,
+ 108,117, 97, 95,101,114,114,111,114, 40,116,111,108,117, 97,
+ 95, 83, 44, 34,105,110,118, 97,108,105,100, 32, 92, 39,115,
+ 101,108,102, 92, 39, 32,105,110, 32,102,117,110, 99,116,105,
+ 111,110, 32, 92, 39, 39, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 46, 46, 39, 92, 39, 34, 41, 59, 39, 41, 59, 10,101,
+ 110,100, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32,116,
+ 104,101,110, 32,110, 97,114,103, 61, 50, 32,101,108,115,101,
+ 32,110, 97,114,103, 61, 49, 32,101,110,100, 10,105,102, 32,
+ 115,101,108,102, 46, 97,114,103,115, 91, 49, 93, 46,116,121,
+ 112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104,
+ 101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,
+ 105,108,101, 32,115,101,108,102, 46, 97,114,103,115, 91,105,
+ 93, 32,100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,
+ 105, 93, 58,103,101,116, 97,114,114, 97,121, 40,110, 97,114,
+ 103, 41, 10,110, 97,114,103, 32, 61, 32,110, 97,114,103, 43,
+ 49, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32, 97,
+ 110,100, 32,115,101,108,102, 46,110, 97,109,101, 61, 61, 39,
+ 100,101,108,101,116,101, 39, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32,100,101,108,101,116,101, 32,115,
+ 101,108,102, 59, 39, 41, 10,101,108,115,101,105,102, 32, 99,
+ 108, 97,115,115, 32, 97,110,100, 32,115,101,108,102, 46,110,
+ 97,109,101, 32, 61, 61, 32, 39,111,112,101,114, 97,116,111,
+ 114, 38, 91, 93, 39, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39, 32,115,101,108,102, 45, 62,111,112,101,114,
+ 97,116,111,114, 91, 93, 40, 39, 44,115,101,108,102, 46, 97,
+ 114,103,115, 91, 49, 93, 46,110, 97,109,101, 44, 39, 41, 32,
+ 61, 32, 39, 44,115,101,108,102, 46, 97,114,103,115, 91, 50,
+ 93, 46,110, 97,109,101, 44, 39, 59, 39, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 39, 32,123, 39, 41, 10,
+ 105,102, 32,115,101,108,102, 46,116,121,112,101, 32,126, 61,
+ 32, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,116,121,
+ 112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 39, 32, 39, 44,115,
+ 101,108,102, 46,109,111,100, 44,115,101,108,102, 46,116,121,
+ 112,101, 44,115,101,108,102, 46,112,116,114, 44, 39,116,111,
+ 108,117, 97, 73, 95,114,101,116, 32, 61, 32, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 40, 39, 44,115,101,108,102, 46,
+ 109,111,100, 44,115,101,108,102, 46,116,121,112,101, 44,115,
+ 101,108,102, 46,112,116,114, 44, 39, 41, 32, 39, 41, 10,101,
+ 108,115,101, 10,111,117,116,112,117,116, 40, 39, 32, 39, 41,
+ 10,101,110,100, 10,105,102, 32, 99,108, 97,115,115, 32, 97,
+ 110,100, 32,115,101,108,102, 46,110, 97,109,101, 61, 61, 39,
+ 110,101,119, 39, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39,110,101,119, 39, 44, 99,108, 97,115,115, 44, 39,
+ 40, 39, 41, 10,101,108,115,101,105,102, 32, 99,108, 97,115,
+ 115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 99,108, 97,115,115,
+ 46, 46, 39, 58, 58, 39, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 44, 39, 40, 39, 41, 10,101,108,115,101,105,102, 32,
+ 99,108, 97,115,115, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39,115,101,108,102, 45, 62, 39, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 44, 39, 40, 39, 41, 10,101,108,
+ 115,101, 10,111,117,116,112,117,116, 40,115,101,108,102, 46,
+ 110, 97,109,101, 44, 39, 40, 39, 41, 10,101,110,100, 10, 10,
+ 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,
+ 101, 32,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,
+ 100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93,
+ 58,112, 97,115,115,112, 97,114, 40, 41, 10,105, 32, 61, 32,
+ 105, 43, 49, 10,105,102, 32,115,101,108,102, 46, 97,114,103,
+ 115, 91,105, 93, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39, 44, 39, 41, 10,101,110,100, 10,101,110,100, 10,
+ 10,111,117,116,112,117,116, 40, 39, 41, 59, 39, 41, 10, 10,
+ 10,105,102, 32,115,101,108,102, 46,116,121,112,101, 32,126,
+ 61, 32, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,116,
+ 121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,
+ 104,101,110, 10,110,114,101,116, 32, 61, 32,110,114,101,116,
+ 32, 43, 32, 49, 10,108,111, 99, 97,108, 32,116, 44, 99,116,
+ 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,108,102,
+ 46,116,121,112,101, 41, 10,105,102, 32,116, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95,112,117,115,104, 39, 46, 46,116, 46, 46, 39, 40,116,
+ 111,108,117, 97, 95, 83, 44, 40, 39, 44, 99,116, 44, 39, 41,
+ 116,111,108,117, 97, 73, 95,114,101,116, 41, 59, 39, 41, 10,
+ 101,108,115,101, 10,105,102, 32,115,101,108,102, 46,112,116,
+ 114, 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112,
+ 117,116, 40, 39, 35,105,102,100,101,102, 32, 95, 95, 99,112,
+ 108,117,115,112,108,117,115, 92,110, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 32,118,111,105,100, 42, 32,116,111,108,
+ 117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,110,101,119,
+ 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39, 40,116,
+ 111,108,117, 97, 73, 95,114,101,116, 41, 59, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 35,101,108,115,101, 92,110, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 32,118,111,105,100,
+ 42, 32,116,111,108,117, 97, 73, 95, 99,108,111,110,101, 32,
+ 61, 32,116,111,108,117, 97, 95, 99,111,112,121, 40,116,111,
+ 108,117, 97, 95, 83, 44, 40,118,111,105,100, 42, 41, 38,116,
+ 111,108,117, 97, 73, 95,114,101,116, 44,115,105,122,101,111,
+ 102, 40, 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39,
+ 41, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 35,
+ 101,110,100,105,102, 92,110, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,104,117,
+ 115,101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83,
+ 44,116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40,
+ 116,111,108,117, 97, 95, 83, 44,116,111,108,117, 97, 73, 95,
+ 99,108,111,110,101, 44, 39, 44,115,101,108,102, 46,116, 97,
+ 103, 44, 39, 41, 44, 39, 44,115,101,108,102, 46,116, 97,103,
+ 44, 39, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,125, 39, 41, 10, 10,101,108,115,101,105,102, 32,115,101,
+ 108,102, 46,112,116,114, 32, 61, 61, 32, 39, 38, 39, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,
+ 108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,
+ 101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,105,100,
+ 42, 41, 38,116,111,108,117, 97, 73, 95,114,101,116, 44, 39,
+ 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41,
+ 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32,
+ 116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,
+ 121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,
+ 105,100, 42, 41,116,111,108,117, 97, 73, 95,114,101,116, 44,
+ 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39,
+ 41, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10,108,
+ 111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,
+ 115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111,
+ 10,110,114,101,116, 32, 61, 32,110,114,101,116, 32, 43, 32,
+ 115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,114,101,
+ 116,118, 97,108,117,101, 40, 41, 10,105, 32, 61, 32,105, 43,
+ 49, 10,101,110,100, 10,111,117,116,112,117,116, 40, 39, 32,
+ 125, 39, 41, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32,
+ 116,104,101,110, 32,110, 97,114,103, 61, 50, 32,101,108,115,
+ 101, 32,110, 97,114,103, 61, 49, 32,101,110,100, 10,105,102,
+ 32,115,101,108,102, 46, 97,114,103,115, 91, 49, 93, 46,116,
+ 121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,
+ 104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,
+ 104,105,108,101, 32,115,101,108,102, 46, 97,114,103,115, 91,
+ 105, 93, 32,100,111, 10,115,101,108,102, 46, 97,114,103,115,
+ 91,105, 93, 58,115,101,116, 97,114,114, 97,121, 40,110, 97,
+ 114,103, 41, 10,110, 97,114,103, 32, 61, 32,110, 97,114,103,
+ 43, 49, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,105,102, 32,115,101,108,102, 46, 97,
+ 114,103,115, 91, 49, 93, 46,116,121,112,101, 32,126, 61, 32,
+ 39,118,111,105,100, 39, 32,116,104,101,110, 10,108,111, 99,
+ 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,
+ 108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,115,
+ 101,108,102, 46, 97,114,103,115, 91,105, 93, 58,102,114,101,
+ 101, 97,114,114, 97,121, 40, 41, 10,105, 32, 61, 32,105, 43,
+ 49, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10, 10,
+ 111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10,111,117,
+ 116,112,117,116, 40, 39, 32,114,101,116,117,114,110, 32, 39,
+ 46, 46,110,114,101,116, 46, 46, 39, 59, 39, 41, 10, 10, 10,
+ 111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,108,
+ 101,114,114,111,114, 58, 92,110, 39, 41, 10,108,111, 99, 97,
+ 108, 32,111,118,101,114,108,111, 97,100, 32, 61, 32,115,116,
+ 114,115,117, 98, 40,115,101,108,102, 46, 99,110, 97,109,101,
+ 44, 45, 50, 44, 45, 49, 41, 32, 45, 32, 49, 10,105,102, 32,
+ 111,118,101,114,108,111, 97,100, 32, 62, 61, 32, 48, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,114,101,
+ 116,117,114,110, 32, 39, 46, 46,115,116,114,115,117, 98, 40,
+ 115,101,108,102, 46, 99,110, 97,109,101, 44, 49, 44, 45, 51,
+ 41, 46, 46,102,111,114,109, 97,116, 40, 34, 37, 48, 50,100,
+ 34, 44,111,118,101,114,108,111, 97,100, 41, 46, 46, 39, 40,
+ 116,111,108,117, 97, 95, 83, 41, 59, 39, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95,101,114,114,111,114, 40,116,111,108,117, 97, 95, 83,
+ 44, 34, 35,102,101,114,114,111,114, 32,105,110, 32,102,117,
+ 110, 99,116,105,111,110, 32, 92, 39, 39, 46, 46,115,101,108,
+ 102, 46,108,110, 97,109,101, 46, 46, 39, 92, 39, 46, 34, 41,
+ 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,114,101,
+ 116,117,114,110, 32, 48, 59, 39, 41, 10,101,110,100, 10, 10,
+ 111,117,116,112,117,116, 40, 39,125, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 92,110, 39, 41, 10,101,110,100, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115,
+ 70,117,110, 99,116,105,111,110, 58,114,101,103,105,115,116,
+ 101,114, 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 97,114,
+ 101,110,116, 32, 61, 32,115,101,108,102, 58,105,110, 99,108,
+ 97,115,115, 40, 41, 32,111,114, 32,115,101,108,102, 58,105,
+ 110,109,111,100,117,108,101, 40, 41, 10,105,102, 32,112, 97,
+ 114,101,110,116, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39, 32,116,111,108,117, 97, 95,102,117,110, 99,116,
+ 105,111,110, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46,
+ 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46,
+ 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34,
+ 44, 39, 46, 46,115,101,108,102, 46, 99,110, 97,109,101, 46,
+ 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,102,117,110,
+ 99,116,105,111,110, 40,116,111,108,117, 97, 95, 83, 44, 78,
+ 85, 76, 76, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110,
+ 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102,
+ 46, 99,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,101,
+ 110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,
+ 110, 58,117,110,114,101,103,105,115,116,101,114, 32, 40, 41,
+ 10,105,102, 32,115,101,108,102, 58,105,110, 99,108, 97,115,
+ 115, 40, 41, 61, 61,110,105,108, 32, 97,110,100, 32,115,101,
+ 108,102, 58,105,110,109,111,100,117,108,101, 40, 41, 61, 61,
+ 110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 39, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40,
+ 116,111,108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,
+ 101,116,103,108,111, 98, 97,108, 40,116,111,108,117, 97, 95,
+ 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,
+ 101, 46, 46, 39, 34, 41, 59, 39, 41, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 58,112,
+ 114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,
+ 115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34, 70,117,110, 99,116,105,111,110,123, 34, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,
+ 109,111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,
+ 109,111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,
+ 116, 40,105,100,101,110,116, 46, 46, 34, 32,116,121,112,101,
+ 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,116,121,112,
+ 101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,
+ 105,100,101,110,116, 46, 46, 34, 32,112,116,114, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,112,116,114, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46,
+ 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32, 99,111,110,115,116, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46, 99,111,110,115,116, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32, 99,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46, 99,110, 97,109,101, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32,108,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46,108,110, 97,109,101, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32, 97,114,103,115, 32, 61, 32,123, 34, 41, 10,108,
+ 111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,
+ 115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111,
+ 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 34,
+ 44, 34, 44, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,
+ 110,100, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32,125, 34, 41, 10,112,114,105,110,116, 40,105,100,
+ 101,110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101,
+ 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110,
+ 58,111,118,101,114,108,111, 97,100, 32, 40, 41, 10,114,101,
+ 116,117,114,110, 32,115,101,108,102, 46,112, 97,114,101,110,
+ 116, 58,111,118,101,114,108,111, 97,100, 40,115,101,108,102,
+ 46,108,110, 97,109,101, 41, 10,101,110,100, 10, 10, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 95, 70,117,110, 99,
+ 116,105,111,110, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,
+ 101, 32, 61, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,
+ 111,110, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108,
+ 117, 97, 95,116, 97,103, 41, 10, 10,105,102, 32,116, 46, 99,
+ 111,110,115,116, 32,126, 61, 32, 39, 99,111,110,115,116, 39,
+ 32, 97,110,100, 32,116, 46, 99,111,110,115,116, 32,126, 61,
+ 32, 39, 39, 32,116,104,101,110, 10,101,114,114,111,114, 40,
+ 34, 35,105,110,118, 97,108,105,100, 32, 39, 99,111,110,115,
+ 116, 39, 32,115,112,101, 99,105,102,105, 99, 97,116,105,111,
+ 110, 34, 41, 10,101,110,100, 10, 10, 97,112,112,101,110,100,
+ 40,116, 41, 10,105,102, 32,116, 58,105,110, 99,108, 97,115,
+ 115, 40, 41, 32,116,104,101,110, 10,105,102, 32,116, 46,110,
+ 97,109,101, 32, 61, 61, 32,116, 46,112, 97,114,101,110,116,
+ 46,110, 97,109,101, 32,116,104,101,110, 10,116, 46,110, 97,
+ 109,101, 32, 61, 32, 39,110,101,119, 39, 10,116, 46,108,110,
+ 97,109,101, 32, 61, 32, 39,110,101,119, 39, 10,116, 46,116,
+ 121,112,101, 32, 61, 32,116, 46,112, 97,114,101,110,116, 46,
+ 110, 97,109,101, 10,116, 46,112,116,114, 32, 61, 32, 39, 42,
+ 39, 10,101,108,115,101,105,102, 32,116, 46,110, 97,109,101,
+ 32, 61, 61, 32, 39,126, 39, 46, 46,116, 46,112, 97,114,101,
+ 110,116, 46,110, 97,109,101, 32,116,104,101,110, 10,116, 46,
+ 110, 97,109,101, 32, 61, 32, 39,100,101,108,101,116,101, 39,
+ 10,116, 46,108,110, 97,109,101, 32, 61, 32, 39,100,101,108,
+ 101,116,101, 39, 10,101,110,100, 10,101,110,100, 10,116, 46,
+ 99,110, 97,109,101, 32, 61, 32,116, 58, 99,102,117,110, 99,
+ 110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 34, 41, 46,
+ 46,116, 58,111,118,101,114,108,111, 97,100, 40,116, 41, 10,
+ 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 70,117,110,
+ 99,116,105,111,110, 32, 40,100, 44, 97, 44, 99, 41, 10,108,
+ 111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116, 40,
+ 115,116,114,115,117, 98, 40, 97, 44, 50, 44, 45, 50, 41, 44,
+ 39, 44, 39, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,
+ 108,111, 99, 97,108, 32,108, 32, 61, 32,123,110, 61, 48,125,
+ 10,119,104,105,108,101, 32,116, 91,105, 93, 32,100,111, 10,
+ 108, 46,110, 32, 61, 32,108, 46,110, 43, 49, 10,108, 91,108,
+ 46,110, 93, 32, 61, 32, 68,101, 99,108, 97,114, 97,116,105,
+ 111,110, 40,116, 91,105, 93, 44, 39,118, 97,114, 39, 41, 10,
+ 105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,108,111, 99,
+ 97,108, 32,102, 32, 61, 32, 68,101, 99,108, 97,114, 97,116,
+ 105,111,110, 40,100, 44, 39,102,117,110, 99, 39, 41, 10,102,
+ 46, 97,114,103,115, 32, 61, 32,108, 10,102, 46, 99,111,110,
+ 115,116, 32, 61, 32, 99, 10,114,101,116,117,114,110, 32, 95,
+ 70,117,110, 99,116,105,111,110, 40,102, 41, 10,101,110,100,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 99,108, 97,115,115, 79,112,101,114, 97,116,
+ 111,114, 32, 61, 32,123, 10,107,105,110,100, 32, 61, 32, 39,
+ 39, 44, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,
+ 115, 70,117,110, 99,116,105,111,110, 44, 10,125, 10,115,101,
+ 116,116, 97,103, 40, 99,108, 97,115,115, 79,112,101,114, 97,
+ 116,111,114, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10,
+ 10, 10, 95, 84, 77, 32, 61, 32,123, 91, 39, 43, 39, 93, 32,
+ 61, 32, 39,111,112,101,114, 97,116,111,114, 95, 97,100,100,
+ 39, 44, 10, 91, 39, 45, 39, 93, 32, 61, 32, 39,111,112,101,
+ 114, 97,116,111,114, 95,115,117, 98, 39, 44, 10, 91, 39, 42,
+ 39, 93, 32, 61, 32, 39,111,112,101,114, 97,116,111,114, 95,
+ 109,117,108, 39, 44, 10, 91, 39, 47, 39, 93, 32, 61, 32, 39,
+ 111,112,101,114, 97,116,111,114, 95,100,105,118, 39, 44, 10,
+ 91, 39, 60, 39, 93, 32, 61, 32, 39,111,112,101,114, 97,116,
+ 111,114, 95,108,116, 39, 44, 10, 91, 39, 91, 93, 39, 93, 32,
+ 61, 32, 39,111,112,101,114, 97,116,111,114, 95,103,101,116,
+ 39, 44, 10, 91, 39, 38, 91, 93, 39, 93, 32, 61, 32, 39,111,
+ 112,101,114, 97,116,111,114, 95,115,101,116, 39, 44, 10,125,
+ 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 79,112,101,114, 97,116,111,114, 58,112,114,105,
+ 110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,115,101,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 79,112,101,114, 97,116,111,114,123, 34, 41, 10,112,114,
+ 105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,107,105,
+ 110,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,107,
+ 105,110,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,
+ 116, 40,105,100,101,110,116, 46, 46, 34, 32,109,111,100, 32,
+ 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100, 46,
+ 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,
+ 101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32, 39,
+ 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46,112,116,114, 46, 46, 34, 39, 44, 34, 41,
+ 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,
+ 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,
+ 102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 99,
+ 111,110,115,116, 32, 61, 32, 39, 34, 46, 46,115,101,108,102,
+ 46, 99,111,110,115,116, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 99,
+ 110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102,
+ 46, 99,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,108,
+ 110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102,
+ 46,108,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 97,
+ 114,103,115, 32, 61, 32,123, 34, 41, 10,108,111, 99, 97,108,
+ 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102,
+ 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,115,101,108,
+ 102, 46, 97,114,103,115, 91,105, 93, 58,112,114,105,110,116,
+ 40,105,100,101,110,116, 46, 46, 34, 32, 34, 44, 34, 44, 34,
+ 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,125,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,
+ 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 79,
+ 112,101,114, 97,116,111,114, 32, 40,116, 41, 10,116, 46, 95,
+ 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 79,112,101,
+ 114, 97,116,111,114, 10,115,101,116,116, 97,103, 40,116, 44,
+ 116,111,108,117, 97, 95,116, 97,103, 41, 10, 10,105,102, 32,
+ 116, 46, 99,111,110,115,116, 32,126, 61, 32, 39, 99,111,110,
+ 115,116, 39, 32, 97,110,100, 32,116, 46, 99,111,110,115,116,
+ 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,101,114,114,
+ 111,114, 40, 34, 35,105,110,118, 97,108,105,100, 32, 39, 99,
+ 111,110,115,116, 39, 32,115,112,101, 99,105,102,105, 99, 97,
+ 116,105,111,110, 34, 41, 10,101,110,100, 10, 10, 97,112,112,
+ 101,110,100, 40,116, 41, 10,105,102, 32,110,111,116, 32,116,
+ 58,105,110, 99,108, 97,115,115, 40, 41, 32,116,104,101,110,
+ 10,101,114,114,111,114, 40, 34, 35,111,112,101,114, 97,116,
+ 111,114, 32, 99, 97,110, 32,111,110,108,121, 32, 98,101, 32,
+ 100,101,102,105,110,101,100, 32, 97,115, 32, 99,108, 97,115,
+ 115, 32,109,101,109, 98,101,114, 34, 41, 10,101,110,100, 10,
+ 10,116, 46, 99,110, 97,109,101, 32, 61, 32,116, 58, 99,102,
+ 117,110, 99,110, 97,109,101, 40, 34,116,111,108,117, 97, 73,
+ 34, 41, 46, 46,116, 58,111,118,101,114,108,111, 97,100, 40,
+ 116, 41, 10,116, 46,110, 97,109,101, 32, 61, 32,116, 46,110,
+ 97,109,101, 46, 46,116, 46,107,105,110,100, 10,114,101,116,
+ 117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 79,112,101,114, 97,116,
+ 111,114, 32, 40,100, 44,107, 44, 97, 44, 99, 41, 10,108,111,
+ 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116, 40,115,
+ 116,114,115,117, 98, 40, 97, 44, 50, 44,115,116,114,108,101,
+ 110, 40, 97, 41, 45, 49, 41, 44, 39, 44, 39, 41, 10,108,111,
+ 99, 97,108, 32,105, 61, 49, 10,108,111, 99, 97,108, 32,108,
+ 32, 61, 32,123,110, 61, 48,125, 10,119,104,105,108,101, 32,
+ 116, 91,105, 93, 32,100,111, 10,108, 46,110, 32, 61, 32,108,
+ 46,110, 43, 49, 10,108, 91,108, 46,110, 93, 32, 61, 32, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110, 40,116, 91,105, 93,
+ 44, 39,118, 97,114, 39, 41, 10,105, 32, 61, 32,105, 43, 49,
+ 10,101,110,100, 10,105,102, 32,107, 32, 61, 61, 32, 39, 91,
+ 93, 39, 32,116,104,101,110, 10,100, 32, 61, 32,103,115,117,
+ 98, 40,100, 44, 39, 38, 39, 44, 39, 39, 41, 10,101,108,115,
+ 101,105,102, 32,107, 61, 61, 39, 38, 91, 93, 39, 32,116,104,
+ 101,110, 10,108, 46,110, 32, 61, 32,108, 46,110, 43, 49, 10,
+ 108, 91,108, 46,110, 93, 32, 61, 32, 68,101, 99,108, 97,114,
+ 97,116,105,111,110, 40,100, 44, 39,118, 97,114, 39, 41, 10,
+ 108, 91,108, 46,110, 93, 46,110, 97,109,101, 32, 61, 32, 39,
+ 116,111,108,117, 97, 73, 95,118, 97,108,117,101, 39, 10,101,
+ 110,100, 10,108,111, 99, 97,108, 32,102, 32, 61, 32, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 40,100, 44, 39,102,117,
+ 110, 99, 39, 41, 10,105,102, 32,107, 32, 61, 61, 32, 39, 91,
+ 93, 39, 32, 97,110,100, 32, 40,108, 91, 49, 93, 61, 61,110,
+ 105,108, 32,111,114, 32,105,115, 98, 97,115,105, 99, 40,108,
+ 91, 49, 93, 46,116,121,112,101, 41,126, 61, 39,110,117,109,
+ 98,101,114, 39, 41, 32,116,104,101,110, 10,101,114,114,111,
+ 114, 40, 39,111,112,101,114, 97,116,111,114, 91, 93, 32, 99,
+ 97,110, 32,111,110,108,121, 32, 98,101, 32,100,101,102,105,
+ 110,101,100, 32,102,111,114, 32,110,117,109,101,114,105, 99,
+ 32,105,110,100,101,120, 46, 39, 41, 10,101,110,100, 10,102,
+ 46, 97,114,103,115, 32, 61, 32,108, 10,102, 46, 99,111,110,
+ 115,116, 32, 61, 32, 99, 10,102, 46,107,105,110,100, 32, 61,
+ 32,103,115,117, 98, 40,107, 44, 34, 37,115, 34, 44, 34, 34,
+ 41, 10,102, 46,108,110, 97,109,101, 32, 61, 32, 95, 84, 77,
+ 91,102, 46,107,105,110,100, 93, 10,105,102, 32,110,111,116,
+ 32,102, 46,108,110, 97,109,101, 32,116,104,101,110, 10,101,
+ 114,114,111,114, 40, 34,116,111,108,117, 97, 58, 32,110,111,
+ 32,115,117,112,112,111,114,116, 32,102,111,114, 32,111,112,
+ 101,114, 97,116,111,114, 34, 32, 46, 46, 32,102, 46,107,105,
+ 110,100, 41, 10,101,110,100, 10,105,102, 32,102, 46,107,105,
+ 110,100, 32, 61, 61, 32, 39, 91, 93, 39, 32, 97,110,100, 32,
+ 110,111,116, 32,115,116,114,102,105,110,100, 40,102, 46,109,
+ 111,100, 44, 39, 99,111,110,115,116, 39, 41, 32,116,104,101,
+ 110, 10, 79,112,101,114, 97,116,111,114, 40,100, 44, 39, 38,
+ 39, 46, 46,107, 44, 97, 44, 99, 41, 10,101,110,100, 10,114,
+ 101,116,117,114,110, 32, 95, 79,112,101,114, 97,116,111,114,
+ 40,102, 41, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,
+ 115, 67,108, 97,115,115, 32, 61, 32,123, 10, 95, 98, 97,115,
+ 101, 32, 61, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,
+ 110,101,114, 44, 10,116,121,112,101, 32, 61, 32, 39, 99,108,
+ 97,115,115, 39, 44, 10,110, 97,109,101, 32, 61, 32, 39, 39,
+ 44, 10, 98, 97,115,101, 32, 61, 32, 39, 39, 44, 10,125, 10,
+ 115,101,116,116, 97,103, 40, 99,108, 97,115,115, 67,108, 97,
+ 115,115, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 67,108, 97,115,115, 58,114,101,103,105,115,116,101,114,
+ 32, 40, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,
+ 108,117, 97, 95, 99, 99,108, 97,115,115, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102,
+ 46, 98, 97,115,101, 46, 46, 39, 34, 41, 59, 39, 41, 10,108,
+ 111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,
+ 115,101,108,102, 91,105, 93, 32,100,111, 10,115,101,108,102,
+ 91,105, 93, 58,114,101,103,105,115,116,101,114, 40, 41, 10,
+ 105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,
+ 115,115, 67,108, 97,115,115, 58,117,110,114,101,103,105,115,
+ 116,101,114, 32, 40, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,108,117, 97, 95,112,117,115,104,110,105,108, 40,116,111,
+ 108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,101,116,
+ 103,108,111, 98, 97,108, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46,
+ 39, 34, 41, 59, 39, 41, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,108, 97,
+ 115,115, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,115,
+ 101,108,102, 46,105,116,121,112,101, 44,115,101,108,102, 46,
+ 116, 97,103, 32, 61, 32,116, 97,103,118, 97,114, 40,115,101,
+ 108,102, 46,110, 97,109,101, 41, 59, 10,115,101,108,102, 46,
+ 99,105,116,121,112,101, 44,115,101,108,102, 46, 99,116, 97,
+ 103, 32, 61, 32,116, 97,103,118, 97,114, 40,115,101,108,102,
+ 46,110, 97,109,101, 44, 39, 99,111,110,115,116, 39, 41, 59,
+ 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,
+ 101, 32,115,101,108,102, 91,105, 93, 32,100,111, 10,115,101,
+ 108,102, 91,105, 93, 58,100,101, 99,108,116, 97,103, 40, 41,
+ 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 67,108, 97,115,115, 58,112,114,105,110,116,
+ 32, 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 67,
+ 108, 97,115,115,123, 34, 41, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46,
+ 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34, 32, 98, 97,115,101, 32, 61, 32, 39, 34,
+ 46, 46,115,101,108,102, 46, 98, 97,115,101, 46, 46, 34, 39,
+ 59, 34, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,
+ 104,105,108,101, 32,115,101,108,102, 91,105, 93, 32,100,111,
+ 10,115,101,108,102, 91,105, 93, 58,112,114,105,110,116, 40,
+ 105,100,101,110,116, 46, 46, 34, 32, 34, 44, 34, 44, 34, 41,
+ 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112,114,
+ 105,110,116, 40,105,100,101,110,116, 46, 46, 34,125, 34, 46,
+ 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 95, 67,108, 97,115,115, 32,
+ 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,
+ 108, 97,115,115, 67,108, 97,115,115, 10,115,101,116,116, 97,
+ 103, 40,116, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10,
+ 97,112,112,101,110,100, 40,116, 41, 10,114,101,116,117,114,
+ 110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 67,108, 97,115,115, 32, 40,110, 44,112,
+ 44, 98, 41, 10,108,111, 99, 97,108, 32, 99, 32, 61, 32, 95,
+ 67,108, 97,115,115, 40, 95, 67,111,110,116, 97,105,110,101,
+ 114,123,110, 97,109,101, 61,110, 44, 32, 98, 97,115,101, 61,
+ 112,125, 41, 10,112,117,115,104, 40, 99, 41, 10, 99, 58,112,
+ 97,114,115,101, 40,115,116,114,115,117, 98, 40, 98, 44, 50,
+ 44,115,116,114,108,101,110, 40, 98, 41, 45, 49, 41, 41, 10,
+ 112,111,112, 40, 41, 10,101,110,100, 10, 10, 10, 10, 83, 84,
+ 82, 49, 32, 61, 32, 34, 92, 48, 48, 49, 34, 10, 83, 84, 82,
+ 50, 32, 61, 32, 34, 92, 48, 48, 50, 34, 10, 83, 84, 82, 51,
+ 32, 61, 32, 34, 92, 48, 48, 51, 34, 10, 83, 84, 82, 52, 32,
+ 61, 32, 34, 92, 48, 48, 52, 34, 10, 82, 69, 77, 32, 61, 32,
+ 34, 92, 48, 48, 53, 34, 10, 65, 78, 89, 32, 61, 32, 34, 40,
+ 91, 92, 48, 48, 49, 45, 92, 48, 48, 53, 93, 41, 34, 10, 69,
+ 83, 67, 49, 32, 61, 32, 34, 92, 48, 48, 54, 34, 10, 69, 83,
+ 67, 50, 32, 61, 32, 34, 92, 48, 48, 55, 34, 10, 10, 77, 65,
+ 83, 75, 32, 61, 32,123, 10,123, 69, 83, 67, 49, 44, 32, 34,
+ 92, 92, 39, 34,125, 44, 10,123, 69, 83, 67, 50, 44, 32, 39,
+ 92, 92, 34, 39,125, 44, 10,123, 83, 84, 82, 49, 44, 32, 34,
+ 39, 34,125, 44, 10,123, 83, 84, 82, 50, 44, 32, 39, 34, 39,
+ 125, 44, 10,123, 83, 84, 82, 51, 44, 32, 34, 37, 91, 37, 91,
+ 34,125, 44, 10,123, 83, 84, 82, 52, 44, 32, 34, 37, 93, 37,
+ 93, 34,125, 44, 10,123, 82, 69, 77, 32, 44, 32, 34, 37, 45,
+ 37, 45, 34,125, 44, 10,125, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32,109, 97,115,107, 32, 40,115, 41, 10,102,111,114,
+ 32,105, 32, 61, 32, 49, 44,103,101,116,110, 40, 77, 65, 83,
+ 75, 41, 32,100,111, 10,115, 32, 61, 32,103,115,117, 98, 40,
+ 115, 44, 77, 65, 83, 75, 91,105, 93, 91, 50, 93, 44, 77, 65,
+ 83, 75, 91,105, 93, 91, 49, 93, 41, 10,101,110,100, 10,114,
+ 101,116,117,114,110, 32,115, 10,101,110,100, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32,117,110,109, 97,115,107, 32, 40,
+ 115, 41, 10,102,111,114, 32,105, 32, 61, 32, 49, 44,103,101,
+ 116,110, 40, 77, 65, 83, 75, 41, 32,100,111, 10,115, 32, 61,
+ 32,103,115,117, 98, 40,115, 44, 77, 65, 83, 75, 91,105, 93,
+ 91, 49, 93, 44, 77, 65, 83, 75, 91,105, 93, 91, 50, 93, 41,
+ 10,101,110,100, 10,114,101,116,117,114,110, 32,115, 10,101,
+ 110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 101, 97,110, 32, 40,115, 41, 10, 10,108,111, 99, 97,108, 32,
+ 99,111,100,101, 32, 61, 32, 34,114,101,116,117,114,110, 32,
+ 102,117,110, 99,116,105,111,110, 32, 40, 41, 32, 34, 32, 46,
+ 46, 32,115, 32, 46, 46, 32, 34, 32,101,110,100, 34, 10,105,
+ 102, 32,110,111,116, 32,100,111,115,116,114,105,110,103, 40,
+ 99,111,100,101, 41, 32,116,104,101,110, 10,114,101,116,117,
+ 114,110, 32,110,105,108, 10,101,110,100, 10, 10,108,111, 99,
+ 97,108, 32, 83, 32, 61, 32, 34, 34, 10, 10,115, 32, 61, 32,
+ 109, 97,115,107, 40,115, 41, 10, 10, 10,119,104,105,108,101,
+ 32, 49, 32,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101,
+ 44,100, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44,
+ 65, 78, 89, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10,
+ 83, 32, 61, 32, 83, 46, 46,115,116,114,115,117, 98, 40,115,
+ 44, 49, 44, 98, 45, 49, 41, 10,115, 32, 61, 32,115,116,114,
+ 115,117, 98, 40,115, 44, 98, 43, 49, 41, 10,105,102, 32,100,
+ 61, 61, 83, 84, 82, 49, 32,111,114, 32,100, 61, 61, 83, 84,
+ 82, 50, 32,116,104,101,110, 10,101, 32, 61, 32,115,116,114,
+ 102,105,110,100, 40,115, 44,100, 41, 10, 83, 32, 61, 32, 83,
+ 32, 46, 46,100, 46, 46,115,116,114,115,117, 98, 40,115, 44,
+ 49, 44,101, 41, 10,115, 32, 61, 32,115,116,114,115,117, 98,
+ 40,115, 44,101, 43, 49, 41, 10,101,108,115,101,105,102, 32,
+ 100, 61, 61, 83, 84, 82, 51, 32,116,104,101,110, 10,101, 32,
+ 61, 32,115,116,114,102,105,110,100, 40,115, 44, 83, 84, 82,
+ 52, 41, 10, 83, 32, 61, 32, 83, 46, 46,100, 46, 46,115,116,
+ 114,115,117, 98, 40,115, 44, 49, 44,101, 41, 10,115, 32, 61,
+ 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,
+ 101,108,115,101,105,102, 32,100, 61, 61, 82, 69, 77, 32,116,
+ 104,101,110, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44,
+ 34, 91, 94, 92,110, 93, 42, 40, 92,110, 63, 41, 34, 44, 34,
+ 37, 49, 34, 44, 49, 41, 10,101,110,100, 10,101,108,115,101,
+ 10, 83, 32, 61, 32, 83, 46, 46,115, 10, 98,114,101, 97,107,
+ 10,101,110,100, 10,101,110,100, 10, 10, 83, 32, 61, 32,103,
+ 115,117, 98, 40, 83, 44, 34, 91, 32, 92,116, 93, 43, 34, 44,
+ 34, 32, 34, 41, 10, 83, 32, 61, 32,103,115,117, 98, 40, 83,
+ 44, 34, 91, 32, 92,116, 93, 42, 92,110, 91, 32, 92,116, 93,
+ 42, 34, 44, 34, 92,110, 34, 41, 10, 83, 32, 61, 32,117,110,
+ 109, 97,115,107, 40, 83, 41, 10,114,101,116,117,114,110, 32,
+ 83, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10,105,102, 32,102,108, 97,103,115, 46,102,
+ 32,116,104,101,110, 10,108,111, 99, 97,108, 32,115,116, 44,
+ 32,109,115,103, 32, 61, 32,114,101, 97,100,102,114,111,109,
+ 40,102,108, 97,103,115, 46,102, 41, 10,105,102, 32,110,111,
+ 116, 32,115,116, 32,116,104,101,110, 10,101,114,114,111,114,
+ 40, 39, 35, 39, 46, 46,109,115,103, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,105,102, 32,110,111,116, 32,102,108,
+ 97,103,115, 46,110, 32,116,104,101,110, 10,105,102, 32,102,
+ 108, 97,103,115, 46,102, 32,116,104,101,110, 10,102,108, 97,
+ 103,115, 46,110, 32, 61, 32,103,115,117, 98, 40,102,108, 97,
+ 103,115, 46,102, 44, 34, 37, 46, 46, 42, 34, 44, 34, 34, 41,
+ 10,101,108,115,101, 10,101,114,114,111,114, 40, 34, 35,110,
+ 111, 32,112, 97, 99,107, 97,103,101, 32,110, 97,109,101, 32,
+ 110,111,114, 32,105,110,112,117,116, 32,102,105,108,101, 32,
+ 112,114,111,118,105,100,101,100, 34, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10,108,111, 99, 97,108, 32,112, 32, 61, 32,
+ 80, 97, 99,107, 97,103,101, 40,102,108, 97,103,115, 46,110,
+ 41, 10, 10,105,102, 32,102,108, 97,103,115, 46,102, 32,116,
+ 104,101,110, 10,114,101, 97,100,102,114,111,109, 40, 41, 10,
+ 101,110,100, 10, 10,105,102, 32,102,108, 97,103,115, 46,112,
+ 32,116,104,101,110, 10,114,101,116,117,114,110, 10,101,110,
+ 100, 10, 10,105,102, 32,102,108, 97,103,115, 46,111, 32,116,
+ 104,101,110, 10,108,111, 99, 97,108, 32,115,116, 44,109,115,
+ 103, 32, 61, 32,119,114,105,116,101,116,111, 40,102,108, 97,
+ 103,115, 46,111, 41, 10,105,102, 32,110,111,116, 32,115,116,
+ 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35, 39,
+ 46, 46,109,115,103, 41, 10,101,110,100, 10,101,110,100, 10,
+ 10,105,102, 32,102,108, 97,103,115, 46, 80, 32,116,104,101,
+ 110, 10,112, 58,112,114,105,110,116, 40, 41, 10,101,108,115,
+ 101, 10,112, 58,100,101, 99,108,116, 97,103, 40, 41, 10,112,
+ 58,112,114,101, 97,109, 98,108,101, 40, 41, 10,112, 58,115,
+ 117,112, 99,111,100,101, 40, 41, 10,112, 58,114,101,103,105,
+ 115,116,101,114, 40, 41, 10,112, 58,117,110,114,101,103,105,
+ 115,116,101,114, 40, 41, 10,101,110,100, 10, 10,105,102, 32,
+ 102,108, 97,103,115, 46,111, 32,116,104,101,110, 10,119,114,
+ 105,116,101,116,111, 40, 41, 10,101,110,100, 10, 10, 10,105,
+ 102, 32,110,111,116, 32,102,108, 97,103,115, 46, 80, 32,116,
+ 104,101,110, 10,105,102, 32,102,108, 97,103,115, 46, 72, 32,
+ 116,104,101,110, 10,108,111, 99, 97,108, 32,115,116, 44,109,
+ 115,103, 32, 61, 32,119,114,105,116,101,116,111, 40,102,108,
+ 97,103,115, 46, 72, 41, 10,105,102, 32,110,111,116, 32,115,
+ 116, 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35,
+ 39, 46, 46,109,115,103, 41, 10,101,110,100, 10,112, 58,104,
+ 101, 97,100,101,114, 40, 41, 10,119,114,105,116,101,116,111,
+ 40, 41, 10,101,110,100, 10,101,110,100,32
+ };
+ lua_dobuffer(tolua_S,(char*)B,sizeof(B),"tolua: embedded Lua code");
+ } /* end of embedded lua code */
+
+ return 1;
+}
+/* Close function */
+void tolua_tolualua_close (lua_State* tolua_S)
+{
+}
diff --git a/src/lua/tolualua.h b/src/lua/tolualua.h
new file mode 100644
index 00000000..b380dcef
--- /dev/null
+++ b/src/lua/tolualua.h
@@ -0,0 +1,2713 @@
+/* code automatically generated by bin2c -- DO NOT EDIT */
+{
+/* #include'ing this file in a C program is equivalent to calling
+ lua_dofile("basic.lo");
+ lua_dofile("feature.lo");
+ lua_dofile("declaration.lo");
+ lua_dofile("container.lo");
+ lua_dofile("package.lo");
+ lua_dofile("module.lo");
+ lua_dofile("class.lo");
+ lua_dofile("typedef.lo");
+ lua_dofile("define.lo");
+ lua_dofile("enumerate.lo");
+ lua_dofile("variable.lo");
+ lua_dofile("array.lo");
+ lua_dofile("function.lo");
+ lua_dofile("operator.lo");
+ lua_dofile("verbatim.lo");
+ lua_dofile("code.lo");
+ lua_dofile("doit.lo");
+*/
+/* basic.lo */
+static char B1[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 11, 64, 98, 97,115,105, 99,
+ 46,108,117, 97, 0, 0, 0, 0,190, 25, 0, 60, 17, 22, 12, 60, 18, 11, 1, 11,
+ 2, 60, 19, 11, 3, 11, 4, 60, 20, 11, 5, 11, 4, 60, 21, 11, 6, 11, 4, 60,
+ 22, 11, 7, 11, 4, 60, 23, 11, 8, 11, 4, 60, 24, 11, 9, 11, 4, 60, 25, 11,
+ 10, 11, 11, 60, 26, 11, 12, 11, 13, 60, 27, 11, 14, 11, 11, 60, 28, 11, 15, 11,
+ 13, 60, 29, 11, 16, 11, 17, 30, 11, 60, 30, 25, 0, 60, 34, 22, 0, 25, 18, 60,
+ 37, 11, 20, 25, 19, 60, 45, 15, 22, 2, 1, 0, 25, 21, 60, 46, 15, 24, 15, 21,
+ 11, 25, 15, 19, 2, 1, 3, 25, 23, 60, 49, 11, 27, 25, 26, 60, 69, 15, 28, 15,
+ 26, 2, 0, 1, 60, 72, 11, 30, 25, 29, 60, 80, 22, 0, 25, 31, 60, 81, 11, 33,
+ 25, 32, 60, 89, 11, 35, 25, 34, 60,109, 11, 37, 25, 36, 60,115, 11, 39, 25, 38,
+ 60,121, 11, 41, 25, 40, 60,138, 11, 43, 25, 42, 60,150, 11, 45, 25, 44, 0, 0,
+ 0, 0, 0, 0, 0, 0, 46, 2, 0, 0, 0, 7, 95, 98, 97,115,105, 99, 0, 2,
+ 0, 0, 0, 5,118,111,105,100, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5,
+ 99,104, 97,114, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0,
+ 0, 4,105,110,116, 0, 2, 0, 0, 0, 6,115,104,111,114,116, 0, 2, 0, 0,
+ 0, 5,108,111,110,103, 0, 2, 0, 0, 0, 6,102,108,111, 97,116, 0, 2, 0,
+ 0, 0, 7,100,111,117, 98,108,101, 0, 2, 0, 0, 0, 9, 95, 99,115,116,114,
+105,110,103, 0, 2, 0, 0, 0, 7,115,116,114,105,110,103, 0, 2, 0, 0, 0,
+ 10, 95,117,115,101,114,100, 97,116, 97, 0, 2, 0, 0, 0, 9,117,115,101,114,
+100, 97,116, 97, 0, 2, 0, 0, 0, 6, 99,104, 97,114, 42, 0, 2, 0, 0, 0,
+ 6,118,111,105,100, 42, 0, 2, 0, 0, 0, 11,108,117, 97, 95, 79, 98,106,101,
+ 99,116, 0, 2, 0, 0, 0, 7,111, 98,106,101, 99,116, 0, 2, 0, 0, 0, 10,
+ 95,117,115,101,114,116,121,112,101, 0, 2, 0, 0, 0, 12,116,111,108,117, 97,
+ 95,105,110,100,101,120, 0, 4, 0, 0, 0, 37, 0, 0, 0, 11, 64, 98, 97,115,
+105, 99, 46,108,117, 97, 0, 0, 0, 0, 40, 5, 2, 60, 38, 13, 1, 11, 2, 32,
+ 52, 13, 60, 39, 15, 3, 13, 0, 13, 1, 3, 2, 2, 50, 13, 60, 41, 13, 0, 18,
+ 2, 13, 1, 16, 1, 2, 60, 42, 60, 43, 0, 0, 0, 0, 2, 0, 0, 0, 37, 0,
+ 0, 0, 2,116, 0, 0, 0, 0, 37, 0, 0, 0, 2,102, 0, 0, 0, 0, 4, 2,
+ 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 2,102, 0, 2, 0, 0, 0, 6, 95, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 16,116,111,108,117, 97, 95,111,108,100, 95,105,
+110,100,101,120, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0,
+ 2, 0, 0, 0, 7,110,101,119,116, 97,103, 0, 2, 0, 0, 0, 16,116,111,108,
+117, 97, 95,111,108,100, 95,105,110,100,101,120, 0, 2, 0, 0, 0, 13,115,101,
+116,116, 97,103,109,101,116,104,111,100, 0, 2, 0, 0, 0, 6,105,110,100,101,
+120, 0, 2, 0, 0, 0, 12,116,111,108,117, 97, 95,101,114,114,111,114, 0, 4,
+ 0, 0, 0, 49, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0, 0,
+ 0, 0,163, 9, 1, 60, 50, 15, 2, 60, 51, 15, 3, 25, 2, 60, 52, 15, 4, 13,
+ 0, 7, 1, 7, 1, 2, 1, 3, 11, 5, 32, 52, 24, 60, 53, 15, 6, 11, 7, 15,
+ 4, 13, 0, 7, 2, 2, 1, 2, 42, 11, 8, 42, 2, 0, 1, 50, 19, 60, 55, 15,
+ 6, 11, 9, 13, 0, 42, 11, 8, 42, 2, 0, 1, 60, 57, 1, 2, 60, 59, 15, 10,
+ 52, 73, 60, 60, 15, 12, 15, 10, 11, 13, 2, 3, 2, 60, 61, 13, 4, 4, 0, 32,
+ 52, 4, 15, 10, 23, 4, 60, 62, 15, 14, 13, 4, 11, 15, 11, 16, 2, 1, 3, 23,
+ 4, 60, 63, 15, 14, 13, 4, 11, 17, 11, 18, 2, 1, 3, 23, 4, 60, 64, 15, 6,
+ 11, 19, 13, 4, 42, 11, 20, 42, 2, 0, 1, 5, 3, 50, 2, 60, 65, 60, 66, 13,
+ 1, 25, 2, 60, 67, 0, 0, 0, 0, 8, 0, 0, 0, 49, 0, 0, 0, 2,115, 0,
+ 0, 0, 0, 50, 0, 0, 0, 4,111,117,116, 0, 0, 0, 0, 60, 0, 0, 0, 2,
+ 95, 0, 0, 0, 0, 60, 0, 0, 0, 2, 95, 0, 0, 0, 0, 60, 0, 0, 0, 2,
+115, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0,
+ 0, 64, 0, 0, 0, 0, 0, 0, 0, 21, 2, 0, 0, 0, 2,115, 0, 2, 0, 0,
+ 0, 4,111,117,116, 0, 2, 0, 0, 0, 8, 95, 79, 85, 84, 80, 85, 84, 0, 2,
+ 0, 0, 0, 8, 95, 83, 84, 68, 69, 82, 82, 0, 2, 0, 0, 0, 7,115,116,114,
+115,117, 98, 0, 2, 0, 0, 0, 2, 35, 0, 2, 0, 0, 0, 6,119,114,105,116,
+101, 0, 2, 0, 0, 0, 12, 10, 42, 42, 32,116,111,108,117, 97, 58, 32, 0, 2,
+ 0, 0, 0, 4, 46, 10, 10, 0, 2, 0, 0, 0, 27, 10, 42, 42, 32,116,111,108,
+117, 97, 32,105,110,116,101,114,110, 97,108, 32,101,114,114,111,114, 58, 32, 0,
+ 2, 0, 0, 0, 11, 95, 99,117,114,114, 95, 99,111,100,101, 0, 2, 0, 0, 0,
+ 2, 95, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0,
+ 10, 94, 37,115, 42, 40, 46, 45, 10, 41, 0, 2, 0, 0, 0, 5,103,115,117, 98,
+ 0, 2, 0, 0, 0, 10, 95,117,115,101,114,100, 97,116, 97, 0, 2, 0, 0, 0,
+ 6,118,111,105,100, 42, 0, 2, 0, 0, 0, 9, 95, 99,115,116,114,105,110,103,
+ 0, 2, 0, 0, 0, 6, 99,104, 97,114, 42, 0, 2, 0, 0, 0, 23, 67,111,100,
+101, 32, 98,101,105,110,103, 32,112,114,111, 99,101,115,115,101,100, 58, 10, 0,
+ 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 15,115,101,116,101,114,114,111,114,
+109,101,116,104,111,100, 0, 2, 0, 0, 0, 8,114,101,103,116,121,112,101, 0,
+ 4, 0, 0, 0, 72, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0,
+ 0, 0, 0, 36, 4, 1, 60, 73, 15, 1, 13, 0, 2, 1, 1, 44, 52, 11, 60, 74,
+ 15, 2, 13, 0, 13, 0, 26, 50, 2, 60, 75, 60, 76, 13, 0, 1, 1, 60, 77, 0,
+ 0, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 2,116, 0, 0, 0, 0, 3, 2, 0,
+ 0, 0, 2,116, 0, 2, 0, 0, 0, 7,105,115,116,121,112,101, 0, 2, 0, 0,
+ 0, 10, 95,117,115,101,114,116,121,112,101, 0, 2, 0, 0, 0, 10, 95,116, 97,
+103,110, 97,109,101,115, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0,
+ 4, 0, 0, 0, 81, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0,
+ 0, 0, 0, 67, 5, 2, 60, 82, 13, 0, 11, 2, 31, 48, 5, 13, 1, 7, 0, 31,
+ 48, 8, 15, 3, 13, 0, 2, 1, 1, 44, 48, 6, 15, 4, 13, 1, 16, 44, 52, 26,
+ 60, 83, 15, 4, 13, 1, 13, 0, 26, 60, 84, 15, 5, 11, 6, 13, 1, 42, 11, 7,
+ 42, 2, 0, 1, 50, 2, 60, 85, 60, 86, 0, 0, 0, 0, 2, 0, 0, 0, 81, 0,
+ 0, 0, 6,105,116,121,112,101, 0, 0, 0, 0, 81, 0, 0, 0, 4,116, 97,103,
+ 0, 0, 0, 0, 8, 2, 0, 0, 0, 6,105,116,121,112,101, 0, 2, 0, 0, 0,
+ 4,116, 97,103, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 8,105,115, 98, 97,
+115,105, 99, 0, 2, 0, 0, 0, 10, 95,116, 97,103,110, 97,109,101,115, 0, 2,
+ 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 12,115,116, 97,116,
+105, 99, 32,105,110,116, 32, 0, 2, 0, 0, 0, 2, 59, 0, 2, 0, 0, 0, 7,
+116, 97,103,118, 97,114, 0, 4, 0, 0, 0, 89, 0, 0, 0, 11, 64, 98, 97,115,
+105, 99, 46,108,117, 97, 0, 0, 0, 0,155, 8, 2, 60, 90, 13, 0, 11, 2, 32,
+ 46, 5, 13, 0, 11, 3, 32, 52, 10, 60, 91, 13, 0, 7, 0, 1, 2, 50,124, 60,
+ 92, 15, 4, 13, 0, 2, 1, 1, 52, 18, 60, 93, 13, 0, 11, 5, 15, 4, 13, 0,
+ 2, 1, 1, 42, 1, 2, 50, 95, 60, 95, 15, 8, 13, 0, 2, 2, 1, 60, 96, 15,
+ 9, 13, 2, 11, 1, 2, 1, 2, 52, 4, 11, 1, 23, 1, 60, 97, 13, 3, 23, 0,
+ 60, 98, 15, 10, 13, 3, 2, 0, 1, 60, 99, 11, 5, 60,100, 13, 1, 48, 5, 13,
+ 1, 11, 2, 31, 52, 20, 60,101, 11, 12, 13, 3, 42, 23, 3, 60,102, 13, 4, 11,
+ 13, 42, 23, 4, 50, 2, 60,103, 60,104, 13, 3, 13, 4, 13, 0, 42, 1, 5, 5,
+ 3, 60,105, 60,106, 0, 0, 0, 0, 8, 0, 0, 0, 89, 0, 0, 0, 5,116,121,
+112,101, 0, 0, 0, 0, 89, 0, 0, 0, 6, 99,111,110,115,116, 0, 0, 0, 0,
+ 95, 0, 0, 0, 2,109, 0, 0, 0, 0, 95, 0, 0, 0, 2,116, 0, 0, 0, 0,
+ 99, 0, 0, 0, 2,118, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0, 0,104, 0,
+ 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 14, 2, 0, 0, 0, 5,
+116,121,112,101, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 0, 0, 5,118,111,105,100, 0, 2, 0, 0, 0, 8,105,115, 98,
+ 97,115,105, 99, 0, 2, 0, 0, 0, 11,116,111,108,117, 97, 95,116, 97,103, 95,
+ 0, 2, 0, 0, 0, 2,109, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 12,
+102,105,110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 8,115,116,114,
+102,105,110,100, 0, 2, 0, 0, 0, 8,114,101,103,116,121,112,101, 0, 2, 0,
+ 0, 0, 2,118, 0, 2, 0, 0, 0, 7, 99,111,110,115,116, 32, 0, 2, 0, 0,
+ 0, 7, 99,111,110,115,116, 95, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105,
+ 99, 0, 4, 0, 0, 0,109, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117,
+ 97, 0, 0, 0, 0, 23, 5, 1, 60,110, 15, 3, 13, 0, 2, 2, 1, 60,111, 15,
+ 4, 13, 2, 16, 1, 3, 60,112, 0, 0, 0, 0, 3, 0, 0, 0,109, 0, 0, 0,
+ 5,116,121,112,101, 0, 0, 0, 0,110, 0, 0, 0, 2,109, 0, 0, 0, 0,110,
+ 0, 0, 0, 2,116, 0, 0, 0, 0, 5, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 2,109, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 12,102,
+105,110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 7, 95, 98, 97,115,
+105, 99, 0, 2, 0, 0, 0, 7,105,115,116,121,112,101, 0, 4, 0, 0, 0,115,
+ 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0, 0, 0, 0, 30, 3,
+ 1, 60,116, 15, 1, 13, 0, 16, 46, 5, 15, 2, 13, 0, 16, 46, 7, 15, 3, 13,
+ 0, 2, 1, 1, 1, 1, 60,117, 0, 0, 0, 0, 1, 0, 0, 0,115, 0, 0, 0,
+ 2,116, 0, 0, 0, 0, 4, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 7, 95,
+ 98, 97,115,105, 99, 0, 2, 0, 0, 0, 10, 95,117,115,101,114,116,121,112,101,
+ 0, 2, 0, 0, 0, 10,105,115,116,121,112,101,100,101,102, 0, 2, 0, 0, 0,
+ 6,115,112,108,105,116, 0, 4, 0, 0, 0,121, 0, 0, 0, 11, 64, 98, 97,115,
+105, 99, 46,108,117, 97, 0, 0, 0, 0,117, 11, 2, 60,122, 22, 1, 11, 3, 7,
+ 0, 30, 0, 60,123, 13, 2, 58, 5, 1, 60,127, 11, 7, 13, 1, 42, 11, 8, 42,
+ 60,128, 15, 9, 13, 0, 11, 10, 11, 11, 2, 1, 3, 23, 0, 60,129, 15, 9, 13,
+ 0, 11, 12, 11, 11, 2, 1, 3, 23, 0, 60,130, 15, 9, 13, 0, 13, 4, 13, 3,
+ 2, 1, 3, 23, 0, 60,131, 13, 2, 11, 14, 13, 2, 18, 14, 7, 1, 37, 26, 60,
+132, 13, 2, 13, 2, 18, 14, 15, 9, 13, 0, 11, 15, 11, 11, 2, 1, 3, 26, 60,
+133, 13, 2, 1, 5, 60,134, 0, 0, 0, 0, 5, 0, 0, 0,121, 0, 0, 0, 2,
+115, 0, 0, 0, 0,121, 0, 0, 0, 2,116, 0, 0, 0, 0,122, 0, 0, 0, 2,
+108, 0, 0, 0, 0,123, 0, 0, 0, 2,102, 0, 0, 0, 0,127, 0, 0, 0, 2,
+112, 0, 0, 0, 0, 16, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 2,116, 0,
+ 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,102,
+ 0, 4, 0, 0, 0,123, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97,
+ 0, 0, 0, 0, 30, 5, 1, 60,124, 12, 0, 11, 2, 12, 0, 18, 2, 7, 1, 37,
+ 26, 60,125, 12, 0, 12, 0, 18, 2, 13, 0, 26, 60,126, 0, 0, 0, 0, 1, 0,
+ 0, 0,123, 0, 0, 0, 2,115, 0, 0, 0, 0, 3, 2, 0, 0, 0, 2,115, 0,
+ 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,112,
+ 0, 2, 0, 0, 0, 11, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 0, 2, 0, 0,
+ 0, 4, 37,115, 42, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0,
+ 5, 94, 37,115, 43, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5, 37,115, 43,
+ 36, 0, 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0,
+ 9, 40, 37,115, 37,115, 42, 41, 36, 0, 2, 0, 0, 0, 7, 99,111,110, 99, 97,
+116, 0, 4, 0, 0, 0,138, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117,
+ 97, 0, 0, 0, 0, 69, 8, 3, 60,139, 11, 4, 60,140, 13, 1, 50, 39, 60,142,
+ 13, 3, 13, 0, 13, 4, 16, 42, 23, 3, 60,143, 13, 4, 7, 1, 37, 23, 4, 60,
+144, 13, 4, 13, 2, 34, 52, 7, 13, 3, 11, 6, 42, 23, 3, 60,145, 60,141, 13,
+ 4, 13, 2, 34, 54, 48, 60,146, 13, 3, 1, 5, 60,147, 0, 0, 0, 0, 5, 0,
+ 0, 0,138, 0, 0, 0, 2,116, 0, 0, 0, 0,138, 0, 0, 0, 2,102, 0, 0,
+ 0, 0,138, 0, 0, 0, 2,108, 0, 0, 0, 0,139, 0, 0, 0, 2,115, 0, 0,
+ 0, 0,140, 0, 0, 0, 2,105, 0, 0, 0, 0, 7, 2, 0, 0, 0, 2,116, 0,
+ 2, 0, 0, 0, 2,102, 0, 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,115,
+ 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 2, 32,
+ 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 4, 0, 0, 0,150, 0, 0,
+ 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0, 0, 0, 0,161, 6,128, 60,
+151, 7, 1, 50,104, 60,153, 15, 3, 48, 10, 15, 4, 15, 3, 11, 5, 2, 1, 2,
+ 44, 48, 14, 60,154, 15, 4, 13, 0, 13, 1, 16, 11, 6, 2, 1, 2, 52, 11, 60,
+155, 15, 7, 11, 8, 2, 0, 1, 50, 2, 60,156, 60,157, 15, 7, 13, 0, 13, 1,
+ 16, 2, 0, 1, 60,158, 13, 0, 13, 1, 16, 11, 9, 31, 52, 20, 60,159, 15, 10,
+ 13, 0, 13, 1, 16, 9, 1, 9, 1, 2, 1, 3, 25, 3, 50, 2, 60,160, 60,161,
+ 13, 1, 7, 1, 37, 23, 1, 60,162, 60,152, 13, 1, 13, 0, 18, 2, 34, 54,115,
+ 60,163, 15, 4, 13, 0, 13, 0, 18, 2, 16, 11, 11, 2, 1, 2, 52, 15, 60,164,
+ 4, 0, 25, 3, 15, 7, 11, 12, 2, 0, 1, 50, 2, 60,165, 60,166, 0, 0, 0,
+ 0, 2, 0, 0, 0,150, 0, 0, 0, 4, 97,114,103, 0, 0, 0, 0,151, 0, 0,
+ 0, 2,105, 0, 0, 0, 0, 13, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 4,
+ 97,114,103, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 6, 95, 99,111,110,
+116, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 7,
+ 91, 37, 40, 44, 34, 93, 0, 2, 0, 0, 0, 8, 94, 91, 37, 97, 95,126, 93, 0,
+ 2, 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0,
+ 0, 0, 1, 0, 2, 0, 0, 0, 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0,
+ 14, 91, 37, 47, 37, 41, 37, 59, 37,123, 37,125, 93, 36, 0, 2, 0, 0, 0, 2,
+ 10, 0,
+};
+
+/* feature.lo */
+static char B2[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 13, 64,102,101, 97,116,117,
+114,101, 46,108,117, 97, 0, 0, 0, 0, 83, 3, 0, 60, 16, 22, 0, 60, 17, 25,
+ 0, 60, 20, 15, 0, 11, 1, 11, 2, 26, 60, 24, 15, 0, 11, 3, 11, 4, 26, 60,
+ 28, 15, 0, 11, 5, 11, 6, 26, 60, 32, 15, 0, 11, 7, 11, 8, 26, 60, 36, 15,
+ 0, 11, 9, 11, 10, 26, 60, 40, 15, 0, 11, 11, 11, 12, 26, 60, 49, 15, 0, 11,
+ 13, 11, 14, 26, 60, 59, 15, 0, 11, 15, 11, 16, 26, 0, 0, 0, 0, 0, 0, 0,
+ 0, 17, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 0,
+ 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0, 4, 0, 0, 0, 20, 0, 0,
+ 0, 13, 64,102,101, 97,116,117,114,101, 46,108,117, 97, 0, 0, 0, 0, 5, 1,
+ 1, 60, 21, 0, 0, 0, 0, 1, 0, 0, 0, 20, 0, 0, 0, 5,115,101,108,102,
+ 0, 0, 0, 0, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0, 4, 0,
+ 0, 0, 24, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101, 46,108,117, 97, 0,
+ 0, 0, 0, 5, 1, 1, 60, 25, 0, 0, 0, 0, 1, 0, 0, 0, 24, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 0, 2, 0, 0, 0, 9,114,101,103,105,115,
+116,101,114, 0, 4, 0, 0, 0, 28, 0, 0, 0, 13, 64,102,101, 97,116,117,114,
+101, 46,108,117, 97, 0, 0, 0, 0, 5, 1, 1, 60, 29, 0, 0, 0, 0, 1, 0,
+ 0, 0, 28, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 0, 2, 0, 0, 0,
+ 11,117,110,114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0, 32, 0, 0, 0,
+ 13, 64,102,101, 97,116,117,114,101, 46,108,117, 97, 0, 0, 0, 0, 5, 1, 1,
+ 60, 33, 0, 0, 0, 0, 1, 0, 0, 0, 32, 0, 0, 0, 5,115,101,108,102, 0,
+ 0, 0, 0, 0, 2, 0, 0, 0, 9,112,114,101, 97,109, 98,108,101, 0, 4, 0,
+ 0, 0, 36, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101, 46,108,117, 97, 0,
+ 0, 0, 0, 5, 1, 1, 60, 37, 0, 0, 0, 0, 1, 0, 0, 0, 36, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,
+115,115, 0, 4, 0, 0, 0, 40, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101,
+ 46,108,117, 97, 0, 0, 0, 0, 44, 3, 1, 60, 41, 13, 0, 18, 1, 48, 9, 13,
+ 0, 18, 1, 18, 2, 11, 3, 32, 52, 12, 60, 42, 13, 0, 18, 1, 18, 4, 1, 1,
+ 50, 8, 60, 44, 4, 0, 1, 1, 60, 45, 60, 46, 0, 0, 0, 0, 1, 0, 0, 0,
+ 40, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 5, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0,
+ 5,116,121,112,101, 0, 2, 0, 0, 0, 6, 99,108, 97,115,115, 0, 2, 0, 0,
+ 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101,
+ 0, 4, 0, 0, 0, 49, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101, 46,108,
+117, 97, 0, 0, 0, 0, 44, 3, 1, 60, 50, 13, 0, 18, 1, 48, 9, 13, 0, 18,
+ 1, 18, 2, 11, 3, 32, 52, 12, 60, 51, 13, 0, 18, 1, 18, 4, 1, 1, 50, 8,
+ 60, 53, 4, 0, 1, 1, 60, 54, 60, 55, 0, 0, 0, 0, 1, 0, 0, 0, 49, 0,
+ 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 5, 2, 0, 0, 0, 5,115,101,108,
+102, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0, 5,116,
+121,112,101, 0, 2, 0, 0, 0, 7,109,111,100,117,108,101, 0, 2, 0, 0, 0,
+ 5,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,110, 99,110, 97,109,101,
+ 0, 4, 0, 0, 0, 59, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101, 46,108,
+117, 97, 0, 0, 0, 0, 72, 5, 2, 60, 60, 13, 0, 18, 2, 52, 17, 60, 61, 13,
+ 0, 18, 2, 20, 3, 13, 1, 2, 1, 2, 23, 1, 50, 2, 60, 62, 60, 63, 13, 0,
+ 18, 4, 52, 16, 60, 64, 13, 1, 11, 5, 42, 13, 0, 18, 4, 42, 1, 2, 50, 16,
+ 60, 66, 13, 1, 11, 5, 42, 13, 0, 18, 6, 42, 1, 2, 60, 67, 60, 68, 0, 0,
+ 0, 0, 2, 0, 0, 0, 59, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 59,
+ 0, 0, 0, 2,110, 0, 0, 0, 0, 7, 2, 0, 0, 0, 2,110, 0, 2, 0, 0,
+ 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2,
+ 0, 0, 0, 10, 99,102,117,110, 99,110, 97,109,101, 0, 2, 0, 0, 0, 6,108,
+110, 97,109,101, 0, 2, 0, 0, 0, 2, 95, 0, 2, 0, 0, 0, 5,110, 97,109,
+101, 0,
+};
+
+/* declaration.lo */
+static char B3[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 17, 64,100,101, 99,108, 97,
+114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0,193, 17, 0, 60, 24, 22,
+ 8, 60, 25, 11, 1, 15, 2, 11, 3, 60, 26, 11, 4, 11, 5, 60, 27, 11, 4, 11,
+ 6, 60, 28, 11, 4, 11, 7, 60, 29, 11, 4, 11, 8, 60, 30, 11, 4, 11, 9, 60,
+ 31, 11, 4, 11, 10, 60, 32, 11, 4, 30, 7, 60, 33, 25, 0, 60, 34, 15, 11, 15,
+ 0, 15, 12, 2, 0, 2, 60, 37, 11, 14, 25, 13, 60, 45, 15, 0, 11, 15, 11, 16,
+ 26, 60, 84, 15, 0, 11, 17, 11, 18, 26, 60,117, 15, 0, 11, 19, 11, 20, 26, 60,
+130, 15, 0, 11, 21, 11, 22, 26, 60,136, 15, 0, 11, 23, 11, 24, 26, 60,149, 15,
+ 0, 11, 25, 11, 26, 26, 60,184, 15, 0, 11, 27, 11, 28, 26, 60,218, 15, 0, 11,
+ 29, 11, 30, 26, 60,250, 15, 0, 11, 31, 11, 32, 26, 59, 1, 1, 15, 0, 11, 33,
+ 11, 34, 26, 59, 1, 12, 15, 0, 11, 35, 11, 36, 26, 59, 1, 26, 11, 38, 25, 37,
+ 59, 1, 42, 11, 40, 25, 39, 0, 0, 0, 0, 0, 0, 0, 0, 41, 2, 0, 0, 0,
+ 17, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 2, 0,
+ 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,
+101, 97,116,117,114,101, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 4,112,116,114,
+ 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 4,100,105,109, 0,
+ 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0,
+ 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97,
+ 95,116, 97,103, 0, 2, 0, 0, 0, 15, 99,114,101, 97,116,101, 95,118, 97,114,
+110, 97,109,101, 0, 4, 0, 0, 0, 37, 0, 0, 0, 17, 64,100,101, 99,108, 97,
+114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 34, 2, 0, 60, 38, 15,
+ 0, 44, 52, 4, 7, 0, 25, 0, 60, 39, 15, 0, 7, 1, 37, 25, 0, 60, 40, 11,
+ 1, 15, 0, 42, 1, 0, 60, 41, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0,
+ 0, 11, 95,118, 97,114,110,117,109, 98,101,114, 0, 2, 0, 0, 0, 11,116,111,
+108,117, 97, 95,118, 97,114, 95, 0, 2, 0, 0, 0, 10, 99,104,101, 99,107,110,
+ 97,109,101, 0, 4, 0, 0, 0, 45, 0, 0, 0, 17, 64,100,101, 99,108, 97,114,
+ 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 1,140, 12, 1, 60, 47, 15, 0,
+ 13, 0, 18, 2, 7, 1, 7, 1, 2, 1, 3, 11, 3, 32, 48, 10, 15, 4, 13, 0,
+ 18, 5, 2, 1, 1, 44, 52, 70, 60, 48, 13, 0, 11, 2, 13, 0, 18, 5, 13, 0,
+ 18, 2, 42, 26, 60, 49, 15, 7, 13, 0, 18, 8, 11, 9, 2, 1, 2, 60, 50, 13,
+ 0, 11, 5, 13, 1, 13, 1, 18, 10, 16, 26, 60, 51, 13, 0, 11, 8, 15, 11, 13,
+ 1, 7, 1, 13, 1, 18, 10, 7, 1, 38, 2, 1, 3, 26, 5, 1, 50, 2, 60, 52,
+ 60, 54, 15, 7, 13, 0, 18, 2, 11, 13, 2, 1, 2, 60, 55, 13, 1, 18, 10, 7,
+ 2, 32, 52, 28, 60, 56, 13, 0, 11, 2, 13, 1, 7, 1, 16, 26, 60, 57, 13, 0,
+ 11, 14, 13, 1, 13, 1, 18, 10, 16, 26, 50, 2, 60, 58, 60, 60, 15, 18, 13, 0,
+ 18, 2, 11, 19, 2, 3, 2, 60, 61, 13, 2, 52, 34, 60, 62, 13, 0, 11, 2, 15,
+ 0, 13, 0, 18, 2, 7, 1, 13, 2, 7, 1, 38, 2, 1, 3, 26, 60, 63, 13, 0,
+ 11, 20, 13, 4, 26, 50, 2, 60, 64, 60, 67, 13, 0, 18, 5, 11, 21, 31, 48, 7,
+ 13, 0, 18, 5, 11, 22, 31, 48, 7, 13, 0, 18, 2, 11, 21, 32, 52, 14, 60, 68,
+ 13, 0, 11, 2, 15, 23, 2, 1, 0, 26, 50,135, 60, 69, 13, 0, 18, 24, 11, 25,
+ 32, 52,122, 60, 70, 13, 0, 18, 5, 11, 21, 32, 48, 7, 13, 0, 18, 2, 11, 21,
+ 31, 52, 30, 60, 71, 13, 0, 11, 5, 13, 0, 18, 5, 13, 0, 18, 2, 42, 26, 60,
+ 72, 13, 0, 11, 2, 15, 23, 2, 1, 0, 26, 50, 70, 60, 73, 15, 4, 13, 0, 18,
+ 2, 2, 1, 1, 52, 55, 60, 74, 13, 0, 18, 5, 11, 21, 32, 52, 11, 13, 0, 11,
+ 5, 13, 0, 18, 2, 26, 50, 19, 60, 75, 13, 0, 11, 5, 13, 0, 18, 5, 11, 26,
+ 42, 13, 0, 18, 2, 42, 26, 60, 76, 13, 0, 11, 2, 15, 23, 2, 1, 0, 26, 50,
+ 2, 60, 77, 50, 2, 60, 78, 60, 80, 0, 0, 0, 0, 7, 0, 0, 0, 45, 0, 0,
+ 0, 5,115,101,108,102, 0, 0, 0, 0, 49, 0, 0, 0, 2,109, 0, 0, 0, 0,
+ 51, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 2,116, 0, 0, 0, 0, 60, 0,
+ 0, 0, 2, 98, 0, 0, 0, 0, 60, 0, 0, 0, 2,101, 0, 0, 0, 0, 60, 0,
+ 0, 0, 2,100, 0, 0, 0, 0, 27, 2, 0, 0, 0, 7,115,116,114,115,117, 98,
+ 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101,
+ 0, 2, 0, 0, 0, 2, 91, 0, 2, 0, 0, 0, 7,105,115,116,121,112,101, 0,
+ 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 2,109, 0, 2, 0, 0,
+ 0, 6,115,112,108,105,116, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0,
+ 0, 6, 37,115, 37,115, 42, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 7,
+ 99,111,110, 99, 97,116, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 2, 61,
+ 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 2, 98, 0, 2, 0, 0,
+ 0, 2,101, 0, 2, 0, 0, 0, 2,100, 0, 2, 0, 0, 0, 8,115,116,114,102,
+105,110,100, 0, 2, 0, 0, 0, 9, 37, 91, 40, 46, 45, 41, 37, 93, 0, 2, 0,
+ 0, 0, 4,100,105,109, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5,118,111,
+105,100, 0, 2, 0, 0, 0, 15, 99,114,101, 97,116,101, 95,118, 97,114,110, 97,
+109,101, 0, 2, 0, 0, 0, 5,107,105,110,100, 0, 2, 0, 0, 0, 4,118, 97,
+114, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 10, 99,104,101, 99,107,116,
+121,112,101, 0, 4, 0, 0, 0, 84, 0, 0, 0, 17, 64,100,101, 99,108, 97,114,
+ 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0,152, 4, 1, 60, 87, 15, 0,
+ 13, 0, 18, 2, 2, 1, 1, 48, 7, 13, 0, 18, 3, 11, 4, 31, 52, 22, 60, 88,
+ 13, 0, 11, 5, 13, 0, 18, 3, 26, 60, 89, 13, 0, 11, 3, 4, 0, 26, 50, 2,
+ 60, 90, 60, 93, 13, 0, 18, 6, 11, 4, 31, 48, 7, 13, 0, 18, 5, 11, 4, 31,
+ 52, 11, 60, 94, 15, 7, 11, 8, 2, 0, 1, 50, 2, 60, 95, 60, 98, 13, 0, 18,
+ 2, 11, 4, 31, 52, 13, 60, 99, 15, 9, 13, 0, 18, 2, 2, 0, 1, 50, 2, 60,
+100, 60,103, 13, 0, 18, 2, 11, 10, 32, 52, 9, 13, 0, 11, 2, 11, 11, 26, 50,
+ 22, 60,104, 13, 0, 18, 2, 11, 12, 32, 52, 9, 13, 0, 11, 2, 11, 13, 26, 50,
+ 2, 60,105, 60,114, 0, 0, 0, 0, 1, 0, 0, 0, 84, 0, 0, 0, 5,115,101,
+108,102, 0, 0, 0, 0, 14, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0,
+ 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 4,
+114,101,116, 0, 2, 0, 0, 0, 4,100,105,109, 0, 2, 0, 0, 0, 6,101,114,
+114,111,114, 0, 2, 0, 0, 0, 53, 35,105,110,118, 97,108,105,100, 32,112, 97,
+114, 97,109,101,116,101,114, 58, 32, 99, 97,110,110,111,116, 32,114,101,116,117,
+114,110, 32, 97,110, 32, 97,114,114, 97,121, 32,111,102, 32,118, 97,108,117,101,
+115, 0, 2, 0, 0, 0, 8,114,101,103,116,121,112,101, 0, 2, 0, 0, 0, 10,
+ 95,117,115,101,114,100, 97,116, 97, 0, 2, 0, 0, 0, 6,118,111,105,100, 42,
+ 0, 2, 0, 0, 0, 9, 95, 99,115,116,114,105,110,103, 0, 2, 0, 0, 0, 6,
+ 99,104, 97,114, 42, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0,
+ 0,117, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,
+117, 97, 0, 0, 0, 0,172, 6, 3, 60,118, 15, 2, 13, 1, 11, 3, 42, 2, 0,
+ 1, 60,119, 15, 2, 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0,
+ 1, 60,120, 15, 2, 13, 1, 11, 8, 42, 13, 0, 18, 9, 42, 11, 7, 42, 2, 0,
+ 1, 60,121, 15, 2, 13, 1, 11, 10, 42, 13, 0, 18, 11, 42, 11, 7, 42, 2, 0,
+ 1, 60,122, 15, 2, 13, 1, 11, 12, 42, 13, 0, 18, 13, 42, 11, 7, 42, 2, 0,
+ 1, 60,123, 15, 2, 13, 1, 11, 14, 42, 13, 0, 18, 15, 42, 11, 7, 42, 2, 0,
+ 1, 60,124, 15, 2, 13, 1, 11, 16, 42, 13, 0, 18, 17, 42, 11, 7, 42, 2, 0,
+ 1, 60,125, 15, 2, 13, 1, 11, 18, 42, 13, 0, 18, 19, 42, 11, 7, 42, 2, 0,
+ 1, 60,126, 15, 2, 13, 1, 11, 20, 42, 13, 2, 42, 2, 0, 1, 60,127, 0, 0,
+ 0, 0, 3, 0, 0, 0,117, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,117,
+ 0, 0, 0, 6,105,100,101,110,116, 0, 0, 0, 0,117, 0, 0, 0, 6, 99,108,
+111,115,101, 0, 0, 0, 0, 21, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2,
+ 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116,
+ 0, 2, 0, 0, 0, 13, 68,101, 99,108, 97,114, 97,116,105,111,110,123, 0, 2,
+ 0, 0, 0, 10, 32,109,111,100, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3, 39, 44,
+ 0, 2, 0, 0, 0, 10, 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0,
+ 5,116,121,112,101, 0, 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61, 32, 39,
+ 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101,
+ 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 10,
+ 32,100,105,109, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,100,105,109, 0, 2,
+ 0, 0, 0, 10, 32,100,101,102, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,100,
+101,102, 0, 2, 0, 0, 0, 10, 32,114,101,116, 32, 32, 61, 32, 39, 0, 2, 0,
+ 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 8,100,
+101, 99,108,116, 97,103, 0, 4, 0, 0, 0,130, 0, 0, 0, 17, 64,100,101, 99,
+108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 56, 10, 1, 60,
+131, 13, 0, 11, 1, 13, 0, 11, 2, 15, 3, 13, 0, 18, 4, 15, 5, 13, 0, 18,
+ 6, 11, 7, 2, 1, 2, 2, 2, 2, 27, 1, 27, 2, 5, 4, 60,132, 15, 8, 13,
+ 0, 18, 1, 13, 0, 18, 2, 2, 0, 2, 60,133, 0, 0, 0, 0, 1, 0, 0, 0,
+130, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 9, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 6,105,116,121,112,101, 0, 2, 0, 0, 0, 4,
+116, 97,103, 0, 2, 0, 0, 0, 7,116, 97,103,118, 97,114, 0, 2, 0, 0, 0,
+ 5,116,121,112,101, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2,
+ 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2,
+ 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0, 2, 0, 0, 0, 13,111,117,116,
+ 99,104,101, 99,107,116,121,112,101, 0, 4, 0, 0, 0,136, 0, 0, 0, 17, 64,
+100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 83,
+ 6, 2, 60,137, 4, 1, 60,138, 13, 0, 18, 4, 11, 5, 31, 52, 14, 60,139, 11,
+ 6, 23, 2, 60,140, 7, 0, 23, 3, 50, 25, 60,142, 13, 0, 18, 1, 23, 2, 60,
+143, 13, 0, 18, 2, 11, 5, 31, 46, 2, 7, 0, 23, 3, 60,144, 60,145, 11, 7,
+ 13, 1, 42, 11, 8, 42, 13, 2, 42, 11, 8, 42, 13, 3, 42, 11, 9, 42, 1, 4,
+ 60,146, 0, 0, 0, 0, 4, 0, 0, 0,136, 0, 0, 0, 5,115,101,108,102, 0,
+ 0, 0, 0,136, 0, 0, 0, 5,110, 97,114,103, 0, 0, 0, 0,137, 0, 0, 0,
+ 4,116, 97,103, 0, 0, 0, 0,137, 0, 0, 0, 4,100,101,102, 0, 0, 0, 0,
+ 10, 2, 0, 0, 0, 5,110, 97,114,103, 0, 2, 0, 0, 0, 4,116, 97,103, 0,
+ 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2,
+ 0, 0, 0, 4,100,105,109, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 16,116,
+111,108,117, 97, 95,116, 97,103, 95,116, 97, 98,108,101, 0, 2, 0, 0, 0, 14,
+116,111,108,117, 97, 95,105,115,116,121,112,101, 40, 0, 2, 0, 0, 0, 2, 44,
+ 0, 2, 0, 0, 0, 2, 41, 0, 2, 0, 0, 0, 8,100,101, 99,108, 97,114,101,
+ 0, 4, 0, 0, 0,149, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,
+111,110, 46,108,117, 97, 0, 0, 0, 1, 84, 15, 2, 60,150, 11, 2, 60,151, 13,
+ 0, 18, 1, 11, 2, 31, 52, 4, 11, 4, 23, 2, 60,152, 15, 5, 11, 6, 13, 0,
+ 18, 7, 13, 0, 18, 8, 13, 2, 2, 0, 4, 60,153, 13, 0, 18, 9, 11, 2, 31,
+ 48, 12, 15, 10, 13, 0, 18, 9, 2, 1, 1, 4, 0, 32, 52, 11, 60,154, 15, 5,
+ 11, 4, 2, 0, 1, 50, 2, 60,155, 60,156, 15, 5, 13, 0, 18, 11, 2, 0, 1,
+ 60,157, 13, 0, 18, 9, 11, 2, 31, 52, 76, 60,158, 15, 10, 13, 0, 18, 9, 2,
+ 1, 1, 4, 0, 31, 52, 17, 60,159, 15, 5, 11, 12, 13, 0, 18, 9, 11, 13, 2,
+ 0, 3, 50, 41, 60,161, 15, 5, 11, 14, 13, 0, 18, 7, 13, 0, 18, 8, 13, 2,
+ 11, 15, 60,162, 11, 16, 13, 0, 18, 9, 11, 17, 13, 0, 18, 8, 13, 2, 11, 18,
+ 2, 0, 11, 60,163, 50,161, 60,165, 15, 20, 13, 0, 18, 8, 2, 1, 1, 60,166,
+ 15, 5, 11, 21, 2, 0, 1, 60,167, 13, 3, 44, 48, 5, 13, 2, 11, 2, 32, 52,
+ 7, 15, 5, 11, 4, 2, 0, 1, 60,168, 15, 5, 11, 22, 13, 0, 18, 7, 13, 0,
+ 18, 8, 2, 0, 3, 60,169, 13, 3, 44, 52, 11, 60,170, 15, 5, 11, 4, 2, 0,
+ 1, 50, 2, 60,171, 60,172, 15, 5, 11, 23, 2, 0, 1, 60,173, 7, 0, 60,174,
+ 13, 0, 18, 24, 11, 2, 31, 52, 6, 13, 0, 18, 24, 23, 4, 60,175, 13, 3, 52,
+ 24, 60,176, 15, 5, 11, 25, 13, 3, 42, 11, 26, 13, 1, 11, 27, 13, 4, 11, 18,
+ 2, 0, 6, 50, 19, 60,178, 15, 5, 11, 28, 13, 1, 11, 27, 13, 4, 11, 18, 2,
+ 0, 5, 60,179, 5, 2, 60,180, 60,181, 0, 0, 0, 0, 7, 0, 0, 0,149, 0,
+ 0, 0, 5,115,101,108,102, 0, 0, 0, 0,149, 0, 0, 0, 5,110, 97,114,103,
+ 0, 0, 0, 0,150, 0, 0, 0, 4,112,116,114, 0, 0, 0, 0,165, 0, 0, 0,
+ 2,116, 0, 0, 0, 0,173, 0, 0, 0, 4,100,101,102, 0, 0, 0, 0,179, 0,
+ 0, 0, 0, 0, 0, 0,179, 0, 0, 0, 0, 0, 0, 0, 29, 2, 0, 0, 0, 5,
+110, 97,114,103, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 1, 0,
+ 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0, 0,
+ 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0,
+ 4,109,111,100, 0, 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 4,
+100,105,109, 0, 2, 0, 0, 0, 9,116,111,110,117,109, 98,101,114, 0, 2, 0,
+ 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 2, 91, 0, 2, 0, 0, 0, 3,
+ 93, 59, 0, 2, 0, 0, 0, 5, 32, 61, 32, 40, 0, 2, 0, 0, 0, 3, 42, 41,
+ 0, 2, 0, 0, 0, 8,109, 97,108,108,111, 99, 40, 0, 2, 0, 0, 0, 9, 42,
+115,105,122,101,111,102, 40, 0, 2, 0, 0, 0, 4, 41, 41, 59, 0, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0,
+ 0, 4, 32, 61, 32, 0, 2, 0, 0, 0, 3, 40, 40, 0, 2, 0, 0, 0, 3, 41,
+ 32, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,103,101,116, 0, 2, 0, 0, 0, 2, 40, 0, 2, 0, 0, 0, 2, 44, 0,
+ 2, 0, 0, 0, 19,116,111,108,117, 97, 95,103,101,116,117,115,101,114,116,121,
+112,101, 40, 0, 2, 0, 0, 0, 9,103,101,116, 97,114,114, 97,121, 0, 4, 0,
+ 0, 0,184, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,
+108,117, 97, 0, 0, 0, 1, 78, 13, 2, 60,185, 13, 0, 18, 2, 11, 3, 31, 51,
+ 1, 59, 60,186, 15, 4, 11, 5, 2, 0, 1, 60,187, 13, 0, 18, 6, 11, 3, 31,
+ 46, 2, 7, 0, 60,188, 15, 4, 11, 7, 13, 1, 11, 8, 13, 0, 18, 9, 11, 8,
+ 13, 0, 18, 2, 11, 8, 13, 2, 11, 10, 2, 0, 9, 60,189, 15, 4, 11, 11, 2,
+ 0, 1, 60,190, 15, 4, 11, 12, 2, 0, 1, 60,191, 15, 4, 11, 13, 2, 0, 1,
+ 60,192, 15, 4, 11, 14, 2, 0, 1, 60,193, 15, 4, 11, 15, 13, 1, 11, 16, 2,
+ 0, 3, 60,194, 15, 4, 11, 17, 13, 0, 18, 2, 42, 11, 18, 42, 2, 0, 1, 60,
+195, 15, 20, 13, 0, 18, 21, 2, 1, 1, 60,196, 11, 3, 60,197, 13, 0, 18, 22,
+ 11, 3, 31, 52, 4, 11, 23, 23, 4, 60,198, 15, 4, 11, 24, 13, 0, 18, 25, 11,
+ 26, 42, 2, 0, 2, 60,199, 13, 3, 44, 48, 5, 13, 4, 11, 3, 32, 52, 7, 15,
+ 4, 11, 23, 2, 0, 1, 60,200, 15, 4, 11, 27, 13, 0, 18, 28, 13, 0, 18, 21,
+ 2, 0, 3, 60,201, 13, 3, 44, 52, 11, 60,202, 15, 4, 11, 23, 2, 0, 1, 50,
+ 2, 60,203, 60,204, 15, 4, 11, 29, 2, 0, 1, 60,205, 7, 0, 60,206, 13, 0,
+ 18, 6, 11, 3, 31, 52, 6, 13, 0, 18, 6, 23, 5, 60,207, 13, 3, 52, 21, 60,
+208, 15, 4, 11, 30, 13, 3, 42, 11, 31, 42, 13, 5, 11, 32, 2, 0, 3, 50, 15,
+ 60,210, 15, 4, 11, 33, 13, 5, 11, 32, 2, 0, 3, 60,211, 60,212, 15, 4, 11,
+ 34, 2, 0, 1, 60,213, 15, 4, 11, 35, 2, 0, 1, 5, 4, 50, 2, 60,214, 60,
+215, 0, 0, 0, 0, 10, 0, 0, 0,184, 0, 0, 0, 5,115,101,108,102, 0, 0,
+ 0, 0,184, 0, 0, 0, 5,110, 97,114,103, 0, 0, 0, 0,187, 0, 0, 0, 4,
+100,101,102, 0, 0, 0, 0,195, 0, 0, 0, 2,116, 0, 0, 0, 0,196, 0, 0,
+ 0, 4,112,116,114, 0, 0, 0, 0,205, 0, 0, 0, 4,100,101,102, 0, 0, 0,
+ 0,213, 0, 0, 0, 0, 0, 0, 0,213, 0, 0, 0, 0, 0, 0, 0,213, 0, 0,
+ 0, 0, 0, 0, 0,213, 0, 0, 0, 0, 0, 0, 0, 36, 2, 0, 0, 0, 5,110,
+ 97,114,103, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 4,100,
+105,109, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116,
+ 0, 2, 0, 0, 0, 4, 32, 32,123, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2,
+ 0, 0, 0, 27, 32, 32, 32,105,102, 32, 40, 33,116,111,108,117, 97, 95, 97,114,
+114, 97,121,105,115,116,121,112,101, 40, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0,
+ 0, 0, 4,116, 97,103, 0, 2, 0, 0, 0, 3, 41, 41, 0, 2, 0, 0, 0, 16,
+ 32, 32, 32, 32,103,111,116,111, 32,101,114,114,111,114, 59, 0, 2, 0, 0, 0,
+ 9, 32, 32, 32,101,108,115,101, 10, 0, 2, 0, 0, 0, 5, 32, 32, 32,123, 0,
+ 2, 0, 0, 0, 11, 32, 32, 32, 32,105,110,116, 32,105, 59, 0, 2, 0, 0, 0,
+ 34, 32, 32, 32, 32,108,117, 97, 95, 79, 98,106,101, 99,116, 32,108,111, 32, 61,
+ 32,108,117, 97, 95,103,101,116,112, 97,114, 97,109, 40, 0, 2, 0, 0, 0, 3,
+ 41, 59, 0, 2, 0, 0, 0, 16, 32, 32, 32, 32,102,111,114, 40,105, 61, 48, 59,
+ 32,105, 60, 0, 2, 0, 0, 0, 6, 59,105, 43, 43, 41, 0, 2, 0, 0, 0, 2,
+116, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 5,
+116,121,112,101, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 2, 42,
+ 0, 2, 0, 0, 0, 4, 32, 32, 32, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0,
+ 2, 0, 0, 0, 7, 91,105, 93, 32, 61, 32, 0, 2, 0, 0, 0, 3, 40, 40, 0,
+ 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3, 41, 32, 0, 2, 0, 0,
+ 0, 15,116,111,108,117, 97, 95,103,101,116,102,105,101,108,100, 0, 2, 0, 0,
+ 0, 9, 40,108,111, 44,105, 43, 49, 44, 0, 2, 0, 0, 0, 4, 41, 41, 59, 0,
+ 2, 0, 0, 0, 31,116,111,108,117, 97, 95,103,101,116,102,105,101,108,100,117,
+115,101,114,116,121,112,101, 40,108,111, 44,105, 43, 49, 44, 0, 2, 0, 0, 0,
+ 5, 32, 32, 32,125, 0, 2, 0, 0, 0, 4, 32, 32,125, 0, 2, 0, 0, 0, 9,
+115,101,116, 97,114,114, 97,121, 0, 4, 0, 0, 0,218, 0, 0, 0, 17, 64,100,
+101, 99,108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 1, 42, 9,
+ 2, 60,219, 13, 0, 18, 2, 11, 3, 31, 51, 1, 23, 60,220, 15, 4, 11, 5, 2,
+ 0, 1, 60,221, 15, 4, 11, 6, 2, 0, 1, 60,222, 15, 4, 11, 7, 13, 1, 11,
+ 8, 2, 0, 3, 60,223, 15, 4, 11, 9, 13, 0, 18, 2, 42, 11, 10, 42, 2, 0,
+ 1, 60,224, 15, 12, 13, 0, 18, 13, 2, 1, 1, 60,225, 13, 2, 11, 14, 32, 52,
+ 23, 60,226, 15, 4, 11, 15, 13, 2, 42, 11, 16, 42, 13, 0, 18, 17, 11, 18, 2,
+ 0, 3, 50,175, 60,227, 13, 2, 52, 23, 60,228, 15, 4, 11, 15, 13, 2, 42, 11,
+ 19, 42, 13, 0, 18, 17, 11, 18, 2, 0, 3, 50,146, 60,230, 13, 0, 18, 20, 11,
+ 3, 32, 52,110, 60,231, 15, 4, 11, 21, 2, 0, 1, 60,232, 15, 4, 11, 22, 2,
+ 0, 1, 60,233, 15, 4, 11, 23, 13, 0, 18, 13, 11, 24, 13, 0, 18, 17, 11, 18,
+ 2, 0, 5, 60,234, 15, 4, 11, 25, 2, 0, 1, 60,235, 15, 4, 11, 26, 13, 0,
+ 18, 17, 11, 27, 13, 0, 18, 13, 11, 28, 2, 0, 5, 60,236, 15, 4, 11, 29, 2,
+ 0, 1, 60,237, 15, 4, 11, 30, 13, 0, 18, 31, 11, 32, 13, 0, 18, 31, 11, 8,
+ 2, 0, 5, 60,238, 15, 4, 11, 33, 2, 0, 1, 50, 23, 60,242, 15, 4, 11, 34,
+ 13, 0, 18, 17, 11, 35, 13, 0, 18, 31, 11, 8, 2, 0, 5, 60,243, 60,244, 60,
+245, 15, 4, 11, 36, 2, 0, 1, 5, 1, 50, 2, 60,246, 60,247, 0, 0, 0, 0,
+ 4, 0, 0, 0,218, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,218, 0, 0,
+ 0, 5,110, 97,114,103, 0, 0, 0, 0,224, 0, 0, 0, 2,116, 0, 0, 0, 0,
+245, 0, 0, 0, 0, 0, 0, 0, 37, 2, 0, 0, 0, 5,110, 97,114,103, 0, 2,
+ 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 4,100,105,109, 0, 2, 0,
+ 0, 0, 1, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0,
+ 4, 32, 32,123, 0, 2, 0, 0, 0, 10, 32, 32, 32,105,110,116, 32,105, 59, 0,
+ 2, 0, 0, 0, 33, 32, 32, 32,108,117, 97, 95, 79, 98,106,101, 99,116, 32,108,
+111, 32, 61, 32,108,117, 97, 95,103,101,116,112, 97,114, 97,109, 40, 0, 2, 0,
+ 0, 0, 3, 41, 59, 0, 2, 0, 0, 0, 15, 32, 32, 32,102,111,114, 40,105, 61,
+ 48, 59, 32,105, 60, 0, 2, 0, 0, 0, 6, 59,105, 43, 43, 41, 0, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0,
+ 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2,
+ 0, 0, 0, 19, 32, 32, 32,116,111,108,117, 97, 95,112,117,115,104,102,105,101,
+108,100, 0, 2, 0, 0, 0, 17, 40,108,111, 44,105, 43, 49, 44, 40,100,111,117,
+ 98,108,101, 41, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 6,
+ 91,105, 93, 41, 59, 0, 2, 0, 0, 0, 9, 40,108,111, 44,105, 43, 49, 44, 0,
+ 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 5, 32, 32, 32,123, 0, 2,
+ 0, 0, 0, 20, 35,105,102,100,101,102, 32, 95, 95, 99,112,108,117,115,112,108,
+117,115, 10, 0, 2, 0, 0, 0, 29, 32, 32, 32, 32,118,111,105,100, 42, 32,116,
+111,108,117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,110,101,119, 0, 2, 0,
+ 0, 0, 2, 40, 0, 2, 0, 0, 0, 7, 35,101,108,115,101, 10, 0, 2, 0, 0,
+ 0, 45, 32, 32, 32, 32,118,111,105,100, 42, 32,116,111,108,117, 97, 73, 95, 99,
+108,111,110,101, 32, 61, 32,116,111,108,117, 97, 95, 99,111,112,121, 40, 40,118,
+111,105,100, 42, 41, 38, 0, 2, 0, 0, 0, 12, 91,105, 93, 44,115,105,122,101,
+111,102, 40, 0, 2, 0, 0, 0, 4, 41, 41, 59, 0, 2, 0, 0, 0, 8, 35,101,
+110,100,105,102, 10, 0, 2, 0, 0, 0, 63, 32, 32, 32, 32,116,111,108,117, 97,
+ 95,112,117,115,104,102,105,101,108,100,117,115,101,114,116,121,112,101, 40,108,
+111, 44,105, 43, 49, 44,116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40,
+116,111,108,117, 97, 73, 95, 99,108,111,110,101, 44, 0, 2, 0, 0, 0, 4,116,
+ 97,103, 0, 2, 0, 0, 0, 3, 41, 44, 0, 2, 0, 0, 0, 5, 32, 32, 32,125,
+ 0, 2, 0, 0, 0, 42, 32, 32, 32,116,111,108,117, 97, 95,112,117,115,104,102,
+105,101,108,100,117,115,101,114,116,121,112,101, 40,108,111, 44,105, 43, 49, 44,
+ 40,118,111,105,100, 42, 41, 0, 2, 0, 0, 0, 5, 91,105, 93, 44, 0, 2, 0,
+ 0, 0, 4, 32, 32,125, 0, 2, 0, 0, 0, 10,102,114,101,101, 97,114,114, 97,
+121, 0, 4, 0, 0, 0,250, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,
+105,111,110, 46,108,117, 97, 0, 0, 0, 0, 49, 5, 1, 60,251, 13, 0, 18, 1,
+ 11, 2, 31, 48, 12, 15, 3, 13, 0, 18, 1, 2, 1, 1, 4, 0, 32, 52, 17, 60,
+252, 15, 4, 11, 5, 13, 0, 18, 6, 11, 7, 2, 0, 3, 50, 2, 60,253, 60,254,
+ 0, 0, 0, 0, 1, 0, 0, 0,250, 0, 0, 0, 5,115,101,108,102, 0, 0, 0,
+ 0, 8, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 4,100,105,109,
+ 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 9,116,111,110,117,109, 98,101,114,
+ 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 8, 32, 32,
+102,114,101,101, 40, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0,
+ 3, 41, 59, 0, 2, 0, 0, 0, 8,112, 97,115,115,112, 97,114, 0, 4, 0, 0,
+ 1, 1, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,
+117, 97, 0, 0, 0, 0, 79, 4, 1, 59, 1, 2, 13, 0, 18, 1, 11, 2, 32, 52,
+ 17, 59, 1, 3, 15, 3, 11, 4, 13, 0, 18, 5, 42, 2, 0, 1, 50, 44, 59, 1,
+ 4, 13, 0, 18, 6, 11, 4, 32, 52, 17, 59, 1, 5, 15, 3, 11, 2, 13, 0, 18,
+ 5, 42, 2, 0, 1, 50, 15, 59, 1, 7, 15, 3, 13, 0, 18, 5, 2, 0, 1, 59,
+ 1, 8, 59, 1, 9, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 5,115,101,
+108,102, 0, 0, 0, 0, 7, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0,
+ 0, 4,112,116,114, 0, 2, 0, 0, 0, 2, 38, 0, 2, 0, 0, 0, 7,111,117,
+116,112,117,116, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0, 0, 0, 5,110, 97,109,
+101, 0, 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 9,114,101,116,118,
+ 97,108,117,101, 0, 4, 0, 0, 1, 12, 0, 0, 0, 17, 64,100,101, 99,108, 97,
+114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0,133, 6, 1, 59, 1, 13,
+ 13, 0, 18, 1, 11, 2, 31, 52,112, 59, 1, 14, 15, 4, 13, 0, 18, 5, 2, 1,
+ 1, 59, 1, 15, 13, 1, 11, 6, 32, 52, 26, 59, 1, 16, 15, 7, 11, 8, 13, 1,
+ 42, 11, 9, 42, 13, 0, 18, 10, 42, 11, 11, 42, 2, 0, 1, 50, 60, 59, 1, 17,
+ 13, 1, 52, 26, 59, 1, 18, 15, 7, 11, 8, 13, 1, 42, 11, 12, 42, 13, 0, 18,
+ 10, 42, 11, 11, 42, 2, 0, 1, 50, 27, 59, 1, 20, 15, 7, 11, 13, 13, 0, 18,
+ 10, 42, 11, 14, 42, 13, 0, 18, 15, 11, 11, 2, 0, 3, 59, 1, 21, 5, 1, 50,
+ 3, 59, 1, 22, 59, 1, 23, 0, 0, 0, 0, 3, 0, 0, 1, 12, 0, 0, 0, 5,
+115,101,108,102, 0, 0, 0, 1, 14, 0, 0, 0, 2,116, 0, 0, 0, 1, 21, 0,
+ 0, 0, 0, 0, 0, 0, 16, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0,
+ 0, 4,114,101,116, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2,116, 0, 2,
+ 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 5,116,121,112,
+101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0, 0, 7,111,
+117,116,112,117,116, 0, 2, 0, 0, 0, 14, 32, 32, 32,116,111,108,117, 97, 95,
+112,117,115,104, 0, 2, 0, 0, 0, 10, 40, 40,100,111,117, 98,108,101, 41, 0,
+ 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2, 0,
+ 0, 0, 2, 40, 0, 2, 0, 0, 0, 30, 32, 32, 32,116,111,108,117, 97, 95,112,
+117,115,104,117,115,101,114,116,121,112,101, 40, 40,118,111,105,100, 42, 41, 0,
+ 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 4,116, 97,103, 0, 2, 0, 0, 0,
+ 13, 95, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 4, 0, 0, 1, 26, 0,
+ 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0,
+ 0, 0, 0,136, 8, 1, 59, 1, 27, 13, 0, 18, 1, 48, 7, 13, 0, 18, 1, 11,
+ 2, 31, 52, 60, 59, 1, 28, 15, 4, 13, 0, 18, 1, 11, 5, 2, 1, 2, 59, 1,
+ 29, 13, 0, 11, 1, 13, 1, 7, 1, 16, 26, 59, 1, 30, 13, 0, 11, 6, 15, 7,
+ 13, 1, 7, 2, 16, 46, 5, 13, 1, 7, 1, 16, 11, 8, 11, 2, 2, 1, 3, 26,
+ 5, 1, 50, 3, 59, 1, 31, 59, 1, 32, 13, 0, 11, 9, 15, 10, 26, 59, 1, 33,
+ 15, 11, 13, 0, 15, 12, 2, 0, 2, 59, 1, 34, 13, 0, 20, 13, 2, 0, 1, 59,
+ 1, 35, 13, 0, 20, 14, 2, 0, 1, 59, 1, 36, 13, 0, 1, 1, 59, 1, 37, 0,
+ 0, 0, 0, 3, 0, 0, 1, 26, 0, 0, 0, 2,116, 0, 0, 0, 1, 28, 0, 0,
+ 0, 2,110, 0, 0, 0, 1, 30, 0, 0, 0, 0, 0, 0, 0, 15, 2, 0, 0, 0,
+ 2,116, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 1, 0, 2,
+ 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0, 0,
+ 0, 2, 64, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 5,
+103,115,117, 98, 0, 2, 0, 0, 0, 7, 37, 91, 46, 45, 37, 93, 0, 2, 0, 0,
+ 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 17, 99,108, 97,115,115, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,
+103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0,
+ 0, 10, 99,104,101, 99,107,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,104,101,
+ 99,107,116,121,112,101, 0, 2, 0, 0, 0, 12, 68,101, 99,108, 97,114, 97,116,
+105,111,110, 0, 4, 0, 0, 1, 42, 0, 0, 0, 17, 64,100,101, 99,108, 97,114,
+ 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 3,164, 20, 2, 59, 1, 44, 15,
+ 2, 13, 0, 11, 3, 11, 4, 2, 1, 3, 23, 0, 59, 1, 46, 13, 1, 11, 5, 32,
+ 52, 44, 59, 1, 48, 13, 0, 11, 6, 32, 46, 5, 13, 0, 11, 7, 32, 52, 22, 59,
+ 1, 49, 15, 8, 22, 2, 11, 9, 11, 7, 11, 1, 13, 1, 30, 1, 3, 2, 1, 50,
+ 3, 59, 1, 50, 50, 3, 59, 1, 51, 59, 1, 54, 15, 11, 13, 0, 11, 12, 2, 1,
+ 2, 59, 1, 55, 13, 2, 18, 13, 7, 2, 32, 52,126, 59, 1, 56, 13, 1, 11, 14,
+ 32, 52, 15, 59, 1, 57, 15, 15, 11, 16, 13, 0, 42, 2, 0, 1, 50, 3, 59, 1,
+ 58, 59, 1, 59, 15, 11, 13, 2, 7, 1, 16, 11, 18, 2, 1, 2, 59, 1, 60, 15,
+ 8, 22, 6, 59, 1, 61, 11, 19, 13, 2, 7, 2, 16, 11, 20, 59, 1, 62, 11, 21,
+ 11, 22, 59, 1, 63, 11, 23, 11, 9, 59, 1, 64, 13, 3, 13, 3, 18, 13, 16, 11,
+ 24, 59, 1, 65, 15, 25, 13, 3, 7, 1, 13, 3, 18, 13, 7, 1, 38, 2, 1, 3,
+ 11, 1, 59, 1, 66, 13, 1, 30, 5, 59, 1, 67, 3, 4, 1, 5, 1, 50, 3, 59,
+ 1, 68, 59, 1, 71, 15, 11, 13, 0, 11, 26, 2, 1, 2, 23, 2, 59, 1, 72, 13,
+ 2, 18, 13, 7, 2, 32, 52,126, 59, 1, 73, 13, 1, 11, 14, 32, 52, 15, 59, 1,
+ 74, 15, 15, 11, 16, 13, 0, 42, 2, 0, 1, 50, 3, 59, 1, 75, 59, 1, 76, 15,
+ 11, 13, 2, 7, 1, 16, 11, 18, 2, 1, 2, 59, 1, 77, 15, 8, 22, 6, 59, 1,
+ 78, 11, 19, 13, 2, 7, 2, 16, 11, 20, 59, 1, 79, 11, 21, 11, 22, 59, 1, 80,
+ 11, 21, 11, 9, 59, 1, 81, 13, 3, 13, 3, 18, 13, 16, 11, 24, 59, 1, 82, 15,
+ 25, 13, 3, 7, 1, 13, 3, 18, 13, 7, 1, 38, 2, 1, 3, 11, 1, 59, 1, 83,
+ 13, 1, 30, 5, 59, 1, 84, 3, 4, 1, 5, 1, 50, 3, 59, 1, 85, 59, 1, 88,
+ 15, 11, 13, 0, 11, 23, 2, 1, 2, 23, 2, 59, 1, 89, 13, 2, 18, 13, 7, 2,
+ 32, 52, 91, 59, 1, 90, 15, 11, 13, 2, 7, 1, 16, 11, 18, 2, 1, 2, 59, 1,
+ 91, 15, 8, 22, 5, 59, 1, 92, 11, 19, 13, 2, 7, 2, 16, 11, 20, 59, 1, 93,
+ 11, 23, 11, 9, 59, 1, 94, 13, 3, 13, 3, 18, 13, 16, 11, 24, 59, 1, 95, 15,
+ 25, 13, 3, 7, 1, 13, 3, 18, 13, 7, 1, 38, 2, 1, 3, 11, 1, 59, 1, 96,
+ 13, 1, 30, 4, 59, 1, 97, 3, 4, 1, 5, 1, 50, 3, 59, 1, 98, 59, 1,101,
+ 15, 2, 13, 0, 11, 28, 11, 29, 2, 1, 3, 59, 1,102, 15, 11, 13, 3, 11, 30,
+ 2, 1, 2, 23, 2, 59, 1,103, 13, 2, 18, 31, 7, 2, 32, 52,113, 59, 1,104,
+ 13, 2, 7, 2, 15, 32, 13, 2, 7, 2, 16, 11, 33, 11, 30, 2, 1, 3, 26, 59,
+ 1,105, 15, 11, 13, 2, 7, 1, 16, 11, 18, 2, 1, 2, 59, 1,106, 15, 8, 22,
+ 5, 59, 1,107, 11, 19, 13, 2, 7, 2, 16, 11, 20, 59, 1,108, 11, 21, 11, 9,
+ 59, 1,109, 13, 4, 13, 4, 18, 31, 16, 11, 24, 59, 1,110, 15, 25, 13, 4, 7,
+ 1, 13, 4, 18, 31, 7, 1, 38, 2, 1, 3, 11, 1, 59, 1,111, 13, 1, 30, 4,
+ 59, 1,112, 3, 5, 1, 5, 1, 50, 3, 59, 1,113, 59, 1,115, 13, 1, 11, 5,
+ 32, 52,129, 59, 1,117, 15, 11, 13, 0, 11, 18, 2, 1, 2, 23, 2, 59, 1,118,
+ 4, 0, 59, 1,119, 15, 35, 13, 2, 13, 2, 18, 31, 16, 2, 1, 1, 52, 6, 11,
+ 6, 23, 4, 50, 21, 13, 2, 13, 2, 18, 31, 16, 23, 4, 13, 2, 11, 31, 13, 2,
+ 18, 31, 7, 1, 38, 26, 59, 1,120, 15, 8, 22, 4, 59, 1,121, 11, 19, 13, 4,
+ 11, 9, 59, 1,122, 13, 2, 13, 2, 18, 31, 16, 11, 24, 59, 1,123, 15, 25, 13,
+ 2, 7, 1, 13, 2, 18, 31, 7, 1, 38, 2, 1, 3, 11, 1, 59, 1,124, 13, 1,
+ 30, 3, 59, 1,125, 3, 5, 1, 5, 1, 50,130, 59, 1,130, 15, 11, 13, 0, 11,
+ 18, 2, 1, 2, 23, 2, 59, 1,131, 13, 2, 13, 2, 18, 31, 16, 59, 1,132, 4,
+ 1, 59, 1,133, 13, 2, 18, 31, 7, 1, 35, 52, 38, 59, 1,134, 13, 2, 13, 2,
+ 18, 31, 7, 1, 38, 16, 23, 5, 59, 1,135, 15, 25, 13, 2, 7, 1, 13, 2, 18,
+ 31, 7, 2, 38, 2, 1, 3, 23, 6, 50, 3, 59, 1,136, 59, 1,137, 15, 8, 22,
+ 4, 59, 1,138, 11, 19, 13, 4, 11, 9, 59, 1,139, 13, 5, 11, 24, 59, 1,140,
+ 13, 6, 11, 1, 59, 1,141, 13, 1, 30, 3, 59, 1,142, 3, 7, 1, 5, 3, 59,
+ 1,143, 59, 1,145, 0, 0, 0, 0, 20, 0, 0, 1, 42, 0, 0, 0, 2,115, 0,
+ 0, 0, 1, 42, 0, 0, 0, 5,107,105,110,100, 0, 0, 0, 1, 54, 0, 0, 0,
+ 2,116, 0, 0, 0, 1, 59, 0, 0, 0, 2,109, 0, 0, 0, 1, 67, 0, 0, 0,
+ 0, 0, 0, 1, 76, 0, 0, 0, 2,109, 0, 0, 0, 1, 84, 0, 0, 0, 0, 0,
+ 0, 1, 90, 0, 0, 0, 2,109, 0, 0, 0, 1, 97, 0, 0, 0, 0, 0, 0, 1,
+101, 0, 0, 0, 3,115, 49, 0, 0, 0, 1,105, 0, 0, 0, 2,109, 0, 0, 0,
+ 1,112, 0, 0, 0, 0, 0, 0, 1,118, 0, 0, 0, 2,118, 0, 0, 0, 1,125,
+ 0, 0, 0, 0, 0, 0, 1,131, 0, 0, 0, 2,118, 0, 0, 0, 1,132, 0, 0,
+ 0, 3,116,112, 0, 0, 0, 1,132, 0, 0, 0, 3,109,100, 0, 0, 0, 1,142,
+ 0, 0, 0, 0, 0, 0, 1,142, 0, 0, 0, 0, 0, 0, 1,142, 0, 0, 0, 0,
+ 0, 0, 0, 38, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 5,107,105,110,100,
+ 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 8, 37,115, 42, 61,
+ 37,115, 42, 0, 2, 0, 0, 0, 2, 61, 0, 2, 0, 0, 0, 4,118, 97,114, 0,
+ 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5,118,111,105,100, 0, 2, 0, 0, 0,
+ 13, 95, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 2, 0, 0, 0, 5,116,
+121,112,101, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,
+116, 0, 2, 0, 0, 0, 7, 37, 42, 37,115, 42, 38, 0, 2, 0, 0, 0, 2,110,
+ 0, 2, 0, 0, 0, 5,102,117,110, 99, 0, 2, 0, 0, 0, 6,101,114,114,111,
+114, 0, 2, 0, 0, 0, 32, 35,105,110,118, 97,108,105,100, 32,102,117,110, 99,
+116,105,111,110, 32,114,101,116,117,114,110, 32,116,121,112,101, 58, 32, 0, 2,
+ 0, 0, 0, 2,109, 0, 2, 0, 0, 0, 6, 37,115, 37,115, 42, 0, 2, 0, 0,
+ 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0,
+ 2, 42, 0, 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 2, 38, 0, 2,
+ 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 7, 99,111,110, 99, 97,116, 0,
+ 2, 0, 0, 0, 8, 37, 42, 37,115, 42, 37, 42, 0, 2, 0, 0, 0, 3,115, 49,
+ 0, 2, 0, 0, 0, 7, 40, 37, 98, 91, 93, 41, 0, 4, 0, 0, 1,101, 0, 0,
+ 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0, 0,
+ 0, 0, 17, 5, 1, 59, 1,101, 15, 1, 13, 0, 11, 2, 11, 3, 3, 1, 3, 0,
+ 0, 0, 0, 1, 0, 0, 1,101, 0, 0, 0, 2,110, 0, 0, 0, 0, 4, 2, 0,
+ 0, 0, 2,110, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 3,
+ 37, 42, 0, 2, 0, 0, 0, 2, 1, 0, 2, 0, 0, 0, 3, 37, 42, 0, 2, 0,
+ 0, 0, 2,110, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 2,
+ 1, 0, 2, 0, 0, 0, 2,118, 0, 2, 0, 0, 0, 7,105,115,116,121,112,101,
+ 0, 2, 0, 0, 0, 3,116,112, 0, 2, 0, 0, 0, 3,109,100, 0,
+};
+
+/* container.lo */
+static char B4[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 15, 64, 99,111,110,116, 97,
+105,110,101,114, 46,108,117, 97, 0, 0, 0, 0,162, 5, 0, 60, 17, 60, 18, 22,
+ 2, 60, 19, 11, 1, 4, 0, 11, 2, 60, 20, 15, 3, 30, 1, 60, 21, 25, 0, 60,
+ 22, 15, 4, 15, 0, 15, 5, 2, 0, 2, 60, 25, 15, 0, 11, 6, 11, 7, 26, 60,
+ 36, 15, 0, 11, 8, 11, 9, 26, 60, 48, 11, 11, 25, 10, 60, 58, 11, 13, 25, 12,
+ 60, 63, 11, 15, 25, 14, 60, 68, 11, 17, 25, 16, 60, 73, 11, 19, 25, 18, 60, 78,
+ 11, 21, 25, 20, 60, 83, 11, 23, 25, 22, 60, 88, 15, 24, 11, 25, 11, 26, 26, 60,
+ 95, 15, 24, 11, 27, 11, 28, 26, 60,101, 15, 24, 11, 29, 11, 30, 26, 60,110, 15,
+ 24, 11, 31, 11, 32, 26, 60,129, 15, 24, 11, 33, 11, 34, 26, 60,147, 15, 24, 11,
+ 35, 11, 36, 26, 59, 1, 49, 15, 24, 11, 37, 11, 38, 26, 0, 0, 0, 0, 0, 0,
+ 0, 0, 39, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,
+101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2, 0, 0, 0, 6, 95, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,
+101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,
+111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,
+103, 0, 4, 0, 0, 0, 25, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,
+114, 46,108,117, 97, 0, 0, 0, 0, 59, 4, 1, 60, 26, 15, 0, 13, 0, 2, 0,
+ 1, 60, 27, 7, 1, 50, 23, 60, 29, 13, 0, 13, 1, 16, 20, 3, 2, 0, 1, 60,
+ 30, 13, 1, 7, 1, 37, 23, 1, 60, 31, 60, 28, 13, 0, 13, 1, 16, 54, 32, 60,
+ 32, 15, 4, 2, 0, 0, 60, 33, 0, 0, 0, 0, 2, 0, 0, 0, 25, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 27, 0, 0, 0, 2,105, 0, 0, 0, 0, 5,
+ 2, 0, 0, 0, 5,112,117,115,104, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0,
+ 2, 0, 0, 0, 4,112,111,112, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,
+101, 0, 4, 0, 0, 0, 36, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,
+114, 46,108,117, 97, 0, 0, 0, 0, 59, 4, 1, 60, 37, 15, 0, 13, 0, 2, 0,
+ 1, 60, 38, 7, 1, 50, 23, 60, 40, 13, 0, 13, 1, 16, 20, 3, 2, 0, 1, 60,
+ 41, 13, 1, 7, 1, 37, 23, 1, 60, 42, 60, 39, 13, 0, 13, 1, 16, 54, 32, 60,
+ 43, 15, 4, 2, 0, 0, 60, 44, 0, 0, 0, 0, 2, 0, 0, 0, 36, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 38, 0, 0, 0, 2,105, 0, 0, 0, 0, 5,
+ 2, 0, 0, 0, 5,112,117,115,104, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0,
+ 2, 0, 0, 0, 4,112,111,112, 0, 2, 0, 0, 0, 11, 95, 67,111,110,116, 97,
+105,110,101,114, 0, 4, 0, 0, 0, 48, 0, 0, 0, 15, 64, 99,111,110,116, 97,
+105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 64, 6, 1, 60, 49, 13, 0, 11,
+ 1, 15, 2, 26, 60, 50, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 51, 13, 0, 11,
+ 5, 7, 0, 26, 60, 52, 13, 0, 11, 6, 22, 1, 11, 5, 7, 0, 30, 0, 26, 60,
+ 53, 13, 0, 11, 7, 22, 0, 26, 60, 54, 13, 0, 1, 1, 60, 55, 0, 0, 0, 0,
+ 1, 0, 0, 0, 48, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 8, 2, 0,
+ 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2,
+ 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 0, 2,
+ 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,116, 97,103, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 9,116,121,
+112,101,100,101,102,115, 0, 2, 0, 0, 0, 7,108,110, 97,109,101,115, 0, 2,
+ 0, 0, 0, 5,112,117,115,104, 0, 4, 0, 0, 0, 58, 0, 0, 0, 15, 64, 99,
+111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 14, 4, 1, 60,
+ 59, 15, 1, 11, 2, 13, 0, 26, 60, 60, 0, 0, 0, 0, 1, 0, 0, 0, 58, 0,
+ 0, 0, 2,116, 0, 0, 0, 0, 3, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0,
+ 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0,
+ 5, 99,117,114,114, 0, 2, 0, 0, 0, 4,112,111,112, 0, 4, 0, 0, 0, 63,
+ 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0,
+ 0, 0, 18, 3, 0, 60, 64, 15, 0, 11, 1, 15, 0, 18, 1, 18, 2, 26, 60, 65,
+ 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,
+111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2,
+ 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0, 7, 97,112,112,101,
+110,100, 0, 4, 0, 0, 0, 68, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,
+101,114, 46,108,117, 97, 0, 0, 0, 0, 18, 4, 1, 60, 69, 15, 1, 18, 2, 20,
+ 3, 13, 0, 3, 1, 2, 60, 70, 0, 0, 0, 0, 1, 0, 0, 0, 68, 0, 0, 0,
+ 2,116, 0, 0, 0, 0, 4, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 15, 99,
+108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,
+117,114,114, 0, 2, 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0, 0,
+ 14, 97,112,112,101,110,100,116,121,112,101,100,101,102, 0, 4, 0, 0, 0, 73,
+ 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0,
+ 0, 0, 18, 4, 1, 60, 74, 15, 1, 18, 2, 20, 3, 13, 0, 3, 1, 2, 60, 75,
+ 0, 0, 0, 0, 1, 0, 0, 0, 73, 0, 0, 0, 2,116, 0, 0, 0, 0, 4, 2,
+ 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116,
+ 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2, 0, 0, 0,
+ 14, 97,112,112,101,110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 12,
+102,105,110,100,116,121,112,101,100,101,102, 0, 4, 0, 0, 0, 78, 0, 0, 0,
+ 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 18,
+ 4, 1, 60, 79, 15, 1, 18, 2, 20, 3, 13, 0, 3, 1, 2, 60, 80, 0, 0, 0,
+ 0, 1, 0, 0, 0, 78, 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0, 4, 2,
+ 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,
+111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2,
+ 0, 0, 0, 12,102,105,110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0,
+ 10,105,115,116,121,112,101,100,101,102, 0, 4, 0, 0, 0, 83, 0, 0, 0, 15,
+ 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 18, 4,
+ 1, 60, 84, 15, 1, 18, 2, 20, 3, 13, 0, 3, 1, 2, 60, 85, 0, 0, 0, 0,
+ 1, 0, 0, 0, 83, 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0, 4, 2, 0,
+ 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,
+110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2, 0,
+ 0, 0, 10,105,115,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 15, 99,108,
+ 97,115,115, 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 7, 97,112,
+112,101,110,100, 0, 4, 0, 0, 0, 88, 0, 0, 0, 15, 64, 99,111,110,116, 97,
+105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 39, 6, 2, 60, 89, 13, 0, 11,
+ 2, 13, 0, 18, 2, 7, 1, 37, 26, 60, 90, 13, 0, 13, 0, 18, 2, 13, 1, 26,
+ 60, 91, 13, 1, 11, 3, 13, 0, 26, 60, 92, 0, 0, 0, 0, 2, 0, 0, 0, 88,
+ 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 88, 0, 0, 0, 2,116, 0, 0,
+ 0, 0, 4, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2,
+ 0, 0, 0, 14, 97,112,112,101,110,100,116,121,112,101,100,101,102, 0, 4, 0,
+ 0, 0, 95, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117,
+ 97, 0, 0, 0, 0, 38, 6, 2, 60, 96, 13, 0, 18, 2, 11, 3, 13, 0, 18, 2,
+ 18, 3, 7, 1, 37, 26, 60, 97, 13, 0, 18, 2, 13, 0, 18, 2, 18, 3, 13, 1,
+ 26, 60, 98, 0, 0, 0, 0, 2, 0, 0, 0, 95, 0, 0, 0, 5,115,101,108,102,
+ 0, 0, 0, 0, 95, 0, 0, 0, 2,116, 0, 0, 0, 0, 4, 2, 0, 0, 0, 2,
+116, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 9,116,121,112,
+101,100,101,102,115, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 9,111,118,
+101,114,108,111, 97,100, 0, 4, 0, 0, 0,101, 0, 0, 0, 15, 64, 99,111,110,
+116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 67, 6, 2, 60,102, 13,
+ 0, 18, 2, 13, 1, 16, 44, 52, 13, 60,103, 13, 0, 18, 2, 13, 1, 7, 0, 26,
+ 50, 21, 60,105, 13, 0, 18, 2, 13, 1, 13, 0, 18, 2, 13, 1, 16, 7, 1, 37,
+ 26, 60,106, 60,107, 15, 3, 11, 4, 13, 0, 18, 2, 13, 1, 16, 3, 2, 2, 60,
+108, 0, 0, 0, 0, 2, 0, 0, 0,101, 0, 0, 0, 5,115,101,108,102, 0, 0,
+ 0, 0,101, 0, 0, 0, 6,108,110, 97,109,101, 0, 0, 0, 0, 5, 2, 0, 0,
+ 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0,
+ 0, 0, 7,108,110, 97,109,101,115, 0, 2, 0, 0, 0, 7,102,111,114,109, 97,
+116, 0, 2, 0, 0, 0, 5, 37, 48, 50,100, 0, 2, 0, 0, 0, 12,102,105,110,
+100,116,121,112,101,100,101,102, 0, 4, 0, 0, 0,110, 0, 0, 0, 15, 64, 99,
+111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0,142, 10, 2, 60,
+111, 13, 0, 50,117, 60,113, 13, 2, 18, 3, 52, 97, 60,114, 7, 1, 50, 76, 60,
+116, 13, 2, 18, 3, 13, 3, 16, 18, 5, 13, 1, 32, 52, 47, 60,117, 13, 2, 18,
+ 3, 13, 3, 16, 18, 8, 13, 2, 18, 3, 13, 3, 16, 18, 0, 60,118, 15, 11, 13,
+ 5, 2, 2, 1, 60,119, 13, 6, 11, 12, 42, 13, 4, 42, 13, 7, 1, 8, 5, 4,
+ 50, 2, 60,120, 60,121, 13, 3, 7, 1, 37, 23, 3, 60,122, 60,115, 13, 2, 18,
+ 3, 13, 3, 16, 54, 87, 5, 1, 50, 2, 60,123, 60,124, 13, 2, 18, 13, 23, 2,
+ 60,125, 60,112, 13, 2, 54,123, 60,126, 11, 14, 13, 1, 1, 3, 60,127, 0, 0,
+ 0, 0, 13, 0, 0, 0,110, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,110,
+ 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0,111, 0, 0, 0, 4,101,110,118,
+ 0, 0, 0, 0,114, 0, 0, 0, 2,105, 0, 0, 0, 0,117, 0, 0, 0, 5,109,
+111,100, 49, 0, 0, 0, 0,117, 0, 0, 0, 6,116,121,112,101, 49, 0, 0, 0,
+ 0,118, 0, 0, 0, 5,109,111,100, 50, 0, 0, 0, 0,118, 0, 0, 0, 6,116,
+121,112,101, 50, 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0,
+ 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0,
+122, 0, 0, 0, 0, 0, 0, 0, 15, 2, 0, 0, 0, 5,116,121,112,101, 0, 2,
+ 0, 0, 0, 4,101,110,118, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0,
+ 0, 0, 9,116,121,112,101,100,101,102,115, 0, 2, 0, 0, 0, 2,105, 0, 2,
+ 0, 0, 0, 6,117,116,121,112,101, 0, 2, 0, 0, 0, 5,109,111,100, 49, 0,
+ 2, 0, 0, 0, 6,116,121,112,101, 49, 0, 2, 0, 0, 0, 4,109,111,100, 0,
+ 2, 0, 0, 0, 5,109,111,100, 50, 0, 2, 0, 0, 0, 6,116,121,112,101, 50,
+ 0, 2, 0, 0, 0, 12,102,105,110,100,116,121,112,101,100,101,102, 0, 2, 0,
+ 0, 0, 2, 32, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0,
+ 0, 1, 0, 2, 0, 0, 0, 10,105,115,116,121,112,101,100,101,102, 0, 4, 0,
+ 0, 0,129, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117,
+ 97, 0, 0, 0, 0,101, 6, 2, 60,130, 13, 0, 50, 78, 60,132, 13, 2, 18, 3,
+ 52, 58, 60,133, 7, 1, 50, 37, 60,135, 13, 2, 18, 3, 13, 3, 16, 18, 5, 13,
+ 1, 32, 52, 8, 60,136, 7, 1, 1, 4, 50, 2, 60,137, 60,138, 13, 3, 7, 1,
+ 37, 23, 3, 60,139, 60,134, 13, 2, 18, 3, 13, 3, 16, 54, 48, 5, 1, 50, 2,
+ 60,140, 60,141, 13, 2, 18, 6, 23, 2, 60,142, 60,131, 13, 2, 54, 84, 60,143,
+ 4, 0, 1, 3, 60,144, 0, 0, 0, 0, 5, 0, 0, 0,129, 0, 0, 0, 5,115,
+101,108,102, 0, 0, 0, 0,129, 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0,
+130, 0, 0, 0, 4,101,110,118, 0, 0, 0, 0,133, 0, 0, 0, 2,105, 0, 0,
+ 0, 0,139, 0, 0, 0, 0, 0, 0, 0, 7, 2, 0, 0, 0, 5,116,121,112,101,
+ 0, 2, 0, 0, 0, 4,101,110,118, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 9,116,121,112,101,100,101,102,115, 0, 2, 0, 0, 0, 2,105,
+ 0, 2, 0, 0, 0, 6,117,116,121,112,101, 0, 2, 0, 0, 0, 7,112, 97,114,
+101,110,116, 0, 2, 0, 0, 0, 8,100,111,112, 97,114,115,101, 0, 4, 0, 0,
+ 0,147, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97,
+ 0, 0, 0, 4, 55, 13, 2, 60,151, 15, 5, 13, 1, 11, 6, 2, 4, 2, 60,152,
+ 13, 2, 52, 42, 60,153, 15, 8, 13, 1, 13, 2, 13, 3, 2, 1, 3, 25, 7, 60,
+154, 15, 9, 13, 4, 13, 5, 2, 0, 2, 60,155, 15, 8, 13, 1, 13, 3, 7, 1,
+ 37, 3, 6, 2, 50, 2, 60,156, 5, 4, 60,157, 60,161, 15, 5, 13, 1, 11, 10,
+ 2, 3, 2, 60,162, 13, 2, 52, 40, 60,163, 15, 8, 13, 1, 13, 2, 13, 3, 2,
+ 1, 3, 25, 7, 60,164, 15, 11, 13, 4, 2, 0, 1, 60,165, 15, 8, 13, 1, 13,
+ 3, 7, 1, 37, 3, 5, 2, 50, 2, 60,166, 5, 3, 60,167, 60,171, 15, 5, 13,
+ 1, 11, 12, 2, 3, 2, 60,172, 13, 2, 52, 40, 60,173, 15, 8, 13, 1, 13, 2,
+ 13, 3, 2, 1, 3, 25, 7, 60,174, 15, 13, 13, 4, 2, 0, 1, 60,175, 15, 8,
+ 13, 1, 13, 3, 7, 1, 37, 3, 5, 2, 50, 2, 60,176, 5, 3, 60,177, 60,180,
+ 15, 5, 13, 1, 11, 14, 2, 4, 2, 60,181, 13, 2, 52, 52, 60,182, 15, 8, 13,
+ 1, 13, 2, 13, 3, 2, 1, 3, 25, 7, 60,183, 15, 13, 13, 4, 2, 0, 1, 60,
+184, 15, 15, 11, 16, 13, 5, 42, 2, 0, 1, 60,185, 15, 8, 13, 1, 13, 3, 7,
+ 1, 37, 3, 6, 2, 50, 2, 60,186, 5, 4, 60,187, 60,191, 15, 5, 13, 1, 11,
+ 21, 2, 6, 2, 60,192, 13, 2, 52, 46, 60,193, 15, 8, 13, 1, 13, 2, 13, 3,
+ 2, 1, 3, 25, 7, 60,194, 15, 22, 13, 4, 13, 5, 13, 6, 13, 7, 2, 0, 4,
+ 60,195, 15, 8, 13, 1, 13, 3, 7, 1, 37, 3, 8, 2, 50, 2, 60,196, 5, 6,
+ 60,197, 60,201, 15, 5, 13, 1, 11, 23, 2, 5, 2, 60,202, 13, 2, 44, 52, 23,
+ 60,204, 15, 5, 13, 1, 11, 24, 2, 5, 2, 23, 6, 23, 5, 23, 4, 23, 3, 23,
+ 2, 50, 2, 60,205, 60,206, 13, 2, 52, 44, 60,207, 15, 8, 13, 1, 13, 2, 13,
+ 3, 2, 1, 3, 25, 7, 60,208, 15, 25, 13, 4, 13, 5, 13, 6, 2, 0, 3, 60,
+209, 15, 8, 13, 1, 13, 3, 7, 1, 37, 3, 7, 2, 50, 2, 60,210, 5, 5, 60,
+211, 60,215, 15, 5, 13, 1, 11, 26, 2, 5, 2, 60,216, 13, 2, 44, 52, 23, 60,
+218, 15, 5, 13, 1, 11, 27, 2, 5, 2, 23, 6, 23, 5, 23, 4, 23, 3, 23, 2,
+ 50, 2, 60,219, 60,220, 13, 2, 52, 44, 60,221, 15, 8, 13, 1, 13, 2, 13, 3,
+ 2, 1, 3, 25, 7, 60,222, 15, 25, 13, 4, 13, 5, 13, 6, 2, 0, 3, 60,223,
+ 15, 8, 13, 1, 13, 3, 7, 1, 37, 3, 7, 2, 50, 2, 60,224, 5, 5, 60,225,
+ 60,229, 15, 5, 13, 1, 11, 29, 2, 5, 2, 60,230, 13, 2, 44, 52, 59, 60,231,
+ 15, 5, 13, 1, 11, 30, 2, 5, 2, 23, 6, 23, 5, 23, 4, 23, 3, 23, 2, 60,
+232, 13, 2, 44, 52, 27, 60,233, 11, 31, 23, 5, 60,234, 15, 5, 13, 1, 11, 32,
+ 2, 4, 2, 23, 4, 23, 6, 23, 3, 23, 2, 50, 2, 60,235, 50, 2, 60,236, 60,
+237, 13, 2, 52, 80, 60,238, 13, 5, 11, 31, 31, 52, 25, 60,239, 4, 1, 60,240,
+ 15, 5, 13, 5, 11, 33, 2, 3, 2, 23, 5, 23, 8, 23, 7, 5, 2, 50, 2, 60,
+241, 60,242, 15, 8, 13, 1, 13, 2, 13, 3, 2, 1, 3, 25, 7, 60,243, 15, 34,
+ 13, 4, 13, 5, 13, 6, 2, 0, 3, 60,244, 15, 8, 13, 1, 13, 3, 7, 1, 37,
+ 3, 7, 2, 50, 2, 60,245, 5, 5, 60,246, 60,250, 15, 5, 13, 1, 11, 36, 2,
+ 3, 2, 60,251, 13, 2, 52, 40, 60,252, 15, 8, 13, 1, 13, 2, 13, 3, 2, 1,
+ 3, 25, 7, 60,253, 15, 15, 13, 4, 2, 0, 1, 60,254, 15, 8, 13, 1, 13, 3,
+ 7, 1, 37, 3, 5, 2, 50, 2, 60,255, 5, 3, 59, 1, 0, 59, 1, 4, 15, 5,
+ 13, 1, 11, 37, 2, 3, 2, 59, 1, 5, 13, 2, 52, 43, 59, 1, 6, 15, 8, 13,
+ 1, 13, 2, 13, 3, 2, 1, 3, 25, 7, 59, 1, 7, 15, 38, 13, 4, 2, 0, 1,
+ 59, 1, 8, 15, 8, 13, 1, 13, 3, 7, 1, 37, 3, 5, 2, 50, 3, 59, 1, 9,
+ 5, 3, 59, 1, 10, 59, 1, 14, 15, 5, 13, 1, 11, 39, 2, 3, 2, 59, 1, 15,
+ 13, 2, 52, 43, 59, 1, 16, 15, 8, 13, 1, 13, 2, 13, 3, 2, 1, 3, 25, 7,
+ 59, 1, 17, 15, 40, 13, 4, 2, 0, 1, 59, 1, 18, 15, 8, 13, 1, 13, 3, 7,
+ 1, 37, 3, 5, 2, 50, 3, 59, 1, 19, 5, 3, 59, 1, 20, 59, 1, 24, 15, 5,
+ 13, 1, 11, 42, 2, 3, 2, 59, 1, 25, 13, 2, 52, 36, 59, 1, 26, 15, 43, 15,
+ 8, 13, 4, 7, 2, 9, 2, 2, 1, 3, 2, 0, 1, 59, 1, 27, 15, 8, 13, 1,
+ 13, 3, 7, 1, 37, 3, 5, 2, 50, 3, 59, 1, 28, 5, 3, 59, 1, 29, 59, 1,
+ 33, 15, 5, 13, 1, 11, 45, 2, 3, 2, 59, 1, 34, 13, 2, 52, 27, 59, 1, 35,
+ 15, 46, 13, 4, 2, 0, 1, 59, 1, 36, 15, 8, 13, 1, 13, 3, 7, 1, 37, 3,
+ 5, 2, 50, 3, 59, 1, 37, 5, 3, 59, 1, 38, 59, 1, 41, 15, 47, 13, 1, 11,
+ 48, 11, 31, 2, 1, 3, 11, 31, 31, 52, 19, 59, 1, 42, 13, 1, 25, 7, 59, 1,
+ 43, 15, 49, 11, 50, 2, 0, 1, 50, 10, 59, 1, 45, 11, 31, 1, 2, 59, 1, 46,
+ 59, 1, 47, 0, 0, 0, 0,106, 0, 0, 0,147, 0, 0, 0, 5,115,101,108,102,
+ 0, 0, 0, 0,147, 0, 0, 0, 2,115, 0, 0, 0, 0,151, 0, 0, 0, 2, 98,
+ 0, 0, 0, 0,151, 0, 0, 0, 2,101, 0, 0, 0, 0,151, 0, 0, 0, 5,110,
+ 97,109,101, 0, 0, 0, 0,151, 0, 0, 0, 5, 98,111,100,121, 0, 0, 0, 0,
+156, 0, 0, 0, 0, 0, 0, 0,156, 0, 0, 0, 0, 0, 0, 0,156, 0, 0, 0,
+ 0, 0, 0, 0,156, 0, 0, 0, 0, 0, 0, 0,161, 0, 0, 0, 2, 98, 0, 0,
+ 0, 0,161, 0, 0, 0, 2,101, 0, 0, 0, 0,161, 0, 0, 0, 5,110, 97,109,
+101, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0,
+ 0,166, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, 0, 2, 98, 0, 0, 0, 0,171,
+ 0, 0, 0, 2,101, 0, 0, 0, 0,171, 0, 0, 0, 5, 98,111,100,121, 0, 0,
+ 0, 0,176, 0, 0, 0, 0, 0, 0, 0,176, 0, 0, 0, 0, 0, 0, 0,176, 0,
+ 0, 0, 0, 0, 0, 0,180, 0, 0, 0, 2, 98, 0, 0, 0, 0,180, 0, 0, 0,
+ 2,101, 0, 0, 0, 0,180, 0, 0, 0, 5, 98,111,100,121, 0, 0, 0, 0,180,
+ 0, 0, 0, 5,110, 97,109,101, 0, 0, 0, 0,186, 0, 0, 0, 0, 0, 0, 0,
+186, 0, 0, 0, 0, 0, 0, 0,186, 0, 0, 0, 0, 0, 0, 0,186, 0, 0, 0,
+ 0, 0, 0, 0,191, 0, 0, 0, 2, 98, 0, 0, 0, 0,191, 0, 0, 0, 2,101,
+ 0, 0, 0, 0,191, 0, 0, 0, 5,100,101, 99,108, 0, 0, 0, 0,191, 0, 0,
+ 0, 5,107,105,110,100, 0, 0, 0, 0,191, 0, 0, 0, 4, 97,114,103, 0, 0,
+ 0, 0,191, 0, 0, 0, 6, 99,111,110,115,116, 0, 0, 0, 0,196, 0, 0, 0,
+ 0, 0, 0, 0,196, 0, 0, 0, 0, 0, 0, 0,196, 0, 0, 0, 0, 0, 0, 0,
+196, 0, 0, 0, 0, 0, 0, 0,196, 0, 0, 0, 0, 0, 0, 0,196, 0, 0, 0,
+ 0, 0, 0, 0,201, 0, 0, 0, 2, 98, 0, 0, 0, 0,201, 0, 0, 0, 2,101,
+ 0, 0, 0, 0,201, 0, 0, 0, 5,100,101, 99,108, 0, 0, 0, 0,201, 0, 0,
+ 0, 4, 97,114,103, 0, 0, 0, 0,201, 0, 0, 0, 6, 99,111,110,115,116, 0,
+ 0, 0, 0,210, 0, 0, 0, 0, 0, 0, 0,210, 0, 0, 0, 0, 0, 0, 0,210,
+ 0, 0, 0, 0, 0, 0, 0,210, 0, 0, 0, 0, 0, 0, 0,210, 0, 0, 0, 0,
+ 0, 0, 0,215, 0, 0, 0, 2, 98, 0, 0, 0, 0,215, 0, 0, 0, 2,101, 0,
+ 0, 0, 0,215, 0, 0, 0, 5,100,101, 99,108, 0, 0, 0, 0,215, 0, 0, 0,
+ 4, 97,114,103, 0, 0, 0, 0,215, 0, 0, 0, 6, 99,111,110,115,116, 0, 0,
+ 0, 0,224, 0, 0, 0, 0, 0, 0, 0,224, 0, 0, 0, 0, 0, 0, 0,224, 0,
+ 0, 0, 0, 0, 0, 0,224, 0, 0, 0, 0, 0, 0, 0,224, 0, 0, 0, 0, 0,
+ 0, 0,229, 0, 0, 0, 2, 98, 0, 0, 0, 0,229, 0, 0, 0, 2,101, 0, 0,
+ 0, 0,229, 0, 0, 0, 5,110, 97,109,101, 0, 0, 0, 0,229, 0, 0, 0, 5,
+ 98, 97,115,101, 0, 0, 0, 0,229, 0, 0, 0, 5, 98,111,100,121, 0, 0, 0,
+ 0,239, 0, 0, 0, 2, 98, 0, 0, 0, 0,239, 0, 0, 0, 2,101, 0, 0, 0,
+ 0,240, 0, 0, 0, 0, 0, 0, 0,240, 0, 0, 0, 0, 0, 0, 0,245, 0, 0,
+ 0, 0, 0, 0, 0,245, 0, 0, 0, 0, 0, 0, 0,245, 0, 0, 0, 0, 0, 0,
+ 0,245, 0, 0, 0, 0, 0, 0, 0,245, 0, 0, 0, 0, 0, 0, 0,250, 0, 0,
+ 0, 2, 98, 0, 0, 0, 0,250, 0, 0, 0, 2,101, 0, 0, 0, 0,250, 0, 0,
+ 0, 6,116,121,112,101,115, 0, 0, 0, 0,255, 0, 0, 0, 0, 0, 0, 0,255,
+ 0, 0, 0, 0, 0, 0, 0,255, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 2,
+ 98, 0, 0, 0, 1, 4, 0, 0, 0, 2,101, 0, 0, 0, 1, 4, 0, 0, 0, 5,
+100,101, 99,108, 0, 0, 0, 1, 9, 0, 0, 0, 0, 0, 0, 1, 9, 0, 0, 0,
+ 0, 0, 0, 1, 9, 0, 0, 0, 0, 0, 0, 1, 14, 0, 0, 0, 2, 98, 0, 0,
+ 0, 1, 14, 0, 0, 0, 2,101, 0, 0, 0, 1, 14, 0, 0, 0, 5,100,101, 99,
+108, 0, 0, 0, 1, 19, 0, 0, 0, 0, 0, 0, 1, 19, 0, 0, 0, 0, 0, 0,
+ 1, 19, 0, 0, 0, 0, 0, 0, 1, 24, 0, 0, 0, 2, 98, 0, 0, 0, 1, 24,
+ 0, 0, 0, 2,101, 0, 0, 0, 1, 24, 0, 0, 0, 5, 99,111,100,101, 0, 0,
+ 0, 1, 28, 0, 0, 0, 0, 0, 0, 1, 28, 0, 0, 0, 0, 0, 0, 1, 28, 0,
+ 0, 0, 0, 0, 0, 1, 33, 0, 0, 0, 2, 98, 0, 0, 0, 1, 33, 0, 0, 0,
+ 2,101, 0, 0, 0, 1, 33, 0, 0, 0, 5,108,105,110,101, 0, 0, 0, 1, 37,
+ 0, 0, 0, 0, 0, 0, 1, 37, 0, 0, 0, 0, 0, 0, 1, 37, 0, 0, 0, 0,
+ 0, 0, 0, 51, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 2, 98, 0, 2, 0,
+ 0, 0, 2,101, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 5,
+ 98,111,100,121, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0,
+ 0, 0, 41, 94, 37,115, 42,109,111,100,117,108,101, 37,115, 37,115, 42, 40, 91,
+ 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40, 37, 98,123,125, 41,
+ 37,115, 42, 0, 2, 0, 0, 0, 11, 95, 99,117,114,114, 95, 99,111,100,101, 0,
+ 2, 0, 0, 0, 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0, 7, 77,111,100,
+117,108,101, 0, 2, 0, 0, 0, 34, 94, 37,115, 42, 35,100,101,102,105,110,101,
+ 37,115, 37,115, 42, 40, 91, 94, 37,115, 93, 42, 41, 91, 94, 10, 93, 42, 10, 37,
+115, 42, 0, 2, 0, 0, 0, 7, 68,101,102,105,110,101, 0, 2, 0, 0, 0, 28,
+ 94, 37,115, 42,101,110,117,109, 91, 94,123, 93, 42, 40, 37, 98,123,125, 41, 37,
+115, 42, 59, 63, 37,115, 42, 0, 2, 0, 0, 0, 10, 69,110,117,109,101,114, 97,
+116,101, 0, 2, 0, 0, 0, 55, 94, 37,115, 42,116,121,112,101,100,101,102, 37,
+115, 37,115, 42,101,110,117,109, 91, 94,123, 93, 42, 40, 37, 98,123,125, 41, 37,
+115, 42, 40, 91, 37,119, 95, 93, 91, 94, 37,115, 93, 42, 41, 37,115, 42, 59, 37,
+115, 42, 0, 2, 0, 0, 0, 8, 84,121,112,101,100,101,102, 0, 2, 0, 0, 0,
+ 5,105,110,116, 32, 0, 2, 0, 0, 0, 5,100,101, 99,108, 0, 2, 0, 0, 0,
+ 5,107,105,110,100, 0, 2, 0, 0, 0, 4, 97,114,103, 0, 2, 0, 0, 0, 6,
+ 99,111,110,115,116, 0, 2, 0, 0, 0, 78, 94, 37,115, 42, 40, 91, 95, 37,119,
+ 93, 91, 95, 37,119, 37,115, 37, 42, 38, 93, 42,111,112,101,114, 97,116,111,114,
+ 41, 37,115, 42, 40, 91, 94, 37,115, 93, 91, 94, 37,115, 93, 42, 41, 37,115, 42,
+ 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63,
+ 41, 37,115, 42, 59, 37,115, 42, 0, 2, 0, 0, 0, 9, 79,112,101,114, 97,116,
+111,114, 0, 2, 0, 0, 0, 71, 94, 37,115, 42, 40, 91,126, 95, 37,119, 93, 91,
+ 95, 64, 37,119, 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93, 41, 37,115, 42,
+ 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63,
+ 41, 37,115, 42, 61, 63, 37,115, 42, 48, 63, 37,115, 42, 59, 37,115, 42, 0, 2,
+ 0, 0, 0, 43, 94, 37,115, 42, 40, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37,
+ 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41, 37,
+115, 42, 59, 37,115, 42, 0, 2, 0, 0, 0, 9, 70,117,110, 99,116,105,111,110,
+ 0, 2, 0, 0, 0, 64, 94, 37,115, 42, 40, 91,126, 95, 37,119, 93, 91, 95, 64,
+ 37,119, 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37,
+ 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41, 37,
+115, 42, 37, 98,123,125, 37,115, 42, 0, 2, 0, 0, 0, 46, 94, 37,115, 42, 40,
+ 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99,
+ 63,111, 63,110, 63,115, 63,116, 63, 41, 37,115, 42, 37, 98,123,125, 37,115, 42,
+ 0, 2, 0, 0, 0, 5, 98, 97,115,101, 0, 2, 0, 0, 0, 49, 94, 37,115, 42,
+ 99,108, 97,115,115, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 93, 42,
+ 41, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42,
+ 59, 37,115, 42, 0, 2, 0, 0, 0, 50, 94, 37,115, 42,115,116,114,117, 99,116,
+ 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40,
+ 46, 45, 41, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42, 59, 37,115, 42, 0,
+ 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 66, 94, 37,115, 42,116,121,112,101,100,
+101,102, 37,115, 37,115, 42,115,116,114,117, 99,116, 37,115, 37,115, 42, 91, 95,
+ 37,119, 93, 42, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42, 40, 91, 95, 37,
+119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 59, 37,115, 42, 0, 2, 0, 0,
+ 0, 17, 46, 45, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 36, 0, 2,
+ 0, 0, 0, 6, 67,108, 97,115,115, 0, 2, 0, 0, 0, 6,116,121,112,101,115,
+ 0, 2, 0, 0, 0, 28, 94, 37,115, 42,116,121,112,101,100,101,102, 37,115, 37,
+115, 42, 40, 46, 45, 41, 37,115, 42, 59, 37,115, 42, 0, 2, 0, 0, 0, 40, 94,
+ 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 64, 37,115, 37,119, 37,100, 37, 42,
+ 38, 93, 42, 91, 95, 37,119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42, 0, 2,
+ 0, 0, 0, 9, 86, 97,114,105, 97, 98,108,101, 0, 2, 0, 0, 0, 43, 94, 37,
+115, 42, 40, 91, 95, 37,119, 93, 91, 93, 91, 95, 64, 37,115, 37,119, 37,100, 37,
+ 42, 38, 93, 42, 91, 93, 95, 37,119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42,
+ 0, 2, 0, 0, 0, 6, 65,114,114, 97,121, 0, 2, 0, 0, 0, 5, 99,111,100,
+101, 0, 2, 0, 0, 0, 11, 94, 37,115, 42, 40, 37, 98, 1, 2, 41, 0, 2, 0,
+ 0, 0, 5, 67,111,100,101, 0, 2, 0, 0, 0, 5,108,105,110,101, 0, 2, 0,
+ 0, 0, 12, 94, 37,115, 42, 37, 36, 40, 46, 45, 10, 41, 0, 2, 0, 0, 0, 9,
+ 86,101,114, 98, 97,116,105,109, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2,
+ 0, 0, 0, 6, 37,115, 37,115, 42, 0, 2, 0, 0, 0, 6,101,114,114,111,114,
+ 0, 2, 0, 0, 0, 13, 35,112, 97,114,115,101, 32,101,114,114,111,114, 0, 2,
+ 0, 0, 0, 6,112, 97,114,115,101, 0, 4, 0, 0, 1, 49, 0, 0, 0, 15, 64,
+ 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 35, 5, 2,
+ 50, 17, 59, 1, 51, 13, 0, 20, 3, 13, 1, 2, 1, 2, 23, 1, 59, 1, 52, 59,
+ 1, 50, 13, 1, 11, 1, 31, 54, 27, 59, 1, 53, 0, 0, 0, 0, 2, 0, 0, 1,
+ 49, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 1, 49, 0, 0, 0, 2,115, 0,
+ 0, 0, 0, 4, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0,
+ 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,100,111,112, 97,114,115,101, 0,
+
+};
+
+/* package.lo */
+static char B5[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 13, 64,112, 97, 99,107, 97,
+103,101, 46,108,117, 97, 0, 0, 0, 0,102, 5, 0, 60, 20, 22, 2, 60, 21, 11,
+ 1, 15, 2, 11, 3, 60, 22, 11, 4, 30, 1, 60, 23, 25, 0, 60, 24, 15, 5, 15,
+ 0, 15, 6, 2, 0, 2, 60, 27, 15, 0, 11, 7, 11, 8, 26, 60, 36, 15, 0, 11,
+ 9, 11, 10, 26, 60, 60, 15, 0, 11, 11, 11, 12, 26, 60,100, 15, 0, 11, 13, 11,
+ 14, 26, 60,119, 15, 0, 11, 15, 11, 16, 26, 60,132, 15, 0, 11, 17, 11, 18, 26,
+ 60,146, 11, 20, 25, 19, 60,155, 11, 22, 25, 21, 0, 0, 0, 0, 0, 0, 0, 0,
+ 23, 2, 0, 0, 0, 13, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 0, 2,
+ 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115,
+ 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 8,112, 97, 99,107, 97,103,101, 0, 2, 0, 0, 0, 7,115,101,
+116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0,
+ 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 27, 0, 0, 0, 13,
+ 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0, 0, 61, 6, 1, 60,
+ 28, 15, 0, 11, 1, 13, 0, 18, 3, 42, 2, 0, 1, 60, 29, 7, 1, 50, 27, 60,
+ 31, 13, 0, 13, 1, 16, 20, 0, 11, 5, 11, 5, 2, 0, 3, 60, 32, 13, 1, 7,
+ 1, 37, 23, 1, 60, 33, 60, 30, 13, 0, 13, 1, 16, 54, 36, 60, 34, 0, 0, 0,
+ 0, 2, 0, 0, 0, 27, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 29, 0,
+ 0, 0, 2,105, 0, 0, 0, 0, 6, 2, 0, 0, 0, 6,112,114,105,110,116, 0,
+ 2, 0, 0, 0, 10, 80, 97, 99,107, 97,103,101, 58, 32, 0, 2, 0, 0, 0, 5,
+115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 2,
+105, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 11,112,114,101,112,114,111, 99,
+101,115,115, 0, 4, 0, 0, 0, 36, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,
+101, 46,108,117, 97, 0, 0, 0, 1, 83, 7, 1, 60, 38, 13, 0, 11, 1, 11, 2,
+ 13, 0, 18, 1, 42, 26, 60, 40, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 4,
+ 11, 5, 2, 1, 3, 26, 60, 41, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 6,
+ 11, 7, 2, 1, 3, 26, 60, 42, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 8,
+ 11, 9, 2, 1, 3, 26, 60, 43, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 10,
+ 11, 5, 2, 1, 3, 26, 60, 44, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 7,
+ 11, 6, 2, 1, 3, 26, 60, 45, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 9,
+ 11, 8, 2, 1, 3, 26, 60, 46, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 11,
+ 11, 12, 2, 1, 3, 26, 60, 47, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 13,
+ 11, 14, 2, 1, 3, 26, 60, 48, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 15,
+ 11, 14, 2, 1, 3, 26, 60, 49, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 16,
+ 11, 14, 2, 1, 3, 26, 60, 50, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 17,
+ 11, 5, 2, 1, 3, 26, 60, 51, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 18,
+ 11, 19, 2, 1, 3, 26, 60, 52, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 18,
+ 11, 19, 2, 1, 3, 26, 60, 53, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 20,
+ 11, 21, 2, 1, 3, 26, 60, 55, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 22,
+ 11, 7, 2, 1, 3, 26, 60, 56, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 23,
+ 11, 9, 2, 1, 3, 26, 60, 57, 0, 0, 0, 0, 1, 0, 0, 0, 36, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 24, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 5, 99,111,100,101, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0,
+ 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 10, 40, 47, 47, 91, 94, 10, 93, 42,
+ 41, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 4, 47, 37, 42, 0, 2, 0, 0,
+ 0, 2, 1, 0, 2, 0, 0, 0, 4, 37, 42, 47, 0, 2, 0, 0, 0, 2, 2, 0,
+ 2, 0, 0, 0, 5, 37, 98, 1, 2, 0, 2, 0, 0, 0, 8, 37,115, 42, 64, 37,
+115, 42, 0, 2, 0, 0, 0, 2, 64, 0, 2, 0, 0, 0, 14, 37,115, 63,105,110,
+108,105,110,101, 40, 37,115, 41, 0, 2, 0, 0, 0, 3, 37, 49, 0, 2, 0, 0,
+ 0, 14, 37,115, 63,101,120,116,101,114,110, 40, 37,115, 41, 0, 2, 0, 0, 0,
+ 15, 37,115, 63,118,105,114,116,117, 97,108, 40, 37,115, 41, 0, 2, 0, 0, 0,
+ 8,112,117, 98,108,105, 99, 58, 0, 2, 0, 0, 0, 18, 40, 91, 94, 37,119, 95,
+ 93, 41,118,111,105,100, 37,115, 42, 37, 42, 0, 2, 0, 0, 0, 13, 37, 49, 95,
+117,115,101,114,100, 97,116, 97, 32, 0, 2, 0, 0, 0, 18, 40, 91, 94, 37,119,
+ 95, 93, 41, 99,104, 97,114, 37,115, 42, 37, 42, 0, 2, 0, 0, 0, 12, 37, 49,
+ 95, 99,115,116,114,105,110,103, 32, 0, 2, 0, 0, 0, 5, 37, 36, 37, 91, 0,
+ 2, 0, 0, 0, 5, 37, 36, 37, 93, 0, 2, 0, 0, 0, 9,112,114,101, 97,109,
+ 98,108,101, 0, 4, 0, 0, 0, 60, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,
+101, 46,108,117, 97, 0, 0, 0, 1, 79, 5, 1, 60, 61, 15, 0, 11, 1, 2, 0,
+ 1, 60, 62, 15, 0, 11, 2, 13, 0, 18, 4, 42, 11, 5, 42, 2, 0, 1, 60, 63,
+ 15, 0, 11, 6, 15, 7, 42, 11, 8, 42, 15, 9, 2, 1, 0, 42, 11, 10, 42, 2,
+ 0, 1, 60, 64, 15, 0, 11, 11, 2, 0, 1, 60, 66, 15, 12, 18, 13, 44, 52, 54,
+ 60, 67, 15, 0, 11, 14, 2, 0, 1, 60, 68, 15, 0, 11, 15, 13, 0, 18, 4, 42,
+ 11, 16, 42, 2, 0, 1, 60, 69, 15, 0, 11, 17, 13, 0, 18, 4, 42, 11, 18, 42,
+ 2, 0, 1, 60, 70, 15, 0, 11, 5, 2, 0, 1, 50, 2, 60, 71, 60, 73, 15, 12,
+ 18, 19, 52, 46, 60, 74, 15, 0, 11, 20, 2, 0, 1, 60, 75, 15, 0, 11, 21, 2,
+ 0, 1, 60, 76, 15, 0, 11, 22, 13, 0, 18, 4, 42, 11, 23, 42, 2, 0, 1, 60,
+ 77, 15, 0, 11, 24, 2, 0, 1, 50, 2, 60, 78, 60, 79, 15, 0, 11, 25, 2, 0,
+ 1, 60, 80, 7, 1, 50, 23, 60, 82, 13, 0, 13, 1, 16, 20, 27, 2, 0, 1, 60,
+ 83, 13, 1, 7, 1, 37, 23, 1, 60, 84, 60, 81, 13, 0, 13, 1, 16, 54, 32, 60,
+ 85, 15, 0, 11, 5, 2, 0, 1, 60, 86, 15, 0, 11, 28, 2, 0, 1, 60, 87, 13,
+ 0, 20, 29, 2, 0, 1, 60, 88, 15, 0, 11, 5, 2, 0, 1, 60, 89, 15, 0, 11,
+ 30, 2, 0, 1, 60, 90, 15, 0, 11, 31, 2, 0, 1, 60, 91, 15, 0, 11, 32, 2,
+ 0, 1, 60, 92, 15, 33, 15, 34, 11, 35, 2, 0, 2, 60, 93, 15, 33, 15, 36, 11,
+ 37, 2, 0, 2, 60, 94, 15, 38, 11, 39, 2, 0, 1, 60, 95, 15, 38, 11, 5, 2,
+ 0, 1, 60, 96, 0, 0, 0, 0, 2, 0, 0, 0, 60, 0, 0, 0, 5,115,101,108,
+102, 0, 0, 0, 0, 80, 0, 0, 0, 2,105, 0, 0, 0, 0, 40, 2, 0, 0, 0,
+ 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 4, 47, 42, 10, 0, 2, 0, 0,
+ 0, 17, 42, 42, 32, 76,117, 97, 32, 98,105,110,100,105,110,103, 58, 32, 0, 2,
+ 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2,
+ 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 31, 42, 42, 32, 71,101,110,101,114, 97,
+116,101,100, 32, 97,117,116,111,109, 97,116,105, 99, 97,108,108,121, 32, 98,121,
+ 32, 0, 2, 0, 0, 0, 14, 84, 79, 76, 85, 65, 95, 86, 69, 82, 83, 73, 79, 78,
+ 0, 2, 0, 0, 0, 5, 32,111,110, 32, 0, 2, 0, 0, 0, 5,100, 97,116,101,
+ 0, 2, 0, 0, 0, 3, 46, 10, 0, 2, 0, 0, 0, 5, 42, 47, 10, 10, 0, 2,
+ 0, 0, 0, 6,102,108, 97,103,115, 0, 2, 0, 0, 0, 2,104, 0, 2, 0, 0,
+ 0, 24, 47, 42, 32, 69,120,112,111,114,116,101,100, 32,102,117,110, 99,116,105,
+111,110, 32, 42, 47, 0, 2, 0, 0, 0, 12,105,110,116, 32, 32,116,111,108,117,
+ 97, 95, 0, 2, 0, 0, 0, 14, 95,111,112,101,110, 32, 40,118,111,105,100, 41,
+ 59, 0, 2, 0, 0, 0, 12,118,111,105,100, 32,116,111,108,117, 97, 95, 0, 2,
+ 0, 0, 0, 15, 95, 99,108,111,115,101, 32, 40,118,111,105,100, 41, 59, 0, 2,
+ 0, 0, 0, 2, 97, 0, 2, 0, 0, 0, 45, 47, 42, 32, 65,117,116,111,109, 97,
+116,105, 99, 32,105,110,105,116,105, 97,108,105,122, 97,116,105,111,110, 32,102,
+111,114, 32, 67, 43, 43, 32, 99,111,100,101, 32, 42, 47, 10, 0, 2, 0, 0, 0,
+ 20, 35,105,102,100,101,102, 32, 95, 95, 99,112,108,117,115,112,108,117,115, 10,
+ 0, 2, 0, 0, 0, 26,115,116, 97,116,105, 99, 32,105,110,116, 32,100,117,109,
+109,121, 32, 61, 32,116,111,108,117, 97, 95, 0, 2, 0, 0, 0, 10, 95,111,112,
+101,110, 32, 40, 41, 59, 0, 2, 0, 0, 0, 9, 35,101,110,100,105,102, 10, 10,
+ 0, 2, 0, 0, 0, 21, 35,105,110, 99,108,117,100,101, 32, 34,116,111,108,117,
+ 97, 46,104, 34, 10, 10, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 9,112,
+114,101, 97,109, 98,108,101, 0, 2, 0, 0, 0, 20, 47, 42, 32,116, 97,103, 32,
+118, 97,114,105, 97, 98,108,101,115, 32, 42, 47, 0, 2, 0, 0, 0, 8,100,101,
+ 99,108,116, 97,103, 0, 2, 0, 0, 0, 51, 47, 42, 32,102,117,110, 99,116,105,
+111,110, 32,116,111, 32,114,101,103,105,115,116,101,114, 32,116,121,112,101, 32,
+ 97,110,100, 32,105,110,105,116,105, 97,108,105,122,101, 32,116, 97,103, 32, 42,
+ 47, 0, 2, 0, 0, 0, 35,115,116, 97,116,105, 99, 32,118,111,105,100, 32,116,
+111,108,117, 97, 73, 95,105,110,105,116, 95,116, 97,103, 32, 40,118,111,105,100,
+ 41, 0, 2, 0, 0, 0, 2,123, 0, 2, 0, 0, 0, 8,102,111,114,101, 97, 99,
+104, 0, 2, 0, 0, 0, 10, 95,117,115,101,114,116,121,112,101, 0, 4, 0, 0,
+ 0, 92, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0,
+ 0, 0, 16, 6, 2, 60, 92, 15, 2, 11, 3, 13, 1, 11, 4, 2, 0, 3, 0, 0,
+ 0, 0, 2, 0, 0, 0, 92, 0, 0, 0, 2,110, 0, 0, 0, 0, 92, 0, 0, 0,
+ 2,118, 0, 0, 0, 0, 5, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,118,
+ 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 18, 32,116,
+111,108,117, 97, 95,117,115,101,114,116,121,112,101, 40, 34, 0, 2, 0, 0, 0,
+ 4, 34, 41, 59, 0, 2, 0, 0, 0, 10, 95,116, 97,103,110, 97,109,101,115, 0,
+ 4, 0, 0, 0, 93, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117,
+ 97, 0, 0, 0, 0, 21, 7, 2, 60, 93, 15, 2, 11, 3, 13, 1, 11, 4, 13, 0,
+ 42, 11, 5, 2, 0, 4, 0, 0, 0, 0, 2, 0, 0, 0, 93, 0, 0, 0, 2,110,
+ 0, 0, 0, 0, 93, 0, 0, 0, 2,118, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,
+110, 0, 2, 0, 0, 0, 2,118, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116,
+ 0, 2, 0, 0, 0, 16, 32,116,111,108,117, 97, 95,115,101,116,116, 97,103, 40,
+ 34, 0, 2, 0, 0, 0, 4, 34, 44, 38, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2,
+ 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 2,125, 0, 2, 0,
+ 0, 0, 9,114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0,100, 0, 0, 0,
+ 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0, 0,132, 4, 1,
+ 60,101, 15, 0, 11, 1, 2, 0, 1, 60,102, 15, 0, 11, 2, 13, 0, 18, 4, 42,
+ 11, 5, 42, 2, 0, 1, 60,103, 15, 0, 11, 6, 2, 0, 1, 60,104, 15, 0, 11,
+ 7, 2, 0, 1, 60,105, 15, 0, 11, 8, 2, 0, 1, 60,106, 15, 0, 11, 9, 2,
+ 0, 1, 60,107, 7, 1, 50, 23, 60,109, 13, 0, 13, 1, 16, 20, 11, 2, 0, 1,
+ 60,110, 13, 1, 7, 1, 37, 23, 1, 60,111, 60,108, 13, 0, 13, 1, 16, 54, 32,
+ 60,112, 15, 0, 11, 12, 2, 0, 1, 60,113, 15, 0, 11, 13, 2, 0, 1, 60,114,
+ 15, 0, 11, 14, 2, 0, 1, 60,115, 0, 0, 0, 0, 2, 0, 0, 0,100, 0, 0,
+ 0, 5,115,101,108,102, 0, 0, 0, 0,107, 0, 0, 0, 2,105, 0, 0, 0, 0,
+ 15, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 20, 47, 42,
+ 32, 79,112,101,110, 32,102,117,110, 99,116,105,111,110, 32, 42, 47, 0, 2, 0,
+ 0, 0, 11,105,110,116, 32,116,111,108,117, 97, 95, 0, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 13, 95,
+111,112,101,110, 32, 40,118,111,105,100, 41, 0, 2, 0, 0, 0, 2,123, 0, 2,
+ 0, 0, 0, 15, 32,116,111,108,117, 97, 95,111,112,101,110, 40, 41, 59, 0, 2,
+ 0, 0, 0, 19, 32,108,117, 97, 95, 98,101,103,105,110, 98,108,111, 99,107, 40,
+ 41, 59, 0, 2, 0, 0, 0, 20, 32,116,111,108,117, 97, 73, 95,105,110,105,116,
+ 95,116, 97,103, 40, 41, 59, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 9,
+114,101,103,105,115,116,101,114, 0, 2, 0, 0, 0, 17, 32,108,117, 97, 95,101,
+110,100, 98,108,111, 99,107, 40, 41, 59, 0, 2, 0, 0, 0, 11, 32,114,101,116,
+117,114,110, 32, 49, 59, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 11,117,
+110,114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0,119, 0, 0, 0, 13, 64,
+112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0, 0, 87, 4, 1, 60,120,
+ 15, 0, 11, 1, 2, 0, 1, 60,121, 15, 0, 11, 2, 13, 0, 18, 4, 42, 11, 5,
+ 42, 2, 0, 1, 60,122, 15, 0, 11, 6, 2, 0, 1, 60,123, 7, 1, 50, 23, 60,
+125, 13, 0, 13, 1, 16, 20, 8, 2, 0, 1, 60,126, 13, 1, 7, 1, 37, 23, 1,
+ 60,127, 60,124, 13, 0, 13, 1, 16, 54, 32, 60,128, 15, 0, 11, 9, 2, 0, 1,
+ 60,129, 0, 0, 0, 0, 2, 0, 0, 0,119, 0, 0, 0, 5,115,101,108,102, 0,
+ 0, 0, 0,123, 0, 0, 0, 2,105, 0, 0, 0, 0, 10, 2, 0, 0, 0, 7,111,
+117,116,112,117,116, 0, 2, 0, 0, 0, 21, 47, 42, 32, 67,108,111,115,101, 32,
+102,117,110, 99,116,105,111,110, 32, 42, 47, 0, 2, 0, 0, 0, 12,118,111,105,
+100, 32,116,111,108,117, 97, 95, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2,
+ 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 14, 95, 99,108,111,115,101,
+ 32, 40,118,111,105,100, 41, 0, 2, 0, 0, 0, 2,123, 0, 2, 0, 0, 0, 2,
+105, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,101,114, 0, 2, 0,
+ 0, 0, 2,125, 0, 2, 0, 0, 0, 7,104,101, 97,100,101,114, 0, 4, 0, 0,
+ 0,132, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0,
+ 0, 0,127, 4, 1, 60,133, 15, 0, 11, 1, 2, 0, 1, 15, 0, 11, 2, 13, 0,
+ 18, 4, 42, 11, 5, 42, 2, 0, 1, 60,134, 15, 0, 11, 6, 15, 7, 42, 11, 8,
+ 42, 15, 9, 2, 1, 0, 42, 11, 10, 42, 2, 0, 1, 60,135, 15, 0, 11, 11, 2,
+ 0, 1, 60,137, 15, 12, 18, 13, 44, 52, 54, 60,138, 15, 0, 11, 14, 2, 0, 1,
+ 60,139, 15, 0, 11, 15, 13, 0, 18, 4, 42, 11, 16, 42, 2, 0, 1, 60,140, 15,
+ 0, 11, 17, 13, 0, 18, 4, 42, 11, 18, 42, 2, 0, 1, 60,141, 15, 0, 11, 5,
+ 2, 0, 1, 50, 2, 60,142, 60,143, 0, 0, 0, 0, 1, 0, 0, 0,132, 0, 0,
+ 0, 5,115,101,108,102, 0, 0, 0, 0, 19, 2, 0, 0, 0, 7,111,117,116,112,
+117,116, 0, 2, 0, 0, 0, 4, 47, 42, 10, 0, 2, 0, 0, 0, 17, 42, 42, 32,
+ 76,117, 97, 32, 98,105,110,100,105,110,103, 58, 32, 0, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 2, 10,
+ 0, 2, 0, 0, 0, 31, 42, 42, 32, 71,101,110,101,114, 97,116,101,100, 32, 97,
+117,116,111,109, 97,116,105, 99, 97,108,108,121, 32, 98,121, 32, 0, 2, 0, 0,
+ 0, 14, 84, 79, 76, 85, 65, 95, 86, 69, 82, 83, 73, 79, 78, 0, 2, 0, 0, 0,
+ 5, 32,111,110, 32, 0, 2, 0, 0, 0, 5,100, 97,116,101, 0, 2, 0, 0, 0,
+ 3, 46, 10, 0, 2, 0, 0, 0, 5, 42, 47, 10, 10, 0, 2, 0, 0, 0, 6,102,
+108, 97,103,115, 0, 2, 0, 0, 0, 2,104, 0, 2, 0, 0, 0, 24, 47, 42, 32,
+ 69,120,112,111,114,116,101,100, 32,102,117,110, 99,116,105,111,110, 32, 42, 47,
+ 0, 2, 0, 0, 0, 12,105,110,116, 32, 32,116,111,108,117, 97, 95, 0, 2, 0,
+ 0, 0, 14, 95,111,112,101,110, 32, 40,118,111,105,100, 41, 59, 0, 2, 0, 0,
+ 0, 12,118,111,105,100, 32,116,111,108,117, 97, 95, 0, 2, 0, 0, 0, 15, 95,
+ 99,108,111,115,101, 32, 40,118,111,105,100, 41, 59, 0, 2, 0, 0, 0, 9, 95,
+ 80, 97, 99,107, 97,103,101, 0, 4, 0, 0, 0,146, 0, 0, 0, 13, 64,112, 97,
+ 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0, 0, 31, 4, 1, 60,147, 13, 0,
+ 11, 1, 15, 2, 26, 60,148, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60,149, 13, 0,
+ 1, 1, 60,150, 0, 0, 0, 0, 1, 0, 0, 0,146, 0, 0, 0, 2,116, 0, 0,
+ 0, 0, 5, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101,
+ 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 0, 2,
+ 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,116, 97,103, 0, 2, 0, 0, 0, 8, 80, 97, 99,107, 97,103,101, 0, 4,
+ 0, 0, 0,155, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97,
+ 0, 0, 0, 0,114, 10, 1, 60,157, 15, 2, 11, 3, 2, 1, 1, 60,159, 4, 0,
+ 60,161, 15, 5, 13, 1, 11, 6, 11, 7, 60,169, 2, 2, 3, 23, 2, 23, 1, 60,
+170, 13, 2, 7, 0, 32, 56, 28, 60,172, 15, 9, 15, 10, 22, 2, 11, 0, 13, 0,
+ 11, 1, 13, 1, 30, 1, 2, 1, 1, 2, 1, 1, 60,173, 15, 11, 13, 3, 2, 0,
+ 1, 60,174, 13, 3, 20, 12, 2, 0, 1, 60,175, 13, 3, 20, 13, 13, 3, 18, 1,
+ 2, 0, 2, 60,176, 15, 14, 2, 0, 0, 60,177, 13, 3, 1, 4, 60,178, 0, 0,
+ 0, 0, 4, 0, 0, 0,155, 0, 0, 0, 5,110, 97,109,101, 0, 0, 0, 0,157,
+ 0, 0, 0, 5, 99,111,100,101, 0, 0, 0, 0,159, 0, 0, 0, 7,110,115,117,
+ 98,115,116, 0, 0, 0, 0,172, 0, 0, 0, 2,116, 0, 0, 0, 0, 15, 2, 0,
+ 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 5, 99,111,100,101, 0, 2, 0,
+ 0, 0, 5,114,101, 97,100, 0, 2, 0, 0, 0, 3, 46, 42, 0, 2, 0, 0, 0,
+ 7,110,115,117, 98,115,116, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0,
+ 0, 0, 13, 37, 36, 60, 40, 46, 45, 41, 62, 37,115, 42, 10, 0, 4, 0, 0, 0,
+161, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0,
+ 0, 71, 6, 1, 60,162, 15, 3, 13, 0, 11, 4, 2, 2, 2, 60,163, 13, 1, 44,
+ 52, 20, 60,164, 15, 5, 11, 6, 13, 2, 42, 11, 7, 42, 13, 0, 42, 2, 0, 1,
+ 50, 2, 60,165, 60,166, 15, 9, 13, 1, 11, 10, 2, 1, 2, 60,167, 15, 11, 13,
+ 1, 2, 0, 1, 60,168, 13, 3, 1, 4, 60,169, 0, 0, 0, 0, 4, 0, 0, 0,
+161, 0, 0, 0, 3,102,110, 0, 0, 0, 0,162, 0, 0, 0, 3,102,112, 0, 0,
+ 0, 0,162, 0, 0, 0, 4,109,115,103, 0, 0, 0, 0,166, 0, 0, 0, 2,115,
+ 0, 0, 0, 0, 12, 2, 0, 0, 0, 3,102,110, 0, 2, 0, 0, 0, 3,102,112,
+ 0, 2, 0, 0, 0, 4,109,115,103, 0, 2, 0, 0, 0, 9,111,112,101,110,102,
+105,108,101, 0, 2, 0, 0, 0, 2,114, 0, 2, 0, 0, 0, 6,101,114,114,111,
+114, 0, 2, 0, 0, 0, 2, 35, 0, 2, 0, 0, 0, 3, 58, 32, 0, 2, 0, 0,
+ 0, 2,115, 0, 2, 0, 0, 0, 5,114,101, 97,100, 0, 2, 0, 0, 0, 3, 46,
+ 42, 0, 2, 0, 0, 0, 10, 99,108,111,115,101,102,105,108,101, 0, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 9, 95, 80, 97, 99,107, 97,103,101, 0, 2, 0,
+ 0, 0, 11, 95, 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5,112,
+117,115,104, 0, 2, 0, 0, 0, 11,112,114,101,112,114,111, 99,101,115,115, 0,
+ 2, 0, 0, 0, 6,112, 97,114,115,101, 0, 2, 0, 0, 0, 4,112,111,112, 0,
+
+};
+
+/* module.lo */
+static char B6[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 12, 64,109,111,100,117,108,
+101, 46,108,117, 97, 0, 0, 0, 0, 75, 5, 0, 60, 19, 22, 2, 60, 20, 11, 1,
+ 15, 2, 11, 3, 60, 21, 11, 4, 30, 1, 60, 22, 25, 0, 60, 23, 15, 5, 15, 0,
+ 15, 6, 2, 0, 2, 60, 26, 15, 0, 11, 7, 11, 8, 26, 60, 36, 15, 0, 11, 9,
+ 11, 10, 26, 60, 41, 15, 0, 11, 11, 11, 12, 26, 60, 53, 11, 14, 25, 13, 60, 62,
+ 11, 16, 25, 15, 0, 0, 0, 0, 0, 0, 0, 0, 17, 2, 0, 0, 0, 12, 99,108,
+ 97,115,115, 77,111,100,117,108,101, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101,
+ 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114,
+ 0, 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 7,109,111,100,117,
+108,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,
+116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,114,101,103,105,115,
+116,101,114, 0, 4, 0, 0, 0, 26, 0, 0, 0, 12, 64,109,111,100,117,108,101,
+ 46,108,117, 97, 0, 0, 0, 0, 60, 4, 1, 60, 27, 15, 0, 11, 1, 13, 0, 18,
+ 3, 42, 11, 4, 42, 2, 0, 1, 60, 28, 7, 1, 50, 23, 60, 30, 13, 0, 13, 1,
+ 16, 20, 6, 2, 0, 1, 60, 31, 13, 1, 7, 1, 37, 23, 1, 60, 32, 60, 29, 13,
+ 0, 13, 1, 16, 54, 32, 60, 33, 0, 0, 0, 0, 2, 0, 0, 0, 26, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 28, 0, 0, 0, 2,105, 0, 0, 0, 0, 7,
+ 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 16, 32,116,111,
+108,117, 97, 95,109,111,100,117,108,101, 40, 34, 0, 2, 0, 0, 0, 5,115,101,
+108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 4, 34, 41,
+ 59, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,
+101,114, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,101,114, 0, 4,
+ 0, 0, 0, 36, 0, 0, 0, 12, 64,109,111,100,117,108,101, 46,108,117, 97, 0,
+ 0, 0, 0, 22, 4, 1, 60, 37, 15, 0, 11, 1, 13, 0, 18, 3, 42, 11, 4, 42,
+ 2, 0, 1, 60, 38, 0, 0, 0, 0, 1, 0, 0, 0, 36, 0, 0, 0, 5,115,101,
+108,102, 0, 0, 0, 0, 5, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2,
+ 0, 0, 0, 32, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40, 41, 59, 32,
+108,117, 97, 95,115,101,116,103,108,111, 98, 97,108, 40, 34, 0, 2, 0, 0, 0,
+ 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0,
+ 4, 34, 41, 59, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0,
+ 41, 0, 0, 0, 12, 64,109,111,100,117,108,101, 46,108,117, 97, 0, 0, 0, 0,
+ 97, 8, 3, 60, 42, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 43, 15, 2, 13,
+ 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 44, 7, 1, 50,
+ 30, 60, 46, 13, 0, 13, 3, 16, 20, 2, 13, 1, 11, 9, 42, 11, 10, 2, 0, 3,
+ 60, 47, 13, 3, 7, 1, 37, 23, 3, 60, 48, 60, 45, 13, 0, 13, 3, 16, 54, 39,
+ 60, 49, 15, 2, 13, 1, 11, 11, 42, 13, 2, 42, 2, 0, 1, 60, 50, 0, 0, 0,
+ 0, 4, 0, 0, 0, 41, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 41, 0,
+ 0, 0, 6,105,100,101,110,116, 0, 0, 0, 0, 41, 0, 0, 0, 6, 99,108,111,
+115,101, 0, 0, 0, 0, 44, 0, 0, 0, 2,105, 0, 0, 0, 0, 12, 2, 0, 0,
+ 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2,
+ 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 8, 77,111,100,117,108,
+101,123, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61, 32, 39, 0, 2, 0,
+ 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0,
+ 0, 0, 3, 39, 59, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 2, 32, 0,
+ 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 8, 95,
+ 77,111,100,117,108,101, 0, 4, 0, 0, 0, 53, 0, 0, 0, 12, 64,109,111,100,
+117,108,101, 46,108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60, 54, 13, 0, 11, 1,
+ 15, 2, 26, 60, 55, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 56, 15, 5, 13, 0,
+ 2, 0, 1, 60, 57, 13, 0, 1, 1, 60, 58, 0, 0, 0, 0, 1, 0, 0, 0, 53,
+ 0, 0, 0, 2,116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0,
+ 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 12, 99,108, 97,115,115, 77,111,
+100,117,108,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0,
+ 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 7, 97,112,112,
+101,110,100, 0, 2, 0, 0, 0, 7, 77,111,100,117,108,101, 0, 4, 0, 0, 0,
+ 62, 0, 0, 0, 12, 64,109,111,100,117,108,101, 46,108,117, 97, 0, 0, 0, 0,
+ 75, 10, 2, 60, 63, 15, 3, 15, 4, 22, 1, 11, 5, 13, 0, 30, 0, 2, 1, 1,
+ 2, 1, 1, 60, 64, 15, 6, 13, 2, 2, 0, 1, 60, 65, 13, 2, 20, 7, 15, 8,
+ 13, 1, 7, 2, 15, 9, 13, 1, 2, 1, 1, 7, 1, 38, 2, 1, 3, 2, 0, 2,
+ 60, 66, 15, 10, 2, 0, 0, 60, 67, 13, 2, 1, 3, 60, 68, 0, 0, 0, 0, 3,
+ 0, 0, 0, 62, 0, 0, 0, 2,110, 0, 0, 0, 0, 62, 0, 0, 0, 2, 98, 0,
+ 0, 0, 0, 63, 0, 0, 0, 2,116, 0, 0, 0, 0, 11, 2, 0, 0, 0, 2,110,
+ 0, 2, 0, 0, 0, 2, 98, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 8,
+ 95, 77,111,100,117,108,101, 0, 2, 0, 0, 0, 11, 95, 67,111,110,116, 97,105,
+110,101,114, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 5,112,
+117,115,104, 0, 2, 0, 0, 0, 6,112, 97,114,115,101, 0, 2, 0, 0, 0, 7,
+115,116,114,115,117, 98, 0, 2, 0, 0, 0, 7,115,116,114,108,101,110, 0, 2,
+ 0, 0, 0, 4,112,111,112, 0,
+};
+
+/* class.lo */
+static char B7[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 11, 64, 99,108, 97,115,115,
+ 46,108,117, 97, 0, 0, 0, 0, 96, 9, 0, 60, 20, 22, 4, 60, 21, 11, 1, 15,
+ 2, 11, 3, 60, 22, 11, 4, 11, 5, 60, 23, 11, 6, 11, 7, 60, 24, 11, 6, 30,
+ 3, 60, 25, 25, 0, 60, 26, 15, 8, 15, 0, 15, 9, 2, 0, 2, 60, 30, 15, 0,
+ 11, 10, 11, 11, 26, 60, 40, 15, 0, 11, 12, 11, 13, 26, 60, 45, 15, 0, 11, 14,
+ 11, 15, 26, 60, 57, 15, 0, 11, 16, 11, 17, 26, 60, 70, 11, 19, 25, 18, 60, 79,
+ 11, 21, 25, 20, 0, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 11, 99,108,
+ 97,115,115, 67,108, 97,115,115, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0,
+ 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 0,
+ 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 6, 99,108, 97,115,115,
+ 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0,
+ 0, 5, 98, 97,115,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2,
+ 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,114,
+101,103,105,115,116,101,114, 0, 4, 0, 0, 0, 30, 0, 0, 0, 11, 64, 99,108,
+ 97,115,115, 46,108,117, 97, 0, 0, 0, 0, 68, 4, 1, 60, 31, 15, 0, 11, 1,
+ 13, 0, 18, 3, 42, 11, 4, 42, 13, 0, 18, 5, 42, 11, 6, 42, 2, 0, 1, 60,
+ 32, 7, 1, 50, 23, 60, 34, 13, 0, 13, 1, 16, 20, 8, 2, 0, 1, 60, 35, 13,
+ 1, 7, 1, 37, 23, 1, 60, 36, 60, 33, 13, 0, 13, 1, 16, 54, 32, 60, 37, 0,
+ 0, 0, 0, 2, 0, 0, 0, 30, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,
+ 32, 0, 0, 0, 2,105, 0, 0, 0, 0, 9, 2, 0, 0, 0, 7,111,117,116,112,
+117,116, 0, 2, 0, 0, 0, 16, 32,116,111,108,117, 97, 95, 99, 99,108, 97,115,
+115, 40, 34, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110,
+ 97,109,101, 0, 2, 0, 0, 0, 4, 34, 44, 34, 0, 2, 0, 0, 0, 5, 98, 97,
+115,101, 0, 2, 0, 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 2,105, 0, 2,
+ 0, 0, 0, 9,114,101,103,105,115,116,101,114, 0, 2, 0, 0, 0, 11,117,110,
+114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0, 40, 0, 0, 0, 11, 64, 99,
+108, 97,115,115, 46,108,117, 97, 0, 0, 0, 0, 22, 4, 1, 60, 41, 15, 0, 11,
+ 1, 13, 0, 18, 3, 42, 11, 4, 42, 2, 0, 1, 60, 42, 0, 0, 0, 0, 1, 0,
+ 0, 0, 40, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 5, 2, 0, 0, 0,
+ 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 32, 32,108,117, 97, 95,112,117,
+115,104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98,
+ 97,108, 40, 34, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,
+110, 97,109,101, 0, 2, 0, 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 8,100,
+101, 99,108,116, 97,103, 0, 4, 0, 0, 0, 45, 0, 0, 0, 11, 64, 99,108, 97,
+115,115, 46,108,117, 97, 0, 0, 0, 0,128, 8, 1, 60, 46, 13, 0, 11, 1, 13,
+ 0, 11, 2, 15, 3, 13, 0, 18, 4, 2, 2, 1, 27, 1, 27, 2, 5, 4, 15, 5,
+ 13, 0, 18, 1, 13, 0, 18, 2, 2, 0, 2, 60, 47, 13, 0, 11, 6, 13, 0, 11,
+ 7, 15, 3, 13, 0, 18, 4, 11, 8, 2, 2, 2, 27, 1, 27, 2, 5, 4, 60, 48,
+ 15, 3, 13, 0, 18, 10, 2, 2, 1, 15, 5, 13, 1, 13, 2, 2, 0, 2, 60, 49,
+ 7, 1, 50, 23, 60, 51, 13, 0, 13, 3, 16, 20, 5, 2, 0, 1, 60, 52, 13, 3,
+ 7, 1, 37, 23, 3, 60, 53, 60, 50, 13, 0, 13, 3, 16, 54, 32, 60, 54, 0, 0,
+ 0, 0, 4, 0, 0, 0, 45, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 48,
+ 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0, 48, 0, 0, 0, 4,116, 97,103,
+ 0, 0, 0, 0, 49, 0, 0, 0, 2,105, 0, 0, 0, 0, 12, 2, 0, 0, 0, 5,
+115,101,108,102, 0, 2, 0, 0, 0, 6,105,116,121,112,101, 0, 2, 0, 0, 0,
+ 4,116, 97,103, 0, 2, 0, 0, 0, 7,116, 97,103,118, 97,114, 0, 2, 0, 0,
+ 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0,
+ 2, 0, 0, 0, 7, 99,105,116,121,112,101, 0, 2, 0, 0, 0, 5, 99,116, 97,
+103, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 5,116,121,
+112,101, 0, 2, 0, 0, 0, 5, 98, 97,115,101, 0, 2, 0, 0, 0, 2,105, 0,
+ 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 57, 0, 0, 0, 11,
+ 64, 99,108, 97,115,115, 46,108,117, 97, 0, 0, 0, 0,117, 8, 3, 60, 58, 15,
+ 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 59, 15, 2, 13, 1, 11, 4, 42, 13, 0,
+ 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 60, 15, 2, 13, 1, 11, 8, 42, 13, 0,
+ 18, 9, 42, 11, 10, 42, 2, 0, 1, 60, 61, 7, 1, 50, 30, 60, 63, 13, 0, 13,
+ 3, 16, 20, 2, 13, 1, 11, 12, 42, 11, 13, 2, 0, 3, 60, 64, 13, 3, 7, 1,
+ 37, 23, 3, 60, 65, 60, 62, 13, 0, 13, 3, 16, 54, 39, 60, 66, 15, 2, 13, 1,
+ 11, 14, 42, 13, 2, 42, 2, 0, 1, 60, 67, 0, 0, 0, 0, 4, 0, 0, 0, 57,
+ 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 57, 0, 0, 0, 6,105,100,101,
+110,116, 0, 0, 0, 0, 57, 0, 0, 0, 6, 99,108,111,115,101, 0, 0, 0, 0,
+ 61, 0, 0, 0, 2,105, 0, 0, 0, 0, 15, 2, 0, 0, 0, 6,105,100,101,110,
+116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,
+105,110,116, 0, 2, 0, 0, 0, 7, 67,108, 97,115,115,123, 0, 2, 0, 0, 0,
+ 10, 32,110, 97,109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102,
+ 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2,
+ 0, 0, 0, 10, 32, 98, 97,115,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 3, 39, 59, 0, 2, 0, 0, 0, 2,105, 0, 2,
+ 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,125, 0,
+ 2, 0, 0, 0, 7, 95, 67,108, 97,115,115, 0, 4, 0, 0, 0, 70, 0, 0, 0,
+ 11, 64, 99,108, 97,115,115, 46,108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60, 71,
+ 13, 0, 11, 1, 15, 2, 26, 60, 72, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 73,
+ 15, 5, 13, 0, 2, 0, 1, 60, 74, 13, 0, 1, 1, 60, 75, 0, 0, 0, 0, 1,
+ 0, 0, 0, 70, 0, 0, 0, 2,116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116,
+ 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 11, 99,108, 97,
+115,115, 67,108, 97,115,115, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0,
+ 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 7,
+ 97,112,112,101,110,100, 0, 2, 0, 0, 0, 6, 67,108, 97,115,115, 0, 4, 0,
+ 0, 0, 79, 0, 0, 0, 11, 64, 99,108, 97,115,115, 46,108,117, 97, 0, 0, 0,
+ 0, 73, 11, 3, 60, 80, 15, 4, 15, 5, 22, 2, 11, 6, 13, 0, 11, 7, 13, 1,
+ 30, 1, 2, 1, 1, 2, 1, 1, 60, 81, 15, 8, 13, 3, 2, 0, 1, 60, 82, 13,
+ 3, 20, 9, 15, 10, 13, 2, 7, 2, 15, 11, 13, 2, 2, 1, 1, 7, 1, 38, 2,
+ 1, 3, 2, 0, 2, 60, 83, 15, 12, 2, 0, 0, 60, 84, 0, 0, 0, 0, 4, 0,
+ 0, 0, 79, 0, 0, 0, 2,110, 0, 0, 0, 0, 79, 0, 0, 0, 2,112, 0, 0,
+ 0, 0, 79, 0, 0, 0, 2, 98, 0, 0, 0, 0, 80, 0, 0, 0, 2, 99, 0, 0,
+ 0, 0, 13, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,112, 0, 2, 0, 0,
+ 0, 2, 98, 0, 2, 0, 0, 0, 2, 99, 0, 2, 0, 0, 0, 7, 95, 67,108, 97,
+115,115, 0, 2, 0, 0, 0, 11, 95, 67,111,110,116, 97,105,110,101,114, 0, 2,
+ 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 5, 98, 97,115,101, 0, 2,
+ 0, 0, 0, 5,112,117,115,104, 0, 2, 0, 0, 0, 6,112, 97,114,115,101, 0,
+ 2, 0, 0, 0, 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0, 7,115,116,114,
+108,101,110, 0, 2, 0, 0, 0, 4,112,111,112, 0,
+};
+
+/* typedef.lo */
+static char B8[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 13, 64,116,121,112,101,100,
+101,102, 46,108,117, 97, 0, 0, 0, 0, 52, 7, 0, 60, 24, 22, 3, 60, 25, 11,
+ 1, 11, 2, 11, 3, 60, 26, 11, 2, 11, 4, 60, 27, 11, 2, 30, 2, 60, 28, 25,
+ 0, 60, 31, 15, 0, 11, 5, 11, 6, 26, 60, 40, 11, 8, 25, 7, 60, 49, 11, 10,
+ 25, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 2, 0, 0, 0, 13, 99,108, 97,115,
+115, 84,121,112,101,100,101,102, 0, 2, 0, 0, 0, 6,117,116,121,112,101, 0,
+ 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 5,
+116,121,112,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0,
+ 31, 0, 0, 0, 13, 64,116,121,112,101,100,101,102, 46,108,117, 97, 0, 0, 0,
+ 0, 92, 6, 3, 60, 32, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 33, 15, 2,
+ 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 34, 15, 2,
+ 13, 1, 11, 8, 42, 13, 0, 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 35, 15, 2,
+ 13, 1, 11, 10, 42, 13, 0, 18, 11, 42, 11, 7, 42, 2, 0, 1, 60, 36, 15, 2,
+ 13, 1, 11, 12, 42, 13, 2, 42, 2, 0, 1, 60, 37, 0, 0, 0, 0, 3, 0, 0,
+ 0, 31, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 31, 0, 0, 0, 6,105,
+100,101,110,116, 0, 0, 0, 0, 31, 0, 0, 0, 6, 99,108,111,115,101, 0, 0,
+ 0, 0, 13, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,
+108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0,
+ 9, 84,121,112,101,100,101,102,123, 0, 2, 0, 0, 0, 11, 32,117,116,121,112,
+101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 6,117,116,121,112,101, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 9,
+ 32,109,111,100, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0,
+ 0, 0, 10, 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,116,121,
+112,101, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 9, 95, 84,121,112,101,
+100,101,102, 0, 4, 0, 0, 0, 40, 0, 0, 0, 13, 64,116,121,112,101,100,101,
+102, 46,108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60, 41, 13, 0, 11, 1, 15, 2,
+ 26, 60, 42, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 43, 15, 5, 13, 0, 2, 0,
+ 1, 60, 44, 13, 0, 1, 1, 60, 45, 0, 0, 0, 0, 1, 0, 0, 0, 40, 0, 0,
+ 0, 2,116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6,
+ 95, 98, 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 84,121,112,101,
+100,101,102, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0,
+ 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 14, 97,112,112,101,
+110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 8, 84,121,112,101,100,
+101,102, 0, 4, 0, 0, 0, 49, 0, 0, 0, 13, 64,116,121,112,101,100,101,102,
+ 46,108,117, 97, 0, 0, 0, 0,109, 14, 1, 60, 50, 15, 1, 13, 0, 11, 2, 2,
+ 1, 2, 52, 11, 60, 51, 15, 3, 11, 4, 2, 0, 1, 50, 2, 60, 52, 60, 53, 15,
+ 6, 15, 7, 13, 0, 11, 8, 11, 9, 2, 1, 3, 11, 9, 2, 1, 2, 60, 54, 15,
+ 10, 22, 3, 60, 55, 11, 11, 13, 1, 13, 1, 18, 12, 16, 11, 13, 60, 56, 13, 1,
+ 13, 1, 18, 12, 7, 1, 38, 16, 11, 14, 60, 57, 15, 15, 13, 1, 7, 1, 13, 1,
+ 18, 12, 7, 2, 38, 2, 1, 3, 30, 2, 60, 58, 3, 2, 1, 60, 59, 0, 0, 0,
+ 0, 2, 0, 0, 0, 49, 0, 0, 0, 2,115, 0, 0, 0, 0, 53, 0, 0, 0, 2,
+116, 0, 0, 0, 0, 16, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 8,115,116,
+114,102,105,110,100, 0, 2, 0, 0, 0, 6, 91, 37, 42, 38, 93, 0, 2, 0, 0,
+ 0, 12,116,111,108,117, 97, 95,101,114,114,111,114, 0, 2, 0, 0, 0, 62, 35,
+105,110,118, 97,108,105,100, 32,116,121,112,101,100,101,102, 58, 32,112,111,105,
+110,116,101,114,115, 32, 40, 97,110,100, 32,114,101,102,101,114,101,110, 99,101,
+115, 41, 32, 97,114,101, 32,110,111,116, 32,115,117,112,112,111,114,116,101,100,
+ 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2,
+ 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 6, 37,115, 37,115, 42, 0,
+ 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 9, 95, 84,121,112,101,100,101,102,
+ 0, 2, 0, 0, 0, 6,117,116,121,112,101, 0, 2, 0, 0, 0, 2,110, 0, 2,
+ 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0,
+ 0, 0, 7, 99,111,110, 99, 97,116, 0,
+};
+
+/* define.lo */
+static char B9[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 12, 64,100,101,102,105,110,
+101, 46,108,117, 97, 0, 0, 0, 0, 75, 5, 0, 60, 18, 22, 2, 60, 19, 11, 1,
+ 11, 2, 11, 3, 60, 20, 15, 4, 30, 1, 60, 21, 25, 0, 60, 22, 15, 5, 15, 0,
+ 15, 6, 2, 0, 2, 60, 25, 15, 0, 11, 7, 11, 8, 26, 60, 35, 15, 0, 11, 9,
+ 11, 10, 26, 60, 42, 15, 0, 11, 11, 11, 12, 26, 60, 51, 11, 14, 25, 13, 60, 65,
+ 11, 16, 25, 15, 0, 0, 0, 0, 0, 0, 0, 0, 17, 2, 0, 0, 0, 12, 99,108,
+ 97,115,115, 68,101,102,105,110,101, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0,
+ 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0,
+ 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 0, 2, 0, 0, 0, 7,
+115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,
+103, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0,
+ 25, 0, 0, 0, 12, 64,100,101,102,105,110,101, 46,108,117, 97, 0, 0, 0, 0,
+ 80, 5, 1, 60, 26, 13, 0, 20, 2, 2, 1, 1, 60, 27, 13, 1, 52, 33, 60, 28,
+ 15, 3, 11, 4, 13, 1, 42, 11, 5, 42, 13, 0, 18, 6, 42, 11, 7, 42, 13, 0,
+ 18, 8, 42, 11, 9, 42, 2, 0, 1, 50, 27, 60, 30, 15, 3, 11, 10, 13, 0, 18,
+ 6, 42, 11, 7, 42, 13, 0, 18, 8, 42, 11, 9, 42, 2, 0, 1, 60, 31, 60, 32,
+ 0, 0, 0, 0, 2, 0, 0, 0, 25, 0, 0, 0, 5,115,101,108,102, 0, 0, 0,
+ 0, 26, 0, 0, 0, 2,112, 0, 0, 0, 0, 11, 2, 0, 0, 0, 2,112, 0, 2,
+ 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,
+108,101, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 18,
+ 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,110,116, 40, 34, 0, 2, 0,
+ 0, 0, 4, 34, 44, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0,
+ 0, 0, 3, 34, 44, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0,
+ 3, 41, 59, 0, 2, 0, 0, 0, 23, 32,116,111,108,117, 97, 95, 99,111,110,115,
+116, 97,110,116, 40, 78, 85, 76, 76, 44, 34, 0, 2, 0, 0, 0, 11,117,110,114,
+101,103,105,115,116,101,114, 0, 4, 0, 0, 0, 35, 0, 0, 0, 12, 64,100,101,
+102,105,110,101, 46,108,117, 97, 0, 0, 0, 0, 38, 4, 1, 60, 36, 13, 0, 20,
+ 1, 2, 1, 1, 44, 52, 19, 60, 37, 15, 2, 11, 3, 13, 0, 18, 4, 42, 11, 5,
+ 42, 2, 0, 1, 50, 2, 60, 38, 60, 39, 0, 0, 0, 0, 1, 0, 0, 0, 35, 0,
+ 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 6, 2, 0, 0, 0, 5,115,101,108,
+102, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0,
+ 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 32, 32,108,117, 97, 95,112,117,
+115,104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98,
+ 97,108, 40, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0,
+ 4, 34, 41, 59, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0,
+ 42, 0, 0, 0, 12, 64,100,101,102,105,110,101, 46,108,117, 97, 0, 0, 0, 0,
+ 72, 6, 3, 60, 43, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 44, 15, 2, 13,
+ 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 45, 15, 2, 13,
+ 1, 11, 8, 42, 13, 0, 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 46, 15, 2, 13,
+ 1, 11, 10, 42, 13, 2, 42, 2, 0, 1, 60, 47, 0, 0, 0, 0, 3, 0, 0, 0,
+ 42, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 42, 0, 0, 0, 6,105,100,
+101,110,116, 0, 0, 0, 0, 42, 0, 0, 0, 6, 99,108,111,115,101, 0, 0, 0,
+ 0, 11, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,
+111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 8,
+ 68,101,102,105,110,101,123, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61,
+ 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,
+109,101, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 11, 32,108,110, 97,
+109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0,
+ 0, 0, 2,125, 0, 2, 0, 0, 0, 8, 95, 68,101,102,105,110,101, 0, 4, 0,
+ 0, 0, 51, 0, 0, 0, 12, 64,100,101,102,105,110,101, 46,108,117, 97, 0, 0,
+ 0, 0, 64, 4, 1, 60, 52, 13, 0, 11, 1, 15, 2, 26, 60, 53, 15, 3, 13, 0,
+ 15, 4, 2, 0, 2, 60, 55, 13, 0, 18, 5, 11, 6, 32, 52, 11, 60, 56, 15, 7,
+ 11, 8, 2, 0, 1, 50, 2, 60, 57, 60, 59, 15, 9, 13, 0, 2, 0, 1, 60, 60,
+ 13, 0, 1, 1, 60, 61, 0, 0, 0, 0, 1, 0, 0, 0, 51, 0, 0, 0, 2,116,
+ 0, 0, 0, 0, 10, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,
+115,101, 0, 2, 0, 0, 0, 12, 99,108, 97,115,115, 68,101,102,105,110,101, 0,
+ 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,
+117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0,
+ 0, 1, 0, 2, 0, 0, 0, 6,101,114,114,111,114, 0, 2, 0, 0, 0, 16, 35,
+105,110,118, 97,108,105,100, 32,100,101,102,105,110,101, 0, 2, 0, 0, 0, 7,
+ 97,112,112,101,110,100, 0, 2, 0, 0, 0, 7, 68,101,102,105,110,101, 0, 4,
+ 0, 0, 0, 65, 0, 0, 0, 12, 64,100,101,102,105,110,101, 46,108,117, 97, 0,
+ 0, 0, 0, 54, 9, 1, 60, 66, 15, 2, 13, 0, 11, 3, 2, 1, 2, 60, 67, 15,
+ 4, 22, 2, 60, 68, 11, 5, 13, 1, 7, 1, 16, 11, 6, 60, 69, 13, 1, 7, 2,
+ 16, 46, 5, 13, 1, 7, 1, 16, 30, 1, 60, 70, 3, 2, 1, 60, 71, 0, 0, 0,
+ 0, 2, 0, 0, 0, 65, 0, 0, 0, 2,110, 0, 0, 0, 0, 66, 0, 0, 0, 2,
+116, 0, 0, 0, 0, 7, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,116, 0,
+ 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0, 0, 0, 2, 64, 0, 2, 0,
+ 0, 0, 8, 95, 68,101,102,105,110,101, 0, 2, 0, 0, 0, 5,110, 97,109,101,
+ 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0,
+};
+
+/* enumerate.lo */
+static char B10[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 15, 64,101,110,117,109,101,
+114, 97,116,101, 46,108,117, 97, 0, 0, 0, 0, 69, 3, 0, 60, 19, 22, 1, 60,
+ 20, 11, 1, 15, 2, 30, 0, 60, 21, 25, 0, 60, 22, 15, 3, 15, 0, 15, 4, 2,
+ 0, 2, 60, 25, 15, 0, 11, 5, 11, 6, 26, 60, 42, 15, 0, 11, 7, 11, 8, 26,
+ 60, 53, 15, 0, 11, 9, 11, 10, 26, 60, 64, 11, 12, 25, 11, 60, 73, 11, 14, 25,
+ 13, 0, 0, 0, 0, 0, 0, 0, 0, 15, 2, 0, 0, 0, 15, 99,108, 97,115,115,
+ 69,110,117,109,101,114, 97,116,101, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101,
+ 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 0, 2,
+ 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,101,114, 0,
+ 4, 0, 0, 0, 25, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,116,101, 46,
+108,117, 97, 0, 0, 0, 0,179, 7, 1, 60, 26, 13, 0, 20, 2, 2, 1, 1, 46,
+ 7, 13, 0, 20, 3, 2, 1, 1, 60, 27, 7, 1, 50,141, 60, 29, 13, 1, 52, 93,
+ 60, 30, 13, 0, 20, 2, 2, 1, 1, 52, 43, 60, 31, 15, 5, 11, 6, 13, 1, 42,
+ 11, 7, 42, 13, 0, 18, 8, 13, 2, 16, 42, 11, 9, 42, 13, 1, 42, 11, 10, 42,
+ 13, 0, 13, 2, 16, 42, 11, 11, 42, 2, 0, 1, 50, 37, 60, 33, 15, 5, 11, 6,
+ 13, 1, 42, 11, 7, 42, 13, 0, 18, 8, 13, 2, 16, 42, 11, 9, 42, 13, 0, 13,
+ 2, 16, 42, 11, 11, 42, 2, 0, 1, 60, 34, 50, 31, 60, 36, 15, 5, 11, 12, 13,
+ 0, 18, 8, 13, 2, 16, 42, 11, 9, 42, 13, 0, 13, 2, 16, 42, 11, 11, 42, 2,
+ 0, 1, 60, 37, 60, 38, 13, 2, 7, 1, 37, 23, 2, 60, 39, 60, 28, 13, 0, 13,
+ 2, 16, 54,150, 60, 40, 0, 0, 0, 0, 3, 0, 0, 0, 25, 0, 0, 0, 5,115,
+101,108,102, 0, 0, 0, 0, 26, 0, 0, 0, 2,112, 0, 0, 0, 0, 27, 0, 0,
+ 0, 2,105, 0, 0, 0, 0, 13, 2, 0, 0, 0, 2,112, 0, 2, 0, 0, 0, 5,
+115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115, 0, 2, 0,
+ 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0, 2,105, 0, 2,
+ 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 18, 32,116,111,108,
+117, 97, 95, 99,111,110,115,116, 97,110,116, 40, 34, 0, 2, 0, 0, 0, 4, 34,
+ 44, 34, 0, 2, 0, 0, 0, 7,108,110, 97,109,101,115, 0, 2, 0, 0, 0, 3,
+ 34, 44, 0, 2, 0, 0, 0, 3, 58, 58, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2,
+ 0, 0, 0, 23, 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,110,116, 40,
+ 78, 85, 76, 76, 44, 34, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,
+101,114, 0, 4, 0, 0, 0, 42, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,
+116,101, 46,108,117, 97, 0, 0, 0, 0, 83, 6, 1, 60, 43, 13, 0, 20, 1, 2,
+ 1, 1, 4, 0, 32, 48, 10, 13, 0, 20, 2, 2, 1, 1, 4, 0, 32, 52, 50, 60,
+ 44, 7, 1, 50, 31, 60, 46, 15, 4, 11, 5, 13, 0, 18, 6, 13, 1, 16, 42, 11,
+ 7, 42, 2, 0, 1, 60, 47, 13, 1, 7, 1, 37, 23, 1, 60, 48, 60, 45, 13, 0,
+ 13, 1, 16, 54, 40, 5, 1, 50, 2, 60, 49, 60, 50, 0, 0, 0, 0, 3, 0, 0,
+ 0, 42, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 44, 0, 0, 0, 2,105,
+ 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 8, 2, 0, 0, 0, 5,115,101,
+108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0,
+ 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0,
+ 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 32, 32,108,117, 97, 95,112,
+117,115,104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111,
+ 98, 97,108, 40, 34, 0, 2, 0, 0, 0, 7,108,110, 97,109,101,115, 0, 2, 0,
+ 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0,
+ 0, 0, 53, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,116,101, 46,108,117,
+ 97, 0, 0, 0, 0, 90, 8, 3, 60, 54, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1,
+ 60, 55, 7, 1, 50, 43, 60, 57, 15, 2, 13, 1, 11, 6, 42, 13, 0, 13, 3, 16,
+ 42, 11, 7, 42, 13, 0, 18, 8, 13, 3, 16, 42, 11, 9, 42, 2, 0, 1, 60, 58,
+ 13, 3, 7, 1, 37, 23, 3, 60, 59, 60, 56, 13, 0, 13, 3, 16, 54, 52, 60, 60,
+ 15, 2, 13, 1, 11, 10, 42, 13, 2, 42, 2, 0, 1, 60, 61, 0, 0, 0, 0, 4,
+ 0, 0, 0, 53, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 53, 0, 0, 0,
+ 6,105,100,101,110,116, 0, 0, 0, 0, 53, 0, 0, 0, 6, 99,108,111,115,101,
+ 0, 0, 0, 0, 55, 0, 0, 0, 2,105, 0, 0, 0, 0, 11, 2, 0, 0, 0, 6,
+105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0,
+ 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 11, 69,110,117,109,101,114, 97,
+116,101,123, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 5,115,101,108,102,
+ 0, 2, 0, 0, 0, 3, 32, 39, 0, 2, 0, 0, 0, 3, 39, 40, 0, 2, 0, 0,
+ 0, 7,108,110, 97,109,101,115, 0, 2, 0, 0, 0, 3, 41, 44, 0, 2, 0, 0,
+ 0, 2,125, 0, 2, 0, 0, 0, 11, 95, 69,110,117,109,101,114, 97,116,101, 0,
+ 4, 0, 0, 0, 64, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,116,101, 46,
+108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60, 65, 13, 0, 11, 1, 15, 2, 26, 60,
+ 66, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 67, 15, 5, 13, 0, 2, 0, 1, 60,
+ 68, 13, 0, 1, 1, 60, 69, 0, 0, 0, 0, 1, 0, 0, 0, 64, 0, 0, 0, 2,
+116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 69,110,117,109,101,114,
+ 97,116,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0,
+ 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 7, 97,112,112,101,
+110,100, 0, 2, 0, 0, 0, 10, 69,110,117,109,101,114, 97,116,101, 0, 4, 0,
+ 0, 0, 73, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,116,101, 46,108,117,
+ 97, 0, 0, 0, 0,200, 9, 1, 60, 74, 15, 2, 15, 3, 13, 0, 7, 2, 9, 2,
+ 2, 1, 3, 11, 4, 2, 1, 2, 60, 75, 7, 1, 60, 76, 22, 1, 11, 7, 7, 0,
+ 30, 0, 50, 55, 60, 78, 15, 2, 13, 1, 13, 2, 16, 11, 9, 2, 1, 2, 60, 79,
+ 13, 3, 11, 7, 13, 3, 18, 7, 7, 1, 37, 26, 60, 80, 13, 3, 13, 3, 18, 7,
+ 13, 4, 7, 1, 16, 26, 60, 81, 13, 2, 7, 1, 37, 23, 2, 5, 1, 60, 82, 60,
+ 77, 13, 1, 13, 2, 16, 54, 64, 60, 84, 7, 1, 23, 2, 60, 85, 13, 3, 11, 10,
+ 22, 0, 26, 50, 60, 60, 87, 15, 2, 13, 3, 13, 2, 16, 11, 11, 2, 1, 2, 60,
+ 88, 13, 3, 13, 2, 13, 4, 7, 1, 16, 26, 60, 89, 13, 3, 18, 10, 13, 2, 13,
+ 4, 7, 2, 16, 46, 5, 13, 4, 7, 1, 16, 26, 60, 90, 13, 2, 7, 1, 37, 23,
+ 2, 5, 1, 60, 91, 60, 86, 13, 3, 13, 2, 16, 54, 69, 60, 92, 15, 12, 13, 3,
+ 3, 4, 1, 60, 93, 0, 0, 0, 0, 8, 0, 0, 0, 73, 0, 0, 0, 2, 98, 0,
+ 0, 0, 0, 74, 0, 0, 0, 2,116, 0, 0, 0, 0, 75, 0, 0, 0, 2,105, 0,
+ 0, 0, 0, 76, 0, 0, 0, 2,101, 0, 0, 0, 0, 78, 0, 0, 0, 3,116,116,
+ 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 0, 87, 0, 0, 0, 2,116, 0, 0,
+ 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 13, 2, 0, 0, 0, 2, 98, 0, 2, 0,
+ 0, 0, 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0, 0, 0,
+ 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,
+105, 0, 2, 0, 0, 0, 2,101, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0,
+ 3,116,116, 0, 2, 0, 0, 0, 2, 61, 0, 2, 0, 0, 0, 7,108,110, 97,109,
+101,115, 0, 2, 0, 0, 0, 2, 64, 0, 2, 0, 0, 0, 11, 95, 69,110,117,109,
+101,114, 97,116,101, 0,
+};
+
+/* variable.lo */
+static char B11[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 14, 64,118, 97,114,105, 97,
+ 98,108,101, 46,108,117, 97, 0, 0, 0, 0, 87, 3, 0, 60, 18, 22, 1, 60, 19,
+ 11, 1, 15, 2, 30, 0, 60, 20, 25, 0, 60, 22, 15, 3, 15, 0, 15, 4, 2, 0,
+ 2, 60, 25, 15, 0, 11, 5, 11, 6, 26, 60, 37, 15, 0, 11, 7, 11, 8, 26, 60,
+ 48, 15, 0, 11, 9, 11, 10, 26, 60,154, 15, 0, 11, 11, 11, 12, 26, 60,171, 15,
+ 0, 11, 13, 11, 14, 26, 60,179, 11, 16, 25, 15, 60,188, 11, 18, 25, 17, 0, 0,
+ 0, 0, 0, 0, 0, 0, 19, 2, 0, 0, 0, 14, 99,108, 97,115,115, 86, 97,114,
+105, 97, 98,108,101, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0,
+ 0, 17, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 2,
+ 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,116, 97,103, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0,
+ 0, 25, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97, 0,
+ 0, 0, 0,152, 6, 3, 60, 26, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 27,
+ 15, 2, 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 28,
+ 15, 2, 13, 1, 11, 8, 42, 13, 0, 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 29,
+ 15, 2, 13, 1, 11, 10, 42, 13, 0, 18, 11, 42, 11, 7, 42, 2, 0, 1, 60, 30,
+ 15, 2, 13, 1, 11, 12, 42, 13, 0, 18, 13, 42, 11, 7, 42, 2, 0, 1, 60, 31,
+ 15, 2, 13, 1, 11, 14, 42, 13, 0, 18, 15, 42, 11, 7, 42, 2, 0, 1, 60, 32,
+ 15, 2, 13, 1, 11, 16, 42, 13, 0, 18, 17, 42, 11, 7, 42, 2, 0, 1, 60, 33,
+ 15, 2, 13, 1, 11, 18, 42, 13, 2, 42, 2, 0, 1, 60, 34, 0, 0, 0, 0, 3,
+ 0, 0, 0, 25, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 25, 0, 0, 0,
+ 6,105,100,101,110,116, 0, 0, 0, 0, 25, 0, 0, 0, 6, 99,108,111,115,101,
+ 0, 0, 0, 0, 19, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0,
+ 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0,
+ 0, 0, 10, 86, 97,114,105, 97, 98,108,101,123, 0, 2, 0, 0, 0, 10, 32,109,
+111,100, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0,
+ 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 10,
+ 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,
+112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61, 32, 39, 0, 2,
+ 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 10, 32,100,101,102, 32, 32,
+ 61, 32, 39, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 10, 32,114,
+101,116, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0,
+ 0, 2,125, 0, 2, 0, 0, 0, 9,103,101,116,118, 97,108,117,101, 0, 4, 0,
+ 0, 0, 37, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97,
+ 0, 0, 0, 0, 60, 5, 3, 60, 38, 13, 1, 48, 2, 13, 2, 52, 16, 60, 39, 13,
+ 1, 11, 2, 42, 13, 0, 18, 4, 42, 1, 3, 50, 29, 60, 40, 13, 1, 52, 13, 60,
+ 41, 11, 5, 13, 0, 18, 4, 42, 1, 3, 50, 10, 60, 43, 13, 0, 18, 4, 1, 3,
+ 60, 44, 60, 45, 0, 0, 0, 0, 3, 0, 0, 0, 37, 0, 0, 0, 5,115,101,108,
+102, 0, 0, 0, 0, 37, 0, 0, 0, 6, 99,108, 97,115,115, 0, 0, 0, 0, 37,
+ 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 0, 0, 0, 6, 2, 0, 0, 0, 6,
+ 99,108, 97,115,115, 0, 2, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 2, 0,
+ 0, 0, 3, 58, 58, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 5,110, 97,109,101, 0, 2, 0, 0, 0, 7,115,101,108,102, 45, 62, 0, 2, 0,
+ 0, 0, 8,115,117,112, 99,111,100,101, 0, 4, 0, 0, 0, 48, 0, 0, 0, 14,
+ 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97, 0, 0, 0, 3,189, 17, 1,
+ 60, 49, 13, 0, 20, 2, 2, 1, 1, 60, 52, 13, 1, 52, 21, 60, 53, 15, 3, 11,
+ 4, 13, 0, 18, 5, 11, 6, 13, 1, 11, 7, 2, 0, 5, 50, 17, 60, 55, 15, 3,
+ 11, 4, 13, 0, 18, 5, 11, 7, 2, 0, 3, 60, 56, 60, 57, 13, 0, 11, 8, 13,
+ 0, 20, 9, 11, 10, 2, 1, 2, 26, 60, 58, 15, 3, 11, 11, 13, 0, 18, 8, 11,
+ 12, 2, 0, 3, 60, 59, 15, 3, 11, 13, 2, 0, 1, 60, 62, 15, 16, 13, 0, 18,
+ 17, 11, 18, 2, 3, 2, 60, 63, 13, 1, 48, 5, 13, 4, 4, 0, 32, 52, 39, 60,
+ 64, 15, 3, 11, 19, 13, 1, 11, 20, 11, 21, 2, 0, 4, 60, 65, 15, 3, 11, 22,
+ 13, 1, 11, 23, 2, 0, 3, 60, 66, 15, 3, 11, 24, 2, 0, 1, 50, 35, 60, 67,
+ 13, 4, 52, 27, 60, 68, 13, 0, 11, 17, 15, 16, 13, 0, 18, 17, 11, 25, 2, 3,
+ 2, 27, 2, 23, 3, 23, 3, 5, 2, 50, 2, 60, 69, 60, 73, 13, 1, 48, 5, 13,
+ 4, 4, 0, 32, 52, 19, 60, 74, 15, 3, 11, 26, 13, 0, 18, 5, 42, 11, 27, 42,
+ 2, 0, 1, 50, 2, 60, 75, 60, 78, 15, 29, 13, 0, 18, 30, 2, 1, 1, 60, 79,
+ 13, 5, 11, 31, 32, 52, 32, 60, 80, 15, 3, 11, 32, 13, 5, 42, 11, 33, 42, 13,
+ 0, 20, 34, 13, 1, 13, 4, 2, 1, 3, 42, 11, 35, 42, 2, 0, 1, 50,124, 60,
+ 81, 13, 5, 52, 32, 60, 82, 15, 3, 11, 32, 13, 5, 42, 11, 22, 42, 13, 0, 20,
+ 34, 13, 1, 13, 4, 2, 1, 3, 42, 11, 35, 42, 2, 0, 1, 50, 86, 60, 84, 13,
+ 0, 18, 36, 11, 37, 32, 46, 7, 13, 0, 18, 36, 11, 38, 32, 52, 32, 60, 85, 15,
+ 3, 11, 39, 13, 0, 20, 34, 13, 1, 13, 4, 2, 1, 3, 42, 11, 40, 42, 13, 0,
+ 18, 41, 11, 35, 2, 0, 3, 50, 32, 60, 87, 15, 3, 11, 42, 13, 0, 20, 34, 13,
+ 1, 13, 4, 2, 1, 3, 42, 11, 40, 42, 13, 0, 18, 41, 11, 35, 2, 0, 3, 60,
+ 88, 60, 89, 60, 90, 15, 3, 11, 43, 2, 0, 1, 60, 91, 15, 3, 11, 44, 2, 0,
+ 1, 60, 94, 15, 16, 13, 0, 18, 17, 11, 45, 2, 1, 2, 44, 51, 2, 0, 60, 95,
+ 13, 1, 52, 21, 60, 96, 15, 3, 11, 46, 13, 0, 18, 5, 11, 6, 13, 1, 11, 7,
+ 2, 0, 5, 50, 17, 60, 98, 15, 3, 11, 46, 13, 0, 18, 5, 11, 7, 2, 0, 3,
+ 60, 99, 60,100, 13, 0, 11, 47, 13, 0, 20, 9, 11, 48, 2, 1, 2, 26, 60,101,
+ 15, 3, 11, 11, 13, 0, 18, 47, 11, 12, 2, 0, 3, 60,102, 15, 3, 11, 13, 2,
+ 0, 1, 60,105, 7, 1, 60,106, 13, 1, 48, 5, 13, 4, 4, 0, 32, 52, 65, 60,
+107, 15, 3, 11, 19, 13, 1, 11, 20, 11, 21, 2, 0, 4, 60,108, 15, 3, 11, 22,
+ 13, 1, 11, 23, 2, 0, 3, 60,109, 15, 3, 11, 24, 2, 0, 1, 60,111, 15, 3,
+ 11, 26, 13, 0, 18, 5, 42, 11, 27, 42, 2, 0, 1, 60,112, 13, 6, 7, 1, 37,
+ 23, 6, 50, 44, 60,113, 13, 4, 52, 36, 60,114, 13, 0, 11, 17, 15, 16, 13, 0,
+ 18, 17, 11, 25, 2, 3, 2, 27, 2, 23, 3, 23, 3, 5, 2, 60,115, 13, 6, 7,
+ 1, 37, 23, 6, 50, 2, 60,116, 60,119, 15, 3, 11, 50, 13, 0, 20, 51, 13, 6,
+ 2, 1, 2, 42, 11, 52, 42, 2, 0, 1, 60,120, 15, 3, 11, 53, 2, 0, 1, 60,
+123, 11, 38, 60,124, 13, 0, 18, 36, 11, 38, 31, 52, 4, 11, 20, 23, 7, 60,125,
+ 15, 3, 11, 19, 2, 0, 1, 60,126, 13, 1, 48, 2, 13, 4, 52, 19, 60,127, 15,
+ 3, 13, 1, 11, 54, 42, 13, 0, 18, 5, 42, 2, 0, 1, 50, 35, 60,128, 13, 1,
+ 52, 16, 60,129, 15, 3, 11, 55, 13, 0, 18, 5, 42, 2, 0, 1, 50, 13, 60,131,
+ 15, 3, 13, 0, 18, 5, 2, 0, 1, 60,132, 60,133, 15, 29, 13, 0, 18, 30, 2,
+ 1, 1, 60,134, 15, 3, 11, 56, 2, 0, 1, 60,135, 13, 8, 44, 48, 5, 13, 7,
+ 11, 38, 32, 52, 7, 15, 3, 11, 20, 2, 0, 1, 60,136, 15, 3, 11, 57, 13, 0,
+ 18, 17, 13, 0, 18, 30, 2, 0, 3, 60,137, 13, 8, 44, 52, 11, 60,138, 15, 3,
+ 11, 20, 2, 0, 1, 50, 2, 60,139, 60,140, 15, 3, 11, 58, 2, 0, 1, 60,141,
+ 7, 0, 60,142, 13, 0, 18, 59, 11, 38, 31, 52, 6, 13, 0, 18, 59, 23, 9, 60,
+143, 13, 8, 52, 24, 60,144, 15, 3, 11, 60, 13, 8, 42, 11, 22, 13, 6, 11, 40,
+ 13, 9, 11, 61, 2, 0, 6, 50, 19, 60,146, 15, 3, 11, 62, 13, 6, 11, 40, 13,
+ 9, 11, 61, 2, 0, 5, 60,147, 60,148, 15, 3, 11, 43, 2, 0, 1, 60,149, 15,
+ 3, 11, 44, 2, 0, 1, 5, 4, 50, 2, 60,150, 60,152, 0, 0, 0, 0, 14, 0,
+ 0, 0, 48, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 49, 0, 0, 0, 6,
+ 99,108, 97,115,115, 0, 0, 0, 0, 62, 0, 0, 0, 2, 95, 0, 0, 0, 0, 62,
+ 0, 0, 0, 2, 95, 0, 0, 0, 0, 62, 0, 0, 0, 7,115,116, 97,116,105, 99,
+ 0, 0, 0, 0, 78, 0, 0, 0, 2,116, 0, 0, 0, 0,105, 0, 0, 0, 5,110,
+ 97,114,103, 0, 0, 0, 0,123, 0, 0, 0, 4,112,116,114, 0, 0, 0, 0,133,
+ 0, 0, 0, 2,116, 0, 0, 0, 0,141, 0, 0, 0, 4,100,101,102, 0, 0, 0,
+ 0,149, 0, 0, 0, 0, 0, 0, 0,149, 0, 0, 0, 0, 0, 0, 0,149, 0, 0,
+ 0, 0, 0, 0, 0,149, 0, 0, 0, 0, 0, 0, 0, 63, 2, 0, 0, 0, 6, 99,
+108, 97,115,115, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,
+105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0,
+ 2, 0, 0, 0, 17, 47, 42, 32,103,101,116, 32,102,117,110, 99,116,105,111,110,
+ 58, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32,111,102,
+ 32, 99,108, 97,115,115, 32, 0, 2, 0, 0, 0, 4, 32, 42, 47, 0, 2, 0, 0,
+ 0, 9, 99,103,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,110,
+ 99,110, 97,109,101, 0, 2, 0, 0, 0, 11,116,111,108,117, 97, 73, 95,103,101,
+116, 0, 2, 0, 0, 0, 12,115,116, 97,116,105, 99, 32,118,111,105,100, 0, 2,
+ 0, 0, 0, 7, 40,118,111,105,100, 41, 0, 2, 0, 0, 0, 2,123, 0, 2, 0,
+ 0, 0, 2, 95, 0, 2, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 2, 0, 0,
+ 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2,
+ 0, 0, 0, 13, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41, 0, 2, 0, 0,
+ 0, 2, 32, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0, 0, 0, 8,115,101,108,102,
+ 32, 61, 32, 0, 2, 0, 0, 0, 2, 40, 0, 2, 0, 0, 0, 4, 42, 41, 32, 0,
+ 2, 0, 0, 0, 24,116,111,108,117, 97, 95,103,101,116,117,115,101,114,116,121,
+112,101, 40, 49, 44, 48, 41, 59, 0, 2, 0, 0, 0, 20, 94, 37,115, 42,115,116,
+ 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 0, 2, 0, 0, 0, 65, 32,
+ 32,105,102, 32, 40, 33,115,101,108,102, 41, 32,116,111,108,117, 97, 95,101,114,
+114,111,114, 40, 34,105,110,118, 97,108,105,100, 32, 39,115,101,108,102, 39, 32,
+105,110, 32, 97, 99, 99,101,115,115,105,110,103, 32,118, 97,114,105, 97, 98,108,
+101, 32, 39, 0, 2, 0, 0, 0, 5, 39, 34, 41, 59, 0, 2, 0, 0, 0, 2,116,
+ 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 5,116,
+121,112,101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0, 0,
+ 13, 32, 32,116,111,108,117, 97, 95,112,117,115,104, 0, 2, 0, 0, 0, 10, 40,
+ 40,100,111,117, 98,108,101, 41, 0, 2, 0, 0, 0, 9,103,101,116,118, 97,108,
+117,101, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2, 0, 0, 0, 4,112,116,114, 0,
+ 2, 0, 0, 0, 2, 38, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 30, 32, 32,
+116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,101, 40, 40,
+118,111,105,100, 42, 41, 38, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 4,
+116, 97,103, 0, 2, 0, 0, 0, 29, 32, 32,116,111,108,117, 97, 95,112,117,115,
+104,117,115,101,114,116,121,112,101, 40, 40,118,111,105,100, 42, 41, 0, 2, 0,
+ 0, 0, 2,125, 0, 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 6, 99,111,110,
+115,116, 0, 2, 0, 0, 0, 17, 47, 42, 32,115,101,116, 32,102,117,110, 99,116,
+105,111,110, 58, 0, 2, 0, 0, 0, 9, 99,115,101,116,110, 97,109,101, 0, 2,
+ 0, 0, 0, 11,116,111,108,117, 97, 73, 95,115,101,116, 0, 2, 0, 0, 0, 5,
+110, 97,114,103, 0, 2, 0, 0, 0, 8, 32, 32,105,102, 32, 40, 33, 0, 2, 0,
+ 0, 0, 13,111,117,116, 99,104,101, 99,107,116,121,112,101, 0, 2, 0, 0, 0,
+ 2, 41, 0, 2, 0, 0, 0, 58, 32, 32, 32,116,111,108,117, 97, 95,101,114,114,
+111,114, 40, 34, 35,118,105,110,118, 97,108,105,100, 32,116,121,112,101, 32,105,
+110, 32,118, 97,114,105, 97, 98,108,101, 32, 97,115,115,105,103,110,109,101,110,
+116, 46, 34, 41, 59, 0, 2, 0, 0, 0, 3, 58, 58, 0, 2, 0, 0, 0, 7,115,
+101,108,102, 45, 62, 0, 2, 0, 0, 0, 4, 32, 61, 32, 0, 2, 0, 0, 0, 3,
+ 40, 40, 0, 2, 0, 0, 0, 3, 41, 32, 0, 2, 0, 0, 0, 4,100,101,102, 0,
+ 2, 0, 0, 0, 10,116,111,108,117, 97, 95,103,101,116, 0, 2, 0, 0, 0, 4,
+ 41, 41, 59, 0, 2, 0, 0, 0, 19,116,111,108,117, 97, 95,103,101,116,117,115,
+101,114,116,121,112,101, 40, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,101,
+114, 0, 4, 0, 0, 0,154, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101,
+ 46,108,117, 97, 0, 0, 0, 0,185, 5, 1, 60,155, 13, 0, 20, 2, 2, 1, 1,
+ 46, 7, 13, 0, 20, 3, 2, 1, 1, 60,156, 13, 1, 52, 84, 60,157, 13, 0, 18,
+ 4, 52, 41, 60,158, 15, 5, 11, 6, 13, 1, 42, 11, 7, 42, 13, 0, 18, 8, 42,
+ 11, 9, 42, 13, 0, 18, 10, 42, 11, 11, 42, 13, 0, 18, 4, 42, 11, 12, 42, 2,
+ 0, 1, 50, 33, 60,160, 15, 5, 11, 6, 13, 1, 42, 11, 7, 42, 13, 0, 18, 8,
+ 42, 11, 9, 42, 13, 0, 18, 10, 42, 11, 13, 42, 2, 0, 1, 60,161, 50, 72, 60,
+163, 13, 0, 18, 4, 52, 35, 60,164, 15, 5, 11, 14, 13, 0, 18, 8, 42, 11, 9,
+ 42, 13, 0, 18, 10, 42, 11, 11, 42, 13, 0, 18, 4, 42, 11, 12, 42, 2, 0, 1,
+ 50, 27, 60,166, 15, 5, 11, 14, 13, 0, 18, 8, 42, 11, 9, 42, 13, 0, 18, 10,
+ 42, 11, 13, 42, 2, 0, 1, 60,167, 60,168, 60,169, 0, 0, 0, 0, 2, 0, 0,
+ 0,154, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,155, 0, 0, 0, 7,112,
+ 97,114,101,110,116, 0, 0, 0, 0, 15, 2, 0, 0, 0, 7,112, 97,114,101,110,
+116, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,
+108, 97,115,115, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2,
+ 0, 0, 0, 9, 99,115,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 7,111,117,
+116,112,117,116, 0, 2, 0, 0, 0, 18, 32,116,111,108,117, 97, 95,116, 97, 98,
+108,101,118, 97,114, 40, 34, 0, 2, 0, 0, 0, 4, 34, 44, 34, 0, 2, 0, 0,
+ 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 3, 34, 44, 0, 2, 0, 0, 0,
+ 9, 99,103,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0,
+ 0, 3, 41, 59, 0, 2, 0, 0, 0, 8, 44, 78, 85, 76, 76, 41, 59, 0, 2, 0,
+ 0, 0, 19, 32,116,111,108,117, 97, 95,103,108,111, 98, 97,108,118, 97,114, 40,
+ 34, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,101,114, 0, 4, 0,
+ 0, 0,171, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97,
+ 0, 0, 0, 0, 52, 4, 1, 60,172, 13, 0, 20, 1, 2, 1, 1, 4, 0, 32, 48,
+ 10, 13, 0, 20, 2, 2, 1, 1, 4, 0, 32, 52, 19, 60,173, 15, 3, 11, 4, 13,
+ 0, 18, 5, 42, 11, 6, 42, 2, 0, 1, 50, 2, 60,174, 60,175, 0, 0, 0, 0,
+ 1, 0, 0, 0,171, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 7, 2, 0,
+ 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115,
+ 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0, 7,
+111,117,116,112,117,116, 0, 2, 0, 0, 0, 35, 32,108,117, 97, 95,112,117,115,
+104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,114, 97,119,115,101,116,103,108,
+111, 98, 97,108, 40, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0,
+ 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 10, 95, 86, 97,114,105, 97, 98,108,
+101, 0, 4, 0, 0, 0,179, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101,
+ 46,108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60,180, 13, 0, 11, 1, 15, 2, 26,
+ 60,181, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60,182, 15, 5, 13, 0, 2, 0, 1,
+ 60,183, 13, 0, 1, 1, 60,184, 0, 0, 0, 0, 1, 0, 0, 0,179, 0, 0, 0,
+ 2,116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95,
+ 98, 97,115,101, 0, 2, 0, 0, 0, 14, 99,108, 97,115,115, 86, 97,114,105, 97,
+ 98,108,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0,
+ 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 7, 97,112,112,101,
+110,100, 0, 2, 0, 0, 0, 9, 86, 97,114,105, 97, 98,108,101, 0, 4, 0, 0,
+ 0,188, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97, 0,
+ 0, 0, 0, 21, 5, 1, 60,189, 15, 1, 15, 2, 13, 0, 11, 3, 2, 1, 2, 3,
+ 1, 1, 60,190, 0, 0, 0, 0, 1, 0, 0, 0,188, 0, 0, 0, 2,115, 0, 0,
+ 0, 0, 4, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 10, 95, 86, 97,114,105,
+ 97, 98,108,101, 0, 2, 0, 0, 0, 12, 68,101, 99,108, 97,114, 97,116,105,111,
+110, 0, 2, 0, 0, 0, 4,118, 97,114, 0,
+};
+
+/* array.lo */
+static char B12[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 11, 64, 97,114,114, 97,121,
+ 46,108,117, 97, 0, 0, 0, 0, 87, 3, 0, 60, 18, 22, 1, 60, 19, 11, 1, 15,
+ 2, 30, 0, 60, 20, 25, 0, 60, 22, 15, 3, 15, 0, 15, 4, 2, 0, 2, 60, 25,
+ 15, 0, 11, 5, 11, 6, 26, 60, 38, 15, 0, 11, 7, 11, 8, 26, 60, 49, 15, 0,
+ 11, 9, 11, 10, 26, 60,167, 15, 0, 11, 11, 11, 12, 26, 60,184, 15, 0, 11, 13,
+ 11, 14, 26, 60,192, 11, 16, 25, 15, 60,201, 11, 18, 25, 17, 0, 0, 0, 0, 0,
+ 0, 0, 0, 19, 2, 0, 0, 0, 11, 99,108, 97,115,115, 65,114,114, 97,121, 0,
+ 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 17, 99,108, 97,115,
+115, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 2, 0, 0, 0, 7,115,101,
+116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0,
+ 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 25, 0, 0, 0, 11,
+ 64, 97,114,114, 97,121, 46,108,117, 97, 0, 0, 0, 0,172, 6, 3, 60, 26, 15,
+ 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 27, 15, 2, 13, 1, 11, 4, 42, 13, 0,
+ 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 28, 15, 2, 13, 1, 11, 8, 42, 13, 0,
+ 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 29, 15, 2, 13, 1, 11, 10, 42, 13, 0,
+ 18, 11, 42, 11, 7, 42, 2, 0, 1, 60, 30, 15, 2, 13, 1, 11, 12, 42, 13, 0,
+ 18, 13, 42, 11, 7, 42, 2, 0, 1, 60, 31, 15, 2, 13, 1, 11, 14, 42, 13, 0,
+ 18, 15, 42, 11, 7, 42, 2, 0, 1, 60, 32, 15, 2, 13, 1, 11, 16, 42, 13, 0,
+ 18, 17, 42, 11, 7, 42, 2, 0, 1, 60, 33, 15, 2, 13, 1, 11, 18, 42, 13, 0,
+ 18, 19, 42, 11, 7, 42, 2, 0, 1, 60, 34, 15, 2, 13, 1, 11, 20, 42, 13, 2,
+ 42, 2, 0, 1, 60, 35, 0, 0, 0, 0, 3, 0, 0, 0, 25, 0, 0, 0, 5,115,
+101,108,102, 0, 0, 0, 0, 25, 0, 0, 0, 6,105,100,101,110,116, 0, 0, 0,
+ 0, 25, 0, 0, 0, 6, 99,108,111,115,101, 0, 0, 0, 0, 21, 2, 0, 0, 0,
+ 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0,
+ 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 7, 65,114,114, 97,121,123,
+ 0, 2, 0, 0, 0, 10, 32,109,111,100, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0,
+ 5,115,101,108,102, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3,
+ 39, 44, 0, 2, 0, 0, 0, 10, 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0,
+ 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61,
+ 32, 39, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,
+109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0,
+ 0, 10, 32,100,101,102, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,100,101,102,
+ 0, 2, 0, 0, 0, 10, 32,100,105,109, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0,
+ 4,100,105,109, 0, 2, 0, 0, 0, 10, 32,114,101,116, 32, 32, 61, 32, 39, 0,
+ 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0,
+ 9,103,101,116,118, 97,108,117,101, 0, 4, 0, 0, 0, 38, 0, 0, 0, 11, 64,
+ 97,114,114, 97,121, 46,108,117, 97, 0, 0, 0, 0, 69, 5, 3, 60, 39, 13, 1,
+ 48, 2, 13, 2, 52, 19, 60, 40, 13, 1, 11, 2, 42, 13, 0, 18, 4, 42, 11, 5,
+ 42, 1, 3, 50, 35, 60, 41, 13, 1, 52, 16, 60, 42, 11, 6, 13, 0, 18, 4, 42,
+ 11, 5, 42, 1, 3, 50, 13, 60, 44, 13, 0, 18, 4, 11, 5, 42, 1, 3, 60, 45,
+ 60, 46, 0, 0, 0, 0, 3, 0, 0, 0, 38, 0, 0, 0, 5,115,101,108,102, 0,
+ 0, 0, 0, 38, 0, 0, 0, 6, 99,108, 97,115,115, 0, 0, 0, 0, 38, 0, 0,
+ 0, 7,115,116, 97,116,105, 99, 0, 0, 0, 0, 7, 2, 0, 0, 0, 6, 99,108,
+ 97,115,115, 0, 2, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 2, 0, 0, 0,
+ 3, 58, 58, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110,
+ 97,109,101, 0, 2, 0, 0, 0, 15, 91,116,111,108,117, 97, 73, 95,105,110,100,
+101,120, 93, 0, 2, 0, 0, 0, 7,115,101,108,102, 45, 62, 0, 2, 0, 0, 0,
+ 8,115,117,112, 99,111,100,101, 0, 4, 0, 0, 0, 49, 0, 0, 0, 11, 64, 97,
+114,114, 97,121, 46,108,117, 97, 0, 0, 0, 4, 21, 17, 1, 60, 50, 13, 0, 20,
+ 2, 2, 1, 1, 60, 53, 13, 1, 52, 21, 60, 54, 15, 3, 11, 4, 13, 0, 18, 5,
+ 11, 6, 13, 1, 11, 7, 2, 0, 5, 50, 17, 60, 56, 15, 3, 11, 4, 13, 0, 18,
+ 5, 11, 7, 2, 0, 3, 60, 57, 60, 58, 13, 0, 11, 8, 13, 0, 20, 9, 11, 10,
+ 2, 1, 2, 26, 60, 59, 15, 3, 11, 11, 13, 0, 18, 8, 11, 12, 2, 0, 3, 60,
+ 60, 15, 3, 11, 13, 2, 0, 1, 60, 63, 15, 3, 11, 14, 2, 0, 1, 60, 66, 15,
+ 17, 13, 0, 18, 18, 11, 19, 2, 3, 2, 60, 67, 13, 1, 48, 5, 13, 4, 4, 0,
+ 32, 52, 66, 60, 68, 15, 3, 11, 20, 13, 1, 11, 21, 11, 22, 2, 0, 4, 60, 69,
+ 15, 3, 11, 23, 2, 0, 1, 60, 70, 15, 3, 11, 24, 2, 0, 1, 60, 71, 15, 3,
+ 11, 25, 2, 0, 1, 60, 72, 15, 3, 11, 26, 13, 1, 11, 27, 2, 0, 3, 60, 73,
+ 15, 3, 11, 28, 2, 0, 1, 50, 35, 60, 74, 13, 4, 52, 27, 60, 75, 13, 0, 11,
+ 18, 15, 17, 13, 0, 18, 18, 11, 29, 2, 3, 2, 27, 2, 23, 3, 23, 3, 5, 2,
+ 50, 2, 60, 76, 60, 79, 15, 3, 11, 30, 2, 0, 1, 60, 80, 15, 3, 11, 31, 2,
+ 0, 1, 60, 81, 15, 3, 11, 32, 2, 0, 1, 60, 82, 15, 3, 11, 33, 13, 0, 18,
+ 34, 42, 11, 35, 42, 2, 0, 1, 60, 83, 15, 3, 11, 36, 2, 0, 1, 60, 86, 15,
+ 38, 13, 0, 18, 39, 2, 1, 1, 60, 87, 13, 5, 11, 40, 32, 52, 32, 60, 88, 15,
+ 3, 11, 41, 13, 5, 42, 11, 42, 42, 13, 0, 20, 43, 13, 1, 13, 4, 2, 1, 3,
+ 42, 11, 44, 42, 2, 0, 1, 50,124, 60, 89, 13, 5, 52, 32, 60, 90, 15, 3, 11,
+ 41, 13, 5, 42, 11, 26, 42, 13, 0, 20, 43, 13, 1, 13, 4, 2, 1, 3, 42, 11,
+ 44, 42, 2, 0, 1, 50, 86, 60, 92, 13, 0, 18, 45, 11, 46, 32, 46, 7, 13, 0,
+ 18, 45, 11, 47, 32, 52, 32, 60, 93, 15, 3, 11, 48, 13, 0, 20, 43, 13, 1, 13,
+ 4, 2, 1, 3, 42, 11, 49, 42, 13, 0, 18, 50, 11, 44, 2, 0, 3, 50, 32, 60,
+ 95, 15, 3, 11, 51, 13, 0, 20, 43, 13, 1, 13, 4, 2, 1, 3, 42, 11, 49, 42,
+ 13, 0, 18, 50, 11, 44, 2, 0, 3, 60, 96, 60, 97, 60, 98, 15, 3, 11, 52, 2,
+ 0, 1, 60, 99, 15, 3, 11, 53, 2, 0, 1, 60,102, 15, 17, 13, 0, 18, 18, 11,
+ 54, 2, 1, 2, 44, 51, 2, 33, 60,103, 13, 1, 52, 21, 60,104, 15, 3, 11, 55,
+ 13, 0, 18, 5, 11, 6, 13, 1, 11, 7, 2, 0, 5, 50, 17, 60,106, 15, 3, 11,
+ 55, 13, 0, 18, 5, 11, 7, 2, 0, 3, 60,107, 60,108, 13, 0, 11, 56, 13, 0,
+ 20, 9, 11, 57, 2, 1, 2, 26, 60,109, 15, 3, 11, 11, 13, 0, 18, 56, 11, 12,
+ 2, 0, 3, 60,110, 15, 3, 11, 13, 2, 0, 1, 60,113, 15, 3, 11, 14, 2, 0,
+ 1, 60,116, 15, 17, 13, 0, 18, 18, 11, 19, 2, 3, 2, 60,117, 13, 1, 48, 5,
+ 13, 8, 4, 0, 32, 52, 66, 60,118, 15, 3, 11, 20, 13, 1, 11, 21, 11, 22, 2,
+ 0, 4, 60,119, 15, 3, 11, 23, 2, 0, 1, 60,120, 15, 3, 11, 24, 2, 0, 1,
+ 60,121, 15, 3, 11, 25, 2, 0, 1, 60,122, 15, 3, 11, 26, 13, 1, 11, 27, 2,
+ 0, 3, 60,123, 15, 3, 11, 28, 2, 0, 1, 50, 35, 60,124, 13, 8, 52, 27, 60,
+125, 13, 0, 11, 18, 15, 17, 13, 0, 18, 18, 11, 29, 2, 3, 2, 27, 2, 23, 7,
+ 23, 7, 5, 2, 50, 2, 60,126, 60,129, 15, 3, 11, 30, 2, 0, 1, 60,130, 15,
+ 3, 11, 31, 2, 0, 1, 60,131, 15, 3, 11, 32, 2, 0, 1, 60,132, 15, 3, 11,
+ 33, 13, 0, 18, 34, 42, 11, 35, 42, 2, 0, 1, 60,133, 15, 3, 11, 36, 2, 0,
+ 1, 60,136, 11, 47, 60,137, 13, 0, 18, 45, 11, 47, 31, 52, 4, 11, 21, 23, 9,
+ 60,138, 15, 3, 11, 20, 2, 0, 1, 60,139, 13, 1, 48, 2, 13, 8, 52, 22, 60,
+140, 15, 3, 13, 1, 11, 58, 42, 13, 0, 18, 5, 42, 11, 59, 42, 2, 0, 1, 50,
+ 41, 60,141, 13, 1, 52, 19, 60,142, 15, 3, 11, 60, 13, 0, 18, 5, 42, 11, 59,
+ 42, 2, 0, 1, 50, 16, 60,144, 15, 3, 13, 0, 18, 5, 11, 59, 42, 2, 0, 1,
+ 60,145, 60,146, 15, 38, 13, 0, 18, 39, 2, 1, 1, 60,147, 15, 3, 11, 61, 2,
+ 0, 1, 60,148, 13, 10, 44, 48, 5, 13, 9, 11, 47, 32, 52, 7, 15, 3, 11, 21,
+ 2, 0, 1, 60,149, 15, 3, 11, 62, 13, 0, 18, 18, 13, 0, 18, 39, 2, 0, 3,
+ 60,150, 13, 10, 44, 52, 11, 60,151, 15, 3, 11, 21, 2, 0, 1, 50, 2, 60,152,
+ 60,153, 15, 3, 11, 63, 2, 0, 1, 60,154, 7, 0, 60,155, 13, 0, 18, 64, 11,
+ 47, 31, 52, 6, 13, 0, 18, 64, 23, 11, 60,156, 13, 10, 52, 20, 60,157, 15, 3,
+ 11, 65, 13, 10, 42, 11, 66, 13, 11, 11, 67, 2, 0, 4, 50, 15, 60,159, 15, 3,
+ 11, 68, 13, 11, 11, 67, 2, 0, 3, 60,160, 60,161, 15, 3, 11, 52, 2, 0, 1,
+ 60,162, 15, 3, 11, 53, 2, 0, 1, 5, 6, 50, 2, 60,163, 60,165, 0, 0, 0,
+ 0, 18, 0, 0, 0, 49, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 50, 0,
+ 0, 0, 6, 99,108, 97,115,115, 0, 0, 0, 0, 66, 0, 0, 0, 2, 95, 0, 0,
+ 0, 0, 66, 0, 0, 0, 2, 95, 0, 0, 0, 0, 66, 0, 0, 0, 7,115,116, 97,
+116,105, 99, 0, 0, 0, 0, 86, 0, 0, 0, 2,116, 0, 0, 0, 0,116, 0, 0,
+ 0, 2, 95, 0, 0, 0, 0,116, 0, 0, 0, 2, 95, 0, 0, 0, 0,116, 0, 0,
+ 0, 7,115,116, 97,116,105, 99, 0, 0, 0, 0,136, 0, 0, 0, 4,112,116,114,
+ 0, 0, 0, 0,146, 0, 0, 0, 2,116, 0, 0, 0, 0,154, 0, 0, 0, 4,100,
+101,102, 0, 0, 0, 0,162, 0, 0, 0, 0, 0, 0, 0,162, 0, 0, 0, 0, 0,
+ 0, 0,162, 0, 0, 0, 0, 0, 0, 0,162, 0, 0, 0, 0, 0, 0, 0,162, 0,
+ 0, 0, 0, 0, 0, 0,162, 0, 0, 0, 0, 0, 0, 0, 69, 2, 0, 0, 0, 6,
+ 99,108, 97,115,115, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 8,105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116,
+ 0, 2, 0, 0, 0, 17, 47, 42, 32,103,101,116, 32,102,117,110, 99,116,105,111,
+110, 58, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32,111,
+102, 32, 99,108, 97,115,115, 32, 0, 2, 0, 0, 0, 4, 32, 42, 47, 0, 2, 0,
+ 0, 0, 9, 99,103,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,
+110, 99,110, 97,109,101, 0, 2, 0, 0, 0, 11,116,111,108,117, 97, 73, 95,103,
+101,116, 0, 2, 0, 0, 0, 12,115,116, 97,116,105, 99, 32,118,111,105,100, 0,
+ 2, 0, 0, 0, 7, 40,118,111,105,100, 41, 0, 2, 0, 0, 0, 2,123, 0, 2,
+ 0, 0, 0, 19, 32,105,110,116, 32,116,111,108,117, 97, 73, 95,105,110,100,101,
+120, 59, 0, 2, 0, 0, 0, 2, 95, 0, 2, 0, 0, 0, 7,115,116, 97,116,105,
+ 99, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 4,
+109,111,100, 0, 2, 0, 0, 0, 13, 94, 37,115, 42, 40,115,116, 97,116,105, 99,
+ 41, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0, 0, 0,
+ 6,115,101,108,102, 59, 0, 2, 0, 0, 0, 34, 32,108,117, 97, 95,112,117,115,
+104,111, 98,106,101, 99,116, 40,108,117, 97, 95,103,101,116,112, 97,114, 97,109,
+ 40, 49, 41, 41, 59, 0, 2, 0, 0, 0, 26, 32,108,117, 97, 95,112,117,115,104,
+115,116,114,105,110,103, 40, 34, 46,115,101,108,102, 34, 41, 59, 0, 2, 0, 0,
+ 0, 9, 32,115,101,108,102, 32, 61, 32, 0, 2, 0, 0, 0, 2, 40, 0, 2, 0,
+ 0, 0, 4, 42, 41, 32, 0, 2, 0, 0, 0, 36,108,117, 97, 95,103,101,116,117,
+115,101,114,100, 97,116, 97, 40,108,117, 97, 95,114, 97,119,103,101,116,116, 97,
+ 98,108,101, 40, 41, 41, 59, 0, 2, 0, 0, 0, 20, 94, 37,115, 42,115,116, 97,
+116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 0, 2, 0, 0, 0, 42, 32,105,
+102, 32, 40, 33,116,111,108,117, 97, 95,105,115,116,121,112,101, 40, 50, 44,116,
+111,108,117, 97, 95,116, 97,103, 95,110,117,109, 98,101,114, 44, 48, 41, 41, 0,
+ 2, 0, 0, 0, 50, 32, 32,116,111,108,117, 97, 95,101,114,114,111,114, 40, 34,
+105,110,118, 97,108,105,100, 32,116,121,112,101, 32,105,110, 32, 97,114,114, 97,
+121, 32,105,110,100,101,120,105,110,103, 46, 34, 41, 59, 0, 2, 0, 0, 0, 45,
+ 32,116,111,108,117, 97, 73, 95,105,110,100,101,120, 32, 61, 32, 40,105,110,116,
+ 41,116,111,108,117, 97, 95,103,101,116,110,117,109, 98,101,114, 40, 50, 44, 48,
+ 41, 45, 49, 59, 0, 2, 0, 0, 0, 38, 32,105,102, 32, 40,116,111,108,117, 97,
+ 73, 95,105,110,100,101,120, 60, 48, 32,124,124, 32,116,111,108,117, 97, 73, 95,
+105,110,100,101,120, 62, 61, 0, 2, 0, 0, 0, 4,100,105,109, 0, 2, 0, 0,
+ 0, 2, 41, 0, 2, 0, 0, 0, 47, 32, 32,116,111,108,117, 97, 95,101,114,114,
+111,114, 40, 34, 97,114,114, 97,121, 32,105,110,100,101,120,105,110,103, 32,111,
+117,116, 32,111,102, 32,114, 97,110,103,101, 46, 34, 41, 59, 0, 2, 0, 0, 0,
+ 2,116, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0,
+ 5,116,121,112,101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0,
+ 0, 0, 13, 32, 32,116,111,108,117, 97, 95,112,117,115,104, 0, 2, 0, 0, 0,
+ 10, 40, 40,100,111,117, 98,108,101, 41, 0, 2, 0, 0, 0, 9,103,101,116,118,
+ 97,108,117,101, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2, 0, 0, 0, 4,112,116,
+114, 0, 2, 0, 0, 0, 2, 38, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 30,
+ 32, 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,101,
+ 40, 40,118,111,105,100, 42, 41, 38, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0,
+ 0, 4,116, 97,103, 0, 2, 0, 0, 0, 29, 32, 32,116,111,108,117, 97, 95,112,
+117,115,104,117,115,101,114,116,121,112,101, 40, 40,118,111,105,100, 42, 41, 0,
+ 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 6, 99,
+111,110,115,116, 0, 2, 0, 0, 0, 17, 47, 42, 32,115,101,116, 32,102,117,110,
+ 99,116,105,111,110, 58, 0, 2, 0, 0, 0, 9, 99,115,101,116,110, 97,109,101,
+ 0, 2, 0, 0, 0, 11,116,111,108,117, 97, 73, 95,115,101,116, 0, 2, 0, 0,
+ 0, 3, 58, 58, 0, 2, 0, 0, 0, 15, 91,116,111,108,117, 97, 73, 95,105,110,
+100,101,120, 93, 0, 2, 0, 0, 0, 7,115,101,108,102, 45, 62, 0, 2, 0, 0,
+ 0, 4, 32, 61, 32, 0, 2, 0, 0, 0, 3, 40, 40, 0, 2, 0, 0, 0, 3, 41,
+ 32, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,103,101,116, 0, 2, 0, 0, 0, 4, 40, 51, 44, 0, 2, 0, 0, 0, 4,
+ 41, 41, 59, 0, 2, 0, 0, 0, 21,116,111,108,117, 97, 95,103,101,116,117,115,
+101,114,116,121,112,101, 40, 51, 44, 0, 2, 0, 0, 0, 9,114,101,103,105,115,
+116,101,114, 0, 4, 0, 0, 0,167, 0, 0, 0, 11, 64, 97,114,114, 97,121, 46,
+108,117, 97, 0, 0, 0, 0,185, 5, 1, 60,168, 13, 0, 20, 2, 2, 1, 1, 46,
+ 7, 13, 0, 20, 3, 2, 1, 1, 60,169, 13, 1, 52, 84, 60,170, 13, 0, 18, 4,
+ 52, 41, 60,171, 15, 5, 11, 6, 13, 1, 42, 11, 7, 42, 13, 0, 18, 8, 42, 11,
+ 9, 42, 13, 0, 18, 10, 42, 11, 11, 42, 13, 0, 18, 4, 42, 11, 12, 42, 2, 0,
+ 1, 50, 33, 60,173, 15, 5, 11, 6, 13, 1, 42, 11, 7, 42, 13, 0, 18, 8, 42,
+ 11, 9, 42, 13, 0, 18, 10, 42, 11, 13, 42, 2, 0, 1, 60,174, 50, 72, 60,176,
+ 13, 0, 18, 4, 52, 35, 60,177, 15, 5, 11, 14, 13, 0, 18, 8, 42, 11, 9, 42,
+ 13, 0, 18, 10, 42, 11, 11, 42, 13, 0, 18, 4, 42, 11, 12, 42, 2, 0, 1, 50,
+ 27, 60,179, 15, 5, 11, 14, 13, 0, 18, 8, 42, 11, 9, 42, 13, 0, 18, 10, 42,
+ 11, 13, 42, 2, 0, 1, 60,180, 60,181, 60,182, 0, 0, 0, 0, 2, 0, 0, 0,
+167, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,168, 0, 0, 0, 7,112, 97,
+114,101,110,116, 0, 0, 0, 0, 15, 2, 0, 0, 0, 7,112, 97,114,101,110,116,
+ 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108,
+ 97,115,115, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0,
+ 0, 0, 9, 99,115,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 7,111,117,116,
+112,117,116, 0, 2, 0, 0, 0, 20, 32,116,111,108,117, 97, 95,116, 97, 98,108,
+101, 97,114,114, 97,121, 40, 34, 0, 2, 0, 0, 0, 4, 34, 44, 34, 0, 2, 0,
+ 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 3, 34, 44, 0, 2, 0, 0,
+ 0, 9, 99,103,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0,
+ 0, 0, 3, 41, 59, 0, 2, 0, 0, 0, 8, 44, 78, 85, 76, 76, 41, 59, 0, 2,
+ 0, 0, 0, 21, 32,116,111,108,117, 97, 95,103,108,111, 98, 97,108, 97,114,114,
+ 97,121, 40, 34, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,101,114,
+ 0, 4, 0, 0, 0,184, 0, 0, 0, 11, 64, 97,114,114, 97,121, 46,108,117, 97,
+ 0, 0, 0, 0, 52, 4, 1, 60,185, 13, 0, 20, 1, 2, 1, 1, 4, 0, 32, 48,
+ 10, 13, 0, 20, 2, 2, 1, 1, 4, 0, 32, 52, 19, 60,186, 15, 3, 11, 4, 13,
+ 0, 18, 5, 42, 11, 6, 42, 2, 0, 1, 50, 2, 60,187, 60,188, 0, 0, 0, 0,
+ 1, 0, 0, 0,184, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 7, 2, 0,
+ 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115,
+ 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0, 7,
+111,117,116,112,117,116, 0, 2, 0, 0, 0, 32, 32,108,117, 97, 95,112,117,115,
+104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98, 97,
+108, 40, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 4,
+ 34, 41, 59, 0, 2, 0, 0, 0, 7, 95, 65,114,114, 97,121, 0, 4, 0, 0, 0,
+192, 0, 0, 0, 11, 64, 97,114,114, 97,121, 46,108,117, 97, 0, 0, 0, 0, 40,
+ 4, 1, 60,193, 13, 0, 11, 1, 15, 2, 26, 60,194, 15, 3, 13, 0, 15, 4, 2,
+ 0, 2, 60,195, 15, 5, 13, 0, 2, 0, 1, 60,196, 13, 0, 1, 1, 60,197, 0,
+ 0, 0, 0, 1, 0, 0, 0,192, 0, 0, 0, 2,116, 0, 0, 0, 0, 6, 2, 0,
+ 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0,
+ 11, 99,108, 97,115,115, 65,114,114, 97,121, 0, 2, 0, 0, 0, 7,115,101,116,
+116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2,
+ 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0, 0, 6, 65,114,114, 97,
+121, 0, 4, 0, 0, 0,201, 0, 0, 0, 11, 64, 97,114,114, 97,121, 46,108,117,
+ 97, 0, 0, 0, 0, 21, 5, 1, 60,202, 15, 1, 15, 2, 13, 0, 11, 3, 2, 1,
+ 2, 3, 1, 1, 60,203, 0, 0, 0, 0, 1, 0, 0, 0,201, 0, 0, 0, 2,115,
+ 0, 0, 0, 0, 4, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 7, 95, 65,114,
+114, 97,121, 0, 2, 0, 0, 0, 12, 68,101, 99,108, 97,114, 97,116,105,111,110,
+ 0, 2, 0, 0, 0, 4,118, 97,114, 0,
+};
+
+/* function.lo */
+static char B13[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 14, 64,102,117,110, 99,116,
+105,111,110, 46,108,117, 97, 0, 0, 0, 0,141, 15, 0, 60, 24, 22, 7, 60, 25,
+ 11, 1, 11, 2, 11, 3, 60, 26, 11, 2, 11, 4, 60, 27, 11, 2, 11, 5, 60, 28,
+ 11, 2, 11, 6, 60, 29, 22, 1, 11, 7, 7, 0, 30, 0, 11, 8, 60, 30, 11, 2,
+ 11, 9, 60, 31, 15, 10, 30, 6, 60, 32, 25, 0, 60, 33, 15, 11, 15, 0, 15, 12,
+ 2, 0, 2, 60, 36, 15, 0, 11, 13, 11, 14, 26, 60, 51, 15, 0, 11, 15, 11, 16,
+ 26, 60,232, 15, 0, 11, 17, 11, 18, 26, 60,242, 15, 0, 11, 19, 11, 20, 26, 60,
+250, 15, 0, 11, 21, 11, 22, 26, 59, 1, 14, 15, 0, 11, 23, 11, 24, 26, 59, 1,
+ 21, 11, 26, 25, 25, 59, 1, 49, 11, 28, 25, 27, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 2, 0, 0, 0, 14, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 0,
+ 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5,
+116,121,112,101, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 5,110,
+ 97,109,101, 0, 2, 0, 0, 0, 5, 97,114,103,115, 0, 2, 0, 0, 0, 2,110,
+ 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,
+115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,101,
+ 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,
+108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103,
+ 0, 4, 0, 0, 0, 36, 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,110, 46,
+108,117, 97, 0, 0, 0, 0,141, 10, 1, 60, 37, 13, 0, 20, 1, 2, 1, 1, 48,
+ 7, 13, 0, 18, 2, 11, 2, 32, 52, 21, 60, 38, 15, 3, 13, 0, 18, 4, 18, 5,
+ 13, 0, 18, 4, 18, 6, 2, 0, 2, 50, 2, 60, 39, 60, 40, 13, 0, 11, 7, 13,
+ 0, 11, 8, 15, 9, 13, 0, 18, 10, 15, 11, 13, 0, 18, 12, 11, 2, 2, 1, 2,
+ 2, 2, 2, 27, 1, 27, 2, 5, 4, 60, 41, 15, 3, 13, 0, 18, 7, 13, 0, 18,
+ 8, 2, 0, 2, 60, 42, 7, 1, 50, 25, 60, 44, 13, 0, 18, 14, 13, 1, 16, 20,
+ 3, 2, 0, 1, 60, 45, 13, 1, 7, 1, 37, 23, 1, 60, 46, 60, 43, 13, 0, 18,
+ 14, 13, 1, 16, 54, 36, 60, 47, 0, 0, 0, 0, 2, 0, 0, 0, 36, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 42, 0, 0, 0, 2,105, 0, 0, 0, 0, 15,
+ 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,
+115,115, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 8,100,
+101, 99,108,116, 97,103, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2,
+ 0, 0, 0, 7, 99,105,116,121,112,101, 0, 2, 0, 0, 0, 5, 99,116, 97,103,
+ 0, 2, 0, 0, 0, 6,105,116,121,112,101, 0, 2, 0, 0, 0, 4,116, 97,103,
+ 0, 2, 0, 0, 0, 7,116, 97,103,118, 97,114, 0, 2, 0, 0, 0, 5,116,121,
+112,101, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0,
+ 4,109,111,100, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 5, 97,114,103,
+115, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0, 4, 0, 0, 0, 51,
+ 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,110, 46,108,117, 97, 0, 0, 0,
+ 6, 51, 15, 1, 60, 52, 13, 0, 20, 2, 2, 1, 1, 60, 53, 15, 5, 13, 0, 18,
+ 6, 11, 7, 2, 3, 2, 60, 55, 13, 1, 52, 21, 60, 56, 15, 8, 11, 9, 13, 0,
+ 18, 10, 11, 11, 13, 1, 11, 12, 2, 0, 5, 50, 17, 60, 58, 15, 8, 11, 13, 13,
+ 0, 18, 10, 11, 12, 2, 0, 3, 60, 59, 60, 60, 15, 8, 11, 14, 13, 0, 18, 15,
+ 11, 16, 2, 0, 3, 60, 61, 15, 8, 11, 17, 2, 0, 1, 60, 64, 15, 8, 11, 18,
+ 2, 0, 1, 60, 66, 4, 0, 60, 67, 13, 1, 52, 6, 7, 2, 23, 5, 50, 4, 7,
+ 1, 23, 5, 60, 68, 13, 1, 48, 7, 13, 0, 18, 10, 11, 20, 31, 48, 5, 13, 4,
+ 4, 0, 32, 52, 51, 60, 69, 13, 0, 18, 21, 11, 21, 32, 52, 19, 60, 70, 15, 8,
+ 11, 22, 13, 0, 18, 23, 18, 24, 11, 25, 2, 0, 3, 50, 19, 60, 72, 15, 8, 11,
+ 22, 13, 0, 18, 23, 18, 26, 11, 25, 2, 0, 3, 60, 73, 50, 2, 60, 74, 60, 76,
+ 13, 0, 18, 27, 7, 1, 16, 18, 28, 11, 29, 31, 52, 93, 60, 77, 7, 1, 50, 72,
+ 60, 79, 15, 31, 13, 0, 18, 27, 13, 6, 16, 18, 28, 2, 1, 1, 11, 32, 31, 52,
+ 29, 60, 80, 15, 8, 11, 33, 13, 0, 18, 27, 13, 6, 16, 20, 34, 13, 5, 2, 1,
+ 2, 42, 11, 35, 42, 2, 0, 1, 50, 2, 60, 81, 60, 82, 13, 5, 7, 1, 37, 23,
+ 5, 60, 83, 13, 6, 7, 1, 37, 23, 6, 60, 84, 60, 78, 13, 0, 18, 27, 13, 6,
+ 16, 54, 83, 5, 1, 50, 2, 60, 85, 60, 87, 15, 8, 11, 36, 13, 5, 42, 11, 37,
+ 42, 2, 0, 1, 60, 89, 15, 8, 11, 38, 2, 0, 1, 60, 92, 4, 0, 60, 93, 13,
+ 1, 52, 6, 7, 2, 23, 6, 50, 4, 7, 1, 23, 6, 60, 94, 13, 1, 48, 7, 13,
+ 0, 18, 10, 11, 20, 31, 48, 5, 13, 4, 4, 0, 32, 52, 47, 60, 95, 15, 8, 11,
+ 39, 13, 0, 18, 21, 13, 1, 11, 40, 11, 41, 2, 0, 5, 60, 96, 15, 8, 11, 42,
+ 13, 0, 18, 21, 13, 1, 11, 43, 2, 0, 4, 60, 97, 15, 8, 11, 44, 2, 0, 1,
+ 50, 35, 60, 98, 13, 4, 52, 27, 60, 99, 13, 0, 11, 6, 15, 5, 13, 0, 18, 6,
+ 11, 45, 2, 3, 2, 27, 2, 23, 3, 23, 3, 5, 2, 50, 2, 60,100, 60,102, 13,
+ 0, 18, 27, 7, 1, 16, 18, 28, 11, 29, 31, 52, 57, 60,103, 7, 1, 50, 36, 60,
+105, 13, 0, 18, 27, 13, 7, 16, 20, 46, 13, 6, 2, 0, 2, 60,106, 13, 6, 7,
+ 1, 37, 23, 6, 60,107, 13, 7, 7, 1, 37, 23, 7, 60,108, 60,104, 13, 0, 18,
+ 27, 13, 7, 16, 54, 47, 5, 1, 50, 2, 60,109, 60,112, 13, 1, 48, 7, 13, 0,
+ 18, 10, 11, 20, 31, 48, 5, 13, 4, 4, 0, 32, 52, 19, 60,113, 15, 8, 11, 47,
+ 13, 0, 18, 10, 42, 11, 48, 42, 2, 0, 1, 50, 2, 60,114, 60,117, 13, 1, 52,
+ 6, 7, 2, 23, 6, 50, 4, 7, 1, 23, 6, 60,118, 13, 0, 18, 27, 7, 1, 16,
+ 18, 28, 11, 29, 31, 52, 57, 60,119, 7, 1, 50, 36, 60,121, 13, 0, 18, 27, 13,
+ 7, 16, 20, 49, 13, 6, 2, 0, 2, 60,122, 13, 6, 7, 1, 37, 23, 6, 60,123,
+ 13, 7, 7, 1, 37, 23, 7, 60,124, 60,120, 13, 0, 18, 27, 13, 7, 16, 54, 47,
+ 5, 1, 50, 2, 60,125, 60,128, 13, 1, 48, 7, 13, 0, 18, 10, 11, 50, 32, 52,
+ 12, 60,129, 15, 8, 11, 51, 2, 0, 1, 49, 2,252, 60,130, 13, 1, 48, 7, 13,
+ 0, 18, 10, 11, 52, 32, 52, 34, 60,131, 15, 8, 11, 53, 13, 0, 18, 27, 7, 1,
+ 16, 18, 10, 11, 54, 13, 0, 18, 27, 7, 2, 16, 18, 10, 11, 55, 2, 0, 5, 49,
+ 2,203, 60,133, 15, 8, 11, 56, 2, 0, 1, 60,134, 13, 0, 18, 28, 11, 57, 31,
+ 48, 7, 13, 0, 18, 28, 11, 29, 31, 52, 48, 60,135, 15, 8, 11, 58, 13, 0, 18,
+ 6, 13, 0, 18, 28, 13, 0, 18, 59, 11, 60, 2, 0, 5, 60,136, 15, 8, 11, 42,
+ 13, 0, 18, 6, 13, 0, 18, 28, 13, 0, 18, 59, 11, 61, 2, 0, 5, 50, 11, 60,
+138, 15, 8, 11, 58, 2, 0, 1, 60,139, 60,140, 13, 1, 48, 7, 13, 0, 18, 10,
+ 11, 20, 32, 52, 15, 60,141, 15, 8, 11, 20, 13, 1, 11, 42, 2, 0, 3, 50, 70,
+ 60,142, 13, 1, 48, 2, 13, 4, 52, 21, 60,143, 15, 8, 13, 1, 11, 62, 42, 13,
+ 0, 18, 10, 42, 11, 42, 2, 0, 2, 50, 39, 60,144, 13, 1, 52, 18, 60,145, 15,
+ 8, 11, 63, 13, 0, 18, 10, 42, 11, 42, 2, 0, 2, 50, 15, 60,147, 15, 8, 13,
+ 0, 18, 10, 11, 42, 2, 0, 2, 60,148, 60,151, 7, 1, 50, 49, 60,153, 13, 0,
+ 18, 27, 13, 7, 16, 20, 64, 2, 0, 1, 60,154, 13, 7, 7, 1, 37, 23, 7, 60,
+155, 13, 0, 18, 27, 13, 7, 16, 52, 11, 60,156, 15, 8, 11, 65, 2, 0, 1, 50,
+ 2, 60,157, 60,158, 60,152, 13, 0, 18, 27, 13, 7, 16, 54, 60, 60,160, 15, 8,
+ 11, 66, 2, 0, 1, 60,163, 13, 0, 18, 28, 11, 57, 31, 48, 7, 13, 0, 18, 28,
+ 11, 29, 31, 52,220, 60,164, 15, 31, 13, 0, 18, 28, 2, 1, 1, 60,165, 13, 8,
+ 11, 68, 32, 52, 17, 60,166, 15, 8, 11, 69, 13, 8, 42, 11, 70, 42, 2, 0, 1,
+ 50,179, 60,167, 13, 8, 52, 17, 60,168, 15, 8, 11, 69, 13, 8, 42, 11, 71, 42,
+ 2, 0, 1, 50,156, 60,170, 13, 0, 18, 59, 11, 57, 32, 52, 98, 60,171, 15, 8,
+ 11, 72, 2, 0, 1, 60,172, 15, 8, 11, 73, 2, 0, 1, 60,173, 15, 8, 11, 74,
+ 13, 0, 18, 28, 11, 71, 2, 0, 3, 60,174, 15, 8, 11, 75, 2, 0, 1, 60,175,
+ 15, 8, 11, 76, 13, 0, 18, 28, 11, 77, 2, 0, 3, 60,176, 15, 8, 11, 78, 2,
+ 0, 1, 60,177, 15, 8, 11, 79, 13, 0, 18, 26, 11, 80, 13, 0, 18, 26, 11, 66,
+ 2, 0, 5, 60,178, 15, 8, 11, 81, 2, 0, 1, 50, 45, 60,180, 13, 0, 18, 59,
+ 11, 82, 32, 52, 17, 60,181, 15, 8, 11, 83, 13, 0, 18, 26, 11, 66, 2, 0, 3,
+ 50, 17, 60,183, 15, 8, 11, 84, 13, 0, 18, 26, 11, 66, 2, 0, 3, 60,184, 60,
+185, 5, 1, 50, 2, 60,186, 60,187, 7, 1, 50, 25, 60,189, 13, 0, 18, 27, 13,
+ 8, 16, 20, 85, 2, 0, 1, 60,190, 13, 8, 7, 1, 37, 23, 8, 60,191, 60,188,
+ 13, 0, 18, 27, 13, 8, 16, 54, 36, 60,192, 15, 8, 11, 86, 2, 0, 1, 60,195,
+ 13, 1, 52, 6, 7, 2, 23, 6, 50, 4, 7, 1, 23, 6, 60,196, 13, 0, 18, 27,
+ 7, 1, 16, 18, 28, 11, 29, 31, 52, 57, 60,197, 7, 1, 50, 36, 60,199, 13, 0,
+ 18, 27, 13, 9, 16, 20, 87, 13, 6, 2, 0, 2, 60,200, 13, 6, 7, 1, 37, 23,
+ 6, 60,201, 13, 9, 7, 1, 37, 23, 9, 60,202, 60,198, 13, 0, 18, 27, 13, 9,
+ 16, 54, 47, 5, 1, 50, 2, 60,203, 60,206, 13, 0, 18, 27, 7, 1, 16, 18, 28,
+ 11, 29, 31, 52, 46, 60,207, 7, 1, 50, 25, 60,209, 13, 0, 18, 27, 13, 9, 16,
+ 20, 88, 2, 0, 1, 60,210, 13, 9, 7, 1, 37, 23, 9, 60,211, 60,208, 13, 0,
+ 18, 27, 13, 9, 16, 54, 36, 5, 1, 50, 2, 60,212, 5, 2, 60,213, 60,215, 15,
+ 8, 11, 89, 2, 0, 1, 60,216, 15, 8, 11, 90, 2, 0, 1, 60,219, 15, 8, 11,
+ 91, 2, 0, 1, 60,220, 15, 93, 13, 0, 18, 15, 9, 2, 9, 1, 2, 1, 3, 7,
+ 1, 38, 60,221, 13, 7, 7, 0, 36, 52, 38, 60,222, 15, 8, 11, 39, 15, 93, 13,
+ 0, 18, 15, 7, 1, 9, 3, 2, 1, 3, 42, 15, 94, 11, 95, 13, 7, 2, 1, 2,
+ 42, 11, 96, 42, 2, 0, 1, 50, 19, 60,224, 15, 8, 11, 97, 13, 0, 18, 98, 42,
+ 11, 99, 42, 2, 0, 1, 60,225, 60,227, 15, 8, 11,100, 2, 0, 1, 60,228, 15,
+ 8, 11,101, 2, 0, 1, 60,229, 0, 0, 0, 0, 24, 0, 0, 0, 51, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 52, 0, 0, 0, 6, 99,108, 97,115,115, 0,
+ 0, 0, 0, 53, 0, 0, 0, 2, 95, 0, 0, 0, 0, 53, 0, 0, 0, 2, 95, 0,
+ 0, 0, 0, 53, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 0, 0, 0, 66, 0,
+ 0, 0, 5,110, 97,114,103, 0, 0, 0, 0, 77, 0, 0, 0, 2,105, 0, 0, 0,
+ 0, 84, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, 5,110, 97,114,103, 0, 0,
+ 0, 0,103, 0, 0, 0, 2,105, 0, 0, 0, 0,108, 0, 0, 0, 0, 0, 0, 0,
+119, 0, 0, 0, 2,105, 0, 0, 0, 0,124, 0, 0, 0, 0, 0, 0, 0,151, 0,
+ 0, 0, 2,105, 0, 0, 0, 0,164, 0, 0, 0, 2,116, 0, 0, 0, 0,185, 0,
+ 0, 0, 0, 0, 0, 0,187, 0, 0, 0, 2,105, 0, 0, 0, 0,197, 0, 0, 0,
+ 2,105, 0, 0, 0, 0,202, 0, 0, 0, 0, 0, 0, 0,207, 0, 0, 0, 2,105,
+ 0, 0, 0, 0,211, 0, 0, 0, 0, 0, 0, 0,212, 0, 0, 0, 0, 0, 0, 0,
+212, 0, 0, 0, 0, 0, 0, 0,220, 0, 0, 0, 9,111,118,101,114,108,111, 97,
+100, 0, 0, 0, 0,102, 2, 0, 0, 0, 6, 99,108, 97,115,115, 0, 2, 0, 0,
+ 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115, 0,
+ 2, 0, 0, 0, 2, 95, 0, 2, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 2,
+ 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 4,109,111,100,
+ 0, 2, 0, 0, 0, 13, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41, 0, 2,
+ 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 11, 47, 42, 32,109,
+101,116,104,111,100, 58, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0,
+ 0, 11, 32,111,102, 32, 99,108, 97,115,115, 32, 0, 2, 0, 0, 0, 4, 32, 42,
+ 47, 0, 2, 0, 0, 0, 13, 47, 42, 32,102,117,110, 99,116,105,111,110, 58, 0,
+ 2, 0, 0, 0, 12,115,116, 97,116,105, 99, 32,118,111,105,100, 0, 2, 0, 0,
+ 0, 6, 99,110, 97,109,101, 0, 2, 0, 0, 0, 7, 40,118,111,105,100, 41, 0,
+ 2, 0, 0, 0, 2,123, 0, 2, 0, 0, 0, 7, 32,105,102, 32, 40, 10, 0, 2,
+ 0, 0, 0, 5,110, 97,114,103, 0, 2, 0, 0, 0, 4,110,101,119, 0, 2, 0,
+ 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 22, 32, 32, 32, 32, 32, 33,
+116,111,108,117, 97, 95,105,115,116,121,112,101, 40, 49, 44, 0, 2, 0, 0, 0,
+ 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0, 5, 99,116, 97,103, 0, 2, 0,
+ 0, 0, 8, 44, 48, 41, 32,124,124, 10, 0, 2, 0, 0, 0, 4,116, 97,103, 0,
+ 2, 0, 0, 0, 5, 97,114,103,115, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 5,118,111,105,100, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0,
+ 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 7,111, 98,106,101, 99,
+116, 0, 2, 0, 0, 0, 7, 32, 32, 32, 32, 32, 33, 0, 2, 0, 0, 0, 13,111,
+117,116, 99,104,101, 99,107,116,121,112,101, 0, 2, 0, 0, 0, 5, 32,124,124,
+ 10, 0, 2, 0, 0, 0, 21, 32, 32, 32, 32, 32, 33,116,111,108,117, 97, 95,105,
+115,110,111,111, 98,106, 40, 0, 2, 0, 0, 0, 19, 41, 10, 32, 41, 10, 32, 32,
+103,111,116,111, 32,101,114,114,111,114, 59, 0, 2, 0, 0, 0, 9, 32,101,108,
+115,101, 10, 32,123, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 2, 42, 0,
+ 2, 0, 0, 0, 8,115,101,108,102, 32, 61, 32, 0, 2, 0, 0, 0, 2, 40, 0,
+ 2, 0, 0, 0, 3, 42, 41, 0, 2, 0, 0, 0, 24,116,111,108,117, 97, 95,103,
+101,116,117,115,101,114,116,121,112,101, 40, 49, 44, 48, 41, 59, 0, 2, 0, 0,
+ 0, 20, 94, 37,115, 42,115,116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42,
+ 41, 0, 2, 0, 0, 0, 8,100,101, 99,108, 97,114,101, 0, 2, 0, 0, 0, 55,
+ 32, 32,105,102, 32, 40, 33,115,101,108,102, 41, 32,116,111,108,117, 97, 95,101,
+114,114,111,114, 40, 34,105,110,118, 97,108,105,100, 32, 39,115,101,108,102, 39,
+ 32,105,110, 32,102,117,110, 99,116,105,111,110, 32, 39, 0, 2, 0, 0, 0, 5,
+ 39, 34, 41, 59, 0, 2, 0, 0, 0, 9,103,101,116, 97,114,114, 97,121, 0, 2,
+ 0, 0, 0, 7,100,101,108,101,116,101, 0, 2, 0, 0, 0, 15, 32, 32,100,101,
+108,101,116,101, 32,115,101,108,102, 59, 0, 2, 0, 0, 0, 12,111,112,101,114,
+ 97,116,111,114, 38, 91, 93, 0, 2, 0, 0, 0, 20, 32, 32,115,101,108,102, 45,
+ 62,111,112,101,114, 97,116,111,114, 91, 93, 40, 0, 2, 0, 0, 0, 5, 41, 32,
+ 61, 32, 0, 2, 0, 0, 0, 2, 59, 0, 2, 0, 0, 0, 4, 32, 32,123, 0, 2,
+ 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 32, 32, 0, 2, 0, 0, 0, 4,112,116,
+114, 0, 2, 0, 0, 0, 14,116,111,108,117, 97, 73, 95,114,101,116, 32, 61, 32,
+ 0, 2, 0, 0, 0, 3, 41, 32, 0, 2, 0, 0, 0, 3, 58, 58, 0, 2, 0, 0,
+ 0, 7,115,101,108,102, 45, 62, 0, 2, 0, 0, 0, 8,112, 97,115,115,112, 97,
+114, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0, 0,
+ 14, 32, 32, 32,116,111,108,117, 97, 95,112,117,115,104, 0, 2, 0, 0, 0, 22,
+ 40, 40,100,111,117, 98,108,101, 41,116,111,108,117, 97, 73, 95,114,101,116, 41,
+ 59, 0, 2, 0, 0, 0, 14, 40,116,111,108,117, 97, 73, 95,114,101,116, 41, 59,
+ 0, 2, 0, 0, 0, 5, 32, 32, 32,123, 0, 2, 0, 0, 0, 20, 35,105,102,100,
+101,102, 32, 95, 95, 99,112,108,117,115,112,108,117,115, 10, 0, 2, 0, 0, 0,
+ 29, 32, 32, 32, 32,118,111,105,100, 42, 32,116,111,108,117, 97, 73, 95, 99,108,
+111,110,101, 32, 61, 32,110,101,119, 0, 2, 0, 0, 0, 7, 35,101,108,115,101,
+ 10, 0, 2, 0, 0, 0, 63, 32, 32, 32, 32,118,111,105,100, 42, 32,116,111,108,
+117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,116,111,108,117, 97, 95, 99,111,
+112,121, 40, 40,118,111,105,100, 42, 41, 38,116,111,108,117, 97, 73, 95,114,101,
+116, 44,115,105,122,101,111,102, 40, 0, 2, 0, 0, 0, 4, 41, 41, 59, 0, 2,
+ 0, 0, 0, 8, 35,101,110,100,105,102, 10, 0, 2, 0, 0, 0, 51, 32, 32, 32,
+ 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,101, 40,
+116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40,116,111,108,117, 97, 73,
+ 95, 99,108,111,110,101, 44, 0, 2, 0, 0, 0, 3, 41, 44, 0, 2, 0, 0, 0,
+ 5, 32, 32, 32,125, 0, 2, 0, 0, 0, 2, 38, 0, 2, 0, 0, 0, 42, 32, 32,
+ 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,101, 40,
+ 40,118,111,105,100, 42, 41, 38,116,111,108,117, 97, 73, 95,114,101,116, 44, 0,
+ 2, 0, 0, 0, 41, 32, 32, 32,116,111,108,117, 97, 95,112,117,115,104,117,115,
+101,114,116,121,112,101, 40, 40,118,111,105,100, 42, 41,116,111,108,117, 97, 73,
+ 95,114,101,116, 44, 0, 2, 0, 0, 0, 9,114,101,116,118, 97,108,117,101, 0,
+ 2, 0, 0, 0, 4, 32, 32,125, 0, 2, 0, 0, 0, 9,115,101,116, 97,114,114,
+ 97,121, 0, 2, 0, 0, 0, 10,102,114,101,101, 97,114,114, 97,121, 0, 2, 0,
+ 0, 0, 3, 32,125, 0, 2, 0, 0, 0, 9, 32,114,101,116,117,114,110, 59, 0,
+ 2, 0, 0, 0, 8,101,114,114,111,114, 58, 10, 0, 2, 0, 0, 0, 9,111,118,
+101,114,108,111, 97,100, 0, 2, 0, 0, 0, 7,115,116,114,115,117, 98, 0, 2,
+ 0, 0, 0, 7,102,111,114,109, 97,116, 0, 2, 0, 0, 0, 5, 37, 48, 50,100,
+ 0, 2, 0, 0, 0, 4, 40, 41, 59, 0, 2, 0, 0, 0, 36, 32,116,111,108,117,
+ 97, 95,101,114,114,111,114, 40, 34, 35,102,101,114,114,111,114, 32,105,110, 32,
+102,117,110, 99,116,105,111,110, 32, 39, 0, 2, 0, 0, 0, 6,108,110, 97,109,
+101, 0, 2, 0, 0, 0, 6, 39, 46, 34, 41, 59, 0, 2, 0, 0, 0, 2,125, 0,
+ 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,101,114,
+ 0, 4, 0, 0, 0,232, 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,110, 46,
+108,117, 97, 0, 0, 0, 0, 89, 5, 1, 60,233, 13, 0, 20, 2, 2, 1, 1, 46,
+ 7, 13, 0, 20, 3, 2, 1, 1, 60,234, 13, 1, 52, 33, 60,235, 15, 4, 11, 5,
+ 13, 1, 42, 11, 6, 42, 13, 0, 18, 7, 42, 11, 8, 42, 13, 0, 18, 9, 42, 11,
+ 10, 42, 2, 0, 1, 50, 27, 60,237, 15, 4, 11, 11, 13, 0, 18, 7, 42, 11, 8,
+ 42, 13, 0, 18, 9, 42, 11, 10, 42, 2, 0, 1, 60,238, 60,239, 0, 0, 0, 0,
+ 2, 0, 0, 0,232, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,233, 0, 0,
+ 0, 7,112, 97,114,101,110,116, 0, 0, 0, 0, 12, 2, 0, 0, 0, 7,112, 97,
+114,101,110,116, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,
+105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,
+101, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 18, 32,
+116,111,108,117, 97, 95,102,117,110, 99,116,105,111,110, 40, 34, 0, 2, 0, 0,
+ 0, 4, 34, 44, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0,
+ 0, 3, 34, 44, 0, 2, 0, 0, 0, 6, 99,110, 97,109,101, 0, 2, 0, 0, 0,
+ 3, 41, 59, 0, 2, 0, 0, 0, 23, 32,116,111,108,117, 97, 95,102,117,110, 99,
+116,105,111,110, 40, 78, 85, 76, 76, 44, 34, 0, 2, 0, 0, 0, 11,117,110,114,
+101,103,105,115,116,101,114, 0, 4, 0, 0, 0,242, 0, 0, 0, 14, 64,102,117,
+110, 99,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 52, 4, 1, 60,243, 13,
+ 0, 20, 1, 2, 1, 1, 4, 0, 32, 48, 10, 13, 0, 20, 2, 2, 1, 1, 4, 0,
+ 32, 52, 19, 60,244, 15, 3, 11, 4, 13, 0, 18, 5, 42, 11, 6, 42, 2, 0, 1,
+ 50, 2, 60,245, 60,246, 0, 0, 0, 0, 1, 0, 0, 0,242, 0, 0, 0, 5,115,
+101,108,102, 0, 0, 0, 0, 7, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0,
+ 0, 0, 8,105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 9,105,110,109,111,
+100,117,108,101, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0,
+ 0, 32, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40, 41, 59, 32,108,117,
+ 97, 95,115,101,116,103,108,111, 98, 97,108, 40, 34, 0, 2, 0, 0, 0, 6,108,
+110, 97,109,101, 0, 2, 0, 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 6,112,
+114,105,110,116, 0, 4, 0, 0, 0,250, 0, 0, 0, 14, 64,102,117,110, 99,116,
+105,111,110, 46,108,117, 97, 0, 0, 0, 1, 1, 8, 3, 60,251, 15, 2, 13, 1,
+ 11, 3, 42, 2, 0, 1, 60,252, 15, 2, 13, 1, 11, 4, 42, 13, 0, 18, 6, 42,
+ 11, 7, 42, 2, 0, 1, 60,253, 15, 2, 13, 1, 11, 8, 42, 13, 0, 18, 9, 42,
+ 11, 7, 42, 2, 0, 1, 60,254, 15, 2, 13, 1, 11, 10, 42, 13, 0, 18, 11, 42,
+ 11, 7, 42, 2, 0, 1, 60,255, 15, 2, 13, 1, 11, 12, 42, 13, 0, 18, 13, 42,
+ 11, 7, 42, 2, 0, 1, 59, 1, 0, 15, 2, 13, 1, 11, 14, 42, 13, 0, 18, 15,
+ 42, 11, 7, 42, 2, 0, 1, 59, 1, 1, 15, 2, 13, 1, 11, 16, 42, 13, 0, 18,
+ 17, 42, 11, 7, 42, 2, 0, 1, 59, 1, 2, 15, 2, 13, 1, 11, 18, 42, 13, 0,
+ 18, 19, 42, 11, 7, 42, 2, 0, 1, 59, 1, 3, 15, 2, 13, 1, 11, 20, 42, 2,
+ 0, 1, 59, 1, 4, 7, 1, 50, 35, 59, 1, 6, 13, 0, 18, 22, 13, 3, 16, 20,
+ 2, 13, 1, 11, 23, 42, 11, 24, 2, 0, 3, 59, 1, 7, 13, 3, 7, 1, 37, 23,
+ 3, 59, 1, 8, 59, 1, 5, 13, 0, 18, 22, 13, 3, 16, 54, 47, 59, 1, 9, 15,
+ 2, 13, 1, 11, 25, 42, 2, 0, 1, 59, 1, 10, 15, 2, 13, 1, 11, 26, 42, 13,
+ 2, 42, 2, 0, 1, 59, 1, 11, 0, 0, 0, 0, 4, 0, 0, 0,250, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0,250, 0, 0, 0, 6,105,100,101,110,116, 0,
+ 0, 0, 0,250, 0, 0, 0, 6, 99,108,111,115,101, 0, 0, 0, 1, 4, 0, 0,
+ 0, 2,105, 0, 0, 0, 0, 27, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2,
+ 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116,
+ 0, 2, 0, 0, 0, 10, 70,117,110, 99,116,105,111,110,123, 0, 2, 0, 0, 0,
+ 10, 32,109,111,100, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102,
+ 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0,
+ 0, 0, 10, 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,116,121,
+112,101, 0, 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61, 32, 39, 0, 2, 0,
+ 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61, 32,
+ 39, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32, 99,111,
+110,115,116, 32, 61, 32, 39, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2,
+ 0, 0, 0, 11, 32, 99,110, 97,109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 6,
+ 99,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32,108,110, 97,109,101, 32, 61, 32,
+ 39, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 10, 32, 97,
+114,103,115, 32, 61, 32,123, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 5,
+ 97,114,103,115, 0, 2, 0, 0, 0, 3, 32, 32, 0, 2, 0, 0, 0, 2, 44, 0,
+ 2, 0, 0, 0, 3, 32,125, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 9,
+111,118,101,114,108,111, 97,100, 0, 4, 0, 0, 1, 14, 0, 0, 0, 14, 64,102,
+117,110, 99,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 22, 4, 1, 59, 1,
+ 15, 13, 0, 18, 1, 20, 2, 13, 0, 18, 3, 3, 1, 2, 59, 1, 16, 0, 0, 0,
+ 0, 1, 0, 0, 1, 14, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 4, 2,
+ 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116,
+ 0, 2, 0, 0, 0, 9,111,118,101,114,108,111, 97,100, 0, 2, 0, 0, 0, 6,
+108,110, 97,109,101, 0, 2, 0, 0, 0, 10, 95, 70,117,110, 99,116,105,111,110,
+ 0, 4, 0, 0, 1, 21, 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,110, 46,
+108,117, 97, 0, 0, 0, 0,231, 7, 1, 59, 1, 22, 13, 0, 11, 1, 15, 2, 26,
+ 59, 1, 23, 15, 3, 13, 0, 15, 4, 2, 0, 2, 59, 1, 25, 13, 0, 18, 5, 11,
+ 5, 31, 48, 7, 13, 0, 18, 5, 11, 6, 31, 52, 12, 59, 1, 26, 15, 7, 11, 8,
+ 2, 0, 1, 50, 3, 59, 1, 27, 59, 1, 29, 15, 9, 13, 0, 2, 0, 1, 59, 1,
+ 30, 13, 0, 20, 10, 2, 1, 1, 52,108, 59, 1, 31, 13, 0, 18, 11, 13, 0, 18,
+ 12, 18, 11, 32, 52, 46, 59, 1, 32, 13, 0, 11, 11, 11, 13, 26, 59, 1, 33, 13,
+ 0, 11, 14, 11, 13, 26, 59, 1, 34, 13, 0, 11, 15, 13, 0, 18, 12, 18, 11, 26,
+ 59, 1, 35, 13, 0, 11, 16, 11, 17, 26, 50, 44, 59, 1, 36, 13, 0, 18, 11, 11,
+ 18, 13, 0, 18, 12, 18, 11, 42, 32, 52, 22, 59, 1, 37, 13, 0, 11, 11, 11, 19,
+ 26, 59, 1, 38, 13, 0, 11, 14, 11, 19, 26, 50, 3, 59, 1, 39, 50, 3, 59, 1,
+ 40, 59, 1, 41, 13, 0, 11, 20, 13, 0, 20, 21, 11, 22, 2, 1, 2, 13, 0, 20,
+ 23, 13, 0, 2, 1, 2, 42, 26, 59, 1, 42, 13, 0, 1, 1, 59, 1, 43, 0, 0,
+ 0, 0, 1, 0, 0, 1, 21, 0, 0, 0, 2,116, 0, 0, 0, 0, 24, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 14,
+ 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 0, 2, 0, 0, 0, 7,115,
+101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103,
+ 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 1, 0, 2, 0,
+ 0, 0, 6,101,114,114,111,114, 0, 2, 0, 0, 0, 31, 35,105,110,118, 97,108,
+105,100, 32, 39, 99,111,110,115,116, 39, 32,115,112,101, 99,105,102,105, 99, 97,
+116,105,111,110, 0, 2, 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0,
+ 0, 8,105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0,
+ 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0, 4,110,101,119,
+ 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 5,116,121,112,
+101, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0,
+ 0, 0, 2,126, 0, 2, 0, 0, 0, 7,100,101,108,101,116,101, 0, 2, 0, 0,
+ 0, 6, 99,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,110, 99,110, 97,
+109,101, 0, 2, 0, 0, 0, 7,116,111,108,117, 97, 73, 0, 2, 0, 0, 0, 9,
+111,118,101,114,108,111, 97,100, 0, 2, 0, 0, 0, 9, 70,117,110, 99,116,105,
+111,110, 0, 4, 0, 0, 1, 49, 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,
+110, 46,108,117, 97, 0, 0, 0, 0,147, 11, 3, 59, 1, 50, 15, 4, 15, 5, 13,
+ 1, 7, 2, 9, 2, 2, 1, 3, 11, 6, 2, 1, 2, 59, 1, 51, 7, 1, 59, 1,
+ 52, 22, 1, 11, 9, 7, 0, 30, 0, 50, 50, 59, 1, 54, 13, 5, 11, 9, 13, 5,
+ 18, 9, 7, 1, 37, 26, 59, 1, 55, 13, 5, 13, 5, 18, 9, 15, 10, 13, 3, 13,
+ 4, 16, 11, 11, 2, 1, 2, 26, 59, 1, 56, 13, 4, 7, 1, 37, 23, 4, 59, 1,
+ 57, 59, 1, 53, 13, 3, 13, 4, 16, 54, 60, 59, 1, 58, 15, 10, 13, 0, 11, 13,
+ 2, 1, 2, 59, 1, 59, 13, 6, 11, 14, 13, 5, 26, 59, 1, 60, 13, 6, 11, 15,
+ 13, 2, 26, 59, 1, 61, 15, 16, 13, 6, 3, 7, 1, 59, 1, 62, 0, 0, 0, 0,
+ 7, 0, 0, 1, 49, 0, 0, 0, 2,100, 0, 0, 0, 1, 49, 0, 0, 0, 2, 97,
+ 0, 0, 0, 1, 49, 0, 0, 0, 2, 99, 0, 0, 0, 1, 50, 0, 0, 0, 2,116,
+ 0, 0, 0, 1, 51, 0, 0, 0, 2,105, 0, 0, 0, 1, 52, 0, 0, 0, 2,108,
+ 0, 0, 0, 1, 58, 0, 0, 0, 2,102, 0, 0, 0, 0, 17, 2, 0, 0, 0, 2,
+100, 0, 2, 0, 0, 0, 2, 97, 0, 2, 0, 0, 0, 2, 99, 0, 2, 0, 0, 0,
+ 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0, 0, 0, 7,115,
+116,114,115,117, 98, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,105, 0,
+ 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 12, 68,
+101, 99,108, 97,114, 97,116,105,111,110, 0, 2, 0, 0, 0, 4,118, 97,114, 0,
+ 2, 0, 0, 0, 2,102, 0, 2, 0, 0, 0, 5,102,117,110, 99, 0, 2, 0, 0,
+ 0, 5, 97,114,103,115, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0,
+ 0, 0, 10, 95, 70,117,110, 99,116,105,111,110, 0,
+};
+
+/* operator.lo */
+static char B14[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 14, 64,111,112,101,114, 97,
+116,111,114, 46,108,117, 97, 0, 0, 0, 0,125, 21, 0, 60, 18, 22, 2, 60, 19,
+ 11, 1, 11, 2, 11, 3, 60, 20, 15, 4, 30, 1, 60, 21, 25, 0, 60, 22, 15, 5,
+ 15, 0, 15, 6, 2, 0, 2, 60, 25, 22, 10, 11, 8, 11, 9, 60, 26, 11, 10, 11,
+ 11, 60, 27, 11, 12, 11, 13, 60, 28, 11, 14, 11, 15, 60, 29, 11, 16, 11, 17, 60,
+ 30, 11, 18, 11, 19, 60, 31, 11, 20, 11, 21, 60, 32, 11, 22, 11, 23, 60, 33, 11,
+ 24, 11, 25, 60, 34, 11, 26, 11, 27, 30, 9, 60, 35, 25, 7, 60, 39, 15, 0, 11,
+ 28, 11, 29, 26, 60, 60, 11, 31, 25, 30, 60, 82, 11, 33, 25, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 34, 2, 0, 0, 0, 14, 99,108, 97,115,115, 79,112,101,114, 97,
+116,111,114, 0, 2, 0, 0, 0, 5,107,105,110,100, 0, 2, 0, 0, 0, 1, 0,
+ 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 14, 99,108, 97,115,
+115, 70,117,110, 99,116,105,111,110, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,
+103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0,
+ 0, 4, 95, 84, 77, 0, 2, 0, 0, 0, 2, 43, 0, 2, 0, 0, 0, 13,111,112,
+101,114, 97,116,111,114, 95, 97,100,100, 0, 2, 0, 0, 0, 2, 45, 0, 2, 0,
+ 0, 0, 13,111,112,101,114, 97,116,111,114, 95,115,117, 98, 0, 2, 0, 0, 0,
+ 2, 42, 0, 2, 0, 0, 0, 13,111,112,101,114, 97,116,111,114, 95,109,117,108,
+ 0, 2, 0, 0, 0, 2, 47, 0, 2, 0, 0, 0, 13,111,112,101,114, 97,116,111,
+114, 95,100,105,118, 0, 2, 0, 0, 0, 2, 60, 0, 2, 0, 0, 0, 12,111,112,
+101,114, 97,116,111,114, 95,108,116, 0, 2, 0, 0, 0, 2, 62, 0, 2, 0, 0,
+ 0, 12,111,112,101,114, 97,116,111,114, 95,103,116, 0, 2, 0, 0, 0, 3, 60,
+ 61, 0, 2, 0, 0, 0, 12,111,112,101,114, 97,116,111,114, 95,108,101, 0, 2,
+ 0, 0, 0, 3, 62, 61, 0, 2, 0, 0, 0, 12,111,112,101,114, 97,116,111,114,
+ 95,103,101, 0, 2, 0, 0, 0, 3, 91, 93, 0, 2, 0, 0, 0, 13,111,112,101,
+114, 97,116,111,114, 95,103,101,116, 0, 2, 0, 0, 0, 4, 38, 91, 93, 0, 2,
+ 0, 0, 0, 13,111,112,101,114, 97,116,111,114, 95,115,101,116, 0, 2, 0, 0,
+ 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 39, 0, 0, 0, 14, 64,111,112,
+101,114, 97,116,111,114, 46,108,117, 97, 0, 0, 0, 1, 9, 8, 3, 60, 40, 15,
+ 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 41, 15, 2, 13, 1, 11, 4, 42, 13, 0,
+ 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 42, 15, 2, 13, 1, 11, 8, 42, 13, 0,
+ 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 43, 15, 2, 13, 1, 11, 10, 42, 13, 0,
+ 18, 11, 42, 11, 7, 42, 2, 0, 1, 60, 44, 15, 2, 13, 1, 11, 12, 42, 13, 0,
+ 18, 13, 42, 11, 7, 42, 2, 0, 1, 60, 45, 15, 2, 13, 1, 11, 14, 42, 13, 0,
+ 18, 15, 42, 11, 7, 42, 2, 0, 1, 60, 46, 15, 2, 13, 1, 11, 16, 42, 13, 0,
+ 18, 17, 42, 11, 7, 42, 2, 0, 1, 60, 47, 15, 2, 13, 1, 11, 18, 42, 13, 0,
+ 18, 19, 42, 11, 7, 42, 2, 0, 1, 60, 48, 15, 2, 13, 1, 11, 20, 42, 13, 0,
+ 18, 21, 42, 11, 7, 42, 2, 0, 1, 60, 49, 15, 2, 13, 1, 11, 22, 42, 2, 0,
+ 1, 60, 50, 7, 1, 50, 32, 60, 52, 13, 0, 18, 24, 13, 3, 16, 20, 2, 13, 1,
+ 11, 25, 42, 11, 26, 2, 0, 3, 60, 53, 13, 3, 7, 1, 37, 23, 3, 60, 54, 60,
+ 51, 13, 0, 18, 24, 13, 3, 16, 54, 43, 60, 55, 15, 2, 13, 1, 11, 27, 42, 2,
+ 0, 1, 60, 56, 15, 2, 13, 1, 11, 28, 42, 13, 2, 42, 2, 0, 1, 60, 57, 0,
+ 0, 0, 0, 4, 0, 0, 0, 39, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,
+ 39, 0, 0, 0, 6,105,100,101,110,116, 0, 0, 0, 0, 39, 0, 0, 0, 6, 99,
+108,111,115,101, 0, 0, 0, 0, 50, 0, 0, 0, 2,105, 0, 0, 0, 0, 29, 2,
+ 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101,
+ 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 10, 79,112,101,
+114, 97,116,111,114,123, 0, 2, 0, 0, 0, 11, 32,107,105,110,100, 32, 32, 61,
+ 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,107,105,
+110,100, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 10, 32,109,111,100,
+ 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 10,
+ 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,
+112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61, 32, 39, 0, 2,
+ 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32, 99,111,110,115,116,
+ 32, 61, 32, 39, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0,
+ 11, 32, 99,110, 97,109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 6, 99,110, 97,
+109,101, 0, 2, 0, 0, 0, 11, 32,108,110, 97,109,101, 32, 61, 32, 39, 0, 2,
+ 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 10, 32, 97,114,103,115,
+ 32, 61, 32,123, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 5, 97,114,103,
+115, 0, 2, 0, 0, 0, 3, 32, 32, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0,
+ 0, 3, 32,125, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 10, 95, 79,112,
+101,114, 97,116,111,114, 0, 4, 0, 0, 0, 60, 0, 0, 0, 14, 64,111,112,101,
+114, 97,116,111,114, 46,108,117, 97, 0, 0, 0, 0,140, 7, 1, 60, 61, 13, 0,
+ 11, 1, 15, 2, 26, 60, 62, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 64, 13, 0,
+ 18, 5, 11, 5, 31, 48, 7, 13, 0, 18, 5, 11, 6, 31, 52, 11, 60, 65, 15, 7,
+ 11, 8, 2, 0, 1, 50, 2, 60, 66, 60, 68, 15, 9, 13, 0, 2, 0, 1, 60, 69,
+ 13, 0, 20, 10, 2, 1, 1, 44, 52, 11, 60, 70, 15, 7, 11, 11, 2, 0, 1, 50,
+ 2, 60, 71, 60, 73, 13, 0, 11, 12, 13, 0, 20, 13, 11, 14, 2, 1, 2, 13, 0,
+ 20, 15, 13, 0, 2, 1, 2, 42, 26, 60, 74, 13, 0, 11, 16, 13, 0, 18, 16, 13,
+ 0, 18, 17, 42, 26, 60, 75, 13, 0, 1, 1, 60, 76, 0, 0, 0, 0, 1, 0, 0,
+ 0, 60, 0, 0, 0, 2,116, 0, 0, 0, 0, 18, 2, 0, 0, 0, 2,116, 0, 2,
+ 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 14, 99,108, 97,115,115,
+ 79,112,101,114, 97,116,111,114, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103,
+ 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0,
+ 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 6,101,114,
+114,111,114, 0, 2, 0, 0, 0, 31, 35,105,110,118, 97,108,105,100, 32, 39, 99,
+111,110,115,116, 39, 32,115,112,101, 99,105,102,105, 99, 97,116,105,111,110, 0,
+ 2, 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0, 0, 8,105,110, 99,
+108, 97,115,115, 0, 2, 0, 0, 0, 46, 35,111,112,101,114, 97,116,111,114, 32,
+ 99, 97,110, 32,111,110,108,121, 32, 98,101, 32,100,101,102,105,110,101,100, 32,
+ 97,115, 32, 99,108, 97,115,115, 32,109,101,109, 98,101,114, 0, 2, 0, 0, 0,
+ 6, 99,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,110, 99,110, 97,109,
+101, 0, 2, 0, 0, 0, 7,116,111,108,117, 97, 73, 0, 2, 0, 0, 0, 9,111,
+118,101,114,108,111, 97,100, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0,
+ 0, 0, 5,107,105,110,100, 0, 2, 0, 0, 0, 9, 79,112,101,114, 97,116,111,
+114, 0, 4, 0, 0, 0, 82, 0, 0, 0, 14, 64,111,112,101,114, 97,116,111,114,
+ 46,108,117, 97, 0, 0, 0, 1, 99, 14, 4, 60, 83, 15, 5, 15, 6, 13, 2, 7,
+ 2, 15, 7, 13, 2, 2, 1, 1, 7, 1, 38, 2, 1, 3, 11, 8, 2, 1, 2, 60,
+ 84, 7, 1, 60, 85, 22, 1, 11, 11, 7, 0, 30, 0, 50, 46, 60, 87, 13, 6, 11,
+ 11, 13, 6, 18, 11, 7, 1, 37, 26, 60, 88, 13, 6, 13, 6, 18, 11, 15, 12, 13,
+ 4, 13, 5, 16, 11, 13, 2, 1, 2, 26, 60, 89, 13, 5, 7, 1, 37, 23, 5, 60,
+ 90, 60, 86, 13, 4, 13, 5, 16, 54, 55, 60, 91, 13, 1, 11, 14, 32, 52, 17, 60,
+ 92, 15, 15, 13, 0, 11, 16, 11, 17, 2, 1, 3, 23, 0, 50, 59, 60, 93, 13, 1,
+ 11, 18, 32, 52, 48, 60, 94, 13, 6, 11, 11, 13, 6, 18, 11, 7, 1, 37, 26, 60,
+ 95, 13, 6, 13, 6, 18, 11, 15, 12, 13, 0, 11, 13, 2, 1, 2, 26, 60, 96, 13,
+ 6, 13, 6, 18, 11, 16, 11, 19, 11, 20, 26, 50, 2, 60, 97, 60, 98, 15, 12, 13,
+ 0, 11, 22, 2, 1, 2, 60, 99, 13, 1, 11, 14, 32, 48, 25, 13, 6, 7, 1, 16,
+ 4, 0, 32, 46, 15, 15, 23, 13, 6, 7, 1, 16, 18, 24, 2, 1, 1, 11, 25, 31,
+ 52, 11, 60,100, 15, 26, 11, 27, 2, 0, 1, 50, 2, 60,101, 60,102, 13, 7, 11,
+ 28, 13, 6, 26, 60,103, 13, 7, 11, 29, 13, 3, 26, 60,104, 13, 7, 11, 30, 15,
+ 15, 13, 1, 11, 31, 11, 17, 2, 1, 3, 26, 60,105, 13, 7, 11, 32, 15, 33, 13,
+ 7, 18, 30, 16, 26, 60,106, 13, 7, 18, 30, 11, 14, 32, 48, 12, 15, 34, 13, 7,
+ 18, 35, 11, 29, 2, 1, 2, 44, 52, 20, 60,107, 15, 36, 13, 0, 11, 16, 13, 1,
+ 42, 13, 2, 13, 3, 2, 0, 4, 50, 2, 60,108, 60,109, 15, 37, 13, 7, 3, 8,
+ 1, 60,110, 0, 0, 0, 0, 8, 0, 0, 0, 82, 0, 0, 0, 2,100, 0, 0, 0,
+ 0, 82, 0, 0, 0, 2,107, 0, 0, 0, 0, 82, 0, 0, 0, 2, 97, 0, 0, 0,
+ 0, 82, 0, 0, 0, 2, 99, 0, 0, 0, 0, 83, 0, 0, 0, 2,116, 0, 0, 0,
+ 0, 84, 0, 0, 0, 2,105, 0, 0, 0, 0, 85, 0, 0, 0, 2,108, 0, 0, 0,
+ 0, 98, 0, 0, 0, 2,102, 0, 0, 0, 0, 38, 2, 0, 0, 0, 2,100, 0, 2,
+ 0, 0, 0, 2,107, 0, 2, 0, 0, 0, 2, 97, 0, 2, 0, 0, 0, 2, 99, 0,
+ 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0,
+ 0, 0, 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0, 7,115,116,114,108,101,
+110, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0,
+ 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 12, 68,101, 99,108, 97,
+114, 97,116,105,111,110, 0, 2, 0, 0, 0, 4,118, 97,114, 0, 2, 0, 0, 0,
+ 3, 91, 93, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 2, 38,
+ 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 4, 38, 91, 93, 0, 2, 0, 0, 0,
+ 5,110, 97,109,101, 0, 2, 0, 0, 0, 13,116,111,108,117, 97, 73, 95,118, 97,
+108,117,101, 0, 2, 0, 0, 0, 2,102, 0, 2, 0, 0, 0, 5,102,117,110, 99,
+ 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 5,116,
+121,112,101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0, 0,
+ 6,101,114,114,111,114, 0, 2, 0, 0, 0, 50,111,112,101,114, 97,116,111,114,
+ 91, 93, 32, 99, 97,110, 32,111,110,108,121, 32, 98,101, 32,100,101,102,105,110,
+101,100, 32,102,111,114, 32,110,117,109,101,114,105, 99, 32,105,110,100,101,120,
+ 46, 0, 2, 0, 0, 0, 5, 97,114,103,115, 0, 2, 0, 0, 0, 6, 99,111,110,
+115,116, 0, 2, 0, 0, 0, 5,107,105,110,100, 0, 2, 0, 0, 0, 3, 37,115,
+ 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 4, 95, 84, 77,
+ 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 4,109,
+111,100, 0, 2, 0, 0, 0, 9, 79,112,101,114, 97,116,111,114, 0, 2, 0, 0,
+ 0, 10, 95, 79,112,101,114, 97,116,111,114, 0,
+};
+
+/* verbatim.lo */
+static char B15[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 14, 64,118,101,114, 98, 97,
+116,105,109, 46,108,117, 97, 0, 0, 0, 0, 84, 5, 0, 60, 20, 22, 2, 60, 21,
+ 11, 1, 11, 2, 11, 3, 60, 22, 15, 4, 30, 1, 60, 23, 25, 0, 60, 24, 15, 5,
+ 15, 0, 15, 6, 2, 0, 2, 60, 27, 15, 0, 11, 7, 11, 8, 26, 60, 34, 15, 0,
+ 11, 9, 11, 10, 26, 60, 42, 15, 0, 11, 11, 11, 12, 26, 60, 50, 15, 0, 11, 13,
+ 11, 14, 26, 60, 58, 11, 16, 25, 15, 60, 67, 11, 18, 25, 17, 0, 0, 0, 0, 0,
+ 0, 0, 0, 19, 2, 0, 0, 0, 14, 99,108, 97,115,115, 86,101,114, 98, 97,116,
+105,109, 0, 2, 0, 0, 0, 5,108,105,110,101, 0, 2, 0, 0, 0, 1, 0, 2,
+ 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115,
+ 70,101, 97,116,117,114,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0,
+ 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,
+112,114,101, 97,109, 98,108,101, 0, 4, 0, 0, 0, 27, 0, 0, 0, 14, 64,118,
+101,114, 98, 97,116,105,109, 46,108,117, 97, 0, 0, 0, 0, 29, 3, 1, 60, 28,
+ 13, 0, 18, 1, 44, 52, 13, 60, 29, 15, 2, 13, 0, 18, 3, 2, 0, 1, 50, 2,
+ 60, 30, 60, 31, 0, 0, 0, 0, 1, 0, 0, 0, 27, 0, 0, 0, 5,115,101,108,
+102, 0, 0, 0, 0, 4, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 5, 99,111,110,100, 0, 2, 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0,
+ 0, 5,108,105,110,101, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0,
+ 4, 0, 0, 0, 34, 0, 0, 0, 14, 64,118,101,114, 98, 97,116,105,109, 46,108,
+117, 97, 0, 0, 0, 0, 37, 3, 1, 60, 35, 13, 0, 18, 1, 52, 22, 60, 36, 15,
+ 2, 13, 0, 18, 3, 2, 0, 1, 60, 37, 15, 2, 11, 4, 2, 0, 1, 50, 2, 60,
+ 38, 60, 39, 0, 0, 0, 0, 1, 0, 0, 0, 34, 0, 0, 0, 5,115,101,108,102,
+ 0, 0, 0, 0, 5, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,
+ 99,111,110,100, 0, 2, 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0, 0,
+ 5,108,105,110,101, 0, 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 9,114,101,
+103,105,115,116,101,114, 0, 4, 0, 0, 0, 42, 0, 0, 0, 14, 64,118,101,114,
+ 98, 97,116,105,109, 46,108,117, 97, 0, 0, 0, 0, 28, 3, 1, 60, 43, 13, 0,
+ 18, 1, 52, 13, 60, 44, 15, 2, 13, 0, 18, 3, 2, 0, 1, 50, 2, 60, 45, 60,
+ 46, 0, 0, 0, 0, 1, 0, 0, 0, 42, 0, 0, 0, 5,115,101,108,102, 0, 0,
+ 0, 0, 4, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5, 99,111,
+110,100, 0, 2, 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0, 0, 5,108,
+105,110,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 50,
+ 0, 0, 0, 14, 64,118,101,114, 98, 97,116,105,109, 46,108,117, 97, 0, 0, 0,
+ 0, 52, 6, 3, 60, 51, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 52, 15, 2,
+ 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 53, 15, 2,
+ 13, 1, 11, 8, 42, 13, 2, 42, 2, 0, 1, 60, 54, 0, 0, 0, 0, 3, 0, 0,
+ 0, 50, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 50, 0, 0, 0, 6,105,
+100,101,110,116, 0, 0, 0, 0, 50, 0, 0, 0, 6, 99,108,111,115,101, 0, 0,
+ 0, 0, 9, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,
+108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0,
+ 10, 86,101,114, 98, 97,116,105,109,123, 0, 2, 0, 0, 0, 10, 32,108,105,110,
+101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 5,108,105,110,101, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 2,125,
+ 0, 2, 0, 0, 0, 10, 95, 86,101,114, 98, 97,116,105,109, 0, 4, 0, 0, 0,
+ 58, 0, 0, 0, 14, 64,118,101,114, 98, 97,116,105,109, 46,108,117, 97, 0, 0,
+ 0, 0, 40, 4, 1, 60, 59, 13, 0, 11, 1, 15, 2, 26, 60, 60, 15, 3, 13, 0,
+ 15, 4, 2, 0, 2, 60, 61, 15, 5, 13, 0, 2, 0, 1, 60, 62, 13, 0, 1, 1,
+ 60, 63, 0, 0, 0, 0, 1, 0, 0, 0, 58, 0, 0, 0, 2,116, 0, 0, 0, 0,
+ 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2,
+ 0, 0, 0, 14, 99,108, 97,115,115, 86,101,114, 98, 97,116,105,109, 0, 2, 0,
+ 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97,
+ 95,116, 97,103, 0, 2, 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0,
+ 0, 9, 86,101,114, 98, 97,116,105,109, 0, 4, 0, 0, 0, 67, 0, 0, 0, 14,
+ 64,118,101,114, 98, 97,116,105,109, 46,108,117, 97, 0, 0, 0, 0, 75, 8, 1,
+ 60, 68, 4, 0, 60, 69, 15, 2, 13, 0, 7, 1, 7, 1, 2, 1, 3, 11, 3, 32,
+ 52, 21, 60, 70, 7, 1, 23, 1, 60, 71, 15, 2, 13, 0, 7, 2, 2, 1, 2, 23,
+ 0, 50, 2, 60, 72, 60, 73, 15, 4, 22, 2, 60, 74, 11, 5, 13, 0, 11, 6, 60,
+ 75, 13, 1, 30, 1, 60, 76, 3, 2, 1, 60, 77, 0, 0, 0, 0, 2, 0, 0, 0,
+ 67, 0, 0, 0, 2,108, 0, 0, 0, 0, 68, 0, 0, 0, 2, 99, 0, 0, 0, 0,
+ 7, 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2, 99, 0, 2, 0, 0, 0, 7,
+115,116,114,115,117, 98, 0, 2, 0, 0, 0, 2, 36, 0, 2, 0, 0, 0, 10, 95,
+ 86,101,114, 98, 97,116,105,109, 0, 2, 0, 0, 0, 5,108,105,110,101, 0, 2,
+ 0, 0, 0, 5, 99,111,110,100, 0,
+};
+
+/* code.lo */
+static char B16[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 10, 64, 99,111,100,101, 46,
+108,117, 97, 0, 0, 0, 0, 66, 5, 0, 60, 19, 22, 2, 60, 20, 11, 1, 11, 2,
+ 11, 3, 60, 21, 15, 4, 30, 1, 60, 22, 25, 0, 60, 23, 15, 5, 15, 0, 15, 6,
+ 2, 0, 2, 60, 26, 15, 0, 11, 7, 11, 8, 26, 60, 63, 15, 0, 11, 9, 11, 10,
+ 26, 60, 71, 11, 12, 25, 11, 60, 80, 11, 14, 25, 13, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 2, 0, 0, 0, 10, 99,108, 97,115,115, 67,111,100,101, 0, 2, 0, 0,
+ 0, 5,116,101,120,116, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 6, 95, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,
+101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,
+111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,
+101,114, 0, 4, 0, 0, 0, 26, 0, 0, 0, 10, 64, 99,111,100,101, 46,108,117,
+ 97, 0, 0, 0, 0,239, 13, 1, 60, 27, 15, 1, 2, 1, 0, 60, 28, 15, 1, 2,
+ 1, 0, 60, 29, 15, 4, 13, 2, 11, 5, 2, 1, 2, 60, 30, 13, 3, 44, 52, 11,
+ 60, 31, 15, 6, 11, 7, 2, 0, 1, 50, 2, 60, 32, 60, 34, 15, 8, 13, 3, 13,
+ 0, 18, 10, 2, 0, 2, 60, 35, 15, 11, 13, 3, 2, 0, 1, 60, 36, 15, 12, 11,
+ 13, 13, 1, 42, 11, 14, 42, 13, 2, 42, 2, 0, 1, 60, 37, 15, 15, 13, 2, 2,
+ 0, 1, 60, 40, 15, 4, 13, 1, 11, 17, 2, 1, 2, 60, 41, 13, 4, 4, 0, 32,
+ 52, 11, 60, 42, 15, 6, 11, 7, 2, 0, 1, 50, 2, 60, 43, 60, 44, 15, 19, 13,
+ 4, 11, 20, 2, 1, 2, 60, 45, 15, 11, 13, 4, 2, 0, 1, 60, 46, 15, 15, 13,
+ 1, 2, 0, 1, 60, 48, 15, 21, 11, 22, 2, 0, 1, 60, 49, 15, 21, 11, 23, 2,
+ 0, 1, 60, 50, 22, 1, 11, 2, 7, 0, 30, 0, 60, 51, 15, 21, 15, 25, 13, 5,
+ 11, 26, 13, 6, 58, 27, 1, 60, 55, 2, 1, 3, 2, 0, 1, 60, 56, 15, 21, 11,
+ 28, 2, 0, 1, 60, 57, 15, 21, 11, 29, 2, 0, 1, 60, 58, 15, 21, 11, 30, 2,
+ 0, 1, 60, 59, 0, 0, 0, 0, 7, 0, 0, 0, 26, 0, 0, 0, 5,115,101,108,
+102, 0, 0, 0, 0, 27, 0, 0, 0, 2,111, 0, 0, 0, 0, 28, 0, 0, 0, 2,
+110, 0, 0, 0, 0, 29, 0, 0, 0, 2,102, 0, 0, 0, 0, 40, 0, 0, 0, 3,
+102,112, 0, 0, 0, 0, 44, 0, 0, 0, 2,115, 0, 0, 0, 0, 50, 0, 0, 0,
+ 2,116, 0, 0, 0, 0, 31, 2, 0, 0, 0, 2,111, 0, 2, 0, 0, 0, 8,116,
+109,112,110, 97,109,101, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,102,
+ 0, 2, 0, 0, 0, 9,111,112,101,110,102,105,108,101, 0, 2, 0, 0, 0, 2,
+119, 0, 2, 0, 0, 0, 6,101,114,114,111,114, 0, 2, 0, 0, 0, 61, 10, 32,
+ 32, 32, 99, 97,110,110,111,116, 32,111,112,101,110, 32,116,101,109,112,111,114,
+ 97,114,121, 32,102,105,108,101, 32,116,111, 32,112,114,111, 99, 99,101,115,115,
+ 32,101,109, 98,101,100,100,101,100, 32, 76,117, 97, 32, 99,111,100,101, 0, 2,
+ 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 5,116,101,120,116, 0, 2, 0, 0, 0, 10, 99,108,111,115,101,
+102,105,108,101, 0, 2, 0, 0, 0, 8,101,120,101, 99,117,116,101, 0, 2, 0,
+ 0, 0, 9,108,117, 97, 99, 32, 45,111, 32, 0, 2, 0, 0, 0, 2, 32, 0, 2,
+ 0, 0, 0, 7,114,101,109,111,118,101, 0, 2, 0, 0, 0, 3,102,112, 0, 2,
+ 0, 0, 0, 3,114, 98, 0, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 5,114,
+101, 97,100, 0, 2, 0, 0, 0, 3, 46, 42, 0, 2, 0, 0, 0, 7,111,117,116,
+112,117,116, 0, 2, 0, 0, 0, 35, 10, 32,123, 32, 47, 42, 32, 98,101,103,105,
+110, 32,101,109, 98,101,100,100,101,100, 32,108,117, 97, 32, 99,111,100,101, 32,
+ 42, 47, 10, 0, 2, 0, 0, 0, 35, 32, 32,115,116, 97,116,105, 99, 32,117,110,
+115,105,103,110,101,100, 32, 99,104, 97,114, 32, 66, 91, 93, 32, 61, 32,123, 10,
+ 32, 32, 32, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 5,103,115,117, 98,
+ 0, 2, 0, 0, 0, 4, 40, 46, 41, 0, 4, 0, 0, 0, 51, 0, 0, 0, 10, 64,
+ 99,111,100,101, 46,108,117, 97, 0, 0, 0, 0, 61, 6, 1, 60, 52, 11, 2, 60,
+ 53, 12, 0, 11, 4, 12, 0, 18, 4, 7, 1, 37, 26, 12, 0, 18, 4, 7, 20, 32,
+ 52, 11, 12, 0, 11, 4, 7, 0, 26, 11, 5, 23, 1, 60, 54, 15, 6, 11, 7, 15,
+ 8, 13, 0, 2, 1, 1, 13, 1, 3, 2, 3, 60, 55, 0, 0, 0, 0, 2, 0, 0,
+ 0, 51, 0, 0, 0, 2, 99, 0, 0, 0, 0, 52, 0, 0, 0, 2,101, 0, 0, 0,
+ 0, 9, 2, 0, 0, 0, 2, 99, 0, 2, 0, 0, 0, 2,101, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0,
+ 5, 10, 32, 32, 32, 0, 2, 0, 0, 0, 7,102,111,114,109, 97,116, 0, 2, 0,
+ 0, 0, 7, 37, 51,117, 44, 37,115, 0, 2, 0, 0, 0, 8,115,116,114, 98,121,
+116,101, 0, 2, 0, 0, 0, 6, 32, 32,125, 59, 10, 0, 2, 0, 0, 0, 56, 32,
+ 32,108,117, 97, 95,100,111, 98,117,102,102,101,114, 40, 66, 44,115,105,122,101,
+111,102, 40, 66, 41, 44, 34,116,111,108,117, 97, 58, 32,101,109, 98,101,100,100,
+101,100, 32, 76,117, 97, 32, 99,111,100,101, 34, 41, 59, 0, 2, 0, 0, 0, 36,
+ 32,125, 32, 47, 42, 32,101,110,100, 32,111,102, 32,101,109, 98,101,100,100,101,
+100, 32,108,117, 97, 32, 99,111,100,101, 32, 42, 47, 10, 10, 0, 2, 0, 0, 0,
+ 6,112,114,105,110,116, 0, 4, 0, 0, 0, 63, 0, 0, 0, 10, 64, 99,111,100,
+101, 46,108,117, 97, 0, 0, 0, 0, 52, 6, 3, 60, 64, 15, 2, 13, 1, 11, 3,
+ 42, 2, 0, 1, 60, 65, 15, 2, 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7,
+ 42, 2, 0, 1, 60, 66, 15, 2, 13, 1, 11, 8, 42, 13, 2, 42, 2, 0, 1, 60,
+ 67, 0, 0, 0, 0, 3, 0, 0, 0, 63, 0, 0, 0, 5,115,101,108,102, 0, 0,
+ 0, 0, 63, 0, 0, 0, 6,105,100,101,110,116, 0, 0, 0, 0, 63, 0, 0, 0,
+ 6, 99,108,111,115,101, 0, 0, 0, 0, 9, 2, 0, 0, 0, 6,105,100,101,110,
+116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,
+105,110,116, 0, 2, 0, 0, 0, 6, 67,111,100,101,123, 0, 2, 0, 0, 0, 11,
+ 32,116,101,120,116, 32, 61, 32, 91, 91, 0, 2, 0, 0, 0, 5,115,101,108,102,
+ 0, 2, 0, 0, 0, 5,116,101,120,116, 0, 2, 0, 0, 0, 4, 93, 93, 44, 0,
+ 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 6, 95, 67,111,100,101, 0, 4, 0,
+ 0, 0, 71, 0, 0, 0, 10, 64, 99,111,100,101, 46,108,117, 97, 0, 0, 0, 0,
+ 40, 4, 1, 60, 72, 13, 0, 11, 1, 15, 2, 26, 60, 73, 15, 3, 13, 0, 15, 4,
+ 2, 0, 2, 60, 74, 15, 5, 13, 0, 2, 0, 1, 60, 75, 13, 0, 1, 1, 60, 76,
+ 0, 0, 0, 0, 1, 0, 0, 0, 71, 0, 0, 0, 2,116, 0, 0, 0, 0, 6, 2,
+ 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0,
+ 0, 10, 99,108, 97,115,115, 67,111,100,101, 0, 2, 0, 0, 0, 7,115,101,116,
+116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2,
+ 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0, 0, 5, 67,111,100,101,
+ 0, 4, 0, 0, 0, 80, 0, 0, 0, 10, 64, 99,111,100,101, 46,108,117, 97, 0,
+ 0, 0, 0, 24, 5, 1, 60, 81, 15, 1, 22, 1, 60, 82, 11, 2, 13, 0, 30, 0,
+ 60, 83, 3, 1, 1, 60, 84, 0, 0, 0, 0, 1, 0, 0, 0, 80, 0, 0, 0, 2,
+108, 0, 0, 0, 0, 3, 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 6, 95, 67,
+111,100,101, 0, 2, 0, 0, 0, 5,116,101,120,116, 0,
+};
+
+/* doit.lo */
+static char B17[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 10, 64,100,111,105,116, 46,
+108,117, 97, 0, 0, 0, 1, 92, 6, 0, 60, 17, 15, 0, 18, 1, 52, 38, 60, 18,
+ 15, 4, 15, 0, 18, 1, 2, 2, 1, 60, 19, 13, 0, 44, 52, 14, 60, 20, 15, 5,
+ 11, 6, 13, 1, 42, 2, 0, 1, 50, 2, 60, 21, 5, 2, 50, 2, 60, 22, 60, 25,
+ 15, 0, 18, 7, 44, 52, 43, 60, 26, 15, 0, 18, 1, 52, 22, 60, 27, 15, 0, 11,
+ 7, 15, 8, 15, 0, 18, 1, 11, 9, 11, 10, 2, 1, 3, 26, 50, 11, 60, 29, 15,
+ 5, 11, 11, 2, 0, 1, 60, 30, 50, 2, 60, 31, 60, 33, 15, 13, 15, 0, 18, 7,
+ 2, 1, 1, 60, 35, 15, 0, 18, 1, 52, 9, 60, 36, 15, 4, 2, 0, 0, 50, 2,
+ 60, 37, 60, 39, 15, 0, 18, 12, 52, 4, 60, 41, 1, 1, 60, 43, 15, 0, 18, 14,
+ 52, 38, 60, 44, 15, 15, 15, 0, 18, 14, 2, 2, 1, 60, 45, 13, 1, 44, 52, 14,
+ 60, 46, 15, 5, 11, 6, 13, 2, 42, 2, 0, 1, 50, 2, 60, 47, 5, 2, 50, 2,
+ 60, 48, 60, 50, 15, 0, 18, 16, 52, 11, 60, 51, 13, 0, 20, 17, 2, 0, 1, 50,
+ 38, 60, 53, 13, 0, 20, 18, 2, 0, 1, 60, 54, 13, 0, 20, 19, 2, 0, 1, 60,
+ 55, 13, 0, 20, 20, 2, 0, 1, 60, 56, 13, 0, 20, 21, 2, 0, 1, 60, 57, 60,
+ 59, 15, 0, 18, 14, 52, 9, 60, 60, 15, 15, 2, 0, 0, 50, 2, 60, 61, 60, 64,
+ 15, 0, 18, 16, 44, 52, 66, 60, 65, 15, 0, 18, 22, 52, 54, 60, 66, 15, 15, 15,
+ 0, 18, 22, 2, 2, 1, 60, 67, 13, 1, 44, 52, 14, 60, 68, 15, 5, 11, 6, 13,
+ 2, 42, 2, 0, 1, 50, 2, 60, 69, 60, 70, 13, 0, 20, 23, 2, 0, 1, 60, 71,
+ 15, 15, 2, 0, 0, 5, 2, 50, 2, 60, 72, 50, 2, 60, 73, 0, 0, 0, 0, 0,
+ 0, 0, 0, 24, 2, 0, 0, 0, 6,102,108, 97,103,115, 0, 2, 0, 0, 0, 2,
+102, 0, 2, 0, 0, 0, 3,115,116, 0, 2, 0, 0, 0, 4,109,115,103, 0, 2,
+ 0, 0, 0, 9,114,101, 97,100,102,114,111,109, 0, 2, 0, 0, 0, 6,101,114,
+114,111,114, 0, 2, 0, 0, 0, 2, 35, 0, 2, 0, 0, 0, 2,110, 0, 2, 0,
+ 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 5, 37, 46, 46, 42, 0, 2, 0,
+ 0, 0, 1, 0, 2, 0, 0, 0, 41, 35,110,111, 32,112, 97, 99,107, 97,103,101,
+ 32,110, 97,109,101, 32,110,111,114, 32,105,110,112,117,116, 32,102,105,108,101,
+ 32,112,114,111,118,105,100,101,100, 0, 2, 0, 0, 0, 2,112, 0, 2, 0, 0,
+ 0, 8, 80, 97, 99,107, 97,103,101, 0, 2, 0, 0, 0, 2,111, 0, 2, 0, 0,
+ 0, 8,119,114,105,116,101,116,111, 0, 2, 0, 0, 0, 2, 80, 0, 2, 0, 0,
+ 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 9,112,114,101, 97,109, 98,108,
+101, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0, 2, 0, 0, 0, 9,
+114,101,103,105,115,116,101,114, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,
+115,116,101,114, 0, 2, 0, 0, 0, 2, 72, 0, 2, 0, 0, 0, 7,104,101, 97,
+100,101,114, 0,
+};
+
+ lua_dobuffer(B1,sizeof(B1),"basic.lo");
+ lua_dobuffer(B2,sizeof(B2),"feature.lo");
+ lua_dobuffer(B3,sizeof(B3),"declaration.lo");
+ lua_dobuffer(B4,sizeof(B4),"container.lo");
+ lua_dobuffer(B5,sizeof(B5),"package.lo");
+ lua_dobuffer(B6,sizeof(B6),"module.lo");
+ lua_dobuffer(B7,sizeof(B7),"class.lo");
+ lua_dobuffer(B8,sizeof(B8),"typedef.lo");
+ lua_dobuffer(B9,sizeof(B9),"define.lo");
+ lua_dobuffer(B10,sizeof(B10),"enumerate.lo");
+ lua_dobuffer(B11,sizeof(B11),"variable.lo");
+ lua_dobuffer(B12,sizeof(B12),"array.lo");
+ lua_dobuffer(B13,sizeof(B13),"function.lo");
+ lua_dobuffer(B14,sizeof(B14),"operator.lo");
+ lua_dobuffer(B15,sizeof(B15),"verbatim.lo");
+ lua_dobuffer(B16,sizeof(B16),"code.lo");
+ lua_dobuffer(B17,sizeof(B17),"doit.lo");
+}
diff --git a/src/lua/tolualua.pkg b/src/lua/tolualua.pkg
new file mode 100644
index 00000000..1694c2c1
--- /dev/null
+++ b/src/lua/tolualua.pkg
@@ -0,0 +1,21 @@
+$[
+$<basic.lua>
+$<feature.lua>
+$<verbatim.lua>
+$<code.lua>
+$<typedef.lua>
+$<container.lua>
+$<package.lua>
+$<module.lua>
+$<define.lua>
+$<enumerate.lua>
+$<declaration.lua>
+$<variable.lua>
+$<array.lua>
+$<function.lua>
+$<operator.lua>
+$<class.lua>
+$<clean.lua>
+$<doit.lua>
+$]
+
diff --git a/src/lua/typedef.lua b/src/lua/typedef.lua
new file mode 100644
index 00000000..1633f3e6
--- /dev/null
+++ b/src/lua/typedef.lua
@@ -0,0 +1,59 @@
+-- tolua: typedef class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: typedef.lua,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Typedef class
+-- Represents a type synonym.
+-- The 'de facto' type replaces the typedef before the
+-- remaining code is parsed.
+-- The following fields are stored:
+-- utype = typedef name
+-- type = 'de facto' type
+-- mod = modifiers to the 'de facto' type
+classTypedef = {
+ utype = '',
+ mod = '',
+ type = ''
+}
+
+-- Print method
+function classTypedef:print (ident,close)
+ print(ident.."Typedef{")
+ print(ident.." utype = '"..self.utype.."',")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Typedef (t)
+ t._base = classTypedef
+ settag(t,tolua_tag)
+ appendtypedef(t)
+ return t
+end
+
+-- Constructor
+-- Expects one string representing the type definition.
+function Typedef (s)
+ if strfind(s,'[%*&]') then
+ tolua_error("#invalid typedef: pointers (and references) are not supported")
+ end
+ local t = split(gsub(s,"%s%s*"," ")," ")
+ return _Typedef {
+ utype = t[t.n],
+ type = t[t.n-1],
+ mod = concat(t,1,t.n-2)
+ }
+end
+
+
diff --git a/src/lua/variable.lua b/src/lua/variable.lua
new file mode 100644
index 00000000..310808b8
--- /dev/null
+++ b/src/lua/variable.lua
@@ -0,0 +1,192 @@
+-- tolua: variable class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: variable.lua,v 1.4 2004/06/04 13:42:10 neil Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Variable class
+-- Represents a extern variable or a public member of a class.
+-- Stores all fields present in a declaration.
+classVariable = {
+ _base = classDeclaration,
+}
+
+settag(classVariable,tolua_tag)
+
+-- Print method
+function classVariable:print (ident,close)
+ print(ident.."Variable{")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." def = '"..self.def.."',")
+ print(ident.." ret = '"..self.ret.."',")
+ print(ident.."}"..close)
+end
+
+-- get variable value
+function classVariable:getvalue (class,static)
+ if class and static then
+ return class..'::'..self.name
+ elseif class then
+ return 'self->'..self.name
+ else
+ return self.name
+ end
+end
+
+-- Write binding functions
+function classVariable:supcode ()
+ local class = self:inclass()
+
+ -- get function ------------------------------------------------
+ if class then
+ output("/* get function:",self.name," of class ",class," */")
+ else
+ output("/* get function:",self.name," */")
+ end
+ self.cgetname = self:cfuncname("toluaI_get")
+ output("static int",self.cgetname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- declare self, if the case
+ local _,_,static = strfind(self.mod,'^%s*(static)')
+ if class and static==nil then
+ output(' ',class,'*','self = ')
+ output('(',class,'*) ')
+ output('tolua_getusertype(tolua_S,1,0);')
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ end
+
+
+ -- check self value
+ if class and static==nil then
+ output(' if (!self) TOLUA_ERR_SELF;');
+ end
+
+ -- return value
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_push'..t..'(tolua_S,(',ct,')'..self:getvalue(class,static)..');')
+ else
+ if self.ptr == '&' or self.ptr == '' then
+ output(' tolua_pushusertype(tolua_S,(void*)&'..self:getvalue(class,static)..',',self.tag,');')
+ else
+ output(' tolua_pushusertype(tolua_S,(void*)'..self:getvalue(class,static)..',',self.tag,');')
+ end
+ end
+ output(' return 1;')
+ output('}')
+ output('\n')
+
+ -- set function ------------------------------------------------
+ if not strfind(self.mod,'const') then
+ if class then
+ output("/* set function:",self.name," of class ",class," */")
+ else
+ output("/* set function:",self.name," */")
+ end
+ self.csetname = self:cfuncname("toluaI_set")
+ output("static int",self.csetname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- declare self, if the case
+ local narg=1
+ if class and static==nil then
+ output(' ',class,'*','self = ')
+ output('(',class,'*) ')
+ output('tolua_getusertype(tolua_S,1,0);')
+ -- check self value
+ output(' if (!self) TOLUA_ERR_SELF;');
+ narg = narg+1
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ narg = narg+1
+ end
+
+ -- check type
+ output(' if (!'..self:outchecktype(narg)..')')
+ output(' TOLUA_ERR_ASSIGN;')
+
+ -- assign value
+ local ptr = ''
+ if self.ptr~='' then ptr = '*' end
+ output(' ')
+ if class and static then
+ output(class..'::'..self.name)
+ elseif class then
+ output('self->'..self.name)
+ else
+ output(self.name)
+ end
+ local t = isbasic(self.type)
+ output(' = ')
+ if not t and ptr=='' then output('*') end
+ output('((',self.mod,self.type)
+ if not t then
+ output('*')
+ end
+ output(') ')
+ local def = 0
+ if self.def ~= '' then def = self.def end
+ if t then
+ output('tolua_get'..t,'(tolua_S,',narg,',',def,'));')
+ else
+ output('tolua_getusertype(tolua_S,',narg,',',def,'));')
+ end
+ output(' return 0;')
+ output('}')
+ output('\n')
+ end
+
+end
+
+function classVariable:register ()
+ local parent = self:inclass() or self:inmodule()
+ if parent then
+ if self.csetname then
+ output(' tolua_tablevar(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..','..self.csetname..');')
+ else
+ output(' tolua_tablevar(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..',NULL);')
+ end
+ else
+ if self.csetname then
+ output(' tolua_globalvar(tolua_S,"'..self.lname..'",'..self.cgetname..','..self.csetname..');')
+ else
+ output(' tolua_globalvar(tolua_S,"'..self.lname..'",'..self.cgetname..',NULL);')
+ end
+ end
+end
+
+function classVariable:unregister ()
+ if self:inclass()==nil and self:inmodule()==nil then
+ output(' lua_getglobals(tolua_S);')
+ output(' lua_pushstring(tolua_S,"',self.lname,'"); lua_pushnil(tolua_S); lua_rawset(tolua_S,-3);')
+ output(' lua_pop(tolua_S,1);')
+ end
+end
+
+
+-- Internal constructor
+function _Variable (t)
+ t._base = classVariable
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the variable declaration.
+function Variable (s)
+ return _Variable (Declaration(s,'var'))
+end
+
+
diff --git a/src/lua/verbatim.lua b/src/lua/verbatim.lua
new file mode 100644
index 00000000..9dae0dc3
--- /dev/null
+++ b/src/lua/verbatim.lua
@@ -0,0 +1,77 @@
+-- tolua: verbatim class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: verbatim.lua,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Verbatim class
+-- Represents a line translated directed to the binding file.
+-- The following filds are stored:
+-- line = line text
+classVerbatim = {
+ line = '',
+ _base = classFeature,
+}
+settag(classVerbatim,tolua_tag)
+
+-- preamble verbatim
+function classVerbatim:preamble ()
+ if not self.cond then
+ write(self.line)
+ end
+end
+
+-- support code
+function classVerbatim:supcode ()
+ if self.cond then
+ write(self.line)
+ write('\n')
+ end
+end
+
+-- register code
+function classVerbatim:register ()
+ if self.cond then
+ write(self.line)
+ end
+end
+
+
+-- Print method
+function classVerbatim:print (ident,close)
+ print(ident.."Verbatim{")
+ print(ident.." line = '"..self.line.."',")
+ print(ident.."}"..close)
+end
+
+
+-- Internal constructor
+function _Verbatim (t)
+ t._base = classVerbatim
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the text line
+function Verbatim (l)
+ local c
+ if strsub(l,1,1) == '$' then
+ c = 1
+ l = strsub(l,2)
+ end
+ return _Verbatim {
+ line = l,
+ cond = c
+ }
+end
+
+
diff --git a/src/lua_bind.c b/src/lua_bind.c
new file mode 100644
index 00000000..67b75ee6
--- /dev/null
+++ b/src/lua_bind.c
@@ -0,0 +1,691 @@
+/* File: lua_bind.c */
+
+/* Purpose: various lua bindings */
+
+/*
+ * Copyright (c) 2001 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#include "lua.h"
+#include "tolua.h"
+extern lua_State *L;
+
+magic_power *grab_magic_power(magic_power *m_ptr, int num)
+{
+ return (&m_ptr[num]);
+}
+
+bool_ lua_spell_success(magic_power *spell, int stat, char *oups_fct)
+{
+ int chance;
+ int minfail = 0;
+
+ /* Spell failure chance */
+ chance = spell->fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (p_ptr->lev - spell->min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[stat]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell->mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell->mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[stat]];
+
+ /* Failure rate */
+ chance = clamp_failure_chance(chance, minfail);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+
+ if (oups_fct != NULL)
+ exec_lua(format("%s(%d)", oups_fct, chance));
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+/*
+ * Create objects
+ */
+object_type *new_object()
+{
+ object_type *o_ptr;
+ MAKE(o_ptr, object_type);
+ return (o_ptr);
+}
+
+void end_object(object_type *o_ptr)
+{
+ FREE(o_ptr, object_type);
+}
+
+/*
+ * Powers
+ */
+s16b add_new_power(cptr name, cptr desc, cptr gain, cptr lose, byte level, byte cost, byte stat, byte diff)
+{
+ /* Increase the size */
+ reinit_powers_type(power_max + 1);
+
+ /* Copy the strings */
+ C_MAKE(powers_type[power_max - 1].name, strlen(name) + 1, char);
+ strcpy(powers_type[power_max - 1].name, name);
+ C_MAKE(powers_type[power_max - 1].desc_text, strlen(desc) + 1, char);
+ strcpy(powers_type[power_max - 1].desc_text, desc);
+ C_MAKE(powers_type[power_max - 1].gain_text, strlen(gain) + 1, char);
+ strcpy(powers_type[power_max - 1].gain_text, gain);
+ C_MAKE(powers_type[power_max - 1].lose_text, strlen(lose) + 1, char);
+ strcpy(powers_type[power_max - 1].lose_text, lose);
+
+ /* Copy the other stuff */
+ powers_type[power_max - 1].level = level;
+ powers_type[power_max - 1].cost = cost;
+ powers_type[power_max - 1].stat = stat;
+ powers_type[power_max - 1].diff = diff;
+
+ return (power_max - 1);
+}
+
+static char *lua_item_tester_fct;
+static bool_ lua_item_tester(object_type* o_ptr)
+{
+ int oldtop = lua_gettop(L);
+ bool_ ret;
+
+ lua_getglobal(L, lua_item_tester_fct);
+ tolua_pushusertype(L, o_ptr, tolua_tag(L, "object_type"));
+ lua_call(L, 1, 1);
+ ret = lua_tonumber(L, -1);
+ lua_settop(L, oldtop);
+ return (ret);
+}
+
+void lua_set_item_tester(int tval, char *fct)
+{
+ if (tval)
+ {
+ item_tester_tval = tval;
+ }
+ else
+ {
+ lua_item_tester_fct = fct;
+ item_tester_hook = lua_item_tester;
+ }
+}
+
+char *lua_object_desc(object_type *o_ptr, int pref, int mode)
+{
+ static char buf[150];
+
+ object_desc(buf, o_ptr, pref, mode);
+ return (buf);
+}
+
+/*
+ * Monsters
+ */
+
+void find_position(int y, int x, int *yy, int *xx)
+{
+ int attempts = 500;
+
+ do
+ {
+ scatter(yy, xx, y, x, 6);
+ }
+ while (!(in_bounds(*yy, *xx) && cave_floor_bold(*yy, *xx)) && --attempts);
+}
+
+static char *summon_lua_okay_fct;
+bool_ summon_lua_okay(int r_idx)
+{
+ int oldtop = lua_gettop(L);
+ bool_ ret;
+
+ lua_getglobal(L, lua_item_tester_fct);
+ tolua_pushnumber(L, r_idx);
+ lua_call(L, 1, 1);
+ ret = lua_tonumber(L, -1);
+ lua_settop(L, oldtop);
+ return (ret);
+}
+
+bool_ lua_summon_monster(int y, int x, int lev, bool_ friend_, char *fct)
+{
+ summon_lua_okay_fct = fct;
+
+ if (!friend_)
+ return summon_specific(y, x, lev, SUMMON_LUA);
+ else
+ return summon_specific_friendly(y, x, lev, SUMMON_LUA, TRUE);
+}
+
+/*
+ * Quests
+ */
+s16b add_new_quest(char *name)
+{
+ int i;
+
+ /* Increase the size */
+ reinit_quests(max_q_idx + 1);
+ quest[max_q_idx - 1].type = HOOK_TYPE_LUA;
+ strncpy(quest[max_q_idx - 1].name, name, 39);
+
+ for (i = 0; i < 10; i++)
+ strncpy(quest[max_q_idx - 1].desc[i], "", 39);
+
+ return (max_q_idx - 1);
+}
+
+void desc_quest(int q_idx, int d, char *desc)
+{
+ if (d >= 0 && d < 10)
+ strncpy(quest[q_idx].desc[d], desc, 79);
+}
+
+/*
+ * Misc
+ */
+bool_ get_com_lua(cptr prompt, int *com)
+{
+ char c;
+
+ if (!get_com(prompt, &c)) return (FALSE);
+ *com = c;
+ return (TRUE);
+}
+
+/* Spell schools */
+s16b new_school(int i, cptr name, s16b skill)
+{
+ schools[i].name = string_make(name);
+ schools[i].skill = skill;
+ return (i);
+}
+
+s16b new_spell(int i, cptr name)
+{
+ school_spells[i].name = string_make(name);
+ school_spells[i].level = 0;
+ school_spells[i].level = 0;
+ return (i);
+}
+
+spell_type *grab_spell_type(s16b num)
+{
+ return (&school_spells[num]);
+}
+
+school_type *grab_school_type(s16b num)
+{
+ return (&schools[num]);
+}
+
+/* Change this fct if I want to switch to learnable spells */
+s32b lua_get_level(s32b s, s32b lvl, s32b max, s32b min, s32b bonus)
+{
+ s32b tmp;
+
+ tmp = lvl - ((school_spells[s].skill_level - 1) * (SKILL_STEP / 10));
+
+ if (tmp >= (SKILL_STEP / 10)) /* We require at least one spell level */
+ tmp += bonus;
+
+ tmp = (tmp * (max * (SKILL_STEP / 10)) / (SKILL_MAX / 10));
+
+ if (tmp < 0) /* Shift all negative values, so they map to appropriate integer */
+ tmp -= SKILL_STEP / 10 - 1;
+
+ /* Now, we can safely divide */
+ lvl = tmp / (SKILL_STEP / 10);
+
+ if (lvl < min)
+ lvl = min;
+
+ return lvl;
+}
+
+s32b lua_spell_chance(s32b chance, int level, int skill_level, int mana, int cur_mana, int stat)
+{
+ int minfail;
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (level - 1);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[stat]] - 1);
+
+ /* Not enough mana to cast */
+ if (chance < 0) chance = 0;
+ if (mana > cur_mana)
+ {
+ chance += 15 * (mana - cur_mana);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[stat]];
+
+ /*
+ * Non mage characters never get too good
+ */
+ if (!(has_ability(AB_PERFECT_CASTING)))
+ {
+ if (minfail < 5) minfail = 5;
+ }
+
+ /* Hack -- Priest prayer penalty for "edged" weapons -DGK */
+ if ((forbid_non_blessed()) && (p_ptr->icky_wield)) chance += 25;
+
+ /* Return the chance */
+ return clamp_failure_chance(chance, minfail);
+}
+
+s32b lua_spell_device_chance(s32b chance, int level, int base_level)
+{
+ int minfail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= (level - 1);
+
+ /* Extract the minimum failure rate */
+ minfail = 15 - get_skill_scale(SKILL_DEVICE, 25);
+
+ /* Return the chance */
+ return clamp_failure_chance(chance, minfail);
+}
+
+/* Cave */
+cave_type *lua_get_cave(int y, int x)
+{
+ return (&(cave[y][x]));
+}
+
+void set_target(int y, int x)
+{
+ target_who = -1;
+ target_col = x;
+ target_row = y;
+}
+
+void get_target(int dir, int *y, int *x)
+{
+ int ty, tx;
+
+ /* Use the given direction */
+ tx = p_ptr->px + (ddx[dir] * 100);
+ ty = p_ptr->py + (ddy[dir] * 100);
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+ *y = ty;
+ *x = tx;
+}
+
+/* Level gen */
+void get_map_size(char *name, int *ysize, int *xsize)
+{
+ *xsize = 0;
+ *ysize = 0;
+ init_flags = INIT_GET_SIZE;
+ process_dungeon_file(name, ysize, xsize, cur_hgt, cur_wid, TRUE, TRUE);
+}
+
+void load_map(char *name, int *y, int *x)
+{
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(name, y, x, cur_hgt, cur_wid, TRUE, TRUE);
+}
+
+bool_ alloc_room(int by0, int bx0, int ysize, int xsize, int *y1, int *x1, int *y2, int *x2)
+{
+ int xval, yval, x, y;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return FALSE;
+
+ /* Get corner values */
+ *y1 = yval - ysize / 2;
+ *x1 = xval - xsize / 2;
+ *y2 = yval + (ysize) / 2;
+ *x2 = xval + (xsize) / 2;
+
+ /* Place a full floor under the room */
+ for (y = *y1 - 1; y <= *y2 + 1; y++)
+ {
+ for (x = *x1 - 1; x <= *x2 + 1; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+ c_ptr->info |= (CAVE_ROOM);
+ c_ptr->info |= (CAVE_GLOW);
+ }
+ }
+ return TRUE;
+}
+
+
+/* Files */
+void lua_print_hook(cptr str)
+{
+ fprintf(hook_file, "%s", str);
+}
+
+
+/*
+ * Finds a good random bounty monster
+ * Im too lazy to write it in lua since the lua API for monsters is not very well yet
+ */
+
+/*
+ * Hook for bounty monster selection.
+ */
+static bool_ lua_mon_hook_bounty(int r_idx)
+{
+ monster_race* r_ptr = &r_info[r_idx];
+
+
+ /* Reject uniques */
+ if (r_ptr->flags1 & RF1_UNIQUE) return (FALSE);
+
+ /* Reject those who cannot leave anything */
+ if (!(r_ptr->flags9 & RF9_DROP_CORPSE)) return (FALSE);
+
+ /* Accept only monsters that can be generated */
+ if (r_ptr->flags9 & RF9_SPECIAL_GENE) return (FALSE);
+ if (r_ptr->flags9 & RF9_NEVER_GENE) return (FALSE);
+
+ /* Reject pets */
+ if (r_ptr->flags7 & RF7_PET) return (FALSE);
+
+ /* Reject friendly creatures */
+ if (r_ptr->flags7 & RF7_FRIENDLY) return (FALSE);
+
+ /* Accept only monsters that are not breeders */
+ if (r_ptr->flags4 & RF4_MULTIPLY) return (FALSE);
+
+ /* Forbid joke monsters */
+ if (r_ptr->flags8 & RF8_JOKEANGBAND) return (FALSE);
+
+ /* Accept only monsters that are not good */
+ if (r_ptr->flags3 & RF3_GOOD) return (FALSE);
+
+ /* The rest are acceptable */
+ return (TRUE);
+}
+
+int lua_get_new_bounty_monster(int lev)
+{
+ int r_idx;
+
+ /*
+ * Set up the hooks -- no bounties on uniques or monsters
+ * with no corpses
+ */
+ get_mon_num_hook = lua_mon_hook_bounty;
+ get_mon_num_prep();
+
+ /* Set up the quest monster. */
+ r_idx = get_mon_num(lev);
+
+ /* Undo the filters */
+ get_mon_num_hook = NULL;
+ get_mon_num_prep();
+
+ return r_idx;
+}
+
+/*
+ * Some misc functions
+ */
+char *lua_input_box(cptr title, int max)
+{
+ static char buf[80];
+ int wid, hgt;
+
+ strcpy(buf, "");
+ Term_get_size(&wid, &hgt);
+ if (!input_box(title, hgt / 2, wid / 2, buf, (max > 79) ? 79 : max))
+ return "";
+ return buf;
+}
+
+char lua_msg_box(cptr title)
+{
+ int wid, hgt;
+
+ Term_get_size(&wid, &hgt);
+ return msg_box(title, hgt / 2, wid / 2);
+}
+
+list_type *lua_create_list(int size)
+{
+ list_type *l;
+ cptr *list;
+
+ MAKE(l, list_type);
+ C_MAKE(list, size, cptr);
+ l->list = list;
+ return l;
+}
+
+void lua_delete_list(list_type *l, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ string_free(l->list[i]);
+ C_FREE(l->list, size, cptr);
+ FREE(l, list_type);
+}
+
+void lua_add_to_list(list_type *l, int idx, cptr str)
+{
+ l->list[idx] = string_make(str);
+}
+
+void lua_display_list(int y, int x, int h, int w, cptr title, list_type* list, int max, int begin, int sel, byte sel_color)
+{
+ display_list(y, x, h, w, title, list->list, max, begin, sel, sel_color);
+}
+
+/*
+ * Gods
+ */
+s16b add_new_gods(char *name)
+{
+ int i;
+
+ /* Increase the size */
+ reinit_gods(max_gods + 1);
+ deity_info[max_gods - 1].name = string_make(name);
+
+ for (i = 0; i < 10; i++)
+ strncpy(deity_info[max_gods - 1].desc[i], "", 39);
+
+ return (max_gods - 1);
+}
+
+void desc_god(int g_idx, int d, char *desc)
+{
+ if (d >= 0 && d < 10)
+ strncpy(deity_info[g_idx].desc[d], desc, 79);
+}
+
+/*
+ * Returns the direction of the compass that y2, x2 is from y, x
+ * the return value will be one of the following: north, south,
+ * east, west, north-east, south-east, south-west, north-west,
+ * or "close" if it is within 2 tiles.
+ */
+cptr compass(int y, int x, int y2, int x2)
+{
+ static char compass_dir[64];
+
+ // is it close to the north/south meridian?
+ int y_diff = y2 - y;
+
+ // determine if y2, x2 is to the north or south of y, x
+ const char *y_axis;
+ if ((y_diff > -3) && (y_diff < 3))
+ {
+ y_axis = 0;
+ }
+ else if (y2 > y)
+ {
+ y_axis = "south";
+ }
+ else
+ {
+ y_axis = "north";
+ }
+
+ // is it close to the east/west meridian?
+ int x_diff = x2 - x;
+
+ // determine if y2, x2 is to the east or west of y, x
+ const char *x_axis;
+ if ((x_diff > -3) && (x_diff < 3))
+ {
+ x_axis = 0;
+ }
+ else if (x2 > x)
+ {
+ x_axis = "east";
+ }
+ else
+ {
+ x_axis = "west";
+ }
+
+ // Maybe it is very close
+ if ((!x_axis) && (!y_axis)) { strcpy(compass_dir, "close"); }
+ // Maybe it is (almost) due N/S
+ else if (!x_axis) { strcpy(compass_dir, y_axis); }
+ // Maybe it is (almost) due E/W
+ else if (!y_axis) { strcpy(compass_dir, x_axis); }
+ // or if it is neither
+ else { sprintf(compass_dir, "%s-%s", y_axis, x_axis); }
+ return compass_dir;
+}
+
+/* Returns a relative approximation of the 'distance' of y2, x2 from y, x. */
+cptr approximate_distance(int y, int x, int y2, int x2)
+{
+ // how far to away to the north/south?
+ int y_diff = abs(y2 - y);
+ // how far to away to the east/west?
+ int x_diff = abs(x2 - x);
+ // find which one is the larger distance
+ int most_dist = x_diff;
+ if (y_diff > most_dist) {
+ most_dist = y_diff;
+ }
+
+ // how far away then?
+ if (most_dist >= 41) {
+ return "a very long way";
+ } else if (most_dist >= 25) {
+ return "a long way";
+ } else if (most_dist >= 8) {
+ return "quite some way";
+ } else {
+ return "not very far";
+ }
+}
+
+bool_ drop_text_left(byte c, cptr str, int y, int o)
+{
+ int i = strlen(str);
+ int x = 39 - (strlen(str) / 2) + o;
+ while (i > 0)
+ {
+ int a = 0;
+ int time = 0;
+
+ if (str[i-1] != ' ')
+ {
+ while (a < x + i - 1)
+ {
+ Term_putch(a - 1, y, c, 32);
+ Term_putch(a, y, c, str[i-1]);
+ time = time + 1;
+ if (time >= 4)
+ {
+ Term_xtra(TERM_XTRA_DELAY, 1);
+ time = 0;
+ }
+ Term_redraw_section(a - 1, y, a, y);
+ a = a + 1;
+
+ inkey_scan = TRUE;
+ if (inkey()) {
+ return TRUE;
+ }
+ }
+ }
+
+ i = i - 1;
+ }
+ return FALSE;
+}
+
+bool_ drop_text_right(byte c, cptr str, int y, int o)
+{
+ int x = 39 - (strlen(str) / 2) + o;
+ int i = 1;
+ while (i <= strlen(str))
+ {
+ int a = 79;
+ int time = 0;
+
+ if (str[i-1] != ' ') {
+ while (a >= x + i - 1)
+ {
+ Term_putch(a + 1, y, c, 32);
+ Term_putch(a, y, c, str[i-1]);
+ time = time + 1;
+ if (time >= 4) {
+ Term_xtra(TERM_XTRA_DELAY, 1);
+ time = 0;
+ }
+ Term_redraw_section(a, y, a + 1, y);
+ a = a - 1;
+
+ inkey_scan = TRUE;
+ if (inkey()) {
+ return TRUE;
+ }
+ }
+ }
+
+ i = i + 1;
+ }
+ return FALSE;
+}
diff --git a/src/maid-x11.c b/src/maid-x11.c
new file mode 100755
index 00000000..86df2119
--- /dev/null
+++ b/src/maid-x11.c
@@ -0,0 +1,855 @@
+/* File: maid-x11.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#if defined(USE_X11) || defined(USE_XAW)
+
+/*
+ * This file defines some "XImage" manipulation functions for X11.
+ *
+ * Original code by Desvignes Sebastien (desvigne@solar12.eerie.fr).
+ *
+ * BMP format support by Denis Eropkin (denis@dream.homepage.ru).
+ *
+ * Major fixes and cleanup by Ben Harrison (benh@phial.com).
+ *
+ * This file is designed to be "included" by "main-x11.c" or "main-xaw.c",
+ * which will have already "included" several relevant header files.
+ */
+
+#ifndef IsModifierKey
+
+/*
+ * Keysym macros, used on Keysyms to test for classes of symbols
+ * These were stolen from one of the X11 header files
+ *
+ * Also appears in "main-x11.c".
+ */
+
+#define IsKeypadKey(keysym) \
+(((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))
+
+#define IsCursorKey(keysym) \
+(((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select))
+
+#define IsPFKey(keysym) \
+(((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4))
+
+#define IsFunctionKey(keysym) \
+(((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35))
+
+#define IsMiscFunctionKey(keysym) \
+(((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space))
+
+#define IsModifierKey(keysym) \
+(((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R))
+
+#endif /* IsModifierKey */
+
+
+/*
+ * Checks if the keysym is a special key or a normal key
+ * Assume that XK_MISCELLANY keysyms are special
+ *
+ * Also appears in "main-x11.c".
+ */
+#define IsSpecialKey(keysym) \
+((unsigned)(keysym) >= 0xFF00)
+
+
+/*
+ * Hack -- Convert an RGB value to an X11 Pixel, or die.
+ */
+static unsigned long create_pixel(Display *dpy, byte red, byte green, byte blue)
+{
+ Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy));
+
+ char cname[8];
+
+ XColor xcolour;
+
+ /* Build the color */
+
+ xcolour.red = red * 255 + red;
+ xcolour.green = green * 255 + green;
+ xcolour.blue = blue * 255 + blue;
+ xcolour.flags = DoRed | DoGreen | DoBlue;
+
+ /* Attempt to Allocate the Parsed color */
+ if (!(XAllocColor(dpy, cmap, &xcolour)))
+ {
+ quit_fmt("Couldn't allocate bitmap color '%s'\n", cname);
+ }
+
+ return (xcolour.pixel);
+}
+
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * The Win32 "BITMAPFILEHEADER" type.
+ */
+typedef struct BITMAPFILEHEADER
+{
+ u16b bfType;
+ u32b bfSize;
+ u16b bfReserved1;
+ u16b bfReserved2;
+ u32b bfOffBits;
+}
+BITMAPFILEHEADER;
+
+
+/*
+ * The Win32 "BITMAPINFOHEADER" type.
+ */
+typedef struct BITMAPINFOHEADER
+{
+ u32b biSize;
+ u32b biWidth;
+ u32b biHeight;
+ u16b biPlanes;
+ u16b biBitCount;
+ u32b biCompresion;
+ u32b biSizeImage;
+ u32b biXPelsPerMeter;
+ u32b biYPelsPerMeter;
+ u32b biClrUsed;
+ u32b biClrImportand;
+}
+BITMAPINFOHEADER;
+
+/*
+ * The Win32 "RGBQUAD" type.
+ */
+typedef struct RGBQUAD
+{
+ unsigned char b, g, r;
+ unsigned char filler;
+}
+RGBQUAD;
+
+
+/*** Helper functions for system independent file loading. ***/
+
+static byte get_byte(FILE *fff)
+{
+ /* Get a character, and return it */
+ return (getc(fff) & 0xFF);
+}
+
+static void rd_byte(FILE *fff, byte *ip)
+{
+ *ip = get_byte(fff);
+}
+
+static void rd_u16b(FILE *fff, u16b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u16b)(get_byte(fff)) << 8);
+}
+
+static void rd_u32b(FILE *fff, u32b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u32b)(get_byte(fff)) << 8);
+ (*ip) |= ((u32b)(get_byte(fff)) << 16);
+ (*ip) |= ((u32b)(get_byte(fff)) << 24);
+}
+
+
+/*
+ * Read a Win32 BMP file.
+ *
+ * This function replaces the old ReadRaw and RemapColors functions.
+ *
+ * Assumes that the bitmap has a size such that no padding is needed in
+ * various places. Currently only handles bitmaps with 3 to 256 colors.
+ */
+static XImage *ReadBMP(Display *dpy, char *Name)
+{
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+
+ int depth = DefaultDepth(dpy, DefaultScreen(dpy));
+
+ FILE *f;
+
+ BITMAPFILEHEADER fileheader;
+ BITMAPINFOHEADER infoheader;
+
+ XImage *Res = NULL;
+
+ char *Data;
+
+ int ncol;
+
+ int total;
+
+ int i, j;
+
+ u32b x, y;
+
+ unsigned long clr_pixels[256];
+
+
+ /* Open the BMP file */
+ f = fopen(Name, "r");
+
+ /* No such file */
+ if (f == NULL)
+ {
+ return (NULL);
+ }
+
+ /* Read the "BITMAPFILEHEADER" */
+ rd_u16b(f, &(fileheader.bfType));
+ rd_u32b(f, &(fileheader.bfSize));
+ rd_u16b(f, &(fileheader.bfReserved1));
+ rd_u16b(f, &(fileheader.bfReserved2));
+ rd_u32b(f, &(fileheader.bfOffBits));
+
+ /* Read the "BITMAPINFOHEADER" */
+ rd_u32b(f, &(infoheader.biSize));
+ rd_u32b(f, &(infoheader.biWidth));
+ rd_u32b(f, &(infoheader.biHeight));
+ rd_u16b(f, &(infoheader.biPlanes));
+ rd_u16b(f, &(infoheader.biBitCount));
+ rd_u32b(f, &(infoheader.biCompresion));
+ rd_u32b(f, &(infoheader.biSizeImage));
+ rd_u32b(f, &(infoheader.biXPelsPerMeter));
+ rd_u32b(f, &(infoheader.biYPelsPerMeter));
+ rd_u32b(f, &(infoheader.biClrUsed));
+ rd_u32b(f, &(infoheader.biClrImportand));
+
+ /* Verify the header */
+ if (feof(f) ||
+ (fileheader.bfType != 19778) ||
+ (infoheader.biSize != 40))
+ {
+ quit_fmt("Incorrect BMP file format %s", Name);
+ }
+
+ /* The two headers above occupy 54 bytes total */
+ /* The "bfOffBits" field says where the data starts */
+ /* The "biClrUsed" field does not seem to be reliable */
+ /* Compute number of colors recorded */
+ ncol = (fileheader.bfOffBits - 54) / 4;
+
+ for (i = 0; i < ncol; i++)
+ {
+ RGBQUAD clrg;
+
+ /* Read an "RGBQUAD" */
+ rd_byte(f, &(clrg.b));
+ rd_byte(f, &(clrg.g));
+ rd_byte(f, &(clrg.r));
+ rd_byte(f, &(clrg.filler));
+
+ /* Analyze the color */
+ clr_pixels[i] = create_pixel(dpy, clrg.r, clrg.g, clrg.b);
+ }
+
+ /* Determine total bytes needed for image */
+ i = 1;
+ j = (depth - 1) >> 2;
+ while (j >>= 1) i <<= 1;
+ total = infoheader.biWidth * infoheader.biHeight * i;
+
+ /* Allocate image memory */
+ C_MAKE(Data, total, char);
+
+ Res = XCreateImage(dpy, visual, depth, ZPixmap, 0 /*offset*/,
+ Data, infoheader.biWidth, infoheader.biHeight,
+ 8 /*bitmap_pad*/, 0 /*bytes_per_line*/);
+
+ /* Failure */
+ if (Res == NULL)
+ {
+ C_KILL(Data, total, char);
+ fclose(f);
+ return (NULL);
+ }
+
+ for (y = 0; y < infoheader.biHeight; y++)
+ {
+ int y2 = infoheader.biHeight - y - 1;
+
+ for (x = 0; x < infoheader.biWidth; x++)
+ {
+ int ch = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name);
+
+ if (infoheader.biBitCount == 24)
+ {
+ int c2 = getc(f);
+ int c3 = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name);
+
+ XPutPixel(Res, x, y2, create_pixel(dpy, ch, c2, c3));
+ }
+ else if (infoheader.biBitCount == 8)
+ {
+ XPutPixel(Res, x, y2, clr_pixels[ch]);
+ }
+ else if (infoheader.biBitCount == 4)
+ {
+ XPutPixel(Res, x, y2, clr_pixels[ch / 16]);
+ x++;
+ XPutPixel(Res, x, y2, clr_pixels[ch % 16]);
+ }
+ else
+ {
+ /* Technically 1 bit is legal too */
+ quit_fmt("Illegal biBitCount %d in %s",
+ infoheader.biBitCount, Name);
+ }
+ }
+ }
+
+ fclose(f);
+
+ return Res;
+}
+
+
+/* ========================================================*/
+/* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */
+/* ========================================================*/
+
+/*
+ * to save ourselves some labour, define a maximum expected icon width here:
+ */
+#define MAX_ICON_WIDTH 32
+
+
+/* some static variables for composing and decomposing pixel values into
+ * red, green and blue values
+ */
+static unsigned long redMask, greenMask, blueMask;
+static int redShift, greenShift, blueShift;
+
+
+/*
+ * Use smooth rescaling?
+ */
+static bool_ smoothRescaling = TRUE;
+
+
+/*
+ * GetScaledRow reads a scan from the given XImage, scales it smoothly
+ * and returns the red, green and blue values in arrays.
+ * The values in this arrays must be divided by a certain value that is
+ * calculated in ScaleIcon.
+ * x, y is the position, iw is the input width and ow the output width
+ * redScan, greenScan and blueScan must be sufficiently sized
+ */
+static void GetScaledRow(XImage *Im, int x, int y, int iw, int ow,
+ unsigned long *redScan, unsigned long *greenScan,
+ unsigned long *blueScan)
+{
+ int xi, si, sifrac, ci, cifrac, addWhole, addFrac;
+ unsigned long pix;
+ int prevRed, prevGreen, prevBlue, nextRed, nextGreen, nextBlue;
+ bool_ getNextPix;
+
+ if (iw == ow)
+ {
+ /* unscaled */
+ for (xi = 0; xi < ow; xi++)
+ {
+ pix = XGetPixel(Im, x + xi, y);
+ redScan [xi] = (pix >> redShift) & redMask;
+ greenScan [xi] = (pix >> greenShift) & greenMask;
+ blueScan [xi] = (pix >> blueShift) & blueMask;
+ }
+ }
+ else if (iw < ow)
+ {
+ /* scaling by subsampling (grow) */
+ iw--;
+ ow--;
+ /* read first pixel: */
+ pix = XGetPixel(Im, x, y);
+ nextRed = (pix >> redShift) & redMask;
+ nextGreen = (pix >> greenShift) & greenMask;
+ nextBlue = (pix >> blueShift) & blueMask;
+ prevRed = nextRed;
+ prevGreen = nextGreen;
+ prevBlue = nextBlue;
+ /* si and sifrac give the subsampling position: */
+ si = x;
+ sifrac = 0;
+ /* getNextPix tells us, that we need the next pixel */
+ getNextPix = TRUE;
+
+ for (xi = 0; xi <= ow; xi++)
+ {
+ if (getNextPix)
+ {
+ prevRed = nextRed;
+ prevGreen = nextGreen;
+ prevBlue = nextBlue;
+ if (xi < ow)
+ {
+ /* only get next pixel if in same icon */
+ pix = XGetPixel(Im, si + 1, y);
+ nextRed = (pix >> redShift) & redMask;
+ nextGreen = (pix >> greenShift) & greenMask;
+ nextBlue = (pix >> blueShift) & blueMask;
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by ow occurs in ScaleIcon */
+ redScan [xi] = prevRed * (ow - sifrac) + nextRed * sifrac;
+ greenScan [xi] = prevGreen * (ow - sifrac) + nextGreen * sifrac;
+ blueScan [xi] = prevBlue * (ow - sifrac) + nextBlue * sifrac;
+
+ /* advance sampling position: */
+ sifrac += iw;
+ if (sifrac >= ow)
+ {
+ si++;
+ sifrac -= ow;
+ getNextPix = TRUE;
+ }
+ else
+ {
+ getNextPix = FALSE;
+ }
+
+ }
+ }
+ else
+ {
+ /* scaling by averaging (shrink) */
+ /* width of an output pixel in input pixels: */
+ addWhole = iw / ow;
+ addFrac = iw % ow;
+ /* start position of the first output pixel: */
+ si = x;
+ sifrac = 0;
+ /* get first input pixel: */
+ pix = XGetPixel(Im, x, y);
+ nextRed = (pix >> redShift) & redMask;
+ nextGreen = (pix >> greenShift) & greenMask;
+ nextBlue = (pix >> blueShift) & blueMask;
+ for (xi = 0; xi < ow; xi++)
+ {
+ /* find endpoint of the current output pixel: */
+ ci = si + addWhole;
+ cifrac = sifrac + addFrac;
+ if (cifrac >= ow)
+ {
+ ci++;
+ cifrac -= ow;
+ }
+ /* take fraction of current input pixel (starting segment): */
+ redScan[xi] = nextRed * (ow - sifrac);
+ greenScan[xi] = nextGreen * (ow - sifrac);
+ blueScan[xi] = nextBlue * (ow - sifrac);
+ si++;
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ pix = XGetPixel(Im, si, y);
+ redScan[xi] += ((pix >> redShift) & redMask) * ow;
+ greenScan[xi] += ((pix >> greenShift) & greenMask) * ow;
+ blueScan[xi] += ((pix >> blueShift) & blueMask) * ow;
+ si++;
+ }
+ /* add fraction of current input pixel (ending segment): */
+ if (xi < ow - 1)
+ {
+ /* only get next pixel if still in icon: */
+ pix = XGetPixel(Im, si, y);
+ nextRed = (pix >> redShift) & redMask;
+ nextGreen = (pix >> greenShift) & greenMask;
+ nextBlue = (pix >> blueShift) & blueMask;
+ }
+ sifrac = cifrac;
+ if (sifrac > 0)
+ {
+ redScan[xi] += nextRed * sifrac;
+ greenScan[xi] += nextGreen * sifrac;
+ blueScan[xi] += nextBlue * sifrac;
+ }
+ }
+ }
+}
+
+
+/*
+ * PutRGBScan takes arrays for red, green and blue and writes pixel values
+ * according to this values in the XImage-structure. w is the number of
+ * pixels to write and div is the value by which all red/green/blue values
+ * are divided first.
+ */
+static void PutRGBScan(XImage *Im, int x, int y, int w, int div,
+ unsigned long *redScan, unsigned long *greenScan,
+ unsigned long *blueScan)
+{
+ int xi;
+ unsigned long pix;
+ unsigned long adj = div / 2;
+ for (xi = 0; xi < w; xi++)
+ {
+ pix = (((((redScan[xi] + adj) / div) & redMask) << redShift) +
+ ((((greenScan[xi] + adj) / div) & greenMask) << greenShift) +
+ ((((blueScan[xi] + adj) / div) & blueMask) << blueShift));
+ XPutPixel(Im, x + xi, y, pix);
+ }
+}
+
+
+/*
+ * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut,
+ * locate (x2, y2).
+ * Source size is (ix, iy) and destination size is (ox, oy).
+ * It does this by getting icon scan line from GetScaledScan and handling
+ * them the same way as pixels are handled in GetScaledScan.
+ * This even allows icons to be scaled differently in horizontal and
+ * vertical directions (eg. shrink horizontal, grow vertical).
+ */
+static void ScaleIcon(XImage *ImIn, XImage *ImOut,
+ int x1, int y1, int x2, int y2,
+ int ix, int iy, int ox, int oy)
+{
+ int div;
+ int xi, yi, si, sifrac, ci, cifrac, addWhole, addFrac;
+
+ /* buffers for pixel rows: */
+ unsigned long prevRed [MAX_ICON_WIDTH];
+ unsigned long prevGreen [MAX_ICON_WIDTH];
+ unsigned long prevBlue [MAX_ICON_WIDTH];
+ unsigned long nextRed [MAX_ICON_WIDTH];
+ unsigned long nextGreen [MAX_ICON_WIDTH];
+ unsigned long nextBlue [MAX_ICON_WIDTH];
+ unsigned long tempRed [MAX_ICON_WIDTH];
+ unsigned long tempGreen [MAX_ICON_WIDTH];
+ unsigned long tempBlue [MAX_ICON_WIDTH];
+
+ bool_ getNextRow;
+
+ /* get divider value for the horizontal scaling: */
+ if (ix == ox)
+ div = 1;
+ else if (ix < ox)
+ div = ox - 1;
+ else
+ div = ix;
+
+ if (iy == oy)
+ {
+ /* no scaling needed vertically: */
+ for (yi = 0; yi < oy; yi++)
+ {
+ GetScaledRow(ImIn, x1, y1 + yi, ix, ox,
+ tempRed, tempGreen, tempBlue);
+ PutRGBScan(ImOut, x2, y2 + yi, ox, div,
+ tempRed, tempGreen, tempBlue);
+ }
+ }
+ else if (iy < oy)
+ {
+ /* scaling by subsampling (grow): */
+ iy--;
+ oy--;
+ div *= oy;
+ /* get first row: */
+ GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
+ /* si and sifrac give the subsampling position: */
+ si = y1;
+ sifrac = 0;
+ /* getNextRow tells us, that we need the next row */
+ getNextRow = TRUE;
+ for (yi = 0; yi <= oy; yi++)
+ {
+ if (getNextRow)
+ {
+ for (xi = 0; xi < ox; xi++)
+ {
+ prevRed[xi] = nextRed[xi];
+ prevGreen[xi] = nextGreen[xi];
+ prevBlue[xi] = nextBlue[xi];
+ }
+ if (yi < oy)
+ {
+ /* only get next row if in same icon */
+ GetScaledRow(ImIn, x1, si + 1, ix, ox,
+ nextRed, nextGreen, nextBlue);
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by oy occurs in PutRGBScan */
+ for (xi = 0; xi < ox; xi++)
+ {
+ tempRed[xi] = (prevRed[xi] * (oy - sifrac) +
+ nextRed[xi] * sifrac);
+ tempGreen[xi] = (prevGreen[xi] * (oy - sifrac) +
+ nextGreen[xi] * sifrac);
+ tempBlue[xi] = (prevBlue[xi] * (oy - sifrac) +
+ nextBlue[xi] * sifrac);
+ }
+
+ /* write row to output image: */
+ PutRGBScan(ImOut, x2, y2 + yi, ox, div,
+ tempRed, tempGreen, tempBlue);
+
+ /* advance sampling position: */
+ sifrac += iy;
+ if (sifrac >= oy)
+ {
+ si++;
+ sifrac -= oy;
+ getNextRow = TRUE;
+ }
+ else
+ {
+ getNextRow = FALSE;
+ }
+
+ }
+ }
+ else
+ {
+ /* scaling by averaging (shrink) */
+ div *= iy;
+ /* height of a output row in input rows: */
+ addWhole = iy / oy;
+ addFrac = iy % oy;
+ /* start position of the first output row: */
+ si = y1;
+ sifrac = 0;
+ /* get first input row: */
+ GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
+ for (yi = 0; yi < oy; yi++)
+ {
+ /* find endpoint of the current output row: */
+ ci = si + addWhole;
+ cifrac = sifrac + addFrac;
+ if (cifrac >= oy)
+ {
+ ci++;
+ cifrac -= oy;
+ }
+ /* take fraction of current input row (starting segment): */
+ for (xi = 0; xi < ox; xi++)
+ {
+ tempRed[xi] = nextRed[xi] * (oy - sifrac);
+ tempGreen[xi] = nextGreen[xi] * (oy - sifrac);
+ tempBlue[xi] = nextBlue[xi] * (oy - sifrac);
+ }
+ si++;
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ GetScaledRow(ImIn, x1, si, ix, ox,
+ nextRed, nextGreen, nextBlue);
+ for (xi = 0; xi < ox; xi++)
+ {
+ tempRed[xi] += nextRed[xi] * oy;
+ tempGreen[xi] += nextGreen[xi] * oy;
+ tempBlue[xi] += nextBlue[xi] * oy;
+ }
+ si++;
+ }
+ /* add fraction of current input row (ending segment): */
+ if (yi < oy - 1)
+ {
+ /* only get next row if still in icon: */
+ GetScaledRow(ImIn, x1, si, ix, ox,
+ nextRed, nextGreen, nextBlue);
+ }
+ sifrac = cifrac;
+ for (xi = 0; xi < ox; xi++)
+ {
+ tempRed[xi] += nextRed[xi] * sifrac;
+ tempGreen[xi] += nextGreen[xi] * sifrac;
+ tempBlue[xi] += nextBlue[xi] * sifrac;
+ }
+ /* write row to output image: */
+ PutRGBScan(ImOut, x2, y2 + yi, ox, div,
+ tempRed, tempGreen, tempBlue);
+ }
+ }
+}
+
+
+
+static XImage *ResizeImageSmooth(Display *dpy, XImage *Im,
+ int ix, int iy, int ox, int oy)
+{
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+
+ int width1, height1, width2, height2;
+ int x1, x2, y1, y2;
+
+ XImage *Tmp;
+
+ char *Data;
+
+ width1 = Im->width;
+ height1 = Im->height;
+
+ width2 = ox * width1 / ix;
+ height2 = oy * height1 / iy;
+
+ Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
+
+ Tmp = XCreateImage(dpy, visual,
+ Im->depth, ZPixmap, 0, Data, width2, height2,
+ 32, 0);
+
+ /* compute values for decomposing pixel into color values: */
+ redMask = Im->red_mask;
+ redShift = 0;
+ while ((redMask & 1) == 0)
+ {
+ redShift++;
+ redMask >>= 1;
+ }
+ greenMask = Im->green_mask;
+ greenShift = 0;
+ while ((greenMask & 1) == 0)
+ {
+ greenShift++;
+ greenMask >>= 1;
+ }
+ blueMask = Im->blue_mask;
+ blueShift = 0;
+ while ((blueMask & 1) == 0)
+ {
+ blueShift++;
+ blueMask >>= 1;
+ }
+
+ /* scale each icon: */
+ for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy)
+ {
+ for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox)
+ {
+ ScaleIcon(Im, Tmp, x1, y1, x2, y2,
+ ix, iy, ox, oy);
+ }
+ }
+
+ return Tmp;
+}
+
+/*
+ * Resize an image. XXX XXX XXX
+ *
+ * Also appears in "main-xaw.c".
+ */
+static XImage *ResizeImage(Display *dpy, XImage *Im,
+ int ix, int iy, int ox, int oy)
+{
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+
+ int width1, height1, width2, height2;
+ int x1, x2, y1, y2, Tx, Ty;
+ int *px1, *px2, *dx1, *dx2;
+ int *py1, *py2, *dy1, *dy2;
+
+ XImage *Tmp;
+
+ char *Data;
+
+ if (smoothRescaling && (ix != ox || iy != oy) &&
+ visual->class == TrueColor)
+ {
+ return ResizeImageSmooth(dpy, Im, ix, iy, ox, oy);
+ }
+
+ width1 = Im->width;
+ height1 = Im->height;
+
+ width2 = ox * width1 / ix;
+ height2 = oy * height1 / iy;
+
+ Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
+
+ Tmp = XCreateImage(dpy, visual,
+ Im->depth, ZPixmap, 0, Data, width2, height2,
+ 32, 0);
+
+ if (ix > ox)
+ {
+ px1 = &x1;
+ px2 = &x2;
+ dx1 = &ix;
+ dx2 = &ox;
+ }
+ else
+ {
+ px1 = &x2;
+ px2 = &x1;
+ dx1 = &ox;
+ dx2 = &ix;
+ }
+
+ if (iy > oy)
+ {
+ py1 = &y1;
+ py2 = &y2;
+ dy1 = &iy;
+ dy2 = &oy;
+ }
+ else
+ {
+ py1 = &y2;
+ py2 = &y1;
+ dy1 = &oy;
+ dy2 = &iy;
+ }
+
+ Ty = *dy1 / 2;
+
+ for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); )
+ {
+ Tx = *dx1 / 2;
+
+ for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); )
+ {
+ XPutPixel(Tmp, x2, y2, XGetPixel(Im, x1, y1));
+
+ (*px1)++;
+
+ Tx -= *dx2;
+ if (Tx < 0)
+ {
+ Tx += *dx1;
+ (*px2)++;
+ }
+ }
+
+ (*py1)++;
+
+ Ty -= *dy2;
+ if (Ty < 0)
+ {
+ Ty += *dy1;
+ (*py2)++;
+ }
+ }
+
+ return Tmp;
+}
+
+#endif /* USE_GRAPHICS */
+
+#endif /* USE_X11 || USE_XAW */
diff --git a/src/main-crb.c b/src/main-crb.c
new file mode 100644
index 00000000..a4a1a742
--- /dev/null
+++ b/src/main-crb.c
@@ -0,0 +1,6402 @@
+/* File: main-crb.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, Keith Randall, Peter Ammon, Ron Anderson
+ * and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with Macintosh computers running OS X,
+ * or OS 8/9 with CarbonLib system extention.
+ *
+ * To use this file, use an appropriate "Makefile" or "Project File", which
+ * should define "MACINTOSH".
+ *
+ * The official compilation uses the CodeWarrior Pro compiler.
+ *
+ * If you are never going to use "graphics" (especially if you are not
+ * compiling support for graphics anyway) then you can delete the "pict"
+ * resources with id "1001", "1002", "1003" and "1004" with no dangerous
+ * side effects.
+ *
+ *
+ * This file assumes that you will be using a PPC Mac running OS X
+ * or OS 8/9 (8.6 or greater) with CarbonLib system extention enabled.
+ * In fact, the game will refuse to run unless these features are available.
+ *
+ * MACH_O_CARBON code pushes the system requirement a bit further, and
+ * I don't think it works on System 8, even with CarbonLib, because it uses
+ * the Bundle services, but I may be wrong.
+ *
+ * Note that the "preference" file is now a simple XML text file
+ * called "<program name>.plist" in case of PEF Carbon, and "<Java-style
+ * program id defined in Info.plist>.plist" for Mach-O Carbon, which contains
+ * key-value paris, so it no longer has to check version stamp to validate
+ * its contents.
+ *
+ *
+ * Note that "init1.c", "init2.c", "load1.c", "load2.c", and "birth.c"
+ * should probably be "unloaded" as soon as they are no longer needed,
+ * to save space, but I do not know how to do this. XXX XXX XXX
+ *
+ * Stange bug -- The first "ClipRect()" call crashes if the user closes
+ * all the windows, switches to another application, switches back, and
+ * re-opens the main window, for example, using "command-a". XXX XXX XXX
+ *
+ *
+ * Initial framework (and most code) by Ben Harrison (benh@phial.com).
+ *
+ * Some code adapted from "MacAngband 2.6.1" by Keith Randall
+ *
+ * Initial PowerMac port by Maarten Hazewinkel (mmhazewi@cs.ruu.nl).
+ *
+ * Most Apple Event code provided by Steve Linberg (slinberg@crocker.com).
+ *
+ * Most of the graphics code is adapted from an extremely minimal subset of
+ * the "Sprite World II" package, an amazing (and free) animation package.
+ *
+ * Carbon code adapted from works by Peter Ammon and Ron Anderson.
+ *
+ * (List of changes made by "pelpel" follow)
+ * Some API calls are updated to OS 8.x-- ones.
+ *
+ * Pixmap locking code in Term_pict_map() follows Carbon Porting Guide
+ * by Apple.
+ *
+ * The idle loop in TERM_XTRA_DELAY is rewritten to sleep on WaitNextEvent
+ * for a couple of reasons.
+ *
+ * CheckEvent now really blocks whenever asked to wait.
+ *
+ * The unused buffer GWorld is completely removed. It has long been pure waste
+ * of memory.
+ *
+ * The default font-size combination was changed because the old one, Monaco
+ * at 12 points causes the redraw artefact problem on OS X.
+ *
+ * Characters in the ASCII mode are clipped by their bounding rects to reduce
+ * redraw artefacts that were quite annoying in certain font-point combos.
+ *
+ * Transparency effect now avoids double bitblts whenever possible.
+ *
+ * Old tiles were drawn in a wrong fashion by the USE_TRANSPARENCY code.
+ *
+ * ASCII and the two graphics modes are now controlled by single graf_mode
+ * variable. arg_* and use_* variables are set when requested mode is
+ * successfully initialised.
+ *
+ * Most of the menus are now loaded from resources.
+ *
+ * Moved TileWidth and TileHeight menus into Special. There were too many menus.
+ *
+ * Added support for 32x32 tiles, now for [V] only.
+ *
+ * Related to the above, globe_init no longer loads tile images twice if
+ * a tileset doesn't have corresponding masks.
+ *
+ * Added support for POSIX-style pathnames, for Mach-O Carbon (gcc, CW >= 7).
+ * We can finally live without Pascal strings to handle files this way.
+ *
+ * (Mach-O Carbon) Graphics tiles are moved out of the resource fork into
+ * bundle-based data fork files.
+ *
+ * Changed size-related menu code, because they no longer function because
+ * some APIs have been changed to return Unicode in some cases.
+ *
+ * Changed the transparency code again, this time using Ron Anderson's code,
+ * which makes more sound assumption about background colour and is more
+ * efficient.
+ *
+ * The old asynchronous sound player could try to lock the same handle more
+ * than once, load same sound resource already in use, or unlock and release
+ * currently playing sound.
+ *
+ * hook_quit() now releases memory-related resources dynamically allocated by
+ * the graphics and sound code.
+ *
+ * Important Resources in the resource file:
+ *
+ * FREF 130 = ANGBAND_CREATOR / 'APPL' (application)
+ * FREF 129 = ANGBAND_CREATOR / 'SAVE' (save file)
+ * FREF 130 = ANGBAND_CREATOR / 'TEXT' (generic text file)
+ * FREF 131 = ANGBAND_CREATOR / 'DATA' (binary image file, score file)
+ *
+ * DLOG 128 = "About Angband..."
+ *
+ * ALRT 128 = unused (?)
+ * ALRT 129 = "Warning..."
+ *
+ * DITL 128 = body for DLOG 128
+ * DITL 129 = body for ALRT 129
+ * DITL 130 = body for ALRT 130
+ *
+ * ICON 128 = "warning" icon
+ *
+ * MBAR 128 = array of MENU id's (128, 129, 130, 131, 132, 133, 134)
+ * MENU 128 = apple (about, -, ...)
+ * MENU 129 = File (new, open, close, save, -, score, quit)
+ * (If SAVEFILE_SCREEN is defined)
+ * MENU 129 = File (close, save, -, score, quit)
+ * MENU 130 = Edit (undo, -, cut, copy, paste, clear)
+ * MENU 131 = Font (bold, wide, -)
+ * MENU 132 = Size ()
+ * MENU 133 = Windows ()
+ * MENU 134 = Special (Sound, Graphics, TileWidth, TileHeight, -, Fiddle,
+ * Wizard)
+ * Graphics have following submenu attached:
+ * MENU 144 = Graphics (None, 8x8, 16x16, 32x32, enlarge tiles)
+ * TileWidth and TileHeight submenus are filled in by this program.
+ * MENU 145 = TileWidth ()
+ * MENU 146 = TileHeight ()
+ *
+ * On CFM(PEF) Carbon only:
+ * PICT 1001 = Graphics tile set (8x8)
+ * PICT 1002 = Graphics tile set (16x16 images)
+ * PICT 1004 = Graphics tile set (32x32)
+ *
+ * Mach-O Carbon now uses data fork resources:
+ * 8x8.png = Graphics tile set (8x8)
+ * 16x16.png = Graphics tile set (16x16 images)
+ * 32x32.png = Graphics tile set (32x32)
+ * These files should go into the Resources subdirectory of an application
+ * bundle.
+ *
+ * STR# 128 = "Please select the "lib" folder"
+ *
+ * plst 0 can be empty, but required for single binary Carbon apps on OS X
+ * Isn't necessary for Mach-O Carbon.
+ *
+ *
+ * File name patterns:
+ * all 'APEX' files have a filename of the form "*:apex:*" (?)
+ * all 'DATA' files have a filename of the form "*:data:*"
+ * all 'SAVE' files have a filename of the form "*:save:*"
+ * all 'USER' files have a filename of the form "*:user:*" (?)
+ *
+ * Perhaps we should attempt to set the "_ftype" flag inside this file,
+ * to avoid nasty file type information being spread all through the
+ * rest of the code. (?) This might require adding hooks into the
+ * "fd_open()" and "my_fopen()" functions in "util.c". XXX XXX XXX
+ *
+ *
+ * Reasons for each header file:
+ *
+ * angband.h = Angband header file
+ *
+ * Types.h = (included anyway)
+ * Gestalt.h = gestalt code
+ * QuickDraw.h = (included anyway)
+ * OSUtils.h = (included anyway)
+ * Files.h = file code
+ * Fonts.h = font code
+ * Menus.h = menu code
+ * Dialogs.h = dialog code
+ * Windows.h = (included anyway)
+ * Palettes.h = palette code
+ * ToolUtils.h = HiWord() / LoWord()
+ * Events.h = event code
+ * Resources.h = resource code
+ * Controls.h = button code
+ * SegLoad.h = ExitToShell(), AppFile, etc
+ * Memory.h = NewPtr(), etc
+ * QDOffscreen.h = GWorld code
+ * Sound.h = Sound code
+ * Navigation.h = save file / lib locating dialogues
+ * CFPreferences.h = Preferences
+ * CFNumber.h = read/write short values from/to preferences
+ */
+
+/*
+ * Yet another main-xxx.c for Carbon (pelpel) - revision 11d
+ *
+ * Since I'm using CodeWarrior, the traditional header files are
+ * #include'd below.
+ *
+ * I also compiled Angband 3.0.2 successfully with OS X's gcc.
+ * Please follow these instructions if you are interested.
+ *
+ * ---(developer CD gcc + makefile porting notes, for Angband 3.0.2)-------
+ * 1. Compiling the binary
+ *
+ * If you try this on OS X + gcc, please use makefile.std, replacing
+ * main.c and main.o with main-crb.c and main-crb.o, removing all main-xxx.c
+ * and main-xxx.o from SRCS and OBJS, and, and use these settings:
+ *
+ * COPTS = -Wall -O1 -g -fpascal-strings
+ * INCLUDES =
+ * DEFINES = -DMACH_O_CARBON -DANGBAND30X
+ * LIBS = -framework CoreFoundation -framework QuickTime -framework Carbon
+ *
+ * -DANGBAND30X only affects main-crb.c. This is because I'm also compiling
+ * a couple of variants, and this arrangement makes my life easier.
+ *
+ * Never, ever #define MACINTOSH. It'll wreck havoc in system interface
+ * (mostly because of totally different pathname convention).
+ *
+ * You might wish to disable some SET_UID features for various reasons:
+ * to have user folder within the lib folder, savefile names etc.
+ *
+ * For the best compatibility with the Classic ports and my PEF Carbon
+ * ports, my_fopen, fd_make and fd_open [in util.c] should call
+ * (void)fsetfileinfo(buf, _fcreator, _ftype);
+ * when a file is successfully opened. Or you'll see odd icons for some files
+ * in the lib folder. In order to do so, extern.h should contain these lines,
+ * within #ifdef MACH_O_CARBON:
+ * extern int fsetfileinfo(char *path, u32b fcreator, u32b ftype);
+ * extern u32b _fcreator;
+ * extern u32b _ftype;
+ * And enable the four FILE_TYPE macros in h-config.h for defined(MACH_O_CARBON)
+ * in addition to defined(MACINTOSH) && !defined(applec), i.e.
+ * #if defined(MACINTOSH) && !defined(applec) || defined(MACH_O_CARBON)
+ *
+ * This is a very good way to spot bugs in use of these macros, btw.
+ *
+ * 2. Installation
+ *
+ * The "angband" binary must be arranged this way for it to work:
+ *
+ * lib/ <- the lib folder
+ * Angband (OS X).app/
+ * Contents/
+ * MacOS/
+ * angband <- the binary you've just compiled
+ * Info.plist <- to be explained below
+ * Resources/
+ * Angband.icns
+ * Data.icns
+ * Edit.icns
+ * Save.icns
+ * 8x8.png <- 8x8 tiles
+ * 16x16.png <- 16x16 tiles
+ * angband.rsrc <- see below
+ *
+ * 3. Preparing Info.plist
+ *
+ * Info.plist is an XML file describing some attributes of an application,
+ * and this is appropriate for Angband:
+ *
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <plist version="1.0">
+ * <dict>
+ * <key>CFBundleName</key><string>Angband</string>
+ * <key>CFBundleDisplayName</key><string>Angband (OS X)</string>
+ * <key>CFBundleExecutable</key><string>angband</string>
+ * <key>CFBundlePackageType</key><string>APPL</string>
+ * <key>CFBundleSignature</key><string>A271</string>
+ * <key>CFBundleVersion</key><string>3.0.2</string>
+ * <key>CFBundleShortVersionString</key><string>3.0.2</string>
+ * <key>CFBundleIconFile</key><string>Angband</string>
+ * <key>CFBundleIdentifier</key><string>net.thangorodrim.Angband</string>
+ * <key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
+ * <key>CFBundleDocumentTypes</key>
+ * <array>
+ * <dict>
+ * <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ * <key>CFBundleTypeIconFile</key><string>Save</string>
+ * <key>CFBundleTypeName</key><string>Angband saved game</string>
+ * <key>CFBundleTypeOSTypes</key><array><string>SAVE</string></array>
+ * <key>CFBundleTypeRole</key><string>Editor</string>
+ * </dict>
+ * <dict>
+ * <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ * <key>CFBundleTypeIconFile</key><string>Edit</string>
+ * <key>CFBundleTypeName</key><string>Angband game data</string>
+ * <key>CFBundleTypeOSTypes</key><array><string>TEXT</string></array>
+ * <key>CFBundleTypeRole</key><string>Editor</string>
+ * </dict>
+ * <dict>
+ * <key>CFBundleTypeExtentions</key><array><string>raw</string></array>
+ * <key>CFBundleTypeIconFile</key><string>Data</string>
+ * <key>CFBundleTypeName</key><string>Angband game data</string>
+ * <key>CFBundleTypeOSTypes</key><array><string>DATA</string></array>
+ * <key>CFBundleTypeRole</key><string>Editor</string>
+ * </dict>
+ * </array>
+ * </dict>
+ * </plist>
+ *
+ * 4. Menu, diaglogue and gfx resources
+ *
+ * The binary assumes angband.rsrc should be in the traditional resource
+ * mangager format. Please run this command to create it from its textual
+ * description:
+ *
+ * Rez -i /Developer/Headers/FlatCarbon -d MACH_O -o angband.rsrc Angband.r
+ *
+ * The command is in /Developer/Tools. You might wish to include it in your
+ * PATH.
+ *
+ * It's better to comment out the definitions of BNDL and plst resources
+ * before you do that. I think you can DeRez the resulting tome.rsrc and
+ * feed it to the Interface Builder to produce a set of compatible .nib files,
+ * but this file also needs to be updated to understand .nib... On the other
+ * hand, I really don't like to hardcode UI definitions in C.
+ *
+ * Graphics resources are moved out of the resource fork and become ordinary
+ * PNG files. Make sure to set its resolution to 72 dpi (<- VERY important)
+ * while keeping vertical and horizontal scaling factor to 100% (<- VERY
+ * important), when you convert tiles in any formats to PNG. This means
+ * that the real size of an image must shrink or grow when you change it's dpi.
+ *
+ * Sound resources are a bit more complicated.
+ * The easiest way is:
+ * 1) Grab recent Mac Angband binary.
+ * 2) Run this command:
+ * DeRez -only 'snd ' (Angband binary) > sound.r
+ * 3) And specify sound.r files in addition to Angband.r when you run Rez.
+ *
+ * ---(end of OS X + gcc porting note)--------------------------------------
+ *
+ * Code adapted from Peter Ammon's work on 2.8.3 and some modifications
+ * are made when Apple's Carbon Porting Guide says they are absolutely
+ * necessary. Other arbirary changes are mostly because of my hatred
+ * of deep nestings and indentations. The code for controlling graphics modes
+ * have been thoroughly revised simply because I didn't like it (^ ^;).
+ * A bonus of this is that graphics settings can be loaded from Preferences
+ * quite easily.
+ *
+ * I also took Ron Anderson's (minimising the use of local-global coordinate
+ * conversions). Some might say his QuickTime multimedia is the most
+ * significant achievement... Play your favourite CD instead, if you really
+ * miss that (^ ^;) I might consider incorporating it if it makes use of
+ * event notification.
+ *
+ * I replaced some old API calls with new (OS 8.x--) ones, especially
+ * when I felt Apple is strongly against their continued usage.
+ *
+ * Similarly, USE_SFL_CODE should be always active, so I removed ifdef's
+ * just to prevent accidents, as well as to make the code a bit cleaner.
+ *
+ * On the contrary, I deliberately left traditional resource interfaces.
+ * Whatever Apple might say, I abhor file name extentions. And keeping two
+ * different sets of resources for Classic and Carbon is just too much for
+ * a personal project XXX
+ *
+ * Because Carbon forbids the use of 68K code, ANGBAND_LITE_MAC sections
+ * are removed.
+ *
+ * Because the default font-size combination causes redraw artefact problem
+ * (some characters, even in monospace fonts, have negative left bearings),
+ * I introduced rather crude hack to clip all character drawings within
+ * their bounding rects. If you don't like this, please comment out the line
+ * #define CLIP_HACK
+ * below.
+ *
+ * The check for return values of AEProcessAppleEvent is removed,
+ * because it results in annoying dialogues on OS X, also because
+ * Apple says it isn't usually necessary.
+ *
+ * Because the always_pict code is *so* slow, I changed the graphics
+ * mode selection a bit to use higher_pict when a user chooses a fixed
+ * width font and doesn't changes tile width / height.
+ *
+ * Added support for David Gervais' 32x32 tiles.
+ *
+ * Replaced transparency effect code by Ron Anderson's.
+ *
+ * Added support for gcc & make compilation. They come free with OS X
+ * (on the developer CD). This means that it can be compiled as a bundle.
+ *
+ * For Mach-O Carbon binary, moved graphics tiles out of the
+ * resource fork and made them plain PNG files, to be stored in the application
+ * bundle's "Resources" subdirectory.
+ *
+ * For Mach-O Carbon binary, provided a compile-time option (USE_QT_SOUND) to
+ * move sound effect samples out of the resource fork and use *.wav files in
+ * the bundle's "Resources" subdirectory. The "*" part must match the names in
+ * angband_sound_name (variable.c) exactly. This doesn't hurt performance
+ * a lot in [V] (it's got ~25 sound events), but problematic in [Z]-based
+ * ones (they have somewhere around 70 sound events).
+ *
+ * You still can use the resource file that comes with the ext-mac archive
+ * on the Angband FTP server, with these additions:
+ * - MENUs 131--134 and 144--146, as described above
+ * - MBAR 128: just a array of 128 through 134
+ * - plst 0 : can be empty, although Apple recommends us to fill it in.
+ * - STR# 128 : something like "Please select your lib folder"
+ *
+ * Since this involves considerable amount of work, I attached
+ * a plain text resource definition (= Rez format) .
+ * I heavily commented on the file, hoping it could be easily adapted
+ * to future versions of Angband as well as variants.
+ * I omitted sound effects and graphic tiles to make it reasonably small.
+ * Please copy them from any recent Mac binaries - you can use, say ZAngband
+ * ones for Vanilla or [V]-based variants quite safely. T.o.M.E. uses fairly
+ * extended 16x16 tileset and it is maintained actively. IIRC Thangorodrim
+ * compile page has an intruction explaining how to convert tiles for
+ * use on the Mac... It can be tricky, depending on your choice of
+ * graphics utility. Remember setting resolution to 72 pixels per inch,
+ * while keeping vertical/horizontal scale factor to 100% and dump the
+ * result as a PICT from resource.
+ *
+ * To build Carbonised Angband with CodeWarrior, copy your PPC project
+ * and
+ * - replace main-mac.c in the project with this file (in the link order tab)
+ * - remove InterfaceLib and MathLib
+ * - add CarbonLib (found in Carbon SDK or CW's UniversalInterfaces) --
+ * if you have compiler/linker errors, you'll need Carbon SDK 1.1 or greater
+ * - replace MSL C.PPC.Lib with MSL C.Carbon.Lib (both found in
+ * MSL:MSL_C:MSL_MacOS:Lib:PPC)
+ * - leave MSL RuntimePPC.Lib as it is
+ * - don't forget to update resource file, as described above
+ * - as in Classic targets, you may have to include <unistd.h> and
+ * <fcntl.h>. The most convinient place for them is the first
+ * #ifdef MACINTOSH in h-system.h
+ * - check variant dependent ifdef's explained below, and add
+ * appropriate one(s) in your A-mac-h.pch.
+ */
+
+
+/*
+ * Force Carbon-compatible APIs
+ */
+#ifndef MACH_O_CARBON
+
+/* Can be CodeWarrior or MPW */
+# define TARGET_API_MAC_CARBON 1
+
+#else
+
+/*
+* Must be Mach-O Carbon target with OS X gcc.
+* No need to set TARGET_API_MAC_CARBON to 1 here, but I assume it should
+* be able to make efficient use of BSD functions, hence:
+*/
+# define USE_MALLOC
+/* Not yet */
+/* # define USE_NIB */
+
+#endif /* !MACH_O_CARBON */
+
+
+#include "angband.h"
+
+#if defined(MACINTOSH) || defined(MACH_O_CARBON)
+
+/*
+ * Check and create if needed the directory dirpath
+ */
+bool_ private_check_user_directory(cptr dirpath)
+{
+ /* Is this used anywhere else in *bands? */
+ struct stat stat_buf;
+
+ int ret;
+
+ /* See if it already exists */
+ ret = stat(dirpath, &stat_buf);
+
+ /* It does */
+ if (ret == 0)
+ {
+ /* Now we see if it's a directory */
+ if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) return (TRUE);
+
+ /*
+ * Something prevents us from create a directory with
+ * the same pathname
+ */
+ return (FALSE);
+ }
+
+ /* No - this maybe the first time. Try to create a directory */
+ else
+ {
+ /* Create the ~/.ToME directory */
+ ret = mkdir(dirpath, 0700);
+
+ /* An error occured */
+ if (ret == -1) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+ }
+}
+
+/*
+ * Check existence of ".ToME/" directory in the user's
+ * home directory or try to create it if it doesn't exist.
+ * Returns FALSE if all the attempts fail.
+ */
+static bool_ check_create_user_dir(void)
+{
+ char dirpath[1024];
+ char versionpath[1024];
+ char savepath[1024];
+#ifdef PRIVATE_USER_PATH_DATA
+ char datapath[1024];
+#endif
+#ifdef PRIVATE_USER_PATH_APEX
+ char apexpath[1024];
+#endif
+
+ /* Get an absolute path from the filename */
+ path_parse(dirpath, 1024, PRIVATE_USER_PATH);
+ strcpy(versionpath, dirpath);
+ strcat(versionpath, USER_PATH_VERSION);
+ strcpy(savepath, versionpath);
+ strcat(savepath, "/save");
+#ifdef PRIVATE_USER_PATH_DATA
+ strcpy(datapath, versionpath);
+ strcat(datapath, "/data");
+#endif
+#ifdef PRIVATE_USER_PATH_APEX
+ strcpy(apexpath, versionpath);
+ strcat(apexpath, "/apex");
+#endif
+
+ return /* don't forget, the dirpath muts come first */
+ private_check_user_directory(dirpath) &&
+ private_check_user_directory(versionpath) &&
+#ifdef PRIVATE_USER_PATH_DATA
+ private_check_user_directory(datapath) &&
+#endif
+#ifdef PRIVATE_USER_PATH_APEX
+ private_check_user_directory(apexpath) &&
+#endif
+ private_check_user_directory(savepath);
+}
+
+
+/*
+ * Variant-dependent features:
+ *
+ * #define ALLOW_BIG_SCREEN (V, Ey, O, T.o.M.E., and Z. Dr's big screen needs
+ * more work. New S one is too idiosyncratic...)
+ * #define ANG281_RESET_VISUALS (Cth, Gum, T.o.M.E., Z)
+ * #define SAVEFILE_SCREEN (T.o.M.E.)
+ * #define ZANG_AUTO_SAVE (O and Z)
+ * #define HAS_SCORE_MENU (V and T.o.M.E.)
+ * #define ANGBAND_CREATOR four letter code for your variant, if any.
+ * or use the default one.
+ *
+ * For [Z], you also have to say -- #define inkey_flag (p_ptr->inkey_flag)
+ * but before that, please, please consider using main-mac-carbon.c in [Z],
+ * that has some interesting features.
+ */
+
+/* Some porting examples */
+#ifdef ANGBAND30X
+# define USE_DOUBLE_TILES
+# define ALLOW_BIG_SCREEN
+# define HAS_SCORE_MENU
+# define NEW_ZVIRT_HOOKS
+#endif /* ANGBAND30X */
+
+# define USE_DOUBLE_TILES
+# define SAVEFILE_SCREEN
+# define ANG281_RESET_VISUALS
+# define ALLOW_BIG_SCREEN
+# define HAS_SCORE_MENU
+# define ANGBAND_CREATOR 'PrnA'
+
+/* Default creator signature */
+#ifndef ANGBAND_CREATOR
+# define ANGBAND_CREATOR 'A271'
+#endif
+
+
+/*
+ * Use rewritten asynchronous sound player
+ */
+#define USE_ASYNC_SOUND
+
+
+/*
+ * A rather crude fix to reduce amount of redraw artefacts.
+ * Some fixed width fonts (i.e. Monaco) has characters with negative
+ * left bearings, so Term_wipe_mac or overwriting cannot completely
+ * erase them. This could be introduced to Classic Mac OS ports too,
+ * but since I've never heard any complaints and I don't like to
+ * make 68K ports even slower, I won't do so there.
+ */
+#define CLIP_HACK /* */
+
+/*
+ * To cope with pref file related problems. It no longer has to be acculate,
+ * because preferences are stored in plist.
+ */
+#define PREF_VER_MAJOR VERSION_MAJOR
+#define PREF_VER_MINOR VERSION_MINOR
+#define PREF_VER_PATCH VERSION_PATCH
+#define PREF_VER_EXTRA VERSION_EXTRA
+
+
+/*
+ * In OS X + gcc, use <Carbon/Carbon.h>, <CoreServices/CoreServices.h> and
+ * <CoreFoundation/CoreFoundation.h> for ALL of these, including the Apple
+ * Event ones. <QuickTime/QuickTime.h> is used by the tile loading code.
+ */
+#ifdef MACH_O_CARBON
+
+#include <Carbon/Carbon.h>
+#include <QuickTime/QuickTime.h>
+#include <CoreServices/CoreServices.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#else /* MACH_O_CARBON */
+
+#include <Types.h>
+#include <Gestalt.h>
+#include <QuickDraw.h>
+#include <Files.h>
+#include <Fonts.h>
+#include <Menus.h>
+#include <Dialogs.h>
+#include <Windows.h>
+#include <Palettes.h>
+#include <ToolUtils.h>
+#include <Events.h>
+#include <SegLoad.h>
+#include <Resources.h>
+#include <Controls.h>
+#include <Memory.h>
+#include <QDOffscreen.h>
+#include <Sound.h>
+#include <Navigation.h>
+#include <CFPreferences.h>
+#include <CFNumber.h>
+#include <AppleEvents.h>
+#include <EPPC.h>
+#include <Folders.h>
+
+#endif /* MACH_O_CARBON */
+
+/* MacOSX == Unix == Good */
+#ifdef USE_MACOSX
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#endif
+
+
+/*
+ * Use "malloc()" instead of "NewPtr()"
+ */
+/* #define USE_MALLOC */
+
+
+/*
+ * Information about each of the 256 available colors
+ */
+static RGBColor color_info[256];
+
+
+#ifdef MACH_O_CARBON
+
+/*
+ * Creator signature and file type - Didn't I say that I abhor file name
+ * extentions? Names and metadata are entirely different set of notions.
+ */
+OSType _fcreator;
+OSType _ftype;
+
+#endif /* MACH_O_CARBON */
+
+
+/*
+ * Forward declare
+ */
+typedef struct term_data term_data;
+
+/*
+ * Extra "term" data
+ */
+struct term_data
+{
+ term *t;
+
+ Rect r;
+
+ WindowPtr w;
+
+
+ short padding;
+
+ short pixelDepth;
+
+ GWorldPtr theGWorld; /* not used ... */
+
+ GDHandle theGDH;
+
+ GDHandle mainSWGDH; /* not used ... */
+
+ Str15 title;
+
+ s16b oops;
+
+ s16b keys;
+
+ s16b last;
+
+ s16b mapped;
+
+ s16b rows;
+ s16b cols;
+
+ s16b font_id;
+ s16b font_size;
+ s16b font_face;
+ s16b font_mono;
+
+ s16b font_o_x;
+ s16b font_o_y;
+ s16b font_wid;
+ s16b font_hgt;
+
+ s16b tile_o_x;
+ s16b tile_o_y;
+ s16b tile_wid;
+ s16b tile_hgt;
+
+ s16b size_wid;
+ s16b size_hgt;
+
+ s16b size_ow1;
+ s16b size_oh1;
+ s16b size_ow2;
+ s16b size_oh2;
+};
+
+
+
+
+/*
+ * Forward declare -- see below
+ */
+static bool_ CheckEvents(bool_ wait);
+
+
+#ifndef MACH_O_CARBON
+
+/*
+ * Hack -- location of the main directory
+ */
+static short app_vol;
+static long app_dir;
+
+#endif /* !MACH_O_CARBON */
+
+
+/*
+ * Delay handling of double-clicked savefiles
+ */
+Boolean open_when_ready = FALSE;
+
+/*
+ * Delay handling of pre-emptive "quit" event
+ */
+Boolean quit_when_ready = FALSE;
+
+
+/*
+ * Aqua automatically supplies the Quit menu.
+ */
+static Boolean is_aqua = FALSE;
+
+/*
+ * Version of Mac OS - for version specific bug workarounds (; ;)
+ */
+static long mac_os_version;
+
+
+/*
+ * Hack -- game in progress
+ */
+static int game_in_progress = 0;
+
+
+/*
+ * Only do "SetPort()" when needed
+ */
+static WindowPtr active = NULL;
+
+
+/*
+ * Maximum number of terms
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * An array of term_data's
+ */
+static term_data data[MAX_TERM_DATA];
+
+
+/*
+ * Note when "open"/"new" become valid
+ */
+static bool_ initialized = FALSE;
+
+
+
+/*
+ * Convert a C string to a pascal string in place
+ *
+ * This function may be defined elsewhere, but since it is so
+ * small, it is not worth finding the proper function name for
+ * all the different platforms.
+ */
+static void ctopstr(StringPtr src)
+{
+ int i;
+ byte len;
+
+ /* Hack -- pointer */
+ char *s = (char*)(src);
+
+ len = strlen(s);
+
+ /* Hack -- convert the string */
+ for (i = len; i > 1; i--) s[i] = s[i - 1];
+
+ /* Hack -- terminate the string */
+ s[0] = len;
+}
+
+
+#ifdef MACH_O_CARBON
+
+/* Carbon File Manager utilities by pelpel */
+
+/*
+ * (Carbon)
+ * Convert a pathname to a corresponding FSSpec.
+ * Returns noErr on success.
+ */
+static OSErr path_to_spec(const char *path, FSSpec *spec)
+{
+ OSErr err;
+ FSRef ref;
+
+ /* Convert pathname to FSRef ... */
+ err = FSPathMakeRef(path, &ref, NULL);
+ if (err != noErr) return (err);
+
+ /* ... then FSRef to FSSpec */
+ err = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
+
+ /* Inform caller of success or failure */
+ return (err);
+}
+
+
+/*
+ * (Carbon)
+ * Convert a FSSpec to a corresponding pathname.
+ * Returns noErr on success.
+ */
+static OSErr spec_to_path(const FSSpec *spec, char *buf, size_t size)
+{
+ OSErr err;
+ FSRef ref;
+
+ /* Convert FSSpec to FSRef ... */
+ err = FSpMakeFSRef(spec, &ref);
+ if (err != noErr) return (err);
+
+ /* ... then FSRef to pathname */
+ err = FSRefMakePath(&ref, buf, size);
+
+ /* Inform caller of success or failure */
+ return (err);
+}
+
+
+/*
+ * (Carbon) [via path_to_spec]
+ * Set creator and filetype of a file specified by POSIX-style pathname.
+ * Returns 0 on success, -1 in case of errors.
+ */
+int fsetfileinfo(char *pathname, OSType fcreator, OSType ftype)
+{
+ OSErr err;
+ FSSpec spec;
+ FInfo info;
+
+ /* Convert pathname to FSSpec */
+ if (path_to_spec(pathname, &spec) != noErr) return ( -1);
+
+ /* Obtain current finder info of the file */
+ if (FSpGetFInfo(&spec, &info) != noErr) return ( -1);
+
+ /* Overwrite creator and type */
+ info.fdCreator = fcreator;
+ info.fdType = ftype;
+ err = FSpSetFInfo(&spec, &info);
+
+ /* Inform caller of success or failure */
+ return ((err == noErr) ? 0 : -1);
+}
+
+
+#else /* MACH_O_CARBON */
+
+/*
+* Convert refnum+vrefnum+fname into a full file name
+* Store this filename in 'buf' (make sure it is long enough)
+* Note that 'fname' looks to be a "pascal" string
+*/
+static void refnum_to_name(char *buf, long refnum, short vrefnum, char *fname)
+{
+ DirInfo pb;
+ Str255 name;
+ int err;
+ int i, j;
+
+ char res[1000];
+
+ i = 999;
+
+ res[i] = 0;
+ i--;
+ for (j = 1; j <= fname[0]; j++)
+ {
+ res[i - fname[0] + j] = fname[j];
+ }
+ i -= fname[0];
+
+ pb.ioCompletion = NULL;
+ pb.ioNamePtr = name;
+ pb.ioVRefNum = vrefnum;
+ pb.ioDrParID = refnum;
+ pb.ioFDirIndex = -1;
+
+ while (1)
+ {
+ pb.ioDrDirID = pb.ioDrParID;
+ err = PBGetCatInfoSync((CInfoPBPtr) & pb);
+ res[i] = ':';
+ i--;
+ for (j = 1; j <= name[0]; j++)
+ {
+ res[i - name[0] + j] = name[j];
+ }
+ i -= name[0];
+
+ if (pb.ioDrDirID == fsRtDirID) break;
+ }
+
+ /* Extract the result */
+ for (j = 0, i++; res[i]; j++, i++) buf[j] = res[i];
+ buf[j] = 0;
+}
+
+
+/*
+* Convert a pascal string in place
+*
+* This function may be defined elsewhere, but since it is so
+* small, it is not worth finding the proper function name for
+* all the different platforms.
+*/
+static void ptocstr(StringPtr src)
+{
+ int i;
+
+ /* Hack -- pointer */
+ char *s = (char*)(src);
+
+ /* Hack -- convert the string */
+ for (i = s[0]; i; i--, s++) s[0] = s[1];
+
+ /* Hack -- terminate the string */
+ s[0] = '\0';
+}
+
+
+/*
+* Utility routines by Steve Linberg
+*
+* The following three routines (pstrcat, pstrinsert, and PathNameFromDirID)
+* were taken from the Think Reference section called "Getting a Full Pathname"
+* (under the File Manager section). We need PathNameFromDirID to get the
+* full pathname of the opened savefile, making no assumptions about where it
+* is.
+*
+* I had to hack PathNameFromDirID a little for MetroWerks, but it's awfully
+* nice.
+*/
+static void pstrcat(StringPtr dst, StringPtr src)
+{
+ /* copy string in */
+ BlockMove(src + 1, dst + *dst + 1, *src);
+
+ /* adjust length byte */
+ *dst += *src;
+}
+
+
+/*
+* pstrinsert - insert string 'src' at beginning of string 'dst'
+*/
+static void pstrinsert(StringPtr dst, StringPtr src)
+{
+ /* make room for new string */
+ BlockMove(dst + 1, dst + *src + 1, *dst);
+
+ /* copy new string in */
+ BlockMove(src + 1, dst + 1, *src);
+
+ /* adjust length byte */
+ *dst += *src;
+}
+
+
+static void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName)
+{
+ CInfoPBRec block;
+ Str255 directoryName;
+ OSErr err;
+
+ fullPathName[0] = '\0';
+
+ block.dirInfo.ioDrParID = dirID;
+ block.dirInfo.ioNamePtr = directoryName;
+
+ while (1)
+ {
+ block.dirInfo.ioVRefNum = vRefNum;
+ block.dirInfo.ioFDirIndex = -1;
+ block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID;
+ err = PBGetCatInfoSync(&block);
+ pstrcat(directoryName, (StringPtr)"\p:");
+ pstrinsert(fullPathName, directoryName);
+ if (block.dirInfo.ioDrDirID == 2) break;
+ }
+}
+
+#endif /* MACH_O_CARBON */
+
+
+
+
+/*
+ * Center a rectangle inside another rectangle
+ *
+ * Consider using RepositionWindow() whenever possible
+ */
+static void center_rect(Rect *r, Rect *s)
+{
+ int centerx = (s->left + s->right) / 2;
+ int centery = (2 * s->top + s->bottom) / 3;
+ int dx = centerx - (r->right - r->left) / 2 - r->left;
+ int dy = centery - (r->bottom - r->top) / 2 - r->top;
+ r->left += dx;
+ r->right += dx;
+ r->top += dy;
+ r->bottom += dy;
+}
+
+
+/*
+ * Activate a given window, if necessary
+ */
+static void activate(WindowPtr w)
+{
+ /* Activate */
+ if (active != w)
+ {
+ /* Activate */
+ if (w) SetPort(GetWindowPort(w));
+
+ /* Remember */
+ active = w;
+ }
+}
+
+
+/*
+ * Display a warning message
+ */
+static void mac_warning(cptr warning)
+{
+ Str255 text;
+ int len, i;
+
+ /* Limit of 250 chars */
+ len = strlen(warning);
+ if (len > 250) len = 250;
+
+ /* Make a "Pascal" string */
+ text[0] = len;
+ for (i = 0; i < len; i++) text[i + 1] = warning[i];
+
+ /* Prepare the dialog box values */
+ ParamText(text, "\p", "\p", "\p");
+
+ /* Display the Alert, wait for Okay */
+ Alert(129, 0L);
+}
+
+
+
+/*** Some generic functions ***/
+
+/*
+ * Hack -- activate a color (0 to 255)
+ */
+static void term_data_color(term_data *td, int a)
+{
+ /* Activate the color */
+ if (td->last != a)
+ {
+ /* Activate the color */
+ RGBForeColor(&color_info[a]);
+
+ /* Memorize color */
+ td->last = a;
+ }
+}
+
+
+/*
+ * Hack -- Apply and Verify the "font" info
+ *
+ * This should usually be followed by "term_data_check_size()"
+ *
+ * XXX XXX To force (re)initialisation of td->tile_wid and td->tile_hgt
+ * you have to reset them to zero before this function is called.
+ * XXX XXX This is automatic when the program starts because the term_data
+ * array is WIPE'd by term_data_hack, but isn't in the other cases, i.e.
+ * font, font style and size changes.
+ */
+static void term_data_check_font(term_data *td)
+{
+ int i;
+
+ FontInfo info;
+
+ WindowPtr old = active;
+
+
+ /* Activate */
+ activate(td->w);
+
+ /* Instantiate font */
+ TextFont(td->font_id);
+ TextSize(td->font_size);
+ TextFace(td->font_face);
+
+ /* Extract the font info */
+ GetFontInfo(&info);
+
+ /* Assume monospaced */
+ td->font_mono = TRUE;
+
+ /* Extract the font sizing values XXX XXX XXX */
+ td->font_wid = CharWidth('@'); /* info.widMax; */
+ td->font_hgt = info.ascent + info.descent;
+ td->font_o_x = 0;
+ td->font_o_y = info.ascent;
+
+ /* Check important characters */
+ for (i = 33; i < 127; i++)
+ {
+ /* Hack -- notice non-mono-space */
+ if (td->font_wid != CharWidth(i)) td->font_mono = FALSE;
+
+ /* Hack -- collect largest width */
+ if (td->font_wid < CharWidth(i)) td->font_wid = CharWidth(i);
+ }
+
+ /* Set default offsets */
+ td->tile_o_x = td->font_o_x;
+ td->tile_o_y = td->font_o_y;
+
+ /* Set default tile size */
+ if (td->tile_wid == 0) td->tile_wid = td->font_wid;
+ if (td->tile_hgt == 0) td->tile_hgt = td->font_hgt;
+
+ /* Re-activate the old window */
+ activate(old);
+}
+
+
+/*
+ * Hack -- Apply and Verify the "size" info
+ */
+static void term_data_check_size(term_data *td)
+{
+ if (td == &data[0])
+ {
+#ifndef ALLOW_BIG_SCREEN
+
+ /* Forbid resizing of the Angband window */
+ td->cols = 80;
+ td->rows = 24;
+
+#else
+
+ /* Enforce minimal size */
+ if (td->cols < 80) td->cols = 80;
+ if (td->rows < 24) td->rows = 24;
+
+#endif /* !ALLOW_BIG_SCREEN */
+ }
+
+ /* Information windows can be much smaller */
+ else
+ {
+ if (td->cols < 1) td->cols = 1;
+ if (td->rows < 1) td->rows = 1;
+ }
+
+ /* Enforce maximal sizes */
+ if (td->cols > 255) td->cols = 255;
+ if (td->rows > 255) td->rows = 255;
+
+ /* Minimal tile size */
+ if (td->tile_wid < td->font_wid) td->tile_wid = td->font_wid;
+ if (td->tile_hgt < td->font_hgt) td->tile_hgt = td->font_hgt;
+
+ /* Default tile offsets */
+ td->tile_o_x = (td->tile_wid - td->font_wid) / 2;
+ td->tile_o_y = (td->tile_hgt - td->font_hgt) / 2;
+
+ /* Minimal tile offsets */
+ if (td->tile_o_x < 0) td->tile_o_x = 0;
+ if (td->tile_o_y < 0) td->tile_o_y = 0;
+
+ /* Apply font offsets */
+ td->tile_o_x += td->font_o_x;
+ td->tile_o_y += td->font_o_y;
+
+ /* Calculate full window size */
+ td->size_wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2;
+ td->size_hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ {
+ BitMap tScreen;
+
+ /* Get current screen */
+ (void)GetQDGlobalsScreenBits(&tScreen);
+
+ /* Verify the top */
+ if (td->r.top > tScreen.bounds.bottom - td->size_hgt)
+ {
+ td->r.top = tScreen.bounds.bottom - td->size_hgt;
+ }
+
+ /* Verify the top */
+ if (td->r.top < tScreen.bounds.top + GetMBarHeight())
+ {
+ td->r.top = tScreen.bounds.top + GetMBarHeight();
+ }
+
+ /* Verify the left */
+ if (td->r.left > tScreen.bounds.right - td->size_wid)
+ {
+ td->r.left = tScreen.bounds.right - td->size_wid;
+ }
+
+ /* Verify the left */
+ if (td->r.left < tScreen.bounds.left)
+ {
+ td->r.left = tScreen.bounds.left;
+ }
+ }
+
+ /* Calculate bottom right corner */
+ td->r.right = td->r.left + td->size_wid;
+ td->r.bottom = td->r.top + td->size_hgt;
+
+ /* Assume no graphics */
+ td->t->higher_pict = FALSE;
+ td->t->always_pict = FALSE;
+
+
+ /* Handle graphics */
+ if (use_graphics)
+ {
+ /* Use higher pict whenever possible */
+ if (td->font_mono) td->t->higher_pict = TRUE;
+
+ /* Use always_pict only when necessary */
+ else td->t->always_pict = TRUE;
+ }
+
+ /* Fake mono-space */
+ if (!td->font_mono ||
+ (td->font_wid != td->tile_wid) ||
+ (td->font_hgt != td->tile_hgt))
+ {
+ /*
+ * Handle fake monospace
+ *
+ * pelpel: This is SLOW. Couldn't we use CharExtra
+ * and SpaceExtra for monospaced fonts?
+ */
+ if (td->t->higher_pict) td->t->higher_pict = FALSE;
+ td->t->always_pict = TRUE;
+ }
+}
+
+
+/*
+ * Hack -- resize a term_data
+ *
+ * This should normally be followed by "term_data_redraw()"
+ */
+static void term_data_resize(term_data *td)
+{
+ /*
+ * Actually resize the window
+ *
+ * ResizeWindow is the preferred API call, but it cannot
+ * be used here.
+ */
+ SizeWindow(td->w, td->size_wid, td->size_hgt, 0);
+}
+
+
+
+/*
+ * Hack -- redraw a term_data
+ *
+ * Note that "Term_redraw()" calls "TERM_XTRA_CLEAR"
+ */
+static void term_data_redraw(term_data *td)
+{
+ term *old = Term;
+ Rect tRect;
+
+ /* Activate the term */
+ Term_activate(td->t);
+
+ /* Redraw the contents */
+ Term_redraw();
+
+ /* Flush the output */
+ Term_fresh();
+
+ /* Restore the old term */
+ Term_activate(old);
+
+ /* No need to redraw */
+ ValidWindowRect(td->w, GetPortBounds(GetWindowPort(td->w), &tRect));
+}
+
+
+/*
+ * Graphics support
+ */
+
+/* Set by Term_xtra_mac_react */
+#ifdef MACH_O_CARBON
+static CFStringRef pict_id; /* PICT id of image tiles */
+#else
+static int pict_id; /* PICT id of image tiles */
+#endif /* MACH_O_CARBON */
+
+static int graf_width; /* Width of a tile in pixels */
+static int graf_height; /* Height of a tile in pixels */
+
+/* Calculated by PICT loading code */
+static int pict_cols; /* Number of columns in tiles */
+static int pict_rows; /* Number of rows in tiles */
+
+/* Available graphics modes */
+#define GRAF_MODE_NONE 0 /* plain ASCII */
+#define GRAF_MODE_8X8 1 /* 8x8 tiles */
+#define GRAF_MODE_16X16 2 /* 16x16 tiles */
+#define GRAF_MODE_32X32 3 /* 32x32 tiles */
+
+static int graf_mode = GRAF_MODE_NONE; /* current graphics mode */
+static int graf_mode_req = GRAF_MODE_NONE; /* requested graphics mode */
+
+#define TR_NONE 0 /* No transparency */
+#define TR_OVER 1 /* Overwriting with transparent black pixels */
+static int transparency_mode = TR_NONE; /* types of transparency effect */
+
+
+/*
+ * Forward Declare
+ */
+typedef struct FrameRec FrameRec;
+
+/*
+ * Frame
+ *
+ * - GWorld for the frame image
+ * - Handle to pix map (saved for unlocking/locking)
+ * - Pointer to color pix map (valid only while locked)
+ */
+struct FrameRec
+{
+ GWorldPtr framePort;
+ PixMapHandle framePixHndl;
+ PixMapPtr framePix;
+};
+
+
+/*
+ * The global picture data
+ */
+static FrameRec *frameP = NULL;
+
+
+/*
+ * Lock a frame
+ */
+static void BenSWLockFrame(FrameRec *srcFrameP)
+{
+ PixMapHandle pixMapH;
+
+ pixMapH = GetGWorldPixMap(srcFrameP->framePort);
+ (void)LockPixels(pixMapH);
+ HLockHi((Handle)pixMapH);
+ srcFrameP->framePixHndl = pixMapH;
+ srcFrameP->framePix = (PixMapPtr)(*(Handle)pixMapH);
+}
+
+
+/*
+ * Unlock a frame
+ */
+static void BenSWUnlockFrame(FrameRec *srcFrameP)
+{
+ if (srcFrameP->framePort != NULL)
+ {
+ HUnlock((Handle)srcFrameP->framePixHndl);
+ UnlockPixels(srcFrameP->framePixHndl);
+ }
+
+ srcFrameP->framePix = NULL;
+}
+
+
+
+#ifdef MACH_O_CARBON
+
+/* Moving graphics resources into data fork -- pelpel */
+
+/*
+ * (Carbon, Bundle)
+ * Given base and type names of a resource, find a file in the
+ * current application bundle and return its FSSpec in the third argument.
+ * Returns true on success, false otherwise.
+ * e.g. get_resource_spec(CFSTR("8x8"), CFSTR("png"), &spec);
+ */
+static Boolean get_resource_spec(
+ CFStringRef base_name, CFStringRef type_name, FSSpec *spec)
+{
+ CFURLRef res_url;
+ FSRef ref;
+
+ /* Find the tile resource specified in the current bundle */
+ res_url = CFBundleCopyResourceURL(
+ CFBundleGetMainBundle(), base_name, type_name, NULL);
+
+ /* Oops */
+ if (res_url == NULL) return (false);
+
+ /* Convert CFURL to FSRef */
+ (void)CFURLGetFSRef(res_url, &ref);
+
+ /* Convert FSRef to FSSpec */
+ (void)FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
+
+ /* Free allocated CF data */
+ CFRelease(res_url);
+
+ /* Success */
+ return (true);
+}
+
+
+/*
+ * (QuickTime)
+ * Create a off-screen GWorld from contents of a file specified by a FSSpec.
+ *
+ * Globals referenced: data[0], graf_height, graf_width
+ * Globals updated: pict_rows, pict_cols.
+ */
+static OSErr create_gworld_from_spec(
+ GWorldPtr *tile_gw, FSSpec *tile_spec)
+{
+ OSErr err;
+ GraphicsImportComponent gi;
+ GWorldPtr gw, tmp_gw;
+ GDHandle gdh, tmp_gdh;
+ Rect r;
+ SInt16 depth;
+
+ /* See if QuickTime understands the file format */
+ err = GetGraphicsImporterForFile(tile_spec, &gi);
+
+ /* Oops */
+ if (err != noErr) return (err);
+
+ /* Get depth */
+ depth = data[0].pixelDepth;
+
+ /* Get GDH */
+ gdh = data[0].theGDH;
+
+ /* Retrieve the rect of the image */
+ err = GraphicsImportGetNaturalBounds(gi, &r);
+
+ /* Adjust it, so that the upper left corner becomes (0, 0) */
+ OffsetRect(&r, -r.left, -r.top);
+
+ /* Calculate and set numbers of rows and columns */
+ pict_rows = r.bottom / graf_height;
+ pict_cols = r.right / graf_width;
+
+ /* Create a GWorld */
+ err = NewGWorld(&gw, depth, &r, NULL, gdh, noNewDevice);
+
+ /* Oops */
+ if (err != noErr) return (err);
+
+ /* Save the pointer to the GWorld */
+ *tile_gw = gw;
+
+ /* Save the current GWorld */
+ GetGWorld(&tmp_gw, &tmp_gdh);
+
+ /* Activate the newly created GWorld */
+ (void)GraphicsImportSetGWorld(gi, gw, NULL);
+
+ /* Prevent pixmap from moving while drawing */
+ (void)LockPixels(GetGWorldPixMap(gw));
+
+ /* Clear the pixels */
+ EraseRect(&r);
+
+ /* Draw the image into it */
+ (void)GraphicsImportDraw(gi);
+
+ /* Release the lock*/
+ UnlockPixels(GetGWorldPixMap(gw));
+
+ /* Restore GWorld */
+ SetGWorld(tmp_gw, tmp_gdh);
+
+ /* Close the image importer */
+ CloseComponent(gi);
+
+ /* Success */
+ return (noErr);
+}
+
+#else /* MACH_O_CARBON */
+
+static OSErr BenSWCreateGWorldFromPict(
+ GWorldPtr *pictGWorld, PicHandle pictH)
+{
+ OSErr err;
+ GWorldPtr saveGWorld;
+ GDHandle saveGDevice;
+ GWorldPtr tempGWorld;
+ Rect pictRect;
+ short depth;
+ GDHandle theGDH;
+
+ tempGWorld = NULL;
+
+ /* Reset */
+ *pictGWorld = NULL;
+
+ /* Get depth */
+ depth = data[0].pixelDepth;
+
+ /* Get GDH */
+ theGDH = data[0].theGDH;
+
+ /* Obtain size rectangle */
+ pictRect = (**pictH).picFrame;
+ OffsetRect(&pictRect, -pictRect.left, -pictRect.top);
+
+ /* Calculate and set numbers of rows and columns */
+ pict_rows = pictRect.bottom / graf_height;
+ pict_cols = pictRect.right / graf_width;
+
+ /* Create a GWorld */
+ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice);
+
+ /* Oops */
+ if (err != noErr) return (err);
+
+ /* Save pointer */
+ *pictGWorld = tempGWorld;
+
+ /* Save GWorld */
+ GetGWorld(&saveGWorld, &saveGDevice);
+
+ /* Activate */
+ SetGWorld(tempGWorld, nil);
+
+ /* Dump the pict into the GWorld */
+ (void)LockPixels(GetGWorldPixMap(tempGWorld));
+ EraseRect(&pictRect);
+ DrawPicture(pictH, &pictRect);
+ UnlockPixels(GetGWorldPixMap(tempGWorld));
+
+ /* Restore GWorld */
+ SetGWorld(saveGWorld, saveGDevice);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* MACH_O_CARBON */
+
+
+/*
+ * Init the global "frameP"
+ */
+static errr globe_init(void)
+{
+ OSErr err;
+
+ GWorldPtr tempPictGWorldP;
+
+#ifdef MACH_O_CARBON
+ FSSpec pict_spec;
+#else
+ PicHandle newPictH;
+#endif /* MACH_O_CARBON */
+
+
+ /* Use window XXX XXX XXX */
+ SetPort(GetWindowPort(data[0].w));
+
+
+#ifdef MACH_O_CARBON
+
+ /* Get the tile resources */
+ if (!get_resource_spec(pict_id, CFSTR("png"), &pict_spec)) return ( -1);
+
+ /* Create GWorld */
+ err = create_gworld_from_spec(&tempPictGWorldP, &pict_spec);
+
+#else /* MACH_O_CARBON */
+
+ /* Get the pict resource */
+ if ((newPictH = GetPicture(pict_id)) == 0) return ( -1);
+
+ /* Create GWorld */
+ err = BenSWCreateGWorldFromPict(&tempPictGWorldP, newPictH);
+
+ /* Release resource */
+ ReleaseResource((Handle)newPictH);
+
+#endif /* MACH_O_CARBON */
+
+ /* Error */
+ if (err != noErr) return (err);
+
+ /* Create the frame */
+ frameP = (FrameRec*)NewPtrClear((Size)sizeof(FrameRec));
+
+ /* Analyze result */
+ if (frameP == NULL) return ( -1);
+
+ /* Save GWorld */
+ frameP->framePort = tempPictGWorldP;
+
+ /* Lock it */
+ BenSWLockFrame(frameP);
+
+ /* Success */
+ return (noErr);
+}
+
+
+/*
+ * Nuke the global "frameP"
+ */
+static errr globe_nuke(void)
+{
+ /* Dispose */
+ if (frameP)
+ {
+ /* Unlock */
+ BenSWUnlockFrame(frameP);
+
+ /* Dispose of the GWorld */
+ DisposeGWorld(frameP->framePort);
+
+ /* Dispose of the memory */
+ DisposePtr((Ptr)frameP);
+
+ /* Forget */
+ frameP = NULL;
+ }
+
+ /* Flush events */
+ FlushEvents(everyEvent, 0);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_ASYNC_SOUND
+
+/*
+ * Asynchronous sound player - completely revised (beta)
+ */
+#if defined(USE_QT_SOUND) && !defined(MACH_O_CARBON)
+# undef USE_QT_SOUND
+#endif /* USE_QT_SOUND && !MACH_O_CARBON */
+
+/*
+ * How many sound channels will be pooled
+ *
+ * Was: 20, but I don't think we need 20 sound effects playing
+ * simultaneously :) -- pelpel
+ */
+#define MAX_CHANNELS 8
+
+/*
+ * A pool of sound channels
+ */
+static SndChannelPtr channels[MAX_CHANNELS];
+
+/*
+ * Status of the channel pool
+ */
+static Boolean channel_initialised = FALSE;
+
+/*
+ * Data handles containing sound samples
+ */
+static SndListHandle samples[SOUND_MAX];
+
+/*
+ * Reference counts of sound samples
+ */
+static SInt16 sample_refs[SOUND_MAX];
+
+#define SOUND_VOLUME_MIN 0 /* Default minimum sound volume */
+#define SOUND_VOLUME_MAX 255 /* Default maximum sound volume */
+#define VOLUME_MIN 0 /* Minimum sound volume in % */
+#define VOLUME_MAX 100 /* Maximum sound volume in % */
+#define VOLUME_INC 5 /* Increment sound volume in % */
+
+/*
+ * I'm just too lazy to write a panel for this XXX XXX
+ */
+static int sound_volume = SOUND_VOLUME_MAX;
+
+
+#ifdef USE_QT_SOUND
+
+/*
+ * QuickTime sound, by Ron Anderson
+ *
+ * I didn't choose to use Windows-style .ini files (Ron wrote a parser
+ * for it, but...), nor did I use lib/xtra directory, hoping someone
+ * would code plist-based configuration code in the future -- pelpel
+ */
+
+/*
+ * (QuickTime)
+ * Load sound effects from data-fork resources. They are wav files
+ * with the same names as angband_sound_name[] (variable.c)
+ *
+ * Globals referenced: angband_sound_name[]
+ * Globals updated: samples[] (they can be *huge*)
+ */
+static void load_sounds(void)
+{
+ OSErr err;
+ int i;
+
+ /* Start QuickTime */
+ err = EnterMovies();
+
+ /* Error */
+ if (err != noErr) return;
+
+ /*
+ * This loop may take a while depending on the count and size of samples
+ * to load.
+ *
+ * We should use a progress dialog for this.
+ */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ /* Apple APIs always give me headacke :( */
+ CFStringRef name;
+ FSSpec spec;
+ SInt16 file_id;
+ SInt16 res_id;
+ Str255 movie_name;
+ Movie movie;
+ Track track;
+ Handle h;
+ Boolean res;
+
+ /* Allocate CFString with the name of sound event to be processed */
+ name = CFStringCreateWithCString(NULL, angband_sound_name[i],
+ kTextEncodingUS_ASCII);
+
+ /* Error */
+ if (name == NULL) continue;
+
+ /* Find sound sample resource with the same name */
+ res = get_resource_spec(name, CFSTR("wav"), &spec);
+
+ /* Free the reference to CFString */
+ CFRelease(name);
+
+ /* Error */
+ if (!res) continue;
+
+ /* Open the sound file */
+ err = OpenMovieFile(&spec, &file_id, fsRdPerm);
+
+ /* Error */
+ if (err != noErr) continue;
+
+ /* Create Movie from the file */
+ err = NewMovieFromFile(&movie, file_id, &res_id, movie_name,
+ newMovieActive, NULL);
+
+ /* Error */
+ if (err != noErr) goto close_file;
+
+ /* Get the first track of the movie */
+ track = GetMovieIndTrackType(movie, 1, AudioMediaCharacteristic,
+ movieTrackCharacteristic | movieTrackEnabledOnly );
+
+ /* Error */
+ if (track == NULL) goto close_movie;
+
+ /* Allocate a handle to store sample */
+ h = NewHandle(0);
+
+ /* Error */
+ if (h == NULL) goto close_track;
+
+ /* Dump the sample into the handle */
+ err = PutMovieIntoTypedHandle(movie, track, soundListRsrc, h, 0,
+ GetTrackDuration(track), 0L, NULL);
+
+ /* Success */
+ if (err == noErr)
+ {
+ /* Store the handle in the sample list */
+ samples[i] = (SndListHandle)h;
+ }
+
+ /* Failure */
+ else
+ {
+ /* Free unused handle */
+ DisposeHandle(h);
+ }
+
+ /* Free the track */
+close_track:
+ DisposeMovieTrack(track);
+
+ /* Free the movie */
+close_movie:
+ DisposeMovie(movie);
+
+ /* Close the movie file */
+close_file:
+ CloseMovieFile(file_id);
+ }
+
+ /* Stop QuickTime */
+ ExitMovies();
+}
+
+#else /* USE_QT_SOUND */
+
+/*
+* Return a handle of 'snd ' resource given Angband sound event number,
+* or NULL if it isn't found.
+*
+* Globals referenced: angband_sound_name[] (variable.c)
+*/
+static SndListHandle find_sound(int num)
+{
+Str255 sound;
+
+/* Get the proper sound name */
+strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]);
+sound[0] = strlen((char*)sound + 1);
+
+/* Obtain resource XXX XXX XXX */
+return ((SndListHandle)GetNamedResource('snd ', sound));
+}
+
+#endif /* USE_QT_SOUND */
+
+
+/*
+ * Clean up sound support - to be called when the game exits.
+ *
+ * Globals referenced: channels[], samples[], sample_refs[].
+ */
+static void cleanup_sound(void)
+{
+ int i;
+
+ /* No need to clean it up */
+ if (!channel_initialised) return;
+
+ /* Dispose channels */
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ /* Drain sound commands and free the channel */
+ SndDisposeChannel(channels[i], TRUE);
+ }
+
+ /* Free sound data */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ /* Still locked */
+ if ((sample_refs[i] > 0) && (samples[i] != NULL))
+ {
+ /* Unlock it */
+ HUnlock((Handle)samples[i]);
+ }
+
+#ifndef USE_QT_SOUND
+
+ /* Release it */
+ if (samples[i]) ReleaseResource((Handle)samples[i]);
+
+#else
+/* Free handle */
+ if (samples[i]) DisposeHandle((Handle)samples[i]);
+
+#endif /* !USE_QT_SOUND */
+ }
+}
+
+
+/*
+ * Play sound effects asynchronously -- pelpel
+ *
+ * I don't believe those who first started using the previous implementations
+ * imagined this is *much* more complicated as it may seem. Anyway,
+ * introduced round-robin scheduling of channels and made it much more
+ * paranoid about HLock/HUnlock.
+ *
+ * XXX XXX de-refcounting, HUnlock and ReleaseResource should be done
+ * using channel's callback procedures, which set global flags, and
+ * a procedure hooked into CheckEvents does housekeeping. On the other
+ * hand, this lazy reclaiming strategy keeps things simple (no interrupt
+ * time code) and provides a sort of cache for sound data.
+ *
+ * Globals referenced: channel_initialised, channels[], samples[],
+ * sample_refs[].
+ * Globals updated: channel_initialised, channels[], sample_refs[].
+ * Only in !USE_QT_SOUND, samples[].
+ */
+static void play_sound(int num, int vol)
+{
+ OSErr err;
+ int i;
+ int prev_num;
+ SndListHandle h;
+ SndChannelPtr chan;
+ SCStatus status;
+
+ static int next_chan;
+ static SInt16 channel_occupants[MAX_CHANNELS];
+ static SndCommand volume_cmd, quiet_cmd;
+
+
+ /* Initialise sound channels */
+ if (!channel_initialised)
+ {
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ /* Paranoia - Clear occupant table */
+ /* channel_occupants[i] = 0; */
+
+ /* Create sound channel for all sounds to play from */
+ err = SndNewChannel(&channels[i], sampledSynth, initMono, NULL);
+
+ /* Error */
+ if (err != noErr)
+ {
+ /* Free channels */
+ while (--i >= 0)
+ {
+ SndDisposeChannel(channels[i], TRUE);
+ }
+
+ /* Notify error */
+ plog("Cannot initialise sound channels!");
+
+ /* Cancel request */
+ use_sound = arg_sound = FALSE;
+
+ /* Failure */
+ return;
+ }
+ }
+
+ /* First channel to use */
+ next_chan = 0;
+
+ /* Prepare volume command */
+ volume_cmd.cmd = volumeCmd;
+ volume_cmd.param1 = 0;
+ volume_cmd.param2 = 0;
+
+ /* Prepare quiet command */
+ quiet_cmd.cmd = quietCmd;
+ quiet_cmd.param1 = 0;
+ quiet_cmd.param2 = 0;
+
+ /* Initialisation complete */
+ channel_initialised = TRUE;
+ }
+
+ /* Paranoia */
+ if ((num <= 0) || (num >= SOUND_MAX)) return;
+
+ /* Prepare volume command */
+ volume_cmd.param2 = (SInt16)((vol << 4) | vol);
+
+ /* Channel to use (round robin) */
+ chan = channels[next_chan];
+
+ /* See if the resource is already in use */
+ if (sample_refs[num] > 0)
+ {
+ /* Resource in use */
+ h = samples[num];
+
+ /* Increase the refcount */
+ sample_refs[num]++;
+ }
+
+ /* Sound is not currently in use */
+ else
+ {
+ /* Get handle for the sound */
+#ifdef USE_QT_SOUND
+ h = samples[num];
+#else
+ h = find_sound(num);
+#endif /* USE_QT_SOUND */
+
+ /* Sample not available */
+ if (h == NULL) return;
+
+#ifndef USE_QT_SOUND
+
+ /* Load resource */
+ LoadResource((Handle)h);
+
+ /* Remember it */
+ samples[num] = h;
+
+#endif /* !USE_QT_SOUND */
+
+ /* Lock the handle */
+ HLock((Handle)h);
+
+ /* Initialise refcount */
+ sample_refs[num] = 1;
+ }
+
+ /* Poll the channel */
+ err = SndChannelStatus(chan, sizeof(SCStatus), &status);
+
+ /* It isn't available */
+ if ((err != noErr) || status.scChannelBusy)
+ {
+ /* Shut it down */
+ SndDoImmediate(chan, &quiet_cmd);
+ }
+
+ /* Previously played sound on this channel */
+ prev_num = channel_occupants[next_chan];
+
+ /* Process previously played sound */
+ if (prev_num != 0)
+ {
+ /* Decrease refcount */
+ sample_refs[prev_num]--;
+
+ /* We can free it now */
+ if (sample_refs[prev_num] <= 0)
+ {
+ /* Unlock */
+ HUnlock((Handle)samples[prev_num]);
+
+#ifndef USE_QT_SOUND
+
+ /* Release */
+ ReleaseResource((Handle)samples[prev_num]);
+
+ /* Forget handle */
+ samples[prev_num] = NULL;
+
+#endif /* !USE_QT_SOUND */
+
+ /* Paranoia */
+ sample_refs[prev_num] = 0;
+ }
+ }
+
+ /* Remember this sound as the current occupant of the channel */
+ channel_occupants[next_chan] = num;
+
+ /* Set up volume for channel */
+ SndDoImmediate(chan, &volume_cmd);
+
+ /* Play new sound asynchronously */
+ SndPlay(chan, h, TRUE);
+
+ /* Schedule next channel (round robin) */
+ next_chan++;
+ if (next_chan >= MAX_CHANNELS) next_chan = 0;
+}
+
+#endif /* USE_ASYNC_SOUND */
+
+
+
+
+/*** Support for the "z-term.c" package ***/
+
+
+/*
+ * Initialize a new Term
+ *
+ * Note also the "window type" called "noGrowDocProc", which might be more
+ * appropriate for the main "screen" window.
+ *
+ * Note the use of "srcCopy" mode for optimized screen writes.
+ */
+static void Term_init_mac(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+ WindowAttributes wattrs;
+ OSStatus err;
+
+ static RGBColor black = {0x0000, 0x0000, 0x0000};
+ static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};
+
+#ifndef ALLOW_BIG_SCREEN
+
+ /* Every window has close and collapse boxes */
+ wattrs = kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute;
+
+ /* Information windows are resizable */
+ if (td != &data[0]) wattrs |= kWindowResizableAttribute;
+
+#else
+
+ /* Big screen - every window has close, collapse and resize boxes */
+ wattrs = kWindowCloseBoxAttribute |
+ kWindowCollapseBoxAttribute |
+ kWindowResizableAttribute;
+
+#endif /* !ALLOW_BIG_SCREEN */
+
+ /* Make the window */
+ err = CreateNewWindow(
+ kDocumentWindowClass,
+ wattrs,
+ &td->r,
+ &td->w);
+
+ /*
+ * XXX XXX Although the original main-mac.c doesn't perform error
+ * checking, it should be done here.
+ */
+
+ /* Set window title */
+ SetWTitle(td->w, td->title);
+
+ /* Activate the window */
+ activate(td->w);
+
+ /* Erase behind words */
+ TextMode(srcCopy);
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize the window */
+ term_data_resize(td);
+
+
+ /* Prepare the colors (real colors) */
+ RGBBackColor(&black);
+ RGBForeColor(&white);
+
+ /* Block */
+ {
+ Rect globalRect;
+ GDHandle mainGDH;
+ GDHandle currentGDH;
+ GWorldPtr windowGWorld;
+ PixMapHandle basePixMap;
+
+ /* Obtain the global rect */
+ GetWindowBounds((WindowRef)td->w, kWindowContentRgn, &globalRect);
+
+ /* Obtain the proper GDH */
+ mainGDH = GetMaxDevice(&globalRect);
+
+ /* Extract GWorld and GDH */
+ GetGWorld(&windowGWorld, &currentGDH);
+
+ /* Obtain base pixmap */
+ basePixMap = (**mainGDH).gdPMap;
+
+ /* Save pixel depth */
+ td->pixelDepth = (**basePixMap).pixelSize;
+
+ /* Save Window GWorld - unused */
+ td->theGWorld = windowGWorld;
+
+ /* Save Window GDH */
+ td->theGDH = currentGDH;
+
+ /* Save main GDH - unused */
+ td->mainSWGDH = mainGDH;
+ }
+
+ {
+ Rect portRect;
+
+ /* Get current Rect */
+ GetPortBounds(GetWindowPort(td->w), &portRect);
+
+ /* Clip to the window */
+ ClipRect(&portRect);
+
+ /* Erase the window */
+ EraseRect(&portRect);
+
+ /* Invalidate the window */
+ InvalWindowRect(td->w, &portRect);
+ }
+
+ /*
+ * A certain release of OS X fails to display windows at proper
+ * locations (_ _#)
+ */
+ if ((mac_os_version >= 0x1000) && (mac_os_version < 0x1010))
+ {
+ /* Hack - Make sure the window is displayed at (r.left,r.top) */
+ MoveWindow(td->w, td->r.left, td->r.top, 1);
+ }
+
+ /* Display the window if needed */
+ if (td->mapped)
+ {
+ TransitionWindow(td->w,
+ kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL);
+ }
+
+ /* Hack -- set "mapped" flag */
+ t->mapped_flag = td->mapped;
+
+ /* Forget color */
+ td->last = -1;
+}
+
+
+
+/*
+ * Nuke an old Term
+ */
+static void Term_nuke_mac(term *t)
+{
+ /* XXX */
+}
+
+
+
+/*
+ * Unused
+ */
+static errr Term_user_mac(int n)
+{
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_mac_react(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ int i;
+
+
+ /* Reset color */
+ td->last = -1;
+
+ /* Update colors */
+ for (i = 0; i < 256; i++)
+ {
+ u16b rv, gv, bv;
+
+ /* Extract the R,G,B data */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Save the actual color */
+ color_info[i].red = (rv | (rv << 8));
+ color_info[i].green = (gv | (gv << 8));
+ color_info[i].blue = (bv | (bv << 8));
+ }
+
+
+ /* Handle sound */
+ if (use_sound != arg_sound)
+ {
+ /* Apply request */
+ use_sound = arg_sound;
+ }
+
+
+ /* Handle graphics */
+ if (graf_mode_req != graf_mode)
+ {
+ /* dispose old GWorld's if present */
+ globe_nuke();
+
+ /*
+ * Setup parameters according to request
+ *
+ * In [Z], you have to set use_graphics and arg_graphics to
+ * GRAPHICS_NONE, GRAPHICS_ORIGINAL or GRAPHICS_ADAM_BOLT, and
+ * comment ANGBAND_GRAF out.
+ */
+ switch (graf_mode_req)
+ {
+ /* ASCII - no graphics whatsoever */
+ case GRAF_MODE_NONE:
+ {
+ use_graphics = arg_graphics = FALSE;
+ transparency_mode = TR_NONE;
+ break;
+ }
+
+ /*
+ * 8x8 tiles (PICT id 1001)
+ * no transparency effect
+ * "old" graphics definitions
+ */
+ case GRAF_MODE_8X8:
+ {
+ use_graphics = arg_graphics = TRUE;
+ ANGBAND_GRAF = "old";
+ transparency_mode = TR_NONE;
+#ifdef MACH_O_CARBON
+ pict_id = CFSTR("8x8");
+#else
+ pict_id = 1001;
+#endif /* MACH_O_CARBON */
+ graf_width = graf_height = 8;
+ break;
+ }
+
+ /*
+ * 16x16 tiles (images: PICT id 1002, masks: PICT id 1003)
+ * with transparency effect
+ * "new" graphics definitions
+ */
+ case GRAF_MODE_16X16:
+ {
+ use_graphics = arg_graphics = TRUE;
+ ANGBAND_GRAF = "new";
+ transparency_mode = TR_OVER;
+#ifdef MACH_O_CARBON
+ pict_id = CFSTR("16x16");
+#else
+ pict_id = 1002;
+#endif /* MACH_O_CARBON */
+ graf_width = graf_height = 16;
+ break;
+ }
+
+ /*
+ * 32x32 tiles (images: PICT id 1004)
+ * with transparency effect
+ * "david" graphics definitions
+ * Vanilla-specific
+ */
+ case GRAF_MODE_32X32:
+ {
+ use_graphics = arg_graphics = TRUE;
+ ANGBAND_GRAF = "david";
+ transparency_mode = TR_OVER;
+#ifdef MACH_O_CARBON
+ pict_id = CFSTR("32x32");
+#else
+ pict_id = 1004;
+#endif /* MACH_O_CARBON */
+ graf_width = graf_height = 32;
+ break;
+ }
+ }
+
+ /* load tiles and setup GWorlds if tiles are requested */
+ if ((graf_mode_req != GRAF_MODE_NONE) && (globe_init() != 0))
+ {
+ /* Oops */
+ plog("Cannot initialize graphics!");
+
+ /* reject request */
+ graf_mode_req = GRAF_MODE_NONE;
+
+ /* reset graphics flags */
+ use_graphics = arg_graphics = FALSE;
+
+ /* reset transparency mode */
+ transparency_mode = TR_NONE;
+ }
+
+ /* update current graphics mode */
+ graf_mode = graf_mode_req;
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Resize the window */
+ term_data_resize(td);
+
+ /* Reset visuals */
+#ifndef ANG281_RESET_VISUALS
+ reset_visuals(TRUE);
+#else
+ reset_visuals();
+#endif /* !ANG281_RESET_VISUALS */
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Do a "special thing"
+ */
+static errr Term_xtra_mac(int n, int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ Rect r;
+
+ /* Analyze */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ {
+ /* Make a noise */
+ SysBeep(1);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Make a sound */
+ case TERM_XTRA_SOUND:
+ {
+#ifndef USE_ASYNC_SOUND
+
+ /*
+ * This may not be your choice, but much safer and much less
+ * resource hungry. Existing implementations can quite easily
+ * crash, by starting asynchronous playing and immediately
+ * unlocking and releasing the sound data just started playing...
+ * -- pelpel
+ */
+ Handle handle;
+ Str255 sound;
+
+ /* Get the proper sound name */
+ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[v]);
+ sound[0] = strlen((char*)sound + 1);
+
+ /* Obtain resource XXX XXX XXX */
+ handle = GetNamedResource('snd ', sound);
+
+ /* Oops -- it is a failure, but we return 0 anyway */
+ if (handle == NULL) return (0);
+
+ /* Load and Lock */
+ LoadResource(handle);
+ HLock(handle);
+
+ /* Play sound (wait for completion) */
+ SndPlay(NULL, (SndListHandle)handle, FALSE);
+
+ /* Unlock and release */
+ HUnlock(handle);
+ ReleaseResource(handle);
+
+#else /* !USE_ASYNC_SOUND */
+
+ /* Play sound */
+ play_sound(v, sound_volume);
+
+#endif /* !USE_ASYNC_SOUND */
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ {
+ /* Process an event */
+ (void)CheckEvents(FALSE);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process pending events */
+ case TERM_XTRA_EVENT:
+ {
+ /* Process an event */
+ (void)CheckEvents(v);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush all pending events (if any) */
+ case TERM_XTRA_FLUSH:
+ {
+ /* Hack -- flush all events */
+ while (CheckEvents(FALSE)) /* loop */;
+
+ /* Success */
+ return (0);
+ }
+
+ /* Hack -- Change the "soft level" */
+ case TERM_XTRA_LEVEL:
+ {
+ /* Activate if requested */
+ if (v) activate(td->w);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ {
+ Rect portRect;
+
+ /* Get current Rect */
+ GetPortBounds(GetWindowPort(td->w), &portRect);
+
+ /* No clipping XXX XXX XXX */
+ ClipRect(&portRect);
+
+ /* Erase the window */
+ EraseRect(&portRect);
+
+ /* Set the color */
+ term_data_color(td, TERM_WHITE);
+
+ /* Frame the window in white */
+ MoveTo(0, 0);
+ LineTo(0, td->size_hgt - 1);
+ LineTo(td->size_wid - 1, td->size_hgt - 1);
+ LineTo(td->size_wid - 1, 0);
+
+ /* Clip to the new size */
+ r.left = portRect.left + td->size_ow1;
+ r.top = portRect.top + td->size_oh1;
+ r.right = portRect.right - td->size_ow2;
+ r.bottom = portRect.bottom - td->size_oh2;
+ ClipRect(&r);
+
+ /* Success */
+ return (0);
+ }
+
+ /* React to changes */
+ case TERM_XTRA_REACT:
+ {
+ /* React to changes */
+ return (Term_xtra_mac_react());
+ }
+
+ /* Delay (milliseconds) */
+ case TERM_XTRA_DELAY:
+ {
+ /*
+ * WaitNextEvent relinquishes CPU as well as
+ * induces a screen refresh on OS X
+ */
+
+ /* If needed */
+ if (v > 0)
+ {
+ EventRecord tmp;
+ UInt32 ticks;
+
+ /* Convert millisecs to ticks */
+ ticks = (v * 60L) / 1000;
+
+ /*
+ * Hack? - Put the programme into sleep.
+ * No events match ~everyEvent, so nothing
+ * should be lost in Angband's event queue.
+ * Even if ticks are 0, it's worth calling for
+ * the above mentioned reasons.
+ */
+ WaitNextEvent((EventMask)~everyEvent, &tmp, ticks, nil);
+ }
+
+ /* Success */
+ return (0);
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN:
+ {
+ char *s = strdup(angband_term_name[0]);
+
+ ctopstr((StringPtr)s);
+ SetWTitle(data[0].w, (StringPtr)s);
+
+ free(s);
+ return (0);
+ }
+
+/* MacOSX == Unix == Good */
+#ifdef USE_MACOSX
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while ((entry = readdir(directory)))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+
+ closedir(directory);
+ return 0;
+ }
+#endif
+ }
+
+ /* Oops */
+ return (1);
+}
+
+
+
+/*
+ * Low level graphics (Assumes valid input).
+ * Draw a "cursor" at (x,y), using a "yellow box".
+ * We are allowed to use "Term_what()" to determine
+ * the current screen contents (for inverting, etc).
+ */
+static errr Term_curs_mac(int x, int y)
+{
+ Rect r;
+
+ term_data *td = (term_data*)(Term->data);
+
+ /* Set the color */
+ term_data_color(td, TERM_YELLOW);
+
+ /* Frame the grid */
+ r.left = x * td->tile_wid + td->size_ow1;
+ r.right = r.left + td->tile_wid;
+ r.top = y * td->tile_hgt + td->size_oh1;
+ r.bottom = r.top + td->tile_hgt;
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Mogami's bigtile patch */
+
+ /* Adjust it if double width tiles are requested */
+ if (use_bigtile &&
+ (x + 1 < Term->wid) &&
+ (Term->old->a[y][x + 1] == 255))
+ {
+ r.right += td->tile_wid;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ FrameRect(&r);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Low level graphics (Assumes valid input)
+ *
+ * Erase "n" characters starting at (x,y)
+ */
+static errr Term_wipe_mac(int x, int y, int n)
+{
+ Rect r;
+
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Erase the block of characters */
+ r.left = x * td->tile_wid + td->size_ow1;
+ r.right = r.left + n * td->tile_wid;
+ r.top = y * td->tile_hgt + td->size_oh1;
+ r.bottom = r.top + td->tile_hgt;
+ EraseRect(&r);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Low level graphics. Assumes valid input.
+ *
+ * Draw several ("n") chars, with an attr, at a given location.
+ */
+static errr Term_text_mac(int x, int y, int n, byte a, const char *cp)
+{
+ int xp, yp;
+
+#ifdef CLIP_HACK
+ Rect r;
+#endif /* CLIP_HACK */
+
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Set the color */
+ term_data_color(td, a);
+
+#ifdef CLIP_HACK
+ /* Hack - only draw within the bounding rect */
+ r.left = x * td->tile_wid + td->size_ow1;
+ r.right = r.left + n * td->tile_wid;
+ r.top = y * td->tile_hgt + td->size_oh1;
+ r.bottom = r.top + td->tile_hgt;
+ ClipRect(&r);
+
+ /* Hack - clear the content of the bounding rect */
+ EraseRect(&r);
+#endif /* CLIP_HACK */
+
+ /* Starting pixel */
+ xp = x * td->tile_wid + td->tile_o_x + td->size_ow1;
+ yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1;
+
+ /* Move to the correct location */
+ MoveTo(xp, yp);
+
+ /* Draw the character */
+ if (n == 1) DrawChar(*cp);
+
+ /* Draw the string */
+ else DrawText(cp, 0, n);
+
+#ifdef CLIP_HACK
+ /* Obtain current window's rect */
+ GetPortBounds(GetWindowPort(td->w), &r);
+
+ /* Clip to the window again */
+ ClipRect(&r);
+#endif /* CLIP_HACK */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Low level graphics (Assumes valid input)
+ *
+ * Erase "n" characters starting at (x,y)
+ */
+static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp,
+ const byte *eap, const char *ecp)
+{
+ int i;
+ Rect dst_r;
+ GrafPtr port;
+ PixMapHandle pixmap_h;
+
+#ifdef CLIP_HACK
+ Rect portRect;
+#endif /* CLIP_HACK */
+
+ term_data *td = (term_data*)(Term->data);
+
+ static RGBColor black = {0x0000, 0x0000, 0x0000};
+ static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};
+
+
+#ifdef CLIP_HACK
+ /* Remember current window's rect */
+ GetPortBounds(GetWindowPort(td->w), &portRect);
+#endif /* CLIP_HACK */
+
+ /* Destination rectangle */
+ dst_r.left = x * td->tile_wid + td->size_ow1;
+#ifndef USE_DOUBLE_TILES
+ dst_r.right = dst_r.left + td->tile_wid;
+#endif /* !USE_DOUBLE_TILES */
+ dst_r.top = y * td->tile_hgt + td->size_oh1;
+ dst_r.bottom = dst_r.top + td->tile_hgt;
+
+ /* Scan the input */
+ for (i = 0; i < n; i++)
+ {
+ bool_ done = FALSE;
+
+ byte a = *ap++;
+ char c = *cp++;
+
+ byte ta = *tap++;
+ char tc = *tcp++;
+ byte ea = *eap++;
+ char ec = *ecp++;
+ bool_ has_overlay = (ea && ec);
+
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Hack -- a filler for double-width tile */
+ if (use_bigtile && (a == 255))
+ {
+ /* Advance */
+ dst_r.left += td->tile_wid;
+
+ /* Ignore */
+ continue;
+ }
+
+ /* Prepare right side of rectagle now */
+ dst_r.right = dst_r.left + td->tile_wid;
+
+#endif /* USE_DOUBLE_TILES */
+
+ /* Graphics -- if Available and Needed */
+ if (use_graphics && ((byte)a & 0x80) && ((byte)c & 0x80))
+ {
+ int col, row;
+ Rect src_r;
+ int t_col, t_row;
+ Rect terrain_r;
+ int e_col, e_row;
+ Rect ego_r;
+
+ /* Row and Col */
+ row = ((byte)a & 0x7F) % pict_rows;
+ col = ((byte)c & 0x7F) % pict_cols;
+
+ /* Source rectangle */
+ src_r.left = col * graf_width;
+ src_r.top = row * graf_height;
+ src_r.right = src_r.left + graf_width;
+ src_r.bottom = src_r.top + graf_height;
+
+ /* Row and Col */
+ t_row = ((byte)ta & 0x7F) % pict_rows;
+ t_col = ((byte)tc & 0x7F) % pict_cols;
+
+ /* Source rectangle */
+ terrain_r.left = t_col * graf_width;
+ terrain_r.top = t_row * graf_height;
+ terrain_r.right = terrain_r.left + graf_width;
+ terrain_r.bottom = terrain_r.top + graf_height;
+
+ /* If there's an overlay */
+ if (has_overlay)
+ {
+ /* Row and Col */
+ e_row = ((byte)ea & 0x7F) % pict_rows;
+ e_col = ((byte)ec & 0x7F) % pict_cols;
+
+ /* Source rectangle */
+ ego_r.left = e_col * graf_width;
+ ego_r.top = e_row * graf_height;
+ ego_r.right = ego_r.left + graf_width;
+ ego_r.bottom = ego_r.top + graf_height;
+ }
+
+ /* Hardwire CopyBits */
+ RGBBackColor(&white);
+ RGBForeColor(&black);
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Double width tiles */
+ if (use_bigtile) dst_r.right += td->tile_wid;
+
+#endif /* USE_DOUBLE_TILES */
+
+ /*
+ * OS X requires locking and unlocking of window port
+ * when we draw directly to its pixmap.
+ * The Lock/Unlock protocol is described in the Carbon
+ * Porting Guide.
+ */
+
+ /* Obtain current window's graphic port */
+ port = GetWindowPort(td->w);
+
+ /* Lock pixels, so we can use handle safely */
+ LockPortBits(port);
+
+ /* Get Pixmap handle */
+ pixmap_h = GetPortPixMap(port);
+
+ /* Transparency effect */
+ switch (transparency_mode)
+ {
+ /* No transparency effects */
+ case TR_NONE:
+ default:
+ {
+ /* Draw the picture */
+ CopyBits((BitMap*)frameP->framePix,
+ (BitMap*)*pixmap_h,
+ &src_r, &dst_r, srcCopy, NULL);
+
+ break;
+ }
+
+ /* Overwriting with transparent black pixels */
+ case TR_OVER:
+ {
+ /* Draw the terrain */
+ CopyBits((BitMap*)frameP->framePix,
+ (BitMap*)*pixmap_h,
+ &terrain_r, &dst_r, srcCopy, NULL);
+
+ /* Make black pixels transparent */
+ RGBBackColor(&black);
+
+ /* Draw mon/obj if there's one */
+ if ((row != t_row) || (col != t_col))
+ CopyBits((BitMap*)frameP->framePix,
+ (BitMap*)*pixmap_h,
+ &src_r, &dst_r, transparent, NULL);
+
+ /* Draw overlay if there's one */
+ if (has_overlay)
+ {
+ CopyBits((BitMap*)frameP->framePix,
+ (BitMap*)*pixmap_h,
+ &ego_r, &dst_r, transparent, NULL);
+ }
+
+ break;
+ }
+ }
+
+ /* Release the lock and dispose the PixMap handle */
+ UnlockPortBits(port);
+
+ /* Restore colors */
+ RGBBackColor(&black);
+ RGBForeColor(&white);
+
+ /* Forget color */
+ td->last = -1;
+
+ /* Done */
+ done = TRUE;
+ }
+
+ /* Normal */
+ if (!done)
+ {
+ int xp, yp;
+
+#ifdef CLIP_HACK
+ /* Hack - avoid writing outside of dst_r */
+ ClipRect(&dst_r);
+ /* Some characters do not match dst_r, therefore we have to... */
+#endif /* CLIP_HACK */
+
+ /* Erase */
+ EraseRect(&dst_r);
+
+ /* Set the color */
+ term_data_color(td, a);
+
+ /* Starting pixel */
+ xp = dst_r.left + td->tile_o_x;
+ yp = dst_r.top + td->tile_o_y;
+
+ /* Move to the correct location */
+ MoveTo(xp, yp);
+
+ /* Draw the character */
+ DrawChar(c);
+
+#ifdef CLIP_HACK
+ /* Clip to the window - inefficient (; ;) XXX XXX */
+ ClipRect(&portRect);
+#endif /* CLIP_HACK */
+ }
+
+ /* Advance */
+ dst_r.left += td->tile_wid;
+#ifndef USE_DOUBLE_TILES
+ dst_r.right += td->tile_wid;
+#endif /* !USE_DOUBLE_TILES */
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+
+
+/*
+ * Create and initialize window number "i"
+ */
+static void term_data_link(int i)
+{
+ term *old = Term;
+
+ term_data *td = &data[i];
+
+ /* Only once */
+ if (td->t) return;
+
+ /* Require mapped */
+ if (!td->mapped) return;
+
+ /* Allocate */
+ MAKE(td->t, term);
+
+ /* Initialize the term */
+ term_init(td->t, td->cols, td->rows, td->keys);
+
+ /* Use a "software" cursor */
+ td->t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ td->t->attr_blank = TERM_WHITE;
+ td->t->char_blank = ' ';
+
+ /* Prepare the init/nuke hooks */
+ td->t->init_hook = Term_init_mac;
+ td->t->nuke_hook = Term_nuke_mac;
+
+ /* Prepare the function hooks */
+ td->t->user_hook = Term_user_mac;
+ td->t->xtra_hook = Term_xtra_mac;
+ td->t->wipe_hook = Term_wipe_mac;
+ td->t->curs_hook = Term_curs_mac;
+ td->t->text_hook = Term_text_mac;
+ td->t->pict_hook = Term_pict_mac;
+
+#if 0
+
+ /* Doesn't make big difference? */
+ td->t->never_bored = TRUE;
+
+#endif
+
+ /* Link the local structure */
+ td->t->data = (void *)(td);
+
+ /* Activate it */
+ Term_activate(td->t);
+
+ /* Global pointer */
+ angband_term[i] = td->t;
+
+ /* Activate old */
+ Term_activate(old);
+}
+
+
+
+
+#ifdef MACH_O_CARBON
+
+/*
+ * (Carbon, Bundle)
+ * Return a POSIX pathname of the lib directory, or NULL if it can't be
+ * located. Caller must supply a buffer along with its size in bytes,
+ * where returned pathname will be stored.
+ * I prefer use of goto's to several nested if's, if they involve error
+ * handling. Sorry if you are offended by their presence. Modern
+ * languages have neater constructs for this kind of jobs -- pelpel
+ */
+static char *locate_lib(char *buf, size_t size)
+{
+ CFURLRef main_url = NULL;
+ CFStringRef main_str = NULL;
+ char *p;
+ char *res = NULL;
+
+ /* Obtain the URL of the main bundle */
+ main_url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+
+ /* Oops */
+ if (main_url == NULL) goto ret;
+
+ /* Convert it to POSIX pathname */
+ main_str = CFURLCopyFileSystemPath(main_url, kCFURLPOSIXPathStyle);
+
+ /* Oops */
+ if (main_str == NULL) goto ret;
+
+ /* Convert it again from darn unisomething encoding to ASCII */
+ if (CFStringGetCString(main_str, buf, size, kTextEncodingUS_ASCII) == FALSE)
+ goto ret;
+
+ /*
+ * Paranoia - bounds check
+ */
+ if (strlen(buf) + 25 + 1 > size) goto ret;
+
+ /* Location of the data */
+ strcat(buf, "/Contents/Resources/");
+
+ /* Set result */
+ res = buf;
+
+ret:
+
+ /* Release objects allocated and implicitly retained by the program */
+ if (main_str) CFRelease(main_str);
+ if (main_url) CFRelease(main_url);
+
+ /* pathname of the lib folder or NULL */
+ return (res);
+}
+
+
+#else /* MACH_O_CARBON */
+
+/*
+* Set the "current working directory" (also known as the "default"
+* volume/directory) to the location of the current application.
+*
+* Original code by: Maarten Hazewinkel (mmhazewi@cs.ruu.nl)
+*
+* Completely rewritten to use Carbon Process Manager. It retrieves the
+* volume and direcotry of the current application and simply stores it
+* in the (static) global variables app_vol and app_dir, but doesn't
+* mess with the "current working directory", because it has long been
+* an obsolete (and arcane!) feature.
+*/
+static void SetupAppDir(void)
+{
+ OSErr err;
+ ProcessSerialNumber curPSN;
+ ProcessInfoRec procInfo;
+ FSSpec cwdSpec;
+
+ /* Initialise PSN info for the current process */
+ curPSN.highLongOfPSN = 0;
+ curPSN.lowLongOfPSN = kCurrentProcess;
+
+ /* Fill in mandatory fields */
+ procInfo.processInfoLength = sizeof(ProcessInfoRec);
+ procInfo.processName = nil;
+ procInfo.processAppSpec = &cwdSpec;
+
+ /* Obtain current process information */
+ err = GetProcessInformation(&curPSN, &procInfo);
+
+ /* Oops */
+ if (err != noErr)
+ {
+ mac_warning("Unable to get process information");
+
+ /* Quit without writing anything */
+ ExitToShell();
+ }
+
+ /* Extract and save the Vol and Dir */
+ app_vol = cwdSpec.vRefNum;
+ app_dir = cwdSpec.parID;
+}
+
+#endif /* MACH_O_CARBON */
+
+
+
+
+/*
+ * Using Core Foundation's Preferences services -- pelpel
+ *
+ * Requires OS 8.6 or greater with CarbonLib 1.1 or greater. Or OS X,
+ * of course.
+ *
+ * Without this, we can support older versions of OS 8 as well
+ * (with CarbonLib 1.0.4).
+ *
+ * Frequent allocation/deallocation of small chunks of data is
+ * far from my liking, but since this is only called at the
+ * beginning and the end of a session, I hope this hardly matters.
+ */
+
+
+/*
+ * Store "value" as the value for preferences item name
+ * pointed by key
+ */
+static void save_pref_short(const char *key, short value)
+{
+ CFStringRef cf_key;
+ CFNumberRef cf_value;
+
+ /* allocate and initialise the key */
+ cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII);
+
+ /* allocate and initialise the value */
+ cf_value = CFNumberCreate(NULL, kCFNumberShortType, &value);
+
+ if ((cf_key != NULL) && (cf_value != NULL))
+ {
+ /* Store the key-value pair in the applications preferences */
+ CFPreferencesSetAppValue(
+ cf_key,
+ cf_value,
+ kCFPreferencesCurrentApplication);
+ }
+
+ /*
+ * Free CF data - the reverse order is a vain attempt to
+ * minimise memory fragmentation.
+ */
+ if (cf_value) CFRelease(cf_value);
+ if (cf_key) CFRelease(cf_key);
+}
+
+
+/*
+ * Load preference value for key, returns TRUE if it succeeds with
+ * vptr updated appropriately, FALSE otherwise.
+ */
+static bool_ query_load_pref_short(const char *key, short *vptr)
+{
+ CFStringRef cf_key;
+ CFNumberRef cf_value;
+
+ /* allocate and initialise the key */
+ cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII);
+
+ /* Oops */
+ if (cf_key == NULL) return (FALSE);
+
+ /* Retrieve value for the key */
+ cf_value = CFPreferencesCopyAppValue(
+ cf_key,
+ kCFPreferencesCurrentApplication);
+
+ /* Value not found */
+ if (cf_value == NULL)
+ {
+ CFRelease(cf_key);
+ return (FALSE);
+ }
+
+ /* Convert the value to short */
+ CFNumberGetValue(
+ cf_value,
+ kCFNumberShortType,
+ vptr);
+
+ /* Free CF data */
+ CFRelease(cf_value);
+ CFRelease(cf_key);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Update short data pointed by vptr only if preferences
+ * value for key is located.
+ */
+static void load_pref_short(const char *key, short *vptr)
+{
+ short tmp;
+
+ if (query_load_pref_short(key, &tmp)) *vptr = tmp;
+ return;
+}
+
+
+/*
+ * Save preferences to preferences file for current host+current user+
+ * current application.
+ */
+static void cf_save_prefs()
+{
+ int i;
+
+ /* Version stamp */
+ save_pref_short("version.major", PREF_VER_MAJOR);
+ save_pref_short("version.minor", PREF_VER_MINOR);
+ save_pref_short("version.patch", PREF_VER_PATCH);
+ save_pref_short("version.extra", PREF_VER_EXTRA);
+
+ /* Gfx settings */
+ save_pref_short("arg.arg_sound", arg_sound);
+ save_pref_short("arg.graf_mode", graf_mode);
+#ifdef USE_DOUBLE_TILES
+ save_pref_short("arg.big_tile", use_bigtile);
+#endif /* USE_DOUBLE_TILES */
+
+ /* Windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term_data *td = &data[i];
+
+ save_pref_short(format("term%d.font_mono", i), td->font_mono);
+ save_pref_short(format("term%d.font_o_x", i), td->font_o_x);
+ save_pref_short(format("term%d.font_o_y", i), td->font_o_y);
+ save_pref_short(format("term%d.font_wid", i), td->font_wid);
+ save_pref_short(format("term%d.font_hgt", i), td->font_hgt);
+ save_pref_short(format("term%d.tile_o_x", i), td->tile_o_x);
+ save_pref_short(format("term%d.tile_o_y", i), td->tile_o_y);
+ save_pref_short(format("term%d.right", i), td->r.right);
+ save_pref_short(format("term%d.bottom", i), td->r.bottom);
+ save_pref_short(format("term%d.ow1", i), td->size_ow1);
+ save_pref_short(format("term%d.oh1", i), td->size_oh1);
+ save_pref_short(format("term%d.ow2", i), td->size_ow2);
+ save_pref_short(format("term%d.oh2", i), td->size_oh2);
+
+ save_pref_short(format("term%d.mapped", i), td->mapped);
+
+ save_pref_short(format("term%d.font_id", i), td->font_id);
+ save_pref_short(format("term%d.font_size", i), td->font_size);
+ save_pref_short(format("term%d.font_face", i), td->font_face);
+
+ save_pref_short(format("term%d.tile_wid", i), td->tile_wid);
+ save_pref_short(format("term%d.tile_hgt", i), td->tile_hgt);
+
+ save_pref_short(format("term%d.cols", i), td->cols);
+ save_pref_short(format("term%d.rows", i), td->rows);
+ save_pref_short(format("term%d.left", i), td->r.left);
+ save_pref_short(format("term%d.top", i), td->r.top);
+ }
+
+ /*
+ * Make sure preferences are persistent
+ */
+ CFPreferencesAppSynchronize(
+ kCFPreferencesCurrentApplication);
+}
+
+
+/*
+ * Load preferences from preferences file for current host+current user+
+ * current application.
+ */
+static void cf_load_prefs()
+{
+ bool_ ok;
+ short pref_major, pref_minor, pref_patch, pref_extra;
+ int i;
+
+ /* Assume nothing is wrong, yet */
+ ok = TRUE;
+
+ /* Load version information */
+ ok &= query_load_pref_short("version.major", &pref_major);
+ ok &= query_load_pref_short("version.minor", &pref_minor);
+ ok &= query_load_pref_short("version.patch", &pref_patch);
+ ok &= query_load_pref_short("version.extra", &pref_extra);
+
+ /* Any of the above failed */
+ if (!ok)
+ {
+ /* This may be the first run */
+ mac_warning("Preferences are not found.");
+
+ /* Ignore the rest */
+ return;
+ }
+
+#if 0
+
+ /* Check version */
+ if ((pref_major != PREF_VER_MAJOR) ||
+ (pref_minor != PREF_VER_MINOR) ||
+ (pref_patch != PREF_VER_PATCH) ||
+ (pref_extra != PREF_VER_EXTRA))
+ {
+ /* Message */
+ mac_warning(
+ format("Ignoring %d.%d.%d.%d preferences.",
+ pref_major, pref_minor, pref_patch, pref_extra));
+
+ /* Ignore */
+ return;
+ }
+
+#endif
+
+ /* Gfx settings */
+ {
+ short pref_tmp;
+
+ /* sound */
+ if (query_load_pref_short("arg.arg_sound", &pref_tmp))
+ arg_sound = pref_tmp;
+
+ /* graphics */
+ if (query_load_pref_short("arg.graf_mode", &pref_tmp))
+ graf_mode_req = pref_tmp;
+
+#ifdef USE_DOUBLE_TILES
+
+ /* double-width tiles */
+ if (query_load_pref_short("arg.big_tile", &pref_tmp))
+ {
+ use_bigtile = pref_tmp;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ }
+
+ /* Windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term_data *td = &data[i];
+
+ load_pref_short(format("term%d.mapped", i), &td->mapped);
+
+ load_pref_short(format("term%d.font_id", i), &td->font_id);
+ load_pref_short(format("term%d.font_size", i), &td->font_size);
+ load_pref_short(format("term%d.font_face", i), &td->font_face);
+
+ load_pref_short(format("term%d.tile_wid", i), &td->tile_wid);
+ load_pref_short(format("term%d.tile_hgt", i), &td->tile_hgt);
+
+ load_pref_short(format("term%d.cols", i), &td->cols);
+ load_pref_short(format("term%d.rows", i), &td->rows);
+ load_pref_short(format("term%d.left", i), &td->r.left);
+ load_pref_short(format("term%d.top", i), &td->r.top);
+
+ load_pref_short(format("term%d.font_mono", i), &td->font_mono);
+ load_pref_short(format("term%d.font_o_x", i), &td->font_o_x);
+ load_pref_short(format("term%d.font_o_y", i), &td->font_o_y);
+ load_pref_short(format("term%d.font_wid", i), &td->font_wid);
+ load_pref_short(format("term%d.font_hgt", i), &td->font_hgt);
+ load_pref_short(format("term%d.tile_o_x", i), &td->tile_o_x);
+ load_pref_short(format("term%d.tile_o_y", i), &td->tile_o_y);
+ load_pref_short(format("term%d.right", i), &td->r.right);
+ load_pref_short(format("term%d.bottom", i), &td->r.bottom);
+ load_pref_short(format("term%d.ow1", i), &td->size_ow1);
+ load_pref_short(format("term%d.oh1", i), &td->size_oh1);
+ load_pref_short(format("term%d.ow2", i), &td->size_ow2);
+ load_pref_short(format("term%d.oh2", i), &td->size_oh2);
+ }
+}
+
+
+
+
+/*
+ * Hack -- default data for a window
+ */
+static void term_data_hack(term_data *td)
+{
+ short fid;
+
+ /* Default to Monaco font */
+ GetFNum("\pmonaco", &fid);
+
+ /* Wipe it */
+ WIPE(td, term_data);
+
+ /* No color */
+ td->last = -1;
+
+ /* Default borders */
+ td->size_ow1 = 2;
+ td->size_ow2 = 2;
+ td->size_oh1 = 2;
+ td->size_oh2 = 2;
+
+ /* Start hidden */
+ td->mapped = FALSE;
+
+ /* Default font */
+ td->font_id = fid;
+
+ /* Default font size - was 12 */
+ td->font_size = 14;
+
+ /* Default font face */
+ td->font_face = 0;
+
+ /* Default size */
+ td->rows = 24;
+ td->cols = 80;
+
+ /* Default position */
+ td->r.left = 10;
+ td->r.top = 40;
+
+ /* Minimal keys */
+ td->keys = 16;
+}
+
+
+/*
+ * Read the preference file, Create the windows.
+ *
+ * We attempt to use "FindFolder()" to track down the preference file.
+ */
+static void init_windows(void)
+{
+ int i, b = 0;
+
+ term_data *td;
+
+
+ /*** Default values ***/
+
+ /* Initialize (backwards) */
+ for (i = MAX_TERM_DATA; i-- > 0; )
+ {
+ int n;
+
+ cptr s;
+
+ /* Obtain */
+ td = &data[i];
+
+ /* Defaults */
+ term_data_hack(td);
+
+ /* Obtain title */
+ s = angband_term_name[i];
+
+ /* Get length */
+ n = strlen(s);
+
+ /* Maximal length */
+ if (n > 15) n = 15;
+
+ /* Copy the title */
+ strncpy((char*)(td->title) + 1, s, n);
+
+ /* Save the length */
+ td->title[0] = n;
+
+ /* Tile the windows */
+ td->r.left += (b * 30);
+ td->r.top += (b * 30);
+
+ /* Tile */
+ b++;
+ }
+
+
+ /*** Load preferences ***/
+
+ cf_load_prefs();
+
+
+ /*** Instantiate ***/
+
+ /* Main window */
+ td = &data[0];
+
+ /* Many keys */
+ td->keys = 1024;
+
+ /* Start visible */
+ td->mapped = TRUE;
+
+ /* Link (backwards, for stacking order) */
+ for (i = MAX_TERM_DATA; i-- > 0; )
+ {
+ term_data_link(i);
+ }
+
+ /* Main window */
+ td = &data[0];
+
+ /* Main window */
+ Term_activate(td->t);
+}
+
+
+/*
+ * Save preferences
+ */
+static void save_pref_file(void)
+{
+ cf_save_prefs();
+}
+
+
+
+
+#ifndef SAVEFILE_SCREEN
+
+/*
+ * Prepare savefile dialogue and set the variable
+ * savefile accordingly. Returns true if it succeeds, false (or
+ * aborts) otherwise. If all is false, only allow files whose type
+ * is 'SAVE'.
+ * Originally written by Peter Ammon
+ */
+static bool_ select_savefile(bool_ all)
+{
+ OSErr err;
+ FSSpec theFolderSpec;
+ FSSpec savedGameSpec;
+ NavDialogOptions dialogOptions;
+ NavReplyRecord reply;
+ /* Used only when 'all' is true */
+ NavTypeList types = {ANGBAND_CREATOR, 1, 1, {'SAVE'}};
+ NavTypeListHandle myTypeList;
+ AEDesc defaultLocation;
+
+#ifdef MACH_O_CARBON
+
+ /* Find the save folder */
+ err = path_to_spec(ANGBAND_DIR_SAVE, &theFolderSpec);
+
+#else
+
+ /* Find :lib:save: folder */
+ err = FSMakeFSSpec(
+ app_vol,
+ app_dir,
+ "\p:lib:save:",
+ &theFolderSpec);
+
+#endif
+
+ /* Oops */
+ if (err != noErr) quit("Unable to find the folder :lib:save:");
+
+ /* Get default Navigator dialog options */
+ err = NavGetDefaultDialogOptions(&dialogOptions);
+
+ /* Clear preview option */
+ dialogOptions.dialogOptionFlags &= ~kNavAllowPreviews;
+
+ /* Disable multiple file selection */
+ dialogOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles;
+
+ /* Make descriptor for default location */
+ err = AECreateDesc(
+ typeFSS,
+ &theFolderSpec,
+ sizeof(FSSpec),
+ &defaultLocation);
+
+ /* Oops */
+ if (err != noErr) quit("Unable to allocate descriptor");
+
+ /* We are indifferent to signature and file types */
+ if (all)
+ {
+ myTypeList = (NavTypeListHandle)nil;
+ }
+
+ /* Set up type handle */
+ else
+ {
+ err = PtrToHand(&types, (Handle *) & myTypeList, sizeof(NavTypeList));
+
+ /* Oops */
+ if (err != noErr) quit("Error in PtrToHand. Try enlarging heap");
+
+ }
+
+ /* Call NavGetFile() with the types list */
+ err = NavChooseFile(
+ &defaultLocation,
+ &reply,
+ &dialogOptions,
+ nil,
+ nil,
+ nil,
+ myTypeList,
+ nil);
+
+ /* Free type list */
+ DisposeHandle((Handle)myTypeList);
+
+ /* Invalid response -- allow the user to cancel */
+ if (!reply.validRecord) return (FALSE);
+
+ /* Retrieve FSSpec from the reply */
+ if (err == noErr)
+ {
+ AEKeyword theKeyword;
+ DescType actualType;
+ Size actualSize;
+
+ /* Get a pointer to selected file */
+ (void)AEGetNthPtr(
+ &reply.selection,
+ 1,
+ typeFSS,
+ &theKeyword,
+ &actualType,
+ &savedGameSpec,
+ sizeof(FSSpec),
+ &actualSize);
+
+ /* Dispose NavReplyRecord, resources and descriptors */
+ (void)NavDisposeReply(&reply);
+ }
+
+ /* Dispose location info */
+ AEDisposeDesc(&defaultLocation);
+
+#ifdef MACH_O_CARBON
+
+ /* Convert FSSpec to pathname and store it in variable savefile */
+ (void)spec_to_path(&savedGameSpec, savefile, sizeof(savefile));
+
+#else
+
+ /* Convert FSSpec to pathname and store it in variable savefile */
+ refnum_to_name(
+ savefile,
+ savedGameSpec.parID,
+ savedGameSpec.vRefNum,
+ (char *)savedGameSpec.name);
+
+#endif
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Handle menu: "File" + "New"
+ */
+static void do_menu_file_new(void)
+{
+ /* Hack */
+ HiliteMenu(0);
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Flush input */
+ Term_flush();
+
+ /* Play a game */
+ play_game(TRUE);
+
+ /* Hack -- quit */
+ quit(NULL);
+}
+
+
+/*
+ * Handle menu: "File" + "Open" / "Import"
+ */
+static void do_menu_file_open(bool_ all)
+{
+ /* Let the player to choose savefile */
+ if (!select_savefile(all)) return;
+
+ /* Hack */
+ HiliteMenu(0);
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Flush input */
+ flush();
+
+ /* Play a game */
+ play_game(FALSE);
+
+ /* Hack -- quit */
+ quit(NULL);
+}
+
+#endif /* !SAVEFILE_SCREEN */
+
+
+/*
+ * Handle the "open_when_ready" flag
+ */
+static void handle_open_when_ready(void)
+{
+ /* Check the flag XXX XXX XXX make a function for this */
+ if (open_when_ready && initialized && !game_in_progress)
+ {
+ /* Forget */
+ open_when_ready = FALSE;
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Wait for it */
+ pause_line(23);
+
+ /* Flush input */
+ flush();
+
+#ifdef SAVEFILE_SCREEN
+
+ /* User double-clicked savefile; no savefile screen */
+ no_begin_screen = TRUE;
+
+#endif /* SAVEFILE_SCREEN */
+
+ /* Play a game */
+ play_game(FALSE);
+
+ /* Quit */
+ quit(NULL);
+ }
+}
+
+
+
+
+/*
+ * Menus
+ *
+ * The standard menus are:
+ *
+ * Apple (128) = { About, -, ... }
+ * File (129) = { New,Open,Import,Close,Save,-,Score,Quit }
+ * (If SAVEFILE_SCREEN is defined, this becomes)
+ * File (129) = { Close,Save,-,Score,Quit }
+ * Edit (130) = { Cut, Copy, Paste, Clear } (?)
+ * Font (131) = { Bold, Extend, -, Monaco, ..., -, ... }
+ * Size (132) = { ... }
+ * Window (133) = { Angband, Term-1/Mirror, Term-2/Recall, Term-3/Choice,
+ * Term-4, Term-5, Term-6, Term-7 }
+ * Special (134) = { Sound, Graphics, TileWidth, TileHeight, -,
+ * Fiddle, Wizard }
+ */
+
+/* Apple menu */
+#define MENU_APPLE 128
+#define ITEM_ABOUT 1
+
+/* File menu */
+#define MENU_FILE 129
+#ifndef SAVEFILE_SCREEN
+# define ITEM_NEW 1
+# define ITEM_OPEN 2
+# define ITEM_IMPORT 3
+# define ITEM_CLOSE 4
+# define ITEM_SAVE 5
+# ifdef HAS_SCORE_MENU
+# define ITEM_SCORE 7
+# define ITEM_QUIT 8
+# else
+# define ITEM_QUIT 7
+# endif /* HAS_SCORE_MENU */
+#else /* !SAVEFILE_SCREEN - in-game savefile menu */
+# define ITEM_CLOSE 1
+# define ITEM_SAVE 2
+# ifdef HAS_SCORE_MENU
+# define ITEM_SCORE 4
+# define ITEM_QUIT 5
+# else
+# define ITEM_QUIT 4
+# endif /* HAS_SCORE_MENU */
+#endif /* !SAVEFILE_SCREEN */
+
+/* Edit menu */
+#define MENU_EDIT 130
+#define ITEM_UNDO 1
+#define ITEM_CUT 3
+#define ITEM_COPY 4
+#define ITEM_PASTE 5
+#define ITEM_CLEAR 6
+
+/* Font menu */
+#define MENU_FONT 131
+#define ITEM_BOLD 1
+#define ITEM_WIDE 2
+
+/* Size menu */
+#define MENU_SIZE 132
+
+/* Windows menu */
+#define MENU_WINDOWS 133
+
+/* Special menu */
+#define MENU_SPECIAL 134
+#define ITEM_SOUND 1
+#define ITEM_GRAPH 2
+# define SUBMENU_GRAPH 144
+# define ITEM_NONE 1
+# define ITEM_8X8 2
+# define ITEM_16X16 3
+# define ITEM_32X32 4
+# define ITEM_BIGTILE 6
+#define ITEM_TILEWIDTH 3
+# define SUBMENU_TILEWIDTH 145
+#define ITEM_TILEHEIGHT 4
+# define SUBMENU_TILEHEIGHT 146
+#define ITEM_FIDDLE 6
+#define ITEM_WIZARD 7
+
+
+/*
+ * I HATE UNICODE! We've never wanted it. Some multi-national companies
+ * made it up as their internationalisation "solution". So I won't use
+ * any such API's -- pelpel
+ */
+#define NSIZES 32
+static byte menu_size_values[NSIZES];
+static byte menu_tilewidth_values[NSIZES];
+static byte menu_tileheight_values[NSIZES];
+
+/*
+ * Initialize the menus
+ *
+ * Fixed top level menus are now loaded all at once by GetNewMBar().
+ * Although this simplifies the function a bit, we have to make sure
+ * that resources have all the expected entries defined XXX XXX
+ */
+static void init_menubar(void)
+{
+ int i, n;
+
+ Rect r;
+
+ WindowPtr tmpw;
+
+ MenuRef m;
+
+#ifdef USE_NIB
+
+ /* The new way - loading main menu using Interface Builder services */
+ {
+ IBNibRef nib;
+ OSStatus err;
+
+ /* Create a nib reference to the main nib file */
+ err = CreateNibReference(CFSTR("main"), &nib);
+
+ /* Fatal error - missing Main.nib */
+ if (err != noErr) quit("Cannot find Main.nib in the bundle!");
+
+ /* Unarchive the menu bar and make it ready to use */
+ err = SetMenuBarFromNib(nib, CFSTR("MainMenu"));
+
+ /* Fatal error - couldn't insert menu bar */
+ if (err != noErr) quit("Cannot prepare menu bar!");
+
+ /* Dispose of the nib reference because we don't need it any longer */
+ DisposeNibReference(nib);
+ }
+
+#else /* USE_NIB */
+
+ /* The old way - loading main menu from Resource Manager resource */
+ {
+ Handle mbar;
+
+ /* Load menubar from resources */
+ mbar = GetNewMBar(128);
+
+ /* Whoops! */
+ if (mbar == nil) quit("Cannot find menubar('MBAR') id 128!");
+
+ /* Insert them into the current menu list */
+ SetMenuBar(mbar);
+
+ /* Free handle */
+ DisposeHandle(mbar);
+ }
+
+#endif /* USE_NIB */
+
+
+ /* Apple menu (id 128) - we don't have to do anything */
+
+#ifndef USE_NIB
+
+ /* File menu (id 129) - Aqua provides Quit menu for us */
+ if (is_aqua)
+ {
+ /* Get a handle to the file menu */
+ m = GetMenuHandle(MENU_FILE);
+
+ /* Nuke the quit menu since Aqua does that for us */
+ DeleteMenuItem(m, ITEM_QUIT);
+
+#ifndef HAS_SCORE_MENU
+
+ /* Hack - because the above leaves a separator as the last item */
+ DeleteMenuItem(m, ITEM_QUIT - 1);
+
+#endif /* !HAS_SCORE_MENU */
+ }
+
+#endif /* !USE_NIB */
+
+
+ /* Edit menu (id 130) - we don't have to do anything */
+
+
+ /*
+ * Font menu (id 131) - append names of mono-spaced fonts
+ * followed by all available ones
+ */
+ m = GetMenuHandle(MENU_FONT);
+
+ /* Fake window */
+ r.left = r.right = r.top = r.bottom = 0;
+
+ /* Make the fake window so that we can retrieve font info */
+ (void)CreateNewWindow(
+ kDocumentWindowClass,
+ kWindowNoAttributes,
+ &r,
+ &tmpw);
+
+ /* Activate the "fake" window */
+ SetPort(GetWindowPort(tmpw));
+
+ /* Default mode */
+ TextMode(0);
+
+ /* Default size */
+ TextSize(12);
+
+ /* Add the fonts to the menu */
+ AppendResMenu(m, 'FONT');
+
+ /* Size of menu */
+ n = CountMenuItems(m);
+
+ /* Scan the menu */
+ for (i = n; i >= 4; i--)
+ {
+ Str255 tmpName;
+ short fontNum;
+
+ /* Acquire the font name */
+ GetMenuItemText(m, i, tmpName);
+
+ /* Acquire the font index */
+ GetFNum(tmpName, &fontNum);
+
+ /* Apply the font index */
+ TextFont(fontNum);
+
+ /* Remove non-mono-spaced fonts */
+ if ((CharWidth('i') != CharWidth('W')) || (CharWidth('W') == 0))
+ {
+ /* Delete the menu item */
+ DeleteMenuItem(m, i);
+ }
+ }
+
+ /* Destroy the fake window */
+ DisposeWindow(tmpw);
+
+ /* Add a separator */
+ AppendMenu(m, "\p-");
+
+ /* Add the fonts to the menu */
+ AppendResMenu(m, 'FONT');
+
+
+#ifndef USE_NIB
+
+ /* Size menu (id 132) */
+ m = GetMenuHandle(MENU_SIZE);
+
+ /* Add some sizes (stagger choices) */
+ for (i = 8, n = 1; i <= 32; i += ((i / 16) + 1), n++)
+ {
+ Str15 buf;
+
+ /* Textual size */
+ strnfmt((char*)buf + 1, 15, "%d", i);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Add the item */
+ AppendMenu(m, buf);
+
+ /* Remember its value, for we can't be sure it's in ASCII */
+ menu_size_values[n] = i;
+ }
+
+#endif /* !USE_NIB */
+
+
+ /* Windows menu (id 133) */
+ m = GetMenuHandle(MENU_WINDOWS);
+
+ /* Default choices */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ Str15 buf;
+
+ /* Describe the item */
+ strnfmt((char*)buf + 1, 15, "%.15s", angband_term_name[i]);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Add the item */
+ AppendMenu(m, buf);
+
+ /* Command-Key shortcuts */
+ if (i < 8) SetItemCmd(m, i + 1, I2D(i));
+ }
+
+
+#ifndef USE_NIB
+
+ /* Special menu (id 134) */
+ m = GetMenuHandle(MENU_SPECIAL);
+
+ /* Insert Graphics submenu (id 144) */
+ {
+ MenuHandle submenu;
+
+ /* Get the submenu */
+ submenu = GetMenu(SUBMENU_GRAPH);
+
+ /* Insert it */
+ SetMenuItemHierarchicalMenu(m, ITEM_GRAPH, submenu);
+ }
+
+ /* Insert TileWidth submenu (id 145) */
+ {
+ MenuHandle submenu;
+
+ /* Get the submenu */
+ submenu = GetMenu(SUBMENU_TILEWIDTH);
+
+ /* Add some sizes */
+ for (i = 4, n = 1; i <= 32; i++, n++)
+ {
+ Str15 buf;
+
+ /* Textual size */
+ strnfmt((char*)buf + 1, 15, "%d", i);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Append item */
+ AppendMenu(submenu, buf);
+
+ /* Remember its value, for we can't be sure it's in ASCII */
+ menu_tilewidth_values[n] = i;
+ }
+
+ /* Insert it */
+ SetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, submenu);
+ }
+
+ /* Insert TileHeight submenu (id 146) */
+ {
+ MenuHandle submenu;
+
+ /* Get the submenu */
+ submenu = GetMenu(SUBMENU_TILEHEIGHT);
+
+
+ /* Add some sizes */
+ for (i = 4, n = 1; i <= 32; i++, n++)
+ {
+ Str15 buf;
+
+ /* Textual size */
+ strnfmt((char*)buf + 1, 15, "%d", i);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Append item */
+ AppendMenu(submenu, buf);
+
+ /* Remember its value, for we can't be sure it's in ASCII */
+ menu_tileheight_values[n] = i;
+ }
+
+ /* Insert it */
+ SetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, submenu);
+ }
+
+#endif /* !USE_NIB */
+
+ /* Update the menu bar */
+ DrawMenuBar();
+}
+
+
+/*
+ * Prepare the menus
+ *
+ * It is very important that the player not be allowed to "save" the game
+ * unless the "inkey_flag" variable is set, indicating that the game is
+ * waiting for a new command. XXX XXX XXX
+ */
+
+static void setup_menus(void)
+{
+ int i, n;
+
+ short value;
+
+ Str255 s;
+
+ MenuHandle m;
+
+ term_data *td = NULL;
+
+
+ /* Relevant "term_data" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Unused */
+ if (!data[i].t) continue;
+
+ /* Notice the matching window */
+ if (data[i].w == FrontWindow()) td = &data[i];
+ }
+
+
+ /* File menu */
+ m = GetMenuHandle(MENU_FILE);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Enable "new"/"open..."/"import..." */
+ if (initialized && !game_in_progress)
+ {
+ EnableMenuItem(m, ITEM_NEW);
+ EnableMenuItem(m, ITEM_OPEN);
+ EnableMenuItem(m, ITEM_IMPORT);
+ }
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Enable "close" */
+ if (initialized)
+ {
+ EnableMenuItem(m, ITEM_CLOSE);
+ }
+
+ /* Enable "save" */
+ if (initialized && character_generated && inkey_flag)
+ {
+ EnableMenuItem(m, ITEM_SAVE);
+ }
+
+#ifdef HAS_SCORE_MENU
+
+ /* Enable "score" */
+ if (initialized && character_generated && !character_icky)
+ {
+ EnableMenuItem(m, ITEM_SCORE);
+ }
+
+#endif /* HAS_SCORE_MENU */
+
+ /* Enable "quit" */
+ if (!is_aqua)
+ {
+ if (!initialized || !character_generated || inkey_flag)
+ {
+ EnableMenuItem(m, ITEM_QUIT);
+ }
+ }
+
+
+ /* Edit menu */
+ m = GetMenuHandle(MENU_EDIT);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+ /* Enable "edit" options if "needed" */
+ if (!td)
+ {
+ EnableMenuItem(m, ITEM_UNDO);
+ EnableMenuItem(m, ITEM_CUT);
+ EnableMenuItem(m, ITEM_COPY);
+ EnableMenuItem(m, ITEM_PASTE);
+ EnableMenuItem(m, ITEM_CLEAR);
+ }
+
+
+ /* Font menu */
+ m = GetMenuHandle(MENU_FONT);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+ /* Hack -- look cute XXX XXX */
+ /* SetItemStyle(m, ITEM_BOLD, bold); */
+
+ /* Hack -- look cute XXX XXX */
+ /* SetItemStyle(m, ITEM_WIDE, extend); */
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Enable "bold" */
+ EnableMenuItem(m, ITEM_BOLD);
+
+ /* Enable "extend" */
+ EnableMenuItem(m, ITEM_WIDE);
+
+ /* Check the appropriate "bold-ness" */
+ if (td->font_face & bold) CheckMenuItem(m, ITEM_BOLD, TRUE);
+
+ /* Check the appropriate "wide-ness" */
+ if (td->font_face & extend) CheckMenuItem(m, ITEM_WIDE, TRUE);
+
+ /* Analyze fonts */
+ for (i = 4; i <= n; i++)
+ {
+ /* Enable it */
+ EnableMenuItem(m, i);
+
+ /* Analyze font */
+ GetMenuItemText(m, i, s);
+ GetFNum(s, &value);
+
+ /* Check active font */
+ if (td->font_id == value) CheckMenuItem(m, i, TRUE);
+ }
+ }
+
+
+ /* Size menu */
+ m = GetMenuHandle(MENU_SIZE);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Analyze sizes */
+ for (i = 1; i <= n; i++)
+ {
+ /* Analyze size */
+ value = menu_size_values[i];
+
+ /* Enable the "real" sizes */
+ if (RealFont(td->font_id, value)) EnableMenuItem(m, i);
+
+ /* Check the current size */
+ if (td->font_size == value) CheckMenuItem(m, i, TRUE);
+ }
+ }
+
+
+ /* Windows menu */
+ m = GetMenuHandle(MENU_WINDOWS);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Check active windows */
+ for (i = 1; i <= n; i++)
+ {
+ /* Check if needed */
+ CheckMenuItem(m, i, data[i - 1].mapped);
+ }
+
+
+ /* Special menu */
+ m = GetMenuHandle(MENU_SPECIAL);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+ /* Item "arg_sound" */
+ EnableMenuItem(m, ITEM_SOUND);
+ CheckMenuItem(m, ITEM_SOUND, arg_sound);
+
+ /* Item "Graphics" */
+ EnableMenuItem(m, ITEM_GRAPH);
+ {
+ MenuRef submenu;
+
+ /* Graphics submenu */
+ (void)GetMenuItemHierarchicalMenu(m, ITEM_GRAPH, &submenu);
+
+ /* Get menu size */
+ n = CountMenuItems(submenu);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(submenu, i);
+ CheckMenuItem(submenu, i, FALSE);
+ }
+
+ /* Item "None" */
+ EnableMenuItem(submenu, ITEM_NONE);
+ CheckMenuItem(submenu, ITEM_NONE, (graf_mode == GRAF_MODE_NONE));
+
+ /* Item "8x8" */
+ EnableMenuItem(submenu, ITEM_8X8);
+ CheckMenuItem(submenu, ITEM_8X8, (graf_mode == GRAF_MODE_8X8));
+
+ /* Item "16x16" */
+ EnableMenuItem(submenu, ITEM_16X16);
+ CheckMenuItem(submenu, ITEM_16X16, (graf_mode == GRAF_MODE_16X16));
+
+ /* Item "32x32" */
+ /*EnableMenuItem(submenu, ITEM_32X32);
+ CheckMenuItem(submenu, ITEM_32X32, (graf_mode == GRAF_MODE_32X32));*/
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Item "Big tiles" */
+ if (inkey_flag) EnableMenuItem(submenu, ITEM_BIGTILE);
+ CheckMenuItem(submenu, ITEM_BIGTILE, use_bigtile);
+
+#endif /* USE_DOUBLE_TILES */
+
+ }
+
+ /* Item "TileWidth" */
+ EnableMenuItem(m, ITEM_TILEWIDTH);
+ {
+ MenuRef submenu;
+
+ /* TileWidth submenu */
+ (void)GetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, &submenu);
+
+ /* Get menu size */
+ n = CountMenuItems(submenu);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(submenu, i);
+ CheckMenuItem(submenu, i, FALSE);
+ }
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Analyze sizes */
+ for (i = 1; i <= n; i++)
+ {
+ /* Analyze size */
+ value = menu_tilewidth_values[i];
+
+ /* Enable */
+ if (value >= td->font_wid) EnableMenuItem(submenu, i);
+
+ /* Check the current size */
+ if (td->tile_wid == value) CheckMenuItem(submenu, i, TRUE);
+ }
+ }
+ }
+
+ /* Item "TileHeight" */
+ EnableMenuItem(m, ITEM_TILEHEIGHT);
+ {
+ MenuRef submenu;
+
+ /* TileWidth submenu */
+ (void)GetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, &submenu);
+
+ /* Get menu size */
+ n = CountMenuItems(submenu);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(submenu, i);
+ CheckMenuItem(submenu, i, FALSE);
+ }
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Analyze sizes */
+ for (i = 1; i <= n; i++)
+ {
+ /* Analyze size */
+ value = menu_tileheight_values[i];
+
+ /* Enable */
+ if (value >= td->font_hgt) EnableMenuItem(submenu, i);
+
+ /* Check the current size */
+ if (td->tile_hgt == value) CheckMenuItem(submenu, i, TRUE);
+ }
+ }
+ }
+
+ /* Item "arg_fiddle" */
+ EnableMenuItem(m, ITEM_FIDDLE);
+ CheckMenuItem(m, ITEM_FIDDLE, arg_fiddle);
+
+ /* Item "arg_wizard" */
+ EnableMenuItem(m, ITEM_WIZARD);
+ CheckMenuItem(m, ITEM_WIZARD, arg_wizard);
+
+
+ /* TileHeight menu */
+ m = GetMenuHandle(SUBMENU_TILEHEIGHT);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+}
+
+
+/*
+ * Process a menu selection (see above)
+ *
+ * Hack -- assume that invalid menu selections are disabled above,
+ * which I have been informed may not be reliable. XXX XXX XXX
+ */
+static void menu(long mc)
+{
+ int i;
+
+ int menuid, selection;
+
+ static unsigned char s[1000];
+
+ short fid;
+
+ term_data *td = NULL;
+
+ WindowPtr old_win;
+
+
+ /* Analyze the menu command */
+ menuid = HiWord(mc);
+ selection = LoWord(mc);
+
+
+ /* Find the window */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Skip dead windows */
+ if (!data[i].t) continue;
+
+ /* Notice matches */
+ if (data[i].w == FrontWindow()) td = &data[i];
+ }
+
+
+ /* Branch on the menu */
+ switch (menuid)
+ {
+ /* Apple Menu */
+ case MENU_APPLE:
+ {
+ /* About Angband... */
+ if (selection == ITEM_ABOUT)
+ {
+ DialogPtr dialog;
+ short item_hit;
+
+ /* Get the about dialogue */
+ dialog = GetNewDialog(128, 0, (WindowPtr) - 1);
+
+ /* Move it to the middle of the screen */
+ RepositionWindow(
+ GetDialogWindow(dialog),
+ NULL,
+ kWindowCenterOnMainScreen);
+
+ /* Show the dialog */
+ TransitionWindow(GetDialogWindow(dialog),
+ kWindowZoomTransitionEffect,
+ kWindowShowTransitionAction,
+ NULL);
+
+ /* Wait for user to click on it */
+ ModalDialog(0, &item_hit);
+
+ /* Free the dialogue */
+ DisposeDialog(dialog);
+ break;
+ }
+
+ break;
+ }
+
+ /* File Menu */
+ case MENU_FILE:
+ {
+ switch (selection)
+ {
+#ifndef SAVEFILE_SCREEN
+
+ /* New */
+ case ITEM_NEW:
+ {
+ do_menu_file_new();
+ break;
+ }
+
+ /* Open... */
+ case ITEM_OPEN:
+ {
+ do_menu_file_open(FALSE);
+ break;
+ }
+
+ /* Import... */
+ case ITEM_IMPORT:
+ {
+ do_menu_file_open(TRUE);
+ break;
+ }
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Close */
+ case ITEM_CLOSE:
+ {
+ /* No window */
+ if (!td) break;
+
+ /* Not Mapped */
+ td->mapped = FALSE;
+
+ /* Not Mapped */
+ td->t->mapped_flag = FALSE;
+
+ /* Hide the window */
+ TransitionWindow(td->w,
+ kWindowZoomTransitionEffect,
+ kWindowHideTransitionAction,
+ NULL);
+
+ break;
+ }
+
+ /* Save */
+ case ITEM_SAVE:
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Hack -- Save the game */
+#ifndef ZANG_AUTO_SAVE
+ do_cmd_save_game();
+#else
+ do_cmd_save_game(FALSE);
+#endif /* !ZANG_AUTO_SAVE */
+
+ break;
+ }
+
+#ifdef HAS_SCORE_MENU
+
+ /* Show score */
+ case ITEM_SCORE:
+ {
+ char buf[1024];
+
+ /* Paranoia */
+ if (!initialized || character_icky ||
+ !game_in_progress || !character_generated)
+ {
+ /* Can't happen but just in case */
+ plog("You may not do that right now.");
+
+ break;
+ }
+
+ /* Build the pathname of the score file */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_APEX,
+ "scores.raw");
+
+ /* Hack - open the score file for reading */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ /* Paranoia - No score file */
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file is not available.");
+
+ break;
+ }
+
+ /* Mega-Hack - prevent various functions XXX XXX XXX */
+ initialized = FALSE;
+
+ /* Save screen */
+ screen_save();
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Prepare scores */
+ if (game_in_progress && character_generated)
+ {
+ predict_score();
+ }
+
+#if 0 /* I don't like this - pelpel */
+
+ /* Mega-Hack - No current player XXX XXX XXX XXX */
+ else
+ {
+ display_scores_aux(0, MAX_HISCORES, -1, NULL);
+ }
+
+#endif
+
+ /* Close the high score file */
+ (void)fd_close(highscore_fd);
+
+ /* Forget the fd */
+ highscore_fd = -1;
+
+ /* Restore screen */
+ screen_load();
+
+ /* Hack - Flush it */
+ Term_fresh();
+
+ /* Mega-Hack - We are ready again */
+ initialized = TRUE;
+
+ /* Done */
+ break;
+ }
+
+#endif /* HAS_SCORE_MENU */
+
+ /* Quit (with save) */
+ case ITEM_QUIT:
+ {
+ /* Save the game (if necessary) */
+ if (game_in_progress && character_generated)
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+#ifndef ZANG_AUTO_SAVE
+ do_cmd_save_game();
+#else
+ do_cmd_save_game(FALSE);
+#endif /* !ZANG_AUTO_SAVE */
+ }
+
+ /* Quit */
+ quit(NULL);
+ break;
+ }
+ }
+ break;
+ }
+
+ /* Edit menu */
+ case MENU_EDIT:
+ {
+ /* Unused */
+ break;
+ }
+
+ /* Font menu */
+ case MENU_FONT:
+ {
+ /* Require a window */
+ if (!td) break;
+
+ /* Memorize old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Toggle the "bold" setting */
+ if (selection == ITEM_BOLD)
+ {
+ /* Toggle the setting */
+ if (td->font_face & bold)
+ {
+ td->font_face &= ~bold;
+ }
+ else
+ {
+ td->font_face |= bold;
+ }
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ break;
+ }
+
+ /* Toggle the "wide" setting */
+ if (selection == ITEM_WIDE)
+ {
+ /* Toggle the setting */
+ if (td->font_face & extend)
+ {
+ td->font_face &= ~extend;
+ }
+ else
+ {
+ td->font_face |= extend;
+ }
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ break;
+ }
+
+ /* Get a new font name */
+ GetMenuItemText(GetMenuHandle(MENU_FONT), selection, s);
+ GetFNum(s, &fid);
+
+ /* Save the new font id */
+ td->font_id = fid;
+
+ /* Current size is bad for new font */
+ if (!RealFont(td->font_id, td->font_size))
+ {
+ /* Find similar size */
+ for (i = 1; i <= 32; i++)
+ {
+ /* Adjust smaller */
+ if (td->font_size - i >= 8)
+ {
+ if (RealFont(td->font_id, td->font_size - i))
+ {
+ td->font_size -= i;
+ break;
+ }
+ }
+
+ /* Adjust larger */
+ if (td->font_size + i <= 128)
+ {
+ if (RealFont(td->font_id, td->font_size + i))
+ {
+ td->font_size += i;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore the window */
+ activate(old_win);
+
+ break;
+ }
+
+ /* Size menu */
+ case MENU_SIZE:
+ {
+ if (!td) break;
+
+ /* Save old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ td->font_size = menu_size_values[selection];
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+
+ /* Window menu */
+ case MENU_WINDOWS:
+ {
+ /* Parse */
+ i = selection - 1;
+
+ /* Check legality of choice */
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ /* Obtain the window */
+ td = &data[i];
+
+ /* Mapped */
+ td->mapped = TRUE;
+
+ /* Link */
+ term_data_link(i);
+
+ /* Mapped (?) */
+ td->t->mapped_flag = TRUE;
+
+ /* Show the window */
+ TransitionWindow(td->w,
+ kWindowZoomTransitionEffect,
+ kWindowShowTransitionAction,
+ NULL);
+
+ /* Bring to the front */
+ SelectWindow(td->w);
+
+ break;
+ }
+
+ /* Special menu */
+ case MENU_SPECIAL:
+ {
+ switch (selection)
+ {
+ case ITEM_SOUND:
+ {
+ /* Toggle arg_sound */
+ arg_sound = !arg_sound;
+
+ /* React to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ break;
+ }
+
+ case ITEM_FIDDLE:
+ {
+ arg_fiddle = !arg_fiddle;
+
+ break;
+ }
+
+ case ITEM_WIZARD:
+ {
+ arg_wizard = !arg_wizard;
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ /* Graphics submenu */
+ case SUBMENU_GRAPH:
+ {
+ switch (selection)
+ {
+ case ITEM_NONE:
+ {
+ graf_mode_req = GRAF_MODE_NONE;
+
+ break;
+ }
+
+ case ITEM_8X8:
+ {
+ graf_mode_req = GRAF_MODE_8X8;
+
+ break;
+ }
+
+ case ITEM_16X16:
+ {
+ graf_mode_req = GRAF_MODE_16X16;
+
+ break;
+ }
+
+ case ITEM_32X32:
+ {
+ graf_mode_req = GRAF_MODE_32X32;
+
+ break;
+ }
+
+#ifdef USE_DOUBLE_TILES
+
+ case ITEM_BIGTILE:
+ {
+ term *old = Term;
+ term_data *td = &data[0];
+
+ /* Toggle "use_bigtile" */
+ use_bigtile = !use_bigtile;
+ arg_bigtile = use_bigtile;
+
+ /* Activate */
+ Term_activate(td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Activate old */
+ Term_activate(old);
+
+ break;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ }
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+
+ /* TileWidth menu */
+ case SUBMENU_TILEWIDTH:
+ {
+ if (!td) break;
+
+ /* Save old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Analyse value */
+ td->tile_wid = menu_tilewidth_values[selection];
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+
+ /* TileHeight menu */
+ case SUBMENU_TILEHEIGHT:
+ {
+ if (!td) break;
+
+ /* Save old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Analyse value */
+ td->tile_hgt = menu_tileheight_values[selection];
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+ }
+
+
+ /* Clean the menu */
+ HiliteMenu(0);
+}
+
+
+/*
+ * Check for extra required parameters -- From "Maarten Hazewinkel"
+ */
+static OSErr CheckRequiredAEParams(const AppleEvent *theAppleEvent)
+{
+ OSErr aeError;
+ DescType returnedType;
+ Size actualSize;
+
+ aeError = AEGetAttributePtr(
+ theAppleEvent, keyMissedKeywordAttr, typeWildCard,
+ &returnedType, NULL, 0, &actualSize);
+
+ if (aeError == errAEDescNotFound) return (noErr);
+
+ if (aeError == noErr) return (errAEParamMissed);
+
+ return (aeError);
+}
+
+
+/*
+ * Apple Event Handler -- Open Application
+ */
+static OSErr AEH_Start(const AppleEvent *theAppleEvent, AppleEvent *reply,
+ SInt32 handlerRefCon)
+{
+ return (CheckRequiredAEParams(theAppleEvent));
+}
+
+
+/*
+ * Apple Event Handler -- Quit Application
+ */
+static OSErr AEH_Quit(const AppleEvent *theAppleEvent, AppleEvent *reply,
+ SInt32 handlerRefCon)
+{
+ /* Quit later */
+ quit_when_ready = TRUE;
+
+ /* Check arguments */
+ return (CheckRequiredAEParams(theAppleEvent));
+}
+
+
+/*
+ * Apple Event Handler -- Print Documents
+ */
+static OSErr AEH_Print(const AppleEvent *theAppleEvent, AppleEvent *reply,
+ SInt32 handlerRefCon)
+{
+ return (errAEEventNotHandled);
+}
+
+
+/*
+ * Apple Event Handler by Steve Linberg (slinberg@crocker.com).
+ *
+ * The old method of opening savefiles from the finder does not work
+ * on the Power Macintosh, because CountAppFiles and GetAppFiles,
+ * used to return information about the selected document files when
+ * an application is launched, are part of the Segment Loader, which
+ * is not present in the RISC OS due to the new memory architecture.
+ *
+ * The "correct" way to do this is with AppleEvents. The following
+ * code is modeled on the "Getting Files Selected from the Finder"
+ * snippet from Think Reference 2.0. (The prior sentence could read
+ * "shamelessly swiped & hacked")
+ */
+static OSErr AEH_Open(const AppleEvent *theAppleEvent, AppleEvent* reply,
+ SInt32 handlerRefCon)
+{
+ FSSpec myFSS;
+ AEDescList docList;
+ OSErr err;
+ Size actualSize;
+ AEKeyword keywd;
+ DescType returnedType;
+ char msg[128];
+ FInfo myFileInfo;
+
+ /* Put the direct parameter (a descriptor list) into a docList */
+ err = AEGetParamDesc(
+ theAppleEvent, keyDirectObject, typeAEList, &docList);
+ if (err) return err;
+
+ /*
+ * We ignore the validity check, because we trust the FInder, and we only
+ * allow one savefile to be opened, so we ignore the depth of the list.
+ */
+ err = AEGetNthPtr(
+ &docList, 1L, typeFSS, &keywd, &returnedType,
+ (Ptr) & myFSS, sizeof(myFSS), &actualSize);
+ if (err) return err;
+
+ /* Only needed to check savefile type below */
+ err = FSpGetFInfo(&myFSS, &myFileInfo);
+ if (err)
+ {
+ strnfmt(msg, 128, "Argh! FSpGetFInfo failed with code %d", err);
+ mac_warning(msg);
+ return err;
+ }
+
+ /* Ignore non 'SAVE' files */
+ if (myFileInfo.fdType != 'SAVE') return noErr;
+
+#ifdef MACH_O_CARBON
+
+ /* Extract a file name */
+ (void)spec_to_path(&myFSS, savefile, sizeof(savefile));
+
+#else
+
+ /* XXX XXX XXX Extract a file name */
+ PathNameFromDirID(myFSS.parID, myFSS.vRefNum, (StringPtr)savefile);
+ pstrcat((StringPtr)savefile, (StringPtr)&myFSS.name);
+
+ /* Convert the string */
+ ptocstr((StringPtr)savefile);
+
+#endif /* MACH_O_CARBON */
+
+ /* Delay actual open */
+ open_when_ready = TRUE;
+
+ /* Dispose */
+ err = AEDisposeDesc(&docList);
+
+ /* Success */
+ return noErr;
+}
+
+
+/*
+ * Handle quit_when_ready, by Peter Ammon,
+ * slightly modified to check inkey_flag.
+ */
+static void quit_calmly(void)
+{
+ /* Quit immediately if game's not started */
+ if (!game_in_progress || !character_generated) quit(NULL);
+
+ /* Save the game and Quit (if it's safe) */
+ if (inkey_flag)
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+#ifndef ZANG_AUTO_SAVE
+ do_cmd_save_game();
+#else
+ do_cmd_save_game(FALSE);
+#endif /* !ZANG_AUTO_SAVE */
+
+ /* Quit */
+ quit(NULL);
+ }
+
+ /* Wait until inkey_flag is set */
+}
+
+
+/*
+ * Macintosh modifiers (event.modifier & ccc):
+ * cmdKey, optionKey, shiftKey, alphaLock, controlKey
+ *
+ *
+ * Macintosh Keycodes (0-63 normal, 64-95 keypad, 96-127 extra):
+ *
+ * Return:36
+ * Delete:51
+ *
+ * Period:65
+ * Star:67
+ * Plus:69
+ * Clear:71
+ * Slash:75
+ * Enter:76
+ * Minus:78
+ * Equal:81
+ * 0-7:82-89
+ * 8-9:91-92
+ *
+ * backslash/vertical bar (Japanese keyboard):93
+ *
+ * F5: 96
+ * F6: 97
+ * F7: 98
+ * F3:99
+ * F8:100
+ * F10:101
+ * F11:103
+ * F13:105
+ * F14:107
+ * F9:109
+ * F12:111
+ * F15:113
+ * Help:114
+ * Home:115
+ * PgUp:116
+ * Del:117
+ * F4: 118
+ * End:119
+ * F2:120
+ * PgDn:121
+ * F1:122
+ * Lt:123
+ * Rt:124
+ * Dn:125
+ * Up:126
+ */
+
+
+/*
+ * Check for Events, return TRUE if we process any
+ *
+ * Now it really waits for events if wait set to true, to prevent
+ * undesirable monopoly of CPU. The side-effect is that you cannot do
+ * while (CheckEvents(TRUE)); without discretion.
+ */
+static bool_ CheckEvents(bool_ wait)
+{
+ EventRecord event;
+
+ WindowPtr w;
+
+ Rect r;
+
+ UInt32 sleep_ticks;
+
+ int ch, ck;
+
+ int mc, ms, mo, mx;
+
+ int i;
+
+ term_data *td = NULL;
+
+
+ /*
+ * With the wait mode blocking for available event / timeout,
+ * the non-wait mode should actually call WaitNextEvent,
+ * because of those event draining loops. Or we had to
+ * implement yet another mode.
+ */
+
+ /* Handles the quit_when_ready flag */
+ if (quit_when_ready) quit_calmly();
+
+ /* Blocking call to WaitNextEvent - should use MAX_INT XXX XXX */
+ if (wait) sleep_ticks = 0x7FFFFFFFL;
+
+ /* Non-blocking */
+ else sleep_ticks = 0L;
+
+ /* Get an event (or null) */
+ WaitNextEvent(everyEvent, &event, sleep_ticks, nil);
+
+ /* Hack -- Nothing is ready yet */
+ if (event.what == nullEvent) return (FALSE);
+
+
+ /* Analyze the event */
+ switch (event.what)
+ {
+
+#if 0
+
+ case activateEvt:
+ {
+ w = (WindowPtr)event.message;
+
+ activate(w);
+
+ break;
+ }
+
+#endif
+
+ case updateEvt:
+ {
+ /* Extract the window */
+ w = (WindowPtr)event.message;
+
+ /* Find the window */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Skip dead windows */
+ if (!data[i].t) continue;
+
+ /* Notice matches */
+ if (data[i].w == w) td = &data[i];
+ }
+
+ /* Hack XXX XXX XXX */
+ BeginUpdate(w);
+ EndUpdate(w);
+
+ /* Redraw the window */
+ if (td) term_data_redraw(td);
+
+ break;
+ }
+
+ case keyDown:
+ case autoKey:
+ {
+ /* Extract some modifiers */
+ mc = (event.modifiers & controlKey) ? TRUE : FALSE;
+ ms = (event.modifiers & shiftKey) ? TRUE : FALSE;
+ mo = (event.modifiers & optionKey) ? TRUE : FALSE;
+ mx = (event.modifiers & cmdKey) ? TRUE : FALSE;
+
+ /* Keypress: (only "valid" if ck < 96) */
+ ch = (event.message & charCodeMask) & 255;
+
+ /* Keycode: see table above */
+ ck = ((event.message & keyCodeMask) >> 8) & 255;
+
+ /* Command + "normal key" -> menu action */
+ if (mx && (ck < 64))
+ {
+#ifdef MENU_SHORTCUTS
+ /* Hack -- Prepare the menus */
+ setup_menus();
+
+ /* Run the Menu-Handler */
+ menu(MenuKey(ch));
+
+ /* Turn off the menus */
+ HiliteMenu(0);
+
+ /* Done */
+ break;
+#else
+ /* Begin special trigger */
+ Term_keypress(31);
+
+ /* Send some modifier keys */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (mo) Term_keypress('O');
+ if (mx) Term_keypress('X');
+
+ /* Enqueue the keypress */
+ Term_keypress(ch);
+
+ /* Terminate the trigger */
+ Term_keypress(13);
+#endif
+ }
+
+ /* Hide the mouse pointer */
+ ObscureCursor();
+
+ /* Normal key -> simple keypress */
+ if ((ck < 64) || (ck == 93))
+ {
+ /* Enqueue the keypress */
+ Term_keypress(ch);
+ }
+
+ /* Keypad keys -> trigger plus simple keypress */
+ else if (!mc && !ms && !mo && !mx && (ck < 96))
+ {
+ /* Hack -- "enter" is confused */
+ if (ck == 76) ch = '\n';
+
+ /* Begin special trigger */
+ Term_keypress(31);
+
+ /* Send the "keypad" modifier */
+ Term_keypress('K');
+
+ /* Send the "ascii" keypress */
+ Term_keypress(ch);
+
+ /* Terminate the trigger */
+ Term_keypress(13);
+ }
+
+ /* Bizarre key -> encoded keypress */
+ else if (ck <= 127)
+ {
+ /* Begin special trigger */
+ Term_keypress(31);
+
+ /* Send some modifier keys */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (mo) Term_keypress('O');
+ if (mx) Term_keypress('X');
+
+ /* Downshift and encode the keycode */
+ Term_keypress(I2D((ck - 64) / 10));
+ Term_keypress(I2D((ck - 64) % 10));
+
+ /* Terminate the trigger */
+ Term_keypress(13);
+ }
+
+ break;
+ }
+
+ case mouseDown:
+ {
+ int code;
+
+ /* Analyze click location */
+ code = FindWindow(event.where, &w);
+
+ /* Find the window */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Skip dead windows */
+ if (!data[i].t) continue;
+
+ /* Notice matches */
+ if (data[i].w == w) td = &data[i];
+ }
+
+ /* Analyze */
+ switch (code)
+ {
+ case inMenuBar:
+ {
+ setup_menus();
+ menu(MenuSelect(event.where));
+ HiliteMenu(0);
+ break;
+ }
+
+ case inDrag:
+ {
+ WindowPtr old_win;
+ BitMap tBitMap;
+ Rect pRect;
+
+ r = GetQDGlobalsScreenBits(&tBitMap)->bounds;
+ r.top += 20; /* GetMBarHeight() XXX XXX XXX */
+ InsetRect(&r, 4, 4);
+ DragWindow(w, event.where, &r);
+
+ /* Oops */
+ if (!td) break;
+
+ /* Save */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Analyze */
+ GetWindowBounds(
+ (WindowRef)td->w,
+ kWindowContentRgn,
+ &pRect);
+ td->r.left = pRect.left;
+ td->r.top = pRect.top;
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+
+ case inGoAway:
+ {
+ /* Oops */
+ if (!td) break;
+
+ /* Track the go-away box */
+ if (TrackGoAway(w, event.where))
+ {
+ /* Not Mapped */
+ td->mapped = FALSE;
+
+ /* Not Mapped */
+ td->t->mapped_flag = FALSE;
+
+ /* Hide the window */
+ TransitionWindow(td->w,
+ kWindowZoomTransitionEffect,
+ kWindowHideTransitionAction,
+ NULL);
+ }
+
+ break;
+ }
+
+ case inGrow:
+ {
+ int x, y;
+
+ Rect nr;
+
+ term *old = Term;
+
+ /* Oops */
+ if (!td) break;
+
+#ifndef ALLOW_BIG_SCREEN
+
+ /* Minimum and maximum sizes */
+ r.left = 20 * td->tile_wid + td->size_ow1;
+ r.right = 80 * td->tile_wid + td->size_ow1 + td->size_ow2 + 1;
+ r.top = 1 * td->tile_hgt + td->size_oh1;
+ r.bottom = 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;
+
+ /* Grow the rectangle */
+ if (!ResizeWindow(w, event.where, &r, NULL)) break;
+#else
+
+ /* Grow the rectangle */
+ if (!ResizeWindow(w, event.where, NULL, NULL)) break;
+
+#endif /* !ALLOW_BIG_SCREEN */
+
+
+ /* Obtain geometry of resized window */
+ GetWindowBounds(w, kWindowContentRgn, &nr);
+
+ /* Extract the new size in pixels */
+ y = nr.bottom - nr.top - td->size_oh1 - td->size_oh2;
+ x = nr.right - nr.left - td->size_ow1 - td->size_ow2;
+
+ /* Extract a "close" approximation */
+ td->rows = y / td->tile_hgt;
+ td->cols = x / td->tile_wid;
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Activate */
+ Term_activate(td->t);
+
+ /* Hack -- Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ Term_activate(old);
+
+ break;
+ }
+
+ case inContent:
+ {
+ SelectWindow(w);
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ /* OS Event -- From "Maarten Hazewinkel" */
+ case osEvt:
+ {
+ switch ((event.message >> 24) & 0x000000FF)
+ {
+ case suspendResumeMessage:
+
+ /* Resuming: activate the front window */
+ if (event.message & resumeFlag)
+ {
+ Cursor tempCursor;
+ SetPort(GetWindowPort(FrontWindow()));
+ SetCursor(GetQDGlobalsArrow(&tempCursor));
+ }
+
+ /* Suspend: deactivate the front window */
+ else
+ {
+ /* Nothing */
+ }
+
+ break;
+ }
+
+ break;
+ }
+
+ /* From "Steve Linberg" and "Maarten Hazewinkel" */
+ case kHighLevelEvent:
+ {
+ /* Process apple events */
+ (void)AEProcessAppleEvent(&event);
+
+ /* Handle "quit_when_ready" */
+ if (quit_when_ready)
+ {
+#if 0 /* Doesn't work with Aqua well */
+ /* Forget */
+ quit_when_ready = FALSE;
+
+ /* Do the menu key */
+ menu(MenuKey('q'));
+#endif
+ /* Turn off the menus */
+ HiliteMenu(0);
+ }
+
+ /* Handle "open_when_ready" */
+ else if (open_when_ready)
+ {
+ handle_open_when_ready();
+ }
+
+ break;
+ }
+
+ }
+
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
+/*** Some Hooks for various routines ***/
+
+
+/*
+ * Mega-Hack -- emergency lifeboat
+ */
+static void *lifeboat = NULL;
+
+
+/*
+ * Hook to "release" memory
+ */
+#ifdef NEW_ZVIRT_HOOKS /* [V] removed the unused 'size' argument. */
+static void *hook_rnfree(void *v)
+#else
+static void *hook_rnfree(void *v, size_t size)
+#endif /* NEW_ZVIRT_HOOKS */
+{
+
+#ifdef USE_MALLOC
+
+ /* Alternative method */
+ free(v);
+
+#else
+
+ /* Dispose */
+ DisposePtr(v);
+
+#endif
+
+ /* Success */
+ return (NULL);
+}
+
+/*
+ * Hook to "allocate" memory
+ */
+static void *hook_ralloc(size_t size)
+{
+
+#ifdef USE_MALLOC
+
+ /* Make a new pointer */
+ return (malloc(size));
+
+#else
+
+ /* Make a new pointer */
+ return (NewPtr(size));
+
+#endif
+
+}
+
+/*
+ * Hook to handle "out of memory" errors
+ */
+static void *hook_rpanic(size_t size)
+{
+ /* void *mem = NULL; */
+
+ /* Free the lifeboat */
+ if (lifeboat)
+ {
+ /* Free the lifeboat */
+ DisposePtr(lifeboat);
+
+ /* Forget the lifeboat */
+ lifeboat = NULL;
+
+ /* Mega-Hack -- Warning */
+ mac_warning("Running out of Memory!\rAbort this process now!");
+
+ /* Mega-Hack -- Never leave this function */
+ while (TRUE) CheckEvents(TRUE);
+ }
+
+ /* Mega-Hack -- Crash */
+ return (NULL);
+}
+
+
+/*
+ * Hook to tell the user something important
+ */
+static void hook_plog(cptr str)
+{
+ /* Warning message */
+ mac_warning(str);
+}
+
+
+/*
+ * Hook to tell the user something, and then quit
+ */
+static void hook_quit(cptr str)
+{
+ /* Warning if needed */
+ if (str) mac_warning(str);
+
+#ifdef USE_ASYNC_SOUND
+
+ /* Clean up sound support */
+ cleanup_sound();
+
+#endif /* USE_ASYNC_SOUND */
+
+ /* Dispose of graphic tiles */
+ if (frameP)
+ {
+ /* Unlock */
+ BenSWUnlockFrame(frameP);
+
+ /* Dispose of the GWorld */
+ DisposeGWorld(frameP->framePort);
+
+ /* Dispose of the memory */
+ DisposePtr((Ptr)frameP);
+ }
+
+ /* Write a preference file */
+ save_pref_file();
+
+ /* All done */
+ ExitToShell();
+}
+
+
+/*
+ * Hook to tell the user something, and then crash
+ */
+static void hook_core(cptr str)
+{
+ /* XXX Use the debugger */
+ /* DebugStr(str); */
+
+ /* Warning */
+ if (str) mac_warning(str);
+
+ /* Warn, then save player */
+ mac_warning("Fatal error.\rI will now attempt to save and quit.");
+
+ /* Attempt to save */
+ if (!save_player()) mac_warning("Warning -- save failed!");
+
+ /* Quit */
+ quit(NULL);
+}
+
+
+
+/*** Main program ***/
+
+
+/*
+ * Init some stuff
+ *
+ * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
+ * "Macintosh Save Bug" by using "absolute" path names, since on
+ * System 7 machines anyway, the "current working directory" often
+ * "changes" due to background processes, invalidating any "relative"
+ * path names. Note that the Macintosh is limited to 255 character
+ * path names, so be careful about deeply embedded directories...
+ *
+ * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
+ * "missing lib folder bug" by allowing the user to help find the
+ * "lib" folder by hand if the "application folder" code fails...
+ *
+ *
+ * The problem description above no longer applies, but I left it here,
+ * modified for Carbon, to allow the game proceeds when a user doesn't
+ * placed the Angband binary and the lib folder in the same place for
+ * whatever reasons. -- pelpel
+ */
+static void init_stuff(void)
+{
+ Rect r;
+ BitMap tBitMap;
+ Rect screenRect;
+ Point topleft;
+
+ char path[1024];
+
+ OSErr err = noErr;
+ NavDialogOptions dialogOptions;
+ FSSpec theFolderSpec;
+ NavReplyRecord theReply;
+
+
+ /* Fake rectangle */
+ r.left = 0;
+ r.top = 0;
+ r.right = 344;
+ r.bottom = 188;
+
+ /* Center it */
+ screenRect = GetQDGlobalsScreenBits(&tBitMap)->bounds;
+ center_rect(&r, &screenRect);
+
+ /* Extract corner */
+ topleft.v = r.top;
+ topleft.h = r.left;
+
+ /* Default to the "lib" folder with the application */
+#ifdef MACH_O_CARBON
+ if (locate_lib(path, sizeof(path)) == NULL) quit(NULL);
+#else
+ refnum_to_name(path, app_dir, app_vol, (char*)("\plib:"));
+#endif
+
+
+ /* Check until done */
+ while (1)
+ {
+ /* Prepare the paths */
+ init_file_paths(path);
+
+ /* Build the filename */
+ path_build(path, 1024, ANGBAND_DIR_FILE, "news.txt");
+
+ /* Attempt to open and close that file */
+ if (0 == fd_close(fd_open(path, O_RDONLY))) break;
+
+ /* Warning */
+ plog_fmt("Unable to open the '%s' file.", path);
+
+ /* Warning */
+ plog("The Angband 'lib' folder is probably missing or misplaced.");
+
+ /* Ask the user to choose the lib folder */
+ err = NavGetDefaultDialogOptions(&dialogOptions);
+
+ /* Paranoia */
+ if (err != noErr) quit(NULL);
+
+ /* Set default location option */
+ dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
+
+ /* Clear preview option */
+ dialogOptions.dialogOptionFlags &= ~(kNavAllowPreviews);
+
+ /* Forbit selection of multiple files */
+ dialogOptions.dialogOptionFlags &= ~(kNavAllowMultipleFiles);
+
+ /* Display location */
+ dialogOptions.location = topleft;
+
+#if 0
+
+ /* Load the message for the missing folder from the resource fork */
+ /* GetIndString(dialogOptions.message, 128, 1); */
+
+#else
+
+ /* Set the message for the missing folder XXX XXX */
+ strcpy(dialogOptions.message + 1, "Please select the \"lib\" folder");
+ dialogOptions.message[0] = strlen(dialogOptions.message + 1);
+
+#endif
+
+ /* Wait for the user to choose a folder */
+ err = NavChooseFolder(
+ nil, &theReply, &dialogOptions, nil, nil, nil);
+
+ /* Assume the player doesn't want to go on */
+ if ((err != noErr) || !theReply.validRecord) quit(NULL);
+
+ /* Retrieve FSSpec from the reply */
+ {
+ AEKeyword theKeyword;
+ DescType actualType;
+ Size actualSize;
+
+ /* Get a pointer to selected folder */
+ err = AEGetNthPtr(
+ &(theReply.selection), 1, typeFSS, &theKeyword,
+ &actualType, &theFolderSpec, sizeof(FSSpec), &actualSize);
+
+ /* Paranoia */
+ if (err != noErr) quit(NULL);
+ }
+
+ /* Free navitagor reply */
+ err = NavDisposeReply(&theReply);
+
+ /* Paranoia */
+ if (err != noErr) quit(NULL);
+
+ /* Extract textual file name for given file */
+#ifdef MACH_O_CARBON
+ if (spec_to_path(&theFolderSpec, path, sizeof(path)) != noErr)
+ {
+ quit(NULL);
+ }
+#else /* MACH_O_CARBON */
+refnum_to_name(
+ path,
+ theFolderSpec.parID,
+ theFolderSpec.vRefNum,
+ (char *)theFolderSpec.name);
+#endif /* MACH_O_CARBON */
+ }
+}
+
+
+/*
+ * Macintosh Main loop
+ */
+int main(void)
+{
+ int i;
+ long response;
+ OSStatus err;
+ EventRecord tempEvent;
+ UInt32 numberOfMasters = 10;
+
+ /* Get more Masters -- it is not recommended by Apple, should go away */
+ MoreMasterPointers(numberOfMasters);
+
+ /* Check for existence of Carbon */
+ err = Gestalt(gestaltCarbonVersion, &response);
+
+ if (err != noErr) quit("This program requires Carbon API");
+
+ /* See if we are running on Aqua */
+ err = Gestalt(gestaltMenuMgrAttr, &response);
+
+ /* Cache the result */
+ if ((err == noErr) &&
+ (response & gestaltMenuMgrAquaLayoutMask)) is_aqua = TRUE;
+
+ /*
+ * Remember Mac OS version, in case we have to cope with version-specific
+ * problems
+ */
+ (void)Gestalt(gestaltSystemVersion, &mac_os_version);
+
+
+ /* Set up the Macintosh */
+ InitCursor();
+
+ /* Flush events */
+ FlushEvents(everyEvent, 0);
+
+ /* Flush events some more (?) */
+ if (EventAvail(everyEvent, &tempEvent)) FlushEvents(everyEvent, 0);
+
+
+ /* Install the start event hook (ignore error codes) */
+ AEInstallEventHandler(
+ kCoreEventClass,
+ kAEOpenApplication,
+ NewAEEventHandlerUPP(AEH_Start),
+ 0L,
+ FALSE);
+
+ /* Install the quit event hook (ignore error codes) */
+ AEInstallEventHandler(
+ kCoreEventClass,
+ kAEQuitApplication,
+ NewAEEventHandlerUPP(AEH_Quit),
+ 0L,
+ FALSE);
+
+ /* Install the print event hook (ignore error codes) */
+ AEInstallEventHandler(
+ kCoreEventClass,
+ kAEPrintDocuments,
+ NewAEEventHandlerUPP(AEH_Print),
+ 0L,
+ FALSE);
+
+ /* Install the open event hook (ignore error codes) */
+ AEInstallEventHandler(
+ kCoreEventClass,
+ kAEOpenDocuments,
+ NewAEEventHandlerUPP(AEH_Open),
+ 0L,
+ FALSE);
+
+
+#ifndef MACH_O_CARBON
+
+ /* Find the current application */
+ SetupAppDir();
+
+#endif /* !MACH_O_CARBON */
+
+ /* Mark ourself as the file creator */
+ _fcreator = ANGBAND_CREATOR;
+
+ /* Default to saving a "text" file */
+ _ftype = 'TEXT';
+
+
+ /* Hook in some "z-virt.c" hooks */
+ rnfree_aux = hook_rnfree;
+ ralloc_aux = hook_ralloc;
+ rpanic_aux = hook_rpanic;
+
+ /* Hooks in some "z-util.c" hooks */
+ plog_aux = hook_plog;
+ quit_aux = hook_quit;
+ core_aux = hook_core;
+
+
+ /* Initialize colors */
+ for (i = 0; i < 256; i++)
+ {
+ u16b rv, gv, bv;
+
+ /* Extract the R,G,B data */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Save the actual color */
+ color_info[i].red = (rv | (rv << 8));
+ color_info[i].green = (gv | (gv << 8));
+ color_info[i].blue = (bv | (bv << 8));
+ }
+
+
+ /* Show the "watch" cursor */
+ SetCursor(*(GetCursor(watchCursor)));
+
+ /* Prepare the menubar */
+ init_menubar();
+
+ /* Prepare the windows */
+ init_windows();
+
+ /* Hack -- process all events */
+ while (CheckEvents(FALSE)) /* loop */;
+
+ /* Reset the cursor */
+ {
+ Cursor tempCursor;
+
+ SetCursor(GetQDGlobalsArrow(&tempCursor));
+ }
+
+ /* Mega-Hack -- Allocate a "lifeboat" */
+ lifeboat = NewPtr(16384);
+
+#ifdef USE_QT_SOUND
+
+ /* Load sound effect resources */
+ load_sounds();
+
+#endif /* USE_QT_SOUND */
+
+ /* Note the "system" */
+ ANGBAND_SYS = "mac";
+
+ if (check_create_user_dir() == FALSE)
+ quit("Cannot create directory " PRIVATE_USER_PATH);
+
+ /* Initialize */
+ init_stuff();
+
+ /* Initialize */
+ init_angband();
+
+
+ /* Hack -- process all events */
+ while (CheckEvents(FALSE)) /* loop */;
+
+
+ /* We are now initialized */
+ initialized = TRUE;
+
+
+ /* Handle "open_when_ready" */
+ handle_open_when_ready();
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Prompt the user - You may have to change this for some variants */
+ prt("[Choose 'New' or 'Open' from the 'File' menu]", 23, 15);
+
+ /* Flush the prompt */
+ Term_fresh();
+
+ /* Hack -- Process Events Forever */
+ while (TRUE) CheckEvents(TRUE);
+
+#else
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Wait for keypress */
+ pause_line(23);
+
+ /* flush input - Warning: without this, _system_ would hang */
+ flush();
+
+ /* Play the game - note the value of the argument */
+ play_game(FALSE);
+
+ /* Quit */
+ quit(NULL);
+
+ /* Since it's a int function */
+ return (0);
+#endif /* !SAVEFILE_SCREEN */
+}
+
+#endif /* MACINTOSH || MACH_O_CARBON */
+
diff --git a/src/main-gcu.c b/src/main-gcu.c
new file mode 100644
index 00000000..57c41703
--- /dev/null
+++ b/src/main-gcu.c
@@ -0,0 +1,1222 @@
+/* File: main-gcu.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband run on Unix/Curses machines.
+ *
+ *
+ * To use this file, you must define "USE_GCU" in the Makefile.
+ *
+ *
+ * Note that this file is not "intended" to support non-Unix machines,
+ * nor is it intended to support VMS or other bizarre setups.
+ *
+ * Also, this package assumes that the underlying "curses" handles both
+ * the "nonl()" and "cbreak()" commands correctly, see the "OPTION" below.
+ *
+ * This code should work with most versions of "curses" or "ncurses",
+ * and the "main-ncu.c" file (and USE_NCU define) are no longer used.
+ *
+ * This file provides up to 4 term windows.
+ *
+ * This file will attempt to redefine the screen colors to conform to
+ * standard Angband colors. It will only do so if the terminal type
+ * indicates that it can do so. See the page:
+ *
+ * http://www.umr.edu/~keldon/ang-patch/ncurses_color.html
+ *
+ * for information on this.
+ *
+ * Consider the use of "savetty()" and "resetty()". XXX XXX XXX
+ */
+
+
+#include "angband.h"
+
+
+#ifdef USE_GCU
+
+#include <limits.h>
+
+/*
+ * Hack -- play games with "bool" and "term"
+ */
+#undef bool
+
+/* Avoid 'struct term' name conflict with <curses.h> (via <term.h>) on AIX */
+#define term System_term
+
+/*
+ * Include the proper "header" file
+ */
+#ifdef USE_NCURSES
+# include <ncurses.h>
+#else
+# include <curses.h>
+#endif
+
+#undef term
+
+/*
+ * Try redefining the colors at startup.
+ */
+#define REDEFINE_COLORS
+
+
+/*
+ * Hack -- try to guess which systems use what commands
+ * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set.
+ * Mega-Hack -- try to guess when "POSIX" is available.
+ * If the user defines two of these, we will probably crash.
+ */
+#if !defined(USE_TPOSIX)
+# if !defined(USE_TERMIO) && !defined(USE_TCHARS)
+# if defined(_POSIX_VERSION)
+# define USE_TPOSIX
+# else
+# if defined(USG) || defined(linux) || defined(SOLARIS)
+# define USE_TERMIO
+# else
+# define USE_TCHARS
+# endif
+# endif
+# endif
+#endif
+
+/*
+ * POSIX stuff
+ */
+#ifdef USE_TPOSIX
+# include <sys/ioctl.h>
+# include <termios.h>
+#endif
+
+/*
+ * One version needs these files
+ */
+#ifdef USE_TERMIO
+# include <sys/ioctl.h>
+# include <termio.h>
+#endif
+
+/*
+ * The other needs these files
+ */
+#ifdef USE_TCHARS
+# include <sys/ioctl.h>
+# include <sys/resource.h>
+# include <sys/param.h>
+# include <sys/file.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+
+
+/*
+ * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
+ *
+ * They should both work due to the "(i != 1)" test below.
+ */
+#ifndef O_NDELAY
+# define O_NDELAY O_NONBLOCK
+#endif
+
+
+/*
+ * OPTION: some machines lack "cbreak()"
+ * On these machines, we use an older definition
+ */
+/* #define cbreak() crmode() */
+
+
+/*
+ * OPTION: some machines cannot handle "nonl()" and "nl()"
+ * On these machines, we can simply ignore those commands.
+ */
+/* #define nonl() */
+/* #define nl() */
+
+
+/*
+ * Save the "normal" and "angband" terminal settings
+ */
+
+#ifdef USE_TPOSIX
+
+static struct termios norm_termios;
+
+static struct termios game_termios;
+
+#endif
+
+#ifdef USE_TERMIO
+
+static struct termio norm_termio;
+
+static struct termio game_termio;
+
+#endif
+
+#ifdef USE_TCHARS
+
+static struct ltchars norm_special_chars;
+static struct sgttyb norm_ttyb;
+static struct tchars norm_tchars;
+static int norm_local_chars;
+
+static struct ltchars game_special_chars;
+static struct sgttyb game_ttyb;
+static struct tchars game_tchars;
+static int game_local_chars;
+
+#endif
+
+/*
+ * Information about a term
+ */
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t; /* All term info */
+
+ WINDOW *win; /* Pointer to the curses window */
+};
+
+/* Max number of windows on screen */
+#define MAX_TERM_DATA 4
+
+/* Information about our windows */
+static term_data data[MAX_TERM_DATA];
+
+
+/*
+ * Hack -- Number of initialized "term" structures
+ */
+static int active = 0;
+
+
+#ifdef A_COLOR
+
+/*
+ * Hack -- define "A_BRIGHT" to be "A_BOLD", because on many
+ * machines, "A_BRIGHT" produces ugly "inverse" video.
+ */
+#ifndef A_BRIGHT
+# define A_BRIGHT A_BOLD
+#endif
+
+/*
+ * Software flag -- we are allowed to use color
+ */
+static int can_use_color = FALSE;
+
+/*
+ * Software flag -- we are allowed to change the colors
+ */
+static int can_fix_color = FALSE;
+
+/*
+ * Simple Angband to Curses color conversion table
+ */
+static int colortable[16];
+
+#endif
+
+
+
+/*
+ * Place the "keymap" into its "normal" state
+ */
+static void keymap_norm(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* restore the saved values of the special chars */
+ (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TCSETA, (char *)&norm_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TIOCSLTC, (char *)&norm_special_chars);
+ (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
+ (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
+ (void)ioctl(0, TIOCLSET, (char *)&norm_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Place the "keymap" into the "game" state
+ */
+static void keymap_game(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* restore the saved values of the special chars */
+ (void)tcsetattr(0, TCSAFLUSH, &game_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TCSETA, (char *)&game_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TIOCSLTC, (char *)&game_special_chars);
+ (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
+ (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
+ (void)ioctl(0, TIOCLSET, (char *)&game_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Save the normal keymap
+ */
+static void keymap_norm_prepare(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* Get the normal keymap */
+ tcgetattr(0, &norm_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* Get the normal keymap */
+ (void)ioctl(0, TCGETA, (char *)&norm_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* Get the normal keymap */
+ (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
+ (void)ioctl(0, TIOCGLTC, (char *)&norm_special_chars);
+ (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
+ (void)ioctl(0, TIOCLGET, (char *)&norm_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Save the keymaps (normal and game)
+ */
+static void keymap_game_prepare(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* Acquire the current mapping */
+ tcgetattr(0, &game_termios);
+
+ /* Force "Ctrl-C" to interupt */
+ game_termios.c_cc[VINTR] = (char)3;
+
+ /* Force "Ctrl-Z" to suspend */
+ game_termios.c_cc[VSUSP] = (char)26;
+
+ /* Hack -- Leave "VSTART/VSTOP" alone */
+
+ /* Disable the standard control characters */
+ game_termios.c_cc[VQUIT] = (char) - 1;
+ game_termios.c_cc[VERASE] = (char) - 1;
+ game_termios.c_cc[VKILL] = (char) - 1;
+ game_termios.c_cc[VEOF] = (char) - 1;
+ game_termios.c_cc[VEOL] = (char) - 1;
+
+ /* Normally, block until a character is read */
+ game_termios.c_cc[VMIN] = 1;
+ game_termios.c_cc[VTIME] = 0;
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* Acquire the current mapping */
+ (void)ioctl(0, TCGETA, (char *)&game_termio);
+
+ /* Force "Ctrl-C" to interupt */
+ game_termio.c_cc[VINTR] = (char)3;
+
+ /* Force "Ctrl-Z" to suspend */
+ game_termio.c_cc[VSUSP] = (char)26;
+
+ /* Hack -- Leave "VSTART/VSTOP" alone */
+
+ /* Disable the standard control characters */
+ game_termio.c_cc[VQUIT] = (char) - 1;
+ game_termio.c_cc[VERASE] = (char) - 1;
+ game_termio.c_cc[VKILL] = (char) - 1;
+ game_termio.c_cc[VEOF] = (char) - 1;
+ game_termio.c_cc[VEOL] = (char) - 1;
+
+ /* Normally, block until a character is read */
+ game_termio.c_cc[VMIN] = 1;
+ game_termio.c_cc[VTIME] = 0;
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* Get the default game characters */
+ (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
+ (void)ioctl(0, TIOCGLTC, (char *)&game_special_chars);
+ (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
+ (void)ioctl(0, TIOCLGET, (char *)&game_local_chars);
+
+ /* Force suspend (^Z) */
+ game_special_chars.t_suspc = (char)26;
+
+ /* Cancel some things */
+ game_special_chars.t_dsuspc = (char) - 1;
+ game_special_chars.t_rprntc = (char) - 1;
+ game_special_chars.t_flushc = (char) - 1;
+ game_special_chars.t_werasc = (char) - 1;
+ game_special_chars.t_lnextc = (char) - 1;
+
+ /* Force interupt (^C) */
+ game_tchars.t_intrc = (char)3;
+
+ /* Force start/stop (^Q, ^S) */
+ game_tchars.t_startc = (char)17;
+ game_tchars.t_stopc = (char)19;
+
+ /* Cancel some things */
+ game_tchars.t_quitc = (char) - 1;
+ game_tchars.t_eofc = (char) - 1;
+ game_tchars.t_brkc = (char) - 1;
+
+#endif
+
+}
+
+
+
+
+/*
+ * Suspend/Resume
+ */
+static errr Term_xtra_gcu_alive(int v)
+{
+ int x, y;
+
+
+ /* Suspend */
+ if (!v)
+ {
+ /* Go to normal keymap mode */
+ keymap_norm();
+
+ /* Restore modes */
+ nocbreak();
+ echo();
+ nl();
+
+ /* Hack -- make sure the cursor is visible */
+ Term_xtra(TERM_XTRA_SHAPE, 1);
+
+ /* Flush the curses buffer */
+ (void)refresh();
+
+ /* Get current cursor position */
+ getyx(curscr, y, x);
+
+ /* Move the cursor to bottom right corner */
+ mvcur(y, x, LINES - 1, 0);
+
+ /* Exit curses */
+ endwin();
+
+ /* Flush the output */
+ (void)fflush(stdout);
+ }
+
+ /* Resume */
+ else
+ {
+ /* Refresh */
+ /* (void)touchwin(curscr); */
+ /* (void)wrefresh(curscr); */
+
+ /* Restore the settings */
+ cbreak();
+ noecho();
+ nonl();
+
+ /* Go to angband keymap mode */
+ keymap_game();
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Init the "curses" system
+ */
+static void Term_init_gcu(term *t)
+{
+ term_data *td = (term_data *)(t->data);
+
+ /* Count init's, handle first */
+ if (active++ != 0) return;
+
+ /* Erase the window */
+ (void)wclear(td->win);
+
+ /* Reset the cursor */
+ (void)wmove(td->win, 0, 0);
+
+ /* Flush changes */
+ (void)wrefresh(td->win);
+
+ /* Game keymap */
+ keymap_game();
+}
+
+
+/*
+ * Nuke the "curses" system
+ */
+static void Term_nuke_gcu(term *t)
+{
+ int x, y;
+ term_data *td = (term_data *)(t->data);
+
+ /* Delete this window */
+ delwin(td->win);
+
+ /* Count nuke's, handle last */
+ if (--active != 0) return;
+
+ /* Hack -- make sure the cursor is visible */
+ Term_xtra(TERM_XTRA_SHAPE, 1);
+
+#ifdef A_COLOR
+ /* Reset colors to defaults */
+ start_color();
+#endif
+
+ /* Get current cursor position */
+ getyx(curscr, y, x);
+
+ /* Move the cursor to bottom right corner */
+ mvcur(y, x, LINES - 1, 0);
+
+ /* Flush the curses buffer */
+ (void)refresh();
+
+ /* Exit curses */
+ endwin();
+
+ /* Flush the output */
+ (void)fflush(stdout);
+
+ /* Normal keymap */
+ keymap_norm();
+}
+
+
+
+
+#ifdef USE_GETCH
+
+/*
+ * Process events, with optional wait
+ */
+static errr Term_xtra_gcu_event(int v)
+{
+ int i, k;
+
+ /* Wait */
+ if (v)
+ {
+ /* Paranoia -- Wait for it */
+ nodelay(stdscr, FALSE);
+
+ /* Get a keypress */
+ i = getch();
+
+ /* Mega-Hack -- allow graceful "suspend" */
+ for (k = 0; (k < 10) && (i == ERR); k++) i = getch();
+
+ /* Broken input is special */
+ if (i == ERR) abort();
+ if (i == EOF) abort();
+ }
+
+ /* Do not wait */
+ else
+ {
+ /* Do not wait for it */
+ nodelay(stdscr, TRUE);
+
+ /* Check for keypresses */
+ i = getch();
+
+ /* Wait for it next time */
+ nodelay(stdscr, FALSE);
+
+ /* None ready */
+ if (i == ERR) return (1);
+ if (i == EOF) return (1);
+ }
+
+ /* Enqueue the keypress */
+ Term_keypress(i);
+
+ /* Success */
+ return (0);
+}
+
+#else /* USE_GETCH */
+
+/*
+* Process events (with optional wait)
+*/
+static errr Term_xtra_gcu_event(int v)
+{
+ int i, k;
+
+ char buf[2];
+
+ /* Wait */
+ if (v)
+ {
+ /* Wait for one byte */
+ i = read(0, buf, 1);
+
+ /* Hack -- Handle bizarre "errors" */
+ if ((i <= 0) && (errno != EINTR)) abort();
+ }
+
+ /* Do not wait */
+ else
+ {
+ /* Get the current flags for stdin */
+ k = fcntl(0, F_GETFL, 0);
+
+ /* Oops */
+ if (k < 0) return (1);
+
+ /* Tell stdin not to block */
+ if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) return (1);
+
+ /* Read one byte, if possible */
+ i = read(0, buf, 1);
+
+ /* Replace the flags for stdin */
+ if (fcntl(0, F_SETFL, k)) return (1);
+ }
+
+ /* Ignore "invalid" keys */
+ if ((i != 1) || (!buf[0])) return (1);
+
+ /* Enqueue the keypress */
+ Term_keypress(buf[0]);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GETCH */
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_gcu_react(void)
+{
+
+#ifdef A_COLOR
+
+ int i;
+
+ /* Cannot handle color redefinition */
+ if (!can_fix_color) return (0);
+
+ /* Set the colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Set one color (note scaling) */
+ init_color(i,
+ angband_color_table[i][1] * 1000 / 255,
+ angband_color_table[i][2] * 1000 / 255,
+ angband_color_table[i][3] * 1000 / 255);
+ }
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_gcu(int n, int v)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ /* Analyze the request */
+ switch (n)
+ {
+ /* Clear screen */
+ case TERM_XTRA_CLEAR:
+ touchwin(td->win);
+ (void)wclear(td->win);
+ return (0);
+
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ (void)write(1, "\007", 1);
+ return (0);
+
+ /* Flush the Curses buffer */
+ case TERM_XTRA_FRESH:
+ (void)wrefresh(td->win);
+ return (0);
+
+#ifdef USE_CURS_SET
+
+ /* Change the cursor visibility */
+ case TERM_XTRA_SHAPE:
+ curs_set(v);
+ return (0);
+
+#endif
+
+ /* Suspend/Resume curses */
+ case TERM_XTRA_ALIVE:
+ return (Term_xtra_gcu_alive(v));
+
+ /* Process events */
+ case TERM_XTRA_EVENT:
+ return (Term_xtra_gcu_event(v));
+
+ /* Flush events */
+ case TERM_XTRA_FLUSH:
+ while (!Term_xtra_gcu_event(FALSE));
+ return (0);
+
+ /* Delay */
+ case TERM_XTRA_DELAY:
+ usleep(1000 * v);
+ return (0);
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while ((entry = readdir(directory)))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+ }
+
+ /* React to events */
+ case TERM_XTRA_REACT:
+ Term_xtra_gcu_react();
+ return (0);
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+/*
+ * Actually MOVE the hardware cursor
+ */
+static errr Term_curs_gcu(int x, int y)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ /* Literally move the cursor */
+ wmove(td->win, y, x);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase a grid of space
+ * Hack -- try to be "semi-efficient".
+ */
+static errr Term_wipe_gcu(int x, int y, int n)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ /* Place cursor */
+ wmove(td->win, y, x);
+
+ /* Clear to end of line */
+ if (x + n >= td->t.wid)
+ {
+ wclrtoeol(td->win);
+ }
+
+ /* Clear some characters */
+ else
+ {
+ while (n-- > 0) waddch(td->win, ' ');
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Place some text on the screen using an attribute
+ */
+static errr Term_text_gcu(int x, int y, int n, byte a, cptr s)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ int i, pic;
+
+#ifdef A_COLOR
+ /* Set the color */
+ if (can_use_color) wattrset(td->win, colortable[a & 0x0F]);
+#endif
+
+ /* Move the cursor */
+ wmove(td->win, y, x);
+
+ /* Draw each character */
+ for (i = 0; i < n; i++)
+ {
+#ifdef USE_GRAPHICS
+ /* Special character */
+ if (use_graphics && (s[i] & 0x80))
+ {
+ /* Determine picture to use */
+ switch (s[i] & 0x7F)
+ {
+
+#ifdef ACS_CKBOARD
+ /* Wall */
+ case '#':
+ pic = ACS_CKBOARD;
+ break;
+#endif /* ACS_CKBOARD */
+
+#ifdef ACS_BOARD
+ /* Mineral vein */
+ case '%':
+ pic = ACS_BOARD;
+ break;
+#endif /* ACS_BOARD */
+
+ /* XXX */
+ default:
+ pic = '?';
+ break;
+ }
+
+ /* Draw the picture */
+ waddch(td->win, pic);
+
+ /* Next character */
+ continue;
+ }
+#endif
+
+ /* Draw a normal character */
+ waddch(td->win, (byte)s[i]);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Create a window for the given "term_data" argument.
+ *
+ * Assumes legal arguments.
+ */
+static errr term_data_init_gcu(term_data *td, int rows, int cols, int y, int x)
+{
+ term *t = &td->t;
+
+ /* Create new window */
+ td->win = newwin(rows, cols, y, x);
+
+ /* Check for failure */
+ if (!td->win)
+ {
+ /* Error */
+ quit("Failed to setup curses window.");
+ }
+
+ /* Initialize the term */
+ term_init(t, cols, rows, 256);
+
+ /* Avoid bottom right corner */
+ t->icky_corner = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Set some hooks */
+ t->init_hook = Term_init_gcu;
+ t->nuke_hook = Term_nuke_gcu;
+
+ /* Set some more hooks */
+ t->text_hook = Term_text_gcu;
+ t->wipe_hook = Term_wipe_gcu;
+ t->curs_hook = Term_curs_gcu;
+ t->xtra_hook = Term_xtra_gcu;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate it */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+static void hook_quit(cptr str)
+{
+ /* Unused */
+ (void)str;
+
+ /* Exit curses */
+ endwin();
+}
+
+
+/*
+ * Prepare "curses" for use by the file "z-term.c"
+ *
+ * Installs the "hook" functions defined above, and then activates
+ * the main screen "term", which clears the screen and such things.
+ *
+ * Someone should really check the semantics of "initscr()"
+ */
+errr init_gcu(int argc, char **argv)
+{
+ int i;
+
+ int num_term = MAX_TERM_DATA, next_win = 0;
+
+ bool_ use_big_screen = FALSE;
+
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ if (prefix(argv[i], "-b"))
+ {
+ use_big_screen = TRUE;
+ continue;
+ }
+
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+
+ /* Extract the normal keymap */
+ keymap_norm_prepare();
+
+
+#if defined(USG)
+ /* Initialize for USG Unix */
+ if (initscr() == NULL) return ( -1);
+#else
+/* Initialize for other systems */
+ if (initscr() == (WINDOW*)ERR) return ( -1);
+#endif
+
+ /* Activate hooks */
+ quit_aux = hook_quit;
+ core_aux = hook_quit;
+
+ /* Require standard size screen */
+ if ((LINES < 24) || (COLS < 80))
+ {
+ quit("Angband needs at least an 80x24 'curses' screen");
+ }
+
+
+#ifdef USE_GRAPHICS
+
+ /* Set graphics flag */
+ use_graphics = arg_graphics;
+
+#endif
+
+#ifdef A_COLOR
+
+ /*** Init the Color-pairs and set up a translation table ***/
+
+ /* Do we have color, and enough color, available? */
+ can_use_color = ((start_color() != ERR) && has_colors() &&
+ (COLORS >= 8) && (COLOR_PAIRS >= 8));
+
+#ifdef REDEFINE_COLORS
+
+ /* Can we change colors? */
+ can_fix_color = (can_use_color && can_change_color() &&
+ (COLORS >= 16) && (COLOR_PAIRS > 8));
+
+#endif
+
+ /* Attempt to use customized colors */
+ if (can_fix_color)
+ {
+ /* Prepare the color pairs */
+ for (i = 1; i <= 8; i++)
+ {
+ /* Reset the color */
+ if (init_pair(i, i - 1, 0) == ERR)
+ {
+ quit("Color pair init failed");
+ }
+
+ /* Set up the colormap */
+ colortable[i - 1] = (COLOR_PAIR(i) | A_NORMAL);
+ colortable[i + 7] = (COLOR_PAIR(i) | A_BRIGHT);
+ }
+
+ /* Take account of "gamma correction" XXX XXX XXX */
+
+ /* Prepare the "Angband Colors" */
+ Term_xtra_gcu_react();
+ }
+
+ /* Attempt to use colors */
+ else if (can_use_color)
+ {
+ /* Color-pair 0 is *always* WHITE on BLACK */
+
+ /* Prepare the color pairs */
+ init_pair(1, COLOR_RED, COLOR_BLACK);
+ init_pair(2, COLOR_GREEN, COLOR_BLACK);
+ init_pair(3, COLOR_YELLOW, COLOR_BLACK);
+ init_pair(4, COLOR_BLUE, COLOR_BLACK);
+ init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
+ init_pair(6, COLOR_CYAN, COLOR_BLACK);
+ init_pair(7, COLOR_BLACK, COLOR_BLACK);
+
+ /* Prepare the "Angband Colors" -- Bright white is too bright */
+ colortable[0] = (COLOR_PAIR(7) | A_NORMAL); /* Black */
+ colortable[1] = (COLOR_PAIR(0) | A_NORMAL); /* White */
+ colortable[2] = (COLOR_PAIR(6) | A_NORMAL); /* Grey XXX */
+ colortable[3] = (COLOR_PAIR(1) | A_BRIGHT); /* Orange XXX */
+ colortable[4] = (COLOR_PAIR(1) | A_NORMAL); /* Red */
+ colortable[5] = (COLOR_PAIR(2) | A_NORMAL); /* Green */
+ colortable[6] = (COLOR_PAIR(4) | A_NORMAL); /* Blue */
+ colortable[7] = (COLOR_PAIR(3) | A_NORMAL); /* Umber */
+ colortable[8] = (COLOR_PAIR(7) | A_BRIGHT); /* Dark-grey XXX */
+ colortable[9] = (COLOR_PAIR(6) | A_BRIGHT); /* Light-grey XXX */
+ colortable[10] = (COLOR_PAIR(5) | A_NORMAL); /* Purple */
+ colortable[11] = (COLOR_PAIR(3) | A_BRIGHT); /* Yellow */
+ colortable[12] = (COLOR_PAIR(5) | A_BRIGHT); /* Light Red XXX */
+ colortable[13] = (COLOR_PAIR(2) | A_BRIGHT); /* Light Green */
+ colortable[14] = (COLOR_PAIR(4) | A_BRIGHT); /* Light Blue */
+ colortable[15] = (COLOR_PAIR(3) | A_NORMAL); /* Light Umber XXX */
+ }
+
+#endif
+
+
+ /*** Low level preparation ***/
+
+#ifdef USE_GETCH
+
+ /* Paranoia -- Assume no waiting */
+ nodelay(stdscr, FALSE);
+
+#endif
+
+ /* Prepare */
+ cbreak();
+ noecho();
+ nonl();
+
+ /* Extract the game keymap */
+ keymap_game_prepare();
+
+
+ /*** Now prepare the term(s) ***/
+
+ /* Big screen -- one big term */
+ if (use_big_screen)
+ {
+ /* Create a term */
+ term_data_init_gcu(&data[0], LINES, COLS, 0, 0);
+
+ /* Remember the term */
+ angband_term[0] = &data[0].t;
+ }
+
+ /* No big screen -- create as many term windows as possible */
+ else
+ {
+ /* Create several terms */
+ for (i = 0; i < num_term; i++)
+ {
+ int rows, cols, y, x;
+
+ /* Decide on size and position */
+ switch (i)
+ {
+ /* Upper left */
+ case 0:
+ {
+ rows = 24;
+ cols = 80;
+ y = x = 0;
+ break;
+ }
+
+ /* Lower left */
+ case 1:
+ {
+ rows = LINES - 25;
+ cols = 80;
+ y = 25;
+ x = 0;
+ break;
+ }
+
+ /* Upper right */
+ case 2:
+ {
+ rows = 24;
+ cols = COLS - 81;
+ y = 0;
+ x = 81;
+ break;
+ }
+
+ /* Lower right */
+ case 3:
+ {
+ rows = LINES - 25;
+ cols = COLS - 81;
+ y = 25;
+ x = 81;
+ break;
+ }
+
+ /* XXX */
+ default:
+ {
+ rows = cols = y = x = 0;
+ break;
+ }
+ }
+
+ /* Skip non-existant windows */
+ if (rows <= 0 || cols <= 0) continue;
+
+ /* Create a term */
+ term_data_init_gcu(&data[next_win], rows, cols, y, x);
+
+ /* Remember the term */
+ angband_term[next_win] = &data[next_win].t;
+
+ /* One more window */
+ next_win++;
+ }
+ }
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+ /* Remember the active screen */
+ term_screen = &data[0].t;
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* USE_GCU */
+
+
diff --git a/src/main-gtk2.c b/src/main-gtk2.c
new file mode 100644
index 00000000..4830638a
--- /dev/null
+++ b/src/main-gtk2.c
@@ -0,0 +1,5026 @@
+/* File: main-gtk.c */
+
+/*
+ * Copyright (c) 2000-2001 Robert Ruehlmann,
+ * Steven Fuerst, Uwe Siems, "pelpel", et al.
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/*
+ * Robert Ruehlmann wrote the original Gtk port. Since an initial work is
+ * much harder than enhancements, his effort worth more credits than
+ * others.
+ *
+ * Steven Fuerst implemented colour-depth independent X server support,
+ * graphics, resizing and big screen support for ZAngband as well as
+ * fast image rescaling that is included here.
+ *
+ * Uwe Siems wrote smooth tiles rescaling code (on by default).
+ * Try this with 8x8 tiles. They *will* look different.
+ *
+ * "pelpel" wrote another colour-depth independent X support
+ * using GdkRGB, added several hooks and callbacks for various
+ * reasons, wrote no-backing store mode (off by default),
+ * added GtkItemFactory based menu system, introduced
+ * USE_GRAPHICS code bloat (^ ^;), added comments (I have
+ * a strange habit of writing comments while I code...)
+ * and reorganised the file a bit.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Activate variant-specific features
+ *
+ * Angband 2.9.3 and close variants don't require any.
+ *
+ * Angband 2.9.4 alpha and later removed the short-lived
+ * can_save flag, so please #define can_save TRUE, or remove
+ * all the references to it. They also changed long-lived
+ * z-virt macro names. Find C_FREE/C_KILL and replace them
+ * with FREE/KILL, which takes one pointer parameter.
+ *
+ * [Z]-based variants (Gum and Cth, for example) usually need
+ * ANG293_COMPAT, ANG291_COMPAT and ANG281_RESET_VISUALS.
+ *
+ * [O] needs ANG293_COMPAT and ZANG_SAVE_GAME.
+ *
+ * ZAngband has its own enhanced main-gtk.c as mentioned above, and
+ * you *should* use it :-)
+ *
+ */
+#define TOME
+
+#ifdef TOME
+# define ANG293_COMPAT /* Requires V2.9.3 compatibility code */
+# define ANG291_COMPAT /* Requires V2.9.1 compatibility code */
+# define ANG281_RESET_VISUALS /* The old style reset_visuals() */
+# define SAVEFILE_SCREEN /* New/Open integrated into the game */
+# define USE_DOUBLE_TILES /* Mogami's bigtile patch */
+#endif /* TOME */
+
+/*
+ * Some examples
+ */
+#ifdef ANGBAND300
+# define can_save TRUE /* Mimick the short-lived flag */
+# define C_FREE(P, N, T) FREE(P) /* Emulate the long-lived macro */
+#endif /* ANGBAND300 */
+
+#ifdef GUMBAND
+# define ANG293_COMPAT /* Requires V2.9.3 compatibility code */
+# define ANG291_COMPAT /* Requires V2.9.1 compatibility code */
+# define ANG281_RESET_VISUALS /* The old style reset_visuals() */
+# define OLD_SAVEFILE_CODE /* See also SAVEFILE_MUTABLE in files.c */
+# define NO_REDRAW_SECTION /* Doesn't have Term_redraw_section() */
+#endif /* GUMBAND */
+
+#ifdef OANGBAND
+# define ANG293_COMPAT /* Requires V2.9.3 compatibility code */
+# define ZANG_SAVE_GAME /* do_cmd_save_game with auto_save parameter */
+#endif /* OANGBAND */
+
+
+#ifdef USE_GTK2
+
+/* Force ANSI standard */
+/* #define __STRICT_ANSI__ */
+
+/* No GCC-specific includes */
+/* #undef __GNUC__ */
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+
+/*
+ * Include some helpful X11 code.
+ */
+#ifndef ANG293_COMPAT
+# include "maid-x11.h"
+#endif /* !ANG293_COMPAT */
+
+
+/*
+ * Number of pixels inserted between the menu bar and the main screen
+ */
+#define NO_PADDING 0
+
+
+/*
+ * Largest possible number of terminal windows supported by the game
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * Extra data to associate with each "window"
+ *
+ * Each "window" is represented by a "term_data" structure, which
+ * contains a "term" structure, which contains a pointer (t->data)
+ * back to the term_data structure.
+ */
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Since GdkRGB doesn't provide us some useful functions...
+ */
+typedef struct GdkRGBImage GdkRGBImage;
+
+struct GdkRGBImage
+{
+ gint width;
+ gint height;
+ gint ref_count;
+ guchar *image;
+};
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * This structure holds everything you need to manipulate terminals
+ */
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t;
+
+ GtkWidget *window;
+ GtkWidget *drawing_area;
+ GdkPixmap *backing_store;
+ GdkFont *font;
+ GdkGC *gc;
+
+ bool_ shown;
+ byte last_attr;
+
+ int font_wid;
+ int font_hgt;
+
+ int rows;
+ int cols;
+
+#ifdef USE_GRAPHICS
+
+ int tile_wid;
+ int tile_hgt;
+
+ GdkRGBImage *tiles;
+ guint32 bg_pixel;
+ GdkRGBImage *trans_buf;
+
+#endif /* USE_GRAPHICS */
+
+ cptr name;
+};
+
+
+/*
+ * Where to draw when we call Gdk drawing primitives
+ */
+# define TERM_DATA_DRAWABLE(td) \
+((td)->backing_store ? (td)->backing_store : (td)->drawing_area->window)
+
+# define TERM_DATA_REFRESH(td, x, y, wid, hgt) \
+if ((td)->backing_store) gdk_draw_pixmap( \
+(td)->drawing_area->window, \
+(td)->gc, \
+(td)->backing_store, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(wid) * (td)->font_wid, \
+(hgt) * (td)->font_hgt)
+
+
+#if 0
+
+/* Compile time option version */
+
+# ifdef USE_BACKING_STORE
+
+# define TERM_DATA_DRAWABLE(td) (td)->backing_store
+
+# define TERM_DATA_REFRESH(td, x, y, wid, hgt) \
+gdk_draw_pixmap( \
+(td)->drawing_area->window, \
+(td)->gc, \
+(td)->backing_store, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(wid) * (td)->font_wid, \
+(hgt) * (td)->font_hgt)
+
+# else /* USE_BACKING_STORE */
+
+# define TERM_DATA_DRAWABLE(td) (td)->drawing_area->window
+# define TERM_DATA_REFRESH(td, x, y, wid, hgt)
+
+# endif /* USE_BACKING_STORE */
+
+#endif /* 0 */
+
+
+/*
+ * An array of "term_data" structures, one for each "sub-window"
+ */
+static term_data data[MAX_TERM_DATA];
+
+/*
+ * Number of active terms
+ */
+static int num_term = 1;
+
+
+/*
+ * RGB values of the sixteen Angband colours
+ */
+static guint32 angband_colours[16];
+
+
+/*
+ * Set to TRUE when a game is in progress
+ */
+static bool_ game_in_progress = FALSE;
+
+
+/*
+ * This is in some cases used for double buffering as well as
+ * a backing store, speeding things up under client-server
+ * configurations, while turning this off *might* work better
+ * with the MIT Shm extention which is usually active if you run
+ * Angband locally, because it reduces amount of memory-to-memory copy.
+ */
+static bool_ use_backing_store = TRUE;
+
+
+
+
+/**** Vanilla compatibility functions ****/
+
+#ifdef ANG293_COMPAT
+
+/*
+ * Look up some environment variables to find font name for each window.
+ */
+static cptr get_default_font(int term)
+{
+ char buf[64];
+ cptr font_name;
+
+ /* Window specific font name */
+ strnfmt(buf, 64, "ANGBAND_X11_FONT_%s", angband_term_name[term]);
+
+ /* Check environment for that font */
+ font_name = getenv(buf);
+
+ /* Window specific font name */
+ strnfmt(buf, 64, "ANGBAND_X11_FONT_%d", term);
+
+ /* Check environment for that font */
+ if (!font_name) font_name = getenv(buf);
+
+ /* Check environment for "base" font */
+ if (!font_name) font_name = getenv("ANGBAND_X11_FONT");
+
+ /* No environment variables, use default font */
+ if (!font_name) font_name = DEFAULT_X11_FONT_SCREEN;
+
+ return (font_name);
+}
+
+
+# ifndef SAVEFILE_SCREEN
+
+/*
+ * In [V]2.9.3, this frees all dynamically allocated memory
+ */
+static void cleanup_angband(void)
+{
+ /* XXX XXX XXX */
+}
+
+# endif /* !SAVEFILE_SCREEN */
+
+/*
+ * New global flag to indicate if it's safe to save now
+ */
+#define can_save TRUE
+
+#endif /* ANG293_COMPAT */
+
+
+#ifdef ANG291_COMPAT
+
+/*
+ * The standard game uses this to implement lighting effects
+ * for 16x16 tiles in cave.c...
+ *
+ * Because of the way it is implemented in X11 ports,
+ * we can set this to TRUE even if we are using the 8x8 tileset.
+ */
+static bool_ use_transparency = TRUE;
+
+#endif /* ANG291_COMPAT */
+
+
+
+
+/**** Low level routines - memory allocation ****/
+
+/*
+ * Hook to "release" memory
+ */
+#ifdef ANGBAND300
+static vptr hook_rnfree(vptr v)
+#else
+static vptr hook_rnfree(vptr v, huge size)
+#endif /* ANGBAND300 */
+{
+ /* Dispose */
+ g_free(v);
+
+ /* Success */
+ return (NULL);
+}
+
+
+/*
+ * Hook to "allocate" memory
+ */
+static vptr hook_ralloc(huge size)
+{
+ /* Make a new pointer */
+ return (g_malloc(size));
+}
+
+
+
+/**** Low level routines - colours and graphics ****/
+
+
+/*
+ * Remeber RGB values for sixteen Angband colours, in a format
+ * that is convinient for GdkRGB GC functions.
+ *
+ * XXX XXX Duplication of maid-x11.c is far from the Angband
+ * ideal of code cleanliness, but the whole point of using GdkRGB
+ * is to let it handle colour allocation which it does in a very
+ * clever fashion. Ditto for the tile scaling code and the BMP loader
+ * below.
+ */
+static void init_colours(void)
+{
+ int i;
+
+
+ /* Process each colour */
+ for (i = 0; i < 16; i++)
+ {
+ u32b red, green, blue;
+
+ /* Retrieve RGB values from the game */
+ red = angband_color_table[i][1];
+ green = angband_color_table[i][2];
+ blue = angband_color_table[i][3];
+
+ /* Remember a GdkRGB value, that is 0xRRGGBB */
+ angband_colours[i] = (red << 16) | (green << 8) | blue;
+ }
+}
+
+
+/*
+ * Set foreground colour of window td to attr, only when it is necessary
+ */
+static void term_data_set_fg(term_data *td, byte attr)
+{
+ /* We can use the current gc */
+ if (td->last_attr == attr) return;
+
+ /* Activate the colour */
+ gdk_rgb_gc_set_foreground(td->gc, angband_colours[attr]);
+
+ /* Remember it */
+ td->last_attr = attr;
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Graphics mode selector - current setting and requested value
+ */
+#define GRAF_MODE_NONE 0
+#define GRAF_MODE_OLD 1
+#define GRAF_MODE_NEW 2
+
+static int graf_mode = GRAF_MODE_NONE;
+static int graf_mode_request = GRAF_MODE_NONE;
+
+/*
+ * Use smooth rescaling?
+ */
+static bool_ smooth_rescaling = TRUE;
+static bool_ smooth_rescaling_request = TRUE;
+
+/*
+ * Dithering
+ */
+static GdkRgbDither dith_mode = GDK_RGB_DITHER_NORMAL;
+
+/*
+ * Need to reload and resize tiles when fonts are changed.
+ */
+static bool_ resize_request = FALSE;
+
+/*
+ * Numbers of columns and rows in current tileset
+ * calculated and set by the tile loading code in graf_init()
+ * and used by Term_pict_gtk()
+ */
+static int tile_rows;
+static int tile_cols;
+
+
+/*
+ * Directory name(s)
+ */
+static cptr ANGBAND_DIR_XTRA_GRAF;
+
+
+/*
+ * Be nice to old graphics hardwares -- using GdkRGB.
+ *
+ * We don't have colour allocation failure any longer this way,
+ * even with 8bpp X servers. Gimp *does* work with 8bpp, why not Angband?
+ *
+ * Initialisation (before any widgets are created)
+ * gdk_rgb_init();
+ * gtk_widget_set_default_colormap (gdk_rgb_get_cmap());
+ * gtk_widget_set_default_visual (gdk_rgb_get_visual());
+ *
+ * Setting fg/bg colours
+ * void gdk_rgb_gc_set_foreground(GdkGC *gc, guint32 rgb);
+ * void gdk_rgb_gc_set_background(GdkGC *gc, guint32 rgb);
+ * where rgb is 0xRRGGBB.
+ *
+ * Drawing rgb images
+ * void gdk_draw_rgb_image(
+ * GdkDrawable *drawable,
+ * GdkGC *gc,
+ * gint x, gint y,
+ * gint width, gint height,
+ * GdkRgbDither dith,
+ * guchar *rgb_buf,
+ * gint rowstride);
+ *
+ * dith:
+ * GDK_RGB_DITHER_NORMAL : dither if 8bpp or below
+ * GDK_RGB_DITHER_MAX : dither if 16bpp or below.
+ *
+ * for 0 <= i < width and 0 <= j < height,
+ * the pixel (x + i, y + j) is colored with
+ * red value rgb_buf[j * rowstride + i * 3],
+ * green value rgb_buf[j * rowstride + i * 3 + 1], and
+ * blue value rgb_buf[j * rowstride + i * 3 + 2].
+ */
+
+/*
+ * gdk_image compatibility functions - should be part of gdk, IMHO.
+ */
+
+/*
+ * Create GdkRGBImage of width * height and return pointer
+ * to it. Returns NULL on failure
+ */
+static GdkRGBImage *gdk_rgb_image_new(
+ gint width,
+ gint height)
+{
+ GdkRGBImage *result;
+
+ /* Allocate a struct */
+ result = g_new(GdkRGBImage, 1);
+
+ /* Oops */
+ if (result == NULL) return (NULL);
+
+ /* Allocate buffer */
+ result->image = g_new0(guchar, width * height * 3);
+
+ /* Oops */
+ if (result->image == NULL)
+ {
+ g_free(result);
+ return (NULL);
+ }
+
+ /* Initialise size fields */
+ result->width = width;
+ result->height = height;
+
+ /* Initialise reference count */
+ result->ref_count = 1;
+
+ /* Success */
+ return (result);
+}
+
+/*
+ * Free a GdkRGBImage
+ */
+static void gdk_rgb_image_destroy(
+ GdkRGBImage *im)
+{
+ /* Paranoia */
+ if (im == NULL) return;
+
+ /* Free the RGB buffer */
+ g_free(im->image);
+
+ /* Free the structure */
+ g_free(im);
+}
+
+
+#if 0
+
+/*
+ * Unref a GdkRGBImage
+ */
+static void gdk_rgb_image_unref(
+ GdkRGBImage *im)
+{
+ /* Paranoia */
+ g_return_if_fail(im != NULL);
+
+ /* Decrease reference count by 1 */
+ im->ref_count--;
+
+ /* Free if nobody's using it */
+ if (im->ref_count <= 0) gdk_rgb_image_destroy(im);
+}
+
+
+/*
+ * Reference a GdkRGBImage
+ */
+static void gdk_rgb_image_ref(
+ GdkRGBImage *im)
+{
+ /* Paranoia */
+ g_return_if_fail(im != NULL);
+
+ /* Increase reference count by 1 */
+ im->ref_count++;
+}
+
+#endif /* 0 */
+
+
+/*
+ * Write RGB pixel of the format 0xRRGGBB to (x, y) in GdkRGBImage
+ */
+static void gdk_rgb_image_put_pixel(
+ GdkRGBImage *im,
+ gint x,
+ gint y,
+ guint32 pixel)
+{
+ guchar *rgbp;
+
+ /* Paranoia */
+ g_return_if_fail(im != NULL);
+
+ /* Paranoia */
+ if ((x < 0) || (x >= im->width)) return;
+
+ /* Paranoia */
+ if ((y < 0) || (y >= im->height)) return;
+
+ /* Access RGB data */
+ rgbp = &im->image[(y * im->width * 3) + (x * 3)];
+
+ /* Red */
+ *rgbp++ = (pixel >> 16) & 0xFF;
+ /* Green */
+ *rgbp++ = (pixel >> 8) & 0xFF;
+ /* Blue */
+ *rgbp = pixel & 0xFF;
+}
+
+
+/*
+ * Returns RGB pixel (0xRRGGBB) at (x, y) in GdkRGBImage
+ */
+static guint32 gdk_rgb_image_get_pixel(
+ GdkRGBImage *im,
+ gint x,
+ gint y)
+{
+ guchar *rgbp;
+
+ /* Paranoia */
+ if (im == NULL) return (0);
+
+ /* Paranoia - returns black */
+ if ((x < 0) || (x >= im->width)) return (0);
+
+ /* Paranoia */
+ if ((y < 0) || (y >= im->height)) return (0);
+
+ /* Access RGB data */
+ rgbp = &im->image[(y * im->width * 3) + (x * 3)];
+
+ /* Return result */
+ return ((rgbp[0] << 16) | (rgbp[1] << 8) | (rgbp[2]));
+}
+
+
+/*
+ * Since gdk_draw_rgb_image is a bit harder to use than it's
+ * GdkImage counterpart, I wrote a grue function that takes
+ * exactly the same parameters as gdk_draw_image, with
+ * the GdkImage parameter replaced with GdkRGBImage.
+ */
+static void gdk_draw_rgb_image_2(
+ GdkDrawable *drawable,
+ GdkGC *gc,
+ GdkRGBImage *image,
+ gint xsrc,
+ gint ysrc,
+ gint xdest,
+ gint ydest,
+ gint width,
+ gint height)
+{
+ /* Paranoia */
+ g_return_if_fail(drawable != NULL);
+ g_return_if_fail(image != NULL);
+
+ /* Paranoia */
+ if (xsrc < 0 || (xsrc + width - 1) >= image->width) return;
+ if (ysrc < 0 || (ysrc + height - 1) >= image->height) return;
+
+ /* Draw the image at (xdest, ydest), with dithering if bpp <= 8/16 */
+ gdk_draw_rgb_image(
+ drawable,
+ gc,
+ xdest,
+ ydest,
+ width,
+ height,
+ dith_mode,
+ &image->image[(ysrc * image->width * 3) + (xsrc * 3)],
+ image->width * 3);
+}
+
+
+/*
+ * Code for smooth icon rescaling from Uwe Siems, Jan 2000
+ *
+ * XXX XXX Duplication of maid-x11.c, again. It doesn't do any colour
+ * allocation, either.
+ */
+
+/*
+ * to save ourselves some labour, define a maximum expected icon width here:
+ */
+#define MAX_ICON_WIDTH 32
+
+
+/*
+ * Each pixel is kept in this structure during smooth rescaling
+ * calculations, to make things a bit easier
+ */
+typedef struct rgb_type rgb_type;
+
+struct rgb_type
+{
+ guint32 red;
+ guint32 green;
+ guint32 blue;
+};
+
+/*
+ * Because there are many occurences of this, and because
+ * it's logical to do so...
+ */
+#define pixel_to_rgb(pix, rgb_buf) \
+(rgb_buf)->red = ((pix) >> 16) & 0xFF; \
+(rgb_buf)->green = ((pix) >> 8) & 0xFF; \
+(rgb_buf)->blue = (pix) & 0xFF
+
+
+/*
+ * get_scaled_row reads a scan from the given GdkRGBImage, scales it smoothly
+ * and returns the red, green and blue values in arrays.
+ * The values in this arrays must be divided by a certain value that is
+ * calculated in scale_icon.
+ * x, y is the position, iw is the input width and ow the output width
+ * scan must be sufficiently sized
+ */
+static void get_scaled_row(
+ GdkRGBImage *im,
+ int x,
+ int y,
+ int iw,
+ int ow,
+ rgb_type *scan)
+{
+ int xi, si, sifrac, ci, cifrac, add_whole, add_frac;
+ guint32 pix;
+ rgb_type prev;
+ rgb_type next;
+ bool_ get_next_pix;
+
+ /* Unscaled */
+ if (iw == ow)
+ {
+ for (xi = 0; xi < ow; xi++)
+ {
+ pix = gdk_rgb_image_get_pixel(im, x + xi, y);
+ pixel_to_rgb(pix, &scan[xi]);
+ }
+ }
+
+ /* Scaling by subsampling (grow) */
+ else if (iw < ow)
+ {
+ iw--;
+ ow--;
+
+ /* read first pixel: */
+ pix = gdk_rgb_image_get_pixel(im, x, y);
+ pixel_to_rgb(pix, &next);
+ prev = next;
+
+ /* si and sifrac give the subsampling position: */
+ si = x;
+ sifrac = 0;
+
+ /* get_next_pix tells us, that we need the next pixel */
+ get_next_pix = TRUE;
+
+ for (xi = 0; xi <= ow; xi++)
+ {
+ if (get_next_pix)
+ {
+ prev = next;
+ if (xi < ow)
+ {
+ /* only get next pixel if in same icon */
+ pix = gdk_rgb_image_get_pixel(im, si + 1, y);
+ pixel_to_rgb(pix, &next);
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by ow occurs in scale_icon */
+ scan[xi].red = prev.red * (ow - sifrac) + next.red * sifrac;
+ scan[xi].green = prev.green * (ow - sifrac) + next.green * sifrac;
+ scan[xi].blue = prev.blue * (ow - sifrac) + next.blue * sifrac;
+
+ /* advance sampling position: */
+ sifrac += iw;
+ if (sifrac >= ow)
+ {
+ si++;
+ sifrac -= ow;
+ get_next_pix = TRUE;
+ }
+ else
+ {
+ get_next_pix = FALSE;
+ }
+
+ }
+ }
+
+ /* Scaling by averaging (shrink) */
+ else
+ {
+ /* width of an output pixel in input pixels: */
+ add_whole = iw / ow;
+ add_frac = iw % ow;
+
+ /* start position of the first output pixel: */
+ si = x;
+ sifrac = 0;
+
+ /* get first input pixel: */
+ pix = gdk_rgb_image_get_pixel(im, x, y);
+ pixel_to_rgb(pix, &next);
+
+ for (xi = 0; xi < ow; xi++)
+ {
+ /* find endpoint of the current output pixel: */
+ ci = si + add_whole;
+ cifrac = sifrac + add_frac;
+ if (cifrac >= ow)
+ {
+ ci++;
+ cifrac -= ow;
+ }
+
+ /* take fraction of current input pixel (starting segment): */
+ scan[xi].red = next.red * (ow - sifrac);
+ scan[xi].green = next.green * (ow - sifrac);
+ scan[xi].blue = next.blue * (ow - sifrac);
+ si++;
+
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ rgb_type tmp_rgb;
+
+ pix = gdk_rgb_image_get_pixel(im, si, y);
+ pixel_to_rgb(pix, &tmp_rgb);
+ scan[xi].red += tmp_rgb.red * ow;
+ scan[xi].green += tmp_rgb.green * ow;
+ scan[xi].blue += tmp_rgb.blue * ow;
+ si++;
+ }
+
+ /* add fraction of current input pixel (ending segment): */
+ if (xi < ow - 1)
+ {
+ /* only get next pixel if still in icon: */
+ pix = gdk_rgb_image_get_pixel(im, si, y);
+ pixel_to_rgb(pix, &next);
+ }
+
+ sifrac = cifrac;
+ if (sifrac > 0)
+ {
+ scan[xi].red += next.red * sifrac;
+ scan[xi].green += next.green * sifrac;
+ scan[xi].blue += next.blue * sifrac;
+ }
+ }
+ }
+}
+
+
+/*
+ * put_rgb_scan takes arrays for red, green and blue and writes pixel values
+ * according to this values in the GdkRGBImage-structure. w is the number of
+ * pixels to write and div is the value by which all red/green/blue values
+ * are divided first.
+ */
+static void put_rgb_scan(
+ GdkRGBImage *im,
+ int x,
+ int y,
+ int w,
+ int div,
+ rgb_type *scan)
+{
+ int xi;
+ guint32 pix;
+ guint32 adj = div / 2;
+
+ for (xi = 0; xi < w; xi++)
+ {
+ byte r, g, b;
+
+ /* un-factor the RGB values */
+ r = (scan[xi].red + adj) / div;
+ g = (scan[xi].green + adj) / div;
+ b = (scan[xi].blue + adj) / div;
+
+ /* Make a (virtual) 24-bit pixel */
+ pix = (r << 16) | (g << 8) | (b);
+
+ /* Draw it into image */
+ gdk_rgb_image_put_pixel(im, x + xi, y, pix);
+ }
+}
+
+
+/*
+ * scale_icon transfers an area from GdkRGBImage im_in, locate (x1,y1) to
+ * im_out, locate (x2, y2). Source size is (ix, iy) and destination size
+ * is (ox, oy).
+ *
+ * It does this by getting icon scan line from get_scaled_scan and handling
+ * them the same way as pixels are handled in get_scaled_scan.
+ * This even allows icons to be scaled differently in horizontal and
+ * vertical directions (eg. shrink horizontal, grow vertical).
+ */
+static void scale_icon(
+ GdkRGBImage *im_in,
+ GdkRGBImage *im_out,
+ int x1,
+ int y1,
+ int x2,
+ int y2,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ int div;
+ int xi, yi, si, sifrac, ci, cifrac, add_whole, add_frac;
+
+ /* buffers for pixel rows: */
+ rgb_type prev[MAX_ICON_WIDTH];
+ rgb_type next[MAX_ICON_WIDTH];
+ rgb_type temp[MAX_ICON_WIDTH];
+
+ bool_ get_next_row;
+
+ /* get divider value for the horizontal scaling: */
+ if (ix == ox)
+ div = 1;
+ else if (ix < ox)
+ div = ox - 1;
+ else
+ div = ix;
+
+ /* no scaling needed vertically: */
+ if (iy == oy)
+ {
+ for (yi = 0; yi < oy; yi++)
+ {
+ get_scaled_row(im_in, x1, y1 + yi, ix, ox, temp);
+ put_rgb_scan(im_out, x2, y2 + yi, ox, div, temp);
+ }
+ }
+
+ /* scaling by subsampling (grow): */
+ else if (iy < oy)
+ {
+ iy--;
+ oy--;
+ div *= oy;
+
+ /* get first row: */
+ get_scaled_row(im_in, x1, y1, ix, ox, next);
+
+ /* si and sifrac give the subsampling position: */
+ si = y1;
+ sifrac = 0;
+
+ /* get_next_row tells us, that we need the next row */
+ get_next_row = TRUE;
+ for (yi = 0; yi <= oy; yi++)
+ {
+ if (get_next_row)
+ {
+ for (xi = 0; xi < ox; xi++)
+ {
+ prev[xi] = next[xi];
+ }
+ if (yi < oy)
+ {
+ /* only get next row if in same icon */
+ get_scaled_row(im_in, x1, si + 1, ix, ox, next);
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by oy occurs in put_rgb_scan */
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red = (prev[xi].red * (oy - sifrac) +
+ next[xi].red * sifrac);
+ temp[xi].green = (prev[xi].green * (oy - sifrac) +
+ next[xi].green * sifrac);
+ temp[xi].blue = (prev[xi].blue * (oy - sifrac) +
+ next[xi].blue * sifrac);
+ }
+
+ /* write row to output image: */
+ put_rgb_scan(im_out, x2, y2 + yi, ox, div, temp);
+
+ /* advance sampling position: */
+ sifrac += iy;
+ if (sifrac >= oy)
+ {
+ si++;
+ sifrac -= oy;
+ get_next_row = TRUE;
+ }
+ else
+ {
+ get_next_row = FALSE;
+ }
+
+ }
+ }
+
+ /* scaling by averaging (shrink) */
+ else
+ {
+ div *= iy;
+
+ /* height of a output row in input rows: */
+ add_whole = iy / oy;
+ add_frac = iy % oy;
+
+ /* start position of the first output row: */
+ si = y1;
+ sifrac = 0;
+
+ /* get first input row: */
+ get_scaled_row(im_in, x1, y1, ix, ox, next);
+ for (yi = 0; yi < oy; yi++)
+ {
+ /* find endpoint of the current output row: */
+ ci = si + add_whole;
+ cifrac = sifrac + add_frac;
+ if (cifrac >= oy)
+ {
+ ci++;
+ cifrac -= oy;
+ }
+
+ /* take fraction of current input row (starting segment): */
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red = next[xi].red * (oy - sifrac);
+ temp[xi].green = next[xi].green * (oy - sifrac);
+ temp[xi].blue = next[xi].blue * (oy - sifrac);
+ }
+ si++;
+
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ get_scaled_row(im_in, x1, si, ix, ox, next);
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red += next[xi].red * oy;
+ temp[xi].green += next[xi].green * oy;
+ temp[xi].blue += next[xi].blue * oy;
+ }
+ si++;
+ }
+
+ /* add fraction of current input row (ending segment): */
+ if (yi < oy - 1)
+ {
+ /* only get next row if still in icon: */
+ get_scaled_row(im_in, x1, si, ix, ox, next);
+ }
+ sifrac = cifrac;
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red += next[xi].red * sifrac;
+ temp[xi].green += next[xi].green * sifrac;
+ temp[xi].blue += next[xi].blue * sifrac;
+ }
+
+ /* write row to output image: */
+ put_rgb_scan(im_out, x2, y2 + yi, ox, div, temp);
+ }
+ }
+}
+
+
+/*
+ * Rescale icons using sort of anti-aliasing technique.
+ */
+static GdkRGBImage *resize_tiles_smooth(
+ GdkRGBImage *im,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ int width1, height1, width2, height2;
+ int x1, x2, y1, y2;
+
+ GdkRGBImage *tmp;
+
+ /* Original size */
+ width1 = im->width;
+ height1 = im->height;
+
+ /* Rescaled size */
+ width2 = ox * width1 / ix;
+ height2 = oy * height1 / iy;
+
+ /* Allocate GdkRGBImage for resized tiles */
+ tmp = gdk_rgb_image_new(width2, height2);
+
+ /* Oops */
+ if (tmp == NULL) return (NULL);
+
+ /* Scale each icon */
+ for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy)
+ {
+ for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox)
+ {
+ scale_icon(im, tmp, x1, y1, x2, y2,
+ ix, iy, ox, oy);
+ }
+ }
+
+ return tmp;
+}
+
+
+/*
+ * Steven Fuerst's tile resizing code
+ * Taken from Z because I think the algorithm is cool.
+ */
+
+/* 24-bit version - GdkRGB uses 24 bit RGB data internally */
+static void copy_pixels(
+ int wid,
+ int y,
+ int offset,
+ int *xoffsets,
+ GdkRGBImage *old_image,
+ GdkRGBImage *new_image)
+{
+ int i;
+
+ /* Get source and destination */
+ byte *src = &old_image->image[offset * old_image->width * 3];
+ byte *dst = &new_image->image[y * new_image->width * 3];
+
+ /* Copy to the image */
+ for (i = 0; i < wid; i++)
+ {
+ *dst++ = src[3 * xoffsets[i]];
+ *dst++ = src[3 * xoffsets[i] + 1];
+ *dst++ = src[3 * xoffsets[i] + 2];
+ }
+}
+
+
+#if 0
+
+/* 32-bit version: it might be useful in the future */
+static void copy_pixels(
+ int wid,
+ int y,
+ int offset,
+ int *xoffsets,
+ GdkRGBImage *old_image,
+ GdkRGBImage *new_image)
+{
+ int i;
+
+ /* Get source and destination */
+ byte *src = &old_image->image[offset * old_image->width * 4];
+ byte *dst = &new_image->image[y * new_image->width * 4];
+
+ /* Copy to the image */
+ for (i = 0; i < wid; i++)
+ {
+ *dst++ = src[4 * xoffsets[i]];
+ *dst++ = src[4 * xoffsets[i] + 1];
+ *dst++ = src[4 * xoffsets[i] + 2];
+ *dst++ = src[4 * xoffsets[i] + 3];
+ }
+}
+
+#endif
+
+
+/*
+ * Resize ix * iy pixel tiles in old to ox * oy pixels
+ * and return a new GdkRGBImage containing the resized tiles
+ */
+static GdkRGBImage *resize_tiles_fast(
+ GdkRGBImage *old_image,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ GdkRGBImage *new_image;
+
+ int old_wid, old_hgt;
+
+ int new_wid, new_hgt;
+
+ int add, remainder, rem_tot, offset;
+
+ int *xoffsets;
+
+ int i;
+
+
+ /* Get the size of the old image */
+ old_wid = old_image->width;
+ old_hgt = old_image->height;
+
+ /* Calculate the size of the new image */
+ new_wid = (old_wid / ix) * ox;
+ new_hgt = (old_hgt / iy) * oy;
+
+ /* Allocate a GdkRGBImage to store resized tiles */
+ new_image = gdk_rgb_image_new(new_wid, new_hgt);
+
+ /* Paranoia */
+ if (new_image == NULL) return (NULL);
+
+ /* now begins the cool part of SF's code */
+
+ /*
+ * Calculate an offsets table, so the transformation
+ * is faster. This is much like the Bresenham algorithm
+ */
+
+ /* Set up x offset table */
+ C_MAKE(xoffsets, new_wid, int);
+
+ /* Initialize line parameters */
+ add = old_wid / new_wid;
+ remainder = old_wid % new_wid;
+
+ /* Start at left */
+ offset = 0;
+
+ /* Half-tile offset so 'line' is centered correctly */
+ rem_tot = new_wid / 2;
+
+ for (i = 0; i < new_wid; i++)
+ {
+ /* Store into the table */
+ xoffsets[i] = offset;
+
+ /* Move to next entry */
+ offset += add;
+
+ /* Take care of fractional part */
+ rem_tot += remainder;
+ if (rem_tot >= new_wid)
+ {
+ rem_tot -= new_wid;
+ offset++;
+ }
+ }
+
+ /* Scan each row */
+
+ /* Initialize line parameters */
+ add = old_hgt / new_hgt;
+ remainder = old_hgt % new_hgt;
+
+ /* Start at left */
+ offset = 0;
+
+ /* Half-tile offset so 'line' is centered correctly */
+ rem_tot = new_hgt / 2;
+
+ for (i = 0; i < new_hgt; i++)
+ {
+ /* Copy pixels to new image */
+ copy_pixels(new_wid, i, offset, xoffsets, old_image, new_image);
+
+ /* Move to next entry */
+ offset += add;
+
+ /* Take care of fractional part */
+ rem_tot += remainder;
+ if (rem_tot >= new_hgt)
+ {
+ rem_tot -= new_hgt;
+ offset++;
+ }
+ }
+
+ /* Free offset table */
+ C_FREE(xoffsets, new_wid, int);
+
+ return (new_image);
+}
+
+
+/*
+ * Resize an image of ix * iy pixels and return a newly allocated
+ * image of ox * oy pixels.
+ */
+static GdkRGBImage *resize_tiles(
+ GdkRGBImage *im,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ GdkRGBImage *result;
+
+ /*
+ * I hope we can always use this with GdkRGB, which uses a 5x5x5
+ * colour cube (125 colours) by default, and resort to dithering
+ * when it can't find good match there or expand the cube, so it
+ * works with 8bpp X servers.
+ */
+ if (smooth_rescaling_request && (ix != ox || iy != oy))
+ {
+ result = resize_tiles_smooth(im, ix, iy, ox, oy);
+ }
+
+ /*
+ * Unless smoothing is requested by user, we use the fast
+ * resizing code.
+ */
+ else
+ {
+ result = resize_tiles_fast(im, ix, iy, ox, oy);
+ }
+
+ /* Return rescaled tiles, or NULL */
+ return (result);
+}
+
+
+/*
+ * Tile loaders - XPM and BMP
+ */
+
+/*
+ * A helper function for the XPM loader
+ *
+ * Read next string delimited by double quotes from
+ * the input stream. Return TRUE on success, FALSE
+ * if it finds EOF or buffer overflow.
+ *
+ * I never mean this to be generic, so its EOF and buffer
+ * overflow behaviour is terribly stupid -- there are no
+ * provisions for recovery.
+ *
+ * CAVEAT: treatment of backslash is not compatible with the standard
+ * C usage XXX XXX XXX XXX
+ */
+static bool_ read_str(char *buf, u32b len, FILE *f)
+{
+ int c;
+
+ /* Paranoia - Buffer too small */
+ if (len <= 0) return (FALSE);
+
+ /* Find " */
+ while ((c = getc(f)) != '"')
+ {
+ /* Premature EOF */
+ if (c == EOF) return (FALSE);
+ }
+
+ while (1)
+ {
+ /* Read next char */
+ c = getc(f);
+
+ /* Premature EOF */
+ if (c == EOF) return (FALSE);
+
+ /* Terminating " */
+ if (c == '"') break;
+
+ /* Escape */
+ if (c == '\\')
+ {
+ /* Use next char */
+ c = getc(f);
+
+ /* Premature EOF */
+ if (c == EOF) return (FALSE);
+ }
+
+ /* Store character in the buffer */
+ *buf++ = c;
+
+ /* Decrement count */
+ len--;
+
+ /* Buffer full - we have to place a NULL at the end */
+ if (len <= 0) return (FALSE);
+ }
+
+ /* Make a C string if there's room left */
+ if (len > 0) *buf = '\0';
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Remember pixel symbol to RGB colour mappings
+ */
+
+/*
+ * I've forgot the formula, but I remember prime number yields
+ * good results
+ */
+#define HASH_SIZE 19
+
+typedef struct pal_type pal_type;
+
+struct pal_type
+{
+ u32b str;
+ u32b rgb;
+ pal_type *next;
+};
+
+
+/*
+ * A simple, slow and stupid XPM loader
+ */
+static GdkRGBImage *load_xpm(cptr filename)
+{
+ FILE *f;
+ GdkRGBImage *img = NULL;
+ int width, height, colours, chars;
+ int i, j, k;
+ bool_ ret;
+ pal_type *pal = NULL;
+ pal_type *head[HASH_SIZE];
+ u32b buflen = 0;
+ char *lin = NULL;
+ char buf[1024];
+
+ /* Build path to the XPM file */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_GRAF, filename);
+
+ /* Open it */
+ f = my_fopen(buf, "r");
+
+ /* Oops */
+ if (f == NULL) return (NULL);
+
+ /* Read header */
+ ret = read_str(buf, 1024, f);
+
+ /* Oops */
+ if (!ret)
+ {
+ /* Notify error */
+ plog("Cannot find XPM header");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Parse header */
+ if (4 != sscanf(buf, "%d %d %d %d", &width, &height, &colours, &chars))
+ {
+ /* Notify error */
+ plog("Bad XPM header");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /*
+ * Paranoia - the code can handle upto four letters per pixel,
+ * but such large number of colours certainly requires a smarter
+ * symbol-to-colour mapping algorithm...
+ */
+ if ((width <= 0) || (height <= 0) || (colours <= 0) || (chars <= 0) ||
+ (chars > 2))
+ {
+ /* Notify error */
+ plog("Invalid width/height/depth");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Allocate palette */
+ C_MAKE(pal, colours, pal_type);
+
+ /* Initialise hash table */
+ for (i = 0; i < HASH_SIZE; i++) head[i] = NULL;
+
+ /* Parse palette */
+ for (i = 0; i < colours; i++)
+ {
+ u32b tmp;
+ int h_idx;
+
+ /* Read next string */
+ ret = read_str(buf, 1024, f);
+
+ /* Check I/O result */
+ if (!ret)
+ {
+ /* Notify error */
+ plog("EOF in palette");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Clear symbol code */
+ tmp = 0;
+
+ /* Encode pixel symbol */
+ for (j = 0; j < chars; j++)
+ {
+ tmp = (tmp << 8) | (buf[j] & 0xFF);
+ }
+
+ /* Remember it */
+ pal[i].str = tmp;
+
+ /* Skip spaces */
+ while ((buf[j] == ' ') || (buf[j] == '\t')) j++;
+
+ /* Verify 'c' */
+ if (buf[j] != 'c')
+ {
+ /* Notify error */
+ plog("No 'c' in palette definition");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Advance cursor */
+ j++;
+
+ /* Skip spaces */
+ while ((buf[j] == ' ') || (buf[j] == '\t')) j++;
+
+ /* Hack - Assume 'None' */
+ if (buf[j] == 'N')
+ {
+ /* Angband always uses black background */
+ pal[i].rgb = 0x000000;
+ }
+
+ /* Read colour */
+ else if ((1 != sscanf(&buf[j], "#%06lX", &tmp)) &&
+ (1 != sscanf(&buf[j], "#%06lx", &tmp)))
+ {
+ /* Notify error */
+ plog("Badly formatted colour");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Remember it */
+ pal[i].rgb = tmp;
+
+ /* Store it in hash table as well */
+ h_idx = pal[i].str % HASH_SIZE;
+
+ /* Link the entry */
+ pal[i].next = head[h_idx];
+ head[h_idx] = &pal[i];
+ }
+
+ /* Allocate image */
+ img = gdk_rgb_image_new(width, height);
+
+ /* Oops */
+ if (img == NULL)
+ {
+ /* Notify error */
+ plog("Cannot allocate image");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Calculate buffer length */
+ buflen = width * chars + 1;
+
+ /* Allocate line buffer */
+ C_MAKE(lin, buflen, char);
+
+ /* For each row */
+ for (i = 0; i < height; i++)
+ {
+ /* Read a row of image data */
+ ret = read_str(lin, buflen, f);
+
+ /* Oops */
+ if (!ret)
+ {
+ /* Notify error */
+ plog("EOF in middle of image data");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* For each column */
+ for (j = 0; j < width; j++)
+ {
+ u32b tmp;
+ pal_type *h_ptr;
+
+ /* Clear encoded pixel */
+ tmp = 0;
+
+ /* Encode pixel symbol */
+ for (k = 0; k < chars; k++)
+ {
+ tmp = (tmp << 8) | (lin[j * chars + k] & 0xFF);
+ }
+
+ /* Find colour */
+ for (h_ptr = head[tmp % HASH_SIZE];
+ h_ptr != NULL;
+ h_ptr = h_ptr->next)
+ {
+ /* Found a match */
+ if (h_ptr->str == tmp) break;
+ }
+
+ /* No match found */
+ if (h_ptr == NULL)
+ {
+ /* Notify error */
+ plog("Invalid pixel symbol");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Draw it */
+ gdk_rgb_image_put_pixel(
+ img,
+ j,
+ i,
+ h_ptr->rgb);
+ }
+ }
+
+ /* Close file */
+ my_fclose(f);
+
+ /* Free line buffer */
+ C_FREE(lin, buflen, char);
+
+ /* Free palette */
+ C_FREE(pal, colours, pal_type);
+
+ /* Return result */
+ return (img);
+
+oops:
+
+ /* Close file */
+ my_fclose(f);
+
+ /* Free image */
+ if (img) gdk_rgb_image_destroy(img);
+
+ /* Free line buffer */
+ if (lin) C_FREE(lin, buflen, char);
+
+ /* Free palette */
+ if (pal) C_FREE(pal, colours, pal_type);
+
+ /* Failure */
+ return (NULL);
+}
+
+
+/*
+ * A BMP loader, yet another duplication of maid-x11.c functions.
+ *
+ * Another duplication, again because of different image format and
+ * avoidance of colour allocation.
+ *
+ * XXX XXX XXX XXX Should avoid using a propriatary and closed format.
+ * Since it's much bigger than gif that was used before, why don't
+ * we switch to XPM? NetHack does. Well, NH has always been much
+ * closer to the GNU/Un*x camp and it's GPL'ed quite early...
+ *
+ * The names and naming convention are worse than the worst I've ever
+ * seen, so I deliberately changed them to fit well with the rest of
+ * the code. Or are they what xx calls them? If it's the case, there's
+ * no reason to follow *their* words.
+ */
+
+/*
+ * BMP file header
+ */
+typedef struct bmp_file_type bmp_file_type;
+
+struct bmp_file_type
+{
+ u16b type;
+ u32b size;
+ u16b reserved1;
+ u16b reserved2;
+ u32b offset;
+};
+
+
+/*
+ * BMP file information fields
+ */
+typedef struct bmp_info_type bmp_info_type;
+
+struct bmp_info_type
+{
+ u32b size;
+ u32b width;
+ u32b height;
+ u16b planes;
+ u16b bit_count;
+ u32b compression;
+ u32b size_image;
+ u32b x_pels_per_meter;
+ u32b y_pels_per_meter;
+ u32b colors_used;
+ u32b color_importand;
+};
+
+/*
+ * "RGBQUAD" type.
+ */
+typedef struct rgb_quad_type rgb_quad_type;
+
+struct rgb_quad_type
+{
+ unsigned char b, g, r;
+ unsigned char filler;
+};
+
+
+/*** Helper functions for system independent file loading. ***/
+
+static byte get_byte(FILE *fff)
+{
+ /* Get a character, and return it */
+ return (getc(fff) & 0xFF);
+}
+
+static void rd_byte(FILE *fff, byte *ip)
+{
+ *ip = get_byte(fff);
+}
+
+static void rd_u16b(FILE *fff, u16b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u16b)(get_byte(fff)) << 8);
+}
+
+static void rd_u32b(FILE *fff, u32b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u32b)(get_byte(fff)) << 8);
+ (*ip) |= ((u32b)(get_byte(fff)) << 16);
+ (*ip) |= ((u32b)(get_byte(fff)) << 24);
+}
+
+
+/*
+ * Read a BMP file (a certain trademark nuked)
+ *
+ * This function replaces the old ReadRaw and RemapColors functions.
+ *
+ * Assumes that the bitmap has a size such that no padding is needed in
+ * various places. Currently only handles bitmaps with 3 to 256 colors.
+ */
+GdkRGBImage *load_bmp(cptr filename)
+{
+ FILE *f;
+
+ char path[1024];
+
+ bmp_file_type file_hdr;
+ bmp_info_type info_hdr;
+
+ GdkRGBImage *result = NULL;
+
+ int ncol;
+
+ int i;
+
+ u32b x, y;
+
+ guint32 colour_pixels[256];
+
+
+ /* Build the path to the bmp file */
+ path_build(path, 1024, ANGBAND_DIR_XTRA_GRAF, filename);
+
+ /* Open the BMP file */
+ f = fopen(path, "r");
+
+ /* No such file */
+ if (f == NULL)
+ {
+ return (NULL);
+ }
+
+ /* Read the "bmp_file_type" */
+ rd_u16b(f, &file_hdr.type);
+ rd_u32b(f, &file_hdr.size);
+ rd_u16b(f, &file_hdr.reserved1);
+ rd_u16b(f, &file_hdr.reserved2);
+ rd_u32b(f, &file_hdr.offset);
+
+ /* Read the "bmp_info_type" */
+ rd_u32b(f, &info_hdr.size);
+ rd_u32b(f, &info_hdr.width);
+ rd_u32b(f, &info_hdr.height);
+ rd_u16b(f, &info_hdr.planes);
+ rd_u16b(f, &info_hdr.bit_count);
+ rd_u32b(f, &info_hdr.compression);
+ rd_u32b(f, &info_hdr.size_image);
+ rd_u32b(f, &info_hdr.x_pels_per_meter);
+ rd_u32b(f, &info_hdr.y_pels_per_meter);
+ rd_u32b(f, &info_hdr.colors_used);
+ rd_u32b(f, &info_hdr.color_importand);
+
+ /* Verify the header */
+ if (feof(f) ||
+ (file_hdr.type != 19778) ||
+ (info_hdr.size != 40))
+ {
+ plog(format("Incorrect BMP file format %s", filename));
+ fclose(f);
+ return (NULL);
+ }
+
+ /*
+ * The two headers above occupy 54 bytes total
+ * The "offset" field says where the data starts
+ * The "colors_used" field does not seem to be reliable
+ */
+
+ /* Compute number of colors recorded */
+ ncol = (file_hdr.offset - 54) / 4;
+
+ for (i = 0; i < ncol; i++)
+ {
+ rgb_quad_type clr;
+
+ /* Read an "rgb_quad_type" */
+ rd_byte(f, &clr.b);
+ rd_byte(f, &clr.g);
+ rd_byte(f, &clr.r);
+ rd_byte(f, &clr.filler);
+
+ /* Remember the pixel */
+ colour_pixels[i] = (clr.r << 16) | (clr.g << 8) | (clr.b);
+ }
+
+ /* Allocate GdkRGBImage large enough to store the image */
+ result = gdk_rgb_image_new(info_hdr.width, info_hdr.height);
+
+ /* Failure */
+ if (result == NULL)
+ {
+ fclose(f);
+ return (NULL);
+ }
+
+ for (y = 0; y < info_hdr.height; y++)
+ {
+ u32b y2 = info_hdr.height - y - 1;
+
+ for (x = 0; x < info_hdr.width; x++)
+ {
+ int ch = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f))
+ {
+ plog(format("Unexpected end of file in %s", filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+
+ if (info_hdr.bit_count == 24)
+ {
+ int c3, c2 = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f))
+ {
+ plog(format("Unexpected end of file in %s", filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+
+ c3 = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f))
+ {
+ plog(format("Unexpected end of file in %s", filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+
+ /* Draw the pixel */
+ gdk_rgb_image_put_pixel(
+ result,
+ x,
+ y2,
+ (ch << 16) | (c2 << 8) | (c3));
+ }
+ else if (info_hdr.bit_count == 8)
+ {
+ gdk_rgb_image_put_pixel(result, x, y2, colour_pixels[ch]);
+ }
+ else if (info_hdr.bit_count == 4)
+ {
+ gdk_rgb_image_put_pixel(result, x, y2, colour_pixels[ch / 16]);
+ x++;
+ gdk_rgb_image_put_pixel(result, x, y2, colour_pixels[ch % 16]);
+ }
+ else
+ {
+ /* Technically 1 bit is legal too */
+ plog(format("Illegal bit count %d in %s",
+ info_hdr.bit_count, filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+ }
+ }
+
+ fclose(f);
+
+ return result;
+}
+
+
+/*
+ * Try to load an XPM file, or a BMP file if it fails
+ *
+ * Choice of file format may better be made yet another option XXX
+ */
+static GdkRGBImage *load_tiles(cptr basename)
+{
+ char buf[32];
+ GdkRGBImage *img;
+
+ /* build xpm file name */
+ strnfmt(buf, 32, "%s.xpm", basename);
+
+ /* Try to load it */
+ img = load_xpm(buf);
+
+ /* OK */
+ if (img) return (img);
+
+ /* Try again for a bmp file */
+ strnfmt(buf, 32, "%s.bmp", basename);
+
+ /* Try loading it */
+ img = load_bmp(buf);
+
+ /* Return result, success or failure */
+ return (img);
+}
+
+
+/*
+ * Free all tiles and graphics buffers associated with windows
+ *
+ * This is conspirator of graf_init() below, sharing its inefficiency
+ */
+static void graf_nuke()
+{
+ int i;
+
+ term_data *td;
+
+
+ /* Nuke all terms */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Access term_data structure */
+ td = &data[i];
+
+ /* Disable graphics */
+ td->t.higher_pict = FALSE;
+
+ /* Free previously allocated tiles */
+ if (td->tiles) gdk_rgb_image_destroy(td->tiles);
+
+ /* Forget pointer */
+ td->tiles = NULL;
+
+ /* Free previously allocated transparency buffer */
+ if (td->trans_buf) gdk_rgb_image_destroy(td->trans_buf);
+
+ /* Forget stale pointer */
+ td->trans_buf = NULL;
+ }
+}
+
+
+/*
+ * Load tiles, scale them to current font size, and store a pointer
+ * to them in a term_data structure for each term.
+ *
+ * XXX XXX XXX This is a terribly stupid quick hack.
+ *
+ * XXX XXX XXX Windows using the same font should share resized tiles
+ */
+static bool_ graf_init(
+ cptr filename,
+ int tile_wid,
+ int tile_hgt)
+{
+ term_data *td;
+
+ bool_ result;
+
+ GdkRGBImage *raw_tiles, *scaled_tiles;
+
+ GdkRGBImage *buffer;
+
+ int i;
+
+
+ /* Paranoia */
+ if (filename == NULL) return (FALSE);
+
+ /* Load tiles */
+ raw_tiles = load_tiles(filename);
+
+ /* Oops */
+ if (raw_tiles == NULL)
+ {
+ /* Clean up */
+ graf_nuke();
+
+ /* Failure */
+ return (FALSE);
+ }
+
+ /* Calculate and remember numbers of rows and columns */
+ tile_rows = raw_tiles->height / tile_hgt;
+ tile_cols = raw_tiles->width / tile_wid;
+
+ /* Be optimistic */
+ result = TRUE;
+
+
+ /*
+ * (Re-)init each term
+ * XXX It might help speeding this up to avoid doing so if a window
+ * doesn't need graphics (e.g. inventory/equipment and message recall).
+ */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Access term_data */
+ td = &data[i];
+
+ /* Shouldn't waste anything for unused terms */
+ if (!td->shown) continue;
+
+ /* Enable graphics */
+ td->t.higher_pict = TRUE;
+
+ /* See if we need rescaled tiles XXX */
+ if ((td->tiles == NULL) ||
+ (td->tiles->width != td->tile_wid * tile_cols) ||
+ (td->tiles->height != td->tile_hgt * tile_rows))
+ {
+ /* Free old tiles if present */
+ if (td->tiles) gdk_rgb_image_destroy(td->tiles);
+
+ /* Forget pointer */
+ td->tiles = NULL;
+
+ /* Scale the tiles to current font bounding rect */
+ scaled_tiles = resize_tiles(
+ raw_tiles,
+ tile_wid, tile_hgt,
+ td->tile_wid, td->tile_hgt);
+
+ /* Oops */
+ if (scaled_tiles == NULL)
+ {
+ /* Failure */
+ result = FALSE;
+
+ break;
+ }
+
+ /* Store it */
+ td->tiles = scaled_tiles;
+ }
+
+ /* See if we have to (re)allocate a new buffer XXX */
+ if ((td->trans_buf == NULL) ||
+ (td->trans_buf->width != td->tile_wid) ||
+ (td->trans_buf->height != td->tile_hgt))
+ {
+ /* Free old buffer if present */
+ if (td->trans_buf) gdk_rgb_image_destroy(td->trans_buf);
+
+ /* Forget pointer */
+ td->trans_buf = NULL;
+
+ /* Allocate a new buffer */
+ buffer = gdk_rgb_image_new(td->tile_wid, td->tile_hgt);
+
+ /* Oops */
+ if (buffer == NULL)
+ {
+ /* Failure */
+ result = FALSE;
+
+ break;
+ }
+
+ /* Store it */
+ td->trans_buf = buffer;
+ }
+
+ /*
+ * Giga-Hack - assume top left corner of 0x86/0x80 should be
+ * in the background colour XXX XXX XXX XXX
+ */
+ td->bg_pixel = gdk_rgb_image_get_pixel(
+ raw_tiles,
+ 0,
+ tile_hgt * 6);
+
+ }
+
+
+ /* Alas, we need to free wasted images */
+ if (result == FALSE) graf_nuke();
+
+ /* We don't need the raw image any longer */
+ gdk_rgb_image_destroy(raw_tiles);
+
+ /* Report success or failure */
+ return (result);
+}
+
+
+/*
+ * React to various changes in graphics mode settings
+ *
+ * It is *not* a requirement for tiles to have same pixel width and height.
+ * The program can work with any conbinations of graf_wid and graf_hgt
+ * (oops, they must be representable by u16b), as long as they are lesser
+ * or equal to 32 if you use smooth rescaling.
+ */
+static void init_graphics(void)
+{
+ cptr tile_name;
+
+ u16b graf_wid = 0, graf_hgt = 0;
+
+
+ /* No graphics requests are made - Can't this be simpler? XXX XXX */
+ if ((graf_mode_request == graf_mode) &&
+ (smooth_rescaling_request == smooth_rescaling) &&
+ !resize_request) return;
+
+ /* Prevent further unsolicited reaction */
+ resize_request = FALSE;
+
+
+ /* Dispose unusable old tiles - awkward... XXX XXX */
+ if ((graf_mode_request == GRAF_MODE_NONE) ||
+ (graf_mode_request != graf_mode) ||
+ (smooth_rescaling_request != smooth_rescaling)) graf_nuke();
+
+
+ /* Setup parameters according to request */
+ switch (graf_mode_request)
+ {
+ /* ASCII - no graphics whatsoever */
+ default:
+ case GRAF_MODE_NONE:
+ {
+ tile_name = NULL;
+ use_graphics = arg_graphics = FALSE;
+
+ break;
+ }
+
+ /*
+ * 8x8 tiles originally collected for the Amiga port
+ * from several contributers by Lars Haugseth, converted
+ * to 256 colours and expanded by the Z devteam
+ *
+ * Use the "old" tile assignments
+ *
+ * Dawnmist is working on it for ToME
+ */
+ case GRAF_MODE_OLD:
+ {
+ tile_name = "8x8";
+ graf_wid = graf_hgt = 8;
+ ANGBAND_GRAF = "old";
+ use_graphics = arg_graphics = TRUE;
+
+ break;
+ }
+
+ /*
+ * Adam Bolt's 16x16 tiles
+ * "new" tile assignments
+ * It is updated for ToME by Andreas Koch
+ */
+ case GRAF_MODE_NEW:
+ {
+ tile_name = "16x16";
+ graf_wid = graf_hgt = 16;
+ ANGBAND_GRAF = "new";
+ use_graphics = arg_graphics = TRUE;
+
+ break;
+ }
+ }
+
+
+ /* load tiles and set them up if tiles are requested */
+ if ((graf_mode_request != GRAF_MODE_NONE) &&
+ !graf_init(tile_name, graf_wid, graf_hgt))
+ {
+ /* Oops */
+ plog("Cannot initialize graphics");
+
+ /* reject requests */
+ graf_mode_request = GRAF_MODE_NONE;
+ smooth_rescaling_request = smooth_rescaling;
+
+ /* reset graphics flags */
+ use_graphics = arg_graphics = FALSE;
+ }
+
+ /* Update current graphics mode */
+ graf_mode = graf_mode_request;
+ smooth_rescaling = smooth_rescaling_request;
+
+ /* Reset visuals */
+#ifndef ANG281_RESET_VISUALS
+ reset_visuals(TRUE);
+#else
+ reset_visuals();
+#endif /* !ANG281_RESET_VISUALS */
+}
+
+#endif /* USE_GRAPHICS */
+
+
+
+
+/**** Term package support routines ****/
+
+
+/*
+ * Free data used by a term
+ */
+static void Term_nuke_gtk(term *t)
+{
+ term_data *td = t->data;
+
+
+ /* Free name */
+ if (td->name) string_free(td->name);
+
+ /* Forget it */
+ td->name = NULL;
+
+ /* Free font */
+ if (td->font) gdk_font_unref(td->font);
+
+ /* Forget it */
+ td->font = NULL;
+
+ /* Free backing store */
+ if (td->backing_store) gdk_pixmap_unref(td->backing_store);
+
+ /* Forget it too */
+ td->backing_store = NULL;
+
+#ifdef USE_GRAPHICS
+
+ /* Free tiles */
+ if (td->tiles) gdk_rgb_image_destroy(td->tiles);
+
+ /* Forget pointer */
+ td->tiles = NULL;
+
+ /* Free transparency buffer */
+ if (td->trans_buf) gdk_rgb_image_destroy(td->trans_buf);
+
+ /* Amnesia */
+ td->trans_buf = NULL;
+
+#endif /* USE_GRAPHICS */
+}
+
+
+/*
+ * Erase the whole term.
+ */
+static errr Term_clear_gtk(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Clear the area */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(td),
+ td->drawing_area->style->black_gc,
+ 1,
+ 0,
+ 0,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, 0, 0, td->cols, td->rows);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase some characters.
+ */
+static errr Term_wipe_gtk(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Fill the area with the background colour */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(td),
+ td->drawing_area->style->black_gc,
+ TRUE,
+ x * td->font_wid,
+ y * td->font_hgt,
+ n * td->font_wid,
+ td->font_hgt);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, n, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some textual characters.
+ */
+static errr Term_text_gtk(int x, int y, int n, byte a, cptr s)
+{
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Set foreground colour */
+ term_data_set_fg(td, a);
+
+ /* Clear the line */
+ Term_wipe_gtk(x, y, n);
+
+ /* Draw the text to the window */
+ gdk_draw_text(
+ TERM_DATA_DRAWABLE(td),
+ td->font,
+ td->gc,
+ x * td->font_wid,
+ td->font->ascent + y * td->font_hgt,
+ s,
+ n);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, n, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw software cursor at (x, y)
+ */
+static errr Term_curs_gtk(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+ int cells = 1;
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Set foreground colour */
+ term_data_set_fg(td, TERM_YELLOW);
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Mogami's bigtile patch */
+
+ /* Adjust it if wide tiles are requested */
+ if (use_bigtile &&
+ (x + 1 < Term->wid) &&
+ (Term->old->a[y][x + 1] == 255))
+ {
+ cells = 2;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ /* Draw the software cursor */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(td),
+ td->gc,
+ FALSE,
+ x * td->font_wid,
+ y * td->font_hgt,
+ td->font_wid * cells - 1,
+ td->font_hgt - 1);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, cells, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * XXX XXX Low level graphics helper
+ * Draw a tile at (s_x, s_y) over one at (t_x, t_y) and store the
+ * result in td->trans_buf
+ *
+ * XXX XXX Even if CPU's are faster than necessary these days,
+ * this should be made inline. Or better, there should be an API
+ * to take advantage of graphics hardware. They almost always have
+ * assortment of builtin bitblt's...
+ */
+static void overlay_tiles_2(
+ term_data *td,
+ int s_x, int s_y,
+ int t_x, int t_y)
+{
+ guint32 pix;
+ int x, y;
+
+
+ /* Process each row */
+ for (y = 0; y < td->tile_hgt; y++)
+ {
+ /* Process each column */
+ for (x = 0; x < td->tile_wid; x++)
+ {
+ /* Get an overlay pixel */
+ pix = gdk_rgb_image_get_pixel(td->tiles, s_x + x, s_y + y);
+
+ /* If it's in background color, use terrain instead */
+ if (pix == td->bg_pixel)
+ pix = gdk_rgb_image_get_pixel(td->tiles, t_x + x, t_y + y);
+
+ /* Store the result in trans_buf */
+ gdk_rgb_image_put_pixel(td->trans_buf, x, y, pix);
+ }
+ }
+}
+
+
+/*
+ * XXX XXX Low level graphics helper
+ * Draw a tile at (e_x, e_y) over one at (s_x, s_y) over another one
+ * at (t_x, t_y) and store the result in td->trans_buf
+ *
+ * XXX XXX The same comment applies as that for the above...
+ */
+static void overlay_tiles_3(
+ term_data *td,
+ int e_x, int e_y,
+ int s_x, int s_y,
+ int t_x, int t_y)
+{
+ guint32 pix;
+ int x, y;
+
+
+ /* Process each row */
+ for (y = 0; y < td->tile_hgt; y++)
+ {
+ /* Process each column */
+ for (x = 0; x < td->tile_wid; x++)
+ {
+ /* Get an overlay pixel */
+ pix = gdk_rgb_image_get_pixel(td->tiles, e_x + x, e_y + y);
+
+ /*
+ * If it's background colour, try to use one from
+ * the second layer
+ */
+ if (pix == td->bg_pixel)
+ pix = gdk_rgb_image_get_pixel(td->tiles, s_x + x, s_y + y);
+
+ /*
+ * If it's background colour again, fall back to
+ * the terrain layer
+ */
+ if (pix == td->bg_pixel)
+ pix = gdk_rgb_image_get_pixel(td->tiles, t_x + x, t_y + y);
+
+ /* Store the pixel in trans_buf */
+ gdk_rgb_image_put_pixel(td->trans_buf, x, y, pix);
+ }
+ }
+}
+
+
+
+/*
+ * Low level graphics (Assumes valid input)
+ *
+ * Draw "n" tiles/characters starting at (x,y)
+ */
+static errr Term_pict_gtk(
+ int x, int y, int n,
+ const byte *ap, const char *cp,
+ const byte *tap, const char *tcp,
+ const byte *eap, const char *ecp)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ int i;
+
+ int d_x, d_y;
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Hack - remember real number of columns affected XXX XXX XXX */
+ int cols;
+
+# endif /* USE_DOUBLE_TILES */
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Top left corner of the destination rect */
+ d_x = x * td->font_wid;
+ d_y = y * td->font_hgt;
+
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Reset column counter */
+ cols = 0;
+
+# endif /* USE_DOUBLE_TILES */
+
+ /* Scan the input */
+ for (i = 0; i < n; i++)
+ {
+ byte a;
+ char c;
+ int s_x, s_y;
+
+ byte ta;
+ char tc;
+ int t_x, t_y;
+
+ byte ea;
+ char ec;
+ int e_x = 0, e_y = 0;
+ bool_ has_overlay;
+
+
+ /* Grid attr/char */
+ a = *ap++;
+ c = *cp++;
+
+ /* Terrain attr/char */
+ ta = *tap++;
+ tc = *tcp++;
+
+ /* Overlay attr/char */
+ ea = *eap++;
+ ec = *ecp++;
+ has_overlay = (ea && ec);
+
+ /* Row and Col */
+ s_y = (((byte)a & 0x7F) % tile_rows) * td->tile_hgt;
+ s_x = (((byte)c & 0x7F) % tile_cols) * td->tile_wid;
+
+ /* Terrain Row and Col */
+ t_y = (((byte)ta & 0x7F) % tile_rows) * td->tile_hgt;
+ t_x = (((byte)tc & 0x7F) % tile_cols) * td->tile_wid;
+
+ /* Overlay Row and Col */
+ if (has_overlay)
+ {
+ e_y = (((byte)ea & 0x7F) % tile_rows) * td->tile_hgt;
+ e_x = (((byte)ec & 0x7F) % tile_cols) * td->tile_wid;
+ }
+
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Mogami's bigtile patch */
+
+ /* Hack -- a filler for wide tile */
+ if (use_bigtile && (a == 255))
+ {
+ /* Advance */
+ d_x += td->font_wid;
+
+ /* Ignore */
+ continue;
+ }
+
+# endif /* USE_DOUBLE_TILES */
+
+ /* Optimise the common case: terrain == obj/mons */
+ if (!use_transparency ||
+ ((s_x == t_x) && (s_y == t_y)))
+ {
+
+ /* The simplest possible case - no overlay */
+ if (!has_overlay)
+ {
+ /* Draw the tile */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->tiles,
+ s_x, s_y,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+ }
+
+ /* We have to draw overlay... */
+ else
+ {
+ /* Overlay */
+ overlay_tiles_2(td, e_x, e_y, s_x, s_y);
+
+ /* And draw the result */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->trans_buf,
+ 0, 0,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+ /* Hack -- Prevent potential display problem */
+ gdk_flush();
+ }
+
+ }
+
+ /*
+ * Since there's no masking bitblt in X,
+ * we have to do that manually...
+ */
+ else
+ {
+
+ /* No overlay */
+ if (!has_overlay)
+ {
+ /* Build terrain + masked overlay image */
+ overlay_tiles_2(td, s_x, s_y, t_x, t_y);
+ }
+
+ /* With overlay */
+ else
+ {
+ /* Ego over mon/PC over terrain */
+ overlay_tiles_3(td, e_x, e_y, s_x, s_y,
+ t_x, t_y);
+ }
+
+ /* Draw it */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->trans_buf,
+ 0, 0,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+ /* Hack -- Prevent potential display problem */
+ gdk_flush();
+ }
+
+ /*
+ * Advance x-coordinate - wide font fillers are taken care of
+ * before entering the tile drawing code.
+ */
+ d_x += td->font_wid;
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Add up *real* number of columns updated XXX XXX XXX */
+ cols += use_bigtile ? 2 : 1;
+
+# endif /* USE_DOUBLE_TILES */
+ }
+
+# ifndef USE_DOUBLE_TILES
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, n, 1);
+
+# else
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, cols, 1);
+
+# endif /* USE_DOUBLE_TILES */
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Process an event, if there's none block when wait is set true,
+ * return immediately otherwise.
+ */
+static void CheckEvent(bool_ wait)
+{
+ /* Process an event */
+ (void)gtk_main_iteration_do(wait);
+}
+
+
+/*
+ * Process all pending events (without blocking)
+ */
+static void DrainEvents(void)
+{
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_gtk(int n, int v)
+{
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ {
+ /* Beep */
+ gdk_beep();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush the output */
+ case TERM_XTRA_FRESH:
+ {
+ /* Flush pending X requests - almost always no-op */
+ gdk_flush();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ {
+ /* Process a pending event if there's one */
+ CheckEvent(FALSE);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process Events */
+ case TERM_XTRA_EVENT:
+ {
+ /* Process an event */
+ CheckEvent(v);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush the events */
+ case TERM_XTRA_FLUSH:
+ {
+ /* Process all pending events */
+ DrainEvents();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Handle change in the "level" */
+ case TERM_XTRA_LEVEL:
+ return (0);
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ return (Term_clear_gtk());
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ {
+ /* sleep for v milliseconds */
+ usleep(v * 1000);
+
+ /* Done */
+ return (0);
+ }
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory) return (1);
+
+ while ((entry = readdir(directory)) != NULL)
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if ((stat(file, &filedata) == 0) && S_ISDIR(filedata.st_mode))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] =
+ string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN: gtk_window_set_title(GTK_WINDOW(data[0].window), angband_term_name[0]); return (0);
+
+ /* React to changes */
+ case TERM_XTRA_REACT:
+ {
+ /* (re-)init colours */
+ init_colours();
+
+#ifdef USE_GRAPHICS
+
+ /* Initialise graphics */
+ init_graphics();
+
+#endif /* USE_GRAPHICS */
+
+ /* Success */
+ return (0);
+ }
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+
+
+/**** Event handlers ****/
+
+
+/*
+ * Operation overkill
+ * Verify term size info - just because the other windowing ports have this
+ */
+static void term_data_check_size(term_data *td)
+{
+ /* Enforce minimum window size */
+ if (td == &data[0])
+ {
+ if (td->cols < 80) td->cols = 80;
+ if (td->rows < 24) td->rows = 24;
+ }
+ else
+ {
+ if (td->cols < 1) td->cols = 1;
+ if (td->rows < 1) td->rows = 1;
+ }
+
+ /* Paranoia - Enforce maximum size allowed by the term package */
+ if (td->cols > 255) td->cols = 255;
+ if (td->rows > 255) td->rows = 255;
+}
+
+
+/*
+ * Enforce these size constraints within Gtk/Gdk
+ * These increments are nice, because you can see numbers of rows/cols
+ * while you resize a term.
+ */
+static void term_data_set_geometry_hints(term_data *td)
+{
+ GdkGeometry geometry;
+
+ /* Resizing is character size oriented */
+ geometry.width_inc = td->font_wid;
+ geometry.height_inc = td->font_hgt;
+
+ /* Enforce minimum size - the main window */
+ if (td == &data[0])
+ {
+ geometry.min_width = 80 * td->font_wid;
+ geometry.min_height = 24 * td->font_hgt;
+ }
+
+ /* Subwindows can be much smaller */
+ else
+ {
+ geometry.min_width = 1 * td->font_wid;
+ geometry.min_height = 1 * td->font_hgt;
+ }
+
+ /* Enforce term package's hard limit */
+ geometry.max_width = 255 * td->font_wid;
+ geometry.max_height = 255 * td->font_hgt;
+
+ /* This affects geometry display while we resize a term */
+ geometry.base_width = 0;
+ geometry.base_height = 0;
+
+ /* Give the window a new set of resizing hints */
+ gtk_window_set_geometry_hints(GTK_WINDOW(td->window),
+ td->drawing_area, &geometry,
+ GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE
+ | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC);
+}
+
+
+/*
+ * (Re)allocate a backing store for the window
+ */
+static void term_data_set_backing_store(term_data *td)
+{
+ /* Paranoia */
+ if (!GTK_WIDGET_REALIZED(td->drawing_area)) return;
+
+ /* Free old one if we cannot use it any longer */
+ if (td->backing_store)
+ {
+ int wid, hgt;
+
+ /* Retrive the size of the old backing store */
+ gdk_window_get_size(td->backing_store, &wid, &hgt);
+
+ /* Continue using it if it's the same with desired size */
+ if (use_backing_store &&
+ (td->cols * td->font_wid == wid) &&
+ (td->rows * td->font_hgt == hgt)) return;
+
+ /* Free it */
+ gdk_pixmap_unref(td->backing_store);
+
+ /* Forget the pointer */
+ td->backing_store = NULL;
+ }
+
+ /* See user preference */
+ if (use_backing_store)
+ {
+ /* Allocate new backing store */
+ td->backing_store = gdk_pixmap_new(
+ td->drawing_area->window,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt,
+ -1);
+
+ /* Oops - but we can do without it */
+ g_return_if_fail(td->backing_store != NULL);
+
+ /* Clear the backing store */
+ gdk_draw_rectangle(
+ td->backing_store,
+ td->drawing_area->style->black_gc,
+ TRUE,
+ 0,
+ 0,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+ }
+}
+
+
+/*
+ * Save game only when it's safe to do so
+ */
+static void save_game_gtk(void)
+{
+ /* We have nothing to save, yet */
+ if (!game_in_progress || !character_generated) return;
+
+ /* It isn't safe to save game now */
+ if (!inkey_flag || !can_save)
+ {
+ plog("You may not save right now.");
+ return;
+ }
+
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+#ifdef ZANG_SAVE_GAME
+ /* Also for OAngband - the parameter tells if it's autosave */
+ do_cmd_save_game(FALSE);
+#else
+/* Everything else */
+ do_cmd_save_game();
+#endif /* ZANG_SAVE_GAME */
+}
+
+
+/*
+ * Display message in a modal dialog
+ */
+static void gtk_message(cptr msg)
+{
+ GtkWidget *dialog, *label, *ok_button;
+
+ /* Create the widgets */
+ dialog = gtk_dialog_new();
+ g_assert(dialog != NULL);
+
+ label = gtk_label_new(msg);
+ g_assert(label != NULL);
+
+ ok_button = gtk_button_new_with_label("OK");
+ g_assert(ok_button != NULL);
+
+ /* Ensure that the dialogue box is destroyed when OK is clicked */
+ gtk_signal_connect_object(
+ GTK_OBJECT(ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)dialog);
+ gtk_container_add(
+ GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+ ok_button);
+
+ /* Add the label, and show the dialog */
+ gtk_container_add(
+ GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
+ label);
+
+ /* And make it modal */
+ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+
+ /* Show the dialog */
+ gtk_widget_show_all(dialog);
+}
+
+
+/*
+ * Hook to tell the user something important
+ */
+static void hook_plog(cptr str)
+{
+ /* Warning message */
+ gtk_message(str);
+}
+
+
+/*
+ * Process File-Quit menu command
+ */
+static void quit_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* Save current game */
+ save_game_gtk();
+
+ /* It's done */
+ quit(NULL);
+}
+
+
+/*
+ * Process File-Save menu command
+ */
+static void save_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* Save current game */
+ save_game_gtk();
+}
+
+
+/*
+ * Handle destruction of the Angband window
+ */
+static void destroy_main_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ /* This allows for cheating, but... */
+ quit(NULL);
+}
+
+
+/*
+ * Handle destruction of Subwindows
+ */
+static void destroy_sub_event_handler(
+ GtkWidget *window,
+ gpointer user_data)
+{
+ /* Hide the window */
+ gtk_widget_hide_all(window);
+}
+
+
+#ifndef SAVEFILE_SCREEN
+
+/*
+ * Process File-New menu command
+ */
+static void new_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ if (game_in_progress)
+ {
+ plog("You can't start a new game while you're still playing!");
+ return;
+ }
+
+ /* The game is in progress */
+ game_in_progress = TRUE;
+
+ /* Flush input */
+ Term_flush();
+
+ /* Play game */
+ play_game(TRUE);
+
+ /* Houseclearing */
+ cleanup_angband();
+
+ /* Done */
+ quit(NULL);
+}
+
+#endif /* !SAVEFILE_SCREEN */
+
+
+/*
+ * Load fond specified by an XLFD fontname and
+ * set up related term_data members
+ */
+static void load_font(term_data *td, cptr fontname)
+{
+ GdkFont *old = td->font;
+
+ /* Load font */
+ td->font = gdk_font_load(fontname);
+
+ if (td->font)
+ {
+ /* Free the old font */
+ if (old) gdk_font_unref(old);
+ }
+ else
+ {
+ /* Oops, but we can still use the old one */
+ td->font = old;
+ }
+
+ /* Calculate the size of the font XXX */
+ td->font_wid = gdk_char_width(td->font, '@');
+ td->font_hgt = td->font->ascent + td->font->descent;
+
+#ifndef USE_DOUBLE_TILES
+
+ /* Use the current font size for tiles as well */
+ td->tile_wid = td->font_wid;
+ td->tile_hgt = td->font_hgt;
+
+#else /* !USE_DOUBLE_TILES */
+
+ /* Calculate the size of tiles */
+ if (use_bigtile && (td == &data[0])) td->tile_wid = td->font_wid * 2;
+ else td->tile_wid = td->font_wid;
+ td->tile_hgt = td->font_hgt;
+
+#endif /* !USE_DOUBLE_TILES */
+}
+
+
+/*
+ * Process Options-Font-* menu command
+ */
+static void change_font_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *widget)
+{
+ /* Not implemented */
+}
+
+
+/*
+ * Process Terms-* menu command - hide/show terminal window
+ */
+static void term_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *widget)
+{
+ term_data *td = &data[user_action];
+
+ /* We don't mess with the Angband window */
+ if (td == &data[0]) return;
+
+ /* It's shown */
+ if (td->shown)
+ {
+ /* Hide the window */
+ gtk_widget_hide_all(td->window);
+ }
+
+ /* It's hidden */
+ else
+ {
+ /* Show the window */
+ gtk_widget_show_all(td->window);
+ }
+}
+
+
+/*
+ * Toggles the boolean value of use_backing_store and
+ * setup / remove backing store for each term
+ */
+static void change_backing_store_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ int i;
+
+ /* Toggle the backing store mode */
+ use_backing_store = !use_backing_store;
+
+ /* Reset terms */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term_data_set_backing_store(&data[i]);
+ }
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Set graf_mode_request according to user selection,
+ * and let Term_xtra react to the change.
+ */
+static void change_graf_mode_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* Set request according to user selection */
+ graf_mode_request = (int)user_action;
+
+ /*
+ * Hack - force redraw
+ * This induces a call to Term_xtra(TERM_XTRA_REACT, 0) as well
+ */
+ Term_key_push(KTRL('R'));
+}
+
+
+/*
+ * Set dither_mode according to user selection
+ */
+static void change_dith_mode_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* Set request according to user selection */
+ dith_mode = (int)user_action;
+
+ /*
+ * Hack - force redraw
+ */
+ Term_key_push(KTRL('R'));
+}
+
+
+/*
+ * Toggles the graphics tile scaling mode (Fast/Smooth)
+ */
+static void change_smooth_mode_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* (Try to) toggle the smooth rescaling mode */
+ smooth_rescaling_request = !smooth_rescaling;
+
+ /*
+ * Hack - force redraw
+ * This induces a call to Term_xtra(TERM_XTRA_REACT, 0) as well
+ */
+ Term_key_push(KTRL('R'));
+}
+
+
+# ifdef USE_DOUBLE_TILES
+
+static void change_wide_tile_mode_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ term *old = Term;
+ term_data *td = &data[0];
+
+ /* Toggle "use_bigtile" */
+ use_bigtile = !use_bigtile;
+
+#ifdef TOME
+ /* T.o.M.E. requires this as well */
+ arg_bigtile = use_bigtile;
+#endif /* TOME */
+
+ /* Double the width of tiles (only for the main window) */
+ if (use_bigtile)
+ {
+ td->tile_wid = td->font_wid * 2;
+ }
+
+ /* Use the width of current font */
+ else
+ {
+ td->tile_wid = td->font_wid;
+ }
+
+ /* Need to resize the tiles */
+ resize_request = TRUE;
+
+ /* Activate the main window */
+ Term_activate(&td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Activate the old term */
+ Term_activate(old);
+
+ /* Hack - force redraw XXX ??? XXX */
+ Term_key_push(KTRL('R'));
+}
+
+# endif /* USE_DOUBLE_TILES */
+
+
+/*
+ * Toggles the boolean value of use_transparency
+ */
+static void change_trans_mode_event_handler(
+ gpointer user_data,
+ guint user_aciton,
+ GtkWidget *was_clicked)
+{
+ /* Toggle the transparency mode */
+ use_transparency = !use_transparency;
+
+ /* Hack - force redraw */
+ Term_key_push(KTRL('R'));
+}
+
+#endif /* USE_GRAPHICS */
+
+
+#ifndef SAVEFILE_SCREEN
+
+/*
+ * Caution: Modal or not, callbacks are called by gtk_main(),
+ * so this is the right place to start a game.
+ */
+static void file_ok_callback(
+ GtkWidget *widget,
+ GtkWidget *file_selector)
+{
+ strcpy(savefile,
+ gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_selector)));
+
+ gtk_widget_destroy(file_selector);
+
+ /* game is in progress */
+ game_in_progress = TRUE;
+
+ /* Flush input */
+ Term_flush();
+
+ /* Play game */
+ play_game(FALSE);
+
+ /* Free memory allocated by game */
+ cleanup_angband();
+
+ /* Done */
+ quit(NULL);
+}
+
+
+/*
+ * Process File-Open menu command
+ */
+static void open_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ GtkWidget *file_selector;
+ char buf[1024];
+
+
+ if (game_in_progress)
+ {
+ plog("You can't open a new game while you're still playing!");
+ return;
+ }
+
+ /* Prepare the savefile path */
+ path_build(buf, 1024, ANGBAND_DIR_SAVE, "*");
+
+ file_selector = gtk_file_selection_new("Select a savefile");
+ gtk_file_selection_set_filename(
+ GTK_FILE_SELECTION(file_selector),
+ buf);
+ gtk_signal_connect(
+ GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button),
+ "clicked",
+ file_ok_callback,
+ (gpointer)file_selector);
+
+ /*
+ * Ensure that the dialog box is destroyed when the user
+ * clicks a button.
+ */
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)file_selector);
+
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)file_selector);
+
+ gtk_window_set_modal(GTK_WINDOW(file_selector), TRUE);
+ gtk_widget_show(GTK_WIDGET(file_selector));
+}
+
+#endif /* !SAVEFILE_SCREEN */
+
+
+/*
+ * React to "delete" signal sent to Window widgets
+ */
+static gboolean delete_event_handler(
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ /* Save game if possible */
+ save_game_gtk();
+
+ /* Don't prevent closure */
+ return (FALSE);
+}
+
+
+/*
+ * Convert keypress events to ASCII codes and enqueue them
+ * for game
+ */
+static gboolean keypress_event_handler(
+ GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+#if 1
+ int i, mc, ms, mo, mx;
+
+ char msg[128];
+
+ /* Hack - do not do anything until the player picks from the menu */
+ if (!game_in_progress) return (TRUE);
+
+ /* Hack - Ignore parameters */
+ (void) widget;
+ (void) user_data;
+
+ /* Extract four "modifier flags" */
+ mc = (event->state & GDK_CONTROL_MASK) ? TRUE : FALSE;
+ ms = (event->state & GDK_SHIFT_MASK) ? TRUE : FALSE;
+ mo = (event->state & GDK_MOD1_MASK) ? TRUE : FALSE;
+ mx = (event->state & GDK_MOD3_MASK) ? TRUE : FALSE;
+
+ /*
+ * Hack XXX
+ * Parse shifted numeric (keypad) keys specially.
+ */
+ if ((event->state == GDK_SHIFT_MASK)
+ && (event->keyval >= GDK_KP_0) && (event->keyval <= GDK_KP_9))
+ {
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%cS_%X%c", 31, event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+ }
+
+ /* Normal keys with no modifiers */
+ if (event->length && !mo && !mx)
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; i < event->length; i++) Term_keypress(event->string[i]);
+
+ /* All done */
+ return (TRUE);
+ }
+
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch ((uint) event->keyval)
+ {
+ case GDK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return (TRUE);
+ }
+
+ case GDK_Return:
+ {
+ Term_keypress('\r');
+ return (TRUE);
+ }
+
+ case GDK_Tab:
+ {
+ Term_keypress('\t');
+ return (TRUE);
+ }
+
+ case GDK_Delete:
+ case GDK_BackSpace:
+ {
+ Term_keypress('\010');
+ return (TRUE);
+ }
+
+ case GDK_Shift_L:
+ case GDK_Shift_R:
+ case GDK_Control_L:
+ case GDK_Control_R:
+ case GDK_Caps_Lock:
+ case GDK_Shift_Lock:
+ case GDK_Meta_L:
+ case GDK_Meta_R:
+ case GDK_Alt_L:
+ case GDK_Alt_R:
+ case GDK_Super_L:
+ case GDK_Super_R:
+ case GDK_Hyper_L:
+ case GDK_Hyper_R:
+ {
+ /* Hack - do nothing to control characters */
+ return (TRUE);
+ }
+ }
+
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%c%s%s%s%s_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+
+#else
+ int i, mc, ms, mo, mx;
+
+ char msg[128];
+
+
+ /* Extract four "modifier flags" */
+ mc = (event->state & GDK_CONTROL_MASK) ? TRUE : FALSE;
+ ms = (event->state & GDK_SHIFT_MASK) ? TRUE : FALSE;
+ mo = (event->state & GDK_MOD1_MASK) ? TRUE : FALSE;
+ mx = (event->state & GDK_MOD3_MASK) ? TRUE : FALSE;
+ printf("0=%d 9=%d;; keyval=%d; mc=%d, ms=%d ::=:: ", GDK_KP_0, GDK_KP_9, event->keyval, mc, ms);
+ /* Enqueue the normal key(s) */
+ for (i = 0; i < event->length; i++) printf("%d;", event->string[i]);
+ printf("\n");
+
+ /*
+ * Hack XXX
+ * Parse shifted numeric (keypad) keys specially.
+ */
+ if ((event->state & GDK_SHIFT_MASK)
+ && (event->keyval >= GDK_KP_Left) && (event->keyval <= GDK_KP_Delete))
+ {
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%cS_%X%c", 31, event->keyval, 13);
+ printf("%cS_%X%c", 31, event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+ }
+
+ /* Normal keys with no modifiers */
+ if (event->length && !mo && !mx)
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; i < event->length; i++) Term_keypress(event->string[i]);
+
+ /* All done */
+ return (TRUE);
+ }
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch ((uint) event->keyval)
+ {
+ case GDK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return (TRUE);
+ }
+
+ case GDK_Return:
+ {
+ Term_keypress('\r');
+ return (TRUE);
+ }
+
+ case GDK_Tab:
+ {
+ Term_keypress('\t');
+ return (TRUE);
+ }
+
+ case GDK_Delete:
+ case GDK_BackSpace:
+ {
+ Term_keypress('\010');
+ return (TRUE);
+ }
+
+ case GDK_Shift_L:
+ case GDK_Shift_R:
+ case GDK_Control_L:
+ case GDK_Control_R:
+ case GDK_Caps_Lock:
+ case GDK_Shift_Lock:
+ case GDK_Meta_L:
+ case GDK_Meta_R:
+ case GDK_Alt_L:
+ case GDK_Alt_R:
+ case GDK_Super_L:
+ case GDK_Super_R:
+ case GDK_Hyper_L:
+ case GDK_Hyper_R:
+ {
+ /* Hack - do nothing to control characters */
+ return (TRUE);
+ }
+ }
+
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%c%s%s%s%s_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+#endif
+}
+
+
+/*
+ * Widget customisation (for drawing area) - "realize" signal
+ *
+ * In this program, called when window containing the drawing
+ * area is shown first time.
+ */
+static void realize_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* Create graphic context */
+ td->gc = gdk_gc_new(td->drawing_area->window);
+
+ /* Set foreground and background colours - isn't bg used at all? */
+ gdk_rgb_gc_set_background(td->gc, 0x000000);
+ gdk_rgb_gc_set_foreground(td->gc, angband_colours[TERM_WHITE]);
+
+ /* No last foreground colour, yet */
+ td->last_attr = -1;
+
+ /* Allocate the backing store */
+ term_data_set_backing_store(td);
+
+ /* Clear the window */
+ gdk_draw_rectangle(
+ widget->window,
+ widget->style->black_gc,
+ TRUE,
+ 0,
+ 0,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+}
+
+
+/*
+ * Widget customisation (for drawing area) - "show" signal
+ */
+static void show_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* Set the shown flag */
+ td->shown = TRUE;
+}
+
+
+/*
+ * Widget customisation (for drawing area) - "hide" signal
+ */
+static void hide_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* Set the shown flag */
+ td->shown = FALSE;
+}
+
+
+/*
+ * Widget customisation (for drawing area)- handle size allocation requests
+ */
+static void size_allocate_event_handler(
+ GtkWidget *widget,
+ GtkAllocation *allocation,
+ gpointer user_data)
+{
+ term_data *td = user_data;
+ int old_rows, old_cols;
+ term *old = Term;
+
+ /* Paranoia */
+ g_return_if_fail(widget != NULL);
+ g_return_if_fail(allocation != NULL);
+ g_return_if_fail(td != NULL);
+
+ /* Remember old values */
+ old_cols = td->cols;
+ old_rows = td->rows;
+
+ /* Update numbers of rows and columns */
+ td->cols = (allocation->width + td->font_wid - 1) / td->font_wid;
+ td->rows = (allocation->height + td->font_hgt - 1) / td->font_hgt;
+
+ /* Overkill - Validate them */
+ term_data_check_size(td);
+
+ /* Adjust size request and set it */
+ allocation->width = td->cols * td->font_wid;
+ allocation->height = td->rows * td->font_hgt;
+ widget->allocation = *allocation;
+
+ /* Widget is realized, so we do some drawing works */
+ if (GTK_WIDGET_REALIZED(widget))
+ {
+ /* Reallocate the backing store */
+ term_data_set_backing_store(td);
+
+ /* Actually handles resizing in Gtk */
+ gdk_window_move_resize(
+ widget->window,
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+
+ /* And in the term package */
+ Term_activate(&td->t);
+
+ /* Resize if necessary */
+ if ((td->cols != old_cols) || (td->rows != old_rows))
+ (void)Term_resize(td->cols, td->rows);
+
+ /* Redraw its content */
+ Term_redraw();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Update exposed area in a window (for drawing area)
+ */
+static gboolean expose_event_handler(
+ GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer user_data)
+{
+ term_data *td = user_data;
+
+ term *old = Term;
+
+#ifndef NO_REDRAW_SECTION
+
+ int x1, x2, y1, y2;
+
+#endif /* !NO_REDRAW_SECTION */
+
+
+ /* Paranoia */
+ if (td == NULL) return (TRUE);
+
+ /* The window has a backing store */
+ if (td->backing_store)
+ {
+ /* Simply restore the exposed area from the backing store */
+ gdk_draw_pixmap(
+ td->drawing_area->window,
+ td->gc,
+ td->backing_store,
+ event->area.x,
+ event->area.y,
+ event->area.x,
+ event->area.y,
+ event->area.width,
+ event->area.height);
+ }
+
+ /* No backing store - use the game's code to redraw the area */
+ else
+ {
+
+ /* Activate the relevant term */
+ Term_activate(&td->t);
+
+# ifdef NO_REDRAW_SECTION
+
+ /* K.I.S.S. version */
+
+ /* Redraw */
+ Term_redraw();
+
+# else /* NO_REDRAW_SECTION */
+
+ /*
+ * Complex version - The above is enough, but since we have
+ * Term_redraw_section... This might help if we had a graphics
+ * mode.
+ */
+
+ /* Convert coordinate in pixels to character cells */
+ x1 = event->area.x / td->font_wid;
+ x2 = (event->area.x + event->area.width) / td->font_wid;
+ y1 = event->area.y / td->font_hgt;
+ y2 = (event->area.y + event->area.height) / td->font_hgt;
+
+ /*
+ * No paranoia - boundary checking is done in
+ * Term_redraw_section
+ */
+
+ /* Redraw the area */
+ Term_redraw_section(x1, y1, x2, y2);
+
+# endif /* NO_REDRAW_SECTION */
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+
+ /* We've processed the event ourselves */
+ return (TRUE);
+}
+
+
+
+
+/**** Initialisation ****/
+
+/*
+ * Initialise a term_data struct
+ */
+static errr term_data_init(term_data *td, int i)
+{
+ term *t = &td->t;
+ char *p;
+
+ td->cols = 80;
+ td->rows = 24;
+
+ /* Initialize the term */
+ term_init(t, td->cols, td->rows, 1024);
+
+ /* Store the name of the term */
+ td->name = string_make(angband_term_name[i]);
+
+ /* Instance names should start with a lowercase letter XXX */
+ for (p = (char *)td->name; *p; p++) *p = tolower(*p);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ t->xtra_hook = Term_xtra_gtk;
+ t->text_hook = Term_text_gtk;
+ t->wipe_hook = Term_wipe_gtk;
+ t->curs_hook = Term_curs_gtk;
+#ifdef USE_GRAPHICS
+ t->pict_hook = Term_pict_gtk;
+#endif /* USE_GRAPHICS */
+ t->nuke_hook = Term_nuke_gtk;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Neater menu code with GtkItemFactory.
+ *
+ * Menu bar of the Angband window
+ *
+ * Entry format: Path, Accelerator, Callback, Callback arg, type
+ * where type is one of:
+ * <Item> - simple item, alias NULL
+ * <Branch> - has submenu
+ * <Separator> - as you read it
+ * <CheckItem> - has a check mark
+ * <ToggleItem> - is a toggle
+ */
+static GtkItemFactoryEntry main_menu_items[] =
+{
+ /* "File" menu */
+ { "/File", NULL,
+ NULL, 0, "<Branch>", NULL
+ },
+#ifndef SAVEFILE_SCREEN
+ { "/File/New", "<mod1>N",
+ new_event_handler, 0, NULL, NULL },
+ { "/File/Open", "<mod1>O",
+ open_event_handler, 0, NULL, NULL },
+ { "/File/sep1", NULL,
+ NULL, 0, "<Separator>", NULL },
+#endif /* !SAVEFILE_SCREEN */
+ { "/File/Save", "<mod1>S",
+ save_event_handler, 0, NULL, NULL },
+ { "/File/Quit", "<mod1>Q",
+ quit_event_handler, 0, NULL, NULL },
+
+ /* "Terms" menu */
+ { "/Terms", NULL,
+ NULL, 0, "<Branch>", NULL },
+ /* XXX XXX XXX NULL's are replaced by the program */
+ { NULL, "<mod1>0",
+ term_event_handler, 0, "<CheckItem>", NULL },
+ { NULL, "<mod1>1",
+ term_event_handler, 1, "<CheckItem>", NULL },
+ { NULL, "<mod1>2",
+ term_event_handler, 2, "<CheckItem>", NULL },
+ { NULL, "<mod1>3",
+ term_event_handler, 3, "<CheckItem>", NULL },
+ { NULL, "<mod1>4",
+ term_event_handler, 4, "<CheckItem>", NULL },
+ { NULL, "<mod1>5",
+ term_event_handler, 5, "<CheckItem>", NULL },
+ { NULL, "<mod1>6",
+ term_event_handler, 6, "<CheckItem>", NULL },
+ { NULL, "<mod1>7",
+ term_event_handler, 7, "<CheckItem>", NULL },
+
+ /* "Options" menu */
+ { "/Options", NULL,
+ NULL, 0, "<Branch>", NULL },
+
+ /* "Font" submenu */
+ { "/Options/Font", NULL,
+ NULL, 0, "<Branch>", NULL },
+ /* XXX XXX XXX Again, NULL's are filled by the program */
+ { NULL, NULL,
+ change_font_event_handler, 0, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 1, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 2, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 3, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 4, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 5, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 6, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 7, NULL, NULL },
+
+#ifdef USE_GRAPHICS
+
+ /* "Graphics" submenu */
+ { "/Options/Graphics", NULL,
+ NULL, 0, "<Branch>", NULL },
+ { "/Options/Graphics/None", NULL,
+ change_graf_mode_event_handler, GRAF_MODE_NONE, "<CheckItem>", NULL },
+ { "/Options/Graphics/Old", NULL,
+ change_graf_mode_event_handler, GRAF_MODE_OLD, "<CheckItem>", NULL },
+ { "/Options/Graphics/New", NULL,
+ change_graf_mode_event_handler, GRAF_MODE_NEW, "<CheckItem>", NULL },
+# ifdef USE_DOUBLE_TILES
+ { "/Options/Graphics/sep3", NULL,
+ NULL, 0, "<Separator>", NULL },
+ { "/Options/Graphics/Wide tiles", NULL,
+ change_wide_tile_mode_event_handler, 0, "<CheckItem>", NULL },
+# endif /* USE_DOUBLE_TILES */
+ { "/Options/Graphics/sep1", NULL,
+ NULL, 0, "<Separator>", NULL },
+ { "/Options/Graphics/Dither if <= 8bpp", NULL,
+ change_dith_mode_event_handler, GDK_RGB_DITHER_NORMAL, "<CheckItem>", NULL },
+ { "/Options/Graphics/Dither if <= 16bpp", NULL,
+ change_dith_mode_event_handler, GDK_RGB_DITHER_MAX, "<CheckItem>", NULL },
+ { "/Options/Graphics/sep2", NULL,
+ NULL, 0, "<Separator>", NULL },
+ { "/Options/Graphics/Smoothing", NULL,
+ change_smooth_mode_event_handler, 0, "<CheckItem>", NULL },
+ { "/Options/Graphics/Transparency", NULL,
+ change_trans_mode_event_handler, 0, "<CheckItem>", NULL },
+
+#endif /* USE_GRAPHICS */
+
+ /* "Misc" submenu */
+ { "/Options/Misc", NULL,
+ NULL, 0, "<Branch>", NULL },
+ { "/Options/Misc/Backing store", NULL,
+ change_backing_store_event_handler, 0, "<CheckItem>", NULL },
+};
+
+
+/*
+ * XXX XXX Fill those NULL's in the menu definition with
+ * angband_term_name[] strings
+ */
+static void setup_menu_paths(void)
+{
+ int i;
+ int nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]);
+ GtkItemFactoryEntry *term_entry, *font_entry;
+ char buf[64];
+
+ /* Find the "Terms" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Terms")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ term_entry = &main_menu_items[i + 1];
+
+ /* Find "Font" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Options/Font")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ font_entry = &main_menu_items[i + 1];
+
+ /* For each terminal */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* XXX XXX Build the real path name to the entry */
+ strnfmt(buf, 64, "/Terms/%s", angband_term_name[i]);
+
+ /* XXX XXX Store it in the menu definition */
+ term_entry[i].path = (gchar*)string_make(buf);
+
+ /* XXX XXX Build the real path name to the entry */
+ strnfmt(buf, 64, "/Options/Font/%s", angband_term_name[i]);
+
+ /* XXX XXX Store it in the menu definition */
+ font_entry[i].path = (gchar*)string_make(buf);
+ }
+}
+
+
+/*
+ * XXX XXX Free strings allocated by setup_menu_paths()
+ */
+static void free_menu_paths(void)
+{
+ int i;
+ int nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]);
+ GtkItemFactoryEntry *term_entry, *font_entry;
+
+ /* Find the "Terms" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Terms")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ term_entry = &main_menu_items[i + 1];
+
+ /* Find "Font" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Options/Font")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ font_entry = &main_menu_items[i + 1];
+
+ /* For each terminal */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* XXX XXX Free Term menu path */
+ if (term_entry[i].path) string_free((cptr)term_entry[i].path);
+
+ /* XXX XXX Free Font menu path */
+ if (font_entry[i].path) string_free((cptr)font_entry[i].path);
+ }
+}
+
+
+/*
+ * Find widget corresponding to path name
+ * return NULL on error
+ */
+static GtkWidget *get_widget_from_path(cptr path)
+{
+ GtkItemFactory *item_factory;
+ GtkWidget *widget;
+
+ /* Paranoia */
+ if (path == NULL) return (NULL);
+
+ /* Look up item factory */
+ item_factory = gtk_item_factory_from_path(path);
+
+ /* Oops */
+ if (item_factory == NULL) return (NULL);
+
+ /* Look up widget */
+ widget = gtk_item_factory_get_widget(item_factory, path);
+
+ /* Return result */
+ return (widget);
+}
+
+
+/*
+ * Enable/disable a menu item
+ */
+void enable_menu_item(cptr path, bool_ enabled)
+{
+ GtkWidget *widget;
+
+ /* Access menu item widget */
+ widget = get_widget_from_path(path);
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU_ITEM(widget));
+
+ /*
+ * In Gtk's terminology, enabled is sensitive
+ * and disabled insensitive
+ */
+ gtk_widget_set_sensitive(widget, enabled);
+}
+
+
+/*
+ * Check/uncheck a menu item. The item should be of the GtkCheckMenuItem type
+ */
+void check_menu_item(cptr path, bool_ checked)
+{
+ GtkWidget *widget;
+
+ /* Access menu item widget */
+ widget = get_widget_from_path(path);
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_CHECK_MENU_ITEM(widget));
+
+ /*
+ * Put/remove check mark
+ *
+ * Mega-Hack -- The function supposed to be used here,
+ * gtk_check_menu_item_set_active(), emits an "activate" signal
+ * to the GtkMenuItem class of the widget, as if the menu item
+ * were selected by user, thereby causing bizarre behaviour.
+ * XXX XXX XXX
+ */
+ GTK_CHECK_MENU_ITEM(widget)->active = checked;
+}
+
+
+/*
+ * Update the "File" menu
+ */
+static void file_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+#ifndef SAVEFILE_SCREEN
+ bool_ game_start_ok;
+#endif /* !SAVEFILE_SCREEN */
+ bool_ save_ok, quit_ok;
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Can we start a game now? */
+ game_start_ok = !game_in_progress;
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Cave we save/quit now? */
+ if (!character_generated || !game_in_progress)
+ {
+ save_ok = FALSE;
+ quit_ok = TRUE;
+ }
+ else
+ {
+ if (inkey_flag && can_save) save_ok = quit_ok = TRUE;
+ else save_ok = quit_ok = FALSE;
+ }
+
+ /* Enable / disable menu items according to those conditions */
+#ifndef SAVEFILE_SCREEN
+ enable_menu_item("<Angband>/File/New", game_start_ok);
+ enable_menu_item("<Angband>/File/Open", game_start_ok);
+#endif /* !SAVEFILE_SCREEN */
+ enable_menu_item("<Angband>/File/Save", save_ok);
+ enable_menu_item("<Angband>/File/Quit", quit_ok);
+}
+
+
+/*
+ * Update the "Terms" menu
+ */
+static void term_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ int i;
+ char buf[64];
+
+ /* For each term */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Build the path name */
+ strnfmt(buf, 64, "<Angband>/Terms/%s", angband_term_name[i]);
+
+ /* Update the check mark on the item */
+ check_menu_item(buf, data[i].shown);
+ }
+}
+
+
+/*
+ * Update the "Font" submenu
+ */
+static void font_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ int i;
+ char buf[64];
+
+ /* For each term */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Build the path name */
+ strnfmt(buf, 64, "<Angband>/Options/Font/%s", angband_term_name[i]);
+
+ /* Enable selection if the term is shown */
+ enable_menu_item(buf, data[i].shown);
+ }
+}
+
+
+/*
+ * Update the "Misc" submenu
+ */
+static void misc_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ /* Update an item */
+ check_menu_item(
+ "<Angband>/Options/Misc/Backing store",
+ use_backing_store);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Update the "Graphics" submenu
+ */
+static void graf_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ /* Update menu items */
+ check_menu_item(
+ "<Angband>/Options/Graphics/None",
+ (graf_mode == GRAF_MODE_NONE));
+ check_menu_item(
+ "<Angband>/Options/Graphics/Old",
+ (graf_mode == GRAF_MODE_OLD));
+ check_menu_item(
+ "<Angband>/Options/Graphics/New",
+ (graf_mode == GRAF_MODE_NEW));
+
+#ifdef USE_DOUBLE_TILES
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Wide tiles",
+ use_bigtile);
+
+#endif /* USE_DOUBLE_TILES */
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Dither if <= 8bpp",
+ (dith_mode == GDK_RGB_DITHER_NORMAL));
+ check_menu_item(
+ "<Angband>/Options/Graphics/Dither if <= 16bpp",
+ (dith_mode == GDK_RGB_DITHER_MAX));
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Smoothing",
+ smooth_rescaling);
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Transparency",
+ use_transparency);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Construct a menu hierarchy using GtkItemFactory, setting up
+ * callbacks and accelerators along the way, and return
+ * a GtkMenuBar widget.
+ */
+GtkWidget *get_main_menu(term_data *td)
+{
+ GtkItemFactory *item_factory;
+ GtkAccelGroup *accel_group;
+ gint nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]);
+
+
+ /* XXX XXX Setup path names in the "Terms" and "Font" menus */
+ setup_menu_paths();
+
+ /* Allocate an accelerator group */
+ accel_group = gtk_accel_group_new();
+ g_assert(accel_group != NULL);
+
+ /* Initialise the item factory */
+ item_factory = gtk_item_factory_new(
+ GTK_TYPE_MENU_BAR,
+ "<Angband>",
+ accel_group);
+ g_assert(item_factory != NULL);
+
+ /* Generate the menu items */
+ gtk_item_factory_create_items(
+ item_factory,
+ nmenu_items,
+ main_menu_items,
+ NULL);
+
+ /* Attach the new accelerator group to the window */
+ gtk_window_add_accel_group(
+ GTK_WINDOW(td->window),
+ accel_group);
+
+ /* Return the actual menu bar created */
+ return (gtk_item_factory_get_widget(item_factory, "<Angband>"));
+}
+
+
+/*
+ * Install callbacks to update menus
+ */
+static void add_menu_update_callbacks()
+{
+ GtkWidget *widget;
+
+ /* Access the "File" menu */
+ widget = get_widget_from_path("<Angband>/File");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(file_menu_update_handler),
+ NULL);
+
+ /* Access the "Terms" menu */
+ widget = get_widget_from_path("<Angband>/Terms");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(term_menu_update_handler),
+ NULL);
+
+ /* Access the "Font" menu */
+ widget = get_widget_from_path("<Angband>/Options/Font");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(font_menu_update_handler),
+ NULL);
+
+ /* Access the "Misc" menu */
+ widget = get_widget_from_path("<Angband>/Options/Misc");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(misc_menu_update_handler),
+ NULL);
+
+#ifdef USE_GRAPHICS
+
+ /* Access Graphics menu */
+ widget = get_widget_from_path("<Angband>/Options/Graphics");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(graf_menu_update_handler),
+ NULL);
+
+#endif /* USE_GRAPHICS */
+}
+
+
+/*
+ * Create Gtk widgets for a terminal window and set up callbacks
+ */
+static void init_gtk_window(term_data *td, int i)
+{
+ GtkWidget *menu_bar = NULL, *box;
+ cptr font;
+
+ bool_ main_window = (i == 0) ? TRUE : FALSE;
+
+
+ /* Create window */
+ td->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ /* Set title */
+ gtk_window_set_title(GTK_WINDOW(td->window), td->name);
+
+
+ /* Get default font for this term */
+ font = get_default_font(i);
+
+ /* Load font and initialise related term_data fields */
+ load_font(td, font);
+
+
+ /* Create drawing area */
+ td->drawing_area = gtk_drawing_area_new();
+
+ /* Set the size of the drawing area */
+ gtk_drawing_area_size(
+ GTK_DRAWING_AREA(td->drawing_area),
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+
+ /* Set geometry hints */
+ term_data_set_geometry_hints(td);
+
+
+ /* Install window event handlers */
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(delete_event_handler),
+ NULL);
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "key_press_event",
+ GTK_SIGNAL_FUNC(keypress_event_handler),
+ NULL);
+
+ /* Destroying the Angband window terminates the game */
+ if (main_window)
+ {
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "destroy_event",
+ GTK_SIGNAL_FUNC(destroy_main_event_handler),
+ NULL);
+ }
+
+ /* The other windows are just hidden */
+ else
+ {
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "destroy_event",
+ GTK_SIGNAL_FUNC(destroy_sub_event_handler),
+ td);
+ }
+
+
+ /* Install drawing area event handlers */
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "realize",
+ GTK_SIGNAL_FUNC(realize_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "show",
+ GTK_SIGNAL_FUNC(show_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "hide",
+ GTK_SIGNAL_FUNC(hide_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "size_allocate",
+ GTK_SIGNAL_FUNC(size_allocate_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "expose_event",
+ GTK_SIGNAL_FUNC(expose_event_handler),
+ (gpointer)td);
+
+
+ /* Create menu */
+ if (main_window)
+ {
+ /* Build the main menu bar */
+ menu_bar = get_main_menu(td);
+ g_assert(menu_bar != NULL);
+
+ /* Since it's tedious to scatter the menu update code around */
+ add_menu_update_callbacks();
+ }
+
+
+ /* Pack the menu bar together with the main window */
+ /* For vertical placement of the menu bar and the drawing area */
+ box = gtk_vbox_new(FALSE, 0);
+
+ /* Let the window widget own it */
+ gtk_container_add(GTK_CONTAINER(td->window), box);
+
+ /* The main window has a menu bar */
+ if (main_window)
+ gtk_box_pack_start(
+ GTK_BOX(box),
+ menu_bar,
+ FALSE,
+ FALSE,
+ NO_PADDING);
+
+ /* And place the drawing area just beneath it */
+ gtk_box_pack_start_defaults(GTK_BOX(box), td->drawing_area);
+
+
+ /* Show the widgets - use of td->shown is a dirty hack XXX XXX */
+ if (td->shown) gtk_widget_show_all(td->window);
+}
+
+
+/*
+ * To be hooked into quit(). See z-util.c
+ */
+static void hook_quit(cptr str)
+{
+ /* Free menu paths dynamically allocated */
+ free_menu_paths();
+
+# ifdef USE_GRAPHICS
+
+ /* Free pathname string */
+ if (ANGBAND_DIR_XTRA_GRAF) string_free(ANGBAND_DIR_XTRA_GRAF);
+
+# endif /* USE_GRAPHICS */
+
+ /* Terminate the program */
+ gtk_exit(0);
+}
+
+
+#ifdef ANGBAND300
+
+/*
+ * Help message for this port
+ */
+const char help_gtk[] =
+ "GTK for X11, subopts -n<windows>\n"
+ " -b(acking store off)\n"
+#ifdef USE_GRAPHICS
+ " -g(raphics) -o(ld graphics) -s(moothscaling off) \n"
+ " -t(ransparency on)\n"
+# ifdef USE_DOUBLE_TILES
+ " -w(ide tiles)\n"
+# endif /* USE_DOUBLE_TILES */
+#endif /* USE_GRAPHICS */
+ " and standard GTK options";
+
+#endif /* ANGBAND300 */
+
+
+/*
+ * Initialization function
+ */
+errr init_gtk2(int argc, char **argv)
+{
+ int i;
+
+
+ /* Initialize the environment */
+ gtk_init(&argc, &argv);
+
+ /* Activate hooks - Use gtk/glib interface throughout */
+ ralloc_aux = hook_ralloc;
+ rnfree_aux = hook_rnfree;
+ quit_aux = hook_quit;
+ core_aux = hook_quit;
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ /* Number of terminals displayed at start up */
+ if (prefix(argv[i], "-n"))
+ {
+ num_term = atoi(&argv[i][2]);
+ if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA;
+ else if (num_term < 1) num_term = 1;
+ continue;
+ }
+
+ /* Disable use of pixmaps as backing store */
+ if (streq(argv[i], "-b"))
+ {
+ use_backing_store = FALSE;
+ continue;
+ }
+
+#ifdef USE_GRAPHICS
+
+ /* Requests "old" graphics */
+ if (streq(argv[i], "-o"))
+ {
+ graf_mode_request = GRAF_MODE_OLD;
+ continue;
+ }
+
+ /* Requests "new" graphics */
+ if (streq(argv[i], "-g"))
+ {
+ graf_mode_request = GRAF_MODE_NEW;
+ continue;
+ }
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Requests wide tile mode */
+ if (streq(argv[i], "-w"))
+ {
+ use_bigtile = TRUE;
+# ifdef TOME
+ /* T.o.M.E. uses older version of the patch */
+ arg_bigtile = TRUE;
+# endif /* TOME */
+ continue;
+ }
+
+# endif /* USE_DOUBLE_TILES */
+
+
+ /* Enable transparency effect */
+ if (streq(argv[i], "-t"))
+ {
+ use_transparency = TRUE;
+ continue;
+ }
+
+ /* Disable smooth rescaling of tiles */
+ if (streq(argv[i], "-s"))
+ {
+ smooth_rescaling_request = FALSE;
+ continue;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* None of the above */
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+#ifdef USE_GRAPHICS
+
+ {
+ char path[1024];
+
+ /* Build the "graf" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "graf");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_GRAF = string_make(path);
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* Initialise colours */
+ gdk_rgb_init();
+ gtk_widget_set_default_colormap(gdk_rgb_get_cmap());
+ gtk_widget_set_default_visual(gdk_rgb_get_visual());
+ init_colours();
+
+ /*
+ * Initialise the windows backwards, so that
+ * the Angband window comes in front
+ */
+ for (i = MAX_TERM_DATA - 1; i >= 0; i--)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+
+ /* Hack - Set the shown flag, meaning "to be shown" XXX XXX */
+ if (i < num_term) td->shown = TRUE;
+ else td->shown = FALSE;
+
+ /* Save global entry */
+ angband_term[i] = Term;
+
+ /* Init the window */
+ init_gtk_window(td, i);
+ }
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Set the system suffix */
+ ANGBAND_SYS = "gtk";
+
+ /* Catch nasty signals */
+ signals_init();
+
+ /* Initialize */
+ init_angband();
+
+#ifndef OLD_SAVEFILE_CODE
+
+ /* Hack - because this port has New/Open menus XXX */
+ savefile[0] = '\0';
+
+#endif /* !OLD_SAVEFILE_CODE */
+
+ /* Prompt the user */
+ prt("[Choose 'New' or 'Open' from the 'File' menu]", 23, 17);
+ Term_fresh();
+
+ /* Activate more hook */
+ plog_aux = hook_plog;
+
+
+ /* Processing loop */
+ gtk_main();
+
+
+ /* Free allocated memory */
+ cleanup_angband();
+
+ /* Stop now */
+ quit(NULL);
+
+#else /* !SAVEFILE_SCREEN */
+
+ /* Activate more hook */
+ plog_aux = hook_plog;
+
+ /* It's too early to set this, but cannot do so elsewhere XXX XXX */
+ game_in_progress = TRUE;
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GTK2 */
diff --git a/src/main-sdl.c b/src/main-sdl.c
new file mode 100644
index 00000000..1b53cfc7
--- /dev/null
+++ b/src/main-sdl.c
@@ -0,0 +1,2253 @@
+/* Copyright (C) 2003-2004 Neil Stevens <neil@hakubi.us>
+ // Copyright (C) 2004 Ethan Stump <estump@seas.upenn.edu>
+ //
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
+ // of this software and associated documentation files (the "Software"), to deal
+ // in the Software without restriction, including without limitation the rights
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ // copies of the Software, and to permit persons to whom the Software is
+ // furnished to do so, subject to the following conditions:
+ //
+ // The above copyright notice and this permission notice shall be included in
+ // all copies or substantial portions of the Software.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ // THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ // AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ //
+ // Except as contained in this notice, the name(s) of the author(s) shall not be
+ // used in advertising or otherwise to promote the sale, use or other dealings
+ // in this Software without prior written authorization from the author(s).
+ */
+
+#ifdef USE_SDL
+
+#include "angband.h"
+#include <SDL.h>
+#include <SDL_image.h>
+#include <SDL_ttf.h>
+
+#include <math.h>
+
+/*************************************************
+ GLOBAL SDL-ToME PROPERTIES
+ *************************************************/
+
+/*Main window properties that may be loaded at runtime from
+a preference file or environmental variables. However,
+default values (defined above) can be used. */
+static int arg_width = 0;
+static int arg_height = 0;
+static int arg_bpp = 16;
+
+/**************/
+
+/* Default font properties - used unless otherwise changed.
+These properties are the size and also default font to load. */
+#define DEF_FONT_SIZE 14
+#define DEF_FONT_FILE "VeraMono.ttf"
+
+/* The font properties that may perhaps be changed at runtime,
+due to environmental variables, preference files, or in-program
+commands.*/
+static int arg_font_size = DEF_FONT_SIZE;
+static char arg_font_name[64] = DEF_FONT_FILE;
+
+/**************/
+
+/* Graphics setting - signifies what graphics to use. Valid ints
+are available with given defines */
+
+/* No graphics - use only colored text */
+#define NO_GRAPHICS 0
+/* "Old" graphics - use 8x8.bmp to extract graphics tiles */
+#define GRAPHICS_8x8 8
+/* "New" graphics - use 16x16.bmp as tiles and apply mask.bmp for transparency*/
+#define GRAPHICS_16x16 16
+
+static int arg_graphics_type = NO_GRAPHICS;
+
+
+/**************/
+
+/* The number of term_data structures to set aside mem for */
+#define MAX_CONSOLE_COUNT 8
+
+/* The number of consoles that are actually being used.
+This number could be changed via preference files, environmental
+variables, command-line arguments, or possibly even in-game
+keypresses or menu-selections. */
+static int arg_console_count = 1;
+
+/* When rendering multiple terminals, each is drawn with a
+surrounding border. This value controls the width of this
+border */
+#define BORDER_THICKNESS 1
+
+/**************/
+
+/* some miscellaneous settings which have not been dealt
+with yet */
+static bool_ arg_double_width = FALSE;
+
+/* flag signifying whether the game is in full screen */
+static bool_ arg_full_screen = FALSE;
+
+/* a flag to show whether window properties have been
+set or not... if so, the properties can be dumped
+upon quit*/
+static bool_ window_properties_set = FALSE;
+
+/*************************************************
+ GLOBAL SDL-ToME VARIABLES
+ *************************************************/
+
+/* the main screen to draw to */
+static SDL_Surface *screen;
+
+/* the video settings for the system */
+static SDL_VideoInfo *videoInfo;
+
+/* a flag to suspend updating of the screen;
+this is in place so that when a large area is being
+redrawn -- like when doing a Term_redraw() or when
+redoing the entire screen -- all of the changes
+can be stored up before doing an update. This
+should cut down on screen flicker */
+static bool_ suspendUpdate = FALSE;
+
+/* some helper surfaces that are used for rendering
+characters */
+static SDL_Surface *worksurf;
+static SDL_Surface *crayon;
+
+/* the cursor surface */
+static SDL_Surface *cursor = NULL;
+
+/* the array of pre-rendered characters
+(see loadAndRenderFont() below) */
+SDL_Surface *text[128];
+
+/* the actual TTF_Font used (XXX should get rid of this)*/
+TTF_Font *font=0;
+
+/* the width and height of the uniformly-sized pre-rendered
+characters */
+int t_width = 0, t_height = 0;
+
+
+/*************************************************
+ COLOR SETUP
+ *************************************************/
+
+/* Simple black, mapped using the format of the main screen */
+int screen_black;
+
+/* The color to use for the cursor */
+static int cursor_color = 0;
+/* default cursor color is a semi-transparent yellow */
+#define DEF_CURSOR_COLOR 255,255,0,128
+
+/* The array of colors, mapped to the format of the crayon surface,
+since this is ultimately the surface that color is begin applied to */
+static int color_data[16];
+
+/* The following macro is for color defining...
+ Note that the color is fully opaque... */
+#define COLOR(r,g,b) \
+ SDL_MapRGBA(crayon->format,r,g,b,SDL_ALPHA_OPAQUE)
+
+/*These color macros will setup the colors to use, but must be called after
+ the SDL video has been set. That way SDL can correct for any funky video
+ setttings. */
+
+#define BLACK COLOR( 0, 0, 0) /* 0*/
+#define WHITE COLOR(255,255,255) /* 1*/
+#define MID_GREY COLOR(128,128,128) /* 2*/
+#define BRIGHT_ORANGE COLOR(255,128, 0) /* 3*/
+#define RED COLOR(192, 0, 0) /* 4*/
+#define GREEN COLOR( 0,128, 64) /* 5*/
+#define BRIGHT_BLUE COLOR( 0, 0,255) /* 6*/
+#define DARK_ORANGE COLOR(128, 64, 0) /* 7*/
+#define DARK_GREY COLOR( 64, 64, 64) /* 8*/
+#define BRIGHT_GREY COLOR(192,192,192) /* 9*/
+#define PURPLE COLOR(255, 0,255) /*10*/
+#define YELLOW COLOR(255,255, 0) /*11*/
+#define BRIGHT_RED COLOR(255, 0, 0) /*12*/
+#define BRIGHT_GREEN COLOR( 0,255, 0) /*13*/
+#define AQUAMARINE COLOR( 0,255,255) /*14*/
+#define BROWN COLOR(192,128, 64) /*15*/
+
+/*************************************************
+ TERMINAL DATA STRUCTURE SETUP
+ *************************************************/
+
+/* Forward declare */
+typedef struct _term_data term_data;
+
+/* A structure for each "term" */
+struct _term_data
+{
+ term t; /* the term structure, defined in z-term.h */
+ cptr name; /* name of this term sub-window */
+
+ uint rows, cols; /* row/column count */
+ SDL_Rect rect; /* the bounding rectangle for the entire box;
+ includes border and empty space as well */
+ /* this rectangle is in screen coordinates */
+
+ int border_thick; /* thickness of border to draw around window */
+ int border_color; /* current color of the border */
+ uint cushion_x_top, cushion_x_bot, cushion_y_top, cushion_y_bot;
+ /* empty space cushion between border and tiles */
+
+ uint tile_width; /* the width of each tile (graphic or otherwise)*/
+ uint tile_height; /* the height of each tile (graphic or otherwise)*/
+
+ SDL_Surface *surf; /* the surface that graphics for this screen are
+ rendered to before blitting to main screen */
+ int black,white,purple; /* basic colors keyed to this terminal's surface */
+
+};
+
+/* The array of term data structures */
+static term_data data[MAX_CONSOLE_COUNT];
+
+/* Ordered array of pointers to term data structures, placed in order of
+priority: lowest is on top of all others, the higher the index, the further
+back into the screen that it is drawn */
+static term_data *term_order[MAX_CONSOLE_COUNT];
+
+/*************************************************
+ FILE-SPECIFIC MACROS
+ *************************************************/
+
+/* Debug macros! */
+#define DB(str) \
+ printf("main-sdl: %s\n",str);
+
+/* Prints out the RGBA values of a given color */
+#define TYPECOLOR32(color) printf(" R:%d\tG:%d\tB:%d\tA:%d\t\n",\
+ color>>24,(color&0x00ff0000)>>16,\
+ (color&0x0000ff00)>>8,(color&0x000000ff))
+
+#define TYPECOLOR16(color) printf(" R:%d\tG:%d\tB:%d\tA:%d\t\n",\
+ (color&0xf000)>>12,(color&0x0f00)>>8,\
+ (color&0x00f0)>>4,(color&0x000f))
+
+/* SDL Surface locking and unlocking */
+#define SDL_LOCK(surf) \
+ if (SDL_MUSTLOCK(surf) ){ \
+ if (SDL_LockSurface(surf) < 0) { \
+ printf("Can't lock the screen: %s\n", SDL_GetError()); \
+ exit(1); \
+ } \
+ }
+
+#define SDL_UNLOCK(surf) \
+ if (SDL_MUSTLOCK(surf) ){ \
+ SDL_UnlockSurface(surf); \
+ }
+
+/* Wrapped SDL_UpdateRects function, to take into
+account the fact that updates may be suspended by
+the suspendUpdate flag... this macro should be used
+whenever a rect needs updated */
+#define SDL_UPDATE(rect) \
+ if (!suspendUpdate) \
+ SDL_UpdateRects(screen,1,&rect)
+
+/* A complete screen redraw macro */
+#define SDL_REDRAW_SCREEN \
+ SDL_UpdateRect(screen,0,0,arg_width,arg_height)
+
+/*************************************************
+ QUITTING
+ *************************************************/
+
+/* function prototype */
+void dumpWindowSettings(void);
+
+/* SDL Quitting function... declare a few functions first.*/
+void killFontAndAlphabet(void);
+static void sdl_quit(cptr string)
+{
+ printf("sdl_quit called.\n");
+ printf("message: %s\n",string);
+ /* Need to take care of font and rendered characters */
+ killFontAndAlphabet();
+ if (TTF_WasInit())
+ TTF_Quit();
+ /* Then exit SDL */
+ SDL_Quit();
+
+ /* Dump the window properties, if available */
+ if (window_properties_set)
+ dumpWindowSettings();
+
+ /* And now for the default quit behavior */
+ quit_aux = 0;
+ quit(string);
+}
+
+/*************************************************
+ FONT SUPPORT FUNCTIONS
+ *************************************************/
+
+/* function prototype for creating surfaces */
+SDL_Surface *createSurface(int width, int height);
+
+/* killFontAndAlphabet will effectively de-initialize the font system;
+it does this by closing the font and destroying any pre-rendered
+text in memory */
+void killFontAndAlphabet(void)
+{
+ int i;
+ /* need to close a font and free all of its corresponding pre-rendered
+ surfaces */
+ if (font)
+ {
+ TTF_CloseFont(font);
+ font = 0;
+ }
+ for (i=0;i<128;i++)
+ {
+ if(text[i])
+ {
+ SDL_FreeSurface(text[i]);
+ text[i] = NULL;
+ }
+ }
+}
+
+/* loadAndRenderFont is responsible for loading and initializing
+a font. First, SDL_ttf calls are made to load and set the style
+for the desired font. Next, a character alphabet is rendered and
+each character is placed onto a uniformly-sized surface within
+the text[] array. Whenever text is needed for displaying on-screen,
+this array is referenced and the desired character picture is used. */
+void loadAndRenderFont(char *fname, int size)
+{
+ int minx,maxx,miny,maxy,advance,i,midline = 0;
+ char filename[PATH_MAX + 1];
+ char fontdir[PATH_MAX + 1];
+ SDL_Color base_color = {255,255,255,255};
+ SDL_Surface *temp_surf;
+ SDL_Rect tgt = {0,0,0,0};
+
+
+ /* Assuming that the filename is valid,open the font */
+ path_build(fontdir, PATH_MAX, ANGBAND_DIR_XTRA, "font");
+ path_build(filename, PATH_MAX, fontdir, fname);
+ font = TTF_OpenFont(filename,size);
+ if (font == NULL)
+ sdl_quit("Error loading that font!");
+ /* Set the font style to normal */
+ TTF_SetFontStyle(font,TTF_STYLE_NORMAL);
+
+ /* Collect some measurements on this font -
+ arbitrarily choose the letter 'a' to get width*/
+ TTF_GlyphMetrics(font,'a',&minx,&maxx,&miny,&maxy,&advance);
+ /* the width of each character tile */
+ t_width = advance;
+ /* the height of each character tile */
+ t_height = TTF_FontHeight(font);
+ /* position of the y=0 line in each tile */
+ midline = TTF_FontAscent(font);
+
+ /* now... render each of the individual characters */
+ for (i=0;i<128;i++)
+ {
+ /* make a pretty blended glyph */
+ temp_surf=TTF_RenderGlyph_Blended(font,i,base_color);
+ /* and make sure that we got it right! */
+ if (temp_surf == NULL)
+ sdl_quit("Glyph failed to render!");
+ /* get the metrics of this particular glyph so we can position it */
+ TTF_GlyphMetrics(font,i,&minx,&maxx,&miny,&maxy,&advance);
+ /* copy rendered glyph into text queue, at the right position*/
+ tgt.x = minx;
+ tgt.y = midline-maxy;
+ /* but first... we'll need a surface in the text queue to blit to! */
+ text[i] = createSurface(t_width,t_height);
+ /* turn OFF src-alpha... results in brute
+ copy of the RGBA contents of surf */
+ SDL_SetAlpha(temp_surf,0,0);
+ SDL_BlitSurface(temp_surf,NULL,text[i],&tgt);
+ /* turn OFF src-alpha since we'll be using worksurf for blitting */
+ SDL_SetAlpha(text[i],0,0);
+ /* kill the surface to patch up memory leaks */
+ SDL_FreeSurface(temp_surf);
+ }
+}
+
+/* KEYPRESS_STRING repeatedly sends characters to the terminal
+XXX - should implement routine from maim-sdl.c, it's sooo much
+cleaner */
+#define KEYPRESS_STRING(str) \
+strcpy(buf,str); \
+n = buf; \
+while (*n != '\0') { \
+ Term_keypress((int)(*(n++))); \
+}
+
+/* function prototype */
+void manipulationMode(void);
+void redrawAllTerminals(void);
+/* This is the main event handling routine that will be called
+whenever an event is pulled off of the queue (in Term_xtra_sdl())*/
+void handleEvent(SDL_Event *event)
+{
+ static char buf[24]; /* a buffer used when passing key names */
+ char *n; /* and a pointer to manipulate this buffer */
+
+ switch( event->type )
+ {
+ case SDL_KEYDOWN:
+ {
+ /* handle key presses */
+
+ /* I'm reading that as long as the upper 9 bits of the unicode
+ * value are zero, then the lower 7 bits are direct ASCII characters.
+ * Furthermore, it seems that all basic keys return non-zero values
+ * for the lower 7 bits, but function keys and other various things
+ * return 0000000 for the lower 7 bits.
+ * Basically, if the lower 7 bits are zero, do something special
+ * (like start a macro), but otherwise just pass along the ASCII
+ * code!
+ */
+ byte ascii_part = event->key.keysym.unicode & 0x00ff;
+
+ /* gimme the key name */
+ printf("Key is: %s\n",SDL_GetKeyName(event->key.keysym.sym));
+
+ /* allow for full screen toggling! */
+ if ((event->key.keysym.sym == SDLK_RETURN) && \
+ (SDL_GetModState() & KMOD_ALT))
+ {
+ SDL_WM_ToggleFullScreen(screen);
+ /* toggle the internal full screen flag */
+ arg_full_screen = (arg_full_screen ? FALSE : TRUE);
+ }
+
+ /* entry into window manipulation mode */
+ if ((event->key.keysym.sym == SDLK_RETURN) && \
+ (SDL_GetModState() & KMOD_CTRL))
+ {
+ DB("Manipulation mode!");
+ manipulationMode();
+ }
+
+ /*printf("ascii_part: %d\n",ascii_part);*/
+ if (ascii_part)
+ {
+ /* We have now determined that the ASCII part is not '0', so
+ we can safely pass along the ASCII value! */
+ Term_keypress(ascii_part);
+ }
+ else
+ {
+ /* We want to ignore keypresses that are simply the modifier
+ keys*/
+ if (!( (event->key.keysym.sym == SDLK_RSHIFT) |
+ (event->key.keysym.sym == SDLK_LSHIFT) |
+ (event->key.keysym.sym == SDLK_RALT) |
+ (event->key.keysym.sym == SDLK_LALT) |
+ (event->key.keysym.sym == SDLK_RCTRL) |
+ (event->key.keysym.sym == SDLK_LCTRL) ))
+ {
+
+ /* now build a macro string using the modifiers together
+ with the key that was just pressed*/
+
+ /* As for the formatting...
+ * We pass the key press and modifiers as follows:
+ * \[ctrl-alt-shift-"key name"]
+ * following the previously established convention...
+ *
+ * All of the things that happen are defined in pref-sdl.prf
+ */
+
+ KEYPRESS_STRING("\["); /*Output the first part... */
+ /* See if a control key is down */
+ if (event->key.keysym.mod & KMOD_CTRL)
+ {
+ KEYPRESS_STRING("ctrl-");
+ }
+ /* See if an alt key is down */
+ if (event->key.keysym.mod & KMOD_ALT)
+ {
+ KEYPRESS_STRING("alt-");
+ }
+ /* See if a shift key is down */
+ if (event->key.keysym.mod & KMOD_SHIFT)
+ {
+ KEYPRESS_STRING("shift-");
+ }
+
+ /* Add in the name of whatever key was pressed */
+ KEYPRESS_STRING(SDL_GetKeyName(event->key.keysym.sym));
+
+ /* and end it... */
+ KEYPRESS_STRING("]");
+ }
+ }
+ break;
+ }
+ case SDL_QUIT:
+ {
+ /* handle quit requests */
+ DB("Emergency Blit");
+ redrawAllTerminals();
+ save_player();
+ save_dungeon();
+ sdl_quit("Quitting!\n");
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+/* declare the screen clearing function used below */
+void eraseTerminal();
+void drawTermStuff(term_data *td, SDL_Rect *rect);
+static errr Term_xtra_sdl(int n, int v)
+{
+ static SDL_Event event;
+ term_data *td;
+
+
+ /* Analyze */
+ switch (n)
+ {
+ case TERM_XTRA_EVENT:
+ {
+ if (v)
+ {
+ /* Perform event checking with blocking */
+ SDL_WaitEvent( &event );
+ handleEvent( &event );
+ } else {
+ /* Perform event checking without blocking */
+ if (SDL_PollEvent(&event)){
+ /* We found an event! */
+ handleEvent(&event);
+ }
+ }
+ return(0);
+ }
+
+ case TERM_XTRA_FLUSH:
+ {
+ /* Keep doing events until the queue is empty! */
+ while (SDL_PollEvent(&event))
+ {
+ handleEvent(&event);
+ }
+ return (0);
+ }
+
+ case TERM_XTRA_CLEAR:
+ {
+ /* Clear the terminal */
+ DB("TERM_XTRA_CLEAR");
+ suspendUpdate = TRUE;
+ eraseTerminal();
+ return (0);
+ }
+
+ case TERM_XTRA_SHAPE:
+ {
+ /*
+ * Set the cursor visibility XXX XXX XXX
+ *
+ * This action should change the visibility of the cursor,
+ * if possible, to the requested value (0=off, 1=on)
+ *
+ * This action is optional, but can improve both the
+ * efficiency (and attractiveness) of the program.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_FROSH:
+ {
+ /*
+ * Flush a row of output XXX XXX XXX
+ *
+ * This action should make sure that row "v" of the "output"
+ * to the window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FRESH" entry below takes care of any
+ * necessary flushing issues.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_FRESH:
+ {
+ /*
+ * Flush output XXX XXX XXX
+ *
+ * This action should make sure that all "output" to the
+ * window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FROSH" entry above takes care of any
+ * necessary flushing issues.
+ */
+
+ /* If terminal display has been held for any reason,
+ then update the whole thing now!*/
+ DB("TERM_XTRA_FRESH");
+ if (suspendUpdate)
+ {
+ DB(" update WAS suspended... updating now");
+ td = (term_data*)(Term->data);
+ suspendUpdate = FALSE;
+ drawTermStuff(td,NULL);
+ }
+ return (0);
+ }
+
+ case TERM_XTRA_NOISE:
+ {
+ /*
+ * Make a noise XXX XXX XXX
+ *
+ * This action should produce a "beep" noise.
+ *
+ * This action is optional, but convenient.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_SOUND:
+ {
+ /*
+ * Make a sound XXX XXX XXX
+ *
+ * This action should produce sound number "v", where the
+ * "name" of that sound is "sound_names[v]". This method
+ * is still under construction.
+ *
+ * This action is optional, and not very important.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_BORED:
+ {
+ /* Perform event checking without blocking */
+ if (SDL_PollEvent(&event)){
+ /* We found an event! */
+ handleEvent(&event);
+ }
+ return(0);
+ }
+
+ case TERM_XTRA_REACT:
+ {
+ /*
+ * React to global changes XXX XXX XXX
+ *
+ * For example, this action can be used to react to
+ * changes in the global "color_table[256][4]" array.
+ *
+ * This action is optional, but can be very useful for
+ * handling "color changes" and the "arg_sound" and/or
+ * "arg_graphics" options.
+ */
+ return (1);
+ }
+
+ case TERM_XTRA_ALIVE:
+ {
+ /*
+ * Change the "hard" level XXX XXX XXX
+ *
+ * This action is used if the program changes "aliveness"
+ * by being either "suspended" (v=0) or "resumed" (v=1)
+ * This action is optional, unless the computer uses the
+ * same "physical screen" for multiple programs, in which
+ * case this action should clean up to let other programs
+ * use the screen, or resume from such a cleaned up state.
+ *
+ * This action is currently only used by "main-gcu.c",
+ * on UNIX machines, to allow proper "suspending".
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_LEVEL:
+ {
+ /*
+ * Change the "soft" level XXX XXX XXX
+ *
+ * This action is used when the term window changes "activation"
+ * either by becoming "inactive" (v=0) or "active" (v=1)
+ *
+ * This action can be used to do things like activate the proper
+ * font / drawing mode for the newly active term window. This
+ * action should NOT change which window has the "focus", which
+ * window is "raised", or anything like that.
+ *
+ * This action is optional if all the other things which depend
+ * on what term is active handle activation themself, or if only
+ * one "term_data" structure is supported by this file.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_DELAY:
+ {
+ /*
+ * Delay for some milliseconds XXX XXX XXX
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as breath attacks.
+ *
+ * This action is optional, but may be required by this file,
+ * especially if special "macro sequences" must be supported.
+ */
+
+ /* I think that this command is system independent... */
+ /*sleep(v/1000);*/
+ /* main-x11 uses usleep(1000*v); */
+ /* main-win uses Sleep(v); */
+ return (1);
+ }
+
+ case TERM_XTRA_GET_DELAY:
+ {
+ /*
+ * Get Delay of some milliseconds XXX XXX XXX
+ * place the result in Term_xtra_long
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as recording cmovies.
+ *
+ * This action is optional, but cmovies wont perform
+ * good without it
+ */
+
+ return (1);
+ }
+ }
+
+ /* Unknown or Unhandled action */
+ return (1);
+}
+
+/*************************************************
+ GRAPHICS ROUTINES
+ *************************************************/
+
+/* wrapper routine for creating an RGB surface with given height and
+ width that corresponds to desired color depth and respects the system
+ pixel format */
+SDL_Surface *createSurface(int width, int height)
+{
+ SDL_Surface *surf;
+ int surface_type;
+
+ if (videoInfo->hw_available)
+ surface_type = SDL_HWSURFACE;
+ else
+ surface_type = SDL_SWSURFACE;
+
+ /* XXX need to make RGBA masks correspond to system pixel format! */
+ switch (arg_bpp)
+ {
+ case 8:
+ {
+ /* I really don't know if 8 bpp is even possible, but here it is */
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,8,0xc0,0x30,0x0c,0x03);
+ break;
+ }
+ case 16:
+ {
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,16,0xf000,0x0f00,0x00f0,0x000f);
+ break;
+ }
+ case 24:
+ {
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,24,0xfc0000,0x03f000,0x000fc0,0x000030);
+ break;
+ }
+ case 32:
+ {
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,32,0xff000000,0x00ff0000,0x0000ff00,0x000000ff);
+ break;
+ }
+ default:
+ {
+ surf = NULL;
+ break;
+ }
+ }
+
+ if (surf == NULL)
+ sdl_quit("Bad Surface Creation!");
+
+ return surf;
+}
+
+/* Take a rectangle in terminal coordinates and then transform it into
+screen coordinates; td is the term_data that this rect belongs to */
+void term_to_screen(SDL_Rect *termrect, term_data *td)
+{
+ termrect->x += td->rect.x;
+ termrect->y += td->rect.y;
+}
+
+/* Do the opposite, take a rectangle in screen coordinates and transform
+it into the terminal coordinates of the given term_data */
+void screen_to_term(SDL_Rect *scrrect, term_data *td)
+{
+ scrrect->x -= td->rect.x;
+ scrrect->y -= td->rect.y;
+}
+
+/* A macro that determines if the 'top' rectangle completely occludes the
+'bottom' rectangle */
+#define BLOCKS(top,bottom) \
+( (top.x <= bottom.x) & ((top.x+top.w)>=(bottom.x+bottom.w)) & \
+ (top.y <= bottom.y) & ((top.y+top.h)>=(bottom.y+bottom.h)) )
+
+#define INTERSECT(r1,r2) \
+!( ( (r1.x > (r2.x+r2.w)) | (r2.x > (r1.x+r1.w)) ) & \
+ ( (r1.y > (r2.y+r2.h)) | (r2.y > (r1.y+r1.h)) ) )
+
+/* A function to calculate the intersection of two rectangles. Takes base
+rectangle and then updates it to include only the rectangles that intersect
+with the test rectangle. If there is an intersection, the function returns
+TRUE and base now contains the intersecting rectangle. If there is no
+intersection, then the function returns FALSE */
+bool_ intersectRects(SDL_Rect *base, SDL_Rect *test)
+{
+ if (INTERSECT((*base),(*test)))
+ {
+ /* Scoot the x-coordinates for the left side*/
+ if ( test->x > base->x )
+ {
+ base->w -= test->x - base->x;
+ base->x = test->x;
+ }
+ /* Scoot the x-coordinates for the right side*/
+ if ( (test->x + test->w) < (base->x + base->w) )
+ {
+ base->w = test->x + test->w - base->x;
+ }
+ /* Scoot the upper y-coordinates */
+ if ( test->y > base->y )
+ {
+ base->h -= test->y - base->y;
+ base->y = test->y;
+ }
+ /* Scoot the lower y-coordinates */
+ if ( (test->y + test->h) < (base->y + base->h) )
+ {
+ base->h = test->y + test->h - base->y;
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* A function to calculate the join of two rectangles; the first argument is
+changed to the joined rectangle */
+SDL_Rect joinRects(SDL_Rect *r1, SDL_Rect *r2)
+{
+ SDL_Rect out = {0,0,0,0};
+
+ if ( (r1 != NULL) & (r2 != NULL) )
+ {
+ /* Lower x-coordinate */
+ if ( r2->x < r1->x )
+ out.x = r2->x;
+ else
+ out.x = r1->x;
+ /* Upper x-coordinate */
+ if ( (r2->x+r2->w) > (r1->x+r1->w) )
+ out.w = (r2->x+r2->w) - out.x;
+ else
+ out.w = (r1->x+r1->w) - out.x;
+ /* Lower y-coordinate */
+ if ( r2->y < r1->y )
+ out.y = r2->y;
+ else
+ out.y = r1->y;
+ if ( (r2->y+r2->h) > (r1->y+r1->h) )
+ out.h = (r2->y+r2->h) - out.y;
+ else
+ out.h = (r1->y+r1->h) - out.y;
+ }
+ return out;
+}
+
+/* Given a term_data (and its associated screen) and a rectangle in terminal
+coordinates (with NULL signifying to take the whole terminal surface), blit
+graphics from the term_data surface to the screen, using the term_data's rect
+to indicate how terminal coordinates transform into screen coordinates.
+This is complicated, however, by the possibility that the indicated area may be
+occluded by overlaying terminals. In this case, if the target area is
+completely occluded, nothing will be done. If partially occluded, it will be
+drawn, but occluding terminals will then re-blit to re-cover the area. */
+void drawTermStuff(term_data *td, SDL_Rect *rect)
+{
+ int n = 0, i;
+ bool_ block = FALSE, cover = FALSE;
+ SDL_Rect spot, isect_term, isect_scr;
+
+ /* first of all, if updating is suspended, do nothing! */
+ if (!suspendUpdate)
+ {
+ /* find out which number in the ordered stack of screens that this
+ terminal is */
+ while ((term_order[n] != td) & (n < MAX_CONSOLE_COUNT))
+ {
+ n++;
+ }
+ if (n == MAX_CONSOLE_COUNT)
+ printf("Could not find terminal in display list...\n");
+ /* now loop through and see if any terminals completely occlude
+ the desired spot; if num=0, note that this will be skipped */
+ if (rect == NULL)
+ {
+ /* Grab the whole terminal screen */
+ spot.x = 0; spot.y = 0;
+ spot.w = td->surf->w; spot.h = td->surf->h;
+ }
+ else
+ {
+ /* Just copy the given area */
+ spot.x = rect->x; spot.y = rect->y;
+ spot.w = rect->w; spot.h = rect->h;
+ }
+ term_to_screen(&spot,td);
+ i = n;
+ while (i--)
+ {
+ if (BLOCKS(term_order[i]->rect,spot))
+ {
+ /* Higher terminal completely occludes this spot */
+ block = TRUE;
+ DB(" Blocks!");
+ }
+ else if (INTERSECT(term_order[i]->rect,spot))
+ {
+ /* Partial occlusion */
+ cover = TRUE;
+ DB(" Covers!");
+ }
+ }
+ /* If any of the higher terminals blocked, then don't do
+ anything */
+ if (!block)
+ {
+ /*printf("Blitting to %d %d %d %d\n",spot.x,spot.y,spot.w,spot.h);*/
+ /* First of all, draw the graphics */
+ SDL_BlitSurface(td->surf,rect,screen,&spot);
+ if (cover)
+ {
+ printf("covering...");
+ /* There are covering terminals, so go through and blit all
+ partially occluding ones */
+ while (n--)
+ {
+ /* copy spot to find the intersect */
+ isect_scr.x = spot.x; isect_scr.y = spot.y;
+ isect_scr.w = spot.w; isect_scr.h = spot.h;
+ if (intersectRects(&isect_scr,&(term_order[n]->rect)))
+ {
+ /* this terminal intersects... re-blit */
+ /* first, convert to term coordinates */
+ isect_term.x = isect_scr.x; isect_term.y = isect_scr.y;
+ isect_term.w = isect_scr.w; isect_term.h = isect_scr.h;
+ screen_to_term(&isect_term,term_order[n]);
+ /* blit from term coordinates to screen coordinates */
+ SDL_BlitSurface(term_order[n]->surf,&isect_term,\
+ screen,&isect_scr);
+ }
+ }
+ }
+ /* Now update what was drawn */
+ DB("Update");
+ SDL_UpdateRects(screen,1,&spot);
+ }
+ }
+}
+
+/* utility routine for creating and setting the color of the cursor;
+it could be useful for setting a new cursor color if desired.
+Could later be expanded to do other stuff with the cursor,
+like a hollow rectangle a la main-win.c or even a graphic */
+void createCursor(byte r, byte g, byte b, byte a)
+{
+ /* free the cursor if it exists */
+ if (cursor != NULL)
+ SDL_FreeSurface(cursor);
+
+ /* and create it anew! (or the first time) */
+ cursor = createSurface(t_width,t_height);
+
+ /* be sure to use alpha channel when blitting! */
+ SDL_SetAlpha(cursor,SDL_SRCALPHA,0);
+
+ /* just set the color for now - drawing rectangles
+ needs surface locking for some setups */
+ cursor_color = SDL_MapRGBA(cursor->format,r,g,b,a);
+ SDL_LOCK(cursor);
+ SDL_FillRect(cursor,NULL,cursor_color);
+ SDL_UNLOCK(cursor);
+}
+
+/* Cursor Display routine - just blits the global cursor
+surface onto the correct location */
+static errr Term_curs_sdl(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+ static SDL_Rect base;
+
+ /* calculate the position to place the cursor */
+ base.x = td->surf->clip_rect.x + x*t_width;
+ base.y = td->surf->clip_rect.y + y*t_height;
+ base.w = t_width;
+ base.h = t_height;
+
+ /* blit the cursor over top of the given spot;
+ note that surface should not be locked
+ (see note in Term_text_sdl() below) */
+ SDL_BlitSurface(cursor,NULL,td->surf,&base);
+
+ /* Now draw to the main screen */
+ drawTermStuff(td,&base);
+ /* Success */
+ return (0);
+}
+
+/* routine for wiping terminal locations - simply draws
+a black rectangle over the offending spots! */
+static errr Term_wipe_sdl(int x, int y, int n)
+{
+ static SDL_Rect base;
+ term_data *td = (term_data*)(Term->data);
+
+ /* calculate boundaries of the area to clear */
+ base.x = td->surf->clip_rect.x + x*t_width;
+ base.y = td->surf->clip_rect.y + y*t_height;
+ base.w = n*t_width;
+ base.h = t_height;
+
+ SDL_LOCK(td->surf);
+
+ /* blank the screen area */
+ SDL_FillRect(td->surf, &base, td->black);
+
+ SDL_UNLOCK(td->surf);
+
+ /* And... UPDATE the rectangle we just wrote to! */
+ drawTermStuff(td,&base);
+
+ /* Success */
+ return (0);
+}
+
+/* Perform a full clear of active terminal; redraw the borders.*/
+void eraseTerminal(void)
+{
+ static SDL_Rect base;
+ term_data *td = (term_data*)(Term->data);
+
+ /* temporarily remove clipping rectangle */
+ SDL_SetClipRect(td->surf,NULL);
+
+ SDL_LOCK(td->surf);
+ /* flood terminal with border color */
+ SDL_FillRect(td->surf,NULL,td->border_color);
+
+ /* get smaller rectangle to hollow out window */
+ base.x = td->border_thick;
+ base.y = td->border_thick;
+ base.w = td->rect.w - 2*td->border_thick;
+ base.h = td->rect.h - 2*td->border_thick;
+
+ /* hollow out terminal */
+ SDL_FillRect(td->surf,&base,td->black);
+
+ SDL_UNLOCK(screen);
+
+ /* reset clipping rectangle */
+ base.x += td->cushion_x_top;
+ base.y += td->cushion_y_top;
+ base.w -= td->cushion_x_top + td->cushion_x_bot;
+ base.h -= td->cushion_y_top + td->cushion_y_bot;
+ SDL_SetClipRect(td->surf,&base);
+ printf("Clip rect: %d %d %d %d\n",base.x,base.y,base.w,base.h);
+ /* And... UPDATE the whole thing */
+ drawTermStuff(td,NULL);
+
+}
+
+/*
+ * Draw some text on the screen
+ *
+ * This function should actually display an array of characters
+ * starting at the given location, using the given "attribute",
+ * and using the given string of characters, which contains
+ * exactly "n" characters and which is NOT null-terminated.
+ *
+ * You may assume "valid" input if the window is properly sized.
+ *
+ * You must be sure that the string, when written, erases anything
+ * (including any visual cursor) that used to be where the text is
+ * drawn. On many machines this happens automatically, on others,
+ * you must first call "Term_wipe_xxx()" to clear the area.
+ *
+ * In color environments, you should activate the color contained
+ * in "color_data[a & 0x0F]", if needed, before drawing anything.
+ *
+ * You may ignore the "attribute" if you are only supporting a
+ * monochrome environment, since this routine is normally never
+ * called to display "black" (invisible) text, including the
+ * default "spaces", and all other colors should be drawn in
+ * the "normal" color in a monochrome environment.
+ *
+ * Note that if you have changed the "attr_blank" to something
+ * which is not black, then this function must be able to draw
+ * the resulting "blank" correctly.
+ *
+ * Note that this function must correctly handle "black" text if
+ * the "always_text" flag is set, if this flag is not set, all the
+ * "black" text will be handled by the "Term_wipe_xxx()" hook.
+ */
+static errr Term_text_sdl(int x, int y, int n, byte a, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+ static SDL_Rect base;
+ SDL_Rect base_back;
+ int i = n;
+ char old = 0;
+
+ /* calculate place to clear off and draw to */
+ base.x = td->surf->clip_rect.x + x*td->tile_width;
+ base.y = td->surf->clip_rect.y + y*td->tile_height;
+ base.w = n*td->tile_width;
+ base.h = td->tile_height;
+
+ base_back = base;
+
+ SDL_LOCK(screen);
+
+ /* blank the drawing area */
+ SDL_FillRect(td->surf, &base, td->black);
+
+ SDL_UNLOCK(screen);
+
+ /* Note that SDL docs specify that SDL_BlitSurface should not be called
+ on locked surfaces... since the character printing routine below revolves
+ around blitting, the surface has been unlocked first*/
+
+ /* loop through the input string, drawing characters */
+ i = n;
+ old = 0;
+ while (i--)
+ {
+ /* Output the character... */
+ /* If character has not changed, then just blit the old surface into
+ the new location to save effort*/
+ if (*cp == old)
+ {
+ /* the desired character/color combo is already on the work surf */
+ /* just blit it! */
+ SDL_BlitSurface(worksurf,NULL,td->surf,&base);
+ } else {
+ /* copy the desired character onto working surface */
+ SDL_BlitSurface(text[*cp],NULL,worksurf,NULL);
+ /* color our crayon surface with the desired color */
+ SDL_FillRect(crayon,NULL,color_data[a&0x0f]);
+ /* apply the color to the character on the working surface */
+ SDL_BlitSurface(crayon,NULL,worksurf,NULL);
+ /* and blit it onto our screen! */
+ SDL_BlitSurface(worksurf,NULL,td->surf,&base);
+ }
+ /* Move to the next position */
+ base.x += t_width;
+ /* Store the old character */
+ old = *cp;
+ /* Increment the character pointer */
+ cp++;
+ }
+
+ /* And update */
+ drawTermStuff(td,&base_back);
+
+ /* Success */
+ return (0);
+}
+
+/*************************************************
+ SPECIAL TERMINAL WINDOW MANIPULATION ROUTINES
+ *************************************************/
+
+/* macro for bounding a value between two given values */
+#define BOUND(val,low,high) \
+if (val < low) \
+{ \
+ val = low; \
+} \
+else if (val > high) \
+{ \
+ val = high; \
+}
+
+/* two macros to get the adjusted maximums for window
+positions... eg the screen width minus the width of the
+window is the maximum x-position that the window can
+be set at. */
+#define MAX_X(td) \
+( arg_width - td->rect.w )
+
+#define MAX_Y(td) \
+( arg_height - td->rect.h )
+
+/* another two macros that give maximum window widths
+based on screen size and current window position together
+width tile widths/heights */
+#define MAX_WIDTH(td) \
+( (int)floorf((arg_width - td->rect.x - 2*td->border_thick - \
+ td->cushion_x_bot - td->cushion_x_top )/td->tile_width))
+
+#define MAX_HEIGHT(td) \
+( (int)floorf((arg_height - td->rect.y - 2*td->border_thick - \
+ td->cushion_y_bot - td->cushion_y_top )/td->tile_height))
+
+/* update the width and height of given term's rectangle by simply
+multiplying the tile count by tile size and adding in cushions and
+borders */
+#define UPDATE_SIZE(td) \
+ td->rect.w = (td->cols)*td->tile_width + td->cushion_x_top \
+ + td->cushion_x_bot + 2*td->border_thick; \
+ td->rect.h = (td->rows)*td->tile_height + td->cushion_y_top \
+ + td->cushion_y_bot + 2*td->border_thick
+
+void recompose(void);
+
+/* Resize the active terminal with new width and height.
+Note that his involves a complicated sequence of events...
+Details to follow below! */
+void resizeTerminal(int width, int height)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* First of all, bound the input width and height to satisfy
+ these conditions:
+ - The main ToME window should be at least 80 cols, 24 rows
+ - no part of each window should be drawn off screen....
+ I'm including borders in this restriction!
+ But no bounds checking needs to take place if the input width
+ and height are unchanged....
+ */
+
+ if (td == &data[0])
+ {
+ /* The active terminal is the main ToME window...
+ don't let the width get below 80, don't let the heights below
+ 24, and don't let it leak off of the edge! */
+ if (width != td->cols)
+ {
+ BOUND(width,80,MAX_WIDTH(td));
+ }
+ if (height != td->rows)
+ {
+ BOUND(height,24,MAX_HEIGHT(td));
+ }
+ }
+ else
+ {
+ /* This is not the main window... just make sure it
+ doesn't shrink to nothing or go past the edge */
+ if (width != td->cols)
+ {
+ BOUND(width,1,MAX_WIDTH(td));
+ }
+ if (height != td->rows)
+ {
+ BOUND(height,1,MAX_HEIGHT(td));
+ }
+ }
+
+ /* Okay, now make sure that something has ACTUALLY changed
+ before doing anything */
+ if ((width != td->cols) || (height != td->rows))
+ {
+
+ /* Now, ask zterm to please resize the term structure! */
+ Term_resize(width,height);
+
+ /* Reactivate, since Term_resize seems to activate the
+ main window again...*/
+ Term_activate(&td->t);
+
+ /* It might not have resized completely to the new
+ size we wanted (some windows have size limits it seems,
+ like the message window). So, update our structure with
+ the size that were actually obtained.*/
+ td->cols = Term->wid;
+ td->rows = Term->hgt;
+
+ /* And recalculate the sizes */
+ UPDATE_SIZE(td);
+
+ /* Create a new surface that can hold the updated size */
+ SDL_FreeSurface(td->surf);
+ td->surf = createSurface(td->rect.w,td->rect.h);
+
+ /* Now we should be in business for a complete redraw! */
+ Term_redraw();
+
+ /* Re-blit everything so it looks good */
+ recompose();
+
+ /* That's it! */
+ }
+}
+
+/* Move the terminal around... a much simpler action that involves
+just changing the pos_x/pos_y values and redrawing!*/
+void moveTerminal(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Now, the window is being shifted about... much simpler
+ situation to handle! But of course, the window must not
+ drift too far or else parts will be hanging off the screen
+ and may lead to errors - bound the input positions to
+ prevent this unfortunate situation... do nothing if the
+ input is no different than the current */
+ if (x != td->rect.x)
+ {
+ BOUND(x,0,MAX_X(td));
+ }
+ if (y != td->rect.y)
+ {
+ BOUND(y,0,MAX_Y(td));
+ }
+
+ /* Okay, now make sure that something changed before doing
+ anything */
+ if ((x != td->rect.x) || (y != td->rect.y))
+ {
+ /* Now update OUR structure */
+ td->rect.x = x;
+ td->rect.y = y;
+
+ /* Then do a reblit to see the results */
+ recompose();
+
+ /* That's it! */
+ }
+}
+
+/* Routine to bring a given term_data to the top of the drawing stack */
+void bringToTop(int current)
+{
+ term_data *td;
+ term_data *tc;
+ int n = 0;
+ int i;
+
+ /* Get the pointer to the desired term_data from the data structure */
+ td = &data[current];
+
+ printf("Current stack: \n");
+ for (i=0;i<arg_console_count;i++)
+ {
+ printf(" %d: %p\n",i,term_order[i]);
+ }
+ printf("\n");
+
+ /* Find the number in the term_order stack */
+ while ((term_order[n] != td) & (n < MAX_CONSOLE_COUNT))
+ {
+ n++;
+ }
+ if (n == MAX_CONSOLE_COUNT)
+ printf("Could not find terminal in display list...\n");
+
+ printf("Order is %d\n",n);
+
+ /* Now move all lower-indexed pointers up one index */
+ while (n)
+ {
+ printf(" move %d to %d\n",n-1,n);
+ printf(" %p\n",term_order[n-1]);
+ printf(" %p\n",term_order[n]);
+ term_order[n] = term_order[n-1];
+ n--;
+ }
+ /* And stick this term_data pointer on top */
+ term_order[0] = td;
+
+ printf("Final stack: \n");
+ for (i=0;i<arg_console_count;i++)
+ {
+ printf(" %d: %p\n",i,term_order[i]);
+ }
+ printf("\n");
+
+}
+
+/* This utility routine will cycle the active term to the
+next available in the data[] array. It will then do a
+redraw of this term so that it is ready to be manipulated.
+The input is the current active terminal, and the output is
+the new active terminal */
+int cycleTerminal(int current)
+{
+ /* redraw the current term to get rid of its purple
+ border */
+ data[current].border_color = data[current].white;
+ Term_redraw();
+
+ /* increment the terminal number*/
+ current++;
+ /* now do a little modulo cycle action and
+ activate the next term! */
+ current %= arg_console_count;
+ Term_activate(&(data[current].t));
+
+ /* before redrawing, set the border color to purple to
+ indicate that this terminal is being manipulated*/
+ data[current].border_color = data[current].purple;
+
+ /* then bring this terminal to the top of the order, so it is drawn on
+ top during manipulation mode */
+ bringToTop(current);
+
+ /* and do a complete redraw */
+ Term_redraw();
+
+ /* return the current terminal... */
+ return current;
+}
+
+/* This routine will simply re-blit all of the surfaces onto the main screen,
+respecting the current term_order */
+void recompose(void)
+{
+ int i = arg_console_count;
+ /* do a complete screen wipe */
+ SDL_LOCK(screen);
+ SDL_FillRect(screen,NULL,screen_black);
+ SDL_UNLOCK(screen);
+
+ /* cycle through the term_order */
+ while (i--)
+ {
+ SDL_BlitSurface(term_order[i]->surf,NULL,screen,&(term_order[i]->rect));
+ }
+
+ /* Update everything */
+ SDL_REDRAW_SCREEN;
+}
+
+/* This utility routine will completely blank the screen and
+then cycle through all terminals, performing a Term_redraw()
+on each and every term that is being used (according to
+arg_term_count that is). The terminals will be redrawn
+last-to-first, so that the main is over top of everything */
+void redrawAllTerminals(void)
+{
+ int i = arg_console_count;
+ DB("Total redraw");
+ /* do a complete screen wipe */
+ SDL_LOCK(screen);
+ SDL_FillRect(screen,NULL,screen_black);
+ SDL_UNLOCK(screen);
+
+ while (i--)
+ {
+ /* Re-order the terminals */
+ term_order[i] = &data[i];
+ }
+
+ i = arg_console_count;
+ /* cycle down through each terminal */
+ while (i--)
+ {
+ /* Activate this terminal */
+ Term_activate(&(data[i].t));
+
+ /* Make its border white since manipulation mode is over */
+ data[i].border_color = data[i].white;
+
+ /* And redraw it */
+ Term_redraw();
+ }
+ /* Loop will end on i=0 ! */
+
+ printf("Current stack: \n");
+ for (i=0;i<arg_console_count;i++)
+ {
+ printf(" %d: %p\n",i,term_order[i]);
+ }
+ printf("\n");
+
+ /* now update the screen completely, just in case*/
+ SDL_REDRAW_SCREEN;
+}
+
+/* This is the special event handling function for doing
+terminal window manipulation! When special manipulation
+mode is activated, execution goes here. This special mode
+has its own keypresses. To begin with, the main terminal
+border is highlighted (in purple) to indicate that it is
+being manipulated. The following keypresses are accepted:
+-Space: switches between editing modes. The system begins
+ in position editing mode, and Enter will toggle size
+ (row/col) editing mode.
+-Arrows: increments/decrements the position/size in an
+ intuitive way! ;) Some modifiers are accepted in order
+ to speed things up on very high resolution screens:
+ . with shift down, increment is five
+ . with ctrl down, increment is ten
+ . with both, increment is fifty!
+ Of course, no movement or resize can cause the window
+ to leave the confines of the screen, so using the big
+ jump is safe.
+-Enter: cycles to the next available terminal to edit.
+-Escape: quits manipulation mode, performing one final
+ redraw to take into account all changes.
+*/
+void manipulationMode(void)
+{
+ term_data *td;
+ SDL_Event event;
+ bool_ done = FALSE, moveMode = TRUE;
+ int mouse_x, mouse_y;
+ int value = 0, delta_x = 0, delta_y = 0;
+ int current_term;
+ SDL_Surface backup;
+
+ /* Begin by redrawing the main terminal with its
+ purple border to signify that it is being edited*/
+
+ /* start with the main terminal */
+ current_term = 0;
+
+ /* get the pointer */
+ td = &data[0];
+
+ /* before redrawing, set the border color to purple to
+ indicate that this terminal is being manipulated*/
+ td->border_color = td->purple;
+
+ /* and do a complete redraw */
+ DB("Term_redraw");
+ Term_redraw();
+
+ /* Now keep looping until Esc has been pressed. */
+ while (!done)
+ {
+ /* Get the keypress event */
+ SDL_WaitEvent(&event);
+ /* Make sure that it is a keypress */
+ if (event.type == SDL_KEYDOWN)
+ {
+ /* Act on the keypress! */
+ switch (event.key.keysym.sym)
+ {
+ case SDLK_ESCAPE:
+ {
+ /* Escape has been pressed, so we're done!*/
+ done = TRUE;
+ break;
+ }
+ case SDLK_SPACE:
+ {
+ /* Space has been pressed: toggle move mode */
+ moveMode = ( moveMode ? FALSE : TRUE );
+ break;
+ }
+ case SDLK_RETURN:
+ {
+ /* Return... cycle the terminals!
+ update the current_term appropriately*/
+ current_term = cycleTerminal(current_term);
+
+ /* Get the new term_data */
+ td = &data[current_term];
+
+ break;
+ }
+ case SDLK_RIGHT:
+ case SDLK_LEFT:
+ case SDLK_DOWN:
+ case SDLK_UP:
+ {
+ /* Increase either the x-position or column
+ width - multiply according to modifiers */
+ value = 1;
+ if (SDL_GetModState() & KMOD_SHIFT)
+ {
+ /* shift is down... a muliplier of 5 */
+ value *= 5;
+ }
+ if (SDL_GetModState() & KMOD_CTRL)
+ {
+ /* control is down... multiply by 10 */
+ value *= 10;
+ }
+
+ /* Now, behavior depends on which key was pressed
+ and whether we are in moveMode resize mode... */
+
+ /* First, set the delta_x/y based on key */
+ if (event.key.keysym.sym == SDLK_RIGHT)
+ {
+ delta_x = 1;
+ delta_y = 0;
+ }
+ if (event.key.keysym.sym == SDLK_LEFT)
+ {
+ delta_x = -1;
+ delta_y = 0;
+ }
+ if (event.key.keysym.sym == SDLK_DOWN)
+ {
+ delta_x = 0;
+ delta_y = 1;
+ }
+ if (event.key.keysym.sym == SDLK_UP)
+ {
+ delta_x = 0;
+ delta_y = -1;
+ }
+
+ /* Now either moveTerminal() or
+ resizeTerminal() based on value of
+ moveMode! */
+ if (moveMode)
+ {
+ moveTerminal(td->rect.x + value*delta_x,\
+ td->rect.y + value*delta_y);
+ }
+ else
+ {
+ resizeTerminal(td->cols + value*delta_x,\
+ td->rows + value*delta_y);
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else if (event.type == SDL_MOUSEBUTTONDOWN)
+ {
+ /* Store the coordinates where the button was pressed */
+ mouse_x = event.button.x;
+ mouse_y = event.button.y;
+ }
+ else if (event.type == SDL_MOUSEMOTION)
+ {
+ /* Mouse is moving... maybe move or resize the window, based
+ on the state of the mouse buttons */
+
+ /* To keep the motion quick, temporarily ignore all mouse motion
+ events until window moving is complete */
+ SDL_EventState(SDL_MOUSEMOTION,SDL_IGNORE);
+
+ if(event.motion.state & SDL_BUTTON(1))
+ {
+ /* the left mouse button is down, move the window,
+ do a differential based on where the button was pressed */
+ moveTerminal(td->rect.x + (event.motion.x - mouse_x), \
+ td->rect.y + (event.motion.y - mouse_y));
+ /* save the most current mouse location */
+ SDL_GetMouseState(&mouse_x,&mouse_y);
+ }
+
+ if(event.motion.state & SDL_BUTTON(3))
+ {
+ /* the right mouse button is down, so resize the window;
+ do a differential, but divide the number by the tile sizes */
+
+ /* see if at least one whole tile width/height has been
+ reached */
+ int delta_cols, delta_rows;
+ delta_cols = (int)floorf(\
+ (float)(event.motion.x - mouse_x)/td->tile_width);
+ delta_rows = (int)floorf(\
+ (float)(event.motion.y - mouse_y)/td->tile_height);
+ if ( delta_cols || delta_rows )
+ {
+ /* something changed, so update */
+ resizeTerminal(td->cols + delta_cols, \
+ td->rows + delta_rows);
+ /* save the most current mouse location */
+ SDL_GetMouseState(&mouse_x,&mouse_y);
+ }
+ }
+
+ /* Deal with mouse motion again */
+ SDL_EventState(SDL_MOUSEMOTION,SDL_ENABLE);
+
+ }
+ }
+ /* Perform the last redraw to take all changes
+ into account */
+ redrawAllTerminals();
+}
+
+/*************************************************
+ INITIALIZATION ROUTINES
+ *************************************************/
+
+static errr term_data_init(term_data *td, int i)
+{
+ term *t = &(td->t);
+ char env_var[80];
+ cptr val;
+
+
+ /***** load position, size information */
+
+ int cols, rows, x, y;
+
+ /* grab the column and row counts from
+ environmental variables for now */
+ sprintf(env_var,"TOME_NUM_COLS_%d",i);
+ val = getenv(env_var);
+ /* make sure it is valid */
+ if (val != NULL)
+ {
+ cols = atoi(val);
+ /* now make sure that the main window will
+ have at least 80x24 */
+ if (td == &data[0])
+ {
+ /* can't really pick an upper bound without
+ knowing what the position is... oh well. */
+ BOUND(cols,80,255);
+ }
+ }
+ else
+ {
+ /* no environmental variable... have to guess
+ something. If it's the main window, choose
+ the minimum. */
+ if (td == &data[0])
+ cols = 80;
+ else
+ cols = 5;
+ }
+ /* do the rows */
+ sprintf(env_var,"TOME_NUM_ROWS_%d",i);
+ val = getenv(env_var);
+ /* make sure it is valid */
+ if (val != NULL)
+ {
+ rows = atoi(val);
+ /* now make sure that the main window will
+ have at least 80x24 */
+ if (td == &data[0])
+ {
+ /* can't really pick an upper bound without
+ knowing what the position is... oh well. */
+ BOUND(rows,24,128);
+ }
+ }
+ else
+ {
+ /* no environmental variable... have to guess
+ something. If it's the main window, choose
+ the minimum. */
+ if (td == &data[0])
+ rows = 24;
+ else
+ rows = 3;
+ }
+ /* store these values in the term_data structure */
+ td->rows = rows;
+ td->cols = cols;
+
+ /* the position will be loaded from environmental
+ variables as well - for the time being*/
+ /* x-location */
+ sprintf(env_var,"TOME_X_POS_%d",i);
+ val = getenv(env_var);
+ /* make sure it is valid */
+ if (val != NULL)
+ {
+ x = atoi(val);
+ /* now do intelligent position checking */
+ BOUND(x,0,MAX_X(td));
+ }
+ else
+ {
+ /* no variable, choose something */
+ x = 20*i;
+ }
+ /* y-location */
+ sprintf(env_var,"TOME_Y_POS_%d",i);
+ val = getenv(env_var);
+ /* make sure it is valid */
+ if (val != NULL)
+ {
+ y = atoi(val);
+ /* position checking again */
+ BOUND(y,0,MAX_Y(td));
+ }
+ else
+ {
+ /* no variable */
+ y = 20*i;
+ }
+ /* and store these values into the structure */
+ td->rect.x = x;
+ td->rect.y = y;
+
+ /*********** term structure initializing */
+
+ /* Initialize the term
+ gets: pointer to address, number of columns, number of rows, number
+ of keypresses to queue up (guess 24?)*/
+ term_init(t, cols, rows, 24);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Picture routine flags */
+ t->always_pict = FALSE;
+ t->higher_pict = FALSE;
+ t->always_text = FALSE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_sdl;
+ t->curs_hook = Term_curs_sdl;
+ t->wipe_hook = Term_wipe_sdl;
+ t->text_hook = Term_text_sdl;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ /************* finish term_data intializing */
+
+ /* name of this term window */
+ td->name = angband_term_name[i];
+
+ /* For now, all font is the same size... use global t_width/height */
+ td->tile_width = t_width;
+ td->tile_height = t_height;
+
+ td->cushion_x_top = 1;
+ td->cushion_x_bot = 1;
+ td->cushion_y_top = 1;
+ td->cushion_y_bot = 1;
+
+ /* Now calculate the total width and height*/
+ UPDATE_SIZE(td);
+
+ /* Create a surface to draw to */
+ td->surf = createSurface(td->rect.w,td->rect.h);
+ SDL_SetAlpha(td->surf,0,0);
+
+ /* Key some colors to this surface */
+ td->black = SDL_MapRGB(td->surf->format, 0, 0, 0);
+ td->white = SDL_MapRGB(td->surf->format,255,255,255);
+ td->purple = SDL_MapRGB(td->surf->format,255, 0,255);
+
+ /* Turn on a border, thickness specified by BORDER_THICKNESS */
+ td->border_thick = BORDER_THICKNESS;
+
+ /* make the default terminal border color to be white */
+ td->border_color = td->white;
+
+
+ printf("Init-int term: %d\n",i);
+
+ /* Success */
+ return (0);
+}
+
+/* dumpWindowSettings is responsible for exporting all current
+values of the window positions, etc. to the screen, so that
+the user can see what the final values were after tweaking */
+void dumpWindowSettings(void)
+{
+ char name[80];
+ char value[8];
+ int i;
+
+ DB("Dumping settings");
+ printf("---------------------------\n");
+ /* cycle through each available terminal */
+ for (i=0; i<arg_console_count; i++)
+ {
+ printf("Terminal %d:\n",i);
+ /* get the name, and value of each value to dump */
+ sprintf(name,"TOME_X_POS_%d",i);
+ sprintf(value,"%d",data[i].rect.x);
+ printf("%s=%s\n",name,value);
+
+ sprintf(name,"TOME_Y_POS_%d",i);
+ sprintf(value,"%d",data[i].rect.y);
+ printf("%s=%s\n",name,value);
+
+ sprintf(name,"TOME_NUM_COLS_%d",i);
+ sprintf(value,"%d",data[i].cols);
+ printf("%s=%s\n",name,value);
+
+ sprintf(name,"TOME_NUM_ROWS_%d",i);
+ sprintf(value,"%d",data[i].rows);
+ printf("%s=%s\n",name,value);
+
+ /* Simple! */
+ printf("\n");
+ }
+}
+
+/* The main-sdl initialization routine!
+This routine processes arguments, opens the SDL
+window, loads fonts, etc. */
+errr init_sdl(int argc, char **argv)
+{
+ int i, surface_type;
+ char filename[PATH_MAX + 1];
+ const char file_sep = '.';
+ /* Flags to pass to SDL_SetVideoMode */
+ int videoFlags;
+
+ /* Before sdl_quit could possible be called, need to make sure that the text
+ array is zeroed, so that sdl_quit->killFontAndAlphabet() doesn't try to free
+ SDL_Surfaces that don't exist ! */
+ memset(text,0,sizeof(text));
+
+ /* Also, clear out the term order array */
+ memset(term_order,0,sizeof(term_order));
+
+ /* initialize SDL */
+ if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
+ {
+ sdl_quit("Video initialization failed!");
+ }
+ DB("SDL Initialized!");
+
+ /* get video info, to be used for determining if hardware acceleration is
+ available, pixel format, etc.... */
+ videoInfo = SDL_GetVideoInfo();
+
+ /* Environment calls to retrieve specific settings...
+ Note that these can be overridden by the command-line
+ arguments that are handled below */
+ if(getenv("TOME_CONSOLE_COUNT"))
+ arg_console_count = atoi(getenv("TOME_CONSOLE_COUNT"));
+ if(getenv("TOME_SCREEN_WIDTH"))
+ arg_width = atoi(getenv("TOME_SCREEN_WIDTH"));
+ if(getenv("TOME_SCREEN_HEIGHT"))
+ arg_height = atoi(getenv("TOME_SCREEN_HEIGHT"));
+ if(getenv("TOME_SCREEN_BPP"))
+ arg_bpp = atoi(getenv("TOME_SCREEN_BPP"));
+ if(getenv("TOME_FONT_SIZE"))
+ arg_font_size = atoi(getenv("TOME_FONT_SIZE"));
+
+ /* Argument handling routine;
+ the argv pointer is already pointing at the '--'
+ argument, so just start from there, parsing each
+ option as it comes along */
+ for (i=1; i < argc ; i++)
+ {
+ /* Set the number of consoles to handle
+ (ie the number of windows) */
+ if (0 == strcmp(argv[i], "-n"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -n\n");
+ return -1;
+ }
+
+ arg_console_count = atoi(argv[i]);
+ if (arg_console_count <= 0 || \
+ arg_console_count > MAX_CONSOLE_COUNT)
+ {
+ printf("Invalid console count given.\n");
+ arg_console_count = 1;
+ }
+ }
+ /* Set the SDL window/screen width in pixels */
+ else if (0 == strcmp(argv[i], "-w"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -w\n");
+ return -1;
+ }
+
+ arg_width = atoi(argv[i]);
+ }
+ /* Set the SDL window/screen height in pixels */
+ else if (0 == strcmp(argv[i], "-h"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -h\n");
+ return -1;
+ }
+
+ arg_height = atoi(argv[i]);
+ }
+ /* Set the SDL window/screen color depth
+ (in bits per pixel -- only 8,16,32 are okay) */
+ else if (0 == strcmp(argv[i], "-bpp"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -bpp\n");
+ return -1;
+ }
+
+ arg_bpp = atoi(argv[i]);
+ if ( (arg_bpp != 8) && (arg_bpp != 16) \
+ && (arg_bpp != 24) && (arg_bpp != 32) )
+ {
+ printf("Invalid color depth. Must be either 8, 16, or 32 bpp!\n");
+ return -1;
+ }
+ }
+ /* see if new graphics are requested...*/
+ else if (0 == strcmp(argv[i], "-g"))
+ {
+ printf("New graphics (16x16) enabled!\n");
+ arg_graphics_type = GRAPHICS_16x16;
+ }
+ /* see if old graphics are requested...*/
+ else if (0 == strcmp(argv[i], "-o"))
+ {
+ printf("Old graphics (8x8) enabled!\n");
+ arg_graphics_type = GRAPHICS_8x8;
+ }
+
+ /* see if double width tiles are requested */
+ else if (0 == strcmp(argv[i], "-b"))
+ {
+ /* do nothing for now */
+ /* arg_double_width = TRUE; */
+ }
+ /* switch into full-screen at startup */
+ else if (0 == strcmp(argv[i], "-fs"))
+ {
+ DB("Full-screen enabled!");
+ arg_full_screen = TRUE;
+ }
+ /* change the font size */
+ else if (0 == strcmp(argv[i], "-s"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -s\n");
+ printf("Please specify font size!\n");
+ return -1;
+ }
+
+ arg_font_size = atoi(argv[i]);
+ }
+ /* change the font to use */
+ else if (0 == strcmp(argv[i], "-f"))
+ {
+ /* Can we please not be so MS Windows-specific? One of the main goals
+ of SDL in ToME was to be more portable. These file name hacks are
+ only the idiom of that one OS, though. -- Neil */
+ DB("Getting font name");
+ if (++i == argc)
+ {
+ printf("Argument missing for option -f\n");
+ printf("Please specify a true-type font found in /lib/xtra/font!\n");
+ return -1;
+ }
+
+ /* tokenize the font name so that no .ttf extension
+ is required */
+ strcpy(arg_font_name,\
+ strtok(argv[i],&file_sep));
+
+ /* and append the extension */
+ strcat(arg_font_name,".ttf");
+
+ /* print a little debug message, so
+ user sees what font was actually selected */
+ printf("\tUsing font: %s\n",arg_font_name);
+
+ /* maybe check to see if file is even
+ existant in /lib/xtra/font */
+ }
+
+ } /* end argument handling */
+
+ /* Make sure that the engine will shutdown SDL properly*/
+ quit_aux = sdl_quit;
+
+ /* Use the ToME logo and set the window name */
+ filename[PATH_MAX] = 0;
+ path_build(filename, PATH_MAX, ANGBAND_DIR_XTRA, "graf/icon.png");
+ SDL_WM_SetIcon(IMG_Load(filename), 0);
+ SDL_WM_SetCaption("ToME", "tome");
+
+ /* SDL video settings, dependent on whether hardware is available */
+ if (videoInfo->hw_available)
+ videoFlags = SDL_HWSURFACE;
+ else
+ videoFlags = SDL_SWSURFACE;
+
+ /* Now ready the fonts! */
+
+ DB("initializing SDL_ttf");
+ if(TTF_Init()==-1) {
+ printf("TTF_Init: %s\n", TTF_GetError());
+ sdl_quit("Bah");
+ }
+
+ DB("loading font...");
+
+ /* load and render the font */
+ loadAndRenderFont(arg_font_name,arg_font_size);
+
+ /* Make the window a nice default size if none is specified */
+ if (arg_width < 1 || arg_height < 1)
+ {
+ arg_width = 80 * t_width;
+ arg_height = 24 * t_height;
+ }
+
+ /* now set the video mode that has been configured */
+ screen = SDL_SetVideoMode( arg_width, arg_height, arg_bpp, videoFlags );
+
+ /* Verify there is a surface */
+ if ( !screen )
+ {
+ DB("No screen!");
+ sdl_quit("Failed to set SDL Surface.");
+ }
+
+ DB("Video Mode Set!");
+
+ /* now switch into full screen if asked for */
+ if (arg_full_screen)
+ SDL_WM_ToggleFullScreen(screen);
+
+ DB("SDL Window Created!");
+
+ /* Graphics! ----
+ If graphics are selected, then load graphical tiles! */
+ if (arg_graphics_type != NO_GRAPHICS)
+ {
+ /* load graphics tiles */
+ }
+
+ /* Initialize the working surface and crayon surface used for rendering
+ text in different colors. */
+
+ worksurf = createSurface(t_width,t_height);
+ crayon = createSurface(t_width,t_height);
+
+ /* The working surface will blit using alpha values... */
+ SDL_SetAlpha(worksurf,SDL_SRCALPHA,0);
+
+ /* Set up the colors using the great little color macros! */
+ color_data[0] = BLACK;
+ color_data[1] = WHITE;
+ color_data[2] = MID_GREY;
+ color_data[3] = BRIGHT_ORANGE;
+ color_data[4] = RED;
+ color_data[5] = GREEN;
+ color_data[6] = BRIGHT_BLUE;
+ color_data[7] = DARK_ORANGE;
+ color_data[8] = DARK_GREY;
+ color_data[9] = BRIGHT_GREY;
+ color_data[10] = PURPLE;
+ color_data[11] = YELLOW;
+ color_data[12] = BRIGHT_RED;
+ color_data[13] = BRIGHT_GREEN;
+ color_data[14] = AQUAMARINE;
+ color_data[15] = BROWN;
+
+ /* And setup the cursor, using the default color...
+ XXX - in the future, this should (and will) be loaded from prefs */
+ createCursor(DEF_CURSOR_COLOR);
+
+ /* Initialize the windows, or whatever that means in this case.
+ Do this in reverse order so that the main window is on top.*/
+ suspendUpdate = TRUE; /* draw everything at the end */
+ i = arg_console_count;
+ while (i--)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+
+ /* Save global entry */
+ angband_term[i] = Term;
+
+ /* Add into term_order */
+ term_order[i] = td;
+
+ }
+
+ /* And setup the basic screen colors -- these are keyed to the format of
+ the main terminal surface */
+ screen_black = SDL_MapRGB(screen->format, 0, 0, 0);
+
+ suspendUpdate = FALSE; /* now draw everything */
+ redrawAllTerminals();
+ /*SDL_REDRAW_SCREEN;*/
+
+ /* now that the windows have been set, their settings can
+ be dumped upon quit! */
+ window_properties_set = TRUE;
+
+ /* Enable UNICODE keysyms - needed for current eventHandling routine */
+ SDL_EnableUNICODE(1);
+
+ /* Enable key repeat! */
+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
+
+ /* main-sdl initialized! */
+ return 0;
+}
+
+#endif
diff --git a/src/main-sla.c b/src/main-sla.c
new file mode 100644
index 00000000..3c02d61f
--- /dev/null
+++ b/src/main-sla.c
@@ -0,0 +1,455 @@
+/* File: main-sla.c */
+
+/* Purpose: Actual Unix "slang" support for Angband */
+
+/*
+ * Author: hans@grumbeer.pfalz.de (Hans-Joachim Baader)
+ *
+ * Most of this code is adapted directly from "main-gcu.c"
+ */
+
+#include "angband.h"
+
+
+#ifdef USE_SLA
+
+
+
+#include <slang.h>
+
+
+/*
+ * Are we "active"?
+ */
+static int slang_on = FALSE;
+
+
+/*
+ * Can we use "color"?
+ */
+static bool_ can_use_color = FALSE;
+
+
+/*
+ * Angband to SLang color conversion table
+ */
+static int colortable[16];
+
+
+/*
+ * Currently, only a single "term" is supported here
+ */
+static term term_screen_body;
+
+
+
+
+
+/*
+ * Hack -- see below
+ */
+void init_pair (int index, char *foreground, char *background)
+{
+ SLtt_set_color (index, "", foreground, background);
+}
+
+
+
+
+
+#define A_NORMAL 0
+#define A_BOLD 8
+#define A_REVERSE 0
+#define REVERSE 8
+
+#define COLOR_BLACK "black"
+#define COLOR_BLUE "blue"
+#define COLOR_GREEN "green"
+#define COLOR_CYAN "cyan"
+#define COLOR_RED "red"
+#define COLOR_MAGENTA "magenta"
+#define COLOR_YELLOW "brown"
+#define COLOR_WHITE "lightgray"
+#define COLOR_BBLACK "gray"
+#define COLOR_BBLUE "brightblue"
+#define COLOR_BGREEN "brightgreen"
+#define COLOR_BCYAN "brightcyan"
+#define COLOR_BRED "brightred"
+#define COLOR_BMAGENTA "brightmagenta"
+#define COLOR_BYELLOW "yellow"
+#define COLOR_BWHITE "white"
+
+
+
+
+
+static char *color_terminals [] =
+{
+#ifdef linux
+ "console",
+#endif
+ "linux",
+ "xterm-color",
+ "color-xterm",
+ "xtermc",
+ "ansi",
+ 0
+};
+
+
+
+
+
+/*
+ * Stolen from the Midnight Commander
+ */
+int has_colors(void)
+{
+ int i;
+
+ char *terminal;
+
+
+ /* Access the terminal type */
+ terminal = getenv("TERM");
+
+ /* Check for colors */
+ SLtt_Use_Ansi_Colors = 0;
+ if (NULL != getenv ("COLORTERM"))
+ {
+ SLtt_Use_Ansi_Colors = 1;
+ }
+
+ /* We want to allow overriding */
+ for (i = 0; color_terminals [i]; i++)
+ {
+ if (strcmp (color_terminals [i], terminal) == 0)
+ {
+ SLtt_Use_Ansi_Colors = 1;
+ }
+ }
+
+ /* Setup emulated colors */
+ if (SLtt_Use_Ansi_Colors)
+ {
+ /*init_pair (REVERSE, "black", "white");*/
+ }
+
+ /* Setup bizarre colors */
+ else
+ {
+ SLtt_set_mono (A_BOLD, NULL, SLTT_BOLD_MASK);
+ SLtt_set_mono (A_REVERSE, NULL, SLTT_REV_MASK);
+ SLtt_set_mono (A_BOLD | A_REVERSE, NULL, SLTT_BOLD_MASK | SLTT_REV_MASK);
+ }
+
+ return SLtt_Use_Ansi_Colors;
+}
+
+
+
+
+
+/*
+ * Nuke SLang
+ */
+static void Term_nuke_sla(term *t)
+{
+ if (!slang_on) return;
+
+ /* Show the cursor */
+ /* curs_set(1); */
+
+ /* Clear the screen */
+ (void)SLsmg_cls();
+
+ /* Refresh */
+ SLsmg_refresh();
+
+ /* We are now off */
+ slang_on = FALSE;
+
+ /* Shut down */
+ SLsmg_reset_smg();
+ SLang_reset_tty();
+}
+
+
+/*
+ * Init SLang
+ */
+static void Term_init_sla(term *t)
+{
+ /* Note that we are on */
+ slang_on = TRUE;
+}
+
+
+/*
+ * Process an event, wait if requested
+ */
+static errr Term_xtra_sla_event(int v)
+{
+ /* Do not wait unless requested */
+ if (!v && (SLang_input_pending (0) == 0)) return (1);
+
+ /* Get and enqueue the key */
+ Term_keypress(SLang_getkey ());
+
+ /* Success */
+ return 0;
+}
+
+
+
+/*
+ * Suspend / Resume
+ */
+static errr Term_xtra_sla_alive(int v)
+{
+ /* Suspend */
+ if (!v)
+ {
+ /* Oops */
+ if (!slang_on) return (1);
+
+ /* We are now off */
+ slang_on = FALSE;
+
+ /* Shut down (temporarily) */
+ SLsmg_reset_smg();
+ SLang_reset_tty();
+ }
+
+ /* Resume */
+ else
+ {
+ /* Oops */
+ if (slang_on) return (1);
+
+ /* Fix the screen */
+ SLsmg_refresh();
+
+ /* Note that we are on */
+ slang_on = TRUE;
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_sla(int n, int v)
+{
+ /* Analyze the request */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ (void)SLsmg_write_char('\007');
+ return (0);
+
+ /* Flush the ncurses buffer */
+ case TERM_XTRA_FRESH:
+ (void)SLsmg_refresh();
+ return (0);
+
+ /* Make the cursor invisible or visible */
+ case TERM_XTRA_SHAPE:
+ /* curs_set(v); */
+ return (0);
+
+ /* Handle events */
+ case TERM_XTRA_EVENT:
+ return (Term_xtra_sla_event(v));
+
+ /* Handle events */
+ case TERM_XTRA_FLUSH:
+ while (!Term_xtra_sla_event(FALSE));
+ return (0);
+
+ /* Suspend/Resume */
+ case TERM_XTRA_ALIVE:
+ return (Term_xtra_sla_alive(v));
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ (void)SLsmg_cls();
+ SLsmg_gotorc(0, 0);
+ return (0);
+
+ /* Delay */
+ case TERM_XTRA_DELAY:
+ usleep(1000 * v);
+ return (0);
+ }
+
+ /* Oops */
+ return (1);
+}
+
+
+
+
+/*
+ * Actually MOVE the hardware cursor
+ */
+static errr Term_curs_sla(int x, int y, int z)
+{
+ /* Literally move the cursor */
+ SLsmg_gotorc (y, x);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Erase some characters
+ */
+static errr Term_wipe_sla(int x, int y, int n)
+{
+ int i;
+
+ /* Place the cursor */
+ SLsmg_gotorc(y, x);
+
+ /* Dump spaces */
+ for (i = 0; i < n; i++) SLsmg_write_char(' ');
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Place some text on the screen using an attribute
+ */
+static errr Term_text_sla(int x, int y, int n, byte a, cptr s)
+{
+ /* Move the cursor */
+ SLsmg_gotorc(y, x);
+
+ /* Set the color */
+ if (can_use_color) SLsmg_set_color(colortable[a&0x0F]);
+
+ /* Dump the string */
+ SLsmg_write_nchars(s, n);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Prepare "SLang" for use by the file "term.c"
+ * Installs the "hook" functions defined above
+ */
+errr init_sla(void)
+{
+ int i, err;
+
+ term *t = &term_screen_body;
+
+
+ /* Initialize, check for errors */
+ err = (SLang_init_tty( -1, TRUE, 0) == -1);
+
+ /* Quit on error */
+ if (err) quit("SLang initialization failed");
+
+ /* Get terminal info */
+ SLtt_get_terminfo ();
+
+ /* Initialize some more */
+ if (SLsmg_init_smg() == 0)
+ {
+ quit("Could not get virtual display memory");
+ }
+
+ /* Check we have enough screen. */
+ err = ((SLtt_Screen_Rows < 24) || (SLtt_Screen_Cols < 80));
+
+ /* Quit with message */
+ if (err) quit("SLang screen must be at least 80x24");
+
+ /* Now let's go for a little bit of color! */
+ err = !has_colors();
+
+ /* Do we have color available? */
+ can_use_color = !err;
+
+ /* Init the Color-pairs and set up a translation table */
+ /* If the terminal has enough colors */
+ /* Color-pair 0 is *always* WHITE on BLACK */
+
+ /* XXX XXX XXX See "main-gcu.c" for proper method */
+
+ /* Only do this on color machines */
+ if (can_use_color)
+ {
+ /* Prepare the color pairs */
+ init_pair(1, COLOR_RED, COLOR_BLACK);
+ init_pair(2, COLOR_GREEN, COLOR_BLACK);
+ init_pair(3, COLOR_YELLOW, COLOR_BLACK);
+ init_pair(4, COLOR_BLUE, COLOR_BLACK);
+ init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
+ init_pair(6, COLOR_CYAN, COLOR_BLACK);
+ init_pair(7, COLOR_BLACK, COLOR_BLACK);
+ init_pair(9, COLOR_BRED, COLOR_BLACK);
+ init_pair(10, COLOR_BGREEN, COLOR_BLACK);
+ init_pair(11, COLOR_BYELLOW, COLOR_BLACK);
+ init_pair(12, COLOR_BBLUE, COLOR_BLACK);
+ init_pair(13, COLOR_BMAGENTA, COLOR_BLACK);
+ init_pair(14, COLOR_BCYAN, COLOR_BLACK);
+ init_pair(15, COLOR_BBLACK, COLOR_BLACK);
+
+ /* Prepare the color table */
+ colortable[0] = 7; /* Black */
+ colortable[1] = 0; /* White */
+ colortable[2] = 6; /* Grey XXX */
+ colortable[3] = 11; /* Orange XXX */
+ colortable[4] = 1; /* Red */
+ colortable[5] = 2; /* Green */
+ colortable[6] = 4; /* Blue */
+ colortable[7] = 3; /* Brown */
+ colortable[8] = 15; /* Dark-grey XXX */
+ colortable[9] = 14; /* Light-grey XXX */
+ colortable[10] = 5; /* Purple */
+ colortable[11] = 11; /* Yellow */
+ colortable[12] = 9; /* Light Red */
+ colortable[13] = 10; /* Light Green */
+ colortable[14] = 12; /* Light Blue */
+ colortable[15] = 3; /* Light Brown XXX */
+ }
+
+
+ /* Initialize the term */
+ term_init(t, 80, 24, 64);
+
+ /* Stick in some hooks */
+ t->nuke_hook = Term_nuke_sla;
+ t->init_hook = Term_init_sla;
+
+ /* Stick in some more hooks */
+ t->xtra_hook = Term_xtra_sla;
+ t->curs_hook = Term_curs_sla;
+ t->wipe_hook = Term_wipe_sla;
+ t->text_hook = Term_text_sla;
+
+ /* Save the term */
+ term_screen = t;
+
+ /* Activate it */
+ Term_activate(t);
+
+
+ /* Success */
+ return 0;
+}
+
+#endif /* USE_SLA */
+
diff --git a/src/main-win.c b/src/main-win.c
new file mode 100644
index 00000000..92a2a775
--- /dev/null
+++ b/src/main-win.c
@@ -0,0 +1,4368 @@
+/* File: main-win.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, Skirmantas Kligys, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with Windows computers.
+ *
+ * To use this file, use an appropriate "Makefile" or "Project File",
+ * make sure that "WINDOWS" and/or "WIN32" are defined somewhere, and
+ * make sure to obtain various extra files as described below.
+ *
+ * The official compilation uses the CodeWarrior Pro compiler, which
+ * includes a special project file and precompilable header file.
+ *
+ *
+ * See also "main-dos.c" and "main-ibm.c".
+ *
+ *
+ * The "lib/user/pref-win.prf" file contains keymaps, macro definitions,
+ * and/or color redefinitions.
+ *
+ * The "lib/user/font-win.prf" contains attr/char mappings for use with the
+ * normal "lib/xtra/font/ .fon" font files.
+ *
+ * The "lib/user/graf-win.prf" contains attr/char mappings for use with the
+ * special "lib/xtra/graf/ .bmp" bitmap files, which are activated by a
+ * menu item.
+ *
+ *
+ * Compiling this file, and using the resulting executable, requires
+ * several extra files not distributed with the standard Angband code.
+ * If "USE_GRAPHICS" is defined, then "readdib.h" and "readdib.c" must
+ * be placed into "src/", and the "8X8.BMP" bitmap file must be placed
+ * into "lib/xtra/graf". In any case, some "*.fon" files (including
+ * "8X13.FON" if nothing else) must be placed into "lib/xtra/font/".
+ * If "USE_SOUND" is defined, then some special library (for example,
+ * "winmm.lib") may need to be linked in, and desired "*.WAV" sound
+ * files must be placed into "lib/xtra/sound/". All of these extra
+ * files can be found in the "ext-win" archive.
+ *
+ *
+ * The "Term_xtra_win_clear()" function should probably do a low-level
+ * clear of the current window, and redraw the borders and other things,
+ * if only for efficiency. XXX XXX XXX
+ *
+ * A simpler method is needed for selecting the "tile size" for windows.
+ * XXX XXX XXX
+ *
+ * The various "warning" messages assume the existance of the "screen.w"
+ * window, I think, and only a few calls actually check for its existance,
+ * this may be okay since "NULL" means "on top of all windows". (?) The
+ * user must never be allowed to "hide" the main window, or the "menubar"
+ * will disappear. XXX XXX XXX
+ *
+ * Special "Windows Help Files" can be placed into "lib/xtra/help/" for
+ * use with the "winhelp.exe" program. These files *may* be available
+ * at the ftp site somewhere, but I have not seen them. XXX XXX XXX
+ *
+ *
+ * Initial framework (and most code) by Ben Harrison (benh@phial.com).
+ *
+ * Original code by Skirmantas Kligys (kligys@scf.usc.edu).
+ *
+ * Additional code by Ross E Becker (beckerr@cis.ohio-state.edu),
+ * and Chris R. Martin (crm7479@tam2000.tamu.edu).
+ */
+
+
+#include "angband.h"
+
+
+#ifdef WINDOWS
+
+
+/*
+ * Extract the "WIN32" flag from the compiler
+ */
+#if defined(__WIN32__) || defined(__WINNT__) || defined(__NT__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+
+
+/*
+ * Hack -- allow use of "screen saver" mode
+ */
+#define USE_SAVER
+
+/*
+ * Menu constants -- see "ANGBAND.RC"
+ */
+
+#define IDM_FILE_NEW 100
+#define IDM_FILE_OPEN 101
+#define IDM_FILE_SAVE 110
+#ifdef ALLOW_QUITTING
+# define IDM_FILE_ABORT 120
+#else
+# define IDM_FILE_SCORE 120
+#endif
+#define IDM_FILE_EXIT 121
+
+#define IDM_WINDOW_VIS_0 200
+#define IDM_WINDOW_VIS_1 201
+#define IDM_WINDOW_VIS_2 202
+#define IDM_WINDOW_VIS_3 203
+#define IDM_WINDOW_VIS_4 204
+#define IDM_WINDOW_VIS_5 205
+#define IDM_WINDOW_VIS_6 206
+#define IDM_WINDOW_VIS_7 207
+
+#define IDM_WINDOW_FONT_0 210
+#define IDM_WINDOW_FONT_1 211
+#define IDM_WINDOW_FONT_2 212
+#define IDM_WINDOW_FONT_3 213
+#define IDM_WINDOW_FONT_4 214
+#define IDM_WINDOW_FONT_5 215
+#define IDM_WINDOW_FONT_6 216
+#define IDM_WINDOW_FONT_7 217
+
+#define IDM_WINDOW_BIZ_0 230
+#define IDM_WINDOW_BIZ_1 231
+#define IDM_WINDOW_BIZ_2 232
+#define IDM_WINDOW_BIZ_3 233
+#define IDM_WINDOW_BIZ_4 234
+#define IDM_WINDOW_BIZ_5 235
+#define IDM_WINDOW_BIZ_6 236
+#define IDM_WINDOW_BIZ_7 237
+
+#define IDM_WINDOW_I_WID_0 240
+#define IDM_WINDOW_I_WID_1 241
+#define IDM_WINDOW_I_WID_2 242
+#define IDM_WINDOW_I_WID_3 243
+#define IDM_WINDOW_I_WID_4 244
+#define IDM_WINDOW_I_WID_5 245
+#define IDM_WINDOW_I_WID_6 246
+#define IDM_WINDOW_I_WID_7 247
+
+#define IDM_WINDOW_D_WID_0 250
+#define IDM_WINDOW_D_WID_1 251
+#define IDM_WINDOW_D_WID_2 252
+#define IDM_WINDOW_D_WID_3 253
+#define IDM_WINDOW_D_WID_4 254
+#define IDM_WINDOW_D_WID_5 255
+#define IDM_WINDOW_D_WID_6 256
+#define IDM_WINDOW_D_WID_7 257
+
+#define IDM_WINDOW_I_HGT_0 260
+#define IDM_WINDOW_I_HGT_1 261
+#define IDM_WINDOW_I_HGT_2 262
+#define IDM_WINDOW_I_HGT_3 263
+#define IDM_WINDOW_I_HGT_4 264
+#define IDM_WINDOW_I_HGT_5 265
+#define IDM_WINDOW_I_HGT_6 266
+#define IDM_WINDOW_I_HGT_7 267
+
+#define IDM_WINDOW_D_HGT_0 270
+#define IDM_WINDOW_D_HGT_1 271
+#define IDM_WINDOW_D_HGT_2 272
+#define IDM_WINDOW_D_HGT_3 273
+#define IDM_WINDOW_D_HGT_4 274
+#define IDM_WINDOW_D_HGT_5 275
+#define IDM_WINDOW_D_HGT_6 276
+#define IDM_WINDOW_D_HGT_7 277
+
+#define IDM_OPTIONS_OLD_GRAPHICS 400
+#define IDM_OPTIONS_NEW_GRAPHICS 401
+#define IDM_OPTIONS_ASCII_GRAPHICS 403
+#define IDM_OPTIONS_SOUND 402
+#define IDM_OPTIONS_BIGTILE 409
+#define IDM_OPTIONS_UNUSED 410
+#define IDM_OPTIONS_SAVER 411
+
+#define IDM_HELP_GENERAL 901
+#define IDM_HELP_SPOILERS 902
+
+
+/*
+ * This may need to be removed for some compilers XXX XXX XXX
+ */
+#define STRICT
+
+/*
+ * Exclude parts of WINDOWS.H that are not needed
+ */
+#define NOCOMM /* Comm driver APIs and definitions */
+#define NOLOGERROR /* LogError() and related definitions */
+#define NOPROFILER /* Profiler APIs */
+#define NOLFILEIO /* _l* file I/O routines */
+#define NOOPENFILE /* OpenFile and related definitions */
+#define NORESOURCE /* Resource management */
+#define NOATOM /* Atom management */
+#define NOLANGUAGE /* Character test routines */
+#define NOLSTRING /* lstr* string management routines */
+#define NODBCS /* Double-byte character set routines */
+#define NOKEYBOARDINFO /* Keyboard driver routines */
+#define NOCOLOR /* COLOR_* color values */
+#define NODRAWTEXT /* DrawText() and related definitions */
+#define NOSCALABLEFONT /* Truetype scalable font support */
+#define NOMETAFILE /* Metafile support */
+#define NOSYSTEMPARAMSINFO /* SystemParametersInfo() and SPI_* definitions */
+#define NODEFERWINDOWPOS /* DeferWindowPos and related definitions */
+#define NOKEYSTATES /* MK_* message key state flags */
+#define NOWH /* SetWindowsHook and related WH_* definitions */
+#define NOCLIPBOARD /* Clipboard APIs and definitions */
+#define NOICONS /* IDI_* icon IDs */
+#define NOMDI /* MDI support */
+#define NOHELP /* Help support */
+
+/* Not defined since it breaks Borland C++ 5.5 */
+/* #define NOCTLMGR */ /* Control management and controls */
+
+/*
+ * Exclude parts of WINDOWS.H that are not needed (Win32)
+ */
+#define WIN32_LEAN_AND_MEAN
+#define NONLS /* All NLS defines and routines */
+#define NOSERVICE /* All Service Controller routines, SERVICE_ equates, etc. */
+#define NOKANJI /* Kanji support stuff. */
+#define NOMCX /* Modem Configuration Extensions */
+
+/*
+ * Include the "windows" support file
+ */
+#include <windows.h>
+
+
+/*
+ * Exclude parts of MMSYSTEM.H that are not needed
+ */
+#define MMNODRV /* Installable driver support */
+#define MMNOWAVE /* Waveform support */
+#define MMNOMIDI /* MIDI support */
+#define MMNOAUX /* Auxiliary audio support */
+#define MMNOJOY /* Joystick support */
+#define MMNOMCI /* MCI support */
+#define MMNOMMIO /* Multimedia file I/O support */
+#define MMNOMMSYSTEM /* General MMSYSTEM functions */
+
+/*
+ * Include some more files
+ */
+#include <mmsystem.h>
+#include <commdlg.h>
+
+/*
+ * Include the support for loading bitmaps
+ */
+#ifdef USE_GRAPHICS
+# include "readdib.h"
+#endif
+
+/*
+ * Hack -- Fake declarations from "dos.h" XXX XXX XXX
+ */
+#ifdef WIN32
+#define INVALID_FILE_NAME (DWORD)0xFFFFFFFF
+#else /* WIN32 */
+#define FA_LABEL 0x08 /* Volume label */
+#define FA_DIREC 0x10 /* Directory */
+unsigned _cdecl _dos_getfileattr(const char *, unsigned *);
+#endif /* WIN32 */
+
+/*
+ * Silliness in WIN32 drawing routine
+ */
+#ifdef WIN32
+# define MoveTo(H, X, Y) MoveToEx(H, X, Y, NULL)
+#endif /* WIN32 */
+
+/*
+ * Silliness for Windows 95
+ */
+#ifndef WS_EX_TOOLWINDOW
+# define WS_EX_TOOLWINDOW 0
+#endif
+
+/*
+ * Foreground color bits (hard-coded by DOS)
+ */
+#define VID_BLACK 0x00
+#define VID_BLUE 0x01
+#define VID_GREEN 0x02
+#define VID_CYAN 0x03
+#define VID_RED 0x04
+#define VID_MAGENTA 0x05
+#define VID_YELLOW 0x06
+#define VID_WHITE 0x07
+
+/*
+ * Bright text (hard-coded by DOS)
+ */
+#define VID_BRIGHT 0x08
+
+/*
+ * Background color bits (hard-coded by DOS)
+ */
+#define VUD_BLACK 0x00
+#define VUD_BLUE 0x10
+#define VUD_GREEN 0x20
+#define VUD_CYAN 0x30
+#define VUD_RED 0x40
+#define VUD_MAGENTA 0x50
+#define VUD_YELLOW 0x60
+#define VUD_WHITE 0x70
+
+/*
+ * Blinking text (hard-coded by DOS)
+ */
+#define VUD_BRIGHT 0x80
+
+
+
+/*
+ * Forward declare
+ */
+typedef struct _term_data term_data;
+
+/*
+ * Extra "term" data
+ *
+ * Note the use of "font_want" for the names of the font file requested by
+ * the user, and the use of "font_file" for the currently active font file.
+ *
+ * The "font_file" is uppercased, and takes the form "8X13.FON", while
+ * "font_want" can be in almost any form as long as it could be construed
+ * as attempting to represent the name of a font.
+ */
+struct _term_data
+{
+ term t;
+
+ cptr s;
+
+ HWND w;
+
+ DWORD dwStyle;
+ DWORD dwExStyle;
+
+ uint keys;
+
+ uint rows;
+ uint cols;
+
+ uint pos_x;
+ uint pos_y;
+ uint size_wid;
+ uint size_hgt;
+ uint size_ow1;
+ uint size_oh1;
+ uint size_ow2;
+ uint size_oh2;
+
+ bool_ size_hack;
+
+ bool_ xtra_hack;
+
+ bool_ visible;
+
+ bool_ bizarre;
+
+ cptr font_want;
+
+ cptr font_file;
+
+ HFONT font_id;
+
+ uint font_wid;
+ uint font_hgt;
+
+ uint tile_wid;
+ uint tile_hgt;
+};
+
+
+/*
+ * Maximum number of windows XXX XXX XXX
+ */
+#define MAX_TERM_DATA 8
+
+/*
+ * An array of term_data's
+ */
+static term_data data[MAX_TERM_DATA];
+
+/*
+ * Hack -- global "window creation" pointer
+ */
+static term_data *my_td;
+
+/*
+ * game in progress
+ */
+bool_ game_in_progress = FALSE;
+
+/*
+ * note when "open"/"new" become valid
+ */
+bool_ initialized = FALSE;
+
+/*
+ * screen paletted, i.e. 256 colors
+ */
+bool_ paletted = FALSE;
+
+/*
+ * 16 colors screen, don't use RGB()
+ */
+bool_ colors16 = FALSE;
+
+/*
+ * Saved instance handle
+ */
+static HINSTANCE hInstance;
+
+/*
+ * Yellow brush for the cursor
+ */
+static HBRUSH hbrYellow;
+
+/*
+ * An icon
+ */
+static HICON hIcon;
+
+/*
+ * A palette
+ */
+static HPALETTE hPal;
+
+
+#ifdef USE_SAVER
+
+/*
+ * The screen saver window
+ */
+static HWND hwndSaver;
+
+#endif /* USE_SAVER */
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Flag set once "graphics" has been initialized
+ */
+static bool_ can_use_graphics = FALSE;
+
+/*
+ * The global bitmap
+ */
+static DIBINIT infGraph;
+
+/*
+ * The global bitmap mask
+ */
+static DIBINIT infMask;
+
+#endif /* USE_GRAPHICS */
+
+
+#ifdef USE_SOUND
+
+/*
+ * Flag set once "sound" has been initialized
+ */
+static bool_ can_use_sound = FALSE;
+
+/*
+ * An array of sound file names
+ */
+static cptr sound_file[SOUND_MAX];
+
+#endif /* USE_SOUND */
+
+
+/*
+ * Full path to ANGBAND.INI
+ */
+static cptr ini_file = NULL;
+
+/*
+ * Name of application
+ */
+static cptr AppName = "ANGBAND";
+
+/*
+ * Name of sub-window type
+ */
+static cptr AngList = "AngList";
+
+/*
+ * Directory names
+ */
+static cptr ANGBAND_DIR_XTRA_FONT;
+static cptr ANGBAND_DIR_XTRA_GRAF;
+static cptr ANGBAND_DIR_XTRA_SOUND;
+static cptr ANGBAND_DIR_XTRA_HELP;
+
+
+/*
+ * The "complex" color values
+ */
+static COLORREF win_clr[256];
+
+
+/*
+ * The "simple" color values
+ *
+ * See "main-ibm.c" for original table information
+ *
+ * The entries below are taken from the "color bits" defined above.
+ *
+ * Note that many of the choices below suck, but so do crappy monitors.
+ */
+static BYTE win_pal[256] =
+{
+ VID_BLACK, /* Dark */
+ VID_WHITE, /* White */
+ VID_CYAN, /* Slate XXX */
+ VID_RED | VID_BRIGHT, /* Orange XXX */
+ VID_RED, /* Red */
+ VID_GREEN, /* Green */
+ VID_BLUE, /* Blue */
+ VID_YELLOW, /* Umber XXX */
+ VID_BLACK | VID_BRIGHT, /* Light Dark */
+ VID_CYAN | VID_BRIGHT, /* Light Slate XXX */
+ VID_MAGENTA, /* Violet XXX */
+ VID_YELLOW | VID_BRIGHT, /* Yellow */
+ VID_MAGENTA | VID_BRIGHT, /* Light Red XXX */
+ VID_GREEN | VID_BRIGHT, /* Light Green */
+ VID_BLUE | VID_BRIGHT, /* Light Blue */
+ VID_YELLOW /* Light Umber XXX */
+};
+
+
+/*
+ * Hack -- define which keys are "special"
+ */
+static bool_ special_key[256];
+static bool_ ignore_key[256];
+
+#if 1
+/*
+ * Hack -- initialization list for "special_key"
+ */
+static byte special_key_list[] = {
+ VK_CLEAR, VK_PAUSE, VK_CAPITAL, VK_KANA,
+#ifdef JP
+ VK_JUNJA, VK_FINAL, VK_KANJI, VK_CONVERT, VK_NONCONVERT, VK_ACCEPT, VK_MODECHANGE,
+#endif
+ VK_PRIOR, VK_NEXT, VK_END, VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN,
+ VK_SELECT, VK_PRINT, VK_EXECUTE, VK_SNAPSHOT, VK_INSERT, VK_DELETE,
+ VK_HELP, VK_APPS,
+ VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10,
+ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, VK_F19, VK_F20,
+ VK_F21, VK_F22, VK_F23, VK_F24, VK_NUMLOCK, VK_SCROLL,
+ VK_ATTN, VK_CRSEL, VK_EXSEL, VK_EREOF, VK_PLAY, VK_ZOOM, VK_NONAME,
+ VK_PA1, 0
+};
+
+static byte ignore_key_list[] = {
+ VK_ESCAPE, VK_TAB, VK_SPACE,
+ 'F', 'W', 'O', 'H', /* these are menu characters.*/
+ VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, VK_RWIN,
+ VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, VK_LMENU, VK_RMENU, 0
+};
+
+#else
+/*
+* Hack -- initialization list for "special_key"
+*
+* We ignore the modifier keys (shift, control, alt, num lock, scroll lock),
+* and the normal keys (escape, tab, return, letters, numbers, etc), but we
+* catch the keypad keys (with and without numlock set, including keypad 5),
+* the function keys (including the "menu" key which maps to F10), and the
+* "pause" key (between scroll lock and numlock). We also catch a few odd
+* keys which I do not recognize, but which are listed among keys which we
+* do catch, so they should be harmless to catch.
+*/
+static byte special_key_list[] =
+{
+ VK_CLEAR, /* 0x0C (KP<5>) */
+
+ VK_PAUSE, /* 0x13 (pause) */
+
+ VK_PRIOR, /* 0x21 (KP<9>) */
+ VK_NEXT, /* 0x22 (KP<3>) */
+ VK_END, /* 0x23 (KP<1>) */
+ VK_HOME, /* 0x24 (KP<7>) */
+ VK_LEFT, /* 0x25 (KP<4>) */
+ VK_UP, /* 0x26 (KP<8>) */
+ VK_RIGHT, /* 0x27 (KP<6>) */
+ VK_DOWN, /* 0x28 (KP<2>) */
+ VK_SELECT, /* 0x29 (?????) */
+ VK_PRINT, /* 0x2A (?????) */
+ VK_EXECUTE, /* 0x2B (?????) */
+ VK_SNAPSHOT, /* 0x2C (?????) */
+ VK_INSERT, /* 0x2D (KP<0>) */
+ VK_DELETE, /* 0x2E (KP<.>) */
+ VK_HELP, /* 0x2F (?????) */
+
+#if 0
+ VK_NUMPAD0, /* 0x60 (KP<0>) */
+ VK_NUMPAD1, /* 0x61 (KP<1>) */
+ VK_NUMPAD2, /* 0x62 (KP<2>) */
+ VK_NUMPAD3, /* 0x63 (KP<3>) */
+ VK_NUMPAD4, /* 0x64 (KP<4>) */
+ VK_NUMPAD5, /* 0x65 (KP<5>) */
+ VK_NUMPAD6, /* 0x66 (KP<6>) */
+ VK_NUMPAD7, /* 0x67 (KP<7>) */
+ VK_NUMPAD8, /* 0x68 (KP<8>) */
+ VK_NUMPAD9, /* 0x69 (KP<9>) */
+ VK_MULTIPLY, /* 0x6A (KP<*>) */
+ VK_ADD, /* 0x6B (KP<+>) */
+ VK_SEPARATOR, /* 0x6C (?????) */
+ VK_SUBTRACT, /* 0x6D (KP<->) */
+ VK_DECIMAL, /* 0x6E (KP<.>) */
+ VK_DIVIDE, /* 0x6F (KP</>) */
+#endif
+
+ VK_F1, /* 0x70 */
+ VK_F2, /* 0x71 */
+ VK_F3, /* 0x72 */
+ VK_F4, /* 0x73 */
+ VK_F5, /* 0x74 */
+ VK_F6, /* 0x75 */
+ VK_F7, /* 0x76 */
+ VK_F8, /* 0x77 */
+ VK_F9, /* 0x78 */
+ VK_F10, /* 0x79 */
+ VK_F11, /* 0x7A */
+ VK_F12, /* 0x7B */
+ VK_F13, /* 0x7C */
+ VK_F14, /* 0x7D */
+ VK_F15, /* 0x7E */
+ VK_F16, /* 0x7F */
+ VK_F17, /* 0x80 */
+ VK_F18, /* 0x81 */
+ VK_F19, /* 0x82 */
+ VK_F20, /* 0x83 */
+ VK_F21, /* 0x84 */
+ VK_F22, /* 0x85 */
+ VK_F23, /* 0x86 */
+ VK_F24, /* 0x87 */
+
+ 0
+};
+#endif
+
+
+/*
+ * Hack -- given a pathname, point at the filename
+ */
+static cptr extract_file_name(cptr s)
+{
+ cptr p;
+
+ /* Start at the end */
+ p = s + strlen(s) - 1;
+
+ /* Back up to divider */
+ while ((p >= s) && (*p != ':') && (*p != '\\')) p--;
+
+ /* Return file name */
+ return (p + 1);
+}
+
+
+/*
+ * Hack -- given a simple filename, extract the "font size" info
+ *
+ * Return a pointer to a static buffer holding the capitalized base name.
+ */
+static char *analyze_font(char *path, int *wp, int *hp)
+{
+ int wid, hgt;
+
+ char *s, *p;
+
+ /* Start at the end */
+ p = path + strlen(path) - 1;
+
+ /* Back up to divider */
+ while ((p >= path) && (*p != ':') && (*p != '\\')) --p;
+
+ /* Advance to file name */
+ ++p;
+
+ /* Capitalize */
+ for (s = p; *s; ++s)
+ {
+ /* Capitalize (be paranoid) */
+ if (islower(*s)) *s = toupper(*s);
+ }
+
+ /* Find first 'X' */
+ s = strchr(p, 'X');
+
+ /* Extract font width */
+ wid = atoi(p);
+
+ /* Extract height */
+ hgt = s ? atoi(s + 1) : 0;
+
+ /* Save results */
+ (*wp) = wid;
+ (*hp) = hgt;
+
+ /* Result */
+ return (p);
+}
+
+
+/*
+ * Check for existance of a file
+ */
+static bool_ check_file(cptr s)
+{
+ char path[1024];
+
+#ifdef WIN32
+
+ DWORD attrib;
+
+#else /* WIN32 */
+
+ unsigned int attrib;
+
+#endif /* WIN32 */
+
+ /* Copy it */
+ strcpy(path, s);
+
+#ifdef WIN32
+
+ /* Examine */
+ attrib = GetFileAttributes(path);
+
+ /* Require valid filename */
+ if (attrib == INVALID_FILE_NAME) return (FALSE);
+
+ /* Prohibit directory */
+ if (attrib & FILE_ATTRIBUTE_DIRECTORY) return (FALSE);
+
+#else /* WIN32 */
+
+ /* Examine and verify */
+ if (_dos_getfileattr(path, &attrib)) return (FALSE);
+
+ /* Prohibit something */
+ if (attrib & FA_LABEL) return (FALSE);
+
+ /* Prohibit directory */
+ if (attrib & FA_DIREC) return (FALSE);
+
+#endif /* WIN32 */
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Check for existance of a directory
+ */
+static bool_ check_dir(cptr s)
+{
+ int i;
+
+ char path[1024];
+
+#ifdef WIN32
+
+ DWORD attrib;
+
+#else /* WIN32 */
+
+ unsigned int attrib;
+
+#endif /* WIN32 */
+
+ /* Copy it */
+ strcpy(path, s);
+
+ /* Check length */
+ i = strlen(path);
+
+ /* Remove trailing backslash */
+ if (i && (path[i - 1] == '\\')) path[--i] = '\0';
+
+#ifdef WIN32
+
+ /* Examine */
+ attrib = GetFileAttributes(path);
+
+ /* Require valid filename */
+ if (attrib == INVALID_FILE_NAME) return (FALSE);
+
+ /* Require directory */
+ if (!(attrib & FILE_ATTRIBUTE_DIRECTORY)) return (FALSE);
+
+#else /* WIN32 */
+
+ /* Examine and verify */
+ if (_dos_getfileattr(path, &attrib)) return (FALSE);
+
+ /* Prohibit something */
+ if (attrib & FA_LABEL) return (FALSE);
+
+ /* Require directory */
+ if (!(attrib & FA_DIREC)) return (FALSE);
+
+#endif /* WIN32 */
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Validate a file
+ */
+static void validate_file(cptr s)
+{
+ /* Verify or fail */
+ if (!check_file(s))
+ {
+ quit_fmt("Cannot find required file:\n%s", s);
+ }
+}
+
+
+/*
+ * Validate a directory
+ */
+static void validate_dir(cptr s)
+{
+ /* Verify or fail */
+ if (!check_dir(s))
+ {
+ quit_fmt("Cannot find required directory:\n%s", s);
+ }
+}
+
+
+/*
+ * Get the "size" for a window
+ */
+static void term_getsize(term_data *td)
+{
+ RECT rc;
+
+ int wid, hgt;
+
+ /*
+ * The Angband window is always at least 80x24. I'm not sure if
+ * small windows work... -- pelpel
+ */
+ if (td == &data[0])
+ {
+ if (td->cols < 80) td->cols = 80;
+ if (td->rows < 24) td->rows = 24;
+ }
+
+ /* The other windows can be smaller */
+ else
+ {
+ /* Paranoia */
+ if (td->cols < 1) td->cols = 1;
+ if (td->rows < 1) td->rows = 1;
+ }
+
+ /* Paranoia */
+ if (td->cols > 255) td->cols = 255;
+ if (td->rows > 255) td->rows = 255;
+
+ /* Window sizes */
+ wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2;
+ hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ /* Fake window size */
+ rc.left = 0;
+ rc.right = rc.left + wid;
+ rc.top = 0;
+ rc.bottom = rc.top + hgt;
+
+ /* XXX XXX XXX */
+ /* rc.right += 1; */
+ /* rc.bottom += 1; */
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Total size */
+ td->size_wid = rc.right - rc.left;
+ td->size_hgt = rc.bottom - rc.top;
+
+ /* See CreateWindowEx */
+ if (!td->w) return;
+
+ /* Extract actual location */
+ GetWindowRect(td->w, &rc);
+
+ /* Save the location */
+ td->pos_x = rc.left;
+ td->pos_y = rc.top;
+}
+
+
+/*
+ * Write the "prefs" for a single term
+ */
+static void save_prefs_aux(term_data *td, cptr sec_name)
+{
+ char buf[1024];
+
+ RECT rc;
+
+ /* Paranoia */
+ if (!td->w) return;
+
+ /* Visible */
+ strcpy(buf, td->visible ? "1" : "0");
+ WritePrivateProfileString(sec_name, "Visible", buf, ini_file);
+
+ /* Font */
+ strcpy(buf, td->font_file ? td->font_file : "8X13.FON");
+ WritePrivateProfileString(sec_name, "Font", buf, ini_file);
+
+ /* Bizarre */
+ strcpy(buf, td->bizarre ? "1" : "0");
+ WritePrivateProfileString(sec_name, "Bizarre", buf, ini_file);
+
+ /* Tile size (x) */
+ wsprintf(buf, "%d", td->tile_wid);
+ WritePrivateProfileString(sec_name, "TileWid", buf, ini_file);
+
+ /* Tile size (y) */
+ wsprintf(buf, "%d", td->tile_hgt);
+ WritePrivateProfileString(sec_name, "TileHgt", buf, ini_file);
+
+ /* Window size (x) */
+ wsprintf(buf, "%d", td->cols);
+ WritePrivateProfileString(sec_name, "NumCols", buf, ini_file);
+
+ /* Window size (y) */
+ wsprintf(buf, "%d", td->rows);
+ WritePrivateProfileString(sec_name, "NumRows", buf, ini_file);
+
+ /* Acquire position */
+ GetWindowRect(td->w, &rc);
+
+ /* Window position (x) */
+ wsprintf(buf, "%d", rc.left);
+ WritePrivateProfileString(sec_name, "PositionX", buf, ini_file);
+
+ /* Window position (y) */
+ wsprintf(buf, "%d", rc.top);
+ WritePrivateProfileString(sec_name, "PositionY", buf, ini_file);
+}
+
+
+/*
+ * Write the "prefs"
+ *
+ * We assume that the windows have all been initialized
+ */
+static void save_prefs(void)
+{
+ int i;
+
+ char buf[128];
+
+ /* Save the "arg_graphics" flag */
+ sprintf(buf, "%d", arg_graphics);
+ WritePrivateProfileString("Angband", "Graphics", buf, ini_file);
+
+ /* Save the "arg_bigtile" flag */
+ strcpy(buf, arg_bigtile ? "1" : "0");
+ WritePrivateProfileString("Angband", "Bigtile", buf, ini_file);
+
+ /* Save the "arg_sound" flag */
+ strcpy(buf, arg_sound ? "1" : "0");
+ WritePrivateProfileString("Angband", "Sound", buf, ini_file);
+
+ /* Save window prefs */
+ for (i = 0; i < MAX_TERM_DATA; ++i)
+ {
+ term_data *td = &data[i];
+
+ sprintf(buf, "Term-%d", i);
+
+ save_prefs_aux(td, buf);
+ }
+}
+
+
+/*
+ * Load the "prefs" for a single term
+ */
+static void load_prefs_aux(term_data *td, cptr sec_name)
+{
+ char tmp[1024];
+
+ int wid, hgt;
+
+ /* Visible */
+ td->visible = (GetPrivateProfileInt(sec_name, "Visible", td->visible, ini_file) != 0);
+
+ /* Desired font, with default */
+ GetPrivateProfileString(sec_name, "Font", "8X13.FON", tmp, 127, ini_file);
+
+ /* Bizarre */
+ td->bizarre = (GetPrivateProfileInt(sec_name, "Bizarre", td->bizarre, ini_file) != 0);
+
+ /* Analyze font, save desired font name */
+ td->font_want = string_make(analyze_font(tmp, &wid, &hgt));
+
+ /* Tile size */
+ td->tile_wid = GetPrivateProfileInt(sec_name, "TileWid", wid, ini_file);
+ td->tile_hgt = GetPrivateProfileInt(sec_name, "TileHgt", hgt, ini_file);
+
+ /* Window size */
+ td->cols = GetPrivateProfileInt(sec_name, "NumCols", td->cols, ini_file);
+ td->rows = GetPrivateProfileInt(sec_name, "NumRows", td->rows, ini_file);
+
+ /* Window position */
+ td->pos_x = GetPrivateProfileInt(sec_name, "PositionX", td->pos_x, ini_file);
+ td->pos_y = GetPrivateProfileInt(sec_name, "PositionY", td->pos_y, ini_file);
+}
+
+
+/*
+ * Load the "prefs"
+ */
+static void load_prefs(void)
+{
+ int i;
+
+ char buf[1024];
+
+ /* Extract the "arg_graphics" flag */
+ arg_graphics = GetPrivateProfileInt("Angband", "Graphics", 0, ini_file);
+
+ /* Extract the "arg_bigtile" flag */
+ arg_bigtile = GetPrivateProfileInt("Angband", "Bigtile", FALSE, ini_file);
+ use_bigtile = arg_bigtile;
+
+ /* Extract the "arg_sound" flag */
+ arg_sound = (GetPrivateProfileInt("Angband", "Sound", 0, ini_file) != 0);
+
+ /* Load window prefs */
+ for (i = 0; i < MAX_TERM_DATA; ++i)
+ {
+ term_data *td = &data[i];
+
+ sprintf(buf, "Term-%d", i);
+
+ load_prefs_aux(td, buf);
+ }
+}
+
+
+/*
+ * Create the new global palette based on the bitmap palette
+ * (if any), and the standard 16 entry palette derived from
+ * "win_clr[]" which is used for the basic 16 Angband colors.
+ *
+ * This function is never called before all windows are ready.
+ *
+ * This function returns FALSE if the new palette could not be
+ * prepared, which should normally be a fatal error. XXX XXX
+ *
+ * Note that only some machines actually use a "palette".
+ */
+static int new_palette(void)
+{
+ HPALETTE hBmPal;
+ HPALETTE hNewPal;
+ HDC hdc;
+ int i, nEntries;
+ int pLogPalSize;
+ int lppeSize;
+ LPLOGPALETTE pLogPal;
+ LPPALETTEENTRY lppe;
+
+ term_data *td;
+
+
+ /* This makes no sense */
+ if (!paletted) return (TRUE);
+
+
+ /* No palette */
+ hBmPal = NULL;
+
+ /* No bitmap */
+ lppeSize = 0;
+ lppe = NULL;
+ nEntries = 0;
+
+#ifdef USE_GRAPHICS
+
+ /* Check the bitmap palette */
+ hBmPal = infGraph.hPalette;
+
+ /* Use the bitmap */
+ if (hBmPal)
+ {
+ lppeSize = 256 * sizeof(PALETTEENTRY);
+ lppe = (LPPALETTEENTRY)ralloc(lppeSize);
+ nEntries = GetPaletteEntries(hBmPal, 0, 255, lppe);
+ if ((nEntries == 0) || (nEntries > 220))
+ {
+ /* Warn the user */
+ plog_fmt("Unusable bitmap palette (%d entries)", nEntries);
+
+ /* Cleanup */
+ rnfree(lppe, lppeSize);
+
+ /* Fail */
+ return (FALSE);
+ }
+ }
+
+#endif
+
+ /* Size of palette */
+ pLogPalSize = sizeof(LOGPALETTE) + (nEntries + 16) * sizeof(PALETTEENTRY);
+
+ /* Allocate palette */
+ pLogPal = (LPLOGPALETTE)ralloc(pLogPalSize);
+
+ /* Version */
+ pLogPal->palVersion = 0x300;
+
+ /* Make room for bitmap and normal data */
+ pLogPal->palNumEntries = nEntries + 16;
+
+ /* Save the bitmap data */
+ for (i = 0; i < nEntries; i++)
+ {
+ pLogPal->palPalEntry[i] = lppe[i];
+ }
+
+ /* Save the normal data */
+ for (i = 0; i < 16; i++)
+ {
+ LPPALETTEENTRY p;
+
+ /* Access the entry */
+ p = &(pLogPal->palPalEntry[i + nEntries]);
+
+ /* Save the colors */
+ p->peRed = GetRValue(win_clr[i]);
+ p->peGreen = GetGValue(win_clr[i]);
+ p->peBlue = GetBValue(win_clr[i]);
+
+ /* Save the flags */
+ p->peFlags = PC_NOCOLLAPSE;
+ }
+
+ /* Free something */
+ if (lppe) rnfree(lppe, lppeSize);
+
+ /* Create a new palette, or fail */
+ hNewPal = CreatePalette(pLogPal);
+ if (!hNewPal) quit("Cannot create palette!");
+
+ /* Free the palette */
+ rnfree(pLogPal, pLogPalSize);
+
+ /* Main window */
+ td = &data[0];
+
+ /* Realize the palette */
+ hdc = GetDC(td->w);
+ SelectPalette(hdc, hNewPal, 0);
+ i = RealizePalette(hdc);
+ ReleaseDC(td->w, hdc);
+ if (i == 0) quit("Cannot realize palette!");
+
+ /* Sub-windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ td = &data[i];
+
+ hdc = GetDC(td->w);
+ SelectPalette(hdc, hNewPal, 0);
+ ReleaseDC(td->w, hdc);
+ }
+
+ /* Delete old palette */
+ if (hPal) DeleteObject(hPal);
+
+ /* Save new palette */
+ hPal = hNewPal;
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Initialize graphics
+ */
+static bool_ init_graphics()
+{
+ /* Initialize once */
+ /*if (can_use_graphics != arg_graphics) */
+ {
+ char buf[1024];
+ int wid, hgt;
+ cptr name;
+
+ /* Unused */
+ PALETTEENTRY entry =
+ {
+ 0, 0, 0, 0
+ };
+ (void)entry;
+
+ if (arg_graphics == 2)
+ {
+ wid = 16;
+ hgt = 16;
+
+ name = "16X16.BMP";
+
+ ANGBAND_GRAF = "new";
+ }
+ else
+ {
+ wid = 8;
+ hgt = 8;
+
+ name = "8X8.BMP";
+ ANGBAND_GRAF = "old";
+ }
+
+ /* Access the bitmap file */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_GRAF, name);
+
+ /* Load the bitmap or quit */
+ if (!ReadDIB(data[0].w, buf, &infGraph))
+ {
+ plog_fmt("Cannot read bitmap file '%s'", name);
+ return (FALSE);
+ }
+
+ /* Save the new sizes */
+ infGraph.CellWidth = wid;
+ infGraph.CellHeight = hgt;
+
+
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_GRAF, "mask.bmp");
+ /* Load the bitmap or quit */
+ if (!ReadDIB(data[0].w, buf, &infMask))
+ {
+ plog_fmt("Cannot read bitmap file '%s'", name);
+ return (FALSE);
+ }
+
+ /* Activate a palette */
+ if (!new_palette())
+ {
+ /* Free bitmap XXX XXX XXX */
+
+ /* Oops */
+ plog("Cannot activate palette!");
+ return (FALSE);
+ }
+
+ /* Graphics available */
+ can_use_graphics = arg_graphics;
+ }
+
+ /* Result */
+ return (can_use_graphics);
+}
+
+
+/*
+ * Initialize sound
+ */
+static bool_ init_sound()
+{
+ /* Initialize once */
+ if (!can_use_sound)
+ {
+ int i;
+
+ char wav[128];
+ char buf[1024];
+
+ /* Prepare the sounds */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ /* Extract name of sound file */
+ sprintf(wav, "%s.wav", angband_sound_name[i]);
+
+ /* Access the sound */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_SOUND, wav);
+
+ /* Save the sound filename, if it exists */
+ if (check_file(buf)) sound_file[i] = string_make(buf);
+ }
+
+ /* Sound available */
+ can_use_sound = TRUE;
+ }
+
+ /* Result */
+ return (can_use_sound);
+}
+
+
+
+/*
+ * Resize a window
+ */
+static void term_window_resize(term_data *td)
+{
+ /* Require window */
+ if (!td->w) return;
+
+ /* Resize the window */
+ SetWindowPos(td->w, 0, 0, 0,
+ td->size_wid, td->size_hgt,
+ SWP_NOMOVE | SWP_NOZORDER);
+
+ /* Redraw later */
+ InvalidateRect(td->w, NULL, TRUE);
+}
+
+
+
+/*
+ * Force the use of a new "font file" for a term_data
+ *
+ * This function may be called before the "window" is ready
+ *
+ * This function returns zero only if everything succeeds.
+ *
+ * Note that the "font name" must be capitalized!!!
+ */
+static errr term_force_font(term_data *td, cptr path)
+{
+ int i;
+
+ int wid, hgt;
+
+ char *base;
+
+ char buf[1024];
+
+
+ /* Forget the old font (if needed) */
+ if (td->font_id) DeleteObject(td->font_id);
+
+ /* Forget old font */
+ if (td->font_file)
+ {
+ bool_ used = FALSE;
+
+ /* Scan windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Check "screen" */
+ if ((td != &data[i]) &&
+ (data[i].font_file) &&
+ (streq(data[i].font_file, td->font_file)))
+ {
+ used = TRUE;
+ }
+ }
+
+ /* Remove unused font resources */
+ if (!used) RemoveFontResource(td->font_file);
+
+ /* Free the old name */
+ string_free(td->font_file);
+
+ /* Forget it */
+ td->font_file = NULL;
+ }
+
+
+ /* No path given */
+ if (!path) return (1);
+
+
+ /* Local copy */
+ strcpy(buf, path);
+
+ /* Analyze font path */
+ base = analyze_font(buf, &wid, &hgt);
+
+ /* Verify suffix */
+ if (!suffix(base, ".FON")) return (1);
+
+ /* Verify file */
+ if (!check_file(buf)) return (1);
+
+ /* Load the new font */
+ if (!AddFontResource(buf)) return (1);
+
+ /* Save new font name */
+ td->font_file = string_make(base);
+
+ /* Remove the "suffix" */
+ base[strlen(base) - 4] = '\0';
+
+ /* Create the font (using the 'base' of the font file name!) */
+ td->font_id = CreateFont(hgt, wid, 0, 0, FW_DONTCARE, 0, 0, 0,
+ ANSI_CHARSET, OUT_DEFAULT_PRECIS,
+ CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ FIXED_PITCH | FF_DONTCARE, base);
+
+ /* Hack -- Unknown size */
+ if (!wid || !hgt)
+ {
+ HDC hdcDesktop;
+ HFONT hfOld;
+ TEXTMETRIC tm;
+
+ /* all this trouble to get the cell size */
+ hdcDesktop = GetDC(HWND_DESKTOP);
+ hfOld = SelectObject(hdcDesktop, td->font_id);
+ GetTextMetrics(hdcDesktop, &tm);
+ SelectObject(hdcDesktop, hfOld);
+ ReleaseDC(HWND_DESKTOP, hdcDesktop);
+
+ /* Font size info */
+ wid = tm.tmAveCharWidth;
+ hgt = tm.tmHeight;
+ }
+
+ /* Save the size info */
+ td->font_wid = wid;
+ td->font_hgt = hgt;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Allow the user to change the font for this window.
+ */
+static void term_change_font(term_data *td)
+{
+ OPENFILENAME ofn;
+
+ char tmp[1024] = "";
+
+ /* Extract a default if possible */
+ if (td->font_file) strcpy(tmp, td->font_file);
+
+ /* Ask for a choice */
+ memset(&ofn, 0, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = data[0].w;
+ ofn.lpstrFilter = "Angband Font Files (*.fon)\0*.fon\0";
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = tmp;
+ ofn.nMaxFile = 128;
+ ofn.lpstrInitialDir = ANGBAND_DIR_XTRA_FONT;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
+ ofn.lpstrDefExt = "fon";
+
+ /* Force choice if legal */
+ if (GetOpenFileName(&ofn))
+ {
+ /* Force the font */
+ if (term_force_font(td, tmp))
+ {
+ /* Access the standard font file */
+ path_build(tmp, 1024, ANGBAND_DIR_XTRA_FONT, "8X13.FON");
+
+ /* Force the use of that font */
+ (void)term_force_font(td, tmp);
+ }
+
+ /* Assume not bizarre */
+ td->bizarre = FALSE;
+
+ /* Reset the tile info */
+ td->tile_wid = td->font_wid;
+ td->tile_hgt = td->font_hgt;
+
+ /* Analyze the font */
+ term_getsize(td);
+
+ /* Resize the window */
+ term_window_resize(td);
+ }
+}
+
+
+
+/*
+ * Hack -- redraw a term_data
+ */
+static void term_data_redraw(term_data *td)
+{
+ /* Activate the term */
+ Term_activate(&td->t);
+
+ /* Redraw the contents */
+ Term_redraw();
+
+ /* Restore the term */
+ Term_activate(term_screen);
+}
+
+
+
+
+
+/*** Function hooks needed by "Term" ***/
+
+
+#if 0
+
+/*
+ * Initialize a new Term
+ */
+static void Term_init_win(term *t)
+{
+ /* XXX Unused */
+}
+
+
+/*
+ * Nuke an old Term
+ */
+static void Term_nuke_win(term *t)
+{
+ /* XXX Unused */
+}
+
+#endif
+
+
+/*
+ * Interact with the User
+ */
+static errr Term_user_win(int n)
+{
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * React to global changes
+ */
+static errr Term_xtra_win_react(void)
+{
+ int i;
+
+
+ /* Simple color */
+ if (colors16)
+ {
+ /* Save the default colors */
+ for (i = 0; i < 256; i++)
+ {
+ /* Simply accept the desired colors */
+ win_pal[i] = angband_color_table[i][0];
+ }
+ }
+
+ /* Complex color */
+ else
+ {
+ COLORREF code;
+
+ byte rv, gv, bv;
+
+ bool_ change = FALSE;
+
+ /* Save the default colors */
+ for (i = 0; i < 256; i++)
+ {
+ /* Extract desired values */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Extract a full color code */
+ code = PALETTERGB(rv, gv, bv);
+
+ /* Activate changes */
+ if (win_clr[i] != code)
+ {
+ /* Note the change */
+ change = TRUE;
+
+ /* Apply the desired color */
+ win_clr[i] = code;
+ }
+ }
+
+ /* Activate the palette if needed */
+ if (change) (void)new_palette();
+ }
+
+
+#ifdef USE_SOUND
+
+ /* Handle "arg_sound" */
+ if (use_sound != arg_sound)
+ {
+ /* Initialize (if needed) */
+ if (arg_sound && !init_sound())
+ {
+ /* Warning */
+ plog("Cannot initialize sound!");
+
+ /* Cannot enable */
+ arg_sound = FALSE;
+ }
+
+ /* Change setting */
+ use_sound = arg_sound;
+ }
+
+#endif
+
+
+#ifdef USE_GRAPHICS
+
+ /* Handle "arg_graphics" */
+ if (use_graphics != arg_graphics)
+ {
+ /* Initialize (if needed) */
+ if (arg_graphics && !init_graphics())
+ {
+ /* Warning */
+ plog("Cannot initialize graphics!");
+
+ /* Cannot enable */
+ arg_graphics = FALSE;
+ }
+
+ /* Change setting */
+ use_graphics = arg_graphics;
+
+ /* Reset visuals */
+ reset_visuals();
+ }
+
+#endif /* USE_GRAPHICS */
+
+
+ /* Clean up windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term *old = Term;
+
+ term_data *td = &data[i];
+
+ /* Update resized windows */
+ if ((td->cols != td->t.wid) || (td->rows != td->t.hgt))
+ {
+ /* Activate */
+ Term_activate(&td->t);
+
+ /* Hack -- Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Redraw the contents */
+ Term_redraw();
+
+ /* Restore */
+ Term_activate(old);
+ }
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Process at least one event
+ */
+static errr Term_xtra_win_event(int v)
+{
+ MSG msg;
+
+ /* Wait for an event */
+ if (v)
+ {
+ /* Block */
+ if (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ /* Check for an event */
+ else
+ {
+ /* Check */
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Process all pending events
+ */
+static errr Term_xtra_win_flush(void)
+{
+ MSG msg;
+
+ /* Process all pending events */
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Hack -- clear the screen
+ *
+ * Make this more efficient XXX XXX XXX
+ */
+static errr Term_xtra_win_clear(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ HDC hdc;
+ RECT rc;
+
+ /* Rectangle to erase */
+ rc.left = td->size_ow1;
+ rc.right = rc.left + td->cols * td->tile_wid;
+ rc.top = td->size_oh1;
+ rc.bottom = rc.top + td->rows * td->tile_hgt;
+
+ /* Erase it */
+ hdc = GetDC(td->w);
+ SetBkColor(hdc, RGB(0, 0, 0));
+ SelectObject(hdc, td->font_id);
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
+ ReleaseDC(td->w, hdc);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Hack -- make a noise
+ */
+static errr Term_xtra_win_noise(void)
+{
+ MessageBeep(MB_ICONASTERISK);
+ return (0);
+}
+
+
+/*
+ * Hack -- make a sound
+ */
+static errr Term_xtra_win_sound(int v)
+{
+ /* Sound disabled */
+ if (!use_sound) return (1);
+
+ /* Illegal sound */
+ if ((v < 0) || (v >= SOUND_MAX)) return (1);
+
+ /* Unknown sound */
+ if (!sound_file[v]) return (1);
+
+#ifdef USE_SOUND
+
+#ifdef WIN32
+
+ /* Play the sound, catch errors */
+ return (PlaySound(sound_file[v], 0, SND_FILENAME | SND_ASYNC));
+
+#else /* WIN32 */
+
+/* Play the sound, catch errors */
+ return (sndPlaySound(sound_file[v], SND_ASYNC));
+
+#endif /* WIN32 */
+
+#endif /* USE_SOUND */
+
+ /* Oops */
+ return (1);
+}
+
+
+/*
+ * Delay for "x" milliseconds
+ */
+static int Term_xtra_win_delay(int v)
+{
+
+#ifdef WIN32
+
+ /* Sleep */
+ Sleep(v);
+
+#else /* WIN32 */
+
+ DWORD t;
+ MSG msg;
+
+ /* Final count */
+ t = GetTickCount() + v;
+
+ /* Wait for it */
+ while (GetTickCount() < t)
+ {
+ /* Handle messages */
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+#endif /* WIN32 */
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Return the HWND of the main window
+ */
+HWND get_main_hwnd()
+{
+ return data[0].w;
+}
+
+/*
+ * Do a "special thing"
+ */
+static errr Term_xtra_win(int n, int v)
+{
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a bell sound */
+ case TERM_XTRA_NOISE:
+ {
+ return (Term_xtra_win_noise());
+ }
+
+ /* Make a special sound */
+ case TERM_XTRA_SOUND:
+ {
+ return (Term_xtra_win_sound(v));
+ }
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ {
+ return (Term_xtra_win_event(0));
+ }
+
+ /* Process an event */
+ case TERM_XTRA_EVENT:
+ {
+ return (Term_xtra_win_event(v));
+ }
+
+ /* Flush all events */
+ case TERM_XTRA_FLUSH:
+ {
+ return (Term_xtra_win_flush());
+ }
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ {
+ return (Term_xtra_win_clear());
+ }
+
+ /* React to global changes */
+ case TERM_XTRA_REACT:
+ {
+ return (Term_xtra_win_react());
+ }
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ {
+ return (Term_xtra_win_delay(v));
+ }
+
+ /* Get the current time in milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ DWORD t;
+
+ t = GetTickCount();
+ Term_xtra_long = t;
+ return 0;
+ }
+
+ /*
+ * Scans for subdirectories in a directory "scansubdir_dir"
+ * and place teh result in "scansubdir_result/scansubdir_max"
+ */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ BOOL ok;
+ HANDLE h;
+ WIN32_FIND_DATA fd;
+ for (h = FindFirstFile(format("%s\\*", scansubdir_dir), &fd), ok = 1;
+ h != INVALID_HANDLE_VALUE && ok;
+ ok = FindNextFile(h, &fd))
+ {
+ if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (strcmp(fd.cFileName, ".")) && (strcmp(fd.cFileName, "..")))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(fd.cFileName);
+ scansubdir_max++;
+ }
+ }
+
+ return 0;
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN:
+ SetWindowText(get_main_hwnd(), angband_term_name[0]); return (0);
+ }
+
+ /* Oops */
+ return 1;
+}
+
+
+
+/*
+ * Low level graphics (Assumes valid input).
+ *
+ * Draw a "cursor" at (x,y), using a "yellow box".
+ */
+static errr Term_curs_win(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ RECT rc;
+ HDC hdc;
+
+ /* Frame the grid */
+ rc.left = x * td->tile_wid + td->size_ow1;
+ rc.right = rc.left + td->tile_wid;
+ rc.top = y * td->tile_hgt + td->size_oh1;
+ rc.bottom = rc.top + td->tile_hgt;
+
+ if (use_bigtile && x + 1 < Term->wid && Term->old->a[y][x + 1] == 255)
+ rc.right += td->tile_wid;
+
+ /* Cursor is done as a yellow "box" */
+ hdc = GetDC(data[0].w);
+ FrameRect(hdc, &rc, hbrYellow);
+ ReleaseDC(data[0].w, hdc);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Low level graphics (Assumes valid input).
+ *
+ * Erase a "block" of "n" characters starting at (x,y).
+ */
+static errr Term_wipe_win(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ HDC hdc;
+ RECT rc;
+
+ /* Rectangle to erase in client coords */
+ rc.left = x * td->tile_wid + td->size_ow1;
+ rc.right = rc.left + n * td->tile_wid;
+ rc.top = y * td->tile_hgt + td->size_oh1;
+ rc.bottom = rc.top + td->tile_hgt;
+
+ hdc = GetDC(td->w);
+ SetBkColor(hdc, RGB(0, 0, 0));
+ SelectObject(hdc, td->font_id);
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
+ ReleaseDC(td->w, hdc);
+
+ /* Success */
+ return 0;
+}
+
+/*
+ * Low level graphics. Assumes valid input.
+ *
+ * Draw several ("n") chars, with an attr, at a given location.
+ *
+ * All "graphic" data is handled by "Term_pict_win()", below.
+ *
+ * One would think there is a more efficient method for telling a window
+ * what color it should be using to draw with, but perhaps simply changing
+ * it every time is not too inefficient. XXX XXX XXX
+ */
+static errr Term_text_win(int x, int y, int n, byte a, const char *s)
+{
+ term_data *td = (term_data*)(Term->data);
+ RECT rc;
+ HDC hdc;
+
+
+ /* Total rectangle */
+ rc.left = x * td->tile_wid + td->size_ow1;
+ rc.right = rc.left + n * td->tile_wid;
+ rc.top = y * td->tile_hgt + td->size_oh1;
+ rc.bottom = rc.top + td->tile_hgt;
+
+ /* Acquire DC */
+ hdc = GetDC(td->w);
+
+ /* Background color */
+ SetBkColor(hdc, RGB(0, 0, 0));
+
+ /* Foreground color */
+ if (colors16)
+ {
+ SetTextColor(hdc, PALETTEINDEX(win_pal[a]));
+ }
+ else if (paletted)
+ {
+ SetTextColor(hdc, win_clr[a&0x0F]);
+ }
+ else
+ {
+ SetTextColor(hdc, win_clr[a]);
+ }
+
+ /* Use the font */
+ SelectObject(hdc, td->font_id);
+
+ /* Bizarre size */
+ if (td->bizarre ||
+ (td->tile_hgt != td->font_hgt) ||
+ (td->tile_wid != td->font_wid))
+ {
+ int i;
+
+ /* Erase complete rectangle */
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
+
+ /* New rectangle */
+ rc.left += ((td->tile_wid - td->font_wid) / 2);
+ rc.right = rc.left + td->font_wid;
+ rc.top += ((td->tile_hgt - td->font_hgt) / 2);
+ rc.bottom = rc.top + td->font_hgt;
+
+ /* Dump each character */
+ for (i = 0; i < n; i++)
+ {
+ /* Dump the text */
+ ExtTextOut(hdc, rc.left, rc.top, 0, &rc,
+ s + i, 1, NULL);
+
+ /* Advance */
+ rc.left += td->tile_wid;
+ rc.right += td->tile_wid;
+ }
+ }
+
+ /* Normal size */
+ else
+ {
+ /* Dump the text */
+ ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE | ETO_CLIPPED, &rc,
+ s, n, NULL);
+ }
+
+ /* Release DC */
+ ReleaseDC(td->w, hdc);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Low level graphics. Assumes valid input.
+ *
+ * Draw an array of "special" attr/char pairs at the given location.
+ *
+ * We use the "Term_pict_win()" function for "graphic" data, which are
+ * encoded by setting the "high-bits" of both the "attr" and the "char"
+ * data. We use the "attr" to represent the "row" of the main bitmap,
+ * and the "char" to represent the "col" of the main bitmap. The use
+ * of this function is induced by the "higher_pict" flag.
+ *
+ * If "graphics" is not available, we simply "wipe" the given grids.
+ */
+static errr Term_pict_win(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+{
+ term_data *td = (term_data*)(Term->data);
+
+#ifdef USE_GRAPHICS
+
+ int i;
+ int x1, y1, w1, h1;
+ int x2, y2, w2, h2, tw2;
+
+ int x3, y3;
+
+ HDC hdcMask = NULL;
+
+ int x4, y4;
+
+ HDC hdc;
+ HDC hdcSrc;
+ HBITMAP hbmSrcOld;
+
+ /* Paranoia */
+ if (!use_graphics)
+ {
+ /* Erase the grids */
+ return (Term_wipe_win(x, y, n));
+ }
+
+ /* Size of bitmap cell */
+ w1 = infGraph.CellWidth;
+ h1 = infGraph.CellHeight;
+
+ /* Size of window cell */
+ w2 = td->tile_wid;
+ h2 = td->tile_hgt;
+ tw2 = w2;
+
+ /* big tile mode */
+ if (use_bigtile) tw2 *= 2;
+
+ /* Location of window cell */
+ x2 = x * w2 + td->size_ow1;
+ y2 = y * h2 + td->size_oh1;
+
+ /* Info */
+ hdc = GetDC(td->w);
+
+ /* More info */
+ hdcSrc = CreateCompatibleDC(hdc);
+ hbmSrcOld = SelectObject(hdcSrc, infGraph.hBitmap);
+
+ if (arg_graphics == 2)
+ {
+ hdcMask = CreateCompatibleDC(hdc);
+ SelectObject(hdcMask, infMask.hBitmap);
+ }
+
+ /* Draw attr/char pairs */
+ for (i = 0; i < n; i++, x2 += w2)
+ {
+ byte a = ap[i];
+ char c = cp[i];
+
+ /* Extract picture */
+ int row = (a & 0x7F);
+ int col = (c & 0x7F);
+
+ /* Location of bitmap cell */
+ x1 = col * w1;
+ y1 = row * h1;
+
+ if (arg_graphics == 2)
+ {
+ x3 = (tcp[i] & 0x7F) * w1;
+ y3 = (tap[i] & 0x7F) * h1;
+
+ /* Perfect size */
+ if ((w1 == tw2) && (h1 == h2))
+ {
+ /* Copy the terrain picture from the bitmap to the window */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x3, y3, SRCCOPY);
+
+ /* Mask out the tile */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcMask, x1, y1, SRCAND);
+
+ /* Draw the tile */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, SRCPAINT);
+
+ if (ecp[i] != 0 && eap[i] != 0)
+ {
+ x4 = (ecp[i] & 0x7F) * w1;
+ y4 = (eap[i] & 0x7F) * h1;
+
+ /* Mask out the tile */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcMask, x4, y4, SRCAND);
+
+ /* Draw the tile */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x4, y4, SRCPAINT);
+ }
+ }
+
+ /* Need to stretch */
+ else
+ {
+ /* Set the correct mode for stretching the tiles */
+ SetStretchBltMode(hdc, COLORONCOLOR);
+
+ /* Copy the terrain picture from the bitmap to the window */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x3, y3, w1, h1, SRCCOPY);
+
+ /* Only draw if terrain and overlay are different */
+ if ((x1 != x3) || (y1 != y3))
+ {
+ /* Mask out the tile */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcMask, x1, y1, w1, h1, SRCAND);
+
+ /* Draw the tile */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, w1, h1, SRCPAINT);
+ }
+
+ if (ecp[i] != 0 && eap[i] != 0)
+ {
+ x4 = (ecp[i] & 0x7F) * w1;
+ y4 = (eap[i] & 0x7F) * h1;
+
+ /* Mask out the tile */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcMask, x4, y4, w1, h1, SRCAND);
+
+ /* Draw the tile */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x4, y4, w1, h1, SRCPAINT);
+ }
+ }
+ }
+ else
+ {
+ /* Perfect size */
+ if ((w1 == tw2) && (h1 == h2))
+ {
+ /* Copy the picture from the bitmap to the window */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, SRCCOPY);
+ }
+
+ /* Need to stretch */
+ else
+ {
+ /* Set the correct mode for stretching the tiles */
+ SetStretchBltMode(hdc, COLORONCOLOR);
+
+ /* Copy the picture from the bitmap to the window */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, w1, h1, SRCCOPY);
+ }
+ }
+ }
+
+ /* Release */
+ SelectObject(hdcSrc, hbmSrcOld);
+ DeleteDC(hdcSrc);
+
+ if (arg_graphics == 2)
+ {
+ /* Release */
+ SelectObject(hdcMask, hbmSrcOld);
+ DeleteDC(hdcMask);
+ }
+
+ /* Release */
+ ReleaseDC(td->w, hdc);
+
+#else /* USE_GRAPHICS */
+
+ /* Just erase this grid */
+ return (Term_wipe_win(x, y, n));
+
+#endif /* USE_GRAPHICS */
+
+ /* Success */
+ return 0;
+}
+
+
+/*** Other routines ***/
+
+
+/*
+ * Create and initialize a "term_data" given a title
+ */
+static void term_data_link(term_data *td)
+{
+ term *t = &td->t;
+
+ /* Initialize the term */
+ term_init(t, td->cols, td->rows, td->keys);
+
+ /* Use a "software" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Use "Term_pict" for "graphic" data */
+ t->higher_pict = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+#if 0
+ /* Prepare the init/nuke hooks */
+ t->init_hook = Term_init_win;
+ t->nuke_hook = Term_nuke_win;
+#endif
+
+ /* Prepare the template hooks */
+ t->user_hook = Term_user_win;
+ t->xtra_hook = Term_xtra_win;
+ t->curs_hook = Term_curs_win;
+ t->wipe_hook = Term_wipe_win;
+ t->text_hook = Term_text_win;
+ t->pict_hook = Term_pict_win;
+
+ /* Remember where we came from */
+ t->data = (vptr)(td);
+}
+
+
+/*
+ * Create the windows
+ *
+ * First, instantiate the "default" values, then read the "ini_file"
+ * to over-ride selected values, then create the windows, and fonts.
+ *
+ * Must use SW_SHOW not SW_SHOWNA, since on 256 color display
+ * must make active to realize the palette. XXX XXX XXX
+ */
+static void init_windows(void)
+{
+ int i;
+
+ term_data *td;
+
+ char buf[1024];
+
+
+ /* Main window */
+ td = &data[0];
+ WIPE(td, term_data);
+ td->s = angband_term_name[0];
+ td->keys = 1024;
+ td->rows = 24;
+ td->cols = 80;
+ td->visible = TRUE;
+ td->size_ow1 = 2;
+ td->size_ow2 = 2;
+ td->size_oh1 = 2;
+ td->size_oh2 = 2;
+ td->pos_x = 7 * 30;
+ td->pos_y = 7 * 20;
+
+ /* Sub windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ td = &data[i];
+ WIPE(td, term_data);
+ td->s = angband_term_name[i];
+ td->keys = 16;
+ td->rows = 24;
+ td->cols = 80;
+ td->visible = FALSE;
+ td->size_ow1 = 1;
+ td->size_ow2 = 1;
+ td->size_oh1 = 1;
+ td->size_oh2 = 1;
+ td->pos_x = (7 - i) * 30;
+ td->pos_y = (7 - i) * 20;
+ }
+
+
+ /* Load prefs */
+ load_prefs();
+
+
+ /* Main window (need these before term_getsize gets called) */
+ td = &data[0];
+ td->dwStyle = (WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU |
+ WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION |
+ WS_VISIBLE);
+ td->dwExStyle = 0;
+ td->visible = TRUE;
+
+ /* Sub windows (need these before term_getsize gets called) */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ td = &data[i];
+ td->dwStyle = (WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU);
+ td->dwExStyle = (WS_EX_TOOLWINDOW);
+ }
+
+
+ /* All windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ td = &data[i];
+
+ /* Access the standard font file */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_FONT, td->font_want);
+
+ /* Activate the chosen font */
+ if (term_force_font(td, buf))
+ {
+ /* Access the standard font file */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_FONT, "8X13.FON");
+
+ /* Force the use of that font */
+ (void)term_force_font(td, buf);
+
+ /* Oops */
+ td->tile_wid = 8;
+ td->tile_hgt = 13;
+
+ /* Assume not bizarre */
+ td->bizarre = FALSE;
+ }
+
+ /* Analyze the font */
+ term_getsize(td);
+
+ /* Resize the window */
+ term_window_resize(td);
+ }
+
+
+ /* Sub windows (reverse order) */
+ for (i = MAX_TERM_DATA - 1; i >= 1; --i)
+ {
+ td = &data[i];
+
+ my_td = td;
+ td->w = CreateWindowEx(td->dwExStyle, AngList,
+ td->s, td->dwStyle,
+ td->pos_x, td->pos_y,
+ td->size_wid, td->size_hgt,
+ HWND_DESKTOP, NULL, hInstance, NULL);
+ my_td = NULL;
+ if (!td->w) quit("Failed to create sub-window");
+
+ if (td->visible)
+ {
+ td->size_hack = TRUE;
+ ShowWindow(td->w, SW_SHOW);
+ td->size_hack = FALSE;
+ }
+
+ term_data_link(td);
+ angband_term[i] = &td->t;
+
+ if (td->visible)
+ {
+ /* Activate the window */
+ SetActiveWindow(td->w);
+
+ /* Bring window to top */
+ SetWindowPos(td->w, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+ }
+
+
+ /* Main window */
+ td = &data[0];
+
+ /* Main window */
+ my_td = td;
+ td->w = CreateWindowEx(td->dwExStyle, AppName,
+ td->s, td->dwStyle,
+ td->pos_x, td->pos_y,
+ td->size_wid, td->size_hgt,
+ HWND_DESKTOP, NULL, hInstance, NULL);
+ my_td = NULL;
+ if (!td->w) quit("Failed to create Angband window");
+
+ term_data_link(td);
+ angband_term[0] = &td->t;
+
+ /* Activate the main window */
+ SetActiveWindow(td->w);
+
+ /* Bring main window back to top */
+ SetWindowPos(td->w, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+
+
+ /* New palette XXX XXX XXX */
+ (void)new_palette();
+
+
+ /* Create a "brush" for drawing the "cursor" */
+ hbrYellow = CreateSolidBrush(win_clr[TERM_YELLOW]);
+
+
+ /* Process pending messages */
+ (void)Term_xtra_win_flush();
+}
+
+
+
+/*
+ * Prepare the menus
+ */
+static void setup_menus(void)
+{
+ int i;
+
+ HMENU hm = GetMenu(data[0].w);
+
+
+ /* Menu "File", Disable all */
+ EnableMenuItem(hm, IDM_FILE_NEW,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_FILE_OPEN,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_FILE_SAVE,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+#ifdef ALLOW_QUITTING
+ EnableMenuItem(hm, IDM_FILE_ABORT,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+#else
+ EnableMenuItem(hm, IDM_FILE_SCORE,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+#endif /* ALLOW_QUITTING */
+ EnableMenuItem(hm, IDM_FILE_EXIT,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ /* No character available */
+ if (!character_generated)
+ {
+ /* Menu "File", Item "New" */
+ EnableMenuItem(hm, IDM_FILE_NEW,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ /* Menu "File", Item "Open" */
+ EnableMenuItem(hm, IDM_FILE_OPEN,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+ /* A character available */
+ if (character_generated)
+ {
+ /* Menu "File", Item "Save" */
+ EnableMenuItem(hm, IDM_FILE_SAVE,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+#ifdef ALLOW_QUITTING
+
+ /* Menu "File", Item "Abort" */
+ EnableMenuItem(hm, IDM_FILE_ABORT,
+ MF_BYCOMMAND | MF_ENABLED);
+
+#else
+
+ /* Menu "File", Item "Score" */
+ if (initialized && character_generated && !character_icky)
+ {
+ EnableMenuItem(hm, IDM_FILE_SCORE,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+#endif
+
+ /* Menu "File", Item "Exit" */
+ EnableMenuItem(hm, IDM_FILE_EXIT,
+ MF_BYCOMMAND | MF_ENABLED);
+
+
+ /* Menu "Window::Visibility" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_VIS_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ CheckMenuItem(hm, IDM_WINDOW_VIS_0 + i,
+ (data[i].visible ? MF_CHECKED : MF_UNCHECKED));
+
+ EnableMenuItem(hm, IDM_WINDOW_VIS_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+ /* Menu "Window::Font" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_FONT_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_FONT_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+ }
+
+ /* Menu "Window::Bizarre Display" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_BIZ_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ CheckMenuItem(hm, IDM_WINDOW_BIZ_0 + i,
+ (data[i].bizarre ? MF_CHECKED : MF_UNCHECKED));
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_BIZ_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Window::Increase Tile Width" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_I_WID_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_I_WID_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Window::Decrease Tile Width" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_D_WID_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_D_WID_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Window::Increase Tile Height" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_I_HGT_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_I_HGT_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Window::Decrease Tile Height" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_D_HGT_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_D_HGT_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Options", disable all */
+ EnableMenuItem(hm, IDM_OPTIONS_OLD_GRAPHICS,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_NEW_GRAPHICS,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_ASCII_GRAPHICS,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_BIGTILE,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_SOUND,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_UNUSED,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_SAVER,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ /* Menu "Options", update all */
+ CheckMenuItem(hm, IDM_OPTIONS_OLD_GRAPHICS,
+ (arg_graphics == 1 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_NEW_GRAPHICS,
+ (arg_graphics == 2 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_ASCII_GRAPHICS,
+ (arg_graphics == 0 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_BIGTILE,
+ (arg_bigtile ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_SOUND,
+ (arg_sound ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_UNUSED,
+ (0 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_SAVER,
+ (hwndSaver ? MF_CHECKED : MF_UNCHECKED));
+
+#ifdef USE_GRAPHICS
+ /* Menu "Options", Item "Graphics" */
+ EnableMenuItem(hm, IDM_OPTIONS_OLD_GRAPHICS, MF_ENABLED);
+ /* Menu "Options", Item "Graphics" */
+ EnableMenuItem(hm, IDM_OPTIONS_NEW_GRAPHICS, MF_ENABLED);
+ /* Menu "Options", Item "Graphics" */
+ EnableMenuItem(hm, IDM_OPTIONS_ASCII_GRAPHICS, MF_ENABLED);
+ /* Menu "Options", Item "Graphics" */
+ EnableMenuItem(hm, IDM_OPTIONS_BIGTILE, MF_ENABLED);
+#endif
+
+#ifdef USE_SOUND
+ /* Menu "Options", Item "Sound" */
+ EnableMenuItem(hm, IDM_OPTIONS_SOUND, MF_ENABLED);
+#endif
+
+#ifdef USE_SAVER
+ /* Menu "Options", Item "ScreenSaver" */
+ EnableMenuItem(hm, IDM_OPTIONS_SAVER,
+ MF_BYCOMMAND | MF_ENABLED);
+#endif
+
+}
+
+
+/*
+ * Check for double clicked (or dragged) savefile
+ *
+ * Apparently, Windows copies the entire filename into the first
+ * piece of the "command line string". Perhaps we should extract
+ * the "basename" of that filename and append it to the "save" dir.
+ */
+static void check_for_save_file(LPSTR cmd_line)
+{
+ char *s, *p;
+
+ /* First arg */
+ s = cmd_line;
+
+ /* Second arg */
+ p = strchr(s, ' ');
+
+ /* Tokenize, advance */
+ if (p) *p++ = '\0';
+
+ /* No args */
+ if (!*s) return;
+
+ /* Extract filename */
+ strcat(savefile, s);
+
+ /* Validate the file */
+ validate_file(savefile);
+
+ /* Game in progress */
+ game_in_progress = TRUE;
+
+ /* Play game */
+ play_game(FALSE);
+}
+
+
+/*
+ * Process a menu command
+ */
+static void process_menus(WORD wCmd)
+{
+ int i;
+
+ term_data *td;
+
+ OPENFILENAME ofn;
+
+ /* Analyze */
+ switch (wCmd)
+ {
+ /* New game */
+ case IDM_FILE_NEW:
+ {
+ if (!initialized)
+ {
+ plog("You cannot do that yet...");
+ }
+ else if (game_in_progress)
+ {
+ plog("You can't start a new game while you're still playing!");
+ }
+ else
+ {
+ game_in_progress = TRUE;
+ Term_flush();
+ play_game(TRUE);
+ quit(NULL);
+ }
+ break;
+ }
+
+ /* Open game */
+ case IDM_FILE_OPEN:
+ {
+ if (!initialized)
+ {
+ plog("You cannot do that yet...");
+ }
+ else if (game_in_progress)
+ {
+ plog("You can't open a new game while you're still playing!");
+ }
+ else
+ {
+ memset(&ofn, 0, sizeof(ofn));
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500)
+ ofn.lStructSize = sizeof(OPENFILENAME) - (sizeof(void*) + 2 * sizeof(DWORD));
+#else // old headers
+ofn.lStructSize = sizeof(OPENFILENAME);
+#endif
+ ofn.hwndOwner = data[0].w;
+ ofn.lpstrFilter = "Save Files (*.)\0*\0";
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = savefile;
+ ofn.nMaxFile = 1024;
+ ofn.lpstrInitialDir = ANGBAND_DIR_SAVE;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
+
+ if (GetOpenFileName(&ofn))
+ {
+ /* Load 'savefile' */
+ validate_file(savefile);
+ game_in_progress = TRUE;
+ Term_flush();
+ play_game(FALSE);
+ quit(NULL);
+ }
+ }
+ break;
+ }
+
+ /* Save game */
+ case IDM_FILE_SAVE:
+ {
+ if (game_in_progress && character_generated)
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+ do_cmd_save_game();
+ }
+ else
+ {
+ plog("You may not do that right now.");
+ }
+ break;
+ }
+
+ /* Exit */
+ case IDM_FILE_EXIT:
+ {
+ if (game_in_progress && character_generated)
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+ do_cmd_save_game();
+ }
+ quit(NULL);
+ break;
+ }
+
+#ifdef ALLOW_QUITTING
+
+ /* Abort */
+ case IDM_FILE_ABORT:
+ {
+ if (game_in_progress && character_generated)
+ {
+ /* XXX XXX XXX */
+ if (MessageBox(data[0].w,
+ "Your character will be not saved!", "Warning",
+ MB_ICONEXCLAMATION | MB_OKCANCEL) == IDCANCEL)
+ {
+ break;
+ }
+ }
+ quit(NULL);
+ break;
+ }
+
+#else
+
+ /* Score */
+ case IDM_FILE_SCORE:
+ {
+ char buf[1024];
+
+ /* Paranoia */
+ if (!initialized || character_icky ||
+ !game_in_progress || !character_generated)
+ {
+ /* Can't happen but just in case */
+ plog("You may not do that right now.");
+
+ break;
+ }
+
+ /* Build the pathname of the score file */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Hack - open the score file for reading */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ /* Paranoia - No score file */
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file is not available.");
+
+ break;
+ }
+
+ /* Mega-Hack - prevent various functions XXX XXX XXX */
+ initialized = FALSE;
+
+ /* Save screen */
+ screen_save();
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Prepare scores */
+ if (game_in_progress && character_generated)
+ {
+ predict_score();
+ }
+
+#if 0 /* I don't like this - pelpel */
+
+ /* Mega-Hack - No current player XXX XXX XXX XXX */
+ else
+ {
+ display_scores_aux(0, MAX_HISCORES, -1, NULL);
+ }
+
+#endif
+
+ /* Close the high score file */
+ (void)fd_close(highscore_fd);
+
+ /* Forget the fd */
+ highscore_fd = -1;
+
+ /* Restore screen */
+ screen_load();
+
+ /* Hack - Flush it */
+ Term_fresh();
+
+ /* Mega-Hack - We are ready again */
+ initialized = TRUE;
+
+ /* Done */
+ break;
+ }
+
+#endif
+
+ case IDM_WINDOW_VIS_0:
+ {
+ plog("You are not allowed to do that!");
+
+ break;
+ }
+
+ /* Window visibility */
+ case IDM_WINDOW_VIS_1:
+ case IDM_WINDOW_VIS_2:
+ case IDM_WINDOW_VIS_3:
+ case IDM_WINDOW_VIS_4:
+ case IDM_WINDOW_VIS_5:
+ case IDM_WINDOW_VIS_6:
+ case IDM_WINDOW_VIS_7:
+ {
+ i = wCmd - IDM_WINDOW_VIS_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ if (!td->visible)
+ {
+ td->visible = TRUE;
+ ShowWindow(td->w, SW_SHOW);
+ term_data_redraw(td);
+ }
+ else
+ {
+ td->visible = FALSE;
+ ShowWindow(td->w, SW_HIDE);
+ }
+
+ break;
+ }
+
+ /* Window fonts */
+ case IDM_WINDOW_FONT_0:
+ case IDM_WINDOW_FONT_1:
+ case IDM_WINDOW_FONT_2:
+ case IDM_WINDOW_FONT_3:
+ case IDM_WINDOW_FONT_4:
+ case IDM_WINDOW_FONT_5:
+ case IDM_WINDOW_FONT_6:
+ case IDM_WINDOW_FONT_7:
+ {
+ i = wCmd - IDM_WINDOW_FONT_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ term_change_font(td);
+
+ break;
+ }
+
+ /* Bizarre Display */
+ case IDM_WINDOW_BIZ_0:
+ case IDM_WINDOW_BIZ_1:
+ case IDM_WINDOW_BIZ_2:
+ case IDM_WINDOW_BIZ_3:
+ case IDM_WINDOW_BIZ_4:
+ case IDM_WINDOW_BIZ_5:
+ case IDM_WINDOW_BIZ_6:
+ case IDM_WINDOW_BIZ_7:
+ {
+ i = wCmd - IDM_WINDOW_BIZ_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->bizarre = !td->bizarre;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ /* Increase Tile Width */
+ case IDM_WINDOW_I_WID_0:
+ case IDM_WINDOW_I_WID_1:
+ case IDM_WINDOW_I_WID_2:
+ case IDM_WINDOW_I_WID_3:
+ case IDM_WINDOW_I_WID_4:
+ case IDM_WINDOW_I_WID_5:
+ case IDM_WINDOW_I_WID_6:
+ case IDM_WINDOW_I_WID_7:
+ {
+ i = wCmd - IDM_WINDOW_I_WID_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->tile_wid += 1;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ /* Decrease Tile Height */
+ case IDM_WINDOW_D_WID_0:
+ case IDM_WINDOW_D_WID_1:
+ case IDM_WINDOW_D_WID_2:
+ case IDM_WINDOW_D_WID_3:
+ case IDM_WINDOW_D_WID_4:
+ case IDM_WINDOW_D_WID_5:
+ case IDM_WINDOW_D_WID_6:
+ case IDM_WINDOW_D_WID_7:
+ {
+ i = wCmd - IDM_WINDOW_D_WID_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->tile_wid -= 1;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ /* Increase Tile Height */
+ case IDM_WINDOW_I_HGT_0:
+ case IDM_WINDOW_I_HGT_1:
+ case IDM_WINDOW_I_HGT_2:
+ case IDM_WINDOW_I_HGT_3:
+ case IDM_WINDOW_I_HGT_4:
+ case IDM_WINDOW_I_HGT_5:
+ case IDM_WINDOW_I_HGT_6:
+ case IDM_WINDOW_I_HGT_7:
+ {
+ i = wCmd - IDM_WINDOW_I_HGT_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->tile_hgt += 1;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ /* Decrease Tile Height */
+ case IDM_WINDOW_D_HGT_0:
+ case IDM_WINDOW_D_HGT_1:
+ case IDM_WINDOW_D_HGT_2:
+ case IDM_WINDOW_D_HGT_3:
+ case IDM_WINDOW_D_HGT_4:
+ case IDM_WINDOW_D_HGT_5:
+ case IDM_WINDOW_D_HGT_6:
+ case IDM_WINDOW_D_HGT_7:
+ {
+ i = wCmd - IDM_WINDOW_D_HGT_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->tile_hgt -= 1;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ case IDM_OPTIONS_OLD_GRAPHICS:
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Set "arg_graphics" */
+ arg_graphics = 1;
+
+ /* React to changes */
+ Term_xtra_win_react();
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+
+ case IDM_OPTIONS_NEW_GRAPHICS:
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Set "arg_graphics" */
+ arg_graphics = 2;
+
+ /* React to changes */
+ Term_xtra_win_react();
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+ case IDM_OPTIONS_ASCII_GRAPHICS:
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Set "ASCII Graphics" */
+ arg_graphics = 0;
+ /* React to Changes */
+ Term_xtra_win_react();
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+
+ case IDM_OPTIONS_BIGTILE:
+ {
+ term_data *td = &data[0];
+
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Toggle "arg_sound" */
+ arg_bigtile = !arg_bigtile;
+
+ /* Activate */
+ Term_activate(&td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Redraw later */
+ InvalidateRect(td->w, NULL, TRUE);
+
+ break;
+ }
+
+ case IDM_OPTIONS_SOUND:
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Toggle "arg_sound" */
+ arg_sound = !arg_sound;
+
+ /* React to changes */
+ Term_xtra_win_react();
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+
+ case IDM_OPTIONS_UNUSED:
+ {
+ /* Unused for now XXX XXX XXX */
+
+ break;
+ }
+
+#ifdef USE_SAVER
+
+ case IDM_OPTIONS_SAVER:
+ {
+ if (hwndSaver)
+ {
+ DestroyWindow(hwndSaver);
+ hwndSaver = NULL;
+ }
+ else
+ {
+ /* Create a screen scaver window */
+ hwndSaver = CreateWindowEx(WS_EX_TOPMOST, "WindowsScreenSaverClass",
+ "Angband Screensaver",
+ WS_POPUP | WS_MAXIMIZE | WS_VISIBLE,
+ 0, 0, GetSystemMetrics(SM_CXSCREEN),
+ GetSystemMetrics(SM_CYSCREEN),
+ NULL, NULL, hInstance, NULL);
+
+ if (hwndSaver)
+ {
+ /* Push the window to the bottom XXX XXX XXX */
+ SetWindowPos(hwndSaver, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+ else
+ {
+ plog("Failed to create saver window");
+ }
+ }
+ break;
+ }
+
+#endif
+
+ case IDM_HELP_GENERAL:
+ {
+ char buf[1024];
+ char tmp[1024];
+ path_build(tmp, 1024, ANGBAND_DIR_XTRA_HELP, "angband.hlp");
+ if (check_file(tmp))
+ {
+ sprintf(buf, "winhelp.exe %s", tmp);
+ WinExec(buf, SW_NORMAL);
+ }
+ else
+ {
+ plog_fmt("Cannot find help file: %s", tmp);
+ plog("Use the online help files instead.");
+ }
+ break;
+ }
+
+ case IDM_HELP_SPOILERS:
+ {
+ char buf[1024];
+ char tmp[1024];
+ path_build(tmp, 1024, ANGBAND_DIR_XTRA_HELP, "spoilers.hlp");
+ if (check_file(tmp))
+ {
+ sprintf(buf, "winhelp.exe %s", tmp);
+ WinExec(buf, SW_NORMAL);
+ }
+ else
+ {
+ plog_fmt("Cannot find help file: %s", tmp);
+ plog("Use the online help files instead.");
+ }
+ break;
+ }
+ }
+}
+
+
+
+#ifdef __MWERKS__
+LRESULT FAR PASCAL AngbandWndProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+LRESULT FAR PASCAL AngbandWndProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#else /* __MWERKS__ */
+LRESULT FAR PASCAL AngbandWndProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#endif /* __MWERKS__ */
+{
+ PAINTSTRUCT ps;
+ HDC hdc;
+ term_data *td;
+ MINMAXINFO FAR *lpmmi;
+ RECT rc;
+ int i;
+
+
+ /* Acquire proper "term_data" info */
+ td = (term_data *)GetWindowLong(hWnd, 0);
+
+ /* Handle message */
+ switch (uMsg)
+ {
+ /* XXX XXX XXX */
+ case WM_NCCREATE:
+ {
+ SetWindowLong(hWnd, 0, (LONG)(my_td));
+ break;
+ }
+
+ /* XXX XXX XXX */
+ case WM_CREATE:
+ {
+ return 0;
+ }
+
+ case WM_GETMINMAXINFO:
+ {
+ lpmmi = (MINMAXINFO FAR *)lParam;
+
+ /* this message was sent before WM_NCCREATE */
+ if (!td) return 1;
+
+ /* Minimum window size is 8x2 */
+ rc.left = rc.top = 0;
+ rc.right = rc.left + 8 * td->tile_wid + td->size_ow1 + td->size_ow2;
+ rc.bottom = rc.top + 2 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Save minimum size */
+ lpmmi->ptMinTrackSize.x = rc.right - rc.left;
+ lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;
+
+ /* Maximum window size */
+ rc.left = rc.top = 0;
+ rc.right = rc.left + 255 * td->tile_wid + td->size_ow1 + td->size_ow2;
+ rc.bottom = rc.top + 255 * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ /* Paranoia */
+ rc.right += (td->tile_wid - 1);
+ rc.bottom += (td->tile_hgt - 1);
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Save maximum size */
+ lpmmi->ptMaxSize.x = rc.right - rc.left;
+ lpmmi->ptMaxSize.y = rc.bottom - rc.top;
+
+ /* Save maximum size */
+ lpmmi->ptMaxTrackSize.x = rc.right - rc.left;
+ lpmmi->ptMaxTrackSize.y = rc.bottom - rc.top;
+
+ return 0;
+ }
+
+ case WM_PAINT:
+ {
+ BeginPaint(hWnd, &ps);
+ if (td) term_data_redraw(td);
+ EndPaint(hWnd, &ps);
+ ValidateRect(hWnd, NULL);
+ return 0;
+ }
+
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ {
+ /* Unused */
+ /* BYTE KeyState = 0x00; */
+
+ bool_ mc = FALSE;
+ bool_ ms = FALSE;
+ bool_ ma = FALSE;
+
+ /* Extract the modifiers */
+ if (GetKeyState(VK_CONTROL) & 0x8000) mc = TRUE;
+ if (GetKeyState(VK_SHIFT) & 0x8000) ms = TRUE;
+ if (GetKeyState(VK_MENU) & 0x8000) ma = TRUE;
+
+ /* Handle "special" keys */
+ if (special_key[(byte)(wParam)] || (ma && !ignore_key[(byte)(wParam)]) )
+ {
+ /* Begin the macro trigger */
+ Term_keypress(31);
+
+ /* Send the modifiers */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (ma) Term_keypress('A');
+
+ /* Extract "scan code" */
+ i = LOBYTE(HIWORD(lParam));
+
+ /* Introduce the scan code */
+ Term_keypress('x');
+
+ /* Encode the hexidecimal scan code */
+ Term_keypress(hexsym[i / 16]);
+ Term_keypress(hexsym[i % 16]);
+
+ /* End the macro trigger */
+ Term_keypress(13);
+
+ return 0;
+ }
+
+ break;
+ }
+
+ case WM_CHAR:
+ {
+ Term_keypress(wParam);
+ return 0;
+ }
+
+ case WM_INITMENU:
+ {
+ setup_menus();
+ return 0;
+ }
+
+ case WM_CLOSE:
+ {
+ if (game_in_progress && character_generated)
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+ do_cmd_save_game();
+ }
+ quit(NULL);
+ return 0;
+ }
+
+ case WM_QUIT:
+ {
+ quit(NULL);
+ return 0;
+ }
+
+ case WM_COMMAND:
+ {
+ process_menus(LOWORD(wParam));
+ return 0;
+ }
+
+ case WM_SIZE:
+ {
+ /* this message was sent before WM_NCCREATE */
+ if (!td) return 1;
+
+ /* it was sent from inside CreateWindowEx */
+ if (!td->w) return 1;
+
+ /* was sent from WM_SIZE */
+ if (td->size_hack) return 1;
+
+ switch (wParam)
+ {
+ case SIZE_MINIMIZED:
+ {
+ /* Hide sub-windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ if (data[i].visible) ShowWindow(data[i].w, SW_HIDE);
+ }
+ return 0;
+ }
+
+ case SIZE_MAXIMIZED:
+ {
+ /* fall through XXX XXX XXX */
+ }
+
+ case SIZE_RESTORED:
+ {
+ uint cols = (LOWORD(lParam) - td->size_ow1 - td->size_ow2) / td->tile_wid;
+ uint rows = (HIWORD(lParam) - td->size_oh1 - td->size_oh2) / td->tile_hgt;
+
+ /* New size */
+ if ((td->cols != cols) || (td->rows != rows))
+ {
+ /* save the new size */
+ td->cols = cols;
+ td->rows = rows;
+
+ /* Activate */
+ Term_activate(&td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Redraw later */
+ InvalidateRect(td->w, NULL, TRUE);
+ }
+
+ td->size_hack = TRUE;
+
+ /* Restore sub-windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ if (data[i].visible) ShowWindow(data[i].w, SW_SHOWNOACTIVATE);
+ }
+
+ td->size_hack = FALSE;
+
+ return 0;
+ }
+ }
+ break;
+ }
+
+ case WM_PALETTECHANGED:
+ {
+ /* Ignore if palette change caused by itself */
+ if ((HWND)wParam == hWnd) return 0;
+
+ /* Fall through... */
+ }
+
+ case WM_QUERYNEWPALETTE:
+ {
+ if (!paletted) return 0;
+
+ hdc = GetDC(hWnd);
+
+ SelectPalette(hdc, hPal, FALSE);
+
+ i = RealizePalette(hdc);
+
+ /* if any palette entries changed, repaint the window. */
+ if (i) InvalidateRect(hWnd, NULL, TRUE);
+
+ ReleaseDC(hWnd, hdc);
+
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ {
+ if (wParam && !HIWORD(lParam))
+ {
+ /* Do something to sub-windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ SetWindowPos(data[i].w, hWnd, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ }
+
+ /* Focus on main window */
+ SetFocus(hWnd);
+
+ return 0;
+ }
+
+ break;
+ }
+ }
+
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+#ifdef __MWERKS__
+LRESULT FAR PASCAL AngbandListProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+LRESULT FAR PASCAL AngbandListProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#else /* __MWERKS__ */
+LRESULT FAR PASCAL AngbandListProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#endif /* __MWERKS__ */
+{
+ term_data *td;
+ MINMAXINFO FAR *lpmmi;
+ RECT rc;
+ PAINTSTRUCT ps;
+ HDC hdc;
+ int i;
+
+
+ /* Acquire proper "term_data" info */
+ td = (term_data *)GetWindowLong(hWnd, 0);
+
+ /* Process message */
+ switch (uMsg)
+ {
+ /* XXX XXX XXX */
+ case WM_NCCREATE:
+ {
+ SetWindowLong(hWnd, 0, (LONG)(my_td));
+ break;
+ }
+
+ /* XXX XXX XXX */
+ case WM_CREATE:
+ {
+ return 0;
+ }
+
+ case WM_GETMINMAXINFO:
+ {
+ /* this message was sent before WM_NCCREATE */
+ if (!td) return 1;
+
+ lpmmi = (MINMAXINFO FAR *)lParam;
+
+ /* Minimum size */
+ rc.left = rc.top = 0;
+ rc.right = rc.left + 8 * td->tile_wid + td->size_ow1 + td->size_ow2;
+ rc.bottom = rc.top + 2 * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Save the minimum size */
+ lpmmi->ptMinTrackSize.x = rc.right - rc.left;
+ lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;
+
+ /* Maximum window size */
+ rc.left = rc.top = 0;
+ rc.right = rc.left + 80 * td->tile_wid + td->size_ow1 + td->size_ow2;
+ rc.bottom = rc.top + 24 * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ /* Paranoia */
+ rc.right += (td->tile_wid - 1);
+ rc.bottom += (td->tile_hgt - 1);
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Save maximum size */
+ lpmmi->ptMaxSize.x = rc.right - rc.left;
+ lpmmi->ptMaxSize.y = rc.bottom - rc.top;
+
+ /* Save the maximum size */
+ lpmmi->ptMaxTrackSize.x = rc.right - rc.left;
+ lpmmi->ptMaxTrackSize.y = rc.bottom - rc.top;
+
+ return 0;
+ }
+
+ case WM_SIZE:
+ {
+ /* this message was sent before WM_NCCREATE */
+ if (!td) return 1;
+
+ /* it was sent from inside CreateWindowEx */
+ if (!td->w) return 1;
+
+ /* was sent from inside WM_SIZE */
+ if (td->size_hack) return 1;
+
+ td->size_hack = TRUE;
+
+ td->cols = (LOWORD(lParam) - td->size_ow1 - td->size_ow2) / td->tile_wid;
+ td->rows = (HIWORD(lParam) - td->size_oh1 - td->size_oh2) / td->tile_hgt;
+
+ term_getsize(td);
+
+ MoveWindow(hWnd, td->pos_x, td->pos_y, td->size_wid, td->size_hgt, TRUE);
+
+ td->size_hack = FALSE;
+
+ return 0;
+ }
+
+ case WM_PAINT:
+ {
+ BeginPaint(hWnd, &ps);
+ if (td) term_data_redraw(td);
+ EndPaint(hWnd, &ps);
+ return 0;
+ }
+
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ {
+ /* Unused */
+ /* BYTE KeyState = 0x00; */
+
+ bool_ mc = FALSE;
+ bool_ ms = FALSE;
+ bool_ ma = FALSE;
+
+ /* Extract the modifiers */
+ if (GetKeyState(VK_CONTROL) & 0x8000) mc = TRUE;
+ if (GetKeyState(VK_SHIFT) & 0x8000) ms = TRUE;
+ if (GetKeyState(VK_MENU) & 0x8000) ma = TRUE;
+
+ /* Handle "special" keys */
+ if (special_key[(byte)(wParam)] || (ma && !ignore_key[(byte)(wParam)]) )
+ {
+ /* Begin the macro trigger */
+ Term_keypress(31);
+
+ /* Send the modifiers */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (ma) Term_keypress('A');
+
+ /* Extract "scan code" */
+ i = LOBYTE(HIWORD(lParam));
+
+ /* Introduce the scan code */
+ Term_keypress('x');
+
+ /* Encode the hexidecimal scan code */
+ Term_keypress(hexsym[i / 16]);
+ Term_keypress(hexsym[i % 16]);
+
+ /* End the macro trigger */
+ Term_keypress(13);
+
+ return 0;
+ }
+
+ break;
+ }
+
+ case WM_CHAR:
+ {
+ Term_keypress(wParam);
+ return 0;
+ }
+
+ case WM_PALETTECHANGED:
+ {
+ /* ignore if palette change caused by itself */
+ if ((HWND)wParam == hWnd) return FALSE;
+ /* otherwise, fall through!!! */
+ }
+
+ case WM_QUERYNEWPALETTE:
+ {
+ if (!paletted) return 0;
+ hdc = GetDC(hWnd);
+ SelectPalette(hdc, hPal, FALSE);
+ i = RealizePalette(hdc);
+ /* if any palette entries changed, repaint the window. */
+ if (i) InvalidateRect(hWnd, NULL, TRUE);
+ ReleaseDC(hWnd, hdc);
+ return 0;
+ }
+
+ case WM_NCLBUTTONDOWN:
+ {
+
+#ifdef HTCLOSE
+ if (wParam == HTCLOSE) wParam = HTSYSMENU;
+#endif /* HTCLOSE */
+
+ if (wParam == HTSYSMENU)
+ {
+ if (td->visible)
+ {
+ td->visible = FALSE;
+ ShowWindow(td->w, SW_HIDE);
+ }
+
+ return 0;
+ }
+
+ break;
+ }
+ }
+
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+#ifdef USE_SAVER
+
+#define MOUSE_SENS 40
+
+#ifdef __MWERKS__
+LRESULT FAR PASCAL AngbandSaverProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+LRESULT FAR PASCAL AngbandSaverProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#else /* __MWERKS__ */
+LRESULT FAR PASCAL AngbandSaverProc(HWND hWnd, UINT uMsg,
+WPARAM wParam, LPARAM lParam)
+#endif /* __MWERKS__ */
+{
+ static int iMouse = 0;
+ static WORD xMouse = 0;
+ static WORD yMouse = 0;
+
+ int dx, dy;
+
+
+ /* Process */
+ switch (uMsg)
+ {
+ /* XXX XXX XXX */
+ case WM_NCCREATE:
+ {
+ break;
+ }
+
+ case WM_SETCURSOR:
+ {
+ SetCursor(NULL);
+ return 0;
+ }
+
+#if 0
+ case WM_ACTIVATE:
+ {
+ if (LOWORD(wParam) == WA_INACTIVE) break;
+
+ /* else fall through */
+ }
+#endif
+
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_KEYDOWN:
+ {
+ SendMessage(hWnd, WM_CLOSE, 0, 0);
+ return 0;
+ }
+
+ case WM_MOUSEMOVE:
+ {
+ if (iMouse)
+ {
+ dx = LOWORD(lParam) - xMouse;
+ dy = HIWORD(lParam) - yMouse;
+
+ if (dx < 0) dx = -dx;
+ if (dy < 0) dy = -dy;
+
+ if ((dx > MOUSE_SENS) || (dy > MOUSE_SENS))
+ {
+ SendMessage(hWnd, WM_CLOSE, 0, 0);
+ }
+ }
+
+ /* Save last location */
+ iMouse = 1;
+ xMouse = LOWORD(lParam);
+ yMouse = HIWORD(lParam);
+
+ return 0;
+ }
+
+ case WM_CLOSE:
+ {
+ DestroyWindow(hwndSaver);
+ hwndSaver = NULL;
+ return 0;
+ }
+ }
+
+ /* Oops */
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+#endif /* USE_SAVER */
+
+
+
+
+
+/*** Temporary Hooks ***/
+
+
+/*
+ * Display warning message (see "z-util.c")
+ */
+static void hack_plog(cptr str)
+{
+ /* Give a warning */
+ if (str)
+ {
+ MessageBox(NULL, str, "Warning",
+ MB_ICONEXCLAMATION | MB_OK);
+ }
+}
+
+
+/*
+ * Display error message and quit (see "z-util.c")
+ */
+static void hack_quit(cptr str)
+{
+ /* Give a warning */
+ if (str)
+ {
+ MessageBox(NULL, str, "Error",
+ MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
+ }
+
+ /* Unregister the classes */
+ UnregisterClass(AppName, hInstance);
+
+ /* Destroy the icon */
+ if (hIcon) DestroyIcon(hIcon);
+
+ /* Exit */
+ exit(0);
+}
+
+
+
+/*** Various hooks ***/
+
+
+/*
+ * Display warning message (see "z-util.c")
+ */
+static void hook_plog(cptr str)
+{
+ /* Warning */
+ if (str)
+ {
+ MessageBox(data[0].w, str, "Warning",
+ MB_ICONEXCLAMATION | MB_OK);
+ }
+}
+
+
+/*
+ * Display error message and quit (see "z-util.c")
+ */
+static void hook_quit(cptr str)
+{
+ int i;
+
+
+ /* Give a warning */
+ if (str)
+ {
+ MessageBox(data[0].w, str, "Error",
+ MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
+ }
+
+
+ /* Save the preferences */
+ save_prefs();
+
+
+ /*** Could use 'Term_nuke_win()' XXX XXX XXX */
+
+ /* Destroy all windows */
+ for (i = MAX_TERM_DATA - 1; i >= 0; --i)
+ {
+ term_force_font(&data[i], NULL);
+ if (data[i].font_want) string_free(data[i].font_want);
+ if (data[i].w) DestroyWindow(data[i].w);
+ data[i].w = 0;
+ }
+
+
+ /*** Free some other stuff ***/
+
+ DeleteObject(hbrYellow);
+
+ if (hPal) DeleteObject(hPal);
+
+ UnregisterClass(AppName, hInstance);
+
+ if (hIcon) DestroyIcon(hIcon);
+
+ exit(0);
+}
+
+/*** Initialize ***/
+
+
+/*
+ * Init some stuff
+ */
+static void init_stuff(void)
+{
+ int i;
+
+ char path[1024];
+
+
+ /* Get program name with full path */
+ GetModuleFileName(hInstance, path, 512);
+
+ /* Get the name of the "*.ini" file */
+ strcpy(path + strlen(path) - 4, ".INI");
+
+ /* Save the the name of the ini-file */
+ ini_file = string_make(path);
+
+ /* Validate the ini-file */
+ validate_file(ini_file);
+
+ /* Analyze the path */
+ i = strlen(path);
+
+ /* Get the path */
+ for (; i > 0; i--)
+ {
+ if (path[i] == '\\')
+ {
+ /* End of path */
+ break;
+ }
+ }
+
+ /* Add "lib" to the path */
+ strcpy(path + i + 1, "lib\\");
+
+ /* Validate the path */
+ validate_dir(path);
+
+ /*** Initialise the file paths ***/
+
+ /* Start with standard ones */
+ init_file_paths(path);
+
+ /* Build the "font" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "font");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_FONT = string_make(path);
+
+
+ /*** Validate the paths to ensure we have a working install ***/
+
+ validate_dir(ANGBAND_DIR_APEX);
+ validate_dir(ANGBAND_DIR_DATA);
+ validate_dir(ANGBAND_DIR_EDIT);
+ validate_dir(ANGBAND_DIR_FILE);
+ validate_dir(ANGBAND_DIR_HELP);
+ validate_dir(ANGBAND_DIR_INFO);
+ validate_dir(ANGBAND_DIR_NOTE);
+ validate_dir(ANGBAND_DIR_SAVE);
+ validate_dir(ANGBAND_DIR_PREF);
+ validate_dir(ANGBAND_DIR_USER);
+ validate_dir(ANGBAND_DIR_XTRA);
+ validate_dir(ANGBAND_DIR_CMOV);
+ validate_dir(ANGBAND_DIR_XTRA_FONT);
+
+ /* Build the filename */
+ path_build(path, 1024, ANGBAND_DIR_XTRA_FONT, "8X13.FON");
+
+ /* Hack -- Validate the basic font */
+ validate_file(path);
+
+
+#ifdef USE_GRAPHICS
+
+ /* Build the "graf" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "graf");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_GRAF = string_make(path);
+
+ /* Validate the "graf" directory */
+ validate_dir(ANGBAND_DIR_XTRA_GRAF);
+
+ /* Build the filename */
+ path_build(path, 1024, ANGBAND_DIR_XTRA_GRAF, "8X8.BMP");
+
+ /* Hack -- Validate the basic graf */
+ validate_file(path);
+
+#endif
+
+
+#ifdef USE_SOUND
+
+ /* Build the "sound" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "sound");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_SOUND = string_make(path);
+
+ /* Validate the "sound" directory */
+ validate_dir(ANGBAND_DIR_XTRA_SOUND);
+
+#endif
+
+
+ /* Build the "help" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "help");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_HELP = string_make(path);
+
+ /* Validate the "help" directory */
+ /* validate_dir(ANGBAND_DIR_XTRA_HELP); */
+}
+
+int FAR PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
+ LPSTR lpCmdLine, int nCmdShow)
+{
+ int i;
+
+ WNDCLASS wc;
+ HDC hdc;
+ MSG msg;
+
+ /* Save globally */
+ hInstance = hInst;
+
+ /* Initialize */
+ if (hPrevInst == NULL)
+ {
+ wc.style = CS_CLASSDC;
+ wc.lpfnWndProc = AngbandWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 4; /* one long pointer to term_data */
+ wc.hInstance = hInst;
+ wc.hIcon = hIcon = LoadIcon(hInst, AppName);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = GetStockObject(BLACK_BRUSH);
+ wc.lpszMenuName = AppName;
+ wc.lpszClassName = AppName;
+
+ if (!RegisterClass(&wc)) exit(1);
+
+ wc.lpfnWndProc = AngbandListProc;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = AngList;
+
+ if (!RegisterClass(&wc)) exit(2);
+
+#ifdef USE_SAVER
+
+ wc.style = CS_VREDRAW | CS_HREDRAW | CS_SAVEBITS | CS_DBLCLKS;
+ wc.lpfnWndProc = AngbandSaverProc;
+ wc.hCursor = NULL;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = "WindowsScreenSaverClass";
+
+ if (!RegisterClass(&wc)) exit(3);
+
+#endif
+
+ }
+
+ /* Temporary hooks */
+ plog_aux = hack_plog;
+ quit_aux = hack_quit;
+ core_aux = hack_quit;
+
+ /* Prepare the filepaths */
+ init_stuff();
+
+ /* Initialize the keypress analyzer */
+ for (i = 0; special_key_list[i]; ++i)
+ {
+ special_key[special_key_list[i]] = TRUE;
+ }
+
+ /* Determine if display is 16/256/true color */
+ hdc = GetDC(NULL);
+ colors16 = (GetDeviceCaps(hdc, BITSPIXEL) == 4);
+ paletted = ((GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) ? TRUE : FALSE);
+ ReleaseDC(NULL, hdc);
+
+ /* Initialize the colors */
+ for (i = 0; i < 256; i++)
+ {
+ byte rv, gv, bv;
+
+ /* Extract desired values */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Extract the "complex" code */
+ win_clr[i] = PALETTERGB(rv, gv, bv);
+
+ /* Save the "simple" code */
+ angband_color_table[i][0] = win_pal[i];
+ }
+
+ /* Prepare the windows */
+ init_windows();
+
+ /* Activate hooks */
+ plog_aux = hook_plog;
+ quit_aux = hook_quit;
+ core_aux = hook_quit;
+
+ /* Set the system suffix */
+ ANGBAND_SYS = "win";
+
+
+ /* Set the keyboard suffix */
+ if (7 != GetKeyboardType(0))
+ ANGBAND_KEYBOARD = "0";
+ else
+ {
+ /* Japanese keyboard */
+ switch (GetKeyboardType(1))
+ {
+ case 0x0D01:
+ case 0x0D02:
+ case 0x0D03:
+ case 0x0D04:
+ case 0x0D05:
+ case 0x0D06:
+ /* NEC PC-98x1 */
+ ANGBAND_KEYBOARD = "NEC98";
+ break;
+ default:
+ /* PC/AT */
+ ANGBAND_KEYBOARD = "JAPAN";
+ }
+ }
+
+ /* Initialize */
+ init_angband();
+
+ /* Prompt the user */
+ prt("", 23, 0);
+ prt("[Press any key to proceed]", 23, 27);
+ Term_fresh();
+
+ inkey();
+
+ /* We are now initialized */
+ initialized = TRUE;
+
+ /* Did the user double click on a save file? */
+ check_for_save_file(lpCmdLine);
+
+ game_in_progress = TRUE;
+ play_game(FALSE);
+
+ /* Prompt the user */
+ Term_fresh();
+
+ /* Process messages forever */
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ /* Initialize the keypress analyzer */
+ for (i = 0; ignore_key_list[i]; ++i)
+ {
+ ignore_key[ignore_key_list[i]] = TRUE;
+ }
+
+ /* Paranoia */
+ quit(NULL);
+
+ /* Paranoia */
+ return (0);
+}
+
+
+#endif /* WINDOWS */
+
+
diff --git a/src/main-x11.c b/src/main-x11.c
new file mode 100644
index 00000000..e32e2617
--- /dev/null
+++ b/src/main-x11.c
@@ -0,0 +1,3210 @@
+/* File: main-x11.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with UNIX/X11 computers.
+ *
+ * To use this file, compile with "USE_X11" defined, and link against all
+ * the various "X11" libraries which may be needed.
+ *
+ * See also "main-xaw.c".
+ *
+ * Part of this file provides a user interface package composed of several
+ * pseudo-objects, including "metadpy" (a display), "infowin" (a window),
+ * "infoclr" (a color), and "infofnt" (a font). Actually, the package was
+ * originally much more interesting, but it was bastardized to keep this
+ * file simple.
+ *
+ * The rest of this file is an implementation of "main-xxx.c" for X11.
+ *
+ * Most of this file is by Ben Harrison (benh@phial.com).
+ */
+
+/*
+ * The following shell script can be used to launch Angband, assuming that
+ * it was extracted into "~/Angband", and compiled using "USE_X11", on a
+ * Linux machine, with a 1280x1024 screen, using 6 windows (with the given
+ * characteristics), with gamma correction of 1.8 -> (1 / 1.8) * 256 = 142,
+ * and without graphics (add "-g" for graphics). Just copy this comment
+ * into a file, remove the leading " * " characters (and the head/tail of
+ * this comment), and make the file executable.
+ *
+ *
+ * #!/bin/csh
+ *
+ * # Describe attempt
+ * echo "Launching angband..."
+ * sleep 2
+ *
+ * # Main window
+ * setenv ANGBAND_X11_FONT_0 10x20
+ * setenv ANGBAND_X11_AT_X_0 5
+ * setenv ANGBAND_X11_AT_Y_0 510
+ *
+ * # Message window
+ * setenv ANGBAND_X11_FONT_1 8x13
+ * setenv ANGBAND_X11_AT_X_1 5
+ * setenv ANGBAND_X11_AT_Y_1 22
+ * setenv ANGBAND_X11_ROWS_1 35
+ *
+ * # Inventory window
+ * setenv ANGBAND_X11_FONT_2 8x13
+ * setenv ANGBAND_X11_AT_X_2 635
+ * setenv ANGBAND_X11_AT_Y_2 182
+ * setenv ANGBAND_X11_ROWS_3 23
+ *
+ * # Equipment window
+ * setenv ANGBAND_X11_FONT_3 8x13
+ * setenv ANGBAND_X11_AT_X_3 635
+ * setenv ANGBAND_X11_AT_Y_3 22
+ * setenv ANGBAND_X11_ROWS_3 12
+ *
+ * # Monster recall window
+ * setenv ANGBAND_X11_FONT_4 6x13
+ * setenv ANGBAND_X11_AT_X_4 817
+ * setenv ANGBAND_X11_AT_Y_4 847
+ * setenv ANGBAND_X11_COLS_4 76
+ * setenv ANGBAND_X11_ROWS_4 11
+ *
+ * # Object recall window
+ * setenv ANGBAND_X11_FONT_5 6x13
+ * setenv ANGBAND_X11_AT_X_5 817
+ * setenv ANGBAND_X11_AT_Y_5 520
+ * setenv ANGBAND_X11_COLS_5 76
+ * setenv ANGBAND_X11_ROWS_5 24
+ *
+ * # The build directory
+ * cd ~/Angband
+ *
+ * # Gamma correction
+ * setenv ANGBAND_X11_GAMMA 142
+ *
+ * # Launch Angband
+ * ./src/angband -mx11 -- -n6 &
+ *
+ */
+
+#include "angband.h"
+
+#ifdef USE_X11
+
+#ifndef __MAKEDEPEND__
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/keysymdef.h>
+#include <X11/Xatom.h>
+#endif /* __MAKEDEPEND__ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include <sys/time.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+/*
+ * Include some helpful X11 code.
+ */
+#include "maid-x11.c"
+
+/*
+ * Hack -- avoid some compiler warnings
+ */
+#define IGNORE_UNUSED_FUNCTIONS
+
+
+/*
+ * Notes on Colors:
+ *
+ * 1) On a monochrome (or "fake-monochrome") display, all colors
+ * will be "cast" to "fg," except for the bg color, which is,
+ * obviously, cast to "bg". Thus, one can ignore this setting.
+ *
+ * 2) Because of the inner functioning of the color allocation
+ * routines, colors may be specified as (a) a typical color name,
+ * (b) a hexidecimal color specification (preceded by a pound sign),
+ * or (c) by strings such as "fg", "bg", "zg".
+ *
+ * 3) Due to the workings of the init routines, many colors
+ * may also be dealt with by their actual pixel values. Note that
+ * the pixel with all bits set is "zg = (1<<metadpy->depth)-1", which
+ * is not necessarily either black or white.
+ */
+
+
+
+/**** Generic Types ****/
+
+
+/*
+ * An X11 pixell specifier
+ */
+typedef unsigned long Pixell;
+
+/*
+ * The structures defined below
+ */
+typedef struct metadpy metadpy;
+typedef struct infowin infowin;
+typedef struct infoclr infoclr;
+typedef struct infofnt infofnt;
+
+
+/*
+ * A structure summarizing a given Display.
+ *
+ * - The Display itself
+ * - The default Screen for the display
+ * - The virtual root (usually just the root)
+ * - The default colormap (from a macro)
+ *
+ * - The "name" of the display
+ *
+ * - The socket to listen to for events
+ *
+ * - The width of the display screen (from a macro)
+ * - The height of the display screen (from a macro)
+ * - The bit depth of the display screen (from a macro)
+ *
+ * - The black Pixell (from a macro)
+ * - The white Pixell (from a macro)
+ *
+ * - The background Pixell (default: black)
+ * - The foreground Pixell (default: white)
+ * - The maximal Pixell (Equals: ((2 ^ depth)-1), is usually ugly)
+ *
+ * - Bit Flag: Force all colors to black and white (default: !color)
+ * - Bit Flag: Allow the use of color (default: depth > 1)
+ * - Bit Flag: We created 'dpy', and so should nuke it when done.
+ */
+struct metadpy
+{
+ Display *dpy;
+ Screen *screen;
+ Window root;
+ Colormap cmap;
+
+ char *name;
+
+ int fd;
+
+ uint width;
+ uint height;
+ uint depth;
+
+ Pixell black;
+ Pixell white;
+
+ Pixell bg;
+ Pixell fg;
+ Pixell zg;
+
+uint mono:
+ 1;
+uint color:
+ 1;
+uint nuke:
+ 1;
+};
+
+
+
+/*
+ * A Structure summarizing Window Information.
+ *
+ * I assume that a window is at most 30000 pixels on a side.
+ * I assume that the root windw is also at most 30000 square.
+ *
+ * - The Window
+ * - The current Input Event Mask
+ *
+ * - The location of the window
+ * - The width, height of the window
+ * - The border width of this window
+ *
+ * - Byte: 1st Extra byte
+ *
+ * - Bit Flag: This window is currently Mapped
+ * - Bit Flag: This window needs to be redrawn
+ * - Bit Flag: This window has been resized
+ *
+ * - Bit Flag: We should nuke 'win' when done with it
+ *
+ * - Bit Flag: 1st extra flag
+ * - Bit Flag: 2nd extra flag
+ * - Bit Flag: 3rd extra flag
+ * - Bit Flag: 4th extra flag
+ */
+struct infowin
+{
+ Window win;
+ long mask;
+
+ s16b ox, oy;
+
+ s16b x, y;
+ s16b w, h;
+ u16b b;
+
+ byte byte1;
+
+uint mapped:
+ 1;
+uint redraw:
+ 1;
+uint resize:
+ 1;
+
+uint nuke:
+ 1;
+
+uint flag1:
+ 1;
+uint flag2:
+ 1;
+uint flag3:
+ 1;
+uint flag4:
+ 1;
+};
+
+
+
+
+
+
+/*
+ * A Structure summarizing Operation+Color Information
+ *
+ * - The actual GC corresponding to this info
+ *
+ * - The Foreground Pixell Value
+ * - The Background Pixell Value
+ *
+ * - Num (0-15): The operation code (As in Clear, Xor, etc)
+ * - Bit Flag: The GC is in stipple mode
+ * - Bit Flag: Destroy 'gc' at Nuke time.
+ */
+struct infoclr
+{
+ GC gc;
+
+ Pixell fg;
+ Pixell bg;
+
+uint code:
+ 4;
+uint stip:
+ 1;
+uint nuke:
+ 1;
+};
+
+
+
+/*
+ * A Structure to Hold Font Information
+ *
+ * - The 'XFontStruct*' (yields the 'Font')
+ *
+ * - The font name
+ *
+ * - The default character width
+ * - The default character height
+ * - The default character ascent
+ *
+ * - Byte: Pixel offset used during fake mono
+ *
+ * - Flag: Force monospacing via 'wid'
+ * - Flag: Nuke info when done
+ */
+struct infofnt
+{
+ XFontStruct *info;
+
+ cptr name;
+
+ s16b wid;
+ s16b twid;
+ s16b hgt;
+ s16b asc;
+
+ byte off;
+
+uint mono:
+ 1;
+uint nuke:
+ 1;
+};
+
+
+
+
+/**** Generic Macros ****/
+
+
+
+/* Set current metadpy (Metadpy) to 'M' */
+#define Metadpy_set(M) \
+Metadpy = M
+
+
+/* Initialize 'M' using Display 'D' */
+#define Metadpy_init_dpy(D) \
+Metadpy_init_2(D,cNULL)
+
+/* Initialize 'M' using a Display named 'N' */
+#define Metadpy_init_name(N) \
+Metadpy_init_2((Display*)(NULL),N)
+
+/* Initialize 'M' using the standard Display */
+#define Metadpy_init() \
+Metadpy_init_name("")
+
+
+/* Init an infowin by giving father as an (info_win*) (or NULL), and data */
+#define Infowin_init_dad(D,X,Y,W,H,B,FG,BG) \
+Infowin_init_data(((D) ? ((D)->win) : (Window)(None)), \
+X,Y,W,H,B,FG,BG)
+
+
+/* Init a top level infowin by pos,size,bord,Colors */
+#define Infowin_init_top(X,Y,W,H,B,FG,BG) \
+Infowin_init_data(None,X,Y,W,H,B,FG,BG)
+
+
+/* Request a new standard window by giving Dad infowin and X,Y,W,H */
+#define Infowin_init_std(D,X,Y,W,H,B) \
+Infowin_init_dad(D,X,Y,W,H,B,Metadpy->fg,Metadpy->bg)
+
+
+/* Set the current Infowin */
+#define Infowin_set(I) \
+(Infowin = (I))
+
+
+/* Set the current Infoclr */
+#define Infoclr_set(C) \
+(Infoclr = (C))
+
+
+#define Infoclr_init_ppo(F,B,O,M) \
+Infoclr_init_data(F,B,O,M)
+
+#define Infoclr_init_cco(F,B,O,M) \
+Infoclr_init_ppo(Infoclr_Pixell(F),Infoclr_Pixell(B),O,M)
+
+#define Infoclr_init_ppn(F,B,O,M) \
+Infoclr_init_ppo(F,B,Infoclr_Opcode(O),M)
+
+#define Infoclr_init_ccn(F,B,O,M) \
+Infoclr_init_cco(F,B,Infoclr_Opcode(O),M)
+
+
+/* Set the current infofnt */
+#define Infofnt_set(I) \
+(Infofnt = (I))
+
+
+/* Errr: Expose Infowin */
+#define Infowin_expose() \
+(!(Infowin->redraw = 1))
+
+/* Errr: Unxpose Infowin */
+#define Infowin_unexpose() \
+(Infowin->redraw = 0)
+
+
+
+/**** Generic Globals ****/
+
+
+/*
+ * The "default" values
+ */
+static metadpy metadpy_default;
+
+
+/*
+ * The "current" variables
+ */
+static metadpy *Metadpy = &metadpy_default;
+static infowin *Infowin = (infowin*)(NULL);
+static infoclr *Infoclr = (infoclr*)(NULL);
+static infofnt *Infofnt = (infofnt*)(NULL);
+
+
+/**** Generic code ****/
+
+/*
+ * Simple routine to save the state of the game when the display connection
+ * is broken. Remember, you cannot do anything in this function that will
+ * generate X protocol requests.
+ */
+int x_io_error_handler(Display *d)
+{
+ /* We have nothing to save */
+ if (!character_generated) return 0;
+
+ save_dungeon();
+ save_player();
+
+ return 0;
+}
+
+/*
+ * Calculate how much space there is in the key queue for the current term.
+ */
+int Term_queue_space(void)
+{
+ /* Find the gap if the tail is before the head. */
+ int space = Term->key_tail - Term->key_head;
+
+ /* Otherwise, add in the extra for looping. */
+ if (space <= 0) space = Term->key_size - space;
+
+ /* The last space is never used as that would be interpreted as leaving
+ * no pending keypresses. */
+ return space -1;
+}
+
+
+/*
+ * Add a series of keypresses to the "queue".
+ *
+ * Return any errors generated by Term_keypress() in doing so, or SUCCESS
+ * if there are none.
+ *
+ * Catch the "out of space" error before anything is printed.
+ *
+ * NB: The keys added here will be interpreted by any macros or keymaps.
+ */
+errr type_string(char *str, uint len)
+{
+ char *s;
+
+ term *old = Term;
+
+ /* Paranoia - no string. */
+ if (!str) return 5;
+
+ /* Hack - calculate the string length here if none given. */
+ if (!len) len = strlen(str);
+
+ /* Activate the main window, as all pastes go there. */
+ Term_activate(term_screen);
+
+ /* Not enough space for the string. */
+ if (Term_queue_space() <= (int)len)
+ return 7;
+
+ for (s = str; s < str + len; s++)
+ {
+ errr err = Term_keypress(*s);
+
+ /* Catch errors other than "str[i] == 0", which is ignored. */
+ if (err && err != -1) return err;
+ }
+
+ /* Activate the original window. */
+ Term_activate(old);
+
+ return 0;
+}
+
+
+/*
+ * Init the current metadpy, with various initialization stuff.
+ *
+ * Inputs:
+ * dpy: The Display* to use (if NULL, create it)
+ * name: The name of the Display (if NULL, the current)
+ *
+ * Notes:
+ * If 'name' is NULL, but 'dpy' is set, extract name from dpy
+ * If 'dpy' is NULL, then Create the named Display
+ * If 'name' is NULL, and so is 'dpy', use current Display
+ *
+ * Return -1 if no Display given, and none can be opened.
+ */
+static errr Metadpy_init_2(Display *dpy, cptr name)
+{
+ metadpy *m = Metadpy;
+
+ /*** Open the display if needed ***/
+
+ /* If no Display given, attempt to Create one */
+ if (!dpy)
+ {
+ /* Attempt to open the display */
+ dpy = XOpenDisplay(name);
+
+ /* Failure */
+ if (!dpy) return ( -1);
+
+ /* We will have to nuke it when done */
+ m->nuke = 1;
+ }
+
+ /* Since the Display was given, use it */
+ else
+ {
+ /* We will not have to nuke it when done */
+ m->nuke = 0;
+ }
+
+ XSetIOErrorHandler(x_io_error_handler);
+
+ /*** Save some information ***/
+
+ /* Save the Display itself */
+ m->dpy = dpy;
+
+ /* Get the Screen and Virtual Root Window */
+ m->screen = DefaultScreenOfDisplay(dpy);
+ m->root = RootWindowOfScreen(m->screen);
+
+ /* Get the default colormap */
+ m->cmap = DefaultColormapOfScreen(m->screen);
+
+ /* Extract the true name of the display */
+ m->name = DisplayString(dpy);
+
+ /* Extract the fd */
+ m->fd = ConnectionNumber(Metadpy->dpy);
+
+ /* Save the Size and Depth of the screen */
+ m->width = WidthOfScreen(m->screen);
+ m->height = HeightOfScreen(m->screen);
+ m->depth = DefaultDepthOfScreen(m->screen);
+
+ /* Save the Standard Colors */
+ m->black = BlackPixelOfScreen(m->screen);
+ m->white = WhitePixelOfScreen(m->screen);
+
+ /*** Make some clever Guesses ***/
+
+ /* Guess at the desired 'fg' and 'bg' Pixell's */
+ m->bg = m->black;
+ m->fg = m->white;
+
+ /* Calculate the Maximum allowed Pixel value. */
+ m->zg = (1 << m->depth) - 1;
+
+ /* Save various default Flag Settings */
+ m->color = ((m->depth > 1) ? 1 : 0);
+ m->mono = ((m->color) ? 0 : 1);
+
+ /* Return "success" */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Nuke the current metadpy
+ */
+static errr Metadpy_nuke(void)
+{
+ metadpy *m = Metadpy;
+
+
+ /* If required, Free the Display */
+ if (m->nuke)
+ {
+ /* Close the Display */
+ XCloseDisplay(m->dpy);
+
+ /* Forget the Display */
+ m->dpy = (Display*)(NULL);
+
+ /* Do not nuke it again */
+ m->nuke = 0;
+ }
+
+ /* Return Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * General Flush/ Sync/ Discard routine
+ */
+static errr Metadpy_update(int flush, int sync, int discard)
+{
+ /* Flush if desired */
+ if (flush) XFlush(Metadpy->dpy);
+
+ /* Sync if desired, using 'discard' */
+ if (sync) XSync(Metadpy->dpy, discard);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Make a simple beep
+ */
+static errr Metadpy_do_beep(void)
+{
+ /* Make a simple beep */
+ XBell(Metadpy->dpy, 100);
+
+ return (0);
+}
+
+
+
+/*
+ * Set the name (in the title bar) of Infowin
+ */
+static errr Infowin_set_name(cptr name)
+{
+ Status st;
+ XTextProperty tp;
+ char buf[128];
+ char *bp = buf;
+ strcpy(buf, name);
+ st = XStringListToTextProperty(&bp, 1, &tp);
+ if (st) XSetWMName(Metadpy->dpy, Infowin->win, &tp);
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Set the icon name of Infowin
+ */
+static errr Infowin_set_icon_name(cptr name)
+{
+ Status st;
+ XTextProperty tp;
+ char buf[128];
+ char *bp = buf;
+ strcpy(buf, name);
+ st = XStringListToTextProperty(&bp, 1, &tp);
+ if (st) XSetWMIconName(Metadpy->dpy, Infowin->win, &tp);
+ return (0);
+}
+
+
+/*
+ * Nuke Infowin
+ */
+static errr Infowin_nuke(void)
+{
+ infowin *iwin = Infowin;
+
+ /* Nuke if requested */
+ if (iwin->nuke)
+ {
+ /* Destory the old window */
+ XDestroyWindow(Metadpy->dpy, iwin->win);
+ }
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Prepare a new 'infowin'.
+ */
+static errr Infowin_prepare(Window xid)
+{
+ infowin *iwin = Infowin;
+
+ Window tmp_win;
+ XWindowAttributes xwa;
+ int x, y;
+ unsigned int w, h, b, d;
+
+ /* Assign stuff */
+ iwin->win = xid;
+
+ /* Check For Error XXX Extract some ACTUAL data from 'xid' */
+ XGetGeometry(Metadpy->dpy, xid, &tmp_win, &x, &y, &w, &h, &b, &d);
+
+ /* Apply the above info */
+ iwin->x = x;
+ iwin->y = y;
+ iwin->w = w;
+ iwin->h = h;
+ iwin->b = b;
+
+ /* Check Error XXX Extract some more ACTUAL data */
+ XGetWindowAttributes(Metadpy->dpy, xid, &xwa);
+
+ /* Apply the above info */
+ iwin->mask = xwa.your_event_mask;
+ iwin->mapped = ((xwa.map_state == IsUnmapped) ? 0 : 1);
+
+ /* And assume that we are exposed */
+ iwin->redraw = 1;
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Initialize a new 'infowin'.
+ */
+static errr Infowin_init_real(Window xid)
+{
+ /* Wipe it clean */
+ (void)WIPE(Infowin, infowin);
+
+ /* Start out non-nukable */
+ Infowin->nuke = 0;
+
+ /* Attempt to Prepare ourself */
+ return (Infowin_prepare(xid));
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Init an infowin by giving some data.
+ *
+ * Inputs:
+ * dad: The Window that should own this Window (if any)
+ * x,y: The position of this Window
+ * w,h: The size of this Window
+ * b,d: The border width and pixel depth
+ *
+ * Notes:
+ * If 'dad == None' assume 'dad == root'
+ */
+static errr Infowin_init_data(Window dad, int x, int y, int w, int h,
+ int b, Pixell fg, Pixell bg)
+{
+ Window xid;
+
+ /* Wipe it clean */
+ (void)WIPE(Infowin, infowin);
+
+
+ /*** Error Check XXX ***/
+
+
+ /*** Create the Window 'xid' from data ***/
+
+ /* What happened here? XXX XXX XXX */
+
+ /* If no parent given, depend on root */
+ if (dad == None)
+
+ /* #ifdef USE_GRAPHICS
+
+ xid = XCreateWindow(Metadpy->dpy, Metadpy->root, x, y, w, h, b, 8, InputOutput, CopyFromParent, 0, 0);
+
+ else
+ */
+
+ /* #else */
+
+ dad = Metadpy->root;
+
+ /* #endif */
+
+ /* Create the Window XXX Error Check */
+ xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg, bg);
+
+ /* Start out selecting No events */
+ XSelectInput(Metadpy->dpy, xid, 0L);
+
+
+ /*** Prepare the new infowin ***/
+
+ /* Mark it as nukable */
+ Infowin->nuke = 1;
+
+ /* Attempt to Initialize the infowin */
+ return (Infowin_prepare(xid));
+}
+
+
+
+/*
+ * Modify the event mask of an Infowin
+ */
+static errr Infowin_set_mask(long mask)
+{
+ /* Save the new setting */
+ Infowin->mask = mask;
+
+ /* Execute the Mapping */
+ XSelectInput(Metadpy->dpy, Infowin->win, Infowin->mask);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Request that Infowin be mapped
+ */
+static errr Infowin_map(void)
+{
+ /* Execute the Mapping */
+ XMapWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Request that Infowin be unmapped
+ */
+static errr Infowin_unmap(void)
+{
+ /* Execute the Un-Mapping */
+ XUnmapWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Request that Infowin be raised
+ */
+static errr Infowin_raise(void)
+{
+ /* Raise towards visibility */
+ XRaiseWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Request that Infowin be lowered
+ */
+static errr Infowin_lower(void)
+{
+ /* Lower towards invisibility */
+ XLowerWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Request that Infowin be moved to a new location
+ */
+static errr Infowin_impell(int x, int y)
+{
+ /* Execute the request */
+ XMoveWindow(Metadpy->dpy, Infowin->win, x, y);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Move and Resize an infowin
+ */
+static errr Infowin_locate(int x, int y, int w, int h)
+{
+ /* Execute the request */
+ XMoveResizeWindow(Metadpy->dpy, Infowin->win, x, y, w, h);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Visually clear Infowin
+ */
+static errr Infowin_wipe(void)
+{
+ /* Execute the request */
+ XClearWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Visually Paint Infowin with the current color
+ */
+static errr Infowin_fill(void)
+{
+ /* Execute the request */
+ XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc,
+ 0, 0, Infowin->w, Infowin->h);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * A NULL terminated pair list of legal "operation names"
+ *
+ * Pairs of values, first is texttual name, second is the string
+ * holding the decimal value that the operation corresponds to.
+ */
+static cptr opcode_pairs[] =
+{
+ "cpy", "3",
+ "xor", "6",
+ "and", "1",
+ "ior", "7",
+ "nor", "8",
+ "inv", "10",
+ "clr", "0",
+ "set", "15",
+
+ "src", "3",
+ "dst", "5",
+
+ "+andReverse", "2",
+ "+andInverted", "4",
+ "+noop", "5",
+ "+equiv", "9",
+ "+orReverse", "11",
+ "+copyInverted", "12",
+ "+orInverted", "13",
+ "+nand", "14",
+ NULL
+};
+
+
+/*
+ * Parse a word into an operation "code"
+ *
+ * Inputs:
+ * str: A string, hopefully representing an Operation
+ *
+ * Output:
+ * 0-15: if 'str' is a valid Operation
+ * -1: if 'str' could not be parsed
+ */
+static int Infoclr_Opcode(cptr str)
+{
+ register int i;
+
+ /* Scan through all legal operation names */
+ for (i = 0; opcode_pairs[i*2]; ++i)
+ {
+ /* Is this the right oprname? */
+ if (streq(opcode_pairs[i*2], str))
+ {
+ /* Convert the second element in the pair into a Code */
+ return (atoi(opcode_pairs[i*2 + 1]));
+ }
+ }
+
+ /* The code was not found, return -1 */
+ return ( -1);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Request a Pixell by name. Note: uses 'Metadpy'.
+ *
+ * Inputs:
+ * name: The name of the color to try to load (see below)
+ *
+ * Output:
+ * The Pixell value that metched the given name
+ * 'Metadpy->fg' if the name was unparseable
+ *
+ * Valid forms for 'name':
+ * 'fg', 'bg', 'zg', '<name>' and '#<code>'
+ */
+static Pixell Infoclr_Pixell(cptr name)
+{
+ XColor scrn;
+
+ /* Attempt to Parse the name */
+ if (name && name[0])
+ {
+ /* The 'bg' color is available */
+ if (streq(name, "bg")) return (Metadpy->bg);
+
+ /* The 'fg' color is available */
+ if (streq(name, "fg")) return (Metadpy->fg);
+
+ /* The 'zg' color is available */
+ if (streq(name, "zg")) return (Metadpy->zg);
+
+ /* The 'white' color is available */
+ if (streq(name, "white")) return (Metadpy->white);
+
+ /* The 'black' color is available */
+ if (streq(name, "black")) return (Metadpy->black);
+
+ /* Attempt to parse 'name' into 'scrn' */
+ if (!(XParseColor(Metadpy->dpy, Metadpy->cmap, name, &scrn)))
+ {
+ plog_fmt("Warning: Couldn't parse color '%s'\n", name);
+ }
+
+ /* Attempt to Allocate the Parsed color */
+ if (!(XAllocColor(Metadpy->dpy, Metadpy->cmap, &scrn)))
+ {
+ plog_fmt("Warning: Couldn't allocate color '%s'\n", name);
+ }
+
+ /* The Pixel was Allocated correctly */
+ else return (scrn.pixel);
+ }
+
+ /* Warn about the Default being Used */
+ plog_fmt("Warning: Using 'fg' for unknown color '%s'\n", name);
+
+ /* Default to the 'Foreground' color */
+ return (Metadpy->fg);
+}
+
+
+/*
+ * Initialize a new 'infoclr' with a real GC.
+ */
+static errr Infoclr_init_1(GC gc)
+{
+ infoclr *iclr = Infoclr;
+
+ /* Wipe the iclr clean */
+ (void)WIPE(iclr, infoclr);
+
+ /* Assign the GC */
+ iclr->gc = gc;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Nuke an old 'infoclr'.
+ */
+static errr Infoclr_nuke(void)
+{
+ infoclr *iclr = Infoclr;
+
+ /* Deal with 'GC' */
+ if (iclr->nuke)
+ {
+ /* Free the GC */
+ XFreeGC(Metadpy->dpy, iclr->gc);
+ }
+
+ /* Forget the current */
+ Infoclr = (infoclr*)(NULL);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Initialize an infoclr with some data
+ *
+ * Inputs:
+ * fg: The Pixell for the requested Foreground (see above)
+ * bg: The Pixell for the requested Background (see above)
+ * op: The Opcode for the requested Operation (see above)
+ * stip: The stipple mode
+ */
+static errr Infoclr_init_data(Pixell fg, Pixell bg, int op, int stip)
+{
+ infoclr *iclr = Infoclr;
+
+ GC gc;
+ XGCValues gcv;
+ unsigned long gc_mask;
+
+
+
+ /*** Simple error checking of opr and clr ***/
+
+ /* Check the 'Pixells' for realism */
+ if (bg > Metadpy->zg) return ( -1);
+ if (fg > Metadpy->zg) return ( -1);
+
+ /* Check the data for trueness */
+ if ((op < 0) || (op > 15)) return ( -1);
+
+
+ /*** Create the requested 'GC' ***/
+
+ /* Assign the proper GC function */
+ gcv.function = op;
+
+ /* Assign the proper GC background */
+ gcv.background = bg;
+
+ /* Assign the proper GC foreground */
+ gcv.foreground = fg;
+
+ /* Hack -- Handle XOR (xor is code 6) by hacking bg and fg */
+ if (op == 6) gcv.background = 0;
+ if (op == 6) gcv.foreground = (bg ^ fg);
+
+ /* Assign the proper GC Fill Style */
+ gcv.fill_style = (stip ? FillStippled : FillSolid);
+
+ /* Turn off 'Give exposure events for pixmap copying' */
+ gcv.graphics_exposures = False;
+
+ /* Set up the GC mask */
+ gc_mask = (GCFunction | GCBackground | GCForeground |
+ GCFillStyle | GCGraphicsExposures);
+
+ /* Create the GC detailed above */
+ gc = XCreateGC(Metadpy->dpy, Metadpy->root, gc_mask, &gcv);
+
+
+ /*** Initialize ***/
+
+ /* Wipe the iclr clean */
+ (void)WIPE(iclr, infoclr);
+
+ /* Assign the GC */
+ iclr->gc = gc;
+
+ /* Nuke it when done */
+ iclr->nuke = 1;
+
+ /* Assign the parms */
+ iclr->fg = fg;
+ iclr->bg = bg;
+ iclr->code = op;
+ iclr->stip = stip ? 1 : 0;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Change the 'fg' for an infoclr
+ *
+ * Inputs:
+ * fg: The Pixell for the requested Foreground (see above)
+ */
+static errr Infoclr_change_fg(Pixell fg)
+{
+ infoclr *iclr = Infoclr;
+
+
+ /*** Simple error checking of opr and clr ***/
+
+ /* Check the 'Pixells' for realism */
+ if (fg > Metadpy->zg) return ( -1);
+
+
+ /*** Change ***/
+
+ /* Change */
+ XSetForeground(Metadpy->dpy, iclr->gc, fg);
+
+ /* Success */
+ return (0);
+}
+
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Nuke an old 'infofnt'.
+ */
+static errr Infofnt_nuke(void)
+{
+ infofnt *ifnt = Infofnt;
+
+ /* Deal with 'name' */
+ if (ifnt->name)
+ {
+ /* Free the name */
+ string_free(ifnt->name);
+ }
+
+ /* Nuke info if needed */
+ if (ifnt->nuke)
+ {
+ /* Free the font */
+ XFreeFont(Metadpy->dpy, ifnt->info);
+ }
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Prepare a new 'infofnt'
+ */
+static errr Infofnt_prepare(XFontStruct *info)
+{
+ infofnt *ifnt = Infofnt;
+
+ XCharStruct *cs;
+
+ /* Assign the struct */
+ ifnt->info = info;
+
+ /* Jump into the max bouonds thing */
+ cs = &(info->max_bounds);
+
+ /* Extract default sizing info */
+ ifnt->asc = info->ascent;
+ ifnt->hgt = info->ascent + info->descent;
+ ifnt->wid = cs->width;
+ if (use_bigtile)
+ ifnt->twid = 2 * ifnt->wid;
+ else
+ ifnt->twid = ifnt->wid;
+
+
+#ifdef OBSOLETE_SIZING_METHOD
+ /* Extract default sizing info */
+ ifnt->asc = cs->ascent;
+ ifnt->hgt = (cs->ascent + cs->descent);
+ ifnt->wid = cs->width;
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Initialize a new 'infofnt'.
+ */
+static errr Infofnt_init_real(XFontStruct *info)
+{
+ /* Wipe the thing */
+ (void)WIPE(Infofnt, infofnt);
+
+ /* No nuking */
+ Infofnt->nuke = 0;
+
+ /* Attempt to prepare it */
+ return (Infofnt_prepare(info));
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Init an infofnt by its Name
+ *
+ * Inputs:
+ * name: The name of the requested Font
+ */
+static errr Infofnt_init_data(cptr name)
+{
+ XFontStruct *info;
+
+
+ /*** Load the info Fresh, using the name ***/
+
+ /* If the name is not given, report an error */
+ if (!name) return ( -1);
+
+ /* Attempt to load the font */
+ info = XLoadQueryFont(Metadpy->dpy, name);
+
+ /* The load failed, try to recover */
+ if (!info) return ( -1);
+
+
+ /*** Init the font ***/
+
+ /* Wipe the thing */
+ (void)WIPE(Infofnt, infofnt);
+
+ /* Attempt to prepare it */
+ if (Infofnt_prepare(info))
+ {
+ /* Free the font */
+ XFreeFont(Metadpy->dpy, info);
+
+ /* Fail */
+ return ( -1);
+ }
+
+ /* Save a copy of the font name */
+ Infofnt->name = string_make(name);
+
+ /* Mark it as nukable */
+ Infofnt->nuke = 1;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Standard Text
+ */
+static errr Infofnt_text_std(int x, int y, cptr str, int len)
+{
+ int i;
+
+
+ /*** Do a brief info analysis ***/
+
+ /* Do nothing if the string is null */
+ if (!str || !*str) return ( -1);
+
+ /* Get the length of the string */
+ if (len < 0) len = strlen(str);
+
+
+ /*** Decide where to place the string, vertically ***/
+
+ /* Ignore Vertical Justifications */
+ y = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
+
+
+ /*** Decide where to place the string, horizontally ***/
+
+ /* Line up with x at left edge of column 'x' */
+ x = (x * Infofnt->wid) + Infowin->ox;
+
+
+ /*** Actually draw 'str' onto the infowin ***/
+
+ /* Be sure the correct font is ready */
+ XSetFont(Metadpy->dpy, Infoclr->gc, Infofnt->info->fid);
+
+
+ /*** Handle the fake mono we can enforce on fonts ***/
+
+ /* Monotize the font */
+ if (Infofnt->mono)
+ {
+ /* Do each character */
+ for (i = 0; i < len; ++i)
+ {
+ /* Note that the Infoclr is set up to contain the Infofnt */
+ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc,
+ x + i * Infofnt->wid + Infofnt->off, y, str + i, 1);
+ }
+ }
+
+ /* Assume monoospaced font */
+ else
+ {
+ /* Note that the Infoclr is set up to contain the Infofnt */
+ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc,
+ x, y, str, len);
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Painting where text would be
+ */
+static errr Infofnt_text_non(int x, int y, cptr str, int len)
+{
+ int w, h;
+
+
+ /*** Find the width ***/
+
+ /* Negative length is a flag to count the characters in str */
+ if (len < 0) len = strlen(str);
+
+ /* The total width will be 'len' chars * standard width */
+ w = len * Infofnt->wid;
+
+
+ /*** Find the X dimensions ***/
+
+ /* Line up with x at left edge of column 'x' */
+ x = x * Infofnt->wid + Infowin->ox;
+
+
+ /*** Find other dimensions ***/
+
+ /* Simply do 'Infofnt->hgt' (a single row) high */
+ h = Infofnt->hgt;
+
+ /* Simply do "at top" in row 'y' */
+ y = y * h + Infowin->oy;
+
+
+ /*** Actually 'paint' the area ***/
+
+ /* Just do a Fill Rectangle */
+ XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, w, h);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*************************************************************************/
+
+
+/*
+ * Angband specific code follows... (ANGBAND)
+ */
+
+
+/*
+ * Hack -- cursor color
+ */
+static infoclr *xor;
+
+/*
+ * Actual color table
+ */
+static infoclr *clr[256];
+
+/*
+ * Color info (unused, red, green, blue).
+ */
+static byte color_table[256][4];
+
+/*
+ * Forward declare
+ */
+typedef struct term_data term_data;
+
+/*
+ * A structure for each "term"
+ */
+struct term_data
+{
+ term t;
+
+ infofnt *fnt;
+
+ infowin *win;
+
+#ifdef USE_GRAPHICS
+
+ XImage *tiles;
+
+ /* Tempory storage for overlaying tiles. */
+ XImage *TmpImage;
+
+#endif
+
+};
+
+
+/*
+ * The number of term data structures
+ */
+#define MAX_TERM_DATA 8
+
+/*
+ * The array of term data structures
+ */
+static term_data data[MAX_TERM_DATA];
+
+/* Use short names for the most commonly used elements of various structures. */
+#define DPY (Metadpy->dpy)
+#define WIN (Infowin->win)
+
+/*
+ * Simply push a set of co-ordinates around.
+ */
+typedef struct co_ord co_ord;
+struct co_ord
+{
+ int x;
+ int y;
+};
+
+/*
+ * A special structure to store information about the text currently
+ * selected.
+ */
+typedef struct x11_selection_type x11_selection_type;
+struct x11_selection_type
+{
+ bool_ select; /* The selection is currently in use. */
+ bool_ drawn; /* The selection is currently displayed. */
+ term *t; /* The window where the selection is found. */
+ co_ord init; /* The starting co-ordinates. */
+ co_ord cur; /* The end co-ordinates (the current ones if still copying). */
+ co_ord old; /* The previous end co-ordinates. */
+ Time time; /* The time at which the selection was finalised. */
+};
+
+static x11_selection_type s_ptr[1];
+
+
+
+/*
+ * Process a keypress event
+ *
+ * Also appears in "main-xaw.c".
+ */
+static void react_keypress(XKeyEvent *xev)
+{
+ int i, n, mc, ms, mo, mx;
+
+ uint ks1;
+
+ XKeyEvent *ev = (XKeyEvent*)(xev);
+
+ KeySym ks;
+
+ char buf[128];
+ char msg[128];
+
+
+ /* Check for "normal" keypresses */
+ n = XLookupString(ev, buf, 125, &ks, NULL);
+
+ /* Terminate */
+ buf[n] = '\0';
+
+
+ /* Hack -- Ignore "modifier keys" */
+ if (IsModifierKey(ks)) return;
+
+
+ /* Hack -- convert into an unsigned int */
+ ks1 = (uint)(ks);
+
+ /* Extract four "modifier flags" */
+ mc = (ev->state & ControlMask) ? TRUE : FALSE;
+ ms = (ev->state & ShiftMask) ? TRUE : FALSE;
+ mo = (ev->state & Mod1Mask) ? TRUE : FALSE;
+ mx = (ev->state & Mod2Mask) ? TRUE : FALSE;
+
+
+ /* Normal keys with no modifiers */
+ if (n && !mo && !mx && !IsSpecialKey(ks))
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; buf[i]; i++) Term_keypress(buf[i]);
+
+ /* All done */
+ return;
+ }
+
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch (ks1)
+ {
+ case XK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return;
+ }
+
+ case XK_Return:
+ {
+ Term_keypress('\r');
+ return;
+ }
+
+ case XK_Tab:
+ {
+ Term_keypress('\t');
+ return;
+ }
+
+ case XK_Delete:
+ case XK_BackSpace:
+ {
+ Term_keypress('\010');
+ return;
+ }
+ }
+
+
+ /* Hack -- Use the KeySym */
+ if (ks)
+ {
+ sprintf(msg, "%c%s%s%s%s_%lX%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ (unsigned long)(ks), 13);
+ }
+
+ /* Hack -- Use the Keycode */
+ else
+ {
+ sprintf(msg, "%c%s%s%s%sK_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ ev->keycode, 13);
+ }
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+
+ /* Hack -- auto-define macros as needed */
+ if (n && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, buf);
+ }
+}
+
+
+/*
+ * Find the square a particular pixel is part of.
+ */
+static void pixel_to_square(int * const x, int * const y,
+ const int ox, const int oy)
+{
+ (*x) = (ox - Infowin->ox) / Infofnt->wid;
+ (*y) = (oy - Infowin->oy) / Infofnt->hgt;
+}
+
+/*
+ * Find the pixel at the top-left corner of a square.
+ */
+static void square_to_pixel(int * const x, int * const y,
+ const int ox, const int oy)
+{
+ (*x) = ox * Infofnt->wid + Infowin->ox;
+ (*y) = oy * Infofnt->hgt + Infowin->oy;
+}
+
+/*
+ * Convert co-ordinates from starting corner/opposite corner to minimum/maximum.
+ */
+static void sort_co_ord(co_ord *min, co_ord *max,
+ const co_ord *b, const co_ord *a)
+{
+ min->x = MIN(a->x, b->x);
+ min->y = MIN(a->y, b->y);
+ max->x = MAX(a->x, b->x);
+ max->y = MAX(a->y, b->y);
+}
+
+/*
+ * Remove the selection by redrawing it.
+ */
+static void mark_selection_clear(int x1, int y1, int x2, int y2)
+{
+ Term_redraw_section(x1, y1, x2, y2);
+}
+
+/*
+ * Select an area by drawing a grey box around it.
+ * NB. These two functions can cause flicker as the selection is modified,
+ * as the game redraws the entire marked section.
+ */
+static void mark_selection_mark(int x1, int y1, int x2, int y2)
+{
+ square_to_pixel(&x1, &y1, x1, y1);
+ square_to_pixel(&x2, &y2, x2, y2);
+ XDrawRectangle(Metadpy->dpy, Infowin->win, clr[2]->gc, x1, y1,
+ x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
+}
+
+/*
+ * Mark a selection by drawing boxes around it (for now).
+ */
+static void mark_selection(void)
+{
+ co_ord min, max;
+ term *old = Term;
+ bool_ draw = s_ptr->select;
+ bool_ clear = s_ptr->drawn;
+
+ /* Open the correct term if necessary. */
+ if (s_ptr->t != old) Term_activate(s_ptr->t);
+
+ if (clear)
+ {
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->old);
+ mark_selection_clear(min.x, min.y, max.x, max.y);
+ }
+ if (draw)
+ {
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
+ mark_selection_mark(min.x, min.y, max.x, max.y);
+ }
+
+ /* Finish on the current term. */
+ if (s_ptr->t != old) Term_activate(old);
+
+ s_ptr->old.x = s_ptr->cur.x;
+ s_ptr->old.y = s_ptr->cur.y;
+ s_ptr->drawn = s_ptr->select;
+}
+
+/*
+ * Forget a selection for one reason or another.
+ */
+static void copy_x11_release(void)
+{
+ /* Deselect the current selection. */
+ s_ptr->select = FALSE;
+
+ /* Remove its graphical represesntation. */
+ mark_selection();
+}
+
+/*
+ * Start to select some text on the screen.
+ */
+static void copy_x11_start(int x, int y)
+{
+ if (s_ptr->select) copy_x11_release();
+
+ /* Remember where the selection started. */
+ s_ptr->t = Term;
+ s_ptr->init.x = s_ptr->cur.x = s_ptr->old.x = x;
+ s_ptr->init.y = s_ptr->cur.y = s_ptr->old.y = y;
+}
+
+/*
+ * Respond to movement of the mouse when selecting text.
+ */
+static void copy_x11_cont(int x, int y, unsigned int buttons)
+{
+ /* Use the nearest square within bounds if the mouse is outside. */
+ x = MIN(MAX(x, 0), Term->wid - 1);
+ y = MIN(MAX(y, 0), Term->hgt - 1);
+
+ /* The left mouse button isn't pressed. */
+ if (~buttons & Button1Mask) return;
+
+ /* Not a selection in this window. */
+ if (s_ptr->t != Term) return;
+
+ /* Not enough movement. */
+ if (x == s_ptr->old.x && y == s_ptr->old.y && s_ptr->select) return;
+
+ /* Something is being selected. */
+ s_ptr->select = TRUE;
+
+ /* Track the selection. */
+ s_ptr->cur.x = x;
+ s_ptr->cur.y = y;
+
+ /* Hack - display it inefficiently. */
+ mark_selection();
+}
+
+/*
+ * Respond to release of the left mouse button by putting the selected text in
+ * the primary buffer.
+ */
+static void copy_x11_end(const Time time)
+{
+ /* No selection. */
+ if (!s_ptr->select) return;
+
+ /* Not a selection in this window. */
+ if (s_ptr->t != Term) return;
+
+ /* Remember when the selection was finalised. */
+ s_ptr->time = time;
+
+ /* Acquire the primary selection. */
+ XSetSelectionOwner(Metadpy->dpy, XA_PRIMARY, Infowin->win, time);
+ if (XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY) != Infowin->win)
+ {
+ /* Failed to acquire the selection, so forget it. */
+ bell();
+ s_ptr->select = FALSE;
+ mark_selection();
+ }
+}
+
+/*
+ * Send a message to request that the PRIMARY buffer be sent here.
+ */
+static void paste_x11_request(const Time time)
+{
+ XEvent event[1];
+ XSelectionRequestEvent *ptr = &(event->xselectionrequest);
+
+ /* Set various things. */
+ ptr->type = SelectionRequest;
+ ptr->display = Metadpy->dpy;
+ ptr->owner = XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY);
+ ptr->requestor = Infowin->win;
+ ptr->selection = XA_PRIMARY;
+ ptr->target = XA_STRING;
+ ptr->property = XA_STRING; /* Unused */
+ ptr->time = time;
+
+ /* Check the owner. */
+ if (ptr->owner == None)
+ {
+ /* No selection. */
+ bell();
+ return;
+ }
+
+ /* Send the SelectionRequest event. */
+ XSendEvent(Metadpy->dpy, ptr->owner, False, NoEventMask, event);
+}
+
+/*
+ * Add a character to a string in preparation for sending it to another
+ * client as a STRING.
+ * This doesn't change anything, as clients tend not to have difficulty in
+ * receiving this format (although the standard specifies a restricted set).
+ * Strings do not have a colour.
+ */
+static int add_char_string(char *buf, byte a, char c)
+{
+ *buf = c;
+ return 1;
+}
+
+/*
+ * Send some text requested by another X client.
+ */
+static void paste_x11_send(XSelectionRequestEvent *rq)
+{
+ XEvent event;
+ XSelectionEvent *ptr = &(event.xselection);
+ int (*add)(char *, byte, char) = 0;
+
+ /* Set the event parameters. */
+ ptr->type = SelectionNotify;
+ ptr->property = rq->property;
+ ptr->display = rq->display;
+ ptr->requestor = rq->requestor;
+ ptr->selection = rq->selection;
+ ptr->target = rq->target;
+ ptr->time = rq->time;
+
+ /* Determine the correct "add a character" function.
+ * As Term->wid is at most 255, these can add up to 4 characters of
+ * output per character of input without problem.
+ * The mechanism will need to change if much more than this is needed.
+ */
+ switch (rq->target)
+ {
+ case XA_STRING:
+ add = add_char_string;
+ break;
+ default:
+ goto error;
+ }
+
+ /* Reply to a known target received recently with data. */
+ if (rq->time >= s_ptr->time && add)
+ {
+ char buf[1024];
+ co_ord max, min;
+ int x, y, l;
+ byte a;
+ char c;
+
+ /* Work out which way around to paste. */
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
+
+ /* Paranoia. */
+ if (XGetSelectionOwner(DPY, XA_PRIMARY) != WIN)
+ {
+ bell();
+ goto error;
+ }
+
+ /* Delete the old value of the property. */
+ XDeleteProperty(DPY, rq->requestor, rq->property);
+
+ for (y = 0; y < Term->hgt; y++)
+ {
+ if (y < min.y) continue;
+ if (y > max.y) break;
+
+ for (x = l = 0; x < Term->wid; x++)
+ {
+ if (x < min.x) continue;
+ if (x > max.x) break;
+
+ /* Find the character. */
+ Term_what(x, y, &a, &c);
+
+ /* Add it. */
+ l += (*add)(buf + l, a, c);
+ }
+
+ /* Terminate all but the last line in an appropriate way. */
+ if (y != max.y) l += (*add)(buf + l, TERM_WHITE, '\n');
+
+ /* Send the (non-empty) string. */
+ XChangeProperty(DPY, rq->requestor, rq->property, rq->target, 8,
+ PropModeAppend, (unsigned char*)buf, l);
+ }
+ }
+ else
+ {
+ /* Respond to all bad requests with property None. */
+error:
+ ptr->property = None;
+ }
+
+ /* Send whatever event we're left with. */
+ XSendEvent(DPY, rq->requestor, FALSE, NoEventMask, &event);
+}
+
+extern errr type_string(char *str, uint len);
+
+/*
+ * Add the contents of the PRIMARY buffer to the input queue.
+ *
+ * Hack - This doesn't use the "time" of the event, and so accepts anything a
+ * client tries to send it.
+ */
+static void paste_x11_accept(const XSelectionEvent *ptr)
+{
+ long offset;
+ unsigned long left;
+
+ /* Failure. */
+ if (ptr->property == None)
+ {
+ bell();
+ return;
+ }
+
+ if (ptr->selection != XA_PRIMARY)
+ {
+ bell();
+ return;
+ }
+ if (ptr->target != XA_STRING)
+ {
+ bell();
+ return;
+ }
+
+ for (offset = 0; ; offset += left)
+ {
+ errr err;
+
+ /* A pointer for the pasted information. */
+ unsigned char *data;
+
+ Atom type;
+ int fmt;
+ unsigned long nitems;
+
+ /* Set data to the string, and catch errors. */
+ if (XGetWindowProperty(Metadpy->dpy, Infowin->win, XA_STRING, offset,
+ 32767, TRUE, XA_STRING, &type, &fmt, &nitems, &left, &data)
+ != Success) break;
+
+ /* Paste the text. */
+ err = type_string((char*)data, (uint)nitems);
+
+ /* Free the data pasted. */
+ XFree(data);
+
+ /* No room. */
+ if (err == 7)
+ {
+ bell();
+ break;
+ }
+ /* Paranoia? - strange errors. */
+ else if (err)
+ {
+ break;
+ }
+
+ /* Pasted everything. */
+ if (!left) return;
+ }
+
+ /* An error has occurred, so free the last bit of data before returning. */
+ XFree(data);
+}
+
+/*
+ * Handle various events conditional on presses of a mouse button.
+ */
+static void handle_button(Time time, int x, int y, int button,
+ bool_ press)
+{
+ /* The co-ordinates are only used in Angband format. */
+ pixel_to_square(&x, &y, x, y);
+
+ if (press && button == 1) copy_x11_start(x, y);
+ if (!press && button == 1) copy_x11_end(time);
+ if (!press && button == 2) paste_x11_request(time);
+}
+
+
+/*
+ * Process events
+ */
+static errr CheckEvent(bool_ wait)
+{
+ term_data *old_td = (term_data*)(Term->data);
+
+ XEvent xev_body, *xev = &xev_body;
+
+ term_data *td = NULL;
+ infowin *iwin = NULL;
+
+ int i;
+
+
+ /* Do not wait unless requested */
+ if (!wait && !XPending(Metadpy->dpy)) return (1);
+
+ /* Hack - redraw the selection, if needed.
+ * This doesn't actually check that one of its squares was drawn to,
+ * only that this may have happened.
+ */
+ if (s_ptr->select && !s_ptr->drawn) mark_selection();
+
+ /* Load the Event */
+ XNextEvent(Metadpy->dpy, xev);
+
+
+ /* Notice new keymaps */
+ if (xev->type == MappingNotify)
+ {
+ XRefreshKeyboardMapping(&xev->xmapping);
+ return 0;
+ }
+
+
+ /* Scan the windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ if (xev->xany.window == data[i].win->win)
+ {
+ td = &data[i];
+ iwin = td->win;
+ break;
+ }
+ }
+
+ /* Unknown window */
+ if (!td || !iwin) return (0);
+
+
+ /* Hack -- activate the Term */
+ Term_activate(&td->t);
+
+ /* Hack -- activate the window */
+ Infowin_set(iwin);
+
+
+ /* Switch on the Type */
+ switch (xev->type)
+ {
+
+ case ButtonPress:
+ case ButtonRelease:
+ {
+ bool_ press = (xev->type == ButtonPress);
+
+ /* Where is the mouse */
+ int x = xev->xbutton.x;
+ int y = xev->xbutton.y;
+
+ int z;
+
+ /* Which button is involved */
+ if (xev->xbutton.button == Button1) z = 1;
+ else if (xev->xbutton.button == Button2) z = 2;
+ else if (xev->xbutton.button == Button3) z = 3;
+ else if (xev->xbutton.button == Button4) z = 4;
+ else if (xev->xbutton.button == Button5) z = 5;
+ else z = 0;
+
+ /* Where is the mouse */
+ x = xev->xbutton.x;
+ y = xev->xbutton.y;
+
+ /* XXX Handle */
+ handle_button(xev->xbutton.time, x, y, z, press);
+
+ break;
+ }
+
+ case EnterNotify:
+ case LeaveNotify:
+ {
+ /* Where is the mouse */
+ /* XXX Handle */
+
+ break;
+ }
+
+ case MotionNotify:
+ {
+ int x = xev->xmotion.x;
+ int y = xev->xmotion.y;
+ unsigned int z = xev->xmotion.state;
+
+ /* Convert to co-ordinates Angband understands. */
+ pixel_to_square(&x, &y, x, y);
+
+ /* Alter the selection if appropriate. */
+ copy_x11_cont(x, y, z);
+
+ /* XXX Handle */
+
+ break;
+ }
+
+ case SelectionNotify:
+ {
+ paste_x11_accept(&(xev->xselection));
+ break;
+ }
+
+ case SelectionRequest:
+ {
+ paste_x11_send(&(xev->xselectionrequest));
+ break;
+ }
+
+ case SelectionClear:
+ {
+ s_ptr->select = FALSE;
+ mark_selection();
+ break;
+ }
+
+ case KeyRelease:
+ {
+ /* Nothing */
+ break;
+ }
+
+ case KeyPress:
+ {
+ /* Hack -- use "old" term */
+ Term_activate(&old_td->t);
+
+ /* Process the key */
+ react_keypress(&(xev->xkey));
+
+ break;
+ }
+
+ case Expose:
+ {
+ /* Ignore "extra" exposes */
+ if (xev->xexpose.count) break;
+
+ /* Clear the window */
+ Infowin_wipe();
+
+ /* Redraw */
+ Term_redraw();
+
+ break;
+ }
+
+ case MapNotify:
+ {
+ Infowin->mapped = 1;
+ Term->mapped_flag = TRUE;
+ break;
+ }
+
+ case UnmapNotify:
+ {
+ Infowin->mapped = 0;
+ Term->mapped_flag = FALSE;
+ break;
+ }
+
+ /* Move and/or Resize */
+ case ConfigureNotify:
+ {
+ int cols, rows;
+
+ int ox = Infowin->ox;
+ int oy = Infowin->oy;
+
+ /* Save the new Window Parms */
+ Infowin->x = xev->xconfigure.x;
+ Infowin->y = xev->xconfigure.y;
+ Infowin->w = xev->xconfigure.width;
+ Infowin->h = xev->xconfigure.height;
+
+ /* Determine "proper" number of rows/cols */
+ cols = ((Infowin->w - (ox + ox)) / td->fnt->wid);
+ rows = ((Infowin->h - (oy + oy)) / td->fnt->hgt);
+
+ /* Hack -- minimal size */
+ if (td == &data[0])
+ {
+ if (cols < 80) cols = 80;
+ if (rows < 24) rows = 24;
+ }
+
+ else
+ {
+ if (cols < 1) cols = 1;
+ if (rows < 1) rows = 1;
+ }
+
+ /* Paranoia */
+ if (cols > 255) cols = 255;
+ if (rows > 255) rows = 255;
+
+ /* Resize the Term (if needed) */
+ Term_resize(cols, rows);
+ break;
+ }
+ }
+
+
+ /* Hack -- Activate the old term */
+ Term_activate(&old_td->t);
+
+ /* Hack -- Activate the proper window */
+ Infowin_set(old_td->win);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle "activation" of a term
+ */
+static errr Term_xtra_x11_level(int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Handle "activate" */
+ if (v)
+ {
+ /* Activate the window */
+ Infowin_set(td->win);
+
+ /* Activate the font */
+ Infofnt_set(td->fnt);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_x11_react(void)
+{
+ int i;
+
+ if (Metadpy->color)
+ {
+ /* Check the colors */
+ for (i = 0; i < 256; i++)
+ {
+ if ((color_table[i][0] != angband_color_table[i][0]) ||
+ (color_table[i][1] != angband_color_table[i][1]) ||
+ (color_table[i][2] != angband_color_table[i][2]) ||
+ (color_table[i][3] != angband_color_table[i][3]))
+ {
+ Pixell pixel;
+
+ /* Save new values */
+ color_table[i][0] = angband_color_table[i][0];
+ color_table[i][1] = angband_color_table[i][1];
+ color_table[i][2] = angband_color_table[i][2];
+ color_table[i][3] = angband_color_table[i][3];
+
+ /* Create pixel */
+ pixel = create_pixel(Metadpy->dpy,
+ color_table[i][1],
+ color_table[i][2],
+ color_table[i][3]);
+
+ /* Change the foreground */
+ Infoclr_set(clr[i]);
+ Infoclr_change_fg(pixel);
+ }
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_x11(int n, int v)
+{
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ Metadpy_do_beep(); return (0);
+
+ /* Flush the output XXX XXX */
+ case TERM_XTRA_FRESH: Metadpy_update(1, 0, 0); return (0);
+
+ /* Process random events XXX */
+ case TERM_XTRA_BORED:
+ {
+ return (CheckEvent(0));
+ }
+
+ /* Process Events XXX */
+ case TERM_XTRA_EVENT:
+ {
+ return (CheckEvent(v));
+ }
+
+ /* Flush the events XXX */
+ case TERM_XTRA_FLUSH: while (!CheckEvent(FALSE)); return (0);
+
+ /* Handle change in the "level" */
+ case TERM_XTRA_LEVEL: return (Term_xtra_x11_level(v));
+
+ /* Clear the screen, and redraw any selection later. */
+ case TERM_XTRA_CLEAR: Infowin_wipe(); s_ptr->drawn = FALSE; return (0);
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ usleep(1000 * v);
+ return (0);
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while ((entry = readdir(directory)))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+
+ closedir(directory);
+ return 0;
+ }
+
+ /* React to changes */
+ case TERM_XTRA_REACT: return (Term_xtra_x11_react());
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN: Infowin_set_name(angband_term_name[0]); return (0);
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+/*
+ * Draw the cursor as an inverted rectangle.
+ *
+ * Consider a rectangular outline like "main-mac.c". XXX XXX
+ */
+static errr Term_curs_x11(int x, int y)
+{
+ /* Draw the cursor */
+ Infoclr_set(xor);
+
+ if (use_bigtile && x + 1 < Term->wid && Term->old->a[y][x + 1] == 255)
+ Infofnt_text_non(x, y, " ", 2);
+ else
+ /* Hilite the cursor character */
+ Infofnt_text_non(x, y, " ", 1);
+
+ /* Redraw the selection if any, as it may have been obscured. (later) */
+ s_ptr->drawn = FALSE;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase some characters.
+ */
+static errr Term_wipe_x11(int x, int y, int n)
+{
+ /* Erase (use black) */
+ Infoclr_set(clr[TERM_DARK]);
+
+ /* Mega-Hack -- Erase some space */
+ Infofnt_text_non(x, y, "", n);
+
+ /* Redraw the selection if any, as it may have been obscured. (later) */
+ s_ptr->drawn = FALSE;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some textual characters.
+ */
+static errr Term_text_x11(int x, int y, int n, byte a, cptr s)
+{
+ /* Draw the text */
+ Infoclr_set(clr[a]);
+
+ /* Draw the text */
+ Infofnt_text_std(x, y, s, n);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Draw some graphical characters.
+ */
+static errr Term_pict_x11(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+{
+ int i, x1, y1;
+
+ byte a;
+ char c;
+
+ byte ta;
+ char tc;
+ int x2, y2;
+
+ byte ea;
+ char ec;
+ int x3, y3;
+ bool_ has_overlay;
+
+ int k, l;
+
+ unsigned long pixel, blank;
+
+ term_data *td = (term_data*)(Term->data);
+
+ y *= Infofnt->hgt;
+ x *= Infofnt->wid;
+
+ /* Add in affect of window boundaries */
+ y += Infowin->oy;
+ x += Infowin->ox;
+
+ for (i = 0; i < n; ++i, x += td->fnt->wid)
+ {
+ a = *ap++;
+ c = *cp++;
+
+ /* For extra speed - cache these values */
+ x1 = (c & 0x7F) * td->fnt->twid;
+ y1 = (a & 0x7F) * td->fnt->hgt;
+
+ ta = *tap++;
+ tc = *tcp++;
+
+ /* For extra speed - cache these values */
+ x2 = (tc & 0x7F) * td->fnt->twid;
+ y2 = (ta & 0x7F) * td->fnt->hgt;
+
+ ea = *eap++;
+ ec = *ecp++;
+ has_overlay = (ea && ec);
+
+ /* For extra speed - cache these values too */
+ x3 = (ec & 0x7F) * td->fnt->twid;
+ y3 = (ea & 0x7F) * td->fnt->hgt;
+
+ /* Optimise the common case */
+ if ((x1 == x2) && (y1 == y2))
+ {
+ /* Draw object / terrain */
+ if (!has_overlay)
+ {
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->tiles,
+ x1, y1,
+ x, y,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+ /* There's a terrain overlay */
+ else
+ {
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(td->tiles, 0, td->fnt->hgt * 6);
+ for (k = 0; k < td->fnt->twid; k++)
+ {
+ for (l = 0; l < td->fnt->hgt; l++)
+ {
+ /* If mask set in overlay... */
+ if ((pixel = XGetPixel(td->tiles, x3 + k, y3 + l)) == blank)
+ {
+ /* Output from the terrain */
+ pixel = XGetPixel(td->tiles, x1 + k, y1 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(td->TmpImage, k, l, pixel);
+ }
+ }
+
+ /* Draw to screen */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->TmpImage,
+ 0, 0, x, y,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+ }
+ else
+ {
+
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(td->tiles, 0, td->fnt->hgt * 6);
+
+ for (k = 0; k < td->fnt->twid; k++)
+ {
+ for (l = 0; l < td->fnt->hgt; l++)
+ {
+ /* Overlay */
+ if (has_overlay)
+ {
+ pixel = XGetPixel(td->tiles, x3 + k, y3 + l);
+ }
+
+ /* Hack -- No overlay */
+ else
+ {
+ pixel = blank;
+ }
+
+ /* If it's blank... */
+ if (pixel == blank)
+ {
+ /* Look at mon/obj */
+ pixel = XGetPixel(td->tiles, x1 + k, y1 + l);
+ }
+
+ /* If it's blank too, use terrain */
+ if (pixel == blank)
+ {
+ pixel = XGetPixel(td->tiles, x2 + k, y2 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(td->TmpImage, k, l, pixel);
+ }
+ }
+
+
+
+ /* Draw to screen */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->TmpImage,
+ 0, 0, x, y,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+ x += td->fnt->wid;
+ }
+
+ /* Redraw the selection if any, as it may have been obscured. (later) */
+ s_ptr->drawn = FALSE;
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+
+/*
+ * Initialize a term_data
+ */
+static errr term_data_init(term_data *td, int i)
+{
+ term *t = &td->t;
+
+ cptr name = angband_term_name[i];
+
+ cptr font;
+
+ int x = 0;
+ int y = 0;
+
+ int cols = 80;
+ int rows = 24;
+
+ int ox = 1;
+ int oy = 1;
+
+ int wid, hgt, num;
+
+ char buf[80];
+
+ cptr str;
+
+ int val;
+
+ XClassHint *ch;
+
+ char res_name[20];
+ char res_class[20];
+
+ XSizeHints *sh;
+
+
+ /* Window specific font name */
+ sprintf(buf, "ANGBAND_X11_FONT_%d", i);
+
+ /* Check environment for that font */
+ font = getenv(buf);
+
+ /* Check environment for "base" font */
+ if (!font) font = getenv("ANGBAND_X11_FONT");
+
+ /* No environment variables, use default font */
+ if (!font)
+ {
+ switch (i)
+ {
+ case 0:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 1:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 2:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 3:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 4:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 5:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 6:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 7:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ default:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ }
+ }
+
+ /* Window specific location (x) */
+ sprintf(buf, "ANGBAND_X11_AT_X_%d", i);
+ str = getenv(buf);
+ x = (str != NULL) ? atoi(str) : -1;
+
+ /* Window specific location (y) */
+ sprintf(buf, "ANGBAND_X11_AT_Y_%d", i);
+ str = getenv(buf);
+ y = (str != NULL) ? atoi(str) : -1;
+
+
+ /* Window specific cols */
+ sprintf(buf, "ANGBAND_X11_COLS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) cols = val;
+
+ /* Window specific rows */
+ sprintf(buf, "ANGBAND_X11_ROWS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) rows = val;
+
+
+ /* Window specific inner border offset (ox) */
+ sprintf(buf, "ANGBAND_X11_IBOX_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) ox = val;
+
+ /* Window specific inner border offset (oy) */
+ sprintf(buf, "ANGBAND_X11_IBOY_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) oy = val;
+
+
+ /* Prepare the standard font */
+ MAKE(td->fnt, infofnt);
+ Infofnt_set(td->fnt);
+ Infofnt_init_data(font);
+
+ /* Hack -- key buffer size */
+ num = (i == 0 ? 1024 : 16);
+
+ /* Assume full size windows */
+ wid = cols * td->fnt->wid + (ox + ox);
+ hgt = rows * td->fnt->hgt + (oy + oy);
+
+ /* Create a top-window */
+ MAKE(td->win, infowin);
+ Infowin_set(td->win);
+ Infowin_init_top(x, y, wid, hgt, 0,
+ Metadpy->fg, Metadpy->bg);
+
+ /* Ask for certain events */
+ Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask |
+ PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
+
+ /* Set the window name */
+ Infowin_set_name(name);
+
+ /* Save the inner border */
+ Infowin->ox = ox;
+ Infowin->oy = oy;
+
+ /* Make Class Hints */
+ ch = XAllocClassHint();
+
+ if (ch == NULL) quit("XAllocClassHint failed");
+
+ strcpy(res_name, name);
+ res_name[0] = FORCELOWER(res_name[0]);
+ ch->res_name = res_name;
+
+ strcpy(res_class, "Angband");
+ ch->res_class = res_class;
+
+ XSetClassHint(Metadpy->dpy, Infowin->win, ch);
+
+ /* Make Size Hints */
+ sh = XAllocSizeHints();
+
+ /* Oops */
+ if (sh == NULL) quit("XAllocSizeHints failed");
+
+ /* Fixed window size */
+ if (i == 0)
+ {
+ /* Main window: 80x24 -- 255x255 */
+ sh->flags = PMinSize | PMaxSize;
+ sh->min_width = 80 * td->fnt->wid + (ox + ox);
+ sh->min_height = 24 * td->fnt->hgt + (oy + oy);
+ sh->max_width = 255 * td->fnt->wid + (ox + ox);
+ sh->max_height = 255 * td->fnt->hgt + (oy + oy);
+ }
+
+ /* Variable window size */
+ else
+ {
+ /* Subwindows: 1x1 -- 255x255 */
+ sh->flags = PMinSize | PMaxSize;
+ sh->min_width = td->fnt->wid + (ox + ox);
+ sh->min_height = td->fnt->hgt + (oy + oy);
+ sh->max_width = 255 * td->fnt->wid + (ox + ox);
+ sh->max_height = 255 * td->fnt->hgt + (oy + oy);
+ }
+
+ /* Resize increment */
+ sh->flags |= PResizeInc;
+ sh->width_inc = td->fnt->wid;
+ sh->height_inc = td->fnt->hgt;
+
+ /* Base window size */
+ sh->flags |= PBaseSize;
+ sh->base_width = (ox + ox);
+ sh->base_height = (oy + oy);
+
+ /* Use the size hints */
+ XSetWMNormalHints(Metadpy->dpy, Infowin->win, sh);
+
+ /* Map the window */
+ Infowin_map();
+
+
+ /* Move the window to requested location */
+ if ((x >= 0) && (y >= 0)) Infowin_impell(x, y);
+
+
+ /* Initialize the term */
+ term_init(t, cols, rows, num);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_x11;
+ t->curs_hook = Term_curs_x11;
+ t->wipe_hook = Term_wipe_x11;
+ t->text_hook = Term_text_x11;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialization function for an "X11" module to Angband
+ */
+errr init_x11(int argc, char *argv[])
+{
+ int i;
+
+ cptr dpy_name = "";
+
+ int num_term = 1;
+
+#ifdef USE_GRAPHICS
+
+ char filename[1024];
+
+ int pict_wid = 0;
+ int pict_hgt = 0;
+ bool_ force_old_graphics = FALSE;
+
+ char *TmpData;
+
+#endif /* USE_GRAPHICS */
+
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ if (prefix(argv[i], "-d"))
+ {
+ dpy_name = &argv[i][2];
+ continue;
+ }
+
+#ifdef USE_GRAPHICS
+
+ if (prefix(argv[i], "-s"))
+ {
+ smoothRescaling = FALSE;
+ continue;
+ }
+
+ if (prefix(argv[i], "-o"))
+ {
+ force_old_graphics = TRUE;
+ continue;
+ }
+
+ if (prefix(argv[i], "-b"))
+ {
+ arg_bigtile = use_bigtile = TRUE;
+ continue;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ if (prefix(argv[i], "-n"))
+ {
+ num_term = atoi(&argv[i][2]);
+ if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA;
+ else if (num_term < 1) num_term = 1;
+ continue;
+ }
+
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+
+ /* Init the Metadpy if possible */
+ if (Metadpy_init_name(dpy_name)) return ( -1);
+
+
+ /* Prepare cursor color */
+ MAKE(xor, infoclr);
+ Infoclr_set(xor);
+ Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0);
+
+
+ /* Prepare normal colors */
+ for (i = 0; i < 256; ++i)
+ {
+ Pixell pixel;
+
+ MAKE(clr[i], infoclr);
+
+ Infoclr_set(clr[i]);
+
+ /* Acquire Angband colors */
+ color_table[i][0] = angband_color_table[i][0];
+ color_table[i][1] = angband_color_table[i][1];
+ color_table[i][2] = angband_color_table[i][2];
+ color_table[i][3] = angband_color_table[i][3];
+
+ /* Default to monochrome */
+ pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg);
+
+ /* Handle color */
+ if (Metadpy->color)
+ {
+ /* Create pixel */
+ pixel = create_pixel(Metadpy->dpy,
+ color_table[i][1],
+ color_table[i][2],
+ color_table[i][3]);
+ }
+
+ /* Initialize the color */
+ Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0);
+ }
+
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+
+ /* Save global entry */
+ angband_term[i] = Term;
+ }
+
+ /* Raise the "Angband" window */
+ Infowin_set(data[0].win);
+ Infowin_raise();
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+
+#ifdef USE_GRAPHICS
+
+ /* Try graphics */
+ if (arg_graphics)
+ {
+ /* Try the "16x16.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/16x16.bmp");
+
+ /* Use the "16x16.bmp" file if it exists */
+ if (!force_old_graphics &&
+ (0 == fd_close(fd_open(filename, O_RDONLY))))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 16;
+
+ ANGBAND_GRAF = "new";
+ }
+ else
+ {
+ /* Try the "8x8.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/8x8.bmp");
+
+ /* Use the "8x8.bmp" file if it exists */
+ if (0 == fd_close(fd_open(filename, O_RDONLY)))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 8;
+
+ ANGBAND_GRAF = "old";
+ }
+ }
+ }
+
+ /* Load graphics */
+ if (use_graphics)
+ {
+ Display *dpy = Metadpy->dpy;
+
+ XImage *tiles_raw;
+
+ /* Load the graphical tiles */
+ tiles_raw = ReadBMP(dpy, filename);
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ term *t = &td->t;
+
+ /* Graphics hook */
+ t->pict_hook = Term_pict_x11;
+
+ /* Use graphics sometimes */
+ t->higher_pict = TRUE;
+
+ /* Resize tiles */
+ td->tiles =
+ ResizeImage(dpy, tiles_raw,
+ pict_wid, pict_hgt,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+ /* Initialize the transparency masks */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+ int ii, jj;
+ int depth = DefaultDepth(dpy, DefaultScreen(dpy));
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+ int total;
+
+
+ /* Determine total bytes needed for image */
+ ii = 1;
+ jj = (depth - 1) >> 2;
+ while (jj >>= 1) ii <<= 1;
+ total = td->fnt->twid * td->fnt->hgt * ii;
+
+
+ TmpData = (char *)malloc(total);
+
+ td->TmpImage = XCreateImage(dpy, visual, depth,
+ ZPixmap, 0, TmpData,
+ td->fnt->twid, td->fnt->hgt, 8, 0);
+
+ }
+
+ /* Free tiles_raw? XXX XXX */
+ }
+
+#endif /* USE_GRAPHICS */
+
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_X11 */
+
diff --git a/src/main-xaw.c b/src/main-xaw.c
new file mode 100644
index 00000000..8795e00d
--- /dev/null
+++ b/src/main-xaw.c
@@ -0,0 +1,1888 @@
+/* File: main-xaw.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, Torbjorn Lindgren, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with UNIX/X11 computers.
+ *
+ * To use this file, compile with "USE_XAW" defined, and link against all
+ * the various "X11" libraries which may be needed.
+ *
+ * See also "main-x11.c".
+ *
+ * The Angband widget is not as self-contained as it really should be.
+ * Originally everything was output to a Pixmap which was later copied
+ * to the screen when necessary. The idea was abandoned since Pixmaps
+ * create big performance problems for some really old X terminals (such
+ * as 3/50's running Xkernel).
+ *
+ * Initial framework (and some code) by Ben Harrison (benh@phial.com).
+ *
+ * Most of this file is by Torbjorn Lindgren (tl@cd.chalmers.se).
+ *
+ * Major modifications by Ben Harrison (benh@phial.com).
+ */
+
+
+#include "angband.h"
+
+
+#ifdef USE_XAW
+
+
+#ifndef __MAKEDEPEND__
+#include <X11/Xlib.h>
+#include <X11/StringDefs.h>
+#include <X11/Xutil.h>
+#include <X11/Intrinsic.h>
+#include <X11/Shell.h>
+#include <X11/keysym.h>
+#include <X11/keysymdef.h>
+#include <X11/IntrinsicP.h>
+#include <X11/CoreP.h>
+#include <X11/ShellP.h>
+#include <X11/StringDefs.h>
+#include <X11/Xaw/SimpleP.h>
+#include <X11/Xaw/Simple.h>
+#include <X11/Xaw/XawInit.h>
+#endif /* __MAKEDEPEND__ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+
+/*
+ * Include some helpful X11 code.
+ */
+#include "maid-x11.c"
+
+
+
+/**** Resources ****/
+
+
+/*
+
+Name Class RepType Default Value
+---- ----- ------- -------------
+background Background Pixel XtDefaultBackground
+border BorderColor Pixel XtDefaultForeground
+borderWidth BorderWidth Dimension 1
+cursor Cursor Cursor None
+cursorName Cursor String NULL
+destroyCallback Callback Pointer NULL
+height Height Dimension 0
+insensitiveBorder Insensitive Pixmap Gray
+mappedWhenManaged MappedWhenManaged Boolean True
+pointerColor Foreground Pixel XtDefaultForeground
+pointerColorBackground Background Pixel XtDefaultBackground
+sensitive Sensitive Boolean True
+width Width Dimension 0
+x Position Position 0
+y Position Position 0
+
+
+The colors can be changed using the standard Angband user pref files,
+which can also be used to provide black text on a white background,
+by setting color zero to "#FFFFFF" and color one to "#000000", since
+the other colors are unused.
+
+*/
+
+
+/*
+ * New resource names
+ */
+#define XtNstartRows "startRows"
+#define XtNstartColumns "startColumns"
+#define XtNminRows "minRows"
+#define XtNminColumns "minColumns"
+#define XtNmaxRows "maxRows"
+#define XtNmaxColumns "maxColumns"
+#define XtNinternalBorder "internalBorder"
+#define XtNredrawCallback "redrawCallback"
+
+/*
+ * Total normal colors
+ */
+#define NUM_COLORS 256
+
+/*
+ * Special "XOR" color
+ */
+#define COLOR_XOR 256
+
+
+
+/**** The Widget Code ****/
+
+
+/*
+ * Forward declarations
+ */
+typedef struct AngbandPart AngbandPart;
+typedef struct AngbandRec *AngbandWidget;
+typedef struct AngbandRec AngbandRec;
+typedef struct AngbandClassRec *AngbandWidgetClass;
+typedef struct AngbandClassPart AngbandClassPart;
+typedef struct AngbandClassRec AngbandClassRec;
+
+typedef struct term_data term_data;
+
+
+/*
+ * A structure for each "term"
+ */
+struct term_data
+{
+ term t;
+
+ AngbandWidget widget;
+};
+
+
+/*
+ * Maximum number of windows
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * An array of term_data's
+ */
+static term_data data[MAX_TERM_DATA];
+
+
+/*
+ * Current number of windows open
+ */
+static int num_term = MAX_TERM_DATA;
+
+
+/*
+ * New fields for the Angband widget record
+ */
+struct AngbandPart
+{
+ /* Settable resources */
+ int start_rows;
+ int start_columns;
+ int min_rows;
+ int min_columns;
+ int max_rows;
+ int max_columns;
+ int internal_border;
+ String font;
+
+ XtCallbackList redraw_callbacks;
+
+#ifdef USE_GRAPHICS
+
+ /* Tiles */
+ XImage *tiles;
+
+ /* Tempory storage for overlaying tiles. */
+ XImage *TmpImage;
+
+#endif /* USE_GRAPHICS */
+
+ /* Private state */
+ XFontStruct *fnt;
+ Dimension fontheight;
+ Dimension fontwidth;
+ Dimension fontascent;
+
+ /* Color info for GC's */
+ byte color[NUM_COLORS][4];
+
+ /* GC's (including "xor") */
+ GC gc[NUM_COLORS + 1];
+};
+
+
+/*
+ * Full instance record declaration
+ */
+struct AngbandRec
+{
+ CorePart core;
+ SimplePart simple;
+ AngbandPart angband;
+};
+
+
+/*
+ * New fields for the Angband widget class record
+ */
+struct AngbandClassPart
+{
+ int dummy;
+};
+
+
+/*
+ * Full class record declaration
+ */
+struct AngbandClassRec
+{
+ CoreClassPart core_class;
+ SimpleClassPart simple_class;
+ AngbandClassPart angband_class;
+};
+
+
+
+/*
+ * Hack -- see below
+ */
+#define offset(field) XtOffsetOf(AngbandRec, angband.field)
+
+
+/*
+ * Fallback resources for Angband widget
+ */
+static XtResource resources[] =
+{
+ { XtNstartRows, XtCValue, XtRInt, sizeof(int),
+ offset(start_rows), XtRImmediate, (XtPointer) 24 },
+ { XtNstartColumns, XtCValue, XtRInt, sizeof(int),
+ offset(start_columns), XtRImmediate, (XtPointer) 80 },
+ { XtNminRows, XtCValue, XtRInt, sizeof(int),
+ offset(min_rows), XtRImmediate, (XtPointer) 1 },
+ { XtNminColumns, XtCValue, XtRInt, sizeof(int),
+ offset(min_columns), XtRImmediate, (XtPointer) 1 },
+ { XtNmaxRows, XtCValue, XtRInt, sizeof(int),
+ offset(max_rows), XtRImmediate, (XtPointer) 255 },
+ { XtNmaxColumns, XtCValue, XtRInt, sizeof(int),
+ offset(max_columns), XtRImmediate, (XtPointer) 255 },
+ { XtNinternalBorder, XtCValue, XtRInt, sizeof(int),
+ offset(internal_border), XtRImmediate, (XtPointer) 2 },
+ { XtNfont, XtCFont, XtRString, sizeof(char *),
+ offset(font), XtRString, DEFAULT_X11_FONT },
+ { XtNredrawCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
+ offset(redraw_callbacks), XtRCallback, (XtPointer)NULL }
+};
+
+
+/*
+ * Hack -- see above
+ */
+#undef offset
+
+
+/*
+ * Forward declarations for Widget functions
+ */
+static void Initialize(AngbandWidget request, AngbandWidget wnew);
+static void Redisplay(AngbandWidget w, XEvent *event, Region region);
+static Boolean SetValues(AngbandWidget current, AngbandWidget request,
+ AngbandWidget wnew, ArgList args, Cardinal *num_args);
+static void Destroy(AngbandWidget widget);
+static void Resize_term(AngbandWidget wnew);
+
+/*
+ * Forward declaration for internal functions
+ */
+static void calculateSizeHints(AngbandWidget wnew);
+static XFontStruct *getFont(AngbandWidget widget,
+ String font, Boolean fallback);
+
+
+/*
+ * Hack -- see below
+ */
+#define superclass (&simpleClassRec)
+
+
+/*
+ * Class record constanst
+ */
+AngbandClassRec angbandClassRec =
+{
+ {
+ /* Core class fields initialization */
+ /* superclass */ (WidgetClass) superclass,
+ /* class_name */ "Angband",
+ /* widget_size */ sizeof(AngbandRec),
+ /* class_initialize */ NULL,
+ /* class_part_initialize*/ NULL,
+ /* class_inited */ FALSE,
+ /* initialize */ (XtInitProc) Initialize,
+ /* initialize_hook */ NULL,
+ /* realize */ XtInheritRealize,
+ /* actions */ NULL,
+ /* num_actions */ 0,
+ /* resources */ resources,
+ /* num_resources */ XtNumber(resources),
+ /* xrm_class */ NULLQUARK,
+ /* compress_motion */ TRUE,
+ /* compress_exposure */ XtExposeCompressMultiple,
+ /* compress_enterleave */ TRUE,
+ /* visible_interest */ FALSE,
+ /* destroy */ (XtWidgetProc) Destroy,
+ /* resize */ (XtWidgetProc) Resize_term,
+ /* expose */ (XtExposeProc) Redisplay,
+ /* set_values */ (XtSetValuesFunc) SetValues,
+ /* set_values_hook */ NULL,
+ /* set_values_almost */ XtInheritSetValuesAlmost,
+ /* get_values_hook */ NULL,
+ /* accept_focus */ NULL,
+ /* version */ XtVersion,
+ /* callback_private */ NULL,
+ /* tm_table */ NULL,
+ /* query_geometry */ NULL,
+ /* display_accelerator */ XtInheritDisplayAccelerator,
+ /* extension */ NULL
+ },
+ /* Simple class fields initialization */
+ {
+ /* change_sensitive */ XtInheritChangeSensitive
+#ifndef OLDXAW
+ , NULL
+#endif
+ },
+ /* Angband class fields initialization */
+ {
+ /* nothing */ 0
+ }
+};
+
+/*
+ * Hack -- see above
+ */
+#undef superclass
+
+
+/*
+ * Class record pointer
+ */
+WidgetClass angbandWidgetClass = (WidgetClass) &angbandClassRec;
+
+
+/*
+ * Public procedures
+ */
+
+/*
+ * Simple routine to save the state of the game when the display connection
+ * is broken. Remember, you cannot do anything in this function that will
+ * generate X protocol requests.
+ */
+static int x_io_error_handler(Display *d)
+{
+ /* We have nothing to save */
+ if (!character_generated) return 0;
+
+ save_dungeon();
+ save_player();
+
+ return 0;
+}
+
+
+/*
+ * Clear an area
+ */
+static void AngbandClearArea(AngbandWidget widget,
+ int x, int y, int w, int h, int a)
+{
+ /* Figure out which area to clear */
+ y = y * widget->angband.fontheight + widget->angband.internal_border;
+ x = x * widget->angband.fontwidth + widget->angband.internal_border;
+
+ /* Clear the area */
+ XFillRectangle(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[a],
+ x, y,
+ widget->angband.fontwidth * w,
+ widget->angband.fontheight * h);
+}
+
+
+
+/*
+ * Output some text
+ */
+static void AngbandOutputText(AngbandWidget widget, int x, int y,
+ String txt, int len, int a)
+{
+ /* Do nothing if the string is null */
+ if (!txt || !*txt) return;
+
+ /* Check the length, and fix it if it's below zero */
+ if (len < 0) len = strlen(txt);
+
+ /* Figure out where to place the text */
+ y = (y * widget->angband.fontheight + widget->angband.fontascent +
+ widget->angband.internal_border);
+ x = (x * widget->angband.fontwidth + widget->angband.internal_border);
+
+ /* Place the string */
+ XDrawImageString(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[a], x, y, txt, len);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Draw some graphical characters.
+ */
+static void AngbandOutputPict(AngbandWidget widget, int x, int y, int n,
+ const byte *ap, const char *cp, const byte *tap, const char *tcp,
+ const byte *eap, const char *ecp)
+{
+ int i, x1, y1;
+
+ byte a;
+ char c;
+
+ byte ta;
+ char tc;
+
+ int x2, y2;
+
+ byte ea;
+ char ec;
+
+ int x3, y3;
+ bool_ has_overlay;
+
+ int k, l;
+ unsigned long pixel, blank;
+
+ /* Figure out where to place the text */
+ y = (y * widget->angband.fontheight + widget->angband.internal_border);
+ x = (x * widget->angband.fontwidth + widget->angband.internal_border);
+
+ for (i = 0; i < n; ++i)
+ {
+ a = *ap++;
+ c = *cp++;
+
+ /* For extra speed - cache these values */
+ x1 = (c & 0x7F) * widget->angband.fontwidth;
+ y1 = (a & 0x7F) * widget->angband.fontheight;
+
+ ta = *tap++;
+ tc = *tcp++;
+
+ /* For extra speed - cache these values */
+ x2 = (tc & 0x7F) * widget->angband.fontwidth;
+ y2 = (ta & 0x7F) * widget->angband.fontheight;
+
+ ea = *eap++;
+ ec = *ecp++;
+ has_overlay = (ea && ec);
+
+ /* For extra speed -- cache these values */
+ x3 = (ec & 0x7F) * widget->angband.fontwidth;
+ y3 = (ea & 0x7F) * widget->angband.fontheight;
+
+ /* Optimise the common case */
+ if ((x1 == x2) && (y1 == y2))
+ {
+
+ /* No overlay */
+ if (!has_overlay)
+ {
+ /* Draw object / terrain */
+ XPutImage(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[0],
+ widget->angband.tiles,
+ x1, y1,
+ x, y,
+ widget->angband.fontwidth,
+ widget->angband.fontheight);
+ }
+
+ /* Terrain overlay */
+ else
+ {
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(widget->angband.tiles,
+ 0, widget->angband.fontheight * 6);
+
+ for (k = 0; k < widget->angband.fontwidth; k++)
+ {
+ for (l = 0; l < widget->angband.fontheight; l++)
+ {
+ /* If mask set... */
+ if ((pixel = XGetPixel(widget->angband.tiles,
+ x3 + k, y3 + l)) == blank)
+ {
+ /* Output from the terrain */
+ pixel = XGetPixel(widget->angband.tiles,
+ x1 + k, y1 + l);
+ }
+
+ /* Store into the temp storage */
+ XPutPixel(widget->angband.TmpImage,
+ k, l, pixel);
+ }
+ }
+
+ /* Draw terrain + overlay */
+ XPutImage(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[0],
+ widget->angband.TmpImage,
+ 0, 0,
+ x, y,
+ widget->angband.fontwidth,
+ widget->angband.fontheight);
+ }
+
+ }
+ else
+ {
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(widget->angband.tiles,
+ 0, widget->angband.fontheight * 6);
+
+ for (k = 0; k < widget->angband.fontwidth; k++)
+ {
+ for (l = 0; l < widget->angband.fontheight; l++)
+ {
+ /* Get overlay pixel */
+ if (has_overlay)
+ {
+ pixel = XGetPixel(widget->angband.tiles,
+ x3 + k, y3 + l);
+ }
+
+ /* Hack -- no overlay */
+ else
+ {
+ pixel = blank;
+ }
+
+ /* If it's blank */
+ if (pixel == blank)
+ {
+ /* Use obj/mon */
+ pixel = XGetPixel(widget->angband.tiles,
+ x1 + k, y1 + l);
+ }
+
+ /* Use terrain if it's blank too */
+ if (pixel == blank)
+ {
+ pixel = XGetPixel(widget->angband.tiles,
+ x2 + k, y2 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(widget->angband.TmpImage,
+ k, l, pixel);
+ }
+ }
+
+ /* Draw to screen */
+
+ /* Draw object / terrain */
+ XPutImage(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[0],
+ widget->angband.TmpImage,
+ 0, 0,
+ x, y,
+ widget->angband.fontwidth,
+ widget->angband.fontheight);
+ }
+
+ x += widget->angband.fontwidth;
+ }
+}
+
+#endif /* USE_GRAPHICS */
+
+/*
+ * Private procedures
+ */
+
+
+/*
+ * Procedure Initialize() is called during the widget creation
+ * process. Initialize() load fonts and calculates window geometry.
+ * The request parameter is filled in by parents to this widget. The
+ * wnew parameter is the request parameter plus data filled in by this
+ * widget. All changes should be done to the wnew parameter.
+ */
+static void Initialize(AngbandWidget request, AngbandWidget wnew)
+{
+ Display *dpy = XtDisplay(wnew);
+
+ int depth = DefaultDepthOfScreen(XtScreen((Widget) wnew));
+
+ XGCValues gcv;
+ TopLevelShellWidget parent =
+ (TopLevelShellWidget)XtParent((Widget) wnew);
+ int i;
+
+ /* Default background pixel */
+ unsigned long bg = create_pixel(dpy,
+ angband_color_table[0][1],
+ angband_color_table[0][2],
+ angband_color_table[0][3]);
+
+ /* Default foreground pixel */
+ unsigned long fg = create_pixel(dpy,
+ angband_color_table[1][1],
+ angband_color_table[1][2],
+ angband_color_table[1][3]);
+
+ /* Fix the background color */
+ wnew->core.background_pixel = bg;
+
+ /* Get some information about the font */
+ wnew->angband.fnt = getFont(wnew, wnew->angband.font, TRUE);
+ wnew->angband.fontheight = wnew->angband.fnt->ascent +
+ wnew->angband.fnt->descent;
+ wnew->angband.fontwidth = wnew->angband.fnt->max_bounds.width;
+ wnew->angband.fontascent = wnew->angband.fnt->ascent;
+
+ /* Create and initialize the graphics contexts */ /* GXset? */
+ gcv.font = wnew->angband.fnt->fid;
+ gcv.graphics_exposures = FALSE;
+ gcv.background = bg;
+
+ for (i = 0; i < NUM_COLORS; i++)
+ {
+ unsigned long pixel;
+
+ /* Acquire Angband colors */
+ wnew->angband.color[i][0] = angband_color_table[i][0];
+ wnew->angband.color[i][1] = angband_color_table[i][1];
+ wnew->angband.color[i][2] = angband_color_table[i][2];
+ wnew->angband.color[i][3] = angband_color_table[i][3];
+
+ if (depth > 1)
+ {
+ /* Create pixel */
+ pixel = create_pixel(dpy,
+ wnew->angband.color[i][1],
+ wnew->angband.color[i][2],
+ wnew->angband.color[i][3]);
+ }
+ else
+ {
+ /* Use background or foreground */
+ pixel = ((i == 0) ? bg : fg);
+ }
+
+ gcv.foreground = pixel;
+
+ /* Copy */
+ gcv.function = 3;
+
+ wnew->angband.gc[i] = XtGetGC((Widget)wnew,
+ (GCFont | GCForeground | GCFunction |
+ GCBackground | GCGraphicsExposures),
+ &gcv);
+ }
+
+ /* Create a special GC for highlighting */
+ gcv.foreground = (BlackPixelOfScreen(XtScreen((Widget)wnew)) ^
+ WhitePixelOfScreen(XtScreen((Widget)wnew)));
+ gcv.background = 0;
+
+ gcv.function = GXxor;
+ wnew->angband.gc[COLOR_XOR] = XtGetGC((Widget)wnew,
+ (GCFunction | GCForeground | GCBackground |
+ GCGraphicsExposures),
+ &gcv);
+
+ /* Calculate window geometry */
+ wnew->core.height = (wnew->angband.start_rows * wnew->angband.fontheight +
+ 2 * wnew->angband.internal_border);
+ wnew->core.width = (wnew->angband.start_columns * wnew->angband.fontwidth +
+ 2 * wnew->angband.internal_border);
+
+ /* We need to be able to resize the Widget if the user wants to */
+ /* change font on the fly! */
+ parent->shell.allow_shell_resize = TRUE;
+
+ /* Calculates all the size hints */
+ calculateSizeHints(wnew);
+}
+
+
+/*
+ * Procedure Destroy() is called during the destruction of the widget.
+ * Destroy() releases and frees GCs, frees the pixmaps and frees the
+ * fonts.
+ */
+static void Destroy(AngbandWidget widget)
+{
+ int n;
+
+ /* Free all GC's */
+ for (n = 0; n < NUM_COLORS + 1; n++)
+ {
+ XtReleaseGC((Widget)widget, widget->angband.gc[n]);
+ }
+
+ /* Free the font */
+ XFreeFont(XtDisplay((Widget)widget), widget->angband.fnt);
+}
+
+
+static void Resize_term(AngbandWidget wnew)
+{
+ int cols, rows, wid, hgt;
+
+ int ox = wnew->angband.internal_border;
+ int oy = wnew->angband.internal_border;
+
+ int i;
+ term_data *old_td;
+ term_data *td = &data[0];
+
+
+ /*
+ * Mega-Hack -- avoid calling this before the terms package is
+ * initialised
+ */
+ if (!Term) return;
+
+ old_td = (term_data*)(Term->data);
+
+ /* Hack - Find the term to activate */
+ for (i = 0; i < num_term; i++)
+ {
+ td = &data[i];
+
+ /* Paranoia: none of the widgets matched */
+ if (!td) return;
+
+ /* Have we found it? */
+ if (td->widget == wnew) break;
+ }
+
+ /* Paranoia -- No matches */
+ if (i == num_term) return;
+
+ /* Activate the proper Term */
+ Term_activate(&td->t);
+
+ /* Determine "proper" number of rows/cols */
+ cols = ((wnew->core.width - (ox + ox)) / wnew->angband.fontwidth);
+ rows = ((wnew->core.height - (oy + oy)) / wnew->angband.fontheight);
+
+ /* Hack -- minimal size */
+ if (cols < 1) cols = 1;
+ if (rows < 1) rows = 1;
+
+ if (i == 0)
+ {
+ /* Hack the main window must be at least 80x24 */
+ if (cols < 80) cols = 80;
+ if (rows < 24) rows = 24;
+ }
+
+ /* Desired size of window */
+ wid = cols * wnew->angband.fontwidth + (ox + ox);
+ hgt = rows * wnew->angband.fontheight + (oy + oy);
+
+ /* Resize the Term (if needed) */
+ (void) Term_resize(cols, rows);
+
+ /* Activate the old term */
+ Term_activate(&old_td->t);
+}
+
+/*
+ * Procedure Redisplay() is called as the result of an Expose event.
+ * Use the redraw callback to do a full redraw
+ */
+static void Redisplay(AngbandWidget wnew, XEvent *xev, Region region)
+{
+ int x1, x2, y1, y2;
+
+ int i;
+
+ term_data *old_td = (term_data*)(Term->data);
+ term_data *td = &data[0];
+
+ /* Hack - Find the term to activate */
+ for (i = 0; i < num_term; i++)
+ {
+ td = &data[i];
+
+ /* Have we found it? */
+ if (td->widget == wnew) break;
+
+ /* Paranoia: none of the widgets matched */
+ if (!td) return;
+ }
+
+ /* Activate the proper Term */
+ Term_activate(&td->t);
+
+ /* Find the bounds of the exposed region */
+
+ /*
+ * This probably could be obtained from the Region parameter -
+ * but I don't know anything about XAW.
+ */
+ x1 = (xev->xexpose.x - wnew->angband.internal_border)
+ / wnew->angband.fontwidth;
+ x2 = (xev->xexpose.x + xev->xexpose.width -
+ wnew->angband.internal_border) / wnew->angband.fontwidth;
+
+ y1 = (xev->xexpose.y - wnew->angband.internal_border)
+ / wnew->angband.fontheight;
+ y2 = (xev->xexpose.y + xev->xexpose.height -
+ wnew->angband.internal_border) / wnew->angband.fontheight;
+
+ Term_redraw_section(x1, y1, x2, y2);
+
+ /* Activate the old term */
+ Term_activate(&old_td->t);
+
+
+#if 0
+ if (XtHasCallbacks((Widget)widget, XtNredrawCallback) == XtCallbackHasSome)
+ {
+ XtCallCallbacks((Widget)widget, XtNredrawCallback, NULL);
+ }
+#endif /* 0 */
+}
+
+
+/*
+ * Font and internal_border can be changed on the fly.
+ *
+ * The entire widget is redrawn if any of those parameters change (all
+ * can potentially have effects that spans the whole widget).
+ *
+ * Color changes are handled elsewhere.
+ *
+ * This function is very underspecified, in terms of how these changes can
+ * occur, and what is true about the various AngbandWidget's passed in. It
+ * is very likely that this code no longer works.
+ */
+static Boolean SetValues(AngbandWidget current, AngbandWidget request,
+ AngbandWidget wnew, ArgList args,
+ Cardinal *num_args)
+{
+ Display *dpy = XtDisplay(wnew);
+
+ Boolean font_changed = FALSE;
+ Boolean border_changed = FALSE;
+ int height, width;
+ int i;
+
+
+ /* Handle font change */
+ if (wnew->angband.font != current->angband.font)
+ {
+ /* Check if the font exists */
+ wnew->angband.fnt = getFont(wnew, wnew->angband.font, FALSE);
+
+ /* The font didn't exist */
+ if (wnew->angband.fnt == NULL)
+ {
+ wnew->angband.fnt = current->angband.fnt;
+ wnew->angband.font = current->angband.font;
+ XtWarning("Couldn't find the requested font!");
+ }
+ else
+ {
+ font_changed = TRUE;
+
+ /* Free the old font */
+ XFreeFont(XtDisplay((Widget)wnew), current->angband.fnt);
+ /* Update font information */
+ wnew->angband.fontheight = wnew->angband.fnt->ascent +
+ wnew->angband.fnt->descent;
+ wnew->angband.fontwidth = wnew->angband.fnt->max_bounds.width;
+ wnew->angband.fontascent = wnew->angband.fnt->ascent;
+ }
+ }
+
+ /* Handle font change */
+ if (font_changed)
+ {
+ /* Update all GC's */
+ for (i = 0; i < NUM_COLORS; i++)
+ {
+ /* Steal the old GC */
+ wnew->angband.gc[i] = current->angband.gc[i];
+ current->angband.gc[i] = NULL;
+
+ /* Be sure the correct font is ready */
+ XSetFont(dpy, wnew->angband.gc[i], wnew->angband.fnt->fid);
+ }
+
+ /* Steal the old GC */
+ wnew->angband.gc[NUM_COLORS] = current->angband.gc[NUM_COLORS];
+ current->angband.gc[NUM_COLORS] = NULL;
+ }
+
+
+ /* Check if internal border width has changed, used later */
+ if (current->angband.internal_border != wnew->angband.internal_border)
+ {
+ border_changed = TRUE;
+ }
+
+
+ /* If the font or the internal border has changed, all geometry */
+ /* has to be recalculated */
+ if (font_changed || border_changed)
+ {
+ /* Change window size */
+ height = ((current->core.height - 2 * current->angband.internal_border) /
+ current->angband.fontheight * wnew->angband.fontheight +
+ 2 * current->angband.internal_border);
+ width = ((current->core.width - 2 * current->angband.internal_border) /
+ current->angband.fontwidth * wnew->angband.fontwidth +
+ 2 * wnew->angband.internal_border);
+
+ /* Get the new width */
+ if (XtMakeResizeRequest((Widget)wnew, width, height, NULL, NULL) ==
+ XtGeometryNo)
+ {
+ /* Not allowed */
+ XtWarning("Size change denied!");
+ }
+ else
+ {
+ /* Recalculate size hints */
+ calculateSizeHints(wnew);
+ }
+ }
+
+ /* Tell it to redraw the widget if anything has changed */
+ return (font_changed || border_changed);
+}
+
+
+/*
+ * Calculate size hints
+ */
+static void calculateSizeHints(AngbandWidget wnew)
+{
+ TopLevelShellWidget parent =
+ (TopLevelShellWidget)XtParent((Widget) wnew);
+
+ /* Calculate minimum size */
+ parent->wm.size_hints.min_height =
+ (wnew->angband.min_rows * wnew->angband.fontheight +
+ 2 * wnew->angband.internal_border);
+
+ /* Calculate minimum size */
+ parent->wm.size_hints.min_width =
+ (wnew->angband.min_columns * wnew->angband.fontwidth +
+ 2 * wnew->angband.internal_border);
+
+ /* Calculate minimum size */
+ parent->wm.size_hints.flags |= PMinSize;
+
+ /* Calculate maximum size */
+ parent->wm.size_hints.max_height =
+ (wnew->angband.max_rows * wnew->angband.fontheight +
+ 2 * wnew->angband.internal_border);
+
+ /* Calculate maximum size */
+ parent->wm.size_hints.max_width =
+ (wnew->angband.max_columns * wnew->angband.fontwidth +
+ 2 * wnew->angband.internal_border);
+
+ /* Calculate maximum size */
+ parent->wm.size_hints.flags |= PMaxSize;
+
+ /* Calculate increment size */
+ parent->wm.size_hints.height_inc = wnew->angband.fontheight;
+ parent->wm.size_hints.width_inc = wnew->angband.fontwidth;
+ parent->wm.size_hints.flags |= PResizeInc;
+
+ /* Calculate base size */
+ parent->wm.base_height = 2 * wnew->angband.internal_border;
+ parent->wm.base_width = 2 * wnew->angband.internal_border;
+ parent->wm.size_hints.flags |= PBaseSize;
+}
+
+
+/*
+ * Load a font
+ */
+static XFontStruct *getFont(AngbandWidget widget,
+ String font, Boolean fallback)
+{
+ Display *dpy = XtDisplay((Widget) widget);
+ char buf[256];
+ XFontStruct *fnt = NULL;
+
+ if (!(fnt = XLoadQueryFont(dpy, font)) && fallback)
+ {
+ sprintf(buf, "Can't find the font \"%s\", trying fixed\n", font);
+ XtWarning(buf);
+ if (!(fnt = XLoadQueryFont(dpy, "fixed")))
+ {
+ XtError("Can't fint the font \"fixed\"!, bailing out\n");
+ }
+ }
+
+ return fnt;
+}
+
+
+
+/*** The Angband code ****/
+
+
+
+
+
+/*
+ * Number of fallback resources per window
+ */
+#define TERM_FALLBACKS 6
+
+
+
+/*
+ * The names of the term_data's
+ */
+char *termNames[MAX_TERM_DATA] =
+{
+ "angband",
+ "mirror",
+ "recall",
+ "choice",
+ "term-4",
+ "term-5",
+ "term-6",
+ "term-7"
+};
+
+
+/*
+ * The special Arg's
+ */
+Arg specialArgs[TERM_FALLBACKS] =
+{
+ { XtNstartRows, 24},
+ { XtNstartColumns, 80},
+ { XtNminRows, 24},
+ { XtNminColumns, 80},
+ { XtNmaxRows, 255},
+ { XtNmaxColumns, 255}
+};
+
+
+/*
+ * The default Arg's
+ */
+Arg defaultArgs[TERM_FALLBACKS] =
+{
+ { XtNstartRows, 24},
+ { XtNstartColumns, 80},
+ { XtNminRows, 1},
+ { XtNminColumns, 1},
+ { XtNmaxRows, 255},
+ { XtNmaxColumns, 255}
+};
+
+
+/*
+ * The application context
+ */
+XtAppContext appcon;
+
+
+/*
+ * User changable information about widgets
+ */
+static String fallback[] =
+{
+ "Angband.angband.iconName: ToME",
+ "Angband.angband.title: ToME",
+ "Angband.term-1.iconName: Mirror",
+ "Angband.term-1.title: Mirror",
+ "Angband.term-2.iconName: Recall",
+ "Angband.term-2.title: Recall",
+ "Angband.term-3.iconName: Choice",
+ "Angband.term-3.title: Choice",
+ "Angband.term-4.iconName: Term 4",
+ "Angband.term-4.title: Term 4",
+ "Angband.term-5.iconName: Term 5",
+ "Angband.term-5.title: Term 5",
+ "Angband.term-6.iconName: Term 6",
+ "Angband.term-6.title: Term 6",
+ "Angband.term-7.iconName: Term 7",
+ "Angband.term-7.title: Term 7",
+ NULL
+};
+
+
+
+/*
+ * Do a redraw
+ */
+static void react_redraw(Widget widget,
+ XtPointer client_data, XtPointer call_data)
+{
+ term_data *old_td = (term_data*)(Term->data);
+ term_data *td = (term_data*)client_data;
+
+ /* Activate the proper Term */
+ Term_activate(&td->t);
+
+ /* Request a redraw */
+ Term_redraw();
+
+ /* Activate the old Term */
+ Term_activate(&old_td->t);
+}
+
+
+
+/*
+ * Process a keypress event
+ *
+ * Also appears in "main-x11.c".
+ */
+static void react_keypress(XKeyEvent *xev)
+{
+ int i, n, mc, ms, mo, mx;
+
+ uint ks1;
+
+ XKeyEvent *ev = (XKeyEvent*)(xev);
+
+ KeySym ks;
+
+ char buf[128];
+ char msg[128];
+
+
+ /* Check for "normal" keypresses */
+ n = XLookupString(ev, buf, 125, &ks, NULL);
+
+ /* Terminate */
+ buf[n] = '\0';
+
+
+ /* Hack -- Ignore "modifier keys" */
+ if (IsModifierKey(ks)) return;
+
+
+ /* Hack -- convert into an unsigned int */
+ ks1 = (uint)(ks);
+
+ /* Extract four "modifier flags" */
+ mc = (ev->state & ControlMask) ? TRUE : FALSE;
+ ms = (ev->state & ShiftMask) ? TRUE : FALSE;
+ mo = (ev->state & Mod1Mask) ? TRUE : FALSE;
+ mx = (ev->state & Mod2Mask) ? TRUE : FALSE;
+
+
+ /* Normal keys with no modifiers */
+ if (n && !mo && !mx && !IsSpecialKey(ks))
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; buf[i]; i++) Term_keypress(buf[i]);
+
+ /* All done */
+ return;
+ }
+
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch (ks1)
+ {
+ case XK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return;
+ }
+
+ case XK_Return:
+ {
+ Term_keypress('\r');
+ return;
+ }
+
+ case XK_Tab:
+ {
+ Term_keypress('\t');
+ return;
+ }
+
+ case XK_Delete:
+ case XK_BackSpace:
+ {
+ Term_keypress('\010');
+ return;
+ }
+ }
+
+
+ /* Hack -- Use the KeySym */
+ if (ks)
+ {
+ sprintf(msg, "%c%s%s%s%s_%lX%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ (unsigned long)(ks), 13);
+ }
+
+ /* Hack -- Use the Keycode */
+ else
+ {
+ sprintf(msg, "%c%s%s%s%sK_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ ev->keycode, 13);
+ }
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+
+ /* Hack -- auto-define macros as needed */
+ if (n && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, buf);
+ }
+}
+
+
+/*
+ * Handle an event
+ */
+static void handle_event(Widget widget, XtPointer client_data, XEvent *event,
+ Boolean *continue_to_dispatch)
+{
+ term_data *old_td = (term_data*)(Term->data);
+ term_data *td = (term_data *)client_data;
+
+ /* Continue to process the event by default */
+ *continue_to_dispatch = TRUE;
+
+ /* Activate the Term */
+ Term_activate(&td->t);
+
+ switch (event->type)
+ {
+ case KeyPress:
+ {
+ /* Hack -- use old term */
+ Term_activate(&old_td->t);
+
+ /* Handle the keypress */
+ react_keypress(&(event->xkey));
+
+ /* We took care of the event */
+ *continue_to_dispatch = FALSE;
+
+ break;
+ }
+
+ /* Oops */
+ default:
+ {
+ break;
+ }
+ }
+
+ /* Activate the old term */
+ Term_activate(&old_td->t);
+
+ return;
+}
+
+
+/*
+ * Process an event (or just check for one)
+ */
+errr CheckEvent(bool_ wait)
+{
+ XEvent event;
+
+ /* No events ready, and told to just check */
+ if (!wait && !XtAppPending(appcon)) return 1;
+
+ /* Process */
+ while (1)
+ {
+ XtAppNextEvent(appcon, &event);
+ XtDispatchEvent(&event);
+ if (!XtAppPending(appcon)) break;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Monstrous hack.
+ */
+static void Term_xtra_xaw_react_aux(term_data *td)
+{
+ AngbandWidget wnew = td->widget;
+
+ Display *dpy = XtDisplay((Widget) wnew);
+
+ int depth = DefaultDepthOfScreen(XtScreen((Widget) wnew));
+
+ int i;
+
+ /* See if any colors need to be changed */
+ for (i = 0; i < NUM_COLORS; i++)
+ {
+ if (depth > 1)
+ {
+ if ((wnew->angband.color[i][0] != angband_color_table[i][0]) ||
+ (wnew->angband.color[i][1] != angband_color_table[i][1]) ||
+ (wnew->angband.color[i][2] != angband_color_table[i][2]) ||
+ (wnew->angband.color[i][3] != angband_color_table[i][3]))
+ {
+ unsigned long pixel;
+
+ /* Save new values */
+ wnew->angband.color[i][0] = angband_color_table[i][0];
+ wnew->angband.color[i][1] = angband_color_table[i][1];
+ wnew->angband.color[i][2] = angband_color_table[i][2];
+ wnew->angband.color[i][3] = angband_color_table[i][3];
+
+ /* Create pixel */
+ pixel = create_pixel(dpy,
+ wnew->angband.color[i][1],
+ wnew->angband.color[i][2],
+ wnew->angband.color[i][3]);
+
+
+ /* Change */
+ XSetForeground(dpy, wnew->angband.gc[i], pixel);
+ }
+ }
+ }
+}
+
+
+/*
+ * Monstrous hack.
+ */
+static errr Term_xtra_xaw_react(void)
+{
+ int i;
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ if (!td) break;
+
+ Term_xtra_xaw_react_aux(td);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_xaw(int n, int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ Widget widget = (Widget)(td->widget);
+
+ Display *dpy = XtDisplay(widget);
+
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ XBell(dpy, 100);
+ return (0);
+
+ /* Flush the output */
+ case TERM_XTRA_FRESH:
+ XFlush(dpy);
+ /* Allow flushed events to be showed */
+ CheckEvent(FALSE);
+ return (0);
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ return (CheckEvent(0));
+
+ /* Process events */
+ case TERM_XTRA_EVENT:
+ return (CheckEvent(v));
+
+ /* Flush events */
+ case TERM_XTRA_FLUSH:
+ while (!CheckEvent(FALSE));
+ return (0);
+
+ /* Clear the window */
+ case TERM_XTRA_CLEAR:
+ XClearWindow(dpy, XtWindow(widget));
+ return (0);
+
+ /* Delay */
+ case TERM_XTRA_DELAY:
+ usleep(1000 * v);
+ return (0);
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while (entry = readdir(directory))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+ }
+
+ case TERM_XTRA_REACT:
+ return (Term_xtra_xaw_react());
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+
+/*
+ * Erase a number of characters
+ */
+static errr Term_wipe_xaw(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Erase using color 0 */
+ AngbandClearArea(td->widget, x, y, n, 1, 0);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Draw the cursor, by hiliting with XOR
+ *
+ * Should perhaps use rectangle outline, ala "main-mac.c". XXX XXX XXX
+ */
+static errr Term_curs_xaw(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Hilite the cursor character */
+ AngbandClearArea(td->widget, x, y, 1, 1, COLOR_XOR);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw a number of characters
+ */
+static errr Term_text_xaw(int x, int y, int n, byte a, cptr s)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Draw the text */
+ AngbandOutputText(td->widget, x, y, (String)s, n, a);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Draw some graphical characters.
+ */
+static errr Term_pict_xaw(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Draw the pictures */
+ AngbandOutputPict(td->widget, x, y, n, ap, cp, tap, tcp, eap, ecp);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Raise a term
+ */
+static void term_raise(term_data *td)
+{
+ Widget widget = (Widget)(td->widget);
+
+ XRaiseWindow(XtDisplay(XtParent(widget)), XtWindow(XtParent(widget)));
+}
+
+
+/*
+ * Initialize a term_data
+ */
+static errr term_data_init(term_data *td, Widget topLevel,
+ int key_buf, String name,
+ ArgList widget_arg, Cardinal widget_arg_no, int i)
+{
+ Widget parent;
+ term *t = &td->t;
+
+ int cols = 80;
+ int rows = 24;
+
+ char buf[80];
+ cptr str;
+
+ int val;
+
+ /* Create the shell widget */
+ parent = XtCreatePopupShell(name, topLevelShellWidgetClass, topLevel,
+ NULL, 0);
+
+ /* Window specific cols */
+ sprintf(buf, "ANGBAND_X11_COLS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) cols = val;
+
+ /* Window specific rows */
+ sprintf(buf, "ANGBAND_X11_ROWS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) rows = val;
+
+ /* Hack the main window must be at least 80x24 */
+ if (i == 0)
+ {
+ if (cols < 80) cols = 80;
+ if (rows < 24) rows = 24;
+ }
+
+ /* Reset the initial size */
+ widget_arg[0].value = rows;
+ widget_arg[1].value = cols;
+
+ /* Create the interior widget */
+ td->widget = (AngbandWidget)
+ XtCreateManagedWidget(name, angbandWidgetClass,
+ parent, widget_arg, widget_arg_no);
+
+ /* Initialize the term (full size) */
+ term_init(t, cols, rows, key_buf);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_xaw;
+ t->curs_hook = Term_curs_xaw;
+ t->wipe_hook = Term_wipe_xaw;
+ t->text_hook = Term_text_xaw;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Register the keypress event handler */
+ XtAddEventHandler((Widget)td->widget, KeyPressMask,
+ False, (XtEventHandler) handle_event, td);
+
+ /* Redraw callback */
+ XtAddCallback((Widget)td->widget, XtNredrawCallback,
+ react_redraw, td);
+
+ /* Realize the widget */
+ XtRealizeWidget(parent);
+
+ /* Make it visible */
+ XtPopup(parent, XtGrabNone);
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ Resize_term(td->widget);
+
+ return 0;
+}
+
+
+/*
+ * Initialization function for an X Athena Widget module to Angband
+ *
+ * We should accept "-d<dpy>" requests in the "argv" array. XXX XXX XXX
+ */
+errr init_xaw(int argc, char *argv[])
+{
+ int i;
+ Widget topLevel;
+ Display *dpy;
+
+ cptr dpy_name = "";
+
+
+#ifdef USE_GRAPHICS
+
+ char filename[1024];
+
+ int pict_wid = 0;
+ int pict_hgt = 0;
+ bool_ force_old_graphics = FALSE;
+
+ char *TmpData;
+
+#endif /* USE_GRAPHICS */
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ if (prefix(argv[i], "-d"))
+ {
+ dpy_name = &argv[i][2];
+ continue;
+ }
+
+#ifdef USE_GRAPHICS
+
+ if (prefix(argv[i], "-s"))
+ {
+ smoothRescaling = FALSE;
+ continue;
+ }
+
+ if (prefix(argv[i], "-o"))
+ {
+ force_old_graphics = TRUE;
+ continue;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ if (prefix(argv[i], "-n"))
+ {
+ num_term = atoi(&argv[i][2]);
+ if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA;
+ else if (num_term < 1) num_term = 1;
+ continue;
+ }
+
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+
+ /* Attempt to open the local display */
+ dpy = XOpenDisplay(dpy_name);
+
+ /* Failure -- assume no X11 available */
+ if (!dpy) return ( -1);
+
+ /* Close the local display */
+ XCloseDisplay(dpy);
+
+
+#ifdef USE_XAW_LANG
+
+ /* Support locale processing */
+ XtSetLanguageProc(NULL, NULL, NULL);
+
+#endif /* USE_XAW_LANG */
+
+
+ /* Initialize the toolkit */
+ topLevel = XtAppInitialize(&appcon, "Angband", NULL, 0, &argc, argv,
+ fallback, NULL, 0);
+
+ XSetIOErrorHandler(x_io_error_handler);
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ term_data_init(td, topLevel, 1024, termNames[i],
+ (i == 0) ? specialArgs : defaultArgs,
+ TERM_FALLBACKS, i);
+
+ angband_term[i] = Term;
+ }
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+ /* Raise the "Angband" window */
+ term_raise(&data[0]);
+
+
+#ifdef USE_GRAPHICS
+
+ /* Try graphics */
+ if (arg_graphics)
+ {
+ /* Try the "16x16.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/16x16.bmp");
+
+ /* Use the "16x16.bmp" file if it exists */
+ if (!force_old_graphics &&
+ (0 == fd_close(fd_open(filename, O_RDONLY))))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 16;
+
+ ANGBAND_GRAF = "new";
+ }
+ else
+ {
+ /* Try the "8x8.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/8x8.bmp");
+
+ /* Use the "8x8.bmp" file if it exists */
+ if (0 == fd_close(fd_open(filename, O_RDONLY)))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 8;
+
+ ANGBAND_GRAF = "old";
+ }
+ }
+ }
+
+ /* Load graphics */
+ if (use_graphics)
+ {
+ /* Hack -- Get the Display */
+ term_data *td = &data[0];
+ Widget widget = (Widget)(td->widget);
+ Display *dpy = XtDisplay(widget);
+
+ XImage *tiles_raw;
+
+ /* Load the graphical tiles */
+ tiles_raw = ReadBMP(dpy, filename);
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ term *t = &td->t;
+
+ t->pict_hook = Term_pict_xaw;
+
+ t->higher_pict = TRUE;
+
+ /* Resize tiles */
+ td->widget->angband.tiles =
+ ResizeImage(dpy, tiles_raw,
+ pict_wid, pict_hgt,
+ td->widget->angband.fontwidth,
+ td->widget->angband.fontheight);
+ }
+
+ /* Initialize the transparency temp storage*/
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+ int ii, jj;
+ int depth = DefaultDepth(dpy, DefaultScreen(dpy));
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+ int total;
+
+
+ /* Determine total bytes needed for image */
+ ii = 1;
+ jj = (depth - 1) >> 2;
+ while (jj >>= 1) ii <<= 1;
+ total = td->widget->angband.fontwidth *
+ td->widget->angband.fontheight * ii;
+
+
+ TmpData = (char *)malloc(total);
+
+ td->widget->angband.TmpImage = XCreateImage(dpy,
+ visual, depth,
+ ZPixmap, 0, TmpData,
+ td->widget->angband.fontwidth,
+ td->widget->angband.fontheight, 8, 0);
+
+ }
+
+
+ /* Free tiles_raw? XXX XXX */
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_XAW */
+
diff --git a/src/main-xxx.c b/src/main-xxx.c
new file mode 100644
index 00000000..c80f01f0
--- /dev/null
+++ b/src/main-xxx.c
@@ -0,0 +1,785 @@
+/* File: main-xxx.c */
+
+/* Purpose: Sample visual module for Angband 2.8.1 */
+
+/*
+ * This file written by "Ben Harrison (benh@phial.com)".
+ *
+ * This file is intended to show one way to build a "visual module"
+ * for Angband to allow it to work with a new system. It does not
+ * actually work, but if the code near "XXX XXX XXX" comments were
+ * replaced with functional code, then it probably would.
+ *
+ * See "z-term.c" for info on the concept of the "generic terminal",
+ * and for more comments about what this file must supply.
+ *
+ * There are two basic ways to port Angband to a new system. The
+ * first involves modifying the "main-gcu.c" and/or "main-x11.c"
+ * files to support some version of "curses" and/or "X11" on your
+ * machine, and to compile with the "USE_GCU" and/or "USE_X11"
+ * compilation flags defined. The second involves creating a
+ * new "main-xxx.c" file, based on this sample file (or on any
+ * existing "main-xxx.c" file), and comes in two flavors, based
+ * on whether it contains a "main()" function (as in "main-mac.c"
+ * and "main-win.c") or not (as in "main-gcu.c" or "main-x11.c").
+ *
+ * If the "main-xxx.c" file includes its own "main()" function,
+ * then you should NOT link in the "main.c" file, and your "main()"
+ * function must process any command line arguments, initialize the
+ * "visual system", and call "play_game()" with appropriate arguments.
+ *
+ * If the "main-xxx.c" file does not include its own "main()"
+ * function, then you must add some code to "main.c" which, if
+ * the appropriate "USE_XXX" compilation flag is defined, will
+ * attempt to call the "init_xxx()" function in the "main-xxx.c"
+ * file, which should initialize the "visual system" and return
+ * zero if it was successful. The "main()" function in "main.c"
+ * will take care of processing command line arguments and then
+ * calling "play_game()" with appropriate arguments.
+ *
+ * Note that the "util.c" file often contains functions which must
+ * be modified in small ways for various platforms, even if you are
+ * able to use the existing "main-gcu.c" and/or "main-x11.c" files,
+ * in particular, the "file handling" functions may not work on all
+ * systems.
+ *
+ * When you complete a port to a new system, you should email any
+ * newly created files, and any changes made to existing files,
+ * including "h-config.h", "config.h", and any of the "Makefile"
+ * files, to "benh@phial.com" for inclusion in the next version.
+ *
+ * Try to stick to a "three letter" naming scheme for "main-xxx.c"
+ * and "Makefile.xxx" and such for consistency and simplicity.
+ */
+
+
+#include "angband.h"
+
+
+#ifdef USE_XXX
+
+
+/*
+ * Extra data to associate with each "window"
+ *
+ * Each "window" is represented by a "term_data" structure, which
+ * contains a "term" structure, which contains a pointer (t->data)
+ * back to the term_data structure.
+ */
+
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t;
+
+ /* Other fields if needed XXX XXX XXX */
+};
+
+
+
+/*
+ * Number of "term_data" structures to support XXX XXX XXX
+ *
+ * You MUST support at least one "term_data" structure, and the
+ * game will currently use up to eight "term_data" structures if
+ * they are available.
+ *
+ * If only one "term_data" structure is supported, then a lot of
+ * the things that would normally go into a "term_data" structure
+ * could be made into global variables instead.
+ */
+#define MAX_TERM_DATA 1
+
+
+/*
+ * An array of "term_data" structures, one for each "sub-window"
+ */
+static term_data data[MAX_TERM_DATA];
+
+
+#if 0 /* Fix the syntax below XXX XXX XXX */
+
+/*
+ * The "color" array for the visual module XXX XXX XXX
+ *
+ * This table should be used in whetever way is necessary to
+ * convert the Angband Color Indexes into the proper "color data"
+ * for the visual system. On the Macintosh, these are arrays of
+ * three shorts, on the IBM, these are combinations of the eight
+ * basic color codes with optional "bright" bits, on X11, these
+ * are actual "pixel" codes extracted from another table which
+ * contains textual color names.
+ *
+ * The Angband Color Set (0 to 15):
+ * Black, White, Slate, Orange, Red, Blue, Green, Umber
+ * D-Gray, L-Gray, Violet, Yellow, L-Red, L-Blue, L-Green, L-Umber
+ *
+ * Colors 8 to 15 are basically "enhanced" versions of Colors 0 to 7.
+ *
+ * As decribed in one of the header files, in a perfect world, the
+ * colors below should fit a nice clean "quartered" specification
+ * in RGB codes, but this must often be Gamma Corrected. The 1/4
+ * parts of each Red,Green,Blue are shown in the comments below,
+ * again, these values are *before* gamma correction.
+ */
+static local_color_data_type color_data[16] =
+{
+ /* XXX XXX XXX 0,0,0 */, /* TERM_DARK */
+ /* XXX XXX XXX 4,4,4 */, /* TERM_WHITE */
+ /* XXX XXX XXX 2,2,2 */, /* TERM_SLATE */
+ /* XXX XXX XXX 4,2,0 */, /* TERM_ORANGE */
+ /* XXX XXX XXX 3,0,0 */, /* TERM_RED */
+ /* XXX XXX XXX 0,2,1 */, /* TERM_GREEN */
+ /* XXX XXX XXX 0,0,4 */, /* TERM_BLUE */
+ /* XXX XXX XXX 2,1,0 */, /* TERM_UMBER */
+ /* XXX XXX XXX 1,1,1 */, /* TERM_L_DARK */
+ /* XXX XXX XXX 3,3,3 */, /* TERM_L_WHITE */
+ /* XXX XXX XXX 4,0,4 */, /* TERM_VIOLET */
+ /* XXX XXX XXX 4,4,0 */, /* TERM_YELLOW */
+ /* XXX XXX XXX 4,0,0 */, /* TERM_L_RED */
+ /* XXX XXX XXX 0,4,0 */, /* TERM_L_GREEN */
+ /* XXX XXX XXX 0,4,4 */, /* TERM_L_BLUE */
+ /* XXX XXX XXX 3,2,1 */ /* TERM_L_UMBER */
+};
+
+#endif
+
+
+
+/*** Function hooks needed by "Term" ***/
+
+
+/*
+ * Init a new "term"
+ *
+ * This function should do whatever is necessary to prepare a new "term"
+ * for use by the "term.c" package. This may include clearing the window,
+ * preparing the cursor, setting the font/colors, etc. Usually, this
+ * function does nothing, and the "init_xxx()" function does it all.
+ */
+static void Term_init_xxx(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+
+ /* XXX XXX XXX */
+}
+
+
+
+/*
+ * Nuke an old "term"
+ *
+ * This function is called when an old "term" is no longer needed. It should
+ * do whatever is needed to clean up before the program exits, such as wiping
+ * the screen, restoring the cursor, fixing the font, etc. Often this function
+ * does nothing and lets the operating system clean up when the program quits.
+ */
+static void Term_nuke_xxx(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+
+ /* XXX XXX XXX */
+}
+
+
+
+/*
+ * Do a "user action" on the current "term"
+ *
+ * This function allows the visual module to do implementation defined
+ * things when the user activates the "system defined command" command.
+ *
+ * This function is normally not used.
+ *
+ * In general, this function should return zero if the action is successfully
+ * handled, and non-zero if the action is unknown or incorrectly handled.
+ */
+static errr Term_user_xxx(int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Unknown */
+ return (1);
+}
+
+
+/*
+ * Do a "special thing" to the current "term"
+ *
+ * This function must react to a large number of possible arguments, each
+ * corresponding to a different "action request" by the "z-term.c" package,
+ * or by the application itself.
+ *
+ * The "action type" is specified by the first argument, which must be a
+ * constant of the form "TERM_XTRA_*" as given in "term.h", and the second
+ * argument specifies the "information" for that argument, if any, and will
+ * vary according to the first argument.
+ *
+ * In general, this function should return zero if the action is successfully
+ * handled, and non-zero if the action is unknown or incorrectly handled.
+ */
+static errr Term_xtra_xxx(int n, int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Analyze */
+ switch (n)
+ {
+ case TERM_XTRA_EVENT:
+ {
+ /*
+ * Process some pending events XXX XXX XXX
+ *
+ * Wait for at least one event if "v" is non-zero
+ * otherwise, if no events are ready, return at once.
+ * When "keypress" events are encountered, the "ascii"
+ * value corresponding to the key should be sent to the
+ * "Term_keypress()" function. Certain "bizarre" keys,
+ * such as function keys or arrow keys, may send special
+ * sequences of characters, such as control-underscore,
+ * plus letters corresponding to modifier keys, plus an
+ * underscore, plus carriage return, which can be used by
+ * the main program for "macro" triggers. This action
+ * should handle as many events as is efficiently possible
+ * but is only required to handle a single event, and then
+ * only if one is ready or "v" is true.
+ *
+ * This action is required.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_FLUSH:
+ {
+ /*
+ * Flush all pending events XXX XXX XXX
+ *
+ * This action should handle all events waiting on the
+ * queue, optionally discarding all "keypress" events,
+ * since they will be discarded anyway in "z-term.c".
+ *
+ * This action is required, but may not be "essential".
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_CLEAR:
+ {
+ /*
+ * Clear the entire window XXX XXX XXX
+ *
+ * This action should clear the entire window, and redraw
+ * any "borders" or other "graphic" aspects of the window.
+ *
+ * This action is required.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_SHAPE:
+ {
+ /*
+ * Set the cursor visibility XXX XXX XXX
+ *
+ * This action should change the visibility of the cursor,
+ * if possible, to the requested value (0=off, 1=on)
+ *
+ * This action is optional, but can improve both the
+ * efficiency (and attractiveness) of the program.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_FROSH:
+ {
+ /*
+ * Flush a row of output XXX XXX XXX
+ *
+ * This action should make sure that row "v" of the "output"
+ * to the window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FRESH" entry below takes care of any
+ * necessary flushing issues.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_FRESH:
+ {
+ /*
+ * Flush output XXX XXX XXX
+ *
+ * This action should make sure that all "output" to the
+ * window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FROSH" entry above takes care of any
+ * necessary flushing issues.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_NOISE:
+ {
+ /*
+ * Make a noise XXX XXX XXX
+ *
+ * This action should produce a "beep" noise.
+ *
+ * This action is optional, but convenient.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_SOUND:
+ {
+ /*
+ * Make a sound XXX XXX XXX
+ *
+ * This action should produce sound number "v", where the
+ * "name" of that sound is "sound_names[v]". This method
+ * is still under construction.
+ *
+ * This action is optional, and not very important.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_BORED:
+ {
+ /*
+ * Handle random events when bored XXX XXX XXX
+ *
+ * This action is optional, and normally not important
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_REACT:
+ {
+ /*
+ * React to global changes XXX XXX XXX
+ *
+ * For example, this action can be used to react to
+ * changes in the global "color_table[256][4]" array.
+ *
+ * This action is optional, but can be very useful for
+ * handling "color changes" and the "arg_sound" and/or
+ * "arg_graphics" options.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_ALIVE:
+ {
+ /*
+ * Change the "hard" level XXX XXX XXX
+ *
+ * This action is used if the program changes "aliveness"
+ * by being either "suspended" (v=0) or "resumed" (v=1)
+ * This action is optional, unless the computer uses the
+ * same "physical screen" for multiple programs, in which
+ * case this action should clean up to let other programs
+ * use the screen, or resume from such a cleaned up state.
+ *
+ * This action is currently only used by "main-gcu.c",
+ * on UNIX machines, to allow proper "suspending".
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_LEVEL:
+ {
+ /*
+ * Change the "soft" level XXX XXX XXX
+ *
+ * This action is used when the term window changes "activation"
+ * either by becoming "inactive" (v=0) or "active" (v=1)
+ *
+ * This action can be used to do things like activate the proper
+ * font / drawing mode for the newly active term window. This
+ * action should NOT change which window has the "focus", which
+ * window is "raised", or anything like that.
+ *
+ * This action is optional if all the other things which depend
+ * on what term is active handle activation themself, or if only
+ * one "term_data" structure is supported by this file.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_DELAY:
+ {
+ /*
+ * Delay for some milliseconds XXX XXX XXX
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as breath attacks.
+ *
+ * This action is optional, but may be required by this file,
+ * especially if special "macro sequences" must be supported.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_GET_DELAY:
+ {
+ /*
+ * Get Delay of some milliseconds XXX XXX XXX
+ * place the result in Term_xtra_long
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as recording cmovies.
+ *
+ * This action is optional, but cmovies wont perform
+ * good without it
+ */
+
+ return (0);
+ }
+ }
+
+ /* Unknown or Unhandled action */
+ return (1);
+}
+
+
+/*
+ * Display the cursor
+ *
+ * This routine should display the cursor at the given location
+ * (x,y) in some manner. On some machines this involves actually
+ * moving the physical cursor, on others it involves drawing a fake
+ * cursor in some form of graphics mode. Note the "soft_cursor"
+ * flag which tells "z-term.c" to treat the "cursor" as a "visual"
+ * thing and not as a "hardware" cursor.
+ *
+ * You may assume "valid" input if the window is properly sized.
+ *
+ * You may use the "Term_grab(x, y, &a, &c)" function, if needed,
+ * to determine what attr/char should be "under" the new cursor,
+ * for "inverting" purposes or whatever.
+ */
+static errr Term_curs_xxx(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase some characters
+ *
+ * This function should erase "n" characters starting at (x,y).
+ *
+ * You may assume "valid" input if the window is properly sized.
+ */
+static errr Term_wipe_xxx(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some text on the screen
+ *
+ * This function should actually display an array of characters
+ * starting at the given location, using the given "attribute",
+ * and using the given string of characters, which contains
+ * exactly "n" characters and which is NOT null-terminated.
+ *
+ * You may assume "valid" input if the window is properly sized.
+ *
+ * You must be sure that the string, when written, erases anything
+ * (including any visual cursor) that used to be where the text is
+ * drawn. On many machines this happens automatically, on others,
+ * you must first call "Term_wipe_xxx()" to clear the area.
+ *
+ * In color environments, you should activate the color contained
+ * in "color_data[a & 0x0F]", if needed, before drawing anything.
+ *
+ * You may ignore the "attribute" if you are only supporting a
+ * monochrome environment, since this routine is normally never
+ * called to display "black" (invisible) text, including the
+ * default "spaces", and all other colors should be drawn in
+ * the "normal" color in a monochrome environment.
+ *
+ * Note that if you have changed the "attr_blank" to something
+ * which is not black, then this function must be able to draw
+ * the resulting "blank" correctly.
+ *
+ * Note that this function must correctly handle "black" text if
+ * the "always_text" flag is set, if this flag is not set, all the
+ * "black" text will be handled by the "Term_wipe_xxx()" hook.
+ */
+static errr Term_text_xxx(int x, int y, int n, byte a, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some attr/char pairs on the screen
+ *
+ * This routine should display the given "n" attr/char pairs at
+ * the given location (x,y). This function is only used if one
+ * of the flags "always_pict" or "higher_pict" is defined.
+ *
+ * You must be sure that the attr/char pairs, when displayed, will
+ * erase anything (including any visual cursor) that used to be at
+ * the given location. On many machines this is automatic, but on
+ * others, you must first call "Term_wipe_xxx(x, y, 1)".
+ *
+ * With the "higher_pict" flag, this function can be used to allow
+ * the display of "pseudo-graphic" pictures, for example, by using
+ * the attr/char pair as an encoded index into a pixmap of special
+ * "pictures".
+ *
+ * With the "always_pict" flag, this function can be used to force
+ * every attr/char pair to be drawn by this function, which can be
+ * very useful if this file can optimize its own display calls.
+ *
+ * This function is often associated with the "arg_graphics" flag.
+ *
+ * This function is only used if one of the "higher_pict" and/or
+ * "always_pict" flags are set.
+ */
+static errr Term_pict_xxx(int x, int y, int n, const byte *ap, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Internal Functions ***/
+
+
+/*
+ * Instantiate a "term_data" structure
+ *
+ * This is one way to prepare the "term_data" structures and to
+ * "link" the various informational pieces together.
+ *
+ * This function assumes that every window should be 80x24 in size
+ * (the standard size) and should be able to queue 256 characters.
+ * Technically, only the "main screen window" needs to queue any
+ * characters, but this method is simple. One way to allow some
+ * variation is to add fields to the "term_data" structure listing
+ * parameters for that window, initialize them in the "init_xxx()"
+ * function, and then use them in the code below.
+ *
+ * Note that "activation" calls the "Term_init_xxx()" hook for
+ * the "term" structure, if needed.
+ */
+static void term_data_link(int i)
+{
+ term_data *td = &data[i];
+
+ /* Initialize the term */
+ term_init(td->t, 80, 24, 256);
+
+ /* Choose "soft" or "hard" cursor XXX XXX XXX */
+ /* A "soft" cursor must be explicitly "drawn" by the program */
+ /* while a "hard" cursor has some "physical" existance and is */
+ /* moved whenever text is drawn on the screen. See "term.c". */
+ /* td->t->soft_cursor = TRUE; */
+
+ /* Avoid the "corner" of the window XXX XXX XXX */
+ /* td->t->icky_corner = TRUE; */
+
+ /* Use "Term_pict()" for all attr/char pairs XXX XXX XXX */
+ /* See the "Term_pict_xxx()" function above. */
+ /* td->t->always_pict = TRUE; */
+
+ /* Use "Term_pict()" for some attr/char pairs XXX XXX XXX */
+ /* See the "Term_pict_xxx()" function above. */
+ /* td->t->higher_pict = TRUE; */
+
+ /* Use "Term_text()" even for "black" text XXX XXX XXX */
+ /* See the "Term_text_xxx()" function above. */
+ /* td->t->always_text = TRUE; */
+
+ /* Ignore the "TERM_XTRA_BORED" action XXX XXX XXX */
+ /* This may make things slightly more efficient. */
+ /* td->t->never_bored = TRUE; */
+
+ /* Ignore the "TERM_XTRA_FROSH" action XXX XXX XXX */
+ /* This may make things slightly more efficient. */
+ /* td->t->never_frosh = TRUE; */
+
+ /* Erase with "white space" XXX XXX XXX */
+ /* td->t->attr_blank = TERM_WHITE; */
+ /* td->t->char_blank = ' '; */
+
+ /* Prepare the init/nuke hooks */
+ td->t->init_hook = Term_init_xxx;
+ td->t->nuke_hook = Term_nuke_xxx;
+
+ /* Prepare the template hooks */
+ td->t->user_hook = Term_user_xxx;
+ td->t->xtra_hook = Term_xtra_xxx;
+ td->t->curs_hook = Term_curs_xxx;
+ td->t->wipe_hook = Term_wipe_xxx;
+ td->t->text_hook = Term_text_xxx;
+ td->t->pict_hook = Term_pict_xxx;
+
+ /* Remember where we came from */
+ td->t->data = (vptr)(td);
+
+ /* Activate it */
+ Term_activate(td->t);
+
+ /* Global pointer */
+ ang_term[i] = td->t;
+}
+
+
+
+/*
+ * Initialization function
+ */
+errr init_xxx(void)
+{
+ /* Initialize globals XXX XXX XXX */
+
+ /* Initialize "term_data" structures XXX XXX XXX */
+
+ /* Create windows (backwards!) */
+ for (i = TERM_DATA_MAX - 1; i >= 0; i--)
+ {
+ /* Link */
+ term_data_link(i);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef INTERNAL_MAIN
+
+
+/*
+ * Some special machines need their own "main()" function, which they
+ * can provide here, making sure NOT to compile the "main.c" file.
+ *
+ * These systems usually have some form of "event loop", run forever
+ * as the last step of "main()", which handles things like menus and
+ * window movement, and calls "play_game(FALSE)" to load a game after
+ * initializing "savefile" to a filename, or "play_game(TRUE)" to make
+ * a new game. The event loop would also be triggered by "Term_xtra()"
+ * (the TERM_XTRA_EVENT action), in which case the event loop would not
+ * actually "loop", but would run once and return.
+ */
+
+
+/*
+ * An event handler XXX XXX XXX
+ *
+ * You may need an event handler, which can be used by both
+ * by the "TERM_XTRA_BORED" and "TERM_XTRA_EVENT" entries in
+ * the "Term_xtra_xxx()" function, and also to wait for the
+ * user to perform whatever user-interface operation is needed
+ * to request the start of a new game or the loading of an old
+ * game, both of which should launch the "play_game()" function.
+ */
+static bool_ CheckEvents(bool_ wait)
+{
+ /* XXX XXX XXX */
+
+ return (0);
+}
+
+
+/*
+ * Init some stuff
+ *
+ * This function is used to keep the "path" variable off the stack.
+ */
+static void init_stuff(void)
+{
+ char path[1024];
+
+ /* Prepare the path XXX XXX XXX */
+ /* This must in some way prepare the "path" variable */
+ /* so that it points at the "lib" directory. Every */
+ /* machine handles this in a different way... */
+ strcpy(path, "XXX XXX XXX");
+
+ /* Prepare the filepaths */
+ init_file_paths(path);
+}
+
+
+/*
+ * Main function
+ *
+ * This function must do a lot of stuff.
+ */
+int main(int argc, char *argv[])
+{
+ /* Initialize the machine itself XXX XXX XXX */
+
+ /* Process command line arguments XXX XXX XXX */
+
+ /* Initialize the windows */
+ if (init_xxx()) quit("Oops!");
+
+ /* XXX XXX XXX */
+ ANGBAND_SYS = "xxx";
+
+ /* Initialize some stuff */
+ init_stuff();
+
+ /* Initialize */
+ init_angband * /
+
+ /* Allow auto-startup XXX XXX XXX */
+
+ /* Event loop forever XXX XXX XXX */
+ while (TRUE) CheckEvents(TRUE);
+}
+
+
+#endif /* INTERNAL_MAIN */
+
+
+#endif /* USE_XXX */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 00000000..48ab3f04
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,691 @@
+/* File: main.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#include "angband.h"
+
+
+
+/*
+ * Some machines have a "main()" function in their "main-xxx.c" file,
+ * all the others use this file for their "main()" function.
+ */
+
+
+#if !defined(MACINTOSH) && !defined(WINDOWS)
+
+
+/*
+ * A hook for "quit()".
+ *
+ * Close down, then fall back into "quit()".
+ */
+static void quit_hook(cptr s)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 8 - 1; j >= 0; j--)
+ {
+ /* Unused */
+ if (!angband_term[j]) continue;
+
+ /* Nuke it */
+ term_nuke(angband_term[j]);
+ }
+}
+
+
+
+/*
+ * Check and create if needed the directory dirpath
+ */
+bool_ private_check_user_directory(cptr dirpath)
+{
+ /* Is this used anywhere else in *bands? */
+ struct stat stat_buf;
+
+ int ret;
+
+ /* See if it already exists */
+ ret = stat(dirpath, &stat_buf);
+
+ /* It does */
+ if (ret == 0)
+ {
+ /* Now we see if it's a directory */
+ if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) return (TRUE);
+
+ /*
+ * Something prevents us from create a directory with
+ * the same pathname
+ */
+ return (FALSE);
+ }
+
+ /* No - this maybe the first time. Try to create a directory */
+ else
+ {
+ /* Create the ~/.ToME directory */
+ ret = mkdir(dirpath, 0700);
+
+ /* An error occured */
+ if (ret == -1) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+ }
+}
+
+/*
+ * Check existence of ".ToME/" directory in the user's
+ * home directory or try to create it if it doesn't exist.
+ * Returns FALSE if all the attempts fail.
+ */
+static bool_ check_create_user_dir(void)
+{
+ char dirpath[1024];
+ char versionpath[1024];
+ char savepath[1024];
+
+ /* Get an absolute path from the filename */
+ path_parse(dirpath, 1024, PRIVATE_USER_PATH);
+ strcpy(versionpath, dirpath);
+ strcat(versionpath, USER_PATH_VERSION);
+ strcpy(savepath, versionpath);
+ strcat(savepath, "/save");
+
+ return private_check_user_directory(dirpath) && private_check_user_directory(versionpath) && private_check_user_directory(savepath);
+}
+
+
+/*
+ * Initialize and verify the file paths, and the score file.
+ *
+ * Use the ANGBAND_PATH environment var if possible, else use
+ * DEFAULT_PATH, and in either case, branch off appropriately.
+ *
+ * First, we'll look for the ANGBAND_PATH environment variable,
+ * and then look for the files in there. If that doesn't work,
+ * we'll try the DEFAULT_PATH constant. So be sure that one of
+ * these two things works...
+ *
+ * We must ensure that the path ends with "PATH_SEP" if needed,
+ * since the "init_file_paths()" function will simply append the
+ * relevant "sub-directory names" to the given path.
+ */
+static void init_stuff(void)
+{
+ char path[1024];
+
+ cptr tail;
+
+ /* Get the environment variable */
+ tail = getenv("TOME_PATH");
+
+ /* Use the angband_path, or a default */
+ strcpy(path, tail ? tail : DEFAULT_PATH);
+
+ /* Hack -- Add a path separator (only if needed) */
+ if (!suffix(path, PATH_SEP)) strcat(path, PATH_SEP);
+
+ /* Initialize */
+ init_file_paths(path);
+}
+
+
+
+/*
+ * Handle a "-d<what>=<path>" option
+ *
+ * The "<what>" can be any string starting with the same letter as the
+ * name of a subdirectory of the "lib" folder (i.e. "i" or "info").
+ *
+ * The "<path>" can be any legal path for the given system, and should
+ * not end in any special path separator (i.e. "/tmp" or "~/.ang-info").
+ */
+static void change_path(cptr info)
+{
+ cptr s;
+
+ /* Find equal sign */
+ s = strchr(info, '=');
+
+ /* Verify equal sign */
+ if (!s) quit_fmt("Try '-d<what>=<path>' not '-d%s'", info);
+
+ /* Analyze */
+ switch (tolower(info[0]))
+ {
+ case 'a':
+ {
+ string_free(ANGBAND_DIR_APEX);
+ ANGBAND_DIR_APEX = string_make(s + 1);
+ break;
+ }
+
+ case 'f':
+ {
+ string_free(ANGBAND_DIR_FILE);
+ ANGBAND_DIR_FILE = string_make(s + 1);
+ break;
+ }
+
+ case 'h':
+ {
+ string_free(ANGBAND_DIR_HELP);
+ ANGBAND_DIR_HELP = string_make(s + 1);
+ break;
+ }
+
+ case 'i':
+ {
+ string_free(ANGBAND_DIR_INFO);
+ ANGBAND_DIR_INFO = string_make(s + 1);
+ break;
+ }
+
+ case 'u':
+ {
+ string_free(ANGBAND_DIR_USER);
+ ANGBAND_DIR_USER = string_make(s + 1);
+ break;
+ }
+
+ case 'x':
+ {
+ string_free(ANGBAND_DIR_XTRA);
+ ANGBAND_DIR_XTRA = string_make(s + 1);
+ break;
+ }
+
+ case 'd':
+ {
+ string_free(ANGBAND_DIR_DATA);
+ ANGBAND_DIR_DATA = string_make(s + 1);
+ break;
+ }
+
+ case 'e':
+ {
+ string_free(ANGBAND_DIR_EDIT);
+ ANGBAND_DIR_EDIT = string_make(s + 1);
+ break;
+ }
+
+ case 's':
+ {
+ string_free(ANGBAND_DIR_SAVE);
+ ANGBAND_DIR_SAVE = string_make(s + 1);
+ break;
+ }
+
+ default:
+ {
+ quit_fmt("Bad semantics in '-d%s'", info);
+ }
+ }
+}
+
+
+/*
+ * Simple "main" function for multiple platforms.
+ *
+ * Note the special "--" option which terminates the processing of
+ * standard options. All non-standard options (if any) are passed
+ * directly to the "init_xxx()" function.
+ */
+int main(int argc, char *argv[])
+{
+ int i;
+
+ bool_ done = FALSE;
+
+ bool_ new_game = FALSE;
+
+ int show_score = 0;
+
+ cptr mstr = NULL;
+
+ bool_ args = TRUE;
+
+#ifdef CHECK_MEMORY_LEAKS
+ GC_find_leak = 1;
+#endif /* CHECK_MEMORY_LEAKS */
+
+
+ /* Save the "program name" XXX XXX XXX */
+ argv0 = argv[0];
+
+
+ /* Default permissions on files */
+ (void)umask(022);
+
+
+ /* Get the file paths */
+ init_stuff();
+
+
+ /* Get the user id (?) */
+ player_uid = getuid();
+
+ /* Acquire the "user name" as a default player name */
+ user_name(player_name, player_uid);
+
+
+ /*
+ * On multiuser systems, users' private directories are
+ * used to store pref files, chardumps etc.
+ */
+ {
+ bool_ ret;
+
+ /* Create a directory for the user's files */
+ ret = check_create_user_dir();
+
+ /* Oops */
+ if (ret == FALSE) quit("Cannot create directory " PRIVATE_USER_PATH);
+ }
+
+
+
+
+ /* Process the command line arguments */
+ for (i = 1; args && (i < argc); i++)
+ {
+ /* Require proper options */
+ if (argv[i][0] != '-') goto usage;
+
+ /* Analyze option */
+ switch (argv[i][1])
+ {
+ case 'N':
+ case 'n':
+ {
+ new_game = TRUE;
+ break;
+ }
+
+ case 'F':
+ case 'f':
+ {
+ arg_fiddle = TRUE;
+ break;
+ }
+
+ case 'W':
+ case 'w':
+ {
+ arg_wizard = TRUE;
+ break;
+ }
+
+ case 'V':
+ case 'v':
+ {
+ arg_sound = TRUE;
+ break;
+ }
+
+ case 'G':
+ case 'g':
+ {
+ arg_graphics = TRUE;
+ break;
+ }
+
+ case 'R':
+ case 'r':
+ {
+ arg_force_roguelike = TRUE;
+ break;
+ }
+
+ case 'O':
+ case 'o':
+ {
+ arg_force_original = TRUE;
+ break;
+ }
+
+ case 'S':
+ case 's':
+ {
+ show_score = atoi(&argv[i][2]);
+ if (show_score <= 0) show_score = 10;
+ break;
+ }
+
+ case 'u':
+ case 'U':
+ {
+ if (!argv[i][2]) goto usage;
+ strcpy(player_name, &argv[i][2]);
+ strcpy(player_base, &argv[i][2]);
+ no_begin_screen = TRUE;
+ break;
+ }
+
+ case 'm':
+ {
+ if (!argv[i][2]) goto usage;
+ mstr = &argv[i][2];
+ break;
+ }
+
+ case 'M':
+ {
+ if (!argv[i][2]) goto usage;
+ force_module = string_make(&argv[i][2]);
+ break;
+ }
+
+ case 'h':
+ {
+ goto usage;
+ break;
+ }
+
+ case 'H':
+ {
+ char *s;
+ int j;
+
+ init_lua();
+ for (j = i + 1; j < argc; j++)
+ {
+ s = argv[j];
+
+ while (*s != '.') s++;
+ *s = '\0';
+ s++;
+ txt_to_html("head.aux", "foot.aux", argv[j], s, FALSE, FALSE);
+ }
+
+ return 0;
+ }
+
+ case 'd':
+ case 'D':
+ {
+ change_path(&argv[i][2]);
+ break;
+ }
+
+ case '-':
+ {
+ if (argv[i][2] == 'h' && !strcmp((argv[i] + 2), "help"))
+ goto usage;
+ else
+ {
+ argv[i] = argv[0];
+ argc = argc - i;
+ argv = argv + i;
+ args = FALSE;
+ break;
+ }
+ }
+
+ default:
+usage:
+ {
+ int j;
+
+ /* Dump usage information */
+ for (j = 0; j < argc; j++) printf("%s ", argv[j]);
+ printf("\n");
+ puts("Usage: tome [options] [-- subopts]");
+ puts(" -h This help");
+ puts(" -n Start a new character");
+ puts(" -f Request fiddle mode");
+ puts(" -w Request wizard mode");
+ puts(" -v Request sound mode");
+ puts(" -g Request graphics mode");
+ puts(" -o Request original keyset");
+ puts(" -r Request rogue-like keyset");
+ puts(" -H <list of files> Convert helpfile to html");
+ puts(" -s<num> Show <num> high scores");
+ puts(" -u<who> Use your <who> savefile");
+ puts(" -M<which> Use the <which> module");
+ puts(" -m<sys> Force 'main-<sys>.c' usage");
+ puts(" -d<def> Define a 'lib' dir sub-path");
+
+#ifdef USE_GTK2
+ puts(" -mgtk2 To use GTK2");
+ puts(" -- Sub options");
+ puts(" -- -n# Number of terms to use");
+ puts(" -- -b Turn off software backing store");
+# ifdef USE_GRAPHICS
+ puts(" -- -s Turn off smoothscaling graphics");
+ puts(" -- -o Requests \"old\" graphics");
+ puts(" -- -g Requests \"new\" graphics");
+ puts(" -- -t Enable transparency effect");
+# endif /* USE_GRAPHICS */
+#endif /* USE_GTK2 */
+
+#ifdef USE_XAW
+ puts(" -mxaw To use XAW");
+ puts(" -- Sub options");
+ puts(" -- -n# Number of terms to use");
+ puts(" -- -d<name> Display to use");
+# ifdef USE_GRAPHICS
+ puts(" -- -s Turn off smoothscaling graphics");
+ puts(" -- -o Requests \"old\" graphics");
+# endif /* USE_GRAPHICS */
+#endif /* USE_XAW */
+
+#ifdef USE_X11
+ puts(" -mx11 To use X11");
+ puts(" -- Sub options");
+ puts(" -- -n# Number of terms to use");
+ puts(" -- -d<name> Display to use");
+# ifdef USE_GRAPHICS
+ puts(" -- -s Turn off smoothscaling graphics");
+ puts(" -- -o Requests \"old\" graphics");
+ puts(" -- -b Requests double-width tiles");
+# endif /* USE_GRAPHICS */
+#endif /* USE_X11 */
+
+#ifdef USE_GCU
+ puts(" -mgcu To use curses");
+ puts(" -- Sub options");
+ puts(" -- -b Requests big screen");
+#endif /* USE_GCU */
+
+#ifdef USE_SLA
+ puts(" -msla To use SLang");
+#endif /* USE_SLA */
+
+#ifdef USE_SDL
+ puts(" -msdl To use SDL");
+ puts(" -- Sub options");
+ puts(" -- -n # Number of virtual consoles to use");
+ puts(" -- -g Request new graphics (16x16)");
+ puts(" -- -o Request old graphics (8x8)");
+ puts(" -- -b Requests double-width tiles");
+ puts(" -- -w # Request screen width in pixels");
+ puts(" -- -h # Request screen height in pixels");
+ puts(" -- -bpp # Request screen color depth in bits");
+ puts(" -- -fs Start with full-screen display");
+ puts(" -- -s # Request font size");
+ puts(" -- -f <font> Request true-type font by name");
+#endif /* USE_SDL */
+
+ /* Actually abort the process */
+ quit(NULL);
+ }
+ }
+ }
+
+ /* Hack -- Forget standard args */
+ if (args)
+ {
+ argc = 1;
+ argv[1] = NULL;
+ }
+
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+
+ /* Install "quit" hook */
+ quit_aux = quit_hook;
+
+
+#ifdef USE_GLU
+ /* Attempt to use the "main-glu.c" support */
+ if (!done && (!mstr || (streq(mstr, "glu"))))
+ {
+ extern errr init_glu(int, char**);
+ if (0 == init_glu(argc, argv))
+ {
+ ANGBAND_SYS = "glu";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_GTK2
+ /* Attempt to use the "main-gtk2.c" support */
+ if (!done && (!mstr || (streq(mstr, "gtk2"))))
+ {
+ extern errr init_gtk2(int, char**);
+ if (0 == init_gtk2(argc, argv))
+ {
+ ANGBAND_SYS = "gtk2";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_GTK
+ /* Attempt to use the "main-gtk.c" support */
+ if (!done && (!mstr || (streq(mstr, "gtk"))))
+ {
+ extern errr init_gtk(int, char**);
+ if (0 == init_gtk(argc, argv))
+ {
+ ANGBAND_SYS = "gtk";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_XAW
+ /* Attempt to use the "main-xaw.c" support */
+ if (!done && (!mstr || (streq(mstr, "xaw"))))
+ {
+ extern errr init_xaw(int, char**);
+ if (0 == init_xaw(argc, argv))
+ {
+ ANGBAND_SYS = "xaw";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_X11
+ /* Attempt to use the "main-x11.c" support */
+ if (!done && (!mstr || (streq(mstr, "x11"))))
+ {
+ extern errr init_x11(int, char**);
+ if (0 == init_x11(argc, argv))
+ {
+ ANGBAND_SYS = "x11";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_GCU
+ /* Attempt to use the "main-gcu.c" support */
+ if (!done && (!mstr || (streq(mstr, "gcu"))))
+ {
+ extern errr init_gcu(int, char**);
+ if (0 == init_gcu(argc, argv))
+ {
+ ANGBAND_SYS = "gcu";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_GLU
+ /* Attempt to use the "main-glu.c" support */
+ if (!done && (!mstr || (streq(mstr, "glu"))))
+ {
+ extern errr init_glu(int, char**);
+ if (0 == init_glu(argc, argv))
+ {
+ ANGBAND_SYS = "glu";
+ done = TRUE;
+ }
+ }
+#endif
+
+
+#ifdef USE_SLA
+ /* Attempt to use the "main-sla.c" support */
+ if (!done && (!mstr || (streq(mstr, "sla"))))
+ {
+ extern errr init_sla(void);
+ if (0 == init_sla())
+ {
+ ANGBAND_SYS = "sla";
+ done = TRUE;
+ }
+ }
+#endif
+
+
+#ifdef USE_SDL
+ /* Attempt to use the "main-sdl.c" support */
+ if (!done && (!mstr || (streq(mstr, "sdl"))))
+ {
+ extern errr init_sdl(int, char**);
+ if (0 == init_sdl(argc, argv))
+ {
+ ANGBAND_SYS = "sdl";
+ done = TRUE;
+ }
+ }
+#endif
+
+ /* Make sure we have a display! */
+ if (!done) quit("Unable to prepare any 'display module'!");
+
+
+ /* Catch nasty signals */
+ signals_init();
+
+ /* Initialize */
+ init_angband();
+
+ /* Hack -- If requested, display scores and quit */
+ if (show_score > 0) display_scores(0, show_score);
+
+ /* Wait for response */
+ pause_line(23);
+
+ /* Play the game */
+ play_game(new_game);
+
+#ifdef CHECK_MEMORY_LEAKS
+ CHECK_LEAKS();
+#endif
+
+ /* Quit */
+ quit(NULL);
+
+ /* Exit */
+ return (0);
+}
+
+#endif
diff --git a/src/melee1.c b/src/melee1.c
new file mode 100644
index 00000000..4e5d3208
--- /dev/null
+++ b/src/melee1.c
@@ -0,0 +1,3065 @@
+/* File: melee1.c */
+
+/* Purpose: Monster attacks */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+
+/*
+ * Critical blow. All hits that do 95% of total possible damage,
+ * and which also do at least 20 damage, or, sometimes, N damage.
+ * This is used only to determine "cuts" and "stuns".
+ */
+static int monster_critical(int dice, int sides, int dam)
+{
+ int max = 0;
+ int total = dice * sides;
+
+ /* Must do at least 95% of perfect */
+ if (dam < total * 19 / 20) return (0);
+
+ /* Weak blows rarely work */
+ if ((dam < 20) && (rand_int(100) >= dam)) return (0);
+
+ /* Perfect damage */
+ if (dam == total) max++;
+
+ /* Super-charge */
+ if (dam >= 20)
+ {
+ while (rand_int(100) < 2) max++;
+ }
+
+ /* Critical damage */
+ if (dam > 45) return (6 + max);
+ if (dam > 33) return (5 + max);
+ if (dam > 25) return (4 + max);
+ if (dam > 18) return (3 + max);
+ if (dam > 11) return (2 + max);
+ return (1 + max);
+}
+
+
+
+
+
+/*
+ * Determine if a monster attack against the player succeeds.
+ * Always miss 5% of the time, Always hit 5% of the time.
+ * Otherwise, match monster power against player armor.
+ */
+static int check_hit(int power, int level)
+{
+ int i, k, ac;
+
+ /* Percentile dice */
+ k = rand_int(100);
+
+ /* Hack -- Always miss or hit */
+ if (k < 10) return (k < 5);
+
+ /* Calculate the "attack quality" */
+ i = (power + (level * 3));
+
+ /* Total armor */
+ ac = p_ptr->ac + p_ptr->to_a;
+
+ /* Power and Level compete against Armor */
+ if ((i > 0) && (randint(i - luck( -10, 10)) > ((ac * 3) / 4))) return (TRUE);
+
+ /* Assume miss */
+ return (FALSE);
+}
+
+
+
+/*
+ * Hack -- possible "insult" messages
+ */
+static cptr desc_insult[] =
+{
+ "insults you!",
+ "insults your mother!",
+ "jumps around you!",
+ "humiliates you!",
+ "defiles you!",
+ "dances around you!",
+ "makes obnoxious gestures!",
+ "pokes you!!!"
+};
+
+
+
+/*
+ * Hack -- possible "insult" messages
+ */
+static cptr desc_moan[] =
+{
+ "seems sad about something.",
+ "asks if you have seen his dogs.",
+ "tells you to get off his land.",
+ "mumbles something about mushrooms.",
+
+ /* Mathilde's sentence */
+ "giggles at you.",
+ "asks you if you want to giggle with her.",
+ "says she is always happy."
+};
+
+
+/*
+ * Get the "power" of an attack of given effect type.
+ */
+int get_attack_power(int effect)
+{
+ switch (effect)
+ {
+ case RBE_HURT:
+ return 60;
+ case RBE_POISON:
+ return 5;
+ case RBE_UN_BONUS:
+ return 20;
+ case RBE_UN_POWER:
+ return 15;
+ case RBE_EAT_GOLD:
+ return 5;
+ case RBE_EAT_ITEM:
+ return 5;
+ case RBE_EAT_FOOD:
+ return 5;
+ case RBE_EAT_LITE:
+ return 5;
+ case RBE_ACID:
+ return 0;
+ case RBE_ELEC:
+ return 10;
+ case RBE_FIRE:
+ return 10;
+ case RBE_COLD:
+ return 10;
+ case RBE_BLIND:
+ return 2;
+ case RBE_CONFUSE:
+ return 10;
+ case RBE_TERRIFY:
+ return 10;
+ case RBE_PARALYZE:
+ return 2;
+ case RBE_LOSE_STR:
+ return 0;
+ case RBE_LOSE_DEX:
+ return 0;
+ case RBE_LOSE_CON:
+ return 0;
+ case RBE_LOSE_INT:
+ return 0;
+ case RBE_LOSE_WIS:
+ return 0;
+ case RBE_LOSE_CHR:
+ return 0;
+ case RBE_LOSE_ALL:
+ return 2;
+ case RBE_SHATTER:
+ return 60;
+ case RBE_EXP_10:
+ return 5;
+ case RBE_EXP_20:
+ return 5;
+ case RBE_EXP_40:
+ return 5;
+ case RBE_EXP_80:
+ return 5;
+ case RBE_DISEASE:
+ return 5;
+ case RBE_TIME:
+ return 5;
+ case RBE_SANITY:
+ return 60;
+ case RBE_HALLU:
+ return 10;
+ case RBE_PARASITE:
+ return 5;
+ case RBE_ABOMINATION:
+ return 30;
+ }
+ /* Unknown effects have no power */
+ return 0;
+}
+
+/*
+ * Attack the player via physical attacks.
+ */
+bool_ carried_make_attack_normal(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int ap_cnt;
+
+ int k, tmp, ac, rlev;
+ int do_cut, do_stun;
+
+ char ddesc[80] = "your symbiote";
+ cptr sym_name = symbiote_name(TRUE);
+
+ bool_ touched = FALSE, alive = TRUE;
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & (RF1_NEVER_BLOW)) return (FALSE);
+
+ /* Total armor */
+ ac = p_ptr->ac + p_ptr->to_a;
+
+ /* Extract the effective monster level */
+ rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1);
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = r_ptr->blow[ap_cnt].effect;
+ int method = r_ptr->blow[ap_cnt].method;
+ int d_dice = r_ptr->blow[ap_cnt].d_dice;
+ int d_side = r_ptr->blow[ap_cnt].d_side;
+
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Extract visibility (before blink) */
+ visible = TRUE;
+
+ /* Extract the attack "power" */
+ power = get_attack_power(effect);
+
+
+ /* Monster hits player */
+ if (!effect || check_hit(power, rlev))
+ {
+ /* Always disturbing */
+ disturb(1, 0);
+
+ /* Hack -- Apply "protection from evil" */
+ if ((p_ptr->protevil > 0) &&
+ (r_ptr->flags3 & (RF3_EVIL)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Evil-ness */
+ r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Message */
+ msg_format("%s is repelled.", sym_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Hack -- Apply "protection from good" */
+ if ((p_ptr->protgood > 0) &&
+ (r_ptr->flags3 & (RF3_GOOD)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Good-ness */
+ r_ptr->r_flags3 |= (RF3_GOOD);
+
+ /* Message */
+ msg_format("%s is repelled.", sym_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Assume no cut or stun */
+ do_cut = do_stun = 0;
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hits you.";
+ do_cut = do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touches you.";
+ touched = TRUE;
+ sound(SOUND_TOUCH);
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punches you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kicks you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claws you.";
+ touched = TRUE;
+ do_cut = 1;
+ sound(SOUND_CLAW);
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bites you.";
+ do_cut = 1;
+ touched = TRUE;
+ sound(SOUND_BITE);
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "stings you.";
+ touched = TRUE;
+ sound(SOUND_STING);
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's you.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butts you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crushes you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulfs you.";
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charges you.";
+ touched = TRUE;
+ sound(SOUND_BUY); /* Note! This is "charges", not "charges at". */
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawls on you.";
+ touched = TRUE;
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drools on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spits on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_EXPLODE:
+ {
+ act = "explodes.";
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gazes at you.";
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wails at you.";
+ sound(SOUND_WAIL);
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "releases spores at you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "projects XXX4's at you.";
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "begs you for money.";
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = desc_insult[rand_int(8)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ act = desc_moan[rand_int(4)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ if (randint(3) == 1)
+ act = "sings 'We are a happy family.'";
+ else
+ act = "sings 'I love you, you love me.'";
+ sound(SOUND_SHOW);
+ break;
+ }
+ }
+
+ /* Message */
+ if (act) msg_format("%s %s", sym_name, act);
+
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side);
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ /* Hack -- Assume obvious */
+ obvious = TRUE;
+
+ /* Hack -- No damage */
+ damage = 0;
+
+ break;
+ }
+
+ case RBE_HURT:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Hack -- Player armor reduces total damage */
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ break;
+ }
+
+ case RBE_ABOMINATION:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Morph, but let mimicry skill have a chance to stop this */
+ if (magik(60 - get_skill(SKILL_MIMICRY)))
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers twisting your body!");
+
+ set_mimic(damage, resolve_mimic_name("Abomination"), 50);
+ }
+ else
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers trying to twisting your body, but they fail.");
+ }
+
+ break;
+ }
+
+ case RBE_SANITY:
+ {
+ obvious = TRUE;
+
+ take_sanity_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_POISON:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Take "poison" effect */
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Allow complete resist */
+ if (!p_ptr->resist_disen)
+ {
+ /* Apply disenchantment */
+ if (apply_disenchant(0)) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case RBE_UN_POWER:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_GOLD:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_LITE:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered in acid!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ acid_dam(damage, ddesc);
+
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are struck by electricity!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ elec_dam(damage, ddesc);
+
+
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are enveloped in flames!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ fire_dam(damage, ddesc);
+
+
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered with frost!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ cold_dam(damage, ddesc);
+
+
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "blind" */
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + 10 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_CONFUSE:
+ {
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "confused" */
+ if (!p_ptr->resist_conf)
+ {
+ if (set_confused(p_ptr->confused + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "afraid" */
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_afraid(p_ptr->afraid + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ /* Hack -- Prevent perma-paralysis via damage */
+ if (p_ptr->paralyzed && (damage < 1)) damage = 1;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "paralyzed" */
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_paralyzed(p_ptr->paralyzed + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_INT:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_WIS:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_DEX:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_CON:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_CHR:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_ALL:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stats) */
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_SHATTER:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Hack -- Reduce damage based on the player armor class */
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Radius 8 earthquake centered at the monster */
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 8);
+ }
+
+ break;
+ }
+
+ case RBE_EXP_10:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 95))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(10, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_20:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 90))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(20, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_40:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(40, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_80:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(80, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_DISEASE:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Take "poison" effect */
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Damage CON (10% chance)*/
+ if (randint(100) < 11)
+ {
+ /* 1% chance for perm. damage */
+ bool_ perm = (randint(10) == 1);
+ if (dec_stat(A_CON, randint(10), perm)) obvious = TRUE;
+ }
+
+ break;
+ }
+ case RBE_PARASITE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ if (!p_ptr->parasite) set_parasite(damage, r_idx);
+
+ break;
+ }
+ case RBE_HALLU:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "image" */
+ if (!p_ptr->resist_chaos)
+ {
+ if (set_image(p_ptr->image + 3 + randint(rlev / 2)))
+ {
+ obvious = TRUE;
+ }
+ }
+ break;
+
+ }
+ case RBE_TIME:
+ {
+ switch (randint(10))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ msg_print("You feel life has clocked back.");
+ lose_exp(100 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ {
+ int stat = rand_int(6);
+
+ switch (stat)
+ {
+ case A_STR:
+ act = "strong";
+ break;
+ case A_INT:
+ act = "bright";
+ break;
+ case A_WIS:
+ act = "wise";
+ break;
+ case A_DEX:
+ act = "agile";
+ break;
+ case A_CON:
+ act = "hardy";
+ break;
+ case A_CHR:
+ act = "beautiful";
+ break;
+ }
+
+ msg_format("You're not as %s as you used to be...", act);
+
+ p_ptr->stat_cur[stat] = (p_ptr->stat_cur[stat] * 3) / 4;
+ if (p_ptr->stat_cur[stat] < 3) p_ptr->stat_cur[stat] = 3;
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+
+ case 10:
+ {
+ msg_print("You're not as powerful as you used to be...");
+
+ for (k = 0; k < 6; k++)
+ {
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ }
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+ }
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+ }
+
+
+ /* Hack -- only one of cut or stun */
+ if (do_cut && do_stun)
+ {
+ /* Cancel cut */
+ if (rand_int(100) < 50)
+ {
+ do_cut = 0;
+ }
+
+ /* Cancel stun */
+ else
+ {
+ do_stun = 0;
+ }
+ }
+
+ /* Handle cut */
+ if (do_cut)
+ {
+ int k = 0;
+
+ /* Critical hit (zero if non-critical) */
+ tmp = monster_critical(d_dice, d_side, damage);
+
+ /* Roll for damage */
+ switch (tmp)
+ {
+ case 0:
+ k = 0;
+ break;
+ case 1:
+ k = randint(5);
+ break;
+ case 2:
+ k = randint(5) + 5;
+ break;
+ case 3:
+ k = randint(20) + 20;
+ break;
+ case 4:
+ k = randint(50) + 50;
+ break;
+ case 5:
+ k = randint(100) + 100;
+ break;
+ case 6:
+ k = 300;
+ break;
+ default:
+ k = 500;
+ break;
+ }
+
+ /* Apply the cut */
+ if (k) (void)set_cut(p_ptr->cut + k);
+ }
+
+ /* Handle stun */
+ if (do_stun)
+ {
+ int k = 0;
+
+ /* Critical hit (zero if non-critical) */
+ tmp = monster_critical(d_dice, d_side, damage);
+
+ /* Roll for damage */
+ switch (tmp)
+ {
+ case 0:
+ k = 0;
+ break;
+ case 1:
+ k = randint(5);
+ break;
+ case 2:
+ k = randint(10) + 10;
+ break;
+ case 3:
+ k = randint(20) + 20;
+ break;
+ case 4:
+ k = randint(30) + 30;
+ break;
+ case 5:
+ k = randint(40) + 40;
+ break;
+ case 6:
+ k = 100;
+ break;
+ default:
+ k = 200;
+ break;
+ }
+
+ /* Apply the stun */
+ if (k) (void)set_stun(p_ptr->stun + k);
+ }
+
+ if (touched)
+ {
+ if (p_ptr->sh_fire && alive)
+ {
+ r_ptr->r_flags3 |= RF3_IM_FIRE;
+ }
+
+ if (p_ptr->sh_elec && alive)
+ {
+ r_ptr->r_flags3 |= RF3_IM_ELEC;
+ }
+ touched = FALSE;
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+
+ /* Disturbing */
+ disturb(1, 0);
+
+ /* Message */
+ msg_format("%s misses you.", sym_name);
+
+ break;
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+ /* Assume we attacked */
+ return (TRUE);
+}
+
+/*
+ * Give unprotected player the Black Breath with a 1 in (chance) probability
+ *
+ */
+void black_breath_attack(int chance)
+{
+ if (!p_ptr->protundead && randint(chance) == 1)
+ {
+ msg_print("Your foe calls upon your soul!");
+ msg_print("You feel the Black Breath slowly draining you of life...");
+ p_ptr->black_breath = TRUE;
+ }
+}
+
+/*
+ * Attack the player via physical attacks.
+ */
+bool_ make_attack_normal(int m_idx, byte divis)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ int ap_cnt;
+
+ int i, j, k, tmp, ac, rlev;
+ int do_cut, do_stun, do_vampire;
+
+ s32b gold;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char m_name[80];
+
+ char ddesc[80];
+
+ bool_ blinked;
+ bool_ touched = FALSE, fear = FALSE, alive = TRUE;
+ bool_ explode = FALSE;
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & (RF1_NEVER_BLOW)) return (FALSE);
+
+ /* ...nor if friendly */
+ if (is_friend(m_ptr) >= 0)
+ {
+ if (p_ptr->control == m_idx) swap_position(m_ptr->fy, m_ptr->fx);
+ return FALSE;
+ }
+
+ /* Cannot attack the player if mortal and player fated to never die by the ... */
+ if ((r_ptr->flags7 & RF7_MORTAL) && (p_ptr->no_mortal)) return (FALSE);
+
+ /* Total armor */
+ ac = p_ptr->ac + p_ptr->to_a;
+
+ /* Extract the effective monster level */
+ rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get the "died from" information (i.e. "a kobold") */
+ monster_desc(ddesc, m_ptr, 0x88);
+
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = m_ptr->blow[ap_cnt].effect;
+ int method = m_ptr->blow[ap_cnt].method;
+ int d_dice = m_ptr->blow[ap_cnt].d_dice;
+ int d_side = m_ptr->blow[ap_cnt].d_side;
+
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Extract visibility (before blink) */
+ if (m_ptr->ml) visible = TRUE;
+
+ /* Extract the attack "power" */
+ switch (effect)
+ {
+ case RBE_HURT:
+ power = 60;
+ break;
+ case RBE_POISON:
+ power = 5;
+ break;
+ case RBE_UN_BONUS:
+ power = 20;
+ break;
+ case RBE_UN_POWER:
+ power = 15;
+ break;
+ case RBE_EAT_GOLD:
+ power = 5;
+ break;
+ case RBE_EAT_ITEM:
+ power = 5;
+ break;
+ case RBE_EAT_FOOD:
+ power = 5;
+ break;
+ case RBE_EAT_LITE:
+ power = 5;
+ break;
+ case RBE_ACID:
+ power = 0;
+ break;
+ case RBE_ELEC:
+ power = 10;
+ break;
+ case RBE_FIRE:
+ power = 10;
+ break;
+ case RBE_COLD:
+ power = 10;
+ break;
+ case RBE_BLIND:
+ power = 2;
+ break;
+ case RBE_CONFUSE:
+ power = 10;
+ break;
+ case RBE_TERRIFY:
+ power = 10;
+ break;
+ case RBE_PARALYZE:
+ power = 2;
+ break;
+ case RBE_LOSE_STR:
+ power = 0;
+ break;
+ case RBE_LOSE_DEX:
+ power = 0;
+ break;
+ case RBE_LOSE_CON:
+ power = 0;
+ break;
+ case RBE_LOSE_INT:
+ power = 0;
+ break;
+ case RBE_LOSE_WIS:
+ power = 0;
+ break;
+ case RBE_LOSE_CHR:
+ power = 0;
+ break;
+ case RBE_LOSE_ALL:
+ power = 2;
+ break;
+ case RBE_SHATTER:
+ power = 60;
+ break;
+ case RBE_EXP_10:
+ power = 5;
+ break;
+ case RBE_EXP_20:
+ power = 5;
+ break;
+ case RBE_EXP_40:
+ power = 5;
+ break;
+ case RBE_EXP_80:
+ power = 5;
+ break;
+ case RBE_DISEASE:
+ power = 5;
+ break;
+ case RBE_TIME:
+ power = 5;
+ break;
+ case RBE_SANITY:
+ power = 60;
+ break;
+ case RBE_HALLU:
+ power = 10;
+ break;
+ case RBE_PARASITE:
+ power = 5;
+ break;
+ case RBE_ABOMINATION:
+ power = 20;
+ break;
+ }
+
+
+ /* Monster hits player */
+ if (!effect || check_hit(power, rlev))
+ {
+ int chance = p_ptr->dodge_chance - ((rlev * 5) / 6);
+
+ /* Always disturbing */
+ disturb(1, 0);
+
+ if ((chance > 0) && magik(chance))
+ {
+ char m_poss[80];
+ monster_desc(m_poss, m_ptr, 0x06);
+ msg_format("You dodge %s attack!", m_poss);
+ continue;
+ }
+
+ /* Eru can help you */
+ PRAY_GOD(GOD_ERU)
+ {
+ s32b chance = p_ptr->grace;
+
+ if (chance > 50000) chance = 50000;
+ chance -= rlev * 300;
+
+ if ((randint(100000) < chance) && (r_ptr->flags3 & (RF3_EVIL)))
+ {
+ /* Remember the Evil-ness */
+ r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Message */
+ msg_format("The hand of Eru Iluvatar stops %s blow.", m_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+ }
+
+ /* Hack -- Apply "protection from evil" */
+ if ((p_ptr->protevil > 0) &&
+ (r_ptr->flags3 & (RF3_EVIL)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Evil-ness */
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+
+ /* Message */
+ msg_format("%^s is repelled.", m_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Hack -- Apply "protection from good" */
+ if ((p_ptr->protgood > 0) &&
+ (r_ptr->flags3 & (RF3_GOOD)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Good-ness */
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_GOOD);
+ }
+
+ /* Message */
+ msg_format("%^s is repelled.", m_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Assume no cut or stun */
+ do_cut = do_stun = do_vampire = 0;
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hits you.";
+ do_cut = do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touches you.";
+ touched = TRUE;
+ sound(SOUND_TOUCH);
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punches you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kicks you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claws you.";
+ touched = TRUE;
+ do_cut = 1;
+ sound(SOUND_CLAW);
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bites you.";
+ do_cut = 1;
+ if (magik(5) && (strstr(r_name + r_ptr->name, "Vampire") || strstr(r_name + r_ptr->name, "vampire")))
+ do_vampire = TRUE;
+ touched = TRUE;
+ sound(SOUND_BITE);
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "stings you.";
+ touched = TRUE;
+ sound(SOUND_STING);
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's you.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butts you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crushes you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulfs you.";
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charges you.";
+ touched = TRUE;
+ sound(SOUND_BUY); /* Note! This is "charges", not "charges at". */
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawls on you.";
+ touched = TRUE;
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drools on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spits on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_EXPLODE:
+ {
+ act = "explodes.";
+ explode = TRUE;
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gazes at you.";
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wails at you.";
+ sound(SOUND_WAIL);
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "releases spores at you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "projects XXX4's at you.";
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "begs you for money.";
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = desc_insult[rand_int(8)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ if (strstr((r_name + r_ptr->name), "Mathilde, the Science Student"))
+ act = desc_moan[rand_int(3) + 4];
+ else
+ act = desc_moan[rand_int(4)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ if (randint(3) == 1)
+ act = "sings 'We are a happy family.'";
+ else
+ act = "sings 'I love you, you love me.'";
+ sound(SOUND_SHOW);
+ break;
+ }
+ }
+
+ /* Message */
+ if (act) msg_format("%^s %s", m_name, act);
+
+ /* The undead can give the player the Black Breath with
+ * a successful blow. Uniques have a better chance. -LM-
+ * Nazgul have a 25% chance
+ */
+ if (r_ptr->flags7 & RF7_NAZGUL)
+ {
+ black_breath_attack(4);
+ }
+ else if ((m_ptr->level >= 35) && (r_ptr->flags3 & (RF3_UNDEAD)) &&
+ (r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ black_breath_attack(300 - m_ptr->level);
+ }
+ else if ((m_ptr->level >= 40) && (r_ptr->flags3 & (RF3_UNDEAD)))
+ {
+ black_breath_attack(450 - m_ptr->level);
+ }
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side);
+
+ /* Sometime reduce the damage */
+ damage /= divis;
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ /* Hack -- Assume obvious */
+ obvious = TRUE;
+
+ /* Hack -- No damage */
+ damage = 0;
+
+ break;
+ }
+
+ case RBE_HURT:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Hack -- Player armor reduces total damage */
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ break;
+ }
+
+ case RBE_ABOMINATION:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Morph, but let mimicry skill have a chance to stop this */
+ if (magik(60 - get_skill(SKILL_MIMICRY)))
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers twisting your body!");
+
+ set_mimic(damage, resolve_mimic_name("Abomination"), 50);
+ }
+ else
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers trying to twisting your body, but they fail.");
+ }
+
+ break;
+ }
+
+ case RBE_SANITY:
+ {
+ obvious = TRUE;
+
+ take_sanity_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_POISON:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Take "poison" effect */
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_POIS);
+
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Allow complete resist */
+ if (!p_ptr->resist_disen)
+ {
+ /* Apply disenchantment */
+ if (apply_disenchant(0)) obvious = TRUE;
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_DISEN);
+
+ break;
+ }
+
+ case RBE_UN_POWER:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Find an item */
+ for (k = 0; k < 10; k++)
+ {
+ /* Pick an item */
+ i = rand_int(INVEN_PACK);
+
+ /* Obtain the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Drain charged wands/staffs
+ Hack -- don't let artifacts get drained */
+ if (((o_ptr->tval == TV_STAFF) ||
+ (o_ptr->tval == TV_WAND)) &&
+ (o_ptr->pval) &&
+ !artifact_p(o_ptr))
+ {
+ /* Message */
+ msg_print("Energy drains from your pack!");
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Heal */
+ j = rlev;
+ m_ptr->hp += j * o_ptr->pval * o_ptr->number;
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Uncharge */
+ o_ptr->pval = 0;
+
+ /* Combine / Reorder the pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Done */
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case RBE_EAT_GOLD:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Saving throw (unless paralyzed) based on dex and level */
+ if (!p_ptr->paralyzed &&
+ (rand_int(100) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] +
+ p_ptr->lev)))
+ {
+ /* Saving throw message */
+ msg_print("You quickly protect your money pouch!");
+
+ /* Occasional blink anyway */
+ if (rand_int(3)) blinked = TRUE;
+ }
+
+ /* Eat gold */
+ else
+ {
+ gold = (p_ptr->au / 10) + randint(25);
+ if (gold < 2) gold = 2;
+ if (gold > 5000) gold = (p_ptr->au / 20) + randint(3000);
+ if (gold > p_ptr->au) gold = p_ptr->au;
+ p_ptr->au -= gold;
+ if (gold <= 0)
+ {
+ msg_print("Nothing was stolen.");
+ }
+ else if (p_ptr->au)
+ {
+ msg_print("Your purse feels lighter.");
+ msg_format("%ld coins were stolen!", (long)gold);
+ }
+ else
+ {
+ msg_print("Your purse feels lighter.");
+ msg_print("All of your coins were stolen!");
+ }
+
+ while (gold > 0)
+ {
+ object_type forge, *j_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(j_ptr);
+
+ /* Prepare a gold object */
+ object_prep(j_ptr, lookup_kind(TV_GOLD, 9));
+
+ /* Determine how much the treasure is "worth" */
+ j_ptr->pval = (gold >= 15000) ? 15000 : gold;
+
+ monster_carry(m_ptr, m_idx, j_ptr);
+
+ gold -= 15000;
+ }
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Blink away */
+ blinked = TRUE;
+ }
+
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Saving throw (unless paralyzed) based on dex and level */
+ if (!p_ptr->paralyzed &&
+ (rand_int(100) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] +
+ p_ptr->lev)))
+ {
+ /* Saving throw message */
+ msg_print("You grab hold of your backpack!");
+
+ /* Occasional "blink" anyway */
+ blinked = TRUE;
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Done */
+ break;
+ }
+
+ /* Find an item */
+ for (k = 0; k < 10; k++)
+ {
+ /* Pick an item */
+ i = rand_int(INVEN_PACK);
+
+ /* Obtain the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip artifacts */
+ if (artifact_p(o_ptr) || o_ptr->art_name) continue;
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, FALSE, 3);
+
+ /* Message */
+ msg_format("%sour %s (%c) was stolen!",
+ ((o_ptr->number > 1) ? "One of y" : "Y"),
+ o_name, index_to_label(i));
+
+ /* Option */
+ if (testing_carry)
+ {
+ s16b o_idx;
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *j_ptr;
+
+ /* Get new object */
+ j_ptr = &o_list[o_idx];
+
+ /* Copy object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify number */
+ j_ptr->number = 1;
+
+ /* Hack -- If a wand, allocate total
+ * maximum timeouts or charges between those
+ * stolen and those missed. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval / o_ptr->number;
+ o_ptr->pval -= j_ptr->pval;
+ }
+
+ /* Forget mark */
+ j_ptr->marked = FALSE;
+
+ /* Memorize monster */
+ j_ptr->held_m_idx = m_idx;
+
+ /* Build stack */
+ j_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ /* Build stack */
+ m_ptr->hold_o_idx = o_idx;
+ }
+ }
+ else
+ {
+ if (strstr((r_name + r_ptr->name), "black market")
+ && randint(2) != 1)
+ {
+ s16b o_idx;
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *j_ptr;
+ if (cheat_xtra || cheat_peek)
+ msg_print("Moving object to black market...");
+
+ /* Get new object */
+ j_ptr = &o_list[o_idx];
+
+ /* Copy object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify number */
+ j_ptr->number = 1;
+
+ /* Forget mark */
+ j_ptr->marked = FALSE;
+
+ move_to_black_market(j_ptr);
+ }
+ }
+ }
+
+ /* Steal the items */
+ inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Blink away */
+ blinked = TRUE;
+
+ /* Done */
+ break;
+ }
+
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Steal some food */
+ for (k = 0; k < 10; k++)
+ {
+ /* Pick an item from the pack */
+ i = rand_int(INVEN_PACK);
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip non-food objects */
+ if (o_ptr->tval != TV_FOOD) continue;
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Message */
+ msg_format("%sour %s (%c) was eaten!",
+ ((o_ptr->number > 1) ? "One of y" : "Y"),
+ o_name, index_to_label(i));
+
+ /* Steal the items */
+ inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Done */
+ break;
+ }
+
+ break;
+ }
+
+ case RBE_EAT_LITE:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Access the lite */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Drain fuel */
+ if ((o_ptr->pval > 0) && (!artifact_p(o_ptr)))
+ {
+ /* Reduce fuel */
+ o_ptr->pval -= (250 + randint(250));
+ if (o_ptr->pval < 1) o_ptr->pval = 1;
+
+ /* Notice */
+ if (!p_ptr->blind)
+ {
+ msg_print("Your light dims.");
+ obvious = TRUE;
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered in acid!");
+
+ /* Special damage */
+ acid_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_ACID);
+
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are struck by electricity!");
+
+ /* Special damage */
+ elec_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_ELEC);
+
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are enveloped in flames!");
+
+ /* Special damage */
+ fire_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_FIRE);
+
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered with frost!");
+
+ /* Special damage */
+ cold_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_COLD);
+
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "blind" */
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + 10 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_BLIND);
+
+ break;
+ }
+
+ case RBE_CONFUSE:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "confused" */
+ if (!p_ptr->resist_conf)
+ {
+ if (set_confused(p_ptr->confused + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_CONF);
+
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "afraid" */
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_afraid(p_ptr->afraid + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_FEAR);
+
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ /* Hack -- Prevent perma-paralysis via damage */
+ if (p_ptr->paralyzed && (damage < 1)) damage = 1;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "paralyzed" */
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_paralyzed(p_ptr->paralyzed + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_FREE);
+
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_INT:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_WIS:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_DEX:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_CON:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_CHR:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_ALL:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stats) */
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_SHATTER:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Hack -- Reduce damage based on the player armor class */
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Radius 8 earthquake centered at the monster */
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(m_ptr->fy, m_ptr->fx, 8);
+ }
+
+ break;
+ }
+
+ case RBE_EXP_10:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 95))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(10, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_20:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 90))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(20, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_40:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(40, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_80:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(80, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_DISEASE:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Take "poison" effect */
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Damage CON (10% chance)*/
+ if (randint(100) < 11)
+ {
+ /* 1% chance for perm. damage */
+ bool_ perm = (randint(10) == 1);
+ if (dec_stat(A_CON, randint(10), perm)) obvious = TRUE;
+ }
+
+ break;
+ }
+ case RBE_HALLU:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "image" */
+ if (!p_ptr->resist_chaos)
+ {
+ if (set_image(p_ptr->image + 3 + randint(rlev / 2)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_CHAOS);
+
+ break;
+
+ }
+ case RBE_TIME:
+ {
+ switch (randint(10))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ msg_print("You feel life has clocked back.");
+ lose_exp(100 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ {
+ int stat = rand_int(6);
+
+ switch (stat)
+ {
+ case A_STR:
+ act = "strong";
+ break;
+ case A_INT:
+ act = "bright";
+ break;
+ case A_WIS:
+ act = "wise";
+ break;
+ case A_DEX:
+ act = "agile";
+ break;
+ case A_CON:
+ act = "hardy";
+ break;
+ case A_CHR:
+ act = "beautiful";
+ break;
+ }
+
+ msg_format("You're not as %s as you used to be...", act);
+
+ p_ptr->stat_cur[stat] = (p_ptr->stat_cur[stat] * 3) / 4;
+ if (p_ptr->stat_cur[stat] < 3) p_ptr->stat_cur[stat] = 3;
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+
+ case 10:
+ {
+ msg_print("You're not as powerful as you used to be...");
+
+ for (k = 0; k < 6; k++)
+ {
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ }
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+ }
+ take_hit(damage, ddesc);
+ break;
+ }
+ case RBE_PARASITE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ if (!p_ptr->parasite) set_parasite(damage, m_ptr->r_idx);
+
+ break;
+ }
+ }
+
+
+ /* Hack -- only one of cut or stun */
+ if (do_cut && do_stun)
+ {
+ /* Cancel cut */
+ if (rand_int(100) < 50)
+ {
+ do_cut = 0;
+ }
+
+ /* Cancel stun */
+ else
+ {
+ do_stun = 0;
+ }
+ }
+
+ /* Handle cut */
+ if (do_cut)
+ {
+ int k = 0;
+
+ /* Critical hit (zero if non-critical) */
+ tmp = monster_critical(d_dice, d_side, damage);
+
+ /* Roll for damage */
+ switch (tmp)
+ {
+ case 0:
+ k = 0;
+ break;
+ case 1:
+ k = randint(5);
+ break;
+ case 2:
+ k = randint(5) + 5;
+ break;
+ case 3:
+ k = randint(20) + 20;
+ break;
+ case 4:
+ k = randint(50) + 50;
+ break;
+ case 5:
+ k = randint(100) + 100;
+ break;
+ case 6:
+ k = 300;
+ break;
+ default:
+ k = 500;
+ break;
+ }
+
+ /* Apply the cut */
+ if (k) (void)set_cut(p_ptr->cut + k);
+ }
+
+ /* Handle stun */
+ if (do_stun)
+ {
+ int k = 0;
+
+ /* Critical hit (zero if non-critical) */
+ tmp = monster_critical(d_dice, d_side, damage);
+
+ /* Roll for damage */
+ switch (tmp)
+ {
+ case 0:
+ k = 0;
+ break;
+ case 1:
+ k = randint(5);
+ break;
+ case 2:
+ k = randint(10) + 10;
+ break;
+ case 3:
+ k = randint(20) + 20;
+ break;
+ case 4:
+ k = randint(30) + 30;
+ break;
+ case 5:
+ k = randint(40) + 40;
+ break;
+ case 6:
+ k = 100;
+ break;
+ default:
+ k = 200;
+ break;
+ }
+
+ /* Apply the stun */
+ if (k) (void)set_stun(p_ptr->stun + k);
+ }
+
+ /* Do vampiric thingies */
+ if (do_vampire)
+ {
+ /* Change to resist(but never total protection) */
+/* if (magik(3) || (magik(m_ptr->level - (p_ptr->lev / 2))))
+ call_lua("gain_corruption", "(s)", "", "Vampire");*/
+ }
+
+ if (explode)
+ {
+ sound(SOUND_EXPLODE);
+ if (mon_take_hit(m_idx, m_ptr->hp + 1, &fear, NULL))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+
+ if (touched)
+ {
+ if (p_ptr->sh_fire && alive)
+ {
+ if (!(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ msg_format("%^s is suddenly very hot!", m_name);
+ if (mon_take_hit(m_idx, damroll(2, 6), &fear,
+ " turns into a pile of ash."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ else
+ {
+ if (m_ptr->ml)
+ r_ptr->r_flags3 |= RF3_IM_FIRE;
+ }
+ }
+
+ if (p_ptr->sh_elec && alive)
+ {
+ if (!(r_ptr->flags3 & RF3_IM_ELEC))
+ {
+ msg_format("%^s gets zapped!", m_name);
+ if (mon_take_hit(m_idx, damroll(2, 6), &fear,
+ " turns into a pile of cinder."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ else
+ {
+ if (m_ptr->ml)
+ r_ptr->r_flags3 |= RF3_IM_ELEC;
+ }
+ }
+ if (p_ptr->shield && (p_ptr->shield_opt & SHIELD_COUNTER) && alive)
+ {
+ msg_format("%^s gets bashed by your mystic shield!", m_name);
+ if (mon_take_hit(m_idx, damroll(p_ptr->shield_power_opt, p_ptr->shield_power_opt2), &fear,
+ " is bashed by your mystic shield."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ if (p_ptr->shield && (p_ptr->shield_opt & SHIELD_FIRE) && alive)
+ {
+ if (!(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ msg_format("%^s gets burned by your fiery shield!", m_name);
+ if (mon_take_hit(m_idx, damroll(p_ptr->shield_power_opt, p_ptr->shield_power_opt2), &fear,
+ " is burned by your fiery shield."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ else
+ {
+ if (m_ptr->ml)
+ r_ptr->r_flags3 |= RF3_IM_FIRE;
+ }
+ }
+ if (p_ptr->shield && (p_ptr->shield_opt & SHIELD_GREAT_FIRE) && alive)
+ {
+ msg_format("%^s gets burned by your fiery shield!", m_name);
+ if (mon_take_hit(m_idx, damroll(p_ptr->shield_power_opt, p_ptr->shield_power_opt2), &fear,
+ " is burned by your fiery shield."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ if (p_ptr->shield && (p_ptr->shield_opt & SHIELD_FEAR) && alive)
+ {
+ int tmp;
+
+ if ((!(r_ptr->flags1 & RF1_UNIQUE)) && (damroll(p_ptr->shield_power_opt, p_ptr->shield_power_opt2) - m_ptr->level > 0))
+ {
+ msg_format("%^s gets scared away!", m_name);
+
+ /* Increase fear */
+ tmp = m_ptr->monfear + p_ptr->shield_power_opt;
+ fear = TRUE;
+
+ /* Set fear */
+ m_ptr->monfear = (tmp < 200) ? tmp : 200;
+ }
+ }
+
+ touched = FALSE;
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+
+ /* Visible monsters */
+ if (m_ptr->ml)
+ {
+ /* Disturbing */
+ disturb(1, 0);
+
+ /* Message */
+ msg_format("%^s misses you.", m_name);
+ }
+
+ break;
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+
+
+ /* Blink away */
+ if (blinked)
+ {
+ msg_print("The thief flees laughing!");
+ teleport_away(m_idx, MAX_SIGHT * 2 + 5);
+ }
+
+
+ /* Always notice cause of death */
+ if (death && (r_ptr->r_deaths < MAX_SHORT))
+ {
+ r_ptr->r_deaths++;
+ }
+
+ if (m_ptr->ml && fear)
+ {
+ sound (SOUND_FLEE);
+ msg_format("%^s flees in terror!", m_name);
+ }
+
+ /* Assume we attacked */
+ return (TRUE);
+}
+
+
diff --git a/src/melee2.c b/src/melee2.c
new file mode 100644
index 00000000..6ada6bd0
--- /dev/null
+++ b/src/melee2.c
@@ -0,0 +1,7591 @@
+/* File: melee2.c */
+
+/* Purpose: Monster spells and movement */
+
+/*
+* Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+*
+* This software may be copied and distributed for educational, research, and
+* not for profit purposes provided that this copyright and statement are
+* included in all such copies.
+*/
+
+/*
+* This file has several additions to it by Keldon Jones (keldon@umr.edu)
+* to improve the general quality of the AI (version 0.1.1).
+*/
+
+#include "angband.h"
+
+#define SPEAK_CHANCE 8
+#define GRINDNOISE 20
+
+#define FOLLOW_DISTANCE 6
+
+/*
+ * Based on mon_take_hit... all monster attacks on
+ * other monsters should use
+ */
+bool_ mon_take_hit_mon(int s_idx, int m_idx, int dam, bool_ *fear, cptr note)
+{
+ monster_type *m_ptr = &m_list[m_idx], *s_ptr = &m_list[s_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ s32b div, new_exp, new_exp_frac;
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Some mosnters are immune to death */
+ if (r_ptr->flags7 & RF7_NO_DEATH) return FALSE;
+
+ /* Wake it up */
+ m_ptr->csleep = 0;
+
+ /* Hurt it */
+ m_ptr->hp -= dam;
+
+ /* It is dead now... or is it? */
+ if (m_ptr->hp < 0)
+ {
+ if (((r_ptr->flags1 & RF1_UNIQUE) && (m_ptr->status <= MSTATUS_NEUTRAL_P)) ||
+ (m_ptr->mflag & MFLAG_QUEST))
+ {
+ m_ptr->hp = 1;
+ }
+ else
+ {
+ char m_name[80];
+ s32b dive = s_ptr->level;
+
+ if (!dive) dive = 1;
+
+ /* Extract monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Make a sound */
+ if ((r_ptr->flags3 & RF3_DEMON) ||
+ (r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags2 & RF2_STUPID) ||
+ (r_ptr->flags3 & RF3_NONLIVING) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ sound(SOUND_N_KILL);
+ }
+ else
+ {
+ sound(SOUND_KILL);
+ }
+
+ /* Death by Missile/Spell attack */
+ if (note)
+ {
+ cmonster_msg(TERM_L_RED, "%^s%s", m_name, note);
+ }
+ /* Death by Physical attack -- living monster */
+ else if (!m_ptr->ml)
+ {
+ /* Do nothing */
+ }
+ /* Death by Physical attack -- non-living monster */
+ else if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ cmonster_msg(TERM_L_RED, "%^s is destroyed.", m_name);
+ }
+ else
+ {
+ cmonster_msg(TERM_L_RED, "%^s is killed.", m_name);
+ }
+
+ dive = r_ptr->mexp * m_ptr->level / dive;
+ if (!dive) dive = 1;
+
+ /* Monster gains some xp */
+ monster_gain_exp(s_idx, dive, FALSE);
+
+ /* Monster lore skill allows gaining xp from pets */
+ if (get_skill(SKILL_LORE) && (s_ptr->status >= MSTATUS_PET))
+ {
+ /* Maximum player level */
+ div = p_ptr->max_plv;
+
+ /* Give some experience for the kill */
+ new_exp = ((long)r_ptr->mexp * m_ptr->level) / div;
+
+ /* Handle fractional experience */
+ new_exp_frac = ((((long)r_ptr->mexp * m_ptr->level) % div)
+ * 0x10000L / div) + p_ptr->exp_frac;
+
+ /* Keep track of experience */
+ if (new_exp_frac >= 0x10000L)
+ {
+ new_exp++;
+ p_ptr->exp_frac = new_exp_frac - 0x10000;
+ }
+ else
+ {
+ p_ptr->exp_frac = new_exp_frac;
+ }
+
+ /*
+ * Factor the xp by the skill level
+ * Note that a score of 50 in the skill makes the gain be 120% of the exp
+ */
+ new_exp = new_exp * get_skill_scale(SKILL_LORE, 120) / 100;
+
+ /* Gain experience */
+ gain_exp(new_exp);
+ }
+
+ /* When an Unique dies, it stays dead */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ r_ptr->max_num = 0;
+ }
+
+ /* Generate treasure */
+ monster_death(m_idx);
+
+ /* Delete the monster */
+ delete_monster_idx(m_idx);
+
+ /* Not afraid */
+ (*fear) = FALSE;
+
+ /* Monster is dead */
+ return (TRUE);
+ }
+
+ }
+
+#ifdef ALLOW_FEAR
+
+ /* Mega-Hack -- Pain cancels fear */
+ if (m_ptr->monfear && (dam > 0))
+ {
+ int tmp = randint(dam);
+
+ /* Cure a little fear */
+ if (tmp < m_ptr->monfear)
+ {
+ /* Reduce fear */
+ m_ptr->monfear -= tmp;
+ }
+
+ /* Cure all the fear */
+ else
+ {
+ /* Cure fear */
+ m_ptr->monfear = 0;
+
+ /* No more fear */
+ (*fear) = FALSE;
+ }
+ }
+
+ /* Sometimes a monster gets scared by damage */
+ if (!m_ptr->monfear && !(r_ptr->flags3 & (RF3_NO_FEAR)))
+ {
+ int percentage;
+
+ /* Percentage of fully healthy */
+ percentage = (100L * m_ptr->hp) / m_ptr->maxhp;
+
+ /*
+ * Run (sometimes) if at 10% or less of max hit points,
+ * or (usually) when hit for half its current hit points
+ */
+ if (((percentage <= 10) && (rand_int(10) < percentage)) ||
+ ((dam >= m_ptr->hp) && (rand_int(100) < 80)))
+ {
+ /* Hack -- note fear */
+ (*fear) = TRUE;
+
+ /* XXX XXX XXX Hack -- Add some timed fear */
+ m_ptr->monfear = (randint(10) +
+ (((dam >= m_ptr->hp) && (percentage > 7)) ?
+ 20 : ((11 - percentage) * 5)));
+ }
+ }
+
+#endif /* ALLOW_FEAR */
+
+ /* Not dead yet */
+ return (FALSE);
+}
+
+
+
+
+
+/*
+* And now for Intelligent monster attacks (including spells).
+*
+* Original idea and code by "DRS" (David Reeves Sward).
+* Major modifications by "BEN" (Ben Harrison).
+*
+* Give monsters more intelligent attack/spell selection based on
+* observations of previous attacks on the player, and/or by allowing
+* the monster to "cheat" and know the player status.
+*
+* Maintain an idea of the player status, and use that information
+* to occasionally eliminate "ineffective" spell attacks. We could
+* also eliminate ineffective normal attacks, but there is no reason
+* for the monster to do this, since he gains no benefit.
+* Note that MINDLESS monsters are not allowed to use this code.
+* And non-INTELLIGENT monsters only use it partially effectively.
+*
+* Actually learn what the player resists, and use that information
+* to remove attacks or spells before using them. This will require
+* much less space, if I am not mistaken. Thus, each monster gets a
+* set of 32 bit flags, "smart", build from the various "SM_*" flags.
+*
+* This has the added advantage that attacks and spells are related.
+* The "smart_learn" option means that the monster "learns" the flags
+* that should be set, and "smart_cheat" means that he "knows" them.
+* So "smart_cheat" means that the "smart" field is always up to date,
+* while "smart_learn" means that the "smart" field is slowly learned.
+* Both of them have the same effect on the "choose spell" routine.
+*/
+
+
+
+/*
+* Internal probability routine
+*/
+static bool_ int_outof(monster_race *r_ptr, int prob)
+{
+ /* Non-Smart monsters are half as "smart" */
+ if (!(r_ptr->flags2 & (RF2_SMART))) prob = prob / 2;
+
+ /* Roll the dice */
+ return (rand_int(100) < prob);
+}
+
+
+
+/*
+ * Remove the "bad" spells from a spell list
+ */
+static void remove_bad_spells(int m_idx, u32b *f4p, u32b *f5p, u32b *f6p)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ u32b f4 = (*f4p);
+ u32b f5 = (*f5p);
+ u32b f6 = (*f6p);
+
+ u32b smart = 0L;
+
+
+ /* Too stupid to know anything */
+ if (r_ptr->flags2 & (RF2_STUPID)) return;
+
+
+ /* Must be cheating or learning */
+ if (!smart_cheat && !smart_learn) return;
+
+
+ /* Update acquired knowledge */
+ if (smart_learn)
+ {
+ /* Hack -- Occasionally forget player status */
+ if (m_ptr->smart && (rand_int(100) < 1)) m_ptr->smart = 0L;
+
+ /* Use the memorized flags */
+ smart = m_ptr->smart;
+ }
+
+
+ /* Cheat if requested */
+ if (smart_cheat)
+ {
+ /* Know basic info */
+ if (p_ptr->resist_acid) smart |= (SM_RES_ACID);
+ if (p_ptr->oppose_acid) smart |= (SM_OPP_ACID);
+ if (p_ptr->immune_acid) smart |= (SM_IMM_ACID);
+ if (p_ptr->resist_elec) smart |= (SM_RES_ELEC);
+ if (p_ptr->oppose_elec) smart |= (SM_OPP_ELEC);
+ if (p_ptr->immune_elec) smart |= (SM_IMM_ELEC);
+ if (p_ptr->resist_fire) smart |= (SM_RES_FIRE);
+ if (p_ptr->oppose_fire) smart |= (SM_OPP_FIRE);
+ if (p_ptr->immune_fire) smart |= (SM_IMM_FIRE);
+ if (p_ptr->resist_cold) smart |= (SM_RES_COLD);
+ if (p_ptr->oppose_cold) smart |= (SM_OPP_COLD);
+ if (p_ptr->immune_cold) smart |= (SM_IMM_COLD);
+
+ /* Know poison info */
+ if (p_ptr->resist_pois) smart |= (SM_RES_POIS);
+ if (p_ptr->oppose_pois) smart |= (SM_OPP_POIS);
+
+ /* Know special resistances */
+ if (p_ptr->resist_neth) smart |= (SM_RES_NETH);
+ if (p_ptr->resist_lite) smart |= (SM_RES_LITE);
+ if (p_ptr->resist_dark) smart |= (SM_RES_DARK);
+ if (p_ptr->resist_fear) smart |= (SM_RES_FEAR);
+ if (p_ptr->resist_conf) smart |= (SM_RES_CONF);
+ if (p_ptr->resist_chaos) smart |= (SM_RES_CHAOS);
+ if (p_ptr->resist_disen) smart |= (SM_RES_DISEN);
+ if (p_ptr->resist_blind) smart |= (SM_RES_BLIND);
+ if (p_ptr->resist_nexus) smart |= (SM_RES_NEXUS);
+ if (p_ptr->resist_sound) smart |= (SM_RES_SOUND);
+ if (p_ptr->resist_shard) smart |= (SM_RES_SHARD);
+ if (p_ptr->reflect) smart |= (SM_IMM_REFLECT);
+
+ /* Know bizarre "resistances" */
+ if (p_ptr->free_act) smart |= (SM_IMM_FREE);
+ if (!p_ptr->msp) smart |= (SM_IMM_MANA);
+ }
+
+
+ /* Nothing known */
+ if (!smart) return;
+
+
+ if (smart & (SM_IMM_ACID))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_ACID);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_ACID);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ACID);
+ }
+ else if ((smart & (SM_OPP_ACID)) && (smart & (SM_RES_ACID)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_ACID);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_ACID);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_ACID);
+ }
+ else if ((smart & (SM_OPP_ACID)) || (smart & (SM_RES_ACID)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_ACID);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_ACID);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_ACID);
+ }
+
+
+ if (smart & (SM_IMM_ELEC))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_ELEC);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_ELEC);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ELEC);
+ }
+ else if ((smart & (SM_OPP_ELEC)) && (smart & (SM_RES_ELEC)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_ELEC);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_ELEC);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_ELEC);
+ }
+ else if ((smart & (SM_OPP_ELEC)) || (smart & (SM_RES_ELEC)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_ELEC);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_ELEC);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_ELEC);
+ }
+
+
+ if (smart & (SM_IMM_FIRE))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_FIRE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_FIRE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_FIRE);
+ }
+ else if ((smart & (SM_OPP_FIRE)) && (smart & (SM_RES_FIRE)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_FIRE);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_FIRE);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_FIRE);
+ }
+ else if ((smart & (SM_OPP_FIRE)) || (smart & (SM_RES_FIRE)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_FIRE);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_FIRE);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_FIRE);
+ }
+
+
+ if (smart & (SM_IMM_COLD))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ICEE);
+ }
+ else if ((smart & (SM_OPP_COLD)) && (smart & (SM_RES_COLD)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_COLD);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_COLD);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_ICEE);
+ }
+ else if ((smart & (SM_OPP_COLD)) || (smart & (SM_RES_COLD)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_COLD);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_COLD);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_ICEE);
+ }
+
+
+ if ((smart & (SM_OPP_POIS)) && (smart & (SM_RES_POIS)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_POIS);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_POIS);
+ if (int_outof(r_ptr, 40)) f4 &= ~(RF4_BA_NUKE);
+ if (int_outof(r_ptr, 40)) f4 &= ~(RF4_BR_NUKE);
+ }
+ else if ((smart & (SM_OPP_POIS)) || (smart & (SM_RES_POIS)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_POIS);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_POIS);
+ }
+
+
+ if (smart & (SM_RES_NETH))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_NETH);
+ if (int_outof(r_ptr, 50)) f5 &= ~(RF5_BA_NETH);
+ if (int_outof(r_ptr, 50)) f5 &= ~(RF5_BO_NETH);
+ }
+
+ if (smart & (SM_RES_LITE))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_LITE);
+ }
+
+ if (smart & (SM_RES_DARK))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_DARK);
+ if (int_outof(r_ptr, 50)) f5 &= ~(RF5_BA_DARK);
+ }
+
+ if (smart & (SM_RES_FEAR))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_SCARE);
+ }
+
+ if (smart & (SM_RES_CONF))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_CONF);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_CONF);
+ }
+
+ if (smart & (SM_RES_CHAOS))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_CONF);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_CONF);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_CHAO);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BA_CHAO);
+ }
+
+ if (smart & (SM_RES_DISEN))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_DISE);
+ }
+
+ if (smart & (SM_RES_BLIND))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BLIND);
+ }
+
+ if (smart & (SM_RES_NEXUS))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_NEXU);
+ if (int_outof(r_ptr, 50)) f6 &= ~(RF6_TELE_LEVEL);
+ }
+
+ if (smart & (SM_RES_SOUND))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_SOUN);
+ }
+
+ if (smart & (SM_RES_SHARD))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_SHAR);
+ if (int_outof(r_ptr, 20)) f4 &= ~(RF4_ROCKET);
+ }
+
+ if (smart & (SM_IMM_REFLECT))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_FIRE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ACID);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ELEC);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_POIS);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_NETH);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_WATE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_MANA);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_PLAS);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ICEE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_MISSILE);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_1);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_2);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_3);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_4);
+ }
+
+ if (smart & (SM_IMM_FREE))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_HOLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_SLOW);
+ }
+
+ if (smart & (SM_IMM_MANA))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_DRAIN_MANA);
+ }
+
+ /* XXX XXX XXX No spells left? */
+ /* if (!f4 && !f5 && !f6) ... */
+
+ (*f4p) = f4;
+ (*f5p) = f5;
+ (*f6p) = f6;
+}
+
+
+/*
+ * Determine if there is a space near the player in which
+ * a summoned creature can appear
+ */
+static bool_ summon_possible(int y1, int x1)
+{
+ int y, x;
+
+ /* Start at the player's location, and check 2 grids in each dir */
+ for (y = y1 - 2; y <= y1 + 2; y++)
+ {
+ for (x = x1 - 2; x <= x1 + 2; x++)
+ {
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Only check a circular area */
+ if (distance(y1, x1, y, x) > 2) continue;
+
+ /* Hack: no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) return (FALSE);
+
+ /* ...nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START)
+ && (cave[y][x].feat <= FEAT_PATTERN_XTRA2)) continue;
+
+ /* Require empty floor grid in line of sight */
+ if (cave_empty_bold(y, x) && los(y1, x1, y, x)) return (TRUE);
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+/*
+ * Determine if a bolt spell will hit the player.
+ *
+ * This is exactly like "projectable", but it will return FALSE if a monster
+ * is in the way.
+ */
+static bool_ clean_shot(int y1, int x1, int y2, int x2)
+{
+ int dist, y, x;
+
+ /* Start at the initial location */
+ y = y1, x = x1;
+
+ /* See "project()" and "projectable()" */
+ for (dist = 0; dist <= MAX_RANGE; dist++)
+ {
+ /* Never pass through walls */
+ if (dist && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) break;
+
+ /* Never pass through monsters */
+ if (dist && cave[y][x].m_idx > 0)
+ {
+ if (is_friend(&m_list[cave[y][x].m_idx]) < 0) break;
+ }
+
+ /* Check for arrival at "final target" */
+ if ((x == x2) && (y == y2)) return (TRUE);
+
+ /* Calculate the new location */
+ mmove2(&y, &x, y1, x1, y2, x2);
+ }
+
+ /* Assume obstruction */
+ return (FALSE);
+}
+
+
+/*
+ * Cast a bolt at the player
+ * Stop if we hit a monster
+ * Affect monsters and the player
+ */
+static void bolt(int m_idx, int typ, int dam_hp)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+
+ /* Target the player with a bolt attack */
+ (void)project(m_idx, 0, p_ptr->py, p_ptr->px, dam_hp, typ, flg);
+}
+
+
+/*
+ * Return TRUE if a spell is good for hurting the player (directly).
+ */
+static bool_ spell_attack(byte spell)
+{
+ /* All RF4 spells hurt (except for shriek, multiply, summon animal) */
+ if (spell >= 96 + 3 && spell <= 96 + 31) return (TRUE);
+
+ /* Various "ball" spells */
+ if (spell >= 128 && spell <= 128 + 8) return (TRUE);
+
+ /* "Cause wounds" and "bolt" spells */
+ if (spell >= 128 + 12 && spell <= 128 + 26) return (TRUE);
+
+ /* Hand of Doom */
+ if (spell == 160 + 1) return (TRUE);
+
+ /* Doesn't hurt */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell is good for escaping.
+ */
+static bool_ spell_escape(byte spell)
+{
+ /* Blink or Teleport */
+ if (spell == 160 + 4 || spell == 160 + 5) return (TRUE);
+
+ /* Teleport the player away */
+ if (spell == 160 + 7 || spell == 160 + 8) return (TRUE);
+
+ /* Isn't good for escaping */
+ return (FALSE);
+}
+
+/*
+ * Return TRUE if a spell is good for annoying the player.
+ */
+static bool_ spell_annoy(byte spell)
+{
+ /* Shriek */
+ if (spell == 96 + 0) return (TRUE);
+
+ /* Brain smash, et al (added curses) */
+ if (spell >= 128 + 9 && spell <= 128 + 14) return (TRUE);
+
+ /* Scare, confuse, blind, slow, paralyze */
+ if (spell >= 128 + 27 && spell <= 128 + 31) return (TRUE);
+
+ /* Teleport to */
+ if (spell == 160 + 6) return (TRUE);
+
+ /* Darkness, make traps, cause amnesia */
+ if (spell >= 160 + 9 && spell <= 160 + 11) return (TRUE);
+
+ /* Doesn't annoy */
+ return (FALSE);
+}
+
+/*
+ * Return TRUE if a spell summons help.
+ */
+static bool_ spell_summon(byte spell)
+{
+ /* RF4_S_ANIMAL, RF6_S_ANIMALS */
+ if (spell == 96 + 2 || spell == 160 + 3) return (TRUE);
+ /* All other summon spells */
+ if (spell >= 160 + 13 && spell <= 160 + 31) return (TRUE);
+
+ /* Doesn't summon */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell is good in a tactical situation.
+ */
+static bool_ spell_tactic(byte spell)
+{
+ /* Blink */
+ if (spell == 160 + 4) return (TRUE);
+
+ /* Not good */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell hastes.
+ */
+static bool_ spell_haste(byte spell)
+{
+ /* Haste self */
+ if (spell == 160 + 0) return (TRUE);
+
+ /* Not a haste spell */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell is good for healing.
+ */
+static bool_ spell_heal(byte spell)
+{
+ /* Heal */
+ if (spell == 160 + 2) return (TRUE);
+
+ /* No healing */
+ return (FALSE);
+}
+
+
+/*
+ * Have a monster choose a spell from a list of "useful" spells.
+ *
+ * Note that this list does NOT include spells that will just hit
+ * other monsters, and the list is restricted when the monster is
+ * "desperate". Should that be the job of this function instead?
+ *
+ * Stupid monsters will just pick a spell randomly. Smart monsters
+ * will choose more "intelligently".
+ *
+ * Use the helper functions above to put spells into categories.
+ *
+ * This function may well be an efficiency bottleneck.
+ */
+static int choose_attack_spell(int m_idx, byte spells[], byte num)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ byte escape[96], escape_num = 0;
+ byte attack[96], attack_num = 0;
+ byte summon[96], summon_num = 0;
+ byte tactic[96], tactic_num = 0;
+ byte annoy[96], annoy_num = 0;
+ byte haste[96], haste_num = 0;
+ byte heal[96], heal_num = 0;
+
+ int i;
+
+ /* Stupid monsters choose randomly */
+ if (r_ptr->flags2 & (RF2_STUPID))
+ {
+ /* Pick at random */
+ return (spells[rand_int(num)]);
+ }
+
+ /* Categorize spells */
+ for (i = 0; i < num; i++)
+ {
+ /* Escape spell? */
+ if (spell_escape(spells[i])) escape[escape_num++] = spells[i];
+
+ /* Attack spell? */
+ if (spell_attack(spells[i])) attack[attack_num++] = spells[i];
+
+ /* Summon spell? */
+ if (spell_summon(spells[i])) summon[summon_num++] = spells[i];
+
+ /* Tactical spell? */
+ if (spell_tactic(spells[i])) tactic[tactic_num++] = spells[i];
+
+ /* Annoyance spell? */
+ if (spell_annoy(spells[i])) annoy[annoy_num++] = spells[i];
+
+ /* Haste spell? */
+ if (spell_haste(spells[i])) haste[haste_num++] = spells[i];
+
+ /* Heal spell? */
+ if (spell_heal(spells[i])) heal[heal_num++] = spells[i];
+ }
+
+ /*** Try to pick an appropriate spell type ***/
+
+ /* Hurt badly or afraid, attempt to flee */
+ if ((m_ptr->hp < m_ptr->maxhp / 3) || m_ptr->monfear)
+ {
+ /* Choose escape spell if possible */
+ if (escape_num) return (escape[rand_int(escape_num)]);
+ }
+
+ /* Still hurt badly, couldn't flee, attempt to heal */
+ if (m_ptr->hp < m_ptr->maxhp / 3)
+ {
+ /* Choose heal spell if possible */
+ if (heal_num) return (heal[rand_int(heal_num)]);
+ }
+
+ /* Player is close and we have attack spells, blink away */
+ if ((distance(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx) < 4) && attack_num && (rand_int(100) < 75))
+ {
+ /* Choose tactical spell */
+ if (tactic_num) return (tactic[rand_int(tactic_num)]);
+ }
+
+ /* We're hurt (not badly), try to heal */
+ if ((m_ptr->hp < m_ptr->maxhp * 3 / 4) && (rand_int(100) < 75))
+ {
+ /* Choose heal spell if possible */
+ if (heal_num) return (heal[rand_int(heal_num)]);
+ }
+
+ /* Summon if possible (sometimes) */
+ if (summon_num && (rand_int(100) < 50))
+ {
+ /* Choose summon spell */
+ return (summon[rand_int(summon_num)]);
+ }
+
+ /* Attack spell (most of the time) */
+ if (attack_num && (rand_int(100) < 85))
+ {
+ /* Choose attack spell */
+ return (attack[rand_int(attack_num)]);
+ }
+
+ /* Try another tactical spell (sometimes) */
+ if (tactic_num && (rand_int(100) < 50))
+ {
+ /* Choose tactic spell */
+ return (tactic[rand_int(tactic_num)]);
+ }
+
+ /* Haste self if we aren't already somewhat hasted (rarely) */
+ if (haste_num && (rand_int(100) < (20 + m_ptr->speed - m_ptr->mspeed)))
+ {
+ /* Choose haste spell */
+ return (haste[rand_int(haste_num)]);
+ }
+
+ /* Annoy player (most of the time) */
+ if (annoy_num && (rand_int(100) < 85))
+ {
+ /* Choose annoyance spell */
+ return (annoy[rand_int(annoy_num)]);
+ }
+
+ /* Choose no spell */
+ return (0);
+}
+
+
+/*
+ * Cast a breath (or ball) attack at the player
+ * Pass over any monsters that may be in the way
+ * Affect grids, objects, monsters, and the player
+ */
+static void breath(int m_idx, int typ, int dam_hp, int rad)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Determine the radius of the blast */
+ if (rad < 1) rad = (r_ptr->flags2 & (RF2_POWERFUL)) ? 3 : 2;
+
+ /* Target the player with a ball attack */
+ (void)project(m_idx, rad, p_ptr->py, p_ptr->px, dam_hp, typ, flg);
+}
+
+
+/*
+ * Monster casts a breath (or ball) attack at another monster.
+ * Pass over any monsters that may be in the way
+ * Affect grids, objects, monsters, and the player
+ */
+static void monst_breath_monst(int m_idx, int y, int x, int typ, int dam_hp, int rad)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Determine the radius of the blast */
+ if (rad < 1) rad = (r_ptr->flags2 & (RF2_POWERFUL)) ? 3 : 2;
+
+ (void)project(m_idx, rad, y, x, dam_hp, typ, flg);
+}
+
+
+/*
+ * Monster casts a bolt at another monster
+ * Stop if we hit a monster
+ * Affect monsters and the player
+ */
+static void monst_bolt_monst(int m_idx, int y, int x, int typ, int dam_hp)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+
+ (void)project(m_idx, 0, y, x, dam_hp, typ, flg);
+}
+
+
+void monster_msg(cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ if (disturb_other)
+ msg_print(buf);
+ else
+ {
+ message_add(MESSAGE_MSG, buf, TERM_WHITE);
+ p_ptr->window |= PW_MESSAGE;
+ }
+}
+
+void cmonster_msg(char a, cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ if (disturb_other)
+ cmsg_print(a, buf);
+ else
+ {
+ message_add(MESSAGE_MSG, buf, a);
+ p_ptr->window |= PW_MESSAGE;
+ }
+}
+
+
+/*
+ * Monster tries to 'cast a spell' (or breath, etc)
+ * at another monster.
+ */
+int monst_spell_monst_spell = -1;
+static bool_ monst_spell_monst(int m_idx)
+{
+ int y = 0, x = 0;
+ int i = 1, k, t_idx;
+ int chance, thrown_spell, count = 0;
+ byte spell[96], num = 0;
+ char m_name[80], t_name[80];
+ char m_poss[80];
+ char ddesc[80];
+ int rlev; /* monster level */
+ monster_type *m_ptr = &m_list[m_idx]; /* Attacker */
+ monster_race *r_ptr = race_inf(m_ptr);
+ monster_type *t_ptr; /* Putative target */
+ monster_race *tr_ptr;
+ u32b f4, f5, f6; /* racial spell flags */
+ bool_ direct = TRUE;
+ bool_ wake_up = FALSE;
+
+ /* Extract the blind-ness */
+ bool_ blind = (p_ptr->blind ? TRUE : FALSE);
+
+ /* Extract the "see-able-ness" */
+ bool_ seen = (!blind && m_ptr->ml);
+
+ bool_ see_m;
+ bool_ see_t;
+ bool_ see_either;
+ bool_ see_both;
+
+ bool_ friendly = FALSE;
+
+ if (is_friend(m_ptr) > 0) friendly = TRUE;
+
+ /* Cannot cast spells when confused */
+ if (m_ptr->confused) return (FALSE);
+
+ /* Hack -- Extract the spell probability */
+ chance = (r_ptr->freq_inate + r_ptr->freq_spell) / 2;
+
+ /* Not allowed to cast spells */
+ if ((!chance) && (monst_spell_monst_spell == -1)) return (FALSE);
+
+ if ((rand_int(100) >= chance) && (monst_spell_monst_spell == -1)) return (FALSE);
+
+ /* Target location */
+ if (m_ptr->target > -1)
+ {
+ if (m_ptr->target > 0)
+ {
+ i = m_ptr->target;
+ }
+ else return FALSE;
+ }
+ else return FALSE;
+
+
+ {
+ t_idx = i;
+ t_ptr = &m_list[t_idx];
+ tr_ptr = race_inf(t_ptr);
+
+ /* Hack -- no fighting >100 squares from player */
+ if (t_ptr->cdis > MAX_RANGE) return FALSE;
+
+ /* Monster must be projectable */
+ if (!projectable(m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx)) return FALSE;
+
+ /* OK -- we-ve got a target */
+ y = t_ptr->fy;
+ x = t_ptr->fx;
+
+ /* Extract the monster level */
+ rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+ /* Extract the racial spell flags */
+ f4 = r_ptr->flags4;
+ f5 = r_ptr->flags5;
+ f6 = r_ptr->flags6;
+
+ /* Hack -- allow "desperate" spells */
+ if ((r_ptr->flags2 & (RF2_SMART)) &&
+ (m_ptr->hp < m_ptr->maxhp / 10) &&
+ (rand_int(100) < 50))
+ {
+ /* Require intelligent spells */
+ f4 &= (RF4_INT_MASK);
+ f5 &= (RF5_INT_MASK);
+ f6 &= (RF6_INT_MASK);
+
+ /* No spells left */
+ if ((!f4 && !f5 && !f6) && (monst_spell_monst_spell == -1)) return (FALSE);
+ }
+
+ /* Extract the "inate" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f4 & (1L << k)) spell[num++] = k + 32 * 3;
+ }
+
+ /* Extract the "normal" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f5 & (1L << k)) spell[num++] = k + 32 * 4;
+ }
+
+ /* Extract the "bizarre" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f6 & (1L << k)) spell[num++] = k + 32 * 5;
+ }
+
+ /* No spells left */
+ if (!num) return (FALSE);
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) return (FALSE);
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) return (FALSE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0x00);
+
+ /* Get the monster possessive ("his"/"her"/"its") */
+ monster_desc(m_poss, m_ptr, 0x22);
+
+ /* Get the target's name (or "it") */
+ monster_desc(t_name, t_ptr, 0x00);
+
+ /* Hack -- Get the "died from" name */
+ monster_desc(ddesc, m_ptr, 0x88);
+
+ /* Choose a spell to cast */
+ thrown_spell = spell[rand_int(num)];
+
+ /* Force a spell ? */
+ if (monst_spell_monst_spell > -1)
+ {
+ thrown_spell = monst_spell_monst_spell;
+ monst_spell_monst_spell = -1;
+ }
+
+ see_m = seen;
+ see_t = (!blind && t_ptr->ml);
+ see_either = (see_m || see_t);
+ see_both = (see_m && see_t);
+
+ switch (thrown_spell)
+ {
+ /* RF4_SHRIEK */
+ case 96 + 0:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!see_m) monster_msg("You hear a shriek.");
+ else monster_msg("%^s shrieks at %s.", m_name, t_name);
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF4_MULTIPLY */
+ case 96 + 1:
+ {
+ break;
+ }
+
+ /* RF4_S_ANIMAL */
+ case 96 + 2:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons an animal!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_ANIMAL);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF4_ROCKET */
+ case 96 + 3:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear an explosion!");
+ else if (blind) monster_msg("%^s shoots something.", m_name);
+ else monster_msg("%^s fires a rocket at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_ROCKET,
+ ((m_ptr->hp / 4) > 800 ? 800 : (m_ptr->hp / 4)), 2);
+ break;
+ }
+
+ /* RF4_ARROW_1 */
+ case 96 + 4:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear a strange noise.");
+ else if (blind) monster_msg("%^s makes a strange noise.", m_name);
+ else monster_msg("%^s fires an arrow at %s.", m_name, t_name);
+ sound(SOUND_SHOOT);
+ monst_bolt_monst(m_idx, y, x, GF_ARROW, damroll(1, 6));
+ break;
+ }
+
+ /* RF4_ARROW_2 */
+ case 96 + 5:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear a strange noise.");
+ else if (blind) monster_msg("%^s makes a strange noise.", m_name);
+ else monster_msg("%^s fires an arrow at %s.", m_name, t_name);
+ sound(SOUND_SHOOT);
+ monst_bolt_monst(m_idx, y, x, GF_ARROW, damroll(3, 6));
+ break;
+ }
+
+ /* RF4_ARROW_3 */
+ case 96 + 6:
+ {
+ if (disturb_other) disturb(1, 0);
+
+ if (!see_either) monster_msg("You hear a strange noise.");
+ else if (blind) monster_msg("%^s makes a strange noise.", m_name);
+ else monster_msg("%^s fires a missile at %s.", m_name, t_name);
+ sound(SOUND_SHOOT);
+ monst_bolt_monst(m_idx, y, x, GF_ARROW, damroll(5, 6));
+ break;
+ }
+
+ /* RF4_ARROW_4 */
+ case 96 + 7:
+ {
+ if (!see_either) monster_msg("You hear a strange noise.");
+ else if (disturb_other) disturb(1, 0);
+ if (blind) monster_msg("%^s makes a strange noise.", m_name);
+ else monster_msg("%^s fires a missile at %s.", m_name, t_name);
+ sound(SOUND_SHOOT);
+ monst_bolt_monst(m_idx, y, x, GF_ARROW, damroll(7, 6));
+ break;
+ }
+
+ /* RF4_BR_ACID */
+ case 96 + 8:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes acid at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_ACID,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_ELEC */
+ case 96 + 9:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes lightning at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_ELEC,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_FIRE */
+ case 96 + 10:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes fire at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_FIRE,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_COLD */
+ case 96 + 11:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes frost at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_COLD,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_POIS */
+ case 96 + 12:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes gas at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_POIS,
+ ((m_ptr->hp / 3) > 800 ? 800 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_NETH */
+ case 96 + 13:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes nether at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_NETHER,
+ ((m_ptr->hp / 6) > 550 ? 550 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_LITE */
+ case 96 + 14:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes light at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_LITE,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_DARK */
+ case 96 + 15:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes darkness at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_DARK,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_CONF */
+ case 96 + 16:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes confusion at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_CONFUSION,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_SOUN */
+ case 96 + 17:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes sound at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_SOUND,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_CHAO */
+ case 96 + 18:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes chaos at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_CHAOS,
+ ((m_ptr->hp / 6) > 600 ? 600 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_DISE */
+ case 96 + 19:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes disenchantment at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_DISENCHANT,
+ ((m_ptr->hp / 6) > 500 ? 500 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_NEXU */
+ case 96 + 20:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes nexus at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_NEXUS,
+ ((m_ptr->hp / 3) > 250 ? 250 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_TIME */
+ case 96 + 21:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes time at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_TIME,
+ ((m_ptr->hp / 3) > 150 ? 150 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_INER */
+ case 96 + 22:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes inertia at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_INERTIA,
+ ((m_ptr->hp / 6) > 200 ? 200 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_GRAV */
+ case 96 + 23:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes gravity at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_GRAVITY,
+ ((m_ptr->hp / 3) > 200 ? 200 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_SHAR */
+ case 96 + 24:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes shards at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_SHARDS,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_PLAS */
+ case 96 + 25:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes plasma at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_PLASMA,
+ ((m_ptr->hp / 6) > 150 ? 150 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_WALL */
+ case 96 + 26:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes force at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_FORCE,
+ ((m_ptr->hp / 6) > 200 ? 200 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_MANA */
+ case 96 + 27:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes magical energy at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_MANA,
+ ((m_ptr->hp / 3) > 250 ? 250 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BA_NUKE */
+ case 96 + 28:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear someone mumble.");
+ else if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a ball of radiation at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_NUKE,
+ (rlev + damroll(10, 6)), 2);
+ break;
+ }
+
+ /* RF4_BR_NUKE */
+ case 96 + 29:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes toxic waste at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_NUKE,
+ ((m_ptr->hp / 3) > 800 ? 800 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BA_CHAO */
+ case 96 + 30:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear someone mumble frighteningly.");
+ else if (blind) monster_msg("%^s mumbles frighteningly.", m_name);
+ else monster_msg("%^s invokes a raw Chaos upon %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_CHAOS,
+ (rlev * 2) + damroll(10, 10), 4);
+ break;
+ }
+
+ /* RF4_BR_DISI -> Breathe Disintegration */
+ case 96 + 31:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes disintegration at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_DISINTEGRATE,
+ ((m_ptr->hp / 3) > 300 ? 300 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF5_BA_ACID */
+ case 128 + 0:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts an acid ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_ACID, randint(rlev * 3) + 15, 2);
+ break;
+ }
+
+ /* RF5_BA_ELEC */
+ case 128 + 1:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a lightning ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_ELEC, randint(rlev * 3 / 2) + 8, 2);
+ break;
+ }
+
+ /* RF5_BA_FIRE */
+ case 128 + 2:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a fire ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_FIRE, randint(rlev * 7 / 2) + 10, 2);
+ break;
+ }
+
+ /* RF5_BA_COLD */
+ case 128 + 3:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a frost ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_COLD, randint(rlev * 3 / 2) + 10, 2);
+ break;
+ }
+
+ /* RF5_BA_POIS */
+ case 128 + 4:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a stinking cloud at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_POIS, damroll(12, 2), 2);
+ break;
+ }
+
+ /* RF5_BA_NETH */
+ case 128 + 5:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a nether ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_NETHER, (50 + damroll(10, 10) + rlev), 2);
+ break;
+ }
+
+ /* RF5_BA_WATE */
+ case 128 + 6:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s gestures fluidly at %s.", m_name, t_name);
+ monster_msg("%^s is engulfed in a whirlpool.", t_name);
+ monst_breath_monst(m_idx, y, x, GF_WATER, randint(rlev * 5 / 2) + 50, 4);
+ break;
+ }
+
+ /* RF5_BA_MANA */
+ case 128 + 7:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble powerfully.");
+ else
+ if (blind) monster_msg("%^s mumbles powerfully.", m_name);
+ else monster_msg("%^s invokes a mana storm upon %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_MANA, (rlev * 5) + damroll(10, 10), 4);
+ break;
+ }
+
+ /* RF5_BA_DARK */
+ case 128 + 8:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble powerfully.");
+ else
+ if (blind) monster_msg("%^s mumbles powerfully.", m_name);
+ else monster_msg("%^s invokes a darkness storm upon %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_DARK, (rlev * 5) + damroll(10, 10), 4);
+ break;
+ }
+
+ /* RF5_DRAIN_MANA */
+ case 128 + 9:
+ {
+ /* Attack power */
+ int r1 = (randint(rlev) / 2) + 1;
+
+ if (see_m)
+ {
+ /* Basic message */
+ monster_msg("%^s draws psychic energy from %s.", m_name, t_name);
+ }
+
+ /* Heal the monster */
+ if (m_ptr->hp < m_ptr->maxhp)
+ {
+ if (!(tr_ptr->flags4 || tr_ptr->flags5 || tr_ptr->flags6))
+ {
+ if (see_both)
+ monster_msg("%^s is unaffected!", t_name);
+ }
+ else
+ {
+ /* Heal */
+ m_ptr->hp += (6 * r1);
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Special message */
+ if (seen)
+ {
+ monster_msg("%^s appears healthier.", m_name);
+ }
+ }
+ }
+
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_MIND_BLAST */
+ case 128 + 10:
+ {
+ if (!direct) break;
+
+ if (disturb_other) disturb(1, 0);
+
+ if (!seen)
+ {
+ /* */
+ }
+ else
+ {
+ monster_msg("%^s gazes intently at %s.", m_name, t_name);
+ }
+
+ /* Attempt a saving throw */
+ if ((tr_ptr->flags1 & (RF1_UNIQUE)) ||
+ (tr_ptr->flags3 & (RF3_NO_CONF)) ||
+ (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (tr_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) tr_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* No obvious effect */
+ if (see_t)
+ {
+ monster_msg("%^s is unaffected!", t_name);
+ }
+ }
+ else
+ {
+ bool_ fear;
+ monster_msg("%^s is blasted by psionic energy.", t_name);
+ t_ptr->confused += rand_int(4) + 4;
+
+ mon_take_hit_mon(m_idx, t_idx, damroll(8, 8), &fear, " collapses, a mindless husk.");
+ }
+
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_BRAIN_SMASH */
+ case 128 + 11:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!seen)
+ {
+ /* */
+ }
+ else
+ {
+ monster_msg("%^s gazes intently at %s.", m_name, t_name);
+ }
+
+ /* Attempt a saving throw */
+ if ((tr_ptr->flags1 & (RF1_UNIQUE)) ||
+ (tr_ptr->flags3 & (RF3_NO_CONF)) ||
+ (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (tr_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) tr_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+ /* No obvious effect */
+ if (see_t)
+ {
+ monster_msg("%^s is unaffected!", t_name);
+ }
+ }
+ else
+ {
+ bool_ fear;
+ if (see_t)
+ {
+ monster_msg("%^s is blasted by psionic energy.", t_name);
+ }
+ t_ptr->confused += rand_int(4) + 4;
+ t_ptr->mspeed -= rand_int(4) + 4;
+ t_ptr->stunned += rand_int(4) + 4;
+ mon_take_hit_mon(m_idx, t_idx, damroll(12, 15), &fear, " collapses, a mindless husk.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_CAUSE_1 */
+ case 128 + 12:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s points at %s and curses.", m_name, t_name);
+ if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ else
+ {
+ bool_ fear;
+ mon_take_hit_mon(m_idx, t_idx, damroll(3, 8), &fear, " is destroyed.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_CAUSE_2 */
+ case 128 + 13:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s points at %s and curses horribly.", m_name, t_name);
+ if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ else
+ {
+ bool_ fear;
+ mon_take_hit_mon(m_idx, t_idx, damroll(8, 8), &fear, " is destroyed.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_CAUSE_3 */
+ case 128 + 14:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s points at %s, incanting terribly!", m_name, t_name);
+ if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ else
+ {
+ bool_ fear;
+ mon_take_hit_mon(m_idx, t_idx, damroll(10, 15), &fear, " is destroyed.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_CAUSE_4 */
+ case 128 + 15:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s points at %s, screaming the word 'DIE!'", m_name, t_name);
+ if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ else
+ {
+ bool_ fear;
+ mon_take_hit_mon(m_idx, t_idx, damroll(15, 15), &fear, " is destroyed.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_BO_ACID */
+ case 128 + 16:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts an acid bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_ACID,
+ damroll(7, 8) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_BO_ELEC */
+ case 128 + 17:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a lightning bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_ELEC,
+ damroll(4, 8) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_BO_FIRE */
+ case 128 + 18:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a fire bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_FIRE,
+ damroll(9, 8) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_BO_COLD */
+ case 128 + 19:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a frost bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_COLD,
+ damroll(6, 8) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_BO_POIS */
+ case 128 + 20:
+ {
+ /* XXX XXX XXX */
+ break;
+ }
+
+ /* RF5_BO_NETH */
+ case 128 + 21:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a nether bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_NETHER,
+ 30 + damroll(5, 5) + (rlev * 3) / 2);
+ break;
+ }
+
+ /* RF5_BO_WATE */
+ case 128 + 22:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a water bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_WATER,
+ damroll(10, 10) + (rlev));
+ break;
+ }
+
+ /* RF5_BO_MANA */
+ case 128 + 23:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a mana bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_MANA,
+ randint(rlev * 7 / 2) + 50);
+ break;
+ }
+
+ /* RF5_BO_PLAS */
+ case 128 + 24:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a plasma bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_PLASMA,
+ 10 + damroll(8, 7) + (rlev));
+ break;
+ }
+
+ /* RF5_BO_ICEE */
+ case 128 + 25:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts an ice bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_ICE,
+ damroll(6, 6) + (rlev));
+ break;
+ }
+
+ /* RF5_MISSILE */
+ case 128 + 26:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a magic missile at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_MISSILE,
+ damroll(2, 6) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_SCARE */
+ case 128 + 27:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles, and you hear scary noises.", m_name);
+ else monster_msg("%^s casts a fearful illusion at %s.", m_name, t_name);
+ if (tr_ptr->flags3 & RF3_NO_FEAR)
+ {
+ if (see_t) monster_msg("%^s refuses to be frightened.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s refuses to be frightened.", t_name);
+ }
+ else
+ {
+ if (!(t_ptr->monfear) && see_t) monster_msg("%^s flees in terror!", t_name);
+ t_ptr->monfear += rand_int(4) + 4;
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_BLIND */
+ case 128 + 28:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a spell, burning %s%s eyes.", m_name, t_name,
+ (!strcmp(t_name, "it") ? "s" : "'s"));
+ if (tr_ptr->flags3 & RF3_NO_CONF) /* Simulate blindness with confusion */
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else
+ {
+ if (see_t) monster_msg("%^s is blinded!", t_name);
+ t_ptr->confused += 12 + (byte)rand_int(4);
+ }
+ wake_up = TRUE;
+ break;
+
+ }
+
+ /* RF5_CONF */
+ case 128 + 29:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles, and you hear puzzling noises.", m_name);
+ else monster_msg("%^s creates a mesmerising illusion in front of %s.", m_name, t_name);
+ if (tr_ptr->flags3 & RF3_NO_CONF)
+ {
+ if (see_t) monster_msg("%^s disbelieves the feeble spell.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s disbelieves the feeble spell.", t_name);
+ }
+ else
+ {
+ if (see_t) monster_msg("%^s seems confused.", t_name);
+ t_ptr->confused += 12 + (byte)rand_int(4);
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_SLOW */
+ case 128 + 30:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!blind && see_either) monster_msg("%^s drains power from %s%s muscles.", m_name, t_name,
+ (!strcmp(t_name, "it") ? "s" : "'s"));
+ if (tr_ptr->flags1 & RF1_UNIQUE)
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else
+ {
+ t_ptr->mspeed -= 10;
+ if (see_t) monster_msg("%^s starts moving slower.", t_name);
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_HOLD */
+ case 128 + 31:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!blind && see_m) monster_msg("%^s stares intently at %s.", m_name, t_name);
+ if ((tr_ptr->flags1 & RF1_UNIQUE) ||
+ (tr_ptr->flags3 & RF3_NO_STUN))
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else
+ {
+ t_ptr->stunned += randint(4) + 4;
+ if (see_t) monster_msg("%^s is paralyzed!", t_name);
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+
+ /* RF6_HASTE */
+ case 160 + 0:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m)
+ {
+ monster_msg("%^s mumbles.", m_name);
+ }
+ else
+ {
+ monster_msg("%^s concentrates on %s body.", m_name, m_poss);
+ }
+
+ /* Allow quick speed increases to base+10 */
+ if (m_ptr->mspeed < m_ptr->speed + 10)
+ {
+ if (see_m) monster_msg("%^s starts moving faster.", m_name);
+ m_ptr->mspeed += 10;
+ }
+
+ /* Allow small speed increases to base+20 */
+ else if (m_ptr->mspeed < m_ptr->speed + 20)
+ {
+ if (see_m) monster_msg("%^s starts moving faster.", m_name);
+ m_ptr->mspeed += 2;
+ }
+
+ break;
+ }
+
+ /* RF6_HAND_DOOM */
+ case 160 + 1:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!see_m) monster_msg("You hear someone invoke the Hand of Doom!");
+ else if (!blind) monster_msg("%^s invokes the Hand of Doom on %s.", m_name, t_name);
+ else
+ monster_msg ("You hear someone invoke the Hand of Doom!");
+ if (tr_ptr->flags1 & RF1_UNIQUE)
+ {
+ if (!blind && see_t) monster_msg("^%s is unaffected!", t_name);
+ }
+ else
+ {
+ if (((m_ptr->level) + randint(20)) >
+ ((t_ptr->level) + 10 + randint(20)))
+ {
+ t_ptr->hp = t_ptr->hp
+ - (((s32b) ((65 + randint(25)) * (t_ptr->hp))) / 100);
+ if (t_ptr->hp < 1) t_ptr->hp = 1;
+ }
+ else
+ {
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ }
+
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF6_HEAL */
+ case 160 + 2:
+ {
+ if (disturb_other) disturb(1, 0);
+
+ /* Message */
+ if (blind || !see_m)
+ {
+ monster_msg("%^s mumbles.", m_name);
+ }
+ else
+ {
+ monster_msg("%^s concentrates on %s wounds.", m_name, m_poss);
+ }
+
+ /* Heal some */
+ m_ptr->hp += (rlev * 6);
+
+ /* Fully healed */
+ if (m_ptr->hp >= m_ptr->maxhp)
+ {
+ /* Fully healed */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Message */
+ if (seen)
+ {
+ monster_msg("%^s looks completely healed!", m_name);
+ }
+ else
+ {
+ monster_msg("%^s sounds completely healed!", m_name);
+ }
+ }
+
+ /* Partially healed */
+ else
+ {
+ /* Message */
+ if (seen)
+ {
+ monster_msg("%^s looks healthier.", m_name);
+ }
+ else
+ {
+ monster_msg("%^s sounds healthier.", m_name);
+ }
+ }
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Cancel fear */
+ if (m_ptr->monfear)
+ {
+ /* Cancel fear */
+ m_ptr->monfear = 0;
+
+ /* Message */
+ if (see_m) monster_msg("%^s recovers %s courage.", m_name, m_poss);
+ }
+
+ break;
+ }
+
+ /* RF6_S_ANIMALS */
+ case 160 + 3:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons some animals!", m_name);
+ for (k = 0; k < 4; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_ANIMAL);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_BLINK */
+ case 160 + 4:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (see_m) monster_msg("%^s blinks away.", m_name);
+ teleport_away(m_idx, 10);
+ break;
+ }
+
+ /* RF6_TPORT */
+ case 160 + 5:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ else
+ {
+ if (disturb_other) disturb(1, 0);
+ if (see_m) monster_msg("%^s teleports away.", m_name);
+ teleport_away(m_idx, MAX_SIGHT * 2 + 5);
+ break;
+ }
+ }
+
+ /* RF6_TELE_TO */
+ case 160 + 6:
+ {
+ /* Not implemented */
+ break;
+ }
+
+ /* RF6_TELE_AWAY */
+ case 160 + 7:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break;
+
+ if (!direct) break;
+ else
+ {
+ bool_ resists_tele = FALSE;
+ if (disturb_other) disturb(1, 0);
+ monster_msg("%^s teleports %s away.", m_name, t_name);
+
+
+ if (tr_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (tr_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (see_t)
+ {
+ tr_ptr->r_flags3 |= RF3_RES_TELE;
+ monster_msg("%^s is unaffected!", t_name);
+ }
+ resists_tele = TRUE;
+ }
+ else if (t_ptr->level > randint(100))
+ {
+ if (see_t)
+ {
+ tr_ptr->r_flags3 |= RF3_RES_TELE;
+ monster_msg("%^s resists!", t_name);
+ }
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ teleport_away(t_idx, MAX_SIGHT * 2 + 5);
+ }
+ }
+
+ break;
+ }
+
+ /* RF6_TELE_LEVEL */
+ case 160 + 8:
+ {
+ /* Not implemented */
+ break;
+ }
+
+ /* RF6_DARKNESS */
+ case 160 + 9:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s gestures in shadow.", m_name);
+ if (seen)
+ monster_msg("%^s is surrounded by darkness.", t_name);
+ (void)project(m_idx, 3, y, x, 0, GF_DARK_WEAK, PROJECT_GRID | PROJECT_KILL);
+ /* Lite up the room */
+ unlite_room(y, x);
+ break;
+ }
+
+ /* RF6_TRAPS */
+ case 160 + 10:
+ {
+ /* Not implemented */
+ break;
+ }
+
+ /* RF6_FORGET */
+ case 160 + 11:
+ {
+ /* Not implemented */
+ break;
+ }
+
+ /* RF6_ANIM_DEAD */
+ case 160 + 12:
+ {
+ break;
+ }
+
+ /* RF6_S_BUG */
+ case 160 + 13:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically codes some software bugs.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_BUG, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_BUG);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_RNG */
+ case 160 + 14:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically codes some RNGs.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_RNG, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_RNG);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+
+ /* RF6_S_THUNDERLORD */
+ case 160 + 15:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a Thunderlord!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_THUNDERLORD, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_THUNDERLORD);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_SUMMON_KIN */
+ case 160 + 16:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons %s %s.",
+ m_name, m_poss,
+ ((r_ptr->flags1) & RF1_UNIQUE ?
+ "minions" : "kin"));
+ summon_kin_type = r_ptr->d_char; /* Big hack */
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_KIN, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_KIN);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+
+
+ break;
+ }
+
+ /* RF6_S_HI_DEMON */
+ case 160 + 17:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons greater demons!", m_name);
+ if (blind && count) monster_msg("You hear heavy steps nearby.");
+ if (friendly)
+ summon_specific_friendly(y, x, rlev, SUMMON_HI_DEMON, TRUE);
+ else
+ summon_cyber();
+ break;
+ }
+
+ /* RF6_S_MONSTER */
+ case 160 + 18:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons help!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, 0);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_MONSTERS */
+ case 160 + 19:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons monsters!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, 0);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_ANT */
+ case 160 + 20:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons ants.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_ANT, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_ANT);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_SPIDER */
+ case 160 + 21:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons spiders.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_SPIDER, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_SPIDER);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HOUND */
+ case 160 + 22:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons hounds.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HOUND, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HOUND);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HYDRA */
+ case 160 + 23:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons hydras.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HYDRA, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HYDRA);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_ANGEL */
+ case 160 + 24:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons an angel!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_ANGEL, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_ANGEL);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_DEMON */
+ case 160 + 25:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a demon!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_DEMON, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_DEMON);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_UNDEAD */
+ case 160 + 26:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons an undead adversary!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_UNDEAD, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_UNDEAD);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_DRAGON */
+ case 160 + 27:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a dragon!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_DRAGON, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_DRAGON);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HI_UNDEAD */
+ case 160 + 28:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons greater undead!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HI_UNDEAD_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+ if (blind && count)
+ {
+ monster_msg("You hear many creepy things appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_HI_DRAGON */
+ case 160 + 29:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons ancient dragons!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HI_DRAGON_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HI_DRAGON);
+ }
+ if (blind && count)
+ {
+ monster_msg("You hear many powerful things appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_WRAITH */
+ case 160 + 30:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a wraith!", m_name);
+
+
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_WRAITH);
+ }
+
+ if (blind && count)
+ {
+ monster_msg("You hear immortal beings appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_UNIQUE */
+ case 160 + 31:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons special opponents!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ if (!friendly)
+ count += summon_specific(y, x, rlev, SUMMON_UNIQUE);
+ }
+ for (k = 0; k < 8; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HI_UNDEAD_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+ if (blind && count)
+ {
+ monster_msg("You hear many powerful things appear nearby.");
+ }
+ break;
+ }
+ }
+
+ if (wake_up)
+ {
+ t_ptr->csleep = 0;
+ }
+
+
+ /* Remember what the monster did, if we saw it */
+ if (seen)
+ {
+ /* Inate spell */
+ if (thrown_spell < 32*4)
+ {
+ r_ptr->r_flags4 |= (1L << (thrown_spell - 32 * 3));
+ if (r_ptr->r_cast_inate < MAX_UCHAR) r_ptr->r_cast_inate++;
+ }
+
+ /* Bolt or Ball */
+ else if (thrown_spell < 32*5)
+ {
+ r_ptr->r_flags5 |= (1L << (thrown_spell - 32 * 4));
+ if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++;
+ }
+
+ /* Special spell */
+ else if (thrown_spell < 32*6)
+ {
+ r_ptr->r_flags6 |= (1L << (thrown_spell - 32 * 5));
+ if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++;
+ }
+ }
+
+ /* Always take note of monsters that kill you ---
+ * even accidentally */
+ if (death && (r_ptr->r_deaths < MAX_SHORT))
+ {
+ r_ptr->r_deaths++;
+ }
+
+ /* A spell was cast */
+ return (TRUE);
+ }
+
+ /* No enemy found */
+ return (FALSE);
+}
+
+
+void curse_equipment(int chance, int heavy_chance)
+{
+ bool_ changed = FALSE;
+ u32b o1, o2, o3, o4, esp, o5;
+ object_type * o_ptr =
+ &p_ptr->inventory[rand_range(INVEN_WIELD, INVEN_TOTAL - 1)];
+
+ if (randint(100) > chance) return;
+
+ if (!(o_ptr->k_idx)) return;
+
+ object_flags(o_ptr, &o1, &o2, &o3, &o4, &o5, &esp);
+
+
+ /* Extra, biased saving throw for blessed items */
+ if ((o3 & (TR3_BLESSED)) && (randint(888) > chance))
+ {
+ char o_name[256];
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("Your %s resist%s cursing!", o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+ /* Hmmm -- can we wear multiple items? If not, this is unnecessary */
+ return;
+ }
+
+ if ((randint(100) <= heavy_chance) &&
+ (o_ptr->name1 || o_ptr->name2 || o_ptr->art_name))
+ {
+ if (!(o3 & TR3_HEAVY_CURSE))
+ changed = TRUE;
+ o_ptr->art_flags3 |= TR3_HEAVY_CURSE;
+ o_ptr->art_flags3 |= TR3_CURSED;
+ o_ptr->ident |= IDENT_CURSED;
+ }
+ else
+ {
+ if (!(o_ptr->ident & (IDENT_CURSED)))
+ changed = TRUE;
+ o_ptr->art_flags3 |= TR3_CURSED;
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ if (changed)
+ {
+ msg_print("There is a malignant black aura surrounding you...");
+ if (o_ptr->note)
+ {
+ if (streq(quark_str(o_ptr->note), "uncursed"))
+ {
+ o_ptr->note = 0;
+ }
+ }
+ }
+}
+
+
+void curse_equipment_dg(int chance, int heavy_chance)
+{
+ bool_ changed = FALSE;
+ u32b o1, o2, o3, o4, esp, o5;
+ object_type * o_ptr =
+ &p_ptr->inventory[rand_range(INVEN_WIELD, INVEN_TOTAL - 1)];
+
+ if (randint(100) > chance) return;
+
+ if (!(o_ptr->k_idx)) return;
+
+ object_flags(o_ptr, &o1, &o2, &o3, &o4, &o5, &esp);
+
+
+ /* Extra, biased saving throw for blessed items */
+ if ((o3 & (TR3_BLESSED)) && (randint(888) > chance))
+ {
+ char o_name[256];
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("Your %s resist%s cursing!", o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+ /* Hmmm -- can we wear multiple items? If not, this is unnecessary */
+ /* DG -- Yes we can, in the quiver */
+ return;
+ }
+
+ if ((randint(100) <= heavy_chance) &&
+ (o_ptr->name1 || o_ptr->name2 || o_ptr->art_name))
+ {
+ if (!(o3 & TR3_HEAVY_CURSE))
+ changed = TRUE;
+ o_ptr->art_flags3 |= TR3_HEAVY_CURSE;
+ o_ptr->art_flags3 |= TR3_CURSED;
+ o_ptr->art_flags4 |= TR4_DG_CURSE;
+ o_ptr->ident |= IDENT_CURSED;
+ }
+ else
+ {
+ if (!(o_ptr->ident & (IDENT_CURSED)))
+ changed = TRUE;
+ o_ptr->art_flags3 |= TR3_CURSED;
+ o_ptr->art_flags4 |= TR4_DG_CURSE;
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ if (changed)
+ {
+ msg_print("There is a malignant black aura surrounding you...");
+ if (o_ptr->note)
+ {
+ if (streq(quark_str(o_ptr->note), "uncursed"))
+ {
+ o_ptr->note = 0;
+ }
+ }
+ }
+}
+
+
+/*
+ * Creatures can cast spells, shoot missiles, and breathe.
+ *
+ * Returns "TRUE" if a spell (or whatever) was (successfully) cast.
+ *
+ * XXX XXX XXX This function could use some work, but remember to
+ * keep it as optimized as possible, while retaining generic code.
+ *
+ * Verify the various "blind-ness" checks in the code.
+ *
+ * XXX XXX XXX Note that several effects should really not be "seen"
+ * if the player is blind. See also "effects.c" for other "mistakes".
+ *
+ * Perhaps monsters should breathe at locations *near* the player,
+ * since this would allow them to inflict "partial" damage.
+ *
+ * Perhaps smart monsters should decline to use "bolt" spells if
+ * there is a monster in the way, unless they wish to kill it.
+ *
+ * Note that, to allow the use of the "track_target" option at some
+ * later time, certain non-optimal things are done in the code below,
+ * including explicit checks against the "direct" variable, which is
+ * currently always true by the time it is checked, but which should
+ * really be set according to an explicit "projectable()" test, and
+ * the use of generic "x,y" locations instead of the player location,
+ * with those values being initialized with the player location.
+ *
+ * It will not be possible to "correctly" handle the case in which a
+ * monster attempts to attack a location which is thought to contain
+ * the player, but which in fact is nowhere near the player, since this
+ * might induce all sorts of messages about the attack itself, and about
+ * the effects of the attack, which the player might or might not be in
+ * a position to observe. Thus, for simplicity, it is probably best to
+ * only allow "faulty" attacks by a monster if one of the important grids
+ * (probably the initial or final grid) is in fact in view of the player.
+ * It may be necessary to actually prevent spell attacks except when the
+ * monster actually has line of sight to the player. Note that a monster
+ * could be left in a bizarre situation after the player ducked behind a
+ * pillar and then teleported away, for example.
+ *
+ * Note that certain spell attacks do not use the "project()" function
+ * but "simulate" it via the "direct" variable, which is always at least
+ * as restrictive as the "project()" function. This is necessary to
+ * prevent "blindness" attacks and such from bending around walls, etc,
+ * and to allow the use of the "track_target" option in the future.
+ *
+ * Note that this function attempts to optimize the use of spells for the
+ * cases in which the monster has no spells, or has spells but cannot use
+ * them, or has spells but they will have no "useful" effect. Note that
+ * this function has been an efficiency bottleneck in the past.
+ *
+ * Note the special "MFLAG_NICE" flag, which prevents a monster from using
+ * any spell attacks until the player has had a single chance to move.
+ */
+bool_ make_attack_spell(int m_idx)
+{
+ int k, chance, thrown_spell, rlev, failrate;
+ byte spell[96], num = 0;
+ u32b f4, f5, f6;
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ char m_name[80];
+ bool_ no_inate = FALSE;
+ int x, y;
+
+ /* Summon count */
+ int count = 0;
+
+ /* Extract the blind-ness */
+ bool_ blind = (p_ptr->blind ? TRUE : FALSE);
+
+ /* Extract the "see-able-ness" */
+ bool_ seen = (!blind && m_ptr->ml);
+
+ /* Assume "normal" target */
+ bool_ normal = TRUE;
+
+ /* Assume "projectable" */
+ bool_ direct = TRUE;
+
+ /* Target location */
+ if (m_ptr->target > -1)
+ {
+ if (!m_ptr->target)
+ {
+ y = p_ptr->py;
+ x = p_ptr->px;
+ }
+ else
+ {
+ return (FALSE);
+ }
+ }
+ else return FALSE;
+
+ /* Cannot cast spells when confused */
+ if (m_ptr->confused) return (FALSE);
+
+ /* Cannot cast spells when nice */
+ if (m_ptr->mflag & (MFLAG_NICE)) return (FALSE);
+ if (is_friend(m_ptr) >= 0) return (FALSE);
+
+ /* Cannot attack the player if mortal and player fated to never die by the ... */
+ if ((r_ptr->flags7 & RF7_MORTAL) && (p_ptr->no_mortal)) return (FALSE);
+
+ /* Hack -- Extract the spell probability */
+ chance = (r_ptr->freq_inate + r_ptr->freq_spell) / 2;
+
+ /* Not allowed to cast spells */
+ if (!chance) return (FALSE);
+
+ if (stupid_monsters)
+ {
+ /* Only do spells occasionally */
+ if (rand_int(100) >= chance) return (FALSE);
+ }
+ else
+ {
+ if (rand_int(100) >= chance) return (FALSE);
+
+ /* Sometimes forbid inate attacks (breaths) */
+ if (rand_int(100) >= (chance * 2)) no_inate = TRUE;
+ }
+
+ /* XXX XXX XXX Handle "track_target" option (?) */
+
+
+ /* Hack -- require projectable player */
+ if (normal)
+ {
+ /* Check range */
+ if (m_ptr->cdis > MAX_RANGE) return (FALSE);
+
+ /* Check path */
+ if (!projectable(m_ptr->fy, m_ptr->fx, y, x)) return (FALSE);
+ }
+
+ /* Extract the monster level */
+ rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+ /* Extract the racial spell flags */
+ f4 = r_ptr->flags4;
+ f5 = r_ptr->flags5;
+ f6 = r_ptr->flags6;
+
+ if (!stupid_monsters)
+ {
+ /* Forbid inate attacks sometimes */
+ if (no_inate) f4 = 0L;
+ }
+
+ /* Hack -- allow "desperate" spells */
+ if ((r_ptr->flags2 & (RF2_SMART)) &&
+ (m_ptr->hp < m_ptr->maxhp / 10) &&
+ (rand_int(100) < 50))
+ {
+ /* Require intelligent spells */
+ f4 &= (RF4_INT_MASK);
+ f5 &= (RF5_INT_MASK);
+ f6 &= (RF6_INT_MASK);
+
+ /* No spells left */
+ if (!f4 && !f5 && !f6) return (FALSE);
+ }
+
+ /* Remove the "ineffective" spells */
+ remove_bad_spells(m_idx, &f4, &f5, &f6);
+
+ /* No spells left */
+ if (!f4 && !f5 && !f6) return (FALSE);
+
+ if (!stupid_monsters)
+ {
+ /* Check for a clean bolt shot */
+ if ((f4&(RF4_BOLT_MASK) || f5 & (RF5_BOLT_MASK) ||
+ f6&(RF6_BOLT_MASK)) &&
+ !(r_ptr->flags2 & (RF2_STUPID)) &&
+ !clean_shot(m_ptr->fy, m_ptr->fx, y, x))
+ {
+ /* Remove spells that will only hurt friends */
+ f4 &= ~(RF4_BOLT_MASK);
+ f5 &= ~(RF5_BOLT_MASK);
+ f6 &= ~(RF6_BOLT_MASK);
+ }
+
+ /* Check for a possible summon */
+ if ((f4 & (RF4_SUMMON_MASK) || f5 & (RF5_SUMMON_MASK) ||
+ f6 & (RF6_SUMMON_MASK)) &&
+ !(r_ptr->flags2 & (RF2_STUPID)) &&
+ !(summon_possible(y, x)))
+ {
+ /* Remove summoning spells */
+ f4 &= ~(RF4_SUMMON_MASK);
+ f5 &= ~(RF5_SUMMON_MASK);
+ f6 &= ~(RF6_SUMMON_MASK);
+ }
+
+ /* No spells left */
+ if (!f4 && !f5 && !f6) return (FALSE);
+ }
+
+ /* Extract the "inate" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f4 & (1L << k)) spell[num++] = k + 32 * 3;
+ }
+
+ /* Extract the "normal" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f5 & (1L << k)) spell[num++] = k + 32 * 4;
+ }
+
+ /* Extract the "bizarre" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f6 & (1L << k)) spell[num++] = k + 32 * 5;
+ }
+
+ /* No spells left */
+ if (!num) return (FALSE);
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) return (FALSE);
+
+ /* Stop if player is leaving */
+ if (p_ptr->leaving) return (FALSE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0x00);
+
+ if (stupid_monsters)
+ {
+ /* Choose a spell to cast */
+ thrown_spell = spell[rand_int(num)];
+ }
+ else
+ {
+ thrown_spell = choose_attack_spell(m_idx, spell, num);
+
+ /* Abort if no spell was chosen */
+ if (!thrown_spell) return (FALSE);
+
+ /* Calculate spell failure rate */
+ failrate = 25 - (rlev + 3) / 4;
+
+ /* Hack -- Stupid monsters will never fail (for jellies and such) */
+ if (r_ptr->flags2 & (RF2_STUPID)) failrate = 0;
+
+ /* Check for spell failure (inate attacks never fail) */
+ if ((thrown_spell >= 128) && (rand_int(100) < failrate))
+ {
+ /* Message */
+ msg_format("%^s tries to cast a spell, but fails.", m_name);
+
+ return (TRUE);
+ }
+ }
+
+ /* Can the player disrupt its puny attempts? */
+ if ((p_ptr->antimagic_dis >= m_ptr->cdis) && (magik(p_ptr->antimagic)) && (thrown_spell >= 128))
+ {
+ char m_poss[80];
+
+ /* Get monster's possessive noun form ("the Illusionist's") */
+ monster_desc(m_poss, m_ptr, 0x06);
+
+ msg_format("Your anti-magic field disrupts %s spell.", m_poss);
+ }
+ else
+ {
+ char m_poss[80];
+ char ddesc[80];
+
+ /* Get the monster possessive ("his"/"her"/"its") */
+ monster_desc(m_poss, m_ptr, 0x22);
+
+ /* Hack -- Get the "died from" name */
+ monster_desc(ddesc, m_ptr, 0x88);
+
+ /* Cast the spell. */
+ switch (thrown_spell)
+ {
+ /* RF4_SHRIEK */
+ case 96 + 0:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s makes a high pitched shriek.", m_name);
+ aggravate_monsters(m_idx);
+ break;
+ }
+
+ /* RF4_MULTIPLY */
+ case 96 + 1:
+ {
+ break;
+ }
+
+ /* RF4_S_ANIMAL */
+ case 96 + 2:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons an animal!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_ANIMAL);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF4_ROCKET */
+ case 96 + 3:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s shoots something.", m_name);
+ else msg_format("%^s fires a rocket.", m_name);
+ breath(m_idx, GF_ROCKET,
+ ((m_ptr->hp / 4) > 800 ? 800 : (m_ptr->hp / 4)), 2);
+ update_smart_learn(m_idx, DRS_SHARD);
+ break;
+ }
+
+ /* RF4_ARROW_1 */
+ case 96 + 4:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s makes a strange noise.", m_name);
+ else msg_format("%^s fires an arrow.", m_name);
+ bolt(m_idx, GF_ARROW, damroll(1, 6));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF4_ARROW_2 */
+ case 96 + 5:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s makes a strange noise.", m_name);
+ else msg_format("%^s fires an arrow!", m_name);
+ bolt(m_idx, GF_ARROW, damroll(3, 6));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF4_ARROW_3 */
+ case 96 + 6:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s makes a strange noise.", m_name);
+ else msg_format("%^s fires a missile.", m_name);
+ bolt(m_idx, GF_ARROW, damroll(5, 6));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF4_ARROW_4 */
+ case 96 + 7:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s makes a strange noise.", m_name);
+ else msg_format("%^s fires a missile!", m_name);
+ bolt(m_idx, GF_ARROW, damroll(7, 6));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF4_BR_ACID */
+ case 96 + 8:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes acid.", m_name);
+ breath(m_idx, GF_ACID,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_ACID);
+ break;
+ }
+
+ /* RF4_BR_ELEC */
+ case 96 + 9:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes lightning.", m_name);
+ breath(m_idx, GF_ELEC,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_ELEC);
+ break;
+ }
+
+ /* RF4_BR_FIRE */
+ case 96 + 10:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes fire.", m_name);
+ breath(m_idx, GF_FIRE,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_FIRE);
+ break;
+ }
+
+ /* RF4_BR_COLD */
+ case 96 + 11:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes frost.", m_name);
+ breath(m_idx, GF_COLD,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_COLD);
+ break;
+ }
+
+ /* RF4_BR_POIS */
+ case 96 + 12:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes gas.", m_name);
+ breath(m_idx, GF_POIS,
+ ((m_ptr->hp / 3) > 800 ? 800 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_POIS);
+ break;
+ }
+
+
+ /* RF4_BR_NETH */
+ case 96 + 13:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes nether.", m_name);
+ breath(m_idx, GF_NETHER,
+ ((m_ptr->hp / 6) > 550 ? 550 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_NETH);
+ break;
+ }
+
+ /* RF4_BR_LITE */
+ case 96 + 14:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes light.", m_name);
+ breath(m_idx, GF_LITE,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_LITE);
+ break;
+ }
+
+ /* RF4_BR_DARK */
+ case 96 + 15:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes darkness.", m_name);
+ breath(m_idx, GF_DARK,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_DARK);
+ break;
+ }
+
+ /* RF4_BR_CONF */
+ case 96 + 16:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes confusion.", m_name);
+ breath(m_idx, GF_CONFUSION,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_CONF);
+ break;
+ }
+
+ /* RF4_BR_SOUN */
+ case 96 + 17:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes sound.", m_name);
+ breath(m_idx, GF_SOUND,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_SOUND);
+ break;
+ }
+
+ /* RF4_BR_CHAO */
+ case 96 + 18:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes chaos.", m_name);
+ breath(m_idx, GF_CHAOS,
+ ((m_ptr->hp / 6) > 600 ? 600 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_CHAOS);
+ break;
+ }
+
+ /* RF4_BR_DISE */
+ case 96 + 19:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes disenchantment.", m_name);
+ breath(m_idx, GF_DISENCHANT,
+ ((m_ptr->hp / 6) > 500 ? 500 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_DISEN);
+ break;
+ }
+
+ /* RF4_BR_NEXU */
+ case 96 + 20:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes nexus.", m_name);
+ breath(m_idx, GF_NEXUS,
+ ((m_ptr->hp / 3) > 250 ? 250 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_NEXUS);
+ break;
+ }
+
+ /* RF4_BR_TIME */
+ case 96 + 21:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes time.", m_name);
+ breath(m_idx, GF_TIME,
+ ((m_ptr->hp / 3) > 150 ? 150 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_INER */
+ case 96 + 22:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes inertia.", m_name);
+ breath(m_idx, GF_INERTIA,
+ ((m_ptr->hp / 6) > 200 ? 200 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_GRAV */
+ case 96 + 23:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes gravity.", m_name);
+ breath(m_idx, GF_GRAVITY,
+ ((m_ptr->hp / 3) > 200 ? 200 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_SHAR */
+ case 96 + 24:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes shards.", m_name);
+ breath(m_idx, GF_SHARDS,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_SHARD);
+ break;
+ }
+
+ /* RF4_BR_PLAS */
+ case 96 + 25:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes plasma.", m_name);
+ breath(m_idx, GF_PLASMA,
+ ((m_ptr->hp / 6) > 150 ? 150 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_WALL */
+ case 96 + 26:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes force.", m_name);
+ breath(m_idx, GF_FORCE,
+ ((m_ptr->hp / 6) > 200 ? 200 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_MANA */
+ case 96 + 27:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes magical energy.", m_name);
+ breath(m_idx, GF_MANA,
+ ((m_ptr->hp / 3) > 250 ? 250 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BA_NUKE */
+ case 96 + 28:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a ball of radiation.", m_name);
+ breath(m_idx, GF_NUKE, (rlev + damroll(10, 6)), 2);
+ update_smart_learn(m_idx, DRS_POIS);
+ break;
+ }
+
+ /* RF4_BR_NUKE */
+ case 96 + 29:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes toxic waste.", m_name);
+ breath(m_idx, GF_NUKE,
+ ((m_ptr->hp / 3) > 800 ? 800 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_POIS);
+ break;
+ }
+
+ /* RF4_BA_CHAO */
+ case 96 + 30:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles frighteningly.", m_name);
+ else msg_format("%^s invokes a raw chaos.", m_name);
+ breath(m_idx, GF_CHAOS, (rlev * 2) + damroll(10, 10), 4);
+ update_smart_learn(m_idx, DRS_CHAOS);
+ break;
+ }
+
+ /* RF4_BR_DISI -> Disintegration breath! */
+ case 96 + 31:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes disintegration.", m_name);
+ breath(m_idx, GF_DISINTEGRATE,
+ ((m_ptr->hp / 3) > 300 ? 300 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+
+
+ /* RF5_BA_ACID */
+ case 128 + 0:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts an acid ball.", m_name);
+ breath(m_idx, GF_ACID,
+ randint(rlev * 3) + 15, 2);
+ update_smart_learn(m_idx, DRS_ACID);
+ break;
+ }
+
+ /* RF5_BA_ELEC */
+ case 128 + 1:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a lightning ball.", m_name);
+ breath(m_idx, GF_ELEC,
+ randint(rlev * 3 / 2) + 8, 2);
+ update_smart_learn(m_idx, DRS_ELEC);
+ break;
+ }
+
+ /* RF5_BA_FIRE */
+ case 128 + 2:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a fire ball.", m_name);
+ breath(m_idx, GF_FIRE,
+ randint(rlev * 7 / 2) + 10, 2);
+ update_smart_learn(m_idx, DRS_FIRE);
+ break;
+ }
+
+ /* RF5_BA_COLD */
+ case 128 + 3:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a frost ball.", m_name);
+ breath(m_idx, GF_COLD,
+ randint(rlev * 3 / 2) + 10, 2);
+ update_smart_learn(m_idx, DRS_COLD);
+ break;
+ }
+
+ /* RF5_BA_POIS */
+ case 128 + 4:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a stinking cloud.", m_name);
+ breath(m_idx, GF_POIS,
+ damroll(12, 2), 2);
+ update_smart_learn(m_idx, DRS_POIS);
+ break;
+ }
+
+ /* RF5_BA_NETH */
+ case 128 + 5:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a nether ball.", m_name);
+ breath(m_idx, GF_NETHER,
+ (50 + damroll(10, 10) + rlev), 2);
+ update_smart_learn(m_idx, DRS_NETH);
+ break;
+ }
+
+ /* RF5_BA_WATE */
+ case 128 + 6:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s gestures fluidly.", m_name);
+ msg_print("You are engulfed in a whirlpool.");
+ breath(m_idx, GF_WATER,
+ randint(rlev * 5 / 2) + 50, 4);
+ break;
+ }
+
+ /* RF5_BA_MANA */
+ case 128 + 7:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles powerfully.", m_name);
+ else msg_format("%^s invokes a mana storm.", m_name);
+ breath(m_idx, GF_MANA,
+ (rlev * 5) + damroll(10, 10), 4);
+ break;
+ }
+
+ /* RF5_BA_DARK */
+ case 128 + 8:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles powerfully.", m_name);
+ else msg_format("%^s invokes a darkness storm.", m_name);
+ breath(m_idx, GF_DARK,
+ (rlev * 5) + damroll(10, 10), 4);
+ update_smart_learn(m_idx, DRS_DARK);
+ break;
+ }
+
+ /* RF5_DRAIN_MANA */
+ case 128 + 9:
+ {
+ if (!direct) break;
+ if (p_ptr->csp)
+ {
+ int r1;
+
+ /* Disturb if legal */
+ disturb(1, 0);
+
+ /* Basic message */
+ msg_format("%^s draws psychic energy from you!", m_name);
+
+ /* Attack power */
+ r1 = (randint(rlev) / 2) + 1;
+
+ /* Full drain */
+ if (r1 >= p_ptr->csp)
+ {
+ r1 = p_ptr->csp;
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+ }
+
+ /* Partial drain */
+ else
+ {
+ p_ptr->csp -= r1;
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Heal the monster */
+ if (m_ptr->hp < m_ptr->maxhp)
+ {
+ /* Heal */
+ m_ptr->hp += (6 * r1);
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Special message */
+ if (seen)
+ {
+ msg_format("%^s appears healthier.", m_name);
+ }
+ }
+ }
+ update_smart_learn(m_idx, DRS_MANA);
+ break;
+ }
+
+ /* RF5_MIND_BLAST */
+ case 128 + 10:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (!seen)
+ {
+ msg_print("You feel something focusing on your mind.");
+ }
+ else
+ {
+ msg_format("%^s gazes deep into your eyes.", m_name);
+ }
+
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ msg_print("Your mind is blasted by psionic energy.");
+
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+
+ if ((!p_ptr->resist_chaos) && (randint(3) == 1))
+ {
+ (void) set_image(p_ptr->image + rand_int(250) + 150);
+ }
+
+ take_sanity_hit(damroll(8, 8), ddesc);
+ }
+ break;
+ }
+
+ /* RF5_BRAIN_SMASH */
+ case 128 + 11:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (!seen)
+ {
+ msg_print("You feel something focusing on your mind.");
+ }
+ else
+ {
+ msg_format("%^s looks deep into your eyes.", m_name);
+ }
+
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ msg_print("Your mind is blasted by psionic energy.");
+ take_sanity_hit(damroll(12, 15), ddesc);
+ if (!p_ptr->resist_blind)
+ {
+ (void)set_blind(p_ptr->blind + 8 + rand_int(8));
+ }
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+ if (!p_ptr->free_act)
+ {
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(4) + 4);
+ }
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+
+ while (rand_int(100) > p_ptr->skill_sav)
+ (void)do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ while (rand_int(100) > p_ptr->skill_sav)
+ (void)do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+
+ if (!p_ptr->resist_chaos)
+ {
+ (void) set_image(p_ptr->image + rand_int(250) + 150);
+ }
+ }
+ break;
+ }
+
+ /* RF5_CAUSE_1 */
+ case 128 + 12:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s points at you and curses.", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ curse_equipment(33, 0);
+ take_hit(damroll(3, 8), ddesc);
+ }
+ break;
+ }
+
+ /* RF5_CAUSE_2 */
+ case 128 + 13:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s points at you and curses horribly.", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ curse_equipment(50, 5);
+ take_hit(damroll(8, 8), ddesc);
+ }
+ break;
+ }
+
+ /* RF5_CAUSE_3 */
+ case 128 + 14:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles loudly.", m_name);
+ else msg_format("%^s points at you, incanting terribly!", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ curse_equipment(80, 15);
+ take_hit(damroll(10, 15), ddesc);
+ }
+ break;
+ }
+
+ /* RF5_CAUSE_4 */
+ case 128 + 15:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s screams the word 'DIE!'", m_name);
+ else msg_format("%^s points at you, screaming the word DIE!", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ take_hit(damroll(15, 15), ddesc);
+ (void)set_cut(p_ptr->cut + damroll(10, 10));
+ }
+ break;
+ }
+
+ /* RF5_BO_ACID */
+ case 128 + 16:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a acid bolt.", m_name);
+ bolt(m_idx, GF_ACID, damroll(7, 8) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_ACID);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_ELEC */
+ case 128 + 17:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a lightning bolt.", m_name);
+ bolt(m_idx, GF_ELEC, damroll(4, 8) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_ELEC);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_FIRE */
+ case 128 + 18:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a fire bolt.", m_name);
+ bolt(m_idx, GF_FIRE, damroll(9, 8) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_FIRE);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_COLD */
+ case 128 + 19:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a frost bolt.", m_name);
+ bolt(m_idx, GF_COLD, damroll(6, 8) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_COLD);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_POIS */
+ case 128 + 20:
+ {
+ /* XXX XXX XXX */
+ break;
+ }
+
+ /* RF5_BO_NETH */
+ case 128 + 21:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a nether bolt.", m_name);
+ bolt(m_idx, GF_NETHER, 30 + damroll(5, 5) + (rlev * 3) / 2);
+ update_smart_learn(m_idx, DRS_NETH);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_WATE */
+ case 128 + 22:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a water bolt.", m_name);
+ bolt(m_idx, GF_WATER, damroll(10, 10) + (rlev));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_MANA */
+ case 128 + 23:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a mana bolt.", m_name);
+ bolt(m_idx, GF_MANA, randint(rlev * 7 / 2) + 50);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_PLAS */
+ case 128 + 24:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a plasma bolt.", m_name);
+ bolt(m_idx, GF_PLASMA, 10 + damroll(8, 7) + (rlev));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_ICEE */
+ case 128 + 25:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts an ice bolt.", m_name);
+ bolt(m_idx, GF_ICE, damroll(6, 6) + (rlev));
+ update_smart_learn(m_idx, DRS_COLD);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_MISSILE */
+ case 128 + 26:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a magic missile.", m_name);
+ bolt(m_idx, GF_MISSILE, damroll(2, 6) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_SCARE */
+ case 128 + 27:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles, and you hear scary noises.", m_name);
+ else msg_format("%^s casts a fearful illusion.", m_name);
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You refuse to be frightened.");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You refuse to be frightened.");
+ }
+ else
+ {
+ (void)set_afraid(p_ptr->afraid + rand_int(4) + 4);
+ }
+ update_smart_learn(m_idx, DRS_FEAR);
+ break;
+ }
+
+ /* RF5_BLIND */
+ case 128 + 28:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a spell, burning your eyes!", m_name);
+ if (p_ptr->resist_blind)
+ {
+ msg_print("You are unaffected!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ (void)set_blind(12 + rand_int(4));
+ }
+ update_smart_learn(m_idx, DRS_BLIND);
+ break;
+ }
+
+ /* RF5_CONF */
+ case 128 + 29:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles, and you hear puzzling noises.", m_name);
+ else msg_format("%^s creates a mesmerizing illusion.", m_name);
+ if (p_ptr->resist_conf)
+ {
+ msg_print("You disbelieve the feeble spell.");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You disbelieve the feeble spell.");
+ }
+ else
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+ update_smart_learn(m_idx, DRS_CONF);
+ break;
+ }
+
+ /* RF5_SLOW */
+ case 128 + 30:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s drains power from your muscles!", m_name);
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ }
+ update_smart_learn(m_idx, DRS_FREE);
+ break;
+ }
+
+ /* RF5_HOLD */
+ case 128 + 31:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s stares deep into your eyes!", m_name);
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_format("You resist the effects!");
+ }
+ else
+ {
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(4) + 4);
+ }
+ update_smart_learn(m_idx, DRS_FREE);
+ break;
+ }
+
+
+
+ /* RF6_HASTE */
+ case 160 + 0:
+ {
+ disturb(1, 0);
+ if (blind)
+ {
+ msg_format("%^s mumbles.", m_name);
+ }
+ else
+ {
+ msg_format("%^s concentrates on %s body.", m_name, m_poss);
+ }
+
+ /* Allow quick speed increases to base+10 */
+ if (m_ptr->mspeed < m_ptr->speed + 10)
+ {
+ msg_format("%^s starts moving faster.", m_name);
+ m_ptr->mspeed += 10;
+ }
+
+ /* Allow small speed increases to base+20 */
+ else if (m_ptr->mspeed < m_ptr->speed + 20)
+ {
+ msg_format("%^s starts moving faster.", m_name);
+ m_ptr->mspeed += 2;
+ }
+
+ break;
+ }
+
+ /* RF6_HAND_DOOM */
+ case 160 + 1:
+ {
+ disturb(1, 0);
+ msg_format("%^s invokes the Hand of Doom!", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_format("You resist the effects!");
+ }
+ else
+ {
+ int dummy = (((s32b) ((65 + randint(25)) * (p_ptr->chp))) / 100);
+ msg_print("Your feel your life fade away!");
+ take_hit(dummy, m_name);
+ curse_equipment(100, 20);
+
+ if (p_ptr->chp < 1) p_ptr->chp = 1;
+ }
+ break;
+ }
+
+ /* RF6_HEAL */
+ case 160 + 2:
+ {
+ disturb(1, 0);
+
+ /* Message */
+ if (blind)
+ {
+ msg_format("%^s mumbles.", m_name);
+ }
+ else
+ {
+ msg_format("%^s concentrates on %s wounds.", m_name, m_poss);
+ }
+
+ /* Heal some */
+ m_ptr->hp += (rlev * 6);
+
+ /* Fully healed */
+ if (m_ptr->hp >= m_ptr->maxhp)
+ {
+ /* Fully healed */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Message */
+ if (seen)
+ {
+ msg_format("%^s looks completely healed!", m_name);
+ }
+ else
+ {
+ msg_format("%^s sounds completely healed!", m_name);
+ }
+ }
+
+ /* Partially healed */
+ else
+ {
+ /* Message */
+ if (seen)
+ {
+ msg_format("%^s looks healthier.", m_name);
+ }
+ else
+ {
+ msg_format("%^s sounds healthier.", m_name);
+ }
+ }
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Cancel fear */
+ if (m_ptr->monfear)
+ {
+ /* Cancel fear */
+ m_ptr->monfear = 0;
+
+ /* Message */
+ msg_format("%^s recovers %s courage.", m_name, m_poss);
+ }
+ break;
+ }
+
+ /* RF6_S_ANIMALS */
+ case 160 + 3:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons some animals!", m_name);
+ for (k = 0; k < 4; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_ANIMAL);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_BLINK */
+ case 160 + 4:
+ {
+ disturb(1, 0);
+ msg_format("%^s blinks away.", m_name);
+ teleport_away(m_idx, 10);
+ break;
+ }
+
+ /* RF6_TPORT */
+ case 160 + 5:
+ {
+ disturb(1, 0);
+ msg_format("%^s teleports away.", m_name);
+ teleport_away(m_idx, MAX_SIGHT * 2 + 5);
+ break;
+ }
+
+ /* RF6_TELE_TO */
+ case 160 + 6:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s commands you to return.", m_name);
+ teleport_player_to(m_ptr->fy, m_ptr->fx);
+ break;
+ }
+
+ /* RF6_TELE_AWAY */
+ case 160 + 7:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s teleports you away.", m_name);
+ teleport_player(100);
+ break;
+ }
+
+ /* RF6_TELE_LEVEL */
+ case 160 + 8:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles strangely.", m_name);
+ else msg_format("%^s gestures at your feet.", m_name);
+ if (p_ptr->resist_nexus)
+ {
+ msg_print("You are unaffected!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ teleport_player_level();
+ }
+ update_smart_learn(m_idx, DRS_NEXUS);
+ break;
+ }
+
+ /* RF6_DARKNESS */
+ case 160 + 9:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s gestures in shadow.", m_name);
+ (void)unlite_area(0, 3);
+ break;
+ }
+
+ /* RF6_TRAPS */
+ case 160 + 10:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles, and then cackles evilly.", m_name);
+ else msg_format("%^s casts a spell and cackles evilly.", m_name);
+ (void)trap_creation();
+ break;
+ }
+
+ /* RF6_FORGET */
+ case 160 + 11:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s tries to blank your mind.", m_name);
+
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else if (lose_all_info())
+ {
+ msg_print("Your memories fade away.");
+ }
+ break;
+ }
+
+ /* RF6_ANIM_DEAD */
+ case 160 + 12:
+ break;
+
+ /* RF6_S_BUG */
+ case 160 + 13:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically codes some software bugs.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_BUG);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_RNG */
+ case 160 + 14:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically codes some RNGs.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_RNG);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_THUNDERLORD */
+ case 160 + 15:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons a Thunderlord!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_THUNDERLORD);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_SUMMON_KIN */
+ case 160 + 16:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons %s %s.",
+ m_name, m_poss,
+ ((r_ptr->flags1) & RF1_UNIQUE ?
+ "minions" : "kin"));
+ summon_kin_type = r_ptr->d_char; /* Big hack */
+
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_KIN);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+
+ break;
+ }
+
+ /* RF6_S_HI_DEMON */
+ case 160 + 17:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons greater demons!", m_name);
+ if (blind && count) msg_print("You hear heavy steps nearby.");
+ summon_cyber();
+ break;
+ }
+
+ /* RF6_S_MONSTER */
+ case 160 + 18:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons help!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, 0);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_MONSTERS */
+ case 160 + 19:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons monsters!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, 0);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_ANT */
+ case 160 + 20:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons ants.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_ANT);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_SPIDER */
+ case 160 + 21:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons spiders.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_SPIDER);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HOUND */
+ case 160 + 22:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons hounds.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HOUND);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HYDRA */
+ case 160 + 23:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons hydras.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HYDRA);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_ANGEL */
+ case 160 + 24:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons an angel!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_ANGEL);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_DEMON */
+ case 160 + 25:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons a demon!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_DEMON);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_UNDEAD */
+ case 160 + 26:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons an undead adversary!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_UNDEAD);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_DRAGON */
+ case 160 + 27:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons a dragon!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_DRAGON);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HI_UNDEAD */
+ case 160 + 28:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons greater undead!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+ if (blind && count)
+ {
+ msg_print("You hear many creepy things appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_HI_DRAGON */
+ case 160 + 29:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons ancient dragons!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HI_DRAGON);
+ }
+ if (blind && count)
+ {
+ msg_print("You hear many powerful things appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_WRAITH */
+ case 160 + 30:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons Wraith!", m_name);
+
+
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_WRAITH);
+ }
+
+ if (blind && count)
+ {
+ msg_print("You hear immortal beings appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_UNIQUE */
+ case 160 + 31:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons special opponents!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_UNIQUE);
+ }
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+ if (blind && count)
+ {
+ msg_print("You hear many powerful things appear nearby.");
+ }
+ break;
+ }
+ }
+ }
+
+ /* Remember what the monster did to us */
+ if (seen)
+ {
+ /* Inate spell */
+ if (thrown_spell < 32*4)
+ {
+ r_ptr->r_flags4 |= (1L << (thrown_spell - 32 * 3));
+ if (r_ptr->r_cast_inate < MAX_UCHAR) r_ptr->r_cast_inate++;
+ }
+
+ /* Bolt or Ball */
+ else if (thrown_spell < 32*5)
+ {
+ r_ptr->r_flags5 |= (1L << (thrown_spell - 32 * 4));
+ if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++;
+ }
+
+ /* Special spell */
+ else if (thrown_spell < 32*6)
+ {
+ r_ptr->r_flags6 |= (1L << (thrown_spell - 32 * 5));
+ if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++;
+ }
+ }
+
+
+ /* Always take note of monsters that kill you */
+ if (death && (r_ptr->r_deaths < MAX_SHORT))
+ {
+ r_ptr->r_deaths++;
+ }
+
+ /* A spell was cast */
+ return (TRUE);
+}
+
+
+/*
+ * Returns whether a given monster will try to run from the player.
+ *
+ * Monsters will attempt to avoid very powerful players. See below.
+ *
+ * Because this function is called so often, little details are important
+ * for efficiency. Like not using "mod" or "div" when possible. And
+ * attempting to check the conditions in an optimal order. Note that
+ * "(x << 2) == (x * 4)" if "x" has enough bits to hold the result.
+ *
+ * Note that this function is responsible for about one to five percent
+ * of the processor use in normal conditions...
+ */
+static int mon_will_run(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+#ifdef ALLOW_TERROR
+
+ u16b p_lev, m_lev;
+ u16b p_chp, p_mhp;
+ u16b m_chp, m_mhp;
+ u32b p_val, m_val;
+
+#endif
+
+ /* Keep monsters from running too far away */
+ if (m_ptr->cdis > MAX_SIGHT + 5) return (FALSE);
+
+ /* Friends don't run away */
+ if (is_friend(m_ptr) >= 0) return (FALSE);
+
+ /* All "afraid" monsters will run away */
+ if (m_ptr->monfear) return (TRUE);
+
+#ifdef ALLOW_TERROR
+
+ /* Nearby monsters will not become terrified */
+ if (m_ptr->cdis <= 5) return (FALSE);
+
+ /* Examine player power (level) */
+ p_lev = p_ptr->lev;
+
+ /* Examine monster power (level plus morale) */
+ m_lev = m_ptr->level + (m_idx & 0x08) + 25;
+
+ /* Optimize extreme cases below */
+ if (m_lev > p_lev + 4) return (FALSE);
+ if (m_lev + 4 <= p_lev) return (TRUE);
+
+ /* Examine player health */
+ p_chp = p_ptr->chp;
+ p_mhp = p_ptr->mhp;
+
+ /* Examine monster health */
+ m_chp = m_ptr->hp;
+ m_mhp = m_ptr->maxhp;
+
+ /* Prepare to optimize the calculation */
+ p_val = (p_lev * p_mhp) + (p_chp << 2); /* div p_mhp */
+ m_val = (m_lev * m_mhp) + (m_chp << 2); /* div m_mhp */
+
+ /* Strong players scare strong monsters */
+ if (p_val * m_mhp > m_val * p_mhp) return (TRUE);
+
+#endif
+
+ /* Assume no terror */
+ return (FALSE);
+}
+
+
+
+
+/*
+* Choose the "best" direction for "flowing"
+*
+* Note that ghosts and rock-eaters are never allowed to "flow",
+* since they should move directly towards the player.
+*
+* Prefer "non-diagonal" directions, but twiddle them a little
+* to angle slightly towards the player's actual location.
+*
+* Allow very perceptive monsters to track old "spoor" left by
+* previous locations occupied by the player. This will tend
+* to have monsters end up either near the player or on a grid
+* recently occupied by the player (and left via "teleport").
+*
+* Note that if "smell" is turned on, all monsters get vicious.
+*
+* Also note that teleporting away from a location will cause
+* the monsters who were chasing you to converge on that location
+* as long as you are still near enough to "annoy" them without
+* being close enough to chase directly. I have no idea what will
+* happen if you combine "smell" with low "aaf" values.
+*/
+
+/*
+* Provide a location to flee to, but give the player a wide berth.
+*
+* A monster may wish to flee to a location that is behind the player,
+* but instead of heading directly for it, the monster should "swerve"
+* around the player so that he has a smaller chance of getting hit.
+*/
+static bool_ get_fear_moves_aux(int m_idx, int *yp, int *xp)
+{
+ int y, x, y1, x1, fy, fx, gy = 0, gx = 0;
+ int when = 0, score = -1;
+ int i;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Monster flowing disabled */
+ if (!flow_by_sound) return (FALSE);
+
+ /* Monster location */
+ fy = m_ptr->fy;
+ fx = m_ptr->fx;
+
+ /* Desired destination */
+ y1 = fy - (*yp);
+ x1 = fx - (*xp);
+
+ /* The player is not currently near the monster grid */
+ if (cave[fy][fx].when < cave[p_ptr->py][p_ptr->px].when)
+ {
+ /* No reason to attempt flowing */
+ return (FALSE);
+ }
+
+ /* Monster is too far away to use flow information */
+ if (cave[fy][fx].cost > MONSTER_FLOW_DEPTH) return (FALSE);
+ if (cave[fy][fx].cost > r_ptr->aaf) return (FALSE);
+
+ /* Check nearby grids, diagonals first */
+ for (i = 7; i >= 0; i--)
+ {
+ int dis, s;
+
+ /* Get the location */
+ y = fy + ddy_ddd[i];
+ x = fx + ddx_ddd[i];
+
+ /* Ignore illegal locations */
+ if (cave[y][x].when == 0) continue;
+
+ /* Ignore ancient locations */
+ if (cave[y][x].when < when) continue;
+
+ /* Calculate distance of this grid from our destination */
+ dis = distance(y, x, y1, x1);
+
+ /* Score this grid */
+ s = 5000 / (dis + 3) - 500 / (cave[y][x].cost + 1);
+
+ /* No negative scores */
+ if (s < 0) s = 0;
+
+ /* Ignore lower scores */
+ if (s < score) continue;
+
+ /* Save the score and time */
+ when = cave[y][x].when;
+ score = s;
+
+ /* Save the location */
+ gy = y;
+ gx = x;
+ }
+
+ /* No legal move (?) */
+ if (!when) return (FALSE);
+
+ /* Find deltas */
+ (*yp) = fy - gy;
+ (*xp) = fx - gx;
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Choose a "safe" location near a monster for it to run toward.
+*
+* A location is "safe" if it can be reached quickly and the player
+* is not able to fire into it (it isn't a "clean shot"). So, this will
+* cause monsters to "duck" behind walls. Hopefully, monsters will also
+* try to run towards corridor openings if they are in a room.
+*
+* This function may take lots of CPU time if lots of monsters are
+* fleeing.
+*
+* Return TRUE if a safe location is available.
+*/
+static bool_ find_safety(int m_idx, int *yp, int *xp)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ int fy = m_ptr->fy;
+ int fx = m_ptr->fx;
+
+ int y, x, d, dis;
+ int gy = 0, gx = 0, gdis = 0;
+
+ /* Start with adjacent locations, spread further */
+ for (d = 1; d < 10; d++)
+ {
+ /* Check nearby locations */
+ for (y = fy - d; y <= fy + d; y++)
+ {
+ for (x = fx - d; x <= fx + d; x++)
+ {
+ /* Skip illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Skip locations in a wall */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Check distance */
+ if (distance(y, x, fy, fx) != d) continue;
+
+ /* Check for "availability" (if monsters can flow) */
+ if (flow_by_sound)
+ {
+ /* Ignore grids very far from the player */
+ if (cave[y][x].when < cave[p_ptr->py][p_ptr->px].when) continue;
+
+ /* Ignore too-distant grids */
+ if (cave[y][x].cost > cave[fy][fx].cost + 2 * d) continue;
+ }
+
+ /* Check for absence of shot */
+ if (!projectable(y, x, p_ptr->py, p_ptr->px))
+ {
+ /* Calculate distance from player */
+ dis = distance(y, x, p_ptr->py, p_ptr->px);
+
+ /* Remember if further than previous */
+ if (dis > gdis)
+ {
+ gy = y;
+ gx = x;
+ gdis = dis;
+ }
+ }
+ }
+ }
+
+ /* Check for success */
+ if (gdis > 0)
+ {
+ /* Good location */
+ (*yp) = fy - gy;
+ (*xp) = fx - gx;
+
+ /* Found safe place */
+ return (TRUE);
+ }
+ }
+
+ /* No safe place */
+ return (FALSE);
+}
+
+
+/*
+ * Choose a good hiding place near a monster for it to run toward.
+ *
+ * Pack monsters will use this to "ambush" the player and lure him out
+ * of corridors into open space so they can swarm him.
+ *
+ * Return TRUE if a good location is available.
+ */
+static bool_ find_hiding(int m_idx, int *yp, int *xp)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ int fy = m_ptr->fy;
+ int fx = m_ptr->fx;
+
+ int y, x, d, dis;
+ int gy = 0, gx = 0, gdis = 999, min;
+
+ /* Closest distance to get */
+ min = distance(p_ptr->py, p_ptr->px, fy, fx) * 3 / 4 + 2;
+
+ /* Start with adjacent locations, spread further */
+ for (d = 1; d < 10; d++)
+ {
+ /* Check nearby locations */
+ for (y = fy - d; y <= fy + d; y++)
+ {
+ for (x = fx - d; x <= fx + d; x++)
+ {
+ /* Skip illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Skip locations in a wall */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Check distance */
+ if (distance(y, x, fy, fx) != d) continue;
+
+ /* Check for hidden, available grid */
+ if (!player_can_see_bold(y, x) && clean_shot(fy, fx, y, x))
+ {
+ /* Calculate distance from player */
+ dis = distance(y, x, p_ptr->py, p_ptr->px);
+
+ /* Remember if closer than previous */
+ if (dis < gdis && dis >= min)
+ {
+ gy = y;
+ gx = x;
+ gdis = dis;
+ }
+ }
+ }
+ }
+
+ /* Check for success */
+ if (gdis < 999)
+ {
+ /* Good location */
+ (*yp) = fy - gy;
+ (*xp) = fx - gx;
+
+ /* Found good place */
+ return (TRUE);
+ }
+ }
+
+ /* No good place */
+ return (FALSE);
+}
+
+
+/* Find an appropriate corpse */
+void find_corpse(monster_type *m_ptr, int *y, int *x)
+{
+ int k, last = -1;
+
+ for (k = 0; k < max_o_idx; k++)
+ {
+ object_type *o_ptr = &o_list[k];
+ monster_race *rt_ptr, *rt2_ptr;
+
+ if (!o_ptr->k_idx) continue;
+
+ if (o_ptr->tval != TV_CORPSE) continue;
+ if ((o_ptr->sval != SV_CORPSE_CORPSE) && (o_ptr->sval != SV_CORPSE_SKELETON)) continue;
+
+ rt_ptr = &r_info[o_ptr->pval2];
+
+ /* Cannot incarnate into a higher level monster */
+ if (rt_ptr->level > m_ptr->level) continue;
+
+ /* Must be in LOS */
+ if (!los(m_ptr->fy, m_ptr->fx, o_ptr->iy, o_ptr->ix)) continue;
+
+ if (last != -1)
+ {
+ rt2_ptr = &r_info[o_list[last].pval2];
+ if (rt_ptr->level > rt2_ptr->level) last = k;
+ else continue;
+ }
+ else
+ {
+ last = k;
+ }
+ }
+
+ /* Must be ok now */
+ if (last != -1)
+ {
+ *y = o_list[last].iy;
+ *x = o_list[last].ix;
+ }
+}
+
+/*
+ * Choose target
+ */
+static void get_target_monster(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int i, t = -1, d = 9999;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *t_ptr = &m_list[i];
+ /* hack should call the function for ego monsters ... but no_target i not meant to be added by ego and it speeds up the code */
+ monster_race *rt_ptr = &r_info[t_ptr->r_idx];
+ int dd;
+
+ /* Ignore "dead" monsters */
+ if (!t_ptr->r_idx) continue;
+
+ if (m_idx == i) continue;
+
+ /* Cannot be targeted */
+ if (rt_ptr->flags7 & RF7_NO_TARGET) continue;
+
+ if (is_enemy(m_ptr, t_ptr) && (los(m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx) &&
+ ((dd = distance(m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx)) < d)))
+ {
+ t = i;
+ d = dd;
+ }
+ }
+ /* Hack */
+ if ((is_friend(m_ptr) < 0) && los(m_ptr->fy, m_ptr->fx, p_ptr->py, p_ptr->px) && (distance(m_ptr->fy, m_ptr->fx, p_ptr->py, p_ptr->px) < d)) t = 0;
+
+ m_ptr->target = t;
+}
+
+/*
+ * Choose "logical" directions for monster movement
+ */
+static bool_ get_moves(int m_idx, int *mm)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ int y, ay, x, ax;
+
+ int move_val = 0;
+
+ int y2 = p_ptr->py;
+ int x2 = p_ptr->px;
+ bool_ done = FALSE;
+
+ /* Oups get nearer */
+ if ((is_friend(m_ptr) > 0) && (m_ptr->cdis > p_ptr->pet_follow_distance))
+ {
+ y2 = p_ptr->py;
+ x2 = p_ptr->px;
+ }
+ /* Use the target */
+ else if (!m_ptr->target)
+ {
+ y2 = p_ptr->py;
+ x2 = p_ptr->px;
+ }
+ else if (m_ptr->target > 0)
+ {
+ y2 = m_list[m_ptr->target].fy;
+ x2 = m_list[m_ptr->target].fx;
+ }
+
+ /* Hack doppleganger confuses monsters(even pets) */
+ if (doppleganger)
+ {
+ if (magik(70))
+ {
+ y2 = m_list[doppleganger].fy;
+ x2 = m_list[doppleganger].fx;
+ }
+ }
+
+ /* A possessor is not interrested in the player, it only wants a corpse */
+ if (r_ptr->flags7 & RF7_POSSESSOR)
+ {
+ find_corpse(m_ptr, &y2, &x2);
+ }
+
+ /* Let quests redefine AI */
+ if (r_ptr->flags7 & RF7_AI_SPECIAL)
+ {
+ if (process_hooks_ret(HOOK_MONSTER_AI, "dd", "(d)", m_idx))
+ {
+ y2 = process_hooks_return[0].num;
+ x2 = process_hooks_return[1].num;
+ }
+ }
+
+ if (m_idx == p_ptr->control)
+ {
+ if ((r_ptr->flags7 & RF7_AI_PLAYER) || magik(85))
+ {
+ if (distance(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx) < 50)
+ {
+ y2 = m_ptr->fy + ddy[p_ptr->control_dir];
+ x2 = m_ptr->fx + ddx[p_ptr->control_dir];
+ }
+ }
+ }
+
+ /* Extract the "pseudo-direction" */
+ y = m_ptr->fy - y2;
+ x = m_ptr->fx - x2;
+
+ /* Tease the player */
+ if (r_ptr->flags7 & RF7_AI_ANNOY)
+ {
+ if (distance(m_ptr->fy, m_ptr->fx, y2, x2) < 4)
+ {
+ y = -y;
+ x = -x;
+ }
+ }
+
+ /* Death orbs .. */
+ if (r_ptr->flags2 & RF2_DEATH_ORB)
+ {
+ if (!los(m_ptr->fy, m_ptr->fx, y2, x2))
+ {
+ return (FALSE);
+ }
+ }
+
+ if (!stupid_monsters && (is_friend(m_ptr) < 0))
+ {
+ int tx = x2, ty = y2;
+
+ /*
+ * Animal packs try to get the player out of corridors
+ * (...unless they can move through walls -- TY)
+ */
+ if ((r_ptr->flags1 & RF1_FRIENDS) &&
+ (r_ptr->flags3 & RF3_ANIMAL) &&
+ !((r_ptr->flags2 & (RF2_PASS_WALL)) ||
+ (r_ptr->flags2 & (RF2_KILL_WALL))))
+ {
+ int i, room = 0;
+
+ /* Count room grids next to player */
+ for (i = 0; i < 8; i++)
+ {
+ /* Check grid */
+ if (cave[ty + ddy_ddd[i]][tx + ddx_ddd[i]].info & (CAVE_ROOM))
+ {
+ /* One more room grid */
+ room++;
+ }
+ }
+
+ /* Not in a room and strong player */
+ if ((room < 8) && (p_ptr->chp > ((p_ptr->mhp * 3) / 4)))
+ {
+ /* Find hiding place */
+ if (find_hiding(m_idx, &y, &x)) done = TRUE;
+ }
+ }
+
+ /* Monster groups try to surround the player */
+ if (!done && (r_ptr->flags1 & RF1_FRIENDS))
+ {
+ int i;
+
+ /* Find an empty square near the target to fill */
+ for (i = 0; i < 8; i++)
+ {
+ /* Pick squares near target (semi-randomly) */
+ y2 = ty + ddy_ddd[(m_idx + i) & 7];
+ x2 = tx + ddx_ddd[(m_idx + i) & 7];
+
+ /* Already there? */
+ if ((m_ptr->fy == y2) && (m_ptr->fx == x2))
+ {
+ /* Attack the target */
+ y2 = ty;
+ x2 = tx;
+
+ break;
+ }
+
+ /* Ignore filled grids */
+ if (!cave_empty_bold(y2, x2)) continue;
+
+ /* Try to fill this hole */
+ break;
+ }
+
+ /* Extract the new "pseudo-direction" */
+ y = m_ptr->fy - y2;
+ x = m_ptr->fx - x2;
+
+ /* Done */
+ done = TRUE;
+ }
+ }
+
+ /* Apply fear if possible and necessary */
+ if ((stupid_monsters) || (is_friend(m_ptr) > 0))
+ {
+ if (mon_will_run(m_idx))
+ {
+ /* XXX XXX Not very "smart" */
+ y = ( -y), x = ( -x);
+ }
+ }
+ else
+ {
+ if (!done && mon_will_run(m_idx))
+ {
+ /* Try to find safe place */
+ if (!find_safety(m_idx, &y, &x))
+ {
+ /* This is not a very "smart" method XXX XXX */
+ y = ( -y);
+ x = ( -x);
+ }
+ else
+ {
+ /* Attempt to avoid the player */
+ if (flow_by_sound)
+ {
+ /* Adjust movement */
+ (void)get_fear_moves_aux(m_idx, &y, &x);
+ }
+ }
+ }
+ }
+
+
+ if (!stupid_monsters)
+ {
+ /* Check for no move */
+ if (!x && !y) return (FALSE);
+ }
+
+ /* Extract the "absolute distances" */
+ ax = ABS(x);
+ ay = ABS(y);
+
+ /* Do something weird */
+ if (y < 0) move_val += 8;
+ if (x > 0) move_val += 4;
+
+ /* Prevent the diamond maneuvre */
+ if (ay > (ax << 1))
+ {
+ move_val++;
+ move_val++;
+ }
+ else if (ax > (ay << 1))
+ {
+ move_val++;
+ }
+
+ /* Extract some directions */
+ switch (move_val)
+ {
+ case 0:
+ mm[0] = 9;
+ if (ay > ax)
+ {
+ mm[1] = 8;
+ mm[2] = 6;
+ mm[3] = 7;
+ mm[4] = 3;
+ }
+ else
+ {
+ mm[1] = 6;
+ mm[2] = 8;
+ mm[3] = 3;
+ mm[4] = 7;
+ }
+ break;
+ case 1:
+ case 9:
+ mm[0] = 6;
+ if (y < 0)
+ {
+ mm[1] = 3;
+ mm[2] = 9;
+ mm[3] = 2;
+ mm[4] = 8;
+ }
+ else
+ {
+ mm[1] = 9;
+ mm[2] = 3;
+ mm[3] = 8;
+ mm[4] = 2;
+ }
+ break;
+ case 2:
+ case 6:
+ mm[0] = 8;
+ if (x < 0)
+ {
+ mm[1] = 9;
+ mm[2] = 7;
+ mm[3] = 6;
+ mm[4] = 4;
+ }
+ else
+ {
+ mm[1] = 7;
+ mm[2] = 9;
+ mm[3] = 4;
+ mm[4] = 6;
+ }
+ break;
+ case 4:
+ mm[0] = 7;
+ if (ay > ax)
+ {
+ mm[1] = 8;
+ mm[2] = 4;
+ mm[3] = 9;
+ mm[4] = 1;
+ }
+ else
+ {
+ mm[1] = 4;
+ mm[2] = 8;
+ mm[3] = 1;
+ mm[4] = 9;
+ }
+ break;
+ case 5:
+ case 13:
+ mm[0] = 4;
+ if (y < 0)
+ {
+ mm[1] = 1;
+ mm[2] = 7;
+ mm[3] = 2;
+ mm[4] = 8;
+ }
+ else
+ {
+ mm[1] = 7;
+ mm[2] = 1;
+ mm[3] = 8;
+ mm[4] = 2;
+ }
+ break;
+ case 8:
+ mm[0] = 3;
+ if (ay > ax)
+ {
+ mm[1] = 2;
+ mm[2] = 6;
+ mm[3] = 1;
+ mm[4] = 9;
+ }
+ else
+ {
+ mm[1] = 6;
+ mm[2] = 2;
+ mm[3] = 9;
+ mm[4] = 1;
+ }
+ break;
+ case 10:
+ case 14:
+ mm[0] = 2;
+ if (x < 0)
+ {
+ mm[1] = 3;
+ mm[2] = 1;
+ mm[3] = 6;
+ mm[4] = 4;
+ }
+ else
+ {
+ mm[1] = 1;
+ mm[2] = 3;
+ mm[3] = 4;
+ mm[4] = 6;
+ }
+ break;
+ case 12:
+ mm[0] = 1;
+ if (ay > ax)
+ {
+ mm[1] = 2;
+ mm[2] = 4;
+ mm[3] = 3;
+ mm[4] = 7;
+ }
+ else
+ {
+ mm[1] = 4;
+ mm[2] = 2;
+ mm[3] = 7;
+ mm[4] = 3;
+ }
+ break;
+ }
+
+
+
+ /* Wants to move... */
+ return (TRUE);
+}
+
+
+int check_hit2(int power, int level, int ac)
+{
+ int i, k;
+
+ /* Percentile dice */
+ k = rand_int(100);
+
+ /* Hack -- Always miss or hit */
+ if (k < 10) return (k < 5);
+
+ /* Calculate the "attack quality" */
+ i = (power + (level * 3));
+
+ /* Power and Level compete against Armor */
+ if ((i > 0) && (randint(i) > ((ac * 3) / 4))) return (TRUE);
+
+ /* Assume miss */
+ return (FALSE);
+}
+
+
+/* Monster attacks monster */
+static bool_ monst_attack_monst(int m_idx, int t_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx], *t_ptr = &m_list[t_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ monster_race *tr_ptr = race_inf(t_ptr);
+ int ap_cnt;
+ int ac, rlev, pt;
+ char m_name[80], t_name[80];
+ char ddesc[80], temp[80];
+ bool_ blinked = FALSE, touched = FALSE;
+ bool_ explode = FALSE;
+ bool_ fear = FALSE;
+ byte y_saver = t_ptr->fy;
+ byte x_saver = t_ptr->fx;
+
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & RF1_NEVER_BLOW) return FALSE;
+ if (tr_ptr->flags7 & RF7_IM_MELEE) return FALSE;
+
+ /* Total armor */
+ ac = t_ptr->ac;
+
+ /* Extract the effective monster level */
+ rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get the monster name (or "it") */
+ monster_desc(t_name, t_ptr, 0);
+
+ /* Get the "died from" information (i.e. "a kobold") */
+ monster_desc(ddesc, m_ptr, 0x88);
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ if (!(m_ptr->ml || t_ptr->ml))
+ {
+ monster_msg("You hear noise.");
+ }
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = m_ptr->blow[ap_cnt].effect;
+ int method = m_ptr->blow[ap_cnt].method;
+ int d_dice = m_ptr->blow[ap_cnt].d_dice;
+ int d_side = m_ptr->blow[ap_cnt].d_side;
+
+ if (t_ptr == m_ptr) /* Paranoia */
+ {
+ if (wizard)
+ monster_msg("Monster attacking self?");
+ break;
+ }
+
+ /* Stop attacking if the target dies! */
+ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver)
+ break;
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+ if (blinked) /* Stop! */
+ {
+ /* break; */
+ }
+
+ /* Extract visibility (before blink) */
+ if (m_ptr->ml) visible = TRUE;
+
+ /* Extract the attack "power" */
+ power = get_attack_power(effect);
+
+
+ /* Monster hits*/
+ if (!effect || check_hit2(power, rlev, ac))
+ {
+ /* Always disturbing */
+ if (disturb_other) disturb(1, 0);
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hits %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touches %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punches %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kicks %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claws %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bites %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "stings %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's %s.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butts %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crushes %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulfs %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charges %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawls on %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drools on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spits on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_EXPLODE:
+ {
+ act = "explodes.";
+ explode = TRUE;
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gazes at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wails at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "releases spores at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "projects XXX4's at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "begs %s for money.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = "insults %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ act = "moans at %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ act = "sings to %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+ }
+
+ /* Message */
+ if (act)
+ {
+ strfmt(temp, act, t_name);
+ if (m_ptr->ml || t_ptr->ml)
+ monster_msg("%^s %s", m_name, temp);
+
+ }
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side);
+
+ /* Hack need more punch against monsters */
+ damage *= 3;
+
+ pt = GF_MISSILE;
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ damage = 0;
+ pt = 0;
+ break;
+ }
+
+ case RBE_HURT:
+ case RBE_SANITY:
+ {
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+ break;
+ }
+
+ case RBE_POISON:
+ case RBE_DISEASE:
+ {
+ pt = GF_POIS;
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ case RBE_UN_POWER:
+ case RBE_ABOMINATION:
+ {
+ pt = GF_DISENCHANT;
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ case RBE_EAT_LITE:
+ {
+ pt = damage = 0;
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ case RBE_EAT_GOLD:
+ {
+ pt = damage = 0;
+ if (randint(2) == 1) blinked = TRUE;
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ pt = GF_ACID;
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ pt = GF_ELEC;
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ pt = GF_FIRE;
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ pt = GF_COLD;
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ break;
+ }
+
+ case RBE_HALLU:
+ case RBE_CONFUSE:
+ {
+ pt = GF_CONFUSION;
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ pt = GF_TURN_ALL;
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ pt = GF_OLD_SLEEP; /* sort of close... */
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ case RBE_LOSE_INT:
+ case RBE_LOSE_WIS:
+ case RBE_LOSE_DEX:
+ case RBE_LOSE_CON:
+ case RBE_LOSE_CHR:
+ case RBE_LOSE_ALL:
+ case RBE_PARASITE:
+ {
+ break;
+ }
+ case RBE_SHATTER:
+ {
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(m_ptr->fy, m_ptr->fx, 8);
+ }
+ break;
+ }
+ case RBE_EXP_10:
+ case RBE_EXP_20:
+ case RBE_EXP_40:
+ case RBE_EXP_80:
+ {
+ pt = GF_NETHER;
+ break;
+ }
+ case RBE_TIME:
+ {
+ pt = GF_TIME;
+ break;
+ }
+ default:
+ {
+ pt = 0;
+ break;
+ }
+ }
+
+ if (pt)
+ {
+ /* Do damage if not exploding */
+ if (!explode)
+ {
+ project(m_idx, 0, t_ptr->fy, t_ptr->fx,
+ (pt == GF_OLD_SLEEP ? m_ptr->level : damage), pt, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ if (touched)
+ {
+ /* Aura fire */
+ if ((tr_ptr->flags2 & RF2_AURA_FIRE) &&
+ !(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ if (m_ptr->ml || t_ptr->ml)
+ {
+ blinked = FALSE;
+ monster_msg("%^s is suddenly very hot!", m_name);
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_FIRE;
+ }
+ project(t_idx, 0, m_ptr->fy, m_ptr->fx,
+ damroll (1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_FIRE, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ /* Aura elec */
+ if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) && !(r_ptr->flags3 & (RF3_IM_ELEC)))
+ {
+ if (m_ptr->ml || t_ptr->ml)
+ {
+ blinked = FALSE;
+ monster_msg("%^s gets zapped!", m_name);
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_ELEC;
+ }
+ project(t_idx, 0, m_ptr->fy, m_ptr->fx,
+ damroll (1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_ELEC, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ }
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+ {
+ /* Visible monsters */
+ if (m_ptr->ml)
+ {
+ /* Disturbing */
+ disturb(1, 0);
+
+ /* Message */
+ monster_msg("%^s misses %s.", m_name, t_name);
+ }
+
+ break;
+ }
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+
+ if (explode)
+ {
+ sound(SOUND_EXPLODE);
+ mon_take_hit_mon(m_idx, m_idx, m_ptr->hp + 1, &fear, " explodes into tiny shreds.");
+
+ blinked = FALSE;
+ }
+
+
+ /* Blink away */
+ if (blinked)
+ {
+ if (m_ptr->ml)
+ {
+ monster_msg("The thief flees laughing!");
+ }
+ else
+ {
+ monster_msg("You hear laughter!");
+ }
+
+ teleport_away(m_idx, MAX_SIGHT * 2 + 5);
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Hack -- local "player stealth" value (see below)
+ */
+static u32b noise = 0L;
+
+/* Determine whether the player is invisible to a monster */
+static bool_ player_invis(monster_type * m_ptr)
+{
+ s16b inv, mlv;
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ inv = p_ptr->invis;
+
+ mlv = (s16b) m_ptr->level;
+
+ if (r_ptr->flags3 & RF3_NO_SLEEP)
+ mlv += 10;
+ if (r_ptr->flags3 & RF3_DRAGON)
+ mlv += 20;
+ if (r_ptr->flags3 & RF3_UNDEAD)
+ mlv += 15;
+ if (r_ptr->flags3 & RF3_DEMON)
+ mlv += 15;
+ if (r_ptr->flags3 & RF3_ANIMAL)
+ mlv += 15;
+ if (r_ptr->flags3 & RF3_ORC)
+ mlv -= 15;
+ if (r_ptr->flags3 & RF3_TROLL)
+ mlv -= 10;
+ if (r_ptr->flags2 & RF2_STUPID)
+ mlv /= 2;
+ if (r_ptr->flags2 & RF2_SMART)
+ mlv = (mlv * 5) / 4;
+ if (m_ptr->mflag & MFLAG_QUEST)
+ inv = 0;
+ if (r_ptr->flags2 & RF2_INVISIBLE)
+ inv = 0;
+ if (m_ptr->mflag & MFLAG_CONTROL)
+ inv = 0;
+ if (mlv < 1)
+ mlv = 1;
+ return (inv >= randint(mlv*2));
+}
+
+/*
+ * Process a monster
+ *
+ * The monster is known to be within 100 grids of the player
+ *
+ * In several cases, we directly update the monster lore
+ *
+ * Note that a monster is only allowed to "reproduce" if there
+ * are a limited number of "reproducing" monsters on the current
+ * level. This should prevent the level from being "swamped" by
+ * reproducing monsters. It also allows a large mass of mice to
+ * prevent a louse from multiplying, but this is a small price to
+ * pay for a simple multiplication method.
+ *
+ * XXX Monster fear is slightly odd, in particular, monsters will
+ * fixate on opening a door even if they cannot open it. Actually,
+ * the same thing happens to normal monsters when they hit a door
+ *
+ * XXX XXX XXX In addition, monsters which *cannot* open or bash
+ * down a door will still stand there trying to open it...
+ *
+ * XXX Technically, need to check for monster in the way
+ * combined with that monster being in a wall (or door?)
+ *
+ * A "direction" of "5" means "pick a random direction".
+ */
+static void process_monster(int m_idx, bool_ is_frien)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ cave_type *c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+
+ int i, d, oy, ox, ny, nx;
+
+ int mm[8];
+
+ monster_type *y_ptr;
+
+ bool_ do_turn;
+ bool_ do_move;
+ bool_ do_view;
+
+ bool_ did_open_door;
+ bool_ did_bash_door;
+ bool_ did_take_item;
+ bool_ did_kill_item;
+ bool_ did_move_body;
+ bool_ did_kill_body;
+ bool_ did_pass_wall;
+ bool_ did_kill_wall;
+ bool_ gets_angry = FALSE;
+ bool_ inv;
+ bool_ xxx = FALSE;
+
+ inv = player_invis(m_ptr);
+
+ if (r_ptr->flags9 & RF9_DOPPLEGANGER) doppleganger = m_idx;
+
+ /* Handle "bleeding" */
+ if (m_ptr->bleeding)
+ {
+ int d = 1 + (m_ptr->maxhp / 50);
+ if (d > m_ptr->bleeding) d = m_ptr->bleeding;
+
+ /* Exit if the monster dies */
+ if (mon_take_hit(m_idx, d, &xxx, " bleeds to death.")) return;
+
+ /* Hack -- Recover from bleeding */
+ if (m_ptr->bleeding > d)
+ {
+ /* Recover somewhat */
+ m_ptr->bleeding -= d;
+ }
+
+ /* Fully recover */
+ else
+ {
+ /* Recover fully */
+ m_ptr->bleeding = 0;
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Get the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s is no longer bleeding.", m_name);
+
+ /* Hack -- Update the health bar */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+ }
+ }
+ }
+
+ /* Handle "poisoned" */
+ if (m_ptr->poisoned)
+ {
+ int d = (m_ptr->poisoned) / 10;
+ if (d < 1) d = 1;
+
+ /* Exit if the monster dies */
+ if (mon_take_hit(m_idx, d, &xxx, " dies of poison.")) return;
+
+ /* Hack -- Recover from bleeding */
+ if (m_ptr->poisoned > d)
+ {
+ /* Recover somewhat */
+ m_ptr->poisoned -= d;
+ }
+
+ /* Fully recover */
+ else
+ {
+ /* Recover fully */
+ m_ptr->poisoned = 0;
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Get the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s is no longer poisoned.", m_name);
+
+ /* Hack -- Update the health bar */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+ }
+ }
+ }
+
+ /* Handle "sleep" */
+ if (m_ptr->csleep)
+ {
+ u32b notice = 0;
+
+ /* Hack -- handle non-aggravation */
+ if (!p_ptr->aggravate) notice = rand_int(1024);
+
+ /* Hack -- See if monster "notices" player */
+ if ((notice * notice * notice) <= noise)
+ {
+ /* Hack -- amount of "waking" */
+ int d = 1;
+
+ /* Wake up faster near the player */
+ if (m_ptr->cdis < 50) d = (100 / m_ptr->cdis);
+
+ /* Hack -- handle aggravation */
+ if (p_ptr->aggravate) d = m_ptr->csleep;
+
+ /* Still asleep */
+ if (m_ptr->csleep > d)
+ {
+ /* Monster wakes up "a little bit" */
+ m_ptr->csleep -= d;
+
+ /* Notice the "not waking up" */
+ if (m_ptr->ml)
+ {
+ /* Hack -- Count the ignores */
+ if (r_ptr->r_ignore < MAX_UCHAR)
+ {
+ r_ptr->r_ignore++;
+ }
+ }
+ }
+
+ /* Just woke up */
+ else
+ {
+ /* Reset sleep counter */
+ m_ptr->csleep = 0;
+
+ /* Notice the "waking up" */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s wakes up.", m_name);
+
+ /* Hack -- Count the wakings */
+ if (r_ptr->r_wake < MAX_UCHAR)
+ {
+ r_ptr->r_wake++;
+ }
+ }
+ }
+ }
+
+ /* Still sleeping */
+ if (m_ptr->csleep) return;
+ }
+
+
+ /* Handle "stun" */
+ if (m_ptr->stunned)
+ {
+ int d = 1;
+
+ /* Make a "saving throw" against stun */
+ if (rand_int(5000) <= m_ptr->level * m_ptr->level)
+ {
+ /* Recover fully */
+ d = m_ptr->stunned;
+ }
+
+ /* Hack -- Recover from stun */
+ if (m_ptr->stunned > d)
+ {
+ /* Recover somewhat */
+ m_ptr->stunned -= d;
+ }
+
+ /* Fully recover */
+ else
+ {
+ /* Recover fully */
+ m_ptr->stunned = 0;
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s is no longer stunned.", m_name);
+ }
+ }
+
+ /* Still stunned */
+ if (m_ptr->stunned) return;
+ }
+
+
+ /* Handle confusion */
+ if (m_ptr->confused)
+ {
+ /* Amount of "boldness" */
+ int d = randint(m_ptr->level / 10 + 1);
+
+ /* Still confused */
+ if (m_ptr->confused > d)
+ {
+ /* Reduce the confusion */
+ m_ptr->confused -= d;
+ }
+
+ /* Recovered */
+ else
+ {
+ /* No longer confused */
+ m_ptr->confused = 0;
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s is no longer confused.", m_name);
+ }
+ }
+ }
+
+ /* No one wants to be your friend if you're aggravating */
+ if ((m_ptr->status > MSTATUS_NEUTRAL) && (m_ptr->status < MSTATUS_COMPANION) && (p_ptr->aggravate) && !(r_ptr->flags7 & RF7_PET))
+ gets_angry = TRUE;
+
+ /* Paranoia... no friendly uniques outside wizard mode -- TY */
+ if ((m_ptr->status > MSTATUS_NEUTRAL) && (m_ptr->status < MSTATUS_COMPANION) && !(wizard) &&
+ (r_ptr->flags1 & (RF1_UNIQUE)) && !(r_ptr->flags7 & RF7_PET))
+ gets_angry = TRUE;
+
+ if (gets_angry)
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s suddenly becomes hostile!", m_name);
+ change_side(m_ptr);
+ break;
+ }
+ }
+
+ /* Handle "fear" */
+ if (m_ptr->monfear)
+ {
+ /* Amount of "boldness" */
+ int d = randint(m_ptr->level / 10 + 1);
+
+ /* Still afraid */
+ if (m_ptr->monfear > d)
+ {
+ /* Reduce the fear */
+ m_ptr->monfear -= d;
+ }
+
+ /* Recover from fear, take note if seen */
+ else
+ {
+ /* No longer afraid */
+ m_ptr->monfear = 0;
+
+ /* Visual note */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+ char m_poss[80];
+
+ /* Acquire the monster name/poss */
+ monster_desc(m_name, m_ptr, 0);
+ monster_desc(m_poss, m_ptr, 0x22);
+
+ /* Dump a message */
+ msg_format("%^s recovers %s courage.", m_name, m_poss);
+ }
+ }
+ }
+
+ /* Get the origin */
+ oy = m_ptr->fy;
+ ox = m_ptr->fx;
+
+ /* Attempt to "multiply" if able and allowed */
+ if ((r_ptr->flags4 & (RF4_MULTIPLY)) && (num_repro < MAX_REPRO))
+ {
+ if (ai_multiply(m_idx)) return;
+ }
+
+ if (speak_unique)
+ {
+ if (randint(SPEAK_CHANCE) == 1)
+ {
+ if (player_has_los_bold(oy, ox) && (r_ptr->flags2 & RF2_CAN_SPEAK))
+ {
+ char m_name[80];
+ char monmessage[80];
+
+ /* Acquire the monster name/poss */
+ if (m_ptr->ml)
+ monster_desc(m_name, m_ptr, 0);
+ else
+ strcpy(m_name, "It");
+
+ /* xtra_line function by Matt Graham--allow uniques to */
+ /* say "unique" things based on their monster index. */
+ /* Try for the unique's lines in "monspeak.txt" first. */
+ /* 0 is SUCCESS, of course.... */
+
+ if (!process_hooks(HOOK_MON_SPEAK, "(d,s)", m_idx, m_name))
+ {
+ if (get_xtra_line("monspeak.txt", m_ptr, monmessage) != 0)
+ {
+ /* Get a message from old defaults if new don't work */
+
+ if (is_friend(m_ptr) > 0)
+ get_rnd_line("speakpet.txt", monmessage);
+ else if (m_ptr->monfear)
+ get_rnd_line("monfear.txt", monmessage);
+ else
+ get_rnd_line("bravado.txt", monmessage);
+ }
+ msg_format("%^s %s", m_name, monmessage);
+ }
+ }
+ }
+ }
+
+ /* Need a new target ? */
+ if ((m_ptr->target == -1) || magik(10)) get_target_monster(m_idx);
+
+
+ /* Attempt to cast a spell */
+ if (make_attack_spell(m_idx)) return;
+
+ /*
+ * Attempt to cast a spell at an enemy other than the player
+ * (may slow the game a smidgeon, but I haven't noticed.)
+ */
+ hack_message_pain_may_silent = TRUE;
+ if (monst_spell_monst(m_idx))
+ {
+ hack_message_pain_may_silent = FALSE;
+ return;
+ }
+ hack_message_pain_may_silent = FALSE;
+
+
+ /* Hack -- Assume no movement */
+ mm[0] = mm[1] = mm[2] = mm[3] = 0;
+ mm[4] = mm[5] = mm[6] = mm[7] = 0;
+
+ /* Confused -- 100% random */
+ if (m_ptr->confused || (inv == TRUE && m_ptr->target == 0))
+ {
+ /* Try four "random" directions */
+ mm[0] = mm[1] = mm[2] = mm[3] = 5;
+ }
+
+ /* 75% random movement */
+ else if ((r_ptr->flags1 & (RF1_RAND_50)) &&
+ (r_ptr->flags1 & (RF1_RAND_25)) &&
+ (rand_int(100) < 75))
+ {
+ /* Memorize flags */
+ if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_RAND_50);
+ if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_RAND_25);
+
+ /* Try four "random" directions */
+ mm[0] = mm[1] = mm[2] = mm[3] = 5;
+ }
+
+ /* 50% random movement */
+ else if ((r_ptr->flags1 & (RF1_RAND_50)) &&
+ (rand_int(100) < 50))
+ {
+ /* Memorize flags */
+ if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_RAND_50);
+
+ /* Try four "random" directions */
+ mm[0] = mm[1] = mm[2] = mm[3] = 5;
+ }
+
+ /* 25% random movement */
+ else if ((r_ptr->flags1 & (RF1_RAND_25)) &&
+ (rand_int(100) < 25))
+ {
+ /* Memorize flags */
+ if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_RAND_25);
+
+ /* Try four "random" directions */
+ mm[0] = mm[1] = mm[2] = mm[3] = 5;
+ }
+
+ /* Normal movement */
+ else
+ {
+ if (stupid_monsters)
+ {
+ /* Logical moves */
+ get_moves(m_idx, mm);
+ }
+ else
+ {
+ /* Logical moves, may do nothing */
+ if (!get_moves(m_idx, mm)) return;
+ }
+ }
+
+ /* Paranoia -- quest code could delete it */
+ if (!c_ptr->m_idx) return;
+
+ /* Assume nothing */
+ do_turn = FALSE;
+ do_move = FALSE;
+ do_view = FALSE;
+
+ /* Assume nothing */
+ did_open_door = FALSE;
+ did_bash_door = FALSE;
+ did_take_item = FALSE;
+ did_kill_item = FALSE;
+ did_move_body = FALSE;
+ did_kill_body = FALSE;
+ did_pass_wall = FALSE;
+ did_kill_wall = FALSE;
+
+
+ /* Take a zero-terminated array of "directions" */
+ for (i = 0; mm[i]; i++)
+ {
+ /* Get the direction */
+ d = mm[i];
+
+ /* Hack -- allow "randomized" motion */
+ if (d == 5) d = ddd[rand_int(8)];
+
+ /* Get the destination */
+ ny = oy + ddy[d];
+ nx = ox + ddx[d];
+
+ /* Access that cave grid */
+ c_ptr = &cave[ny][nx];
+
+ /* Access that cave grid's contents */
+ y_ptr = &m_list[c_ptr->m_idx];
+
+
+ /* Floor is open? */
+ if (cave_floor_bold(ny, nx))
+ {
+ /* Go ahead and move */
+ do_move = TRUE;
+ }
+
+ /* Floor is trapped? */
+ else if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ /* Go ahead and move */
+ do_move = TRUE;
+ }
+
+ /* Hack -- check for Glyph of Warding */
+ if ((c_ptr->feat == FEAT_GLYPH) &&
+ !(r_ptr->flags1 & RF1_NEVER_BLOW))
+ {
+ /* Assume no move allowed */
+ do_move = FALSE;
+
+ /* Break the ward */
+ if (randint(BREAK_GLYPH) < m_ptr->level)
+ {
+ /* Describe observable breakage */
+ if (c_ptr->info & CAVE_MARK)
+ {
+ msg_print("The rune of protection is broken!");
+ }
+
+ /* Forget the rune */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Break the rune */
+ place_floor_convert_glass(ny, nx);
+
+ /* Allow movement */
+ do_move = TRUE;
+ }
+ }
+
+ /* Hack -- trees are obstacle */
+ else if ((cave[ny][nx].feat == FEAT_TREES) && (r_ptr->flags9 & RF9_KILL_TREES))
+ {
+ do_move = TRUE;
+
+ /* Forget the tree */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ cave_set_feat(ny, nx, FEAT_GRASS);
+ }
+
+ /* Hack -- player 'in' wall */
+ else if ((ny == p_ptr->py) && (nx == p_ptr->px))
+ {
+ do_move = TRUE;
+ }
+
+ else if (c_ptr->m_idx)
+ {
+ /* Possibly a monster to attack */
+ do_move = TRUE;
+ }
+
+ /* Permanent wall */
+ else if (f_info[c_ptr->feat].flags1 & FF1_PERMANENT)
+ {
+ /* Nothing */
+ }
+
+
+ /* Some monsters can fly */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_LEVITATE) && (r_ptr->flags7 & (RF7_CAN_FLY)))
+ {
+ /* Pass through walls/doors/rubble */
+ do_move = TRUE;
+ }
+
+ /* Some monsters can fly */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_FLY) && (r_ptr->flags7 & (RF7_CAN_FLY)))
+ {
+ /* Pass through trees/... */
+ do_move = TRUE;
+ }
+
+ /* Monster moves through walls (and doors) */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_PASS) && (r_ptr->flags2 & (RF2_PASS_WALL)))
+ {
+ /* Pass through walls/doors/rubble */
+ do_move = TRUE;
+
+ /* Monster went through a wall */
+ did_pass_wall = TRUE;
+ }
+
+ /* Monster destroys walls (and doors) */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_PASS) && (r_ptr->flags2 & (RF2_KILL_WALL)))
+ {
+ /* Eat through walls/doors/rubble */
+ do_move = TRUE;
+
+ /* Monster destroyed a wall */
+ did_kill_wall = TRUE;
+
+ if (randint(GRINDNOISE) == 1)
+ {
+ msg_print("There is a grinding sound.");
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ cave_set_feat(ny, nx, FEAT_FLOOR);
+
+ /* Note changes to viewable region */
+ if (player_has_los_bold(ny, nx)) do_view = TRUE;
+ }
+
+ /* Monster moves through walls (and doors) */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_PASS) && (r_ptr->flags2 & (RF2_PASS_WALL)))
+ {
+ /* Pass through walls/doors/rubble */
+ do_move = TRUE;
+
+ /* Monster went through a wall */
+ did_pass_wall = TRUE;
+ }
+
+ /* Monster moves through webs */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_WEB) &&
+ (r_ptr->flags7 & RF7_SPIDER))
+ {
+ /* Pass through webs */
+ do_move = TRUE;
+ }
+
+ /* Handle doors and secret doors */
+ else if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) ||
+ (c_ptr->feat == FEAT_SECRET))
+ {
+ bool_ may_bash = TRUE;
+
+ /* Take a turn */
+ do_turn = TRUE;
+
+ if ((r_ptr->flags2 & (RF2_OPEN_DOOR)) &&
+ ((is_friend(m_ptr) <= 0) || p_ptr->pet_open_doors))
+ {
+ /* Closed doors and secret doors */
+ if ((c_ptr->feat == FEAT_DOOR_HEAD) ||
+ (c_ptr->feat == FEAT_SECRET))
+ {
+ /* The door is open */
+ did_open_door = TRUE;
+
+ /* Do not bash the door */
+ may_bash = FALSE;
+ }
+
+ /* Locked doors (not jammed) */
+ else if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08)
+ {
+ int k;
+
+ /* Door power */
+ k = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07);
+
+ /* Try to unlock it XXX XXX XXX */
+ if (rand_int(m_ptr->hp / 10) > k)
+ {
+ /* Unlock the door */
+ cave_set_feat(ny, nx, FEAT_DOOR_HEAD + 0x00);
+
+ /* Do not bash the door */
+ may_bash = FALSE;
+ }
+ }
+ }
+
+ /* Stuck doors -- attempt to bash them down if allowed */
+ if (may_bash && (r_ptr->flags2 & RF2_BASH_DOOR) &&
+ ((is_friend(m_ptr) <= 0) || p_ptr->pet_open_doors))
+ {
+ int k;
+
+ /* Door power */
+ k = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07);
+
+ /* Attempt to Bash XXX XXX XXX */
+ if (rand_int(m_ptr->hp / 10) > k)
+ {
+ /* Message */
+ msg_print("You hear a door burst open!");
+
+ /* Disturb (sometimes) */
+ if (disturb_minor) disturb(0, 0);
+
+ /* The door was bashed open */
+ did_bash_door = TRUE;
+
+ /* Hack -- fall into doorway */
+ do_move = TRUE;
+ }
+ }
+
+
+ /* Deal with doors in the way */
+ if (did_open_door || did_bash_door)
+ {
+ /* It's no longer hidden */
+ cave[ny][nx].mimic = 0;
+
+ /* Break down the door */
+ if (did_bash_door && (rand_int(100) < 50))
+ {
+ cave_set_feat(ny, nx, FEAT_BROKEN);
+ }
+
+ /* Open the door */
+ else
+ {
+ cave_set_feat(ny, nx, FEAT_OPEN);
+ }
+
+ /* Handle viewable doors */
+ if (player_has_los_bold(ny, nx)) do_view = TRUE;
+ }
+ }
+ else if (do_move && (c_ptr->feat == FEAT_MINOR_GLYPH)
+ && !(r_ptr->flags1 & RF1_NEVER_BLOW))
+ {
+ /* Assume no move allowed */
+ do_move = FALSE;
+
+ /* Break the ward */
+ if (randint(BREAK_MINOR_GLYPH) < m_ptr->level)
+ {
+ /* Describe observable breakage */
+ if (c_ptr->info & CAVE_MARK)
+ {
+ if (ny == p_ptr->py && nx == p_ptr->px)
+ {
+ msg_print("The rune explodes!");
+ fire_ball(GF_MANA, 0,
+ 2 * ((p_ptr->lev / 2) + damroll(7, 7)), 2);
+ }
+ else
+ msg_print("An explosive rune was disarmed.");
+ }
+
+ /* Forget the rune */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Break the rune */
+ place_floor_convert_glass(ny, nx);
+
+ /* Allow movement */
+ do_move = TRUE;
+ }
+ }
+
+ /* Hack -- the Between teleport the monsters too */
+ else if (cave[ny][nx].feat == FEAT_BETWEEN)
+ {
+ nx = cave[ny][nx].special & 255;
+ ny = cave[ny][nx].special >> 8;
+ get_pos_player(10, &ny, &nx);
+
+ /* Access that cave grid */
+ c_ptr = &cave[ny][nx];
+
+ /* Access that cave grid's contents */
+ y_ptr = &m_list[c_ptr->m_idx];
+
+ if (!(r_ptr->flags3 & RF3_IM_COLD))
+ {
+ if ((m_ptr->hp - distance(ny, nx, oy, ox)*2) <= 0)
+ {
+ ny = oy + ddy[d];
+ nx = ox + ddx[d];
+ do_move = FALSE;
+ }
+ else
+ {
+ m_ptr->hp -= distance(ny, nx, oy, ox) * 2;
+ do_move = TRUE;
+ }
+ }
+ else
+ {
+ do_move = TRUE;
+ }
+ }
+
+ /* Execute the inscription -- MEGA HACK -- */
+ if ((c_ptr->inscription) && (c_ptr->inscription != INSCRIP_CHASM))
+ {
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_MONST_WALK)
+ {
+ bool_ t;
+ t = execute_inscription(c_ptr->inscription, ny, nx);
+ if (!t && do_move)
+ {
+ /* Hack -- attack the player even if on the inscription */
+ if ((ny == p_ptr->py) && (nx == p_ptr->px))
+ do_move = TRUE;
+ else
+ do_move = FALSE;
+ }
+ }
+ }
+
+ /* Some monsters never attack */
+ if (do_move && (ny == p_ptr->py) && (nx == p_ptr->px) &&
+ (r_ptr->flags1 & RF1_NEVER_BLOW))
+ {
+ /* Do not move */
+ do_move = FALSE;
+ }
+
+ /* The player is in the way. Attack him. */
+ if (do_move && (ny == p_ptr->py) && (nx == p_ptr->px))
+ {
+ /* Do the attack */
+ (void)make_attack_normal(m_idx, 1);
+
+ /* Do not move */
+ do_move = FALSE;
+
+ /* Took a turn */
+ do_turn = TRUE;
+ }
+
+ if ((cave[ny][nx].feat >= FEAT_PATTERN_START) &&
+ (cave[ny][nx].feat <= FEAT_PATTERN_XTRA2) &&
+ do_turn == FALSE)
+ {
+ do_move = FALSE;
+ }
+
+
+ /* A monster is in the way */
+ if (do_move && c_ptr->m_idx)
+ {
+ monster_race *z_ptr = race_inf(y_ptr);
+ monster_type *m2_ptr = &m_list[c_ptr->m_idx];
+
+ /* Assume no movement */
+ do_move = FALSE;
+
+ /* Kill weaker monsters */
+ if ((r_ptr->flags2 & RF2_KILL_BODY) &&
+ (r_ptr->mexp > z_ptr->mexp) && (cave_floor_bold(ny, nx)) &&
+ /* Friends don't kill friends... */
+ !((is_friend(m_ptr) > 0) && (is_friend(m2_ptr) > 0)) &&
+ /* Uniques aren't faceless monsters in a crowd */
+ !(z_ptr->flags1 & RF1_UNIQUE) &&
+ /* Don't wreck quests */
+ !(m2_ptr->mflag & (MFLAG_QUEST | MFLAG_QUEST2)) &&
+ /* Don't punish summoners for relying on their friends */
+ (is_friend(m2_ptr) <= 0))
+ {
+ /* Allow movement */
+ do_move = TRUE;
+
+ /* Monster ate another monster */
+ did_kill_body = TRUE;
+
+ /* XXX XXX XXX Message */
+
+ /* Kill the monster */
+ delete_monster(ny, nx);
+
+ /* Hack -- get the empty monster */
+ y_ptr = &m_list[c_ptr->m_idx];
+ }
+
+ /* Attack 'enemies' */
+ else if (is_enemy(m_ptr, m2_ptr) || m_ptr->confused)
+ {
+ do_move = FALSE;
+ /* attack */
+ if (m2_ptr->r_idx && (m2_ptr->hp >= 0))
+ {
+ hack_message_pain_may_silent = TRUE;
+ if (monst_attack_monst(m_idx, c_ptr->m_idx))
+ {
+ hack_message_pain_may_silent = FALSE;
+ return;
+ }
+ hack_message_pain_may_silent = FALSE;
+ }
+ }
+
+ /* Push past weaker monsters (unless leaving a wall) */
+ else if ((r_ptr->flags2 & RF2_MOVE_BODY) &&
+ (r_ptr->mexp > z_ptr->mexp) && cave_floor_bold(ny, nx) &&
+ (cave_floor_bold(m_ptr->fy, m_ptr->fx)))
+ {
+ /* Allow movement */
+ do_move = TRUE;
+
+ /* Monster pushed past another monster */
+ did_move_body = TRUE;
+
+ /* XXX XXX XXX Message */
+ }
+ }
+
+ /*
+ * Check if monster can cross terrain
+ * This is checked after the normal attacks
+ * to allow monsters to attack an enemy,
+ * even if it can't enter the terrain.
+ */
+ if (do_move && !monster_can_cross_terrain(c_ptr->feat, r_ptr))
+ {
+ /* Assume no move allowed */
+ do_move = FALSE;
+ }
+
+ /* Some monsters never move */
+ if (do_move && (r_ptr->flags1 & RF1_NEVER_MOVE))
+ {
+ /* Hack -- memorize lack of attacks */
+ /* if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_NEVER_MOVE); */
+
+ /* Do not move */
+ do_move = FALSE;
+ }
+
+
+
+ /* Creature has been allowed move */
+ if (do_move)
+ {
+ s16b this_o_idx, next_o_idx = 0;
+
+ /* Take a turn */
+ do_turn = TRUE;
+
+ /* Hack -- Update the old location */
+ cave[oy][ox].m_idx = c_ptr->m_idx;
+
+ /* Mega-Hack -- move the old monster, if any */
+ if (c_ptr->m_idx)
+ {
+ /* Move the old monster */
+ y_ptr->fy = oy;
+ y_ptr->fx = ox;
+
+ /* Update the old monster */
+ update_mon(c_ptr->m_idx, TRUE);
+
+ /* Wake up the moved monster */
+ m_list[c_ptr->m_idx].csleep = 0;
+
+ /*
+ * Update monster light -- I'm too lazy to check flags
+ * here, and those ego monster_race functions aren't
+ * re-entrant XXX XXX XXX
+ */
+ p_ptr->update |= (PU_MON_LITE);
+ }
+
+ /* Hack -- Update the new location */
+ c_ptr->m_idx = m_idx;
+
+ /* Move the monster */
+ m_ptr->fy = ny;
+ m_ptr->fx = nx;
+
+ /* Update the monster */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(oy, ox);
+
+ /* Redraw the new grid */
+ lite_spot(ny, nx);
+
+ /* Execute the inscription -- MEGA HACK -- */
+ if (c_ptr->inscription == INSCRIP_CHASM)
+ {
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_MONST_WALK)
+ {
+ execute_inscription(c_ptr->inscription, ny, nx);
+ }
+ }
+
+ /* Possible disturb */
+ if (m_ptr->ml && (disturb_move ||
+ ((m_ptr->mflag & (MFLAG_VIEW)) &&
+ disturb_near)))
+ {
+ /* Disturb */
+ if ((is_friend(m_ptr) < 0) || disturb_pets)
+ disturb(0, 0);
+ }
+
+ /* Check for monster trap */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ if (mon_hit_trap(m_idx)) return;
+ }
+ else
+ {
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Skip gold */
+ if (o_ptr->tval == TV_GOLD) continue;
+
+ /* Incarnate ? */
+ if ((o_ptr->tval == TV_CORPSE) && (r_ptr->flags7 & RF7_POSSESSOR) &&
+ ((o_ptr->sval == SV_CORPSE_CORPSE) || (o_ptr->sval == SV_CORPSE_SKELETON)))
+ {
+ if (ai_possessor(m_idx, this_o_idx)) return;
+ }
+
+ /* Take or Kill objects on the floor */
+ /* rr9: Pets will no longer pick up/destroy items */
+ if ((((r_ptr->flags2 & (RF2_TAKE_ITEM)) &&
+ ((is_friend(m_ptr) <= 0) || p_ptr->pet_pickup_items)) ||
+ (r_ptr->flags2 & (RF2_KILL_ITEM))) &&
+ (is_friend(m_ptr) <= 0))
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ u32b flg3 = 0L;
+
+ char m_name[80];
+ char o_name[80];
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Acquire the object name */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0x04);
+
+ /* React to objects that hurt the monster */
+ if (f5 & (TR5_KILL_DEMON)) flg3 |= (RF3_DEMON);
+ if (f5 & (TR5_KILL_UNDEAD)) flg3 |= (RF3_UNDEAD);
+ if (f1 & (TR1_SLAY_DRAGON)) flg3 |= (RF3_DRAGON);
+ if (f1 & (TR1_SLAY_TROLL)) flg3 |= (RF3_TROLL);
+ if (f1 & (TR1_SLAY_GIANT)) flg3 |= (RF3_GIANT);
+ if (f1 & (TR1_SLAY_ORC)) flg3 |= (RF3_ORC);
+ if (f1 & (TR1_SLAY_DEMON)) flg3 |= (RF3_DEMON);
+ if (f1 & (TR1_SLAY_UNDEAD)) flg3 |= (RF3_UNDEAD);
+ if (f1 & (TR1_SLAY_ANIMAL)) flg3 |= (RF3_ANIMAL);
+ if (f1 & (TR1_SLAY_EVIL)) flg3 |= (RF3_EVIL);
+
+ /* The object cannot be picked up by the monster */
+ if (artifact_p(o_ptr) || (r_ptr->flags3 & flg3) ||
+ (o_ptr->art_name))
+ {
+ /* Only give a message for "take_item" */
+ if (r_ptr->flags2 & (RF2_TAKE_ITEM))
+ {
+ /* Take note */
+ did_take_item = TRUE;
+
+ /* Describe observable situations */
+ if (m_ptr->ml && player_has_los_bold(ny, nx))
+ {
+ /* Dump a message */
+ msg_format("%^s tries to pick up %s, but fails.",
+ m_name, o_name);
+ }
+ }
+ }
+
+ /* Pick up the item */
+ else if (r_ptr->flags2 & (RF2_TAKE_ITEM))
+ {
+ /* Take note */
+ did_take_item = TRUE;
+
+ /* Describe observable situations */
+ if (player_has_los_bold(ny, nx))
+ {
+ /* Dump a message */
+ msg_format("%^s picks up %s.", m_name, o_name);
+ }
+
+ /* Option */
+ if (testing_carry)
+ {
+ /* Excise the object */
+ excise_object_idx(this_o_idx);
+
+ /* Forget mark */
+ o_ptr->marked = FALSE;
+
+ /* Forget location */
+ o_ptr->iy = o_ptr->ix = 0;
+
+ /* Memorize monster */
+ o_ptr->held_m_idx = m_idx;
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ /* Carry object */
+ m_ptr->hold_o_idx = this_o_idx;
+ }
+
+ /* Nope */
+ else
+ {
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+ }
+ }
+
+ /* Destroy the item */
+ else
+ {
+ /* Take note */
+ did_kill_item = TRUE;
+
+ /* Describe observable situations */
+ if (player_has_los_bold(ny, nx))
+ {
+ /* Dump a message */
+ msg_format("%^s crushes %s.", m_name, o_name);
+ }
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+ }
+ }
+ }
+ }
+
+ /* Update monster light */
+ if (r_ptr->flags9 & RF9_HAS_LITE) p_ptr->update |= (PU_MON_LITE);
+ }
+
+ /* Stop when done */
+ if (do_turn) break;
+ }
+
+
+ /* If we haven't done anything, try casting a spell again */
+ if (!do_turn && !do_move && !m_ptr->monfear && !stupid_monsters &&
+ (is_friend(m_ptr) < 0))
+ {
+ /* Cast spell */
+ if (make_attack_spell(m_idx)) return;
+ }
+
+
+ /* Notice changes in view */
+ if (do_view)
+ {
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+ }
+
+
+ /* Learn things from observable monster */
+ if (m_ptr->ml)
+ {
+ /* Monster opened a door */
+ if (did_open_door) r_ptr->r_flags2 |= (RF2_OPEN_DOOR);
+
+ /* Monster bashed a door */
+ if (did_bash_door) r_ptr->r_flags2 |= (RF2_BASH_DOOR);
+
+ /* Monster tried to pick something up */
+ if (did_take_item) r_ptr->r_flags2 |= (RF2_TAKE_ITEM);
+
+ /* Monster tried to crush something */
+ if (did_kill_item) r_ptr->r_flags2 |= (RF2_KILL_ITEM);
+
+ /* Monster pushed past another monster */
+ if (did_move_body) r_ptr->r_flags2 |= (RF2_MOVE_BODY);
+
+ /* Monster ate another monster */
+ if (did_kill_body) r_ptr->r_flags2 |= (RF2_KILL_BODY);
+
+ /* Monster passed through a wall */
+ if (did_pass_wall) r_ptr->r_flags2 |= (RF2_PASS_WALL);
+
+ /* Monster destroyed a wall */
+ if (did_kill_wall) r_ptr->r_flags2 |= (RF2_KILL_WALL);
+ }
+
+
+ /* Hack -- get "bold" if out of options */
+ if (!do_turn && !do_move && m_ptr->monfear)
+ {
+ /* No longer afraid */
+ m_ptr->monfear = 0;
+
+ /* Message if seen */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s turns to fight!", m_name);
+ }
+
+ /* XXX XXX XXX Actually do something now (?) */
+ }
+}
+
+
+void summon_maint(int m_idx)
+{
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Can you pay? */
+ if ((s32b)(p_ptr->maintain_sum / 10000) > p_ptr->csp)
+ {
+ char m_name[80];
+
+ monster_desc(m_name, m_ptr, 0);
+
+ msg_format("You lose control of %s.", m_name);
+
+ /* Well, then, I guess I'm dead. */
+ delete_monster_idx(m_idx);
+ }
+ else
+ {
+ s32b cl, ml, floor, cost;
+
+ cl = get_skill_scale(SKILL_SUMMON, 100);
+ ml = m_ptr->level * 10000;
+
+ /* Floor = 19 * ml / 990 + 8 / 199
+ This gives a floor of 0.1 at level 1 and a floor of 2 at level 100
+
+ Since ml is multiplied by 10000 already, we multiply the 8/199 too
+ */
+ floor = ml * 19 / 990 + 80000 / 199;
+ cost = (ml / cl - 10000) / 4;
+ if(cost < floor)
+ cost = floor;
+
+ /* Well, then I'll take my wages from you. */
+ p_ptr->maintain_sum += cost;
+ }
+ return;
+}
+
+
+/*
+ * Process all the "live" monsters, once per game turn.
+ *
+ * During each game turn, we scan through the list of all the "live" monsters,
+ * (backwards, so we can excise any "freshly dead" monsters), energizing each
+ * monster, and allowing fully energized monsters to move, attack, pass, etc.
+ *
+ * Note that monsters can never move in the monster array (except when the
+ * "compact_monsters()" function is called by "dungeon()" or "save_player()").
+ *
+ * This function is responsible for at least half of the processor time
+ * on a normal system with a "normal" amount of monsters and a player doing
+ * normal things.
+ *
+ * When the player is resting, virtually 90% of the processor time is spent
+ * in this function, and its children, "process_monster()" and "make_move()".
+ *
+ * Most of the rest of the time is spent in "update_view()" and "lite_spot()",
+ * especially when the player is running.
+ *
+ * Note the special "MFLAG_BORN" flag, which allows us to ignore "fresh"
+ * monsters while they are still being "born". A monster is "fresh" only
+ * during the turn in which it is created, and we use the "hack_m_idx" to
+ * determine if the monster is yet to be processed during the current turn.
+ *
+ * Note the special "MFLAG_NICE" flag, which allows the player to get one
+ * move before any "nasty" monsters get to use their spell attacks.
+ *
+ * Note that when the "knowledge" about the currently tracked monster
+ * changes (flags, attacks, spells), we induce a redraw of the monster
+ * recall window.
+ */
+void process_monsters(void)
+{
+ int i, e;
+ int fx, fy;
+
+ bool_ test;
+ bool_ is_frien = FALSE;
+
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ int old_monster_race_idx;
+
+ u32b old_r_flags1 = 0L;
+ u32b old_r_flags2 = 0L;
+ u32b old_r_flags3 = 0L;
+ u32b old_r_flags4 = 0L;
+ u32b old_r_flags5 = 0L;
+ u32b old_r_flags6 = 0L;
+
+ byte old_r_blows0 = 0;
+ byte old_r_blows1 = 0;
+ byte old_r_blows2 = 0;
+ byte old_r_blows3 = 0;
+
+ byte old_r_cast_inate = 0;
+ byte old_r_cast_spell = 0;
+
+ /* Check the doppleganger */
+ if (doppleganger && !(r_info[m_list[doppleganger].r_idx].flags9 & RF9_DOPPLEGANGER))
+ doppleganger = 0;
+
+ /* Memorize old race */
+ old_monster_race_idx = monster_race_idx;
+
+ /* Acquire knowledge */
+ if (monster_race_idx)
+ {
+ /* Acquire current monster */
+ r_ptr = &r_info[monster_race_idx];
+
+ /* Memorize flags */
+ old_r_flags1 = r_ptr->r_flags1;
+ old_r_flags2 = r_ptr->r_flags2;
+ old_r_flags3 = r_ptr->r_flags3;
+ old_r_flags4 = r_ptr->r_flags4;
+ old_r_flags5 = r_ptr->r_flags5;
+ old_r_flags6 = r_ptr->r_flags6;
+
+ /* Memorize blows */
+ old_r_blows0 = r_ptr->r_blows[0];
+ old_r_blows1 = r_ptr->r_blows[1];
+ old_r_blows2 = r_ptr->r_blows[2];
+ old_r_blows3 = r_ptr->r_blows[3];
+
+ /* Memorize castings */
+ old_r_cast_inate = r_ptr->r_cast_inate;
+ old_r_cast_spell = r_ptr->r_cast_spell;
+ }
+
+
+ /* Hack -- calculate the "player noise" */
+ noise = (1L << (30 - p_ptr->skill_stl));
+
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Calculate "upkeep" for friendly monsters */
+ if (m_ptr->status == MSTATUS_PET)
+ {
+ total_friends++;
+ total_friend_levels += m_ptr->level;
+ }
+
+
+ /* Handle "fresh" monsters */
+ if (m_ptr->mflag & (MFLAG_BORN))
+ {
+ /* No longer "fresh" */
+ m_ptr->mflag &= ~(MFLAG_BORN);
+
+ /* Skip */
+ continue;
+ }
+
+
+ /* Obtain the energy boost */
+ e = extract_energy[m_ptr->mspeed];
+
+ /* Give this monster some energy */
+ m_ptr->energy += e;
+
+
+ /* Not enough energy to move */
+ if (m_ptr->energy < 100) continue;
+
+ /* Use up "some" energy */
+ m_ptr->energy -= 100;
+
+
+ /* Hack -- Require proximity */
+ if (m_ptr->cdis >= 100) continue;
+
+
+ /* Access the race */
+ r_ptr = race_inf(m_ptr);
+
+ /* Access the location */
+ fx = m_ptr->fx;
+ fy = m_ptr->fy;
+
+
+ /* Assume no move */
+ test = FALSE;
+
+ /* Control monster aint affected by distance */
+ if (p_ptr->control == i)
+ {
+ test = TRUE;
+ }
+
+ /* No free upkeep on partial summons just because they're out
+ * of line of sight. */
+ else if (m_ptr->mflag & MFLAG_PARTIAL) test = TRUE;
+
+ /* Handle "sensing radius" */
+ else if (m_ptr->cdis <= r_ptr->aaf)
+ {
+ /* We can "sense" the player */
+ test = TRUE;
+ }
+
+ /* Handle "sight" and "aggravation" */
+ else if ((m_ptr->cdis <= MAX_SIGHT) &&
+ (player_has_los_bold(fy, fx) ||
+ p_ptr->aggravate))
+ {
+ /* We can "see" or "feel" the player */
+ test = TRUE;
+ }
+
+ /* Hack -- Monsters can "smell" the player from far away */
+ /* Note that most monsters have "aaf" of "20" or so */
+ else if (flow_by_sound &&
+ (cave[p_ptr->py][p_ptr->px].when == cave[fy][fx].when) &&
+ (cave[fy][fx].cost < MONSTER_FLOW_DEPTH) &&
+ (cave[fy][fx].cost < r_ptr->aaf))
+ {
+ /* We can "smell" the player */
+ test = TRUE;
+ }
+
+ /* Running away wont save them ! */
+ if (m_ptr->poisoned || m_ptr->bleeding) test = TRUE;
+
+ /* Do nothing */
+ if (!test) continue;
+
+ /* Save global index */
+ hack_m_idx = i;
+
+ if (is_friend(m_ptr) > 0) is_frien = TRUE;
+
+ /* Process the monster */
+ process_monster(i, is_frien);
+
+ /* Hack -- notice death or departure */
+ if (!alive || death) break;
+
+ /* If it's still alive and friendly, charge upkeep. */
+ if (m_ptr->mflag & MFLAG_PARTIAL) summon_maint(i);
+
+ /* Notice leaving */
+ if (p_ptr->leaving) break;
+ }
+
+ /* Reset global index */
+ hack_m_idx = 0;
+
+
+ /* Tracking a monster race (the same one we were before) */
+ if (monster_race_idx && (monster_race_idx == old_monster_race_idx))
+ {
+ /* Acquire monster race */
+ r_ptr = &r_info[monster_race_idx];
+
+ /* Check for knowledge change */
+ if ((old_r_flags1 != r_ptr->r_flags1) ||
+ (old_r_flags2 != r_ptr->r_flags2) ||
+ (old_r_flags3 != r_ptr->r_flags3) ||
+ (old_r_flags4 != r_ptr->r_flags4) ||
+ (old_r_flags5 != r_ptr->r_flags5) ||
+ (old_r_flags6 != r_ptr->r_flags6) ||
+ (old_r_blows0 != r_ptr->r_blows[0]) ||
+ (old_r_blows1 != r_ptr->r_blows[1]) ||
+ (old_r_blows2 != r_ptr->r_blows[2]) ||
+ (old_r_blows3 != r_ptr->r_blows[3]) ||
+ (old_r_cast_inate != r_ptr->r_cast_inate) ||
+ (old_r_cast_spell != r_ptr->r_cast_spell))
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+ }
+}
diff --git a/src/modules.c b/src/modules.c
new file mode 100644
index 00000000..39b41d20
--- /dev/null
+++ b/src/modules.c
@@ -0,0 +1,274 @@
+/* File: modules.c */
+
+/* Purpose: T-engine modules */
+
+/*
+ * Copyright (c) 2003 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+static void module_reset_dir_aux(cptr *dir, cptr new_path)
+{
+ char buf[1024];
+
+ /* Build the new path */
+ strnfmt(buf, sizeof (buf), "%s%s%s", *dir, PATH_SEP, new_path);
+
+ string_free(*dir);
+ *dir = string_make(buf);
+
+ /* Make it if needed */
+ if (!private_check_user_directory(*dir))
+ quit(format("Unable to create module dir %s\n", *dir));
+}
+
+void module_reset_dir(cptr dir, cptr new_path)
+{
+ cptr *d = 0;
+ char buf[1025];
+
+ if (!strcmp(dir, "apex")) d = &ANGBAND_DIR_APEX;
+ if (!strcmp(dir, "core")) d = &ANGBAND_DIR_CORE;
+ if (!strcmp(dir, "dngn")) d = &ANGBAND_DIR_DNGN;
+ if (!strcmp(dir, "data")) d = &ANGBAND_DIR_DATA;
+ if (!strcmp(dir, "edit")) d = &ANGBAND_DIR_EDIT;
+ if (!strcmp(dir, "file")) d = &ANGBAND_DIR_FILE;
+ if (!strcmp(dir, "help")) d = &ANGBAND_DIR_HELP;
+ if (!strcmp(dir, "info")) d = &ANGBAND_DIR_INFO;
+ if (!strcmp(dir, "scpt")) d = &ANGBAND_DIR_SCPT;
+ if (!strcmp(dir, "patch")) d = &ANGBAND_DIR_PATCH;
+ if (!strcmp(dir, "pref")) d = &ANGBAND_DIR_PREF;
+ if (!strcmp(dir, "xtra")) d = &ANGBAND_DIR_XTRA;
+ if (!strcmp(dir, "user")) d = &ANGBAND_DIR_USER;
+ if (!strcmp(dir, "note")) d = &ANGBAND_DIR_NOTE;
+ if (!strcmp(dir, "cmov")) d = &ANGBAND_DIR_CMOV;
+ if (
+#ifdef PRIVATE_USER_PATH_APEX
+ !strcmp(dir, "apex") ||
+#endif
+ !strcmp(dir, "user") ||
+ !strcmp(dir, "note") ||
+ !strcmp(dir, "cmov"))
+ {
+ char user_path[1024];
+ /* copied from init_file_paths */
+ path_parse(user_path, 1024, PRIVATE_USER_PATH);
+ strcat(user_path, USER_PATH_VERSION);
+ strnfmt(buf, 1024, "%s%s%s", user_path, PATH_SEP, new_path);
+ string_free(*d);
+ *d = string_make(buf);
+ }
+#ifdef PRIVATE_USER_PATH_DATA
+ else if (!strcmp(dir, "data"))
+ {
+ module_reset_dir_aux(&ANGBAND_DIR_DATA, new_path);
+ }
+#endif
+ else if (!strcmp(dir, "save"))
+ {
+ module_reset_dir_aux(&ANGBAND_DIR_SAVE, new_path);
+ }
+ else
+ {
+ /* Build the new path */
+ strnfmt(buf, 1024, "%s%s%s%s%s", ANGBAND_DIR_MODULES, PATH_SEP, new_path, PATH_SEP, dir);
+
+ string_free(*d);
+ *d = string_make(buf);
+ }
+}
+
+static void dump_modules(int sel, int max)
+{
+ int i;
+
+ char buf[40], pre = ' ', post = ')';
+ cptr name;
+
+ char ind;
+
+
+ for (i = 0; i < max; i++)
+ {
+ ind = I2A(i % 26);
+ if (i >= 26) ind = toupper(ind);
+
+ if (sel == i)
+ {
+ pre = '[';
+ post = ']';
+ }
+ else
+ {
+ pre = ' ';
+ post = ')';
+ }
+
+ call_lua("get_module_name", "(d)", "s", i, &name);
+ strnfmt(buf, 40, "%c%c%c %s", pre, ind, post, name);
+
+ if (sel == i)
+ {
+ call_lua("get_module_desc", "(d)", "s", i, &name);
+ print_desc_aux(name, 5, 0);
+
+ c_put_str(TERM_L_BLUE, buf, 10 + (i / 4), 20 * (i % 4));
+ }
+ else
+ put_str(buf, 10 + (i / 4), 20 * (i % 4));
+ }
+}
+
+static void activate_module()
+{
+ /* Initialize the module table */
+ call_lua("assign_current_module", "(s)", "", game_module);
+
+ /* Do misc inits */
+ call_lua("get_module_info", "(s)", "d", "max_plev", &max_plev);
+ call_lua("get_module_info", "(s)", "d", "death_dungeon", &DUNGEON_DEATH);
+
+ call_lua("get_module_info", "(s)", "d", "random_artifact_weapon_chance", &RANDART_WEAPON);
+ call_lua("get_module_info", "(s)", "d", "random_artifact_armor_chance", &RANDART_ARMOR);
+ call_lua("get_module_info", "(s)", "d", "random_artifact_jewelry_chance", &RANDART_JEWEL);
+
+ call_lua("get_module_info", "(s,d)", "d", "version", 1, &VERSION_MAJOR);
+ call_lua("get_module_info", "(s,d)", "d", "version", 2, &VERSION_MINOR);
+ call_lua("get_module_info", "(s,d)", "d", "version", 3, &VERSION_PATCH);
+ version_major = VERSION_MAJOR;
+ version_minor = VERSION_MINOR;
+ version_patch = VERSION_PATCH;
+
+ /* Change window name if needed */
+ if (strcmp(game_module, "ToME"))
+ {
+ strnfmt(angband_term_name[0], 79, "T-Engine: %s", game_module);
+ Term_xtra(TERM_XTRA_RENAME_MAIN_WIN, 0);
+ }
+
+ /* Reprocess the player name, just in case */
+ process_player_base();
+}
+
+/* Did the player force a module on command line */
+cptr force_module = NULL;
+
+/* Display possible modules and select one */
+bool_ select_module()
+{
+ s32b k, sel, max;
+
+ /* Init some lua */
+ init_lua();
+
+ /* Some ports need to separate the module scripts from the installed mods,
+ so we need to check for these in two different places */
+ if(!tome_dofile_anywhere(ANGBAND_DIR_CORE, "mods_aux.lua", FALSE))
+ tome_dofile_anywhere(ANGBAND_DIR_MODULES, "mods_aux.lua", TRUE);
+ if(!tome_dofile_anywhere(ANGBAND_DIR_CORE, "modules.lua", FALSE))
+ tome_dofile_anywhere(ANGBAND_DIR_MODULES, "modules.lua", TRUE);
+
+ /* Grab the savefiles */
+ call_lua("max_modules", "()", "d", &max);
+
+ /* No need to bother the player if there is only one module */
+ sel = -1;
+ if (force_module)
+ call_lua("find_module", "(s)", "d", force_module, &sel);
+ if (max == 1)
+ sel = 0;
+ if (sel != -1)
+ {
+ cptr tmp;
+
+ /* Process the module */
+ call_lua("init_module", "(d)", "", sel);
+ call_lua("get_module_name", "(d)", "s", sel, &tmp);
+ game_module = string_make(tmp);
+
+ activate_module();
+
+ return FALSE;
+ }
+
+ sel = 0;
+
+ /* Preprocess the basic prefs, we need them to have movement keys */
+ process_pref_file("pref.prf");
+
+ while (TRUE)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Let the user choose */
+ c_put_str(TERM_YELLOW, "Welcome to ToME, you must select a module to play,", 1, 12);
+ c_put_str(TERM_YELLOW, "either ToME official module or third party ones.", 2, 13);
+ put_str("Press 8/2/4/6 to move, Return to select and Esc to quit.", 4, 3);
+
+ dump_modules(sel, max);
+
+ k = inkey();
+
+ if (k == ESCAPE)
+ {
+ quit(NULL);
+ }
+ if (k == '6')
+ {
+ sel++;
+ if (sel >= max) sel = 0;
+ continue;
+ }
+ else if (k == '4')
+ {
+ sel--;
+ if (sel < 0) sel = max - 1;
+ continue;
+ }
+ else if (k == '2')
+ {
+ sel += 4;
+ if (sel >= max) sel = sel % max;
+ continue;
+ }
+ else if (k == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = (sel + max - 1) % max;
+ continue;
+ }
+ else if (k == '\r')
+ {
+ if (sel < 26) k = I2A(sel);
+ else k = toupper(I2A(sel));
+ }
+
+ {
+ int x;
+ cptr tmp;
+
+ if (islower(k)) x = A2I(k);
+ else x = A2I(tolower(k)) + 26;
+
+ if ((x < 0) || (x >= max)) continue;
+
+ /* Process the module */
+ call_lua("init_module", "(d)", "", x);
+ call_lua("get_module_name", "(d)", "s", x, &tmp);
+ game_module = string_make(tmp);
+
+ activate_module();
+
+ return (FALSE);
+ }
+ }
+
+ /* Shouldnt happen */
+ return (FALSE);
+}
diff --git a/src/monster.pkg b/src/monster.pkg
new file mode 100644
index 00000000..a9efd089
--- /dev/null
+++ b/src/monster.pkg
@@ -0,0 +1,2324 @@
+/* File: monster.pkg */
+
+/*
+ * Purpose: Lua interface defitions for monsters.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/* To make easy object creations */
+$static monster_type lua_monster_forge;
+/** @var monster_forge;
+ * @brief monster_type
+ */
+static monster_type lua_monster_forge @ monster_forge;
+
+/** @name Monster status
+ * @note Player POV
+ * @{ */
+/** @def MSTATUS_ENEMY */
+#define MSTATUS_ENEMY -2
+
+/** @def MSTATUS_NEUTRAL_M */
+#define MSTATUS_NEUTRAL_M -1
+
+/** @def MSTATUS_NEUTRAL */
+#define MSTATUS_NEUTRAL 0
+
+/** @def MSTATUS_NEUTRAL_P */
+#define MSTATUS_NEUTRAL_P 1
+
+/** @def MSTATUS_FRIEND */
+#define MSTATUS_FRIEND 2
+
+/** @def MSTATUS_PET */
+#define MSTATUS_PET 3
+
+/** @def MSTATUS_COMPANION */
+#define MSTATUS_COMPANION 4
+/** @} */
+
+/** @name Race flags #1
+ * @{ */
+/** @def RF1_UNIQUE
+ * @note Unique Monster
+ */
+#define RF1_UNIQUE 0x00000001
+/** @def RF1_QUESTOR
+ * @note Quest Monster
+ */
+#define RF1_QUESTOR 0x00000002
+/** @def RF1_MALE
+ * @note Male gender
+ */
+#define RF1_MALE 0x00000004
+/** @def RF1_FEMALE
+ * @note Female gender
+ */
+#define RF1_FEMALE 0x00000008
+/** @def RF1_CHAR_CLEAR
+ * @note Absorbs symbol
+ */
+#define RF1_CHAR_CLEAR 0x00000010
+/** @def RF1_CHAR_MULTI
+ * @note Changes symbol
+ */
+#define RF1_CHAR_MULTI 0x00000020
+/** @def RF1_ATTR_CLEAR
+ * @note Absorbs color
+ */
+#define RF1_ATTR_CLEAR 0x00000040
+/** @def RF1_ATTR_MULTI
+ * @note Changes color
+ */
+#define RF1_ATTR_MULTI 0x00000080
+/** @def RF1_FORCE_DEPTH
+ * @note Start at "correct" depth
+ */
+#define RF1_FORCE_DEPTH 0x00000100
+/** @def RF1_FORCE_MAXHP
+ * @note Start with max hitpoints
+ */
+#define RF1_FORCE_MAXHP 0x00000200
+/** @def RF1_FORCE_SLEEP
+ * @note Start out sleeping
+ */
+#define RF1_FORCE_SLEEP 0x00000400
+/** @def RF1_FORCE_EXTRA
+ * @note Start out something
+ */
+#define RF1_FORCE_EXTRA 0x00000800
+/** @def RF1_FRIEND
+ * @note Arrive with a friend
+ */
+#define RF1_FRIEND 0x00001000
+/** @def RF1_FRIENDS
+ * @note Arrive with some friends
+ */
+#define RF1_FRIENDS 0x00002000
+/** @def RF1_ESCORT
+ * @note Arrive with an escort
+ */
+#define RF1_ESCORT 0x00004000
+/** @def RF1_ESCORTS
+ * @note Arrive with some escorts
+ */
+#define RF1_ESCORTS 0x00008000
+/** @def RF1_NEVER_BLOW
+ * @note Never make physical blow
+ */
+#define RF1_NEVER_BLOW 0x00010000
+/** @def RF1_NEVER_MOVE
+ * @note Never make physical move
+ */
+#define RF1_NEVER_MOVE 0x00020000
+/** @def RF1_RAND_25
+ * @note Moves randomly (25%)
+ */
+#define RF1_RAND_25 0x00040000
+/** @def RF1_RAND_50
+ * @note Moves randomly (50%)
+ */
+#define RF1_RAND_50 0x00080000
+/** @def RF1_ONLY_GOLD
+ * @note Drop only gold
+ */
+#define RF1_ONLY_GOLD 0x00100000
+/** @def RF1_ONLY_ITEM
+ * @note Drop only items
+ */
+#define RF1_ONLY_ITEM 0x00200000
+/** @def RF1_DROP_60
+ * @note Drop an item/gold (60%)
+ */
+#define RF1_DROP_60 0x00400000
+/** @def RF1_DROP_90
+ * @note Drop an item/gold (90%)
+ */
+#define RF1_DROP_90 0x00800000
+/** @def RF1_DROP_1D2
+ * @note Drop 1d2 items/gold
+ */
+#define RF1_DROP_1D2 0x01000000
+/** @def RF1_DROP_2D2
+ * @note Drop 2d2 items/gold
+ */
+#define RF1_DROP_2D2 0x02000000
+/** @def RF1_DROP_3D2
+ * @note Drop 3d2 items/gold
+ */
+#define RF1_DROP_3D2 0x04000000
+/** @def RF1_DROP_4D2
+ * @note Drop 4d2 items/gold
+ */
+#define RF1_DROP_4D2 0x08000000
+/** @def RF1_DROP_GOOD
+ * @note Drop good items
+ */
+#define RF1_DROP_GOOD 0x10000000
+/** @def RF1_DROP_GREAT
+ * @note Drop great items
+ */
+#define RF1_DROP_GREAT 0x20000000
+/** @def RF1_DROP_USEFUL
+ * @note Drop "useful" items
+ */
+#define RF1_DROP_USEFUL 0x40000000
+/** @def RF1_DROP_CHOSEN
+ * @note Drop "chosen" items
+ */
+#define RF1_DROP_CHOSEN 0x80000000
+/** @} */
+
+/** @name Race flags #2
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF2_STUPID
+ * @note Monster is stupid
+ */
+#define RF2_STUPID 0x00000001
+/** @def RF2_SMART
+ * @note Monster is smart
+ */
+#define RF2_SMART 0x00000002
+/** @def RF2_CAN_SPEAK
+ * @note TY: can speak
+ */
+#define RF2_CAN_SPEAK 0x00000004
+/** @def RF2_REFLECTING
+ * @note Reflects bolts
+ */
+#define RF2_REFLECTING 0x00000008
+/** @def RF2_INVISIBLE
+ * @note Monster avoids vision
+ */
+#define RF2_INVISIBLE 0x00000010
+/** @def RF2_COLD_BLOOD
+ * @note Monster avoids infra
+ */
+#define RF2_COLD_BLOOD 0x00000020
+/** @def RF2_EMPTY_MIND
+ * @note Monster avoids telepathy
+ */
+#define RF2_EMPTY_MIND 0x00000040
+/** @def RF2_WEIRD_MIND
+ * @note Monster avoids telepathy?
+ */
+#define RF2_WEIRD_MIND 0x00000080
+/** @def RF2_DEATH_ORB
+ * @note Death Orb
+ */
+#define RF2_DEATH_ORB 0x00000100
+/** @def RF2_REGENERATE
+ * @note Monster regenerates
+ */
+#define RF2_REGENERATE 0x00000200
+/** @def RF2_SHAPECHANGER
+ * @note TY: shapechanger
+ */
+#define RF2_SHAPECHANGER 0x00000400
+/** @def RF2_ATTR_ANY
+ * @note TY: Attr_any
+ */
+#define RF2_ATTR_ANY 0x00000800
+/** @def RF2_POWERFUL
+ * @note Monster has strong breath
+ */
+#define RF2_POWERFUL 0x00001000
+/** @def RF2_ELDRITCH_HORROR
+ * @note Sanity-blasting horror
+ */
+#define RF2_ELDRITCH_HORROR 0x00002000
+/** @def RF2_AURA_FIRE
+ * @note Burns in melee
+ */
+#define RF2_AURA_FIRE 0x00004000
+/** @def RF2_AURA_ELEC
+ * @note Shocks in melee
+ */
+#define RF2_AURA_ELEC 0x00008000
+/** @def RF2_OPEN_DOOR
+ * @note Monster can open doors
+ */
+#define RF2_OPEN_DOOR 0x00010000
+/** @def RF2_BASH_DOOR
+ * @note Monster can bash doors
+ */
+#define RF2_BASH_DOOR 0x00020000
+/** @def RF2_PASS_WALL
+ * @note Monster can pass walls
+ */
+#define RF2_PASS_WALL 0x00040000
+/** @def RF2_KILL_WALL
+ * @note Monster can destroy walls
+ */
+#define RF2_KILL_WALL 0x00080000
+/** @def RF2_MOVE_BODY
+ * @note Monster can move monsters
+ */
+#define RF2_MOVE_BODY 0x00100000
+/** @def RF2_KILL_BODY
+ * @note Monster can kill monsters
+ */
+#define RF2_KILL_BODY 0x00200000
+/** @def RF2_TAKE_ITEM
+ * @note Monster can pick up items
+ */
+#define RF2_TAKE_ITEM 0x00400000
+/** @def RF2_KILL_ITEM
+ * @note Monster can crush items
+ */
+#define RF2_KILL_ITEM 0x00800000
+/** @def RF2_BRAIN_1 */
+#define RF2_BRAIN_1 0x01000000
+
+/** @def RF2_BRAIN_2 */
+#define RF2_BRAIN_2 0x02000000
+
+/** @def RF2_BRAIN_3 */
+#define RF2_BRAIN_3 0x04000000
+
+/** @def RF2_BRAIN_4 */
+#define RF2_BRAIN_4 0x08000000
+
+/** @def RF2_BRAIN_5 */
+#define RF2_BRAIN_5 0x10000000
+
+/** @def RF2_BRAIN_6 */
+#define RF2_BRAIN_6 0x20000000
+
+/** @def RF2_BRAIN_7 */
+#define RF2_BRAIN_7 0x40000000
+
+/** @def RF2_BRAIN_8 */
+#define RF2_BRAIN_8 0x80000000
+/** @} */
+
+/** @name Race flags #3
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF3_ORC
+ * @note Orc
+ */
+#define RF3_ORC 0x00000001
+/** @def RF3_TROLL
+ * @note Troll
+ */
+#define RF3_TROLL 0x00000002
+/** @def RF3_GIANT
+ * @note Giant
+ */
+#define RF3_GIANT 0x00000004
+/** @def RF3_DRAGON
+ * @note Dragon
+ */
+#define RF3_DRAGON 0x00000008
+/** @def RF3_DEMON
+ * @note Demon
+ */
+#define RF3_DEMON 0x00000010
+/** @def RF3_UNDEAD
+ * @note Undead
+ */
+#define RF3_UNDEAD 0x00000020
+/** @def RF3_EVIL
+ * @note Evil
+ */
+#define RF3_EVIL 0x00000040
+/** @def RF3_ANIMAL
+ * @note Animal
+ */
+#define RF3_ANIMAL 0x00000080
+/** @def RF3_THUNDERLORD
+ * @note DG: Thunderlord
+ */
+#define RF3_THUNDERLORD 0x00000100
+/** @def RF3_GOOD
+ * @note Good
+ */
+#define RF3_GOOD 0x00000200
+/** @def RF3_AURA_COLD
+ * @note Freezes in melee
+ */
+#define RF3_AURA_COLD 0x00000400
+/** @def RF3_NONLIVING
+ * @note TY: Non-Living (?)
+ */
+#define RF3_NONLIVING 0x00000800
+/** @def RF3_HURT_LITE
+ * @note Hurt by lite
+ */
+#define RF3_HURT_LITE 0x00001000
+/** @def RF3_HURT_ROCK
+ * @note Hurt by rock remover
+ */
+#define RF3_HURT_ROCK 0x00002000
+/** @def RF3_SUSCEP_FIRE
+ * @note Hurt badly by fire
+ */
+#define RF3_SUSCEP_FIRE 0x00004000
+/** @def RF3_SUSCEP_COLD
+ * @note Hurt badly by cold
+ */
+#define RF3_SUSCEP_COLD 0x00008000
+/** @def RF3_IM_ACID
+ * @note Resist acid a lot
+ */
+#define RF3_IM_ACID 0x00010000
+/** @def RF3_IM_ELEC
+ * @note Resist elec a lot
+ */
+#define RF3_IM_ELEC 0x00020000
+/** @def RF3_IM_FIRE
+ * @note Resist fire a lot
+ */
+#define RF3_IM_FIRE 0x00040000
+/** @def RF3_IM_COLD
+ * @note Resist cold a lot
+ */
+#define RF3_IM_COLD 0x00080000
+/** @def RF3_IM_POIS
+ * @note Resist poison a lot
+ */
+#define RF3_IM_POIS 0x00100000
+/** @def RF3_RES_TELE
+ * @note Resist teleportation
+ */
+#define RF3_RES_TELE 0x00200000
+/** @def RF3_RES_NETH
+ * @note Resist nether a lot
+ */
+#define RF3_RES_NETH 0x00400000
+/** @def RF3_RES_WATE
+ * @note Resist water
+ */
+#define RF3_RES_WATE 0x00800000
+/** @def RF3_RES_PLAS
+ * @note Resist plasma
+ */
+#define RF3_RES_PLAS 0x01000000
+/** @def RF3_RES_NEXU
+ * @note Resist nexus
+ */
+#define RF3_RES_NEXU 0x02000000
+/** @def RF3_RES_DISE
+ * @note Resist disenchantment
+ */
+#define RF3_RES_DISE 0x04000000
+/** @def RF3_UNIQUE_4
+ * @note Is a "Nazgul" unique
+ */
+#define RF3_UNIQUE_4 0x08000000
+/** @def RF3_NO_FEAR
+ * @note Cannot be scared
+ */
+#define RF3_NO_FEAR 0x10000000
+/** @def RF3_NO_STUN
+ * @note Cannot be stunned
+ */
+#define RF3_NO_STUN 0x20000000
+/** @def RF3_NO_CONF
+ * @note Cannot be confused
+ */
+#define RF3_NO_CONF 0x40000000
+/** @def RF3_NO_SLEEP
+ * @note Cannot be slept
+ */
+#define RF3_NO_SLEEP 0x80000000
+/** @} */
+
+/** @name Race flags #4
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF4_SHRIEK
+ * @note Shriek for help
+ */
+#define RF4_SHRIEK 0x00000001
+/** @def RF4_MULTIPLY
+ * @note Monster reproduces
+ */
+#define RF4_MULTIPLY 0x00000002
+/** @def RF4_S_ANIMAL
+ * @note Summon animals
+ */
+#define RF4_S_ANIMAL 0x00000004
+/** @def RF4_ROCKET
+ * @note TY: Rocket
+ */
+#define RF4_ROCKET 0x00000008
+/** @def RF4_ARROW_1
+ * @note Fire an arrow (light)
+ */
+#define RF4_ARROW_1 0x00000010
+/** @def RF4_ARROW_2
+ * @note Fire an arrow (heavy)
+ */
+#define RF4_ARROW_2 0x00000020
+/** @def RF4_ARROW_3
+ * @note Fire missiles (light)
+ */
+#define RF4_ARROW_3 0x00000040
+/** @def RF4_ARROW_4
+ * @note Fire missiles (heavy)
+ */
+#define RF4_ARROW_4 0x00000080
+/** @def RF4_BR_ACID
+ * @note Breathe Acid
+ */
+#define RF4_BR_ACID 0x00000100
+/** @def RF4_BR_ELEC
+ * @note Breathe Elec
+ */
+#define RF4_BR_ELEC 0x00000200
+/** @def RF4_BR_FIRE
+ * @note Breathe Fire
+ */
+#define RF4_BR_FIRE 0x00000400
+/** @def RF4_BR_COLD
+ * @note Breathe Cold
+ */
+#define RF4_BR_COLD 0x00000800
+/** @def RF4_BR_POIS
+ * @note Breathe Poison
+ */
+#define RF4_BR_POIS 0x00001000
+/** @def RF4_BR_NETH
+ * @note Breathe Nether
+ */
+#define RF4_BR_NETH 0x00002000
+/** @def RF4_BR_LITE
+ * @note Breathe Lite
+ */
+#define RF4_BR_LITE 0x00004000
+/** @def RF4_BR_DARK
+ * @note Breathe Dark
+ */
+#define RF4_BR_DARK 0x00008000
+/** @def RF4_BR_CONF
+ * @note Breathe Confusion
+ */
+#define RF4_BR_CONF 0x00010000
+/** @def RF4_BR_SOUN
+ * @note Breathe Sound
+ */
+#define RF4_BR_SOUN 0x00020000
+/** @def RF4_BR_CHAO
+ * @note Breathe Chaos
+ */
+#define RF4_BR_CHAO 0x00040000
+/** @def RF4_BR_DISE
+ * @note Breathe Disenchant
+ */
+#define RF4_BR_DISE 0x00080000
+/** @def RF4_BR_NEXU
+ * @note Breathe Nexus
+ */
+#define RF4_BR_NEXU 0x00100000
+/** @def RF4_BR_TIME
+ * @note Breathe Time
+ */
+#define RF4_BR_TIME 0x00200000
+/** @def RF4_BR_INER
+ * @note Breathe Inertia
+ */
+#define RF4_BR_INER 0x00400000
+/** @def RF4_BR_GRAV
+ * @note Breathe Gravity
+ */
+#define RF4_BR_GRAV 0x00800000
+/** @def RF4_BR_SHAR
+ * @note Breathe Shards
+ */
+#define RF4_BR_SHAR 0x01000000
+/** @def RF4_BR_PLAS
+ * @note Breathe Plasma
+ */
+#define RF4_BR_PLAS 0x02000000
+/** @def RF4_BR_WALL
+ * @note Breathe Force
+ */
+#define RF4_BR_WALL 0x04000000
+/** @def RF4_BR_MANA
+ * @note Breathe Mana
+ */
+#define RF4_BR_MANA 0x08000000
+/** @def RF4_BA_NUKE
+ * @note TY: Nuke Ball
+ */
+#define RF4_BA_NUKE 0x10000000
+/** @def RF4_BR_NUKE
+ * @note TY: Toxic Breath
+ */
+#define RF4_BR_NUKE 0x20000000
+/** @def RF4_BA_CHAO
+ * @note Chaos Ball
+ */
+#define RF4_BA_CHAO 0x40000000
+/** @def RF4_BR_DISI
+ * @note Breathe Disintegration
+ */
+#define RF4_BR_DISI 0x80000000
+/** @} */
+
+/** @name Race flags #5
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF5_BA_ACID
+ * @note Acid Ball
+ */
+#define RF5_BA_ACID 0x00000001
+/** @def RF5_BA_ELEC
+ * @note Elec Ball
+ */
+#define RF5_BA_ELEC 0x00000002
+/** @def RF5_BA_FIRE
+ * @note Fire Ball
+ */
+#define RF5_BA_FIRE 0x00000004
+/** @def RF5_BA_COLD
+ * @note Cold Ball
+ */
+#define RF5_BA_COLD 0x00000008
+/** @def RF5_BA_POIS
+ * @note Poison Ball
+ */
+#define RF5_BA_POIS 0x00000010
+/** @def RF5_BA_NETH
+ * @note Nether Ball
+ */
+#define RF5_BA_NETH 0x00000020
+/** @def RF5_BA_WATE
+ * @note Water Ball
+ */
+#define RF5_BA_WATE 0x00000040
+/** @def RF5_BA_MANA
+ * @note Mana Storm
+ */
+#define RF5_BA_MANA 0x00000080
+/** @def RF5_BA_DARK
+ * @note Darkness Storm
+ */
+#define RF5_BA_DARK 0x00000100
+/** @def RF5_DRAIN_MANA
+ * @note Drain Mana
+ */
+#define RF5_DRAIN_MANA 0x00000200
+/** @def RF5_MIND_BLAST
+ * @note Blast Mind
+ */
+#define RF5_MIND_BLAST 0x00000400
+/** @def RF5_BRAIN_SMASH
+ * @note Smash Brain
+ */
+#define RF5_BRAIN_SMASH 0x00000800
+/** @def RF5_CAUSE_1
+ * @note Cause Light Wound
+ */
+#define RF5_CAUSE_1 0x00001000
+/** @def RF5_CAUSE_2
+ * @note Cause Serious Wound
+ */
+#define RF5_CAUSE_2 0x00002000
+/** @def RF5_CAUSE_3
+ * @note Cause Critical Wound
+ */
+#define RF5_CAUSE_3 0x00004000
+/** @def RF5_CAUSE_4
+ * @note Cause Mortal Wound
+ */
+#define RF5_CAUSE_4 0x00008000
+/** @def RF5_BO_ACID
+ * @note Acid Bolt
+ */
+#define RF5_BO_ACID 0x00010000
+/** @def RF5_BO_ELEC
+ * @note Elec Bolt (unused)
+ */
+#define RF5_BO_ELEC 0x00020000
+/** @def RF5_BO_FIRE
+ * @note Fire Bolt
+ */
+#define RF5_BO_FIRE 0x00040000
+/** @def RF5_BO_COLD
+ * @note Cold Bolt
+ */
+#define RF5_BO_COLD 0x00080000
+/** @def RF5_BO_POIS
+ * @note Poison Bolt (unused)
+ */
+#define RF5_BO_POIS 0x00100000
+/** @def RF5_BO_NETH
+ * @note Nether Bolt
+ */
+#define RF5_BO_NETH 0x00200000
+/** @def RF5_BO_WATE
+ * @note Water Bolt
+ */
+#define RF5_BO_WATE 0x00400000
+/** @def RF5_BO_MANA
+ * @note Mana Bolt
+ */
+#define RF5_BO_MANA 0x00800000
+/** @def RF5_BO_PLAS
+ * @note Plasma Bolt
+ */
+#define RF5_BO_PLAS 0x01000000
+/** @def RF5_BO_ICEE
+ * @note Ice Bolt
+ */
+#define RF5_BO_ICEE 0x02000000
+/** @def RF5_MISSILE
+ * @note Magic Missile
+ */
+#define RF5_MISSILE 0x04000000
+/** @def RF5_SCARE
+ * @note Frighten Player
+ */
+#define RF5_SCARE 0x08000000
+/** @def RF5_BLIND
+ * @note Blind Player
+ */
+#define RF5_BLIND 0x10000000
+/** @def RF5_CONF
+ * @note Confuse Player
+ */
+#define RF5_CONF 0x20000000
+/** @def RF5_SLOW
+ * @note Slow Player
+ */
+#define RF5_SLOW 0x40000000
+/** @def RF5_HOLD
+ * @note Paralyze Player
+ */
+#define RF5_HOLD 0x80000000
+/** @} */
+
+/** @name Race flags #6
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF6_HASTE
+ * @note Speed self
+ */
+#define RF6_HASTE 0x00000001
+/** @def RF6_HAND_DOOM
+ * @note Hand of Doom
+ */
+#define RF6_HAND_DOOM 0x00000002
+/** @def RF6_HEAL
+ * @note Heal self
+ */
+#define RF6_HEAL 0x00000004
+/** @def RF6_S_ANIMALS
+ * @note Summon animals
+ */
+#define RF6_S_ANIMALS 0x00000008
+/** @def RF6_BLINK
+ * @note Teleport Short
+ */
+#define RF6_BLINK 0x00000010
+/** @def RF6_TPORT
+ * @note Teleport Long
+ */
+#define RF6_TPORT 0x00000020
+/** @def RF6_TELE_TO
+ * @note Move player to monster
+ */
+#define RF6_TELE_TO 0x00000040
+/** @def RF6_TELE_AWAY
+ * @note Move player far away
+ */
+#define RF6_TELE_AWAY 0x00000080
+/** @def RF6_TELE_LEVEL
+ * @note Move player vertically
+ */
+#define RF6_TELE_LEVEL 0x00000100
+/** @def RF6_DARKNESS
+ * @note Create Darkness
+ */
+#define RF6_DARKNESS 0x00000200
+/** @def RF6_TRAPS
+ * @note Create Traps
+ */
+#define RF6_TRAPS 0x00000400
+/** @def RF6_FORGET
+ * @note Cause amnesia
+ */
+#define RF6_FORGET 0x00000800
+/** @def RF6_RAISE_DEAD
+ * @note Raise Dead
+ */
+#define RF6_RAISE_DEAD 0x00001000
+/** @def RF6_S_BUG
+ * @note Summon Software bug
+ */
+#define RF6_S_BUG 0x00002000
+/** @def RF6_S_RNG
+ * @note Summon RNG
+ */
+#define RF6_S_RNG 0x00004000
+/** @def RF6_S_THUNDERLORD
+ * @note Summon Thunderlords
+ */
+#define RF6_S_THUNDERLORD 0x00008000
+/** @def RF6_S_KIN
+ * @note Summon "kin"
+ */
+#define RF6_S_KIN 0x00010000
+/** @def RF6_S_HI_DEMON
+ * @note Summon greater demons!
+ */
+#define RF6_S_HI_DEMON 0x00020000
+/** @def RF6_S_MONSTER
+ * @note Summon Monster
+ */
+#define RF6_S_MONSTER 0x00040000
+/** @def RF6_S_MONSTERS
+ * @note Summon Monsters
+ */
+#define RF6_S_MONSTERS 0x00080000
+/** @def RF6_S_ANT
+ * @note Summon Ants
+ */
+#define RF6_S_ANT 0x00100000
+/** @def RF6_S_SPIDER
+ * @note Summon Spiders
+ */
+#define RF6_S_SPIDER 0x00200000
+/** @def RF6_S_HOUND
+ * @note Summon Hounds
+ */
+#define RF6_S_HOUND 0x00400000
+/** @def RF6_S_HYDRA
+ * @note Summon Hydras
+ */
+#define RF6_S_HYDRA 0x00800000
+/** @def RF6_S_ANGEL
+ * @note Summon Angel
+ */
+#define RF6_S_ANGEL 0x01000000
+/** @def RF6_S_DEMON
+ * @note Summon Demon
+ */
+#define RF6_S_DEMON 0x02000000
+/** @def RF6_S_UNDEAD
+ * @note Summon Undead
+ */
+#define RF6_S_UNDEAD 0x04000000
+/** @def RF6_S_DRAGON
+ * @note Summon Dragon
+ */
+#define RF6_S_DRAGON 0x08000000
+/** @def RF6_S_HI_UNDEAD
+ * @note Summon Greater Undead
+ */
+#define RF6_S_HI_UNDEAD 0x10000000
+/** @def RF6_S_HI_DRAGON
+ * @note Summon Ancient Dragon
+ */
+#define RF6_S_HI_DRAGON 0x20000000
+/** @def RF6_S_WRAITH
+ * @note Summon Unique Wraith
+ */
+#define RF6_S_WRAITH 0x40000000
+/** @def RF6_S_UNIQUE
+ * @note Summon Unique Monster
+ */
+#define RF6_S_UNIQUE 0x80000000
+/** @} */
+
+/** @name Race flags #7
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF7_AQUATIC
+ * @note Aquatic monster
+ */
+#define RF7_AQUATIC 0x00000001
+/** @def RF7_CAN_SWIM
+ * @note Monster can swim
+ */
+#define RF7_CAN_SWIM 0x00000002
+/** @def RF7_CAN_FLY
+ * @note Monster can fly
+ */
+#define RF7_CAN_FLY 0x00000004
+/** @def RF7_FRIENDLY
+ * @note Monster is friendly
+ */
+#define RF7_FRIENDLY 0x00000008
+/** @def RF7_PET
+ * @note Monster is a pet
+ */
+#define RF7_PET 0x00000010
+/** @def RF7_MORTAL
+ * @note Monster is a mortal being
+ */
+#define RF7_MORTAL 0x00000020
+/** @def RF7_SPIDER
+ * @note Monster is a spider (can pass webs)
+ */
+#define RF7_SPIDER 0x00000040
+/** @def RF7_NAZGUL
+ * @note Monster is a Nazgul
+ */
+#define RF7_NAZGUL 0x00000080
+/** @def RF7_DG_CURSE
+ * @note If killed the monster grant a DG Curse to the player
+ */
+#define RF7_DG_CURSE 0x00000100
+/** @def RF7_POSSESSOR
+ * @note Is it a dreaded possessor monster ?
+ */
+#define RF7_POSSESSOR 0x00000200
+/** @def RF7_NO_DEATH
+ * @note Cannot be killed
+ */
+#define RF7_NO_DEATH 0x00000400
+/** @def RF7_NO_TARGET
+ * @note Cannot be targeted
+ */
+#define RF7_NO_TARGET 0x00000800
+/** @def RF7_AI_ANNOY
+ * @note Try to tease the player
+ */
+#define RF7_AI_ANNOY 0x00001000
+/** @def RF7_AI_SPECIAL
+ * @note For quests
+ */
+#define RF7_AI_SPECIAL 0x00002000
+/** @def RF7_NO_THEFT
+ * @note Monster is immune to theft
+ */
+#define RF7_NO_THEFT 0x00040000
+/** @def RF7_SPIRIT
+ * @note This is a Spirit, coming from the Void
+ */
+#define RF7_SPIRIT 0x00080000
+/** @def RF7_IM_MELEE
+ * @note IM melee
+ */
+#define RF7_IM_MELEE 0x00100000
+/** @} */
+
+/** @name Race flags #8
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF8_DUNGEON */
+#define RF8_DUNGEON 0x00000001
+
+/** @def RF8_WILD_TOWN */
+#define RF8_WILD_TOWN 0x00000002
+
+/** @def RF8_XXX8X02 */
+#define RF8_XXX8X02 0x00000004
+
+/** @def RF8_WILD_SHORE */
+#define RF8_WILD_SHORE 0x00000008
+
+/** @def RF8_WILD_OCEAN */
+#define RF8_WILD_OCEAN 0x00000010
+
+/** @def RF8_WILD_WASTE */
+#define RF8_WILD_WASTE 0x00000020
+
+/** @def RF8_WILD_WOOD */
+#define RF8_WILD_WOOD 0x00000040
+
+/** @def RF8_WILD_VOLCANO */
+#define RF8_WILD_VOLCANO 0x00000080
+
+/** @def RF8_XXX8X08 */
+#define RF8_XXX8X08 0x00000100
+
+/** @def RF8_WILD_MOUNTAIN */
+#define RF8_WILD_MOUNTAIN 0x00000200
+
+/** @def RF8_WILD_GRASS */
+#define RF8_WILD_GRASS 0x00000400
+
+/********* FREE *********/
+/** @def RF8_CTHANGBAND */
+#define RF8_CTHANGBAND 0x00001000
+
+/********* FREE *********/
+/** @def RF8_ZANGBAND */
+#define RF8_ZANGBAND 0x00004000
+
+/** @def RF8_JOKEANGBAND */
+#define RF8_JOKEANGBAND 0x00008000
+
+/** @def RF8_ANGBAND */
+#define RF8_ANGBAND 0x00010000
+
+
+/** @def RF8_WILD_TOO */
+#define RF8_WILD_TOO 0x80000000
+/** @} */
+
+/** @name Race flags #9
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF9_DROP_CORPSE */
+#define RF9_DROP_CORPSE 0x00000001
+
+/** @def RF9_DROP_SKELETON */
+#define RF9_DROP_SKELETON 0x00000002
+
+/** @def RF9_HAS_LITE
+ * @note Carries a lite
+ */
+#define RF9_HAS_LITE 0x00000004
+/** @def RF9_MIMIC
+ * @note *REALLY* looks like an object ... only nastier
+ */
+#define RF9_MIMIC 0x00000008
+/** @def RF9_HAS_EGG
+ * @note Can be monster's eggs
+ */
+#define RF9_HAS_EGG 0x00000010
+/** @def RF9_IMPRESED
+ * @note The monster can follow you on each level until he dies
+ */
+#define RF9_IMPRESED 0x00000020
+/** @def RF9_SUSCEP_ACID
+ * @note Susceptible to acid
+ */
+#define RF9_SUSCEP_ACID 0x00000040
+/** @def RF9_SUSCEP_ELEC
+ * @note Susceptible to lightning
+ */
+#define RF9_SUSCEP_ELEC 0x00000080
+/** @def RF9_SUSCEP_POIS
+ * @note Susceptible to poison
+ */
+#define RF9_SUSCEP_POIS 0x00000100
+/** @def RF9_KILL_TREES
+ * @note Monster can eat trees
+ */
+#define RF9_KILL_TREES 0x00000200
+/** @def RF9_WYRM_PROTECT
+ * @note The monster is protected by great wyrms of power: They'll be summoned if it's killed
+ */
+#define RF9_WYRM_PROTECT 0x00000400
+/** @def RF9_DOPPLEGANGER
+ * @note The monster looks like you
+ */
+#define RF9_DOPPLEGANGER 0x00000800
+/** @def RF9_ONLY_DEPTH
+ * @note The monster can only be generated at the GIVEN depth
+ */
+#define RF9_ONLY_DEPTH 0x00001000
+/** @def RF9_SPECIAL_GENE
+ * @note The monster can only be generated in special conditions like quests, special dungeons, ...
+ */
+#define RF9_SPECIAL_GENE 0x00002000
+/** @def RF9_NEVER_GENE
+ * @note The monster cannot be normaly generated
+ */
+#define RF9_NEVER_GENE 0x00004000
+/** @} */
+
+/** @name Monster flags
+ * @{ */
+/** @def MFLAG_VIEW
+ * @note Monster is in line of sight
+ */
+#define MFLAG_VIEW 0x00000001
+/** @def MFLAG_QUEST
+ * @note Monster is subject to a quest
+ */
+#define MFLAG_QUEST 0x00000002
+/** @def MFLAG_PARTIAL
+ * @note Monster is a partial summon
+ */
+#define MFLAG_PARTIAL 0x00000004
+/** @def MFLAG_CONTROL
+ * @note Monster is controlled
+ */
+#define MFLAG_CONTROL 0x00000008
+/** @def MFLAG_BORN
+ * @note Monster is still being born
+ */
+#define MFLAG_BORN 0x00000010
+/** @def MFLAG_NICE
+ * @note Monster is still being nice
+ */
+#define MFLAG_NICE 0x00000020
+/** @def MFLAG_SHOW
+ * @note Monster is recently memorized
+ */
+#define MFLAG_SHOW 0x00000040
+/** @def MFLAG_MARK
+ * @note Monster is currently memorized
+ */
+#define MFLAG_MARK 0x00000080
+/** @def MFLAG_NO_DROP
+ * @note Monster wont drop obj/corpse
+ */
+#define MFLAG_NO_DROP 0x00000100
+/** @def MFLAG_QUEST2
+ * @note Monster is subject to a quest
+ */
+#define MFLAG_QUEST2 0x00000200
+/** @} */
+
+/** @struct monster_blow
+ * @brief Monster blows (attacks)
+ */
+struct monster_blow
+{
+ /** @structvar method
+ * @brief Number
+ */
+ byte method;
+ /** @structvar effect
+ * @brief Number
+ */
+ byte effect;
+ /** @structvar d_dice
+ * @brief Number
+ */
+ byte d_dice;
+ /** @structvar d_side
+ * @brief Number
+ */
+ byte d_side;
+};
+
+/** @struct monster_race
+ * @brief Monster race
+ */
+struct monster_race
+{
+ /** @structvar name
+ * @brief Number
+ * @note Name (offset)
+ */
+ u32b name;
+ /** @structvar text
+ * @brief Number
+ * @note Text (offset)
+ */
+ u32b text;
+
+ /** @structvar hdice
+ * @brief Number
+ * @note Creatures hit dice count
+ */
+ byte hdice;
+ /** @structvar hside
+ * @brief Number
+ * @note Creatures hit dice sides
+ */
+ byte hside;
+
+ /** @structvar ac
+ * @brief Number
+ * @note Armour Class
+ */
+ s16b ac;
+
+ /** @structvar sleep
+ * @brief Number
+ * @note Inactive counter (base)
+ */
+ s16b sleep;
+ /** @structvar aaf
+ * @brief Number
+ * @note Area affect radius (1-100)
+ */
+ byte aaf;
+ /** @structvar speed
+ * @brief Number
+ * @note Speed (normally 110)
+ */
+ byte speed;
+
+ /** @structvar mexp
+ * @brief Number
+ * @note Exp value for kill
+ */
+ s32b mexp;
+
+ /** @structvar weight
+ * @brief Number
+ * @note Weight of the monster
+ */
+ s32b weight;
+
+ /** @structvar freq_inate
+ * @brief Number
+ * @note Inate spell frequency
+ */
+ byte freq_inate;
+ /** @structvar freq_spell
+ * @brief Number
+ * @note Other spell frequency
+ */
+ byte freq_spell;
+
+ /** @structvar flags1
+ * @brief Number
+ * @note Flags 1 (general)
+ */
+ u32b flags1;
+ /** @structvar flags2
+ * @brief Number
+ * @note Flags 2 (abilities)
+ */
+ u32b flags2;
+ /** @structvar flags3
+ * @brief Number
+ * @note Flags 3 (race/resist)
+ */
+ u32b flags3;
+ /** @structvar flags4
+ * @brief Number
+ * @note Flags 4 (inate/breath)
+ */
+ u32b flags4;
+ /** @structvar flags5
+ * @brief Number
+ * @note Flags 5 (normal spells)
+ */
+ u32b flags5;
+ /** @structvar flags6
+ * @brief Number
+ * @note Flags 6 (special spells)
+ */
+ u32b flags6;
+ /** @structvar flags7
+ * @brief Number
+ * @note Flags 7 (movement related abilities)
+ */
+ u32b flags7;
+ /** @structvar flags8
+ * @brief Number
+ * @note Flags 8 (wilderness info)
+ */
+ u32b flags8;
+ /** @structvar flags9
+ * @brief Number
+ * @note Flags 9 (drops info)
+ */
+ u32b flags9;
+
+ /** @structvar blow[4]
+ * @brief magic_power
+ * @note Up to four blows per round
+ */
+ monster_blow blow[4];
+
+ /** @structvar body_parts[BODY_MAX]
+ * @brief Number
+ * @note To help to decide what to use when body changing
+ */
+ byte body_parts[BODY_MAX];
+
+ /** @structvar level
+ * @brief Number
+ * @note Level of creature
+ */
+ byte level;
+ /** @structvar rarity
+ * @brief Number
+ * @note Rarity of creature
+ */
+ byte rarity;
+
+
+ /** @structvar d_attr
+ * @brief Number
+ * @note Default monster attribute
+ */
+ byte d_attr;
+ /** @structvar d_char
+ * @brief String
+ * @note Default monster character
+ */
+ char d_char;
+
+
+ /** @structvar x_attr
+ * @brief Number
+ * @note Desired monster attribute
+ */
+ byte x_attr;
+ /** @structvar x_char
+ * @brief String
+ * @note Desired monster character
+ */
+ char x_char;
+
+
+ /** @structvar max_num
+ * @brief Number
+ * @note Maximum population allowed per level
+ */
+ s16b max_num;
+
+ /** @structvar cur_num
+ * @brief Number
+ * @note Monster population on current level
+ */
+ byte cur_num;
+
+
+ /** @structvar r_sights
+ * @brief Number
+ * @note Count sightings of this monster
+ */
+ s16b r_sights;
+ /** @structvar r_deaths
+ * @brief Number
+ * @note Count deaths from this monster
+ */
+ s16b r_deaths;
+
+ /** @structvar r_pkills
+ * @brief Number
+ * @note Count monsters killed in this life
+ */
+ s16b r_pkills;
+ /** @structvar r_tkills
+ * @brief Number
+ * @note Count monsters killed in all lives
+ */
+ s16b r_tkills;
+
+ /** @structvar r_wake
+ * @brief Number
+ * @note Number of times woken up (?)
+ */
+ byte r_wake;
+ /** @structvar r_ignore
+ * @brief Number
+ * @note Number of times ignored (?)
+ */
+ byte r_ignore;
+
+ /** @structvar r_xtra1
+ * @brief Number
+ * @note Something (unused)
+ */
+ byte r_xtra1;
+ /** @structvar r_xtra2
+ * @brief Number
+ * @note Something (unused)
+ */
+ byte r_xtra2;
+
+ /** @structvar r_drop_gold
+ * @brief Number
+ * @note Max number of gold dropped at once
+ */
+ byte r_drop_gold;
+ /** @structvar r_drop_item
+ * @brief Number
+ * @note Max number of item dropped at once
+ */
+ byte r_drop_item;
+
+ /** @structvar r_cast_inate
+ * @brief Number
+ * @note Max number of inate spells seen
+ */
+ byte r_cast_inate;
+ /** @structvar r_cast_spell
+ * @brief Number
+ * @note Max number of other spells seen
+ */
+ byte r_cast_spell;
+
+ /** @structvar r_blows[4]
+ * @brief Number
+ * @note Number of times each blow type was seen
+ */
+ byte r_blows[4];
+
+ /** @structvar r_flags1
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags1;
+ /** @structvar r_flags2
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags2;
+ /** @structvar r_flags3
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags3;
+ /** @structvar r_flags4
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags4;
+ /** @structvar r_flags5
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags5;
+ /** @structvar r_flags6
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags6;
+ /** @structvar r_flags7
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags7;
+ /** @structvar r_flags8
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags8;
+ /** @structvar r_flags9
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags9;
+
+ /** @structvar on_saved
+ * @brief Boolean
+ * @note Is the (unique) on a saved level ?
+ */
+ bool on_saved;
+
+ /** @structvar total_visible
+ * @brief Number
+ * @note Amount of this race that are visible
+ */
+ byte total_visible;
+
+ /** @structvar drops
+ * @brief obj_theme
+ * @note The drops type
+ */
+ obj_theme drops;
+};
+
+/** @struct monster_type
+ * @brief Monster type
+ */
+struct monster_type
+{
+ /** @structvar r_idx
+ * @brief Number
+ * @note Monster race index
+ */
+ s16b r_idx;
+
+ /** @structvar ego
+ * @brief Number
+ * @note Ego monster type
+ */
+ u16b ego;
+
+ /** @structvar fy
+ * @brief Number
+ * @note Y location on map
+ */
+ byte fy;
+ /** @structvar fx
+ * @brief Number
+ * @note X location on map
+ */
+ byte fx;
+
+ /** @structvar hp
+ * @brief Number
+ * @note Current Hit points
+ */
+ s16b hp;
+ /** @structvar maxhp
+ * @brief Number
+ * @note Max Hit points
+ */
+ s16b maxhp;
+
+ /** @structvar blow[4]
+ * @brief magic_power
+ * @note Up to four blows per round
+ */
+ monster_blow blow[4];
+
+ /** @structvar speed
+ * @brief Number
+ * @note Speed (normally 110)
+ */
+ byte speed;
+ /** @structvar level
+ * @brief Number
+ * @note Level of creature
+ */
+ byte level;
+ /** @structvar ac
+ * @brief Number
+ * @note Armour Class
+ */
+ s16b ac;
+ /** @structvar exp
+ * @brief Number
+ * @note Experience
+ */
+ u32b exp;
+
+ /** @structvar csleep
+ * @brief Number
+ * @note Inactive counter
+ */
+ s16b csleep;
+
+ /** @structvar mspeed
+ * @brief Number
+ * @note Monster "speed"
+ */
+ byte mspeed;
+ /** @structvar energy
+ * @brief Number
+ * @note Monster "energy"
+ */
+ byte energy;
+
+ /** @structvar stunned
+ * @brief Number
+ * @note Monster is stunned
+ */
+ byte stunned;
+ /** @structvar confused
+ * @brief Number
+ * @note Monster is confused
+ */
+ byte confused;
+ /** @structvar monfear
+ * @brief Number
+ * @note Monster is afraid
+ */
+ byte monfear;
+
+ /** @structvar bleeding
+ * @brief Number
+ * @note Monster is bleeding
+ */
+ s16b bleeding;
+ /** @structvar poisoned
+ * @brief Number
+ * @note Monster is poisoned
+ */
+ s16b poisoned;
+
+ /** @structvar cdis
+ * @brief Number
+ * @note Current dis from player
+ */
+ byte cdis;
+
+ /** @structvar mflag
+ * @brief Number
+ * @note Extra monster flags
+ */
+ s32b mflag;
+
+ /** @structvar ml
+ * @brief Boolean
+ * @note Monster is "visible"
+ */
+ bool ml;
+
+ /** @structvar hold_o_idx
+ * @brief Number
+ * @note Object being held (if any)
+ */
+ s16b hold_o_idx;
+
+ /** @structvar smart
+ * @brief Number
+ * @note Field for "smart_learn"
+ */
+ u32b smart;
+
+ /** @structvar status
+ * @brief Number
+ * @note Status(friendly, pet, companion, ..)
+ */
+ s16b status;
+
+ /** @structvar target
+ * @brief Number
+ * @note Monster target
+ */
+ s16b target;
+
+ /** @structvar possessor
+ * @brief Number
+ * @note Is it under the control of a possessor ?
+ */
+ s16b possessor;
+};
+
+$static monster_type *lua_get_monster(int m_idx){return (&m_list[m_idx]);}
+/** @fn monster(int m_idx);
+ * @brief Return the monster with index "m_idx" in the monster list.\n
+ * @param m_idx Number \n the index of the monster in the monster list
+ * @brief Monster index
+ * @return monster_type \n The monster.
+ * @note (see file w_mnster.c)
+ */
+static monster_type *lua_get_monster @ monster(int m_idx);
+
+/** @var m_list[max_m_idx]
+ * @brief monster_type
+ * @note List of monsters
+ */
+extern monster_type m_list[max_m_idx];
+
+/** @fn race_info_idx(int r_idx, int ego)
+ * @brief Get monster info and ego info for monster with monster index "r_idx"
+ * and monster ego "ego".\n
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param ego Number \n the index of the ego in the monster ego array
+ * @brief Ego index
+ * @return monster_race \n The monster race.
+ * @note
+ * If "ego" > 0, the ego information is applied to the monster information and
+ * the new monster information is returned.
+ * @note
+ * For example, race_info_idx(141,7) will create a brown yeek (monster)
+ * shaman (ego).
+ * @note (see file monster2.c)
+ */
+extern monster_race* race_info_idx(int r_idx, int ego);
+
+/** @fn delete_monster_idx(int i)
+ * @brief Delete monster "i" from the monster array.\n
+ * @param i Number \n the index of the monster in the monster list
+ * @brief Monster index
+ * @note (see file monster2.c)
+ */
+extern void delete_monster_idx(int i);
+
+/** @fn m_pop(void)
+ * @brief Get an empty slot in the monster list.
+ * @return Number \n The index of an empty slot the monster list.
+ * @note
+ * If there are no empty slots, a slot will be reclaimed from a "dead"
+ * monster. If all slots are full, 0 is returned, which means the function
+ * has failed ("Too many monsters!").
+ * @note (see file monster2.c)
+ */
+extern s16b m_pop(void);
+
+/** @fn get_mon_num_prep(void)
+ * @brief Apply a "monster restriction function" to the "monster allocation
+ * table".
+ * @return Number \n 0 (success) always.
+ * @note
+ * There are no parameters, but there are some other variables which will
+ * need to be set. They are get_mon_num_hook and get_mon_num2_hook. They
+ * are pointers to functions.
+ * @note
+ * For example, get_mon_num_hook = monster_volcano means when
+ * get_mon_num_hook is called (*get_mon_num_hook)(index), the actual
+ * function called is monster_volcano(index). This particular function
+ * returns TRUE if the monster indicated by "index" has the
+ * RF8_WILD_VOLCANO flag set.
+ * @note
+ * It is a good idea to store the old value of get_mon_num_hook before
+ * setting a new one, and restoring it when your function is finished.
+ * @note
+ * Following is a list of functions which can be assigned to
+ * get_mon_num_hook:\n
+ * create_molds_hook\n
+ * create_townpeople_hook\n
+ * mon_hook_bounty\n
+ * monster_dungeon\n
+ * monster_grass\n
+ * monster_mountain\n
+ * monster_ocean\n
+ * monster_quest\n
+ * monster_shore\n
+ * monster_town\n
+ * monster_volcano\n
+ * monster_waste\n
+ * monster_wood\n
+ * mutate_monster_okay\n
+ * place_monster_okay\n
+ * summon_specific_okay\n
+ * vault_aux_animal\n
+ * vault_aux_chapel\n
+ * vault_aux_clone\n
+ * vault_aux_demon\n
+ * vault_aux_dragon\n
+ * vault_aux_giant\n
+ * vault_aux_jelly\n
+ * vault_aux_kennel\n
+ * vault_aux_orc\n
+ * vault_aux_symbol\n
+ * vault_aux_treasure\n
+ * vault_aux_troll\n
+ * vault_aux_undead
+ * @note
+ * Or you can write your own. The function must take an integer (index)
+ * as a parameter and return boolean (TRUE if the monster is selected,
+ * or FALSE if it is not).
+ * @note (see file monster2.c)
+ */
+extern errr get_mon_num_prep(void);
+
+/** @fn get_mon_num(int level)
+ * @brief For the given level "level", return the index of an appropriate
+ * monster race.\n
+ * @param level Number \n a dungeon level (which is adjusted before
+ * it is used).
+ * @brief Dungeon level
+ * @return Number \n The index of a monster race in the monster race array.
+ * @note
+ * This function uses the "prob2" field of the "monster allocation table",
+ * and various local information, to calculate the "prob3" field of the
+ * same table, which is then used to choose an "appropriate" monster, in
+ * a relatively efficient manner.
+ * @note
+ * Note that "town" monsters will *only* be created in the town, and
+ * "normal" monsters will *never* be created in the town, unless the
+ * "level" is "modified", for example, by polymorph or summoning.
+ * @note
+ * There is a small chance (1/50) of "boosting" the given depth by
+ * a small amount (up to four levels), except in the town.
+ * @note
+ * It is (slightly) more likely to acquire a monster of the given level
+ * than one of a lower level. This is done by choosing several monsters
+ * appropriate to the given level and keeping the "hardest" one.
+ * @note
+ * Note that if no monsters are "appropriate", then this function will
+ * fail, and return zero, but this should *almost* never happen.
+ * @note (see file monster2.c)
+ */
+extern s16b get_mon_num(int level);
+
+$static char *lua_monster_desc(monster_type *m_ptr, int mode){static char buf[200]; monster_desc(buf, m_ptr, mode); return buf;}
+/** @fn monster_desc(monster_type *m_ptr, int mode);
+ * @brief Return a monster description for monster "monster_type" using flag
+ * "mode".\n
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @param mode Number \n description mode (see below)
+ * @brief Description mode
+ * @return String \n The description of the monster.
+ * @note
+ * We can correctly describe monsters based on their visibility.\n
+ * We can force all monsters to be treated as visible or invisible.\n
+ * We can build nominatives, objectives, possessives, or reflexives.\n
+ * We can selectively pronominalize hidden, visible, or all monsters.\n
+ * We can use definite or indefinite descriptions for hidden monsters.\n
+ * We can use definite or indefinite descriptions for visible monsters.
+ * @note
+ * Pronominalization involves the gender whenever possible and allowed,
+ * so that by cleverly requesting pronominalization / visibility, you
+ * can get messages like "You hit someone. She screams in agony!".
+ * @note
+ * Reflexives are acquired by requesting Objective plus Possessive.
+ * @note
+ * If no m_ptr arg is given (?), the monster is assumed to be hidden,
+ * unless the "Assume Visible" mode is requested.
+ * @note
+ * If no r_ptr arg is given, it is extracted from m_ptr and r_info
+ * If neither m_ptr nor r_ptr is given, the monster is assumed to
+ * be neuter, singular, and hidden (unless "Assume Visible" is set),
+ * in which case you may be in trouble... :-)
+ * @note
+ * I am assuming that no monster name is more than 70 characters long,
+ * so that "char desc[80];" is sufficiently large for any result.
+ * @note
+ * Mode Flags:\n
+ * 0x01 --> Objective (or Reflexive)\n
+ * 0x02 --> Possessive (or Reflexive)\n
+ * 0x04 --> Use indefinites for hidden monsters ("something")\n
+ * 0x08 --> Use indefinites for visible monsters ("a kobold")\n
+ * 0x10 --> Pronominalize hidden monsters\n
+ * 0x20 --> Pronominalize visible monsters\n
+ * 0x40 --> Assume the monster is hidden\n
+ * 0x80 --> Assume the monster is visible
+ * @note
+ * Useful Modes:\n
+ * 0x00 --> Full nominative name ("the kobold") or "it"\n
+ * 0x04 --> Full nominative name ("the kobold") or "something"\n
+ * 0x80 --> Genocide resistance name ("the kobold")\n
+ * 0x88 --> Killing name ("a kobold")\n
+ * 0x22 --> Possessive, genderized if visible ("his") or "its"\n
+ * 0x23 --> Reflexive, genderized if visible ("himself") or "itself"
+ * @note (see file monster2.c)
+ */
+static char *lua_monster_desc @ monster_desc(monster_type *m_ptr, int mode);
+
+$static char *lua_monster_race_desc(int r_idx, int ego){static char buf[200]; monster_race_desc(buf, r_idx, ego); return buf;}
+/** @fn monster_race_desc(int r_idx, int ego);
+ * @brief Return the monster description for monster with monster index
+ * "r_idx" and monster ego "ego".\n
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param ego Number \n the index of the ego in the monster ego array
+ * @brief Ego index
+ * @return String \n The description of the monster race.
+ * @note
+ * The monster description is made up of the ego name (if any) and monster
+ * name, or the unique name.
+ * @note (see file w_mnster.c)
+ */
+static char *lua_monster_race_desc @ monster_race_desc(int r_idx, int ego);
+
+/** @fn monster_race_desc(char *desc, int r_idx, int ego)
+ * @brief Return the monster description "desc" for monster with monster index
+ * "r_idx" and monster ego "ego".\n
+ * @param *desc String
+ * @brief Description
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param ego Number \n the index of the ego in the monster ego array
+ * @brief Ego index
+ * @return *desc String \n The description of the monster race.
+ * @note
+ * The monster description is made up of the ego name (if any) and monster
+ * name, or the unique name.
+ * @note (see file monster2.c)
+ */
+extern void monster_race_desc(char *desc, int r_idx, int ego);
+
+/** @fn monster_carry(monster_type *m_ptr, int m_idx, object_type *q_ptr)
+ * @brief Allow monster "m_ptr" with monster index "m_idx" to carry object
+ * "q_ptr".\n
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @param m_idx Number \n the index of the monster in the monster list
+ * @brief Monster index
+ * @param *q_ptr object_type \n the object
+ * @brief Object
+ * @note
+ * The monster can only carry the object if there is room for the object in the
+ * object list.
+ * @note (see file monster2.c)
+ */
+extern void monster_carry(monster_type *m_ptr, int m_idx, object_type *q_ptr);
+
+/** @fn place_monster_aux(int y, int x, int r_idx, bool slp, bool grp, int status)
+ * @brief Attempt to place a monster with monster race index "r_idx" and status
+ * "status" at grid "y", "x". The monster may be asleep ("slp") or surrounded
+ * by a group of identical monsters ("grp").\n
+ * @param y Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param slp Boolean \n TRUE if monster is asleep, otherwise FALSE
+ * @brief Asleep?
+ * @param grp Boolean \n TRUE if monster appears in a group, otherwise FALSE
+ * @brief Group?
+ * @param status Number \n the status of the monster from the player's point
+ * of view (see MSTATUS_foo flags)
+ * @brief Monster status
+ * @return Boolean \n TRUE if the monster is placed successfully, otherwise
+ * FALSE.
+ * @note
+ * Note that certain monsters are now marked as requiring "friends".
+ * These monsters, if successfully placed, and if the "grp" parameter
+ * is TRUE, will be surrounded by a "group" of identical monsters.
+ * @note
+ * Note that certain monsters are now marked as requiring an "escort",
+ * which is a collection of monsters with similar "race" but lower
+ * level.
+ * @note
+ * Some monsters induce a fake "group" flag on their escorts.
+ * @note
+ * Note the "bizarre" use of non-recursion to prevent annoying output
+ * when running a code profiler.
+ * @note
+ * Note the use of the new "monster allocation table" code to restrict
+ * the "get_mon_num()" function to "legal" escort types.
+ * @note (see file monster2.c)
+ */
+extern bool place_monster_aux(int y, int x, int r_idx, bool slp, bool grp, int status);
+
+/** @fn place_monster(int y, int x, bool slp, bool grp)
+ * @brief Attempt to place a monster at grid "y", "x". The monster may be
+ * asleep ("slp") or surrounded by a group of identical monsters ("grp").\n
+ * @param y Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param slp Boolean \n TRUE if monster is asleep, otherwise FALSE
+ * @brief Asleep?
+ * @param grp Boolean \n TRUE if monster appears in a group, otherwise FALSE
+ * @brief Group?
+ * @return Boolean \n TRUE if the monster is placed successfully, otherwise
+ * FALSE.
+ * @note
+ * Attempt to find a monster appropriate to the "monster_level"
+ * @note (see file monster2.c)
+ */
+extern bool place_monster(int y, int x, bool slp, bool grp);
+
+/** @fn place_monster_one(int y, int x, int r_idx, int ego, bool slp, int status)
+ * @brief Attempt to place a monster with monster race index "r_idx", monster
+ * ego "ego" and status "status" at grid "y", "x". The monster may be asleep
+ * ("slp").\n
+ * @param y Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param ego Number \n the index of the ego in the monster ego array
+ * @brief Ego index
+ * @param slp Boolean \n TRUE if monster is asleep, otherwise FALSE
+ * @brief Asleep?
+ * @param status Number \n the status of the monster from the player's point
+ * of view (see MSTATUS_foo flags)
+ * @brief Monster status
+ * @return Number \n The index of the placed monster in the monster list.
+ * @note
+ * To give the player a sporting chance, any monster that appears in
+ * line-of-sight and is extremely dangerous can be marked as
+ * "FORCE_SLEEP", which will cause them to be placed with low energy,
+ * which often (but not always) lets the player move before they do.
+ * @note
+ * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters.
+ * @note
+ * XXX XXX XXX Use special "here" and "dead" flags for unique monsters,
+ * remove old "cur_num" and "max_num" fields.
+ * @note
+ * XXX XXX XXX Actually, do something similar for artifacts, to simplify
+ * the "preserve" mode, and to make the "what artifacts" flag more useful.
+ * @note
+ * This is the only function which may place a monster in the dungeon,
+ * except for the savefile loading code.
+ * @note (see file monster2.c)
+ */
+extern s16b place_monster_one(int y, int x, int r_idx, int ego, bool slp, int status);
+
+/** @fn is_friend(monster_type *m_ptr)
+ * @brief Return a value to indicate the status of monster "m_ptr".\n
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @return Number \n -1 if monster is an enemy, 0 if it is neutral, and 1 if
+ * it is friendly.
+ * @note (see file monster3.c)
+ */
+extern int is_friend(monster_type *m_ptr);
+
+/** @fn is_enemy(monster_type *m_ptr, monster_type *t_ptr)
+ * @brief Determine if monster "m_ptr" should attack monster "t_ptr".\n
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @param *t_ptr monster_type \n the target monster
+ * @brief Target monster
+ * @return Boolean \n TRUE if monster "m_ptr" should attack monster "t_ptr",
+ * otherwise FALSE.
+ * @note
+ * If "m_ptr" is stupid and "r_ptr" is a different type of monster then the
+ * function will return TRUE.\n
+ * If "m_ptr" is not neutral and "r_ptr" is a breeder, and "r_ptr" is a
+ * different type of monster then the function will return TRUE (and vice
+ * versa).\n
+ * If both monsters are not neutral and one is friendly and the other isn't
+ * then the function will return TRUE.
+ * @note (see file monster3.c)
+ */
+extern bool is_enemy(monster_type *m_ptr, monster_type *t_ptr);
+
+/** @fn change_side(monster_type *m_ptr)
+ * @brief Change the status of monster "m_ptr" from friendly to unfriendly and
+ * vice versa.
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @return Boolean \n TRUE if the status changed, otherwise FALSE.
+ * @note
+ * Friends and pets become enemies.\n
+ * Neutral monsters become neutral pets and vice versa.\n
+ * Companions are unaffected.
+ * @note (see file monster3.c)
+ */
+extern bool change_side(monster_type *m_ptr);
+
+/** @fn find_position(int y, int x, int *yy = 0, int *xx = 0)
+ * @brief Find a new grid "yy", "xx" within 6 grids of target grid "y", "x".\n
+ * @param y Number \n the y coordinate of the origin grid
+ * @brief Origin y-coordinate
+ * @param x Number \n the x coordinate of the origin grid
+ * @brief Origin x-coordinate
+ * @param yy Number \n the y coordinate of the target grid
+ * @brief Target y-coordinate
+ * @param xx Number \n the x coordinate of the target grid
+ * @brief Target x-coordinate
+ * @note
+ * The new grid must be within line-of-sight of the target grid. A
+ * maximum of 5000 attempts is made.
+ * @note (see file lua_bind.c)
+ */
+extern void find_position(int y, int x, int *yy = 0, int *xx = 0);
+
+/** @var summon_specific_level
+ * @brief Number
+ * @note
+ * Force summoned monsters to be at this level.
+ */
+extern int summon_specific_level;
+
+/** @var summon_kin_type
+ * @brief String
+ * @note
+ * The monster character for those monsters who can summon kin.
+ */
+extern char summon_kin_type;
+
+/** @fn summon_specific(int y1, int x1, int lev, int type)
+ * @brief Place a monster of type "type" near grid "y","x".\n
+ * @param y1 Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x1 Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param lev Number \n the monster level of the summoning monster
+ * @brief Summoner level
+ * @param type Number \n the type of summoned monster
+ * @brief Monster type
+ * @return Boolean \n TRUE if a monster was summoned, otherwise FALSE.
+ * @note
+ * We will attempt to place the monster up to 20 times before giving up.
+ * @note
+ * Note: SUMMON_UNIQUE and SUMMON_WRAITH (XXX) will summon Unique's\n
+ * Note: SUMMON_HI_UNDEAD and SUMMON_HI_DRAGON may summon Unique's\n
+ * Note: None of the other summon codes will ever summon Unique's.
+ * @note
+ * This function has been changed. We now take the "monster level"
+ * of the summoning monster as a parameter, and use that, along with
+ * the current dungeon level, to help determine the level of the
+ * desired monster. Note that this is an upper bound, and also
+ * tends to "prefer" monsters of that level. Currently, we use
+ * the average of the dungeon and monster levels, and then add
+ * five to allow slight increases in monster power.
+ * @note
+ * Note that we use the new "monster allocation table" creation code
+ * to restrict the "get_mon_num()" function to the set of "legal"
+ * monsters, making this function much faster and more reliable.
+ * @note
+ * Note that this function may not succeed, though this is very rare.
+ * @note (see file monster2.c)
+ */
+extern bool summon_specific(int y1, int x1, int lev, int type);
+
+/** @fn summon_specific_friendly(int y1, int x1, int lev, int type, bool Group_ok)
+ * @brief Place a friendly monster of type "type" near grid "y","x". The
+ * monster may be surrounded by a group of identical monsters ("Group_ok").\n
+ * @param y1 Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x1 Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param lev Number \n the monster level of the summoning monster
+ * @brief Summoner level
+ * @param type Number \n the type of summoned monster
+ * @brief Monster type
+ * @param Group_ok Boolean \n TRUE if monster appears in a group, otherwise
+ * FALSE
+ * @brief Group?
+ * @return Boolean \n TRUE if a monster was summoned, otherwise FALSE.
+ * @note (see file monster2.c)
+ */
+extern bool summon_specific_friendly(int y1, int x1, int lev, int type, bool Group_ok);
+
+/** @fn summon_monster_aux(int y, int x, int lev, bool friend, char *fct);
+ * @brief Place a monster near grid "y","x".\n
+ * @param y Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param lev Number \n the monster level of the summoning monster
+ * @brief Summoner level
+ * @param friend Boolean \n TRUE if friendly monsters are to be summoned,
+ * otherwise FALSE
+ * @brief Friendly?
+ * @param *fct String \n the function which determines which type of monster
+ * will be summoned
+ * @brief Monster type function
+ * @return Boolean \n TRUE if a monster was summoned, otherwise FALSE.
+ * @note (see file w_mnster.c)
+ */
+extern bool lua_summon_monster @ summon_monster_aux(int y, int x, int lev, bool friend, char *fct);
+
+/** @fn can_create_companion()
+ * @brief Determine if a companion can be created.
+ * @return Boolean \n TRUE if a companion can be created, otherwise FALSE.
+ * @note
+ * The companions are counted. If this is less than the number allowed by
+ * the player monster lore skill, the function returns TRUE, otherwise the
+ * function returns FALSE.
+ * @note (see file monster3.c)
+ */
+extern bool can_create_companion();
+
+/** @fn monster_set_level(int m_idx, int level)
+ * @brief Set a new level for monster with monster index "m_idx".\n
+ * @param m_idx Number \n the index of the monster in the monster list
+ * @brief Monster index
+ * @param level Number \n the new level of the monster
+ * @brief Monster level
+ * @note
+ * The new level can not exceed 150. If the new level is higher than the
+ * monster level, the monster experience value is recalculated.
+ * @note (see file monster2.c)
+ */
+extern void monster_set_level(int m_idx, int level);
+
+/** @name Summon types
+ * @note Legal restrictions for "summon_specific()"
+ */
+/** @def SUMMON_ANT
+ * @note Summon giant ant (a) excluding uniques.
+ */
+#define SUMMON_ANT 11
+
+/** @def SUMMON_SPIDER
+ * @note Summon spider/scorpion/tick (S) excluding uniques.
+ */
+#define SUMMON_SPIDER 12
+
+/** @def SUMMON_HOUND
+ * @note Summon canine (C) or zephyr hound (Z) excluding uniques.
+ */
+#define SUMMON_HOUND 13
+
+/** @def SUMMON_HYDRA
+ * @note Summon multi-headed hydra (M) excluding uniques.
+ */
+#define SUMMON_HYDRA 14
+
+/** @def SUMMON_ANGEL
+ * @note Summon angelic being (A) excluding uniques.
+ */
+#define SUMMON_ANGEL 15
+
+/** @def SUMMON_DEMON
+ * @note Summon demon (RF3_DEMON) excluding uniques.
+ */
+#define SUMMON_DEMON 16
+
+/** @def SUMMON_UNDEAD
+ * @note Summon undead (RF3_UNDEAD) excluding uniques.
+ */
+#define SUMMON_UNDEAD 17
+
+/** @def SUMMON_DRAGON
+ * @note Summon dragon (RF3_DRAGON) excluding uniques.
+ */
+#define SUMMON_DRAGON 18
+
+/** @def SUMMON_HI_UNDEAD
+ * @note Summon lich (L) or vampire (V) or wight/wraith (W) including uniques.
+ */
+#define SUMMON_HI_UNDEAD 21
+
+/** @def SUMMON_HI_DRAGON
+ * @note Summon ancient dragon (D) including uniques.
+ */
+#define SUMMON_HI_DRAGON 22
+
+/** @def SUMMON_WRAITH
+ * @note Summon wight/wraith (W) including uniques.
+ */
+#define SUMMON_WRAITH 31
+
+/** @def SUMMON_UNIQUE
+ * @note Summon unique (RF1_UNIQUE).
+ */
+#define SUMMON_UNIQUE 32
+
+/** @def SUMMON_BIZARRE1
+ * @note Summon mold (m) excluding uniques.
+ */
+#define SUMMON_BIZARRE1 33
+
+/** @def SUMMON_BIZARRE2
+ * @note Summon giant bat (b) excluding uniques.
+ */
+#define SUMMON_BIZARRE2 34
+
+/** @def SUMMON_BIZARRE3
+ * @note Summon quylthulg (Q) excluding uniques.
+ */
+#define SUMMON_BIZARRE3 35
+
+/** @def SUMMON_BIZARRE4
+ * @note Summon vortex (v) excluding uniques.
+ */
+#define SUMMON_BIZARRE4 36
+
+/** @def SUMMON_BIZARRE5
+ * @note Summon creeping coins ($) excluding uniques.
+ */
+#define SUMMON_BIZARRE5 37
+
+/** @def SUMMON_BIZARRE6
+ * @note Summon mimic (!?=$|) excluding uniques.
+ */
+#define SUMMON_BIZARRE6 38
+
+/** @def SUMMON_HI_DEMON
+ * @note Summon demon (RF3_DEMON) and major demon (U) excluding uniques.
+ */
+#define SUMMON_HI_DEMON 39
+
+/** @def SUMMON_KIN
+ * @note Summon monster of the same character type excluding uniques.
+ */
+#define SUMMON_KIN 40
+
+/** @def SUMMON_DAWN
+ * @note Summon monster with "the Dawn" in the name excluding uniques.
+ */
+#define SUMMON_DAWN 41
+
+/** @def SUMMON_ANIMAL
+ * @note Summon animal (RF3_ANIMAL) excluding uniques.
+ */
+#define SUMMON_ANIMAL 42
+
+/** @def SUMMON_ANIMAL_RANGER
+ * @note Summon animal (RF3_ANIMAL) and giant ant, giant bat, centipede,
+ * feline,giant louse, quadruped, rodent, worm or worm mass, bird, canine,
+ * insect, snake, killer beetle, multi-headed hydra, reptile/amphibian,
+ * spider/scorpion/tick (abcflqrwBCIJKMRS) and not dragon (RF3_DRAGON) and not
+ * evil (RF3_EVIL) and not undead (RF3_UNDEAD) and not demon (RF3_DEMON) and
+ * not inate/breath and not normal spells and not special spells excluding
+ * uniques.
+ */
+#define SUMMON_ANIMAL_RANGER 43
+
+/** @def SUMMON_HI_UNDEAD_NO_UNIQUES
+ * @note Summon lich (L) or vampire (V) or wight/wraith (W) excluding uniques.
+ */
+#define SUMMON_HI_UNDEAD_NO_UNIQUES 44
+
+/** @def SUMMON_HI_DRAGON_NO_UNIQUES
+ * @note Summon ancient dragon (D) excluding uniques.
+ */
+#define SUMMON_HI_DRAGON_NO_UNIQUES 45
+
+/** @def SUMMON_NO_UNIQUES
+ * @note Summon non-uniques (not RF1_UNIQUE).
+ */
+#define SUMMON_NO_UNIQUES 46
+
+/** @def SUMMON_PHANTOM
+ * @note Summon monster with "Phantom" in the name excluding uniques.
+ */
+#define SUMMON_PHANTOM 47
+
+/** @def SUMMON_ELEMENTAL
+ * @note Summon monster with "lemental" in the name excluding uniques.
+ */
+#define SUMMON_ELEMENTAL 48
+
+/** @def SUMMON_THUNDERLORD
+ * @note Summon thunderlords (RF3_THUNDERLORD) including uniques.
+ */
+#define SUMMON_THUNDERLORD 49
+
+/** @def SUMMON_BLUE_HORROR
+ * @note Summon monster with "lue horror" in the name excluding uniques.
+ */
+#define SUMMON_BLUE_HORROR 50
+
+/** @def SUMMON_BUG
+ * @note Summon monster with "Software bug" in the name excluding uniques.
+ */
+#define SUMMON_BUG 51
+
+/** @def SUMMON_RNG
+ * @note Summon monster with "Random Number Generator" in the name excluding
+ * uniques.
+ */
+#define SUMMON_RNG 52
+
+/** @def SUMMON_MINE
+ * @note Summon mines (RF1_NEVER_MOVE) including uniques.
+ */
+#define SUMMON_MINE 53
+
+/** @def SUMMON_HUMAN
+ * @note Summon (p) excluding uniques.
+ */
+#define SUMMON_HUMAN 54
+
+/** @def SUMMON_SHADOWS
+ * @note Summon ghost (G) excluding uniques.
+ */
+#define SUMMON_SHADOWS 55
+
+/** @def SUMMON_GHOST
+ * @note Summon ghost (G) including uniques.
+ */
+#define SUMMON_GHOST 56
+
+/** @def SUMMON_QUYLTHULG
+ * @note Summon (Q) excluding uniques.
+ */
+#define SUMMON_QUYLTHULG 57
+
+/** @def SUMMON_LUA
+ * @note Summon monsters according to a Lua script.
+ */
+#define SUMMON_LUA 58
+/** @} */
+
+/** @fn do_control_reconnect()
+ * @brief Find the controlled monster and reconnect to it.
+ * @return Boolean \n TRUE if there is a controlled monster, otherwise FALSE.
+ * @note
+ * The monster list is scanned for a monster with MFLAG_CONTROL. If it is
+ * found, the function returns TRUE.
+ * @note (see file monster3.c)
+ */
+extern bool do_control_reconnect();
+
+/* monster thing */
+/** @var m_max
+ * @brief Number
+ * @note The number of monsters currently in the monster list.
+ */
+extern s16b m_max;
diff --git a/src/monster1.c b/src/monster1.c
new file mode 100644
index 00000000..196272ce
--- /dev/null
+++ b/src/monster1.c
@@ -0,0 +1,1908 @@
+/* File: monster1.c */
+
+/* Purpose: describe monsters (using monster memory) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Christopher J. Stuart
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Pronoun arrays, by gender.
+ */
+static cptr wd_he[3] = { "it", "he", "she" };
+static cptr wd_his[3] = { "its", "his", "her" };
+
+
+/*
+ * Pluralizer. Args(count, singular, plural)
+ */
+#define plural(c,s,p) \
+(((c) == 1) ? (s) : (p))
+
+
+
+
+
+
+/*
+ * Determine if the "armor" is known
+ * The higher the level, the fewer kills needed.
+ */
+static bool_ know_armour(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ s32b level = r_ptr->level;
+
+ s32b kills = r_ptr->r_tkills;
+
+ /* Normal monsters */
+ if (kills > 304 / (4 + level)) return (TRUE);
+
+ /* Skip non-uniques */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE))) return (FALSE);
+
+ /* Unique monsters */
+ if (kills > 304 / (38 + (5*level) / 4)) return (TRUE);
+
+ /* Assume false */
+ return (FALSE);
+}
+
+
+/*
+ * Determine if the "damage" of the given attack is known
+ * the higher the level of the monster, the fewer the attacks you need,
+ * the more damage an attack does, the more attacks you need
+ */
+static bool_ know_damage(int r_idx, int i)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ s32b level = r_ptr->level;
+
+ s32b a = r_ptr->r_blows[i];
+
+ s32b d1 = r_ptr->blow[i].d_dice;
+ s32b d2 = r_ptr->blow[i].d_side;
+
+ s32b d = d1 * d2;
+
+ /* Normal monsters */
+ if ((4 + level) * a > 80 * d) return (TRUE);
+
+ /* Skip non-uniques */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE))) return (FALSE);
+
+ /* Unique monsters */
+ if ((4 + level) * (2 * a) > 80 * d) return (TRUE);
+
+ /* Assume false */
+ return (FALSE);
+}
+
+
+/*
+ * Hack -- display monster information using "text_out()"
+ *
+ * Note that there is now a compiler option to only read the monster
+ * descriptions from the raw file when they are actually needed, which
+ * saves about 60K of memory at the cost of disk access during monster
+ * recall, which is optional to the user.
+ *
+ * This function should only be called with the cursor placed at the
+ * left edge of the screen, on a cleared line, in which the recall is
+ * to take place. One extra blank line is left after the recall.
+ */
+static void roff_aux(int r_idx, int ego, int remem)
+{
+ monster_race *r_ptr;
+
+ bool_ old = FALSE;
+ bool_ sin = FALSE;
+
+ int m, n, r;
+
+ cptr p, q;
+
+ int msex = 0;
+
+ bool_ breath = FALSE;
+ bool_ magic = FALSE;
+
+ u32b flags1;
+ u32b flags2;
+ u32b flags3;
+ u32b flags4;
+ u32b flags5;
+ u32b flags6;
+ u32b flags7;
+ u32b flags9;
+
+ int vn = 0;
+ byte color[64];
+ cptr vp[64];
+
+ monster_race save_mem;
+
+
+
+ /* Access the race and lore */
+ r_ptr = race_info_idx(r_idx, ego);
+
+
+ /* Cheat -- Know everything */
+ if (cheat_know)
+ {
+ /* XXX XXX XXX */
+
+ /* Save the "old" memory */
+ save_mem = *r_ptr;
+
+ /* Hack -- Maximal kills */
+ r_ptr->r_tkills = MAX_SHORT;
+
+ /* Hack -- Maximal info */
+ r_ptr->r_wake = r_ptr->r_ignore = MAX_UCHAR;
+
+ /* Observe "maximal" attacks */
+ for (m = 0; m < 4; m++)
+ {
+ /* Examine "actual" blows */
+ if (r_ptr->blow[m].effect || r_ptr->blow[m].method)
+ {
+ /* Hack -- maximal observations */
+ r_ptr->r_blows[m] = MAX_UCHAR;
+ }
+ }
+
+ /* Hack -- maximal drops */
+ r_ptr->r_drop_gold = r_ptr->r_drop_item =
+ (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0));
+
+ /* Hack -- but only "valid" drops */
+ if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0;
+ if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0;
+
+ /* Hack -- observe many spells */
+ r_ptr->r_cast_inate = MAX_UCHAR;
+ r_ptr->r_cast_spell = MAX_UCHAR;
+
+ /* Hack -- know all the flags */
+ r_ptr->r_flags1 = r_ptr->flags1;
+ r_ptr->r_flags2 = r_ptr->flags2;
+ r_ptr->r_flags3 = r_ptr->flags3;
+ r_ptr->r_flags4 = r_ptr->flags4;
+ r_ptr->r_flags5 = r_ptr->flags5;
+ r_ptr->r_flags6 = r_ptr->flags6;
+ r_ptr->r_flags7 = r_ptr->flags7;
+ r_ptr->r_flags8 = r_ptr->flags8;
+ r_ptr->r_flags9 = r_ptr->flags9;
+ }
+
+
+ /* Extract a gender (if applicable) */
+ if (r_ptr->flags1 & (RF1_FEMALE)) msex = 2;
+ else if (r_ptr->flags1 & (RF1_MALE)) msex = 1;
+
+
+ /* Obtain a copy of the "known" flags */
+ flags1 = (r_ptr->flags1 & r_ptr->r_flags1);
+ flags2 = (r_ptr->flags2 & r_ptr->r_flags2);
+ flags3 = (r_ptr->flags3 & r_ptr->r_flags3);
+ flags4 = (r_ptr->flags4 & r_ptr->r_flags4);
+ flags5 = (r_ptr->flags5 & r_ptr->r_flags5);
+ flags6 = (r_ptr->flags6 & r_ptr->r_flags6);
+ flags7 = (r_ptr->flags7 & r_ptr->r_flags7);
+ flags9 = (r_ptr->flags9 & r_ptr->r_flags9);
+
+
+ /* Assume some "obvious" flags */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) flags1 |= (RF1_UNIQUE);
+ if (r_ptr->flags1 & (RF1_MALE)) flags1 |= (RF1_MALE);
+ if (r_ptr->flags1 & (RF1_FEMALE)) flags1 |= (RF1_FEMALE);
+
+ /* Assume some "creation" flags */
+ if (r_ptr->flags1 & (RF1_FRIEND)) flags1 |= (RF1_FRIEND);
+ if (r_ptr->flags1 & (RF1_FRIENDS)) flags1 |= (RF1_FRIENDS);
+ if (r_ptr->flags1 & (RF1_ESCORT)) flags1 |= (RF1_ESCORT);
+ if (r_ptr->flags1 & (RF1_ESCORTS)) flags1 |= (RF1_ESCORTS);
+
+ /* Killing a monster reveals some properties */
+ if (r_ptr->r_tkills)
+ {
+ /* Know "race" flags */
+ if (r_ptr->flags3 & (RF3_ORC)) flags3 |= (RF3_ORC);
+ if (r_ptr->flags3 & (RF3_TROLL)) flags3 |= (RF3_TROLL);
+ if (r_ptr->flags3 & (RF3_GIANT)) flags3 |= (RF3_GIANT);
+ if (r_ptr->flags3 & (RF3_DRAGON)) flags3 |= (RF3_DRAGON);
+ if (r_ptr->flags3 & (RF3_DEMON)) flags3 |= (RF3_DEMON);
+ if (r_ptr->flags3 & (RF3_UNDEAD)) flags3 |= (RF3_UNDEAD);
+ if (r_ptr->flags3 & (RF3_EVIL)) flags3 |= (RF3_EVIL);
+ if (r_ptr->flags3 & (RF3_GOOD)) flags3 |= (RF3_GOOD);
+ if (r_ptr->flags3 & (RF3_ANIMAL)) flags3 |= (RF3_ANIMAL);
+ if (r_ptr->flags3 & (RF3_THUNDERLORD)) flags3 |= (RF3_THUNDERLORD);
+ if (r_ptr->flags7 & (RF7_SPIDER)) flags7 |= (RF7_SPIDER);
+
+ /* Know "forced" flags */
+ if (r_ptr->flags1 & (RF1_FORCE_DEPTH)) flags1 |= (RF1_FORCE_DEPTH);
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP)) flags1 |= (RF1_FORCE_MAXHP);
+ }
+
+
+ /* Require a flag to show kills */
+ if (!(show_details))
+ {
+ /* nothing */
+ }
+
+ /* Treat uniques differently */
+ else if (flags1 & (RF1_UNIQUE))
+ {
+ /* Hack -- Determine if the unique is "dead" */
+ bool_ dead = (r_ptr->max_num == 0) ? TRUE : FALSE;
+
+ /* We've been killed... */
+ if (r_ptr->r_deaths)
+ {
+ /* Killed ancestors */
+ text_out(format("%^s has slain %d of your ancestors",
+ wd_he[msex], r_ptr->r_deaths));
+
+ /* But we've also killed it */
+ if (dead)
+ {
+ text_out(format(", but you have avenged them! ") );
+ }
+
+ /* Unavenged (ever) */
+ else
+ {
+ text_out(format(", who %s unavenged. ",
+ plural(r_ptr->r_deaths, "remains", "remain")));
+ }
+ }
+
+ /* Dead unique who never hurt us */
+ else if (dead)
+ {
+ text_out("You have slain this foe. ");
+ }
+ }
+
+ /* Not unique, but killed us */
+ else if (r_ptr->r_deaths)
+ {
+ /* Dead ancestors */
+ text_out(format("%d of your ancestors %s been killed by this creature, ",
+ r_ptr->r_deaths, plural(r_ptr->r_deaths, "has", "have")));
+
+ /* Some kills this life */
+ if (r_ptr->r_pkills)
+ {
+ text_out("and you have exterminated at least ");
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->r_pkills));
+ text_out(" of the creatures. ");
+ }
+
+ /* Some kills past lives */
+ else if (r_ptr->r_tkills)
+ {
+ text_out(format("and %s have exterminated at least %d of the creatures. ",
+ "your ancestors", r_ptr->r_tkills));
+ }
+
+ /* No kills */
+ else
+ {
+ text_out(format("and %s is not ever known to have been defeated. ",
+ wd_he[msex]));
+ }
+ }
+
+ /* Normal monsters */
+ else
+ {
+ /* Killed some this life */
+ if (r_ptr->r_pkills)
+ {
+ text_out("You have killed at least ");
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->r_pkills));
+ text_out(" of these creatures. ");
+ }
+
+ /* Killed some last life */
+ else if (r_ptr->r_tkills)
+ {
+ text_out(format("Your ancestors have killed at least %d of these creatures. ",
+ r_ptr->r_tkills));
+ }
+
+ /* Killed none */
+ else
+ {
+ text_out("No battles to the death are recalled. ");
+ }
+ }
+
+
+ /* Descriptions */
+ if (show_details)
+ {
+ char buf[2048];
+
+ /* Simple method */
+ strcpy(buf, r_text + r_ptr->text);
+
+ /* Dump it */
+ text_out(buf);
+ text_out(" ");
+ }
+
+
+ /* Nothing yet */
+ old = FALSE;
+
+ /* Describe location */
+ if (r_ptr->flags7 & RF7_PET)
+ {
+ text_out(format("%^s is ", wd_he[msex]));
+ text_out_c(TERM_L_BLUE, "friendly");
+ text_out(" to you");
+ old = TRUE;
+ }
+
+ /* Describe location */
+ if (r_ptr->level == 0)
+ {
+ if (old)
+ text_out(", ");
+ else
+ text_out(format("%^s ", wd_he[msex]));
+ text_out_c(TERM_L_GREEN, "lives in the town or the wilderness");
+ old = TRUE;
+ }
+ else if (r_ptr->r_tkills)
+ {
+ if (old)
+ text_out(", ");
+ else
+ text_out(format("%^s ", wd_he[msex]));
+ if (depth_in_feet)
+ {
+ text_out(format("is normally found at depths of ", wd_he[msex]));
+ if (dun_level < r_ptr->level) /* out of depth monster */
+ {
+ text_out_c(TERM_L_RED, format("%d", r_ptr->level * 50));
+ }
+ else
+ {
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->level * 50));
+ }
+ text_out(" feet");
+ }
+ else
+ {
+ text_out(format("is normally found on level ", wd_he[msex]));
+ if (dun_level < r_ptr->level) /* out of depth monster */
+ {
+ text_out_c(TERM_L_RED, format("%d", r_ptr->level));
+ }
+ else
+ {
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->level));
+ }
+ }
+ old = TRUE;
+ }
+
+
+ /* Describe movement */
+ if (TRUE)
+ {
+ /* Introduction */
+ if (old)
+ {
+ text_out(", and ");
+ }
+ else
+ {
+ text_out(format("%^s ", wd_he[msex]));
+ old = TRUE;
+ }
+ text_out("moves");
+
+ /* Random-ness */
+ if ((flags1 & (RF1_RAND_50)) || (flags1 & (RF1_RAND_25)))
+ {
+ /* Adverb */
+ if ((flags1 & (RF1_RAND_50)) && (flags1 & (RF1_RAND_25)))
+ {
+ text_out(" extremely");
+ }
+ else if (flags1 & (RF1_RAND_50))
+ {
+ text_out(" somewhat");
+ }
+ else if (flags1 & (RF1_RAND_25))
+ {
+ text_out(" a bit");
+ }
+
+ /* Adjective */
+ text_out(" erratically");
+
+ /* Hack -- Occasional conjunction */
+ if (r_ptr->speed != 110) text_out(", and");
+ }
+
+ /* Speed */
+ if (r_ptr->speed > 110)
+ {
+ if (r_ptr->speed > 130) text_out_c(TERM_RED, " incredibly");
+ else if (r_ptr->speed > 120) text_out_c(TERM_ORANGE, " very");
+ text_out_c(TERM_L_RED, " quickly");
+ }
+ else if (r_ptr->speed < 110)
+ {
+ if (r_ptr->speed < 90) text_out_c(TERM_L_GREEN, " incredibly");
+ else if (r_ptr->speed < 100) text_out_c(TERM_BLUE, " very");
+ text_out_c(TERM_L_BLUE, " slowly");
+ }
+ else
+ {
+ text_out(" at normal speed");
+ }
+ }
+
+ /* The code above includes "attack speed" */
+ if (flags1 & (RF1_NEVER_MOVE))
+ {
+ /* Introduce */
+ if (old)
+ {
+ text_out(", but ");
+ }
+ else
+ {
+ text_out(format("%^s ", wd_he[msex]));
+ old = TRUE;
+ }
+
+ /* Describe */
+ text_out("does not deign to chase intruders");
+ }
+
+ /* End this sentence */
+ if (old)
+ {
+ text_out(". ");
+ old = FALSE;
+ }
+
+
+ /* Describe experience if known */
+ if (r_ptr->r_tkills)
+ {
+ /* Introduction */
+ if (flags1 & (RF1_UNIQUE))
+ {
+ text_out("Killing this");
+ }
+ else
+ {
+ text_out("A kill of this");
+ }
+
+ /* Describe the "quality" */
+ if (flags2 & (RF2_ELDRITCH_HORROR)) text_out_c(TERM_VIOLET, " sanity-blasting");
+ if (flags3 & (RF3_ANIMAL)) text_out_c(TERM_VIOLET, " natural");
+ if (flags3 & (RF3_EVIL)) text_out_c(TERM_VIOLET, " evil");
+ if (flags3 & (RF3_GOOD)) text_out_c(TERM_VIOLET, " good");
+ if (flags3 & (RF3_UNDEAD)) text_out_c(TERM_VIOLET, " undead");
+
+ /* Describe the "race" */
+ if (flags3 & (RF3_DRAGON)) text_out_c(TERM_VIOLET, " dragon");
+ else if (flags3 & (RF3_DEMON)) text_out_c(TERM_VIOLET, " demon");
+ else if (flags3 & (RF3_GIANT)) text_out_c(TERM_VIOLET, " giant");
+ else if (flags3 & (RF3_TROLL)) text_out_c(TERM_VIOLET, " troll");
+ else if (flags3 & (RF3_ORC)) text_out_c(TERM_VIOLET, " orc");
+ else if (flags3 & (RF3_THUNDERLORD))text_out_c(TERM_VIOLET, " Thunderlord");
+ else if (flags7 & (RF7_SPIDER)) text_out_c(TERM_VIOLET, " spider");
+ else if (flags7 & (RF7_NAZGUL)) text_out_c(TERM_VIOLET, " Nazgul");
+ else text_out(" creature");
+
+ /* Group some variables */
+ if (TRUE)
+ {
+ long i, j;
+
+ /* calculate the integer exp part */
+ i = (long)r_ptr->mexp * r_ptr->level / p_ptr->lev;
+
+ /* calculate the fractional exp part scaled by 100, */
+ /* must use long arithmetic to avoid overflow */
+ j = ((((long)r_ptr->mexp * r_ptr->level % p_ptr->lev) *
+ (long)1000 / p_ptr->lev + 5) / 10);
+
+ /* Mention the experience */
+ text_out(" is worth ");
+ text_out_c(TERM_ORANGE, format("%ld.%02ld", (long)i, (long)j));
+ text_out(" point");
+ text_out(((i == 1) && (j == 0)) ? "" : "s");
+
+ /* Take account of annoying English */
+ p = "th";
+ i = p_ptr->lev % 10;
+ if ((p_ptr->lev / 10) == 1) /* nothing */;
+ else if (i == 1) p = "st";
+ else if (i == 2) p = "nd";
+ else if (i == 3) p = "rd";
+
+ /* Take account of "leading vowels" in numbers */
+ q = "";
+ i = p_ptr->lev;
+ if ((i == 8) || (i == 11) || (i == 18)) q = "n";
+
+ /* Mention the dependance on the player's level */
+ text_out(format(" for a%s %lu%s level character. ",
+ q, (long)i, p));
+ }
+ }
+
+ if ((flags2 & (RF2_AURA_FIRE)) && (flags2 & (RF2_AURA_ELEC)))
+ {
+ text_out(format("%^s is surrounded by ", wd_he[msex]));
+ text_out_c(TERM_VIOLET, "flames and electricity");
+ text_out(". ");
+ }
+ else if (flags2 & (RF2_AURA_FIRE))
+ {
+ text_out(format("%^s is surrounded by ", wd_he[msex]));
+ text_out_c(TERM_ORANGE, "flames");
+ text_out(". ");
+ }
+ else if (flags2 & (RF2_AURA_ELEC))
+ {
+ text_out(format("%^s is surrounded by ", wd_he[msex]));
+ text_out_c(TERM_L_BLUE, "electricity");
+ text_out(". ");
+ }
+
+ if (flags2 & (RF2_REFLECTING))
+ {
+ text_out(format("%^s ", wd_he[msex]));
+ text_out_c(TERM_L_UMBER, "reflects");
+ text_out(" bolt spells. ");
+ }
+
+
+ /* Describe escorts */
+ if ((flags1 & (RF1_ESCORT)) || (flags1 & (RF1_ESCORTS)))
+ {
+ text_out(format("%^s usually appears with escorts. ",
+ wd_he[msex]));
+ }
+
+ /* Describe friends */
+ else if ((flags1 & (RF1_FRIEND)) || (flags1 & (RF1_FRIENDS)))
+ {
+ text_out(format("%^s usually appears in groups. ",
+ wd_he[msex]));
+ }
+
+
+ /* Collect inate attacks */
+ vn = 0;
+ if (flags4 & (RF4_SHRIEK)) vp[vn++] = "shriek for help";
+ if (flags4 & (RF4_ROCKET)) vp[vn++] = "shoot a rocket";
+ if (flags4 & (RF4_ARROW_1)) vp[vn++] = "fire an arrow";
+ if (flags4 & (RF4_ARROW_2)) vp[vn++] = "fire arrows";
+ if (flags4 & (RF4_ARROW_3)) vp[vn++] = "fire a missile";
+ if (flags4 & (RF4_ARROW_4)) vp[vn++] = "fire missiles";
+
+ /* Describe inate attacks */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" may ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" or ");
+
+ /* Dump */
+ text_out_c(TERM_YELLOW, vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Collect breaths */
+ vn = 0;
+ if (flags4 & (RF4_BR_ACID)) vp[vn++] = "acid";
+ if (flags4 & (RF4_BR_ELEC)) vp[vn++] = "lightning";
+ if (flags4 & (RF4_BR_FIRE)) vp[vn++] = "fire";
+ if (flags4 & (RF4_BR_COLD)) vp[vn++] = "frost";
+ if (flags4 & (RF4_BR_POIS)) vp[vn++] = "poison";
+ if (flags4 & (RF4_BR_NETH)) vp[vn++] = "nether";
+ if (flags4 & (RF4_BR_LITE)) vp[vn++] = "light";
+ if (flags4 & (RF4_BR_DARK)) vp[vn++] = "darkness";
+ if (flags4 & (RF4_BR_CONF)) vp[vn++] = "confusion";
+ if (flags4 & (RF4_BR_SOUN)) vp[vn++] = "sound";
+ if (flags4 & (RF4_BR_CHAO)) vp[vn++] = "chaos";
+ if (flags4 & (RF4_BR_DISE)) vp[vn++] = "disenchantment";
+ if (flags4 & (RF4_BR_NEXU)) vp[vn++] = "nexus";
+ if (flags4 & (RF4_BR_TIME)) vp[vn++] = "time";
+ if (flags4 & (RF4_BR_INER)) vp[vn++] = "inertia";
+ if (flags4 & (RF4_BR_GRAV)) vp[vn++] = "gravity";
+ if (flags4 & (RF4_BR_SHAR)) vp[vn++] = "shards";
+ if (flags4 & (RF4_BR_PLAS)) vp[vn++] = "plasma";
+ if (flags4 & (RF4_BR_WALL)) vp[vn++] = "force";
+ if (flags4 & (RF4_BR_MANA)) vp[vn++] = "mana";
+ if (flags4 & (RF4_BR_NUKE)) vp[vn++] = "toxic waste";
+ if (flags4 & (RF4_BR_DISI)) vp[vn++] = "disintegration";
+
+ /* Describe breaths */
+ if (vn)
+ {
+ /* Note breath */
+ breath = TRUE;
+
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" may breathe ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" or ");
+
+ /* Dump */
+ text_out_c(TERM_YELLOW, vp[n]);
+ }
+ }
+
+
+ /* Collect spells */
+ vn = 0;
+ if (flags5 & (RF5_BA_ACID)) vp[vn++] = "produce acid balls";
+ if (flags5 & (RF5_BA_ELEC)) vp[vn++] = "produce lightning balls";
+ if (flags5 & (RF5_BA_FIRE)) vp[vn++] = "produce fire balls";
+ if (flags5 & (RF5_BA_COLD)) vp[vn++] = "produce frost balls";
+ if (flags5 & (RF5_BA_POIS)) vp[vn++] = "produce poison balls";
+ if (flags5 & (RF5_BA_NETH)) vp[vn++] = "produce nether balls";
+ if (flags5 & (RF5_BA_WATE)) vp[vn++] = "produce water balls";
+ if (flags4 & (RF4_BA_NUKE)) vp[vn++] = "produce balls of radiation";
+ if (flags5 & (RF5_BA_MANA)) vp[vn++] = "invoke mana storms";
+ if (flags5 & (RF5_BA_DARK)) vp[vn++] = "invoke darkness storms";
+ if (flags4 & (RF4_BA_CHAO)) vp[vn++] = "invoke raw chaos";
+ if (flags6 & (RF6_HAND_DOOM)) vp[vn++] = "invoke the Hand of Doom";
+ if (flags5 & (RF5_DRAIN_MANA)) vp[vn++] = "drain mana";
+ if (flags5 & (RF5_MIND_BLAST)) vp[vn++] = "cause mind blasting";
+ if (flags5 & (RF5_BRAIN_SMASH)) vp[vn++] = "cause brain smashing";
+ if (flags5 & (RF5_CAUSE_1)) vp[vn++] = "cause light wounds and cursing";
+ if (flags5 & (RF5_CAUSE_2)) vp[vn++] = "cause serious wounds and cursing";
+ if (flags5 & (RF5_CAUSE_3)) vp[vn++] = "cause critical wounds and cursing";
+ if (flags5 & (RF5_CAUSE_4)) vp[vn++] = "cause mortal wounds";
+ if (flags5 & (RF5_BO_ACID)) vp[vn++] = "produce acid bolts";
+ if (flags5 & (RF5_BO_ELEC)) vp[vn++] = "produce lightning bolts";
+ if (flags5 & (RF5_BO_FIRE)) vp[vn++] = "produce fire bolts";
+ if (flags5 & (RF5_BO_COLD)) vp[vn++] = "produce frost bolts";
+ if (flags5 & (RF5_BO_POIS)) vp[vn++] = "produce poison bolts";
+ if (flags5 & (RF5_BO_NETH)) vp[vn++] = "produce nether bolts";
+ if (flags5 & (RF5_BO_WATE)) vp[vn++] = "produce water bolts";
+ if (flags5 & (RF5_BO_MANA)) vp[vn++] = "produce mana bolts";
+ if (flags5 & (RF5_BO_PLAS)) vp[vn++] = "produce plasma bolts";
+ if (flags5 & (RF5_BO_ICEE)) vp[vn++] = "produce ice bolts";
+ if (flags5 & (RF5_MISSILE)) vp[vn++] = "produce magic missiles";
+ if (flags5 & (RF5_SCARE)) vp[vn++] = "terrify";
+ if (flags5 & (RF5_BLIND)) vp[vn++] = "blind";
+ if (flags5 & (RF5_CONF)) vp[vn++] = "confuse";
+ if (flags5 & (RF5_SLOW)) vp[vn++] = "slow";
+ if (flags5 & (RF5_HOLD)) vp[vn++] = "paralyze";
+ if (flags6 & (RF6_HASTE)) vp[vn++] = "haste-self";
+ if (flags6 & (RF6_HEAL)) vp[vn++] = "heal-self";
+ if (flags6 & (RF6_BLINK)) vp[vn++] = "blink-self";
+ if (flags6 & (RF6_TPORT)) vp[vn++] = "teleport-self";
+ if (flags6 & (RF6_S_BUG)) vp[vn++] = "summon software bugs";
+ if (flags6 & (RF6_S_RNG)) vp[vn++] = "summon RNG";
+ if (flags6 & (RF6_TELE_TO)) vp[vn++] = "teleport to";
+ if (flags6 & (RF6_TELE_AWAY)) vp[vn++] = "teleport away";
+ if (flags6 & (RF6_TELE_LEVEL)) vp[vn++] = "teleport level";
+ if (flags6 & (RF6_S_THUNDERLORD)) vp[vn++] = "summon a Thunderlord";
+ if (flags6 & (RF6_DARKNESS)) vp[vn++] = "create darkness";
+ if (flags6 & (RF6_TRAPS)) vp[vn++] = "create traps";
+ if (flags6 & (RF6_FORGET)) vp[vn++] = "cause amnesia";
+ if (flags6 & (RF6_RAISE_DEAD)) vp[vn++] = "raise dead";
+ if (flags6 & (RF6_S_MONSTER)) vp[vn++] = "summon a monster";
+ if (flags6 & (RF6_S_MONSTERS)) vp[vn++] = "summon monsters";
+ if (flags6 & (RF6_S_KIN)) vp[vn++] = "summon aid";
+ if (flags6 & (RF6_S_ANT)) vp[vn++] = "summon ants";
+ if (flags6 & (RF6_S_SPIDER)) vp[vn++] = "summon spiders";
+ if (flags6 & (RF6_S_HOUND)) vp[vn++] = "summon hounds";
+ if (flags6 & (RF6_S_HYDRA)) vp[vn++] = "summon hydras";
+ if (flags6 & (RF6_S_ANGEL)) vp[vn++] = "summon an angel";
+ if (flags6 & (RF6_S_DEMON)) vp[vn++] = "summon a demon";
+ if (flags6 & (RF6_S_UNDEAD)) vp[vn++] = "summon an undead";
+ if (flags6 & (RF6_S_DRAGON)) vp[vn++] = "summon a dragon";
+ if (flags4 & (RF4_S_ANIMAL)) vp[vn++] = "summon animal";
+ if (flags6 & (RF6_S_ANIMALS)) vp[vn++] = "summon animals";
+ if (flags6 & (RF6_S_HI_UNDEAD)) vp[vn++] = "summon Greater Undead";
+ if (flags6 & (RF6_S_HI_DRAGON)) vp[vn++] = "summon Ancient Dragons";
+ if (flags6 & (RF6_S_HI_DEMON)) vp[vn++] = "summon Greater Demons";
+ if (flags6 & (RF6_S_WRAITH)) vp[vn++] = "summon Ringwraith";
+ if (flags6 & (RF6_S_UNIQUE)) vp[vn++] = "summon Unique Monsters";
+
+ /* Describe spells */
+ if (vn)
+ {
+ /* Note magic */
+ magic = TRUE;
+
+ /* Intro */
+ if (breath)
+ {
+ text_out(", and is also");
+ }
+ else
+ {
+ text_out(format("%^s is", wd_he[msex]));
+ }
+
+ /* Verb Phrase */
+ text_out(" magical, casting spells");
+
+ /* Adverb */
+ if (flags2 & (RF2_SMART)) text_out_c(TERM_YELLOW, " intelligently");
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" which ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" or ");
+
+ /* Dump */
+ text_out_c(TERM_YELLOW, vp[n]);
+ }
+ }
+
+
+ /* End the sentence about inate/other spells */
+ if (breath || magic)
+ {
+ /* Total casting */
+ m = r_ptr->r_cast_inate + r_ptr->r_cast_spell;
+
+ /* Average frequency */
+ n = (r_ptr->freq_inate + r_ptr->freq_spell) / 2;
+
+ /* Describe the spell frequency */
+ if (m > 100)
+ {
+ text_out("; ");
+ text_out_c(TERM_L_GREEN, "1");
+ text_out(" time in ");
+ text_out_c(TERM_L_GREEN, format("%d", 100 / n));
+ }
+
+ /* Guess at the frequency */
+ else if (m)
+ {
+ n = ((n + 9) / 10) * 10;
+ text_out("; about ");
+ text_out_c(TERM_L_GREEN, "1");
+ text_out(" time in ");
+ text_out_c(TERM_L_GREEN, format("%d", 100 / n));
+ }
+
+ /* End this sentence */
+ text_out(". ");
+ }
+
+
+ /* Describe monster "toughness" */
+ if (know_armour(r_idx))
+ {
+ /* Armor */
+ text_out(format("%^s has an armor rating of ", wd_he[msex]));
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->ac));
+
+ /* Maximized hitpoints */
+ if (flags1 & (RF1_FORCE_MAXHP))
+ {
+ text_out(" and a life rating of ");
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->hdice * r_ptr->hside));
+ text_out(". ");
+ }
+
+ /* Variable hitpoints */
+ else
+ {
+ text_out(" and a life rating of ");
+ text_out_c(TERM_L_GREEN, format("%dd%d", r_ptr->hdice, r_ptr->hside));
+ text_out(". ");
+ }
+ }
+
+
+
+ /* Collect special abilities. */
+ vn = 0;
+ if (flags2 & (RF2_OPEN_DOOR)) vp[vn++] = "open doors";
+ if (flags2 & (RF2_BASH_DOOR)) vp[vn++] = "bash down doors";
+ if (flags2 & (RF2_PASS_WALL)) vp[vn++] = "pass through walls";
+ if (flags2 & (RF2_KILL_WALL)) vp[vn++] = "bore through walls";
+ if (flags2 & (RF2_MOVE_BODY)) vp[vn++] = "push past weaker monsters";
+ if (flags2 & (RF2_KILL_BODY)) vp[vn++] = "destroy weaker monsters";
+ if (flags2 & (RF2_TAKE_ITEM)) vp[vn++] = "pick up objects";
+ if (flags2 & (RF2_KILL_ITEM)) vp[vn++] = "destroy objects";
+ if (flags9 & (RF9_HAS_LITE)) vp[vn++] = "illuminate the dungeon";
+
+ /* Describe special abilities. */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" can ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump */
+ text_out(vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Describe special abilities. */
+ if (flags2 & (RF2_INVISIBLE))
+ {
+ text_out_c(TERM_GREEN, format("%^s is invisible. ", wd_he[msex]));
+ }
+ if (flags2 & (RF2_COLD_BLOOD))
+ {
+ text_out(format("%^s is cold blooded. ", wd_he[msex]));
+ }
+ if (flags2 & (RF2_EMPTY_MIND))
+ {
+ text_out(format("%^s is not detected by telepathy. ", wd_he[msex]));
+ }
+ if (flags2 & (RF2_WEIRD_MIND))
+ {
+ text_out(format("%^s is rarely detected by telepathy. ", wd_he[msex]));
+ }
+ if (flags4 & (RF4_MULTIPLY))
+ {
+ text_out_c(TERM_L_UMBER, format("%^s breeds explosively. ", wd_he[msex]));
+ }
+ if (flags2 & (RF2_REGENERATE))
+ {
+ text_out_c(TERM_L_WHITE, format("%^s regenerates quickly. ", wd_he[msex]));
+ }
+ if (r_ptr->flags7 & (RF7_MORTAL))
+ {
+ text_out_c(TERM_RED, format("%^s is a mortal being. ", wd_he[msex]));
+ }
+ else
+ {
+ text_out_c(TERM_L_BLUE, format("%^s is an immortal being. ", wd_he[msex]));
+ }
+
+
+ /* Collect susceptibilities */
+ vn = 0;
+ if (flags3 & (RF3_HURT_ROCK))
+ {
+ vp[vn++] = "rock remover";
+ color[vn - 1] = TERM_UMBER;
+ }
+ if (flags3 & (RF3_HURT_LITE))
+ {
+ vp[vn++] = "bright light";
+ color[vn - 1] = TERM_YELLOW;
+ }
+ if (flags3 & (RF3_SUSCEP_FIRE))
+ {
+ vp[vn++] = "fire";
+ color[vn - 1] = TERM_RED;
+ }
+ if (flags3 & (RF3_SUSCEP_COLD))
+ {
+ vp[vn++] = "cold";
+ color[vn - 1] = TERM_L_WHITE;
+ }
+ if (flags9 & (RF9_SUSCEP_ACID))
+ {
+ vp[vn++] = "acid";
+ color[vn - 1] = TERM_GREEN;
+ }
+ if (flags9 & (RF9_SUSCEP_ELEC))
+ {
+ vp[vn++] = "lightning";
+ color[vn - 1] = TERM_L_BLUE;
+ }
+ if (flags9 & (RF9_SUSCEP_POIS))
+ {
+ vp[vn++] = "poison";
+ color[vn - 1] = TERM_L_GREEN;
+ }
+
+ /* Describe susceptibilities */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" is hurt by ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump */
+ text_out_c(color[n], vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Collect immunities */
+ vn = 0;
+ if (flags3 & (RF3_IM_ACID))
+ {
+ vp[vn++] = "acid";
+ color[vn - 1] = TERM_L_GREEN;
+ }
+ if (flags3 & (RF3_IM_ELEC))
+ {
+ vp[vn++] = "lightning";
+ color[vn - 1] = TERM_L_BLUE;
+ }
+ if (flags3 & (RF3_IM_FIRE))
+ {
+ vp[vn++] = "fire";
+ color[vn - 1] = TERM_L_RED;
+ }
+ if (flags3 & (RF3_IM_COLD))
+ {
+ vp[vn++] = "cold";
+ color[vn - 1] = TERM_L_BLUE;
+ }
+ if (flags3 & (RF3_IM_POIS))
+ {
+ vp[vn++] = "poison";
+ color[vn - 1] = TERM_L_GREEN;
+ }
+
+ /* Describe immunities */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" resists ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump */
+ text_out_c(color[n], vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Collect resistances */
+ vn = 0;
+ if (flags3 & (RF3_RES_NETH)) vp[vn++] = "nether";
+ if (flags3 & (RF3_RES_WATE)) vp[vn++] = "water";
+ if (flags3 & (RF3_RES_PLAS)) vp[vn++] = "plasma";
+ if (flags3 & (RF3_RES_NEXU)) vp[vn++] = "nexus";
+ if (flags3 & (RF3_RES_DISE)) vp[vn++] = "disenchantment";
+ if (flags3 & (RF3_RES_TELE)) vp[vn++] = "teleportation";
+
+ /* Describe resistances */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" resists ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump */
+ text_out_c(TERM_L_BLUE, vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Collect non-effects */
+ vn = 0;
+ if (flags3 & (RF3_NO_STUN)) vp[vn++] = "stunned";
+ if (flags3 & (RF3_NO_FEAR)) vp[vn++] = "frightened";
+ if (flags3 & (RF3_NO_CONF)) vp[vn++] = "confused";
+ if (flags3 & (RF3_NO_SLEEP)) vp[vn++] = "slept";
+
+ /* Describe non-effects */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" cannot be ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" or ");
+
+ /* Dump */
+ text_out(vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Do we know how aware it is? */
+ if ((((int)r_ptr->r_wake * (int)r_ptr->r_wake) > r_ptr->sleep) ||
+ (r_ptr->r_ignore == MAX_UCHAR) ||
+ ((r_ptr->sleep == 0) && (r_ptr->r_tkills >= 10)))
+ {
+ cptr act;
+
+ if (r_ptr->sleep > 200)
+ {
+ act = "prefers to ignore";
+ }
+ else if (r_ptr->sleep > 95)
+ {
+ act = "pays very little attention to";
+ }
+ else if (r_ptr->sleep > 75)
+ {
+ act = "pays little attention to";
+ }
+ else if (r_ptr->sleep > 45)
+ {
+ act = "tends to overlook";
+ }
+ else if (r_ptr->sleep > 25)
+ {
+ act = "takes quite a while to see";
+ }
+ else if (r_ptr->sleep > 10)
+ {
+ act = "takes a while to see";
+ }
+ else if (r_ptr->sleep > 5)
+ {
+ act = "is fairly observant of";
+ }
+ else if (r_ptr->sleep > 3)
+ {
+ act = "is observant of";
+ }
+ else if (r_ptr->sleep > 1)
+ {
+ act = "is very observant of";
+ }
+ else if (r_ptr->sleep > 0)
+ {
+ act = "is vigilant for";
+ }
+ else
+ {
+ act = "is ever vigilant for";
+ }
+
+ text_out(format("%^s %s intruders, which %s may notice from %d feet. ",
+ wd_he[msex], act, wd_he[msex], 10 * r_ptr->aaf));
+ }
+
+
+ /* Drops gold and/or items */
+ if (r_ptr->r_drop_gold || r_ptr->r_drop_item)
+ {
+ /* No "n" needed */
+ sin = FALSE;
+
+ /* Intro */
+ text_out(format("%^s may carry", wd_he[msex]));
+
+ /* Count maximum drop */
+ n = MAX(r_ptr->r_drop_gold, r_ptr->r_drop_item);
+
+ /* One drop (may need an "n") */
+ if (n == 1)
+ {
+ text_out(" a");
+ sin = TRUE;
+ }
+
+ /* Two drops */
+ else if (n == 2)
+ {
+ text_out(" one or two");
+ }
+
+ /* Many drops */
+ else
+ {
+ text_out(format(" up to %d", n));
+ }
+
+
+ /* Great */
+ if (flags1 & (RF1_DROP_GREAT))
+ {
+ p = " exceptional";
+ }
+
+ /* Good (no "n" needed) */
+ else if (flags1 & (RF1_DROP_GOOD))
+ {
+ p = " good";
+ sin = FALSE;
+ }
+
+ /* Okay */
+ else
+ {
+ p = NULL;
+ }
+
+
+ /* Objects */
+ if (r_ptr->r_drop_item)
+ {
+ /* Handle singular "an" */
+ if (sin) text_out("n");
+ sin = FALSE;
+
+ /* Dump "object(s)" */
+ if (p) text_out_c(TERM_ORANGE, p);
+ text_out(" object");
+ if (n != 1) text_out("s");
+
+ /* Conjunction replaces variety, if needed for "gold" below */
+ p = " or";
+ }
+
+ /* Treasures */
+ if (r_ptr->r_drop_gold)
+ {
+ /* Cancel prefix */
+ if (!p) sin = FALSE;
+
+ /* Handle singular "an" */
+ if (sin) text_out("n");
+ sin = FALSE;
+
+ /* Dump "treasure(s)" */
+ if (p) text_out(p);
+ text_out(" treasure");
+ if (n != 1) text_out("s");
+ }
+
+ /* End this sentence */
+ text_out(". ");
+ }
+
+
+ /* Count the number of "known" attacks */
+ for (n = 0, m = 0; m < 4; m++)
+ {
+ /* Skip non-attacks */
+ if (!r_ptr->blow[m].method) continue;
+
+ /* Count known attacks */
+ if (r_ptr->r_blows[m]) n++;
+ }
+
+ /* Examine (and count) the actual attacks */
+ for (r = 0, m = 0; m < 4; m++)
+ {
+ int method, effect, d1, d2;
+
+ /* Skip non-attacks */
+ if (!r_ptr->blow[m].method) continue;
+
+ /* Skip unknown attacks */
+ if (!r_ptr->r_blows[m]) continue;
+
+
+ /* Extract the attack info */
+ method = r_ptr->blow[m].method;
+ effect = r_ptr->blow[m].effect;
+ d1 = r_ptr->blow[m].d_dice;
+ d2 = r_ptr->blow[m].d_side;
+
+
+ /* No method yet */
+ p = NULL;
+
+ /* Acquire the method */
+ switch (method)
+ {
+ case RBM_HIT:
+ p = "hit";
+ break;
+ case RBM_TOUCH:
+ p = "touch";
+ break;
+ case RBM_PUNCH:
+ p = "punch";
+ break;
+ case RBM_KICK:
+ p = "kick";
+ break;
+ case RBM_CLAW:
+ p = "claw";
+ break;
+ case RBM_BITE:
+ p = "bite";
+ break;
+ case RBM_STING:
+ p = "sting";
+ break;
+ case RBM_XXX1:
+ break;
+ case RBM_BUTT:
+ p = "butt";
+ break;
+ case RBM_CRUSH:
+ p = "crush";
+ break;
+ case RBM_ENGULF:
+ p = "engulf";
+ break;
+ case RBM_CHARGE:
+ p = "charge";
+ break;
+ case RBM_CRAWL:
+ p = "crawl on you";
+ break;
+ case RBM_DROOL:
+ p = "drool on you";
+ break;
+ case RBM_SPIT:
+ p = "spit";
+ break;
+ case RBM_EXPLODE:
+ p = "explode";
+ break;
+ case RBM_GAZE:
+ p = "gaze";
+ break;
+ case RBM_WAIL:
+ p = "wail";
+ break;
+ case RBM_SPORE:
+ p = "release spores";
+ break;
+ case RBM_XXX4:
+ break;
+ case RBM_BEG:
+ p = "beg";
+ break;
+ case RBM_INSULT:
+ p = "insult";
+ break;
+ case RBM_MOAN:
+ p = "moan";
+ break;
+ case RBM_SHOW:
+ p = "sing";
+ break;
+ }
+
+
+ /* Default effect */
+ q = NULL;
+
+ /* Acquire the effect */
+ switch (effect)
+ {
+ case RBE_HURT:
+ q = "attack";
+ break;
+ case RBE_POISON:
+ q = "poison";
+ break;
+ case RBE_UN_BONUS:
+ q = "disenchant";
+ break;
+ case RBE_UN_POWER:
+ q = "drain charges";
+ break;
+ case RBE_EAT_GOLD:
+ q = "steal gold";
+ break;
+ case RBE_EAT_ITEM:
+ q = "steal items";
+ break;
+ case RBE_EAT_FOOD:
+ q = "eat your food";
+ break;
+ case RBE_EAT_LITE:
+ q = "absorb light";
+ break;
+ case RBE_ACID:
+ q = "shoot acid";
+ break;
+ case RBE_ELEC:
+ q = "electrocute";
+ break;
+ case RBE_FIRE:
+ q = "burn";
+ break;
+ case RBE_COLD:
+ q = "freeze";
+ break;
+ case RBE_BLIND:
+ q = "blind";
+ break;
+ case RBE_CONFUSE:
+ q = "confuse";
+ break;
+ case RBE_TERRIFY:
+ q = "terrify";
+ break;
+ case RBE_PARALYZE:
+ q = "paralyze";
+ break;
+ case RBE_LOSE_STR:
+ q = "reduce strength";
+ break;
+ case RBE_LOSE_INT:
+ q = "reduce intelligence";
+ break;
+ case RBE_LOSE_WIS:
+ q = "reduce wisdom";
+ break;
+ case RBE_LOSE_DEX:
+ q = "reduce dexterity";
+ break;
+ case RBE_LOSE_CON:
+ q = "reduce constitution";
+ break;
+ case RBE_LOSE_CHR:
+ q = "reduce charisma";
+ break;
+ case RBE_LOSE_ALL:
+ q = "reduce all stats";
+ break;
+ case RBE_SHATTER:
+ q = "shatter";
+ break;
+ case RBE_EXP_10:
+ q = "lower experience (by 10d6+)";
+ break;
+ case RBE_EXP_20:
+ q = "lower experience (by 20d6+)";
+ break;
+ case RBE_EXP_40:
+ q = "lower experience (by 40d6+)";
+ break;
+ case RBE_EXP_80:
+ q = "lower experience (by 80d6+)";
+ break;
+ case RBE_DISEASE:
+ q = "disease";
+ break;
+ case RBE_TIME:
+ q = "time";
+ break;
+ case RBE_SANITY:
+ q = "blast sanity";
+ break;
+ case RBE_HALLU:
+ q = "cause hallucinations";
+ break;
+ case RBE_PARASITE:
+ q = "parasite";
+ break;
+ }
+
+
+ /* Introduce the attack description */
+ if (!r)
+ {
+ text_out(format("%^s can ", wd_he[msex]));
+ }
+ else if (r < n - 1)
+ {
+ text_out(", ");
+ }
+ else
+ {
+ text_out(", and ");
+ }
+
+
+ /* Hack -- force a method */
+ if (!p) p = "do something weird";
+
+ /* Describe the method */
+ text_out(p);
+
+
+ /* Describe the effect (if any) */
+ if (q)
+ {
+ /* Describe the attack type */
+ text_out(" to ");
+ text_out_c(TERM_YELLOW, q);
+
+ /* Describe damage (if known) */
+ if (d1 && d2 && know_damage(r_idx, m))
+ {
+ /* Display the damage */
+ text_out(" with damage");
+ text_out_c(TERM_L_GREEN, format(" %dd%d", d1, d2));
+ }
+ }
+
+
+ /* Count the attacks as printed */
+ r++;
+ }
+
+ /* Finish sentence above */
+ if (r)
+ {
+ text_out(". ");
+ }
+
+ /* Notice lack of attacks */
+ else if (flags1 & (RF1_NEVER_BLOW))
+ {
+ text_out(format("%^s has no physical attacks. ", wd_he[msex]));
+ }
+
+ /* Or describe the lack of knowledge */
+ else
+ {
+ text_out(format("Nothing is known about %s attack. ", wd_his[msex]));
+ }
+
+
+ /* All done */
+ text_out("\n");
+
+ /* Cheat -- know everything */
+ if ((cheat_know) && (remem == 0))
+ {
+ /* Hack -- restore memory */
+ COPY(r_ptr, &save_mem, monster_race);
+ }
+}
+
+/*
+ * Hack -- Display the "name" and "attr/chars" of a monster race
+ */
+static void roff_name(int r_idx, int ego)
+{
+ monster_race *r_ptr = race_info_idx(r_idx, ego);
+
+ byte a1, a2;
+ char c1, c2;
+
+ /* Access the chars */
+ c1 = r_ptr->d_char;
+ c2 = r_ptr->x_char;
+
+ /* Access the attrs */
+ a1 = r_ptr->d_attr;
+ a2 = r_ptr->x_attr;
+
+ /* A title (use "The" for non-uniques) */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ Term_addstr( -1, TERM_WHITE, "The ");
+ }
+
+ /* Dump the name */
+ if (ego)
+ {
+ if (re_info[ego].before) Term_addstr( -1, TERM_WHITE, format("%s %s", re_name + re_info[ego].name, r_name + r_ptr->name));
+ else Term_addstr( -1, TERM_WHITE, format("%s %s", r_name + r_ptr->name, re_name + re_info[ego].name));
+ }
+ else
+ {
+ Term_addstr( -1, TERM_WHITE, r_name + r_ptr->name);
+ }
+
+ /* Append the "standard" attr/char info */
+ Term_addstr( -1, TERM_WHITE, " ('");
+ Term_addch(a1, c1);
+ if (use_bigtile && (a1 & 0x80)) Term_addch(255, 255);
+ Term_addstr( -1, TERM_WHITE, "')");
+
+ /* Append the "optional" attr/char info */
+ Term_addstr( -1, TERM_WHITE, "/('");
+ Term_addch(a2, c2);
+ if (use_bigtile && (a2 & 0x80)) Term_addch(255, 255);
+ Term_addstr( -1, TERM_WHITE, "'):");
+}
+
+/*
+ * Hack -- Display the "name" and "attr/chars" of a monster race on top
+ */
+static void roff_top(int r_idx, int ego)
+{
+ /* Clear the top line */
+ Term_erase(0, 0, 255);
+
+ /* Reset the cursor */
+ Term_gotoxy(0, 0);
+
+ roff_name(r_idx, ego);
+}
+
+/*
+ * Hack -- describe the given monster race at the top of the screen
+ */
+void screen_roff(int r_idx, int ego, int remember)
+{
+ /* Flush messages */
+ msg_print(NULL);
+
+ /* Begin recall */
+ Term_erase(0, 1, 255);
+
+ /* Recall monster */
+ roff_aux(r_idx, ego, remember);
+
+ /* Describe monster */
+ roff_top(r_idx, ego);
+}
+
+/*
+ * Ddescribe the given monster race at the current pos of the "term" window
+ */
+void monster_description_out(int r_idx, int ego)
+{
+ roff_name(r_idx, ego);
+ roff_aux(r_idx, ego, 0);
+}
+
+/*
+ * Hack -- describe the given monster race in the current "term" window
+ */
+void display_roff(int r_idx, int ego)
+{
+ int y;
+
+ /* Erase the window */
+ for (y = 0; y < Term->hgt; y++)
+ {
+ /* Erase the line */
+ Term_erase(0, y, 255);
+ }
+
+ /* Begin recall */
+ Term_gotoxy(0, 1);
+
+ /* Recall monster */
+ roff_aux(r_idx, ego, 0);
+
+ /* Describe monster */
+ roff_top(r_idx, ego);
+}
+
+
+bool_ monster_quest(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Random quests are in the dungeon */
+ if (!(r_ptr->flags8 & RF8_DUNGEON)) return FALSE;
+
+ /* No random quests for aquatic monsters */
+ if (r_ptr->flags7 & RF7_AQUATIC) return FALSE;
+
+ /* No random quests for multiplying monsters */
+ if (r_ptr->flags4 & RF4_MULTIPLY) return FALSE;
+
+ return TRUE;
+}
+
+
+bool_ monster_dungeon(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_DUNGEON)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_ocean(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_OCEAN)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_shore(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_SHORE)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_waste(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_WASTE)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_town(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_TOWN)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_wood(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_WOOD)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_volcano(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_VOLCANO)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_mountain(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_MOUNTAIN)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_grass(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_GRASS)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_deep_water(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (!monster_dungeon(r_idx)) return FALSE;
+
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool_ monster_shallow_water(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (!monster_dungeon(r_idx)) return FALSE;
+
+ if (r_ptr->flags2 & RF2_AURA_FIRE)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+bool_ monster_lava(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (!monster_dungeon(r_idx)) return FALSE;
+
+ if (((r_ptr->flags3 & RF3_IM_FIRE) ||
+ (r_ptr->flags7 & RF7_CAN_FLY)) &&
+ !(r_ptr->flags3 & RF3_AURA_COLD))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+void set_mon_num_hook(void)
+{
+ if (!dun_level)
+ {
+ switch (wf_info[wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat].terrain_idx)
+ {
+ case TERRAIN_TOWN:
+ get_mon_num_hook = monster_town;
+ break;
+ case TERRAIN_DEEP_WATER:
+ get_mon_num_hook = monster_ocean;
+ break;
+ case TERRAIN_SHALLOW_WATER:
+ get_mon_num_hook = monster_shore;
+ break;
+ case TERRAIN_DIRT:
+ get_mon_num_hook = monster_waste;
+ break;
+ case TERRAIN_GRASS:
+ get_mon_num_hook = monster_grass;
+ break;
+ case TERRAIN_TREES:
+ get_mon_num_hook = monster_wood;
+ break;
+ case TERRAIN_SHALLOW_LAVA:
+ case TERRAIN_DEEP_LAVA:
+ get_mon_num_hook = monster_volcano;
+ break;
+ case TERRAIN_MOUNTAIN:
+ get_mon_num_hook = monster_mountain;
+ break;
+ default:
+ get_mon_num_hook = monster_dungeon;
+ break;
+ }
+ }
+ else
+ {
+ get_mon_num_hook = monster_dungeon;
+ }
+}
+
+
+/*
+ * Check if monster can cross terrain
+ */
+bool_ monster_can_cross_terrain(byte feat, monster_race *r_ptr)
+{
+ /* Deep water */
+ if (feat == FEAT_DEEP_WATER)
+ {
+ if ((r_ptr->flags7 & RF7_AQUATIC) ||
+ (r_ptr->flags7 & RF7_CAN_FLY) ||
+ (r_ptr->flags7 & RF7_CAN_SWIM))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ /* Shallow water */
+ else if (feat == FEAT_SHAL_WATER)
+ {
+ if (r_ptr->flags2 & RF2_AURA_FIRE)
+ return FALSE;
+ else
+ return TRUE;
+ }
+ /* Aquatic monster */
+ else if ((r_ptr->flags7 & RF7_AQUATIC) &&
+ !(r_ptr->flags7 & RF7_CAN_FLY))
+ {
+ return FALSE;
+ }
+ /* Lava */
+ else if ((feat == FEAT_SHAL_LAVA) ||
+ (feat == FEAT_DEEP_LAVA))
+ {
+ if ((r_ptr->flags3 & RF3_IM_FIRE) ||
+ (r_ptr->flags7 & RF7_CAN_FLY))
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void set_mon_num2_hook(int y, int x)
+{
+ /* Set the monster list */
+ switch (cave[y][x].feat)
+ {
+ case FEAT_SHAL_WATER:
+ get_mon_num2_hook = monster_shallow_water;
+ break;
+ case FEAT_DEEP_WATER:
+ get_mon_num2_hook = monster_deep_water;
+ break;
+ case FEAT_DEEP_LAVA:
+ case FEAT_SHAL_LAVA:
+ get_mon_num2_hook = monster_lava;
+ break;
+ default:
+ get_mon_num2_hook = NULL;
+ break;
+ }
+}
diff --git a/src/monster2.c b/src/monster2.c
new file mode 100644
index 00000000..b0753244
--- /dev/null
+++ b/src/monster2.c
@@ -0,0 +1,4054 @@
+/* File: monster2.c */
+
+/* Purpose: misc code for monsters */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#define MAX_HORROR 20
+#define MAX_FUNNY 22
+#define MAX_COMMENT 5
+
+/* Monster gain a few levels ? */
+void monster_check_experience(int m_idx, bool_ silent)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ char m_name[80];
+
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Gain levels while possible */
+ while ((m_ptr->level < MONSTER_LEVEL_MAX) &&
+ (m_ptr->exp >= (u32b)(MONSTER_EXP(m_ptr->level + 1))))
+ {
+ /* Gain a level */
+ m_ptr->level++;
+
+ if (m_ptr->ml && (!silent)) cmsg_format(TERM_L_BLUE, "%^s gains a level.", m_name);
+
+ /* Gain hp */
+ if (magik(80))
+ {
+ m_ptr->maxhp += r_ptr->hside;
+ m_ptr->hp += r_ptr->hside;
+ }
+
+ /* Gain speed */
+ if (magik(40))
+ {
+ int speed = randint(2);
+ m_ptr->speed += speed;
+ m_ptr->mspeed += speed;
+ }
+
+ /* Gain ac */
+ if (magik(50))
+ {
+ m_ptr->ac += (r_ptr->ac / 10) ? r_ptr->ac / 10 : 1;
+ }
+
+ /* Gain melee power */
+ if (magik(30))
+ {
+ int i = rand_int(3), tries = 20;
+
+ while ((tries--) && !m_ptr->blow[i].d_dice) i = rand_int(3);
+
+ m_ptr->blow[i].d_dice++;
+ }
+ }
+}
+
+/* Monster gain some xp */
+void monster_gain_exp(int m_idx, u32b exp, bool_ silent)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ m_ptr->exp += exp;
+ if (wizard)
+ {
+ char m_name[80];
+
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ if (!silent) msg_format("%^s gains %ld exp.", m_name, exp);
+ }
+
+ monster_check_experience(m_idx, silent);
+}
+
+void monster_set_level(int m_idx, int level)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (level > 150) level = 150;
+
+ if (m_ptr->level < level)
+ {
+ m_ptr->exp = MONSTER_EXP(level);
+ monster_check_experience(m_idx, TRUE);
+ }
+}
+
+/* Will add, sub, .. */
+s32b modify_aux(s32b a, s32b b, char mod)
+{
+ switch (mod)
+ {
+ case MEGO_ADD:
+ return (a + b);
+ break;
+ case MEGO_SUB:
+ return (a - b);
+ break;
+ case MEGO_FIX:
+ return (b);
+ break;
+ case MEGO_PRC:
+ return (a * b / 100);
+ break;
+ default:
+ msg_format("WARNING, unmatching MEGO(%d).", mod);
+ return (0);
+ }
+}
+
+/* Is this ego ok for this monster ? */
+bool_ mego_ok(int r_idx, int ego)
+{
+ monster_ego *re_ptr = &re_info[ego];
+ monster_race *r_ptr = &r_info[r_idx];
+ bool_ ok = FALSE;
+ int i;
+
+ /* needed flags */
+ if (re_ptr->flags1 && ((re_ptr->flags1 & r_ptr->flags1) != re_ptr->flags1)) return FALSE;
+ if (re_ptr->flags2 && ((re_ptr->flags2 & r_ptr->flags2) != re_ptr->flags2)) return FALSE;
+ if (re_ptr->flags3 && ((re_ptr->flags3 & r_ptr->flags3) != re_ptr->flags3)) return FALSE;
+ if (re_ptr->flags7 && ((re_ptr->flags7 & r_ptr->flags7) != re_ptr->flags7)) return FALSE;
+ if (re_ptr->flags8 && ((re_ptr->flags8 & r_ptr->flags8) != re_ptr->flags8)) return FALSE;
+ if (re_ptr->flags9 && ((re_ptr->flags9 & r_ptr->flags9) != re_ptr->flags9)) return FALSE;
+
+ /* unwanted flags */
+ if (re_ptr->hflags1 && (re_ptr->hflags1 & r_ptr->flags1)) return FALSE;
+ if (re_ptr->hflags2 && (re_ptr->hflags2 & r_ptr->flags2)) return FALSE;
+ if (re_ptr->hflags3 && (re_ptr->hflags3 & r_ptr->flags3)) return FALSE;
+ if (re_ptr->hflags7 && (re_ptr->hflags7 & r_ptr->flags7)) return FALSE;
+ if (re_ptr->hflags8 && (re_ptr->hflags8 & r_ptr->flags8)) return FALSE;
+ if (re_ptr->hflags9 && (re_ptr->hflags9 & r_ptr->flags9)) return FALSE;
+
+ /* Need good race -- IF races are specified */
+ if (re_ptr->r_char[0])
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (r_ptr->d_char == re_ptr->r_char[i]) ok = TRUE;
+ }
+ if (!ok) return FALSE;
+ }
+ if (re_ptr->nr_char[0])
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (r_ptr->d_char == re_ptr->nr_char[i]) return (FALSE);
+ }
+ }
+
+ /* Passed all tests ? */
+ return TRUE;
+}
+
+/* Choose an ego type */
+int pick_ego_monster(int r_idx)
+{
+ /* Assume no ego */
+ int ego = 0, lvl;
+ int tries = max_re_idx + 10;
+ monster_ego *re_ptr;
+
+ if ((!(dungeon_flags2 & DF2_ELVEN)) && (!(dungeon_flags2 & DF2_DWARVEN)))
+ {
+ /* No townspeople ego */
+ if (!r_info[r_idx].level) return 0;
+
+ /* First are we allowed to find an ego */
+ if (!magik(MEGO_CHANCE)) return 0;
+
+ /* Lets look for one */
+ while (tries--)
+ {
+ /* Pick one */
+ ego = rand_range(1, max_re_idx - 1);
+ re_ptr = &re_info[ego];
+
+ /* No hope so far */
+ if (!mego_ok(r_idx, ego)) continue;
+
+ /* Not too much OoD */
+ lvl = r_info[r_idx].level;
+ MODIFY(lvl, re_ptr->level, 0);
+ lvl -= ((dun_level / 2) + (rand_int(dun_level / 2)));
+ if (lvl < 1) lvl = 1;
+ if (rand_int(lvl)) continue;
+
+ /* Each ego types have a rarity */
+ if (rand_int(re_ptr->rarity)) continue;
+
+ /* We finally got one ? GREAT */
+ return ego;
+ }
+ }
+ /* Bypass restrictions for themed townspeople */
+ else
+ {
+ if (dungeon_flags2 & DF2_ELVEN)
+ ego = test_mego_name("Elven");
+ else if (dungeon_flags2 & DF2_DWARVEN)
+ ego = test_mego_name("Dwarven");
+
+ if (mego_ok(r_idx, ego))
+ return ego;
+ }
+
+ /* Found none ? so sad, well no ego for the time being */
+ return 0;
+}
+
+/*
+ * Return a (monster_race*) with the combination of the monster
+ * properties and the ego type
+ */
+monster_race* race_info_idx(int r_idx, int ego)
+{
+ static monster_race race;
+ monster_ego *re_ptr = &re_info[ego];
+ monster_race *r_ptr = &r_info[r_idx], *nr_ptr = &race;
+ int i;
+
+ /* No work needed */
+ if (!ego) return r_ptr;
+
+ /* Copy the base monster */
+ COPY(nr_ptr, r_ptr, monster_race);
+
+ /* Adjust the values */
+ for (i = 0; i < 4; i++)
+ {
+ s32b j, k;
+
+ j = modify_aux(nr_ptr->blow[i].d_dice, re_ptr->blow[i].d_dice, re_ptr->blowm[i][0]);
+ if (j < 0) j = 0;
+ k = modify_aux(nr_ptr->blow[i].d_side, re_ptr->blow[i].d_side, re_ptr->blowm[i][1]);
+ if (k < 0) k = 0;
+
+ nr_ptr->blow[i].d_dice = j;
+ nr_ptr->blow[i].d_side = k;
+
+ if (re_ptr->blow[i].method) nr_ptr->blow[i].method = re_ptr->blow[i].method;
+ if (re_ptr->blow[i].effect) nr_ptr->blow[i].effect = re_ptr->blow[i].effect;
+ }
+
+ MODIFY(nr_ptr->hdice, re_ptr->hdice, 1);
+ MODIFY(nr_ptr->hside, re_ptr->hside, 1);
+
+ MODIFY(nr_ptr->ac, re_ptr->ac, 0);
+
+ MODIFY(nr_ptr->sleep, re_ptr->sleep, 0);
+
+ MODIFY(nr_ptr->aaf, re_ptr->aaf, 1);
+ MODIFY(nr_ptr->speed, re_ptr->speed, 50);
+ MODIFY(nr_ptr->mexp, re_ptr->mexp, 0);
+
+ MODIFY(nr_ptr->weight, re_ptr->weight, 10);
+
+ nr_ptr->freq_inate = (nr_ptr->freq_inate > re_ptr->freq_inate)
+ ? nr_ptr->freq_inate : re_ptr->freq_inate;
+ nr_ptr->freq_spell = (nr_ptr->freq_spell > re_ptr->freq_spell)
+ ? nr_ptr->freq_spell : re_ptr->freq_spell;
+
+ MODIFY(nr_ptr->level, re_ptr->level, 1);
+
+ /* Take off some flags */
+ nr_ptr->flags1 &= ~(re_ptr->nflags1);
+ nr_ptr->flags2 &= ~(re_ptr->nflags2);
+ nr_ptr->flags3 &= ~(re_ptr->nflags3);
+ nr_ptr->flags4 &= ~(re_ptr->nflags4);
+ nr_ptr->flags5 &= ~(re_ptr->nflags5);
+ nr_ptr->flags6 &= ~(re_ptr->nflags6);
+ nr_ptr->flags7 &= ~(re_ptr->nflags7);
+ nr_ptr->flags8 &= ~(re_ptr->nflags8);
+ nr_ptr->flags9 &= ~(re_ptr->nflags9);
+
+ /* Add some flags */
+ nr_ptr->flags1 |= re_ptr->mflags1;
+ nr_ptr->flags2 |= re_ptr->mflags2;
+ nr_ptr->flags3 |= re_ptr->mflags3;
+ nr_ptr->flags4 |= re_ptr->mflags4;
+ nr_ptr->flags5 |= re_ptr->mflags5;
+ nr_ptr->flags6 |= re_ptr->mflags6;
+ nr_ptr->flags7 |= re_ptr->mflags7;
+ nr_ptr->flags8 |= re_ptr->mflags8;
+ nr_ptr->flags9 |= re_ptr->mflags9;
+
+ /* Change the char/attr is needed */
+ if (re_ptr->d_char != MEGO_CHAR_ANY)
+ {
+ nr_ptr->d_char = re_ptr->d_char;
+ nr_ptr->x_char = re_ptr->d_char;
+ }
+ if (re_ptr->d_attr != MEGO_CHAR_ANY)
+ {
+ nr_ptr->d_attr = re_ptr->d_attr;
+ nr_ptr->x_attr = re_ptr->d_attr;
+ }
+
+ /* And finanly return a pointer to a fully working monster race */
+ return nr_ptr;
+}
+
+static cptr horror_desc[MAX_HORROR] =
+{
+ "abominable",
+ "abysmal",
+ "appalling",
+ "baleful",
+ "blasphemous",
+
+ "disgusting",
+ "dreadful",
+ "filthy",
+ "grisly",
+ "hideous",
+
+ "hellish",
+ "horrible",
+ "infernal",
+ "loathsome",
+ "nightmarish",
+
+ "repulsive",
+ "sacrilegious",
+ "terrible",
+ "unclean",
+ "unspeakable",
+};
+
+static cptr funny_desc[MAX_FUNNY] =
+{
+ "silly",
+ "hilarious",
+ "absurd",
+ "insipid",
+ "ridiculous",
+
+ "laughable",
+ "ludicrous",
+ "far-out",
+ "groovy",
+ "postmodern",
+
+ "fantastic",
+ "dadaistic",
+ "cubistic",
+ "cosmic",
+ "awesome",
+
+ "incomprehensible",
+ "fabulous",
+ "amazing",
+ "incredible",
+ "chaotic",
+
+ "wild",
+ "preposterous",
+};
+
+static cptr funny_comments[MAX_COMMENT] =
+{
+ "Wow, cosmic, man!",
+ "Rad!",
+ "Groovy!",
+ "Cool!",
+ "Far out!"
+};
+
+
+int get_wilderness_flag(void)
+{
+ int x = p_ptr->wilderness_x;
+ int y = p_ptr->wilderness_y;
+
+ if (dun_level)
+ return (RF8_DUNGEON);
+ else
+ return (1L << wf_info[wild_map[y][x].feat].terrain_idx);
+}
+
+
+/*
+ * Delete a monster by index.
+ *
+ * When a monster is deleted, all of its objects are deleted.
+ */
+void delete_monster_idx(int i)
+{
+ int x, y, j;
+
+ monster_type *m_ptr = &m_list[i];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ bool_ had_lite = FALSE;
+ ;
+
+
+ /* Get location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Hack -- Reduce the racial counter */
+ r_ptr->cur_num--;
+ r_ptr->on_saved = FALSE;
+
+ /* Hack -- count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro--;
+
+ /* XXX XXX XXX remove monster light source */
+ if (r_ptr->flags9 & (RF9_HAS_LITE)) had_lite = TRUE;
+
+
+ /* Hack -- remove target monster */
+ if (i == target_who) target_who = 0;
+
+ /* Hack -- remove tracked monster */
+ if (i == health_who) health_track(0);
+
+ /* Hack -- remove tracked monster */
+ if (i == p_ptr->control) p_ptr->control = 0;
+
+ for (j = m_max - 1; j >= 1; j--)
+ {
+ /* Access the monster */
+ monster_type *t_ptr = &m_list[j];
+
+ /* Ignore "dead" monsters */
+ if (!t_ptr->r_idx) continue;
+
+ if (t_ptr->target == i) t_ptr->target = -1;
+ }
+
+ /* Monster is gone */
+ cave[y][x].m_idx = 0;
+
+
+ /* Delete objects */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Hack -- efficiency */
+ o_ptr->held_m_idx = 0;
+
+ if ( p_ptr->preserve )
+ {
+ /* Hack -- Preserve unknown artifacts */
+ if (artifact_p(o_ptr) && !object_known_p(o_ptr))
+ {
+ /* Mega-Hack -- Preserve the artifact */
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = FALSE;
+ }
+ else if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[o_ptr->k_idx].artifact = FALSE;
+ }
+ else
+ {
+ a_info[o_ptr->name1].cur_num = 0;
+ }
+ }
+ }
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+ }
+
+ /* Delete mind & special race if needed */
+ if (m_ptr->sr_ptr)
+ KILL(m_ptr->sr_ptr, monster_race);
+ if (m_ptr->mind)
+ KILL(m_ptr->mind, monster_mind);
+
+ /* Wipe the Monster */
+ m_ptr = WIPE(m_ptr, monster_type);
+
+ /* Count monsters */
+ m_cnt--;
+
+ /* Do we survided our fate ? */
+ if ((dungeon_type == DUNGEON_DEATH) && (!m_cnt))
+ {
+ msg_print("You overcome your fate, mortal!");
+
+ dungeon_type = DUNGEON_WILDERNESS;
+ dun_level = 0;
+
+ p_ptr->leaving = TRUE;
+ }
+
+ /* Update monster light */
+ if (had_lite) p_ptr->update |= (PU_MON_LITE);
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Visual update */
+ lite_spot(y, x);
+}
+
+
+/*
+ * Delete the monster, if any, at a given location
+ */
+void delete_monster(int y, int x)
+{
+ cave_type *c_ptr;
+
+ /* Paranoia */
+ if (!in_bounds(y, x)) return;
+
+ /* Check the grid */
+ c_ptr = &cave[y][x];
+
+ /* Delete the monster (if any) */
+ if (c_ptr->m_idx) delete_monster_idx(c_ptr->m_idx);
+}
+
+
+/*
+ * Move an object from index i1 to index i2 in the object list
+ */
+static void compact_monsters_aux(int i1, int i2)
+{
+ int y, x, j;
+
+ cave_type *c_ptr;
+
+ monster_type *m_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Do nothing */
+ if (i1 == i2) return;
+
+
+ /* Old monster */
+ m_ptr = &m_list[i1];
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Update the cave */
+ c_ptr->m_idx = i2;
+
+ /* Repair objects being carried by monster */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Reset monster pointer */
+ o_ptr->held_m_idx = i2;
+ }
+
+ /* Hack -- Update the control */
+ if (p_ptr->control == i1) p_ptr->control = i2;
+
+ /* Hack -- Update the doppleganger */
+ if (doppleganger == i1) doppleganger = i2;
+
+ /* Hack -- Update the target */
+ if (target_who == i1) target_who = i2;
+
+ /* Hack -- Update the health bar */
+ if (health_who == i1) health_track(i2);
+
+ for (j = m_max - 1; j >= 1; j--)
+ {
+ /* Access the monster */
+ monster_type *t_ptr = &m_list[j];
+
+ /* Ignore "dead" monsters */
+ if (!t_ptr->r_idx) continue;
+
+ if (t_ptr->target == i1) t_ptr->target = i2;
+ }
+
+ /* Structure copy */
+ COPY(&m_list[i2], &m_list[i1], monster_type);
+
+ /* Delete mind & special race if needed */
+ if (m_list[i1].sr_ptr)
+ KILL(m_list[i1].sr_ptr, monster_race);
+ if (m_list[i1].mind)
+ KILL(m_list[i1].mind, monster_mind);
+
+ /* Wipe the hole */
+ m_ptr = WIPE(&m_list[i1], monster_type);
+}
+
+
+/*
+ * Compact and Reorder the monster list
+ *
+ * This function can be very dangerous, use with caution!
+ *
+ * When actually "compacting" monsters, we base the saving throw
+ * on a combination of monster level, distance from player, and
+ * current "desperation".
+ *
+ * After "compacting" (if needed), we "reorder" the monsters into a more
+ * compact order, and we reset the allocation info, and the "live" array.
+ */
+void compact_monsters(int size)
+{
+ int i, num, cnt;
+ int cur_lev, cur_dis, chance;
+
+
+ /* Message (only if compacting) */
+ if (size) msg_print("Compacting monsters...");
+
+
+ /* Compact at least 'size' objects */
+ for (num = 0, cnt = 1; num < size; cnt++)
+ {
+ /* Get more vicious each iteration */
+ cur_lev = 5 * cnt;
+
+ /* Get closer each iteration */
+ cur_dis = 5 * (20 - cnt);
+
+ /* Check all the monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- skip "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Hack -- High level monsters start out "immune" */
+ if (m_ptr->level > cur_lev) continue;
+
+ /* Ignore nearby monsters */
+ if ((cur_dis > 0) && (m_ptr->cdis < cur_dis)) continue;
+
+ /* Saving throw chance */
+ chance = 90;
+
+ /* Only compact "Quest" Monsters in emergencies */
+ if ((m_ptr->mflag & MFLAG_QUEST) && (cnt < 1000)) chance = 100;
+
+ /* Try not to compact Unique Monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) chance = 99;
+
+ /* All monsters get a saving throw */
+ if (rand_int(100) < chance) continue;
+
+ /* Delete the monster */
+ delete_monster_idx(i);
+
+ /* Count the monster */
+ num++;
+ }
+ }
+
+
+ /* Excise dead monsters (backwards!) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Get the i'th monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Skip real monsters */
+ if (m_ptr->r_idx) continue;
+
+ /* Move last monster into open hole */
+ compact_monsters_aux(m_max - 1, i);
+
+ /* Compress "m_max" */
+ m_max--;
+ }
+}
+
+/*
+ * Delete/Remove all the monsters when the player leaves the level
+ *
+ * This is an efficient method of simulating multiple calls to the
+ * "delete_monster()" function, with no visual effects.
+ */
+void wipe_m_list(void)
+{
+ int i;
+
+ /* Delete all the monsters */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Mega-Hack -- preserve Unique's XXX XXX XXX */
+
+ /* Hack -- Reduce the racial counter */
+ r_ptr->cur_num--;
+
+ /* Monster is gone */
+ cave[m_ptr->fy][m_ptr->fx].m_idx = 0;
+
+ /* Delete mind & special race if needed */
+ if (m_ptr->sr_ptr)
+ KILL(m_ptr->sr_ptr, monster_race);
+ if (m_ptr->mind)
+ KILL(m_ptr->mind, monster_mind);
+
+
+ /* Wipe the Monster */
+ m_ptr = WIPE(m_ptr, monster_type);
+ }
+
+ /* Reset "m_max" */
+ m_max = 1;
+
+ /* Reset "m_cnt" */
+ m_cnt = 0;
+
+ /* Hack -- reset "reproducer" count */
+ num_repro = 0;
+
+ /* Hack -- no more target */
+ target_who = 0;
+
+ /* Reset control */
+ p_ptr->control = 0;
+
+ /* Hack -- no more tracking */
+ health_track(0);
+}
+
+
+/*
+ * Acquires and returns the index of a "free" monster.
+ *
+ * This routine should almost never fail, but it *can* happen.
+ */
+s16b m_pop(void)
+{
+ int i;
+
+
+ /* Normal allocation */
+ if (m_max < max_m_idx)
+ {
+ /* Access the next hole */
+ i = m_max;
+
+ /* Expand the array */
+ m_max++;
+
+ /* Count monsters */
+ m_cnt++;
+
+ /* Return the index */
+ return (i);
+ }
+
+
+ /* Recycle dead monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr;
+
+ /* Acquire monster */
+ m_ptr = &m_list[i];
+
+ /* Skip live monsters */
+ if (m_ptr->r_idx) continue;
+
+ /* Count monsters */
+ m_cnt++;
+
+ /* Use this monster */
+ return (i);
+ }
+
+
+ /* Warn the player (except during dungeon creation) */
+ if (character_dungeon) msg_print("Too many monsters!");
+
+ /* Try not to crash */
+ return (0);
+}
+
+
+
+
+/*
+ * Apply a "monster restriction function" to the "monster allocation table"
+ */
+errr get_mon_num_prep(void)
+{
+ int i;
+
+ /* Scan the allocation table */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Get the entry */
+ alloc_entry *entry = &alloc_race_table[i];
+
+ /* Accept monsters which pass the restriction, if any */
+ if ((!get_mon_num_hook || (*get_mon_num_hook)(entry->index)) &&
+ (!get_mon_num2_hook || (*get_mon_num2_hook)(entry->index)))
+ {
+ /* Accept this monster */
+ entry->prob2 = entry->prob1;
+ }
+
+ /* Do not use this monster */
+ else
+ {
+ /* Decline this monster */
+ entry->prob2 = 0;
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Some dungeon types restrict the possible monsters.
+ * Return TRUE is the monster is OK and FALSE otherwise
+ */
+bool_ apply_rule(monster_race *r_ptr, byte rule)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ if (d_ptr->rules[rule].mode == DUNGEON_MODE_NONE)
+ {
+ return TRUE;
+ }
+ else if ((d_ptr->rules[rule].mode == DUNGEON_MODE_AND) || (d_ptr->rules[rule].mode == DUNGEON_MODE_NAND))
+ {
+ int a;
+
+ if (d_ptr->rules[rule].mflags1)
+ {
+ if ((d_ptr->rules[rule].mflags1 & r_ptr->flags1) != d_ptr->rules[rule].mflags1)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags2)
+ {
+ if ((d_ptr->rules[rule].mflags2 & r_ptr->flags2) != d_ptr->rules[rule].mflags2)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags3)
+ {
+ if ((d_ptr->rules[rule].mflags3 & r_ptr->flags3) != d_ptr->rules[rule].mflags3)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags4)
+ {
+ if ((d_ptr->rules[rule].mflags4 & r_ptr->flags4) != d_ptr->rules[rule].mflags4)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags5)
+ {
+ if ((d_ptr->rules[rule].mflags5 & r_ptr->flags5) != d_ptr->rules[rule].mflags5)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags6)
+ {
+ if ((d_ptr->rules[rule].mflags6 & r_ptr->flags6) != d_ptr->rules[rule].mflags6)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags7)
+ {
+ if ((d_ptr->rules[rule].mflags7 & r_ptr->flags7) != d_ptr->rules[rule].mflags7)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags8)
+ {
+ if ((d_ptr->rules[rule].mflags8 & r_ptr->flags8) != d_ptr->rules[rule].mflags8)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags9)
+ {
+ if ((d_ptr->rules[rule].mflags9 & r_ptr->flags9) != d_ptr->rules[rule].mflags9)
+ return FALSE;
+ }
+ for (a = 0; a < 5; a++)
+ {
+ if (d_ptr->rules[rule].r_char[a] && (d_ptr->rules[rule].r_char[a] != r_ptr->d_char)) return FALSE;
+ }
+
+ /* All checks passed ? lets go ! */
+ return TRUE;
+ }
+ else if ((d_ptr->rules[rule].mode == DUNGEON_MODE_OR) || (d_ptr->rules[rule].mode == DUNGEON_MODE_NOR))
+ {
+ int a;
+
+ if (d_ptr->rules[rule].mflags1 && (r_ptr->flags1 & d_ptr->rules[rule].mflags1)) return TRUE;
+ if (d_ptr->rules[rule].mflags2 && (r_ptr->flags2 & d_ptr->rules[rule].mflags2)) return TRUE;
+ if (d_ptr->rules[rule].mflags3 && (r_ptr->flags3 & d_ptr->rules[rule].mflags3)) return TRUE;
+ if (d_ptr->rules[rule].mflags4 && (r_ptr->flags4 & d_ptr->rules[rule].mflags4)) return TRUE;
+ if (d_ptr->rules[rule].mflags5 && (r_ptr->flags5 & d_ptr->rules[rule].mflags5)) return TRUE;
+ if (d_ptr->rules[rule].mflags6 && (r_ptr->flags6 & d_ptr->rules[rule].mflags6)) return TRUE;
+ if (d_ptr->rules[rule].mflags7 && (r_ptr->flags7 & d_ptr->rules[rule].mflags7)) return TRUE;
+ if (d_ptr->rules[rule].mflags8 && (r_ptr->flags8 & d_ptr->rules[rule].mflags8)) return TRUE;
+ if (d_ptr->rules[rule].mflags9 && (r_ptr->flags9 & d_ptr->rules[rule].mflags9)) return TRUE;
+
+ for (a = 0; a < 5; a++)
+ if (d_ptr->rules[rule].r_char[a] == r_ptr->d_char) return TRUE;
+
+ /* All checks failled ? Sorry ... */
+ return FALSE;
+ }
+
+ /* Should NEVER happen */
+ return FALSE;
+}
+
+bool_ restrict_monster_to_dungeon(int r_idx)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Select a random rule */
+ byte rule = d_ptr->rule_percents[rand_int(100)];
+
+ /* Apply the rule */
+ bool_ rule_ret = apply_rule(r_ptr, rule);
+
+ /* Should the rule be right or not ? */
+ if ((d_ptr->rules[rule].mode == DUNGEON_MODE_NAND) || (d_ptr->rules[rule].mode == DUNGEON_MODE_NOR)) rule_ret = !rule_ret;
+
+ /* Rule ok ? */
+ if (rule_ret) return TRUE;
+
+ /* Not allowed */
+ return FALSE;
+}
+
+/* Ugly hack, let summon unappropriate monsters */
+bool_ summon_hack = FALSE;
+
+/*
+ * Choose a monster race that seems "appropriate" to the given level
+ *
+ * This function uses the "prob2" field of the "monster allocation table",
+ * and various local information, to calculate the "prob3" field of the
+ * same table, which is then used to choose an "appropriate" monster, in
+ * a relatively efficient manner.
+ *
+ * Note that "town" monsters will *only* be created in the town, and
+ * "normal" monsters will *never* be created in the town, unless the
+ * "level" is "modified", for example, by polymorph or summoning.
+ *
+ * There is a small chance (1/50) of "boosting" the given depth by
+ * a small amount (up to four levels), except in the town.
+ *
+ * It is (slightly) more likely to acquire a monster of the given level
+ * than one of a lower level. This is done by choosing several monsters
+ * appropriate to the given level and keeping the "hardest" one.
+ *
+ * Note that if no monsters are "appropriate", then this function will
+ * fail, and return zero, but this should *almost* never happen.
+ */
+s16b get_mon_num(int level)
+{
+ int i, j, p;
+
+ int r_idx;
+
+ long value, total;
+
+ monster_race *r_ptr;
+
+ alloc_entry *table = alloc_race_table;
+
+ int in_tome;
+
+ /* Boost the level */
+ if (level > 0)
+ {
+ /* Occasional "nasty" monster */
+ if (rand_int(NASTY_MON) == 0)
+ {
+ /* Pick a level bonus */
+ int d = level / 4 + 2;
+
+ /* Boost the level */
+ level += ((d < 5) ? d : 5);
+ }
+
+ /* Occasional "nasty" monster */
+ if (rand_int(NASTY_MON) == 0)
+ {
+ /* Pick a level bonus */
+ int d = level / 4 + 2;
+
+ /* Boost the level */
+ level += ((d < 5) ? d : 5);
+ }
+ }
+
+
+ /* Reset total */
+ total = 0L;
+
+ /* Check whether this is ToME or a module */
+ in_tome = strcmp(game_module, "ToME") == 0;
+
+ /* Process probabilities */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Monsters are sorted by depth */
+ if (table[i].level > level) break;
+
+ /* Default */
+ table[i].prob3 = 0;
+
+ /* Access the "r_idx" of the chosen monster */
+ r_idx = table[i].index;
+
+ /* Access the actual race */
+ r_ptr = &r_info[r_idx];
+
+ /* Hack -- "unique" monsters must be "unique" */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) &&
+ (r_ptr->cur_num >= r_ptr->max_num))
+ {
+ continue;
+ }
+
+ /* Depth Monsters never appear out of depth */
+ if ((r_ptr->flags1 & (RF1_FORCE_DEPTH)) && (r_ptr->level > dun_level))
+ {
+ continue;
+ }
+
+ /* Depth Monsters never appear out of their depth */
+ if ((r_ptr->flags9 & (RF9_ONLY_DEPTH)) && (r_ptr->level != dun_level))
+ {
+ continue;
+ }
+
+ if(in_tome)
+ {
+ /* Zangbandish monsters not allowed */
+ if (r_ptr->flags8 & RF8_ZANGBAND) continue;
+
+ /* Lovecraftian monsters not allowed */
+ if (r_ptr->flags8 & RF8_CTHANGBAND) continue;
+ }
+
+ /* Joke monsters allowed ? or not ? */
+ if (!joke_monsters && (r_ptr->flags8 & RF8_JOKEANGBAND)) continue;
+
+ /* Some dungeon types restrict the possible monsters */
+ if (!summon_hack && !restrict_monster_to_dungeon(r_idx) && dun_level) continue;
+
+ /* Accept */
+ table[i].prob3 = table[i].prob2;
+
+ /* Total */
+ total += table[i].prob3;
+ }
+
+ /* No legal monsters */
+ if (total <= 0) return (0);
+
+
+ /* Pick a monster */
+ value = rand_int(total);
+
+ /* Find the monster */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+
+ /* Power boost */
+ p = rand_int(100);
+
+ /* Try for a "harder" monster once (50%) or twice (10%) */
+ if (p < 60)
+ {
+ /* Save old */
+ j = i;
+
+ /* Pick a monster */
+ value = rand_int(total);
+
+ /* Find the monster */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+ /* Keep the "best" one */
+ if (table[i].level < table[j].level) i = j;
+ }
+
+ /* Try for a "harder" monster twice (10%) */
+ if (p < 10)
+ {
+ /* Save old */
+ j = i;
+
+ /* Pick a monster */
+ value = rand_int(total);
+
+ /* Find the monster */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+ /* Keep the "best" one */
+ if (table[i].level < table[j].level) i = j;
+ }
+
+
+ /* Result */
+ return (table[i].index);
+}
+
+
+
+
+
+/*
+ * Build a string describing a monster in some way.
+ *
+ * We can correctly describe monsters based on their visibility.
+ * We can force all monsters to be treated as visible or invisible.
+ * We can build nominatives, objectives, possessives, or reflexives.
+ * We can selectively pronominalize hidden, visible, or all monsters.
+ * We can use definite or indefinite descriptions for hidden monsters.
+ * We can use definite or indefinite descriptions for visible monsters.
+ *
+ * Pronominalization involves the gender whenever possible and allowed,
+ * so that by cleverly requesting pronominalization / visibility, you
+ * can get messages like "You hit someone. She screams in agony!".
+ *
+ * Reflexives are acquired by requesting Objective plus Possessive.
+ *
+ * If no m_ptr arg is given (?), the monster is assumed to be hidden,
+ * unless the "Assume Visible" mode is requested.
+ *
+ * If no r_ptr arg is given, it is extracted from m_ptr and r_info
+ * If neither m_ptr nor r_ptr is given, the monster is assumed to
+ * be neuter, singular, and hidden (unless "Assume Visible" is set),
+ * in which case you may be in trouble... :-)
+ *
+ * I am assuming that no monster name is more than 70 characters long,
+ * so that "char desc[80];" is sufficiently large for any result.
+ *
+ * Mode Flags:
+ * 0x01 --> Objective (or Reflexive)
+ * 0x02 --> Possessive (or Reflexive)
+ * 0x04 --> Use indefinites for hidden monsters ("something")
+ * 0x08 --> Use indefinites for visible monsters ("a kobold")
+ * 0x10 --> Pronominalize hidden monsters
+ * 0x20 --> Pronominalize visible monsters
+ * 0x40 --> Assume the monster is hidden
+ * 0x80 --> Assume the monster is visible
+ * 0x100 --> Ignore insanity
+ *
+ * Useful Modes:
+ * 0x00 --> Full nominative name ("the kobold") or "it"
+ * 0x04 --> Full nominative name ("the kobold") or "something"
+ * 0x80 --> Genocide resistance name ("the kobold")
+ * 0x88 --> Killing name ("a kobold")
+ * 0x22 --> Possessive, genderized if visible ("his") or "its"
+ * 0x23 --> Reflexive, genderized if visible ("himself") or "itself"
+ */
+void monster_desc(char *desc, monster_type *m_ptr, int mode)
+{
+ cptr res;
+ monster_race *r_ptr = race_inf(m_ptr);
+ cptr b_name = (r_name + r_ptr->name);
+ char silly_name[80], name[100];
+ bool_ seen, pron;
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+
+ if (m_ptr->ego)
+ {
+ if (re_info[m_ptr->ego].before) sprintf(name, "%s %s", re_name + re_info[m_ptr->ego].name, b_name);
+ else sprintf(name, "%s %s", b_name, re_name + re_info[m_ptr->ego].name);
+ }
+ else
+ {
+ sprintf(name, "%s", b_name);
+ }
+
+ /*
+ * Are we hallucinating? (Idea from Nethack...)
+ * insanity roll added by pelpel
+ */
+ if (!(mode & 0x100) && (p_ptr->image || (rand_int(300) < insanity)))
+ {
+ if (rand_int(2) == 0)
+ {
+ monster_race *hallu_race;
+
+ do
+ {
+ hallu_race = &r_info[randint(max_r_idx - 2)];
+ }
+ while (hallu_race->flags1 & RF1_UNIQUE);
+
+ strcpy(silly_name, (r_name + hallu_race->name));
+ }
+ else
+ {
+ get_rnd_line("silly.txt", silly_name);
+ }
+
+ strcpy(name, silly_name);
+ }
+
+ /* Can we "see" it (exists + forced, or visible + not unforced) */
+ seen = (m_ptr && ((mode & 0x80) || (!(mode & 0x40) && m_ptr->ml)));
+
+ /* Sexed Pronouns (seen and allowed, or unseen and allowed) */
+ pron = (m_ptr && ((seen && (mode & 0x20)) || (!seen && (mode & 0x10))));
+
+
+ /* First, try using pronouns, or describing hidden monsters */
+ if (!seen || pron)
+ {
+ /* an encoding of the monster "sex" */
+ int kind = 0x00;
+
+ /* Extract the gender (if applicable) */
+ if (r_ptr->flags1 & (RF1_FEMALE)) kind = 0x20;
+ else if (r_ptr->flags1 & (RF1_MALE)) kind = 0x10;
+
+ /* Ignore the gender (if desired) */
+ if (!m_ptr || !pron) kind = 0x00;
+
+
+ /* Assume simple result */
+ res = "it";
+
+ /* Brute force: split on the possibilities */
+ switch (kind | (mode & 0x07))
+ {
+ /* Neuter, or unknown */
+ case 0x00:
+ res = "it";
+ break;
+ case 0x01:
+ res = "it";
+ break;
+ case 0x02:
+ res = "its";
+ break;
+ case 0x03:
+ res = "itself";
+ break;
+ case 0x04:
+ res = "something";
+ break;
+ case 0x05:
+ res = "something";
+ break;
+ case 0x06:
+ res = "something's";
+ break;
+ case 0x07:
+ res = "itself";
+ break;
+
+ /* Male (assume human if vague) */
+ case 0x10:
+ res = "he";
+ break;
+ case 0x11:
+ res = "him";
+ break;
+ case 0x12:
+ res = "his";
+ break;
+ case 0x13:
+ res = "himself";
+ break;
+ case 0x14:
+ res = "someone";
+ break;
+ case 0x15:
+ res = "someone";
+ break;
+ case 0x16:
+ res = "someone's";
+ break;
+ case 0x17:
+ res = "himself";
+ break;
+
+ /* Female (assume human if vague) */
+ case 0x20:
+ res = "she";
+ break;
+ case 0x21:
+ res = "her";
+ break;
+ case 0x22:
+ res = "her";
+ break;
+ case 0x23:
+ res = "herself";
+ break;
+ case 0x24:
+ res = "someone";
+ break;
+ case 0x25:
+ res = "someone";
+ break;
+ case 0x26:
+ res = "someone's";
+ break;
+ case 0x27:
+ res = "herself";
+ break;
+ }
+
+ /* Copy the result */
+ (void)strcpy(desc, res);
+ }
+
+
+ /* Handle visible monsters, "reflexive" request */
+ else if ((mode & 0x02) && (mode & 0x01))
+ {
+ /* The monster is visible, so use its gender */
+ if (r_ptr->flags1 & (RF1_FEMALE)) strcpy(desc, "herself");
+ else if (r_ptr->flags1 & (RF1_MALE)) strcpy(desc, "himself");
+ else strcpy(desc, "itself");
+ }
+
+
+ /* Handle all other visible monster requests */
+ else
+ {
+ /* It could be a Unique */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) && !(p_ptr->image))
+ {
+ /* Start with the name (thus nominative and objective) */
+ (void)strcpy(desc, name);
+ }
+
+ /* It could be an indefinite monster */
+ else if (mode & 0x08)
+ {
+ /* XXX Check plurality for "some" */
+
+ /* Indefinite monsters need an indefinite article */
+ (void)strcpy(desc, is_a_vowel(name[0]) ? "an " : "a ");
+ (void)strcat(desc, name);
+ }
+
+ /* It could be a normal, definite, monster */
+ else
+ {
+ /* Definite monsters need a definite article */
+ if (m_ptr->status >= MSTATUS_PET)
+ (void)strcpy(desc, "your ");
+ else
+ (void)strcpy(desc, "the ");
+
+ (void)strcat(desc, name);
+ }
+
+ /* Handle the Possessive as a special afterthought */
+ if (mode & 0x02)
+ {
+ /* XXX Check for trailing "s" */
+
+ /* Simply append "apostrophe" and "s" */
+ (void)strcat(desc, "'s");
+ }
+ }
+}
+
+void monster_race_desc(char *desc, int r_idx, int ego)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+ cptr b_name = (r_name + r_ptr->name);
+ char name[80];
+
+ if (ego)
+ {
+ if (re_info[ego].before) sprintf(name, "%s %s", re_name + re_info[ego].name, b_name);
+ else sprintf(name, "%s %s", b_name, re_name + re_info[ego].name);
+ }
+ else
+ {
+ sprintf(name, "%s", b_name);
+ }
+
+ /* It could be a Unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ /* Start with the name (thus nominative and objective) */
+ (void)strcpy(desc, name);
+ }
+
+ /* It could be a normal, definite, monster */
+ else
+ {
+ /* Definite monsters need a definite article */
+ (void)strcpy(desc, is_a_vowel(name[0]) ? "an " : "a ");
+
+ (void)strcat(desc, name);
+ }
+}
+
+
+
+/*
+ * Learn about a monster (by "probing" it)
+ */
+void lore_do_probe(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Hack -- Memorize some flags */
+ r_ptr->r_flags1 = r_ptr->flags1;
+ r_ptr->r_flags2 = r_ptr->flags2;
+ r_ptr->r_flags3 = r_ptr->flags3;
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+}
+
+
+/*
+ * Take note that the given monster just dropped some treasure
+ *
+ * Note that learning the "GOOD"/"GREAT" flags gives information
+ * about the treasure (even when the monster is killed for the first
+ * time, such as uniques, and the treasure has not been examined yet).
+ *
+ * This "indirect" method is used to prevent the player from learning
+ * exactly how much treasure a monster can drop from observing only
+ * a single example of a drop. This method actually observes how much
+ * gold and items are dropped, and remembers that information to be
+ * described later by the monster recall code.
+ */
+void lore_treasure(int m_idx, int num_item, int num_gold)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Note the number of things dropped */
+ if (num_item > r_ptr->r_drop_item) r_ptr->r_drop_item = num_item;
+ if (num_gold > r_ptr->r_drop_gold) r_ptr->r_drop_gold = num_gold;
+
+ /* Hack -- memorize the good/great flags */
+ if (r_ptr->flags1 & (RF1_DROP_GOOD)) r_ptr->r_flags1 |= (RF1_DROP_GOOD);
+ if (r_ptr->flags1 & (RF1_DROP_GREAT)) r_ptr->r_flags1 |= (RF1_DROP_GREAT);
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+}
+
+
+
+void sanity_blast(monster_type * m_ptr, bool_ necro)
+{
+ bool_ happened = FALSE;
+ int power = 100;
+
+ if (!necro)
+ {
+ char m_name[80];
+ monster_race *r_ptr;
+
+ if (m_ptr != NULL) r_ptr = race_inf(m_ptr);
+ else return;
+
+ power = (m_ptr->level) + 10;
+
+ if (m_ptr != NULL)
+ {
+ monster_desc(m_name, m_ptr, 0);
+
+ if (!(r_ptr->flags1 & RF1_UNIQUE))
+ {
+ if (r_ptr->flags1 & RF1_FRIENDS)
+ power /= 2;
+ }
+ else power *= 2;
+
+ if (!hack_mind)
+ return ; /* No effect yet, just loaded... */
+
+ if (!(m_ptr->ml))
+ return ; /* Cannot see it for some reason */
+
+ if (!(r_ptr->flags2 & RF2_ELDRITCH_HORROR))
+ return ; /* oops */
+
+
+
+ if ((is_friend(m_ptr) > 0) && (randint(8) != 1))
+ return ; /* Pet eldritch horrors are safe most of the time */
+
+
+ if (randint(power) < p_ptr->skill_sav)
+ {
+ return ; /* Save, no adverse effects */
+ }
+
+
+ if (p_ptr->image)
+ {
+ /* Something silly happens... */
+ msg_format("You behold the %s visage of %s!",
+ funny_desc[(randint(MAX_FUNNY)) - 1], m_name);
+ if (randint(3) == 1)
+ {
+ msg_print(funny_comments[randint(MAX_COMMENT) - 1]);
+ p_ptr->image = (p_ptr->image + randint(m_ptr->level));
+ }
+ return ; /* Never mind; we can't see it clearly enough */
+ }
+
+ /* Something frightening happens... */
+ msg_format("You behold the %s visage of %s!",
+ horror_desc[(randint(MAX_HORROR)) - 1], m_name);
+
+ r_ptr->r_flags2 |= RF2_ELDRITCH_HORROR;
+
+ }
+
+ /* Undead characters are 50% likely to be unaffected */
+ if ((PRACE_FLAG(PR1_UNDEAD)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire")))
+ {
+ if (randint(100) < (25 + (p_ptr->lev))) return;
+ }
+ }
+ else
+ {
+ msg_print("Your sanity is shaken by reading the Necronomicon!");
+ }
+ if (randint(power) < p_ptr->skill_sav) /* Mind blast */
+ {
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+ if ((!p_ptr->resist_chaos) && (randint(3) == 1))
+ {
+ (void) set_image(p_ptr->image + rand_int(250) + 150);
+ }
+ return;
+ }
+
+ if (randint(power) < p_ptr->skill_sav) /* Lose int & wis */
+ {
+ do_dec_stat (A_INT, STAT_DEC_NORMAL);
+ do_dec_stat (A_WIS, STAT_DEC_NORMAL);
+ return;
+ }
+
+
+ if (randint(power) < p_ptr->skill_sav) /* Brain smash */
+ {
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+ if (!p_ptr->free_act)
+ {
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(4) + 4);
+ }
+ while (rand_int(100) > p_ptr->skill_sav)
+ (void)do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ while (rand_int(100) > p_ptr->skill_sav)
+ (void)do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ if (!p_ptr->resist_chaos)
+ {
+ (void) set_image(p_ptr->image + rand_int(250) + 150);
+ }
+ return;
+ }
+
+ if (randint(power) < p_ptr->skill_sav) /* Permanent lose int & wis */
+ {
+ if (dec_stat(A_INT, 10, TRUE)) happened = TRUE;
+ if (dec_stat(A_WIS, 10, TRUE)) happened = TRUE;
+ if (happened)
+ msg_print("You feel much less sane than before.");
+ return;
+ }
+
+
+ if (randint(power) < p_ptr->skill_sav) /* Amnesia */
+ {
+
+ if (lose_all_info())
+ msg_print("You forget everything in your utmost terror!");
+ return;
+ }
+
+ p_ptr->update |= PU_BONUS;
+ handle_stuff();
+}
+
+
+/*
+ * This function updates the monster record of the given monster
+ *
+ * This involves extracting the distance to the player, checking
+ * for visibility (natural, infravision, see-invis, telepathy),
+ * updating the monster visibility flag, redrawing or erasing the
+ * monster when the visibility changes, and taking note of any
+ * "visual" features of the monster (cold-blooded, invisible, etc).
+ *
+ * The only monster fields that are changed here are "cdis" (the
+ * distance from the player), "los" (clearly visible to player),
+ * and "ml" (visible to the player in any way).
+ *
+ * There are a few cases where the calling routine knows that the
+ * distance from the player to the monster has not changed, and so
+ * we have a special parameter "full" to request distance computation.
+ * This lets many calls to this function run very quickly.
+ *
+ * Note that every time a monster moves, we must call this function
+ * for that monster, and update distance. Note that every time the
+ * player moves, we must call this function for every monster, and
+ * update distance. Note that every time the player "state" changes
+ * in certain ways (including "blindness", "infravision", "telepathy",
+ * and "see invisible"), we must call this function for every monster.
+ *
+ * The routines that actually move the monsters call this routine
+ * directly, and the ones that move the player, or notice changes
+ * in the player state, call "update_monsters()".
+ *
+ * Routines that change the "illumination" of grids must also call
+ * this function, since the "visibility" of some monsters may be
+ * based on the illumination of their grid.
+ *
+ * Note that this function is called once per monster every time the
+ * player moves, so it is important to optimize it for monsters which
+ * are far away. Note the optimization which skips monsters which
+ * are far away and were completely invisible last turn.
+ *
+ * Note the optimized "inline" version of the "distance()" function.
+ *
+ * Note that only monsters on the current panel can be "visible",
+ * and then only if they are (1) in line of sight and illuminated
+ * by light or infravision, or (2) nearby and detected by telepathy.
+ *
+ * The player can choose to be disturbed by several things, including
+ * "disturb_move" (monster which is viewable moves in some way), and
+ * "disturb_near" (monster which is "easily" viewable moves in some
+ * way). Note that "moves" includes "appears" and "disappears".
+ *
+ * Note the new "xtra" field which encodes several state flags such
+ * as "detected last turn", and "detected this turn", and "currently
+ * in line of sight", all of which are used for visibility testing.
+ */
+void update_mon(int m_idx, bool_ full)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* The current monster location */
+ int fy = m_ptr->fy;
+ int fx = m_ptr->fx;
+
+ bool_ old_ml = m_ptr->ml;
+
+ /* Seen at all */
+ bool_ flag = FALSE;
+
+ /* Seen by vision */
+ bool_ easy = FALSE;
+
+ /* Seen by telepathy */
+ bool_ hard = FALSE;
+
+ /* Various extra flags */
+ bool_ do_empty_mind = FALSE;
+ bool_ do_weird_mind = FALSE;
+ bool_ do_invisible = FALSE;
+ bool_ do_cold_blood = FALSE;
+
+
+ /* Calculate distance */
+ if (full)
+ {
+ int d, dy, dx;
+
+ /* Distance components */
+ dy = (p_ptr->py > fy) ? (p_ptr->py - fy) : (fy - p_ptr->py);
+ dx = (p_ptr->px > fx) ? (p_ptr->px - fx) : (fx - p_ptr->px);
+
+ /* Approximate distance */
+ d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
+
+ /* Save the distance (in a byte) */
+ m_ptr->cdis = (d < 255) ? d : 255;
+ }
+
+
+ /* Process "distant" monsters */
+ if (m_ptr->cdis > MAX_SIGHT)
+ {
+ /* Ignore unseen monsters */
+ if (!m_ptr->ml) return;
+
+ /* Detected */
+ if (m_ptr->mflag & (MFLAG_MARK)) flag = TRUE;
+ }
+
+
+ /* Process "nearby" monsters on the current "panel" */
+ else if (panel_contains(fy, fx))
+ {
+ /* Normal line of sight, and player is not blind */
+ if (player_has_los_bold(fy, fx) && !p_ptr->blind)
+ {
+ /* Use "infravision" */
+ if (m_ptr->cdis <= (byte)(p_ptr->see_infra))
+ {
+ /* Infravision only works on "warm" creatures */
+ /* Below, we will need to know that infravision failed */
+ if (r_ptr->flags2 & (RF2_COLD_BLOOD)) do_cold_blood = TRUE;
+
+ /* Infravision works */
+ if (!do_cold_blood) easy = flag = TRUE;
+ }
+
+ /* Use "illumination" */
+ if (player_can_see_bold(fy, fx))
+ {
+ /* Take note of invisibility */
+ if (r_ptr->flags2 & (RF2_INVISIBLE)) do_invisible = TRUE;
+
+ /* Visible, or detectable, monsters get seen */
+ if (!do_invisible || p_ptr->see_inv) easy = flag = TRUE;
+ }
+ }
+
+ /* Telepathy can see all "nearby" monsters with "minds" */
+ if (p_ptr->telepathy)
+ {
+ /* Assume we cant see */
+ bool_ can_esp = FALSE;
+
+ /* Different ESP */
+ if ((p_ptr->telepathy & ESP_ORC) && (r_ptr->flags3 & RF3_ORC)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_SPIDER) && (r_ptr->flags7 & RF7_SPIDER)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_TROLL) && (r_ptr->flags3 & RF3_TROLL)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_DRAGON) && (r_ptr->flags3 & RF3_DRAGON)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_GIANT) && (r_ptr->flags3 & RF3_GIANT)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_DEMON) && (r_ptr->flags3 & RF3_DEMON)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_UNDEAD) && (r_ptr->flags3 & RF3_UNDEAD)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_EVIL) && (r_ptr->flags3 & RF3_EVIL)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_ANIMAL) && (r_ptr->flags3 & RF3_ANIMAL)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_THUNDERLORD) && (r_ptr->flags3 & RF3_THUNDERLORD)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_GOOD) && (r_ptr->flags3 & RF3_GOOD)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_NONLIVING) && (r_ptr->flags3 & RF3_NONLIVING)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_UNIQUE) && ((r_ptr->flags1 & RF1_UNIQUE) || (r_ptr->flags3 & RF3_UNIQUE_4))) can_esp = TRUE;
+ if (p_ptr->telepathy & ESP_ALL) can_esp = TRUE;
+
+ /* Only do this when we can really detect monster */
+ if (can_esp)
+ {
+ /* Empty mind, no telepathy */
+ if (r_ptr->flags2 & (RF2_EMPTY_MIND))
+ {
+ do_empty_mind = TRUE;
+ }
+
+ /* Weird mind, occasional telepathy */
+ else if (r_ptr->flags2 & (RF2_WEIRD_MIND))
+ {
+ do_weird_mind = TRUE;
+ if (rand_int(100) < 10)
+ {
+ hard = TRUE;
+ flag = TRUE;
+ }
+ }
+
+ /* Normal mind, allow telepathy */
+ else
+ {
+ hard = TRUE;
+ flag = TRUE;
+ }
+ }
+ }
+
+ /* Apply "detection" spells */
+ if (m_ptr->mflag & (MFLAG_MARK)) flag = TRUE;
+
+ /* Hack -- Wizards have "perfect telepathy" */
+ if (wizard) flag = TRUE;
+ }
+
+
+ /* The monster is now visible */
+ if (flag)
+ {
+ /* It was previously unseen */
+ if (!m_ptr->ml)
+ {
+ /* Mark as visible */
+ m_ptr->ml = TRUE;
+
+ /* Draw the monster */
+ lite_spot(fy, fx);
+
+ /* Update health bar as needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Hack -- Count "fresh" sightings */
+ if (r_ptr->r_sights < MAX_SHORT) r_ptr->r_sights++;
+
+ /* Disturb on appearance */
+ if (disturb_move)
+ {
+ if (disturb_pets || (is_friend(m_ptr) <= 0)) disturb(1, 0);
+ }
+ }
+
+ /* Apply telepathy */
+ if (hard)
+ {
+ /* Hack -- Memorize mental flags */
+ if (r_ptr->flags2 & (RF2_SMART)) r_ptr->r_flags2 |= (RF2_SMART);
+ if (r_ptr->flags2 & (RF2_STUPID)) r_ptr->r_flags2 |= (RF2_STUPID);
+ }
+
+ /* Memorize various observable flags */
+ if (do_empty_mind) r_ptr->r_flags2 |= (RF2_EMPTY_MIND);
+ if (do_weird_mind) r_ptr->r_flags2 |= (RF2_WEIRD_MIND);
+ if (do_cold_blood) r_ptr->r_flags2 |= (RF2_COLD_BLOOD);
+ if (do_invisible) r_ptr->r_flags2 |= (RF2_INVISIBLE);
+ }
+
+ /* The monster is not visible */
+ else
+ {
+ /* It was previously seen */
+ if (m_ptr->ml)
+ {
+ /* Mark as not visible */
+ m_ptr->ml = FALSE;
+
+ /* Erase the monster */
+ lite_spot(fy, fx);
+
+ /* Update health bar as needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Disturb on disappearance*/
+ if (disturb_move)
+ {
+ if (disturb_pets || (is_friend(m_ptr) <= 0)) disturb(1, 0);
+ }
+ }
+ }
+
+
+ /* The monster is now easily visible */
+ if (easy)
+ {
+
+ if (m_ptr->ml != old_ml)
+ {
+ if (r_ptr->flags2 & RF2_ELDRITCH_HORROR)
+ {
+ sanity_blast(m_ptr, FALSE);
+ }
+ }
+
+ /* Change */
+ if (!(m_ptr->mflag & (MFLAG_VIEW)))
+ {
+ /* Mark as easily visible */
+ m_ptr->mflag |= (MFLAG_VIEW);
+
+ /* Disturb on appearance */
+ if (disturb_near)
+ {
+ if (disturb_pets || (is_friend(m_ptr) <= 0)) disturb(1, 0);
+ }
+
+ }
+ }
+
+ /* The monster is not easily visible */
+ else
+ {
+ /* Change */
+ if (m_ptr->mflag & (MFLAG_VIEW))
+ {
+ /* Mark as not easily visible */
+ m_ptr->mflag &= ~(MFLAG_VIEW);
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Disturb on disappearance */
+ if (disturb_near)
+ {
+ if (disturb_pets || (is_friend(m_ptr) <= 0)) disturb(1, 0);
+ }
+ }
+ }
+}
+
+
+
+
+/*
+ * This function simply updates all the (non-dead) monsters (see above).
+ */
+void update_monsters(bool_ full)
+{
+ int i;
+
+ /* Update each (live) monster */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Update the monster */
+ update_mon(i, full);
+ }
+}
+
+
+void monster_carry(monster_type *m_ptr, int m_idx, object_type *q_ptr)
+{
+ object_type *o_ptr;
+
+ /* Get new object */
+ int o_idx = o_pop();
+
+ if (o_idx)
+ {
+ /* Get the item */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_ptr->hold_o_idx = o_idx;
+ }
+
+ else
+ {
+ /* Hack -- Preserve artifacts */
+ if (q_ptr->name1)
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ else if (k_info[q_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[q_ptr->k_idx].artifact = 0;
+ }
+ else if (q_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[q_ptr->sval].generated = FALSE;
+ }
+ }
+}
+
+
+static int possible_randart[] =
+{
+ TV_MSTAFF,
+ TV_BOOMERANG,
+ TV_DIGGING,
+ TV_HAFTED,
+ TV_POLEARM,
+ TV_AXE,
+ TV_SWORD,
+ TV_BOOTS,
+ TV_GLOVES,
+ TV_HELM,
+ TV_CROWN,
+ TV_SHIELD,
+ TV_CLOAK,
+ TV_SOFT_ARMOR,
+ TV_HARD_ARMOR,
+ TV_LITE,
+ TV_AMULET,
+ TV_RING,
+ -1,
+};
+
+
+bool_ kind_is_randart(int k_idx)
+{
+ int max;
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (!kind_is_legal(k_idx)) return (FALSE);
+
+ for (max = 0; possible_randart[max] != -1; max++)
+ {
+ if (k_ptr->tval == possible_randart[max]) return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * Attempt to place a monster of the given race at the given location.
+ *
+ * To give the player a sporting chance, any monster that appears in
+ * line-of-sight and is extremely dangerous can be marked as
+ * "FORCE_SLEEP", which will cause them to be placed with low energy,
+ * which often (but not always) lets the player move before they do.
+ *
+ * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters.
+ *
+ * XXX XXX XXX Use special "here" and "dead" flags for unique monsters,
+ * remove old "cur_num" and "max_num" fields.
+ *
+ * XXX XXX XXX Actually, do something similar for artifacts, to simplify
+ * the "preserve" mode, and to make the "what artifacts" flag more useful.
+ *
+ * This is the only function which may place a monster in the dungeon,
+ * except for the savefile loading code.
+ */
+bool_ bypass_r_ptr_max_num = FALSE;
+int place_monster_result = 0;
+bool_ place_monster_one_no_drop = FALSE;
+monster_race *place_monster_one_race = NULL;
+s16b place_monster_one(int y, int x, int r_idx, int ego, bool_ slp, int status)
+{
+ int i;
+ char dummy[5];
+ bool_ add_level = FALSE;
+ int min_level = 0, max_level = 0;
+
+ cave_type *c_ptr;
+
+ monster_type *m_ptr;
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+ cptr name = (r_name + r_ptr->name);
+
+ /* Grab the special race if needed */
+ if (place_monster_one_race)
+ {
+ r_ptr = place_monster_one_race;
+ }
+
+ /* DO NOT PLACE A MONSTER IN THE SMALL SCALE WILDERNESS !!! */
+ if (p_ptr->wild_mode)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Verify location */
+ if (!in_bounds(y, x))
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Require empty space */
+ if (!cave_empty_bold(y, x))
+ {
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster(%d): EMPTY BOLD", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Require no monster free grid, or special permission */
+ if ((cave[y][x].info & CAVE_FREE) && (!m_allow_special[r_idx]))
+ {
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster(%d): CAVE_FREE", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Hack -- no creation on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Nor on the altars */
+ if ((cave[y][x].feat >= FEAT_ALTAR_HEAD)
+ && (cave[y][x].feat <= FEAT_ALTAR_TAIL))
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START)
+ && (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Paranoia */
+ if (!r_idx)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Paranoia */
+ if (!r_ptr->name)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Are we allowed to continue ? */
+ if (process_hooks(HOOK_NEW_MONSTER, "(d)", r_idx))
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Ego Uniques are NOT to be created */
+ if ((r_ptr->flags1 & RF1_UNIQUE) && ego)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Now could we generate an Ego Monster */
+ /* Grab the special race if needed */
+ if (place_monster_one_race)
+ {
+ r_ptr = place_monster_one_race;
+ }
+ else
+ {
+ r_ptr = race_info_idx(r_idx, ego);
+ }
+
+ if (!monster_can_cross_terrain(cave[y][x].feat, r_ptr))
+ {
+ if (wizard) cmsg_print(TERM_L_RED, "WARNING: Refused monster: cannot cross terrain");
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Unallow some uniques to be generated outside of their quests/special levels/dungeons */
+ if ((r_ptr->flags9 & RF9_SPECIAL_GENE) && (!m_allow_special[r_idx]))
+ {
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster(%d): SPECIAL_GENE", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Disallow Spirits in The Void, now this *IS* an ugly hack, I hate to do it ... */
+ if ((r_ptr->flags7 & RF7_SPIRIT) && (dungeon_type != DUNGEON_VOID))
+ {
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster(%d): SPIRIT in non VOID", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Fully forbid it */
+ if (r_ptr->flags9 & RF9_NEVER_GENE)
+ {
+ if (wizard) cmsg_print(TERM_L_RED, "WARNING: Refused monster: NEVER_GENE");
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Hack -- "unique" monsters must be "unique" */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) && (r_ptr->max_num == -1) && (!m_allow_special[r_idx]))
+ {
+ /* Cannot create */
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster %d: unique not unique", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* The monster is already on an unique level */
+ if (r_ptr->on_saved)
+ {
+ if (wizard) cmsg_print(TERM_L_RED, "WARNING: Refused monster: unique already on saved level");
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Hack -- "unique" monsters must be "unique" */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) && (r_ptr->cur_num >= r_ptr->max_num) && (r_ptr->max_num != -1) && (!bypass_r_ptr_max_num))
+ {
+ /* Cannot create */
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster %d: cur_num >= max_num", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Depth monsters may NOT be created out of depth */
+ if ((r_ptr->flags1 & (RF1_FORCE_DEPTH)) && (dun_level < r_ptr->level))
+ {
+ /* Cannot create */
+ if (wizard) cmsg_print(TERM_L_RED, "WARNING: FORCE_DEPTH");
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Powerful monster */
+ if (r_ptr->level > dun_level)
+ {
+ /* Unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ /* Message for cheaters */
+ if ((cheat_hear) || (p_ptr->precognition)) msg_format("Deep Unique (%s).", name);
+
+ /* Boost rating by twice delta-depth */
+ rating += (r_ptr->level - dun_level) * 2;
+ }
+
+ /* Normal monsters */
+ else
+ {
+ /* Message for cheaters */
+ if ((cheat_hear) || (p_ptr->precognition)) msg_format("Deep Monster (%s).", name);
+
+ /* Boost rating by delta-depth */
+ rating += (r_ptr->level - dun_level);
+ }
+ }
+
+ /* Note the monster */
+ else if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ /* Unique monsters induce message */
+ if ((cheat_hear) || (p_ptr->precognition)) msg_format("Unique (%s).", name);
+ }
+
+
+ /* Access the location */
+ c_ptr = &cave[y][x];
+
+ /* Make a new monster */
+ c_ptr->m_idx = m_pop();
+ hack_m_idx_ii = c_ptr->m_idx;
+
+ /* Mega-Hack -- catch "failure" */
+ if (!c_ptr->m_idx)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+
+ /* Get a new monster record */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Save the race */
+ m_ptr->r_idx = r_idx;
+ m_ptr->ego = ego;
+
+ /* No special, no mind */
+ m_ptr->sr_ptr = place_monster_one_race;
+ m_ptr->mind = NULL;
+
+ /* Place the monster at the location */
+ m_ptr->fy = y;
+ m_ptr->fx = x;
+
+ /* No "damage" yet */
+ m_ptr->stunned = 0;
+ m_ptr->confused = 0;
+ m_ptr->monfear = 0;
+
+ /* No target yet */
+ m_ptr->target = -1;
+
+ /* Unknown distance */
+ m_ptr->cdis = 0;
+
+ /* No flags */
+ m_ptr->mflag = 0;
+
+ /* Not visible */
+ m_ptr->ml = FALSE;
+
+ /* No objects yet */
+ m_ptr->hold_o_idx = 0;
+
+ m_ptr->status = status;
+
+ /* Friendly? */
+ if (m_ptr->status < MSTATUS_FRIEND && r_ptr->flags7 & RF7_PET)
+ {
+ m_ptr->status = MSTATUS_FRIEND;
+ }
+ if (m_ptr->status < MSTATUS_NEUTRAL && r_ptr->flags7 & RF7_NEUTRAL)
+ {
+ m_ptr->status = MSTATUS_NEUTRAL;
+ }
+
+ /* Assume no sleeping */
+ m_ptr->csleep = 0;
+
+ /* Enforce sleeping if needed */
+ if (slp && r_ptr->sleep)
+ {
+ int val = r_ptr->sleep;
+ m_ptr->csleep = ((val * 2) + randint(val * 10));
+ }
+
+ /* Generate the monster's inventory(if any) */
+ /* Only if not fated to die */
+ if ((dungeon_type != DUNGEON_DEATH) && (!place_monster_one_no_drop))
+ {
+ bool_ good = (r_ptr->flags1 & (RF1_DROP_GOOD)) ? TRUE : FALSE;
+ bool_ great = (r_ptr->flags1 & (RF1_DROP_GREAT)) ? TRUE : FALSE;
+
+ bool_ do_gold = (!(r_ptr->flags1 & (RF1_ONLY_ITEM)));
+ bool_ do_item = (!(r_ptr->flags1 & (RF1_ONLY_GOLD)));
+ bool_ do_mimic = (r_ptr->flags9 & (RF9_MIMIC));
+ int j;
+
+ int force_coin = get_coin_type(r_ptr);
+
+ int dump_item = 0;
+ int dump_gold = 0;
+ object_type forge;
+ object_type *q_ptr;
+
+ int number = 0;
+
+ /* Average dungeon and monster levels */
+ object_level = (dun_level + r_ptr->level) / 2;
+
+ /* Determine how much we can drop */
+ if ((r_ptr->flags1 & (RF1_DROP_60)) && (rand_int(100) < 60)) number++;
+ if ((r_ptr->flags1 & (RF1_DROP_90)) && (rand_int(100) < 90)) number++;
+ if (r_ptr->flags1 & (RF1_DROP_1D2)) number += damroll(1, 2);
+ if (r_ptr->flags1 & (RF1_DROP_2D2)) number += damroll(2, 2);
+ if (r_ptr->flags1 & (RF1_DROP_3D2)) number += damroll(3, 2);
+ if (r_ptr->flags1 & (RF1_DROP_4D2)) number += damroll(4, 2);
+ if (r_ptr->flags9 & (RF9_MIMIC)) number = 1;
+
+ /* Hack -- handle creeping coins */
+ coin_type = force_coin;
+
+ if (r_ptr->flags7 & RF7_DROP_RANDART)
+ {
+ int tries = 1000;
+ obj_theme theme;
+ int i;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ theme.treasure = 101;
+ theme.combat = 101;
+ theme.magic = 101;
+ theme.tools = 101;
+
+ init_match_theme(theme);
+
+ /* Apply restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Rebuild allocation table */
+ get_obj_num_prep();
+
+ i = 0;
+ while (tries)
+ {
+ tries--;
+ i = get_obj_num(dun_level);
+ if (!i) continue;
+
+ if (!kind_is_randart(i)) continue;
+ break;
+ }
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+
+ if (tries)
+ {
+ object_prep(q_ptr, i);
+ create_artifact(q_ptr, FALSE, TRUE);
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ monster_carry(m_ptr, c_ptr->m_idx, q_ptr);
+ }
+ }
+
+ /* Drop some objects */
+ for (j = 0; j < number; j++)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Make Gold */
+ if ((!do_mimic) && do_gold && (!do_item || (rand_int(100) < 50)))
+ {
+ /* Make some gold */
+ if (!make_gold(q_ptr)) continue;
+
+ /* XXX XXX XXX */
+ dump_gold++;
+ }
+
+ /* Make Object */
+ else
+ {
+ /* Make an object */
+ if (!do_mimic)
+ {
+ if (!make_object(q_ptr, good, great, r_ptr->drops)) continue;
+ }
+ else
+ {
+ /* Try hard for mimics */
+ int tries = 1000;
+
+ while (tries--)
+ {
+ if (make_object(q_ptr, good, great, r_ptr->drops)) break;
+ }
+ /* BAD */
+ if (!tries) continue;
+ }
+
+ /* XXX XXX XXX */
+ dump_item++;
+ }
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+ monster_carry(m_ptr, c_ptr->m_idx, q_ptr);
+ }
+
+ /* Reset the object level */
+ object_level = dun_level;
+
+ /* Reset "coin" type */
+ coin_type = 0;
+ }
+ place_monster_one_no_drop = FALSE;
+
+
+ /* Assign maximal hitpoints */
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP))
+ {
+ m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
+ }
+ else
+ {
+ m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside);
+ }
+
+ /* And start out fully healthy */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Some basic info */
+ for (i = 0; i < 4; i++)
+ {
+ m_ptr->blow[i].method = r_ptr->blow[i].method;
+ m_ptr->blow[i].effect = r_ptr->blow[i].effect;
+ m_ptr->blow[i].d_dice = r_ptr->blow[i].d_dice;
+ m_ptr->blow[i].d_side = r_ptr->blow[i].d_side;
+ }
+ m_ptr->ac = r_ptr->ac;
+ m_ptr->level = r_ptr->level;
+ m_ptr->speed = r_ptr->speed;
+ m_ptr->exp = MONSTER_EXP(m_ptr->level);
+
+ /* Extract the monster base speed */
+ m_ptr->mspeed = m_ptr->speed;
+
+ /* Hack -- small racial variety */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ /* Allow some small variation per monster */
+ i = extract_energy[m_ptr->speed] / 10;
+ if (i) m_ptr->mspeed += rand_spread(0, i);
+ }
+
+
+ if (dungeon_flags2 & DF2_ADJUST_LEVEL_1_2)
+ {
+ min_level = max_level = dun_level / 2;
+ add_level = TRUE;
+ }
+ if (dungeon_flags1 & DF1_ADJUST_LEVEL_1)
+ {
+ if (!min_level) min_level = dun_level;
+ max_level = dun_level;
+ add_level = TRUE;
+ }
+ if (dungeon_flags1 & DF1_ADJUST_LEVEL_2)
+ {
+ if (!min_level) min_level = dun_level * 2;
+ max_level = dun_level * 2;
+ add_level = TRUE;
+ }
+ if (add_level) monster_set_level(c_ptr->m_idx, rand_range(min_level, max_level));
+
+ /* Give a random starting energy */
+ m_ptr->energy = (byte)rand_int(100);
+
+ /* Force monster to wait for player */
+ if (r_ptr->flags1 & (RF1_FORCE_SLEEP))
+ {
+ /* Monster is still being nice */
+ m_ptr->mflag |= (MFLAG_NICE);
+
+ /* Must repair monsters */
+ repair_monsters = TRUE;
+ }
+
+ /* Hack -- see "process_monsters()" */
+ if (c_ptr->m_idx < hack_m_idx)
+ {
+ /* Monster is still being born */
+ m_ptr->mflag |= (MFLAG_BORN);
+ }
+
+
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, TRUE);
+
+
+ /* Hack -- Count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro++;
+
+
+ /* Hack -- Notice new multi-hued monsters */
+ if (r_ptr->flags1 & (RF1_ATTR_MULTI)) shimmer_monsters = TRUE;
+
+ /* Hack -- we need to modify the REAL r_info, not the fake one */
+ r_ptr = &r_info[r_idx];
+
+ /* Hack -- Count the monsters on the level */
+ r_ptr->cur_num++;
+
+ /* Unique monsters on saved levels should be "marked" */
+ if ((r_ptr->flags1 & RF1_UNIQUE) && get_dungeon_save(dummy))
+ {
+ r_ptr->on_saved = TRUE;
+ }
+
+ place_monster_one_race = NULL;
+
+ /* Success */
+ place_monster_result = c_ptr->m_idx;
+ return c_ptr->m_idx;
+}
+
+/*
+ * Maximum size of a group of monsters
+ */
+#define GROUP_MAX 32
+
+
+/*
+ * Attempt to place a "group" of monsters around the given location
+ */
+static bool_ place_monster_group(int y, int x, int r_idx, bool_ slp, int status)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int old, n, i;
+ int total = 0, extra = 0;
+
+ int hack_n = 0;
+
+ byte hack_y[GROUP_MAX];
+ byte hack_x[GROUP_MAX];
+
+
+ /* Pick a group size */
+ total = randint(13);
+
+ /* Hard monsters, small groups */
+ if (r_ptr->level > dun_level)
+ {
+ extra = r_ptr->level - dun_level;
+ extra = 0 - randint(extra);
+ }
+
+ /* Easy monsters, large groups */
+ else if (r_ptr->level < dun_level)
+ {
+ extra = dun_level - r_ptr->level;
+ extra = randint(extra);
+ }
+
+ /* Hack -- limit group reduction */
+ if (extra > 12) extra = 12;
+
+ /* Modify the group size */
+ total += extra;
+
+ /* Minimum size */
+ if (total < 1) total = 1;
+
+ /* Maximum size */
+ if (total > GROUP_MAX) total = GROUP_MAX;
+
+
+ /* Save the rating */
+ old = rating;
+
+ /* Start on the monster */
+ hack_n = 1;
+ hack_x[0] = x;
+ hack_y[0] = y;
+
+ /* Puddle monsters, breadth first, up to total */
+ for (n = 0; (n < hack_n) && (hack_n < total); n++)
+ {
+ /* Grab the location */
+ int hx = hack_x[n];
+ int hy = hack_y[n];
+
+ /* Check each direction, up to total */
+ for (i = 0; (i < 8) && (hack_n < total); i++)
+ {
+ int mx = hx + ddx_ddd[i];
+ int my = hy + ddy_ddd[i];
+
+ /* Walls and Monsters block flow */
+ if (!cave_empty_bold(my, mx)) continue;
+
+ /* Attempt to place another monster */
+ if (place_monster_one(my, mx, r_idx, pick_ego_monster(r_idx), slp, status))
+ {
+ /* Add it to the "hack" set */
+ hack_y[hack_n] = my;
+ hack_x[hack_n] = mx;
+ hack_n++;
+ }
+ }
+ }
+
+ /* Hack -- restore the rating */
+ rating = old;
+
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- help pick an escort type
+ */
+static int place_monster_idx = 0;
+
+/*
+ * Hack -- help pick an escort type
+ */
+static bool_ place_monster_okay(int r_idx)
+{
+ monster_race *r_ptr = &r_info[place_monster_idx];
+
+ monster_race *z_ptr = &r_info[r_idx];
+
+ /* Hack - Escorts have to have the same dungeon flag */
+ if (monster_dungeon(place_monster_idx) != monster_dungeon(r_idx)) return (FALSE);
+
+ /* Require similar "race" */
+ if (z_ptr->d_char != r_ptr->d_char) return (FALSE);
+
+ /* Skip more advanced monsters */
+ if (z_ptr->level > r_ptr->level) return (FALSE);
+
+ /* Skip unique monsters */
+ if (z_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Paranoia -- Skip identical monsters */
+ if (place_monster_idx == r_idx) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Attempt to place a monster of the given race at the given location
+ *
+ * Note that certain monsters are now marked as requiring "friends".
+ * These monsters, if successfully placed, and if the "grp" parameter
+ * is TRUE, will be surrounded by a "group" of identical monsters.
+ *
+ * Note that certain monsters are now marked as requiring an "escort",
+ * which is a collection of monsters with similar "race" but lower level.
+ *
+ * Some monsters induce a fake "group" flag on their escorts.
+ *
+ * Note the "bizarre" use of non-recursion to prevent annoying output
+ * when running a code profiler.
+ *
+ * Note the use of the new "monster allocation table" code to restrict
+ * the "get_mon_num()" function to "legal" escort types.
+ */
+bool_ place_monster_aux(int y, int x, int r_idx, bool_ slp, bool_ grp, int status)
+{
+ int i;
+ monster_race *r_ptr = &r_info[r_idx];
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+
+ /* Place one monster, or fail */
+ if (!place_monster_one(y, x, r_idx, pick_ego_monster(r_idx), slp, status)) return (FALSE);
+
+
+ /* Require the "group" flag */
+ if (!grp) return (TRUE);
+
+
+ /* Friends for certain monsters */
+ if (r_ptr->flags1 & (RF1_FRIENDS))
+ {
+ /* Attempt to place a group */
+ (void)place_monster_group(y, x, r_idx, slp, status);
+ }
+
+
+ /* Escorts for certain monsters */
+ if (r_ptr->flags1 & (RF1_ESCORT))
+ {
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Set the escort index */
+ place_monster_idx = r_idx;
+
+ /* Set the escort hook */
+ get_mon_num_hook = place_monster_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Try to place several "escorts" */
+ for (i = 0; i < 50; i++)
+ {
+ int nx, ny, z, d = 3;
+
+ /* Pick a location */
+ scatter(&ny, &nx, y, x, d);
+
+ /* Require empty grids */
+ if (!cave_empty_bold(ny, nx)) continue;
+
+ set_mon_num2_hook(ny, nx);
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a random race */
+ z = get_mon_num(r_ptr->level);
+
+ /* Reset restriction */
+ get_mon_num2_hook = NULL;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Handle failure */
+ if (!z) break;
+
+ /* Place a single escort */
+ place_monster_one(ny, nx, z, pick_ego_monster(z), slp, status);
+
+ /* Place a "group" of escorts if needed */
+ if ((r_info[z].flags1 & (RF1_FRIENDS)) ||
+ (r_ptr->flags1 & (RF1_ESCORTS)))
+ {
+ /* Place a group of monsters */
+ (void)place_monster_group(ny, nx, z, slp, status);
+ }
+ }
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- attempt to place a monster at the given location
+ *
+ * Attempt to find a monster appropriate to the "monster_level"
+ */
+bool_ place_monster(int y, int x, bool_ slp, bool_ grp)
+{
+ int r_idx;
+
+ /* Set monster restriction */
+ set_mon_num2_hook(y, x);
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a monster */
+ r_idx = get_mon_num(monster_level);
+
+ /* Reset restriction */
+ get_mon_num2_hook = NULL;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Handle failure */
+ if (!r_idx) return (FALSE);
+
+ /* Attempt to place the monster */
+ if (place_monster_aux(y, x, r_idx, slp, grp, MSTATUS_ENEMY)) return (TRUE);
+
+ /* Oops */
+ return (FALSE);
+}
+
+
+#ifdef MONSTER_HORDES
+
+bool_ alloc_horde(int y, int x)
+{
+ int r_idx = 0;
+ monster_race * r_ptr = NULL;
+ monster_type * m_ptr;
+ int attempts = 1000;
+
+ set_mon_num2_hook(y, x);
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ while (--attempts)
+ {
+ /* Pick a monster */
+ r_idx = get_mon_num(monster_level);
+
+ /* Handle failure */
+ if (!r_idx) return (FALSE);
+
+ r_ptr = &r_info[r_idx];
+
+ if (!(r_ptr->flags1 & (RF1_UNIQUE))
+ && !(r_ptr->flags1 & (RF1_ESCORTS)))
+ break;
+ }
+
+ get_mon_num2_hook = NULL;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ if (attempts < 1) return FALSE;
+
+ attempts = 1000;
+
+ while (--attempts)
+ {
+ /* Attempt to place the monster */
+ if (place_monster_aux(y, x, r_idx, FALSE, FALSE, MSTATUS_ENEMY)) break;
+ }
+
+ if (attempts < 1) return FALSE;
+
+
+ m_ptr = &m_list[hack_m_idx_ii];
+
+ summon_kin_type = r_ptr->d_char;
+
+ for (attempts = randint(10) + 5; attempts; attempts--)
+ {
+ (void) summon_specific(m_ptr->fy, m_ptr->fx, dun_level, SUMMON_KIN);
+ }
+
+ return TRUE;
+}
+
+#endif /* MONSTER_HORDES */
+
+/*
+ * Attempt to allocate a random monster in the dungeon.
+ *
+ * Place the monster at least "dis" distance from the player.
+ *
+ * Use "slp" to choose the initial "sleep" status
+ *
+ * Use "monster_level" for the monster level
+ */
+bool_ alloc_monster(int dis, bool_ slp)
+{
+ int y, x;
+ int attempts_left = 10000;
+
+ /* Find a legal, distant, unoccupied, space */
+ while (attempts_left--)
+ {
+ /* Pick a location */
+ y = rand_int(cur_hgt);
+ x = rand_int(cur_wid);
+
+ /* Require empty floor grid (was "naked") */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Accept far away grids */
+ if (distance(y, x, p_ptr->py, p_ptr->px) > dis) break;
+ }
+
+ if (!attempts_left)
+ {
+ if (cheat_xtra || cheat_hear)
+ {
+ msg_print("Warning! Could not allocate a new monster. Small level?");
+ }
+
+ return (FALSE);
+ }
+
+
+#ifdef MONSTER_HORDES
+ if (randint(5000) <= dun_level)
+ {
+ if (alloc_horde(y, x))
+ {
+ if ((cheat_hear) || (p_ptr->precognition)) msg_print("Monster horde.");
+ return (TRUE);
+ }
+ }
+ else
+ {
+#endif /* MONSTER_HORDES */
+
+ /* Attempt to place the monster, allow groups */
+ if (place_monster(y, x, slp, TRUE)) return (TRUE);
+
+#ifdef MONSTER_HORDES
+ }
+#endif /* MONSTER_HORDES */
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+
+
+/*
+ * Hack -- the "type" of the current "summon specific"
+ */
+static int summon_specific_type = 0;
+
+
+/*
+ * Hack -- help decide if a monster race is "okay" to summon
+ */
+bool_ summon_specific_okay(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ bool_ okay = FALSE;
+
+ /* Hack - Only summon dungeon monsters */
+ if (!monster_dungeon(r_idx)) return (FALSE);
+
+ /* Hack -- no specific type specified */
+ if (!summon_specific_type) return (TRUE);
+
+ /* Check our requirements */
+ switch (summon_specific_type)
+ {
+ case SUMMON_ANT:
+ {
+ okay = ((r_ptr->d_char == 'a') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_SPIDER:
+ {
+ okay = ((r_ptr->d_char == 'S') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HOUND:
+ {
+ okay = (((r_ptr->d_char == 'C') || (r_ptr->d_char == 'Z')) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HYDRA:
+ {
+ okay = ((r_ptr->d_char == 'M') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_ANGEL:
+ {
+ okay = ((r_ptr->d_char == 'A') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_DEMON:
+ {
+ okay = ((r_ptr->flags3 & (RF3_DEMON)) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_UNDEAD:
+ {
+ okay = ((r_ptr->flags3 & (RF3_UNDEAD)) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_DRAGON:
+ {
+ okay = ((r_ptr->flags3 & (RF3_DRAGON)) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HI_UNDEAD:
+ {
+ okay = ((r_ptr->d_char == 'L') ||
+ (r_ptr->d_char == 'V') ||
+ (r_ptr->d_char == 'W'));
+ break;
+ }
+
+ case SUMMON_HI_DRAGON:
+ {
+ okay = (r_ptr->d_char == 'D');
+ break;
+ }
+
+ case SUMMON_WRAITH:
+ {
+ okay = (r_ptr->d_char == 'W');
+ break;
+ }
+
+ case SUMMON_GHOST:
+ {
+ okay = (r_ptr->d_char == 'G');
+ break;
+ }
+
+ case SUMMON_UNIQUE:
+ {
+ okay = (r_ptr->flags1 & (RF1_UNIQUE)) ? TRUE : FALSE;
+ break;
+ }
+
+ case SUMMON_BIZARRE1:
+ {
+ okay = ((r_ptr->d_char == 'm') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+ case SUMMON_BIZARRE2:
+ {
+ okay = ((r_ptr->d_char == 'b') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+ case SUMMON_BIZARRE3:
+ {
+ okay = ((r_ptr->d_char == 'Q') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_BIZARRE4:
+ {
+ okay = ((r_ptr->d_char == 'v') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_BIZARRE5:
+ {
+ okay = ((r_ptr->d_char == '$') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_BIZARRE6:
+ {
+ okay = (((r_ptr->d_char == '!') ||
+ (r_ptr->d_char == '?') ||
+ (r_ptr->d_char == '=') ||
+ (r_ptr->d_char == '$') ||
+ (r_ptr->d_char == '|')) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HI_DEMON:
+ {
+ okay = ((r_ptr->flags3 & (RF3_DEMON)) &&
+ (r_ptr->d_char == 'U') &&
+ !(r_ptr->flags1 & RF1_UNIQUE));
+ break;
+ }
+
+
+ case SUMMON_KIN:
+ {
+ okay = ((r_ptr->d_char == summon_kin_type) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_DAWN:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "the Dawn")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_ANIMAL:
+ {
+ okay = ((r_ptr->flags3 & (RF3_ANIMAL)) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_ANIMAL_RANGER:
+ {
+ okay = ((r_ptr->flags3 & (RF3_ANIMAL)) &&
+ (strchr("abcflqrwBCIJKMRS", r_ptr->d_char)) &&
+ !(r_ptr->flags3 & (RF3_DRAGON)) &&
+ !(r_ptr->flags3 & (RF3_EVIL)) &&
+ !(r_ptr->flags3 & (RF3_UNDEAD)) &&
+ !(r_ptr->flags3 & (RF3_DEMON)) &&
+ !(r_ptr->flags4 || r_ptr->flags5 || r_ptr->flags6) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HI_UNDEAD_NO_UNIQUES:
+ {
+ okay = (((r_ptr->d_char == 'L') ||
+ (r_ptr->d_char == 'V') ||
+ (r_ptr->d_char == 'W')) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HI_DRAGON_NO_UNIQUES:
+ {
+ okay = ((r_ptr->d_char == 'D') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_NO_UNIQUES:
+ {
+ okay = (!(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_PHANTOM:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "Phantom")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_ELEMENTAL:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "lemental")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_THUNDERLORD:
+ {
+ okay = (r_ptr->flags3 & RF3_THUNDERLORD) ? TRUE : FALSE;
+ break;
+ }
+
+ case SUMMON_BLUE_HORROR:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "lue horror")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_BUG:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "Software bug")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_RNG:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "Random Number Generator")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+ case SUMMON_MINE:
+ {
+ okay = (r_ptr->flags1 & RF1_NEVER_MOVE) ? TRUE : FALSE;
+ break;
+ }
+
+ case SUMMON_HUMAN:
+ {
+ okay = ((r_ptr->d_char == 'p') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_SHADOWS:
+ {
+ okay = ((r_ptr->d_char == 'G') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_QUYLTHULG:
+ {
+ okay = ((r_ptr->d_char == 'Q') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_LUA:
+ {
+ okay = summon_lua_okay(r_idx);
+ break;
+ }
+ }
+
+ /* Result */
+ return (okay);
+}
+
+
+/*
+ * Place a monster (of the specified "type") near the given
+ * location. Return TRUE if a monster was actually summoned.
+ *
+ * We will attempt to place the monster up to 10 times before giving up.
+ *
+ * Note: SUMMON_UNIQUE and SUMMON_WRAITH (XXX) will summon Unique's
+ * Note: SUMMON_HI_UNDEAD and SUMMON_HI_DRAGON may summon Unique's
+ * Note: None of the other summon codes will ever summon Unique's.
+ *
+ * This function has been changed. We now take the "monster level"
+ * of the summoning monster as a parameter, and use that, along with
+ * the current dungeon level, to help determine the level of the
+ * desired monster. Note that this is an upper bound, and also
+ * tends to "prefer" monsters of that level. Currently, we use
+ * the average of the dungeon and monster levels, and then add
+ * five to allow slight increases in monster power.
+ *
+ * Note that we use the new "monster allocation table" creation code
+ * to restrict the "get_mon_num()" function to the set of "legal"
+ * monsters, making this function much faster and more reliable.
+ *
+ * Note that this function may not succeed, though this is very rare.
+ */
+int summon_specific_level = 0;
+bool_ summon_specific(int y1, int x1, int lev, int type)
+{
+ int i, x, y, r_idx;
+ bool_ Group_ok = TRUE;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ /* Look for a location */
+ for (i = 0; i < 20; ++i)
+ {
+ /* Pick a distance */
+ int d = (i / 15) + 1;
+
+ /* Pick a location */
+ scatter(&y, &x, y1, x1, d);
+
+ /* Require "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) return (FALSE);
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START) &&
+ (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ continue;
+
+ /* Okay */
+ break;
+ }
+
+ /* Failure */
+ if (i == 20) return (FALSE);
+
+ /* Save the "summon" type */
+ summon_specific_type = type;
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = summon_specific_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+
+ /* Pick a monster, using the level calculation */
+ summon_hack = TRUE;
+ r_idx = get_mon_num((dun_level + lev) / 2 + 5);
+ summon_hack = FALSE;
+
+#ifdef R_IDX_TESTING_HACK
+ r_idx = 356;
+#endif
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+
+ /* Handle failure */
+ if (!r_idx) return (FALSE);
+
+
+ if ((type == SUMMON_DAWN) || (type == SUMMON_BLUE_HORROR))
+ {
+ Group_ok = FALSE;
+ }
+
+ /* Attempt to place the monster (awake, allow groups) */
+ if (!place_monster_aux(y, x, r_idx, FALSE, Group_ok, MSTATUS_ENEMY)) return (FALSE);
+ if (summon_specific_level)
+ {
+ monster_set_level(place_monster_result, summon_specific_level);
+ summon_specific_level = 0;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+bool_ summon_specific_friendly(int y1, int x1, int lev, int type, bool_ Group_ok)
+{
+ int i, x, y, r_idx;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ /* Look for a location */
+ for (i = 0; i < 20; ++i)
+ {
+ /* Pick a distance */
+ int d = (i / 15) + 1;
+
+ /* Pick a location */
+ scatter(&y, &x, y1, x1, d);
+
+ /* Require "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) return (FALSE);
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START) &&
+ (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ continue;
+
+ /* Okay */
+ break;
+ }
+
+ /* Failure */
+ if (i == 20) return (FALSE);
+
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Save the "summon" type */
+ summon_specific_type = type;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = summon_specific_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a monster, using the level calculation */
+ r_idx = get_mon_num((dun_level + lev) / 2 + 5);
+
+#ifdef R_IDX_TESTING_HACK
+ r_idx = 356;
+#endif
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Handle failure */
+ if (!r_idx) return (FALSE);
+
+ /* Attempt to place the monster (awake, allow groups) */
+ if (!place_monster_aux(y, x, r_idx, FALSE, Group_ok, MSTATUS_PET)) return (FALSE);
+ if (summon_specific_level)
+ {
+ monster_set_level(place_monster_result, summon_specific_level);
+ summon_specific_level = 0;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Swap the players/monsters (if any) at two locations XXX XXX XXX
+ */
+void monster_swap(int y1, int x1, int y2, int x2)
+{
+ int m1, m2;
+
+ monster_type *m_ptr;
+ cave_type *c_ptr1, *c_ptr2;
+
+ c_ptr1 = &cave[y1][x1];
+ c_ptr2 = &cave[y2][x2];
+
+ /* Monsters */
+ m1 = c_ptr1->m_idx;
+ m2 = c_ptr2->m_idx;
+
+
+ /* Update grids */
+ c_ptr1->m_idx = m2;
+ c_ptr2->m_idx = m1;
+
+
+ /* Monster 1 */
+ if (m1 > 0)
+ {
+ m_ptr = &m_list[m1];
+
+ /* Move monster */
+ m_ptr->fy = y2;
+ m_ptr->fx = x2;
+
+ /* Update monster */
+ update_mon(m1, TRUE);
+ }
+
+ /* Player 1 */
+ else if (m1 < 0)
+ {
+ /* Move player */
+ p_ptr->py = y2;
+ p_ptr->px = x2;
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Window stuff */
+ /* It's probably not a good idea to recalculate the
+ * overhead view each turn.
+
+ p_ptr->window |= (PW_OVERHEAD);
+ */
+ }
+
+ /* Monster 2 */
+ if (m2 > 0)
+ {
+ m_ptr = &m_list[m2];
+
+ /* Move monster */
+ m_ptr->fy = y1;
+ m_ptr->fx = x1;
+
+ /* Update monster */
+ update_mon(m2, TRUE);
+ }
+
+ /* Player 2 */
+ else if (m2 > 0)
+ {
+ /* Move player */
+ p_ptr->py = y1;
+ p_ptr->px = x1;
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Window stuff */
+ /* It's probably not a good idea to recalculate the
+ * overhead view each turn.
+
+ p_ptr->window |= (PW_OVERHEAD);
+ */
+ }
+
+
+ /* Redraw */
+ lite_spot(y1, x1);
+ lite_spot(y2, x2);
+}
+
+
+/*
+ * Hack -- help decide if a monster race is "okay" to summon
+ */
+static bool_ mutate_monster_okay(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ bool_ okay = FALSE;
+
+ /* Hack - Only summon dungeon monsters */
+ if (!monster_dungeon(r_idx)) return (FALSE);
+
+ okay = ((r_ptr->d_char == summon_kin_type) && !(r_ptr->flags1 & (RF1_UNIQUE))
+ && (r_ptr->level >= dun_level));
+
+ return okay;
+}
+
+
+/*
+ * Let the given monster attempt to reproduce.
+ *
+ * Note that "reproduction" REQUIRES empty space.
+ */
+bool_ multiply_monster(int m_idx, bool_ charm, bool_ clone)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ int i, y, x, new_race;
+
+ bool_ result = FALSE;
+
+ if (no_breeds)
+ {
+ msg_print("It tries to breed but it fails!");
+ return FALSE;
+ }
+
+ /* Try up to 18 times */
+ for (i = 0; i < 18; i++)
+ {
+ int d = 1;
+
+
+ /* Pick a location */
+ scatter(&y, &x, m_ptr->fy, m_ptr->fx, d);
+
+ /* Require an "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ new_race = m_ptr->r_idx;
+
+ /* It can mutate into a nastier monster */
+ if ((rand_int(100) < 3) && (!clone))
+ {
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = mutate_monster_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ summon_kin_type = r_ptr->d_char;
+
+ /* Pick a monster, using the level calculation */
+ new_race = get_mon_num(dun_level + 5);
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+ }
+
+ /* Create a new monster (awake, no groups) */
+ result = place_monster_aux(y, x, new_race, FALSE, FALSE, (charm) ? MSTATUS_PET : MSTATUS_ENEMY);
+
+ /* Done */
+ break;
+ }
+
+ if (clone && result) m_list[hack_m_idx_ii].smart |= SM_CLONED;
+
+ /* Result */
+ return (result);
+}
+
+
+
+
+
+/*
+ * Dump a message describing a monster's reaction to damage
+ *
+ * Technically should attempt to treat "Beholder"'s as jelly's
+ */
+bool_ hack_message_pain_may_silent = FALSE;
+void message_pain_hook(cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ if (hack_message_pain_may_silent)
+ monster_msg(buf);
+ else
+ msg_print(buf);
+}
+
+void message_pain(int m_idx, int dam)
+{
+ long oldhp, newhp, tmp;
+ int percentage;
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ char m_name[80];
+
+ /* Get the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Notice non-damage */
+ if (dam == 0)
+ {
+ message_pain_hook("%^s is unharmed.", m_name);
+ return;
+ }
+
+ /* Note -- subtle fix -CFT */
+ newhp = (long)(m_ptr->hp);
+ oldhp = newhp + (long)(dam);
+ tmp = (newhp * 100L) / oldhp;
+ percentage = (int)(tmp);
+
+
+ /* Jelly's, Mold's, Vortex's, Quthl's */
+ if (strchr("jmvQ", r_ptr->d_char))
+ {
+ if (percentage > 95)
+ message_pain_hook("%^s barely notices.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("%^s flinches.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("%^s squelches.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("%^s quivers in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("%^s writhes about.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("%^s writhes in agony.", m_name);
+ else
+ message_pain_hook("%^s jerks limply.", m_name);
+ }
+
+ /* Dogs and Hounds */
+ else if (strchr("CZ", r_ptr->d_char))
+ {
+ if (percentage > 95)
+ message_pain_hook("%^s shrugs off the attack.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("%^s snarls with pain.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("%^s yelps in pain.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("%^s howls in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("%^s howls in agony.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("%^s writhes in agony.", m_name);
+ else
+ message_pain_hook("%^s yelps feebly.", m_name);
+ }
+
+ /* One type of monsters (ignore,squeal,shriek) */
+ else if (strchr("FIKMRSXabclqrst", r_ptr->d_char))
+ {
+ if (percentage > 95)
+ message_pain_hook("%^s ignores the attack.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("%^s grunts with pain.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("%^s squeals in pain.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("%^s shrieks in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("%^s shrieks in agony.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("%^s writhes in agony.", m_name);
+ else
+ message_pain_hook("%^s cries out feebly.", m_name);
+ }
+
+ /* Another type of monsters (shrug,cry,scream) */
+ else
+ {
+ if (percentage > 95)
+ message_pain_hook("%^s shrugs off the attack.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("%^s grunts with pain.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("%^s cries out in pain.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("%^s screams in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("%^s screams in agony.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("%^s writhes in agony.", m_name);
+ else
+ message_pain_hook("%^s cries out feebly.", m_name);
+ }
+}
+
+
+
+/*
+ * Learn about an "observed" resistance.
+ */
+void update_smart_learn(int m_idx, int what)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+
+ /* Not allowed to learn */
+ if (!smart_learn) return;
+
+ /* Too stupid to learn anything */
+ if (r_ptr->flags2 & (RF2_STUPID)) return;
+
+ /* Not intelligent, only learn sometimes */
+ if (!(r_ptr->flags2 & (RF2_SMART)) && (rand_int(100) < 50)) return;
+
+
+ /* XXX XXX XXX */
+
+ /* Analyze the knowledge */
+ switch (what)
+ {
+ case DRS_ACID:
+ if (p_ptr->resist_acid) m_ptr->smart |= (SM_RES_ACID);
+ if (p_ptr->oppose_acid) m_ptr->smart |= (SM_OPP_ACID);
+ if (p_ptr->immune_acid) m_ptr->smart |= (SM_IMM_ACID);
+ break;
+
+ case DRS_ELEC:
+ if (p_ptr->resist_elec) m_ptr->smart |= (SM_RES_ELEC);
+ if (p_ptr->oppose_elec) m_ptr->smart |= (SM_OPP_ELEC);
+ if (p_ptr->immune_elec) m_ptr->smart |= (SM_IMM_ELEC);
+ break;
+
+ case DRS_FIRE:
+ if (p_ptr->resist_fire) m_ptr->smart |= (SM_RES_FIRE);
+ if (p_ptr->oppose_fire) m_ptr->smart |= (SM_OPP_FIRE);
+ if (p_ptr->immune_fire) m_ptr->smart |= (SM_IMM_FIRE);
+ break;
+
+ case DRS_COLD:
+ if (p_ptr->resist_cold) m_ptr->smart |= (SM_RES_COLD);
+ if (p_ptr->oppose_cold) m_ptr->smart |= (SM_OPP_COLD);
+ if (p_ptr->immune_cold) m_ptr->smart |= (SM_IMM_COLD);
+ break;
+
+ case DRS_POIS:
+ if (p_ptr->resist_pois) m_ptr->smart |= (SM_RES_POIS);
+ if (p_ptr->oppose_pois) m_ptr->smart |= (SM_OPP_POIS);
+ break;
+
+
+ case DRS_NETH:
+ if (p_ptr->resist_neth) m_ptr->smart |= (SM_RES_NETH);
+ break;
+
+ case DRS_LITE:
+ if (p_ptr->resist_lite) m_ptr->smart |= (SM_RES_LITE);
+ break;
+
+ case DRS_DARK:
+ if (p_ptr->resist_dark) m_ptr->smart |= (SM_RES_DARK);
+ break;
+
+ case DRS_FEAR:
+ if (p_ptr->resist_fear) m_ptr->smart |= (SM_RES_FEAR);
+ break;
+
+ case DRS_CONF:
+ if (p_ptr->resist_conf) m_ptr->smart |= (SM_RES_CONF);
+ break;
+
+ case DRS_CHAOS:
+ if (p_ptr->resist_chaos) m_ptr->smart |= (SM_RES_CHAOS);
+ break;
+
+ case DRS_DISEN:
+ if (p_ptr->resist_disen) m_ptr->smart |= (SM_RES_DISEN);
+ break;
+
+ case DRS_BLIND:
+ if (p_ptr->resist_blind) m_ptr->smart |= (SM_RES_BLIND);
+ break;
+
+ case DRS_NEXUS:
+ if (p_ptr->resist_nexus) m_ptr->smart |= (SM_RES_NEXUS);
+ break;
+
+ case DRS_SOUND:
+ if (p_ptr->resist_sound) m_ptr->smart |= (SM_RES_SOUND);
+ break;
+
+ case DRS_SHARD:
+ if (p_ptr->resist_shard) m_ptr->smart |= (SM_RES_SHARD);
+ break;
+
+
+ case DRS_FREE:
+ if (p_ptr->free_act) m_ptr->smart |= (SM_IMM_FREE);
+ break;
+
+ case DRS_MANA:
+ if (!p_ptr->msp) m_ptr->smart |= (SM_IMM_MANA);
+ break;
+
+ case DRS_REFLECT:
+ if (p_ptr->reflect) m_ptr-> smart |= (SM_IMM_REFLECT);
+ break;
+ }
+}
+
+
+/*
+ * Place the player in the dungeon XXX XXX
+ */
+s16b player_place(int y, int x)
+{
+ /* Paranoia XXX XXX */
+ if (cave[y][x].m_idx != 0) return (0);
+
+ /* Save player location */
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ /* Success */
+ return ( -1);
+}
+
+/*
+ * Drop all items carried by a monster
+ */
+void monster_drop_carried_objects(monster_type *m_ptr)
+{
+ s16b this_o_idx, next_o_idx = 0;
+ object_type forge;
+ object_type *o_ptr;
+ object_type *q_ptr;
+
+
+ /* Drop objects being carried */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Paranoia */
+ o_ptr->held_m_idx = 0;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Copy the object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ /* Drop it */
+ drop_near(q_ptr, -1, m_ptr->fy, m_ptr->fx);
+ }
+
+ /* Forget objects */
+ m_ptr->hold_o_idx = 0;
+}
diff --git a/src/monster3.c b/src/monster3.c
new file mode 100644
index 00000000..a2b5fb38
--- /dev/null
+++ b/src/monster3.c
@@ -0,0 +1,706 @@
+/* File: monster3.c */
+
+/* Purpose: Monsters AI */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Is the mon,ster in friendly state(pet, friend, ..)
+ * -1 = enemy, 0 = neutral, 1 = friend
+ */
+int is_friend(monster_type *m_ptr)
+{
+ switch (m_ptr->status)
+ {
+ /* pets/friends/companion attacks monsters */
+ case MSTATUS_NEUTRAL_P:
+ case MSTATUS_FRIEND:
+ case MSTATUS_PET:
+ case MSTATUS_COMPANION:
+ return 1;
+ break;
+ case MSTATUS_NEUTRAL_M:
+ case MSTATUS_ENEMY:
+ return -1;
+ break;
+ case MSTATUS_NEUTRAL:
+ return 0;
+ break;
+ }
+
+ /* OUPS */
+ return (0);
+}
+
+/* Should they attack each others */
+bool_ is_enemy(monster_type *m_ptr, monster_type *t_ptr)
+{
+ monster_race *r_ptr = &r_info[m_ptr->r_idx], *rt_ptr = &r_info[t_ptr->r_idx];
+ int s1 = is_friend(m_ptr), s2 = is_friend(t_ptr);
+
+ /* Monsters hates breeders */
+ if ((m_ptr->status != MSTATUS_NEUTRAL) && (rt_ptr->flags4 & RF4_MULTIPLY) && (num_repro > MAX_REPRO * 2 / 3) && (r_ptr->d_char != rt_ptr->d_char)) return TRUE;
+ if ((t_ptr->status != MSTATUS_NEUTRAL) && (r_ptr->flags4 & RF4_MULTIPLY) && (num_repro > MAX_REPRO * 2 / 3) && (r_ptr->d_char != rt_ptr->d_char)) return TRUE;
+
+ /* No special conditions, lets test normal flags */
+ if (s1 && s2 && (s1 == -s2)) return TRUE;
+
+ /* Not ennemy */
+ return (FALSE);
+}
+
+bool_ change_side(monster_type *m_ptr)
+{
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* neutrals and companions */
+ switch (m_ptr->status)
+ {
+ case MSTATUS_FRIEND:
+ m_ptr->status = MSTATUS_ENEMY;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, -m_ptr->level * 4);
+ break;
+ case MSTATUS_NEUTRAL_P:
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ case MSTATUS_NEUTRAL_M:
+ m_ptr->status = MSTATUS_NEUTRAL_P;
+ break;
+ case MSTATUS_PET:
+ m_ptr->status = MSTATUS_ENEMY;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, -m_ptr->level * 4);
+ break;
+ case MSTATUS_COMPANION:
+ return FALSE;
+ break;
+ }
+ /* changed */
+ return TRUE;
+}
+
+/* Multiply !! */
+bool_ ai_multiply(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ int k, y, x, oy = m_ptr->fy, ox = m_ptr->fx;
+ bool_ is_frien = (is_friend(m_ptr) > 0);
+
+ /* Count the adjacent monsters */
+ for (k = 0, y = oy - 1; y <= oy + 1; y++)
+ {
+ for (x = ox - 1; x <= ox + 1; x++)
+ {
+ if (cave[y][x].m_idx) k++;
+ }
+ }
+
+ if (is_friend(m_ptr) > 0)
+ {
+ is_frien = TRUE;
+ }
+ else
+ {
+ is_frien = FALSE;
+ }
+
+ /* Hack -- multiply slower in crowded areas */
+ if ((k < 4) && (!k || !rand_int(k * MON_MULT_ADJ)))
+ {
+ /* Try to multiply */
+ if (multiply_monster(m_idx, (is_frien), FALSE))
+ {
+ /* Take note if visible */
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags4 |= (RF4_MULTIPLY);
+ }
+
+ /* Multiplying takes energy */
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* Possessor incarnates */
+bool_ ai_possessor(int m_idx, int o_idx)
+{
+ object_type *o_ptr = &o_list[o_idx];
+ monster_type *m_ptr = &m_list[m_idx];
+ int r_idx = m_ptr->r_idx, r2_idx = o_ptr->pval2;
+ int i;
+ monster_race *r_ptr = &r_info[r2_idx];
+ char m_name[80], m_name2[80];
+
+ monster_desc(m_name, m_ptr, 0x00);
+ monster_race_desc(m_name2, r2_idx, 0);
+
+ if (m_ptr->ml) msg_format("%^s incarnates into a %s!", m_name, m_name2);
+
+ /* Remove the old one */
+ delete_object_idx(o_idx);
+
+ m_ptr->r_idx = r2_idx;
+ m_ptr->ego = 0;
+
+ /* No "damage" yet */
+ m_ptr->stunned = 0;
+ m_ptr->confused = 0;
+ m_ptr->monfear = 0;
+
+ /* No target yet */
+ m_ptr->target = -1;
+
+ /* Assume no sleeping */
+ m_ptr->csleep = 0;
+
+ /* Assign maximal hitpoints */
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP))
+ {
+ m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
+ }
+ else
+ {
+ m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside);
+ }
+
+ /* And start out fully healthy */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Some basic info */
+ for (i = 0; i < 4; i++)
+ {
+ m_ptr->blow[i].method = r_ptr->blow[i].method;
+ m_ptr->blow[i].effect = r_ptr->blow[i].effect;
+ m_ptr->blow[i].d_dice = r_ptr->blow[i].d_dice;
+ m_ptr->blow[i].d_side = r_ptr->blow[i].d_side;
+ }
+ m_ptr->ac = r_ptr->ac;
+ m_ptr->level = r_ptr->level;
+ m_ptr->speed = r_ptr->speed;
+ m_ptr->exp = MONSTER_EXP(m_ptr->level);
+
+ /* Extract the monster base speed */
+ m_ptr->mspeed = m_ptr->speed;
+
+ m_ptr->energy = 0;
+
+ /* Hack -- Count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro++;
+
+ /* Hack -- Notice new multi-hued monsters */
+ if (r_ptr->flags1 & (RF1_ATTR_MULTI)) shimmer_monsters = TRUE;
+
+ /* Hack -- Count the monsters on the level */
+ r_ptr->cur_num++;
+ r_info[r_idx].cur_num--;
+
+ m_ptr->possessor = r_idx;
+
+ /* Update the monster */
+ update_mon(m_idx, TRUE);
+
+ return TRUE;
+}
+
+void ai_deincarnate(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int r2_idx = m_ptr->possessor, r_idx = m_ptr->r_idx;
+ monster_race *r_ptr = &r_info[r2_idx];
+ int i;
+ char m_name[80];
+
+ monster_desc(m_name, m_ptr, 0x04);
+
+ if (m_ptr->ml) msg_format("The soul of %s deincarnates!", m_name);
+
+ m_ptr->r_idx = r2_idx;
+ m_ptr->ego = 0;
+
+ /* No "damage" yet */
+ m_ptr->stunned = 0;
+ m_ptr->confused = 0;
+ m_ptr->monfear = 0;
+
+ /* No target yet */
+ m_ptr->target = -1;
+
+ /* Assume no sleeping */
+ m_ptr->csleep = 0;
+
+ /* Assign maximal hitpoints */
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP))
+ {
+ m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
+ }
+ else
+ {
+ m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside);
+ }
+
+ /* And start out fully healthy */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Some basic info */
+ for (i = 0; i < 4; i++)
+ {
+ m_ptr->blow[i].method = r_ptr->blow[i].method;
+ m_ptr->blow[i].effect = r_ptr->blow[i].effect;
+ m_ptr->blow[i].d_dice = r_ptr->blow[i].d_dice;
+ m_ptr->blow[i].d_side = r_ptr->blow[i].d_side;
+ }
+ m_ptr->ac = r_ptr->ac;
+ m_ptr->level = r_ptr->level;
+ m_ptr->speed = r_ptr->speed;
+ m_ptr->exp = MONSTER_EXP(m_ptr->level);
+
+ /* Extract the monster base speed */
+ m_ptr->mspeed = m_ptr->speed;
+
+ m_ptr->energy = 0;
+
+ /* Hack -- Count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro++;
+
+ /* Hack -- Notice new multi-hued monsters */
+ if (r_ptr->flags1 & (RF1_ATTR_MULTI)) shimmer_monsters = TRUE;
+
+ /* Hack -- Count the monsters on the level */
+ r_ptr->cur_num++;
+ r_info[r_idx].cur_num--;
+
+ m_ptr->possessor = 0;
+
+ /* Update the monster */
+ update_mon(m_idx, TRUE);
+}
+
+/* Returns if a new companion is allowed */
+bool_ can_create_companion(void)
+{
+ int i, mcnt = 0;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status == MSTATUS_COMPANION) mcnt++;
+ }
+
+ if (mcnt < (1 + get_skill_scale(SKILL_LORE, 6))) return (TRUE);
+ else return (FALSE);
+}
+
+
+/* Player controlled monsters */
+bool_ do_control_walk(void)
+{
+ /* Get a "repeated" direction */
+ if (p_ptr->control)
+ {
+ int dir;
+
+ if (get_rep_dir(&dir))
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Actually move the monster */
+ p_ptr->control_dir = dir;
+ }
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+bool_ do_control_inven(void)
+{
+ int monst_list[23];
+
+ if (!p_ptr->control) return FALSE;
+ screen_save();
+ prt("Carried items", 0, 0);
+ show_monster_inven(p_ptr->control, monst_list);
+ inkey();
+ screen_load();
+ return TRUE;
+}
+
+bool_ do_control_pickup(void)
+{
+ int this_o_idx, next_o_idx = 0;
+ monster_type *m_ptr = &m_list[p_ptr->control];
+ cave_type *c_ptr;
+ bool_ done = FALSE;
+
+ if (!p_ptr->control) return FALSE;
+
+ /* Scan all objects in the grid */
+ c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Skip gold */
+ if (o_ptr->tval == TV_GOLD) continue;
+
+ /* Excise the object */
+ excise_object_idx(this_o_idx);
+
+ /* Forget mark */
+ o_ptr->marked = FALSE;
+
+ /* Forget location */
+ o_ptr->iy = o_ptr->ix = 0;
+
+ /* Memorize monster */
+ o_ptr->held_m_idx = p_ptr->control;
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ /* Carry object */
+ m_ptr->hold_o_idx = this_o_idx;
+ done = TRUE;
+ }
+ if (done) msg_print("You pick up all objects on the floor.");
+ return TRUE;
+}
+
+bool_ do_control_drop(void)
+{
+ monster_type *m_ptr = &m_list[p_ptr->control];
+
+ if (!p_ptr->control) return FALSE;
+ monster_drop_carried_objects(m_ptr);
+ return TRUE;
+}
+
+bool_ do_control_magic(void)
+{
+ int power = -1;
+ int num = 0, i;
+ int powers[96];
+ bool_ flag, redraw;
+ int ask;
+ char choice;
+ char out_val[160];
+ monster_race *r_ptr = &r_info[m_list[p_ptr->control].r_idx];
+ int label;
+
+ if (!p_ptr->control) return FALSE;
+
+ if (get_check("Do you want to abandon the creature?"))
+ {
+ if (get_check("Abandon it permanently?"))
+ delete_monster_idx(p_ptr->control);
+ p_ptr->control = 0;
+ return TRUE;
+ }
+
+ /* List the monster powers -- RF4_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags4 & BIT(i))
+ {
+ if (!monster_powers[i].power) continue;
+ powers[num++] = i;
+ }
+ }
+
+ /* List the monster powers -- RF5_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags5 & BIT(i))
+ {
+ if (!monster_powers[i + 32].power) continue;
+ powers[num++] = i + 32;
+ }
+ }
+
+ /* List the monster powers -- RF6_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags6 & BIT(i))
+ {
+ if (!monster_powers[i + 64].power) continue;
+ powers[num++] = i + 64;
+ }
+ }
+
+ if (!num)
+ {
+ msg_print("You have no powers you can use.");
+ return (TRUE);
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Get the last label */
+ label = (num <= 26) ? I2A(num - 1) : I2D(num - 1 - 26);
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78,
+ "(Powers a-%c, *=List, ESC=exit) Use which power of your golem? ",
+ label);
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ byte y = 1, x = 0;
+ int ctr = 0;
+ char dummy[80];
+
+ strcpy(dummy, "");
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ prt ("", y++, x);
+
+ while (ctr < num)
+ {
+ monster_power *mp_ptr = &monster_powers[powers[ctr]];
+
+ label = (ctr < 26) ? I2A(ctr) : I2D(ctr - 26);
+
+ strnfmt(dummy, 80, " %c) %s",
+ label, mp_ptr->name);
+
+ if (ctr < 17)
+ {
+ prt(dummy, y + ctr, x);
+ }
+ else
+ {
+ prt(dummy, y + ctr - 17, x + 40);
+ }
+
+ ctr++;
+ }
+
+ if (ctr < 17)
+ {
+ prt ("", y + ctr, x);
+ }
+ else
+ {
+ prt ("", y + 17, x);
+ }
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ if (choice == '\r' && num == 1)
+ {
+ choice = 'a';
+ }
+
+ if (isalpha(choice))
+ {
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ /* Can't uppercase digits XXX XXX XXX */
+ ask = FALSE;
+
+ i = choice - '0' + 26;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Save the spell index */
+ power = powers[i];
+
+ /* Verify it */
+ if (ask)
+ {
+ char tmp_val[160];
+
+ /* Prompt */
+ strnfmt(tmp_val, 78, "Use %s? ", monster_powers[power].name);
+
+ /* Belay that order */
+ if (!get_check(tmp_val)) continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Take a turn */
+ if (flag)
+ {
+ energy_use = 100;
+ monst_spell_monst_spell = power + 96;
+ }
+ return TRUE;
+}
+
+/* Finds the controlled monster and "reconnect" to it */
+bool_ do_control_reconnect()
+{
+ int i;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->mflag & MFLAG_CONTROL)
+ {
+ p_ptr->control = i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Turns a simple pet into a faithful companion
+ */
+void do_cmd_companion()
+{
+ monster_type *m_ptr;
+ int ii, jj;
+
+ if (!can_create_companion())
+ {
+ msg_print("You cannot have any more companions.");
+ return;
+ }
+
+ if (!tgt_pt(&ii, &jj))
+ {
+ msg_print("You must target a pet.");
+ return;
+ }
+
+ if (cave[jj][ii].m_idx)
+ {
+ char m_name[100];
+
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ monster_desc(m_name, m_ptr, 0);
+ if (m_ptr->status == MSTATUS_PET)
+ {
+ m_ptr->status = MSTATUS_COMPANION;
+ msg_format("%^s agrees to follow you.", m_name);
+ }
+ else
+ {
+ msg_format("%^s is not your pet!", m_name);
+ }
+ }
+ else
+ msg_print("You must target a pet.");
+}
+
+/*
+ * List companions to the character sheet.
+ */
+void dump_companions(FILE *outfile)
+{
+ int i;
+ int done_hdr = 0;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status == MSTATUS_COMPANION)
+ {
+ char pet_name[80];
+
+ /* Output the header if we haven't yet. */
+ if (!done_hdr)
+ {
+ done_hdr = 1;
+ fprintf(outfile, "\n\n [Current companions]\n\n");
+ }
+
+ /* List the monster. */
+ monster_desc(pet_name, m_ptr, 0x88);
+ fprintf(outfile, "%s (level %d)\n", pet_name, m_ptr->level);
+ }
+ }
+}
diff --git a/src/notes.c b/src/notes.c
new file mode 100644
index 00000000..3504f61c
--- /dev/null
+++ b/src/notes.c
@@ -0,0 +1,188 @@
+/* File: notes.c */
+
+/* Purpose: Note taking to a file */
+
+/*
+ * Copyright (c) 1989, 1999 James E. Wilson, Robert A. Koeneke,
+ * Robert Ruehlmann
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Show the notes file on the screen
+ */
+void show_notes_file(void)
+{
+ char basename[13];
+ char buf[1024];
+ char caption[10 + 13];
+
+ /* Hack -- extract first 8 characters of name and append an extension */
+ (void)strnfmt(basename, sizeof(basename), "%.8s.nte", player_base);
+ basename[sizeof(basename) - 1] = '\0';
+
+ /* Build the path */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_NOTE, basename);
+
+ /* Use a caption, forcing direct access to the note file */
+ sprintf(caption, "Note file %s", basename);
+
+ /* Invoke show_file */
+ (void)show_file(buf, caption, 0, 0);
+
+ /* Done */
+ return;
+}
+
+/*
+ * Output a string to the notes file.
+ * This is the only function that references that file.
+ */
+void output_note(char *final_note)
+{
+ FILE *fff;
+ char basename[13];
+ char buf[1024];
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Hack -- extract first 8 characters of name and append an extension */
+ (void)strnfmt(basename, sizeof(basename), "%.8s.nte", player_base);
+ basename[sizeof(basename) - 1] = '\0';
+
+ /* Build the path */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_NOTE, basename);
+
+ /* Open notes file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return;
+
+ /* Add note, and close note file */
+ my_fputs(fff, final_note, 0);
+
+ /* Close the handle */
+ my_fclose(fff);
+
+ /* Done */
+ return;
+}
+
+
+/*
+ * Add note to file using a string + character symbol
+ * to specify its type so that the notes file can be
+ * searched easily by external utilities.
+ */
+void add_note(char *note, char code)
+{
+ char buf[100];
+ char final_note[100];
+ char long_day[50];
+ char depths[32];
+
+ /* Get the first 60 chars - so do not have an overflow */
+ C_WIPE(buf, 100, char);
+ strncpy(buf, note, 60);
+
+ /* Get date and time */
+ sprintf(long_day, "%ld:%02ld %s, %s", (long int) ((bst(HOUR, turn) % 12 == 0) ? 12 : (bst(HOUR, turn) % 12)),
+ (long int) bst(MINUTE, turn), (bst(HOUR, turn) < 12) ? "AM" : "PM", get_month_name(bst(DAY, turn), FALSE,
+ FALSE));
+
+ /* Get depth */
+ if (!dun_level) strcpy(depths, " Town");
+ else if (depth_in_feet) sprintf(depths, "%4dft", dun_level * 50);
+ else sprintf(depths, "Lev%3d", dun_level);
+
+ /* Make note */
+ sprintf(final_note, "%-20s %s %c: %s", long_day, depths, code, buf);
+
+ /* Output to the notes file */
+ output_note(final_note);
+}
+
+
+/*
+ * Append a note to the notes file using a "type".
+ */
+void add_note_type(int note_number)
+{
+ char long_day[50], true_long_day[50];
+ char buf[1024];
+ time_t ct = time((time_t*)0);
+
+ /* Get the date */
+ strftime(true_long_day, 30, "%Y-%m-%d at %H:%M:%S", localtime(&ct));
+
+ /* Get the date */
+ sprintf(buf, "%ld", (long int) (bst(YEAR, turn) + START_YEAR));
+ sprintf(long_day, "%ld:%02ld %s the %s of III %s", (long int) ((bst(HOUR, turn) % 12 == 0) ? 12 : (bst(HOUR, turn) % 12)), (long int) bst(MINUTE, turn), (bst(HOUR, turn) < 12) ? "AM" : "PM", get_month_name(bst(DAY, turn), FALSE, FALSE), buf);
+
+ /* Work out what to do */
+ switch (note_number)
+ {
+ case NOTE_BIRTH:
+ {
+ /* Player has just been born */
+ char player[100];
+
+ /* Build the string containing the player information */
+ sprintf(player, "the %s %s", get_player_race_name(p_ptr->prace, p_ptr->pracem), class_info[p_ptr->pclass].spec[p_ptr->pspec].title + c_name);
+
+ /* Add in "character start" information */
+ sprintf(buf,
+ "\n"
+ "================================================\n"
+ "%s %s\n"
+ "Born on %s\n"
+ "================================================\n",
+ player_name, player, true_long_day);
+
+ break;
+ }
+
+ case NOTE_WINNER:
+ {
+ sprintf(buf,
+ "%s slew Morgoth on %s\n"
+ "Long live %s!\n"
+ "================================================",
+ player_name, long_day, player_name);
+
+ break;
+ }
+
+ case NOTE_SAVE_GAME:
+ {
+ /* Saving the game */
+ sprintf(buf, "\nSession end: %s", true_long_day);
+
+ break;
+ }
+
+ case NOTE_ENTER_DUNGEON:
+ {
+ /* Entering the game after a break. */
+ sprintf(buf,
+ "================================================\n"
+ "New session start: %s\n",
+ true_long_day);
+
+ break;
+ }
+
+ default:
+ return;
+ }
+
+ /* Output the notes to the file */
+ output_note(buf);
+}
diff --git a/src/object.pkg b/src/object.pkg
new file mode 100644
index 00000000..a89dad9a
--- /dev/null
+++ b/src/object.pkg
@@ -0,0 +1,1169 @@
+/* File: object.pkg */
+
+/*
+ * Purpose: Lua interface defitions for objects.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+typedef char* cptr;
+typedef int errr;
+typedef unsigned char bool;
+typedef unsigned char byte;
+typedef signed short s16b;
+typedef unsigned short u16b;
+typedef signed int s32b;
+typedef unsigned int u32b;
+
+/* To make easy object creations */
+$static object_type lua_obj_forge;
+static object_type lua_obj_forge @ obj_forge;
+$static obj_theme lua_obj_theme;
+static obj_theme lua_obj_theme @ theme_forge;
+
+#define TR1_STR 0x00000001L /* STR += "pval" */
+#define TR1_INT 0x00000002L /* INT += "pval" */
+#define TR1_WIS 0x00000004L /* WIS += "pval" */
+#define TR1_DEX 0x00000008L /* DEX += "pval" */
+#define TR1_CON 0x00000010L /* CON += "pval" */
+#define TR1_CHR 0x00000020L /* CHR += "pval" */
+#define TR1_MANA 0x00000040L /* Mana multipler */
+#define TR1_SPELL 0x00000080L /* Spell power increase */
+#define TR1_STEALTH 0x00000100L /* Stealth += "pval" */
+#define TR1_SEARCH 0x00000200L /* Search += "pval" */
+#define TR1_INFRA 0x00000400L /* Infra += "pval" */
+#define TR1_TUNNEL 0x00000800L /* Tunnel += "pval" */
+#define TR1_SPEED 0x00001000L /* Speed += "pval" */
+#define TR1_BLOWS 0x00002000L /* Blows += "pval" */
+#define TR1_CHAOTIC 0x00004000L
+#define TR1_VAMPIRIC 0x00008000L
+#define TR1_SLAY_ANIMAL 0x00010000L
+#define TR1_SLAY_EVIL 0x00020000L
+#define TR1_SLAY_UNDEAD 0x00040000L
+#define TR1_SLAY_DEMON 0x00080000L
+#define TR1_SLAY_ORC 0x00100000L
+#define TR1_SLAY_TROLL 0x00200000L
+#define TR1_SLAY_GIANT 0x00400000L
+#define TR1_SLAY_DRAGON 0x00800000L
+#define TR1_KILL_DRAGON 0x01000000L /* Execute Dragon */
+#define TR1_VORPAL 0x02000000L /* Later */
+#define TR1_IMPACT 0x04000000L /* Cause Earthquakes */
+#define TR1_BRAND_POIS 0x08000000L
+#define TR1_BRAND_ACID 0x10000000L
+#define TR1_BRAND_ELEC 0x20000000L
+#define TR1_BRAND_FIRE 0x40000000L
+#define TR1_BRAND_COLD 0x80000000L
+#define TR1_NULL_MASK 0x00000000L
+
+#define TR2_SUST_STR 0x00000001L
+#define TR2_SUST_INT 0x00000002L
+#define TR2_SUST_WIS 0x00000004L
+#define TR2_SUST_DEX 0x00000008L
+#define TR2_SUST_CON 0x00000010L
+#define TR2_SUST_CHR 0x00000020L
+#define TR2_INVIS 0x00000040L /* Invisibility */
+#define TR2_LIFE 0x00000080L /* Life multiplier */
+#define TR2_IM_ACID 0x00000100L
+#define TR2_IM_ELEC 0x00000200L
+#define TR2_IM_FIRE 0x00000400L
+#define TR2_IM_COLD 0x00000800L
+#define TR2_SENS_FIRE 0x00001000L /* Sensibility to fire */
+#define TR2_REFLECT 0x00002000L /* Reflect 'bolts' */
+#define TR2_FREE_ACT 0x00004000L /* Free Action */
+#define TR2_HOLD_LIFE 0x00008000L /* Hold Life */
+#define TR2_RES_ACID 0x00010000L
+#define TR2_RES_ELEC 0x00020000L
+#define TR2_RES_FIRE 0x00040000L
+#define TR2_RES_COLD 0x00080000L
+#define TR2_RES_POIS 0x00100000L
+#define TR2_RES_FEAR 0x00200000L
+#define TR2_RES_LITE 0x00400000L
+#define TR2_RES_DARK 0x00800000L
+#define TR2_RES_BLIND 0x01000000L
+#define TR2_RES_CONF 0x02000000L
+#define TR2_RES_SOUND 0x04000000L
+#define TR2_RES_SHARDS 0x08000000L
+#define TR2_RES_NETHER 0x10000000L
+#define TR2_RES_NEXUS 0x20000000L
+#define TR2_RES_CHAOS 0x40000000L
+#define TR2_RES_DISEN 0x80000000L
+#define TR2_NULL_MASK 0x00000000L
+
+#define TR3_SH_FIRE 0x00000001L /* Immolation (Fire) */
+#define TR3_SH_ELEC 0x00000002L /* Electric Sheath */
+#define TR3_AUTO_CURSE 0x00000004L /* The obj will recurse itself */
+#define TR3_DECAY 0x00000008L /* Decay */
+#define TR3_NO_TELE 0x00000010L /* Anti-teleportation */
+#define TR3_NO_MAGIC 0x00000020L /* Anti-magic */
+#define TR3_WRAITH 0x00000040L /* Wraithform */
+#define TR3_TY_CURSE 0x00000080L /* The Ancient Curse */
+#define TR3_EASY_KNOW 0x00000100L /* Aware -> Known */
+#define TR3_HIDE_TYPE 0x00000200L /* Hide "pval" description */
+#define TR3_SHOW_MODS 0x00000400L /* Always show Tohit/Todam */
+#define TR3_INSTA_ART 0x00000800L /* Item must be an artifact */
+#define TR3_FEATHER 0x00001000L /* Feather Falling */
+#define TR3_LITE1 0x00002000L /* lite radius 1 */
+#define TR3_SEE_INVIS 0x00004000L /* See Invisible */
+#define TR3_NORM_ART 0x00008000L /* Artifact in k_info */
+#define TR3_SLOW_DIGEST 0x00010000L /* Item slows down digestion */
+#define TR3_REGEN 0x00020000L /* Item induces regeneration */
+#define TR3_XTRA_MIGHT 0x00040000L /* Bows get extra multiplier */
+#define TR3_XTRA_SHOTS 0x00080000L /* Bows get extra shots */
+#define TR3_IGNORE_ACID 0x00100000L /* Item ignores Acid Damage */
+#define TR3_IGNORE_ELEC 0x00200000L /* Item ignores Elec Damage */
+#define TR3_IGNORE_FIRE 0x00400000L /* Item ignores Fire Damage */
+#define TR3_IGNORE_COLD 0x00800000L /* Item ignores Cold Damage */
+#define TR3_ACTIVATE 0x01000000L /* Item can be activated */
+#define TR3_DRAIN_EXP 0x02000000L /* Item drains Experience */
+#define TR3_TELEPORT 0x04000000L /* Item teleports player */
+#define TR3_AGGRAVATE 0x08000000L /* Item aggravates monsters */
+#define TR3_BLESSED 0x10000000L /* Item is Blessed */
+#define TR3_CURSED 0x20000000L /* Item is Cursed */
+#define TR3_HEAVY_CURSE 0x40000000L /* Item is Heavily Cursed */
+#define TR3_PERMA_CURSE 0x80000000L /* Item is Perma Cursed */
+#define TR3_NULL_MASK 0x00000000L
+
+#define TR4_NEVER_BLOW 0x00000001L /* Weapon can't attack */
+#define TR4_PRECOGNITION 0x00000002L /* Like activating the cheat mode */
+#define TR4_BLACK_BREATH 0x00000004L /* Tolkien's Black Breath */
+#define TR4_RECHARGE 0x00000008L /* For artifact Wands and Staffs */
+#define TR4_FLY 0x00000010L /* This one and ONLY this one allow you to fly over trees */
+#define TR4_DG_CURSE 0x00000020L /* The Ancient Morgothian Curse */
+#define TR4_COULD2H 0x00000040L /* Can wield it 2 Handed */
+#define TR4_MUST2H 0x00000080L /* Must wield it 2 Handed */
+#define TR4_LEVELS 0x00000100L /* Can gain exp/exp levels !! */
+#define TR4_CLONE 0x00000200L /* Can clone monsters */
+#define TR4_SPECIAL_GENE 0x00000400L /* The object can only be generated in special conditions like quests, special dungeons, ... */
+#define TR4_CLIMB 0x00000800L /* Allow climbing mountains */
+#define TR4_FAST_CAST 0x00001000L /* Rod is x2 time faster to use */
+#define TR4_CAPACITY 0x00002000L /* Rod can take x2 mana */
+#define TR4_CHARGING 0x00004000L /* Rod recharge faster */
+#define TR4_CHEAPNESS 0x00008000L /* Rod spells are cheaper(in mana cost) to cast */
+#define TR4_FOUNTAIN 0x00010000L /* Available as fountain (for potions) */
+#define TR4_ANTIMAGIC_50 0x00020000L /* Forbid magic */
+#define TR4_ANTIMAGIC_30 0x00040000L /* Forbid magic */
+#define TR4_ANTIMAGIC_20 0x00080000L /* Forbid magic */
+#define TR4_ANTIMAGIC_10 0x00100000L /* Forbid magic */
+#define TR4_EASY_USE 0x00200000L /* Easily activable */
+#define TR4_IM_NETHER 0x00400000L /* Immunity to nether */
+#define TR4_RECHARGED 0x00800000L /* Object has been recharged once */
+#define TR4_ULTIMATE 0x01000000L /* ULTIMATE artifact */
+#define TR4_AUTO_ID 0x02000000L /* Id stuff on floor */
+#define TR4_LITE2 0x04000000L /* lite radius 2 */
+#define TR4_LITE3 0x08000000L /* lite radius 3 */
+#define TR4_FUEL_LITE 0x10000000L /* fuelable lite */
+#define TR4_ART_EXP 0x20000000L /* Will accumulate xp */
+#define TR4_CURSE_NO_DROP 0x40000000L /* The obj wont be dropped */
+#define TR4_NO_RECHARGE 0x80000000L /* Object Cannot be recharged */
+#define TR4_NULL_MASK 0xFFFFFFFCL
+
+#define TR5_TEMPORARY 0x00000001L /* In timeout turns it is destroyed */
+#define TR5_DRAIN_MANA 0x00000002L /* Drains mana */
+#define TR5_DRAIN_HP 0x00000004L /* Drains hp */
+#define TR5_KILL_DEMON 0x00000008L /* Execute Demon */
+#define TR5_KILL_UNDEAD 0x00000010L /* Execute Undead */
+#define TR5_CRIT 0x00000020L /* More critical hits */
+#define TR5_ATTR_MULTI 0x00000040L /* Object shimmer -- only allowed in k_info */
+#define TR5_WOUNDING 0x00000080L /* Wounds monsters */
+#define TR5_FULL_NAME 0x00000100L /* Uses direct name from k_info */
+#define TR5_LUCK 0x00000200L /* Luck += pval */
+#define TR5_IMMOVABLE 0x00000400L /* Cannot move */
+#define TR5_SPELL_CONTAIN 0x00000800L /* Can contain a spell */
+#define TR5_RES_MORGUL 0x00001000L /* Is not shattered by morgul fiends(nazguls) */
+#define TR5_ACTIVATE_NO_WIELD 0x00002000L /* Can be 'A'ctivated without being wielded */
+#define TR5_MAGIC_BREATH 0x00004000L /* Can breath anywere */
+#define TR5_WATER_BREATH 0x00008000L /* Can breath underwater */
+#define TR5_WIELD_CAST 0x00010000L /* Need to be wielded to cast spelsl fomr it(if it can be wiekded) */
+
+#define ESP_ORC 0x00000001L
+#define ESP_TROLL 0x00000002L
+#define ESP_DRAGON 0x00000004L
+#define ESP_GIANT 0x00000008L
+#define ESP_DEMON 0x00000010L
+#define ESP_UNDEAD 0x00000020L
+#define ESP_EVIL 0x00000040L
+#define ESP_ANIMAL 0x00000080L
+#define ESP_THUNDERLORD 0x00000100L
+#define ESP_GOOD 0x00000200L
+#define ESP_NONLIVING 0x00000400L
+#define ESP_UNIQUE 0x00000800L
+#define ESP_SPIDER 0x00001000L
+#define ESP_ALL 0x80000000L
+
+/*
+ * Bit flags for the "get_item" function
+ */
+#define USE_EQUIP 0x01 /* Allow equip items */
+#define USE_INVEN 0x02 /* Allow inven items */
+#define USE_FLOOR 0x04 /* Allow floor items */
+#define USE_EXTRA 0x08 /* Allow extra items */
+
+#define INVEN_WIELD 24 /* 3 weapons -- WEAPONS */
+#define INVEN_BOW 27 /* 1 bow -- WEAPON */
+#define INVEN_RING 28 /* 6 rings -- FINGER */
+#define INVEN_NECK 34 /* 2 amulets -- HEAD */
+#define INVEN_LITE 36 /* 1 lite -- TORSO */
+#define INVEN_BODY 37 /* 1 body -- TORSO */
+#define INVEN_OUTER 38 /* 1 cloak -- TORSO */
+#define INVEN_ARM 39 /* 3 arms -- ARMS */
+#define INVEN_HEAD 42 /* 2 heads -- HEAD */
+#define INVEN_HANDS 44 /* 3 hands -- ARMS */
+#define INVEN_FEET 47 /* 2 feets -- LEGS */
+#define INVEN_CARRY 49 /* 1 carried monster -- TORSO */
+#define INVEN_AMMO 50 /* 1 quiver -- TORSO */
+#define INVEN_TOOL 51 /* 1 tool -- ARMS */
+#define INVEN_TOTAL 52
+#define INVEN_EQ (INVEN_TOTAL - INVEN_WIELD)
+
+#define TV_SKELETON 1 /* Skeletons ('s') */
+#define TV_BOTTLE 2 /* Empty bottles ('!') */
+#define TV_BATERIE 4 /* For the Alchemists */
+#define TV_SPIKE 5 /* Spikes ('~') */
+#define TV_MSTAFF 6 /* Mage Staffs */
+#define TV_CHEST 7 /* Chests ('~') */
+#define TV_PARCHMENT 8 /* Parchments from Kamband */
+#define TV_PARCHEMENT 8 /* compatibility define */
+#define TV_CORPSE 9 /* Monster corpses */
+#define TV_EGG 10 /* Monster Eggs */
+#define TV_JUNK 11 /* Sticks, Pottery, etc ('~') */
+#define TV_TOOL 12 /* Tools */
+#define TV_INSTRUMENT 14 /* Musical instruments */
+#define TV_BOOMERANG 15 /* Boomerangs */
+#define TV_SHOT 16 /* Ammo for slings */
+#define TV_ARROW 17 /* Ammo for bows */
+#define TV_BOLT 18 /* Ammo for x-bows */
+#define TV_BOW 19 /* Slings/Bows/Xbows */
+#define TV_DIGGING 20 /* Shovels/Picks */
+#define TV_HAFTED 21 /* Priest Weapons */
+#define TV_POLEARM 22 /* Pikes/Glaives/Spears/etc. */
+#define TV_SWORD 23 /* Edged Weapons */
+#define TV_AXE 24 /* Axes/Cleavers */
+#define TV_BOOTS 30 /* Boots */
+#define TV_GLOVES 31 /* Gloves */
+#define TV_HELM 32 /* Helms */
+#define TV_CROWN 33 /* Crowns */
+#define TV_SHIELD 34 /* Shields */
+#define TV_CLOAK 35 /* Cloaks */
+#define TV_SOFT_ARMOR 36 /* Soft Armor */
+#define TV_HARD_ARMOR 37 /* Hard Armor */
+#define TV_DRAG_ARMOR 38 /* Dragon Scale Mail */
+#define TV_LITE 39 /* Lites (including Specials) */
+#define TV_AMULET 40 /* Amulets (including Specials) */
+#define TV_RING 45 /* Rings (including Specials) */
+#define TV_TRAPKIT 46 /* Trapkits */
+#define TV_TOTEM 54 /* Summoner totems */
+#define TV_STAFF 55
+#define TV_WAND 65
+#define TV_ROD 66
+#define TV_ROD_MAIN 67
+#define TV_SCROLL 70
+#define TV_POTION 71
+#define TV_POTION2 72 /* Second set of potion */
+#define TV_FLASK 77
+#define TV_FOOD 80
+#define TV_HYPNOS 99 /* To wield monsters !:) */
+#define TV_GOLD 100 /* Gold can only be picked up by players */
+#define TV_RANDART 102 /* Random Artifacts */
+#define TV_RUNE1 104 /* Base runes */
+#define TV_RUNE2 105 /* Modifier runes */
+
+#define TV_BOOK 111
+#define TV_SYMBIOTIC_BOOK 112
+#define TV_MUSIC_BOOK 113
+#define TV_DRUID_BOOK 114
+#define TV_DAEMON_BOOK 115
+
+/* The "sval" codes for TV_TOOL */
+#define SV_TOOL_CLIMB 0
+#define SV_PORTABLE_HOLE 1
+
+/* The "sval" codes for TV_MSTAFF */
+#define SV_MSTAFF 1
+
+/* The "sval" codes for TV_SHOT/TV_ARROW/TV_BOLT */
+#define SV_AMMO_LIGHT 0 /* pebbles */
+#define SV_AMMO_NORMAL 1 /* shots, arrows, bolts */
+#define SV_AMMO_HEAVY 2 /* seeker arrows and bolts, mithril shots */
+
+/* The "sval" codes for TV_INSTRUMENT */
+#define SV_DRUM 58
+#define SV_HARP 59
+#define SV_HORN 60
+
+/* The "sval" codes for TV_TRAPKIT */
+#define SV_TRAPKIT_SLING 1
+#define SV_TRAPKIT_BOW 2
+#define SV_TRAPKIT_XBOW 3
+#define SV_TRAPKIT_POTION 4
+#define SV_TRAPKIT_SCROLL 5
+#define SV_TRAPKIT_DEVICE 6
+
+/* The "sval" codes for TV_BOOMERANG */
+#define SV_BOOM_S_WOOD 1 /* 1d4 */
+#define SV_BOOM_WOOD 2 /* 1d9 */
+#define SV_BOOM_S_METAL 3 /* 1d8 */
+#define SV_BOOM_METAL 4 /* 2d4 */
+
+/* The "sval" codes for TV_BOW (note information in "sval") */
+#define SV_SLING 2 /* (x2) */
+#define SV_SHORT_BOW 12 /* (x2) */
+#define SV_LONG_BOW 13 /* (x3) */
+#define SV_LIGHT_XBOW 23 /* (x3) */
+#define SV_HEAVY_XBOW 24 /* (x4) */
+
+/* The "sval" codes for TV_DIGGING */
+#define SV_SHOVEL 1
+#define SV_GNOMISH_SHOVEL 2
+#define SV_DWARVEN_SHOVEL 3
+#define SV_PICK 4
+#define SV_ORCISH_PICK 5
+#define SV_DWARVEN_PICK 6
+#define SV_MATTOCK 7
+
+/* The "sval" values for TV_HAFTED */
+#define SV_CLUB 1 /* 1d4 */
+#define SV_WHIP 2 /* 1d6 */
+#define SV_QUARTERSTAFF 3 /* 1d9 */
+#define SV_NUNCHAKU 4 /* 2d3 */
+#define SV_MACE 5 /* 2d4 */
+#define SV_BALL_AND_CHAIN 6 /* 2d4 */
+#define SV_WAR_HAMMER 8 /* 3d3 */
+#define SV_LUCERN_HAMMER 10 /* 2d5 */
+#define SV_THREE_PIECE_ROD 11 /* 3d3 */
+#define SV_MORNING_STAR 12 /* 2d6 */
+#define SV_FLAIL 13 /* 2d6 */
+#define SV_LEAD_FILLED_MACE 15 /* 3d4 */
+#define SV_TWO_HANDED_FLAIL 18 /* 3d6 */
+#define SV_GREAT_HAMMER 19 /* 4d6 */
+#define SV_MACE_OF_DISRUPTION 20 /* 5d8 */
+#define SV_GROND 50 /* 3d4 */
+
+/* The "sval" values for TV_AXE */
+#define SV_HATCHET 1 /* 1d5 */
+#define SV_CLEAVER 2 /* 2d4 */
+#define SV_LIGHT_WAR_AXE 8 /* 2d5 */
+#define SV_BEAKED_AXE 10 /* 2d6 */
+#define SV_BROAD_AXE 11 /* 2d6 */
+#define SV_BATTLE_AXE 22 /* 2d8 */
+#define SV_GREAT_AXE 25 /* 4d4 */
+#define SV_LOCHABER_AXE 28 /* 3d8 */
+#define SV_SLAUGHTER_AXE 30 /* 5d7 */
+
+/* The "sval" values for TV_POLEARM */
+#define SV_SPEAR 2 /* 1d6 */
+#define SV_SICKLE 3 /* 2d3 */
+#define SV_AWL_PIKE 4 /* 1d8 */
+#define SV_TRIDENT 5 /* 1d9 */
+#define SV_FAUCHARD 6 /* 1d10 */
+#define SV_BROAD_SPEAR 7 /* 1d9 */
+#define SV_PIKE 8 /* 2d5 */
+#define SV_GLAIVE 13 /* 2d6 */
+#define SV_HALBERD 15 /* 3d4 */
+#define SV_GUISARME 16 /* 2d5 */
+#define SV_SCYTHE 17 /* 5d3 */
+#define SV_LANCE 20 /* 2d8 */
+#define SV_TRIFURCATE_SPEAR 26 /* 2d9 */
+#define SV_HEAVY_LANCE 29 /* 4d8 */
+#define SV_SCYTHE_OF_SLICING 30 /* 8d4 */
+
+/* The "sval" codes for TV_SWORD */
+#define SV_BROKEN_DAGGER 1 /* 1d1 */
+#define SV_BROKEN_SWORD 2 /* 1d2 */
+#define SV_DAGGER 4 /* 1d4 */
+#define SV_MAIN_GAUCHE 5 /* 1d5 */
+#define SV_RAPIER 7 /* 1d6 */
+#define SV_SMALL_SWORD 8 /* 1d6 */
+#define SV_BASILLARD 9 /* 1d8 */
+#define SV_SHORT_SWORD 10 /* 1d7 */
+#define SV_SABRE 11 /* 1d7 */
+#define SV_CUTLASS 12 /* 1d7 */
+#define SV_KHOPESH 14 /* 2d4 */
+#define SV_TULWAR 15 /* 2d4 */
+#define SV_BROAD_SWORD 16 /* 2d5 */
+#define SV_LONG_SWORD 17 /* 2d5 */
+#define SV_SCIMITAR 18 /* 2d5 */
+#define SV_KATANA 20 /* 3d4 */
+#define SV_BASTARD_SWORD 21 /* 3d4 */
+#define SV_GREAT_SCIMITAR 22 /* 4d5 */
+#define SV_CLAYMORE 23 /* 2d8 */
+#define SV_ESPADON 24 /* 2d9 */
+#define SV_TWO_HANDED_SWORD 25 /* 3d6 */
+#define SV_FLAMBERGE 26 /* 3d7 */
+#define SV_EXECUTIONERS_SWORD 28 /* 4d5 */
+#define SV_ZWEIHANDER 29 /* 4d6 */
+#define SV_BLADE_OF_CHAOS 30 /* 6d5 */
+#define SV_BLUESTEEL_BLADE 31 /* 3d9 */
+#define SV_SHADOW_BLADE 32 /* 4d4 */
+#define SV_DARK_SWORD 33 /* 3d7 */
+
+/* The "sval" codes for TV_SHIELD */
+#define SV_SMALL_LEATHER_SHIELD 2
+#define SV_SMALL_METAL_SHIELD 3
+#define SV_LARGE_LEATHER_SHIELD 4
+#define SV_LARGE_METAL_SHIELD 5
+#define SV_DRAGON_SHIELD 6
+#define SV_SHIELD_OF_DEFLECTION 10
+
+/* The "sval" codes for TV_HELM */
+#define SV_HARD_LEATHER_CAP 2
+#define SV_METAL_CAP 3
+#define SV_IRON_HELM 5
+#define SV_STEEL_HELM 6
+#define SV_DRAGON_HELM 7
+#define SV_IRON_CROWN 10
+#define SV_GOLDEN_CROWN 11
+#define SV_JEWELED_CROWN 12
+#define SV_MORGOTH 50
+
+/* The "sval" codes for TV_BOOTS */
+#define SV_PAIR_OF_SOFT_LEATHER_BOOTS 2
+#define SV_PAIR_OF_HARD_LEATHER_BOOTS 3
+#define SV_PAIR_OF_METAL_SHOD_BOOTS 6
+
+/* The "sval" codes for TV_CLOAK */
+#define SV_CLOAK 1
+#define SV_ELVEN_CLOAK 2
+#define SV_FUR_CLOAK 3
+#define SV_SHADOW_CLOAK 6
+
+/* The "sval" codes for TV_GLOVES */
+#define SV_SET_OF_LEATHER_GLOVES 1
+#define SV_SET_OF_GAUNTLETS 2
+#define SV_SET_OF_CESTI 5
+
+/* The "sval" codes for TV_SOFT_ARMOR */
+#define SV_FILTHY_RAG 1
+#define SV_ROBE 2
+#define SV_PAPER_ARMOR 3 /* 4 */
+#define SV_SOFT_LEATHER_ARMOR 4
+#define SV_SOFT_STUDDED_LEATHER 5
+#define SV_HARD_LEATHER_ARMOR 6
+#define SV_HARD_STUDDED_LEATHER 7
+#define SV_RHINO_HIDE_ARMOR 8
+#define SV_CORD_ARMOR 9 /* 6 */
+#define SV_PADDED_ARMOR 10 /* 4 */
+#define SV_LEATHER_SCALE_MAIL 11
+#define SV_LEATHER_JACK 12
+#define SV_STONE_AND_HIDE_ARMOR 15 /* 15 */
+#define SV_THUNDERLORD_SUIT 16
+
+/* The "sval" codes for TV_HARD_ARMOR */
+#define SV_RUSTY_CHAIN_MAIL 1 /* 14- */
+#define SV_RING_MAIL 2 /* 12 */
+#define SV_METAL_SCALE_MAIL 3 /* 13 */
+#define SV_CHAIN_MAIL 4 /* 14 */
+#define SV_DOUBLE_RING_MAIL 5 /* 15 */
+#define SV_AUGMENTED_CHAIN_MAIL 6 /* 16 */
+#define SV_DOUBLE_CHAIN_MAIL 7 /* 16 */
+#define SV_BAR_CHAIN_MAIL 8 /* 18 */
+#define SV_METAL_BRIGANDINE_ARMOUR 9 /* 19 */
+#define SV_SPLINT_MAIL 10 /* 19 */
+#define SV_PARTIAL_PLATE_ARMOUR 12 /* 22 */
+#define SV_METAL_LAMELLAR_ARMOUR 13 /* 23 */
+#define SV_FULL_PLATE_ARMOUR 15 /* 25 */
+#define SV_RIBBED_PLATE_ARMOUR 18 /* 28 */
+#define SV_MITHRIL_CHAIN_MAIL 20 /* 28+ */
+#define SV_MITHRIL_PLATE_MAIL 25 /* 35+ */
+#define SV_ADAMANTITE_PLATE_MAIL 30 /* 40+ */
+
+/* The "sval" codes for TV_DRAG_ARMOR */
+#define SV_DRAGON_BLACK 1
+#define SV_DRAGON_BLUE 2
+#define SV_DRAGON_WHITE 3
+#define SV_DRAGON_RED 4
+#define SV_DRAGON_GREEN 5
+#define SV_DRAGON_MULTIHUED 6
+#define SV_DRAGON_SHINING 10
+#define SV_DRAGON_LAW 12
+#define SV_DRAGON_BRONZE 14
+#define SV_DRAGON_GOLD 16
+#define SV_DRAGON_CHAOS 18
+#define SV_DRAGON_BALANCE 20
+#define SV_DRAGON_POWER 30
+
+/* The sval codes for TV_LITE */
+#define SV_LITE_TORCH 0
+#define SV_LITE_LANTERN 1
+#define SV_LITE_TORCH_EVER 2
+#define SV_LITE_DWARVEN 3
+#define SV_LITE_FEANORIAN 4
+#define SV_LITE_GALADRIEL 100
+#define SV_LITE_ELENDIL 101
+#define SV_LITE_THRAIN 102
+#define SV_LITE_UNDEATH 103
+#define SV_LITE_PALANTIR 104
+#define SV_ANCHOR_SPACETIME 105
+#define SV_STONE_LORE 106
+
+
+/* The "sval" codes for TV_AMULET */
+#define SV_AMULET_DOOM 0
+#define SV_AMULET_TELEPORT 1
+#define SV_AMULET_ADORNMENT 2
+#define SV_AMULET_SLOW_DIGEST 3
+#define SV_AMULET_RESIST_ACID 4
+#define SV_AMULET_SEARCHING 5
+#define SV_AMULET_BRILLANCE 6
+#define SV_AMULET_CHARISMA 7
+#define SV_AMULET_THE_MAGI 8
+#define SV_AMULET_REFLECTION 9
+#define SV_AMULET_CARLAMMAS 10
+#define SV_AMULET_INGWE 11
+#define SV_AMULET_DWARVES 12
+#define SV_AMULET_NO_MAGIC 13
+#define SV_AMULET_NO_TELE 14
+#define SV_AMULET_RESISTANCE 15
+#define SV_AMULET_NOTHING 16
+#define SV_AMULET_SERPENT 17
+#define SV_AMULET_TORIS_MEJISTOS 18
+#define SV_AMULET_TRICKERY 23
+#define SV_AMULET_DEVOTION 25
+#define SV_AMULET_WEAPONMASTERY 24
+#define SV_AMULET_WISDOM 28
+#define SV_AMULET_INFRA 26
+#define SV_AMULET_SPELL 27
+
+/* The sval codes for TV_RING */
+#define SV_RING_WOE 0
+#define SV_RING_AGGRAVATION 1
+#define SV_RING_WEAKNESS 2
+#define SV_RING_STUPIDITY 3
+#define SV_RING_TELEPORTATION 4
+#define SV_RING_SPECIAL 5
+#define SV_RING_SLOW_DIGESTION 6
+#define SV_RING_FEATHER_FALL 7
+#define SV_RING_RESIST_FIRE 8
+#define SV_RING_RESIST_COLD 9
+#define SV_RING_SUSTAIN_STR 10
+#define SV_RING_SUSTAIN_INT 11
+#define SV_RING_SUSTAIN_WIS 12
+#define SV_RING_SUSTAIN_DEX 13
+#define SV_RING_SUSTAIN_CON 14
+#define SV_RING_SUSTAIN_CHR 15
+#define SV_RING_PROTECTION 16
+#define SV_RING_ACID 17
+#define SV_RING_FLAMES 18
+#define SV_RING_ICE 19
+#define SV_RING_RESIST_POIS 20
+#define SV_RING_FREE_ACTION 21
+#define SV_RING_SEE_INVIS 22
+#define SV_RING_SEARCHING 23
+#define SV_RING_STR 24
+#define SV_RING_INT 25
+#define SV_RING_DEX 26
+#define SV_RING_CON 27
+#define SV_RING_ACCURACY 28
+#define SV_RING_DAMAGE 29
+#define SV_RING_SLAYING 30
+#define SV_RING_SPEED 31
+#define SV_RING_BARAHIR 32
+#define SV_RING_TULKAS 33
+#define SV_RING_NARYA 34
+#define SV_RING_NENYA 35
+#define SV_RING_VILYA 36
+#define SV_RING_POWER 37
+#define SV_RING_RES_FEAR 38
+#define SV_RING_RES_LD 39
+#define SV_RING_RES_NETHER 40
+#define SV_RING_RES_NEXUS 41
+#define SV_RING_RES_SOUND 42
+#define SV_RING_RES_CONFUSION 43
+#define SV_RING_RES_SHARDS 44
+#define SV_RING_RES_DISENCHANT 45
+#define SV_RING_RES_CHAOS 46
+#define SV_RING_RES_BLINDNESS 47
+#define SV_RING_LORDLY 48
+#define SV_RING_ATTACKS 49
+#define SV_RING_NOTHING 50
+#define SV_RING_PRECONITION 51
+#define SV_RING_FLAR 52
+#define SV_RING_INVIS 53
+#define SV_RING_FLYING 54
+#define SV_RING_WRAITH 55
+#define SV_RING_ELEC 56
+#define SV_RING_CRIT 57
+#define SV_RING_SPELL 58
+
+/* The "sval" codes for TV_STAFF */
+#define SV_STAFF_SCHOOL 1
+#define SV_STAFF_NOTHING 2
+
+/* The "sval" codes for TV_WAND */
+#define SV_WAND_SCHOOL 1
+#define SV_WAND_NOTHING 2
+
+/* The "sval" codes for TV_ROD(Rod Tips) */
+#define SV_ROD_NOTHING 0
+#define SV_ROD_DETECT_DOOR 1
+#define SV_ROD_IDENTIFY 2
+#define SV_ROD_RECALL 3
+#define SV_ROD_ILLUMINATION 4
+#define SV_ROD_MAPPING 5
+#define SV_ROD_DETECTION 6
+#define SV_ROD_PROBING 7
+#define SV_ROD_CURING 8
+#define SV_ROD_HEALING 9
+#define SV_ROD_RESTORATION 10
+#define SV_ROD_SPEED 11
+/* xxx (aimed) */
+#define SV_ROD_TELEPORT_AWAY 13
+#define SV_ROD_DISARMING 14
+#define SV_ROD_LITE 15
+#define SV_ROD_SLEEP_MONSTER 16
+#define SV_ROD_SLOW_MONSTER 17
+#define SV_ROD_DRAIN_LIFE 18
+#define SV_ROD_POLYMORPH 19
+#define SV_ROD_ACID_BOLT 20
+#define SV_ROD_ELEC_BOLT 21
+#define SV_ROD_FIRE_BOLT 22
+#define SV_ROD_COLD_BOLT 23
+#define SV_ROD_ACID_BALL 24
+#define SV_ROD_ELEC_BALL 25
+#define SV_ROD_FIRE_BALL 26
+#define SV_ROD_COLD_BALL 27
+#define SV_ROD_HAVOC 28
+#define SV_ROD_DETECT_TRAP 29
+#define SV_ROD_HOME 30
+
+
+/* The "sval" codes for TV_ROD_MAIN(Rods) */
+/* Note that the sval is the max mana capacity of the rod */
+
+#define SV_ROD_WOODEN 10
+#define SV_ROD_COPPER 20
+#define SV_ROD_IRON 50
+#define SV_ROD_ALUMINIUM 75
+#define SV_ROD_SILVER 100
+#define SV_ROD_GOLDEN 125
+#define SV_ROD_MITHRIL 160
+#define SV_ROD_ADMANTITE 200
+
+
+/* The "sval" codes for TV_SCROLL */
+
+#define SV_SCROLL_DARKNESS 0
+#define SV_SCROLL_AGGRAVATE_MONSTER 1
+#define SV_SCROLL_CURSE_ARMOR 2
+#define SV_SCROLL_CURSE_WEAPON 3
+#define SV_SCROLL_SUMMON_MONSTER 4
+#define SV_SCROLL_SUMMON_UNDEAD 5
+#define SV_SCROLL_SUMMON_MINE 6
+#define SV_SCROLL_TRAP_CREATION 7
+#define SV_SCROLL_PHASE_DOOR 8
+#define SV_SCROLL_TELEPORT 9
+#define SV_SCROLL_TELEPORT_LEVEL 10
+#define SV_SCROLL_WORD_OF_RECALL 11
+#define SV_SCROLL_IDENTIFY 12
+#define SV_SCROLL_STAR_IDENTIFY 13
+#define SV_SCROLL_REMOVE_CURSE 14
+#define SV_SCROLL_STAR_REMOVE_CURSE 15
+#define SV_SCROLL_ENCHANT_ARMOR 16
+#define SV_SCROLL_ENCHANT_WEAPON_TO_HIT 17
+#define SV_SCROLL_ENCHANT_WEAPON_TO_DAM 18
+#define SV_SCROLL_ENCHANT_WEAPON_PVAL 19
+#define SV_SCROLL_STAR_ENCHANT_ARMOR 20
+#define SV_SCROLL_STAR_ENCHANT_WEAPON 21
+#define SV_SCROLL_RECHARGING 22
+#define SV_SCROLL_RESET_RECALL 23
+#define SV_SCROLL_LIGHT 24
+#define SV_SCROLL_MAPPING 25
+#define SV_SCROLL_DETECT_GOLD 26
+#define SV_SCROLL_DETECT_ITEM 27
+#define SV_SCROLL_DETECT_TRAP 28
+#define SV_SCROLL_DETECT_DOOR 29
+#define SV_SCROLL_DETECT_INVIS 30
+#define SV_SCROLL_DIVINATION 31
+#define SV_SCROLL_SATISFY_HUNGER 32
+#define SV_SCROLL_BLESSING 33
+#define SV_SCROLL_HOLY_CHANT 34
+#define SV_SCROLL_HOLY_PRAYER 35
+#define SV_SCROLL_MONSTER_CONFUSION 36
+#define SV_SCROLL_PROTECTION_FROM_EVIL 37
+#define SV_SCROLL_RUNE_OF_PROTECTION 38
+#define SV_SCROLL_TRAP_DOOR_DESTRUCTION 39
+#define SV_SCROLL_DEINCARNATION 40
+#define SV_SCROLL_STAR_DESTRUCTION 41
+#define SV_SCROLL_DISPEL_UNDEAD 42
+#define SV_SCROLL_MASS_RESURECTION 43
+#define SV_SCROLL_GENOCIDE 44
+#define SV_SCROLL_MASS_GENOCIDE 45
+#define SV_SCROLL_ACQUIREMENT 46
+#define SV_SCROLL_STAR_ACQUIREMENT 47
+#define SV_SCROLL_FIRE 48
+#define SV_SCROLL_ICE 49
+#define SV_SCROLL_CHAOS 50
+#define SV_SCROLL_RUMOR 51
+#define SV_SCROLL_ARTIFACT 52
+#define SV_SCROLL_NOTHING 53
+
+/* The "sval" codes for TV_POTION */
+#define SV_POTION_WATER 0
+#define SV_POTION_APPLE_JUICE 1
+#define SV_POTION_SLIME_MOLD 2
+#define SV_POTION_BLOOD 3
+#define SV_POTION_SLOWNESS 4
+#define SV_POTION_SALT_WATER 5
+#define SV_POTION_POISON 6
+#define SV_POTION_BLINDNESS 7
+#define SV_POTION_INVIS 8
+#define SV_POTION_CONFUSION 9
+#define SV_POTION_MUTATION 10
+#define SV_POTION_SLEEP 11
+#define SV_POTION_LEARNING 12
+#define SV_POTION_LOSE_MEMORIES 13
+/* xxx */
+#define SV_POTION_RUINATION 15
+#define SV_POTION_DEC_STR 16
+#define SV_POTION_DEC_INT 17
+#define SV_POTION_DEC_WIS 18
+#define SV_POTION_DEC_DEX 19
+#define SV_POTION_DEC_CON 20
+#define SV_POTION_DEC_CHR 21
+#define SV_POTION_DETONATIONS 22
+#define SV_POTION_DEATH 23
+#define SV_POTION_INFRAVISION 24
+#define SV_POTION_DETECT_INVIS 25
+#define SV_POTION_SLOW_POISON 26
+#define SV_POTION_CURE_POISON 27
+#define SV_POTION_BOLDNESS 28
+#define SV_POTION_SPEED 29
+#define SV_POTION_RESIST_HEAT 30
+#define SV_POTION_RESIST_COLD 31
+#define SV_POTION_HEROISM 32
+#define SV_POTION_BESERK_STRENGTH 33
+#define SV_POTION_CURE_LIGHT 34
+#define SV_POTION_CURE_SERIOUS 35
+#define SV_POTION_CURE_CRITICAL 36
+#define SV_POTION_HEALING 37
+#define SV_POTION_STAR_HEALING 38
+#define SV_POTION_LIFE 39
+#define SV_POTION_RESTORE_MANA 40
+#define SV_POTION_RESTORE_EXP 41
+#define SV_POTION_RES_STR 42
+#define SV_POTION_RES_INT 43
+#define SV_POTION_RES_WIS 44
+#define SV_POTION_RES_DEX 45
+#define SV_POTION_RES_CON 46
+#define SV_POTION_RES_CHR 47
+#define SV_POTION_INC_STR 48
+#define SV_POTION_INC_INT 49
+#define SV_POTION_INC_WIS 50
+#define SV_POTION_INC_DEX 51
+#define SV_POTION_INC_CON 52
+#define SV_POTION_INC_CHR 53
+/* xxx */
+#define SV_POTION_AUGMENTATION 55
+#define SV_POTION_ENLIGHTENMENT 56
+#define SV_POTION_STAR_ENLIGHTENMENT 57
+#define SV_POTION_SELF_KNOWLEDGE 58
+#define SV_POTION_EXPERIENCE 59
+#define SV_POTION_RESISTANCE 60
+#define SV_POTION_CURING 61
+#define SV_POTION_INVULNERABILITY 62
+#define SV_POTION_NEW_LIFE 63
+
+#define SV_POTION_LAST 63
+
+/* The "sval" codes for TV_POTION2 */
+#define SV_POTION2_MIMIC 1
+#define SV_POTION2_CURE_LIGHT_SANITY 14
+#define SV_POTION2_CURE_SERIOUS_SANITY 15
+#define SV_POTION2_CURE_CRITICAL_SANITY 16
+#define SV_POTION2_CURE_SANITY 17
+#define SV_POTION2_CURE_WATER 18
+
+#define SV_POTION2_LAST 18
+
+/* The "sval" codes for TV_FOOD */
+#define SV_FOOD_POISON 0
+#define SV_FOOD_BLINDNESS 1
+#define SV_FOOD_PARANOIA 2
+#define SV_FOOD_CONFUSION 3
+#define SV_FOOD_HALLUCINATION 4
+#define SV_FOOD_PARALYSIS 5
+#define SV_FOOD_WEAKNESS 6
+#define SV_FOOD_SICKNESS 7
+#define SV_FOOD_STUPIDITY 8
+#define SV_FOOD_NAIVETY 9
+#define SV_FOOD_UNHEALTH 10
+#define SV_FOOD_DISEASE 11
+#define SV_FOOD_CURE_POISON 12
+#define SV_FOOD_CURE_BLINDNESS 13
+#define SV_FOOD_CURE_PARANOIA 14
+#define SV_FOOD_CURE_CONFUSION 15
+#define SV_FOOD_CURE_SERIOUS 16
+#define SV_FOOD_RESTORE_STR 17
+#define SV_FOOD_RESTORE_CON 18
+#define SV_FOOD_RESTORING 19
+/* many missing mushrooms */
+#define SV_FOOD_BISCUIT 32
+#define SV_FOOD_JERKY 33
+#define SV_FOOD_RATION 35
+#define SV_FOOD_SLIME_MOLD 36
+#define SV_FOOD_WAYBREAD 37
+#define SV_FOOD_PINT_OF_ALE 38
+#define SV_FOOD_PINT_OF_WINE 39
+#define SV_FOOD_ATHELAS 40
+#define SV_FOOD_GREAT_HEALTH 41
+#define SV_FOOD_FORTUNE_COOKIE 42
+
+/* The "sval" codes for TV_BATERIE */
+#define SV_BATERIE_POISON 1
+#define SV_BATERIE_EXPLOSION 2
+#define SV_BATERIE_TELEPORT 3
+#define SV_BATERIE_COLD 4
+#define SV_BATERIE_FIRE 5
+#define SV_BATERIE_ACID 6
+#define SV_BATERIE_LIFE 7
+#define SV_BATERIE_CONFUSION 8
+#define SV_BATERIE_LITE 9
+#define SV_BATERIE_CHAOS 10
+#define SV_BATERIE_TIME 11
+#define SV_BATERIE_MAGIC 12
+#define SV_BATERIE_XTRA_LIFE 13
+#define SV_BATERIE_DARKNESS 14
+#define SV_BATERIE_KNOWLEDGE 15
+#define SV_BATERIE_FORCE 16
+#define SV_BATERIE_LIGHTNING 17
+#define SV_BATERIE_MANA 18
+
+/* The "sval" codes for TV_CORPSE */
+#define SV_CORPSE_CORPSE 1
+#define SV_CORPSE_SKELETON 2
+#define SV_CORPSE_HEAD 3
+#define SV_CORPSE_SKULL 4
+#define SV_CORPSE_MEAT 5
+
+/* The "sval" codes for TV_DAEMON_BOOK */
+#define SV_DEMONBLADE 55
+#define SV_DEMONSHIELD 56
+#define SV_DEMONHORN 57
+
+/*
+ * Special Object Flags
+ */
+#define IDENT_SENSE 0x01 /* Item has been "sensed" */
+#define IDENT_FIXED 0x02 /* Item has been "haggled" */
+#define IDENT_EMPTY 0x04 /* Item charges are known */
+#define IDENT_KNOWN 0x08 /* Item abilities are known */
+#define IDENT_STOREB 0x10 /* Item is storebought !!!! */
+#define IDENT_MENTAL 0x20 /* Item information is known */
+#define IDENT_CURSED 0x40 /* Item is temporarily cursed */
+
+/*
+ * Location of objects when they were found
+ */
+#define OBJ_FOUND_MONSTER 1
+#define OBJ_FOUND_FLOOR 2
+#define OBJ_FOUND_VAULT 3
+#define OBJ_FOUND_SPECIAL 4
+#define OBJ_FOUND_RUBBLE 5
+#define OBJ_FOUND_REWARD 6
+#define OBJ_FOUND_STORE 7
+#define OBJ_FOUND_STOLEN 8
+#define OBJ_FOUND_SELFMADE 9
+
+struct obj_theme
+{
+ byte treasure;
+ byte combat;
+ byte magic;
+ byte tools;
+};
+
+struct object_kind
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte tval; /* Object type */
+ byte sval; /* Object sub type */
+
+ s32b pval; /* Object extra info */
+ s32b pval2; /* Object extra info */
+
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to damage */
+ s16b to_a; /* Bonus to armor */
+
+ s16b ac; /* Base armor */
+
+ byte dd;
+ byte ds; /* Damage dice/sides */
+
+ s32b weight; /* Weight */
+
+ s32b cost; /* Object "base cost" */
+
+ u32b flags1; /* Flags, set 1 */
+ u32b flags2; /* Flags, set 2 */
+ u32b flags3; /* Flags, set 3 */
+ u32b flags4; /* Flags, set 4 */
+ u32b flags5; /* Flags, set 5 */
+
+ byte locale[4]; /* Allocation level(s) */
+ byte chance[4]; /* Allocation chance(s) */
+
+ byte level; /* Level */
+ byte extra; /* Something */
+
+
+ byte d_attr; /* Default object attribute */
+ char d_char; /* Default object character */
+
+
+ byte x_attr; /* Desired object attribute */
+ char x_char; /* Desired object character */
+
+
+ byte flavor; /* Special object flavor (or zero) */
+
+ bool easy_know; /* This object is always known (if aware) */
+
+
+ bool aware; /* The player is "aware" of the item's effects */
+
+ bool tried; /* The player has "tried" one of the items */
+
+ bool know; /* extractable flag for the alchemist */
+
+ u32b esp; /* ESP flags */
+
+ byte btval; /* Become Object type */
+ byte bsval; /* Become Object sub type */
+ bool artifact; /* Is it a normal artifact(already generated) */
+
+ s16b power; /* Power granted(if any) */
+};
+
+struct artifact_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte tval; /* Artifact type */
+ byte sval; /* Artifact sub type */
+
+ s16b pval; /* Artifact extra info */
+
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to damage */
+ s16b to_a; /* Bonus to armor */
+
+ s16b ac; /* Base armor */
+
+ byte dd;
+ byte ds; /* Damage when hits */
+
+ s16b weight; /* Weight */
+
+ s32b cost; /* Artifact "cost" */
+
+ u32b flags1; /* Artifact Flags, set 1 */
+ u32b flags2; /* Artifact Flags, set 2 */
+ u32b flags3; /* Artifact Flags, set 3 */
+ u32b flags4; /* Artifact Flags, set 4 */
+ u32b flags5; /* Artifact Flags, set 5 */
+
+ byte level; /* Artifact level */
+ byte rarity; /* Artifact rarity */
+
+ byte cur_num; /* Number created (0 or 1) */
+ byte max_num; /* Unused (should be "1") */
+
+ u32b esp; /* ESP flags */
+
+ s16b power; /* Power granted(if any) */
+};
+
+struct ego_item_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ bool before; /* Before or after the object name ? */
+
+ byte tval[6];
+ byte min_sval[6];
+ byte max_sval[6];
+
+ byte rating; /* Rating boost */
+
+ byte level; /* Minimum level */
+ byte rarity; /* Object rarity */
+ byte mrarity; /* Object rarity */
+
+ s16b max_to_h; /* Maximum to-hit bonus */
+ s16b max_to_d; /* Maximum to-dam bonus */
+ s16b max_to_a; /* Maximum to-ac bonus */
+
+ s32b max_pval; /* Maximum pval */
+
+ s32b cost; /* Ego-item "cost" */
+
+ byte rar[5];
+ u32b flags1[5]; /* Ego-Item Flags, set 1 */
+ u32b flags2[5]; /* Ego-Item Flags, set 2 */
+ u32b flags3[5]; /* Ego-Item Flags, set 3 */
+ u32b flags4[5]; /* Ego-Item Flags, set 4 */
+ u32b flags5[5]; /* Ego-Item Flags, set 5 */
+ u32b esp[5]; /* ESP flags */
+ u32b fego[5]; /* ego flags */
+
+ s16b power; /* Power granted(if any) */
+};
+
+struct object_type
+{
+ s16b k_idx; /* Kind index (zero if "dead") */
+
+ byte iy; /* Y-position on map, or zero */
+ byte ix; /* X-position on map, or zero */
+
+ byte tval; /* Item type (from kind) */
+ byte sval; /* Item sub-type (from kind) */
+
+ s32b pval; /* Item extra-parameter */
+ s16b pval2; /* Item extra-parameter for some special
+ items*/
+ s32b pval3; /* Item extra-parameter for some special
+ items*/
+
+ byte discount; /* Discount (if any) */
+
+ byte number; /* Number of items */
+
+ s32b weight; /* Item weight */
+
+ byte elevel; /* Item exp level */
+ s32b exp; /* Item exp */
+
+ byte name1; /* Artifact type, if any */
+ s16b name2; /* Ego-Item type, if any */
+ s16b name2b; /* Second Ego-Item type, if any */
+
+ byte xtra1; /* Extra info type */
+ s16b xtra2; /* Extra info index */
+
+ s16b to_h; /* Plusses to hit */
+ s16b to_d; /* Plusses to damage */
+ s16b to_a; /* Plusses to AC */
+
+ s16b ac; /* Normal AC */
+
+ byte dd;
+ byte ds; /* Damage dice/sides */
+
+ s16b timeout; /* Timeout Counter */
+
+ byte ident; /* Special flags */
+
+ byte marked; /* Object is marked */
+
+ u16b note; /* Inscription index */
+ u16b art_name; /* Artifact name (random artifacts) */
+
+ u32b art_flags1; /* Flags, set 1 Alas, these were necessary */
+ u32b art_flags2; /* Flags, set 2 for the random artifacts of*/
+ u32b art_flags3; /* Flags, set 3 Zangband */
+ u32b art_flags4; /* Flags, set 4 PernAngband */
+ u32b art_flags5; /* Flags, set 5 PernAngband */
+ u32b art_esp; /* Flags, set esp PernAngband */
+
+ s16b next_o_idx; /* Next object in stack (if any) */
+
+ s16b held_m_idx; /* Monster holding us (if any) */
+
+ byte sense; /* Pseudo-id status */
+
+ byte found; /* How did we find it */
+ s16b found_aux1; /* Stores info for found */
+ s16b found_aux2; /* Stores info for found */
+};
+
+/* Pseudo-id defines */
+#define SENSE_NONE 0
+#define SENSE_CURSED 1
+#define SENSE_AVERAGE 2
+#define SENSE_GOOD_LIGHT 3
+#define SENSE_GOOD_HEAVY 4
+#define SENSE_EXCELLENT 5
+#define SENSE_WORTHLESS 6
+#define SENSE_TERRIBLE 7
+#define SENSE_SPECIAL 8
+#define SENSE_BROKEN 9
+#define SENSE_UNCURSED 10
+
+extern object_type o_list[max_o_idx];
+extern object_kind k_info[max_k_idx];
+extern char *k_name;
+extern char *k_text;
+extern artifact_type a_info[max_a_idx];
+extern char *a_name;
+extern char *a_text;
+extern header *e_head;
+extern ego_item_type e_info[max_e_idx];
+extern char *e_name;
+extern char *e_text;
+
+extern s16b m_bonus(int max, int level);
+extern s16b wield_slot_ideal(object_type *o_ptr, bool ideal);
+extern s16b wield_slot(object_type *o_ptr);
+extern void object_flags(object_type *o_ptr, u32b *f1 = 0, u32b *f2 = 0, u32b *f3 = 0, u32b *f4 = 0, u32b *f5 = 0, u32b *esp = 0);
+extern char *lua_object_desc @ object_desc(object_type *o_ptr, int pref, int mode);
+extern bool object_out_desc(object_type *o_ptr, FILE *fff, bool trim_down, bool wait_for_it);
+extern void inven_item_describe(int item);
+extern void inven_item_increase(int item, int num);
+extern bool inven_item_optimize(int item);
+extern void floor_item_describe(int item);
+extern void floor_item_increase(int item, int num);
+extern void floor_item_optimize(int item);
+extern void delete_object_idx(int o_idx);
+extern s16b o_pop(void);
+extern errr get_obj_num_prep(void);
+extern bool ident_all(void);
+extern s16b get_obj_num(int level);
+extern s16b lookup_kind(int tval, int sval);
+extern void object_wipe(object_type *o_ptr);
+extern void object_prep(object_type *o_ptr, int k_idx);
+extern void object_copy(object_type *o_ptr, object_type *j_ptr);
+extern bool inven_carry_okay(object_type *o_ptr);
+extern void apply_magic(object_type *o_ptr, int lev, bool okay, bool good, bool great);
+extern bool make_object(object_type *j_ptr, bool good, bool great, obj_theme theme);
+extern s16b drop_near(object_type *o_ptr, int chance, int y, int x);
+extern object_type *get_object(int item);
+extern object_type *new_object();
+extern void end_object(object_type *o_ptr);
+extern bool get_item @ get_item_aux(int *cp, cptr pmt, cptr str, int mode);
+extern void lua_set_item_tester(int tval, char *fct);
+extern bool is_magestaff();
+extern void identify_pack_fully(void);
+extern s16b inven_carry(object_type *o_ptr, bool final);
+extern s32b calc_total_weight(void);
+extern int get_slot(int slot);
+extern bool is_blessed(object_type *o_ptr);
+extern cptr sense_desc[1000]; /* 1000 is just a hack for tolua */
+extern void object_pickup(int this_o_idx);
+
+$static bool lua_is_artifact(object_type *o_ptr) { return artifact_p(o_ptr); }
+static bool lua_is_artifact@is_artifact(object_type *o_ptr);
+
+$static bool lua_is_aware(object_type *o_ptr) { return object_aware_p(o_ptr); }
+static bool lua_is_aware@is_aware(object_type *o_ptr);
+
+$static bool lua_is_known(object_type *o_ptr) { return object_known_p(o_ptr); }
+static bool lua_is_known@is_known(object_type *o_ptr);
+
+$static void lua_set_aware(object_type *o_ptr) { object_aware(o_ptr); }
+static void lua_set_aware@set_aware(object_type *o_ptr);
+
+$static void lua_set_known(object_type *o_ptr) { object_known(o_ptr); }
+static void lua_set_known@set_known(object_type *o_ptr);
+
+extern byte value_check_aux1(object_type *o_ptr);
+extern byte value_check_aux1_magic(object_type *o_ptr);
+extern byte value_check_aux2(object_type *o_ptr);
+extern byte value_check_aux2_magic(object_type *o_ptr);
+
+extern bool remove_curse_object(object_type *o_ptr, bool all);
diff --git a/src/object1.c b/src/object1.c
new file mode 100644
index 00000000..fef7fb85
--- /dev/null
+++ b/src/object1.c
@@ -0,0 +1,6669 @@
+/* File: object1.c */
+
+/* Purpose: Object code, part 1 */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Hack -- note that "TERM_MULTI" is now just "TERM_VIOLET".
+ * We will have to find a cleaner method for "MULTI_HUED" later.
+ * There were only two multi-hued "flavors" (one potion, one food).
+ * Plus five multi-hued "base-objects" (3 dragon scales, one blade
+ * of chaos, and one something else). See the SHIMMER_OBJECTS code
+ * in "dungeon.c" and the object color extractor in "cave.c".
+ */
+#define TERM_MULTI TERM_VIOLET
+
+
+/*
+ * Max sizes of the following arrays
+ */
+#define MAX_ROCKS 62 /* Used with rings (min 58) */
+#define MAX_AMULETS 34 /* Used with amulets (min 30) */
+#define MAX_WOODS 35 /* Used with staffs (min 32) */
+#define MAX_METALS 39 /* Used with wands/rods (min 32/30) */
+#define MAX_COLORS 66 /* Used with potions (min 62) */
+#define MAX_SHROOM 20 /* Used with mushrooms (min 20) */
+#define MAX_TITLES 55 /* Used with scrolls (min 55) */
+#define MAX_SYLLABLES 164 /* Used with scrolls (see below) */
+
+
+/*
+ * Rings (adjectives and colors)
+ */
+
+static cptr ring_adj[MAX_ROCKS] =
+{
+ "Alexandrite", "Amethyst", "Aquamarine", "Azurite", "Beryl",
+ "Bloodstone", "Calcite", "Carnelian", "Corundum", "Diamond",
+ "Emerald", "Fluorite", "Garnet", "Granite", "Jade",
+ "Jasper", "Lapis Lazuli", "Malachite", "Marble", "Moonstone",
+ "Onyx", "Opal", "Pearl", "Quartz", "Quartzite",
+ "Rhodonite", "Ruby", "Sapphire", "Tiger Eye", "Topaz",
+ "Turquoise", "Zircon", "Platinum", "Bronze", "Gold",
+ "Obsidian", "Silver", "Tortoise Shell", "Mithril", "Jet",
+ "Engagement", "Adamantite",
+ "Wire", "Dilithium", "Bone", "Wooden",
+ "Spikard", "Serpent", "Wedding", "Double",
+ "Plain", "Brass", "Scarab", "Shining",
+ "Rusty", "Transparent", "Copper", "Black Opal", "Nickel",
+ "Glass", "Fluorspar", "Agate",
+};
+
+static byte ring_col[MAX_ROCKS] =
+{
+ TERM_GREEN, TERM_VIOLET, TERM_L_BLUE, TERM_L_BLUE, TERM_L_GREEN,
+ TERM_RED, TERM_WHITE, TERM_RED, TERM_SLATE, TERM_WHITE,
+ TERM_GREEN, TERM_L_GREEN, TERM_RED, TERM_L_DARK, TERM_L_GREEN,
+ TERM_UMBER, TERM_BLUE, TERM_GREEN, TERM_WHITE, TERM_L_WHITE,
+ TERM_L_RED, TERM_L_WHITE, TERM_WHITE, TERM_L_WHITE, TERM_L_WHITE,
+ TERM_L_RED, TERM_RED, TERM_BLUE, TERM_YELLOW, TERM_YELLOW,
+ TERM_L_BLUE, TERM_L_UMBER, TERM_WHITE, TERM_L_UMBER, TERM_YELLOW,
+ TERM_L_DARK, TERM_L_WHITE, TERM_GREEN, TERM_L_BLUE, TERM_L_DARK,
+ TERM_YELLOW, TERM_VIOLET,
+ TERM_UMBER, TERM_L_WHITE, TERM_WHITE, TERM_UMBER,
+ TERM_BLUE, TERM_GREEN, TERM_YELLOW, TERM_ORANGE,
+ TERM_YELLOW, TERM_ORANGE, TERM_L_GREEN, TERM_YELLOW,
+ TERM_RED, TERM_WHITE, TERM_UMBER, TERM_L_DARK, TERM_L_WHITE,
+ TERM_WHITE, TERM_BLUE, TERM_L_WHITE
+};
+
+
+/*
+ * Amulets (adjectives and colors)
+ */
+
+static cptr amulet_adj[MAX_AMULETS] =
+{
+ "Amber", "Driftwood", "Coral", "Agate", "Ivory",
+ "Obsidian", "Bone", "Brass", "Bronze", "Pewter",
+ "Tortoise Shell", "Golden", "Azure", "Crystal", "Silver",
+ "Copper", "Amethyst", "Mithril", "Sapphire", "Dragon Tooth",
+ "Carved Oak", "Sea Shell", "Flint Stone", "Ruby", "Scarab",
+ "Origami Paper", "Meteoric Iron", "Platinum", "Glass", "Beryl",
+ "Malachite", "Adamantite", "Mother-of-pearl", "Runed"
+};
+
+static byte amulet_col[MAX_AMULETS] =
+{
+ TERM_YELLOW, TERM_L_UMBER, TERM_WHITE, TERM_L_WHITE, TERM_WHITE,
+ TERM_L_DARK, TERM_WHITE, TERM_ORANGE, TERM_L_UMBER, TERM_SLATE,
+ TERM_GREEN, TERM_YELLOW, TERM_L_BLUE, TERM_L_BLUE, TERM_L_WHITE,
+ TERM_L_UMBER, TERM_VIOLET, TERM_L_BLUE, TERM_BLUE, TERM_L_WHITE,
+ TERM_UMBER, TERM_L_BLUE, TERM_SLATE, TERM_RED, TERM_L_GREEN,
+ TERM_WHITE, TERM_L_DARK, TERM_L_WHITE, TERM_WHITE, TERM_L_GREEN,
+ TERM_GREEN, TERM_VIOLET, TERM_L_WHITE, TERM_UMBER
+};
+
+
+/*
+ * Staffs (adjectives and colors)
+ */
+
+static cptr staff_adj[MAX_WOODS] =
+{
+ "Aspen", "Balsa", "Banyan", "Birch", "Cedar",
+ "Cottonwood", "Cypress", "Dogwood", "Elm", "Eucalyptus",
+ "Hemlock", "Hickory", "Ironwood", "Locust", "Mahogany",
+ "Maple", "Mulberry", "Oak", "Pine", "Redwood",
+ "Rosewood", "Spruce", "Sycamore", "Teak", "Walnut",
+ "Mistletoe", "Hawthorn", "Bamboo", "Silver", "Runed",
+ "Golden", "Ashen", "Gnarled", "Ivory", "Willow"
+};
+
+static byte staff_col[MAX_WOODS] =
+{
+ TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER,
+ TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER,
+ TERM_L_UMBER, TERM_L_UMBER, TERM_UMBER, TERM_L_UMBER, TERM_UMBER,
+ TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_RED,
+ TERM_RED, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_UMBER,
+ TERM_GREEN, TERM_L_UMBER, TERM_L_UMBER, TERM_L_WHITE, TERM_UMBER,
+ TERM_YELLOW, TERM_SLATE, TERM_UMBER, TERM_L_WHITE, TERM_L_UMBER
+};
+
+
+/*
+ * Wands (adjectives and colors)
+ */
+
+static cptr wand_adj[MAX_METALS] =
+{
+ "Aluminium", "Cast Iron", "Chromium", "Copper", "Gold",
+ "Iron", "Magnesium", "Molybdenum", "Nickel", "Rusty",
+ "Silver", "Steel", "Tin", "Titanium", "Tungsten",
+ "Zirconium", "Zinc", "Aluminium-Plated", "Copper-Plated", "Gold-Plated",
+ "Nickel-Plated", "Silver-Plated", "Steel-Plated", "Tin-Plated", "Zinc-Plated",
+ "Mithril-Plated", "Mithril", "Runed", "Bronze", "Brass",
+ "Platinum", "Lead", "Lead-Plated", "Ivory" , "Adamantite",
+ "Uridium", "Long", "Short", "Hexagonal"
+};
+
+static byte wand_col[MAX_METALS] =
+{
+ TERM_L_BLUE, TERM_L_DARK, TERM_WHITE, TERM_UMBER, TERM_YELLOW,
+ TERM_SLATE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_RED,
+ TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_WHITE, TERM_WHITE,
+ TERM_L_WHITE, TERM_L_WHITE, TERM_L_BLUE, TERM_L_UMBER, TERM_YELLOW,
+ TERM_L_UMBER, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE,
+ TERM_L_BLUE, TERM_L_BLUE, TERM_UMBER, TERM_L_UMBER, TERM_L_UMBER,
+ TERM_WHITE, TERM_SLATE, TERM_SLATE, TERM_WHITE, TERM_VIOLET,
+ TERM_L_RED, TERM_L_BLUE, TERM_BLUE, TERM_RED
+};
+
+
+/*
+ * Rods (adjectives and colors).
+ * Efficiency -- copied from wand arrays
+ */
+
+static cptr rod_adj[MAX_METALS];
+
+static byte rod_col[MAX_METALS];
+
+
+/*
+ * Mushrooms (adjectives and colors)
+ */
+
+static cptr food_adj[MAX_SHROOM] =
+{
+ "Blue", "Black", "Black Spotted", "Brown", "Dark Blue",
+ "Dark Green", "Dark Red", "Yellow", "Furry", "Green",
+ "Grey", "Light Blue", "Light Green", "Violet", "Red",
+ "Slimy", "Tan", "White", "White Spotted", "Wrinkled",
+};
+
+static byte food_col[MAX_SHROOM] =
+{
+ TERM_BLUE, TERM_L_DARK, TERM_L_DARK, TERM_UMBER, TERM_BLUE,
+ TERM_GREEN, TERM_RED, TERM_YELLOW, TERM_L_WHITE, TERM_GREEN,
+ TERM_SLATE, TERM_L_BLUE, TERM_L_GREEN, TERM_VIOLET, TERM_RED,
+ TERM_SLATE, TERM_L_UMBER, TERM_WHITE, TERM_WHITE, TERM_UMBER
+};
+
+
+/*
+ * Color adjectives and colors, for potions.
+ * Hack -- The first four entries are hard-coded.
+ * (water, apple juice, slime mold juice, something)
+ */
+
+static cptr potion_adj[MAX_COLORS] =
+{
+ "Clear", "Light Brown", "Icky Green", "Strangely Phosphorescent",
+ "Azure", "Blue", "Blue Speckled", "Black", "Brown", "Brown Speckled",
+ "Bubbling", "Chartreuse", "Cloudy", "Copper Speckled", "Crimson", "Cyan",
+ "Dark Blue", "Dark Green", "Dark Red", "Gold Speckled", "Green",
+ "Green Speckled", "Grey", "Grey Speckled", "Hazy", "Indigo",
+ "Light Blue", "Light Green", "Magenta", "Metallic Blue", "Metallic Red",
+ "Metallic Green", "Metallic Purple", "Misty", "Orange", "Orange Speckled",
+ "Pink", "Pink Speckled", "Puce", "Purple", "Purple Speckled",
+ "Red", "Red Speckled", "Silver Speckled", "Smoky", "Tangerine",
+ "Violet", "Vermilion", "White", "Yellow", "Violet Speckled",
+ "Pungent", "Clotted Red", "Viscous Pink", "Oily Yellow", "Gloopy Green",
+ "Shimmering", "Coagulated Crimson", "Yellow Speckled", "Gold",
+ "Manly", "Stinking", "Oily Black", "Ichor", "Ivory White", "Sky Blue",
+};
+
+static byte potion_col[MAX_COLORS] =
+{
+ TERM_WHITE, TERM_L_UMBER, TERM_GREEN, TERM_MULTI,
+ TERM_L_BLUE, TERM_BLUE, TERM_BLUE, TERM_L_DARK, TERM_UMBER, TERM_UMBER,
+ TERM_L_WHITE, TERM_L_GREEN, TERM_WHITE, TERM_L_UMBER, TERM_RED, TERM_L_BLUE,
+ TERM_BLUE, TERM_GREEN, TERM_RED, TERM_YELLOW, TERM_GREEN,
+ TERM_GREEN, TERM_SLATE, TERM_SLATE, TERM_L_WHITE, TERM_VIOLET,
+ TERM_L_BLUE, TERM_L_GREEN, TERM_RED, TERM_BLUE, TERM_RED,
+ TERM_GREEN, TERM_VIOLET, TERM_L_WHITE, TERM_ORANGE, TERM_ORANGE,
+ TERM_L_RED, TERM_L_RED, TERM_VIOLET, TERM_VIOLET, TERM_VIOLET,
+ TERM_RED, TERM_RED, TERM_L_WHITE, TERM_L_DARK, TERM_ORANGE,
+ TERM_VIOLET, TERM_RED, TERM_WHITE, TERM_YELLOW, TERM_VIOLET,
+ TERM_L_RED, TERM_RED, TERM_L_RED, TERM_YELLOW, TERM_GREEN,
+ TERM_MULTI, TERM_RED, TERM_YELLOW, TERM_YELLOW,
+ TERM_L_UMBER, TERM_UMBER, TERM_L_DARK, TERM_RED, TERM_WHITE, TERM_L_BLUE
+};
+
+
+/*
+ * Syllables for scrolls (must be 1-4 letters each)
+ */
+
+static cptr syllables[MAX_SYLLABLES] =
+{
+ "a", "ab", "ag", "aks", "ala", "an", "ankh", "app",
+ "arg", "arze", "ash", "aus", "ban", "bar", "bat", "bek",
+ "bie", "bin", "bit", "bjor", "blu", "bot", "bu",
+ "byt", "comp", "con", "cos", "cre", "dalf", "dan",
+ "den", "der", "doe", "dok", "eep", "el", "eng", "er", "ere", "erk",
+ "esh", "evs", "fa", "fid", "flit", "for", "fri", "fu", "gan",
+ "gar", "glen", "gop", "gre", "ha", "he", "hyd", "i",
+ "ing", "ion", "ip", "ish", "it", "ite", "iv", "jo",
+ "kho", "kli", "klis", "la", "lech", "man", "mar",
+ "me", "mi", "mic", "mik", "mon", "mung", "mur", "nag", "nej",
+ "nelg", "nep", "ner", "nes", "nis", "nih", "nin", "o",
+ "od", "ood", "org", "orn", "ox", "oxy", "pay", "pet",
+ "ple", "plu", "po", "pot", "prok", "re", "rea", "rhov",
+ "ri", "ro", "rog", "rok", "rol", "sa", "san", "sat",
+ "see", "sef", "seh", "shu", "ski", "sna", "sne", "snik",
+ "sno", "so", "sol", "sri", "sta", "sun", "ta", "tab",
+ "tem", "ther", "ti", "tox", "trol", "tue", "turs", "u",
+ "ulk", "um", "un", "uni", "ur", "val", "viv", "vly",
+ "vom", "wah", "wed", "werg", "wex", "whon", "wun", "x",
+ "yerg", "yp", "zun", "tri", "blaa", "jah", "bul", "on",
+ "foo", "ju", "xuxu"
+};
+
+/*
+ * Hold the titles of scrolls, 6 to 14 characters each
+ * Also keep an array of scroll colors (always WHITE for now)
+ */
+
+static char scroll_adj[MAX_TITLES][16];
+
+static byte scroll_col[MAX_TITLES];
+
+
+/*
+ * Certain items have a flavor
+ * This function is used only by "flavor_init()"
+ */
+static bool_ object_flavor(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ /* Analyze the item */
+ switch (k_ptr->tval)
+ {
+ case TV_AMULET:
+ {
+ return (0x80 + amulet_col[k_ptr->sval]);
+ }
+
+ case TV_RING:
+ {
+ return (0x90 + ring_col[k_ptr->sval]);
+ }
+
+ case TV_STAFF:
+ {
+ return (0xA0 + staff_col[k_ptr->sval]);
+ }
+
+ case TV_WAND:
+ {
+ return (0xB0 + wand_col[k_ptr->sval]);
+ }
+
+ case TV_ROD:
+ {
+ return (0xC0 + rod_col[k_ptr->sval]);
+ }
+
+ case TV_SCROLL:
+ {
+ return (0xD0 + scroll_col[k_ptr->sval]);
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ {
+ return (0xE0 + potion_col[k_ptr->sval]);
+ }
+
+ case TV_FOOD:
+ {
+ if (k_ptr->sval < SV_FOOD_MIN_FOOD)
+ {
+ return (0xF0 + food_col[k_ptr->sval]);
+ }
+
+ break;
+ }
+ }
+
+ /* No flavor */
+ return (0);
+}
+
+
+void get_table_name(char *out_string)
+{
+ int testcounter = (randint(3)) + 1;
+
+ strcpy(out_string, "'");
+
+ if (randint(3) == 2)
+ {
+ while (testcounter--)
+ strcat(out_string, syllables[(randint(MAX_SYLLABLES)) - 1]);
+ }
+
+ else
+ {
+ char Syllable[80];
+ testcounter = (randint(2)) + 1;
+ while (testcounter--)
+ {
+ get_rnd_line("elvish.txt", Syllable);
+ strcat(out_string, Syllable);
+ }
+ }
+
+ out_string[1] = toupper(out_string[1]);
+
+ strcat(out_string, "'");
+
+ out_string[18] = '\0';
+
+ return;
+}
+
+
+/*
+ * Certain items, if aware, are known instantly
+ * This function is used only by "flavor_init()"
+ *
+ * XXX XXX XXX Add "EASY_KNOW" flag to "k_info.txt" file
+ */
+static bool_ object_easy_know(int i)
+{
+ object_kind *k_ptr = &k_info[i];
+
+ /* Analyze the "tval" */
+ switch (k_ptr->tval)
+ {
+ /* Spellbooks */
+ case TV_DRUID_BOOK:
+ case TV_MUSIC_BOOK:
+ case TV_SYMBIOTIC_BOOK:
+ {
+ return (TRUE);
+ }
+
+ /* Simple items */
+ case TV_FLASK:
+ case TV_EGG:
+ case TV_BOTTLE:
+ case TV_SKELETON:
+ case TV_CORPSE:
+ case TV_HYPNOS:
+ case TV_SPIKE:
+ case TV_JUNK:
+ {
+ return (TRUE);
+ }
+
+ /* All Food, Potions, Scrolls, Rods */
+ case TV_FOOD:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_SCROLL:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ case TV_BATERIE:
+ {
+ if (k_ptr->flags3 & TR3_NORM_ART)
+ return ( FALSE );
+ return (TRUE);
+ }
+
+ /* Some Rings, Amulets, Lites */
+ case TV_RING:
+ case TV_AMULET:
+ case TV_LITE:
+ {
+ if (k_ptr->flags3 & (TR3_EASY_KNOW)) return (TRUE);
+ return (FALSE);
+ }
+ }
+
+ /* Nope */
+ return (FALSE);
+}
+
+/*
+ * Prepare the "variable" part of the "k_info" array.
+ *
+ * The "color"/"metal"/"type" of an item is its "flavor".
+ * For the most part, flavors are assigned randomly each game.
+ *
+ * Initialize descriptions for the "colored" objects, including:
+ * Rings, Amulets, Staffs, Wands, Rods, Food, Potions, Scrolls.
+ *
+ * The first 4 entries for potions are fixed (Water, Apple Juice,
+ * Slime Mold Juice, Unused Potion).
+ *
+ * Scroll titles are always between 6 and 14 letters long. This is
+ * ensured because every title is composed of whole words, where every
+ * word is from 1 to 8 letters long (one or two syllables of 1 to 4
+ * letters each), and that no scroll is finished until it attempts to
+ * grow beyond 15 letters. The first time this can happen is when the
+ * current title has 6 letters and the new word has 8 letters, which
+ * would result in a 6 letter scroll title.
+ *
+ * Duplicate titles are avoided by requiring that no two scrolls share
+ * the same first four letters (not the most efficient method, and not
+ * the least efficient method, but it will always work).
+ *
+ * Hack -- make sure everything stays the same for each saved game
+ * This is accomplished by the use of a saved "random seed", as in
+ * "town_gen()". Since no other functions are called while the special
+ * seed is in effect, so this function is pretty "safe".
+ *
+ * Note that the "hacked seed" may provide an RNG with alternating parity!
+ */
+void flavor_init(void)
+{
+ int i, j;
+
+ byte temp_col;
+
+ cptr temp_adj;
+
+
+ /* Hack -- Use the "simple" RNG */
+ Rand_quick = TRUE;
+
+ /* Hack -- Induce consistant flavors */
+ Rand_value = seed_flavor;
+
+
+ /* Efficiency -- Rods/Wands share initial array */
+ for (i = 0; i < MAX_METALS; i++)
+ {
+ rod_adj[i] = wand_adj[i];
+ rod_col[i] = wand_col[i];
+ }
+
+
+ /* Rings have "ring colors" */
+ for (i = 0; i < MAX_ROCKS; i++)
+ {
+ j = rand_int(MAX_ROCKS);
+ temp_adj = ring_adj[i];
+ ring_adj[i] = ring_adj[j];
+ ring_adj[j] = temp_adj;
+ temp_col = ring_col[i];
+ ring_col[i] = ring_col[j];
+ ring_col[j] = temp_col;
+ }
+
+ /* Amulets have "amulet colors" */
+ for (i = 0; i < MAX_AMULETS; i++)
+ {
+ j = rand_int(MAX_AMULETS);
+ temp_adj = amulet_adj[i];
+ amulet_adj[i] = amulet_adj[j];
+ amulet_adj[j] = temp_adj;
+ temp_col = amulet_col[i];
+ amulet_col[i] = amulet_col[j];
+ amulet_col[j] = temp_col;
+ }
+
+ /* Staffs */
+ for (i = 0; i < MAX_WOODS; i++)
+ {
+ j = rand_int(MAX_WOODS);
+ temp_adj = staff_adj[i];
+ staff_adj[i] = staff_adj[j];
+ staff_adj[j] = temp_adj;
+ temp_col = staff_col[i];
+ staff_col[i] = staff_col[j];
+ staff_col[j] = temp_col;
+ }
+
+ /* Wands */
+ for (i = 0; i < MAX_METALS; i++)
+ {
+ j = rand_int(MAX_METALS);
+ temp_adj = wand_adj[i];
+ wand_adj[i] = wand_adj[j];
+ wand_adj[j] = temp_adj;
+ temp_col = wand_col[i];
+ wand_col[i] = wand_col[j];
+ wand_col[j] = temp_col;
+ }
+
+ /* Rods */
+ for (i = 0; i < MAX_METALS; i++)
+ {
+ j = rand_int(MAX_METALS);
+ temp_adj = rod_adj[i];
+ rod_adj[i] = rod_adj[j];
+ rod_adj[j] = temp_adj;
+ temp_col = rod_col[i];
+ rod_col[i] = rod_col[j];
+ rod_col[j] = temp_col;
+ }
+
+ /* Foods (Mushrooms) */
+ for (i = 0; i < MAX_SHROOM; i++)
+ {
+ j = rand_int(MAX_SHROOM);
+ temp_adj = food_adj[i];
+ food_adj[i] = food_adj[j];
+ food_adj[j] = temp_adj;
+ temp_col = food_col[i];
+ food_col[i] = food_col[j];
+ food_col[j] = temp_col;
+ }
+
+ /* Potions */
+ for (i = 4; i < MAX_COLORS; i++)
+ {
+ j = rand_int(MAX_COLORS - 4) + 4;
+ temp_adj = potion_adj[i];
+ potion_adj[i] = potion_adj[j];
+ potion_adj[j] = temp_adj;
+ temp_col = potion_col[i];
+ potion_col[i] = potion_col[j];
+ potion_col[j] = temp_col;
+ }
+
+ /* Scrolls (random titles, always white) */
+ for (i = 0; i < MAX_TITLES; i++)
+ {
+ /* Get a new title */
+ while (TRUE)
+ {
+ char buf[80];
+
+ bool_ okay;
+
+ /* Start a new title */
+ buf[0] = '\0';
+
+ /* Collect words until done */
+ while (1)
+ {
+ int q, s;
+
+ char tmp[80];
+
+ /* Start a new word */
+ tmp[0] = '\0';
+
+ /* Choose one or two syllables */
+ s = ((rand_int(100) < 30) ? 1 : 2);
+
+ /* Add a one or two syllable word */
+ for (q = 0; q < s; q++)
+ {
+ /* Add the syllable */
+ strcat(tmp, syllables[rand_int(MAX_SYLLABLES)]);
+ }
+
+ /* Stop before getting too long */
+ if (strlen(buf) + 1 + strlen(tmp) > 15) break;
+
+ /* Add a space */
+ strcat(buf, " ");
+
+ /* Add the word */
+ strcat(buf, tmp);
+ }
+
+ /* Save the title */
+ strcpy(scroll_adj[i], buf + 1);
+
+ /* Assume okay */
+ okay = TRUE;
+
+ /* Check for "duplicate" scroll titles */
+ for (j = 0; j < i; j++)
+ {
+ cptr hack1 = scroll_adj[j];
+ cptr hack2 = scroll_adj[i];
+
+ /* Compare first four characters */
+ if (*hack1++ != *hack2++) continue;
+ if (*hack1++ != *hack2++) continue;
+ if (*hack1++ != *hack2++) continue;
+ if (*hack1++ != *hack2++) continue;
+
+ /* Not okay */
+ okay = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Break when done */
+ if (okay) break;
+ }
+
+ /* All scrolls are white */
+ scroll_col[i] = TERM_WHITE;
+ }
+
+
+ /* Hack -- Use the "complex" RNG */
+ Rand_quick = FALSE;
+
+ /* Analyze every object */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Skip "empty" objects */
+ if (!k_ptr->name) continue;
+
+ /* Extract "flavor" (if any) */
+ k_ptr->flavor = object_flavor(i);
+
+ /* No flavor yields aware */
+ if ((!k_ptr->flavor) && (k_ptr->tval != TV_ROD_MAIN)) k_ptr->aware = TRUE;
+
+ /* Check for "easily known" */
+ k_ptr->easy_know = object_easy_know(i);
+ }
+}
+
+/*
+ * Reset the "visual" lists
+ *
+ * This involves resetting various things to their "default" state.
+ *
+ * If the "prefs" flag is TRUE, then we will also load the appropriate
+ * "user pref file" based on the current setting of the "use_graphics"
+ * flag. This is useful for switching "graphics" on/off.
+ *
+ * The features, objects, and monsters, should all be encoded in the
+ * relevant "font.pref" and/or "graf.prf" files. XXX XXX XXX
+ *
+ * The "prefs" parameter is no longer meaningful. XXX XXX XXX
+ */
+void reset_visuals(void)
+{
+ int i;
+
+ /* Extract some info about terrain features */
+ for (i = 0; i < max_f_idx; i++)
+ {
+ feature_type *f_ptr = &f_info[i];
+
+ /* Assume we will use the underlying values */
+ f_ptr->x_attr = f_ptr->d_attr;
+ f_ptr->x_char = f_ptr->d_char;
+ }
+
+ /* Extract default attr/char code for stores */
+ for (i = 0; i < max_st_idx; i++)
+ {
+ store_info_type *st_ptr = &st_info[i];
+
+ /* Default attr/char */
+ st_ptr->x_attr = st_ptr->d_attr;
+ st_ptr->x_char = st_ptr->d_char;
+ }
+
+ /* Extract default attr/char code for objects */
+ for (i = 0; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Default attr/char */
+ k_ptr->x_attr = k_ptr->d_attr;
+ k_ptr->x_char = k_ptr->d_char;
+ }
+
+ /* Extract default attr/char code for monsters */
+ for (i = 0; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Default attr/char */
+ r_ptr->x_attr = r_ptr->d_attr;
+ r_ptr->x_char = r_ptr->d_char;
+ }
+
+ /* Reset attr/char code for ego monster overlay graphics */
+ for (i = 0; i < max_re_idx; i++)
+ {
+ monster_ego *re_ptr = &re_info[i];
+
+ /* Default attr/char */
+ re_ptr->g_attr = 0;
+ re_ptr->g_char = 0;
+ }
+
+ /* Reset attr/char code for race modifier overlay graphics */
+ for (i = 0; i < max_rmp_idx; i++)
+ {
+ player_race_mod *rmp_ptr = &race_mod_info[i];
+
+ /* Default attr/char */
+ rmp_ptr->g_attr = 0;
+ rmp_ptr->g_char = 0;
+ }
+
+ /* Reset attr/char code for trap overlay graphics */
+ for (i = 0; i < max_rmp_idx; i++)
+ {
+ trap_type *t_ptr = &t_info[i];
+
+ /* Default attr/char */
+ t_ptr->g_attr = 0;
+ t_ptr->g_char = 0;
+ }
+
+
+ if (use_graphics)
+ {
+ /* Process "graf.prf" */
+ process_pref_file("graf.prf");
+
+ /*
+ * Hack -- remember graphics mode as an integer value,
+ * for faster processing of map_info()
+ */
+
+ /* IBM-PC pseudo-graphics -- not maintained, but the code is there */
+ if (streq(ANGBAND_SYS, "ibm"))
+ {
+ graphics_mode = GRAPHICS_IBM;
+ }
+
+ /*
+ * Isometric view. Also assumes all the attributes of the "new"
+ * graphics.
+ */
+ else if (streq(ANGBAND_GRAF, "iso"))
+ {
+ graphics_mode = GRAPHICS_ISO;
+ }
+
+ /*
+ * "New" graphics -- supports graphics overlay for traps, ego monsters
+ * and player subraces, and has tiles for lighting effects (row + 1
+ * and row + 2 for "darker" versions of terrain features)
+ */
+ else if (streq(ANGBAND_GRAF, "new"))
+ {
+ graphics_mode = GRAPHICS_NEW;
+ }
+
+ /*
+ * "Old" graphics -- doesn't support graphics overlay and lighting
+ * effects
+ */
+ else if (streq(ANGBAND_GRAF, "old"))
+ {
+ graphics_mode = GRAPHICS_OLD;
+ }
+
+ /* ??? */
+ else
+ {
+ graphics_mode = GRAPHICS_UNKNOWN;
+ }
+ }
+
+ /* Normal symbols */
+ else
+ {
+ /* Process "font.prf" */
+ process_pref_file("font.prf");
+
+ graphics_mode = GRAPHICS_NONE;
+ }
+}
+
+
+/*
+ * Extract "xtra" flags from object.
+ */
+void object_flags_xtra(object_type *o_ptr, u32b *f2, u32b *f3, u32b *esp)
+{
+ switch (o_ptr->xtra1)
+ {
+ case EGO_XTRA_SUSTAIN:
+ {
+ /* Choose a sustain */
+ switch (o_ptr->xtra2 % 6)
+ {
+ case 0:
+ (*f2) |= (TR2_SUST_STR);
+ break;
+ case 1:
+ (*f2) |= (TR2_SUST_INT);
+ break;
+ case 2:
+ (*f2) |= (TR2_SUST_WIS);
+ break;
+ case 3:
+ (*f2) |= (TR2_SUST_DEX);
+ break;
+ case 4:
+ (*f2) |= (TR2_SUST_CON);
+ break;
+ case 5:
+ (*f2) |= (TR2_SUST_CHR);
+ break;
+ }
+
+ break;
+ }
+
+ case EGO_XTRA_POWER:
+ {
+ /* Choose a power */
+ switch (o_ptr->xtra2 % 11)
+ {
+ case 0:
+ (*f2) |= (TR2_RES_BLIND);
+ break;
+ case 1:
+ (*f2) |= (TR2_RES_CONF);
+ break;
+ case 2:
+ (*f2) |= (TR2_RES_SOUND);
+ break;
+ case 3:
+ (*f2) |= (TR2_RES_SHARDS);
+ break;
+ case 4:
+ (*f2) |= (TR2_RES_NETHER);
+ break;
+ case 5:
+ (*f2) |= (TR2_RES_NEXUS);
+ break;
+ case 6:
+ (*f2) |= (TR2_RES_CHAOS);
+ break;
+ case 7:
+ (*f2) |= (TR2_RES_DISEN);
+ break;
+ case 8:
+ (*f2) |= (TR2_RES_POIS);
+ break;
+ case 9:
+ (*f2) |= (TR2_RES_DARK);
+ break;
+ case 10:
+ (*f2) |= (TR2_RES_LITE);
+ break;
+ }
+
+ break;
+ }
+
+ case EGO_XTRA_ABILITY:
+ {
+ /* Choose an ability */
+ switch (o_ptr->xtra2 % 8)
+ {
+ case 0:
+ (*f3) |= (TR3_FEATHER);
+ break;
+ case 1:
+ (*f3) |= (TR3_LITE1);
+ break;
+ case 2:
+ (*f3) |= (TR3_SEE_INVIS);
+ break;
+ case 3:
+ (*esp) |= (ESP_ALL);
+ break;
+ case 4:
+ (*f3) |= (TR3_SLOW_DIGEST);
+ break;
+ case 5:
+ (*f3) |= (TR3_REGEN);
+ break;
+ case 6:
+ (*f2) |= (TR2_FREE_ACT);
+ break;
+ case 7:
+ (*f2) |= (TR2_HOLD_LIFE);
+ break;
+ }
+
+ break;
+ }
+
+ }
+}
+
+
+/*
+ * Obtain the "flags" for an item
+ */
+bool_ object_flags_no_set = FALSE;
+void object_flags(object_type *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Base object */
+ (*f1) = k_ptr->flags1;
+ (*f2) = k_ptr->flags2;
+ (*f3) = k_ptr->flags3;
+ (*f4) = k_ptr->flags4;
+ (*f5) = k_ptr->flags5;
+ (*esp) = k_ptr->esp;
+
+ /* Artifact */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ (*f1) = a_ptr->flags1;
+ (*f2) = a_ptr->flags2;
+ (*f3) = a_ptr->flags3;
+ (*f4) = a_ptr->flags4;
+ (*f5) = a_ptr->flags5;
+ (*esp) = a_ptr->esp;
+
+ if ((!object_flags_no_set) && (a_ptr->set != -1))
+ apply_flags_set(o_ptr->name1, a_ptr->set, f1, f2, f3, f4, f5, esp);
+ }
+
+ /* Random artifact ! */
+ if (o_ptr->art_flags1 || o_ptr->art_flags2 || o_ptr->art_flags3 || o_ptr->art_flags4 || o_ptr->art_flags5 || o_ptr->art_esp)
+ {
+ (*f1) |= o_ptr->art_flags1;
+ (*f2) |= o_ptr->art_flags2;
+ (*f3) |= o_ptr->art_flags3;
+ (*f4) |= o_ptr->art_flags4;
+ (*f5) |= o_ptr->art_flags5;
+ (*esp) |= o_ptr->art_esp;
+ }
+
+ /* Extra powers */
+ if (!(o_ptr->art_name))
+ {
+ object_flags_xtra(o_ptr, f2, f3, esp);
+ }
+}
+
+/* Return object granted power */
+int object_power(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+ int power = -1;
+
+ /* Base object */
+ power = k_ptr->power;
+
+ /* Ego-item */
+ if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+
+ if (power == -1) power = e_ptr->power;
+
+ if (o_ptr->name2b)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2b];
+
+ if (power == -1) power = e_ptr->power;
+ }
+ }
+
+ /* Artifact */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ if (power == -1) power = a_ptr->power;
+ }
+
+ return (power);
+}
+
+
+
+/*
+ * Obtain the "flags" for an item which are known to the player
+ */
+void object_flags_known(object_type *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ bool_ spoil = FALSE;
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Clear */
+ (*f1) = (*f2) = (*f3) = (*f4) = (*esp) = (*f5) = 0L;
+
+ /* Must be identified */
+ if (!object_known_p(o_ptr)) return;
+
+ /* Base object */
+ (*f1) = k_ptr->flags1;
+ (*f2) = k_ptr->flags2;
+ (*f3) = k_ptr->flags3;
+ (*f4) = k_ptr->flags4;
+ (*f5) = k_ptr->flags5;
+ (*esp) = k_ptr->esp;
+
+ (*f1) |= k_ptr->oflags1;
+ (*f2) |= k_ptr->oflags2;
+ (*f3) |= k_ptr->oflags3;
+ (*f4) |= k_ptr->oflags4;
+ (*f5) |= k_ptr->oflags5;
+ (*esp) |= k_ptr->oesp;
+
+#ifdef SPOIL_ARTIFACTS
+ /* Full knowledge for some artifacts */
+ if (artifact_p(o_ptr) || o_ptr->art_name) spoil = TRUE;
+#endif /* SPOIL_ARTIFACTS */
+
+#ifdef SPOIL_EGO_ITEMS
+ /* Full knowledge for some ego-items */
+ if (ego_item_p(o_ptr)) spoil = TRUE;
+#endif /* SPOIL_EGO_ITEMS */
+
+ /* Artifact */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Need full knowledge or spoilers */
+ if (spoil || (o_ptr->ident & IDENT_MENTAL))
+ {
+ (*f1) = a_ptr->flags1;
+ (*f2) = a_ptr->flags2;
+ (*f3) = a_ptr->flags3;
+ (*f4) = a_ptr->flags4;
+ (*f5) = a_ptr->flags5;
+ (*esp) = a_ptr->esp;
+
+ if ((!object_flags_no_set) && (a_ptr->set != -1))
+ apply_flags_set(o_ptr->name1, a_ptr->set, f1, f2, f3, f4, f5, esp);
+ }
+ else
+ {
+ (*f1) = (*f2) = (*f3) = (*f4) = (*esp) = (*f5) = 0L;
+ }
+
+ (*f1) |= a_ptr->oflags1;
+ (*f2) |= a_ptr->oflags2;
+ (*f3) |= a_ptr->oflags3;
+ (*f4) |= a_ptr->oflags4;
+ (*f5) |= a_ptr->oflags5;
+ (*esp) |= a_ptr->oesp;
+ }
+
+ /* Random artifact or ego item! */
+ if (o_ptr->art_flags1 || o_ptr->art_flags2 || o_ptr->art_flags3 || o_ptr->art_flags4 || o_ptr->art_flags5 || o_ptr->art_esp)
+ {
+ /* Need full knowledge or spoilers */
+ if (spoil || (o_ptr->ident & IDENT_MENTAL))
+ {
+ (*f1) |= o_ptr->art_flags1;
+ (*f2) |= o_ptr->art_flags2;
+ (*f3) |= o_ptr->art_flags3;
+ (*f4) |= o_ptr->art_flags4;
+ (*f5) |= o_ptr->art_flags5;
+ (*esp) |= o_ptr->art_esp;
+ }
+
+ (*f1) |= o_ptr->art_oflags1;
+ (*f2) |= o_ptr->art_oflags2;
+ (*f3) |= o_ptr->art_oflags3;
+ (*f4) |= o_ptr->art_oflags4;
+ (*f5) |= o_ptr->art_oflags5;
+ (*esp) |= o_ptr->art_oesp;
+ }
+
+ /* Full knowledge for *identified* objects */
+ if (!(o_ptr->ident & IDENT_MENTAL)) return;
+
+ if (!(o_ptr->art_name))
+ {
+ object_flags_xtra(o_ptr, f2, f3, esp);
+ }
+
+ /* Hack - Res Chaos -> Res Confusion */
+ if (*f2 & TR2_RES_CHAOS) (*f2) |= (TR2_RES_CONF);
+}
+
+
+
+
+
+/*
+ * Print a char "c" into a string "t", as if by sprintf(t, "%c", c),
+ * and return a pointer to the terminator (t + 1).
+ */
+static char *object_desc_chr(char *t, char c)
+{
+ /* Copy the char */
+ *t++ = c;
+
+ /* Terminate */
+ *t = '\0';
+
+ /* Result */
+ return (t);
+}
+
+
+/*
+ * Print a string "s" into a string "t", as if by strcpy(t, s),
+ * and return a pointer to the terminator.
+ */
+static char *object_desc_str(char *t, cptr s)
+{
+ /* Copy the string */
+ while (*s) *t++ = *s++;
+
+ /* Terminate */
+ *t = '\0';
+
+ /* Result */
+ return (t);
+}
+
+/*
+ * Do the actual conversion of a number for object_desc_num() and
+ * object_desc_int().
+ */
+static char *convert_number(char *result, u32b num)
+{
+ char *tp;
+ char temp[11];
+
+ tp = temp;
+ *tp = '0' + (num % 10);
+ for (num /= 10; num != 0; num /= 10)
+ {
+ *++tp = '0' + (num % 10);
+ }
+
+ while (tp != temp)
+ {
+ *result++ = *tp--;
+ }
+ *result++ = *tp;
+ *result = '\0';
+
+ return result;
+}
+
+/*
+ * Print a nnumber "n" into a string "t", as if by
+ * sprintf(t, "%u", n), and return a pointer to the terminator.
+ */
+static char *object_desc_num(char *result, s32b num)
+{
+ u32b n;
+
+ if (num < 0)
+ {
+ *result++ = '-';
+ n = -num;
+ }
+ else
+ n = num;
+
+ /* Result */
+ return convert_number(result, n);
+}
+
+/*
+ * Print an signed number "num" into a string "result", as if by
+ * sprintf(t, "%+d", n), and return a pointer to the terminator.
+ * Note that we always print a sign, either "+" or "-".
+ */
+static char *object_desc_int(char *result, s32b num)
+{
+ u32b n;
+
+ /* Negative */
+ if (num < 0)
+ {
+ /* Take the absolute value */
+ n = -num;
+
+ /* Use a "minus" sign */
+ *result++ = '-';
+ }
+ /* Positive (or zero) */
+ else
+ {
+ /* Use the actual number */
+ n = num;
+
+ /* Use a "plus" sign */
+ *result++ = '+';
+ }
+
+ /* Result */
+ return convert_number(result, n);
+}
+
+/*
+ * Creates a description of the item "o_ptr", and stores it in "out_val".
+ *
+ * One can choose the "verbosity" of the description, including whether
+ * or not the "number" of items should be described, and how much detail
+ * should be used when describing the item.
+ *
+ * The given "buf" must be 80 chars long to hold the longest possible
+ * description, which can get pretty long, including incriptions, such as:
+ * "no more Maces of Disruption (Defender) (+10,+10) [+5] (+3 to stealth)".
+ * Note that the inscription will be clipped to keep the total description
+ * under 79 chars (plus a terminator).
+ *
+ * Note the use of "object_desc_num()" and "object_desc_int()" as hyper-efficient,
+ * portable, versions of some common "sprintf()" commands.
+ *
+ * Note that all ego-items (when known) append an "Ego-Item Name", unless
+ * the item is also an artifact, which should NEVER happen.
+ *
+ * Note that all artifacts (when known) append an "Artifact Name", so we
+ * have special processing for "Specials" (artifact Lites, Rings, Amulets).
+ * The "Specials" never use "modifiers" if they are "known", since they
+ * have special "descriptions", such as "The Necklace of the Dwarves".
+ *
+ * Special Lite's use the "k_info" base-name (Phial, Star, or Arkenstone),
+ * plus the artifact name, just like any other artifact, if known.
+ *
+ * Special Ring's and Amulet's, if not "aware", use the same code as normal
+ * rings and amulets, and if "aware", use the "k_info" base-name (Ring or
+ * Amulet or Necklace). They will NEVER "append" the "k_info" name. But,
+ * they will append the artifact name, just like any artifact, if known.
+ *
+ * None of the Special Rings/Amulets are "EASY_KNOW", though they could be,
+ * at least, those which have no "pluses", such as the three artifact lites.
+ *
+ * Hack -- Display "The One Ring" as "a Plain Gold Ring" until aware.
+ *
+ * If "pref" then a "numeric" prefix will be pre-pended.
+ *
+ * Mode:
+ * 0 -- The Cloak of Death
+ * 1 -- The Cloak of Death [1,+3]
+ * 2 -- The Cloak of Death [1,+3] (+2 to Stealth)
+ * 3 -- The Cloak of Death [1,+3] (+2 to Stealth) {nifty}
+ */
+void object_desc(char *buf, object_type *o_ptr, int pref, int mode)
+{
+ bool_ hack_name = FALSE;
+ cptr basenm, modstr;
+ int indexx;
+
+ bool_ aware = FALSE;
+ bool_ known = FALSE;
+
+ bool_ append_name = FALSE;
+
+ bool_ show_weapon = FALSE;
+ bool_ show_armour = FALSE;
+
+ cptr s, u;
+ char *t;
+
+ char p1 = '(', p2 = ')';
+ char b1 = '[', b2 = ']';
+ char c1 = '{', c2 = '}';
+
+ char tmp_val[160];
+ char tmp_val2[90];
+
+ s32b power;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ cptr str;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ /* See if the object is "aware" */
+ if (object_aware_p(o_ptr)) aware = TRUE;
+
+ /* See if the object is "known" */
+ if (object_known_p(o_ptr)) known = TRUE;
+
+ /* Hack -- Extract the sub-type "indexx" */
+ indexx = o_ptr->sval;
+
+ /* Extract default "base" string */
+ basenm = (k_name + k_ptr->name);
+
+ /* Assume no "modifier" string */
+ modstr = "";
+
+ /* Analyze the object */
+ switch (o_ptr->tval)
+ {
+ /* Some objects are easy to describe */
+ case TV_SKELETON:
+ case TV_BOTTLE:
+ case TV_JUNK:
+ case TV_SPIKE:
+ case TV_FLASK:
+ case TV_CHEST:
+ case TV_INSTRUMENT:
+ case TV_TOOL:
+ case TV_DIGGING:
+ {
+ break;
+ }
+
+
+ /* Missiles/ Bows/ Weapons */
+ case TV_SHOT:
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ {
+ show_weapon = TRUE;
+ break;
+ }
+
+ /* Trapping Kits */
+ case TV_TRAPKIT:
+ {
+ modstr = basenm;
+ basenm = "& # Trap Set~";
+ break;
+ }
+
+ /* Armour */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_SHIELD:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ show_armour = TRUE;
+ break;
+ }
+
+
+ /* Lites (including a few "Specials") */
+ case TV_LITE:
+ {
+ break;
+ }
+
+ /* Amulets (including a few "Specials") */
+ case TV_AMULET:
+ {
+ /* Color the object */
+ modstr = amulet_adj[indexx];
+ if (aware) append_name = TRUE;
+
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Amulet~";
+ else
+ basenm = aware ? "& # Amulet~" : "& # Amulet~";
+
+ if (known && !o_ptr->art_name && artifact_p(o_ptr)) basenm = k_ptr->name + k_name;
+
+ break;
+ }
+
+ /* Rings (including a few "Specials") */
+ case TV_RING:
+ {
+ /* Color the object */
+ modstr = ring_adj[indexx];
+ if (aware) append_name = TRUE;
+
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Ring~";
+ else
+ basenm = aware ? "& # Ring~" : "& # Ring~";
+
+ /* Hack -- The One Ring */
+ if (!aware && (o_ptr->sval == SV_RING_POWER)) modstr = "Plain Gold";
+
+ if (known && !o_ptr->art_name && artifact_p(o_ptr)) basenm = k_ptr->name + k_name;
+
+ break;
+ }
+
+ case TV_STAFF:
+ {
+ /* Color the object */
+ modstr = staff_adj[o_ptr->pval2 % MAX_WOODS];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Staff~";
+ else
+ basenm = "& # Staff~";
+ break;
+ }
+
+ case TV_WAND:
+ {
+ /* Color the object */
+ modstr = wand_adj[o_ptr->pval2 % MAX_METALS];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Wand~";
+ else
+ basenm = "& # Wand~";
+ break;
+ }
+
+ case TV_ROD:
+ {
+ /* Color the object */
+ modstr = rod_adj[indexx];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Rod Tip~";
+ else
+ basenm = aware ? "& # Rod Tip~" : "& # Rod Tip~";
+ if (o_ptr->sval == SV_ROD_HOME)
+ {
+ basenm = "& Great Rod Tip~ of Home Summoning";
+ hack_name = TRUE;
+ }
+ break;
+ }
+
+ case TV_ROD_MAIN:
+ {
+ object_kind *tip_ptr = &k_info[lookup_kind(TV_ROD, o_ptr->pval)];
+
+ modstr = k_name + tip_ptr->name;
+ break;
+ }
+
+ case TV_SCROLL:
+ {
+ /* Color the object */
+ modstr = scroll_adj[indexx];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Scroll~";
+ else
+ basenm = aware ? "& Scroll~ titled \"#\"" : "& Scroll~ titled \"#\"";
+ break;
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ {
+ /* Color the object */
+ if ((o_ptr->tval != TV_POTION2) || (o_ptr->sval != SV_POTION2_MIMIC) || (!aware))
+ {
+ modstr = potion_adj[indexx];
+ if (aware) append_name = TRUE;
+ }
+ else
+ {
+ call_lua("get_mimic_info", "(d,s)", "s", o_ptr->pval2, "name", &modstr);
+ }
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Potion~";
+ else
+ basenm = aware ? "& # Potion~" : "& # Potion~";
+ break;
+ }
+
+ case TV_FOOD:
+ {
+ /* Ordinary food is "boring" */
+ if (o_ptr->sval >= SV_FOOD_MIN_FOOD) break;
+
+ /* Color the object */
+ modstr = food_adj[indexx];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Mushroom~";
+ else
+ basenm = aware ? "& # Mushroom~" : "& # Mushroom~";
+ break;
+ }
+
+
+ /* Cloak of Mimicry */
+ case TV_CLOAK:
+ {
+ show_armour = TRUE;
+ if (o_ptr->sval == SV_MIMIC_CLOAK)
+ {
+ call_lua("get_mimic_info", "(d,s)", "s", o_ptr->pval2, "obj_name", &modstr);
+ }
+ break;
+ }
+
+
+ case TV_SYMBIOTIC_BOOK:
+ {
+ modstr = basenm;
+ basenm = "& Symbiotic Spellbook~ #";
+ break;
+ }
+
+ case TV_MUSIC_BOOK:
+ {
+ modstr = basenm;
+ basenm = "& Songbook~ #";
+ break;
+ }
+
+ /* Druid Books */
+ case TV_DRUID_BOOK:
+ {
+ modstr = basenm;
+ basenm = "& Elemental Stone~ #";
+ break;
+ }
+
+ case TV_BATERIE:
+ {
+ modstr = basenm;
+ basenm = "& Essence~ of #";
+ break;
+ }
+
+ case TV_PARCHMENT:
+ {
+ modstr = basenm;
+ basenm = "& Parchment~ - #";
+ break;
+ }
+
+
+ /* Hack -- Gold/Gems */
+ case TV_GOLD:
+ {
+ strcpy(buf, basenm);
+ return;
+ }
+
+ case TV_CORPSE:
+ {
+ monster_race* r_ptr = &r_info[o_ptr->pval2];
+ modstr = basenm;
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ basenm = format("& %s's #~", r_name + r_ptr->name);
+ else
+ basenm = format("& %s #~", r_name + r_ptr->name);
+ break;
+ }
+
+ case TV_EGG:
+ {
+ monster_race* r_ptr = &r_info[o_ptr->pval2];
+ modstr = basenm;
+
+ basenm = format("& %s #~", r_name + r_ptr->name);
+ break;
+ }
+
+ case TV_HYPNOS:
+ {
+ /* We print hit points further down. --dsb */
+ monster_race* r_ptr = &r_info[o_ptr->pval];
+ modstr = basenm;
+ basenm = format("& %s~", r_name + r_ptr->name);
+ break;
+ }
+
+ case TV_TOTEM:
+ {
+ char name[80];
+ monster_type monster;
+
+ monster.sr_ptr = 0;
+ monster.r_idx = o_ptr->pval;
+ monster.ego = o_ptr->pval2;
+ monster.ml = TRUE;
+ monster.status = MSTATUS_ENEMY;
+
+ monster_desc(name, &monster, 0x188);
+
+ modstr = basenm;
+ basenm = format("& #~ of %s", name);
+ break;
+ }
+
+ case TV_RANDART:
+ {
+ modstr = basenm;
+
+ if (known)
+ {
+ basenm = random_artifacts[indexx].name_full;
+ }
+ else
+ {
+ basenm = random_artifacts[indexx].name_short;
+ }
+ break;
+ }
+
+ case TV_RUNE2:
+ {
+ if (o_ptr->sval != RUNE_STONE)
+ {
+ modstr = basenm;
+ basenm = "& Rune~ [#]";
+ }
+ break;
+ }
+
+ case TV_RUNE1:
+ {
+ modstr = basenm;
+ basenm = "& Rune~ [#]";
+ break;
+ }
+
+ case TV_DAEMON_BOOK:
+ case TV_BOOK:
+ {
+ basenm = k_name + k_ptr->name;
+ if (o_ptr->sval == 255) modstr = school_spells[o_ptr->pval].name;
+ break;
+ }
+
+ /* Used in the "inventory" routine */
+ default:
+ {
+ if (process_hooks_ret(HOOK_ITEM_NAME, "ss", "(O,s,s)",
+ o_ptr, basenm, modstr))
+ {
+ basenm = process_hooks_return[0].str;
+ modstr = process_hooks_return[1].str;
+ break;
+ }
+ else
+ {
+ strcpy(buf, "(nothing)");
+ return;
+ }
+ }
+ }
+
+ /* Mega Hack */
+ if ((!hack_name) && known && (k_ptr->flags5 & TR5_FULL_NAME)) basenm = (k_name + k_ptr->name);
+
+
+ /* Start dumping the result */
+ t = tmp_val;
+
+ /* The object "expects" a "number" */
+ if (basenm[0] == '&')
+ {
+ monster_race* r_ptr;
+ cptr ego = NULL;
+
+ if (o_ptr->tval == TV_CORPSE) r_ptr = &r_info[o_ptr->pval2];
+ else r_ptr = &r_info[o_ptr->pval];
+
+ /* Grab any ego-item name */
+ if (known && (o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN))
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+ ego_item_type *e2_ptr = &e_info[o_ptr->name2b];
+
+ if (e_ptr->before)
+ {
+ ego = e_ptr->name + e_name;
+ }
+ else if (e2_ptr->before)
+ {
+ ego = e2_ptr->name + e_name;
+ }
+ }
+
+ /* Skip the ampersand (and space) */
+ s = basenm + 2;
+
+ /* No prefix */
+ if (pref <= 0)
+ {
+ /* Nothing */
+ }
+
+ /* Hack -- None left */
+ else if (o_ptr->number <= 0)
+ {
+ t = object_desc_str(t, "no more ");
+ }
+
+ /* Extract the number */
+ else if (o_ptr->number > 1)
+ {
+ t = object_desc_num(t, o_ptr->number);
+ t = object_desc_chr(t, ' ');
+ }
+
+ else if ((o_ptr->tval == TV_CORPSE) && (r_ptr->flags1 & RF1_UNIQUE))
+ {}
+
+
+ else if ((o_ptr->tval == TV_HYPNOS) && (r_ptr->flags1 & RF1_UNIQUE))
+ {}
+
+ /* Hack -- The only one of its kind */
+ else if (known && (artifact_p(o_ptr) || o_ptr->art_name))
+ {
+ t = object_desc_str(t, "The ");
+ }
+
+ else if (ego != NULL)
+ {
+ if (is_a_vowel(ego[0]))
+ {
+ t = object_desc_str(t, "an ");
+ }
+ else
+ {
+ t = object_desc_str(t, "a ");
+ }
+ }
+
+ /* A single one, with a vowel in the modifier */
+ else if ((*s == '#') && (is_a_vowel(modstr[0])))
+ {
+ t = object_desc_str(t, "an ");
+ }
+
+ /* A single one, with a vowel */
+ else if (is_a_vowel(*s))
+ {
+ t = object_desc_str(t, "an ");
+ }
+
+ /* A single one, without a vowel */
+ else
+ {
+ t = object_desc_str(t, "a ");
+ }
+
+ /* Grab any ego-item name */
+ if (known && (o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN))
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+ ego_item_type *e2_ptr = &e_info[o_ptr->name2b];
+
+ if (e_ptr->before)
+ {
+ t = object_desc_str(t, (e_name + e_ptr->name));
+ t = object_desc_chr(t, ' ');
+ }
+ if (e2_ptr->before)
+ {
+ t = object_desc_str(t, (e_name + e2_ptr->name));
+ t = object_desc_chr(t, ' ');
+ }
+ }
+
+ /* -TM- Hack -- Add false-artifact names */
+ /* Dagger inscribed {@w0%Smelly} will be named
+ * Smelly Dagger {@w0} */
+
+ if (o_ptr->note)
+ {
+ str = strchr(quark_str(o_ptr->note), '%');
+
+ /* Add the false name */
+ if (str)
+ {
+ t = object_desc_str(t, &str[1]);
+ t = object_desc_chr(t, ' ');
+ }
+ }
+
+ }
+
+ /* Hack -- objects that "never" take an article */
+ else
+ {
+ /* No ampersand */
+ s = basenm;
+
+ /* No pref */
+ if (!pref)
+ {
+ /* Nothing */
+ }
+
+ /* Hack -- all gone */
+ else if (o_ptr->number <= 0)
+ {
+ t = object_desc_str(t, "no more ");
+ }
+
+ /* Prefix a number if required */
+ else if (o_ptr->number > 1)
+ {
+ t = object_desc_num(t, o_ptr->number);
+ t = object_desc_chr(t, ' ');
+ }
+
+ else if (o_ptr->tval == TV_RANDART)
+ {
+ /* Do nothing, since randarts have their prefix already included */
+ }
+
+ /* Hack -- The only one of its kind */
+ else if (known && (artifact_p(o_ptr) || o_ptr->art_name))
+ {
+ t = object_desc_str(t, "The ");
+ }
+
+ /* Hack -- single items get no prefix */
+ else
+ {
+ /* Nothing */
+ }
+
+ /* Grab any ego-item name */
+ if (known && (o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN))
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+ ego_item_type *e2_ptr = &e_info[o_ptr->name2b];
+
+ if (e_ptr->before)
+ {
+ t = object_desc_str(t, (e_name + e_ptr->name));
+ t = object_desc_chr(t, ' ');
+ }
+ if (e2_ptr->before)
+ {
+ t = object_desc_str(t, (e_name + e2_ptr->name));
+ t = object_desc_chr(t, ' ');
+ }
+ }
+ }
+
+ /* Paranoia -- skip illegal tildes */
+ /* while (*s == '~') s++; */
+
+ /* Copy the string */
+ for (; *s; s++)
+ {
+ /* Pluralizer */
+ if (*s == '~')
+ {
+ /* Add a plural if needed */
+ if ((o_ptr->number != 1) && (pref >= 0))
+ {
+ char k = t[ -1];
+
+ /* XXX XXX XXX Mega-Hack */
+
+ /* Hack -- "Cutlass-es" and "Torch-es" */
+ if ((k == 's') || (k == 'h')) *t++ = 'e';
+
+ /* Add an 's' */
+ *t++ = 's';
+ }
+ }
+
+ /* Modifier */
+ else if (*s == '#')
+ {
+ /* Grab any ego-item name */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ t = object_desc_chr(t, ' ');
+
+ if (known && o_ptr->name2)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+
+ t = object_desc_str(t, (e_name + e_ptr->name));
+ }
+ }
+
+ /* Insert the modifier */
+ for (u = modstr; *u; u++) *t++ = *u;
+ }
+
+ /* Normal */
+ else
+ {
+ /* Copy */
+ *t++ = *s;
+ }
+ }
+
+ /* Terminate */
+ *t = '\0';
+
+
+ /* Append the "kind name" to the "base name" */
+ if ((append_name) && (!artifact_p(o_ptr)))
+ {
+ t = object_desc_str(t, " of ");
+
+ if (((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF)))
+ {
+ t = object_desc_str(t, school_spells[o_ptr->pval2].name);
+ if (mode >= 1)
+ {
+ s32b bonus = o_ptr->pval3 & 0xFFFF;
+ s32b max = o_ptr->pval3 >> 16;
+
+ t = object_desc_chr(t, '[');
+ t = object_desc_num(t, bonus);
+ t = object_desc_chr(t, '|');
+ t = object_desc_num(t, max);
+ t = object_desc_chr(t, ']');
+ }
+ }
+ else
+ t = object_desc_str(t, (k_name + k_ptr->name));
+ }
+
+ /* Hack -- Append "Artifact" or "Special" names */
+ if (known)
+ {
+
+ /* -TM- Hack -- Add false-artifact names */
+ /* Dagger inscribed {@w0#of Smell} will be named
+ * Dagger of Smell {@w0} */
+ if (o_ptr->note)
+ {
+ str = strchr(quark_str(o_ptr->note), '#');
+
+ /* Add the false name */
+ if (str)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_str(t, &str[1]);
+ }
+ }
+
+ /* Is it a new random artifact ? */
+ if (o_ptr->art_name)
+ {
+ t = object_desc_chr(t, ' ');
+
+ t = object_desc_str(t, quark_str(o_ptr->art_name));
+ }
+
+
+ /* Grab any artifact name */
+ else if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Unique corpses don't require another name */
+ if (o_ptr->tval != TV_CORPSE)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_str(t, (a_name + a_ptr->name));
+ }
+ }
+
+ /* Grab any ego-item name */
+ else if ((o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN))
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+ ego_item_type *e2_ptr = &e_info[o_ptr->name2b];
+
+ if (o_ptr->name2 && !e_ptr->before)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_str(t, (e_name + e_ptr->name));
+ }
+ if (o_ptr->name2b && !e2_ptr->before)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_str(t, (e_name + e2_ptr->name));
+ }
+ }
+ }
+
+ /* It contains a spell */
+ if ((known) && (f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 != -1))
+ {
+ t = object_desc_str(t, format(" [%s]", school_spells[o_ptr->pval2].name));
+ }
+
+ /* Add symbiote hp here, after the "fake-artifact" name. --dsb */
+ if (o_ptr->tval == TV_HYPNOS)
+ {
+ t = object_desc_str(t, " (");
+ t = object_desc_num(t, o_ptr->pval2);
+ t = object_desc_str(t, " hp)");
+ }
+
+ /* No more details wanted */
+ if (mode < 1) goto copyback;
+
+ /* Hack -- Some objects can have an exp level */
+ if ((f4 & TR4_LEVELS) && known)
+ {
+ t = object_desc_str(t, " (E:");
+ if (exp_need)
+ {
+ s32b need;
+ /* Formula from check_experience_obj(). */
+ need = player_exp[o_ptr->elevel - 1] * 5 / 2;
+ t = object_desc_num(t, need - o_ptr->exp);
+ }
+ else
+ {
+ t = object_desc_num(t, o_ptr->exp);
+ }
+ t = object_desc_str(t, ", L:");
+ t = object_desc_num(t, o_ptr->elevel);
+ t = object_desc_chr(t, ')');
+ }
+ if ((f4 & TR4_ART_EXP) && known)
+ {
+ t = object_desc_str(t, " (Exp:");
+ t = object_desc_num(t, o_ptr->exp);
+ t = object_desc_chr(t, ')');
+ }
+
+ /* Hack -- Chests must be described in detail */
+ if (o_ptr->tval == TV_CHEST)
+ {
+ /* Not searched yet */
+ if (!known)
+ {
+ /* Nothing */
+ }
+
+ /* May be "empty" */
+ else if (!o_ptr->pval)
+ {
+ t = object_desc_str(t, " (empty)");
+ }
+
+ /* May be "disarmed" */
+ else if (o_ptr->pval < 0)
+ {
+ t = object_desc_str(t, " (disarmed)");
+ }
+
+ /* Describe the traps, if any */
+ else
+ {
+ /* Describe the traps */
+ t = object_desc_str(t, " (");
+ if (t_info[o_ptr->pval].ident)
+ t = object_desc_str(t, t_name + t_info[o_ptr->pval].name);
+ else
+ t = object_desc_str(t, "trapped");
+ t = object_desc_str(t, ")");
+ }
+ }
+
+
+ /* Display the item like a weapon */
+ if (f3 & (TR3_SHOW_MODS)) show_weapon = TRUE;
+
+ /* Display the item like a weapon */
+ if (o_ptr->to_h && o_ptr->to_d) show_weapon = TRUE;
+
+ /* Display the item like armour */
+ if (o_ptr->ac) show_armour = TRUE;
+
+ /* Dump base weapon info */
+ switch (o_ptr->tval)
+ {
+ /* Missiles and Weapons */
+ case TV_SHOT:
+ case TV_BOLT:
+ case TV_ARROW:
+ /* Exploding arrow? */
+ if (o_ptr->pval2 != 0)
+ t = object_desc_str(t, " (exploding)");
+ /* No break, we want to continue the description */
+
+ case TV_BOOMERANG:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_AXE:
+ case TV_SWORD:
+ case TV_DAEMON_BOOK:
+ if ((o_ptr->tval == TV_DAEMON_BOOK) && (o_ptr->sval != SV_DEMONBLADE))
+ break;
+
+ /* Append a "damage" string */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_num(t, o_ptr->dd);
+ t = object_desc_chr(t, 'd');
+ t = object_desc_num(t, o_ptr->ds);
+ t = object_desc_chr(t, p2);
+
+ /* All done */
+ break;
+
+
+ /* Bows get a special "damage string" */
+ case TV_BOW:
+
+ /* Mega-Hack -- Extract the "base power" */
+ power = (o_ptr->sval % 10);
+
+ /* Apply the "Extra Might" flag */
+ if (f3 & (TR3_XTRA_MIGHT)) power += o_ptr->pval;
+
+ /* Append a special "damage" string */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_chr(t, 'x');
+ t = object_desc_num(t, power);
+ t = object_desc_chr(t, p2);
+
+ /* All done */
+ break;
+ }
+
+
+ /* Add the weapon bonuses */
+ if (known)
+ {
+ /* Show the tohit/todam on request */
+ if (show_weapon)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_int(t, o_ptr->to_h);
+ t = object_desc_chr(t, ',');
+ t = object_desc_int(t, o_ptr->to_d);
+ t = object_desc_chr(t, p2);
+ }
+
+ /* Show the tohit if needed */
+ else if (o_ptr->to_h)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_int(t, o_ptr->to_h);
+ if (!(f3 & (TR3_HIDE_TYPE)) || o_ptr->art_name)
+ t = object_desc_str(t, " to accuracy");
+ t = object_desc_chr(t, p2);
+ }
+
+ /* Show the todam if needed */
+ else if (o_ptr->to_d)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_int(t, o_ptr->to_d);
+ if (!(f3 & (TR3_HIDE_TYPE)) || o_ptr->art_name)
+ t = object_desc_str(t, " to damage");
+ t = object_desc_chr(t, p2);
+ }
+ }
+
+
+ /* Add the armor bonuses */
+ if (known)
+ {
+ /* Show the armor class info */
+ if (show_armour)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, b1);
+ t = object_desc_num(t, o_ptr->ac);
+ t = object_desc_chr(t, ',');
+ t = object_desc_int(t, o_ptr->to_a);
+ t = object_desc_chr(t, b2);
+ }
+
+ /* No base armor, but does increase armor */
+ else if (o_ptr->to_a)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, b1);
+ t = object_desc_int(t, o_ptr->to_a);
+ t = object_desc_chr(t, b2);
+ }
+ }
+
+ /* Hack -- always show base armor */
+ else if (show_armour)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, b1);
+ t = object_desc_num(t, o_ptr->ac);
+ t = object_desc_chr(t, b2);
+ }
+
+ if ((f1 & TR1_MANA) && (known) && (o_ptr->pval > 0))
+ {
+ t = object_desc_chr(t, '(');
+ if (munchkin_multipliers)
+ {
+ t = object_desc_num(t, 100 * o_ptr->pval / 5);
+ }
+ else
+ {
+ t = object_desc_num(t, 100 * o_ptr->pval / 10);
+ }
+ t = object_desc_str(t, "%)");
+ }
+
+ if ((known) && (f2 & TR2_LIFE) ) /* Can disp neg now -- Improv */
+ {
+ t = object_desc_chr(t, '(');
+ if (munchkin_multipliers)
+ {
+ t = object_desc_num(t, 100 * o_ptr->pval / 5);
+ }
+ else
+ {
+ t = object_desc_num(t, 100 * o_ptr->pval / 10);
+ }
+ t = object_desc_str(t, "%)");
+ }
+
+ /* No more details wanted */
+ if (mode < 2) goto copyback;
+
+
+ /* Hack -- Wands and Staffs have charges */
+ if (known &&
+ ((o_ptr->tval == TV_STAFF) ||
+ (o_ptr->tval == TV_WAND)))
+ {
+ /* Dump " (N charges)" */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_num(t, o_ptr->pval);
+ t = object_desc_str(t, " charge");
+ if (o_ptr->pval != 1) t = object_desc_chr(t, 's');
+ t = object_desc_chr(t, p2);
+ }
+
+ /*
+ * Hack -- Rods have a "charging" indicator.
+ */
+ else if (known && (o_ptr->tval == TV_ROD_MAIN))
+ {
+ /* Display prettily. */
+ t = object_desc_str(t, " (");
+ t = object_desc_num(t, o_ptr->timeout);
+ t = object_desc_chr(t, '/');
+ t = object_desc_num(t, o_ptr->pval2);
+ t = object_desc_chr(t, ')');
+ }
+
+ /*
+ * Hack -- Rods have a "charging" indicator.
+ */
+ else if (known && (o_ptr->tval == TV_ROD))
+ {
+ /* Display prettily. */
+ t = object_desc_str(t, " (");
+ t = object_desc_num(t, o_ptr->pval);
+ t = object_desc_str(t, " Mana to cast");
+ t = object_desc_chr(t, ')');
+ }
+
+ /* Hack -- Process Lanterns/Torches */
+ else if ((o_ptr->tval == TV_LITE) && (f4 & TR4_FUEL_LITE))
+ {
+ /* Hack -- Turns of light for normal lites */
+ t = object_desc_str(t, " (with ");
+ t = object_desc_num(t, o_ptr->timeout);
+ t = object_desc_str(t, " turns of light)");
+ }
+
+
+ /* Dump "pval" flags for wearable items */
+ if (known && ((f1 & (TR1_PVAL_MASK)) || (f5 & (TR5_PVAL_MASK))))
+ {
+ /* Start the display */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+
+ /* Dump the "pval" itself */
+ t = object_desc_int(t, o_ptr->pval);
+
+ /* Do not display the "pval" flags */
+ if (f3 & (TR3_HIDE_TYPE))
+ {
+ /* Nothing */
+ }
+
+ /* Speed */
+ else if (f1 & (TR1_SPEED))
+ {
+ /* Dump " to speed" */
+ t = object_desc_str(t, " to speed");
+ }
+
+ /* Attack speed */
+ else if (f1 & (TR1_BLOWS))
+ {
+ /* Add " attack" */
+ t = object_desc_str(t, " attack");
+
+ /* Add "attacks" */
+ if (ABS(o_ptr->pval) != 1) t = object_desc_chr(t, 's');
+ }
+
+ /* Critical chance */
+ else if (f5 & (TR5_CRIT))
+ {
+ /* Add " attack" */
+ t = object_desc_str(t, "% of critical hits");
+ }
+
+ /* Stealth */
+ else if (f1 & (TR1_STEALTH))
+ {
+ /* Dump " to stealth" */
+ t = object_desc_str(t, " to stealth");
+ }
+
+ /* Search */
+ else if (f1 & (TR1_SEARCH))
+ {
+ /* Dump " to searching" */
+ t = object_desc_str(t, " to searching");
+ }
+
+ /* Infravision */
+ else if (f1 & (TR1_INFRA))
+ {
+ /* Dump " to infravision" */
+ t = object_desc_str(t, " to infravision");
+ }
+
+ /* Tunneling */
+ else if (f1 & (TR1_TUNNEL))
+ {
+ /* Nothing */
+ }
+
+ /* Finish the display */
+ t = object_desc_chr(t, p2);
+ }
+
+
+ /* Indicate "charging" artifacts XXX XXX XXX */
+ if (known && (f3 & TR3_ACTIVATE) && o_ptr->timeout)
+ {
+ if(o_ptr->tval == TV_EGG)
+ /* Hack -- Dump " (stopped)" if relevant */
+ t = object_desc_str(t, " (stopped)");
+ else
+ /* Hack -- Dump " (charging)" if relevant */
+ t = object_desc_str(t, " (charging)");
+ }
+
+ /* Indicate "charging" Mage Staffs XXX XXX XXX */
+ if (known && o_ptr->timeout && (is_ego_p(o_ptr, EGO_MSTAFF_SPELL)))
+ {
+ /* Hack -- Dump " (charging spell1)" if relevant */
+ t = object_desc_str(t, " (charging spell1)");
+ }
+ if (known && o_ptr->xtra2 && (is_ego_p(o_ptr, EGO_MSTAFF_SPELL)))
+ {
+ /* Hack -- Dump " (charging spell2)" if relevant */
+ t = object_desc_str(t, " (charging spell2)");
+ }
+
+
+ /* No more details wanted */
+ if (mode < 3) goto copyback;
+
+
+ /* No inscription yet */
+ tmp_val2[0] = '\0';
+
+ /* Sensed stuff */
+ if (o_ptr->ident & (IDENT_SENSE))
+ {
+ strcpy(tmp_val2, sense_desc[o_ptr->sense]);
+ }
+
+ /* Hack - Note "cursed" if the item is 'known' and cursed */
+ if (cursed_p(o_ptr) && (known) && (!tmp_val2[0]))
+ {
+ if (tmp_val2[0]) strcat(tmp_val2, ", ");
+ strcat(tmp_val2, "cursed");
+ }
+
+ /* Use the standard inscription if available */
+ if (o_ptr->note)
+ {
+ char *u = tmp_val2;
+
+ if (tmp_val2[0]) strcat(tmp_val2, ", ");
+
+ strcat(tmp_val2, quark_str(o_ptr->note));
+
+ for (; *u && (*u != '#') && (*u != '%'); u++);
+
+ *u = '\0';
+ }
+
+ /* Mega-Hack -- note empty wands/staffs */
+ if (!known && (o_ptr->ident & (IDENT_EMPTY)))
+ {
+ if (tmp_val2[0]) strcat(tmp_val2, ", ");
+ strcat(tmp_val2, "empty");
+ }
+
+ /* Note "tried" if the object has been tested unsuccessfully */
+ if (!aware && object_tried_p(o_ptr))
+ {
+ if (tmp_val2[0]) strcat(tmp_val2, ", ");
+ strcpy(tmp_val2, "tried");
+ }
+
+ /* Note the discount, if any */
+ if ((o_ptr->discount) && (!tmp_val2[0]))
+ {
+ object_desc_num(tmp_val2, o_ptr->discount);
+ strcat(tmp_val2, "% off");
+ }
+
+ /* Append the inscription, if any */
+ if (tmp_val2[0])
+ {
+ int n;
+
+ /* Hack -- How much so far */
+ n = (t - tmp_val);
+
+ /* Paranoia -- do not be stupid */
+ if (n > 75) n = 75;
+
+ /* Hack -- shrink the inscription */
+ tmp_val2[75 - n] = '\0';
+
+ /* Append the inscription */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, c1);
+ t = object_desc_str(t, tmp_val2);
+ t = object_desc_chr(t, c2);
+ }
+copyback:
+ /* Here's where we dump the built string into buf. */
+ tmp_val[79] = '\0';
+ t = tmp_val;
+ while ((*(buf++) = *(t++))); /* copy the string over */
+}
+
+
+/*
+ * Hack -- describe an item currently in a store's inventory
+ * This allows an item to *look* like the player is "aware" of it
+ */
+void object_desc_store(char *buf, object_type *o_ptr, int pref, int mode)
+{
+ /* Save the "aware" flag */
+ bool_ hack_aware = k_info[o_ptr->k_idx].aware;
+
+ /* Save the "known" flag */
+ bool_ hack_known = (o_ptr->ident & (IDENT_KNOWN)) ? TRUE : FALSE;
+
+
+ /* Set the "known" flag */
+ o_ptr->ident |= (IDENT_KNOWN);
+
+ /* Force "aware" for description */
+ k_info[o_ptr->k_idx].aware = TRUE;
+
+
+ /* Describe the object */
+ object_desc(buf, o_ptr, pref, mode);
+
+
+ /* Restore "aware" flag */
+ k_info[o_ptr->k_idx].aware = hack_aware;
+
+ /* Clear the known flag */
+ if (!hack_known) o_ptr->ident &= ~(IDENT_KNOWN);
+}
+
+
+
+
+/*
+ * Determine the "Activation" (if any) for an artifact
+ * Return a string, or NULL for "no activation"
+ */
+cptr item_activation(object_type *o_ptr, byte num)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Needed hacks */
+ static char rspell[2][80];
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Require activation ability */
+ if (!(f3 & (TR3_ACTIVATE))) return (NULL);
+
+
+ /*
+ * We need to deduce somehow that it is a random artifact -- one
+ * problem: It could be a random artifact which has NOT YET received
+ * a name. Thus we eliminate other possibilities instead of checking
+ * for art_name
+ */
+
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ int gf, mod, mana;
+
+ if (!num)
+ {
+ gf = o_ptr->pval & 0xFFFF;
+ mod = o_ptr->pval3 & 0xFFFF;
+ mana = o_ptr->pval2 & 0xFF;
+ }
+ else
+ {
+ gf = o_ptr->pval >> 16;
+ mod = o_ptr->pval3 >> 16;
+ mana = o_ptr->pval2 >> 8;
+ }
+ sprintf(rspell[num], "runespell(%s, %s, %d) every %d turns",
+ k_info[lookup_kind(TV_RUNE1, gf)].name + k_name,
+ k_info[lookup_kind(TV_RUNE2, mod)].name + k_name,
+ mana, mana * 5);
+ return rspell[num];
+ }
+
+ if (o_ptr->tval == TV_EGG)
+ {
+ return "stop or resume the egg development";
+ }
+
+ if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ if (!((o_ptr->name1 && a_info[o_ptr->name1].activate) ||
+ (o_ptr->name2 && e_info[o_ptr->name2].activate) ||
+ (o_ptr->name2b && e_info[o_ptr->name2b].activate)))
+ {
+ if (o_ptr->sval == SV_HORN)
+ {
+ return "aggravate monster every 100 turns";
+ }
+ }
+ }
+
+ if (process_hooks_ret(HOOK_ACTIVATE_DESC, "s", "(O)", o_ptr))
+ return (process_hooks_return[0].str);
+
+ return activation_aux(o_ptr, FALSE, 0);
+}
+
+/* Grab the tval desc */
+bool_ grab_tval_desc(int tval)
+{
+ int tv = 0;
+
+ while (tval_descs[tv].tval && (tval_descs[tv].tval != tval))
+ {
+ tv++;
+ }
+
+ if (!tval_descs[tv].tval) return FALSE;
+
+ text_out_c(TERM_L_BLUE, tval_descs[tv].desc);
+ text_out("\n");
+
+ return TRUE;
+}
+
+#define CHECK_FIRST(txt, first) \
+if ((first)) { (first) = FALSE; text_out((txt)); } else text_out(", ");
+
+/*
+ * Display the damage done with a multiplier
+ */
+void output_dam(object_type *o_ptr, int mult, int mult2, cptr against, cptr against2, bool_ *first)
+{
+ int dam;
+
+ dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5 * mult;
+ dam += (o_ptr->to_d + p_ptr->to_d + p_ptr->to_d_melee) * 10;
+ dam *= p_ptr->num_blow;
+ CHECK_FIRST("", *first);
+ if (dam > 0)
+ {
+ if (dam % 10)
+ text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10));
+ else
+ text_out_c(TERM_L_GREEN, format("%d", dam / 10));
+ }
+ else
+ text_out_c(TERM_L_RED, "0");
+ text_out(format(" against %s", against));
+
+ if (mult2)
+ {
+ dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5 * mult2;
+ dam += (o_ptr->to_d + p_ptr->to_d + p_ptr->to_d_melee) * 10;
+ dam *= p_ptr->num_blow;
+ CHECK_FIRST("", *first);
+ if (dam > 0)
+ {
+ if (dam % 10)
+ text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10));
+ else
+ text_out_c(TERM_L_GREEN, format("%d", dam / 10));
+ }
+ else
+ text_out_c(TERM_L_RED, "0");
+ text_out(format(" against %s", against2));
+ }
+}
+
+/*
+ * Outputs the damage we do/would do with the weapon
+ */
+void display_weapon_damage(object_type *o_ptr)
+{
+ object_type forge, *old_ptr = &forge;
+ u32b f1, f2, f3, f4, f5, esp;
+ bool_ first = TRUE;
+ bool_ full = o_ptr->ident & (IDENT_MENTAL);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Ok now the hackish stuff, we replace the current weapon with this one */
+ object_copy(old_ptr, &p_ptr->inventory[INVEN_WIELD]);
+ object_copy(&p_ptr->inventory[INVEN_WIELD], o_ptr);
+ calc_bonuses(TRUE);
+
+ text_out("\nUsing it you would have ");
+ text_out_c(TERM_L_GREEN, format("%d ", p_ptr->num_blow));
+ text_out(format("blow%s and do an average damage per turn of ", (p_ptr->num_blow) ? "s" : ""));
+
+ if (full && (f1 & TR1_SLAY_ANIMAL)) output_dam(o_ptr, 2, 0, "animals", NULL, &first);
+ if (full && (f1 & TR1_SLAY_EVIL)) output_dam(o_ptr, 2, 0, "evil creatures", NULL, &first);
+ if (full && (f1 & TR1_SLAY_ORC)) output_dam(o_ptr, 3, 0, "orcs", NULL, &first);
+ if (full && (f1 & TR1_SLAY_TROLL)) output_dam(o_ptr, 3, 0, "trolls", NULL, &first);
+ if (full && (f1 & TR1_SLAY_GIANT)) output_dam(o_ptr, 3, 0, "giants", NULL, &first);
+ if (full && (f1 & TR1_KILL_DRAGON)) output_dam(o_ptr, 5, 0, "dragons", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_DRAGON)) output_dam(o_ptr, 3, 0, "dragons", NULL, &first);
+ if (full && (f5 & TR5_KILL_UNDEAD)) output_dam(o_ptr, 5, 0, "undead", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_UNDEAD)) output_dam(o_ptr, 3, 0, "undead", NULL, &first);
+ if (full && (f5 & TR5_KILL_DEMON)) output_dam(o_ptr, 5, 0, "demons", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_DEMON)) output_dam(o_ptr, 3, 0, "demons", NULL, &first);
+
+ if (full && (f1 & TR1_BRAND_FIRE)) output_dam(o_ptr, 3, 6, "non fire resistant creatures", "fire susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_COLD)) output_dam(o_ptr, 3, 6, "non cold resistant creatures", "cold susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_ELEC)) output_dam(o_ptr, 3, 6, "non lightning resistant creatures", "lightning susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_ACID)) output_dam(o_ptr, 3, 6, "non acid resistant creatures", "acid susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_POIS)) output_dam(o_ptr, 3, 6, "non poison resistant creatures", "poison susceptible creatures", &first);
+
+ output_dam(o_ptr, 1, 0, (first) ? "all monsters" : "other monsters", NULL, &first);
+
+ text_out(".");
+
+ /* get our weapon back */
+ object_copy(&p_ptr->inventory[INVEN_WIELD], old_ptr);
+ calc_bonuses(TRUE);
+}
+
+/*
+ * Display the ammo damage done with a multiplier
+ */
+void output_ammo_dam(object_type *o_ptr, int mult, int mult2, cptr against, cptr against2, bool_ *first)
+{
+ int dam;
+ object_type *b_ptr = &p_ptr->inventory[INVEN_BOW];
+ int is_boomerang = (o_ptr->tval == TV_BOOMERANG);
+ int tmul = get_shooter_mult(b_ptr) + p_ptr->xtra_might;
+ if (is_boomerang) tmul = p_ptr->throw_mult;
+
+ dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5;
+ dam += o_ptr->to_d * 10;
+ if (!is_boomerang) dam += b_ptr->to_d * 10;
+ dam *= tmul;
+ if (!is_boomerang) dam += (p_ptr->to_d_ranged) * 10;
+ dam *= mult;
+ CHECK_FIRST("", *first);
+ if (dam > 0)
+ {
+ if (dam % 10)
+ text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10));
+ else
+ text_out_c(TERM_L_GREEN, format("%d", dam / 10));
+ }
+ else
+ text_out_c(TERM_L_RED, "0");
+ text_out(format(" against %s", against));
+
+ if (mult2)
+ {
+ dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5;
+ dam += o_ptr->to_d * 10;
+ if (!is_boomerang) dam += b_ptr->to_d * 10;
+ dam *= tmul;
+ if (!is_boomerang) dam += (p_ptr->to_d_ranged) * 10;
+ dam *= mult2;
+ CHECK_FIRST("", *first);
+ if (dam > 0)
+ {
+ if (dam % 10)
+ text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10));
+ else
+ text_out_c(TERM_L_GREEN, format("%d", dam / 10));
+ }
+ else
+ text_out_c(TERM_L_RED, "0");
+ text_out(format(" against %s", against2));
+ }
+}
+
+/*
+ * Outputs the damage we do/would do with the current bow and this ammo
+ */
+void display_ammo_damage(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ bool_ first = TRUE;
+ int i;
+ bool_ full = o_ptr->ident & (IDENT_MENTAL);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (o_ptr->tval == TV_BOOMERANG)
+ text_out("\nUsing it you would do an average damage per throw of ");
+ else
+ text_out("\nUsing it with your current shooter you would do an average damage per shot of ");
+ if (full && (f1 & TR1_SLAY_ANIMAL)) output_ammo_dam(o_ptr, 2, 0, "animals", NULL, &first);
+ if (full && (f1 & TR1_SLAY_EVIL)) output_ammo_dam(o_ptr, 2, 0, "evil creatures", NULL, &first);
+ if (full && (f1 & TR1_SLAY_ORC)) output_ammo_dam(o_ptr, 3, 0, "orcs", NULL, &first);
+ if (full && (f1 & TR1_SLAY_TROLL)) output_ammo_dam(o_ptr, 3, 0, "trolls", NULL, &first);
+ if (full && (f1 & TR1_SLAY_GIANT)) output_ammo_dam(o_ptr, 3, 0, "giants", NULL, &first);
+ if (full && (f1 & TR1_KILL_DRAGON)) output_ammo_dam(o_ptr, 5, 0, "dragons", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_DRAGON)) output_ammo_dam(o_ptr, 3, 0, "dragons", NULL, &first);
+ if (full && (f5 & TR5_KILL_UNDEAD)) output_ammo_dam(o_ptr, 5, 0, "undeads", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_UNDEAD)) output_ammo_dam(o_ptr, 3, 0, "undeads", NULL, &first);
+ if (full && (f5 & TR5_KILL_DEMON)) output_ammo_dam(o_ptr, 5, 0, "demons", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_DEMON)) output_ammo_dam(o_ptr, 3, 0, "demons", NULL, &first);
+
+ if (full && (f1 & TR1_BRAND_FIRE)) output_ammo_dam(o_ptr, 3, 6, "non fire resistant creatures", "fire susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_COLD)) output_ammo_dam(o_ptr, 3, 6, "non cold resistant creatures", "cold susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_ELEC)) output_ammo_dam(o_ptr, 3, 6, "non lightning resistant creatures", "lightning susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_ACID)) output_ammo_dam(o_ptr, 3, 6, "non acid resistant creatures", "acid susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_POIS)) output_ammo_dam(o_ptr, 3, 6, "non poison resistant creatures", "poison susceptible creatures", &first);
+
+ output_ammo_dam(o_ptr, 1, 0, (first) ? "all monsters" : "other monsters", NULL, &first);
+ text_out(". ");
+
+ if (o_ptr->pval2)
+ {
+ text_out("The explosion will be ");
+ i = 0;
+ while (gf_names[i].gf != -1)
+ {
+ if (gf_names[i].gf == o_ptr->pval2)
+ break;
+ i++;
+ }
+ text_out_c(TERM_L_GREEN, (gf_names[i].gf != -1) ? gf_names[i].name : "something weird");
+ text_out(".");
+ }
+}
+
+/*
+ * Describe a magic stick powers
+ */
+void describe_device(object_type *o_ptr)
+{
+ /* Wands/... of shcool spell */
+ if (((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF)) && object_known_p(o_ptr))
+ {
+ /* Enter device mode */
+ set_stick_mode(o_ptr);
+
+ text_out("\nSpell description:");
+ exec_lua(format("print_device_desc(%d)", o_ptr->pval2));
+
+ text_out("\nSpell level: ");
+ text_out_c(TERM_L_BLUE, string_exec_lua(format("return tostring(get_level(%d, 50, 0))", o_ptr->pval2)));
+
+ text_out("\nMinimum Magic Device level to increase spell level: ");
+ text_out_c(TERM_L_BLUE, format("%d", school_spells[o_ptr->pval2].skill_level));
+
+ text_out("\nSpell fail: ");
+ text_out_c(TERM_GREEN, string_exec_lua(format("return tostring(spell_chance(%d))", o_ptr->pval2)));
+
+ text_out("\nSpell info: ");
+ text_out_c(TERM_YELLOW, string_exec_lua(format("return __spell_info[%d]()", o_ptr->pval2)));
+
+ /* Leave device mode */
+ unset_stick_mode();
+
+ text_out("\n");
+ }
+}
+
+
+/*
+ * Helper for object_out_desc
+ *
+ * Print the level something was found on
+ *
+ */
+static cptr object_out_desc_where_found(s16b level, s16b dungeon)
+{
+ static char str[80];
+
+ if (dungeon == DUNGEON_WILDERNESS)
+ /* Taking care of older objects */
+ if (level == 0)
+ sprintf(str, "in the wilderness or in a town");
+ else if (wf_info[level].terrain_idx == TERRAIN_TOWN)
+ sprintf(str, "in the town of %s", wf_info[level].name + wf_name);
+ else
+ sprintf(str, "in %s", wf_info[level].text + wf_text);
+ else
+ sprintf(str, "on level %d of %s", level, d_info[dungeon].name + d_name);
+
+ return str;
+}
+
+/*
+ * Describe an item
+ */
+bool_ object_out_desc(object_type *o_ptr, FILE *fff, bool_ trim_down, bool_ wait_for_it)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ char *txt;
+
+ cptr vp[64];
+ byte vc[64];
+ int vn;
+
+ bool_ first = TRUE;
+
+ /* Extract the flags */
+ if ((!(o_ptr->ident & (IDENT_MENTAL))) && (!fff))
+ {
+ f1 = o_ptr->art_oflags1;
+ f2 = o_ptr->art_oflags2;
+ f3 = o_ptr->art_oflags3;
+ f4 = o_ptr->art_oflags4;
+ f5 = o_ptr->art_oflags5;
+ esp = o_ptr->art_oesp;
+ }
+ else
+ {
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ }
+
+ if (fff)
+ {
+ /* Set up stuff for text_out */
+ text_out_file = fff;
+ text_out_hook = text_out_to_file;
+ }
+ else
+ {
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Set up stuff for text_out */
+ text_out_hook = text_out_to_screen;
+ text_out("\n");
+ }
+
+ /* No need to dump that */
+ if (!fff)
+ {
+ if (!trim_down) grab_tval_desc(o_ptr->tval);
+ }
+
+ if (object_known_p(o_ptr))
+ {
+ if (o_ptr->k_idx && (!trim_down))
+ {
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ text_out_c(TERM_ORANGE, k_text + k_ptr->text);
+ text_out("\n");
+ }
+
+ if (o_ptr->name1 && (!trim_down))
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ text_out_c(TERM_YELLOW, a_text + a_ptr->text);
+ text_out("\n");
+
+ if (a_ptr->set != -1)
+ {
+ set_type *set_ptr = &set_info[a_ptr->set];
+
+ text_out_c(TERM_GREEN, set_text + set_ptr->desc);
+ text_out("\n");
+ }
+ }
+
+ if ((f4 & TR4_LEVELS) && (!trim_down))
+ {
+ int j = 0;
+
+ if (count_bits(o_ptr->pval3) == 0) text_out("It is sentient");
+ else if (count_bits(o_ptr->pval3) > 1) text_out("It is sentient and can have access to the realms of ");
+ else text_out("It is sentient and can have access to the realm of ");
+
+ first = TRUE;
+ txt = "";
+ for (j = 0; j < MAX_FLAG_GROUP; j++)
+ {
+ if (BIT(j) & o_ptr->pval3)
+ {
+ CHECK_FIRST(txt, first);
+ text_out_c(flags_groups[j].color, flags_groups[j].name);
+ }
+ }
+
+ text_out(". ");
+ }
+
+ if (f4 & TR4_ULTIMATE)
+ {
+ if ((wield_slot(o_ptr) == INVEN_WIELD) ||
+ (wield_slot(o_ptr) == INVEN_BOW))
+ text_out_c(TERM_VIOLET, "It is part of the trinity of the ultimate weapons. ");
+ else
+ text_out_c(TERM_VIOLET, "It is the ultimate armor. ");
+ }
+
+ if (f4 & TR4_COULD2H) text_out("It can be wielded two-handed. ");
+ if (f4 & TR4_MUST2H) text_out("It must be wielded two-handed. ");
+
+ /* Mega-Hack -- describe activation */
+ if (f3 & (TR3_ACTIVATE))
+ {
+ text_out("It can be activated for ");
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ text_out(item_activation(o_ptr, 0));
+ text_out(" and ");
+ text_out(item_activation(o_ptr, 1));
+ }
+ else
+ text_out(item_activation(o_ptr, 0));
+
+ /* Mega-hack -- get rid of useless line for e.g. randarts */
+ if (f5 & (TR5_ACTIVATE_NO_WIELD))
+ text_out(". ");
+ else
+ text_out(" if it is being worn. ");
+ }
+ /* Granted power */
+ if (object_power(o_ptr) != -1)
+ {
+ text_out("It grants you the power of ");
+ text_out(powers_type[object_power(o_ptr)].name);
+ text_out(" if it is being worn. ");
+ }
+
+ /* Hack -- describe lites */
+ if ((o_ptr->tval == TV_LITE) || (f3 & TR3_LITE1) || (f4 & TR4_LITE2) || (f4 & TR4_LITE3))
+ {
+ int radius = 0;
+
+ if (f3 & TR3_LITE1) radius++;
+ if (f4 & TR4_LITE2) radius += 2;
+ if (f4 & TR4_LITE3) radius += 3;
+ if (radius > 5) radius = 5;
+
+ if (f4 & TR4_FUEL_LITE)
+ {
+ text_out(format("It provides light (radius %d) when fueled. ", radius));
+ }
+ else
+ {
+ text_out(format("It provides light (radius %d) forever. ", radius));
+ }
+ }
+
+ /* Mega Hack^3 -- describe the Anchor of Space-time */
+ if (o_ptr->name1 == ART_ANCHOR)
+ {
+ text_out("It prevents the space-time continuum from being disrupted. ");
+ }
+
+ if ((f4 & (TR4_ANTIMAGIC_50)) || (f4 & (TR4_ANTIMAGIC_30)) || (f4 & (TR4_ANTIMAGIC_20)) || (f4 & (TR4_ANTIMAGIC_10)))
+ {
+ text_out("It generates an antimagic field. ");
+ }
+
+ if (f5 & TR5_SPELL_CONTAIN)
+ {
+ if (o_ptr->pval2 == -1)
+ text_out("It can be used to store a spell. ");
+ else
+ text_out("It has a spell stored inside. ");
+ }
+
+ /* Pick up stat bonuses */
+ vn = 0;
+ if (f1 & (TR1_STR)) vp[vn++] = "strength";
+ if (f1 & (TR1_INT)) vp[vn++] = "intelligence";
+ if (f1 & (TR1_WIS)) vp[vn++] = "wisdom";
+ if (f1 & (TR1_DEX)) vp[vn++] = "dexterity";
+ if (f1 & (TR1_CON)) vp[vn++] = "constitution";
+ if (f1 & (TR1_CHR)) vp[vn++] = "charisma";
+ if ((o_ptr->tval != TV_TRAPKIT) && (f1 & (TR1_STEALTH))) vp[vn++] = "stealth";
+ if (f1 & (TR1_SEARCH)) vp[vn++] = "searching";
+ if (f1 & (TR1_INFRA)) vp[vn++] = "infravision";
+ if (f1 & (TR1_TUNNEL)) vp[vn++] = "ability to tunnel";
+ if (f1 & (TR1_SPEED)) vp[vn++] = "speed";
+ if (f1 & (TR1_BLOWS)) vp[vn++] = "attack speed";
+ if (f5 & (TR5_CRIT)) vp[vn++] = "ability to score critical hits";
+ if (f5 & (TR5_LUCK)) vp[vn++] = "luck";
+ if (f1 & (TR1_SPELL)) vp[vn++] = "spell power";
+
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It ");
+
+ /* What it does */
+ if (o_ptr->pval > 0) text_out("increases ");
+ else text_out("decreases ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("your ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out(vp[i]);
+ }
+
+ text_out(" by ");
+ if (o_ptr->pval > 0)
+ text_out_c(TERM_L_GREEN, format("%i", o_ptr->pval));
+ else
+ text_out_c(TERM_L_RED, format("%i", -o_ptr->pval));
+ text_out(". ");
+ }
+
+
+ vn = 0;
+ if (f1 & (TR1_MANA)) vp[vn++] = "mana capacity";
+ if (f2 & (TR2_LIFE)) vp[vn++] = "hit points";
+
+ /* Describe with percentuals */
+ if (vn)
+ {
+ int percent;
+
+ /* What it does */
+ if (o_ptr->pval > 0)
+ text_out("It increases");
+ else
+ text_out("It decreases");
+
+ text_out(" your ");
+ text_out(vp[0]);
+ if (vn == 2)
+ {
+ text_out(" and ");
+ text_out(vp[1]);
+ }
+
+ text_out(" by ");
+ percent = 100 * o_ptr->pval / ( munchkin_multipliers ? 5 : 10 );
+
+
+ if (o_ptr->pval > 0)
+ text_out_c(TERM_L_GREEN, format("%i%%", percent));
+ else
+ text_out_c(TERM_L_RED, format("%i%%", -percent));
+ text_out(". ");
+ }
+
+ if ((o_ptr->tval == TV_TRAPKIT) && (f1 & (TR1_STEALTH)))
+ {
+ text_out("It is well-hidden. ");
+ }
+
+ vn = 0;
+ if (f1 & (TR1_BRAND_ACID))
+ {
+ vc[vn] = TERM_GREEN;
+ vp[vn++] = "acid";
+ }
+ if (f1 & (TR1_BRAND_ELEC))
+ {
+ vc[vn] = TERM_L_BLUE;
+ vp[vn++] = "electricity";
+ }
+ if (f1 & (TR1_BRAND_FIRE))
+ {
+ vc[vn] = TERM_RED;
+ vp[vn++] = "fire";
+ }
+ if (f1 & (TR1_BRAND_COLD))
+ {
+ vc[vn] = TERM_L_WHITE;
+ vp[vn++] = "frost";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It does extra damage ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("from ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out_c(vc[i], vp[i]);
+ }
+ text_out(". ");
+ }
+
+
+ if (f1 & (TR1_BRAND_POIS))
+ {
+ text_out("It ");
+ text_out_c(TERM_L_GREEN, "poisons your foes");
+ text_out(". ");
+ }
+
+ if (f1 & (TR1_CHAOTIC))
+ {
+ text_out("It produces chaotic effects. ");
+ }
+
+ if (f1 & (TR1_VAMPIRIC))
+ {
+ text_out("It drains life from your foes. ");
+ }
+
+ if (f1 & (TR1_IMPACT))
+ {
+ text_out("It can cause earthquakes. ");
+ }
+
+ if (f1 & (TR1_VORPAL))
+ {
+ text_out("It is very sharp and can cut your foes. ");
+ }
+
+ if (f5 & (TR5_WOUNDING))
+ {
+ text_out("It is very sharp and can make your foes bleed. ");
+ }
+
+ if (f1 & (TR1_KILL_DRAGON))
+ {
+ text_out("It is a great bane of dragons. ");
+ }
+ else if (f1 & (TR1_SLAY_DRAGON))
+ {
+ text_out("It is especially deadly against dragons. ");
+ }
+ if (f1 & (TR1_SLAY_ORC))
+ {
+ text_out("It is especially deadly against orcs. ");
+ }
+ if (f1 & (TR1_SLAY_TROLL))
+ {
+ text_out("It is especially deadly against trolls. ");
+ }
+ if (f1 & (TR1_SLAY_GIANT))
+ {
+ text_out("It is especially deadly against giants. ");
+ }
+ if (f5 & (TR5_KILL_DEMON))
+ {
+ text_out("It is a great bane of demons. ");
+ }
+ else if (f1 & (TR1_SLAY_DEMON))
+ {
+ text_out("It strikes at demons with holy wrath. ");
+ }
+ if (f5 & (TR5_KILL_UNDEAD))
+ {
+ text_out("It is a great bane of undead. ");
+ }
+ else if (f1 & (TR1_SLAY_UNDEAD))
+ {
+ text_out("It strikes at undead with holy wrath. ");
+ }
+ if (f1 & (TR1_SLAY_EVIL))
+ {
+ text_out("It fights against evil with holy fury. ");
+ }
+ if (f1 & (TR1_SLAY_ANIMAL))
+ {
+ text_out("It is especially deadly against natural creatures. ");
+ }
+
+ if (f2 & (TR2_INVIS))
+ {
+ text_out("It makes you invisible. ");
+ }
+
+ if (o_ptr->tval != TV_TRAPKIT)
+ {
+ vn = 0;
+ if (f2 & (TR2_SUST_STR))
+ {
+ vp[vn++] = "strength";
+ }
+ if (f2 & (TR2_SUST_INT))
+ {
+ vp[vn++] = "intelligence";
+ }
+ if (f2 & (TR2_SUST_WIS))
+ {
+ vp[vn++] = "wisdom";
+ }
+ if (f2 & (TR2_SUST_DEX))
+ {
+ vp[vn++] = "dexterity";
+ }
+ if (f2 & (TR2_SUST_CON))
+ {
+ vp[vn++] = "constitution";
+ }
+ if (f2 & (TR2_SUST_CHR))
+ {
+ vp[vn++] = "charisma";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It sustains ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("your ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out(vp[i]);
+ }
+ text_out(". ");
+ }
+
+ vn = 0;
+ if (f2 & (TR2_IM_ACID))
+ {
+ vc[vn] = TERM_GREEN;
+ vp[vn++] = "acid";
+ }
+ if (f2 & (TR2_IM_ELEC))
+ {
+ vc[vn] = TERM_L_BLUE;
+ vp[vn++] = "electricity";
+ }
+ if (f2 & (TR2_IM_FIRE))
+ {
+ vc[vn] = TERM_RED;
+ vp[vn++] = "fire";
+ }
+ if (f2 & (TR2_IM_COLD))
+ {
+ vc[vn] = TERM_L_WHITE;
+ vp[vn++] = "cold";
+ }
+ if (f4 & (TR4_IM_NETHER))
+ {
+ vc[vn] = TERM_L_GREEN;
+ vp[vn++] = "nether";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It provides immunity ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("to ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out_c(vc[i], vp[i]);
+ }
+ text_out(". ");
+ }
+ }
+ else
+ {
+ if (f2 & (TRAP2_AUTOMATIC_5))
+ {
+ text_out("It can rearm itself. ");
+ }
+ if (f2 & (TRAP2_AUTOMATIC_99))
+ {
+ text_out("It rearms itself. ");
+ }
+ if (f2 & (TRAP2_KILL_GHOST))
+ {
+ text_out("It is effective against Ghosts. ");
+ }
+ if (f2 & (TRAP2_TELEPORT_TO))
+ {
+ text_out("It can teleport monsters to you. ");
+ }
+ if (f2 & (TRAP2_ONLY_DRAGON))
+ {
+ text_out("It can only be set off by dragons. ");
+ }
+ if (f2 & (TRAP2_ONLY_DEMON))
+ {
+ text_out("It can only be set off by demons. ");
+ }
+ if (f2 & (TRAP2_ONLY_UNDEAD))
+ {
+ text_out("It can only be set off by undead. ");
+ }
+ if (f2 & (TRAP2_ONLY_ANIMAL))
+ {
+ text_out("It can only be set off by animals. ");
+ }
+ if (f2 & (TRAP2_ONLY_EVIL))
+ {
+ text_out("It can only be set off by evil creatures. ");
+ }
+ }
+
+ if (f2 & (TR2_FREE_ACT))
+ {
+ text_out("It provides immunity to paralysis. ");
+ }
+ if (f2 & (TR2_RES_FEAR))
+ {
+ text_out("It makes you completely fearless. ");
+ }
+
+ vn = 0;
+ if (f2 & (TR2_HOLD_LIFE))
+ {
+ vp[vn++] = "life draining";
+ }
+ if ((f2 & (TR2_RES_ACID)) && !(f2 & (TR2_IM_ACID)))
+ {
+ vp[vn++] = "acid";
+ }
+ if ((f2 & (TR2_RES_ELEC)) && !(f2 & (TR2_IM_ELEC)))
+ {
+ vp[vn++] = "electricity";
+ }
+ if ((f2 & (TR2_RES_FIRE)) && !(f2 & (TR2_IM_FIRE)))
+ {
+ vp[vn++] = "fire";
+ }
+ if ((f2 & (TR2_RES_COLD)) && !(f2 & (TR2_IM_COLD)))
+ {
+ vp[vn++] = "cold";
+ }
+ if (f2 & (TR2_RES_POIS))
+ {
+ vp[vn++] = "poison";
+ }
+ if (f2 & (TR2_RES_LITE))
+ {
+ vp[vn++] = "light";
+ }
+ if (f2 & (TR2_RES_DARK))
+ {
+ vp[vn++] = "dark";
+ }
+ if (f2 & (TR2_RES_BLIND))
+ {
+ vp[vn++] = "blindness";
+ }
+ if (f2 & (TR2_RES_CONF))
+ {
+ vp[vn++] = "confusion";
+ }
+ if (f2 & (TR2_RES_SOUND))
+ {
+ vp[vn++] = "sound";
+ }
+ if (f2 & (TR2_RES_SHARDS))
+ {
+ vp[vn++] = "shards";
+ }
+ if ((f2 & (TR2_RES_NETHER)) && !(f4 & (TR4_IM_NETHER)))
+ {
+ vp[vn++] = "nether";
+ }
+ if (f2 & (TR2_RES_NEXUS))
+ {
+ vp[vn++] = "nexus";
+ }
+ if (f2 & (TR2_RES_CHAOS))
+ {
+ vp[vn++] = "chaos";
+ }
+ if (f2 & (TR2_RES_DISEN))
+ {
+ vp[vn++] = "disenchantment";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It provides resistance ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("to ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out(vp[i]);
+ }
+ text_out(". ");
+ }
+
+ if (f2 & (TR2_SENS_FIRE))
+ {
+ text_out("It renders you especially vulnerable to fire. ");
+ }
+ if (f3 & (TR3_WRAITH))
+ {
+ text_out("It renders you incorporeal. ");
+ }
+ if (f5 & (TR5_WATER_BREATH))
+ {
+ text_out("It allows you to breathe underwater. ");
+ }
+ if (f5 & (TR5_MAGIC_BREATH))
+ {
+ text_out("It allows you to breathe without air. ");
+ }
+ if (f3 & (TR3_FEATHER))
+ {
+ text_out("It allows you to levitate. ");
+ }
+ if (f4 & (TR4_FLY))
+ {
+ text_out("It allows you to fly. ");
+ }
+ if (f4 & (TR4_CLIMB))
+ {
+ text_out("It allows you to climb mountains. ");
+ }
+ if (f5 & (TR5_IMMOVABLE))
+ {
+ text_out("It renders you immovable. ");
+ }
+ if (f3 & (TR3_SEE_INVIS))
+ {
+ text_out("It allows you to see invisible monsters. ");
+ }
+ if (esp)
+ {
+ if (esp & ESP_ALL) text_out("It gives telepathic powers. ");
+ else
+ {
+ vn = 0;
+ if (esp & ESP_ORC) vp[vn++] = "orcs";
+ if (esp & ESP_TROLL) vp[vn++] = "trolls";
+ if (esp & ESP_DRAGON) vp[vn++] = "dragons";
+ if (esp & ESP_SPIDER) vp[vn++] = "spiders";
+ if (esp & ESP_GIANT) vp[vn++] = "giants";
+ if (esp & ESP_DEMON) vp[vn++] = "demons";
+ if (esp & ESP_UNDEAD) vp[vn++] = "undead";
+ if (esp & ESP_EVIL) vp[vn++] = "evil beings";
+ if (esp & ESP_ANIMAL) vp[vn++] = "animals";
+ if (esp & ESP_THUNDERLORD) vp[vn++] = "thunderlords";
+ if (esp & ESP_GOOD) vp[vn++] = "good beings";
+ if (esp & ESP_NONLIVING) vp[vn++] = "non-living things";
+ if (esp & ESP_UNIQUE) vp[vn++] = "unique beings";
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It allows you to sense the presence ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("of ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out(vp[i]);
+ }
+ text_out(". ");
+ }
+ }
+ }
+
+ if (f3 & (TR3_SLOW_DIGEST))
+ {
+ text_out("It slows your metabolism. ");
+ }
+ if (f3 & (TR3_REGEN))
+ {
+ text_out("It speeds your regenerative powers. ");
+ }
+ if (f2 & (TR2_REFLECT))
+ {
+ text_out("It reflects bolts and arrows. ");
+ }
+ if (f3 & (TR3_SH_FIRE))
+ {
+ text_out("It produces a fiery sheath. ");
+ }
+ if (f3 & (TR3_SH_ELEC))
+ {
+ text_out("It produces an electric sheath. ");
+ }
+ if (f3 & (TR3_NO_MAGIC))
+ {
+ text_out("It produces an anti-magic shell. ");
+ }
+ if (f3 & (TR3_NO_TELE))
+ {
+ text_out("It prevents teleportation. ");
+ }
+ if (f3 & (TR3_XTRA_MIGHT))
+ {
+ text_out("It fires missiles with extra might. ");
+ }
+ if (f3 & (TR3_XTRA_SHOTS))
+ {
+ text_out("It fires missiles excessively fast. ");
+ }
+
+ vn = 0;
+ if (f5 & (TR5_DRAIN_MANA))
+ {
+ vc[vn] = TERM_BLUE;
+ vp[vn++] = "mana";
+ }
+ if (f5 & (TR5_DRAIN_HP))
+ {
+ vc[vn] = TERM_RED;
+ vp[vn++] = "life";
+ }
+ if (f3 & (TR3_DRAIN_EXP))
+ {
+ vc[vn] = TERM_L_DARK;
+ vp[vn++] = "experience";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("drains ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out_c(vc[i], vp[i]);
+ }
+ text_out(". ");
+ }
+
+ if (f3 & (TR3_BLESSED))
+ {
+ text_out("It has been blessed by the gods. ");
+ }
+ if (f4 & (TR4_AUTO_ID))
+ {
+ text_out("It identifies all items for you. ");
+ }
+
+ if (f3 & (TR3_TELEPORT))
+ {
+ text_out("It induces random teleportation. ");
+ }
+ if (f3 & (TR3_AGGRAVATE))
+ {
+ text_out("It aggravates nearby creatures. ");
+ }
+ if (f4 & (TR4_NEVER_BLOW))
+ {
+ text_out("It can't attack. ");
+ }
+ if (f4 & (TR4_BLACK_BREATH))
+ {
+ text_out("It fills you with the Black Breath. ");
+ }
+ if (cursed_p(o_ptr))
+ {
+ if (f3 & (TR3_PERMA_CURSE))
+ {
+ text_out("It is permanently cursed. ");
+ }
+ else if (f3 & (TR3_HEAVY_CURSE))
+ {
+ text_out("It is heavily cursed. ");
+ }
+ else
+ {
+ text_out("It is cursed. ");
+ }
+ }
+ if (f3 & (TR3_TY_CURSE))
+ {
+ text_out("It carries an ancient foul curse. ");
+ }
+
+ if (f4 & (TR4_DG_CURSE))
+ {
+ text_out("It carries an ancient Morgothian curse. ");
+ }
+ if (f4 & (TR4_CLONE))
+ {
+ text_out("It can clone monsters. ");
+ }
+ if (f4 & (TR4_CURSE_NO_DROP))
+ {
+ text_out("It cannot be dropped while cursed. ");
+ }
+ if (f3 & (TR3_AUTO_CURSE))
+ {
+ text_out("It can re-curse itself. ");
+ }
+
+ if (f4 & (TR4_CAPACITY))
+ {
+ text_out("It can hold more mana. ");
+ }
+ if (f4 & (TR4_CHEAPNESS))
+ {
+ text_out("It can cast spells for a lesser mana cost. ");
+ }
+ if (f4 & (TR4_FAST_CAST))
+ {
+ text_out("It can cast spells faster. ");
+ }
+ if (f4 & (TR4_CHARGING))
+ {
+ text_out("It regenerates its mana faster. ");
+ }
+
+ if (f5 & (TR5_RES_MORGUL))
+ {
+ text_out("It can resist being shattered by morgul beings. ");
+ }
+ if ((f3 & (TR3_IGNORE_ACID)) && (f3 & (TR3_IGNORE_FIRE)) && (f3 & (TR3_IGNORE_COLD)) && (f3 & (TR3_IGNORE_ELEC)))
+ {
+ text_out("It cannot be harmed by acid, cold, lightning or fire. ");
+ }
+ else
+ {
+ if (f3 & (TR3_IGNORE_ACID))
+ {
+ text_out("It cannot be harmed by acid. ");
+ }
+ if (f3 & (TR3_IGNORE_ELEC))
+ {
+ text_out("It cannot be harmed by electricity. ");
+ }
+ if (f3 & (TR3_IGNORE_FIRE))
+ {
+ text_out("It cannot be harmed by fire. ");
+ }
+ if (f3 & (TR3_IGNORE_COLD))
+ {
+ text_out("It cannot be harmed by cold. ");
+ }
+ }
+ }
+
+
+ if (!trim_down && !fff)
+ {
+ describe_device(o_ptr);
+
+ if (object_known_p(o_ptr))
+ {
+ /* Damage display for weapons */
+ if (wield_slot(o_ptr) == INVEN_WIELD)
+ display_weapon_damage(o_ptr);
+
+ /* Breakage/Damage display for boomerangs */
+ if (o_ptr->tval == TV_BOOMERANG)
+ {
+ if (artifact_p(o_ptr))
+ text_out("\nIt can never be broken.");
+ else
+ text_out("\nIt has 1% chance to break upon hit.");
+ display_ammo_damage(o_ptr);
+ }
+
+ /* Breakage/Damage display for ammo */
+ if (wield_slot(o_ptr) == INVEN_AMMO)
+ {
+ if (artifact_p(o_ptr))
+ {
+ text_out("\nIt can never be broken.");
+ }
+ /* Exclude exploding arrows */
+ else if (o_ptr->pval2 == 0)
+ {
+ text_out("\nIt has ");
+ text_out_c(TERM_L_RED, format("%d", breakage_chance(o_ptr)));
+ text_out("% chance to break upon hit.");
+ }
+ display_ammo_damage(o_ptr);
+ }
+
+ /* Monster recall for totems and corpses */
+ if (o_ptr->tval == TV_TOTEM)
+ {
+ monster_description_out(o_ptr->pval, 0);
+ }
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ monster_description_out(o_ptr->pval2, 0);
+ }
+ }
+
+ if (!object_known_p(o_ptr))
+ text_out("\nYou might need to identify the item to know some more about it...");
+ else if (!(o_ptr->ident & (IDENT_MENTAL)))
+ text_out("\nYou might need to *identify* the item to know more about it...");
+ }
+
+ /* Copying how others seem to do it. -- neil */
+ if (o_ptr->tval == TV_RING || o_ptr->tval == TV_AMULET ||
+ !trim_down || (ego_item_p(o_ptr)) || (artifact_p(o_ptr)))
+ {
+ /* Where did we found it ? */
+ if (o_ptr->found == OBJ_FOUND_MONSTER)
+ {
+ char m_name[80];
+
+ monster_race_desc(m_name, o_ptr->found_aux1, o_ptr->found_aux2);
+ text_out(format("\nYou found it in the remains of %s %s.",
+ m_name, object_out_desc_where_found(o_ptr->found_aux4, o_ptr->found_aux3)));
+ }
+ else if (o_ptr->found == OBJ_FOUND_FLOOR)
+ {
+ text_out(format("\nYou found it lying on the ground %s.",
+ object_out_desc_where_found(o_ptr->found_aux2, o_ptr->found_aux1)));
+ }
+ else if (o_ptr->found == OBJ_FOUND_VAULT)
+ {
+ text_out(format("\nYou found it lying in a vault %s.",
+ object_out_desc_where_found(o_ptr->found_aux2, o_ptr->found_aux1)));
+ }
+ else if (o_ptr->found == OBJ_FOUND_SPECIAL)
+ {
+ text_out("\nYou found it lying on the floor of a special level.");
+ }
+ else if (o_ptr->found == OBJ_FOUND_RUBBLE)
+ {
+ text_out("\nYou found it while digging a rubble.");
+ }
+ else if (o_ptr->found == OBJ_FOUND_REWARD)
+ {
+ text_out("\nIt was given to you as a reward.");
+ }
+ else if (o_ptr->found == OBJ_FOUND_STORE)
+ {
+ text_out(format("\nYou bought it from the %s.",
+ st_info[o_ptr->found_aux1].name + st_name));
+ }
+ else if (o_ptr->found == OBJ_FOUND_STOLEN)
+ {
+ text_out(format("\nYou stole it from the %s.",
+ st_info[o_ptr->found_aux1].name + st_name));
+ }
+ else if (o_ptr->found == OBJ_FOUND_SELFMADE)
+ {
+ text_out("\nYou made it yourself.");
+ }
+ /* useful for debugging
+ else
+ {
+ text_out("\nYou ordered it from a catalog in the Town.");
+ }*/
+ }
+
+ if (fff)
+ {
+ /* Flush the line position. */
+ text_out("\n");
+ text_out_file = NULL;
+ }
+ else
+ {
+ if (wait_for_it)
+ {
+ /* Wait for it */
+ inkey();
+
+ /* Restore the screen */
+ Term_load();
+ }
+ character_icky = FALSE;
+
+ }
+
+ /* Reset stuff for text_out */
+ text_out_hook = text_out_to_screen;
+
+ /* Gave knowledge */
+ return (TRUE);
+}
+
+
+
+/*
+ * Convert an inventory index into a one character label
+ * Note that the label does NOT distinguish inven/equip.
+ */
+char index_to_label(int i)
+{
+ /* Indexes for "inven" are easy */
+ if (i < INVEN_WIELD) return (I2A(i));
+
+ /* Indexes for "equip" are offset */
+ return (I2A(i - INVEN_WIELD));
+}
+
+
+/*
+ * Convert a label into the index of an item in the "inven"
+ * Return "-1" if the label does not indicate a real item
+ */
+s16b label_to_inven(int c)
+{
+ int i;
+
+ /* Convert */
+ i = (islower(c) ? A2I(c) : -1);
+
+ /* Verify the index */
+ if ((i < 0) || (i > INVEN_PACK)) return ( -1);
+
+ /* Empty slots can never be chosen */
+ if (!p_ptr->inventory[i].k_idx) return ( -1);
+
+ /* Return the index */
+ return (i);
+}
+
+
+/*
+ * Convert a label into the index of a item in the "equip"
+ * Return "-1" if the label does not indicate a real item
+ */
+s16b label_to_equip(int c)
+{
+ int i;
+
+ /* Convert */
+ i = ((islower(c) || (c > 'z')) ? A2I(c) : -1) + INVEN_WIELD;
+
+ /* Verify the index */
+ if ((i < INVEN_WIELD) || (i >= INVEN_TOTAL)) return ( -1);
+
+ /* Empty slots can never be chosen */
+ if (!p_ptr->inventory[i].k_idx) return ( -1);
+
+ /* Return the index */
+ return (i);
+}
+
+/*
+ * Returns the next free slot of the given "type", return the first
+ * if all are used
+ */
+int get_slot(int slot)
+{
+ int i = 0;
+
+ /* If there are at least one body part corretsonding, the find the free one */
+ if (p_ptr->body_parts[slot - INVEN_WIELD] == slot)
+ {
+ /* Find a free body part */
+ while ((i < 6) && (slot + i < INVEN_TOTAL) && (p_ptr->body_parts[slot - INVEN_WIELD + i] == slot))
+ {
+ if (p_ptr->body_parts[slot + i - INVEN_WIELD])
+ {
+ /* Free ? return the slot */
+ if (!p_ptr->inventory[slot + i].k_idx) return (slot + i);
+ }
+ else break;
+
+ i++;
+ }
+ /* Found nothing ? return the first one */
+ return slot;
+ }
+ /* No body parts ? return -1 */
+ else return ( -1);
+}
+
+/*
+ * Determine which equipment slot (if any) an item likes, ignoring the player's
+ * current body and stuff if ideal == TRUE
+ */
+s16b wield_slot_ideal(object_type *o_ptr, bool_ ideal)
+{
+ /* Try for a script first */
+ if (process_hooks_ret(HOOK_WIELD_SLOT, "d", "(O,d)", o_ptr, ideal))
+ return process_hooks_return[0].num;
+
+ /* Slot for equipment */
+ switch (o_ptr->tval)
+ {
+ case TV_DIGGING:
+ case TV_TOOL:
+ {
+ return ideal ? INVEN_TOOL : get_slot(INVEN_TOOL);
+ }
+
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ {
+ return ideal ? INVEN_WIELD : get_slot(INVEN_WIELD);
+ }
+
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_INSTRUMENT:
+ {
+ return ideal ? INVEN_BOW : get_slot(INVEN_BOW);
+ }
+
+ case TV_RING:
+ {
+ return ideal ? INVEN_RING : get_slot(INVEN_RING);
+ }
+
+ case TV_AMULET:
+ {
+ return ideal ? INVEN_NECK : get_slot(INVEN_NECK);
+ }
+
+ case TV_LITE:
+ {
+ return ideal ? INVEN_LITE : get_slot(INVEN_LITE);
+ }
+
+ case TV_DRAG_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_SOFT_ARMOR:
+ {
+ return ideal ? INVEN_BODY : get_slot(INVEN_BODY);
+ }
+
+ case TV_CLOAK:
+ {
+ return ideal ? INVEN_OUTER : get_slot(INVEN_OUTER);
+ }
+
+ case TV_SHIELD:
+ {
+ return ideal ? INVEN_ARM : get_slot(INVEN_ARM);
+ }
+
+ case TV_CROWN:
+ case TV_HELM:
+ {
+ return ideal ? INVEN_HEAD : get_slot(INVEN_HEAD);
+ }
+
+ case TV_GLOVES:
+ {
+ return ideal ? INVEN_HANDS : get_slot(INVEN_HANDS);
+ }
+
+ case TV_BOOTS:
+ {
+ return ideal ? INVEN_FEET : get_slot(INVEN_FEET);
+ }
+
+ case TV_HYPNOS:
+ {
+ return ideal ? INVEN_CARRY : get_slot(INVEN_CARRY);
+ }
+
+ case TV_SHOT:
+ {
+ if (ideal)
+ {
+ return INVEN_AMMO;
+ }
+ else if (p_ptr->inventory[INVEN_AMMO].k_idx &&
+ object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]) &&
+ p_ptr->inventory[INVEN_AMMO].number + o_ptr->number < MAX_STACK_SIZE)
+ {
+ return get_slot(INVEN_AMMO);
+ }
+ else if ((p_ptr->inventory[INVEN_BOW].k_idx) && (p_ptr->inventory[INVEN_BOW].tval == TV_BOW))
+ {
+ if (p_ptr->inventory[INVEN_BOW].sval < 10)
+ return get_slot(INVEN_AMMO);
+ }
+ return -1;
+ }
+
+ case TV_ARROW:
+ {
+ if (ideal)
+ {
+ return INVEN_AMMO;
+ }
+ else if (p_ptr->inventory[INVEN_AMMO].k_idx &&
+ object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]) &&
+ p_ptr->inventory[INVEN_AMMO].number + o_ptr->number < MAX_STACK_SIZE)
+ {
+ return get_slot(INVEN_AMMO);
+ }
+ else if ((p_ptr->inventory[INVEN_BOW].k_idx) && (p_ptr->inventory[INVEN_BOW].tval == TV_BOW))
+ {
+ if ((p_ptr->inventory[INVEN_BOW].sval >= 10) && (p_ptr->inventory[INVEN_BOW].sval < 20))
+ return get_slot(INVEN_AMMO);
+ }
+ return -1;
+ }
+
+ case TV_BOLT:
+ {
+ if (ideal)
+ {
+ return INVEN_AMMO;
+ }
+ else if (p_ptr->inventory[INVEN_AMMO].k_idx &&
+ object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]) &&
+ p_ptr->inventory[INVEN_AMMO].number + o_ptr->number < MAX_STACK_SIZE)
+ {
+ return get_slot(INVEN_AMMO);
+ }
+ else if ((p_ptr->inventory[INVEN_BOW].k_idx) && (p_ptr->inventory[INVEN_BOW].tval == TV_BOW))
+ {
+ if (p_ptr->inventory[INVEN_BOW].sval >= 20)
+ return get_slot(INVEN_AMMO);
+ }
+ return -1;
+ }
+ }
+
+ /* No slot available */
+ return ( -1);
+}
+
+/*
+ * Determine which equipment slot (if any) an item likes for the player's
+ * current body and stuff
+ */
+s16b wield_slot(object_type *o_ptr)
+{
+ return wield_slot_ideal(o_ptr, FALSE);
+}
+
+/*
+ * Return a string mentioning how a given item is carried
+ */
+cptr mention_use(int i)
+{
+ cptr p;
+
+ /* Examine the location */
+ switch (i)
+ {
+ case INVEN_WIELD:
+ case INVEN_WIELD + 1:
+ case INVEN_WIELD + 2:
+ p = "Wielding";
+ break;
+ case INVEN_BOW:
+ p = "Shooting";
+ break;
+ case INVEN_RING:
+ case INVEN_RING + 1:
+ case INVEN_RING + 2:
+ case INVEN_RING + 3:
+ case INVEN_RING + 4:
+ case INVEN_RING + 5:
+ p = "On finger";
+ break;
+ case INVEN_NECK:
+ case INVEN_NECK + 1:
+ p = "Around neck";
+ break;
+ case INVEN_LITE:
+ p = "Light source";
+ break;
+ case INVEN_BODY:
+ p = "On body";
+ break;
+ case INVEN_OUTER:
+ p = "About body";
+ break;
+ case INVEN_ARM:
+ case INVEN_ARM + 1:
+ case INVEN_ARM + 2:
+ p = "On arm";
+ break;
+ case INVEN_HEAD:
+ case INVEN_HEAD + 1:
+ p = "On head";
+ break;
+ case INVEN_HANDS:
+ case INVEN_HANDS + 1:
+ case INVEN_HANDS + 2:
+ p = "On hands";
+ break;
+ case INVEN_FEET:
+ case INVEN_FEET + 1:
+ p = "On feet";
+ break;
+ case INVEN_CARRY:
+ p = "Symbiote";
+ break;
+ case INVEN_AMMO:
+ p = "Quiver";
+ break;
+ case INVEN_TOOL:
+ p = "Using";
+ break;
+ default:
+ p = "In pack";
+ break;
+ }
+
+ /* Hack -- Heavy weapons */
+ if ((INVEN_WIELD <= i) && (i <= INVEN_WIELD + 2))
+ {
+ object_type *o_ptr;
+ o_ptr = &p_ptr->inventory[i];
+ if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10)
+ {
+ p = "Just lifting";
+ }
+ }
+
+ /* Hack -- music instruments and heavy bow */
+ if (i == INVEN_BOW)
+ {
+ object_type *o_ptr;
+ o_ptr = &p_ptr->inventory[i];
+ if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ p = "Playing";
+ }
+ else if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10)
+ {
+ p = "Just holding";
+ }
+ }
+
+ /* Return the result */
+ return (p);
+}
+
+
+/*
+ * Return a string describing how a given item is being worn.
+ * Currently, only used for items in the equipment, not inventory.
+ */
+cptr describe_use(int i)
+{
+ cptr p;
+
+ switch (i)
+ {
+ case INVEN_WIELD:
+ case INVEN_WIELD + 1:
+ case INVEN_WIELD + 2:
+ p = "attacking monsters with";
+ break;
+ case INVEN_BOW:
+ p = "shooting missiles with";
+ break;
+ case INVEN_RING:
+ case INVEN_RING + 1:
+ case INVEN_RING + 2:
+ case INVEN_RING + 3:
+ case INVEN_RING + 4:
+ case INVEN_RING + 5:
+ p = "wearing on your finger";
+ break;
+ case INVEN_NECK:
+ case INVEN_NECK + 1:
+ p = "wearing around your neck";
+ break;
+ case INVEN_LITE:
+ p = "using to light the way";
+ break;
+ case INVEN_BODY:
+ p = "wearing on your body";
+ break;
+ case INVEN_OUTER:
+ p = "wearing on your back";
+ break;
+ case INVEN_ARM:
+ case INVEN_ARM + 1:
+ case INVEN_ARM + 2:
+ p = "wearing on your arm";
+ break;
+ case INVEN_HEAD:
+ case INVEN_HEAD + 1:
+ p = "wearing on your head";
+ break;
+ case INVEN_HANDS:
+ case INVEN_HANDS + 1:
+ case INVEN_HANDS + 2:
+ p = "wearing on your hands";
+ break;
+ case INVEN_FEET:
+ case INVEN_FEET + 1:
+ p = "wearing on your feet";
+ break;
+ case INVEN_CARRY:
+ p = "in symbiosis with";
+ break;
+ case INVEN_AMMO:
+ p = "carrying in your quiver";
+ break;
+ case INVEN_TOOL:
+ p = "using as a tool";
+ break;
+ default:
+ p = "carrying in your pack";
+ break;
+ }
+
+ /* Hack -- Heavy weapons */
+ if ((INVEN_WIELD <= i) && (i <= INVEN_WIELD + 2))
+ {
+ object_type *o_ptr;
+ o_ptr = &p_ptr->inventory[i];
+ if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10)
+ {
+ p = "just lifting";
+ }
+ }
+
+ /* Hack -- Music instruments and heavy bow */
+ if (i == INVEN_BOW)
+ {
+ object_type *o_ptr;
+ o_ptr = &p_ptr->inventory[i];
+ if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ p = "playing music with";
+ }
+ else if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10)
+ {
+ p = "just holding";
+ }
+ }
+
+ /* Return the result */
+ return p;
+}
+
+/*
+ * Check an item against the item tester info
+ */
+bool_ item_tester_okay(object_type *o_ptr)
+{
+ /* Hack -- allow listing empty slots */
+ if (item_tester_full) return (TRUE);
+
+ /* Require an item */
+ if (!o_ptr->k_idx) return (FALSE);
+
+ /* Hack -- ignore "gold" */
+ if (o_ptr->tval == TV_GOLD) return (FALSE);
+
+ /* Check the tval */
+ if (item_tester_tval)
+ {
+ if (!(item_tester_tval == o_ptr->tval)) return (FALSE);
+ }
+
+ /* Check the hook */
+ if (item_tester_hook)
+ {
+ if (!(*item_tester_hook)(o_ptr)) return (FALSE);
+ }
+
+ /* Assume okay */
+ return (TRUE);
+}
+
+
+
+
+void show_equip_aux(bool_ mirror, bool_ everything);
+void show_inven_aux(bool_ mirror, bool_ everything);
+
+/*
+ * Choice window "shadow" of the "show_inven()" function
+ */
+void display_inven(void)
+{
+ show_inven_aux(TRUE, inventory_no_move);
+}
+
+
+
+/*
+ * Choice window "shadow" of the "show_equip()" function
+ */
+void display_equip(void)
+{
+ show_equip_aux(TRUE, inventory_no_move);
+}
+
+
+
+/* Get the color of the letter idx */
+byte get_item_letter_color(object_type *o_ptr)
+{
+ byte color = TERM_WHITE;
+
+ /* Must have knowlegde */
+ if (!object_known_p(o_ptr)) return (TERM_SLATE);
+
+ if (ego_item_p(o_ptr)) color = TERM_L_BLUE;
+ if (artifact_p(o_ptr)) color = TERM_YELLOW;
+ if (o_ptr->name1 && ( -1 != a_info[o_ptr->name1].set)) color = TERM_GREEN;
+ if (o_ptr->name1 && (a_info[o_ptr->name1].flags4 & TR4_ULTIMATE) && (o_ptr->ident & (IDENT_MENTAL))) color = TERM_VIOLET;
+
+ return (color);
+}
+
+
+/*
+ * Display the inventory.
+ *
+ * Hack -- do not display "trailing" empty slots
+ */
+void show_inven_aux(bool_ mirror, bool_ everything)
+{
+ int i, j, k, l, z = 0;
+ int row, col, len, lim;
+ int wid, hgt;
+ object_type *o_ptr;
+ char o_name[80];
+ char tmp_val[80];
+ int out_index[23];
+ byte out_color[23];
+ char out_desc[23][80];
+
+
+ /* Retrive current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Starting row */
+ row = mirror ? 0 : 1;
+
+ /* Starting column */
+ col = mirror ? 0 : 50;
+
+ /* Default "max-length" */
+ len = 79 - col;
+
+ /* Maximum space allowed for descriptions */
+ lim = 79 - 3;
+
+ /* Require space for weight (if needed) */
+ if (show_weights) lim -= 9;
+
+ /* Require space for icon */
+ if (show_inven_graph) lim -= 2;
+
+ /* Find the "final" slot */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Track */
+ z = i + 1;
+ }
+
+ /* Display the inventory */
+ for (k = 0, i = 0; i < z; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Is this item acceptable? */
+ if (!item_tester_okay(o_ptr))
+ {
+ if ( !everything )
+ continue;
+ out_index[k] = -i - 1;
+ }
+ else
+ {
+ /* Save the object index */
+ out_index[k] = i + 1;
+ }
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Hack -- enforce max length */
+ o_name[lim] = '\0';
+
+ /* Save the object color, and description */
+ out_color[k] = tval_to_attr[o_ptr->tval % 128];
+ (void)strcpy(out_desc[k], o_name);
+
+ /* Find the predicted "line length" */
+ l = strlen(out_desc[k]) + 5;
+
+ /* Be sure to account for the weight */
+ if (show_weights) l += 9;
+
+ /* Account for icon if displayed */
+ if (show_inven_graph) l += 2;
+
+ /* Maintain the maximum length */
+ if (l > len) len = l;
+
+ /* Advance to next "line" */
+ k++;
+ }
+
+ /* Find the column to start in */
+ if (mirror) col = 0;
+ else col = (len > wid - 4) ? 0 : (wid - len - 1);
+
+ /* Output each entry */
+ for (j = 0; j < k; j++)
+ {
+ /* Get the index */
+ i = out_index[j];
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[ABS(i) - 1];
+
+ /* Clear the line */
+ prt("", row + j, col ? col - 2 : col);
+
+ /* Prepare an index --(-- */
+ /* Prepare a blank index if not selectable */
+ if ( i > 0 )
+ sprintf(tmp_val, "%c)", index_to_label(i - 1));
+ else
+ sprintf(tmp_val, " ");
+
+ /* Clear the line with the (possibly indented) index */
+ c_put_str(get_item_letter_color(o_ptr), tmp_val, row + j, col);
+
+ /* Display graphics for object, if desired */
+ if (show_inven_graph)
+ {
+ byte a = object_attr(o_ptr);
+ char c = object_char(o_ptr);
+
+ if (!o_ptr->k_idx) c = ' ';
+
+ Term_draw(col + 3, row + j, a, c);
+ }
+
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], row + j,
+ show_inven_graph ? (col + 5) : (col + 3));
+
+ /* Display the weight if needed */
+ if (show_weights)
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ (void)sprintf(tmp_val, "%3d.%1d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, row + j, wid - 9);
+ }
+ }
+
+ /* Shadow windows */
+ if (mirror)
+ {
+ /* Erase the rest of the window */
+ for (j = row + k; j < Term->hgt; j++)
+ {
+ /* Erase the line */
+ Term_erase(0, j, 255);
+ }
+ }
+
+ /* Main window */
+ else
+ {
+ /* Make a "shadow" below the list (only if needed) */
+ if (j && (j < 23)) prt("", row + j, col ? col - 2 : col);
+ }
+}
+
+
+void show_inven()
+{
+ show_inven_aux(FALSE, FALSE);
+}
+
+void show_equip()
+{
+ show_equip_aux(FALSE, FALSE);
+}
+
+/*
+ * Display the equipment.
+ */
+void show_equip_aux(bool_ mirror, bool_ everything)
+{
+ int i, j, k, l;
+ int row, col, len, lim, idx;
+ int wid, hgt;
+ object_type *o_ptr;
+ char tmp_val[80];
+ char o_name[80];
+ int out_index[INVEN_TOOL - INVEN_WIELD];
+ int out_rindex[INVEN_TOOL - INVEN_WIELD];
+ byte out_color[INVEN_TOOL - INVEN_WIELD];
+ char out_desc[INVEN_TOOL - INVEN_WIELD][80];
+
+
+ /* Retrive current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Starting row */
+ row = mirror ? 0 : 1;
+
+ /* Starting column */
+ col = mirror ? 0 : 50;
+
+ /* Maximal length */
+ len = 79 - col;
+
+ /* Maximum space allowed for descriptions */
+ lim = 79 - 3;
+
+ /* Require space for labels (if needed) */
+ if (show_labels) lim -= (14 + 2);
+
+ /* Require space for weight (if needed) */
+ if (show_weights) lim -= 9;
+
+ if (show_equip_graph) lim -= 2;
+
+ /* Scan the equipment list */
+ idx = 0;
+ for (k = 0, i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ /* Is there actualy a body part here ? */
+ if (!p_ptr->body_parts[i - INVEN_WIELD]) continue;
+
+ /* Mega Hack -- don't show symbiote slot if we can't use it */
+ if ((i == INVEN_CARRY) && (!get_skill(SKILL_SYMBIOTIC))) continue;
+
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Inform the player that he/she can't use a shield */
+ if ((p_ptr->body_parts[i - INVEN_WIELD] == INVEN_ARM) &&
+ !o_ptr->k_idx &&
+ p_ptr->inventory[i - INVEN_ARM + INVEN_WIELD].k_idx)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_type *q_ptr = &p_ptr->inventory[i - INVEN_ARM + INVEN_WIELD];
+ char q_name[80];
+
+ /* Description */
+ object_desc(q_name, q_ptr, TRUE, 3);
+
+ /* Get weapon flags */
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_MUST2H)
+ {
+ sprintf(o_name, "(two handed) %s", q_name);
+
+ /* Truncate the description */
+ o_name[lim] = 0;
+
+ /* Save the index */
+ out_index[k] = idx;
+ out_rindex[k] = i;
+ idx++;
+
+ /* Save the color */
+ out_color[k] = TERM_L_RED;
+ (void)strcpy(out_desc[k], o_name);
+ continue;
+ }
+ }
+
+ if ((p_ptr->body_parts[i - INVEN_WIELD] == INVEN_WIELD) &&
+ !o_ptr->k_idx)
+ {
+ sprintf(o_name, "(%s)", melee_names[get_melee_skill()]);
+
+ /* Truncate the description */
+ o_name[lim] = 0;
+
+ /* Save the index */
+ out_index[k] = idx;
+ out_rindex[k] = i;
+ idx++;
+
+ /* Save the color */
+ out_color[k] = TERM_L_BLUE;
+ (void)strcpy(out_desc[k], o_name);
+ }
+ else
+ {
+ idx++;
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Truncate the description */
+ o_name[lim] = 0;
+ /* Is this item acceptable? */
+ if (!item_tester_okay(o_ptr))
+ {
+ if (!everything) continue;
+ out_index[k] = -1;
+ }
+ else
+ {
+ /* Save the index */
+ out_index[k] = idx;
+ }
+ out_rindex[k] = i;
+
+ /* Save the color */
+ out_color[k] = tval_to_attr[o_ptr->tval % 128];
+ (void)strcpy(out_desc[k], o_name);
+ }
+
+ /* Extract the maximal length (see below) */
+ l = strlen(out_desc[k]) + (2 + 3);
+
+ /* Increase length for labels (if needed) */
+ if (show_labels) l += (14 + 2);
+
+ /* Increase length for weight (if needed) */
+ if (show_weights) l += 9;
+
+ if (show_equip_graph) l += 2;
+
+ /* Maintain the max-length */
+ if (l > len) len = l;
+
+ /* Advance the entry */
+ k++;
+ }
+
+ /* Hack -- Find a column to start in */
+ if (mirror) col = 0;
+ else col = (len > wid - 4) ? 0 : (wid - len - 1);
+
+ /* Output each entry */
+ for (j = 0; j < k; j++)
+ {
+ if (j > 20) break;
+
+ /* Get the index */
+ i = out_index[j];
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[out_rindex[j]];
+
+ /* Clear the line */
+ prt("", row + j, col ? col - 2 : col);
+
+ /* Prepare an index --(-- */
+ if (out_index[j] >= 0 )
+ sprintf(tmp_val, "%c)", index_to_label(out_rindex[j]));
+ else
+ sprintf(tmp_val, " ");
+
+ /* Clear the line with the (possibly indented) index */
+ c_put_str(get_item_letter_color(o_ptr), tmp_val, row + j, col);
+
+ if (show_equip_graph)
+ {
+ byte a = object_attr(o_ptr);
+ char c = object_char(o_ptr);
+
+ if (!o_ptr->k_idx) c = ' ';
+
+ Term_draw(col + 3, row + j, a, c);
+ }
+
+ /* Use labels */
+ if (show_labels)
+ {
+ /* Mention the use */
+ (void)sprintf(tmp_val, "%-14s: ", mention_use(out_rindex[j]));
+ put_str(tmp_val, row + j, show_equip_graph ? col + 5 : col + 3);
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], row + j, show_equip_graph ? col + 21 : col + 19);
+ }
+
+ /* No labels */
+ else
+ {
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], row + j, show_equip_graph ? col + 5 : col + 3);
+ }
+
+ /* Display the weight if needed */
+ if (show_weights)
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ (void)sprintf(tmp_val, "%3d.%d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, row + j, wid - 9);
+ }
+ }
+
+
+ /* Shadow windows */
+ if (mirror)
+ {
+ /* Erase the rest of the window */
+ for (j = row + k; j < Term->hgt; j++)
+ {
+ /* Erase the line */
+ Term_erase(0, j, 255);
+ }
+ }
+
+ /* Main window */
+ else
+ {
+ /* Make a "shadow" below the list (only if needed) */
+ if (j && (j < 23)) prt("", row + j, col ? col - 2 : col);
+ }
+}
+
+
+
+
+/*
+ * Flip "inven" and "equip" in any sub-windows
+ */
+void toggle_inven_equip(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ /* Unused */
+ if (!angband_term[j]) continue;
+
+ /* Flip inven to equip */
+ if (window_flag[j] & (PW_INVEN))
+ {
+ /* Flip flags */
+ window_flag[j] &= ~(PW_INVEN);
+ window_flag[j] |= (PW_EQUIP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Flip inven to equip */
+ else if (window_flag[j] & (PW_EQUIP))
+ {
+ /* Flip flags */
+ window_flag[j] &= ~(PW_EQUIP);
+ window_flag[j] |= (PW_INVEN);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+ }
+}
+
+
+
+/*
+ * Verify the choice of an item.
+ *
+ * The item can be negative to mean "item on floor".
+ */
+bool_ verify(cptr prompt, int item)
+{
+ char o_name[80];
+
+ char out_val[160];
+
+ object_type *o_ptr;
+
+ /* Get object */
+ o_ptr = get_object(item);
+
+ /* Describe */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Prompt */
+ (void)sprintf(out_val, "%s %s? ", prompt, o_name);
+
+ /* Query */
+ return (get_check(out_val));
+}
+
+
+/*
+ * Hack -- allow user to "prevent" certain choices
+ *
+ * The item can be negative to mean "item on floor".
+ */
+static bool_ get_item_allow(int item)
+{
+ cptr s;
+
+ object_type *o_ptr;
+
+ /* Get object */
+ o_ptr = get_object(item);
+
+ /* No inscription */
+ if (!o_ptr->note) return (TRUE);
+
+ /* Find a '!' */
+ s = strchr(quark_str(o_ptr->note), '!');
+
+ /* Process preventions */
+ while (s)
+ {
+ /* Check the "restriction" */
+ if ((s[1] == command_cmd) || (s[1] == '*'))
+ {
+ /* Verify the choice */
+ if (!verify("Really try", item)) return (FALSE);
+ }
+
+ /* Find another '!' */
+ s = strchr(s + 1, '!');
+ }
+
+ /* Allow it */
+ return (TRUE);
+}
+
+
+
+/*
+ * Auxiliary function for "get_item()" -- test an index
+ */
+static bool_ get_item_okay(int i)
+{
+ /* Illegal items */
+ if ((i < 0) || (i >= INVEN_TOTAL)) return (FALSE);
+
+ /* Verify the item */
+ if (!item_tester_okay(&p_ptr->inventory[i])) return (FALSE);
+
+ /* Assume okay */
+ return (TRUE);
+}
+
+
+
+/*
+ * Find the "first" inventory object with the given "tag".
+ *
+ * A "tag" is a char "n" appearing as "@n" anywhere in the
+ * inscription of an object.
+ *
+ * Also, the tag "@xn" will work as well, where "n" is a tag-char,
+ * and "x" is the "current" command_cmd code.
+ */
+static int get_tag(int *cp, char tag)
+{
+ int i;
+ cptr s;
+
+
+ /* Check every object */
+ for (i = 0; i < INVEN_TOTAL; ++i)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip empty inscriptions */
+ if (!o_ptr->note) continue;
+
+ /* Find a '@' */
+ s = strchr(quark_str(o_ptr->note), '@');
+
+ /* Process all tags */
+ while (s)
+ {
+ /* Check the normal tags */
+ if (s[1] == tag)
+ {
+ /* Save the actual inventory ID */
+ *cp = i;
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Check the special tags */
+ if ((s[1] == command_cmd) && (s[2] == tag))
+ {
+ /* Save the actual inventory ID */
+ *cp = i;
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Find another '@' */
+ s = strchr(s + 1, '@');
+ }
+ }
+
+ /* No such tag */
+ return (FALSE);
+}
+
+/*
+ * scan_floor --
+ *
+ * Return a list of o_list[] indexes of items at the given cave
+ * location. Valid flags are:
+ *
+ * mode & 0x01 -- Item tester
+ * mode & 0x02 -- Marked items only
+ * mode & 0x04 -- Stop after first
+ */
+bool_ scan_floor(int *items, int *item_num, int y, int x, int mode)
+{
+ int this_o_idx, next_o_idx;
+
+ int num = 0;
+
+ (*item_num) = 0;
+
+ /* Sanity */
+ if (!in_bounds(y, x)) return (FALSE);
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = cave[y][x].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Item tester */
+ if ((mode & 0x01) && !item_tester_okay(o_ptr)) continue;
+
+ /* Marked */
+ if ((mode & 0x02) && !o_ptr->marked) continue;
+
+ /* Accept this item */
+ items[num++] = this_o_idx;
+
+ /* Only one */
+ if (mode & 0x04) break;
+
+ /* XXX Hack -- Enforce limit */
+ if (num == 23) break;
+ }
+
+ /* Number of items */
+ (*item_num) = num;
+
+ /* Result */
+ return (num != 0);
+}
+
+/*
+ * Display a list of the items on the floor at the given location.
+ */
+void show_floor(int y, int x)
+{
+ int i, j, k, l;
+ int col, len, lim;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char tmp_val[80];
+
+ int out_index[23];
+ byte out_color[23];
+ char out_desc[23][80];
+
+ int floor_list[23], floor_num;
+
+ /* Default length */
+ len = 79 - 50;
+
+ /* Maximum space allowed for descriptions */
+ lim = 79 - 3;
+
+ /* Require space for weight (if needed) */
+ if (show_weights) lim -= 9;
+
+ /* Scan for objects in the grid, using item_tester_okay() */
+ (void) scan_floor(floor_list, &floor_num, y, x, 0x01);
+
+ /* Display the inventory */
+ for (k = 0, i = 0; i < floor_num; i++)
+ {
+ o_ptr = &o_list[floor_list[i]];
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Hack -- enforce max length */
+ o_name[lim] = '\0';
+
+ /* Save the index */
+ out_index[k] = i;
+
+ /* Acquire inventory color */
+ out_color[k] = tval_to_attr[o_ptr->tval & 0x7F];
+
+ /* Save the object description */
+ strcpy(out_desc[k], o_name);
+
+ /* Find the predicted "line length" */
+ l = strlen(out_desc[k]) + 5;
+
+ /* Be sure to account for the weight */
+ if (show_weights) l += 9;
+
+ /* Maintain the maximum length */
+ if (l > len) len = l;
+
+ /* Advance to next "line" */
+ k++;
+ }
+
+ /* Find the column to start in */
+ col = (len > 76) ? 0 : (79 - len);
+
+ /* Output each entry */
+ for (j = 0; j < k; j++)
+ {
+ /* Get the index */
+ i = floor_list[out_index[j]];
+
+ /* Get the item */
+ o_ptr = &o_list[i];
+
+ /* Clear the line */
+ prt("", j + 1, col ? col - 2 : col);
+
+ /* Prepare an index --(-- */
+ sprintf(tmp_val, "%c)", index_to_label(j));
+
+ /* Clear the line with the (possibly indented) index */
+ put_str(tmp_val, j + 1, col);
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], j + 1, col + 3);
+
+ /* Display the weight if needed */
+ if (show_weights)
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ sprintf(tmp_val, "%3d.%1d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, j + 1, 71);
+ }
+ }
+
+ /* Make a "shadow" below the list (only if needed) */
+ if (j && (j < 23)) prt("", j + 1, col ? col - 2 : col);
+}
+
+/*
+ * This version of get_item() is called by get_item() when
+ * the easy_floor is on.
+ */
+bool_ (*get_item_extra_hook)(int *cp);
+bool_ get_item_floor(int *cp, cptr pmt, cptr str, int mode)
+{
+ char n1 = 0, n2 = 0, which = ' ';
+
+ int j, k, i1, i2, e1, e2;
+
+ bool_ done, item;
+
+ bool_ oops = FALSE;
+
+ bool_ equip = FALSE;
+ bool_ inven = FALSE;
+ bool_ floor = FALSE;
+ bool_ extra = FALSE;
+ bool_ automat = FALSE;
+
+ bool_ allow_equip = FALSE;
+ bool_ allow_inven = FALSE;
+ bool_ allow_floor = FALSE;
+
+ bool_ toggle = FALSE;
+
+ char tmp_val[160];
+ char out_val[160];
+
+ int floor_num, floor_list[23], floor_top = 0;
+
+ k = 0;
+
+ /* Get the item index */
+ if (repeat_pull(cp))
+ {
+ /* Floor item? */
+ if (*cp < 0)
+ {
+ object_type *o_ptr;
+
+ /* Special index */
+ k = 0 - (*cp);
+
+ /* Acquire object */
+ o_ptr = &o_list[k];
+
+ /* Validate the item */
+ if (item_tester_okay(o_ptr))
+ {
+ /* Forget the item_tester_tval restriction */
+ item_tester_tval = 0;
+
+ /* Forget the item_tester_hook restriction */
+ item_tester_hook = NULL;
+
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Verify the item */
+ else if (get_item_okay(*cp))
+ {
+ /* Forget the item_tester_tval restriction */
+ item_tester_tval = 0;
+
+ /* Forget the item_tester_hook restriction */
+ item_tester_hook = NULL;
+
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+
+ /* Extract args */
+ if (mode & (USE_EQUIP)) equip = TRUE;
+ if (mode & (USE_INVEN)) inven = TRUE;
+ if (mode & (USE_FLOOR)) floor = TRUE;
+ if (mode & (USE_EXTRA)) extra = TRUE;
+ if (mode & (USE_AUTO)) automat = TRUE;
+
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Not done */
+ done = FALSE;
+
+ /* No item selected */
+ item = FALSE;
+
+
+ /* Full inventory */
+ i1 = 0;
+ i2 = INVEN_PACK - 1;
+
+ /* Forbid inventory */
+ if (!inven) i2 = -1;
+
+ /* Restrict inventory indexes */
+ while ((i1 <= i2) && (!get_item_okay(i1))) i1++;
+ while ((i1 <= i2) && (!get_item_okay(i2))) i2--;
+
+
+ /* Full equipment */
+ e1 = INVEN_WIELD;
+ e2 = INVEN_TOTAL - 1;
+
+ /* Forbid equipment */
+ if (!equip) e2 = -1;
+
+ /* Restrict equipment indexes */
+ while ((e1 <= e2) && (!get_item_okay(e1))) e1++;
+ while ((e1 <= e2) && (!get_item_okay(e2))) e2--;
+
+
+ /* Count "okay" floor items */
+ floor_num = 0;
+
+ /* Restrict floor usage */
+ if (floor)
+ {
+ /* Scan all objects in the grid */
+ (void) scan_floor(floor_list, &floor_num, p_ptr->py, p_ptr->px, 0x01);
+ }
+
+ /* Accept inventory */
+ if (i1 <= i2) allow_inven = TRUE;
+
+ /* Accept equipment */
+ if (e1 <= e2) allow_equip = TRUE;
+
+ /* Accept floor */
+ if (floor_num) allow_floor = TRUE;
+
+ /* Require at least one legal choice */
+ if (!allow_inven && !allow_equip && !allow_floor)
+ {
+ /* Oops */
+ oops = TRUE;
+
+ /* Done */
+ done = TRUE;
+ }
+
+ /* Analyze choices */
+ else
+ {
+ /* Hack -- Start on equipment if requested */
+ if ((command_wrk == (USE_EQUIP)) && allow_equip)
+ {
+ command_wrk = (USE_EQUIP);
+ }
+
+ /* Use inventory if allowed */
+ else if (allow_inven)
+ {
+ command_wrk = (USE_INVEN);
+ }
+
+ /* Use equipment if allowed */
+ else if (allow_equip)
+ {
+ command_wrk = (USE_EQUIP);
+ }
+
+ /* Use floor if allowed */
+ else if (allow_floor)
+ {
+ command_wrk = (USE_FLOOR);
+ }
+ }
+
+ /* Save screen */
+ screen_save();
+
+ /* Repeat until done */
+ while (!done)
+ {
+ /* Show choices */
+ if (show_choices)
+ {
+ int ni = 0;
+ int ne = 0;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ /* Unused */
+ if (!angband_term[j]) continue;
+
+ /* Count windows displaying inven */
+ if (window_flag[j] & (PW_INVEN)) ni++;
+
+ /* Count windows displaying equip */
+ if (window_flag[j] & (PW_EQUIP)) ne++;
+ }
+
+ /* Toggle if needed */
+ if ((command_wrk == (USE_EQUIP) && ni && !ne) ||
+ (command_wrk == (USE_INVEN) && !ni && ne))
+ {
+ /* Toggle */
+ toggle_inven_equip();
+
+ /* Track toggles */
+ toggle = !toggle;
+ }
+
+ /* Update */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Redraw windows */
+ window_stuff();
+ }
+
+ /* Inventory screen */
+ if (command_wrk == (USE_INVEN))
+ {
+ /* Extract the legal requests */
+ n1 = I2A(i1);
+ n2 = I2A(i2);
+
+ /* Redraw */
+ show_inven();
+ }
+
+ /* Equipment screen */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ /* Extract the legal requests */
+ n1 = I2A(e1 - INVEN_WIELD);
+ n2 = I2A(e2 - INVEN_WIELD);
+
+ /* Redraw */
+ show_equip();
+ }
+
+ /* Floor screen */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ j = floor_top;
+ k = MIN(floor_top + 23, floor_num) - 1;
+
+ /* Extract the legal requests */
+ n1 = I2A(j - floor_top);
+ n2 = I2A(k - floor_top);
+
+ /* Redraw */
+ show_floor(p_ptr->py, p_ptr->px);
+ }
+
+ /* Viewing inventory */
+ if (command_wrk == (USE_INVEN))
+ {
+ /* Begin the prompt */
+ sprintf(out_val, "Inven:");
+
+ /* Build the prompt */
+ sprintf(tmp_val, " %c-%c,",
+ index_to_label(i1), index_to_label(i2));
+
+ /* Append */
+ strcat(out_val, tmp_val);
+
+ /* Append */
+ if (allow_equip) strcat(out_val, " / for Equip,");
+
+ /* Append */
+ if (allow_floor) strcat(out_val, " - for floor,");
+ }
+
+ /* Viewing equipment */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ /* Begin the prompt */
+ sprintf(out_val, "Equip:");
+
+ /* Build the prompt */
+ sprintf(tmp_val, " %c-%c,",
+ index_to_label(e1), index_to_label(e2));
+
+ /* Append */
+ strcat(out_val, tmp_val);
+
+ /* Append */
+ if (allow_inven) strcat(out_val, " / for Inven,");
+
+ /* Append */
+ if (allow_floor) strcat(out_val, " - for floor,");
+ }
+
+ /* Viewing floor */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ /* Begin the prompt */
+ sprintf(out_val, "Floor:");
+
+ /* Build the prompt */
+ sprintf(tmp_val, " %c-%c,", n1, n2);
+
+ /* Append */
+ strcat(out_val, tmp_val);
+
+ /* Append */
+ if (allow_inven)
+ {
+ strcat(out_val, " / for Inven,");
+ }
+ else if (allow_equip)
+ {
+ strcat(out_val, " / for Equip,");
+ }
+ }
+
+ /* Extra? */
+ if (extra)
+ {
+ strcat(out_val, " @ for extra selection,");
+ }
+
+ /* Create automatizer rule?? */
+ if (automat)
+ {
+ if (automatizer_create)
+ strcat(out_val, " $ new automatizer rule(ON),");
+ else
+ strcat(out_val, " $ new automatizer rule(OFF),");
+ }
+
+ /* Finish the prompt */
+ strcat(out_val, " ESC");
+
+ /* Build the prompt */
+ sprintf(tmp_val, "(%s) %s", out_val, pmt);
+
+ /* Show the prompt */
+ prt(tmp_val, 0, 0);
+
+ /* Get a key */
+ which = inkey();
+
+ /* Parse it */
+ switch (which)
+ {
+ case ESCAPE:
+ {
+ done = TRUE;
+ break;
+ }
+
+ case '/':
+ {
+ if (command_wrk == (USE_INVEN))
+ {
+ if (!allow_equip)
+ {
+ bell();
+ break;
+ }
+ command_wrk = (USE_EQUIP);
+ }
+ else if (command_wrk == (USE_EQUIP))
+ {
+ if (!allow_inven)
+ {
+ bell();
+ break;
+ }
+ command_wrk = (USE_INVEN);
+ }
+ else if (command_wrk == (USE_FLOOR))
+ {
+ if (allow_inven)
+ {
+ command_wrk = (USE_INVEN);
+ }
+ else if (allow_equip)
+ {
+ command_wrk = (USE_EQUIP);
+ }
+ else
+ {
+ bell();
+ break;
+ }
+ }
+
+ /* Hack -- Fix screen */
+ screen_load();
+ screen_save();
+
+ /* Need to redraw */
+ break;
+ }
+
+ case '-':
+ {
+ if (!allow_floor)
+ {
+ bell();
+ break;
+ }
+
+ /*
+ * If we are already examining the floor, and there
+ * is only one item, we will always select it.
+ * If we aren't examining the floor and there is only
+ * one item, we will select it if floor_query_flag
+ * is FALSE.
+ */
+ if (floor_num == 1)
+ {
+ if (command_wrk == (USE_FLOOR))
+ {
+ /* Special index */
+ k = 0 - floor_list[0];
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+
+ break;
+ }
+ }
+
+ /* Hack -- Fix screen */
+ screen_load();
+ screen_save();
+
+ command_wrk = (USE_FLOOR);
+
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ /* Look up the tag */
+ if (!get_tag(&k, which))
+ {
+ bell();
+ break;
+ }
+
+ /* Hack -- Validate the item */
+ if ((k < INVEN_WIELD) ? !inven : !equip)
+ {
+ bell();
+ break;
+ }
+
+ /* Validate the item */
+ if (!get_item_okay(k))
+ {
+ bell();
+ break;
+ }
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+ break;
+ }
+
+ case '\n':
+ case '\r':
+ {
+ /* Choose "default" inventory item */
+ if (command_wrk == (USE_INVEN))
+ {
+ k = ((i1 == i2) ? i1 : -1);
+ }
+
+ /* Choose "default" equipment item */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ k = ((e1 == e2) ? e1 : -1);
+ }
+
+ /* Choose "default" floor item */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ if (floor_num == 1)
+ {
+ /* Special index */
+ k = 0 - floor_list[0];
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+ }
+ break;
+ }
+
+ /* Validate the item */
+ if (!get_item_okay(k))
+ {
+ bell();
+ break;
+ }
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+ break;
+ }
+
+ case '@':
+ {
+ int i;
+
+ if (extra && get_item_extra_hook(&i))
+ {
+ (*cp) = i;
+ item = TRUE;
+ done = TRUE;
+ }
+ break;
+ }
+
+ case '$':
+ {
+ automatizer_create = !automatizer_create;
+ break;
+ }
+
+ default:
+ {
+ int ver;
+
+ ver = isupper(which);
+ which = tolower(which);
+
+ /* Convert letter to inventory index */
+ if (command_wrk == (USE_INVEN))
+ {
+ k = label_to_inven(which);
+ if (k == -1)
+ {
+ bell();
+ break;
+ }
+ }
+
+ /* Convert letter to equipment index */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ k = label_to_equip(which);
+ if (k == -1)
+ {
+ bell();
+ break;
+ }
+ }
+
+ /* Convert letter to floor index */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ k = islower(which) ? A2I(which) : -1;
+ if (k < 0 || k >= floor_num)
+ {
+ bell();
+ break;
+ }
+
+ /* Special index */
+ k = 0 - floor_list[k];
+ }
+
+ /* Validate the item */
+ if ((k >= 0) && !get_item_okay(k))
+ {
+ bell();
+ break;
+ }
+
+ /* Verify the item */
+ if (ver && !verify("Try", k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Fix the screen */
+ screen_load();
+
+ /* Forget the item_tester_tval restriction */
+ item_tester_tval = 0;
+
+ /* Forget the item_tester_hook restriction */
+ item_tester_hook = NULL;
+
+ /* Track */
+ if (item && done)
+ {
+ if (*cp >= 0)
+ {
+ object_track(&p_ptr->inventory[*cp]);
+ }
+ else
+ {
+ object_track(&o_list[0 - *cp]);
+ }
+ }
+
+ /* Clean up */
+ if (show_choices)
+ {
+ /* Toggle again if needed */
+ if (toggle) toggle_inven_equip();
+
+ /* Update */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Window stuff */
+ window_stuff();
+ }
+
+
+ /* Clear the prompt line */
+ prt("", 0, 0);
+
+ /* Warning if needed */
+ if (oops && str) msg_print(str);
+
+
+ if (item) repeat_push(*cp);
+
+ /* Result */
+ return (item);
+}
+
+
+/*
+ * Let the user select an item, save its "index"
+ *
+ * Return TRUE only if an acceptable item was chosen by the user.
+ *
+ * The selected item must satisfy the "item_tester_hook()" function,
+ * if that hook is set, and the "item_tester_tval", if that value is set.
+ *
+ * All "item_tester" restrictions are cleared before this function returns.
+ *
+ * The user is allowed to choose acceptable items from the equipment,
+ * inventory, or floor, respectively, if the proper flag was given,
+ * and there are any acceptable items in that location.
+ *
+ * The equipment or inventory are displayed (even if no acceptable
+ * items are in that location) if the proper flag was given.
+ *
+ * If there are no acceptable items available anywhere, and "str" is
+ * not NULL, then it will be used as the text of a warning message
+ * before the function returns.
+ *
+ * Note that the user must press "-" to specify the item on the floor,
+ * and there is no way to "examine" the item on the floor, while the
+ * use of "capital" letters will "examine" an inventory/equipment item,
+ * and prompt for its use.
+ *
+ * If a legal item is selected from the inventory, we save it in "cp"
+ * directly (0 to 35), and return TRUE.
+ *
+ * If a legal item is selected from the floor, we save it in "cp" as
+ * a negative (-1 to -511), and return TRUE.
+ *
+ * If no item is available, we do nothing to "cp", and we display a
+ * warning message, using "str" if available, and return FALSE.
+ *
+ * If no item is selected, we do nothing to "cp", and return FALSE.
+ *
+ * Global "p_ptr->command_new" is used when viewing the inventory or equipment
+ * to allow the user to enter a command while viewing those screens, and
+ * also to induce "auto-enter" of stores, and other such stuff.
+ *
+ * Global "p_ptr->command_wrk" is used to choose between equip/inven listings.
+ * If it is TRUE then we are viewing inventory, else equipment.
+ *
+ * We always erase the prompt when we are done, leaving a blank line,
+ * or a warning message, if appropriate, if no items are available.
+ */
+bool_ get_item(int *cp, cptr pmt, cptr str, int mode)
+{
+ automatizer_create = FALSE;
+
+ return get_item_floor(cp, pmt, str, mode);
+}
+
+/*
+ * Hook to determine if an object is getable
+ */
+static bool_ item_tester_hook_getable(object_type *o_ptr)
+{
+ if (!inven_carry_okay(o_ptr)) return (FALSE);
+
+ if ((o_ptr->tval == TV_HYPNOS) && (!get_skill(SKILL_SYMBIOTIC))) return FALSE;
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+/*
+ * Wear a single item from o_ptr
+ */
+int wear_ammo(object_type *o_ptr)
+{
+ int slot, num = 1;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Check the slot */
+ slot = wield_slot(o_ptr);
+
+ if(slot == -1)
+ return -1;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain local object */
+ object_copy(q_ptr, o_ptr);
+
+ num = o_ptr->number;
+
+ /* Modify quantity */
+ q_ptr->number = num;
+
+ /* Access the wield slot */
+ o_ptr = &p_ptr->inventory[slot];
+
+ q_ptr->number += o_ptr->number;
+
+ /* Wear the new stuff */
+ object_copy(o_ptr, q_ptr);
+
+ /* Increment the equip counter by hand */
+ equip_cnt++;
+
+ /* Cursed! */
+ if (cursed_p(o_ptr))
+ {
+ /* Warn the player */
+ msg_print("Oops! It feels deathly cold!");
+
+ /* Note the curse */
+ o_ptr->ident |= (IDENT_SENSE);
+ o_ptr->sense = SENSE_CURSED;
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Recalculate hitpoint */
+ p_ptr->update |= (PU_HP);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA);
+
+ /* Redraw monster hitpoint */
+ p_ptr->redraw |= (PR_MH);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ return slot;
+}
+
+
+/*
+ * Try to pickup arrows
+ */
+void pickup_ammo()
+{
+ s16b this_o_idx, next_o_idx = 0, slot;
+ char o_name[80];
+
+ /* Scan the pile of objects */
+ for (this_o_idx = cave[p_ptr->py][p_ptr->px].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ if (object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]))
+ {
+ msg_print("You add the ammo to your quiver.");
+ slot = wear_ammo(o_ptr);
+
+ if (slot != -1)
+ {
+ /* Get the item again */
+ o_ptr = &p_ptr->inventory[slot];
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).", o_name, index_to_label(slot));
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+ }
+ }
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+ }
+}
+
+
+/*
+ * Make the player carry everything in a grid
+ *
+ * If "pickup" is FALSE then only gold will be picked up
+ *
+ * This is called by py_pickup() when easy_floor is TRUE.
+ */
+bool_ can_carry_heavy(object_type *o_ptr)
+{
+ /* Query if object is heavy */
+ if (prompt_pickup_heavy)
+ {
+ int i, j;
+ int old_enc = 0;
+ int new_enc = 0;
+
+ /* Extract the "weight limit" (in tenth pounds) */
+ i = weight_limit();
+
+ /* Calculate current encumbarance */
+ j = calc_total_weight();
+
+ /* Apply encumbarance from weight */
+ if (j > i / 2) old_enc = ((j - (i / 2)) / (i / 10));
+
+ /* Increase the weight, recalculate encumbarance */
+ j += (o_ptr->number * o_ptr->weight);
+
+ /* Apply encumbarance from weight */
+ if (j > i / 2) new_enc = ((j - (i / 2)) / (i / 10));
+
+ /* Should we query? */
+ if (new_enc > old_enc)
+ {
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+/* Do the actuall picking up */
+void object_pickup(int this_o_idx)
+{
+ int slot = 0;
+ char o_name[80] = "";
+ object_type *q_ptr, *o_ptr;
+
+ /* Access the item */
+ o_ptr = &o_list[this_o_idx];
+
+ if (p_ptr->auto_id)
+ {
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ }
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Note that the pack is too full */
+ if (!inven_carry_okay(o_ptr) && !object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]))
+ {
+ msg_format("You have no room for %s.", o_name);
+ }
+
+ /* Pick up object */
+ else
+ {
+ /* Tell the scripts */
+ if (process_hooks(HOOK_GET, "(O,d)", o_ptr, this_o_idx))
+ return;
+
+ q_ptr = &p_ptr->inventory[INVEN_AMMO];
+
+ /* Carry the item */
+ if (object_similar(o_ptr, q_ptr))
+ {
+ msg_print("You add the ammo to your quiver.");
+ slot = wear_ammo(o_ptr);
+ }
+ else
+ {
+ slot = inven_carry(o_ptr, FALSE);
+ }
+
+ /* Sanity check */
+ if (slot != -1)
+ {
+ /* Get the item again */
+ o_ptr = &p_ptr->inventory[slot];
+
+ object_track(o_ptr);
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).", o_name, index_to_label(slot));
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ /* Sense object. */
+ sense_inventory();
+ }
+ }
+}
+
+
+void py_pickup_floor(int pickup)
+{
+ s16b this_o_idx, next_o_idx = 0;
+
+ char o_name[80] = "";
+ object_type *o_ptr = 0;
+
+ int floor_num = 0, floor_o_idx = 0;
+
+ bool_ do_pickup = TRUE;
+
+ bool_ do_ask = TRUE;
+
+ /* Hack -- ignore monster traps */
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_MON_TRAP) return;
+
+ /* Try to grab ammo */
+ pickup_ammo();
+
+ /* Mega Hack -- If we have auto-Id, do an ID sweep *before* squleching,
+ * so that we don't have to walk over things twice to get them
+ * squelched. --dsb */
+ if (p_ptr->auto_id)
+ {
+ this_o_idx = cave[p_ptr->py][p_ptr->px].o_idx;
+
+ for (; this_o_idx; this_o_idx = next_o_idx)
+ {
+ /* Aquire the object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire the next object index */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Identify Object */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ }
+ }
+
+ /* Squeltch the floor */
+ squeltch_grid();
+
+ /* Scan the pile of objects */
+ for (this_o_idx = cave[p_ptr->py][p_ptr->px].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Hack -- disturb */
+ disturb(0, 0);
+
+ /* Pick up gold */
+ if (o_ptr->tval == TV_GOLD)
+ {
+ char goldname[80];
+ object_desc(goldname, o_ptr, TRUE, 3);
+ /* Message */
+ msg_format("You have found %ld gold pieces worth of %s.",
+ (long)o_ptr->pval, goldname);
+
+ /* Collect the gold */
+ p_ptr->au += o_ptr->pval;
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Delete the gold */
+ delete_object_idx(this_o_idx);
+
+ continue;
+ }
+
+ {
+ char testdesc[80];
+
+ object_desc(testdesc, o_ptr, TRUE, 3);
+ if (0 != strncmp(testdesc, "(nothing)", 80))
+ {
+ strncpy(o_name, testdesc, 80);
+ }
+ }
+
+ /* Count non-gold */
+ floor_num++;
+
+ /* Remember this index */
+ floor_o_idx = this_o_idx;
+ }
+
+ /* There were no non-gold items */
+ if (!floor_num) return;
+
+ /* Mention number of items */
+ if (!pickup)
+ {
+ /* One item */
+ if (floor_num == 1)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[floor_o_idx];
+
+ /* Message */
+ msg_format("You see %s.", o_name);
+ }
+
+ /* Multiple items */
+ else
+ {
+ /* Message */
+ msg_format("You see a pile of %d items.", floor_num);
+ }
+
+ /* Done */
+ return;
+ }
+
+ /* One item */
+ if (floor_num == 1)
+ {
+ /* Hack -- query every item */
+ if (carry_query_flag || (!can_carry_heavy(&o_list[floor_o_idx])))
+ {
+ if (!inven_carry_okay(o_ptr) && !object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]))
+ {
+ object_desc(o_name, o_ptr, TRUE, 3);
+ msg_format("You have no room for %s.", o_name);
+ do_pickup = FALSE;
+ }
+ else
+ {
+ char out_val[160];
+ sprintf(out_val, "Pick up %s? ", o_name);
+ do_pickup = get_check(out_val);
+ }
+ }
+
+ /* Don't ask */
+ do_ask = FALSE;
+
+ if ((o_list[floor_o_idx].tval == TV_HYPNOS) && (!get_skill(SKILL_SYMBIOTIC)))
+ do_pickup = FALSE;
+ else
+ this_o_idx = floor_o_idx;
+ }
+
+ /* Ask */
+ if (do_ask)
+ {
+ cptr q, s;
+
+ int item;
+
+ /* Get an item */
+
+ item_tester_hook = item_tester_hook_getable;
+
+ q = "Get which item? ";
+ s = "You have no room in your pack for any of the items here.";
+ if (get_item(&item, q, s, (USE_FLOOR)))
+ {
+ this_o_idx = 0 - item;
+
+ if (!can_carry_heavy(&o_list[this_o_idx]))
+ {
+ char out_val[160];
+
+ /* Describe the object */
+ object_desc(o_name, &o_list[this_o_idx], TRUE, 3);
+
+ sprintf(out_val, "Pick up %s? ", o_name);
+ do_pickup = get_check(out_val);
+ }
+ }
+ else
+ {
+ do_pickup = FALSE;
+ }
+ }
+
+ /* Pick up the item */
+ if (do_pickup)
+ {
+ object_pickup(this_o_idx);
+ }
+}
+
+/* Add a flags group */
+void gain_flag_group(object_type *o_ptr, bool_ silent)
+{
+ int grp = 0;
+ int tries = 1000;
+
+ while (tries--)
+ {
+ grp = rand_int(MAX_FLAG_GROUP);
+
+ /* If we already got this group continue */
+ if (o_ptr->pval3 & BIT(grp)) continue;
+
+ /* Not enough points ? */
+ if (flags_groups[grp].price > o_ptr->pval2) continue;
+
+ /* Ok, enough points and not already got it */
+ break;
+ }
+
+ /* Ack, nothing found */
+ if (tries <= 1) return;
+
+ o_ptr->pval2 -= flags_groups[grp].price;
+ o_ptr->pval3 |= BIT(grp);
+
+ if (!silent)
+ {
+ char o_name[80];
+
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("%s gains access to the %s realm.", o_name, flags_groups[grp].name);
+ }
+}
+
+u32b get_flag(object_type *o_ptr, int grp, int k)
+{
+ u32b f = 0, flag_set = 0;
+ int tries = 1000;
+ u32b f1, f2, f3, f4, f5, esp, flag_test;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* get the corresponding flag set of the group */
+ switch (k)
+ {
+ case 0:
+ flag_set = flags_groups[grp].flags1;
+ flag_test = f1;
+ break;
+ case 1:
+ flag_set = flags_groups[grp].flags2;
+ flag_test = f2;
+ break;
+ case 2:
+ flag_set = flags_groups[grp].flags3;
+ flag_test = f3;
+ break;
+ case 3:
+ flag_set = flags_groups[grp].flags4;
+ flag_test = f4;
+ break;
+ case 4:
+ flag_set = flags_groups[grp].esp;
+ flag_test = esp;
+ break;
+ default:
+ flag_set = flags_groups[grp].flags1;
+ flag_test = f1;
+ break;
+ }
+
+ /* If no flags, no need to look */
+ if (!count_bits(flag_set)) return 0;
+
+ while (tries--)
+ {
+ /* get a random flag */
+ f = BIT(rand_int(32));
+
+ /* is it part of the group */
+ if (!(f & flag_set)) continue;
+
+ /* Already got it */
+ if (f & flag_test) continue;
+
+ /* Ok one */
+ break;
+ }
+
+ if (tries <= 1) return (0);
+ else return (f);
+}
+
+/* Add a flags from a flag group */
+void gain_flag_group_flag(object_type *o_ptr, bool_ silent)
+{
+ int grp = 0, k = 0;
+ u32b f = 0;
+ int tries = 20000;
+
+ if (!count_bits(o_ptr->pval3)) return;
+
+ while (tries--)
+ {
+ /* Get a flag set */
+ k = rand_int(5);
+
+ /* get a flag group */
+ grp = rand_int(MAX_FLAG_GROUP);
+
+ if (!(BIT(grp) & o_ptr->pval3)) continue;
+
+ /* Return a flag from the group/set */
+ f = get_flag(o_ptr, grp, k);
+
+ if (!f) continue;
+
+ break;
+ }
+
+ if (tries <= 1) return;
+
+ switch (k)
+ {
+ case 0:
+ o_ptr->art_flags1 |= f;
+ break;
+ case 1:
+ o_ptr->art_flags2 |= f;
+ break;
+ case 2:
+ o_ptr->art_flags3 |= f;
+ break;
+ case 3:
+ o_ptr->art_flags4 |= f;
+ break;
+ case 4:
+ o_ptr->art_esp |= f;
+ break;
+ }
+
+ if (!silent)
+ {
+ char o_name[80];
+
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("%s gains a new power from the %s realm.", o_name, flags_groups[grp].name);
+ }
+}
+
+/*
+ * When an object gain a level, he can gain some attributes
+ */
+void object_gain_level(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* First it can gain some tohit and todam */
+ if ((o_ptr->tval == TV_AXE) || (o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM) ||
+ (o_ptr->tval == TV_HAFTED) || (o_ptr->tval == TV_MSTAFF))
+ {
+ int k = rand_int(100);
+
+ /* gain +2,+1 */
+ if (k < 33)
+ {
+ o_ptr->to_h += randint(2);
+ o_ptr->to_d += 1;
+ }
+ /* +1 and 1 point */
+ else if (k < 66)
+ {
+ o_ptr->to_h += 1;
+ o_ptr->pval2++;
+
+ if (magik(NEW_GROUP_CHANCE)) gain_flag_group(o_ptr, FALSE);
+ }
+ else
+ {
+ if (!o_ptr->pval3) gain_flag_group(o_ptr, FALSE);
+
+ gain_flag_group_flag(o_ptr, FALSE);
+
+ if (!o_ptr->pval) o_ptr->pval = 1;
+ else
+ {
+ while (magik(20 - (o_ptr->pval * 2))) o_ptr->pval++;
+
+ if (o_ptr->pval > 5) o_ptr->pval = 5;
+ }
+ }
+ }
+}
+
+
+/*
+ * Item sets fcts
+ */
+bool_ wield_set(s16b a_idx, s16b set_idx, bool_ silent)
+{
+ set_type *s_ptr = &set_info[set_idx];
+ int i;
+
+ if ( -1 == a_info[a_idx].set) return (FALSE);
+ for (i = 0; i < s_ptr->num; i++)
+ if (a_idx == s_ptr->arts[i].a_idx) break;
+ if (!s_ptr->arts[i].present)
+ {
+ s_ptr->num_use++;
+ s_ptr->arts[i].present = TRUE;
+ if (s_ptr->num_use > s_ptr->num) msg_print("ERROR!! s_ptr->num_use > s_ptr->use");
+ else if ((s_ptr->num_use == s_ptr->num) && (!silent)) cmsg_format(TERM_GREEN, "%s item set completed.", s_ptr->name + set_name);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+bool_ takeoff_set(s16b a_idx, s16b set_idx)
+{
+ set_type *s_ptr = &set_info[set_idx];
+ int i;
+
+ if ( -1 == a_info[a_idx].set) return (FALSE);
+ for (i = 0; i < s_ptr->num; i++)
+ if (a_idx == s_ptr->arts[i].a_idx) break;
+
+ if (s_ptr->arts[i].present)
+ {
+ s_ptr->arts[i].present = FALSE;
+ s_ptr->num_use--;
+
+ if (s_ptr->num_use == 255) msg_print("ERROR!! s_ptr->num_use < 0");
+ if (s_ptr->num_use == s_ptr->num - 1) cmsg_format(TERM_GREEN, "%s item set not complete anymore.", s_ptr->name + set_name);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+bool_ apply_set(s16b a_idx, s16b set_idx)
+{
+ set_type *s_ptr = &set_info[set_idx];
+ int i, j;
+
+ if ( -1 == a_info[a_idx].set) return (FALSE);
+ for (i = 0; i < s_ptr->num; i++)
+ if (a_idx == s_ptr->arts[i].a_idx) break;
+ if (s_ptr->arts[i].present)
+ {
+ for (j = 0; j < s_ptr->num_use; j++)
+ {
+ apply_flags(s_ptr->arts[i].flags1[j],
+ s_ptr->arts[i].flags2[j],
+ s_ptr->arts[i].flags3[j],
+ s_ptr->arts[i].flags4[j],
+ s_ptr->arts[i].flags5[j],
+ s_ptr->arts[i].esp[j],
+ s_ptr->arts[i].pval[j],
+ 0, 0, 0, 0);
+ }
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+bool_ apply_flags_set(s16b a_idx, s16b set_idx,
+ u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ set_type *s_ptr = &set_info[set_idx];
+ int i, j;
+
+ if ( -1 == a_info[a_idx].set) return (FALSE);
+
+ for (i = 0; i < s_ptr->num; i++)
+ {
+ if (a_idx == s_ptr->arts[i].a_idx) break;
+ }
+
+ if (s_ptr->arts[i].present)
+ {
+ for (j = 0; j < s_ptr->num_use; j++)
+ {
+ (*f1) |= s_ptr->arts[i].flags1[j];
+ (*f2) |= s_ptr->arts[i].flags2[j];
+ (*f3) |= s_ptr->arts[i].flags3[j];
+ (*f4) |= s_ptr->arts[i].flags4[j];
+ (*f5) |= s_ptr->arts[i].flags5[j];
+ (*esp) |= s_ptr->arts[i].esp[j];
+ }
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+
+
+
+
diff --git a/src/object2.c b/src/object2.c
new file mode 100644
index 00000000..98afb815
--- /dev/null
+++ b/src/object2.c
@@ -0,0 +1,6617 @@
+/* File: object2.c */
+
+/* Purpose: Object code, part 2 */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Calculate the player's total inventory weight.
+ */
+s32b calc_total_weight(void)
+{
+ int i;
+ s32b total;
+ for (i = total = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (o_ptr->k_idx) total += o_ptr->weight * o_ptr->number;
+ }
+ return total;
+}
+
+/*
+ * Excise a dungeon object from any stacks
+ */
+void excise_object_idx(int o_idx)
+{
+ object_type *j_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ s16b prev_o_idx = 0;
+
+
+ /* Object */
+ j_ptr = &o_list[o_idx];
+
+ /* Monster */
+ if (j_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Monster */
+ m_ptr = &m_list[j_ptr->held_m_idx];
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Done */
+ if (this_o_idx == o_idx)
+ {
+ /* No previous */
+ if (prev_o_idx == 0)
+ {
+ /* Remove from list */
+ m_ptr->hold_o_idx = next_o_idx;
+ }
+
+ /* Real previous */
+ else
+ {
+ object_type *k_ptr;
+
+ /* Previous object */
+ k_ptr = &o_list[prev_o_idx];
+
+ /* Remove from list */
+ k_ptr->next_o_idx = next_o_idx;
+ }
+
+ /* Forget next pointer */
+ o_ptr->next_o_idx = 0;
+
+ /* Done */
+ break;
+ }
+
+ /* Save prev_o_idx */
+ prev_o_idx = this_o_idx;
+ }
+ }
+
+ /* Dungeon */
+ else
+ {
+ cave_type *c_ptr;
+
+ int y = j_ptr->iy;
+ int x = j_ptr->ix;
+
+ /* Grid */
+ c_ptr = &cave[y][x];
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Done */
+ if (this_o_idx == o_idx)
+ {
+ /* No previous */
+ if (prev_o_idx == 0)
+ {
+ /* Remove from list */
+ c_ptr->o_idx = next_o_idx;
+ }
+
+ /* Real previous */
+ else
+ {
+ object_type *k_ptr;
+
+ /* Previous object */
+ k_ptr = &o_list[prev_o_idx];
+
+ /* Remove from list */
+ k_ptr->next_o_idx = next_o_idx;
+ }
+
+ /* Forget next pointer */
+ o_ptr->next_o_idx = 0;
+
+ /* Done */
+ break;
+ }
+
+ /* Save prev_o_idx */
+ prev_o_idx = this_o_idx;
+ }
+ }
+}
+
+
+/*
+ * Delete a dungeon object
+ *
+ * Handle "stacks" of objects correctly.
+ */
+void delete_object_idx(int o_idx)
+{
+ object_type *j_ptr;
+
+ /* Excise */
+ excise_object_idx(o_idx);
+
+ /* Object */
+ j_ptr = &o_list[o_idx];
+
+ /* Dungeon floor */
+ if (!(j_ptr->held_m_idx))
+ {
+ int y, x;
+
+ /* Location */
+ y = j_ptr->iy;
+ x = j_ptr->ix;
+
+ /* Visual update */
+ lite_spot(y, x);
+ }
+
+ /* Wipe the object */
+ object_wipe(j_ptr);
+
+ /* Count objects */
+ o_cnt--;
+}
+
+
+/*
+ * Deletes all objects at given location
+ */
+void delete_object(int y, int x)
+{
+ cave_type *c_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Refuse "illegal" locations */
+ if (!in_bounds(y, x)) return;
+
+
+ /* Grid */
+ c_ptr = &cave[y][x];
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Wipe the object */
+ object_wipe(o_ptr);
+
+ /* Count objects */
+ o_cnt--;
+ }
+
+ /* Objects are gone */
+ c_ptr->o_idx = 0;
+
+ /* Visual update */
+ lite_spot(y, x);
+}
+
+
+/*
+ * Move an object from index i1 to index i2 in the object list
+ */
+static void compact_objects_aux(int i1, int i2)
+{
+ int i;
+
+ cave_type *c_ptr;
+
+ object_type *o_ptr;
+
+
+ /* Do nothing */
+ if (i1 == i2) return;
+
+
+ /* Repair objects */
+ for (i = 1; i < o_max; i++)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[i];
+
+ /* Skip "dead" objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Repair "next" pointers */
+ if (o_ptr->next_o_idx == i1)
+ {
+ /* Repair */
+ o_ptr->next_o_idx = i2;
+ }
+ }
+
+
+ /* Acquire object */
+ o_ptr = &o_list[i1];
+
+
+ /* Monster */
+ if (o_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Acquire monster */
+ m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Repair monster */
+ if (m_ptr->hold_o_idx == i1)
+ {
+ /* Repair */
+ m_ptr->hold_o_idx = i2;
+ }
+ }
+
+ /* Dungeon */
+ else
+ {
+ int y, x;
+
+ /* Acquire location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+
+ /* Acquire grid */
+ c_ptr = &cave[y][x];
+
+ /* Repair grid */
+ if (c_ptr->o_idx == i1)
+ {
+ /* Repair */
+ c_ptr->o_idx = i2;
+ }
+ }
+
+
+ /* Structure copy */
+ o_list[i2] = o_list[i1];
+
+ /* Wipe the hole */
+ object_wipe(o_ptr);
+}
+
+
+/*
+ * Compact and Reorder the object list
+ *
+ * This function can be very dangerous, use with caution!
+ *
+ * When actually "compacting" objects, we base the saving throw on a
+ * combination of object level, distance from player, and current
+ * "desperation".
+ *
+ * After "compacting" (if needed), we "reorder" the objects into a more
+ * compact order, and we reset the allocation info, and the "live" array.
+ */
+void compact_objects(int size)
+{
+ int i, y, x, num;
+
+ int cur_lev, cur_dis, chance;
+
+ /* Compact */
+ if (size)
+ {
+ /* Message */
+ msg_print("Compacting objects...");
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+
+
+ /* Compact at least 'size' objects */
+ for (num = 0, cur_lev = 1; num < size; cur_lev++)
+ {
+ /* Get closer each iteration (start at distance 12). Around level 100 distance-protect nothing. */
+ cur_dis = 12 * (101 - cur_lev) / 100;
+
+ /* Examine the objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* High level objects are "immune" as long as we're not desperate enough */
+ if (k_ptr->level > cur_lev) continue;
+
+ /* Monster owned objects */
+ if (o_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Acquire monster */
+ m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Monsters start with protecting objects well */
+ chance = 100;
+
+ /* Get the location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+ }
+ /* Dungeon floor objects */
+ else
+ {
+ /* Floor objects start with lower protection */
+ chance = 90;
+
+ /* Get the location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+ }
+
+ /* Near enough objects are "immune", even if low level */
+ /* (like, importantly, food rations after hitting a trap of drop items) */
+ if ((cur_dis > 0) && (distance(p_ptr->py, p_ptr->px, y, x) < cur_dis)) continue;
+
+ /* object protection goes down as we get vicious */
+ /* around level 200 only artifacts have protection */
+ chance = chance - cur_lev / 2;
+
+ /* Artifacts */
+ if ( artifact_p(o_ptr) || o_ptr->art_name )
+ {
+ /* Artifacts are "immune if the level is lower */
+ /* than 300 + artifact level */
+ if ( cur_lev < 300 + k_ptr->level )
+ continue;
+
+ /* That's 400 + level for fixed artifacts */
+ if ( (k_ptr->flags3 & TR3_NORM_ART) && cur_lev < 400 + k_ptr->level )
+ continue;
+
+ /* Never protect if level is high enough; so we don't wipe a better artifact */
+ chance = -1;
+
+ /* rewind the level so we never wipe many */
+ /* artifacts of same level if one will do!!! */
+ cur_lev--;
+ }
+
+ /* Maybe some code to spare the God relic here. But I'd rather raise its level to 150 */
+
+ /* Apply the saving throw */
+ if (rand_int(100) < chance) continue;
+
+ /* Delete the object */
+ delete_object_idx(i);
+
+ /* Count it */
+ num++;
+ }
+ }
+
+
+ /* Excise dead objects (backwards!) */
+ for (i = o_max - 1; i >= 1; i--)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip real objects */
+ if (o_ptr->k_idx) continue;
+
+ /* Move last object into open hole */
+ compact_objects_aux(o_max - 1, i);
+
+ /* Compress "o_max" */
+ o_max--;
+ }
+}
+
+
+
+
+/*
+ * Delete all the items when player leaves the level
+ *
+ * Note -- we do NOT visually reflect these (irrelevant) changes
+ *
+ * Hack -- we clear the "c_ptr->o_idx" field for every grid,
+ * and the "m_ptr->next_o_idx" field for every monster, since
+ * we know we are clearing every object. Technically, we only
+ * clear those fields for grids/monsters containing objects,
+ * and we clear it once for every such object.
+ */
+void wipe_o_list(void)
+{
+ int i;
+
+ /* Delete the existing objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Mega-Hack -- preserve artifacts */
+ if (!character_dungeon || p_ptr->preserve)
+ {
+ /* Hack -- Preserve unknown artifacts */
+ if (artifact_p(o_ptr) && !object_known_p(o_ptr))
+ {
+ /* Mega-Hack -- Preserve the artifact */
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = FALSE;
+ }
+ else if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[o_ptr->k_idx].artifact = FALSE;
+ }
+ else
+ {
+ a_info[o_ptr->name1].cur_num = 0;
+ }
+ }
+ }
+
+ /* Monster */
+ if (o_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Monster */
+ m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Hack -- see above */
+ m_ptr->hold_o_idx = 0;
+ }
+
+ /* Dungeon */
+ else
+ {
+ cave_type *c_ptr;
+
+ /* Access location */
+ int y = o_ptr->iy;
+ int x = o_ptr->ix;
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+ /* Hack -- see above */
+ c_ptr->o_idx = 0;
+ }
+
+ /* Wipe the object */
+ o_ptr = WIPE(o_ptr, object_type);
+ }
+
+ /* Reset "o_max" */
+ o_max = 1;
+
+ /* Reset "o_cnt" */
+ o_cnt = 0;
+}
+
+
+/*
+ * Acquires and returns the index of a "free" object.
+ *
+ * This routine should almost never fail, but in case it does,
+ * we must be sure to handle "failure" of this routine.
+ */
+s16b o_pop(void)
+{
+ int i;
+
+
+ /* Initial allocation */
+ if (o_max < max_o_idx)
+ {
+ /* Get next space */
+ i = o_max;
+
+ /* Expand object array */
+ o_max++;
+
+ /* Count objects */
+ o_cnt++;
+
+ /* Use this object */
+ return (i);
+ }
+
+
+ /* Recycle dead objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[i];
+
+ /* Skip live objects */
+ if (o_ptr->k_idx) continue;
+
+ /* Count objects */
+ o_cnt++;
+
+ /* Use this object */
+ return (i);
+ }
+
+
+ /* Warn the player (except during dungeon creation) */
+ if (character_dungeon) msg_print("Too many objects!");
+
+ /* Oops */
+ return (0);
+}
+
+
+
+/*
+ * Apply a "object restriction function" to the "object allocation table"
+ */
+errr get_obj_num_prep(void)
+{
+ int i;
+
+ /* Get the entry */
+ alloc_entry *table = alloc_kind_table;
+
+ /* Scan the allocation table */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Accept objects which pass the restriction, if any */
+ if (!get_obj_num_hook || (*get_obj_num_hook)(table[i].index))
+ {
+ /* Accept this object */
+ table[i].prob2 = table[i].prob1;
+ }
+
+ /* Do not use this object */
+ else
+ {
+ /* Decline this object */
+ table[i].prob2 = 0;
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Choose an object kind that seems "appropriate" to the given level
+ *
+ * This function uses the "prob2" field of the "object allocation table",
+ * and various local information, to calculate the "prob3" field of the
+ * same table, which is then used to choose an "appropriate" object, in
+ * a relatively efficient manner.
+ *
+ * It is (slightly) more likely to acquire an object of the given level
+ * than one of a lower level. This is done by choosing several objects
+ * appropriate to the given level and keeping the "hardest" one.
+ *
+ * Note that if no objects are "appropriate", then this function will
+ * fail, and return zero, but this should *almost* never happen.
+ */
+s16b get_obj_num(int level)
+{
+ int i, j, p;
+ int k_idx;
+ long value, total;
+ object_kind *k_ptr;
+ alloc_entry *table = alloc_kind_table;
+
+
+ /* Boost level */
+ if (level > 0)
+ {
+ /* Occasional "boost" */
+ if (rand_int(GREAT_OBJ) == 0)
+ {
+ /* What a bizarre calculation */
+ level = 1 + (level * MAX_DEPTH / randint(MAX_DEPTH));
+ }
+ }
+
+
+ /* Reset total */
+ total = 0L;
+
+ /* Process probabilities */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Objects are sorted by depth */
+ if (table[i].level > level) break;
+
+ /* Default */
+ table[i].prob3 = 0;
+
+ /* Access the index */
+ k_idx = table[i].index;
+
+ /* Access the actual kind */
+ k_ptr = &k_info[k_idx];
+
+ /* Hack -- prevent embedded chests */
+ if (opening_chest && (k_ptr->tval == TV_CHEST)) continue;
+
+ /* Accept */
+ table[i].prob3 = table[i].prob2;
+
+ /* Total */
+ total += table[i].prob3;
+ }
+
+ /* No legal objects */
+ if (total <= 0) return (0);
+
+
+ /* Pick an object */
+ value = rand_int(total);
+
+ /* Find the object */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+
+ /* Power boost */
+ p = rand_int(100);
+
+ /* Try for a "better" object once (50%) or twice (10%) */
+ if (p < 60)
+ {
+ /* Save old */
+ j = i;
+
+ /* Pick a object */
+ value = rand_int(total);
+
+ /* Find the monster */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+ /* Keep the "best" one */
+ if (table[i].level < table[j].level) i = j;
+ }
+
+ /* Try for a "better" object twice (10%) */
+ if (p < 10)
+ {
+ /* Save old */
+ j = i;
+
+ /* Pick a object */
+ value = rand_int(total);
+
+ /* Find the object */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+ /* Keep the "best" one */
+ if (table[i].level < table[j].level) i = j;
+ }
+
+
+ /* Result */
+ return (table[i].index);
+}
+
+
+
+
+
+
+
+
+/*
+ * Known is true when the "attributes" of an object are "known".
+ * These include tohit, todam, toac, cost, and pval (charges).
+ *
+ * Note that "knowing" an object gives you everything that an "awareness"
+ * gives you, and much more. In fact, the player is always "aware" of any
+ * item of which he has full "knowledge".
+ *
+ * But having full knowledge of, say, one "wand of wonder", does not, by
+ * itself, give you knowledge, or even awareness, of other "wands of wonder".
+ * It happens that most "identify" routines (including "buying from a shop")
+ * will make the player "aware" of the object as well as fully "know" it.
+ *
+ * This routine also removes any inscriptions generated by "feelings".
+ */
+void object_known(object_type *o_ptr)
+{
+
+ /* No Sensing */
+ o_ptr->sense = SENSE_NONE;
+
+ /* Clear the "Felt" info */
+ o_ptr->ident &= ~(IDENT_SENSE);
+
+ /* Clear the "Empty" info */
+ o_ptr->ident &= ~(IDENT_EMPTY);
+
+ /* Now we know about the item */
+ o_ptr->ident |= (IDENT_KNOWN);
+}
+
+
+
+
+
+/*
+ * The player is now aware of the effects of the given object.
+ */
+void object_aware(object_type *o_ptr)
+{
+ /* Fully aware of the effects */
+ k_info[o_ptr->k_idx].aware = TRUE;
+}
+
+
+
+/*
+ * Something has been "sampled"
+ */
+void object_tried(object_type *o_ptr)
+{
+ /* Mark it as tried (even if "aware") */
+ k_info[o_ptr->k_idx].tried = TRUE;
+}
+
+
+
+/*
+ * Return the "value" of an "unknown" item
+ * Make a guess at the value of non-aware items
+ */
+static s32b object_value_base(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Aware item -- use template cost */
+ if ((object_aware_p(o_ptr)) && (o_ptr->tval != TV_EGG)) return (k_ptr->cost);
+
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Un-aware Food */
+ case TV_FOOD:
+ return (5L);
+
+ /* Un-aware Potions */
+ case TV_POTION2:
+ return (20L);
+
+ /* Un-aware Potions */
+ case TV_POTION:
+ return (20L);
+
+ /* Un-aware Scrolls */
+ case TV_SCROLL:
+ return (20L);
+
+ /* Un-aware Staffs */
+ case TV_STAFF:
+ return (70L);
+
+ /* Un-aware Wands */
+ case TV_WAND:
+ return (50L);
+
+ /* Un-aware Rods */
+ case TV_ROD:
+ return (90L);
+
+ /* Un-aware Rings */
+ case TV_RING:
+ return (45L);
+
+ /* Un-aware Amulets */
+ case TV_AMULET:
+ return (45L);
+
+ /* Eggs */
+ case TV_EGG:
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ /* Pay the monster level */
+ return (r_ptr->level * 100) + 100;
+
+ /* Done */
+ break;
+ }
+ }
+
+ /* Paranoia -- Oops */
+ return (0L);
+}
+
+/* Return the value of the flags the object has... */
+s32b flag_cost(object_type * o_ptr, int plusses)
+{
+ s32b total = 0;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f5 & TR5_TEMPORARY)
+ {
+ return 0;
+ }
+ if (f4 & TR4_CURSE_NO_DROP)
+ {
+ return 0;
+ }
+ if (f1 & TR1_STR) total += (1000 * plusses);
+ if (f1 & TR1_INT) total += (1000 * plusses);
+ if (f1 & TR1_WIS) total += (1000 * plusses);
+ if (f1 & TR1_DEX) total += (1000 * plusses);
+ if (f1 & TR1_CON) total += (1000 * plusses);
+ if (f1 & TR1_CHR) total += (250 * plusses);
+ if (f1 & TR1_CHAOTIC) total += 10000;
+ if (f1 & TR1_VAMPIRIC) total += 13000;
+ if (f1 & TR1_STEALTH) total += (250 * plusses);
+ if (f1 & TR1_SEARCH) total += (100 * plusses);
+ if (f1 & TR1_INFRA) total += (150 * plusses);
+ if (f1 & TR1_TUNNEL) total += (175 * plusses);
+ if ((f1 & TR1_SPEED) && (plusses > 0))
+ total += (10000 + (2500 * plusses));
+ if ((f1 & TR1_BLOWS) && (plusses > 0))
+ total += (10000 + (2500 * plusses));
+ if (f1 & TR1_MANA) total += (1000 * plusses);
+ if (f1 & TR1_SPELL) total += (2000 * plusses);
+ if (f1 & TR1_SLAY_ANIMAL) total += 3500;
+ if (f1 & TR1_SLAY_EVIL) total += 4500;
+ if (f1 & TR1_SLAY_UNDEAD) total += 3500;
+ if (f1 & TR1_SLAY_DEMON) total += 3500;
+ if (f1 & TR1_SLAY_ORC) total += 3000;
+ if (f1 & TR1_SLAY_TROLL) total += 3500;
+ if (f1 & TR1_SLAY_GIANT) total += 3500;
+ if (f1 & TR1_SLAY_DRAGON) total += 3500;
+ if (f5 & TR5_KILL_DEMON) total += 5500;
+ if (f5 & TR5_KILL_UNDEAD) total += 5500;
+ if (f1 & TR1_KILL_DRAGON) total += 5500;
+ if (f1 & TR1_VORPAL) total += 5000;
+ if (f1 & TR1_IMPACT) total += 5000;
+ if (f1 & TR1_BRAND_POIS) total += 7500;
+ if (f1 & TR1_BRAND_ACID) total += 7500;
+ if (f1 & TR1_BRAND_ELEC) total += 7500;
+ if (f1 & TR1_BRAND_FIRE) total += 5000;
+ if (f1 & TR1_BRAND_COLD) total += 5000;
+ if (f2 & TR2_SUST_STR) total += 850;
+ if (f2 & TR2_SUST_INT) total += 850;
+ if (f2 & TR2_SUST_WIS) total += 850;
+ if (f2 & TR2_SUST_DEX) total += 850;
+ if (f2 & TR2_SUST_CON) total += 850;
+ if (f2 & TR2_SUST_CHR) total += 250;
+ if (f2 & TR2_INVIS) total += 3000;
+ if (f2 & TR2_LIFE) total += (5000 * plusses);
+ if (f2 & TR2_IM_ACID) total += 10000;
+ if (f2 & TR2_IM_ELEC) total += 10000;
+ if (f2 & TR2_IM_FIRE) total += 10000;
+ if (f2 & TR2_IM_COLD) total += 10000;
+ if (f2 & TR2_SENS_FIRE) total -= 100;
+ if (f2 & TR2_REFLECT) total += 10000;
+ if (f2 & TR2_FREE_ACT) total += 4500;
+ if (f2 & TR2_HOLD_LIFE) total += 8500;
+ if (f2 & TR2_RES_ACID) total += 1250;
+ if (f2 & TR2_RES_ELEC) total += 1250;
+ if (f2 & TR2_RES_FIRE) total += 1250;
+ if (f2 & TR2_RES_COLD) total += 1250;
+ if (f2 & TR2_RES_POIS) total += 2500;
+ if (f2 & TR2_RES_FEAR) total += 2500;
+ if (f2 & TR2_RES_LITE) total += 1750;
+ if (f2 & TR2_RES_DARK) total += 1750;
+ if (f2 & TR2_RES_BLIND) total += 2000;
+ if (f2 & TR2_RES_CONF) total += 2000;
+ if (f2 & TR2_RES_SOUND) total += 2000;
+ if (f2 & TR2_RES_SHARDS) total += 2000;
+ if (f2 & TR2_RES_NETHER) total += 2000;
+ if (f2 & TR2_RES_NEXUS) total += 2000;
+ if (f2 & TR2_RES_CHAOS) total += 2000;
+ if (f2 & TR2_RES_DISEN) total += 10000;
+ if (f3 & TR3_SH_FIRE) total += 5000;
+ if (f3 & TR3_SH_ELEC) total += 5000;
+ if (f3 & TR3_DECAY) total += 0;
+ if (f3 & TR3_NO_TELE) total += 2500;
+ if (f3 & TR3_NO_MAGIC) total += 2500;
+ if (f3 & TR3_WRAITH) total += 250000;
+ if (f3 & TR3_TY_CURSE) total -= 15000;
+ if (f3 & TR3_EASY_KNOW) total += 0;
+ if (f3 & TR3_HIDE_TYPE) total += 0;
+ if (f3 & TR3_SHOW_MODS) total += 0;
+ if (f3 & TR3_INSTA_ART) total += 0;
+ if (f3 & TR3_LITE1) total += 750;
+ if (f4 & TR4_LITE2) total += 1250;
+ if (f4 & TR4_LITE3) total += 2750;
+ if (f3 & TR3_SEE_INVIS) total += 2000;
+ if (esp) total += (12500 * count_bits(esp));
+ if (f3 & TR3_SLOW_DIGEST) total += 750;
+ if (f3 & TR3_REGEN) total += 2500;
+ if (f3 & TR3_XTRA_MIGHT) total += 2250;
+ if (f3 & TR3_XTRA_SHOTS) total += 10000;
+ if (f3 & TR3_IGNORE_ACID) total += 100;
+ if (f3 & TR3_IGNORE_ELEC) total += 100;
+ if (f3 & TR3_IGNORE_FIRE) total += 100;
+ if (f3 & TR3_IGNORE_COLD) total += 100;
+ if (f3 & TR3_ACTIVATE) total += 100;
+ if (f3 & TR3_DRAIN_EXP) total -= 12500;
+ if (f3 & TR3_TELEPORT)
+ {
+ if (o_ptr->ident & IDENT_CURSED)
+ total -= 7500;
+ else
+ total += 250;
+ }
+ if (f3 & TR3_AGGRAVATE) total -= 10000;
+ if (f3 & TR3_BLESSED) total += 750;
+ if ((f3 & TR3_CURSED) && (o_ptr->ident & IDENT_CURSED)) total -= 5000;
+ if ((f3 & TR3_HEAVY_CURSE) && (o_ptr->ident & IDENT_CURSED)) total -= 12500;
+ if (f3 & TR3_PERMA_CURSE) total -= 15000;
+ if (f3 & TR3_FEATHER) total += 1250;
+ if (f4 & TR4_FLY) total += 10000;
+ if (f4 & TR4_NEVER_BLOW) total -= 15000;
+ if (f4 & TR4_PRECOGNITION) total += 250000;
+ if (f4 & TR4_BLACK_BREATH) total -= 12500;
+ if (f4 & TR4_DG_CURSE) total -= 25000;
+ if (f4 & TR4_CLONE) total -= 10000;
+ if (f4 & TR4_LEVELS) total += o_ptr->elevel * 2000;
+
+ /* Also, give some extra for activatable powers... */
+
+ if ((o_ptr->art_name) && (o_ptr->art_flags3 & (TR3_ACTIVATE)))
+ {
+ int type = o_ptr->xtra2;
+
+ if (type == ACT_SUNLIGHT) total += 250;
+ else if (type == ACT_BO_MISS_1) total += 250;
+ else if (type == ACT_BA_POIS_1) total += 300;
+ else if (type == ACT_BO_ELEC_1) total += 250;
+ else if (type == ACT_BO_ACID_1) total += 250;
+ else if (type == ACT_BO_COLD_1) total += 250;
+ else if (type == ACT_BO_FIRE_1) total += 250;
+ else if (type == ACT_BA_COLD_1) total += 750;
+ else if (type == ACT_BA_FIRE_1) total += 1000;
+ else if (type == ACT_DRAIN_1) total += 500;
+ else if (type == ACT_BA_COLD_2) total += 1250;
+ else if (type == ACT_BA_ELEC_2) total += 1500;
+ else if (type == ACT_DRAIN_2) total += 750;
+ else if (type == ACT_VAMPIRE_1) total += 1000;
+ else if (type == ACT_BO_MISS_2) total += 1000;
+ else if (type == ACT_BA_FIRE_2) total += 1750;
+ else if (type == ACT_BA_COLD_3) total += 2500;
+ else if (type == ACT_BA_ELEC_3) total += 2500;
+ else if (type == ACT_WHIRLWIND) total += 7500;
+ else if (type == ACT_VAMPIRE_2) total += 2500;
+ else if (type == ACT_CALL_CHAOS) total += 5000;
+ else if (type == ACT_ROCKET) total += 5000;
+ else if (type == ACT_DISP_EVIL) total += 4000;
+ else if (type == ACT_DISP_GOOD) total += 3500;
+ else if (type == ACT_BA_MISS_3) total += 5000;
+ else if (type == ACT_CONFUSE) total += 500;
+ else if (type == ACT_SLEEP) total += 750;
+ else if (type == ACT_QUAKE) total += 600;
+ else if (type == ACT_TERROR) total += 2500;
+ else if (type == ACT_TELE_AWAY) total += 2000;
+ else if (type == ACT_GENOCIDE) total += 10000;
+ else if (type == ACT_MASS_GENO) total += 10000;
+ else if (type == ACT_CHARM_ANIMAL) total += 7500;
+ else if (type == ACT_CHARM_UNDEAD) total += 10000;
+ else if (type == ACT_CHARM_OTHER) total += 10000;
+ else if (type == ACT_CHARM_ANIMALS) total += 12500;
+ else if (type == ACT_CHARM_OTHERS) total += 17500;
+ else if (type == ACT_SUMMON_ANIMAL) total += 10000;
+ else if (type == ACT_SUMMON_PHANTOM) total += 12000;
+ else if (type == ACT_SUMMON_ELEMENTAL) total += 15000;
+ else if (type == ACT_SUMMON_DEMON) total += 20000;
+ else if (type == ACT_SUMMON_UNDEAD) total += 20000;
+ else if (type == ACT_CURE_LW) total += 500;
+ else if (type == ACT_CURE_MW) total += 750;
+ else if (type == ACT_REST_LIFE) total += 7500;
+ else if (type == ACT_REST_ALL) total += 15000;
+ else if (type == ACT_CURE_700) total += 10000;
+ else if (type == ACT_CURE_1000) total += 15000;
+ else if (type == ACT_ESP) total += 1500;
+ else if (type == ACT_BERSERK) total += 800;
+ else if (type == ACT_PROT_EVIL) total += 5000;
+ else if (type == ACT_RESIST_ALL) total += 5000;
+ else if (type == ACT_SPEED) total += 15000;
+ else if (type == ACT_XTRA_SPEED) total += 25000;
+ else if (type == ACT_WRAITH) total += 25000;
+ else if (type == ACT_INVULN) total += 25000;
+ else if (type == ACT_LIGHT) total += 150;
+ else if (type == ACT_MAP_LIGHT) total += 500;
+ else if (type == ACT_DETECT_ALL) total += 1000;
+ else if (type == ACT_DETECT_XTRA) total += 12500;
+ else if (type == ACT_ID_FULL) total += 10000;
+ else if (type == ACT_ID_PLAIN) total += 1250;
+ else if (type == ACT_RUNE_EXPLO) total += 4000;
+ else if (type == ACT_RUNE_PROT) total += 10000;
+ else if (type == ACT_SATIATE) total += 2000;
+ else if (type == ACT_DEST_DOOR) total += 100;
+ else if (type == ACT_STONE_MUD) total += 1000;
+ else if (type == ACT_RECHARGE) total += 1000;
+ else if (type == ACT_ALCHEMY) total += 10000;
+ else if (type == ACT_DIM_DOOR) total += 10000;
+ else if (type == ACT_TELEPORT) total += 2000;
+ else if (type == ACT_RECALL) total += 7500;
+ }
+
+ return total;
+}
+
+
+
+/*
+ * Return the "real" price of a "known" item, not including discounts
+ *
+ * Wand and staffs get cost for each charge
+ *
+ * Armor is worth an extra 100 gold per bonus point to armor class.
+ *
+ * Weapons are worth an extra 100 gold per bonus point (AC,TH,TD).
+ *
+ * Missiles are only worth 5 gold per bonus point, since they
+ * usually appear in groups of 20, and we want the player to get
+ * the same amount of cash for any "equivalent" item. Note that
+ * missiles never have any of the "pval" flags, and in fact, they
+ * only have a few of the available flags, primarily of the "slay"
+ * and "brand" and "ignore" variety.
+ *
+ * Armor with a negative armor bonus is worthless.
+ * Weapons with negative hit+damage bonuses are worthless.
+ *
+ * Every wearable item with a "pval" bonus is worth extra (see below).
+ */
+s32b object_value_real(object_type *o_ptr)
+{
+ s32b value;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ if (o_ptr->tval == TV_RANDART)
+ {
+ return random_artifacts[o_ptr->sval].cost;
+ }
+
+ /* Hack -- "worthless" items */
+ if (!k_ptr->cost) return (0L);
+
+ /* Base cost */
+ value = k_ptr->cost;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f5 & TR5_TEMPORARY) return (0L);
+
+ if (o_ptr->art_flags1 || o_ptr->art_flags2 || o_ptr->art_flags3)
+ {
+ value += flag_cost (o_ptr, o_ptr->pval);
+ }
+ /* Artifact */
+ else if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Hack -- "worthless" artifacts */
+ if (!a_ptr->cost) return (0L);
+
+ /* Hack -- Use the artifact cost instead */
+ value = a_ptr->cost;
+ }
+
+ /* Ego-Item */
+ else if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+
+ /* Hack -- "worthless" ego-items */
+ if (!e_ptr->cost) return (0L);
+
+ /* Hack -- Reward the ego-item with a bonus */
+ value += e_ptr->cost;
+
+ if (o_ptr->name2b)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2b];
+
+ /* Hack -- "worthless" ego-items */
+ if (!e_ptr->cost) return (0L);
+
+ /* Hack -- Reward the ego-item with a bonus */
+ value += e_ptr->cost;
+ }
+ }
+
+ /* Pay the spell */
+ if (f5 & TR5_SPELL_CONTAIN)
+ {
+ if (o_ptr->pval2 != -1)
+ value += 5000 + 500 * school_spells[o_ptr->pval2].skill_level;
+ else
+ value += 5000;
+ }
+
+ /* Analyze pval bonus */
+ switch (o_ptr->tval)
+ {
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_LITE:
+ case TV_AMULET:
+ case TV_RING:
+ case TV_MSTAFF:
+ case TV_TRAPKIT:
+ case TV_INSTRUMENT:
+ {
+ /* No pval */
+ if (!o_ptr->pval) break;
+
+ /* Give credit for stat bonuses */
+ if (f1 & (TR1_STR)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_INT)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_WIS)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_DEX)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_CON)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_CHR)) value += (o_ptr->pval * 200L);
+
+ if (f5 & (TR5_CRIT)) value += (o_ptr->pval * 500L);
+
+ /* Give credit for stealth and searching */
+ if (f1 & (TR1_STEALTH)) value += (o_ptr->pval * 100L);
+ if (f1 & (TR1_SEARCH)) value += (o_ptr->pval * 100L);
+
+ /* Give credit for infra-vision and tunneling */
+ if (f1 & (TR1_INFRA)) value += (o_ptr->pval * 50L);
+ if (f1 & (TR1_TUNNEL)) value += (o_ptr->pval * 50L);
+
+ /* Give credit for extra attacks */
+ if (f1 & (TR1_BLOWS)) value += (o_ptr->pval * 2000L);
+
+ /* Give credit for speed bonus */
+ if (f1 & (TR1_SPEED)) value += (o_ptr->pval * 30000L);
+
+ break;
+ }
+ }
+
+
+ /* Analyze the item */
+ switch (o_ptr->tval)
+ {
+ /* Eggs */
+ case TV_EGG:
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ /* Pay the monster level */
+ value += r_ptr->level * 100;
+
+ /* Done */
+ break;
+ }
+
+ /* Wands/Staffs */
+ case TV_WAND:
+ {
+ /* Par for the spell */
+ value *= school_spells[o_ptr->pval2].skill_level;
+ /* Take the average of the base and max spell levels */
+ value *= (((o_ptr->pval3 >> 16) & 0xFFFF) + (o_ptr->pval3 & 0xFFFF)) / 2;
+ /* Hack */
+ value /= 6;
+
+ /* Pay extra for charges */
+ value += ((value / 20) * o_ptr->pval) / o_ptr->number;
+
+ /* Done */
+ break;
+ }
+ case TV_STAFF:
+ {
+ /* Par for the spell */
+ value *= school_spells[o_ptr->pval2].skill_level;
+ /* Take the average of the base and max spell levels */
+ value *= (((o_ptr->pval3 >> 16) & 0xFFFF) + (o_ptr->pval3 & 0xFFFF)) / 2;
+ /* Hack */
+ value /= 6;
+
+ /* Pay extra for charges */
+ value += ((value / 20) * o_ptr->pval);
+
+ /* Done */
+ break;
+ }
+ case TV_BOOK:
+ {
+ if (o_ptr->sval == 255)
+ {
+ /* Pay extra for the spell */
+ value = value * school_spells[o_ptr->pval].skill_level;
+ }
+ /* Done */
+ break;
+ }
+
+ /* Rods */
+ case TV_ROD_MAIN:
+ {
+ s16b tip_idx;
+
+ /* It's not combined */
+ if (o_ptr->pval == 0) break;
+
+ /* Look up the tip attached */
+ tip_idx = lookup_kind(TV_ROD, o_ptr->pval);
+
+ /* Paranoia */
+ if (tip_idx > 0)
+ {
+ /* Add its cost */
+ value += k_info[tip_idx].cost;
+ }
+
+ /* Done */
+ break;
+ }
+
+ /* Rings/Amulets */
+ case TV_RING:
+ case TV_AMULET:
+ {
+ /* Hack -- negative bonuses are bad */
+ if (o_ptr->to_a < 0 && !value) return (0L);
+ if (o_ptr->to_h < 0 && !value) return (0L);
+ if (o_ptr->to_d < 0 && !value) return (0L);
+
+ /* Give credit for bonuses */
+ value += ((o_ptr->to_h + o_ptr->to_d + o_ptr->to_a) * 100L);
+
+ /* Done */
+ break;
+ }
+
+ /* Armor */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_SHIELD:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ /* Hack -- negative armor bonus */
+ if (o_ptr->to_a < 0 && !value) return (0L);
+
+ /* Give credit for bonuses */
+ value += ((o_ptr->to_h + o_ptr->to_d + o_ptr->to_a) * 100L);
+
+ /* Done */
+ break;
+ }
+
+ /* Bows/Weapons */
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_SWORD:
+ case TV_DAEMON_BOOK:
+ case TV_AXE:
+ case TV_POLEARM:
+ case TV_TRAPKIT:
+ {
+ /* Hack -- negative hit/damage bonuses */
+ if (o_ptr->to_h + o_ptr->to_d < 0 && !value) return (0L);
+
+ /* Factor in the bonuses */
+ value += ((o_ptr->to_h + o_ptr->to_d + o_ptr->to_a) * 100L);
+
+ /* Hack -- Factor in extra damage dice */
+ if ((o_ptr->dd > k_ptr->dd) && (o_ptr->ds == k_ptr->ds))
+ {
+ value += (o_ptr->dd - k_ptr->dd) * o_ptr->ds * 100L;
+ }
+
+ /* Done */
+ break;
+ }
+
+ /* Ammo */
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ {
+ /* Hack -- negative hit/damage bonuses */
+ if (o_ptr->to_h + o_ptr->to_d < 0 && !value) return (0L);
+
+ /* Factor in the bonuses */
+ value += ((o_ptr->to_h + o_ptr->to_d) * 5L);
+
+ /* Hack -- Factor in extra damage dice */
+ if ((o_ptr->dd > k_ptr->dd) && (o_ptr->ds == k_ptr->ds))
+ {
+ value += (o_ptr->dd - k_ptr->dd) * o_ptr->ds * 5L;
+ }
+
+ /* Special attack (exploding arrow) */
+ if (o_ptr->pval2 != 0) value *= 14;
+
+ /* Done */
+ break;
+ }
+ }
+
+ /* Return the value */
+ return (value);
+}
+
+
+/*
+ * Return the price of an item including plusses (and charges)
+ *
+ * This function returns the "value" of the given item (qty one)
+ *
+ * Never notice "unknown" bonuses or properties, including "curses",
+ * since that would give the player information he did not have.
+ *
+ * Note that discounted items stay discounted forever, even if
+ * the discount is "forgotten" by the player via memory loss.
+ */
+s32b object_value(object_type *o_ptr)
+{
+ s32b value;
+
+
+ /* Unknown items -- acquire a base value */
+ if (object_known_p(o_ptr))
+ {
+ /* Cursed items -- worthless */
+ if (cursed_p(o_ptr)) return (0L);
+
+ /* Real value (see above) */
+ value = object_value_real(o_ptr);
+ }
+
+ /* Known items -- acquire the actual value */
+ else
+ {
+ /* Hack -- Felt cursed items */
+ if ((o_ptr->ident & (IDENT_SENSE)) && cursed_p(o_ptr)) return (0L);
+
+ /* Base value (see above) */
+ value = object_value_base(o_ptr);
+ }
+
+
+ /* Apply discount (if any) */
+ if (o_ptr->discount) value -= (value * o_ptr->discount / 100L);
+
+
+ /* Return the final value */
+ return (value);
+}
+
+
+
+
+
+/*
+ * Determine if an item can "absorb" a second item
+ *
+ * See "object_absorb()" for the actual "absorption" code.
+ *
+ * If permitted, we allow wands/staffs (if they are known to have equal
+ * charges) and rods (if fully charged) to combine. They will unstack
+ * (if necessary) when they are used.
+ *
+ * If permitted, we allow weapons/armor to stack, if fully "known".
+ *
+ * Missiles will combine if both stacks have the same "known" status.
+ * This is done to make unidentified stacks of missiles useful.
+ *
+ * Food, potions, scrolls, and "easy know" items always stack.
+ *
+ * Chests, and activatable items, never stack (for various reasons).
+ */
+bool_ object_similar(object_type *o_ptr, object_type *j_ptr)
+{
+ int total = o_ptr->number + j_ptr->number;
+ u32b f1, f2, f3, f4, f5, esp, f11, f12, f13, f14, esp1, f15;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ object_flags(j_ptr, &f11, &f12, &f13, &f14, &f15, &esp1);
+
+
+ /* Require identical object types */
+ if (o_ptr->k_idx != j_ptr->k_idx) return (0);
+
+ if ((f5 & TR5_SPELL_CONTAIN) || (f15 & TR5_SPELL_CONTAIN))
+ return FALSE;
+
+ /* Analyze the items */
+ switch (o_ptr->tval)
+ {
+ /* School Book */
+ case TV_BOOK:
+ {
+ if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return FALSE;
+
+ /* Beware artifatcs should not combibne with "lesser" thing */
+ if (artifact_p(o_ptr) != artifact_p(j_ptr)) return (FALSE);
+
+ /* Do not combine different ego or normal ones */
+ if (ego_item_p(o_ptr) != ego_item_p(j_ptr)) return (FALSE);
+
+ /* Random books should stack if they are identical */
+ if ((o_ptr->sval == 255) && (j_ptr->sval == 255))
+ {
+ if (o_ptr->pval != j_ptr->pval)
+ return (FALSE);
+ }
+
+ return (TRUE);
+ }
+
+ /* Chests */
+ case TV_CHEST:
+ {
+ /* Never okay */
+ return (0);
+ }
+
+ case TV_RANDART:
+ {
+ return FALSE;
+ }
+
+ case TV_RUNE1:
+ {
+ return TRUE;
+ }
+
+ case TV_RUNE2:
+ {
+ if ((o_ptr->sval == RUNE_STONE) || (j_ptr->sval == RUNE_STONE)) return FALSE;
+ else return TRUE;
+ }
+
+ case TV_INSTRUMENT:
+ {
+ return FALSE;
+ }
+
+ case TV_HYPNOS:
+ case TV_EGG:
+ {
+ return FALSE;
+ }
+
+ /* Totems */
+ case TV_TOTEM:
+ {
+ if ((o_ptr->pval == j_ptr->pval) && (o_ptr->pval2 == j_ptr->pval2)) return TRUE;
+ return FALSE;
+ }
+
+ /* Corpses*/
+ case TV_CORPSE:
+ {
+ return FALSE;
+ }
+
+ /* Food and Potions and Scrolls */
+ case TV_POTION:
+ case TV_POTION2:
+ {
+ if (o_ptr->pval2 != j_ptr->pval2) return FALSE;
+
+ /* Assume okay */
+ break;
+ }
+
+ case TV_SCROLL:
+ {
+ if (o_ptr->pval != j_ptr->pval) return FALSE;
+ if (o_ptr->pval2 != j_ptr->pval2) return FALSE;
+ break;
+ }
+
+ /* Staffs */
+ case TV_STAFF:
+ {
+ /* Require either knowledge or known empty for both staffs. */
+ if ((!(o_ptr->ident & (IDENT_EMPTY)) &&
+ !object_known_p(o_ptr)) ||
+ (!(j_ptr->ident & (IDENT_EMPTY)) &&
+ !object_known_p(j_ptr))) return (0);
+
+ /* Require identical charges, since staffs are bulky. */
+ if (o_ptr->pval != j_ptr->pval) return (0);
+
+ /* Do not combine recharged ones with non recharged ones. */
+ if ((f4 & TR4_RECHARGED) != (f14 & TR4_RECHARGED)) return (0);
+
+ /* Do not combine different spells */
+ if (o_ptr->pval2 != j_ptr->pval2) return (0);
+
+ /* Do not combine different base levels */
+ if (o_ptr->pval3 != j_ptr->pval3) return (0);
+
+ /* Beware artifatcs should not combibne with "lesser" thing */
+ if (o_ptr->name1 != j_ptr->name1) return (0);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2 != j_ptr->name2) return (0);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2b != j_ptr->name2b) return (0);
+
+ /* Assume okay */
+ break;
+ }
+
+ /* Wands */
+ case TV_WAND:
+ {
+
+ /* Require either knowledge or known empty for both wands. */
+ if ((!(o_ptr->ident & (IDENT_EMPTY)) &&
+ !object_known_p(o_ptr)) ||
+ (!(j_ptr->ident & (IDENT_EMPTY)) &&
+ !object_known_p(j_ptr))) return (0);
+
+ /* Beware artifatcs should not combibne with "lesser" thing */
+ if (o_ptr->name1 != j_ptr->name1) return (0);
+
+ /* Do not combine recharged ones with non recharged ones. */
+ if ((f4 & TR4_RECHARGED) != (f14 & TR4_RECHARGED)) return (0);
+
+ /* Do not combine different spells */
+ if (o_ptr->pval2 != j_ptr->pval2) return (0);
+
+ /* Do not combine different base levels */
+ if (o_ptr->pval3 != j_ptr->pval3) return (0);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2 != j_ptr->name2) return (0);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2b != j_ptr->name2b) return (0);
+
+ /* Assume okay */
+ break;
+ }
+
+ /* Rod Tips */
+ case TV_ROD:
+ {
+ /* Probably okay */
+ break;
+ }
+
+ /* Rods */
+ case TV_ROD_MAIN:
+ {
+ return FALSE;
+ break;
+ }
+
+ /* Weapons and Armor */
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_TRAPKIT:
+ case TV_DAEMON_BOOK:
+ {
+ /* Require permission */
+ if (!stack_allow_items) return (0);
+
+ /* Fall through */
+ }
+
+ /* Rings, Amulets, Lites */
+ case TV_RING:
+ case TV_AMULET:
+ case TV_LITE:
+ {
+ /* Require full knowledge of both items */
+ if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return (0);
+
+ /* Require identical "turns of light" */
+ if (o_ptr->timeout != j_ptr->timeout) return (FALSE);
+
+ /* Fall through */
+ }
+
+ /* Missiles */
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_SHOT:
+ {
+ /* Require identical knowledge of both items */
+ if (object_known_p(o_ptr) != object_known_p(j_ptr)) return (0);
+
+ /* Require identical "bonuses" */
+ if (o_ptr->to_h != j_ptr->to_h) return (FALSE);
+ if (o_ptr->to_d != j_ptr->to_d) return (FALSE);
+ if (o_ptr->to_a != j_ptr->to_a) return (FALSE);
+
+ /* Require identical "pval" code */
+ if (o_ptr->pval != j_ptr->pval) return (FALSE);
+
+ /* Require identical exploding status code */
+ if (o_ptr->pval2 != j_ptr->pval2) return (FALSE);
+
+ /* Require identical "artifact" names */
+ if (o_ptr->name1 != j_ptr->name1) return (FALSE);
+
+ /* Require identical "ego-item" names */
+ if (o_ptr->name2 != j_ptr->name2) return (FALSE);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2b != j_ptr->name2b) return (FALSE);
+
+ /* Hack -- Never stack "powerful" items */
+ /*
+ Why?!
+ -- wilh
+ */
+ /* #if 0 */
+ if (o_ptr->xtra1 || j_ptr->xtra1) return (FALSE);
+ /* #endif */
+
+ /* Hack -- Never stack recharging items */
+ if ((o_ptr->timeout || j_ptr->timeout) &&
+ (o_ptr->tval != TV_LITE)) return (FALSE);
+
+ /* Require identical "values" */
+ if (o_ptr->ac != j_ptr->ac) return (FALSE);
+ if (o_ptr->dd != j_ptr->dd) return (FALSE);
+ if (o_ptr->ds != j_ptr->ds) return (FALSE);
+
+ /* Probably okay */
+ break;
+ }
+
+ /* UHH ugly hack for the mushroom quest, sorry */
+ case TV_FOOD:
+ {
+ if (o_ptr->pval2 != j_ptr->pval2) return (FALSE);
+ break;
+ }
+
+ /* UHH ugly hack for the fireproof quest, sorry */
+ case TV_BATERIE:
+ {
+ if (o_ptr->pval2 != j_ptr->pval2) return (FALSE);
+ break;
+ }
+
+ /* Various */
+ default:
+ {
+ /* Require knowledge */
+ if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return (0);
+
+ /* Probably okay */
+ break;
+ }
+ }
+
+
+ /* Hack -- Identical art_flags! */
+ if ((o_ptr->art_flags1 != j_ptr->art_flags1) ||
+ (o_ptr->art_flags2 != j_ptr->art_flags2) ||
+ (o_ptr->art_flags3 != j_ptr->art_flags3))
+ return (0);
+
+ /* Hack -- Require identical "cursed" status */
+ if ((o_ptr->ident & (IDENT_CURSED)) != (j_ptr->ident & (IDENT_CURSED))) return (0);
+
+ /* Hack -- require semi-matching "inscriptions" */
+ if (o_ptr->note && j_ptr->note && (o_ptr->note != j_ptr->note)) return (0);
+
+ /* Hack -- normally require matching "inscriptions" */
+ if (!stack_force_notes && (o_ptr->note != j_ptr->note)) return (0);
+
+ /* Hack -- normally require matching "discounts" */
+ if (!stack_force_costs && (o_ptr->discount != j_ptr->discount)) return (0);
+
+
+ /* Maximal "stacking" limit */
+ if (total >= MAX_STACK_SIZE) return (0);
+
+
+ /* They match, so they must be similar */
+ return (TRUE);
+}
+
+
+/*
+ * Allow one item to "absorb" another, assuming they are similar
+ */
+void object_absorb(object_type *o_ptr, object_type *j_ptr)
+{
+ int total = o_ptr->number + j_ptr->number;
+
+ /* Add together the item counts */
+ o_ptr->number = ((total < MAX_STACK_SIZE) ? total : (MAX_STACK_SIZE - 1));
+
+ /* Hack -- blend "known" status */
+ if (object_known_p(j_ptr)) object_known(o_ptr);
+
+ /* Hack -- clear "storebought" if only one has it */
+ if (((o_ptr->ident & IDENT_STOREB) || (j_ptr->ident & IDENT_STOREB)) &&
+ (!((o_ptr->ident & IDENT_STOREB) && (j_ptr->ident & IDENT_STOREB))))
+ {
+ if (j_ptr->ident & IDENT_STOREB) j_ptr->ident &= 0xEF;
+ if (o_ptr->ident & IDENT_STOREB) o_ptr->ident &= 0xEF;
+ }
+
+ /* Hack -- blend "mental" status */
+ if (j_ptr->ident & (IDENT_MENTAL)) o_ptr->ident |= (IDENT_MENTAL);
+
+ /* Hack -- blend "inscriptions" */
+ if (j_ptr->note) o_ptr->note = j_ptr->note;
+
+ /* Hack -- could average discounts XXX XXX XXX */
+ /* Hack -- save largest discount XXX XXX XXX */
+ if (o_ptr->discount < j_ptr->discount) o_ptr->discount = j_ptr->discount;
+
+ /* Hack -- if wands are stacking, combine the charges. -LM- */
+ if (o_ptr->tval == TV_WAND)
+ {
+ o_ptr->pval += j_ptr->pval;
+ }
+}
+
+
+
+/*
+ * Find the index of the object_kind with the given tval and sval
+ */
+s16b lookup_kind(int tval, int sval)
+{
+ int k;
+
+ /* Look for it */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Found a match */
+ if ((k_ptr->tval == tval) && (k_ptr->sval == sval)) return (k);
+ }
+
+ /* Oops */
+ if (wizard) msg_format("No object (%d,%d)", tval, sval);
+
+ /* Oops */
+ return (0);
+}
+
+
+/*
+ * Wipe an object clean.
+ */
+void object_wipe(object_type *o_ptr)
+{
+ /* Wipe the structure */
+ o_ptr = WIPE(o_ptr, object_type);
+}
+
+
+/*
+ * Prepare an object based on an existing object
+ */
+void object_copy(object_type *o_ptr, object_type *j_ptr)
+{
+ /* Copy the structure */
+ COPY(o_ptr, j_ptr, object_type);
+}
+
+
+/*
+ * Prepare an object based on an object kind.
+ */
+void object_prep(object_type *o_ptr, int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ /* Clear the record */
+ o_ptr = WIPE(o_ptr, object_type);
+
+ /* Save the kind index */
+ o_ptr->k_idx = k_idx;
+
+ /* Efficiency -- tval/sval */
+ o_ptr->tval = k_ptr->tval;
+ o_ptr->sval = k_ptr->sval;
+
+ /* Default "pval" */
+ o_ptr->pval = k_ptr->pval;
+ o_ptr->pval2 = k_ptr->pval2;
+
+ /* Default number */
+ o_ptr->number = 1;
+
+ /* Default weight */
+ o_ptr->weight = k_ptr->weight;
+
+ /* Default magic */
+ o_ptr->to_h = k_ptr->to_h;
+ o_ptr->to_d = k_ptr->to_d;
+ o_ptr->to_a = k_ptr->to_a;
+
+ /* Default power */
+ o_ptr->ac = k_ptr->ac;
+ o_ptr->dd = k_ptr->dd;
+ o_ptr->ds = k_ptr->ds;
+
+ /* Hack -- cursed items are always "cursed" */
+ if (k_ptr->flags3 & (TR3_CURSED)) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (k_ptr->flags4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ o_ptr->pval2 = 1; /* Start with one point */
+ o_ptr->pval3 = 0; /* No flags groups */
+ }
+}
+
+
+/*
+ * Help determine an "enchantment bonus" for an object.
+ *
+ * To avoid floating point but still provide a smooth distribution of bonuses,
+ * we simply round the results of division in such a way as to "average" the
+ * correct floating point value.
+ *
+ * This function has been changed. It uses "randnor()" to choose values from
+ * a normal distribution, whose mean moves from zero towards the max as the
+ * level increases, and whose standard deviation is equal to 1/4 of the max,
+ * and whose values are forced to lie between zero and the max, inclusive.
+ *
+ * Since the "level" rarely passes 100 before Morgoth is dead, it is very
+ * rare to get the "full" enchantment on an object, even a deep levels.
+ *
+ * It is always possible (albeit unlikely) to get the "full" enchantment.
+ *
+ * A sample distribution of values from "m_bonus(10, N)" is shown below:
+ *
+ * N 0 1 2 3 4 5 6 7 8 9 10
+ * --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
+ * 0 66.37 13.01 9.73 5.47 2.89 1.31 0.72 0.26 0.12 0.09 0.03
+ * 8 46.85 24.66 12.13 8.13 4.20 2.30 1.05 0.36 0.19 0.08 0.05
+ * 16 30.12 27.62 18.52 10.52 6.34 3.52 1.95 0.90 0.31 0.15 0.05
+ * 24 22.44 15.62 30.14 12.92 8.55 5.30 2.39 1.63 0.62 0.28 0.11
+ * 32 16.23 11.43 23.01 22.31 11.19 7.18 4.46 2.13 1.20 0.45 0.41
+ * 40 10.76 8.91 12.80 29.51 16.00 9.69 5.90 3.43 1.47 0.88 0.65
+ * 48 7.28 6.81 10.51 18.27 27.57 11.76 7.85 4.99 2.80 1.22 0.94
+ * 56 4.41 4.73 8.52 11.96 24.94 19.78 11.06 7.18 3.68 1.96 1.78
+ * 64 2.81 3.07 5.65 9.17 13.01 31.57 13.70 9.30 6.04 3.04 2.64
+ * 72 1.87 1.99 3.68 7.15 10.56 20.24 25.78 12.17 7.52 4.42 4.62
+ * 80 1.02 1.23 2.78 4.75 8.37 12.04 27.61 18.07 10.28 6.52 7.33
+ * 88 0.70 0.57 1.56 3.12 6.34 10.06 15.76 30.46 12.58 8.47 10.38
+ * 96 0.27 0.60 1.25 2.28 4.30 7.60 10.77 22.52 22.51 11.37 16.53
+ * 104 0.22 0.42 0.77 1.36 2.62 5.33 8.93 13.05 29.54 15.23 22.53
+ * 112 0.15 0.20 0.56 0.87 2.00 3.83 6.86 10.06 17.89 27.31 30.27
+ * 120 0.03 0.11 0.31 0.46 1.31 2.48 4.60 7.78 11.67 25.53 45.72
+ * 128 0.02 0.01 0.13 0.33 0.83 1.41 3.24 6.17 9.57 14.22 64.07
+ */
+s16b m_bonus(int max, int level)
+{
+ int bonus, stand, extra, value;
+
+
+ /* Paranoia -- enforce maximal "level" */
+ if (level > MAX_DEPTH - 1) level = MAX_DEPTH - 1;
+
+
+ /* The "bonus" moves towards the max */
+ bonus = ((max * level) / MAX_DEPTH);
+
+ /* Hack -- determine fraction of error */
+ extra = ((max * level) % MAX_DEPTH);
+
+ /* Hack -- simulate floating point computations */
+ if (rand_int(MAX_DEPTH) < extra) bonus++;
+
+
+ /* The "stand" is equal to one quarter of the max */
+ stand = (max / 4);
+
+ /* Hack -- determine fraction of error */
+ extra = (max % 4);
+
+ /* Hack -- simulate floating point computations */
+ if (rand_int(4) < extra) stand++;
+
+
+ /* Choose an "interesting" value */
+ value = randnor(bonus, stand);
+
+ /* Enforce the minimum value */
+ if (value < 0) return (0);
+
+ /* Enforce the maximum value */
+ if (value > max) return (max);
+
+ /* Result */
+ return (value);
+}
+
+
+/*
+ * Tinker with the random artifact to make it acceptable
+ * for a certain depth; also connect a random artifact to an
+ * object.
+ */
+static void finalize_randart(object_type* o_ptr, int lev)
+{
+ int r;
+ int i = 0;
+ int foo = lev + randnor(0, 5);
+ bool_ flag = TRUE;
+
+ /* Paranoia */
+ if (o_ptr->tval != TV_RANDART) return;
+
+ if (foo < 1) foo = 1;
+ if (foo > 100) foo = 100;
+
+ while (flag)
+ {
+ r = rand_int(MAX_RANDARTS);
+
+ if (!(random_artifacts[r].generated) || i > 2000)
+ {
+ random_artifact* ra_ptr = &random_artifacts[r];
+
+ o_ptr->sval = r;
+ o_ptr->pval2 = ra_ptr->activation;
+ o_ptr->xtra2 = activation_info[ra_ptr->activation].spell;
+
+ ra_ptr->level = lev;
+ ra_ptr->generated = TRUE;
+ flag = FALSE;
+ }
+
+ i++;
+ }
+}
+
+
+
+/*
+ * Cheat -- describe a created object for the user
+ */
+static void object_mention(object_type *o_ptr)
+{
+ char o_name[80];
+
+ /* Describe */
+ object_desc_store(o_name, o_ptr, FALSE, 0);
+
+ /* Artifact */
+ if (artifact_p(o_ptr))
+ {
+ /* Silly message */
+ msg_format("Artifact (%s)", o_name);
+ }
+
+ /* Random Artifact */
+ else if (o_ptr->art_name)
+ {
+ msg_print("Random artifact");
+ }
+
+ /* Ego-item */
+ else if (ego_item_p(o_ptr))
+ {
+ /* Silly message */
+ msg_format("Ego-item (%s)", o_name);
+ }
+
+ /* Normal item */
+ else
+ {
+ /* Silly message */
+ msg_format("Object (%s)", o_name);
+ }
+}
+
+
+void random_artifact_resistance(object_type * o_ptr)
+{
+ bool_ give_resistance = FALSE, give_power = FALSE;
+
+ switch (o_ptr->name1)
+ {
+ case ART_CELEBORN:
+ case ART_ARVEDUI:
+ case ART_CASPANION:
+ case ART_TRON:
+ case ART_ROHIRRIM:
+ case ART_CELEGORM:
+ case ART_ANARION:
+ case ART_THRANDUIL:
+ case ART_LUTHIEN:
+ case ART_THROR:
+ case ART_THORIN:
+ case ART_NIMTHANC:
+ case ART_DETHANC:
+ case ART_NARTHANC:
+ case ART_STING:
+ case ART_TURMIL:
+ case ART_THALKETTOTH:
+ {
+ /* Give a resistance */
+ give_resistance = TRUE;
+ }
+ break;
+ case ART_MAEDHROS:
+ case ART_GLAMDRING:
+ case ART_ORCRIST:
+ case ART_ANDURIL:
+ case ART_ZARCUTHRA:
+ case ART_GURTHANG:
+ case ART_HARADEKKET:
+ case ART_CUBRAGOL:
+ case ART_DAWN:
+ {
+ /* Give a resistance OR a power */
+ if (randint(2) == 1) give_resistance = TRUE;
+ else give_power = TRUE;
+ }
+ break;
+ case ART_NENYA:
+ case ART_VILYA:
+ case ART_BERUTHIEL:
+ case ART_FINGOLFIN:
+ case ART_THINGOL:
+ case ART_ULMO:
+ case ART_OLORIN:
+ {
+ /* Give a power */
+ give_power = TRUE;
+ }
+ break;
+ case ART_POWER:
+ case ART_GONDOR:
+ case ART_AULE:
+ {
+ /* Give both */
+ give_power = TRUE;
+ give_resistance = TRUE;
+ }
+ break;
+ }
+
+ if (give_power)
+ {
+ o_ptr->xtra1 = EGO_XTRA_ABILITY;
+
+ /* Randomize the "xtra" power */
+ if (o_ptr->xtra1) o_ptr->xtra2 = randint(256);
+ }
+
+ artifact_bias = 0;
+
+ if (give_resistance)
+ {
+ random_resistance(o_ptr, FALSE, ((randint(22)) + 16));
+ }
+}
+
+
+/*
+ * Mega-Hack -- Attempt to create one of the "Special Objects"
+ *
+ * We are only called from "make_object()", and we assume that
+ * "apply_magic()" is called immediately after we return.
+ *
+ * Note -- see "make_artifact()" and "apply_magic()"
+ */
+static bool_ make_artifact_special(object_type *o_ptr)
+{
+ int i;
+ int k_idx = 0;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* No artifacts in the town */
+ if (!dun_level) return (FALSE);
+
+ /* Check the artifact list (just the "specials") */
+ for (i = 0; i < max_a_idx; i++)
+ {
+ artifact_type *a_ptr = &a_info[i];
+
+ /* Skip "empty" artifacts */
+ if (!a_ptr->name) continue;
+
+ /* Cannot make an artifact twice */
+ if (a_ptr->cur_num) continue;
+
+ /* Cannot generate non special ones */
+ if (!(a_ptr->flags3 & TR3_INSTA_ART)) continue;
+
+ /* Cannot generate some artifacts because they can only exists in special dungeons/quests/... */
+ if ((a_ptr->flags4 & TR4_SPECIAL_GENE) && (!a_allow_special[i])) continue;
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (a_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (a_ptr->level - dun_level) * 2;
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0) continue;
+ }
+
+ /* Artifact "rarity roll" */
+ if (rand_int(a_ptr->rarity - luck( -(a_ptr->rarity / 2), a_ptr->rarity / 2)) != 0) continue;
+
+ /* Find the base object */
+ k_idx = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* XXX XXX Enforce minimum "object" level (loosely) */
+ if (k_info[k_idx].level > object_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (k_info[k_idx].level - object_level) * 5;
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0) continue;
+ }
+
+ /* Assign the template */
+ object_prep(o_ptr, k_idx);
+
+ /* Mega-Hack -- mark the item as an artifact */
+ o_ptr->name1 = i;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (f4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_info[k_idx].level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Failure */
+ return (FALSE);
+}
+
+
+/*
+ * Attempt to change an object into an artifact
+ *
+ * This routine should only be called by "apply_magic()"
+ *
+ * Note -- see "make_artifact_special()" and "apply_magic()"
+ */
+static bool_ make_artifact(object_type *o_ptr)
+{
+ int i;
+ u32b f1, f2, f3, f4, f5, esp;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* No artifacts in the town */
+ if (!dun_level) return (FALSE);
+
+ /* Paranoia -- no "plural" artifacts */
+ if (o_ptr->number != 1) return (FALSE);
+
+ /* Check the artifact list (skip the "specials") */
+ for (i = 0; i < max_a_idx; i++)
+ {
+ artifact_type *a_ptr = &a_info[i];
+
+ /* Skip "empty" items */
+ if (!a_ptr->name) continue;
+
+ /* Cannot make an artifact twice */
+ if (a_ptr->cur_num) continue;
+
+ /* Cannot generate special ones */
+ if (a_ptr->flags3 & TR3_INSTA_ART) continue;
+
+ /* Cannot generate some artifacts because they can only exists in special dungeons/quests/... */
+ if ((a_ptr->flags4 & TR4_SPECIAL_GENE) && (!a_allow_special[i])) continue;
+
+ /* Must have the correct fields */
+ if (a_ptr->tval != o_ptr->tval) continue;
+ if (a_ptr->sval != o_ptr->sval) continue;
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (a_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (a_ptr->level - dun_level) * 2;
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0) continue;
+ }
+
+ /* We must make the "rarity roll" */
+ if (rand_int(a_ptr->rarity - luck( -(a_ptr->rarity / 2), a_ptr->rarity / 2)) != 0) continue;
+
+ /* Hack -- mark the item as an artifact */
+ o_ptr->name1 = i;
+
+ /* Hack: Some artifacts get random extra powers */
+ random_artifact_resistance(o_ptr);
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (f4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Failure */
+ return (FALSE);
+}
+
+/*
+ * Attempt to change an object into an ego
+ *
+ * This routine should only be called by "apply_magic()"
+ */
+static bool_ make_ego_item(object_type *o_ptr, bool_ good)
+{
+ int i = 0, j;
+ int *ok_ego, ok_num = 0;
+ bool_ ret = FALSE;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ if (artifact_p(o_ptr) || o_ptr->name2) return (FALSE);
+
+ C_MAKE(ok_ego, max_e_idx, int);
+
+ /* Grab the ok ego */
+ for (i = 0; i < max_e_idx; i++)
+ {
+ ego_item_type *e_ptr = &e_info[i];
+ bool_ ok = FALSE;
+
+ /* Skip "empty" items */
+ if (!e_ptr->name) continue;
+
+ /* Must have the correct fields */
+ for (j = 0; j < 6; j++)
+ {
+ if (e_ptr->tval[j] == o_ptr->tval)
+ {
+ if ((e_ptr->min_sval[j] <= o_ptr->sval) && (e_ptr->max_sval[j] >= o_ptr->sval)) ok = TRUE;
+ }
+
+ if (ok) break;
+ }
+ if (!ok)
+ {
+ /* Doesnt count as a try*/
+ continue;
+ }
+
+ /* Good should be good, bad should be bad */
+ if (good && (!e_ptr->cost)) continue;
+ if ((!good) && e_ptr->cost) continue;
+
+ /* Must posses the good flags */
+ if (((k_ptr->flags1 & e_ptr->need_flags1) != e_ptr->need_flags1) ||
+ ((k_ptr->flags2 & e_ptr->need_flags2) != e_ptr->need_flags2) ||
+ ((k_ptr->flags3 & e_ptr->need_flags3) != e_ptr->need_flags3) ||
+ ((k_ptr->flags4 & e_ptr->need_flags4) != e_ptr->need_flags4) ||
+ ((k_ptr->flags5 & e_ptr->need_flags5) != e_ptr->need_flags5) ||
+ ((k_ptr->esp & e_ptr->need_esp) != e_ptr->need_esp))
+ continue;
+ if ((k_ptr->flags1 & e_ptr->forbid_flags1) ||
+ (k_ptr->flags2 & e_ptr->forbid_flags2) ||
+ (k_ptr->flags3 & e_ptr->forbid_flags3) ||
+ (k_ptr->flags4 & e_ptr->forbid_flags4) ||
+ (k_ptr->flags5 & e_ptr->forbid_flags5) ||
+ (k_ptr->esp & e_ptr->forbid_esp))
+ continue;
+
+ /* ok */
+ ok_ego[ok_num++] = i;
+ }
+
+ /* Now test them a few times */
+ for (i = 0; i < ok_num * 10; i++)
+ {
+ ego_item_type *e_ptr;
+
+ int j = ok_ego[rand_int(ok_num)];
+ e_ptr = &e_info[j];
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (e_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (e_ptr->level - dun_level);
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0)
+ {
+ continue;
+ }
+ }
+
+ /* We must make the "rarity roll" */
+ if (rand_int(e_ptr->mrarity - luck( -(e_ptr->mrarity / 2), e_ptr->mrarity / 2)) > e_ptr->rarity)
+ {
+ continue;
+ }
+
+ /* Hack -- mark the item as an ego */
+ o_ptr->name2 = j;
+
+ /* Success */
+ ret = TRUE;
+ break;
+ }
+
+ /*
+ * Sometimes(rarely) tries for a double ego
+ * Also make sure we dont already have a name2b, wchih would mean a special ego item
+ */
+ if (magik(7 + luck( -7, 7)) && (!o_ptr->name2b))
+ {
+ /* Now test them a few times */
+ for (i = 0; i < ok_num * 10; i++)
+ {
+ ego_item_type *e_ptr;
+
+ int j = ok_ego[rand_int(ok_num)];
+ e_ptr = &e_info[j];
+
+ /* Cannot be a double ego of the same ego type */
+ if (j == o_ptr->name2) continue;
+
+ /* Cannot have 2 suffixes or 2 prefixes */
+ if (e_info[o_ptr->name2].before && e_ptr->before) continue;
+ if ((!e_info[o_ptr->name2].before) && (!e_ptr->before)) continue;
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (e_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (e_ptr->level - dun_level);
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0)
+ {
+ continue;
+ }
+ }
+
+ /* We must make the "rarity roll" */
+ if (rand_int(e_ptr->mrarity - luck( -(e_ptr->mrarity / 2), e_ptr->mrarity / 2)) > e_ptr->rarity)
+ {
+ continue;
+ }
+
+ /* Hack -- mark the item as an ego */
+ o_ptr->name2b = j;
+
+ /* Success */
+ ret = TRUE;
+ break;
+ }
+ }
+
+ C_FREE(ok_ego, max_e_idx, int);
+
+ /* Return */
+ return (ret);
+}
+
+
+/*
+ * Charge a new stick.
+ */
+void charge_stick(object_type *o_ptr)
+{
+ o_ptr->pval = exec_lua(format("return get_stick_charges(%d)", o_ptr->pval2));
+}
+
+/*
+ * Apply magic to an item known to be a "weapon"
+ *
+ * Hack -- note special base damage dice boosting
+ * Hack -- note special processing for weapon/digger
+ * Hack -- note special rating boost for dragon scale mail
+ */
+static void a_m_aux_1(object_type *o_ptr, int level, int power)
+{
+ int tohit1 = randint(5) + m_bonus(5, level);
+ int todam1 = randint(5) + m_bonus(5, level);
+
+ int tohit2 = m_bonus(10, level);
+ int todam2 = m_bonus(10, level);
+
+ artifact_bias = 0;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Make ego item */
+ if ((rand_int(RANDART_WEAPON) == 1) && (o_ptr->tval != TV_TRAPKIT)) create_artifact(o_ptr, FALSE, TRUE);
+ else make_ego_item(o_ptr, TRUE);
+ }
+ else if (power < -1)
+ {
+ /* Make ego item */
+ make_ego_item(o_ptr, FALSE);
+ }
+
+ /* Good */
+ if (power > 0)
+ {
+ /* Enchant */
+ o_ptr->to_h += tohit1;
+ o_ptr->to_d += todam1;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Enchant again */
+ o_ptr->to_h += tohit2;
+ o_ptr->to_d += todam2;
+ }
+ }
+
+ /* Cursed */
+ else if (power < 0)
+ {
+ /* Penalize */
+ o_ptr->to_h -= tohit1;
+ o_ptr->to_d -= todam1;
+
+ /* Very cursed */
+ if (power < -1)
+ {
+ /* Penalize again */
+ o_ptr->to_h -= tohit2;
+ o_ptr->to_d -= todam2;
+ }
+
+ /* Cursed (if "bad") */
+ if (o_ptr->to_h + o_ptr->to_d < 0) o_ptr->ident |= (IDENT_CURSED);
+ }
+
+ /* Some special cases */
+ if (process_hooks(HOOK_APPLY_MAGIC, "(O,d,d)", o_ptr, level, power))
+ return;
+ switch (o_ptr->tval)
+ {
+ case TV_TRAPKIT:
+ {
+ /* Good */
+ if (power > 0) o_ptr->to_a += randint(5);
+
+ /* Very good */
+ if (power > 1) o_ptr->to_a += randint(5);
+
+ /* Bad */
+ if (power < 0) o_ptr->to_a -= randint(5);
+
+ /* Very bad */
+ if (power < -1) o_ptr->to_a -= randint(5);
+
+ break;
+ }
+ case TV_MSTAFF:
+ {
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ int gf[2], i;
+
+ for (i = 0; i < 2; i++)
+ {
+ int k = 0;
+
+ gf[i] = 0;
+ while (!k)
+ {
+ k = lookup_kind(TV_RUNE1, (gf[i] = rand_int(MAX_GF)));
+ }
+ }
+
+ o_ptr->pval = gf[0] + (gf[1] << 16);
+ o_ptr->pval3 = rand_int(RUNE_MOD_MAX) + (rand_int(RUNE_MOD_MAX) << 16);
+ o_ptr->pval2 = randint(70) + (randint(70) << 8);
+ }
+ else
+ o_ptr->art_flags5 |= (TR5_SPELL_CONTAIN | TR5_WIELD_CAST);
+ break;
+ }
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_SHOT:
+ {
+ if ((power == 1) && !o_ptr->name2)
+ {
+ if (randint(100) < 30)
+ {
+ /* Exploding missile */
+ int power[27] = {GF_ELEC, GF_POIS, GF_ACID,
+ GF_COLD, GF_FIRE, GF_PLASMA, GF_LITE,
+ GF_DARK, GF_SHARDS, GF_SOUND,
+ GF_CONFUSION, GF_FORCE, GF_INERTIA,
+ GF_MANA, GF_METEOR, GF_ICE, GF_CHAOS,
+ GF_NETHER, GF_NEXUS, GF_TIME,
+ GF_GRAVITY, GF_KILL_WALL, GF_AWAY_ALL,
+ GF_TURN_ALL, GF_NUKE, GF_STUN,
+ GF_DISINTEGRATE};
+
+ o_ptr->pval2 = power[rand_int(27)];
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+static void dragon_resist(object_type * o_ptr)
+{
+ do
+ {
+ artifact_bias = 0;
+
+ if (randint(4) == 1)
+ random_resistance(o_ptr, FALSE, ((randint(14)) + 4));
+ else
+ random_resistance(o_ptr, FALSE, ((randint(22)) + 16));
+ }
+ while (randint(2) == 1);
+}
+
+
+/*
+ * Apply magic to an item known to be "armor"
+ *
+ * Hack -- note special processing for crown/helm
+ * Hack -- note special processing for robe of permanence
+ */
+static void a_m_aux_2(object_type *o_ptr, int level, int power)
+{
+ int toac1 = randint(5) + m_bonus(5, level);
+
+ int toac2 = m_bonus(10, level);
+
+ artifact_bias = 0;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Make ego item */
+ if (rand_int(RANDART_ARMOR) == 1) create_artifact(o_ptr, FALSE, TRUE);
+ else make_ego_item(o_ptr, TRUE);
+ }
+ else if (power < -1)
+ {
+ /* Make ego item */
+ make_ego_item(o_ptr, FALSE);
+ }
+
+ /* Good */
+ if (power > 0)
+ {
+ /* Enchant */
+ o_ptr->to_a += toac1;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Enchant again */
+ o_ptr->to_a += toac2;
+ }
+ }
+
+ /* Cursed */
+ else if (power < 0)
+ {
+ /* Penalize */
+ o_ptr->to_a -= toac1;
+
+ /* Very cursed */
+ if (power < -1)
+ {
+ /* Penalize again */
+ o_ptr->to_a -= toac2;
+ }
+
+ /* Cursed (if "bad") */
+ if (o_ptr->to_a < 0) o_ptr->ident |= (IDENT_CURSED);
+ }
+
+ /* Analyze type */
+ if (process_hooks(HOOK_APPLY_MAGIC, "(O,d,d)", o_ptr, level, power))
+ return;
+ switch (o_ptr->tval)
+ {
+ case TV_CLOAK:
+ {
+ if (o_ptr->sval == SV_ELVEN_CLOAK)
+ o_ptr->pval = randint(4); /* No cursed elven cloaks...? */
+ else if (o_ptr->sval == SV_MIMIC_CLOAK)
+ {
+ s32b mimic;
+
+ call_lua("find_random_mimic_shape", "(d,d)", "d", level, TRUE, &mimic);
+ o_ptr->pval2 = mimic;
+ }
+ break;
+ }
+ case TV_DRAG_ARMOR:
+ {
+ /* Rating boost */
+ rating += 30;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+
+ break;
+ }
+ case TV_SHIELD:
+ {
+ if (o_ptr->sval == SV_DRAGON_SHIELD)
+ {
+ /* Rating boost */
+ rating += 5;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ dragon_resist(o_ptr);
+ }
+ break;
+ }
+ case TV_HELM:
+ {
+ if (o_ptr->sval == SV_DRAGON_HELM)
+ {
+ /* Rating boost */
+ rating += 5;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ dragon_resist(o_ptr);
+ }
+ break;
+ }
+ }
+}
+
+
+
+/*
+ * Apply magic to an item known to be a "ring" or "amulet"
+ *
+ * Hack -- note special rating boost for ring of speed
+ * Hack -- note special rating boost for amulet of the magi
+ * Hack -- note special "pval boost" code for ring of speed
+ * Hack -- note that some items must be cursed (or blessed)
+ */
+static void a_m_aux_3(object_type *o_ptr, int level, int power)
+{
+
+ artifact_bias = 0;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Make ego item */
+ if (rand_int(RANDART_JEWEL) == 1) create_artifact(o_ptr, FALSE, TRUE);
+ else make_ego_item(o_ptr, TRUE);
+ }
+ else if (power < -1)
+ {
+ /* Make ego item */
+ make_ego_item(o_ptr, FALSE);
+ }
+
+ /* Apply magic (good or bad) according to type */
+ if (process_hooks(HOOK_APPLY_MAGIC, "(O,d,d)", o_ptr, level, power))
+ return;
+ switch (o_ptr->tval)
+ {
+ case TV_RING:
+ {
+ /* Analyze */
+ switch (o_ptr->sval)
+ {
+ /* Strength, Constitution, Dexterity, Intelligence */
+ case SV_RING_ATTACKS:
+ {
+ /* Stat bonus */
+ o_ptr->pval = m_bonus(3, level);
+ if (o_ptr->pval < 1) o_ptr->pval = 1;
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Critical hits */
+ case SV_RING_CRIT:
+ {
+ /* Stat bonus */
+ o_ptr->pval = m_bonus(10, level);
+ if (o_ptr->pval < 1) o_ptr->pval = 1;
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+
+ case SV_RING_STR:
+ case SV_RING_CON:
+ case SV_RING_DEX:
+ case SV_RING_INT:
+ {
+ /* Stat bonus */
+ o_ptr->pval = 1 + m_bonus(5, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Ring of Speed! */
+ case SV_RING_SPEED:
+ {
+ /* Base speed (1 to 10) */
+ o_ptr->pval = randint(5) + m_bonus(5, level);
+
+ /* Super-charge the ring */
+ while (rand_int(100) < 50) o_ptr->pval++;
+
+ /* Cursed Ring */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+
+ break;
+ }
+
+ /* Rating boost */
+ rating += 25;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+
+ break;
+ }
+
+ case SV_RING_LORDLY:
+ {
+ do
+ {
+ random_resistance(o_ptr, FALSE, ((randint(20)) + 18));
+ }
+ while (randint(4) == 1);
+
+ /* Bonus to armor class */
+ o_ptr->to_a = 10 + randint(5) + m_bonus(10, level);
+ rating += 5;
+ }
+ break;
+
+ /* Searching */
+ case SV_RING_SEARCHING:
+ {
+ /* Bonus to searching */
+ o_ptr->pval = 1 + m_bonus(5, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Flames, Acid, Ice */
+ case SV_RING_FLAMES:
+ case SV_RING_ACID:
+ case SV_RING_ICE:
+ {
+ /* Bonus to armor class */
+ o_ptr->to_a = 5 + randint(5) + m_bonus(10, level);
+ break;
+ }
+
+ /* Weakness, Stupidity */
+ case SV_RING_WEAKNESS:
+ case SV_RING_STUPIDITY:
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Penalize */
+ o_ptr->pval = 0 - (1 + m_bonus(5, level));
+
+ break;
+ }
+
+ /* WOE, Stupidity */
+ case SV_RING_WOE:
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Penalize */
+ o_ptr->to_a = 0 - (5 + m_bonus(10, level));
+ o_ptr->pval = 0 - (1 + m_bonus(5, level));
+
+ break;
+ }
+
+ /* Ring of damage */
+ case SV_RING_DAMAGE:
+ {
+ /* Bonus to damage */
+ o_ptr->to_d = 5 + randint(8) + m_bonus(10, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonus */
+ o_ptr->to_d = 0 - (o_ptr->to_d);
+ }
+
+ break;
+ }
+
+ /* Ring of Accuracy */
+ case SV_RING_ACCURACY:
+ {
+ /* Bonus to hit */
+ o_ptr->to_h = 5 + randint(8) + m_bonus(10, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse tohit */
+ o_ptr->to_h = 0 - (o_ptr->to_h);
+ }
+
+ break;
+ }
+
+ /* Ring of Protection */
+ case SV_RING_PROTECTION:
+ {
+ /* Bonus to armor class */
+ o_ptr->to_a = 5 + randint(8) + m_bonus(10, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse toac */
+ o_ptr->to_a = 0 - (o_ptr->to_a);
+ }
+
+ break;
+ }
+
+ /* Ring of Slaying */
+ case SV_RING_SLAYING:
+ {
+ /* Bonus to damage and to hit */
+ o_ptr->to_d = randint(7) + m_bonus(10, level);
+ o_ptr->to_h = randint(7) + m_bonus(10, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonuses */
+ o_ptr->to_h = 0 - (o_ptr->to_h);
+ o_ptr->to_d = 0 - (o_ptr->to_d);
+ }
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case TV_AMULET:
+ {
+ /* Analyze */
+ switch (o_ptr->sval)
+ {
+ /* Amulet of Trickery */
+ case SV_AMULET_TRICKERY:
+ case SV_AMULET_DEVOTION:
+ {
+ o_ptr->pval = 1 + m_bonus(3, level);
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ break;
+ }
+
+ case SV_AMULET_WEAPONMASTERY:
+ {
+ o_ptr->pval = 1 + m_bonus(2, level);
+ o_ptr->to_a = 1 + m_bonus(4, level);
+ o_ptr->to_h = 1 + m_bonus(5, level);
+ o_ptr->to_d = 1 + m_bonus(5, level);
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ break;
+ }
+
+ /* Amulet of wisdom/charisma */
+ case SV_AMULET_BRILLANCE:
+ case SV_AMULET_CHARISMA:
+ case SV_AMULET_WISDOM:
+ case SV_AMULET_INFRA:
+ {
+ o_ptr->pval = 1 + m_bonus(5, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonuses */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Amulet of the Serpents */
+ case SV_AMULET_SERPENT:
+ {
+ o_ptr->pval = 1 + m_bonus(5, level);
+ o_ptr->to_a = 1 + m_bonus(6, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonuses */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ case SV_AMULET_NO_MAGIC:
+ case SV_AMULET_NO_TELE:
+ {
+ if (power < 0)
+ {
+ o_ptr->ident |= (IDENT_CURSED);
+ }
+ break;
+ }
+
+ case SV_AMULET_RESISTANCE:
+ {
+ if (randint(3) == 1) random_resistance(o_ptr, FALSE, ((randint(34)) + 4));
+ if (randint(5) == 1) o_ptr->art_flags2 |= TR2_RES_POIS;
+ }
+ break;
+
+ /* Amulet of searching */
+ case SV_AMULET_SEARCHING:
+ {
+ o_ptr->pval = randint(5) + m_bonus(5, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonuses */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Amulet of the Magi -- never cursed */
+ case SV_AMULET_THE_MAGI:
+ {
+ o_ptr->pval = 1 + m_bonus(3, level);
+
+ if (randint(3) == 1) o_ptr->art_flags3 |= TR3_SLOW_DIGEST;
+
+ /* Boost the rating */
+ rating += 25;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+
+ break;
+ }
+
+ /* Amulet of Doom -- always cursed */
+ case SV_AMULET_DOOM:
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Penalize */
+ o_ptr->pval = 0 - (randint(5) + m_bonus(5, level));
+ o_ptr->to_a = 0 - (randint(5) + m_bonus(5, level));
+
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Apply magic to an item known to be "boring"
+ *
+ * Hack -- note the special code for various items
+ */
+static void a_m_aux_4(object_type *o_ptr, int level, int power)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ s32b bonus_lvl, max_lvl;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Make ego item */
+ if ((rand_int(RANDART_JEWEL) == 1) && (o_ptr->tval == TV_LITE)) create_artifact(o_ptr, FALSE, TRUE);
+ else make_ego_item(o_ptr, TRUE);
+ }
+ else if (power < -1)
+ {
+ /* Make ego item */
+ make_ego_item(o_ptr, FALSE);
+ }
+
+ /* Apply magic (good or bad) according to type */
+ if (process_hooks(HOOK_APPLY_MAGIC, "(O,d,d)", o_ptr, level, power))
+ return;
+ switch (o_ptr->tval)
+ {
+ case TV_BOOK:
+ {
+ /* Randomize random books */
+ if (o_ptr->sval == 255)
+ {
+ int i = 0;
+
+ /* Only random ones */
+ if (magik(75))
+ i = exec_lua(format("return get_random_spell(SKILL_MAGIC, %d)", level));
+ else
+ i = exec_lua(format("return get_random_spell(SKILL_SPIRITUALITY, %d)", level));
+
+ /* Use globe of light(or the first one) */
+ if (i == -1)
+ o_ptr->pval = 0;
+ else
+ o_ptr->pval = i;
+ }
+
+ break;
+ }
+
+ case TV_LITE:
+ {
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- random fuel */
+ if (f4 & TR4_FUEL_LITE)
+ {
+ if (k_info[o_ptr->k_idx].pval2 > 0) o_ptr->timeout = randint(k_info[o_ptr->k_idx].pval2);
+ }
+
+ break;
+ }
+
+ case TV_CORPSE:
+ {
+ /* Hack -- choose a monster */
+ monster_race* r_ptr;
+ int r_idx = get_mon_num(dun_level);
+ r_ptr = &r_info[r_idx];
+
+ if (!(r_ptr->flags1 & RF1_UNIQUE))
+ o_ptr->pval2 = r_idx;
+ else
+ o_ptr->pval2 = 2;
+ o_ptr->pval3 = 0;
+ break;
+ }
+
+ case TV_EGG:
+ {
+ /* Hack -- choose a monster */
+ monster_race* r_ptr;
+ int r_idx, count = 0;
+ bool_ OK = FALSE;
+
+ while ((!OK) && (count < 1000))
+ {
+ r_idx = get_mon_num(dun_level);
+ r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags9 & RF9_HAS_EGG)
+ {
+ o_ptr->pval2 = r_idx;
+ OK = TRUE;
+ }
+ count++;
+ }
+ if (count == 1000) o_ptr->pval2 = 940; /* Blue fire-lizard */
+
+ r_ptr = &r_info[o_ptr->pval2];
+ o_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 100) + 1;
+ o_ptr->pval = r_ptr->weight * 3 + rand_int(r_ptr->weight) + 1;
+ break;
+ }
+
+ case TV_HYPNOS:
+ {
+ /* Hack -- choose a monster */
+ monster_race* r_ptr;
+ int r_idx = get_mon_num(dun_level);
+ r_ptr = &r_info[r_idx];
+
+ if (!(r_ptr->flags1 & RF1_NEVER_MOVE))
+ o_ptr->pval = r_idx;
+ else
+ o_ptr->pval = 20;
+
+ r_idx = o_ptr->pval;
+ r_ptr = &r_info[r_idx];
+
+ o_ptr->pval3 = maxroll(r_ptr->hdice, r_ptr->hside);
+ o_ptr->pval2 = o_ptr->pval2;
+ o_ptr->exp = 0;
+ o_ptr->elevel = r_ptr->level;
+ break;
+ }
+
+ case TV_WAND:
+ {
+ /* Decide the spell, pval == -1 means to bypass spell selection */
+ if (o_ptr->pval != -1)
+ {
+ int spl = exec_lua("return get_random_stick(TV_WAND, dun_level)");
+
+ if (spl == -1) spl = exec_lua("return find_spell('Manathrust')");
+
+ o_ptr->pval2 = spl;
+ }
+ /* Is the spell predefined by the object kind? */
+ else if (k_ptr->pval == -1)
+ {
+ o_ptr->pval2 = k_ptr->pval2;
+ }
+
+ /* Ok now get a base level */
+ call_lua("get_stick_base_level", "(d,d,d)", "d", TV_WAND, dun_level, o_ptr->pval2, &bonus_lvl);
+ call_lua("get_stick_max_level", "(d,d,d)", "d", TV_WAND, dun_level, o_ptr->pval2, &max_lvl);
+ o_ptr->pval3 = (max_lvl << 16) + (bonus_lvl & 0xFFFF);
+
+ /* Hack -- charge wands */
+ charge_stick(o_ptr);
+
+ break;
+ }
+
+ case TV_STAFF:
+ {
+ /* Decide the spell, pval == -1 means to bypass spell selection */
+ if (o_ptr->pval != -1)
+ {
+ int spl = exec_lua("return get_random_stick(TV_STAFF, dun_level)");
+
+ if (spl == -1) spl = exec_lua("return find_spell('Globe of Light')");
+
+ o_ptr->pval2 = spl;
+ }
+ /* Is the spell predefined by the object kind? */
+ else if (k_ptr->pval == -1)
+ {
+ o_ptr->pval2 = k_ptr->pval2;
+ }
+
+ /* Ok now get a base level */
+ call_lua("get_stick_base_level", "(d,d,d)", "d", TV_STAFF, dun_level, o_ptr->pval2, &bonus_lvl);
+ call_lua("get_stick_max_level", "(d,d,d)", "d", TV_STAFF, dun_level, o_ptr->pval2, &max_lvl);
+ o_ptr->pval3 = (max_lvl << 16) + (bonus_lvl & 0xFFFF);
+
+ /* Hack -- charge staffs */
+ charge_stick(o_ptr);
+
+ break;
+ }
+
+ case TV_CHEST:
+ {
+ /* Hack -- skip ruined chests */
+ if (k_info[o_ptr->k_idx].level <= 0) break;
+
+ /* Pick a trap */
+ place_trap_object(o_ptr);
+
+ /* Hack - set pval2 to the number of objects in it */
+ if (o_ptr->pval)
+ o_ptr->pval2 = (o_ptr->sval % SV_CHEST_MIN_LARGE) * 2;
+ break;
+ }
+ case TV_POTION:
+ if (o_ptr->sval == SV_POTION_BLOOD)
+ {
+ /* Rating boost */
+ rating += 25;
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ }
+ break;
+ case TV_POTION2:
+ if (o_ptr->sval == SV_POTION2_MIMIC)
+ {
+ s32b mimic;
+
+ call_lua("find_random_mimic_shape", "(d,d)", "d", level, FALSE, &mimic);
+ o_ptr->pval2 = mimic;
+ }
+ break;
+ case TV_INSTRUMENT:
+ {
+ if (o_ptr->sval != SV_HORN)
+ {
+ /* Nothing */
+ }
+ else
+ {
+ if (is_ego_p(o_ptr, EGO_INST_DRAGONKIND))
+ {
+ switch (randint(4))
+ {
+ case 1:
+ o_ptr->pval2 = GF_ELEC;
+ break;
+ case 2:
+ o_ptr->pval2 = GF_FIRE;
+ break;
+ case 3:
+ o_ptr->pval2 = GF_COLD;
+ break;
+ case 4:
+ o_ptr->pval2 = GF_ACID;
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ case TV_TOOL:
+ {
+ break;
+ }
+
+ }
+}
+
+void trap_hack(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_TRAPKIT) return;
+
+ switch (o_ptr->sval)
+ {
+ case SV_TRAPKIT_POTION:
+ case SV_TRAPKIT_SCROLL:
+ case SV_TRAPKIT_DEVICE:
+ o_ptr->to_h = 0;
+ o_ptr->to_d = 0;
+ default:
+ return;
+ }
+}
+
+/* Add a random glag to the ego item */
+void add_random_ego_flag(object_type *o_ptr, int fego, bool_ *limit_blows)
+{
+ if (fego & ETR4_SUSTAIN)
+ {
+ /* Make a random sustain */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ break;
+ case 2:
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ break;
+ case 3:
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ break;
+ case 4:
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ break;
+ case 5:
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ break;
+ case 6:
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ break;
+ }
+ }
+
+ if (fego & ETR4_OLD_RESIST)
+ {
+ /* Make a random resist, equal probabilities */
+ switch (randint(11))
+ {
+ case 1:
+ o_ptr->art_flags2 |= (TR2_RES_BLIND);
+ break;
+ case 2:
+ o_ptr->art_flags2 |= (TR2_RES_CONF);
+ break;
+ case 3:
+ o_ptr->art_flags2 |= (TR2_RES_SOUND);
+ break;
+ case 4:
+ o_ptr->art_flags2 |= (TR2_RES_SHARDS);
+ break;
+ case 5:
+ o_ptr->art_flags2 |= (TR2_RES_NETHER);
+ break;
+ case 6:
+ o_ptr->art_flags2 |= (TR2_RES_NEXUS);
+ break;
+ case 7:
+ o_ptr->art_flags2 |= (TR2_RES_CHAOS);
+ break;
+ case 8:
+ o_ptr->art_flags2 |= (TR2_RES_DISEN);
+ break;
+ case 9:
+ o_ptr->art_flags2 |= (TR2_RES_POIS);
+ break;
+ case 10:
+ o_ptr->art_flags2 |= (TR2_RES_DARK);
+ break;
+ case 11:
+ o_ptr->art_flags2 |= (TR2_RES_LITE);
+ break;
+ }
+ }
+
+ if (fego & ETR4_ABILITY)
+ {
+ /* Choose an ability */
+ switch (randint(8))
+ {
+ case 1:
+ o_ptr->art_flags3 |= (TR3_FEATHER);
+ break;
+ case 2:
+ o_ptr->art_flags3 |= (TR3_LITE1);
+ break;
+ case 3:
+ o_ptr->art_flags3 |= (TR3_SEE_INVIS);
+ break;
+ case 4:
+ o_ptr->art_esp |= (ESP_ALL);
+ break;
+ case 5:
+ o_ptr->art_flags3 |= (TR3_SLOW_DIGEST);
+ break;
+ case 6:
+ o_ptr->art_flags3 |= (TR3_REGEN);
+ break;
+ case 7:
+ o_ptr->art_flags2 |= (TR2_FREE_ACT);
+ break;
+ case 8:
+ o_ptr->art_flags2 |= (TR2_HOLD_LIFE);
+ break;
+ }
+ }
+
+ if (fego & ETR4_R_ELEM)
+ {
+ /* Make an acid/elec/fire/cold/poison resist */
+ random_resistance(o_ptr, FALSE, randint(14) + 4);
+ }
+ if (fego & ETR4_R_LOW)
+ {
+ /* Make an acid/elec/fire/cold resist */
+ random_resistance(o_ptr, FALSE, randint(12) + 4);
+ }
+
+ if (fego & ETR4_R_HIGH)
+ {
+ /* Make a high resist */
+ random_resistance(o_ptr, FALSE, randint(22) + 16);
+ }
+ if (fego & ETR4_R_ANY)
+ {
+ /* Make any resist */
+ random_resistance(o_ptr, FALSE, randint(34) + 4);
+ }
+
+ if (fego & ETR4_R_DRAGON)
+ {
+ /* Make "dragon resist" */
+ dragon_resist(o_ptr);
+ }
+
+ if (fego & ETR4_SLAY_WEAP)
+ {
+ /* Make a Weapon of Slaying */
+
+ if (randint(3) == 1) /* double damage */
+ o_ptr->dd *= 2;
+ else
+ {
+ do
+ {
+ o_ptr->dd++;
+ }
+ while (randint(o_ptr->dd) == 1);
+ do
+ {
+ o_ptr->ds++;
+ }
+ while (randint(o_ptr->ds) == 1);
+ }
+ if (randint(5) == 1)
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ }
+ if (o_ptr->tval == TV_SWORD && (randint(3) == 1))
+ {
+ o_ptr->art_flags1 |= TR1_VORPAL;
+ }
+ }
+
+ if (fego & ETR4_DAM_DIE)
+ {
+ /* Increase damage dice */
+ o_ptr->dd++;
+ }
+
+ if (fego & ETR4_DAM_SIZE)
+ {
+ /* Increase damage dice size */
+ o_ptr->ds++;
+ }
+
+ if (fego & ETR4_LIMIT_BLOWS)
+ {
+ /* Swap this flag */
+ *limit_blows = !(*limit_blows);
+ }
+
+ if (fego & ETR4_PVAL_M1)
+ {
+ /* Increase pval */
+ o_ptr->pval++;
+ }
+
+ if (fego & ETR4_PVAL_M2)
+ {
+ /* Increase pval */
+ o_ptr->pval += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_PVAL_M3)
+ {
+ /* Increase pval */
+ o_ptr->pval += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_PVAL_M5)
+ {
+ /* Increase pval */
+ o_ptr->pval += m_bonus(5, dun_level);
+ }
+ if (fego & ETR4_AC_M1)
+ {
+ /* Increase ac */
+ o_ptr->to_a++;
+ }
+
+ if (fego & ETR4_AC_M2)
+ {
+ /* Increase ac */
+ o_ptr->to_a += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_AC_M3)
+ {
+ /* Increase ac */
+ o_ptr->to_a += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_AC_M5)
+ {
+ /* Increase ac */
+ o_ptr->to_a += m_bonus(5, dun_level);
+ }
+
+ if (fego & ETR4_TH_M1)
+ {
+ /* Increase to hit */
+ o_ptr->to_h++;
+ }
+
+ if (fego & ETR4_TH_M2)
+ {
+ /* Increase to hit */
+ o_ptr->to_h += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_TH_M3)
+ {
+ /* Increase to hit */
+ o_ptr->to_h += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_TH_M5)
+ {
+ /* Increase to hit */
+ o_ptr->to_h += m_bonus(5, dun_level);
+ }
+
+ if (fego & ETR4_TD_M1)
+ {
+ /* Increase to dam */
+ o_ptr->to_d++;
+ }
+
+ if (fego & ETR4_TD_M2)
+ {
+ /* Increase to dam */
+ o_ptr->to_d += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_TD_M3)
+ {
+ /* Increase to dam */
+ o_ptr->to_d += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_TD_M5)
+ {
+ /* Increase to dam */
+ o_ptr->to_d += m_bonus(5, dun_level);
+ }
+
+ if (fego & ETR4_R_P_ABILITY)
+ {
+ /* Add a random pval-affected ability */
+ /* This might cause boots with + to blows */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr->art_flags1 |= TR1_STEALTH;
+ break;
+ case 2:
+ o_ptr->art_flags1 |= TR1_SEARCH;
+ break;
+ case 3:
+ o_ptr->art_flags1 |= TR1_INFRA;
+ break;
+ case 4:
+ o_ptr->art_flags1 |= TR1_TUNNEL;
+ break;
+ case 5:
+ o_ptr->art_flags1 |= TR1_SPEED;
+ break;
+ case 6:
+ o_ptr->art_flags1 |= TR1_BLOWS;
+ break;
+ }
+ }
+ if (fego & ETR4_R_STAT)
+ {
+ /* Add a random stat */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr->art_flags1 |= TR1_STR;
+ break;
+ case 2:
+ o_ptr->art_flags1 |= TR1_INT;
+ break;
+ case 3:
+ o_ptr->art_flags1 |= TR1_WIS;
+ break;
+ case 4:
+ o_ptr->art_flags1 |= TR1_DEX;
+ break;
+ case 5:
+ o_ptr->art_flags1 |= TR1_CON;
+ break;
+ case 6:
+ o_ptr->art_flags1 |= TR1_CHR;
+ break;
+ }
+ }
+
+ if (fego & ETR4_R_STAT_SUST)
+ {
+ /* Add a random stat and sustain it */
+ switch (randint(6))
+ {
+ case 1:
+ {
+ o_ptr->art_flags1 |= TR1_STR;
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ break;
+ }
+
+ case 2:
+ {
+ o_ptr->art_flags1 |= TR1_INT;
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ break;
+ }
+
+ case 3:
+ {
+ o_ptr->art_flags1 |= TR1_WIS;
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ break;
+ }
+
+ case 4:
+ {
+ o_ptr->art_flags1 |= TR1_DEX;
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ break;
+ }
+
+ case 5:
+ {
+ o_ptr->art_flags1 |= TR1_CON;
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ break;
+ }
+ case 6:
+ {
+ o_ptr->art_flags1 |= TR1_CHR;
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ break;
+ }
+ }
+ }
+
+ if (fego & ETR4_R_IMMUNITY)
+ {
+ /* Give a random immunity */
+ switch (randint(4))
+ {
+ case 1:
+ {
+ o_ptr->art_flags2 |= TR2_IM_FIRE;
+ o_ptr->art_flags3 |= TR3_IGNORE_FIRE;
+ break;
+ }
+ case 2:
+ {
+ o_ptr->art_flags2 |= TR2_IM_ACID;
+ o_ptr->art_flags3 |= TR3_IGNORE_ACID;
+ break;
+ }
+ case 3:
+ {
+ o_ptr->art_flags2 |= TR2_IM_ELEC;
+ o_ptr->art_flags3 |= TR3_IGNORE_ELEC;
+ break;
+ }
+ case 4:
+ {
+ o_ptr->art_flags2 |= TR2_IM_COLD;
+ o_ptr->art_flags3 |= TR3_IGNORE_COLD;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Complete the "creation" of an object by applying "magic" to the item
+ *
+ * This includes not only rolling for random bonuses, but also putting the
+ * finishing touches on ego-items and artifacts, giving charges to wands and
+ * staffs, giving fuel to lites, and placing traps on chests.
+ *
+ * In particular, note that "Instant Artifacts", if "created" by an external
+ * routine, must pass through this function to complete the actual creation.
+ *
+ * The base "chance" of the item being "good" increases with the "level"
+ * parameter, which is usually derived from the dungeon level, being equal
+ * to the level plus 10, up to a maximum of 75. If "good" is true, then
+ * the object is guaranteed to be "good". If an object is "good", then
+ * the chance that the object will be "great" (ego-item or artifact), also
+ * increases with the "level", being equal to half the level, plus 5, up to
+ * a maximum of 20. If "great" is true, then the object is guaranteed to be
+ * "great". At dungeon level 65 and below, 15/100 objects are "great".
+ *
+ * If the object is not "good", there is a chance it will be "cursed", and
+ * if it is "cursed", there is a chance it will be "broken". These chances
+ * are related to the "good" / "great" chances above.
+ *
+ * Otherwise "normal" rings and amulets will be "good" half the time and
+ * "cursed" half the time, unless the ring/amulet is always good or cursed.
+ *
+ * If "okay" is true, and the object is going to be "great", then there is
+ * a chance that an artifact will be created. This is true even if both the
+ * "good" and "great" arguments are false. As a total hack, if "great" is
+ * true, then the item gets 3 extra "attempts" to become an artifact.
+ */
+int hack_apply_magic_power = 0;
+void apply_magic(object_type *o_ptr, int lev, bool_ okay, bool_ good, bool_ great)
+{
+ int i, rolls, f1, f2, power;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Aply luck */
+ lev += luck( -7, 7);
+
+ /* Spell in it ? no ! */
+ if (k_ptr->flags5 & TR5_SPELL_CONTAIN)
+ o_ptr->pval2 = -1;
+
+ /* Important to do before all else, be sure to have the basic obvious flags set */
+ o_ptr->art_oflags1 = k_ptr->oflags1;
+ o_ptr->art_oflags2 = k_ptr->oflags2;
+ o_ptr->art_oflags3 = k_ptr->oflags3;
+ o_ptr->art_oflags4 = k_ptr->oflags4;
+ o_ptr->art_oflags5 = k_ptr->oflags5;
+ o_ptr->art_oesp = k_ptr->oesp;
+
+ /* No need to touch normal artifacts */
+ if (k_ptr->flags3 & TR3_NORM_ART)
+ {
+ /* Ahah! we tried to trick us !! */
+ if (k_ptr->artifact ||
+ ((k_ptr->flags4 & TR4_SPECIAL_GENE) &&
+ (!k_allow_special[o_ptr->k_idx])))
+ {
+ object_prep(o_ptr, lookup_kind(k_ptr->btval, k_ptr->bsval));
+ if (wizard) msg_print("We've been tricked!");
+ }
+ else
+ {
+ /* Arg I hate so much to do that ... but I see no other way */
+ if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))
+ {
+ s32b base_lvl, max_lvl;
+
+ /* Is the spell predefined by the object kind? */
+ if (k_ptr->pval == -1)
+ {
+ o_ptr->pval2 = k_ptr->pval2;
+ }
+
+ /* Determine a base and a max level */
+ call_lua("get_stick_base_level", "(d,d,d)", "d", o_ptr->tval, dun_level, o_ptr->pval2, &base_lvl);
+ call_lua("get_stick_max_level", "(d,d,d)", "d", o_ptr->tval, dun_level, o_ptr->pval2, &max_lvl);
+ o_ptr->pval3 = (max_lvl << 16) + (base_lvl & 0xFFFF);
+
+ /* Hack -- charge wands */
+ charge_stick(o_ptr);
+ }
+
+ k_ptr->artifact = TRUE;
+
+ if (cheat_peek || p_ptr->precognition) object_mention(o_ptr);
+ }
+
+ return;
+ }
+
+ /* Maximum "level" for various things */
+ if (lev > MAX_DEPTH - 1) lev = MAX_DEPTH - 1;
+
+
+ /* Base chance of being "good" */
+ f1 = lev + 10 + luck( -15, 15);
+
+ /* Maximal chance of being "good" */
+ if (f1 > 75) f1 = 75;
+
+ /* Base chance of being "great" */
+ f2 = f1 / 2;
+
+ /* Maximal chance of being "great" */
+ if (f2 > 20) f2 = 20;
+
+
+ /* Assume normal */
+ power = 0;
+
+ /* Roll for "good" */
+ if (good || magik(f1))
+ {
+ /* Assume "good" */
+ power = 1;
+
+ /* Roll for "great" */
+ if (great || magik(f2)) power = 2;
+ }
+
+ /* Roll for "cursed" */
+ else if (magik(f1))
+ {
+ /* Assume "cursed" */
+ power = -1;
+
+ /* Roll for "broken" */
+ if (magik(f2)) power = -2;
+ }
+
+ /* Mega hack */
+ if (hack_apply_magic_power)
+ {
+ if (hack_apply_magic_power == -99)
+ power = 0;
+ else
+ power = hack_apply_magic_power;
+ }
+ hack_apply_magic_power = 0;
+
+ /* Assume no rolls */
+ rolls = 0;
+
+ /* Get one roll if excellent */
+ if (power >= 2) rolls = 1;
+
+ /* Hack -- Get four rolls if forced great */
+ if (great) rolls = 4;
+
+ /* Hack -- Get no rolls if not allowed */
+ if (!okay || o_ptr->name1) rolls = 0;
+
+ /* Roll for artifacts if allowed */
+ for (i = 0; i < rolls; i++)
+ {
+ /* Roll for an artifact */
+ if (make_artifact(o_ptr)) break;
+ }
+
+ /* Mega hack -- to lazy to do it properly with hooks :) */
+ if ((o_ptr->name1 == ART_POWER) && (quest[QUEST_ONE].status < QUEST_STATUS_TAKEN))
+ {
+ o_ptr->name1 = 0;
+ o_ptr->name2 = 0;
+ o_ptr->name2b = 0;
+ object_prep(o_ptr, lookup_kind(TV_RING, SV_RING_INVIS));
+ }
+
+
+ /* Hack -- analyze artifacts */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Hack -- Mark the artifact as "created" */
+ a_ptr->cur_num = 1;
+
+ /* Extract the other fields */
+ o_ptr->pval = a_ptr->pval;
+ o_ptr->ac = a_ptr->ac;
+ o_ptr->dd = a_ptr->dd;
+ o_ptr->ds = a_ptr->ds;
+ o_ptr->to_a = a_ptr->to_a;
+ o_ptr->to_h = a_ptr->to_h;
+ o_ptr->to_d = a_ptr->to_d;
+ o_ptr->weight = a_ptr->weight;
+ o_ptr->number = 1;
+
+ /* Hack -- extract the "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Mega-Hack -- increase the rating */
+ rating += 10;
+
+ /* Mega-Hack -- increase the rating again */
+ if (a_ptr->cost > 50000L) rating += 10;
+
+ /* Set the good item flag */
+ good_item_flag = TRUE;
+
+ /* Cheat -- peek at the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+
+ /* Spell in it ? no ! */
+ if (a_ptr->flags5 & TR5_SPELL_CONTAIN)
+ o_ptr->pval2 = -1;
+
+ /* Give a basic exp/exp level to an artifact that needs it */
+ if (a_ptr->flags4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Done */
+ return;
+ }
+
+
+ /* Apply magic */
+ switch (o_ptr->tval)
+ {
+ case TV_RANDART:
+ {
+ finalize_randart(o_ptr, lev);
+ break;
+ }
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_TRAPKIT:
+ {
+ if (power) a_m_aux_1(o_ptr, lev, power);
+ break;
+ }
+
+ case TV_DAEMON_BOOK:
+ {
+ /* UGLY, but needed, depending of sval teh demon stuff are eitehr weapon or armor */
+ if (o_ptr->sval == SV_DEMONBLADE)
+ {
+ if (power) a_m_aux_1(o_ptr, lev, power);
+ }
+ else
+ {
+ if (power) a_m_aux_2(o_ptr, lev, power);
+ }
+ break;
+ }
+
+ case TV_DRAG_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_SOFT_ARMOR:
+ case TV_SHIELD:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_CLOAK:
+ case TV_GLOVES:
+ case TV_BOOTS:
+ {
+ a_m_aux_2(o_ptr, lev, power);
+ break;
+ }
+
+ case TV_RING:
+ case TV_AMULET:
+ {
+ if (!power && (rand_int(100) < 50)) power = -1;
+ a_m_aux_3(o_ptr, lev, power);
+ break;
+ }
+
+ default:
+ {
+ a_m_aux_4(o_ptr, lev, power);
+ break;
+ }
+ }
+
+ if (o_ptr->art_name) rating += 40;
+
+ /* Hack -- analyze ego-items */
+ else if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr;
+ int j;
+ bool_ limit_blows = FALSE;
+ u32b f1, f2, f3, f4, f5, esp;
+ s16b e_idx;
+
+ e_idx = o_ptr->name2;
+
+ /* Ok now, THAT is truly ugly */
+try_an_other_ego:
+ e_ptr = &e_info[e_idx];
+
+ /* Hack -- extra powers */
+ for (j = 0; j < 5; j++)
+ {
+ /* Rarity check */
+ if (magik(e_ptr->rar[j]))
+ {
+ o_ptr->art_flags1 |= e_ptr->flags1[j];
+ o_ptr->art_flags2 |= e_ptr->flags2[j];
+ o_ptr->art_flags3 |= e_ptr->flags3[j];
+ o_ptr->art_flags4 |= e_ptr->flags4[j];
+ o_ptr->art_flags5 |= e_ptr->flags5[j];
+ o_ptr->art_esp |= e_ptr->esp[j];
+
+ o_ptr->art_oflags1 |= e_ptr->oflags1[j];
+ o_ptr->art_oflags2 |= e_ptr->oflags2[j];
+ o_ptr->art_oflags3 |= e_ptr->oflags3[j];
+ o_ptr->art_oflags4 |= e_ptr->oflags4[j];
+ o_ptr->art_oflags5 |= e_ptr->oflags5[j];
+ o_ptr->art_oesp |= e_ptr->oesp[j];
+
+ add_random_ego_flag(o_ptr, e_ptr->fego[j], &limit_blows);
+ }
+ }
+
+ /* No insane number of blows */
+ if (limit_blows && (o_ptr->art_flags1 & TR1_BLOWS))
+ {
+ if (o_ptr->pval > 2) o_ptr->pval = randint(2);
+ }
+
+ /* get flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- acquire "cursed" flag */
+ if (f3 & TR3_CURSED) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Hack -- obtain bonuses */
+ if (e_ptr->max_to_h > 0) o_ptr->to_h += randint(e_ptr->max_to_h);
+ if (e_ptr->max_to_h < 0) o_ptr->to_h -= randint( -e_ptr->max_to_h);
+ if (e_ptr->max_to_d > 0) o_ptr->to_d += randint(e_ptr->max_to_d);
+ if (e_ptr->max_to_d < 0) o_ptr->to_d -= randint( -e_ptr->max_to_d);
+ if (e_ptr->max_to_a > 0) o_ptr->to_a += randint(e_ptr->max_to_a);
+ if (e_ptr->max_to_a < 0) o_ptr->to_a -= randint( -e_ptr->max_to_a);
+
+ /* Hack -- obtain pval */
+ if (e_ptr->max_pval > 0) o_ptr->pval += randint(e_ptr->max_pval);
+ if (e_ptr->max_pval < 0) o_ptr->pval -= randint( -e_ptr->max_pval);
+
+ /* Hack -- apply rating bonus */
+ rating += e_ptr->rating;
+
+ if (o_ptr->name2b && (o_ptr->name2b != e_idx))
+ {
+ e_idx = o_ptr->name2b;
+ goto try_an_other_ego;
+ }
+
+ /* Spell in it ? no ! */
+ if (f5 & TR5_SPELL_CONTAIN)
+ {
+ /* Mega hack, mage staves of spell cannot SPELL_CONTAIN */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ o_ptr->art_flags5 &= ~TR5_SPELL_CONTAIN;
+ }
+ else
+ o_ptr->pval2 = -1;
+ }
+
+ /* Cheat -- describe the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ }
+
+
+ /* Examine real objects */
+ if (o_ptr->k_idx)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Hack -- acquire "cursed" flag */
+ if (k_ptr->flags3 & (TR3_CURSED)) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (f4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Spell in it ? no ! */
+ if (f5 & TR5_SPELL_CONTAIN)
+ {
+ /* Mega hack, mage staves of spell cannot SPELL_CONTAIN */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ o_ptr->art_flags5 &= ~TR5_SPELL_CONTAIN;
+ }
+ else
+ o_ptr->pval2 = -1;
+ }
+
+ /* Hacccccccckkkkk attack ! :) -- To prevent som ugly crashs */
+ if ((o_ptr->tval == TV_MSTAFF) && (o_ptr->sval == SV_MSTAFF) && (o_ptr->pval < 0))
+ {
+ o_ptr->pval = 0;
+ }
+
+ /* Hack, cant be done in a_m_aux4 because the ego flags are not yet in place */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* Set the max mana and the current mana */
+ o_ptr->pval2 = (f4 & TR4_CAPACITY) ? o_ptr->sval * 2 : o_ptr->sval;
+
+ o_ptr->timeout = o_ptr->pval2;
+ }
+
+ /* Remove some unnecessary stuff hack */
+ if (o_ptr->tval == TV_TRAPKIT) trap_hack(o_ptr);
+ }
+}
+
+
+/* The themed objects to use */
+static obj_theme match_theme;
+
+/*
+ * XXX XXX XXX It relies on the fact that obj_theme is a four byte structure
+ * for its efficient operation. A horrendous hack, I'd say.
+ */
+void init_match_theme(obj_theme theme)
+{
+ /* Save the theme */
+ match_theme = theme;
+}
+
+/*
+ * Ditto XXX XXX XXX
+ */
+static bool_ theme_changed(obj_theme theme)
+{
+ /* Any of the themes has been changed */
+ if (theme.treasure != match_theme.treasure) return (TRUE);
+ if (theme.combat != match_theme.combat) return (TRUE);
+ if (theme.magic != match_theme.magic) return (TRUE);
+ if (theme.tools != match_theme.tools) return (TRUE);
+
+ /* No changes */
+ return (FALSE);
+}
+
+
+/*
+ * Maga-Hack -- match certain types of object only.
+ */
+bool_ kind_is_theme(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ s32b prob = 0;
+
+
+ /*
+ * Paranoia -- Prevent accidental "(Nothing)"
+ * that are results of uninitialised theme structs.
+ *
+ * Caution: Junks go into the allocation table.
+ */
+ if (match_theme.treasure + match_theme.combat +
+ match_theme.magic + match_theme.tools == 0) return (TRUE);
+
+
+ /* Pick probability to use */
+ switch (k_ptr->tval)
+ {
+ case TV_SKELETON:
+ case TV_BOTTLE:
+ case TV_JUNK:
+ case TV_CORPSE:
+ case TV_EGG:
+ {
+ /*
+ * Degree of junk is defined in terms of the other
+ * 4 quantities XXX XXX XXX
+ * The type of prob should be *signed* as well as
+ * larger than theme components, or we would see
+ * unexpected, well, junks.
+ */
+ prob = 100 - (match_theme.treasure + match_theme.combat +
+ match_theme.magic + match_theme.tools);
+ break;
+ }
+ case TV_CHEST:
+ prob = match_theme.treasure;
+ break;
+ case TV_CROWN:
+ prob = match_theme.treasure;
+ break;
+ case TV_DRAG_ARMOR:
+ prob = match_theme.treasure;
+ break;
+ case TV_AMULET:
+ prob = match_theme.treasure;
+ break;
+ case TV_RING:
+ prob = match_theme.treasure;
+ break;
+
+ case TV_SHOT:
+ prob = match_theme.combat;
+ break;
+ case TV_ARROW:
+ prob = match_theme.combat;
+ break;
+ case TV_BOLT:
+ prob = match_theme.combat;
+ break;
+ case TV_BOOMERANG:
+ prob = match_theme.combat;
+ break;
+ case TV_BOW:
+ prob = match_theme.combat;
+ break;
+ case TV_HAFTED:
+ prob = match_theme.combat;
+ break;
+ case TV_POLEARM:
+ prob = match_theme.combat;
+ break;
+ case TV_SWORD:
+ prob = match_theme.combat;
+ break;
+ case TV_AXE:
+ prob = match_theme.combat;
+ break;
+ case TV_GLOVES:
+ prob = match_theme.combat;
+ break;
+ case TV_HELM:
+ prob = match_theme.combat;
+ break;
+ case TV_SHIELD:
+ prob = match_theme.combat;
+ break;
+ case TV_SOFT_ARMOR:
+ prob = match_theme.combat;
+ break;
+ case TV_HARD_ARMOR:
+ prob = match_theme.combat;
+ break;
+
+ case TV_MSTAFF:
+ prob = match_theme.magic;
+ break;
+ case TV_STAFF:
+ prob = match_theme.magic;
+ break;
+ case TV_WAND:
+ prob = match_theme.magic;
+ break;
+ case TV_ROD:
+ prob = match_theme.magic;
+ break;
+ case TV_ROD_MAIN:
+ prob = match_theme.magic;
+ break;
+ case TV_SCROLL:
+ prob = match_theme.magic;
+ break;
+ case TV_PARCHMENT:
+ prob = match_theme.magic;
+ break;
+ case TV_POTION:
+ prob = match_theme.magic;
+ break;
+ case TV_POTION2:
+ prob = match_theme.magic;
+ break;
+ case TV_BATERIE:
+ prob = match_theme.magic;
+ break;
+ case TV_RANDART:
+ prob = match_theme.magic;
+ break;
+ case TV_RUNE1:
+ prob = match_theme.magic;
+ break;
+ case TV_RUNE2:
+ prob = match_theme.magic;
+ break;
+ case TV_BOOK:
+ prob = match_theme.magic;
+ break;
+ case TV_SYMBIOTIC_BOOK:
+ prob = match_theme.magic;
+ break;
+ case TV_MUSIC_BOOK:
+ prob = match_theme.magic;
+ break;
+ case TV_DRUID_BOOK:
+ prob = match_theme.magic;
+ break;
+ case TV_DAEMON_BOOK:
+ prob = match_theme.magic;
+ break;
+
+ case TV_LITE:
+ prob = match_theme.tools;
+ break;
+ case TV_CLOAK:
+ prob = match_theme.tools;
+ break;
+ case TV_BOOTS:
+ prob = match_theme.tools;
+ break;
+ case TV_SPIKE:
+ prob = match_theme.tools;
+ break;
+ case TV_DIGGING:
+ prob = match_theme.tools;
+ break;
+ case TV_FLASK:
+ prob = match_theme.tools;
+ break;
+ case TV_FOOD:
+ prob = match_theme.tools;
+ break;
+ case TV_TOOL:
+ prob = match_theme.tools;
+ break;
+ case TV_INSTRUMENT:
+ prob = match_theme.tools;
+ break;
+ case TV_TRAPKIT:
+ prob = match_theme.tools;
+ break;
+ }
+
+ /* Roll to see if it can be made */
+ if (rand_int(100) < prob) return (TRUE);
+
+ /* Not a match */
+ return (FALSE);
+}
+
+/*
+ * Determine if an object must not be generated.
+ */
+int kind_is_legal_special = -1;
+bool_ kind_is_legal(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (!kind_is_theme(k_idx)) return FALSE;
+
+ if (k_ptr->flags4 & TR4_SPECIAL_GENE)
+ {
+ if (k_allow_special[k_idx]) return TRUE;
+ else return FALSE;
+ }
+
+ /* No 2 times the same normal artifact */
+ if ((k_ptr->flags3 & TR3_NORM_ART) && (k_ptr->artifact))
+ {
+ return FALSE;
+ }
+
+ if (k_ptr->tval == TV_CORPSE)
+ {
+ if (k_ptr->sval != SV_CORPSE_SKULL && k_ptr->sval != SV_CORPSE_SKELETON &&
+ k_ptr->sval != SV_CORPSE_HEAD && k_ptr->sval != SV_CORPSE_CORPSE)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ if (k_ptr->tval == TV_HYPNOS) return FALSE;
+
+ /* Used only for the Nazgul rings */
+ if ((k_ptr->tval == TV_RING) && (k_ptr->sval == SV_RING_SPECIAL)) return FALSE;
+
+ /* Are we forced to one tval ? */
+ if ((kind_is_legal_special != -1) && (kind_is_legal_special != k_ptr->tval)) return (FALSE);
+
+ /* Assume legal */
+ return TRUE;
+}
+
+
+/*
+ * Hack -- determine if a template is "good"
+ */
+bool_ kind_is_good(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (!kind_is_legal(k_idx)) return FALSE;
+
+ /* Analyze the item type */
+ switch (k_ptr->tval)
+ {
+ /* Armor -- Good unless damaged */
+ case TV_HARD_ARMOR:
+ case TV_SOFT_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ {
+ if (k_ptr->to_a < 0) return (FALSE);
+ return (TRUE);
+ }
+
+ /* Weapons -- Good unless damaged */
+ case TV_BOW:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_DIGGING:
+ case TV_TRAPKIT:
+ case TV_MSTAFF:
+ case TV_BOOMERANG:
+ {
+ if (k_ptr->to_h < 0) return (FALSE);
+ if (k_ptr->to_d < 0) return (FALSE);
+ return (TRUE);
+ }
+
+ /* Ammo -- Arrows/Bolts are good */
+ case TV_BOLT:
+ case TV_ARROW:
+ {
+ return (TRUE);
+ }
+
+ /* Rods - Silver and better are good */
+ case TV_ROD_MAIN:
+ {
+ if (k_ptr->sval >= SV_ROD_SILVER) return (TRUE);
+ return FALSE;
+ }
+
+ /* Expensive rod tips are good */
+ case TV_ROD:
+ {
+ /* Probing is not good, but Recall is*/
+ if (k_ptr->cost >= 4500) return TRUE;
+ return FALSE;
+ }
+
+ /* The Tomes are good */
+ case TV_BOOK:
+ {
+ if (k_ptr->sval <= SV_BOOK_MAX_GOOD) return (TRUE);
+ return FALSE;
+ }
+
+ /* Rings -- Rings of Speed are good */
+ case TV_RING:
+ {
+ if (k_ptr->sval == SV_RING_SPEED) return (TRUE);
+ return (FALSE);
+ }
+
+ /* Amulets -- Some are good */
+ case TV_AMULET:
+ {
+ if (k_ptr->sval == SV_AMULET_THE_MAGI) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_DEVOTION) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_WEAPONMASTERY) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_TRICKERY) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_RESISTANCE) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_REFLECTION) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_TELEPATHY) return (TRUE);
+ return (FALSE);
+ }
+ }
+
+ /* Assume not good */
+ return (FALSE);
+}
+
+/*
+* Determine if template is suitable for building a randart -- dsb
+*/
+bool_ kind_is_artifactable(int k_idx)
+{
+ int i, j;
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (kind_is_good(k_idx))
+ {
+ /* We consider the item artifactable if there is at least one
+ * randart power in ra_info that could be added to this item. */
+ for (i = 0; i < max_ra_idx; i++)
+ {
+ randart_part_type *ra_ptr = &ra_info[i];
+
+ for (j = 0; j < 20; j++)
+ {
+ if (ra_ptr->tval[j] != k_ptr->tval) continue;
+ if (ra_ptr->min_sval[j] > k_ptr->sval) continue;
+ if (ra_ptr->max_sval[j] < k_ptr->sval) continue;
+ /* Winner */
+ return TRUE;
+ }
+ }
+ }
+
+ /* No match. Too bad. */
+ return FALSE;
+}
+
+
+/*
+ * Attempt to make an object (normal or good/great)
+ *
+ * This routine plays nasty games to generate the "special artifacts".
+ *
+ * This routine uses "object_level" for the "generation level".
+ *
+ * We assume that the given object has been "wiped".
+ *
+ * To Watch: The allocation table caching is heavily relies on
+ * an assumption that the SPECIAL_GENE objects should only be created
+ * through the forge--object_prep()--apply_magic() sequence and
+ * get_obj_num() should never be called for that purpose XXX XXX XXX
+ */
+bool_ make_object(object_type *j_ptr, bool_ good, bool_ great, obj_theme theme)
+{
+ int invprob, base;
+
+
+ /* Chance of "special object" */
+ invprob = (good ? 10 - luck( -9, 9) : 1000);
+
+ /* Base level for the object */
+ base = (good ? (object_level + 10) : object_level);
+
+
+ /* Generate a special object, or a normal object */
+ if ((rand_int(invprob) != 0) || !make_artifact_special(j_ptr))
+ {
+ int k_idx;
+
+ /* See if the theme has been changed XXX XXX XXX */
+ if (theme_changed(theme))
+ {
+ /* Select items based on "theme" */
+ init_match_theme(theme);
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+ }
+
+ /* Good objects */
+ if (good)
+ {
+ /* Activate restriction */
+ get_obj_num_hook = kind_is_good;
+
+ /* Prepare allocation table */
+ get_obj_num_prep();
+ }
+
+ /* Normal objects -- only when the cache is invalidated */
+ else if (!alloc_kind_table_valid)
+ {
+ /* Activate normal restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Prepare allocation table */
+ get_obj_num_prep();
+
+ /* The table is synchronised */
+ alloc_kind_table_valid = TRUE;
+ }
+
+ /* Pick a random object */
+ k_idx = get_obj_num(base);
+
+ /* Good objects */
+ if (good)
+ {
+ /* Restore normal restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Prepare allocation table */
+ get_obj_num_prep();
+
+ /* The table is synchronised */
+ alloc_kind_table_valid = TRUE;
+ }
+
+ /* Handle failure */
+ if (!k_idx) return (FALSE);
+
+ /* Prepare the object */
+ object_prep(j_ptr, k_idx);
+ }
+
+ /* Apply magic (allow artifacts) */
+ apply_magic(j_ptr, object_level, TRUE, good, great);
+
+ /* Hack -- generate multiple spikes/missiles */
+ switch (j_ptr->tval)
+ {
+ case TV_SPIKE:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ {
+ j_ptr->number = (byte)damroll(6, 7);
+ }
+ }
+
+ /* hack, no multiple artifacts */
+ if (artifact_p(j_ptr)) j_ptr->number = 1;
+
+ /* Notice "okay" out-of-depth objects */
+ if (!cursed_p(j_ptr) &&
+ (k_info[j_ptr->k_idx].level > dun_level))
+ {
+ /* Rating increase */
+ rating += (k_info[j_ptr->k_idx].level - dun_level);
+
+ /* Cheat -- peek at items */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(j_ptr);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Attempt to place an object (normal or good/great) at the given location.
+ *
+ * This routine plays nasty games to generate the "special artifacts".
+ *
+ * This routine uses "object_level" for the "generation level".
+ *
+ * This routine requires a clean floor grid destination.
+ */
+void place_object(int y, int x, bool_ good, bool_ great, int where)
+{
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ object_type forge;
+ object_type *q_ptr;
+
+
+ /* Paranoia -- check bounds */
+ if (!in_bounds(y, x)) return;
+
+ /* Require clean floor space */
+ if (!cave_clean_bold(y, x)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Make an object (if possible) */
+ if (!make_object(q_ptr, good, great, d_info[dungeon_type].objs)) return;
+
+ if (where == OBJ_FOUND_VAULT)
+ {
+ q_ptr->found = OBJ_FOUND_VAULT;
+ q_ptr->found_aux1 = dungeon_type;
+ q_ptr->found_aux2 = level_or_feat(dungeon_type, dun_level);
+ }
+ else if (where == OBJ_FOUND_FLOOR)
+ {
+ q_ptr->found = OBJ_FOUND_FLOOR;
+ q_ptr->found_aux1 = dungeon_type;
+ q_ptr->found_aux2 = level_or_feat(dungeon_type, dun_level);
+ }
+ else if (where == OBJ_FOUND_SPECIAL)
+ {
+ q_ptr->found = OBJ_FOUND_SPECIAL;
+ }
+ else if (where == OBJ_FOUND_RUBBLE)
+ {
+ q_ptr->found = OBJ_FOUND_RUBBLE;
+ }
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure Copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Location */
+ o_ptr->iy = y;
+ o_ptr->ix = x;
+
+ /* Acquire grid */
+ c_ptr = &cave[y][x];
+
+ /* Build a stack */
+ o_ptr->next_o_idx = c_ptr->o_idx;
+
+ /* Place the object */
+ c_ptr->o_idx = o_idx;
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+
+ /* Object array overflow */
+ else
+ {
+ /* Hack -- Preserve artifacts */
+ if (q_ptr->name1)
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ else if (k_info[q_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[q_ptr->k_idx].artifact = 0;
+ }
+ else if (q_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[q_ptr->sval].generated = FALSE;
+ }
+ }
+}
+
+
+/*
+ * XXX XXX XXX Do not use these hard-coded values.
+ */
+#define OBJ_GOLD_LIST 480 /* First "gold" entry */
+#define MAX_GOLD 18 /* Number of "gold" entries */
+
+/*
+ * Make a treasure object
+ *
+ * The location must be a legal, clean, floor grid.
+ */
+bool_ make_gold(object_type *j_ptr)
+{
+ int i;
+
+ s32b base;
+
+
+ /* Hack -- Pick a Treasure variety */
+ i = ((randint(object_level + 2) + 2) / 2) - 1;
+
+ /* Apply "extra" magic */
+ if (rand_int(GREAT_OBJ) == 0)
+ {
+ i += randint(object_level + 1);
+ }
+
+ /* Hack -- Creeping Coins only generate "themselves" */
+ if (coin_type) i = coin_type;
+
+ /* Do not create "illegal" Treasure Types */
+ if (i >= MAX_GOLD) i = MAX_GOLD - 1;
+
+ /* Prepare a gold object */
+ object_prep(j_ptr, OBJ_GOLD_LIST + i);
+
+ /* Hack -- Base coin cost */
+ base = k_info[OBJ_GOLD_LIST + i].cost;
+
+ /* Determine how much the treasure is "worth" */
+ j_ptr->pval = (base + (8L * randint(base)) + randint(8));
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Places a treasure (Gold or Gems) at given location
+ *
+ * The location must be a legal, clean, floor grid.
+ */
+void place_gold(int y, int x)
+{
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ object_type forge;
+ object_type *q_ptr;
+
+
+ /* Paranoia -- check bounds */
+ if (!in_bounds(y, x)) return;
+
+ /* Require clean floor space */
+ if (!cave_clean_bold(y, x)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Make some gold */
+ if (!make_gold(q_ptr)) return;
+
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[o_idx];
+
+ /* Copy the object */
+ object_copy(o_ptr, q_ptr);
+
+ /* Save location */
+ o_ptr->iy = y;
+ o_ptr->ix = x;
+
+ /* Acquire grid */
+ c_ptr = &cave[y][x];
+
+ /* Build a stack */
+ o_ptr->next_o_idx = c_ptr->o_idx;
+
+ /* Place the object */
+ c_ptr->o_idx = o_idx;
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+}
+
+
+/*
+ * Let an object fall to the ground at or near a location.
+ *
+ * The initial location is assumed to be "in_bounds()".
+ *
+ * This function takes a parameter "chance". This is the percentage
+ * chance that the item will "disappear" instead of drop. If the object
+ * has been thrown, then this is the chance of disappearance on contact.
+ *
+ * Hack -- this function uses "chance" to determine if it should produce
+ * some form of "description" of the drop event (under the player).
+ *
+ * We check several locations to see if we can find a location at which
+ * the object can combine, stack, or be placed. Artifacts will try very
+ * hard to be placed, including "teleporting" to a useful grid if needed.
+ */
+s16b drop_near(object_type *j_ptr, int chance, int y, int x)
+{
+ int i, k, d, s;
+
+ int bs, bn;
+ int by, bx;
+ int dy, dx;
+ int ty, tx;
+
+ s16b o_idx = 0;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ cave_type *c_ptr;
+
+ char o_name[80];
+
+ bool_ flag = FALSE;
+ bool_ done = FALSE;
+
+ bool_ plural = FALSE;
+
+
+ /* Extract plural */
+ if (j_ptr->number != 1) plural = TRUE;
+
+ /* Describe object */
+ object_desc(o_name, j_ptr, FALSE, 0);
+
+
+ /* Handle normal "breakage" */
+ if (!(j_ptr->art_name || artifact_p(j_ptr)) && (rand_int(100) < chance))
+ {
+ /* Message */
+ msg_format("The %s disappear%s.",
+ o_name, (plural ? "" : "s"));
+
+ /* Debug */
+ if (wizard) msg_print("(breakage)");
+
+ /* Failure */
+ return (0);
+ }
+
+
+ /* Score */
+ bs = -1;
+
+ /* Picker */
+ bn = 0;
+
+ /* Default */
+ by = y;
+ bx = x;
+
+ /* Scan local grids */
+ for (dy = -3; dy <= 3; dy++)
+ {
+ /* Scan local grids */
+ for (dx = -3; dx <= 3; dx++)
+ {
+ bool_ comb = FALSE;
+
+ /* Calculate actual distance */
+ d = (dy * dy) + (dx * dx);
+
+ /* Ignore distant grids */
+ if (d > 10) continue;
+
+ /* Location */
+ ty = y + dy;
+ tx = x + dx;
+
+ /* Skip illegal grids */
+ if (!in_bounds(ty, tx)) continue;
+
+ /* Require line of sight */
+ if (!los(y, x, ty, tx)) continue;
+
+ /* Obtain grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Require floor space (or shallow terrain) -KMW- */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_FLOOR)) continue;
+
+ /* No traps */
+ if (c_ptr->t_idx) continue;
+
+ /* No objects */
+ k = 0;
+
+ /* Scan objects in that grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Check for possible combination */
+ if (object_similar(o_ptr, j_ptr)) comb = TRUE;
+
+ /* Count objects */
+ k++;
+ }
+
+ /* Add new object */
+ if (!comb) k++;
+
+ /* No stacking (allow combining) */
+ if (!testing_stack && (k > 1)) continue;
+
+ /* Paranoia */
+ if (k > 23) continue;
+
+ /* Calculate score */
+ s = 1000 - (d + k * 5);
+
+ /* Skip bad values */
+ if (s < bs) continue;
+
+ /* New best value */
+ if (s > bs) bn = 0;
+
+ /* Apply the randomizer to equivalent values */
+ if ((++bn >= 2) && (rand_int(bn) != 0)) continue;
+
+ /* Keep score */
+ bs = s;
+
+ /* Track it */
+ by = ty;
+ bx = tx;
+
+ /* Okay */
+ flag = TRUE;
+ }
+ }
+
+
+ /* Handle lack of space */
+ if (!flag && !(artifact_p(j_ptr) || j_ptr->art_name))
+ {
+ /* Message */
+ msg_format("The %s disappear%s.",
+ o_name, (plural ? "" : "s"));
+
+ /* Debug */
+ if (wizard) msg_print("(no floor space)");
+
+ /* Failure */
+ return (0);
+ }
+
+
+ /* Find a grid */
+ for (i = 0; !flag; i++)
+ {
+ /* Bounce around */
+ if (i < 1000)
+ {
+ ty = rand_spread(by, 1);
+ tx = rand_spread(bx, 1);
+ }
+
+ /* Random locations */
+ else
+ {
+ ty = rand_int(cur_hgt);
+ tx = rand_int(cur_wid);
+ }
+
+ /* Grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Require floor space (or shallow terrain) -KMW- */
+ if ((c_ptr->feat != FEAT_FLOOR) &&
+ (c_ptr->feat != FEAT_SHAL_WATER) &&
+ (c_ptr->feat != FEAT_GRASS) &&
+ (c_ptr->feat != FEAT_DIRT) &&
+ (c_ptr->feat != FEAT_SHAL_LAVA)) continue;
+
+ /* Bounce to that location */
+ by = ty;
+ bx = tx;
+
+ /* Require floor space */
+ if (!cave_clean_bold(by, bx)) continue;
+
+ /* Okay */
+ flag = TRUE;
+ }
+
+
+ /* Grid */
+ c_ptr = &cave[by][bx];
+
+ /* Scan objects in that grid for combination */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Check for combination */
+ if (object_similar(o_ptr, j_ptr))
+ {
+ /* Combine the items */
+ object_absorb(o_ptr, j_ptr);
+
+ /* Success */
+ done = TRUE;
+
+ /* Done */
+ break;
+ }
+ }
+
+ /* Get new object */
+ if (!done) o_idx = o_pop();
+
+ /* Failure */
+ if (!done && !o_idx)
+ {
+ /* Message */
+ msg_format("The %s disappear%s.",
+ o_name, (plural ? "" : "s"));
+
+ /* Debug */
+ if (wizard) msg_print("(too many objects)");
+
+ /* Hack -- Preserve artifacts */
+ if (j_ptr->name1)
+ {
+ a_info[j_ptr->name1].cur_num = 0;
+ }
+ else if (k_info[j_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[j_ptr->k_idx].artifact = 0;
+ }
+ else if (j_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[j_ptr->sval].generated = FALSE;
+ }
+
+ /* Failure */
+ return (0);
+ }
+
+ /* Stack */
+ if (!done)
+ {
+ /* Structure copy */
+ object_copy(&o_list[o_idx], j_ptr);
+
+ /* Access new object */
+ j_ptr = &o_list[o_idx];
+
+ /* Locate */
+ j_ptr->iy = by;
+ j_ptr->ix = bx;
+
+ /* No monster */
+ j_ptr->held_m_idx = 0;
+
+ /* Build a stack */
+ j_ptr->next_o_idx = c_ptr->o_idx;
+
+ /* Place the object */
+ c_ptr->o_idx = o_idx;
+
+ /* Success */
+ done = TRUE;
+ }
+
+ /* Note the spot */
+ note_spot(by, bx);
+
+ /* Draw the spot */
+ lite_spot(by, bx);
+
+ /* Mega-Hack -- no message if "dropped" by player */
+ /* Message when an object falls under the player */
+ if (chance && (by == p_ptr->py) && (bx == p_ptr->px))
+ {
+ msg_print("You feel something roll beneath your feet.");
+ /* Sound */
+ sound(SOUND_DROP);
+ }
+
+ /* XXX XXX XXX */
+
+ /* Result */
+ return (o_idx);
+}
+
+
+
+
+/*
+ * Scatter some "great" objects near the player
+ */
+void acquirement(int y1, int x1, int num, bool_ great, bool_ known)
+{
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ /* Acquirement */
+ while (num--)
+ {
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Wipe the object */
+ object_wipe(i_ptr);
+
+ /* Make a good (or great) object (if possible) */
+ if (!make_object(i_ptr, TRUE, great, d_info[dungeon_type].objs)) continue;
+
+ if (known)
+ {
+ object_aware(i_ptr);
+ object_known(i_ptr);
+ }
+
+ /* Drop the object */
+ drop_near(i_ptr, -1, y1, x1);
+ }
+}
+
+
+
+/*
+ * Hack -- instantiate a trap
+ *
+ * XXX XXX XXX This routine should be redone to reflect trap "level".
+ * That is, it does not make sense to have spiked pits at 50 feet.
+ * Actually, it is not this routine, but the "trap instantiation"
+ * code, which should also check for "trap doors" on quest levels.
+ */
+void pick_trap(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Paranoia */
+ if ((c_ptr->t_idx == 0) || (c_ptr->info & CAVE_TRDT)) return;
+
+ /* Activate the trap */
+ c_ptr->info |= (CAVE_TRDT);
+
+ /* Notice and redraw */
+ note_spot(y, x);
+ lite_spot(y, x);
+}
+
+/*
+ * Describe the charges on an item in the inventory.
+ */
+void inven_item_charges(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Require staff/wand */
+ if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return;
+
+ /* Require known item */
+ if (!object_known_p(o_ptr)) return;
+
+ /* Multiple charges */
+ if (o_ptr->pval != 1)
+ {
+ /* Print a message */
+ msg_format("You have %d charges remaining.", o_ptr->pval);
+ }
+
+ /* Single charge */
+ else
+ {
+ /* Print a message */
+ msg_format("You have %d charge remaining.", o_ptr->pval);
+ }
+}
+
+
+/*
+ * Describe an item in the inventory.
+ */
+void inven_item_describe(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+ char o_name[80];
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Print a message */
+ msg_format("You have %s.", o_name);
+}
+
+
+/*
+ * Increase the "number" of an item in the inventory
+ */
+void inven_item_increase(int item, int num)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Apply */
+ num += o_ptr->number;
+
+ /* Bounds check */
+ if (num > 255) num = 255;
+ else if (num < 0) num = 0;
+
+ /* Un-apply */
+ num -= o_ptr->number;
+
+ /* Change the number and weight */
+ if (num)
+ {
+ /* Add the number */
+ o_ptr->number += num;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana XXX */
+ p_ptr->update |= (PU_MANA);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+}
+
+
+/*
+ * Erase an inventory slot if it has no more items
+ */
+bool_ inven_item_optimize(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Only optimize real items */
+ if (!o_ptr->k_idx) return (FALSE);
+
+ /* Only optimize empty items */
+ if (o_ptr->number) return (FALSE);
+
+ /* The item is in the pack */
+ if (item < INVEN_WIELD)
+ {
+ int i;
+
+ /* One less item */
+ inven_cnt--;
+
+ /* Slide everything down */
+ for (i = item; i < INVEN_PACK; i++)
+ {
+ /* Structure copy */
+ p_ptr->inventory[i] = p_ptr->inventory[i + 1];
+ }
+
+ /* Erase the "final" slot */
+ object_wipe(&p_ptr->inventory[i]);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /* The item is being wielded */
+ else
+ {
+ /* One less item */
+ equip_cnt--;
+
+ /* Take care of item sets*/
+ if (o_ptr->name1)
+ {
+ takeoff_set(p_ptr->inventory[item].name1, a_info[p_ptr->inventory[item].name1].set);
+ }
+
+ /* Erase the empty slot */
+ object_wipe(&p_ptr->inventory[item]);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Recalculate mana XXX */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Describe the charges on an item on the floor.
+ */
+void floor_item_charges(int item)
+{
+ object_type *o_ptr = &o_list[item];
+
+ /* Require staff/wand */
+ if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return;
+
+ /* Require known item */
+ if (!object_known_p(o_ptr)) return;
+
+ /* Multiple charges */
+ if (o_ptr->pval != 1)
+ {
+ /* Print a message */
+ msg_format("There are %d charges remaining.", o_ptr->pval);
+ }
+
+ /* Single charge */
+ else
+ {
+ /* Print a message */
+ msg_format("There is %d charge remaining.", o_ptr->pval);
+ }
+}
+
+
+
+/*
+ * Describe an item in the inventory.
+ */
+void floor_item_describe(int item)
+{
+ object_type *o_ptr = &o_list[item];
+ char o_name[80];
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Print a message */
+ msg_format("You see %s.", o_name);
+}
+
+
+/*
+ * Increase the "number" of an item on the floor
+ */
+void floor_item_increase(int item, int num)
+{
+ object_type *o_ptr = &o_list[item];
+
+ /* Apply */
+ num += o_ptr->number;
+
+ /* Bounds check */
+ if (num > 255) num = 255;
+ else if (num < 0) num = 0;
+
+ /* Un-apply */
+ num -= o_ptr->number;
+
+ /* Change the number */
+ o_ptr->number += num;
+}
+
+
+/*
+ * Optimize an item on the floor (destroy "empty" items)
+ */
+void floor_item_optimize(int item)
+{
+ object_type *o_ptr = &o_list[item];
+
+ /* Paranoia -- be sure it exists */
+ if (!o_ptr->k_idx) return;
+
+ /* Only optimize empty items */
+ if (o_ptr->number) return;
+
+ /* Delete the object */
+ delete_object_idx(item);
+}
+
+
+/*
+ * Increase stack size for item, describe and optimize.
+ */
+void inc_stack_size(int item, int delta) {
+ inc_stack_size_ex(item, delta, OPTIMIZE, DESCRIBE);
+}
+
+/*
+ * Increase stack size for item.
+ */
+void inc_stack_size_ex(int item, int delta, optimize_flag opt, describe_flag desc) {
+ /* Pack item? */
+ if (item >= 0)
+ {
+ inven_item_increase(item, delta);
+ if (desc == DESCRIBE)
+ {
+ inven_item_describe(item);
+ }
+ if (opt == OPTIMIZE)
+ {
+ inven_item_optimize(item);
+ }
+ }
+
+ /* Floor item? */
+ else
+ {
+ floor_item_increase(0 - item, delta);
+ if (desc == DESCRIBE)
+ {
+ floor_item_describe(0 - item);
+ }
+ if (opt == OPTIMIZE)
+ {
+ floor_item_optimize(0 - item);
+ }
+ }
+}
+
+
+
+/*
+ * Check if we have space for an item in the pack without overflow
+ */
+bool_ inven_carry_okay(object_type *o_ptr)
+{
+ int j;
+
+ if (o_ptr->tval == TV_GOLD) return FALSE;
+
+ /* Empty slot? */
+ if (inven_cnt < INVEN_PACK) return (TRUE);
+
+ /* Similar slot? */
+ for (j = 0; j < INVEN_PACK; j++)
+ {
+ object_type *j_ptr = &p_ptr->inventory[j];
+
+ /* Skip non-objects */
+ if (!j_ptr->k_idx) continue;
+
+ /* Check if the two items can be combined */
+ if (object_similar(j_ptr, o_ptr)) return (TRUE);
+ }
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+/*
+ * Add an item to the players inventory, and return the slot used.
+ *
+ * If the new item can combine with an existing item in the inventory,
+ * it will do so, using "object_similar()" and "object_absorb()", otherwise,
+ * the item will be placed into the "proper" location in the inventory.
+ *
+ * This function can be used to "over-fill" the player's pack, but only
+ * once, and such an action must trigger the "overflow" code immediately.
+ * Note that when the pack is being "over-filled", the new item must be
+ * placed into the "overflow" slot, and the "overflow" must take place
+ * before the pack is reordered, but (optionally) after the pack is
+ * combined. This may be tricky. See "dungeon.c" for info.
+ *
+ * Note that this code must remove any location/stack information
+ * from the object once it is placed into the inventory.
+ *
+ * The "final" flag tells this function to bypass the "combine"
+ * and "reorder" code until later.
+ */
+s16b inven_carry(object_type *o_ptr, bool_ final)
+{
+ int i, j, k;
+ int n = -1;
+ object_type *j_ptr;
+
+ /* Not final */
+ if (!final)
+ {
+ /* Check for combining */
+ for (j = 0; j < INVEN_PACK; j++)
+ {
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Skip non-objects */
+ if (!j_ptr->k_idx) continue;
+
+ /* Hack -- track last item */
+ n = j;
+
+ /* Check if the two items can be combined */
+ if (object_similar(j_ptr, o_ptr))
+ {
+ /* Combine the items */
+ object_absorb(j_ptr, o_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Success */
+ return (j);
+ }
+ }
+ }
+
+
+ /* Paranoia */
+ if (inven_cnt > INVEN_PACK) return ( -1);
+
+
+ /* Find an empty slot */
+ for (j = 0; j <= INVEN_PACK; j++)
+ {
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Use it if found */
+ if (!j_ptr->k_idx) break;
+ }
+
+ /* Use that slot */
+ i = j;
+
+
+ /* Hack -- pre-reorder the pack */
+ if (!final && (i < INVEN_PACK))
+ {
+ s32b o_value, j_value;
+
+ /* Get the "value" of the item */
+ o_value = object_value(o_ptr);
+
+ /* Scan every occupied slot */
+ for (j = 0; j < INVEN_PACK; j++)
+ {
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Use empty slots */
+ if (!j_ptr->k_idx) break;
+
+ /* Objects sort by decreasing type */
+ if (o_ptr->tval > j_ptr->tval) break;
+ if (o_ptr->tval < j_ptr->tval) continue;
+
+ /* Non-aware (flavored) items always come last */
+ if (!object_aware_p(o_ptr)) continue;
+ if (!object_aware_p(j_ptr)) break;
+
+ /* Objects sort by increasing sval */
+ if (o_ptr->sval < j_ptr->sval) break;
+ if (o_ptr->sval > j_ptr->sval) continue;
+
+ /* Unidentified objects always come last */
+ if (!object_known_p(o_ptr)) continue;
+ if (!object_known_p(j_ptr)) break;
+
+ /* Hack: otherwise identical rods sort by
+ increasing recharge time --dsb */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (o_ptr->timeout > j_ptr->timeout) break;
+ if (o_ptr->timeout < j_ptr->timeout) continue;
+ }
+
+ /* Determine the "value" of the pack item */
+ j_value = object_value(j_ptr);
+
+ /* Objects sort by decreasing value */
+ if (o_value > j_value) break;
+ if (o_value < j_value) continue;
+ }
+
+ /* Use that slot */
+ i = j;
+
+ /* Slide objects */
+ for (k = n; k >= i; k--)
+ {
+ /* Hack -- Slide the item */
+ object_copy(&p_ptr->inventory[k + 1], &p_ptr->inventory[k]);
+ }
+
+ /* Wipe the empty slot */
+ object_wipe(&p_ptr->inventory[i]);
+ }
+
+
+ /* Acquire a copy of the item */
+ object_copy(&p_ptr->inventory[i], o_ptr);
+
+ /* Access new object */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Clean out unused fields */
+ o_ptr->iy = o_ptr->ix = 0;
+ o_ptr->next_o_idx = 0;
+ o_ptr->held_m_idx = 0;
+
+ /* Count the items */
+ inven_cnt++;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Return the slot */
+ return (i);
+}
+
+
+
+/*
+ * Take off (some of) a non-cursed equipment item
+ *
+ * Note that only one item at a time can be wielded per slot.
+ *
+ * Note that taking off an item when "full" may cause that item
+ * to fall to the ground.
+ *
+ * Return the inventory slot into which the item is placed.
+ */
+s16b inven_takeoff(int item, int amt, bool_ force_drop)
+{
+ int slot;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ cptr act;
+
+ char o_name[80];
+
+
+ /* Get the item to take off */
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Paranoia */
+ if (amt <= 0) return ( -1);
+
+ /* Verify */
+ if (amt > o_ptr->number) amt = o_ptr->number;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /* Describe the object */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Took off weapon */
+ if (item == INVEN_WIELD)
+ {
+ act = "You were wielding";
+ }
+
+ /* Took off bow */
+ else if (item == INVEN_BOW)
+ {
+ act = "You were holding";
+ }
+
+ /* Took off light */
+ else if (item == INVEN_LITE)
+ {
+ act = "You were holding";
+ }
+
+ /* Took off ammo */
+ else if (item == INVEN_AMMO)
+ {
+ act = "You were carrying in your quiver";
+ }
+
+ /* Took off tool */
+ else if (item == INVEN_TOOL)
+ {
+ act = "You were using";
+ }
+
+ /* Took off something */
+ else
+ {
+ act = "You were wearing";
+ }
+
+ /* Modify, Optimize */
+ inc_stack_size_ex(item, -amt, OPTIMIZE, NO_DESCRIBE);
+
+ if ((item == INVEN_CARRY) && (get_skill(SKILL_SYMBIOTIC)))
+ {
+ /* Drop the monster */
+ o_ptr->pval2 = 0;
+ msg_print("You carefully drop the poor monster on the floor.");
+ drop_near(q_ptr, 0, p_ptr->py, p_ptr->px);
+ slot = -1;
+ }
+ else if (force_drop)
+ {
+ drop_near(q_ptr, 0, p_ptr->py, p_ptr->px);
+ slot = -1;
+ }
+ else
+ {
+ /* Carry the object */
+ slot = inven_carry(q_ptr, FALSE);
+ }
+
+ /* Message */
+ msg_format("%s %s (%c).", act, o_name, index_to_label(slot));
+
+ /* Return slot */
+ return (slot);
+}
+
+
+
+
+/*
+ * Drop (some of) a non-cursed inventory/equipment item
+ *
+ * The object will be dropped "near" the current location
+ */
+void inven_drop(int item, int amt, int dy, int dx, bool_ silent)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Access original object */
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Error check */
+ if (amt <= 0) return;
+
+ /* Not too many */
+ if (amt > o_ptr->number) amt = o_ptr->number;
+
+
+ /* Take off equipment */
+ if (item >= INVEN_WIELD)
+ {
+ /* Take off first */
+ item = inven_takeoff(item, amt, FALSE);
+
+ /* Access original object */
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ if (item > -1)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain local object */
+ object_copy(q_ptr, o_ptr);
+
+ /*
+ * Hack -- If rods or wands are dropped, the total maximum timeout or
+ * charges need to be allocated between the two stacks. If all the items
+ * are being dropped, it makes for a neater message to leave the original
+ * stack's pval alone. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ if (amt < o_ptr->number) o_ptr->pval -= q_ptr->pval;
+ }
+ }
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /* Describe local object */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Message */
+ if (!silent) msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Drop it near the player */
+ drop_near(q_ptr, 0, dy, dx);
+
+ /* Modify, Describe, Optimize */
+ inc_stack_size(item, -amt);
+ }
+}
+
+
+
+/*
+ * Combine items in the pack
+ *
+ * Note special handling of the "overflow" slot
+ */
+void combine_pack(void)
+{
+ int i, j, k;
+ object_type *o_ptr;
+ object_type *j_ptr;
+ bool_ flag = FALSE;
+
+
+ /* Combine the pack (backwards) */
+ for (i = INVEN_PACK; i > 0; i--)
+ {
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty items */
+ if (!o_ptr->k_idx) continue;
+
+ /* Scan the items above that item */
+ for (j = 0; j < i; j++)
+ {
+ /* Get the item */
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Skip empty items */
+ if (!j_ptr->k_idx) continue;
+
+ /* Can we drop "o_ptr" onto "j_ptr"? */
+ if (object_similar(j_ptr, o_ptr))
+ {
+ /* Take note */
+ flag = TRUE;
+
+ /* Add together the item counts */
+ object_absorb(j_ptr, o_ptr);
+
+ /* One object is gone */
+ inven_cnt--;
+
+ /* Slide everything down */
+ for (k = i; k < INVEN_PACK; k++)
+ {
+ /* Structure copy */
+ p_ptr->inventory[k] = p_ptr->inventory[k + 1];
+ }
+
+ /* Erase the "final" slot */
+ object_wipe(&p_ptr->inventory[k]);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+ /* Message */
+ if (flag) msg_print("You combine some items in your pack.");
+}
+
+
+/*
+ * Reorder items in the pack
+ *
+ * Note special handling of the "overflow" slot
+ */
+void reorder_pack(void)
+{
+ int i, j, k;
+ s32b o_value;
+ s32b j_value;
+ object_type forge;
+ object_type *q_ptr;
+ object_type *j_ptr;
+ object_type *o_ptr;
+ bool_ flag = FALSE;
+
+
+ /* Re-order the pack (forwards) */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ /* Mega-Hack -- allow "proper" over-flow */
+ if ((i == INVEN_PACK) && (inven_cnt == INVEN_PACK)) break;
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty slots */
+ if (!o_ptr->k_idx) continue;
+
+ /* Get the "value" of the item */
+ o_value = object_value(o_ptr);
+
+ /* Scan every occupied slot */
+ for (j = 0; j < INVEN_PACK; j++)
+ {
+ /* Get the item already there */
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Use empty slots */
+ if (!j_ptr->k_idx) break;
+
+ /* Objects sort by decreasing type */
+ if (o_ptr->tval > j_ptr->tval) break;
+ if (o_ptr->tval < j_ptr->tval) continue;
+
+ /* Non-aware (flavored) items always come last */
+ if (!object_aware_p(o_ptr)) continue;
+ if (!object_aware_p(j_ptr)) break;
+
+ /* Objects sort by increasing sval */
+ if (o_ptr->sval < j_ptr->sval) break;
+ if (o_ptr->sval > j_ptr->sval) continue;
+
+ /* Unidentified objects always come last */
+ if (!object_known_p(o_ptr)) continue;
+ if (!object_known_p(j_ptr)) break;
+
+
+ /* Hack: otherwise identical rods sort by
+ increasing recharge time --dsb */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (o_ptr->timeout > j_ptr->timeout) break;
+ if (o_ptr->timeout < j_ptr->timeout) continue;
+ }
+
+ /* Determine the "value" of the pack item */
+ j_value = object_value(j_ptr);
+
+
+
+ /* Objects sort by decreasing value */
+ if (o_value > j_value) break;
+ if (o_value < j_value) continue;
+ }
+
+ /* Never move down */
+ if (j >= i) continue;
+
+ /* Take note */
+ flag = TRUE;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Save a copy of the moving item */
+ object_copy(q_ptr, &p_ptr->inventory[i]);
+
+ /* Slide the objects */
+ for (k = i; k > j; k--)
+ {
+ /* Slide the item */
+ object_copy(&p_ptr->inventory[k], &p_ptr->inventory[k - 1]);
+ }
+
+ /* Insert the moving item */
+ object_copy(&p_ptr->inventory[j], q_ptr);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /* Message */
+ if (flag) msg_print("You reorder some items in your pack.");
+}
+
+/*
+ * Hack -- display an object kind in the current window
+ *
+ * Include list of usable spells for readible books
+ */
+void display_koff(int k_idx)
+{
+ int y;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char o_name[80];
+
+
+ /* Erase the window */
+ for (y = 0; y < Term->hgt; y++)
+ {
+ /* Erase the line */
+ Term_erase(0, y, 255);
+ }
+
+ /* No info */
+ if (!k_idx) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Prepare the object */
+ object_wipe(q_ptr);
+
+ /* Prepare the object */
+ object_prep(q_ptr, k_idx);
+
+
+ /* Describe */
+ object_desc_store(o_name, q_ptr, FALSE, 0);
+
+ /* Mention the object name */
+ Term_putstr(0, 0, -1, TERM_WHITE, o_name);
+}
+
+
+/*
+ * Let the floor carry an object
+ */
+s16b floor_carry(int y, int x, object_type *j_ptr)
+{
+ int n = 0;
+
+ s16b o_idx;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Scan objects in that grid for combination */
+ for (this_o_idx = cave[y][x].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Check for combination */
+ if (object_similar(o_ptr, j_ptr))
+ {
+ /* Combine the items */
+ object_absorb(o_ptr, j_ptr);
+
+ /* Result */
+ return (this_o_idx);
+ }
+
+ /* Count objects */
+ n++;
+ }
+
+ /* The stack is already too large */
+ if (n > 23) return (0);
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure Copy */
+ object_copy(o_ptr, j_ptr);
+
+ /* Location */
+ o_ptr->iy = y;
+ o_ptr->ix = x;
+
+ /* Forget monster */
+ o_ptr->held_m_idx = 0;
+
+ /* Build a stack */
+ o_ptr->next_o_idx = cave[y][x].o_idx;
+
+ /* Place the object */
+ cave[y][x].o_idx = o_idx;
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+
+ /* Result */
+ return (o_idx);
+}
+
+/*
+ * Notice a decaying object in the pack
+ */
+void pack_decay(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ int amt = o_ptr->number;
+
+ s16b m_type;
+ s32b wt;
+
+ byte known = o_ptr->name1;
+
+ byte gone = 1;
+
+ char desc[80];
+
+ /* Player notices each decaying object */
+ object_desc(desc, o_ptr, TRUE, 3);
+ msg_format("You feel %s decompose.", desc);
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Obtain local object */
+ object_copy(i_ptr, o_ptr);
+
+ /* Remember what creature we were */
+ m_type = o_ptr->pval2;
+
+ /* and how much we weighed */
+ wt = r_ptr->weight;
+
+ /* Get rid of decayed object */
+ inc_stack_size_ex(item, -amt, OPTIMIZE, NO_DESCRIBE);
+
+ if (i_ptr->tval == TV_CORPSE)
+ {
+ /* Monster must have a skull for its head to become one */
+ if (i_ptr->sval == SV_CORPSE_HEAD)
+ {
+ /* Replace the head with a skull */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKULL));
+ i_ptr->weight = wt / 60 + rand_int(wt) / 600;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Monster must have a skeleton for its corpse to become one */
+ if ((i_ptr->sval == SV_CORPSE_CORPSE) && (r_ptr->flags3 & RF9_DROP_SKELETON))
+ {
+ /* Replace the corpse with a skeleton */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKELETON));
+ i_ptr->weight = wt / 4 + rand_int(wt) / 40;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Don't restore if the item is gone */
+ if (!gone)
+ {
+ i_ptr->number = amt;
+ i_ptr->pval2 = m_type;
+
+ /* Should become "The skull of Farmer Maggot", not "A skull" */
+ if (known)
+ {
+ object_aware(i_ptr);
+
+ /* Named skeletons are artifacts */
+ i_ptr->name1 = 201;
+ }
+ inven_carry(i_ptr, TRUE);
+ }
+ }
+}
+
+/*
+ * Decay an object on the floor
+ */
+void floor_decay(int item)
+{
+ object_type *o_ptr = &o_list[item];
+
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ int amt = o_ptr->number;
+
+ s16b m_type;
+ s32b wt;
+
+ byte known = o_ptr->name1;
+
+ /* Assume we disappear */
+ byte gone = 1;
+
+ byte x = o_ptr->ix;
+ byte y = o_ptr->iy;
+
+ /* Maybe the player sees it */
+ bool_ visible = player_can_see_bold(o_ptr->iy, o_ptr->ix);
+ char desc[80];
+
+ if (visible)
+ {
+ /* Player notices each decaying object */
+ object_desc(desc, o_ptr, TRUE, 3);
+ msg_format("You see %s decompose.", desc);
+ }
+
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Obtain local object */
+ object_copy(i_ptr, o_ptr);
+
+ /* Remember what creature we were */
+ m_type = o_ptr->pval2;
+
+ /* and how much we weighed */
+ wt = r_ptr->weight;
+
+ floor_item_increase(item, -amt);
+ floor_item_optimize(item);
+
+ if (i_ptr->tval == TV_CORPSE)
+ {
+ /* Monster must have a skull for its head to become one */
+ if (i_ptr->sval == SV_CORPSE_HEAD)
+ {
+ /* Replace the head with a skull */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKULL));
+ i_ptr->weight = wt / 60 + rand_int(wt) / 600;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Monster must have a skeleton for its corpse to become one */
+ if ((i_ptr->sval == SV_CORPSE_CORPSE) && (r_ptr->flags3 & RF9_DROP_SKELETON))
+ {
+ /* Replace the corpse with a skeleton */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKELETON));
+ i_ptr->weight = wt / 4 + rand_int(wt) / 40;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Don't restore if the item is gone */
+ if (!gone)
+ {
+ i_ptr->number = amt;
+ i_ptr->pval2 = m_type;
+
+ /* Should become "The skull of Farmer Maggot", not "A skull" */
+ if (known)
+ {
+ object_aware(i_ptr);
+
+ /* Named skeletons are artifacts */
+ i_ptr->name1 = 201;
+ }
+ floor_carry(y, x, i_ptr);
+ }
+ }
+}
+
+/* Return the item be it on the floor or in inven */
+object_type *get_object(int item)
+{
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ return &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ return &o_list[0 - item];
+ }
+}
+
diff --git a/src/player.pkg b/src/player.pkg
new file mode 100644
index 00000000..dfdced26
--- /dev/null
+++ b/src/player.pkg
@@ -0,0 +1,3519 @@
+/* File: player.pkg */
+
+/*
+ * Purpose: Lua interface defitions for the player.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @def PY_MAX_LEVEL
+ * @note Maximum level
+ */
+#define PY_MAX_LEVEL 50
+
+/** @var player_exp[PY_MAX_LEVEL]
+ * @brief Number
+ * @note Array of experience points per level.
+ */
+extern s32b player_exp[PY_MAX_LEVEL];
+
+/** @name Attributes
+ * @brief Indexes of the various "stats" (hard-coded by savefiles, etc).
+ * @{ */
+/** @def A_STR
+ * @note Strength */
+#define A_STR 0
+
+/** @def A_INT
+ * @note Intelligence */
+#define A_INT 1
+
+/** @def A_WIS
+ * @note Wisdom */
+#define A_WIS 2
+
+/** @def A_DEX
+ * @note Dexterity */
+#define A_DEX 3
+
+/** @def A_CON
+ * @note Constitution */
+#define A_CON 4
+
+/** @def A_CHR
+ * @note Charisma */
+#define A_CHR 5
+/** @} */
+
+/* Ugly hack, should be in foo-info, the subrace saved to the savefile */
+/** @def SUBRACE_SAVE */
+#define SUBRACE_SAVE 9
+
+
+/** @name Sex
+ * @brief Player sex constants (hard-coded by save-files, arrays, etc)
+ * @{ */
+/** @def SEX_FEMALE */
+#define SEX_FEMALE 0
+
+/** @def SEX_MALE */
+#define SEX_MALE 1
+
+/** @def SEX_NEUTER */
+#define SEX_NEUTER 2
+
+
+/** @def MAX_SEXES */
+#define MAX_SEXES 3
+/** @} */
+
+/** @name Race flags
+ * @{ */
+/** @def PR1_EXPERIMENTAL
+ * @note Is still under developemnt
+ */
+#define PR1_EXPERIMENTAL 0x00000001L
+/* XXX */
+/** @def PR1_RESIST_BLACK_BREATH
+ * @note Resist black breath
+ */
+#define PR1_RESIST_BLACK_BREATH 0x00000004L
+/** @def PR1_NO_STUN
+ * @note Never stunned
+ */
+#define PR1_NO_STUN 0x00000008L
+/** @def PR1_XTRA_MIGHT_BOW
+ * @note Xtra might with bows
+ */
+#define PR1_XTRA_MIGHT_BOW 0x00000010L
+/** @def PR1_XTRA_MIGHT_XBOW
+ * @note Xtra might with xbows
+ */
+#define PR1_XTRA_MIGHT_XBOW 0x00000020L
+/** @def PR1_XTRA_MIGHT_SLING
+ * @note Xtra might with slings
+ */
+#define PR1_XTRA_MIGHT_SLING 0x00000040L
+/** @def PR1_AC_LEVEL
+ * @note More AC with levels
+ */
+#define PR1_AC_LEVEL 0x00000080L
+/** @def PR1_HURT_LITE
+ * @note Hurt by light
+ */
+#define PR1_HURT_LITE 0x00000100L
+/** @def PR1_VAMPIRE
+ * @note Vampire
+ */
+#define PR1_VAMPIRE 0x00000200L
+/** @def PR1_UNDEAD
+ * @note Undead
+ */
+#define PR1_UNDEAD 0x00000400L
+/** @def PR1_NO_CUT
+ * @note no cuts
+ */
+#define PR1_NO_CUT 0x00000800L
+/** @def PR1_CORRUPT
+ * @note hack-- corrupted
+ */
+#define PR1_CORRUPT 0x00001000L
+/** @def PR1_NO_FOOD
+ * @note little gain from food
+ */
+#define PR1_NO_FOOD 0x00002000L
+/** @def PR1_NO_GOD
+ * @note cannot worship
+ */
+#define PR1_NO_GOD 0x00004000L
+/* XXX */
+/** @def PR1_ELF
+ * @note Is an elf
+ */
+#define PR1_ELF 0x00010000L
+/** @def PR1_SEMI_WRAITH
+ * @note Takes damage when going in walls
+ */
+#define PR1_SEMI_WRAITH 0x00020000L
+/** @def PR1_NO_SUBRACE_CHANGE
+ * @note Impossible to change subrace
+ */
+#define PR1_NO_SUBRACE_CHANGE 0x00040000L
+/* XXX */
+/** @def PR1_ANTIMAGIC
+ * @note antimagic ... hack
+ */
+#define PR1_ANTIMAGIC 0x00100000L
+/** @def PR1_MOLD_FRIEND
+ * @note Not attacked by molds wielded
+ */
+#define PR1_MOLD_FRIEND 0x00200000L
+/** @def PR1_GOD_FRIEND
+ * @note Better grace
+ */
+#define PR1_GOD_FRIEND 0x00400000L
+/* XXX */
+/** @def PR1_INNATE_SPELLS
+ * @note KNown all spells, only need books
+ */
+#define PR1_INNATE_SPELLS 0x01000000L
+/* XXX */
+/* XXX */
+/** @def PR1_EASE_STEAL
+ * @note Gain xp by stealing
+ */
+#define PR1_EASE_STEAL 0x08000000L
+/* XXX */
+/* XXX */
+/* XXX */
+/* XXX */
+
+/* XXX */
+/** @def PR2_ASTRAL
+ * @note Is it an astral being coming from th halls of mandos ?
+ */
+#define PR2_ASTRAL 0x00000002L
+/* XXX */
+/** @} */
+
+/** @name Notice flags
+ * @brief Bit flags for the "p_ptr->notice" variable
+ * @{ */
+/** @def PN_COMBINE
+ * @note Combine the pack
+ */
+#define PN_COMBINE 0x00000001L
+/** @def PN_REORDER
+ * @note Reorder the pack
+ */
+#define PN_REORDER 0x00000002L
+/* xxx (many) */
+/** @} */
+
+
+/** @name Update flags
+ * @brief Bit flags for the "p_ptr->update" variable
+ * @{ */
+/** @def PU_BONUS
+ * @note Calculate bonuses
+ */
+#define PU_BONUS 0x00000001L
+/** @def PU_TORCH
+ * @note Calculate torch radius
+ */
+#define PU_TORCH 0x00000002L
+/** @def PU_BODY
+ * @note Calculate body parts
+ */
+#define PU_BODY 0x00000004L
+/** @def PU_SANITY
+ * @note Calculate csan and msan
+ */
+#define PU_SANITY 0x00000008L
+/** @def PU_HP
+ * @note Calculate chp and mhp
+ */
+#define PU_HP 0x00000010L
+/** @def PU_MANA
+ * @note Calculate csp and msp
+ */
+#define PU_MANA 0x00000020L
+/** @def PU_SPELLS
+ * @note Calculate spells
+ */
+#define PU_SPELLS 0x00000040L
+/** @def PU_POWERS
+ * @note Calculate powers
+ */
+#define PU_POWERS 0x00000080L
+/* xxx (many) */
+/** @def PU_UN_VIEW
+ * @note Forget view
+ */
+#define PU_UN_VIEW 0x00010000L
+/* xxx (many) */
+/** @def PU_VIEW
+ * @note Update view
+ */
+#define PU_VIEW 0x00100000L
+/** @def PU_MON_LITE
+ * @note Update monster light
+ */
+#define PU_MON_LITE 0x00200000L
+/* xxx */
+/** @def PU_MONSTERS
+ * @note Update monsters
+ */
+#define PU_MONSTERS 0x01000000L
+/** @def PU_DISTANCE
+ * @note Update distances
+ */
+#define PU_DISTANCE 0x02000000L
+/* xxx */
+/** @def PU_FLOW
+ * @note Update flow
+ */
+#define PU_FLOW 0x10000000L
+/* xxx (many) */
+/** @} */
+
+
+/** @name Redraw flags
+ * @brief Bit flags for the "p_ptr->redraw" variable
+ * @{ */
+/** @def PR_MISC
+ * @note Display Race/Class
+ */
+#define PR_MISC 0x00000001L
+/** @def PR_TITLE
+ * @note Display Title
+ */
+#define PR_TITLE 0x00000002L
+/** @def PR_LEV
+ * @note Display Level
+ */
+#define PR_LEV 0x00000004L
+/** @def PR_EXP
+ * @note Display Experience
+ */
+#define PR_EXP 0x00000008L
+/** @def PR_STATS
+ * @note Display Stats
+ */
+#define PR_STATS 0x00000010L
+/** @def PR_ARMOR
+ * @note Display Armor
+ */
+#define PR_ARMOR 0x00000020L
+/** @def PR_HP
+ * @note Display Hitpoints
+ */
+#define PR_HP 0x00000040L
+/** @def PR_MANA
+ * @note Display Mana
+ */
+#define PR_MANA 0x00000080L
+/** @def PR_GOLD
+ * @note Display Gold
+ */
+#define PR_GOLD 0x00000100L
+/** @def PR_DEPTH
+ * @note Display Depth
+ */
+#define PR_DEPTH 0x00000200L
+/****/
+/** @def PR_HEALTH
+ * @note Display Health Bar
+ */
+#define PR_HEALTH 0x00000800L
+/** @def PR_CUT
+ * @note Display Extra (Cut)
+ */
+#define PR_CUT 0x00001000L
+/** @def PR_STUN
+ * @note Display Extra (Stun)
+ */
+#define PR_STUN 0x00002000L
+/** @def PR_HUNGER
+ * @note Display Extra (Hunger)
+ */
+#define PR_HUNGER 0x00004000L
+/** @def PR_PIETY
+ * @note Display Piety
+ */
+#define PR_PIETY 0x00008000L
+/** @def PR_BLIND
+ * @note Display Extra (Blind)
+ */
+#define PR_BLIND 0x00010000L
+/** @def PR_CONFUSED
+ * @note Display Extra (Confused)
+ */
+#define PR_CONFUSED 0x00020000L
+/** @def PR_AFRAID
+ * @note Display Extra (Afraid)
+ */
+#define PR_AFRAID 0x00040000L
+/** @def PR_POISONED
+ * @note Display Extra (Poisoned)
+ */
+#define PR_POISONED 0x00080000L
+/** @def PR_STATE
+ * @note Display Extra (State)
+ */
+#define PR_STATE 0x00100000L
+/** @def PR_SPEED
+ * @note Display Extra (Speed)
+ */
+#define PR_SPEED 0x00200000L
+/** @def PR_STUDY
+ * @note Display Extra (Study)
+ */
+#define PR_STUDY 0x00400000L
+/** @def PR_SANITY
+ * @note Display Sanity
+ */
+#define PR_SANITY 0x00800000L
+/** @def PR_EXTRA
+ * @note Display Extra Info
+ */
+#define PR_EXTRA 0x01000000L
+/** @def PR_BASIC
+ * @note Display Basic Info
+ */
+#define PR_BASIC 0x02000000L
+/** @def PR_MAP
+ * @note Display Map
+ */
+#define PR_MAP 0x04000000L
+/** @def PR_WIPE
+ * @note Hack -- Total Redraw
+ */
+#define PR_WIPE 0x08000000L
+/** @def PR_MH
+ * @note Display Monster hitpoints
+ */
+#define PR_MH 0x10000000L
+/** @def PR_MH
+ * @note Display Monster hitpoints
+ */
+#define PR_MH 0x10000000L
+/** @def PR_DTRAP
+ * @note Display Extra (DTrap)
+ */
+#define PR_DTRAP 0x20000000L
+/* xxx */
+/* xxx */
+/** @} */
+
+
+/** @name Window flags
+ * @brief Bit flags for the "p_ptr->window" variable (etc)
+ * @{ */
+/** @def PW_INVEN
+ * @note Display inven/equip
+ */
+#define PW_INVEN 0x00000001L
+/** @def PW_EQUIP
+ * @note Display equip/inven
+ */
+#define PW_EQUIP 0x00000002L
+/* xxx */
+/** @def PW_PLAYER
+ * @note Display character
+ */
+#define PW_PLAYER 0x00000008L
+/** @def PW_M_LIST
+ * @note Show monster list
+ */
+#define PW_M_LIST 0x00000010L
+/* xxx */
+/** @def PW_MESSAGE
+ * @note Display messages
+ */
+#define PW_MESSAGE 0x00000040L
+/** @def PW_OVERHEAD
+ * @note Display overhead view
+ */
+#define PW_OVERHEAD 0x00000080L
+/** @def PW_MONSTER
+ * @note Display monster recall
+ */
+#define PW_MONSTER 0x00000100L
+/** @def PW_OBJECT
+ * @note Display object recall
+ */
+#define PW_OBJECT 0x00000200L
+/* xxx */
+/** @def PW_SNAPSHOT
+ * @note Display snap-shot
+ */
+#define PW_SNAPSHOT 0x00000800L
+/* xxx */
+/* xxx */
+/** @def PW_BORG_1
+ * @note Display borg messages
+ */
+#define PW_BORG_1 0x00004000L
+/** @def PW_BORG_2
+ * @note Display borg status
+ */
+#define PW_BORG_2 0x00008000L
+/** @} */
+
+/** @struct deity_type
+ */
+struct deity_type
+{
+ /** @structvar name
+ * @brief String
+ */
+ cptr name;
+};
+/** @var deity_info[max_gods]
+ * @brief deity_type
+ * @note Array of gods.
+ */
+extern deity_type deity_info[max_gods];
+
+/** @name Body parts
+ * @{ */
+/** @def BODY_WEAPON */
+#define BODY_WEAPON 0
+
+/** @def BODY_TORSO */
+#define BODY_TORSO 1
+
+/** @def BODY_ARMS */
+#define BODY_ARMS 2
+
+/** @def BODY_FINGER */
+#define BODY_FINGER 3
+
+/** @def BODY_HEAD */
+#define BODY_HEAD 4
+
+/** @def BODY_LEGS */
+#define BODY_LEGS 5
+
+/** @def BODY_MAX */
+#define BODY_MAX 6
+/** @} */
+
+
+/** @struct player_type
+ */
+struct player_type
+{
+ /** @structvar lives
+ * @brief Number
+ * @note How many times we resurected
+ */
+ s32b lives;
+
+ /** @structvar oldpy
+ * @brief Number
+ * @note Previous player location -KMW-
+ */
+ s16b oldpy;
+ /** @structvar oldpx
+ * @brief Number
+ * @note Previous player location -KMW-
+ */
+ s16b oldpx;
+
+ /** @structvar py
+ * @brief Number
+ * @note Player location
+ */
+ s16b py;
+ /** @structvar px
+ * @brief Number
+ * @note Player location
+ */
+ s16b px;
+
+ /** @structvar psex
+ * @brief Number
+ * @note Sex index
+ */
+ byte psex;
+ /** @structvar prace
+ * @brief Number
+ * @note Race index
+ */
+ byte prace;
+ /** @structvar pracem
+ * @brief Number
+ * @note Race Mod index
+ */
+ byte pracem;
+ /** @structvar pclass
+ * @brief Number
+ * @note Class index
+ */
+ byte pclass;
+ /** @structvar mimic_form
+ * @brief Number
+ * @note Actualy transformation
+ */
+ byte mimic_form;
+ /** @structvar mimic_level
+ * @brief Number
+ * @note Level of the mimic effect
+ */
+ s16b mimic_level;
+ /** @structvar oops
+ * @brief Number
+ * @note Unused
+ */
+ byte oops;
+
+ object_type inventory[INVEN_TOTAL] @inventory_real;
+
+ /** @structvar hitdie
+ * @brief Number
+ * @note Hit dice (sides)
+ */
+ byte hitdie;
+ /** @structvar expfact
+ * @brief Number
+ * @note Experience factor
+ */
+ u16b expfact;
+
+ /** @structvar allow_one_death
+ * @brief Number
+ * @note Blood of life
+ */
+ byte allow_one_death;
+
+ /** @structvar age
+ * @brief Number
+ * @note Characters age
+ */
+ s16b age;
+ /** @structvar ht
+ * @brief Number
+ * @note Height
+ */
+ s16b ht;
+ /** @structvar wt
+ * @brief Number
+ * @note Weight
+ */
+ s16b wt;
+ /** @structvar sc
+ * @brief Number
+ * @note Social Class
+ */
+ s16b sc;
+
+
+ /** @structvar au
+ * @brief Number
+ * @note Current Gold
+ */
+ s32b au;
+
+ /** @structvar max_exp
+ * @brief Number
+ * @note Max experience
+ */
+ s32b max_exp;
+ /** @structvar exp
+ * @brief Number
+ * @note Cur experience
+ */
+ s32b exp;
+ /** @structvar exp_frac
+ * @brief Number
+ * @note Cur exp frac (times 2^16)
+ */
+ u16b exp_frac;
+
+ /** @structvar lev
+ * @brief Number
+ * @note Level
+ */
+ s16b lev;
+
+ /** @structvar town_num
+ * @brief Number
+ * @note Current town number
+ */
+ s16b town_num;
+ /** @structvar inside_quest
+ * @brief Number
+ * @note Inside quest level
+ */
+ s16b inside_quest;
+ /** @structvar exit_bldg
+ * @brief Boolean
+ * @note Goal obtained in arena? -KMW-
+ */
+ bool exit_bldg;
+
+ /** @structvar wilderness_x
+ * @brief Number
+ * @note Coordinates in the wilderness
+ */
+ s32b wilderness_x;
+ /** @structvar wilderness_y
+ * @brief Number
+ */
+ s32b wilderness_y;
+ /** @structvar wild_mode
+ * @brief Boolean
+ * @note TRUE = Small map, FLASE = Big map
+ */
+ bool wild_mode;
+ /** @structvar old_wild_mode
+ * @brief Boolean
+ * @note TRUE = Small map, FLASE = Big map
+ */
+ bool old_wild_mode;
+
+ /** @structvar mhp
+ * @brief Number
+ * @note Max hit pts
+ */
+ s16b mhp;
+ /** @structvar chp
+ * @brief Number
+ * @note Cur hit pts
+ */
+ s16b chp;
+ /** @structvar chp_frac
+ * @brief Number
+ * @note Cur hit frac (times 2^16)
+ */
+ u16b chp_frac;
+ /** @structvar hp_mod
+ * @brief Number
+ * @note A modificator(permanent)
+ */
+ s16b hp_mod;
+
+ /** @structvar msp
+ * @brief Number
+ * @note Max mana pts
+ */
+ s16b msp;
+ /** @structvar csp
+ * @brief Number
+ * @note Cur mana pts
+ */
+ s16b csp;
+ /** @structvar csp_frac
+ * @brief Number
+ * @note Cur mana frac (times 2^16)
+ */
+ u16b csp_frac;
+
+ /** @structvar msane
+ * @brief Number
+ * @note Max sanity
+ */
+ s16b msane;
+ /** @structvar csane
+ * @brief Number
+ * @note Cur sanity
+ */
+ s16b csane;
+ /** @structvar csane_frac
+ * @brief Number
+ * @note Cur sanity frac
+ */
+ u16b csane_frac;
+
+ /** @structvar grace
+ * @brief Number
+ * @note Your God's appreciation factor.
+ */
+ s32b grace;
+ /** @structvar pgod
+ * @brief Number
+ * @note Your God.
+ */
+ byte pgod;
+ /** @structvar praying
+ * @brief Boolean
+ * @note Praying to your god.
+ */
+ bool praying;
+
+ /** @structvar max_plv
+ * @brief Number
+ * @note Max Player Level
+ */
+ s16b max_plv;
+
+ /** @structvar stat_max[6]
+ * @brief Number
+ * @note Current "maximal" stat values
+ */
+ s16b stat_max[6];
+ /** @structvar stat_cur[6]
+ * @brief Number
+ * @note Current "natural" stat values
+ */
+ s16b stat_cur[6];
+
+ /** @structvar luck_cur
+ * @brief Number
+ * @note Current "natural" luck value (range -30 <> 30)
+ */
+ s16b luck_cur;
+ /** @structvar luck_max
+ * @brief Number
+ * @note Current "maximal base" luck value (range -30 <> 30)
+ */
+ s16b luck_max;
+ /** @structvar luck_base
+ * @brief Number
+ * @note Current "base" luck value (range -30 <> 30)
+ */
+ s16b luck_base;
+
+ /** @structvar fast
+ * @brief Number
+ * @note Timed -- Fast
+ */
+ s16b fast;
+ /** @structvar lightspeed
+ * @brief Number
+ * @note Timed -- Light Speed
+ */
+ s16b lightspeed;
+ /** @structvar slow
+ * @brief Number
+ * @note Timed -- Slow
+ */
+ s16b slow;
+ /** @structvar blind
+ * @brief Number
+ * @note Timed -- Blindness
+ */
+ s16b blind;
+ /** @structvar paralyzed
+ * @brief Number
+ * @note Timed -- Paralysis
+ */
+ s16b paralyzed;
+ /** @structvar confused
+ * @brief Number
+ * @note Timed -- Confusion
+ */
+ s16b confused;
+ /** @structvar afraid
+ * @brief Number
+ * @note Timed -- Fear
+ */
+ s16b afraid;
+ /** @structvar image
+ * @brief Number
+ * @note Timed -- Hallucination
+ */
+ s16b image;
+ /** @structvar poisoned
+ * @brief Number
+ * @note Timed -- Poisoned
+ */
+ s16b poisoned;
+ /** @structvar cut
+ * @brief Number
+ * @note Timed -- Cut
+ */
+ s16b cut;
+ /** @structvar stun
+ * @brief Number
+ * @note Timed -- Stun
+ */
+ s16b stun;
+
+ /** @structvar protevil
+ * @brief Number
+ * @note Timed -- Protection from Evil
+ */
+ s16b protevil;
+ /** @structvar protgood
+ * @brief Number
+ * @note Timed -- Protection from Good
+ */
+ s16b protgood;
+ /** @structvar protundead
+ * @brief Number
+ * @note Timed -- Protection from Undead
+ */
+ s16b protundead;
+ /** @structvar invuln
+ * @brief Number
+ * @note Timed -- Invulnerable
+ */
+ s16b invuln;
+ /** @structvar hero
+ * @brief Number
+ * @note Timed -- Heroism
+ */
+ s16b hero;
+ /** @structvar shero
+ * @brief Number
+ * @note Timed -- Super Heroism
+ */
+ s16b shero;
+ /** @structvar shield
+ * @brief Number
+ * @note Timed -- Shield Spell
+ */
+ s16b shield;
+ /** @structvar shield_power
+ * @brief Number
+ * @note Timed -- Shield Spell Power
+ */
+ s16b shield_power;
+ /** @structvar shield_opt
+ * @brief Number
+ * @note Timed -- Shield Spell options
+ */
+ s16b shield_opt;
+ /** @structvar blessed
+ * @brief Number
+ * @note Timed -- Blessed
+ */
+ s16b blessed;
+ /** @structvar tim_invis
+ * @brief Number
+ * @note Timed -- See Invisible
+ */
+ s16b tim_invis;
+ /** @structvar tim_infra
+ * @brief Number
+ * @note Timed -- Infra Vision
+ */
+ s16b tim_infra;
+
+ /** @structvar oppose_acid
+ * @brief Number
+ * @note Timed -- oppose acid
+ */
+ s16b oppose_acid;
+ /** @structvar oppose_elec
+ * @brief Number
+ * @note Timed -- oppose lightning
+ */
+ s16b oppose_elec;
+ /** @structvar oppose_fire
+ * @brief Number
+ * @note Timed -- oppose heat
+ */
+ s16b oppose_fire;
+ /** @structvar oppose_cold
+ * @brief Number
+ * @note Timed -- oppose cold
+ */
+ s16b oppose_cold;
+ /** @structvar oppose_pois
+ * @brief Number
+ * @note Timed -- oppose poison
+ */
+ s16b oppose_pois;
+ /** @structvar oppose_ld
+ * @brief Number
+ * @note Timed -- oppose light & dark
+ */
+ s16b oppose_ld;
+ /** @structvar oppose_cc
+ * @brief Number
+ * @note Timed -- oppose chaos & confusion
+ */
+ s16b oppose_cc;
+ /** @structvar oppose_ss
+ * @brief Number
+ * @note Timed -- oppose sound & shards
+ */
+ s16b oppose_ss;
+ /** @structvar oppose_nex
+ * @brief Number
+ * @note Timed -- oppose nexus
+ */
+ s16b oppose_nex;
+
+
+ /** @structvar tim_esp
+ * @brief Number
+ * @note Timed ESP
+ */
+ s16b tim_esp;
+ /** @structvar tim_wraith
+ * @brief Number
+ * @note Timed wraithform
+ */
+ s16b tim_wraith;
+ /** @structvar tim_ffall
+ * @brief Number
+ * @note Timed Levitation
+ */
+ s16b tim_ffall;
+ /** @structvar tim_fly
+ * @brief Number
+ * @note Timed Levitation
+ */
+ s16b tim_fly;
+ /** @structvar tim_fire_aura
+ * @brief Number
+ * @note Timed Fire Aura
+ */
+ s16b tim_fire_aura;
+ /** @structvar tim_regen
+ * @brief Number
+ * @note Timed regen
+ */
+ s16b tim_regen;
+ /** @structvar tim_regen_pow
+ * @brief Number
+ * @note Timed regen
+ */
+ s16b tim_regen_pow;
+ /** @structvar tim_poison
+ * @brief Number
+ * @note Timed poison hands
+ */
+ s16b tim_poison;
+ /** @structvar tim_thunder
+ * @brief Number
+ * @note Timed thunderstorm
+ */
+ s16b tim_thunder;
+ /** @structvar tim_thunder_p1
+ * @brief Number
+ * @note Timed thunderstorm
+ */
+ s16b tim_thunder_p1;
+ /** @structvar tim_thunder_p2
+ * @brief Number
+ * @note Timed thunderstorm
+ */
+ s16b tim_thunder_p2;
+
+ /** @structvar resist_magic
+ * @brief Number
+ * @note Timed Resist Magic (later)
+ */
+ s16b resist_magic;
+ /** @structvar tim_invisible
+ * @brief Number
+ * @note Timed Invisibility
+ */
+ s16b tim_invisible;
+ /** @structvar tim_inv_pow
+ * @brief Number
+ * @note Power of timed invisibility
+ */
+ s16b tim_inv_pow;
+ /** @structvar tim_mimic
+ * @brief Number
+ * @note Timed Mimic
+ */
+ s16b tim_mimic;
+ /** @structvar tim_lite
+ * @brief Number
+ * @note Timed Lite
+ */
+ s16b tim_lite;
+ /** @structvar holy
+ * @brief Number
+ * @note Holy Aura
+ */
+ s16b holy;
+ /** @structvar walk_water
+ * @brief Number
+ * @note Walk over water as a god
+ */
+ s16b walk_water;
+ /** @structvar tim_mental_barrier
+ * @brief Number
+ * @note Sustain Int&Wis
+ */
+ s16b tim_mental_barrier;
+ /** @structvar strike
+ * @brief Number
+ * @note True Strike(+25 hit)
+ */
+ s16b strike;
+ /** @structvar meditation
+ * @brief Number
+ * @note Meditation(+50 mana -25 to hit/to dam)
+ */
+ s16b meditation;
+ /** @structvar tim_reflect
+ * @brief Number
+ * @note Timed Reflection
+ */
+ s16b tim_reflect;
+ /** @structvar tim_res_time
+ * @brief Number
+ * @note Timed Resistance to Time
+ */
+ s16b tim_res_time;
+ /** @structvar tim_deadly
+ * @brief Number
+ * @note Timed deadly blow
+ */
+ s16b tim_deadly;
+ /** @structvar prob_travel
+ * @brief Number
+ * @note Timed probability travel
+ */
+ s16b prob_travel;
+ /** @structvar disrupt_shield
+ * @brief Number
+ * @note Timed disruption shield
+ */
+ s16b disrupt_shield;
+ /** @structvar parasite
+ * @brief Number
+ * @note Timed parasite
+ */
+ s16b parasite;
+ /** @structvar parasite_r_idx
+ * @brief Number
+ * @note Timed parasite monster
+ */
+ s16b parasite_r_idx;
+ /** @structvar loan
+ * @brief Number
+ */
+ u32b loan;
+ /** @structvar loan_time
+ * @brief Number
+ * @note Timer -- loan
+ */
+ u32b loan_time;
+ /** @structvar tim_magic_breath
+ * @brief Number
+ * @note Magical breathing -- can breath anywhere
+ */
+ s16b tim_magic_breath;
+ /** @structvar tim_water_breath
+ * @brief Number
+ * @note Water breathing -- can breath underwater
+ */
+ s16b tim_water_breath;
+
+ /** @structvar immov_cntr
+ * @brief Number
+ * @note Timed -- Last ``immovable'' command.
+ */
+ s16b immov_cntr;
+
+ /** @structvar music_extra
+ * @brief Number
+ * @note Music songs
+ */
+ u32b music_extra;
+ /** @structvar music_extra2
+ * @brief Number
+ * @note Music songs
+ */
+ u32b music_extra2;
+
+ /** @structvar chaos_patron
+ * @brief Number
+ */
+ s16b chaos_patron;
+
+ /** @structvar recall_dungeon
+ * @brief Number
+ * @note Recall in which dungeon
+ */
+ s16b recall_dungeon;
+ /** @structvar word_recall
+ * @brief Number
+ * @note Word of recall counter
+ */
+ s16b word_recall;
+
+ /** @structvar energy
+ * @brief Number
+ * @note Current energy
+ */
+ s32b energy;
+
+ /** @structvar food
+ * @brief Number
+ * @note Current nutrition
+ */
+ s16b food;
+
+ /** @structvar confusing
+ * @brief Number
+ * @note Glowing hands
+ */
+ byte confusing;
+ /** @structvar searching
+ * @brief Number
+ * @note Currently searching
+ */
+ byte searching;
+
+ /** @structvar new_spells
+ * @brief Number
+ * @note Number of spells available
+ */
+ s16b new_spells;
+
+ /** @structvar old_spells
+ * @brief Number
+ */
+ s16b old_spells;
+
+ /** @structvar xtra_spells
+ * @brief Number
+ * @note Number of xtra spell learned(via potion)
+ */
+ s16b xtra_spells;
+
+ /** @structvar cur_lite
+ * @brief Number
+ * @note Radius of lite (if any)
+ */
+ s16b cur_lite;
+
+ /*** Extra flags -- used for lua and easying stuff ***/
+ /** @structvar xtra_f1
+ * @brief Number
+ */
+ u32b xtra_f1;
+ /** @structvar xtra_f2
+ * @brief Number
+ */
+ u32b xtra_f2;
+ /** @structvar xtra_f3
+ * @brief Number
+ */
+ u32b xtra_f3;
+ /** @structvar xtra_f4
+ * @brief Number
+ */
+ u32b xtra_f4;
+ /** @structvar xtra_f5
+ * @brief Number
+ */
+ u32b xtra_f5;
+ /** @structvar xtra_esp
+ * @brief Number
+ */
+ u32b xtra_esp;
+
+ /** @structvar pspeed
+ * @brief Number
+ * @note Current speed
+ */
+ s16b pspeed;
+
+ /** @structvar notice
+ * @brief Number
+ * @note Special Updates (bit flags)
+ */
+ u32b notice;
+ /** @structvar update
+ * @brief Number
+ * @note Pending Updates (bit flags)
+ */
+ u32b update;
+ /** @structvar redraw
+ * @brief Number
+ * @note Normal Redraws (bit flags)
+ */
+ u32b redraw;
+ /** @structvar window
+ * @brief Number
+ * @note Window Redraws (bit flags)
+ */
+ u32b window;
+
+ /** @structvar stat_use[6]
+ * @brief Number
+ * @note Current modified stats
+ */
+ s16b stat_use[6];
+ /** @structvar stat_top[6]
+ * @brief Number
+ * @note Maximal modified stats
+ */
+ s16b stat_top[6];
+
+ /** @structvar stat_add[6]
+ * @brief Number
+ * @note Modifiers to stat values
+ */
+ s16b stat_add[6];
+ /** @structvar stat_ind[6]
+ * @brief Number
+ * @note Indexes into stat tables
+ */
+ s16b stat_ind[6];
+ /** @structvar stat_cnt[6]
+ * @brief Number
+ * @note Counter for temporary drains
+ */
+ s16b stat_cnt[6];
+ /** @structvar stat_los[6]
+ * @brief Number
+ * @note Amount of temporary drains
+ */
+ s16b stat_los[6];
+
+ /** @structvar immune_acid
+ * @brief Boolean
+ * @note Immunity to acid
+ */
+ bool immune_acid;
+ /** @structvar immune_elec
+ * @brief Boolean
+ * @note Immunity to lightning
+ */
+ bool immune_elec;
+ /** @structvar immune_fire
+ * @brief Boolean
+ * @note Immunity to fire
+ */
+ bool immune_fire;
+ /** @structvar immune_cold
+ * @brief Boolean
+ * @note Immunity to cold
+ */
+ bool immune_cold;
+ /** @structvar immune_neth
+ * @brief Boolean
+ * @note Immunity to nether
+ */
+ bool immune_neth;
+
+ /** @structvar resist_acid
+ * @brief Boolean
+ * @note Resist acid
+ */
+ bool resist_acid;
+ /** @structvar resist_elec
+ * @brief Boolean
+ * @note Resist lightning
+ */
+ bool resist_elec;
+ /** @structvar resist_fire
+ * @brief Boolean
+ * @note Resist fire
+ */
+ bool resist_fire;
+ /** @structvar resist_cold
+ * @brief Boolean
+ * @note Resist cold
+ */
+ bool resist_cold;
+ /** @structvar resist_pois
+ * @brief Boolean
+ * @note Resist poison
+ */
+ bool resist_pois;
+
+ /** @structvar resist_conf
+ * @brief Boolean
+ * @note Resist confusion
+ */
+ bool resist_conf;
+ /** @structvar resist_sound
+ * @brief Boolean
+ * @note Resist sound
+ */
+ bool resist_sound;
+ /** @structvar resist_lite
+ * @brief Boolean
+ * @note Resist light
+ */
+ bool resist_lite;
+ /** @structvar resist_dark
+ * @brief Boolean
+ * @note Resist darkness
+ */
+ bool resist_dark;
+ /** @structvar resist_chaos
+ * @brief Boolean
+ * @note Resist chaos
+ */
+ bool resist_chaos;
+ /** @structvar resist_disen
+ * @brief Boolean
+ * @note Resist disenchant
+ */
+ bool resist_disen;
+ /** @structvar resist_shard
+ * @brief Boolean
+ * @note Resist shards
+ */
+ bool resist_shard;
+ /** @structvar resist_nexus
+ * @brief Boolean
+ * @note Resist nexus
+ */
+ bool resist_nexus;
+ /** @structvar resist_blind
+ * @brief Boolean
+ * @note Resist blindness
+ */
+ bool resist_blind;
+ /** @structvar resist_neth
+ * @brief Boolean
+ * @note Resist nether
+ */
+ bool resist_neth;
+ /** @structvar resist_fear
+ * @brief Boolean
+ * @note Resist fear
+ */
+ bool resist_fear;
+ /** @structvar resist_continuum
+ * @brief Boolean
+ * @note Resist space-time continuum disruption
+ */
+ bool resist_continuum;
+
+ /** @structvar sensible_fire
+ * @brief Boolean
+ * @note Fire does more damage on the player
+ */
+ bool sensible_fire;
+ /** @structvar sensible_lite
+ * @brief Boolean
+ * @note Lite does more damage on the player and blinds her/him
+ */
+ bool sensible_lite;
+
+ /** @structvar reflect
+ * @brief Boolean
+ * @note Reflect 'bolt' attacks
+ */
+ bool reflect;
+ /** @structvar sh_fire
+ * @brief Boolean
+ * @note Fiery 'immolation' effect
+ */
+ bool sh_fire;
+ /** @structvar sh_elec
+ * @brief Boolean
+ * @note Electric 'immolation' effect
+ */
+ bool sh_elec;
+ /** @structvar wraith_form
+ * @brief Boolean
+ * @note wraithform
+ */
+ bool wraith_form;
+
+ /** @structvar anti_magic
+ * @brief Boolean
+ * @note Anti-magic
+ */
+ bool anti_magic;
+ /** @structvar anti_tele
+ * @brief Boolean
+ * @note Prevent teleportation
+ */
+ bool anti_tele;
+
+ /** @structvar sustain_str
+ * @brief Boolean
+ * @note Keep strength
+ */
+ bool sustain_str;
+ /** @structvar sustain_int
+ * @brief Boolean
+ * @note Keep intelligence
+ */
+ bool sustain_int;
+ /** @structvar sustain_wis
+ * @brief Boolean
+ * @note Keep wisdom
+ */
+ bool sustain_wis;
+ /** @structvar sustain_dex
+ * @brief Boolean
+ * @note Keep dexterity
+ */
+ bool sustain_dex;
+ /** @structvar sustain_con
+ * @brief Boolean
+ * @note Keep constitution
+ */
+ bool sustain_con;
+ /** @structvar sustain_chr
+ * @brief Boolean
+ * @note Keep charisma
+ */
+ bool sustain_chr;
+
+ /** @structvar aggravate
+ * @brief Boolean
+ * @note Aggravate monsters
+ */
+ bool aggravate;
+ /** @structvar teleport
+ * @brief Boolean
+ * @note Random teleporting
+ */
+ bool teleport;
+
+ /** @structvar exp_drain
+ * @brief Boolean
+ * @note Experience draining
+ */
+ bool exp_drain;
+ /** @structvar drain_mana
+ * @brief Number
+ * @note mana draining
+ */
+ byte drain_mana;
+ /** @structvar drain_life
+ * @brief Number
+ * @note hp draining
+ */
+ byte drain_life;
+
+ /** @structvar magical_breath
+ * @brief Boolean
+ * @note Magical breathing -- can breath anywhere
+ */
+ bool magical_breath;
+ /** @structvar water_breath
+ * @brief Boolean
+ * @note Water breathing -- can breath underwater
+ */
+ bool water_breath;
+ /** @structvar climb
+ * @brief Boolean
+ * @note Can climb mountains
+ */
+ bool climb;
+ /** @structvar fly
+ * @brief Boolean
+ * @note Can fly over some features
+ */
+ bool fly;
+ /** @structvar ffall
+ * @brief Boolean
+ * @note No damage falling
+ */
+ bool ffall;
+ /** @structvar lite
+ * @brief Boolean
+ * @note Permanent light
+ */
+ bool lite;
+ /** @structvar free_act
+ * @brief Boolean
+ * @note Never paralyzed
+ */
+ bool free_act;
+ /** @structvar see_inv
+ * @brief Boolean
+ * @note Can see invisible
+ */
+ bool see_inv;
+ /** @structvar regenerate
+ * @brief Boolean
+ * @note Regenerate hit pts
+ */
+ bool regenerate;
+ /** @structvar hold_life
+ * @brief Boolean
+ * @note Resist life draining
+ */
+ bool hold_life;
+ /** @structvar telepathy
+ * @brief Number
+ * @note Telepathy
+ */
+ u32b telepathy;
+ /** @structvar slow_digest
+ * @brief Boolean
+ * @note Slower digestion
+ */
+ bool slow_digest;
+ /** @structvar bless_blade
+ * @brief Boolean
+ * @note Blessed blade
+ */
+ bool bless_blade;
+ /** @structvar xtra_might
+ * @brief Number
+ * @note Extra might bow
+ */
+ byte xtra_might;
+ /** @structvar impact
+ * @brief Boolean
+ * @note Earthquake blows
+ */
+ bool impact;
+ /** @structvar auto_id
+ * @brief Boolean
+ * @note Auto id items
+ */
+ bool auto_id;
+
+ /** @structvar dis_to_h
+ * @brief Number
+ * @note Known bonus to hit
+ */
+ s16b dis_to_h;
+ /** @structvar dis_to_d
+ * @brief Number
+ * @note Known bonus to dam
+ */
+ s16b dis_to_d;
+ /** @structvar dis_to_a
+ * @brief Number
+ * @note Known bonus to ac
+ */
+ s16b dis_to_a;
+
+ /** @structvar dis_ac
+ * @brief Number
+ * @note Known base ac
+ */
+ s16b dis_ac;
+
+ /** @structvar to_m
+ * @brief Number
+ * @note Bonus to mana
+ */
+ s16b to_m;
+ /** @structvar to_s
+ * @brief Number
+ * @note Bonus to spell
+ */
+ s16b to_s;
+ /** @structvar to_h
+ * @brief Number
+ * @note Bonus to hit
+ */
+ s16b to_h;
+ /** @structvar to_d
+ * @brief Number
+ * @note Bonus to dam
+ */
+ s16b to_d;
+ /** @structvar to_a
+ * @brief Number
+ * @note Bonus to ac
+ */
+ s16b to_a;
+
+ /** @structvar to_h_melee
+ * @brief Number
+ * @note Bonus to hit
+ */
+ s16b to_h_melee;
+ /** @structvar to_d_melee
+ * @brief Number
+ * @note Bonus to dam
+ */
+ s16b to_d_melee;
+
+ /** @structvar to_h_ranged
+ * @brief Number
+ * @note Bonus to hit
+ */
+ s16b to_h_ranged;
+ /** @structvar to_d_ranged
+ * @brief Number
+ * @note Bonus to dam
+ */
+ s16b to_d_ranged;
+
+ /** @structvar num_blow
+ * @brief Number
+ * @note Number of blows
+ */
+ s16b num_blow;
+ /** @structvar num_fire
+ * @brief Number
+ * @note Number of shots
+ */
+ s16b num_fire;
+
+ /** @structvar ac
+ * @brief Number
+ * @note Base ac
+ */
+ s16b ac;
+
+ /** @structvar antimagic
+ * @brief Number
+ * @note Power of the anti magic field
+ */
+ byte antimagic;
+ /** @structvar antimagic_dis
+ * @brief Number
+ * @note Radius of the anti magic field
+ */
+ byte antimagic_dis;
+
+ /** @structvar see_infra
+ * @brief Number
+ * @note Infravision range
+ */
+ s16b see_infra;
+
+ /** @structvar skill_dis
+ * @brief Number
+ * @note Skill: Disarming
+ */
+ s16b skill_dis;
+ /** @structvar skill_dev
+ * @brief Number
+ * @note Skill: Magic Devices
+ */
+ s16b skill_dev;
+ /** @structvar skill_sav
+ * @brief Number
+ * @note Skill: Saving throw
+ */
+ s16b skill_sav;
+ /** @structvar skill_stl
+ * @brief Number
+ * @note Skill: Stealth factor
+ */
+ s16b skill_stl;
+ /** @structvar skill_srh
+ * @brief Number
+ * @note Skill: Searching ability
+ */
+ s16b skill_srh;
+ /** @structvar skill_fos
+ * @brief Number
+ * @note Skill: Searching frequency
+ */
+ s16b skill_fos;
+ /** @structvar skill_thn
+ * @brief Number
+ * @note Skill: To hit (normal)
+ */
+ s16b skill_thn;
+ /** @structvar skill_thb
+ * @brief Number
+ * @note Skill: To hit (shooting)
+ */
+ s16b skill_thb;
+ /** @structvar skill_tht
+ * @brief Number
+ * @note Skill: To hit (throwing)
+ */
+ s16b skill_tht;
+ /** @structvar skill_dig
+ * @brief Number
+ * @note Skill: Digging
+ */
+ s16b skill_dig;
+
+ /** @structvar skill_points
+ * @brief Number
+ */
+ s16b skill_points;
+
+ /** @structvar control
+ * @brief Number
+ * @note Controlled monster
+ */
+ s16b control;
+ /** @structvar control_dir
+ * @brief Number
+ * @note Controlled monster
+ */
+ byte control_dir;
+ /** @structvar companion_killed
+ * @brief Number
+ * @note Number of companion death
+ */
+ s16b companion_killed;
+ /** @structvar black_breath
+ * @brief Boolean
+ * @note The Tolkien's Black Breath
+ */
+ bool black_breath;
+ /** @structvar body_monster
+ * @brief Number
+ * @note In which body is the player
+ */
+ u16b body_monster;
+
+ /** @structvar body_parts[28]
+ * @brief Number
+ * @note Various body modifiers
+ */
+ byte body_parts[28];
+
+ /** @structvar powers_mod[POWER_MAX_INIT]
+ * @brief Boolean
+ * @note Intrinsinc powers
+ */
+ bool powers_mod[POWER_MAX_INIT];
+ /** @structvar powers[power_max]
+ * @brief Boolean
+ */
+ bool powers[power_max];
+
+ /** @structvar spellbinder_num
+ * @brief Number
+ * @note Number of spells bound
+ */
+ byte spellbinder_num;
+ /** @structvar spellbinder[4]
+ * @brief Number
+ * @note Spell bounds
+ */
+ u32b spellbinder[4];
+ /** @structvar spellbinder_trigger
+ * @brief Number
+ * @note Spellbinder trigger condition
+ */
+ byte spellbinder_trigger;
+
+ /* Corruptions */
+ /** @structvar corruptions_aux;
+ * @brief Boolean
+ */
+ bool corruptions[max_corruptions] @ corruptions_aux;
+
+ /* Astral */
+ /** @structvar astral
+ * @brief Boolean
+ * @note We started at the bottom ?
+ */
+ bool astral;
+
+ /*** Temporary fields ***/
+
+ /** @structvar leaving
+ * @brief Boolean
+ * @note True if player is leaving
+ */
+ bool leaving;
+};
+
+/** @name Spellbinder triggers
+ * @{ */
+/** @def SPELLBINDER_HP75
+ * @note Trigger spellbinder at 75% maximum hit points */
+#define SPELLBINDER_HP75 1
+
+/** @def SPELLBINDER_HP50
+ * @note Trigger spellbinder at 50% maximum hit points */
+#define SPELLBINDER_HP50 2
+
+/** @def SPELLBINDER_HP25
+ * @note Trigger spellbinder at 25% maximum hit points */
+#define SPELLBINDER_HP25 3
+/** @} */
+
+
+/** @struct player_race
+ */
+struct player_race
+{
+ /** @structvar title
+ * @brief Number
+ * @note Type of race
+ */
+ s32b title;
+ /** @structvar desc
+ * @brief Number
+ */
+ s32b desc;
+
+ /** @structvar infra
+ * @brief Number
+ * @note Infra-vision range
+ */
+ byte infra;
+};
+
+/** @struct player_race_mod
+ */
+struct player_race_mod
+{
+ /** @structvar title
+ * @brief Number
+ * @note Type of race mod
+ */
+ s32b title;
+ /** @structvar desc
+ * @brief Number
+ * @note Desc
+ */
+ s32b desc;
+ /** @structvar place
+ * @brief Boolean
+ * @note TRUE = race race modifier, FALSE = Race modifier race
+ */
+ bool place;
+
+ /** @structvar r_adj[6]
+ * @brief Number
+ * @note (+) Racial stat bonuses
+ */
+ s16b r_adj[6];
+
+ /** @structvar luck
+ * @brief String
+ * @note Luck
+ */
+ char luck;
+ /** @structvar mana
+ * @brief Number
+ * @note Mana %
+ */
+ s16b mana;
+
+ /** @structvar r_dis
+ * @brief Number
+ * @note (+) disarming
+ */
+ s16b r_dis;
+ /** @structvar r_dev
+ * @brief Number
+ * @note (+) magic devices
+ */
+ s16b r_dev;
+ /** @structvar r_sav
+ * @brief Number
+ * @note (+) saving throw
+ */
+ s16b r_sav;
+ /** @structvar r_stl
+ * @brief Number
+ * @note (+) stealth
+ */
+ s16b r_stl;
+ /** @structvar r_srh
+ * @brief Number
+ * @note (+) search ability
+ */
+ s16b r_srh;
+ /** @structvar r_fos
+ * @brief Number
+ * @note (+) search frequency
+ */
+ s16b r_fos;
+ /** @structvar r_thn
+ * @brief Number
+ * @note (+) combat (normal)
+ */
+ s16b r_thn;
+ /** @structvar r_thb
+ * @brief Number
+ * @note (+) combat (shooting)
+ */
+ s16b r_thb;
+
+ /** @structvar r_mhp
+ * @brief String
+ * @note (+) Race mod hit-dice modifier
+ */
+ char r_mhp;
+ /** @structvar r_exp
+ * @brief Number
+ * @note (+) Race mod experience factor
+ */
+ s16b r_exp;
+
+ /** @structvar b_age
+ * @brief String
+ * @note (+) base age
+ */
+ char b_age;
+ /** @structvar m_age
+ * @brief String
+ * @note (+) mod age
+ */
+ char m_age;
+
+ /** @structvar m_b_ht
+ * @brief String
+ * @note (+) base height (males)
+ */
+ char m_b_ht;
+ /** @structvar m_m_ht
+ * @brief String
+ * @note (+) mod height (males)
+ */
+ char m_m_ht;
+ /** @structvar m_b_wt
+ * @brief String
+ * @note (+) base weight (males)
+ */
+ char m_b_wt;
+ /** @structvar m_m_wt
+ * @brief String
+ * @note (+) mod weight (males)
+ */
+ char m_m_wt;
+
+ /** @structvar f_b_ht
+ * @brief String
+ * @note (+) base height (females)
+ */
+ char f_b_ht;
+ /** @structvar f_m_ht
+ * @brief String
+ * @note (+) mod height (females)
+ */
+ char f_m_ht;
+ /** @structvar f_b_wt
+ * @brief String
+ * @note (+) base weight (females)
+ */
+ char f_b_wt;
+ /** @structvar f_m_wt
+ * @brief String
+ * @note (+) mod weight (females)
+ */
+ char f_m_wt;
+
+ /** @structvar infra
+ * @brief String
+ * @note (+) Infra-vision range
+ */
+ char infra;
+
+ /** @structvar choice[2]
+ * @brief Number
+ * @note Legal race choices
+ */
+ u32b choice[2];
+
+ /** @structvar pclass[2]
+ * @brief Number
+ * @note Classes allowed
+ */
+ u32b pclass[2];
+ /** @structvar mclass[2]
+ * @brief Number
+ * @note Classes restricted
+ */
+ u32b mclass[2];
+
+ /** @structvar powers[4]
+ * @brief Number
+ * @note Powers of the subrace
+ */
+ s16b powers[4];
+
+ /** @structvar body_parts[BODY_MAX]
+ * @brief String
+ * @note To help to decide what to use when body changing
+ */
+ char body_parts[BODY_MAX];
+
+ /** @structvar flags1
+ * @brief Number
+ */
+ u32b flags1;
+ /** @structvar flags2
+ * @brief Number
+ * @note flags
+ */
+ u32b flags2;
+
+ /** @structvar oflags1[51]
+ * @brief Number
+ */
+ u32b oflags1[51];
+ /** @structvar oflags2[51]
+ * @brief Number
+ */
+ u32b oflags2[51];
+ /** @structvar oflags3[51]
+ * @brief Number
+ */
+ u32b oflags3[51];
+ /** @structvar oflags4[51]
+ * @brief Number
+ */
+ u32b oflags4[51];
+ /** @structvar oflags5[51]
+ * @brief Number
+ */
+ u32b oflags5[51];
+ /** @structvar oesp[51]
+ * @brief Number
+ */
+ u32b oesp[51];
+ /** @structvar opval[51]
+ * @brief Number
+ */
+ s16b opval[51];
+
+ /** @structvar g_attr
+ * @brief Number
+ * @note Overlay graphic attribute
+ */
+ byte g_attr;
+ /** @structvar g_char
+ * @brief String
+ * @note Overlay graphic character
+ */
+ char g_char;
+
+ /** @structvar skill_basem[MAX_SKILLS]
+ * @brief String
+ */
+ char skill_basem[MAX_SKILLS];
+ /** @structvar skill_base[MAX_SKILLS]
+ * @brief Number
+ */
+ u32b skill_base[MAX_SKILLS];
+ /** @structvar skill_modm[MAX_SKILLS]
+ * @brief String
+ */
+ char skill_modm[MAX_SKILLS];
+ /** @structvar skill_mod[MAX_SKILLS]
+ * @brief Number
+ */
+ s16b skill_mod[MAX_SKILLS];
+};
+
+/** @var energy_use
+ * @brief Number
+ * @note Energy use for an action (0 if action does not take a turn).
+ */
+extern s32b energy_use;
+
+/** @var player;
+ * @brief player_type
+ * @note The player.
+ */
+extern player_type *p_ptr @ player;
+
+/** @var max_rp_idx
+ * @brief Number
+ * @note Maximum number of entries in player race array.
+ */
+extern u16b max_rp_idx;
+
+/** @var race_info[max_rp_idx]
+ * @brief player_race
+ * @note Array of player races.
+ */
+extern player_race race_info[max_rp_idx];
+
+/** @var *rp_name
+ * @brief String
+ * @note Name of player race.
+ */
+extern char *rp_name;
+
+/** @var *rp_text
+ * @brief String
+ */
+extern char *rp_text;
+
+/** @var max_rmp_idx
+ * @brief Number
+ * @note Maximum number of player subraces.
+ */
+extern u16b max_rmp_idx;
+
+/** @var _mod race_mod_info[max_rmp_idx]
+ * @brief player_race
+ * @note Array of player subraces.
+ */
+extern player_race_mod race_mod_info[max_rmp_idx];
+
+/** @var *rmp_name
+ * @brief String
+ * @note Name of player subrace.
+ */
+extern char *rmp_name;
+
+/** @var *rmp_text
+ * @brief String
+ */
+extern char *rmp_text;
+
+/** @var class_info[max_c_idx]
+ * @brief player_class
+ * @note Array of classes.
+ */
+extern player_class class_info[max_c_idx];
+
+/** @var *c_name
+ * @brief String
+ * @note Name of player class.
+ */
+extern char *c_name;
+
+/** @var *c_text
+ * @brief String
+ */
+extern char *c_text;
+
+/** @var flush_failure
+ * @brief Boolean
+ * @note TRUE if flush input on any failure, otherwise FALSE.
+ */
+extern bool flush_failure;
+
+/** @fn set_roots(int v, s16b ac, s16b dam)
+ * @brief Player has timed roots.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param ac Number \n bonus to AC
+ * @brief AC bonus
+ * @param dam Number \n bonus to melee to-damage
+ * @brief To-damage bonus
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_roots(int v, s16b ac, s16b dam);
+
+/** @fn set_shadow(int v)
+ * @brief Player has wraith form.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_shadow(int v);
+
+/** @fn set_parasite(int v, int r)
+ * @brief Player has timed parasite.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param r Number \n index of race in monster race array
+ * @brief Parasite race index
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * When the time remaining reaches 0, there is an 80% chance the parasite will
+ * be born, otherwise it will die away.
+ * @note (see file xtra2.c)
+ */
+extern bool set_parasite(int v, int r);
+
+/** @fn set_disrupt_shield(int v)
+ * @brief Player has timed disrupt shield (feels invulnerable).\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_disrupt_shield(int v);
+
+/** @fn set_prob_travel(int v)
+ * @brief Player has timed probability travel.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_prob_travel(int v);
+
+/** @fn set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag)
+ * @brief Player's weapon has a spell effect.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param gf Number \n spell effect
+ * @brief Spell effect
+ * @param dam Number \n damage caused by spell effect
+ * @brief Spell damage
+ * @param rad Number \n radius of spell effect
+ * @brief Spell radius
+ * @param flag Number \n spell projection effect
+ * @brief Spell properties
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag);
+
+/** @fn set_tim_deadly(int v)
+ * @brief Player has deadly accuracy.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_deadly(int v);
+
+/** @fn set_tim_res_time(int v)
+ * @brief Player has timed time resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_res_time(int v);
+
+/** @fn set_tim_reflect(int v)
+ * @brief Player has timed reflection.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_reflect(int v);
+
+/** @fn set_meditation(int v)
+ * @brief Player can meditate (forcibly pseudo-id).\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_meditation(int v);
+
+/** @fn set_strike(int v)
+ * @brief Player has true strike.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_strike(int v);
+
+/** @fn set_walk_water(int v)
+ * @brief Player can walk on water.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_walk_water(int v);
+
+/** @fn set_tim_ffall(int v)
+ * @brief Player has timed levitation (feather-fall).\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_ffall(int v);
+
+/** @fn set_tim_fire_aura(int v)
+ * @brief Player has a timed fiery aura.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_fire_aura(int v);
+
+/** @fn set_tim_regen(int v, int p)
+ * @brief Player has timed regeneration.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n power of regeneration
+ * @brief Regeneration power
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_regen(int v, int p);
+
+/** @fn set_holy(int v)
+ * @brief Player has a timed holy aura.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_holy(int v);
+
+/** @fn set_grace(s32b v)
+ * @brief Set the amount of grace a player has with a god.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range -30,000 to 30,000)
+ * @brief Grace
+ * @note (see file xtra2.c)
+ */
+extern void set_grace(s32b v);
+
+/** @fn set_mimic(int v, int p, int level)
+ * @brief Player has mimic form.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n the mimic form
+ * @brief Mimic form
+ * @param level Number \n the level of the mimic form
+ * @brief Mimic level
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_mimic(int v, int p, int level);
+
+/** @fn set_no_breeders(int v)
+ * @brief Player has timed breeder prevention.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_no_breeders(int v);
+
+/** @fn set_tim_esp(int v)
+ * @brief Player has timed ESP.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_esp(int v);
+
+/** @fn set_invis(int v, int p)
+ * @brief Player has timed invisibility.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n power of invisibility
+ * @brief Invisibility power
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_invis(int v, int p);
+
+/** @fn set_lite(int v)
+ * @brief Player has timed light.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note the use of "PU_VIEW", which is needed to
+ * memorize any terrain features which suddenly become "visible".
+ * @note
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ * @note (see file xtra2.c)
+ */
+extern bool set_lite(int v);
+
+/** @fn set_blind(int v)
+ * @brief Player has timed blindness.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note the use of "PU_UN_VIEW", which is needed to memorize any terrain
+ * features which suddenly become "visible".
+ * @note
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ * @note (see file xtra2.c)
+ */
+extern bool set_blind(int v);
+
+/** @fn set_confused(int v)
+ * @brief Player has timed confusion.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_confused(int v);
+
+/** @fn set_poisoned(int v)
+ * @brief Player has timed poisoning.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_poisoned(int v);
+
+/** @fn set_afraid(int v)
+ * @brief Player has timed fear.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_afraid(int v);
+
+/** @fn set_paralyzed(int v)
+ * @brief Player has timed paralysis.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_paralyzed(int v);
+
+/** @fn set_image(int v)
+ * @brief Player has timed hallucination.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note that we must redraw the map when hallucination changes.
+ * @note (see file xtra2.c)
+ */
+extern bool set_image(int v);
+
+/** @fn set_fast(int v, int p)
+ * @brief Player has timed speed boost.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n speed factor
+ * @brief Speed factor
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_fast(int v, int p);
+
+/** @fn set_light_speed(int v)
+ * @brief Player has timed light speed.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_light_speed(int v);
+
+/** @fn set_slow(int v)
+ * @brief Player has timed slowness.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_slow(int v);
+
+/** @fn set_shield(int v, int p, s16b o, s16b d1, s16b d2)
+ * @brief Player has timed mystic shield.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n bonus to AC
+ * @brief AC bonus
+ * @param o Number \n type of shield (see SHIELD_foo fields)
+ * @brief Shield type
+ * @param d1 Number \n number of dice for damage roll
+ * @brief Damage dice
+ * @param d2 Number \n number of sides per die for damage roll
+ * @brief Damage sides
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_shield(int v, int p, s16b o, s16b d1, s16b d2);
+
+/* For calc_bonus hooks */
+/** @fn apply_flags(u32b f1, u32b f2, u32b f3, u32b f4, u32b f5, u32b esp, s16b pval = 0, s16b tval = 0, s16b to_h = 0, s16b to_d = 0, s16b to_a = 0)
+ * @brief Apply flags and values to the player.\n
+ * @param f1 Number \n flags to be applied to the player
+ * @brief Flag1
+ * @param f2 Number \n flags to be applied to the player
+ * @brief Flag2
+ * @param f3 Number \n flags to be applied to the player
+ * @brief Flag3
+ * @param f4 Number \n flags to be applied to the player
+ * @brief Flag4
+ * @param f5 Number \n flags to be applied to the player
+ * @brief Flag5
+ * @param esp Number \n ESP flag
+ * @brief Esp flag
+ * @param pval Number \n PVal to be applied to the player
+ * @brief Pval
+ * @param tval Number \n TVal to be applied to the player
+ * @brief Tval
+ * @param to_h Number \n to-hit bonus to be applied to the player
+ * @brief To-hit
+ * @param to_d Number \n to-damage bonus to be applied to the player
+ * @brief To-damage
+ * @param to_a Number \n AC bonus to be applied to the player
+ * @brief AC
+ * @note
+ * f1 can apply to attribuets, spell power, mana capacity, stealth, searching
+ * ability and frequency, infravision, digging, speed, extra blows, and
+ * earthquakes.
+ * @note
+ * f2 can apply to life capacity, sensible fire, invisibility, free action,
+ * hold life, immunities (except neither), resistances, reflection, and
+ * sustains.
+ * @note
+ * f3 can apply to extra shots, aggravate, teleport, drain XP, blessed, extra
+ * might, slow digestion, regeneration, lite, see invisible, wraith form,
+ * feather fall, fire sheath, electricity sheath, anti magic, and anti
+ * teleport.
+ * @note
+ * f4 can apply to lite, flying, climbing, nether immunity, precognition, and
+ * anti-magic power and radius.
+ * @note
+ * f5 can apply to luck, critical hits, drain mana, drain life, immovable,
+ * water breath, and magic breath.
+ * @note
+ * esp can apply to, well, just telepathy.
+ * @note
+ * pval can apply to attributes, luck, spell power, mana capacity, life
+ * capacity, stealth, search ability and frequency (x 5), infravision, digging
+ * (x 20), speed, extra blows, critical blows, invisibility (x 10), extra
+ * might, anti-magic power and radius.
+ * @note
+ * tval can apply to lite
+ * @note
+ * to_h, to_d, and to_ac can apply to anti-magic power and radius.
+ * @note (see file xtra1.c)
+ */
+extern void apply_flags(u32b f1, u32b f2, u32b f3, u32b f4, u32b f5, u32b esp, s16b pval = 0, s16b tval = 0, s16b to_h = 0, s16b to_d = 0, s16b to_a = 0);
+
+/** @name Shield effect options
+ * @{ */
+/** @def SHIELD_NONE */
+#define SHIELD_NONE 0x0000
+
+/** @def SHIELD_COUNTER */
+#define SHIELD_COUNTER 0x0001
+
+/** @def SHIELD_FIRE */
+#define SHIELD_FIRE 0x0002
+
+/** @def SHIELD_GREAT_FIRE */
+#define SHIELD_GREAT_FIRE 0x0004
+
+/** @def SHIELD_FEAR */
+#define SHIELD_FEAR 0x0008
+/** @} */
+
+
+/** @fn set_tim_thunder(int v, int p1, int p2)
+ * @brief Player has timed thunderstorm.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p1 Number \n number of dice for damage roll
+ * @brief Damage dice
+ * @param p2 Number \n number of sides per die for damage roll
+ * @brief Damage sides
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_thunder(int v, int p1, int p2);
+
+/** @fn set_tim_breath(int v, bool magical)
+ * @brief Player has timed magic/water breath.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param magical Boolean \n TRUE if player has magic breath, or FALSE if the
+ * player has water breath
+ * @brief Magic breath?
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_breath(int v, bool magical);
+
+/** @fn set_tim_fly(int v)
+ * @brief Player has timed flight.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_fly(int v);
+
+/** @fn set_blessed(int v)
+ * @brief Player has timed blessing.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Blessing gives +5 bonus AC and +10 bonus to-hit.
+ * @note (see file xtra2.c)
+ */
+extern bool set_blessed(int v);
+
+/** @fn set_hero(int v)
+ * @brief Player has timed heroism.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Heroism gives +10 bonus max HP, +12 bonus to-hit, and resist fear.
+ * @note (see file xtra2.c)
+ */
+extern bool set_hero(int v);
+
+/** @fn set_shero(int v)
+ * @brief Player has timed berserk strength.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Berserk strength gives +30 bonus max HP, +24 bonus to-hit, -10 penalty AC,
+ * and resist fear.
+ * @note (see file xtra2.c)
+ */
+extern bool set_shero(int v);
+
+/** @fn set_protevil(int v)
+ * @brief Player has timed protection from evil.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Protection from evil gives the player a chance to repel evil monsters.
+ * @note (see file xtra2.c)
+ */
+extern bool set_protevil(int v);
+
+/** @fn set_protgood(int v)
+ * @brief Player has timed protection from good.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Protection from good gives the player a chance to repel good monsters.
+ * @note (see file xtra2.c)
+ */
+extern bool set_protgood(int v);
+
+/** @fn set_protundead(int v)
+ * @brief Player has timed protection from undead.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Protection from undead protects against getting the Black Breath in a melee
+ * attack.
+ * @note (see file xtra2.c)
+ */
+extern bool set_protundead(int v);
+
+/** @fn set_invuln(int v)
+ * @brief Player has timed invulnerability.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Invulnerability prevents damage from walking on lava, walking the Straight
+ * Road, poison, cuts, and starvation. It gives +100 bonus to AC.
+ * @note
+ * It can be ended by the player attacking a monster, firing a missile,
+ * throwing an object, or activating a power.
+ * @note (see file xtra2.c)
+ */
+extern bool set_invuln(int v);
+
+/** @fn set_tim_invis(int v)
+ * @brief Player has timed "see invisibile".\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_invis(int v);
+
+/** @fn set_tim_infra(int v)
+ * @brief Player has timed infravision.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_infra(int v);
+
+/** @fn set_mental_barrier(int v)
+ * @brief Player has timed mental barrier.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Mental barrier sustains intelligence and wisdom.
+ * @note (see file xtra2.c)
+ */
+extern bool set_mental_barrier(int v);
+
+/** @fn set_poison(int v)
+ * @brief Player has timed poison hands.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_poison(int v);
+
+/** @fn set_oppose_acid(int v)
+ * @brief Player has timed acid resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_acid(int v);
+
+/** @fn set_oppose_elec(int v)
+ * @brief Player has timed electricity resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_elec(int v);
+
+/** @fn set_oppose_fire(int v)
+ * @brief Player has timed fire resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_fire(int v);
+
+/** @fn set_oppose_cold(int v)
+ * @brief Player has timed cold resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_cold(int v);
+
+/** @fn set_oppose_pois(int v)
+ * @brief Player has timed poison resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_pois(int v);
+
+/** @fn set_oppose_ld(int v)
+ * @brief Player has timed light and dark resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_ld(int v);
+
+/** @fn set_oppose_cc(int v)
+ * @brief Player has timed chaos and confusion resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_cc(int v);
+
+/** @fn set_oppose_ss(int v)
+ * @brief Player has timed sound and shard resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_ss(int v);
+
+/** @fn set_oppose_nex(int v)
+ * @brief Player has timed nexus resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_nex(int v);
+
+/** @fn set_stun(int v)
+ * @brief Player stun level changes.\n
+ * @param v Number \n the level of stun (must be in the range 0 to 10,000)
+ * @brief Stun level
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note the special code to only notice "range" changes.
+ * @note
+ * Some races resist stunning.
+ * @note
+ * There is a v chance in 1000 or 1 in 16 that a stun will be the result of
+ * a vicious blow to the head. If so, the player will lose a point of
+ * intelligence, or wisdom, or both unless the stat is sustained.
+ * @note (see file xtra2.c)
+ */
+extern bool set_stun(int v);
+
+/** @fn set_cut(int v)
+ * @brief Player cut level changes.\n
+ * @param v Number \n the level of cut (must be in the range 0 to 10,000)
+ * @brief Cut level
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note the special code to only notice "range" changes.
+ * @note
+ * Some races resist cutting.
+ * @note
+ * There is a v chance in 1000 or 1 in 16 that a cut will result in scarrring.
+ * If so, the player will lose a point of charisma unless it is sustained.
+ * @note (see file xtra2.c)
+ */
+extern bool set_cut(int v);
+
+/** @fn set_food(int v)
+ * @brief Player hunger level changes.\n
+ * @param v Number \n the level of cut (must be in the range 0 to 10,000)
+ * @brief Cut level
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * The "p_ptr->food" variable can get as large as 20000, allowing the
+ * addition of the most "filling" item, Elvish Waybread, which adds
+ * 7500 food units, without overflowing the 32767 maximum limit.
+ * @note
+ * Perhaps we should disturb the player with various messages,
+ * especially messages about hunger status changes. XXX XXX XXX
+ * @note
+ * Digestion of food is handled in "dungeon.c", in which, normally,
+ * the player digests about 20 food units per 100 game turns, more
+ * when "fast", more when "regenerating", less with "slow digestion",
+ * but when the player is "gorged", he digests 100 food units per 10
+ * game turns, or a full 1000 food units per 100 game turns.
+ * @note
+ * Note that the player's speed is reduced by 10 units while gorged,
+ * so if the player eats a single food ration (5000 food units) when
+ * full (15000 food units), he will be gorged for (5000/100)*10 = 500
+ * game turns, or 500/(100/5) = 25 player turns (if nothing else is
+ * affecting the player speed).
+ * @note (see file xtra2.c)
+ */
+extern bool set_food(int v);
+
+/** @name Hunger flags
+ * @brief Player "food" crucial values
+ * @{ */
+/** @def PY_FOOD_MAX
+ * @note Food value (Bloated)
+ */
+#define PY_FOOD_MAX 15000
+/** @def PY_FOOD_FULL
+ * @note Food value (Normal)
+ */
+#define PY_FOOD_FULL 10000
+/** @def PY_FOOD_ALERT
+ * @note Food value (Hungry)
+ */
+#define PY_FOOD_ALERT 2000
+/** @def PY_FOOD_WEAK
+ * @note Food value (Weak)
+ */
+#define PY_FOOD_WEAK 1000
+/** @def PY_FOOD_FAINT
+ * @note Food value (Fainting)
+ */
+#define PY_FOOD_FAINT 500
+/** @def PY_FOOD_STARVE
+ * @note Food value (Starving)
+ */
+#define PY_FOOD_STARVE 100
+/** @} */
+
+/** @fn check_experience(void)
+ * @brief Check if player experience level has changed.\n
+ * @note
+ * If a player has achieved a level for the first time, give a corruption
+ * (1 chance in 3) if it applies, increase skill points, check ability levels,
+ * and add a note if notes are taken.
+ * @note (see file xtra2.c)
+ */
+extern void check_experience(void);
+
+/** @fn check_experience_obj(object_type *o_ptr)
+ * @brief Check if object "o_ptr" experience level has changed.\n
+ * @param *o_ptr object_type \n the object
+ * @brief Object
+ * @note
+ * If an object has achieved a level for the first time, apply gains.
+ * @note (see file xtra2.c)
+ */
+extern void check_experience_obj(object_type *o_ptr);
+
+/** @fn gain_exp(s32b amount)
+ * @brief Gain "amount" of experience.\n
+ * @param amount Number \n the experience points to gain.
+ * @brief Experience
+ * @note
+ * Count the number of objects which will gain experience. The objects share
+ * equally 2/3 of "amount". Give corruption if it applies. Gain experience.
+ * If experience is less than maximum, then increase maximum experience by 20%
+ * of "amount". Check for level change and print experience (check_experience).
+ * @note (see file xtra2.c)
+ */
+extern void gain_exp(s32b amount);
+
+/** @fn lose_exp(s32b amount)
+ * @brief Decrease experience by "amount".\n
+ * @param amount Number \n the experience points to lose.
+ * @brief Experience
+ * @note
+ * Experience can not fall below zero. Check for level change and print
+ * experience (check_experience).
+ * @note (see file xtra2.c)
+ */
+extern void lose_exp(s32b amount);
+
+/** @fn no_lite(void)
+ * @brief Return true if the player's grid is dark.
+ * @return Boolean \n TRUE if the player's grid is dark, otherwise FALSE.
+ * @note (see file cave.c)
+ */
+extern bool no_lite(void);
+
+/** @var dun_level
+ * @brief Number
+ * @note Current dungeon level
+ */
+extern s16b dun_level;
+
+
+/** @name Gods
+ * @{ */
+/** @def GOD_ALL */
+#define GOD_ALL -1
+
+/** @def GOD_NONE */
+#define GOD_NONE 0
+
+/** @def GOD_ERU */
+#define GOD_ERU 1
+
+/** @def GOD_MANWE */
+#define GOD_MANWE 2
+
+/** @def GOD_TULKAS */
+#define GOD_TULKAS 3
+
+/** @def GOD_MELKOR */
+#define GOD_MELKOR 4
+
+/** @def GOD_YAVANNA */
+#define GOD_YAVANNA 5
+/** @} */
+
+
+/** @fn inc_piety(int god, s32b amt)
+ * @brief Increase piety for god "god" by amount "amt".\n
+ * @param god Number \n the god
+ * @brief God
+ * @param amt Number \n the amount of piety
+ * @brief Piety
+ * @note
+ * If the player worships all gods, or "god", the piety (grace) will increase.
+ * @note (see file gods.c)
+ */
+extern void inc_piety(int god, s32b amt);
+
+/** @fn abandon_god(int god)
+ * @brief Player renounces their religion.\n
+ * @param god Number \n the god
+ * @brief God
+ * @note
+ * If the player worships all gods or "god", the player worships no god and
+ * the piety score is set to 0.
+ * @note (see file gods.c)
+ */
+extern void abandon_god(int god);
+
+/** @fn wisdom_scale(int max)
+ * @brief Rescale the wisdom value to a 0 <-> max range.\n
+ * @param max Number \n the new maximum value of the rescaled wisdom
+ * @brief New maximum wisdom
+ * @return Number \n The rescaled value of player wisdom.
+ * @note (see file gods.c)
+ */
+extern int wisdom_scale(int max);
+
+/** @fn follow_god(int god, bool silent)
+ * @brief Player starts to follow god "god".\n
+ * @param god Number \n the god
+ * @brief God
+ * @param silent Boolean \n TRUE if Melkor message is displayed, otherwise
+ * FALSE.
+ * @brief Show message?
+ * @note
+ * Unbelievers can not follow a god.
+ * @note
+ * If the player does not worship a god, they start worshipping "god". If the
+ * god is Melkor, the player gains the Udun skill (show a message if silent is
+ * FALSE).
+ * @note (see file gods.c)
+ */
+extern void follow_god(int god, bool silent);
+
+/** @fn add_new_gods(char *name)
+ * @brief Add a new god to the deity array.\n
+ * @param *name String \n the name of the god
+ * @brief God name
+ * @return Number \n The index of the new god inthe deity array.
+ * @note (see file lua_bind.c)
+ */
+extern s16b add_new_gods(char *name);
+
+/** @fn desc_god(int g_idx, int d, char *desc)
+ * @brief Return line "d" of the description of god with god index "g_idx".\n
+ * @param g_idx Number \n the index of god in the deity array.
+ * @brief God index
+ * @param d Number \n the line of the description
+ * (must be in the range 0 to 9).
+ * @brief Line of description
+ * @param *desc String
+ * @brief Description
+ * @return *desc String \n Line "d" of the god description.
+ * @note (see file lua_bind.c)
+ */
+extern void desc_god(int g_idx, int d, char *desc);
+
+/** @name Powers
+ * @{ */
+/** @def PWR_SPIT_ACID
+ * @note Spit acid (GF_ACID) */
+#define PWR_SPIT_ACID 0
+
+/** @def PWR_BR_FIRE
+ * @note Breathe fire (GF_FIRE) */
+#define PWR_BR_FIRE 1
+
+/** @def PWR_HYPN_GAZE
+ * @note Hypnotic gaze */
+#define PWR_HYPN_GAZE 2
+
+/** @def PWR_TELEKINES
+ * @note Telekinesis (fetch an object) */
+#define PWR_TELEKINES 3
+
+/** @def PWR_VTELEPORT
+ * @note Teleport */
+#define PWR_VTELEPORT 4
+
+/** @def PWR_MIND_BLST
+ * @note Mind blast (GF_PSI) */
+#define PWR_MIND_BLST 5
+
+/** @def PWR_RADIATION
+ * @note Emit radiation (GF_NUKE) */
+#define PWR_RADIATION 6
+
+/** @def PWR_VAMPIRISM
+ * @note Vampire bite */
+#define PWR_VAMPIRISM 7
+
+/** @def PWR_SMELL_MET
+ * @note Detect treasure */
+#define PWR_SMELL_MET 8
+
+/** @def PWR_SMELL_MON
+ * @note Detect normal monsters */
+#define PWR_SMELL_MON 9
+
+/** @def PWR_BLINK
+ * @note Short teleport (up to 10 grids) */
+#define PWR_BLINK 10
+
+/** @def PWR_EAT_ROCK
+ * @note Eat rock for food (wall to mud) */
+#define PWR_EAT_ROCK 11
+
+/** @def PWR_SWAP_POS
+ * @note Swap position with a monster */
+#define PWR_SWAP_POS 12
+
+/** @def PWR_SHRIEK
+ * @note Shriek (GF_SOUND and aggravate) */
+#define PWR_SHRIEK 13
+
+/** @def PWR_ILLUMINE
+ * @note Lite area */
+#define PWR_ILLUMINE 14
+
+/** @def PWR_DET_CURSE
+ * @note Detect cursed items in inventory */
+#define PWR_DET_CURSE 15
+
+/** @def PWR_BERSERK
+ * @note Berserk rage */
+#define PWR_BERSERK 16
+
+/** @def PWR_POLYMORPH
+ * @note Polymorph self */
+#define PWR_POLYMORPH 17
+
+/** @def PWR_MIDAS_TCH
+ * @note Midas touch - turn an item into gold */
+#define PWR_MIDAS_TCH 18
+
+/** @def PWR_GROW_MOLD
+ * @note Summon mold */
+#define PWR_GROW_MOLD 19
+
+/** @def PWR_RESIST
+ * @note Temporary elemental resist */
+#define PWR_RESIST 20
+
+/** @def PWR_EARTHQUAKE
+ * @note Cause an earthquake (destruction) */
+#define PWR_EARTHQUAKE 21
+
+/** @def PWR_EAT_MAGIC
+ * @note Absorb energy from magic items */
+#define PWR_EAT_MAGIC 22
+
+/** @def PWR_WEIGH_MAG
+ * @note Report magic affecting player */
+#define PWR_WEIGH_MAG 23
+
+/** @def PWR_STERILITY
+ * @note Player experiences forced abstinence */
+#define PWR_STERILITY 24
+
+/** @def PWR_PANIC_HIT
+ * @note Hit a monster and run away */
+#define PWR_PANIC_HIT 25
+
+/** @def PWR_DAZZLE
+ * @note Stun, confuse, and turn monsters */
+#define PWR_DAZZLE 26
+
+/** @def PWR_DARKRAY
+ * @note Fire a beam of light (GF_LITE) */
+#define PWR_DARKRAY 27
+
+/** @def PWR_RECALL
+ * @note Recall to dungeon/town */
+#define PWR_RECALL 28
+
+/** @def PWR_BANISH
+ * @note Banish evil creatures */
+#define PWR_BANISH 29
+
+/** @def PWR_COLD_TOUCH
+ * @note Bolt of cold (GF_COLD) */
+#define PWR_COLD_TOUCH 30
+
+/** @def PWR_LAUNCHER
+ * @note Increase the multiplier for a thrown object */
+#define PWR_LAUNCHER 31
+
+/** @def PWR_PASSWALL
+ * @note Walk through a wall */
+#define PWR_PASSWALL 32
+
+/** @def PWR_DETECT_TD
+ * @note Detect traps, doors, and stairs */
+#define PWR_DETECT_TD 33
+
+/** @def PWR_COOK_FOOD
+ * @note Create some food */
+#define PWR_COOK_FOOD 34
+
+/** @def PWR_UNFEAR
+ * @note Remove fear */
+#define PWR_UNFEAR 35
+
+/** @def PWR_EXPL_RUNE
+ * @note Set an explosive rune */
+#define PWR_EXPL_RUNE 36
+
+/** @def PWR_STM
+ * @note Bash a wall (stone to mud) */
+#define PWR_STM 37
+
+/** @def PWR_POIS_DART
+ * @note Throw a poison dart (GF_POIS) */
+#define PWR_POIS_DART 38
+
+/** @def PWR_MAGIC_MISSILE
+ * @note Fire a magic missile (GF_MISSILE) */
+#define PWR_MAGIC_MISSILE 39
+
+/** @def PWR_GROW_TREE
+ * @note Grow trees around the player */
+#define PWR_GROW_TREE 40
+
+/** @def PWR_BR_COLD
+ * @note Breathe cold (GF_COLD) */
+#define PWR_BR_COLD 41
+
+/** @def PWR_BR_CHAOS
+ * @note Breathe chaos (GF_CHAOS) */
+#define PWR_BR_CHAOS 42
+
+/** @def PWR_BR_ELEM
+ * @note Breath elements (GF_MISSILE) */
+#define PWR_BR_ELEM 43
+
+/** @def PWR_WRECK_WORLD
+ * @note Change the world (new level) */
+#define PWR_WRECK_WORLD 44
+
+/** @def PWR_SCARE
+ * @note Howl to scare monsters */
+#define PWR_SCARE 45
+
+/** @def PWR_REST_LIFE
+ * @note Restore life levels */
+#define PWR_REST_LIFE 46
+
+/** @def PWR_SUMMON_MONSTER
+ * @note Beastmaster powers (summon pets) */
+#define PWR_SUMMON_MONSTER 47
+
+/** @def PWR_NECRO
+ * @note Cast a necromancy spell */
+#define PWR_NECRO 48
+
+/** @def PWR_ROHAN
+ * @note Use flash aura or light speed jump */
+#define PWR_ROHAN 49
+
+/** @def PWR_THUNDER
+ * @note Use thunder strike, ride the straight road, or go back in town */
+#define PWR_THUNDER 50
+
+/** @def PWR_DEATHMOLD
+ * @note Use deathmold powers:\n
+ * (a) Teleport to a specific place\n
+ * (b) Fetch an item\n
+ * (c) Go up 50'\n
+ * (d) Go down 50'
+ */
+#define PWR_DEATHMOLD 51
+
+/** @def PWR_HYPNO
+ * @note Hypnotise a pet */
+#define PWR_HYPNO 52
+
+/** @def PWR_UNHYPNO
+ * @note Unhypnotise a pet */
+#define PWR_UNHYPNO 53
+
+/** @def PWR_INCARNATE
+ * @note Incarnate into a body */
+#define PWR_INCARNATE 54
+
+/** @def PWR_MAGIC_MAP
+ * @note Magic mapping */
+#define PWR_MAGIC_MAP 55
+
+/** @def PWR_LAY_TRAP
+ * @note Set a trap */
+#define PWR_LAY_TRAP 56
+
+/** @def PWR_MERCHANT
+ * @note Appraise item, warp item, or identify item */
+#define PWR_MERCHANT 57
+
+/** @def PWR_COMPANION
+ * @note Create a companion */
+#define PWR_COMPANION 58
+
+/** @def PWR_BEAR
+ * @note Mimic a bear */
+#define PWR_BEAR 59
+
+/** @def PWR_DODGE
+ * @note Report chance of dodging a monster */
+#define PWR_DODGE 60
+
+/** @def PWR_BALROG
+ * @note Mimic a balrog */
+#define PWR_BALROG 61
+/** @} */
+
+/* Misc */
+/** @fn do_cmd_throw(void)
+ * @brief Throw an object from the pack or floor.
+ * @note
+ * Note: "unseen" monsters are very hard to hit.
+ * @note
+ * Should throwing a weapon do full damage? Should it allow the magic
+ * to hit bonus of the weapon to have an effect? Should it ever cause
+ * the item to be destroyed? Should it do any damage at all?
+ * @note (see file cmd2.c)
+ */
+extern void do_cmd_throw(void);
+
+/** @fn change_wild_mode()
+ * @brief Toggle between big map and little map.
+ * @note
+ * If the player is immovable, and the map is big, the player receives a
+ * warning and is allowed to proceed.
+ * @note
+ * If the player is about to be recalled, and the map is big, the map is
+ * not changed.
+ * @note
+ * The map is changed. The game is saved if autosave is set to "levels".
+ * @note (see file spells2.c)
+ */
+extern void change_wild_mode();
+
+/** @fn switch_class(int sclass)
+ * @brief Change to an other class.\n
+ * @param sclass Number \n the inex of the new class in the class array
+ * @brief Class index
+ * @note (see file xtra2.c)
+ */
+extern void switch_class(int sclass);
+
+/** @fn switch_subclass(int sclass)
+ * @brief Change to an other subclass.\n
+ * @param sclass Number \n the new subclass
+ * @brief Subclass
+ * @note (see file xtra2.c)
+ */
+extern void switch_subclass(int sclass);
+
+/** @fn switch_subrace(int racem, bool copy_old)
+ * @brief Change to an other subrace.\n
+ * @param racem Number \n index of subrace in subrace array
+ * @brief Subrace index
+ * @param copy_old Boolean \n TRUE if the new subrace is to be saved,
+ * otherwise FALSE.
+ * @brief Copy old subrace?
+ * @note (see file xtra2.c)
+ */
+extern void switch_subrace(int racem, bool copy_old);
+
+/** @fn get_subrace_title(int racem)
+ * @brief Return the subrace title.\n
+ * @param racem Number \n index of subrace in subrace array
+ * @brief Subrace index
+ * @return String \n Title of subrace.
+ * @note (see file xtra2.c)
+ */
+extern cptr get_subrace_title(int racem);
+
+/** @fn set_subrace_title(int racem, cptr name)
+ * @brief Set the subrace title.\n
+ * @param racem Number \n index of subrace in subrace array
+ * @brief Subrace index
+ * @param name String \n new title of subrace
+ * @brief New title
+ * @note (see file xtra2.c)
+ */
+extern void set_subrace_title(int racem, cptr name);
+
+/** @fn do_rebirth()
+ * @brief The player is reborn after a class, race, or subrace change.
+ * @note
+ * The experience factor is recalculated. The hit dice are reset and new HP
+ * are calculated. There may be a level change involved.
+ * @note (see file xtra2.c)
+ */
+extern void do_rebirth();
+
+/* Player race flags */
+$static bool lua_test_race_flags(int slot, u32b flags) { if (slot == 1) return (PRACE_FLAG(flags)) ? TRUE : FALSE; else return (PRACE_FLAG2(flags)) ? TRUE : FALSE; }
+/** @fn test_race_flags(int slot, u32b flags);
+ * @brief Test flag "flags" against race flags, race modifier flags, class
+ * flags, and specialist flags.\n
+ * @param slot Number \n 1 if testing against first set of flags (PRACE_FLAG),
+ * 2 if testing against second set of flags (PRACE_FLAG2)
+ * @brief Flag selecter.
+ * @param flags Number \n the flags to be tested
+ * @brief Test flags
+ * @return Boolean \n TRUE if test flags match any of the corresponding race,
+ * race modifier, class, and specialist flags.
+ * @note (see file w_player.c)
+ */
+static bool lua_test_race_flags@test_race_flags(int slot, u32b flags);
+
+/** @name Winner states
+ * @{ */
+/** @def WINNER_NORMAL
+ * @note Player has killed Morgoth */
+#define WINNER_NORMAL 1
+
+/** @def WINNER_ULTRA
+ * @note Player has killed Melkor */
+#define WINNER_ULTRA 2
+/** @} */
+
+/** @var wizard
+ * @brief Boolean
+ * @note TRUE if player currently in Wizard mode, otherwise FALSE.
+ */
+extern bool wizard;
+
+/** @var total_winner
+ * @brief Number
+ * @note Game has been won (see WINNER_foo fields).
+ */
+extern u16b total_winner;
+
+/** @var has_won
+ * @brief Number
+ * @note Game has been won (see WINNER_foo fields).
+ */
+extern u16b has_won;
+
+/** @var joke_monsters
+ * @brief Boolean
+ * @note TRUE if allowing joke monsters, otherwise FALSE.
+ */
+extern bool joke_monsters;
+
+extern s16b max_dlv[999999];
diff --git a/src/player_c.pkg b/src/player_c.pkg
new file mode 100644
index 00000000..f55f9325
--- /dev/null
+++ b/src/player_c.pkg
@@ -0,0 +1,1060 @@
+/* File: player_c.pkg */
+
+/*
+ * Purpose: Lua interface defitions for player classes.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @struct player_class
+ * @brief Player class
+ */
+struct player_class
+{
+ /** @structvar title
+ * @brief Number
+ * @note Type of class
+ */
+ s32b title;
+ /** @structvar desc
+ * @brief Number
+ * @note Small desc of the class
+ */
+ s32b desc;
+ /** @structvar titles[10]
+ * @brief Number
+ */
+ s32b titles[10];
+
+ /** @structvar c_adj[6]
+ * @brief Number
+ * @note Class stat modifier
+ */
+ s16b c_adj[6];
+
+ /** @structvar c_dis
+ * @brief Number
+ * @note class disarming
+ */
+ s16b c_dis;
+ /** @structvar c_dev
+ * @brief Number
+ * @note class magic devices
+ */
+ s16b c_dev;
+ /** @structvar c_sav
+ * @brief Number
+ * @note class saving throws
+ */
+ s16b c_sav;
+ /** @structvar c_stl
+ * @brief Number
+ * @note class stealth
+ */
+ s16b c_stl;
+ /** @structvar c_srh
+ * @brief Number
+ * @note class searching ability
+ */
+ s16b c_srh;
+ /** @structvar c_fos
+ * @brief Number
+ * @note class searching frequency
+ */
+ s16b c_fos;
+ /** @structvar c_thn
+ * @brief Number
+ * @note class to hit (normal)
+ */
+ s16b c_thn;
+ /** @structvar c_thb
+ * @brief Number
+ * @note class to hit (bows)
+ */
+ s16b c_thb;
+
+ /** @structvar x_dis
+ * @brief Number
+ * @note extra disarming
+ */
+ s16b x_dis;
+ /** @structvar x_dev
+ * @brief Number
+ * @note extra magic devices
+ */
+ s16b x_dev;
+ /** @structvar x_sav
+ * @brief Number
+ * @note extra saving throws
+ */
+ s16b x_sav;
+ /** @structvar x_stl
+ * @brief Number
+ * @note extra stealth
+ */
+ s16b x_stl;
+ /** @structvar x_srh
+ * @brief Number
+ * @note extra searching ability
+ */
+ s16b x_srh;
+ /** @structvar x_fos
+ * @brief Number
+ * @note extra searching frequency
+ */
+ s16b x_fos;
+ /** @structvar x_thn
+ * @brief Number
+ * @note extra to hit (normal)
+ */
+ s16b x_thn;
+ /** @structvar x_thb
+ * @brief Number
+ * @note extra to hit (bows)
+ */
+ s16b x_thb;
+
+ /** @structvar c_mhp
+ * @brief Number
+ * @note Class hit-dice adjustment
+ */
+ s16b c_mhp;
+ /** @structvar c_exp
+ * @brief Number
+ * @note Class experience factor
+ */
+ s16b c_exp;
+
+ /** @structvar powers[4]
+ * @brief Number
+ * @note Powers of the class
+ */
+ s16b powers[4];
+
+ /** @structvar spell_book
+ * @brief Number
+ * @note Tval of spell books (if any)
+ */
+ s16b spell_book;
+ /** @structvar spell_stat
+ * @brief Number
+ * @note Stat for spells (if any)
+ */
+ s16b spell_stat;
+ /** @structvar spell_lev
+ * @brief Number
+ * @note The higher it is the higher the spells level are
+ */
+ s16b spell_lev;
+ /** @structvar spell_fail
+ * @brief Number
+ * @note The higher it is the higher the spells failure are
+ */
+ s16b spell_fail;
+ /** @structvar spell_mana
+ * @brief Number
+ * @note The higher it is the higher the spells mana are
+ */
+ s16b spell_mana;
+ /** @structvar spell_first
+ * @brief Number
+ * @note Level of first spell
+ */
+ s16b spell_first;
+ /** @structvar spell_weight
+ * @brief Number
+ * @note Weight that hurts spells
+ */
+ s16b spell_weight;
+ /** @structvar max_spell_level
+ * @brief Number
+ * @note Maximun spell level
+ */
+ byte max_spell_level;
+ /** @structvar magic_max_spell
+ * @brief Number
+ * @note Maximun numbner of spells one can learn by natural means
+ */
+ byte magic_max_spell;
+
+ /** @structvar flags1
+ * @brief Number
+ * @note flags
+ */
+ s32b flags1;
+
+ /** @structvar mana
+ * @brief Number
+ */
+ s16b mana;
+ /** @structvar blow_num
+ * @brief Number
+ */
+ s16b blow_num;
+ /** @structvar blow_wgt
+ * @brief Number
+ */
+ s16b blow_wgt;
+ /** @structvar blow_mul
+ * @brief Number
+ */
+ s16b blow_mul;
+ /** @structvar extra_blows
+ * @brief Number
+ */
+ s16b extra_blows;
+
+ /** @structvar sense_base
+ * @brief Number
+ */
+ s32b sense_base;
+ /** @structvar sense_pl
+ * @brief Number
+ */
+ s32b sense_pl;
+ /** @structvar sense_plus
+ * @brief Number
+ */
+ s32b sense_plus;
+ /** @structvar sense_heavy
+ * @brief Number
+ */
+ byte sense_heavy;
+ /** @structvar sense_heavy_magic
+ * @brief Number
+ */
+ byte sense_heavy_magic;
+};
+
+/** @var *cp_ptr
+ * @brief player_class
+ * @note Player class information
+ */
+extern player_class *cp_ptr;
+
+
+
+/** @struct skill_type
+ * @brief Skills
+ */
+struct skill_type
+{
+ /** @structvar name
+ * @brief Number
+ * @note Name
+ */
+ u32b name;
+ /** @structvar desc
+ * @brief Number
+ * @note Description
+ */
+ u32b desc;
+ /** @structvar action_desc
+ * @brief Number
+ * @note Action Description
+ */
+ u32b action_desc;
+
+ /** @structvar action_mkey
+ * @brief Number
+ * @note Action do to
+ */
+ s16b action_mkey;
+
+ /** @structvar i_value
+ * @brief Number
+ * @note Actual value
+ */
+ u32b i_value;
+ /** @structvar i_mod
+ * @brief Number
+ * @note Modifier(1 skill point = modifier skill)
+ */
+ u16b i_mod;
+
+ /** @structvar value
+ * @brief Number
+ * @note Actual value
+ */
+ u32b value;
+ /** @structvar mod
+ * @brief Number
+ * @note Modifier(1 skill point = modifier skill)
+ */
+ u16b mod;
+ /** @structvar rate
+ * @brief Number
+ * @note Modifier decreasing rate
+ */
+ s16b rate;
+
+ /** @structvar uses
+ * @brief Number
+ * @note Number of times used
+ */
+ u32b uses;
+
+ /** @structvar action[9999]
+ * @brief Number
+ * @note List of actions against other skills
+ */
+ s16b action[9999];
+
+ /** @structvar father
+ * @brief Number
+ * @note Father in the skill tree
+ */
+ s16b father;
+ /** @structvar dev
+ * @brief Boolean
+ * @note Is the branch developped ?
+ */
+ bool dev;
+ /** @structvar order
+ * @brief Number
+ * @note Order in the tree
+ */
+ s16b order;
+ /** @structvar hidden
+ * @brief Boolean
+ * @note Innactive
+ */
+ bool hidden;
+};
+
+/** @def MAX_SKILLS
+ * @brief Maximum number of skills
+ */
+#define MAX_SKILLS 100
+
+
+$static cptr get_skill_name(int i) { return s_name + s_info[i].name; }
+/** @fn get_skill_name(int i)
+ * @brief Return name of skill with index "i" in skill array.\n
+ * @param i Number \n the index of skill in skill array.
+ * @brief Skill index
+ * @return String \n The name of the skill with index "i" in the skill array.
+ * @note (see file w_play_c.c)
+ */
+static cptr get_skill_name(int i);
+
+/** @var old_max_s_idx
+ * @brief Number
+ * @note Previous maximum skill index
+ */
+extern u16b old_max_s_idx;
+/** @var max_s_idx
+ * @brief Number
+ * @note Current maximum skill index
+ */
+extern u16b max_s_idx;
+/** @var s_info[MAX_SKILLS]
+ * @brief skill_type
+ * @note Array of player skills
+ */
+skill_type s_info[MAX_SKILLS];
+
+/** @name Skills
+ * @{ */
+/** @def SKILL_CONVEYANCE
+ * @brief Conveyance
+ * @note
+ * Ability to learn and use spells from the Conveyance school
+ */
+#define SKILL_CONVEYANCE 1
+
+/** @def SKILL_MANA
+ * @brief Mana
+ * @note
+ * Ability to learn and use spells from the Mana school
+ */
+#define SKILL_MANA 2
+
+/** @def SKILL_FIRE
+ * @brief Fire
+ * @note
+ * Ability to learn and use spells from the Fire school
+ */
+#define SKILL_FIRE 3
+
+/** @def SKILL_AIR
+ * @brief Air
+ * @note
+ * Ability to learn and use spells from the Air school
+ */
+#define SKILL_AIR 4
+
+/** @def SKILL_WATER
+ * @brief Water
+ * @note
+ * Ability to learn and use spells from the Water school
+ */
+#define SKILL_WATER 5
+
+/** @def SKILL_NATURE
+ * @brief Nature
+ * @note
+ * Ability to learn and use spells from the Nature school
+ */
+#define SKILL_NATURE 6
+
+/** @def SKILL_EARTH
+ * @brief Earth
+ * @note
+ * Ability to learn and use spells from the Earth school
+ */
+#define SKILL_EARTH 7
+
+/** @def SKILL_SYMBIOTIC
+ * @brief Symbiosis
+ * @note
+ * Ability to enter in symbiosis with monsters unable to move by themselves
+ */
+#define SKILL_SYMBIOTIC 8
+
+/** @def SKILL_MUSIC
+ * @brief Music
+ * @note
+ * Ability to learn and sing songs
+ */
+#define SKILL_MUSIC 9
+
+/** @def SKILL_DIVINATION
+ * @brief Divination
+ * @note
+ * Ability to learn and use spells from the Divination school
+ */
+#define SKILL_DIVINATION 10
+
+/** @def SKILL_TEMPORAL
+ * @brief Temporal
+ * @note
+ * Ability to learn and use spells from the Temporal school
+ */
+#define SKILL_TEMPORAL 11
+
+/** @def SKILL_DRUID
+ * @brief Druidistic
+ * @note
+ * Ability to learn and use prayers from the Druidistic realm
+ */
+#define SKILL_DRUID 12
+
+/** @def SKILL_DAEMON
+ * @brief Demonology
+ * @note
+ * Ability to use incantations from the Demonblades
+ */
+#define SKILL_DAEMON 13
+
+/** @def SKILL_META
+ * @brief Meta
+ * @note
+ * Ability to learn and use spells from the Meta school
+ */
+#define SKILL_META 14
+
+/** @def SKILL_MAGIC
+ * @brief Magic
+ * @note
+ * General ability to do magic, also affect mana reserves and
+ * magic device ability. Helps pseudo-id of magic objects
+ */
+#define SKILL_MAGIC 15
+
+/** @def SKILL_COMBAT
+ * @brief Combat
+ * @note
+ * General ability to fight and to pseudo-id armours and weapons.
+ * It also allows to use heavier armours without penalties
+ */
+#define SKILL_COMBAT 16
+
+/** @def SKILL_MASTERY
+ * @brief Weaponmastery
+ * @note
+ * General ability to use melee weapons
+ */
+#define SKILL_MASTERY 17
+
+/** @def SKILL_SWORD
+ * @brief Sword-mastery
+ * @note
+ * Ability to use swords
+ */
+#define SKILL_SWORD 18
+
+/** @def SKILL_AXE
+ * @brief Axe-mastery
+ * @note
+ * Ability to use axes
+ */
+#define SKILL_AXE 19
+
+/** @def SKILL_POLEARM
+ * @brief Polearm-mastery
+ * @note
+ * Ability to use polearms
+ */
+#define SKILL_POLEARM 20
+
+/** @def SKILL_HAFTED
+ * @brief Hafted-mastery
+ * @note
+ * Ability to use hafted weapons
+ */
+#define SKILL_HAFTED 21
+
+/** @def SKILL_BACKSTAB
+ * @brief Backstab
+ * @note
+ * Ability to backstab fleeing and sleeping monsters to increase damage
+ */
+#define SKILL_BACKSTAB 22
+
+/** @def SKILL_ARCHERY
+ * @brief Archery
+ * @note
+ * General ability to use ranged weapons
+ */
+#define SKILL_ARCHERY 23
+
+/** @def SKILL_SLING
+ * @brief Sling-mastery
+ * @note
+ * Ability to use slings
+ */
+#define SKILL_SLING 24
+
+/** @def SKILL_BOW
+ * @brief Bow-mastery
+ * @note
+ * Ability to use bows
+ */
+#define SKILL_BOW 25
+
+/** @def SKILL_XBOW
+ * @brief Crossbow-mastery
+ * @note
+ * Ability to use crossbows
+ */
+#define SKILL_XBOW 26
+
+/** @def SKILL_BOOMERANG
+ * @brief Boomerang-mastery
+ * @note
+ * Ability to use boomerangs
+ */
+#define SKILL_BOOMERANG 27
+
+/** @def SKILL_SPIRITUALITY
+ * @brief Spirituality
+ * @note
+ * General ability to use spiritual skills and also influence Saving Throw
+ */
+#define SKILL_SPIRITUALITY 28
+
+/** @def SKILL_MINDCRAFT
+ * @brief Mindcraft
+ * @note
+ * Ability to focus the powers of the mind
+ */
+#define SKILL_MINDCRAFT 29
+
+/** @def SKILL_MISC
+ * @brief Misc
+ * @note
+ * Not a real skill, it is only used to regroup some skills
+ */
+#define SKILL_MISC 30
+
+/** @def SKILL_NECROMANCY
+ * @brief Necromancy
+ * @note
+ * Ability to harness the powers of the dead
+ */
+#define SKILL_NECROMANCY 31
+
+/** @def SKILL_MIMICRY
+ * @brief Mimicry
+ * @note
+ * Ability to use cloaks of mimicry to change form
+ */
+#define SKILL_MIMICRY 32
+
+/** @def SKILL_ANTIMAGIC
+ * @brief Antimagic
+ * @note
+ * Ability to generates an antimagic field
+ */
+#define SKILL_ANTIMAGIC 33
+
+/** @def SKILL_RUNECRAFT
+ * @brief Runecraft
+ * @note
+ * Ability to combine magic runes to create your own spells
+ */
+#define SKILL_RUNECRAFT 34
+
+/** @def SKILL_SNEAK
+ * @brief Sneakiness
+ * @note
+ * General ability at the sneakiness skills
+ */
+#define SKILL_SNEAK 35
+
+/** @def SKILL_STEALTH
+ * @brief Stealth
+ * @note
+ * Ability to move unnoticed, silently
+ */
+#define SKILL_STEALTH 36
+
+/** @def SKILL_DISARMING
+ * @brief Disarming
+ * @note
+ * Ability to disarm the various traps
+ */
+#define SKILL_DISARMING 37
+
+/* XXX */
+
+/** @def SKILL_ALCHEMY
+ * @brief Alchemy
+ * @note
+ * Ability to use essences to modify/create magic items
+ */
+#define SKILL_ALCHEMY 39
+
+/** @def SKILL_STEALING
+ * @brief Stealing
+ * @note
+ * Ability to steal objects
+ */
+#define SKILL_STEALING 40
+
+/** @def SKILL_SORCERY
+ * @brief Sorcery
+ * @note
+ * Ability to use all the magic schools as if their skill was sorcery
+ */
+#define SKILL_SORCERY 41
+
+/** @def SKILL_HAND
+ * @brief Barehand-combat
+ * @note
+ * Ability to fight barehanded
+ */
+#define SKILL_HAND 42
+
+/** @def SKILL_THAUMATURGY
+ * @brief Thaumaturgy
+ * @note
+ * Ability to gain and cast innate spells
+ */
+#define SKILL_THAUMATURGY 43
+
+/** @def SKILL_SUMMON
+ * @brief Summoning
+ * @note
+ * Ability to create totems from monsters and use them to summon monsters
+ */
+#define SKILL_SUMMON 44
+
+/** @def SKILL_SPELL
+ * @brief Spell-power
+ * @note
+ * Ability to increase the power of spells
+ */
+#define SKILL_SPELL 45
+
+/** @def SKILL_DODGE
+ * @brief Dodging
+ * @note
+ * Ability to dodge blows and bolts
+ */
+#define SKILL_DODGE 46
+
+/** @def SKILL_BEAR
+ * @brief Bearform-combat
+ * @note
+ * Ability to fight in bear form
+ */
+#define SKILL_BEAR 47
+
+/** @def SKILL_LORE
+ * @brief Monster-lore
+ * @note
+ * General ability at the monster related skills, ability to gain experience
+ * from friendly kills. It also affects the number of companions the player
+ * can have
+ */
+#define SKILL_LORE 48
+
+/** @def SKILL_PRESERVATION
+ * @brief Corpse-preservation
+ * @note
+ * Ability to not destroy the monster corpse when killing them
+ */
+#define SKILL_PRESERVATION 49
+
+/** @def SKILL_POSSESSION
+ * @brief Possession
+ * @note
+ * Ability to incarnate into monsters
+ */
+#define SKILL_POSSESSION 50
+
+/** @def SKILL_MIND
+ * @brief Mind
+ * @note
+ * Ability to learn and use spells from the Mind school
+ */
+#define SKILL_MIND 51
+
+/** @def SKILL_CRITS
+ * @brief Critical-hits
+ * @note
+ * Ability to deal critical hits with swords < 5lb
+ */
+#define SKILL_CRITS 52
+
+/** @def SKILL_PRAY
+ * @brief Prayer
+ * @note
+ * Ability to learn and use spells from the gods schools
+ */
+#define SKILL_PRAY 53
+
+/** @def SKILL_LEARN
+ * @brief Spell-learning
+ * @note
+ * You should not see that ! that is a BUG!
+ */
+#define SKILL_LEARN 54
+
+/** @def SKILL_UDUN
+ * @brief Udun
+ * @note
+ * Ability to learn and use spells from the Udun school
+ */
+#define SKILL_UDUN 55
+
+/** @def SKILL_DEVICE
+ * @brief Magic-Device
+ * @note
+ * Ease the use of magical devices, such as wands, staves and rods.
+ * It also helps pseudo-id of magic objects
+ */
+#define SKILL_DEVICE 56
+
+/** @def SKILL_STUN
+ * @brief Stunning-blows
+ * @note
+ * Ability to stun opponents when doing critical hits with hafted weapons > 5lb
+ */
+#define SKILL_STUN 57
+
+/** @def SKILL_BOULDER
+ * @brief Boulder-throwing
+ * @note
+ * Ability to make and throw boulders
+ */
+#define SKILL_BOULDER 58
+
+/** @def SKILL_GEOMANCY
+ * @brief Geomancy
+ * @note
+ * Ability to understand the raw elemental forces of nature and use
+ * them to advantage. Most spells need Fire/Water/Earth/Air skills
+ */
+#define SKILL_GEOMANCY 59
+
+
+/** @def SKILL_MAX
+ * @note Maximun skill value
+ */
+#define SKILL_MAX 50000
+/** @def SKILL_STEP
+ * @note 1 skill point
+ */
+#define SKILL_STEP 1000
+
+/** @} */
+
+/** @fn get_skill(int skill)
+ * @brief Return the value of skill with index "skill" in skill array.\n
+ * @param skill Number \n the index of skill in skill array.
+ * @brief Skill index
+ * @return Number \n The value of the skill with index "skill" in the skill
+ * array.
+ * @note (see file skills.c)
+ */
+extern s16b get_skill(int skill);
+
+/** @fn get_skill_scale(int skill, u32b scale)
+ * @brief Return the value of skill with index "skill" in skill array rescaled
+ * to a maximum of "scale".\n
+ * @param skill Number \n the index of skill in skill array.
+ * @brief Skill index
+ * @param scale Number \n the maximum rescaled skill value.
+ * @brief Scaled maximum
+ * @return Number \n The rescaled value of the skill with index "skill" in the
+ * skill array.
+ * @note (see file skills.c)
+ */
+extern s16b get_skill_scale(int skill, u32b scale);
+
+/** @fn do_get_new_skill()
+ * @brief Player select one of four new skills.
+ * @note (see file skills.c)
+ */
+extern void do_get_new_skill();
+
+/** @fn get_melee_skills()
+ * @brief Return the number of melee skills the player has.
+ * @return Number \n The number of melee skills.
+ * @note
+ * A skill is counted if the value > 0 and the skill is not hidden.
+ * @note (see file skills.c)
+ */
+extern s16b get_melee_skills();
+
+/** @fn find_skill(cptr name)
+ * @brief Return the index of skill with name "name".\n
+ * @param name String \n the name of the skill.
+ * @brief Skill name
+ * @return Number \n The index of the skill with name "name" in the skill
+ * array.
+ * @note
+ * The search is case sensitive.\n
+ * If no skills match the name, -1 is returned.
+ * @note (see file skills.c)
+ */
+extern s16b find_skill(cptr name);
+
+/** @fn find_skill_i(cptr name)
+ * @brief Return the index of skill with name "name".\n
+ * @param name String \n the name of the skill.
+ * @brief Skill name
+ * @return Number \n The index of the skill with name "name" in the skill
+ * array.
+ * @note
+ * The search ignores case.\n
+ * If no skills match the name, -1 is returned.
+ * @note (see file skills.c)
+ */
+extern s16b find_skill_i(cptr name);
+
+$static char *get_class_name() {return spp_ptr->title + c_name;}
+/** @fn *get_class_name()
+ * @brief Return the player's class.
+ * @return String \n The player's type of class + class name
+ * @note (see file w_play_c.c)
+ */
+char *get_class_name();
+
+$static char *get_race_name() {return rp_ptr->title + rp_name;}
+/** @fn *get_race_name()
+ * @brief Return the player's race.
+ * @return String \n The player's type of race + race name
+ * @note (see file w_play_c.c)
+ */
+char *get_race_name();
+
+$static char *get_subrace_name() {return rmp_ptr->title + rmp_name;}
+/** @fn *get_subrace_name()
+ * @brief Return the player's subrace.
+ * @return String \n The player's type of subrace + subrace name
+ * @note (see file w_play_c.c)
+ */
+char *get_subrace_name();
+
+/** @struct ability_type
+ * @brief Abilities
+ */
+struct ability_type
+{
+ /** @structvar action_mkey
+ * @brief Number
+ * @note Action do to
+ */
+ s16b action_mkey;
+
+ /** @structvar cost
+ * @brief Number
+ * @note Skill points cost
+ */
+ s16b cost;
+
+ /** @structvar acquired
+ * @brief Boolean
+ * @note Do the player actualylg ot it ?
+ */
+ bool acquired;
+};
+
+/** @fn find_ability(cptr name)
+ * @brief Return the index of ability with name "name".\n
+ * @param name String \n the name of the ability.
+ * @brief Ability name
+ * @return Number \n The index of the ability with name "name" in the ability
+ * array.
+ * @note
+ * The search is case sensitive.\n
+ * If no abilities match the name, -1 is returned.
+ * @note (see file skills.c)
+ */
+extern s16b find_ability(cptr name);
+
+/** @fn do_cmd_ability()
+ * @brief Allow the user to interact with abilities.
+ * @note
+ * This screen is typically used to view abilities, and increase them.
+ * @note (see file skills.c)
+ */
+extern void do_cmd_ability();
+
+/** @fn has_ability(int ab)
+ * @brief Does the player have ability "ab"?
+ * @param ab Number \n the index of ability in ability array.
+ * @brief Ability index
+ * @return Boolean \n TRUE if player has the ability, otherwise FALSE.
+ * @note (see file skills.c)
+ */
+extern bool has_ability(int ab);
+
+/** @var max_ab_idx
+ * @brief Number
+ * @note Maximum ability index
+ */
+extern s16b max_ab_idx;
+/** @var ab_info[max_ab_idx]
+ * @brief ability_type
+ * @note Array of player abilities
+ */
+extern ability_type ab_info[max_ab_idx];
+
+/** @name Abilities
+ * @{ */
+/** @def AB_SPREAD_BLOWS
+ * @brief Spread blows
+ * @note
+ * If a monster dies to an attack but the player still has blows left
+ * they won't lose the full turn, allowing them to attack some other
+ * monster in the same turn.
+ */
+#define AB_SPREAD_BLOWS 0
+
+/** @def AB_TREE_WALK
+ * @brief Tree walking
+ * @note
+ * Allows player to walk in dense forest.
+ */
+#define AB_TREE_WALK 1
+
+/** @def AB_PERFECT_CASTING
+ * @brief Perfect casting
+ * @note
+ * Allows player to reach 0% failure rate on spells.
+ */
+#define AB_PERFECT_CASTING 2
+
+/** @def AB_MAX_BLOW1
+ * @brief Extra Max Blow(1)
+ * @note
+ * Increases player "maximum possible blows" number by 1.
+ */
+#define AB_MAX_BLOW1 3
+
+/** @def AB_MAX_BLOW2
+ * @brief Extra Max Blow(2)
+ * @note
+ * Increases player "maximum possible blows" number by 1
+ * (Cumulative with Extra Max Blow(1)).
+ */
+#define AB_MAX_BLOW2 4
+
+/** @def AB_AMMO_CREATION
+ * @brief Ammo creation
+ * @note
+ * Allows player to create shots, arrows and bolts from various materials.
+ */
+#define AB_AMMO_CREATION 5
+
+/** @def AB_DEATH_TOUCH
+ * @brief Touch of death
+ * @note
+ * Player melee blows can insta-kill, but they only receive 1/3 of the
+ * experience for that kill.
+ */
+#define AB_DEATH_TOUCH 6
+
+/** @def AB_CREATE_ART
+ * @brief Artifact creation
+ * @note
+ * In combination with a high alchemy skill this ability will let the player
+ * design their very own artifacts.
+ */
+#define AB_CREATE_ART 7
+
+/** @def AB_FAR_REACHING
+ * @brief Far reaching attack
+ * @note
+ * The player can attack an enemy one square far using a polearm.
+ * At high levels of polearm skill, they can even hit two enemies at once.
+ */
+#define AB_FAR_REACHING 8
+
+/** @def AB_TRAPPING
+ * @brief Trapping
+ * @note
+ * Enables player to set traps which harm monsters.
+ */
+#define AB_TRAPPING 9
+
+/** @def AB_UNDEAD_FORM
+ * @brief Undead form
+ * @note
+ * Ability to turn into a weak undead being when you "die".
+ * You must then kill enough monsters to absorb enough life energy
+ * to come back to life.
+ */
+#define AB_UNDEAD_FORM 10
+
+/** @} */
+
diff --git a/src/plots.c b/src/plots.c
new file mode 100644
index 00000000..53d3e1bc
--- /dev/null
+++ b/src/plots.c
@@ -0,0 +1,473 @@
+/* File: plots.c */
+
+/* Purpose: plots & quests */
+
+/*
+ * Copyright (c) 2001 James E. Wilson, Robert A. Koeneke, DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#include "lua/lua.h"
+#include "tolua.h"
+extern lua_State* L;
+
+/* #define DEBUG_HOOK */
+
+/******** Hooks stuff *********/
+FILE *hook_file;
+
+#define MAX_ARGS 50
+
+static hooks_chain *hooks_heads[MAX_HOOKS];
+
+/* Wipe hooks and init them with quest hooks */
+void wipe_hooks()
+{
+ int i;
+
+ for (i = 0; i < MAX_HOOKS; i++)
+ {
+ hooks_heads[i] = NULL;
+ }
+}
+void init_hooks()
+{
+ int i;
+
+ for (i = 0; i < MAX_Q_IDX_INIT; i++)
+ {
+ if ((quest[i].type == HOOK_TYPE_C) && (quest[i].init != NULL)) quest[i].init(i);
+ }
+}
+
+void dump_hooks(int h_idx)
+{
+ int min = 0, max = MAX_HOOKS, i;
+
+ if (h_idx != -1)
+ {
+ min = h_idx;
+ max = h_idx + 1;
+ }
+
+ for (i = min; i < max; i++)
+ {
+ hooks_chain *c = hooks_heads[i];
+
+ /* Find it */
+ while (c != NULL)
+ {
+ msg_format("%s(%s)", c->name, (c->type == HOOK_TYPE_C) ? "C" : "Lua");
+
+ c = c->next;
+ }
+ }
+}
+
+/* Check a hook */
+bool_ check_hook(int h_idx)
+{
+ hooks_chain *c = hooks_heads[h_idx];
+
+ return (c != NULL);
+}
+
+/* Add a hook */
+hooks_chain* add_hook(int h_idx, hook_type hook, cptr name)
+{
+ hooks_chain *new_, *c = hooks_heads[h_idx];
+
+ /* Find it */
+ while ((c != NULL) && (strcmp(c->name, name)))
+ {
+ c = c->next;
+ }
+
+ /* If not already in the list, add it */
+ if (c == NULL)
+ {
+ MAKE(new_, hooks_chain);
+ new_->hook = hook;
+ sprintf(new_->name, "%s", name);
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK ADD: %s", name);
+ if (take_notes) add_note(format("HOOK ADD: %s", name), 'D');
+#endif
+ new_->next = hooks_heads[h_idx];
+ hooks_heads[h_idx] = new_;
+ return (new_);
+ }
+ else return (c);
+}
+
+void add_hook_script(int h_idx, char *script, cptr name)
+{
+ hooks_chain *c = add_hook(h_idx, NULL, name);
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK LUA ADD: %s : %s", name, script);
+#endif
+ sprintf(c->script, "%s", script);
+ c->type = HOOK_TYPE_LUA;
+}
+
+/* Remove a hook */
+void del_hook(int h_idx, hook_type hook)
+{
+ hooks_chain *c = hooks_heads[h_idx], *p = NULL;
+
+ /* Find it */
+ while ((c != NULL) && (c->hook != hook))
+ {
+ p = c;
+ c = c->next;
+ }
+
+ /* Remove it */
+ if (c != NULL)
+ {
+ if (p == NULL)
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK DEL: %s", c->name);
+ if (take_notes) add_note(format("HOOK DEL: %s", c->name), 'D');
+#endif
+ hooks_heads[h_idx] = c->next;
+ FREE(c, hooks_chain);
+ }
+ else
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK DEL: %s", c->name);
+ if (take_notes) add_note(format("HOOK DEL: %s", c->name), 'D');
+#endif
+ p->next = c->next;
+ FREE(c, hooks_chain);
+ }
+ }
+}
+
+void del_hook_name(int h_idx, cptr name)
+{
+ hooks_chain *c = hooks_heads[h_idx], *p = NULL;
+
+ /* Find it */
+ while ((c != NULL) && (strcmp(c->name, name)))
+ {
+ p = c;
+ c = c->next;
+ }
+
+ /* Remove it */
+ if (c != NULL)
+ {
+ if (p == NULL)
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK DEL: %s", c->name);
+ if (take_notes) add_note(format("HOOK DEL: %s", c->name), 'D');
+#endif
+ hooks_heads[h_idx] = c->next;
+ FREE(c, hooks_chain);
+ }
+ else
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK DEL: %s", c->name);
+ if (take_notes) add_note(format("HOOK DEL: %s", c->name), 'D');
+#endif
+ p->next = c->next;
+ FREE(c, hooks_chain);
+ }
+ }
+}
+
+/* get the next argument */
+static hook_return param_pile[MAX_ARGS];
+static int get_next_arg_pos = 0;
+static int get_next_arg_pile_pos = 0;
+s32b get_next_arg(char *fmt)
+{
+ while (TRUE)
+ {
+ switch (fmt[get_next_arg_pos++])
+ {
+ case 'd':
+ case 'l':
+ return (param_pile[get_next_arg_pile_pos++].num);
+ case ')':
+ get_next_arg_pos--;
+ return 0;
+ case '(':
+ case ',':
+ break;
+ }
+ }
+}
+char* get_next_arg_str(char *fmt)
+{
+ while (TRUE)
+ {
+ switch (fmt[get_next_arg_pos++])
+ {
+ case 's':
+ return (char*)(param_pile[get_next_arg_pile_pos++].str);
+ case ')':
+ get_next_arg_pos--;
+ return 0;
+ case '(':
+ case ',':
+ break;
+ }
+ }
+}
+
+/* Actually process the hooks */
+int process_hooks_restart = FALSE;
+hook_return process_hooks_return[20];
+static bool_ vprocess_hooks_return (int h_idx, char *ret, char *fmt, va_list *ap)
+{
+ hooks_chain *c = hooks_heads[h_idx];
+ va_list real_ap;
+
+ while (c != NULL)
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK: %s", c->name);
+ if (take_notes) add_note(format("HOOK PROCESS: %s", c->name), 'D');
+#endif
+ if (c->type == HOOK_TYPE_C)
+ {
+ int i = 0, nb = 0;
+
+ /* Push all args in the pile */
+ i = 0;
+ COPY(&real_ap, ap, va_list);
+ while (fmt[i])
+ {
+ switch (fmt[i])
+ {
+ case 'O':
+ param_pile[nb++].o_ptr = va_arg(real_ap, object_type *);
+ break;
+ case 's':
+ param_pile[nb++].str = va_arg(real_ap, char *);
+ break;
+ case 'd':
+ case 'l':
+ param_pile[nb++].num = va_arg(real_ap, s32b);
+ break;
+ case '(':
+ case ')':
+ case ',':
+ break;
+ }
+ i++;
+ }
+
+ get_next_arg_pos = 0;
+ get_next_arg_pile_pos = 0;
+ if (c->hook(fmt))
+ {
+ return TRUE;
+ }
+
+ /* Should we restart ? */
+ if (process_hooks_restart)
+ {
+ c = hooks_heads[h_idx];
+ process_hooks_restart = FALSE;
+ }
+ else
+ {
+ c = c->next;
+ }
+ }
+ else if (c->type == HOOK_TYPE_LUA)
+ {
+ int i = 0, nb = 0, nbr = 1;
+ int oldtop = lua_gettop(L), size;
+
+ /* Push the function */
+ lua_getglobal(L, c->script);
+
+ /* Push and count the arguments */
+ COPY(&real_ap, ap, va_list);
+ while (fmt[i])
+ {
+ switch (fmt[i++])
+ {
+ case 'd':
+ case 'l':
+ tolua_pushnumber(L, va_arg(real_ap, s32b));
+ nb++;
+ break;
+ case 's':
+ tolua_pushstring(L, va_arg(real_ap, char*));
+ nb++;
+ break;
+ case 'O':
+ tolua_pushusertype(L, (void*)va_arg(real_ap, object_type*), tolua_tag(L, "object_type"));
+ nb++;
+ break;
+ case 'M':
+ tolua_pushusertype(L, (void*)va_arg(real_ap, monster_type*), tolua_tag(L, "monster_type"));
+ nb++;
+ break;
+ case '(':
+ case ')':
+ case ',':
+ break;
+ }
+ }
+
+ /* Count returns */
+ nbr += strlen(ret);
+
+ /* Call the function */
+ if (lua_call(L, nb, nbr))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling '%s' lua hook script. Breaking the hook chain now.", c->script);
+ return FALSE;
+ }
+
+ /* Number of returned values, SHOULD be the same as nbr, but I'm paranoid */
+ size = lua_gettop(L) - oldtop;
+
+ /* get the extra returns if needed */
+ for (i = 0; i < nbr - 1; i++)
+ {
+ if ((ret[i] == 'd') || (ret[i] == 'l'))
+ {
+ if (lua_isnumber(L, ( -size) + 1 + i)) process_hooks_return[i].num = tolua_getnumber(L, ( -size) + 1 + i, 0);
+ else process_hooks_return[i].num = 0;
+ }
+ else if (ret[i] == 's')
+ {
+ if (lua_isstring(L, ( -size) + 1 + i)) process_hooks_return[i].str = tolua_getstring(L, ( -size) + 1 + i, "");
+ else process_hooks_return[i].str = NULL;
+ }
+ else if (ret[i] == 'O')
+ {
+ if (tolua_istype(L, ( -size) + 1 + i, tolua_tag(L, "object_type"), 0))
+ process_hooks_return[i].o_ptr = (object_type*)tolua_getuserdata(L, ( -size) + 1 + i, NULL);
+ else
+ process_hooks_return[i].o_ptr = NULL;
+ }
+ else if (ret[i] == 'M')
+ {
+ if (tolua_istype(L, ( -size) + 1 + i, tolua_tag(L, "monster_type"), 0))
+ process_hooks_return[i].m_ptr = (monster_type*)tolua_getuserdata(L, ( -size) + 1 + i, NULL);
+ else
+ process_hooks_return[i].m_ptr = NULL;
+ }
+ else process_hooks_return[i].num = 0;
+ }
+
+ /* Get the basic return(continue or stop the hook chain) */
+ if (tolua_getnumber(L, -size, 0))
+ {
+ lua_settop(L, oldtop);
+ return (TRUE);
+ }
+ if (process_hooks_restart)
+ {
+ c = hooks_heads[h_idx];
+ process_hooks_restart = FALSE;
+ }
+ else
+ c = c->next;
+ lua_settop(L, oldtop);
+ }
+ else
+ {
+ msg_format("Unkown hook type %d, name %s", c->type, c->name);
+ }
+ }
+
+ return FALSE;
+}
+
+bool_ process_hooks_ret(int h_idx, char *ret, char *fmt, ...)
+{
+ va_list ap;
+ bool_ r;
+
+ va_start(ap, fmt);
+ r = vprocess_hooks_return (h_idx, ret, fmt, &ap);
+ va_end(ap);
+ return (r);
+}
+
+bool_ process_hooks(int h_idx, char *fmt, ...)
+{
+ va_list ap;
+ bool_ ret;
+
+ va_start(ap, fmt);
+ ret = vprocess_hooks_return (h_idx, "", fmt, &ap);
+ va_end(ap);
+ return (ret);
+}
+
+/******** Plots & Quest stuff ********/
+
+static void quest_describe(int q_idx)
+{
+ int i = 0;
+
+ while ((i < 10) && (quest[q_idx].desc[i][0] != '\0'))
+ {
+ cmsg_print(TERM_YELLOW, quest[q_idx].desc[i++]);
+ }
+}
+
+/* Catch-all quest hook */
+bool_ quest_null_hook(int q)
+{
+ /* Do nothing */
+ return (FALSE);
+}
+
+/************************** Random Quests *************************/
+#include "q_rand.c"
+
+/**************************** Main plot ***************************/
+#include "q_main.c"
+#include "q_one.c"
+#include "q_ultrag.c"
+#include "q_ultrae.c"
+
+/**************************** Bree plot ***************************/
+#include "q_thief.c"
+#include "q_hobbit.c"
+#include "q_troll.c"
+#include "q_wight.c"
+#include "q_nazgul.c"
+#include "q_shroom.c"
+
+/*************************** Lorien plot **************************/
+#include "q_wolves.c"
+#include "q_spider.c"
+#include "q_poison.c"
+
+/************************** Gondolin plot *************************/
+#include "q_dragons.c"
+#include "q_eol.c"
+#include "q_nirna.c"
+#include "q_invas.c"
+
+/************************* Minas Anor plot ************************/
+#include "q_haunted.c"
+#include "q_betwen.c"
+
+/************************* Khazad-dum plot ************************/
+#include "q_evil.c"
+
+/*************************** Other plot ***************************/
+#include "q_narsil.c"
+#include "q_thrain.c"
diff --git a/src/plots.h b/src/plots.h
new file mode 100644
index 00000000..a1a11e6c
--- /dev/null
+++ b/src/plots.h
@@ -0,0 +1,48 @@
+/* File: plots.h */
+
+/* Purpose: extern plots declarations */
+
+extern bool_ quest_null_hook(int q);
+
+/******* Random Quests ********/
+extern bool_ is_randhero(int level);
+extern bool_ quest_random_init_hook(int q_idx);
+extern bool_ quest_random_describe(FILE *fff);
+
+/******* Plot main ********/
+extern bool_ quest_necro_init_hook(int q_idx);
+extern bool_ quest_one_init_hook(int q_idx);
+extern bool_ quest_sauron_init_hook(int q_idx);
+extern bool_ quest_morgoth_init_hook(int q_idx);
+extern bool_ quest_ultra_good_init_hook(int q_idx);
+extern bool_ quest_ultra_evil_init_hook(int q_idx);
+
+/******* Plot Bree *********/
+extern bool_ quest_thieves_init_hook(int q_idx);
+extern bool_ quest_hobbit_init_hook(int q_idx);
+extern bool_ quest_troll_init_hook(int q_idx);
+extern bool_ quest_wight_init_hook(int q_idx);
+extern bool_ quest_nazgul_init_hook(int q_idx);
+extern bool_ quest_shroom_init_hook(int q_idx);
+
+/******* Plot Lorien *********/
+extern bool_ quest_wolves_init_hook(int q_idx);
+extern bool_ quest_spider_init_hook(int q_idx);
+extern bool_ quest_poison_init_hook(int q_idx);
+
+/******* Plot Gondolin *********/
+extern bool_ quest_dragons_init_hook(int q_idx);
+extern bool_ quest_eol_init_hook(int q_idx);
+extern bool_ quest_nirnaeth_init_hook(int q_idx);
+extern bool_ quest_invasion_init_hook(int q_idx);
+
+/******* Plot Minas Anor *********/
+extern bool_ quest_haunted_init_hook(int q_idx);
+extern bool_ quest_between_init_hook(int q_idx);
+
+/******* Plot Khazad-dum *********/
+extern bool_ quest_evil_init_hook(int q_idx);
+
+/******* Plot Other *********/
+extern bool_ quest_narsil_init_hook(int q_idx);
+extern bool_ quest_thrain_init_hook(int q_idx);
diff --git a/src/powers.c b/src/powers.c
new file mode 100644
index 00000000..12b2c2c0
--- /dev/null
+++ b/src/powers.c
@@ -0,0 +1,1388 @@
+/* File: powers.c */
+
+/* Purpose: Powers */
+
+/*
+ * Copyright (c) 2001 James E. Wilson, Robert A. Koeneke, DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Note: return value indicates the amount of mana to use
+ */
+bool_ power_chance(power_type *x_ptr)
+{
+ bool_ use_hp = FALSE;
+ int diff = x_ptr->diff;
+
+ /* Always true ? */
+ if (!x_ptr->cost) return TRUE;
+
+ /* Not enough mana - use hp */
+ if (p_ptr->csp < x_ptr->cost) use_hp = TRUE;
+
+ /* Power is not available yet */
+ if (p_ptr->lev < x_ptr->level)
+ {
+ msg_format("You need to attain level %d to use this power.", x_ptr->level);
+ energy_use = 0;
+ return (FALSE);
+ }
+
+ /* Too confused */
+ else if (p_ptr->confused)
+ {
+ msg_print("You are too confused to use this power.");
+ energy_use = 0;
+ return (FALSE);
+ }
+
+ /* Risk death? */
+ else if (use_hp && (p_ptr->chp < x_ptr->cost))
+ {
+ if (!(get_check("Really use the power in your weakened state? ")))
+ {
+ energy_use = 0;
+ return (FALSE);
+ }
+ }
+
+ /* Else attempt to do it! */
+
+ if (p_ptr->stun)
+ {
+ diff += p_ptr->stun;
+ }
+ else if (p_ptr->lev > x_ptr->level)
+ {
+ int lev_adj = ((p_ptr->lev - x_ptr->level) / 3);
+ if (lev_adj > 10) lev_adj = 10;
+ diff -= lev_adj;
+ }
+
+ if (diff < 5) diff = 5;
+
+ /* take time and pay the price */
+ if (use_hp)
+ {
+ take_hit(((x_ptr->cost / 2) + (randint(x_ptr->cost / 2))),
+ "concentrating too hard");
+ }
+ else
+ {
+ p_ptr->csp -= (x_ptr->cost / 2 ) + (randint(x_ptr->cost / 2));
+ }
+ energy_use = 100;
+
+ /* Redraw mana and hp */
+ p_ptr->redraw |= (PR_HP | PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Success? */
+ if (randint(p_ptr->stat_cur[x_ptr->stat]) >=
+ ((diff / 2) + randint(diff / 2)))
+ {
+ return (TRUE);
+ }
+
+ if (flush_failure) flush();
+ msg_print("You've failed to concentrate hard enough.");
+
+ return (FALSE);
+}
+
+static void power_activate(int power)
+{
+ s16b plev = p_ptr->lev;
+ char ch = 0;
+ int amber_power = 0;
+ int dir = 0, dummy;
+ object_type *q_ptr;
+ object_type forge;
+ int ii = 0, ij = 0;
+ /* char out_val[80]; */
+ /* cptr p = "Power of the flame: "; */
+ cptr q, s;
+ power_type *x_ptr = &powers_type[power], x_ptr_foo;
+ int x, y;
+ cave_type *c_ptr;
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+
+ if (!power_chance(x_ptr)) return;
+
+ switch (power)
+ {
+ case PWR_BALROG:
+ {
+ set_mimic(p_ptr->lev / 2, resolve_mimic_name("Balrog"), p_ptr->lev);
+ }
+ break;
+ case PWR_BEAR:
+ {
+ set_mimic(150 + (p_ptr->lev * 10) , resolve_mimic_name("Bear"), p_ptr->lev);
+ }
+ break;
+ case PWR_COMPANION:
+ {
+ if (!can_create_companion())
+ {
+ msg_print("You cannot have more companions.");
+ }
+ else
+ {
+ monster_type *m_ptr;
+ int ii, jj;
+
+ msg_print("Select the friendly monster:");
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ if (m_ptr->status != MSTATUS_PET)
+ {
+ msg_print("You cannot convert this monster.");
+ return;
+ }
+
+ m_ptr->status = MSTATUS_COMPANION;
+ }
+ }
+ }
+ break;
+ case PWR_MERCHANT:
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("[A]ppraise item, [W]arp item or [I]dentify item? ", &ch))
+ {
+ amber_power = 0;
+ break;
+ }
+
+ if (ch == 'A' || ch == 'a')
+ {
+ amber_power = 1;
+ break;
+ }
+
+ if (ch == 'W' || ch == 'w')
+ {
+ amber_power = 2;
+ break;
+ }
+
+ if (ch == 'I' || ch == 'i')
+ {
+ amber_power = 3;
+ break;
+ }
+ }
+
+ if (amber_power == 1)
+ {
+ x_ptr_foo.level = 5;
+ x_ptr_foo.cost = 5;
+ x_ptr_foo.stat = A_INT;
+ x_ptr_foo.diff = 5;
+ if (power_chance(&x_ptr_foo))
+ {
+ /* Appraise an object */
+ int idx;
+ cptr q, s;
+
+ /* Get the item */
+ q = "Appraise which item? ";
+ s = "You have nothing to appraise.";
+ if (get_item(&idx, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)))
+ {
+ object_type *o_ptr;
+ char out_val[80], value[16];
+
+ /* The item is in the pack */
+ if (idx >= 0) o_ptr = &p_ptr->inventory[idx];
+ /* The item is on the floor */
+ else o_ptr = &o_list[0 - idx];
+
+ /* Appraise it */
+ sprintf(value, "%i au", (int)object_value(o_ptr));
+
+ /* Inscribe the value */
+ /* Get the original inscription */
+ if (o_ptr->note)
+ {
+ strcpy(out_val, quark_str(o_ptr->note));
+ strcat(out_val, " ");
+ }
+ else
+ out_val[0] = '\0';
+
+ strcat(out_val, value);
+
+ /* Save the new inscription */
+ o_ptr->note = quark_add(out_val);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+ }
+ }
+ if (amber_power == 2)
+ {
+ x_ptr_foo.level = 15;
+ x_ptr_foo.cost = 10;
+ x_ptr_foo.stat = A_INT;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ int chest, item;
+ cptr q1, s1, q2, s2;
+ u32b flag = (USE_EQUIP | USE_INVEN | USE_FLOOR);
+ object_type *o1_ptr = &p_ptr->inventory[0], *o2_ptr = &p_ptr->inventory[1];
+ int ok = 0;
+
+ q1 = "Select a chest! ";
+ s1 = "You need a chest to warp items.";
+
+ q2 = "Warp which item? ";
+ s2 = "You have nothing to warp.";
+
+ item_tester_tval = TV_CHEST;
+
+ /* Get the chest */
+ if (get_item(&chest, q1, s1, flag))
+ {
+ if (chest >= 0) o1_ptr = &p_ptr->inventory[chest];
+ else o1_ptr = &o_list[0 - chest];
+
+ /* Is the chest disarmed? */
+ if (o1_ptr->pval > 0)
+ msg_print("This chest may be trapped.");
+
+ /* Is it ruined? */
+ else if (k_info[o1_ptr->k_idx].level <= 0)
+ msg_print("This chest is broken.");
+
+ /* Is it empty? */
+ else if (o1_ptr->pval2 >= (o1_ptr->sval % SV_CHEST_MIN_LARGE) * 2)
+ msg_print("This chest is full.");
+
+ else ok = 1;
+ }
+
+ /* Get the item */
+ if (ok && get_item(&item, q2, s2, flag))
+ {
+ ok = 0;
+
+ o2_ptr = get_object(item);
+
+ /* Is the item cursed? */
+ if ((item >= INVEN_WIELD) && cursed_p(o2_ptr))
+ msg_print("Hmmm, it seems to be cursed.");
+
+ /* Is it the same chest? */
+ if (item == chest)
+ msg_print("You can't put a chest into itself.");
+
+ /* Is it another chest? */
+ if (o2_ptr->tval == TV_CHEST)
+ msg_print("You can't put a chest into another one.");
+
+ /* Try to use the power */
+ else ok = 1;
+ }
+
+ if (ok)
+ {
+ int tmp, level;
+
+ /* Calculate the level of objects */
+ tmp = o1_ptr->pval;
+
+ /* Get the level of the current object */
+ /* Cursed items/cheap items always break */
+ if (k_info[o2_ptr->k_idx].cost < 20) level = 0;
+ /* Not-so-cheap items break 90% of the time */
+ else if (k_info[o2_ptr->k_idx].cost < 100) level = 1;
+ else level = k_info[o2_ptr->k_idx].level;
+
+ /* Break some items */
+ if (randint(10) > level)
+ msg_print("The item disappeared!");
+ else
+ {
+ level /= (o1_ptr->sval % SV_CHEST_MIN_LARGE) * 2;
+
+ /* Increase the number of objects in
+ * the chest */
+ o1_ptr->pval2++;
+
+ /* Set the level of chest */
+ tmp = tmp - level;
+ o1_ptr->pval = tmp;
+ }
+
+ /* Destroy item */
+ inc_stack_size(item, -1);
+ }
+ }
+ }
+ if (amber_power == 3)
+ {
+ x_ptr_foo.level = 30;
+ x_ptr_foo.cost = 20;
+ x_ptr_foo.stat = A_INT;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ ident_spell();
+ }
+ }
+ break;
+ case PWR_LAY_TRAP:
+ {
+ do_cmd_set_trap();
+ }
+ break;
+ case PWR_MAGIC_MAP:
+ {
+ msg_print("You sense the world around you.");
+ map_area();
+ }
+ break;
+
+ case PWR_PASSWALL:
+ {
+ if (!get_aim_dir(&dir))
+ break;
+ if (passwall(dir, TRUE))
+ msg_print("A passage opens, and you step through.");
+ else
+ msg_print("There is no wall there!");
+ }
+ break;
+
+ case PWR_COOK_FOOD:
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create the item */
+ object_prep(q_ptr, 21);
+
+ /* Drop the object from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+ msg_print("You cook some food.");
+ }
+ break;
+
+ case PWR_UNFEAR:
+ {
+ msg_print("You play tough.");
+ (void)set_afraid(0);
+ }
+ break;
+
+ case PWR_BERSERK:
+ {
+ msg_print("RAAAGH!");
+ (void)set_afraid(0);
+
+ (void)set_shero(p_ptr->shero + 10 + randint(plev));
+ (void)hp_player(30);
+ }
+ break;
+
+ case PWR_EXPL_RUNE:
+ {
+ msg_print("You carefully set an explosive rune...");
+ explosive_rune();
+ }
+ break;
+
+ case PWR_STM:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You bash at a stone wall.");
+ (void)wall_to_mud(dir);
+ }
+ break;
+
+ case PWR_ROHAN:
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("Use [F]lash aura or [L]ight speed jump? ", &ch))
+ {
+ amber_power = 0;
+ break;
+ }
+
+ if (ch == 'F' || ch == 'f')
+ {
+ amber_power = 1;
+ break;
+ }
+
+ if (ch == 'L' || ch == 'l')
+ {
+ amber_power = 2;
+ break;
+ }
+ }
+
+ if (amber_power == 1)
+ {
+ x_ptr_foo.level = 1;
+ x_ptr_foo.cost = 9;
+ x_ptr_foo.stat = A_CHR;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ if (!(get_aim_dir(&dir))) break;
+ msg_print("You flash a bright aura.");
+ if (p_ptr->lev < 10)
+ fire_bolt(GF_CONFUSION, dir, plev*2);
+ else
+ fire_ball(GF_CONFUSION, dir, plev*2, 2);
+ }
+ }
+ if (amber_power == 2)
+ {
+ x_ptr_foo.level = 30;
+ x_ptr_foo.cost = 30;
+ x_ptr_foo.stat = A_WIS;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ (void)set_light_speed(p_ptr->lightspeed + 3);
+ }
+ }
+ break;
+
+
+ case PWR_POIS_DART:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You throw a dart of poison.");
+ fire_bolt(GF_POIS, dir, plev);
+ }
+ break;
+
+ case PWR_DETECT_TD:
+ {
+ msg_print("You examine your surroundings.");
+ (void)detect_traps(DEFAULT_RADIUS);
+ (void)detect_doors(DEFAULT_RADIUS);
+ (void)detect_stairs(DEFAULT_RADIUS);
+ }
+ break;
+
+ case PWR_MAGIC_MISSILE:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You cast a magic missile.");
+ fire_bolt_or_beam(10, GF_MISSILE, dir,
+ damroll(3 + ((plev - 1) / 5), 4));
+ }
+ break;
+
+ case PWR_THUNDER:
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("Use [T]hunder strike, [R]ide the straight road, go [B]ack in town? ", &ch))
+ {
+ amber_power = 0;
+ break;
+ }
+
+ if (ch == 'T' || ch == 't')
+ {
+ amber_power = 1;
+ break;
+ }
+
+ if (ch == 'R' || ch == 'r')
+ {
+ amber_power = 2;
+ break;
+ }
+
+ if (ch == 'B' || ch == 'b')
+ {
+ amber_power = 3;
+ break;
+ }
+
+ }
+
+ if (amber_power == 1)
+ {
+ x_ptr_foo.level = 1;
+ x_ptr_foo.cost = p_ptr->lev;
+ x_ptr_foo.stat = A_CON;
+ x_ptr_foo.diff = 6;
+ if (power_chance(&x_ptr_foo))
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_format("You conjure up thunder!");
+ fire_beam(GF_ELEC, dir, p_ptr->lev * 2);
+ fire_beam(GF_SOUND, dir, p_ptr->lev * 2);
+ p_ptr->energy -= 100;
+ }
+ }
+ if (amber_power == 2)
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels ...");
+ break;
+ }
+ x_ptr_foo.level = 3;
+ x_ptr_foo.cost = 15;
+ x_ptr_foo.stat = A_CON;
+ x_ptr_foo.diff = 6;
+ if (power_chance(&x_ptr_foo))
+ {
+ msg_print("You enter the straight road and fly beside the world. Where to exit?");
+ if (!tgt_pt(&ii, &ij)) return;
+ p_ptr->energy -= 60 - plev;
+ if (!cave_empty_bold(ij, ii) || (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev*20 + 2) || !(cave[ij][ii].info & CAVE_MARK))
+ {
+ msg_print("You fail to exit correctly!");
+ p_ptr->energy -= 100;
+ teleport_player(10);
+ }
+ else teleport_player_to(ij, ii);
+ }
+
+ }
+ if (amber_power == 3)
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No recall on special levels..");
+ break;
+ }
+ x_ptr_foo.level = 7;
+ x_ptr_foo.cost = 30;
+ x_ptr_foo.stat = A_CON;
+ x_ptr_foo.diff = 6;
+ if (power_chance(&x_ptr_foo))
+ {
+ if (dun_level == 0)
+ msg_print("You are already in town!");
+ else
+ {
+ msg_print("You enter the straight road and fly beside the world.");
+ p_ptr->energy -= 100;
+ p_ptr->word_recall = 1;
+ }
+ }
+ }
+ break;
+
+ case PWR_GROW_TREE:
+ {
+ msg_print("You make the trees grow!");
+ grow_trees((plev / 8 < 1) ? 1 : plev / 8);
+ }
+ break;
+
+ case PWR_DEATHMOLD:
+ do_cmd_immovable_special();
+ break;
+
+ case PWR_BR_COLD:
+ {
+ msg_print("You breathe cold...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_COLD, dir, p_ptr->lev * 2, 1 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_BR_CHAOS:
+ {
+ msg_print("You breathe chaos...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_CHAOS, dir, p_ptr->lev * 2, 1 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_BR_ELEM:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_format("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, (p_ptr->lev)*2,
+ ((p_ptr->lev) / 15) + 1);
+ }
+ break;
+
+ case PWR_SUMMON_MONSTER:
+ {
+ do_cmd_beastmaster();
+ }
+ break;
+
+ case PWR_WRECK_WORLD:
+ msg_print("The power of Eru Iluvatar flows through you!");
+ msg_print("The world changes!");
+
+ autosave_checkpoint();
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ break;
+
+ case PWR_VAMPIRISM:
+ {
+ /* Only works on adjacent monsters */
+ if (!get_rep_dir(&dir)) break; /* was get_aim_dir */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("You bite into thin air!");
+ break;
+ }
+
+ msg_print("You grin and bare your fangs...");
+ dummy = plev + randint(plev) * MAX(1, plev / 10); /* Dmg */
+ if (drain_life(dir, dummy))
+ {
+ if (p_ptr->food < PY_FOOD_FULL)
+ /* No heal if we are "full" */
+ (void)hp_player(dummy);
+ else
+ msg_print("You were not hungry.");
+ /* Gain nutritional sustenance: 150/hp drained */
+ /* A Food ration gives 5000 food points (by contrast) */
+ /* Don't ever get more than "Full" this way */
+ /* But if we ARE Gorged, it won't cure us */
+ dummy = p_ptr->food + MIN(5000, 100 * dummy);
+ if (p_ptr->food < PY_FOOD_MAX) /* Not gorged already */
+ (void)set_food(dummy >= PY_FOOD_MAX ? PY_FOOD_MAX - 1 : dummy);
+ }
+ else
+ msg_print("Yechh. That tastes foul.");
+ }
+ break;
+
+ case PWR_SCARE:
+ {
+ msg_print("You emit an eldritch howl!");
+ if (!get_aim_dir(&dir)) break;
+ (void)fear_monster(dir, plev);
+ }
+ break;
+
+ case PWR_REST_LIFE:
+ {
+ msg_print("You attempt to restore your lost energies.");
+ (void)restore_level();
+ }
+ break;
+
+ case PWR_HYPNO:
+ {
+ int dir, x, y;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+ object_type *q_ptr;
+ object_type forge;
+
+ msg_print("Hypnotize which pet?");
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (c_ptr->m_idx)
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if ((r_ptr->flags1 & RF1_NEVER_MOVE) && (m_ptr->status == MSTATUS_PET) && (!(r_ptr->flags9 & RF9_SPECIAL_GENE)))
+ {
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_HYPNOS, 1));
+ q_ptr->number = 1;
+ q_ptr->pval = m_ptr->r_idx;
+ q_ptr->pval2 = m_ptr->hp;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ q_ptr->ident |= IDENT_STOREB;
+
+ drop_near(q_ptr, 0, y, x);
+
+ delete_monster(y, x);
+ health_who = 0;
+ }
+ else
+ msg_print("You can only hypnotize monsters that can't move.");
+ }
+ else msg_print("There is no pet here!");
+ }
+ break;
+
+ case PWR_UNHYPNO:
+ {
+ monster_type *m_ptr;
+ int m_idx;
+ int item, x, y, d;
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ /* Restrict choices to monsters */
+ item_tester_tval = TV_HYPNOS;
+
+ /* Get an item */
+ q = "Awaken which monster? ";
+ s = "You have no monster to awaken.";
+ if (!get_item(&item, q, s, (USE_FLOOR))) return;
+
+ o_ptr = &o_list[0 - item];
+
+ d = 2;
+ while (d < 100)
+ {
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ if (cave_floor_bold(y, x) && (!cave[y][x].m_idx)) break;
+
+ d++;
+ }
+
+ if (d >= 100) return;
+
+ if ((m_idx = place_monster_one(y, x, o_ptr->pval, 0, FALSE, MSTATUS_PET)) == 0) return;
+
+ m_ptr = &m_list[m_idx];
+ m_ptr->hp = o_ptr->pval2;
+
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ break;
+
+ case PWR_NECRO:
+ {
+ do_cmd_necromancer();
+ break;
+ }
+ case PWR_INCARNATE:
+ {
+ do_cmd_integrate_body();
+ break;
+ }
+
+ case PWR_SPIT_ACID:
+ {
+ msg_print("You spit acid...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_ACID, dir, p_ptr->lev, 1 + (p_ptr->lev / 30));
+ }
+ break;
+
+ case PWR_BR_FIRE:
+ {
+ msg_print("You breathe fire...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_FIRE, dir, p_ptr->lev * 2, 1 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_HYPN_GAZE:
+ {
+ msg_print("Your eyes look mesmerising...");
+ if (get_aim_dir(&dir))
+ (void) charm_monster(dir, p_ptr->lev);
+ }
+ break;
+
+ case PWR_TELEKINES:
+ {
+ msg_print("You concentrate...");
+ if (get_aim_dir(&dir))
+ fetch(dir, p_ptr->lev * 10, TRUE);
+ }
+ break;
+
+ case PWR_VTELEPORT:
+ {
+ msg_print("You concentrate...");
+ teleport_player(10 + 4*(p_ptr->lev));
+ }
+ break;
+
+ case PWR_MIND_BLST:
+ {
+ msg_print("You concentrate...");
+ if (!get_aim_dir(&dir)) return;
+ fire_bolt(GF_PSI, dir, damroll(3 + ((p_ptr->lev - 1) / 5), 3));
+ }
+ break;
+
+ case PWR_RADIATION:
+ {
+ msg_print("Radiation flows from your body!");
+ fire_ball(GF_NUKE, 0, (p_ptr->lev * 2), 3 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_SMELL_MET:
+ {
+ (void)detect_treasure(DEFAULT_RADIUS);
+ }
+ break;
+
+ case PWR_SMELL_MON:
+ {
+ (void)detect_monsters_normal(DEFAULT_RADIUS);
+ }
+ break;
+
+ case PWR_BLINK:
+ {
+ teleport_player(10);
+ }
+ break;
+
+ case PWR_EAT_ROCK:
+ {
+ int x, y, ox, oy;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) break;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (cave_floor_bold(y, x))
+ {
+ msg_print("You bite into thin air!");
+ break;
+ }
+ else if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT) || (c_ptr->feat == FEAT_MOUNTAIN))
+ {
+ msg_print("Ouch! This wall is harder than your teeth!");
+ break;
+ }
+ else if (c_ptr->m_idx)
+ {
+ msg_print("There's something in the way!");
+ break;
+ }
+ else if (c_ptr->feat == FEAT_TREES)
+ {
+ msg_print("You don't like the woody taste!");
+ break;
+ }
+ else
+ {
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_RUBBLE))
+ {
+ (void)set_food(p_ptr->food + 3000);
+ }
+ else if ((c_ptr->feat >= FEAT_MAGMA) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K))
+ {
+ (void)set_food(p_ptr->food + 5000);
+ }
+ else if ((c_ptr->feat >= FEAT_SANDWALL) &&
+ (c_ptr->feat <= FEAT_SANDWALL_K))
+ {
+ (void)set_food(p_ptr->food + 500);
+ }
+ else
+ {
+ msg_print("This granite is very filling!");
+ (void)set_food(p_ptr->food + 10000);
+ }
+ }
+ (void)wall_to_mud(dir);
+
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ lite_spot(p_ptr->py, p_ptr->px);
+ lite_spot(oy, ox);
+
+ verify_panel();
+
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->update |= (PU_DISTANCE);
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ break;
+
+ case PWR_SWAP_POS:
+ {
+ if (!get_aim_dir(&dir)) return;
+ (void)teleport_swap(dir);
+ }
+ break;
+
+ case PWR_SHRIEK:
+ {
+ (void)fire_ball(GF_SOUND, 0, 4 * p_ptr->lev, 8);
+ (void)aggravate_monsters(0);
+ }
+ break;
+
+ case PWR_ILLUMINE:
+ {
+ (void)lite_area(damroll(2, (p_ptr->lev / 2)), (p_ptr->lev / 10) + 1);
+ }
+ break;
+
+ case PWR_DET_CURSE:
+ {
+ int i;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+ if (!cursed_p(o_ptr)) continue;
+
+ if (!o_ptr->sense) o_ptr->sense = SENSE_CURSED;
+ }
+ }
+ break;
+
+ case PWR_POLYMORPH:
+ {
+ do_poly_self();
+ }
+ break;
+
+ case PWR_MIDAS_TCH:
+ {
+ (void)alchemy();
+ }
+ break;
+
+ case PWR_GROW_MOLD:
+ {
+ int i;
+ for (i = 0; i < 8; i++)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, p_ptr->lev, SUMMON_BIZARRE1, FALSE);
+ }
+ }
+ break;
+
+ case PWR_RESIST:
+ {
+ int num = p_ptr->lev / 10;
+ int dur = randint(20) + 20;
+
+ if (rand_int(5) < num)
+ {
+ (void)set_oppose_acid(p_ptr->oppose_acid + dur);
+ num--;
+ }
+ if (rand_int(4) < num)
+ {
+ (void)set_oppose_elec(p_ptr->oppose_elec + dur);
+ num--;
+ }
+ if (rand_int(3) < num)
+ {
+ (void)set_oppose_fire(p_ptr->oppose_fire + dur);
+ num--;
+ }
+ if (rand_int(2) < num)
+ {
+ (void)set_oppose_cold(p_ptr->oppose_cold + dur);
+ num--;
+ }
+ if (num)
+ {
+ (void)set_oppose_pois(p_ptr->oppose_pois + dur);
+ num--;
+ }
+ }
+ break;
+
+ case PWR_EARTHQUAKE:
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 10);
+ }
+ break;
+
+ case PWR_EAT_MAGIC:
+ {
+ object_type * o_ptr;
+ int lev, item;
+
+ item_tester_hook = item_tester_hook_recharge;
+
+ /* Get an item */
+ q = "Drain which item? ";
+ s = "You have nothing to drain.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) break;
+
+ o_ptr = get_object(item);
+
+ lev = k_info[o_ptr->k_idx].level;
+
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (!o_ptr->timeout)
+ {
+ msg_print("You can't absorb energy from a discharged rod.");
+ }
+ else
+ {
+ p_ptr->csp += o_ptr->timeout;
+ o_ptr->timeout = 0;
+ }
+ }
+ else
+ {
+ if (o_ptr->pval > 0)
+ {
+ p_ptr->csp += o_ptr->pval * lev;
+ o_ptr->pval = 0;
+ }
+ else
+ {
+ msg_print("There's no energy there to absorb!");
+ }
+ o_ptr->ident |= IDENT_EMPTY;
+ }
+
+ if (p_ptr->csp > p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ }
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ p_ptr->window |= (PW_INVEN);
+ }
+ break;
+
+ case PWR_WEIGH_MAG:
+ {
+ report_magics();
+ }
+ break;
+
+ case PWR_STERILITY:
+ {
+ /* Fake a population explosion. */
+ msg_print("You suddenly have a headache!");
+ take_hit(randint(30) + 30, "the strain of forcing abstinence");
+ num_repro += MAX_REPRO;
+ }
+ break;
+
+ case PWR_PANIC_HIT:
+ {
+ int x, y;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ if (cave[y][x].m_idx)
+ {
+ py_attack(y, x, -1);
+ teleport_player(30);
+ }
+ else
+ {
+ msg_print("You don't see any monster in this direction.");
+ msg_print(NULL);
+ }
+ }
+ break;
+
+ case PWR_DAZZLE:
+ {
+ stun_monsters(p_ptr->lev * 4);
+ confuse_monsters(p_ptr->lev * 4);
+ turn_monsters(p_ptr->lev * 4);
+ }
+ break;
+
+ case PWR_DARKRAY:
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_beam(GF_LITE, dir, 2*p_ptr->lev);
+ }
+ break;
+
+ case PWR_RECALL:
+ {
+ if (!(dungeon_flags2 & DF2_ASK_LEAVE) || ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")))
+ {
+ recall_player(21, 15);
+ }
+ }
+ break;
+
+ case PWR_BANISH:
+ {
+ int x, y;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("You sense no evil there!");
+ break;
+ }
+
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags3 & RF3_EVIL)
+ {
+ /* Delete the monster, rather than killing it. */
+ delete_monster_idx(c_ptr->m_idx);
+ msg_print("The evil creature vanishes in a puff of sulphurous smoke!");
+ }
+ else
+ {
+ msg_print("Your invocation is ineffectual!");
+ }
+ }
+ break;
+
+ case PWR_COLD_TOUCH:
+ {
+ int x, y;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("You wave your hands in the air.");
+ break;
+ }
+ fire_bolt(GF_COLD, dir, 2 * (p_ptr->lev));
+ }
+ break;
+
+ case PWR_LAUNCHER:
+ {
+ /* Gives a multiplier of 2 at first, up to 5 at 48th */
+ p_ptr->throw_mult = 2 + (p_ptr->lev / 16);
+ do_cmd_throw();
+ p_ptr->throw_mult = 1;
+ }
+ break;
+
+ case PWR_DODGE:
+ use_ability_blade();
+ break;
+
+ default:
+ if (!process_hooks(HOOK_ACTIVATE_POWER, "(d)", power))
+ {
+ msg_format("Warning power_activate() called with invalid power(%d).", power);
+ energy_use = 0;
+ }
+ break;
+ }
+
+ p_ptr->redraw |= (PR_HP | PR_MANA);
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/*
+ * Print a batch of power.
+ */
+static void print_power_batch(int *p, int start, int max, bool_ mode)
+{
+ char buff[80];
+ power_type* spell;
+ int i = start, j = 0;
+
+ if (mode) prt(format(" %-31s Level Mana Fail", "Name"), 1, 20);
+
+ for (i = start; i < (start + 20); i++)
+ {
+ if (i >= max) break;
+
+ spell = &powers_type[p[i]];
+
+ sprintf(buff, " %c-%3d) %-30s %5d %4d %s@%d", I2A(j), p[i] + 1, spell->name,
+ spell->level, spell->cost, stat_names[spell->stat], spell->diff);
+
+ if (mode) prt(buff, 2 + j, 20);
+ j++;
+ }
+ if (mode) prt("", 2 + j, 20);
+ prt(format("Select a power (a-%c), +/- to scroll:", I2A(j - 1)), 0, 0);
+}
+
+
+/*
+ * List powers and ask to pick one.
+ */
+
+static power_type* select_power(int *x_idx)
+{
+ char which;
+ int max = 0, i, start = 0;
+ power_type* ret;
+ bool_ mode = FALSE;
+ int *p;
+
+ C_MAKE(p, power_max, int);
+ /* Count the max */
+ for (i = 0; i < power_max; i++)
+ {
+ if (p_ptr->powers[i])
+ {
+ p[max++] = i;
+ }
+ }
+
+ /* Exit if there aren't powers */
+ if (max == 0)
+ {
+ *x_idx = -1;
+ ret = NULL;
+ msg_print("You don't have any special powers.");
+ }
+ else
+ {
+ character_icky = TRUE;
+ Term_save();
+
+ while (1)
+ {
+ print_power_batch(p, start, max, mode);
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ *x_idx = -1;
+ ret = NULL;
+ break;
+ }
+ else if (which == '*' || which == '?' || which == ' ')
+ {
+ mode = (mode) ? FALSE : TRUE;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '+')
+ {
+ start += 20;
+ if (start >= max) start -= 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '-')
+ {
+ start -= 20;
+ if (start < 0) start += 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else
+ {
+ which = tolower(which);
+ if (start + A2I(which) >= max)
+ {
+ bell();
+ continue;
+ }
+ if (start + A2I(which) < 0)
+ {
+ bell();
+ continue;
+ }
+
+ *x_idx = p[start + A2I(which)];
+ ret = &powers_type[p[start + A2I(which)]];
+ break;
+ }
+ }
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ C_FREE(p, power_max, int);
+
+ return ret;
+}
+
+/* Ask & execute a power */
+void do_cmd_power()
+{
+ int x_idx;
+ power_type *x_ptr;
+ bool_ push = TRUE;
+
+ /* Get the skill, if available */
+ if (repeat_pull(&x_idx))
+ {
+ if ((x_idx < 0) || (x_idx >= power_max)) return;
+ x_ptr = &powers_type[x_idx];
+ push = FALSE;
+ }
+ else if (!command_arg) x_ptr = select_power(&x_idx);
+ else
+ {
+ x_idx = command_arg - 1;
+ if ((x_idx < 0) || (x_idx >= power_max)) return;
+ x_ptr = &powers_type[x_idx];
+ }
+
+ if (x_ptr == NULL) return;
+
+ if (push) repeat_push(x_idx);
+
+ if (p_ptr->powers[x_idx])
+ power_activate(x_idx);
+ else
+ msg_print("You do not have access to this power.");
+}
diff --git a/src/q_betwen.c b/src/q_betwen.c
new file mode 100644
index 00000000..e6452dd9
--- /dev/null
+++ b/src/q_betwen.c
@@ -0,0 +1,188 @@
+#undef cquest
+#define cquest (quest[QUEST_BETWEEN])
+
+bool_ quest_between_move_hook(char *fmt)
+{
+ s32b y;
+ s32b x;
+ cave_type *c_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ /* The tower of Turgon */
+ if ((c_ptr->feat == FEAT_SHOP) && (c_ptr->special == 27))
+ {
+ cmsg_print(TERM_YELLOW, "Turgon is there.");
+ cmsg_print(TERM_YELLOW, "'Ah, thank you, noble hero! Now please return to Minas Anor to finish the link.'");
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ return TRUE;
+ }
+
+ /* Only 1 ambush */
+ if (cquest.data[0]) return (FALSE);
+
+ if (!p_ptr->wild_mode)
+ {
+ if (p_ptr->wilderness_y > 19) return (FALSE);
+ }
+ else
+ {
+ if (p_ptr->py > 19) return (FALSE);
+ }
+
+ /* Mark as entered */
+ cquest.data[0] = TRUE;
+
+ p_ptr->wild_mode = FALSE;
+ p_ptr->inside_quest = QUEST_BETWEEN;
+ p_ptr->leaving = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Looks like a full wing of thunderlords ambushes you!");
+ cmsg_print(TERM_YELLOW, "Trone steps forth and speaks: 'The secret of the Void Jumpgates");
+ cmsg_print(TERM_YELLOW, "will not be used by any but the thunderlords!'");
+
+ return FALSE;
+}
+bool_ quest_between_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_BETWEEN) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("between.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ /* Otherwise instadeath */
+ energy_use = 0;
+
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ return TRUE;
+}
+bool_ quest_between_finish_hook(char *fmt)
+{
+ s32b q_idx;
+ object_type forge, *q_ptr;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_BETWEEN) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Ah you finally arrived, I hope your travel wasn't too hard.", 8, 0);
+ c_put_str(TERM_YELLOW, "As a reward you can freely use the Void Jumpgates for quick travel.", 9, 0);
+ c_put_str(TERM_YELLOW, "Oh and take that horn, it shall serve you well.", 10, 0);
+
+ /* prepare the reward */
+ q_ptr = &forge;
+ object_prep(q_ptr, test_item_name("& Golden Horn~ of the Thunderlords"));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = 1;
+
+
+ /* Mega-Hack -- Actually create the Golden Horn of the Thunderlords */
+ k_allow_special[test_item_name("& Golden Horn~ of the Thunderlords")] = TRUE;
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+ k_allow_special[test_item_name("& Golden Horn~ of the Thunderlords")] = FALSE;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->discount = 100;
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FINISH, quest_between_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_between_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_BETWEEN) return FALSE;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_NEUTRAL) mcnt++;
+ }
+
+ if (mcnt < 2)
+ {
+ cmsg_print(TERM_YELLOW, "You can escape now.");
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ cave[p_ptr->py][p_ptr->px].special = 0;
+
+ return FALSE;
+ }
+
+
+ return FALSE;
+}
+bool_ quest_between_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n You established a permanent void jumpgates liaison between Minas Anor and Gondolin,");
+ fprintf(hook_file, "\n thus allowing the last alliance to exist.");
+ }
+ return (FALSE);
+}
+bool_ quest_between_forbid_hook(char *fmt)
+{
+ s32b q_idx;
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_BETWEEN) return (FALSE);
+
+ if (p_ptr->lev < 45)
+ {
+ c_put_str(TERM_WHITE, "I fear you are not ready for the next quest, come back later.", 8, 0);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+bool_ quest_between_init_hook(int q)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MOVE, quest_between_move_hook, "between_move");
+ add_hook(HOOK_GEN_QUEST, quest_between_gen_hook, "between_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_between_finish_hook, "between_finish");
+ add_hook(HOOK_MONSTER_DEATH, quest_between_death_hook, "between_death");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_between_dump_hook, "between_dump");
+ add_hook(HOOK_INIT_QUEST, quest_between_forbid_hook, "between_forbid");
+ return (FALSE);
+}
diff --git a/src/q_dragons.c b/src/q_dragons.c
new file mode 100644
index 00000000..025e8ecd
--- /dev/null
+++ b/src/q_dragons.c
@@ -0,0 +1,150 @@
+#undef cquest
+#define cquest (quest[QUEST_DRAGONS])
+
+bool_ quest_dragons_gen_hook(char *fmt)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_DRAGONS) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("dragons.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some columns */
+ for (i = 35; i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ /* Bar columns on even squares so the whole level is guaranteed to be
+ accessible */
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags % 2) && !(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ --i;
+ cave_set_feat(y, x, FEAT_MOUNTAIN);
+ }
+ }
+
+ /* Place some random dragons */
+ for (i = 25; i > 0; )
+ {
+ int m_idx, flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ /* blue, white, red, black, bronze, gold, green, multi-hued */
+ int baby_dragons[8] = {163, 164, 167, 166, 218, 219, 165, 204};
+ int young_dragons[8] = {459, 460, 563, 546, 462, 559, 461, 556};
+ int mature_dragons[8] = {560, 549, 589, 592, 562, 590, 561, 593};
+ int happy_dragons[8] = {601, 617, 644, 624, 602, 645, 618, 675};
+
+ int chance, dragon, color;
+
+ color = rand_int(8);
+ chance = rand_int(100);
+ if (chance == 0)
+ dragon = happy_dragons[color];
+ else if (chance < 33)
+ dragon = baby_dragons[color];
+ else if (chance < 66)
+ dragon = young_dragons[color];
+ else
+ dragon = mature_dragons[color];
+
+ --i;
+ m_idx = place_monster_one(y, x, dragon, 0, magik(33), MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool_ quest_dragons_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_DRAGONS) return FALSE;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (mcnt <= 1)
+ {
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_dragons_death_hook);
+ del_hook(HOOK_GEN_QUEST, quest_dragons_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Gondolin is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+bool_ quest_dragons_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_DRAGONS) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for killing the dragons!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the cave as your house as a reward.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_EOL;
+
+ return TRUE;
+}
+
+bool_ quest_dragons_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_dragons_death_hook, "dragons_monster_death");
+ add_hook(HOOK_QUEST_FINISH, quest_dragons_finish_hook, "dragons_finish");
+ add_hook(HOOK_GEN_QUEST, quest_dragons_gen_hook, "dragons_geb");
+ }
+ return (FALSE);
+}
diff --git a/src/q_eol.c b/src/q_eol.c
new file mode 100644
index 00000000..5b1cf78e
--- /dev/null
+++ b/src/q_eol.c
@@ -0,0 +1,195 @@
+#undef cquest
+#define cquest (quest[QUEST_EOL])
+
+bool_ quest_eol_gen_hook(char *fmt)
+{
+ int x, y;
+ bool_ done = FALSE;
+ int xsize = 50, ysize = 30, y0, x0;
+ int m_idx = 0;
+
+ if (p_ptr->inside_quest != QUEST_EOL) return FALSE;
+
+ x0 = 2 + (xsize / 2);
+ y0 = 2 + (ysize / 2);
+
+ feat_wall_outer = FEAT_WALL_OUTER;
+ feat_wall_inner = FEAT_WALL_INNER;
+
+ for (y = 0; y < 100; y++)
+ {
+ floor_type[y] = FEAT_FLOOR;
+ fill_type[y] = FEAT_WALL_OUTER;
+ }
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ while (!done)
+ {
+ int grd, roug, cutoff;
+
+ /* testing values for these parameters feel free to adjust*/
+ grd = 2 ^ (randint(4));
+
+ /* want average of about 16 */
+ roug = randint(8) * randint(4);
+
+ /* about size/2 */
+ cutoff = randint(xsize / 4) + randint(ysize / 4) + randint(xsize / 4) + randint(ysize / 4);
+
+ /* make it */
+ generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff);
+
+ /* Convert to normal format+ clean up*/
+ done = generate_fracave(y0, x0, xsize, ysize, cutoff, FALSE, TRUE);
+ }
+
+ /* Place a few traps */
+ for (x = xsize - 1; x >= 2; x--)
+ for (y = ysize - 1; y >= 2; y--)
+ {
+ if (!cave_clean_bold(y, x)) continue;
+
+ /* Place eol at the other end */
+ if (!m_idx)
+ {
+ m_allow_special[test_monster_name("Eol, the Dark Elf")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Eol, the Dark Elf"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Eol, the Dark Elf")] = FALSE;
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ }
+
+ if (magik(18))
+ {
+ place_trap(y, x);
+ }
+
+ /* Place player at one end */
+ p_ptr->py = y;
+ p_ptr->px = x;
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+
+ return TRUE;
+}
+bool_ quest_eol_finish_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_EOL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "A tragedy, but the deed needed to be done.", 8, 0);
+ c_put_str(TERM_YELLOW, "Accept my thanks, and that reward.", 9, 0);
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_LITE, SV_LITE_DWARVEN));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->name2 = EGO_LITE_MAGI;
+ apply_magic(q_ptr, 1, FALSE, FALSE, FALSE);
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NIRNAETH;
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_eol_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_eol_fail_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_EOL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "You fled ! I did not think you would flee...", 8, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FAIL, quest_eol_fail_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_eol_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_EOL) return FALSE;
+
+ if (r_idx == test_monster_name("Eol, the Dark Elf"))
+ {
+ cmsg_print(TERM_YELLOW, "Such a sad end...");
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_eol_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return FALSE;
+}
+bool_ quest_eol_stair_hook(char *fmt)
+{
+ monster_race *r_ptr = &r_info[test_monster_name("Eol, the Dark Elf")];
+ cptr down;
+
+ down = get_next_arg_str(fmt);
+
+ if (p_ptr->inside_quest != QUEST_EOL) return FALSE;
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_LESS) return TRUE;
+
+ if (r_ptr->max_num)
+ {
+ if (!strcmp(down, "up"))
+ {
+ /* Flush input */
+ flush();
+
+ if (!get_check("Really abandon the quest?")) return TRUE;
+
+ cmsg_print(TERM_YELLOW, "You flee away from Eol...");
+ cquest.status = QUEST_STATUS_FAILED;
+ del_hook(HOOK_STAIR, quest_eol_stair_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+ }
+
+ return FALSE;
+}
+bool_ quest_eol_init_hook(int q)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_eol_death_hook, "eol_death");
+ add_hook(HOOK_GEN_QUEST, quest_eol_gen_hook, "eol_gen");
+ add_hook(HOOK_STAIR, quest_eol_stair_hook, "eol_stair");
+ add_hook(HOOK_QUEST_FAIL, quest_eol_fail_hook, "eol_fail");
+ add_hook(HOOK_QUEST_FINISH, quest_eol_finish_hook, "eol_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_evil.c b/src/q_evil.c
new file mode 100644
index 00000000..a143f65c
--- /dev/null
+++ b/src/q_evil.c
@@ -0,0 +1,117 @@
+#undef cquest
+#define cquest (quest[QUEST_EVIL])
+
+bool_ quest_evil_gen_hook(char *fmt)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_EVIL) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("evil.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some random balrogs */
+ for (i = 6; i > 0; )
+ {
+ int m_idx, flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ m_idx = place_monster_one(y, x, 996, 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool_ quest_evil_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_EVIL) return FALSE;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (mcnt <= 1)
+ {
+ /* TODO: change to COMPLETED and remove NULL when mayor is added */
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_FINISHED;
+ *(quest[p_ptr->inside_quest].plot) = QUEST_NULL;
+ del_hook(HOOK_MONSTER_DEATH, quest_evil_death_hook);
+ del_hook(HOOK_GEN_QUEST, quest_evil_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Khazad-Dum is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+bool_ quest_evil_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_EVIL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for saving us!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the cave as your house as a reward.", 9, 0);
+
+ /* End the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ return TRUE;
+}
+
+bool_ quest_evil_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_evil_death_hook, "evil_monster_death");
+ add_hook(HOOK_QUEST_FINISH, quest_evil_finish_hook, "evil_finish");
+ add_hook(HOOK_GEN_QUEST, quest_evil_gen_hook, "evil_geb");
+ }
+ return (FALSE);
+}
diff --git a/src/q_haunted.c b/src/q_haunted.c
new file mode 100644
index 00000000..db8992bc
--- /dev/null
+++ b/src/q_haunted.c
@@ -0,0 +1,147 @@
+#undef cquest
+#define cquest (quest[QUEST_HAUNTED])
+
+bool_ quest_haunted_gen_hook(char *fmt)
+{
+ int x, y, i, m_idx;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_HAUNTED) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("haunted.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some ghosts */
+ for (i = 12; i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ m_idx = place_monster_one(y, x, 477, 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ /* Place some random monsters to haunt us */
+ for (i = damroll(4, 4); i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ int monsters[22] = { 65, 100, 124, 125, 133, 231, 273, 327, 365, 416, 418,
+ 507, 508, 533, 534, 553, 554, 555, 577, 607, 622, 665};
+ int monster = monsters[rand_int(22)];
+ m_idx = place_monster_one(y, x, monster, 0, FALSE, MSTATUS_ENEMY);
+ m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ /* Place some random traps */
+ for (i = 10 + damroll(4, 4); i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ --i;
+ place_trap(y, x);
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool_ quest_haunted_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_HAUNTED) return FALSE;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (mcnt <= 1)
+ {
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_haunted_death_hook);
+ del_hook(HOOK_GEN_QUEST, quest_haunted_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Minas Anor is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+bool_ quest_haunted_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_HAUNTED) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for saving us!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the building as your house as a reward.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_BETWEEN;
+
+ return TRUE;
+}
+
+bool_ quest_haunted_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_haunted_death_hook, "haunted_monster_death");
+ add_hook(HOOK_QUEST_FINISH, quest_haunted_finish_hook, "haunted_finish");
+ add_hook(HOOK_GEN_QUEST, quest_haunted_gen_hook, "haunted_geb");
+ }
+ return (FALSE);
+}
diff --git a/src/q_hobbit.c b/src/q_hobbit.c
new file mode 100644
index 00000000..f3b7d856
--- /dev/null
+++ b/src/q_hobbit.c
@@ -0,0 +1,195 @@
+#undef cquest
+#define cquest (quest[QUEST_HOBBIT])
+
+bool_ quest_hobbit_town_gen_hook(char *fmt)
+{
+ int x = 1, y = 1, tries = 10000;
+ s32b small;
+
+ small = get_next_arg(fmt);
+
+ if ((turn < (cquest.data[1] + (DAY * 10L))) || (cquest.status > QUEST_STATUS_COMPLETED) || (small) || (p_ptr->town_num != 1)) return (FALSE);
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(20) + (cur_hgt / 2) - 10;
+ x = randint(20) + (cur_wid / 2) - 10;
+
+ /* Is it a good spot ? */
+ /* Not in player los, and avoid shop grids */
+ if (!los(p_ptr->py, p_ptr->px, y, x) && cave_empty_bold(y, x) &&
+ cave_plain_floor_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place Melinda */
+ m_allow_special[test_monster_name("Melinda Proudfoot")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Melinda Proudfoot"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Melinda Proudfoot")] = FALSE;
+
+ return FALSE;
+}
+bool_ quest_hobbit_gen_hook(char *fmt)
+{
+ int x = 1, y = 1, tries = 10000;
+
+ if ((cquest.status != QUEST_STATUS_TAKEN) || (dun_level != cquest.data[0]) || (dungeon_type != DUNGEON_MAZE)) return FALSE;
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(cur_hgt - 4) + 2;
+ x = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place the hobbit */
+ m_allow_special[test_monster_name("Merton Proudfoot, the lost hobbit")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Merton Proudfoot, the lost hobbit"), 0, FALSE, MSTATUS_FRIEND);
+ m_allow_special[test_monster_name("Merton Proudfoot, the lost hobbit")] = FALSE;
+
+ return FALSE;
+}
+bool_ quest_hobbit_give_hook(char *fmt)
+{
+ object_type *o_ptr;
+ monster_type *m_ptr;
+ s32b m_idx, item;
+
+ m_idx = get_next_arg(fmt);
+ item = get_next_arg(fmt);
+
+ o_ptr = &p_ptr->inventory[item];
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != test_monster_name("Merton Proudfoot, the lost hobbit")) return (FALSE);
+
+ if ((o_ptr->tval != TV_SCROLL) || (o_ptr->sval != SV_SCROLL_WORD_OF_RECALL)) return (FALSE);
+
+ msg_print("'Oh, thank you, noble one!'");
+ msg_print("Merton Proudfoot reads the scroll and is recalled to the safety of his home.");
+
+ delete_monster_idx(m_idx);
+
+ inc_stack_size_ex(item, -1, OPTIMIZE, NO_DESCRIBE);
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ del_hook(HOOK_GIVE, quest_hobbit_give_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_hobbit_speak_hook(char *fmt)
+{
+ s32b m_idx = get_next_arg(fmt);
+
+ if (m_list[m_idx].r_idx != test_monster_name("Melinda Proudfoot")) return (FALSE);
+
+ if (cquest.status < QUEST_STATUS_COMPLETED)
+ {
+ cptr m_name;
+
+ m_name = get_next_arg_str(fmt);
+
+ msg_format("%^s begs for your help.", m_name);
+ }
+ return (TRUE);
+}
+bool_ quest_hobbit_chat_hook(char *fmt)
+{
+ monster_type *m_ptr;
+ s32b m_idx;
+
+ m_idx = get_next_arg(fmt);
+
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != test_monster_name("Melinda Proudfoot")) return (FALSE);
+
+ if (cquest.status < QUEST_STATUS_COMPLETED)
+ {
+ msg_print("Oh! Oh!");
+ msg_print("My poor Merton, where is my poor Merton? He was playing near that dreadful");
+ msg_print("maze and never been seen again! Could you find him for me?");
+
+ cquest.status = QUEST_STATUS_TAKEN;
+ quest[QUEST_HOBBIT].init(QUEST_HOBBIT);
+ }
+ else if (cquest.status == QUEST_STATUS_COMPLETED)
+ {
+ object_type forge, *q_ptr;
+
+ msg_print("My Merton is back! You saved him, hero.");
+ msg_print("Take this as a proof of my gratitude. It was given to my family");
+ msg_print("by a famed wizard, but it should serve you better than me.");
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_ROD, SV_ROD_RECALL));
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_REWARD;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ cquest.status = QUEST_STATUS_FINISHED;
+
+ del_hook(HOOK_MON_SPEAK, quest_hobbit_speak_hook);
+ process_hooks_restart = TRUE;
+ delete_monster_idx(m_idx);
+
+ return TRUE;
+ }
+ else
+ {
+ msg_print("Thanks again.");
+ }
+
+ return TRUE;
+}
+bool_ quest_hobbit_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n You saved a young hobbit from an horrible fate.");
+ }
+ return (FALSE);
+}
+bool_ quest_hobbit_init_hook(int q_idx)
+{
+ /* Get a level to place the hobbit */
+ if (!cquest.data[0])
+ {
+ cquest.data[0] = rand_range(26, 34);
+ cquest.data[1] = turn;
+ if (wizard) message_add(MESSAGE_MSG, format("Hobbit level %d", cquest.data[0]), TERM_BLUE);
+ }
+
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_GIVE, quest_hobbit_give_hook, "hobbit_give");
+ add_hook(HOOK_GEN_LEVEL, quest_hobbit_gen_hook, "hobbit_gen");
+ add_hook(HOOK_WILD_GEN, quest_hobbit_town_gen_hook, "hobbit_town_gen");
+ add_hook(HOOK_CHAT, quest_hobbit_chat_hook, "hobbit_chat");
+ add_hook(HOOK_MON_SPEAK, quest_hobbit_speak_hook, "hobbit_speak");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook(HOOK_MON_SPEAK, quest_hobbit_speak_hook, "hobbit_speak");
+ add_hook(HOOK_WILD_GEN, quest_hobbit_town_gen_hook, "hobbit_town_gen");
+ add_hook(HOOK_CHAT, quest_hobbit_chat_hook, "hobbit_chat");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_hobbit_dump_hook, "hobbit_dump");
+ return (FALSE);
+}
diff --git a/src/q_invas.c b/src/q_invas.c
new file mode 100644
index 00000000..59c71d62
--- /dev/null
+++ b/src/q_invas.c
@@ -0,0 +1,201 @@
+#undef cquest
+#define cquest (quest[QUEST_INVASION])
+
+bool_ quest_invasion_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("maeglin.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ cquest.data[0] = y;
+ cquest.data[1] = x;
+ p_ptr->py = y;
+ p_ptr->px = x;
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ }
+ }
+
+ return TRUE;
+}
+bool_ quest_invasion_ai_hook(char *fmt)
+{
+ monster_type *m_ptr;
+ s32b m_idx;
+
+ m_idx = get_next_arg(fmt);
+ m_ptr = &m_list[m_idx];
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ /* Ugly but thats better than a call to test_monster_name which is SLOW */
+ if (m_ptr->r_idx == 825)
+ {
+ /* Oups he fleed */
+ if ((m_ptr->fy == cquest.data[0]) && (m_ptr->fx == cquest.data[1]))
+ {
+ delete_monster_idx(m_idx);
+
+ cmsg_print(TERM_YELLOW, "Maeglin found the way to Gondolin! All hope is lost now!");
+ cquest.status = QUEST_STATUS_FAILED;
+ town_info[2].destroyed = TRUE;
+ return (FALSE);
+ }
+
+ /* Attack or flee ?*/
+ if (distance(m_ptr->fy, m_ptr->fx, p_ptr->py, p_ptr->px) <= 2)
+ {
+ return (FALSE);
+ }
+ else
+ {
+ process_hooks_return[0].num = cquest.data[0];
+ process_hooks_return[1].num = cquest.data[1];
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+bool_ quest_invasion_turn_hook(char *fmt)
+{
+ bool_ old_quick_messages = quick_messages;
+
+ if (cquest.status != QUEST_STATUS_UNTAKEN) return (FALSE);
+ if (p_ptr->lev < 45) return (FALSE);
+
+ /* Wait until the end of the current quest */
+ if (p_ptr->inside_quest) return ( FALSE);
+
+ /* Wait until the end of the astral mode */
+ if (p_ptr->astral) return ( FALSE);
+
+ /* Ok give the quest */
+ quick_messages = FALSE;
+ cmsg_print(TERM_YELLOW, "A Thunderlord appears in front of you and says:");
+ cmsg_print(TERM_YELLOW, "'Hello, noble hero. I am Liron, rider of Tolan. Turgon, King of Gondolin sent me.'");
+ cmsg_print(TERM_YELLOW, "'Gondolin is being invaded; he needs your help now or everything will be lost.'");
+ cmsg_print(TERM_YELLOW, "'Please come quickly!'");
+
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ quick_messages = old_quick_messages;
+
+ quest_invasion_init_hook(QUEST_INVASION);
+ del_hook(HOOK_END_TURN, quest_invasion_turn_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+}
+bool_ quest_invasion_dump_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_FAILED)
+ {
+ fprintf(hook_file, "\n You abandoned Gondolin when it most needed you, thus causing its destruction.");
+ }
+ if ((cquest.status == QUEST_STATUS_FINISHED) || (cquest.status == QUEST_STATUS_REWARDED) || (cquest.status == QUEST_STATUS_COMPLETED))
+ {
+ fprintf(hook_file, "\n You saved Gondolin from destruction.");
+ }
+ return (FALSE);
+}
+bool_ quest_invasion_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ if (r_idx == test_monster_name("Maeglin, the Traitor of Gondolin"))
+ {
+ cmsg_print(TERM_YELLOW, "You did it! Gondolin will remain hidden.");
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_invasion_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return FALSE;
+}
+bool_ quest_invasion_stair_hook(char *fmt)
+{
+ cptr down;
+
+ down = get_next_arg_str(fmt);
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_LESS) return TRUE;
+
+ if (!strcmp(down, "up"))
+ {
+ if (cquest.status == QUEST_STATUS_FAILED)
+ {
+ cmsg_print(TERM_YELLOW, "The armies of Morgoth totally devastated Gondolin, leaving nothing but ruins...");
+ }
+ else if (cquest.status == QUEST_STATUS_COMPLETED)
+ {
+ cmsg_print(TERM_YELLOW, "Turgon appears before you and speaks:");
+ cmsg_print(TERM_YELLOW, "'I will never be able to thank you enough.'");
+ cmsg_print(TERM_YELLOW, "'My most powerful mages will cast a powerful spell for you, giving you extra life.'");
+
+ p_ptr->hp_mod += 150;
+ p_ptr->update |= (PU_HP);
+ cquest.status = QUEST_STATUS_FINISHED;
+ }
+ else
+ {
+ /* Flush input */
+ flush();
+
+ if (!get_check("Really abandon the quest?")) return TRUE;
+ cmsg_print(TERM_YELLOW, "You flee away from Maeglin and his army...");
+ cquest.status = QUEST_STATUS_FAILED;
+ town_info[2].destroyed = TRUE;
+ }
+ del_hook(HOOK_STAIR, quest_invasion_stair_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return TRUE;
+}
+bool_ quest_invasion_init_hook(int q_idx)
+{
+ add_hook(HOOK_END_TURN, quest_invasion_turn_hook, "invasion_turn");
+ add_hook(HOOK_CHAR_DUMP, quest_invasion_dump_hook, "invasion_dump");
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_AI, quest_invasion_ai_hook, "invasion_ai");
+ add_hook(HOOK_GEN_QUEST, quest_invasion_gen_hook, "invasion_gen");
+ add_hook(HOOK_MONSTER_DEATH, quest_invasion_death_hook, "invasion_death");
+ add_hook(HOOK_STAIR, quest_invasion_stair_hook, "invasion_stair");
+ }
+ return (FALSE);
+}
diff --git a/src/q_main.c b/src/q_main.c
new file mode 100644
index 00000000..a13b0790
--- /dev/null
+++ b/src/q_main.c
@@ -0,0 +1,176 @@
+bool_ quest_main_monsters_hook(char *fmt)
+{
+ s32b r_idx;
+ r_idx = get_next_arg(fmt);
+
+ /* Sauron */
+ if (r_idx == 860)
+ {
+ /* No Sauron until Necromancer dies */
+ if (r_info[819].max_num) return TRUE;
+ }
+ /* Morgoth */
+ else if (r_idx == 862)
+ {
+ /* No Morgoth until Sauron dies */
+ if (r_info[860].max_num) return TRUE;
+ }
+ return FALSE;
+}
+bool_ quest_morgoth_hook(char *fmt)
+{
+ /* Using test_monster_name() here would be a lot less ugly, but would take much more time */
+ monster_race *r_ptr = &r_info[862];
+
+ /* Need to kill him */
+ if (!r_ptr->max_num)
+ {
+ /* Total winner */
+ total_winner = WINNER_NORMAL;
+ has_won = WINNER_NORMAL;
+ quest[QUEST_MORGOTH].status = QUEST_STATUS_FINISHED;
+
+ /* Redraw the "title" */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Congratulations */
+ if (quest[QUEST_ONE].status == QUEST_STATUS_FINISHED)
+ {
+ cmsg_print(TERM_L_GREEN, "*** CONGRATULATIONS ***");
+ cmsg_print(TERM_L_GREEN, "You have banished Morgoth's foul spirit from Ea, and as you watch, a cleansing");
+ cmsg_print(TERM_L_GREEN, "wind roars through the dungeon, dispersing the nether mists around where the");
+ cmsg_print(TERM_L_GREEN, "body fell. You feel thanks, and a touch of sorrow, from the Valar");
+ cmsg_print(TERM_L_GREEN, "for your deed. You will be forever heralded, your deed forever legendary.");
+ cmsg_print(TERM_L_GREEN, "You may retire (commit suicide) when you are ready.");
+ }
+ else
+ {
+ cmsg_print(TERM_VIOLET, "*** CONGRATULATIONS ***");
+ cmsg_print(TERM_VIOLET, "You have banished Morgoth from Arda, and made Ea a safer place.");
+ cmsg_print(TERM_VIOLET, "As you look down at the dispersing mists around Morgoth, a sudden intuition");
+ cmsg_print(TERM_VIOLET, "grasps you. Fingering the One Ring, you gather the nether mists around");
+ cmsg_print(TERM_VIOLET, "yourself, and inhale deeply their seductive power.");
+ cmsg_print(TERM_VIOLET, "You will be forever feared, your orders forever obeyed.");
+ cmsg_print(TERM_VIOLET, "You may retire (commit suicide) when you are ready.");
+ }
+
+ /* Continue the plot(maybe) */
+ del_hook(HOOK_MONSTER_DEATH, quest_morgoth_hook);
+ process_hooks_restart = TRUE;
+
+ /* Either ultra good if the one Ring is destroyed, or ultra evil if used */
+ if (quest[QUEST_ONE].status == QUEST_STATUS_FINISHED)
+ *(quest[QUEST_MORGOTH].plot) = QUEST_ULTRA_GOOD;
+ else
+ *(quest[QUEST_MORGOTH].plot) = QUEST_ULTRA_EVIL;
+ quest[*(quest[QUEST_MORGOTH].plot)].init(*(quest[QUEST_MORGOTH].plot));
+ }
+ return (FALSE);
+}
+bool_ quest_morgoth_dump_hook(char *fmt)
+{
+ if (quest[QUEST_MORGOTH].status >= QUEST_STATUS_COMPLETED)
+ {
+ if (quest[QUEST_ONE].status == QUEST_STATUS_FINISHED)
+ fprintf(hook_file, "\n You saved Arda and became a famed %s.", sp_ptr->winner);
+ else
+ fprintf(hook_file, "\n You became a new force of darkness and enslaved all free people.");
+ }
+ return (FALSE);
+}
+bool_ quest_morgoth_init_hook(int q_idx)
+{
+ if ((quest[QUEST_MORGOTH].status >= QUEST_STATUS_TAKEN) && (quest[QUEST_MORGOTH].status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_morgoth_hook, "morgort_death");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_morgoth_dump_hook, "morgoth_dump");
+ add_hook(HOOK_NEW_MONSTER, quest_main_monsters_hook, "main_new_monster");
+ return (FALSE);
+}
+
+bool_ quest_sauron_hook(char *fmt)
+{
+ /* Using test_monster_name() here would be a lot less ugly, but would take much more time */
+ monster_race *r_ptr = &r_info[860];
+
+ /* Need to kill him */
+ if (!r_ptr->max_num)
+ {
+ cmsg_print(TERM_YELLOW, "Well done! You are on the way to slaying Morgoth...");
+ quest[QUEST_SAURON].status = QUEST_STATUS_FINISHED;
+
+ quest[QUEST_MORGOTH].status = QUEST_STATUS_TAKEN;
+ quest_describe(QUEST_MORGOTH);
+
+ del_hook(HOOK_MONSTER_DEATH, quest_sauron_hook);
+ add_hook(HOOK_MONSTER_DEATH, quest_morgoth_hook, "morgort_death");
+ *(quest[QUEST_SAURON].plot) = QUEST_MORGOTH;
+ quest_morgoth_init_hook(QUEST_MORGOTH);
+
+ process_hooks_restart = TRUE;
+ }
+ return (FALSE);
+}
+
+bool_ quest_sauron_resurect_hook(char *fmt)
+{
+ s32b m_idx = get_next_arg(fmt);
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ if ((r_ptr->flags7 & RF7_NAZGUL) && r_info[860].max_num)
+ {
+ msg_format("Somehow you feel %s is not totally destroyed...", (r_ptr->flags1 & RF1_FEMALE ? "she" : "he"));
+ r_ptr->max_num = 1;
+ }
+ else if ((m_ptr->r_idx == 860) && (quest[QUEST_ONE].status < QUEST_STATUS_FINISHED))
+ {
+ msg_print("Sauron will not be permanently defeated until the One Ring is either destroyed or used...");
+ r_ptr->max_num = 1;
+ }
+ return FALSE;
+}
+
+bool_ quest_sauron_init_hook(int q_idx)
+{
+ if ((quest[QUEST_SAURON].status >= QUEST_STATUS_TAKEN) && (quest[QUEST_SAURON].status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_sauron_hook, "sauron_death");
+ }
+ add_hook(HOOK_NEW_MONSTER, quest_main_monsters_hook, "main_new_monster");
+ add_hook(HOOK_MONSTER_DEATH, quest_sauron_resurect_hook, "sauron_resurect_death");
+ return (FALSE);
+}
+
+bool_ quest_necro_hook(char *fmt)
+{
+ /* Using test_monster_name() here would be a lot less ugly, but would take much more time */
+ monster_race *r_ptr = &r_info[819];
+
+ /* Need to kill him */
+ if (!r_ptr->max_num)
+ {
+ cmsg_print(TERM_YELLOW, "You see the spirit of the necromancer rise and flee...");
+ cmsg_print(TERM_YELLOW, "It looks like it was indeed Sauron...");
+ cmsg_print(TERM_YELLOW, "You should report that to Galadriel as soon as possible.");
+
+ quest[QUEST_NECRO].status = QUEST_STATUS_FINISHED;
+
+ *(quest[QUEST_NECRO].plot) = QUEST_ONE;
+ quest[*(quest[QUEST_NECRO].plot)].init(*(quest[QUEST_NECRO].plot));
+
+ del_hook(HOOK_MONSTER_DEATH, quest_necro_hook);
+ process_hooks_restart = TRUE;
+ }
+ return (FALSE);
+}
+bool_ quest_necro_init_hook(int q_idx)
+{
+ if ((quest[QUEST_NECRO].status >= QUEST_STATUS_TAKEN) && (quest[QUEST_NECRO].status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_necro_hook, "necro_death");
+ }
+ add_hook(HOOK_NEW_MONSTER, quest_main_monsters_hook, "main_new_monster");
+ return (FALSE);
+}
diff --git a/src/q_narsil.c b/src/q_narsil.c
new file mode 100644
index 00000000..27ec218e
--- /dev/null
+++ b/src/q_narsil.c
@@ -0,0 +1,108 @@
+#undef cquest
+#define cquest (quest[QUEST_NARSIL])
+
+bool_ quest_narsil_move_hook(char *fmt)
+{
+ s32b y, x;
+ cave_type *c_ptr;
+ int i;
+ object_type *o_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ /* The castle of Aragorn */
+ if ((c_ptr->feat != FEAT_SHOP) || (c_ptr->special != 14)) return FALSE;
+
+ /* Look out for Narsil */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = get_object(i);
+
+ if (!o_ptr->k_idx) continue;
+
+ if (o_ptr->name1 == ART_NARSIL) break;
+ }
+
+ if (i == INVEN_TOTAL) return FALSE;
+
+ cmsg_print(TERM_YELLOW, "I heard that the broken sword had been found!");
+ cmsg_print(TERM_YELLOW, "I thought it was only a rumor... until now.");
+ cmsg_print(TERM_YELLOW, "What you have is really the sword that was broken.");
+ cmsg_print(TERM_YELLOW, "I will get it reforged...");
+ cmsg_print(TERM_L_BLUE, "Aragorn leaves for a few hours then comes back...");
+ cmsg_print(TERM_YELLOW, "Here it is, Anduril, the sword that was forged and is");
+ cmsg_print(TERM_YELLOW, "reforged again. Take it; you will surely need it for your quest.");
+
+ object_prep(o_ptr, lookup_kind(TV_SWORD, SV_LONG_SWORD));
+ o_ptr->name1 = ART_ANDURIL;
+ apply_magic(o_ptr, -1, TRUE, TRUE, TRUE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ inven_item_describe(i);
+ inven_item_optimize(i);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER | PW_INVEN);
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_FINISHED;
+
+ del_hook(HOOK_MOVE, quest_narsil_move_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_narsil_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n The sword that was broken is now reforged.");
+ }
+ return (FALSE);
+}
+bool_ quest_narsil_identify_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ int i;
+ object_type *o_ptr;
+ s32b item;
+
+ item = get_next_arg(fmt);
+
+ o_ptr = get_object(item);
+
+ if (o_ptr->name1 == ART_NARSIL)
+ {
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ for (i = 0; i < 5; i++)
+ {
+ if (quest[QUEST_NARSIL].desc[i][0] != '\0')
+ {
+ cmsg_print(TERM_YELLOW, quest[QUEST_NARSIL].desc[i]);
+ }
+ }
+
+ add_hook(HOOK_MOVE, quest_narsil_move_hook, "narsil_move");
+ del_hook(HOOK_IDENTIFY, quest_narsil_identify_hook);
+ process_hooks_restart = TRUE;
+ }
+ }
+
+ return (FALSE);
+}
+bool_ quest_narsil_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MOVE, quest_narsil_move_hook, "narsil_move");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN) add_hook(HOOK_IDENTIFY, quest_narsil_identify_hook, "narsil_id");
+ add_hook(HOOK_CHAR_DUMP, quest_narsil_dump_hook, "narsil_dump");
+ return (FALSE);
+}
diff --git a/src/q_nazgul.c b/src/q_nazgul.c
new file mode 100644
index 00000000..66d3dc98
--- /dev/null
+++ b/src/q_nazgul.c
@@ -0,0 +1,116 @@
+#undef cquest
+#define cquest (quest[QUEST_NAZGUL])
+
+bool_ quest_nazgul_gen_hook(char *fmt)
+{
+ int m_idx, x = 1, y = 1, tries = 10000;
+ s32b small;
+
+ small = get_next_arg(fmt);
+
+ if ((cquest.status != QUEST_STATUS_TAKEN) || (small) || (p_ptr->town_num != 1)) return (FALSE);
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(cur_hgt - 4) + 2;
+ x = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ /* Not in player los */
+ if ((!los(p_ptr->py, p_ptr->px, y, x)) && cave_empty_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place the nazgul */
+ m_allow_special[test_monster_name("Uvatha the Horseman")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Uvatha the Horseman"), 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ m_allow_special[test_monster_name("Uvatha the Horseman")] = FALSE;
+
+ return FALSE;
+}
+bool_ quest_nazgul_finish_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_NAZGUL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "I believe he will not come back! Thank you.", 8, 0);
+ c_put_str(TERM_YELLOW, "Some time ago a ranger gave me this.", 9, 0);
+ c_put_str(TERM_YELLOW, "I believe it will help you on your quest.", 10, 0);
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_FOOD, SV_FOOD_ATHELAS));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = 6;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* End the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FINISH, quest_nazgul_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_nazgul_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n You saved Bree from a dreadful Nazgul.");
+ }
+ return (FALSE);
+}
+bool_ quest_nazgul_forbid_hook(char *fmt)
+{
+ s32b q_idx;
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_NAZGUL) return (FALSE);
+
+ if (p_ptr->lev < 30)
+ {
+ c_put_str(TERM_WHITE, "I fear you are not ready for the next quest, come back later.", 8, 0);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+bool_ quest_nazgul_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return (FALSE);
+ if (r_idx != test_monster_name("Uvatha the Horseman")) return (FALSE);
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ del_hook(HOOK_MONSTER_DEATH, quest_nazgul_death_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+}
+bool_ quest_nazgul_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_nazgul_death_hook, "nazgul_death");
+ add_hook(HOOK_WILD_GEN, quest_nazgul_gen_hook, "nazgul_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_nazgul_finish_hook, "nazgul_finish");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_nazgul_dump_hook, "nazgul_dump");
+ add_hook(HOOK_INIT_QUEST, quest_nazgul_forbid_hook, "nazgul_forbid");
+ return (FALSE);
+}
diff --git a/src/q_nirna.c b/src/q_nirna.c
new file mode 100644
index 00000000..be856d31
--- /dev/null
+++ b/src/q_nirna.c
@@ -0,0 +1,109 @@
+#undef cquest
+#define cquest (quest[QUEST_NIRNAETH])
+
+bool_ quest_nirnaeth_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_NIRNAETH) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("nirnaeth.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ /* Count the number of monsters */
+ cquest.data[0] = 0;
+ cquest.data[1] = 0;
+ for (x = 2; x < xstart; x++)
+ for (y = 2; y < ystart; y++)
+ {
+ if (cave[y][x].m_idx) cquest.data[0]++;
+ }
+
+ return TRUE;
+}
+bool_ quest_nirnaeth_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_NIRNAETH) return FALSE;
+
+ /* Killed at least 2/3 of them ? better reward ! */
+ if (cquest.data[1] >= (2 * cquest.data[0] / 3))
+ {
+ c_put_str(TERM_YELLOW, "Not only did you found a way out, but you also destroyed a good", 8, 0);
+ c_put_str(TERM_YELLOW, "number of trolls! Thank you so much. Take this gold please.", 9, 0);
+ c_put_str(TERM_YELLOW, "I also grant you access to the royal jewelry shop!", 10, 0);
+
+ p_ptr->au += 200000;
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else
+ {
+ c_put_str(TERM_YELLOW, "I thank you for your efforts.", 8, 0);
+ c_put_str(TERM_YELLOW, "I grant you access to the royal jewelry shop!", 9, 0);
+ }
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FINISH, quest_nirnaeth_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_nirnaeth_death_hook(char *fmt)
+{
+ if (p_ptr->inside_quest != QUEST_NIRNAETH) return FALSE;
+
+ cquest.data[1]++;
+
+ return FALSE;
+}
+bool_ quest_nirnaeth_stair_hook(char *fmt)
+{
+ if (p_ptr->inside_quest != QUEST_NIRNAETH) return FALSE;
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_LESS) return (FALSE);
+
+ cmsg_print(TERM_YELLOW, "You found a way out!");
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_STAIR, quest_nirnaeth_stair_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+}
+bool_ quest_nirnaeth_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_nirnaeth_death_hook, "nirnaeth_death");
+ add_hook(HOOK_GEN_QUEST, quest_nirnaeth_gen_hook, "nirnaeth_gen");
+ add_hook(HOOK_STAIR, quest_nirnaeth_stair_hook, "nirnaeth_stair");
+ add_hook(HOOK_QUEST_FINISH, quest_nirnaeth_finish_hook, "nirnaeth_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_one.c b/src/q_one.c
new file mode 100644
index 00000000..4bfeaf3e
--- /dev/null
+++ b/src/q_one.c
@@ -0,0 +1,354 @@
+#undef cquest
+#define cquest (quest[QUEST_ONE])
+
+bool_ quest_one_move_hook(char *fmt)
+{
+ s32b y, x;
+ cave_type *c_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ if (quest[QUEST_NECRO].status < QUEST_STATUS_FINISHED) return (FALSE);
+
+ /* The mirror of Galadriel */
+ if ((c_ptr->feat != FEAT_SHOP) || (c_ptr->special != 23)) return (FALSE);
+
+ cmsg_print(TERM_YELLOW, "You meet Galadriel; she seems worried.");
+ cmsg_print(TERM_YELLOW, "'So it was Sauron that lurked in Dol Guldur...'");
+ cmsg_print(TERM_YELLOW, "'The Enemy is growing in power. Morgoth will be unreachable as long'");
+ cmsg_print(TERM_YELLOW, "'as his most powerful servant, Sauron, lives. But the power of Sauron'");
+ cmsg_print(TERM_YELLOW, "'lies in the One Ring. Our only hope is that you find it'");
+ cmsg_print(TERM_YELLOW, "'and destroy it. I know it will tempt you, but *NEVER* use it'");
+ cmsg_print(TERM_YELLOW, "'or it will corrupt you forever.'");
+
+ GOD(GOD_ERU)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Eru will abandon you if you wear it.'");
+ }
+
+ GOD(GOD_MANWE)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Manwe will abandon you if you wear it.'");
+ }
+
+ GOD(GOD_TULKAS)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Tulkas will abandon you if you wear it.'");
+ }
+
+ GOD(GOD_YAVANNA)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Yavanna will abandon you if you wear it.'");
+ }
+
+ cmsg_print(TERM_YELLOW, "'Without the destruction of the ring, Sauron's death can only be temporary'");
+ cmsg_print(TERM_YELLOW, "'When you have it, bring it to Mount Doom, in Mordor,'");
+ cmsg_print(TERM_YELLOW, "'to destroy it in the Great Fire where it was forged.'");
+ cmsg_print(TERM_YELLOW, "'I do not know where to find it. Seek it through Middle-earth. Maybe there'");
+ cmsg_print(TERM_YELLOW, "'are other people that might know.'");
+ cmsg_print(TERM_YELLOW, "'Do not forget: the Ring must be cast back into the fires of Mount Doom!'");
+
+ GOD(GOD_MELKOR)
+ {
+ cmsg_print(TERM_YELLOW, "'Melkor will abandon you when you do, but you must do it anyway!'");
+ }
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_TAKEN;
+ cquest.init(QUEST_ONE);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+bool_ quest_one_drop_hook(char *fmt)
+{
+ s32b o_idx;
+ object_type *o_ptr;
+
+ o_idx = get_next_arg(fmt);
+ o_ptr = &p_ptr->inventory[o_idx];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ if (o_ptr->name1 != ART_POWER) return FALSE;
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_GREAT_FIRE) return FALSE;
+
+ cmsg_print(TERM_YELLOW, "You throw the One Ring into the #RGreat Fire#y; it is rapidly consumed");
+ cmsg_print(TERM_YELLOW, "by the searing flames.");
+ cmsg_print(TERM_YELLOW, "You feel the powers of evil weakening.");
+ cmsg_print(TERM_YELLOW, "Now you can go onto the hunt for Sauron!");
+
+ inc_stack_size_ex(o_idx, -99, OPTIMIZE, NO_DESCRIBE);
+
+ abandon_god(GOD_MELKOR);
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_FINISHED;
+ *(quest[QUEST_ONE].plot) = QUEST_SAURON;
+ quest[*(quest[QUEST_ONE].plot)].status = QUEST_STATUS_TAKEN;
+ quest[*(quest[QUEST_ONE].plot)].init(*(quest[QUEST_ONE].plot));
+
+ return TRUE;
+}
+bool_ quest_one_wield_hook(char *fmt)
+{
+ s32b o_idx;
+ object_type *o_ptr;
+
+ o_idx = get_next_arg(fmt);
+ o_ptr = &p_ptr->inventory[o_idx];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ if (o_ptr->name1 != ART_POWER) return FALSE;
+
+ /* Flush input */
+ flush();
+
+ if (!get_check("You were warned not to wear it; are you sure?")) return TRUE;
+ /* Flush input */
+ flush();
+
+ if (!get_check("You were warned not to wear it; are you *REALLY* sure?")) return TRUE;
+
+ /* Flush input */
+ flush();
+
+ if (!get_check("You were *WARNED* not to wear it; are you *R*E*A*L*L*Y* sure?")) return TRUE;
+
+ cmsg_print(TERM_YELLOW, "As you put it on your finger you feel #Ddark powers #ysapping your soul.");
+ cmsg_print(TERM_YELLOW, "The ring firmly binds to your finger!");
+ cmsg_print(TERM_YELLOW, "You feel you are drawn to the shadow world! Your material form weakens.");
+
+ abandon_god(GOD_ERU);
+ abandon_god(GOD_MANWE);
+ abandon_god(GOD_TULKAS);
+ abandon_god(GOD_YAVANNA);
+
+ /*
+ * Ok now we are evil, right ?
+ * Towns aren't, right ?
+ * So let's destroy them !
+ */
+ town_info[1].destroyed = TRUE;
+ town_info[2].destroyed = TRUE;
+ town_info[3].destroyed = TRUE;
+ town_info[4].destroyed = TRUE;
+ town_info[5].destroyed = TRUE;
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_FAILED_DONE;
+ *(quest[QUEST_ONE].plot) = QUEST_SAURON;
+ quest[*(quest[QUEST_ONE].plot)].status = QUEST_STATUS_TAKEN;
+ quest[*(quest[QUEST_ONE].plot)].init(*(quest[QUEST_ONE].plot));
+
+ /* Ok lets reset the lives counter */
+ p_ptr->lives = 0;
+
+ return FALSE;
+}
+bool_ quest_one_hp_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_FAILED_DONE)
+ {
+ s32b mhp;
+ int i;
+
+ mhp = get_next_arg(fmt);
+
+ for (i = 0; i < p_ptr->lives + 1; i++)
+ mhp = (mhp * 2) / 3;
+
+ process_hooks_return[0].num = mhp;
+ return (TRUE);
+ }
+ return (FALSE);
+}
+bool_ quest_one_die_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_FAILED_DONE)
+ {
+ if (p_ptr->mhp > 1)
+ {
+ cmsg_print(TERM_YELLOW, "You feel the power of the One Ring sustaining your life,");
+ cmsg_print(TERM_YELLOW, "but it drags you even more into the shadow world.");
+ return (TRUE);
+ }
+ else
+ {
+ cmsg_print(TERM_YELLOW, "The One Ring finally drags you totally to the shadow world.");
+ cmsg_print(TERM_YELLOW, "Your mortal existence ends there.");
+ strcpy(died_from, "being drawn to the shadow world");
+ }
+ }
+ return (FALSE);
+}
+bool_ quest_one_identify_hook(char *fmt)
+{
+ s32b item;
+
+ item = get_next_arg(fmt);
+
+ if (cquest.status == QUEST_STATUS_TAKEN)
+ {
+ object_type *o_ptr;
+
+ o_ptr = get_object(item);
+
+ if ((o_ptr->name1 == ART_POWER) && (!object_known_p(o_ptr)))
+ {
+ cmsg_print(TERM_YELLOW, "You finally found the One Ring, source of Sauron's power, and key to");
+ cmsg_print(TERM_YELLOW, "its destruction. Remember, bring it to Mount Doom and destroy it.");
+ cmsg_print(TERM_YELLOW, "And *NEVER* use it.");
+ }
+ }
+
+ return (FALSE);
+}
+bool_ quest_one_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+ bool_ ok = FALSE;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (a_info[ART_POWER].cur_num) return FALSE;
+
+ /* Paranoia */
+ if (cquest.status != QUEST_STATUS_TAKEN) return (FALSE);
+
+ if (magik(30) && (r_idx == test_monster_name("Sauron, the Sorcerer")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("Ar-Pharazon the Golden")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("Shelob, Spider of Darkness")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("The Watcher in the Water")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("Glaurung, Father of the Dragons")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("Feagwath, the Undead Sorcerer")))
+ {
+ ok = TRUE;
+ }
+
+ if (ok)
+ {
+ int i;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ /* Mega-Hack -- Prepare to make "Grond" */
+ object_prep(q_ptr, lookup_kind(TV_RING, SV_RING_POWER));
+
+ /* Mega-Hack -- Mark this item as "the one ring" */
+ q_ptr->name1 = ART_POWER;
+
+ /* Mega-Hack -- Actually create "the one ring" */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Find a space */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ /* Skip non-objects */
+ if (!p_ptr->inventory[i].k_idx) break;
+ }
+ /* Arg, no space ! */
+ if (i == INVEN_PACK)
+ {
+ char o_name[200];
+
+ object_desc(o_name, &p_ptr->inventory[INVEN_PACK - 1], FALSE, 0);
+
+ /* Drop the item */
+ inven_drop(INVEN_PACK - 1, 99, p_ptr->py, p_ptr->px, FALSE);
+
+ cmsg_format(TERM_VIOLET, "You feel the urge to drop your %s to make room in your inventory.", o_name);
+ }
+
+ /* Carry it */
+ cmsg_format(TERM_VIOLET, "You feel the urge to pick up that plain gold ring you see.");
+ inven_carry(q_ptr, FALSE);
+ }
+
+ return (FALSE);
+}
+bool_ quest_one_dump_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_FINISHED)
+ {
+ fprintf(hook_file, "\n You destroyed the One Ring, thus weakening Sauron.");
+ }
+ if (cquest.status == QUEST_STATUS_FAILED_DONE)
+ {
+ fprintf(hook_file, "\n You fell under the evil influence of the One Ring and decided to wear it.");
+ }
+ return (FALSE);
+}
+bool_ quest_one_gen_hook(char *fmt)
+{
+ s32b x, y, tries = 10000;
+
+ /* Paranoia */
+ if (cquest.status != QUEST_STATUS_TAKEN) return (FALSE);
+ if ((dungeon_type != DUNGEON_ANGBAND) || (dun_level != 99)) return (FALSE);
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(cur_hgt - 4) + 2;
+ x = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ if (tries)
+ {
+ int m_idx = place_monster_one(y, x, test_monster_name("Sauron, the Sorcerer"), 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ }
+
+ return (FALSE);
+}
+bool_ quest_one_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_LEVEL_END_GEN, quest_one_gen_hook, "one_gen");
+ add_hook(HOOK_MONSTER_DEATH, quest_one_death_hook, "one_death");
+ add_hook(HOOK_DROP, quest_one_drop_hook, "one_drop");
+ add_hook(HOOK_WIELD, quest_one_wield_hook, "one_wield");
+ add_hook(HOOK_IDENTIFY, quest_one_identify_hook, "one_id");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook(HOOK_MOVE, quest_one_move_hook, "one_move");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_one_dump_hook, "one_dump");
+ add_hook(HOOK_CALC_HP, quest_one_hp_hook, "one_hp");
+ add_hook(HOOK_DIE, quest_one_die_hook, "one_die");
+ return (FALSE);
+}
diff --git a/src/q_poison.c b/src/q_poison.c
new file mode 100644
index 00000000..e6fed3a1
--- /dev/null
+++ b/src/q_poison.c
@@ -0,0 +1,238 @@
+#undef cquest
+#define cquest (quest[QUEST_POISON])
+
+static int wild_locs[4][2] =
+{
+ { 32, 49, },
+ { 32, 48, },
+ { 33, 48, },
+ { 34, 48, },
+};
+
+static bool_ create_molds_hook(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags4 & RF4_MULTIPLY) return FALSE;
+
+ if (r_ptr->d_char == 'm') return TRUE;
+ else if (r_ptr->d_char == ',') return TRUE;
+ else if (r_ptr->d_char == 'e') return TRUE;
+ else return FALSE;
+}
+
+bool_ quest_poison_gen_hook(char *fmt)
+{
+ int cy = 1, cx = 1, x, y, tries = 10000, r_idx;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+ if (p_ptr->wilderness_y != wild_locs[cquest.data[0]][0]) return FALSE;
+ if (p_ptr->wilderness_x != wild_locs[cquest.data[0]][1]) return FALSE;
+ if (p_ptr->wild_mode) return FALSE;
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ cy = randint(cur_hgt - 24) + 22;
+ cx = randint(cur_wid - 34) + 32;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(cy, cx)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place the baddies */
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = create_molds_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a monster, using the level calculation */
+ for (x = cx - 25; x <= cx + 25; x++)
+ for (y = cy - 25; y <= cy + 25; y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (distance(cy, cx, y, x) > 25) continue;
+
+ if (magik(80) && ((cave[y][x].feat == FEAT_DEEP_WATER) || (cave[y][x].feat == FEAT_SHAL_WATER))) cave_set_feat(y, x, FEAT_TAINTED_WATER);
+
+ if (distance(cy, cx, y, x) > 10) continue;
+
+ if (magik(60))
+ {
+ int m_idx;
+
+ r_idx = get_mon_num(30);
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+
+ /* Sometimes make it up some levels */
+ if (magik(80) && m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (m_ptr->level < p_ptr->lev)
+ {
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + randint(p_ptr->lev - m_ptr->level));
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+ }
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ return FALSE;
+}
+bool_ quest_poison_finish_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_POISON) return FALSE;
+
+ c_put_str(TERM_YELLOW, "The water is clean again! Thank you so much.", 8, 0);
+ c_put_str(TERM_YELLOW, "The beautiful Mallorns are safe. Take this as a proof of our gratitude.", 9, 0);
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_DRAG_ARMOR, SV_DRAGON_BLUE));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = 1;
+ q_ptr->name2 = EGO_ELVENKIND;
+ apply_magic(q_ptr, 1, FALSE, FALSE, FALSE);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FINISH, quest_poison_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_poison_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n You saved the beautiful Mallorns of Lothlorien.");
+ }
+ return (FALSE);
+}
+bool_ quest_poison_quest_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_POISON) return FALSE;
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_POTION2, SV_POTION2_CURE_WATER));
+ q_ptr->number = 99;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ q_ptr->note = quark_add("quest");
+ (void)inven_carry(q_ptr, FALSE);
+
+ del_hook(HOOK_INIT_QUEST, quest_poison_quest_hook);
+ process_hooks_restart = TRUE;
+
+ return FALSE;
+}
+bool_ quest_poison_drop_hook(char *fmt)
+{
+ s32b mcnt = 0, i, x, y, o_idx;
+ object_type *o_ptr;
+
+ o_idx = get_next_arg(fmt);
+ o_ptr = &p_ptr->inventory[o_idx];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+ if (p_ptr->wilderness_y != wild_locs[cquest.data[0]][0]) return FALSE;
+ if (p_ptr->wilderness_x != wild_locs[cquest.data[0]][1]) return FALSE;
+ if (p_ptr->wild_mode) return FALSE;
+
+ if (o_ptr->tval != TV_POTION2) return FALSE;
+ if (o_ptr->sval != SV_POTION2_CURE_WATER) return FALSE;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_NEUTRAL) mcnt++;
+ }
+
+ if (mcnt < 10)
+ {
+ for (x = 1; x < cur_wid - 1; x++)
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (cave[y][x].feat == FEAT_TAINTED_WATER) cave_set_feat(y, x, FEAT_SHAL_WATER);
+ }
+
+ cmsg_print(TERM_YELLOW, "Well done! The water seems to be clean now.");
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ del_hook(HOOK_DROP, quest_poison_drop_hook);
+ process_hooks_restart = TRUE;
+
+ return FALSE;
+ }
+ else
+ {
+ msg_print("There are too many monsters left to cure the water.");
+ return TRUE;
+ }
+ return FALSE;
+}
+bool_ quest_poison_init_hook(int q_idx)
+{
+ /* Get a place to place the poison */
+ if (!cquest.data[1])
+ {
+ cquest.data[1] = TRUE;
+ cquest.data[0] = rand_int(4);
+ if (wizard) message_add(MESSAGE_MSG, format("Wilderness poison %d, %d", wild_locs[cquest.data[0]][0], wild_locs[cquest.data[0]][1]), TERM_BLUE);
+ }
+
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_DROP, quest_poison_drop_hook, "poison_drop");
+ add_hook(HOOK_WILD_GEN, quest_poison_gen_hook, "poison_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_poison_finish_hook, "poison_finish");
+ }
+ if (cquest.status < QUEST_STATUS_COMPLETED)
+ {
+ add_hook(HOOK_INIT_QUEST, quest_poison_quest_hook, "poison_iquest");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_poison_dump_hook, "poison_dump");
+ return (FALSE);
+}
diff --git a/src/q_rand.c b/src/q_rand.c
new file mode 100644
index 00000000..5a3b3ab2
--- /dev/null
+++ b/src/q_rand.c
@@ -0,0 +1,465 @@
+static int randquest_hero[] = { 20, 13, 15, 16, 9, 17, 18, 8, -1 };
+
+bool_ is_randhero(int level)
+{
+ int i;
+ bool_ result = FALSE;
+
+ for (i = 0; randquest_hero[i] != -1; i++)
+ {
+ if (random_quests[level].type == randquest_hero[i])
+ {
+ result = TRUE;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void do_get_new_obj(int y, int x)
+{
+ obj_theme theme;
+ char *items[3];
+ object_type *q_ptr[3], forge[3];
+ int max = 0, res, i;
+
+ /* Create 3 ones */
+ max = 0;
+ for (i = 0; i < 3; i++)
+ {
+ /* Get local object */
+ q_ptr[max] = &forge[max];
+
+ /* Wipe the object */
+ object_wipe(q_ptr[max]);
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+
+ /* Make a great object */
+ make_object(q_ptr[max], TRUE, TRUE, theme);
+ q_ptr[max]->found = OBJ_FOUND_REWARD;
+
+ C_MAKE(items[max], 100, char);
+ object_desc(items[max], q_ptr[max], 0, 0);
+ max++;
+ }
+
+
+ while (TRUE)
+ {
+ res = ask_menu("Choose a reward to get(a-c to choose, ESC to cancel)?", (char **)items, 3);
+
+ /* Ok ? lets learn ! */
+ if (res > -1)
+ {
+ /* Drop it in the dungeon */
+ drop_near(q_ptr[res], -1, y + 1, x);
+
+ cmsg_print(TERM_YELLOW, "There, Noble Hero. I put it there. Thanks again!");
+ break;
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+
+ object_type *o_ptr = q_ptr[i];
+
+ /* Check if there is any not chosen artifact */
+ if (i != res && artifact_p(o_ptr))
+ {
+ /* Mega-Hack -- Preserve the artifact */
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = FALSE;
+ }
+ else if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[o_ptr->k_idx].artifact = FALSE;
+ }
+ else if (o_ptr->name1)
+ {
+ a_info[o_ptr->name1].cur_num = 0;
+ }
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ C_KILL(items[i], 100, char);
+
+}
+
+void princess_death(s32b m_idx, s32b r_idx)
+{
+ int r;
+
+ cmsg_print(TERM_YELLOW, "O Great And Noble Hero, you saved me!");
+ cmsg_print(TERM_YELLOW, "I am heading home now. I cannot reward you as I should, but please take this.");
+
+ /* Look for the princess */
+ for (r = m_max - 1; r >= 1; r--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[r];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Is it the princess? */
+ if (m_ptr->r_idx == 969)
+ {
+ int x = m_ptr->fx;
+ int y = m_ptr->fy;
+ int i, j;
+
+ delete_monster_idx(r);
+
+ /* Wipe the glass walls and create a stair */
+ for (i = x - 1; i <= x + 1; i++)
+ for (j = y - 1; j <= y + 1; j++)
+ {
+ if (in_bounds(j, i)) cave_set_feat(j, i, FEAT_FLOOR);
+ }
+ cave_set_feat(y, x, FEAT_MORE);
+
+ do_get_new_obj(y, x);
+
+ random_quests[dun_level].done = TRUE;
+
+ break;
+ }
+ }
+}
+
+void hero_death(s32b m_idx, s32b r_idx)
+{
+ random_quests[dun_level].done = TRUE;
+
+ cmsg_print(TERM_YELLOW, "The adventurer steps out of the shadows and picks up his sword:");
+ cmsg_print(TERM_YELLOW, "'Ah! My sword! My trusty sword! Thanks.");
+
+ if (!can_create_companion())
+ {
+ cmsg_print(TERM_YELLOW, "I must go on my own way now.");
+ cmsg_print(TERM_YELLOW, "But before I go, I can help your skills.'");
+ cmsg_print(TERM_YELLOW, "He touches your forehead.");
+ do_get_new_skill();
+ return;
+ }
+ cmsg_print(TERM_YELLOW, "If you wish, I can help you in your adventures.'");
+
+ /* Flush input */
+ flush();
+
+ if (get_check("Do you want him to join you? "))
+ {
+ int x, y, i;
+
+ /* Look for a location */
+ for (i = 0; i < 20; ++i)
+ {
+ /* Pick a distance */
+ int d = (i / 15) + 1;
+
+ /* Pick a location */
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ /* Require "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) continue;
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START) &&
+ (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ continue;
+
+ /* Okay */
+ break;
+ }
+
+ if (i < 20)
+ {
+ int m_idx;
+
+ m_allow_special[test_monster_name("Adventurer")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Adventurer"), 0, FALSE, MSTATUS_COMPANION);
+ m_allow_special[test_monster_name("Adventurer")] = FALSE;
+ if (m_idx)
+ {
+ m_list[m_idx].exp = MONSTER_EXP(1 + (dun_level * 3 / 2));
+ m_list[m_idx].status = MSTATUS_COMPANION;
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ else
+ msg_print("The adventurer suddenly seems afraid and flees...");
+ }
+ else
+ {
+ cmsg_print(TERM_YELLOW, "'As you wish, but I want to do something for you.'");
+ cmsg_print(TERM_YELLOW, "He touches your forehead.");
+ do_get_new_skill();
+ }
+}
+
+bool_ quest_random_death_hook(char *fmt)
+{
+ int r_idx;
+ s32b m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return (FALSE);
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return (FALSE);
+ if (!random_quests[dun_level].type) return (FALSE);
+ if (random_quests[dun_level].done) return (FALSE);
+ if (p_ptr->inside_quest) return (FALSE);
+ if (random_quests[dun_level].r_idx != r_idx) return (FALSE);
+
+ if (!(m_list[m_idx].mflag & MFLAG_QUEST)) return (FALSE);
+
+ /* Killed enough ?*/
+ quest[QUEST_RANDOM].data[0]++;
+ if (quest[QUEST_RANDOM].data[0] == random_quests[dun_level].type)
+ {
+ if (is_randhero(dun_level))
+ hero_death(m_idx, r_idx);
+ else
+ princess_death(m_idx, r_idx);
+ }
+
+ return (FALSE);
+}
+bool_ quest_random_turn_hook(char *fmt)
+{
+ quest[QUEST_RANDOM].data[0] = 0;
+ quest[QUEST_RANDOM].data[1] = 0;
+ return (FALSE);
+}
+bool_ quest_random_feeling_hook(char *fmt)
+{
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return (FALSE);
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return (FALSE);
+ if (!random_quests[dun_level].type) return (FALSE);
+ if (random_quests[dun_level].done) return (FALSE);
+ if (p_ptr->inside_quest) return (FALSE);
+ if (!dun_level) return (FALSE);
+
+ if (is_randhero(dun_level))
+ {
+ cmsg_format(TERM_YELLOW, "A strange man wrapped in a dark cloak steps out of the shadows:");
+ cmsg_format(TERM_YELLOW, "'Oh, please help me! A horrible %s stole my sword! I'm nothing without it.'", r_info[random_quests[dun_level].r_idx].name + r_name);
+ }
+ else
+ cmsg_format(TERM_YELLOW, "You hear someone shouting: 'Leave me alone, stupid %s'", r_info[random_quests[dun_level].r_idx].name + r_name);
+ return (FALSE);
+}
+bool_ quest_random_gen_hero_hook(char *fmt)
+{
+ int i;
+
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return (FALSE);
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return (FALSE);
+ if (!random_quests[dun_level].type) return (FALSE);
+ if (random_quests[dun_level].done) return (FALSE);
+ if (p_ptr->inside_quest) return (FALSE);
+ if (!is_randhero(dun_level)) return (FALSE);
+
+ i = random_quests[dun_level].type;
+
+ m_allow_special[random_quests[dun_level].r_idx] = TRUE;
+ while (i)
+ {
+ int m_idx, y = rand_range(1, cur_hgt - 2), x = rand_range(1, cur_wid - 2);
+
+ m_idx = place_monster_one(y, x, random_quests[dun_level].r_idx, 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+ m_ptr->mflag |= MFLAG_QUEST;
+ i--;
+ }
+ }
+ m_allow_special[random_quests[dun_level].r_idx] = FALSE;
+
+ return (FALSE);
+}
+bool_ quest_random_gen_hook(char *fmt)
+{
+ s32b x, y, bx0, by0;
+ int xstart;
+ int ystart;
+ int y2, x2, yval, xval;
+ int y1, x1, xsize, ysize;
+
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return (FALSE);
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return (FALSE);
+ if (!random_quests[dun_level].type) return (FALSE);
+ if (random_quests[dun_level].done) return (FALSE);
+ if (p_ptr->inside_quest) return (FALSE);
+ if (quest[QUEST_RANDOM].data[1]) return (FALSE);
+ if (is_randhero(dun_level)) return (FALSE);
+
+ by0 = get_next_arg(fmt);
+ bx0 = get_next_arg(fmt);
+
+ /* Pick a room size */
+ get_map_size(format("qrand%d.map", random_quests[dun_level].type), &ysize, &xsize);
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return FALSE;
+
+ /* Get corner values */
+ y1 = yval - ysize / 2;
+ x1 = xval - xsize / 2;
+ y2 = y1 + ysize - 1;
+ x2 = x1 + xsize - 1;
+
+ /* Place a full floor under the room */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+ cave[y][x].info |= (CAVE_ROOM|CAVE_GLOW);
+ }
+ }
+
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM | CAVE_GLOW);
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ xstart = x1;
+ ystart = y1;
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(format("qrand%d.map", random_quests[dun_level].type), &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = x1; x < xstart; x++)
+ for (y = y1; y < ystart; y++)
+ {
+ cave[y][x].info |= CAVE_ICKY | CAVE_ROOM;
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ monster_type *m_ptr;
+ int i;
+
+ m_allow_special[random_quests[dun_level].r_idx] = TRUE;
+ i = place_monster_one(y, x, random_quests[dun_level].r_idx, 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[random_quests[dun_level].r_idx] = FALSE;
+ if (i)
+ {
+ m_ptr = &m_list[i];
+ m_ptr->mflag |= MFLAG_QUEST;
+ }
+ }
+ }
+
+ /* Dont try another one for this generation */
+ quest[QUEST_RANDOM].data[1] = 1;
+
+ /* Boost level feeling a bit - a la pits */
+ rating += 10;
+
+ return (TRUE);
+}
+bool_ quest_random_dump_hook(char *fmt)
+{
+ static char *number[] =
+ { "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" };
+ int i, valid = 0, lscnt = 0, pcnt = 0;
+
+ for (i = 0; i < MAX_RANDOM_QUEST; i++)
+ {
+ if (random_quests[i].type)
+ {
+ valid++;
+ if (random_quests[i].done)
+ {
+ if (is_randhero(i))
+ lscnt++;
+ else
+ pcnt++;
+ }
+ }
+ }
+
+ if (valid)
+ {
+ if (pcnt > 10)
+ fprintf(hook_file, "\n You have completed %d princess quests.", pcnt);
+ else if (pcnt > 1)
+ fprintf(hook_file, "\n You have completed %s princess quests.", number[pcnt-2]);
+ else if (pcnt == 1)
+ fprintf(hook_file, "\n You have completed one princess quest.");
+ else
+ fprintf(hook_file, "\n You haven't completed a single princess quest.");
+
+ if (lscnt > 10)
+ fprintf(hook_file, "\n You have completed %d lost sword quests.", lscnt);
+ else if (lscnt > 1)
+ fprintf(hook_file, "\n You have completed %s lost sword quests.", number[lscnt-2]);
+ else if (lscnt == 1)
+ fprintf(hook_file, "\n You have completed one lost sword quest.");
+ else
+ fprintf(hook_file, "\n You haven't completed a single lost sword quest.");
+ }
+
+ return (FALSE);
+}
+
+bool_ quest_random_describe(FILE *fff)
+{
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return FALSE;
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return FALSE;
+ if (!random_quests[dun_level].type) return FALSE;
+ if (random_quests[dun_level].done) return FALSE;
+ if (p_ptr->inside_quest) return FALSE;
+ if (!dun_level) return FALSE;
+
+ if (!is_randhero(dun_level))
+ {
+ fprintf(fff, "#####yCaptured princess!\n");
+ fprintf(fff, "A princess is being held prisoner and tortured here!\n");
+ fprintf(fff, "Save her from the horrible %s.\n",
+ r_info[random_quests[dun_level].r_idx].name + r_name);
+ }
+ else
+ {
+ fprintf(fff, "#####yLost sword!\n");
+ fprintf(fff, "An adventurer lost his sword to a bunch of %s!\n",
+ r_info[random_quests[dun_level].r_idx].name + r_name);
+ fprintf(fff, "Kill them all to get it back.\n");
+ }
+ fprintf(fff, "Number: %d, Killed: %ld.\n",
+ random_quests[dun_level].type, (long int) quest[QUEST_RANDOM].data[0]);
+ fprintf(fff, "\n");
+ return TRUE;
+}
+
+bool_ quest_random_init_hook(int q_idx)
+{
+ add_hook(HOOK_MONSTER_DEATH, quest_random_death_hook, "rand_death");
+ add_hook(HOOK_NEW_LEVEL, quest_random_turn_hook, "rand_new_lvl");
+ add_hook(HOOK_LEVEL_REGEN, quest_random_turn_hook, "rand_regen_lvl");
+ add_hook(HOOK_LEVEL_END_GEN, quest_random_gen_hero_hook, "rand_gen_hero");
+ add_hook(HOOK_BUILD_ROOM1, quest_random_gen_hook, "rand_gen");
+ add_hook(HOOK_FEELING, quest_random_feeling_hook, "rand_feel");
+ add_hook(HOOK_CHAR_DUMP, quest_random_dump_hook, "rand_dump");
+ return (FALSE);
+}
diff --git a/src/q_shroom.c b/src/q_shroom.c
new file mode 100644
index 00000000..b6e26cdf
--- /dev/null
+++ b/src/q_shroom.c
@@ -0,0 +1,293 @@
+#undef cquest
+#define cquest (quest[QUEST_SHROOM])
+
+bool_ quest_shroom_speak_hook(char *fmt);
+
+bool_ quest_shroom_town_gen_hook(char *fmt)
+{
+ int m_idx, x = 1, y = 1, tries = 10000;
+ s32b small;
+
+ small = get_next_arg(fmt);
+
+ /* Generate the shrooms field */
+ if ((!small) && (p_ptr->wilderness_y == 21) && (p_ptr->wilderness_x == 33))
+ {
+ /* Create the field */
+ for (x = (cur_wid / 2) - 7; x <= (cur_wid / 2) + 7; x++)
+ for (y = (cur_hgt / 2) - 5; y <= (cur_hgt / 2) + 5; y++)
+ cave_set_feat(y, x, 181);
+
+ /* Throw in some 'shrooms */
+ for (x = 0; x < (cquest.data[1] - cquest.data[0]); x++)
+ {
+ object_type forge, *q_ptr = &forge;
+
+ object_prep(q_ptr, lookup_kind(TV_FOOD, rand_range(1, 18)));
+ q_ptr->number = 1;
+ /* Mark them */
+ q_ptr->pval2 = 1;
+ drop_near(q_ptr, -1, rand_range((cur_hgt / 2) - 5, (cur_hgt / 2) + 5), rand_range((cur_wid / 2) - 7, (cur_wid / 2) + 7));
+ }
+
+ /* Throw in some dogs ;) */
+ y = rand_range((cur_hgt / 2) - 5, (cur_hgt / 2) + 5);
+ x = rand_range((cur_wid / 2) - 7, (cur_wid / 2) + 7);
+ m_allow_special[test_monster_name("Grip, Farmer Maggot's dog")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Grip, Farmer Maggot's dog"), 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ m_allow_special[test_monster_name("Grip, Farmer Maggot's dog")] = FALSE;
+
+ y = rand_range((cur_hgt / 2) - 5, (cur_hgt / 2) + 5);
+ x = rand_range((cur_wid / 2) - 7, (cur_wid / 2) + 7);
+ m_allow_special[test_monster_name("Wolf, Farmer Maggot's dog")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Wolf, Farmer Maggot's dog"), 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ m_allow_special[test_monster_name("Wolf, Farmer Maggot's dog")] = FALSE;
+
+ y = rand_range((cur_hgt / 2) - 5, (cur_hgt / 2) + 5);
+ x = rand_range((cur_wid / 2) - 7, (cur_wid / 2) + 7);
+ m_allow_special[test_monster_name("Fang, Farmer Maggot's dog")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Fang, Farmer Maggot's dog"), 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ m_allow_special[test_monster_name("Fang, Farmer Maggot's dog")] = FALSE;
+
+ msg_print("You hear frenzied barking.");
+ }
+
+ /* Generate maggot in town, in daylight */
+ if ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18) || (cquest.status > QUEST_STATUS_COMPLETED) || (small) || (p_ptr->town_num != 1)) return (FALSE);
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(20) + (cur_hgt / 2) - 10;
+ x = randint(20) + (cur_wid / 2) - 10;
+
+ /* Is it a good spot ? */
+ /* Not in player los, and avoid shop grids */
+ if (!los(p_ptr->py, p_ptr->px, y, x) && cave_empty_bold(y, x) &&
+ cave_plain_floor_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place Farmer Maggot */
+ m_allow_special[test_monster_name("Farmer Maggot")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Farmer Maggot"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Farmer Maggot")] = FALSE;
+
+ return FALSE;
+}
+bool_ quest_shroom_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (cquest.status > QUEST_STATUS_COMPLETED) return FALSE;
+
+ if ((r_idx == test_monster_name("Wolf, Farmer Maggot's dog")) ||
+ (r_idx == test_monster_name("Grip, Farmer Maggot's dog")) ||
+ (r_idx == test_monster_name("Fang, Farmer Maggot's dog")))
+ {
+ msg_print("The dog yells a last time and drops dead on the grass.");
+ }
+
+ return FALSE;
+}
+bool_ quest_shroom_give_hook(char *fmt)
+{
+ object_type *o_ptr;
+ monster_type *m_ptr;
+ s32b m_idx, item;
+
+ m_idx = get_next_arg(fmt);
+ item = get_next_arg(fmt);
+
+ o_ptr = &p_ptr->inventory[item];
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != test_monster_name("Farmer Maggot")) return (FALSE);
+
+ /* If one is dead .. its bad */
+ if ((r_info[test_monster_name("Grip, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Wolf, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Fang, Farmer Maggot's dog")].max_num == 0))
+ {
+ cquest.status = QUEST_STATUS_FAILED_DONE;
+ msg_print("My puppy! My poor, defenceless puppy...");
+ msg_print("YOU MURDERER! Out of my sight!");
+ delete_monster_idx(m_idx);
+
+ del_hook(HOOK_GIVE, quest_shroom_give_hook);
+ del_hook(HOOK_CHAT, quest_shroom_speak_hook);
+ del_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook);
+ process_hooks_restart = TRUE;
+ return TRUE;
+ }
+
+ if ((o_ptr->tval != TV_FOOD) || (o_ptr->pval2 != 1)) return (FALSE);
+
+ /* Take a mushroom */
+ inc_stack_size_ex(item, -1, OPTIMIZE, NO_DESCRIBE);
+ cquest.data[0]++;
+
+ if (cquest.data[0] == cquest.data[1])
+ {
+ object_type forge, *q_ptr;
+
+ msg_print("Oh thank you!");
+ msg_print("Take my sling and those mushrooms, may they help you!");
+ msg_print("Farmer Maggot heads back to his house.");
+
+ /* Mushrooms */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_FOOD, SV_FOOD_CURE_SERIOUS));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = rand_range(15, 20);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->discount = 100;
+ q_ptr->ident |= IDENT_STOREB;
+ if (inven_carry_okay(q_ptr))
+ inven_carry(q_ptr, FALSE);
+ else
+ drop_near(q_ptr, 0, p_ptr->py, p_ptr->px);
+
+ /* The sling of farmer maggot */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_BOW, SV_SLING));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = 1;
+ q_ptr->name1 = 149;
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->discount = 100;
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ delete_monster_idx(m_idx);
+
+ cquest.status = QUEST_STATUS_FINISHED;
+
+ del_hook(HOOK_GIVE, quest_shroom_give_hook);
+ process_hooks_restart = TRUE;
+ }
+ else
+ msg_format("Oh thank you, but you still have %d mushrooms to bring back!", cquest.data[1] - cquest.data[0]);
+
+ return TRUE;
+}
+bool_ quest_shroom_speak_hook(char *fmt)
+{
+ s32b m_idx = get_next_arg(fmt);
+
+ if (m_list[m_idx].r_idx != test_monster_name("Farmer Maggot")) return (FALSE);
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ cptr m_name;
+
+ m_name = get_next_arg_str(fmt);
+
+ msg_format("%^s asks your help.", m_name);
+ exec_lua("ingame_help('monster_chat')");
+ }
+ else
+ {
+ /* If one is dead .. its bad */
+ if ((r_info[test_monster_name("Grip, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Wolf, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Fang, Farmer Maggot's dog")].max_num == 0))
+ {
+ cquest.status = QUEST_STATUS_FAILED_DONE;
+ msg_print("My puppy! My poor, defenceless puppy...");
+ msg_print("YOU MURDERER! Out of my sight!");
+ delete_monster_idx(m_idx);
+
+ del_hook(HOOK_GIVE, quest_shroom_give_hook);
+ del_hook(HOOK_CHAT, quest_shroom_speak_hook);
+ del_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook);
+ process_hooks_restart = TRUE;
+ return TRUE;
+ }
+ msg_format("You still have %d mushrooms to bring back!", cquest.data[1] - cquest.data[0]);
+ }
+ return (TRUE);
+}
+bool_ quest_shroom_chat_hook(char *fmt)
+{
+ monster_type *m_ptr;
+ s32b m_idx;
+
+ m_idx = get_next_arg(fmt);
+
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != test_monster_name("Farmer Maggot")) return (FALSE);
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ msg_print("My mushrooms, my mushrooms!");
+ msg_print("The rain, a dark horrible rain, began so I had to return to my home.");
+ msg_print("But when I came back my dogs were all mad and didn't let me near the field.");
+ msg_print("Could you please bring me back all the mushrooms that have grown in my field");
+ msg_print("to the west of Bree? Please try to not harm my dogs. They are so lovely...");
+
+ cquest.status = QUEST_STATUS_TAKEN;
+ quest[QUEST_SHROOM].init(QUEST_SHROOM);
+ }
+ else
+ {
+ /* If one is dead .. its bad */
+ if ((r_info[test_monster_name("Grip, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Wolf, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Fang, Farmer Maggot's dog")].max_num == 0))
+ {
+ cquest.status = QUEST_STATUS_FAILED_DONE;
+ msg_print("My puppy! My poor, defenceless puppy...");
+ msg_print("YOU MURDERER! Out of my sight!");
+ delete_monster_idx(m_idx);
+
+ del_hook(HOOK_GIVE, quest_shroom_give_hook);
+ del_hook(HOOK_CHAT, quest_shroom_speak_hook);
+ del_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook);
+ process_hooks_restart = TRUE;
+ return TRUE;
+ }
+ msg_format("You still have %d mushrooms to bring back!", cquest.data[1] - cquest.data[0]);
+ }
+
+ return TRUE;
+}
+bool_ quest_shroom_init_hook(int q_idx)
+{
+ /* Get a number of 'shrooms */
+ if (!cquest.data[1])
+ {
+ cquest.data[0] = 0;
+ cquest.data[1] = rand_range(7, 14);
+ if (wizard) message_add(MESSAGE_MSG, format("Shrooms number %d", cquest.data[1]), TERM_BLUE);
+ }
+
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_shroom_death_hook, "shroom_death");
+ add_hook(HOOK_GIVE, quest_shroom_give_hook, "shroom_give");
+ add_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook, "shroom_town_gen");
+ add_hook(HOOK_CHAT, quest_shroom_chat_hook, "shroom_chat");
+ add_hook(HOOK_MON_SPEAK, quest_shroom_speak_hook, "shroom_speak");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook(HOOK_MON_SPEAK, quest_shroom_speak_hook, "shroom_speak");
+ add_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook, "shroom_town_gen");
+ add_hook(HOOK_CHAT, quest_shroom_chat_hook, "shroom_chat");
+ }
+ return (FALSE);
+}
diff --git a/src/q_spider.c b/src/q_spider.c
new file mode 100644
index 00000000..a739535b
--- /dev/null
+++ b/src/q_spider.c
@@ -0,0 +1,108 @@
+#undef cquest
+#define cquest (quest[QUEST_SPIDER])
+
+bool_ quest_spider_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_SPIDER) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("spiders.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ return TRUE;
+}
+bool_ quest_spider_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_SPIDER) return FALSE;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ if (mcnt <= 1)
+ {
+ cmsg_print(TERM_YELLOW, "The forest is now safer, thanks to you.");
+
+ /* Yavanna LOVES saving forests */
+ GOD(GOD_YAVANNA)
+ {
+ cmsg_print(TERM_L_GREEN, "You feel the gentle touch of Yavanna, as she smiles at you.");
+ inc_piety(GOD_YAVANNA, 6000);
+ }
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_spider_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return (FALSE);
+}
+bool_ quest_spider_finish_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_SPIDER) return FALSE;
+
+ c_put_str(TERM_YELLOW, "All of us praise your mighty deed in driving back the", 8, 0);
+ c_put_str(TERM_YELLOW, "menace. Take this as a reward.", 9, 0);
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_POTION, SV_POTION_AUGMENTATION));
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_REWARD;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_POISON;
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_spider_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_spider_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_spider_death_hook, "spider_death");
+ add_hook(HOOK_GEN_QUEST, quest_spider_gen_hook, "spider_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_spider_finish_hook, "spider_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_thief.c b/src/q_thief.c
new file mode 100644
index 00000000..6b033f8c
--- /dev/null
+++ b/src/q_thief.c
@@ -0,0 +1,172 @@
+#undef cquest
+#define cquest (quest[QUEST_THIEVES])
+
+bool_ quest_thieves_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+ bool_ again = TRUE;
+
+ if (p_ptr->inside_quest != QUEST_THIEVES) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("thieves.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Rip the inventory from the player */
+ cmsg_print(TERM_YELLOW, "You feel a vicious blow on your head.");
+ while (again)
+ {
+ again = FALSE;
+ for (x = 0; x < INVEN_TOTAL; x++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[x];
+
+ if (!o_ptr->k_idx) continue;
+
+ if ((x >= INVEN_WIELD) && cursed_p(o_ptr)) continue;
+
+ inven_drop(x, 99, 4, 24, TRUE);
+
+ /* Thats ugly .. but it works */
+ again = TRUE;
+ break;
+ }
+ }
+
+ del_hook(HOOK_GEN_QUEST, quest_thieves_gen_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_thieves_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_THIEVES) return FALSE;
+
+ /* ALARM !!! */
+ if ((cave[17][22].feat == FEAT_OPEN) ||
+ (cave[17][22].feat == FEAT_BROKEN))
+ {
+ cmsg_print(TERM_L_RED, "An alarm rings!");
+ aggravate_monsters(0);
+ cave_set_feat(14, 20, FEAT_OPEN);
+ cave_set_feat(14, 16, FEAT_OPEN);
+ cave_set_feat(14, 12, FEAT_OPEN);
+ cave_set_feat(14, 8, FEAT_OPEN);
+ cave_set_feat(20, 20, FEAT_OPEN);
+ cave_set_feat(20, 16, FEAT_OPEN);
+ cave_set_feat(20, 12, FEAT_OPEN);
+ cave_set_feat(20, 8, FEAT_OPEN);
+ msg_print("The door explodes.");
+ cave_set_feat(17, 22, FEAT_FLOOR);
+ }
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status < MSTATUS_FRIEND) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (!mcnt)
+ {
+ msg_print("The magic hiding the stairs is now gone.");
+ cave_set_feat(23, 4, FEAT_LESS);
+ cave[23][4].special = 0;
+
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_END_TURN, quest_thieves_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "You stopped the thieves and saved Bree!");
+ return (FALSE);
+ }
+ return FALSE;
+}
+bool_ quest_thieves_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_THIEVES) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for killing the band of thieves!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the hideout as your house as a reward.", 9, 0);
+
+ /* Continue the plot */
+
+ /* 10% chance to randomly select, otherwise use the combat/magic skill ratio */
+ if (magik(10) || (s_info[SKILL_COMBAT].value == s_info[SKILL_MAGIC].value))
+ {
+ *(quest[q_idx].plot) = (magik(50)) ? QUEST_TROLL : QUEST_WIGHT;
+ }
+ else
+ {
+ if (s_info[SKILL_COMBAT].value > s_info[SKILL_MAGIC].value)
+ *(quest[q_idx].plot) = QUEST_TROLL;
+ else
+ *(quest[q_idx].plot) = QUEST_WIGHT;
+ }
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_thieves_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool_ quest_thieves_feeling_hook(char *fmt)
+{
+ if (p_ptr->inside_quest != QUEST_THIEVES) return FALSE;
+
+ msg_print("You wake up in a prison cell.");
+ msg_print("All your possessions have been stolen!");
+
+ del_hook(HOOK_FEELING, quest_thieves_feeling_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool_ quest_thieves_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_END_TURN, quest_thieves_hook, "thieves_end_turn");
+ add_hook(HOOK_QUEST_FINISH, quest_thieves_finish_hook, "thieves_finish");
+ add_hook(HOOK_GEN_QUEST, quest_thieves_gen_hook, "thieves_geb");
+ add_hook(HOOK_FEELING, quest_thieves_feeling_hook, "thieves_feel");
+ }
+ return (FALSE);
+}
diff --git a/src/q_thrain.c b/src/q_thrain.c
new file mode 100644
index 00000000..b2b1be9f
--- /dev/null
+++ b/src/q_thrain.c
@@ -0,0 +1,230 @@
+#undef cquest
+#define cquest (quest[QUEST_THRAIN])
+
+bool_ quest_thrain_death_hook(char *fmt)
+{
+ s32b m_idx;
+ int r, x, y;
+ monster_type *m_ptr;
+
+ m_idx = get_next_arg(fmt);
+
+ if ((cquest.status >= QUEST_STATUS_FINISHED) || (dun_level !=cquest.data[0]) || (dungeon_type != DUNGEON_DOL_GULDUR)) return (FALSE);
+ m_ptr = &m_list[m_idx];
+ if ((m_ptr->r_idx != test_monster_name("Dwar, Dog Lord of Waw")) && (m_ptr->r_idx != test_monster_name("Hoarmurath of Dir"))) return (FALSE);
+
+ cquest.data[2]++;
+
+ if (cquest.data[2] < 2) return (FALSE);
+
+ cmsg_print(TERM_YELLOW, "The magic hiding the room dissipates.");
+ for (x = 0; x < cur_wid; x++)
+ for (y = 0; y < cur_hgt; y++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ if (c_ptr->mimic != 61) continue;
+ if (!(c_ptr->info & CAVE_FREE)) continue;
+
+ c_ptr->mimic = 0;
+ lite_spot(y, x);
+ }
+
+ cquest.status = QUEST_STATUS_FINISHED;
+ cmsg_print(TERM_YELLOW, "Thrain speaks:");
+ cmsg_print(TERM_YELLOW, "'Ah, at last you came to me! But... I fear it is too late for me.");
+ cmsg_print(TERM_YELLOW, "However your quest continues, you must beware for the Necromancer");
+ cmsg_print(TERM_YELLOW, "is in fact Sauron, the Dark Lord! He stole the Ring of Durin and tortured");
+ cmsg_print(TERM_YELLOW, "me... arrgh... please make him pay!'");
+
+ /* Look for Thrain */
+ for (r = m_max - 1; r >= 1; r--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[r];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Is it the princess? */
+ if (m_ptr->r_idx == test_monster_name("Thrain, the King Under the Mountain"))
+ {
+ int x = m_ptr->fx;
+ int y = m_ptr->fy;
+ int i, j;
+ object_type forge, *q_ptr;
+
+ delete_monster_idx(r);
+
+ /* Wipe the glass walls and create a stair */
+ for (i = x - 1; i <= x + 1; i++)
+ for (j = y - 1; j <= y + 1; j++)
+ {
+ if (in_bounds(j, i)) cave_set_feat(j, i, FEAT_FLOOR);
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+ object_prep(q_ptr, lookup_kind(TV_HELM, SV_DRAGON_HELM));
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_REWARD;
+ create_artifact(q_ptr, FALSE, TRUE);
+ q_ptr->art_name = quark_add("of Thrain");
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ break;
+ }
+ }
+
+
+ del_hook(HOOK_MONSTER_DEATH, quest_thrain_death_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+}
+
+bool_ quest_thrain_gen_hook(char *fmt)
+{
+ s32b x, y, bx0, by0;
+ int xstart;
+ int ystart;
+ int y2, x2, yval, xval;
+ int y1, x1, xsize, ysize;
+
+ if (dungeon_type != DUNGEON_DOL_GULDUR) return (FALSE);
+ if (cquest.data[0] != dun_level) return (FALSE);
+ if (cquest.data[1]) return (FALSE);
+ if ((cquest.status < QUEST_STATUS_TAKEN) || (cquest.status >= QUEST_STATUS_FINISHED)) return (FALSE);
+
+ by0 = get_next_arg(fmt);
+ bx0 = get_next_arg(fmt);
+
+ /* Pick a room size */
+ get_map_size("thrain.map", &ysize, &xsize);
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return FALSE;
+
+ /* Get corner values */
+ y1 = yval - ysize / 2;
+ x1 = xval - xsize / 2;
+ y2 = y1 + ysize - 1;
+ x2 = x1 + xsize - 1;
+
+ /* Place a full floor under the room */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+ cave[y][x].info |= (CAVE_ROOM|CAVE_GLOW);
+ }
+ }
+
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM | CAVE_GLOW);
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ xstart = x1;
+ ystart = y1;
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("thrain.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = x1; x < xstart; x++)
+ for (y = y1; y < ystart; y++)
+ {
+ cave[y][x].info |= CAVE_ICKY | CAVE_ROOM | CAVE_FREE;
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ int i;
+
+ m_allow_special[test_monster_name("Thrain, the King Under the Mountain")] = TRUE;
+ i = place_monster_one(y, x, test_monster_name("Thrain, the King Under the Mountain"), 0, FALSE, MSTATUS_NEUTRAL);
+ if (i) m_list[i].mflag |= MFLAG_QUEST;
+ m_allow_special[test_monster_name("Thrain, the King Under the Mountain")] = FALSE;
+ }
+ }
+
+ /* Don't try another one for this generation */
+ cquest.data[1] = 1;
+
+ return (TRUE);
+}
+bool_ quest_thrain_feeling_hook(char *fmt)
+{
+ if (dungeon_type != DUNGEON_DOL_GULDUR) return (FALSE);
+ if (cquest.data[0] != dun_level) return (FALSE);
+ if (cquest.status != QUEST_STATUS_UNTAKEN) return (FALSE);
+
+ cmsg_format(TERM_YELLOW, "You hear someone shouting under the torture.");
+ cquest.status = QUEST_STATUS_TAKEN;
+ cquest.init(QUEST_THRAIN);
+
+ return (FALSE);
+}
+bool_ quest_thrain_move_hook(char *fmt)
+{
+ s32b y;
+ s32b x;
+ cave_type *c_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (dungeon_type != DUNGEON_DOL_GULDUR) return (FALSE);
+ if (cquest.data[0] != dun_level) return (FALSE);
+ if ((cquest.status < QUEST_STATUS_TAKEN) || (cquest.status >= QUEST_STATUS_FINISHED)) return (FALSE);
+ if (!(c_ptr->info & CAVE_FREE)) return (FALSE);
+ if (c_ptr->mimic != 61) return (FALSE);
+
+ cmsg_print(TERM_YELLOW, "The magic hiding the room dissipates.");
+ for (x = 0; x < cur_wid; x++)
+ for (y = 0; y < cur_hgt; y++)
+ {
+ c_ptr = &cave[y][x];
+
+ if (c_ptr->mimic != 61) continue;
+ if (!(c_ptr->info & CAVE_FREE)) continue;
+
+ c_ptr->mimic = 0;
+ lite_spot(y, x);
+ }
+
+ return (FALSE);
+}
+bool_ quest_thrain_turn_hook(char *fmt)
+{
+ cquest.data[1] = 0;
+ cquest.data[2] = 0;
+ return (FALSE);
+}
+bool_ quest_thrain_init_hook(int q)
+{
+ if (!cquest.data[0])
+ {
+ cquest.data[0] = rand_range(d_info[DUNGEON_DOL_GULDUR].mindepth + 1, d_info[DUNGEON_DOL_GULDUR].maxdepth - 1);
+ if (wizard) message_add(MESSAGE_MSG, format("Thrain lvl %d", cquest.data[0]), TERM_BLUE);
+ }
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MOVE, quest_thrain_move_hook, "thrain_move");
+ }
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_LEVEL_REGEN, quest_thrain_turn_hook, "thrain_regen_lvl");
+ add_hook(HOOK_NEW_LEVEL, quest_thrain_turn_hook, "thrain_new_lvl");
+ add_hook(HOOK_BUILD_ROOM1, quest_thrain_gen_hook, "thrain_gen");
+ add_hook(HOOK_FEELING, quest_thrain_feeling_hook, "thrain_feel");
+ add_hook(HOOK_MONSTER_DEATH, quest_thrain_death_hook, "thrain_death");
+ }
+ return (FALSE);
+}
diff --git a/src/q_troll.c b/src/q_troll.c
new file mode 100644
index 00000000..c314d2a7
--- /dev/null
+++ b/src/q_troll.c
@@ -0,0 +1,178 @@
+#undef cquest
+#define cquest (quest[QUEST_TROLL])
+
+bool_ quest_troll_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_TROLL) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("trolls.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ int m_idx;
+
+ m_allow_special[test_monster_name("Tom the Stone Troll")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Tom the Stone Troll"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Tom the Stone Troll")] = FALSE;
+
+ if (m_idx)
+ {
+ int o_idx;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ m_list[m_idx].mflag |= MFLAG_QUEST;
+
+ a_allow_special[ART_GLAMDRING] = TRUE;
+
+ /* Mega-Hack -- Prepare to make "Glamdring" */
+ object_prep(q_ptr, lookup_kind(TV_SWORD, SV_BROAD_SWORD));
+
+ /* Mega-Hack -- Mark this item as "Glamdring" */
+ q_ptr->name1 = ART_GLAMDRING;
+
+ /* Mega-Hack -- Actually create "Glamdring" */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ a_allow_special[ART_GLAMDRING] = FALSE;
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ if (o_idx)
+ {
+ /* Get the item */
+ object_type *o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_list[m_idx].hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idx = o_idx;
+ }
+ else
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ }
+ }
+ }
+
+ /* Reinitialize the ambush ... hehehe */
+ cquest.data[0] = FALSE;
+ return TRUE;
+}
+bool_ quest_troll_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_TROLL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "I heard about your noble deeds.", 8, 0);
+ c_put_str(TERM_YELLOW, "Keep what you found... may it serve you well.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NAZGUL;
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_troll_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_troll_death_hook(char *fmt)
+{
+ int x, y, xstart = 2, ystart = 2;
+ s32b r_idx, m_idx;
+ ;
+
+ m_idx = get_next_arg(fmt);
+
+ r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_TROLL) return FALSE;
+
+ if (r_idx == test_monster_name("Tom the Stone Troll"))
+ {
+ cave_set_feat(3, 3, FEAT_LESS);
+ cave[3][3].special = 0;
+
+ cmsg_print(TERM_YELLOW, "Without Tom, the trolls won't be able to do much.");
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_troll_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ init_flags = INIT_GET_SIZE;
+ process_dungeon_file("trolls.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ if (cquest.data[0]) return FALSE;
+
+ cquest.data[0] = TRUE;
+
+ msg_print("Oops, seems like an ambush...");
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Ahah ! */
+ if (c_ptr->info & CAVE_SPEC)
+ {
+ int r_idx;
+
+ cave_set_feat(y, x, FEAT_GRASS);
+ c_ptr->info &= ~CAVE_SPEC;
+
+ r_idx = (rand_int(2) == 0) ? test_monster_name("Forest troll") : test_monster_name("Stone troll");
+ place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY);
+ }
+ }
+
+ return FALSE;
+}
+bool_ quest_troll_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_troll_death_hook, "troll_death");
+ add_hook(HOOK_GEN_QUEST, quest_troll_gen_hook, "troll_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_troll_finish_hook, "troll_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_ultrae.c b/src/q_ultrae.c
new file mode 100644
index 00000000..78471df5
--- /dev/null
+++ b/src/q_ultrae.c
@@ -0,0 +1,11 @@
+/*
+ * Here takes place the Evil ultra ending
+ */
+
+#undef cquest
+#define cquest (quest[QUEST_ULTRA_EVIL])
+
+bool_ quest_ultra_evil_init_hook(int q)
+{
+ return (FALSE);
+}
diff --git a/src/q_ultrag.c b/src/q_ultrag.c
new file mode 100644
index 00000000..a5a09f2d
--- /dev/null
+++ b/src/q_ultrag.c
@@ -0,0 +1,276 @@
+/*
+ * Here takes place the Good ultra ending
+ */
+
+#undef cquest
+#define cquest (quest[QUEST_ULTRA_GOOD])
+
+bool_ quest_ultra_good_move_hook(char *fmt)
+{
+ s32b y, x;
+ cave_type *c_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ bool_ old_quick_messages = quick_messages;
+
+ if (quest[QUEST_MORGOTH].status < QUEST_STATUS_FINISHED) return (FALSE);
+
+ /* The mirror of Galadriel */
+ if ((c_ptr->feat != FEAT_SHOP) || (c_ptr->special != 23)) return (FALSE);
+
+ quick_messages = FALSE;
+ cmsg_print(TERM_L_BLUE, "You meet Galadriel.");
+ cmsg_print(TERM_YELLOW, "'I still cannot believe this is all over.'");
+ cmsg_print(TERM_YELLOW, "'Morgoth's reign of terror is over at last!'");
+ cmsg_print(TERM_YELLOW, "'His spirit has been banished to the Void where he cannot do much harm.'");
+ cmsg_print(TERM_YELLOW, "'We can never thank you enough, hero!'");
+ cmsg_print(TERM_L_BLUE, "Although everything seems all right, Galadriel seems a little subdued.");
+ cmsg_print(TERM_YELLOW, "'The spirit of Morgoth is not destroyed however -- only banished.'");
+ cmsg_print(TERM_YELLOW, "'He can still control his allies left on Arda.'");
+ cmsg_print(TERM_YELLOW, "'Maybe... maybe there could be a way to remove the threat of evil forever.'");
+ cmsg_print(TERM_YELLOW, "'Somebody would have to go into the Void to do it.'");
+ cmsg_print(TERM_YELLOW, "'But going there is certain death; we cannot ask it of anyone.'");
+ cmsg_print(TERM_YELLOW, "'But you may choose, of your own free will, to attempt it...'");
+ cmsg_print(TERM_L_BLUE, "Galadriel plainly presents the choice that now lies before you:");
+
+ cmsg_print(TERM_YELLOW, "'You have earned the right to make whatever you wish of your future.'");
+ cmsg_print(TERM_YELLOW, "'Become a ruler of Arda if you so desire; reign long, enjoying'");
+ cmsg_print(TERM_YELLOW, "'the adulation of all, and have a happy life. Or, you can turn your'");
+ cmsg_print(TERM_YELLOW, "'back on safety. Enter the Void, alone, to fight a hopeless battle'");
+ cmsg_print(TERM_YELLOW, "'and face certain death.'");
+
+ /* This is SO important a question that flush pending inputs */
+ flush();
+
+ if (!get_check("Will you stay on Arda and lead a happy life?"))
+ {
+ cmsg_print(TERM_YELLOW, "'So be it. I will open a portal to the Void.'");
+ cmsg_print(TERM_YELLOW, "'But you must know this: the portal can only lead one way.'");
+ cmsg_print(TERM_YELLOW, "'It will close once you enter, so as not to permit the horrors'");
+ cmsg_print(TERM_YELLOW, "'that lurk in the Void to enter Arda. Your only way to come back'");
+ cmsg_print(TERM_YELLOW, "'is to defeat the spirit of Morgoth, known as Melkor.'");
+ cmsg_print(TERM_YELLOW, "'You will not be able to recall back either.'");
+ cmsg_print(TERM_YELLOW, "'You can still choose to retire; it is not too late'");
+ cmsg_print(TERM_YELLOW, "'to save your life.'");
+ cmsg_print(TERM_YELLOW, "'One last thing: It is quite certain that Melkor will have erected'");
+ cmsg_print(TERM_YELLOW, "'powerful magical barriers around him. You certainly will'");
+ cmsg_print(TERM_YELLOW, "'need to find a way to break them to get to him.'");
+
+ /* Create the entrance */
+ cave_set_feat(y - 5, x, FEAT_MORE);
+ cave[y - 5][x].special = 11;
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_TAKEN;
+ cquest.init(QUEST_ULTRA_GOOD);
+ }
+ quick_messages = old_quick_messages;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+bool_ quest_ultra_good_stair_hook(char *fmt)
+{
+ cptr dir;
+
+ dir = get_next_arg_str(fmt);
+
+ if (dungeon_type != DUNGEON_VOID)
+ return FALSE;
+
+ /* Cant leave */
+ if ((!strcmp(dir, "up")) && (dun_level == 128))
+ {
+ cmsg_print(TERM_YELLOW, "The portal to Arda is now closed.");
+ return TRUE;
+ }
+ /* there is no coming back */
+ if ((!strcmp(dir, "up")) && (dun_level == 150))
+ {
+ cmsg_print(TERM_YELLOW, "The barrier seems to be impenetrable from this side.");
+ cmsg_print(TERM_YELLOW, "You will have to move on.");
+ return TRUE;
+ }
+ /* Cant enter without the flame imperishable */
+ if ((!strcmp(dir, "down")) && (dun_level == 149))
+ {
+ int i;
+ bool_ ultimate = FALSE;
+
+ /* Now look for an ULTIMATE artifact, that is, one imbued with the flame */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_type *o_ptr = get_object(i);
+
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the gloves */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_ULTIMATE)
+ {
+ ultimate = TRUE;
+ break;
+ }
+ }
+
+ if (!ultimate)
+ {
+ cmsg_print(TERM_YELLOW, "It seems the level is protected by an impassable barrier of pure magic.");
+ cmsg_print(TERM_YELLOW, "Only the most powerful magic could remove it. You will need to use");
+ cmsg_print(TERM_YELLOW, "the Flame Imperishable to pass. The source of Eru Iluvatar's own power.");
+ return TRUE;
+ }
+ else
+ {
+ cmsg_print(TERM_YELLOW, "The power of the Flame Imperishable shatters the magical barrier.");
+ cmsg_print(TERM_YELLOW, "The way before you is free.");
+ }
+ }
+
+ return FALSE;
+}
+
+bool_ quest_ultra_good_recall_hook(char *fmt)
+{
+ if ((dungeon_type != DUNGEON_VOID) && (dungeon_type != DUNGEON_NETHER_REALM))
+ return FALSE;
+
+ cmsg_print(TERM_YELLOW, "You cannot recall. The portal to Arda is closed.");
+ return TRUE;
+}
+
+bool_ quest_ultra_good_death_hook(char *fmt)
+{
+ s32b m_idx = get_next_arg(fmt);
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Melkor is dead! */
+ if (m_ptr->r_idx == 1044)
+ {
+ /* Total winner */
+ total_winner = WINNER_ULTRA;
+ has_won = WINNER_ULTRA;
+ quest[QUEST_ULTRA_GOOD].status = QUEST_STATUS_FINISHED;
+
+ /* Redraw the "title" */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Congratulations */
+ cmsg_print(TERM_L_GREEN, "****** CONGRATULATIONS ******");
+ cmsg_print(TERM_L_GREEN, "You have done more than the impossible. You ended the threat of");
+ cmsg_print(TERM_L_GREEN, "Melkor forever. Thanks to you, Arda will live in eternal peace.");
+ cmsg_print(TERM_L_GREEN, "You feel the spirit of Eru touching you. You feel your spirit rising!");
+ cmsg_print(TERM_L_GREEN, "Before you, a portal to Arda opens. You can now come back to your world");
+ cmsg_print(TERM_L_GREEN, "and live happily ever after.");
+ cmsg_print(TERM_L_GREEN, "What you do now is up to you, but your deeds shall ever be remembered.");
+ cmsg_print(TERM_L_GREEN, "You may retire (commit suicide) when you are ready.");
+
+ /* Create the entrance */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MORE);
+
+ /* Remove now used hook */
+ del_hook(HOOK_MONSTER_DEATH, quest_ultra_good_death_hook);
+ process_hooks_restart = TRUE;
+
+ /* End plot */
+ *(quest[QUEST_ULTRA_GOOD].plot) = QUEST_NULL;
+ }
+
+ /* Tik'svvrzllat is dead! */
+ if (m_ptr->r_idx == 1032)
+ {
+ int i;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ /* Mega-Hack -- Prepare to make the Flame Imperishable */
+ object_prep(q_ptr, lookup_kind(TV_JUNK, 255));
+
+ /* Mega-Hack -- Actually create the Flame Imperishable */
+ k_allow_special[296] = TRUE;
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+ k_allow_special[296] = FALSE;
+
+ /* Identify it fully */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+
+ /* Find a space */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ /* Skip non-objects */
+ if (!p_ptr->inventory[i].k_idx) break;
+ }
+ /* Arg, no space ! */
+ if (i == INVEN_PACK)
+ {
+ char o_name[200];
+
+ object_desc(o_name, &p_ptr->inventory[INVEN_PACK - 1], FALSE, 0);
+
+ /* Drop the item */
+ inven_drop(INVEN_PACK - 1, 99, p_ptr->py, p_ptr->px, FALSE);
+
+ cmsg_format(TERM_VIOLET, "You feel the urge to drop your %s to make room in your inventory.", o_name);
+ }
+
+ /* Carry it */
+ cmsg_format(TERM_VIOLET, "You feel the urge to pick up the Flame Imperishable.");
+ inven_carry(q_ptr, FALSE);
+ }
+ return (FALSE);
+}
+bool_ quest_ultra_good_dump_hook(char *fmt)
+{
+ if (quest[QUEST_ULTRA_GOOD].status >= QUEST_STATUS_TAKEN)
+ {
+ /* Ultra winner ! */
+ if (total_winner == WINNER_ULTRA)
+ {
+ fprintf(hook_file, "\n You destroyed Melkor forever and have been elevated to the status of Vala by Eru Iluvatar.");
+ fprintf(hook_file, "\n Arda will forever be free.");
+ }
+ else
+ {
+ /* Tried and failed */
+ if (death)
+ {
+ fprintf(hook_file, "\n You tried to destroy Melkor forever, but died in the attempt.");
+ fprintf(hook_file, "\n Arda will be quiet, but not free from evil.");
+ }
+ }
+ }
+ return (FALSE);
+}
+
+
+bool_ quest_ultra_good_init_hook(int q)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_STAIR, quest_ultra_good_stair_hook, "ultrag_stair");
+ add_hook(HOOK_RECALL, quest_ultra_good_recall_hook, "ultrag_recall");
+ add_hook(HOOK_MONSTER_DEATH, quest_ultra_good_death_hook, "ultrag_death");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook(HOOK_MOVE, quest_ultra_good_move_hook, "ultrag_move");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_ultra_good_dump_hook, "ultrag_dump");
+ return (FALSE);
+}
diff --git a/src/q_wight.c b/src/q_wight.c
new file mode 100644
index 00000000..3f6b2c34
--- /dev/null
+++ b/src/q_wight.c
@@ -0,0 +1,156 @@
+#undef cquest
+#define cquest (quest[QUEST_WIGHT])
+
+bool_ quest_wight_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_WIGHT) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("wights.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ int m_idx = 0;
+
+ m_allow_special[test_monster_name("The Wight-King of the Barrow-downs")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("The Wight-King of the Barrow-downs"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("The Wight-King of the Barrow-downs")] = FALSE;
+
+ if (m_idx)
+ {
+ int o_idx;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ m_list[m_idx].mflag |= MFLAG_QUEST;
+
+ /* Prepare to make the */
+ object_prep(q_ptr, lookup_kind(TV_SOFT_ARMOR, SV_FILTHY_RAG));
+
+ /* Name the rags */
+
+ q_ptr->art_name = quark_add("of the Wight");
+
+ q_ptr->art_flags1 |= ( TR1_INT | TR1_SEARCH );
+ q_ptr->art_flags2 |= ( TR2_RES_BLIND | TR2_SENS_FIRE | TR2_RES_CONF );
+ q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD | TR3_SEE_INVIS);
+
+ /* For game balance... */
+ q_ptr->art_flags3 |= (TR3_CURSED | TR3_HEAVY_CURSE);
+ q_ptr->ident |= IDENT_CURSED;
+
+ if (randint(2) == 1)
+ {
+ q_ptr->art_flags1 |= (TR1_SPELL);
+ q_ptr->pval = 6;
+ }
+ else
+ {
+ q_ptr->art_flags1 |= (TR1_MANA);
+ q_ptr->pval = 2;
+ }
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ if (o_idx)
+ {
+ /* Get the item */
+ object_type *o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_list[m_idx].hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idx = o_idx;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+bool_ quest_wight_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_WIGHT) return FALSE;
+
+ if (r_idx == test_monster_name("The Wight-King of the Barrow-downs"))
+ {
+ cmsg_print(TERM_YELLOW, "Without their King the wights won't be able to do much.");
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ cave[p_ptr->py][p_ptr->px].special = 0;
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_wight_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return (FALSE);
+}
+bool_ quest_wight_finish_hook(char *fmt)
+{
+ s32b q_idx;
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_WIGHT) return FALSE;
+
+ c_put_str(TERM_YELLOW, "I heard about your noble deeds.", 8, 0);
+ c_put_str(TERM_YELLOW, "Keep what you found .. may it serve you well.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NAZGUL;
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_wight_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool_ quest_wight_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_wight_death_hook, "wight_death");
+ add_hook(HOOK_GEN_QUEST, quest_wight_gen_hook, "wight_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_wight_finish_hook, "wight_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_wolves.c b/src/q_wolves.c
new file mode 100644
index 00000000..2ec14cc2
--- /dev/null
+++ b/src/q_wolves.c
@@ -0,0 +1,130 @@
+#undef cquest
+#define cquest (quest[QUEST_WOLVES])
+
+bool_ quest_wolves_gen_hook(char *fmt)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_WOLVES) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("wolves.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some random wolves */
+ for (i = damroll(4, 4); i > 0; )
+ {
+ int m_idx, flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ m_idx = place_monster_one(y, x, 196, 0, magik(50), MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ /* Place some random wargs */
+ for (i = damroll(4, 4); i > 0; )
+ {
+ int m_idx, flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ m_idx = place_monster_one(y, x, 257, 0, magik(50), MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool_ quest_wolves_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_WOLVES) return FALSE;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (mcnt <= 1)
+ {
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_wolves_death_hook);
+ del_hook(HOOK_GEN_QUEST, quest_wolves_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Lothlorien is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+bool_ quest_wolves_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_WOLVES) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for killing the pack of wolves!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the hut as your house as a reward.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_SPIDER;
+
+ return TRUE;
+}
+
+bool_ quest_wolves_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_wolves_death_hook, "wolves_monster_death");
+ add_hook(HOOK_QUEST_FINISH, quest_wolves_finish_hook, "wolves_finish");
+ add_hook(HOOK_GEN_QUEST, quest_wolves_gen_hook, "wolves_geb");
+ }
+ return (FALSE);
+}
diff --git a/src/quest.pkg b/src/quest.pkg
new file mode 100755
index 00000000..4ba93b7a
--- /dev/null
+++ b/src/quest.pkg
@@ -0,0 +1,170 @@
+/* File: quest.pkg */
+
+/*
+ * Purpose: Lua interface definitions for quests.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @name Quest Status Flags
+ * @brief Quest status
+ * @{ */
+
+/** @def QUEST_STATUS_IGNORED */
+#define QUEST_STATUS_IGNORED -1
+
+/** @def QUEST_STATUS_UNTAKEN */
+#define QUEST_STATUS_UNTAKEN 0
+
+/** @def QUEST_STATUS_TAKEN */
+#define QUEST_STATUS_TAKEN 1
+
+/** @def QUEST_STATUS_COMPLETED */
+#define QUEST_STATUS_COMPLETED 2
+
+/** @def QUEST_STATUS_REWARDED */
+#define QUEST_STATUS_REWARDED 3
+
+/** @def QUEST_STATUS_FAILED */
+#define QUEST_STATUS_FAILED 4
+
+/** @def QUEST_STATUS_FINISHED */
+#define QUEST_STATUS_FINISHED 5
+
+/** @def QUEST_STATUS_FAILED_DONE */
+#define QUEST_STATUS_FAILED_DONE 6
+/** @} */
+
+/** @struct quest_type
+ * @brief Quest
+ */
+struct quest_type
+{
+ /** @structvar silent
+ * @brief Boolean
+ * @note Does quest appear on quest list?
+ */
+ bool silent;
+
+ /** @structvar dynamic_desc
+ * @brief Boolean
+ * @note Do we need to ask a function to get the description ?
+ */
+ bool dynamic_desc;
+
+ /** @structvar status
+ * @brief Number
+ * @note Is the quest taken, completed, finished?
+ */
+ s16b status;
+
+ /** @structvar level
+ * @brief Number
+ * @note Dungeon level
+ */
+ s16b level;
+
+ /** @structvar type
+ * @brief Number
+ * @note Lua or C ?
+ */
+ byte type;
+};
+
+/** @var max_q_idx
+ * @brief Number
+ * @note Maximum number of quests in quest list
+ */
+extern s16b max_q_idx;
+
+/** @var quest_aux;
+ * @brief quest_type
+ * @note Array of quests
+ */
+extern quest_type quest[max_q_idx] @ quest_aux;
+
+$static quest_type *lua_get_quest(int q_idx){return &quest[q_idx];}
+
+/** @fn quest(int q_idx);
+ * @brief Return quest with index "q_idx" from quest array.\n
+ * @param q_idx Number \n the index of a quest in the quest array.
+ * @brief Quest index
+ * @return quest_type \n The quest at index "q_idx".
+ * @note (see file w_quest.c)
+ */
+static quest_type *lua_get_quest @ quest(int q_idx);
+
+/** @fn new_quest(char *name);
+ * @dgonly
+ * @brief Add a new quest to the end of the quest array.\n
+ * @param *name String \n the name of the new quest.
+ * @brief Quest name
+ * @return Number \n The index of the new quest in the quest array.
+ * @note (see file lua_bind.c)
+ */
+extern s16b add_new_quest @ new_quest(char *name);
+
+/** @fn quest_desc(int q_idx, int d, char *desc);
+ * @dgonly
+ * @brief Return the description of a quest.\n
+ * @param q_idx Number \n the index of a quest in the quest array.
+ * @brief Quest index
+ * @param d Number \n the index of a line in the quest description.
+ * @brief Description line
+ * @param *desc String
+ * @brief Description
+ * @return *desc String \n Line "d" of the description of quest with index
+ * "q_idx" in the quest array.
+ * @note (see file lua_bind.c)
+ */
+extern void desc_quest @ quest_desc(int q_idx, int d, char *desc);
+
+/** @fn get_new_bounty_monster(int lev);
+ * @brief Find a good random bounty monster.\n
+ * @param lev Number \n the level of the bounty monster.
+ * @brief Monster level
+ * @return Number \n The index of the monster in the r_info array.
+ * @note (see file lua_bind.c)
+ */
+extern int lua_get_new_bounty_monster@get_new_bounty_monster(int lev);
diff --git a/src/randart.c b/src/randart.c
new file mode 100644
index 00000000..298ee83a
--- /dev/null
+++ b/src/randart.c
@@ -0,0 +1,476 @@
+/* File: randart.c */
+
+/* Purpose: Randart creation code */
+
+/*
+ * Copyright (c) 2001 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/* Chance of using syllables to form the name instead of the "template" files */
+#define TABLE_NAME 45
+#define A_CURSED 13
+#define WEIRD_LUCK 12
+#define ACTIVATION_CHANCE 3
+
+/*
+ * Attempt to add a power to a randart
+ */
+static bool_ grab_one_power(int *ra_idx, object_type *o_ptr, bool_ good, s16b *max_times)
+{
+ int i = 0, j;
+ int *ok_ra, ok_num = 0;
+ bool_ ret = FALSE;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ C_MAKE(ok_ra, max_ra_idx, int);
+
+ /* Grab the ok randart */
+ for (i = 0; i < max_ra_idx; i++)
+ {
+ randart_part_type *ra_ptr = &ra_info[i];
+ bool_ ok = FALSE;
+
+ /* Must have the correct fields */
+ for (j = 0; j < 20; j++)
+ {
+ if (ra_ptr->tval[j] == o_ptr->tval)
+ {
+ if ((ra_ptr->min_sval[j] <= o_ptr->sval) && (ra_ptr->max_sval[j] >= o_ptr->sval)) ok = TRUE;
+ }
+
+ if (ok) break;
+ }
+ if ((0 < ra_ptr->max_pval) && (ra_ptr->max_pval < o_ptr->pval)) ok = FALSE;
+ if (!ok)
+ {
+ /* Doesnt count as a try*/
+ continue;
+ }
+
+ /* Good should be good, bad should be bad */
+ if (good && (ra_ptr->value <= 0)) continue;
+ if ((!good) && (ra_ptr->value > 0)) continue;
+
+ if (max_times[i] >= ra_ptr->max) continue;
+
+ /* Must NOT have the antagonic flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f1 & ra_ptr->aflags1) continue;
+ if (f2 & ra_ptr->aflags2) continue;
+ if (f3 & ra_ptr->aflags3) continue;
+ if (f4 & ra_ptr->aflags4) continue;
+ if (f5 & ra_ptr->aflags5) continue;
+ if (esp & ra_ptr->aesp) continue;
+
+ /* ok */
+ ok_ra[ok_num++] = i;
+ }
+
+ /* Now test them a few times */
+ for (i = 0; i < ok_num * 10; i++)
+ {
+ randart_part_type *ra_ptr;
+
+ i = ok_ra[rand_int(ok_num)];
+ ra_ptr = &ra_info[i];
+
+ /* XXX XXX Enforce minimum player level (loosely) */
+ if (ra_ptr->level > p_ptr->lev)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (ra_ptr->level - p_ptr->lev);
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0)
+ {
+ continue;
+ }
+ }
+
+ /* We must make the "rarity roll" */
+ if (rand_int(ra_ptr->mrarity) < ra_ptr->rarity)
+ {
+ continue;
+ }
+
+ /* Hack -- mark the item as an ego */
+ *ra_idx = i;
+ max_times[i]++;
+
+ /* Success */
+ ret = TRUE;
+ break;
+ }
+
+ C_FREE(ok_ra, max_ra_idx, int);
+
+ /* Return */
+ return (ret);
+}
+
+void give_activation_power (object_type * o_ptr)
+{
+ o_ptr->xtra2 = 0;
+ o_ptr->art_flags3 &= ~TR3_ACTIVATE;
+ o_ptr->timeout = 0;
+}
+
+
+int get_activation_power()
+{
+ object_type *o_ptr, forge;
+
+ o_ptr = &forge;
+
+ give_activation_power(o_ptr);
+
+ return o_ptr->xtra2;
+}
+
+#define MIN_NAME_LEN 5
+#define MAX_NAME_LEN 9
+#define S_WORD 26
+#define E_WORD S_WORD
+
+static long lprobs[S_WORD + 1][S_WORD + 1][S_WORD + 1];
+static long ltotal[S_WORD + 1][S_WORD + 1];
+
+/*
+ * Use W. Sheldon Simms' random name generator. This function builds
+ * probability tables which are used later on for letter selection. It
+ * relies on the ASCII character set.
+ */
+void build_prob(cptr learn)
+{
+ int c_prev, c_cur, c_next;
+
+ /* Build raw frequencies */
+ while (1)
+ {
+ c_prev = c_cur = S_WORD;
+
+ do
+ {
+ c_next = *learn++;
+ }
+ while (!isalpha(c_next) && (c_next != '\0'));
+
+ if (c_next == '\0') break;
+
+ do
+ {
+ c_next = A2I(tolower(c_next));
+ lprobs[c_prev][c_cur][c_next]++;
+ ltotal[c_prev][c_cur]++;
+ c_prev = c_cur;
+ c_cur = c_next;
+ c_next = *learn++;
+ }
+ while (isalpha(c_next));
+
+ lprobs[c_prev][c_cur][E_WORD]++;
+ ltotal[c_prev][c_cur]++;
+ }
+}
+
+
+/*
+ * Use W. Sheldon Simms' random name generator. Generate a random word using
+ * the probability tables we built earlier. Relies on the ASCII character
+ * set. Relies on European vowels (a, e, i, o, u). The generated name should
+ * be copied/used before calling this function again.
+ */
+static char *make_word(void)
+{
+ static char word_buf[90];
+ int r, totalfreq;
+ int tries, lnum, vow;
+ int c_prev, c_cur, c_next;
+ char *cp;
+
+startover:
+ vow = 0;
+ lnum = 0;
+ tries = 0;
+ cp = word_buf;
+ c_prev = c_cur = S_WORD;
+
+ while (1)
+ {
+getletter:
+ c_next = 0;
+ r = rand_int(ltotal[c_prev][c_cur]);
+ totalfreq = lprobs[c_prev][c_cur][c_next];
+
+ while (totalfreq <= r)
+ {
+ c_next++;
+ totalfreq += lprobs[c_prev][c_cur][c_next];
+ }
+
+ if (c_next == E_WORD)
+ {
+ if ((lnum < MIN_NAME_LEN) || vow == 0)
+ {
+ tries++;
+ if (tries < 10) goto getletter;
+ goto startover;
+ }
+ *cp = '\0';
+ break;
+ }
+
+ if (lnum >= MAX_NAME_LEN) goto startover;
+
+ *cp = I2A(c_next);
+
+ if (is_a_vowel(*cp)) vow++;
+
+ cp++;
+ lnum++;
+ c_prev = c_cur;
+ c_cur = c_next;
+ }
+
+ word_buf[0] = toupper(word_buf[0]);
+
+ return (word_buf);
+}
+
+
+void get_random_name(char * return_name)
+{
+ char *word = make_word();
+
+ if (rand_int(3) == 0)
+ sprintf(return_name, "'%s'", word);
+ else
+ sprintf(return_name, "of %s", word);
+}
+
+
+bool_ create_artifact(object_type *o_ptr, bool_ a_scroll, bool_ get_name)
+{
+ char new_name[80];
+ int powers = 0, i;
+ s32b total_flags, total_power = 0;
+ bool_ a_cursed = FALSE;
+ u32b f1, f2, f3, f4, f5, esp;
+ s16b *max_times;
+ s16b pval = 0;
+ bool_ limit_blows = FALSE;
+
+ strcpy(new_name, "");
+
+ if ((!a_scroll) && (randint(A_CURSED) == 1)) a_cursed = TRUE;
+
+ i = 0;
+ while (ra_gen[i].chance)
+ {
+ powers += damroll(ra_gen[i].dd, ra_gen[i].ds) + ra_gen[i].plus;
+ i++;
+ }
+
+ if ((!a_cursed) && (randint(30) == 1)) powers *= 2;
+
+ if (a_cursed) powers /= 2;
+
+ C_MAKE(max_times, max_ra_idx, s16b);
+
+ /* Main loop */
+ while (powers)
+ {
+ int ra_idx;
+ randart_part_type *ra_ptr;
+
+ powers--;
+
+ if (!grab_one_power(&ra_idx, o_ptr, TRUE, max_times)) continue;
+
+ ra_ptr = &ra_info[ra_idx];
+
+ if (wizard) msg_format("Adding randart power: %d", ra_idx);
+
+ total_power += ra_ptr->value;
+
+ o_ptr->art_flags1 |= ra_ptr->flags1;
+ o_ptr->art_flags2 |= ra_ptr->flags2;
+ o_ptr->art_flags3 |= ra_ptr->flags3;
+ o_ptr->art_flags4 |= ra_ptr->flags4;
+ o_ptr->art_flags5 |= ra_ptr->flags5;
+ o_ptr->art_esp |= ra_ptr->esp;
+
+ add_random_ego_flag(o_ptr, ra_ptr->fego, &limit_blows);
+
+ /* get flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- acquire "cursed" flag */
+ if (f3 & TR3_CURSED) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Hack -- obtain bonuses */
+ if (ra_ptr->max_to_h > 0) o_ptr->to_h += randint(ra_ptr->max_to_h);
+ if (ra_ptr->max_to_h < 0) o_ptr->to_h -= randint( -ra_ptr->max_to_h);
+ if (ra_ptr->max_to_d > 0) o_ptr->to_d += randint(ra_ptr->max_to_d);
+ if (ra_ptr->max_to_d < 0) o_ptr->to_d -= randint( -ra_ptr->max_to_d);
+ if (ra_ptr->max_to_a > 0) o_ptr->to_a += randint(ra_ptr->max_to_a);
+ if (ra_ptr->max_to_a < 0) o_ptr->to_a -= randint( -ra_ptr->max_to_a);
+
+ /* Hack -- obtain pval */
+ if (((pval > ra_ptr->max_pval) && ra_ptr->max_pval) || (!pval)) pval = ra_ptr->max_pval;
+ };
+ C_FREE(max_times, max_ra_idx, s16b);
+
+ if (pval > 0) o_ptr->pval = randint(pval);
+ if (pval < 0) o_ptr->pval = randint( -pval);
+
+ /* No insane number of blows */
+ if (limit_blows && (o_ptr->art_flags1 & TR1_BLOWS))
+ {
+ if (o_ptr->pval > 2) o_ptr->pval = randint(2);
+ }
+
+ /* Just to be sure */
+ o_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD);
+
+ total_flags = flag_cost(o_ptr, o_ptr->pval);
+ if (cheat_peek) msg_format("%ld", total_flags);
+
+ if (a_cursed) curse_artifact(o_ptr);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (get_name)
+ {
+ if (a_scroll)
+ {
+ char dummy_name[80];
+
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->ident |= (IDENT_STOREB | IDENT_MENTAL);
+
+ strcpy(dummy_name, "");
+ object_out_desc(o_ptr, NULL, FALSE, TRUE);
+
+ if (get_string("What do you want to call the artifact? ", dummy_name, 80))
+ {
+ strcpy(new_name, "called '");
+ strcat(new_name, dummy_name);
+ strcat(new_name, "'");
+ }
+ else
+ /* Default name = of 'player name' */
+ sprintf(new_name, "of '%s'", player_name);
+ }
+ else
+ {
+ get_random_name(new_name);
+ }
+ }
+
+ /* Save the inscription */
+ o_ptr->art_name = quark_add(new_name);
+ o_ptr->name2 = o_ptr->name2b = 0;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* HACKS for ToME */
+ if (o_ptr->tval == TV_CLOAK && o_ptr->sval == SV_MIMIC_CLOAK)
+ {
+ s32b mimic;
+ call_lua("find_random_mimic_shape", "(d,d)", "d", 127, TRUE, &mimic);
+ o_ptr->pval2 = mimic;
+ }
+ else if (f5 & TR5_SPELL_CONTAIN)
+ {
+ o_ptr->pval2 = -1;
+ }
+
+ return TRUE;
+}
+
+
+bool_ artifact_scroll(void)
+{
+ int item;
+ bool_ okay = FALSE;
+ object_type *o_ptr;
+ char o_name[80];
+
+ cptr q, s;
+
+
+ /* Enchant weapon/armour */
+ item_tester_hook = item_tester_hook_artifactable;
+
+ /* Get an item */
+ q = "Enchant which item? ";
+ s = "You have nothing to enchant.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Describe */
+ msg_format("%s %s radiate%s a blinding light!",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+
+ if (artifact_p(o_ptr))
+ {
+ msg_format("The %s %s already %s!",
+ o_name, ((o_ptr->number > 1) ? "are" : "is"),
+ ((o_ptr->number > 1) ? "artifacts" : "an artifact"));
+ okay = FALSE;
+ }
+
+ else if (o_ptr->name2)
+ {
+ msg_format("The %s %s already %s!",
+ o_name, ((o_ptr->number > 1) ? "are" : "is"),
+ ((o_ptr->number > 1) ? "ego items" : "an ego item"));
+ okay = FALSE;
+ }
+
+ else
+ {
+ if (o_ptr->number > 1)
+ {
+ msg_print("Not enough enough energy to enchant more than one object!");
+ msg_format("%d of your %s %s destroyed!", (o_ptr->number) - 1, o_name, (o_ptr->number > 2 ? "were" : "was"));
+ o_ptr->number = 1;
+ }
+ okay = create_artifact(o_ptr, TRUE, TRUE);
+ }
+
+ /* Failure */
+ if (!okay)
+ {
+ /* Flush */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("The enchantment failed.");
+ }
+ else
+ o_ptr->found = OBJ_FOUND_SELFMADE;
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
diff --git a/src/readdib.c b/src/readdib.c
new file mode 100644
index 00000000..294c2702
--- /dev/null
+++ b/src/readdib.c
@@ -0,0 +1,342 @@
+/* File: readbits.c */
+
+/*
+ * This package provides a routine to read a DIB file and set up the
+ * device dependent version of the image.
+ *
+ * This file has been modified for use with "Angband 2.8.2"
+ *
+ * COPYRIGHT:
+ *
+ * (C) Copyright Microsoft Corp. 1993. All rights reserved.
+ *
+ * You have a royalty-free right to use, modify, reproduce and
+ * distribute the Sample Files (and/or any modified version) in
+ * any way you find useful, provided that you agree that
+ * Microsoft has no warranty obligations or liability for any
+ * Sample Application Files which are modified.
+ */
+
+#ifdef WINDOWS
+
+#include <windows.h>
+
+#include "readdib.h"
+
+
+/*
+ * Extract the "WIN32" flag from the compiler
+ */
+#if defined(__WIN32__) || defined(__WINNT__) || defined(__NT__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+
+/*
+ * Make sure "huge" is legal XXX XXX XXX
+ */
+#undef huge
+#ifdef WIN32
+# define huge /* oops */
+#endif
+
+
+/*
+ * Number of bytes to be read during each read operation
+ */
+#define MAXREAD 32768
+
+/*
+ * Private routine to read more than 64K at a time
+ *
+ * Reads data in steps of 32k till all the data has been read.
+ *
+ * Returns number of bytes requested, or zero if something went wrong.
+ */
+static DWORD PASCAL lread(int fh, VOID far *pv, DWORD ul)
+{
+ DWORD ulT = ul;
+ BYTE huge *hp = pv;
+
+ while (ul > (DWORD)MAXREAD)
+ {
+ if (_lread(fh, (LPSTR)hp, (WORD)MAXREAD) != MAXREAD)
+ return 0;
+ ul -= MAXREAD;
+ hp += MAXREAD;
+ }
+ if (_lread(fh, (LPSTR)hp, (WORD)ul) != (WORD)ul)
+ return 0;
+ return ulT;
+}
+
+
+/*
+ * Given a BITMAPINFOHEADER, create a palette based on the color table.
+ *
+ * Returns the handle of a palette, or zero if something went wrong.
+ */
+static HPALETTE PASCAL NEAR MakeDIBPalette(LPBITMAPINFOHEADER lpInfo)
+{
+ NPLOGPALETTE npPal;
+ RGBQUAD far *lpRGB;
+ HPALETTE hLogPal;
+ WORD i;
+
+ /*
+ * since biClrUsed field was filled during the loading of the DIB,
+ * we know it contains the number of colors in the color table.
+ */
+ if (lpInfo->biClrUsed)
+ {
+ npPal = (NPLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) +
+ (WORD)lpInfo->biClrUsed * sizeof(PALETTEENTRY));
+ if (!npPal)
+ return (FALSE);
+
+ npPal->palVersion = 0x300;
+ npPal->palNumEntries = (WORD)lpInfo->biClrUsed;
+
+ /* get pointer to the color table */
+ lpRGB = (RGBQUAD FAR *)((LPSTR)lpInfo + lpInfo->biSize);
+
+ /* copy colors from the color table to the LogPalette structure */
+ for (i = 0; i < lpInfo->biClrUsed; i++, lpRGB++)
+ {
+ npPal->palPalEntry[i].peRed = lpRGB->rgbRed;
+ npPal->palPalEntry[i].peGreen = lpRGB->rgbGreen;
+ npPal->palPalEntry[i].peBlue = lpRGB->rgbBlue;
+ npPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
+ }
+
+ hLogPal = CreatePalette((LPLOGPALETTE)npPal);
+ LocalFree((HANDLE)npPal);
+ return (hLogPal);
+ }
+
+ /*
+ * 24-bit DIB with no color table. return default palette. Another
+ * option would be to create a 256 color "rainbow" palette to provide
+ * some good color choices.
+ */
+ else
+ {
+ return (GetStockObject(DEFAULT_PALETTE));
+ }
+}
+
+
+/*
+ * Given a DIB, create a bitmap and corresponding palette to be used for a
+ * device-dependent representation of the image.
+ *
+ * Returns TRUE on success (phPal and phBitmap are filled with appropriate
+ * handles. Caller is responsible for freeing objects) and FALSE on failure
+ * (unable to create objects, both pointer are invalid).
+ */
+static BOOL NEAR PASCAL MakeBitmapAndPalette(HDC hDC, HANDLE hDIB,
+ HPALETTE * phPal, HBITMAP * phBitmap)
+{
+ LPBITMAPINFOHEADER lpInfo;
+ BOOL result = FALSE;
+ HBITMAP hBitmap;
+ HPALETTE hPalette, hOldPal;
+ LPSTR lpBits;
+
+ lpInfo = (LPBITMAPINFOHEADER) GlobalLock(hDIB);
+ if ((hPalette = MakeDIBPalette(lpInfo)) != 0)
+ {
+ /* Need to realize palette for converting DIB to bitmap. */
+ hOldPal = SelectPalette(hDC, hPalette, TRUE);
+ RealizePalette(hDC);
+
+ lpBits = ((LPSTR)lpInfo + (WORD)lpInfo->biSize +
+ (WORD)lpInfo->biClrUsed * sizeof(RGBQUAD));
+ hBitmap = CreateDIBitmap(hDC, lpInfo, CBM_INIT, lpBits,
+ (LPBITMAPINFO)lpInfo, DIB_RGB_COLORS);
+
+ SelectPalette(hDC, hOldPal, TRUE);
+ RealizePalette(hDC);
+
+ if (!hBitmap)
+ {
+ DeleteObject(hPalette);
+ }
+ else
+ {
+ *phBitmap = hBitmap;
+ *phPal = hPalette;
+ result = TRUE;
+ }
+ }
+ return (result);
+}
+
+
+
+/*
+ * Reads a DIB from a file, obtains a handle to its BITMAPINFO struct, and
+ * loads the DIB. Once the DIB is loaded, the function also creates a bitmap
+ * and palette out of the DIB for a device-dependent form.
+ *
+ * Returns TRUE if the DIB is loaded and the bitmap/palette created, in which
+ * case, the DIBINIT structure pointed to by pInfo is filled with the appropriate
+ * handles, and FALSE if something went wrong.
+ */
+BOOL ReadDIB(HWND hWnd, LPSTR lpFileName, DIBINIT *pInfo)
+{
+ unsigned fh;
+ LPBITMAPINFOHEADER lpbi;
+ OFSTRUCT of;
+ BITMAPFILEHEADER bf;
+ WORD nNumColors;
+ BOOL result = FALSE;
+ char str[128];
+ WORD offBits;
+ HDC hDC;
+ BOOL bCoreHead = FALSE;
+
+ /* Open the file and get a handle to it's BITMAPINFO */
+ fh = OpenFile(lpFileName, &of, OF_READ);
+ if (fh == -1)
+ {
+ wsprintf(str, "Can't open file '%ls'", (LPSTR)lpFileName);
+ MessageBox(NULL, str, "Error", MB_ICONSTOP | MB_OK);
+ return (FALSE);
+ }
+
+ pInfo->hDIB = GlobalAlloc(GHND, (DWORD)(sizeof(BITMAPINFOHEADER) +
+ 256 * sizeof(RGBQUAD)));
+
+ if (!pInfo->hDIB)
+ return (FALSE);
+
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(pInfo->hDIB);
+
+ /* read the BITMAPFILEHEADER */
+ if (sizeof (bf) != _lread(fh, (LPSTR)&bf, sizeof(bf)))
+ goto ErrExit;
+
+ /* 'BM' */
+ if (bf.bfType != 0x4d42)
+ goto ErrExit;
+
+ if (sizeof(BITMAPCOREHEADER) != _lread(fh, (LPSTR)lpbi, sizeof(BITMAPCOREHEADER)))
+ goto ErrExit;
+
+ if (lpbi->biSize == sizeof(BITMAPCOREHEADER))
+ {
+ lpbi->biSize = sizeof(BITMAPINFOHEADER);
+ lpbi->biBitCount = ((LPBITMAPCOREHEADER)lpbi)->bcBitCount;
+ lpbi->biPlanes = ((LPBITMAPCOREHEADER)lpbi)->bcPlanes;
+ lpbi->biHeight = ((LPBITMAPCOREHEADER)lpbi)->bcHeight;
+ lpbi->biWidth = ((LPBITMAPCOREHEADER)lpbi)->bcWidth;
+ bCoreHead = TRUE;
+ }
+ else
+ {
+ /* get to the start of the header and read INFOHEADER */
+ _llseek(fh, sizeof(BITMAPFILEHEADER), SEEK_SET);
+ if (sizeof(BITMAPINFOHEADER) != _lread(fh, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER)))
+ goto ErrExit;
+ }
+
+ if (!(nNumColors = (WORD)lpbi->biClrUsed))
+ {
+ /* no color table for 24-bit, default size otherwise */
+ if (lpbi->biBitCount != 24)
+ nNumColors = 1 << lpbi->biBitCount;
+ }
+
+ /* fill in some default values if they are zero */
+ if (lpbi->biClrUsed == 0)
+ lpbi->biClrUsed = nNumColors;
+
+ if (lpbi->biSizeImage == 0)
+ {
+ lpbi->biSizeImage = (((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3)
+ * lpbi->biHeight);
+ }
+
+ /* otherwise wouldn't work with 16 color bitmaps -- S.K. */
+ else if ((nNumColors == 16) && (lpbi->biSizeImage > bf.bfSize))
+ {
+ lpbi->biSizeImage /= 2;
+ }
+
+ /* get a proper-sized buffer for header, color table and bits */
+ GlobalUnlock(pInfo->hDIB);
+ pInfo->hDIB = GlobalReAlloc(pInfo->hDIB, lpbi->biSize +
+ nNumColors * sizeof(RGBQUAD) +
+ lpbi->biSizeImage, 0);
+
+ /* can't resize buffer for loading */
+ if (!pInfo->hDIB)
+ goto ErrExit2;
+
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(pInfo->hDIB);
+
+ /* read the color table */
+ if (!bCoreHead)
+ {
+ _lread(fh, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD));
+ }
+ else
+ {
+ signed int i;
+ RGBQUAD FAR *pQuad;
+ RGBTRIPLE FAR *pTriple;
+
+ _lread(fh, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBTRIPLE));
+
+ pQuad = (RGBQUAD FAR *)((LPSTR)lpbi + lpbi->biSize);
+ pTriple = (RGBTRIPLE FAR *) pQuad;
+ for (i = nNumColors - 1; i >= 0; i--)
+ {
+ pQuad[i].rgbRed = pTriple[i].rgbtRed;
+ pQuad[i].rgbBlue = pTriple[i].rgbtBlue;
+ pQuad[i].rgbGreen = pTriple[i].rgbtGreen;
+ pQuad[i].rgbReserved = 0;
+ }
+ }
+
+ /* offset to the bits from start of DIB header */
+ offBits = (WORD)lpbi->biSize + nNumColors * sizeof(RGBQUAD);
+
+ if (bf.bfOffBits != 0L)
+ {
+ _llseek(fh, bf.bfOffBits, SEEK_SET);
+ }
+
+ /* Use local version of '_lread()' above */
+ if (lpbi->biSizeImage == lread(fh, (LPSTR)lpbi + offBits, lpbi->biSizeImage))
+ {
+ GlobalUnlock(pInfo->hDIB);
+
+ hDC = GetDC(hWnd);
+ if (!MakeBitmapAndPalette(hDC, pInfo->hDIB, &(pInfo->hPalette),
+ &(pInfo->hBitmap)))
+ {
+ ReleaseDC(hWnd, hDC);
+ goto ErrExit2;
+ }
+ else
+ {
+ ReleaseDC(hWnd, hDC);
+ result = TRUE;
+ }
+ }
+ else
+ {
+ErrExit:
+ GlobalUnlock(pInfo->hDIB);
+ErrExit2:
+ GlobalFree(pInfo->hDIB);
+ }
+
+ _lclose(fh);
+ return (result);
+}
+
+#endif
diff --git a/src/readdib.h b/src/readdib.h
new file mode 100644
index 00000000..c6402b50
--- /dev/null
+++ b/src/readdib.h
@@ -0,0 +1,21 @@
+/* File: readdib.h */
+
+/*
+ * This file has been modified for use with "Angband 2.8.2"
+ *
+ * Copyright 1991 Microsoft Corporation. All rights reserved.
+ */
+
+/*
+ * Information about a bitmap
+ */
+typedef struct {
+ HANDLE hDIB;
+ HANDLE hBitmap;
+ HANDLE hPalette;
+ BYTE CellWidth;
+ BYTE CellHeight;
+} DIBINIT;
+
+/* Read a DIB from a file */
+BOOL ReadDIB(HWND, LPSTR, DIBINIT *);
diff --git a/src/script.c b/src/script.c
new file mode 100644
index 00000000..89c9ff3b
--- /dev/null
+++ b/src/script.c
@@ -0,0 +1,535 @@
+/* File: script.c */
+
+/* Purpose: scripting in lua */
+
+/*
+ * Copyright (c) 2001 Dark God
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#include "lua/lua.h"
+#include "lua/lualib.h"
+#include "lauxlib.h"
+#include "tolua.h"
+
+int tolua_monster_open (lua_State *L);
+int tolua_player_open (lua_State *L);
+int tolua_player_c_open (lua_State *L);
+int tolua_util_open (lua_State *L);
+int tolua_z_pack_open (lua_State *L);
+int tolua_object_open (lua_State *L);
+int tolua_spells_open (lua_State *L);
+int tolua_quest_open (lua_State *L);
+int tolua_dungeon_open (lua_State *L);
+
+/*
+ * Lua state
+ */
+lua_State* L = NULL;
+
+/* ToME Lua error message handler */
+static int tome_errormessage(lua_State *L)
+{
+ char buf[200];
+ cptr str = luaL_check_string(L, 1);
+ int i = 0, j = 0;
+
+ while (str[i])
+ {
+ if (str[i] == '#')
+ {
+ buf[j++] = '$';
+ }
+ else if (str[i] != '\n')
+ {
+ buf[j++] = str[i];
+ }
+ else
+ {
+ buf[j] = '\0';
+ cmsg_format(TERM_VIOLET, "LUA: %s", buf);
+ j = 0;
+ }
+ i++;
+ }
+ buf[j] = '\0';
+ cmsg_format(TERM_VIOLET, "LUA: %s", buf);
+ return (0);
+}
+
+static struct luaL_reg tome_iolib[] =
+{
+ { "_ALERT", tome_errormessage },
+};
+
+#define luaL_check_bit(L, n) ((long)luaL_check_number(L, n))
+#define luaL_check_ubit(L, n) ((unsigned long)luaL_check_bit(L, n))
+
+
+/*
+ * Monadic bit negation operation
+ * MONADIC(not, ~)
+ */
+static int int_not(lua_State* L)
+{
+ lua_pushnumber(L, ~luaL_check_bit(L, 1));
+ return 1;
+}
+
+
+/*
+ * Dyadic integer modulus operation
+ * DYADIC(mod, %)
+ */
+static int int_mod(lua_State* L)
+{
+ lua_pushnumber(L, luaL_check_bit(L, 1) % luaL_check_bit(L, 2));
+ return 1;
+}
+
+
+/*
+ * Variable length bitwise AND operation
+ * VARIADIC(and, &)
+ */
+static int int_and(lua_State *L)
+{
+ int n = lua_gettop(L), i;
+ long w = luaL_check_bit(L, 1);
+
+ for (i = 2; i <= n; i++) w &= luaL_check_bit(L, i);
+ lua_pushnumber(L, w);
+
+ return 1;
+}
+
+
+/*
+ * Variable length bitwise OR operation
+ * VARIADIC(or, |)
+ */
+static int int_or(lua_State *L)
+{
+ int n = lua_gettop(L), i;
+ long w = luaL_check_bit(L, 1);
+
+ for (i = 2; i <= n; i++) w |= luaL_check_bit(L, i);
+ lua_pushnumber(L, w);
+
+ return 1;
+}
+
+
+/*
+ * Variable length bitwise XOR operation
+ * VARIADIC(xor, ^)
+ */
+static int int_xor(lua_State *L)
+{
+ int n = lua_gettop(L), i;
+ long w = luaL_check_bit(L, 1);
+
+ for (i = 2; i <= n; i++) w ^= luaL_check_bit(L, i);
+ lua_pushnumber(L, w);
+
+ return 1;
+}
+
+
+/*
+ * Binary left shift operation
+ * TDYADIC(lshift, <<, , u)
+ */
+static int int_lshift(lua_State* L)
+{
+ lua_pushnumber(L, luaL_check_bit(L, 1) << luaL_check_ubit(L, 2));
+ return 1;
+}
+
+/*
+ * Binary logical right shift operation
+ * TDYADIC(rshift, >>, u, u)
+ */
+static int int_rshift(lua_State* L)
+{
+ lua_pushnumber(L, luaL_check_ubit(L, 1) >> luaL_check_ubit(L, 2));
+ return 1;
+}
+
+/*
+ * Binary arithmetic right shift operation
+ * TDYADIC(arshift, >>, , u)
+ */
+static int int_arshift(lua_State* L)
+{
+ lua_pushnumber(L, luaL_check_bit(L, 1) >> luaL_check_ubit(L, 2));
+ return 1;
+}
+
+
+static const struct luaL_reg bitlib[] =
+{
+ {"bnot", int_not},
+ {"imod", int_mod}, /* "mod" already in Lua math library */
+ {"band", int_and},
+ {"bor", int_or},
+ {"bxor", int_xor},
+ {"lshift", int_lshift},
+ {"rshift", int_rshift},
+ {"arshift", int_arshift},
+};
+
+/*
+ * Initialize lua scripting
+ */
+static bool_ init_lua_done = FALSE;
+void init_lua()
+{
+ /* Hack -- Do not initialize more than once */
+ if (init_lua_done) return;
+ init_lua_done = TRUE;
+
+ /* Start the interpreter with default stack size */
+ L = lua_open(0);
+
+ /* Register the Lua base libraries */
+ lua_baselibopen(L);
+ lua_strlibopen(L);
+ lua_iolibopen(L);
+ lua_dblibopen(L);
+
+ /* Register tome lua debug library */
+ luaL_openl(L, tome_iolib);
+
+ /* Register the bitlib */
+ luaL_openl(L, bitlib);
+
+ /* Register the ToME main APIs */
+ tolua_player_open(L);
+ tolua_player_c_open(L);
+ tolua_util_open(L);
+ tolua_z_pack_open(L);
+ tolua_object_open(L);
+ tolua_monster_open(L);
+ tolua_spells_open(L);
+ tolua_quest_open(L);
+ tolua_dungeon_open(L);
+}
+
+void init_lua_init()
+{
+ int i, max;
+
+ /* Load the first lua file */
+ tome_dofile_anywhere(ANGBAND_DIR_CORE, "init.lua", TRUE);
+
+ /* Finish up schools */
+ max = exec_lua("return __schools_num");
+ init_schools(max);
+ for (i = 0; i < max; i++)
+ {
+ exec_lua(format("finish_school(%d)", i));
+ }
+
+ /* Finish up the spells */
+ max = exec_lua("return __tmp_spells_num");
+ init_spells(max);
+ for (i = 0; i < max; i++)
+ {
+ exec_lua(format("finish_spell(%d)", i));
+ }
+
+ /* Finish up the corruptions */
+ max = exec_lua("return __corruptions_max");
+ init_corruptions(max);
+}
+
+bool_ tome_dofile(char *file)
+{
+ char buf[1024];
+ int oldtop = lua_gettop(L);
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_SCPT, file);
+
+ if (!file_exist(buf))
+ {
+ /* No lua source(.lua), maybe a compiled one(.luo) ? */
+ if (suffix(buf, ".lua"))
+ {
+ int len = strlen(buf);
+ buf[len - 1] = 'o';
+ if (!file_exist(buf))
+ {
+ cmsg_format(TERM_VIOLET,
+ "tome_dofile(): file %s(%s) doesn't exist.", file, buf);
+ return (FALSE);
+ }
+ }
+ }
+
+ lua_dofile(L, buf);
+ lua_settop(L, oldtop);
+
+ return (TRUE);
+}
+
+bool_ tome_dofile_anywhere(cptr dir, char *file, bool_ test_exist)
+{
+ char buf[1024];
+ int oldtop = lua_gettop(L);
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), dir, file);
+
+ if (!file_exist(buf))
+ {
+ /* No lua source(.lua), maybe a compiled one(.luo) ? */
+ if (suffix(buf, ".lua"))
+ {
+ int len = strlen(buf);
+ buf[len - 1] = 'o';
+ if (!file_exist(buf))
+ {
+ if (test_exist)
+ cmsg_format(TERM_VIOLET,
+ "tome_dofile_anywhere(): file %s(%s) doesn't exist in %s.", dir, file, buf);
+ return (FALSE);
+ }
+ }
+ }
+
+ lua_dofile(L, buf);
+ lua_settop(L, oldtop);
+
+ return (TRUE);
+}
+
+int exec_lua(char *file)
+{
+ int oldtop = lua_gettop(L);
+ int res;
+
+ if (!lua_dostring(L, file))
+ {
+ int size = lua_gettop(L) - oldtop;
+ res = tolua_getnumber(L, -size, 0);
+ }
+ else
+ res = 0;
+
+ lua_settop(L, oldtop);
+ return (res);
+}
+
+cptr string_exec_lua(char *file)
+{
+ int oldtop = lua_gettop(L);
+ cptr res;
+
+ if (!lua_dostring(L, file))
+ {
+ int size = lua_gettop(L) - oldtop;
+ res = tolua_getstring(L, -size, "");
+ }
+ else
+ res = "";
+ lua_settop(L, oldtop);
+ return (res);
+}
+
+void dump_lua_stack(int min, int max)
+{
+ int i;
+
+ cmsg_print(TERM_YELLOW, "lua_stack:");
+ for (i = min; i <= max; i++)
+ {
+ if (lua_isnumber(L, i)) cmsg_format(TERM_YELLOW, "%d [n] = %d", i, tolua_getnumber(L, i, 0));
+ else if (lua_isstring(L, i)) cmsg_format(TERM_YELLOW, "%d [s] = '%s'", i, tolua_getstring(L, i, 0));
+ }
+ cmsg_print(TERM_YELLOW, "END lua_stack");
+}
+
+bool_ call_lua(cptr function, cptr args, cptr ret, ...)
+{
+ int i = 0, nb = 0, nbr = 0;
+ int oldtop = lua_gettop(L), size;
+ va_list ap;
+
+ va_start(ap, ret);
+
+ /* Push the function */
+ lua_getglobal(L, function);
+
+ /* Push and count the arguments */
+ while (args[i])
+ {
+ switch (args[i++])
+ {
+ case 'd':
+ case 'l':
+ tolua_pushnumber(L, va_arg(ap, s32b));
+ nb++;
+ break;
+ case 's':
+ tolua_pushstring(L, va_arg(ap, char*));
+ nb++;
+ break;
+ case 'O':
+ tolua_pushusertype(L, (void*)va_arg(ap, object_type*), tolua_tag(L, "object_type"));
+ nb++;
+ break;
+ case 'M':
+ tolua_pushusertype(L, (void*)va_arg(ap, monster_type*), tolua_tag(L, "monster_type"));
+ nb++;
+ break;
+ case 'n':
+ lua_pushnil(L);
+ nb++;
+ break;
+ case '(':
+ case ')':
+ case ',':
+ break;
+ }
+ }
+
+ /* Count returns */
+ nbr = strlen(ret);
+
+ /* Call the function */
+ if (lua_call(L, nb, nbr))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling '%s' from call_lua. Things should start breaking up from now on!", function);
+ return FALSE;
+ }
+
+ /* Number of returned values, SHOULD be the same as nbr, but I'm paranoid */
+ size = lua_gettop(L) - oldtop;
+
+ /* Get the returns */
+ for (i = 0; ret[i]; i++)
+ {
+ switch (ret[i])
+ {
+ case 'd':
+ case 'l':
+ {
+ s32b *tmp = va_arg(ap, s32b*);
+
+ if (lua_isnumber(L, ( -size) + i)) *tmp = tolua_getnumber(L, ( -size) + i, 0);
+ else *tmp = 0;
+ break;
+ }
+
+ case 's':
+ {
+ cptr *tmp = va_arg(ap, cptr*);
+
+ if (lua_isstring(L, ( -size) + i)) *tmp = tolua_getstring(L, ( -size) + i, "");
+ else *tmp = NULL;
+ break;
+ }
+
+ case 'O':
+ {
+ object_type **tmp = va_arg(ap, object_type**);
+
+ if (tolua_istype(L, ( -size) + i, tolua_tag(L, "object_type"), 0))
+ *tmp = (object_type*)tolua_getuserdata(L, ( -size) + i, NULL);
+ else
+ *tmp = NULL;
+ break;
+ }
+
+ case 'M':
+ {
+ monster_type **tmp = va_arg(ap, monster_type**);
+
+ if (tolua_istype(L, ( -size) + i, tolua_tag(L, "monster_type"), 0))
+ *tmp = (monster_type*)tolua_getuserdata(L, ( -size) + i, NULL);
+ else
+ *tmp = NULL;
+ break;
+ }
+
+ default:
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling '%s' from call_lua: Unknown return type '%c'", function, ret[i]);
+ return FALSE;
+ }
+ }
+
+ lua_settop(L, oldtop);
+
+ va_end(ap);
+
+ return TRUE;
+}
+
+bool_ get_lua_var(cptr name, char type, void *arg)
+{
+ int oldtop = lua_gettop(L), size;
+
+ /* Push the function */
+ lua_getglobal(L, name);
+
+ size = lua_gettop(L) - oldtop;
+
+ switch (type)
+ {
+ case 'd':
+ case 'l':
+ {
+ s32b *tmp = (s32b*)arg;
+
+ if (lua_isnumber(L, ( -size))) *tmp = tolua_getnumber(L, ( -size), 0);
+ else *tmp = 0;
+ break;
+ }
+
+ case 's':
+ {
+ cptr *tmp = (cptr*)arg;
+
+ if (lua_isstring(L, ( -size))) *tmp = tolua_getstring(L, ( -size), "");
+ else *tmp = NULL;
+ break;
+ }
+
+ case 'O':
+ {
+ object_type **tmp = (object_type**)arg;
+
+ if (tolua_istype(L, ( -size), tolua_tag(L, "object_type"), 0))
+ *tmp = (object_type*)tolua_getuserdata(L, ( -size), NULL);
+ else
+ *tmp = NULL;
+ break;
+ }
+
+ case 'M':
+ {
+ monster_type **tmp = (monster_type**)arg;
+
+ if (tolua_istype(L, ( -size), tolua_tag(L, "monster_type"), 0))
+ *tmp = (monster_type*)tolua_getuserdata(L, ( -size), NULL);
+ else
+ *tmp = NULL;
+ break;
+ }
+
+ default:
+ cmsg_format(TERM_VIOLET, "ERROR in get_lua_var while calling '%s': Unknown return type '%c'", name, type);
+ return FALSE;
+ }
+
+ lua_settop(L, oldtop);
+
+ return TRUE;
+}
diff --git a/src/skills.c b/src/skills.c
new file mode 100644
index 00000000..36b4f585
--- /dev/null
+++ b/src/skills.c
@@ -0,0 +1,1661 @@
+/* File: skills.c */
+
+/* Purpose: player skills */
+
+/*
+ * Copyright (c) 2001 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Advance the skill point of the skill specified by i and
+ * modify related skills
+ */
+void increase_skill(int i, s16b *invest)
+{
+ s32b max_skill_overage;
+
+ /* No skill points to be allocated */
+ if (!p_ptr->skill_points) return;
+
+ /* The skill cannot be increased */
+ if (!s_info[i].mod) return;
+
+ /* The skill is already maxed */
+ if (s_info[i].value >= SKILL_MAX) return;
+
+ /* Cannot allocate more than player level + max_skill_overage levels */
+ call_lua("get_module_info", "(s)", "d", "max_skill_overage", &max_skill_overage);
+ if (((s_info[i].value + s_info[i].mod) / SKILL_STEP) >= (p_ptr->lev + max_skill_overage + 1))
+ {
+ int hgt, wid;
+
+ Term_get_size(&wid, &hgt);
+ msg_box(format("Cannot raise a skill value above %i + player level.", max_skill_overage), (int)(hgt / 2), (int)(wid / 2));
+ return;
+ }
+
+ /* Spend an unallocated skill point */
+ p_ptr->skill_points--;
+
+ /* Increase the skill */
+ s_info[i].value += s_info[i].mod;
+ invest[i]++;
+}
+
+
+/*
+ * Descrease the skill point of the skill specified by i and
+ * modify related skills
+ */
+void decrease_skill(int i, s16b *invest)
+{
+ /* Cannot decrease more */
+ if (!invest[i]) return;
+
+ /* The skill cannot be decreased */
+ if (!s_info[i].mod) return;
+
+ /* The skill already has minimal value */
+ if (!s_info[i].value) return;
+
+ /* Free a skill point */
+ p_ptr->skill_points++;
+
+ /* Decrease the skill */
+ s_info[i].value -= s_info[i].mod;
+ invest[i]--;
+}
+
+
+/*
+ * Given the name of a skill, returns skill index or -1 if no
+ * such skill is found
+ */
+s16b find_skill(cptr name)
+{
+ u16b i;
+
+ /* Scan skill list */
+ for (i = 1; i < max_s_idx; i++)
+ {
+ /* The name matches */
+ if (streq(s_info[i].name + s_name, name)) return (i);
+ }
+
+ /* No match found */
+ return ( -1);
+}
+s16b find_skill_i(cptr name)
+{
+ u16b i;
+
+ /* Scan skill list */
+ for (i = 1; i < max_s_idx; i++)
+ {
+ /* The name matches */
+ if (0 == stricmp(s_info[i].name + s_name, name)) return (i);
+ }
+
+ /* No match found */
+ return ( -1);
+}
+
+
+/*
+ *
+ */
+s16b get_skill(int skill)
+{
+ return (s_info[skill].value / SKILL_STEP);
+}
+
+
+/*
+ * Return "scale" (a misnomer -- this is max value) * (current skill value)
+ * / (max skill value)
+ */
+s16b get_skill_scale(int skill, u32b scale)
+{
+ s32b temp;
+
+ /*
+ * SKILL_STEP shouldn't matter here because the second parameter is
+ * relatively small (the largest one being somewhere around 200),
+ * AND because we could have used much simpler 0--50 if the ability
+ * progression were only possible at step boundaries.
+ *
+ * Because I'm not at all certain about my interpretation of the mysterious
+ * formula given above, I verified this works the same by using a tiny
+ * scheme program... -- pelpel
+ */
+ temp = scale * s_info[skill].value;
+
+ return (temp / SKILL_MAX);
+}
+
+
+/*
+ *
+ */
+int get_idx(int i)
+{
+ int j;
+
+ for (j = 1; j < max_s_idx; j++)
+ {
+ if (s_info[j].order == i)
+ return (j);
+ }
+ return (0);
+}
+
+static bool_ is_known(int s_idx)
+{
+ int i;
+
+ if (wizard) return TRUE;
+ if (s_info[s_idx].value || s_info[s_idx].mod) return TRUE;
+
+ for (i = 0; i < max_s_idx; i++)
+ {
+ /* It is our child, if we don't know it we continue to search, if we know it it is enough*/
+ if (s_info[i].father == s_idx)
+ {
+ if (is_known(i))
+ return TRUE;
+ }
+ }
+
+ /* Ok know none */
+ return FALSE;
+}
+
+/*
+ *
+ */
+void init_table_aux(int table[MAX_SKILLS][2], int *idx, int father, int lev,
+ bool_ full)
+{
+ int j, i;
+
+ for (j = 1; j < max_s_idx; j++)
+ {
+ i = get_idx(j);
+ if (s_info[i].father != father) continue;
+ if (s_info[i].hidden) continue;
+ if (!is_known(i)) continue;
+
+ table[*idx][0] = i;
+ table[*idx][1] = lev;
+ (*idx)++;
+ if (s_info[i].dev || full) init_table_aux(table, idx, i, lev + 1, full);
+ }
+}
+
+
+void init_table(int table[MAX_SKILLS][2], int *max, bool_ full)
+{
+ *max = 0;
+ init_table_aux(table, max, -1, 0, full);
+}
+
+
+bool_ has_child(int sel)
+{
+ int i;
+
+ for (i = 1; i < max_s_idx; i++)
+ {
+ if ((s_info[i].father == sel) && (is_known(i)))
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+
+/*
+ * Dump the skill tree
+ */
+void dump_skills(FILE *fff)
+{
+ int i, j, max = 0;
+ int table[MAX_SKILLS][2];
+ char buf[80];
+
+ init_table(table, &max, TRUE);
+
+ fprintf(fff, "\nSkills (points left: %d)", p_ptr->skill_points);
+
+ for (j = 0; j < max; j++)
+ {
+ int z;
+
+ i = table[j][0];
+
+ if ((s_info[i].value == 0) && (i != SKILL_MISC))
+ {
+ if (s_info[i].mod == 0) continue;
+ }
+
+ sprintf(buf, "\n");
+
+ for (z = 0; z < table[j][1]; z++) strcat(buf, " ");
+
+ if (!has_child(i))
+ {
+ strcat(buf, format(" . %s", s_info[i].name + s_name));
+ }
+ else
+ {
+ strcat(buf, format(" - %s", s_info[i].name + s_name));
+ }
+
+ fprintf(fff, "%-49s%s%06.3f [%05.3f]",
+ buf, s_info[i].value < 0 ? "-" : " ",
+ ((double) ABS(s_info[i].value)) / SKILL_STEP,
+ ((double) s_info[i].mod) / 1000);
+ }
+
+ fprintf(fff, "\n");
+}
+
+
+/*
+ * Draw the skill tree
+ */
+void print_skills(int table[MAX_SKILLS][2], int max, int sel, int start)
+{
+ int i, j;
+ int wid, hgt;
+ cptr keys;
+
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ c_prt(TERM_WHITE, format("%s Skills Screen", game_module), 0, 28);
+ keys = format("#BEnter#W to develop a branch, #Bup#W/#Bdown#W to move, #Bright#W/#Bleft#W to modify, #B?#W for help");
+ display_message(0, 1, strlen(keys), TERM_WHITE, keys);
+ c_prt((p_ptr->skill_points) ? TERM_L_BLUE : TERM_L_RED,
+ format("Skill points left: %d", p_ptr->skill_points), 2, 0);
+ print_desc_aux(s_info[table[sel][0]].desc + s_text, 3, 0);
+
+ for (j = start; j < start + (hgt - 7); j++)
+ {
+ byte color = TERM_WHITE;
+ char deb = ' ', end = ' ';
+
+ if (j >= max) break;
+
+ i = table[j][0];
+
+ if ((s_info[i].value == 0) && (i != SKILL_MISC))
+ {
+ if (s_info[i].mod == 0) color = TERM_L_DARK;
+ else color = TERM_ORANGE;
+ }
+ else if (s_info[i].value == SKILL_MAX) color = TERM_L_BLUE;
+ if (s_info[i].hidden) color = TERM_L_RED;
+ if (j == sel)
+ {
+ color = TERM_L_GREEN;
+ deb = '[';
+ end = ']';
+ }
+ if (!has_child(i))
+ {
+ c_prt(color, format("%c.%c%s", deb, end, s_info[i].name + s_name),
+ j + 7 - start, table[j][1] * 4);
+ }
+ else if (s_info[i].dev)
+ {
+ c_prt(color, format("%c-%c%s", deb, end, s_info[i].name + s_name),
+ j + 7 - start, table[j][1] * 4);
+ }
+ else
+ {
+ c_prt(color, format("%c+%c%s", deb, end, s_info[i].name + s_name),
+ j + 7 - start, table[j][1] * 4);
+ }
+ c_prt(color,
+ format("%s%02ld.%03ld [%01d.%03d]",
+ s_info[i].value < 0 ? "-" : " ",
+ ABS(s_info[i].value) / SKILL_STEP,
+ ABS(s_info[i].value) % SKILL_STEP,
+ ABS(s_info[i].mod) / 1000,
+ ABS(s_info[i].mod) % 1000),
+ j + 7 - start, 60);
+ }
+}
+
+/*
+ * Checks various stuff to do when skills change, like new spells, ...
+ */
+void recalc_skills(bool_ init)
+{
+ static int thaum_level = 0;
+
+ /* TODO: This should be a hook in ToME's lua */
+ if (init)
+ {
+ thaum_level = get_skill_scale(SKILL_THAUMATURGY, 100);
+ }
+ else
+ {
+ int thaum_gain = 0;
+
+ /* Gain thaum spells */
+ while (thaum_level < get_skill_scale(SKILL_THAUMATURGY, 100))
+ {
+ if (spell_num == MAX_SPELLS) break;
+ thaum_level++;
+ generate_spell((thaum_level + 1) / 2);
+ thaum_gain++;
+ }
+ if (thaum_gain)
+ {
+ if (thaum_gain == 1)
+ msg_print("You have gained one new thaumaturgy spell.");
+ else
+ msg_format("You have gained %d new thaumaturgy spells.", thaum_gain);
+ }
+
+ process_hooks(HOOK_RECALC_SKILLS, "()");
+
+ /* Update stuffs */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ /* Redraw various info */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+ }
+}
+
+/*
+ * Recalc the skill value
+ */
+void recalc_skills_theory(s16b *invest, s32b *base_val, s32b *base_mod, s32b *bonus)
+{
+ int i, j;
+
+ /* First we assign the normal points */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ /* Calc the base */
+ s_info[i].value = base_val[i] + (base_mod[i] * invest[i]) + bonus[i];
+
+ /* It cannot exceed SKILL_MAX */
+ if (s_info[i].value > SKILL_MAX) s_info[i].value = SKILL_MAX;
+ }
+
+ /* Then we modify related skills */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ for (j = 1; j < max_s_idx; j++)
+ {
+ /* Ignore self */
+ if (j == i) continue;
+
+ /* Exclusive skills */
+ if ((s_info[i].action[j] == SKILL_EXCLUSIVE) && invest[i])
+ {
+ /* Turn it off */
+ p_ptr->skill_points += invest[j];
+ invest[j] = 0;
+ s_info[j].value = 0;
+ }
+
+ /* Non-exclusive skills */
+ else if (s_info[i].action[j])
+ {
+ /* Increase / decrease with a % */
+ s32b val = s_info[j].value + (invest[i] * s_info[j].mod * s_info[i].action[j] / 100);
+
+ /* It cannot exceed SKILL_MAX */
+ if (val > SKILL_MAX) val = SKILL_MAX;
+
+ /* Save the modified value */
+ s_info[j].value = val;
+ }
+ }
+ }
+}
+
+/*
+ * Interreact with skills
+ */
+void do_cmd_skill()
+{
+ int sel = 0, start = 0, max;
+ char c;
+ int table[MAX_SKILLS][2];
+ int i;
+ int wid, hgt;
+ s16b skill_points_save;
+ s32b *skill_values_save;
+ s32b *skill_mods_save;
+ s16b *skill_rates_save;
+ s16b *skill_invest;
+ s32b *skill_bonus;
+
+ recalc_skills(TRUE);
+
+ /* Save the screen */
+ screen_save();
+
+ /* Allocate arrays to save skill values */
+ C_MAKE(skill_values_save, MAX_SKILLS, s32b);
+ C_MAKE(skill_mods_save, MAX_SKILLS, s32b);
+ C_MAKE(skill_rates_save, MAX_SKILLS, s16b);
+ C_MAKE(skill_invest, MAX_SKILLS, s16b);
+ C_MAKE(skill_bonus, MAX_SKILLS, s32b);
+
+ /* Save skill points */
+ skill_points_save = p_ptr->skill_points;
+
+ /* Save skill values */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ skill_type *s_ptr = &s_info[i];
+
+ skill_values_save[i] = s_ptr->value;
+ skill_mods_save[i] = s_ptr->mod;
+ skill_rates_save[i] = s_ptr->rate;
+ skill_invest[i] = 0;
+ }
+
+ /* Clear the screen */
+ Term_clear();
+
+ /* Initialise the skill list */
+ init_table(table, &max, FALSE);
+
+ while (TRUE)
+ {
+ Term_get_size(&wid, &hgt);
+
+ /* Display list of skills */
+ recalc_skills_theory(skill_invest, skill_values_save, skill_mods_save, skill_bonus);
+ print_skills(table, max, sel, start);
+
+ /* Wait for user input */
+ c = inkey();
+
+ /* Leave the skill screen */
+ if (c == ESCAPE) break;
+
+ /* Expand / collapse list of skills */
+ else if (c == '\r')
+ {
+ if (s_info[table[sel][0]].dev) s_info[table[sel][0]].dev = FALSE;
+ else s_info[table[sel][0]].dev = TRUE;
+ init_table(table, &max, FALSE);
+ }
+
+ /* Next page */
+ else if (c == 'n')
+ {
+ sel += (hgt - 7);
+ if (sel >= max) sel = max - 1;
+ }
+
+ /* Previous page */
+ else if (c == 'p')
+ {
+ sel -= (hgt - 7);
+ if (sel < 0) sel = 0;
+ }
+
+ /* Select / increase a skill */
+ else
+ {
+ int dir;
+
+ /* Allow use of numpad / arrow keys / roguelike keys */
+ dir = get_keymap_dir(c);
+
+ /* Move cursor down */
+ if (dir == 2) sel++;
+
+ /* Move cursor up */
+ if (dir == 8) sel--;
+
+ /* Miscellaneous skills cannot be increased/decreased as a group */
+ if (table[sel][0] == SKILL_MISC) continue;
+
+ /* Increase the current skill */
+ if (dir == 6) increase_skill(table[sel][0], skill_invest);
+
+ /* Decrease the current skill */
+ if (dir == 4) decrease_skill(table[sel][0], skill_invest);
+
+ /* XXX XXX XXX Wizard mode commands outside of wizard2.c */
+
+ /* Increase the skill */
+ if (wizard && (c == '+')) skill_bonus[table[sel][0]] += SKILL_STEP;
+
+ /* Decrease the skill */
+ if (wizard && (c == '-')) skill_bonus[table[sel][0]] -= SKILL_STEP;
+
+ /* Contextual help */
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'skill', '%s')", s_info[table[sel][0]].name + s_name));
+ ;
+
+ /* Handle boundaries and scrolling */
+ if (sel < 0) sel = max - 1;
+ if (sel >= max) sel = 0;
+ if (sel < start) start = sel;
+ if (sel >= start + (hgt - 7)) start = sel - (hgt - 7) + 1;
+ }
+ }
+
+
+ /* Some skill points are spent */
+ if (p_ptr->skill_points != skill_points_save)
+ {
+ /* Flush input as we ask an important and irreversible question */
+ flush();
+
+ /* Ask we can commit the change */
+ if (msg_box("Save and use these skill values? (y/n)", (int)(hgt / 2), (int)(wid / 2)) != 'y')
+ {
+ /* User declines -- restore the skill values before exiting */
+
+ /* Restore skill points */
+ p_ptr->skill_points = skill_points_save;
+
+ /* Restore skill values */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ skill_type *s_ptr = &s_info[i];
+
+ s_ptr->value = skill_values_save[i];
+ s_ptr->mod = skill_mods_save[i];
+ s_ptr->rate = skill_rates_save[i];
+ }
+ }
+ }
+
+
+ /* Free arrays to save skill values */
+ C_FREE(skill_values_save, MAX_SKILLS, s32b);
+ C_FREE(skill_mods_save, MAX_SKILLS, s32b);
+ C_FREE(skill_rates_save, MAX_SKILLS, s16b);
+ C_FREE(skill_invest, MAX_SKILLS, s16b);
+ C_FREE(skill_bonus, MAX_SKILLS, s32b);
+
+ /* Load the screen */
+ screen_load();
+
+ recalc_skills(FALSE);
+}
+
+
+
+/*
+ * List of melee skills
+ */
+s16b melee_skills[MAX_MELEE] =
+{
+ SKILL_MASTERY,
+ SKILL_HAND,
+ SKILL_BEAR,
+};
+char *melee_names[MAX_MELEE] =
+{
+ "Weapon combat",
+ "Barehanded combat",
+ "Bearform combat",
+};
+static bool_ melee_bool[MAX_MELEE];
+static int melee_num[MAX_MELEE];
+
+s16b get_melee_skill()
+{
+ int i;
+
+ for (i = 0; i < MAX_MELEE; i++)
+ {
+ if (p_ptr->melee_style == melee_skills[i])
+ return (i);
+ }
+ return (0);
+}
+
+s16b get_melee_skills()
+{
+ int i, j = 0;
+
+ for (i = 0; i < MAX_MELEE; i++)
+ {
+ if ((s_info[melee_skills[i]].value > 0) && (!s_info[melee_skills[i]].hidden))
+ {
+ melee_bool[i] = TRUE;
+ j++;
+ }
+ else
+ melee_bool[i] = FALSE;
+ }
+
+ return (j);
+}
+
+static void choose_melee()
+{
+ int i, j, z = 0;
+ int force_drop = FALSE, style_unchanged = FALSE;
+
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+ j = get_melee_skills();
+ prt("Choose a melee style:", 0, 0);
+ for (i = 0; i < MAX_MELEE; i++)
+ {
+ if (melee_bool[i])
+ {
+ prt(format("%c) %s", I2A(z), melee_names[i]), z + 1, 0);
+ melee_num[z] = i;
+ z++;
+ }
+ }
+
+ while (TRUE)
+ {
+ char c = inkey();
+
+ if (c == ESCAPE) break;
+ if (A2I(c) < 0) continue;
+ if (A2I(c) >= j) continue;
+
+ for (i = 0, z = 0; z < A2I(c); i++)
+ if (melee_bool[i]) z++;
+
+ if (p_ptr->melee_style == melee_skills[melee_num[z]])
+ {
+ style_unchanged = TRUE;
+ break;
+ }
+
+ for (i = INVEN_WIELD; p_ptr->body_parts[i - INVEN_WIELD] == INVEN_WIELD; i++)
+ {
+ if (p_ptr->inventory[i].k_idx)
+ {
+ if (cursed_p(&p_ptr->inventory[i]))
+ {
+ char name[80];
+ object_desc(name, &p_ptr->inventory[i], 0, 0);
+ msg_format("Hmmm, your %s seems to be cursed.", name);
+ break;
+ }
+ else if (INVEN_PACK == inven_takeoff(i, 255, force_drop))
+ {
+ force_drop = TRUE;
+ }
+ }
+ }
+ p_ptr->melee_style = melee_skills[melee_num[z]];
+ energy_use = 100;
+ break;
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate hitpoint */
+ p_ptr->update |= (PU_HP);
+
+ /* Redraw monster hitpoint */
+ p_ptr->redraw |= (PR_MH);
+
+ Term_load();
+ character_icky = FALSE;
+
+ if (style_unchanged)
+ {
+ msg_format("You are already using %s.", melee_names[melee_num[z]]);
+ }
+}
+
+void select_default_melee()
+{
+ int i;
+
+ get_melee_skills();
+ p_ptr->melee_style = SKILL_MASTERY;
+ for (i = 0; i < MAX_MELEE; i++)
+ {
+ if (melee_bool[i])
+ {
+ p_ptr->melee_style = melee_skills[i];
+ break;
+ }
+ }
+}
+
+/*
+ * Print a batch of skills.
+ */
+static void print_skill_batch(int *p, cptr *p_desc, int start, int max, bool_ mode)
+{
+ char buff[80];
+ int i = start, j = 0;
+
+ if (mode) prt(format(" %-31s", "Name"), 1, 20);
+
+ for (i = start; i < (start + 20); i++)
+ {
+ if (i >= max) break;
+
+ if (p[i] > 0)
+ sprintf(buff, " %c - %d) %-30s", I2A(j), p[i], p_desc[i]);
+ else
+ sprintf(buff, " %c - %d) %-30s", I2A(j), p[i], "Change melee style");
+
+ if (mode) prt(buff, 2 + j, 20);
+ j++;
+ }
+ if (mode) prt("", 2 + j, 20);
+ prt(format("Select a skill (a-%c), @ to select by name, +/- to scroll:", I2A(j - 1)), 0, 0);
+}
+
+int do_cmd_activate_skill_aux()
+{
+ char which;
+ int max = 0, i, start = 0;
+ int ret;
+ bool_ mode = FALSE;
+ int *p;
+ cptr *p_desc;
+
+ C_MAKE(p, max_s_idx + max_ab_idx, int);
+ C_MAKE(p_desc, max_s_idx + max_ab_idx, cptr);
+
+ /* Count the max */
+
+ /* More than 1 melee skill ? */
+ if (get_melee_skills() > 1)
+ {
+ p_desc[max] = "Change melee mode";
+ p[max++] = 0;
+ }
+
+ for (i = 1; i < max_s_idx; i++)
+ {
+ if (s_info[i].action_mkey && s_info[i].value && ((!s_info[i].hidden) || (i == SKILL_LEARN)))
+ {
+ int j;
+ bool_ next = FALSE;
+
+ /* Already got it ? */
+ for (j = 0; j < max; j++)
+ {
+ if (s_info[i].action_mkey == p[j])
+ {
+ next = TRUE;
+ break;
+ }
+ }
+ if (next) continue;
+
+ p_desc[max] = s_text + s_info[i].action_desc;
+ p[max++] = s_info[i].action_mkey;
+ }
+ }
+
+ for (i = 0; i < max_ab_idx; i++)
+ {
+ if (ab_info[i].action_mkey && ab_info[i].acquired)
+ {
+ int j;
+ bool_ next = FALSE;
+
+ /* Already got it ? */
+ for (j = 0; j < max; j++)
+ {
+ if (ab_info[i].action_mkey == p[j])
+ {
+ next = TRUE;
+ break;
+ }
+ }
+ if (next) continue;
+
+ p_desc[max] = ab_text + ab_info[i].action_desc;
+ p[max++] = ab_info[i].action_mkey;
+ }
+ }
+
+ if (!max)
+ {
+ msg_print("You don't have any activable skills or abilities.");
+ return -1;
+ }
+
+ character_icky = TRUE;
+ Term_save();
+
+ while (1)
+ {
+ print_skill_batch(p, p_desc, start, max, mode);
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ ret = -1;
+ break;
+ }
+ else if (which == '*' || which == '?' || which == ' ')
+ {
+ mode = (mode) ? FALSE : TRUE;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '+')
+ {
+ start += 20;
+ if (start >= max) start -= 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '-')
+ {
+ start -= 20;
+ if (start < 0) start += 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '@')
+ {
+ char buf[80];
+
+ strcpy(buf, "Cast a spell");
+ if (!get_string("Skill action? ", buf, 79))
+ return FALSE;
+
+ /* Find the skill it is related to */
+ for (i = 0; i < max; i++)
+ {
+ if (!strcmp(buf, p_desc[i]))
+ break;
+ }
+ if ((i < max))
+ {
+ ret = p[i];
+ break;
+ }
+
+ }
+ else
+ {
+ which = tolower(which);
+ if (start + A2I(which) >= max)
+ {
+ bell();
+ continue;
+ }
+ if (start + A2I(which) < 0)
+ {
+ bell();
+ continue;
+ }
+
+ ret = p[start + A2I(which)];
+ break;
+ }
+ }
+ Term_load();
+ character_icky = FALSE;
+
+ C_FREE(p, max_s_idx + max_ab_idx, int);
+ C_FREE(p_desc, max_s_idx + max_ab_idx, cptr);
+
+ return ret;
+}
+
+/* Ask & execute a skill */
+void do_cmd_activate_skill()
+{
+ int x_idx;
+ bool_ push = TRUE;
+
+ /* Get the skill, if available */
+ if (repeat_pull(&x_idx))
+ {
+ push = FALSE;
+ }
+ else if (!command_arg) x_idx = do_cmd_activate_skill_aux();
+ else
+ {
+ int i, j;
+
+ x_idx = command_arg;
+
+ /* Check validity */
+ for (i = 1; i < max_s_idx; i++)
+ {
+ if (s_info[i].value && (s_info[i].action_mkey == x_idx))
+ break;
+ }
+ for (j = 0; j < max_ab_idx; j++)
+ {
+ if (ab_info[j].acquired && (ab_info[j].action_mkey == x_idx))
+ break;
+ }
+
+ if ((j == max_ab_idx) && (i == max_s_idx))
+ {
+ msg_print("Uh?");
+ return;
+ }
+ }
+
+ if (x_idx == -1) return;
+
+ if (push) repeat_push(x_idx);
+
+ if (!x_idx)
+ {
+ choose_melee();
+ return;
+ }
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+ switch (x_idx)
+ {
+ case MKEY_ANTIMAGIC:
+ do_cmd_unbeliever();
+ break;
+ case MKEY_MINDCRAFT:
+ do_cmd_mindcraft();
+ break;
+ case MKEY_ALCHEMY:
+ do_cmd_alchemist();
+ break;
+ case MKEY_MIMIC:
+ do_cmd_mimic();
+ break;
+ case MKEY_POWER_MAGE:
+ do_cmd_powermage();
+ break;
+ case MKEY_RUNE:
+ do_cmd_runecrafter();
+ break;
+ case MKEY_FORGING:
+ do_cmd_archer();
+ break;
+ case MKEY_INCARNATION:
+ do_cmd_possessor();
+ break;
+ case MKEY_TELEKINESIS:
+ do_cmd_portable_hole();
+ break;
+ case MKEY_BLADE:
+ do_cmd_blade();
+ break;
+ case MKEY_SUMMON:
+ do_cmd_summoner();
+ break;
+ case MKEY_NECRO:
+ do_cmd_necromancer();
+ break;
+ case MKEY_SYMBIOTIC:
+ do_cmd_symbiotic();
+ break;
+ case MKEY_TRAP:
+ do_cmd_set_trap();
+ break;
+ case MKEY_STEAL:
+ do_cmd_steal();
+ break;
+ case MKEY_DODGE:
+ use_ability_blade();
+ break;
+ case MKEY_SCHOOL:
+ cast_school_spell();
+ break;
+ case MKEY_COPY:
+ do_cmd_copy_spell();
+ break;
+ case MKEY_BOULDER:
+ do_cmd_create_boulder();
+ break;
+ case MKEY_COMPANION:
+ if (get_skill(SKILL_LORE) >= 12)
+ do_cmd_companion();
+ else
+ msg_print("You need a skill level of at least 12.");
+ break;
+ case MKEY_PIERCING:
+ do_cmd_set_piercing();
+ break;
+ default:
+ process_hooks(HOOK_MKEY, "(d)", x_idx);
+ break;
+ }
+}
+
+
+/* Which magic forbids non FA gloves */
+bool_ forbid_gloves()
+{
+ if (get_skill(SKILL_SORCERY)) return (TRUE);
+ if (get_skill(SKILL_MANA)) return (TRUE);
+ if (get_skill(SKILL_FIRE)) return (TRUE);
+ if (get_skill(SKILL_AIR)) return (TRUE);
+ if (get_skill(SKILL_WATER)) return (TRUE);
+ if (get_skill(SKILL_EARTH)) return (TRUE);
+ if (get_skill(SKILL_THAUMATURGY)) return (TRUE);
+ return (FALSE);
+}
+
+/* Which gods forbid edged weapons */
+bool_ forbid_non_blessed()
+{
+ GOD(GOD_ERU) return (TRUE);
+ return (FALSE);
+}
+
+
+/*
+ * Gets the base value of a skill, given a race/class/...
+ */
+void compute_skills(s32b *v, s32b *m, int i)
+{
+ s32b value, mod;
+
+ /***** general skills *****/
+
+ /* If the skill correspond to the magic school lets pump them a bit */
+ value = gen_skill_base[i];
+ mod = gen_skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, gen_skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, gen_skill_modm[i]);
+
+ /***** race skills *****/
+
+ value = rp_ptr->skill_base[i];
+ mod = rp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, rp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, rp_ptr->skill_modm[i]);
+
+ /***** race mod skills *****/
+
+ value = rmp_ptr->skill_base[i];
+ mod = rmp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, rmp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, rmp_ptr->skill_modm[i]);
+
+ /***** class skills *****/
+
+ value = cp_ptr->skill_base[i];
+ mod = cp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, cp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, cp_ptr->skill_modm[i]);
+
+ /***** class spec skills *****/
+
+ value = spp_ptr->skill_base[i];
+ mod = spp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, spp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, spp_ptr->skill_modm[i]);
+}
+
+/*
+ * Initialize a skill with given values
+ */
+void init_skill(s32b value, s32b mod, int i)
+{
+ s_info[i].value = value;
+ s_info[i].mod = mod;
+
+ if (s_info[i].flags1 & SKF1_HIDDEN)
+ s_info[i].hidden = TRUE;
+ else
+ s_info[i].hidden = FALSE;
+}
+
+void do_get_new_skill()
+{
+ char *items[LOST_SWORD_NSKILLS];
+ int skl[LOST_SWORD_NSKILLS];
+ s32b val[LOST_SWORD_NSKILLS], mod[LOST_SWORD_NSKILLS];
+ bool_ used[MAX_SKILLS];
+ int available_skills[MAX_SKILLS];
+ int max = 0, max_a = 0, res, i;
+
+ /* Check if some skills didn't influence other stuff */
+ recalc_skills(TRUE);
+
+ /* Grab the ones we can gain */
+ max = 0;
+ for (i = 0; i < max_s_idx; i++)
+ {
+ if (s_info[i].flags1 & SKF1_RANDOM_GAIN)
+ available_skills[max++] = i;
+ }
+ available_skills[max++] = -1;
+
+ /* Init */
+ for (max = 0; max < MAX_SKILLS; max++)
+ {
+ used[max] = FALSE;
+ }
+
+ /* Count the number of available skills */
+ while (available_skills[max_a] != -1) max_a++;
+
+ /* Get LOST_SWORD_NSKILLS skills */
+ for (max = 0; max < LOST_SWORD_NSKILLS; max++)
+ {
+ int i;
+ skill_type *s_ptr;
+
+ /* Get an non used skill */
+ do
+ {
+ i = rand_int(max_a);
+
+ /* Does it pass the check? */
+ if (!magik(s_info[available_skills[i]].random_gain_chance))
+ continue;
+ }
+ while (used[available_skills[i]]);
+
+ s_ptr = &s_info[available_skills[i]];
+ used[available_skills[i]] = TRUE;
+
+ if (s_ptr->mod)
+ {
+ if (s_ptr->mod < 300)
+ {
+ val[max] = 1000;
+ mod[max] = 300 - s_ptr->mod;
+ }
+ else if (s_ptr->mod < 500)
+ {
+ val[max] = s_ptr->mod * 1;
+ mod[max] = 100;
+ if (mod[max] + s_ptr->mod > 500)
+ mod[max] = 500 - s_ptr->mod;
+ }
+ else
+ {
+ val[max] = s_ptr->mod * 3;
+ mod[max] = 0;
+ }
+ }
+ else
+ {
+ mod[max] = 300;
+ val[max] = 1000;
+ }
+ if (s_ptr->value + val[max] > SKILL_MAX) val[max] = SKILL_MAX - s_ptr->value;
+ skl[max] = available_skills[i];
+ items[max] = (char *)string_make(format("%-40s: +%02ld.%03ld value, +%01d.%03d modifier", s_ptr->name + s_name, val[max] / SKILL_STEP, val[max] % SKILL_STEP, mod[max] / SKILL_STEP, mod[max] % SKILL_STEP));
+ }
+
+ while (TRUE)
+ {
+ char last = 'a' + (LOST_SWORD_NSKILLS-1);
+ char buf[80];
+ sprintf(buf, "Choose a skill to learn(a-%c to choose, ESC to cancel)?", last);
+ res = ask_menu(buf, (char **)items, LOST_SWORD_NSKILLS);
+
+ /* Ok ? lets learn ! */
+ if (res > -1)
+ {
+ skill_type *s_ptr;
+ bool_ oppose = FALSE;
+ int oppose_skill = -1;
+
+ /* Check we don't oppose an existing skill */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ if ((s_info[i].action[skl[res]] == SKILL_EXCLUSIVE) &&
+ (s_info[i].value != 0))
+ {
+ oppose = TRUE;
+ oppose_skill = i;
+ break;
+ }
+ }
+
+ /* Ok we oppose, so be sure */
+ if (oppose)
+ {
+ cptr msg;
+
+ /*
+ * Because this is SO critical a question, we must flush
+ * input to prevent killing character off -- pelpel
+ */
+ flush();
+
+ /* Prepare prompt */
+ msg = format("This skill is mutually exclusive with "
+ "at least %s, continue?",
+ s_info[oppose_skill].name + s_name);
+
+ /* The player rejected the choice */
+ if (!get_check(msg)) continue;
+ }
+
+ s_ptr = &s_info[skl[res]];
+ s_ptr->value += val[res];
+ s_ptr->mod += mod[res];
+ if (mod[res])
+ {
+ msg_format("You can now learn the %s skill.",
+ s_ptr->name + s_name);
+ }
+ else
+ {
+ msg_format("Your knowledge of the %s skill increases.",
+ s_ptr->name + s_name);
+ }
+ break;
+ }
+ }
+
+ /* Free them ! */
+ for (max = 0; max < LOST_SWORD_NSKILLS; max++)
+ {
+ string_free(items[max]);
+ }
+
+ /* Check if some skills didn't influence other stuff */
+ recalc_skills(FALSE);
+}
+
+
+
+
+/**************************************** ABILITIES *****************************************/
+
+/*
+ * Given the name of an ability, returns ability index or -1 if no
+ * such ability is found
+ */
+s16b find_ability(cptr name)
+{
+ u16b i;
+
+ /* Scan ability list */
+ for (i = 0; i < max_ab_idx; i++)
+ {
+ /* The name matches */
+ if (streq(ab_info[i].name + ab_name, name)) return (i);
+ }
+
+ /* No match found */
+ return ( -1);
+}
+
+/*
+ * Do the player have the ability
+ */
+bool_ has_ability(int ab)
+{
+ return ab_info[ab].acquired;
+}
+
+/* Do we meet the requirements */
+bool_ can_learn_ability(int ab)
+{
+ ability_type *ab_ptr = &ab_info[ab];
+ int i;
+
+ if (ab_ptr->acquired)
+ return FALSE;
+
+ if (p_ptr->skill_points < ab_info[ab].cost)
+ return FALSE;
+
+ for (i = 0; i < 10; i++)
+ {
+ /* Must have skill level */
+ if (ab_ptr->skills[i] > -1)
+ {
+ if (get_skill(ab_ptr->skills[i]) < ab_ptr->skill_levels[i])
+ return FALSE;
+ }
+
+ /* Must have ability */
+ if (ab_ptr->need_abilities[i] > -1)
+ {
+ if (!ab_info[ab_ptr->need_abilities[i]].acquired)
+ return FALSE;
+ }
+
+ /* Must not have ability */
+ if (ab_ptr->forbid_abilities[i] > -1)
+ {
+ if (ab_info[ab_ptr->forbid_abilities[i]].acquired)
+ return FALSE;
+ }
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ /* Must have stat */
+ if (ab_ptr->stat[i] > -1)
+ {
+ if (p_ptr->stat_ind[i] < ab_ptr->stat[i] - 3)
+ return FALSE;
+ }
+ }
+
+ /* Do the script allow us? */
+ if (process_hooks(HOOK_LEARN_ABILITY, "(d)", ab))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Learn an ability */
+void gain_ability(int ab)
+{
+ int wid, hgt;
+ Term_get_size(&wid, &hgt);
+
+ if (!can_learn_ability(ab))
+ {
+ msg_box("You cannot learn this ability.", (int)(hgt / 2), (int)(wid / 2));
+ return;
+ }
+
+ /* Flush input as we ask an important and irreversible question */
+ flush();
+
+ /* Ask we can commit the change */
+ if (msg_box("Learn this ability(this is permanent)? (y/n)", (int)(hgt / 2), (int)(wid / 2)) != 'y')
+ {
+ return;
+ }
+
+ ab_info[ab].acquired = TRUE;
+ p_ptr->skill_points -= ab_info[ab].cost;
+}
+
+/* helper function to generate a sorted table */
+static void add_sorted_ability(int *table, int *max, int ab)
+{
+ int i;
+
+ for (i = 0; i < *max; i++)
+ {
+ if (strcmp(ab_name + ab_info[ab].name, ab_name + ab_info[table[i]].name) < 0)
+ {
+ int z;
+
+ /* Move all indexes up */
+ for (z = *max; z > i; z--)
+ {
+ table[z] = table[z - 1];
+ }
+ break;
+ }
+ }
+ table[i] = ab;
+
+ (*max)++;
+}
+
+/*
+ * Print the abilities list
+ */
+void dump_abilities(FILE *fff)
+{
+ int i, j;
+ int *table;
+ int max = 0;
+
+ C_MAKE(table, max_ab_idx, int);
+
+ /* Initialise the abilities list */
+ for (i = 0; i < max_ab_idx; i++)
+ {
+ if (ab_info[i].name && has_ability(i))
+ add_sorted_ability(table, &max, i);
+ }
+
+ if (max)
+ {
+ fprintf(fff, "\nAbilities");
+
+ for (j = 0; j < max; j++)
+ {
+ i = table[j];
+
+ fprintf(fff, "\n * %s", ab_info[i].name + ab_name);
+ }
+
+ fprintf(fff, "\n");
+ }
+}
+
+/*
+ * Draw the abilities list
+ */
+void print_abilities(int table[], int max, int sel, int start)
+{
+ int i, j;
+ int wid, hgt;
+ cptr keys;
+
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ c_prt(TERM_WHITE, format("%s Abilities Screen", game_module), 0, 28);
+ keys = format("#Bup#W/#Bdown#W to move, #Bright#W to buy, #B?#W for help");
+ display_message(0, 1, strlen(keys), TERM_WHITE, keys);
+ c_prt((p_ptr->skill_points) ? TERM_L_BLUE : TERM_L_RED,
+ format("Skill points left: %d", p_ptr->skill_points), 2, 0);
+
+ print_desc_aux(ab_info[table[sel]].desc + ab_text, 3, 0);
+
+ for (j = start; j < start + (hgt - 7); j++)
+ {
+ byte color = TERM_WHITE;
+ char deb = ' ', end = ' ';
+
+ if (j >= max) break;
+
+ i = table[j];
+
+ if (ab_info[i].acquired)
+ color = TERM_L_BLUE;
+ else if (can_learn_ability(i))
+ color = TERM_WHITE;
+ else
+ color = TERM_L_DARK;
+
+
+ if (j == sel)
+ {
+ color = TERM_L_GREEN;
+ deb = '[';
+ end = ']';
+ }
+
+ c_prt(color, format("%c.%c%s", deb, end, ab_info[i].name + ab_name),
+ j + 7 - start, 0);
+
+ if (!ab_info[i].acquired)
+ {
+ c_prt(color, format("%d", ab_info[i].cost), j + 7 - start, 60);
+ }
+ else
+ {
+ c_prt(color, "Known", j + 7 - start, 60);
+ }
+ }
+}
+
+/*
+ * Interreact with abilitiess
+ */
+void do_cmd_ability()
+{
+ int sel = 0, start = 0, max = 0;
+ char c;
+ int *table;
+ int i;
+ int wid, hgt;
+
+ C_MAKE(table, max_ab_idx, int);
+
+ /* Save the screen */
+ screen_save();
+
+ /* Clear the screen */
+ Term_clear();
+
+ /* Initialise the abilities list */
+ for (i = 0; i < max_ab_idx; i++)
+ {
+if (ab_info[i].name)
+ add_sorted_ability(table, &max, i);
+ }
+
+ while (TRUE)
+ {
+ Term_get_size(&wid, &hgt);
+
+ /* Display list of skills */
+ print_abilities(table, max, sel, start);
+
+ /* Wait for user input */
+ c = inkey();
+
+ /* Leave the skill screen */
+ if (c == ESCAPE) break;
+
+ /* Next page */
+ else if (c == 'n')
+ {
+ sel += (hgt - 7);
+ if (sel >= max) sel = max - 1;
+ }
+
+ /* Previous page */
+ else if (c == 'p')
+ {
+ sel -= (hgt - 7);
+ if (sel < 0) sel = 0;
+ }
+
+ /* Select / increase a skill */
+ else
+ {
+ int dir;
+
+ /* Allow use of numpad / arrow keys / roguelike keys */
+ dir = get_keymap_dir(c);
+
+ /* Move cursor down */
+ if (dir == 2) sel++;
+
+ /* Move cursor up */
+ if (dir == 8) sel--;
+
+ /* gain ability */
+ if (dir == 6) gain_ability(table[sel]);
+
+ /* XXX XXX XXX Wizard mode commands outside of wizard2.c */
+
+ if (wizard && (c == '+')) ab_info[table[sel]].acquired = TRUE;
+ if (wizard && (c == '-')) ab_info[table[sel]].acquired = FALSE;
+
+ /* Contextual help */
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'ability', '%s')", ab_info[table[sel]].name + ab_name));
+ ;
+
+ /* Handle boundaries and scrolling */
+ if (sel < 0) sel = max - 1;
+ if (sel >= max) sel = 0;
+ if (sel < start) start = sel;
+ if (sel >= start + (hgt - 7)) start = sel - (hgt - 7) + 1;
+ }
+ }
+
+ /* Load the screen */
+ screen_load();
+
+ C_FREE(table, max_ab_idx, int);
+
+ /* Update stuffs */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ /* Redraw various info */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+}
+
+/*
+ * Apply abilities to be granted this level
+ */
+void apply_level_abilities(int level)
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ if (cp_ptr->abilities[i].level == level)
+ {
+ if ((level > 1) && (!ab_info[cp_ptr->abilities[i].ability].acquired))
+ cmsg_format(TERM_L_GREEN, "You have learned the ability '%s'.", ab_name + ab_info[cp_ptr->abilities[i].ability].name);
+ ab_info[cp_ptr->abilities[i].ability].acquired = TRUE;
+ }
+ if (spp_ptr->abilities[i].level == level)
+ {
+ if ((level > 1) && (!ab_info[spp_ptr->abilities[i].ability].acquired))
+ cmsg_format(TERM_L_GREEN, "You have learned the ability '%s'.", ab_name + ab_info[spp_ptr->abilities[i].ability].name);
+ ab_info[spp_ptr->abilities[i].ability].acquired = TRUE;
+ }
+ if (rp_ptr->abilities[i].level == level)
+ {
+ if ((level > 1) && (!ab_info[rp_ptr->abilities[i].ability].acquired))
+ cmsg_format(TERM_L_GREEN, "You have learned the ability '%s'.", ab_name + ab_info[rp_ptr->abilities[i].ability].name);
+ ab_info[rp_ptr->abilities[i].ability].acquired = TRUE;
+ }
+ if (rmp_ptr->abilities[i].level == level)
+ {
+ if ((level > 1) && (!ab_info[rmp_ptr->abilities[i].ability].acquired))
+ cmsg_format(TERM_L_GREEN, "You have learned the ability '%s'.", ab_name + ab_info[rmp_ptr->abilities[i].ability].name);
+ ab_info[rmp_ptr->abilities[i].ability].acquired = TRUE;
+ }
+ }
+}
diff --git a/src/spells.pkg b/src/spells.pkg
new file mode 100644
index 00000000..e785de0d
--- /dev/null
+++ b/src/spells.pkg
@@ -0,0 +1,2448 @@
+/* File: spells.pkg */
+
+/*
+ * Purpose: Lua interface defitions for spells.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+$#include "lua.h"
+
+/** @typedef *mcptr
+ * @note String
+ */
+typedef char *mcptr;
+
+/** @typedef cptr
+ * @note String
+ */
+typedef const char* cptr;
+
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @def DEFAULT_RADIUS */
+#define DEFAULT_RADIUS 25
+
+/** @name Spell Damage Types
+ * @brief Type of damage caused by spell
+ * @{ */
+/** @def GF_ELEC */
+#define GF_ELEC 1
+
+/** @def GF_POIS */
+#define GF_POIS 2
+
+/** @def GF_ACID */
+#define GF_ACID 3
+
+/** @def GF_COLD */
+#define GF_COLD 4
+
+/** @def GF_FIRE */
+#define GF_FIRE 5
+
+/** @def GF_UNBREATH */
+#define GF_UNBREATH 6
+
+/** @def GF_CORPSE_EXPL */
+#define GF_CORPSE_EXPL 7
+
+/** @def GF_MISSILE */
+#define GF_MISSILE 10
+
+/** @def GF_ARROW */
+#define GF_ARROW 11
+
+/** @def GF_PLASMA */
+#define GF_PLASMA 12
+
+/** @def GF_WAVE */
+#define GF_WAVE 13
+
+/** @def GF_WATER */
+#define GF_WATER 14
+
+/** @def GF_LITE */
+#define GF_LITE 15
+
+/** @def GF_DARK */
+#define GF_DARK 16
+
+/** @def GF_LITE_WEAK */
+#define GF_LITE_WEAK 17
+
+/** @def GF_DARK_WEAK */
+#define GF_DARK_WEAK 18
+
+/** @def GF_SHARDS */
+#define GF_SHARDS 20
+
+/** @def GF_SOUND */
+#define GF_SOUND 21
+
+/** @def GF_CONFUSION */
+#define GF_CONFUSION 22
+
+/** @def GF_FORCE */
+#define GF_FORCE 23
+
+/** @def GF_INERTIA */
+#define GF_INERTIA 24
+
+/** @def GF_MANA */
+#define GF_MANA 26
+
+/** @def GF_METEOR */
+#define GF_METEOR 27
+
+/** @def GF_ICE */
+#define GF_ICE 28
+
+/** @def GF_CHAOS */
+#define GF_CHAOS 30
+
+/** @def GF_NETHER */
+#define GF_NETHER 31
+
+/** @def GF_DISENCHANT */
+#define GF_DISENCHANT 32
+
+/** @def GF_NEXUS */
+#define GF_NEXUS 33
+
+/** @def GF_TIME */
+#define GF_TIME 34
+
+/** @def GF_GRAVITY */
+#define GF_GRAVITY 35
+
+/** @def GF_KILL_WALL */
+#define GF_KILL_WALL 40
+
+/** @def GF_KILL_DOOR */
+#define GF_KILL_DOOR 41
+
+/** @def GF_KILL_TRAP */
+#define GF_KILL_TRAP 42
+
+/** @def GF_MAKE_WALL */
+#define GF_MAKE_WALL 45
+
+/** @def GF_MAKE_DOOR */
+#define GF_MAKE_DOOR 46
+
+/** @def GF_MAKE_TRAP */
+#define GF_MAKE_TRAP 47
+
+/** @def GF_OLD_CLONE */
+#define GF_OLD_CLONE 51
+
+/** @def GF_OLD_POLY */
+#define GF_OLD_POLY 52
+
+/** @def GF_OLD_HEAL */
+#define GF_OLD_HEAL 53
+
+/** @def GF_OLD_SPEED */
+#define GF_OLD_SPEED 54
+
+/** @def GF_OLD_SLOW */
+#define GF_OLD_SLOW 55
+
+/** @def GF_OLD_CONF */
+#define GF_OLD_CONF 56
+
+/** @def GF_OLD_SLEEP */
+#define GF_OLD_SLEEP 57
+
+/** @def GF_OLD_DRAIN */
+#define GF_OLD_DRAIN 58
+
+/** @def GF_AWAY_UNDEAD */
+#define GF_AWAY_UNDEAD 61
+
+/** @def GF_AWAY_EVIL */
+#define GF_AWAY_EVIL 62
+
+/** @def GF_AWAY_ALL */
+#define GF_AWAY_ALL 63
+
+/** @def GF_TURN_UNDEAD */
+#define GF_TURN_UNDEAD 64
+
+/** @def GF_TURN_EVIL */
+#define GF_TURN_EVIL 65
+
+/** @def GF_TURN_ALL */
+#define GF_TURN_ALL 66
+
+/** @def GF_DISP_UNDEAD */
+#define GF_DISP_UNDEAD 67
+
+/** @def GF_DISP_EVIL */
+#define GF_DISP_EVIL 68
+
+/** @def GF_DISP_ALL */
+#define GF_DISP_ALL 69
+
+/* New types for Zangband begin here... */
+
+/** @def GF_DISP_DEMON */
+#define GF_DISP_DEMON 70
+
+/** @def GF_DISP_LIVING */
+#define GF_DISP_LIVING 71
+
+/** @def GF_ROCKET */
+#define GF_ROCKET 72
+
+/** @def GF_NUKE */
+#define GF_NUKE 73
+
+/** @def GF_MAKE_GLYPH */
+#define GF_MAKE_GLYPH 74
+
+/** @def GF_STASIS */
+#define GF_STASIS 75
+
+/** @def GF_STONE_WALL */
+#define GF_STONE_WALL 76
+
+/** @def GF_DEATH_RAY */
+#define GF_DEATH_RAY 77
+
+/** @def GF_STUN */
+#define GF_STUN 78
+
+/** @def GF_HOLY_FIRE */
+#define GF_HOLY_FIRE 79
+
+/** @def GF_HELL_FIRE */
+#define GF_HELL_FIRE 80
+
+/** @def GF_DISINTEGRATE */
+#define GF_DISINTEGRATE 81
+
+/** @def GF_CHARM */
+#define GF_CHARM 82
+
+/** @def GF_CONTROL_UNDEAD */
+#define GF_CONTROL_UNDEAD 83
+
+/** @def GF_CONTROL_ANIMAL */
+#define GF_CONTROL_ANIMAL 84
+
+/** @def GF_PSI */
+#define GF_PSI 85
+
+/** @def GF_PSI_DRAIN */
+#define GF_PSI_DRAIN 86
+
+/** @def GF_TELEKINESIS */
+#define GF_TELEKINESIS 87
+
+/** @def GF_JAM_DOOR */
+#define GF_JAM_DOOR 88
+
+/** @def GF_DOMINATION */
+#define GF_DOMINATION 89
+
+/** @def GF_DISP_GOOD */
+#define GF_DISP_GOOD 90
+
+/** @def GF_IDENTIFY */
+#define GF_IDENTIFY 91
+
+/** @def GF_RAISE */
+#define GF_RAISE 92
+
+/** @def GF_STAR_IDENTIFY */
+#define GF_STAR_IDENTIFY 93
+
+/** @def GF_DESTRUCTION */
+#define GF_DESTRUCTION 94
+
+/** @def GF_STUN_CONF */
+#define GF_STUN_CONF 95
+
+/** @def GF_STUN_DAM */
+#define GF_STUN_DAM 96
+
+/** @def GF_CONF_DAM */
+#define GF_CONF_DAM 98
+
+/** @def GF_STAR_CHARM */
+#define GF_STAR_CHARM 99
+
+/** @def GF_IMPLOSION */
+#define GF_IMPLOSION 100
+
+/** @def GF_LAVA_FLOW */
+#define GF_LAVA_FLOW 101
+
+/** @def GF_FEAR */
+#define GF_FEAR 102
+
+/** @def GF_BETWEEN_GATE */
+#define GF_BETWEEN_GATE 103
+
+/** @def GF_WINDS_MANA */
+#define GF_WINDS_MANA 104
+
+/** @def GF_DEATH */
+#define GF_DEATH 105
+
+/** @def GF_CONTROL_DEMON */
+#define GF_CONTROL_DEMON 106
+
+/** @def GF_RAISE_DEMON */
+#define GF_RAISE_DEMON 107
+
+/** @def GF_TRAP_DEMONSOUL */
+#define GF_TRAP_DEMONSOUL 108
+
+/** @def GF_ATTACK */
+#define GF_ATTACK 109
+
+/** @def GF_CHARM_UNMOVING */
+#define GF_CHARM_UNMOVING 110
+
+/** @def MAX_GF */
+#define MAX_GF 111
+/** @} */
+
+/** @name Spell Projection Flags
+ * @brief Area affected by spell
+ * @{ */
+/** @def PROJECT_JUMP
+ * @note Jump directly to the target location (this is a hack)
+ */
+#define PROJECT_JUMP 0x00000001
+
+/** @def PROJECT_BEAM
+ * @note Work as a beam weapon (affect every grid passed through)
+ */
+#define PROJECT_BEAM 0x00000002
+
+/** @def PROJECT_THRU
+ * @note Continue "through" the target (used for "bolts"/"beams")
+ */
+#define PROJECT_THRU 0x00000004
+
+/** @def PROJECT_STOP
+ * @note Stop as soon as we hit a monster (used for "bolts")
+ */
+#define PROJECT_STOP 0x00000008
+
+/** @def PROJECT_GRID
+ * @note Affect each grid in the "blast area" in some way
+ */
+#define PROJECT_GRID 0x00000010
+
+/** @def PROJECT_ITEM
+ * @note Affect each object in the "blast area" in some way
+ */
+#define PROJECT_ITEM 0x00000020
+
+/** @def PROJECT_KILL
+ * @note Affect each monster in the "blast area" in some way
+ */
+#define PROJECT_KILL 0x00000040
+
+/** @def PROJECT_HIDE
+ * @note Hack -- disable "visual" feedback from projection
+ */
+#define PROJECT_HIDE 0x00000080
+
+/** @def PROJECT_VIEWABLE
+ * @note Affect monsters in LOS
+ */
+#define PROJECT_VIEWABLE 0x00000100
+
+/** @def PROJECT_METEOR_SHOWER
+ * @note Affect random grids
+ */
+#define PROJECT_METEOR_SHOWER 0x00000200
+
+/** @def PROJECT_BLAST
+ * @note Like Mega_blast, but will only affect viewable grids
+ */
+#define PROJECT_BLAST 0x00000400
+
+/** @def PROJECT_PANEL
+ * @note Affect everything in the panel.
+ */
+#define PROJECT_PANEL 0x00000800
+
+/** @def PROJECT_ALL
+ * @note Affect every single grid.
+ */
+#define PROJECT_ALL 0x00001000
+
+/** @def PROJECT_WALL
+ * @note Continue "through" the walls
+ */
+#define PROJECT_WALL 0x00002000
+
+/** @def PROJECT_MANA_PATH
+ * @note Follow a mana path.
+ */
+#define PROJECT_MANA_PATH 0x00004000
+
+/** @def PROJECT_ABSORB_MANA
+ * @note The spell increase in power as it absord grid's mana.
+ */
+#define PROJECT_ABSORB_MANA 0x00008000
+
+/** @def PROJECT_STAY */
+#define PROJECT_STAY 0x00010000
+/** @} */
+
+/** @var project_time
+ * @brief Number
+ * @note The length of time a spell effect exists.
+ */
+extern int project_time;
+
+/** @fn teleport_player_directed(int rad, int dir)
+ * @brief Teleport a player up to "rad" grids away roughly in "dir"
+ * direction.\n
+ * @param rad Number \n rad must not exceed 200. The distance teleported is
+ * at least a quarter of rad.
+ * @brief Distance
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @note
+ * Teleport player, using a distance and a direction as a rough guide.\n\n
+ * This function is not at all obsessive about correctness.\n
+ * This function allows teleporting into vaults (!)
+ * @note (see file spells1.c)
+ */
+extern void teleport_player_directed(int rad, int dir);
+
+/** @fn teleport_away(int m_idx, int dis)
+ * @brief Teleport monster indicated by "m_idx" up to "dis" grids away.\n
+ * @param m_idx Number \n m_idx is the index of the monster in m_list[].
+ * @brief Monster index
+ * @param dis Number \n dis must not exceed 200. The distance teleported
+ * is a minimum of a quarter of "dis".
+ * @brief Distance
+ * @note
+ * Teleport a monster, normally up to "dis" grids away.\n\n
+ * Attempt to move the monster at least "dis/2" grids away.\n\n
+ * But allow variation to prevent infinite loops.
+ * @note (see file spells1.c)
+ */
+extern void teleport_away(int m_idx, int dis);
+
+/** @fn teleport_player(int dis)
+ * @brief Teleport player up to "dis" grids away.\n
+ * @param dis Number \n dis must not exceed 200. The distance teleported
+ * is a minimum of a quarter of dis.
+ * @brief Distance
+ * @note
+ * Teleport the player to a location up to "dis" grids away.\n\n
+ * If no such spaces are readily available, the distance may increase.\n
+ * Try very hard to move the player at least a quarter that distance.
+ * @note (see file spells1.c)
+ */
+extern void teleport_player(int dis);
+
+/** @fn teleport_player_to(int ny, int nx)
+ * @brief Teleport player to a grid near coordinate ("ny", "nx").\n
+ * @param ny Number \n ny is the y co-ordinate of the location.
+ * @brief Y coordinate
+ * @param nx Number \n nx is the x co-ordinate of the location.
+ * @brief X coordinate
+ * @note
+ * Teleport player to a grid near the given location\n\n
+ * This function is slightly obsessive about correctness.\n
+ * This function allows teleporting into vaults (!)\n\n
+ * If the location is empty, the player goes there, otherwise they go to
+ * a grid as close as possible to the location.
+ * @note (see file spells1.c)
+ */
+extern void teleport_player_to(int ny, int nx);
+
+/** @fn teleport_monster_to(int m_idx, int ny, int nx)
+ * @brief Teleport monster indicated by "m_idx" to a grid near coordinate
+ * ("ny", "nx").\n
+ * @param m_idx Number \n m_idx is the index of the monster in m_list[].
+ * @brief Monster index
+ * @param ny Number \n ny is the y co-ordinate of the location.
+ * @brief Y coordinate
+ * @param nx Number \n nx is the x co-ordinate of the location.
+ * @brief X coordinate
+ * @note
+ * Teleport a monster to a grid near the given location\n\n
+ * This function is slightly obsessive about correctness.\n\n
+ * If the location is empty, the monster goes there, otherwise they go to
+ * a grid as close as possible to the location.
+ * @note (see file spells1.c)
+ */
+extern void teleport_monster_to(int m_idx, int ny, int nx);
+
+/** @fn teleport_monster(int dir)
+ * @brief Teleport away all monsters in direction "dir".\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * All monsters in direction "dir" are teleported away and sustain
+ * MAX_SIGHT (20) x 5 damage.\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool teleport_monster(int dir);
+
+/** @fn teleport_player_level(void)
+ * @brief Teleport the player one level up or down at random.
+ * @note
+ * Teleport the player one level up or down (random when legal)
+ * @note (see file spells1.c)
+ */
+extern void teleport_player_level(void);
+
+/** @fn fetch(int dir, int wgt, bool require_los)
+ * @brief Fetch an item in direction "dir" with weight "wgt" possibly not in
+ * line of sight.\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param wgt Number \n maximum weight of object.
+ * @brief Weight
+ * @param require_los Boolean \n TRUE if line of sight is required, otherwise
+ * FALSE.
+ * @brief Require-line-of-sight flag
+ * @note
+ * Fetch an item (teleport it right underneath the caster)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * Fetch will fail if player is standing on something, or if the object is
+ * too far away, or if require_los is TRUE and player does not have line
+ * of sight to the object, or the object is too heavy. Otherwise the
+ * object appears at the player's feet (same grid as player).
+ * @note (see file cmd5.c)
+ */
+extern void fetch(int dir, int wgt, bool require_los);
+
+/** @fn recall_player(int d, int f)
+ * @brief Recall the player to town (if in dungeon) or dungeon (if in town).\n
+ * @param d Number \n Random time interval
+ * @brief Dice
+ * @param f Number \n Fixed time interval
+ * @brief Fixed
+ * @note (see file spells1.c)
+ */
+extern void recall_player(int d, int f);
+
+/** @fn take_hit(int damage, cptr kb_str)
+ * @brief Reduce player hit points by "damage" inflicted by "kb_str".\n
+ * @param damage Number \n damage is the number of hit points of damage.
+ * @brief Damage
+ * @param kb_str String \n kb_str describes what killed the player
+ * (in the event the player dies)
+ * @brief Killed by
+ * @note
+ * Decreases players hit points and sets death flag if necessary\n\n
+ * XXX XXX XXX Invulnerability needs to be changed into a "shield"\n\n
+ * XXX XXX XXX Hack -- this function allows the user to save (or quit)
+ * the game when he dies, since the "You die." message is shown before
+ * setting the player to "dead".
+ * @note (see file spells1.c)
+ */
+extern void take_hit(int damage, cptr kb_str);
+
+/** @fn take_sanity_hit(int damage, cptr hit_from)
+ * @brief Reduce player sanity points by "damage" inflicted by "hit_from".\n
+ * @param damage Number \n damage is the number of sanity points of damage.
+ * @brief Damage
+ * @param hit_from String \n hit_from describes what caused the damage.
+ * @brief Hit from
+ * @note
+ * Decrease player's sanity. This is a copy of the function above.\n\n
+ * Reduce the player's current sanity points by "damage" points. If the
+ * player dies, "hit_from" is used to record what the player was killed
+ * by (see high-score table).
+ * @note (see file spells1.c)
+ */
+extern void take_sanity_hit(int damage, cptr hit_from);
+
+/** @fn project(int who, int rad, int y, int x, int dam, int typ, int flg)
+ * @brief Generate a beam/bolt/ball with properties "flg" starting from "who"
+ * with a radius of "rad" at target grid "y,x" for "dam" points of "typ"
+ * damage.\n
+ * @param who Number \n who is > 0 (index of monster in m_list[]), < 0 and
+ * not -100 or -101 (player), -100 or -101 (trap).
+ * @brief Source
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-9 for a ball.
+ * @brief Radius
+ * @param y Number \n y is the y coordinate of the target grid.
+ * @brief Y-coordinate
+ * @param x Number \n x is the x co-ordinate of the target grid.
+ * @brief X-coordinate
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param flg Number \n flg is the projection effect (PROJECT field).
+ * @brief Properties flag
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Generic "beam"/"bolt"/"ball" projection routine.\n\n
+ * Input:\n
+ * who: Index of "source" monster (negative for "player")\n
+ * jk -- -2 for traps, only used with project_jump\n
+ * rad: Radius of explosion (0 = beam/bolt, 1 to 9 = ball)\n
+ * y,x: Target location (or location to travel "towards")\n
+ * dam: Base damage roll to apply to affected monsters (or player)\n
+ * typ: Type of damage to apply to monsters (and objects)\n
+ * flg: Extra bit flags (see PROJECT_xxxx in "defines.h")\n\n
+ * Return:\n
+ * TRUE if any "effects" of the projection were observed, else FALSE\n\n
+ * Allows a monster (or player) to project a beam/bolt/ball of a given kind
+ * towards a given location (optionally passing over the heads of interposing
+ * monsters), and have it do a given amount of damage to the monsters (and
+ * optionally objects) within the given radius of the final location.\n\n
+ * A "bolt" travels from source to target and affects only the target grid.\n
+ * A "beam" travels from source to target, affecting all grids passed through.\n
+ * A "ball" travels from source to the target, exploding at the target, and
+ * affecting everything within the given radius of the target location.\n\n
+ * Traditionally, a "bolt" does not affect anything on the ground, and does
+ * not pass over the heads of interposing monsters, much like a traditional
+ * missile, and will "stop" abruptly at the "target" even if no monster is
+ * positioned there, while a "ball", on the other hand, passes over the heads
+ * of monsters between the source and target, and affects everything except
+ * the source monster which lies within the final radius, while a "beam"
+ * affects every monster between the source and target, except for the casting
+ * monster (or player), and rarely affects things on the ground.\n\n
+ * Two special flags allow us to use this function in special ways, the
+ * "PROJECT_HIDE" flag allows us to perform "invisible" projections, while
+ * the "PROJECT_JUMP" flag allows us to affect a specific grid, without
+ * actually projecting from the source monster (or player).\n\n
+ * The player will only get "experience" for monsters killed by himself
+ * Unique monsters can only be destroyed by attacks from the player\n\n
+ * Only 256 grids can be affected per projection, limiting the effective
+ * "radius" of standard ball attacks to nine units (diameter nineteen).\n\n
+ * One can project in a given "direction" by combining PROJECT_THRU with small
+ * offsets to the initial location (see "line_spell()"), or by calculating
+ * "virtual targets" far away from the player.\n\n
+ * One can also use PROJECT_THRU to send a beam/bolt along an angled path,
+ * continuing until it actually hits something (useful for "stone to mud").\n\n
+ * Bolts and Beams explode INSIDE walls, so that they can destroy doors.\n\n
+ * Balls must explode BEFORE hitting walls, or they would affect monsters
+ * on both sides of a wall. Some bug reports indicate that this is still
+ * happening in 2.7.8 for Windows, though it appears to be impossible.\n\n
+ * We "pre-calculate" the blast area only in part for efficiency.
+ * More importantly, this lets us do "explosions" from the "inside" out.
+ * This results in a more logical distribution of "blast" treasure.
+ * It also produces a better (in my opinion) animation of the explosion.
+ * It could be (but is not) used to have the treasure dropped by monsters
+ * in the middle of the explosion fall "outwards", and then be damaged by
+ * the blast as it spreads outwards towards the treasure drop location.\n\n
+ * Walls and doors are included in the blast area, so that they can be
+ * "burned" or "melted" in later versions.\n\n
+ * This algorithm is intended to maximise simplicity, not necessarily
+ * efficiency, since this function is not a bottleneck in the code.\n\n
+ * We apply the blast effect from ground zero outwards, in several passes,
+ * first affecting features, then objects, then monsters, then the player.
+ * This allows walls to be removed before checking the object or monster
+ * in the wall, and protects objects which are dropped by monsters killed
+ * in the blast, and allows the player to see all affects before he is
+ * killed or teleported away. The semantics of this method are open to
+ * various interpretations, but they seem to work well in practice.\n\n
+ * We process the blast area from ground-zero outwards to allow for better
+ * distribution of treasure dropped by monsters, and because it provides a
+ * pleasing visual effect at low cost.\n\n
+ * Note that the damage done by "ball" explosions decreases with distance.
+ * This decrease is rapid, grids at radius "dist" take "1/dist" damage.\n\n
+ * Notice the "napalm" effect of "beam" weapons. First they "project" to
+ * the target, and then the damage "flows" along this beam of destruction.
+ * The damage at every grid is the same as at the "centre" of a "ball"
+ * explosion, since the "beam" grids are treated as if they ARE at the
+ * centre of a "ball" explosion.\n\n
+ * Currently, specifying "beam" plus "ball" means that locations which are
+ * covered by the initial "beam", and also covered by the final "ball", except
+ * for the final grid (the epicentre of the ball), will be "hit twice", once
+ * by the initial beam, and once by the exploding ball. For the grid right
+ * next to the epicentre, this results in 150% damage being done. The centre
+ * does not have this problem, for the same reason the final grid in a "beam"
+ * plus "bolt" does not -- it is explicitly removed. Simply removing "beam"
+ * grids which are covered by the "ball" will NOT work, as then they will
+ * receive LESS damage than they should. Do not combine "beam" with "ball".\n\n
+ * The array "gy[],gx[]" with current size "grids" is used to hold the
+ * collected locations of all grids in the "blast area" plus "beam path".\n\n
+ * Note the rather complex usage of the "gm[]" array. First, gm[0] is always
+ * zero. Second, for N>1, gm[N] is always the index (in gy[],gx[]) of the
+ * first blast grid (see above) with radius "N" from the blast centre. Note
+ * that only the first gm[1] grids in the blast area thus take full damage.
+ * Also, note that gm[rad+1] is always equal to "grids", which is the total
+ * number of blast grids.\n\n
+ * Note that once the projection is complete, (y2,x2) holds the final location
+ * of bolts/beams, and the "epicentre" of balls.\n\n
+ * Note also that "rad" specifies the "inclusive" radius of projection blast,
+ * so that a "rad" of "one" actually covers 5 or 9 grids, depending on the
+ * implementation of the "distance" function. Also, a bolt can be properly
+ * viewed as a "ball" with a "rad" of "zero".\n\n
+ * Note that if no "target" is reached before the beam/bolt/ball travels the
+ * maximum distance allowed (MAX_RANGE), no "blast" will be induced. This
+ * may be relevant even for bolts, since they have a "1x1" mini-blast.\n\n
+ * Note that for consistency, we "pretend" that the bolt actually takes "time"
+ * to move from point A to point B, even if the player cannot see part of the
+ * projection path. Note that in general, the player will *always* see part
+ * of the path, since it either starts at the player or ends on the player.\n\n
+ * Hack -- we assume that every "projection" is "self-illuminating".\n\n
+ * Hack -- when only a single monster is affected, we automatically track
+ * (and recall) that monster, unless "PROJECT_JUMP" is used.\n\n
+ * Note that all projections now "explode" at their final destination, even
+ * if they were being projected at a more distant destination. This means
+ * that "ball" spells will *always* explode.\n\n
+ * Note that we must call "handle_stuff()" after affecting terrain features
+ * in the blast radius, in case the "illumination" of the grid was changed,
+ * and "update_view()" and "update_monsters()" need to be called.
+ * @note (see file spells1.c)
+ */
+extern bool project(int who, int rad, int y, int x, int dam, int typ, int flg);
+
+/** @fn corrupt_player(void)
+ * @brief Swap two of the player's stats at random.
+ * @note (see file spells1.c)
+ */
+extern void corrupt_player(void);
+
+/** @fn grow_things(s16b type, int rad)
+ * @brief Grow "type" things within "rad" distance of the player.\n
+ * @param type Number \n type of thing to grow (FEAT field).
+ * @brief Type
+ * @param rad Number \n rad is the radius of the area where things may grow.
+ * @brief Radius
+ * @note
+ * Grow things\n\n
+ * Up to (rad * (rad + 11)) things can be grown around the player. The
+ * grids must support growth.
+ * @note (see file spells2.c)
+ */
+extern void grow_things(s16b type, int rad);
+
+/** @fn grow_grass(int rad)
+ * @brief Grow grass within "rad" distance of the player.\n
+ * @param rad Number \n rad is the radius of the area where grass may grow.
+ * @brief Radius
+ * @note
+ * Grow grass\n\n
+ * Up to (rad * (rad + 11)) grass can be grown around the player. The
+ * grids must support growth.
+ * @note (see file spells2.c)
+ */
+extern void grow_grass(int rad);
+
+/** @fn grow_trees(int rad)
+ * @brief Grow trees within "rad" distance of the player.\n
+ * @param rad Number \n rad is the radius of the area where trees may grow.
+ * @brief Radius
+ * @note
+ * Grow trees\n\n
+ * Up to (rad * (rad + 11)) trees can be grown around the player. The
+ * grids must support growth.
+ * @note (see file spells2.c)
+ */
+extern void grow_trees(int rad);
+
+/** @fn hp_player(int num)
+ * @brief Add "num" points to the player's current hit points.\n
+ * @param num Number \n num is the number of points to add.
+ * @brief Number
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Increase players hit points, notice effects\n\n
+ * The total can not exceed the maximum.
+ * @note (see file spells2.c)
+ */
+extern bool hp_player(int num);
+
+/** @fn heal_insanity(int val)
+ * @brief Add "val" points to the player's current sanity points.\n
+ * @param val Number \n val is the number of points to add.
+ * @brief Value
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Heal insanity.\n\n
+ * The total can not exceed the maximum.
+ * @note (see file spells2.c)
+ */
+extern bool heal_insanity(int val);
+
+/** @fn warding_glyph(void)
+ * @brief Place a glyph at the player's location.
+ * @note
+ * Leave a "glyph of warding" which prevents monster movement\n\n
+ * The location must be bare.
+ * @note (see file spells2.c)
+ */
+extern void warding_glyph(void);
+
+/** @fn explosive_rune(void)
+ * @brief Place a minor glyph (explosive rune) at the player's location.
+ * @note
+ * The location must be bare.
+ * @note (see file spells2.c)
+ */
+extern void explosive_rune(void);
+
+/** @fn do_dec_stat(int stat, int mode)
+ * @brief Attempt to reduce the player's "stat" statistic by a point.\n
+ * @param stat Number \n stat is the statistic
+ * @brief Statistic
+ * @param mode Number \n mode is the type of decrease: temporary, normal,
+ * or permanent
+ * @brief Mode
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Lose a "point"
+ * @note (see file spells2.c)
+ */
+extern bool do_dec_stat(int stat, int mode);
+
+/** @fn do_res_stat(int stat, bool full)
+ * @brief Restore the player's "stat" statistic.\n
+ * @param stat Number \n stat is the statistic.
+ * @brief Statistic
+ * @param full Boolean \n TRUE if full restore is required, otherwise FALSE.
+ * @brief Full restore flag
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Restore lost "points" in a stat
+ * @note (see file spells2.c)
+ */
+extern bool do_res_stat(int stat, bool full);
+
+/** @fn do_inc_stat(int stat)
+ * @brief Increase the player's "stat" statistic by a point.\n
+ * @param stat Number \n stat is the statistic.
+ * @brief Statistic
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Gain a "point" in a stat
+ * @note (see file spells2.c)
+ */
+extern bool do_inc_stat(int stat);
+
+/** @fn identify_pack(void)
+ * @brief Identify all items in the inventory.
+ * @note
+ * Identify everything being carried.\n
+ * Done by a potion of "self knowledge".
+ * @note (see file spells2.c)
+ */
+extern void identify_pack(void);
+
+/** @fn remove_curse(void)
+ * @brief Remove all curses except for heavy curses.
+ * @return Boolean \n TRUE if at least one item was uncursed, otherwise FALSE.
+ * @note
+ * Remove most curses\n\n
+ * There is a 1 in (55 - level) chance of reversing the curse effects for
+ * items which are not artifacts. For example, a Ring of Damage (-15) will
+ * become a Ring of Damage (+15).
+ * @note (see file spells2.c)
+ */
+extern bool remove_curse(void);
+
+/** @fn remove_all_curse(void)
+ * @brief Remove all curses including heavy curses.
+ * @return Boolean \n TRUE if at least one item was uncursed, otherwise FALSE.
+ * @note
+ * Remove all curses\n\n
+ * There is a 1 in (55 - level) chance of reversing the curse effects for
+ * items which are not artifacts. For example, a Ring of Damage (-15) will
+ * become a Ring of Damage (+15).
+ * @note (see file spells2.c)
+ */
+extern bool remove_all_curse(void);
+
+/** @fn restore_level(void)
+ * @brief Restore all drained experience points (if any).
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Restores any drained experience
+ * @note (see file spells2.c)
+ */
+extern bool restore_level(void);
+
+/** @fn self_knowledge(FILE *fff=NULL)
+ * @brief Show all attributes including racial powers, mutations, and
+ * equipment effects.\n
+ * @param *fff FILE \n write info to screen if fff is NULL,
+ * otherwise write info to file fff.
+ * @brief Output file
+ * @note
+ * self-knowledge... idea from nethack. Useful for determining powers and
+ * resistances of items. It saves the screen, clears it, then starts listing
+ * attributes, a screenful at a time. (There are a LOT of attributes to
+ * list. It will probably take 2 or 3 screens for a powerful character whose
+ * using several artifacts...) -CFT\n\n
+ * It is now a lot more efficient. -BEN-\n\n
+ * See also "identify_fully()".\n\n
+ * XXX XXX XXX Use the "show_file()" method, perhaps.
+ * @note (see file spells2.c)
+ */
+extern void self_knowledge(FILE *fff=NULL);
+
+/** @fn lose_all_info(void)
+ * @brief Forget about objects and the map.
+ * @return Boolean \n TRUE (always).
+ * @note
+ * Forget everything
+ * @note (see file spells2.c)
+ */
+extern bool lose_all_info(void);
+
+/** @fn detect_traps(int rad)
+ * @brief Detect all traps within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE (always).
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if traps are detected.
+ * @note (see file spells2.c)
+ */
+extern bool detect_traps(int rad);
+
+/** @fn detect_doors(int rad)
+ * @brief Detect all doors within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if doors were detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if doors are detected.
+ * @note (see file spells2.c)
+ */
+extern bool detect_doors(int rad);
+
+/** @fn detect_stairs(int rad)
+ * @brief Detect all exits within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if exits were detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if exits are detected. Exits can be stairs,
+ * shafts, and ways out.
+ * @note (see file spells2.c)
+ */
+extern bool detect_stairs(int rad);
+
+/** @fn detect_treasure(int rad)
+ * @brief Detect all buried treasure within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if buried treasure was detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if buried treasure is detected. Treasure can be
+ * buried in magma, quartz, or sandwall.
+ * @note (see file spells2.c)
+ */
+extern bool detect_treasure(int rad);
+
+/** @var hack_no_detect_message
+ * @brief Boolean
+ * @note Suppress messages generated by "detect" spells?
+ */
+extern bool hack_no_detect_message;
+
+/** @fn detect_objects_gold(int rad)
+ * @brief Detect all gold within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if gold was detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if gold is detected. Gold can be coins or mimics.
+ * Monsters of type "$" are detected but not shown or reported.
+ * @note (see file spells2.c)
+ */
+extern bool detect_objects_gold(int rad);
+
+/** @fn detect_objects_normal(int rad)
+ * @brief Detect all normal (not gold) items within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if normal items were detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if normal items are detected. Items include mimics.
+ * Monsters of type "!=?|" are detected but not shown or reported.
+ * @note (see file spells2.c)
+ */
+extern bool detect_objects_normal(int rad);
+
+/** @fn detect_objects_magic(int rad)
+ * @brief Detect all magic (not gold) items within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if magic items were detected, otherwise FALSE.
+ * @note
+ * This will light up all spaces with "magic" items, including artifacts,
+ * ego-items, potions, scrolls, books, rods, wands, staves, amulets, rings,
+ * and "enchanted" items of the "good" variety.\n\n
+ * It can probably be argued that this function is now too powerful.\n\n
+ * All grids within the radius are searched.\n
+ * A message is displayed if magic items are detected. Items include mimics.
+ * @note (see file spells2.c)
+ */
+extern bool detect_objects_magic(int rad);
+
+/** @fn detect_monsters_normal(int rad)
+ * @brief Detect all non-invisible monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if non-invisible monsters were detected,
+ * otherwise FALSE.
+ * @note
+ * A non-invisible monster is one which is visible, or one which is invisible
+ * but the player can see invisible monsters.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_normal(int rad);
+
+/** @fn detect_monsters_invis(int rad)
+ * @brief Detect all invisible monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if invisible monsters were detected,
+ * otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_invis(int rad);
+
+/** @fn detect_monsters_evil(int rad)
+ * @brief Detect all evil monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if evil monsters were detected, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_evil(int rad);
+
+/** @fn detect_monsters_good(int rad)
+ * @brief Detect all good monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if good monsters were detected, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_good(int rad);
+
+/** @fn detect_monsters_xxx(u32b match_flag, int rad)
+ * @brief Detect all monsters with flag "match_flag" within radius "rad" of the
+ * player.\n
+ * @param match_flag Number \n match_flag is the type of monster. It must be
+ * a RF3_ flag (see defines.h).
+ * @brief Match flag
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if monsters were detected, otherwise FALSE.
+ * @note
+ * A "generic" detect monsters routine, tagged to flags3\n\n
+ * This routine will work with ANY RF3 flag, but messages will only be
+ * printed if the following monsters are detected: demon, undead, good.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_xxx(u32b match_flag, int rad);
+
+/** @fn detect_monsters_string(cptr chars, int rad)
+ * @brief Detect all monsters whose monster symbol is in "chars" within
+ * radius "rad" of the player.\n
+ * @param chars String \n chars is the string of monster types. For
+ * available characters, see the "symbol" field of the graphics (G)
+ * line of r_info.txt.
+ * @brief Symbols
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if monsters were detected, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_string(cptr chars, int rad);
+
+/** @fn detect_monsters_nonliving(int rad)
+ * @brief Detect all nonliving monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if nonliving monsters were detected,
+ * otherwise FALSE.
+ * @note
+ * Detect all "nonliving", "undead" or "demonic" monsters on current panel\n\n
+ * Nonliving monsters are either RF3_NONLIVING, RF3_UNDEAD, or RF3_DEMON.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_nonliving(int rad);
+
+/** @fn detect_all(int rad)
+ * @brief Detect everything within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if something was detected, otherwise FALSE.
+ * @note
+ * Detect everything\n\n
+ * Detects traps, doors, stairs, treasure, gold objects, normal objects,
+ * invisible monsters, non-invisible monsters.
+ */
+extern bool detect_all(int rad);
+
+/** @fn stair_creation(void)
+ * @brief Create stairs at the player location
+ * @note
+ * This is not allowed if the grid is not empty, the player is not in a
+ * dungeon, the player is on a special level, the player is in an arena
+ * or quest. If the player is in the town or wilderness the stairs will
+ * go down. If the player is on a quest level or at the bottom of a
+ * dungeon, the stairs will go up. Otherwise there is an even chance the
+ * stairs will go up or down.
+ */
+extern void stair_creation(void);
+
+/** @fn tgt_pt (int *x=0, int *y=0)
+ * @brief Set a target point\n
+ * @param *x Number
+ * @brief X-coordinate
+ * @param *y Number
+ * @brief Y-coordinate
+ * @return *x Number \n X-coordinate of target.
+ * @return *y Number \n Y-coordinate of target.
+ * @return Boolean \n True if a target was selected, otherwise FALSE.
+ * @note
+ * Allow the user to move the cursor around the screen to select a target.
+ * The user must press the space key to set the target.
+ * @note (see file xtra2.c)
+ */
+extern bool tgt_pt (int *x=0, int *y=0);
+
+/** @fn wall_stone(int y, int x)
+ * @brief Create a stone wall at dungeon grid ("y", "x").\n
+ * @param y Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool wall_stone(int y, int x);
+
+/** @fn create_artifact(object_type *o_ptr, bool a_scroll, bool get_name)
+ * @brief Create an artifact from object "o_ptr".\n
+ * @param *o_ptr object_type \n object to become an artifact
+ * @brief Object
+ * @param a_scroll Boolean \n Is a scroll used to create the artifact?\n
+ * TRUE if the artifact is created by reading a scroll.
+ * @brief Use scroll?
+ * @param get_name Boolean \n Get a name for the artifact?\n
+ * TRUE if the artifact is to be named by the player (if a_scroll is true) or
+ * created randomly (a_scroll is false), or FALSE if an inscription is used.
+ * @brief Get name?
+ * @return *o_ptr object_type \n The artifact.
+ * @return Boolean \n TRUE (always).
+ * @note (see file randart.c)
+ */
+extern bool create_artifact(object_type *o_ptr, bool a_scroll, bool get_name);
+
+/** @fn wall_to_mud(int dir)
+ * @brief Cast a wall-to-mud spell in direction "dir".\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool wall_to_mud(int dir);
+
+/** @fn ident_spell(void)
+ * @brief Identify an object in the inventory (or on the floor).
+ * @return Boolean \n TRUE if object is identified, otherwise FALSE.
+ * @note
+ * Identify an object in the inventory (or on the floor).
+ * This routine does *not* automatically combine objects.
+ * @note (see file spells2.c)
+ */
+extern bool ident_spell(void);
+
+/** @fn identify_fully(void)
+ * @brief Fully "identify" an object in the inventory (or on the floor).
+ * @return Boolean \n TRUE if object is identified, otherwise FALSE.
+ * @note
+ * Fully "identify" an object in the inventory -BEN-
+ * @note (see file spells2.c)
+ */
+extern bool identify_fully(void);
+
+/** @fn recharge(int num)
+ * @brief Recharge an object in the inventory (or on the floor) with "num"
+ * power.\n
+ * @param num Number \n num is the power used in recharging. It is compared
+ * to the object's level to determine whether the item is recharged
+ * successfully or destroyed. If it is recharged, it also determines
+ * how many charges are added, or how much recharge time is reduced.
+ * @brief Power
+ * @return Boolean \n TRUE if something was recharged, otherwise FALSE.
+ * @note
+ * Recharge a wand/staff/rod from the pack or on the floor.\n
+ * This function has been rewritten in Oangband. -LM-\n\n
+ * Mage -- Recharge I --> recharge(90)\n
+ * Mage -- Recharge II --> recharge(150)\n
+ * Mage -- Recharge III --> recharge(220)\n\n
+ * Priest or Necromancer -- Recharge --> recharge(140)\n
+ * Scroll of recharging --> recharge(130)\n
+ * Scroll of *recharging* --> recharge(200)\n\n
+ * It is harder to recharge high level, and highly charged wands,
+ * staffs, and rods. The more wands in a stack, the more easily and
+ * strongly they recharge. Staffs, however, each get fewer charges if
+ * stacked.\n\n
+ * XXX XXX XXX Beware of "sliding index errors".
+ * @note (see file spells2.c)
+ */
+extern bool recharge(int num);
+
+/** @fn aggravate_monsters(int who)
+ * @brief Aggravate monsters, originating from "who".\n
+ * @param who Number \n who is the index of monster in m_list[]
+ * (1 if it is the player) which triggers the aggravation.
+ * @brief Source
+ * @note
+ * Wake up all monsters, and speed up "los" monsters.
+ * @note (see file spells2.c)
+ */
+extern void aggravate_monsters(int who);
+
+/** @fn genocide_aux(bool player_cast, char typ)
+ * @brief Genocide a monster race.\n
+ * @param player_cast Boolean \n player_cast is true if the player cast the
+ * spell so the player can take damage.
+ * @param typ Char \n typ is the letetr of the genocided monsters
+ * @return Boolean \n TRUE if genocide was cast, otherwise FALSE.
+ * @note
+ * Genocide will not work on DF2_NO_GENO dungeon levels, or on "fated to
+ * die" levels.
+ * The player gets 4 points of damage per monster genocided.
+ * @note (see file spells2.c)
+ */
+extern bool genocide_aux(bool player_cast, char typ);
+
+/** @fn genocide(bool player_cast)
+ * @brief Genocide a monster race.\n
+ * @param player_cast Boolean \n player_cast is true if the player cast the
+ * spell so the player can take damage.
+ * @brief Player cast spell?
+ * @return Boolean \n TRUE if genocide was cast, otherwise FALSE.
+ * @note
+ * Genocide will not work on DF2_NO_GENO dungeon levels, or on "fated to
+ * die" levels.
+ * The player gets 4 points of damage per monster genocided.
+ * @note (see file spells2.c)
+ */
+extern bool genocide(bool player_cast);
+
+/** @fn mass_genocide(bool player_cast)
+ * @brief Delete all nearby (non-unique) monsters.\n
+ * @param player_cast Boolean \n player_cast is true if the player cast the
+ * spell so the player can take damage.
+ * @brief Player cast spell?
+ * @return Boolean \n TRUE (always).
+ * @note
+ * Genocide will not work on DF2_NO_GENO dungeon levels, or on "fated to
+ * die" levels.\n
+ * The player gets 3 points of damage per monster genocided.
+ * @note (see file spells2.c)
+ */
+extern bool mass_genocide(bool player_cast);
+
+/** @fn probing(void)
+ * @brief Probe all nearby monsters.
+ * @return Boolean \n TRUE if probe was successful, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool probing(void);
+
+/** @fn banish_evil(int dist)
+ * @brief Banish nearby evil monsters doing "dist" points of GF_AWAY_EVIL
+ * damage.\n
+ * @param dist Number \n dist is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool banish_evil(int dist);
+
+/** @fn dispel_evil(int dam)
+ * @brief Dispel nearby evil monsters doing "dam" points of GF_DISP_EVIL
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_evil(int dam);
+
+/** @fn dispel_good(int dam)
+ * @brief Dispel nearby good monsters doing "dam" points of GF_DISP_GOOD
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_good(int dam);
+
+/** @fn dispel_undead(int dam)
+ * @brief Dispel nearby undead monsters doing "dam" points of GF_DISP_UNDEAD
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_undead(int dam);
+
+/** @fn dispel_monsters(int dam)
+ * @brief Dispel all nearby monsters doing "dam" points of GF_DISP_ALL
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_monsters(int dam);
+
+/** @fn dispel_living(int dam)
+ * @brief Dispel nearby living monsters doing "dam" points of GF_DISP_LIVING
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_living(int dam);
+
+/** @fn dispel_demons(int dam)
+ * @brief Dispel nearby demon monsters doing "dam" points of GF_DISP_DEMON
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_demons(int dam);
+
+/** @fn turn_undead(void)
+ * @brief Turn nearby undead monsters doing a point of GF_TURN_UNDEAD damage
+ * for each player level.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool turn_undead(void);
+
+/** @fn door_creation(void)
+ * @brief Create doors in all grids adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool door_creation(void);
+
+/** @fn trap_creation(void)
+ * @brief Create traps in all grids adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool trap_creation(void);
+
+/** @fn glyph_creation(void)
+ * @brief Create glyphs in all grids adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool glyph_creation(void);
+
+/** @fn wipe(int y1, int x1, int r)
+ * @brief Delete monsters and objects from an area of the dungeon centred at
+ * grid "y1,x1" for a radius "r".\n
+ * @param y1 Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x1 Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @param r Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @note
+ * Wipe -- Empties a part of the dungeon\n\n
+ * This does not work on special levels or quests. The player may become
+ * blinded. The player forgets the affected area and it becomes dark.
+ * All grids become floor.
+ * @note (see file spells2.c)
+ */
+extern void wipe(int y1, int x1, int r);
+
+/** @fn destroy_area(int y1, int x1, int r, bool full, bool bypass)
+ * @brief Delete monsters and objects from an area of the dungeon centred at
+ * grid "y1,x1" for a radius "r".\n
+ * @param y1 Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x1 Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @param r Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @param full Boolean \n unused
+ * @brief *Unused*
+ * @param bypass Boolean \n TRUE if quest levels are not destroyed, otherwise
+ * FALSE.
+ * @brief Exempt quest levels?
+ * @note
+ * The spell of destruction\n\n
+ * This spell "deletes" monsters (instead of "killing" them).\n\n
+ * Later we may use one function for both "destruction" and
+ * "earthquake" by using the "full" to select "destruction".\n\n
+ * This does not work on special levels. This does not work on quests if the
+ * bypass flag is set. The epicentre is NOT affected. The player may become
+ * blinded. The player forgets the affected area and it becomes dark. The
+ * grids can become granite, quartz, magma, or floor.
+ * @note (see file spells2.c)
+ */
+extern void destroy_area(int y1, int x1, int r, bool full, bool bypass);
+
+/** @fn earthquake(int cy, int cx, int r)
+ * @brief Create an earthquake centred on grid "cy,cx" with a radius of "r".\n
+ * @param cy Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param cx Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @param r Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @note
+ * Induce an "earthquake" of the given radius at the given location.\n\n
+ * This will turn some walls into floors and some floors into walls.\n\n
+ * The player will take damage and "jump" into a safe grid if possible,
+ * otherwise, he will "tunnel" through the rubble instantaneously.\n\n
+ * Monsters will take damage, and "jump" into a safe grid if possible,
+ * otherwise they will be "buried" in the rubble, disappearing from
+ * the level in the same way that they do when genocided.\n\n
+ * Note that thus the player and monsters (except eaters of walls and
+ * passers through walls) will never occupy the same grid as a wall.
+ * Note that as of now (2.7.8) no monster may occupy a "wall" grid, even
+ * for a single turn, unless that monster can pass_walls or kill_walls.
+ * This has allowed massive simplification of the "monster" code.\n\n
+ * This does not work on quest levels. The epicentre is NOT affected.
+ * Only about 15% of the grids are affected. The player takes 300 points
+ * of damage if they can't be moved to a safe grid, otherwise damage is
+ * from 10 to 40 points. The player forgets the affected area and it
+ * becomes dark. The grids can become granite, quartz, magma, or floor.
+ * @note (see file spells2.c)
+ */
+extern void earthquake(int cy, int cx, int r);
+
+/** @fn lite_room(int y1, int x1)
+ * @brief Lite room containing grid "y1,x1".\n
+ * @param y1 Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x1 Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @note
+ * Illuminate any room containing the given location.
+ * @note (see file spells2.c)
+ */
+extern void lite_room(int y1, int x1);
+
+/** @fn unlite_room(int y1, int x1)
+ * @brief Unlite room containing grid "y1,x1".\n
+ * @param y1 Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x1 Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @note
+ * Darken all rooms containing the given location.
+ * @note (see file spells2.c)
+ */
+extern void unlite_room(int y1, int x1);
+
+/** @fn lite_area(int dam, int rad)
+ * @brief Lite area around player of radius "rad" causing "dam" points of
+ * damage to monsters.
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is the radius of circle of lite.
+ * @brief Radius
+ * @return Boolean \n TRUE (always).
+ * @note
+ * Hack -- call light around the player\n
+ * Affect all monsters in the projection radius\n\n
+ * Generate a ball of spell type GF_LITE_WEAK.\n
+ * @note (see file spells2.c)
+ */
+extern bool lite_area(int dam, int rad);
+
+/** @fn unlite_area(int dam, int rad)
+ * @brief Unlite area around player of radius "rad" causing "dam" points of
+ * damage to monsters.
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is the radius of circle of lite.
+ * @brief Radius
+ * @return Boolean \n TRUE (always).
+ * @note
+ * Hack -- call darkness around the player\n
+ * Affect all monsters in the projection radius\n\n
+ * Generate a ball of spell type GF_DARK_WEAK.\n
+ * @note (see file spells2.c)
+ */
+extern bool unlite_area(int dam, int rad);
+
+/** @fn fire_ball_beam(int typ, int dir, int dam, int rad)
+ * @brief Generate a ball spell of type "typ" with radius "rad" aimed in
+ * direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a ball-beamed spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * Any radius >16 is treated as 16.
+ * @note (see file spells2.c)
+ */
+extern bool fire_ball_beam(int typ, int dir, int dam, int rad);
+
+/** @fn make_wish(void)
+ * @brief Allow the player to make a wish.
+ * @note (see file xtra2.c)
+ */
+extern void make_wish(void);
+
+/** @fn fire_wave(int typ, int dir, int dam, int rad, int time, s32b eff)
+ * @brief Generate a ball spell of type "typ" with radius "rad" and effect
+ * "eff" lasting "time" turns aimed in direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @param time Number \n time is the number of turns the spell lasts.
+ * @brief Duration
+ * @param eff Number \n eff is the spell effect (EFF field)
+ * @brief Effect
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a wave spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * Any radius >16 is treated as 16.
+ * @note (see file spells2.c)
+ */
+extern bool fire_wave(int typ, int dir, int dam, int rad, int time, s32b eff);
+
+/** @name Spell Effect Flags
+ * @brief Effect of spell
+ * @{ */
+/** @def EFF_WAVE
+ * @note A circle whose radius increase
+ */
+#define EFF_WAVE 0x00000001
+
+/** @def EFF_LAST
+ * @note The wave lasts
+ */
+#define EFF_LAST 0x00000002
+
+/** @def EFF_STORM
+ * @note the effect follows the player
+ */
+#define EFF_STORM 0x00000004
+
+/** @name Spell Effect Direction Flags
+ * @brief Direction of the spell
+ * @{ */
+#define EFF_DIR1 0x00000008 /* Directed effect */
+#define EFF_DIR2 0x00000010 /* Directed effect */
+#define EFF_DIR3 0x00000020 /* Directed effect */
+#define EFF_DIR4 0x00000040 /* Directed effect */
+#define EFF_DIR6 0x00000080 /* Directed effect */
+#define EFF_DIR7 0x00000100 /* Directed effect */
+#define EFF_DIR8 0x00000200 /* Directed effect */
+#define EFF_DIR9 0x00000400 /* Directed effect */
+/** @} */
+/** @} */
+
+/** @fn fire_cloud(int typ, int dir, int dam, int rad, int time)
+ * @brief Generate a ball spell of type "typ" with radius "rad" lasting
+ * "time" turns aimed in direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @param time Number \n time is the number of turns the spell lasts.
+ * @brief Duration
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a cloud spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * Any radius >16 is treated as 16.
+ * @note (see file spells2.c)
+ */
+extern bool fire_cloud(int typ, int dir, int dam, int rad, int time);
+
+/** @fn fire_wall(int typ, int dir, int dam, int time)
+ * @brief Generate a beam spell of type "typ" lasting "time" turns aimed in
+ * direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param time Number \n time is the number of turns the spell lasts.
+ * @brief Duration
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a persistant beam spell\n
+ * Pass through monsters, as a "beam"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool fire_wall(int typ, int dir, int dam, int time);
+
+/** @fn fire_ball(int typ, int dir, int dam, int rad)
+ * @brief Generate a ball spell of type "typ" with radius "rad" aimed in
+ * direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a ball spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool fire_ball(int typ, int dir, int dam, int rad);
+
+/** @fn fire_bolt(int typ, int dir, int dam)
+ * @brief Generate a bolt spell of type "typ" aimed in direction "dir"
+ * for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a bolt spell\n
+ * Stop if we hit a monster, as a "bolt"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool fire_bolt(int typ, int dir, int dam);
+
+/** @fn fire_beam(int typ, int dir, int dam)
+ * @brief Generate a beam spell of type "typ" aimed in direction "dir"
+ * for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a beam spell\n
+ * Pass through monsters, as a "beam"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool fire_beam(int typ, int dir, int dam);
+
+/** @fn fire_druid_ball(int typ, int dir, int dam, int rad)
+ * @brief Generate a druid ball spell of type "typ" with radius "rad" aimed in
+ * direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a druidistic ball spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * The spells follows a mana path\n\n
+ * WARNING: This routine has been deprecated.
+ * @note (see file spells2.c)
+ */
+extern bool fire_druid_ball(int typ, int dir, int dam, int rad);
+
+/** @fn fire_druid_bolt(int typ, int dir, int dam)
+ * @brief Generate a druid bolt spell of type "typ" aimed in direction "dir"
+ * for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a druidistic bolt spell\n
+ * Stop if we hit a monster, as a "bolt"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * The spells follows a mana path\n\n
+ * WARNING: This routine has been deprecated.
+ * @note (see file spells2.c)
+ */
+extern bool fire_druid_bolt(int typ, int dir, int dam);
+
+/** @fn fire_druid_beam(int typ, int dir, int dam)
+ * @brief Generate a druid beam spell of type "typ" aimed in direction "dir"
+ * for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a druidistic beam spell\n
+ * Pass through monsters, as a "beam"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * The spells follows a mana path\n\n
+ * WARNING: This routine has been deprecated.
+ * @note (see file spells2.c)
+ */
+extern bool fire_druid_beam(int typ, int dir, int dam);
+
+/** @fn fire_bolt_or_beam(int prob, int typ, int dir, int dam)
+ * @brief Generate a bolt spell of type "typ" aimed in direction "dir"
+ * for "dam" damage with "prob" percent chance of a beam.\n
+ * @param prob Number \n prob is the percentage chance the spell will be a
+ * beam instead of a bolt.
+ * @brief Beam probability percentage
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a bolt spell, or rarely, a beam spell\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * @note (see file spells2.c)
+ */
+extern bool fire_bolt_or_beam(int prob, int typ, int dir, int dam);
+
+/** @fn alchemy(void)
+ * @brief Turns an object into gold, gain some of its value in a shop
+ * @return Boolean \n TRUE if object turns to gold, otherwise FALSE.
+ * @note
+ * The player selects an object (and quantity if it applies) from the
+ * inventory or the floor and attempts to turn it into gold. If the
+ * price of the item is < 0 then the player gains nothing (fool's gold),
+ * otherwise the player gets a third of the price in gold. Artifacts are
+ * not affected.
+ * @note (see file spells2.c)
+ */
+extern bool alchemy(void);
+
+/** @fn alter_reality(void)
+ * @brief The player leaves the level immediately.
+ * @note (see file spells2.c)
+ */
+extern void alter_reality(void);
+
+/** @fn swap_position(int lty, int ltx)
+ * @brief Swap the position of the player with whatever is in grid "lty,ltx".\n
+ * @param lty Number \n Y-coordinate of target location.
+ * @brief Y-coordinate
+ * @param ltx Number \n X-coordinate of target location.
+ * @brief X-coordinate
+ * @note
+ * Player moves to target location. If there is a monster at the target
+ * location, it is moved to the player location. This is not allowed if
+ * the space-time continuum can not be disrupted.
+ * @note (see file spells2.c)
+ */
+extern void swap_position(int lty, int ltx);
+
+/** @fn teleport_swap(int dir)
+ * @brief Player swaps places with target in direction "dir".\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @note
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, the target is the grid adjacent to the player in direction
+ * "dir".\n\n
+ * The target must be a monster. It will not work if the space-time continuum
+ * can not be disrupted or if the monster resists teleportation.
+ * @note (see file spells2.c)
+ */
+extern void teleport_swap(int dir);
+
+/** @fn project_meteor(int radius, int typ, int dam, u32b flg)
+ * @brief Generate from "radius" to ("radius" x2) ball spells with properties
+ * "flg" of type "typ" for "dam" damage.\n
+ * @param radius Number \n rad is the minimum number of balls created.
+ * rad + randint("rad") balls are created.
+ * @brief Balls
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param flg Number \n flg is the projection effect (PROJECT field).
+ * @brief Properties flag
+ * @note
+ * Apply a "project()" a la meteor shower\n\n
+ * Each ball has a radius of 2 grids. Each target grid is within 5 grids of
+ * the player.
+ * @note (see file spells2.c)
+ */
+extern void project_meteor(int radius, int typ, int dam, u32b flg);
+
+/** @fn passwall(int dir, bool safe)
+ * @brief Move the player through walls in direction "dir", to a "safe"
+ * location.\n
+ * @param dir Number \n dir must be a value from 0 to 9. It can not be 5.
+ * @brief Direction
+ * @param safe Boolean \n TRUE if location must be a safe one, otherwise FALSE.
+ * @brief Safe location?
+ * @return Boolean \n TRUE if move was successful, otherwise FALSE.
+ * @note
+ * Send the player shooting through walls in the given direction until
+ * they reach a non-wall space, or a monster, or a permanent wall.\n\n
+ * If the player ends up in a wall, they take 10d8 damage and the wall is
+ * replaced by a floor.\n\n
+ * This does not work in the wilderness, on quest levels, or if teleport is
+ * not allowed. Stopping on monsters or inside vaults is not allowed.
+ * @note (see file spells2.c)
+ */
+extern bool passwall(int dir, bool safe);
+
+/** @fn project_hook(int typ, int dir, int dam, int flg)
+ * @brief Generate a bolt/beam with properties "flg" in direction "dir" for
+ * "dam" points of "typ" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param flg Number \n flg is the projection effect (PROJECT field).
+ * @brief Properties flag
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Hack -- apply a "projection()" in a direction (or at the target)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool project_hook(int typ, int dir, int dam, int flg);
+
+/** @fn wizard_lock(int dir)
+ * @brief Cast a wizard_lock spell in direction "dir".\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool wizard_lock(int dir);
+
+/** @fn reset_recall(bool no_trepas_max_depth)
+ * @brief Ask the user to set a recall level in a dungeon, possibly no
+ * deeper than maximum dungeon depth.\n
+ * @param no_trepas_max_depth Boolean \n TRUE if user can select maximum
+ * dungeon depth, FALSE if user can select up to player's maximum depth
+ * in the dungeon so far.
+ * @brief Allow maximum dungeon depth?
+ * @return Boolean \n TRUE of recall level was reset, otherwise FALSE.
+ * @note
+ * Ask the user for a dungeon and appropriate level within the dungeon.\n
+ * The user can not specify a dungeon the player has not gone to yet.\n
+ * If the depth is <1, reset fails. If depth is 99 or 100, the level is set
+ * to 98.
+ * @note (see file spells2.c)
+ */
+extern bool reset_recall(bool no_trepas_max_depth);
+
+/** @fn get_aim_dir(int *dp=0)
+ * @brief Get an aiming direction from the user and store it in "dp".\n
+ * @param *dp Number
+ * @brief Direction
+ * @return *dp Number \n Aiming direction.
+ * @return Boolean \n TRUE if a valid direction was returned, otherwise FALSE.
+ * @note
+ * Get an "aiming direction" from the user.\n\n
+ * The "dir" is loaded with 1,2,3,4,6,7,8,9 for "actual direction", and
+ * "0" for "current target", and "-1" for "entry aborted".\n\n
+ * Note that "Force Target", if set, will pre-empt user interaction,
+ * if there is a usable target already set.\n\n
+ * Note that confusion over-rides any (explicit?) user choice.
+ * @note (see file xtra2.c)
+ */
+extern bool get_aim_dir(int *dp=0);
+
+/** @fn get_rep_dir(int *dp=0)
+ * @brief Get a movement direction from the user and store it in "dp".\n
+ * @param *dp Number
+ * @brief Direction
+ * @return *dp Number \n Movement direction.
+ * @return Boolean \n TRUE if a valid direction was returned, otherwise FALSE.
+ * @note
+ * Request a "movement" direction (1,2,3,4,6,7,8,9) from the user,
+ * and place it into "command_dir", unless we already have one.\n\n
+ * This function should be used for all "repeatable" commands, such as
+ * run, walk, open, close, bash, disarm, spike, tunnel, etc, as well
+ * as all commands which must reference a grid adjacent to the player,
+ * and which may not reference the grid under the player. Note that,
+ * for example, it is no longer possible to "disarm" or "open" chests
+ * in the same grid as the player.\n\n
+ * Direction "5" is illegal and will (cleanly) abort the command.\n\n
+ * This function tracks and uses the "global direction", and uses
+ * that as the "desired direction", to which "confusion" is applied.
+ * @note (see file xtra2.c)
+ */
+extern bool get_rep_dir(int *dp=0);
+
+/** @fn project_los(int typ, int dam);
+ * @brief Generate a bolt/beam for "dam" points of "typ" damage to all
+ * viewable monsters in line of sight.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Apply a "project()" directly to all viewable monsters\n\n
+ * Note that affected monsters are NOT auto-tracked by this usage.
+ * @note (see file spells2.c)
+ */
+extern bool project_hack @ project_los(int typ, int dam);
+
+/** @fn map_area(void)
+ * @brief Map current area.
+ * @note
+ * Hack -- map the current panel (plus some) ala "magic mapping"\n\n
+ * Up to 10 grids above and below, and up to 20 grids either side of the
+ * panel are mapped.
+ * @note (see file cave.c)
+ */
+extern void map_area(void);
+
+/** @fn wiz_lite(void)
+ * @brief Lite level using "clairvoyance".
+ * @note
+ * This function "illuminates" every grid in the dungeon, memorizes all
+ * "objects", memorizes all grids as with magic mapping, and, under the
+ * standard option settings (view_perma_grids but not view_torch_grids)
+ * memorizes all floor grids too.\n\n
+ * Note that if "view_perma_grids" is not set, we do not memorize floor
+ * grids, since this would defeat the purpose of "view_perma_grids", not
+ * that anyone seems to play without this option.\n\n
+ * Note that if "view_torch_grids" is set, we do not memorize floor grids,
+ * since this would prevent the use of "view_torch_grids" as a method to
+ * keep track of what grids have been observed directly.
+ * @note (see file cave.c)
+ */
+extern void wiz_lite(void);
+
+/** @fn wiz_lite_extra(void)
+ * @brief Lite and memorize level.
+ * @note (see file cave.c)
+ */
+extern void wiz_lite_extra(void);
+
+/** @fn wiz_dark(void)
+ * @brief Forget all grids and objects.
+ * @note
+ * Forget the dungeon map (ala "Thinking of Maud...").
+ * @note (see file cave.c)
+ */
+extern void wiz_dark(void);
+
+/** @fn create_between_gate(int dist, int y, int x)
+ * @brief Create a between gate at grid "y,x" or at a target grid within
+ * distance "dist" of the player.\n
+ * @param dist Number \n dist is the maximum distance from the player of the
+ * between gate.
+ * @brief Distance
+ * @param y Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @note
+ * Creates a between gate\n\n
+ * This will fail if teleporting is not allowed on the level.\n\n
+ * If the coordinates are given, a between gate is created under the player
+ * and at the given coordinate.\n\n
+ * If there are no coordinates, a target is selected. The gate will not be
+ * created if the grid is not empty, or the grid is in a vault, or the grid
+ * is too far away. There is always a chance (1 in (Conveyance Skill *
+ * Conveyance Skill / 2)) the gate will not be created.
+ * @note (see file spells2.c)
+ */
+extern void create_between_gate(int dist, int y, int x);
+
+/** @fn destroy_doors_touch(void)
+ * @brief Destroy all doors adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool destroy_doors_touch(void);
+
+/** @fn destroy_traps_touch(void)
+ * @brief Destroy all traps adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool destroy_traps_touch(void);
+
+/** @struct magic_power
+ * @brief Innate powers
+ */
+struct magic_power
+{
+ /** @structvar min_lev
+ * @brief Number
+ */
+ int min_lev;
+
+ /** @structvar mana_cost
+ * @brief Number
+ */
+ int mana_cost;
+
+ /** @structvar fail
+ * @brief Number
+ */
+ int fail;
+
+ /** @structvar name
+ * @brief String
+ */
+ cptr name;
+
+ /** @structvar desc
+ * @brief String
+ */
+ cptr desc;
+};
+
+/** @fn get_magic_power(magic_power *m_ptr, int num);
+ * @dgonly
+ * @brief Get magic power number "num" from array "m_ptr" of magic powers.\n
+ * @param *m_ptr magic_power \n m_ptr is the array of magic powers.
+ * @brief Powers
+ * @param num Number \n num is the index to the array.
+ * @brief Index
+ * @return magic_power \n A magic power.
+ * @note
+ * Note: do not call this function.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern magic_power *grab_magic_power @ get_magic_power(magic_power *m_ptr, int num);
+
+/** @fn magic_power_sucess(magic_power *spell, int stat, char *oups_fct=NULL);
+ * @dgonly
+ * @brief Determine if using a magic power succeeds.\n
+ * @param *spell magic_power \n Spell is the magic power the player is using.
+ * @brief Power (spell)
+ * @param stat Number \n stat is the required casting statistic (INT or WIS).
+ * @brief Casting statistic
+ * @param *oups_fct String \n oups_fct is the message displayed when the power
+ * fails.
+ * @brief Fail message
+ * @return Boolean \n TRUE if spell succeeds, otherwise FALSE.
+ * @note
+ * The chance of using a power is adjusted for player magic skill, casting
+ * statistic, player mana points, and stunning. There is always at least a
+ * 5% chance the power works.\n\n
+ * Note: do not call this function.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern bool lua_spell_success @ magic_power_sucess(magic_power *spell, int stat, char *oups_fct=NULL);
+
+/** @fn add_new_power(cptr name, cptr desc, cptr gain, cptr lose, byte level, byte cost, byte stat, byte diff)
+ * @dgonly
+ * @brief Add a new power to the array of magic powers.\n
+ * @param name String \n name is the name of the power.
+ * @brief Name
+ * @param desc String \n desc is the description of the power.
+ * @brief Description
+ * @param gain String \n gain describes the effect when the power starts
+ * working.
+ * @brief Gain message
+ * @param lose String \n loss describes the effect when the power stops
+ * working.
+ * @brief Lose message
+ * @param level Number \n level is the magic skill level a player needs to
+ * use the power.
+ * @brief Level
+ * @param cost Number \n cost is the number of mana points required to use the
+ * power.
+ * @brief Mana cost
+ * @param stat Number \n stat is the required casting statistic (INT or WIS).
+ * @brief Casting statistic
+ * @param diff Number \n diff is the difficulty.
+ * @brief Difficulty
+ * @return Number \n The index of the new power in the magic power array.
+ * @note
+ * Note: do not call this function.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+s16b add_new_power(cptr name, cptr desc, cptr gain, cptr lose, byte level, byte cost, byte stat, byte diff);
+
+/** @var power_max
+ * @brief Number
+ * @note Maximum number of innate powers.
+ */
+extern s16b power_max;
+
+/* Schools */
+
+/** @struct school_spell_type
+ * @brief Spell
+ * @note The spell function must provide the desc
+ */
+struct spell_type@school_spell_type
+{
+ /** @structvar name
+ * @brief String
+ */
+ cptr name;
+
+ /** @structvar skill_level
+ * @brief Number
+ * @note Required level (to learn)
+ */
+ byte skill_level;
+
+ /** @structvar mana
+ * @brief Number
+ * @note Required mana at lvl 1
+ */
+ byte mana;
+
+ /** @structvar mana_max
+ * @brief Number
+ * @note Required mana at max lvl
+ */
+ byte mana_max;
+
+ /** @structvar fail
+ * @brief Number
+ * @note Minimum chance of failure
+ */
+ s16b fail;
+
+ /** @structvar level
+ * @brief Number
+ * @note Spell level(0 = not learnt)
+ */
+ s16b level;
+};
+
+/** @struct school_type
+ * @brief Spell school
+ */
+struct school_type
+{
+ /** @structvar name
+ * @brief String
+ * @note Name
+ */
+ cptr name;
+ /** @structvar skill
+ * @brief Number
+ * @note Skil used for that school
+ */
+ s16b skill;
+};
+
+/** @fn new_school(int i, cptr name, s16b skill)
+ * @dgonly
+ * @brief Add school to array of schools.\n
+ * @param i Number \n i is index of school array where school is added.
+ * There is no range checking.
+ * @brief Index
+ * @param name String \n name is the name of the school.
+ * @brief Name
+ * @param skill Number \n skill is the skill of the school.
+ * @brief Skill
+ * @return Number \ The index parameter.
+ * @note
+ * Note: do not call this function directly.\n
+ * Please use add_school() in s_aux.lua instead.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s16b new_school(int i, cptr name, s16b skill);
+
+/** @fn new_spell(int i, cptr name)
+ * @dgonly
+ * @brief Add spell to array of spells for a school.\n
+ * @param i Number \n i is index of school spell array where spell is added.
+ * There is no range checking.
+ * @brief Index
+ * @param name String \n name is the name of the spell.
+ * @brief Name
+ * @return Number \ The index parameter.
+ * @note
+ * Spell level is set to zero.\n\n
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s16b new_spell(int i, cptr name);
+
+/** @fn spell(s16b num);
+ * @dgonly
+ * @brief Get spell "num" from array of spells for a school.\n
+ * @param num Number \n num is the index of the spell.
+ * There is no range checking.
+ * @brief Index
+ * @return spell_type \n The spell.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern spell_type *grab_spell_type @ spell(s16b num);
+
+/** @fn school(s16b num);
+ * @dgonly
+ * @brief Get school "num" from array of schools.\n
+ * @param num Number \n num is the index of the school.
+ * There is no range checking.
+ * @brief Index
+ * @return school_type \n The school.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern school_type *grab_school_type @ school(s16b num);
+
+/** @fn lua_get_level(s32b s, s32b lvl, s32b max, s32b min, s32b bonus)
+ * @dgonly
+ * @brief Get the casting level of school spell "s".\n
+ * @param s Number \n s is the index of the spell in array of school spells.
+ * There is no range checking.
+ * @brief Spell index
+ * @param lvl Number \n lvl represents the level of player skill.
+ * @brief Player skill level
+ * @param max Number \n max is the maximum level for the spell.
+ * @brief Maximum spell level
+ * @param min Number \n min is the minimum level for the spell.
+ * @brief Minimum spell level
+ * @param bonus Number \n bonus is any bonus to final level.
+ * @brief Bonus
+ * @return Number \n Casting level.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s32b lua_get_level(s32b s, s32b lvl, s32b max, s32b min, s32b bonus);
+
+/** @fn lua_spell_chance(s32b chance, int level, int skill_level, int mana, int cur_mana, int stat)
+ * @dgonly
+ * @brief Get the chance a spell will fail.\n
+ * @param chance Number \n chance is the inital chance a spell will work.
+ * @brief Initial chance
+ * @param level Number \n level represents the level of player skill.
+ * @brief Player skill level
+ * @param skill_level Number \n *unused*.
+ * @brief *Unused*
+ * @param mana Number \n mana is the mana required by the spell.
+ * @brief Spell mana
+ * @param cur_mana Number \n cur_mana is the player's current mana.
+ * @brief Player mana
+ * @param stat Number \n stat is the required casting statistic (INT or WIS).
+ * @brief Casting statistic
+ * @return Number \n Chance of failure.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s32b lua_spell_chance(s32b chance, int level, int skill_level, int mana, int cur_mana, int stat);
+
+/** @fn lua_spell_device_chance(s32b chance, int level, int base_level)
+ * @dgonly
+ * @brief Get the chance a device will fail.\n
+ * @param chance Number \n chance is the inital chance a spell will work.
+ * @brief Initial chance
+ * @param level Number \n level represents the level of player skill.
+ * @brief Player skill level
+ * @param base_level Number \n *unused*
+ * @brief *Unused*
+ * @return Number \n Chance of failure.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s32b lua_spell_device_chance(s32b chance, int level, int base_level);
+
+/** @fn get_school_spell(cptr do_what, cptr check_fct, s16b force_book)
+ * @brief Get a spell from a book.\n
+ * @param do_what String \n what the player wants to do with the spell,
+ * for example "cast" or "copy".
+ * @brief Action
+ * @param check_fct String \n check_fct is the name of a function which checks
+ * if the player has access to the spell.
+ * @brief Check function
+ * @param force_book Number \n If it is different from 0 it for'ces the use of
+ * a spellbook, bypassing spellbook selection
+ * @brief Bypass book selection
+ * @return Number \n Spell number.
+ * @note
+ * Get a spell from a book\n\n
+ * The player must have a book to select a spell. When a book is chosen, the
+ * player is given a choice of spells to select. The player must be able to
+ * access the spell.\n\n
+ * If no spell is chosen, -1 is returned.
+ * @note (see file cmd5.c)
+ */
+extern s32b get_school_spell(cptr do_what, cptr check_fct, s16b force_book);
+
+/** @name Last Teleportation
+ * @brief Coordinates of last successful teleportation
+ * @{ */
+/** @var last_teleportation_y
+ * @brief Number
+ * @note y-coordinate of last successful teleportation
+ */
+extern s16b last_teleportation_y;
+
+/** @var last_teleportation_x
+ * @brief Number
+ * @note x-coordinate of last successful teleportation
+ */
+extern s16b last_teleportation_x;
+/** @} */
+
+/** @fn get_pos_player(int dis, int *ny=0, int *nx=0)
+ * @brief Get a grid near the player.\n
+ * @param dis Number \n is the maximum distance away from the player.
+ * This is limited to 200.
+ * @brief Distance from player
+ * @return y Number \n Y-coordinate of grid.
+ * @return x Number \n X-coordinate of grid.
+ * @note
+ * This function is slightly obsessive about correctness.\n\n
+ * Minimum distance is half the maximum distance. The function attempts to
+ * find a valid grid up to 500 times. If no valid grid is found, the maximum
+ * distance is doubled (though no more than 200) and the minimum distance is
+ * halved. The function does this 100 times.
+ * @note (see file spells1.c)
+ */
+extern void get_pos_player(int dis, int *ny=0, int *nx=0);
diff --git a/src/spells1.c b/src/spells1.c
new file mode 100644
index 00000000..9bfc6fe2
--- /dev/null
+++ b/src/spells1.c
@@ -0,0 +1,9327 @@
+/* File: spells1.c */
+
+/* Purpose: Spell code (part 1) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/* 1/x chance of reducing stats (for elemental attacks) */
+#define HURT_CHANCE 32
+
+/* 1/x chance of hurting even if invulnerable!*/
+#define PENETRATE_INVULNERABILITY 13
+
+/* Maximum number of tries for teleporting */
+#define MAX_TRIES 100
+
+
+/*
+ * Helper function -- return a "nearby" race for polymorphing
+ *
+ * Note that this function is one of the more "dangerous" ones...
+ */
+s16b poly_r_idx(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int i, r;
+
+ /* Hack -- Uniques never polymorph */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ return (r_idx);
+
+ /* Pick a (possibly new) non-unique race */
+ for (i = 0; i < 1000; i++)
+ {
+ /* Pick a new race, using a level calculation */
+ r = get_mon_num((dun_level + r_ptr->level) / 2 + 5);
+
+ /* Handle failure */
+ if (!r) break;
+
+ /* Obtain race */
+ r_ptr = &r_info[r];
+
+ /* Ignore unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) continue;
+
+ /* Use that index */
+ r_idx = r;
+
+ /* Done */
+ break;
+ }
+
+ /* Result */
+ return (r_idx);
+}
+
+/*
+ * Teleport player, using a distance and a direction as a rough guide.
+ *
+ * This function is not at all obsessive about correctness.
+ * This function allows teleporting into vaults (!)
+ */
+void teleport_player_directed(int rad, int dir)
+{
+ int y = p_ptr->py;
+ int x = p_ptr->px;
+ int yfoo = ddy[dir];
+ int xfoo = ddx[dir];
+ int min = rad / 4;
+ int dis = rad;
+ int i, d;
+ bool_ look = TRUE;
+ bool_ y_major = FALSE;
+ bool_ x_major = FALSE;
+ int y_neg = 1;
+ int x_neg = 1;
+ cave_type *c_ptr;
+
+ if (xfoo == 0 && yfoo == 0)
+ {
+ teleport_player(rad);
+ return;
+ }
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ if (yfoo == 0) x_major = TRUE;
+ if (xfoo == 0) y_major = TRUE;
+ if (yfoo < 0) y_neg = -1;
+ if (xfoo < 0) x_neg = -1;
+
+ /* Look until done */
+ while (look)
+ {
+ /* Verify max distance */
+ if (dis > 200)
+ {
+ teleport_player(rad);
+ return;
+ }
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ if (y_major)
+ {
+ y = rand_spread(p_ptr->py + y_neg * dis / 2, dis / 2);
+ }
+ else
+ {
+ y = rand_spread(p_ptr->py, dis / 3);
+ }
+
+ if (x_major)
+ {
+ x = rand_spread(p_ptr->px + x_neg * dis / 2, dis / 2);
+ }
+ else
+ {
+ x = rand_spread(p_ptr->px, dis / 3);
+ }
+
+ d = distance(p_ptr->py, p_ptr->px, y, x);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Require "naked" floor space */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+
+ }
+
+ /* Sound */
+ sound(SOUND_TELEPORT);
+
+ /* Move player */
+ teleport_player_to(y, x);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+
+ c_ptr = &cave[y][x];
+
+ /* Hack -- enter a store if we are on one */
+ if (c_ptr->feat == FEAT_SHOP)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- enter store */
+ command_new = '_';
+ }
+}
+
+
+/*
+ * Teleport a monster, normally up to "dis" grids away.
+ *
+ * Attempt to move the monster at least "dis/2" grids away.
+ *
+ * But allow variation to prevent infinite loops.
+ */
+void teleport_away(int m_idx, int dis)
+{
+ int ny = 0, nx = 0, oy, ox, d, i, min;
+ int tries = 0;
+
+ bool_ look = TRUE;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ /* Paranoia */
+ if (!m_ptr->r_idx) return;
+
+ /* Save the old location */
+ oy = m_ptr->fy;
+ ox = m_ptr->fx;
+
+ /* Minimum distance */
+ min = dis / 2;
+
+ /* Look until done */
+ while (look)
+ {
+ tries++;
+
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ ny = rand_spread(oy, dis);
+ nx = rand_spread(ox, dis);
+ d = distance(oy, ox, ny, nx);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(ny, nx)) continue;
+
+ /* Require "empty" floor space */
+ if (!cave_empty_bold(ny, nx)) continue;
+
+ /* Hack -- no teleport onto glyph of warding */
+ if (cave[ny][nx].feat == FEAT_GLYPH) continue;
+ if (cave[ny][nx].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* ...nor onto the Pattern */
+ if ((cave[ny][nx].feat >= FEAT_PATTERN_START) &&
+ (cave[ny][nx].feat <= FEAT_PATTERN_XTRA2)) continue;
+
+ /* No teleporting into vaults and such */
+ if (!(p_ptr->inside_quest || p_ptr->inside_arena))
+ if (cave[ny][nx].info & (CAVE_ICKY)) continue;
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+
+ /* Stop after MAX_TRIES tries */
+ if (tries > MAX_TRIES) return;
+ }
+
+ /* Sound */
+ sound(SOUND_TPOTHER);
+
+ /* Update the new location */
+ cave[ny][nx].m_idx = m_idx;
+ last_teleportation_y = ny;
+ last_teleportation_x = nx;
+
+ /* Update the old location */
+ cave[oy][ox].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = ny;
+ m_ptr->fx = nx;
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(oy, ox);
+
+ /* Redraw the new grid */
+ lite_spot(ny, nx);
+
+ /* Update monster light */
+ if (r_ptr->flags9 & RF9_HAS_LITE) p_ptr->update |= (PU_MON_LITE);
+}
+
+
+/*
+ * Teleport monster next to the player
+ */
+void teleport_to_player(int m_idx)
+{
+ int ny = 0, nx = 0, oy, ox, d, i, min;
+ int dis = 2;
+
+ bool_ look = TRUE;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ int attempts = 500;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ /* Paranoia */
+ if (!m_ptr->r_idx) return;
+
+ /* "Skill" test */
+ if (randint(100) > m_ptr->level) return;
+
+ /* Save the old location */
+ oy = m_ptr->fy;
+ ox = m_ptr->fx;
+
+ /* Minimum distance */
+ min = dis / 2;
+
+ /* Look until done */
+ while (look && --attempts)
+ {
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ ny = rand_spread(p_ptr->py, dis);
+ nx = rand_spread(p_ptr->px, dis);
+ d = distance(p_ptr->py, p_ptr->px, ny, nx);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(ny, nx)) continue;
+
+ /* Require "empty" floor space */
+ if (!cave_empty_bold(ny, nx)) continue;
+
+ /* Hack -- no teleport onto glyph of warding */
+ if (cave[ny][nx].feat == FEAT_GLYPH) continue;
+ if (cave[ny][nx].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* ...nor onto the Pattern */
+ if ((cave[ny][nx].feat >= FEAT_PATTERN_START) &&
+ (cave[ny][nx].feat <= FEAT_PATTERN_XTRA2)) continue;
+
+ /* No teleporting into vaults and such */
+ /* if (cave[ny][nx].info & (CAVE_ICKY)) continue; */
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+ }
+
+ if (attempts < 1) return;
+
+ /* Sound */
+ sound(SOUND_TPOTHER);
+
+ /* Update the new location */
+ cave[ny][nx].m_idx = m_idx;
+ last_teleportation_y = ny;
+ last_teleportation_x = nx;
+
+ /* Update the old location */
+ cave[oy][ox].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = ny;
+ m_ptr->fx = nx;
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(oy, ox);
+
+ /* Redraw the new grid */
+ lite_spot(ny, nx);
+
+ /* Update monster light */
+ if (r_ptr->flags9 & RF9_HAS_LITE) p_ptr->update |= (PU_MON_LITE);
+}
+
+
+/*
+ * Teleport the player to a location up to "dis" grids away.
+ *
+ * If no such spaces are readily available, the distance may increase.
+ * Try very hard to move the player at least a quarter that distance.
+ */
+/* It'd be better if this was made an argument ... */
+bool_ teleport_player_bypass = FALSE;
+
+void teleport_player(int dis)
+{
+ int d, i, min, ox, oy, x = 0, y = 0;
+ int tries = 0;
+
+ int xx = -1, yy = -1;
+
+ bool_ look = TRUE;
+
+ if (p_ptr->resist_continuum && (!teleport_player_bypass))
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ if (p_ptr->wild_mode) return;
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ if (p_ptr->anti_tele && (!teleport_player_bypass))
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ if ((dungeon_flags2 & DF2_NO_TELEPORT) && (!teleport_player_bypass))
+ {
+ msg_print("No teleport on special levels...");
+ return;
+ }
+
+
+ if (dis > 200) dis = 200; /* To be on the safe side... */
+
+ /* Minimum distance */
+ min = dis / 2;
+
+ /* Look until done */
+ while (look)
+ {
+ tries++;
+
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ y = rand_spread(p_ptr->py, dis);
+ x = rand_spread(p_ptr->px, dis);
+ d = distance(p_ptr->py, p_ptr->px, y, x);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Require "naked" floor space */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* No teleporting into vaults and such */
+ if (cave[y][x].info & (CAVE_ICKY)) continue;
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+
+ /* Stop after MAX_TRIES tries */
+ if (tries > MAX_TRIES) return;
+ }
+
+ /* Sound */
+ sound(SOUND_TELEPORT);
+
+ /* Save the old location */
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->py = y;
+ p_ptr->px = x;
+ last_teleportation_y = y;
+ last_teleportation_x = x;
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ while (xx < 2)
+ {
+ yy = -1;
+
+ while (yy < 2)
+ {
+ if (xx == 0 && yy == 0)
+ {
+ /* Do nothing */
+ }
+ else
+ {
+ if (cave[oy + yy][ox + xx].m_idx)
+ {
+ monster_race *r_ptr = race_inf(&m_list[cave[oy + yy][ox + xx].m_idx]);
+
+ if ((r_ptr->flags6
+ & RF6_TPORT) &&
+ !(r_ptr->flags3
+ & RF3_RES_TELE))
+ /*
+ * The latter limitation is to avoid
+ * totally unkillable suckers...
+ */
+ {
+ if (!(m_list[cave[oy + yy][ox + xx].m_idx].csleep))
+ teleport_to_player(cave[oy + yy][ox + xx].m_idx);
+ }
+ }
+ }
+ yy++;
+ }
+ xx++;
+ }
+
+ /* Redraw the new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+}
+
+
+/*
+ * get a grid near the given location
+ *
+ * This function is slightly obsessive about correctness.
+ */
+void get_pos_player(int dis, int *ny, int *nx)
+{
+ int d, i, min, x = 0, y = 0;
+ int tries = 0;
+
+ bool_ look = TRUE;
+
+ if (dis > 200) dis = 200; /* To be on the safe side... */
+
+ /* Minimum distance */
+ min = dis / 2;
+
+ /* Look until done */
+ while (look)
+ {
+ tries++;
+
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ y = rand_spread(p_ptr->py, dis);
+ x = rand_spread(p_ptr->px, dis);
+ d = distance(p_ptr->py, p_ptr->px, y, x);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Require "naked" floor space */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* No teleporting into vaults and such */
+ if (cave[y][x].info & (CAVE_ICKY)) continue;
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+
+ /* Stop after MAX_TRIES tries */
+ if (tries > MAX_TRIES) return;
+ }
+
+ *ny = y;
+ *nx = x;
+}
+
+/*
+ * Teleport a monster to a grid near the given location
+ *
+ * This function is slightly obsessive about correctness.
+ */
+void teleport_monster_to(int m_idx, int ny, int nx)
+{
+ int y, x, oy, ox, dis = 0, ctr = 0;
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ if (p_ptr->anti_tele)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ /* Find a usable location */
+ while (1)
+ {
+ /* Pick a nearby legal location */
+ while (1)
+ {
+ y = rand_spread(ny, dis);
+ x = rand_spread(nx, dis);
+ if (in_bounds(y, x)) break;
+ }
+
+ /* Not on the player's grid */
+ /* Accept "naked" floor grids */
+ if (cave_naked_bold(y, x) && (y != p_ptr->py) && (x != p_ptr->px)) break;
+
+ /* Occasionally advance the distance */
+ if (++ctr > (4 * dis * dis + 4 * dis + 1))
+ {
+ ctr = 0;
+ dis++;
+ }
+ }
+
+ /* Sound */
+ sound(SOUND_TPOTHER);
+
+ /* Save the old position */
+ oy = m_ptr->fy;
+ ox = m_ptr->fx;
+ cave[oy][ox].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = y;
+ m_ptr->fx = x;
+ cave[y][x].m_idx = m_idx;
+ last_teleportation_y = y;
+ last_teleportation_x = x;
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ /* Redraw the new spot */
+ lite_spot(m_ptr->fy, m_ptr->fx);
+}
+
+
+/*
+ * Teleport player to a grid near the given location
+ *
+ * This function is slightly obsessive about correctness.
+ * This function allows teleporting into vaults (!)
+ */
+void teleport_player_to(int ny, int nx)
+{
+ int y, x, oy, ox, dis = 0, ctr = 0;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ if (p_ptr->anti_tele)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ return;
+ }
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ /* Find a usable location */
+ while (1)
+ {
+ /* Pick a nearby legal location */
+ while (1)
+ {
+ y = rand_spread(ny, dis);
+ x = rand_spread(nx, dis);
+ if (in_bounds(y, x)) break;
+ }
+
+ /* Accept "naked" floor grids */
+ if (cave_naked_bold2(y, x)) break;
+
+ /* Occasionally advance the distance */
+ if (++ctr > (4 * dis * dis + 4 * dis + 1))
+ {
+ ctr = 0;
+ dis++;
+ }
+ }
+
+ /* Sound */
+ sound(SOUND_TELEPORT);
+
+ /* Save the old location */
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->py = y;
+ p_ptr->px = x;
+ last_teleportation_y = y;
+ last_teleportation_x = x;
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ /* Redraw the new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+}
+
+
+
+/*
+ * Teleport the player one level up or down (random when legal)
+ */
+void teleport_player_level(void)
+{
+ /* No effect in arena or quest */
+ if (p_ptr->inside_arena || p_ptr->inside_quest)
+ {
+ msg_print("There is no effect.");
+ return;
+ }
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ return;
+ }
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerfull force prevents your from teleporting.");
+ return;
+ }
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ /* Hack -- when you are fated to die, you cant cheat :) */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ if (p_ptr->anti_tele)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ if (!dun_level)
+ {
+ msg_print("You sink through the floor.");
+
+ autosave_checkpoint();
+
+ dun_level++;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ else if (is_quest(dun_level) || (dun_level >= MAX_DEPTH - 1))
+ {
+ msg_print("You rise up through the ceiling.");
+
+ autosave_checkpoint();
+
+ dun_level--;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ else if (rand_int(100) < 50)
+ {
+ msg_print("You rise up through the ceiling.");
+
+ autosave_checkpoint();
+
+ dun_level--;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ else
+ {
+ msg_print("You sink through the floor.");
+
+ autosave_checkpoint();
+
+ dun_level++;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+
+ /* Sound */
+ sound(SOUND_TPLEVEL);
+}
+
+
+
+/*
+ * Recall the player to town or dungeon
+ */
+void recall_player(int d, int f)
+{
+ /* Rooted means no move */
+ if (p_ptr->tim_roots)
+ {
+ msg_print("Your roots prevent the recall.");
+ return;
+ }
+
+
+ if (dun_level && (max_dlv[dungeon_type] > dun_level) &&
+ !p_ptr->inside_quest)
+ {
+ if (get_check("Reset recall depth? "))
+ max_dlv[dungeon_type] = dun_level;
+
+ }
+ if (!p_ptr->word_recall)
+ {
+ p_ptr->word_recall = rand_int(d) + f;
+ msg_print("The air about you becomes charged...");
+ }
+ else
+ {
+ p_ptr->word_recall = 0;
+ msg_print("A tension leaves the air around you...");
+ }
+ p_ptr->redraw |= (PR_DEPTH);
+}
+
+
+
+/*
+ * Get a legal "multi-hued" color for drawing "spells"
+ */
+static byte mh_attr(int max)
+{
+ switch (randint(max))
+ {
+ case 1:
+ return (TERM_RED);
+ case 2:
+ return (TERM_GREEN);
+ case 3:
+ return (TERM_BLUE);
+ case 4:
+ return (TERM_YELLOW);
+ case 5:
+ return (TERM_ORANGE);
+ case 6:
+ return (TERM_VIOLET);
+ case 7:
+ return (TERM_L_RED);
+ case 8:
+ return (TERM_L_GREEN);
+ case 9:
+ return (TERM_L_BLUE);
+ case 10:
+ return (TERM_UMBER);
+ case 11:
+ return (TERM_L_UMBER);
+ case 12:
+ return (TERM_SLATE);
+ case 13:
+ return (TERM_WHITE);
+ case 14:
+ return (TERM_L_WHITE);
+ case 15:
+ return (TERM_L_DARK);
+ }
+
+ return (TERM_WHITE);
+}
+
+
+/*
+ * Return a color to use for the bolt/ball spells
+ */
+byte spell_color(int type)
+{
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_COLOR, "d", "(d,d)", type, streq(ANGBAND_GRAF, "new")))
+ {
+ return process_hooks_return[0].num;
+ }
+
+ /* Check if A.B.'s new graphics should be used (rr9) */
+ if (streq(ANGBAND_GRAF, "new"))
+ {
+ /* Analyze */
+ switch (type)
+ {
+ case GF_MISSILE:
+ return (0x0F);
+ case GF_ACID:
+ return (0x04);
+ case GF_ELEC:
+ return (0x02);
+ case GF_FIRE:
+ return (0x00);
+ case GF_COLD:
+ return (0x01);
+ case GF_POIS:
+ return (0x03);
+ case GF_UNBREATH:
+ return (0x03);
+ case GF_HOLY_FIRE:
+ return (0x00);
+ case GF_HELL_FIRE:
+ return (0x00);
+ case GF_MANA:
+ return (0x0E);
+ case GF_ARROW:
+ return (0x0F);
+ case GF_WATER:
+ return (0x04);
+ case GF_WAVE:
+ return (0x04);
+ case GF_NETHER:
+ return (0x07);
+ case GF_CHAOS:
+ return (mh_attr(15));
+ case GF_DISENCHANT:
+ return (0x05);
+ case GF_NEXUS:
+ return (0x0C);
+ case GF_CONFUSION:
+ return (mh_attr(4));
+ case GF_SOUND:
+ return (0x09);
+ case GF_SHARDS:
+ return (0x08);
+ case GF_FORCE:
+ return (0x09);
+ case GF_INERTIA:
+ return (0x09);
+ case GF_GRAVITY:
+ return (0x09);
+ case GF_TIME:
+ return (0x09);
+ case GF_LITE_WEAK:
+ return (0x06);
+ case GF_LITE:
+ return (0x06);
+ case GF_DARK_WEAK:
+ return (0x07);
+ case GF_DARK:
+ return (0x07);
+ case GF_PLASMA:
+ return (0x0B);
+ case GF_METEOR:
+ return (0x00);
+ case GF_ICE:
+ return (0x01);
+ case GF_ROCKET:
+ return (0x0F);
+ case GF_DEATH_RAY:
+ return (0x07);
+ case GF_NUKE:
+ return (mh_attr(2));
+ case GF_DISINTEGRATE:
+ return (0x05);
+ case GF_PSI:
+ case GF_PSI_DRAIN:
+ case GF_TELEKINESIS:
+ case GF_DOMINATION:
+ return (0x09);
+ }
+
+ }
+
+ /* Normal tiles or ASCII */
+ else
+ {
+ /* Analyze */
+ switch (type)
+ {
+ case GF_MISSILE:
+ return (TERM_SLATE);
+ case GF_ACID:
+ return (randint(5) < 3 ? TERM_YELLOW : TERM_L_GREEN);
+ case GF_ELEC:
+ return (randint(7) < 6 ? TERM_WHITE : (randint(4) == 1 ? TERM_BLUE : TERM_L_BLUE));
+ case GF_FIRE:
+ return (randint(6) < 4 ? TERM_YELLOW : (randint(4) == 1 ? TERM_RED : TERM_L_RED));
+ case GF_COLD:
+ return (randint(6) < 4 ? TERM_WHITE : TERM_L_WHITE);
+ case GF_POIS:
+ return (randint(5) < 3 ? TERM_L_GREEN : TERM_GREEN);
+ case GF_UNBREATH:
+ return (randint(7) < 3 ? TERM_L_GREEN : TERM_GREEN);
+ case GF_HOLY_FIRE:
+ return (randint(5) == 1 ? TERM_ORANGE : TERM_WHITE);
+ case GF_HELL_FIRE:
+ return (randint(6) == 1 ? TERM_RED : TERM_L_DARK);
+ case GF_MANA:
+ return (randint(5) != 1 ? TERM_VIOLET : TERM_L_BLUE);
+ case GF_ARROW:
+ return (TERM_L_UMBER);
+ case GF_WATER:
+ return (randint(4) == 1 ? TERM_L_BLUE : TERM_BLUE);
+ case GF_WAVE:
+ return (randint(4) == 1 ? TERM_L_BLUE : TERM_BLUE);
+ case GF_NETHER:
+ return (randint(4) == 1 ? TERM_SLATE : TERM_L_DARK);
+ case GF_CHAOS:
+ return (mh_attr(15));
+ case GF_DISENCHANT:
+ return (randint(5) != 1 ? TERM_L_BLUE : TERM_VIOLET);
+ case GF_NEXUS:
+ return (randint(5) < 3 ? TERM_L_RED : TERM_VIOLET);
+ case GF_CONFUSION:
+ return (mh_attr(4));
+ case GF_SOUND:
+ return (randint(4) == 1 ? TERM_VIOLET : TERM_WHITE);
+ case GF_SHARDS:
+ return (randint(5) < 3 ? TERM_UMBER : TERM_SLATE);
+ case GF_FORCE:
+ return (randint(5) < 3 ? TERM_L_WHITE : TERM_ORANGE);
+ case GF_INERTIA:
+ return (randint(5) < 3 ? TERM_SLATE : TERM_L_WHITE);
+ case GF_GRAVITY:
+ return (randint(3) == 1 ? TERM_L_UMBER : TERM_UMBER);
+ case GF_TIME:
+ return (randint(2) == 1 ? TERM_WHITE : TERM_L_DARK);
+ case GF_LITE_WEAK:
+ return (randint(3) == 1 ? TERM_ORANGE : TERM_YELLOW);
+ case GF_LITE:
+ return (randint(4) == 1 ? TERM_ORANGE : TERM_YELLOW);
+ case GF_DARK_WEAK:
+ return (randint(3) == 1 ? TERM_DARK : TERM_L_DARK);
+ case GF_DARK:
+ return (randint(4) == 1 ? TERM_DARK : TERM_L_DARK);
+ case GF_PLASMA:
+ return (randint(5) == 1 ? TERM_RED : TERM_L_RED);
+ case GF_METEOR:
+ return (randint(3) == 1 ? TERM_RED : TERM_UMBER);
+ case GF_ICE:
+ return (randint(4) == 1 ? TERM_L_BLUE : TERM_WHITE);
+ case GF_ROCKET:
+ return (randint(6) < 4 ? TERM_L_RED : (randint(4) == 1 ? TERM_RED : TERM_L_UMBER));
+ case GF_DEATH:
+ case GF_DEATH_RAY:
+ return (TERM_L_DARK);
+ case GF_NUKE:
+ return (mh_attr(2));
+ case GF_DISINTEGRATE:
+ return (randint(3) != 1 ? TERM_L_DARK : (randint(2) == 1 ? TERM_ORANGE : TERM_L_UMBER));
+ case GF_PSI:
+ case GF_PSI_DRAIN:
+ case GF_TELEKINESIS:
+ case GF_DOMINATION:
+ return (randint(3) != 1 ? TERM_L_BLUE : TERM_WHITE);
+ }
+ }
+
+ /* Standard "color" */
+ return (TERM_WHITE);
+}
+
+
+/*
+ * Find the attr/char pair to use for a spell effect
+ *
+ * It is moving (or has moved) from (x,y) to (nx,ny).
+ *
+ * If the distance is not "one", we (may) return "*".
+ */
+static u16b bolt_pict(int y, int x, int ny, int nx, int typ)
+{
+ int base;
+
+ byte k;
+
+ byte a;
+ char c;
+
+ /* No motion (*) */
+ if ((ny == y) && (nx == x)) base = 0x30;
+
+ /* Vertical (|) */
+ else if (nx == x) base = 0x40;
+
+ /* Horizontal (-) */
+ else if (ny == y) base = 0x50;
+
+ /* Diagonal (/) */
+ else if ((ny - y) == (x - nx)) base = 0x60;
+
+ /* Diagonal (\) */
+ else if ((ny - y) == (nx - x)) base = 0x70;
+
+ /* Weird (*) */
+ else base = 0x30;
+
+ /* Basic spell color */
+ k = spell_color(typ);
+
+ /* Obtain attr/char */
+ a = misc_to_attr[base + k];
+ c = misc_to_char[base + k];
+
+ /* Create pict */
+ return (PICT(a, c));
+}
+
+/*
+ * Cast the spelbound spells
+ */
+void spellbinder_trigger()
+{
+ int i;
+
+ cmsg_print(TERM_L_GREEN, "The spellbinder is triggered!");
+ for (i = 0; i < p_ptr->spellbinder_num; i++)
+ {
+ msg_format("Triggering spell %s.", school_spells[p_ptr->spellbinder[i]].name);
+ exec_lua(format("cast_school_spell(%d, spell(%d), TRUE)", p_ptr->spellbinder[i], p_ptr->spellbinder[i]));
+ }
+ p_ptr->spellbinder_num = 0;
+ p_ptr->spellbinder_trigger = 0;
+}
+
+
+/*
+ * Decreases players hit points and sets death flag if necessary
+ *
+ * XXX XXX XXX Invulnerability needs to be changed into a "shield"
+ *
+ * XXX XXX XXX Hack -- this function allows the user to save (or quit)
+ * the game when he dies, since the "You die." message is shown before
+ * setting the player to "dead".
+ */
+void take_hit(int damage, cptr hit_from)
+{
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ int old_chp = p_ptr->chp;
+
+ bool_ pen_invuln = FALSE;
+ bool_ monster_take = FALSE;
+
+ char death_message[80];
+
+ int warning = (p_ptr->mhp * hitpoint_warn / 10);
+ int percent;
+
+ /* Paranoia */
+ if (death) return;
+
+ /* Disturb */
+ disturb(1, 0);
+
+ /* Apply "invulnerability" */
+ if (p_ptr->invuln && (damage < 9000))
+ {
+ if (randint(PENETRATE_INVULNERABILITY) == 1)
+ {
+ pen_invuln = TRUE;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ /* Apply disruption shield */
+ if (p_ptr->disrupt_shield)
+ {
+ if (p_ptr->csp > (damage * 2))
+ {
+ p_ptr->csp -= (damage * 2);
+ damage = 0;
+ }
+ else
+ {
+ damage -= p_ptr->csp / 2;
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+ }
+
+ /* Display the mana */
+ p_ptr->redraw |= (PR_MANA);
+ }
+
+ /* Hurt the wielded monster if any */
+ if ((o_ptr->k_idx) && (magik(5 + get_skill(SKILL_SYMBIOTIC))) && (!carried_monster_hit))
+ {
+ cptr sym_name = symbiote_name(TRUE);
+
+ if (o_ptr->pval2 - damage <= 0)
+ {
+ cmsg_format(TERM_L_RED,
+ "%s dies from protecting you, you feel very sad...",
+ sym_name);
+ inc_stack_size_ex(INVEN_CARRY, -1, OPTIMIZE, NO_DESCRIBE);
+ damage -= o_ptr->pval2;
+ o_ptr->pval2 = 0;
+ p_ptr->redraw |= PR_MH;
+ }
+ else
+ {
+ msg_format("%s takes the damage instead of you.", sym_name);
+ o_ptr->pval2 -= damage;
+ monster_take = TRUE;
+ }
+
+ carried_monster_hit = FALSE;
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_MH);
+ }
+
+ /* Hurt the player */
+ if (!monster_take) p_ptr->chp -= damage;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ if (pen_invuln)
+ cmsg_print(TERM_YELLOW, "The attack penetrates your shield of invulnerability!");
+
+ /* Dead player */
+ if (p_ptr->chp < 0)
+ {
+ /* Necromancers get a special treatment */
+ if (((!has_ability(AB_UNDEAD_FORM)) || ((p_ptr->necro_extra & CLASS_UNDEAD))))
+ {
+ /* Sound */
+ sound(SOUND_DEATH);
+
+ /* Hack -- Note death */
+ if (!last_words)
+ {
+ cmsg_print(TERM_RED, "You die.");
+ msg_print(NULL);
+ }
+ else
+ {
+ (void)get_rnd_line("death.txt", death_message);
+ cmsg_print(TERM_RED, death_message);
+ }
+
+ /* Note cause of death */
+ (void)strcpy(died_from, hit_from);
+
+ if (p_ptr->image) strcat(died_from, "(?)");
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* No longer a winner */
+ total_winner = FALSE;
+
+
+ /* Note death */
+ death = TRUE;
+
+ if (get_check("Dump the screen? "))
+ {
+ do_cmd_html_dump();
+ }
+
+ /* Dead */
+ return;
+ }
+ /* Just turn the necromancer into an undead */
+ else
+ {
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ p_ptr->necro_extra2 = p_ptr->lev + (rand_int(p_ptr->lev / 2) - (p_ptr->lev / 4));
+ if (p_ptr->necro_extra2 < 1) p_ptr->necro_extra2 = 1;
+ cmsg_format(TERM_L_DARK, "You have to kill %d monster%s to be brought back to life.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s");
+
+ /* MEGA-HACK !!! */
+ calc_hitpoints();
+
+ /* Enforce maximum */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ do_cmd_wiz_cure_all();
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ }
+
+ /* Hitpoint warning */
+ if (p_ptr->chp < warning)
+ {
+ /* Hack -- bell on first notice */
+ if (alert_hitpoint && (old_chp > warning)) bell();
+
+ sound(SOUND_WARN);
+
+ /* Message */
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ cmsg_print(TERM_RED, "*** LOW DEATHPOINT WARNING! ***");
+ else
+ cmsg_print(TERM_RED, "*** LOW HITPOINT WARNING! ***");
+ msg_print(NULL);
+ }
+
+ /* How much life is left ? */
+ percent = p_ptr->chp * 100 / p_ptr->mhp;
+
+ /* Check the spellbinder trigger */
+ if (p_ptr->spellbinder_trigger == SPELLBINDER_HP75)
+ {
+ /* Trigger ?! */
+ if (percent <= 75)
+ spellbinder_trigger();
+ }
+ else if (p_ptr->spellbinder_trigger == SPELLBINDER_HP50)
+ {
+ /* Trigger ?! */
+ if (percent <= 50)
+ spellbinder_trigger();
+ }
+ else if (p_ptr->spellbinder_trigger == SPELLBINDER_HP25)
+ {
+ /* Trigger ?! */
+ if (percent <= 25)
+ spellbinder_trigger();
+ }
+
+ /* Melkor acn summon to help you */
+ if (percent < 25)
+ {
+ PRAY_GOD(GOD_MELKOR)
+ {
+ int chance = p_ptr->grace / 500; /* * 100 / 50000; */
+
+ if (magik(chance - 10))
+ {
+ int i;
+ int type = SUMMON_DEMON;
+
+ if (magik(50))
+ type = SUMMON_UNDEAD;
+
+ if (p_ptr->grace > 10000)
+ {
+ if (type == SUMMON_DEMON)
+ type = SUMMON_HI_DEMON;
+ else
+ type = SUMMON_HI_UNDEAD;
+ }
+
+ chance /= 10;
+ if (chance < 1) chance = 1;
+ for (i = 0; i < chance; i++)
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level / 2, type, FALSE);
+ msg_print("Melkor summons monsters to help you!");
+ }
+ }
+ }
+
+ if (player_char_health)
+ lite_spot(p_ptr->py, p_ptr->px);
+}
+
+
+/* Decrease player's sanity. This is a copy of the function above. */
+void take_sanity_hit(int damage, cptr hit_from)
+{
+ int old_csane = p_ptr->csane;
+
+ char death_message[80];
+
+ int warning = (p_ptr->msane * hitpoint_warn / 10);
+
+
+ /* Paranoia */
+ if (death) return;
+
+ /* Disturb */
+ disturb(1, 0);
+
+
+ /* Hurt the player */
+ p_ptr->csane -= damage;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_SANITY);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Dead player */
+ if (p_ptr->csane < 0)
+ {
+ /* Sound */
+ sound(SOUND_DEATH);
+
+ /* Hack -- Note death */
+ cmsg_print(TERM_VIOLET, "You turn into an unthinking vegetable.");
+ if (!last_words)
+ {
+ cmsg_print(TERM_RED, "You die.");
+ msg_print(NULL);
+ }
+ else
+ {
+ (void)get_rnd_line("death.txt", death_message);
+ cmsg_print(TERM_RED, death_message);
+ }
+
+ /* Note cause of death */
+ (void)strcpy(died_from, hit_from);
+
+ if (p_ptr->image) strcat(died_from, "(?)");
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Note death */
+ death = TRUE;
+
+ if (get_check("Dump the screen? "))
+ {
+ do_cmd_html_dump();
+ }
+
+ /* Dead */
+ return;
+ }
+
+ /* Hitpoint warning */
+ if (p_ptr->csane < warning)
+ {
+ /* Hack -- bell on first notice */
+ if (alert_hitpoint && (old_csane > warning)) bell();
+
+ sound(SOUND_WARN);
+
+ /* Message */
+ cmsg_print(TERM_RED, "*** LOW SANITY WARNING! ***");
+ msg_print(NULL);
+ }
+}
+
+
+/*
+ * Note that amulets, rods, and high-level spell books are immune
+ * to "inventory damage" of any kind. Also sling ammo and shovels.
+ */
+
+
+/*
+ * Does a given class of objects (usually) hate acid?
+ * Note that acid can either melt or corrode something.
+ */
+static bool_ hates_acid(object_type *o_ptr)
+{
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Wearable items */
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOW:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ return (TRUE);
+ }
+
+ /* Staffs/Scrolls are wood/paper */
+ case TV_STAFF:
+ case TV_SCROLL:
+ {
+ return (TRUE);
+ }
+
+ /* Ouch */
+ case TV_CHEST:
+ {
+ return (TRUE);
+ }
+
+ /* Junk is useless */
+ case TV_SKELETON:
+ case TV_BOTTLE:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Does a given object (usually) hate electricity?
+ */
+static bool_ hates_elec(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_RING:
+ case TV_WAND:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Does a given object (usually) hate fire?
+ * Hafted/Polearm weapons have wooden shafts.
+ * Arrows/Bows are mostly wooden.
+ */
+static bool_ hates_fire(object_type *o_ptr)
+{
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Special case for archers */
+ case TV_ARROW:
+ {
+ return TRUE;
+ };
+
+ /* Wearable */
+ case TV_LITE:
+ case TV_BOW:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ {
+ return (TRUE);
+ }
+
+ /* Books */
+ case TV_BOOK:
+ case TV_SYMBIOTIC_BOOK:
+ case TV_MUSIC_BOOK:
+ {
+ return (TRUE);
+ }
+
+ /* Chests */
+ case TV_CHEST:
+ {
+ return (TRUE);
+ }
+
+ /* Staffs/Scrolls burn */
+ case TV_STAFF:
+ case TV_SCROLL:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Does a given object (usually) hate cold?
+ */
+static bool_ hates_cold(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_POTION2:
+ case TV_POTION:
+ case TV_FLASK:
+ case TV_BOTTLE:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+
+
+
+
+
+
+
+/*
+ * Melt something
+ */
+static int set_acid_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_acid(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_ACID)) return (FALSE);
+ return (TRUE);
+}
+
+
+/*
+ * Electrical damage
+ */
+static int set_elec_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_elec(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_ELEC)) return (FALSE);
+ return (TRUE);
+}
+
+
+/*
+ * Burn something
+ */
+static int set_fire_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_fire(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_FIRE)) return (FALSE);
+ return (TRUE);
+}
+
+
+/*
+ * Freeze things
+ */
+static int set_cold_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_cold(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_COLD)) return (FALSE);
+ return (TRUE);
+}
+
+
+
+
+/*
+ * This seems like a pretty standard "typedef"
+ */
+typedef int (*inven_func)(object_type *);
+
+/*
+ * Destroys a type of item on a given percent chance
+ * Note that missiles are no longer necessarily all destroyed
+ * Destruction taken from "melee.c" code for "stealing".
+ * Returns number of items destroyed.
+ */
+static int inven_damage(inven_func typ, int perc)
+{
+ int i, j, k, amt;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Count the casualties */
+ k = 0;
+
+ /* Scan through the slots backwards */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Hack -- for now, skip artifacts */
+ if (artifact_p(o_ptr) || o_ptr->art_name) continue;
+
+ /* Give this item slot a shot at death */
+ if ((*typ)(o_ptr))
+ {
+ /* Count the casualties */
+ for (amt = j = 0; j < o_ptr->number; ++j)
+ {
+ if (rand_int(100) < perc) amt++;
+ }
+
+ /* Some casualities */
+ if (amt)
+ {
+ /* Get a description */
+ object_desc(o_name, o_ptr, FALSE, 3);
+
+ /* Message */
+ msg_format("%sour %s (%c) %s destroyed!",
+ ((o_ptr->number > 1) ?
+ ((amt == o_ptr->number) ? "All of y" :
+ (amt > 1 ? "Some of y" : "One of y")) : "Y"),
+ o_name, index_to_label(i),
+ ((amt > 1) ? "were" : "was"));
+
+ /* Potions smash open */
+ if (k_info[o_ptr->k_idx].tval == TV_POTION)
+ {
+ (void)potion_smash_effect(0, p_ptr->py, p_ptr->px, o_ptr->sval);
+ }
+
+ /*
+ * Hack -- If rods or wand are destroyed, the total maximum
+ * timeout or charges of the stack needs to be reduced,
+ * unless all the items are being destroyed. -LM-
+ */
+ if ((o_ptr->tval == TV_WAND)
+ && (amt < o_ptr->number))
+ {
+ o_ptr->pval -= o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Destroy "amt" items */
+ inc_stack_size_ex(i, -amt, OPTIMIZE, NO_DESCRIBE);
+
+ /* Count the casualties */
+ k += amt;
+ }
+ }
+ }
+
+ /* Return the casualty count */
+ return (k);
+}
+
+
+
+
+/*
+ * Acid has hit the player, attempt to affect some armor.
+ *
+ * Note that the "base armor" of an object never changes.
+ *
+ * If any armor is damaged (or resists), the player takes less damage.
+ */
+static int minus_ac(void)
+{
+ object_type *o_ptr = NULL;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ char o_name[80];
+
+
+ /* Pick a (possibly empty) inventory slot */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr = &p_ptr->inventory[INVEN_BODY];
+ break;
+ case 2:
+ o_ptr = &p_ptr->inventory[INVEN_ARM];
+ break;
+ case 3:
+ o_ptr = &p_ptr->inventory[INVEN_OUTER];
+ break;
+ case 4:
+ o_ptr = &p_ptr->inventory[INVEN_HANDS];
+ break;
+ case 5:
+ o_ptr = &p_ptr->inventory[INVEN_HEAD];
+ break;
+ case 6:
+ o_ptr = &p_ptr->inventory[INVEN_FEET];
+ break;
+ }
+
+ /* Nothing to damage */
+ if (!o_ptr->k_idx) return (FALSE);
+
+ /* No damage left to be done */
+ if (o_ptr->ac + o_ptr->to_a <= 0) return (FALSE);
+
+
+ /* Describe */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Object resists */
+ if (f3 & (TR3_IGNORE_ACID))
+ {
+ msg_format("Your %s is unaffected!", o_name);
+
+ return (TRUE);
+ }
+
+ /* Message */
+ msg_format("Your %s is damaged!", o_name);
+
+ /* Damage the item */
+ o_ptr->to_a--;
+
+ /* Calculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER);
+
+ /* Item was damaged */
+ return (TRUE);
+}
+
+
+/*
+ * Hurt the player with Acid
+ */
+void acid_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Total Immunity */
+ if (p_ptr->immune_acid || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->resist_acid) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_acid) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_acid || p_ptr->resist_acid)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+
+ /* If any armor gets hit, defend the player */
+ if (minus_ac()) dam = (dam + 1) / 2;
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->oppose_acid && p_ptr->resist_acid))
+ inven_damage(set_acid_destroy, inv);
+}
+
+
+/*
+ * Hurt the player with electricity
+ */
+void elec_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Total immunity */
+ if (p_ptr->immune_elec || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->oppose_elec) dam = (dam + 2) / 3;
+ if (p_ptr->resist_elec) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_elec || p_ptr->resist_elec)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_DEX, STAT_DEC_NORMAL);
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->oppose_elec && p_ptr->resist_elec))
+ inven_damage(set_elec_destroy, inv);
+}
+
+
+
+
+/*
+ * Hurt the player with Fire
+ */
+void fire_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Totally immune */
+ if (p_ptr->immune_fire || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->sensible_fire) dam = (dam + 2) * 2;
+ if (p_ptr->resist_fire) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_fire) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_fire || p_ptr->resist_fire)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->resist_fire && p_ptr->oppose_fire))
+ inven_damage(set_fire_destroy, inv);
+}
+
+
+/*
+ * Hurt the player with Cold
+ */
+void cold_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Total immunity */
+ if (p_ptr->immune_cold || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->resist_cold) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_cold) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_cold || p_ptr->resist_cold)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->resist_cold && p_ptr->oppose_cold))
+ inven_damage(set_cold_destroy, inv);
+}
+
+
+
+
+
+/*
+ * Increases a stat by one randomized level -RAK-
+ *
+ * Note that this function (used by stat potions) now restores
+ * the stat BEFORE increasing it.
+ */
+bool_ inc_stat(int stat)
+{
+ int value, gain;
+
+ /* Then augment the current/max stat */
+ value = p_ptr->stat_cur[stat];
+
+ /* Cannot go above 18/100 */
+ if (value < 18 + 100)
+ {
+ /* Gain one (sometimes two) points */
+ if (value < 18)
+ {
+ gain = ((rand_int(100) < 75) ? 1 : 2);
+ value += gain;
+ }
+
+ /* Gain 1/6 to 1/3 of distance to 18/100 */
+ else if (value < 18 + 98)
+ {
+ /* Approximate gain value */
+ gain = (((18 + 100) - value) / 2 + 3) / 2;
+
+ /* Paranoia */
+ if (gain < 1) gain = 1;
+
+ /* Apply the bonus */
+ value += randint(gain) + gain / 2;
+
+ /* Maximal value */
+ if (value > 18 + 99) value = 18 + 99;
+ }
+
+ /* Gain one point at a time */
+ else
+ {
+ value++;
+ }
+
+ /* Save the new value */
+ p_ptr->stat_cur[stat] = value;
+
+ /* Bring up the maximum too */
+ if (value > p_ptr->stat_max[stat])
+ {
+ p_ptr->stat_max[stat] = value;
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Nothing to gain */
+ return (FALSE);
+}
+
+
+
+/*
+ * Decreases a stat by an amount indended to vary from 0 to 100 percent.
+ *
+ * Amount could be a little higher in extreme cases to mangle very high
+ * stats from massive assaults. -CWS
+ *
+ * Note that "permanent" means that the *given* amount is permanent,
+ * not that the new value becomes permanent. This may not work exactly
+ * as expected, due to "weirdness" in the algorithm, but in general,
+ * if your stat is already drained, the "max" value will not drop all
+ * the way down to the "cur" value.
+ */
+bool_ dec_stat(int stat, int amount, int mode)
+{
+ int cur, max, loss = 0, same, res = FALSE;
+
+
+ /* Acquire current value */
+ cur = p_ptr->stat_cur[stat];
+ max = p_ptr->stat_max[stat];
+
+ /* Note when the values are identical */
+ same = (cur == max);
+
+ /* Damage "current" value */
+ if (cur > 3)
+ {
+ /* Handle "low" values */
+ if (cur <= 18)
+ {
+ if (amount > 90) cur--;
+ if (amount > 50) cur--;
+ if (amount > 20) cur--;
+ cur--;
+ }
+
+ /* Handle "high" values */
+ else
+ {
+ /* Hack -- Decrement by a random amount between one-quarter */
+ /* and one-half of the stat bonus times the percentage, with a */
+ /* minimum damage of half the percentage. -CWS */
+ loss = (((cur - 18) / 2 + 1) / 2 + 1);
+
+ /* Paranoia */
+ if (loss < 1) loss = 1;
+
+ /* Randomize the loss */
+ loss = ((randint(loss) + loss) * amount) / 100;
+
+ /* Maximal loss */
+ if (loss < amount / 2) loss = amount / 2;
+
+ /* Lose some points */
+ cur = cur - loss;
+
+ /* Hack -- Only reduce stat to 17 sometimes */
+ if (cur < 18) cur = (amount <= 20) ? 18 : 17;
+ }
+
+ /* Prevent illegal values */
+ if (cur < 3) cur = 3;
+
+ /* Something happened */
+ if (cur != p_ptr->stat_cur[stat]) res = TRUE;
+ }
+
+ /* Damage "max" value */
+ if ((mode == STAT_DEC_PERMANENT) && (max > 3))
+ {
+ /* Handle "low" values */
+ if (max <= 18)
+ {
+ if (amount > 90) max--;
+ if (amount > 50) max--;
+ if (amount > 20) max--;
+ max--;
+ }
+
+ /* Handle "high" values */
+ else
+ {
+ /* Hack -- Decrement by a random amount between one-quarter */
+ /* and one-half of the stat bonus times the percentage, with a */
+ /* minimum damage of half the percentage. -CWS */
+ loss = (((max - 18) / 2 + 1) / 2 + 1);
+ loss = ((randint(loss) + loss) * amount) / 100;
+ if (loss < amount / 2) loss = amount / 2;
+
+ /* Lose some points */
+ max = max - loss;
+
+ /* Hack -- Only reduce stat to 17 sometimes */
+ if (max < 18) max = (amount <= 20) ? 18 : 17;
+ }
+
+ /* Hack -- keep it clean */
+ if (same || (max < cur)) max = cur;
+
+ /* Something happened */
+ if (max != p_ptr->stat_max[stat]) res = TRUE;
+ }
+
+ /* Apply changes */
+ if (res)
+ {
+ if (mode == STAT_DEC_TEMPORARY)
+ {
+ u16b dectime;
+
+ /* a little crude, perhaps */
+ dectime = rand_int(max_dlv[dungeon_type] * 50) + 50;
+
+ /* Calculate loss */
+ loss = p_ptr->stat_cur[stat] - cur;
+
+ /* prevent overflow, stat_cnt = u16b */
+ /* or add another temporary drain... */
+ if ( ((p_ptr->stat_cnt[stat] + dectime) < p_ptr->stat_cnt[stat]) ||
+ (p_ptr->stat_los[stat] > 0) )
+
+ {
+ p_ptr->stat_cnt[stat] += dectime;
+ p_ptr->stat_los[stat] += loss;
+ }
+ else
+ {
+ p_ptr->stat_cnt[stat] = dectime;
+ p_ptr->stat_los[stat] = loss;
+ }
+ }
+
+ /* Actually set the stat to its new value. */
+ p_ptr->stat_cur[stat] = cur;
+ p_ptr->stat_max[stat] = max;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+ }
+
+ /* Done */
+ return (res);
+}
+
+
+/*
+ * Restore a stat. Return TRUE only if this actually makes a difference.
+ */
+bool_ res_stat(int stat, bool_ full)
+{
+ /* Fully restore */
+ if (full)
+ {
+ /* Restore if needed */
+ if (p_ptr->stat_cur[stat] != p_ptr->stat_max[stat])
+ {
+ /* Restore */
+ p_ptr->stat_cur[stat] = p_ptr->stat_max[stat];
+
+ /* Remove temporary drain */
+ p_ptr->stat_cnt[stat] = 0;
+ p_ptr->stat_los[stat] = 0;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Something happened */
+ return (TRUE);
+ }
+ }
+
+ /* Restore temporary drained stat */
+ else
+ {
+ /* Restore if needed */
+ if (p_ptr->stat_los[stat])
+ {
+ /* Restore */
+ p_ptr->stat_cur[stat] += p_ptr->stat_los[stat];
+
+ /* Remove temporary drain */
+ p_ptr->stat_cnt[stat] = 0;
+ p_ptr->stat_los[stat] = 0;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Something happened */
+ return (TRUE);
+ }
+ }
+
+ /* Nothing to restore */
+ return (FALSE);
+}
+
+
+
+
+/*
+ * Apply disenchantment to the player's stuff
+ *
+ * XXX XXX XXX This function is also called from the "melee" code
+ *
+ * If "mode is set to 0 then a random slot will be used, if not the "mode"
+ * slot will be used.
+ *
+ * Return "TRUE" if the player notices anything
+ */
+bool_ apply_disenchant(int mode)
+{
+ int t = mode;
+ object_type *o_ptr;
+ char o_name[80];
+
+ if (!mode)
+ {
+ /* Pick a random slot */
+ switch (randint(8))
+ {
+ case 1:
+ t = INVEN_WIELD;
+ break;
+ case 2:
+ t = INVEN_BOW;
+ break;
+ case 3:
+ t = INVEN_BODY;
+ break;
+ case 4:
+ t = INVEN_OUTER;
+ break;
+ case 5:
+ t = INVEN_ARM;
+ break;
+ case 6:
+ t = INVEN_HEAD;
+ break;
+ case 7:
+ t = INVEN_HANDS;
+ break;
+ case 8:
+ t = INVEN_FEET;
+ break;
+ }
+ }
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[t];
+
+ /* No item, nothing happens */
+ if (!o_ptr->k_idx) return (FALSE);
+
+
+ /* Nothing to disenchant */
+ if ((o_ptr->to_h <= 0) && (o_ptr->to_d <= 0) && (o_ptr->to_a <= 0))
+ {
+ /* Nothing to notice */
+ return (FALSE);
+ }
+
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+
+ /* Artifacts have 71% chance to resist */
+ if ((artifact_p(o_ptr) || o_ptr->art_name) && (rand_int(100) < 71))
+ {
+ /* Message */
+ msg_format("Your %s (%c) resist%s disenchantment!",
+ o_name, index_to_label(t),
+ ((o_ptr->number != 1) ? "" : "s"));
+
+ /* Notice */
+ return (TRUE);
+ }
+
+
+ /* Disenchant tohit */
+ if (o_ptr->to_h > 0) o_ptr->to_h--;
+ if ((o_ptr->to_h > 5) && (rand_int(100) < 20)) o_ptr->to_h--;
+
+ /* Disenchant todam */
+ if (o_ptr->to_d > 0) o_ptr->to_d--;
+ if ((o_ptr->to_d > 5) && (rand_int(100) < 20)) o_ptr->to_d--;
+
+ /* Disenchant toac */
+ if (o_ptr->to_a > 0) o_ptr->to_a--;
+ if ((o_ptr->to_a > 5) && (rand_int(100) < 20)) o_ptr->to_a--;
+
+ /* Message */
+ msg_format("Your %s (%c) %s disenchanted!",
+ o_name, index_to_label(t),
+ ((o_ptr->number != 1) ? "were" : "was"));
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER);
+
+ /* Notice */
+ return (TRUE);
+}
+
+
+void corrupt_player(void)
+{
+ int max1, cur1, max2, cur2, ii, jj;
+
+ /* Pick a pair of stats */
+ ii = rand_int(6);
+ for (jj = ii; jj == ii; jj = rand_int(6)) /* loop */;
+
+ max1 = p_ptr->stat_max[ii];
+ cur1 = p_ptr->stat_cur[ii];
+ max2 = p_ptr->stat_max[jj];
+ cur2 = p_ptr->stat_cur[jj];
+
+ p_ptr->stat_max[ii] = max2;
+ p_ptr->stat_cur[ii] = cur2;
+ p_ptr->stat_max[jj] = max1;
+ p_ptr->stat_cur[jj] = cur1;
+
+ p_ptr->update |= (PU_BONUS);
+}
+
+
+/*
+ * Apply Nexus
+ */
+static void apply_nexus(monster_type *m_ptr)
+{
+ if (m_ptr == NULL) return;
+
+ if (!(dungeon_flags2 & DF2_NO_TELEPORT))
+ {
+ switch (randint(7))
+ {
+ case 1:
+ case 2:
+ case 3:
+ {
+ teleport_player(200);
+ break;
+ }
+
+ case 4:
+ case 5:
+ {
+ teleport_player_to(m_ptr->fy, m_ptr->fx);
+ break;
+ }
+
+ case 6:
+ {
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ break;
+ }
+
+ /* Teleport Level */
+ teleport_player_level();
+ break;
+ }
+
+ case 7:
+ {
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ break;
+ }
+
+ msg_print("Your body starts to scramble...");
+ corrupt_player();
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Convert 2 couples of coordonates to a direction
+ */
+int yx_to_dir(int y2, int x2, int y1, int x1)
+{
+ int y = y2 - y1, x = x2 - x1;
+
+ if ((y == 0) && (x == 1)) return 6;
+ if ((y == 0) && (x == -1)) return 4;
+ if ((y == -1) && (x == 0)) return 8;
+ if ((y == 1) && (x == 0)) return 2;
+ if ((y == -1) && (x == -1)) return 7;
+ if ((y == -1) && (x == 1)) return 9;
+ if ((y == 1) && (x == 1)) return 3;
+ if ((y == 1) && (x == -1)) return 1;
+
+ return 5;
+}
+
+/*
+ * Give the opposate direction of the given one
+ */
+int invert_dir(int dir)
+{
+ if (dir == 4) return 6;
+ if (dir == 6) return 4;
+ if (dir == 8) return 2;
+ if (dir == 2) return 8;
+ if (dir == 7) return 3;
+ if (dir == 9) return 1;
+ if (dir == 1) return 9;
+ if (dir == 3) return 7;
+ return 5;
+}
+
+
+/*
+ * Determine which way the mana path follow
+ */
+int get_mana_path_dir(int y, int x, int oy, int ox, int pdir, int mana)
+{
+ int dir[8] = {5, 5, 5, 5, 5, 5, 5, 5}, n = 0, i, r = 0;
+
+ /* Check which case are allowed */
+ if (cave[y - 1][x].mana == mana) dir[n++] = 8;
+ if (cave[y + 1][x].mana == mana) dir[n++] = 2;
+ if (cave[y][x - 1].mana == mana) dir[n++] = 4;
+ if (cave[y][x + 1].mana == mana) dir[n++] = 6;
+
+ /* If only 2 possibilities select the only good one */
+ if (n == 2)
+ {
+ if (invert_dir(yx_to_dir(y, x, oy, ox)) != dir[0]) return dir[0];
+ if (invert_dir(yx_to_dir(y, x, oy, ox)) != dir[1]) return dir[1];
+
+ /* Should never happen */
+ return 5;
+ }
+
+
+ /* Check if it's not your last place */
+ for (i = 0; i < n; i++)
+ {
+ if ((oy == y + ddy[dir[i]]) && (ox == x + ddx[dir[i]]))
+ {
+ if (dir[i] == 8) dir[i] = 2;
+ else if (dir[i] == 2) dir[i] = 8;
+ else if (dir[i] == 6) dir[i] = 4;
+ else if (dir[i] == 4) dir[i] = 6;
+ }
+ }
+
+ /* Select the desired one if possible */
+ for (i = 0; i < n; i++)
+ {
+ if ((dir[i] == pdir) &&
+ (cave[y + ddy[dir[i]]][x + ddx[dir[i]]].mana == mana))
+ {
+ return dir[i];
+ }
+ }
+
+ /* If not select a random one */
+ if (n > 2)
+ {
+ byte nb = 200;
+
+ while (nb)
+ {
+ nb--;
+
+ r = rand_int(n);
+ if ((dir[r] != 5) && (yx_to_dir(y, x, oy, ox) != dir[r])) break;
+ }
+ return dir[r];
+ }
+ /* If nothing is found return 5 */
+ else return 5;
+}
+
+
+/*
+ * Determine the path taken by a projection.
+ *
+ * The projection will always start from the grid (y1,x1), and will travel
+ * towards the grid (y2,x2), touching one grid per unit of distance along
+ * the major axis, and stopping when it enters the destination grid or a
+ * wall grid, or has travelled the maximum legal distance of "range".
+ *
+ * Note that "distance" in this function (as in the "update_view()" code)
+ * is defined as "MAX(dy,dx) + MIN(dy,dx)/2", which means that the player
+ * actually has an "octagon of projection" not a "circle of projection".
+ *
+ * The path grids are saved into the grid array pointed to by "gp", and
+ * there should be room for at least "range" grids in "gp". Note that
+ * due to the way in which distance is calculated, this function normally
+ * uses fewer than "range" grids for the projection path, so the result
+ * of this function should never be compared directly to "range". Note
+ * that the initial grid (y1,x1) is never saved into the grid array, not
+ * even if the initial grid is also the final grid. XXX XXX XXX
+ *
+ * The "flg" flags can be used to modify the behavior of this function.
+ *
+ * In particular, the "PROJECT_STOP" and "PROJECT_THRU" flags have the same
+ * semantics as they do for the "project" function, namely, that the path
+ * will stop as soon as it hits a monster, or that the path will continue
+ * through the destination grid, respectively.
+ *
+ * The "PROJECT_JUMP" flag, which for the "project()" function means to
+ * start at a special grid (which makes no sense in this function), means
+ * that the path should be "angled" slightly if needed to avoid any wall
+ * grids, allowing the player to "target" any grid which is in "view".
+ * This flag is non-trivial and has not yet been implemented, but could
+ * perhaps make use of the "vinfo" array (above). XXX XXX XXX
+ *
+ * This function returns the number of grids (if any) in the path. This
+ * function will return zero if and only if (y1,x1) and (y2,x2) are equal.
+ *
+ * This algorithm is similar to, but slightly different from, the one used
+ * by "update_view_los()", and very different from the one used by "los()".
+ */
+sint project_path(u16b *gp, int range, int y1, int x1, int y2, int x2, int flg)
+{
+ int y, x, mana = 0, dir = 0;
+
+ int n = 0;
+ int k = 0;
+
+ /* Absolute */
+ int ay, ax;
+
+ /* Offsets */
+ int sy, sx;
+
+ /* Fractions */
+ int frac;
+
+ /* Scale factors */
+ int full, half;
+
+ /* Slope */
+ int m;
+
+
+ /* No path necessary (or allowed) */
+ if ((x1 == x2) && (y1 == y2)) return (0);
+
+ /* Hack -- to make a bolt/beam/ball follow a mana path */
+ if (flg & PROJECT_MANA_PATH)
+ {
+ int oy = y1, ox = x1, pdir = yx_to_dir(y2, x2, y1, x1);
+
+ /* Get the mana path level to follow */
+ mana = cave[y1][x1].mana;
+
+ /* Start */
+ dir = get_mana_path_dir(y1, x1, y1, x1, pdir, mana);
+ y = y1 + ddy[dir];
+ x = x1 + ddx[dir];
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp[n++] = GRID(y, x);
+
+ /* Hack -- Check maximum range */
+ if (n >= range + 10) return n;
+
+ /* Always stop at non-initial wall grids */
+ if ((n > 0) && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) return n;
+
+ /* Sometimes stop at non-initial monsters/players */
+ if (flg & (PROJECT_STOP))
+ {
+ if ((n > 0) && (cave[y][x].m_idx != 0)) return n;
+ }
+
+ /* Get the new direction */
+ dir = get_mana_path_dir(y, x, oy, ox, pdir, mana);
+ if (dir == 5) return n;
+ oy = y;
+ ox = x;
+ y += ddy[dir];
+ x += ddx[dir];
+ }
+ }
+
+ /* Analyze "dy" */
+ if (y2 < y1)
+ {
+ ay = (y1 - y2);
+ sy = -1;
+ }
+ else
+ {
+ ay = (y2 - y1);
+ sy = 1;
+ }
+
+ /* Analyze "dx" */
+ if (x2 < x1)
+ {
+ ax = (x1 - x2);
+ sx = -1;
+ }
+ else
+ {
+ ax = (x2 - x1);
+ sx = 1;
+ }
+
+
+ /* Number of "units" in one "half" grid */
+ half = (ay * ax);
+
+ /* Number of "units" in one "full" grid */
+ full = half << 1;
+
+
+ /* Vertical */
+ if (ay > ax)
+ {
+ /* Start at tile edge */
+ frac = ax * ax;
+
+ /* Let m = ((dx/dy) * full) = (dx * dx * 2) = (frac * 2) */
+ m = frac << 1;
+
+ /* Start */
+ y = y1 + sy;
+ x = x1;
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp[n++] = GRID(y, x);
+
+ /* Hack -- Check maximum range */
+ if ((n + (k >> 1)) >= range) break;
+
+ /* Sometimes stop at destination grid */
+ if (!(flg & (PROJECT_THRU)))
+ {
+ if ((x == x2) && (y == y2)) break;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((n > 0) && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x)) && !(flg & PROJECT_WALL)) break;
+
+ /* Sometimes stop at non-initial monsters/players */
+ if (flg & (PROJECT_STOP))
+ {
+ if ((n > 0) && (cave[y][x].m_idx != 0)) break;
+ }
+
+ /* Slant */
+ if (m)
+ {
+ /* Advance (X) part 1 */
+ frac += m;
+
+ /* Horizontal change */
+ if (frac >= half)
+ {
+ /* Advance (X) part 2 */
+ x += sx;
+
+ /* Advance (X) part 3 */
+ frac -= full;
+
+ /* Track distance */
+ k++;
+ }
+ }
+
+ /* Advance (Y) */
+ y += sy;
+ }
+ }
+
+ /* Horizontal */
+ else if (ax > ay)
+ {
+ /* Start at tile edge */
+ frac = ay * ay;
+
+ /* Let m = ((dy/dx) * full) = (dy * dy * 2) = (frac * 2) */
+ m = frac << 1;
+
+ /* Start */
+ y = y1;
+ x = x1 + sx;
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp[n++] = GRID(y, x);
+
+ /* Hack -- Check maximum range */
+ if ((n + (k >> 1)) >= range) break;
+
+ /* Sometimes stop at destination grid */
+ if (!(flg & (PROJECT_THRU)))
+ {
+ if ((x == x2) && (y == y2)) break;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((n > 0) && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x)) && !(flg & PROJECT_WALL)) break;
+
+ /* Sometimes stop at non-initial monsters/players */
+ if (flg & (PROJECT_STOP))
+ {
+ if ((n > 0) && (cave[y][x].m_idx != 0)) break;
+ }
+
+ /* Slant */
+ if (m)
+ {
+ /* Advance (Y) part 1 */
+ frac += m;
+
+ /* Vertical change */
+ if (frac >= half)
+ {
+ /* Advance (Y) part 2 */
+ y += sy;
+
+ /* Advance (Y) part 3 */
+ frac -= full;
+
+ /* Track distance */
+ k++;
+ }
+ }
+
+ /* Advance (X) */
+ x += sx;
+ }
+ }
+
+ /* Diagonal */
+ else
+ {
+ /* Start */
+ y = y1 + sy;
+ x = x1 + sx;
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp[n++] = GRID(y, x);
+
+ /* Hack -- Check maximum range */
+ if ((n + (n >> 1)) >= range) break;
+
+ /* Sometimes stop at destination grid */
+ if (!(flg & (PROJECT_THRU)))
+ {
+ if ((x == x2) && (y == y2)) break;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((n > 0) && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x)) && !(flg & PROJECT_WALL)) break;
+
+ /* Sometimes stop at non-initial monsters/players */
+ if (flg & (PROJECT_STOP))
+ {
+ if ((n > 0) && (cave[y][x].m_idx != 0)) break;
+ }
+
+ /* Advance (Y) */
+ y += sy;
+
+ /* Advance (X) */
+ x += sx;
+ }
+ }
+
+
+ /* Length */
+ return (n);
+}
+
+
+
+/*
+ * Mega-Hack -- track "affected" monsters (see "project()" comments)
+ */
+static int project_m_n;
+static int project_m_x;
+static int project_m_y;
+
+
+
+/*
+ * We are called from "project()" to "damage" terrain features
+ *
+ * We are called both for "beam" effects and "ball" effects.
+ *
+ * The "r" parameter is the "distance from ground zero".
+ *
+ * Note that we determine if the player can "see" anything that happens
+ * by taking into account: blindness, line-of-sight, and illumination.
+ *
+ * We return "TRUE" if the effect of the projection is "obvious".
+ *
+ * XXX XXX XXX We also "see" grids which are "memorized", probably a hack
+ *
+ * XXX XXX XXX Perhaps we should affect doors?
+ */
+static bool_ project_f(int who, int r, int y, int x, int dam, int typ)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ bool_ obvious = FALSE;
+
+ bool_ flag = FALSE;
+
+ bool_ seen;
+
+
+ /* XXX XXX XXX */
+ who = who ? who : 0;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+ /* Remember if the grid is with the LoS of player */
+ seen = player_can_see_bold(y, x);
+
+ /* Analyze the type */
+ switch (typ)
+ {
+ /* Ignore most effects */
+ case GF_ELEC:
+ case GF_SOUND:
+ case GF_MANA:
+ case GF_PSI:
+ case GF_PSI_DRAIN:
+ case GF_TELEKINESIS:
+ case GF_DOMINATION:
+ {
+ break;
+ }
+
+ case GF_COLD:
+ case GF_ICE:
+ {
+ int percent = c_ptr->feat == GF_COLD ? 20 : 50;
+
+ /* Only affects "boring" grids */
+ if (!cave_plain_floor_bold(y, x)) break;
+
+ if (rand_int(100) < percent)
+ {
+ cave_set_feat(y, x, FEAT_ICE);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case GF_BETWEEN_GATE:
+ {
+ int y1 = randint(cur_hgt) - 1;
+ int x1 = randint(cur_wid) - 1;
+ int y2 = y1;
+ int x2 = x1;
+ int tries = 1000;
+
+ /*
+ * Avoid "interesting" and/or permanent features
+ *
+ * If we can make sure that all the "permanent" features
+ * have the remember flag set as well, we can simplify
+ * the conditional... -- pelpel
+ */
+ if (!cave_plain_floor_bold(y, x) ||
+ (f_info[cave[y][x].feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Destination shouldn't be "interesting" either */
+ while (tries &&
+ (!cave_plain_floor_bold(y2, x2) ||
+ (f_info[cave[y2][x2].feat].flags1 & FF1_PERMANENT)))
+ {
+ y2 = y1 = randint(cur_hgt) - 1;
+ x2 = x1 = randint(cur_wid) - 1;
+ scatter(&y2, &x2, y1, x1, 20);
+ tries --;
+ }
+
+ /* No boarding grids found */
+ if (!tries) break;
+
+ /* Place a pair of between gates */
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ cave[y][x].special = x2 + (y2 << 8);
+
+ cave_set_feat(y2, x2, FEAT_BETWEEN);
+ cave[y2][x2].special = x + (y << 8);
+
+ if (seen)
+ {
+ obvious = TRUE;
+ note_spot(y, x);
+ }
+
+ if (player_can_see_bold(y2, x2))
+ {
+ obvious = TRUE;
+ note_spot(y2, x2);
+ }
+
+ break;
+ }
+
+ /* Burn trees & melt ice */
+ case GF_FIRE:
+ case GF_METEOR:
+ case GF_PLASMA:
+ case GF_HOLY_FIRE:
+ case GF_HELL_FIRE:
+ {
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Trees *will* burn */
+ if (c_ptr->feat == FEAT_TREES)
+ {
+ cave_set_feat(y, x, FEAT_DEAD_TREE);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ inc_piety(GOD_YAVANNA, -50);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Trees *will* burn */
+ if (c_ptr->feat == FEAT_SMALL_TREES)
+ {
+ cave_set_feat(y, x, FEAT_DEAD_SMALL_TREE);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ inc_piety(GOD_YAVANNA, -60);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Ice can melt (chance == 30%) */
+ else if (c_ptr->feat == FEAT_ICE)
+ {
+ int k = rand_int(100);
+
+ if (k >= 30) break;
+
+ /* Melt ice */
+ if (k < 10) cave_set_feat(y, x, FEAT_DIRT);
+ else if (k < 30) cave_set_feat(y, x, FEAT_SHAL_WATER);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Floors can become ash or lava (chance == 25%) */
+ else if (f_info[c_ptr->feat].flags1 & FF1_FLOOR)
+ {
+ int k = rand_int(100);
+
+ if (k >= 25) break;
+
+ /* Burn floor */
+ if (k < 10) cave_set_feat(y, x, FEAT_SHAL_LAVA);
+ else if (k < 25) cave_set_feat(y, x, FEAT_ASH);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Sandwall can be turned into glass (chance == 30%) */
+ else if ((c_ptr->feat == FEAT_SANDWALL) ||
+ (c_ptr->feat == FEAT_SANDWALL_H) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ int k = rand_int(100);
+
+ /* Glass it */
+ if (k < 30)
+ {
+ cave_set_feat(y, x, FEAT_GLASS_WALL);
+
+ /* Visibility change */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ if (seen) obvious = TRUE;
+ }
+
+ }
+
+ break;
+ }
+
+ case GF_WAVE:
+ case GF_WATER:
+ {
+ int p1 = 0;
+ int p2 = 0;
+ int f1 = 0;
+ int f2 = 0;
+ int f = 0;
+ int k;
+
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Needs more than 30 damage */
+ if (dam < 30) break;
+
+ if ((c_ptr->feat == FEAT_FLOOR) ||
+ (c_ptr->feat == FEAT_DIRT) ||
+ (c_ptr->feat == FEAT_GRASS))
+ {
+ /* 35% chance to create shallow water */
+ p1 = 35;
+ f1 = FEAT_SHAL_WATER;
+
+ /* 5% chance to create deep water */
+ p2 = 40;
+ f2 = FEAT_DEEP_WATER;
+ }
+ else if ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_MAGMA_H) ||
+ (c_ptr->feat == FEAT_MAGMA_K) ||
+ (c_ptr->feat == FEAT_SHAL_LAVA))
+ {
+ /* 15% chance to convert it to normal floor */
+ p1 = 15;
+ f1 = FEAT_FLOOR;
+ }
+ else if (c_ptr->feat == FEAT_DEEP_LAVA)
+ {
+ /* 10% chance to convert it to shallow lava */
+ p1 = 10;
+ f1 = FEAT_SHAL_LAVA;
+
+ /* 5% chance to convert it to normal floor */
+ p2 = 15;
+ f2 = FEAT_FLOOR;
+ }
+ else if ((c_ptr->feat == FEAT_SHAL_WATER) ||
+ (c_ptr->feat == FEAT_DARK_PIT))
+ {
+ /* 10% chance to convert it to deep water */
+ p1 = 10;
+ f1 = FEAT_DEEP_WATER;
+ }
+
+ k = rand_int(100);
+
+ if (k < p1) f = f1;
+ else if (k < p2) f = f2;
+
+ if (f)
+ {
+ if (f == FEAT_FLOOR) place_floor_convert_glass(y, x);
+ else cave_set_feat(y, x, f);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case GF_NETHER:
+ case GF_NEXUS:
+ case GF_ACID:
+ case GF_SHARDS:
+ case GF_TIME:
+ case GF_FORCE:
+ case GF_NUKE:
+ {
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ if ((c_ptr->feat == FEAT_TREES) ||
+ (c_ptr->feat == FEAT_SMALL_TREES))
+ {
+ /* Destroy the grid */
+ cave_set_feat(y, x, FEAT_DEAD_TREE);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ inc_piety(GOD_YAVANNA, -50);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case GF_DISINTEGRATE:
+ {
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ if (((c_ptr->feat == FEAT_TREES) ||
+ (c_ptr->feat == FEAT_SMALL_TREES) ||
+ (f_info[c_ptr->feat].flags1 & FF1_FLOOR)) &&
+ (rand_int(100) < 30))
+ {
+ /* Flow change */
+ if (c_ptr->feat == FEAT_TREES) p_ptr->update |= (PU_FLOW);
+
+ cave_set_feat(y, x, FEAT_ASH);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ if (c_ptr->feat == FEAT_TREES || c_ptr->feat == FEAT_SMALL_TREES)
+ inc_piety(GOD_YAVANNA, -50);
+
+ /* Visibility change */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ /* Destroy Traps (and Locks) */
+ case GF_KILL_TRAP:
+ {
+ /* Destroy normal traps and disarm monster traps */
+ if ((c_ptr->t_idx != 0) || (c_ptr->feat == FEAT_MON_TRAP))
+ {
+ /* Check line of sight */
+ if (player_has_los_bold(y, x))
+ {
+ msg_print("There is a bright flash of light!");
+ obvious = TRUE;
+ }
+
+ /* Forget the trap */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT);
+
+ /* Destroy normal traps */
+ c_ptr->t_idx = 0;
+
+ /* Disarm monster traps */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ c_ptr->special = c_ptr->special2 = 0;
+
+ /* Remove the feature */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_PERMANENT))
+ place_floor_convert_glass(y, x);
+ }
+
+ /* Hack -- Force redraw */
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+
+ /* Secret / Locked doors are found and unlocked */
+ else if ((c_ptr->feat == FEAT_SECRET) ||
+ ((c_ptr->feat >= FEAT_DOOR_HEAD + 0x01) &&
+ (c_ptr->feat <= FEAT_DOOR_HEAD + 0x07)))
+ {
+
+ /* Check line of sound */
+ if (player_has_los_bold(y, x))
+ {
+ msg_print("Click!");
+ obvious = TRUE;
+ }
+
+ /* Remove feature mimic */
+ cave[y][x].mimic = 0;
+
+ /* Unlock the door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+ }
+
+ break;
+ }
+
+ /* Destroy Doors (and traps) */
+ case GF_KILL_DOOR:
+ {
+ /* Destroy all doors and traps, and disarm monster traps */
+ if ((c_ptr->feat == FEAT_OPEN) ||
+ (c_ptr->feat == FEAT_BROKEN) ||
+ (c_ptr->t_idx != 0) ||
+ (c_ptr->feat == FEAT_MON_TRAP) ||
+ ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)))
+ {
+ /* Check line of sight */
+ if (player_has_los_bold(y, x))
+ {
+ /* Message */
+ msg_print("There is a bright flash of light!");
+ obvious = TRUE;
+
+ /* Visibility change */
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+ }
+ }
+
+ /* Forget the door */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT);
+
+ /* Remove normal traps */
+ c_ptr->t_idx = 0;
+
+ /* Disarm monster traps */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ c_ptr->special = c_ptr->special2 = 0;
+
+ /* Remove the feature */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_PERMANENT))
+ place_floor_convert_glass(y, x);
+
+ /* Hack -- Force redraw */
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+
+ break;
+ }
+
+ case GF_JAM_DOOR: /* Jams a door (as if with a spike) */
+ {
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Convert "locked" to "stuck" XXX XXX XXX */
+ if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08) c_ptr->feat += 0x08;
+
+ /* Add one spike to the door */
+ if (c_ptr->feat < FEAT_DOOR_TAIL) c_ptr->feat++;
+
+ /* Check line of sight */
+ if (player_has_los_bold(y, x))
+ {
+ /* Message */
+ msg_print("The door seems stuck.");
+ obvious = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ /* Destroy walls (and doors) */
+ case GF_KILL_WALL:
+ {
+ /* Non-walls (etc) */
+ if (cave_floor_bold(y, x)) break;
+
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Granite -- How about other wall types? */
+ if ((c_ptr->feat >= FEAT_WALL_EXTRA) &&
+ (c_ptr->feat <= FEAT_WALL_SOLID))
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The wall turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the wall */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Quartz / Magma / Sand with treasure */
+ else if (((c_ptr->feat >= FEAT_MAGMA_H) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K)) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The vein turns into mud!");
+ msg_print("You have found something!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the wall */
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Place some gold */
+ place_gold(y, x);
+ }
+
+ /* Quartz / Magma / Sand */
+ else if ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_QUARTZ) ||
+ (c_ptr->feat == FEAT_SANDWALL) ||
+ (c_ptr->feat == FEAT_SANDWALL_H))
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The vein turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the wall */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Rubble */
+ else if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The rubble turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the rubble */
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Hack -- place an object */
+ if (rand_int(100) < 10)
+ {
+ /* Found something */
+ if (seen)
+ {
+ msg_print("There was something buried in the rubble!");
+ obvious = TRUE;
+ }
+
+ /* Place gold */
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_RUBBLE);
+ }
+ }
+
+ /* Destroy doors (and secret doors) */
+ else if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) ||
+ (c_ptr->feat == FEAT_SECRET))
+ {
+ /* Hack -- special message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The door turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Remove mimic */
+ c_ptr->mimic = 0;
+
+ /* Destroy the feature */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+
+ break;
+ }
+
+ /* Make doors */
+ case GF_MAKE_DOOR:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Create a closed door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+
+ /* Observe */
+ if (c_ptr->info & (CAVE_MARK)) obvious = TRUE;
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ break;
+ }
+
+ /* Make traps */
+ case GF_MAKE_TRAP:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ /* Place a trap */
+ place_trap(y, x);
+
+ break;
+ }
+
+
+ case GF_MAKE_GLYPH:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ cave_set_feat(y, x, FEAT_GLYPH);
+
+ if (seen) obvious = TRUE;
+
+ break;
+ }
+
+
+
+ case GF_STONE_WALL:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+ if (!(f_info[c_ptr->feat].flags1 & FF1_FLOOR)) break;
+
+ /* Place a wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+
+ if (seen) obvious = TRUE;
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ break;
+ }
+
+ case GF_WINDS_MANA:
+ {
+ if (dam >= 256)
+ {
+ /* With erase mana */
+
+ /* Absorb some of the mana of the grid */
+ p_ptr->csp += cave[y][x].mana / 80;
+ if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp;
+
+ /* Set the new amount */
+ cave[y][x].mana = dam - 256;
+ }
+ else
+ {
+ /* Without erase mana */
+ int amt = cave[y][x].mana + dam;
+
+ /* Check if not overflow */
+ if (amt > 255) amt = 255;
+
+ /* Set the new amount */
+ cave[y][x].mana = amt;
+ }
+
+ break;
+ }
+
+ case GF_LAVA_FLOW:
+ {
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Shallow Lava */
+ if (dam == 1)
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_naked_bold(y, x)) break;
+
+ /* Place a shallow lava */
+ cave_set_feat(y, x, FEAT_SHAL_LAVA);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Deep Lava */
+ else
+ {
+ /* Require a "naked" floor grid */
+ if (cave_perma_bold(y, x) || !dam) break;
+
+ /* Place a deep lava */
+ cave_set_feat(y, x, FEAT_DEEP_LAVA);
+
+ if (seen) obvious = TRUE;
+
+ /* Dam is used as a counter for the number of grid to convert */
+ dam--;
+ }
+
+ break;
+ }
+
+ /* Lite up the grid */
+ case GF_LITE_WEAK:
+ case GF_LITE:
+ {
+ /* Turn on the light */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /* Observe */
+ if (seen) obvious = TRUE;
+
+ /*
+ * Mega-Hack -- Update the monster in the affected grid
+ * This allows "spear of light" (etc) to work "correctly"
+ */
+ if (c_ptr->m_idx) update_mon(c_ptr->m_idx, FALSE);
+
+ break;
+ }
+
+ /* Darken the grid */
+ case GF_DARK_WEAK:
+ case GF_DARK:
+ {
+ /* Notice */
+ if (seen) obvious = TRUE;
+
+ /* Turn off the light. */
+ c_ptr->info &= ~(CAVE_GLOW);
+
+ /* Hack -- Forget "boring" grids */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Forget */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ note_spot(y, x);
+ }
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /*
+ * Mega-Hack -- Update the monster in the affected grid
+ * This allows "spear of light" (etc) to work "correctly"
+ */
+ if (c_ptr->m_idx) update_mon(c_ptr->m_idx, FALSE);
+
+ /* All done */
+ break;
+ }
+
+ case GF_DESTRUCTION:
+ {
+ int t;
+
+ /* Lose room and vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* Lose light and knowledge */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+
+ /* Hack -- Notice player affect */
+ if ((x == p_ptr->px) && (y == p_ptr->py))
+ {
+ /* Hurt the player later */
+ flag = TRUE;
+
+ /* Do not hurt this grid */
+ break;
+ ;
+ }
+
+ /* Delete the monster (if any) */
+ delete_monster(y, x);
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Destroy "valid" grids */
+ if (cave_valid_bold(y, x))
+ {
+ /* Delete objects */
+ delete_object(y, x);
+
+ /* Wall (or floor) type */
+ t = rand_int(200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 60)
+ {
+ /* Create quartz vein */
+ cave_set_feat(y, x, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 90)
+ {
+ /* Create magma vein */
+ cave_set_feat(y, x, FEAT_MAGMA);
+ }
+
+ /* Sand */
+ else if (t < 110)
+ {
+ /* Create sand vein */
+ cave_set_feat(y, x, FEAT_SANDWALL);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Visibility and flow changes */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+ }
+
+ obvious = TRUE;
+ break;
+ }
+ default:
+ {
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "dd", "(s,d,d,d,d,d,d)", "grid", who, typ, dam, r, y, x))
+ {
+ obvious = process_hooks_return[0].num;
+ flag = process_hooks_return[1].num;
+ }
+ break;
+ }
+ }
+
+ /* Hack -- Affect player */
+ if (flag)
+ {
+ /* Message */
+ msg_print("There is a searing blast of light!");
+
+ /* Blind the player */
+ if (!p_ptr->resist_blind && !p_ptr->resist_lite)
+ {
+ /* Become blind */
+ (void)set_blind(p_ptr->blind + 10 + randint(10));
+ }
+ }
+
+ /* Return "Anything seen?" */
+ return (obvious);
+}
+
+
+/* Array of raisable ego monster */
+#define MAX_RAISE 10
+static int raise_ego[MAX_RAISE] =
+{
+ 1, /* Skeleton */
+ 1, /* Skeleton */
+ 1, /* Skeleton */
+ 1, /* Skeleton */
+ 2, /* Zombie */
+ 2, /* Zombie */
+ 2, /* Zombie */
+ 4, /* Spectre */
+ 4, /* Spectre */
+ 3, /* Lich */
+};
+
+
+/*
+ * We are called from "project()" to "damage" objects
+ *
+ * We are called both for "beam" effects and "ball" effects.
+ *
+ * Perhaps we should only SOMETIMES damage things on the ground.
+ *
+ * The "r" parameter is the "distance from ground zero".
+ *
+ * Note that we determine if the player can "see" anything that happens
+ * by taking into account: blindness, line-of-sight, and illumination.
+ *
+ * XXX XXX XXX We also "see" grids which are "memorized", probably a hack
+ *
+ * We return "TRUE" if the effect of the projection is "obvious".
+ */
+static bool_ project_o(int who, int r, int y, int x, int dam, int typ)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ bool_ obvious = FALSE;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ char o_name[80];
+
+ int o_sval = 0;
+ bool_ is_potion = FALSE;
+
+
+ /* XXX XXX XXX */
+ who = who ? who : 0;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ bool_ is_art = FALSE;
+ bool_ ignore = FALSE;
+ bool_ plural = FALSE;
+ bool_ do_kill = FALSE;
+
+ cptr note_kill = NULL;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Get the "plural"-ness */
+ if (o_ptr->number > 1) plural = TRUE;
+
+ /* Check for artifact */
+ if ((artifact_p(o_ptr) || o_ptr->art_name)) is_art = TRUE;
+
+ /* Analyze the type */
+ switch (typ)
+ {
+ /* makes corpses explode */
+ case GF_CORPSE_EXPL:
+ {
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+ s32b dama, radius = 7;
+
+ if (r_ptr->flags1 & RF1_FORCE_MAXHP)
+ dama = maxroll(r_ptr->hdice, r_ptr->hside);
+ else
+ dama = damroll(r_ptr->hdice, r_ptr->hside);
+
+ /* Adjust the damage */
+ dama = dama * dam / 100;
+
+ /* Adjust the radius */
+ radius = radius * dam / 100;
+
+ do_kill = TRUE;
+ note_kill = (plural ? " explode!" : " explodes!");
+ project(who, radius, y, x, dama, GF_SHARDS, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL);
+ }
+ break;
+ }
+
+ /* Acid -- Lots of things */
+ case GF_ACID:
+ {
+ if (hates_acid(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " melt!" : " melts!");
+ if (f3 & (TR3_IGNORE_ACID)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Elec -- Rings and Wands */
+ case GF_ELEC:
+ {
+ if (hates_elec(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ if (f3 & (TR3_IGNORE_ELEC)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Fire -- Flammable objects */
+ case GF_FIRE:
+ {
+ if (hates_fire(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " burn up!" : " burns up!");
+ if (f3 & (TR3_IGNORE_FIRE)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Cold -- potions and flasks */
+ case GF_COLD:
+ {
+ if (hates_cold(o_ptr))
+ {
+ note_kill = (plural ? " shatter!" : " shatters!");
+ do_kill = TRUE;
+ if (f3 & (TR3_IGNORE_COLD)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Fire + Elec */
+ case GF_PLASMA:
+ {
+ if (hates_fire(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " burn up!" : " burns up!");
+ if (f3 & (TR3_IGNORE_FIRE)) ignore = TRUE;
+ }
+ if (hates_elec(o_ptr))
+ {
+ ignore = FALSE;
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ if (f3 & (TR3_IGNORE_ELEC)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Fire + Cold */
+ case GF_METEOR:
+ {
+ if (hates_fire(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " burn up!" : " burns up!");
+ if (f3 & (TR3_IGNORE_FIRE)) ignore = TRUE;
+ }
+ if (hates_cold(o_ptr))
+ {
+ ignore = FALSE;
+ do_kill = TRUE;
+ note_kill = (plural ? " shatter!" : " shatters!");
+ if (f3 & (TR3_IGNORE_COLD)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Hack -- break potions and such */
+ case GF_ICE:
+ case GF_SHARDS:
+ case GF_FORCE:
+ case GF_SOUND:
+ {
+ if (hates_cold(o_ptr))
+ {
+ note_kill = (plural ? " shatter!" : " shatters!");
+ do_kill = TRUE;
+ }
+ break;
+ }
+
+ /* Mana and Chaos -- destroy everything */
+ case GF_MANA:
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ break;
+ }
+
+ case GF_DISINTEGRATE:
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " evaporate!" : " evaporates!");
+ break;
+ }
+
+ case GF_CHAOS:
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ if (f2 & (TR2_RES_CHAOS)) ignore = TRUE;
+ break;
+ }
+
+ /* Holy Fire and Hell Fire -- destroys cursed non-artifacts */
+ case GF_HOLY_FIRE:
+ case GF_HELL_FIRE:
+ {
+ if (cursed_p(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ }
+ break;
+ }
+
+ /* Unlock chests */
+ case GF_KILL_TRAP:
+ case GF_KILL_DOOR:
+ {
+ /* Chests are noticed only if trapped or locked */
+ if (o_ptr->tval == TV_CHEST)
+ {
+ /* Disarm/Unlock traps */
+ if (o_ptr->pval > 0)
+ {
+ /* Disarm or Unlock */
+ o_ptr->pval = (0 - o_ptr->pval);
+
+ /* Identify */
+ object_known(o_ptr);
+
+ /* Notice */
+ if (o_ptr->marked)
+ {
+ msg_print("Click!");
+ obvious = TRUE;
+ }
+ }
+ }
+
+ break;
+ }
+ case GF_STAR_IDENTIFY:
+ {
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Mark the item as fully known */
+ o_ptr->ident |= (IDENT_MENTAL);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", 0 - this_o_idx, "full");
+
+ /* Squelch ! */
+ squeltch_grid();
+
+ break;
+ }
+ case GF_IDENTIFY:
+ {
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", 0 - this_o_idx, "normal");
+
+ /* Squelch ! */
+ squeltch_grid();
+
+ break;
+ }
+ case GF_RAISE:
+ {
+ get_pos_player(7, &y, &x);
+
+ /* Only corpses can be raised */
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ int ego = raise_ego[rand_int(MAX_RAISE)];
+
+ if (place_monster_one(y, x, o_ptr->pval2, ego, FALSE, (!who) ? MSTATUS_PET : MSTATUS_ENEMY))
+ msg_print("A monster rises from the grave!");
+ do_kill = TRUE;
+ }
+ break;
+ }
+ case GF_RAISE_DEMON:
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+ cptr name;
+
+ if (o_ptr->tval != TV_CORPSE) break;
+
+ if (randint(100) > r_ptr->level - p_ptr->lev)
+ {
+ if (r_ptr->level < 10) name = "Manes";
+ else if (r_ptr->level < 18) name = "Tengu";
+ else if (r_ptr->level < 26) name = "Imp";
+ else if (r_ptr->level < 34) name = "Arch-vile";
+ else if (r_ptr->level < 42) name = "Bodak";
+ else if (r_ptr->level < 50) name = "Erynies";
+ else if (r_ptr->level < 58) name = "Vrock";
+ else if (r_ptr->level < 66) name = "Hezrou";
+ else if (r_ptr->level < 74) name = "Glabrezu";
+ else if (r_ptr->level < 82) name = "Nalfeshnee";
+ else if (r_ptr->level < 90) name = "Marilith";
+ else name = "Nycadaemon";
+
+ if (place_monster_one(y, x, test_monster_name(name), 0, FALSE, (!who) ? MSTATUS_PET : MSTATUS_ENEMY))
+ msg_print("A demon emerges from Hell!");
+ }
+
+ do_kill = TRUE;
+ break;
+ }
+ default:
+ {
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "dd", "(s,d,d,d,d,d,d,O)", "object", who, typ, dam, r, y, x, o_ptr))
+ {
+ obvious = process_hooks_return[0].num;
+ do_kill = process_hooks_return[1].num;
+ }
+ break;
+ }
+ }
+
+
+ /* Attempt to destroy the object */
+ if (do_kill)
+ {
+ /* Effect "observed" */
+ if (o_ptr->marked)
+ {
+ obvious = TRUE;
+ object_desc(o_name, o_ptr, FALSE, 0);
+ }
+
+ /* Artifacts, and other objects, get to resist */
+ if (is_art || ignore)
+ {
+ /* Observe the resist */
+ if (o_ptr->marked)
+ {
+ msg_format("The %s %s unaffected!",
+ o_name, (plural ? "are" : "is"));
+ }
+ }
+
+ /* Kill it */
+ else
+ {
+ /* Describe if needed */
+ if (o_ptr->marked && note_kill)
+ {
+ msg_format("The %s%s", o_name, note_kill);
+ }
+
+ o_sval = o_ptr->sval;
+ is_potion = ((k_info[o_ptr->k_idx].tval == TV_POTION) || (k_info[o_ptr->k_idx].tval == TV_POTION2));
+
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ /* Potions produce effects when 'shattered' */
+ if (is_potion)
+ {
+ (void)potion_smash_effect(who, y, x, o_sval);
+ }
+
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+ }
+
+ /* Return "Anything seen?" */
+ return (obvious);
+}
+
+/* Can the monster be hurt ? */
+bool_ hurt_monster(monster_type *m_ptr)
+{
+ if (m_ptr->status == MSTATUS_COMPANION) return FALSE;
+ else return TRUE;
+}
+
+/*
+ * Helper function for "project()" below.
+ *
+ * Handle a beam/bolt/ball causing damage to a monster.
+ *
+ * This routine takes a "source monster" (by index) which is mostly used to
+ * determine if the player is causing the damage, and a "radius" (see below),
+ * which is used to decrease the power of explosions with distance, and a
+ * location, via integers which are modified by certain types of attacks
+ * (polymorph and teleport being the obvious ones), a default damage, which
+ * is modified as needed based on various properties, and finally a "damage
+ * type" (see below).
+ *
+ * Note that this routine can handle "no damage" attacks (like teleport) by
+ * taking a "zero" damage, and can even take "parameters" to attacks (like
+ * confuse) by accepting a "damage", using it to calculate the effect, and
+ * then setting the damage to zero. Note that the "damage" parameter is
+ * divided by the radius, so monsters not at the "epicenter" will not take
+ * as much damage (or whatever)...
+ *
+ * Note that "polymorph" is dangerous, since a failure in "place_monster()"'
+ * may result in a dereference of an invalid pointer. XXX XXX XXX
+ *
+ * Various messages are produced, and damage is applied.
+ *
+ * Just "casting" a substance (i.e. plasma) does not make you immune, you must
+ * actually be "made" of that substance, or "breathe" big balls of it.
+ *
+ * We assume that "Plasma" monsters, and "Plasma" breathers, are immune
+ * to plasma.
+ *
+ * We assume "Nether" is an evil, necromantic force, so it doesn't hurt undead,
+ * and hurts evil less. If can breath nether, then it resists it as well.
+ *
+ * Damage reductions use the following formulas:
+ * Note that "dam = dam * 6 / (randint(6) + 6);"
+ * gives avg damage of .655, ranging from .858 to .500
+ * Note that "dam = dam * 5 / (randint(6) + 6);"
+ * gives avg damage of .544, ranging from .714 to .417
+ * Note that "dam = dam * 4 / (randint(6) + 6);"
+ * gives avg damage of .444, ranging from .556 to .333
+ * Note that "dam = dam * 3 / (randint(6) + 6);"
+ * gives avg damage of .327, ranging from .427 to .250
+ * Note that "dam = dam * 2 / (randint(6) + 6);"
+ * gives something simple.
+ *
+ * In this function, "result" messages are postponed until the end, where
+ * the "note" string is appended to the monster name, if not NULL. So,
+ * to make a spell have "no effect" just set "note" to NULL. You should
+ * also set "notice" to FALSE, or the player will learn what the spell does.
+ *
+ * We attempt to return "TRUE" if the player saw anything "useful" happen.
+ */
+bool_ project_m(int who, int r, int y, int x, int dam, int typ)
+{
+ int tmp;
+
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ char killer [80];
+
+ cptr name = (r_name + r_ptr->name);
+
+ /* Is the monster "seen"? */
+ bool_ seen;
+
+ /* Were the effects "obvious" (if seen)? */
+ bool_ obvious = FALSE;
+
+ /* Were the effects "irrelevant"? */
+ bool_ skipped = FALSE;
+
+
+ /* Move setting */
+ int x1 = 0;
+ int y1 = 0;
+ int a = 0;
+ int b = 0;
+ int do_move = 0;
+
+ /* Polymorph setting (true or false) */
+ int do_poly = 0;
+
+ /* Teleport setting (max distance) */
+ int do_dist = 0;
+
+ /* Confusion setting (amount to confuse) */
+ int do_conf = 0;
+
+ /* Stunning setting (amount to stun) */
+ int do_stun = 0;
+
+ /* Bleeding amount */
+ int do_cut = 0;
+
+ /* Poison amount */
+ int do_pois = 0;
+
+ /* Sleep amount (amount to sleep) */
+ int do_sleep = 0;
+
+ /* Fear amount (amount to fear) */
+ int do_fear = 0;
+
+
+ /* Hold the monster name */
+ char m_name[80];
+
+ /* Assume no note */
+ cptr note = NULL;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+
+ /* Nobody here */
+ if (!c_ptr->m_idx) return (FALSE);
+
+ /* Never affect projector */
+ if (who && (c_ptr->m_idx == who)) return (FALSE);
+
+ /*
+ * Don't affect already dead monsters
+ * Prevents problems with chain reactions of exploding monsters
+ */
+ if (m_ptr->hp < 0) return (FALSE);
+
+
+ /* Remember if the monster is within player's line of sight */
+ seen = (m_ptr->ml && ((who != -101) && (who != -100))) ? TRUE : FALSE;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+
+ /* Get the monster name (BEFORE polymorphing) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Mega Gachk */
+ if (r_ptr->flags2 & RF2_DEATH_ORB)
+ {
+ msg_format("%^s is immune to magic.", m_name);
+ return seen;
+ }
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+ if (!who && (is_friend(m_ptr) >= 0))
+ {
+ bool_ get_angry = FALSE;
+ /* Grrr? */
+ switch (typ)
+ {
+ case GF_AWAY_UNDEAD:
+ case GF_AWAY_EVIL:
+ case GF_AWAY_ALL:
+ case GF_CHARM:
+ case GF_CHARM_UNMOVING:
+ case GF_STAR_CHARM:
+ case GF_CONTROL_UNDEAD:
+ case GF_CONTROL_ANIMAL:
+ case GF_CONTROL_DEMON:
+ case GF_OLD_HEAL:
+ case GF_OLD_SPEED:
+ case GF_DARK_WEAK:
+ case GF_JAM_DOOR:
+ case GF_RAISE:
+ case GF_RAISE_DEMON:
+ case GF_IDENTIFY:
+ break; /* none of the above anger */
+ case GF_TRAP_DEMONSOUL:
+ if (r_ptr->flags3 & RF3_DEMON)
+ get_angry = TRUE;
+ break;
+ case GF_KILL_WALL:
+ if (r_ptr->flags3 & (RF3_HURT_ROCK))
+ get_angry = TRUE;
+ break;
+ case GF_HOLY_FIRE:
+ if (!(r_ptr->flags3 & (RF3_GOOD)))
+ get_angry = TRUE;
+ break;
+ case GF_TURN_UNDEAD:
+ case GF_DISP_UNDEAD:
+ if (r_ptr->flags3 & RF3_UNDEAD)
+ get_angry = TRUE;
+ break;
+ case GF_TURN_EVIL:
+ case GF_DISP_EVIL:
+ if (r_ptr->flags3 & RF3_EVIL)
+ get_angry = TRUE;
+ break;
+ case GF_DISP_GOOD:
+ if (r_ptr->flags3 & RF3_GOOD)
+ get_angry = TRUE;
+ break;
+ case GF_DISP_DEMON:
+ if (r_ptr->flags3 & RF3_DEMON)
+ get_angry = TRUE;
+ break;
+ case GF_DISP_LIVING:
+ case GF_UNBREATH:
+ if (!(r_ptr->flags3 & (RF3_UNDEAD)) &&
+ !(r_ptr->flags3 & (RF3_NONLIVING)))
+ get_angry = TRUE;
+ break;
+ case GF_PSI:
+ case GF_PSI_DRAIN:
+ if (!(r_ptr->flags2 & (RF2_EMPTY_MIND)))
+ get_angry = TRUE;
+ break;
+ case GF_DOMINATION:
+ if (!(r_ptr->flags3 & (RF3_NO_CONF)))
+ get_angry = TRUE;
+ break;
+ case GF_OLD_POLY:
+ case GF_OLD_CLONE:
+ if (randint(8) == 1)
+ get_angry = TRUE;
+ break;
+ case GF_LITE:
+ case GF_LITE_WEAK:
+ if (r_ptr->flags3 & RF3_HURT_LITE)
+ get_angry = TRUE;
+ break;
+ default:
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "d", "(s,d,d,d,d,d,d,M)", "angry", who, typ, dam, r, y, x, m_ptr))
+ {
+ get_angry = process_hooks_return[0].num;
+ }
+ else
+ get_angry = TRUE;
+ }
+
+ /* Now anger it if appropriate */
+ if (get_angry == TRUE && !(who))
+ {
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ if (change_side(m_ptr)) msg_format("%^s gets angry!", m_name);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+ }
+
+
+ /* Analyze the damage type */
+ switch (typ)
+ {
+ case GF_ATTACK:
+ {
+ if (seen) obvious = TRUE;
+
+ py_attack(y, x, dam);
+
+ skipped = TRUE;
+
+ dam = 0;
+ break;
+ }
+
+ case GF_IDENTIFY:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Probe */
+ do_probe(c_ptr->m_idx);
+
+ dam = 0;
+ break;
+ }
+
+ /* Death -- instant death */
+ case GF_DEATH:
+ {
+ if (seen) obvious = TRUE;
+
+ if (r_ptr->r_flags1 & RF1_UNIQUE)
+ {
+ note = " resists.";
+ dam = 0;
+ }
+ else
+ {
+ /* It KILLS */
+ dam = 32535;
+ }
+ break;
+ }
+ /* Magic Missile -- pure damage */
+ case GF_MISSILE:
+ {
+ if (seen) obvious = TRUE;
+ break;
+ }
+
+ /* Acid */
+ case GF_ACID:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags9 & (RF9_SUSCEP_ACID))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ if (seen) r_ptr->r_flags9 |= (RF9_SUSCEP_ACID);
+ }
+ if (r_ptr->flags3 & (RF3_IM_ACID))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_ACID);
+ }
+ break;
+ }
+
+ /* Electricity */
+ case GF_ELEC:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags9 & (RF9_SUSCEP_ELEC))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ if (seen) r_ptr->r_flags9 |= (RF9_SUSCEP_ELEC);
+ }
+ if (r_ptr->flags3 & (RF3_IM_ELEC))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_ELEC);
+ }
+ break;
+ }
+
+ /* Fire damage */
+ case GF_FIRE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_SUSCEP_FIRE))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ if (seen) r_ptr->r_flags3 |= (RF3_SUSCEP_FIRE);
+ }
+ if (r_ptr->flags3 & (RF3_IM_FIRE))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_FIRE);
+ }
+ break;
+ }
+
+ /* Cold */
+ case GF_COLD:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_SUSCEP_COLD))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ if (seen) r_ptr->r_flags3 |= (RF3_SUSCEP_COLD);
+ }
+ if (r_ptr->flags3 & (RF3_IM_COLD))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_COLD);
+ }
+ break;
+ }
+
+ /* Poison */
+ case GF_POIS:
+ {
+ if (seen) obvious = TRUE;
+ if (magik(25)) do_pois = (10 + randint(11) + r) / (r + 1);
+ if (r_ptr->flags9 & (RF9_SUSCEP_POIS))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ do_pois *= 2;
+ if (seen) r_ptr->r_flags9 |= (RF9_SUSCEP_POIS);
+ }
+ if (r_ptr->flags3 & (RF3_IM_POIS))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ do_pois = 0;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_POIS);
+ }
+ break;
+ }
+
+
+ /* Thick Poison */
+ case GF_UNBREATH:
+ {
+ if (seen) obvious = TRUE;
+ if (magik(15)) do_pois = (10 + randint(11) + r) / (r + 1);
+ if ((r_ptr->flags3 & (RF3_NONLIVING)) || (r_ptr->flags3 & (RF3_UNDEAD)))
+ {
+ note = " is immune.";
+ dam = 0;
+ do_pois = 0;
+ }
+ break;
+ }
+
+ /* Nuclear waste */
+ case GF_NUKE:
+ {
+ if (seen) obvious = TRUE;
+
+ if (r_ptr->flags3 & (RF3_IM_POIS))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_POIS);
+ }
+ else if (randint(3) == 1) do_poly = TRUE;
+ break;
+ }
+
+ /* Holy Orb -- hurts Evil (replaced with Hellfire) */
+ case GF_HELL_FIRE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ dam *= 2;
+ note = " is hit hard.";
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+ break;
+ }
+
+ /* Holy Fire -- hurts Evil, Good are immune, others _resist_ */
+ case GF_HOLY_FIRE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_GOOD))
+ {
+ dam = 0;
+ note = " is immune.";
+ if (seen) r_ptr->r_flags3 |= (RF3_GOOD);
+ }
+ else if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ dam *= 2;
+ note = " is hit hard.";
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+ else
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+ /* Arrow -- XXX no defense */
+ case GF_ARROW:
+ {
+ if (seen) obvious = TRUE;
+ break;
+ }
+
+ /* Plasma -- XXX perhaps check ELEC or FIRE */
+ case GF_PLASMA:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_RES_PLAS))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen)
+ r_ptr->r_flags3 |= (RF3_RES_PLAS);
+ }
+ break;
+ }
+
+ /* Nether -- see above */
+ case GF_NETHER:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ note = " is immune.";
+ dam = 0;
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+ else if (r_ptr->flags3 & (RF3_RES_NETH))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_NETH);
+ }
+ else if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ dam /= 2;
+ note = " resists somewhat.";
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+ break;
+ }
+
+ /* Water (acid) damage -- Water spirits/elementals are immune */
+ case GF_WATER:
+ {
+ if (seen) obvious = TRUE;
+ if ((r_ptr->d_char == 'E') &&
+ (prefix(name, "W") ||
+ (strstr((r_name + r_ptr->name), "Unmaker"))))
+ {
+ note = " is immune.";
+ dam = 0;
+ }
+ else if (r_ptr->flags3 & (RF3_RES_WATE))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_WATE);
+ }
+ break;
+ }
+
+ /* Wave = Water + Force */
+ case GF_WAVE:
+ {
+ if (seen) obvious = TRUE;
+ if ((r_ptr->d_char == 'E') &&
+ (prefix(name, "W") ||
+ (strstr((r_name + r_ptr->name), "Unmaker"))))
+ {
+ note = " is immune.";
+ dam = 0;
+ }
+ else if (r_ptr->flags3 & (RF3_RES_WATE))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_WATE);
+ }
+
+ if (who == 0)
+ {
+ a = 0;
+ b = 0;
+
+ /* Get vector from firer to target */
+ x1 = (m_ptr->fx - p_ptr->px) * 10;
+ y1 = (m_ptr->fy - p_ptr->py) * 10;
+
+ /* Make sure no zero divides */
+ if (x1 == 0) x1 = 1;
+ if (y1 == 0) y1 = 1;
+
+ /* Select direction monster is being pushed */
+
+ /* Roughly horizontally */
+ if ((2*y1) / x1 == 0)
+ {
+ if (x1 > 0)
+ {
+ a = 1, b = 0;
+ }
+ else
+ {
+ a = -1, b = 0;
+ }
+ }
+
+ /* Roughly vertically */
+ else if ((2*x1) / y1 == 0)
+ {
+ if (y1 > 0)
+ {
+ a = 0, b = 1;
+ }
+ else
+ {
+ a = 0, b = -1;
+ }
+ }
+
+ /* Take diagonals */
+ else
+ {
+ if (y1 > 0)
+ {
+ b = 1;
+ }
+ else
+ {
+ b = -1;
+ }
+ if (x1 > 0)
+ {
+ a = 1;
+ }
+ else
+ {
+ a = -1;
+ }
+ }
+
+ /* Move monster 2 offsets back */
+ do_move = 2;
+
+ /* Old monster coords in x,y */
+ y1 = m_ptr->fy;
+ x1 = m_ptr->fx;
+
+ /* Monster move offsets in a,b */
+ note = " is thrown away.";
+ }
+ break;
+ }
+
+ /* Chaos -- Chaos breathers resist */
+ case GF_CHAOS:
+ {
+ if (seen) obvious = TRUE;
+ do_poly = TRUE;
+ do_conf = (5 + randint(11) + r) / (r + 1);
+ if ((r_ptr->flags4 & (RF4_BR_CHAO)) ||
+ ((r_ptr->flags3 & (RF3_DEMON)) && (randint(3) == 1)))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ do_poly = FALSE;
+ }
+ break;
+ }
+
+ /* Shards -- Shard breathers resist */
+ case GF_SHARDS:
+ {
+ if (seen) obvious = TRUE;
+ if (magik(33)) do_cut = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags4 & (RF4_BR_SHAR))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ do_cut = 0;
+ }
+ break;
+ }
+
+ /* Rocket: Shard resistance helps */
+ case GF_ROCKET:
+ {
+ if (seen) obvious = TRUE;
+
+ if (magik(12)) do_cut = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags4 & (RF4_BR_SHAR))
+ {
+ note = " resists somewhat.";
+ dam /= 2;
+ do_cut = 0;
+ }
+ break;
+ }
+
+
+ /* Sound -- Sound breathers resist */
+ case GF_SOUND:
+ {
+ if (seen) obvious = TRUE;
+ if (who <= 0)
+ {
+ if (rand_int(100 - p_ptr->lev) < 50)
+ do_stun = (10 + randint(15) + r) / (r + 1);
+ }
+ else
+ do_stun = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags4 & (RF4_BR_SOUN))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+ /* Confusion */
+ case GF_CONFUSION:
+ {
+ if (seen) obvious = TRUE;
+ do_conf = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags4 & (RF4_BR_CONF))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ else if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ note = " resists somewhat.";
+ dam /= 2;
+ }
+ break;
+ }
+
+ /* Disenchantment -- Breathers and Disenchanters resist */
+ case GF_DISENCHANT:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_RES_DISE))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_DISE);
+ }
+ break;
+ }
+
+ /* Nexus -- Breathers and Existers resist */
+ case GF_NEXUS:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_RES_NEXU))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_NEXU);
+ }
+ break;
+ }
+
+ /* Force */
+ case GF_FORCE:
+ {
+ if (seen) obvious = TRUE;
+
+ /*
+ * If fired by player, try pushing monster.
+ * First get vector from player to monster.
+ * x10 so we can use pseudo-fixed point maths.
+ *
+ * Really should use get_angle_to_grid (util.c)
+ */
+ if (who == 0)
+ {
+ a = 0;
+ b = 0;
+
+ /* Get vector from firer to target */
+ x1 = (m_ptr->fx - p_ptr->px) * 10;
+ y1 = (m_ptr->fy - p_ptr->py) * 10;
+
+ /* Make sure no zero divides */
+ if (x1 == 0) x1 = 1;
+ if (y1 == 0) y1 = 1;
+
+ /* Select direction monster is being pushed */
+
+ /* Roughly horizontally */
+ if ((2*y1) / x1 == 0)
+ {
+ if (x1 > 0)
+ {
+ a = 1, b = 0;
+ }
+ else
+ {
+ a = -1, b = 0;
+ }
+ }
+
+ /* Roughly vertically */
+ else if ((2*x1) / y1 == 0)
+ {
+ if (y1 > 0)
+ {
+ a = 0, b = 1;
+ }
+ else
+ {
+ a = 0, b = -1;
+ }
+ }
+
+ /* Take diagonals */
+ else
+ {
+ if (y1 > 0)
+ {
+ b = 1;
+ }
+ else
+ {
+ b = -1;
+ }
+ if (x1 > 0)
+ {
+ a = 1;
+ }
+ else
+ {
+ a = -1;
+ }
+ }
+
+ /* Move monster 2 offsets back */
+ do_move = 2;
+
+ /* Old monster coords in x,y */
+ y1 = m_ptr->fy;
+ x1 = m_ptr->fx;
+
+ /* Monster move offsets in a,b */
+ note = " is thrown away.";
+ }
+
+ /* --hack-- Only stun if a monster fired it */
+ else do_stun = (randint(15) + r) / (r + 1);
+
+ if (r_ptr->flags4 & (RF4_BR_WALL))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+ /* Inertia -- breathers resist */
+ case GF_INERTIA:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags4 & (RF4_BR_INER))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ /* Powerful monsters can resist */
+ if (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10)
+ {
+ obvious = FALSE;
+ }
+ /* Normal monsters slow down */
+ else
+ {
+ if (m_ptr->mspeed > 60) m_ptr->mspeed -= 10;
+ note = " starts moving slower.";
+ }
+ }
+ break;
+ }
+
+ /* Time -- breathers resist */
+ case GF_TIME:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags4 & (RF4_BR_TIME))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+ /* Gravity -- breathers resist */
+ case GF_GRAVITY:
+ {
+ bool_ resist_tele = FALSE;
+
+ if (seen) obvious = TRUE;
+
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resist_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resist_tele = TRUE;
+ }
+ }
+
+ if (!resist_tele) do_dist = 10;
+ else do_dist = 0;
+
+ if (r_ptr->flags4 & (RF4_BR_GRAV))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ do_dist = 0;
+ }
+ else
+ {
+ /* 1. slowness */
+ /* Powerful monsters can resist */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ obvious = FALSE;
+ }
+ /* Normal monsters slow down */
+ else
+ {
+ if (m_ptr->mspeed > 60) m_ptr->mspeed -= 10;
+ note = " starts moving slower.";
+ }
+
+ /* 2. stun */
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ }
+ break;
+ }
+
+ /* Pure damage */
+ case GF_MANA:
+ {
+ if (seen) obvious = TRUE;
+ break;
+ }
+
+
+ /* Pure damage */
+ case GF_DISINTEGRATE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_HURT_ROCK))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_ROCK);
+ note = " loses some skin!";
+ note_dies = " evaporates!";
+ dam *= 2;
+ }
+
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ if (rand_int(m_ptr->level + 10) > rand_int(p_ptr->lev))
+ {
+ note = " resists.";
+ dam >>= 3;
+ }
+ }
+ break;
+ }
+
+ case GF_FEAR:
+ {
+ if (r_ptr->flags3 & (RF3_NO_FEAR))
+ note = " is unaffected.";
+ else
+ set_afraid(p_ptr->afraid + (dam / 2) + randint(dam / 2));
+
+ /* No damage */
+ dam = 0;
+ break;
+ }
+
+ case GF_PSI:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags2 & RF2_EMPTY_MIND)
+ {
+ dam = 0;
+ note = " is immune!";
+ }
+ else if ((r_ptr->flags2 & RF2_STUPID) ||
+ (r_ptr->flags2 & RF2_WEIRD_MIND) ||
+ (r_ptr->flags3 & RF3_ANIMAL) ||
+ (m_ptr->level > randint(3 * dam)))
+ {
+ dam /= 3;
+ note = " resists.";
+
+ /* Powerful demons & undead can turn a mindcrafter's
+ * attacks back on them */
+ if (((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_DEMON)) &&
+ (m_ptr->level > p_ptr->lev / 2) &&
+ (randint(2) == 1))
+ {
+ note = NULL;
+ msg_format("%^s%s corrupted mind backlashes your attack!",
+ m_name, (seen ? "'s" : "s"));
+ /* Saving throw */
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ /* Injure +/- confusion */
+ monster_desc(killer, m_ptr, 0x88);
+ take_hit(dam, killer); /* has already been /3 */
+ if (randint(4) == 1)
+ {
+ switch (randint(4))
+ {
+ case 1:
+ set_confused(p_ptr->confused + 3 + randint(dam));
+ break;
+ case 2:
+ set_stun(p_ptr->stun + randint(dam));
+ break;
+ case 3:
+ {
+ if (r_ptr->flags3 & (RF3_NO_FEAR))
+ note = " is unaffected.";
+ else
+ set_afraid(p_ptr->afraid + 3 + randint(dam));
+ break;
+ }
+ default:
+ if (!p_ptr->free_act)
+ (void)set_paralyzed(p_ptr->paralyzed + randint(dam));
+ break;
+ }
+ }
+ }
+ dam = 0;
+ }
+ }
+
+ if ((dam > 0) && (randint(4) == 1))
+ {
+ switch (randint(4))
+ {
+ case 1:
+ do_conf = 3 + randint(dam);
+ break;
+ case 2:
+ do_stun = 3 + randint(dam);
+ break;
+ case 3:
+ do_fear = 3 + randint(dam);
+ break;
+ default:
+ do_sleep = 3 + randint(dam);
+ break;
+ }
+ }
+
+ note_dies = " collapses, a mindless husk.";
+ break;
+ }
+
+ case GF_PSI_DRAIN:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags2 & RF2_EMPTY_MIND)
+ {
+ dam = 0;
+ note = " is immune!";
+ }
+ else if ((r_ptr->flags2 & RF2_STUPID) ||
+ (r_ptr->flags2 & RF2_WEIRD_MIND) ||
+ (r_ptr->flags3 & RF3_ANIMAL) ||
+ (m_ptr->level > randint(3 * dam)))
+ {
+ dam /= 3;
+ note = " resists.";
+
+ /*
+ * Powerful demons & undead can turn a mindcrafter's
+ * attacks back on them
+ */
+ if (((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_DEMON)) &&
+ (m_ptr->level > p_ptr->lev / 2) &&
+ (randint(2) == 1))
+ {
+ note = NULL;
+ msg_format("%^s%s corrupted mind backlashes your attack!",
+ m_name, (seen ? "'s" : "s"));
+ /* Saving throw */
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ /* Injure + mana drain */
+ monster_desc(killer, m_ptr, 0x88);
+ msg_print("Your psychic energy is drained!");
+ p_ptr->csp = MAX(0, p_ptr->csp - damroll(5, dam) / 2);
+ p_ptr->redraw |= PR_MANA;
+ take_hit(dam, killer); /* has already been /3 */
+ }
+ dam = 0;
+ }
+ }
+ else if (dam > 0)
+ {
+ int b = damroll(5, dam) / 4;
+ msg_format("You convert %s%s pain into psychic energy!",
+ m_name, (seen ? "'s" : "s"));
+ b = MIN(p_ptr->msp, p_ptr->csp + b);
+ p_ptr->csp = b;
+ p_ptr->redraw |= PR_MANA;
+ }
+
+ note_dies = " collapses, a mindless husk.";
+ break;
+ }
+
+ case GF_TELEKINESIS:
+ {
+ if (seen) obvious = TRUE;
+ do_dist = 7;
+ /* 1. stun */
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > 5 + randint(dam)))
+ {
+ /* Resist */
+ do_stun = 0;
+ /* No obvious effect */
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ /* Meteor -- powerful magic missile */
+ case GF_METEOR:
+ {
+ if (seen) obvious = TRUE;
+ break;
+ }
+
+ case GF_DOMINATION:
+ {
+ if (is_friend(m_ptr) > 0) break;
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /*
+ * Powerful demons & undead can turn a mindcrafter's
+ * attacks back on them
+ */
+ if (((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_DEMON)) &&
+ (m_ptr->level > p_ptr->lev / 2) &&
+ (randint(2) == 1))
+ {
+ note = NULL;
+ msg_format("%^s%s corrupted mind backlashes your attack!",
+ m_name, (seen ? "'s" : "s"));
+ /* Saving throw */
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ /* Confuse, stun, terrify */
+ switch (randint(4))
+ {
+ case 1:
+ set_stun(p_ptr->stun + dam / 2);
+ break;
+ case 2:
+ set_confused(p_ptr->confused + dam / 2);
+ break;
+ default:
+ {
+ if (r_ptr->flags3 & (RF3_NO_FEAR))
+ note = " is unaffected.";
+ else
+ set_afraid(p_ptr->afraid + dam);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ }
+ else
+ {
+ if ((dam > 29) && (randint(100) < dam))
+ {
+ note = " is in your thrall!";
+ m_ptr->status = MSTATUS_PET;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+ else
+ {
+ switch (randint(4))
+ {
+ case 1:
+ do_stun = dam / 2;
+ break;
+ case 2:
+ do_conf = dam / 2;
+ break;
+ default:
+ do_fear = dam;
+ }
+ }
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+
+ /* Ice -- Cold + Cuts + Stun */
+ case GF_ICE:
+ {
+ if (seen) obvious = TRUE;
+ do_stun = (randint(15) + 1) / (r + 1);
+ if (magik(33)) do_cut = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags3 & (RF3_SUSCEP_COLD))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ do_cut *= 2;
+ if (seen) r_ptr->r_flags3 |= (RF3_SUSCEP_COLD);
+ }
+ if (r_ptr->flags3 & (RF3_IM_COLD))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ do_cut = 0;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_COLD);
+ }
+ break;
+ }
+
+
+ /* Drain Life */
+ case GF_OLD_DRAIN:
+ {
+ if (seen) obvious = TRUE;
+
+ if ((r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (strchr("Egv", r_ptr->d_char)))
+ {
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+ if (r_ptr->flags3 & (RF3_DEMON))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_DEMON);
+ }
+
+ note = " is unaffected!";
+ obvious = FALSE;
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Death Ray */
+ case GF_DEATH_RAY:
+ {
+ if (seen) obvious = TRUE;
+ if ((r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)))
+ {
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+
+ note = " is immune.";
+ obvious = FALSE;
+ dam = 0;
+ }
+ else if (((r_ptr->flags1 & (RF1_UNIQUE)) &&
+ (randint(888) != 666)) ||
+ (((m_ptr->level + randint(20)) > randint((dam) + randint(10))) &&
+ randint(100) != 66 ))
+ {
+ note = " resists!";
+ obvious = FALSE;
+ dam = 0;
+ }
+
+ else dam = (p_ptr->lev) * 200;
+
+ break;
+ }
+
+ /* Polymorph monster (Use "dam" as "power") */
+ case GF_OLD_POLY:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt to polymorph (see below) */
+ do_poly = TRUE;
+
+ /* Powerful monsters can resist */
+ if ((r_ptr->flags1 & RF1_UNIQUE) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ note = " is unaffected!";
+ do_poly = FALSE;
+ obvious = FALSE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+
+ break;
+ }
+
+
+ /* Clone monsters (Ignore "dam") */
+ case GF_OLD_CLONE:
+ {
+ bool_ is_frien = FALSE;
+
+ if (seen) obvious = TRUE;
+ if ((is_friend(m_ptr) > 0) && (randint(3) != 1))
+ is_frien = TRUE;
+
+ /* Heal fully */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Speed up */
+ if (m_ptr->mspeed < 150) m_ptr->mspeed += 10;
+
+ /* Attempt to clone. */
+ if (multiply_monster(c_ptr->m_idx, is_frien, TRUE))
+ {
+ note = " spawns!";
+ }
+
+ /* No "real" damage */
+ dam = 0;
+
+ break;
+ }
+
+
+ /* Heal Monster (use "dam" as amount of healing) */
+ case GF_OLD_HEAL:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Wake up */
+ m_ptr->csleep = 0;
+
+ /* Heal */
+ m_ptr->hp += dam;
+
+ /* No overflow */
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == c_ptr->m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Message */
+ note = " looks healthier.";
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Speed Monster (Ignore "dam") */
+ case GF_OLD_SPEED:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Speed up */
+ if (m_ptr->mspeed < m_ptr->speed + 15) m_ptr->mspeed += 10;
+ note = " starts moving faster.";
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Slow Monster (Use "dam" as "power") */
+ case GF_OLD_SLOW:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Powerful monsters can resist */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* Normal monsters slow down */
+ else
+ {
+ if (m_ptr->mspeed > 60) m_ptr->mspeed -= 10;
+ note = " starts moving slower.";
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Sleep (Use "dam" as "power") */
+ case GF_OLD_SLEEP:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_SLEEP)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_SLEEP))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_SLEEP);
+ }
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else
+ {
+ /* Go to sleep (much) later */
+ note = " falls asleep!";
+ do_sleep = 500;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Sleep (Use "dam" as "power") */
+ case GF_STASIS:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else
+ {
+ /* Go to sleep (much) later */
+ note = " is suspended!";
+ do_sleep = 500;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Charm monster */
+ case GF_CHARM:
+ {
+ dam += (adj_con_fix[p_ptr->stat_ind[A_CHR]] - 1);
+
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->mflag & MFLAG_QUEST) ||
+ (r_ptr->flags3 & RF3_NO_CONF) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 5))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ if (is_friend(m_ptr) < 0)
+ {
+ note = " suddenly seems friendly!";
+ m_ptr->status = MSTATUS_FRIEND;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* *Charm* monster */
+ case GF_STAR_CHARM:
+ {
+ dam += (adj_con_fix[p_ptr->stat_ind[A_CHR]] - 1);
+
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->mflag & MFLAG_QUEST) ||
+ (r_ptr->flags3 & RF3_NO_CONF) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 5))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ if (is_friend(m_ptr) < 0)
+ {
+ note = " suddenly seems friendly!";
+ if (can_create_companion()) m_ptr->status = MSTATUS_COMPANION;
+ else m_ptr->status = MSTATUS_PET;
+
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Control undead */
+ case GF_CONTROL_UNDEAD:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & RF1_UNIQUE) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & RF3_UNDEAD)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " is in your thrall!";
+ m_ptr->status = MSTATUS_PET;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Control never-moving */
+ case GF_CHARM_UNMOVING:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & RF1_UNIQUE) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags1 & RF1_NEVER_MOVE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " is in your thrall!";
+ m_ptr->status = MSTATUS_PET;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Tame animal */
+ case GF_CONTROL_ANIMAL:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & (RF3_ANIMAL))) ||
+ (r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " is tamed!";
+ m_ptr->status = MSTATUS_PET;
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Control demon */
+ case GF_CONTROL_DEMON:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & (RF3_DEMON))) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " obeys your commands!";
+ m_ptr->status = MSTATUS_PET;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Confusion (Use "dam" as "power") */
+ case GF_OLD_CONF:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Get confused later */
+ do_conf = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ case GF_STUN:
+ {
+ if (seen) obvious = TRUE;
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Confusion (Use "dam" as "power") */
+ case GF_CONF_DAM:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Get confused later */
+ do_conf = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ case GF_STUN_DAM:
+ {
+ if (seen) obvious = TRUE;
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ /* Implosion is the same than Stun_dam but only affect the living */
+ case GF_IMPLOSION:
+ {
+ if (seen) obvious = TRUE;
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* Non_living resists */
+ if (r_ptr->flags3 & (RF3_NONLIVING))
+ {
+ /* Resist */
+ do_stun = 0;
+ dam = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ /* Confusion & Stunning (Use "dam" as "power") */
+ case GF_STUN_CONF:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Get confused later */
+ do_conf = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+
+ /* Lite, but only hurts susceptible creatures */
+ case GF_LITE_WEAK:
+ {
+ /* Hurt by light */
+ if (r_ptr->flags3 & (RF3_HURT_LITE))
+ {
+ /* Obvious effect */
+ if (seen) obvious = TRUE;
+
+ /* Memorize the effects */
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
+
+ /* Special effect */
+ note = " cringes from the light!";
+ note_dies = " shrivels away in the light!";
+ }
+
+ /* Normally no damage */
+ else
+ {
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+
+
+ /* Lite -- opposite of Dark */
+ case GF_LITE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags4 & (RF4_BR_LITE))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ else if (r_ptr->flags3 & (RF3_HURT_LITE))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
+ note = " cringes from the light!";
+ note_dies = " shrivels away in the light!";
+ dam *= 2;
+ }
+ break;
+ }
+
+
+ /* Dark -- opposite of Lite */
+ case GF_DARK:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Likes darkness... */
+ if ((r_ptr->flags4 & (RF4_BR_DARK)) ||
+ (r_ptr->flags3 & RF3_ORC) ||
+ (r_ptr->flags3 & RF3_HURT_LITE))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+
+ /* Stone to Mud */
+ case GF_KILL_WALL:
+ {
+ /* Hurt by rock remover */
+ if (r_ptr->flags3 & (RF3_HURT_ROCK))
+ {
+ /* Notice effect */
+ if (seen) obvious = TRUE;
+
+ /* Memorize the effects */
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_ROCK);
+
+ /* Cute little message */
+ note = " loses some skin!";
+ note_dies = " dissolves!";
+ }
+
+ /* Usually, ignore the effects */
+ else
+ {
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+
+ /* Teleport undead (Use "dam" as "power") */
+ case GF_AWAY_UNDEAD:
+ {
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ /* Only affect undead */
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ bool_ resists_tele = FALSE;
+
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resists_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ if (seen) obvious = TRUE;
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ do_dist = dam;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Teleport evil (Use "dam" as "power") */
+ case GF_AWAY_EVIL:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ /* Only affect evil */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ bool_ resists_tele = FALSE;
+
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resists_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ if (seen) obvious = TRUE;
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+ do_dist = dam;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Teleport monster (Use "dam" as "power") */
+ case GF_AWAY_ALL:
+ {
+ bool_ resists_tele = FALSE;
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resists_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Prepare to teleport */
+ do_dist = dam;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Turn undead (Use "dam" as "power") */
+ case GF_TURN_UNDEAD:
+ {
+ /* Only affect undead */
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Apply some fear */
+ do_fear = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10)
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ do_fear = 0;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Turn evil (Use "dam" as "power") */
+ case GF_TURN_EVIL:
+ {
+ /* Only affect evil */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Apply some fear */
+ do_fear = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10)
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ do_fear = 0;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Turn monster (Use "dam" as "power") */
+ case GF_TURN_ALL:
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Apply some fear */
+ do_fear = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (r_ptr->flags3 & (RF3_NO_FEAR)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ do_fear = 0;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Dispel undead */
+ case GF_DISP_UNDEAD:
+ {
+ /* Only affect undead */
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+
+ /* Dispel evil */
+ case GF_DISP_EVIL:
+ {
+ /* Only affect evil */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel good */
+ case GF_DISP_GOOD:
+ {
+ /* Only affect good */
+ if (r_ptr->flags3 & (RF3_GOOD))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_GOOD);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel living */
+ case GF_DISP_LIVING:
+ {
+ /* Only affect non-undead */
+ if (!(r_ptr->flags3 & (RF3_UNDEAD)) &&
+ !(r_ptr->flags3 & (RF3_NONLIVING)))
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel demons */
+ case GF_DISP_DEMON:
+ {
+ /* Only affect demons */
+ if (r_ptr->flags3 & (RF3_DEMON))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_DEMON);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel monster */
+ case GF_DISP_ALL:
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+
+ break;
+ }
+
+ /* Raise Death -- Heal monster */
+ case GF_RAISE:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Wake up */
+ m_ptr->csleep = 0;
+
+ /* Heal */
+ m_ptr->hp += dam;
+
+ /* No overflow */
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == c_ptr->m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Message */
+ note = " looks healthier.";
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Trap the soul of a demon and leave body */
+ case GF_TRAP_DEMONSOUL:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Check race */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & (RF3_DEMON))))
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ dam = 0;
+ }
+ /* Hack : drop corpse if the demon is killed by this
+ * spell */
+ else if (dam > m_ptr->hp)
+ {
+ object_type forge, *i_ptr = &forge;
+
+ /* Wipe the object */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE));
+
+ /* Unique corpses are unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ object_aware(i_ptr);
+ i_ptr->name1 = 201;
+ }
+
+ /* Length of decay - very long time */
+ i_ptr->pval = 100000;
+
+ /* Set weight */
+ i_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1;
+
+ /* Remember what we are */
+ i_ptr->pval2 = m_ptr->r_idx;
+
+ /* Give HP */
+ i_ptr->pval3 = maxroll(r_ptr->hdice, r_ptr->hside);
+
+ /* Drop it */
+ drop_near(i_ptr, -1, y, x);
+ }
+
+ break;
+ }
+
+ /* Default */
+ default:
+ {
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "dddddddddss", "(s,d,d,d,d,d,d,M)", "monster", who, typ, dam, r, y, x, m_ptr))
+ {
+ obvious = process_hooks_return[0].num;
+ dam = process_hooks_return[1].num;
+ do_stun = process_hooks_return[2].num;
+ do_fear = process_hooks_return[3].num;
+ do_conf = process_hooks_return[4].num;
+ do_dist = process_hooks_return[5].num;
+ do_pois = process_hooks_return[6].num;
+ do_cut = process_hooks_return[7].num;
+ do_poly = process_hooks_return[8].num;
+ if (process_hooks_return[9].str != NULL)
+ note = process_hooks_return[9].str;
+ if (process_hooks_return[10].str != NULL)
+ note_dies = process_hooks_return[10].str;
+ }
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+ break;
+ }
+ }
+
+
+ /* Absolutely no effect */
+ if (skipped) return (FALSE);
+
+
+ /* "Unique" monsters cannot be polymorphed */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) do_poly = FALSE;
+
+ /*
+ * "Quest" monsters cannot be polymorphed
+ */
+ if (m_ptr->mflag & MFLAG_QUEST)
+ do_poly = FALSE;
+
+ /* "Unique" monsters can only be "killed" by the player unless they are player's friends */
+ if ((r_ptr->flags1 & RF1_UNIQUE) && (m_ptr->status <= MSTATUS_NEUTRAL_P))
+ {
+ /* Uniques may only be killed by the player */
+ if (who && (who != -2) && (dam > m_ptr->hp)) dam = m_ptr->hp;
+ }
+
+ /*
+ * "Quest" monsters can only be "killed" by the player
+ */
+ if (m_ptr->mflag & MFLAG_QUEST)
+ {
+ if ((who > 0) && (dam > m_ptr->hp)) dam = m_ptr->hp;
+ }
+
+ if (do_pois && (!(r_ptr->flags3 & RF3_IM_POIS)) && (!(r_ptr->flags3 & RF4_BR_POIS)) && hurt_monster(m_ptr))
+ {
+ if (m_ptr->poisoned) note = " is more poisoned.";
+ else note = " is poisoned.";
+ m_ptr->poisoned += do_pois;
+ }
+
+ if (do_cut && (!(r_ptr->flags4 & RF4_BR_WALL)) && hurt_monster(m_ptr))
+ {
+ if (m_ptr->bleeding) note = " bleeds more strongly.";
+ else note = " starts bleeding.";
+ m_ptr->bleeding += do_cut;
+ }
+
+ /* Check for death */
+ if ((dam > m_ptr->hp) && hurt_monster(m_ptr))
+ {
+ /* Extract method of death */
+ note = note_dies;
+ }
+
+ /* Mega-Hack -- Handle "polymorph" -- monsters get a saving throw */
+ else if (do_poly && cave_floor_bold(y, x) && (randint(90) > m_ptr->level))
+ {
+ /* Default -- assume no polymorph */
+ note = " is unaffected!";
+
+ /* Handle polymorph */
+ if (do_poly_monster(y, x))
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Monster polymorphs */
+ note = " changes!";
+
+ /* Turn off the damage */
+ dam = 0;
+
+ /* Hack -- Get new monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Hack -- Get new race */
+ r_ptr = race_inf(m_ptr);
+ }
+ }
+
+ /* Handle moving the monster.
+ *
+ * Note: This is a effect of force, but only when used
+ * by the player. (For the moment). The usual stun effect
+ * is not applied.
+ */
+ else if (do_move && hurt_monster(m_ptr))
+ {
+ int back = 0;
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ back = 0; /* Default of no movement */
+
+ /* How far can we push the monster? */
+ for (do_move = 1; do_move < 3; do_move++)
+ {
+ /* Get monster coords */
+ /* And offset position */
+ y1 = m_ptr->fy + (b * do_move);
+ x1 = m_ptr->fx + (a * do_move);
+
+ if (!in_bounds(y1, x1)) continue;
+
+ /* Require "empty" floor space */
+ if (!in_bounds(y1, x1) || !cave_empty_bold(y1, x1)) continue;
+
+ /* Hack -- no teleport onto glyph of warding */
+ if (cave[y1][x1].feat == FEAT_GLYPH) continue;
+
+ /* amount moved */
+ back = do_move;
+ }
+
+ /* Move the monster */
+ if (back)
+ {
+ y1 = m_ptr->fy + (b * back);
+ x1 = m_ptr->fx + (a * back);
+ monster_swap(m_ptr->fy, m_ptr->fx, y1, x1);
+
+ if (back == 2)
+ {
+ note = " is knocked back!";
+ }
+ if (back == 1)
+ {
+ note = " is knocked back and crushed!";
+
+ /* was kept from being pushed all the way, do extra dam */
+ dam = dam * 13 / 10;
+ }
+
+ /* Get new position */
+ y = y1;
+ x = x1;
+
+ /* Hack -- get new grid */
+ c_ptr = &cave[y][x];
+ }
+ else /* could not move the monster */
+ {
+ note = " is severely crushed!";
+
+ /* Do extra damage (1/3)*/
+ dam = dam * 15 / 10;
+ }
+
+ }
+
+ /* Handle "teleport" */
+ else if (do_dist)
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " disappears!";
+
+ /* Teleport */
+ teleport_away(c_ptr->m_idx, do_dist);
+
+ /* Hack -- get new location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Hack -- get new grid */
+ c_ptr = &cave[y][x];
+ }
+
+ /* Sound and Impact breathers never stun */
+ else if (do_stun &&
+ !(r_ptr->flags4 & (RF4_BR_SOUN)) &&
+ !(r_ptr->flags4 & (RF4_BR_WALL)) && hurt_monster(m_ptr))
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Get confused */
+ if (m_ptr->stunned)
+ {
+ note = " is more dazed.";
+ tmp = m_ptr->stunned + (do_stun / 2);
+ }
+ else
+ {
+ note = " is dazed.";
+ tmp = do_stun;
+ }
+
+ /* Apply stun */
+ m_ptr->stunned = (tmp < 200) ? tmp : 200;
+ }
+
+ /* Confusion and Chaos breathers (and sleepers) never confuse */
+ else if (do_conf &&
+ !(r_ptr->flags3 & (RF3_NO_CONF)) &&
+ !(r_ptr->flags4 & (RF4_BR_CONF)) &&
+ !(r_ptr->flags4 & (RF4_BR_CHAO)) && hurt_monster(m_ptr))
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Already partially confused */
+ if (m_ptr->confused)
+ {
+ note = " looks more confused.";
+ tmp = m_ptr->confused + (do_conf / 2);
+ }
+
+ /* Was not confused */
+ else
+ {
+ note = " looks confused.";
+ tmp = do_conf;
+ }
+
+ /* Apply confusion */
+ m_ptr->confused = (tmp < 200) ? tmp : 200;
+ }
+
+
+ /* Fear */
+ if (do_fear && hurt_monster(m_ptr))
+ {
+ /* Increase fear */
+ tmp = m_ptr->monfear + do_fear;
+
+ /* Set fear */
+ m_ptr->monfear = (tmp < 200) ? tmp : 200;
+ }
+
+
+ /* If another monster did the damage, hurt the monster by hand */
+ if (who > 0)
+ {
+ bool_ fear = FALSE;
+
+ /* Dead monster */
+ if (mon_take_hit_mon(who, c_ptr->m_idx, dam, &fear, note_dies))
+ {}
+
+ /* Damaged monster */
+ else
+ {
+ /* Give detailed messages if visible or destroyed */
+ if (note && seen) msg_format("%^s%s", m_name, note);
+
+ /* Hack -- Pain message */
+ else if (dam > 0) message_pain(c_ptr->m_idx, dam);
+
+ /* Hack -- handle sleep */
+ if (do_sleep) m_ptr->csleep = do_sleep;
+ }
+ }
+ /* If the player did it, give him experience, check fear */
+ else if (hurt_monster(m_ptr))
+ {
+ bool_ fear = FALSE;
+
+ /* Hurt the monster, check for fear and death */
+ if (mon_take_hit(c_ptr->m_idx, dam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* Damaged monster */
+ else
+ {
+ /* Give detailed messages if visible or destroyed */
+ if (note && seen) msg_format("%^s%s", m_name, note);
+
+ /* Hack -- Pain message */
+ else if (dam > 0) message_pain(c_ptr->m_idx, dam);
+
+ /* Take note */
+ if ((fear || do_fear) && (m_ptr->ml))
+ {
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+
+ /* Hack -- handle sleep */
+ if (do_sleep) m_ptr->csleep = do_sleep;
+ }
+ }
+
+
+ /* XXX XXX XXX Verify this code */
+
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, FALSE);
+
+ /* Redraw the monster grid */
+ lite_spot(y, x);
+
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+
+ /* Track it */
+ project_m_n++;
+ project_m_x = x;
+ project_m_y = y;
+
+
+ /* Return "Anything seen?" */
+ return (obvious);
+}
+
+
+/* Is the spell unsafe for the player ? */
+bool_ unsafe = FALSE;
+
+
+/*
+ * Helper function for "project()" below.
+ *
+ * Handle a beam/bolt/ball causing damage to the player.
+ *
+ * This routine takes a "source monster" (by index), a "distance", a default
+ * "damage", and a "damage type". See "project_m()" above.
+ *
+ * If "rad" is non-zero, then the blast was centered elsewhere, and the damage
+ * is reduced (see "project_m()" above). This can happen if a monster breathes
+ * at the player and hits a wall instead.
+ *
+ * NOTE (Zangband): 'Bolt' attacks can be reflected back, so we need to know
+ * if this is actually a ball or a bolt spell
+ *
+ *
+ * We return "TRUE" if any "obvious" effects were observed. XXX XXX Actually,
+ * we just assume that the effects were obvious, for historical reasons.
+ */
+static bool_ project_p(int who, int r, int y, int x, int dam, int typ, int a_rad)
+{
+ int k = 0, do_move = 0, a = 0, b = 0, x1 = 0, y1 = 0;
+
+ /* Hack -- assume obvious */
+ bool_ obvious = TRUE;
+
+ /* Player blind-ness */
+ bool_ blind = (p_ptr->blind ? TRUE : FALSE);
+
+ /* Player needs a "description" (he is blind) */
+ bool_ fuzzy = FALSE;
+
+ /* Source monster */
+ monster_type *m_ptr = NULL;
+
+ /* Monster name (for attacks) */
+ char m_name[80];
+
+ /* Monster name (for damage) */
+ char killer[80];
+
+ /* Hack -- messages */
+ cptr act = NULL;
+
+
+ /* Player is not here */
+ if ((x != p_ptr->px) || (y != p_ptr->py)) return (FALSE);
+
+ /* Player cannot hurt himself */
+ if ((!who) && (!unsafe)) return (FALSE);
+
+ /* Bolt attack from a monster */
+ if ((!a_rad) && get_skill(SKILL_DODGE) && (who > 0))
+ {
+ int chance = (p_ptr->dodge_chance - ((r_info[who].level * 5) / 6)) / 3;
+
+ if ((chance > 0) && magik(chance))
+ {
+ msg_print("You dodge a magical attack!");
+ return (TRUE);
+ }
+ }
+
+ /* Effects done by the plane cannot bounce */
+ if (p_ptr->reflect && !a_rad && !(randint(10) == 1) && ((who != -101) && (who != -100)))
+ {
+ int t_y, t_x;
+ int max_attempts = 10;
+
+ if (blind) msg_print("Something bounces!");
+ else msg_print("The attack bounces!");
+
+ /* Choose 'new' target */
+ do
+ {
+ t_y = m_list[who].fy - 1 + randint(3);
+ t_x = m_list[who].fx - 1 + randint(3);
+ max_attempts--;
+ }
+ while (max_attempts && in_bounds2(t_y, t_x) &&
+ !(player_has_los_bold(t_y, t_x)));
+
+ if (max_attempts < 1)
+ {
+ t_y = m_list[who].fy;
+ t_x = m_list[who].fx;
+ }
+
+ project(0, 0, t_y, t_x, dam, typ, (PROJECT_STOP | PROJECT_KILL));
+
+ disturb(1, 0);
+ return TRUE;
+ }
+
+ /* XXX XXX XXX */
+ /* Limit maximum damage */
+ if (dam > 1600) dam = 1600;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+
+ /* If the player is blind, be more descriptive */
+ if (blind) fuzzy = TRUE;
+
+ /* If the player is hit by a trap, be more descritive */
+ if (who == -2) fuzzy = TRUE;
+
+ /* Did ``God'' do it? */
+ if (who == -99)
+ {
+ if (p_ptr->pgod)
+ {
+ /* Find out the name of player's god. */
+ sprintf(killer, "%s",
+ deity_info[p_ptr->pgod].name);
+ }
+ else strcpy(killer, "Divine Wrath");
+ }
+
+ /* Did the dungeon do it? */
+ if (who == -100)
+ {
+ sprintf(killer, "%s",
+ d_name + d_info[dungeon_type].name);
+ }
+ if (who == -101)
+ {
+ sprintf(killer, "%s",
+ f_name + f_info[cave[p_ptr->py][p_ptr->px].feat].name);
+ }
+
+ if (who >= -1)
+ {
+ /* Get the source monster */
+ m_ptr = &m_list[who];
+
+ /* Get the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get the monster's real name */
+ monster_desc(killer, m_ptr, 0x88);
+ }
+
+ if (who == -2)
+ {
+ sprintf(killer, "%s",
+ t_name + t_info[cave[p_ptr->py][p_ptr->px].t_idx].name);
+ }
+
+ /* Analyze the damage */
+ switch (typ)
+ {
+ case GF_DEATH_RAY:
+ {
+ if (fuzzy) msg_print("You are hit by pure death!");
+ take_hit(32000, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_ACID:
+ {
+ if (fuzzy) msg_print("You are hit by acid!");
+ acid_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_FIRE:
+ {
+ if (fuzzy) msg_print("You are hit by fire!");
+ fire_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_COLD:
+ {
+ if (fuzzy) msg_print("You are hit by cold!");
+ cold_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_ELEC:
+ {
+ if (fuzzy) msg_print("You are hit by lightning!");
+ elec_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- also poisons player */
+ case GF_POIS:
+ {
+ if (fuzzy) msg_print("You are hit by poison!");
+ if (p_ptr->resist_pois) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_pois) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_pois || p_ptr->resist_pois)) &&
+ randint(HURT_CHANCE) == 1)
+ {
+ do_dec_stat(A_CON, STAT_DEC_NORMAL);
+ }
+
+ take_hit(dam, killer);
+
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(dam) + 10);
+ }
+ break;
+ }
+
+ /* Standard damage -- also poisons / mutates player */
+ case GF_NUKE:
+ {
+ if (fuzzy) msg_print("You are hit by radiation!");
+ if (p_ptr->resist_pois) dam = (2 * dam + 2) / 5;
+ if (p_ptr->oppose_pois) dam = (2 * dam + 2) / 5;
+ take_hit(dam, killer);
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(dam) + 10);
+
+ if (randint(5) == 1) /* 6 */
+ {
+ msg_print("You undergo a freakish metamorphosis!");
+ if (randint(4) == 1) /* 4 */
+ do_poly_self();
+ else
+ corrupt_player();
+ }
+
+ if (randint(6) == 1)
+ {
+ inven_damage(set_acid_destroy, 2);
+ }
+ }
+ break;
+ }
+
+ /* Standard damage */
+ case GF_MISSILE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Holy Orb -- Player only takes partial damage */
+ case GF_HOLY_FIRE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ case GF_HELL_FIRE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Arrow -- XXX no dodging */
+ case GF_ARROW:
+ {
+ if (fuzzy) msg_print("You are hit by something sharp!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Plasma -- XXX No resist */
+ case GF_PLASMA:
+ {
+ if (fuzzy) msg_print("You are hit by something *HOT*!");
+ take_hit(dam, killer);
+
+ if (!p_ptr->resist_sound)
+ {
+ int k = (randint((dam > 40) ? 35 : (dam * 3 / 4 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+
+ if (!(p_ptr->resist_fire ||
+ p_ptr->oppose_fire ||
+ p_ptr->immune_fire))
+ {
+ inven_damage(set_acid_destroy, 3);
+ }
+
+ break;
+ }
+
+ /* Nether -- drain experience */
+ case GF_NETHER:
+ {
+ if (fuzzy) msg_print("You are hit by nether forces!");
+ {
+ if (p_ptr->immune_neth)
+ {
+ dam = 0;
+ }
+ else if (p_ptr->resist_neth)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(200 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(200 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+
+ take_hit(dam, killer);
+ }
+
+ break;
+ }
+
+ /* Water -- stun/confuse */
+ case GF_WAVE:
+ case GF_WATER:
+ {
+ if (fuzzy) msg_print("You are hit by something wet!");
+ if (!p_ptr->resist_sound)
+ {
+ set_stun(p_ptr->stun + randint(40));
+ }
+ if (!p_ptr->resist_conf)
+ {
+ set_confused(p_ptr->confused + randint(5) + 5);
+ }
+
+ if (randint(5) == 1)
+ {
+ inven_damage(set_cold_destroy, 3);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Chaos -- many effects */
+ case GF_CHAOS:
+ {
+ if (fuzzy) msg_print("You are hit by a wave of anarchy!");
+ if (p_ptr->resist_chaos)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(20) + 10);
+ }
+ if (!p_ptr->resist_chaos)
+ {
+ (void)set_image(p_ptr->image + randint(10));
+ }
+ if (!p_ptr->resist_neth && !p_ptr->resist_chaos)
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(500 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(5000 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+ if ((!p_ptr->resist_chaos) || (randint(9) == 1))
+ {
+ inven_damage(set_elec_destroy, 2);
+ inven_damage(set_fire_destroy, 2);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Shards -- mostly cutting */
+ case GF_SHARDS:
+ {
+ if (fuzzy) msg_print("You are hit by something sharp!");
+ if (p_ptr->resist_shard)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ (void)set_cut(p_ptr->cut + dam);
+ }
+
+ if ((!p_ptr->resist_shard) || (randint(13) == 1))
+ {
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Sound -- mostly stunning */
+ case GF_SOUND:
+ {
+ if (fuzzy) msg_print("You are hit by a loud noise!");
+ if (p_ptr->resist_sound)
+ {
+ dam *= 5;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ int k = (randint((dam > 90) ? 35 : (dam / 3 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+
+ if ((!p_ptr->resist_sound) || (randint(13) == 1))
+ {
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Pure confusion */
+ case GF_CONFUSION:
+ {
+ if (fuzzy) msg_print("You are hit by something puzzling!");
+ if (p_ptr->resist_conf)
+ {
+ dam *= 5;
+ dam /= (randint(6) + 6);
+ }
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + randint(20) + 10);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Disenchantment -- see above */
+ case GF_DISENCHANT:
+ {
+ if (fuzzy) msg_print("You are hit by something static!");
+ if (p_ptr->resist_disen)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ (void)apply_disenchant(0);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Nexus -- see above */
+ case GF_NEXUS:
+ {
+ if (fuzzy) msg_print("You are hit by something strange!");
+ if (p_ptr->resist_nexus)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ apply_nexus(m_ptr);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Force -- mostly stun */
+ case GF_FORCE:
+ {
+ if (fuzzy) msg_print("You are hit by kinetic force!");
+ if (!p_ptr->resist_sound)
+ {
+ (void)set_stun(p_ptr->stun + randint(20));
+ /*
+ * If fired by player, try pushing monster.
+ * First get vector from player to monster.
+ * x10 so we can use pseudo-fixed point maths.
+ *
+ * Really should use get_angle_to_grid (util.c)
+ */
+ if (who > 0)
+ {
+ a = 0;
+ b = 0;
+
+ /* Get vector from firer to target */
+ x1 = (p_ptr->px - m_ptr->fx) * 10;
+ y1 = (p_ptr->py - m_ptr->fy) * 10;
+
+ /* Make sure no zero divides */
+ if (x1 == 0) x1 = 1;
+ if (y1 == 0) y1 = 1;
+
+ /* Select direction player is being pushed */
+
+ /* Roughly horizontally */
+ if ((2*y1) / x1 == 0)
+ {
+ if (x1 > 0)
+ {
+ a = 1, b = 0;
+ }
+ else
+ {
+ a = -1, b = 0;
+ }
+ }
+
+ /* Roughly vertically */
+ else if ((2*x1) / y1 == 0)
+ {
+ if (y1 > 0)
+ {
+ a = 0, b = 1;
+ }
+ else
+ {
+ a = 0, b = -1;
+ }
+ }
+
+ /* Take diagonals */
+ else
+ {
+ if (y1 > 0)
+ {
+ b = 1;
+ }
+ else
+ {
+ b = -1;
+ }
+ if (x1 > 0)
+ {
+ a = 1;
+ }
+ else
+ {
+ a = -1;
+ }
+ }
+
+ /* Move monster 2 offsets back */
+ do_move = 2;
+
+ /* Old monster coords in x,y */
+ y1 = p_ptr->py;
+ x1 = p_ptr->px;
+ }
+ }
+ else
+ take_hit(dam, killer);
+ break;
+ }
+
+
+ /* Rocket -- stun, cut */
+ case GF_ROCKET:
+ {
+ if (fuzzy) msg_print("There is an explosion!");
+ if (!p_ptr->resist_sound)
+ {
+ (void)set_stun(p_ptr->stun + randint(20));
+ }
+ if (p_ptr->resist_shard)
+ {
+ dam /= 2;
+ }
+ else
+ {
+ (void)set_cut(p_ptr-> cut + ( dam / 2) );
+ }
+
+ if ((!p_ptr->resist_shard) || (randint(12) == 1))
+ {
+ inven_damage(set_cold_destroy, 3);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Inertia -- slowness */
+ case GF_INERTIA:
+ {
+ if (fuzzy) msg_print("You are hit by something slow!");
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Lite -- blinding */
+ case GF_LITE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ if (p_ptr->resist_lite)
+ {
+ dam *= 4;
+ dam /= (randint(6) + 6);
+ }
+ else if (!blind && !p_ptr->resist_blind)
+ {
+ (void)set_blind(p_ptr->blind + randint(5) + 2);
+ }
+ if (p_ptr->sensible_lite)
+ {
+ msg_print("The light scorches your flesh!");
+ dam *= 3;
+ }
+ take_hit(dam, killer);
+
+ if (p_ptr->tim_wraith)
+ {
+ p_ptr->tim_wraith = 0;
+ msg_print("The light forces you out of your incorporeal shadow form.");
+
+ p_ptr->redraw |= PR_MAP;
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+
+ break;
+ }
+
+ /* Dark -- blinding */
+ case GF_DARK:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ if (p_ptr->resist_dark)
+ {
+ dam *= 4;
+ dam /= (randint(6) + 6);
+ }
+ else if (!blind && !p_ptr->resist_blind)
+ {
+ (void)set_blind(p_ptr->blind + randint(5) + 2);
+ }
+ if (p_ptr->wraith_form) hp_player(dam);
+ else take_hit(dam, killer);
+ break;
+ }
+
+ /* Time -- bolt fewer effects XXX */
+ case GF_TIME:
+ {
+ if (fuzzy) msg_print("You are hit by a blast from the past!");
+
+ if (p_ptr->resist_continuum)
+ {
+ dam *= 4;
+ dam /= (randint(6) + 6);
+ msg_print("You feel as if time is passing you by.");
+ }
+ else
+ {
+ switch (randint(10))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ msg_print("You feel life has clocked back.");
+ lose_exp(100 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ {
+ switch (randint(6))
+ {
+ case 1:
+ k = A_STR;
+ act = "strong";
+ break;
+ case 2:
+ k = A_INT;
+ act = "bright";
+ break;
+ case 3:
+ k = A_WIS;
+ act = "wise";
+ break;
+ case 4:
+ k = A_DEX;
+ act = "agile";
+ break;
+ case 5:
+ k = A_CON;
+ act = "hardy";
+ break;
+ case 6:
+ k = A_CHR;
+ act = "beautiful";
+ break;
+ }
+
+ msg_format("You're not as %s as you used to be...", act);
+
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+
+ case 10:
+ {
+ msg_print("You're not as powerful as you used to be...");
+
+ for (k = 0; k < 6; k++)
+ {
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ }
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+ }
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Gravity -- stun plus slowness plus teleport */
+ case GF_GRAVITY:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ if (fuzzy) msg_print("You are hit by something heavy!");
+ msg_print("Gravity warps around you.");
+ if (!unsafe)
+ {
+ teleport_player(5);
+ if (!p_ptr->ffall)
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ if (!(p_ptr->resist_sound || p_ptr->ffall))
+ {
+ int k = (randint((dam > 90) ? 35 : (dam / 3 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+ if (p_ptr->ffall)
+ {
+ dam = (dam * 2) / 3;
+ }
+
+ if ((!p_ptr->ffall) || (randint(13) == 1))
+ {
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ take_hit(dam, killer);
+ }
+ else
+ teleport_player(dam);
+ break;
+ }
+
+ /* Standard damage */
+ case GF_DISINTEGRATE:
+ {
+ if (fuzzy) msg_print("You are hit by pure energy!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ case GF_OLD_HEAL:
+ {
+ if (fuzzy) msg_print("You are hit by something invigorating!");
+ (void)hp_player(dam);
+ dam = 0;
+ break;
+ }
+
+ case GF_OLD_SPEED:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ (void)set_fast(p_ptr->fast + randint(5), 10);
+ dam = 0;
+ break;
+ }
+
+ case GF_OLD_SLOW:
+ {
+ if (fuzzy) msg_print("You are hit by something slow!");
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ break;
+ }
+
+ case GF_OLD_SLEEP:
+ {
+ if (p_ptr->free_act) break;
+ if (fuzzy) msg_print("You fall asleep!");
+ set_paralyzed(p_ptr->paralyzed + dam);
+ dam = 0;
+ break;
+ }
+
+ /* Pure damage */
+ case GF_MANA:
+ {
+ if (fuzzy) msg_print("You are hit by an aura of magic!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Pure damage */
+ case GF_METEOR:
+ {
+ if (fuzzy) msg_print("Something falls from the sky on you!");
+ take_hit(dam, killer);
+ if ((!p_ptr->resist_shard) || (randint(13) == 1))
+ {
+ if (!p_ptr->immune_fire) inven_damage(set_fire_destroy, 2);
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ break;
+ }
+
+ /* Ice -- cold plus stun plus cuts */
+ case GF_ICE:
+ {
+ if (fuzzy) msg_print("You are hit by something sharp and cold!");
+ cold_dam(dam, killer);
+ if (!p_ptr->resist_shard)
+ {
+ (void)set_cut(p_ptr->cut + damroll(5, 8));
+ }
+ if (!p_ptr->resist_sound)
+ {
+ (void)set_stun(p_ptr->stun + randint(15));
+ }
+
+ if ((!(p_ptr->resist_cold || p_ptr->oppose_cold)) || (randint(12) == 1))
+ {
+ if (!(p_ptr->immune_cold)) inven_damage(set_cold_destroy, 3);
+ }
+
+ break;
+ }
+
+ /* Knowledge */
+ case GF_IDENTIFY:
+ {
+ if (fuzzy) msg_print("You are hit by pure knowledge!");
+ self_knowledge(NULL);
+ break;
+ }
+
+ /* Psi -- ESP */
+ case GF_PSI:
+ {
+ if (fuzzy) msg_print("You are hit by pure psionic energy!");
+ set_tim_esp(p_ptr->tim_esp + dam);
+ break;
+ }
+
+ /* Statis -- paralyse */
+ case GF_STASIS:
+ {
+ if (fuzzy) msg_print("You are hit by something paralyzing!");
+ set_paralyzed(p_ptr->paralyzed + dam);
+ break;
+ }
+
+ /* Raise Death -- restore life */
+ case GF_RAISE:
+ {
+ if (fuzzy) msg_print("You are hit by pure anti-death energy!");
+ restore_level();
+ break;
+ }
+
+ /* Make Glyph -- Shield */
+ case GF_MAKE_GLYPH:
+ {
+ if (fuzzy) msg_print("You are hit by pure protection!");
+ set_shield(p_ptr->shield + dam, 50, 0, 0, 0);
+ break;
+ }
+
+ /* Default */
+ default:
+ {
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "dd", "(s,d,d,d,d,d,d)", "player", who, typ, dam, r, y, x))
+ {
+ obvious = process_hooks_return[0].num;
+ dam = process_hooks_return[1].num;
+ }
+ else
+ {
+ /* No damage */
+ dam = 0;
+ }
+ break;
+ }
+ }
+
+
+ /* Handle moving the player.
+ *
+ * Note: This is a effect of force
+ */
+ if (do_move)
+ {
+ int back = 0;
+
+ back = 0; /* Default of no movement */
+
+ /* How far can we push the monster? */
+ for (do_move = 1; do_move < 3; do_move++)
+ {
+ /* Get monster coords */
+ /* And offset position */
+ y1 = p_ptr->py + (b * do_move);
+ x1 = p_ptr->px + (a * do_move);
+
+ if (!in_bounds(y1, x1)) continue;
+
+ /* Require "empty" floor space */
+ if (!in_bounds(y1, x1) || !cave_empty_bold(y1, x1)) continue;
+
+ /* amount moved */
+ back = do_move;
+ }
+
+ /* Move the monster */
+ if (back)
+ {
+ y1 = p_ptr->py + (b * back);
+ x1 = p_ptr->px + (a * back);
+ swap_position(y1, x1);
+
+ if (back == 2)
+ {
+ msg_print("You are knocked back!");
+ }
+ if (back == 1)
+ {
+ msg_print("You are knocked back and crushed!");
+
+ /* was kept from being pushed all the way, do extra dam */
+ dam = dam * 13 / 10;
+ }
+
+ /* Get new position */
+ y = y1;
+ x = x1;
+ }
+ else /* could not move the monster */
+ {
+ msg_print("You are severely crushed!");
+
+ /* Do extra damage (1/3)*/
+ dam = dam * 15 / 10;
+ }
+
+ take_hit(dam, killer);
+
+ }
+
+
+ /* Disturb */
+ disturb(1, 0);
+
+
+ /* Return "Anything seen?" */
+ return (obvious);
+}
+
+
+
+/*
+ * Generic "beam"/"bolt"/"ball" projection routine.
+ *
+ * Input:
+ * who: Index of "source" monster (negative for "player")
+ * jk -- -2 for traps, only used with project_jump
+ * rad: Radius of explosion (0 = beam/bolt, 1 to 9 = ball)
+ * y,x: Target location (or location to travel "towards")
+ * dam: Base damage roll to apply to affected monsters (or player)
+ * typ: Type of damage to apply to monsters (and objects)
+ * flg: Extra bit flags (see PROJECT_xxxx in "defines.h")
+ *
+ * Return:
+ * TRUE if any "effects" of the projection were observed, else FALSE
+ *
+ * Allows a monster (or player) to project a beam/bolt/ball of a given kind
+ * towards a given location (optionally passing over the heads of interposing
+ * monsters), and have it do a given amount of damage to the monsters (and
+ * optionally objects) within the given radius of the final location.
+ *
+ * A "bolt" travels from source to target and affects only the target grid.
+ * A "beam" travels from source to target, affecting all grids passed through.
+ * A "ball" travels from source to the target, exploding at the target, and
+ * affecting everything within the given radius of the target location.
+ *
+ * Traditionally, a "bolt" does not affect anything on the ground, and does
+ * not pass over the heads of interposing monsters, much like a traditional
+ * missile, and will "stop" abruptly at the "target" even if no monster is
+ * positioned there, while a "ball", on the other hand, passes over the heads
+ * of monsters between the source and target, and affects everything except
+ * the source monster which lies within the final radius, while a "beam"
+ * affects every monster between the source and target, except for the casting
+ * monster (or player), and rarely affects things on the ground.
+ *
+ * Two special flags allow us to use this function in special ways, the
+ * "PROJECT_HIDE" flag allows us to perform "invisible" projections, while
+ * the "PROJECT_JUMP" flag allows us to affect a specific grid, without
+ * actually projecting from the source monster (or player).
+ *
+ * The player will only get "experience" for monsters killed by himself
+ * Unique monsters can only be destroyed by attacks from the player
+ *
+ * Only 256 grids can be affected per projection, limiting the effective
+ * "radius" of standard ball attacks to nine units (diameter nineteen).
+ *
+ * One can project in a given "direction" by combining PROJECT_THRU with small
+ * offsets to the initial location (see "line_spell()"), or by calculating
+ * "virtual targets" far away from the player.
+ *
+ * One can also use PROJECT_THRU to send a beam/bolt along an angled path,
+ * continuing until it actually hits something (useful for "stone to mud").
+ *
+ * Bolts and Beams explode INSIDE walls, so that they can destroy doors.
+ *
+ * Balls must explode BEFORE hitting walls, or they would affect monsters
+ * on both sides of a wall. Some bug reports indicate that this is still
+ * happening in 2.7.8 for Windows, though it appears to be impossible.
+ *
+ * We "pre-calculate" the blast area only in part for efficiency.
+ * More importantly, this lets us do "explosions" from the "inside" out.
+ * This results in a more logical distribution of "blast" treasure.
+ * It also produces a better (in my opinion) animation of the explosion.
+ * It could be (but is not) used to have the treasure dropped by monsters
+ * in the middle of the explosion fall "outwards", and then be damaged by
+ * the blast as it spreads outwards towards the treasure drop location.
+ *
+ * Walls and doors are included in the blast area, so that they can be
+ * "burned" or "melted" in later versions.
+ *
+ * This algorithm is intended to maximize simplicity, not necessarily
+ * efficiency, since this function is not a bottleneck in the code.
+ *
+ * We apply the blast effect from ground zero outwards, in several passes,
+ * first affecting features, then objects, then monsters, then the player.
+ * This allows walls to be removed before checking the object or monster
+ * in the wall, and protects objects which are dropped by monsters killed
+ * in the blast, and allows the player to see all affects before he is
+ * killed or teleported away. The semantics of this method are open to
+ * various interpretations, but they seem to work well in practice.
+ *
+ * We process the blast area from ground-zero outwards to allow for better
+ * distribution of treasure dropped by monsters, and because it provides a
+ * pleasing visual effect at low cost.
+ *
+ * Note that the damage done by "ball" explosions decreases with distance.
+ * This decrease is rapid, grids at radius "dist" take "1/dist" damage.
+ *
+ * Notice the "napalm" effect of "beam" weapons. First they "project" to
+ * the target, and then the damage "flows" along this beam of destruction.
+ * The damage at every grid is the same as at the "center" of a "ball"
+ * explosion, since the "beam" grids are treated as if they ARE at the
+ * center of a "ball" explosion.
+ *
+ * Currently, specifying "beam" plus "ball" means that locations which are
+ * covered by the initial "beam", and also covered by the final "ball", except
+ * for the final grid (the epicenter of the ball), will be "hit twice", once
+ * by the initial beam, and once by the exploding ball. For the grid right
+ * next to the epicenter, this results in 150% damage being done. The center
+ * does not have this problem, for the same reason the final grid in a "beam"
+ * plus "bolt" does not -- it is explicitly removed. Simply removing "beam"
+ * grids which are covered by the "ball" will NOT work, as then they will
+ * receive LESS damage than they should. Do not combine "beam" with "ball".
+ *
+ * The array "gy[],gx[]" with current size "grids" is used to hold the
+ * collected locations of all grids in the "blast area" plus "beam path".
+ *
+ * Note the rather complex usage of the "gm[]" array. First, gm[0] is always
+ * zero. Second, for N>1, gm[N] is always the index (in gy[],gx[]) of the
+ * first blast grid (see above) with radius "N" from the blast center. Note
+ * that only the first gm[1] grids in the blast area thus take full damage.
+ * Also, note that gm[rad+1] is always equal to "grids", which is the total
+ * number of blast grids.
+ *
+ * Note that once the projection is complete, (y2,x2) holds the final location
+ * of bolts/beams, and the "epicenter" of balls.
+ *
+ * Note also that "rad" specifies the "inclusive" radius of projection blast,
+ * so that a "rad" of "one" actually covers 5 or 9 grids, depending on the
+ * implementation of the "distance" function. Also, a bolt can be properly
+ * viewed as a "ball" with a "rad" of "zero".
+ *
+ * Note that if no "target" is reached before the beam/bolt/ball travels the
+ * maximum distance allowed (MAX_RANGE), no "blast" will be induced. This
+ * may be relevant even for bolts, since they have a "1x1" mini-blast.
+ *
+ * Note that for consistency, we "pretend" that the bolt actually takes "time"
+ * to move from point A to point B, even if the player cannot see part of the
+ * projection path. Note that in general, the player will *always* see part
+ * of the path, since it either starts at the player or ends on the player.
+ *
+ * Hack -- we assume that every "projection" is "self-illuminating".
+ *
+ * Hack -- when only a single monster is affected, we automatically track
+ * (and recall) that monster, unless "PROJECT_JUMP" is used.
+ *
+ * Note that all projections now "explode" at their final destination, even
+ * if they were being projected at a more distant destination. This means
+ * that "ball" spells will *always* explode.
+ *
+ * Note that we must call "handle_stuff()" after affecting terrain features
+ * in the blast radius, in case the "illumination" of the grid was changed,
+ * and "update_view()" and "update_monsters()" need to be called.
+ */
+bool_ project(int who, int rad, int y, int x, int dam, int typ, int flg)
+{
+ int i, t, dist;
+
+ int y1, x1;
+ int y2, x2;
+
+ int dist_hack = 0;
+
+ int y_saver, x_saver; /* For reflecting monsters */
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+ /* Assume the player sees nothing */
+ bool_ notice = FALSE;
+
+ /* Assume the player has seen nothing */
+ bool_ visual = FALSE;
+
+ /* Assume the player has seen no blast grids */
+ bool_ drawn = FALSE;
+
+ /* Is the player blind? */
+ bool_ blind = (p_ptr->blind ? TRUE : FALSE);
+
+ /* Number of grids in the "path" */
+ int path_n = 0;
+
+ /* Actual grids in the "path" */
+ u16b path_g[1024];
+
+ /* Number of grids in the "blast area" (including the "beam" path) */
+ int grids = 0;
+
+ int effect = 0;
+
+ /* Coordinates of the affected grids */
+ byte gx[1024], gy[1024];
+
+ /* Encoded "radius" info (see above) */
+ byte gm[64];
+
+
+ /* Hack -- Jump to target */
+ if (flg & (PROJECT_JUMP))
+ {
+ x1 = x;
+ y1 = y;
+
+ /* Clear the flag */
+ flg &= ~(PROJECT_JUMP);
+ }
+
+ /* Start at player */
+ else if ((who <= 0) && ((who != -101) && (who != -100)))
+ {
+ x1 = p_ptr->px;
+ y1 = p_ptr->py;
+ }
+
+ /* Start at monster */
+ else if (who > 0)
+ {
+ x1 = m_list[who].fx;
+ y1 = m_list[who].fy;
+ }
+
+ /* Oops */
+ else
+ {
+ x1 = x;
+ y1 = y;
+ }
+
+ y_saver = y1;
+ x_saver = x1;
+
+ /* Default "destination" */
+ y2 = y;
+ x2 = x;
+
+
+ /* Hack -- verify stuff */
+ if (flg & (PROJECT_THRU))
+ {
+ if ((x1 == x2) && (y1 == y2))
+ {
+ flg &= ~(PROJECT_THRU);
+ }
+ }
+
+
+ /* Hack -- Assume there will be no blast (max radius 16) */
+ for (dist = 0; dist < 64; dist++) gm[dist] = 0;
+
+
+ /* Initial grid */
+ y = y1;
+ x = x1;
+ dist = 0;
+
+ /* Collect beam grids */
+ if (flg & (PROJECT_BEAM))
+ {
+ gy[grids] = y;
+ gx[grids] = x;
+ grids++;
+ }
+
+
+ /* Calculate the projection path */
+ if ((who == -101) || (who == -100))
+ path_n = 0;
+ else
+ path_n = project_path(path_g, MAX_RANGE, y1, x1, y2, x2, flg);
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ /* Project along the path */
+ for (i = 0; i < path_n; ++i)
+ {
+ int oy = y;
+ int ox = x;
+
+ int ny = GRID_Y(path_g[i]);
+ int nx = GRID_X(path_g[i]);
+
+ /* Hack -- Balls explode before reaching walls */
+ if (!cave_floor_bold(ny, nx) && (rad > 0)) break;
+
+ /* Advance */
+ y = ny;
+ x = nx;
+
+ /* Collect beam grids */
+ if (flg & (PROJECT_BEAM))
+ {
+ gy[grids] = y;
+ gx[grids] = x;
+ grids++;
+ }
+
+ /* Only do visuals if requested */
+ if (!blind && !(flg & (PROJECT_HIDE)))
+ {
+ /* Only do visuals if the player can "see" the bolt */
+ if (panel_contains(y, x) && player_has_los_bold(y, x))
+ {
+ u16b p;
+
+ byte a;
+ char c;
+
+ /* Obtain the bolt pict */
+ p = bolt_pict(oy, ox, y, x, typ);
+
+ /* Extract attr/char */
+ a = PICT_A(p);
+ c = PICT_C(p);
+
+ /* Visual effects */
+ print_rel(c, a, y, x);
+ move_cursor_relative(y, x);
+ if (fresh_before) Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ if (fresh_before) Term_fresh();
+
+ /* Display "beam" grids */
+ if (flg & (PROJECT_BEAM))
+ {
+ /* Obtain the explosion pict */
+ p = bolt_pict(y, x, y, x, typ);
+
+ /* Extract attr/char */
+ a = PICT_A(p);
+ c = PICT_C(p);
+
+ /* Visual effects */
+ print_rel(c, a, y, x);
+ }
+
+ /* Hack -- Activate delay */
+ visual = TRUE;
+ }
+
+ /* Hack -- delay anyway for consistency */
+ else if (visual)
+ {
+ /* Delay for consistency */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+ }
+ }
+
+
+ /* Save the "blast epicenter" */
+ y2 = y;
+ x2 = x;
+
+ /* Start the "explosion" */
+ gm[0] = 0;
+
+ /* Hack -- make sure beams get to "explode" */
+ gm[1] = grids;
+
+ dist_hack = dist;
+
+ /* Explode */
+ if (TRUE)
+ {
+ /* Hack -- remove final beam grid */
+ if (flg & (PROJECT_BEAM))
+ {
+ grids--;
+ }
+
+ /* Determine the blast area, work from the inside out */
+ for (dist = 0; dist <= rad; dist++)
+ {
+ /* Scan the maximal blast area of radius "dist" */
+ for (y = y2 - dist; y <= y2 + dist; y++)
+ {
+ for (x = x2 - dist; x <= x2 + dist; x++)
+ {
+ /* Ignore "illegal" locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Enforce a "circular" explosion */
+ if (distance(y2, x2, y, x) != dist) continue;
+
+ /* Ball explosions are stopped by walls */
+ if (typ == GF_DISINTEGRATE)
+ {
+ if (cave_valid_bold(y, x) &&
+ (cave[y][x].feat < FEAT_PATTERN_START
+ || cave[y][x].feat > FEAT_PATTERN_XTRA2))
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Update some things -- similar to GF_KILL_WALL */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+ }
+ else
+ {
+ if (!los(y2, x2, y, x)) continue;
+ }
+
+ /* Save this grid */
+ gy[grids] = y;
+ gx[grids] = x;
+ grids++;
+ }
+ }
+
+ /* Encode some more "radius" info */
+ gm[dist + 1] = grids;
+ }
+ }
+
+
+ /* Speed -- ignore "non-explosions" */
+ if (!grids) return (FALSE);
+
+
+ /* Display the "blast area" if requested */
+ if (!blind && !(flg & (PROJECT_HIDE)))
+ {
+ /* Then do the "blast", from inside out */
+ for (t = 0; t <= rad; t++)
+ {
+ /* Dump everything with this radius */
+ for (i = gm[t]; i < gm[t + 1]; i++)
+ {
+ /* Extract the location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Only do visuals if the player can "see" the blast */
+ if (panel_contains(y, x) && player_has_los_bold(y, x))
+ {
+ u16b p;
+
+ byte a;
+ char c;
+
+ drawn = TRUE;
+
+ /* Obtain the explosion pict */
+ p = bolt_pict(y, x, y, x, typ);
+
+ /* Extract attr/char */
+ a = PICT_A(p);
+ c = PICT_C(p);
+
+ /* Visual effects -- Display */
+ print_rel(c, a, y, x);
+ }
+ }
+
+ /* Hack -- center the cursor */
+ move_cursor_relative(y2, x2);
+
+ /* Flush each "radius" seperately */
+ if (fresh_before) Term_fresh();
+
+ /* Delay (efficiently) */
+ if (visual || drawn)
+ {
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+ }
+
+ /* Flush the erasing */
+ if (drawn)
+ {
+ /* Erase the explosion drawn above */
+ for (i = 0; i < grids; i++)
+ {
+ /* Extract the location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Hack -- Erase if needed */
+ if (panel_contains(y, x) && player_has_los_bold(y, x))
+ {
+ lite_spot(y, x);
+ }
+ }
+
+ /* Hack -- center the cursor */
+ move_cursor_relative(y2, x2);
+
+ /* Flush the explosion */
+ if (fresh_before) Term_fresh();
+ }
+ }
+
+
+ /* Check features */
+ if (flg & (PROJECT_GRID))
+ {
+ /* Start with "dist" of zero */
+ dist = 0;
+
+ /* Effect ? */
+ if (flg & PROJECT_STAY)
+ {
+ effect = new_effect(typ, dam, project_time, p_ptr->py, p_ptr->px, rad, project_time_effect);
+ project_time = 0;
+ project_time_effect = 0;
+ }
+
+ /* Scan for features */
+ for (i = 0; i < grids; i++)
+ {
+ /* Hack -- Notice new "dist" values */
+ if (gm[dist + 1] == i) dist++;
+
+ /* Get the grid location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Affect the feature in that grid */
+ if (project_f(who, dist, y, x, dam, typ)) notice = TRUE;
+
+ /* Effect ? */
+ if (flg & PROJECT_STAY)
+ {
+ cave[y][x].effect = effect;
+ lite_spot(y, x);
+ }
+ }
+ }
+
+
+ /* Update stuff if needed */
+ if (p_ptr->update) update_stuff();
+
+
+ /* Check objects */
+ if (flg & (PROJECT_ITEM))
+ {
+ /* Start with "dist" of zero */
+ dist = 0;
+
+ /* Scan for objects */
+ for (i = 0; i < grids; i++)
+ {
+ /* Hack -- Notice new "dist" values */
+ if (gm[dist + 1] == i) dist++;
+
+ /* Get the grid location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Affect the object in the grid */
+ if (project_o(who, dist, y, x, dam, typ)) notice = TRUE;
+ }
+ }
+
+
+ /* Check monsters */
+ if (flg & (PROJECT_KILL))
+ {
+ /* Mega-Hack */
+ project_m_n = 0;
+ project_m_x = 0;
+ project_m_y = 0;
+
+ /* Start with "dist" of zero */
+ dist = 0;
+
+ /* Scan for monsters */
+ for (i = 0; i < grids; i++)
+ {
+ /* Hack -- Notice new "dist" values */
+ if (gm[dist + 1] == i) dist++;
+
+ /* Get the grid location */
+ y = gy[i];
+ x = gx[i];
+
+ if (grids > 1)
+ {
+ /* Affect the monster in the grid */
+ if (project_m(who, dist, y, x, dam, typ)) notice = TRUE;
+ }
+ else
+ {
+ monster_race *ref_ptr = race_inf(&m_list[cave[y][x].m_idx]);
+
+ if ((ref_ptr->flags2 & (RF2_REFLECTING)) && (randint(10) != 1)
+ && (dist_hack > 1))
+ {
+ int t_y, t_x;
+ int max_attempts = 10;
+
+ /* Choose 'new' target */
+ do
+ {
+ t_y = y_saver - 1 + randint(3);
+ t_x = x_saver - 1 + randint(3);
+ max_attempts--;
+ }
+
+ while (max_attempts && in_bounds2(t_y, t_x) &&
+ !(los(y, x, t_y, t_x)));
+
+ if (max_attempts < 1)
+ {
+ t_y = y_saver;
+ t_x = x_saver;
+ }
+
+ if (m_list[cave[y][x].m_idx].ml)
+ {
+ msg_print("The attack bounces!");
+ ref_ptr->r_flags2 |= RF2_REFLECTING;
+ }
+
+ project(cave[y][x].m_idx, 0, t_y, t_x, dam, typ, flg);
+ }
+ else
+ {
+ if (project_m(who, dist, y, x, dam, typ)) notice = TRUE;
+ }
+ }
+ }
+
+ /* Player affected one monster (without "jumping") */
+ if ((who < 0) && (project_m_n == 1) && !(flg & (PROJECT_JUMP)))
+ {
+ /* Location */
+ x = project_m_x;
+ y = project_m_y;
+
+ /* Track if possible */
+ if (cave[y][x].m_idx > 0)
+ {
+ monster_type *m_ptr = &m_list[cave[y][x].m_idx];
+
+ /* Hack -- auto-recall */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack - auto-track */
+ if (m_ptr->ml) health_track(cave[y][x].m_idx);
+ }
+ }
+ }
+
+
+ /* Check player */
+ if (flg & (PROJECT_KILL))
+ {
+ /* Start with "dist" of zero */
+ dist = 0;
+
+ /* Scan for player */
+ for (i = 0; i < grids; i++)
+ {
+ /* Hack -- Notice new "dist" values */
+ if (gm[dist + 1] == i) dist++;
+
+ /* Get the grid location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Affect the player */
+ if (project_p(who, dist, y, x, dam, typ, rad)) notice = TRUE;
+ }
+ }
+
+ /* Return "something was noticed" */
+ return (notice);
+}
+
+
+
+/*
+ * Potions "smash open" and cause an area effect when
+ * (1) they are shattered while in the player's inventory,
+ * due to cold (etc) attacks;
+ * (2) they are thrown at a monster, or obstacle;
+ * (3) they are shattered by a "cold ball" or other such spell
+ * while lying on the floor.
+ *
+ * Arguments:
+ * who --- who caused the potion to shatter (0=player)
+ * potions that smash on the floor are assumed to
+ * be caused by no-one (who = 1), as are those that
+ * shatter inside the player inventory.
+ * (Not anymore -- I changed this; TY)
+ * y, x --- coordinates of the potion (or player if
+ * the potion was in her inventory);
+ * o_ptr --- pointer to the potion object.
+ */
+bool_ potion_smash_effect(int who, int y, int x, int o_sval)
+{
+ int radius = 2;
+ int dt = 0;
+ int dam = 0;
+ bool_ ident = FALSE;
+ bool_ angry = FALSE;
+
+ switch (o_sval)
+ {
+ case SV_POTION_SALT_WATER:
+ case SV_POTION_SLIME_MOLD:
+ case SV_POTION_LOSE_MEMORIES:
+ case SV_POTION_DEC_STR:
+ case SV_POTION_DEC_INT:
+ case SV_POTION_DEC_WIS:
+ case SV_POTION_DEC_DEX:
+ case SV_POTION_DEC_CON:
+ case SV_POTION_DEC_CHR:
+ case SV_POTION_WATER: /* perhaps a 'water' attack? */
+ case SV_POTION_APPLE_JUICE:
+ return TRUE;
+
+ case SV_POTION_INFRAVISION:
+ case SV_POTION_DETECT_INVIS:
+ case SV_POTION_SLOW_POISON:
+ case SV_POTION_CURE_POISON:
+ case SV_POTION_BOLDNESS:
+ case SV_POTION_RESIST_HEAT:
+ case SV_POTION_RESIST_COLD:
+ case SV_POTION_HEROISM:
+ case SV_POTION_BESERK_STRENGTH:
+ case SV_POTION_RESTORE_EXP:
+ case SV_POTION_RES_STR:
+ case SV_POTION_RES_INT:
+ case SV_POTION_RES_WIS:
+ case SV_POTION_RES_DEX:
+ case SV_POTION_RES_CON:
+ case SV_POTION_RES_CHR:
+ case SV_POTION_INC_STR:
+ case SV_POTION_INC_INT:
+ case SV_POTION_INC_WIS:
+ case SV_POTION_INC_DEX:
+ case SV_POTION_INC_CON:
+ case SV_POTION_INC_CHR:
+ case SV_POTION_AUGMENTATION:
+ case SV_POTION_ENLIGHTENMENT:
+ case SV_POTION_STAR_ENLIGHTENMENT:
+ case SV_POTION_SELF_KNOWLEDGE:
+ case SV_POTION_EXPERIENCE:
+ case SV_POTION_RESISTANCE:
+ case SV_POTION_INVULNERABILITY:
+ case SV_POTION_NEW_LIFE:
+ /* All of the above potions have no effect when shattered */
+ return FALSE;
+ case SV_POTION_SLOWNESS:
+ dt = GF_OLD_SLOW;
+ dam = 5;
+ ident = TRUE;
+ angry = TRUE;
+ break;
+ case SV_POTION_POISON:
+ dt = GF_POIS;
+ dam = 3;
+ ident = TRUE;
+ angry = TRUE;
+ break;
+ case SV_POTION_BLINDNESS:
+ dt = GF_DARK;
+ ident = TRUE;
+ angry = TRUE;
+ break;
+ case SV_POTION_CONFUSION: /* Booze */
+ dt = GF_OLD_CONF;
+ ident = TRUE;
+ angry = TRUE;
+ break;
+ case SV_POTION_SLEEP:
+ dt = GF_OLD_SLEEP;
+ angry = TRUE;
+ ident = TRUE;
+ break;
+ case SV_POTION_RUINATION:
+ case SV_POTION_DETONATIONS:
+ dt = GF_SHARDS;
+ dam = damroll(25, 25);
+ angry = TRUE;
+ ident = TRUE;
+ break;
+ case SV_POTION_DEATH:
+ dt = GF_MANA; /* !! */
+ dam = damroll(10, 10);
+ angry = TRUE;
+ radius = 1;
+ ident = TRUE;
+ break;
+ case SV_POTION_SPEED:
+ dt = GF_OLD_SPEED;
+ ident = TRUE;
+ break;
+ case SV_POTION_CURE_LIGHT:
+ dt = GF_OLD_HEAL;
+ dam = damroll(2, 3);
+ ident = TRUE;
+ break;
+ case SV_POTION_CURE_SERIOUS:
+ dt = GF_OLD_HEAL;
+ dam = damroll(4, 3);
+ ident = TRUE;
+ break;
+ case SV_POTION_CURE_CRITICAL:
+ case SV_POTION_CURING:
+ dt = GF_OLD_HEAL;
+ dam = damroll(6, 3);
+ ident = TRUE;
+ break;
+ case SV_POTION_HEALING:
+ dt = GF_OLD_HEAL;
+ dam = damroll(10, 10);
+ ident = TRUE;
+ break;
+ case SV_POTION_STAR_HEALING:
+ case SV_POTION_LIFE:
+ dt = GF_OLD_HEAL;
+ dam = damroll(50, 50);
+ radius = 1;
+ ident = TRUE;
+ break;
+ case SV_POTION_RESTORE_MANA: /* MANA */
+ dt = GF_MANA;
+ dam = damroll(10, 10);
+ radius = 1;
+ ident = TRUE;
+ break;
+ default:
+ /* Do nothing */
+ ;
+ }
+
+ (void) project(who, radius, y, x, dam, dt,
+ (PROJECT_JUMP | PROJECT_ITEM | PROJECT_KILL));
+
+ /* XXX those potions that explode need to become "known" */
+ return angry;
+}
+
+/* This is for Thaumaturgy */
+static const int destructive_attack_types[10] =
+{
+ GF_KILL_WALL,
+ GF_KILL_WALL,
+ GF_KILL_WALL,
+ GF_STONE_WALL,
+ GF_STONE_WALL,
+ GF_STONE_WALL,
+ GF_DESTRUCTION,
+ GF_DESTRUCTION,
+ GF_DESTRUCTION,
+ GF_DESTRUCTION,
+};
+
+/* Also for Power-mages */
+static const int attack_types[25] =
+{
+ GF_ARROW,
+ GF_MISSILE,
+ GF_MANA,
+ GF_WATER,
+ GF_PLASMA,
+ GF_METEOR,
+ GF_ICE,
+ GF_GRAVITY,
+ GF_INERTIA,
+ GF_FORCE,
+ GF_TIME,
+ GF_ACID,
+ GF_ELEC,
+ GF_FIRE,
+ GF_COLD,
+ GF_POIS,
+ GF_LITE,
+ GF_DARK,
+ GF_CONFUSION,
+ GF_SOUND,
+ GF_SHARDS,
+ GF_NEXUS,
+ GF_NETHER,
+ GF_CHAOS,
+ GF_DISENCHANT,
+};
+
+/*
+ * Describe the attack using normal names.
+ */
+
+void describe_attack_fully(int type, char* r)
+{
+ switch (type)
+ {
+ case GF_ARROW:
+ strcpy(r, "arrows");
+ break;
+ case GF_MISSILE:
+ strcpy(r, "magic missiles");
+ break;
+ case GF_MANA:
+ strcpy(r, "mana");
+ break;
+ case GF_LITE_WEAK:
+ strcpy(r, "light");
+ break;
+ case GF_DARK_WEAK:
+ strcpy(r, "dark");
+ break;
+ case GF_WATER:
+ strcpy(r, "water");
+ break;
+ case GF_PLASMA:
+ strcpy(r, "plasma");
+ break;
+ case GF_METEOR:
+ strcpy(r, "meteors");
+ break;
+ case GF_ICE:
+ strcpy(r, "ice");
+ break;
+ case GF_GRAVITY:
+ strcpy(r, "gravity");
+ break;
+ case GF_INERTIA:
+ strcpy(r, "inertia");
+ break;
+ case GF_FORCE:
+ strcpy(r, "force");
+ break;
+ case GF_TIME:
+ strcpy(r, "pure time");
+ break;
+ case GF_ACID:
+ strcpy(r, "acid");
+ break;
+ case GF_ELEC:
+ strcpy(r, "lightning");
+ break;
+ case GF_FIRE:
+ strcpy(r, "flames");
+ break;
+ case GF_COLD:
+ strcpy(r, "cold");
+ break;
+ case GF_POIS:
+ strcpy(r, "poison");
+ break;
+ case GF_LITE:
+ strcpy(r, "pure light");
+ break;
+ case GF_DARK:
+ strcpy(r, "pure dark");
+ break;
+ case GF_CONFUSION:
+ strcpy(r, "confusion");
+ break;
+ case GF_SOUND:
+ strcpy(r, "sound");
+ break;
+ case GF_SHARDS:
+ strcpy(r, "shards");
+ break;
+ case GF_NEXUS:
+ strcpy(r, "nexus");
+ break;
+ case GF_NETHER:
+ strcpy(r, "nether");
+ break;
+ case GF_CHAOS:
+ strcpy(r, "chaos");
+ break;
+ case GF_DISENCHANT:
+ strcpy(r, "disenchantment");
+ break;
+ case GF_KILL_WALL:
+ strcpy(r, "wall destruction");
+ break;
+ case GF_KILL_DOOR:
+ strcpy(r, "door destruction");
+ break;
+ case GF_KILL_TRAP:
+ strcpy(r, "trap destruction");
+ break;
+ case GF_STONE_WALL:
+ strcpy(r, "wall creation");
+ break;
+ case GF_MAKE_DOOR:
+ strcpy(r, "door creation");
+ break;
+ case GF_MAKE_TRAP:
+ strcpy(r, "trap creation");
+ break;
+ case GF_DESTRUCTION:
+ strcpy(r, "destruction");
+ break;
+
+ default:
+ strcpy(r, "something unknown");
+ break;
+ }
+}
+
+/*
+ * Give a randomly-generated spell a name.
+ * Note that it only describes the first effect!
+ */
+
+static void name_spell(random_spell* s_ptr)
+{
+ char buff[30];
+ cptr buff2 = "???";
+
+ if (s_ptr->proj_flags & PROJECT_STOP && s_ptr->radius == 0)
+ {
+ buff2 = "Bolt";
+ }
+ else if (s_ptr->proj_flags & PROJECT_BEAM)
+ {
+ buff2 = "Beam";
+ }
+ else if (s_ptr->proj_flags & PROJECT_STOP && s_ptr->radius > 0)
+ {
+ buff2 = "Ball";
+ }
+ else if (s_ptr->proj_flags & PROJECT_BLAST)
+ {
+ buff2 = "Blast";
+ }
+ else if (s_ptr->proj_flags & PROJECT_METEOR_SHOWER)
+ {
+ buff2 = "Area";
+ }
+ else if (s_ptr->proj_flags & PROJECT_VIEWABLE)
+ {
+ buff2 = "View";
+ }
+
+ describe_attack_fully(s_ptr->GF, buff);
+ strnfmt(s_ptr->name, 30, "%s - %s", buff2, buff);
+}
+
+void generate_spell(int plev)
+{
+ random_spell* rspell;
+ int dice, sides, chance, mana, power;
+ bool_ destruc_gen = FALSE;
+ bool_ simple_gen = TRUE;
+ bool_ ball_desc = FALSE;
+
+ if (spell_num == MAX_SPELLS) return;
+
+ rspell = &random_spells[spell_num];
+
+ power = rand_int(5);
+
+ dice = plev / 2;
+ sides = plev;
+ mana = plev;
+
+ /* Make the spell more or less powerful. */
+ dice += power;
+ sides += power;
+ mana += (plev * power) / 15;
+
+ /* Stay within reasonable bounds. */
+ if (dice < 1) dice = 1;
+
+ if (sides < 5) sides = 5;
+
+ if (mana < 1) mana = 1;
+
+ rspell->level = plev;
+ rspell->mana = mana;
+ rspell->untried = TRUE;
+
+ /* Spells are always maximally destructive. */
+ rspell->proj_flags = PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID;
+
+ chance = randint(100);
+
+ /* Hack -- Always start with Magic Missile or derivative at lev. 1 */
+ if (plev == 1 || chance < 25)
+ {
+ rspell->proj_flags |= PROJECT_STOP;
+ /* Swap dice and sides for better damage */
+ rspell->dam_dice = sides;
+ rspell->dam_sides = dice;
+ rspell->radius = 0;
+ }
+ else if (chance < 50)
+ {
+ rspell->proj_flags |= PROJECT_BEAM;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+ rspell->radius = 0;
+ }
+ else if (chance < 76)
+ {
+ rspell->proj_flags |= PROJECT_STOP;
+ rspell->radius = dice / 3;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+ ball_desc = TRUE;
+ }
+ else if (chance < 83)
+ {
+ rspell->proj_flags |= PROJECT_BLAST;
+ rspell->radius = sides / 3;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+
+ destruc_gen = TRUE;
+ simple_gen = FALSE;
+ }
+ else if (chance < 90)
+ {
+ rspell->proj_flags |= PROJECT_METEOR_SHOWER;
+ /* Area effect spells do way less damage "per shot" */
+ rspell->dam_dice = dice / 5;
+ rspell->dam_sides = sides / 5;
+ rspell->radius = sides / 3;
+ if (rspell->radius < 4) rspell->radius = 4;
+
+ destruc_gen = TRUE;
+ }
+ else
+ {
+ rspell->proj_flags |= PROJECT_VIEWABLE;
+ /* View spells do less damage */
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides / 2;
+ }
+
+ /* Both a destructive and a simple spell requested --
+ * pick one or the other. */
+ if (destruc_gen && simple_gen)
+ {
+ if (magik(25))
+ {
+ simple_gen = FALSE;
+ }
+ else
+ {
+ destruc_gen = FALSE;
+ }
+ }
+
+ /* Pick a simple spell */
+ if (simple_gen)
+ {
+ rspell->GF = attack_types[rand_int(25)];
+ }
+ /* Pick a destructive spell */
+ else
+ {
+ rspell->GF = destructive_attack_types[rand_int(10)];
+ }
+
+ /* Give the spell a name. */
+ name_spell(rspell);
+ if (ball_desc)
+ {
+ /* 30 character limit on the string! */
+ sprintf(rspell->desc, "Dam: %d, Rad: %d, Pow: %d",
+ sides, dice, power);
+ }
+ else
+ {
+ sprintf(rspell->desc, "Damage: %dd%d, Power: %d",
+ dice, sides, power);
+ }
+
+ spell_num++;
+}
+
+/*
+ * Polymorph a monster at given location.
+ */
+s16b do_poly_monster(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr;
+
+ s16b hack_m_idx;
+ s16b old_m_idx;
+ s16b new_m_idx = 0;
+
+ s16b new_r_idx;
+
+ /* Get a "old" monster */
+ old_m_idx = c_ptr->m_idx;
+
+ /* Giga-Hack -- Remember monster */
+ hack_m_idx = old_m_idx;
+
+ /* Get a monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Pick a "new" monster race */
+ new_r_idx = poly_r_idx(m_ptr->r_idx);
+
+ /* No polymorph happend */
+ if (new_r_idx == m_ptr->r_idx) return 0;
+
+ /* Giga-Hack -- Removes the moster XXX XXX XXX XXX */
+ c_ptr->m_idx = 0;
+
+ /*
+ * Handle polymorph --
+ * Create a new monster (no groups)
+ */
+ if (place_monster_aux(y, x, new_r_idx, FALSE, FALSE, m_ptr->status))
+ {
+ /* Get a "new" monster */
+ new_m_idx = c_ptr->m_idx;
+
+ /* Giga-Hack -- Remember "new" monster */
+ hack_m_idx = new_m_idx;
+
+ /* "Kill" the "old" monster */
+ delete_monster_idx(old_m_idx);
+
+ p_ptr->redraw |= (PR_MAP);
+ }
+
+ /* Giga-Hack -- restore saved monster XXX XXX XXX */
+ c_ptr->m_idx = hack_m_idx;
+
+ return new_m_idx;
+}
diff --git a/src/spells2.c b/src/spells2.c
new file mode 100644
index 00000000..5467499c
--- /dev/null
+++ b/src/spells2.c
@@ -0,0 +1,8076 @@
+/* File: spells2.c */
+
+/* Purpose: Spell code (part 2) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#define WEIRD_LUCK 12
+#define BIAS_LUCK 20
+/*
+ * Bias luck needs to be higher than weird luck,
+ * since it is usually tested several times...
+ */
+
+void summon_dragon_riders();
+
+
+/*
+ * Grow things
+ */
+void grow_things(s16b type, int rad)
+{
+ int a, i, j;
+
+ for (a = 0; a < rad * rad + 11; a++)
+ {
+ i = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+ j = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+
+ if (!in_bounds(p_ptr->py + j, p_ptr->px + i)) continue;
+ if (distance(p_ptr->py, p_ptr->px, p_ptr->py + j, p_ptr->px + i) > rad) continue;
+
+ if (cave_clean_bold(p_ptr->py + j, p_ptr->px + i))
+ {
+ cave_set_feat(p_ptr->py + j, p_ptr->px + i, type);
+ }
+ }
+}
+
+/*
+ * Grow trees
+ */
+void grow_trees(int rad)
+{
+ int a, i, j;
+
+ for (a = 0; a < rad * rad + 11; a++)
+ {
+ i = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+ j = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+
+ if (!in_bounds(p_ptr->py + j, p_ptr->px + i)) continue;
+ if (distance(p_ptr->py, p_ptr->px, p_ptr->py + j, p_ptr->px + i) > rad) continue;
+
+ if (cave_clean_bold(p_ptr->py + j, p_ptr->px + i) && (f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_SUPPORT_GROWTH))
+ {
+ cave_set_feat(p_ptr->py + j, p_ptr->px + i, FEAT_TREES);
+ }
+ }
+}
+
+/*
+ * Grow grass
+ */
+void grow_grass(int rad)
+{
+ int a, i, j;
+
+ for (a = 0; a < rad * rad + 11; a++)
+ {
+ i = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+ j = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+
+ if (!in_bounds(p_ptr->py + j, p_ptr->px + i)) continue;
+ if (distance(p_ptr->py, p_ptr->px, p_ptr->py + j, p_ptr->px + i) > rad) continue;
+
+ if (cave_clean_bold(p_ptr->py + j, p_ptr->px + i) && (f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_SUPPORT_GROWTH))
+ {
+ cave_set_feat(p_ptr->py + j, p_ptr->px + i, FEAT_GRASS);
+ }
+ }
+}
+
+/*
+ * Increase players hit points, notice effects
+ */
+bool_ hp_player(int num)
+{
+ bool_ dead = p_ptr->chp < 0;
+
+ /* Healing needed */
+ if (p_ptr->chp < p_ptr->mhp)
+ {
+ /* Gain hitpoints */
+ p_ptr->chp += num;
+
+ /* Enforce maximum */
+ if ((p_ptr->chp >= p_ptr->mhp) ||
+ /* prevent wrapping */
+ (!dead && (p_ptr->chp < 0)))
+ {
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Heal 0-4 */
+ if (num < 5)
+ {
+ msg_print("You feel a little better.");
+ }
+
+ /* Heal 5-14 */
+ else if (num < 15)
+ {
+ msg_print("You feel better.");
+ }
+
+ /* Heal 15-34 */
+ else if (num < 35)
+ {
+ msg_print("You feel much better.");
+ }
+
+ /* Heal 35+ */
+ else
+ {
+ msg_print("You feel very good.");
+ }
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Ignore */
+ return (FALSE);
+}
+
+
+
+/*
+ * Leave a "glyph of warding" which prevents monster movement
+ */
+void warding_glyph(void)
+{
+ /* XXX XXX XXX */
+ if (!cave_clean_bold(p_ptr->py, p_ptr->px))
+ {
+ msg_print("The object resists the spell.");
+ return;
+ }
+
+ /* Create a glyph */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_GLYPH);
+}
+
+void explosive_rune(void)
+{
+ /* XXX XXX XXX */
+ if (!cave_clean_bold(p_ptr->py, p_ptr->px))
+ {
+ msg_print("The object resists the spell.");
+ return;
+ }
+
+ /* Create a glyph */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MINOR_GLYPH);
+}
+
+
+
+/*
+ * Array of stat "descriptions"
+ */
+static cptr desc_stat_pos[] =
+{
+ "strong",
+ "smart",
+ "wise",
+ "dextrous",
+ "healthy",
+ "cute"
+};
+
+/*
+ * Array of long descriptions of stat
+ */
+
+static cptr long_desc_stat[] =
+{
+ "strength",
+ "intelligence",
+ "wisdom",
+ "dexterity",
+ "constitution",
+ "charisma"
+};
+
+/*
+ * Array of stat "descriptions"
+ */
+static cptr desc_stat_neg[] =
+{
+ "weak",
+ "stupid",
+ "naive",
+ "clumsy",
+ "sickly",
+ "ugly"
+};
+
+
+/*
+ * Lose a "point"
+ */
+bool_ do_dec_stat(int stat, int mode)
+{
+ bool_ sust = FALSE;
+
+ /* Access the "sustain" */
+ switch (stat)
+ {
+ case A_STR:
+ if (p_ptr->sustain_str) sust = TRUE;
+ break;
+ case A_INT:
+ if (p_ptr->sustain_int) sust = TRUE;
+ break;
+ case A_WIS:
+ if (p_ptr->sustain_wis) sust = TRUE;
+ break;
+ case A_DEX:
+ if (p_ptr->sustain_dex) sust = TRUE;
+ break;
+ case A_CON:
+ if (p_ptr->sustain_con) sust = TRUE;
+ break;
+ case A_CHR:
+ if (p_ptr->sustain_chr) sust = TRUE;
+ break;
+ }
+
+ /* Sustain */
+ if (sust)
+ {
+ /* Message */
+ msg_format("You feel %s for a moment, but the feeling passes.",
+ desc_stat_neg[stat]);
+
+ /* Notice effect */
+ return (TRUE);
+ }
+
+ /* Attempt to reduce the stat */
+ if (dec_stat(stat, 10, mode))
+ {
+ /* Message */
+ msg_format("You feel very %s.", desc_stat_neg[stat]);
+
+ /* Notice effect */
+ return (TRUE);
+ }
+
+ /* Nothing obvious */
+ return (FALSE);
+}
+
+
+/*
+ * Restore lost "points" in a stat
+ */
+bool_ do_res_stat(int stat, bool_ full)
+{
+ /* Keep a copy of the current stat, so we can evaluate it if necessary */
+ int cur_stat = p_ptr->stat_cur[stat];
+
+ /* Attempt to increase */
+ if (res_stat(stat, full))
+ {
+ /* Message, depending on whether we got stronger or weaker */
+ if (cur_stat > p_ptr->stat_max[stat])
+ {
+ msg_format("You feel your %s boost drain away.", long_desc_stat[stat]);
+ }
+ else
+ {
+ msg_format("You feel less %s.", desc_stat_neg[stat]);
+ }
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Nothing obvious */
+ return (FALSE);
+}
+
+
+/*
+ * Gain a "point" in a stat
+ */
+bool_ do_inc_stat(int stat)
+{
+ bool_ res;
+
+ /* Restore strength */
+ res = res_stat(stat, TRUE);
+
+ /* Attempt to increase */
+ if (inc_stat(stat))
+ {
+ /* Message */
+ msg_format("Wow! You feel very %s!", desc_stat_pos[stat]);
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Restoration worked */
+ if (res)
+ {
+ /* Message */
+ msg_format("You feel less %s.", desc_stat_neg[stat]);
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Nothing obvious */
+ return (FALSE);
+}
+
+
+
+/*
+ * Identify everything being carried.
+ * Done by a potion of "self knowledge".
+ */
+void identify_pack(void)
+{
+ int i;
+
+ /* Simply identify and know every item */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Aware and Known */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", i, "normal");
+ }
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+}
+
+/*
+ * common portions of identify_fully and identify_pack_fully
+ */
+static void make_item_fully_identified(object_type *o_ptr)
+{
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Mark the item as fully known */
+ o_ptr->ident |= (IDENT_MENTAL);
+
+ /* For those with alchemist skills, learn how to create it */
+ alchemist_learn_object(o_ptr);
+}
+
+/*
+ * Identify everything being carried.
+ * Done by a potion of "self knowledge".
+ */
+void identify_pack_fully(void)
+{
+ int i;
+
+ /* Simply identify and know every item */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ make_item_fully_identified(o_ptr);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", i, "full");
+ }
+
+ p_ptr->update |= (PU_BONUS);
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+}
+
+/*
+ * Used by the "enchant" function (chance of failure)
+ * (modified for Zangband, we need better stuff there...) -- TY
+ */
+static int enchant_table[16] =
+{
+ 0, 10, 50, 100, 200,
+ 300, 400, 500, 650, 800,
+ 950, 987, 993, 995, 998,
+ 1000
+};
+
+bool_ remove_curse_object(object_type *o_ptr, bool_ all)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) return FALSE;
+
+ /* Uncursed already */
+ if (!cursed_p(o_ptr)) return FALSE;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Heavily Cursed Items need a special spell */
+ if (!all && (f3 & (TR3_HEAVY_CURSE))) return FALSE;
+
+ /* Perma-Cursed Items can NEVER be uncursed */
+ if (f3 & (TR3_PERMA_CURSE)) return FALSE;
+
+ /* Uncurse it */
+ o_ptr->ident &= ~(IDENT_CURSED);
+
+ /* Hack -- Assume felt */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ /* Take note */
+ o_ptr->sense = SENSE_UNCURSED;
+
+ /* Reverse the curse effect */
+ /* jk - scrolls of *remove curse* have a 1 in (55-level chance to */
+ /* reverse the curse effects - a ring of damage(-15) {cursed} then */
+ /* becomes a ring of damage (+15) */
+ /* this does not go for artifacts - a Sword of Mormegil +40,+60 would */
+ /* be somewhat unbalancing */
+ /* due to the nature of this procedure, it only works on cursed items */
+ /* ie you get only one chance! */
+ if ((randint(55-p_ptr->lev) == 1) && !artifact_p(o_ptr))
+ {
+ if (o_ptr->to_a < 0) o_ptr->to_a = -o_ptr->to_a;
+ if (o_ptr->to_h < 0) o_ptr->to_h = -o_ptr->to_h;
+ if (o_ptr->to_d < 0) o_ptr->to_d = -o_ptr->to_d;
+ if (o_ptr->pval < 0) o_ptr->pval = -o_ptr->pval;
+ }
+
+ /* Recalculate the bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+
+ return TRUE;
+}
+
+/*
+ * Removes curses from items in inventory
+ *
+ * Note that Items which are "Perma-Cursed" (The One Ring,
+ * The Crown of Morgoth) can NEVER be uncursed.
+ *
+ * Note that if "all" is FALSE, then Items which are
+ * "Heavy-Cursed" (Mormegil, Calris, and Weapons of Morgul)
+ * will not be uncursed.
+ */
+static int remove_curse_aux(int all)
+{
+ int i, cnt = 0;
+
+ /* Attempt to uncurse items being worn */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!remove_curse_object(o_ptr, all)) continue;
+
+ /* Count the uncursings */
+ cnt++;
+ }
+
+ /* Return "something uncursed" */
+ return (cnt);
+}
+
+
+/*
+ * Remove most curses
+ */
+bool_ remove_curse(void)
+{
+ return (remove_curse_aux(FALSE) ? TRUE : FALSE);
+}
+
+/*
+ * Remove all curses
+ */
+bool_ remove_all_curse(void)
+{
+ return (remove_curse_aux(TRUE) ? TRUE : FALSE);
+}
+
+
+
+/*
+ * Restores any drained experience
+ */
+bool_ restore_level(void)
+{
+ /* Restore experience */
+ if (p_ptr->exp < p_ptr->max_exp)
+ {
+ /* Message */
+ msg_print("You feel your life energies returning.");
+
+ /* Restore the experience */
+ p_ptr->exp = p_ptr->max_exp;
+
+ /* Check the experience */
+ check_experience();
+
+ /* Did something */
+ return (TRUE);
+ }
+
+ /* No effect */
+ return (FALSE);
+}
+
+
+bool_ alchemy(void) /* Turns an object into gold, gain some of its value in a shop */
+{
+ int item, amt = 1;
+ int old_number;
+ long price;
+ bool_ force = FALSE;
+ object_type *o_ptr;
+ char o_name[80];
+ char out_val[160];
+
+ cptr q, s;
+
+ /* Hack -- force destruction */
+ if (command_arg > 0) force = TRUE;
+
+ /* Get an item */
+ q = "Turn which item to gold? ";
+ s = "You have nothing to turn to gold.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+
+ /* See how many items */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return FALSE;
+ }
+
+
+ /* Describe the object */
+ old_number = o_ptr->number;
+ o_ptr->number = amt;
+ object_desc(o_name, o_ptr, TRUE, 3);
+ o_ptr->number = old_number;
+
+ /* Verify unless quantity given */
+ if (!force)
+ {
+ if (!((auto_destroy) && (object_value(o_ptr) < 1)))
+ {
+ /* Make a verification */
+ sprintf(out_val, "Really turn %s to gold? ", o_name);
+ if (!get_check(out_val)) return FALSE;
+ }
+ }
+
+ /* Artifacts cannot be destroyed */
+ if (artifact_p(o_ptr) || o_ptr->art_name)
+ {
+ byte feel = SENSE_SPECIAL;
+
+ /* Message */
+ msg_format("You fail to turn %s to gold!", o_name);
+
+ /* Hack -- Handle icky artifacts */
+ if (cursed_p(o_ptr)) feel = SENSE_TERRIBLE;
+
+ /* Hack -- inscribe the artifact */
+ o_ptr->sense = feel;
+
+ /* We have "felt" it (again) */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ return FALSE;
+ }
+
+ price = object_value_real(o_ptr);
+
+ if (price <= 0)
+ /* Message */
+ msg_format("You turn %s to fool's gold.", o_name);
+ else
+ {
+ price /= 3;
+
+ if (amt > 1) price *= amt;
+
+ msg_format("You turn %s to %ld coins worth of gold.", o_name, price);
+ p_ptr->au += price;
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ }
+
+ /* Eliminate the item */
+ inc_stack_size(item, -amt);
+
+ return TRUE;
+}
+
+
+
+
+/*
+ * self-knowledge... idea from nethack. Useful for determining powers and
+ * resistances of items. It saves the screen, clears it, then starts listing
+ * attributes, a screenful at a time. (There are a LOT of attributes to
+ * list. It will probably take 2 or 3 screens for a powerful character whose
+ * using several artifacts...) -CFT
+ *
+ * It is now a lot more efficient. -BEN-
+ *
+ * See also "identify_fully()".
+ *
+ * XXX XXX XXX Use the "show_file()" method, perhaps.
+ */
+void self_knowledge(FILE *fff)
+{
+ int i = 0, j, k;
+
+ u32b f1 = 0L, f2 = 0L, f3 = 0L, f4 = 0L, f5 = 0L, esp = 0L;
+
+ int iter; /* Iterator for a loop */
+
+ object_type *o_ptr;
+
+ char Dummy[80];
+
+ cptr info[200];
+
+ strcpy (Dummy, "");
+
+ /* Acquire item flags from equipment */
+ for (k = INVEN_WIELD; k < INVEN_TOTAL; k++)
+ {
+ u32b t1, t2, t3, t4, t5, esp;
+
+ o_ptr = &p_ptr->inventory[k];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &t1, &t2, &t3, &t4, &t5, &esp);
+
+ /* Extract flags */
+ f1 |= t1;
+ f2 |= t2;
+ f3 |= t3;
+ }
+
+ if (death)
+ {
+ static char buf[250];
+
+ sprintf(buf, "You are dead, killed by %s %s.",
+ died_from, describe_player_location());
+ info[i++] = buf;
+ }
+
+ /* Racial powers... */
+ if (p_ptr->body_monster != 0)
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ if (r_ptr->flags1 & RF1_CHAR_CLEAR ||
+ r_ptr->flags1 & RF1_ATTR_CLEAR)
+ info[i++] = "You are transparent.";
+ if ((r_ptr->flags1 & RF1_CHAR_MULTI) ||
+ (r_ptr->flags2 & RF2_SHAPECHANGER))
+ info[i++] = "Your form constantly changes.";
+ if (r_ptr->flags1 & RF1_ATTR_MULTI)
+ info[i++] = "Your color constantly changes.";
+ if (r_ptr->flags1 & RF1_NEVER_BLOW)
+ info[i++] = "You do not have a physical weapon.";
+ if (r_ptr->flags1 & RF1_NEVER_MOVE)
+ info[i++] = "You cannot move.";
+ if ((r_ptr->flags1 & RF1_RAND_25) &&
+ (r_ptr->flags1 & RF1_RAND_50))
+ info[i++] = "You move extremely erratically.";
+ else if (r_ptr->flags1 & RF1_RAND_50)
+ info[i++] = "You move somewhat erratically.";
+ else if (r_ptr->flags1 & RF1_RAND_25)
+ info[i++] = "You move a bit erratically.";
+ if (r_ptr->flags2 & RF2_STUPID)
+ info[i++] = "You are very stupid (INT -4).";
+ if (r_ptr->flags2 & RF2_SMART)
+ info[i++] = "You are very smart (INT +4).";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_CAN_SPEAK)
+ info[i++] = "You can speak.";
+ else
+ info[i++] = "You cannot speak.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_COLD_BLOOD)
+ info[i++] = "You are cold blooded.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_EMPTY_MIND)
+ info[i++] = "You have an empty mind.";
+ if (r_ptr->flags2 & RF2_WEIRD_MIND)
+ info[i++] = "You have a weird mind.";
+ if (r_ptr->flags4 & RF4_MULTIPLY)
+ info[i++] = "You can multiply.";
+ if (r_ptr->flags2 & RF2_POWERFUL)
+ info[i++] = "You have strong breath.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_ELDRITCH_HORROR)
+ info[i++] = "You are an eldritch horror.";
+ if (r_ptr->flags2 & RF2_OPEN_DOOR)
+ info[i++] = "You can open doors.";
+ else
+ info[i++] = "You cannot open doors.";
+ if (r_ptr->flags2 & RF2_BASH_DOOR)
+ info[i++] = "You can bash doors.";
+ else
+ info[i++] = "You cannot bash doors.";
+ if (r_ptr->flags2 & RF2_PASS_WALL)
+ info[i++] = "You can pass walls.";
+ if (r_ptr->flags2 & RF2_KILL_WALL)
+ info[i++] = "You destroy walls.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_MOVE_BODY)
+ info[i++] = "You can move monsters.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_ORC)
+ info[i++] = "You have orc blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_TROLL)
+ info[i++] = "You have troll blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_GIANT)
+ info[i++] = "You have giant blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_DRAGON)
+ info[i++] = "You have dragon blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_DEMON)
+ info[i++] = "You have demon blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_UNDEAD)
+ info[i++] = "You are an undead.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_ANIMAL)
+ info[i++] = "You are an animal.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_THUNDERLORD)
+ info[i++] = "You have thunderlord blood in your veins.";
+ if (r_ptr->flags3 & RF3_EVIL)
+ info[i++] = "You are inherently evil.";
+ else if (r_ptr->flags3 & RF3_GOOD)
+ info[i++] = "You are inherently good.";
+ if (r_ptr->flags3 & RF3_AURA_COLD)
+ info[i++] = "You are surrounded by a chilly aura.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_NONLIVING)
+ info[i++] = "You are not living.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_HURT_LITE)
+ info[i++] = "Your eyes are vulnerable to bright light.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_HURT_ROCK)
+ info[i++] = "You can be hurt by rock remover.";
+ if (r_ptr->flags3 & RF3_SUSCEP_FIRE)
+ info[i++] = "You are vulnerable to fire.";
+ if (r_ptr->flags3 & RF3_SUSCEP_COLD)
+ info[i++] = "You are vulnerable to cold.";
+ if (r_ptr->flags3 & RF3_RES_TELE)
+ info[i++] = "You are resistant to teleportation.";
+ if (r_ptr->flags3 & RF3_RES_NETH)
+ info[i++] = "You are resistant to nether.";
+ if (r_ptr->flags3 & RF3_RES_WATE)
+ info[i++] = "You are resistant to water.";
+ if (r_ptr->flags3 & RF3_RES_PLAS)
+ info[i++] = "You are resistant to plasma.";
+ if (r_ptr->flags3 & RF3_RES_WATE)
+ info[i++] = "You are resistant to nexus.";
+ if (r_ptr->flags3 & RF3_RES_DISE)
+ info[i++] = "You are resistant to disease.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_NO_SLEEP)
+ info[i++] = "You cannot be slept.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_UNIQUE_4)
+ info[i++] = "You are a Nazgul.";
+ if (r_ptr->flags3 & RF3_NO_FEAR)
+ info[i++] = "You are immune to fear.";
+ if (r_ptr->flags3 & RF3_NO_STUN)
+ info[i++] = "You are immune to stun.";
+ if (r_ptr->flags3 & RF3_NO_CONF)
+ info[i++] = "You are immune to confusion.";
+ if (r_ptr->flags3 & RF3_NO_SLEEP)
+ info[i++] = "You are immune to sleep.";
+
+ if (r_ptr->flags4 & RF4_SHRIEK)
+ info[i++] = "You can aggravate monsters.";
+ if (r_ptr->flags4 & RF4_ROCKET)
+ info[i++] = "You can fire a rocket.";
+ if (r_ptr->flags4 & RF4_ARROW_1)
+ info[i++] = "You can fire a light arrow.";
+ if (r_ptr->flags4 & RF4_ARROW_2)
+ info[i++] = "You can fire a heavy arrow.";
+ if (r_ptr->flags4 & RF4_ARROW_3)
+ info[i++] = "You can fire a light missile.";
+ if (r_ptr->flags4 & RF4_ARROW_4)
+ info[i++] = "You can fire a heavy missile.";
+ if (r_ptr->flags4 & RF4_BR_ACID)
+ info[i++] = "You can breathe acid.";
+ if (r_ptr->flags4 & RF4_BR_ELEC)
+ info[i++] = "You can breathe electricity.";
+ if (r_ptr->flags4 & RF4_BR_FIRE)
+ info[i++] = "You can breathe fire.";
+ if (r_ptr->flags4 & RF4_BR_COLD)
+ info[i++] = "You can breathe cold.";
+ if (r_ptr->flags4 & RF4_BR_POIS)
+ info[i++] = "You can breathe poison.";
+ if (r_ptr->flags4 & RF4_BR_NETH)
+ info[i++] = "You can breathe nether.";
+ if (r_ptr->flags4 & RF4_BR_LITE)
+ info[i++] = "You can breathe light.";
+ if (r_ptr->flags4 & RF4_BR_DARK)
+ info[i++] = "You can breathe darkness.";
+ if (r_ptr->flags4 & RF4_BR_CONF)
+ info[i++] = "You can breathe confusion.";
+ if (r_ptr->flags4 & RF4_BR_SOUN)
+ info[i++] = "You can breathe sound.";
+ if (r_ptr->flags4 & RF4_BR_CHAO)
+ info[i++] = "You can breathe chaos.";
+ if (r_ptr->flags4 & RF4_BR_DISE)
+ info[i++] = "You can breathe disenchantment.";
+ if (r_ptr->flags4 & RF4_BR_NEXU)
+ info[i++] = "You can breathe nexus.";
+ if (r_ptr->flags4 & RF4_BR_TIME)
+ info[i++] = "You can breathe time.";
+ if (r_ptr->flags4 & RF4_BR_INER)
+ info[i++] = "You can breathe inertia.";
+ if (r_ptr->flags4 & RF4_BR_GRAV)
+ info[i++] = "You can breathe gravity.";
+ if (r_ptr->flags4 & RF4_BR_SHAR)
+ info[i++] = "You can breathe shards.";
+ if (r_ptr->flags4 & RF4_BR_PLAS)
+ info[i++] = "You can breathe plasma.";
+ if (r_ptr->flags4 & RF4_BR_WALL)
+ info[i++] = "You can breathe force.";
+ if (r_ptr->flags4 & RF4_BR_MANA)
+ info[i++] = "You can breathe mana.";
+ if (r_ptr->flags4 & RF4_BR_NUKE)
+ info[i++] = "You can breathe nuke.";
+ if (r_ptr->flags4 & RF4_BR_DISI)
+ info[i++] = "You can breathe disintegration.";
+ if (r_ptr->flags5 & RF5_BA_ACID)
+ info[i++] = "You can cast a ball of acid.";
+ if (r_ptr->flags5 & RF5_BA_ELEC)
+ info[i++] = "You can cast a ball of electricity.";
+ if (r_ptr->flags5 & RF5_BA_FIRE)
+ info[i++] = "You can cast a ball of fire.";
+ if (r_ptr->flags5 & RF5_BA_COLD)
+ info[i++] = "You can cast a ball of cold.";
+ if (r_ptr->flags5 & RF5_BA_POIS)
+ info[i++] = "You can cast a ball of poison.";
+ if (r_ptr->flags5 & RF5_BA_NETH)
+ info[i++] = "You can cast a ball of nether.";
+ if (r_ptr->flags5 & RF5_BA_WATE)
+ info[i++] = "You can cast a ball of water.";
+ /* Not implemented */
+ if (r_ptr->flags5 & RF5_DRAIN_MANA)
+ info[i++] = "You can drain mana.";
+ if (r_ptr->flags5 & RF5_MIND_BLAST)
+ info[i++] = "You can cause mind blasting.";
+ if (r_ptr->flags5 & RF5_BRAIN_SMASH)
+ info[i++] = "You can cause brain smashing.";
+ if (r_ptr->flags5 & RF5_CAUSE_1)
+ info[i++] = "You can cause light wounds.";
+ if (r_ptr->flags5 & RF5_CAUSE_2)
+ info[i++] = "You can cause serious wounds.";
+ if (r_ptr->flags5 & RF5_CAUSE_3)
+ info[i++] = "You can cause critical wounds.";
+ if (r_ptr->flags5 & RF5_CAUSE_4)
+ info[i++] = "You can cause mortal wounds.";
+ if (r_ptr->flags5 & RF5_BO_ACID)
+ info[i++] = "You can cast a bolt of acid.";
+ if (r_ptr->flags5 & RF5_BO_ELEC)
+ info[i++] = "You can cast a bolt of electricity.";
+ if (r_ptr->flags5 & RF5_BO_FIRE)
+ info[i++] = "You can cast a bolt of fire.";
+ if (r_ptr->flags5 & RF5_BO_COLD)
+ info[i++] = "You can cast a bolt of cold.";
+ if (r_ptr->flags5 & RF5_BO_POIS)
+ info[i++] = "You can cast a bolt of poison.";
+ if (r_ptr->flags5 & RF5_BO_NETH)
+ info[i++] = "You can cast a bolt of nether.";
+ if (r_ptr->flags5 & RF5_BO_WATE)
+ info[i++] = "You can cast a bolt of water.";
+ if (r_ptr->flags5 & RF5_BO_MANA)
+ info[i++] = "You can cast a bolt of mana.";
+ if (r_ptr->flags5 & RF5_BO_PLAS)
+ info[i++] = "You can cast a bolt of plasma.";
+ if (r_ptr->flags5 & RF5_BO_ICEE)
+ info[i++] = "You can cast a bolt of ice.";
+ if (r_ptr->flags5 & RF5_MISSILE)
+ info[i++] = "You can cast magic missile.";
+ if (r_ptr->flags5 & RF5_SCARE)
+ info[i++] = "You can terrify.";
+ if (r_ptr->flags5 & RF5_BLIND)
+ info[i++] = "You can blind.";
+ if (r_ptr->flags5 & RF5_CONF)
+ info[i++] = "You can use confusion.";
+ if (r_ptr->flags5 & RF5_SLOW)
+ info[i++] = "You can cast slow.";
+ if (r_ptr->flags5 & RF5_HOLD)
+ info[i++] = "You can touch to paralyze.";
+ if (r_ptr->flags6 & RF6_HASTE)
+ info[i++] = "You can haste yourself.";
+ if (r_ptr->flags6 & RF6_HAND_DOOM)
+ info[i++] = "You can invoke Hand of Doom.";
+ if (r_ptr->flags6 & RF6_HEAL)
+ info[i++] = "You can heal yourself.";
+ if (r_ptr->flags6 & RF6_BLINK)
+ info[i++] = "You can blink.";
+ if (r_ptr->flags6 & RF6_TPORT)
+ info[i++] = "You can teleport.";
+ if (r_ptr->flags6 & RF6_TELE_TO)
+ info[i++] = "You can go between places.";
+ if (r_ptr->flags6 & RF6_TELE_AWAY)
+ info[i++] = "You can teleport away.";
+ if (r_ptr->flags6 & RF6_TELE_LEVEL)
+ info[i++] = "You can teleport level.";
+ if (r_ptr->flags6 & RF6_DARKNESS)
+ info[i++] = "You can create darkness.";
+ if (r_ptr->flags6 & RF6_TRAPS)
+ info[i++] = "You can create traps.";
+ /* Not implemented */
+ if (r_ptr->flags6 & RF6_FORGET)
+ info[i++] = "You can fade memories.";
+ if (r_ptr->flags6 & RF6_RAISE_DEAD)
+ info[i++] = "You can Raise the Dead.";
+ if (r_ptr->flags6 & RF6_S_BUG)
+ info[i++] = "You can magically summon a Software Bugs.";
+ if (r_ptr->flags6 & RF6_S_RNG)
+ info[i++] = "You can magically summon the RNG.";
+ if (r_ptr->flags6 & RF6_S_THUNDERLORD)
+ info[i++] = "You can magically summon some Thunderlords.";
+ if (r_ptr->flags6 & RF6_S_KIN)
+ info[i++] = "You can magically summon some Kins.";
+ if (r_ptr->flags6 & RF6_S_HI_DEMON)
+ info[i++] = "You can magically summon greater demons.";
+ if (r_ptr->flags6 & RF6_S_MONSTER)
+ info[i++] = "You can magically summon a monster.";
+ if (r_ptr->flags6 & RF6_S_MONSTERS)
+ info[i++] = "You can magically summon monsters.";
+ if (r_ptr->flags6 & RF6_S_ANT)
+ info[i++] = "You can magically summon ants.";
+ if (r_ptr->flags6 & RF6_S_SPIDER)
+ info[i++] = "You can magically summon spiders.";
+ if (r_ptr->flags6 & RF6_S_HOUND)
+ info[i++] = "You can magically summon hounds.";
+ if (r_ptr->flags6 & RF6_S_HYDRA)
+ info[i++] = "You can magically summon hydras.";
+ if (r_ptr->flags6 & RF6_S_ANGEL)
+ info[i++] = "You can magically summon an angel.";
+ if (r_ptr->flags6 & RF6_S_DEMON)
+ info[i++] = "You can magically summon a demon.";
+ if (r_ptr->flags6 & RF6_S_UNDEAD)
+ info[i++] = "You can magically summon an undead.";
+ if (r_ptr->flags6 & RF6_S_DRAGON)
+ info[i++] = "You can magically summon a dragon.";
+ if (r_ptr->flags6 & RF6_S_HI_UNDEAD)
+ info[i++] = "You can magically summon greater undead.";
+ if (r_ptr->flags6 & RF6_S_HI_DRAGON)
+ info[i++] = "You can magically summon greater dragons.";
+ if (r_ptr->flags6 & RF6_S_WRAITH)
+ info[i++] = "You can magically summon a wraith.";
+ /* Not implemented */
+ if (r_ptr->flags6 & RF6_S_UNIQUE)
+ info[i++] = "You can magically summon an unique monster.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ info[i++] = "You are aquatic.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_CAN_SWIM)
+ info[i++] = "You can swim.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ info[i++] = "You can fly.";
+ if ((r_ptr->flags7 & RF7_MORTAL) == 0)
+ info[i++] = "You are immortal.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_NAZGUL)
+ info[i++] = "You are a Nazgul.";
+
+ if (r_ptr->flags7 & RF7_SPIDER)
+ info[i++] = "You are a spider.";
+
+ if (r_ptr->flags8 & RF8_WILD_TOWN)
+ info[i++] = "You appear in towns.";
+ if (r_ptr->flags8 & RF8_WILD_SHORE)
+ info[i++] = "You appear on the shore.";
+ if (r_ptr->flags8 & RF8_WILD_OCEAN)
+ info[i++] = "You appear in the ocean.";
+ if (r_ptr->flags8 & RF8_WILD_WASTE)
+ info[i++] = "You appear in the waste.";
+ if (r_ptr->flags8 & RF8_WILD_WOOD)
+ info[i++] = "You appear in woods.";
+ if (r_ptr->flags8 & RF8_WILD_VOLCANO)
+ info[i++] = "You appear in volcanos.";
+ if (r_ptr->flags8 & RF8_WILD_MOUNTAIN)
+ info[i++] = "You appear in the mountains.";
+ if (r_ptr->flags8 & RF8_WILD_GRASS)
+ info[i++] = "You appear in grassy areas.";
+
+ if (r_ptr->flags9 & RF9_SUSCEP_ACID)
+ info[i++] = "You are vulnerable to acid.";
+ if (r_ptr->flags9 & RF9_SUSCEP_ELEC)
+ info[i++] = "You are vulnerable to electricity.";
+ if (r_ptr->flags9 & RF9_SUSCEP_POIS)
+ info[i++] = "You are vulnerable to poison.";
+ if (r_ptr->flags9 & RF9_KILL_TREES)
+ info[i++] = "You can eat trees.";
+ if (r_ptr->flags9 & RF9_WYRM_PROTECT)
+ info[i++] = "You are protected by great wyrms of power.";
+ }
+
+ /* List powers */
+ for (iter = 0; iter < power_max; iter++)
+ {
+ if (p_ptr->powers[iter])
+ {
+ info[i++] = powers_type[iter].desc_text;
+ }
+ }
+
+ if (p_ptr->allow_one_death)
+ {
+ info[i++] = "The Blood of Life flows through your veins.";
+ }
+ if (p_ptr->blind)
+ {
+ info[i++] = "You cannot see.";
+ }
+ if (p_ptr->confused)
+ {
+ info[i++] = "You are confused.";
+ }
+ if (p_ptr->afraid)
+ {
+ info[i++] = "You are terrified.";
+ }
+ if (p_ptr->cut)
+ {
+ info[i++] = "You are bleeding.";
+ }
+ if (p_ptr->stun)
+ {
+ info[i++] = "You are stunned.";
+ }
+ if (p_ptr->poisoned)
+ {
+ info[i++] = "You are poisoned.";
+ }
+ if (p_ptr->image)
+ {
+ info[i++] = "You are hallucinating.";
+ }
+ if (p_ptr->aggravate)
+ {
+ info[i++] = "You aggravate monsters.";
+ }
+ if (p_ptr->teleport)
+ {
+ info[i++] = "Your position is very uncertain.";
+ }
+ if (p_ptr->blessed)
+ {
+ info[i++] = "You feel righteous.";
+ }
+ if (p_ptr->hero)
+ {
+ info[i++] = "You feel heroic.";
+ }
+ if (p_ptr->shero)
+ {
+ info[i++] = "You are in a battle rage.";
+ }
+ if (p_ptr->protevil)
+ {
+ info[i++] = "You are protected from evil.";
+ }
+ if (p_ptr->protgood)
+ {
+ info[i++] = "You are protected from good.";
+ }
+ if (p_ptr->shield)
+ {
+ info[i++] = "You are protected by a mystic shield.";
+ }
+ if (p_ptr->invuln)
+ {
+ info[i++] = "You are temporarily invulnerable.";
+ }
+ if (p_ptr->confusing)
+ {
+ info[i++] = "Your hands are glowing dull red.";
+ }
+ if (p_ptr->searching)
+ {
+ info[i++] = "You are looking around very carefully.";
+ }
+ if (p_ptr->new_spells)
+ {
+ info[i++] = "You can learn some spells/prayers.";
+ }
+ if (p_ptr->word_recall)
+ {
+ info[i++] = "You will soon be recalled.";
+ }
+ if (p_ptr->see_infra)
+ {
+ info[i++] = "Your eyes are sensitive to infrared light.";
+ }
+ if (p_ptr->see_inv)
+ {
+ info[i++] = "You can see invisible creatures.";
+ }
+ if (p_ptr->magical_breath)
+ {
+ info[i++] = "You can breathe without air.";
+ }
+ else if (p_ptr->water_breath)
+ {
+ info[i++] = "You can breathe underwater.";
+ }
+ if (p_ptr->ffall)
+ {
+ info[i++] = "You levitate just over the ground.";
+ }
+ if (p_ptr->climb)
+ {
+ info[i++] = "You can climb high mountains.";
+ }
+ if (p_ptr->free_act)
+ {
+ info[i++] = "You have free action.";
+ }
+ if (p_ptr->regenerate)
+ {
+ info[i++] = "You regenerate quickly.";
+ }
+ if (p_ptr->slow_digest)
+ {
+ info[i++] = "Your appetite is small.";
+ }
+ if (p_ptr->telepathy)
+ {
+ if (p_ptr->telepathy & ESP_ALL) info[i++] = "You have ESP.";
+ else
+ {
+ if (p_ptr->telepathy & ESP_ORC) info[i++] = "You can sense the presence of orcs.";
+ if (p_ptr->telepathy & ESP_TROLL) info[i++] = "You can sense the presence of trolls.";
+ if (p_ptr->telepathy & ESP_DRAGON) info[i++] = "You can sense the presence of dragons.";
+ if (p_ptr->telepathy & ESP_SPIDER) info[i++] = "You can sense the presence of spiders.";
+ if (p_ptr->telepathy & ESP_GIANT) info[i++] = "You can sense the presence of giants.";
+ if (p_ptr->telepathy & ESP_DEMON) info[i++] = "You can sense the presence of demons.";
+ if (p_ptr->telepathy & ESP_UNDEAD) info[i++] = "You can sense presence of undead.";
+ if (p_ptr->telepathy & ESP_EVIL) info[i++] = "You can sense the presence of evil beings.";
+ if (p_ptr->telepathy & ESP_ANIMAL) info[i++] = "You can sense the presence of animals.";
+ if (p_ptr->telepathy & ESP_THUNDERLORD) info[i++] = "You can sense the presence of thunderlords.";
+ if (p_ptr->telepathy & ESP_GOOD) info[i++] = "You can sense the presence of good beings.";
+ if (p_ptr->telepathy & ESP_NONLIVING) info[i++] = "You can sense the presence of non-living things.";
+ if (p_ptr->telepathy & ESP_UNIQUE) info[i++] = "You can sense the presence of unique beings.";
+ }
+ }
+ if (!luck( -100, 100))
+ {
+ info[i++] = "You have normal luck.";
+ }
+ else if (luck( -100, 100) < 0)
+ {
+ if (luck( -100, 100) < -90)
+ {
+ info[i++] = "You are incredibly unlucky.";
+ }
+ else if (luck( -100, 100) < -60)
+ {
+ info[i++] = "You are extremely unlucky.";
+ }
+ else if (luck( -100, 100) < -30)
+ {
+ info[i++] = "You are very unlucky.";
+ }
+ else
+ {
+ info[i++] = "You are unlucky.";
+ }
+ }
+ else if (luck( -100, 100) > 0)
+ {
+ if (luck( -100, 100) > 90)
+ {
+ info[i++] = "You are incredibly lucky.";
+ }
+ else if (luck( -100, 100) > 60)
+ {
+ info[i++] = "You are extremely lucky.";
+ }
+ else if (luck( -100, 100) > 30)
+ {
+ info[i++] = "You are very lucky.";
+ }
+ else
+ {
+ info[i++] = "You are lucky.";
+ }
+ }
+ if (p_ptr->auto_id)
+ {
+ info[i++] = "You know everything.";
+ }
+ if (p_ptr->hold_life)
+ {
+ info[i++] = "You have a firm hold on your life force.";
+ }
+ if (p_ptr->reflect)
+ {
+ info[i++] = "You reflect arrows and bolts.";
+ }
+ if (p_ptr->sh_fire)
+ {
+ info[i++] = "You are surrounded with a fiery aura.";
+ }
+ if (p_ptr->sh_elec)
+ {
+ info[i++] = "You are surrounded with electricity.";
+ }
+ if (p_ptr->antimagic)
+ {
+ info[i++] = "You are surrounded by an anti-magic field.";
+ }
+ if (p_ptr->anti_magic)
+ {
+ info[i++] = "You are surrounded by an anti-magic shell.";
+ }
+ if (p_ptr->wraith_form)
+ {
+ info[i++] = "You are incorporeal.";
+ }
+ if (p_ptr->anti_tele)
+ {
+ info[i++] = "You cannot teleport.";
+ }
+ if (p_ptr->lite)
+ {
+ info[i++] = "You are carrying a permanent light.";
+ }
+
+ if (p_ptr->immune_acid)
+ {
+ info[i++] = "You are completely immune to acid.";
+ }
+ else if ((p_ptr->resist_acid) && (p_ptr->oppose_acid))
+ {
+ info[i++] = "You resist acid exceptionally well.";
+ }
+ else if ((p_ptr->resist_acid) || (p_ptr->oppose_acid))
+ {
+ info[i++] = "You are resistant to acid.";
+ }
+
+ if (p_ptr->immune_elec)
+ {
+ info[i++] = "You are completely immune to lightning.";
+ }
+ else if ((p_ptr->resist_elec) && (p_ptr->oppose_elec))
+ {
+ info[i++] = "You resist lightning exceptionally well.";
+ }
+ else if ((p_ptr->resist_elec) || (p_ptr->oppose_elec))
+ {
+ info[i++] = "You are resistant to lightning.";
+ }
+
+ if (p_ptr->immune_fire)
+ {
+ info[i++] = "You are completely immune to fire.";
+ }
+ else if ((p_ptr->resist_fire) && (p_ptr->oppose_fire))
+ {
+ info[i++] = "You resist fire exceptionally well.";
+ }
+ else if ((p_ptr->resist_fire) || (p_ptr->oppose_fire))
+ {
+ info[i++] = "You are resistant to fire.";
+ }
+ else if (p_ptr->sensible_fire)
+ {
+ info[i++] = "You are very vulnerable to fire.";
+ }
+
+ if (p_ptr->immune_cold)
+ {
+ info[i++] = "You are completely immune to cold.";
+ }
+ else if ((p_ptr->resist_cold) && (p_ptr->oppose_cold))
+ {
+ info[i++] = "You resist cold exceptionally well.";
+ }
+ else if ((p_ptr->resist_cold) || (p_ptr->oppose_cold))
+ {
+ info[i++] = "You are resistant to cold.";
+ }
+
+ if ((p_ptr->resist_pois) && (p_ptr->oppose_pois))
+ {
+ info[i++] = "You resist poison exceptionally well.";
+ }
+ else if ((p_ptr->resist_pois) || (p_ptr->oppose_pois))
+ {
+ info[i++] = "You are resistant to poison.";
+ }
+
+ if (p_ptr->resist_lite)
+ {
+ info[i++] = "You are resistant to bright light.";
+ }
+ if (p_ptr->resist_dark)
+ {
+ info[i++] = "You are resistant to darkness.";
+ }
+ if (p_ptr->resist_conf)
+ {
+ info[i++] = "You are resistant to confusion.";
+ }
+ if (p_ptr->resist_sound)
+ {
+ info[i++] = "You are resistant to sonic attacks.";
+ }
+ if (p_ptr->resist_disen)
+ {
+ info[i++] = "You are resistant to disenchantment.";
+ }
+ if (p_ptr->resist_chaos)
+ {
+ info[i++] = "You are resistant to chaos.";
+ }
+ if (p_ptr->resist_shard)
+ {
+ info[i++] = "You are resistant to blasts of shards.";
+ }
+ if (p_ptr->resist_nexus)
+ {
+ info[i++] = "You are resistant to nexus attacks.";
+ }
+ if (p_ptr->immune_neth)
+ {
+ info[i++] = "You are immune to nether forces.";
+ }
+ else if (p_ptr->resist_neth)
+ {
+ info[i++] = "You are resistant to nether forces.";
+ }
+ if (p_ptr->resist_fear)
+ {
+ info[i++] = "You are completely fearless.";
+ }
+ if (p_ptr->resist_blind)
+ {
+ info[i++] = "Your eyes are resistant to blindness.";
+ }
+ if (p_ptr->resist_continuum)
+ {
+ info[i++] = "The space-time continuum cannot be disrupted near you.";
+ }
+
+ if (p_ptr->sustain_str)
+ {
+ info[i++] = "Your strength is sustained.";
+ }
+ if (p_ptr->sustain_int)
+ {
+ info[i++] = "Your intelligence is sustained.";
+ }
+ if (p_ptr->sustain_wis)
+ {
+ info[i++] = "Your wisdom is sustained.";
+ }
+ if (p_ptr->sustain_con)
+ {
+ info[i++] = "Your constitution is sustained.";
+ }
+ if (p_ptr->sustain_dex)
+ {
+ info[i++] = "Your dexterity is sustained.";
+ }
+ if (p_ptr->sustain_chr)
+ {
+ info[i++] = "Your charisma is sustained.";
+ }
+ if (p_ptr->black_breath)
+ {
+ info[i++] = "You suffer from Black Breath.";
+ }
+
+ if (f1 & (TR1_STR))
+ {
+ info[i++] = "Your strength is affected by your equipment.";
+ }
+ if (f1 & (TR1_INT))
+ {
+ info[i++] = "Your intelligence is affected by your equipment.";
+ }
+ if (f1 & (TR1_WIS))
+ {
+ info[i++] = "Your wisdom is affected by your equipment.";
+ }
+ if (f1 & (TR1_DEX))
+ {
+ info[i++] = "Your dexterity is affected by your equipment.";
+ }
+ if (f1 & (TR1_CON))
+ {
+ info[i++] = "Your constitution is affected by your equipment.";
+ }
+ if (f1 & (TR1_CHR))
+ {
+ info[i++] = "Your charisma is affected by your equipment.";
+ }
+
+ if (f1 & (TR1_STEALTH))
+ {
+ info[i++] = "Your stealth is affected by your equipment.";
+ }
+ if (f1 & (TR1_SEARCH))
+ {
+ info[i++] = "Your searching ability is affected by your equipment.";
+ }
+ if (f1 & (TR1_INFRA))
+ {
+ info[i++] = "Your infravision is affected by your equipment.";
+ }
+ if (f1 & (TR1_TUNNEL))
+ {
+ info[i++] = "Your digging ability is affected by your equipment.";
+ }
+ if (f1 & (TR1_SPEED))
+ {
+ info[i++] = "Your speed is affected by your equipment.";
+ }
+ if (f1 & (TR1_BLOWS))
+ {
+ info[i++] = "Your attack speed is affected by your equipment.";
+ }
+ if (f5 & (TR5_CRIT))
+ {
+ info[i++] = "Your ability to score critical hits is affected by your equipment.";
+ }
+
+
+ /* Access the current weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Analyze the weapon */
+ if (o_ptr->k_idx)
+ {
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Indicate Blessing */
+ if (f3 & (TR3_BLESSED))
+ {
+ info[i++] = "Your weapon has been blessed by the gods.";
+ }
+
+ if (f1 & (TR1_CHAOTIC))
+ {
+ info[i++] = "Your weapon is branded with the Sign of Chaos.";
+ }
+
+ /* Hack */
+ if (f1 & (TR1_IMPACT))
+ {
+ info[i++] = "The impact of your weapon can cause earthquakes.";
+ }
+
+ if (f1 & (TR1_VORPAL))
+ {
+ info[i++] = "Your weapon is very sharp.";
+ }
+
+ if (f1 & (TR1_VAMPIRIC))
+ {
+ info[i++] = "Your weapon drains life from your foes.";
+ }
+
+ /* Special "Attack Bonuses" */
+ if (f1 & (TR1_BRAND_ACID))
+ {
+ info[i++] = "Your weapon melts your foes.";
+ }
+ if (f1 & (TR1_BRAND_ELEC))
+ {
+ info[i++] = "Your weapon shocks your foes.";
+ }
+ if (f1 & (TR1_BRAND_FIRE))
+ {
+ info[i++] = "Your weapon burns your foes.";
+ }
+ if (f1 & (TR1_BRAND_COLD))
+ {
+ info[i++] = "Your weapon freezes your foes.";
+ }
+ if (f1 & (TR1_BRAND_POIS))
+ {
+ info[i++] = "Your weapon poisons your foes.";
+ }
+
+ /* Special "slay" flags */
+ if (f1 & (TR1_SLAY_ANIMAL))
+ {
+ info[i++] = "Your weapon strikes at animals with extra force.";
+ }
+ if (f1 & (TR1_SLAY_EVIL))
+ {
+ info[i++] = "Your weapon strikes at evil with extra force.";
+ }
+ if (f1 & (TR1_SLAY_UNDEAD))
+ {
+ info[i++] = "Your weapon strikes at undead with holy wrath.";
+ }
+ if (f1 & (TR1_SLAY_DEMON))
+ {
+ info[i++] = "Your weapon strikes at demons with holy wrath.";
+ }
+ if (f1 & (TR1_SLAY_ORC))
+ {
+ info[i++] = "Your weapon is especially deadly against orcs.";
+ }
+ if (f1 & (TR1_SLAY_TROLL))
+ {
+ info[i++] = "Your weapon is especially deadly against trolls.";
+ }
+ if (f1 & (TR1_SLAY_GIANT))
+ {
+ info[i++] = "Your weapon is especially deadly against giants.";
+ }
+ if (f1 & (TR1_SLAY_DRAGON))
+ {
+ info[i++] = "Your weapon is especially deadly against dragons.";
+ }
+
+ /* Special "kill" flags */
+ if (f1 & (TR1_KILL_DRAGON))
+ {
+ info[i++] = "Your weapon is a great bane of dragons.";
+ }
+ /* Special "kill" flags */
+ if (f5 & (TR5_KILL_DEMON))
+ {
+ info[i++] = "Your weapon is a great bane of demons.";
+ }
+ /* Special "kill" flags */
+ if (f5 & (TR5_KILL_UNDEAD))
+ {
+ info[i++] = "Your weapon is a great bane of undeads.";
+ }
+ }
+
+ /* Print on screen or in a file ? */
+ if (fff == NULL)
+ {
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Erase the screen */
+ for (k = 1; k < 24; k++) prt("", k, 13);
+
+ /* Label the information */
+ prt(" Your Attributes:", 1, 15);
+
+ /* We will print on top of the map (column 13) */
+ for (k = 2, j = 0; j < i; j++)
+ {
+ /* Show the info */
+ prt(info[j], k++, 15);
+
+ /* Every 20 entries (lines 2 to 21), start over */
+ if ((k == 22) && (j + 1 < i))
+ {
+ prt("-- more --", k, 15);
+ inkey();
+ for (; k > 2; k--) prt("", k, 15);
+ }
+ }
+
+ /* Pause */
+ prt("[Press any key to continue]", k, 13);
+ inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+ else
+ {
+ /* Label the information */
+ fprintf(fff, " Your Attributes:\n");
+
+ /* We will print on top of the map (column 13) */
+ for (j = 0; j < i; j ++)
+ {
+ /* Show the info */
+ fprintf(fff, "%s\n", info[j]);
+ }
+ }
+}
+
+
+static int report_magics_aux(int dur)
+{
+ if (dur <= 5)
+ {
+ return 0;
+ }
+ else if (dur <= 10)
+ {
+ return 1;
+ }
+ else if (dur <= 20)
+ {
+ return 2;
+ }
+ else if (dur <= 50)
+ {
+ return 3;
+ }
+ else if (dur <= 100)
+ {
+ return 4;
+ }
+ else if (dur <= 200)
+ {
+ return 5;
+ }
+ else
+ {
+ return 6;
+ }
+}
+
+static cptr report_magic_durations[] =
+{
+ "for a short time",
+ "for a little while",
+ "for a while",
+ "for a long while",
+ "for a long time",
+ "for a very long time",
+ "for an incredibly long time",
+ "until you hit a monster"
+};
+
+
+void report_magics(void)
+{
+ int i = 0, j, k;
+
+ char Dummy[80];
+
+ cptr info[128];
+ int info2[128];
+
+ if (p_ptr->blind)
+ {
+ info2[i] = report_magics_aux(p_ptr->blind);
+ info[i++] = "You cannot see";
+ }
+ if (p_ptr->confused)
+ {
+ info2[i] = report_magics_aux(p_ptr->confused);
+ info[i++] = "You are confused";
+ }
+ if (p_ptr->afraid)
+ {
+ info2[i] = report_magics_aux(p_ptr->afraid);
+ info[i++] = "You are terrified";
+ }
+ if (p_ptr->poisoned)
+ {
+ info2[i] = report_magics_aux(p_ptr->poisoned);
+ info[i++] = "You are poisoned";
+ }
+ if (p_ptr->image)
+ {
+ info2[i] = report_magics_aux(p_ptr->image);
+ info[i++] = "You are hallucinating";
+ }
+
+ if (p_ptr->blessed)
+ {
+ info2[i] = report_magics_aux(p_ptr->blessed);
+ info[i++] = "You feel righteous";
+ }
+ if (p_ptr->hero)
+ {
+ info2[i] = report_magics_aux(p_ptr->hero);
+ info[i++] = "You feel heroic";
+ }
+ if (p_ptr->shero)
+ {
+ info2[i] = report_magics_aux(p_ptr->shero);
+ info[i++] = "You are in a battle rage";
+ }
+ if (p_ptr->protevil)
+ {
+ info2[i] = report_magics_aux(p_ptr->protevil);
+ info[i++] = "You are protected from evil";
+ }
+ if (p_ptr->protgood)
+ {
+ info2[i] = report_magics_aux(p_ptr->protgood);
+ info[i++] = "You are protected from good";
+ }
+ if (p_ptr->shield)
+ {
+ info2[i] = report_magics_aux(p_ptr->shield);
+ info[i++] = "You are protected by a mystic shield";
+ }
+ if (p_ptr->invuln)
+ {
+ info2[i] = report_magics_aux(p_ptr->invuln);
+ info[i++] = "You are invulnerable";
+ }
+ if (p_ptr->tim_wraith)
+ {
+ info2[i] = report_magics_aux(p_ptr->tim_wraith);
+ info[i++] = "You are incorporeal";
+ }
+ if (p_ptr->confusing)
+ {
+ info2[i] = 7;
+ info[i++] = "Your hands are glowing dull red.";
+ }
+ if (p_ptr->word_recall)
+ {
+ info2[i] = report_magics_aux(p_ptr->word_recall);
+ info[i++] = "You waiting to be recalled";
+ }
+ if (p_ptr->oppose_acid)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_acid);
+ info[i++] = "You are resistant to acid";
+ }
+ if (p_ptr->oppose_elec)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_elec);
+ info[i++] = "You are resistant to lightning";
+ }
+ if (p_ptr->oppose_fire)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_fire);
+ info[i++] = "You are resistant to fire";
+ }
+ if (p_ptr->oppose_cold)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_cold);
+ info[i++] = "You are resistant to cold";
+ }
+ if (p_ptr->oppose_pois)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_pois);
+ info[i++] = "You are resistant to poison";
+ }
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Erase the screen */
+ for (k = 1; k < 24; k++) prt("", k, 13);
+
+ /* Label the information */
+ prt(" Your Current Magic:", 1, 15);
+
+ /* We will print on top of the map (column 13) */
+ for (k = 2, j = 0; j < i; j++)
+ {
+ /* Show the info */
+ sprintf( Dummy, "%s %s.", info[j],
+ report_magic_durations[info2[j]] );
+ prt(Dummy, k++, 15);
+
+ /* Every 20 entries (lines 2 to 21), start over */
+ if ((k == 22) && (j + 1 < i))
+ {
+ prt("-- more --", k, 15);
+ inkey();
+ for (; k > 2; k--) prt("", k, 15);
+ }
+ }
+
+ /* Pause */
+ prt("[Press any key to continue]", k, 13);
+ inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+}
+
+
+
+/*
+ * Forget everything
+ */
+bool_ lose_all_info(void)
+{
+ int i;
+
+ /* Forget info about objects */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Allow "protection" by the MENTAL flag */
+ if (o_ptr->ident & (IDENT_MENTAL)) continue;
+
+ /* Remove sensing */
+ o_ptr->sense = SENSE_NONE;
+
+ /* Hack -- Clear the "empty" flag */
+ o_ptr->ident &= ~(IDENT_EMPTY);
+
+ /* Hack -- Clear the "known" flag */
+ o_ptr->ident &= ~(IDENT_KNOWN);
+
+ /* Hack -- Clear the "felt" flag */
+ o_ptr->ident &= ~(IDENT_SENSE);
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Mega-Hack -- Forget the map */
+ wiz_dark();
+
+ /* It worked */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Detect all traps on current panel
+ */
+bool_ detect_traps(int rad)
+{
+ int x, y;
+ bool_ detect = FALSE;
+ cave_type *c_ptr;
+
+
+ /* Scan the current panel */
+ for (y = p_ptr->py - rad; y <= p_ptr->py + rad; y++)
+ {
+ for (x = p_ptr->px - rad; x <= p_ptr->px + rad; x++)
+ {
+ /* Reject locations outside of dungeon */
+ if (!in_bounds(y, x)) continue;
+
+ /* Reject those out of radius */
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* mark as detected */
+ c_ptr->info |= CAVE_DETECT;
+
+ /* Detect invisible traps */
+ if (c_ptr->t_idx != 0)
+ {
+ /* Hack -- Remember detected traps */
+ c_ptr->info |= (CAVE_MARK);
+
+ /* Pick a trap */
+ pick_trap(y, x);
+
+ /* Obvious */
+ detect = TRUE;
+ }
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of traps!");
+ }
+
+ /*
+ * This reveals un-identified trap detection items,
+ * but so does leaving/entering trap-detected areas...
+ * There are a couple of possible solutions:
+ * (1) Immediately self-id such items (i.e. always returns TRUE)
+ * (2) add another parameter to function which tells if unaware
+ * item is used for trap detection, and if it is the case,
+ * do two-pass scanning, first scanning for traps if an unaware
+ * item is used and return FALSE there are none,
+ * followed by current implementation --pelpel
+ */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Result -- see my comment above -- pelpel */
+ /* return (detect); */
+ return (TRUE);
+}
+
+
+
+/*
+ * Detect all doors on current panel
+ */
+bool_ detect_doors(int rad)
+{
+ int y, x;
+
+ bool_ detect = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Scan the panel */
+ for (y = p_ptr->py - rad; y <= p_ptr->py + rad; y++)
+ {
+ for (x = p_ptr->px - rad; x <= p_ptr->px + rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ c_ptr = &cave[y][x];
+
+ /* Detect secret doors */
+ if (c_ptr->feat == FEAT_SECRET)
+ {
+ /* Remove feature mimics */
+ cave[y][x].mimic = 0;
+
+ /* Pick a door XXX XXX XXX */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+ }
+
+ /* Detect doors */
+ if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) ||
+ ((c_ptr->feat == FEAT_OPEN) ||
+ (c_ptr->feat == FEAT_BROKEN)))
+ {
+ /* Hack -- Memorize */
+ c_ptr->info |= (CAVE_MARK);
+
+ /* Reveal it */
+ /* c_ptr->mimic = 0; */
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /* Obvious */
+ detect = TRUE;
+ }
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of doors!");
+ }
+
+ /* Result */
+ return (detect);
+}
+
+
+/*
+ * Detect all stairs on current panel
+ */
+bool_ detect_stairs(int rad)
+{
+ int y, x;
+
+ bool_ detect = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Scan the panel */
+ for (y = p_ptr->py - rad; y <= p_ptr->py + rad; y++)
+ {
+ for (x = p_ptr->px - rad; x <= p_ptr->px + rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ c_ptr = &cave[y][x];
+
+ /* Detect stairs */
+ if ((c_ptr->feat == FEAT_LESS) ||
+ (c_ptr->feat == FEAT_MORE) ||
+ (c_ptr->feat == FEAT_SHAFT_DOWN) ||
+ (c_ptr->feat == FEAT_SHAFT_UP) ||
+ (c_ptr->feat == FEAT_WAY_LESS) ||
+ (c_ptr->feat == FEAT_WAY_MORE))
+ {
+ /* Hack -- Memorize */
+ c_ptr->info |= (CAVE_MARK);
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /* Obvious */
+ detect = TRUE;
+ }
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of ways out of this area!");
+ }
+
+ /* Result */
+ return (detect);
+}
+
+
+/*
+ * Detect any treasure on the current panel
+ */
+bool_ detect_treasure(int rad)
+{
+ int y, x;
+
+ bool_ detect = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Scan the current panel */
+ for (y = p_ptr->py - rad; y <= p_ptr->py + rad; y++)
+ {
+ for (x = p_ptr->px - rad; x <= p_ptr->px + rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ c_ptr = &cave[y][x];
+
+ /* Notice embedded gold */
+ if ((c_ptr->feat == FEAT_MAGMA_H) ||
+ (c_ptr->feat == FEAT_QUARTZ_H))
+ {
+ /* Expose the gold */
+ cave_set_feat(y, x, c_ptr->feat + 0x02);
+ }
+ else if (c_ptr->feat == FEAT_SANDWALL_H)
+ {
+ /* Expose the gold */
+ cave_set_feat(y, x, FEAT_SANDWALL_K);
+ }
+
+ /* Magma/Quartz + Known Gold */
+ if ((c_ptr->feat == FEAT_MAGMA_K) ||
+ (c_ptr->feat == FEAT_QUARTZ_K) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ /* Hack -- Memorize */
+ c_ptr->info |= (CAVE_MARK);
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /* Detect */
+ detect = TRUE;
+ }
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of buried treasure!");
+ }
+
+
+
+ /* Result */
+ return (detect);
+}
+
+
+
+/*
+ * Detect all "gold" objects on the current panel
+ */
+bool_ hack_no_detect_message = FALSE;
+bool_ detect_objects_gold(int rad)
+{
+ int i, y, x;
+
+ bool_ detect = FALSE;
+
+
+ /* Scan objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (!(r_ptr->flags9 & RF9_MIMIC)) continue;
+ else
+ {
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+ }
+ }
+ else
+ {
+ /* Location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+ }
+
+ /* Only detect nearby objects */
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ /* Detect "gold" objects */
+ if (o_ptr->tval == TV_GOLD)
+ {
+ /* Hack -- memorize it */
+ o_ptr->marked = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ detect = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (detect && (!hack_no_detect_message))
+ {
+ msg_print("You sense the presence of treasure!");
+ }
+
+ if (detect_monsters_string("$", rad))
+ {
+ detect = TRUE;
+ }
+
+ /* Result */
+ return (detect);
+}
+
+
+/*
+ * Detect all "normal" objects on the current panel
+ */
+bool_ detect_objects_normal(int rad)
+{
+ int i, y, x;
+
+ bool_ detect = FALSE;
+
+
+ /* Scan objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (!(r_ptr->flags9 & RF9_MIMIC)) continue;
+ else
+ {
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+ }
+ }
+ else
+ {
+ /* Location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+ }
+
+ /* Only detect nearby objects */
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ /* Detect "real" objects */
+ if (o_ptr->tval != TV_GOLD)
+ {
+ /* Hack -- memorize it */
+ o_ptr->marked = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ detect = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (detect && (!hack_no_detect_message))
+ {
+ msg_print("You sense the presence of objects!");
+ }
+
+ if (detect_monsters_string("!=?|", rad))
+ {
+ detect = TRUE;
+ }
+
+ /* Result */
+ return (detect);
+}
+
+
+/*
+ * Detect all "magic" objects on the current panel.
+ *
+ * This will light up all spaces with "magic" items, including artifacts,
+ * ego-items, potions, scrolls, books, rods, wands, staves, amulets, rings,
+ * and "enchanted" items of the "good" variety.
+ *
+ * It can probably be argued that this function is now too powerful.
+ */
+bool_ detect_objects_magic(int rad)
+{
+ int i, y, x, tv;
+
+ bool_ detect = FALSE;
+
+
+ /* Scan all objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (!(r_ptr->flags9 & RF9_MIMIC)) continue;
+ else
+ {
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+ }
+ }
+ else
+ {
+ /* Location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+ }
+
+ /* Only detect nearby objects */
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ /* Examine the tval */
+ tv = o_ptr->tval;
+
+ /* Artifacts, misc magic items, or enchanted wearables */
+ if (artifact_p(o_ptr) || ego_item_p(o_ptr) || o_ptr->art_name ||
+ (tv == TV_AMULET) || (tv == TV_RING) || (tv == TV_BATERIE) ||
+ (tv == TV_STAFF) || (tv == TV_WAND) || (tv == TV_ROD) || (tv == TV_ROD_MAIN) ||
+ (tv == TV_SCROLL) || (tv == TV_POTION) || (tv == TV_POTION2) ||
+ (tv == TV_DAEMON_BOOK) ||
+ (tv == TV_SYMBIOTIC_BOOK) || (tv == TV_MUSIC_BOOK) ||
+ ((o_ptr->to_a > 0) || (o_ptr->to_h + o_ptr->to_d > 0)))
+ {
+ /* Memorize the item */
+ o_ptr->marked = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ detect = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of magic objects!");
+ }
+
+ /* Return result */
+ return (detect);
+}
+
+
+/*
+ * Detect all "normal" monsters on the current panel
+ */
+bool_ detect_monsters_normal(int rad)
+{
+ int i, y, x;
+
+ bool_ flag = FALSE;
+
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect all non-invisible monsters */
+ if ((!(r_ptr->flags2 & (RF2_INVISIBLE))) ||
+ p_ptr->see_inv || p_ptr->tim_invis)
+ {
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of monsters!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+/*
+ * Detect all "invisible" monsters on current panel
+ */
+bool_ detect_monsters_invis(int rad)
+{
+ int i, y, x;
+ bool_ flag = FALSE;
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect invisible monsters */
+ if (r_ptr->flags2 & (RF2_INVISIBLE))
+ {
+ /* Take note that they are invisible */
+ r_ptr->r_flags2 |= (RF2_INVISIBLE);
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of invisible creatures!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+
+/*
+ * Detect all "evil" monsters on current panel
+ */
+bool_ detect_monsters_evil(int rad)
+{
+ int i, y, x;
+ bool_ flag = FALSE;
+
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect evil monsters */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ /* Take note that they are evil */
+ r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of evil creatures!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+
+
+/*
+ * Detect all (string) monsters on current panel
+ */
+bool_ detect_monsters_string(cptr chars, int rad)
+{
+ int i, y, x;
+ bool_ flag = FALSE;
+
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect evil monsters */
+ if (strchr(chars, r_ptr->d_char))
+ {
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of monsters!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+/*
+ * A "generic" detect monsters routine, tagged to flags3
+ */
+bool_ detect_monsters_xxx(u32b match_flag, int rad)
+{
+ int i, y, x;
+ bool_ flag = FALSE;
+ cptr desc_monsters = "weird monsters";
+
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect evil monsters */
+ if (r_ptr->flags3 & (match_flag))
+ {
+ /* Take note that they are something */
+ r_ptr->r_flags3 |= (match_flag);
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ switch (match_flag)
+ {
+ case RF3_DEMON:
+ desc_monsters = "demons";
+ break;
+ case RF3_UNDEAD:
+ desc_monsters = "the undead";
+ break;
+ case RF3_GOOD:
+ desc_monsters = "good";
+ break;
+ }
+
+ /* Describe result */
+ msg_format("You sense the presence of %s!", desc_monsters);
+ msg_print(NULL);
+ }
+
+ /* Result */
+ return (flag);
+}
+
+/* Detect good monsters */
+bool_ detect_monsters_good(int rad)
+{
+ return (detect_monsters_xxx(RF3_GOOD, rad));
+}
+
+
+/*
+ * Detect everything
+ */
+bool_ detect_all(int rad)
+{
+ bool_ detect = FALSE;
+
+ /* Detect everything */
+ if (detect_traps(rad)) detect = TRUE;
+ if (detect_doors(rad)) detect = TRUE;
+ if (detect_stairs(rad)) detect = TRUE;
+ if (detect_treasure(rad)) detect = TRUE;
+ if (detect_objects_gold(rad)) detect = TRUE;
+ if (detect_objects_normal(rad)) detect = TRUE;
+ if (detect_monsters_invis(rad)) detect = TRUE;
+ if (detect_monsters_normal(rad)) detect = TRUE;
+
+ /* Result */
+ return (detect);
+}
+
+
+
+/*
+ * Create stairs at the player location
+ */
+void stair_creation(void)
+{
+ /* XXX XXX XXX */
+ if (!cave_valid_bold(p_ptr->py, p_ptr->px))
+ {
+ msg_print("The object resists the spell.");
+ return;
+ }
+
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ msg_print("No stair creation in non dungeons...");
+ return;
+ }
+
+ if (dungeon_flags2 & DF2_SPECIAL)
+ {
+ msg_print("No stair creation on special levels...");
+ return;
+ }
+
+ /* XXX XXX XXX */
+ delete_object(p_ptr->py, p_ptr->px);
+
+ /* Create a staircase */
+ if (p_ptr->inside_arena || p_ptr->inside_quest)
+ {
+ /* arena or quest */
+ msg_print("There is no effect!");
+ }
+ else if (!dun_level)
+ {
+ /* Town/wilderness */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MORE);
+ }
+ else if (is_quest(dun_level) || (dun_level >= MAX_DEPTH - 1))
+ {
+ /* Quest level */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ }
+ else if (rand_int(100) < 50)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MORE);
+ }
+ else
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ }
+}
+
+
+
+
+/*
+ * Hook to specify "weapon"
+ */
+static bool_ item_tester_hook_weapon(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_MSTAFF:
+ case TV_BOOMERANG:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_BOW:
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_SHOT:
+ {
+ return (TRUE);
+ }
+ case TV_DAEMON_BOOK:
+ {
+ switch (o_ptr->sval)
+ {
+ case SV_DEMONBLADE:
+ {
+ return (TRUE);
+ }
+ }
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Hook to specify "armour"
+ */
+bool_ item_tester_hook_armour(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_DRAG_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_SOFT_ARMOR:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ {
+ return (TRUE);
+ }
+ case TV_DAEMON_BOOK:
+ {
+ switch (o_ptr->sval)
+ {
+ case SV_DEMONHORN:
+ case SV_DEMONSHIELD:
+ {
+ return (TRUE);
+ }
+ }
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Check if an object is weapon or armour (but not arrow, bolt, or shot)
+ */
+bool_ item_tester_hook_weapon_armour(object_type *o_ptr)
+{
+ return (item_tester_hook_weapon(o_ptr) ||
+ item_tester_hook_armour(o_ptr));
+}
+
+/*
+ * Check if an object is artifactable
+ */
+bool_ item_tester_hook_artifactable(object_type *o_ptr)
+{
+ return ((item_tester_hook_weapon(o_ptr) ||
+ item_tester_hook_armour(o_ptr) ||
+ (o_ptr->tval == TV_DIGGING) ||
+ (o_ptr->tval == TV_RING) || (o_ptr->tval == TV_AMULET))
+ /* be nice: allow only normal items */
+ && (!artifact_p(o_ptr)) && (!ego_item_p(o_ptr)));
+}
+
+
+/*
+ * Enchants a plus onto an item. -RAK-
+ *
+ * Revamped! Now takes item pointer, number of times to try enchanting,
+ * and a flag of what to try enchanting. Artifacts resist enchantment
+ * some of the time, and successful enchantment to at least +0 might
+ * break a curse on the item. -CFT-
+ *
+ * Note that an item can technically be enchanted all the way to +15 if
+ * you wait a very, very, long time. Going from +9 to +10 only works
+ * about 5% of the time, and from +10 to +11 only about 1% of the time.
+ *
+ * Note that this function can now be used on "piles" of items, and
+ * the larger the pile, the lower the chance of success.
+ */
+bool_ enchant(object_type *o_ptr, int n, int eflag)
+{
+ int i, chance, prob;
+ bool_ res = FALSE;
+ bool_ a = (artifact_p(o_ptr) || o_ptr->art_name);
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Large piles resist enchantment */
+ prob = o_ptr->number * 100;
+
+ /* Missiles are easy to enchant */
+ if ((o_ptr->tval == TV_BOLT) ||
+ (o_ptr->tval == TV_ARROW) ||
+ (o_ptr->tval == TV_SHOT))
+ {
+ prob = prob / 20;
+ }
+
+ /* Try "n" times */
+ for (i = 0; i < n; i++)
+ {
+ /* Hack -- Roll for pile resistance */
+ if (rand_int(prob) >= 100) continue;
+
+ /* Enchant to hit */
+ if (eflag & (ENCH_TOHIT))
+ {
+ if (o_ptr->to_h < 0) chance = 0;
+ else if (o_ptr->to_h > 15) chance = 1000;
+ else chance = enchant_table[o_ptr->to_h];
+
+ if ((randint(1000) > chance) && (!a || (rand_int(100) < 50)))
+ {
+ o_ptr->to_h++;
+ res = TRUE;
+
+ /* only when you get it above -1 -CFT */
+ if (cursed_p(o_ptr) &&
+ (!(f3 & (TR3_PERMA_CURSE))) &&
+ (o_ptr->to_h >= 0) && (rand_int(100) < 25))
+ {
+ msg_print("The curse is broken!");
+ o_ptr->ident &= ~(IDENT_CURSED);
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ o_ptr->sense = SENSE_UNCURSED;
+ }
+ }
+ }
+
+ /* Enchant to damage */
+ if (eflag & (ENCH_TODAM))
+ {
+ if (o_ptr->to_d < 0) chance = 0;
+ else if (o_ptr->to_d > 15) chance = 1000;
+ else chance = enchant_table[o_ptr->to_d];
+
+ if ((randint(1000) > chance) && (!a || (rand_int(100) < 50)))
+ {
+ o_ptr->to_d++;
+ res = TRUE;
+
+ /* only when you get it above -1 -CFT */
+ if (cursed_p(o_ptr) &&
+ (!(f3 & (TR3_PERMA_CURSE))) &&
+ (o_ptr->to_d >= 0) && (rand_int(100) < 25))
+ {
+ msg_print("The curse is broken!");
+ o_ptr->ident &= ~(IDENT_CURSED);
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ o_ptr->sense = SENSE_UNCURSED;
+ }
+ }
+ }
+
+
+ /* Enchant to damage */
+ if (eflag & (ENCH_PVAL))
+ {
+ if (o_ptr->pval < 0) chance = 0;
+ else if (o_ptr->pval > 6) chance = 1000;
+ else chance = enchant_table[o_ptr->pval * 2];
+
+ if ((randint(1000) > chance) && (!a || (rand_int(100) < 50)))
+ {
+ o_ptr->pval++;
+ res = TRUE;
+
+ /* only when you get it above -1 -CFT */
+ if (cursed_p(o_ptr) &&
+ (!(f3 & (TR3_PERMA_CURSE))) &&
+ (o_ptr->pval >= 0) && (rand_int(100) < 25))
+ {
+ msg_print("The curse is broken!");
+ o_ptr->ident &= ~(IDENT_CURSED);
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ o_ptr->sense = SENSE_UNCURSED;
+ }
+ }
+ }
+
+ /* Enchant to armor class */
+ if (eflag & (ENCH_TOAC))
+ {
+ if (o_ptr->to_a < 0) chance = 0;
+ else if (o_ptr->to_a > 15) chance = 1000;
+ else chance = enchant_table[o_ptr->to_a];
+
+ if ((randint(1000) > chance) && (!a || (rand_int(100) < 50)))
+ {
+ o_ptr->to_a++;
+ res = TRUE;
+
+ /* only when you get it above -1 -CFT */
+ if (cursed_p(o_ptr) &&
+ (!(f3 & (TR3_PERMA_CURSE))) &&
+ (o_ptr->to_a >= 0) && (rand_int(100) < 25))
+ {
+ msg_print("The curse is broken!");
+ o_ptr->ident &= ~(IDENT_CURSED);
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ o_ptr->sense = SENSE_UNCURSED;
+ }
+ }
+ }
+ }
+
+ /* Failure */
+ if (!res) return (FALSE);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Enchant an item (in the inventory or on the floor)
+ * Note that "num_ac" requires armour, else weapon
+ * Returns TRUE if attempted, FALSE if cancelled
+ */
+bool_ enchant_spell(int num_hit, int num_dam, int num_ac, int num_pval)
+{
+ int item;
+ bool_ okay = FALSE;
+ object_type *o_ptr;
+ char o_name[80];
+ cptr q, s;
+
+
+ /* Assume enchant weapon */
+ item_tester_hook = item_tester_hook_weapon;
+
+ /* Enchant armor if requested */
+ if (num_ac) item_tester_hook = item_tester_hook_armour;
+
+ /* Get an item */
+ q = "Enchant which item? ";
+ s = "You have nothing to enchant.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Describe */
+ msg_format("%s %s glow%s brightly!",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+
+ /* Enchant */
+ if (enchant(o_ptr, num_hit, ENCH_TOHIT)) okay = TRUE;
+ if (enchant(o_ptr, num_dam, ENCH_TODAM)) okay = TRUE;
+ if (enchant(o_ptr, num_ac, ENCH_TOAC)) okay = TRUE;
+ if (enchant(o_ptr, num_pval, ENCH_PVAL)) okay = TRUE;
+
+ /* Failure */
+ if (!okay)
+ {
+ /* Flush */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("The enchantment failed.");
+ }
+
+ /* Something happened */
+ return (TRUE);
+}
+
+void curse_artifact(object_type * o_ptr)
+{
+ if (o_ptr->pval) o_ptr->pval = 0 - ((o_ptr->pval) + randint(4));
+ if (o_ptr->to_a) o_ptr->to_a = 0 - ((o_ptr->to_a) + randint(4));
+ if (o_ptr->to_h) o_ptr->to_h = 0 - ((o_ptr->to_h) + randint(4));
+ if (o_ptr->to_d) o_ptr->to_d = 0 - ((o_ptr->to_d) + randint(4));
+ o_ptr->art_flags3 |= ( TR3_HEAVY_CURSE | TR3_CURSED );
+ if (randint(3) == 1) o_ptr-> art_flags3 |= TR3_TY_CURSE;
+ if (randint(2) == 1) o_ptr-> art_flags3 |= TR3_AGGRAVATE;
+ if (randint(3) == 1) o_ptr-> art_flags3 |= TR3_DRAIN_EXP;
+ if (randint(3) == 1) o_ptr-> art_flags4 |= TR4_BLACK_BREATH;
+ if (randint(2) == 1) o_ptr-> art_flags3 |= TR3_TELEPORT;
+ else if (randint(3) == 1) o_ptr->art_flags3 |= TR3_NO_TELE;
+ o_ptr->ident |= IDENT_CURSED;
+}
+
+
+/*
+ * Should be merged with randart code.
+ * looks like BASIC coder's work...
+ */
+void random_plus(object_type * o_ptr, bool_ is_scroll)
+{
+ int this_type = (o_ptr->tval < TV_BOOTS ? 23 : 19);
+
+ if (artifact_bias == BIAS_WARRIOR)
+ {
+ if (!(o_ptr->art_flags1 & TR1_STR))
+ {
+ o_ptr->art_flags1 |= TR1_STR;
+ if (randint(2) == 1) return ; /* 50% chance of being a "free" power */
+ }
+
+ if (!(o_ptr->art_flags1 & TR1_CON))
+ {
+ o_ptr->art_flags1 |= TR1_CON;
+ if (randint(2) == 1) return;
+ }
+
+ if (!(o_ptr->art_flags1 & TR1_DEX))
+ {
+ o_ptr->art_flags1 |= TR1_DEX;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_MAGE)
+ {
+ if (!(o_ptr->art_flags1 & TR1_INT))
+ {
+ o_ptr->art_flags1 |= TR1_INT;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_PRIESTLY)
+ {
+ if (!(o_ptr->art_flags1 & TR1_WIS))
+ {
+ o_ptr->art_flags1 |= TR1_WIS;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_RANGER)
+ {
+ if (!(o_ptr->art_flags1 & TR1_CON))
+ {
+ o_ptr->art_flags1 |= TR1_CON;
+ if (randint(2) == 1) return ; /* 50% chance of being a "free" power */
+ }
+
+ if (!(o_ptr->art_flags1 & TR1_DEX))
+ {
+ o_ptr->art_flags1 |= TR1_DEX;
+ if (randint(2) == 1) return;
+ }
+
+ if (!(o_ptr->art_flags1 & TR1_STR))
+ {
+ o_ptr->art_flags1 |= TR1_STR;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_ROGUE)
+ {
+ if (!(o_ptr->art_flags1 & TR1_STEALTH))
+ {
+ o_ptr->art_flags1 |= TR1_STEALTH;
+ if (randint(2) == 1) return;
+ }
+ if (!(o_ptr->art_flags1 & TR1_SEARCH))
+ {
+ o_ptr->art_flags1 |= TR1_SEARCH;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_STR)
+ {
+ if (!(o_ptr->art_flags1 & TR1_STR))
+ {
+ o_ptr->art_flags1 |= TR1_STR;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_WIS)
+ {
+ if (!(o_ptr->art_flags1 & TR1_WIS))
+ {
+ o_ptr->art_flags1 |= TR1_WIS;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_INT)
+ {
+ if (!(o_ptr->art_flags1 & TR1_INT))
+ {
+ o_ptr->art_flags1 |= TR1_INT;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_DEX)
+ {
+ if (!(o_ptr->art_flags1 & TR1_DEX))
+ {
+ o_ptr->art_flags1 |= TR1_DEX;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CON)
+ {
+ if (!(o_ptr->art_flags1 & TR1_CON))
+ {
+ o_ptr->art_flags1 |= TR1_CON;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CHR)
+ {
+ if (!(o_ptr->art_flags1 & TR1_CHR))
+ {
+ o_ptr->art_flags1 |= TR1_CHR;
+ if (randint(2) == 1) return;
+ }
+ }
+
+
+ switch (randint(this_type))
+ {
+ case 1:
+ case 2:
+ o_ptr->art_flags1 |= TR1_STR;
+ /* if (is_scroll) msg_print("It makes you feel strong!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_STR;
+ else if (!(artifact_bias) && randint(7) == 1)
+ artifact_bias = BIAS_WARRIOR;
+ break;
+ case 3:
+ case 4:
+ o_ptr->art_flags1 |= TR1_INT;
+ /* if (is_scroll) msg_print("It makes you feel smart!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_INT;
+ else if (!(artifact_bias) && randint(7) == 1)
+ artifact_bias = BIAS_MAGE;
+ break;
+ case 5:
+ case 6:
+ o_ptr->art_flags1 |= TR1_WIS;
+ /* if (is_scroll) msg_print("It makes you feel wise!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_WIS;
+ else if (!(artifact_bias) && randint(7) == 1)
+ artifact_bias = BIAS_PRIESTLY;
+ break;
+ case 7:
+ case 8:
+ o_ptr->art_flags1 |= TR1_DEX;
+ /* if (is_scroll) msg_print("It makes you feel nimble!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_DEX;
+ else if (!(artifact_bias) && randint(7) == 1)
+ artifact_bias = BIAS_ROGUE;
+ break;
+ case 9:
+ case 10:
+ o_ptr->art_flags1 |= TR1_CON;
+ /* if (is_scroll) msg_print("It makes you feel healthy!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_CON;
+ else if (!(artifact_bias) && randint(9) == 1)
+ artifact_bias = BIAS_RANGER;
+ break;
+ case 11:
+ case 12:
+ o_ptr->art_flags1 |= TR1_CHR;
+ /* if (is_scroll) msg_print("It makes you look great!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_CHR;
+ break;
+ case 13:
+ case 14:
+ o_ptr->art_flags1 |= TR1_STEALTH;
+ /* if (is_scroll) msg_print("It looks muffled."); */
+ if (!(artifact_bias) && randint(3) == 1)
+ artifact_bias = BIAS_ROGUE;
+ break;
+ case 15:
+ case 16:
+ o_ptr->art_flags1 |= TR1_SEARCH;
+ /* if (is_scroll) msg_print("It makes you see better."); */
+ if (!(artifact_bias) && randint(9) == 1)
+ artifact_bias = BIAS_RANGER;
+ break;
+ case 17:
+ case 18:
+ o_ptr->art_flags1 |= TR1_INFRA;
+ /* if (is_scroll) msg_print("It makes you see tiny red animals.");*/
+ break;
+ case 19:
+ o_ptr->art_flags1 |= TR1_SPEED;
+ /* if (is_scroll) msg_print("It makes you move faster!"); */
+ if (!(artifact_bias) && randint(11) == 1)
+ artifact_bias = BIAS_ROGUE;
+ break;
+ case 20:
+ case 21:
+ o_ptr->art_flags1 |= TR1_TUNNEL;
+ /* if (is_scroll) msg_print("Gravel flies from it!"); */
+ break;
+ case 22:
+ case 23:
+ if (o_ptr->tval == TV_BOW) random_plus(o_ptr, is_scroll);
+ else
+ {
+ o_ptr->art_flags1 |= TR1_BLOWS;
+ /* if (is_scroll) msg_print("It seems faster!"); */
+ if (!(artifact_bias) && randint(11) == 1)
+ artifact_bias = BIAS_WARRIOR;
+ }
+ break;
+ }
+}
+
+
+void random_resistance (object_type * o_ptr, bool_ is_scroll, int specific)
+{
+ /* To avoid a number of possible bugs */
+ if (!specific)
+ {
+ if (artifact_bias == BIAS_ACID)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_ACID))
+ {
+ o_ptr->art_flags2 |= TR2_RES_ACID;
+ if (rand_int(2) == 0) return;
+ }
+ if (rand_int(BIAS_LUCK) == 0 && !(o_ptr->art_flags2 & TR2_IM_ACID))
+ {
+ o_ptr->art_flags2 |= TR2_IM_ACID;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_ELEC)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_ELEC))
+ {
+ o_ptr->art_flags2 |= TR2_RES_ELEC;
+ if (rand_int(2) == 0) return;
+ }
+ if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR &&
+ !(o_ptr->art_flags3 & TR3_SH_ELEC))
+ {
+ o_ptr->art_flags2 |= TR3_SH_ELEC;
+ if (rand_int(2) == 0) return;
+ }
+ if (rand_int(BIAS_LUCK) == 0 && !(o_ptr->art_flags2 & TR2_IM_ELEC))
+ {
+ o_ptr->art_flags2 |= TR2_IM_ELEC;
+ if (rand_int(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_FIRE)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_FIRE))
+ {
+ o_ptr->art_flags2 |= TR2_RES_FIRE;
+ if (rand_int(2) == 0) return;
+ }
+ if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR &&
+ !(o_ptr->art_flags3 & TR3_SH_FIRE))
+ {
+ o_ptr->art_flags2 |= TR3_SH_FIRE;
+ if (rand_int(2) == 0) return;
+ }
+ if (rand_int(BIAS_LUCK) == 0 && !(o_ptr->art_flags2 & TR2_IM_FIRE))
+ {
+ o_ptr->art_flags2 |= TR2_IM_FIRE;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_COLD)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_COLD))
+ {
+ o_ptr->art_flags2 |= TR2_RES_COLD;
+ if (rand_int(2) == 0) return;
+ }
+ if (rand_int(BIAS_LUCK) == 0 && !(o_ptr->art_flags2 & TR2_IM_COLD))
+ {
+ o_ptr->art_flags2 |= TR2_IM_COLD;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_POIS)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_POIS))
+ {
+ o_ptr->art_flags2 |= TR2_RES_POIS;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_WARRIOR)
+ {
+ if (rand_int(3) && (!(o_ptr->art_flags2 & TR2_RES_FEAR)))
+ {
+ o_ptr->art_flags2 |= TR2_RES_FEAR;
+ if (rand_int(2) == 0) return;
+ }
+ if ((rand_int(3) == 0) && (!(o_ptr->art_flags3 & TR3_NO_MAGIC)))
+ {
+ o_ptr->art_flags3 |= TR3_NO_MAGIC;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_NECROMANTIC)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_NETHER))
+ {
+ o_ptr->art_flags2 |= TR2_RES_NETHER;
+ if (rand_int(2) == 0) return;
+ }
+ if (!(o_ptr->art_flags2 & TR2_RES_POIS))
+ {
+ o_ptr->art_flags2 |= TR2_RES_POIS;
+ if (rand_int(2) == 0) return;
+ }
+ if (!(o_ptr->art_flags2 & TR2_RES_DARK))
+ {
+ o_ptr->art_flags2 |= TR2_RES_DARK;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CHAOS)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_CHAOS))
+ {
+ o_ptr->art_flags2 |= TR2_RES_CHAOS;
+ if (rand_int(2) == 0) return;
+ }
+ if (!(o_ptr->art_flags2 & TR2_RES_CONF))
+ {
+ o_ptr->art_flags2 |= TR2_RES_CONF;
+ if (rand_int(2) == 0) return;
+ }
+ if (!(o_ptr->art_flags2 & TR2_RES_DISEN))
+ {
+ o_ptr->art_flags2 |= TR2_RES_DISEN;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ }
+
+ switch (specific ? specific : randint(41))
+ {
+ case 1 :
+ if (randint(WEIRD_LUCK) != 1)
+ random_resistance(o_ptr, is_scroll, specific);
+ else
+ {
+ o_ptr->art_flags2 |= TR2_IM_ACID;
+ /* if (is_scroll) msg_print("It looks totally incorruptible."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ACID;
+ }
+ break;
+ case 2:
+ if (randint(WEIRD_LUCK) != 1)
+ random_resistance(o_ptr, is_scroll, specific);
+ else
+ {
+ o_ptr->art_flags2 |= TR2_IM_ELEC;
+ /* if (is_scroll) msg_print("It looks completely grounded."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ELEC;
+ }
+ break;
+ case 3:
+ if (randint(WEIRD_LUCK) != 1)
+ random_resistance(o_ptr, is_scroll, specific);
+ else
+ {
+ o_ptr->art_flags2 |= TR2_IM_COLD;
+ /* if (is_scroll) msg_print("It feels very warm."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_COLD;
+ }
+ break;
+ case 4:
+ if (randint(WEIRD_LUCK) != 1)
+ random_resistance(o_ptr, is_scroll, specific);
+ else
+ {
+ o_ptr->art_flags2 |= TR2_IM_FIRE;
+ /* if (is_scroll) msg_print("It feels very cool."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_FIRE;
+ }
+ break;
+ case 5:
+ case 6:
+ case 13:
+ o_ptr->art_flags2 |= TR2_RES_ACID;
+ /* if (is_scroll) msg_print("It makes your stomach rumble."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ACID;
+ break;
+ case 7:
+ case 8:
+ case 14:
+ o_ptr->art_flags2 |= TR2_RES_ELEC;
+ /* if (is_scroll) msg_print("It makes you feel grounded."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ELEC;
+ break;
+ case 9:
+ case 10:
+ case 15:
+ o_ptr->art_flags2 |= TR2_RES_FIRE;
+ /* if (is_scroll) msg_print("It makes you feel cool!");*/
+ if (!(artifact_bias))
+ artifact_bias = BIAS_FIRE;
+ break;
+ case 11:
+ case 12:
+ case 16:
+ o_ptr->art_flags2 |= TR2_RES_COLD;
+ /* if (is_scroll) msg_print("It makes you feel full of hot air!");*/
+ if (!(artifact_bias))
+ artifact_bias = BIAS_COLD;
+ break;
+ case 17:
+ case 18:
+ o_ptr->art_flags2 |= TR2_RES_POIS;
+ /* if (is_scroll) msg_print("It makes breathing easier for you."); */
+ if (!(artifact_bias) && randint(4) != 1)
+ artifact_bias = BIAS_POIS;
+ else if (!(artifact_bias) && randint(2) == 1)
+ artifact_bias = BIAS_NECROMANTIC;
+ else if (!(artifact_bias) && randint(2) == 1)
+ artifact_bias = BIAS_ROGUE;
+ break;
+ case 19:
+ case 20:
+ o_ptr->art_flags2 |= TR2_RES_FEAR;
+ /* if (is_scroll) msg_print("It makes you feel brave!"); */
+ if (!(artifact_bias) && randint(3) == 1)
+ artifact_bias = BIAS_WARRIOR;
+ break;
+ case 21:
+ o_ptr->art_flags2 |= TR2_RES_LITE;
+ /* if (is_scroll) msg_print("It makes everything look darker.");*/
+ break;
+ case 22:
+ o_ptr->art_flags2 |= TR2_RES_DARK;
+ /* if (is_scroll) msg_print("It makes everything look brigher.");*/
+ break;
+ case 23:
+ case 24:
+ o_ptr->art_flags2 |= TR2_RES_BLIND;
+ /* if (is_scroll) msg_print("It makes you feel you are wearing glasses.");*/
+ break;
+ case 25:
+ case 26:
+ o_ptr->art_flags2 |= TR2_RES_CONF;
+ /* if (is_scroll) msg_print("It makes you feel very determined.");*/
+ if (!(artifact_bias) && randint(6) == 1)
+ artifact_bias = BIAS_CHAOS;
+ break;
+ case 27:
+ case 28:
+ o_ptr->art_flags2 |= TR2_RES_SOUND;
+ /* if (is_scroll) msg_print("It makes you feel deaf!");*/
+ break;
+ case 29:
+ case 30:
+ o_ptr->art_flags2 |= TR2_RES_SHARDS;
+ /* if (is_scroll) msg_print("It makes your skin feel thicker.");*/
+ break;
+ case 31:
+ case 32:
+ o_ptr->art_flags2 |= TR2_RES_NETHER;
+ /* if (is_scroll) msg_print("It makes you feel like visiting a graveyard!");*/
+ if (!(artifact_bias) && randint(3) == 1)
+ artifact_bias = BIAS_NECROMANTIC;
+ break;
+ case 33:
+ case 34:
+ o_ptr->art_flags2 |= TR2_RES_NEXUS;
+ /* if (is_scroll) msg_print("It makes you feel normal.");*/
+ break;
+ case 35:
+ case 36:
+ o_ptr->art_flags2 |= TR2_RES_CHAOS;
+ /* if (is_scroll) msg_print("It makes you feel very firm.");*/
+ if (!(artifact_bias) && randint(2) == 1)
+ artifact_bias = BIAS_CHAOS;
+ break;
+ case 37:
+ case 38:
+ o_ptr->art_flags2 |= TR2_RES_DISEN;
+ /* if (is_scroll) msg_print("It is surrounded by a static feeling.");*/
+ break;
+ case 39:
+ if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR)
+ o_ptr->art_flags3 |= TR3_SH_ELEC;
+ else
+ random_resistance(o_ptr, is_scroll, specific);
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ELEC;
+ break;
+ case 40:
+ if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR)
+ o_ptr->art_flags3 |= TR3_SH_FIRE;
+ else
+ random_resistance(o_ptr, is_scroll, specific);
+ if (!(artifact_bias))
+ artifact_bias = BIAS_FIRE;
+ break;
+ case 41:
+ if (o_ptr->tval == TV_SHIELD || o_ptr->tval == TV_CLOAK ||
+ o_ptr->tval == TV_HELM || o_ptr->tval == TV_HARD_ARMOR)
+ o_ptr->art_flags2 |= TR2_REFLECT;
+ else
+ random_resistance(o_ptr, is_scroll, specific);
+ break;
+ }
+}
+
+void random_misc(object_type * o_ptr, bool_ is_scroll)
+{
+
+ if (artifact_bias == BIAS_RANGER)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_CON))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_STR)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_STR))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_WIS)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_WIS))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_INT)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_INT))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_DEX)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_DEX))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CON)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_CON))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CHR)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_CHR))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CHAOS)
+ {
+ if (!(o_ptr->art_flags3 & TR3_TELEPORT))
+ {
+ o_ptr->art_flags3 |= TR3_TELEPORT;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_FIRE)
+ {
+ if (!(o_ptr->art_flags3 & TR3_LITE1))
+ {
+ o_ptr->art_flags3 |= TR3_LITE1; /* Freebie */
+ }
+ }
+
+
+ switch (randint(31))
+ {
+ case 1:
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become weaker."); */
+ if (!artifact_bias)
+ artifact_bias = BIAS_STR;
+ break;
+
+ case 2:
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become more stupid.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_INT;
+ break;
+
+ case 3:
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become simpler.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_WIS;
+ break;
+
+ case 4:
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become clumsier.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_DEX;
+ break;
+
+ case 5:
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become less healthy.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_CON;
+ break;
+
+ case 6:
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become uglier.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_CHR;
+ break;
+
+ case 7:
+ case 8:
+ case 14:
+ o_ptr->art_flags2 |= TR2_FREE_ACT;
+ /* if (is_scroll) msg_print("It makes you feel like a young rebel!");*/
+ break;
+
+ case 9:
+ o_ptr->art_flags2 |= TR2_HOLD_LIFE;
+ /* if (is_scroll) msg_print("It makes you feel immortal.");*/
+ if (!artifact_bias && (randint(5) == 1))
+ artifact_bias = BIAS_PRIESTLY;
+ else if (!artifact_bias && (randint(6) == 1))
+ artifact_bias = BIAS_NECROMANTIC;
+ break;
+
+ case 10:
+ case 11:
+ o_ptr->art_flags3 |= TR3_LITE1;
+ /* if (is_scroll) msg_print("It starts shining.");*/
+ break;
+
+ case 12:
+ case 13:
+ o_ptr->art_flags3 |= TR3_FEATHER;
+ /* if (is_scroll) msg_print("It feels lighter.");*/
+ break;
+
+ case 15:
+ case 16:
+ case 17:
+ o_ptr->art_flags3 |= TR3_SEE_INVIS;
+ /* if (is_scroll) msg_print("It makes you see the air!");*/
+ break;
+
+ case 18:
+ o_ptr->art_esp |= 1 << (rand_int(32));
+ /* if (is_scroll) msg_print("It makes you hear voices inside your head!");*/
+ if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_MAGE;
+ break;
+
+ case 19:
+ case 20:
+ o_ptr->art_flags3 |= TR3_SLOW_DIGEST;
+ /* if (is_scroll) msg_print("It makes you feel less hungry.");*/
+ break;
+
+ case 21:
+ case 22:
+ o_ptr->art_flags3 |= TR3_REGEN;
+ /* if (is_scroll) msg_print("It looks as good as new.");*/
+ break;
+
+ case 23:
+ o_ptr->art_flags3 |= TR3_TELEPORT;
+ /* if (is_scroll) msg_print("Its position feels uncertain!");*/
+ break;
+
+ case 24:
+ case 25:
+ case 26:
+ if (o_ptr->tval >= TV_BOOTS) random_misc(o_ptr, is_scroll);
+ else
+ {
+ o_ptr->art_flags3 |= TR3_SHOW_MODS;
+ o_ptr->to_a = 4 + (randint(11));
+ }
+ break;
+
+ case 27:
+ case 28:
+ case 29:
+ o_ptr->art_flags3 |= TR3_SHOW_MODS;
+ o_ptr->to_h += 4 + (randint(11));
+ o_ptr->to_d += 4 + (randint(11));
+ break;
+
+ case 30:
+ o_ptr->art_flags3 |= TR3_NO_MAGIC;
+ break;
+
+ case 31:
+ o_ptr->art_flags3 |= TR3_NO_TELE;
+ break;
+ }
+
+
+}
+
+
+void random_slay (object_type * o_ptr, bool_ is_scroll)
+{
+ if (artifact_bias == BIAS_CHAOS && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_CHAOTIC))
+ {
+ o_ptr->art_flags1 |= TR1_CHAOTIC;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_PRIESTLY &&
+ (o_ptr->tval == TV_SWORD || o_ptr->tval == TV_POLEARM || o_ptr->tval == TV_AXE) &&
+ !(o_ptr->art_flags3 & TR3_BLESSED))
+ {
+ /* A free power for "priestly" random artifacts */
+ o_ptr->art_flags3 |= TR3_BLESSED;
+ }
+
+ else if (artifact_bias == BIAS_NECROMANTIC && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_VAMPIRIC))
+ {
+ o_ptr->art_flags1 |= TR1_VAMPIRIC;
+ if (randint(2) == 1) return;
+ }
+ if (!(o_ptr->art_flags1 & TR1_BRAND_POIS) && (randint(2) == 1))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_RANGER && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_SLAY_ANIMAL))
+ {
+ o_ptr->art_flags1 |= TR1_SLAY_ANIMAL;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_ROGUE && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_POIS))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_POIS && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_POIS))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_FIRE && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_FIRE))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_FIRE;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_COLD && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_COLD))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_COLD;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_ELEC && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_ELEC))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_ELEC;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_ACID && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_ACID))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_ACID;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_LAW && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_SLAY_EVIL))
+ {
+ o_ptr->art_flags1 |= TR1_SLAY_EVIL;
+ if (randint(2) == 1) return;
+ }
+ if (!(o_ptr->art_flags1 & TR1_SLAY_UNDEAD))
+ {
+ o_ptr->art_flags1 |= TR1_SLAY_UNDEAD;
+ if (randint(2) == 1) return;
+ }
+ if (!(o_ptr->art_flags1 & TR1_SLAY_DEMON))
+ {
+ o_ptr->art_flags1 |= TR1_SLAY_DEMON;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ if (!(o_ptr->tval == TV_BOW))
+ {
+ switch (randint(34))
+ {
+ case 1:
+ case 2:
+ o_ptr->art_flags1 |= TR1_SLAY_ANIMAL;
+ /* if (is_scroll) msg_print("You start hating animals.");*/
+ break;
+
+ case 3:
+ case 4:
+ o_ptr->art_flags1 |= TR1_SLAY_EVIL;
+ /* if (is_scroll) msg_print("You hate evil creatures.");*/
+ if (!artifact_bias && (randint(2) == 1))
+ artifact_bias = BIAS_LAW;
+ else if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_PRIESTLY;
+ break;
+
+ case 5:
+ case 6:
+ o_ptr->art_flags1 |= TR1_SLAY_UNDEAD;
+ /* if (is_scroll) msg_print("You hate undead creatures.");*/
+ if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_PRIESTLY;
+ break;
+
+ case 7:
+ case 8:
+ o_ptr->art_flags1 |= TR1_SLAY_DEMON;
+ /* if (is_scroll) msg_print("You hate demons.");*/
+ if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_PRIESTLY;
+ break;
+
+ case 9:
+ case 10:
+ o_ptr->art_flags1 |= TR1_SLAY_ORC;
+ /* if (is_scroll) msg_print("You hate orcs.");*/
+ break;
+
+ case 11:
+ case 12:
+ o_ptr->art_flags1 |= TR1_SLAY_TROLL;
+ /* if (is_scroll) msg_print("You hate trolls.");*/
+ break;
+
+ case 13:
+ case 14:
+ o_ptr->art_flags1 |= TR1_SLAY_GIANT;
+ /* if (is_scroll) msg_print("You hate giants.");*/
+ break;
+
+ case 15:
+ case 16:
+ o_ptr->art_flags1 |= TR1_SLAY_DRAGON;
+ /* if (is_scroll) msg_print("You hate dragons.");*/
+ break;
+
+ case 17:
+ o_ptr->art_flags1 |= TR1_KILL_DRAGON;
+ /* if (is_scroll) msg_print("You feel an intense hatred of dragons.");*/
+ break;
+
+ case 18:
+ case 19:
+ if (o_ptr->tval == TV_SWORD)
+ {
+ o_ptr->art_flags1 |= TR1_VORPAL;
+ /* if (is_scroll) msg_print("It looks extremely sharp!");*/
+ if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_WARRIOR;
+ }
+ else random_slay(o_ptr, is_scroll);
+ break;
+
+ case 20:
+ o_ptr->art_flags1 |= TR1_IMPACT;
+ /* if (is_scroll) msg_print("The ground trembles beneath you.");*/
+ break;
+
+ case 21:
+ case 22:
+ o_ptr->art_flags1 |= TR1_BRAND_FIRE;
+ /* if (is_scroll) msg_print("It feels hot!");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_FIRE;
+ break;
+
+ case 23:
+ case 24:
+ o_ptr->art_flags1 |= TR1_BRAND_COLD;
+ /* if (is_scroll) msg_print("It feels cold!");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_COLD;
+ break;
+
+ case 25:
+ case 26:
+ o_ptr->art_flags1 |= TR1_BRAND_ELEC;
+ /* if (is_scroll) msg_print("Ouch! You get zapped!");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_ELEC;
+ break;
+
+ case 27:
+ case 28:
+ o_ptr->art_flags1 |= TR1_BRAND_ACID;
+ /* if (is_scroll) msg_print("Its smell makes you feel dizzy.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_ACID;
+ break;
+
+ case 29:
+ case 30:
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ /* if (is_scroll) msg_print("It smells rotten.");*/
+ if (!artifact_bias && (randint(3) != 1))
+ artifact_bias = BIAS_POIS;
+ else if (!artifact_bias && randint(6) == 1)
+ artifact_bias = BIAS_NECROMANTIC;
+ else if (!artifact_bias)
+ artifact_bias = BIAS_ROGUE;
+ break;
+
+ case 31:
+ case 32:
+ o_ptr->art_flags1 |= TR1_VAMPIRIC;
+ /* if (is_scroll) msg_print("You think it bit you!");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_NECROMANTIC;
+ break;
+
+ default:
+ o_ptr->art_flags1 |= TR1_CHAOTIC;
+ /* if (is_scroll) msg_print("It looks very confusing.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_CHAOS;
+ break;
+ }
+ }
+ else
+ {
+ switch (randint(6))
+ {
+ case 1:
+ case 2:
+ case 3:
+ o_ptr->art_flags3 |= TR3_XTRA_MIGHT;
+ /* if (is_scroll) msg_print("It looks mightier than before."); */
+ if (!artifact_bias && randint(9) == 1)
+ artifact_bias = BIAS_RANGER;
+ break;
+
+ default:
+ o_ptr->art_flags3 |= TR3_XTRA_SHOTS;
+ /* if (is_scroll) msg_print("It seems faster!"); */
+ if (!artifact_bias && randint(9) == 1)
+ artifact_bias = BIAS_RANGER;
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Determines if an item is not identified
+ */
+static bool_ item_tester_hook_unknown(object_type *o_ptr)
+{
+ return (object_known_p(o_ptr) ? FALSE : TRUE);
+}
+
+
+/*
+ * Identify an object in the inventory (or on the floor)
+ * This routine does *not* automatically combine objects.
+ * Returns TRUE if something was identified, else FALSE.
+ */
+bool_ ident_spell(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ cptr q, s;
+
+ /* Get an item */
+ item_tester_hook = item_tester_hook_unknown;
+ q = "Identify which item? ";
+ s = "You have nothing to identify.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe */
+ if (item >= INVEN_WIELD)
+ {
+ msg_format("%^s: %s (%c).",
+ describe_use(item), o_name, index_to_label(item));
+ }
+ else if (item >= 0)
+ {
+ msg_format("In your pack: %s (%c).",
+ o_name, index_to_label(item));
+ }
+ else
+ {
+ msg_format("On the ground: %s.",
+ o_name);
+ }
+
+ /* If the item was an artifact, and if the auto-note is selected, write a message. */
+ if (take_notes && auto_notes && (artifact_p(o_ptr) || o_ptr->name1))
+ {
+ char note[150];
+ char item_name[80];
+ object_desc(item_name, o_ptr, FALSE, 0);
+
+ /* Build note and write */
+ sprintf(note, "Found The %s", item_name);
+ add_note(note, 'A');
+ }
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", item, "normal");
+
+ /* Something happened */
+ return (TRUE);
+}
+
+/*
+ * Identify all objects in the level
+ */
+bool_ ident_all(void)
+{
+ int i;
+
+ object_type *o_ptr;
+
+ for (i = 1; i < o_max; i++)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[i];
+
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* If the item was an artifact, and if the auto-note is selected, write a message. */
+ if (take_notes && auto_notes && (artifact_p(o_ptr) || o_ptr->name1))
+ {
+ char note[150];
+ char item_name[80];
+ object_desc(item_name, o_ptr, FALSE, 0);
+
+ /* Build note and write */
+ sprintf(note, "Found The %s", item_name);
+ add_note(note, 'A');
+ }
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", -i, "normal");
+ }
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
+
+/*
+ * Determine if an object is not fully identified
+ */
+static bool_ item_tester_hook_no_mental(object_type *o_ptr)
+{
+ return ((o_ptr->ident & (IDENT_MENTAL)) ? FALSE : TRUE);
+}
+
+/*
+ * Fully "identify" an object in the inventory -BEN-
+ * This routine returns TRUE if an item was identified.
+ */
+bool_ identify_fully(void)
+{
+ int item;
+ object_type *o_ptr;
+ char o_name[80];
+
+ cptr q, s;
+
+ /* Get an item */
+ item_tester_hook = item_tester_hook_no_mental;
+ q = "Identify which item? ";
+ s = "You have nothing to identify.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Do the identification */
+ make_item_fully_identified(o_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe */
+ if (item >= INVEN_WIELD)
+ {
+ msg_format("%^s: %s (%c).",
+ describe_use(item), o_name, index_to_label(item));
+ }
+ else if (item >= 0)
+ {
+ msg_format("In your pack: %s (%c).",
+ o_name, index_to_label(item));
+ }
+ else
+ {
+ msg_format("On the ground: %s.",
+ o_name);
+ }
+
+ /* If the item was an artifact, and if the auto-note is selected, write a message. */
+ if (take_notes && auto_notes && (artifact_p(o_ptr) || o_ptr->name1))
+ {
+ char note[150];
+ char item_name[80];
+ object_desc(item_name, o_ptr, FALSE, 0);
+
+ /* Build note and write */
+ sprintf(note, "Found The %s", item_name);
+ add_note(note, 'A');
+ }
+
+ /* Describe it fully */
+ object_out_desc(o_ptr, NULL, FALSE, TRUE);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", item, "full");
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Hook for "get_item()". Determine if something is rechargable.
+ */
+bool_ item_tester_hook_recharge(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Some objects cannot be recharged */
+ if (f4 & TR4_NO_RECHARGE) return (FALSE);
+
+ /* Recharge staffs */
+ if (o_ptr->tval == TV_STAFF) return (TRUE);
+
+ /* Recharge wands */
+ if (o_ptr->tval == TV_WAND) return (TRUE);
+
+ /* Hack -- Recharge rods */
+ if (o_ptr->tval == TV_ROD_MAIN) return (TRUE);
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+/*
+ * Recharge a wand/staff/rod from the pack or on the floor.
+ * This function has been rewritten in Oangband. -LM-
+ *
+ * Mage -- Recharge I --> recharge(90)
+ * Mage -- Recharge II --> recharge(150)
+ * Mage -- Recharge III --> recharge(220)
+ *
+ * Priest or Necromancer -- Recharge --> recharge(140)
+ *
+ * Scroll of recharging --> recharge(130)
+ * Scroll of *recharging* --> recharge(200)
+ *
+ * It is harder to recharge high level, and highly charged wands,
+ * staffs, and rods. The more wands in a stack, the more easily and
+ * strongly they recharge. Staffs, however, each get fewer charges if
+ * stacked.
+ *
+ * XXX XXX XXX Beware of "sliding index errors".
+ */
+bool_ recharge(int power)
+{
+ int recharge_strength, recharge_amount;
+ int item, lev;
+
+ bool_ fail = FALSE;
+ byte fail_type = 1;
+
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+ char o_name[80];
+
+ object_type *o_ptr;
+
+ /* Only accept legal items */
+ item_tester_hook = item_tester_hook_recharge;
+
+ /* Get an item */
+ q = "Recharge which item? ";
+ s = "You have nothing to recharge.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Extract the object "level" */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Recharge a rod */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* Extract a recharge strength by comparing object level to power. */
+ recharge_strength = ((power > lev) ? (power - lev) : 0) / 5;
+
+ /* Paranoia */
+ if (recharge_strength < 0) recharge_strength = 0;
+
+ /* Back-fire */
+ if ((rand_int(recharge_strength) == 0) && (!(f4 & TR4_RECHARGE)))
+ {
+ /* Activate the failure code. */
+ fail = TRUE;
+ }
+
+ /* Recharge */
+ else
+ {
+ /* Recharge amount */
+ recharge_amount = (power * damroll(3, 2));
+
+ /* Recharge by that amount */
+ if (o_ptr->timeout + recharge_amount < o_ptr->pval2)
+ o_ptr->timeout += recharge_amount;
+ else
+ o_ptr->timeout = o_ptr->pval2;
+ }
+ }
+
+
+ /* Recharge wand/staff */
+ else
+ {
+ /* Extract a recharge strength by comparing object level to power.
+ * Divide up a stack of wands' charges to calculate charge penalty.
+ */
+ if ((o_ptr->tval == TV_WAND) && (o_ptr->number > 1))
+ recharge_strength = (100 + power - lev -
+ (8 * o_ptr->pval / o_ptr->number)) / 15;
+
+ /* All staffs, unstacked wands. */
+ else recharge_strength = (100 + power - lev -
+ (8 * o_ptr->pval)) / 15;
+
+
+ /* Back-fire XXX XXX XXX */
+ if (((rand_int(recharge_strength) == 0) && (!(f4 & TR4_RECHARGE))) ||
+ (f4 & TR4_NO_RECHARGE))
+ {
+ /* Activate the failure code. */
+ fail = TRUE;
+ }
+
+ /* If the spell didn't backfire, recharge the wand or staff. */
+ else
+ {
+ /* Recharge based on the standard number of charges. */
+ recharge_amount = randint((power / (lev + 2)) + 1);
+
+ /* Multiple wands in a stack increase recharging somewhat. */
+ if ((o_ptr->tval == TV_WAND) && (o_ptr->number > 1))
+ {
+ recharge_amount +=
+ (randint(recharge_amount * (o_ptr->number - 1))) / 2;
+ if (recharge_amount < 1) recharge_amount = 1;
+ if (recharge_amount > 12) recharge_amount = 12;
+ }
+
+ /* But each staff in a stack gets fewer additional charges,
+ * although always at least one.
+ */
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1))
+ {
+ recharge_amount /= o_ptr->number;
+ if (recharge_amount < 1) recharge_amount = 1;
+ }
+
+ /* Recharge the wand or staff. */
+ o_ptr->pval += recharge_amount;
+
+ if (!(f4 & TR4_RECHARGE))
+ {
+ /* Hack -- we no longer "know" the item */
+ o_ptr->ident &= ~(IDENT_KNOWN);
+ }
+
+ /* Hack -- we no longer think the item is empty */
+ o_ptr->ident &= ~(IDENT_EMPTY);
+ }
+ }
+
+ /* Mark as recharged -- For alchemists */
+ o_ptr->art_flags4 |= TR4_RECHARGED;
+
+ /* Inflict the penalties for failing a recharge. */
+ if (fail)
+ {
+ /* Artifacts are never destroyed. */
+ if (artifact_p(o_ptr))
+ {
+ object_desc(o_name, o_ptr, TRUE, 0);
+ msg_format("The recharging backfires - %s is completely drained!", o_name);
+
+ /* Artifact rods. */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ o_ptr->timeout = 0;
+
+ /* Artifact wands and staffs. */
+ else if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))
+ o_ptr->pval = 0;
+ }
+ else
+ {
+ /* Get the object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /*** Determine Seriousness of Failure ***/
+
+ /* Mages recharge objects more safely. */
+ if (has_ability(AB_PERFECT_CASTING))
+ {
+ /* 10% chance to blow up one rod, otherwise draining. */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (randint(10) == 1) fail_type = 2;
+ else fail_type = 1;
+ }
+ /* 75% chance to blow up one wand, otherwise draining. */
+ else if (o_ptr->tval == TV_WAND)
+ {
+ if (randint(3) != 1) fail_type = 2;
+ else fail_type = 1;
+ }
+ /* 50% chance to blow up one staff, otherwise no effect. */
+ else if (o_ptr->tval == TV_STAFF)
+ {
+ if (randint(2) == 1) fail_type = 2;
+ else fail_type = 0;
+ }
+ }
+
+ /* All other classes get no special favors. */
+ else
+ {
+ /* 33% chance to blow up one rod, otherwise draining. */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (randint(3) == 1) fail_type = 2;
+ else fail_type = 1;
+ }
+ /* 20% chance of the entire stack, else destroy one wand. */
+ else if (o_ptr->tval == TV_WAND)
+ {
+ if (randint(5) == 1) fail_type = 3;
+ else fail_type = 2;
+ }
+ /* Blow up one staff. */
+ else if (o_ptr->tval == TV_STAFF)
+ {
+ fail_type = 2;
+ }
+ }
+
+ /*** Apply draining and destruction. ***/
+
+ /* Drain object or stack of objects. */
+ if (fail_type == 1)
+ {
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ msg_print("The recharge backfires, draining the rod further!");
+ if (o_ptr->timeout < 10000)
+ o_ptr->timeout = 0;
+ }
+ else if (o_ptr->tval == TV_WAND)
+ {
+ msg_format("You save your %s from destruction, but all charges are lost.", o_name);
+ o_ptr->pval = 0;
+ }
+ /* Staffs aren't drained. */
+ }
+
+ /* Destroy an object or one in a stack of objects. */
+ if (fail_type == 2)
+ {
+ if (o_ptr->number > 1)
+ msg_format("Wild magic consumes one of your %s!", o_name);
+ else
+ msg_format("Wild magic consumes your %s!", o_name);
+
+ /* Reduce rod stack maximum timeout, drain wands. */
+ if (o_ptr->tval == TV_WAND) o_ptr->pval = 0;
+
+ /* Reduce and describe */
+ inc_stack_size(item, -1);
+ }
+
+ /* Destroy all memebers of a stack of objects. */
+ if (fail_type == 3)
+ {
+ if (o_ptr->number > 1)
+ msg_format("Wild magic consumes all your %s!", o_name);
+ else
+ msg_format("Wild magic consumes your %s!", o_name);
+
+
+ /* Reduce and describe inventory */
+ inc_stack_size(item, -999);
+ }
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Something was done */
+ return (TRUE);
+}
+
+
+
+/*
+ * Apply a "project()" directly to all viewable monsters
+ *
+ * Note that affected monsters are NOT auto-tracked by this usage.
+ */
+bool_ project_hack(int typ, int dam)
+{
+ int i, x, y;
+ int flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE;
+ bool_ obvious = FALSE;
+
+
+ /* Affect all (nearby) monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Require line of sight */
+ if (!player_has_los_bold(y, x)) continue;
+
+ /* Jump directly to the target monster */
+ if (project(0, 0, y, x, dam, typ, flg)) obvious = TRUE;
+ }
+
+ /* Result */
+ return (obvious);
+}
+
+/*
+ * Apply a "project()" a la meteor shower
+ */
+void project_meteor(int radius, int typ, int dam, u32b flg)
+{
+ cave_type *c_ptr;
+ int x, y, dx, dy, d, count = 0, i;
+ int b = radius + randint(radius);
+ for (i = 0; i < b; i++)
+ {
+ for (count = 0; count < 1000; count++)
+ {
+ x = p_ptr->px - 5 + randint(10);
+ y = p_ptr->py - 5 + randint(10);
+ if ((x < 0) || (x >= cur_wid) ||
+ (y < 0) || (y >= cur_hgt)) continue;
+ dx = (p_ptr->px > x) ? (p_ptr->px - x) : (x - p_ptr->px);
+ dy = (p_ptr->py > y) ? (p_ptr->py - y) : (y - p_ptr->py);
+ /* Approximate distance */
+ d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
+ c_ptr = &cave[y][x];
+ /* Check distance */
+ if ((d <= 5) &&
+ /* Check line of sight */
+ (player_has_los_bold(y, x)) &&
+ /* But don't explode IN a wall, letting the player attack through walls */
+ !(c_ptr->info & CAVE_WALL))
+ break;
+ }
+ if (count >= 1000) break;
+ project(0, 2, y, x, dam, typ, PROJECT_JUMP | flg);
+ }
+}
+
+
+/*
+ * Speed monsters
+ */
+bool_ speed_monsters(void)
+{
+ return (project_hack(GF_OLD_SPEED, p_ptr->lev));
+}
+
+/*
+ * Slow monsters
+ */
+bool_ slow_monsters(void)
+{
+ return (project_hack(GF_OLD_SLOW, p_ptr->lev));
+}
+
+/*
+ * Paralyzation monsters
+ */
+bool_ conf_monsters(void)
+{
+ return (project_hack(GF_OLD_CONF, p_ptr->lev));
+}
+
+/*
+ * Sleep monsters
+ */
+bool_ sleep_monsters(void)
+{
+ return (project_hack(GF_OLD_SLEEP, p_ptr->lev));
+}
+
+/*
+ * Scare monsters
+ */
+bool_ scare_monsters(void)
+{
+ return (project_hack(GF_FEAR, p_ptr->lev));
+}
+
+
+/*
+ * Banish evil monsters
+ */
+bool_ banish_evil(int dist)
+{
+ return (project_hack(GF_AWAY_EVIL, dist));
+}
+
+
+/*
+ * Turn undead
+ */
+bool_ turn_undead(void)
+{
+ return (project_hack(GF_TURN_UNDEAD, p_ptr->lev));
+}
+
+
+/*
+ * Dispel undead monsters
+ */
+bool_ dispel_undead(int dam)
+{
+ return (project_hack(GF_DISP_UNDEAD, dam));
+}
+
+/*
+ * Dispel evil monsters
+ */
+bool_ dispel_evil(int dam)
+{
+ return (project_hack(GF_DISP_EVIL, dam));
+}
+
+/*
+ * Dispel good monsters
+ */
+bool_ dispel_good(int dam)
+{
+ return (project_hack(GF_DISP_GOOD, dam));
+}
+
+/*
+ * Dispel all monsters
+ */
+bool_ dispel_monsters(int dam)
+{
+ return (project_hack(GF_DISP_ALL, dam));
+}
+
+/*
+ * Dispel 'living' monsters
+ */
+bool_ dispel_living(int dam)
+{
+ return (project_hack(GF_DISP_LIVING, dam));
+}
+
+/*
+ * Dispel demons
+ */
+bool_ dispel_demons(int dam)
+{
+ return (project_hack(GF_DISP_DEMON, dam));
+}
+
+
+/*
+ * Wake up all monsters, and speed up "los" monsters.
+ */
+void aggravate_monsters(int who)
+{
+ int i;
+ bool_ sleep = FALSE;
+ bool_ speed = FALSE;
+
+
+ /* Aggravate everyone nearby */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Skip aggravating monster (or player) */
+ if (i == who) continue;
+
+ /* Wake up nearby sleeping monsters */
+ if (m_ptr->cdis < MAX_SIGHT * 2)
+ {
+ /* Wake up */
+ if (m_ptr->csleep)
+ {
+ /* Wake up */
+ m_ptr->csleep = 0;
+ sleep = TRUE;
+ }
+ }
+
+ /* Speed up monsters in line of sight */
+ if (player_has_los_bold(m_ptr->fy, m_ptr->fx))
+ {
+ /* Speed up (instantly) to racial base + 10 */
+ if (m_ptr->mspeed < r_ptr->speed + 10)
+ {
+ /* Speed up */
+ m_ptr->mspeed = r_ptr->speed + 10;
+ speed = TRUE;
+ }
+
+ /* Pets may get angry (50% chance) */
+ if (is_friend(m_ptr))
+ {
+ if (randint(2) == 1)
+ {
+ change_side(m_ptr);
+ }
+ }
+ }
+ }
+
+ /* Messages */
+ if (speed) msg_print("You feel a sudden stirring nearby!");
+ else if (sleep) msg_print("You hear a sudden stirring in the distance!");
+}
+
+/*
+ * Generic genocide race selection
+ */
+bool_ get_genocide_race(cptr msg, char *typ)
+{
+ int i, j;
+ cave_type *c_ptr;
+
+ msg_print(msg);
+ if (!tgt_pt(&i, &j)) return FALSE;
+
+ c_ptr = &cave[j][i];
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ *typ = r_ptr->d_char;
+ return TRUE;
+ }
+
+ msg_print("You must select a monster.");
+ return FALSE;
+}
+
+/*
+ * Inflict dam damage of type typee to all monster of the given race
+ */
+bool_ invoke(int dam, int typee)
+{
+ int i;
+ char typ;
+ bool_ result = FALSE;
+ int msec = delay_factor * delay_factor * delay_factor;
+
+ if (dungeon_flags2 & DF2_NO_GENO) return (FALSE);
+
+ /* Hack -- when you are fated to die, you cant cheat :) */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ msg_print("A mysterious force stops the genocide.");
+ return FALSE;
+ }
+
+ /* Get a monster symbol */
+ if (!get_genocide_race("Target a monster to select the race to invoke on.", &typ)) return FALSE;
+
+ /* Delete the monsters of that "type" */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Hack -- Skip Unique Monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) continue;
+
+ /* Hack -- Skip Quest Monsters */
+ if (m_ptr->mflag & MFLAG_QUEST) continue;
+
+ /* Skip "wrong" monsters */
+ if (r_ptr->d_char != typ) continue;
+
+ project_m(0, 0, m_ptr->fy, m_ptr->fx, dam, typee);
+
+ /* Visual feedback */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+
+ /* Take note */
+ result = TRUE;
+ }
+
+ return (result);
+}
+
+
+/*
+ * Delete all non-unique/non-quest monsters of a given "type" from the level
+ */
+bool_ genocide_aux(bool_ player_cast, char typ)
+{
+ int i;
+ bool_ result = FALSE;
+ int msec = delay_factor * delay_factor * delay_factor;
+ int dam = 0;
+
+ /* Delete the monsters of that "type" */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Hack -- Skip Unique Monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) continue;
+
+ /* Hack -- Skip Quest Monsters */
+ if (m_ptr->mflag & MFLAG_QUEST) continue;
+
+ /* Skip "wrong" monsters */
+ if (r_ptr->d_char != typ) continue;
+
+ /* Oups */
+ if (r_ptr->flags2 & RF2_DEATH_ORB)
+ {
+ int wx, wy;
+ int attempts = 500;
+
+ monster_race_desc(r_name, m_ptr->r_idx, 0);
+
+ do
+ {
+ scatter(&wy, &wx, m_ptr->fy, m_ptr->fx, 10);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (place_monster_aux(wy, wx, m_ptr->r_idx, FALSE, TRUE, MSTATUS_ENEMY))
+ {
+ cmsg_format(TERM_L_BLUE, "The spell seems to produce an ... interesting effect on the %s.", r_name);
+ }
+
+ return TRUE;
+ }
+
+ /* Delete the monster */
+ delete_monster_idx(i);
+
+ if (player_cast)
+ {
+ /* Keep track of damage */
+ dam += randint(4);
+ }
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+
+ /* Take note */
+ result = TRUE;
+ }
+
+ if (player_cast)
+ {
+ /* Take damage */
+ take_hit(dam, "the strain of casting Genocide");
+
+ /* Visual feedback */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+ }
+
+ return (result);
+}
+
+bool_ genocide(bool_ player_cast)
+{
+ char typ;
+
+ if (dungeon_flags2 & DF2_NO_GENO) return (FALSE);
+
+ /* Hack -- when you are fated to die, you cant cheat :) */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ msg_print("A mysterious force stops the genocide.");
+ return FALSE;
+ }
+
+ /* Mega-Hack -- Get a monster symbol */
+ if (!get_genocide_race("Target a monster to select the race to genocide.", &typ)) return FALSE;
+
+ return (genocide_aux(player_cast, typ));
+}
+
+
+/*
+ * Delete all nearby (non-unique) monsters
+ */
+bool_ mass_genocide(bool_ player_cast)
+{
+ int i;
+ bool_ result = FALSE;
+ int msec = delay_factor * delay_factor * delay_factor;
+ int dam = 0;
+
+ if (dungeon_flags2 & DF2_NO_GENO) return (FALSE);
+
+ /* Hack -- when you are fated to die, you cant cheat :) */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ msg_print("A mysterious force stops the genocide.");
+ return FALSE;
+ }
+
+ /* Delete the (nearby) monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Hack -- Skip unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) continue;
+
+ /* Hack -- Skip Quest Monsters */
+ if (m_ptr->mflag & MFLAG_QUEST) continue;
+
+ /* Skip distant monsters */
+ if (m_ptr->cdis > MAX_SIGHT) continue;
+
+ /* Oups */
+ if (r_ptr->flags2 & RF2_DEATH_ORB)
+ {
+ int wx, wy;
+ int attempts = 500;
+
+ monster_race_desc(r_name, m_ptr->r_idx, 0);
+
+ do
+ {
+ scatter(&wy, &wx, m_ptr->fy, m_ptr->fx, 10);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (place_monster_aux(wy, wx, m_ptr->r_idx, FALSE, TRUE, MSTATUS_ENEMY))
+ {
+ cmsg_format(TERM_L_BLUE, "The spell seems to produce an ... interesting effect on the %s.", r_name);
+ }
+
+ return TRUE;
+ }
+
+ /* Delete the monster */
+ delete_monster_idx(i);
+
+ if (player_cast)
+ {
+ /* Keep track of damage. */
+ dam += randint(3);
+ }
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+
+ /* Note effect */
+ result = TRUE;
+ }
+
+ if (player_cast)
+ {
+ /* Take damage */
+ take_hit(dam, "the strain of casting Mass Genocide");
+
+ /* Visual feedback */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+ }
+
+ return (result);
+}
+
+/* Probe a monster */
+void do_probe(int m_idx)
+{
+ char m_name[80];
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Get "the monster" or "something" */
+ monster_desc(m_name, m_ptr, 0x04);
+
+ /* Describe the monster */
+ if (!wizard && (m_ptr->status != MSTATUS_COMPANION)) msg_format("%^s has %d hit points.", m_name, m_ptr->hp);
+ else
+ {
+ int i;
+ char t_name[80];
+ msg_format("%^s has %d(%d) hit points, %d ac, %d speed.", m_name, m_ptr->hp, m_ptr->maxhp, m_ptr->ac, m_ptr->mspeed - 110);
+ msg_format("%^s attacks with:", m_name);
+
+ for (i = 0; i < 4; i++)
+ {
+ msg_format(" Blow %d: %dd%d", i, m_ptr->blow[i].d_dice, m_ptr->blow[i].d_side);
+ }
+
+ if (m_ptr->target > 0)
+ monster_desc(t_name, &m_list[m_ptr->target], 0x04);
+ else if (!m_ptr->target)
+ sprintf(t_name, "you");
+ else
+ sprintf(t_name, "nothing");
+ msg_format("%^s target is %s.", m_name, t_name);
+
+ msg_format("%^s has %ld exp and needs %ld.", m_name, m_ptr->exp, MONSTER_EXP(m_ptr->level + 1));
+ }
+
+ /* Learn all of the non-spell, non-treasure flags */
+ lore_do_probe(m_idx);
+}
+
+/*
+ * Probe nearby monsters
+ */
+bool_ probing(void)
+{
+ int i;
+
+ bool_ probe = FALSE;
+
+
+ /* Probe all (nearby) monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Require line of sight */
+ if (!player_has_los_bold(m_ptr->fy, m_ptr->fx)) continue;
+
+ /* Probe visible monsters */
+ if (m_ptr->ml)
+ {
+ /* Start the message */
+ if (!probe) msg_print("Probing...");
+
+ /* Actualy probe */
+ do_probe(i);
+
+ /* Probe worked */
+ probe = TRUE;
+ }
+ }
+
+ /* Done */
+ if (probe)
+ {
+ msg_print("That's all.");
+ }
+
+ /* Result */
+ return (probe);
+}
+
+
+/*
+ * Wipe -- Empties a part of the dungeon
+ */
+void wipe(int y1, int x1, int r)
+{
+ int y, x, k;
+
+ cave_type *c_ptr;
+
+ bool_ flag = FALSE;
+
+ if (dungeon_flags2 & DF2_NO_GENO)
+ {
+ msg_print("Not on special levels!");
+ return;
+ }
+ if (p_ptr->inside_quest)
+ {
+ return;
+ }
+
+ /* Big area of affect */
+ for (y = (y1 - r); y <= (y1 + r); y++)
+ {
+ for (x = (x1 - r); x <= (x1 + r); x++)
+ {
+ /* Skip illegal grids */
+ if (!in_bounds(y, x)) continue;
+
+ /* Extract the distance */
+ k = distance(y1, x1, y, x);
+
+ /* Stay in the circle of death */
+ if (k > r) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Lose room and vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* Lose light and knowledge */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+
+ if (m_list[c_ptr->m_idx].status != MSTATUS_COMPANION) delete_monster(y, x);
+ delete_object(y, x);
+ place_floor_convert_glass(y, x);
+ }
+ }
+
+
+ /* Hack -- Affect player */
+ if (flag)
+ {
+ /* Message */
+ msg_print("There is a searing blast of light!");
+
+ /* Blind the player */
+ if (!p_ptr->resist_blind && !p_ptr->resist_lite)
+ {
+ /* Become blind */
+ (void)set_blind(p_ptr->blind + 10 + randint(10));
+ }
+ }
+
+
+ /* Mega-Hack -- Forget the view */
+ p_ptr->update |= (PU_UN_VIEW);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+/*
+ * The spell of destruction
+ *
+ * This spell "deletes" monsters (instead of "killing" them).
+ *
+ * Later we may use one function for both "destruction" and
+ * "earthquake" by using the "full" to select "destruction".
+ */
+void destroy_area(int y1, int x1, int r, bool_ full, bool_ bypass)
+{
+ int y, x, k, t;
+
+ cave_type *c_ptr;
+
+ bool_ flag = FALSE;
+
+
+ /* XXX XXX */
+ full = full ? full : 0;
+
+ if (dungeon_flags2 & DF2_NO_GENO)
+ {
+ msg_print("Not on special levels!");
+ return;
+ }
+ if (p_ptr->inside_quest && (!bypass))
+ {
+ return;
+ }
+
+ /* Big area of affect */
+ for (y = (y1 - r); y <= (y1 + r); y++)
+ {
+ for (x = (x1 - r); x <= (x1 + r); x++)
+ {
+ /* Skip illegal grids */
+ if (!in_bounds(y, x)) continue;
+
+ /* Extract the distance */
+ k = distance(y1, x1, y, x);
+
+ /* Stay in the circle of death */
+ if (k > r) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Lose room and vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* Lose light and knowledge */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+
+ /* Hack -- Notice player affect */
+ if ((x == p_ptr->px) && (y == p_ptr->py))
+ {
+ /* Hurt the player later */
+ flag = TRUE;
+
+ /* Do not hurt this grid */
+ continue;
+ }
+
+ /* Hack -- Skip the epicenter */
+ if ((y == y1) && (x == x1)) continue;
+
+ /* Delete the monster (if any) */
+ if ((m_list[c_ptr->m_idx].status != MSTATUS_COMPANION) ||
+ (!(m_list[c_ptr->m_idx].mflag & MFLAG_QUEST)) ||
+ (!(m_list[c_ptr->m_idx].mflag & MFLAG_QUEST2)))
+ delete_monster(y, x);
+
+ /* Destroy "valid" grids */
+ if (cave_valid_bold(y, x))
+ {
+ /* Delete objects */
+ delete_object(y, x);
+
+ /* Wall (or floor) type */
+ t = rand_int(200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 70)
+ {
+ /* Create quartz vein */
+ cave_set_feat(y, x, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 100)
+ {
+ /* Create magma vein */
+ cave_set_feat(y, x, FEAT_MAGMA);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+ }
+ }
+ }
+
+
+ /* Hack -- Affect player */
+ if (flag)
+ {
+ /* Message */
+ msg_print("There is a searing blast of light!");
+
+ /* Blind the player */
+ if (!p_ptr->resist_blind && !p_ptr->resist_lite)
+ {
+ /* Become blind */
+ (void)set_blind(p_ptr->blind + 10 + randint(10));
+ }
+ }
+
+
+ /* Mega-Hack -- Forget the view */
+ p_ptr->update |= (PU_UN_VIEW);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+/*
+ * Induce an "earthquake" of the given radius at the given location.
+ *
+ * This will turn some walls into floors and some floors into walls.
+ *
+ * The player will take damage and "jump" into a safe grid if possible,
+ * otherwise, he will "tunnel" through the rubble instantaneously.
+ *
+ * Monsters will take damage, and "jump" into a safe grid if possible,
+ * otherwise they will be "buried" in the rubble, disappearing from
+ * the level in the same way that they do when genocided.
+ *
+ * Note that thus the player and monsters (except eaters of walls and
+ * passers through walls) will never occupy the same grid as a wall.
+ * Note that as of now (2.7.8) no monster may occupy a "wall" grid, even
+ * for a single turn, unless that monster can pass_walls or kill_walls.
+ * This has allowed massive simplification of the "monster" code.
+ */
+void earthquake(int cy, int cx, int r)
+{
+ int i, t, y, x, yy, xx, dy, dx, oy, ox;
+ int damage = 0;
+ int sn = 0, sy = 0, sx = 0;
+ bool_ hurt = FALSE;
+ cave_type *c_ptr;
+ bool_ map[32][32];
+
+ if (p_ptr->inside_quest)
+ {
+ return;
+ }
+
+ /* Paranoia -- Enforce maximum range */
+ if (r > 12) r = 12;
+
+ /* Clear the "maximal blast" area */
+ for (y = 0; y < 32; y++)
+ {
+ for (x = 0; x < 32; x++)
+ {
+ map[y][x] = FALSE;
+ }
+ }
+
+ /* Check around the epicenter */
+ for (dy = -r; dy <= r; dy++)
+ {
+ for (dx = -r; dx <= r; dx++)
+ {
+ /* Extract the location */
+ yy = cy + dy;
+ xx = cx + dx;
+
+ /* Skip illegal grids */
+ if (!in_bounds(yy, xx)) continue;
+
+ /* Skip distant grids */
+ if (distance(cy, cx, yy, xx) > r) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[yy][xx];
+
+ /* Lose room and vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* Lose light and knowledge */
+ c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Skip the epicenter */
+ if (!dx && !dy) continue;
+
+ /* Skip most grids */
+ if (rand_int(100) < 85) continue;
+
+ /* Damage this grid */
+ map[16 + yy - cy][16 + xx - cx] = TRUE;
+
+ /* Hack -- Take note of player damage */
+ if ((yy == p_ptr->py) && (xx == p_ptr->px)) hurt = TRUE;
+ }
+ }
+
+ /* First, affect the player (if necessary) */
+ if (hurt && !p_ptr->wraith_form)
+ {
+ /* Check around the player */
+ for (i = 0; i < 8; i++)
+ {
+ /* Access the location */
+ y = p_ptr->py + ddy[i];
+ x = p_ptr->px + ddx[i];
+
+ /* Skip non-empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Important -- Skip "quake" grids */
+ if (map[16 + y - cy][16 + x - cx]) continue;
+
+ /* Count "safe" grids */
+ sn++;
+
+ /* Randomize choice */
+ if (rand_int(sn) > 0) continue;
+
+ /* Save the safe location */
+ sy = y;
+ sx = x;
+ }
+
+ /* Random message */
+ switch (randint(3))
+ {
+ case 1:
+ {
+ msg_print("The cave ceiling collapses!");
+ break;
+ }
+ case 2:
+ {
+ msg_print("The cave floor twists in an unnatural way!");
+ break;
+ }
+ default:
+ {
+ msg_print("The cave quakes! You are pummeled with debris!");
+ break;
+ }
+ }
+
+ /* Hurt the player a lot */
+ if (!sn)
+ {
+ /* Message and damage */
+ msg_print("You are severely crushed!");
+ damage = 300;
+ }
+
+ /* Destroy the grid, and push the player to safety */
+ else
+ {
+ /* Calculate results */
+ switch (randint(3))
+ {
+ case 1:
+ {
+ msg_print("You nimbly dodge the blast!");
+ damage = 0;
+ break;
+ }
+ case 2:
+ {
+ msg_print("You are bashed by rubble!");
+ damage = damroll(10, 4);
+ (void)set_stun(p_ptr->stun + randint(50));
+ break;
+ }
+ case 3:
+ {
+ msg_print("You are crushed between the floor and ceiling!");
+ damage = damroll(10, 4);
+ (void)set_stun(p_ptr->stun + randint(50));
+ break;
+ }
+ }
+
+ /* Save the old location */
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ /* Move the player to the safe location */
+ p_ptr->py = sy;
+ p_ptr->px = sx;
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ /* Redraw the new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel */
+ verify_panel();
+ }
+
+ /* Important -- no wall on player */
+ map[16 + p_ptr->py - cy][16 + p_ptr->px - cx] = FALSE;
+
+ /* Semi-wraiths have to be hurt *some*, says DG */
+ if (PRACE_FLAG(PR1_SEMI_WRAITH))
+ damage /= 4;
+
+ /* Take some damage */
+ if (damage) take_hit(damage, "an earthquake");
+ }
+
+
+ /* Examine the quaked region */
+ for (dy = -r; dy <= r; dy++)
+ {
+ for (dx = -r; dx <= r; dx++)
+ {
+ /* Extract the location */
+ yy = cy + dy;
+ xx = cx + dx;
+
+ /* Skip unaffected grids */
+ if (!map[16 + yy - cy][16 + xx - cx]) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[yy][xx];
+
+ /* Process monsters */
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Most monsters cannot co-exist with rock */
+ if (!(r_ptr->flags2 & (RF2_KILL_WALL)) &&
+ !(r_ptr->flags2 & (RF2_PASS_WALL)))
+ {
+ char m_name[80];
+
+ /* Assume not safe */
+ sn = 0;
+
+ /* Monster can move to escape the wall */
+ if (!(r_ptr->flags1 & (RF1_NEVER_MOVE)))
+ {
+ /* Look for safety */
+ for (i = 0; i < 8; i++)
+ {
+ /* Access the grid */
+ y = yy + ddy[i];
+ x = xx + ddx[i];
+
+ /* Skip non-empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no safety on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat <= FEAT_PATTERN_XTRA2) &&
+ (cave[y][x].feat >= FEAT_PATTERN_START))
+ continue;
+
+ /* Important -- Skip "quake" grids */
+ if (map[16 + y - cy][16 + x - cx]) continue;
+
+ /* Count "safe" grids */
+ sn++;
+
+ /* Randomize choice */
+ if (rand_int(sn) > 0) continue;
+
+ /* Save the safe grid */
+ sy = y;
+ sx = x;
+ }
+ }
+
+ /* Describe the monster */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Scream in pain */
+ msg_format("%^s wails out in pain!", m_name);
+
+ /* Take damage from the quake */
+ damage = (sn ? damroll(4, 8) : 200);
+
+ /* Monster is certainly awake */
+ m_ptr->csleep = 0;
+
+ /* Apply damage directly */
+ m_ptr->hp -= damage;
+
+ /* Delete (not kill) "dead" monsters */
+ if (m_ptr->hp < 0)
+ {
+ /* Message */
+ msg_format("%^s is embedded in the rock!", m_name);
+
+ /* Delete the monster */
+ delete_monster(yy, xx);
+
+ /* No longer safe */
+ sn = 0;
+ }
+
+ /* Hack -- Escape from the rock */
+ if (sn)
+ {
+ int m_idx = cave[yy][xx].m_idx;
+
+ /* Update the new location */
+ cave[sy][sx].m_idx = m_idx;
+
+ /* Update the old location */
+ cave[yy][xx].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = sy;
+ m_ptr->fx = sx;
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(yy, xx);
+
+ /* Redraw the new grid */
+ lite_spot(sy, sx);
+ }
+ }
+ }
+ }
+ }
+
+
+ /* Examine the quaked region */
+ for (dy = -r; dy <= r; dy++)
+ {
+ for (dx = -r; dx <= r; dx++)
+ {
+ /* Extract the location */
+ yy = cy + dy;
+ xx = cx + dx;
+
+ /* Skip unaffected grids */
+ if (!map[16 + yy - cy][16 + xx - cx]) continue;
+
+ /* Access the cave grid */
+ c_ptr = &cave[yy][xx];
+
+ /* Paranoia -- never affect player */
+ if ((yy == p_ptr->py) && (xx == p_ptr->px)) continue;
+
+ /* Destroy location (if valid) */
+ if (cave_valid_bold(yy, xx))
+ {
+ bool_ floor = cave_floor_bold(yy, xx);
+
+ /* Delete objects */
+ delete_object(yy, xx);
+
+ /* Wall (or floor) type */
+ t = (floor ? rand_int(100) : 200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(yy, xx, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 70)
+ {
+ /* Create quartz vein */
+ cave_set_feat(yy, xx, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 100)
+ {
+ /* Create magma vein */
+ cave_set_feat(yy, xx, FEAT_MAGMA);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ cave_set_feat(yy, xx, FEAT_FLOOR);
+ }
+ }
+ }
+ }
+
+
+ /* Mega-Hack -- Forget the view */
+ p_ptr->update |= (PU_UN_VIEW);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Update the health bar */
+ p_ptr->redraw |= (PR_HEALTH);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+/*
+ * This routine clears the entire "temp" set.
+ *
+ * This routine will Perma-Lite all "temp" grids.
+ *
+ * This routine is used (only) by "lite_room()"
+ *
+ * Dark grids are illuminated.
+ *
+ * Also, process all affected monsters.
+ *
+ * SMART monsters always wake up when illuminated
+ * NORMAL monsters wake up 1/4 the time when illuminated
+ * STUPID monsters wake up 1/10 the time when illuminated
+ */
+static void cave_temp_room_lite(void)
+{
+ int i;
+
+ /* Apply flag changes */
+ for (i = 0; i < temp_n; i++)
+ {
+ int y = temp_y[i];
+ int x = temp_x[i];
+
+ cave_type *c_ptr = &cave[y][x];
+
+ /* No longer in the array */
+ c_ptr->info &= ~(CAVE_TEMP);
+
+ /* Update only non-CAVE_GLOW grids */
+ /* if (c_ptr->info & (CAVE_GLOW)) continue; */
+
+ /* Perma-Lite */
+ c_ptr->info |= (CAVE_GLOW);
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Process the grids */
+ for (i = 0; i < temp_n; i++)
+ {
+ int y = temp_y[i];
+ int x = temp_x[i];
+
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Redraw the grid */
+ lite_spot(y, x);
+
+ /* Process affected monsters */
+ if (c_ptr->m_idx)
+ {
+ int chance = 25;
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, FALSE);
+
+ /* Stupid monsters rarely wake up */
+ if (r_ptr->flags2 & (RF2_STUPID)) chance = 10;
+
+ /* Smart monsters always wake up */
+ if (r_ptr->flags2 & (RF2_SMART)) chance = 100;
+
+ /* Sometimes monsters wake up */
+ if (m_ptr->csleep && (rand_int(100) < chance))
+ {
+ /* Wake up! */
+ m_ptr->csleep = 0;
+
+ /* Notice the "waking up" */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s wakes up.", m_name);
+ }
+ }
+ }
+ }
+
+ /* None left */
+ temp_n = 0;
+}
+
+
+
+/*
+ * This routine clears the entire "temp" set.
+ *
+ * This routine will "darken" all "temp" grids.
+ *
+ * In addition, some of these grids will be "unmarked".
+ *
+ * This routine is used (only) by "unlite_room()"
+ *
+ * Also, process all affected monsters
+ */
+static void cave_temp_room_unlite(void)
+{
+ int i;
+
+ /* Apply flag changes */
+ for (i = 0; i < temp_n; i++)
+ {
+ int y = temp_y[i];
+ int x = temp_x[i];
+
+ cave_type *c_ptr = &cave[y][x];
+
+ /* No longer in the array */
+ c_ptr->info &= ~(CAVE_TEMP);
+
+ /* Darken the grid */
+ c_ptr->info &= ~(CAVE_GLOW);
+
+ /* Hack -- Forget "boring" grids */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ /* note_spot(y, x); */
+ }
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Process the grids */
+ for (i = 0; i < temp_n; i++)
+ {
+ int y = temp_y[i];
+ int x = temp_x[i];
+
+ /* Redraw the grid */
+ lite_spot(y, x);
+ }
+
+ /* None left */
+ temp_n = 0;
+}
+
+
+
+
+/*
+ * Aux function -- see below
+ */
+static void cave_temp_room_aux(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Avoid infinite recursion */
+ if (c_ptr->info & (CAVE_TEMP)) return;
+
+ /* Do not "leave" the current room */
+ if (!(c_ptr->info & (CAVE_ROOM))) return;
+
+ /* Paranoia -- verify space */
+ if (temp_n == TEMP_MAX) return;
+
+ /* Mark the grid as "seen" */
+ c_ptr->info |= (CAVE_TEMP);
+
+ /* Add it to the "seen" set */
+ temp_y[temp_n] = y;
+ temp_x[temp_n] = x;
+ temp_n++;
+}
+
+
+
+
+/*
+ * Illuminate any room containing the given location.
+ */
+void lite_room(int y1, int x1)
+{
+ int i, x, y;
+
+ /* Add the initial grid */
+ cave_temp_room_aux(y1, x1);
+
+ /* While grids are in the queue, add their neighbors */
+ for (i = 0; i < temp_n; i++)
+ {
+ x = temp_x[i], y = temp_y[i];
+
+ /* Walls get lit, but stop light */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Spread adjacent */
+ cave_temp_room_aux(y + 1, x);
+ cave_temp_room_aux(y - 1, x);
+ cave_temp_room_aux(y, x + 1);
+ cave_temp_room_aux(y, x - 1);
+
+ /* Spread diagonal */
+ cave_temp_room_aux(y + 1, x + 1);
+ cave_temp_room_aux(y - 1, x - 1);
+ cave_temp_room_aux(y - 1, x + 1);
+ cave_temp_room_aux(y + 1, x - 1);
+ }
+
+ /* Now, lite them all up at once */
+ cave_temp_room_lite();
+}
+
+
+/*
+ * Darken all rooms containing the given location
+ */
+void unlite_room(int y1, int x1)
+{
+ int i, x, y;
+
+ /* Add the initial grid */
+ cave_temp_room_aux(y1, x1);
+
+ /* Spread, breadth first */
+ for (i = 0; i < temp_n; i++)
+ {
+ x = temp_x[i], y = temp_y[i];
+
+ /* Walls get dark, but stop darkness */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Spread adjacent */
+ cave_temp_room_aux(y + 1, x);
+ cave_temp_room_aux(y - 1, x);
+ cave_temp_room_aux(y, x + 1);
+ cave_temp_room_aux(y, x - 1);
+
+ /* Spread diagonal */
+ cave_temp_room_aux(y + 1, x + 1);
+ cave_temp_room_aux(y - 1, x - 1);
+ cave_temp_room_aux(y - 1, x + 1);
+ cave_temp_room_aux(y + 1, x - 1);
+ }
+
+ /* Now, darken them all at once */
+ cave_temp_room_unlite();
+}
+
+
+
+/*
+ * Hack -- call light around the player
+ * Affect all monsters in the projection radius
+ */
+bool_ lite_area(int dam, int rad)
+{
+ int flg = PROJECT_GRID | PROJECT_KILL;
+
+ /* Hack -- Message */
+ if (!p_ptr->blind)
+ {
+ msg_print("You are surrounded by a white light.");
+ }
+
+ /* Hook into the "project()" function */
+ (void)project(0, rad, p_ptr->py, p_ptr->px, dam, GF_LITE_WEAK, flg);
+
+ /* Lite up the room */
+ lite_room(p_ptr->py, p_ptr->px);
+
+ /* Assume seen */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- call darkness around the player
+ * Affect all monsters in the projection radius
+ */
+bool_ unlite_area(int dam, int rad)
+{
+ int flg = PROJECT_GRID | PROJECT_KILL;
+
+ /* Hack -- Message */
+ if (!p_ptr->blind)
+ {
+ msg_print("Darkness surrounds you.");
+ }
+
+ /* Hook into the "project()" function */
+ (void)project(0, rad, p_ptr->py, p_ptr->px, dam, GF_DARK_WEAK, flg);
+
+ /* Lite up the room */
+ unlite_room(p_ptr->py, p_ptr->px);
+
+ /* Assume seen */
+ return (TRUE);
+}
+
+
+/*
+ * Cast a ball spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool_ fire_ball(int typ, int dir, int dam, int rad)
+{
+ int tx, ty;
+
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ /* Use the given direction */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ flg &= ~(PROJECT_STOP);
+ tx = target_col;
+ ty = target_row;
+ }
+
+ /* Analyze the "dir" and the "target". Hurt items on floor. */
+ return (project(0, (rad > 16) ? 16 : rad, ty, tx, dam, typ, flg));
+}
+
+/*
+ * Cast a cloud spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool_ fire_cloud(int typ, int dir, int dam, int rad, int time)
+{
+ int tx, ty;
+
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_STAY;
+
+ /* Use the given direction */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ flg &= ~(PROJECT_STOP);
+ tx = target_col;
+ ty = target_row;
+ }
+ project_time = time;
+
+ /* Analyze the "dir" and the "target". Hurt items on floor. */
+ return (project(0, (rad > 16) ? 16 : rad, ty, tx, dam, typ, flg));
+}
+
+/*
+ * Cast a wave spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool_ fire_wave(int typ, int dir, int dam, int rad, int time, s32b eff)
+{
+ project_time_effect = eff;
+ return (fire_cloud(typ, dir, dam, rad, time));
+}
+
+/*
+ * Cast a persistant beam spell
+ * Pass through monsters, as a "beam"
+ * Affect monsters (not grids or objects)
+ */
+bool_ fire_wall(int typ, int dir, int dam, int time)
+{
+ int flg = PROJECT_BEAM | PROJECT_KILL | PROJECT_STAY | PROJECT_GRID;
+ project_time = time;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+/*
+ * Cast a druidistic ball spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool_ fire_druid_ball(int typ, int dir, int dam, int rad)
+{
+ int tx, ty;
+
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_MANA_PATH;
+
+ /* Use the given direction */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ flg &= ~(PROJECT_STOP);
+ tx = target_col;
+ ty = target_row;
+ }
+
+ /* Analyze the "dir" and the "target". Hurt items on floor. */
+ return (project(0, (rad > 16) ? 16 : rad, ty, tx, dam, typ, flg));
+}
+
+
+/*
+ * Cast a ball-beamed spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool_ fire_ball_beam(int typ, int dir, int dam, int rad)
+{
+ int tx, ty;
+
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_BEAM;
+
+ /* Use the given direction */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ flg &= ~(PROJECT_STOP);
+ tx = target_col;
+ ty = target_row;
+ }
+
+ /* Analyze the "dir" and the "target". Hurt items on floor. */
+ return (project(0, (rad > 16) ? 16 : rad, ty, tx, dam, typ, flg));
+}
+
+
+void teleport_swap(int dir)
+{
+ int tx, ty;
+ cave_type * c_ptr;
+ monster_type * m_ptr;
+ monster_race * r_ptr;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+ else
+ {
+ tx = p_ptr->px + ddx[dir];
+ ty = p_ptr->py + ddy[dir];
+ }
+ c_ptr = &cave[ty][tx];
+
+ if (!c_ptr->m_idx)
+ {
+ msg_print("You can't trade places with that!");
+ }
+ else
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags3 & RF3_RES_TELE)
+ {
+ msg_print("Your teleportation is blocked!");
+ }
+ else
+ {
+ sound(SOUND_TELEPORT);
+
+ cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx;
+
+ /* Update the old location */
+ c_ptr->m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = p_ptr->py;
+ m_ptr->fx = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->px = tx;
+ p_ptr->py = ty;
+
+ tx = m_ptr->fx;
+ ty = m_ptr->fy;
+
+ /* Update the monster (new location) */
+ update_mon(cave[ty][tx].m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(ty, tx);
+
+ /* Redraw the new grid */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Execute the inscription */
+ c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+ if (c_ptr->inscription)
+ {
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_MONST_WALK)
+ {
+ execute_inscription(c_ptr->inscription, m_ptr->fy, m_ptr->fx);
+ }
+ }
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+ if (c_ptr->inscription)
+ {
+ msg_format("There is an inscription here: %s", inscription_info[c_ptr->inscription].text);
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_WALK)
+ {
+ execute_inscription(c_ptr->inscription, p_ptr->py, p_ptr->px);
+ }
+ }
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+ }
+ }
+}
+
+void swap_position(int lty, int ltx)
+{
+ int tx = ltx, ty = lty;
+ cave_type * c_ptr;
+ monster_type * m_ptr;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ c_ptr = &cave[ty][tx];
+
+ if (!c_ptr->m_idx)
+ {
+ sound(SOUND_TELEPORT);
+
+ /* Keep trace of the old location */
+ tx = p_ptr->px;
+ ty = p_ptr->py;
+
+ /* Move the player */
+ p_ptr->px = ltx;
+ p_ptr->py = lty;
+
+ /* Redraw the old grid */
+ lite_spot(ty, tx);
+
+ /* Redraw the new grid */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+ }
+ else
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ sound(SOUND_TELEPORT);
+
+ cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx;
+
+ /* Update the old location */
+ c_ptr->m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = p_ptr->py;
+ m_ptr->fx = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->px = tx;
+ p_ptr->py = ty;
+
+ tx = m_ptr->fx;
+ ty = m_ptr->fy;
+
+ /* Update the monster (new location) */
+ update_mon(cave[ty][tx].m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(ty, tx);
+
+ /* Redraw the new grid */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+ }
+}
+
+
+/*
+ * Hack -- apply a "projection()" in a direction (or at the target)
+ */
+bool_ project_hook(int typ, int dir, int dam, int flg)
+{
+ int tx, ty;
+
+ /* Pass through the target if needed */
+ flg |= (PROJECT_THRU);
+
+ /* Use the given direction */
+ tx = p_ptr->px + ddx[dir];
+ ty = p_ptr->py + ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+ /* Analyze the "dir" and the "target", do NOT explode */
+ return (project(0, 0, ty, tx, dam, typ, flg));
+}
+
+
+/*
+ * Cast a bolt spell
+ * Stop if we hit a monster, as a "bolt"
+ * Affect monsters (not grids or objects)
+ */
+bool_ fire_bolt(int typ, int dir, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+/*
+ * Cast a druidistic bolt spell
+ * Stop if we hit a monster, as a "bolt"
+ * Affect monsters (not grids or objects)
+ */
+bool_ fire_druid_bolt(int typ, int dir, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL | PROJECT_MANA_PATH;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+
+/*
+ * Cast a druidistic beam spell
+ * Pass through monsters, as a "beam"
+ * Affect monsters (not grids or objects)
+ */
+bool_ fire_druid_beam(int typ, int dir, int dam)
+{
+ int flg = PROJECT_BEAM | PROJECT_KILL | PROJECT_MANA_PATH;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+/*
+ * Cast a beam spell
+ * Pass through monsters, as a "beam"
+ * Affect monsters (not grids or objects)
+ */
+bool_ fire_beam(int typ, int dir, int dam)
+{
+ int flg = PROJECT_BEAM | PROJECT_KILL;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+
+/*
+ * Cast a bolt spell, or rarely, a beam spell
+ */
+bool_ fire_bolt_or_beam(int prob, int typ, int dir, int dam)
+{
+ if (rand_int(100) < prob)
+ {
+ return (fire_beam(typ, dir, dam));
+ }
+ else
+ {
+ return (fire_bolt(typ, dir, dam));
+ }
+}
+
+bool_ fire_godly_wrath(int y, int x, int typ, int rad, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ return (project(0, rad, y, x, dam, typ, flg));
+}
+
+bool_ fire_explosion(int y, int x, int typ, int rad, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ return (project(0, rad, y, x, dam, typ, flg));
+}
+
+/*
+ * Some of the old functions
+ */
+bool_ lite_line(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_KILL;
+ return (project_hook(GF_LITE_WEAK, dir, damroll(6, 8), flg));
+}
+
+
+bool_ drain_life(int dir, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_DRAIN, dir, dam, flg));
+}
+
+
+bool_ wall_to_mud(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+ return (project_hook(GF_KILL_WALL, dir, 20 + randint(30), flg));
+}
+
+
+bool_ wizard_lock(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+ return (project_hook(GF_JAM_DOOR, dir, 20 + randint(30), flg));
+}
+
+
+bool_ destroy_door(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM;
+ return (project_hook(GF_KILL_DOOR, dir, 0, flg));
+}
+
+
+bool_ disarm_trap(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM;
+ return (project_hook(GF_KILL_TRAP, dir, 0, flg));
+}
+
+
+bool_ heal_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_HEAL, dir, damroll(4, 6), flg));
+}
+
+
+bool_ speed_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_SPEED, dir, p_ptr->lev, flg));
+}
+
+
+bool_ slow_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_SLOW, dir, p_ptr->lev, flg));
+}
+
+
+bool_ sleep_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_SLEEP, dir, p_ptr->lev, flg));
+}
+
+
+bool_ stasis_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_STASIS, dir, p_ptr->lev, flg));
+}
+
+
+bool_ confuse_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_CONF, dir, plev, flg));
+}
+
+
+bool_ stun_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_STUN, dir, plev, flg));
+}
+
+
+bool_ poly_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_POLY, dir, p_ptr->lev, flg));
+}
+
+
+bool_ clone_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_CLONE, dir, 0, flg));
+}
+
+
+bool_ fear_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_TURN_ALL, dir, plev, flg));
+}
+
+
+bool_ death_ray(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_DEATH_RAY, dir, plev, flg));
+}
+
+
+bool_ teleport_monster(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_KILL;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return FALSE;
+ }
+
+ return (project_hook(GF_AWAY_ALL, dir, MAX_SIGHT * 5, flg));
+}
+
+
+/*
+ * Hooks -- affect adjacent grids (radius 1 ball attack)
+ */
+bool_ door_creation(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_MAKE_DOOR, flg));
+}
+
+
+bool_ trap_creation(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_MAKE_TRAP, flg));
+}
+
+
+bool_ glyph_creation(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_MAKE_GLYPH, flg));
+}
+
+
+bool_ wall_stone(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+ int flg = PROJECT_GRID | PROJECT_ITEM;
+ int featflags = f_info[c_ptr->feat].flags1;
+
+ bool_ dummy = (project(0, 1, y, x, 0, GF_STONE_WALL, flg));
+
+ if (!(featflags & FF1_PERMANENT) && !(featflags & FF1_WALL))
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ return dummy;
+}
+
+
+bool_ destroy_doors_touch(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_KILL_DOOR, flg));
+}
+
+bool_ destroy_traps_touch(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_KILL_TRAP, flg));
+}
+
+bool_ sleep_monsters_touch(void)
+{
+ int flg = PROJECT_KILL | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, p_ptr->lev, GF_OLD_SLEEP, flg));
+}
+
+
+void call_chaos(void)
+{
+ int Chaos_type, dummy, dir;
+ int plev = p_ptr->lev;
+ bool_ line_chaos = FALSE;
+
+ int hurt_types[30] =
+ {
+ GF_ELEC, GF_POIS, GF_ACID, GF_COLD,
+ GF_FIRE, GF_MISSILE, GF_ARROW, GF_PLASMA,
+ GF_HOLY_FIRE, GF_WATER, GF_LITE, GF_DARK,
+ GF_FORCE, GF_INERTIA, GF_MANA, GF_METEOR,
+ GF_ICE, GF_CHAOS, GF_NETHER, GF_DISENCHANT,
+ GF_SHARDS, GF_SOUND, GF_NEXUS, GF_CONFUSION,
+ GF_TIME, GF_GRAVITY, GF_ROCKET, GF_NUKE,
+ GF_HELL_FIRE, GF_DISINTEGRATE
+ };
+
+ Chaos_type = hurt_types[randint(30) - 1];
+ if (randint(4) == 1) line_chaos = TRUE;
+
+ if (randint(6) == 1)
+ {
+ for (dummy = 1; dummy < 10; dummy++)
+ {
+ if (dummy - 5)
+ {
+ if (line_chaos)
+ fire_beam(Chaos_type, dummy, 75);
+ else
+ fire_ball(Chaos_type, dummy, 75, 2);
+ }
+ }
+ }
+ else if (randint(3) == 1)
+ {
+ fire_ball(Chaos_type, 0, 300, 8);
+ }
+ else
+ {
+ if (!get_aim_dir(&dir)) return;
+ if (line_chaos)
+ fire_beam(Chaos_type, dir, 150);
+ else
+ fire_ball(Chaos_type, dir, 150, 3 + (plev / 35));
+ }
+}
+
+
+/*
+ * Activate the evil Topi Ylinen curse
+ * rr9: Stop the nasty things when a Cyberdemon is summoned
+ * or the player gets paralyzed.
+ */
+void activate_ty_curse(void)
+{
+ int i = 0;
+ bool_ stop_ty = FALSE;
+
+ do
+ {
+ switch (randint(27))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 16:
+ case 17:
+ aggravate_monsters(1);
+ if (randint(6) != 1) break;
+ case 4:
+ case 5:
+ case 6:
+ activate_hi_summon();
+ if (randint(6) != 1) break;
+case 7: case 8: case 9: case 18:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, 0);
+ if (randint(6) != 1) break;
+case 10: case 11: case 12:
+ msg_print("You feel your life draining away...");
+ lose_exp(p_ptr->exp / 16);
+ if (randint(6) != 1) break;
+case 13: case 14: case 15: case 19: case 20:
+ if (p_ptr->free_act && (randint(100) < p_ptr->skill_sav))
+ {
+ /* Do nothing */ ;
+ }
+ else
+ {
+ msg_print("You feel like a statue!");
+ if (p_ptr->free_act)
+ set_paralyzed (p_ptr->paralyzed + randint(3));
+ else
+ set_paralyzed (p_ptr->paralyzed + randint(13));
+ stop_ty = TRUE;
+ }
+ if (randint(6) != 1) break;
+case 21: case 22: case 23:
+ (void)do_dec_stat((randint(6)) - 1, STAT_DEC_NORMAL);
+ if (randint(6) != 1) break;
+ case 24:
+ msg_print("Huh? Who am I? What am I doing here?");
+ lose_all_info();
+ break;
+ case 25:
+ /*
+ * Only summon Cyberdemons deep in the dungeon.
+ */
+ if ((dun_level > 65) && !stop_ty)
+ {
+ summon_cyber();
+ stop_ty = TRUE;
+ break;
+ }
+ default:
+ while (i < 6)
+ {
+ do
+ {
+ (void)do_dec_stat(i, STAT_DEC_NORMAL);
+ }
+ while (randint(2) == 1);
+
+ i++;
+ }
+ }
+ }
+ while ((randint(3) == 1) && !stop_ty);
+}
+
+/*
+ * Activate the ultra evil Dark God curse
+ */
+void activate_dg_curse(void)
+{
+ int i = 0;
+ bool_ stop_dg = FALSE;
+
+ do
+ {
+ switch (randint(30))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 16:
+ case 17:
+ aggravate_monsters(1);
+ if (randint(8) != 1) break;
+ case 4:
+ case 5:
+ case 6:
+ msg_print("Oh! You feel that the curse is replicating itself!");
+ curse_equipment_dg(100, 50 * randint(2));
+ if (randint(8) != 1) break;
+ case 7:
+ case 8:
+ case 9:
+ case 18:
+ curse_equipment(100, 50 * randint(2));
+ if (randint(8) != 1) break;
+ case 10:
+ case 11:
+ case 12:
+ msg_print("You feel your life draining away...");
+ lose_exp(p_ptr->exp / 12);
+ if (rand_int(2))
+ {
+ msg_print("You feel the coldness of the Black Breath attacking you!");
+ p_ptr->black_breath = TRUE;
+ }
+ if (randint(8) != 1) break;
+ case 13:
+ case 14:
+ case 15:
+ if (p_ptr->free_act && (randint(100) < p_ptr->skill_sav))
+ {
+ /* Do nothing */ ;
+ }
+ else
+ {
+ msg_print("You feel like a statue!");
+ if (p_ptr->free_act)
+ set_paralyzed (p_ptr->paralyzed + randint(3));
+ else
+ set_paralyzed (p_ptr->paralyzed + randint(13));
+ stop_dg = TRUE;
+ }
+ if (randint(7) != 1) break;
+ case 19:
+ case 20:
+ {
+ msg_print("Woah! You see 10 little Morgoths dancing before you!");
+ set_confused(p_ptr->confused + randint(13 * 2));
+ if (rand_int(2)) stop_dg = TRUE;
+ }
+ if (randint(7) != 1) break;
+ case 21:
+ case 22:
+ case 23:
+ (void)do_dec_stat((randint(6)) - 1, STAT_DEC_PERMANENT);
+ if (randint(7) != 1) break;
+ case 24:
+ msg_print("Huh? Who am I? What am I doing here?");
+ lose_all_info();
+ break;
+case 27: case 28: case 29:
+ if (p_ptr->inventory[INVEN_WIELD].k_idx)
+ {
+ msg_print("Your weapon now seems useless...");
+ p_ptr->inventory[INVEN_WIELD].art_flags4 = TR4_NEVER_BLOW;
+ }
+ break;
+ case 25:
+ /*
+ * Only summon Thunderlords not too shallow in the dungeon.
+ */
+ if ((dun_level > 25) && !stop_dg)
+ {
+ msg_print("Oh! You attracted some evil Thunderlords!");
+ summon_dragon_riders();
+
+ /* This is evil -- DG */
+ if (rand_int(2)) stop_dg = TRUE;
+ break;
+ }
+ default:
+ while (i < 6)
+ {
+ do
+ {
+ (void)do_dec_stat(i, STAT_DEC_NORMAL);
+ }
+ while (randint(2) == 1);
+
+ i++;
+ }
+ }
+ }
+ while ((randint(4) == 1) && !stop_dg);
+}
+
+
+void activate_hi_summon(void)
+{
+ int i;
+
+ for (i = 0; i < (randint(9) + (dun_level / 40)); i++)
+ {
+ switch (randint(26) + (dun_level / 20) )
+ {
+ case 1:
+ case 2:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANT);
+ break;
+ case 3:
+ case 4:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_SPIDER);
+ break;
+ case 5:
+ case 6:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_HOUND);
+ break;
+ case 7:
+ case 8:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_HYDRA);
+ break;
+ case 9:
+ case 10:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANGEL);
+ break;
+ case 11:
+ case 12:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD);
+ break;
+ case 13:
+ case 14:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_DRAGON);
+ break;
+ case 15:
+ case 16:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_DEMON);
+ break;
+ case 17:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_WRAITH);
+ break;
+ case 18:
+ case 19:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNIQUE);
+ break;
+ case 20:
+ case 21:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_UNDEAD);
+ break;
+ case 22:
+ case 23:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DRAGON);
+ break;
+ case 24:
+ case 25:
+ (void) summon_specific(p_ptr->py, p_ptr->px, 100, SUMMON_HI_DEMON);
+ break;
+ default:
+ (void) summon_specific(p_ptr->py, p_ptr->px, (((dun_level * 3) / 2) + 5), 0);
+ }
+ }
+}
+
+
+void summon_cyber(void)
+{
+ int i;
+ int max_cyber = (dun_level / 50) + randint(6);
+
+ for (i = 0; i < max_cyber; i++)
+ {
+ (void)summon_specific(p_ptr->py, p_ptr->px, 100, SUMMON_HI_DEMON);
+ }
+}
+
+void summon_dragon_riders()
+{
+ int i;
+ int max_dr = (dun_level / 50) + randint(6);
+
+ for (i = 0; i < max_dr; i++)
+ {
+ (void)summon_specific(p_ptr->py, p_ptr->px, 100, SUMMON_THUNDERLORD);
+ }
+}
+
+
+void wall_breaker(void)
+{
+ int dummy = 5;
+
+ if (randint(80 + p_ptr->lev) < 70)
+ {
+ do
+ {
+ dummy = randint(9);
+ }
+ while ((dummy == 5) || (dummy == 0));
+
+ wall_to_mud (dummy);
+ }
+ else if (randint(100) > 30)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 1);
+ }
+ else
+ {
+ for (dummy = 1; dummy < 10; dummy++)
+ {
+ if (dummy - 5) wall_to_mud(dummy);
+ }
+ }
+}
+
+
+void bless_weapon(void)
+{
+ int item;
+ object_type *o_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+ char o_name[80];
+ cptr q, s;
+
+ /* Assume enchant weapon */
+ item_tester_hook = item_tester_hook_weapon;
+
+ /* Get an item */
+ q = "Bless which weapon? ";
+ s = "You have weapon to bless.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (o_ptr->ident & (IDENT_CURSED))
+ {
+
+ if (((f3 & (TR3_HEAVY_CURSE)) && (randint (100) < 33)) ||
+ (f3 & (TR3_PERMA_CURSE)))
+ {
+
+ msg_format("The black aura on %s %s disrupts the blessing!",
+ ((item >= 0) ? "your" : "the"), o_name);
+ return;
+ }
+
+ msg_format("A malignant aura leaves %s %s.",
+ ((item >= 0) ? "your" : "the"), o_name);
+
+ /* Uncurse it */
+ o_ptr->ident &= ~(IDENT_CURSED);
+
+ /* Hack -- Assume felt */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Take note */
+ o_ptr->sense = SENSE_UNCURSED;
+
+ /* Recalculate the bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /*
+ * Next, we try to bless it. Artifacts have a 1/3 chance of
+ * being blessed, otherwise, the operation simply disenchants
+ * them, godly power negating the magic. Ok, the explanation
+ * is silly, but otherwise priests would always bless every
+ * artifact weapon they find. Ego weapons and normal weapons
+ * can be blessed automatically.
+ */
+ if (f3 & TR3_BLESSED)
+ {
+ msg_format("%s %s %s blessed already.",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "were" : "was"));
+ return;
+ }
+
+ if (!(o_ptr->art_name || o_ptr->name1) || (randint(3) == 1))
+ {
+ /* Describe */
+ msg_format("%s %s shine%s!",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+ o_ptr->art_flags3 |= TR3_BLESSED;
+ }
+ else
+ {
+ bool_ dis_happened = FALSE;
+
+ msg_print("The artifact resists your blessing!");
+
+ /* Disenchant tohit */
+ if (o_ptr->to_h > 0)
+ {
+ o_ptr->to_h--;
+ dis_happened = TRUE;
+ }
+
+ if ((o_ptr->to_h > 5) && (rand_int(100) < 33)) o_ptr->to_h--;
+
+ /* Disenchant todam */
+ if (o_ptr->to_d > 0)
+ {
+ o_ptr->to_d--;
+ dis_happened = TRUE;
+ }
+
+ if ((o_ptr->to_d > 5) && (rand_int(100) < 33)) o_ptr->to_d--;
+
+ /* Disenchant toac */
+ if (o_ptr->to_a > 0)
+ {
+ o_ptr->to_a--;
+ dis_happened = TRUE;
+ }
+
+ if ((o_ptr->to_a > 5) && (rand_int(100) < 33)) o_ptr->to_a--;
+
+ if (dis_happened)
+ {
+ msg_print("There is a static feeling in the air...");
+ msg_format("%s %s %s disenchanted!",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "were" : "was"));
+ }
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER);
+}
+
+
+/*
+ * Detect all "nonliving", "undead" or "demonic" monsters on current panel
+ */
+bool_ detect_monsters_nonliving(int rad)
+{
+ int i, y, x;
+ bool_ flag = FALSE;
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect evil monsters */
+ if ((r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags3 & (RF3_DEMON)))
+ {
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of unnatural beings!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+/*
+ * Confuse monsters
+ */
+bool_ confuse_monsters(int dam)
+{
+ return (project_hack(GF_OLD_CONF, dam));
+}
+
+
+/*
+ * Charm monsters
+ */
+bool_ charm_monsters(int dam)
+{
+ return (project_hack(GF_CHARM, dam));
+}
+
+
+/*
+ * Charm animals
+ */
+bool_ charm_animals(int dam)
+{
+ return (project_hack(GF_CONTROL_ANIMAL, dam));
+}
+
+/*
+ * Charm demons
+ */
+bool_ charm_demons(int dam)
+{
+ return (project_hack(GF_CONTROL_DEMON, dam));
+}
+
+
+/*
+ * Stun monsters
+ */
+bool_ stun_monsters(int dam)
+{
+ return (project_hack(GF_STUN, dam));
+}
+
+
+/*
+ * Stasis monsters
+ */
+bool_ stasis_monsters(int dam)
+{
+ return (project_hack(GF_STASIS, dam));
+}
+
+
+/*
+ * Mindblast monsters
+ */
+bool_ mindblast_monsters(int dam)
+{
+ return (project_hack(GF_PSI, dam));
+}
+
+
+/*
+ * Banish all monsters
+ */
+bool_ banish_monsters(int dist)
+{
+ return (project_hack(GF_AWAY_ALL, dist));
+}
+
+
+/*
+ * Turn evil
+ */
+bool_ turn_evil(int dam)
+{
+ return (project_hack(GF_TURN_EVIL, dam));
+}
+
+
+/*
+ * Turn everyone
+ */
+bool_ turn_monsters(int dam)
+{
+ return (project_hack(GF_TURN_ALL, dam));
+}
+
+
+/*
+ * Death-ray all monsters (note: OBSCENELY powerful)
+ */
+bool_ deathray_monsters(void)
+{
+ return (project_hack(GF_DEATH_RAY, p_ptr->lev));
+}
+
+
+bool_ charm_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_CHARM, dir, plev, flg));
+}
+
+bool_ star_charm_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_STAR_CHARM, dir, plev, flg));
+}
+
+
+bool_ control_one_undead(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_CONTROL_UNDEAD, dir, plev, flg));
+}
+
+
+bool_ charm_animal(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_CONTROL_ANIMAL, dir, plev, flg));
+}
+
+void change_wild_mode(void)
+{
+ if (p_ptr->immovable && !p_ptr->wild_mode)
+ {
+ msg_print("Hmm, blinking there will take time.");
+ }
+
+ if (p_ptr->word_recall && !p_ptr->wild_mode)
+ {
+ msg_print("You will soon be recalled.");
+ return;
+ }
+
+ p_ptr->wild_mode = !p_ptr->wild_mode;
+
+ autosave_checkpoint();
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+void alter_reality(void)
+{
+ msg_print("The world changes!");
+
+ autosave_checkpoint();
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+/* Heal insanity. */
+bool_ heal_insanity(int val)
+{
+ if (p_ptr->csane < p_ptr->msane)
+ {
+ p_ptr->csane += val;
+
+ if (p_ptr->csane >= p_ptr->msane)
+ {
+ p_ptr->csane = p_ptr->msane;
+ p_ptr->csane_frac = 0;
+ }
+
+ p_ptr->redraw |= PR_SANITY;
+ p_ptr->window |= (PW_PLAYER);
+
+ if (val < 5)
+ {
+ msg_print("You feel a little better.");
+ }
+ else if (val < 15)
+ {
+ msg_print("You feel better.");
+ }
+ else if (val < 35)
+ {
+ msg_print("You feel much better.");
+ }
+ else
+ {
+ msg_print("You feel very good.");
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Send the player shooting through walls in the given direction until
+ * they reach a non-wall space, or a monster, or a permanent wall.
+ */
+bool_ passwall(int dir, bool_ safe)
+{
+ int x = p_ptr->px, y = p_ptr->py, ox = p_ptr->px, oy = p_ptr->py, lx = p_ptr->px, ly = p_ptr->py;
+ cave_type *c_ptr;
+ bool_ ok = FALSE;
+
+ if (p_ptr->wild_mode) return FALSE;
+ if (p_ptr->inside_quest) return FALSE;
+ if (dungeon_flags2 & DF2_NO_TELEPORT) return FALSE;
+
+ /* Must go somewhere */
+ if (dir == 5) return FALSE;
+
+ while (TRUE)
+ {
+ x += ddx[dir];
+ y += ddy[dir];
+ c_ptr = &cave[y][x];
+
+ /* Perm walls stops the transfer */
+ if ((!in_bounds(y, x)) && (f_info[c_ptr->feat].flags1 & FF1_PERMANENT))
+ {
+ /* get the last working position */
+ x -= ddx[dir];
+ y -= ddy[dir];
+ ok = FALSE;
+ break;
+ }
+
+ /* Never on a monster */
+ if (c_ptr->m_idx) continue;
+
+ /* Never stop in vaults */
+ if (c_ptr->info & CAVE_ICKY) continue;
+
+ /* From now on, the location COULD be used in special case */
+ lx = x;
+ ly = y;
+
+ /* Pass over walls */
+ if (f_info[c_ptr->feat].flags1 & FF1_WALL) continue;
+
+ /* So it must be ok */
+ ok = TRUE;
+ break;
+ }
+
+ if (!ok)
+ {
+ x = lx;
+ y = ly;
+
+ if (!safe)
+ {
+ msg_print("You emerge in the wall!");
+ take_hit(damroll(10, 8), "becoming one with a wall");
+ }
+ place_floor_convert_glass(y, x);
+ }
+
+ /* Move */
+ p_ptr->px = x;
+ p_ptr->py = y;
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ /* Redraw the new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+
+ return (TRUE);
+}
+
+/*
+ * Print a batch of dungeons.
+ */
+static void print_dungeon_batch(int *p, int start, int max, bool_ mode)
+{
+ char buf[80];
+ int i, j;
+ byte attr;
+
+
+ if (mode) prt(format(" %-31s", "Name"), 1, 20);
+
+ for (i = 0, j = start; i < 20 && j < max; i++, j++)
+ {
+ dungeon_info_type *d_ptr = &d_info[p[j]];
+
+ strnfmt(buf, 80, " %c) %-30s", I2A(i), d_name + d_ptr->name);
+ if (mode)
+ {
+ if (d_ptr->min_plev > p_ptr->lev)
+ {
+ attr = TERM_L_DARK;
+ }
+ else
+ {
+ attr = TERM_WHITE;
+ }
+ c_prt(attr, buf, 2 + i, 20);
+ }
+ }
+ if (mode) prt("", 2 + i, 20);
+
+ prt(format("Select a dungeon (a-%c), * to list, @ to select by name, +/- to scroll:", I2A(i - 1)), 0, 0);
+}
+
+int reset_recall_aux()
+{
+ char which;
+ int *p;
+ int max = 0, i, start = 0;
+ int ret;
+ bool_ mode = FALSE;
+
+
+ C_MAKE(p, max_d_idx, int);
+
+ /* Count the max */
+ for (i = 1; i < max_d_idx; i++)
+ {
+ /* skip "blocked" dungeons */
+ if (d_info[i].flags1 & DF1_NO_RECALL) continue;
+
+ if (max_dlv[i])
+ {
+ p[max++] = i;
+ }
+ }
+
+ character_icky = TRUE;
+ Term_save();
+
+ while (1)
+ {
+ print_dungeon_batch(p, start, max, mode);
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ ret = -1;
+ break;
+ }
+
+ else if (which == '*' || which == '?' || which == ' ')
+ {
+ mode = (mode) ? FALSE : TRUE;
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ else if (which == '+')
+ {
+ start += 20;
+ if (start >= max) start -= 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ else if (which == '-')
+ {
+ start -= 20;
+ if (start < 0) start += 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ else if (which == '@')
+ {
+ char buf[80], buf2[80];
+
+ strcpy(buf, (d_info[p_ptr->recall_dungeon].name + d_name));
+ if (!get_string("Which dungeon? ", buf, 79)) continue;
+
+ /* Find the index corresponding to the name */
+ for (i = 1; i < max_d_idx; i++)
+ {
+ sprintf(buf2, "%s", d_info[i].name + d_name);
+
+ /* Lowercase the name */
+ strlower(buf);
+ strlower(buf2);
+ if (strstr(buf2, buf))
+ {
+ /* valid dungeon found */
+ break;
+ }
+ }
+
+ if (i >= max_d_idx)
+ {
+ msg_print("Never heard of that place!");
+ msg_print(NULL);
+ continue;
+ }
+ else if (d_info[i].flags1 & DF1_NO_RECALL)
+ {
+ msg_print("This place blocks my magic!");
+ msg_print(NULL);
+ continue;
+ }
+ else if (d_info[i].min_plev > p_ptr->lev)
+ {
+ msg_print("You cannot go there yet!");
+ msg_print(NULL);
+ continue;
+ }
+ ret = i;
+ break;
+ }
+
+ else
+ {
+ which = tolower(which);
+ if (start + A2I(which) >= max)
+ {
+ bell();
+ continue;
+ }
+ if (start + A2I(which) < 0)
+ {
+ bell();
+ continue;
+ }
+ ret = p[start + A2I(which)];
+ break;
+ }
+ }
+
+ Term_load();
+ character_icky = FALSE;
+
+ C_FREE(p, max_d_idx, int);
+
+ return ret;
+}
+
+bool_ reset_recall(bool_ no_trepas_max_depth)
+{
+ int dun, depth, max;
+
+ /* Choose dungeon */
+ dun = reset_recall_aux();
+
+ if (dun < 1) return FALSE;
+
+ /* Choose depth */
+ if (!no_trepas_max_depth)
+ max = d_info[dun].maxdepth;
+ else
+ max = max_dlv[dun];
+ depth = get_quantity(format("Which level in %s(%d-%d)? ",
+ d_info[dun].name + d_name,
+ d_info[dun].mindepth, max),
+ max);
+
+ if (depth < 1) return FALSE;
+
+ /* Enforce minimum level */
+ if (depth < d_info[dun].mindepth) depth = d_info[dun].mindepth;
+
+ /* Mega hack -- Forbid levels 99 and 100 */
+ if ((depth == 99) || (depth == 100)) depth = 98;
+
+ p_ptr->recall_dungeon = dun;
+ max_dlv[p_ptr->recall_dungeon] = depth;
+
+ return TRUE;
+}
+
+/* The only way to get rid of the dreaded DG_CURSE*/
+void remove_dg_curse()
+{
+ int k;
+
+ /* Parse all the items */
+ for (k = INVEN_WIELD; k < INVEN_TOTAL; k++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[k];
+
+ if (o_ptr->k_idx && (o_ptr->art_flags4 & TR4_DG_CURSE))
+ {
+ o_ptr->art_flags3 &= ~TR3_HEAVY_CURSE;
+ o_ptr->art_flags3 &= ~TR3_CURSED;
+ o_ptr->art_flags4 &= ~TR4_DG_CURSE;
+ msg_print("The Morgothian Curse withers away.");
+ }
+ }
+}
+
+/*
+ * Creates a between gate
+ */
+void create_between_gate(int dist, int y, int x)
+{
+ int ii, ij, plev = get_skill(SKILL_CONVEYANCE);
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("Not on special levels!");
+ return;
+ }
+
+ if ((!x) || (!y))
+ {
+ msg_print("You open a Void Jumpgate. Choose a destination.");
+
+ if (!tgt_pt(&ii, &ij)) return;
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) ||
+ (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > dist) ||
+ (rand_int(plev * plev / 2) == 0))
+ {
+ msg_print("You fail to exit the void correctly!");
+ p_ptr->energy -= 100;
+ get_pos_player(10, &ij, &ii);
+ }
+ }
+ else
+ {
+ ij = y;
+ ii = x;
+ }
+ if (!(f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_PERMANENT))
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN);
+ cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8);
+ }
+ if (!(f_info[cave[ij][ii].feat].flags1 & FF1_PERMANENT))
+ {
+ cave_set_feat(ij, ii, FEAT_BETWEEN);
+ cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8);
+ }
+}
diff --git a/src/squeltch.c b/src/squeltch.c
new file mode 100644
index 00000000..603eaa0e
--- /dev/null
+++ b/src/squeltch.c
@@ -0,0 +1,553 @@
+/* File: squeltch.c */
+
+/* Purpose: Automatizer */
+
+/*
+ * Copyright (c) 2002 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+#include "lua/lua.h"
+#include "tolua.h"
+
+extern lua_State *L;
+
+/*
+ * The functions here use direct lua stack manipulation for calls instead of
+ * exec_lua(format()) because string manipulations are too slow for such
+ * functions
+ */
+
+/* Check the floor for "crap" */
+void squeltch_grid(void)
+{
+ int oldtop;
+ s16b this_o_idx, next_o_idx = 0;
+
+ if (!automatizer_enabled) return;
+
+ oldtop = lua_gettop(L);
+
+ /* Scan the pile of objects */
+ for (this_o_idx = cave[p_ptr->py][p_ptr->px].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ /* Acquire object */
+ object_type * o_ptr = &o_list[this_o_idx];
+
+ /* We've now seen one of these */
+ if (!k_info[o_ptr->k_idx].flavor)
+ {
+ object_aware(o_ptr);
+ }
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Push the function */
+ lua_settop(L, oldtop);
+ lua_getglobal(L, "apply_rules");
+
+ /* Push the args */
+ tolua_pushusertype(L, o_ptr, tolua_tag(L, "object_type"));
+ tolua_pushnumber(L, -this_o_idx);
+
+ /* Call the function */
+ if (lua_call(L, 2, 0))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling 'apply_rules'.");
+ lua_settop(L, oldtop);
+ return;
+ }
+ }
+ lua_settop(L, oldtop);
+}
+
+
+/* Check the inventory for "crap" */
+void squeltch_inventory(void)
+{
+ int oldtop;
+ int i;
+ int num_iter = 0;
+ bool_ found = TRUE;
+
+ if (!automatizer_enabled) return;
+
+ oldtop = lua_gettop(L);
+ while (found && num_iter ++ < 100)
+ {
+ /* Sometimes an index in the inventory is skipped */
+ found = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Push the function */
+ lua_settop(L, oldtop);
+ lua_getglobal(L, "apply_rules");
+
+ /* Push the args */
+ tolua_pushusertype(L, o_ptr, tolua_tag(L, "object_type"));
+ tolua_pushnumber(L, i);
+
+ /* Call the function */
+ if (lua_call(L, 2, 1))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling 'apply_rules'.");
+ lua_settop(L, oldtop);
+ return;
+ }
+
+ /* Did it return TRUE */
+ if (tolua_getnumber(L, -(lua_gettop(L) - oldtop), 0))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ if (num_iter >= 100)
+ {
+ cmsg_format(TERM_VIOLET, "'apply_rules' ran too often.");
+ }
+ lua_settop(L, oldtop);
+}
+
+/********************** The interface **********************/
+static cptr *get_rule_list(int *max)
+{
+ cptr *list;
+ int i;
+
+ *max = exec_lua("return __rules_max");
+ C_MAKE(list, *max, cptr);
+
+ for (i = 0; i < *max; i++)
+ {
+ list[i] = string_exec_lua(format("return __rules[%d].table.args.name", i));
+ }
+
+ return list;
+}
+
+static cptr types_list[] =
+{
+ "and",
+ "or",
+ "not",
+ "name",
+ "contain",
+ "inscribed",
+ "discount",
+ "symbol",
+ "state",
+ "status",
+ "tval",
+ "sval",
+ "race",
+ "subrace",
+ "class",
+ "level",
+ "skill",
+ "ability",
+ "inventory",
+ "equipment",
+ "comment",
+ NULL,
+};
+
+/* Create a new rule */
+static int automatizer_new_rule()
+{
+ int wid, hgt, max, begin = 0, sel = 0;
+ char c;
+
+ /* Get the number of types */
+ max = 0;
+ while (types_list[max] != NULL)
+ max++;
+
+ while (1)
+ {
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ display_list(0, 0, hgt - 1, 15, "Rule types", types_list, max, begin, sel, TERM_L_GREEN);
+
+ exec_lua(format("auto_aux:display_desc('%s')", types_list[sel]));
+ c_prt(TERM_YELLOW, "Example:", 5, 17);
+ exec_lua(format("xml.write_out_y = 6; xml.write_out_x = 16; xml.write_out_h = %d; xml.write_out_w = %d; xml.write_y = 0; xml.write_x = 0; xml:display_xml(auto_aux.types_desc['%s'][2][1], '')", hgt - 3 - 5, wid - 1 - 15 - 1, types_list[sel]));
+
+ c = inkey();
+
+ if (c == ESCAPE) break;
+ else if (c == '8')
+ {
+ sel--;
+ if (sel < 0)
+ {
+ sel = max - 1;
+ begin = max - hgt;
+ if (begin < 0) begin = 0;
+ }
+ if (sel < begin) begin = sel;
+ }
+ else if (c == '2')
+ {
+ sel++;
+ if (sel >= max)
+ {
+ sel = 0;
+ begin = 0;
+ }
+ if (sel >= begin + hgt - 1) begin++;
+ }
+ else if (c == '\r')
+ {
+ return sel;
+ }
+ }
+ return -1;
+}
+
+static void adjust_begin(int *begin, int *sel, int max, int hgt)
+{
+ if (*sel < 0)
+ {
+ *sel = max - 1;
+ *begin = *sel - hgt + 3;
+ if (*begin < 0) *begin = 0;
+ }
+ if (*sel < *begin) *begin = *sel;
+
+ if (*sel >= max)
+ {
+ *sel = 0;
+ *begin = 0;
+ }
+ if (*sel >= *begin + hgt - 2) (*begin)++;
+}
+
+#define ACTIVE_LIST 0
+#define ACTIVE_RULE 1
+void do_cmd_automatizer()
+{
+ int wid = 0, hgt = 0;
+ char c;
+ cptr *list = NULL;
+ int max, begin = 0, sel = 0;
+ int active = ACTIVE_LIST;
+ cptr keys;
+ cptr keys2;
+ cptr keys3;
+
+ Term_get_size(&wid, &hgt);
+
+ if (!automatizer_enabled)
+ {
+ if (msg_box("Automatizer is currently disabled, enable it? (y/n)", hgt / 2, wid / 2) == 'y')
+ {
+ automatizer_enabled = TRUE;
+ }
+ else
+ return;
+ }
+
+ screen_save();
+
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+
+ while (1)
+ {
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ list = get_rule_list(&max);
+ display_list(0, 0, hgt - 1, 15, "Rules", list, max, begin, sel, (active == ACTIVE_LIST) ? TERM_L_GREEN : TERM_GREEN);
+ C_FREE(list, max, cptr);
+
+ draw_box(0, 15, hgt - 4, wid - 1 - 15);
+ if (active == ACTIVE_RULE)
+ {
+ keys = "#Bup#W/#Bdown#W/#Bleft#W/#Bright#W to navitage rule, #B9#W/#B3#W/#B7#W/#B1#W to scroll";
+ keys2 = "#Btab#W for switch, #Ba#Wdd clause, #Bd#Welete clause/rule";
+ keys3 = "#Bx#W to toggle english/xml, #G?#W for Automatizer help";
+ exec_lua("xml.write_active = not nil");
+ }
+ else
+ {
+ keys = "#Bup#W/#Bdown#W to scroll, #Btab#W to switch to the rule window";
+ keys2 = "#Bu#W/#Bd#W to move rules, #Bn#Wew rule, #Br#Wename rule, #Bs#Wave rules";
+ keys3 = "#Rk#W to #rdisable#W the automatizer, #G?#W for Automatizer help";
+ exec_lua("xml.write_active = nil");
+ }
+ display_message(17, hgt - 3, strlen(keys), TERM_WHITE, keys);
+ display_message(17, hgt - 2, strlen(keys2), TERM_WHITE, keys2);
+ display_message(17, hgt - 1, strlen(keys3), TERM_WHITE, keys3);
+
+ if (max) exec_lua(format("xml.write_out_y = 1; xml.write_out_x = 16; xml.write_out_h = %d; xml.write_out_w = %d; xml.write_y = 0; xml.write_x = 0; xml:display_xml(__rules[%d].table, '')", hgt - 4 - 1, wid - 1 - 15 - 1, sel));
+
+ c = inkey();
+
+ if (c == ESCAPE) break;
+ if (active == ACTIVE_LIST)
+ {
+ if (c == '?')
+ {
+ screen_save();
+ show_file("automat.txt", "Automatizer help", 0, 0);
+ screen_load();
+ }
+ else if (c == '8')
+ {
+ if (!max) continue;
+ sel--;
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (c == '2')
+ {
+ if (!max) continue;
+ sel++;
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (c == 'u')
+ {
+ if (!max) continue;
+ sel = exec_lua(format("return auto_aux:move_up(%d)", sel));
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (c == 'd')
+ {
+ if (!max) continue;
+ sel = exec_lua(format("return auto_aux:move_down(%d)", sel));
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (c == 'n')
+ {
+ char name[20] = { '\0' };
+ char typ;
+
+ sprintf(name, "No name");
+ if (input_box("Name?", hgt / 2, wid / 2, name, sizeof(name)+1))
+ {
+ cptr styp = "nothing";
+ typ = msg_box("[D]estroy, [P]ickup, [I]nscribe, [N]othing rule?", hgt / 2, wid / 2);
+ if ((typ == 'd') || (typ == 'D')) styp = "destroy";
+ else if ((typ == 'p') || (typ == 'P')) styp = "pickup";
+ else if ((typ == 'i') || (typ == 'I')) styp = "inscribe";
+ exec_lua(format("auto_aux:new_rule(%d, '%s', '%s', ''); auto_aux:adjust_current(%d)", sel, name, styp, sel));
+ active = ACTIVE_RULE;
+ }
+ }
+ else if (c == 's')
+ {
+ char name[30] = { '\0' };
+
+ sprintf(name, "automat.atm");
+ if (input_box("Save name?", hgt / 2, wid / 2, name, sizeof(name)+1))
+ {
+ char buf[1025];
+ char ch;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ if (file_exist(buf))
+ {
+ c_put_str(TERM_WHITE, "File exists, continue?[y/n]", hgt / 2, wid / 2 - 14);
+ ch = inkey();
+ if ((ch != 'Y') && (ch != 'y'))
+ continue;
+ }
+
+ /* Open the non-existing file */
+ hook_file = my_fopen(buf, "w");
+
+ /* Invalid file */
+ if (!hook_file)
+ {
+ /* Message */
+ c_put_str(TERM_WHITE, "Saving rules failed! ", hgt / 2, wid / 2 - 14);
+ (void) inkey();
+
+ /* Error */
+ continue;
+ }
+
+
+
+ exec_lua("auto_aux:save_ruleset()");
+ my_fclose(hook_file);
+ /* Overwrite query message */
+ c_put_str(TERM_WHITE, "Saved rules in file ", hgt / 2, wid / 2 - 14);
+ (void) inkey();
+ }
+ }
+ else if (c == 'r')
+ {
+ char name[20];
+
+ if (!max) continue;
+
+ sprintf(name, "%s", string_exec_lua(format("return __rules[%d].table.args.name", sel)));
+ if (input_box("New name?", hgt / 2, wid / 2, name, 15))
+ {
+ exec_lua(format("auto_aux:rename_rule(%d, '%s')", sel, name));
+ }
+
+ continue;
+ }
+ else if (c == 'k')
+ {
+ automatizer_enabled = FALSE;
+ break;
+ }
+ else if (c == '\t')
+ {
+ if (!max) continue;
+ active = ACTIVE_RULE;
+ }
+ else if (c == 'x')
+ {
+ exec_lua("xml.display_english = not xml.display_english");
+ }
+ }
+ else if (active == ACTIVE_RULE)
+ {
+ if (c == '?')
+ {
+ screen_save();
+ show_file("automat.txt", "Automatizer help", 0, 0);
+ screen_load();
+ }
+ else if (c == '8')
+ {
+ exec_lua("auto_aux:go_up()");
+ }
+ else if (c == '2')
+ {
+ exec_lua("auto_aux:go_down()");
+ }
+ else if (c == '6')
+ {
+ exec_lua("auto_aux:go_right()");
+ }
+ else if (c == '4')
+ {
+ exec_lua(format("auto_aux:go_left(%d)", sel));
+ }
+ else if (c == '9')
+ {
+ exec_lua("auto_aux:scroll_up()");
+ }
+ else if (c == '3')
+ {
+ exec_lua("auto_aux:scroll_down()");
+ }
+ else if (c == '7')
+ {
+ exec_lua("auto_aux:scroll_left()");
+ }
+ else if (c == '1')
+ {
+ exec_lua("auto_aux:scroll_right()");
+ }
+ else if (c == 'a')
+ {
+ int s = automatizer_new_rule();
+ if (s == -1) continue;
+ exec_lua(format("auto_aux:add_child('%s')", types_list[s]));
+ }
+ else if (c == 'd')
+ {
+ if (max)
+ {
+ int new_sel;
+
+ new_sel = exec_lua(format("return auto_aux:del_self(%d)", sel));
+ if ((sel != new_sel) && (new_sel >= 0))
+ {
+ sel = new_sel;
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (new_sel == -1)
+ {
+ active = ACTIVE_LIST;
+ }
+ }
+ }
+ else if (c == '\t')
+ {
+ active = ACTIVE_LIST;
+ }
+ else if (c == 'x')
+ {
+ exec_lua("xml.display_english = not xml.display_english");
+ }
+ }
+ }
+
+ /* Recalculate the rules */
+ exec_lua("auto_aux.regen_ruleset()");
+
+ screen_load();
+}
+
+/* Add a new rule in an easy way */
+bool_ automatizer_create = FALSE;
+void automatizer_add_rule(object_type *o_ptr, bool_ destroy)
+{
+ char ch;
+ bool_ do_status = FALSE;
+ cptr type = "destroy";
+
+ if (!destroy)
+ {
+ type = "pickup";
+ }
+
+ while (TRUE)
+ {
+ if (!get_com(format("%s all of the same [T]ype, [F]amily or [N]ame, also use [S]tatus (%s)? ", (destroy) ? "Destroy" : "Pickup", (do_status) ? "Yes" : "No"), &ch))
+ {
+ break;
+ }
+
+ if (ch == 'S' || ch == 's')
+ {
+ do_status = !do_status;
+ continue;
+ }
+
+ if (ch == 'T' || ch == 't')
+ {
+ call_lua("easy_add_rule", "(s,s,d,O)", "", type, "tsval", do_status, o_ptr);
+ break;
+ }
+
+ if (ch == 'F' || ch == 'f')
+ {
+ call_lua("easy_add_rule", "(s,s,d,O)", "", type, "tval", do_status, o_ptr);
+ break;
+ }
+
+ if (ch == 'N' || ch == 'n')
+ {
+ call_lua("easy_add_rule", "(s,s,d,O)", "", type, "name", do_status, o_ptr);
+ break;
+ }
+ }
+}
diff --git a/src/status.c b/src/status.c
new file mode 100644
index 00000000..967a8323
--- /dev/null
+++ b/src/status.c
@@ -0,0 +1,773 @@
+/* File status.c */
+
+/* Purpose: Status information */
+
+/* Written by Pat Gunn <qc@apk.net> for ToME
+ * This file is released into the public domain
+ */
+
+/* Throughout this file, I make use of 2-d arrays called flag_arr.
+ * I fill them out with esp as the 0th element because then the second
+ * index is equal to the f-number that they are in the attributes, and
+ * that makes it more intuitive to use. I do need to fill them out in an
+ * odd order, but that's all abstracted nicely away. The 6th element is used
+ * to mark if the rest of the array is filled, that is, if there's an object
+ * there.
+ */
+
+#include "angband.h"
+
+static void row_trival(char*, s16b, u32b, s16b, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_bival(char*, s16b, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_npval(char*, s16b, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void statline(char*, int, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_hd_bon(int, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_count(char*, s16b, u32b, int, s16b, u32b, int, s16b, u32b, int, s16b, u32b, int, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static int row_x_start = 0;
+
+static void status_count(s32b val1, int v1, s32b val2, int v2, s32b val3, int v3, s32b val4, int v4, byte ypos, byte xpos);
+static void status_trival(s32b, s32b, byte, byte);
+static void status_bival(s32b, byte, byte);
+static void status_numeric(s32b, byte, byte);
+static void status_curses(void);
+static void status_companion(void);
+void status_sight(void);
+void status_attr(void);
+void status_combat(void);
+void status_main(void);
+void status_move(void);
+void status_item(void);
+void az_line(int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+#define STATNM_LENGTH 11
+#define SL_LENGTH 11
+
+#define INVEN_PLAYER (INVEN_TOTAL - INVEN_WIELD + 1)
+
+void status_attr(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 0;
+ char c;
+
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Statistics", yo++, 1);
+ yo += 2;
+ az_line(SL_LENGTH, flag_arr);
+ statline("Str", A_STR, TR1_STR, yo++, flag_arr);
+ statline("Int", A_INT, TR1_INT, yo++, flag_arr);
+ statline("Wis", A_INT, TR1_WIS, yo++, flag_arr);
+ statline("Con", A_CON, TR1_CON, yo++, flag_arr);
+ statline("Dex", A_DEX, TR1_DEX, yo++, flag_arr);
+ statline("Chr", A_CHR, TR1_CHR, yo++, flag_arr);
+ row_npval("Luck", 5, TR5_LUCK, yo++, flag_arr);
+ yo++;
+ row_npval("Life", 2, TR2_LIFE, yo++, flag_arr);
+ row_npval("Mana", 1, TR1_MANA, yo++, flag_arr);
+
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ c = inkey();
+ if (c == ESCAPE) break;
+ }
+}
+
+void status_move(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Movement", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_trival("Fly/Lev", 4, TR4_FLY, 3, TR3_FEATHER, yo++, flag_arr);
+ row_bival("Climb", 4, TR4_CLIMB, yo++, flag_arr);
+ row_npval("Dig", 1, TR1_TUNNEL, yo++, flag_arr);
+ row_npval("Speed", 1, TR1_SPEED, yo++, flag_arr);
+ row_bival("Wraith", 3, TR3_WRAITH, yo++, flag_arr);
+ yo++;
+ row_npval("Stealth", 1, TR1_STEALTH, yo++, flag_arr);
+ row_bival("Telep", 3, TR3_TELEPORT, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool_ loop_exit = FALSE;
+ char c;
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ {
+ loop_exit = TRUE;
+ }
+ }
+ if (loop_exit)
+ {
+ break;
+ }
+ }
+}
+
+void status_sight(void)
+/* Tell player about ESP, infravision, auto-id, see invis, and similar */
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Sight", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_bival("SeeInvis", 3, TR3_SEE_INVIS, yo++, flag_arr);
+ row_npval("Invis", 2, TR2_INVIS, yo++, flag_arr);
+ row_npval("Infra", 1, TR1_INFRA, yo++, flag_arr);
+ row_npval("Search", 1, TR1_SEARCH, yo++, flag_arr);
+ row_bival("AutoID", 4, TR4_AUTO_ID, yo++, flag_arr);
+ row_count("Light", 3, TR3_LITE1, 1, 4, TR4_LITE2, 2, 4, TR4_LITE3, 3, 0, 0, 0, yo++, flag_arr);
+ row_bival("Full ESP", 0, ESP_ALL, yo++, flag_arr);
+ row_bival("Orc ESP", 0, ESP_ORC, yo++, flag_arr);
+ row_bival("Trol ESP", 0, ESP_TROLL, yo++, flag_arr);
+ row_bival("Drag ESP", 0, ESP_DRAGON, yo++, flag_arr);
+ row_bival("GiantESP", 0, ESP_GIANT, yo++, flag_arr);
+ row_bival("DemonESP", 0, ESP_DEMON, yo++, flag_arr);
+ row_bival("Undd ESP", 0, ESP_UNDEAD, yo++, flag_arr);
+ row_bival("Evil ESP", 0, ESP_EVIL, yo++, flag_arr);
+ row_bival("Anim ESP", 0, ESP_ANIMAL, yo++, flag_arr);
+ row_bival("Drid ESP", 0, ESP_THUNDERLORD, yo++, flag_arr);
+ row_bival("Good ESP", 0, ESP_GOOD, yo++, flag_arr);
+ row_bival("SpidrESP", 0, ESP_SPIDER, yo++, flag_arr);
+ row_bival("NonlvESP", 0, ESP_NONLIVING, yo++, flag_arr);
+ row_bival("Uniq ESP", 0, ESP_UNIQUE, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool_ loop_exit = FALSE;
+ char c;
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ {
+ loop_exit = TRUE;
+ }
+ }
+ if (loop_exit)
+ {
+ break;
+ }
+ }
+}
+
+void status_item(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Misc", 0, 1);
+
+ row_x_start = 40;
+ az_line(STATNM_LENGTH + row_x_start, flag_arr);
+ row_bival("Blessed", 3, TR3_BLESSED, yo++, flag_arr);
+ row_bival("Activate", 3, TR3_ACTIVATE, yo++, flag_arr);
+ row_bival("EasyKnow", 3, TR3_EASY_KNOW, yo++, flag_arr);
+ row_bival("HideType", 3, TR3_HIDE_TYPE, yo++, flag_arr);
+ yo++;
+ row_bival("SafeAcid", 3, TR3_IGNORE_ACID, yo++, flag_arr);
+ row_bival("SafeElec", 3, TR3_IGNORE_ELEC, yo++, flag_arr);
+ row_bival("SafeFire", 3, TR3_IGNORE_FIRE, yo++, flag_arr);
+ row_bival("SafeCold", 3, TR3_IGNORE_COLD, yo++, flag_arr);
+ row_bival("ResMorgul", 5, TR5_RES_MORGUL, yo++, flag_arr);
+
+ yo = 3;
+ row_x_start = 0;
+ az_line(STATNM_LENGTH, flag_arr);
+ row_bival("Sh.fire", 3, TR3_SH_FIRE, yo++, flag_arr);
+ row_bival("Sh.elec", 3, TR3_SH_ELEC, yo++, flag_arr);
+ row_bival("Regen", 3, TR3_REGEN, yo++, flag_arr);
+ row_bival("SlowDigest", 3, TR3_SLOW_DIGEST, yo++, flag_arr);
+ row_bival("Precog", 4, TR4_PRECOGNITION, yo++, flag_arr);
+ row_bival("Auto.Id", 4, TR4_AUTO_ID, yo++, flag_arr);
+ row_bival("Spell.In", 5, TR5_SPELL_CONTAIN, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool_ loop_exit = FALSE;
+ char c;
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ loop_exit = TRUE;
+ }
+ if (loop_exit)
+ {
+ break;
+ }
+ }
+}
+
+void status_combat(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Combat", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_npval("Spell", 1, TR1_SPELL, yo++, flag_arr);
+ row_npval("Blows", 1, TR1_BLOWS, yo++, flag_arr);
+ row_npval("Crits", 5, TR5_CRIT, yo++, flag_arr);
+ row_npval("Ammo_Mgt", 3, TR3_XTRA_MIGHT, yo++, flag_arr);
+ row_npval("Ammo_Sht", 3, TR3_XTRA_SHOTS, yo++, flag_arr);
+ row_bival("Vorpal", 1, TR1_VORPAL, yo++, flag_arr);
+ row_bival("Quake", 1, TR1_IMPACT, yo++, flag_arr);
+ row_bival("Chaotic", 1, TR1_CHAOTIC, yo++, flag_arr);
+ row_bival("Vampiric", 1, TR1_VAMPIRIC, yo++, flag_arr);
+ row_bival("Poison", 1, TR1_BRAND_POIS, yo++, flag_arr);
+ row_bival("Acidic", 1, TR1_BRAND_ACID, yo++, flag_arr);
+ row_bival("Shocks", 1, TR1_BRAND_ELEC, yo++, flag_arr);
+ row_bival("Burns", 1, TR1_BRAND_FIRE, yo++, flag_arr);
+ row_bival("Chills", 1, TR1_BRAND_COLD, yo++, flag_arr);
+ row_bival("Wound", 5, TR5_WOUNDING, yo++, flag_arr);
+
+ row_x_start = 40;
+ yo = 3;
+ az_line(row_x_start + STATNM_LENGTH, flag_arr);
+ row_bival("No.Blow", 4, TR4_NEVER_BLOW, yo++, flag_arr);
+ row_trival("S/K Undd", 1, TR1_SLAY_UNDEAD, 5, TR5_KILL_UNDEAD, yo++, flag_arr);
+ row_trival("S/K Dmn", 1, TR1_SLAY_DEMON, 5, TR5_KILL_DEMON, yo++, flag_arr);
+ row_trival("S/K Drag", 1, TR1_SLAY_DRAGON, 1, TR1_KILL_DRAGON, yo++, flag_arr);
+ row_bival("Sl.Orc", 1, TR1_SLAY_ORC, yo++, flag_arr);
+ row_bival("Sl.Troll", 1, TR1_SLAY_TROLL, yo++, flag_arr);
+ row_bival("Sl.Giant", 1, TR1_SLAY_GIANT, yo++, flag_arr);
+ row_bival("Sl.Evil", 1, TR1_SLAY_EVIL, yo++, flag_arr);
+ row_bival("Sl.Animal", 1, TR1_SLAY_ANIMAL, yo++, flag_arr);
+ row_hd_bon(0, yo++, flag_arr);
+ row_hd_bon(1, yo++, flag_arr);
+ row_x_start = 0;
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool_ loop_exit = FALSE;
+ char c;
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ loop_exit = TRUE;
+ }
+ if (loop_exit)
+ {
+ break;
+ }
+ }
+}
+
+void status_curses(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Curses", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_trival("Hvy/Nrm", 3, TR3_HEAVY_CURSE, 3, TR3_CURSED, yo++, flag_arr);
+ row_bival("Perma", 3, TR3_PERMA_CURSE, yo++, flag_arr);
+ row_trival("DG/Ty", 4, TR4_DG_CURSE, 3, TR3_TY_CURSE, yo++, flag_arr);
+ row_trival("Prm/Auto", 3, TR3_PERMA_CURSE, 3, TR3_AUTO_CURSE, yo++, flag_arr);
+ row_bival("NoDrop", 4, TR4_CURSE_NO_DROP, yo++, flag_arr);
+ yo++;
+ row_bival("B.Breath", 4, TR4_BLACK_BREATH, yo++, flag_arr);
+ row_bival("Dr.Exp", 3, TR3_DRAIN_EXP, yo++, flag_arr);
+ row_bival("Dr.Mana", 5, TR5_DRAIN_MANA, yo++, flag_arr);
+ row_bival("Dr.HP", 5, TR5_DRAIN_HP, yo++, flag_arr);
+ row_bival("No Hit", 4, TR4_NEVER_BLOW, yo++, flag_arr);
+ row_bival("NoTelep", 3, TR3_NO_TELE, yo++, flag_arr);
+ row_bival("NoMagic", 3, TR3_NO_MAGIC, yo++, flag_arr);
+ row_bival("Aggrav", 3, TR3_AGGRAVATE, yo++, flag_arr);
+ row_bival("Clone", 4, TR4_CLONE, yo++, flag_arr);
+ row_bival("Temp", 5, TR5_TEMPORARY, yo++, flag_arr);
+ yo++;
+ row_count("Antimagic", 4, TR4_ANTIMAGIC_50, 5, 4, TR4_ANTIMAGIC_30, 3, 4, TR4_ANTIMAGIC_20, 2, 4, TR4_ANTIMAGIC_10, 1, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool_ loop_exit = FALSE;
+ char c;
+
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ {
+ loop_exit = TRUE;
+ }
+ }
+ if (loop_exit == TRUE)
+ {
+ break;
+ }
+ }
+}
+
+void status_res(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Resistances", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_trival("Fire", 2, TR2_IM_FIRE, 2, TR2_RES_FIRE, yo++, flag_arr);
+ row_trival("Cold", 2, TR2_IM_COLD, 2, TR2_RES_COLD, yo++, flag_arr);
+ row_trival("Acid", 2, TR2_IM_ACID, 2, TR2_RES_ACID, yo++, flag_arr);
+ row_trival("Lightning", 2, TR2_IM_ELEC, 2, TR2_RES_ELEC, yo++, flag_arr);
+ row_bival("Poison", 2, TR2_RES_POIS, yo++, flag_arr);
+ row_bival("Lite", 2, TR2_RES_LITE, yo++, flag_arr);
+ row_bival("Dark", 2, TR2_RES_DARK, yo++, flag_arr);
+ row_bival("Sound", 2, TR2_RES_SOUND, yo++, flag_arr);
+ row_bival("Shards", 2, TR2_RES_SHARDS, yo++, flag_arr);
+ row_trival("Nether", 4, TR4_IM_NETHER, 2, TR2_RES_NETHER, yo++, flag_arr);
+ row_bival("Nexus", 2, TR2_RES_NEXUS, yo++, flag_arr);
+ row_bival("Chaos", 2, TR2_RES_CHAOS, yo++, flag_arr);
+ row_bival("Disen.", 2, TR2_RES_DISEN, yo++, flag_arr);
+ row_bival("Confusion", 2, TR2_RES_CONF, yo++, flag_arr);
+ row_bival("Blindness", 2, TR2_RES_BLIND, yo++, flag_arr);
+ row_bival("Fear", 2, TR2_RES_FEAR, yo++, flag_arr);
+ row_bival("Free Act", 2, TR2_FREE_ACT, yo++, flag_arr);
+ row_bival("Reflect", 2, TR2_REFLECT, yo++, flag_arr);
+ row_bival("Hold Life", 2, TR2_HOLD_LIFE, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool_ loop_exit = FALSE;
+ char c;
+
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ {
+ loop_exit = TRUE;
+ }
+ }
+ if (loop_exit == TRUE)
+ {
+ break;
+ }
+ }
+}
+
+void status_main(void)
+{
+ int do_quit = 0;
+ char c;
+
+ character_icky = TRUE;
+ Term_save();
+ while (1)
+ {
+ clear_from(0);
+ c_put_str(TERM_WHITE, format("%s Character Status screen", game_module), 0, 10);
+ c_put_str(TERM_WHITE, "1) Statistics", 2, 5);
+ c_put_str(TERM_WHITE, "2) Movement", 3, 5);
+ c_put_str(TERM_WHITE, "3) Combat", 4, 5);
+ c_put_str(TERM_WHITE, "4) Resistances", 5, 5);
+ c_put_str(TERM_WHITE, "5) Misc", 6, 5);
+ c_put_str(TERM_WHITE, "6) Curses", 7, 5);
+ c_put_str(TERM_WHITE, "7) Sight", 8, 5);
+ c_put_str(TERM_WHITE, "8) Companions", 9, 5);
+ c_put_str(TERM_RED, "Press 'q' to Quit", 23, 5);
+ c = inkey();
+ switch (c)
+ {
+ case '1':
+ status_attr();
+ break;
+ case '2':
+ status_move();
+ break;
+ case '3':
+ status_combat();
+ break;
+ case '4':
+ status_res();
+ break;
+ case '5':
+ status_item();
+ break;
+ case '6':
+ status_curses();
+ break;
+ case '7':
+ status_sight();
+ break;
+ case '8':
+ status_companion();
+ break;
+ case 'q':
+ case ESCAPE:
+ do_quit = 1; /* Schedule leaving the outer loop */
+ break;
+ }
+ Term_fresh();
+ if (do_quit) break;
+ }
+ Term_load();
+ character_icky = FALSE;
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+ handle_stuff();
+}
+
+void az_line(int xo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+{
+ int index = xo; /* Leave room for description */
+ int i;
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (p_ptr->inventory[i].k_idx) /* Wearing anything here? */
+ {
+ char cstrng[2];
+ cstrng[0] = (i - INVEN_WIELD) + 'a'; /* Assumes ASCII */
+ cstrng[1] = '\0'; /* terminate it */
+ c_put_str(TERM_WHITE, cstrng, 2, index++); /* Assumes ASCII */
+
+ /* DGDGDGDG */
+ /* object_flags_known(&inventory[i],*/
+ object_flags(&p_ptr->inventory[i], /* Help me debug */
+ &flag_arr[i - INVEN_WIELD][1], /* f1 */
+ &flag_arr[i - INVEN_WIELD][2], /* f2 */
+ &flag_arr[i - INVEN_WIELD][3], /* f3 */
+ &flag_arr[i - INVEN_WIELD][4], /* f4 */
+ &flag_arr[i - INVEN_WIELD][5], /* f5 */
+ &flag_arr[i - INVEN_WIELD][0]); /* esp */
+ flag_arr[i - INVEN_WIELD][6] = 1; /* And mark it to display */
+ }
+ else flag_arr[i - INVEN_WIELD][6] = 0; /* Otherwise don't display it */
+ }
+ c_put_str(TERM_WHITE, "@", 2, index++);
+ player_flags(
+ &flag_arr[INVEN_PLAYER][1], /* f1 */
+ &flag_arr[INVEN_PLAYER][2], /* f2 */
+ &flag_arr[INVEN_PLAYER][3], /* f3 */
+ &flag_arr[INVEN_PLAYER][4], /* f4 */
+ &flag_arr[INVEN_PLAYER][5], /* f5 */
+ &flag_arr[INVEN_PLAYER][0] /* esp */
+ );
+ flag_arr[INVEN_PLAYER][6] = 1;
+}
+
+static void status_trival(s32b val1, s32b val2, byte ypos, byte xpos)
+{
+ if (val1 != 0)
+ c_put_str(TERM_L_BLUE, "*", ypos, xpos);
+ else if (val2 != 0)
+ c_put_str(TERM_L_BLUE, "+", ypos, xpos);
+ else
+ c_put_str(TERM_WHITE, ".", ypos, xpos);
+}
+
+static void status_bival(s32b val, byte ypos, byte xpos)
+{
+ if (val != 0)
+ c_put_str(TERM_L_BLUE, "+", ypos, xpos);
+ else
+ c_put_str(TERM_WHITE, ".", ypos, xpos);
+}
+
+static void status_numeric(s32b val, byte ypos, byte xpos)
+{
+ u32b magnitude = ABS(val);
+ int color = TERM_WHITE; /* default */
+ char strnum[2];
+
+ if (val<0) {
+ color = TERM_RED;
+ };
+ if (val>0) {
+ color = TERM_GREEN;
+ };
+
+ if (magnitude == 0) {
+ sprintf(strnum, ".");
+ } if (magnitude > 9) {
+ sprintf(strnum, "*");
+ } else {
+ sprintf(strnum, "%lu", (unsigned long int) magnitude);
+ }
+
+ c_put_str(color, strnum, ypos, xpos);
+}
+
+static void status_count(s32b val1, int v1, s32b val2, int v2, s32b val3, int v3, s32b val4, int v4, byte ypos, byte xpos)
+{
+ int v = 0;
+
+ if (val1 != 0) v += v1;
+ if (val2 != 0) v += v2;
+ if (val3 != 0) v += v3;
+ if (val4 != 0) v += v4;
+
+ status_numeric(v, ypos, xpos);
+}
+
+static void row_count(char* statname, s16b row1, u32b flag1, int v1, s16b row2, u32b flag2, int v2, s16b row3, u32b flag3, int v3, s16b row4, u32b flag4, int v4, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+{
+ int i;
+ int x = row_x_start;
+
+ c_put_str(TERM_L_GREEN, statname, yo, row_x_start);
+
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ status_count((flag_arr[i][row1] & flag1), v1, (flag_arr[i][row2] & flag2), v2, (flag_arr[i][row3] & flag3), v3, (flag_arr[i][row4] & flag4), v4, yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void row_trival(char* statname, s16b row, u32b flag, s16b row2, u32b flag2, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+{
+ int i;
+ int x = row_x_start;
+ c_put_str(TERM_L_GREEN, statname, yo, row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ status_trival(
+ (flag_arr[i][row] & flag),
+ (flag_arr[i][row2] & flag2),
+ yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void row_bival(char* statname, s16b row, u32b flag, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+{
+ int i;
+ int x = row_x_start;
+ c_put_str(TERM_L_GREEN, statname, yo, row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ status_bival((flag_arr[i][row] & flag), yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void row_npval(char* statname, s16b row, u32b flag, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+/* Displays nicely a pval-based status row */
+{
+ int i;
+ int x = row_x_start;
+ c_put_str(TERM_L_GREEN, statname, yo, row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ if (i == INVEN_PLAYER)
+ /* Special case, player_flags */
+ /* Players lack a pval, no way to calc value */
+ {
+ if (flag_arr[i][row] & flag)
+ c_put_str(TERM_YELLOW, "*", yo, x + STATNM_LENGTH);
+ else c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ if (flag_arr[i][row] & flag)
+ status_numeric(p_ptr->inventory[i + INVEN_WIELD].pval, yo, x + STATNM_LENGTH);
+ else
+ c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void statline(char* statname, int statidx, u32b flag, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+/* Displays a status row for a primary stat */
+{
+ int i;
+ int x = row_x_start;
+ char statstr[8];
+ byte stat_color = TERM_L_RED;
+
+ cnv_stat(p_ptr->stat_use[statidx], statstr);
+
+ c_put_str(TERM_L_GREEN, statstr, yo, 4 + row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ byte color = TERM_L_RED;
+
+ if (flag_arr[i][6] == 1)
+ {
+ switch (statidx)
+ {
+ case A_STR:
+ if (flag_arr[i][2] & TR2_SUST_STR)
+ color = TERM_L_BLUE;
+ break;
+ case A_INT:
+ if (flag_arr[i][2] & TR2_SUST_INT)
+ color = TERM_L_BLUE;
+ break;
+ case A_WIS:
+ if (flag_arr[i][2] & TR2_SUST_WIS)
+ color = TERM_L_BLUE;
+ break;
+ case A_DEX:
+ if (flag_arr[i][2] & TR2_SUST_DEX)
+ color = TERM_L_BLUE;
+ break;
+ case A_CON:
+ if (flag_arr[i][2] & TR2_SUST_CON)
+ color = TERM_L_BLUE;
+ break;
+ case A_CHR:
+ if (flag_arr[i][2] & TR2_SUST_CHR)
+ color = TERM_L_BLUE;
+ break;
+ }
+
+ if (i == INVEN_PLAYER ) /* Player flags */
+ {
+ if (flag_arr[i][1] & flag)
+ c_put_str((color == TERM_L_RED) ? TERM_YELLOW : color, "*", yo, x + SL_LENGTH);
+ else c_put_str((color == TERM_L_RED) ? TERM_WHITE : color, ".", yo, x + SL_LENGTH);
+ x++;
+ continue;
+ }
+ if (flag_arr[i][1] & flag)
+ status_numeric(p_ptr->inventory[i + INVEN_WIELD].pval, yo, x + SL_LENGTH);
+ else
+ c_put_str((color == TERM_L_RED) ? TERM_WHITE : color, ".", yo, x + SL_LENGTH);
+
+ if (color != TERM_L_RED)
+ stat_color = color;
+
+ x++;
+ }
+ }
+
+ c_put_str(stat_color, statname, yo, row_x_start);
+}
+
+static void row_hd_bon(int which, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+/* To-hit/dmg modifiers, selected by 1st argument */
+{
+ int i;
+ int x = row_x_start;
+ if ((which != 0) && (which != 1)) return;
+ c_put_str(TERM_L_GREEN, ((which == 0) ? "To-Hit" : "To-Dmg"), yo, row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ if (i == INVEN_PLAYER) /* Player? */
+ {
+ c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ if ( (which == 0) && (p_ptr->inventory[INVEN_WIELD + i].to_h != 0))
+ {
+ status_numeric(p_ptr->inventory[INVEN_WIELD + i].to_h, yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ if ( (which == 1) && (p_ptr->inventory[INVEN_WIELD + i].to_d != 0))
+ {
+ status_numeric(p_ptr->inventory[INVEN_WIELD + i].to_d, yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void status_companion(void)
+{
+ int i;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+ Term_clear();
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Calculate companions */
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ if (m_ptr->status == MSTATUS_COMPANION)
+ {
+ char m_name[80];
+ int b, y = 0;
+
+ /* Extract monster name */
+ monster_desc(m_name, m_ptr, 0x80);
+
+ fprintf(fff, "#####BCompanion: %s\n", m_name);
+
+ fprintf(fff, " Lev/Exp : [[[[[G%d / %ld]\n", m_ptr->level, (long int) m_ptr->exp);
+ if (m_ptr->level < MONSTER_LEVEL_MAX) fprintf(fff, " Next lvl: [[[[[G%ld]\n", (long int) MONSTER_EXP((s32b)m_ptr->level + 1));
+ else fprintf(fff, " Next lvl: [[[[[G****]\n");
+
+ fprintf(fff, " HP : [[[[[G%ld / %ld]\n", (long int) m_ptr->hp, (long int) m_ptr->maxhp);
+ fprintf(fff, " AC : [[[[[G%d]\n", m_ptr->ac);
+ fprintf(fff, " Speed : [[[[[G%d]\n", m_ptr->mspeed - 110);
+
+ for (b = 0; b < 4; b++)
+ {
+ if (!m_ptr->blow[b].d_dice) continue;
+ if (!m_ptr->blow[b].d_side) continue;
+
+ fprintf(fff, " Blow %1d : [[[[[G%dd%d]\n", y + 1, m_ptr->blow[b].d_dice, m_ptr->blow[b].d_side);
+ y++;
+ }
+
+ fprintf(fff, "\n");
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Companion List", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
diff --git a/src/store.c b/src/store.c
new file mode 100644
index 00000000..78120846
--- /dev/null
+++ b/src/store.c
@@ -0,0 +1,4458 @@
+/* File: store.c */
+
+/* Purpose: Store commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+#define RUMOR_CHANCE 8
+
+#define MAX_COMMENT_1 6
+
+static cptr comment_1[MAX_COMMENT_1] =
+{
+ "Okay.",
+ "Fine.",
+ "Accepted!",
+ "Agreed!",
+ "Done!",
+ "Taken!"
+};
+
+#define MAX_COMMENT_2A 2
+
+static cptr comment_2a[MAX_COMMENT_2A] =
+{
+ "You try my patience. %s is final.",
+ "My patience grows thin. %s is final."
+};
+
+#define MAX_COMMENT_2B 12
+
+static cptr comment_2b[MAX_COMMENT_2B] =
+{
+ "I can take no less than %s gold pieces.",
+ "I will accept no less than %s gold pieces.",
+ "Ha! No less than %s gold pieces.",
+ "You knave! No less than %s gold pieces.",
+ "That's a pittance! I want %s gold pieces.",
+ "That's an insult! I want %s gold pieces.",
+ "As if! How about %s gold pieces?",
+ "My gosh! How about %s gold pieces?",
+ "May the fleas of 1000 orcs molest you! Try %s gold pieces.",
+ "May your most favourite weapons rust! Try %s gold pieces.",
+ "May Morgoth find you tasty! Perhaps %s gold pieces?",
+ "Your mother was an Ogre! Perhaps %s gold pieces?"
+};
+
+#define MAX_COMMENT_3A 2
+
+static cptr comment_3a[MAX_COMMENT_3A] =
+{
+ "You try my patience. %s is final.",
+ "My patience grows thin. %s is final."
+};
+
+
+#define MAX_COMMENT_3B 12
+
+static cptr comment_3b[MAX_COMMENT_3B] =
+{
+ "Perhaps %s gold pieces?",
+ "How about %s gold pieces?",
+ "I will pay no more than %s gold pieces.",
+ "I can afford no more than %s gold pieces.",
+ "Be reasonable. How about %s gold pieces?",
+ "I'll buy it as scrap for %s gold pieces.",
+ "That is too much! How about %s gold pieces?",
+ "That looks war surplus! Say %s gold pieces?",
+ "Never! %s is more like it.",
+ "That's an insult! %s is more like it.",
+ "%s gold pieces and be thankful for it!",
+ "%s gold pieces and not a copper more!"
+};
+
+#define MAX_COMMENT_4A 4
+
+static cptr comment_4a[MAX_COMMENT_4A] =
+{
+ "Enough! You have abused me once too often!",
+ "Arghhh! I have had enough abuse for one day!",
+ "That does it! You shall waste my time no more!",
+ "This is getting nowhere! I'm going to Londis!"
+};
+
+#define MAX_COMMENT_4B 4
+
+static cptr comment_4b[MAX_COMMENT_4B] =
+{
+ "Leave my store!",
+ "Get out of my sight!",
+ "Begone, you scoundrel!",
+ "Out, out, out!"
+};
+
+#define MAX_COMMENT_5 8
+
+static cptr comment_5[MAX_COMMENT_5] =
+{
+ "Try again.",
+ "Ridiculous!",
+ "You will have to do better than that!",
+ "Do you wish to do business or not?",
+ "You've got to be kidding!",
+ "You'd better be kidding!",
+ "You try my patience.",
+ "Hmmm, nice weather we're having."
+};
+
+#define MAX_COMMENT_6 4
+
+static cptr comment_6[MAX_COMMENT_6] =
+{
+ "I must have heard you wrong.",
+ "I'm sorry, I missed that.",
+ "I'm sorry, what was that?",
+ "Sorry, what was that again?"
+};
+
+
+
+/*
+ * Successful haggle.
+ */
+static void say_comment_1(void)
+{
+ char rumour[80];
+
+ msg_print(comment_1[rand_int(MAX_COMMENT_1)]);
+
+ if (randint(RUMOR_CHANCE) == 1 && speak_unique)
+ {
+ msg_print("The shopkeeper whispers something into your ear:");
+ get_rnd_line("rumors.txt", rumour);
+ msg_print(rumour);
+ }
+}
+
+
+/*
+ * Continue haggling (player is buying)
+ */
+static void say_comment_2(s32b value, int annoyed)
+{
+ char tmp_val[80];
+
+ /* Prepare a string to insert */
+ strnfmt(tmp_val, 80, "%ld", (long)value);
+
+ /* Final offer */
+ if (annoyed > 0)
+ {
+ /* Formatted message */
+ msg_format(comment_2a[rand_int(MAX_COMMENT_2A)], tmp_val);
+ }
+
+ /* Normal offer */
+ else
+ {
+ /* Formatted message */
+ msg_format(comment_2b[rand_int(MAX_COMMENT_2B)], tmp_val);
+ }
+}
+
+
+/*
+ * Continue haggling (player is selling)
+ */
+static void say_comment_3(s32b value, int annoyed)
+{
+ char tmp_val[80];
+
+ /* Prepare a string to insert */
+ strnfmt(tmp_val, 80, "%ld", (long)value);
+
+ /* Final offer */
+ if (annoyed > 0)
+ {
+ /* Formatted message */
+ msg_format(comment_3a[rand_int(MAX_COMMENT_3A)], tmp_val);
+ }
+
+ /* Normal offer */
+ else
+ {
+ /* Formatted message */
+ msg_format(comment_3b[rand_int(MAX_COMMENT_3B)], tmp_val);
+ }
+}
+
+
+/*
+ * Kick 'da bum out. -RAK-
+ */
+static void say_comment_4(void)
+{
+ msg_print(comment_4a[rand_int(MAX_COMMENT_4A)]);
+ msg_print(comment_4b[rand_int(MAX_COMMENT_4B)]);
+}
+
+
+/*
+ * You are insulting me
+ */
+static void say_comment_5(void)
+{
+ msg_print(comment_5[rand_int(MAX_COMMENT_5)]);
+}
+
+
+/*
+ * That makes no sense.
+ */
+static void say_comment_6(void)
+{
+ msg_print(comment_6[rand_int(5)]);
+}
+
+
+
+/*
+ * Messages for reacting to purchase prices.
+ */
+
+#define MAX_COMMENT_7A 4
+
+static cptr comment_7a[MAX_COMMENT_7A] =
+{
+ "Arrgghh!",
+ "You moron!",
+ "You hear someone sobbing...",
+ "The shopkeeper howls in agony!"
+};
+
+#define MAX_COMMENT_7B 4
+
+static cptr comment_7b[MAX_COMMENT_7B] =
+{
+ "Darn!",
+ "You fiend!",
+ "The shopkeeper yells at you.",
+ "The shopkeeper glares at you."
+};
+
+#define MAX_COMMENT_7C 4
+
+static cptr comment_7c[MAX_COMMENT_7C] =
+{
+ "Cool!",
+ "You've made my day!",
+ "The shopkeeper giggles.",
+ "The shopkeeper laughs loudly."
+};
+
+#define MAX_COMMENT_7D 4
+
+static cptr comment_7d[MAX_COMMENT_7D] =
+{
+ "Yippee!",
+ "I think I'll retire!",
+ "The shopkeeper jumps for joy.",
+ "The shopkeeper smiles gleefully."
+};
+
+/*
+ * Let a shop-keeper React to a purchase
+ *
+ * We paid "price", it was worth "value", and we thought it was worth "guess"
+ */
+static void purchase_analyze(s32b price, s32b value, s32b guess)
+{
+ /* Item was worthless, but we bought it */
+ if ((value <= 0) && (price > value))
+ {
+ /* Comment */
+ msg_print(comment_7a[rand_int(MAX_COMMENT_7A)]);
+
+ /* Sound */
+ sound(SOUND_STORE1);
+ }
+
+ /* Item was cheaper than we thought, and we paid more than necessary */
+ else if ((value < guess) && (price > value))
+ {
+ /* Comment */
+ msg_print(comment_7b[rand_int(MAX_COMMENT_7B)]);
+
+ /* Sound */
+ sound(SOUND_STORE2);
+ }
+
+ /* Item was a good bargain, and we got away with it */
+ else if ((value > guess) && (value < (4 * guess)) && (price < value))
+ {
+ /* Comment */
+ msg_print(comment_7c[rand_int(MAX_COMMENT_7C)]);
+
+ /* Sound */
+ sound(SOUND_STORE3);
+ }
+
+ /* Item was a great bargain, and we got away with it */
+ else if ((value > guess) && (price < value))
+ {
+ /* Comment */
+ msg_print(comment_7d[rand_int(MAX_COMMENT_7D)]);
+
+ /* Sound */
+ sound(SOUND_STORE4);
+ }
+}
+
+
+
+
+
+/*
+ * We store the current "store number" here so everyone can access it
+ */
+static int cur_store_num = 7;
+
+/*
+ * We store the current "store page" here so everyone can access it
+ */
+static int store_top = 0;
+
+/*
+ * We store the current "store pointer" here so everyone can access it
+ */
+static store_type *st_ptr = NULL;
+
+/*
+ * We store the current "owner type" here so everyone can access it
+ */
+static owner_type *ot_ptr = NULL;
+
+
+
+/*
+ * Determine the price of an item (qty one) in a store.
+ *
+ * This function takes into account the player's charisma, and the
+ * shop-keepers friendliness, and the shop-keeper's base greed, but
+ * never lets a shop-keeper lose money in a transaction.
+ *
+ * The "greed" value should exceed 100 when the player is "buying" the
+ * item, and should be less than 100 when the player is "selling" it.
+ *
+ * Hack -- the black market always charges twice as much as it should.
+ *
+ * Charisma adjustment runs from 80 to 130
+ * Racial adjustment runs from 95 to 130
+ *
+ * Since greed/charisma/racial adjustments are centered at 100, we need
+ * to adjust (by 200) to extract a usable multiplier. Note that the
+ * "greed" value is always something (?).
+ */
+static s32b price_item(object_type *o_ptr, int greed, bool_ flip)
+{
+ int factor;
+ int adjust;
+ s32b price;
+
+
+ /* Get the value of one of the items */
+ price = object_value(o_ptr);
+
+ /* Worthless items */
+ if (price <= 0) return (0L);
+
+ /* Compute the racial factor */
+ if (is_state(st_ptr, STORE_LIKED))
+ {
+ factor = ot_ptr->costs[STORE_LIKED];
+ }
+ else if (is_state(st_ptr, STORE_HATED))
+ {
+ factor = ot_ptr->costs[STORE_HATED];
+ }
+ else
+ {
+ factor = ot_ptr->costs[STORE_NORMAL];
+ }
+
+ /* Add in the charisma factor */
+ factor += adj_chr_gold[p_ptr->stat_ind[A_CHR]];
+
+ /* Shop is buying */
+ if (flip)
+ {
+ /* Mega Hack^3 */
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ price /= 5;
+ break;
+ }
+
+ /* Adjust for greed */
+ adjust = 100 + (300 - (greed + factor));
+
+ /* Never get "silly" */
+ if (adjust > 100) adjust = 100;
+
+ /* Mega-Hack -- Black market sucks */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM) price = price / 2;
+ }
+
+ /* Shop is selling */
+ else
+ {
+ /* Adjust for greed */
+ adjust = 100 + ((greed + factor) - 300);
+
+ /* Never get "silly" */
+ if (adjust < 100) adjust = 100;
+
+ /* Mega-Hack -- Black market sucks */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM) price = price * 2;
+ }
+
+ /* Compute the final price (with rounding) */
+ price = (price * adjust + 50L) / 100L;
+
+ /* Note -- Never become "free" */
+ if (price <= 0L) return (1L);
+
+ /* Return the price */
+ return (price);
+}
+
+
+/*
+ * Special "mass production" computation
+ */
+static int mass_roll(int num, int max)
+{
+ int i, t = 0;
+ for (i = 0; i < num; i++) t += rand_int(max);
+ return (t);
+}
+
+
+/*
+ * Certain "cheap" objects should be created in "piles"
+ * Some objects can be sold at a "discount" (in small piles)
+ */
+static void mass_produce(object_type *o_ptr)
+{
+ int size = 1;
+ int discount = 0;
+
+ s32b cost = object_value(o_ptr);
+
+
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Food, Flasks, and Lites */
+ case TV_FOOD:
+ case TV_FLASK:
+ case TV_LITE:
+ {
+ if (cost <= 5L) size += mass_roll(3, 5);
+ if (cost <= 20L) size += mass_roll(3, 5);
+ break;
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_SCROLL:
+ {
+ if (cost <= 60L) size += mass_roll(3, 5);
+ if (cost <= 240L) size += mass_roll(1, 5);
+ break;
+ }
+
+ case TV_SYMBIOTIC_BOOK:
+ case TV_MUSIC_BOOK:
+ case TV_DRUID_BOOK:
+ case TV_DAEMON_BOOK:
+ case TV_BOOK:
+ {
+ if (cost <= 50L) size += mass_roll(2, 3);
+ if (cost <= 500L) size += mass_roll(1, 3);
+ break;
+ }
+
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_SHIELD:
+ case TV_GLOVES:
+ case TV_BOOTS:
+ case TV_CLOAK:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_POLEARM:
+ case TV_HAFTED:
+ case TV_DIGGING:
+ case TV_BOW:
+ {
+ if (o_ptr->name2) break;
+ if (cost <= 10L) size += mass_roll(3, 5);
+ if (cost <= 100L) size += mass_roll(3, 5);
+ break;
+ }
+
+ case TV_SPIKE:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ {
+ if (cost <= 5L) size += mass_roll(5, 5);
+ if (cost <= 50L) size += mass_roll(5, 5);
+ if (cost <= 500L) size += mass_roll(5, 5);
+ break;
+ }
+
+ /* Because many rods (and a few wands and staffs) are useful mainly
+ * in quantity, the Black Market will occasionally have a bunch of
+ * one kind. -LM- */
+ case TV_ROD:
+ case TV_WAND:
+ case TV_STAFF:
+ {
+ if (cost < 1601L) size += mass_roll(1, 5);
+ else if (cost < 3201L) size += mass_roll(1, 3);
+ break;
+ }
+ }
+
+
+ /* Pick a discount */
+ if (cost < 5)
+ {
+ discount = 0;
+ }
+ else if (rand_int(25) == 0)
+ {
+ discount = 25;
+ }
+ else if (rand_int(150) == 0)
+ {
+ discount = 50;
+ }
+ else if (rand_int(300) == 0)
+ {
+ discount = 75;
+ }
+ else if (rand_int(500) == 0)
+ {
+ discount = 90;
+ }
+
+
+ if (o_ptr->art_name)
+ {
+ if (cheat_peek && discount)
+ {
+ msg_print("No discount on random artifacts.");
+ }
+ discount = 0;
+ }
+
+ /* Save the discount */
+ o_ptr->discount = discount;
+
+ /* Save the total pile size */
+ o_ptr->number = size - (size * discount / 100);
+}
+
+
+
+
+
+
+
+
+/*
+ * Determine if a store item can "absorb" another item
+ *
+ * See "object_similar()" for the same function for the "player"
+ */
+static bool_ store_object_similar(object_type *o_ptr, object_type *j_ptr)
+{
+ /* Hack -- Identical items cannot be stacked */
+ if (o_ptr == j_ptr) return (0);
+
+ /* Different objects cannot be stacked */
+ if (o_ptr->k_idx != j_ptr->k_idx) return (0);
+
+ /* Different charges (etc) cannot be stacked, unless wands or rods. */
+ if ((o_ptr->pval != j_ptr->pval) && (o_ptr->tval != TV_WAND)) return (0);
+
+ /* Require many identical values */
+ if (o_ptr->pval2 != j_ptr->pval2) return (0);
+ if (o_ptr->pval3 != j_ptr->pval3) return (0);
+
+ /* Require many identical values */
+ if (o_ptr->to_h != j_ptr->to_h) return (0);
+ if (o_ptr->to_d != j_ptr->to_d) return (0);
+ if (o_ptr->to_a != j_ptr->to_a) return (0);
+
+ /* Require identical "artifact" names */
+ if (o_ptr->name1 != j_ptr->name1) return (0);
+
+ /* Require identical "ego-item" names */
+ if (o_ptr->name2 != j_ptr->name2) return (0);
+
+ /* Require identical "ego-item" names */
+ if (o_ptr->name2b != j_ptr->name2b) return (0);
+
+ /* Random artifacts don't stack !*/
+ if (o_ptr->art_name || j_ptr->art_name) return (0);
+
+ /* Hack -- Identical art_flags! */
+ if ((o_ptr->art_flags1 != j_ptr->art_flags1) ||
+ (o_ptr->art_flags2 != j_ptr->art_flags2) ||
+ (o_ptr->art_flags3 != j_ptr->art_flags3))
+ return (0);
+
+ /* Hack -- Never stack "powerful" items */
+ if (o_ptr->xtra1 || j_ptr->xtra1) return (0);
+
+ if (o_ptr->tval == TV_LITE)
+ {
+ /* Require identical "turns of light" */
+ if (o_ptr->timeout != j_ptr->timeout) return (0);
+ }
+ else
+ {
+ /* Hack -- Never stack recharging items */
+ if (o_ptr->timeout || j_ptr->timeout) return (0);
+ }
+
+ /* Require many identical values */
+ if (o_ptr->ac != j_ptr->ac) return (0);
+ if (o_ptr->dd != j_ptr->dd) return (0);
+ if (o_ptr->ds != j_ptr->ds) return (0);
+
+ /* Hack -- Never stack chests */
+ if (o_ptr->tval == TV_CHEST) return (0);
+
+ /* Require matching discounts */
+ if (o_ptr->discount != j_ptr->discount) return (0);
+
+ /* They match, so they must be similar */
+ return (TRUE);
+}
+
+
+/*
+ * Allow a store item to absorb another item
+ */
+static void store_object_absorb(object_type *o_ptr, object_type *j_ptr)
+{
+ int total = o_ptr->number + j_ptr->number;
+
+ /* Combine quantity, lose excess items */
+ o_ptr->number = (total > 99) ? 99 : total;
+
+ /* Hack -- if wands are stacking, combine the charges. -LM- */
+ if (o_ptr->tval == TV_WAND)
+ {
+ o_ptr->pval += j_ptr->pval;
+ }
+}
+
+
+/*
+ * Check to see if the shop will be carrying too many objects -RAK-
+ * Note that the shop, just like a player, will not accept things
+ * it cannot hold. Before, one could "nuke" potions this way.
+ */
+static bool_ store_check_num(object_type *o_ptr)
+{
+ int i;
+ object_type *j_ptr;
+
+ /* Free space is always usable */
+ if (st_ptr->stock_num < st_ptr->stock_size) return TRUE;
+
+ /* The "home" acts like the player */
+ if ((cur_store_num == 7) ||
+ (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM))
+ {
+ /* Check all the items */
+ for (i = 0; i < st_ptr->stock_num; i++)
+ {
+ /* Get the existing item */
+ j_ptr = &st_ptr->stock[i];
+
+ /* Can the new object be combined with the old one? */
+ if (object_similar(j_ptr, o_ptr)) return (TRUE);
+ }
+ }
+
+ /* Normal stores do special stuff */
+ else
+ {
+ /* Check all the items */
+ for (i = 0; i < st_ptr->stock_num; i++)
+ {
+ /* Get the existing item */
+ j_ptr = &st_ptr->stock[i];
+
+ /* Can the new object be combined with the old one? */
+ if (store_object_similar(j_ptr, o_ptr)) return (TRUE);
+ }
+ }
+
+ /* But there was no room at the inn... */
+ return (FALSE);
+}
+
+
+bool_ is_blessed(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags_known(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & TR3_BLESSED) return (TRUE);
+ else return (FALSE);
+}
+
+
+
+/*
+ * Determine if the current store will purchase the given item
+ *
+ * Note that a shop-keeper must refuse to buy "worthless" items
+ */
+static bool_ store_will_buy(object_type *o_ptr)
+{
+ /* Hack -- The Home is simple */
+ if (cur_store_num == 7) return (TRUE);
+
+ if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) return TRUE;
+
+ /* XXX XXX XXX Ignore "worthless" items */
+ if (object_value(o_ptr) <= 0) return (FALSE);
+
+ /* Lua can define things to buy */
+ if (process_hooks_ret(HOOK_STORE_BUY, "d", "(d,s,O)", st_ptr->st_idx, st_info[st_ptr->st_idx].name + st_name, o_ptr))
+ {
+ return process_hooks_return[0].num;
+ }
+
+ /* Assume not okay */
+ return (FALSE);
+}
+
+
+
+/*
+ * Add the item "o_ptr" to the inventory of the "Home"
+ *
+ * In all cases, return the slot (or -1) where the object was placed
+ *
+ * Note that this is a hacked up version of "inven_carry()".
+ *
+ * Also note that it may not correctly "adapt" to "knowledge" bacoming
+ * known, the player may have to pick stuff up and drop it again.
+ */
+static int home_carry(object_type *o_ptr)
+{
+ int slot;
+ s32b value, j_value;
+ int i;
+ object_type *j_ptr;
+
+
+ /* Check each existing item (try to combine) */
+ for (slot = 0; slot < st_ptr->stock_num; slot++)
+ {
+ /* Get the existing item */
+ j_ptr = &st_ptr->stock[slot];
+
+ /* The home acts just like the player */
+ if (object_similar(j_ptr, o_ptr))
+ {
+ /* Save the new number of items */
+ object_absorb(j_ptr, o_ptr);
+
+ /* All done */
+ return (slot);
+ }
+ }
+
+ /* No space? */
+ if (st_ptr->stock_num >= st_ptr->stock_size) return ( -1);
+
+
+ /* Determine the "value" of the item */
+ value = object_value(o_ptr);
+
+ /* Check existing slots to see if we must "slide" */
+ for (slot = 0; slot < st_ptr->stock_num; slot++)
+ {
+ /* Get that item */
+ j_ptr = &st_ptr->stock[slot];
+
+ /* Objects sort by decreasing type */
+ if (o_ptr->tval > j_ptr->tval) break;
+ if (o_ptr->tval < j_ptr->tval) continue;
+
+ /* Can happen in the home */
+ if (!object_aware_p(o_ptr)) continue;
+ if (!object_aware_p(j_ptr)) break;
+
+ /* Objects sort by increasing sval */
+ if (o_ptr->sval < j_ptr->sval) break;
+ if (o_ptr->sval > j_ptr->sval) continue;
+
+ /* Objects in the home can be unknown */
+ if (!object_known_p(o_ptr)) continue;
+ if (!object_known_p(j_ptr)) break;
+
+
+ /*
+ * Hack: otherwise identical rods sort by
+ * increasing recharge time --dsb
+ */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (o_ptr->timeout < j_ptr->timeout) break;
+ if (o_ptr->timeout > j_ptr->timeout) continue;
+ }
+
+ /* Objects sort by decreasing value */
+ j_value = object_value(j_ptr);
+ if (value > j_value) break;
+ if (value < j_value) continue;
+ }
+
+ /* Slide the others up */
+ for (i = st_ptr->stock_num; i > slot; i--)
+ {
+ st_ptr->stock[i] = st_ptr->stock[i - 1];
+ }
+
+ /* More stuff now */
+ st_ptr->stock_num++;
+
+ /* Insert the new item */
+ st_ptr->stock[slot] = *o_ptr;
+
+ /* Return the location */
+ return (slot);
+}
+
+
+/*
+ * Add the item "o_ptr" to a real stores inventory.
+ *
+ * If the item is "worthless", it is thrown away (except in the home).
+ *
+ * If the item cannot be combined with an object already in the inventory,
+ * make a new slot for it, and calculate its "per item" price. Note that
+ * this price will be negative, since the price will not be "fixed" yet.
+ * Adding an item to a "fixed" price stack will not change the fixed price.
+ *
+ * In all cases, return the slot (or -1) where the object was placed
+ */
+static int store_carry(object_type *o_ptr)
+{
+ int i, slot;
+ s32b value, j_value;
+ object_type *j_ptr;
+
+
+ /* Evaluate the object */
+ value = object_value(o_ptr);
+
+ /* Cursed/Worthless items "disappear" when sold */
+ if (value <= 0) return ( -1);
+
+ /* All store items are fully *identified* */
+ o_ptr->ident |= IDENT_MENTAL;
+
+ /* Erase the inscription */
+ o_ptr->note = 0;
+
+ /* Check each existing item (try to combine) */
+ for (slot = 0; slot < st_ptr->stock_num; slot++)
+ {
+ /* Get the existing item */
+ j_ptr = &st_ptr->stock[slot];
+
+ /* Can the existing items be incremented? */
+ if (store_object_similar(j_ptr, o_ptr))
+ {
+ /* Hack -- extra items disappear */
+ store_object_absorb(j_ptr, o_ptr);
+
+ /* All done */
+ return (slot);
+ }
+ }
+
+ /* No space? */
+ if (st_ptr->stock_num >= st_ptr->stock_size) return ( -1);
+
+
+ /* Check existing slots to see if we must "slide" */
+ for (slot = 0; slot < st_ptr->stock_num; slot++)
+ {
+ /* Get that item */
+ j_ptr = &st_ptr->stock[slot];
+
+ /* Objects sort by decreasing type */
+ if (o_ptr->tval > j_ptr->tval) break;
+ if (o_ptr->tval < j_ptr->tval) continue;
+
+ /* Objects sort by increasing sval */
+ if (o_ptr->sval < j_ptr->sval) break;
+ if (o_ptr->sval > j_ptr->sval) continue;
+
+
+ /*
+ * Hack: otherwise identical rods sort by
+ * increasing recharge time --dsb
+ */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (o_ptr->timeout < j_ptr->timeout) break;
+ if (o_ptr->timeout > j_ptr->timeout) continue;
+ }
+
+ /* Evaluate that slot */
+ j_value = object_value(j_ptr);
+
+ /* Objects sort by decreasing value */
+ if (value > j_value) break;
+ if (value < j_value) continue;
+ }
+
+ /* Slide the others up */
+ for (i = st_ptr->stock_num; i > slot; i--)
+ {
+ st_ptr->stock[i] = st_ptr->stock[i - 1];
+ }
+
+ /* More stuff now */
+ st_ptr->stock_num++;
+
+ /* Insert the new item */
+ st_ptr->stock[slot] = *o_ptr;
+
+ /* Return the location */
+ return (slot);
+}
+
+
+/*
+ * Increase, by a given amount, the number of a certain item
+ * in a certain store. This can result in zero items.
+ */
+static void store_item_increase(int item, int num)
+{
+ int cnt;
+ object_type *o_ptr;
+
+ /* Get the item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Verify the number */
+ cnt = o_ptr->number + num;
+ if (cnt > 255) cnt = 255;
+ else if (cnt < 0) cnt = 0;
+ num = cnt - o_ptr->number;
+
+ /* Save the new number */
+ o_ptr->number += num;
+}
+
+
+/*
+ * Remove a slot if it is empty
+ */
+static void store_item_optimize(int item)
+{
+ int j;
+ object_type *o_ptr;
+
+ /* Get the item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Must exist */
+ if (!o_ptr->k_idx) return;
+
+ /* Must have no items */
+ if (o_ptr->number) return;
+
+ /* One less item */
+ st_ptr->stock_num--;
+
+ /* Slide everyone */
+ for (j = item; j < st_ptr->stock_num; j++)
+ {
+ st_ptr->stock[j] = st_ptr->stock[j + 1];
+ }
+
+ /* Nuke the final slot */
+ object_wipe(&st_ptr->stock[j]);
+}
+
+
+/*
+ * This function will keep 'crap' out of the black market.
+ * Crap is defined as any item that is "available" elsewhere
+ * Based on a suggestion by "Lee Vogt" <lvogt@cig.mcel.mot.com>
+ */
+static bool_ black_market_crap(object_type *o_ptr)
+{
+ int i, j;
+
+ /* Ego items are never crap */
+ if (o_ptr->name2) return (FALSE);
+
+ /* Good items are never crap */
+ if (o_ptr->to_a > 0) return (FALSE);
+ if (o_ptr->to_h > 0) return (FALSE);
+ if (o_ptr->to_d > 0) return (FALSE);
+
+ /* Check all stores */
+ for (i = 0; i < max_st_idx; i++)
+ {
+ if (i == STORE_HOME) continue;
+ if (st_info[i].flags1 & SF1_MUSEUM) continue;
+
+ /* Check every item in the store */
+ for (j = 0; j < town_info[p_ptr->town_num].store[i].stock_num; j++)
+ {
+ object_type *j_ptr = &town_info[p_ptr->town_num].store[i].stock[j];
+
+ /* Duplicate item "type", assume crappy */
+ if (o_ptr->k_idx == j_ptr->k_idx) return (TRUE);
+ }
+ }
+
+ /* Assume okay */
+ return (FALSE);
+}
+
+
+/*
+ * Attempt to delete (some of) a random item from the store
+ * Hack -- we attempt to "maintain" piles of items when possible.
+ */
+static void store_delete(void)
+{
+ int what, num;
+
+ /* Pick a random slot */
+ what = rand_int(st_ptr->stock_num);
+
+ /* Determine how many items are here */
+ num = st_ptr->stock[what].number;
+
+ /* Hack -- sometimes, only destroy half the items */
+ if (rand_int(100) < 50) num = (num + 1) / 2;
+
+ /* Hack -- sometimes, only destroy a single item */
+ if (rand_int(100) < 50) num = 1;
+
+ /* Hack -- decrement the maximum timeouts and total charges of rods and wands. -LM- */
+ if (st_ptr->stock[what].tval == TV_WAND)
+ {
+ st_ptr->stock[what].pval -= num * st_ptr->stock[what].pval / st_ptr->stock[what].number;
+ }
+
+ /* Actually destroy (part of) the item */
+ store_item_increase(what, -num);
+ store_item_optimize(what);
+}
+
+/* Analyze store flags and return a level */
+int return_level()
+{
+ store_info_type *sti_ptr = &st_info[st_ptr->st_idx];
+ int level;
+
+ if (sti_ptr->flags1 & SF1_RANDOM) level = 0;
+ else level = rand_range(1, STORE_OBJ_LEVEL);
+
+ if (sti_ptr->flags1 & SF1_DEPEND_LEVEL) level += dun_level;
+
+ if (sti_ptr->flags1 & SF1_SHALLOW_LEVEL) level += 5 + rand_int(5);
+ if (sti_ptr->flags1 & SF1_MEDIUM_LEVEL) level += 25 + rand_int(25);
+ if (sti_ptr->flags1 & SF1_DEEP_LEVEL) level += 45 + rand_int(45);
+
+ if (sti_ptr->flags1 & SF1_ALL_ITEM) level += p_ptr->lev;
+
+ return (level);
+}
+
+/* Is it an ok object ? */
+static int store_tval = 0, store_level = 0;
+
+/*
+ * Hack -- determine if a template is "good"
+ */
+static bool_ kind_is_storeok(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (k_info[k_idx].flags3 & TR3_NORM_ART)
+ return ( FALSE );
+
+ if (k_info[k_idx].flags3 & TR3_INSTA_ART)
+ return ( FALSE );
+
+ if (!kind_is_legal(k_idx)) return FALSE;
+
+ if (k_ptr->tval != store_tval) return (FALSE);
+ if (k_ptr->level < (store_level / 2)) return (FALSE);
+
+ return (TRUE);
+}
+
+/*
+ * Creates a random item and gives it to a store
+ * This algorithm needs to be rethought. A lot.
+ *
+ * Note -- the "level" given to "obj_get_num()" is a "favored"
+ * level, that is, there is a much higher chance of getting
+ * items with a level approaching that of the given level...
+ *
+ * Should we check for "permission" to have the given item?
+ */
+static void store_create(void)
+{
+ int i = 0, tries, level = 0, chance, item;
+
+ object_type forge;
+ object_type *q_ptr = NULL;
+ bool_ obj_all_done = FALSE;
+
+
+ /* Paranoia -- no room left */
+ if (st_ptr->stock_num >= st_ptr->stock_size) return;
+
+
+ /* Hack -- consider up to four items */
+ for (tries = 0; tries < 4; tries++)
+ {
+ obj_all_done = FALSE;
+
+ /* Lua can define things to buy */
+ if (process_hooks_ret(HOOK_STORE_STOCK, "O", "(d,s,d)", st_ptr->st_idx, st_info[st_ptr->st_idx].name + st_name, return_level()))
+ {
+ obj_all_done = TRUE;
+ q_ptr = process_hooks_return[0].o_ptr;
+ }
+
+ /* Black Market */
+ else if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM)
+ {
+ obj_theme theme;
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+ init_match_theme(theme);
+
+ /*
+ * Even in Black Markets, illegal objects can be
+ * problematic -- Oxymoron?
+ */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Rebuild the allocation table */
+ get_obj_num_prep();
+
+ /* Pick a level for object/magic */
+ level = return_level();
+
+ /* Random item (usually of given level) */
+ i = get_obj_num(level);
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+
+ /* Handle failure */
+ if (!i) continue;
+
+ }
+
+ /* Normal Store */
+ else
+ {
+ /* Hack -- Pick an item to sell */
+ item = rand_int(st_info[st_ptr->st_idx].table_num);
+ i = st_info[st_ptr->st_idx].table[item][0];
+ chance = st_info[st_ptr->st_idx].table[item][1];
+
+ /* Don't allow k_info artifacts */
+ if ((i <= 10000) && (k_info[i].flags3 & TR3_NORM_ART))
+ continue;
+
+ /* Does it passes the rarity check ? */
+ if (!magik(chance)) continue;
+
+ /* Hack -- fake level for apply_magic() */
+ level = return_level();
+
+ /* Hack -- i > 10000 means it's a tval and all svals are allowed */
+ if (i > 10000)
+ {
+ obj_theme theme;
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+ init_match_theme(theme);
+
+ /* Activate restriction */
+ get_obj_num_hook = kind_is_storeok;
+ store_tval = i - 10000;
+
+ /* Do we forbid too shallow items ? */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_FORCE_LEVEL) store_level = level;
+ else store_level = 0;
+
+ /* Prepare allocation table */
+ get_obj_num_prep();
+
+ /* Get it ! */
+ i = get_obj_num(level);
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+ }
+
+ if (!i) continue;
+ }
+
+ /* Only if not already done */
+ if (!obj_all_done)
+ {
+ /* Don't allow k_info artifacts */
+ if (k_info[i].flags3 & TR3_NORM_ART)
+ continue;
+
+ /* Don't allow artifacts */
+ if (k_info[i].flags3 & TR3_INSTA_ART)
+ continue;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create a new object of the chosen kind */
+ object_prep(q_ptr, i);
+
+ /* Apply some "low-level" magic (no artifacts) */
+ apply_magic(q_ptr, level, FALSE, FALSE, FALSE);
+
+ /* Hack -- Charge lite's */
+ if (q_ptr->tval == TV_LITE)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_FUEL_LITE) q_ptr->timeout = k_info[q_ptr->k_idx].pval2;
+ }
+
+ }
+
+ /* The item is "known" */
+ object_known(q_ptr);
+
+ /* Mark it storebought */
+ q_ptr->ident |= IDENT_STOREB;
+
+ /* Mega-Hack -- no chests in stores */
+ if (q_ptr->tval == TV_CHEST) continue;
+
+ /* Prune the black market */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM)
+ {
+ /* Hack -- No "crappy" items */
+ if (black_market_crap(q_ptr)) continue;
+
+ /* Hack -- No "cheap" items */
+ if (object_value(q_ptr) < 10) continue;
+ }
+
+ /* Prune normal stores */
+ else
+ {
+ /* No "worthless" items */
+ if (object_value(q_ptr) <= 0) continue;
+ }
+
+
+ /* Mass produce and/or Apply discount */
+ mass_produce(q_ptr);
+
+ /* The charges an wands are per each, so multiply to get correct number */
+ if (!obj_all_done && q_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval *= q_ptr->number;
+ }
+
+ /* Attempt to carry the (known) item */
+ (void)store_carry(q_ptr);
+
+ /* Definitely done */
+ break;
+ }
+}
+
+
+
+/*
+ * Eliminate need to bargain if player has haggled well in the past
+ */
+static bool_ noneedtobargain(s32b minprice)
+{
+ s32b good = st_ptr->good_buy;
+ s32b bad = st_ptr->bad_buy;
+
+ /* Cheap items are "boring" */
+ if (minprice < 10L) return (TRUE);
+
+ /* Perfect haggling */
+ if (good == MAX_SHORT) return (TRUE);
+
+ /* Reward good haggles, punish bad haggles, notice price */
+ if (good > ((3 * bad) + (5 + (minprice / 50)))) return (TRUE);
+
+ /* Return the flag */
+ return (FALSE);
+}
+
+
+/*
+ * Update the bargain info
+ */
+static void updatebargain(s32b price, s32b minprice)
+{
+ /* Hack -- auto-haggle */
+ if (auto_haggle) return;
+
+ /* Cheap items are "boring" */
+ if (minprice < 10L) return;
+
+ /* Count the successful haggles */
+ if (price == minprice)
+ {
+ /* Just count the good haggles */
+ if (st_ptr->good_buy < MAX_SHORT)
+ {
+ st_ptr->good_buy++;
+ }
+ }
+
+ /* Count the failed haggles */
+ else
+ {
+ /* Just count the bad haggles */
+ if (st_ptr->bad_buy < MAX_SHORT)
+ {
+ st_ptr->bad_buy++;
+ }
+ }
+}
+
+
+
+/*
+ * Re-displays a single store entry
+ */
+static void display_entry(int pos)
+{
+ int i, cur_col;
+ object_type *o_ptr;
+ s32b x;
+
+ char o_name[80];
+ char out_val[160];
+
+
+ int maxwid = 75;
+
+ /* Get the item */
+ o_ptr = &st_ptr->stock[pos];
+
+ /* Get the "offset" */
+ i = (pos % 12);
+
+ /* Label it, clear the line --(-- */
+ strnfmt(out_val, 160, "%c) ", I2A(i));
+ c_prt(get_item_letter_color(o_ptr), out_val, i + 6, 0);
+
+
+ cur_col = 3;
+ if (show_store_graph)
+ {
+ byte a = object_attr(o_ptr);
+ char c = object_char(o_ptr);
+
+ if (!o_ptr->k_idx) c = ' ';
+
+ Term_draw(cur_col, i + 6, a, c);
+ if (use_bigtile)
+ {
+ cur_col++;
+ if (a & 0x80)
+ Term_draw(cur_col, i + 6, 255, 255);
+ }
+ cur_col += 2;
+ }
+
+ /* Describe an item in the home */
+ if ((cur_store_num == 7) ||
+ (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM))
+ {
+ maxwid = 75;
+
+ /* Leave room for weights, if necessary -DRS- */
+ if (show_weights) maxwid -= 10;
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+ o_name[maxwid] = '\0';
+ c_put_str(tval_to_attr[o_ptr->tval], o_name, i + 6, cur_col);
+
+ /* Show weights */
+ if (show_weights)
+ {
+ /* Only show the weight of an individual item */
+ int wgt = o_ptr->weight;
+ strnfmt(out_val, 160, "%3d.%d lb", wgt / 10, wgt % 10);
+ put_str(out_val, i + 6, 68);
+ }
+ }
+
+ /* Describe an item (fully) in a store */
+ else
+ {
+ byte color = TERM_WHITE;
+
+ /* Must leave room for the "price" */
+ maxwid = 65;
+
+ /* Leave room for weights, if necessary -DRS- */
+ if (show_weights) maxwid -= 7;
+
+ /* Describe the object (fully) */
+ object_desc_store(o_name, o_ptr, TRUE, 3);
+ o_name[maxwid] = '\0';
+ c_put_str(tval_to_attr[o_ptr->tval], o_name, i + 6, cur_col);
+
+ /* Show weights */
+ if (show_weights)
+ {
+ /* Only show the weight of an individual item */
+ int wgt = o_ptr->weight;
+ strnfmt(out_val, 160, "%3d.%d", wgt / 10, wgt % 10);
+ put_str(out_val, i + 6, 61);
+ }
+
+ /* Display a "fixed" cost */
+ if (o_ptr->ident & (IDENT_FIXED))
+ {
+ /* Extract the "minimum" price */
+ x = price_item(o_ptr, ot_ptr->min_inflate, FALSE);
+
+ /* Can we buy one ? */
+ if (x > p_ptr->au) color = TERM_L_DARK;
+
+ /* Actually draw the price (not fixed) */
+ strnfmt(out_val, 160, "%9ld F", (long)x);
+ c_put_str(color, out_val, i + 6, 68);
+ }
+
+ /* Display a "taxed" cost */
+ else if (auto_haggle)
+ {
+ /* Extract the "minimum" price */
+ x = price_item(o_ptr, ot_ptr->min_inflate, FALSE);
+
+ /* Hack -- Apply Sales Tax if needed */
+ if (!noneedtobargain(x)) x += x / 10;
+
+ /* Can we buy one ? */
+ if (x > p_ptr->au) color = TERM_L_DARK;
+
+ /* Actually draw the price (with tax) */
+ strnfmt(out_val, 160, "%9ld ", (long)x);
+ c_put_str(color, out_val, i + 6, 68);
+ }
+
+ /* Display a "haggle" cost */
+ else
+ {
+ /* Extrect the "maximum" price */
+ x = price_item(o_ptr, ot_ptr->max_inflate, FALSE);
+
+ /* Can we buy one ? */
+ if (x > p_ptr->au) color = TERM_L_DARK;
+
+ /* Actually draw the price (not fixed) */
+ strnfmt(out_val, 160, "%9ld ", (long)x);
+ c_put_str(color, out_val, i + 6, 68);
+ }
+ }
+}
+
+
+/*
+ * Displays a store's inventory -RAK-
+ * All prices are listed as "per individual object". -BEN-
+ */
+static void display_inventory(void)
+{
+ int i, k;
+
+ /* Display the next 12 items */
+ for (k = 0; k < 12; k++)
+ {
+ /* Do not display "dead" items */
+ if (store_top + k >= st_ptr->stock_num) break;
+
+ /* Display that line */
+ display_entry(store_top + k);
+ }
+
+ /* Erase the extra lines and the "more" prompt */
+ for (i = k; i < 13; i++) prt("", i + 6, 0);
+
+ /* Assume "no current page" */
+ put_str(" ", 5, 20);
+
+ /* Visual reminder of "more items" */
+ if (st_ptr->stock_num > 12)
+ {
+ /* Show "more" reminder (after the last item) */
+ prt("-more-", k + 6, 3);
+
+ /* Indicate the "current page" */
+ put_str(format("(Page %d) ", store_top / 12 + 1), 5, 20);
+ }
+}
+
+
+/*
+ * Displays players gold -RAK-
+ */
+void store_prt_gold(void)
+{
+ char out_val[64];
+
+ prt("Gold Remaining: ", 19, 53);
+
+ strnfmt(out_val, 64, "%9ld", (long)p_ptr->au);
+ prt(out_val, 19, 68);
+}
+
+
+/*
+ * Displays store (after clearing screen) -RAK-
+ */
+void display_store(void)
+{
+ char buf[80];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* The "Home" is special */
+ if (cur_store_num == 7)
+ {
+ put_str("Your Home", 3, 30);
+
+ /* Label the item descriptions */
+ put_str("Item Description", 5, 3);
+
+ /* If showing weights, show label */
+ if (show_weights)
+ {
+ put_str("Weight", 5, 70);
+ }
+ }
+
+ else if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM)
+ {
+ cptr store_name = (st_name + st_info[cur_store_num].name);
+
+ /* Show the name of the store */
+ strnfmt(buf, 80, "%s", store_name);
+ prt(buf, 3, 30);
+
+ /* Label the item descriptions */
+ put_str("Item Description", 5, 3);
+
+ /* If showing weights, show label */
+ if (show_weights)
+ {
+ put_str("Weight", 5, 70);
+ }
+ }
+
+ /* Normal stores */
+ else
+ {
+ cptr store_name = (st_name + st_info[cur_store_num].name);
+ cptr owner_name = (ow_name + ot_ptr->name);
+
+ /* Put the owner name and race */
+ strnfmt(buf, 80, "%s", owner_name);
+ put_str(buf, 3, 10);
+
+ /* Show the max price in the store (above prices) */
+ strnfmt(buf, 80, "%s (%ld)", store_name, (long)(ot_ptr->max_cost));
+ prt(buf, 3, 50);
+
+ /* Label the item descriptions */
+ put_str("Item Description", 5, 3);
+
+ /* If showing weights, show label */
+ if (show_weights)
+ {
+ put_str("Weight", 5, 60);
+ }
+
+ /* Label the asking price (in stores) */
+ put_str("Price", 5, 72);
+ }
+
+ /* Display the current gold */
+ store_prt_gold();
+
+ /* Draw in the inventory */
+ display_inventory();
+}
+
+
+
+/*
+ * Get the ID of a store item and return its value -RAK-
+ */
+static int get_stock(int *com_val, cptr pmt, int i, int j)
+{
+ char command;
+
+ char out_val[160];
+
+ /* Get the item index */
+ if (repeat_pull(com_val))
+ {
+
+ /* Verify the item */
+ if ((*com_val >= i) && (*com_val <= j))
+ {
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Assume failure */
+ *com_val = ( -1);
+
+ /* Build the prompt */
+ strnfmt(out_val, 160, "(Items %c-%c, ESC to exit) %s",
+ I2A(i), I2A(j), pmt);
+
+ /* Ask until done */
+ while (TRUE)
+ {
+ int k;
+
+ /* Escape */
+ if (!get_com(out_val, &command)) break;
+
+ /* Convert */
+ k = (islower(command) ? A2I(command) : -1);
+
+ /* Legal responses */
+ if ((k >= i) && (k <= j))
+ {
+ *com_val = k;
+ break;
+ }
+
+ /* Oops */
+ bell();
+ }
+
+ /* Clear the prompt */
+ prt("", 0, 0);
+
+ /* Cancel */
+ if (command == ESCAPE) return (FALSE);
+
+ repeat_push(*com_val);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Increase the insult counter and get angry if too many -RAK-
+ */
+static int increase_insults(void)
+{
+ /* Increase insults */
+ st_ptr->insult_cur++;
+
+ /* Become insulted */
+ if (st_ptr->insult_cur > ot_ptr->insult_max)
+ {
+ /* Complain */
+ say_comment_4();
+
+ /* Reset insults */
+ st_ptr->insult_cur = 0;
+ st_ptr->good_buy = 0;
+ st_ptr->bad_buy = 0;
+
+ /* Open tomorrow */
+ st_ptr->store_open = turn + 25000 + randint(25000);
+
+ /* Closed */
+ return (TRUE);
+ }
+
+ /* Not closed */
+ return (FALSE);
+}
+
+
+/*
+ * Decrease insults -RAK-
+ */
+static void decrease_insults(void)
+{
+ /* Decrease insults */
+ if (st_ptr->insult_cur) st_ptr->insult_cur--;
+}
+
+
+/*
+ * Have insulted while haggling -RAK-
+ */
+static int haggle_insults(void)
+{
+ /* Increase insults */
+ if (increase_insults()) return (TRUE);
+
+ /* Display and flush insult */
+ say_comment_5();
+
+ /* Still okay */
+ return (FALSE);
+}
+
+
+/*
+ * Mega-Hack -- Enable "increments"
+ */
+static bool_ allow_inc = FALSE;
+
+/*
+ * Mega-Hack -- Last "increment" during haggling
+ */
+static s32b last_inc = 0L;
+
+
+/*
+ * Get a haggle
+ */
+static int get_haggle(cptr pmt, s32b *poffer, s32b price, int final)
+{
+ s32b i;
+
+ cptr p;
+
+ char buf[128];
+ char out_val[160];
+
+
+ /* Clear old increment if necessary */
+ if (!allow_inc) last_inc = 0L;
+
+
+ /* Final offer */
+ if (final)
+ {
+ strnfmt(buf, 128, "%s [accept] ", pmt);
+ }
+
+ /* Old (negative) increment, and not final */
+ else if (last_inc < 0)
+ {
+ strnfmt(buf, 128, "%s [-%ld] ", pmt, (long)(ABS(last_inc)));
+ }
+
+ /* Old (positive) increment, and not final */
+ else if (last_inc > 0)
+ {
+ strnfmt(buf, 128, "%s [+%ld] ", pmt, (long)(ABS(last_inc)));
+ }
+
+ /* Normal haggle */
+ else
+ {
+ strnfmt(buf, 128, "%s ", pmt);
+ }
+
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Ask until done */
+ while (TRUE)
+ {
+ /* Default */
+ strcpy(out_val, "");
+
+ /* Ask the user for a response */
+ if (!get_string(buf, out_val, 32)) return (FALSE);
+
+ /* Skip leading spaces */
+ for (p = out_val; *p == ' '; p++) /* loop */;
+
+ /* Empty response */
+ if (*p == '\0')
+ {
+ /* Accept current price */
+ if (final)
+ {
+ *poffer = price;
+ last_inc = 0L;
+ break;
+ }
+
+ /* Use previous increment */
+ if (allow_inc && last_inc)
+ {
+ *poffer += last_inc;
+ break;
+ }
+ }
+
+ /* Normal response */
+ else
+ {
+ /* Extract a number */
+ i = atol(p);
+
+ /* Handle "incremental" number */
+ if ((*p == '+' || *p == '-'))
+ {
+ /* Allow increments */
+ if (allow_inc)
+ {
+ /* Use the given "increment" */
+ *poffer += i;
+ last_inc = i;
+ break;
+ }
+ }
+
+ /* Handle normal number */
+ else
+ {
+ /* Use the given "number" */
+ *poffer = i;
+ last_inc = 0L;
+ break;
+ }
+ }
+
+ /* Warning */
+ msg_print("Invalid response.");
+ msg_print(NULL);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Receive an offer (from the player)
+ *
+ * Return TRUE if offer is NOT okay
+ */
+static bool_ receive_offer(cptr pmt, s32b *poffer,
+ s32b last_offer, int factor,
+ s32b price, int final)
+{
+ /* Haggle till done */
+ while (TRUE)
+ {
+ /* Get a haggle (or cancel) */
+ if (!get_haggle(pmt, poffer, price, final)) return (TRUE);
+
+ /* Acceptable offer */
+ if (((*poffer) * factor) >= (last_offer * factor)) break;
+
+ /* Insult, and check for kicked out */
+ if (haggle_insults()) return (TRUE);
+
+ /* Reject offer (correctly) */
+ (*poffer) = last_offer;
+ }
+
+ /* Success */
+ return (FALSE);
+}
+
+
+/*
+ * Haggling routine -RAK-
+ *
+ * Return TRUE if purchase is NOT successful
+ */
+static bool_ purchase_haggle(object_type *o_ptr, s32b *price)
+{
+ s32b cur_ask, final_ask;
+ s32b last_offer, offer;
+ s32b x1, x2, x3;
+ s32b min_per, max_per;
+ int flag, loop_flag, noneed;
+ int annoyed = 0, final = FALSE;
+
+ bool_ cancel = FALSE;
+
+ cptr pmt = "Asking";
+
+ char out_val[160];
+
+
+ *price = 0;
+
+
+ /* Extract the starting offer and the final offer */
+ cur_ask = price_item(o_ptr, ot_ptr->max_inflate, FALSE);
+ final_ask = price_item(o_ptr, ot_ptr->min_inflate, FALSE);
+
+ /* Determine if haggling is necessary */
+ noneed = noneedtobargain(final_ask);
+
+ /* No need to haggle */
+ if (noneed || auto_haggle)
+ {
+ /* No need to haggle */
+ if (noneed)
+ {
+ /* Message summary */
+ msg_print("You eventually agree upon the price.");
+ msg_print(NULL);
+ }
+
+ /* No haggle option */
+ else
+ {
+ /* Message summary */
+ msg_print("You quickly agree upon the price.");
+ msg_print(NULL);
+
+ /* Apply Sales Tax */
+ final_ask += final_ask / 10;
+ }
+
+ /* Final price */
+ cur_ask = final_ask;
+
+ /* Go to final offer */
+ pmt = "Final Offer";
+ final = TRUE;
+ }
+
+
+ /* Haggle for the whole pile */
+ cur_ask *= o_ptr->number;
+ final_ask *= o_ptr->number;
+
+
+ /* Haggle parameters */
+ min_per = ot_ptr->haggle_per;
+ max_per = min_per * 3;
+
+ /* Mega-Hack -- artificial "last offer" value */
+ last_offer = object_value(o_ptr) * o_ptr->number;
+ last_offer = last_offer * (200 - (int)(ot_ptr->max_inflate)) / 100L;
+ if (last_offer <= 0) last_offer = 1;
+
+ /* No offer yet */
+ offer = 0;
+
+ /* No incremental haggling yet */
+ allow_inc = FALSE;
+
+ /* Haggle until done */
+ for (flag = FALSE; !flag; )
+ {
+ loop_flag = TRUE;
+
+ while (!flag && loop_flag)
+ {
+ strnfmt(out_val, 160, "%s : %ld", pmt, (long)cur_ask);
+ put_str(out_val, 1, 0);
+ cancel = receive_offer("What do you offer? ",
+ &offer, last_offer, 1, cur_ask, final);
+
+ if (cancel)
+ {
+ flag = TRUE;
+ }
+ else if (offer > cur_ask)
+ {
+ say_comment_6();
+ offer = last_offer;
+ }
+ else if (offer == cur_ask)
+ {
+ flag = TRUE;
+ *price = offer;
+ }
+ else
+ {
+ loop_flag = FALSE;
+ }
+ }
+
+ if (!flag)
+ {
+ x1 = 100 * (offer - last_offer) / (cur_ask - last_offer);
+ if (x1 < min_per)
+ {
+ if (haggle_insults())
+ {
+ flag = TRUE;
+ cancel = TRUE;
+ }
+ }
+ else if (x1 > max_per)
+ {
+ x1 = x1 * 3 / 4;
+ if (x1 < max_per) x1 = max_per;
+ }
+ x2 = rand_range(x1 - 2, x1 + 2);
+ x3 = ((cur_ask - offer) * x2 / 100L) + 1;
+ /* don't let the price go up */
+ if (x3 < 0) x3 = 0;
+ cur_ask -= x3;
+
+ /* Too little */
+ if (cur_ask < final_ask)
+ {
+ final = TRUE;
+ cur_ask = final_ask;
+ pmt = "Final Offer";
+ annoyed++;
+ if (annoyed > 3)
+ {
+ (void)(increase_insults());
+ cancel = TRUE;
+ flag = TRUE;
+ }
+ }
+ else if (offer >= cur_ask)
+ {
+ flag = TRUE;
+ *price = offer;
+ }
+
+ if (!flag)
+ {
+ last_offer = offer;
+ allow_inc = TRUE;
+ prt("", 1, 0);
+ strnfmt(out_val, 160, "Your last offer: %ld",
+ (long)last_offer);
+ put_str(out_val, 1, 39);
+ say_comment_2(cur_ask, annoyed);
+ }
+ }
+ }
+
+ /* Cancel */
+ if (cancel) return (TRUE);
+
+ /* Update bargaining info */
+ updatebargain(*price, final_ask);
+
+ /* Do not cancel */
+ return (FALSE);
+}
+
+
+/*
+ * Haggling routine -RAK-
+ *
+ * Return TRUE if purchase is NOT successful
+ */
+static bool_ sell_haggle(object_type *o_ptr, s32b *price)
+{
+ s32b purse, cur_ask, final_ask;
+ s32b last_offer = 0, offer = 0;
+ s32b x1, x2, x3;
+ s32b min_per, max_per;
+
+ int flag, loop_flag, noneed;
+ int annoyed = 0, final = FALSE;
+
+ bool_ cancel = FALSE;
+
+ cptr pmt = "Offer";
+
+ char out_val[160];
+
+
+ *price = 0;
+
+
+ /* Obtain the starting offer and the final offer */
+ cur_ask = price_item(o_ptr, ot_ptr->max_inflate, TRUE);
+ final_ask = price_item(o_ptr, ot_ptr->min_inflate, TRUE);
+
+ /* Determine if haggling is necessary */
+ noneed = noneedtobargain(final_ask);
+
+ /* Get the owner's payout limit */
+ purse = (s32b)(ot_ptr->max_cost);
+
+ /* No need to haggle */
+ if (noneed || auto_haggle || (final_ask >= purse))
+ {
+ /* No reason to haggle */
+ if (final_ask >= purse)
+ {
+ /* Message */
+ msg_print("You instantly agree upon the price.");
+ msg_print(NULL);
+
+ /* Offer full purse */
+ final_ask = purse;
+ }
+
+ /* No need to haggle */
+ else if (noneed)
+ {
+ /* Message */
+ msg_print("You eventually agree upon the price.");
+ msg_print(NULL);
+ }
+
+ /* No haggle option */
+ else
+ {
+ /* Message summary */
+ msg_print("You quickly agree upon the price.");
+ msg_print(NULL);
+
+ /* Apply Sales Tax */
+ final_ask -= final_ask / 10;
+ }
+
+ /* Final price */
+ cur_ask = final_ask;
+
+ /* Final offer */
+ final = TRUE;
+ pmt = "Final Offer";
+ }
+
+ /* Haggle for the whole pile */
+ cur_ask *= o_ptr->number;
+ final_ask *= o_ptr->number;
+
+
+ /* XXX XXX XXX Display commands */
+
+ /* Haggling parameters */
+ min_per = ot_ptr->haggle_per;
+ max_per = min_per * 3;
+
+ /* Mega-Hack -- artificial "last offer" value */
+ last_offer = object_value(o_ptr) * o_ptr->number;
+ last_offer = last_offer * ot_ptr->max_inflate / 100L;
+
+ /* No offer yet */
+ offer = 0;
+
+ /* No incremental haggling yet */
+ allow_inc = FALSE;
+
+ /* Haggle */
+ for (flag = FALSE; !flag; )
+ {
+ while (1)
+ {
+ loop_flag = TRUE;
+
+ strnfmt(out_val, 160, "%s : %ld", pmt, (long)cur_ask);
+ put_str(out_val, 1, 0);
+ cancel = receive_offer("What price do you ask? ",
+ &offer, last_offer, -1, cur_ask, final);
+
+ if (cancel)
+ {
+ flag = TRUE;
+ }
+ else if (offer < cur_ask)
+ {
+ say_comment_6();
+ /* rejected, reset offer for incremental haggling */
+ offer = last_offer;
+ }
+ else if (offer == cur_ask)
+ {
+ flag = TRUE;
+ *price = offer;
+ }
+ else
+ {
+ loop_flag = FALSE;
+ }
+
+ /* Stop */
+ if (flag || !loop_flag) break;
+ }
+
+ if (!flag)
+ {
+ x1 = 100 * (last_offer - offer) / (last_offer - cur_ask);
+ if (x1 < min_per)
+ {
+ if (haggle_insults())
+ {
+ flag = TRUE;
+ cancel = TRUE;
+ }
+ }
+ else if (x1 > max_per)
+ {
+ x1 = x1 * 3 / 4;
+ if (x1 < max_per) x1 = max_per;
+ }
+ x2 = rand_range(x1 - 2, x1 + 2);
+ x3 = ((offer - cur_ask) * x2 / 100L) + 1;
+ /* don't let the price go down */
+ if (x3 < 0) x3 = 0;
+ cur_ask += x3;
+
+ if (cur_ask > final_ask)
+ {
+ cur_ask = final_ask;
+ final = TRUE;
+ pmt = "Final Offer";
+ annoyed++;
+ if (annoyed > 3)
+ {
+ flag = TRUE;
+ (void)(increase_insults());
+ }
+ }
+ else if (offer <= cur_ask)
+ {
+ flag = TRUE;
+ *price = offer;
+ }
+
+ if (!flag)
+ {
+ last_offer = offer;
+ allow_inc = TRUE;
+ prt("", 1, 0);
+ strnfmt(out_val, 160,
+ "Your last bid %ld", (long)last_offer);
+ put_str(out_val, 1, 39);
+ say_comment_3(cur_ask, annoyed);
+ }
+ }
+ }
+
+ /* Cancel */
+ if (cancel) return (TRUE);
+
+ /* Update bargaining info */
+ updatebargain(*price, final_ask);
+
+ /* Do not cancel */
+ return (FALSE);
+}
+
+/*
+ * Will the owner retire?
+ */
+static bool_ retire_owner_p(void)
+{
+ store_info_type *sti_ptr = &st_info[town_info[p_ptr->town_num].store[cur_store_num].st_idx];
+
+ if ((sti_ptr->owners[0] == sti_ptr->owners[1]) &&
+ (sti_ptr->owners[0] == sti_ptr->owners[2]) &&
+ (sti_ptr->owners[0] == sti_ptr->owners[3]))
+ {
+ /* there is no other owner */
+ return FALSE;
+ }
+
+ if (rand_int(STORE_SHUFFLE) != 0)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Stole an item from a store -DG-
+ */
+void store_stole(void)
+{
+ int i, amt;
+ int item, item_new;
+
+ object_type forge;
+ object_type *j_ptr;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[160];
+
+ if (cur_store_num == 7)
+ {
+ msg_print("You can't steal from your home!");
+ return;
+ }
+
+ /* Empty? */
+ if (st_ptr->stock_num <= 0)
+ {
+ msg_print("There is no item to steal.");
+ return;
+ }
+
+
+ /* Find the number of objects on this and following pages */
+ i = (st_ptr->stock_num - store_top);
+
+ /* And then restrict it to the current page */
+ if (i > 12) i = 12;
+
+ /* Prompt */
+ strnfmt(out_val, 160, "Which item do you want to steal? ");
+
+ /* Get the item number to be bought */
+ if (!get_stock(&item, out_val, 0, i - 1)) return;
+
+ /* Get the actual index */
+ item = item + store_top;
+
+ /* Get the actual item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Assume the player wants just one of them */
+ amt = 1;
+
+ /* Get local object */
+ j_ptr = &forge;
+
+ /* Get a copy of the object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify quantity */
+ j_ptr->number = amt;
+
+ /* Hack -- require room in pack */
+ if (!inven_carry_okay(j_ptr))
+ {
+ msg_print("You cannot carry that many different items.");
+ return;
+ }
+
+ /* Find out how many the player wants */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ /* Get local object */
+ j_ptr = &forge;
+
+ /* Get desired object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify quantity */
+ j_ptr->number = amt;
+
+ /* Hack -- require room in pack */
+ if (!inven_carry_okay(j_ptr))
+ {
+ msg_print("You cannot carry that many items.");
+ return;
+ }
+
+ /* Player tries to stole it */
+ if (rand_int((40 - p_ptr->stat_ind[A_DEX]) +
+ ((j_ptr->weight * amt) / (5 + get_skill_scale(SKILL_STEALING, 15))) -
+ (get_skill_scale(SKILL_STEALING, 15))) <= 10)
+ {
+ /* Hack -- buying an item makes you aware of it */
+ object_aware(j_ptr);
+
+ /* Be aware of how you found it */
+ j_ptr->found = OBJ_FOUND_STOLEN;
+ j_ptr->found_aux1 = st_ptr->st_idx;
+
+ /* Hack -- clear the "fixed" flag from the item */
+ j_ptr->ident &= ~(IDENT_FIXED);
+
+ /* "Hot" merchandise can't be sold back. It doesn't make sense
+ to be able to sell back to a guy what you just stole from him.
+ Also, without the discount one could fairly easily macro himself
+ an infinite money supply */
+ j_ptr->discount = 100;
+
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ o_ptr->pval -= j_ptr->pval;
+ }
+
+ /* Describe the transaction */
+ object_desc(o_name, j_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You steal %s.", o_name);
+
+ /* Erase the inscription */
+ j_ptr->note = 0;
+
+ /* Give it to the player */
+ item_new = inven_carry(j_ptr, FALSE);
+
+ /* Describe the final result */
+ object_desc(o_name, &p_ptr->inventory[item_new], TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).",
+ o_name, index_to_label(item_new));
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Note how many slots the store used to have */
+ i = st_ptr->stock_num;
+
+ /* Remove the bought items from the store */
+ store_item_increase(item, -amt);
+ store_item_optimize(item);
+
+ /* Store is empty */
+ if (st_ptr->stock_num == 0)
+ {
+ /* Shuffle */
+ if (retire_owner_p())
+ {
+ /* Message */
+ msg_print("The shopkeeper retires.");
+
+ /* Shuffle the store */
+ store_shuffle(cur_store_num);
+ }
+
+ /* Maintain */
+ else
+ {
+ /* Message */
+ msg_print("The shopkeeper brings out some new stock.");
+ }
+
+ /* New inventory */
+ for (i = 0; i < 10; i++)
+ {
+ /* Maintain the store */
+ store_maint(p_ptr->town_num, cur_store_num);
+ }
+
+ /* Start over */
+ store_top = 0;
+
+ /* Redraw everything */
+ display_inventory();
+ }
+
+ /* The item is gone */
+ else if (st_ptr->stock_num != i)
+ {
+ /* Pick the correct screen */
+ if (store_top >= st_ptr->stock_num) store_top -= 12;
+
+ /* Redraw everything */
+ display_inventory();
+ }
+
+ /* Item is still here */
+ else
+ {
+ /* Redraw the item */
+ display_entry(item);
+ }
+ }
+ else
+ {
+ /* Complain */
+ say_comment_4();
+
+ /* Reset insults */
+ st_ptr->insult_cur = 0;
+ st_ptr->good_buy = 0;
+ st_ptr->bad_buy = 0;
+
+ /* Kicked out for a LONG time */
+ st_ptr->store_open = turn + 500000 + randint(500000);
+ }
+
+ /* Not kicked out */
+ return;
+}
+
+/*
+ * Buy an item from a store -RAK-
+ */
+void store_purchase(void)
+{
+ int i, amt = 1, choice;
+ int item, item_new;
+
+ s32b price, best;
+
+ object_type forge;
+ object_type *j_ptr;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[160];
+
+ /* Museum? */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM)
+ {
+ msg_print("You cannot take items from the museum!");
+ return;
+ }
+
+ /* Empty? */
+ if (st_ptr->stock_num <= 0)
+ {
+ if (cur_store_num == 7) msg_print("Your home is empty.");
+ else msg_print("I am currently out of stock.");
+ return;
+ }
+
+
+ /* Find the number of objects on this and following pages */
+ i = (st_ptr->stock_num - store_top);
+
+ /* And then restrict it to the current page */
+ if (i > 12) i = 12;
+
+ /* Prompt */
+ if (cur_store_num == 7)
+ {
+ strnfmt(out_val, 160, "Which item do you want to take? ");
+ }
+ else
+ {
+ strnfmt(out_val, 160, "Which item are you interested in? ");
+ }
+
+ /* Get the item number to be bought */
+ if (!get_stock(&item, out_val, 0, i - 1)) return;
+
+ /* Get the actual index */
+ item = item + store_top;
+
+ /* Get the actual item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Get local object */
+ j_ptr = &forge;
+
+ /* Get a copy of one object to determine the price */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify quantity */
+ j_ptr->number = 1;
+
+ /* Hack -- If a wand, allocate the number of charges of one wand */
+ if (j_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval / o_ptr->number;
+ }
+
+ /* Hack -- require room in pack */
+ if (!inven_carry_okay(j_ptr))
+ {
+ msg_print("You cannot carry that many different items.");
+ return;
+ }
+
+ /* Determine the "best" price (per item) */
+ best = price_item(j_ptr, ot_ptr->min_inflate, FALSE);
+
+ /* Find out how many the player wants */
+ if (o_ptr->number > 1)
+ {
+ s32b q;
+
+
+ /* Hack -- note cost of "fixed" items */
+ if ((cur_store_num != 7) && (o_ptr->ident & (IDENT_FIXED)))
+ {
+ msg_format("That costs %ld gold per item.", (long)(best));
+ }
+
+ /* How many can we buy ? 99 if price is 0*/
+ if (cur_store_num == STORE_HOME)
+ {
+ q = 99;
+ }
+ else if (best == 0)
+ {
+ q = 99;
+ }
+ else
+ {
+ if (auto_haggle)
+ q = p_ptr->au / (best + (best / 10));
+ else
+ q = p_ptr->au / best;
+ }
+ if (o_ptr->number < q)
+ q = o_ptr->number;
+
+ /* None ? ahh too bad */
+ if (!q)
+ {
+ msg_print("You do not have enough gold to buy one.");
+ return;
+ }
+
+ /* Get a quantity */
+ amt = get_quantity(NULL, q);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ /* Get local object */
+ j_ptr = &forge;
+
+ /* Get desired object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify quantity */
+ j_ptr->number = amt;
+
+ /* Hack -- If a rod or wand, allocate total maximum timeouts or charges
+ * between those purchased and left on the shelf. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Hack -- require room in pack */
+ if (!inven_carry_okay(j_ptr))
+ {
+ msg_print("You cannot carry that many items.");
+ return;
+ }
+
+ /* Attempt to buy it */
+ if (cur_store_num != 7)
+ {
+ /* Fixed price, quick buy */
+ if (o_ptr->ident & (IDENT_FIXED))
+ {
+ /* Assume accept */
+ choice = 0;
+
+ /* Go directly to the "best" deal */
+ price = (best * j_ptr->number);
+ }
+
+ /* Haggle for it */
+ else
+ {
+ /* Describe the object (fully) */
+ object_desc_store(o_name, j_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("Buying %s (%c).", o_name, I2A(item));
+ msg_print(NULL);
+
+ /* Haggle for a final price */
+ choice = purchase_haggle(j_ptr, &price);
+
+ /* Hack -- Got kicked out */
+ if (st_ptr->store_open >= turn) return;
+ }
+
+
+ /* Player wants it */
+ if (choice == 0)
+ {
+ /* Fix the item price (if "correctly" haggled) */
+ if (price == (best * j_ptr->number)) o_ptr->ident |= (IDENT_FIXED);
+
+ /* Player can afford it */
+ if (p_ptr->au >= price)
+ {
+ /* Say "okay" */
+ say_comment_1();
+
+ /* Make a sound */
+ sound(SOUND_BUY);
+
+ /* Be happy */
+ decrease_insults();
+
+ /* Spend the money */
+ p_ptr->au -= price;
+
+ /* Update the display */
+ store_prt_gold();
+
+ /* Hack -- buying an item makes you aware of it */
+ object_aware(j_ptr);
+
+ /* Be aware of how you found it */
+ j_ptr->found = OBJ_FOUND_STORE;
+ j_ptr->found_aux1 = st_ptr->st_idx;
+
+ /* Hack -- clear the "fixed" flag from the item */
+ j_ptr->ident &= ~(IDENT_FIXED);
+
+ /* Describe the transaction */
+ object_desc(o_name, j_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You bought %s for %ld gold.", o_name, (long)price);
+
+ /* Erase the inscription */
+ j_ptr->note = 0;
+
+ /* Hack -- If a rod or wand, allocate total maximum
+ * timeouts or charges between those picked up and
+ * those left behind. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ o_ptr->pval -= j_ptr->pval;
+ }
+
+ /* Give it to the player */
+ item_new = inven_carry(j_ptr, FALSE);
+
+ /* Describe the final result */
+ object_desc(o_name, &p_ptr->inventory[item_new], TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).",
+ o_name, index_to_label(item_new));
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Note how many slots the store used to have */
+ i = st_ptr->stock_num;
+
+ /* Remove the bought items from the store */
+ store_item_increase(item, -amt);
+ store_item_optimize(item);
+
+ /* Store is empty */
+ if (st_ptr->stock_num == 0)
+ {
+ /* Shuffle */
+ if (retire_owner_p())
+ {
+ /* Message */
+ msg_print("The shopkeeper retires.");
+
+ /* Shuffle the store */
+ store_shuffle(cur_store_num);
+ }
+
+ /* Maintain */
+ else
+ {
+ /* Message */
+ msg_print("The shopkeeper brings out some new stock.");
+ }
+
+ /* New inventory */
+ for (i = 0; i < 10; i++)
+ {
+ /* Maintain the store */
+ store_maint(p_ptr->town_num, cur_store_num);
+ }
+
+ /* Start over */
+ store_top = 0;
+ }
+
+ /* The item is gone */
+ else if (st_ptr->stock_num != i)
+ {
+ /* Pick the correct screen */
+ if (store_top >= st_ptr->stock_num) store_top -= 12;
+ }
+
+ /* Redraw everything */
+ display_inventory();
+ }
+
+ /* Player cannot afford it */
+ else
+ {
+ /* Simple message (no insult) */
+ msg_print("You do not have enough gold.");
+ }
+ }
+ }
+
+ /* Home is much easier */
+ else
+ {
+ /* Hack -- If a rod or wand, allocate total maximum
+ * timeouts or charges between those picked up and
+ * those left behind. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ o_ptr->pval -= j_ptr->pval;
+ }
+
+ /* Give it to the player */
+ item_new = inven_carry(j_ptr, FALSE);
+
+ /* Describe just the result */
+ object_desc(o_name, &p_ptr->inventory[item_new], TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).", o_name, index_to_label(item_new));
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Take note if we take the last one */
+ i = st_ptr->stock_num;
+
+ /* Remove the items from the home */
+ store_item_increase(item, -amt);
+ store_item_optimize(item);
+
+ /* Hack -- Item is still here */
+ if (i == st_ptr->stock_num)
+ {
+ /* Redraw the item */
+ display_entry(item);
+ }
+
+ /* The item is gone */
+ else
+ {
+ /* Nothing left */
+ if (st_ptr->stock_num == 0) store_top = 0;
+
+ /* Nothing left on that screen */
+ else if (store_top >= st_ptr->stock_num) store_top -= 12;
+
+ /* Redraw everything */
+ display_inventory();
+ }
+ }
+
+ /* Not kicked out */
+ return;
+}
+
+
+/*
+ * Sell an item to the store (or home)
+ */
+void store_sell(void)
+{
+ int choice;
+ int item, item_pos;
+ int amt;
+
+ s32b price, value, dummy;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ char o_name[80];
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ bool_ museum = (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) ? TRUE : FALSE;
+
+ /* Prepare a prompt */
+ if (cur_store_num == 7) q = "Drop which item? ";
+ else if (museum) q = "Donate which item?";
+ else q = "Sell which item? ";
+
+ /* Only allow items the store will buy */
+ item_tester_hook = store_will_buy;
+
+ /* Get an item */
+ if (cur_store_num == STORE_HOME)
+ {
+ s = "You have nothing to drop.";
+ }
+ else if (museum)
+ {
+ s = "You have nothing to donate.";
+ }
+ else
+ {
+ s = "You have nothing that I want.";
+ }
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- Cannot remove cursed items */
+ if (cursed_p(o_ptr))
+ {
+ if (item >= INVEN_WIELD)
+ {
+ /* Oops */
+ msg_print("Hmmm, it seems to be cursed.");
+
+ /* Nope */
+ return;
+ }
+ else
+ {
+ if (f4 & TR4_CURSE_NO_DROP)
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to drop it.");
+
+ /* Nope */
+ return;
+ }
+ }
+ }
+
+
+ /* Assume one item */
+ amt = 1;
+
+ /* Find out how many the player wants (letter means "all") */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Get a copy of the object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /* Hack -- If a rod or wand, allocate total maximum
+ * timeouts or charges to those being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Get a full description */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Remove any inscription for stores */
+ if ((cur_store_num != 7) && !museum)
+ {
+ q_ptr->note = 0;
+ }
+
+ /* Is there room in the store (or the home?) */
+ if (!store_check_num(q_ptr))
+ {
+ if (cur_store_num == 7) msg_print("Your home is full.");
+ else if (museum) msg_print("The museum is full.");
+ else msg_print("I have not the room in my store to keep it.");
+ return;
+ }
+
+
+ /* Real store */
+ if ((cur_store_num != 7) && !museum)
+ {
+ /* Describe the transaction */
+ msg_format("Selling %s (%c).", o_name, index_to_label(item));
+ msg_print(NULL);
+
+ /* Haggle for it */
+ choice = sell_haggle(q_ptr, &price);
+
+ /* Kicked out */
+ if (st_ptr->store_open >= turn) return;
+
+ /* Sold... */
+ if (choice == 0)
+ {
+ /* Say "okay" */
+ say_comment_1();
+
+ /* Make a sound */
+ sound(SOUND_SELL);
+
+ /* Be happy */
+ decrease_insults();
+
+ /* Get some money */
+ p_ptr->au += price;
+
+ /* Update the display */
+ store_prt_gold();
+
+ /* Get the "apparent" value */
+ dummy = object_value(q_ptr) * q_ptr->number;
+
+ /* Identify original item */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Get a copy of the object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /*
+ * Hack -- If a rod or wand, let the shopkeeper know just
+ * how many charges he really paid for. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Get the "actual" value */
+ value = object_value(q_ptr) * q_ptr->number;
+
+ /* Get the description all over again */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Describe the result (in message buffer) */
+ msg_format("You sold %s for %ld gold.", o_name, (long)price);
+
+ /* Analyze the prices (and comment verbally) */
+ purchase_analyze(price, value, dummy);
+
+ /*
+ * Hack -- Allocate charges between those wands or rods sold
+ * and retained, unless all are being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+
+ if (o_ptr->number > amt) o_ptr->pval -= q_ptr->pval;
+ }
+
+ /* Take the item from the player, describe the result */
+ inc_stack_size(item, -amt);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* The store gets that (known) item */
+ item_pos = store_carry(q_ptr);
+
+ /* Re-display if item is now in store */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+ }
+
+ /* Player is at museum */
+ else if (museum)
+ {
+ char o2_name[80];
+ object_desc(o2_name, q_ptr, TRUE, 0);
+
+ msg_print("Once you donate something, you cannot take it back.");
+ if (!get_check(format("Do you really want to donate %s?", o2_name))) return;
+
+ /* Identify it */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+
+ /*
+ * Hack -- Allocate charges between those wands or rods sold
+ * and retained, unless all are being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+
+ if (o_ptr->number > amt) o_ptr->pval -= q_ptr->pval;
+ }
+
+
+ /* Describe */
+ msg_format("You donate %s (%c).", o_name, index_to_label(item));
+
+ choice = 0;
+
+ /* Take it from the players inventory */
+ inc_stack_size(item, -amt);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Update store display */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+
+ /* Player is at home */
+ else
+ {
+ /* Describe */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /*
+ * Hack -- Allocate charges between those wands or rods sold
+ * and retained, unless all are being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+
+ if (o_ptr->number > amt) o_ptr->pval -= q_ptr->pval;
+ }
+
+ /* Take it from the players inventory */
+ inc_stack_size(item, -amt);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Update store display */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+}
+
+
+
+/*
+ * Examine an item in a store -JDL-
+ */
+void store_examine(void)
+{
+ int i;
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[160];
+
+
+ /* Empty? */
+ if (st_ptr->stock_num <= 0)
+ {
+ if (cur_store_num == 7) msg_print("Your home is empty.");
+ else if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) msg_print("The museum is empty.");
+ else msg_print("I am currently out of stock.");
+ return;
+ }
+
+
+ /* Find the number of objects on this and following pages */
+ i = (st_ptr->stock_num - store_top);
+
+ /* And then restrict it to the current page */
+ if (i > 12) i = 12;
+
+ /* Prompt */
+ strnfmt(out_val, 160, "Which item do you want to examine? ");
+
+ /* Get the item number to be examined */
+ if (!get_stock(&item, out_val, 0, i - 1)) return;
+
+ /* Get the actual index */
+ item = item + store_top;
+
+ /* Get the actual item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Debug hack */
+ if (wizard)
+ {
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+ }
+
+ /* Require full knowledge */
+ if (!(o_ptr->ident & (IDENT_MENTAL)))
+ {
+ /* This can only happen in the home */
+ msg_print("You have no special knowledge about that item.");
+ return;
+ }
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe */
+ msg_format("Examining %s...", o_name);
+
+ /* Show the object's powers. */
+ if (!object_out_desc(o_ptr, NULL, FALSE, TRUE))
+ {
+ msg_print("You see nothing special.");
+ }
+
+ /* Show spell listing for instruments, daemonwear and spellbooks. */
+ if ((o_ptr->tval == TV_INSTRUMENT) || (o_ptr->tval == TV_DAEMON_BOOK)
+ || (o_ptr->tval == TV_BOOK))
+ {
+ do_cmd_browse_aux(o_ptr);
+ }
+
+ return;
+}
+
+
+
+
+
+/*
+ * Hack -- set this to leave the store
+ */
+static bool_ leave_store = FALSE;
+
+
+/*
+ * Process a command in a store
+ *
+ * Note that we must allow the use of a few "special" commands
+ * in the stores which are not allowed in the dungeon, and we
+ * must disable some commands which are allowed in the dungeon
+ * but not in the stores, to prevent chaos.
+ */
+static bool_ store_process_command(void)
+{
+ bool_ validcmd = FALSE;
+ int i;
+ store_action_type *ba_ptr;
+ bool_ recreate = FALSE;
+
+ /* Handle repeating the last command */
+ repeat_check();
+
+ for (i = 0; i < 6; i++)
+ {
+ ba_ptr = &ba_info[st_info[st_ptr->st_idx].actions[i]];
+
+ if (ba_ptr->letter)
+ {
+ if (ba_ptr->letter == command_cmd)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ if (ba_ptr->letter_aux)
+ {
+ if (ba_ptr->letter_aux == command_cmd)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (validcmd)
+ {
+ recreate = bldg_process_command(st_ptr, i);
+ }
+ else
+ {
+ /* Parse the command */
+ switch (command_cmd)
+ {
+ /* Leave */
+ case ESCAPE:
+ {
+ leave_store = TRUE;
+ break;
+ }
+
+ /* Browse */
+ case ' ':
+ {
+ if (st_ptr->stock_num <= 12)
+ {
+ msg_print("Entire inventory is shown.");
+ }
+ else
+ {
+ store_top += 12;
+ if (store_top >= st_ptr->stock_num) store_top = 0;
+ display_inventory();
+ }
+ break;
+ }
+
+ /* Browse backwards */
+ case '-':
+ {
+ if (st_ptr->stock_num <= 12)
+ {
+ msg_print("Entire inventory is shown.");
+ }
+ else
+ {
+ store_top -= 12;
+ if (store_top < 0)
+ {
+ store_top = ((st_ptr->stock_num - 1) / 12) * 12;
+ }
+ display_inventory();
+ }
+ }
+
+ /* Redraw */
+ case KTRL('R'):
+ {
+ do_cmd_redraw();
+ display_store();
+ break;
+ }
+
+ /* Ignore return */
+ case '\r':
+ {
+ break;
+ }
+
+
+
+ /*** Inventory Commands ***/
+
+ /* Wear/wield equipment */
+ case 'w':
+ {
+ do_cmd_wield();
+ break;
+ }
+
+ /* Take off equipment */
+ case 't':
+ {
+ do_cmd_takeoff();
+ break;
+ }
+
+ /* Destroy an item */
+ case 'k':
+ {
+ do_cmd_destroy();
+ break;
+ }
+
+ /* Equipment list */
+ case 'e':
+ {
+ do_cmd_equip();
+ break;
+ }
+
+ /* Inventory list */
+ case 'i':
+ {
+ do_cmd_inven();
+ break;
+ }
+
+
+ /*** Various commands ***/
+
+ /* Identify an object */
+ case 'I':
+ {
+ do_cmd_observe();
+ break;
+ }
+
+ /* Hack -- toggle windows */
+ case KTRL('I'):
+ {
+ toggle_inven_equip();
+ break;
+ }
+
+
+
+ /*** Use various objects ***/
+
+ /* Browse a book */
+ case 'b':
+ {
+ do_cmd_browse();
+ break;
+ }
+
+ /* Inscribe an object */
+ case '{':
+ {
+ do_cmd_inscribe();
+ break;
+ }
+
+ /* Uninscribe an object */
+ case '}':
+ {
+ do_cmd_uninscribe();
+ break;
+ }
+
+
+
+ /*** Help and Such ***/
+
+ /* Help */
+ case '?':
+ {
+ do_cmd_help();
+ break;
+ }
+
+ /* Identify symbol */
+ case '/':
+ {
+ do_cmd_query_symbol();
+ break;
+ }
+
+ /* Character description */
+ case 'C':
+ {
+ do_cmd_change_name();
+ display_store();
+ break;
+ }
+
+
+ /*** System Commands ***/
+
+ /* Hack -- User interface */
+ case '!':
+ {
+ (void)Term_user(0);
+ break;
+ }
+
+ /* Single line from a pref file */
+ case '"':
+ {
+ do_cmd_pref();
+ break;
+ }
+
+ /* Interact with macros */
+ case '@':
+ {
+ do_cmd_macros();
+ break;
+ }
+
+ /* Interact with visuals */
+ case '%':
+ {
+ do_cmd_visuals();
+ break;
+ }
+
+ /* Interact with colors */
+ case '&':
+ {
+ do_cmd_colors();
+ break;
+ }
+
+ /* Interact with options */
+ case '=':
+ {
+ do_cmd_options();
+ break;
+ }
+
+
+ /*** Misc Commands ***/
+
+ /* Take notes */
+ case ':':
+ {
+ do_cmd_note();
+ break;
+ }
+
+ /* Version info */
+ case 'V':
+ {
+ do_cmd_version();
+ break;
+ }
+
+ /* Repeat level feeling */
+ case KTRL('F'):
+ {
+ do_cmd_feeling();
+ break;
+ }
+
+ /* Show previous message */
+ case KTRL('O'):
+ {
+ do_cmd_message_one();
+ break;
+ }
+
+ /* Show previous messages */
+ case KTRL('P'):
+ {
+ do_cmd_messages();
+ break;
+ }
+
+ /* Check artifacts, uniques etc. */
+ case '~':
+ case '|':
+ {
+ do_cmd_knowledge();
+ break;
+ }
+
+ /* Load "screen dump" */
+ case '(':
+ {
+ do_cmd_load_screen();
+ break;
+ }
+
+ /* Save "screen dump" */
+ case ')':
+ {
+ do_cmd_save_screen();
+ break;
+ }
+
+
+ /* Hack -- Unknown command */
+ default:
+ {
+ if (st_ptr->st_idx == STORE_HOME)
+ msg_print("That command does not work in this home.");
+ else
+ msg_print("That command does not work in this store.");
+ break;
+ }
+ }
+ }
+
+ return recreate;
+}
+
+
+/*
+ * Enter a store, and interact with it.
+ *
+ * Note that we use the standard "request_command()" function
+ * to get a command, allowing us to use "command_arg" and all
+ * command macros and other nifty stuff, but we use the special
+ * "shopping" argument, to force certain commands to be converted
+ * into other commands, normally, we convert "p" (pray) and "m"
+ * (cast magic) into "g" (get), and "s" (search) into "d" (drop).
+ */
+void do_cmd_store(void)
+{
+ int which;
+ int maintain_num;
+ int tmp_chr;
+ int i;
+ bool_ recreate = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Access the player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ /* Verify a store */
+ if (c_ptr->feat != FEAT_SHOP)
+ {
+ msg_print("You see no store here.");
+ return;
+ }
+
+ /* Extract the store code */
+ which = c_ptr->special;
+
+ /* Hack -- Check the "locked doors" */
+ if (town_info[p_ptr->town_num].store[which].store_open >= turn)
+ {
+ msg_print("The doors are locked.");
+ return;
+ }
+
+ /* Calculate the number of store maintainances since the last visit */
+ maintain_num = (turn - town_info[p_ptr->town_num].store[which].last_visit) / (10L * STORE_TURNS);
+
+ /* Maintain the store max. 10 times */
+ if (maintain_num > 10) maintain_num = 10;
+
+ if (maintain_num)
+ {
+ /* Maintain the store */
+ for (i = 0; i < maintain_num; i++)
+ store_maint(p_ptr->town_num, which);
+
+ /* Save the visit */
+ town_info[p_ptr->town_num].store[which].last_visit = turn;
+ }
+
+ /* Forget the lite */
+ /* forget_lite(); */
+
+ /* Forget the view */
+ forget_view();
+
+
+ /* Hack -- Character is in "icky" mode */
+ character_icky = TRUE;
+
+
+ /* No command argument */
+ command_arg = 0;
+
+ /* No repeated command */
+ command_rep = 0;
+
+ /* No automatic command */
+ command_new = 0;
+
+
+ /* Save the store number */
+ cur_store_num = which;
+
+ /* Save the store and owner pointers */
+ st_ptr = &town_info[p_ptr->town_num].store[cur_store_num];
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Start at the beginning */
+ store_top = 0;
+
+ /* Display the store */
+ display_store();
+
+ /* Mega-Hack -- Ignore keymaps on store action letters */
+ for (i = 0; i < 6; i++)
+ {
+ store_action_type *ba_ptr =
+ &ba_info[st_info[st_ptr->st_idx].actions[i]];
+ request_command_ignore_keymaps[2*i] = ba_ptr->letter;
+ request_command_ignore_keymaps[2*i+1] = ba_ptr->letter_aux;
+
+ }
+
+ /* Do not leave */
+ leave_store = FALSE;
+
+ /* Interact with player */
+ while (!leave_store)
+ {
+ /* Hack -- Clear line 1 */
+ prt("", 1, 0);
+
+ /* Hack -- Check the charisma */
+ tmp_chr = p_ptr->stat_use[A_CHR];
+
+ /* Clear */
+ clear_from(21);
+
+
+ /* Basic commands */
+ c_prt(TERM_YELLOW, " ESC.", 22, 0);
+ prt(") Exit.", 22, 4);
+
+ /* Browse if necessary */
+ if (st_ptr->stock_num > 12)
+ {
+ c_prt(TERM_YELLOW, " SPACE", 23, 0);
+ prt(") Next page", 23, 6);
+ }
+
+ /* Prompt */
+ prt("You may: ", 21, 0);
+
+ /* Show the commands */
+ show_building(st_ptr);
+
+ /* Get a command */
+ request_command(TRUE);
+
+ /* Process the command */
+ if (store_process_command()) recreate = TRUE;
+
+ /* Hack -- Character is still in "icky" mode */
+ character_icky = TRUE;
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* XXX XXX XXX Pack Overflow */
+ if (p_ptr->inventory[INVEN_PACK].k_idx)
+ {
+ int item = INVEN_PACK;
+
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Hack -- Flee from the store */
+ if (cur_store_num != 7)
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee the store...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Flee from the home */
+ else if (!store_check_num(o_ptr))
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee your home...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Drop items into the home */
+ else
+ {
+ int item_pos;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char o_name[80];
+
+
+ /* Give a message */
+ msg_print("Your pack overflows!");
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Grab a copy of the item */
+ object_copy(q_ptr, o_ptr);
+
+ /* Describe it */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Remove it from the players inventory */
+ inc_stack_size(item, -255);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Redraw the home */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+ }
+
+ /* Hack -- Redisplay store prices if charisma changes */
+ if (tmp_chr != p_ptr->stat_use[A_CHR]) display_inventory();
+
+ /* Hack -- get kicked out of the store */
+ if (st_ptr->store_open >= turn) leave_store = TRUE;
+ }
+
+ /* Free turn XXX XXX XXX */
+ energy_use = 0;
+
+ /* Recreate the level only when needed */
+ if (recreate)
+ {
+ /* Reinit wilderness to activate quests ... */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+
+ p_ptr->leaving = TRUE;
+ }
+
+ /* Hack -- Character is no longer in "icky" mode */
+ character_icky = FALSE;
+
+
+ /* Hack -- Cancel automatic command */
+ command_new = 0;
+
+ /* Mega-Hack -- Clear the 'ignore-keymaps' list */
+ memset(request_command_ignore_keymaps, 0, 12);
+
+ /* Flush messages XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Clear the screen */
+ Term_clear();
+
+
+ /* Update everything */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE);
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw entire screen */
+ p_ptr->redraw |= (PR_BASIC | PR_EXTRA);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+/*
+ * Shuffle one of the stores.
+ */
+void store_shuffle(int which)
+{
+ int i, j;
+
+
+ /* Ignore home */
+ if (which == STORE_HOME) return;
+
+ /* Ignoer Museum */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) return;
+
+
+ /* Save the store index */
+ cur_store_num = which;
+
+ /* Activate that store */
+ st_ptr = &town_info[p_ptr->town_num].store[cur_store_num];
+
+ /* Pick a new owner */
+ for (j = st_ptr->owner; j == st_ptr->owner; )
+ {
+ st_ptr->owner = st_info[st_ptr->st_idx].owners[rand_int(4)];
+ }
+
+ /* Activate the new owner */
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Reset the owner data */
+ st_ptr->insult_cur = 0;
+ st_ptr->store_open = 0;
+ st_ptr->good_buy = 0;
+ st_ptr->bad_buy = 0;
+
+
+ /* Hack -- discount all the items */
+ for (i = 0; i < st_ptr->stock_num; i++)
+ {
+ object_type *o_ptr;
+
+ /* Get the item */
+ o_ptr = &st_ptr->stock[i];
+
+ /* Hack -- Sell all old items for "half price" */
+ if (!(o_ptr->art_name))
+ o_ptr->discount = 50;
+
+ /* Hack -- Items are no longer "fixed price" */
+ o_ptr->ident &= ~(IDENT_FIXED);
+
+ /* Mega-Hack -- Note that the item is "on sale" */
+ o_ptr->note = quark_add("on sale");
+ }
+}
+
+
+/*
+ * Maintain the inventory at the stores.
+ */
+void store_maint(int town_num, int store_num)
+{
+ int j, tries = 100;
+
+ int old_rating = rating;
+
+ cur_store_num = store_num;
+
+ /* Ignore home */
+ if (store_num == STORE_HOME) return;
+
+ /* Activate that store */
+ st_ptr = &town_info[town_num].store[store_num];
+
+ /* Ignoer Museum */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) return;
+
+ /* Activate the owner */
+ ot_ptr = &ow_info[st_ptr->owner];
+
+ /* Store keeper forgives the player */
+ st_ptr->insult_cur = 0;
+
+ /* Mega-Hack -- prune the black market */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM)
+ {
+ /* Destroy crappy black market items */
+ for (j = st_ptr->stock_num - 1; j >= 0; j--)
+ {
+ object_type *o_ptr = &st_ptr->stock[j];
+
+ /* Destroy crappy items */
+ if (black_market_crap(o_ptr))
+ {
+ /* Destroy the item */
+ store_item_increase(j, 0 - o_ptr->number);
+ store_item_optimize(j);
+ }
+ }
+ }
+
+
+ /* Choose the number of slots to keep */
+ j = st_ptr->stock_num;
+
+ /* Sell a few items */
+ j = j - randint(STORE_TURNOVER);
+
+ /* Never keep more than "STORE_MAX_KEEP" slots */
+ if (j > STORE_MAX_KEEP) j = STORE_MAX_KEEP;
+
+ /* Always "keep" at least "STORE_MIN_KEEP" items */
+ if (j < STORE_MIN_KEEP) j = STORE_MIN_KEEP;
+
+ /* Hack -- prevent "underflow" */
+ if (j < 0) j = 0;
+
+ /* Destroy objects until only "j" slots are left */
+ while (st_ptr->stock_num > j) store_delete();
+
+
+ /* Choose the number of slots to fill */
+ j = st_ptr->stock_num;
+
+ /* Buy some more items */
+ j = j + randint(STORE_TURNOVER);
+
+ /* Never keep more than "STORE_MAX_KEEP" slots */
+ if (j > STORE_MAX_KEEP) j = STORE_MAX_KEEP;
+
+ /* Always "keep" at least "STORE_MIN_KEEP" items */
+ if (j < STORE_MIN_KEEP) j = STORE_MIN_KEEP;
+
+ /* Hack -- prevent "overflow" */
+ if (j >= st_ptr->stock_size) j = st_ptr->stock_size - 1;
+
+ /* Acquire some new items */
+ while ((st_ptr->stock_num < j) && tries)
+ {
+ store_create();
+ tries--;
+ }
+
+ /* Hack -- Restore the rating */
+ rating = old_rating;
+}
+
+
+/*
+ * Initialize the stores
+ */
+void store_init(int town_num, int store_num)
+{
+ int k;
+
+ cur_store_num = store_num;
+
+ /* Activate that store */
+ st_ptr = &town_info[town_num].store[store_num];
+
+
+ /* Pick an owner */
+ st_ptr->owner = st_info[st_ptr->st_idx].owners[rand_int(4)];
+
+ /* Activate the new owner */
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Initialize the store */
+ st_ptr->store_open = 0;
+ st_ptr->insult_cur = 0;
+ st_ptr->good_buy = 0;
+ st_ptr->bad_buy = 0;
+
+ /* Nothing in stock */
+ st_ptr->stock_num = 0;
+
+ /*
+ * MEGA-HACK - Last visit to store is
+ * BEFORE player birth to enable store restocking
+ */
+ st_ptr->last_visit = -100L * STORE_TURNS;
+
+ /* Clear any old items */
+ for (k = 0; k < st_ptr->stock_size; k++)
+ {
+ object_wipe(&st_ptr->stock[k]);
+ }
+}
+
+
+void move_to_black_market(object_type * o_ptr)
+{
+ st_ptr = &town_info[p_ptr->town_num].store[6];
+ o_ptr->ident |= IDENT_STOREB;
+ (void)store_carry(o_ptr);
+ object_wipe(o_ptr); /* Don't leave a bogus object behind... */
+}
+
+/*
+ * Enter the home, and interact with it from the dungeon (trump magic).
+ *
+ * Note that we use the standard "request_command()" function
+ * to get a command, allowing us to use "command_arg" and all
+ * command macros and other nifty stuff, but we use the special
+ * "shopping" argument, to force certain commands to be converted
+ * into other commands, normally, we convert "p" (pray) and "m"
+ * (cast magic) into "g" (get), and "s" (search) into "d" (drop).
+ */
+void do_cmd_home_trump(void)
+{
+ int which;
+ int maintain_num;
+ int tmp_chr;
+ int i;
+ int town_num;
+
+ /* Extract the store code */
+ which = 7;
+
+ if (p_ptr->town_num) town_num = p_ptr->town_num;
+ else town_num = 1;
+
+ /* Hack -- Check the "locked doors" */
+ if (town_info[town_num].store[which].store_open >= turn)
+ {
+ msg_print("The doors are locked.");
+ return;
+ }
+
+ /* Calculate the number of store maintainances since the last visit */
+ maintain_num = (turn - town_info[town_num].store[which].last_visit) / (10L * STORE_TURNS);
+
+ /* Maintain the store max. 10 times */
+ if (maintain_num > 10) maintain_num = 10;
+
+ if (maintain_num)
+ {
+ /* Maintain the store */
+ for (i = 0; i < maintain_num; i++)
+ store_maint(town_num, which);
+
+ /* Save the visit */
+ town_info[town_num].store[which].last_visit = turn;
+ }
+
+ /* Forget the lite */
+ /* forget_lite(); */
+
+ /* Forget the view */
+ forget_view();
+
+
+ /* Hack -- Character is in "icky" mode */
+ character_icky = TRUE;
+
+
+ /* No command argument */
+ command_arg = 0;
+
+ /* No repeated command */
+ command_rep = 0;
+
+ /* No automatic command */
+ command_new = 0;
+
+
+ /* Save the store number */
+ cur_store_num = which;
+
+ /* Save the store and owner pointers */
+ st_ptr = &town_info[town_num].store[cur_store_num];
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Start at the beginning */
+ store_top = 0;
+
+ /* Display the store */
+ display_store();
+
+ /* Mega-Hack -- Ignore keymaps on store action letters */
+ for (i = 0; i < 6; i++)
+ {
+ store_action_type *ba_ptr =
+ &ba_info[st_info[st_ptr->st_idx].actions[i]];
+ request_command_ignore_keymaps[2*i] = ba_ptr->letter;
+ request_command_ignore_keymaps[2*i+1] = ba_ptr->letter_aux;
+ }
+
+ /* Do not leave */
+ leave_store = FALSE;
+
+ /* Interact with player */
+ while (!leave_store)
+ {
+ /* Hack -- Clear line 1 */
+ prt("", 1, 0);
+
+ /* Hack -- Check the charisma */
+ tmp_chr = p_ptr->stat_use[A_CHR];
+
+ /* Clear */
+ clear_from(21);
+
+
+ /* Basic commands */
+ prt(" ESC) Exit from Building.", 22, 0);
+
+ /* Browse if necessary */
+ if (st_ptr->stock_num > 12)
+ {
+ prt(" SPACE) Next page of stock", 23, 0);
+ }
+
+ /* Home commands */
+ if (cur_store_num == 7)
+ {
+ prt(" g) Get an item.", 22, 31);
+ prt(" d) Drop an item.", 23, 31);
+ }
+
+ /* Shop commands XXX XXX XXX */
+ else
+ {
+ prt(" p) Purchase an item.", 22, 31);
+ prt(" s) Sell an item.", 23, 31);
+ }
+
+ /* Add in the eXamine option */
+ prt(" x) eXamine an item.", 22, 56);
+
+ /* Prompt */
+ prt("You may: ", 21, 0);
+
+ /* Get a command */
+ request_command(TRUE);
+
+ /* Process the command */
+ store_process_command();
+
+ /* Hack -- Character is still in "icky" mode */
+ character_icky = TRUE;
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* XXX XXX XXX Pack Overflow */
+ if (p_ptr->inventory[INVEN_PACK].k_idx)
+ {
+ int item = INVEN_PACK;
+
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Hack -- Flee from the store */
+ if (cur_store_num != 7)
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee the store...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Flee from the home */
+ else if (!store_check_num(o_ptr))
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee your home...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Drop items into the home */
+ else
+ {
+ int item_pos;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char o_name[80];
+
+
+ /* Give a message */
+ msg_print("Your pack overflows!");
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Grab a copy of the item */
+ object_copy(q_ptr, o_ptr);
+
+ /* Describe it */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Remove it from the players inventory */
+ inc_stack_size(item, -255);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Redraw the home */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+ }
+
+ /* Hack -- Redisplay store prices if charisma changes */
+ if (tmp_chr != p_ptr->stat_use[A_CHR]) display_inventory();
+
+ /* Hack -- get kicked out of the store */
+ if (st_ptr->store_open >= turn) leave_store = TRUE;
+ }
+
+
+ /* Hack -- Character is no longer in "icky" mode */
+ character_icky = FALSE;
+
+
+ /* Hack -- Cancel automatic command */
+ command_new = 0;
+
+ /* Mega-Hack -- Clear the 'ignore-keymaps' list */
+ memset(request_command_ignore_keymaps, 0, 12);
+
+ /* Flush messages XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Clear the screen */
+ Term_clear();
+
+
+ /* Update everything */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE);
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw entire screen */
+ p_ptr->redraw |= (PR_BASIC | PR_EXTRA);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+static void pay_for_requested_item(int value, object_type *q_ptr)
+{
+ msg_format("It'll cost %i gold pieces. ", value);
+
+ if (get_check("Do you wish to pay?"))
+ {
+ if (p_ptr->au < value)
+ msg_print("You don't have enough money for it.");
+ else
+ {
+ if (store_carry(q_ptr) != -1)
+ {
+ msg_print("The item has arrived in the Black Market.");
+ p_ptr->au -= value;
+
+ p_ptr->redraw |= PR_GOLD;
+ }
+ else
+ msg_print("There isn't enough room for it.");
+ }
+ }
+}
+
+/*
+ * Request item for merchants
+ */
+void store_request_item(void)
+{
+ char buf[80], name[80];
+ object_type forge, *q_ptr = &forge;
+ store_type *ost_ptr = st_ptr;
+
+ /* Get the Black Market */
+ st_ptr = &town_info[p_ptr->town_num].store[6];
+
+ /* Make an empty string */
+ buf[0] = 0;
+
+ /* Ask for the wish */
+ if (!get_string("Request what? ", buf, 80))
+ {
+ st_ptr = ost_ptr;
+ return;
+ }
+
+ clean_wish_name(buf, name);
+
+ if (test_object_wish(name, q_ptr, &forge, "request"))
+ {
+ int value = object_value_real(q_ptr) * 5;
+
+ /* Pay for the delivery */
+ pay_for_requested_item(value, q_ptr);
+
+ /* Don't search any more */
+ st_ptr = ost_ptr;
+ return;
+ }
+
+ st_ptr = ost_ptr;
+}
diff --git a/src/tables.c b/src/tables.c
new file mode 100644
index 00000000..e976e234
--- /dev/null
+++ b/src/tables.c
@@ -0,0 +1,4792 @@
+/* File: tables.c */
+
+/* Purpose: Angband Tables */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+
+
+/*
+ * Global array for looping through the "keypad directions"
+ */
+s16b ddd[9] =
+ { 2, 8, 6, 4, 3, 1, 9, 7, 5 };
+
+/*
+ * Global arrays for converting "keypad direction" into offsets
+ */
+s16b ddx[10] =
+ { 0, -1, 0, 1, -1, 0, 1, -1, 0, 1 };
+
+s16b ddy[10] =
+ { 0, 1, 1, 1, 0, 0, 0, -1, -1, -1 };
+
+/*
+ * Global arrays for optimizing "ddx[ddd[i]]" and "ddy[ddd[i]]"
+ */
+s16b ddx_ddd[9] =
+ { 0, 0, 1, -1, 1, -1, 1, -1, 0 };
+
+s16b ddy_ddd[9] =
+ { 1, -1, 0, 0, 1, 1, -1, -1, 0 };
+
+
+
+/*
+* Global array for converting numbers to uppercase hexadecimal digit
+ * This array can also be used to convert a number to an octal digit
+ */
+char hexsym[16] =
+ {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+
+/*
+ * Stat Table (INT/WIS) -- Number of half-spells per level
+ */
+byte adj_mag_study[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 2 /* 12 */,
+ 2 /* 13 */,
+ 2 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 2 /* 18/00-18/09 */,
+ 2 /* 18/10-18/19 */,
+ 2 /* 18/20-18/29 */,
+ 2 /* 18/30-18/39 */,
+ 2 /* 18/40-18/49 */,
+ 3 /* 18/50-18/59 */,
+ 3 /* 18/60-18/69 */,
+ 3 /* 18/70-18/79 */,
+ 3 /* 18/80-18/89 */,
+ 4 /* 18/90-18/99 */,
+ 4 /* 18/100-18/109 */,
+ 4 /* 18/110-18/119 */,
+ 5 /* 18/120-18/129 */,
+ 5 /* 18/130-18/139 */,
+ 5 /* 18/140-18/149 */,
+ 5 /* 18/150-18/159 */,
+ 5 /* 18/160-18/169 */,
+ 5 /* 18/170-18/179 */,
+ 5 /* 18/180-18/189 */,
+ 5 /* 18/190-18/199 */,
+ 5 /* 18/200-18/209 */,
+ 6 /* 18/210-18/219 */,
+ 6 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT/WIS) -- extra half-mana-points per level
+ */
+byte adj_mag_mana[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 2 /* 9 */,
+ 2 /* 10 */,
+ 2 /* 11 */,
+ 2 /* 12 */,
+ 2 /* 13 */,
+ 2 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 3 /* 18/20-18/29 */,
+ 3 /* 18/30-18/39 */,
+ 3 /* 18/40-18/49 */,
+ 4 /* 18/50-18/59 */,
+ 4 /* 18/60-18/69 */,
+ 5 /* 18/70-18/79 */,
+ 6 /* 18/80-18/89 */,
+ 7 /* 18/90-18/99 */,
+ 8 /* 18/100-18/109 */,
+ 9 /* 18/110-18/119 */,
+ 10 /* 18/120-18/129 */,
+ 11 /* 18/130-18/139 */,
+ 12 /* 18/140-18/149 */,
+ 13 /* 18/150-18/159 */,
+ 14 /* 18/160-18/169 */,
+ 15 /* 18/170-18/179 */,
+ 16 /* 18/180-18/189 */,
+ 16 /* 18/190-18/199 */,
+ 17 /* 18/200-18/209 */,
+ 17 /* 18/210-18/219 */,
+ 18 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT/WIS) -- Minimum failure rate (percentage)
+ */
+byte adj_mag_fail[] =
+{
+ 99 /* 3 */,
+ 99 /* 4 */,
+ 99 /* 5 */,
+ 99 /* 6 */,
+ 99 /* 7 */,
+ 50 /* 8 */,
+ 30 /* 9 */,
+ 20 /* 10 */,
+ 15 /* 11 */,
+ 12 /* 12 */,
+ 11 /* 13 */,
+ 10 /* 14 */,
+ 9 /* 15 */,
+ 8 /* 16 */,
+ 7 /* 17 */,
+ 6 /* 18/00-18/09 */,
+ 6 /* 18/10-18/19 */,
+ 5 /* 18/20-18/29 */,
+ 5 /* 18/30-18/39 */,
+ 5 /* 18/40-18/49 */,
+ 4 /* 18/50-18/59 */,
+ 4 /* 18/60-18/69 */,
+ 4 /* 18/70-18/79 */,
+ 4 /* 18/80-18/89 */,
+ 3 /* 18/90-18/99 */,
+ 3 /* 18/100-18/109 */,
+ 2 /* 18/110-18/119 */,
+ 2 /* 18/120-18/129 */,
+ 2 /* 18/130-18/139 */,
+ 2 /* 18/140-18/149 */,
+ 1 /* 18/150-18/159 */,
+ 1 /* 18/160-18/169 */,
+ 1 /* 18/170-18/179 */,
+ 1 /* 18/180-18/189 */,
+ 1 /* 18/190-18/199 */,
+ 0 /* 18/200-18/209 */,
+ 0 /* 18/210-18/219 */,
+ 0 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT/WIS) -- Various things
+ */
+byte adj_mag_stat[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 3 /* 18/20-18/29 */,
+ 3 /* 18/30-18/39 */,
+ 3 /* 18/40-18/49 */,
+ 4 /* 18/50-18/59 */,
+ 4 /* 18/60-18/69 */,
+ 5 /* 18/70-18/79 */,
+ 6 /* 18/80-18/89 */,
+ 7 /* 18/90-18/99 */,
+ 8 /* 18/100-18/109 */,
+ 9 /* 18/110-18/119 */,
+ 10 /* 18/120-18/129 */,
+ 11 /* 18/130-18/139 */,
+ 12 /* 18/140-18/149 */,
+ 13 /* 18/150-18/159 */,
+ 14 /* 18/160-18/169 */,
+ 15 /* 18/170-18/179 */,
+ 16 /* 18/180-18/189 */,
+ 17 /* 18/190-18/199 */,
+ 18 /* 18/200-18/209 */,
+ 19 /* 18/210-18/219 */,
+ 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (CHR) -- payment percentages
+ */
+byte adj_chr_gold[] =
+{
+ 130 /* 3 */,
+ 125 /* 4 */,
+ 122 /* 5 */,
+ 120 /* 6 */,
+ 118 /* 7 */,
+ 116 /* 8 */,
+ 114 /* 9 */,
+ 112 /* 10 */,
+ 110 /* 11 */,
+ 108 /* 12 */,
+ 106 /* 13 */,
+ 104 /* 14 */,
+ 103 /* 15 */,
+ 102 /* 16 */,
+ 101 /* 17 */,
+ 100 /* 18/00-18/09 */,
+ 99 /* 18/10-18/19 */,
+ 98 /* 18/20-18/29 */,
+ 97 /* 18/30-18/39 */,
+ 96 /* 18/40-18/49 */,
+ 95 /* 18/50-18/59 */,
+ 94 /* 18/60-18/69 */,
+ 93 /* 18/70-18/79 */,
+ 92 /* 18/80-18/89 */,
+ 91 /* 18/90-18/99 */,
+ 90 /* 18/100-18/109 */,
+ 89 /* 18/110-18/119 */,
+ 88 /* 18/120-18/129 */,
+ 87 /* 18/130-18/139 */,
+ 86 /* 18/140-18/149 */,
+ 85 /* 18/150-18/159 */,
+ 84 /* 18/160-18/169 */,
+ 83 /* 18/170-18/179 */,
+ 82 /* 18/180-18/189 */,
+ 81 /* 18/190-18/199 */,
+ 80 /* 18/200-18/209 */,
+ 79 /* 18/210-18/219 */,
+ 78 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT) -- Magic devices
+ */
+byte adj_int_dev[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 4 /* 18/20-18/29 */,
+ 4 /* 18/30-18/39 */,
+ 5 /* 18/40-18/49 */,
+ 5 /* 18/50-18/59 */,
+ 6 /* 18/60-18/69 */,
+ 6 /* 18/70-18/79 */,
+ 7 /* 18/80-18/89 */,
+ 7 /* 18/90-18/99 */,
+ 8 /* 18/100-18/109 */,
+ 9 /* 18/110-18/119 */,
+ 10 /* 18/120-18/129 */,
+ 11 /* 18/130-18/139 */,
+ 12 /* 18/140-18/149 */,
+ 13 /* 18/150-18/159 */,
+ 14 /* 18/160-18/169 */,
+ 15 /* 18/170-18/179 */,
+ 16 /* 18/180-18/189 */,
+ 17 /* 18/190-18/199 */,
+ 18 /* 18/200-18/209 */,
+ 19 /* 18/210-18/219 */,
+ 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (WIS) -- Saving throw
+ */
+byte adj_wis_sav[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 3 /* 18/20-18/29 */,
+ 3 /* 18/30-18/39 */,
+ 3 /* 18/40-18/49 */,
+ 4 /* 18/50-18/59 */,
+ 4 /* 18/60-18/69 */,
+ 5 /* 18/70-18/79 */,
+ 5 /* 18/80-18/89 */,
+ 6 /* 18/90-18/99 */,
+ 7 /* 18/100-18/109 */,
+ 8 /* 18/110-18/119 */,
+ 9 /* 18/120-18/129 */,
+ 10 /* 18/130-18/139 */,
+ 11 /* 18/140-18/149 */,
+ 12 /* 18/150-18/159 */,
+ 13 /* 18/160-18/169 */,
+ 14 /* 18/170-18/179 */,
+ 15 /* 18/180-18/189 */,
+ 16 /* 18/190-18/199 */,
+ 17 /* 18/200-18/209 */,
+ 18 /* 18/210-18/219 */,
+ 19 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- disarming
+ */
+byte adj_dex_dis[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 0 /* 8 */,
+ 0 /* 9 */,
+ 0 /* 10 */,
+ 0 /* 11 */,
+ 0 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 1 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 4 /* 18/00-18/09 */,
+ 4 /* 18/10-18/19 */,
+ 4 /* 18/20-18/29 */,
+ 4 /* 18/30-18/39 */,
+ 5 /* 18/40-18/49 */,
+ 5 /* 18/50-18/59 */,
+ 5 /* 18/60-18/69 */,
+ 6 /* 18/70-18/79 */,
+ 6 /* 18/80-18/89 */,
+ 7 /* 18/90-18/99 */,
+ 8 /* 18/100-18/109 */,
+ 8 /* 18/110-18/119 */,
+ 8 /* 18/120-18/129 */,
+ 8 /* 18/130-18/139 */,
+ 8 /* 18/140-18/149 */,
+ 9 /* 18/150-18/159 */,
+ 9 /* 18/160-18/169 */,
+ 9 /* 18/170-18/179 */,
+ 9 /* 18/180-18/189 */,
+ 9 /* 18/190-18/199 */,
+ 10 /* 18/200-18/209 */,
+ 10 /* 18/210-18/219 */,
+ 10 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT) -- disarming
+ */
+byte adj_int_dis[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 3 /* 18/20-18/29 */,
+ 4 /* 18/30-18/39 */,
+ 4 /* 18/40-18/49 */,
+ 5 /* 18/50-18/59 */,
+ 6 /* 18/60-18/69 */,
+ 7 /* 18/70-18/79 */,
+ 8 /* 18/80-18/89 */,
+ 9 /* 18/90-18/99 */,
+ 10 /* 18/100-18/109 */,
+ 10 /* 18/110-18/119 */,
+ 11 /* 18/120-18/129 */,
+ 12 /* 18/130-18/139 */,
+ 13 /* 18/140-18/149 */,
+ 14 /* 18/150-18/159 */,
+ 15 /* 18/160-18/169 */,
+ 16 /* 18/170-18/179 */,
+ 17 /* 18/180-18/189 */,
+ 18 /* 18/190-18/199 */,
+ 19 /* 18/200-18/209 */,
+ 19 /* 18/210-18/219 */,
+ 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- bonus to ac (plus 128)
+ */
+byte adj_dex_ta[] =
+{
+ 128 + -4 /* 3 */,
+ 128 + -3 /* 4 */,
+ 128 + -2 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + 0 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 1 /* 15 */,
+ 128 + 1 /* 16 */,
+ 128 + 1 /* 17 */,
+ 128 + 2 /* 18/00-18/09 */,
+ 128 + 2 /* 18/10-18/19 */,
+ 128 + 2 /* 18/20-18/29 */,
+ 128 + 2 /* 18/30-18/39 */,
+ 128 + 2 /* 18/40-18/49 */,
+ 128 + 3 /* 18/50-18/59 */,
+ 128 + 3 /* 18/60-18/69 */,
+ 128 + 3 /* 18/70-18/79 */,
+ 128 + 4 /* 18/80-18/89 */,
+ 128 + 5 /* 18/90-18/99 */,
+ 128 + 6 /* 18/100-18/109 */,
+ 128 + 7 /* 18/110-18/119 */,
+ 128 + 8 /* 18/120-18/129 */,
+ 128 + 9 /* 18/130-18/139 */,
+ 128 + 9 /* 18/140-18/149 */,
+ 128 + 10 /* 18/150-18/159 */,
+ 128 + 11 /* 18/160-18/169 */,
+ 128 + 12 /* 18/170-18/179 */,
+ 128 + 13 /* 18/180-18/189 */,
+ 128 + 14 /* 18/190-18/199 */,
+ 128 + 15 /* 18/200-18/209 */,
+ 128 + 15 /* 18/210-18/219 */,
+ 128 + 16 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- bonus to dam (plus 128)
+ */
+byte adj_str_td[] =
+{
+ 128 + -2 /* 3 */,
+ 128 + -2 /* 4 */,
+ 128 + -1 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + 0 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 0 /* 15 */,
+ 128 + 1 /* 16 */,
+ 128 + 2 /* 17 */,
+ 128 + 2 /* 18/00-18/09 */,
+ 128 + 2 /* 18/10-18/19 */,
+ 128 + 3 /* 18/20-18/29 */,
+ 128 + 3 /* 18/30-18/39 */,
+ 128 + 3 /* 18/40-18/49 */,
+ 128 + 3 /* 18/50-18/59 */,
+ 128 + 3 /* 18/60-18/69 */,
+ 128 + 4 /* 18/70-18/79 */,
+ 128 + 5 /* 18/80-18/89 */,
+ 128 + 5 /* 18/90-18/99 */,
+ 128 + 6 /* 18/100-18/109 */,
+ 128 + 7 /* 18/110-18/119 */,
+ 128 + 8 /* 18/120-18/129 */,
+ 128 + 9 /* 18/130-18/139 */,
+ 128 + 10 /* 18/140-18/149 */,
+ 128 + 11 /* 18/150-18/159 */,
+ 128 + 12 /* 18/160-18/169 */,
+ 128 + 13 /* 18/170-18/179 */,
+ 128 + 14 /* 18/180-18/189 */,
+ 128 + 15 /* 18/190-18/199 */,
+ 128 + 16 /* 18/200-18/209 */,
+ 128 + 18 /* 18/210-18/219 */,
+ 128 + 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- bonus to hit (plus 128)
+ */
+byte adj_dex_th[] =
+{
+ 128 + -3 /* 3 */,
+ 128 + -2 /* 4 */,
+ 128 + -2 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + -1 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 0 /* 15 */,
+ 128 + 1 /* 16 */,
+ 128 + 2 /* 17 */,
+ 128 + 3 /* 18/00-18/09 */,
+ 128 + 3 /* 18/10-18/19 */,
+ 128 + 3 /* 18/20-18/29 */,
+ 128 + 3 /* 18/30-18/39 */,
+ 128 + 3 /* 18/40-18/49 */,
+ 128 + 4 /* 18/50-18/59 */,
+ 128 + 4 /* 18/60-18/69 */,
+ 128 + 4 /* 18/70-18/79 */,
+ 128 + 4 /* 18/80-18/89 */,
+ 128 + 5 /* 18/90-18/99 */,
+ 128 + 6 /* 18/100-18/109 */,
+ 128 + 7 /* 18/110-18/119 */,
+ 128 + 8 /* 18/120-18/129 */,
+ 128 + 9 /* 18/130-18/139 */,
+ 128 + 9 /* 18/140-18/149 */,
+ 128 + 10 /* 18/150-18/159 */,
+ 128 + 11 /* 18/160-18/169 */,
+ 128 + 12 /* 18/170-18/179 */,
+ 128 + 13 /* 18/180-18/189 */,
+ 128 + 14 /* 18/190-18/199 */,
+ 128 + 15 /* 18/200-18/209 */,
+ 128 + 15 /* 18/210-18/219 */,
+ 128 + 16 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- bonus to hit (plus 128)
+ */
+byte adj_str_th[] =
+{
+ 128 + -3 /* 3 */,
+ 128 + -2 /* 4 */,
+ 128 + -1 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + 0 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 0 /* 15 */,
+ 128 + 0 /* 16 */,
+ 128 + 0 /* 17 */,
+ 128 + 1 /* 18/00-18/09 */,
+ 128 + 1 /* 18/10-18/19 */,
+ 128 + 1 /* 18/20-18/29 */,
+ 128 + 1 /* 18/30-18/39 */,
+ 128 + 1 /* 18/40-18/49 */,
+ 128 + 1 /* 18/50-18/59 */,
+ 128 + 1 /* 18/60-18/69 */,
+ 128 + 2 /* 18/70-18/79 */,
+ 128 + 3 /* 18/80-18/89 */,
+ 128 + 4 /* 18/90-18/99 */,
+ 128 + 5 /* 18/100-18/109 */,
+ 128 + 6 /* 18/110-18/119 */,
+ 128 + 7 /* 18/120-18/129 */,
+ 128 + 8 /* 18/130-18/139 */,
+ 128 + 9 /* 18/140-18/149 */,
+ 128 + 10 /* 18/150-18/159 */,
+ 128 + 11 /* 18/160-18/169 */,
+ 128 + 12 /* 18/170-18/179 */,
+ 128 + 13 /* 18/180-18/189 */,
+ 128 + 14 /* 18/190-18/199 */,
+ 128 + 15 /* 18/200-18/209 */,
+ 128 + 15 /* 18/210-18/219 */,
+ 128 + 16 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- weight limit in deca-pounds
+ */
+byte adj_str_wgt[] =
+{
+ 5 /* 3 */,
+ 6 /* 4 */,
+ 7 /* 5 */,
+ 8 /* 6 */,
+ 9 /* 7 */,
+ 10 /* 8 */,
+ 11 /* 9 */,
+ 12 /* 10 */,
+ 13 /* 11 */,
+ 14 /* 12 */,
+ 15 /* 13 */,
+ 16 /* 14 */,
+ 17 /* 15 */,
+ 18 /* 16 */,
+ 19 /* 17 */,
+ 20 /* 18/00-18/09 */,
+ 22 /* 18/10-18/19 */,
+ 24 /* 18/20-18/29 */,
+ 26 /* 18/30-18/39 */,
+ 28 /* 18/40-18/49 */,
+ 30 /* 18/50-18/59 */,
+ 31 /* 18/60-18/69 */,
+ 31 /* 18/70-18/79 */,
+ 32 /* 18/80-18/89 */,
+ 32 /* 18/90-18/99 */,
+ 33 /* 18/100-18/109 */,
+ 33 /* 18/110-18/119 */,
+ 34 /* 18/120-18/129 */,
+ 34 /* 18/130-18/139 */,
+ 35 /* 18/140-18/149 */,
+ 35 /* 18/150-18/159 */,
+ 36 /* 18/160-18/169 */,
+ 36 /* 18/170-18/179 */,
+ 37 /* 18/180-18/189 */,
+ 37 /* 18/190-18/199 */,
+ 38 /* 18/200-18/209 */,
+ 38 /* 18/210-18/219 */,
+ 39 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- weapon weight limit in pounds
+ */
+byte adj_str_hold[] =
+{
+ 4 /* 3 */,
+ 5 /* 4 */,
+ 6 /* 5 */,
+ 7 /* 6 */,
+ 8 /* 7 */,
+ 10 /* 8 */,
+ 12 /* 9 */,
+ 14 /* 10 */,
+ 16 /* 11 */,
+ 18 /* 12 */,
+ 20 /* 13 */,
+ 22 /* 14 */,
+ 24 /* 15 */,
+ 26 /* 16 */,
+ 28 /* 17 */,
+ 30 /* 18/00-18/09 */,
+ 30 /* 18/10-18/19 */,
+ 35 /* 18/20-18/29 */,
+ 40 /* 18/30-18/39 */,
+ 45 /* 18/40-18/49 */,
+ 50 /* 18/50-18/59 */,
+ 55 /* 18/60-18/69 */,
+ 60 /* 18/70-18/79 */,
+ 65 /* 18/80-18/89 */,
+ 70 /* 18/90-18/99 */,
+ 80 /* 18/100-18/109 */,
+ 80 /* 18/110-18/119 */,
+ 80 /* 18/120-18/129 */,
+ 80 /* 18/130-18/139 */,
+ 80 /* 18/140-18/149 */,
+ 90 /* 18/150-18/159 */,
+ 90 /* 18/160-18/169 */,
+ 90 /* 18/170-18/179 */,
+ 90 /* 18/180-18/189 */,
+ 90 /* 18/190-18/199 */,
+ 100 /* 18/200-18/209 */,
+ 100 /* 18/210-18/219 */,
+ 100 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- digging value
+ */
+byte adj_str_dig[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 1 /* 5 */,
+ 2 /* 6 */,
+ 3 /* 7 */,
+ 4 /* 8 */,
+ 4 /* 9 */,
+ 5 /* 10 */,
+ 5 /* 11 */,
+ 6 /* 12 */,
+ 6 /* 13 */,
+ 7 /* 14 */,
+ 7 /* 15 */,
+ 8 /* 16 */,
+ 8 /* 17 */,
+ 9 /* 18/00-18/09 */,
+ 10 /* 18/10-18/19 */,
+ 12 /* 18/20-18/29 */,
+ 15 /* 18/30-18/39 */,
+ 20 /* 18/40-18/49 */,
+ 25 /* 18/50-18/59 */,
+ 30 /* 18/60-18/69 */,
+ 35 /* 18/70-18/79 */,
+ 40 /* 18/80-18/89 */,
+ 45 /* 18/90-18/99 */,
+ 50 /* 18/100-18/109 */,
+ 55 /* 18/110-18/119 */,
+ 60 /* 18/120-18/129 */,
+ 65 /* 18/130-18/139 */,
+ 70 /* 18/140-18/149 */,
+ 75 /* 18/150-18/159 */,
+ 80 /* 18/160-18/169 */,
+ 85 /* 18/170-18/179 */,
+ 90 /* 18/180-18/189 */,
+ 95 /* 18/190-18/199 */,
+ 100 /* 18/200-18/209 */,
+ 100 /* 18/210-18/219 */,
+ 100 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- help index into the "blow" table
+ */
+byte adj_str_blow[] =
+{
+ 3 /* 3 */,
+ 4 /* 4 */,
+ 5 /* 5 */,
+ 6 /* 6 */,
+ 7 /* 7 */,
+ 8 /* 8 */,
+ 9 /* 9 */,
+ 10 /* 10 */,
+ 11 /* 11 */,
+ 12 /* 12 */,
+ 13 /* 13 */,
+ 14 /* 14 */,
+ 15 /* 15 */,
+ 16 /* 16 */,
+ 17 /* 17 */,
+ 20 /* 18/00-18/09 */,
+ 30 /* 18/10-18/19 */,
+ 40 /* 18/20-18/29 */,
+ 50 /* 18/30-18/39 */,
+ 60 /* 18/40-18/49 */,
+ 70 /* 18/50-18/59 */,
+ 80 /* 18/60-18/69 */,
+ 90 /* 18/70-18/79 */,
+ 100 /* 18/80-18/89 */,
+ 110 /* 18/90-18/99 */,
+ 120 /* 18/100-18/109 */,
+ 130 /* 18/110-18/119 */,
+ 140 /* 18/120-18/129 */,
+ 150 /* 18/130-18/139 */,
+ 160 /* 18/140-18/149 */,
+ 170 /* 18/150-18/159 */,
+ 180 /* 18/160-18/169 */,
+ 190 /* 18/170-18/179 */,
+ 200 /* 18/180-18/189 */,
+ 210 /* 18/190-18/199 */,
+ 220 /* 18/200-18/209 */,
+ 230 /* 18/210-18/219 */,
+ 240 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- index into the "blow" table
+ */
+byte adj_dex_blow[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 0 /* 8 */,
+ 0 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 1 /* 15 */,
+ 1 /* 16 */,
+ 1 /* 17 */,
+ 1 /* 18/00-18/09 */,
+ 2 /* 18/10-18/19 */,
+ 2 /* 18/20-18/29 */,
+ 2 /* 18/30-18/39 */,
+ 2 /* 18/40-18/49 */,
+ 3 /* 18/50-18/59 */,
+ 3 /* 18/60-18/69 */,
+ 4 /* 18/70-18/79 */,
+ 4 /* 18/80-18/89 */,
+ 5 /* 18/90-18/99 */,
+ 6 /* 18/100-18/109 */,
+ 7 /* 18/110-18/119 */,
+ 8 /* 18/120-18/129 */,
+ 9 /* 18/130-18/139 */,
+ 10 /* 18/140-18/149 */,
+ 11 /* 18/150-18/159 */,
+ 12 /* 18/160-18/169 */,
+ 14 /* 18/170-18/179 */,
+ 16 /* 18/180-18/189 */,
+ 18 /* 18/190-18/199 */,
+ 20 /* 18/200-18/209 */,
+ 20 /* 18/210-18/219 */,
+ 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- chance of avoiding "theft" and "falling"
+ */
+byte adj_dex_safe[] =
+{
+ 0 /* 3 */,
+ 1 /* 4 */,
+ 2 /* 5 */,
+ 3 /* 6 */,
+ 4 /* 7 */,
+ 5 /* 8 */,
+ 5 /* 9 */,
+ 6 /* 10 */,
+ 6 /* 11 */,
+ 7 /* 12 */,
+ 7 /* 13 */,
+ 8 /* 14 */,
+ 8 /* 15 */,
+ 9 /* 16 */,
+ 9 /* 17 */,
+ 10 /* 18/00-18/09 */,
+ 10 /* 18/10-18/19 */,
+ 15 /* 18/20-18/29 */,
+ 15 /* 18/30-18/39 */,
+ 20 /* 18/40-18/49 */,
+ 25 /* 18/50-18/59 */,
+ 30 /* 18/60-18/69 */,
+ 35 /* 18/70-18/79 */,
+ 40 /* 18/80-18/89 */,
+ 45 /* 18/90-18/99 */,
+ 50 /* 18/100-18/109 */,
+ 60 /* 18/110-18/119 */,
+ 70 /* 18/120-18/129 */,
+ 80 /* 18/130-18/139 */,
+ 90 /* 18/140-18/149 */,
+ 100 /* 18/150-18/159 */,
+ 100 /* 18/160-18/169 */,
+ 100 /* 18/170-18/179 */,
+ 100 /* 18/180-18/189 */,
+ 100 /* 18/190-18/199 */,
+ 100 /* 18/200-18/209 */,
+ 100 /* 18/210-18/219 */,
+ 100 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (CON) -- base regeneration rate
+ */
+byte adj_con_fix[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 0 /* 8 */,
+ 0 /* 9 */,
+ 0 /* 10 */,
+ 0 /* 11 */,
+ 0 /* 12 */,
+ 0 /* 13 */,
+ 1 /* 14 */,
+ 1 /* 15 */,
+ 1 /* 16 */,
+ 1 /* 17 */,
+ 2 /* 18/00-18/09 */,
+ 2 /* 18/10-18/19 */,
+ 2 /* 18/20-18/29 */,
+ 2 /* 18/30-18/39 */,
+ 2 /* 18/40-18/49 */,
+ 3 /* 18/50-18/59 */,
+ 3 /* 18/60-18/69 */,
+ 3 /* 18/70-18/79 */,
+ 3 /* 18/80-18/89 */,
+ 3 /* 18/90-18/99 */,
+ 4 /* 18/100-18/109 */,
+ 4 /* 18/110-18/119 */,
+ 5 /* 18/120-18/129 */,
+ 6 /* 18/130-18/139 */,
+ 6 /* 18/140-18/149 */,
+ 7 /* 18/150-18/159 */,
+ 7 /* 18/160-18/169 */,
+ 8 /* 18/170-18/179 */,
+ 8 /* 18/180-18/189 */,
+ 8 /* 18/190-18/199 */,
+ 9 /* 18/200-18/209 */,
+ 9 /* 18/210-18/219 */,
+ 9 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (CON) -- extra half-hitpoints per level (plus 128)
+ */
+byte adj_con_mhp[] =
+{
+ 128 + -5 /* 3 */,
+ 128 + -3 /* 4 */,
+ 128 + -2 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + 0 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 1 /* 15 */,
+ 128 + 1 /* 16 */,
+ 128 + 2 /* 17 */,
+ 128 + 3 /* 18/00-18/09 */,
+ 128 + 4 /* 18/10-18/19 */,
+ 128 + 4 /* 18/20-18/29 */,
+ 128 + 4 /* 18/30-18/39 */,
+ 128 + 4 /* 18/40-18/49 */,
+ 128 + 5 /* 18/50-18/59 */,
+ 128 + 6 /* 18/60-18/69 */,
+ 128 + 7 /* 18/70-18/79 */,
+ 128 + 8 /* 18/80-18/89 */,
+ 128 + 9 /* 18/90-18/99 */,
+ 128 + 10 /* 18/100-18/109 */,
+ 128 + 11 /* 18/110-18/119 */,
+ 128 + 12 /* 18/120-18/129 */,
+ 128 + 13 /* 18/130-18/139 */,
+ 128 + 14 /* 18/140-18/149 */,
+ 128 + 15 /* 18/150-18/159 */,
+ 128 + 16 /* 18/160-18/169 */,
+ 128 + 18 /* 18/170-18/179 */,
+ 128 + 20 /* 18/180-18/189 */,
+ 128 + 22 /* 18/190-18/199 */,
+ 128 + 25 /* 18/200-18/209 */,
+ 128 + 26 /* 18/210-18/219 */,
+ 128 + 27 /* 18/220+ */
+};
+
+
+/*
+ * This table is used to help calculate the number of blows the player can
+ * make in a single round of attacks (one player turn) with a normal weapon.
+ *
+ * This number ranges from a single blow/round for weak players to up to six
+ * blows/round for powerful warriors.
+ *
+ * Note that certain artifacts and ego-items give "bonus" blows/round.
+ *
+ * First, from the player class, we extract some values:
+ *
+ * Warrior --> num = 6; mul = 5; div = MAX(30, weapon_weight);
+ * Mage --> num = 4; mul = 2; div = MAX(40, weapon_weight);
+ * Priest --> num = 5; mul = 3; div = MAX(35, weapon_weight);
+ * Rogue --> num = 5; mul = 3; div = MAX(30, weapon_weight);
+ * Ranger --> num = 5; mul = 4; div = MAX(35, weapon_weight);
+ * Paladin --> num = 5; mul = 4; div = MAX(30, weapon_weight);
+ *
+ * To get "P", we look up the relevant "adj_str_blow[]" (see above),
+ * multiply it by "mul", and then divide it by "div", rounding down.
+ *
+ * To get "D", we look up the relevant "adj_dex_blow[]" (see above),
+ * note especially column 6 (DEX 18/101) and 11 (DEX 18/150).
+ *
+ * The player gets "blows_table[P][D]" blows/round, as shown below,
+ * up to a maximum of "num" blows/round, plus any "bonus" blows/round.
+ */
+byte blows_table[12][12] =
+{
+ /* P/D */
+ /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11+ */
+
+ /* 0 */
+ { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3 },
+
+ /* 1 */
+ { 1, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4 },
+
+ /* 2 */
+ { 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5 },
+
+ /* 3 */
+ { 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 5 },
+
+ /* 4 */
+ { 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5 },
+
+ /* 5 */
+ { 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5, 6 },
+
+ /* 6 */
+ { 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5, 6 },
+
+ /* 7 */
+ { 2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6 },
+
+ /* 8 */
+ { 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6 },
+
+ /* 9 */
+ { 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6 },
+
+ /* 10 */
+ { 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6 },
+
+ /* 11+ */
+ { 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6 },
+};
+
+
+s16b arena_monsters[MAX_ARENA_MONS] =
+{
+ 30, 43, 102, 118, 126, 149, 173,
+ 183, 188, 191, 216, 230, 238, 244,
+ 255, 262, 293, 297, 321, 349, 372,
+ 401, 415, 454, 464, 485, 538, 631,
+ 641
+};
+
+
+/*
+ * This table allows quick conversion from "speed" to "energy"
+ * The basic function WAS ((S>=110) ? (S-110) : (100 / (120-S)))
+ * Note that table access is *much* quicker than computation.
+ *
+ * Note that the table has been changed at high speeds. From
+ * "Slow (-40)" to "Fast (+30)" is pretty much unchanged, but
+ * at speeds above "Fast (+30)", one approaches an asymptotic
+ * effective limit of 50 energy per turn. This means that it
+ * is relatively easy to reach "Fast (+30)" and get about 40
+ * energy per turn, but then speed becomes very "expensive",
+ * and you must get all the way to "Fast (+50)" to reach the
+ * point of getting 45 energy per turn. After that point,
+ * further increases in speed are more or less pointless,
+ * except to balance out heavy inventory.
+ *
+ * Note that currently the fastest monster is "Fast (+30)".
+ *
+ * It should be possible to lower the energy threshold from
+ * 100 units to 50 units, though this may interact badly with
+ * the (compiled out) small random energy boost code. It may
+ * also tend to cause more "clumping" at high speeds.
+ */
+byte extract_energy[300] =
+{
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* S-50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* S-40 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ /* S-30 */ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
+ /* S-20 */ 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+ /* S-10 */ 5, 5, 5, 5, 6, 6, 7, 7, 8, 9,
+ /* Norm */ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ /* F+10 */ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ /* F+20 */ 30, 31, 32, 33, 34, 35, 36, 36, 37, 37,
+ /* F+30 */ 38, 38, 39, 39, 40, 40, 40, 41, 41, 41,
+ /* F+40 */ 42, 42, 42, 43, 43, 43, 44, 44, 44, 44,
+ /* F+50 */ 45, 45, 45, 45, 45, 46, 46, 46, 46, 46,
+ /* F+60 */ 47, 47, 47, 47, 47, 48, 48, 48, 48, 48,
+ /* F+70 */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Fast */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+};
+
+
+
+
+/*
+ * Base experience levels, may be adjusted up for race and/or class
+ */
+s32b player_exp[PY_MAX_LEVEL] =
+{
+ 10,
+ 25,
+ 45,
+ 70,
+ 100,
+ 140,
+ 200,
+ 280,
+ 380,
+ 500,
+ 650,
+ 850,
+ 1100,
+ 1400,
+ 1800,
+ 2300,
+ 2900,
+ 3600,
+ 4400,
+ 5400,
+ 6800,
+ 8400,
+ 10200,
+ 12500,
+ 17500,
+ 25000,
+ 35000L,
+ 50000L,
+ 75000L,
+ 100000L,
+ 150000L,
+ 200000L,
+ 275000L,
+ 350000L,
+ 450000L,
+ 550000L,
+ 700000L,
+ 850000L,
+ 1000000L,
+ 1250000L,
+ 1500000L,
+ 1800000L,
+ 2100000L,
+ 2400000L,
+ 2700000L,
+ 3000000L,
+ 3500000L,
+ 4000000L,
+ 4500000L,
+ 5000000L
+};
+
+
+/*
+ * Player Sexes
+ *
+ * Title,
+ * Winner
+ */
+player_sex sex_info[MAX_SEXES] =
+{
+ {
+ "Female",
+ "Queen"
+ },
+
+{
+"Male",
+"King"
+},
+{
+"Neuter",
+"Ruler"
+}
+};
+
+/*
+ * Hack -- the "basic" color names (see "TERM_xxx")
+ */
+cptr color_names[16] =
+{
+ "Dark",
+ "White",
+ "Slate",
+ "Orange",
+ "Red",
+ "Green",
+ "Blue",
+ "Umber",
+ "Light Dark",
+ "Light Slate",
+ "Violet",
+ "Yellow",
+ "Light Red",
+ "Light Green",
+ "Light Blue",
+ "Light Umber",
+};
+
+
+/*
+ * Abbreviations of healthy stats
+ */
+cptr stat_names[6] =
+{
+ "STR", "INT", "WIS", "DEX", "CON", "CHR"
+};
+
+/*
+ * Abbreviations of damaged stats
+ */
+cptr stat_names_reduced[6] =
+{
+ "Str", "Int", "Wis", "Dex", "Con", "Chr"
+};
+
+
+/*
+ * Certain "screens" always use the main screen, including News, Birth,
+ * Dungeon, Tomb-stone, High-scores, Macros, Colors, Visuals, Options.
+ *
+ * Later, special flags may allow sub-windows to "steal" stuff from the
+ * main window, including File dump (help), File dump (artifacts, uniques),
+ * Character screen, Small scale map, Previous Messages, Store screen, etc.
+ *
+ * The "ctrl-i" (tab) command flips the "Display inven/equip" and "Display
+ * equip/inven" flags for all windows.
+ *
+ * The "ctrl-g" command (or pseudo-command) should perhaps grab a snapshot
+ * of the main screen into any interested windows.
+ */
+cptr window_flag_desc[32] =
+{
+ "Display inven/equip",
+ "Display equip/inven",
+ NULL,
+ "Display character",
+ "Show visible monsters",
+ NULL,
+ "Display messages",
+ "Display overhead view",
+ "Display monster recall",
+ "Display object recall",
+ NULL,
+ "Display snap-shot",
+ NULL,
+ NULL,
+ "Display borg messages",
+ "Display borg status",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+/*
+ * Available Options
+ *
+ * Option Screen Sets:
+ *
+ * Set 1: User Interface
+ * Set 2: Disturbance
+ * Set 3: Inventory
+ * Set 4: Game Play
+ * Set 5: ToME
+ * Set 6: Birth
+ *
+ * Note that bits 28-31 of set 0 are currently unused.
+ */
+option_type option_info[] =
+{
+ /*** User-Interface ***/
+
+ { &rogue_like_commands, FALSE, 1, 0,
+ "rogue_like_commands", "Rogue-like commands" },
+
+ { &quick_messages, TRUE, 1, 1,
+ "quick_messages", "Activate quick messages" },
+
+ { &other_query_flag, FALSE, 1, 2,
+ "other_query_flag", "Prompt for various information" },
+
+ { &carry_query_flag, FALSE, 1, 3,
+ "carry_query_flag", "Prompt before picking things up" },
+
+ { &use_old_target, FALSE, 1, 4,
+ "use_old_target", "Use old target by default" },
+
+ { &always_pickup, FALSE, 1, 5,
+ "always_pickup", "Pick things up by default" },
+
+ { &prompt_pickup_heavy, TRUE, 1, 6,
+ "prompt_pickup_heavy", "Prompt before picking up heavy objects" },
+
+ { &always_repeat, TRUE, 1, 7,
+ "always_repeat", "Repeat obvious commands" },
+
+ { &depth_in_feet, FALSE, 1, 8,
+ "depth_in_feet", "Show dungeon level in feet" },
+
+ { &stack_force_notes, TRUE, 1, 9,
+ "stack_force_notes", "Merge inscriptions when stacking" },
+
+ { &stack_force_costs, FALSE, 1, 10,
+ "stack_force_costs", "Merge discounts when stacking" },
+
+ { &show_labels, TRUE, 1, 11,
+ "show_labels", "Show labels in object listings" },
+
+ { &show_weights, TRUE, 1, 12,
+ "show_weights", "Show weights in object listings" },
+
+ { &show_inven_graph, TRUE, 1, 13,
+ "show_inven_graph", "Show graphics in inventory list" },
+
+ { &show_equip_graph, TRUE, 1, 14,
+ "show_equip_graph", "Show graphics in equipment list" },
+
+ { &show_store_graph, TRUE, 1, 15,
+ "show_store_graph", "Show graphics in stores" },
+
+ { &show_choices, TRUE, 1, 16,
+ "show_choices", "Show choices in certain sub-windows" },
+
+ { &show_details, TRUE, 1, 17,
+ "show_details", "Show details in certain sub-windows" },
+
+ { &ring_bell, FALSE, 1, 18,
+ "ring_bell", "Audible bell (on errors, etc)" },
+ /* Changed to default to FALSE -- it's so extremely annoying!!! -TY */
+
+ /*** Disturbance ***/
+
+ { &find_ignore_stairs, FALSE, 2, 0,
+ "find_ignore_stairs", "Run past stairs" },
+
+ { &find_ignore_doors, TRUE, 2, 1,
+ "find_ignore_doors", "Run through open doors" },
+
+ { &find_cut, FALSE, 2, 2,
+ "find_cut", "Run past known corners" },
+
+ { &find_examine, TRUE, 2, 3,
+ "find_examine", "Run into potential corners" },
+
+ { &disturb_move, FALSE, 2, 4,
+ "disturb_move", "Disturb whenever any monster moves" },
+
+ { &disturb_near, TRUE, 2, 5,
+ "disturb_near", "Disturb whenever viewable monster moves" },
+
+ { &disturb_panel, TRUE, 2, 6,
+ "disturb_panel", "Disturb whenever map panel changes" },
+
+ { &disturb_detect, TRUE, 2, 21,
+ "disturb_detect", "Disturb whenever leaving trap-detected area" },
+
+ { &disturb_state, TRUE, 2, 7,
+ "disturb_state", "Disturb whenever player state changes" },
+
+ { &disturb_minor, TRUE, 2, 8,
+ "disturb_minor", "Disturb whenever boring things happen" },
+
+ { &disturb_other, FALSE, 2, 9,
+ "disturb_other", "Disturb whenever random things happen" },
+
+ { &alert_hitpoint, FALSE, 2, 10,
+ "alert_hitpoint", "Alert user to critical hitpoints" },
+
+ { &alert_failure, FALSE, 2, 11,
+ "alert_failure", "Alert user to various failures" },
+
+ { &last_words, TRUE, 2, 12,
+ "last_words", "Get last words when the character dies" },
+
+ { &speak_unique, TRUE, 2, 13,
+ "speak_unique", "Allow shopkeepers and uniques to speak" },
+
+ { &auto_destroy, TRUE, 2, 14,
+ "auto_destroy", "No query to destroy known worthless items" },
+
+ { &wear_confirm, TRUE, 2, 15,
+ "confirm_wear", "Confirm to wear/wield known cursed items" },
+
+ { &confirm_stairs, FALSE, 2, 16,
+ "confirm_stairs", "Prompt before exiting a dungeon level" },
+
+ { &disturb_pets, FALSE, 2, 17,
+ "disturb_pets", "Disturb when visible pets move" },
+
+ { &easy_open, TRUE, 2, 18,
+ "easy_open", "Automatically open doors" },
+
+ { &easy_disarm, TRUE, 2, 19,
+ "easy_disarm", "Automatically disarm traps" },
+
+ { &easy_tunnel, FALSE, 2, 20,
+ "easy_tunnel", "Automatically tunnel walls" },
+
+ /*** Game-Play ***/
+
+ { &auto_haggle, TRUE, 3, 0,
+ "auto_haggle", "Auto-haggle in stores" },
+
+ { &auto_scum, TRUE, 3, 1,
+ "auto_scum", "Auto-scum for good levels" },
+
+ { &stack_allow_items, TRUE, 3, 2,
+ "stack_allow_items", "Allow weapons and armour to stack" },
+
+ { &stack_allow_wands, TRUE, 3, 3,
+ "stack_allow_wands", "Allow wands/staffs/rods to stack" },
+
+ { &expand_look, FALSE, 3, 4,
+ "expand_look", "Expand the power of the look command" },
+
+ { &expand_list, FALSE, 3, 5,
+ "expand_list", "Expand the power of the list commands" },
+
+ { &view_perma_grids, TRUE, 3, 6,
+ "view_perma_grids", "Map remembers all perma-lit grids" },
+
+ { &view_torch_grids, FALSE, 3, 7,
+ "view_torch_grids", "Map remembers all torch-lit grids" },
+
+ { &monster_lite, TRUE, 3, 19,
+ "monster_lite", "Allow some monsters to carry light" },
+
+ { &dungeon_align, TRUE, 3, 8,
+ "dungeon_align", "Generate dungeons with aligned rooms" },
+
+ { &dungeon_stair, TRUE, 3, 9,
+ "dungeon_stair", "Generate dungeons with connected stairs" },
+
+ { &flow_by_sound, FALSE, 3, 10,
+ "flow_by_sound", "Monsters chase current location (v.slow)" },
+
+ { &player_symbols, FALSE, 3, 12,
+ "player_symbols", "Use special symbols for the player char"},
+
+ { &plain_descriptions, TRUE, 3, 13,
+ "plain_descriptions", "Plain object descriptions" },
+
+ { &smart_learn, FALSE, 3, 14,
+ "smart_learn", "Monsters learn from their mistakes" },
+
+ { &smart_cheat, FALSE, 3, 15,
+ "smart_cheat", "Monsters exploit players weaknesses" },
+
+ { &stupid_monsters, FALSE, 3, 16,
+ "stupid_monsters", "Monsters behave stupidly" },
+
+ { &small_levels, TRUE, 3, 17,
+ "small_levels", "Allow unusually small dungeon levels" },
+
+ { &empty_levels, TRUE, 3, 18,
+ "empty_levels", "Allow empty 'arena' levels" },
+
+ /*** Efficiency ***/
+
+ { &view_reduce_lite, FALSE, 4, 0,
+ "view_reduce_lite", "Reduce lite-radius when running" },
+
+ { &view_reduce_view, FALSE, 4, 1,
+ "view_reduce_view", "Reduce view-radius in town" },
+
+ { &avoid_abort, FALSE, 4, 2,
+ "avoid_abort", "Avoid checking for user abort" },
+
+ { &avoid_shimmer, FALSE, 4, 17,
+ "avoid_shimmer", "Avoid extra shimmering (fast)" },
+
+ { &avoid_other, FALSE, 4, 3,
+ "avoid_other", "Avoid processing special colors (fast)" },
+
+ { &flush_failure, TRUE, 4, 4,
+ "flush_failure", "Flush input on various failures" },
+
+ { &flush_disturb, FALSE, 4, 5,
+ "flush_disturb", "Flush input whenever disturbed" },
+
+ { &flush_command, FALSE, 4, 6,
+ "flush_command", "Flush input before every command" },
+
+ { &fresh_before, TRUE, 4, 7,
+ "fresh_before", "Flush output before every command" },
+
+ { &fresh_after, FALSE, 4, 8,
+ "fresh_after", "Flush output after every command" },
+
+ { &fresh_message, FALSE, 4, 9,
+ "fresh_message", "Flush output after every message" },
+
+ { &hilite_player, FALSE, 4, 11,
+ "hilite_player", "Hilite the player with the cursor" },
+
+ { &view_yellow_lite, FALSE, 4, 12,
+ "view_yellow_lite", "Use special colors for torch-lit grids" },
+
+ { &view_bright_lite, FALSE, 4, 13,
+ "view_bright_lite", "Use special colors for 'viewable' grids" },
+
+ { &view_granite_lite, FALSE, 4, 14,
+ "view_granite_lite", "Use special colors for wall grids (slow)" },
+
+ { &view_special_lite, FALSE, 4, 15,
+ "view_special_lite", "Use special colors for floor grids (slow)" },
+
+ { &center_player, FALSE, 4, 16,
+ "center_player", "Center the view on the player (very slow)" },
+
+ /*** ToME options ***/
+
+ { &option_ingame_help, TRUE, 5, 1,
+ "ingame_help", "Ingame contextual help" },
+
+ { &exp_need, FALSE, 5, 2,
+ "exp_need", "Show the experience needed for next level" },
+
+ { &autoload_old_colors, FALSE, 5, 3,
+ "old_colors", "Use the old(Z) coloring scheme(reload the game)" },
+
+ { &auto_more, FALSE, 5, 4,
+ "auto_more", "Automatically clear '-more-' prompts" },
+
+ { &player_char_health, TRUE, 5, 6,
+ "player_char_health", "Player char represent his/her health" },
+
+ { &linear_stats, TRUE, 5, 7,
+ "linear_stats", "Stats are represented in a linear way" },
+
+ { &inventory_no_move, FALSE, 5, 8,
+ "inventory_no_move", "In option windows, just omit the select char" },
+
+
+ /*** Birth Options ***/
+
+ { &maximize, TRUE, 6, 1,
+ "maximize", "Maximise stats" },
+
+ { &preserve, TRUE, 6, 2,
+ "preserve", "Preserve artifacts" },
+
+ { &autoroll, TRUE, 6, 3,
+ "autoroll", "Specify 'minimal' stats" },
+
+ { &point_based, FALSE, 6, 17,
+ "point_based", "Generate character using a point system" },
+
+ { &ironman_rooms, FALSE, 6, 6,
+ "ironman_rooms", "Always generate very unusual rooms" },
+
+ { &take_notes, TRUE, 6, 7,
+ "take_notes", "Allow notes to be written to a file" },
+
+ { &auto_notes, TRUE, 6, 8,
+ "auto_notes", "Automatically note important events" },
+
+ { &fast_autoroller, FALSE, 6, 10,
+ "fast_autoroller", "Fast autoroller(NOT on multiuser systems)" },
+
+ { &joke_monsters, FALSE, 6, 14,
+ "joke_monsters", "Allow use of some 'joke' monsters" },
+
+ /* XXX */
+
+ { &always_small_level, FALSE, 6, 16,
+ "always_small_level", "Always make small levels" },
+
+ { &fate_option, TRUE, 6, 18,
+ "fate_option", "You can receive fates, good or bad" },
+
+ /* XXX 17 is used BEFORE */
+
+ /*** Stacking ***/
+
+ { &testing_stack, TRUE, 7, 0,
+ "testing_stack", "Allow objects to stack on floor" },
+
+ { &testing_carry, TRUE, 7, 1,
+ "testing_carry", "Allow monsters to carry objects" },
+
+
+ /*** End of Table ***/
+
+ { NULL, 0, 0, 0,
+ NULL, NULL }
+};
+
+
+cptr chaos_patrons[MAX_PATRON] =
+{
+ "Slortar",
+ "Mabelode",
+ "Chardros",
+ "Hionhurn",
+ "Xiombarg",
+
+ "Pyaray",
+ "Balaan",
+ "Arioch",
+ "Eequor",
+ "Narjhan",
+
+ "Balo",
+ "Khorne",
+ "Slaanesh",
+ "Nurgle",
+ "Tzeentch",
+
+ "Khaine"
+};
+
+int chaos_stats[MAX_PATRON] =
+{
+ A_CON, /* Slortar */
+ A_CON, /* Mabelode */
+ A_STR, /* Chardros */
+ A_STR, /* Hionhurn */
+ A_STR, /* Xiombarg */
+
+ A_INT, /* Pyaray */
+ A_STR, /* Balaan */
+ A_INT, /* Arioch */
+ A_CON, /* Eequor */
+ A_CHR, /* Narjhan */
+
+ -1, /* Balo */
+ A_STR, /* Khorne */
+ A_CHR, /* Slaanesh */
+ A_CON, /* Nurgle */
+ A_INT, /* Tzeentch */
+
+ A_STR, /* Khaine */
+};
+
+
+
+
+int chaos_rewards[MAX_PATRON][20] =
+{
+ /* Slortar the Old: */
+ {
+ REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_POLY_SLF,
+ REW_POLY_SLF, REW_POLY_SLF, REW_GAIN_ABL, REW_GAIN_ABL, REW_GAIN_EXP,
+ REW_GOOD_OBJ, REW_CHAOS_WP, REW_GREA_OBJ, REW_AUGM_ABL, REW_AUGM_ABL
+ },
+
+ /* Mabelode the Faceless: */
+ {
+ REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_H_SUMMON, REW_SUMMON_M,
+ REW_SUMMON_M, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_POLY_WND,
+ REW_POLY_SLF, REW_HEAL_FUL, REW_HEAL_FUL, REW_GAIN_ABL, REW_SER_UNDE,
+ REW_CHAOS_WP, REW_GOOD_OBJ, REW_GOOD_OBJ, REW_GOOD_OBS, REW_GOOD_OBS
+ },
+
+ /* Chardros the Reaper: */
+ {
+ REW_WRATH, REW_WRATH, REW_HURT_LOT, REW_PISS_OFF, REW_H_SUMMON,
+ REW_SUMMON_M, REW_IGNORE, REW_IGNORE, REW_DESTRUCT, REW_SER_UNDE,
+ REW_GENOCIDE, REW_MASS_GEN, REW_MASS_GEN, REW_DISPEL_C, REW_GOOD_OBJ,
+ REW_CHAOS_WP, REW_GOOD_OBS, REW_GOOD_OBS, REW_AUGM_ABL, REW_AUGM_ABL
+ },
+
+ /* Hionhurn the Executioner: */
+ {
+ REW_WRATH, REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL,
+ REW_IGNORE, REW_IGNORE, REW_SER_UNDE, REW_DESTRUCT, REW_GENOCIDE,
+ REW_MASS_GEN, REW_MASS_GEN, REW_HEAL_FUL, REW_GAIN_ABL, REW_GAIN_ABL,
+ REW_CHAOS_WP, REW_GOOD_OBS, REW_GOOD_OBS, REW_AUGM_ABL, REW_AUGM_ABL
+ },
+
+ /* Xiombarg the Sword-Queen: */
+ {
+ REW_TY_CURSE, REW_TY_CURSE, REW_PISS_OFF, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND, REW_POLY_WND,
+ REW_GENOCIDE, REW_DISPEL_C, REW_GOOD_OBJ, REW_GOOD_OBJ, REW_SER_MONS,
+ REW_GAIN_ABL, REW_CHAOS_WP, REW_GAIN_EXP, REW_AUGM_ABL, REW_GOOD_OBS
+ },
+
+
+ /* Pyaray the Tentacled Whisperer of Impossible Secretes: */
+ {
+ REW_WRATH, REW_TY_CURSE, REW_PISS_OFF, REW_H_SUMMON, REW_H_SUMMON,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_POLY_SLF,
+ REW_POLY_SLF, REW_SER_DEMO, REW_HEAL_FUL, REW_GAIN_ABL, REW_GAIN_ABL,
+ REW_CHAOS_WP, REW_DO_HAVOC, REW_GOOD_OBJ, REW_GREA_OBJ, REW_GREA_OBS
+ },
+
+ /* Balaan the Grim: */
+ {
+ REW_TY_CURSE, REW_HURT_LOT, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL,
+ REW_SUMMON_M, REW_LOSE_EXP, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND,
+ REW_SER_UNDE, REW_HEAL_FUL, REW_HEAL_FUL, REW_GAIN_EXP, REW_GAIN_EXP,
+ REW_CHAOS_WP, REW_GOOD_OBJ, REW_GOOD_OBS, REW_GREA_OBS, REW_AUGM_ABL
+ },
+
+ /* Arioch, Duke of Hell: */
+ {
+ REW_WRATH, REW_PISS_OFF, REW_RUIN_ABL, REW_LOSE_EXP, REW_H_SUMMON,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_SLF,
+ REW_POLY_SLF, REW_MASS_GEN, REW_SER_DEMO, REW_HEAL_FUL, REW_CHAOS_WP,
+ REW_CHAOS_WP, REW_GOOD_OBJ, REW_GAIN_EXP, REW_GREA_OBJ, REW_AUGM_ABL
+ },
+
+ /* Eequor, Blue Lady of Dismay: */
+ {
+ REW_WRATH, REW_TY_CURSE, REW_PISS_OFF, REW_CURSE_WP, REW_RUIN_ABL,
+ REW_IGNORE, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND,
+ REW_GOOD_OBJ, REW_GOOD_OBJ, REW_SER_MONS, REW_HEAL_FUL, REW_GAIN_EXP,
+ REW_GAIN_ABL, REW_CHAOS_WP, REW_GOOD_OBS, REW_GREA_OBJ, REW_AUGM_ABL
+ },
+
+ /* Narjhan, Lord of Beggars: */
+ {
+ REW_WRATH, REW_CURSE_AR, REW_CURSE_WP, REW_CURSE_WP, REW_CURSE_AR,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF,
+ REW_POLY_WND, REW_HEAL_FUL, REW_HEAL_FUL, REW_GAIN_EXP, REW_AUGM_ABL,
+ REW_GOOD_OBJ, REW_GOOD_OBJ, REW_CHAOS_WP, REW_GREA_OBJ, REW_GREA_OBS
+ },
+
+ /* Balo the Jester: */
+ {
+ REW_WRATH, REW_SER_DEMO, REW_CURSE_WP, REW_CURSE_AR, REW_LOSE_EXP,
+ REW_GAIN_ABL, REW_LOSE_ABL, REW_POLY_WND, REW_POLY_SLF, REW_IGNORE,
+ REW_DESTRUCT, REW_MASS_GEN, REW_CHAOS_WP, REW_GREA_OBJ, REW_HURT_LOT,
+ REW_AUGM_ABL, REW_RUIN_ABL, REW_H_SUMMON, REW_GREA_OBS, REW_AUGM_ABL
+ },
+
+ /* Khorne the Bloodgod: */
+ {
+ REW_WRATH, REW_HURT_LOT, REW_HURT_LOT, REW_H_SUMMON, REW_H_SUMMON,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_SER_MONS, REW_SER_DEMO,
+ REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_GOOD_OBJ, REW_GOOD_OBJ,
+ REW_CHAOS_WP, REW_GOOD_OBS, REW_GOOD_OBS, REW_GREA_OBJ, REW_GREA_OBS
+ },
+
+ /* Slaanesh: */
+ {
+ REW_WRATH, REW_PISS_OFF, REW_PISS_OFF, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_LOSE_EXP, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_SER_DEMO,
+ REW_POLY_SLF, REW_HEAL_FUL, REW_HEAL_FUL, REW_GOOD_OBJ, REW_GAIN_EXP,
+ REW_GAIN_EXP, REW_CHAOS_WP, REW_GAIN_ABL, REW_GREA_OBJ, REW_AUGM_ABL
+ },
+
+ /* Nurgle: */
+ {
+ REW_WRATH, REW_PISS_OFF, REW_HURT_LOT, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_LOSE_EXP, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_SLF,
+ REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_GOOD_OBJ, REW_GAIN_ABL,
+ REW_GAIN_ABL, REW_SER_UNDE, REW_CHAOS_WP, REW_GREA_OBJ, REW_AUGM_ABL
+ },
+
+ /* Tzeentch: */
+ {
+ REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_LOSE_EXP, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_SLF,
+ REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_CHAOS_WP, REW_GREA_OBJ,
+ REW_GAIN_ABL, REW_GAIN_ABL, REW_GAIN_EXP, REW_GAIN_EXP, REW_AUGM_ABL
+ },
+
+ /* Khaine: */
+ {
+ REW_WRATH, REW_HURT_LOT, REW_PISS_OFF, REW_LOSE_ABL, REW_LOSE_EXP,
+ REW_IGNORE, REW_IGNORE, REW_DISPEL_C, REW_DO_HAVOC, REW_DO_HAVOC,
+ REW_POLY_SLF, REW_POLY_SLF, REW_GAIN_EXP, REW_GAIN_ABL, REW_GAIN_ABL,
+ REW_SER_MONS, REW_GOOD_OBJ, REW_CHAOS_WP, REW_GREA_OBJ, REW_GOOD_OBS
+ }
+};
+
+/* Names used for random artifact name generation */
+cptr artifact_names_list =
+ "adanedhel\n"
+ "adurant\n"
+ "aeglos\n"
+ "aegnor\n"
+ "aelin\n"
+ "aeluin\n"
+ "aerandir\n"
+ "aerin\n"
+ "agarwaen\n"
+ "aglareb\n"
+ "aglarond\n"
+ "aglon\n"
+ "ainulindale\n"
+ "ainur\n"
+ "alcarinque\n"
+ "aldaron\n"
+ "aldudenie\n"
+ "almaren\n"
+ "alqualonde\n"
+ "aman\n"
+ "amandil\n"
+ "amarie\n"
+ "amarth\n"
+ "amlach\n"
+ "amon\n"
+ "amras\n"
+ "amrod\n"
+ "anach\n"
+ "anar\n"
+ "anarion\n"
+ "ancalagon\n"
+ "ancalimon\n"
+ "anarrima\n"
+ "andor\n"
+ "andram\n"
+ "androth\n"
+ "anduin\n"
+ "andunie\n"
+ "anfauglir\n"
+ "anfauglith\n"
+ "angainor\n"
+ "angband\n"
+ "anghabar\n"
+ "anglachel\n"
+ "angrenost\n"
+ "angrim\n"
+ "angrist\n"
+ "angrod\n"
+ "anguirel\n"
+ "annael\n"
+ "annatar\n"
+ "annon\n"
+ "annuminas\n"
+ "apanonar\n"
+ "aradan\n"
+ "aragorn\n"
+ "araman\n"
+ "aranel\n"
+ "aranruth\n"
+ "aranwe\n"
+ "aras\n"
+ "aratan\n"
+ "aratar\n"
+ "arathorn\n"
+ "arda\n"
+ "ard-galen\n"
+ "aredhel\n"
+ "ar-feiniel\n"
+ "argonath\n"
+ "arien\n"
+ "armenelos\n"
+ "arminas\n"
+ "arnor\n"
+ "aros\n"
+ "arossiach\n"
+ "arthad\n"
+ "arvernien\n"
+ "arwen\n"
+ "ascar\n"
+ "astaldo\n"
+ "atalante\n"
+ "atanamir\n"
+ "atanatari\n"
+ "atani\n"
+ "aule\n"
+ "avallone\n"
+ "avari\n"
+ "avathar\n"
+ "balan\n"
+ "balar\n"
+ "balrog\n"
+ "barad\n"
+ "baragund\n"
+ "barahir\n"
+ "baran\n"
+ "baranduin\n"
+ "bar\n"
+ "bauglir\n"
+ "beleg\n"
+ "belegaer\n"
+ "belegost\n"
+ "belegund\n"
+ "beleriand\n"
+ "belfalas\n"
+ "belthil\n"
+ "belthronding\n"
+ "beor\n"
+ "beraid\n"
+ "bereg\n"
+ "beren\n"
+ "boromir\n"
+ "boron\n"
+ "bragollach\n"
+ "brandir\n"
+ "bregolas\n"
+ "bregor\n"
+ "brethil\n"
+ "brilthor\n"
+ "brithiach\n"
+ "brithombar\n"
+ "brithon\n"
+ "cabed\n"
+ "calacirya\n"
+ "calaquendi\n"
+ "calenardhon\n"
+ "calion\n"
+ "camlost\n"
+ "caragdur\n"
+ "caranthir\n"
+ "carcharoth\n"
+ "cardolan\n"
+ "carnil\n"
+ "celeborn\n"
+ "celebrant\n"
+ "celebrimbor\n"
+ "celebrindal\n"
+ "celebros\n"
+ "celegorm\n"
+ "celon\n"
+ "cirdan\n"
+ "cirith\n"
+ "cirth\n"
+ "ciryatan\n"
+ "ciryon\n"
+ "coimas\n"
+ "corollaire\n"
+ "crissaegrim\n"
+ "cuarthal\n"
+ "cuivienen\n"
+ "culurien\n"
+ "curufin\n"
+ "curufinwe\n"
+ "curunir\n"
+ "cuthalion\n"
+ "daedeloth\n"
+ "daeron\n"
+ "dagnir\n"
+ "dagor\n"
+ "dagorlad\n"
+ "dairuin\n"
+ "danwedh\n"
+ "delduwath\n"
+ "denethor\n"
+ "dimbar\n"
+ "dimrost\n"
+ "dinen\n"
+ "dior\n"
+ "dirnen\n"
+ "dolmed\n"
+ "doriath\n"
+ "dorlas\n"
+ "dorthonion\n"
+ "draugluin\n"
+ "drengist\n"
+ "duath\n"
+ "duinath\n"
+ "duilwen\n"
+ "dunedain\n"
+ "dungortheb\n"
+ "earendil\n"
+ "earendur\n"
+ "earnil\n"
+ "earnur\n"
+ "earrame\n"
+ "earwen\n"
+ "echor\n"
+ "echoriath\n"
+ "ecthelion\n"
+ "edain\n"
+ "edrahil\n"
+ "eglador\n"
+ "eglarest\n"
+ "eglath\n"
+ "eilinel\n"
+ "eithel\n"
+ "ekkaia\n"
+ "elbereth\n"
+ "eldalie\n"
+ "eldalieva\n"
+ "eldamar\n"
+ "eldar\n"
+ "eledhwen\n"
+ "elemmire\n"
+ "elende\n"
+ "elendil\n"
+ "elendur\n"
+ "elenna\n"
+ "elentari\n"
+ "elenwe\n"
+ "elerrina\n"
+ "elleth\n"
+ "elmoth\n"
+ "elostirion\n"
+ "elrond\n"
+ "elros\n"
+ "elu\n"
+ "eluchil\n"
+ "elured\n"
+ "elurin\n"
+ "elwe\n"
+ "elwing\n"
+ "emeldir\n"
+ "endor\n"
+ "engrin\n"
+ "engwar\n"
+ "eol\n"
+ "eonwe\n"
+ "ephel\n"
+ "erchamion\n"
+ "ereb\n"
+ "ered\n"
+ "erech\n"
+ "eregion\n"
+ "ereinion\n"
+ "erellont\n"
+ "eressea\n"
+ "eriador\n"
+ "eru\n"
+ "esgalduin\n"
+ "este\n"
+ "estel\n"
+ "estolad\n"
+ "ethir\n"
+ "ezellohar\n"
+ "faelivrin\n"
+ "falas\n"
+ "falathar\n"
+ "falathrim\n"
+ "falmari\n"
+ "faroth\n"
+ "fauglith\n"
+ "feanor\n"
+ "feanturi\n"
+ "felagund\n"
+ "finarfin\n"
+ "finduilas\n"
+ "fingolfin\n"
+ "fingon\n"
+ "finwe\n"
+ "firimar\n"
+ "formenos\n"
+ "fornost\n"
+ "frodo\n"
+ "fuin\n"
+ "fuinur\n"
+ "gabilgathol\n"
+ "galad\n"
+ "galadriel\n"
+ "galathilion\n"
+ "galdor\n"
+ "galen\n"
+ "galvorn\n"
+ "gandalf\n"
+ "gaurhoth\n"
+ "gelion\n"
+ "gelmir\n"
+ "gelydh\n"
+ "gil\n"
+ "gildor\n"
+ "giliath\n"
+ "ginglith\n"
+ "girith\n"
+ "glaurung\n"
+ "glingal\n"
+ "glirhuin\n"
+ "gloredhel\n"
+ "glorfindel\n"
+ "golodhrim\n"
+ "gondolin\n"
+ "gondor\n"
+ "gonnhirrim\n"
+ "gorgoroth\n"
+ "gorlim\n"
+ "gorthaur\n"
+ "gorthol\n"
+ "gothmog\n"
+ "guilin\n"
+ "guinar\n"
+ "guldur\n"
+ "gundor\n"
+ "gurthang\n"
+ "gwaith\n"
+ "gwareth\n"
+ "gwindor\n"
+ "hadhodrond\n"
+ "hador\n"
+ "haladin\n"
+ "haldad\n"
+ "haldan\n"
+ "haldar\n"
+ "haldir\n"
+ "haleth\n"
+ "halmir\n"
+ "handir\n"
+ "harad\n"
+ "hareth\n"
+ "hathaldir\n"
+ "hathol\n"
+ "haudh\n"
+ "helcar\n"
+ "helcaraxe\n"
+ "helevorn\n"
+ "helluin\n"
+ "herumor\n"
+ "herunumen\n"
+ "hildorien\n"
+ "himlad\n"
+ "himring\n"
+ "hirilorn\n"
+ "hisilome\n"
+ "hithaeglir\n"
+ "hithlum\n"
+ "hollin\n"
+ "huan\n"
+ "hunthor\n"
+ "huor\n"
+ "hurin\n"
+ "hyarmendacil\n"
+ "hyarmentir\n"
+ "iant\n"
+ "iaur\n"
+ "ibun\n"
+ "idril\n"
+ "illuin\n"
+ "ilmare\n"
+ "ilmen\n"
+ "iluvatar\n"
+ "imlach\n"
+ "imladris\n"
+ "indis\n"
+ "ingwe\n"
+ "irmo\n"
+ "isil\n"
+ "isildur\n"
+ "istari\n"
+ "ithil\n"
+ "ivrin\n"
+ "kelvar\n"
+ "kementari\n"
+ "ladros\n"
+ "laiquendi\n"
+ "lalaith\n"
+ "lamath\n"
+ "lammoth\n"
+ "lanthir\n"
+ "laurelin\n"
+ "leithian\n"
+ "legolin\n"
+ "lembas\n"
+ "lenwe\n"
+ "linaewen\n"
+ "lindon\n"
+ "lindorie\n"
+ "loeg\n"
+ "lomelindi\n"
+ "lomin\n"
+ "lomion\n"
+ "lorellin\n"
+ "lorien\n"
+ "lorindol\n"
+ "losgar\n"
+ "lothlann\n"
+ "lothlorien\n"
+ "luin\n"
+ "luinil\n"
+ "lumbar\n"
+ "luthien\n"
+ "mablung\n"
+ "maedhros\n"
+ "maeglin\n"
+ "maglor\n"
+ "magor\n"
+ "mahanaxar\n"
+ "mahtan\n"
+ "maiar\n"
+ "malduin\n"
+ "malinalda\n"
+ "mandos\n"
+ "manwe\n"
+ "mardil\n"
+ "melian\n"
+ "melkor\n"
+ "menegroth\n"
+ "meneldil\n"
+ "menelmacar\n"
+ "meneltarma\n"
+ "minas\n"
+ "minastir\n"
+ "mindeb\n"
+ "mindolluin\n"
+ "mindon\n"
+ "minyatur\n"
+ "mirdain\n"
+ "miriel\n"
+ "mithlond\n"
+ "mithrandir\n"
+ "mithrim\n"
+ "mordor\n"
+ "morgoth\n"
+ "morgul\n"
+ "moria\n"
+ "moriquendi\n"
+ "mormegil\n"
+ "morwen\n"
+ "nahar\n"
+ "naeramarth\n"
+ "namo\n"
+ "nandor\n"
+ "nargothrond\n"
+ "narog\n"
+ "narsil\n"
+ "narsilion\n"
+ "narya\n"
+ "nauglamir\n"
+ "naugrim\n"
+ "ndengin\n"
+ "neithan\n"
+ "neldoreth\n"
+ "nenar\n"
+ "nenning\n"
+ "nenuial\n"
+ "nenya\n"
+ "nerdanel\n"
+ "nessa\n"
+ "nevrast\n"
+ "nibin\n"
+ "nienna\n"
+ "nienor\n"
+ "nimbrethil\n"
+ "nimloth\n"
+ "nimphelos\n"
+ "nimrais\n"
+ "nimras\n"
+ "ningloron\n"
+ "niniel\n"
+ "ninniach\n"
+ "ninquelote\n"
+ "niphredil\n"
+ "nirnaeth\n"
+ "nivrim\n"
+ "noegyth\n"
+ "nogrod\n"
+ "noldolante\n"
+ "noldor\n"
+ "numenor\n"
+ "nurtale\n"
+ "obel\n"
+ "ohtar\n"
+ "oiolosse\n"
+ "oiomure\n"
+ "olorin\n"
+ "olvar\n"
+ "olwe\n"
+ "ondolinde\n"
+ "orfalch\n"
+ "ormal\n"
+ "orocarni\n"
+ "orodreth\n"
+ "orodruin\n"
+ "orome\n"
+ "oromet\n"
+ "orthanc\n"
+ "osgiliath\n"
+ "osse\n"
+ "ossiriand\n"
+ "palantir\n"
+ "pelargir\n"
+ "pelori\n"
+ "periannath\n"
+ "quendi\n"
+ "quenta\n"
+ "quenya\n"
+ "radagast\n"
+ "radhruin\n"
+ "ragnor\n"
+ "ramdal\n"
+ "rana\n"
+ "rathloriel\n"
+ "rauros\n"
+ "region\n"
+ "rerir\n"
+ "rhovanion\n"
+ "rhudaur\n"
+ "rhun\n"
+ "rhunen\n"
+ "rian\n"
+ "ringil\n"
+ "ringwil\n"
+ "romenna\n"
+ "rudh\n"
+ "rumil\n"
+ "saeros\n"
+ "salmar\n"
+ "saruman\n"
+ "sauron\n"
+ "serech\n"
+ "seregon\n"
+ "serinde\n"
+ "shelob\n"
+ "silmarien\n"
+ "silmaril\n"
+ "silpion\n"
+ "sindar\n"
+ "singollo\n"
+ "sirion\n"
+ "soronume\n"
+ "sul\n"
+ "sulimo\n"
+ "talath\n"
+ "taniquetil\n"
+ "tar\n"
+ "taras\n"
+ "tarn\n"
+ "tathren\n"
+ "taur\n"
+ "tauron\n"
+ "teiglin\n"
+ "telchar\n"
+ "telemnar\n"
+ "teleri\n"
+ "telperion\n"
+ "telumendil\n"
+ "thalion\n"
+ "thalos\n"
+ "thangorodrim\n"
+ "thargelion\n"
+ "thingol\n"
+ "thoronath\n"
+ "thorondor\n"
+ "thranduil\n"
+ "thuringwethil\n"
+ "tilion\n"
+ "tintalle\n"
+ "tinuviel\n"
+ "tirion\n"
+ "tirith\n"
+ "tol\n"
+ "tulkas\n"
+ "tumhalad\n"
+ "tumladen\n"
+ "tuna\n"
+ "tuor\n"
+ "turambar\n"
+ "turgon\n"
+ "turin\n"
+ "uial\n"
+ "uilos\n"
+ "uinen\n"
+ "ulairi\n"
+ "ulmo\n"
+ "ulumuri\n"
+ "umanyar\n"
+ "umarth\n"
+ "umbar\n"
+ "ungoliant\n"
+ "urthel\n"
+ "uruloki\n"
+ "utumno\n"
+ "vaire\n"
+ "valacirca\n"
+ "valandil\n"
+ "valaquenta\n"
+ "valar\n"
+ "valaraukar\n"
+ "valaroma\n"
+ "valier\n"
+ "valimar\n"
+ "valinor\n"
+ "valinoreva\n"
+ "valmar\n"
+ "vana\n"
+ "vanyar\n"
+ "varda\n"
+ "vasa\n"
+ "vilya\n"
+ "vingilot\n"
+ "vinyamar\n"
+ "voronwe\n"
+ "wethrin\n"
+ "wilwarin\n"
+ "yavanna\n"
+ ;
+
+
+martial_arts ma_blows[MAX_MA] =
+{
+ { "You punch %s.", 1, 0, 2, 4, 0, 0 },
+ { "You kick %s.", 2, 0, 2, 6, 0, 0 },
+ { "You strike %s.", 3, 0, 2, 7, 0, 0 },
+ { "You hit %s with your knee.", 5, 5, 4, 3, MA_KNEE, 0 },
+ { "You hit %s with your elbow.", 7, 5, 2, 8, 0, 0 },
+ { "You butt %s.", 9, 10, 4, 5, 0, 0 },
+ { "You kick %s.", 11, 10, 6, 4, MA_SLOW, 0 },
+ { "You uppercut %s.", 13, 12, 8, 4, MA_STUN, 6 },
+ { "You double-kick %s.", 16, 15, 10, 4, MA_STUN, 8 },
+ { "You hit %s with a Cat's Claw.", 20, 20, 10, 5, 0, 0 },
+ { "You hit %s with a jump kick.", 25, 25, 10, 6, MA_STUN, 10 },
+ { "You hit %s with an Eagle's Claw.", 29, 25, 12, 6, 0, 0 },
+ { "You hit %s with a circle kick.", 33, 30, 12, 8, MA_STUN, 10 },
+ { "You hit %s with an Iron Fist.", 37, 35, 16, 8, MA_STUN, 10 },
+ { "You hit %s with a flying kick.", 41, 35, 16, 10, MA_STUN, 12 },
+ { "You hit %s with a Dragon Fist.", 45, 35, 20, 10, MA_STUN, 16 },
+ { "You hit %s with a Crushing Blow.", 48, 35, 20, 12, MA_STUN, 18 },
+};
+
+/*
+ * cptr desc; A verbose attack description
+ * int min_level; Minimum level to use
+ * int chance; Chance of 'success
+ * int dd; Damage dice
+ * int ds; Damage sides
+ * s16b effect; Special effects
+ * s16b power; Special effects power
+ */
+martial_arts bear_blows[MAX_BEAR] =
+{
+ { "You claw %s.", 1, 0, 3, 4, MA_STUN, 4 },
+ { "You swat %s.", 4, 0, 4, 4, MA_WOUND, 20 },
+ { "You bite %s.", 9, 2, 4, 4, MA_WOUND, 30 },
+ { "You hug %s.", 15, 5, 6, 4, MA_FULL_SLOW, 0 },
+ { "You swat and rake %s.", 25, 10, 6, 5, MA_STUN | MA_WOUND, 10 },
+ { "You hug and claw %s.", 30, 15, 6, 6, MA_FULL_SLOW | MA_WOUND, 60 },
+ { "You double swat %s.", 35, 20, 9, 7, MA_STUN | MA_WOUND, 20 },
+ { "You double swat and rake %s.", 40, 25, 10, 10, MA_STUN | MA_WOUND, 25 },
+};
+
+
+magic_power mindcraft_powers[MAX_MINDCRAFT_POWERS] =
+{
+ /* Level gained, cost, %fail, name, desc */
+ {
+ /* Det. monsters/traps */
+ 1, 1, 15,
+ "Precognition",
+ "Detect monsters, traps and level layout and lights up at higher levels."
+ },
+ {
+ /* ~MM */
+ 2, 1, 20,
+ "Neural Blast",
+ "Blast the minds of your foes."
+ },
+ {
+ /* Phase/Between gate */
+ 3, 2, 25,
+ "Minor Displacement",
+ "Short distance teleportation"
+ },
+ {
+ /* Tele. Self / All */
+ 7, 6, 35,
+ "Major Displacement",
+ "Teleport you and others at high levels."
+ },
+ {
+ 9, 7, 50,
+ "Domination",
+ "Charm monsters"
+ },
+ {
+ /* Telekinetic "bolt" */
+ 11, 7, 30,
+ "Pulverise",
+ "Fires a bolt of pure sound."
+ },
+ {
+ /* Psychic/physical defenses */
+ 13, 12, 50,
+ "Character Armour",
+ "Sets up physical/elemental shield."
+ },
+ {
+ 15, 12, 60,
+ "Psychometry",
+ "Identifies objects."
+ },
+ {
+ /* Ball -> LOS */
+ 18, 10, 45,
+ "Mind Wave",
+ "Projects psi waves to crush the minds of your foes."
+ },
+ {
+ 23, 15, 50,
+ "Adrenaline Channeling",
+ "Heals you, cures you and speeds you."
+ },
+ {
+ /* Convert enemy HP to mana */
+ 25, 10, 40,
+ "Psychic Drain",
+ "Drain your foes' life into your mana reserves"
+ },
+ {
+ /* Ball -> LOS */
+ 28, 20, 45,
+ "Telekinetic Wave",
+ "Powerful wave of pure telekinetic forces."
+ },
+ };
+
+ magic_power necro_powers[MAX_NECRO_POWERS] =
+ {
+ /* Level gained, cost, %fail, name, desc */
+ {
+ /* Bolt/beam/ball/LOS of stun/scare */
+ 1, 2, 10,
+ "Horrify",
+ "Calls upon the darkness to stun and scare your foes."
+ },
+ {
+ /* Ball */
+ 5, 6, 20,
+ "Raise Dead",
+ "Brings back your foes in the form of various undead. Also, can heal monsters."
+ },
+ {
+ /* Summons weapon */
+ 12, 20, 25,
+ "Necromantic Teeth",
+ "Conjures a temporary vampiric weapon."
+ },
+ {
+ /* Heals when killing a monster */
+ 20, 10, 25,
+ "Absorb Soul",
+ "Gives back some life for each kill."
+ },
+ {
+ /* Bolt */
+ 30, 15, 20,
+ "Vampirism",
+ "Drain the life of your foes into your own."
+ },
+ {
+ /* The Death word, always bolt put your HP to 1 */
+ 35, 100, 25,
+ "Death",
+ "Instantly kills your opponent and you, turning yourself into an undead."
+ },
+};
+
+magic_power mimic_powers[MAX_MIMIC_POWERS] =
+{
+ /* Level gained, cost, %fail, name */
+ {
+ /* Use a book of lore */
+ 1, 2, 0,
+ "Mimic",
+ "Lets you use the powers of a Cloak of Mimicry."
+ },
+ {
+ /* Invisibility */
+ 10, 6, 20,
+ "Invisibility",
+ "Hides you from the sight of mortals."
+ },
+ {
+ /* +1 pair of legs */
+ 25, 20, 25,
+ "Legs Mimicry",
+ "Temporarily provides a new pair of legs."
+ },
+ {
+ /* wall form */
+ 30, 40, 30,
+ "Wall Mimicry",
+ "Temporarily lets you walk in walls, and ONLY in walls."
+ },
+ {
+ /* +1 pair of arms, +1 weapon */
+ 35, 100, 40,
+ "Arms Mimicry",
+ "Temporarily provides a new pair of arms."
+ },
+};
+
+magic_power symbiotic_powers[MAX_SYMBIOTIC_POWERS] =
+{
+ /* Level gained, cost, %fail, name */
+ {
+ 1, 1, 0,
+ "Hypnotise",
+ "Hypnotise a non-moving pet to allow you to enter symbiosis(wear) with it."
+ },
+ {
+ 1, 1, 0,
+ "Release",
+ "Release an hypnotised pet."
+ },
+ {
+ 3, 2, 10,
+ "Charm Never-Moving",
+ "Tries to charm a never-moving monster."
+ },
+ {
+ 5, 5, 20,
+ "Life Share",
+ "Evens out your life with your symbiote."
+ },
+ {
+ 10, 10, 20,
+ "Use Minor Powers",
+ "Allows you to use some of the powers of your symbiote."
+ },
+ {
+ 15, 14, 25,
+ "Heal Symbiote",
+ "Heals your symbiotic monster."
+ },
+ {
+ 25, 30, 40,
+ "Use major powers",
+ "Allows you to use all the powers of your symbiote."
+ },
+ {
+ 30, 35, 40,
+ "Summon Never-Moving Pet",
+ "Summons a never-moving pet."
+ },
+ {
+ 40, 60, 70,
+ "Force Symbiosis",
+ "Allows you to use all the powers of a monster in your line of sight."
+ },
+};
+
+
+/*
+ * Textual translation of your god's "niceness".
+ */
+
+cptr deity_niceness[10] =
+{
+ "a lovable deity",
+ "a friendly deity",
+ "an easygoing deity",
+ "a forgiving deity",
+ "an uncaring deity",
+ "a wary deity",
+ "an unforgiving deity",
+ "an impatient deity",
+ "a wrathful deity",
+ "an easily angered deity"
+};
+
+/*
+ * Textual translation of your standing with your god.
+ */
+
+cptr deity_standing[11] =
+{
+ "cursed",
+ "persecuted",
+ "punished",
+ "despised",
+ "disliked",
+ "watched",
+ "unnoticed",
+ "noticed",
+ "rewarded",
+ "favored",
+ "championed"
+};
+
+/*
+ * Name and description (max. 10 lines) of the gods.
+ * Only the first four lines are printed at birth.
+ */
+
+deity_type deity_info_init[MAX_GODS_INIT] =
+{
+ {
+ "Nobody",
+ {
+ "Atheist",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Eru Iluvatar",
+ {
+ "He is the supreme god, he created the world, and most of its inhabitants.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Manwe Sulimo",
+ {
+ "He is the king of the Valar, most powerful of them after Melkor.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Tulkas",
+ {
+ "He is the last of the Valar that came to the world, and the fiercest fighter.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Melkor Bauglir",
+ {
+ "He is the most powerful of the Valar. He became corrupted and he's now ",
+ "the greatest threat of Arda, he is also known as Morgoth, the dark enemy.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Yavanna Kementari",
+ {
+ "She is the Vala of nature, protectress of the great forests of "
+ "Middle-earth.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+};
+
+/* jk - to hit, to dam, to ac, to stealth, to disarm, to saving throw */
+/* this concept is taken from Adom, where Thomas Biskup thought it out, */
+/* as far as I know. */
+tactic_info_type tactic_info[9] =
+{
+ /* hit dam ac stl dis sav */
+ { -10, -10, + 15, + 3, + 15, + 14, "coward"}, /* 4-4 */
+ { -8, -8, + 10, + 2, + 9, + 9, "meek"}, /* 4-3 */
+ { -4, -4, + 5, + 1, + 5, + 5, "wary"}, /* 4-2 */
+ { -2, -2, + 2, + 1, + 2, + 2, "careful"}, /* 4-1 */
+ { 0, 0, 0, 0, 0, 0, "normal"}, /* 4+0 */
+ { 2, 2, -2, -1, -2, -3, "confident"}, /* 4+1 */
+ { 4, 4, -5, -2, -5, -7, "aggressive"}, /* 4+2 */
+ { 6, 6, -10, -3, -11, -12, "furious"}, /* 4+3 */
+ { 8, 12, -25, -5, -18, -18, "berserker"} /* 4+4 */
+};
+
+/*
+ * Random artifact activations.
+ */
+activation activation_info[MAX_T_ACT] =
+{
+ { "death", 0, ACT_DEATH },
+ { "ruination", 0, ACT_RUINATION },
+ { "destruction", 1000, ACT_DESTRUC },
+ { "stupidity", 0, ACT_UNINT },
+ { "weakness", 0, ACT_UNSTR },
+ { "unhealth", 0, ACT_UNCON },
+ { "ugliness", 0, ACT_UNCHR },
+ { "clumsiness", 0, ACT_UNDEX },
+ { "naivete", 0, ACT_UNWIS },
+ { "stat loss", 0, ACT_STATLOSS },
+ { "huge stat loss", 0, ACT_HISTATLOSS },
+ { "experience loss", 0, ACT_EXPLOSS },
+ { "huge experience loss", 0, ACT_HIEXPLOSS },
+ { "teleportation", 1000, ACT_TELEPORT },
+ { "monster summoning", 5, ACT_SUMMON_MONST },
+ { "paralyzation", 0, ACT_PARALYZE },
+ { "hallucination", 100, ACT_HALLU },
+ { "poisoning", 0, ACT_POISON },
+ { "hunger", 0, ACT_HUNGER },
+ { "stun", 0, ACT_STUN },
+ { "cuts", 0, ACT_CUTS },
+ { "paranoia", 0, ACT_PARANO },
+ { "confusion", 0, ACT_CONFUSION },
+ { "blindness", 0, ACT_BLIND },
+ { "pet summoning", 1010, ACT_PET_SUMMON },
+ { "cure paralyzation", 5000, ACT_CURE_PARA },
+ { "cure hallucination", 1000, ACT_CURE_HALLU },
+ { "cure poison", 1000, ACT_CURE_POIS },
+ { "cure hunger", 1000, ACT_CURE_HUNGER },
+ { "cure stun", 1000, ACT_CURE_STUN },
+ { "cure cut", 1000, ACT_CURE_CUTS },
+ { "cure fear", 1000, ACT_CURE_FEAR },
+ { "cure confusion", 1000, ACT_CURE_CONF },
+ { "cure blindness", 1000, ACT_CURE_BLIND },
+ { "cure light wounds", 500, ACT_CURE_LW },
+ { "cure serious wounds", 750, ACT_CURE_MW },
+ { "cure critical wounds", 1000, ACT_CURE_700 },
+ { "curing", 1100, ACT_CURING },
+ { "genocide", 5000, ACT_GENOCIDE },
+ { "mass genocide", 10000, ACT_MASS_GENO },
+ { "restoration", 2000, ACT_REST_ALL },
+ { "light", 1000, ACT_LIGHT },
+ { "darkness", 0, ACT_DARKNESS },
+ { "teleportation", 1000, ACT_TELEPORT },
+ { "level teleportation", 500, ACT_LEV_TELE },
+ { "acquirement", 30000, ACT_ACQUIREMENT },
+ { "something weird", 50, ACT_WEIRD },
+ { "aggravation", 0, ACT_AGGRAVATE },
+ { "corruption", 100, ACT_MUT },
+ { "cure insanity", 2000, ACT_CURE_INSANITY },
+ { "light absortion", 800, ACT_LIGHT_ABSORBTION },
+};
+
+/*
+ * Possible movement type.
+ */
+move_info_type move_info[9] =
+{
+ /* speed, searching, stealth, perception */
+ { -10, 17, 4, 20, "slug-like"},
+ { -8, 12, 4, 16, "very slow"},
+ { -6, 8, 3, 10, "slow"},
+ { -3, 4, 2, 6, "leisurely"},
+ { 0, 0, 0, 0, "normal"},
+ { 1, -4, -1, -4, "brisk"},
+ { 2, -6, -4, -8, "fast"},
+ { 3, -10, -7, -14, "very fast"},
+ { 4, -16, -10, -20, "running"}
+};
+
+/*
+ * Possible inscriptions type.
+ */
+inscription_info_type inscription_info[MAX_INSCRIPTIONS] =
+{
+ { /* Nothing */
+ "",
+ 0,
+ TRUE,
+ 0,
+ },
+ { /* Light up the room(Adunaic) */
+ "ure nimir", /* sun shine */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 30,
+ },
+ { /* Darkness in room(Adunaic) */
+ "lomi gimli", /* night stars */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 10,
+ },
+ { /* Storm(Adunaic) */
+ "dulgi bawiba", /* black winds */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 40,
+ },
+ { /* Protection(Sindarin) */
+ "pedo mellon a minno", /* say friend and enter */
+ INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 8,
+ },
+ { /* Dwarves summoning(Khuzdul) */
+ "Baruk Khazad! Khazad aimenu!", /* Axes of the Dwarves, the Dwarves are upon you! */
+ INSCRIP_EXEC_ENGRAVE,
+ FALSE,
+ 100,
+ },
+ { /* Open Chasm(Nandorin) */
+ "dunna hrassa", /* black precipice */
+ INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 50,
+ },
+ { /* Blast of Black Fire(Orcish) */
+ "burz ghash ronk", /* black fire pool */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 60,
+ },
+};
+
+/*
+ * Inscriptions for pseudo-id
+ */
+cptr sense_desc[] =
+{
+ "whoops",
+ "cursed",
+ "average",
+ "good",
+ "good",
+ "excellent",
+ "worthless",
+ "terrible",
+ "special",
+ "broken",
+ ""
+};
+
+/*
+ * Flag groups used for art creation, level gaining weapons, ...
+ * -----
+ * Name,
+ * Price,
+ * Flags 1,
+ * Flags 2,
+ * Flags 3,
+ * Flags 4,
+ * ESP,
+ */
+flags_group flags_groups[MAX_FLAG_GROUP] =
+{
+ {
+ "Fire",
+ TERM_L_RED,
+ 1,
+ TR1_SLAY_UNDEAD | TR1_BRAND_FIRE,
+ TR2_RES_FIRE,
+ TR3_SH_FIRE | TR3_LITE1 | TR3_IGNORE_FIRE,
+ 0,
+ 0,
+ },
+ {
+ "Cold",
+ TERM_WHITE,
+ 1,
+ TR1_SLAY_DRAGON | TR1_SLAY_DEMON | TR1_BRAND_COLD,
+ TR2_RES_COLD | TR2_INVIS,
+ TR3_SLOW_DIGEST | TR3_IGNORE_COLD,
+ 0,
+ 0,
+ },
+ {
+ "Acid",
+ TERM_GREEN,
+ 3,
+ TR1_SLAY_ANIMAL | TR1_IMPACT | TR1_TUNNEL | TR1_BRAND_ACID,
+ TR2_RES_ACID,
+ TR3_IGNORE_ACID,
+ 0,
+ 0,
+ },
+ {
+ "Lightning",
+ TERM_L_BLUE,
+ 1,
+ TR1_SLAY_EVIL | TR1_BRAND_ELEC,
+ TR2_RES_ELEC,
+ TR3_IGNORE_ELEC | TR3_SH_ELEC | TR3_TELEPORT,
+ 0,
+ 0,
+ },
+ {
+ "Poison",
+ TERM_L_GREEN,
+ 2,
+ TR1_CHR | TR1_VAMPIRIC | TR1_SLAY_ANIMAL | TR1_BRAND_POIS,
+ TR2_SUST_CHR | TR2_RES_POIS,
+ TR3_DRAIN_EXP,
+ 0,
+ ESP_TROLL | ESP_GIANT,
+ },
+ {
+ "Air",
+ TERM_BLUE,
+ 5,
+ TR1_WIS | TR1_STEALTH | TR1_INFRA | TR1_SPEED,
+ TR2_RES_LITE | TR2_RES_DARK | TR2_RES_BLIND | TR2_SUST_WIS,
+ TR3_FEATHER | TR3_SEE_INVIS | TR3_BLESSED,
+ 0,
+ ESP_GOOD,
+ },
+ {
+ "Earth",
+ TERM_L_UMBER,
+ 5,
+ TR1_STR | TR1_CON | TR1_TUNNEL | TR1_BLOWS | TR1_SLAY_TROLL | TR1_SLAY_GIANT | TR1_IMPACT,
+ TR2_SUST_STR | TR2_SUST_CON | TR2_FREE_ACT | TR2_RES_FEAR | TR2_RES_SHARDS,
+ TR3_REGEN,
+ 0,
+ ESP_TROLL | ESP_GIANT,
+ },
+ {
+ "Mind",
+ TERM_YELLOW,
+ 7,
+ TR1_INT | TR1_SEARCH,
+ TR2_SUST_INT | TR2_RES_CONF | TR2_RES_FEAR,
+ 0,
+ 0,
+ ESP_ORC | ESP_TROLL | ESP_GIANT | ESP_ANIMAL | ESP_UNIQUE | ESP_SPIDER | ESP_DEMON,
+ },
+ {
+ "Shield",
+ TERM_RED,
+ 7,
+ TR1_DEX,
+ TR2_SUST_DEX | TR2_INVIS | TR2_REFLECT | TR2_HOLD_LIFE | TR2_RES_SOUND | TR2_RES_NEXUS,
+ TR3_REGEN,
+ 0,
+ 0,
+ },
+ {
+ "Chaos",
+ TERM_VIOLET,
+ 7,
+ TR1_CHAOTIC | TR1_IMPACT,
+ TR2_RES_CHAOS | TR2_RES_DISEN,
+ TR3_REGEN,
+ 0,
+ ESP_ALL,
+ },
+ {
+ "Magic",
+ TERM_L_BLUE,
+ 10,
+ TR1_MANA | TR1_SPELL,
+ TR2_RES_CHAOS | TR2_RES_DISEN,
+ TR3_WRAITH,
+ TR4_PRECOGNITION | TR4_FLY | TR4_CLONE,
+ 0,
+ },
+ {
+ "Antimagic",
+ TERM_L_DARK,
+ 10,
+ TR1_VAMPIRIC | TR1_CHAOTIC | TR1_BLOWS | TR1_SPEED,
+ TR2_LIFE | TR2_REFLECT | TR2_FREE_ACT | TR2_HOLD_LIFE,
+ TR3_NO_MAGIC | TR3_NO_TELE | TR3_SEE_INVIS,
+ TR4_ANTIMAGIC_10 | TR4_ANTIMAGIC_20,
+ 0,
+ },
+};
+
+/* Powers */
+power_type powers_type_init[POWER_MAX_INIT] =
+{
+ {
+ "spit acid",
+ "You can spit acid.",
+ "You gain the ability to spit acid.",
+ "You lose the ability to spit acid.",
+ 9, 9, A_DEX, 15,
+ },
+ {
+ "fire breath",
+ "You can breath fire.",
+ "You gain the ability to breathe fire.",
+ "You lose the ability to breathe fire.",
+ 20, 10, A_CON, 18,
+ },
+ {
+ "hypnotic gaze",
+ "Your gaze is hypnotic.",
+ "Your eyes look mesmerising...",
+ "Your eyes look uninteresting.",
+ 12, 12, A_CHR, 18,
+ },
+ {
+ "telekinesis",
+ "You are telekinetic.",
+ "You gain the ability to move objects telekinetically.",
+ "You lose the ability to move objects telekinetically.",
+ 9, 9, A_WIS, 14,
+ },
+ {
+ "teleport",
+ "You can teleport at will.",
+ "You gain the power of teleportation at will.",
+ "You lose the power of teleportation at will.",
+ 7, 7, A_WIS, 15,
+ },
+ {
+ "mind blast",
+ "You can mind blast your enemies.",
+ "You gain the power of Mind Blast.",
+ "You lose the power of Mind Blast.",
+ 5, 3, A_WIS, 15,
+ },
+ {
+ "emit radiation",
+ "You can emit hard radiation at will.",
+ "You start emitting hard radiation.",
+ "You stop emitting hard radiation.",
+ 15, 15, A_CON, 14,
+ },
+ {
+ "vampiric drain",
+ "You can drain life from a foe.",
+ "You become vampiric.",
+ "You are no longer vampiric.",
+ 4, 5, A_CON, 9,
+ },
+ {
+ "smell metal",
+ "You can smell nearby precious metal.",
+ "You smell a metallic odour.",
+ "You no longer smell a metallic odour.",
+ 3, 2, A_INT, 12,
+ },
+ {
+ "smell monsters",
+ "You can smell nearby monsters.",
+ "You smell filthy monsters.",
+ "You no longer smell filthy monsters.",
+ 5, 4, A_INT, 15,
+ },
+ {
+ "blink",
+ "You can teleport yourself short distances.",
+ "You gain the power of minor teleportation.",
+ "You lose the power of minor teleportation.",
+ 3, 3, A_WIS, 12,
+ },
+ {
+ "eat rock",
+ "You can consume solid rock.",
+ "The walls look delicious.",
+ "The walls look unappetising.",
+ 8, 12, A_CON, 18,
+ },
+ {
+ "swap position",
+ "You can switch locations with another being.",
+ "You feel like walking a mile in someone else's shoes.",
+ "You feel like staying in your own shoes.",
+ 15, 12, A_DEX, 16,
+ },
+ {
+ "shriek",
+ "You can emit a horrible shriek.",
+ "Your vocal cords get much tougher.",
+ "Your vocal cords get much weaker.",
+ 4, 4, A_CON, 6,
+ },
+ {
+ "illuminate",
+ "You can emit bright light.",
+ "You can light up rooms with your presence.",
+ "You can no longer light up rooms with your presence.",
+ 3, 2, A_INT, 10,
+ },
+ {
+ "detect curses",
+ "You can feel the danger of evil magic.",
+ "You can feel evil magic.",
+ "You can no longer feel evil magic.",
+ 7, 14, A_WIS, 14,
+ },
+ {
+ "berserk",
+ "You can drive yourself into a berserk frenzy.",
+ "You feel a controlled rage.",
+ "You no longer feel a controlled rage.",
+ 8, 8, A_STR, 14,
+ },
+ {
+ "polymorph",
+ "You can polymorph yourself at will.",
+ "Your body seems mutable.",
+ "Your body seems stable.",
+ 18, 20, A_CON, 18,
+ },
+ {
+ "Midas touch",
+ "You can turn ordinary items to gold.",
+ "You gain the Midas touch.",
+ "You lose the Midas touch.",
+ 10, 5, A_INT, 12,
+ },
+ {
+ "grow mold",
+ "You can cause mold to grow near you.",
+ "You feel a sudden affinity for mold.",
+ "You feel a sudden dislike for mold.",
+ 1, 6, A_CON, 14,
+ },
+ {
+ "resist elements",
+ "You can harden yourself to the ravages of the elements.",
+ "You feel like you can protect yourself.",
+ "You feel like you might be vulnerable.",
+ 10, 12, A_CON, 12,
+ },
+ {
+ "earthquake",
+ "You can bring down the dungeon around your ears.",
+ "You gain the ability to wreck the dungeon.",
+ "You lose the ability to wreck the dungeon.",
+ 12, 12, A_STR, 16,
+ },
+ {
+ "eat magic",
+ "You can consume magic energy for your own use.",
+ "Your magic items look delicious.",
+ "Your magic items no longer look delicious.",
+ 17, 1, A_WIS, 15,
+ },
+ {
+ "weigh magic",
+ "You can feel the strength of the magics affecting you.",
+ "You feel you can better understand the magic around you.",
+ "You no longer sense magic.",
+ 6, 6, A_INT, 10,
+ },
+ {
+ "sterilise",
+ "You can cause mass impotence.",
+ "You can give everything around you a headache.",
+ "You hear a massed sigh of relief.",
+ 20, 40, A_CHR, 18,
+ },
+ {
+ "panic hit",
+ "You can run for your life after hitting something.",
+ "You suddenly understand how thieves feel.",
+ "You no longer feel jumpy.",
+ 10, 12, A_DEX, 14,
+ },
+ {
+ "dazzle",
+ "You can emit confusing, blinding radiation.",
+ "You gain the ability to emit dazzling lights.",
+ "You lose the ability to emit dazzling lights.",
+ 7, 15, A_CHR, 8,
+ },
+ {
+ "spear of darkness",
+ "You can create a spear of darkness.",
+ "An illusory spear of darkness appears in your hand.",
+ "The spear of darkness disappear.",
+ 7, 10, A_WIS, 9,
+ },
+ {
+ "recall",
+ "You can travel between towns and the depths.",
+ "You feel briefly homesick, but it passes.",
+ "You feel briefly homesick.",
+ 17, 50, A_INT, 16,
+ },
+ {
+ "banish evil",
+ "You can send evil creatures directly to the Nether Realm.",
+ "You feel a holy wrath fill you.",
+ "You no longer feel a holy wrath.",
+ 25, 25, A_WIS, 18,
+ },
+ {
+ "cold touch",
+ "You can freeze things with a touch.",
+ "Your hands get very cold.",
+ "Your hands warm up.",
+ 2, 2, A_CON, 11,
+ },
+ {
+ "throw object",
+ "You can hurl objects with great force.",
+ "Your throwing arm feels much stronger.",
+ "Your throwing arm feels much weaker.",
+ 1, 10, A_STR, 6,
+ },
+ {
+ "find secret passages",
+ "You can use secret passages.",
+ "You suddenly notice lots of hidden ways.",
+ "You no longer can use hidden ways.",
+ 15, 15, A_DEX, 12,
+ },
+ {
+ "detect doors and traps",
+ "You can detect hidden doors and traps.",
+ "You develop an affinity for traps.",
+ "You no longer can detect hidden doors and traps.",
+ 5, 3, A_WIS, 10,
+ },
+ {
+ "create food",
+ "You can create food.",
+ "Your cooking skills greatly improve.",
+ "Your cooking skills return to a normal level.",
+ 15, 10, A_INT, 10,
+ },
+ {
+ "remove fear",
+ "You can embolden yourself.",
+ "You feel your fears lessening.",
+ "You feel your fears growing again.",
+ 3, 5, A_WIS, 8,
+ },
+ {
+ "set explosive rune",
+ "You can set explosive runes.",
+ "You suddenly understand how explosive runes work.",
+ "You suddenly forget how explosive runes work.",
+ 25, 35, A_INT, 15,
+ },
+ {
+ "stone to mud",
+ "You can destroy walls.",
+ "You can destroy walls.",
+ "You cannot destroy walls anymore.",
+ 20, 10, A_STR, 12,
+ },
+ {
+ "poison dart",
+ "You can throw poisoned darts.",
+ "You get an infinite supply of poisoned darts.",
+ "You lose your infinite supply of poisoned darts.",
+ 12, 8, A_DEX, 14,
+ },
+ {
+ "magic missile",
+ "You can cast magic missiles.",
+ "You suddenly understand the basics of magic.",
+ "You forget the basics of magic.",
+ 2, 2, A_INT, 9,
+ },
+ {
+ "grow trees",
+ "You can grow trees.",
+ "You feel an affinity for trees.",
+ "You no longer feel an affinity for trees.",
+ 2, 6, A_CHR, 3,
+ },
+ {
+ "cold breath",
+ "You can breath cold.",
+ "You gain the ability to breathe cold.",
+ "You lose the ability to breathe cold.",
+ 20, 10, A_CON, 18,
+ },
+ {
+ "chaos breath",
+ "You can breath chaos.",
+ "You gain the ability to breathe chaos.",
+ "You lose the ability to breathe chaos.",
+ 20, 10, A_CON, 18,
+ },
+ {
+ "elemental breath",
+ "You can breath the elements.",
+ "You gain the ability to breathe the elements.",
+ "You lose the ability to breathe the elements.",
+ 20, 10, A_CON, 18,
+ },
+ {
+ "change the world",
+ "You can wreck the world around you.",
+ "You gain the ability to wreck the world.",
+ "You lose the ability to wreck the world.",
+ 1, 30, A_CHR, 6,
+ },
+ {
+ "scare monster",
+ "You can scare monsters.",
+ "You gain the ability to scare monsters.",
+ "You lose the ability to scare monsters.",
+ 4, 3, A_INT, 3,
+ },
+ {
+ "restore life",
+ "You can restore lost life forces.",
+ "You gain the ability to restore your life force.",
+ "You lose the ability to restore your life force.",
+ 30, 30, A_WIS, 18,
+ },
+ {
+ "summon monsters",
+ "You can call upon monsters.",
+ "You gain the ability to call upon monsters.",
+ "You lose the ability to call upon monsters.",
+ 0, 0, 0, 0,
+ },
+ {
+ "necromantic powers",
+ "You can use the foul necromantic magic.",
+ "You gain the ability to use the foul necromantic magic.",
+ "You lose the ability to use the foul necromantic magic.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Rohan Knight's Powers",
+ "You can use rohir powers.",
+ "You gain the ability to use rohir powers.",
+ "You lose the ability to use rohir powers.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Thunderlord's Powers",
+ "You can use thunderlords powers.",
+ "You gain the ability to use thunderlords powers.",
+ "You lose the ability to use thunderlords powers.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Death Mold's Powers",
+ "You can use the foul deathmold magic.",
+ "You gain the ability to use the foul deathmold magic.",
+ "You lose the ability to use the foul deathmold magic.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Hypnotise Pet",
+ "You can mystify pets.",
+ "You gain the ability to mystify pets.",
+ "You lose the ability to mystify pets.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Awaken Hypnotised Pet",
+ "You can wake up a pet.",
+ "You gain the ability to wake up a pet.",
+ "You lose the ability to wake up a pet.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Incarnate",
+ "You can incarnate into a body.",
+ "You feel the need to get a body.",
+ "You no longer feel the need for a new body.",
+ 0, 0, 0, 0,
+ },
+ {
+ "magic map",
+ "You can sense what is beyond walls.",
+ "You feel you can sense what is beyond walls.",
+ "You no longer can sense what is beyond walls.",
+ 7, 10, A_WIS, 15,
+ },
+ {
+ "lay trap",
+ "You can lay monster traps.",
+ "You suddenly understand how rogues work.",
+ "You no longer understand how rogues work.",
+ 1, 1, A_DEX, 1,
+ },
+ {
+ "Merchant abilities",
+ "You can request items and get loans.",
+ "From now on you can use the merchant abilities.",
+ "You can no longer use the merchant abilities.",
+ 0, 0, 0, 0,
+ },
+ {
+ "turn pet into companion",
+ "You can turn a pet into a companion.",
+ "You suddenly gain authority over your pets.",
+ "You can no longer convert pets into companions.",
+ 2, 10, A_CHR, 10,
+ },
+ {
+ "turn into a bear",
+ "You can turn into a bear.",
+ "You suddenly gain beorning powers.",
+ "You can no longer shapeshift into a bear.",
+ 2, 5, A_CON, 5,
+ },
+ {
+ "sense dodge success",
+ "You can sense your dodging success chance.",
+ "You suddenly can sense your dodging success chance.",
+ "You can no longer sense your dodging success chance.",
+ 0, 0, 0, 0,
+ },
+ {
+ "turn into a Balrog",
+ "You can turn into a Balrog at will.",
+ "You feel the fire of Udun burning in you.",
+ "You no longer feel the fire of Udun in you.",
+ 35, 80, A_WIS, 25,
+ },
+};
+
+/*
+ * The Quests
+ */
+quest_type quest_init_tome[MAX_Q_IDX_INIT] =
+{
+ {
+ FALSE,
+ FALSE,
+ "",
+ {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 0,
+
+ NULL,
+ HOOK_TYPE_C,
+ quest_null_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ FALSE,
+ FALSE,
+ "Dol Guldur",
+ {
+ "The forest of Mirkwood is a very dangerous place to go, mainly due to",
+ "the activities of the Necromancer that lurks in Dol Guldur.",
+ "Find him, and free Mirkwood from his spells.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_TAKEN,
+ 70,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_necro_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ FALSE,
+ FALSE,
+ "Sauron",
+ {
+ "It is time to take the battle to Morgoth. But, before you can",
+ "reach it, you must find and kill Sauron. Only after defeating",
+ "this powerful sorcerer will the stairs leading to Morgoth's",
+ "room be opened.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 99,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_sauron_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ FALSE,
+ FALSE,
+ "Morgoth",
+ {
+ "Your final quest is the ultimate quest that has always been",
+ "required of you. You must enter the fetid depths of Angband, where",
+ "Morgoth is waiting. Travel deep, and defeat this source of all our",
+ "problems. Be prepared, be patient, and good luck. May the light",
+ "shine on you.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 100,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_morgoth_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ /* Bree plot */
+ {
+ FALSE,
+ FALSE,
+ "Thieves!",
+ {
+ "There are thieves robbing my people! They live in a small",
+ "burrow outside the city walls, but they get inside the walls",
+ "with a tunnel to a building here! Your task is to go into",
+ "the building and kill these ruffians.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 5,
+
+ &plots[PLOT_BREE],
+ HOOK_TYPE_C,
+ quest_thieves_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ FALSE,
+ TRUE,
+ "Random Quest",
+ {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 5,
+
+ NULL,
+ HOOK_TYPE_C,
+ quest_random_init_hook,
+ {0, 0},
+ quest_random_describe,
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "Lost Hobbit",
+ {
+ "Merton Proudfoot, a young hobbit, seems to have disappeared.",
+ "Last time anyone saw him was near the horrible maze to the south of Bree.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 25,
+
+ &plots[PLOT_OTHER],
+ HOOK_TYPE_C,
+ quest_hobbit_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "The Dark Horseman",
+ {
+ "A dark-cloaked horseman has been spotted several times in town.",
+ "He carries an aura of fear with him and people seem to get sick",
+ "wherever he goes. Please do something, but be careful...",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 40,
+
+ &plots[PLOT_BREE],
+ HOOK_TYPE_C,
+ quest_nazgul_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "The Trolls Glade",
+ {
+ "A group of Forest Trolls settled in an abandoned forest in the",
+ "south east of our town. They are killing our people. You must",
+ "put an end to this! It might be best to look for them at night.",
+ "Local hobbits claim that the mighty swords Orcrist and Glamdring",
+ "can be found there! Bring back one of them as a proof!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 30,
+
+ &plots[PLOT_BREE],
+ HOOK_TYPE_C,
+ quest_troll_init_hook,
+ {FALSE, 0},
+ NULL,
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "The Wight Grave",
+ {
+ "The Barrow-Downs hides many mysteries and dangers.",
+ "Lately many people, both men and hobbits, have disappeared there.",
+ "Please put an end to this threat!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 30,
+
+ &plots[PLOT_BREE],
+ HOOK_TYPE_C,
+ quest_wight_init_hook,
+ {FALSE, 0},
+ NULL,
+ },
+
+ /* Lorien plot */
+ {
+ FALSE,
+ FALSE,
+ "Spiders of Mirkwood",
+ {
+ "Powers lurk deep within Mirkwood. Spiders have blocked the",
+ "path through the forest, and Thranduil's folk have been",
+ "unable to hold them off. It is your task to drive them",
+ "away. Be careful -- many traps have been laid by their",
+ "webs, and their venom is dangerous indeed.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 25,
+
+ &plots[PLOT_LORIEN],
+ HOOK_TYPE_C,
+ quest_spider_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ FALSE,
+ FALSE,
+ "Poisoned Water",
+ {
+ "A curse has beset Lothlorien. All trees along the shorelines of Nimrodel",
+ "are withering away. We fear the blight could spread to the whole forest.",
+ "The cause seems to be an unknown poison. You are to go to the West and",
+ "travel along Celebrant and Nimrodel until you discover the source of",
+ "the poisoning. Then you must destroy it and drop these potions on",
+ "the tainted water.",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 30,
+
+ &plots[PLOT_LORIEN],
+ HOOK_TYPE_C,
+ quest_poison_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* Other quests */
+ {
+ FALSE,
+ FALSE,
+ "The Broken Sword",
+ {
+ "You have found Narsil, a broken sword. It is said that the sword that",
+ "was broken shall be reforged... Maybe it is this one.",
+ "You should bring it to Aragorn at Minas Anor -- he would know.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 20,
+
+ &plots[PLOT_OTHER],
+ HOOK_TYPE_C,
+ quest_narsil_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* Gondolin plot */
+ {
+ FALSE,
+ FALSE,
+ "Eol the Dark Elf",
+ {
+ "We have disturbing tidings. Eol the Dark Elf has come seeking his kin in",
+ "Gondolin. We cannot let anyone pass the borders of the city without the",
+ "King's leave. Go forth to the eastern mountains and apprehend him. If",
+ "he resists, use whatever means possible to hinder him from reaching the",
+ "city. Be wary -- the mountain caves may have many hidden traps.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 30,
+
+ &plots[PLOT_GONDOLIN],
+ HOOK_TYPE_C,
+ quest_eol_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ FALSE,
+ FALSE,
+ "Nirnaeth Arnoediad",
+ {
+ "The fortunes of war in the north turn against us.",
+ "Morgoth's treachery has driven our armies back nigh",
+ "to the city's walls. Go forth from the city gates",
+ "and clear a path for them to retreat. You need not",
+ "destroy the troll army, simply drive a path through.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 37,
+
+ &plots[PLOT_GONDOLIN],
+ HOOK_TYPE_C,
+ quest_nirnaeth_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ FALSE,
+ FALSE,
+ "Invasion of Gondolin",
+ {
+ "Morgoth is upon us! Dragons and Balrogs have poured over secret",
+ "ways of the Echoriath, and are looking for our city. They are",
+ "conducted by Maeglin! You must stop him or they will find us.",
+ "Do not let Maeglin get to the stairs or everything will be lost!",
+ "Go now, be brave.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 80,
+
+ &plots[PLOT_GONDOLIN],
+ HOOK_TYPE_C,
+ quest_invasion_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* Minas Anor Plot*/
+ {
+ FALSE,
+ FALSE,
+ "The Last Alliance",
+ {
+ "The armies of Morgoth are closing in on the last remaining strongholds",
+ "of resistance against him. We are too far apart to help each other.",
+ "The arrival of our new Thunderlord allies has helped, but can only delay",
+ "the inevitable. We must be able to stand together and reinforce each other,",
+ "or both our kingdoms will fall separately. The Thunderlords have taught us",
+ "how to use the Void Jumpgates: we need you to open a Void Jumpgate in our",
+ "own city, and that of Gondolin.",
+ "Simply travel to Gondolin, but beware of rebel thunderlords.",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 80,
+
+ &plots[PLOT_MINAS],
+ HOOK_TYPE_C,
+ quest_between_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ FALSE,
+ FALSE,
+ "The One Ring",
+ {
+ "Find the One Ring, then bring it to Mount Doom, in Mordor, to drop",
+ "it in the Great Fire where it was once forged.",
+ "But beware: *NEVER* use it, or you will be corrupted.",
+ "Once it is destroyed you will be able to permanently defeat Sauron.",
+ "The ring must be cast back into the fires of Mount Doom!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 99,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_one_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "Mushroom supplies",
+ {
+ "Farmer Maggot asked you to bring him back his mushrooms.",
+ "Do not harm his dogs.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 3,
+
+ &plots[PLOT_OTHER],
+ HOOK_TYPE_C,
+ quest_shroom_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "The prisoner of Dol Guldur",
+ {
+ "You keep hearing distress cries in the dark tower of",
+ "Dol Guldur...",
+ "Maybe there is someone being held prisoner and tortured!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 60,
+
+ &plots[PLOT_OTHER],
+ HOOK_TYPE_C,
+ quest_thrain_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ /* The 2 ultra endings go here */
+ {
+ FALSE,
+ FALSE,
+ "Falling Toward Apotheosis",
+ {
+ "You must enter the Void where Melkor spirit lurks to destroy",
+ "him forever. Remember however that it is likely to be your own",
+ "death that awaits you.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 150,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_ultra_good_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ FALSE,
+ FALSE,
+ "Falling Toward Apotheosis",
+ {
+ "You must now launch an onslaught on Valinor itself to eliminate",
+ "once and for all any posible resistance to your dominance of Arda.",
+ "Remember however that it is likely to be your own death that awaits you.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 150,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_ultra_evil_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* More Lorien */
+ {
+ FALSE,
+ FALSE,
+ "Wolves!",
+ {
+ "There are wolves pestering my people! They gather in a hut",
+ "on the edge of town and menace everyone nearby. Your task",
+ "is to go in there and clear them out.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 15,
+
+ &plots[PLOT_LORIEN],
+ HOOK_TYPE_C,
+ quest_wolves_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* More Gondolin */
+ {
+ FALSE,
+ FALSE,
+ "Dragons!",
+ {
+ "There are dragons pestering my people! They gather in a",
+ "building on the edge of town and menace everyone nearby.",
+ "Your task is to go into the building and clear them out.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 25,
+
+ &plots[PLOT_GONDOLIN],
+ HOOK_TYPE_C,
+ quest_dragons_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* More Minas Anor */
+ {
+ FALSE,
+ FALSE,
+ "Haunted House!",
+ {
+ "There are undead pestering my people! They gather in a hut",
+ "on the edge of town and menace everyone nearby. Your task",
+ "is to go into the building and clear out the beasts.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 45,
+
+ &plots[PLOT_MINAS],
+ HOOK_TYPE_C,
+ quest_haunted_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* Khazad-Dum Plot*/
+ {
+ FALSE,
+ FALSE,
+ "Evil!",
+ {
+ "We have burrowed too deep, and let out some creatures of",
+ "Morgoth's that threaten to kill us all! Your task is to save us",
+ "from them!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 60,
+
+ &plots[PLOT_KHAZAD],
+ HOOK_TYPE_C,
+ quest_evil_init_hook,
+ {0, 0},
+ NULL,
+ },
+};
+
+
+/* List of powers for Symbiants/Powers */
+monster_power monster_powers[96] =
+ {
+ { RF4_SHRIEK, "Aggravate Monster", 1, FALSE },
+ { RF4_MULTIPLY, "Multiply", 10, FALSE },
+ { RF4_S_ANIMAL, "Summon Animal", 30, FALSE },
+ { RF4_ROCKET, "Fire a Rocket", 40, TRUE },
+ { RF4_ARROW_1, "Light Arrow", 1, FALSE },
+ { RF4_ARROW_2, "Minor Arrow", 3, FALSE },
+ { RF4_ARROW_3, "Major Arrow", 7, TRUE },
+ { RF4_ARROW_4, "Great Arrow", 9, TRUE },
+ { RF4_BR_ACID, "Breathe Acid", 10, FALSE },
+ { RF4_BR_ELEC, "Breathe Lightning", 10, FALSE },
+ { RF4_BR_FIRE, "Breathe Fire", 10, FALSE },
+ { RF4_BR_COLD, "Breathe Cold", 10, FALSE },
+ { RF4_BR_POIS, "Breathe Poison", 15, TRUE },
+ { RF4_BR_NETH, "Breathe Nether", 30, TRUE },
+ { RF4_BR_LITE, "Breathe Light", 20, TRUE },
+ { RF4_BR_DARK, "Breathe Dark", 20, TRUE },
+ { RF4_BR_CONF, "Breathe Confusion", 15, TRUE },
+ { RF4_BR_SOUN, "Breathe Sound", 30, TRUE },
+ { RF4_BR_CHAO, "Breathe Chaos", 30, TRUE },
+ { RF4_BR_DISE, "Breathe Disenchantment", 30, TRUE },
+ { RF4_BR_NEXU, "Breathe Nexus", 30, TRUE },
+ { RF4_BR_TIME, "Breathe Time", 30, TRUE },
+ { RF4_BR_INER, "Breathe Inertia", 30, TRUE },
+ { RF4_BR_GRAV, "Breathe Gravity", 30, TRUE },
+ { RF4_BR_SHAR, "Breathe Shards", 30, TRUE },
+ { RF4_BR_PLAS, "Breathe Plasma", 30, TRUE },
+ { RF4_BR_WALL, "Breathe Force", 30, TRUE },
+ { RF4_BR_MANA, "Breathe Mana", 40, TRUE },
+ { RF4_BA_NUKE, "Nuke Ball", 30, TRUE },
+ { RF4_BR_NUKE, "Breathe Nuke", 40, TRUE },
+ { RF4_BA_CHAO, "Chaos Ball", 30, TRUE },
+ { RF4_BR_DISI, "Breathe Disintegration", 40, TRUE },
+
+ { RF5_BA_ACID, "Acid Ball", 8, FALSE },
+ { RF5_BA_ELEC, "Lightning Ball", 8, FALSE },
+ { RF5_BA_FIRE, "Fire Ball", 8, FALSE },
+ { RF5_BA_COLD, "Cold Ball", 8, FALSE },
+ { RF5_BA_POIS, "Poison Ball", 20, TRUE },
+ { RF5_BA_NETH, "Nether Ball", 20, TRUE },
+ { RF5_BA_WATE, "Water Ball", 20, TRUE },
+ { RF5_BA_MANA, "Mana Ball", 50, TRUE },
+ { RF5_BA_DARK, "Darkness Ball", 20, TRUE },
+ { 0, "(none)", 0, FALSE },
+ { 0, "(none)", 0, FALSE },
+ { 0, "(none)", 0, FALSE },
+ { RF5_CAUSE_1, "Cause Light Wounds", 20, FALSE },
+ { RF5_CAUSE_2, "Cause Medium Wounds", 30, FALSE },
+ { RF5_CAUSE_3, "Cause Critical Wounds", 35, TRUE },
+ { RF5_CAUSE_4, "Cause Mortal Wounds", 45, TRUE },
+ { RF5_BO_ACID, "Acid Bolt", 5, FALSE },
+ { RF5_BO_ELEC, "Lightning Bolt", 5, FALSE },
+ { RF5_BO_FIRE, "Fire Bolt", 5, FALSE },
+ { RF5_BO_COLD, "Cold Bolt", 5, FALSE },
+ { RF5_BO_POIS, "Poison Bolt", 10, TRUE },
+ { RF5_BO_NETH, "Nether Bolt", 15, TRUE },
+ { RF5_BO_WATE, "Water Bolt", 20, TRUE },
+ { RF5_BO_MANA, "Mana Bolt", 25, TRUE },
+ { RF5_BO_PLAS, "Plasma Bolt", 20, TRUE },
+ { RF5_BO_ICEE, "Ice Bolt", 20, TRUE },
+ { RF5_MISSILE, "Magic Missile", 1, FALSE },
+ { RF5_SCARE, "Scare", 4, FALSE },
+ { RF5_BLIND, "Blindness", 6, FALSE },
+ { RF5_CONF, "Confusion", 7, FALSE },
+ { RF5_SLOW, "Slowness", 10, FALSE },
+ { RF5_HOLD, "Paralyse", 10, FALSE },
+
+ { RF6_HASTE, "Haste Self", 50, FALSE },
+ { RF6_HAND_DOOM, "Hand of Doom", 30, TRUE },
+ { RF6_HEAL, "Healing", 60, FALSE },
+ { RF6_S_ANIMALS, "Summon Animals", 60, TRUE },
+ { RF6_BLINK, "Phase Door", 2, FALSE },
+ { RF6_TPORT, "Teleport", 10, FALSE },
+ { RF6_TELE_TO, "Teleport To", 20, TRUE },
+ { RF6_TELE_AWAY, "Teleport Away", 20, FALSE },
+ { RF6_TELE_LEVEL, "Teleport Level", 20, TRUE },
+ { RF6_DARKNESS, "Darkness", 3, FALSE },
+ { RF6_TRAPS, "Create Traps", 10, TRUE },
+ { 0, "(none)", 0, FALSE },
+ { RF6_RAISE_DEAD, "Raise the Dead", 400, TRUE },
+ { 0, "(none)", 0, FALSE },
+ { 0, "(none)", 0, FALSE },
+ { RF6_S_THUNDERLORD, "Summon Thunderlords", 90, TRUE },
+ { RF6_S_KIN, "Summon Kin", 80, FALSE },
+ { RF6_S_HI_DEMON, "Summon Greater Demons", 90, TRUE },
+ { RF6_S_MONSTER, "Summon Monster", 50, FALSE },
+ { RF6_S_MONSTERS, "Summon Monsters", 60, TRUE },
+ { RF6_S_ANT, "Summon Ants", 30, FALSE },
+ { RF6_S_SPIDER, "Summon Spider", 30, FALSE },
+ { RF6_S_HOUND, "Summon Hound", 50, TRUE },
+ { RF6_S_HYDRA, "Summon Hydra", 40, TRUE },
+ { RF6_S_ANGEL, "Summon Angel", 60, TRUE },
+ { RF6_S_DEMON, "Summon Demon", 60, TRUE },
+ { RF6_S_UNDEAD, "Summon Undead", 70, TRUE },
+ { RF6_S_DRAGON, "Summon Dragon", 70, TRUE },
+ { RF6_S_HI_UNDEAD, "Summon High Undead", 90, TRUE },
+ { RF6_S_HI_DRAGON, "Summon High Dragon", 90, TRUE },
+ { RF6_S_WRAITH, "Summon Wraith", 90, TRUE },
+ { 0, "(none)", 0, FALSE },
+ };
+
+/* Tval descriptions */
+tval_desc tval_descs[] =
+{
+ {
+ TV_BATERIE,
+ "Essences contain primitive magic forces which users of the "
+ "Alchemy skill can use to create powerful magic items from "
+ "other magic items."
+ },
+ {
+ TV_MSTAFF,
+ "Mage Staves are the spellcaster's weapons of choice. "
+ "They all reduce spellcasting time to 80% of "
+ "normal time and some will yield even greater powers."
+ },
+ {
+ 3,
+ "XXX"
+ },
+ {
+ TV_PARCHMENT,
+ "Parchments can contain useful information ... or useless "
+ "junk."
+ },
+ {
+ TV_EGG,
+ "Eggs are laid by some monsters. If they hatch in your "
+ "inventory the monster will be your friend."
+ },
+ {
+ TV_TOOL,
+ "Tools can be digging implements, climbing equipment and such. "
+ "They have their own slot in your inventory."
+
+ },
+ {
+ TV_INSTRUMENT,
+ "Musical instruments can be used with the Music skill to play "
+ "magical songs. Some of them can also be activated."
+ },
+ {
+ TV_BOOMERANG,
+ "Boomerangs can be used instead of bows or slings. They "
+ "are more like melee weapons than bows."
+ },
+ {
+ TV_SHOT,
+ "Shots are small, hard balls. They are the standard ammunition "
+ "for slings. You can carry them in your quiver if you have a sling "
+ "equipped."
+ },
+ {
+ TV_ARROW,
+ "Arrows are the standard ammunition for bows. You can carry "
+ "them in your quiver if you have a bow equipped."
+ },
+ {
+ TV_BOLT,
+ "Bolts are the standard ammunition for crossbows. You can "
+ "carry them in your quiver if you have a crossbow equipped."
+ },
+ {
+ TV_BOW,
+ "Slings, bows and crossbows are used to attack monsters "
+ "from a distance."
+ },
+ {
+ TV_DIGGING,
+ "Tools can be digging implements, climbing equipment and such. "
+ "They have their own slot in your inventory."
+ },
+ {
+ TV_HAFTED,
+ "Hafted weapons are melee weapons. Eru followers can use them "
+ "without penalties."
+ },
+ {
+ TV_SWORD,
+ "Swords are melee weapons."
+ },
+ {
+ TV_AXE,
+ "Axes are melee weapons."
+ },
+ {
+ TV_POLEARM,
+ "Polearms are melee weapons."
+ },
+ {
+ TV_DRAG_ARMOR,
+ "Dragon armour is made from the scales of dead dragons. "
+ "These mighty sets of armour usually yield great power to "
+ "their wearer."
+ },
+ {
+ TV_LITE,
+ "Lights allow you to read things and see from afar. Some of "
+ "them need to be fueled but some do not."
+ },
+ {
+ TV_AMULET,
+ "Amulets are fine pieces of jewelry, usually imbued with "
+ "arcane magics."
+ },
+ {
+ TV_RING,
+ "Rings are fine pieces of jewelry, usually imbued with "
+ "arcane magics."
+ },
+ {
+ TV_TRAPKIT,
+ "Trapping kits are used with the trapping ability to set "
+ "deadly monster traps."
+ },
+ {
+ TV_STAFF,
+ "Staves are objects imbued with mystical powers."
+ },
+ {
+ TV_WAND,
+ "Wands are like small staves and usually have a targeted "
+ "effect."
+ },
+ {
+ TV_ROD,
+ "Rod tips are the physical bindings of powerful "
+ "spells. Zap (attach) them to a rod to get a fully "
+ "functional rod. Each spell takes some mana from the rod "
+ "it is attached to to work."
+ },
+ {
+ TV_ROD_MAIN,
+ "Rods contain mana reserves used to cast spells in rod "
+ "tips. Zap (attach) a rod tip to them to get a fully "
+ "functional rod. Each spell takes some mana from the rod "
+ "it is attached to to work."
+ },
+ {
+ TV_SCROLL,
+ "Scrolls are magical parchments imbued with magic spells. "
+ "Some are good, some...are not. When a scroll is read, its "
+ "magic is released and the scroll is destroyed."
+ },
+ {
+ TV_POTION,
+ "Potions are magical liquids. Some of them are "
+ "beneficial...some not."
+ },
+ {
+ TV_POTION2,
+ "Potions are magical liquids. Some of them are "
+ "beneficial...some not."
+ },
+ {
+ TV_FLASK,
+ "Flasks of oil can be used to refill lanterns."
+ },
+ {
+ TV_FOOD,
+ "Everybody needs to eat, even you."
+ },
+ {
+ TV_HYPNOS,
+ "This monster seems to be hypnotised and friendly."
+ },
+ {
+ TV_RANDART,
+ "Those objects are only known of by rumours. It is said that "
+ "they can be activated for great or strange effects..."
+ },
+ {
+ TV_RUNE1,
+ "Runes are used with the Runecraft skill to create brand new spells."
+ },
+ {
+ TV_RUNE2,
+ "Runes are used with the Runecraft skill to create brand new spells."
+ },
+ {
+ TV_JUNK,
+ "Junk is usually worthless, though experienced archers can "
+ "create ammo with them."
+ },
+ {
+ TV_SKELETON,
+ "It looks dead..."
+ },
+ {
+ TV_BOTTLE,
+ "An empty bottle. Maybe an alchemist could refill it."
+ },
+ {
+ TV_SPIKE,
+ "Spikes can be used to jam doors."
+ },
+ {
+ TV_CORPSE,
+ "It looks dead..."
+ },
+ {
+ TV_BOOTS,
+ "Boots can help your armour rating. Some of these are magical."
+ },
+ {
+ TV_GLOVES,
+ "Handgear is used to protect hands, but nonmagical ones "
+ "can sometimes hinder spellcasting. Alchemists need "
+ "gloves in order to do alchemy."
+ },
+ {
+ TV_HELM,
+ "Headgear will protect your head."
+ },
+ {
+ TV_CROWN,
+ "Headgear will protect your head."
+ },
+ {
+ TV_SHIELD,
+ "Shields will help improve your defence rating, but you "
+ "cannot use them with two handed weapons."
+ },
+ {
+ TV_CLOAK,
+ "Cloaks can shield you from damage. Sometimes they also "
+ "provide magical powers."
+ },
+ {
+ TV_SOFT_ARMOR,
+ "Soft armour is light, and will not hinder your combat much."
+ },
+ {
+ TV_HARD_ARMOR,
+ "Hard armour provides much more protection than soft "
+ "armour but also hinders combat much more."
+ },
+ {
+ TV_SYMBIOTIC_BOOK,
+ "This mystical book is used by symbiants to extend their "
+ "symbiosis."
+ },
+ {
+ TV_MUSIC_BOOK,
+ "This song book is used by bards to play songs."
+ },
+ {
+ TV_DRUID_BOOK,
+ "This mystical book is used by druids to call upon the "
+ "powers of nature."
+ },
+ {
+ TV_DAEMON_BOOK,
+ "This unholy demon equipment is used with the Demonology skill to control "
+ "the school of demon power."
+ },
+ {0, ""},
+};
+
+/*
+ * List of the between exits
+ * s16b corresp; Corresponding between gate
+ * bool_ dungeon; Do we exit in a dungeon or in the wild ?
+ *
+ * s16b wild_x, wild_y; Wilderness spot to land onto
+ * s16b p_ptr->px, p_ptr->py; Location of the map
+ *
+ * s16b d_idx; Dungeon to land onto
+ * s16b level;
+ */
+between_exit between_exits[MAX_BETWEEN_EXITS] =
+{
+ {
+ 1,
+ FALSE,
+ 49, 11,
+ 119, 25,
+ 0, 0
+ },
+ {
+ 0,
+ FALSE,
+ 60, 56,
+ 10, 35,
+ 0, 0
+ },
+};
+
+/*
+ * Months
+ */
+int month_day[9] =
+{
+ 0, /* 1 day */
+
+ 1, /* 54 days */
+ 55, /* 72 days */
+ 127, /* 54 days */
+
+ 181, /* 3 days */
+
+ 184, /* 54 days */
+ 238, /* 72 days */
+ 310, /* 54 days */
+
+ 364, /* 1 day */
+};
+
+cptr month_name[9] =
+{
+ "Yestare",
+
+ "Tuile",
+ "Laire",
+ "Yavie",
+
+ "Enderi",
+
+ "Quelle",
+ "Hrive",
+ "Coire",
+
+ "Mettare",
+};
+
+/*
+ * max body parts
+ */
+int max_body_part[BODY_MAX] =
+{
+ 3, /* Weapon */
+ 1, /* Torso */
+ 3, /* Arms */
+ 6, /* Finger */
+ 2, /* Head */
+ 2, /* Legs */
+};
+
+/*
+ * Description of GF_FOO
+ */
+gf_name_type gf_names[] =
+{
+ { GF_ELEC, "electricity" },
+ { GF_POIS, "poison" },
+ { GF_ACID, "acid" },
+ { GF_COLD, "cold" },
+ { GF_FIRE, "fire" },
+ { GF_UNBREATH, "asphyxiating gas" },
+ { GF_CORPSE_EXPL, "corpse explosion" },
+ { GF_MISSILE, "missile" },
+ { GF_ARROW, "arrow" },
+ { GF_PLASMA, "plasma" },
+ { GF_WAVE, "a tidal wave" },
+ { GF_WATER, "water" },
+ { GF_LITE, "light" },
+ { GF_DARK, "darkness" },
+ { GF_LITE_WEAK, "weak light" },
+ { GF_DARK_WEAK, "weak darkness" },
+ { GF_SHARDS, "shards" },
+ { GF_SOUND, "sound" },
+ { GF_CONFUSION, "confusion" },
+ { GF_FORCE, "force" },
+ { GF_INERTIA, "inertia" },
+ { GF_MANA, "pure mana" },
+ { GF_METEOR, "meteor" },
+ { GF_ICE, "ice" },
+ { GF_CHAOS, "chaos" },
+ { GF_NETHER, "nether" },
+ { GF_DISENCHANT, "disenchantment" },
+ { GF_NEXUS, "nexus" },
+ { GF_TIME, "time" },
+ { GF_GRAVITY, "gravity" },
+ { GF_KILL_WALL, "wall destruction" },
+ { GF_KILL_DOOR, "door destruction" },
+ { GF_KILL_TRAP, "trap destruction" },
+ { GF_MAKE_WALL, "wall creation" },
+ { GF_MAKE_DOOR, "door creation" },
+ { GF_MAKE_TRAP, "trap creation" },
+ { GF_OLD_CLONE, "clone" },
+ { GF_OLD_POLY, "polymorph" },
+ { GF_OLD_HEAL, "healing" },
+ { GF_OLD_SPEED, "speed" },
+ { GF_OLD_SLOW, "slowness" },
+ { GF_OLD_CONF, "confusion" },
+ { GF_OLD_SLEEP, "sleep" },
+ { GF_OLD_DRAIN, "drain life" },
+ { GF_AWAY_UNDEAD, "teleport away undead" },
+ { GF_AWAY_EVIL, "teleport away evil" },
+ { GF_AWAY_ALL, "teleport away" },
+ { GF_TURN_UNDEAD, "scare undead" },
+ { GF_TURN_EVIL, "scare evil" },
+ { GF_TURN_ALL, "scare" },
+ { GF_DISP_UNDEAD, "dispel undead" },
+ { GF_DISP_EVIL, "dispel evil" },
+ { GF_DISP_ALL, "dispel" },
+ { GF_DISP_DEMON, "dispel demons" },
+ { GF_DISP_LIVING, "dispel living creatures" },
+ { GF_ROCKET, "rocket" },
+ { GF_NUKE, "nuke" },
+ { GF_MAKE_GLYPH, "glyph creation" },
+ { GF_STASIS, "stasis" },
+ { GF_STONE_WALL, "stone wall creation" },
+ { GF_DEATH_RAY, "death ray" },
+ { GF_STUN, "stunning" },
+ { GF_HOLY_FIRE, "holy fire" },
+ { GF_HELL_FIRE, "hellfire" },
+ { GF_DISINTEGRATE, "disintegration" },
+ { GF_CHARM, "charming" },
+ { GF_CONTROL_UNDEAD, "undead control" },
+ { GF_CONTROL_ANIMAL, "animal control" },
+ { GF_PSI, "psionic energy" },
+ { GF_PSI_DRAIN, "psionic drain" },
+ { GF_TELEKINESIS, "telekinesis" },
+ { GF_JAM_DOOR, "door jamming" },
+ { GF_DOMINATION, "domination" },
+ { GF_DISP_GOOD, "dispel good" },
+ { GF_IDENTIFY, "identification" },
+ { GF_RAISE, "raise dead" },
+ { GF_STAR_IDENTIFY, "*identification*" },
+ { GF_DESTRUCTION, "destruction" },
+ { GF_STUN_CONF, "stunning and confusion" },
+ { GF_STUN_DAM, "stunning and damage" },
+ { GF_CONF_DAM, "confusion and damage" },
+ { GF_STAR_CHARM, "*charming*" },
+ { GF_IMPLOSION, "implosion" },
+ { GF_LAVA_FLOW, "lava" },
+ { GF_FEAR, "fear" },
+ { GF_BETWEEN_GATE, "jumpgate creation" },
+ { GF_WINDS_MANA, "" },
+ { GF_DEATH, "death" },
+ { GF_CONTROL_DEMON, "control demon" },
+ { GF_RAISE_DEMON, "raise demon" },
+ { GF_TRAP_DEMONSOUL, "*control demon*" },
+ { GF_ATTACK, "projected melee attacks" },
+ { -1, NULL },
+};
diff --git a/src/traps.c b/src/traps.c
new file mode 100644
index 00000000..1c8e36c9
--- /dev/null
+++ b/src/traps.c
@@ -0,0 +1,3169 @@
+/* File: traps.c */
+
+/* Purpose: handle traps */
+
+/* the below copyright probably still applies, but it is heavily changed
+ * copied, adapted & re-engineered by JK.
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+bool_ do_player_trap_call_out(void)
+{
+ s16b i, sn, cx, cy;
+ s16b h_index = 0;
+ s16b h_level = 0;
+ monster_type *m_ptr;
+ char m_name[80];
+ bool_ ident = FALSE;
+
+ for (i = 1; i < m_max; i++)
+ {
+ m_ptr = &m_list[i];
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->level >= h_level)
+ {
+ h_level = m_ptr->level;
+ h_index = i;
+ }
+ }
+
+ /* if the level is empty of monsters, h_index will be 0 */
+ if (!h_index) return (FALSE);
+
+ m_ptr = &m_list[h_index];
+
+ sn = 0;
+ for (i = 0; i < 8; i++)
+ {
+ cx = p_ptr->px + ddx[i];
+ cy = p_ptr->py + ddy[i];
+
+ /* Skip non-empty grids */
+ if (!cave_valid_bold(cy, cx)) continue;
+ if (cave[cy][cx].feat == FEAT_GLYPH) continue;
+ if ((cx == p_ptr->px) && (cy == p_ptr->py)) continue;
+ sn++;
+
+ /* Randomize choice */
+ if (rand_int(sn) > 0) continue;
+ cave[cy][cx].m_idx = h_index;
+ cave[m_ptr->fy][m_ptr->fx].m_idx = 0;
+ m_ptr->fx = cx;
+ m_ptr->fy = cy;
+
+ /* we do not change the sublevel! */
+ ident = TRUE;
+ update_mon(h_index, TRUE);
+ monster_desc(m_name, m_ptr, 0x08);
+ msg_format("You hear a rapid-shifting wail, and %s appears!", m_name);
+ break;
+ }
+
+ return (ident);
+}
+
+static bool_ do_trap_teleport_away(object_type *i_ptr, s16b y, s16b x)
+{
+ bool_ ident = FALSE;
+ char o_name[80];
+
+ s16b o_idx = 0;
+ object_type *o_ptr;
+ cave_type *c_ptr;
+
+ s16b x1;
+ s16b y1;
+
+ if (i_ptr == NULL) return (FALSE);
+
+ if (i_ptr->name1 == ART_POWER) return (FALSE);
+
+ while (o_idx == 0)
+ {
+ x1 = rand_int(cur_wid);
+ y1 = rand_int(cur_hgt);
+
+ /* Obtain grid */
+ c_ptr = &cave[y1][x1];
+
+ /* Require floor space (or shallow terrain) -KMW- */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_FLOOR)) continue;
+
+ o_idx = drop_near(i_ptr, 0, y1, x1);
+ }
+
+ o_ptr = &o_list[o_idx];
+
+ x1 = o_ptr->ix;
+ y1 = o_ptr->iy;
+
+ if (!p_ptr->blind)
+ {
+ note_spot(y, x);
+ lite_spot(y, x);
+ ident = TRUE;
+ object_desc(o_name, i_ptr, FALSE, 0);
+ if (player_has_los_bold(y1, x1))
+ {
+ lite_spot(y1, x1);
+ msg_format("The %s suddenly stands elsewhere.", o_name);
+
+ }
+ else
+ {
+ msg_format("You suddenly don't see the %s any more!", o_name);
+ }
+ }
+ else
+ {
+ msg_print("You hear something move.");
+ }
+ return (ident);
+}
+
+/*
+ * this handles a trap that places walls around the player
+ */
+static bool_ player_handle_trap_of_walls(void)
+{
+ bool_ ident;
+
+ s16b dx, dy, cx, cy;
+ s16b sx = 0, sy = 0, sn, i;
+ cave_type *cv_ptr;
+ bool_ map[5][5] =
+ {
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE}
+ };
+
+ for (dy = -2; dy <= 2; dy++)
+ for (dx = -2; dx <= 2; dx++)
+ {
+ /* Extract the location */
+ cx = p_ptr->px + dx;
+ cy = p_ptr->py + dy;
+
+ if (!in_bounds(cy, cx)) continue;
+
+ cv_ptr = &cave[cy][cx];
+
+ if (cv_ptr->m_idx) continue;
+
+ /* Lose room and vault */
+ cv_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+ /* Lose light and knowledge */
+ cv_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Skip the center */
+ if (!dx && !dy) continue;
+
+ /* test for dungeon level */
+ if (randint(100) > 10 + max_dlv[dungeon_type]) continue;
+
+ /* Damage this grid */
+ map[2 + dx][2 + dy] = TRUE;
+ }
+
+ for (dy = -2; dy <= 2; dy++)
+ for (dx = -2; dx <= 2; dx++)
+ {
+ /* Extract the location */
+ cx = p_ptr->px + dx;
+ cy = p_ptr->py + dy;
+
+ /* Skip unaffected grids */
+ if (!map[2 + dx][2 + dy]) continue;
+
+ cv_ptr = &cave[cy][cx];
+
+ if (cv_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[cv_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Most monsters cannot co-exist with rock */
+ if ((!(r_ptr->flags2 & RF2_KILL_WALL)) &&
+ (!(r_ptr->flags2 & RF2_PASS_WALL)))
+ {
+ char m_name[80];
+
+ /* Assume not safe */
+ sn = 0;
+
+ /* Monster can move to escape the wall */
+ if (!(r_ptr->flags1 & RF1_NEVER_MOVE))
+ {
+ /* Look for safety */
+ for (i = 0; i < 8; i++)
+ {
+ /* Access the grid */
+ cy = p_ptr->py + ddy[i];
+ cx = p_ptr->px + ddx[i];
+
+ /* Skip non-empty grids */
+ if (!cave_clean_bold(cy, cx)) continue;
+
+ /* Hack -- no safety on glyph of warding */
+ if (cave[cy][cx].feat == FEAT_GLYPH) continue;
+
+ /* Important -- Skip "quake" grids */
+ if (map[2 + (cx - p_ptr->px)][2 + (cy - p_ptr->py)]) continue;
+
+ /* Count "safe" grids */
+ sn++;
+
+ /* Randomize choice */
+ if (rand_int(sn) > 0) continue;
+
+ /* Save the safe grid */
+ sx = cx;
+ sy = cy;
+
+ ident = TRUE;
+
+ break; /* discontinue for loop - safe grid found */
+ }
+ }
+
+ /* Describe the monster */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Scream in pain */
+ msg_format("%^s wails out in pain!", m_name);
+
+ /* Monster is certainly awake */
+ m_ptr->csleep = 0;
+
+ /* Apply damage directly */
+ m_ptr->hp -= (sn ? damroll(4, 8) : 200);
+
+ /* Delete (not kill) "dead" monsters */
+ if (m_ptr->hp < 0)
+ {
+ /* Message */
+ msg_format("%^s is entombed in the rock!", m_name);
+
+ /* Delete the monster */
+ delete_monster_idx(cave[cy][cx].m_idx);
+
+ /* No longer safe */
+ sn = 0;
+ }
+
+ /* Hack -- Escape from the rock */
+ if (sn)
+ {
+ s16b m_idx = cave[cy][cx].m_idx;
+
+ /* Update the new location */
+ cave[sy][sx].m_idx = m_idx;
+
+ /* Update the old location */
+ cave[cy][cx].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = sy;
+ m_ptr->fx = sx;
+
+ /* do not change fz */
+ /* don't make rock on that square! */
+ if ((sx >= (p_ptr->px - 2)) && (sx <= (p_ptr->px + 2)) &&
+ (sy >= (p_ptr->py - 2)) && (sy <= (p_ptr->py + 2)))
+ {
+ map[2 + (sx - p_ptr->px)][2 + (sy - p_ptr->py)] = FALSE;
+ }
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(cy, cx);
+
+ /* Redraw the new grid */
+ lite_spot(sy, sx);
+ } /* if sn */
+ } /* if monster can co-exist with rock */
+ } /* if monster on square */
+ }
+
+ /* Examine the quaked region */
+ for (dy = -2; dy <= 2; dy++)
+ for (dx = -2; dx <= 2; dx++)
+ {
+ /* Extract the location */
+ cx = p_ptr->px + dx;
+ cy = p_ptr->py + dy;
+
+ /* Skip unaffected grids */
+ if (!map[2 + dx][2 + dy]) continue;
+
+ /* Access the cave grid */
+ cv_ptr = &cave[cy][cx];
+
+ /* Paranoia -- never affect player */
+ if (!dy && !dx) continue;
+
+ /* Destroy location (if valid) */
+ if ((cx < cur_wid) && (cy < cur_hgt) && cave_valid_bold(cy, cx))
+ {
+ bool_ floor = (f_info[cave[cy][cx].feat].flags1 & FF1_FLOOR);
+
+ /* Delete any object that is still there */
+ delete_object(cy, cx);
+
+ if (floor)
+ {
+ cave_set_feat(cy, cx, FEAT_WALL_OUTER);
+ }
+ else
+ {
+ /* Clear previous contents, add floor */
+ cave_set_feat(cy, cx, FEAT_FLOOR);
+ }
+ }
+ }
+
+ /* Mega-Hack -- Forget the view and lite */
+ p_ptr->update |= PU_UN_VIEW;
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Update the health bar */
+ p_ptr->redraw |= (PR_HEALTH);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ handle_stuff();
+
+ msg_print("Suddenly the cave shifts around you. The air is getting stale!");
+
+ ident = TRUE;
+
+ return (ident);
+}
+
+
+/*
+ * this function handles arrow & dagger traps, in various types.
+ * num = number of missiles
+ * tval, sval = kind of missiles
+ * dd,ds = damage roll for missiles
+ * poison_dam = additional poison damage
+ * name = name given if you should die from it...
+ *
+ * return value = ident (always TRUE)
+ */
+static bool_ player_handle_missile_trap(s16b num, s16b tval, s16b sval, s16b dd, s16b ds,
+ s16b pdam, cptr name)
+{
+ object_type *o_ptr, forge;
+ s16b i, k_idx = lookup_kind(tval, sval);
+ char i_name[80];
+
+ o_ptr = &forge;
+ object_prep(o_ptr, k_idx);
+ o_ptr->number = num;
+ apply_magic(o_ptr, max_dlv[dungeon_type], FALSE, FALSE, FALSE);
+ object_desc(i_name, o_ptr, TRUE, 0);
+
+ msg_format("Suddenly %s hit%s you!", i_name,
+ ((num == 1) ? "" : "s"));
+
+ for (i = 0; i < num; i++)
+ {
+ take_hit(damroll(dd, ds), name);
+
+ redraw_stuff();
+
+ if (pdam > 0)
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ (void)set_poisoned(p_ptr->poisoned + pdam);
+ }
+ }
+ }
+
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+
+ return TRUE;
+}
+
+/*
+ * this function handles a "breath" type trap - acid bolt, lightning balls etc.
+ */
+static bool_ player_handle_breath_trap(s16b rad, s16b type, u16b trap)
+{
+ trap_type *t_ptr = &t_info[trap];
+ bool_ ident;
+ s16b my_dd, my_ds, dam;
+
+ my_dd = t_ptr->dd;
+ my_ds = t_ptr->ds;
+
+ /* these traps gets nastier as levels progress */
+ if (max_dlv[dungeon_type] > (2 * t_ptr->minlevel))
+ {
+ my_dd += (max_dlv[dungeon_type] / 15);
+ my_ds += (max_dlv[dungeon_type] / 15);
+ }
+ dam = damroll(my_dd, my_ds);
+
+ ident = project( -2, rad, p_ptr->py, p_ptr->px, dam, type, PROJECT_KILL | PROJECT_JUMP);
+
+ return (ident);
+}
+
+/*
+ * This function damages the player by a trap
+ */
+static void trap_hit(s16b trap)
+{
+ s16b dam;
+ trap_type *t_ptr = &t_info[trap];
+
+ dam = damroll(t_ptr->dd, t_ptr->ds);
+
+ take_hit(dam, t_name + t_ptr->name);
+}
+
+/*
+ * this function activates one trap type, and returns
+ * a bool_ indicating if this trap is now identified
+ */
+bool_ player_activate_trap_type(s16b y, s16b x, object_type *i_ptr, s16b item)
+{
+ bool_ ident = FALSE;
+ s16b trap;
+
+ s16b k, l;
+
+ trap = cave[y][x].t_idx;
+
+ if (i_ptr != NULL)
+ {
+ trap = i_ptr->pval;
+ }
+
+ if ((i_ptr == NULL) && (cave[y][x].o_idx != 0))
+ {
+ i_ptr = &o_list[cave[y][x].o_idx];
+ }
+
+ switch (trap)
+ {
+ /* stat traps */
+ case TRAP_OF_WEAKNESS_I:
+ ident = do_dec_stat(A_STR, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_WEAKNESS_II:
+ ident = do_dec_stat(A_STR, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_WEAKNESS_III:
+ ident = do_dec_stat(A_STR, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_INTELLIGENCE_I:
+ ident = do_dec_stat(A_INT, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_INTELLIGENCE_II:
+ ident = do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_INTELLIGENCE_III:
+ ident = do_dec_stat(A_INT, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_WISDOM_I:
+ ident = do_dec_stat(A_WIS, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_WISDOM_II:
+ ident = do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_WISDOM_III:
+ ident = do_dec_stat(A_WIS, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_FUMBLING_I:
+ ident = do_dec_stat(A_DEX, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_FUMBLING_II:
+ ident = do_dec_stat(A_DEX, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_FUMBLING_III:
+ ident = do_dec_stat(A_DEX, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_WASTING_I:
+ ident = do_dec_stat(A_CON, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_WASTING_II:
+ ident = do_dec_stat(A_CON, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_WASTING_III:
+ ident = do_dec_stat(A_CON, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_BEAUTY_I:
+ ident = do_dec_stat(A_CHR, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_BEAUTY_II:
+ ident = do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_BEAUTY_III:
+ ident = do_dec_stat(A_CHR, STAT_DEC_PERMANENT);
+ break;
+
+ /* Trap of Curse Weapon */
+ case TRAP_OF_CURSE_WEAPON:
+ {
+ ident = curse_weapon();
+ break;
+ }
+
+ /* Trap of Curse Armor */
+ case TRAP_OF_CURSE_ARMOR:
+ {
+ ident = curse_armor();
+ break;
+ }
+
+ /* Earthquake Trap */
+ case TRAP_OF_EARTHQUAKE:
+ {
+ msg_print("As you touch the trap, the ground starts to shake.");
+ earthquake(y, x, 10);
+ ident = TRUE;
+ break;
+ }
+
+ /* Poison Needle Trap */
+ case TRAP_OF_POISON_NEEDLE:
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ msg_print("You prick yourself on a poisoned needle.");
+ (void)set_poisoned(p_ptr->poisoned + rand_int(15) + 10);
+ ident = TRUE;
+ }
+ else
+ {
+ msg_print("You prick yourself on a needle.");
+ }
+ break;
+ }
+
+ /* Summon Monster Trap */
+ case TRAP_OF_SUMMON_MONSTER:
+ {
+ msg_print("A spell hangs in the air.");
+ for (k = 0; k < randint(3); k++)
+ {
+ ident |= summon_specific(y, x, max_dlv[dungeon_type], 0);
+ }
+ break;
+ }
+
+ /* Summon Undead Trap */
+ case TRAP_OF_SUMMON_UNDEAD:
+ {
+ msg_print("A mighty spell hangs in the air.");
+ for (k = 0; k < randint(3); k++)
+ {
+ ident |= summon_specific(y, x, max_dlv[dungeon_type],
+ SUMMON_UNDEAD);
+ }
+ break;
+ }
+
+ /* Summon Greater Undead Trap */
+ case TRAP_OF_SUMMON_GREATER_UNDEAD:
+ {
+ msg_print("An old and evil spell hangs in the air.");
+ for (k = 0; k < randint(3); k++)
+ {
+ ident |= summon_specific(y, x, max_dlv[dungeon_type],
+ SUMMON_HI_UNDEAD);
+ }
+ break;
+ }
+
+ /* Teleport Trap */
+ case TRAP_OF_TELEPORT:
+ {
+ msg_print("The world whirls around you.");
+ teleport_player(RATIO * 67);
+ ident = TRUE;
+ break;
+ }
+
+ /* Paralyzing Trap */
+ case TRAP_OF_PARALYZING:
+ {
+ if (!p_ptr->free_act)
+ {
+ msg_print("You touch a poisoned part and can't move.");
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(10) + 10);
+ ident = TRUE;
+ }
+ else
+ {
+ msg_print("You prick yourself on a needle.");
+ }
+ break;
+ }
+
+ /* Explosive Device */
+ case TRAP_OF_EXPLOSIVE_DEVICE:
+ {
+ msg_print("A hidden explosive device explodes in your face.");
+ take_hit(damroll(5, 8), "an explosion");
+ ident = TRUE;
+ break;
+ }
+
+ /* Teleport Away Trap */
+ case TRAP_OF_TELEPORT_AWAY:
+ {
+ int item, amt;
+ object_type *o_ptr;
+
+ /* teleport away all items */
+ while (cave[y][x].o_idx != 0)
+ {
+ item = cave[y][x].o_idx;
+
+ o_ptr = &o_list[item];
+
+ amt = o_ptr->number;
+
+ ident = do_trap_teleport_away(o_ptr, y, x);
+
+ floor_item_increase(item, -amt);
+ floor_item_optimize(item);
+ }
+ break;
+ }
+
+ /* Lose Memory Trap */
+ case TRAP_OF_LOSE_MEMORY:
+ {
+ lose_exp(p_ptr->exp / 4);
+
+ ident |= dec_stat(A_WIS, rand_int(20) + 10, STAT_DEC_NORMAL);
+ ident |= dec_stat(A_INT, rand_int(20) + 10, STAT_DEC_NORMAL);
+
+ if (!p_ptr->resist_conf)
+ {
+ ident |= set_confused(p_ptr->confused + rand_int(100) + 50);
+ }
+
+ if (ident)
+ {
+ msg_print("You suddenly don't remember what you were doing.");
+ }
+ else
+ {
+ msg_print("You feel an alien force probing your mind.");
+ }
+ break;
+ }
+ /* Bitter Regret Trap */
+ case TRAP_OF_BITTER_REGRET:
+ {
+ msg_print("An age-old and hideous-sounding spell reverberates off the walls.");
+
+ ident |= dec_stat(A_DEX, 25, TRUE);
+ ident |= dec_stat(A_WIS, 25, TRUE);
+ ident |= dec_stat(A_CON, 25, TRUE);
+ ident |= dec_stat(A_STR, 25, TRUE);
+ ident |= dec_stat(A_CHR, 25, TRUE);
+ ident |= dec_stat(A_INT, 25, TRUE);
+ break;
+ }
+
+ /* Bowel Cramps Trap */
+ case TRAP_OF_BOWEL_CRAMPS:
+ {
+ msg_print("A wretched-smelling gas cloud upsets your stomach.");
+
+ (void)set_food(PY_FOOD_STARVE - 1);
+ (void)set_poisoned(0);
+
+ if (!p_ptr->free_act)
+ {
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(dun_level) + 6);
+ }
+ ident = TRUE;
+ break;
+ }
+
+ /* Blindness/Confusion Trap */
+ case TRAP_OF_BLINDNESS_CONFUSION:
+ {
+ msg_print("A powerful magic protected this.");
+
+ if (!p_ptr->resist_blind)
+ {
+ ident |= set_blind(p_ptr->blind + rand_int(100) + 100);
+ }
+ if (!p_ptr->resist_conf)
+ {
+ ident |= set_confused(p_ptr->confused + rand_int(20) + 15);
+ }
+ break;
+ }
+
+ /* Aggravation Trap */
+ case TRAP_OF_AGGRAVATION:
+ {
+ msg_print("You hear a hollow noise echoing through the dungeons.");
+ aggravate_monsters(1);
+ break;
+ }
+
+ /* Multiplication Trap */
+ case TRAP_OF_MULTIPLICATION:
+ {
+ msg_print("You hear a loud click.");
+ for (k = -1; k <= 1; k++)
+ for (l = -1; l <= 1; l++)
+ {
+ if ((in_bounds(p_ptr->py + l, p_ptr->px + k)) &&
+ (!cave[p_ptr->py + l][p_ptr->px + k].t_idx))
+ {
+ place_trap(p_ptr->py + l, p_ptr->px + k);
+ }
+ }
+ ident = TRUE;
+ break;
+ }
+
+ /* Steal Item Trap */
+ case TRAP_OF_STEAL_ITEM:
+ {
+ /*
+ * please note that magical stealing is not so
+ * easily circumvented
+ */
+ if (!p_ptr->paralyzed &&
+ (rand_int(160) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] +
+ p_ptr->lev)))
+ {
+ /* Saving throw message */
+ msg_print("Your backpack seems to vibrate strangely!");
+ break;
+ }
+
+ /* Find an item */
+ for (k = 0; k < rand_int(10); k++)
+ {
+ char i_name[80];
+ object_type *j_ptr, *q_ptr, forge;
+
+ /* Pick an item */
+ s16b i = rand_int(INVEN_PACK);
+
+ /* Obtain the item */
+ j_ptr = &p_ptr->inventory[i];
+
+ /* Accept real items */
+ if (!j_ptr->k_idx) continue;
+
+ /* Don't steal artifacts -CFT */
+ if (artifact_p(j_ptr)) continue;
+
+ /* Get a description */
+ object_desc(i_name, j_ptr, FALSE, 3);
+
+ /* Message */
+ msg_format("%sour %s (%c) was stolen!",
+ ((j_ptr->number > 1) ? "One of y" : "Y"),
+ i_name, index_to_label(i));
+
+ /* Create the item */
+ q_ptr = &forge;
+ object_copy(q_ptr, j_ptr);
+ q_ptr->number = 1;
+
+ /* Drop it somewhere */
+ do_trap_teleport_away(q_ptr, y, x);
+
+ inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE);
+
+ ident = TRUE;
+ }
+ break;
+ }
+
+ /* Summon Fast Quylthulgs Trap */
+ case TRAP_OF_SUMMON_FAST_QUYLTHULGS:
+ {
+ for (k = 0; k < randint(3); k++)
+ {
+ ident |= summon_specific(y, x, max_dlv[dungeon_type], SUMMON_QUYLTHULG);
+ }
+
+ if (ident)
+ {
+ msg_print("You suddenly have company.");
+ (void)set_slow(p_ptr->slow + randint(25) + 15);
+ }
+ break;
+ }
+
+ /* Trap of Sinking */
+ case TRAP_OF_SINKING:
+ {
+ msg_print("You fell through a trap door!");
+
+ if (p_ptr->ffall)
+ {
+ if (dungeon_flags1 & DF1_TOWER)
+ {
+ msg_print("You float gently down to the previous level.");
+ }
+ else
+ {
+ msg_print("You float gently down to the next level.");
+ }
+ }
+ else
+ {
+ take_hit(damroll(2, 8), "a trap door");
+ }
+
+ /* Still alive and autosave enabled */
+ if (p_ptr->chp >= 0)
+ {
+ autosave_checkpoint();
+ }
+
+ if (dungeon_flags1 & DF1_TOWER) dun_level--;
+ else dun_level++;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ break;
+ }
+
+ /* Trap of Mana Drain */
+ case TRAP_OF_MANA_DRAIN:
+ {
+ if (p_ptr->csp > 0)
+ {
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+ p_ptr->redraw |= (PR_MANA);
+ msg_print("You sense a great loss.");
+ ident = TRUE;
+ }
+ else if (p_ptr->msp == 0)
+ {
+ /* no sense saying this unless you never have mana */
+ msg_format("Suddenly you feel glad you're a mere %s",
+ spp_ptr->title + c_name);
+ }
+ else
+ {
+ msg_print("Your head feels dizzy for a moment.");
+ }
+ break;
+ }
+ /* Trap of Missing Money */
+ case TRAP_OF_MISSING_MONEY:
+ {
+ s32b gold = (p_ptr->au / 10) + randint(25);
+
+ if (gold < 2) gold = 2;
+ if (gold > 5000) gold = (p_ptr->au / 20) + randint(3000);
+ if (gold > p_ptr->au) gold = p_ptr->au;
+
+ p_ptr->au -= gold;
+ if (gold <= 0)
+ {
+ msg_print("You feel something touching you.");
+ }
+ else if (p_ptr->au)
+ {
+ msg_print("Your purse feels lighter.");
+ msg_format("%ld coins were stolen!", (long)gold);
+ ident = TRUE;
+ }
+ else
+ {
+ msg_print("Your purse feels empty.");
+ msg_print("All of your coins were stolen!");
+ ident = TRUE;
+ }
+ p_ptr->redraw |= (PR_GOLD);
+ break;
+ }
+
+ /* Trap of No Return */
+ case TRAP_OF_NO_RETURN:
+ {
+ object_type *j_ptr;
+ s16b j;
+
+ for (j = 0; j < INVEN_WIELD; j++)
+ {
+ if (!p_ptr->inventory[j].k_idx) continue;
+
+ j_ptr = &p_ptr->inventory[j];
+
+ if ((j_ptr->tval == TV_SCROLL) &&
+ (j_ptr->sval == SV_SCROLL_WORD_OF_RECALL))
+ {
+ inc_stack_size_ex(j, -j_ptr->number, OPTIMIZE, NO_DESCRIBE);
+
+ combine_pack();
+ reorder_pack();
+
+ if (!ident)
+ {
+ msg_print("A small fire works its way through your backpack. "
+ "Some scrolls are burnt.");
+ }
+ else
+ {
+ msg_print("The fire hasn't finished.");
+ }
+ ident = TRUE;
+ }
+ else if ((j_ptr->tval == TV_ROD_MAIN) &&
+ (j_ptr->pval == SV_ROD_RECALL))
+ {
+ j_ptr->timeout = 0; /* a long time */
+ if (!ident) msg_print("You feel the air stabilise around you.");
+ ident = TRUE;
+ }
+ }
+ if ((!ident) && (p_ptr->word_recall))
+ {
+ msg_print("You feel like staying around.");
+ p_ptr->word_recall = 0;
+ ident = TRUE;
+ }
+ break;
+ }
+
+ /* Trap of Silent Switching */
+ case TRAP_OF_SILENT_SWITCHING:
+ {
+ s16b i, j, slot1, slot2;
+ object_type *j_ptr, *k_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ j_ptr = &p_ptr->inventory[i];
+
+ if (!j_ptr->k_idx) continue;
+
+ /* Do not allow this trap to touch the One Ring */
+ object_flags(j_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if(f3 & TR3_PERMA_CURSE) continue;
+
+ slot1 = wield_slot(j_ptr);
+
+ for (j = 0; j < INVEN_WIELD; j++)
+ {
+ k_ptr = &p_ptr->inventory[j];
+
+ if (!k_ptr->k_idx) continue;
+
+ /* Do not allow this trap to touch the One Ring */
+ object_flags(k_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if(f3 & TR3_PERMA_CURSE) continue;
+
+ /* this is a crude hack, but it prevent wielding 6 torches... */
+ if (k_ptr->number > 1) continue;
+
+ slot2 = wield_slot(k_ptr);
+
+ /* a chance of 4 in 5 of switching something, then 2 in 5 to do it again */
+ if ((slot1 == slot2) &&
+ (rand_int(100) < (80 - (ident * 40))))
+ {
+ object_type tmp_obj;
+
+ if (p_ptr->inventory[j].name1)
+ wield_set(p_ptr->inventory[j].name1, a_info[p_ptr->inventory[j].name1].set, FALSE);
+ if (p_ptr->inventory[i].name1)
+ takeoff_set(p_ptr->inventory[i].name1, a_info[p_ptr->inventory[i].name1].set);
+
+ tmp_obj = p_ptr->inventory[j];
+ p_ptr->inventory[j] = p_ptr->inventory[i];
+ p_ptr->inventory[i] = tmp_obj;
+ ident = TRUE;
+ }
+ }
+ }
+
+ if (ident)
+ {
+ p_ptr->update |= (PU_BONUS);
+ p_ptr->update |= (PU_TORCH);
+ p_ptr->update |= (PU_MANA);
+ msg_print("You somehow feel like another person.");
+ }
+ else
+ {
+ msg_print("You feel a lack of useful items.");
+ }
+ break;
+ }
+
+ /* Trap of Walls */
+ case TRAP_OF_WALLS:
+ {
+ ident = player_handle_trap_of_walls();
+ break;
+ }
+
+ /* Trap of Calling Out */
+ case TRAP_OF_CALLING_OUT:
+ {
+ ident = do_player_trap_call_out();
+
+ if (!ident)
+ {
+ /* Increase "afraid" */
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You feel as if you had a nightmare!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You remember having a nightmare!");
+ }
+ else
+ {
+ if (set_afraid(p_ptr->afraid + 3 + randint(40)))
+ {
+ msg_print("You have a vision of a powerful enemy.");
+ }
+ }
+ }
+ break;
+ }
+
+ /* Trap of Sliding */
+ case TRAP_OF_SLIDING:
+ break;
+
+ /* Trap of Charges Drain */
+ case TRAP_OF_CHARGES_DRAIN:
+ {
+ /* Find an item */
+ for (k = 0; k < 10; k++)
+ {
+ s16b i = rand_int(INVEN_PACK);
+
+ object_type *j_ptr = &p_ptr->inventory[i];
+
+ /* Drain charged wands/staffs
+ Hack -- don't let artifacts get drained */
+ if (((j_ptr->tval == TV_STAFF) ||
+ (j_ptr->tval == TV_WAND)) &&
+ (j_ptr->pval) &&
+ !artifact_p(j_ptr))
+ {
+ ident = TRUE;
+ j_ptr->pval = j_ptr->pval / (randint(4) + 1);
+
+ /* 60% chance of only 1 */
+ if (randint(10) > 3) break;
+ }
+ }
+
+ if (ident)
+ {
+ /* Window stuff */
+ p_ptr->window |= PW_INVEN;
+ /* Combine / Reorder the pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ msg_print("Your backpack seems to be turned upside down.");
+ }
+ else
+ {
+ msg_print("You hear a wail of great disappointment.");
+ }
+ break;
+ }
+
+ /* Trap of Stair Movement */
+ case TRAP_OF_STAIR_MOVEMENT:
+ {
+ s16b cx, cy, i, j;
+ s16b cnt = 0;
+ s16b cnt_seen = 0;
+ s16b tmps, tmpx;
+ s16b tmpspecial, tmpspecial2;
+ u32b tmpf;
+ bool_ seen = FALSE;
+ s16b index_x[20], index_y[20]; /* 20 stairs per level is enough? */
+ cave_type *cv_ptr;
+
+ if (max_dlv[dungeon_type] == 99)
+ {
+ /* no sense in relocating that stair! */
+ msg_print("You have a feeling that this trap could be dangerous.");
+ break;
+ }
+
+ for (cx = 0; cx < cur_wid; cx++)
+ for (cy = 0; cy < cur_hgt; cy++)
+ {
+ cv_ptr = &cave[cy][cx];
+
+ if ((cv_ptr->feat != FEAT_LESS) &&
+ (cv_ptr->feat != FEAT_MORE) &&
+ (cv_ptr->feat != FEAT_SHAFT_UP) &&
+ (cv_ptr->feat != FEAT_SHAFT_DOWN)) continue;
+
+ index_x[cnt] = cx;
+ index_y[cnt] = cy;
+ cnt++;
+ }
+
+ if (cnt == 0)
+ {
+ if (wizard) msg_print("Executing moving stairs trap on level with no stairs!");
+ break;
+ }
+
+ for (i = 0; i < cnt; i++)
+ {
+ seen = FALSE;
+
+ for (j = 0; j < 10; j++) /* try 10 times to relocate */
+ {
+ cave_type *cv_ptr = &cave[index_y[i]][index_x[i]];
+ cave_type *cv_ptr2;
+
+ cx = rand_int(cur_wid);
+ cy = rand_int(cur_hgt);
+
+ if ((cx == index_x[i]) || (cy == index_y[i])) continue;
+
+ cv_ptr2 = &cave[cy][cx];
+
+ if (!cave_valid_bold(cy, cx) || cv_ptr2->o_idx != 0) continue;
+
+ /* don't put anything in vaults */
+ if (cv_ptr2->info & CAVE_ICKY) continue;
+
+ tmpx = cv_ptr2->mimic;
+ tmps = cv_ptr2->info;
+ tmpf = cv_ptr2->feat;
+ tmpspecial = cv_ptr2->special;
+ tmpspecial2 = cv_ptr2->special2;
+ cave[cy][cx].mimic = cv_ptr->mimic;
+ cave[cy][cx].info = cv_ptr->info;
+ cave[cy][cx].special = cv_ptr->special;
+ cave[cy][cx].special2 = cv_ptr->special2;
+ cave_set_feat(cy, cx, cv_ptr->feat);
+ cv_ptr->mimic = tmpx;
+ cv_ptr->info = tmps;
+ cv_ptr->special = tmpspecial;
+ cv_ptr->special2 = tmpspecial2;
+ cave_set_feat(index_y[i], index_x[i], tmpf);
+
+ /* if we are placing walls in rooms, make them rubble instead */
+ if ((cv_ptr->info & CAVE_ROOM) &&
+ (cv_ptr->feat >= FEAT_WALL_EXTRA) &&
+ (cv_ptr->feat <= FEAT_PERM_SOLID))
+ {
+ cave_set_feat(index_y[i], index_x[i], FEAT_RUBBLE);
+ }
+
+ if (player_has_los_bold(cy, cx))
+ {
+ note_spot(cy, cx);
+ lite_spot(cy, cx);
+ seen = TRUE;
+ }
+ else
+ {
+ cv_ptr2->info &= ~CAVE_MARK;
+ }
+
+ if (player_has_los_bold(index_y[i], index_x[i]))
+ {
+ note_spot(index_y[i], index_x[i]);
+ lite_spot(index_y[i], index_x[i]);
+ seen = TRUE;
+ }
+ else
+ {
+ cv_ptr->info &= ~CAVE_MARK;
+ }
+ break;
+ }
+
+ if (seen) cnt_seen++;
+ }
+
+ ident = (cnt_seen > 0);
+
+ if ((ident) && (cnt_seen > 1))
+ {
+ msg_print("You see some stairs move.");
+ }
+ else if (ident)
+ {
+ msg_print("You see a stair move.");
+ }
+ else
+ {
+ msg_print("You hear distant scraping noises.");
+ }
+ p_ptr->redraw |= PR_MAP;
+ break;
+ }
+
+ /* Trap of New Trap */
+ case TRAP_OF_NEW:
+ {
+ /* if we're on a floor or on a door, place a new trap */
+ if ((item == -1) || (item == -2))
+ {
+ place_trap(y, x);
+ if (player_has_los_bold(y, x))
+ {
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+ }
+ else
+ {
+ /* re-trap the chest */
+ place_trap(y, x);
+ }
+ msg_print("You hear a noise, and then its echo.");
+ ident = FALSE;
+ break;
+ }
+
+ /* Trap of Acquirement */
+ case TRAP_OF_ACQUIREMENT:
+ {
+ /* Get a nice thing */
+ msg_print("You notice something falling off the trap.");
+ acquirement(y, x, 1, TRUE, FALSE);
+
+ /* If we're on a floor or on a door, place a new trap */
+ if ((item == -1) || (item == -2))
+ {
+ place_trap(y, x);
+ if (player_has_los_bold(y, x))
+ {
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+ }
+ else
+ {
+ /* Re-trap the chest */
+ place_trap(y, x);
+ }
+ msg_print("You hear a noise, and then its echo.");
+
+ /* Never known */
+ ident = FALSE;
+ }
+ break;
+
+ /* Trap of Scatter Items */
+ case TRAP_OF_SCATTER_ITEMS:
+ {
+ s16b i, j;
+ bool_ message = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+
+ if (!p_ptr->inventory[i].k_idx) continue;
+
+ if (rand_int(10) < 3) continue;
+
+ for (j = 0; j < 10; j++)
+ {
+ object_type tmp_obj, *j_ptr = &tmp_obj;
+ s16b cx = x + 15 - rand_int(30);
+ s16b cy = y + 15 - rand_int(30);
+
+ if (!in_bounds(cy, cx)) continue;
+
+ if (!cave_floor_bold(cy, cx)) continue;
+
+ object_copy(j_ptr, &p_ptr->inventory[i]);
+
+ inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE);
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ (void)floor_carry(cy, cx, j_ptr);
+
+ if (!message)
+ {
+ msg_print("You feel light-footed.");
+ message = TRUE;
+ }
+
+ if (player_has_los_bold(cy, cx))
+ {
+ char i_name[80];
+
+ object_desc(i_name, &tmp_obj, TRUE, 3);
+ note_spot(cy, cx);
+ lite_spot(cy, cx);
+ ident = TRUE;
+ msg_format("Suddenly %s appear%s!", i_name,
+ (j_ptr->number > 1) ? "" : "s");
+ }
+ break;
+ }
+ }
+ ident = message;
+ break;
+ }
+
+ /* Trap of Decay */
+ case TRAP_OF_DECAY:
+ break;
+
+ /* Trap of Wasting Wands */
+ case TRAP_OF_WASTING_WANDS:
+ {
+ s16b i;
+ object_type *j_ptr;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ if (!p_ptr->inventory[i].k_idx) continue;
+
+ j_ptr = &p_ptr->inventory[i];
+
+ if ((j_ptr->tval == TV_WAND) && (rand_int(5) == 1))
+ {
+ if (object_known_p(j_ptr)) ident = TRUE;
+
+ /* Create a Wand of Nothing */
+ object_prep(j_ptr, lookup_kind(TV_WAND, SV_WAND_NOTHING));
+ hack_apply_magic_power = -99;
+ apply_magic(j_ptr, 0, FALSE, FALSE, FALSE);
+ j_ptr->ident &= ~IDENT_KNOWN;
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ else if ((j_ptr->tval == TV_STAFF) && (rand_int(5) == 1))
+ {
+ if (object_known_p(j_ptr)) ident = TRUE;
+
+ /* Create a Staff of Nothing */
+ object_prep(j_ptr, lookup_kind(TV_STAFF, SV_STAFF_NOTHING));
+ hack_apply_magic_power = -99;
+ apply_magic(j_ptr, 0, FALSE, FALSE, FALSE);
+ j_ptr->ident &= ~IDENT_KNOWN;
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+ if (ident)
+ {
+ msg_print("You have lost trust in your backpack!");
+ }
+ else
+ {
+ msg_print("You hear an echoing cry of rage.");
+ }
+ break;
+ }
+
+ /* Trap of Filling */
+ case TRAP_OF_FILLING:
+ {
+ s16b nx, ny;
+
+ for (nx = x - 8; nx <= x + 8; nx++)
+ for (ny = y - 8; ny <= y + 8; ny++)
+ {
+ if (!in_bounds (ny, nx)) continue;
+
+ if (rand_int(distance(ny, nx, y, x)) > 3)
+ {
+ place_trap(ny, nx);
+ }
+ }
+
+ msg_print("The floor vibrates in a strange way.");
+ ident = FALSE;
+ break;
+ }
+
+ case TRAP_OF_DRAIN_SPEED:
+ {
+ object_type *j_ptr;
+ s16b j, chance = 75;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ for (j = 0; j < INVEN_TOTAL; j++)
+ {
+ /* don't bother the overflow slot */
+ if (j == INVEN_PACK) continue;
+
+ if (!p_ptr->inventory[j].k_idx) continue;
+
+ j_ptr = &p_ptr->inventory[j];
+ object_flags(j_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* is it a non-artifact speed item? */
+ if ((!j_ptr->name1) && (f1 & TR1_SPEED))
+ {
+ if (randint(100) < chance)
+ {
+ j_ptr->pval = j_ptr->pval / 2;
+ if (j_ptr->pval == 0)
+ {
+ j_ptr->pval--;
+ }
+ chance /= 2;
+ ident = TRUE;
+ }
+ inven_item_optimize(j);
+ }
+ }
+ if (!ident)
+ {
+ msg_print("You feel some things in your pack vibrating.");
+ }
+ else
+ {
+ combine_pack();
+ reorder_pack();
+ msg_print("You suddenly feel you have time for self-reflection.");
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+ break;
+ }
+
+ /*
+ * single missile traps
+ */
+ case TRAP_OF_ARROW_I:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_NORMAL, 4, 8, 0, "Arrow Trap");
+ break;
+ case TRAP_OF_ARROW_II:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_NORMAL, 5, 8, 0, "Bolt Trap");
+ break;
+ case TRAP_OF_ARROW_III:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_HEAVY, 6, 8, 0, "Seeker Arrow Trap");
+ break;
+ case TRAP_OF_ARROW_IV:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_HEAVY, 8, 10, 0, "Seeker Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_I:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_NORMAL, 4, 8, 10 + randint(20), "Poison Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_II:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_NORMAL, 5, 8, 15 + randint(30), "Poison Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_III:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_HEAVY, 6, 8, 30 + randint(50), "Poison Seeker Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_IV:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_HEAVY, 8, 10, 40 + randint(70), "Poison Seeker Bolt Trap");
+ break;
+ case TRAP_OF_DAGGER_I:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_DAGGER_II:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_DAGGER, 3, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGER_I:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 15 + randint(20), "Poison Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGER_II:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_DAGGER, 3, 8, 20 + randint(30), "Poison Dagger Trap");
+ break;
+
+ /*
+ * multiple missile traps
+ * numbers range from 2 (level 0 to 14) to 10 (level 120 and up)
+ */
+ case TRAP_OF_ARROWS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_NORMAL, 4, 8, 0, "Arrow Trap");
+ break;
+ case TRAP_OF_ARROWS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_NORMAL, 5, 8, 0, "Bolt Trap");
+ break;
+ case TRAP_OF_ARROWS_III:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_HEAVY, 6, 8, 0, "Seeker Arrow Trap");
+ break;
+ case TRAP_OF_ARROWS_IV:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_HEAVY, 8, 10, 0, "Seeker Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_NORMAL, 4, 8, 10 + randint(20), "Poison Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_NORMAL, 5, 8, 15 + randint(30), "Poison Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_III:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_HEAVY, 6, 8, 30 + randint(50), "Poison Seeker Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_IV:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_HEAVY, 8, 10, 40 + randint(70), "Poison Seeker Bolt Trap");
+ break;
+ case TRAP_OF_DAGGERS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_DAGGERS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_DAGGER, 3, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGERS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 15 + randint(20), "Poison Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGERS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_DAGGER, 3, 8, 20 + randint(30), "Poison Dagger Trap");
+ break;
+
+ case TRAP_OF_DROP_ITEMS:
+ {
+ s16b i;
+ bool_ message = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type tmp_obj;
+
+ if (!p_ptr->inventory[i].k_idx) continue;
+ if (randint(100) < 80) continue;
+ if (p_ptr->inventory[i].name1 == ART_POWER) continue;
+
+ tmp_obj = p_ptr->inventory[i];
+
+ /* drop carefully */
+ drop_near(&tmp_obj, 0, y, x);
+
+ inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE);
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ if (!message)
+ {
+ msg_print("You are startled by a sudden sound.");
+ message = TRUE;
+ }
+ ident = TRUE;
+ }
+ if (!ident)
+ {
+ msg_print("You hear a sudden, strange sound.");
+ }
+ break;
+ }
+
+ case TRAP_OF_DROP_ALL_ITEMS:
+ {
+ s16b i;
+ bool_ message = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type tmp_obj;
+
+ if (!p_ptr->inventory[i].k_idx) continue;
+ if (randint(100) < 10) continue;
+ if (p_ptr->inventory[i].name1 == ART_POWER) continue;
+
+ tmp_obj = p_ptr->inventory[i];
+
+ /* drop carefully */
+ drop_near(&tmp_obj, 0, y, x);
+
+ inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE);
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ if (!message)
+ {
+ msg_print("You are greatly startled by a sudden sound.");
+ message = TRUE;
+ }
+ ident = TRUE;
+ }
+ if (!ident)
+ {
+ msg_print("You hear a sudden, strange sound.");
+ }
+ break;
+ }
+
+ case TRAP_OF_DROP_EVERYTHING:
+ {
+ s16b i;
+ bool_ message = FALSE;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type tmp_obj;
+ if (!p_ptr->inventory[i].k_idx) continue;
+ if (randint(100) < 30) continue;
+ if (p_ptr->inventory[i].name1 == ART_POWER) continue;
+
+ tmp_obj = p_ptr->inventory[i];
+ /* drop carefully */
+
+ drop_near(&tmp_obj, 0, y, x);
+
+ inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE);
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ if (!message)
+ {
+ msg_print("You are completely startled by a sudden sound.");
+ message = TRUE;
+ }
+ ident = TRUE;
+ }
+ if (!ident)
+ {
+ msg_print("You hear a sudden, strange sound.");
+ }
+ break;
+ }
+
+ /* Bolt Trap */
+ case TRAP_G_ELEC_BOLT:
+ ident = player_handle_breath_trap(1, GF_ELEC, TRAP_G_ELEC_BOLT);
+ break;
+ case TRAP_G_POIS_BOLT:
+ ident = player_handle_breath_trap(1, GF_POIS, TRAP_G_POIS_BOLT);
+ break;
+ case TRAP_G_ACID_BOLT:
+ ident = player_handle_breath_trap(1, GF_ACID, TRAP_G_ACID_BOLT);
+ break;
+ case TRAP_G_COLD_BOLT:
+ ident = player_handle_breath_trap(1, GF_COLD, TRAP_G_COLD_BOLT);
+ break;
+ case TRAP_G_FIRE_BOLT:
+ ident = player_handle_breath_trap(1, GF_FIRE, TRAP_G_FIRE_BOLT);
+ break;
+ case TRAP_OF_ELEC_BOLT:
+ ident = player_handle_breath_trap(1, GF_ELEC, TRAP_OF_ELEC_BOLT);
+ break;
+ case TRAP_OF_POIS_BOLT:
+ ident = player_handle_breath_trap(1, GF_POIS, TRAP_OF_POIS_BOLT);
+ break;
+ case TRAP_OF_ACID_BOLT:
+ ident = player_handle_breath_trap(1, GF_ACID, TRAP_OF_ACID_BOLT);
+ break;
+ case TRAP_OF_COLD_BOLT:
+ ident = player_handle_breath_trap(1, GF_COLD, TRAP_OF_COLD_BOLT);
+ break;
+ case TRAP_OF_FIRE_BOLT:
+ ident = player_handle_breath_trap(1, GF_FIRE, TRAP_OF_FIRE_BOLT);
+ break;
+ case TRAP_OF_PLASMA_BOLT:
+ ident = player_handle_breath_trap(1, GF_PLASMA, TRAP_OF_PLASMA_BOLT);
+ break;
+ case TRAP_OF_WATER_BOLT:
+ ident = player_handle_breath_trap(1, GF_WATER, TRAP_OF_WATER_BOLT);
+ break;
+ case TRAP_OF_LITE_BOLT:
+ ident = player_handle_breath_trap(1, GF_LITE, TRAP_OF_LITE_BOLT);
+ break;
+ case TRAP_OF_DARK_BOLT:
+ ident = player_handle_breath_trap(1, GF_DARK, TRAP_OF_DARK_BOLT);
+ break;
+ case TRAP_OF_SHARDS_BOLT:
+ ident = player_handle_breath_trap(1, GF_SHARDS, TRAP_OF_SHARDS_BOLT);
+ break;
+ case TRAP_OF_SOUND_BOLT:
+ ident = player_handle_breath_trap(1, GF_SOUND, TRAP_OF_SOUND_BOLT);
+ break;
+ case TRAP_OF_CONFUSION_BOLT:
+ ident = player_handle_breath_trap(1, GF_CONFUSION, TRAP_OF_CONFUSION_BOLT);
+ break;
+ case TRAP_OF_FORCE_BOLT:
+ ident = player_handle_breath_trap(1, GF_FORCE, TRAP_OF_FORCE_BOLT);
+ break;
+ case TRAP_OF_INERTIA_BOLT:
+ ident = player_handle_breath_trap(1, GF_INERTIA, TRAP_OF_INERTIA_BOLT);
+ break;
+ case TRAP_OF_MANA_BOLT:
+ ident = player_handle_breath_trap(1, GF_MANA, TRAP_OF_MANA_BOLT);
+ break;
+ case TRAP_OF_ICE_BOLT:
+ ident = player_handle_breath_trap(1, GF_ICE, TRAP_OF_ICE_BOLT);
+ break;
+ case TRAP_OF_CHAOS_BOLT:
+ ident = player_handle_breath_trap(1, GF_CHAOS, TRAP_OF_CHAOS_BOLT);
+ break;
+ case TRAP_OF_NETHER_BOLT:
+ ident = player_handle_breath_trap(1, GF_NETHER, TRAP_OF_NETHER_BOLT);
+ break;
+ case TRAP_OF_DISENCHANT_BOLT:
+ ident = player_handle_breath_trap(1, GF_DISENCHANT, TRAP_OF_DISENCHANT_BOLT);
+ break;
+ case TRAP_OF_NEXUS_BOLT:
+ ident = player_handle_breath_trap(1, GF_NEXUS, TRAP_OF_NEXUS_BOLT);
+ break;
+ case TRAP_OF_TIME_BOLT:
+ ident = player_handle_breath_trap(1, GF_TIME, TRAP_OF_TIME_BOLT);
+ break;
+ case TRAP_OF_GRAVITY_BOLT:
+ ident = player_handle_breath_trap(1, GF_GRAVITY, TRAP_OF_GRAVITY_BOLT);
+ break;
+
+ /* Ball Trap */
+ case TRAP_OF_ELEC_BALL:
+ ident = player_handle_breath_trap(3, GF_ELEC, TRAP_OF_ELEC_BALL);
+ break;
+ case TRAP_OF_POIS_BALL:
+ ident = player_handle_breath_trap(3, GF_POIS, TRAP_OF_POIS_BALL);
+ break;
+ case TRAP_OF_ACID_BALL:
+ ident = player_handle_breath_trap(3, GF_ACID, TRAP_OF_ACID_BALL);
+ break;
+ case TRAP_OF_COLD_BALL:
+ ident = player_handle_breath_trap(3, GF_COLD, TRAP_OF_COLD_BALL);
+ break;
+ case TRAP_OF_FIRE_BALL:
+ ident = player_handle_breath_trap(3, GF_FIRE, TRAP_OF_FIRE_BALL);
+ break;
+ case TRAP_OF_PLASMA_BALL:
+ ident = player_handle_breath_trap(3, GF_PLASMA, TRAP_OF_PLASMA_BALL);
+ break;
+ case TRAP_OF_WATER_BALL:
+ ident = player_handle_breath_trap(3, GF_WATER, TRAP_OF_WATER_BALL);
+ break;
+ case TRAP_OF_LITE_BALL:
+ ident = player_handle_breath_trap(3, GF_LITE, TRAP_OF_LITE_BALL);
+ break;
+ case TRAP_OF_DARK_BALL:
+ ident = player_handle_breath_trap(3, GF_DARK, TRAP_OF_DARK_BALL);
+ break;
+ case TRAP_OF_SHARDS_BALL:
+ ident = player_handle_breath_trap(3, GF_SHARDS, TRAP_OF_SHARDS_BALL);
+ break;
+ case TRAP_OF_SOUND_BALL:
+ ident = player_handle_breath_trap(3, GF_SOUND, TRAP_OF_SOUND_BALL);
+ break;
+ case TRAP_OF_CONFUSION_BALL:
+ ident = player_handle_breath_trap(3, GF_CONFUSION, TRAP_OF_CONFUSION_BALL);
+ break;
+ case TRAP_OF_FORCE_BALL:
+ ident = player_handle_breath_trap(3, GF_FORCE, TRAP_OF_FORCE_BALL);
+ break;
+ case TRAP_OF_INERTIA_BALL:
+ ident = player_handle_breath_trap(3, GF_INERTIA, TRAP_OF_INERTIA_BALL);
+ break;
+ case TRAP_OF_MANA_BALL:
+ ident = player_handle_breath_trap(3, GF_MANA, TRAP_OF_MANA_BALL);
+ break;
+ case TRAP_OF_ICE_BALL:
+ ident = player_handle_breath_trap(3, GF_ICE, TRAP_OF_ICE_BALL);
+ break;
+ case TRAP_OF_CHAOS_BALL:
+ ident = player_handle_breath_trap(3, GF_CHAOS, TRAP_OF_CHAOS_BALL);
+ break;
+ case TRAP_OF_NETHER_BALL:
+ ident = player_handle_breath_trap(3, GF_NETHER, TRAP_OF_NETHER_BALL);
+ break;
+ case TRAP_OF_DISENCHANT_BALL:
+ ident = player_handle_breath_trap(3, GF_DISENCHANT, TRAP_OF_DISENCHANT_BALL);
+ break;
+ case TRAP_OF_NEXUS_BALL:
+ ident = player_handle_breath_trap(3, GF_NEXUS, TRAP_OF_NEXUS_BALL);
+ break;
+ case TRAP_OF_TIME_BALL:
+ ident = player_handle_breath_trap(3, GF_TIME, TRAP_OF_TIME_BALL);
+ break;
+ case TRAP_OF_GRAVITY_BALL:
+ ident = player_handle_breath_trap(3, GF_GRAVITY, TRAP_OF_GRAVITY_BALL);
+ break;
+
+ /* -SC- */
+ case TRAP_OF_FEMINITY:
+ {
+ msg_print("Gas sprouts out... you feel yourself transmute.");
+ p_ptr->psex = SEX_FEMALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_MASCULINITY:
+ {
+ msg_print("Gas sprouts out... you feel yourself transmute.");
+ p_ptr->psex = SEX_MALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_NEUTRALITY:
+ {
+ msg_print("Gas sprouts out... you feel yourself transmute.");
+ p_ptr->psex = SEX_NEUTER;
+ sp_ptr = &sex_info[p_ptr->psex];
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_AGING:
+ {
+ msg_print("Colors are scintillating around you. "
+ "You see your past running before your eyes.");
+ p_ptr->age += randint((rp_ptr->b_age + rmp_ptr->b_age) / 2);
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_GROWING:
+ {
+ s16b tmp;
+
+ msg_print("Heavy fumes sprout out... you feel yourself transmute.");
+ if (p_ptr->psex == SEX_FEMALE) tmp = rp_ptr->f_b_ht + rmp_ptr->f_b_ht;
+ else tmp = rp_ptr->m_b_ht + rmp_ptr->m_b_ht;
+
+ p_ptr->ht += randint(tmp / 4);
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_SHRINKING:
+ {
+ s16b tmp;
+
+ msg_print("Heavy fumes sprout out... you feel yourself transmute.");
+ if (p_ptr->psex == SEX_FEMALE) tmp = rp_ptr->f_b_ht + rmp_ptr->f_b_ht;
+ else tmp = rp_ptr->m_b_ht + rmp_ptr->m_b_ht;
+
+ p_ptr->ht -= randint(tmp / 4);
+ if (p_ptr->ht <= tmp / 4) p_ptr->ht = tmp / 4;
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ /* Trap of Divine Anger */
+ case TRAP_OF_DIVINE_ANGER:
+ {
+ if (p_ptr->pgod == 0)
+ {
+ msg_format("Suddenly you feel glad you're a mere %s", spp_ptr->title + c_name);
+ }
+ else
+ {
+ cptr name;
+
+ name = deity_info[p_ptr->pgod].name;
+ msg_format("You feel you have angered %s.", name);
+ inc_piety(p_ptr->pgod, -3000);
+ }
+ break;
+ }
+
+ /* Trap of Divine Wrath */
+ case TRAP_OF_DIVINE_WRATH:
+ {
+ if (p_ptr->pgod == 0)
+ {
+ msg_format("Suddenly you feel glad you're a mere %s", spp_ptr->title + c_name);
+ }
+ else
+ {
+ cptr name;
+
+ name = deity_info[p_ptr->pgod].name;
+
+ msg_format("%s quakes in rage: ``Thou art supremely insolent, mortal!!''", name);
+ inc_piety(p_ptr->pgod, -500 * p_ptr->lev);
+ }
+ break;
+ }
+
+ /* Trap of hallucination */
+ case TRAP_OF_HALLUCINATION:
+ {
+ msg_print("Scintillating colors hypnotise you for a moment.");
+
+ set_image(80);
+ }
+ break;
+
+ /* Bolt Trap */
+ case TRAP_OF_ROCKET:
+ ident = player_handle_breath_trap(1, GF_ROCKET, trap);
+ break;
+ case TRAP_OF_NUKE_BOLT:
+ ident = player_handle_breath_trap(1, GF_NUKE, trap);
+ break;
+ case TRAP_OF_HOLY_FIRE:
+ ident = player_handle_breath_trap(1, GF_HOLY_FIRE, trap);
+ break;
+ case TRAP_OF_HELL_FIRE:
+ ident = player_handle_breath_trap(1, GF_HELL_FIRE, trap);
+ break;
+ case TRAP_OF_PSI_BOLT:
+ ident = player_handle_breath_trap(1, GF_PSI, trap);
+ break;
+ case TRAP_OF_PSI_DRAIN:
+ ident = player_handle_breath_trap(1, GF_PSI_DRAIN, trap);
+ break;
+
+ /* Ball Trap */
+ case TRAP_OF_NUKE_BALL:
+ ident = player_handle_breath_trap(3, GF_NUKE, TRAP_OF_NUKE_BALL);
+ break;
+ case TRAP_OF_PSI_BALL:
+ ident = player_handle_breath_trap(3, GF_PSI, TRAP_OF_NUKE_BALL);
+ break;
+
+ default:
+ {
+ msg_print(format("Executing unknown trap %d", trap));
+ }
+ }
+ return ident;
+}
+
+void player_activate_door_trap(s16b y, s16b x)
+{
+ cave_type *c_ptr;
+ bool_ ident = FALSE;
+
+ c_ptr = &cave[y][x];
+
+ /* Return if trap or door not found */
+ if ((c_ptr->t_idx == 0) ||
+ !(f_info[c_ptr->feat].flags1 & FF1_DOOR)) return;
+
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Message */
+ msg_print("You found a trap!");
+
+ /* Pick a trap */
+ pick_trap(y, x);
+
+ /* Hit the trap */
+ ident = player_activate_trap_type(y, x, NULL, -1);
+ if (ident)
+ {
+ t_info[c_ptr->t_idx].ident = TRUE;
+ msg_format("You identified that trap as %s.",
+ t_name + t_info[c_ptr->t_idx].name);
+ }
+}
+
+
+/*
+ * Places a random trap at the given location.
+ *
+ * The location must be a valid, empty, clean, floor grid.
+ */
+void place_trap(int y, int x)
+{
+ s16b trap;
+ trap_type *t_ptr;
+ int cnt;
+ u32b flags;
+ cave_type *c_ptr = &cave[y][x];
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ /* No traps in town or on first level */
+ if (dun_level <= 1) return;
+
+ /*
+ * Avoid open doors -- because DOOR flag is added to make much more
+ * important processing faster
+ */
+ if (c_ptr->feat == FEAT_OPEN) return;
+ if (c_ptr->feat == FEAT_BROKEN) return;
+
+ /* Traps only appears on empty floor */
+ if (!cave_floor_grid(c_ptr) &&
+ !(f_info[c_ptr->feat].flags1 & (FF1_DOOR))) return;
+
+ /* Set flags */
+ if (f_info[c_ptr->feat].flags1 & FF1_DOOR) flags = FTRAP_DOOR;
+ else flags = FTRAP_FLOOR;
+
+ /* Try 100 times */
+ cnt = 100;
+ while (cnt--)
+ {
+ trap = randint(max_t_idx - 1);
+ t_ptr = &t_info[trap];
+
+ /* No traps below their minlevel */
+ if (t_ptr->minlevel > dun_level) continue;
+
+ /* is this a correct trap now? */
+ if (!(t_ptr->flags & flags)) continue;
+
+ /*
+ * Hack -- No trap door at the bottom of dungeon or in flat
+ * (non dungeon) places or on quest levels
+ */
+ if ((trap == TRAP_OF_SINKING) &&
+ ((d_ptr->maxdepth == dun_level) ||
+ (dungeon_flags1 & DF1_FLAT) || (is_quest(dun_level))) )
+ {
+ continue;
+ }
+
+ /* How probable is this trap */
+ if (rand_int(100) < t_ptr->probability)
+ {
+ c_ptr->t_idx = trap;
+ break;
+ }
+ }
+
+ return;
+}
+
+
+/*
+ * Places a random trap on the given chest.
+ *
+ * The object must be a valid chest.
+ */
+void place_trap_object(object_type *o_ptr)
+{
+ s16b trap;
+ trap_type *t_ptr;
+ int cnt;
+
+ /* No traps in town or on first level */
+ if (dun_level <= 1)
+ {
+ /* empty chest were already looted, therefore known */
+ o_ptr->ident |= IDENT_KNOWN;
+ return;
+ }
+
+ /* Try 100 times */
+ cnt = 100;
+ while (cnt--)
+ {
+ trap = randint(max_t_idx - 1);
+ t_ptr = &t_info[trap];
+
+ /* no traps below their minlevel */
+ if (t_ptr->minlevel > dun_level) continue;
+
+ /* Is this a correct trap now? */
+ if (!(t_ptr->flags & FTRAP_CHEST)) continue;
+
+ /* How probable is this trap */
+ if (rand_int(100) < t_ptr->probability)
+ {
+ o_ptr->pval = trap;
+ break;
+ }
+ }
+
+ return;
+}
+
+/* Dangerous trap placing function */
+void wiz_place_trap(int y, int x, int idx)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Dangerous enough as it is... */
+ if (!cave_floor_grid(c_ptr) && (!(f_info[c_ptr->feat].flags1 & FF1_DOOR))) return;
+
+ c_ptr->t_idx = idx;
+}
+
+/*
+ * Here begin monster traps code
+ */
+
+/*
+ * Hook to determine if an object is a device
+ */
+static bool_ item_tester_hook_device(object_type *o_ptr)
+{
+ if (((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->pval != 0)) ||
+ (o_ptr->tval == TV_STAFF) ||
+ (o_ptr->tval == TV_WAND)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+/*
+ * Hook to determine if an object is a potion
+ */
+static bool_ item_tester_hook_potion(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_POTION) ||
+ (o_ptr->tval == TV_POTION2)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+/*
+ * The trap setting code for rogues -MWK-
+ *
+ * Also, it will fail or give weird results if the tvals are resorted!
+ */
+void do_cmd_set_trap(void)
+{
+ int item_kit, item_load, i;
+ int num;
+
+ object_type *o_ptr, *j_ptr, *i_ptr;
+
+ cptr q, s, c;
+
+ object_type object_type_body;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Check some conditions */
+ if (p_ptr->blind)
+ {
+ msg_print("You can't see anything.");
+ return;
+ }
+ if (no_lite())
+ {
+ msg_print("You don't dare to set a trap in the darkness.");
+ return;
+ }
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Only set traps on clean floor grids */
+ if (!cave_clean_bold(p_ptr->py, p_ptr->px))
+ {
+ msg_print("You cannot set a trap on this.");
+ return;
+ }
+
+ /* Restrict choices to trapkits */
+ item_tester_tval = TV_TRAPKIT;
+
+ /* Get an item */
+ q = "Use which trapping kit? ";
+ s = "You have no trapping kits.";
+ if (!get_item(&item_kit, q, s, USE_INVEN)) return;
+
+ o_ptr = &p_ptr->inventory[item_kit];
+
+ /* Trap kits need a second object */
+ switch (o_ptr->sval)
+ {
+ case SV_TRAPKIT_BOW:
+ item_tester_tval = TV_ARROW;
+ break;
+ case SV_TRAPKIT_XBOW:
+ item_tester_tval = TV_BOLT;
+ break;
+ case SV_TRAPKIT_SLING:
+ item_tester_tval = TV_SHOT;
+ break;
+ case SV_TRAPKIT_POTION:
+ item_tester_hook = item_tester_hook_potion;
+ break;
+ case SV_TRAPKIT_SCROLL:
+ item_tester_tval = TV_SCROLL;
+ break;
+ case SV_TRAPKIT_DEVICE:
+ item_tester_hook = item_tester_hook_device;
+ break;
+ default:
+ msg_print("Unknown trapping kit type!");
+ break;
+ }
+
+ /* Get the second item */
+ q = "Load with what? ";
+ s = "You have nothing to load that trap with.";
+ if (!get_item(&item_load, q, s, USE_INVEN)) return;
+
+ /* Get the second object */
+ j_ptr = &p_ptr->inventory[item_load];
+
+ /* Assume a single object */
+ num = 1;
+
+ /* In some cases, take multiple objects to load */
+ if (o_ptr->sval != SV_TRAPKIT_DEVICE)
+ {
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f3 & TR3_XTRA_SHOTS) && (o_ptr->pval > 0)) num += o_ptr->pval;
+
+ if (f2 & (TRAP2_AUTOMATIC_5 | TRAP2_AUTOMATIC_99)) num = 99;
+
+ if (num > j_ptr->number) num = j_ptr->number;
+
+ c = format("How many (1-%d)? ", num);
+
+ /* Ask for number of items to use */
+ num = get_quantity(c, num);
+ }
+
+ /* Canceled */
+ if (!num) return;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Obtain local object for trap content */
+ object_copy(i_ptr, j_ptr);
+
+ /* Set number */
+ i_ptr->number = num;
+
+ /* Drop it here */
+ cave[p_ptr->py][p_ptr->px].special = floor_carry(p_ptr->py, p_ptr->px, i_ptr);
+
+ /* Obtain local object for trap trigger kit */
+ object_copy(i_ptr, o_ptr);
+
+ /* Set number */
+ i_ptr->number = 1;
+
+ /* Drop it here */
+ cave[p_ptr->py][p_ptr->px].special2 = floor_carry(p_ptr->py, p_ptr->px, i_ptr);
+
+ /* Modify, Describe, Optimize */
+ inc_stack_size_ex(item_kit, -1, NO_OPTIMIZE, DESCRIBE);
+ inc_stack_size_ex(item_load, -num, NO_OPTIMIZE, DESCRIBE);
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ if (inven_item_optimize(i)) break;
+ }
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ inven_item_optimize(i);
+ }
+
+ /* Actually set the trap */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MON_TRAP);
+}
+
+/*
+ * Monster hitting a rod trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_rod(int m_idx, object_type *o_ptr)
+{
+ int dam = 0, typ = 0;
+ int rad = 0;
+ monster_type *m_ptr = &m_list[m_idx];
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+
+ /* Depend on rod type */
+ switch (o_ptr->pval)
+ {
+ case SV_ROD_DETECT_TRAP:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ break;
+ case SV_ROD_DETECTION:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ break;
+ case SV_ROD_ILLUMINATION:
+ typ = GF_LITE_WEAK;
+ dam = damroll(2, 15);
+ rad = 3;
+ lite_room(y, x);
+ break;
+ case SV_ROD_CURING:
+ typ = GF_OLD_HEAL;
+ dam = damroll(3, 4); /* and heal conf? */
+ break;
+ case SV_ROD_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = 300;
+ break;
+ case SV_ROD_SPEED:
+ typ = GF_OLD_SPEED;
+ dam = 50;
+ break;
+ case SV_ROD_TELEPORT_AWAY:
+ typ = GF_AWAY_ALL;
+ dam = MAX_SIGHT * 5;
+ break;
+ case SV_ROD_DISARMING:
+ break;
+ case SV_ROD_LITE:
+ typ = GF_LITE_WEAK;
+ dam = damroll(6, 8);
+ break;
+ case SV_ROD_SLEEP_MONSTER:
+ typ = GF_OLD_SLEEP;
+ dam = 50;
+ break;
+ case SV_ROD_SLOW_MONSTER:
+ typ = GF_OLD_SLOW;
+ dam = 50;
+ break;
+ case SV_ROD_DRAIN_LIFE:
+ typ = GF_OLD_DRAIN;
+ dam = 75;
+ break;
+ case SV_ROD_POLYMORPH:
+ typ = GF_OLD_POLY;
+ dam = 50;
+ break;
+ case SV_ROD_ACID_BOLT:
+ typ = GF_ACID;
+ dam = damroll(6, 8);
+ break;
+ case SV_ROD_ELEC_BOLT:
+ typ = GF_ELEC;
+ dam = damroll(3, 8);
+ break;
+ case SV_ROD_FIRE_BOLT:
+ typ = GF_FIRE;
+ dam = damroll(8, 8);
+ break;
+ case SV_ROD_COLD_BOLT:
+ typ = GF_COLD;
+ dam = damroll(5, 8);
+ break;
+ case SV_ROD_ACID_BALL:
+ typ = GF_ACID;
+ dam = 60;
+ rad = 2;
+ break;
+ case SV_ROD_ELEC_BALL:
+ typ = GF_ELEC;
+ dam = 32;
+ rad = 2;
+ break;
+ case SV_ROD_FIRE_BALL:
+ typ = GF_FIRE;
+ dam = 72;
+ rad = 2;
+ break;
+ case SV_ROD_COLD_BALL:
+ typ = GF_COLD;
+ dam = 48;
+ rad = 2;
+ break;
+ default:
+ return (FALSE);
+ }
+
+ /* Actually hit the monster */
+ if (typ) (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a staff trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_staff(int m_idx, object_type *o_ptr)
+{
+ return (FALSE);
+}
+
+/*
+ * Monster hitting a scroll trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_scroll(int m_idx, int sval)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int dam = 0, typ = 0;
+ int rad = 0;
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+ int k;
+
+ /* Depend on scroll type */
+ switch (sval)
+ {
+ case SV_SCROLL_CURSE_ARMOR:
+ case SV_SCROLL_CURSE_WEAPON:
+ case SV_SCROLL_TRAP_CREATION: /* these don't work :-( */
+ case SV_SCROLL_WORD_OF_RECALL: /* should these? */
+ case SV_SCROLL_IDENTIFY:
+ case SV_SCROLL_STAR_IDENTIFY:
+ case SV_SCROLL_MAPPING:
+ case SV_SCROLL_DETECT_GOLD:
+ case SV_SCROLL_DETECT_ITEM:
+ case SV_SCROLL_REMOVE_CURSE:
+ case SV_SCROLL_STAR_REMOVE_CURSE:
+ case SV_SCROLL_ENCHANT_ARMOR:
+ case SV_SCROLL_ENCHANT_WEAPON_TO_HIT:
+ case SV_SCROLL_ENCHANT_WEAPON_TO_DAM:
+ case SV_SCROLL_STAR_ENCHANT_ARMOR:
+ case SV_SCROLL_STAR_ENCHANT_WEAPON:
+ case SV_SCROLL_RECHARGING:
+ case SV_SCROLL_DETECT_DOOR:
+ case SV_SCROLL_DETECT_INVIS:
+ case SV_SCROLL_SATISFY_HUNGER:
+ case SV_SCROLL_RUNE_OF_PROTECTION:
+ case SV_SCROLL_TRAP_DOOR_DESTRUCTION:
+ case SV_SCROLL_PROTECTION_FROM_EVIL:
+ return (FALSE);
+ case SV_SCROLL_DARKNESS:
+ unlite_room(y, x);
+ typ = GF_DARK_WEAK;
+ dam = 10;
+ rad = 3;
+ break;
+ case SV_SCROLL_AGGRAVATE_MONSTER:
+ aggravate_monsters(m_idx);
+ return (FALSE);
+ case SV_SCROLL_SUMMON_MONSTER:
+ for (k = 0; k < randint(3) ; k++) summon_specific(y, x, dun_level, 0);
+ return (FALSE);
+ case SV_SCROLL_SUMMON_UNDEAD:
+ for (k = 0; k < randint(3) ; k++) summon_specific(y, x, dun_level, SUMMON_UNDEAD);
+ return (FALSE);
+ case SV_SCROLL_PHASE_DOOR:
+ typ = GF_AWAY_ALL;
+ dam = 10;
+ break;
+ case SV_SCROLL_TELEPORT:
+ typ = GF_AWAY_ALL;
+ dam = 100;
+ break;
+ case SV_SCROLL_TELEPORT_LEVEL:
+ delete_monster(y, x);
+ return (TRUE);
+ case SV_SCROLL_LIGHT:
+ lite_room(y, x);
+ typ = GF_LITE_WEAK;
+ dam = damroll(2, 8);
+ rad = 2;
+ break;
+ case SV_SCROLL_DETECT_TRAP:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ return (FALSE);
+ case SV_SCROLL_BLESSING:
+ typ = GF_HOLY_FIRE;
+ dam = damroll(1, 4);
+ break;
+ case SV_SCROLL_HOLY_CHANT:
+ typ = GF_HOLY_FIRE;
+ dam = damroll(2, 4);
+ break;
+ case SV_SCROLL_HOLY_PRAYER:
+ typ = GF_HOLY_FIRE;
+ dam = damroll(4, 4);
+ break;
+ case SV_SCROLL_MONSTER_CONFUSION:
+ typ = GF_OLD_CONF;
+ dam = damroll(5, 10);
+ break;
+ case SV_SCROLL_STAR_DESTRUCTION:
+ destroy_area(y, x, 15, TRUE, FALSE);
+ return (FALSE);
+ case SV_SCROLL_DISPEL_UNDEAD:
+ typ = GF_DISP_UNDEAD;
+ rad = 5;
+ dam = 60;
+ break;
+ case SV_SCROLL_GENOCIDE:
+ {
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ genocide_aux(FALSE, r_ptr->d_char);
+ /* although there's no point in a multiple genocide trap... */
+ return (!(r_ptr->flags1 & RF1_UNIQUE));
+ }
+ case SV_SCROLL_MASS_GENOCIDE:
+ for (k = 0; k < 8; k++)
+ delete_monster(y + ddy[k], x + ddx[k]);
+ delete_monster(y, x);
+ return (TRUE);
+ case SV_SCROLL_ACQUIREMENT:
+ acquirement(y, x, 1, TRUE, FALSE);
+ return (FALSE);
+ case SV_SCROLL_STAR_ACQUIREMENT:
+ acquirement(y, x, randint(2) + 1, TRUE, FALSE);
+ return (FALSE);
+ default:
+ return (FALSE);
+ }
+
+ /* Actually hit the monster */
+ (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a wand trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_wand(int m_idx, object_type *o_ptr)
+{
+ return (FALSE);
+}
+
+/*
+ * Monster hitting a potions trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_potion(int m_idx, object_type *o_ptr)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int dam = 0, typ = 0;
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+ int sval = o_ptr->sval;
+
+ /* Depend on potion type */
+ if (o_ptr->tval == TV_POTION)
+ {
+ switch (sval)
+ {
+ /* Nothing happens */
+ case SV_POTION_WATER:
+ case SV_POTION_APPLE_JUICE:
+ case SV_POTION_SLIME_MOLD:
+ case SV_POTION_SALT_WATER:
+ case SV_POTION_DEC_STR:
+ case SV_POTION_DEC_INT:
+ case SV_POTION_DEC_WIS:
+ case SV_POTION_DEC_DEX:
+ case SV_POTION_DEC_CON:
+ case SV_POTION_DEC_CHR:
+ case SV_POTION_INFRAVISION:
+ case SV_POTION_DETECT_INVIS:
+ case SV_POTION_SLOW_POISON:
+ case SV_POTION_CURE_POISON:
+ case SV_POTION_RESIST_HEAT:
+ case SV_POTION_RESIST_COLD:
+ case SV_POTION_RESTORE_MANA:
+ case SV_POTION_RESTORE_EXP:
+ case SV_POTION_RES_STR:
+ case SV_POTION_RES_INT:
+ case SV_POTION_RES_WIS:
+ case SV_POTION_RES_DEX:
+ case SV_POTION_RES_CON:
+ case SV_POTION_RES_CHR:
+ case SV_POTION_INC_STR:
+ case SV_POTION_INC_INT:
+ case SV_POTION_INC_WIS:
+ case SV_POTION_INC_DEX:
+ case SV_POTION_INC_CON:
+ case SV_POTION_INC_CHR:
+ case SV_POTION_AUGMENTATION:
+ case SV_POTION_RUINATION: /* ??? */
+ case SV_POTION_ENLIGHTENMENT:
+ case SV_POTION_STAR_ENLIGHTENMENT:
+ case SV_POTION_SELF_KNOWLEDGE:
+ return (FALSE);
+
+ case SV_POTION_EXPERIENCE:
+ if (m_ptr->level < MONSTER_LEVEL_MAX)
+ {
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + 1);
+ monster_check_experience(m_idx, FALSE);
+ }
+ return (FALSE);
+ case SV_POTION_SLOWNESS:
+ typ = GF_OLD_SLOW;
+ dam = damroll(4, 6);
+ break;
+ case SV_POTION_POISON:
+ typ = GF_POIS;
+ dam = damroll(8, 6);
+ break;
+ case SV_POTION_CONFUSION:
+ typ = GF_CONFUSION;
+ dam = damroll(4, 6);
+ break;
+ case SV_POTION_BLINDNESS:
+ typ = GF_DARK;
+ dam = 10;
+ break;
+ case SV_POTION_SLEEP:
+ typ = GF_OLD_SLEEP;
+ dam = damroll (4, 6);
+ break;
+ case SV_POTION_LOSE_MEMORIES:
+ typ = GF_OLD_CONF;
+ dam = damroll(10, 10);
+ break;
+ case SV_POTION_DETONATIONS:
+ typ = GF_DISINTEGRATE;
+ dam = damroll(20, 20);
+ break;
+ case SV_POTION_DEATH:
+ typ = GF_NETHER;
+ dam = damroll(100, 20);
+ break;
+ case SV_POTION_BOLDNESS:
+ m_ptr->monfear = 0;
+ return (FALSE);
+ case SV_POTION_SPEED:
+ typ = GF_OLD_SPEED;
+ dam = damroll(5, 10);
+ break;
+ case SV_POTION_HEROISM:
+ case SV_POTION_BESERK_STRENGTH:
+ m_ptr->monfear = 0;
+ typ = GF_OLD_HEAL;
+ dam = damroll(2, 10);
+ break;
+ case SV_POTION_CURE_LIGHT:
+ typ = GF_OLD_HEAL;
+ dam = damroll(3, 4);
+ break;
+ case SV_POTION_CURE_SERIOUS:
+ typ = GF_OLD_HEAL;
+ dam = damroll(4, 6);
+ break;
+ case SV_POTION_CURE_CRITICAL:
+ typ = GF_OLD_HEAL;
+ dam = damroll(6, 8);
+ break;
+ case SV_POTION_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = 300;
+ break;
+ case SV_POTION_STAR_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = 1000;
+ break;
+ case SV_POTION_LIFE:
+ {
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ if (r_ptr->flags3 & RF3_UNDEAD)
+ {
+ typ = GF_HOLY_FIRE;
+ dam = damroll(20, 20);
+ }
+ else
+ {
+ typ = GF_OLD_HEAL;
+ dam = 5000;
+ }
+ break;
+ }
+ default:
+ return (FALSE);
+
+ }
+ }
+ else
+ {}
+
+ /* Actually hit the monster */
+ (void) project_m( -2, 0, y, x, dam, typ);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a monster trap -MWK-
+ * Returns True if the monster died, false otherwise
+ */
+bool_ mon_hit_trap(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ object_type *kit_o_ptr, *load_o_ptr, *j_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_type object_type_body;
+
+ int mx = m_ptr->fx;
+ int my = m_ptr->fy;
+
+ int difficulty;
+ int smartness;
+
+ char m_name[80];
+
+ bool_ notice = FALSE;
+ bool_ disarm = FALSE;
+ bool_ remove = FALSE;
+ bool_ dead = FALSE;
+ bool_ fear = FALSE;
+ s32b special = 0;
+
+ int dam, chance, shots;
+ int mul = 0;
+ int breakage = -1;
+
+ int cost = 0;
+
+ /* Get the trap objects */
+ kit_o_ptr = &o_list[cave[my][mx].special2];
+ load_o_ptr = &o_list[cave[my][mx].special];
+ j_ptr = &object_type_body;
+
+ /* Get trap properties */
+ object_flags(kit_o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Can set off check */
+ /* Ghosts only set off Ghost traps */
+ if ((r_ptr->flags2 & RF2_PASS_WALL) && !(f2 & TRAP2_KILL_GHOST)) return (FALSE);
+
+ /* Some traps are specialized to some creatures */
+ if (f2 & TRAP2_ONLY_MASK)
+ {
+ bool_ affect = FALSE;
+ if ((f2 & TRAP2_ONLY_DRAGON) && (r_ptr->flags3 & RF3_DRAGON)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_DEMON) && (r_ptr->flags3 & RF3_DEMON)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_UNDEAD) && (r_ptr->flags3 & RF3_UNDEAD)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_EVIL) && (r_ptr->flags3 & RF3_EVIL)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_ANIMAL) && (r_ptr->flags3 & RF3_ANIMAL)) affect = TRUE;
+
+ /* Don't set it off if forbidden */
+ if (!affect) return (FALSE);
+ }
+
+ /* Get detection difficulty */
+ difficulty = 25;
+
+ /* Some traps are well-hidden */
+ if (f1 & TR1_STEALTH)
+ {
+ difficulty += 10 * (kit_o_ptr->pval);
+ }
+
+ /* Get monster smartness for trap detection */
+ /* Higher level monsters are smarter */
+ smartness = r_ptr->level;
+
+ /* Smart monsters are better at detecting traps */
+ if (r_ptr->flags2 & RF2_SMART) smartness += 10;
+
+ /* Some monsters have already noticed one of out traps */
+ if (m_ptr->smart & SM_NOTE_TRAP) smartness += 20;
+
+ /* Stupid monsters are no good at detecting traps */
+ if (r_ptr->flags2 & (RF2_STUPID | RF2_EMPTY_MIND)) smartness = -150;
+
+ /* Check if the monster notices the trap */
+ if (randint(300) > (difficulty - smartness + 150)) notice = TRUE;
+
+ /* Disarm check */
+ if (notice)
+ {
+ /* The next traps will be easier to spot! */
+ m_ptr->smart |= SM_NOTE_TRAP;
+
+ /* Get trap disarming difficulty */
+ difficulty = (kit_o_ptr->ac + kit_o_ptr->to_a);
+
+ /* Get monster disarming ability */
+ /* Higher level monsters are better */
+ smartness = r_ptr->level / 5;
+
+ /* Smart monsters are better at disarming */
+ if (r_ptr->flags2 & RF2_SMART) smartness *= 2;
+
+ /* Stupid monsters never disarm traps */
+ if (r_ptr->flags2 & RF2_STUPID) smartness = -150;
+
+ /* Nonsmart animals never disarm traps */
+ if ((r_ptr->flags3 & RF3_ANIMAL) && !(r_ptr->flags2 & RF2_SMART)) smartness = -150;
+
+ /* Check if the monster disarms the trap */
+ if (randint(120) > (difficulty - smartness + 80)) disarm = TRUE;
+ }
+
+ /* If disarmed, remove the trap and print a message */
+ if (disarm)
+ {
+ remove = TRUE;
+
+ if (m_ptr->ml)
+ {
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s disarms a trap!", m_name);
+ }
+ }
+
+ /* Otherwise, activate the trap! */
+ else
+ {
+ /* Message for visible monster */
+ if (m_ptr->ml)
+ {
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s sets off a trap!", m_name);
+ }
+ else
+ {
+ /* No message if monster isn't visible ? */
+ }
+
+ /* Next time be more careful */
+ if (randint(100) < 80) m_ptr->smart |= SM_NOTE_TRAP;
+
+ /* Actually activate the trap */
+ switch (kit_o_ptr->sval)
+ {
+ case SV_TRAPKIT_BOW:
+ case SV_TRAPKIT_XBOW:
+ case SV_TRAPKIT_SLING:
+ {
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+ if (shots > load_o_ptr->number) shots = load_o_ptr->number;
+
+ while (shots-- && !dead)
+ {
+ /* Total base damage */
+ dam = damroll(load_o_ptr->dd, load_o_ptr->ds) + load_o_ptr->to_d + kit_o_ptr->to_d;
+
+ /* Total hit probability */
+ chance = (kit_o_ptr->to_h + load_o_ptr->to_h + 20) * BTH_PLUS_ADJ;
+
+ /* Damage multiplier */
+ if (kit_o_ptr->sval == SV_TRAPKIT_BOW) mul = 3;
+ if (kit_o_ptr->sval == SV_TRAPKIT_XBOW) mul = 4;
+ if (kit_o_ptr->sval == SV_TRAPKIT_SLING) mul = 2;
+ if (f3 & TR3_XTRA_MIGHT) mul += kit_o_ptr->pval;
+ if (mul < 0) mul = 0;
+
+ /* Multiply damage */
+ dam *= mul;
+
+ /* Check if we hit the monster */
+ if (test_hit_fire(chance, r_ptr->ac, TRUE))
+ {
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ /* describe the monster (again, just in case :-) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s is hit by a missile.", m_name);
+ }
+
+ /* Apply slays, brand, critical hits */
+ dam = tot_dam_aux(load_o_ptr, dam, m_ptr, &special);
+ dam = critical_shot(load_o_ptr->weight, load_o_ptr->to_h, dam, SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (dam < 0) dam = 0;
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(m_idx, dam, &fear, note_dies))
+ {
+ /* Dead monster */
+ dead = TRUE;
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(m_idx, dam);
+
+ if (special) attack_special(m_ptr, special, dam);
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+
+ }
+
+ /* Exploding ammo */
+ if (load_o_ptr->pval2 != 0)
+ {
+ int rad = 0;
+ int dam = (damroll(load_o_ptr->dd, load_o_ptr->ds) + load_o_ptr->to_d)*2;
+ int flag = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL |
+ PROJECT_JUMP;
+
+ switch (load_o_ptr->sval)
+ {
+ case SV_AMMO_LIGHT:
+ rad = 2;
+ dam /= 2;
+ break;
+ case SV_AMMO_NORMAL:
+ rad = 3;
+ break;
+ case SV_AMMO_HEAVY:
+ rad = 4;
+ dam *= 2;
+ break;
+ }
+
+ project(0, rad, my, mx, dam, load_o_ptr->pval2, flag);
+
+ breakage = 100;
+ }
+ else
+ {
+ breakage = breakage_chance(load_o_ptr);
+ }
+
+ /* Copy and decrease ammo */
+ object_copy(j_ptr, load_o_ptr);
+
+ j_ptr->number = 1;
+
+ load_o_ptr->number--;
+
+ if (load_o_ptr->number <= 0)
+ {
+ remove = TRUE;
+ delete_object_idx(kit_o_ptr->next_o_idx);
+ kit_o_ptr->next_o_idx = 0;
+ }
+
+ /* Drop (or break) near that location */
+ drop_near(j_ptr, breakage, my, mx);
+
+ }
+
+ break;
+ }
+
+ case SV_TRAPKIT_POTION:
+ {
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+ if (shots > load_o_ptr->number) shots = load_o_ptr->number;
+
+ while (shots-- && !dead)
+ {
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ /* describe the monster (again, just in case :-) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s is hit by fumes.", m_name);
+ }
+
+ /* Get the potion effect */
+ dead = mon_hit_trap_aux_potion(m_idx, load_o_ptr);
+
+ /* Copy and decrease ammo */
+ object_copy(j_ptr, load_o_ptr);
+
+ j_ptr->number = 1;
+
+ load_o_ptr->number--;
+
+ if (load_o_ptr->number <= 0)
+ {
+ remove = TRUE;
+ delete_object_idx(kit_o_ptr->next_o_idx);
+ kit_o_ptr->next_o_idx = 0;
+ }
+ }
+
+ break;
+ }
+
+ case SV_TRAPKIT_SCROLL:
+ {
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+ if (shots > load_o_ptr->number) shots = load_o_ptr->number;
+
+ while (shots-- && !dead)
+ {
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ /* describe the monster (again, just in case :-) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s activates a spell!", m_name);
+ }
+
+ /* Get the potion effect */
+ dead = mon_hit_trap_aux_scroll(m_idx, load_o_ptr->sval);
+
+ /* Copy and decrease ammo */
+ object_copy(j_ptr, load_o_ptr);
+
+ j_ptr->number = 1;
+
+ load_o_ptr->number--;
+
+ if (load_o_ptr->number <= 0)
+ {
+ remove = TRUE;
+ delete_object_idx(kit_o_ptr->next_o_idx);
+ kit_o_ptr->next_o_idx = 0;
+ }
+ }
+
+ break;
+ }
+
+ case SV_TRAPKIT_DEVICE:
+ {
+ if (load_o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* Extract mana cost of the rod tip */
+ u32b tf1, tf2, tf3, tf4, tf5, tesp;
+ object_kind *tip_o_ptr = &k_info[lookup_kind(TV_ROD, load_o_ptr->pval)];
+ object_flags(load_o_ptr, &tf1, &tf2, &tf3, &tf4, &tf5, &tesp);
+ cost = (tf4 & TR4_CHEAPNESS) ? tip_o_ptr->pval / 2 : tip_o_ptr->pval;
+ if (cost <= 0) cost = 1;
+ }
+
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+
+ if (load_o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (shots > load_o_ptr->timeout / cost) shots = load_o_ptr->timeout / cost;
+ }
+ else
+ {
+ if (shots > load_o_ptr->pval) shots = load_o_ptr->pval;
+ }
+
+ while (shots-- && !dead)
+ {
+ /* Get the effect effect */
+ switch (load_o_ptr->tval)
+ {
+ case TV_ROD_MAIN:
+ dead = mon_hit_trap_aux_rod(m_idx, load_o_ptr);
+ break;
+ case TV_WAND:
+ dead = mon_hit_trap_aux_wand(m_idx, load_o_ptr);
+ break;
+ case TV_STAFF:
+ dead = mon_hit_trap_aux_staff(m_idx, load_o_ptr);
+ break;
+ }
+
+ if (load_o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* decrease stored mana (timeout) for rods */
+ load_o_ptr->timeout -= cost;
+ }
+ else
+ {
+ /* decrease charges for wands and staves */
+ load_o_ptr->pval--;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ msg_print("oops! nonexistant trap!");
+
+ }
+
+ /* Non-automatic traps are removed */
+ if (!(f2 & (TRAP2_AUTOMATIC_5 | TRAP2_AUTOMATIC_99)))
+ {
+ remove = TRUE;
+ }
+ else if (f2 & TRAP2_AUTOMATIC_5) remove = (randint(5) == 1);
+
+ }
+
+ /* Special trap effect -- teleport to */
+ if ((f2 & TRAP2_TELEPORT_TO) && (!disarm) && (!dead))
+ {
+ teleport_monster_to(m_idx, p_ptr->py, p_ptr->px);
+ }
+
+ /* Remove the trap if inactive now */
+ if (remove) place_floor_convert_glass(my, mx);
+
+ /* did it die? */
+ return (dead);
+}
+
diff --git a/src/types.h b/src/types.h
new file mode 100644
index 00000000..49acb383
--- /dev/null
+++ b/src/types.h
@@ -0,0 +1,2522 @@
+/* File: types.h */
+
+/* Purpose: global type declarations */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+/*
+ * This file should ONLY be included by "angband.h"
+ */
+
+
+/*
+ * Note that "char" may or may not be signed, and that "signed char"
+ * may or may not work on all machines. So always use "s16b" or "s32b"
+ * for signed values. Also, note that unsigned values cause math problems
+ * in many cases, so try to only use "u16b" and "u32b" for "bit flags",
+ * unless you really need the extra bit of information, or you really
+ * need to restrict yourself to a single byte for storage reasons.
+ *
+ * Also, if possible, attempt to restrict yourself to sub-fields of
+ * known size (use "s16b" or "s32b" instead of "int", and "byte" instead
+ * of "bool"), and attempt to align all fields along four-byte words, to
+ * optimize storage issues on 32-bit machines. Also, avoid "bit flags"
+ * since these increase the code size and slow down execution. When
+ * you need to store bit flags, use one byte per flag, or, where space
+ * is an issue, use a "byte" or "u16b" or "u32b", and add special code
+ * to access the various bit flags.
+ *
+ * Many of these structures were developed to reduce the number of global
+ * variables, facilitate structured program design, allow the use of ascii
+ * template files, simplify access to indexed data, or facilitate efficient
+ * clearing of many variables at once.
+ *
+ * Certain data is saved in multiple places for efficient access, currently,
+ * this includes the tval/sval/weight fields in "object_type", various fields
+ * in "header_type", and the "m_idx" and "o_idx" fields in "cave_type". All
+ * of these could be removed, but this would, in general, slow down the game
+ * and increase the complexity of the code.
+ */
+
+
+
+
+
+/*
+ * Template file header information (see "init.c"). 16 bytes.
+ *
+ * Note that the sizes of many of the "arrays" are between 32768 and
+ * 65535, and so we must use "unsigned" values to hold the "sizes" of
+ * these arrays below. Normally, I try to avoid using unsigned values,
+ * since they can cause all sorts of bizarre problems, but I have no
+ * choice here, at least, until the "race" array is split into "normal"
+ * and "unique" monsters, which may or may not actually help.
+ *
+ * Note that, on some machines, for example, the Macintosh, the standard
+ * "read()" and "write()" functions cannot handle more than 32767 bytes
+ * at one time, so we need replacement functions, see "util.c" for details.
+ *
+ * Note that, on some machines, for example, the Macintosh, the standard
+ * "malloc()" function cannot handle more than 32767 bytes at one time,
+ * but we may assume that the "ralloc()" function can handle up to 65535
+ * butes at one time. We should not, however, assume that the "ralloc()"
+ * function can handle more than 65536 bytes at a time, since this might
+ * result in segmentation problems on certain older machines, and in fact,
+ * we should not assume that it can handle exactly 65536 bytes at a time,
+ * since the internal functions may use an unsigned short to specify size.
+ *
+ * In general, these problems occur only on machines (such as most personal
+ * computers) which use 2 byte "int" values, and which use "int" for the
+ * arguments to the relevent functions.
+ */
+
+typedef struct header header;
+
+struct header
+{
+ u16b info_num; /* Number of "info" records */
+
+ u32b name_size; /* Size of the "name" array in bytes */
+
+ u32b text_size; /* Size of the "text" array in bytes */
+};
+
+
+/*
+ * "Themed" objects.
+ * Probability in percent for each class of objects to be dropped.
+ * This could perhaps be an array - but that wouldn't be as clear.
+ */
+typedef struct obj_theme obj_theme;
+struct obj_theme
+{
+ byte treasure;
+ byte combat;
+ byte magic;
+ byte tools;
+};
+
+
+/*
+ * Information about terrain "features"
+ */
+
+typedef struct feature_type feature_type;
+
+struct feature_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+ u32b tunnel; /* Text for tunneling */
+ u32b block; /* Text for blocking */
+
+ byte mimic; /* Feature to mimic */
+
+ u32b flags1; /* First set of flags */
+
+ byte extra; /* Extra byte (unused) */
+
+ s16b unused; /* Extra bytes (unused) */
+
+ byte d_attr; /* Default feature attribute */
+ char d_char; /* Default feature character */
+
+
+ byte x_attr; /* Desired feature attribute */
+ char x_char; /* Desired feature character */
+
+ byte shimmer[7]; /* Shimmer colors */
+
+ int d_dice[4]; /* Number of dices */
+ int d_side[4]; /* Number of sides */
+ int d_frequency[4]; /* Frequency of damage (1 is the minimum) */
+ int d_type[4]; /* Type of damage */
+};
+
+
+/*
+ * Information about object "kinds", including player knowledge.
+ *
+ * Only "aware" and "tried" are saved in the savefile
+ */
+
+typedef struct object_kind object_kind;
+
+struct object_kind
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte tval; /* Object type */
+ byte sval; /* Object sub type */
+
+ s32b pval; /* Object extra info */
+ s32b pval2; /* Object extra info */
+
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to damage */
+ s16b to_a; /* Bonus to armor */
+
+ s16b activate; /* Activation number */
+
+ s16b ac; /* Base armor */
+
+ byte dd, ds; /* Damage dice/sides */
+
+ s32b weight; /* Weight */
+
+ s32b cost; /* Object "base cost" */
+
+ u32b flags1; /* Flags, set 1 */
+ u32b flags2; /* Flags, set 2 */
+ u32b flags3; /* Flags, set 3 */
+ u32b flags4; /* Flags, set 4 */
+ u32b flags5; /* Flags, set 5 */
+
+ u32b oflags1; /* Obvious Flags, set 1 */
+ u32b oflags2; /* Obvious Flags, set 2 */
+ u32b oflags3; /* Obvious Flags, set 3 */
+ u32b oflags4; /* Obvious Flags, set 4 */
+ u32b oflags5; /* Obvious Flags, set 5 */
+
+ byte locale[4]; /* Allocation level(s) */
+ byte chance[4]; /* Allocation chance(s) */
+
+ byte level; /* Level */
+ byte extra; /* Something */
+
+
+ byte d_attr; /* Default object attribute */
+ char d_char; /* Default object character */
+
+
+ byte x_attr; /* Desired object attribute */
+ char x_char; /* Desired object character */
+
+
+ byte flavor; /* Special object flavor (or zero) */
+
+ bool_ easy_know; /* This object is always known (if aware) */
+
+
+ bool_ aware; /* The player is "aware" of the item's effects */
+
+ bool_ tried; /* The player has "tried" one of the items */
+
+ bool_ know; /* extractable flag for the alchemist */
+
+ u32b esp; /* ESP flags */
+ u32b oesp; /* Obvious ESP flags */
+
+ byte btval; /* Become Object type */
+ byte bsval; /* Become Object sub type */
+ bool_ artifact; /* Is it a normal artifact(already generated) */
+
+ s16b power; /* Power granted(if any) */
+};
+
+
+
+/*
+ * Information about "artifacts".
+ *
+ * Note that the save-file only writes "cur_num" to the savefile.
+ *
+ * Note that "max_num" is always "1" (if that artifact "exists")
+ */
+
+typedef struct artifact_type artifact_type;
+
+struct artifact_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte tval; /* Artifact type */
+ byte sval; /* Artifact sub type */
+
+ s16b pval; /* Artifact extra info */
+
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to damage */
+ s16b to_a; /* Bonus to armor */
+
+ s16b activate; /* Activation Number */
+
+ s16b ac; /* Base armor */
+
+ byte dd, ds; /* Damage when hits */
+
+ s16b weight; /* Weight */
+
+ s32b cost; /* Artifact "cost" */
+
+ u32b flags1; /* Artifact Flags, set 1 */
+ u32b flags2; /* Artifact Flags, set 2 */
+ u32b flags3; /* Artifact Flags, set 3 */
+ u32b flags4; /* Artifact Flags, set 4 */
+ u32b flags5; /* Artifact Flags, set 5 */
+
+ u32b oflags1; /* Obvious Flags, set 1 */
+ u32b oflags2; /* Obvious Flags, set 2 */
+ u32b oflags3; /* Obvious Flags, set 3 */
+ u32b oflags4; /* Obvious Flags, set 4 */
+ u32b oflags5; /* Obvious Flags, set 5 */
+
+ byte level; /* Artifact level */
+ byte rarity; /* Artifact rarity */
+
+ byte cur_num; /* Number created (0 or 1) */
+ byte max_num; /* Unused (should be "1") */
+
+ u32b esp; /* ESP flags */
+ u32b oesp; /* ESP flags */
+
+ s16b power; /* Power granted(if any) */
+
+ s16b set; /* Does it belongs to a set ?*/
+};
+
+
+/*
+ * Information about "ego-items".
+ */
+
+typedef struct ego_item_type ego_item_type;
+
+struct ego_item_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ bool_ before; /* Before or after the object name ? */
+
+ byte tval[10];
+ byte min_sval[10];
+ byte max_sval[10];
+
+ byte rating; /* Rating boost */
+
+ byte level; /* Minimum level */
+ byte rarity; /* Object rarity */
+ byte mrarity; /* Object rarity */
+
+ s16b max_to_h; /* Maximum to-hit bonus */
+ s16b max_to_d; /* Maximum to-dam bonus */
+ s16b max_to_a; /* Maximum to-ac bonus */
+
+ s16b activate; /* Activation Number */
+
+ s32b max_pval; /* Maximum pval */
+
+ s32b cost; /* Ego-item "cost" */
+
+ byte rar[5];
+ u32b flags1[5]; /* Ego-Item Flags, set 1 */
+ u32b flags2[5]; /* Ego-Item Flags, set 2 */
+ u32b flags3[5]; /* Ego-Item Flags, set 3 */
+ u32b flags4[5]; /* Ego-Item Flags, set 4 */
+ u32b flags5[5]; /* Ego-Item Flags, set 5 */
+ u32b esp[5]; /* ESP flags */
+ u32b oflags1[5]; /* Ego-Item Obvious Flags, set 1 */
+ u32b oflags2[5]; /* Ego-Item Obvious Flags, set 2 */
+ u32b oflags3[5]; /* Ego-Item Obvious Flags, set 3 */
+ u32b oflags4[5]; /* Ego-Item Obvious Flags, set 4 */
+ u32b oflags5[5]; /* Ego-Item Obvious Flags, set 5 */
+ u32b oesp[5]; /* Obvious ESP flags */
+ u32b fego[5]; /* ego flags */
+
+ u32b need_flags1; /* Ego-Item Flags, set 1 */
+ u32b need_flags2; /* Ego-Item Flags, set 2 */
+ u32b need_flags3; /* Ego-Item Flags, set 3 */
+ u32b need_flags4; /* Ego-Item Flags, set 4 */
+ u32b need_flags5; /* Ego-Item Flags, set 5 */
+ u32b need_esp; /* ESP flags */
+ u32b forbid_flags1; /* Ego-Item Flags, set 1 */
+ u32b forbid_flags2; /* Ego-Item Flags, set 2 */
+ u32b forbid_flags3; /* Ego-Item Flags, set 3 */
+ u32b forbid_flags4; /* Ego-Item Flags, set 4 */
+ u32b forbid_flags5; /* Ego-Item Flags, set 5 */
+ u32b forbid_esp; /* ESP flags */
+
+ s16b power; /* Power granted(if any) */
+};
+
+
+/*
+ * Information about "random artifacts parts".
+ */
+typedef struct randart_part_type randart_part_type;
+struct randart_part_type
+{
+ byte tval[20];
+ byte min_sval[20];
+ byte max_sval[20];
+
+ byte level; /* Minimum level */
+ byte rarity; /* Object rarity */
+ byte mrarity; /* Object rarity */
+
+ s16b max_to_h; /* Maximum to-hit bonus */
+ s16b max_to_d; /* Maximum to-dam bonus */
+ s16b max_to_a; /* Maximum to-ac bonus */
+
+ s32b max_pval; /* Maximum pval */
+
+ s32b value; /* power value */
+ s16b max; /* Number of time it can appear on a single item */
+
+ u32b flags1; /* Ego-Item Flags, set 1 */
+ u32b flags2; /* Ego-Item Flags, set 2 */
+ u32b flags3; /* Ego-Item Flags, set 3 */
+ u32b flags4; /* Ego-Item Flags, set 4 */
+ u32b flags5; /* Ego-Item Flags, set 5 */
+ u32b esp; /* ESP flags */
+ u32b fego; /* ego flags */
+
+ u32b aflags1; /* Ego-Item Flags, set 1 */
+ u32b aflags2; /* Ego-Item Flags, set 2 */
+ u32b aflags3; /* Ego-Item Flags, set 3 */
+ u32b aflags4; /* Ego-Item Flags, set 4 */
+ u32b aflags5; /* Ego-Item Flags, set 5 */
+ u32b aesp; /* ESP flags */
+
+ s16b power; /* Power granted(if any) */
+};
+
+typedef struct randart_gen_type randart_gen_type;
+struct randart_gen_type
+{
+ int chance; /* Chance to have that number of powers */
+ int dd;
+ int ds;
+ int plus; /* xdy+plus power */
+};
+
+
+/*
+ * Monster blow structure
+ *
+ * - Method (RBM_*)
+ * - Effect (RBE_*)
+ * - Damage Dice
+ * - Damage Sides
+ */
+
+typedef struct monster_blow monster_blow;
+
+struct monster_blow
+{
+ byte method;
+ byte effect;
+ byte d_dice;
+ byte d_side;
+};
+
+
+
+/*
+ * Monster "race" information, including racial memories
+ *
+ * Note that "d_attr" and "d_char" are used for MORE than "visual" stuff.
+ *
+ * Note that "x_attr" and "x_char" are used ONLY for "visual" stuff.
+ *
+ * Note that "cur_num" (and "max_num") represent the number of monsters
+ * of the given race currently on (and allowed on) the current level.
+ * This information yields the "dead" flag for Unique monsters.
+ *
+ * Note that "max_num" is reset when a new player is created.
+ * Note that "cur_num" is reset when a new level is created.
+ *
+ * Note that several of these fields, related to "recall", can be
+ * scrapped if space becomes an issue, resulting in less "complete"
+ * monster recall (no knowledge of spells, etc). All of the "recall"
+ * fields have a special prefix to aid in searching for them.
+ */
+
+
+typedef struct monster_race monster_race;
+
+struct monster_race
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ u16b hdice; /* Creatures hit dice count */
+ u16b hside; /* Creatures hit dice sides */
+
+ s16b ac; /* Armour Class */
+
+ s16b sleep; /* Inactive counter (base) */
+ byte aaf; /* Area affect radius (1-100) */
+ byte speed; /* Speed (normally 110) */
+
+ s32b mexp; /* Exp value for kill */
+
+ s32b weight; /* Weight of the monster */
+
+ byte freq_inate; /* Inate spell frequency */
+ byte freq_spell; /* Other spell frequency */
+
+ u32b flags1; /* Flags 1 (general) */
+ u32b flags2; /* Flags 2 (abilities) */
+ u32b flags3; /* Flags 3 (race/resist) */
+ u32b flags4; /* Flags 4 (inate/breath) */
+ u32b flags5; /* Flags 5 (normal spells) */
+ u32b flags6; /* Flags 6 (special spells) */
+ u32b flags7; /* Flags 7 (movement related abilities) */
+ u32b flags8; /* Flags 8 (wilderness info) */
+ u32b flags9; /* Flags 9 (drops info) */
+
+ monster_blow blow[4]; /* Up to four blows per round */
+
+ byte body_parts[BODY_MAX]; /* To help to decide what to use when body changing */
+
+ byte level; /* Level of creature */
+ byte rarity; /* Rarity of creature */
+
+
+ byte d_attr; /* Default monster attribute */
+ char d_char; /* Default monster character */
+
+
+ byte x_attr; /* Desired monster attribute */
+ char x_char; /* Desired monster character */
+
+
+ s16b max_num; /* Maximum population allowed per level */
+
+ byte cur_num; /* Monster population on current level */
+
+
+ s16b r_sights; /* Count sightings of this monster */
+ s16b r_deaths; /* Count deaths from this monster */
+
+ s16b r_pkills; /* Count monsters killed in this life */
+ s16b r_tkills; /* Count monsters killed in all lives */
+
+ byte r_wake; /* Number of times woken up (?) */
+ byte r_ignore; /* Number of times ignored (?) */
+
+ byte r_xtra1; /* Something (unused) */
+ byte r_xtra2; /* Something (unused) */
+
+ byte r_drop_gold; /* Max number of gold dropped at once */
+ byte r_drop_item; /* Max number of item dropped at once */
+
+ byte r_cast_inate; /* Max number of inate spells seen */
+ byte r_cast_spell; /* Max number of other spells seen */
+
+ byte r_blows[4]; /* Number of times each blow type was seen */
+
+ u32b r_flags1; /* Observed racial flags */
+ u32b r_flags2; /* Observed racial flags */
+ u32b r_flags3; /* Observed racial flags */
+ u32b r_flags4; /* Observed racial flags */
+ u32b r_flags5; /* Observed racial flags */
+ u32b r_flags6; /* Observed racial flags */
+ u32b r_flags7; /* Observed racial flags */
+ u32b r_flags8; /* Observed racial flags */
+ u32b r_flags9; /* Observed racial flags */
+
+ bool_ on_saved; /* Is the (unique) on a saved level ? */
+
+ byte total_visible; /* Amount of this race that are visible */
+
+ obj_theme drops; /* The drops type */
+};
+
+
+typedef struct monster_ego monster_ego;
+
+struct monster_ego
+{
+ u32b name; /* Name (offset) */
+ bool_ before; /* Display ego before or after */
+
+ monster_blow blow[4]; /* Up to four blows per round */
+ byte blowm[4][2];
+
+ s16b hdice; /* Creatures hit dice count */
+ s16b hside; /* Creatures hit dice sides */
+
+ s16b ac; /* Armour Class */
+
+ s16b sleep; /* Inactive counter (base) */
+ s16b aaf; /* Area affect radius (1-100) */
+ s16b speed; /* Speed (normally 110) */
+
+ s32b mexp; /* Exp value for kill */
+
+ s32b weight; /* Weight of the monster */
+
+ byte freq_inate; /* Inate spell frequency */
+ byte freq_spell; /* Other spell frequency */
+
+ /* Ego flags */
+ u32b flags1; /* Flags 1 */
+ u32b flags2; /* Flags 1 */
+ u32b flags3; /* Flags 1 */
+ u32b flags7; /* Flags 1 */
+ u32b flags8; /* Flags 1 */
+ u32b flags9; /* Flags 1 */
+ u32b hflags1; /* Flags 1 */
+ u32b hflags2; /* Flags 1 */
+ u32b hflags3; /* Flags 1 */
+ u32b hflags7; /* Flags 1 */
+ u32b hflags8; /* Flags 1 */
+ u32b hflags9; /* Flags 1 */
+
+ /* Monster flags */
+ u32b mflags1; /* Flags 1 (general) */
+ u32b mflags2; /* Flags 2 (abilities) */
+ u32b mflags3; /* Flags 3 (race/resist) */
+ u32b mflags4; /* Flags 4 (inate/breath) */
+ u32b mflags5; /* Flags 5 (normal spells) */
+ u32b mflags6; /* Flags 6 (special spells) */
+ u32b mflags7; /* Flags 7 (movement related abilities) */
+ u32b mflags8; /* Flags 8 (wilderness info) */
+ u32b mflags9; /* Flags 9 (drops info) */
+
+ /* Negative Flags, to be removed from the monster flags */
+ u32b nflags1; /* Flags 1 (general) */
+ u32b nflags2; /* Flags 2 (abilities) */
+ u32b nflags3; /* Flags 3 (race/resist) */
+ u32b nflags4; /* Flags 4 (inate/breath) */
+ u32b nflags5; /* Flags 5 (normal spells) */
+ u32b nflags6; /* Flags 6 (special spells) */
+ u32b nflags7; /* Flags 7 (movement related abilities) */
+ u32b nflags8; /* Flags 8 (wilderness info) */
+ u32b nflags9; /* Flags 9 (drops info) */
+
+ s16b level; /* Level of creature */
+ s16b rarity; /* Rarity of creature */
+
+
+ byte d_attr; /* Default monster attribute */
+ char d_char; /* Default monster character */
+
+ byte g_attr; /* Overlay graphic attribute */
+ char g_char; /* Overlay graphic character */
+
+ char r_char[5]; /* Monster race allowed */
+ char nr_char[5]; /* Monster race not allowed */
+};
+
+
+
+/*
+ * Information about "vault generation"
+ */
+
+typedef struct vault_type vault_type;
+
+struct vault_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte typ; /* Vault type */
+
+ byte rat; /* Vault rating */
+
+ byte hgt; /* Vault height */
+ byte wid; /* Vault width */
+
+ s16b lvl; /* level of special (if any) */
+ byte dun_type; /* Dungeon type where the level will show up */
+
+ s16b mon[10]; /* special monster */
+ int item[3]; /* number of item (usually artifact) */
+};
+
+
+/* jk */
+/* name and description are in some other arrays */
+typedef struct trap_type trap_type;
+
+struct trap_type
+{
+ s16b probability; /* probability of existence */
+ s16b another; /* does this trap easily combine */
+ s16b p1valinc; /* how much does this trap attribute to p1val */
+ byte difficulty; /* how difficult to disarm */
+ byte minlevel; /* what is the minimum level on which the traps should be */
+ byte color; /* what is the color on screen */
+ u32b flags; /* where can these traps go - and perhaps other flags */
+ bool_ ident; /* do we know the name */
+ s16b known; /* how well is this trap known */
+ s16b name; /* normal name like weakness */
+ s16b dd, ds; /* base damage */
+ s16b text; /* longer description once you've met this trap */
+ byte g_attr; /* Overlay graphic attribute */
+ char g_char; /* Overlay graphic character */
+};
+
+
+
+/*
+ * A single "grid" in a Cave
+ *
+ * Note that several aspects of the code restrict the actual cave
+ * to a max size of 256 by 256. In partcular, locations are often
+ * saved as bytes, limiting each coordinate to the 0-255 range.
+ *
+ * The "o_idx" and "m_idx" fields are very interesting. There are
+ * many places in the code where we need quick access to the actual
+ * monster or object(s) in a given cave grid. The easiest way to
+ * do this is to simply keep the index of the monster and object
+ * (if any) with the grid, but this takes 198*66*4 bytes of memory.
+ * Several other methods come to mind, which require only half this
+ * amound of memory, but they all seem rather complicated, and would
+ * probably add enough code that the savings would be lost. So for
+ * these reasons, we simply store an index into the "o_list" and
+ * "m_list" arrays, using "zero" when no monster/object is present.
+ *
+ * Note that "o_idx" is the index of the top object in a stack of
+ * objects, using the "next_o_idx" field of objects (see below) to
+ * create the singly linked list of objects. If "o_idx" is zero
+ * then there are no objects in the grid.
+ */
+
+typedef struct cave_type cave_type;
+
+struct cave_type
+{
+ u16b info; /* Hack -- cave flags */
+
+ byte feat; /* Hack -- feature type */
+
+ s16b o_idx; /* Object in this grid */
+
+ s16b m_idx; /* Monster in this grid */
+
+ s16b t_idx; /* trap index (in t_list) or zero */
+
+ s16b special, special2; /* Special cave info */
+
+ s16b inscription; /* Inscription of the grid */
+
+ byte mana; /* Magical energy of the grid */
+
+ byte mimic; /* Feature to mimic */
+
+ byte cost; /* Hack -- cost of flowing */
+ byte when; /* Hack -- when cost was computed */
+
+ s16b effect; /* The lasting effects */
+};
+
+/* Lasting spell effects(clouds, ..) */
+typedef struct effect_type effect_type;
+struct effect_type
+{
+ s16b time; /* For how long */
+ s16b dam; /* How much damage */
+ s16b type; /* Of which type */
+ s16b cy; /* Center of the cast*/
+ s16b cx; /* Center of the cast*/
+ s16b rad; /* Radius -- if needed */
+ u32b flags; /* Flags */
+};
+
+/*
+ * Object information, for a specific object.
+ *
+ * Note that a "discount" on an item is permanent and never goes away.
+ *
+ * Note that inscriptions are now handled via the "quark_str()" function
+ * applied to the "note" field, which will return NULL if "note" is zero.
+ *
+ * Note that "object" records are "copied" on a fairly regular basis,
+ * and care must be taken when handling such objects.
+ *
+ * Note that "object flags" must now be derived from the object kind,
+ * the artifact and ego-item indexes, and the two "xtra" fields.
+ *
+ * Each cave grid points to one (or zero) objects via the "o_idx"
+ * field (above). Each object then points to one (or zero) objects
+ * via the "next_o_idx" field, forming a singly linked list, which
+ * in game terms, represents a "stack" of objects in the same grid.
+ *
+ * Each monster points to one (or zero) objects via the "hold_o_idx"
+ * field (below). Each object then points to one (or zero) objects
+ * via the "next_o_idx" field, forming a singly linked list, which
+ * in game terms, represents a pile of objects held by the monster.
+ *
+ * The "held_m_idx" field is used to indicate which monster, if any,
+ * is holding the object. Objects being held have "ix=0" and "iy=0".
+ */
+
+typedef struct object_type object_type;
+
+struct object_type
+{
+ s16b k_idx; /* Kind index (zero if "dead") */
+
+ byte iy; /* Y-position on map, or zero */
+ byte ix; /* X-position on map, or zero */
+
+ byte tval; /* Item type (from kind) */
+ byte sval; /* Item sub-type (from kind) */
+
+ s32b pval; /* Item extra-parameter */
+ s16b pval2; /* Item extra-parameter for some special
+ items*/
+ s32b pval3; /* Item extra-parameter for some special
+ items*/
+
+ byte discount; /* Discount (if any) */
+
+ byte number; /* Number of items */
+
+ s32b weight; /* Item weight */
+
+ byte elevel; /* Item exp level */
+ s32b exp; /* Item exp */
+
+ byte name1; /* Artifact type, if any */
+ s16b name2; /* Ego-Item type, if any */
+ s16b name2b; /* Second Ego-Item type, if any */
+
+ byte xtra1; /* Extra info type */
+ s16b xtra2; /* Extra info index */
+
+ s16b to_h; /* Plusses to hit */
+ s16b to_d; /* Plusses to damage */
+ s16b to_a; /* Plusses to AC */
+
+ s16b ac; /* Normal AC */
+
+ byte dd, ds; /* Damage dice/sides */
+
+ s16b timeout; /* Timeout Counter */
+
+ byte ident; /* Special flags */
+
+ byte marked; /* Object is marked */
+
+ u16b note; /* Inscription index */
+ u16b art_name; /* Artifact name (random artifacts) */
+
+ u32b art_flags1; /* Flags, set 1 Alas, these were necessary */
+ u32b art_flags2; /* Flags, set 2 for the random artifacts of*/
+ u32b art_flags3; /* Flags, set 3 Zangband */
+ u32b art_flags4; /* Flags, set 4 PernAngband */
+ u32b art_flags5; /* Flags, set 5 PernAngband */
+ u32b art_esp; /* Flags, set esp PernAngband */
+
+ u32b art_oflags1; /* Obvious Flags, set 1 */
+ u32b art_oflags2; /* Obvious Flags, set 2 */
+ u32b art_oflags3; /* Obvious Flags, set 3 */
+ u32b art_oflags4; /* Obvious Flags, set 4 */
+ u32b art_oflags5; /* Obvious Flags, set 5 */
+ u32b art_oesp; /* Obvious Flags, set esp */
+
+ s16b next_o_idx; /* Next object in stack (if any) */
+
+ s16b held_m_idx; /* Monster holding us (if any) */
+
+ byte sense; /* Pseudo-id status */
+
+ byte found; /* How did we find it */
+ s16b found_aux1; /* Stores info for found */
+ s16b found_aux2; /* Stores info for found */
+ s16b found_aux3; /* Stores info for found */
+ s16b found_aux4; /* Stores info for found */
+};
+
+
+/*
+ * Monster mind, use for skills and such
+ */
+typedef struct monster_mind monster_mind;
+struct monster_mind
+{
+ /*
+ * Without this, bcc can't compile because it does not
+ * allow empty structure. Remove this when you add some
+ * variables to this structure. -- Kusunose
+ */
+ byte dummy;
+};
+
+
+
+/*
+ * Monster information, for a specific monster.
+ *
+ * Note: fy, fx constrain dungeon size to 256x256
+ *
+ * The "hold_o_idx" field points to the first object of a stack
+ * of objects (if any) being carried by the monster (see above).
+ */
+
+typedef struct monster_type monster_type;
+
+struct monster_type
+{
+ s16b r_idx; /* Monster race index */
+
+ u16b ego; /* Ego monster type */
+
+ byte fy; /* Y location on map */
+ byte fx; /* X location on map */
+
+ s32b hp; /* Current Hit points */
+ s32b maxhp; /* Max Hit points */
+
+ monster_blow blow[4]; /* Up to four blows per round */
+
+ byte speed; /* Speed (normally 110) */
+ byte level; /* Level of creature */
+ s16b ac; /* Armour Class */
+ u32b exp; /* Experience */
+
+ s16b csleep; /* Inactive counter */
+
+ byte mspeed; /* Monster "speed" */
+ byte energy; /* Monster "energy" */
+
+ byte stunned; /* Monster is stunned */
+ byte confused; /* Monster is confused */
+ byte monfear; /* Monster is afraid */
+
+ s16b bleeding; /* Monster is bleeding */
+ s16b poisoned; /* Monster is poisoned */
+
+ byte cdis; /* Current dis from player */
+
+ s32b mflag; /* Extra monster flags */
+
+ bool_ ml; /* Monster is "visible" */
+
+ s16b hold_o_idx; /* Object being held (if any) */
+
+ u32b smart; /* Field for "smart_learn" */
+
+ s16b status; /* Status(friendly, pet, companion, ..) */
+
+ s16b target; /* Monster target */
+
+ s16b possessor; /* Is it under the control of a possessor ? */
+
+ monster_race *sr_ptr; /* Does it have a specific race(not in r_info) */
+
+ monster_mind *mind; /* Does it have a mind? */
+};
+
+
+
+
+/*
+ * An entry for the object/monster allocation functions
+ *
+ * Pass 1 is determined from allocation information
+ * Pass 2 is determined from allocation restriction
+ * Pass 3 is determined from allocation calculation
+ */
+
+typedef struct alloc_entry alloc_entry;
+
+struct alloc_entry
+{
+ s16b index; /* The actual index */
+
+ byte level; /* Base dungeon level */
+ byte prob1; /* Probability, pass 1 */
+ byte prob2; /* Probability, pass 2 */
+ byte prob3; /* Probability, pass 3 */
+
+ u16b total; /* Unused for now */
+};
+
+
+
+/*
+ * Available "options"
+ *
+ * - Address of actual option variable (or NULL)
+ *
+ * - Normal Value (TRUE or FALSE)
+ *
+ * - Option Page Number (or zero)
+ *
+ * - Savefile Set (or zero)
+ * - Savefile Bit in that set
+ *
+ * - Textual name (or NULL)
+ * - Textual description
+ */
+
+typedef struct option_type option_type;
+
+struct option_type
+{
+ bool_ *o_var;
+
+ byte o_norm;
+
+ byte o_page;
+
+ byte o_bit;
+
+ cptr o_text;
+ cptr o_desc;
+};
+
+/*
+ * A store owner
+ */
+typedef struct owner_type owner_type;
+
+struct owner_type
+{
+ u32b name; /* Name (offset) */
+
+ s16b max_cost; /* Purse limit */
+
+ byte max_inflate; /* Inflation (max) */
+ byte min_inflate; /* Inflation (min) */
+
+ byte haggle_per; /* Haggle unit */
+
+ byte insult_max; /* Insult limit */
+
+ u32b races[2][2]; /* Liked/hated races */
+ u32b classes[2][2]; /* Liked/hated classes */
+
+ s16b costs[3]; /* Costs for liked people */
+};
+
+
+
+
+/*
+ * A store, with an owner, various state flags, a current stock
+ * of items, and a table of items that are often purchased.
+ */
+typedef struct store_type store_type;
+
+struct store_type
+{
+ u16b st_idx;
+
+ u16b owner; /* Owner index */
+
+ s16b insult_cur; /* Insult counter */
+
+ s16b good_buy; /* Number of "good" buys */
+ s16b bad_buy; /* Number of "bad" buys */
+
+ s32b store_open; /* Closed until this turn */
+
+ s32b last_visit; /* Last visited on this turn */
+
+ byte stock_num; /* Stock -- Number of entries */
+ s16b stock_size; /* Stock -- Total Size of Array */
+ object_type *stock; /* Stock -- Actual stock items */
+};
+
+/*
+ * A store/building type
+ */
+typedef struct store_info_type store_info_type;
+
+struct store_info_type
+{
+ u32b name; /* Name (offset) */
+
+ s16b table[STORE_CHOICES][2]; /* Table -- Legal item kinds */
+ byte table_num; /* Number of items */
+ s16b max_obj; /* Number of items this store can hold */
+
+ u16b owners[4]; /* List of owners(refers to ow_info) */
+
+ u16b actions[6]; /* Actions(refers to ba_info) */
+
+ byte d_attr; /* Default building attribute */
+ char d_char; /* Default building character */
+
+ byte x_attr; /* Desired building attribute */
+ char x_char; /* Desired building character */
+
+ u32b flags1; /* Flags */
+};
+
+/*
+ * Stores/buildings actions
+ */
+typedef struct store_action_type store_action_type;
+
+struct store_action_type
+{
+ u32b name; /* Name (offset) */
+
+ s16b costs[3]; /* Costs for liked people */
+ char letter; /* Action letter */
+ char letter_aux; /* Action letter */
+ s16b action; /* Action code */
+ s16b action_restr; /* Action restriction */
+};
+
+/*
+ * the spell function must provide the desc
+ */
+typedef struct magic_type magic_type;
+
+struct magic_type
+{
+ byte slevel; /* Required level (to learn) */
+ byte smana; /* Required mana (to cast) */
+ byte sfail; /* Minimum chance of failure */
+ byte sexp; /* Encoded experience bonus */
+};
+
+/*
+ * Player sex info
+ */
+
+typedef struct player_sex player_sex;
+
+struct player_sex
+{
+ cptr title; /* Type of sex */
+
+ cptr winner; /* Name of winner */
+};
+
+
+/*
+ * Player racial info
+ */
+
+typedef struct player_race player_race;
+
+struct player_race
+{
+ s32b title; /* Type of race */
+ s32b desc;
+
+ s16b r_adj[6]; /* Racial stat bonuses */
+
+ char luck; /* Luck */
+
+ s16b r_dis; /* disarming */
+ s16b r_dev; /* magic devices */
+ s16b r_sav; /* saving throw */
+ s16b r_stl; /* stealth */
+ s16b r_srh; /* search ability */
+ s16b r_fos; /* search frequency */
+ s16b r_thn; /* combat (normal) */
+ s16b r_thb; /* combat (shooting) */
+
+ byte r_mhp; /* Race hit-dice modifier */
+ u16b r_exp; /* Race experience factor */
+
+ byte b_age; /* base age */
+ byte m_age; /* mod age */
+
+ byte m_b_ht; /* base height (males) */
+ byte m_m_ht; /* mod height (males) */
+ byte m_b_wt; /* base weight (males) */
+ byte m_m_wt; /* mod weight (males) */
+
+ byte f_b_ht; /* base height (females) */
+ byte f_m_ht; /* mod height (females) */
+ byte f_b_wt; /* base weight (females) */
+ byte f_m_wt; /* mod weight (females) */
+
+ byte infra; /* Infra-vision range */
+
+ u32b choice[2]; /* Legal class choices */
+
+ s16b powers[4]; /* Powers of the race */
+
+ byte body_parts[BODY_MAX]; /* To help to decide what to use when body changing */
+
+ s16b chart; /* Chart history */
+
+ u32b flags1;
+ u32b flags2; /* flags */
+
+ u32b oflags1[PY_MAX_LEVEL + 1];
+ u32b oflags2[PY_MAX_LEVEL + 1];
+ u32b oflags3[PY_MAX_LEVEL + 1];
+ u32b oflags4[PY_MAX_LEVEL + 1];
+ u32b oflags5[PY_MAX_LEVEL + 1];
+ u32b oesp[PY_MAX_LEVEL + 1];
+ s16b opval[PY_MAX_LEVEL + 1];
+
+ char skill_basem[MAX_SKILLS];
+ u32b skill_base[MAX_SKILLS];
+ char skill_modm[MAX_SKILLS];
+ s16b skill_mod[MAX_SKILLS];
+
+ s16b obj_tval[5];
+ s16b obj_sval[5];
+ s16b obj_pval[5];
+ s16b obj_dd[5];
+ s16b obj_ds[5];
+ s16b obj_num;
+
+ struct
+ {
+ s16b ability;
+ s16b level;
+ } abilities[10]; /* Abilitiers to be gained by level(doesnt take prereqs in account) */
+};
+
+typedef struct player_race_mod player_race_mod;
+
+struct player_race_mod
+{
+ s32b title; /* Type of race mod */
+ s32b desc; /* Desc */
+ bool_ place; /* TRUE = race race modifier, FALSE = Race modifier race */
+
+ s16b r_adj[6]; /* (+) Racial stat bonuses */
+
+ char luck; /* Luck */
+ s16b mana; /* Mana % */
+
+ s16b r_dis; /* (+) disarming */
+ s16b r_dev; /* (+) magic devices */
+ s16b r_sav; /* (+) saving throw */
+ s16b r_stl; /* (+) stealth */
+ s16b r_srh; /* (+) search ability */
+ s16b r_fos; /* (+) search frequency */
+ s16b r_thn; /* (+) combat (normal) */
+ s16b r_thb; /* (+) combat (shooting) */
+
+ char r_mhp; /* (+) Race mod hit-dice modifier */
+ s16b r_exp; /* (+) Race mod experience factor */
+
+ char b_age; /* (+) base age */
+ char m_age; /* (+) mod age */
+
+ char m_b_ht; /* (+) base height (males) */
+ char m_m_ht; /* (+) mod height (males) */
+ char m_b_wt; /* (+) base weight (males) */
+ char m_m_wt; /* (+) mod weight (males) */
+
+ char f_b_ht; /* (+) base height (females) */
+ char f_m_ht; /* (+) mod height (females) */
+ char f_b_wt; /* (+) base weight (females) */
+ char f_m_wt; /* (+) mod weight (females) */
+
+ char infra; /* (+) Infra-vision range */
+
+ u32b choice[2]; /* Legal race choices */
+
+ u32b pclass[2]; /* Classes allowed */
+ u32b mclass[2]; /* Classes restricted */
+
+ s16b powers[4]; /* Powers of the subrace */
+
+ char body_parts[BODY_MAX]; /* To help to decide what to use when body changing */
+
+ u32b flags1;
+ u32b flags2; /* flags */
+
+ u32b oflags1[PY_MAX_LEVEL + 1];
+ u32b oflags2[PY_MAX_LEVEL + 1];
+ u32b oflags3[PY_MAX_LEVEL + 1];
+ u32b oflags4[PY_MAX_LEVEL + 1];
+ u32b oflags5[PY_MAX_LEVEL + 1];
+ u32b oesp[PY_MAX_LEVEL + 1];
+ s16b opval[PY_MAX_LEVEL + 1];
+
+ byte g_attr; /* Overlay graphic attribute */
+ char g_char; /* Overlay graphic character */
+
+ char skill_basem[MAX_SKILLS];
+ u32b skill_base[MAX_SKILLS];
+ char skill_modm[MAX_SKILLS];
+ s16b skill_mod[MAX_SKILLS];
+
+ s16b obj_tval[5];
+ s16b obj_sval[5];
+ s16b obj_pval[5];
+ s16b obj_dd[5];
+ s16b obj_ds[5];
+ s16b obj_num;
+
+ struct
+ {
+ s16b ability;
+ s16b level;
+ } abilities[10]; /* Abilitiers to be gained by level(doesnt take prereqs in account) */
+};
+
+
+/*
+ * Player class info
+ */
+
+typedef struct player_spec player_spec;
+
+struct player_spec
+{
+ s32b title; /* Type of class spec */
+ s32b desc; /* Small desc of the class spec */
+
+ char skill_basem[MAX_SKILLS]; /* Mod for value */
+ u32b skill_base[MAX_SKILLS]; /* value */
+ char skill_modm[MAX_SKILLS]; /* mod for mod */
+ s16b skill_mod[MAX_SKILLS]; /* mod */
+
+ u32b skill_ideal[MAX_SKILLS]; /* Ideal skill levels at level 50 */
+
+ s16b obj_tval[5];
+ s16b obj_sval[5];
+ s16b obj_pval[5];
+ s16b obj_dd[5];
+ s16b obj_ds[5];
+ s16b obj_num;
+
+ u32b gods;
+
+ u32b flags1;
+ u32b flags2; /* flags */
+
+ struct
+ {
+ s16b ability;
+ s16b level;
+ } abilities[10]; /* Abilitiers to be gained by level(doesnt take prereqs in account) */
+};
+
+typedef struct player_class player_class;
+
+struct player_class
+{
+ s32b title; /* Type of class */
+ s32b desc; /* Small desc of the class */
+ s32b titles[PY_MAX_LEVEL / 5];
+
+ s16b c_adj[6]; /* Class stat modifier */
+
+ s16b c_dis; /* class disarming */
+ s16b c_dev; /* class magic devices */
+ s16b c_sav; /* class saving throws */
+ s16b c_stl; /* class stealth */
+ s16b c_srh; /* class searching ability */
+ s16b c_fos; /* class searching frequency */
+ s16b c_thn; /* class to hit (normal) */
+ s16b c_thb; /* class to hit (bows) */
+
+ s16b x_dis; /* extra disarming */
+ s16b x_dev; /* extra magic devices */
+ s16b x_sav; /* extra saving throws */
+ s16b x_stl; /* extra stealth */
+ s16b x_srh; /* extra searching ability */
+ s16b x_fos; /* extra searching frequency */
+ s16b x_thn; /* extra to hit (normal) */
+ s16b x_thb; /* extra to hit (bows) */
+
+ s16b c_mhp; /* Class hit-dice adjustment */
+ s16b c_exp; /* Class experience factor */
+
+ s16b powers[4]; /* Powers of the class */
+
+ s16b spell_book; /* Tval of spell books (if any) */
+ s16b spell_stat; /* Stat for spells (if any) */
+ s16b spell_lev; /* The higher it is the higher the spells level are */
+ s16b spell_fail; /* The higher it is the higher the spells failure are */
+ s16b spell_mana; /* The higher it is the higher the spells mana are */
+ s16b spell_first; /* Level of first spell */
+ s16b spell_weight; /* Weight that hurts spells */
+ byte max_spell_level; /* Maximun spell level */
+ byte magic_max_spell; /* Maximun numbner of spells one can learn by natural means */
+
+ u32b flags1; /* flags */
+ u32b flags2; /* flags */
+
+ s16b mana;
+ s16b blow_num;
+ s16b blow_wgt;
+ s16b blow_mul;
+ s16b extra_blows;
+
+ s32b sense_base;
+ s32b sense_pl;
+ s32b sense_plus;
+ byte sense_heavy;
+ byte sense_heavy_magic;
+
+ s16b obj_tval[5];
+ s16b obj_sval[5];
+ s16b obj_pval[5];
+ s16b obj_dd[5];
+ s16b obj_ds[5];
+ s16b obj_num;
+
+ char body_parts[BODY_MAX]; /* To help to decide what to use when body changing */
+
+ u32b oflags1[PY_MAX_LEVEL + 1];
+ u32b oflags2[PY_MAX_LEVEL + 1];
+ u32b oflags3[PY_MAX_LEVEL + 1];
+ u32b oflags4[PY_MAX_LEVEL + 1];
+ u32b oflags5[PY_MAX_LEVEL + 1];
+ u32b oesp[PY_MAX_LEVEL + 1];
+ s16b opval[PY_MAX_LEVEL + 1];
+
+ char skill_basem[MAX_SKILLS];
+ u32b skill_base[MAX_SKILLS];
+ char skill_modm[MAX_SKILLS];
+ s16b skill_mod[MAX_SKILLS];
+
+ u32b gods;
+
+ player_spec spec[MAX_SPEC];
+
+ struct
+ {
+ s16b ability;
+ s16b level;
+ } abilities[10]; /* Abilitiers to be gained by level(doesnt take prereqs in account) */
+};
+
+typedef struct meta_class_type meta_class_type;
+struct meta_class_type
+{
+ char name[80]; /* Name */
+ byte color;
+ s16b *classes; /* list of classes */
+};
+
+/* Help type */
+typedef struct help_info help_info;
+struct help_info
+{
+ bool_ enabled; /* ingame help enabled */
+
+ u32b help1; /* help flags 1 */
+};
+
+
+/*
+ * Most of the "player" information goes here.
+ *
+ * This stucture gives us a large collection of player variables.
+ *
+ * This structure contains several "blocks" of information.
+ * (1) the "permanent" info
+ * (2) the "variable" info
+ * (3) the "transient" info
+ *
+ * All of the "permanent" info, and most of the "variable" info,
+ * is saved in the savefile. The "transient" info is recomputed
+ * whenever anything important changes.
+ */
+
+typedef struct player_type player_type;
+
+struct player_type
+{
+ s32b lives; /* How many times we resurected */
+
+ s16b oldpy; /* Previous player location -KMW- */
+ s16b oldpx; /* Previous player location -KMW- */
+
+ s16b py; /* Player location */
+ s16b px; /* Player location */
+
+ byte psex; /* Sex index */
+ byte prace; /* Race index */
+ byte pracem; /* Race Mod index */
+ byte pclass; /* Class index */
+ byte pspec; /* Class spec index */
+ byte mimic_form; /* Actualy transformation */
+ s16b mimic_level; /* Level of the mimic effect */
+ byte oops; /* Unused */
+
+ object_type inventory[INVEN_TOTAL]; /* Player inventory */
+
+ byte hitdie; /* Hit dice (sides) */
+ u16b expfact; /* Experience factor */
+
+ byte maximize; /* Maximize stats */
+ byte preserve; /* Preserve artifacts */
+ byte special; /* Special levels */
+ byte allow_one_death; /* Blood of life */
+
+ s16b age; /* Characters age */
+ s16b ht; /* Height */
+ s16b wt; /* Weight */
+ s16b sc; /* Social Class */
+
+
+ s32b au; /* Current Gold */
+
+ s32b max_exp; /* Max experience */
+ s32b exp; /* Cur experience */
+ u16b exp_frac; /* Cur exp frac (times 2^16) */
+
+ s16b lev; /* Level */
+
+ s16b town_num; /* Current town number */
+ s16b arena_number; /* monster number in arena -KMW- */
+ s16b inside_arena; /* Is character inside arena? */
+ s16b inside_quest; /* Inside quest level */
+ bool_ exit_bldg; /* Goal obtained in arena? -KMW- */
+
+ s32b wilderness_x; /* Coordinates in the wilderness */
+ s32b wilderness_y;
+ bool_ wild_mode; /* TRUE = Small map, FLASE = Big map */
+ bool_ old_wild_mode; /* TRUE = Small map, FLASE = Big map */
+
+ s16b mhp; /* Max hit pts */
+ s16b chp; /* Cur hit pts */
+ u16b chp_frac; /* Cur hit frac (times 2^16) */
+ s16b hp_mod; /* A modificator(permanent) */
+
+ s16b msp; /* Max mana pts */
+ s16b csp; /* Cur mana pts */
+ u16b csp_frac; /* Cur mana frac (times 2^16) */
+
+ s16b msane; /* Max sanity */
+ s16b csane; /* Cur sanity */
+ u16b csane_frac; /* Cur sanity frac */
+
+ s32b grace; /* Your God's appreciation factor. */
+ byte pgod; /* Your God. */
+ bool_ praying; /* Praying to your god. */
+ s16b melkor_sacrifice; /* How much hp has been sacrified for damage */
+
+ s16b max_plv; /* Max Player Level */
+
+ s16b stat_max[6]; /* Current "maximal" stat values */
+ s16b stat_cur[6]; /* Current "natural" stat values */
+
+ s16b luck_cur; /* Current "natural" luck value (range -30 <> 30) */
+ s16b luck_max; /* Current "maximal base" luck value (range -30 <> 30) */
+ s16b luck_base; /* Current "base" luck value (range -30 <> 30) */
+
+ s16b speed_factor; /* Timed -- Fast */
+ s16b fast; /* Timed -- Fast */
+ s16b lightspeed; /* Timed -- Light Speed */
+ s16b slow; /* Timed -- Slow */
+ s16b blind; /* Timed -- Blindness */
+ s16b paralyzed; /* Timed -- Paralysis */
+ s16b confused; /* Timed -- Confusion */
+ s16b afraid; /* Timed -- Fear */
+ s16b image; /* Timed -- Hallucination */
+ s16b poisoned; /* Timed -- Poisoned */
+ s16b cut; /* Timed -- Cut */
+ s16b stun; /* Timed -- Stun */
+
+ s16b protevil; /* Timed -- Protection from Evil*/
+ s16b protgood; /* Timed -- Protection from Good*/
+ s16b protundead; /* Timed -- Protection from Undead*/
+ s16b invuln; /* Timed -- Invulnerable */
+ s16b hero; /* Timed -- Heroism */
+ s16b shero; /* Timed -- Super Heroism */
+ s16b shield; /* Timed -- Shield Spell */
+ s16b shield_power; /* Timed -- Shield Spell Power */
+ s16b shield_opt; /* Timed -- Shield Spell options */
+ s16b shield_power_opt; /* Timed -- Shield Spell Power */
+ s16b shield_power_opt2; /* Timed -- Shield Spell Power */
+ s16b blessed; /* Timed -- Blessed */
+ s16b tim_invis; /* Timed -- See Invisible */
+ s16b tim_infra; /* Timed -- Infra Vision */
+
+ s16b oppose_acid; /* Timed -- oppose acid */
+ s16b oppose_elec; /* Timed -- oppose lightning */
+ s16b oppose_fire; /* Timed -- oppose heat */
+ s16b oppose_cold; /* Timed -- oppose cold */
+ s16b oppose_pois; /* Timed -- oppose poison */
+ s16b oppose_ld; /* Timed -- oppose light & dark */
+ s16b oppose_cc; /* Timed -- oppose chaos & confusion */
+ s16b oppose_ss; /* Timed -- oppose sound & shards */
+ s16b oppose_nex; /* Timed -- oppose nexus */
+
+ s16b rush; /* Rush and Bush */
+
+ s16b tim_esp; /* Timed ESP */
+ s16b tim_wraith; /* Timed wraithform */
+ s16b tim_ffall; /* Timed Levitation */
+ s16b tim_fly; /* Timed Levitation */
+ s16b tim_fire_aura; /* Timed Fire Aura */
+ s16b tim_poison; /* Timed poison hands */
+ s16b tim_thunder; /* Timed thunderstorm */
+ s16b tim_thunder_p1; /* Timed thunderstorm */
+ s16b tim_thunder_p2; /* Timed thunderstorm */
+
+ s16b tim_project; /* Timed project upon melee blow */
+ s16b tim_project_dam;
+ s16b tim_project_gf;
+ s16b tim_project_rad;
+ s16b tim_project_flag;
+
+ s16b tim_roots; /* Timed roots */
+ s16b tim_roots_ac;
+ s16b tim_roots_dam;
+
+ s16b resist_magic; /* Timed Resist Magic (later) */
+ s16b tim_invisible; /* Timed Invisibility */
+ s16b tim_inv_pow; /* Power of timed invisibility */
+ s16b tim_mimic; /* Timed Mimic */
+ s16b tim_lite; /* Timed Lite */
+ s16b tim_regen; /* Timed extra regen */
+ s16b tim_regen_pow; /* Timed extra regen power */
+ s16b holy; /* Holy Aura */
+ s16b walk_water; /* Walk over water as a god */
+ s16b tim_mental_barrier; /* Sustain Int&Wis */
+ s16b strike; /* True Strike(+25 hit) */
+ s16b meditation; /* Meditation(+50 mana -25 to hit/to dam) */
+ s16b tim_reflect; /* Timed Reflection */
+ s16b tim_res_time; /* Timed Resistance to Time */
+ s16b tim_deadly; /* Timed deadly blow */
+ s16b prob_travel; /* Timed probability travel */
+ s16b disrupt_shield;/* Timed disruption shield */
+ s16b parasite; /* Timed parasite */
+ s16b parasite_r_idx;/* Timed parasite monster */
+ s32b loan; /* Amount of loan */
+ s32b loan_time; /* Timer -- time to payback loan */
+ s16b absorb_soul; /* Timed soul absordtion */
+ s16b tim_magic_breath; /* Magical breathing -- can breath anywhere */
+ s16b tim_water_breath; /* Water breathing -- can breath underwater */
+
+ s16b immov_cntr; /* Timed -- Last ``immovable'' command. */
+
+ s16b chaos_patron;
+
+ s16b recall_dungeon; /* Recall in which dungeon */
+ s16b word_recall; /* Word of recall counter */
+
+ s32b energy; /* Current energy */
+
+ s16b food; /* Current nutrition */
+
+ byte confusing; /* Glowing hands */
+ byte searching; /* Currently searching */
+
+ s16b new_spells; /* Number of spells available */
+
+ s16b old_spells;
+
+ s16b xtra_spells; /* Number of xtra spell learned(via potion) */
+
+ bool_ old_cumber_armor;
+ bool_ old_cumber_glove;
+ bool_ old_heavy_wield;
+ bool_ old_heavy_shoot;
+ bool_ old_icky_wield;
+
+ s16b old_lite; /* Old radius of lite (if any) */
+ s16b old_view; /* Old radius of view (if any) */
+
+ s16b old_food_aux; /* Old value of food */
+
+
+ bool_ cumber_armor; /* Mana draining armor */
+ bool_ cumber_glove; /* Mana draining gloves */
+ bool_ heavy_wield; /* Heavy weapon */
+ bool_ heavy_shoot; /* Heavy shooter */
+ bool_ icky_wield; /* Icky weapon */
+ bool_ immovable; /* Immovable character */
+
+ s16b cur_lite; /* Radius of lite (if any) */
+
+
+ u32b notice; /* Special Updates (bit flags) */
+ u32b update; /* Pending Updates (bit flags) */
+ u32b redraw; /* Normal Redraws (bit flags) */
+ u32b window; /* Window Redraws (bit flags) */
+
+ s16b stat_use[6]; /* Current modified stats */
+ s16b stat_top[6]; /* Maximal modified stats */
+
+ s16b stat_add[6]; /* Modifiers to stat values */
+ s16b stat_ind[6]; /* Indexes into stat tables */
+ s16b stat_cnt[6]; /* Counter for temporary drains */
+ s16b stat_los[6]; /* Amount of temporary drains */
+
+ bool_ immune_acid; /* Immunity to acid */
+ bool_ immune_elec; /* Immunity to lightning */
+ bool_ immune_fire; /* Immunity to fire */
+ bool_ immune_cold; /* Immunity to cold */
+ bool_ immune_neth; /* Immunity to nether */
+
+ bool_ resist_acid; /* Resist acid */
+ bool_ resist_elec; /* Resist lightning */
+ bool_ resist_fire; /* Resist fire */
+ bool_ resist_cold; /* Resist cold */
+ bool_ resist_pois; /* Resist poison */
+
+ bool_ resist_conf; /* Resist confusion */
+ bool_ resist_sound; /* Resist sound */
+ bool_ resist_lite; /* Resist light */
+ bool_ resist_dark; /* Resist darkness */
+ bool_ resist_chaos; /* Resist chaos */
+ bool_ resist_disen; /* Resist disenchant */
+ bool_ resist_shard; /* Resist shards */
+ bool_ resist_nexus; /* Resist nexus */
+ bool_ resist_blind; /* Resist blindness */
+ bool_ resist_neth; /* Resist nether */
+ bool_ resist_fear; /* Resist fear */
+ bool_ resist_continuum; /* Resist space-time continuum disruption */
+
+ bool_ sensible_fire; /* Fire does more damage on the player */
+ bool_ sensible_lite; /* Lite does more damage on the player and blinds her/him */
+
+ bool_ reflect; /* Reflect 'bolt' attacks */
+ bool_ sh_fire; /* Fiery 'immolation' effect */
+ bool_ sh_elec; /* Electric 'immolation' effect */
+ bool_ wraith_form; /* wraithform */
+
+ bool_ anti_magic; /* Anti-magic */
+ bool_ anti_tele; /* Prevent teleportation */
+
+ bool_ sustain_str; /* Keep strength */
+ bool_ sustain_int; /* Keep intelligence */
+ bool_ sustain_wis; /* Keep wisdom */
+ bool_ sustain_dex; /* Keep dexterity */
+ bool_ sustain_con; /* Keep constitution */
+ bool_ sustain_chr; /* Keep charisma */
+
+ bool_ aggravate; /* Aggravate monsters */
+ bool_ teleport; /* Random teleporting */
+
+ bool_ exp_drain; /* Experience draining */
+ byte drain_mana; /* mana draining */
+ byte drain_life; /* hp draining */
+
+ bool_ magical_breath; /* Magical breathing -- can breath anywhere */
+ bool_ water_breath; /* Water breathing -- can breath underwater */
+ bool_ climb; /* Can climb mountains */
+ bool_ fly; /* Can fly over some features */
+ bool_ ffall; /* No damage falling */
+ bool_ lite; /* Permanent light */
+ bool_ free_act; /* Never paralyzed */
+ bool_ see_inv; /* Can see invisible */
+ bool_ regenerate; /* Regenerate hit pts */
+ bool_ hold_life; /* Resist life draining */
+ u32b telepathy; /* Telepathy */
+ bool_ slow_digest; /* Slower digestion */
+ bool_ bless_blade; /* Blessed blade */
+ byte xtra_might; /* Extra might bow */
+ bool_ impact; /* Earthquake blows */
+ bool_ auto_id; /* Auto id items */
+
+ s16b invis; /* Invisibility */
+
+ s16b dis_to_h; /* Known bonus to hit */
+ s16b dis_to_d; /* Known bonus to dam */
+ s16b dis_to_a; /* Known bonus to ac */
+
+ s16b dis_ac; /* Known base ac */
+
+ s16b to_l; /* Bonus to life */
+ s16b to_m; /* Bonus to mana */
+ s16b to_s; /* Bonus to spell */
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to dam */
+ s16b to_h_melee; /* Bonus to hit for melee */
+ s16b to_d_melee; /* Bonus to dam for melee */
+ s16b to_h_ranged; /* Bonus to hit for ranged */
+ s16b to_d_ranged; /* Bonus to dam for ranged */
+ s16b to_a; /* Bonus to ac */
+
+ s16b ac; /* Base ac */
+
+ byte antimagic; /* Power of the anti magic field */
+ byte antimagic_dis; /* Radius of the anti magic field */
+
+ s16b see_infra; /* Infravision range */
+
+ s16b skill_dis; /* Skill: Disarming */
+ s16b skill_dev; /* Skill: Magic Devices */
+ s16b skill_sav; /* Skill: Saving throw */
+ s16b skill_stl; /* Skill: Stealth factor */
+ s16b skill_srh; /* Skill: Searching ability */
+ s16b skill_fos; /* Skill: Searching frequency */
+ s16b skill_thn; /* Skill: To hit (normal) */
+ s16b skill_thb; /* Skill: To hit (shooting) */
+ s16b skill_tht; /* Skill: To hit (throwing) */
+ s16b skill_dig; /* Skill: Digging */
+
+ s16b num_blow; /* Number of blows */
+ s16b num_fire; /* Number of shots */
+ s16b xtra_crit; /* % of increased crits */
+
+ byte throw_mult; /* Multiplier for throw damage */
+
+ byte tval_xtra; /* Correct xtra tval */
+
+ byte tval_ammo; /* Correct ammo tval */
+
+ s16b pspeed; /* Current speed */
+
+ u32b mimic_extra; /* Mimicry powers use that */
+ u32b antimagic_extra; /* Antimagic powers */
+ u32b druid_extra; /* Druid powers */
+ u32b druid_extra2; /* Druid powers */
+ u32b druid_extra3; /* Druid powers */
+ u32b music_extra; /* Music songs */
+ u32b music_extra2; /* Music songs */
+ u32b necro_extra; /* Necro powers */
+ u32b necro_extra2; /* Necro powers */
+
+ u32b race_extra1; /* Variable for race */
+ u32b race_extra2; /* Variable for race */
+ u32b race_extra3; /* Variable for race */
+ u32b race_extra4; /* Variable for race */
+ u32b race_extra5; /* Variable for race */
+ u32b race_extra6; /* Variable for race */
+ u32b race_extra7; /* Variable for race */
+
+ s16b dodge_chance; /* Dodging chance */
+
+ u32b maintain_sum; /* Do we have partial summons */
+
+ byte spellbinder_num; /* Number of spells bound */
+ u32b spellbinder[4]; /* Spell bounds */
+ byte spellbinder_trigger; /* Spellbinder trigger condition */
+
+ cptr mimic_name;
+
+ char tactic; /* from 128-4 extremely coward to */
+ /* 128+4 berserker */
+ char movement; /* base movement way */
+
+ s16b companion_killed; /* Number of companion death */
+
+ bool_ no_mortal; /* Fated to never die by the hand of a mortal being */
+
+ bool_ black_breath; /* The Tolkien's Black Breath */
+
+ bool_ precognition; /* Like the cheat mode */
+
+ /*** Extra flags -- used for lua and easying stuff ***/
+ u32b xtra_f1;
+ u32b xtra_f2;
+ u32b xtra_f3;
+ u32b xtra_f4;
+ u32b xtra_f5;
+ u32b xtra_esp;
+
+ /* Corruptions */
+ bool_ *corruptions;
+
+ /*** Pet commands ***/
+ byte pet_follow_distance; /* Length of the imaginary "leash" for pets */
+ byte pet_open_doors; /* flag - allow pets to open doors */
+ byte pet_pickup_items; /* flag - allow pets to pickup items */
+
+ s16b control; /* Controlled monster */
+ byte control_dir; /* Controlled monster */
+
+ /*** Body changing variables ***/
+ u16b body_monster; /* In which body is the player */
+ bool_ disembodied; /* Is the player in a body ? */
+ byte body_parts[INVEN_TOTAL - INVEN_WIELD]; /* Which body parts does he have ? */
+
+ /* Astral */
+ bool_ astral; /* We started at the bottom ? */
+
+ /* Powers */
+ bool_ *powers; /* Actual powers */
+ bool_ powers_mod[POWER_MAX_INIT]; /* Intrinsinc powers */
+
+ /* Skills */
+ s16b skill_points;
+ s16b skill_last_level; /* Prevents gaining skills by losing level and regaining them */
+ s16b melee_style; /* How are */
+ s16b use_piercing_shots; /* for archery */
+
+ /* Help */
+ help_info help;
+
+ /*** Temporary fields ***/
+
+ bool_ did_nothing; /* True if the last action wasnt a real action */
+ bool_ leaving; /* True if player is leaving */
+};
+
+
+/* For Monk martial arts */
+
+typedef struct martial_arts martial_arts;
+
+struct martial_arts
+{
+ cptr desc; /* A verbose attack description */
+ int min_level; /* Minimum level to use */
+ int chance; /* Chance of 'success' */
+ int dd; /* Damage dice */
+ int ds; /* Damage sides */
+ s16b effect; /* Special effects */
+ s16b power; /* Special effects power */
+};
+
+
+
+/* Powers - used by Mindcrafters and Necromancers */
+typedef struct magic_power magic_power;
+
+struct magic_power
+{
+ int min_lev;
+ int mana_cost;
+ int fail;
+ cptr name;
+ cptr desc;
+};
+
+/* Border */
+typedef struct border_type border_type;
+
+struct border_type
+{
+ byte north[MAX_WID];
+ byte south[MAX_WID];
+ byte east[MAX_HGT];
+ byte west[MAX_HGT];
+ byte north_west;
+ byte north_east;
+ byte south_west;
+ byte south_east;
+};
+
+
+/*
+ * A structure describing a wilderness area
+ * with a terrain, a town or a dungeon entrance
+ */
+typedef struct wilderness_type_info wilderness_type_info;
+
+struct wilderness_type_info
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+ u16b entrance; /* Which town is there(<1000 i's a town, >=1000 it a dungeon) */
+ s32b wild_x; /* Map coordinates (backed out while parsing map) */
+ s32b wild_y;
+ byte road; /* Flags of road */
+ int level; /* Difficulty level */
+ u32b flags1; /* Some flags */
+ byte feat; /* The feature of f_info.txt that is used to allow passing, ... and to get a char/color/graph */
+ byte terrain_idx; /* Terrain index(defined in defines.h) */
+
+ byte terrain[MAX_WILD_TERRAIN];/* Feature types for the plasma generator */
+};
+
+/*
+ * A structure describing a wilderness map
+ */
+typedef struct wilderness_map wilderness_map;
+
+struct wilderness_map
+{
+ int feat; /* Wilderness feature */
+ u32b seed; /* Seed for the RNG */
+ u16b entrance; /* Entrance for dungeons */
+
+ bool_ known; /* Is it seen by the player ? */
+};
+
+/*
+ * A structure describing a town with
+ * stores and buildings
+ */
+typedef struct town_type town_type;
+struct town_type
+{
+ cptr name;
+ u32b seed; /* Seed for RNG */
+ store_type *store; /* The stores [max_st_idx] */
+ byte numstores;
+
+ byte flags; /* Town flags */
+ /* Left this for the sake of compatibility */
+ bool_ stocked; /* Is the town actualy stocked ? */
+
+ bool_ destroyed; /* Is the town destroyed? */
+};
+
+
+/* Alchemists */
+
+typedef struct tval_desc2
+{
+ int tval;
+ cptr desc;
+} tval_desc2;
+
+typedef struct alchemist_recipe alchemist_recipe;
+struct alchemist_recipe
+{
+ int sval_essence;
+ byte tval;
+ byte sval;
+ byte qty;
+};
+
+typedef struct artifact_select_flag artifact_select_flag;
+struct artifact_select_flag {
+ byte group; /* Flag group to display it in */
+ int flag; /* item flag to set */
+ byte level; /* Player skill level to start at */
+ int desc; /* Display this description to select flag */
+ u32b xp; /* xp cost for this flag */
+ bool_ pval; /* indicates this flag benifits from pval */
+ int item_desc; /* Description of required item */
+ int item_descp; /* Description of required item */
+ byte rtval; /* Required items' tval */
+ byte rsval; /* Required items' sval */
+ int rpval; /* Required items' pval (zero for no req) */
+ int rflag[6]; /* Monster Race flags for required Corpses */
+};
+
+/*
+ A structure for deity information.
+ */
+typedef struct deity_type deity_type;
+struct deity_type
+{
+ cptr name;
+ char desc[10][80];
+};
+
+/* A structure for tactics */
+typedef struct tactic_info_type tactic_info_type;
+
+struct tactic_info_type
+{
+ s16b to_hit;
+ s16b to_dam;
+ s16b to_ac;
+ s16b to_stealth;
+ s16b to_disarm;
+ s16b to_saving;
+ cptr name;
+};
+
+/* A structure to describe a random artifact. */
+typedef struct random_artifact random_artifact;
+
+struct random_artifact
+{
+ char name_full[80]; /* Full name for the artifact */
+ char name_short[80]; /* Un-Id'd name */
+ byte level; /* Level of the artifact */
+ byte attr; /* Color that is used on the screen */
+ u32b cost; /* Object's value */
+ byte activation; /* Activation. */
+ s16b timeout; /* Timeout. */
+ byte generated; /* Does it exist already? */
+};
+
+/* A structure to describe an activation. */
+typedef struct activation activation;
+
+struct activation
+{
+ char desc[80]; /* Desc of the activation */
+ u32b cost; /* costs value */
+ s16b spell; /* Spell. */
+};
+
+/* A structure to describe a music. */
+typedef struct music music;
+
+struct music
+{
+ char desc[80]; /* Desc of the music */
+ s16b music; /* Music. */
+ s16b dur; /* Duration(if any) */
+ s16b init_recharge; /* Minimal recharge time */
+ s16b turn_recharge; /* Recharge time for each more turn */
+ byte min_inst; /* Minimum instrument for the music */
+ byte rarity; /* Rarity of the music(use 100 to unallow to be randomly generated) */
+};
+
+/* A structure to describe the random spells of the Power Mages */
+typedef struct random_spell random_spell;
+
+struct random_spell
+{
+ char desc[30]; /* Desc of the spell */
+ char name[30]; /* Name of the spell */
+ s16b mana; /* Mana cost */
+ s16b fail; /* Failure rate */
+ u32b proj_flags; /* Project function flags */
+ byte GF; /* Type of the projection */
+ byte radius;
+ byte dam_sides;
+ byte dam_dice;
+ byte level; /* Level needed */
+ bool_ untried; /* Is the spell was tried? */
+};
+
+/* A structure to describe the fate of the player */
+typedef struct fate fate;
+
+struct fate
+{
+ byte fate; /* Which fate */
+ byte level; /* On which level */
+ byte serious; /* Is it sure? */
+ s16b o_idx; /* Object to find */
+ s16b e_idx; /* Ego-Item to find */
+ s16b a_idx; /* Artifact to find */
+ s16b v_idx; /* Vault to find */
+ s16b r_idx; /* Monster to find */
+ s16b count; /* Number of things */
+ s16b time; /* Turn before */
+ bool_ know; /* Has it been predicted? */
+ bool_ icky; /* Hackish runtime-only flag */
+};
+
+/* A structure for movements */
+typedef struct move_info_type move_info_type;
+
+struct move_info_type
+{
+ s16b to_speed;
+ s16b to_search;
+ s16b to_stealth;
+ s16b to_percep;
+ cptr name;
+};
+
+/* Define monster generation rules */
+typedef struct rule_type rule_type;
+struct rule_type
+{
+ byte mode; /* Mode of combination of the monster flags */
+ byte percent; /* Percent of monsters affected by the rule */
+
+ u32b mflags1; /* The monster flags that are allowed */
+ u32b mflags2;
+ u32b mflags3;
+ u32b mflags4;
+ u32b mflags5;
+ u32b mflags6;
+ u32b mflags7;
+ u32b mflags8;
+ u32b mflags9;
+
+ char r_char[5]; /* Monster race allowed */
+};
+
+/* A structure for the != dungeon types */
+typedef struct dungeon_info_type dungeon_info_type;
+struct dungeon_info_type
+{
+ u32b name; /* Name */
+ u32b text; /* Description */
+ char short_name[3]; /* Short name */
+
+ char generator[30]; /* Name of the level generator */
+
+ s16b floor1; /* Floor tile 1 */
+ byte floor_percent1[2]; /* Chance of type 1 */
+ s16b floor2; /* Floor tile 2 */
+ byte floor_percent2[2]; /* Chance of type 2 */
+ s16b floor3; /* Floor tile 3 */
+ byte floor_percent3[2]; /* Chance of type 3 */
+ s16b outer_wall; /* Outer wall tile */
+ s16b inner_wall; /* Inner wall tile */
+ s16b fill_type1; /* Cave tile 1 */
+ byte fill_percent1[2]; /* Chance of type 1 */
+ s16b fill_type2; /* Cave tile 2 */
+ byte fill_percent2[2]; /* Chance of type 2 */
+ s16b fill_type3; /* Cave tile 3 */
+ byte fill_percent3[2]; /* Chance of type 3 */
+ byte fill_method; /* Smoothing parameter for the above */
+
+ s16b mindepth; /* Minimal depth */
+ s16b maxdepth; /* Maximal depth */
+
+ bool_ principal; /* If it's a part of the main dungeon */
+ byte next; /* The next part of the main dungeon */
+ byte min_plev; /* Minimal plev needed to enter -- it's an anti-cheating mesure */
+
+ int min_m_alloc_level; /* Minimal number of monsters per level */
+ int max_m_alloc_chance; /* There is a 1/max_m_alloc_chance chance per round of creating a new monster */
+
+ u32b flags1; /* Flags 1 */
+ u32b flags2; /* Flags 1 */
+
+ int size_x, size_y; /* Desired numers of panels */
+
+ byte rule_percents[100]; /* Flat rule percents */
+ rule_type rules[5]; /* Monster generation rules */
+
+ int final_object; /* The object you'll find at the bottom */
+ int final_artifact; /* The artifact you'll find at the bottom */
+ int final_guardian; /* The artifact's guardian. If an artifact is specified, then it's NEEDED */
+
+ int ix, iy, ox, oy; /* Wilderness coordinates of the entrance/output of the dungeon */
+
+ obj_theme objs; /* The drops type */
+
+ int d_dice[4]; /* Number of dices */
+ int d_side[4]; /* Number of sides */
+ int d_frequency[4]; /* Frequency of damage (1 is the minimum) */
+ int d_type[4]; /* Type of damage */
+
+ s16b t_idx[TOWN_DUNGEON]; /* The towns */
+ s16b t_level[TOWN_DUNGEON]; /* The towns levels */
+ s16b t_num; /* Number of towns */
+};
+
+/* A structure for inscriptions */
+typedef struct inscription_info_type inscription_info_type;
+struct inscription_info_type
+{
+ char text[40]; /* The inscription itself */
+ byte when; /* When it is executed */
+ bool_ know; /* Is the inscription know ? */
+ byte mana; /* Grid mana needed */
+};
+
+/* To hold Runecrafters prefered spells */
+typedef struct rune_spell rune_spell;
+struct rune_spell
+{
+ char name[30]; /* name */
+
+ s16b type; /* Type of the spell(GF) */
+ s16b rune2; /* Modifiers */
+ s16b mana; /* Mana involved */
+};
+
+/* For level gaining artifacts, artifact creation, ... */
+typedef struct flags_group flags_group;
+struct flags_group
+{
+ char name[30]; /* Name */
+ byte color; /* Color */
+
+ byte price; /* Price to "buy" it */
+
+ u32b flags1; /* Flags set 1 */
+ u32b flags2; /* Flags set 2 */
+ u32b flags3; /* Flags set 3 */
+ u32b flags4; /* Flags set 4 */
+ u32b esp; /* ESP flags set */
+};
+
+/* For powers(racial, class, mutation, artifacts, ... */
+typedef struct power_type power_type;
+struct power_type
+{
+ char *name; /* Name */
+ char *desc_text; /* Text describing power */
+ char *gain_text; /* Text displayed on gaining the power */
+ char *lose_text; /* Text displayed on losing the power */
+
+ byte level; /* Min level */
+ byte cost; /* Mana/Life cost */
+ byte stat; /* Stat used */
+ byte diff; /* Difficulty */
+};
+
+/* Hooks */
+typedef bool_ (*hook_type)(char *fmt);
+
+/*
+ * Structure for the "quests"
+ */
+typedef struct quest_type quest_type;
+
+struct quest_type
+{
+ bool_ silent;
+
+ bool_ dynamic_desc; /* Do we need to ask a function to get the description ? */
+
+ char name[40]; /* Quest name */
+
+ char desc[10][80]; /* Quest desc */
+
+ s16b status; /* Is the quest taken, completed, finished? */
+
+ s16b level; /* Dungeon level */
+
+ s16b *plot; /* Which plot does it belongs to? */
+
+ byte type; /* Lua or C ? */
+
+ bool_ (*init)(int q); /* Function that takes care of generating hardcoded quests */
+
+ s32b data[4]; /* Various datas used by the quests */
+
+ bool_ (*gen_desc)(FILE *fff); /* Function for generating description. */
+};
+typedef struct random_quest random_quest;
+struct random_quest
+{
+ byte type; /* Type/number of monsters to kill(0 = no quest) */
+ s16b r_idx; /* Monsters to crush */
+ bool_ done; /* Done ? */
+};
+
+/* Monster powers for player uses */
+typedef struct monster_power monster_power;
+struct monster_power
+{
+ u32b power; /* Power RF?_xxx */
+ cptr name; /* Name of it */
+ int mana; /* Mana needed */
+ bool_ great; /* Need the use of great spells */
+};
+
+/* Tval descs */
+typedef struct tval_desc tval_desc;
+struct tval_desc
+{
+ int tval; /* tval */
+ cptr desc; /* desc */
+};
+
+/*
+ * Between exit
+ */
+typedef struct between_exit between_exit;
+struct between_exit
+{
+ s16b corresp; /* Corresponding between gate */
+ bool_ dungeon; /* Do we exit in a dungeon or in the wild ? */
+
+ s16b wild_x, wild_y; /* Wilderness spot to land onto */
+ s16b px, py; /* Location of the map */
+
+ s16b d_idx; /* Dungeon to land onto */
+ s16b level;
+};
+
+/*
+ * A structure to hold "rolled" information
+ */
+typedef struct birther birther;
+struct birther
+{
+ s16b sex;
+ s16b race;
+ s16b rmod;
+ s16b pclass;
+ s16b spec;
+
+ byte quests;
+
+ byte god;
+ s32b grace;
+ s32b god_favor;
+
+ s16b age;
+ s16b wt;
+ s16b ht;
+ s16b sc;
+
+ s32b au;
+
+ s16b stat[6];
+ s16b luck;
+
+ s16b chaos_patron;
+
+ u32b weapon;
+
+ char history[4][60];
+
+ bool_ quick_ok;
+};
+
+typedef struct hooks_chain hooks_chain;
+struct hooks_chain
+{
+ hook_type hook;
+ char name[40];
+ char script[40];
+ byte type;
+ hooks_chain *next;
+};
+
+typedef union hook_return hook_return;
+union hook_return
+{
+ s32b num;
+ cptr str;
+ object_type *o_ptr;
+ monster_type *m_ptr;
+};
+
+/*
+ * Forward declare
+ */
+typedef struct hist_type hist_type;
+
+/*
+ * Player background information
+ */
+struct hist_type
+{
+ s32b info; /* Textual History -- uses rp_text */
+
+ byte roll; /* Frequency of this entry */
+ s16b chart; /* Chart index */
+ s16b next; /* Next chart index */
+ byte bonus; /* Social Class Bonus + 50 */
+};
+
+/*
+ * Item sets
+ */
+typedef struct set_type set_type;
+struct set_type
+{
+ u32b name; /* Name */
+ u32b desc; /* Desc */
+
+ byte num; /* Number of artifacts used */
+ byte num_use; /* Number actually wore */
+ struct /* the various items */
+ {
+ bool_ present; /* Is it actually wore ? */
+ s16b a_idx; /* What artifact ? */
+ s16b pval[6]; /* Pval for each combination */
+ u32b flags1[6]; /* Flags */
+ u32b flags2[6]; /* Flags */
+ u32b flags3[6]; /* Flags */
+ u32b flags4[6]; /* Flags */
+ u32b flags5[6]; /* Flags */
+ u32b esp[6]; /* Flags */
+ } arts[6];
+};
+
+/* A structure for CLI commands. */
+typedef struct cli_comm cli_comm;
+struct cli_comm
+{
+ cptr comm; /* Extended name of the command. */
+ cptr descrip; /* Description of the command. */
+ s16b key; /* Key to convert command to. */
+};
+
+/*
+ * Skills !
+ */
+typedef struct skill_type skill_type;
+struct skill_type
+{
+ u32b name; /* Name */
+ u32b desc; /* Description */
+ u32b action_desc; /* Action Description */
+
+ s16b action_mkey; /* Action do to */
+
+ s32b i_value; /* Actual value */
+ s32b i_mod; /* Modifier(1 skill point = modifier skill) */
+
+ s32b value; /* Actual value */
+ s32b mod; /* Modifier(1 skill point = modifier skill) */
+ s16b rate; /* Modifier decreasing rate */
+
+ u32b uses; /* Number of times used */
+
+ s16b action[MAX_SKILLS]; /* List of actions against other skills */
+
+ s16b father; /* Father in the skill tree */
+ bool_ dev; /* Is the branch developped ? */
+ s16b order; /* Order in the tree */
+ bool_ hidden; /* Innactive */
+
+ byte random_gain_chance; /* random gain chance, still needs the flag */
+
+ u32b flags1; /* Skill flags */
+};
+
+
+/*
+ * The spell function must provide the desc
+ */
+typedef struct spell_type spell_type;
+struct spell_type
+{
+ cptr name; /* Name */
+ byte skill_level; /* Required level (to learn) */
+ byte mana; /* Required mana at lvl 1 */
+ byte mana_max; /* Required mana at max lvl */
+ s16b fail; /* Minimum chance of failure */
+ s16b level; /* Spell level(0 = not learnt) */
+};
+
+typedef struct school_type school_type;
+struct school_type
+{
+ cptr name; /* Name */
+ s16b skill; /* Skill used for that school */
+};
+
+/*
+ * Desc for GF_FOO
+ */
+typedef struct gf_name_type gf_name_type;
+struct gf_name_type
+{
+ int gf;
+ cptr name;
+};
+
+/*
+ * Timers
+ */
+typedef struct timer_type timer_type;
+struct timer_type
+{
+ timer_type *next; /* The next timer in the list */
+
+ bool_ enabled; /* Is it currently counting? */
+
+ s32b delay; /* Delay between activations */
+ s32b countdown; /* The current number of turns passed, when it reaches delay it fires */
+
+ cptr callback; /* The lua function to call upon firing(no C callback yet .. maybe) */
+};
+
+/*
+ * This is for lua functions that need to pass table to c functions
+ */
+typedef struct list_type list_type;
+struct list_type
+{
+ cptr *list;
+};
+
+/*
+ * Abilities
+ */
+typedef struct ability_type ability_type;
+struct ability_type
+{
+ u32b name; /* Name */
+ u32b desc; /* Description */
+ u32b action_desc; /* Action Description */
+
+ s16b action_mkey; /* Action do to */
+
+ s16b cost; /* Skill points cost */
+
+ bool_ acquired; /* Do the player actualylg ot it ? */
+
+ /* Prereqs */
+ s16b skills[10]; /* List of prereq skills(10 max) */
+ s16b skill_levels[10]; /* List of prereq skills(10 max) */
+ s16b stat[6]; /* List of prereq stats */
+ s16b need_abilities[10]; /* List of prereq abilities(10 max) */
+ s16b forbid_abilities[10]; /* List of forbidden abilities(10 max) */
+};
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 00000000..93e38e4a
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,4479 @@
+/* File: util.c */
+
+/* Purpose: Angband utilities -BEN- */
+
+
+#include "angband.h"
+
+
+
+
+#ifndef HAS_MEMSET
+
+/*
+* For those systems that don't have "memset()"
+*
+* Set the value of each of 'n' bytes starting at 's' to 'c', return 's'
+* If 'n' is negative, you will erase a whole lot of memory.
+*/
+char *memset(char *s, int c, huge n)
+{
+ char *t;
+ for (t = s; len--; ) *t++ = c;
+ return (s);
+}
+
+#endif
+
+
+
+#ifndef HAS_STRICMP
+
+/*
+* For those systems that don't have "stricmp()"
+*
+* Compare the two strings "a" and "b" ala "strcmp()" ignoring case.
+*/
+int stricmp(cptr a, cptr b)
+{
+ cptr s1, s2;
+ char z1, z2;
+
+ /* Scan the strings */
+ for (s1 = a, s2 = b; TRUE; s1++, s2++)
+ {
+ z1 = FORCEUPPER(*s1);
+ z2 = FORCEUPPER(*s2);
+ if (z1 < z2) return ( -1);
+ if (z1 > z2) return (1);
+ if (!z1) return (0);
+ }
+}
+
+#endif
+
+
+#ifdef SET_UID
+
+# ifndef HAS_USLEEP
+
+/*
+* For those systems that don't have "usleep()" but need it.
+*
+* Fake "usleep()" function grabbed from the inl netrek server -cba
+*/
+int usleep(huge usecs)
+{
+ struct timeval Timer;
+
+ int nfds = 0;
+
+#ifdef FD_SET
+ fd_set *no_fds = NULL;
+#else
+int *no_fds = NULL;
+#endif
+
+
+ /* Was: int readfds, writefds, exceptfds; */
+ /* Was: readfds = writefds = exceptfds = 0; */
+
+
+ /* Paranoia -- No excessive sleeping */
+ if (usecs > 4000000L) core("Illegal usleep() call");
+
+
+ /* Wait for it */
+ Timer.tv_sec = (usecs / 1000000L);
+ Timer.tv_usec = (usecs % 1000000L);
+
+ /* Wait for it */
+ if (select(nfds, no_fds, no_fds, no_fds, &Timer) < 0)
+ {
+ /* Hack -- ignore interrupts */
+ if (errno != EINTR) return -1;
+ }
+
+ /* Success */
+ return 0;
+}
+
+# endif
+
+
+/*
+* Hack -- External functions
+*/
+extern struct passwd *getpwuid();
+extern struct passwd *getpwnam();
+
+
+/*
+* Find a default user name from the system.
+*/
+void user_name(char *buf, int id)
+{
+#ifdef SET_UID
+ struct passwd *pw;
+
+ /* Look up the user name */
+ if ((pw = getpwuid(id)))
+ {
+ (void)strcpy(buf, pw->pw_name);
+ buf[16] = '\0';
+
+ return;
+ }
+#endif /* SET_UID */
+
+ /* Oops. Hack -- default to "PLAYER" */
+ strcpy(buf, "PLAYER");
+}
+
+#endif /* SET_UID */
+
+
+
+
+/*
+* The concept of the "file" routines below (and elsewhere) is that all
+* file handling should be done using as few routines as possible, since
+* every machine is slightly different, but these routines always have the
+* same semantics.
+*
+* In fact, perhaps we should use the "path_parse()" routine below to convert
+* from "canonical" filenames (optional leading tilde's, internal wildcards,
+* slash as the path seperator, etc) to "system" filenames (no special symbols,
+* system-specific path seperator, etc). This would allow the program itself
+* to assume that all filenames are "Unix" filenames, and explicitly "extract"
+* such filenames if needed (by "path_parse()", or perhaps "path_canon()").
+*
+* Note that "path_temp" should probably return a "canonical" filename.
+*
+* Note that "my_fopen()" and "my_open()" and "my_make()" and "my_kill()"
+* and "my_move()" and "my_copy()" should all take "canonical" filenames.
+*
+* Note that "canonical" filenames use a leading "slash" to indicate an absolute
+* path, and a leading "tilde" to indicate a special directory, and default to a
+* relative path, but MSDOS uses a leading "drivename plus colon" to indicate the
+* use of a "special drive", and then the rest of the path is parsed "normally",
+* and MACINTOSH uses a leading colon to indicate a relative path, and an embedded
+* colon to indicate a "drive plus absolute path", and finally defaults to a file
+* in the current working directory, which may or may not be defined.
+*
+* We should probably parse a leading "~~/" as referring to "ANGBAND_DIR". (?)
+*/
+
+
+#ifdef SET_UID
+
+/*
+* Extract a "parsed" path from an initial filename
+* Normally, we simply copy the filename into the buffer
+* But leading tilde symbols must be handled in a special way
+* Replace "~/" by the home directory of the current user
+*/
+errr path_parse(char *buf, int max, cptr file)
+{
+ cptr u, s;
+ struct passwd *pw;
+
+
+ /* Assume no result */
+ buf[0] = '\0';
+
+ /* No file? */
+ if (!file) return ( -1);
+
+ /* File needs no parsing */
+ if (file[0] != '~')
+ {
+ strcpy(buf, file);
+ return (0);
+ }
+
+ /* Point at the user */
+ u = file + 1;
+
+ /* Look for non-user portion of the file */
+ s = strstr(u, PATH_SEP);
+
+#ifdef GETLOGIN_BROKEN
+ /* Ask the environment for the home directory */
+ u = getenv("HOME");
+
+ if (!u) return (1);
+
+ (void)strcpy(buf, u);
+#else
+ /* Look up password data for user */
+ pw = getpwuid(getuid());
+
+ /* Nothing found? */
+ if (!pw) return (1);
+
+ /* Make use of the info */
+ (void)strcpy(buf, pw->pw_dir);
+#endif
+
+ /* Append the rest of the filename, if any */
+ if (s) (void)strcat(buf, s);
+
+ /* Success */
+ return (0);
+}
+
+
+#else /* SET_UID */
+
+
+/*
+* Extract a "parsed" path from an initial filename
+*
+* This requires no special processing on simple machines,
+* except for verifying the size of the filename.
+*/
+errr path_parse(char *buf, int max, cptr file)
+{
+ /* Accept the filename */
+ strnfmt(buf, max, "%s", file);
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* SET_UID */
+
+
+/*
+* Hack -- acquire a "temporary" file name if possible
+*
+* This filename is always in "system-specific" form.
+*/
+errr path_temp(char *buf, int max)
+{
+#ifdef WINDOWS
+ static u32b tmp_counter;
+ static char valid_characters[] =
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char rand_ext[4];
+
+ rand_ext[0] = valid_characters[rand_int(sizeof (valid_characters))];
+ rand_ext[1] = valid_characters[rand_int(sizeof (valid_characters))];
+ rand_ext[2] = valid_characters[rand_int(sizeof (valid_characters))];
+ rand_ext[3] = '\0';
+ strnfmt(buf, max, "%s/t_%ud.%s", ANGBAND_DIR_XTRA, tmp_counter, rand_ext);
+ tmp_counter++;
+#else
+ cptr s;
+
+ /* Temp file */
+ s = tmpnam(NULL);
+
+ /* Oops */
+ if (!s) return ( -1);
+
+ /* Format to length */
+ strnfmt(buf, max, "%s", s);
+#endif
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Create a new path by appending a file (or directory) to a path
+*
+* This requires no special processing on simple machines, except
+* for verifying the size of the filename, but note the ability to
+* bypass the given "path" with certain special file-names.
+*
+* Note that the "file" may actually be a "sub-path", including
+* a path and a file.
+*
+* Note that this function yields a path which must be "parsed"
+* using the "parse" function above.
+*/
+errr path_build(char *buf, int max, cptr path, cptr file)
+{
+ /* Special file */
+ if (file[0] == '~')
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Absolute file, on "normal" systems */
+ else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, ""))
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* No path given */
+ else if (!path[0])
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Path and File */
+ else
+ {
+ /* Build the new path */
+ strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- replacement for "fopen()"
+*/
+FILE *my_fopen(cptr file, cptr mode)
+{
+#ifndef MACH_O_CARBON
+
+ char buf[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return (NULL);
+
+ /* Attempt to fopen the file anyway */
+ return (fopen(buf, mode));
+
+#else /* MACH_O_CARBON */
+
+char buf[1024];
+FILE *s;
+
+/* Hack -- Try to parse the path */
+if (path_parse(buf, 1024, file)) return (NULL);
+
+/* Attempt to fopen the file anyway */
+s = fopen(buf, mode);
+
+/* Set creator and type if the file is successfully opened */
+if (s) fsetfileinfo(buf, _fcreator, _ftype);
+
+/* Done */
+return (s);
+
+#endif /* MACH_O_CARBON */
+}
+
+
+/*
+* Hack -- replacement for "fclose()"
+*/
+errr my_fclose(FILE *fff)
+{
+ /* Require a file */
+ if (!fff) return ( -1);
+
+ /* Close, check for error */
+ if (fclose(fff) == EOF) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- replacement for "fgets()"
+*
+* Read a string, without a newline, to a file
+*
+* Process tabs, strip internal non-printables
+*/
+errr my_fgets(FILE *fff, char *buf, huge n)
+{
+ huge i = 0;
+
+ while (TRUE)
+ {
+ int c = fgetc(fff);
+
+ if (c == EOF)
+ {
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success (0) if some characters were read */
+ return (i == 0);
+ }
+
+ /* Handle newline -- DOS (\015\012), Mac (\015), UNIX (\012) */
+ else if (c == '\r')
+ {
+ c = fgetc(fff);
+ if (c != '\n') ungetc(c, fff);
+
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success */
+ return (0);
+ }
+ else if (c == '\n')
+ {
+ c = fgetc(fff);
+ if (c != '\r') ungetc(c, fff);
+
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success */
+ return (0);
+ }
+
+ /* Handle tabs */
+ else if (c == '\t')
+ {
+ /* Hack -- require room */
+ if (i + 8 >= n) break;
+
+ /* Append 1-8 spaces */
+ do { buf[i++] = ' '; } while (i % 8);
+ }
+
+ /* Handle printables */
+ else if (isprint(c))
+ {
+ /* Copy */
+ buf[i++] = c;
+
+ /* Check length */
+ if (i >= n) break;
+ }
+ }
+
+ /* Nothing */
+ buf[0] = '\0';
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+* Hack -- replacement for "fputs()"
+*
+* Dump a string, plus a newline, to a file
+*
+* XXX XXX XXX Process internal weirdness?
+*/
+errr my_fputs(FILE *fff, cptr buf, huge n)
+{
+ /* XXX XXX */
+ n = n ? n : 0;
+
+ /* Dump, ignore errors */
+ (void)fprintf(fff, "%s\n", buf);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Code Warrior is a little weird about some functions
+*/
+#ifdef BEN_HACK
+extern int open(const char *, int, ...);
+extern int close(int);
+extern int read(int, void *, unsigned int);
+extern int write(int, const void *, unsigned int);
+extern long lseek(int, long, int);
+#endif /* BEN_HACK */
+
+
+/*
+* The Macintosh is a little bit brain-dead sometimes
+*/
+#ifdef MACINTOSH
+# define open(N, F, M) \
+((M), open((char*)(N), F))
+# define write(F, B, S) \
+write(F, (char*)(B), S)
+#endif /* MACINTOSH */
+
+
+/*
+* Several systems have no "O_BINARY" flag
+*/
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif /* O_BINARY */
+
+
+/*
+* Hack -- attempt to delete a file
+*/
+errr fd_kill(cptr file)
+{
+ char buf[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+ /* Remove */
+ (void)remove(buf);
+
+ /* XXX XXX XXX */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to move a file
+*/
+errr fd_move(cptr file, cptr what)
+{
+ char buf[1024];
+ char aux[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(aux, 1024, what)) return ( -1);
+
+ /* Rename */
+ (void)rename(buf, aux);
+
+ /* XXX XXX XXX */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to copy a file
+*/
+errr fd_copy(cptr file, cptr what)
+{
+ char buf[1024];
+ char aux[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(aux, 1024, what)) return ( -1);
+
+ /* Copy XXX XXX XXX */
+ /* (void)rename(buf, aux); */
+
+ /* XXX XXX XXX */
+ return (1);
+}
+
+
+/*
+* Hack -- attempt to open a file descriptor (create file)
+*
+* This function should fail if the file already exists
+*
+* Note that we assume that the file should be "binary"
+*
+* XXX XXX XXX The horrible "BEN_HACK" code is for compiling under
+* the CodeWarrior compiler, in which case, for some reason, none
+* of the "O_*" flags are defined, and we must fake the definition
+* of "O_RDONLY", "O_WRONLY", and "O_RDWR" in "A-win-h", and then
+* we must simulate the effect of the proper "open()" call below.
+*/
+int fd_make(cptr file, int mode)
+{
+ char buf[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+#ifdef BEN_HACK
+
+ /* Check for existance */
+ /* if (fd_close(fd_open(file, O_RDONLY | O_BINARY))) return (1); */
+
+ /* Mega-Hack -- Create the file */
+ (void)my_fclose(my_fopen(file, "wb"));
+
+ /* Re-open the file for writing */
+ return (open(buf, O_WRONLY | O_BINARY, mode));
+
+#else /* BEN_HACK */
+
+#ifdef MACH_O_CARBON
+
+{
+int fdes;
+
+/* Create the file, fail if exists, write-only, binary */
+fdes = open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode);
+
+/* Set creator and type if the file is successfully opened */
+if (fdes >= 0) fsetfileinfo(buf, _fcreator, _ftype);
+
+/* Return the descriptor */
+return (fdes);
+}
+
+#else /* MACH_O_CARBON */
+
+/* Create the file, fail if exists, write-only, binary */
+return (open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode));
+
+#endif /* MACH_O_CARBON */
+
+#endif /* BEN_HACK */
+
+}
+
+
+/*
+* Hack -- attempt to open a file descriptor (existing file)
+*
+* Note that we assume that the file should be "binary"
+*/
+int fd_open(cptr file, int flags)
+{
+ char buf[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+#ifdef MACH_O_CARBON
+
+ {
+ int fdes;
+
+ /* Attempt to open the file */
+ fdes = open(buf, flags | O_BINARY, 0);
+
+ /* Set creator and type if the file is successfully opened */
+ if (fdes >= 0) fsetfileinfo(buf, _fcreator, _ftype);
+
+ /* Return the descriptor */
+ return (fdes);
+ }
+
+#else /* MACH_O_CARBON */
+
+/* Attempt to open the file */
+return (open(buf, flags | O_BINARY, 0));
+
+#endif /* MACH_O_CARBON */
+}
+
+
+/*
+* Hack -- attempt to lock a file descriptor
+*
+* Legal lock types -- F_UNLCK, F_RDLCK, F_WRLCK
+*/
+errr fd_lock(int fd, int what)
+{
+ /* XXX XXX */
+ what = what ? what : 0;
+
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+#ifdef SET_UID
+
+# ifdef USG
+
+# if defined(F_ULOCK) && defined(F_LOCK)
+
+ /* Un-Lock */
+ if (what == F_UNLCK)
+ {
+ /* Unlock it, Ignore errors */
+ lockf(fd, F_ULOCK, 0);
+ }
+
+ /* Lock */
+ else
+ {
+ /* Lock the score file */
+ if (lockf(fd, F_LOCK, 0) != 0) return (1);
+ }
+
+# endif
+
+# else
+
+# if defined(LOCK_UN) && defined(LOCK_EX)
+
+ /* Un-Lock */
+ if (what == F_UNLCK)
+ {
+ /* Unlock it, Ignore errors */
+ (void)flock(fd, LOCK_UN);
+ }
+
+ /* Lock */
+ else
+ {
+ /* Lock the score file */
+ if (flock(fd, LOCK_EX) != 0) return (1);
+ }
+
+# endif
+
+# endif
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to seek on a file descriptor
+*/
+errr fd_seek(int fd, huge n)
+{
+ s32b p;
+
+ /* Verify fd */
+ if (fd < 0) return ( -1);
+
+ /* Seek to the given position */
+ p = lseek(fd, n, SEEK_SET);
+
+ /* Failure */
+ if (p < 0) return (1);
+
+ /* Failure */
+ if ((huge)p != n) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to read data from a file descriptor
+*/
+errr fd_read(int fd, char *buf, huge n)
+{
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+#ifndef SET_UID
+
+ /* Read pieces */
+ while (n >= 16384)
+ {
+ /* Read a piece */
+ if (read(fd, buf, 16384) != 16384) return (1);
+
+ /* Shorten the task */
+ buf += 16384;
+
+ /* Shorten the task */
+ n -= 16384;
+ }
+
+#endif
+
+ /* Read the final piece */
+ if ((huge)read(fd, buf, n) != n) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- Attempt to write data to a file descriptor
+*/
+errr fd_write(int fd, cptr buf, huge n)
+{
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+#ifndef SET_UID
+
+ /* Write pieces */
+ while (n >= 16384)
+ {
+ /* Write a piece */
+ if (write(fd, buf, 16384) != 16384) return (1);
+
+ /* Shorten the task */
+ buf += 16384;
+
+ /* Shorten the task */
+ n -= 16384;
+ }
+
+#endif
+
+ /* Write the final piece */
+ if ((huge)write(fd, buf, n) != n) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to close a file descriptor
+*/
+errr fd_close(int fd)
+{
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+ /* Close */
+ (void)close(fd);
+
+ /* XXX XXX XXX */
+ return (0);
+}
+
+
+/*
+* XXX XXX XXX Important note about "colors" XXX XXX XXX
+*
+* The "TERM_*" color definitions list the "composition" of each
+* "Angband color" in terms of "quarters" of each of the three color
+* components (Red, Green, Blue), for example, TERM_UMBER is defined
+* as 2/4 Red, 1/4 Green, 0/4 Blue.
+*
+* The following info is from "Torbjorn Lindgren" (see "main-xaw.c").
+*
+* These values are NOT gamma-corrected. On most machines (with the
+* Macintosh being an important exception), you must "gamma-correct"
+* the given values, that is, "correct for the intrinsic non-linearity
+* of the phosphor", by converting the given intensity levels based
+* on the "gamma" of the target screen, which is usually 1.7 (or 1.5).
+*
+* The actual formula for conversion is unknown to me at this time,
+* but you can use the table below for the most common gamma values.
+*
+* So, on most machines, simply convert the values based on the "gamma"
+* of the target screen, which is usually in the range 1.5 to 1.7, and
+* usually is closest to 1.7. The converted value for each of the five
+* different "quarter" values is given below:
+*
+* Given Gamma 1.0 Gamma 1.5 Gamma 1.7 Hex 1.7
+* ----- ---- ---- ---- ---
+* 0/4 0.00 0.00 0.00 #00
+* 1/4 0.25 0.27 0.28 #47
+* 2/4 0.50 0.55 0.56 #8f
+* 3/4 0.75 0.82 0.84 #d7
+* 4/4 1.00 1.00 1.00 #ff
+*
+* Note that some machines (i.e. most IBM machines) are limited to a
+* hard-coded set of colors, and so the information above is useless.
+*
+* Also, some machines are limited to a pre-determined set of colors,
+* for example, the IBM can only display 16 colors, and only 14 of
+* those colors resemble colors used by Angband, and then only when
+* you ignore the fact that "Slate" and "cyan" are not really matches,
+* so on the IBM, we use "orange" for both "Umber", and "Light Umber"
+* in addition to the obvious "Orange", since by combining all of the
+* "indeterminate" colors into a single color, the rest of the colors
+* are left with "meaningful" values.
+*/
+
+
+/*
+* Move the cursor
+*/
+void move_cursor(int row, int col)
+{
+ Term_gotoxy(col, row);
+}
+
+
+
+/*
+* Convert a decimal to a single digit octal number
+*/
+static char octify(uint i)
+{
+ return (hexsym[i % 8]);
+}
+
+/*
+* Convert a decimal to a single digit hex number
+*/
+static char hexify(uint i)
+{
+ return (hexsym[i % 16]);
+}
+
+
+/*
+* Convert a octal-digit into a decimal
+*/
+static int deoct(char c)
+{
+ if (isdigit(c)) return (D2I(c));
+ return (0);
+}
+
+/*
+* Convert a hexidecimal-digit into a decimal
+*/
+static int dehex(char c)
+{
+ if (isdigit(c)) return (D2I(c));
+ if (islower(c)) return (A2I(c) + 10);
+ if (isupper(c)) return (A2I(tolower(c)) + 10);
+ return (0);
+}
+
+
+static int my_stricmp(cptr a, cptr b)
+{
+ cptr s1, s2;
+ char z1, z2;
+
+ /* Scan the strings */
+ for (s1 = a, s2 = b; TRUE; s1++, s2++)
+ {
+ z1 = FORCEUPPER(*s1);
+ z2 = FORCEUPPER(*s2);
+ if (z1 < z2) return ( -1);
+ if (z1 > z2) return (1);
+ if (!z1) return (0);
+ }
+}
+
+static int my_strnicmp(cptr a, cptr b, int n)
+{
+ cptr s1, s2;
+ char z1, z2;
+
+ /* Scan the strings */
+ for (s1 = a, s2 = b; n > 0; s1++, s2++, n--)
+ {
+ z1 = FORCEUPPER(*s1);
+ z2 = FORCEUPPER(*s2);
+ if (z1 < z2) return ( -1);
+ if (z1 > z2) return (1);
+ if (!z1) return (0);
+ }
+ return 0;
+}
+
+
+static void trigger_text_to_ascii(char **bufptr, cptr *strptr)
+{
+ char *s = *bufptr;
+ cptr str = *strptr;
+ bool_ mod_status[MAX_MACRO_MOD];
+
+ int i, len = 0;
+ int shiftstatus = 0;
+ cptr key_code;
+
+ if (macro_template == NULL)
+ return;
+
+ for (i = 0; macro_modifier_chr[i]; i++)
+ mod_status[i] = FALSE;
+ str++;
+
+ /* Examine modifier keys */
+ while (1)
+ {
+ for (i = 0; macro_modifier_chr[i]; i++)
+ {
+ len = strlen(macro_modifier_name[i]);
+
+ if (!my_strnicmp(str, macro_modifier_name[i], len))
+ break;
+ }
+ if (!macro_modifier_chr[i]) break;
+ str += len;
+ mod_status[i] = TRUE;
+ if ('S' == macro_modifier_chr[i])
+ shiftstatus = 1;
+ }
+ for (i = 0; i < max_macrotrigger; i++)
+ {
+ len = strlen(macro_trigger_name[i]);
+ if (!my_strnicmp(str, macro_trigger_name[i], len) && ']' == str[len])
+ {
+ /* a trigger name found */
+ break;
+ }
+ }
+
+ /* Invalid trigger name? */
+ if (i == max_macrotrigger)
+ {
+ str = strchr(str, ']');
+ if (str)
+ {
+ *s++ = (char)31;
+ *s++ = (char)13;
+ *bufptr = s;
+ *strptr = str; /* where **strptr == ']' */
+ }
+ return;
+ }
+
+ key_code = macro_trigger_keycode[shiftstatus][i];
+ str += len;
+
+ *s++ = (char)31;
+ for (i = 0; macro_template[i]; i++)
+ {
+ char ch = macro_template[i];
+ int j;
+
+ switch (ch)
+ {
+ case '&':
+ for (j = 0; macro_modifier_chr[j]; j++)
+ {
+ if (mod_status[j])
+ *s++ = macro_modifier_chr[j];
+ }
+ break;
+ case '#':
+ strcpy(s, key_code);
+ s += strlen(key_code);
+ break;
+ default:
+ *s++ = ch;
+ break;
+ }
+ }
+ *s++ = (char)13;
+
+ *bufptr = s;
+ *strptr = str; /* where **strptr == ']' */
+ return;
+}
+
+
+/*
+* Hack -- convert a printable string into real ascii
+*
+* I have no clue if this function correctly handles, for example,
+* parsing "\xFF" into a (signed) char. Whoever thought of making
+* the "sign" of a "char" undefined is a complete moron. Oh well.
+*/
+void text_to_ascii(char *buf, cptr str)
+{
+ char *s = buf;
+
+ /* Analyze the "ascii" string */
+ while (*str)
+ {
+ /* Backslash codes */
+ if (*str == '\\')
+ {
+ /* Skip the backslash */
+ str++;
+
+ /* Macro Trigger */
+ if (*str == '[')
+ {
+ trigger_text_to_ascii(&s, &str);
+ }
+
+ /* Hex-mode XXX */
+ else if (*str == 'x')
+ {
+ *s = 16 * dehex(*++str);
+ *s++ += dehex(*++str);
+ }
+
+ /* Hack -- simple way to specify "backslash" */
+ else if (*str == '\\')
+ {
+ *s++ = '\\';
+ }
+
+ /* Hack -- simple way to specify "caret" */
+ else if (*str == '^')
+ {
+ *s++ = '^';
+ }
+
+ /* Hack -- simple way to specify "space" */
+ else if (*str == 's')
+ {
+ *s++ = ' ';
+ }
+
+ /* Hack -- simple way to specify Escape */
+ else if (*str == 'e')
+ {
+ *s++ = ESCAPE;
+ }
+
+ /* Backspace */
+ else if (*str == 'b')
+ {
+ *s++ = '\b';
+ }
+
+ /* Newline */
+ else if (*str == 'n')
+ {
+ *s++ = '\n';
+ }
+
+ /* Return */
+ else if (*str == 'r')
+ {
+ *s++ = '\r';
+ }
+
+ /* Tab */
+ else if (*str == 't')
+ {
+ *s++ = '\t';
+ }
+
+ /* Octal-mode */
+ else if (*str == '0')
+ {
+ *s = 8 * deoct(*++str);
+ *s++ += deoct(*++str);
+ }
+
+ /* Octal-mode */
+ else if (*str == '1')
+ {
+ *s = 64 + 8 * deoct(*++str);
+ *s++ += deoct(*++str);
+ }
+
+ /* Octal-mode */
+ else if (*str == '2')
+ {
+ *s = 64 * 2 + 8 * deoct(*++str);
+ *s++ += deoct(*++str);
+ }
+
+ /* Octal-mode */
+ else if (*str == '3')
+ {
+ *s = 64 * 3 + 8 * deoct(*++str);
+ *s++ += deoct(*++str);
+ }
+
+ /* Skip the final char */
+ str++;
+ }
+
+ /* Normal Control codes */
+ else if (*str == '^')
+ {
+ str++;
+ *s++ = (*str++ & 037);
+ }
+
+ /* Normal chars */
+ else
+ {
+ *s++ = *str++;
+ }
+ }
+
+ /* Terminate */
+ *s = '\0';
+}
+
+
+bool_ trigger_ascii_to_text(char **bufptr, cptr *strptr)
+{
+ char *s = *bufptr;
+ cptr str = *strptr;
+ char key_code[100];
+ int i;
+ cptr tmp;
+
+ if (macro_template == NULL)
+ return FALSE;
+
+ *s++ = '\\';
+ *s++ = '[';
+
+ for (i = 0; macro_template[i]; i++)
+ {
+ int j;
+ char ch = macro_template[i];
+
+ switch (ch)
+ {
+ case '&':
+ while ((tmp = strchr(macro_modifier_chr, *str)))
+ {
+ j = (int)(tmp - macro_modifier_chr);
+ tmp = macro_modifier_name[j];
+ while (*tmp) *s++ = *tmp++;
+ str++;
+ }
+ break;
+ case '#':
+ for (j = 0; *str && *str != (char)13; j++)
+ key_code[j] = *str++;
+ key_code[j] = '\0';
+ break;
+ default:
+ if (ch != *str) return FALSE;
+ str++;
+ }
+ }
+ if (*str++ != (char)13) return FALSE;
+
+ for (i = 0; i < max_macrotrigger; i++)
+ {
+ if (!my_stricmp(key_code, macro_trigger_keycode[0][i])
+ || !my_stricmp(key_code, macro_trigger_keycode[1][i]))
+ break;
+ }
+ if (i == max_macrotrigger)
+ return FALSE;
+
+ tmp = macro_trigger_name[i];
+ while (*tmp) *s++ = *tmp++;
+
+ *s++ = ']';
+
+ *bufptr = s;
+ *strptr = str;
+ return TRUE;
+}
+
+
+/*
+* Hack -- convert a string into a printable form
+*/
+void ascii_to_text(char *buf, cptr str)
+{
+ char *s = buf;
+
+ /* Analyze the "ascii" string */
+ while (*str)
+ {
+ byte i = (byte)(*str++);
+
+ /* Macro Trigger */
+ if (i == 31)
+ {
+ if (!trigger_ascii_to_text(&s, &str))
+ {
+ *s++ = '^';
+ *s++ = '_';
+ }
+ }
+ else if (i == ESCAPE)
+ {
+ *s++ = '\\';
+ *s++ = 'e';
+ }
+ else if (i == ' ')
+ {
+ *s++ = '\\';
+ *s++ = 's';
+ }
+ else if (i == '\b')
+ {
+ *s++ = '\\';
+ *s++ = 'b';
+ }
+ else if (i == '\t')
+ {
+ *s++ = '\\';
+ *s++ = 't';
+ }
+ else if (i == '\n')
+ {
+ *s++ = '\\';
+ *s++ = 'n';
+ }
+ else if (i == '\r')
+ {
+ *s++ = '\\';
+ *s++ = 'r';
+ }
+ else if (i == '^')
+ {
+ *s++ = '\\';
+ *s++ = '^';
+ }
+ else if (i == '\\')
+ {
+ *s++ = '\\';
+ *s++ = '\\';
+ }
+ else if (i < 32)
+ {
+ *s++ = '^';
+ *s++ = i + 64;
+ }
+ else if (i < 127)
+ {
+ *s++ = i;
+ }
+ else if (i < 64)
+ {
+ *s++ = '\\';
+ *s++ = '0';
+ *s++ = octify(i / 8);
+ *s++ = octify(i % 8);
+ }
+ else
+ {
+ *s++ = '\\';
+ *s++ = 'x';
+ *s++ = hexify(i / 16);
+ *s++ = hexify(i % 16);
+ }
+ }
+
+ /* Terminate */
+ *s = '\0';
+}
+
+
+
+/*
+* The "macro" package
+*
+* Functions are provided to manipulate a collection of macros, each
+* of which has a trigger pattern string and a resulting action string
+* and a small set of flags.
+*/
+
+
+
+/*
+* Determine if any macros have ever started with a given character.
+*/
+static bool_ macro__use[256];
+
+
+/*
+* Find the macro (if any) which exactly matches the given pattern
+*/
+sint macro_find_exact(cptr pat)
+{
+ int i;
+
+ /* Nothing possible */
+ if (!macro__use[(byte)(pat[0])])
+ {
+ return ( -1);
+ }
+
+ /* Scan the macros */
+ for (i = 0; i < macro__num; ++i)
+ {
+ /* Skip macros which do not match the pattern */
+ if (!streq(macro__pat[i], pat)) continue;
+
+ /* Found one */
+ return (i);
+ }
+
+ /* No matches */
+ return ( -1);
+}
+
+
+/*
+* Find the first macro (if any) which contains the given pattern
+*/
+static sint macro_find_check(cptr pat)
+{
+ int i;
+
+ /* Nothing possible */
+ if (!macro__use[(byte)(pat[0])])
+ {
+ return ( -1);
+ }
+
+ /* Scan the macros */
+ for (i = 0; i < macro__num; ++i)
+ {
+ /* Skip macros which do not contain the pattern */
+ if (!prefix(macro__pat[i], pat)) continue;
+
+ /* Found one */
+ return (i);
+ }
+
+ /* Nothing */
+ return ( -1);
+}
+
+
+/*
+* Find the first macro (if any) which contains the given pattern and more
+*/
+static sint macro_find_maybe(cptr pat)
+{
+ int i;
+
+ /* Nothing possible */
+ if (!macro__use[(byte)(pat[0])])
+ {
+ return ( -1);
+ }
+
+ /* Scan the macros */
+ for (i = 0; i < macro__num; ++i)
+ {
+ /* Skip macros which do not contain the pattern */
+ if (!prefix(macro__pat[i], pat)) continue;
+
+ /* Skip macros which exactly match the pattern XXX XXX */
+ if (streq(macro__pat[i], pat)) continue;
+
+ /* Found one */
+ return (i);
+ }
+
+ /* Nothing */
+ return ( -1);
+}
+
+
+/*
+* Find the longest macro (if any) which starts with the given pattern
+*/
+static sint macro_find_ready(cptr pat)
+{
+ int i, t, n = -1, s = -1;
+
+ /* Nothing possible */
+ if (!macro__use[(byte)(pat[0])])
+ {
+ return ( -1);
+ }
+
+ /* Scan the macros */
+ for (i = 0; i < macro__num; ++i)
+ {
+ /* Skip macros which are not contained by the pattern */
+ if (!prefix(pat, macro__pat[i])) continue;
+
+ /* Obtain the length of this macro */
+ t = strlen(macro__pat[i]);
+
+ /* Only track the "longest" pattern */
+ if ((n >= 0) && (s > t)) continue;
+
+ /* Track the entry */
+ n = i;
+ s = t;
+ }
+
+ /* Result */
+ return (n);
+}
+
+
+/*
+* Add a macro definition (or redefinition).
+*
+* We should use "act == NULL" to "remove" a macro, but this might make it
+* impossible to save the "removal" of a macro definition. XXX XXX XXX
+*
+* We should consider refusing to allow macros which contain existing macros,
+* or which are contained in existing macros, because this would simplify the
+* macro analysis code. XXX XXX XXX
+*
+* We should consider removing the "command macro" crap, and replacing it
+* with some kind of "powerful keymap" ability, but this might make it hard
+* to change the "roguelike" option from inside the game. XXX XXX XXX
+*/
+errr macro_add(cptr pat, cptr act)
+{
+ int n;
+
+
+ /* Paranoia -- require data */
+ if (!pat || !act) return ( -1);
+
+
+ /* Look for any existing macro */
+ n = macro_find_exact(pat);
+
+ /* Replace existing macro */
+ if (n >= 0)
+ {
+ /* Free the old macro action */
+ string_free(macro__act[n]);
+ }
+
+ /* Create a new macro */
+ else
+ {
+ /* Acquire a new index */
+ n = macro__num++;
+
+ /* Save the pattern */
+ macro__pat[n] = string_make(pat);
+ }
+
+ /* Save the action */
+ macro__act[n] = string_make(act);
+
+ /* Efficiency */
+ macro__use[(byte)(pat[0])] = TRUE;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+* Initialize the "macro" package
+*/
+errr macro_init(void)
+{
+ /* Macro patterns */
+ C_MAKE(macro__pat, MACRO_MAX, cptr);
+
+ /* Macro actions */
+ C_MAKE(macro__act, MACRO_MAX, cptr);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Local "need flush" variable
+*/
+static bool_ flush_later = FALSE;
+
+
+/*
+* Local variable -- we are inside a "macro action"
+*
+* Do not match any macros until "ascii 30" is found.
+*/
+static bool_ parse_macro = FALSE;
+
+/*
+* Local variable -- we are inside a "macro trigger"
+*
+* Strip all keypresses until a low ascii value is found.
+*/
+static bool_ parse_under = FALSE;
+
+
+/*
+* Flush all input chars. Actually, remember the flush,
+* and do a "special flush" before the next "inkey()".
+*
+* This is not only more efficient, but also necessary to make sure
+* that various "inkey()" codes are not "lost" along the way.
+*/
+void flush(void)
+{
+ /* Do it later */
+ flush_later = TRUE;
+}
+
+
+/*
+* Flush the screen, make a noise
+*/
+void bell(void)
+{
+ /* Mega-Hack -- Flush the output */
+ Term_fresh();
+
+ /* Make a bell noise (if allowed) */
+ if (ring_bell) Term_xtra(TERM_XTRA_NOISE, 0);
+
+ /* Flush the input (later!) */
+ flush();
+}
+
+
+/*
+* Hack -- Make a (relevant?) sound
+*/
+void sound(int val)
+{
+ /* No sound */
+ if (!use_sound) return;
+
+ /* Make a sound (if allowed) */
+ Term_xtra(TERM_XTRA_SOUND, val);
+}
+
+
+
+/*
+* Helper function called only from "inkey()"
+*
+* This function does almost all of the "macro" processing.
+*
+* We use the "Term_key_push()" function to handle "failed" macros, as well
+* as "extra" keys read in while choosing the proper macro, and also to hold
+* the action for the macro, plus a special "ascii 30" character indicating
+* that any macro action in progress is complete. Embedded macros are thus
+* illegal, unless a macro action includes an explicit "ascii 30" character,
+* which would probably be a massive hack, and might break things.
+*
+* Only 500 (0+1+2+...+29+30) milliseconds may elapse between each key in
+* the macro trigger sequence. If a key sequence forms the "prefix" of a
+* macro trigger, 500 milliseconds must pass before the key sequence is
+* known not to be that macro trigger. XXX XXX XXX
+*/
+static char inkey_aux(void)
+{
+ int k = 0, n, p = 0, w = 0;
+
+ char ch;
+
+ cptr pat, act;
+
+ char buf[1024];
+
+
+ /* Wait for a keypress */
+ (void)(Term_inkey(&ch, TRUE, TRUE));
+
+
+ /* End "macro action" */
+ if (ch == 30) parse_macro = FALSE;
+
+ /* Inside "macro action" */
+ if (ch == 30) return (ch);
+
+ /* Inside "macro action" */
+ if (parse_macro) return (ch);
+
+ /* Inside "macro trigger" */
+ if (parse_under) return (ch);
+
+
+ /* Save the first key, advance */
+ buf[p++] = ch;
+ buf[p] = '\0';
+
+
+ /* Check for possible macro */
+ k = macro_find_check(buf);
+
+ /* No macro pending */
+ if (k < 0) return (ch);
+
+
+ /* Wait for a macro, or a timeout */
+ while (TRUE)
+ {
+ /* Check for pending macro */
+ k = macro_find_maybe(buf);
+
+ /* No macro pending */
+ if (k < 0) break;
+
+ /* Check for (and remove) a pending key */
+ if (0 == Term_inkey(&ch, FALSE, TRUE))
+ {
+ /* Append the key */
+ buf[p++] = ch;
+ buf[p] = '\0';
+
+ /* Restart wait */
+ w = 0;
+ }
+
+ /* No key ready */
+ else
+ {
+ /* Increase "wait" */
+ w += 10;
+
+ /* Excessive delay */
+ if (w >= 100) break;
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, w);
+ }
+ }
+
+
+ /* Check for available macro */
+ k = macro_find_ready(buf);
+
+ /* No macro available */
+ if (k < 0)
+ {
+ /* Push all the keys back on the queue */
+ while (p > 0)
+ {
+ /* Push the key, notice over-flow */
+ if (Term_key_push(buf[--p])) return (0);
+ }
+
+ /* Wait for (and remove) a pending key */
+ (void)Term_inkey(&ch, TRUE, TRUE);
+
+ /* Return the key */
+ return (ch);
+ }
+
+
+ /* Get the pattern */
+ pat = macro__pat[k];
+
+ /* Get the length of the pattern */
+ n = strlen(pat);
+
+ /* Push the "extra" keys back on the queue */
+ while (p > n)
+ {
+ /* Push the key, notice over-flow */
+ if (Term_key_push(buf[--p])) return (0);
+ }
+
+
+ /* Begin "macro action" */
+ parse_macro = TRUE;
+
+ /* Push the "end of macro action" key */
+ if (Term_key_push(30)) return (0);
+
+
+ /* Access the macro action */
+ act = macro__act[k];
+
+ /* Get the length of the action */
+ n = strlen(act);
+
+ /* Push the macro "action" onto the key queue */
+ while (n > 0)
+ {
+ /* Push the key, notice over-flow */
+ if (Term_key_push(act[--n])) return (0);
+ }
+
+
+ /* Hack -- Force "inkey()" to call us again */
+ return (0);
+}
+
+
+/*
+* Mega-Hack -- special "inkey_next" pointer. XXX XXX XXX
+*
+* This special pointer allows a sequence of keys to be "inserted" into
+* the stream of keys returned by "inkey()". This key sequence will not
+* trigger any macros, and cannot be bypassed by the Borg. It is used
+* in Angband to handle "keymaps".
+*/
+static cptr inkey_next = NULL;
+
+
+/*
+* Get a keypress from the user.
+*
+* This function recognizes a few "global parameters". These are variables
+* which, if set to TRUE before calling this function, will have an effect
+* on this function, and which are always reset to FALSE by this function
+* before this function returns. Thus they function just like normal
+* parameters, except that most calls to this function can ignore them.
+*
+* If "inkey_xtra" is TRUE, then all pending keypresses will be flushed,
+* and any macro processing in progress will be aborted. This flag is
+* set by the "flush()" function, which does not actually flush anything
+* itself, but rather, triggers delayed input flushing via "inkey_xtra".
+*
+* If "inkey_scan" is TRUE, then we will immediately return "zero" if no
+* keypress is available, instead of waiting for a keypress.
+*
+* If "inkey_base" is TRUE, then all macro processing will be bypassed.
+* If "inkey_base" and "inkey_scan" are both TRUE, then this function will
+* not return immediately, but will wait for a keypress for as long as the
+* normal macro matching code would, allowing the direct entry of macro
+* triggers. The "inkey_base" flag is extremely dangerous!
+*
+* If "inkey_flag" is TRUE, then we will assume that we are waiting for a
+* normal command, and we will only show the cursor if "hilite_player" is
+* TRUE (or if the player is in a store), instead of always showing the
+* cursor. The various "main-xxx.c" files should avoid saving the game
+* in response to a "menu item" request unless "inkey_flag" is TRUE, to
+* prevent savefile corruption.
+*
+* If we are waiting for a keypress, and no keypress is ready, then we will
+* refresh (once) the window which was active when this function was called.
+*
+* Note that "back-quote" is automatically converted into "escape" for
+* convenience on machines with no "escape" key. This is done after the
+* macro matching, so the user can still make a macro for "backquote".
+*
+* Note the special handling of "ascii 30" (ctrl-caret, aka ctrl-shift-six)
+* and "ascii 31" (ctrl-underscore, aka ctrl-shift-minus), which are used to
+* provide support for simple keyboard "macros". These keys are so strange
+* that their loss as normal keys will probably be noticed by nobody. The
+* "ascii 30" key is used to indicate the "end" of a macro action, which
+* allows recursive macros to be avoided. The "ascii 31" key is used by
+* some of the "main-xxx.c" files to introduce macro trigger sequences.
+*
+* Hack -- we use "ascii 29" (ctrl-right-bracket) as a special "magic" key,
+* which can be used to give a variety of "sub-commands" which can be used
+* any time. These sub-commands could include commands to take a picture of
+* the current screen, to start/stop recording a macro action, etc.
+*
+* If "angband_term[0]" is not active, we will make it active during this
+* function, so that the various "main-xxx.c" files can assume that input
+* is only requested (via "Term_inkey()") when "angband_term[0]" is active.
+*
+* Mega-Hack -- This function is used as the entry point for clearing the
+* "signal_count" variable, and of the "character_saved" variable.
+*
+* Hack -- Note the use of "inkey_next" to allow "keymaps" to be processed.
+*
+* Mega-Hack -- Note the use of "inkey_hack" to allow the "Borg" to steal
+* control of the keyboard from the user.
+*/
+char inkey(void)
+{
+ int v;
+
+ char kk;
+
+ char ch = 0;
+
+ bool_ done = FALSE;
+
+ term *old = Term;
+
+ /* Hack -- Use the "inkey_next" pointer */
+ if (inkey_next && *inkey_next && !inkey_xtra)
+ {
+ /* Get next character, and advance */
+ ch = *inkey_next++;
+
+ /* Cancel the various "global parameters" */
+ inkey_base = inkey_xtra = inkey_flag = inkey_scan = FALSE;
+
+ /* Accept result */
+ macro_recorder_add(ch);
+ return (ch);
+ }
+
+ /* Forget pointer */
+ inkey_next = NULL;
+
+
+ /* Hack -- handle delayed "flush()" */
+ if (inkey_xtra)
+ {
+ /* End "macro action" */
+ parse_macro = FALSE;
+
+ /* End "macro trigger" */
+ parse_under = FALSE;
+
+ /* Forget old keypresses */
+ Term_flush();
+ }
+
+
+ /* Access cursor state */
+ (void)Term_get_cursor(&v);
+
+ /* Show the cursor if waiting, except sometimes in "command" mode */
+ if (!inkey_scan && (!inkey_flag || hilite_player || character_icky))
+ {
+ /* Show the cursor */
+ (void)Term_set_cursor(1);
+ }
+
+
+ /* Hack -- Activate main screen */
+ Term_activate(angband_term[0]);
+
+
+ /* Get a key */
+ while (!ch)
+ {
+ /* Hack -- Handle "inkey_scan" */
+ if (!inkey_base && inkey_scan &&
+ (0 != Term_inkey(&kk, FALSE, FALSE)))
+ {
+ break;
+ }
+
+
+ /* Hack -- Flush output once when no key ready */
+ if (!done && (0 != Term_inkey(&kk, FALSE, FALSE)))
+ {
+ /* Hack -- activate proper term */
+ Term_activate(old);
+
+ /* Flush output */
+ Term_fresh();
+
+ /* Hack -- activate main screen */
+ Term_activate(angband_term[0]);
+
+ /* Mega-Hack -- reset saved flag */
+ character_saved = FALSE;
+
+ /* Mega-Hack -- reset signal counter */
+ signal_count = 0;
+
+ /* Only once */
+ done = TRUE;
+ }
+
+
+ /* Hack -- Handle "inkey_base" */
+ if (inkey_base)
+ {
+ int w = 0;
+
+ /* Wait forever */
+ if (!inkey_scan)
+ {
+ /* Wait for (and remove) a pending key */
+ if (0 == Term_inkey(&ch, TRUE, TRUE))
+ {
+ /* Done */
+ break;
+ }
+
+ /* Oops */
+ break;
+ }
+
+ /* Wait */
+ while (TRUE)
+ {
+ /* Check for (and remove) a pending key */
+ if (0 == Term_inkey(&ch, FALSE, TRUE))
+ {
+ /* Done */
+ break;
+ }
+
+ /* No key ready */
+ else
+ {
+ /* Increase "wait" */
+ w += 10;
+
+ /* Excessive delay */
+ if (w >= 100) break;
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, w);
+ }
+ }
+
+ /* Done */
+ break;
+ }
+
+
+ /* Get a key (see above) */
+ ch = inkey_aux();
+
+
+ /* Handle "control-right-bracket" */
+ if ((ch == 29) || ((!rogue_like_commands) && (ch == KTRL('D'))))
+ {
+ /* Strip this key */
+ ch = 0;
+
+ if (!do_movies)
+ /* Do an html dump */
+ do_cmd_html_dump();
+ else
+ /* Do a text box in the cmovie */
+ do_cmovie_insert();
+
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Treat back-quote as escape */
+ if (ch == '`') ch = ESCAPE;
+
+
+ /* End "macro trigger" */
+ if (parse_under && (ch <= 32))
+ {
+ /* Strip this key */
+ ch = 0;
+
+ /* End "macro trigger" */
+ parse_under = FALSE;
+ }
+
+
+ /* Handle "control-caret" */
+ if (ch == 30)
+ {
+ /* Strip this key */
+ ch = 0;
+ }
+
+ /* Handle "control-underscore" */
+ else if (ch == 31)
+ {
+ /* Strip this key */
+ ch = 0;
+
+ /* Begin "macro trigger" */
+ parse_under = TRUE;
+ }
+
+ /* Inside "macro trigger" */
+ else if (parse_under)
+ {
+ /* Strip this key */
+ ch = 0;
+ }
+ }
+
+
+ /* Hack -- restore the term */
+ Term_activate(old);
+
+
+ /* Restore the cursor */
+ Term_set_cursor(v);
+
+
+ /* Cancel the various "global parameters" */
+ inkey_base = inkey_xtra = inkey_flag = inkey_scan = FALSE;
+
+
+ /* Return the keypress */
+ macro_recorder_add(ch);
+ return (ch);
+}
+
+
+
+
+/*
+* We use a global array for all inscriptions to reduce the memory
+* spent maintaining inscriptions. Of course, it is still possible
+* to run out of inscription memory, especially if too many different
+* inscriptions are used, but hopefully this will be rare.
+*
+* We use dynamic string allocation because otherwise it is necessary
+* to pre-guess the amount of quark activity. We limit the total
+* number of quarks, but this is much easier to "expand" as needed.
+*
+* Any two items with the same inscription will have the same "quark"
+* index, which should greatly reduce the need for inscription space.
+*
+* Note that "quark zero" is NULL and should not be "dereferenced".
+*/
+
+/*
+* Add a new "quark" to the set of quarks.
+*/
+s16b quark_add(cptr str)
+{
+ int i;
+
+ /* Look for an existing quark */
+ for (i = 1; i < quark__num; i++)
+ {
+ /* Check for equality */
+ if (streq(quark__str[i], str)) return (i);
+ }
+
+ /* Paranoia -- Require room */
+ if (quark__num == QUARK_MAX) return (0);
+
+ /* New maximal quark */
+ quark__num = i + 1;
+
+ /* Add a new quark */
+ quark__str[i] = string_make(str);
+
+ /* Return the index */
+ return (i);
+}
+
+
+/*
+* This function looks up a quark
+*/
+cptr quark_str(s16b i)
+{
+ cptr q;
+
+ /* Verify */
+ if ((i < 0) || (i >= quark__num)) i = 0;
+
+ /* Access the quark */
+ q = quark__str[i];
+
+ /* Return the quark */
+ return (q);
+}
+
+
+
+
+/*
+* Second try for the "message" handling routines.
+*
+* Each call to "message_add(s)" will add a new "most recent" message
+* to the "message recall list", using the contents of the string "s".
+*
+* The messages will be stored in such a way as to maximize "efficiency",
+* that is, we attempt to maximize the number of sequential messages that
+* can be retrieved, given a limited amount of storage space.
+*
+* We keep a buffer of chars to hold the "text" of the messages, not
+* necessarily in "order", and an array of offsets into that buffer,
+* representing the actual messages. This is made more complicated
+* by the fact that both the array of indexes, and the buffer itself,
+* are both treated as "circular arrays" for efficiency purposes, but
+* the strings may not be "broken" across the ends of the array.
+*
+* The "message_add()" function is rather "complex", because it must be
+* extremely efficient, both in space and time, for use with the Borg.
+*/
+
+
+
+/*
+* How many messages are "available"?
+*/
+s16b message_num(void)
+{
+ int last, next, n;
+
+ /* Extract the indexes */
+ last = message__last;
+ next = message__next;
+
+ /* Handle "wrap" */
+ if (next < last) next += MESSAGE_MAX;
+
+ /* Extract the space */
+ n = (next - last);
+
+ /* Return the result */
+ return (n);
+}
+
+
+
+/*
+* Recall the "text" of a saved message
+*/
+cptr message_str(int age)
+{
+ static char buf[1024];
+ s16b x;
+ s16b o;
+ cptr s;
+
+ /* Forgotten messages have no text */
+ if ((age < 0) || (age >= message_num())) return ("");
+
+ /* Acquire the "logical" index */
+ x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX;
+
+ /* Get the "offset" for the message */
+ o = message__ptr[x];
+
+ /* Access the message text */
+ s = &message__buf[o];
+
+ /* Hack -- Handle repeated messages */
+ if (message__count[x] > 1)
+ {
+ strnfmt(buf, 1024, "%s <%dx>", s, message__count[x]);
+ s = buf;
+ }
+
+ /* Return the message text */
+ return (s);
+}
+
+/*
+* Recall the color of a saved message
+*/
+byte message_color(int age)
+{
+ s16b x;
+ byte color = TERM_WHITE;
+
+ /* Forgotten messages have no text */
+ if ((age < 0) || (age >= message_num())) return (TERM_WHITE);
+
+ /* Acquire the "logical" index */
+ x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX;
+
+ /* Get the "offset" for the message */
+ color = message__color[x];
+
+ /* Return the message text */
+ return (color);
+}
+
+/*
+ * Recall the type of a saved message
+ */
+byte message_type(int age)
+{
+ s16b x;
+ byte type;
+
+ /* Forgotten messages have no text */
+ if ((age < 0) || (age >= message_num())) return (MESSAGE_NONE);
+
+ /* Acquire the "logical" index */
+ x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX;
+
+ /* Get the "offset" for the message */
+ type = message__type[x];
+
+ /* Return the message text */
+ return (type);
+}
+
+
+
+/*
+* Add a new message, with great efficiency
+*/
+void message_add(byte type, cptr str, byte color)
+{
+ int i, k, x, n;
+ cptr s;
+
+
+ /*** Step 1 -- Analyze the message ***/
+
+ /* Hack -- Ignore "non-messages" */
+ if (!str) return;
+
+ /* Message length */
+ n = strlen(str);
+
+ /* Important Hack -- Ignore "long" messages */
+ if (n >= MESSAGE_BUF / 4) return;
+
+
+ /*** Step 2 -- Handle repeated messages ***/
+
+ /* Acquire the "logical" last index */
+ x = (message__next + MESSAGE_MAX - 1) % MESSAGE_MAX;
+
+ /* Get the last message text */
+ s = &message__buf[message__ptr[x]];
+
+ /* Last message repeated? */
+ if (streq(str, s))
+ {
+ /* Increase the message count */
+ message__count[x]++;
+
+ /* Success */
+ return;
+ }
+
+
+ /*** Step 3 -- Attempt to optimize ***/
+
+ /* Limit number of messages to check */
+ k = message_num() / 4;
+
+ /* Limit number of messages to check */
+ if (k > MESSAGE_MAX / 32) k = MESSAGE_MAX / 32;
+
+ /* Check the last few messages (if any to count) */
+ for (i = message__next; k; k--)
+ {
+ u16b q;
+
+ cptr old;
+
+ /* Back up and wrap if needed */
+ if (i-- == 0) i = MESSAGE_MAX - 1;
+
+ /* Stop before oldest message */
+ if (i == message__last) break;
+
+ /* Extract "distance" from "head" */
+ q = (message__head + MESSAGE_BUF - message__ptr[i]) % MESSAGE_BUF;
+
+ /* Do not optimize over large distance */
+ if (q > MESSAGE_BUF / 2) continue;
+
+ /* Access the old string */
+ old = &message__buf[message__ptr[i]];
+
+ /* Compare */
+ if (!streq(old, str)) continue;
+
+ /* Get the next message index, advance */
+ x = message__next++;
+
+ /* Handle wrap */
+ if (message__next == MESSAGE_MAX) message__next = 0;
+
+ /* Kill last message if needed */
+ if (message__next == message__last) message__last++;
+
+ /* Handle wrap */
+ if (message__last == MESSAGE_MAX) message__last = 0;
+
+ /* Assign the starting address */
+ message__ptr[x] = message__ptr[i];
+ message__color[x] = color;
+ message__type[x] = type;
+ message__count[x] = 1;
+
+ /* Success */
+ return;
+ }
+
+
+ /*** Step 4 -- Ensure space before end of buffer ***/
+
+ /* Kill messages and Wrap if needed */
+ if (message__head + n + 1 >= MESSAGE_BUF)
+ {
+ /* Kill all "dead" messages */
+ for (i = message__last; TRUE; i++)
+ {
+ /* Wrap if needed */
+ if (i == MESSAGE_MAX) i = 0;
+
+ /* Stop before the new message */
+ if (i == message__next) break;
+
+ /* Kill "dead" messages */
+ if (message__ptr[i] >= message__head)
+ {
+ /* Track oldest message */
+ message__last = i + 1;
+ }
+ }
+
+ /* Wrap "tail" if needed */
+ if (message__tail >= message__head) message__tail = 0;
+
+ /* Start over */
+ message__head = 0;
+ }
+
+
+ /*** Step 5 -- Ensure space before next message ***/
+
+ /* Kill messages if needed */
+ if (message__head + n + 1 > message__tail)
+ {
+ /* Grab new "tail" */
+ message__tail = message__head + n + 1;
+
+ /* Advance tail while possible past first "nul" */
+ while (message__buf[message__tail - 1]) message__tail++;
+
+ /* Kill all "dead" messages */
+ for (i = message__last; TRUE; i++)
+ {
+ /* Wrap if needed */
+ if (i == MESSAGE_MAX) i = 0;
+
+ /* Stop before the new message */
+ if (i == message__next) break;
+
+ /* Kill "dead" messages */
+ if ((message__ptr[i] >= message__head) &&
+ (message__ptr[i] < message__tail))
+ {
+ /* Track oldest message */
+ message__last = i + 1;
+ }
+ }
+ }
+
+
+ /*** Step 6 -- Grab a new message index ***/
+
+ /* Get the next message index, advance */
+ x = message__next++;
+
+ /* Handle wrap */
+ if (message__next == MESSAGE_MAX) message__next = 0;
+
+ /* Kill last message if needed */
+ if (message__next == message__last) message__last++;
+
+ /* Handle wrap */
+ if (message__last == MESSAGE_MAX) message__last = 0;
+
+
+
+ /*** Step 7 -- Insert the message text ***/
+
+ /* Assign the starting address */
+ message__ptr[x] = message__head;
+ message__color[x] = color;
+ message__type[x] = type;
+ message__count[x] = 1;
+
+ /* Append the new part of the message */
+ for (i = 0; i < n; i++)
+ {
+ /* Copy the message */
+ message__buf[message__head + i] = str[i];
+ }
+
+ /* Terminate */
+ message__buf[message__head + i] = '\0';
+
+ /* Advance the "head" pointer */
+ message__head += n + 1;
+}
+
+
+
+/*
+* Hack -- flush
+*/
+static void msg_flush(int x)
+{
+ byte a = TERM_L_BLUE;
+
+ /* Pause for response */
+ Term_putstr(x, 0, -1, a, "-more-");
+
+ /* Get an acceptable keypress */
+ while (1)
+ {
+ int cmd = inkey();
+ if (quick_messages) break;
+ if ((cmd == ESCAPE) || (cmd == ' ')) break;
+ if ((cmd == '\n') || (cmd == '\r')) break;
+ bell();
+ }
+
+ /* Clear the line */
+ Term_erase(0, 0, 255);
+}
+
+/* Display a message */
+void display_message(int x, int y, int split, byte color, cptr t)
+{
+ int i = 0, j = 0;
+
+ while (i < split)
+ {
+ if (t[i] == '#')
+ {
+ if (t[i + 1] == '#')
+ {
+ Term_putstr(x + j, y, 1, color, "#");
+ i += 2;
+ j++;
+ }
+ else
+ {
+ color = color_char_to_attr(t[i + 1]);
+ i += 2;
+ }
+ }
+ else
+ {
+ Term_putstr(x + j, y, 1, color, t + i);
+ i++;
+ j++;
+ }
+ }
+}
+
+/*
+* Output a message to the top line of the screen.
+*
+* Break long messages into multiple pieces (40-72 chars).
+*
+* Allow multiple short messages to "share" the top line.
+*
+* Prompt the user to make sure he has a chance to read them.
+*
+* These messages are memorized for later reference (see above).
+*
+* We could do "Term_fresh()" to provide "flicker" if needed.
+*
+* The global "msg_flag" variable can be cleared to tell us to
+* "erase" any "pending" messages still on the screen.
+*
+* XXX XXX XXX Note that we must be very careful about using the
+* "msg_print()" functions without explicitly calling the special
+* "msg_print(NULL)" function, since this may result in the loss
+* of information if the screen is cleared, or if anything is
+* displayed on the top line.
+*
+* XXX XXX XXX Note that "msg_print(NULL)" will clear the top line
+* even if no messages are pending. This is probably a hack.
+*/
+void cmsg_print(byte color, cptr msg)
+{
+ static int p = 0;
+
+ int n;
+
+ char *t;
+
+ char buf[1024];
+
+ int lim = Term->wid - 8;
+
+
+ /* Hack -- Reset */
+ if (!msg_flag) p = 0;
+
+ /* Message Length */
+ n = (msg ? strlen(msg) : 0);
+
+ /* Hack -- flush when requested or needed */
+ if (p && (!msg || ((p + n) > lim)))
+ {
+ /* Flush */
+ msg_flush(p);
+
+ /* Forget it */
+ msg_flag = FALSE;
+
+ /* Reset */
+ p = 0;
+ }
+
+
+ /* No message */
+ if (!msg) return;
+
+ /* Paranoia */
+ if (n > 1000) return;
+
+
+ /* Memorize the message */
+ if (character_generated) message_add(MESSAGE_MSG, msg, color);
+
+ /* Handle "auto_more" */
+ if (auto_more)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MESSAGE);
+
+ /* Force window update */
+ window_stuff();
+
+ /* Done */
+ return;
+ }
+
+
+ /* Copy it */
+ strcpy(buf, msg);
+
+ /* Analyze the buffer */
+ t = buf;
+
+ /* Split message */
+ while (n > lim)
+ {
+ char oops;
+
+ int check, split;
+
+ /* Default split */
+ split = lim;
+
+ /* Find the "best" split point */
+ for (check = 40; check < lim; check++)
+ {
+ /* Found a valid split point */
+ if (t[check] == ' ') split = check;
+ }
+
+ /* Save the split character */
+ oops = t[split];
+
+ /* Split the message */
+ t[split] = '\0';
+
+ /* Display part of the message */
+ display_message(0, 0, split, color, t);
+
+ /* Flush it */
+ msg_flush(split + 1);
+
+ /* Memorize the piece */
+ /* if (character_generated) message_add(t); */
+
+ /* Restore the split character */
+ t[split] = oops;
+
+ /* Insert a space */
+ t[--split] = ' ';
+
+ /* Prepare to recurse on the rest of "buf" */
+ t += split;
+ n -= split;
+ }
+
+
+ /* Display the tail of the message */
+ display_message(p, 0, n, color, t);
+
+ /* Memorize the tail */
+ /* if (character_generated) message_add(t); */
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MESSAGE);
+
+ /* Remember the message */
+ msg_flag = TRUE;
+
+ /* Remember the position */
+ p += n + 1;
+
+ /* Optional refresh */
+ if (fresh_message) Term_fresh();
+}
+
+/* Hack -- for compatibility and easy sake */
+void msg_print(cptr msg)
+{
+ cmsg_print(TERM_WHITE, msg);
+}
+
+
+/*
+ * Hack -- prevent "accidents" in "screen_save()" or "screen_load()"
+ */
+static int screen_depth = 0;
+
+
+/*
+ * Save the screen, and increase the "icky" depth.
+ *
+ * This function must match exactly one call to "screen_load()".
+ */
+void screen_save(void)
+{
+ /* Hack -- Flush messages */
+ msg_print(NULL);
+
+ /* Save the screen (if legal) */
+ if (screen_depth++ == 0) Term_save();
+
+ /* Increase "icky" depth */
+ character_icky++;
+}
+
+
+/*
+ * Load the screen, and decrease the "icky" depth.
+ *
+ * This function must match exactly one call to "screen_save()".
+ */
+void screen_load(void)
+{
+ /* Hack -- Flush messages */
+ msg_print(NULL);
+
+ /* Load the screen (if legal) */
+ if (--screen_depth == 0) Term_load();
+
+ /* Decrease "icky" depth */
+ character_icky--;
+}
+
+
+/*
+* Display a formatted message, using "vstrnfmt()" and "msg_print()".
+*/
+void msg_format(cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ cmsg_print(TERM_WHITE, buf);
+}
+
+void cmsg_format(byte color, cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ cmsg_print(color, buf);
+}
+
+
+
+/*
+* Display a string on the screen using an attribute.
+*
+* At the given location, using the given attribute, if allowed,
+* add the given string. Do not clear the line.
+*/
+void c_put_str(byte attr, cptr str, int row, int col)
+{
+ /* Position cursor, Dump the attr/text */
+ Term_putstr(col, row, -1, attr, str);
+}
+
+/*
+* As above, but in "white"
+*/
+void put_str(cptr str, int row, int col)
+{
+ /* Spawn */
+ Term_putstr(col, row, -1, TERM_WHITE, str);
+}
+
+
+
+/*
+* Display a string on the screen using an attribute, and clear
+* to the end of the line.
+*/
+void c_prt(byte attr, cptr str, int row, int col)
+{
+ /* Clear line, position cursor */
+ Term_erase(col, row, 255);
+
+ /* Dump the attr/text */
+ Term_addstr( -1, attr, str);
+}
+
+/*
+* As above, but in "white"
+*/
+void prt(cptr str, int row, int col)
+{
+ /* Spawn */
+ c_prt(TERM_WHITE, str, row, col);
+}
+
+
+
+/*
+ * Print some (colored) text to the screen at the current cursor position,
+ * automatically "wrapping" existing text (at spaces) when necessary to
+ * avoid placing any text into the last column, and clearing every line
+ * before placing any text in that line. Also, allow "newline" to force
+ * a "wrap" to the next line. Advance the cursor as needed so sequential
+ * calls to this function will work correctly.
+ *
+ * Once this function has been called, the cursor should not be moved
+ * until all the related "text_out()" calls to the window are complete.
+ *
+ * This function will correctly handle any width up to the maximum legal
+ * value of 256, though it works best for a standard 80 character width.
+ */
+void text_out_to_screen(byte a, cptr str)
+{
+ int x, y;
+
+ int wid, h;
+
+ int wrap;
+
+ cptr s;
+
+
+ /* Obtain the size */
+ (void)Term_get_size(&wid, &h);
+
+ /* Obtain the cursor */
+ (void)Term_locate(&x, &y);
+
+ /* Use special wrapping boundary? */
+ if ((text_out_wrap > 0) && (text_out_wrap < wid))
+ wrap = text_out_wrap;
+ else
+ wrap = wid;
+
+ /* Process the string */
+ for (s = str; *s; s++)
+ {
+ char ch;
+
+ /* Force wrap */
+ if (*s == '\n')
+ {
+ /* Wrap */
+ x = text_out_indent;
+ y++;
+
+ /* Clear line, move cursor */
+ Term_erase(x, y, 255);
+
+ continue;
+ }
+
+ /* Clean up the char */
+ ch = (isprint((unsigned char) * s) ? *s : ' ');
+
+ /* Wrap words as needed */
+ if ((x >= wrap - 1) && (ch != ' '))
+ {
+ int i, n = 0;
+
+ byte av[256];
+ char cv[256];
+
+ /* Wrap word */
+ if (x < wrap)
+ {
+ /* Scan existing text */
+ for (i = wrap - 2; i >= 0; i--)
+ {
+ /* Grab existing attr/char */
+ Term_what(i, y, &av[i], &cv[i]);
+
+ /* Break on space */
+ if (cv[i] == ' ') break;
+
+ /* Track current word */
+ n = i;
+ }
+ }
+
+ /* Special case */
+ if (n == 0) n = wrap;
+
+ /* Clear line */
+ Term_erase(n, y, 255);
+
+ /* Wrap */
+ x = text_out_indent;
+ y++;
+
+ /* Clear line, move cursor */
+ Term_erase(x, y, 255);
+
+ /* Wrap the word (if any) */
+ for (i = n; i < wrap - 1; i++)
+ {
+ /* Dump */
+ Term_addch(av[i], cv[i]);
+
+ /* Advance (no wrap) */
+ if (++x > wrap) x = wrap;
+ }
+ }
+
+ /* Dump */
+ Term_addch(a, ch);
+
+ /* Advance */
+ if (++x > wrap) x = wrap;
+ }
+}
+
+
+/*
+ * Write text to the given file and apply line-wrapping.
+ *
+ * Hook function for text_out(). Make sure that text_out_file points
+ * to an open text-file.
+ *
+ * Long lines will be wrapped at text_out_wrap, or at column 75 if that
+ * is not set; or at a newline character.
+ *
+ * You must be careful to end all file output with a newline character
+ * to "flush" the stored line position.
+ */
+void text_out_to_file(byte a, cptr str)
+{
+ /* Current position on the line */
+ static int pos = 0;
+
+ /* Wrap width */
+ int wrap = (text_out_wrap ? text_out_wrap : 75);
+
+ /* Current location within "str" */
+ cptr s = str;
+
+ /* Unused parameter */
+ (void)a;
+
+ /* Process the string */
+ while (*s)
+ {
+ char ch;
+ int n = 0;
+ int len = wrap - pos;
+ int l_space = 0;
+
+ /* If we are at the start of the line... */
+ if (pos == 0)
+ {
+ int i;
+
+ /* Output the indent */
+ for (i = 0; i < text_out_indent; i++)
+ {
+ fputc(' ', text_out_file);
+ pos++;
+ }
+ }
+
+ /* Find length of line up to next newline or end-of-string */
+ while ((n < len) && !((s[n] == '\n') || (s[n] == '\0')))
+ {
+ /* Mark the most recent space in the string */
+ if (s[n] == ' ') l_space = n;
+
+ /* Increment */
+ n++;
+ }
+
+ /* If we have encountered no spaces */
+ if ((l_space == 0) && (n == len))
+ {
+ /* If we are at the start of a new line */
+ if (pos == text_out_indent)
+ {
+ len = n;
+ }
+ else
+ {
+ /* Begin a new line */
+ fputc('\n', text_out_file);
+
+ /* Reset */
+ pos = 0;
+
+ continue;
+ }
+ }
+ else
+ {
+ /* Wrap at the newline */
+ if ((s[n] == '\n') || (s[n] == '\0')) len = n;
+
+ /* Wrap at the last space */
+ else len = l_space;
+ }
+
+ /* Write that line to file */
+ for (n = 0; n < len; n++)
+ {
+ /* Ensure the character is printable */
+ ch = (isprint(s[n]) ? s[n] : ' ');
+
+ /* Write out the character */
+ fputc(ch, text_out_file);
+
+ /* Increment */
+ pos++;
+ }
+
+ /* Move 's' past the stuff we've written */
+ s += len;
+
+ /* If we are at the end of the string, end */
+ if (*s == '\0') return;
+
+ /* Skip newlines */
+ if (*s == '\n') s++;
+
+ /* Begin a new line */
+ fputc('\n', text_out_file);
+
+ /* Reset */
+ pos = 0;
+
+ /* Skip whitespace */
+ while (*s == ' ') s++;
+ }
+
+ /* We are done */
+ return;
+}
+
+
+/*
+ * Output text to the screen or to a file depending on the selected
+ * text_out hook.
+ */
+void text_out(cptr str)
+{
+ text_out_c(TERM_WHITE, str);
+}
+
+
+/*
+ * Output text to the screen (in color) or to a file depending on the
+ * selected hook.
+ */
+void text_out_c(byte a, cptr str)
+{
+ text_out_hook(a, str);
+}
+
+
+
+
+/*
+* Clear part of the screen
+*/
+void clear_from(int row)
+{
+ int y;
+
+ /* Erase requested rows */
+ for (y = row; y < Term->hgt; y++)
+ {
+ /* Erase part of the screen */
+ Term_erase(0, y, 255);
+ }
+}
+
+/*
+ * Try to find a matching command completion.
+ * Note that this is not so friendly since it doesn't give
+ * a list of possible completions.
+ *
+ * First arg is the string to be completed, second is it's length,
+ * third is it's maximum length.
+ */
+static int complete_where = 0;
+static char complete_buf[100];
+static int complete_command(char *buf, int clen, int mlen)
+{
+ int i, j = 1, max = clen;
+ bool_ gotone = FALSE;
+
+ /* Forget the characters after the end of the string. */
+ complete_buf[clen] = '\0';
+
+ for (i = 0; i < cli_total; i++)
+ {
+ cli_comm *cli_ptr = cli_info + i;
+
+ if (!strncmp(cli_ptr->comm, complete_buf, clen))
+ {
+ Term_erase(0, j, 80);
+ Term_putstr(0, j++, -1, TERM_WHITE, cli_ptr->comm);
+
+ /* For the first match, copy the whole string to buf. */
+ if (!gotone)
+ {
+ sprintf(buf, "%.*s", mlen, cli_ptr->comm);
+ gotone = TRUE;
+ }
+ /* For later matches, simply notice how much of buf it
+ * matches. */
+ else
+ {
+ for (max = clen; max < mlen; max++)
+ {
+ if (cli_ptr->comm[max] == '\0') break;
+ if (cli_ptr->comm[max] != buf[max]) break;
+ }
+ if (max < mlen) buf[max] = '\0';
+ }
+ }
+ }
+
+ return strlen(buf) + 1;
+}
+
+
+/*
+* Get some input at the cursor location.
+* Assume the buffer is initialized to a default string.
+* Note that this string is often "empty" (see below).
+* The default buffer is displayed in yellow until cleared.
+* Pressing RETURN right away accepts the default entry.
+* Normal chars clear the default and append the char.
+* Backspace clears the default or deletes the final char.
+* ESCAPE clears the buffer and the window and returns FALSE.
+* RETURN accepts the current buffer contents and returns TRUE.
+*/
+bool_ askfor_aux_complete = FALSE;
+bool_ askfor_aux(char *buf, int len)
+{
+ int y, x;
+
+ int i = 0;
+
+ int k = 0;
+
+ int wid, hgt;
+
+ bool_ done = FALSE;
+
+
+ /* Locate the cursor */
+ Term_locate(&x, &y);
+
+ /* Get terminal size */
+ Term_get_size(&wid, &hgt);
+
+ /* Paranoia -- check column */
+ if ((x < 0) || (x >= wid)) x = 0;
+
+ /* Restrict the length */
+ if (x + len > wid) len = wid - x;
+
+
+ /* Paranoia -- Clip the default entry */
+ buf[len] = '\0';
+
+
+ /* Display the default answer */
+ Term_erase(x, y, len);
+ Term_putstr(x, y, -1, TERM_YELLOW, buf);
+
+ if (askfor_aux_complete)
+ {
+ screen_save();
+ complete_where = 0;
+ strncpy(complete_buf, buf, 100);
+ }
+
+ /* Process input */
+ while (!done)
+ {
+ /* Place cursor */
+ Term_gotoxy(x + k, y);
+
+ /* Get a key */
+ i = inkey();
+
+ /* Analyze the key */
+ switch (i)
+ {
+ case ESCAPE:
+ k = 0;
+ done = TRUE;
+ break;
+
+ case '\n':
+ case '\r':
+ k = strlen(buf);
+ done = TRUE;
+ break;
+
+ case '\t':
+ if (askfor_aux_complete && k)
+ {
+ screen_load();
+ screen_save();
+ k = complete_command(buf, k, len);
+ }
+ else
+ {
+ bell();
+ }
+
+ case 0x7F:
+ case '\010':
+ if (k > 0) k--;
+ strncpy(complete_buf, buf, k);
+ break;
+
+ default:
+ if ((k < len) && (isprint(i)))
+ {
+ buf[k++] = i;
+ strncpy(complete_buf, buf, k);
+ }
+ else
+ {
+ bell();
+ }
+ break;
+ }
+
+ /* Terminate */
+ buf[k] = '\0';
+
+ /* Update the entry */
+ Term_erase(x, y, len);
+ Term_putstr(x, y, -1, TERM_WHITE, buf);
+ }
+
+ if (askfor_aux_complete)
+ {
+ screen_load();
+ }
+
+ /* Aborted */
+ if (i == ESCAPE) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Get a string from the user
+*
+* The "prompt" should take the form "Prompt: "
+*
+* Note that the initial contents of the string is used as
+* the default response, so be sure to "clear" it if needed.
+*
+* We clear the input, and return FALSE, on "ESCAPE".
+*/
+bool_ get_string(cptr prompt, char *buf, int len)
+{
+ bool_ res;
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Display prompt */
+ prt(prompt, 0, 0);
+
+ /* Ask the user for a string */
+ res = askfor_aux(buf, len);
+
+ /* Clear prompt */
+ prt("", 0, 0);
+
+ /* Result */
+ return (res);
+}
+
+
+/*
+* Verify something with the user
+*
+* The "prompt" should take the form "Query? "
+*
+* Note that "[y/n]" is appended to the prompt.
+*/
+bool_ get_check(cptr prompt)
+{
+ int i;
+
+ char buf[80];
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Hack -- Build a "useful" prompt */
+ strnfmt(buf, 78, "%.70s[y/n] ", prompt);
+
+ /* Prompt for it */
+ prt(buf, 0, 0);
+
+ /* Get an acceptable answer */
+ while (TRUE)
+ {
+ i = inkey();
+ if (quick_messages) break;
+ if (i == ESCAPE) break;
+ if (strchr("YyNn", i)) break;
+ bell();
+ }
+
+ /* Erase the prompt */
+ prt("", 0, 0);
+
+ /* Normal negation */
+ if ((i != 'Y') && (i != 'y')) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Prompts for a keypress
+*
+* The "prompt" should take the form "Command: "
+*
+* Returns TRUE unless the character is "Escape"
+*/
+bool_ get_com(cptr prompt, char *command)
+{
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Display a prompt */
+ prt(prompt, 0, 0);
+
+ /* Get a key */
+ *command = inkey();
+
+ /* Clear the prompt */
+ prt("", 0, 0);
+
+ /* Handle "cancel" */
+ if (*command == ESCAPE) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Request a "quantity" from the user
+*
+* Hack -- allow "command_arg" to specify a quantity
+*/
+s32b get_quantity(cptr prompt, s32b max)
+{
+ s32b amt;
+ int aamt;
+
+ char tmp[80];
+
+ char buf[80];
+
+
+ /* Use "command_arg" */
+ if (command_arg)
+ {
+ /* Extract a number */
+ amt = command_arg;
+
+ /* Clear "command_arg" */
+ command_arg = 0;
+
+ /* Enforce the maximum */
+ if (amt > max) amt = max;
+
+ /* Use it */
+ return (amt);
+ }
+
+ /* Get the item index */
+ if ((max != 1) && repeat_pull(&aamt))
+ {
+ amt = aamt;
+
+ /* Enforce the maximum */
+ if (amt > max) amt = max;
+
+ /* Enforce the minimum */
+ if (amt < 0) amt = 0;
+
+ /* Use it */
+ return (amt);
+ }
+
+ /* Build a prompt if needed */
+ if (!prompt)
+ {
+ /* Build a prompt */
+ sprintf(tmp, "Quantity (1-%ld): ", (long int) max);
+
+ /* Use that prompt */
+ prompt = tmp;
+ }
+
+
+ /* Default to one */
+ amt = 1;
+
+ /* Build the default */
+ sprintf(buf, "%ld", (long int) amt);
+
+ /* Ask for a quantity */
+ if (!get_string(prompt, buf, 9)) return (0);
+
+ /* Extract a number */
+ amt = atoi(buf);
+
+ /* A letter means "all" */
+ if (isalpha(buf[0])) amt = max;
+
+ /* Enforce the maximum */
+ if (amt > max) amt = max;
+
+ /* Enforce the minimum */
+ if (amt < 0) amt = 0;
+
+
+ if (amt) repeat_push(amt);
+
+ /* Return the result */
+ return (amt);
+}
+
+
+/*
+* Pause for user response XXX XXX XXX
+*/
+void pause_line(int row)
+{
+ prt("", row, 0);
+ put_str("[Press any key to continue]", row, 23);
+ inkey();
+ prt("", row, 0);
+}
+
+
+/*
+* Hack -- special buffer to hold the action of the current keymap
+*/
+static char request_command_buffer[256];
+
+/*
+* Mega-Hack -- characters for which keymaps should be ignored in
+* request_command(). This MUST have at least twice as many characters as
+* there are building actions in the actions[] array in store_info_type.
+*/
+#define MAX_IGNORE_KEYMAPS 12
+char request_command_ignore_keymaps[MAX_IGNORE_KEYMAPS];
+
+/*
+* Mega-Hack -- flag set by do_cmd_{inven,equip}() to allow keymaps in
+* auto-command mode.
+*/
+bool_ request_command_inven_mode = FALSE;
+
+
+/*
+* Request a command from the user.
+*
+* Sets p_ptr->command_cmd, p_ptr->command_dir, p_ptr->command_rep,
+* p_ptr->command_arg. May modify p_ptr->command_new.
+*
+* Note that "caret" ("^") is treated specially, and is used to
+* allow manual input of control characters. This can be used
+* on many machines to request repeated tunneling (Ctrl-H) and
+* on the Macintosh to request "Control-Caret".
+*
+* Note that "backslash" is treated specially, and is used to bypass any
+* keymap entry for the following character. This is useful for macros.
+*
+* Note that this command is used both in the dungeon and in
+* stores, and must be careful to work in both situations.
+*
+* Note that "p_ptr->command_new" may not work any more. XXX XXX XXX
+*/
+void request_command(int shopping)
+{
+ int i;
+
+ s16b cmd;
+ char cmd_char;
+
+ int mode;
+
+ cptr act;
+
+
+ /* Keymap mode */
+ mode = get_keymap_mode();
+
+ /* No command yet */
+ command_cmd = 0;
+
+ /* No "argument" yet */
+ command_arg = 0;
+
+ /* No "direction" yet */
+ command_dir = 0;
+
+
+ /* Get command */
+ while (1)
+ {
+ /* Hack -- auto-commands */
+ if (command_new)
+ {
+ /* Flush messages */
+ msg_print(NULL);
+
+ /* Use auto-command */
+ cmd = command_new;
+
+ /* Forget it */
+ command_new = 0;
+
+ /* Hack - bypass keymaps, unless asked not to */
+ if (!inkey_next && !request_command_inven_mode)
+ {
+ inkey_next = "";
+ }
+
+ /* Mega-Hack -- turn off this flag immediately */
+ request_command_inven_mode = FALSE;
+ }
+
+ /* Get a keypress in "command" mode */
+ else
+ {
+ /* Hack -- no flush needed */
+ msg_flag = FALSE;
+
+ /* Activate "command mode" */
+ inkey_flag = TRUE;
+
+ /* Get a command */
+ cmd = inkey();
+ }
+
+ /* Clear top line */
+ prt("", 0, 0);
+
+
+ /* Command Count */
+ if (cmd == '0')
+ {
+ int old_arg = command_arg;
+
+ /* Reset */
+ command_arg = 0;
+
+ /* Begin the input */
+ prt("Count: ", 0, 0);
+
+ /* Get a command count */
+ while (1)
+ {
+ /* Get a new keypress */
+ cmd = inkey();
+
+ /* Simple editing (delete or backspace) */
+ if ((cmd == 0x7F) || (cmd == KTRL('H')))
+ {
+ /* Delete a digit */
+ command_arg = command_arg / 10;
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Actual numeric data */
+ else if (cmd >= '0' && cmd <= '9')
+ {
+ /* Stop count at 9999 */
+ if (command_arg >= 1000)
+ {
+ /* Warn */
+ bell();
+
+ /* Limit */
+ command_arg = 9999;
+ }
+
+ /* Increase count */
+ else
+ {
+ /* Incorporate that digit */
+ command_arg = command_arg * 10 + D2I(cmd);
+ }
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Exit on "unusable" input */
+ else
+ {
+ break;
+ }
+ }
+
+ /* Hack -- Handle "zero" */
+ if (command_arg == 0)
+ {
+ /* Default to 99 */
+ command_arg = 99;
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Hack -- Handle "old_arg" */
+ if (old_arg != 0)
+ {
+ /* Restore old_arg */
+ command_arg = old_arg;
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Hack -- white-space means "enter command now" */
+ if ((cmd == ' ') || (cmd == '\n') || (cmd == '\r'))
+ {
+ /* Get a real command */
+ bool_ temp = get_com("Command: ", &cmd_char);
+ cmd = cmd_char;
+
+ if (!temp)
+ {
+ /* Clear count */
+ command_arg = 0;
+
+ /* Continue */
+ continue;
+ }
+ }
+ }
+
+
+ /* Allow "keymaps" to be bypassed */
+ if (cmd == '\\')
+ {
+ /* Get a real command */
+ (void)get_com("Command: ", &cmd_char);
+
+ cmd = cmd_char;
+
+ /* Hack -- bypass keymaps */
+ if (!inkey_next) inkey_next = "";
+ }
+
+
+ /* Allow "control chars" to be entered */
+ if (cmd == '^')
+ {
+ /* Get a new command and controlify it */
+ if (get_com("Control: ", &cmd_char)) cmd = KTRL(cmd_char);
+ else cmd = 0;
+ }
+
+
+ /* Look up applicable keymap */
+ act = keymap_act[mode][(byte)(cmd)];
+
+ /* Mega-Hack -- Ignore certain keymaps */
+ if (shopping && cmd > 0)
+ {
+ for (i = 0; i < MAX_IGNORE_KEYMAPS; i++)
+ if (cmd == request_command_ignore_keymaps[i])
+ {
+ act = NULL;
+ break;
+ }
+ }
+
+ /* Apply keymap if not inside a keymap already */
+ if (act && !inkey_next)
+ {
+ /* Install the keymap (limited buffer size) */
+ strnfmt(request_command_buffer, 256, "%s", act);
+
+ /* Start using the buffer */
+ inkey_next = request_command_buffer;
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Paranoia */
+ if (!cmd) continue;
+
+
+ /* Use command */
+ command_cmd = cmd;
+
+ /* Done */
+ break;
+ }
+
+ /* Hack -- Auto-repeat certain commands */
+ if (always_repeat && (command_arg <= 0))
+ {
+ /* Hack -- auto repeat certain commands */
+ if (strchr("TBDoc+", command_cmd))
+ {
+ /* Repeat 99 times */
+ command_arg = 99;
+ }
+ }
+
+ /* Hack -- Scan equipment */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ cptr s;
+
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* No inscription */
+ if (!o_ptr->note) continue;
+
+ /* Obtain the inscription */
+ s = quark_str(o_ptr->note);
+
+ /* Find a '^' */
+ s = strchr(s, '^');
+
+ /* Process preventions */
+ while (s)
+ {
+ /* Check the "restriction" character */
+ if ((s[1] == command_cmd) || (s[1] == '*'))
+ {
+ /* Hack -- Verify command */
+ if (!get_check("Are you sure? "))
+ {
+ /* Hack -- Use space */
+ command_cmd = ' ';
+ }
+ }
+
+ /* Find another '^' */
+ s = strchr(s + 1, '^');
+ }
+ }
+
+
+ /* Hack -- erase the message line. */
+ prt("", 0, 0);
+}
+
+
+
+
+/*
+ * Check a char for "vowel-hood"
+ */
+bool_ is_a_vowel(int ch)
+{
+ switch (ch)
+ {
+ case 'a':
+ case 'e':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'A':
+ case 'E':
+ case 'I':
+ case 'O':
+ case 'U':
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * GH
+ * Called from cmd4.c and a few other places. Just extracts
+ * a direction from the keymap for ch (the last direction,
+ * in fact) byte or char here? I'm thinking that keymaps should
+ * generally only apply to single keys, which makes it no more
+ * than 128, so a char should suffice... but keymap_act is 256...
+ */
+int get_keymap_dir(char ch)
+{
+ int d = 0;
+
+ int mode;
+
+ cptr act;
+
+ cptr s;
+
+
+ /* Already a direction? */
+ if (isdigit(ch))
+ {
+ d = D2I(ch);
+ }
+ else
+ {
+ /* Keymap mode */
+ mode = get_keymap_mode();
+
+ /* Extract the action (if any) */
+ act = keymap_act[mode][(byte)(ch)];
+
+ /* Analyze */
+ if (act)
+ {
+ /* Convert to a direction */
+ for (s = act; *s; ++s)
+ {
+ /* Use any digits in keymap */
+ if (isdigit(*s)) d = D2I(*s);
+ }
+ }
+ }
+
+ /* Paranoia */
+ if (d == 5) d = 0;
+
+ /* Return direction */
+ return (d);
+}
+
+
+#define REPEAT_MAX 20
+
+/* Number of chars saved */
+static int repeat__cnt = 0;
+
+/* Current index */
+static int repeat__idx = 0;
+
+/* Saved "stuff" */
+static int repeat__key[REPEAT_MAX];
+
+
+void repeat_push(int what)
+{
+ /* Too many keys */
+ if (repeat__cnt == REPEAT_MAX) return;
+
+ /* Push the "stuff" */
+ repeat__key[repeat__cnt++] = what;
+
+ /* Prevents us from pulling keys */
+ ++repeat__idx;
+}
+
+
+bool_ repeat_pull(int *what)
+{
+ /* All out of keys */
+ if (repeat__idx == repeat__cnt) return (FALSE);
+
+ /* Grab the next key, advance */
+ *what = repeat__key[repeat__idx++];
+
+ /* Success */
+ return (TRUE);
+}
+
+void repeat_check(void)
+{
+ int what;
+
+ /* Ignore some commands */
+ if (command_cmd == ESCAPE) return;
+ if (command_cmd == ' ') return;
+ if (command_cmd == '\r') return;
+ if (command_cmd == '\n') return;
+
+ /* Repeat Last Command */
+ if (command_cmd == 'n')
+ {
+ /* Reset */
+ repeat__idx = 0;
+
+ /* Get the command */
+ if (repeat_pull(&what))
+ {
+ /* Save the command */
+ command_cmd = what;
+ }
+ }
+
+ /* Start saving new command */
+ else
+ {
+ /* Reset */
+ repeat__cnt = 0;
+ repeat__idx = 0;
+
+ what = command_cmd;
+
+ /* Save this command */
+ repeat_push(what);
+ }
+}
+
+
+/*
+ * Read a number at a specific location on the screen
+ *
+ * Allow numbers of any size and save the last keypress.
+ */
+u32b get_number(u32b def, u32b max, int y, int x, char *cmd)
+{
+ u32b res = def;
+
+ /* Player has not typed anything yet */
+ bool_ no_keys = TRUE;
+
+ /* Begin the input with default */
+ prt(format("%lu", def), y, x);
+
+ /* Get a command count */
+ while (1)
+ {
+ /* Get a new keypress */
+ *cmd = inkey();
+
+ /* Simple editing (delete or backspace) */
+ if ((*cmd == 0x7F) || (*cmd == KTRL('H')))
+ {
+ /* Override the default */
+ no_keys = FALSE;
+
+ /* Delete a digit */
+ res = res / 10;
+
+ prt(format("%lu", res), y, x);
+ }
+
+ /* Actual numeric data */
+ else if (*cmd >= '0' && *cmd <= '9')
+ {
+ /* Override the default */
+ if (no_keys)
+ {
+ no_keys = FALSE;
+ res = 0;
+ }
+
+ /* Don't overflow */
+ if (((u32b)(0 - 1) - D2I(*cmd)) / 10 < res)
+ {
+ /* Warn */
+ bell();
+
+ /* Limit */
+ res = (max + 1 == 0) ? (u32b)(0 - 1) : max;
+ }
+
+ /* Stop count at maximum */
+ else if (res * 10 + D2I(*cmd) > max)
+ {
+ /* Warn */
+ bell();
+
+ /* Limit */
+ res = max;
+ }
+
+ /* Increase count */
+ else
+ {
+ /* Incorporate that digit */
+ res = res * 10 + D2I(*cmd);
+ }
+
+ /* Show current count */
+ prt(format("%lu", res), y, x);
+ }
+
+ /* Escape cancels */
+ else if (*cmd == ESCAPE)
+ {
+ res = 0;
+ break;
+ }
+
+ /* Exit on "unusable" input */
+ else
+ {
+ break;
+ }
+ }
+
+ return res;
+}
+
+/*
+ * Allow the user to select multiple items without pressing '0'
+ */
+void get_count(int number, int max)
+{
+ char cmd;
+
+ /* Use the default */
+ command_arg = number;
+
+ /* Hack -- Optional flush */
+ if (flush_command) flush();
+
+ /* Clear top line */
+ prt("", 0, 0);
+
+ /* Begin the input */
+ prt("How many?", 0, 0);
+
+ /* Actually get a number */
+ command_arg = get_number(command_arg, max, 0, 10, &cmd);
+
+ prt("", 0, 0);
+}
+
+byte count_bits(u32b array)
+{
+ byte k = 0, i;
+
+ if (array)
+ for (i = 0; i < 32; i++)
+ if (array & (1 << i)) k++;
+
+ return k;
+}
+
+/* Return the lowered string */
+void strlower(char *buf)
+{
+ u16b i;
+
+ for (i = 0; (buf[i] != 0) && (i < 256) ;i++)
+ {
+ if (isupper(buf[i])) buf[i] = tolower(buf[i]);
+ }
+}
+
+/*
+ * Given monster name as string, return the index in r_info array. Name
+ * must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter. -GSN-
+ */
+
+int test_monster_name(cptr name)
+{
+ int i;
+
+ /* Scan the monsters */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+ cptr mon_name = r_name + r_ptr->name;
+
+ /* If name matches, give us the number */
+ if (stricmp(name, mon_name) == 0) return (i);
+ }
+ return (0);
+}
+int test_mego_name(cptr name)
+{
+ int i;
+
+ /* Scan the monsters */
+ for (i = 1; i < max_re_idx; i++)
+ {
+ monster_ego *re_ptr = &re_info[i];
+ cptr mon_name = re_name + re_ptr->name;
+
+ /* If name matches, give us the number */
+ if (stricmp(name, mon_name) == 0) return (i);
+ }
+ return (0);
+}
+
+/*
+ * Given item name as string, return the index in k_info array. Name
+ * must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter. -DG-
+ */
+
+int test_item_name(cptr name)
+{
+ int i;
+
+ /* Scan the items */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+ cptr obj_name = k_name + k_ptr->name;
+
+ /* If name matches, give us the number */
+ if (stricmp(name, obj_name) == 0) return (i);
+ }
+ return (0);
+}
+
+/*
+ * Break scalar time
+ */
+s32b bst(s32b what, s32b t)
+{
+ s32b turns = t + (10 * DAY_START);
+
+ switch (what)
+ {
+ case MINUTE:
+ return ((turns / 10 / MINUTE) % 60);
+ case HOUR:
+ return (turns / 10 / (HOUR) % 24);
+ case DAY:
+ return (turns / 10 / (DAY) % 365);
+ case YEAR:
+ return (turns / 10 / (YEAR));
+ default:
+ return (0);
+ }
+}
+
+cptr get_month_name(int day, bool_ full, bool_ compact)
+{
+ int i = 8;
+ static char buf[40];
+
+ /* Find the period name */
+ while ((i > 0) && (day < month_day[i]))
+ {
+ i--;
+ }
+
+ switch (i)
+ {
+ /* Yestare/Mettare */
+ case 0:
+ case 8:
+ {
+ char buf2[20];
+
+ sprintf(buf2, "%s", get_day(day + 1));
+ if (full) sprintf(buf, "%s (%s day)", month_name[i], buf2);
+ else sprintf(buf, "%s", month_name[i]);
+ break;
+ }
+ /* 'Normal' months + Enderi */
+ default:
+ {
+ char buf2[20];
+ char buf3[20];
+
+ sprintf(buf2, "%s", get_day(day + 1 - month_day[i]));
+ sprintf(buf3, "%s", get_day(day + 1));
+
+ if (full) sprintf(buf, "%s day of %s (%s day)", buf2, month_name[i], buf3);
+ else if (compact) sprintf(buf, "%s day of %s", buf2, month_name[i]);
+ else sprintf(buf, "%s %s", buf2, month_name[i]);
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+cptr get_day(int day)
+{
+ static char buf[20];
+ cptr p = "th";
+
+ if ((day / 10) == 1) ;
+ else if ((day % 10) == 1) p = "st";
+ else if ((day % 10) == 2) p = "nd";
+ else if ((day % 10) == 3) p = "rd";
+
+ sprintf(buf, "%d%s", day, p);
+ return (buf);
+}
+
+cptr get_player_race_name(int pr, int ps)
+{
+ static char buf[50];
+
+ if (ps)
+ {
+ if (race_mod_info[ps].place) sprintf(buf, "%s %s", race_info[pr].title + rp_name, race_mod_info[ps].title + rmp_name);
+ else sprintf(buf, "%s %s", race_mod_info[ps].title + rmp_name, race_info[pr].title + rp_name);
+ }
+ else
+ {
+ sprintf(buf, "%s", race_info[pr].title + rp_name);
+ }
+
+ return (buf);
+}
+
+/*
+ * Ask to select an item in a list
+ */
+int ask_menu(cptr ask, char **items, int max)
+{
+ int ret = -1, i, start = 0;
+ char c;
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ while (TRUE)
+ {
+ /* Display list */
+ Term_load();
+ Term_save();
+ prt(ask, 0, 0);
+ for (i = start; (i < max) && (i < start + 20); i++)
+ {
+ prt(format("%c) %s", I2A(i - start), items[i]), i - start + 1, 0);
+ }
+
+ /* Wait for user input */
+ c = inkey();
+
+ /* Leave the screen */
+ if (c == ESCAPE) break;
+
+ /* Scroll */
+ else if (c == '+')
+ {
+ if (start + 20 < max)
+ start += 20;
+ continue;
+ }
+
+ /* Scroll */
+ else if (c == '-')
+ {
+ start -= 20;
+ if (start < 0) start = 0;
+ continue;
+ }
+
+ /* Good selection */
+ else
+ {
+ c = tolower(c);
+ if (A2I(c) + start >= max)
+ {
+ bell();
+ continue;
+ }
+ if (A2I(c) + start < 0)
+ {
+ bell();
+ continue;
+ }
+
+ ret = A2I(c) + start;
+ break;
+ }
+ }
+
+ /* Load the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ return ret;
+}
+
+/*
+ * Determine if string "t" is a prefix of string "s"
+ */
+bool_ prefix(cptr s, cptr t)
+{
+ /* Paranoia */
+ if (!s || !t)
+ {
+ if (alert_failure) message_add(MESSAGE_MSG, "prefix() called with null argument!", TERM_RED);
+ return FALSE;
+ }
+
+ /* Scan "t" */
+ while (*t)
+ {
+ /* Compare content and length */
+ if (*t++ != *s++) return (FALSE);
+ }
+
+ /* Matched, we have a prefix */
+ return (TRUE);
+}
+
+/*
+ * Rescale a value
+ */
+s32b value_scale(int value, int vmax, int max, int min)
+{
+ s32b full_max = max - min;
+
+ value = (value * full_max) / vmax;
+ value += min;
+
+ return value;
+}
+
+/*
+ * Displays a box
+ */
+void draw_box(int y, int x, int h, int w)
+{
+ int i, j;
+
+ for (i = x + 1; i < x + w; i++)
+ for (j = y + 1; j < y + h; j++)
+ Term_putch(i, j, TERM_L_BLUE, ' ');
+
+ for (i = x; i < x + w; i++)
+ {
+ c_put_str(TERM_L_BLUE, "-", y, i);
+ c_put_str(TERM_L_BLUE, "-", y + h, i);
+ }
+ for (i = y; i < y + h; i++)
+ {
+ c_put_str(TERM_L_BLUE, "|", i, x);
+ c_put_str(TERM_L_BLUE, "|", i, x + w);
+ }
+ Term_putch(x, y, TERM_L_BLUE, '/');
+ Term_putch(x + w, y, TERM_L_BLUE, '\\');
+ Term_putch(x, y + h, TERM_L_BLUE, '\\');
+ Term_putch(x + w, y + h, TERM_L_BLUE, '/');
+}
+
+
+/*
+ * Displays a scrollable boxed list with a selected item
+ */
+void display_list(int y, int x, int h, int w, cptr title, cptr *list, int max, int begin, int sel, byte sel_color)
+{
+ int i;
+
+ draw_box(y, x, h, w);
+ c_put_str(TERM_L_BLUE, title, y, x + ((w - strlen(title)) / 2));
+
+ for (i = 0; i < h - 1; i++)
+ {
+ byte color = TERM_WHITE;
+
+ if (i + begin >= max) break;
+
+ if (i + begin == sel) color = sel_color;
+ c_put_str(color, list[i + begin], y + 1 + i, x + 1);
+ }
+}
+
+/*
+ * Creates an input box
+ */
+bool_ input_box(cptr text, int y, int x, char *buf, int max)
+{
+ int smax = strlen(text);
+
+ if (max > smax) smax = max;
+ smax++;
+
+ draw_box(y - 1, x - (smax / 2), 3, smax);
+ c_put_str(TERM_WHITE, text, y, x - (strlen(text) / 2));
+
+ Term_gotoxy(x - (smax / 2) + 1, y + 1);
+ return askfor_aux(buf, max);
+}
+
+/*
+ * Creates a msg bbox and ask a question
+ */
+char msg_box(cptr text, int y, int x)
+{
+ if (x == -1)
+ {
+ int wid = 0, hgt = 0;
+ Term_get_size(&wid, &hgt);
+ x = wid / 2;
+ y = hgt / 2;
+ }
+
+ draw_box(y - 1, x - ((strlen(text) + 1) / 2), 2, strlen(text) + 1);
+ c_put_str(TERM_WHITE, text, y, x - ((strlen(text) + 1) / 2) + 1);
+ return inkey();
+}
+
+/* Rescale a value */
+s32b rescale(s32b x, s32b max, s32b new_max)
+{
+ return (x * new_max) / max;
+}
+
+/* Nicer wrapper around TERM_XTRA_SCANSUBDIR */
+void scansubdir(cptr dir)
+{
+ strnfmt(scansubdir_dir, 1024, "%s", dir);
+ Term_xtra(TERM_XTRA_SCANSUBDIR, 0);
+}
+
+/*
+ * Timers
+ */
+timer_type *new_timer(cptr callback, s32b delay)
+{
+ timer_type *t_ptr;
+
+ MAKE(t_ptr, timer_type);
+ t_ptr->next = gl_timers;
+ gl_timers = t_ptr;
+
+ t_ptr->callback = string_make(callback);
+ t_ptr->delay = delay;
+ t_ptr->countdown = delay;
+ t_ptr->enabled = FALSE;
+
+ return t_ptr;
+}
+
+void del_timer(timer_type *t_ptr)
+{
+ timer_type *i, *old;
+
+ old = NULL;
+ for (i = gl_timers; (i != NULL) && (i != t_ptr); old = i, i = i->next)
+ ;
+ if (i)
+ {
+ if (old == NULL)
+ gl_timers = t_ptr->next;
+ else
+ old->next = t_ptr->next;
+ string_free(t_ptr->callback);
+ FREE(t_ptr, timer_type);
+ }
+ else
+ cmsg_print(TERM_VIOLET, "Unknown timer!");
+}
+
+int get_keymap_mode()
+{
+ if (rogue_like_commands)
+ {
+ return KEYMAP_MODE_ROGUE;
+ }
+ else
+ {
+ return KEYMAP_MODE_ORIG;
+ }
+}
diff --git a/src/util.pkg b/src/util.pkg
new file mode 100644
index 00000000..39f70b40
--- /dev/null
+++ b/src/util.pkg
@@ -0,0 +1,2683 @@
+/* File: util.pkg */
+
+/*
+ * Purpose: Lua interface defitions for miscellaneous routines.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+$#include "plots.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @def TRUE */
+#define TRUE
+
+/** @def FALSE */
+#define FALSE
+
+
+/** @def ESCAPE */
+#define ESCAPE '\033'
+
+/** @name Terminal Colours
+ * @{
+ */
+/** @def TERM_DARK
+ * @note 'd' (0,0,0)
+ */
+#define TERM_DARK 0 /* 'd' */
+/** @def TERM_WHITE
+ * @note 'w' (4,4,4)
+ */
+#define TERM_WHITE 1 /* 'w' */
+/** @def TERM_SLATE
+ * @note 's' (2,2,2)
+ */
+#define TERM_SLATE 2 /* 's' */
+/** @def TERM_ORANGE
+ * @note 'o' (4,2,0)
+ */
+#define TERM_ORANGE 3 /* 'o' */
+/** @def TERM_RED
+ * @note 'r' (3,0,0)
+ */
+#define TERM_RED 4 /* 'r' */
+/** @def TERM_GREEN
+ * @note 'g' (0,2,1)
+ */
+#define TERM_GREEN 5 /* 'g' */
+/** @def TERM_BLUE
+ * @note 'b' (0,0,4)
+ */
+#define TERM_BLUE 6 /* 'b' */
+/** @def TERM_UMBER
+ * @note 'u' (2,1,0)
+ */
+#define TERM_UMBER 7 /* 'u' */
+/** @def TERM_L_DARK
+ * @note 'D' (1,1,1)
+ */
+#define TERM_L_DARK 8 /* 'D' */
+/** @def TERM_L_WHITE
+ * @note 'W' (3,3,3)
+ */
+#define TERM_L_WHITE 9 /* 'W' */
+/** @def TERM_VIOLET
+ * @note 'v' (4,0,4)
+ */
+#define TERM_VIOLET 10 /* 'v' */
+/** @def TERM_YELLOW
+ * @note 'y' (4,4,0)
+ */
+#define TERM_YELLOW 11 /* 'y' */
+/** @def TERM_L_RED
+ * @note 'R' (4,0,0)
+ */
+#define TERM_L_RED 12 /* 'R' */
+/** @def TERM_L_GREEN
+ * @note 'G' (0,4,0)
+ */
+#define TERM_L_GREEN 13 /* 'G' */
+/** @def TERM_L_BLUE
+ * @note 'B' (0,4,4)
+ */
+#define TERM_L_BLUE 14 /* 'B' */
+/** @def TERM_L_UMBER
+ * @note 'U' (3,2,1)
+ */
+#define TERM_L_UMBER 15 /* 'U' */
+/** @} */
+
+/** @name Event Hooks
+ * @{
+ */
+/** @def HOOK_MONSTER_DEATH
+ * @brief Monster dies.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @note (see file xtra2.c)
+ */
+#define HOOK_MONSTER_DEATH 0
+
+/** @def HOOK_OPEN
+ * @brief Open door or chest.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file cmd2.c)
+ */
+#define HOOK_OPEN 1
+
+/** @def HOOK_GEN_QUEST
+ * @brief Generate quest level.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file generate.c)
+ */
+#define HOOK_GEN_QUEST 2
+
+/** @def HOOK_END_TURN
+ * @brief Turn ends.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file dungeon.c)
+ */
+#define HOOK_END_TURN 3
+
+/** @def HOOK_FEELING
+ * @brief Display level feeling.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @return Boolean \n TRUE if a level feeling was displayed, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, then no other feelings are displayed and
+ * do_cmd_feeling() returns.
+ * @note (see file cmd4.c)
+ */
+#define HOOK_FEELING 4
+
+/** @def HOOK_NEW_MONSTER
+ * @brief Generate monster.\n
+ * @param Number r_idx \n index of monster in monster race (r_info) array.
+ * @brief Monster index
+ * @return Boolean \n TRUE if monster is not allowed to be created,
+ * otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, then the monster is "killed".
+ * @note (see file monster2.c)
+ */
+#define HOOK_NEW_MONSTER 5
+
+/** @def HOOK_GEN_LEVEL
+ * @brief Generate dungeon level.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file generate.c)
+ */
+#define HOOK_GEN_LEVEL 6
+
+/** @def HOOK_BUILD_ROOM1
+ * @brief Generate room (type 1 - normal rectangular room).\n
+ * @param Number by0 \n y-coordinate of dungeon block where room is built.
+ * @brief Block y-coordinate
+ * @param Number bx0 \n x-coordinate of dungeon block where room is built.
+ * @brief Block x-coordinate
+ * @return Boolean \n TRUE if room was created, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, then the room has been built and build_type1()
+ * returns.
+ * @note (see file generate.c)
+ */
+#define HOOK_BUILD_ROOM1 7
+
+/** @def HOOK_NEW_LEVEL
+ * @brief Start dungeon level.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file dungeon.c)
+ */
+#define HOOK_NEW_LEVEL 8
+
+/** @def HOOK_QUEST_FINISH
+ * @brief Quest finished.\n
+ * @param Number plot \n a plot from the plots array.
+ * @brief Plot
+ * @note (see file bldg.c)
+ */
+#define HOOK_QUEST_FINISH 9
+
+/** @def HOOK_QUEST_FAIL
+ * @brief Quest failed.\n
+ * @param Number plot \n a plot from the plots array.
+ * @brief Plot
+ * @note (see file bldg.c)
+ */
+#define HOOK_QUEST_FAIL 10
+
+/** @def HOOK_GIVE
+ * @brief Give item to monster.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @param Number item \n the item to be given.
+ * @brief Item number
+ * @return Boolean \n TRUE if item was given to monster, otherwise FALSE.
+ * @note
+ * If the hook returns FALSE, then the message "The monster does not want
+ * your item." is displayed.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_GIVE 11
+
+/** @def HOOK_CHAR_DUMP
+ * @brief Add a line to the character sheet.
+ * @note (see files.c)
+ */
+#define HOOK_CHAR_DUMP 12
+
+/** @def HOOK_INIT_QUEST
+ * @brief Quest initialised.\n
+ * @param Number plot \n a plot from the plots array.
+ * @brief Plot
+ * @return Boolean \n TRUE if quest was not initialised, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, castle_quest() returns FALSE.
+ * @note (see file bldg.c)
+ */
+#define HOOK_INIT_QUEST 13
+
+/** @def HOOK_WILD_GEN
+ * @brief Generate wilderness.\n
+ * @param Number wilderness \n if TRUE, then this is overhead wilderness
+ * processing, otherwise it is regular wilderness processing.
+ * @brief Overhead?
+ * @note (see file wild.c)
+ */
+#define HOOK_WILD_GEN 14
+
+/** @def HOOK_DROP
+ * @brief Drop an item.\n
+ * @param Number item \n the item to drop.
+ * @brief Item number
+ * @return Boolean \n TRUE if item was dropped, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_drop() returns, otherwise the function
+ * continues.
+ * @note (see file cmd3.c)
+ */
+#define HOOK_DROP 15
+
+/** @def HOOK_IDENTIFY
+ * @brief Identfy an item.\n
+ * @param Number item \n the item to identify.
+ * @brief Item number
+ * @param String type \n "normal" to identify the item, or "full" to fully
+ * identify an item.
+ * @brief Type
+ * @note (see files spells1.c, spells2.c)
+ */
+#define HOOK_IDENTIFY 16
+
+/** @def HOOK_MOVE
+ * @brief Player moves.\n
+ * @param Number y \n the y-coordinate of the new location.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the new location.
+ * @brief X-coordinate
+ * @return Boolean \n TRUE if player is not allowed to move, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, move_player_aux() returns, otherwise the function
+ * continues.
+ * @note (see file cmd1.c)
+ */
+#define HOOK_MOVE 17
+
+/** @def HOOK_STAIR
+ * @brief Player uses stairs.\n
+ * @param String direction \n "up" if the player is going up stairs, or
+ * "down" if the player is going down stairs.
+ * @brief Direction
+ * @return Boolean \n TRUE if player is not allowed to use stairs, otherwise
+ * FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_go_up() or do_cmd_go_down() returns,
+ * otherwise the function continues.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_STAIR 18
+
+/** @def HOOK_MONSTER_AI
+ * @brief Monster moves.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @return Boolean \n TRUE if monster AI was applied, otherwise FALSE.
+ * @return Number y2 \n New y-coordinate of monster target.
+ * @return Number x2 \n New x-coordinate of monster target.
+ * @note
+ * If the hook returns TRUE, the monster moves toward the hook position.
+ * @note (see file melee2.c)
+ */
+#define HOOK_MONSTER_AI 19
+
+/** @def HOOK_PLAYER_LEVEL
+ * @brief Player gains (or loses) a level.\n
+ * @param Number gained \n the number of levels gained (or lost).
+ * @brief Levels gained
+ * @note (see file xtra2.c)
+ */
+#define HOOK_PLAYER_LEVEL 20
+
+/** @def HOOK_WIELD
+ * @brief Player wields an item.\n
+ * @param Number item \n the item to wield.
+ * @brief Item number
+ * @return Boolean \n TRUE if item was not wielded, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_wield() returns, otherwise the function
+ * continues.
+ * @note (see file cmd3.c)
+ */
+#define HOOK_WIELD 21
+
+/** @def HOOK_INIT
+ * @brief Game initialised.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_INIT 22
+
+/** @def HOOK_QUAFF
+ * @brief Player quaffs a potion.\n
+ * @param Object o_ptr \n the potion to quaff.
+ * @brief Potion
+ * @return Boolean \n TRUE if potion was quaffed, otherwise FALSE.
+ * @return Number ident \n TRUE if the potion was identifed, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "potion identified" flag.
+ * @note (see file cmd6.c)
+ */
+#define HOOK_QUAFF 23
+
+/** @def HOOK_AIM */
+#define HOOK_AIM 24
+
+/** @def HOOK_USE */
+#define HOOK_USE 25
+
+/** @def HOOK_ACTIVATE
+ * @brief Player activates an item.\n
+ * @param Number item \n the item to activate.
+ * @brief Item number
+ * @return Boolean \n TRUE if item was activated, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_activate() returns, otherwise the function
+ * continues.
+ * @note (see file cmd6.c)
+ */
+#define HOOK_ACTIVATE 26
+
+/** @def HOOK_ZAP
+ * @brief Player zaps a rod.\n
+ * @param Number tval \n type of rod to zap.
+ * @brief Type
+ * @param Number sval \n sub-type of rod to zap.
+ * @brief Sub-type
+ * @note (see file cmd6.c)
+ */
+#define HOOK_ZAP 27
+
+/** @def HOOK_READ
+ * @brief Player reads a scroll.\n
+ * @param Object o_ptr \n the scroll to read.
+ * @brief Scroll
+ * @return Boolean \n TRUE if scroll was read, otherwise FALSE.
+ * @return Number used_up \n TRUE if the scroll was used up, otherwise FALSE.
+ * @return Number ident \n TRUE if the scroll was identifed, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "scroll used up" and
+ * "scroll identified" flags.
+ * @note (see file cmd6.c)
+ */
+#define HOOK_READ 28
+
+/** @def HOOK_CALC_BONUS
+ * @brief Calculate player "state" bonuses.
+ * @note (see xtra1.c)
+ */
+#define HOOK_CALC_BONUS 29
+
+/** @def HOOK_CALC_BONUS
+ * @brief Calculate player "state" bonuses, after all calcs are done.
+ * @note (see xtra1.c)
+ */
+#define HOOK_CALC_BONUS_END 77
+
+/** @def HOOK_CALC_POWERS
+ * @brief Calculate player powers.
+ * @note (see xtra1.c)
+ */
+#define HOOK_CALC_POWERS 30
+
+/** @def HOOK_KEYPRESS
+ * @brief User enters a command.\n
+ * @param Number command \n the pressed key (command_cmd).
+ * @brief Command
+ * @return Boolean \n TRUE if special processing was done, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, process_command() returns, otherwise the function
+ * continues.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_KEYPRESS 31
+
+/** @def HOOK_CHAT
+ * @brief Player chats to monster.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @return Boolean \n TRUE if monster chats, otherwise FALSE.
+ * @note
+ * If the hook returns FALSE, the message "There is no monster there." is
+ * printed.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_CHAT 32
+
+/** @def HOOK_MON_SPEAK
+ * @brief Monster speaks.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @param String m_name \n name of the monster.
+ * @brief Monster name
+ * @return Boolean \n TRUE if monster speaks, otherwise FALSE.
+ * @note
+ * If the hook returns FALSE, the monster may say something else.
+ * @note (see file melee2.c)
+ */
+#define HOOK_MON_SPEAK 33
+
+/** @def HOOK_MKEY
+ * @brief Player uses skill.\n
+ * @param Number x_idx \n the skill to execute.
+ * @brief Skill index
+ * @note (see file skills.c)
+ */
+#define HOOK_MKEY 34
+
+/** @def HOOK_BIRTH_OBJECTS
+ * @brief Player receives objects at birth.
+ * @note (see file birth.c)
+ */
+#define HOOK_BIRTH_OBJECTS 35
+
+/** @def HOOK_ACTIVATE_DESC
+ * @brief Display activation description.\n
+ * @param Object o_ptr \n the item to activate.
+ * @brief Object
+ * @return Boolean \n TRUE if item has an activation, otherwise FALSE.
+ * @return String desc \n the activation description.
+ * @note
+ * If the hook returns TRUE, item_activation() returns the hook's activation
+ * description.
+ * @note (see file object1.c)
+ */
+#define HOOK_ACTIVATE_DESC 36
+
+/** @def HOOK_INIT_GAME
+ * @brief Game initialised.\n
+ * @param String when \n "begin" if done at the start of game initialisation,
+ * or "end" if done at end of game initialisation.
+ * @brief When?
+ * @note (see file init2.c)
+ */
+#define HOOK_INIT_GAME 37
+
+/** @def HOOK_ACTIVATE_POWER
+ * @brief Player activates a power.\n
+ * @param Number power \n the power to activate.
+ * @brief Power
+ * @return Boolean \n TRUE if power was activated, otherwise FALSE.
+ * @note
+ * If the hook returns FALSE, power_activate() displays the message
+ * "Warning power_activate() called with invalid power(xx)." where
+ * xx = power.
+ * @note (see file powers.c)
+ */
+#define HOOK_ACTIVATE_POWER 38
+
+/** @def HOOK_ITEM_NAME
+ * @brief Get an item name.\n
+ * @param Object o_ptr \n the item whose name is required.
+ * @brief Object
+ * @return Boolean \n TRUE if name was found, otherwise FALSE.
+ * @return String basenm \n The item name.
+ * @return String modstr \n The item modifier string.
+ * @note (see file object1.c)
+ */
+#define HOOK_ITEM_NAME 39
+
+/** @def HOOK_SAVE_GAME
+ * @brief Save the game.
+ * @note (see file loadsave.c)
+ */
+#define HOOK_SAVE_GAME 40
+
+/** @def HOOK_LOAD_GAME
+ * @brief Load the game.
+ * @note (see file loadsave.c)
+ */
+#define HOOK_LOAD_GAME 41
+
+/** @def HOOK_LEVEL_REGEN
+ * @brief Start generation of a special level.
+ * @note (see file generate.c)
+ */
+#define HOOK_LEVEL_REGEN 42
+
+/** @def HOOK_LEVEL_END_GEN
+ * @brief End generation of a special level.
+ * @note (see file generate.c)
+ */
+#define HOOK_LEVEL_END_GEN 43
+
+/** @def HOOK_BUILDING_ACTION
+ * @brief Player performs an action in a building.\n
+ * @param Number action \n the action performed in the building
+ * @brief Action flag
+ * @return Boolean \n TRUE if player performed the action, otherwise FALSE.
+ * @return Number paid \n TRUE if player paid to perform the action, otherwise
+ * FALSE.
+ * @return Number recreate \n TRUE if something is recreated, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "paid" and "recreate" flags.
+ * @note (see file bldg.c)
+ */
+#define HOOK_BUILDING_ACTION 44
+
+/** @def HOOK_PROCESS_WORLD
+ * @brief Update world every ten turns.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_PROCESS_WORLD 45
+
+/** @def HOOK_WIELD_SLOT
+ * @brief Find equipment slot for object.\n
+ * @param Object o_ptr \n the object to wield.
+ * @brief Object
+ * @param Number ideal \n TRUE if current body and stuff is ignore, otherwise
+ * FALSE.
+ * @return Boolean \n TRUE if hook processed the object, otherwise FALSE.
+ * @return Number slot \n The equipent slot where the object will go (-1 if
+ * there are no available slots).
+ * @note
+ * If the hook returns TRUE, wield_slot_ideal() returns the slot from the hook.
+ * @note (see file objects1.c)
+ */
+#define HOOK_WIELD_SLOT 46
+
+/** @def HOOK_STORE_STOCK
+ * @brief Stock a store.\n
+ * @param Number st_idx \n the index of the store in st_info array.
+ * @brief Store index
+ * @param String name \n the name of the store.
+ * @brief Store name
+ * @param Number level \n the "dungeon level" of the store.
+ * @brief Store level
+ * @return Boolean \n TRUE if hook has selected an object, otherwise FALSE.
+ * @return Object q_ptr \n The item to be stocked in the store.
+ * @note
+ * If the hook returns TRUE, store_create() will create the hook's object and
+ * put it in the store.
+ * @note (see file store.c)
+ */
+#define HOOK_STORE_STOCK 47
+
+/** @def HOOK_STORE_BUY
+ * @brief Store buys an item.\n
+ * @param Number st_idx \n the index of the store in st_info array.
+ * @brief Store index
+ * @param String name \n the name of the store.
+ * @brief Store name
+ * @param Object o_ptr \n the object to buy.
+ * @brief Object
+ * @return Boolean \n TRUE if the hook has processed the object, otherwise
+ * FALSE.
+ * @return Number buy \n TRUE if the store will buy the object, otherwise
+ * FALSE.
+ * @note
+ * If the hook returns TRUE, store_will_buy() will return "buy".
+ * @note (see file store.c)
+ */
+#define HOOK_STORE_BUY 48
+
+/** @def HOOK_GEN_LEVEL_BEGIN
+ * @brief Generate a random dungeon level.
+ * @note (see file generate.c)
+ */
+#define HOOK_GEN_LEVEL_BEGIN 49
+
+/** @def HOOK_GET
+ * @brief Player gets an object.\n
+ * @param Object o_ptr \n the object to get.
+ * @brief Object
+ * @param Number o_idx \n the index of the object in o_list array.
+ * @brief Object index
+ * @return Boolean \n TRUE if hooks processes the object, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, object_pickup() returns, otherwise the function
+ * continues.
+ * @note (see object1.c)
+ */
+#define HOOK_GET 50
+
+/** @def HOOK_REDRAW
+ * @brief Redraw the screen.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_REDRAW 51
+
+/** @def HOOK_RECALC_SKILLS
+ * @brief Recalculate player skills.
+ * @note (see skills.c)
+ */
+#define HOOK_RECALC_SKILLS 52
+
+/** @def HOOK_ENTER_DUNGEON
+ * @brief Player goes down one dungeon level.\n
+ * @param Number special \n special information for player's dungeon grid.
+ * @brief Special info
+ * @return Boolean \n TRUE if the hook prevents the player going down,
+ * otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the player remains on the current dungeon level
+ * and do_cmd_go_down() returns.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_ENTER_DUNGEON 53
+
+/** @def HOOK_FIRE
+ * @brief Player fires an object (bow slot of inventory).\n
+ * @param Object \n the object to fire.
+ * @brief Object
+ * @return Boolean \n TRUE if the hook has fired the object, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, process_command() returns.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_FIRE 54
+
+/** @def HOOK_EAT
+ * @brief Player eats.\n
+ * @param Object o_ptr \n the object the player eats.
+ * @brief Object
+ * @return Boolean \n TRUE if hook processes the object, otherwise FALSE.
+ * @return Number ident \n TRUE if the object was identified, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "food identified" flag.
+ * @note (see file cmd6.c)
+ */
+#define HOOK_EAT 55
+
+/** @def HOOK_DIE
+ * @brief Player dies.
+ * @return Boolean \n TRUE if player does not die, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the player cheats death.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_DIE 56
+
+/** @def HOOK_CALC_HP
+ * @brief Recalculate player HP (hit points).\n
+ * @param Number mhp \n the player's new maximum hit points.
+ * @brief Maximum hit points.
+ * @return Boolean \n TRUE if hook has processed player hit points, otherwise
+ * FALSE.
+ * @note
+ * If the hook returns TRUE, the player's maximum hit points are updated.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_CALC_HP 57
+
+/** @def HOOK_GF_COLOR
+ * @brief Set color for spell.
+ * @param Number type \n type of spell.
+ * @brief Type
+ * @param Number file \n if this is 0 use ANGBAND_GRAF, otherwise use "new".
+ * @brief File
+ * @return Boolean \n TRUE if hook sets a color, otherwise FALSE.
+ * @return Number color \n The color for the spell.
+ * @note
+ * If the hook returns TRUE, spell_color() returns the hook's color, otherwise
+ * the function continues.
+ * @note (see file spells1.c)
+ */
+#define HOOK_GF_COLOR 58
+
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage terrain features.\n
+ * @param String target \n "grid" to indicate spell damages terrain.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number obvious \n TRUE if the player notices the spell, otherwise
+ * FALSE.
+ * @return Number flag \n TRUE if the player is affected, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "obvious" and "flag" fields.
+ * @note (see file spells1.c)
+ */
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage objects.\n
+ * @param String target \n "object" to indicate spell damages objects.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @param Object o_ptr \n the object which is the target of the spell.
+ * @brief Object
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number obvious \n TRUE if the player notices the spell, otherwise
+ * FALSE.
+ * @return Number flag \n TRUE if the player is affected, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "obvious" and "do_kill" fields.
+ * @note (see file spells1.c)
+ */
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage monsters.\n
+ * @param String target \n "angry" to indicate spell angers a friend.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @param Monster m_ptr \n the monster which is the target of the spell.
+ * @brief Monster
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number get_angry \n TRUE if the monster gets angry, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "get_angry" field.
+ * @note (see file spells1.c)
+ */
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage monsters.\n
+ * @param String target \n "monster" to indicate spell damages monsters.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @param Monster m_ptr \n the monster which is the target of the spell.
+ * @brief Monster
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number obvious \n TRUE if the player notices the spell, otherwise
+ * FALSE.
+ * @return Number dam \n The damage the monster takes.
+ * @return Number do_stun \n TRUE if the monster is stunned, otherwise FALSE.
+ * @return Number do_fear \n TRUE if the monster is frightened, otherwise
+ * FALSE.
+ * @return Number do_conf \n TRUE if the monster is confused, otherwise FALSE.
+ * @return Number do_dist \n TRUE if the monster is disturbed, otherwise FALSE.
+ * @return Number do_pois \n TRUE if the monster is poisoned, otherwise FALSE.
+ * @return Number do_cut \n TRUE if the monster is wounded, otherwise FALSE.
+ * @return Number do_poly \n TRUE if the monster is polymorphed, otherwise
+ * FALSE.
+ * @return String note \n The message displayed if the monster if affected.
+ * @return String note_dies \n The message displayed if the monster dies.
+ * @note
+ * If the hook returns TRUE, the hook sets the "obvious", "dam", "do_stun",
+ * "do_fear", "do_conf", "do_dist", "do_pois", "do_cut", "do_poly", "note",
+ * and "note dies" fields, otherwise the spell has no effect and does no
+ * damage.
+ * @note (see file spells1.c)
+ */
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage the player.\n
+ * @param String target \n "player" to indicate spell damages the player.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number obvious \n TRUE if the player notices the spell, otherwise
+ * FALSE.
+ * @return Number dam \n The damage the player takes.
+ * @note
+ * If the hook returns TRUE, the hook sets the "obvious" and "dam" fields,
+ * otherwise there is no damage.
+ * @note (see file spells1.c)
+ */
+#define HOOK_GF_EXEC 59
+
+/** @def HOOK_CALC_MANA
+ * @brief Recalculate player SP (spell points).\n
+ * @param Number msp \n the player's new maximum spell points.
+ * @brief Maximum spell points.
+ * @return Boolean \n TRUE if hook has processed player spell points, otherwise
+ * FALSE.
+ * @note
+ * If the hook returns TRUE, the player's maximum spell points are updated.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_CALC_MANA 60
+
+/** @def HOOK_LOAD_END
+ * @brief Load a savefile.\n
+ * @param Number death \n TRUE if the character is dead, otherwise FALSE.
+ * @brief Dead character?
+ * @return Boolean \n TRUE if hook has processed savefile, otherwise FALSE.
+ * @return Number death \n
+ * @note
+ * If the hook returns TRUE, then "character_loaded" (real living player) is
+ * set to TRUE. The player has been revived.
+ * @note (see file loadsave.c)
+ */
+#define HOOK_LOAD_END 61
+
+/** @def HOOK_RECALL
+ * @brief Player recalls from/to dungeon/town.
+ * @return Boolean \n TRUE if player is not allowed to recall, otherwise
+ * FALSE.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_RECALL 62
+
+/** @def HOOK_FOLLOW_GOD
+ * @brief Player follows a god (gets religion).\n
+ * @param Number god \n the god to follow.
+ * @brief God
+ * @param String action \n "ask" to check if player can follow the god, or
+ * "done" to do something with the god.
+ * @brief Action
+ * @return Boolean \n For "ask": TRUE if player can not follow the god,
+ * otherwise FALSE.
+ * @note
+ * If the action is "ask" and the hook returns TRUE, follow_god() returns.
+ * If the action is "done" the return code is ignored.
+ * @note (see file gods.c)
+ */
+#define HOOK_FOLLOW_GOD 63
+
+/** @def HOOK_SACRIFICE_GOD
+ * @brief Player sacrifices to a god.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_SACRIFICE_GOD 64
+
+/** @def HOOK_BODY_PARTS
+ * @brief Calculate which body parts the player has.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_BODY_PARTS 65
+
+/** @def HOOK_APPLY_MAGIC
+ * @brief Apply magic to an item.\n
+ * @param Object o_ptr \n the item to which magic is applied
+ * @brief Object
+ * @param Number level \n the level of the object
+ * @brief Object level
+ * @param Number power \n the power of the object (0 = normal, 1 = good,
+ * 2 = great, -1 = cursed, -2 = broken)
+ * @brief Power
+ * @return Boolean \n TRUE if hook has applied magic, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, a_m_aux_n() (where n=1 to 4) returns.
+ * @note (see file object2.c)
+ */
+#define HOOK_APPLY_MAGIC 66
+
+/** @def HOOK_PLAYER_EXP
+ * @brief Player gains/loses experience points (XP).\n
+ * @param Number amount \n the number of experience points to gain/lose
+ * @brief Points
+ * @note (see file xtra2.c)
+ */
+#define HOOK_PLAYER_EXP 67
+
+/** @def HOOK_BIRTH
+ * @brief Player is born.
+ * @note (see file birth.c)
+ */
+#define HOOK_BIRTH 68
+
+/** @def HOOK_CALC_LITE
+ * @brief Calculate the lite radius.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_CALC_LITE 69
+
+/** @def HOOK_LEARN_ABILITY
+ * @brief Player learns an ability.\n
+ * @param Number ab \n index of ability in ability (ab_info) array.
+ * @brief Ability index
+ * @return Boolean \n TRUE if player is not to gain the ability, otherwise
+ * FALSE.
+ * @note If the hook returns TRUE, can_learn_ability() returns FALSE.
+ * @note (see file skills.c)
+ */
+#define HOOK_LEARN_ABILITY 70
+
+/** @def HOOK_MOVED
+ * @brief Player finishes moving.
+ * @note (see file cmd1.c)
+ */
+#define HOOK_MOVED 71
+
+/** @def HOOK_GAME_START
+ * @brief Game begins.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_GAME_START 72
+
+/** @def HOOK_TAKEOFF
+ * @brief Player takes off an item.\n
+ * @param Number item \n the item to take off.
+ * @brief Item
+ * @return Booelan \n TRUE if item can not be taken off, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_takeoff() returns.
+ * @note (see file cmd3.c)
+ */
+#define HOOK_TAKEOFF 73
+
+/** @def HOOK_CALC_WEIGHT
+ * @brief Calculate player weight limit.\n
+ * @param Number weight \n the current weight limit.
+ * @brief Weight
+ * @return Boolean \n TRUE if weight was processed, otherwise FALSE.
+ * @return Number weight \n The new maximum weight.
+ * @note
+ * If the hook returns TRUE, weight_limit() returns the hook's weight.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_CALC_WEIGHT 74
+
+/** @def HOOK_FORBID_TRAVEL
+ * @brief Check if the player may press < and travel.\n
+ * @return Boolean \n TRUE if travel is forbidden, otherwise FALSE.
+ */
+#define HOOK_FORBID_TRAVEL 75
+
+/** @def HOOK_DEBUG_COMMAND
+ * @brief User enters a debug command.\n
+ * @param Number command \n the pressed key (cmd).
+ * @brief Command
+ * @return Boolean \n TRUE if special processing was done, otherwise FALSE.
+ */
+#define HOOK_DEBUG_COMMAND 76
+
+
+/** @} */
+
+
+/** @var turn
+ * @brief Number
+ * @note Current game turn
+ */
+extern s32b turn;
+/** @var old_turn
+ * @brief Number
+ * @note Turn when level began (feelings)
+ */
+extern s32b old_turn;
+/** @var cur_wid
+ * @brief Number
+ * @note Current dungeon width
+ */
+extern s16b cur_wid;
+/** @var cur_hgt
+ * @brief Number
+ * @note Current dungeon height
+ */
+extern s16b cur_hgt;
+
+/** @fn disturb(int stop_search, int flush_output)
+ * @brief Disturb the player.\n
+ * @param stop_search Number \n if 0, this will not disturb searching,
+ * otherwise searching is stopped.
+ * @brief Stop search?
+ * @param flush_output Number \n *unused*
+ * @brief *Unused*
+ * @note
+ * Something has happened to disturb the player.\n\n
+ * The first arg indicates a major disturbance, which affects search.\n
+ * The second arg is currently unused, but could induce output flush.\n\n
+ * All disturbance cancels repeated commands, resting, and running.
+ * @note (see file cave.c)
+ */
+extern void disturb(int stop_search, int flush_output);
+
+/** @fn bst(s32b what, s32b t)
+ * @brief Break scalar time.\n
+ * @param what Number \n the unit time "t" is to be broken into. The following
+ * values can be used: MINUTE, HOUR, DAY, YEAR
+ * @brief Unit of time
+ * @param t Number \n the time to be broken.
+ * @brief Time
+ * @return Number \n The number of unit in time "t".
+ * @note (see file util.c)
+ */
+extern s32b bst(s32b what, s32b t);
+
+$static char *path_build_lua(cptr path, cptr file){static char buf[1025]; path_build(buf, 1024, path, file); return buf;}
+
+/** @fn path_build(cptr path, cptr file);
+ * @brief Create a new path by appending a file (or directory) to a path.\n
+ * @param path String \n the original path.
+ * @brief Path
+ * @param file String \n the file or directory to append to the path.
+ * @brief File or directory
+ * @return String \n The new path.
+ * @note
+ * This requires no special processing on simple machines, except
+ * for verifying the size of the filename, but note the ability to
+ * bypass the given "path" with certain special file-names.\n\n
+ * Note that the "file" may actually be a "sub-path", including
+ * a path and a file.\n\n
+ * @note (see file util.c)
+ */
+static char *path_build_lua@path_build(cptr path, cptr file);
+
+/** @fn move_cursor(int row, int col)
+ * @brief Move the cursor of a terminal to row "row" and column "col".\n
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @param col Number \n the target column on the screen.
+ * @brief Column
+ * @note (see file util.c)
+ */
+extern void move_cursor(int row, int col);
+
+/** @fn flush(void)
+ * @brief Flush all input chars.
+ * @note
+ * Actually, remember the flush, and do a "special flush" before the next
+ * "inkey()".
+ * This is not only more efficient, but also necessary to make sure
+ * that various "inkey()" codes are not "lost" along the way.
+ * @note (see file util.c)
+ */
+extern void flush(void);
+
+/** @var inkey_scan
+ * @brief Boolean
+ * @note
+ * If "inkey_scan" is TRUE, then we will immediately return "zero" if no
+ * keypress is available, instead of waiting for a keypress.
+ */
+extern bool inkey_scan;
+
+/** @fn inkey(void)
+ * @brief Get a keypress from the user.
+ * @return String \n the key pressed by the user.
+ * @note
+ * This function recognizes a few "global parameters". These are variables
+ * which, if set to TRUE before calling this function, will have an effect
+ * on this function, and which are always reset to FALSE by this function
+ * before this function returns. Thus they function just like normal
+ * parameters, except that most calls to this function can ignore
+ * them.\n\n
+ * If "inkey_xtra" is TRUE, then all pending keypresses will be flushed,
+ * and any macro processing in progress will be aborted. This flag is
+ * set by the "flush()" function, which does not actually flush anything
+ * itself, but rather, triggers delayed input flushing via
+ * "inkey_xtra".\n\n
+ * If "inkey_scan" is TRUE, then we will immediately return "zero" if no
+ * keypress is available, instead of waiting for a
+ * keypress.\n\n
+ * If "inkey_base" is TRUE, then all macro processing will be bypassed.
+ * If "inkey_base" and "inkey_scan" are both TRUE, then this function will
+ * not return immediately, but will wait for a keypress for as long as the
+ * normal macro matching code would, allowing the direct entry of macro
+ * triggers. The "inkey_base" flag is extremely
+ * dangerous!\n\n
+ * If "inkey_flag" is TRUE, then we will assume that we are waiting for a
+ * normal command, and we will only show the cursor if "hilite_player" is
+ * TRUE (or if the player is in a store), instead of always showing the
+ * cursor. The various "main-xxx.c" files should avoid saving the game
+ * in response to a "menu item" request unless "inkey_flag" is TRUE, to
+ * prevent savefile
+ * corruption.\n\n
+ * If we are waiting for a keypress, and no keypress is ready, then we will
+ * refresh (once) the window which was active when this function was
+ * called.\n\n
+ * Note that "back-quote" is automatically converted into "escape" for
+ * convenience on machines with no "escape" key. This is done after the
+ * macro matching, so the user can still make a macro for
+ * "backquote".\n\n
+ * Note the special handling of "ascii 30" (ctrl-caret, aka ctrl-shift-six)
+ * and "ascii 31" (ctrl-underscore, aka ctrl-shift-minus), which are used to
+ * provide support for simple keyboard "macros". These keys are so strange
+ * that their loss as normal keys will probably be noticed by nobody. The
+ * "ascii 30" key is used to indicate the "end" of a macro action, which
+ * allows recursive macros to be avoided. The "ascii 31" key is used by
+ * some of the "main-xxx.c" files to introduce macro trigger
+ * sequences.\n\n
+ * Hack -- we use "ascii 29" (ctrl-right-bracket) as a special "magic" key,
+ * which can be used to give a variety of "sub-commands" which can be used
+ * any time. These sub-commands could include commands to take a picture of
+ * the current screen, to start/stop recording a macro action,
+ * etc.\n\n
+ * If "angband_term[0]" is not active, we will make it active during this
+ * function, so that the various "main-xxx.c" files can assume that input
+ * is only requested (via "Term_inkey()") when "angband_term[0]" is
+ * active.\n\n
+ * Mega-Hack -- This function is used as the entry point for clearing the
+ * "signal_count" variable, and of the "character_saved"
+ * variable.\n\n
+ * Hack -- Note the use of "inkey_next" to allow "keymaps" to be
+ * processed.\n\n
+ * Mega-Hack -- Note the use of "inkey_hack" to allow the "Borg" to steal
+ * control of the keyboard from the user.
+ * @note (see file util.c)
+ */
+extern char inkey(void);
+
+/** @fn cmsg_print(byte color, cptr msg)
+ * @brief Output message "msg" in colour "color" to the top line of the
+ * screen.\n
+ * @param color Number \n the colour of the message (see TERM_ fields).
+ * @brief Colour
+ * @param msg String \n the message.
+ * @brief Message
+ * @note
+ * Break long messages into multiple pieces (40-72 chars).\n\n
+ * Allow multiple short messages to "share" the top line.\n\n
+ * Prompt the user to make sure he has a chance to read them.\n\n
+ * These messages are memorized for later reference (see above).\n\n
+ * We could do "Term_fresh()" to provide "flicker" if needed.\n\n
+ * The global "msg_flag" variable can be cleared to tell us to
+ * "erase" any "pending" messages still on the
+ * screen.\n\n
+ * XXX XXX XXX Note that we must be very careful about using the
+ * "msg_print()" functions without explicitly calling the special
+ * "msg_print(NULL)" function, since this may result in the loss
+ * of information if the screen is cleared, or if anything is
+ * displayed on the top
+ * line.\n\n
+ * XXX XXX XXX Note that "msg_print(NULL)" will clear the top line
+ * even if no messages are pending. This is probably a hack.
+ * @note (see file util.c)
+ */
+extern void cmsg_print(byte color, cptr msg);
+
+/** @fn msg_print(cptr msg)
+ * @brief Output message "msg" in white to the top line of the screen.\n
+ * @param msg String \n the message.
+ * @brief Message
+ * @note (see file util.c)
+ */
+extern void msg_print(cptr msg);
+
+/** @fn screen_save(void)
+ * @brief Save the screen.
+ * @note
+ * Increase the "icky" depth.\n\n
+ * This function must match exactly one call to "screen_load()".
+ * @note (see file util.c)
+ */
+extern void screen_save(void);
+
+/** @fn screen_load(void)
+ * @brief Load the screen.
+ * @note
+ * Decrease the "icky" depth.\n\n
+ * This function must match exactly one call to "screen_save()".
+ * @note (see file util.c)
+ */
+extern void screen_load(void);
+
+/** @fn Term_save(void)
+ * @brief Save the "requested" screen into the "memorized" screen.
+ * @return Number \n 0 (always).
+ * @note
+ * Every "Term_save()" should match exactly one "Term_load()"
+ * @note (see file z-term.c)
+ */
+extern errr Term_save(void);
+
+/** @fn Term_load(void)
+ * @brief Restore the "requested" contents from the "memorized" screen.
+ * @return Number \n 0 (always).
+ * @note
+ * Every "Term_save()" should match exactly one "Term_load()"
+ * @note (see file z-term.c)
+ */
+extern errr Term_load(void);
+
+/** @fn c_put_str(byte attr, cptr str, int row, int col)
+ * @brief Add string "str" with attributes "attr" to screen at row "row"
+ * and column "col".\n
+ * @param attr Number \n the attribute of the string
+ * @brief Attribute
+ * @param str String \n the string
+ * @brief String
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @param col Number \n the target column on the screen.
+ * @brief Column
+ * @note
+ * Display a string on the screen using an attribute.\n\n
+ * At the given location, using the given attribute, if allowed,
+ * add the given string. Do not clear the line.
+ * @note (see file util.c)
+ */
+extern void c_put_str(byte attr, cptr str, int row, int col);
+
+/** @fn c_prt(byte attr, cptr str, int row, int col)
+ * @brief Add string "str" with attributes "attr" to screen at row "row"
+ * and column "col", clearing to the end of the row.\n
+ * @param attr Number \n the attribute of the string
+ * @brief Attribute
+ * @param str String \n the string
+ * @brief String
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @param col Number \n the target column on the screen.
+ * @brief Column
+ * @note (see file util.c)
+ */
+extern void c_prt(byte attr, cptr str, int row, int col);
+
+/** @fn prt(cptr str, int row, int col)
+ * @brief Add white string "str" to screen at row "row" and column "col",
+ * clearing to the end of the row.\n
+ * @param str String \n the string
+ * @brief String
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @param col Number \n the target column on the screen.
+ * @brief Column
+ * @note (see file util.c)
+ */
+extern void prt(cptr str, int row, int col);
+
+/** @fn message_add(byte type, cptr msg, byte color)
+ * @brief Add a message "msg" of type "type" and colour "color" to the
+ * message array.\n
+ * @param type Number \n the type of message. MESSAGE_MSG for regular
+ * messages.
+ * @brief Type
+ * @param msg String \n the message.
+ * @brief Message
+ * @param color Number \n the colour of the message (see TERM_ fields).
+ * @brief Colour
+ * @note
+ * Use "msg_print() instead. If you insist on using this function, be
+ * careful.
+ * @note (see file util.c)
+ */
+extern void message_add(byte type, cptr msg, byte color);
+
+/** @fn display_message(int x, int y, int split, byte color, cptr t)
+ * @brief Display a message.\n
+ * @param x Number \n the x-coordinate of the screen where the message starts.
+ * @brief X-coordinate
+ * @param y Number \n the y-coordinate of the screen where the message starts.
+ * @brief Y-coordinate
+ * @param split Number \n the position in the message where it is split. The
+ * rest of the message will not appear.
+ * @brief Split position
+ * @param color Number \n the colour of the message (see TERM_ fields).
+ * @brief Colour
+ * @param t String \n the message.
+ * @brief Message
+ * @note
+ * @note (see file util.c)
+ */
+extern void display_message(int x, int y, int split, byte color, cptr t);
+
+/** @fn clear_from(int row)
+ * @brief Clear part of the screen.\n
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @note
+ * Clear all rows from the starting row to the end of the screen.
+ * @note (see file util.c)
+ */
+extern void clear_from(int row);
+
+/** @fn askfor_aux(char *buf, int len)
+ * @brief Get some input at the cursor location.\n
+ * @param *buf String \n Default string (optional).
+ * @brief String
+ * @param len Number \n the maximum length of the string.
+ * @brief Length of string
+ * @return Boolean \n TRUE if string was entered, otherwise FALSE.
+ * @return *buf \n The entered string.
+ * @note
+ * Assume the buffer is initialized to a default string.\n
+ * Note that this string is often "empty" (see below).\n
+ * The default buffer is displayed in yellow until cleared.\n
+ * Pressing RETURN right away accepts the default entry.\n
+ * Normal chars clear the default and append the char.\n
+ * Backspace clears the default or deletes the final char.\n
+ * ESCAPE clears the buffer and the window and returns FALSE.\n
+ * RETURN accepts the current buffer contents and returns TRUE.
+ * @note (see file util.c)
+ */
+extern bool askfor_aux(char *buf, int len);
+
+/** @fn get_string(cptr prompt, char *buf, int len)
+ * @brief Get a string from the user.\n
+ * @param prompt String \n the prompt, which should take the form "Prompt: "
+ * @brief Prompt
+ * @param *buf String
+ * @brief String
+ * @param len Number \n the maximum length of the string.
+ * @brief Length of string
+ * @return Boolean \n TRUE if string was entered, otherwise FALSE.
+ * @return *buf \n The entered string.
+ * @note
+ * Note that the initial contents of the string is used as
+ * the default response, so be sure to "clear" it if needed.\n\n
+ * We clear the input, and return FALSE, on "ESCAPE".
+ * @note (see file util.c)
+ */
+extern bool get_string(cptr prompt, char *buf, int len);
+
+/** @fn get_check(cptr prompt)
+ * @brief Verify something with the user.\n
+ * @param prompt String \n the prompt, which should take the form "Query? "
+ * @brief Prompt
+ * @return Boolean \n TRUE if "y" or "Y" is entered, otherwise FALSE.
+ * @note
+ * Note that "[y/n]" is appended to the prompt.
+ * @note (see file util.c)
+ */
+extern bool get_check(cptr prompt);
+
+/** @fn get_com(cptr promtp, int *com = 0);
+ * @brief Prompts for a keypress.\n
+ * @param promtp String \n the prompt, which should take the form "Command: "
+ * @brief Prompt
+ * @param *com Number
+ * @brief Command
+ * @return Boolean \n FALSE if "Escape" was pressed, otherwise TRUE.
+ * @return *com \n The entered command.
+ * @note (see file util.c)
+ */
+extern bool get_com_lua @ get_com(cptr promtp, int *com = 0);
+
+/** @fn get_quantity(cptr prompt, s32b max)
+ * @brief Request a "quantity" from the user.\n
+ * @param prompt String \n the prompt
+ * @brief Prompt
+ * @param max Number \n the maximum quantity
+ * @brief Maximum quantity
+ * @return Number \n the returned quantity.
+ * @note
+ * Hack -- allow "command_arg" to specify a quantity\n\n
+ * The quantity is in the range 0 to "max" inclusive. The default is 1. A
+ * letter means the maximum.
+ * @note (see file util.c)
+ */
+extern s32b get_quantity(cptr prompt, s32b max);
+
+/** @fn test_monster_name(cptr name)
+ * @brief Given monster name as string, return the index in r_info array.\n
+ * @param name String \n the monster name.
+ * @brief Monster name
+ * @return Number \n The index of the monster in r_info[], or 0 if the name
+ * does not match a monster.
+ * @note
+ * Name must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter.
+ * @note (see file util.c)
+ */
+extern int test_monster_name(cptr name);
+
+/** @fn test_item_name(cptr name)
+ * @brief Given item name as string, return the index in k_info array.\n
+ * @param name String \n the item name.
+ * @brief Item name
+ * @return Number \n The index of the item in k_info[], or 0 if the name
+ * does not match an item.
+ * @note
+ * Name must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter.
+ * @note (see file util.c)
+ */
+extern int test_item_name(cptr name);
+
+/** @fn luck(int min, int max)
+ * @brief Return a luck number between a certain range.\n
+ * @param min Number \n the minimum luck value returned.
+ * @brief Mimimum
+ * @param max Number \n the maximum luck value returned.
+ * @brief Maximum
+ * @return Number \n The scaled value of player's luck.
+ * @note
+ * Player lucked is cap at a minimum of -30 and maximum of +30 before it is
+ * scaled to the range defined by "min" and "max".
+ * @note (see file xtra1.c)
+ */
+extern int luck(int min, int max);
+
+/** @fn get_player_race_name(int pr, int ps)
+ * @brief Return the player's race (and sub-race) name.\n
+ * @param pr Number \n the player's race. It is an index to race_info[].
+ * @brief Player race
+ * @param ps Number \n the player's subrace, if any. It is an index to
+ * race_mod_info[].
+ * @brief Player subrace
+ * @return String \n The player's full race name.
+ * @note (see file util.c)
+ */
+extern cptr get_player_race_name(int pr, int ps);
+
+/** @fn quit(cptr str)
+ * @brief Quit the game.
+ * @param str String \n an error code or a message which is logged.
+ * @brief String
+ * @note
+ * Exit (ala "exit()"). If 'str' is NULL, do "exit(0)".\n
+ * If 'str' begins with "+" or "-", do "exit(atoi(str))".\n
+ * Otherwise, plog() 'str' and exit with an error code of -1.\n
+ * But always use 'quit_aux', if set, before anything else.
+ * @note (see file z-util.c)
+ */
+extern void quit(cptr str);
+
+/** @fn value_scale(int value, int vmax, int max, int min)
+ * @brief Rescale a value
+ * @param value Number \n the original value
+ * @brief Original value
+ * @param vmax Number \n the maximum the original value can be
+ * @brief Original maximum
+ * @param max Number \n the maximum new value
+ * @brief New maximum
+ * @param min Number \n the minimum new value
+ * @brief New minimum
+ * @return Number \n The rescaled value
+ * @note (see file util.c)
+ */
+extern s32b value_scale(int value, int vmax, int max, int min);
+
+/*
+ * compass, approximate_distance
+ */
+extern cptr compass(int y, int x, int y2, int x2);
+extern cptr approximate_distance(int y, int x, int y2, int x2);
+
+/** @fn text_out_c(byte a, cptr str)
+ * @brief Output text to the screen (in color) or to a file depending on the
+ * selected hook.\n
+ * @param a Number \n the attribute of the string
+ * @brief Attribute
+ * @param str String \n the string
+ * @brief String
+ * @note (see file util.c)
+ */
+extern void text_out_c(byte a, cptr str);
+
+/** @fn text_out(cptr str)
+ * @brief Output text to the screen (in white) or to a file depending on the
+ * selected hook.\n
+ * @param str String \n the string
+ * @brief String
+ * @note (see file util.c)
+ */
+extern void text_out(cptr str);
+
+/** @fn change_option(cptr name, bool value)
+ * @brief Switch an option by only knowing its name.\n
+ * @param name String \n the name of the option.
+ * @brief Option name
+ * @param value Boolean \n the new value of the option.
+ * @brief Option value
+ * @return Boolean \n the old value of the option, or FALSE if "name" is not
+ * an option.
+ * @note (see file cmd4.c)
+ */
+extern bool change_option(cptr name, bool value);
+
+/** @var process_hooks_restart
+ * @brief Number
+ * @note
+ * Set this to TRUE after deleting a C-hook (from a quest) to clean up hook
+ * processing. This is not required for Lua hooks as they are not deleted.
+ */
+extern int process_hooks_restart;
+
+/** @fn dump_hooks(int h_idx)
+ * @brief Print the name and type (language) of all hooks in a hook chain.\n
+ * @param h_idx Number \n the index of the hook chain in the hook_chain array.
+ * If this is -1, then all hook chains will be printed.
+ * @brief Hook chain index
+ * @note (see file plots.c)
+ */
+extern void dump_hooks(int h_idx);
+
+/** @fn add_hook_script(int h_idx, char *script, cptr name)
+ * @brief Add Lua script "name" in file "script" to hook_chain.\n
+ * @param h_idx Number \n the index of the hook chain in the hook_chain array.
+ * @brief Hook chain index
+ * @param *script String \n the name of the Lua script file.
+ * @brief Script filename
+ * @param name String \n the name of the script.
+ * @brief Script name
+ * @note (see file plots.c)
+ */
+extern void add_hook_script(int h_idx, char *script, cptr name);
+
+/** @fn del_hook_name(int h_idx, cptr name)
+ * @brief Delete hook with name "name" from a hook chain.\n
+ * @param h_idx Number \n the index of the hook chain in the hook_chain array.
+ * @brief Hook chain index
+ * @param name String \n the name of the hook to delete
+ * @brief Hook name
+ * @note (see file plots.c)
+ */
+extern void del_hook_name(int h_idx, cptr name);
+
+/** @fn tome_dofile(char *file)
+ * @brief Load a Lua file from lib/scpts.\n
+ * @param *file String \n the name of a Lua file to load.
+ * @brief Filename
+ * @return Boolean \n TRUE if file was loaded, otherwise FALSE.
+ * @note (see file script.c)
+ */
+extern bool tome_dofile(char *file);
+
+/** @fn tome_dofile_anywhere(cptr dir, char *file, bool test_exist = TRUE)
+ * @brief Load a Lua file from any directory.\n
+ * @param dir String \n the name of a Lua file directory
+ * @brief Directory
+ * @param *file String \n the name of a Lua file to load.
+ * @brief Filename
+ * @param test_exist Boolean \n TRUE if a message is printed if the file does
+ * not exist, otherwise FALSE.
+ * @brief Message if file does not exist?
+ * @return Boolean \n TRUE if file was loaded, otherwise FALSE.
+ * @note (see file script.c)
+ */
+extern bool tome_dofile_anywhere(cptr dir, char *file, bool test_exist = TRUE);
+
+/** @fn exec_lua(char *file)
+ * @brief Execute Lua command "file" and return the integer result.\n
+ * @param *file String \n the Lua command to execute.
+ * @brief Command
+ * @return Number \n the result of the Lua command.
+ * @note (see file script.c)
+ */
+extern int exec_lua(char *file);
+
+/** @fn dump_lua_stack(int min, int max)
+ * @brief Display part of the Lua stack.\n
+ * @param min Number \n the starting item of the stack dump.
+ * @brief Start item
+ * @param max Number \n the ending item of the stack dump.
+ * @brief End item
+ * @note (see file script.c)
+ */
+extern void dump_lua_stack(int min, int max);
+
+/** @fn string_exec_lua(char *file)
+ * @brief Execute Lua command "file" and return the string result.\n
+ * @param *file String \n the Lua command to execute.
+ * @brief Command
+ * @return String \n the result of the Lua command.
+ * @note (see file script.c)
+ */
+extern cptr string_exec_lua(char *file);
+
+/** @fn print_hook(cptr str);
+ * @brief Print string "string" to the hook file.\n
+ * @param str String \ the string.
+ * @brief String
+ * @note (see file lua_bind.c)
+ */
+extern void lua_print_hook@print_hook(cptr str);
+
+/* Savefile stuff */
+/** @fn register_savefile(int num)
+ * @brief Add "num" slots to the savefile.\n
+ * @param num Number \n the number of slots to add.
+ * @brief Slots
+ * @note (see file loadsave.c)
+ */
+extern void register_savefile(int num);
+
+/** @fn save_number_key(char *key, s32b val)
+ * @brief Save a key-value combination in the save file.\n
+ * @param *key String \n the key to save.
+ * @brief Key
+ * @param val Number \n the value of the key.
+ * @brief Value
+ * @note
+ * The length of the key is stored first, then the key, then the value.
+ * @note (see file loadsave.c)
+ */
+extern void save_number_key(char *key, s32b val);
+
+
+/* Tables */
+/** @var adj_mag_study[100]
+ * @brief Number
+ * @note Stat Table (INT/WIS) -- Number of half-spells per level
+ */
+extern byte adj_mag_study[100];
+
+/** @var adj_mag_mana[100]
+ * @brief Number
+ * @note Stat Table (INT/WIS) -- extra half-mana-points per level
+ */
+extern byte adj_mag_mana[100];
+
+/** @var adj_mag_fail[100]
+ * @brief Number
+ * @note Stat Table (INT/WIS) -- Minimum failure rate (percentage)
+ */
+extern byte adj_mag_fail[100];
+
+/** @var adj_mag_stat[100]
+ * @brief Number
+ * @note Stat Table (INT/WIS) -- Various things
+ */
+extern byte adj_mag_stat[100];
+
+/** @var adj_chr_gold[100]
+ * @brief Number
+ * @note Stat Table (CHR) -- payment percentages
+ */
+extern byte adj_chr_gold[100];
+
+/** @var adj_int_dev[100]
+ * @brief Number
+ * @note Stat Table (INT) -- Magic devices
+ */
+extern byte adj_int_dev[100];
+
+/** @var adj_wis_sav[100]
+ * @brief Number
+ * @note Stat Table (WIS) -- Saving throw
+ */
+extern byte adj_wis_sav[100];
+
+/** @var adj_dex_dis[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- disarming
+ */
+extern byte adj_dex_dis[100];
+
+/** @var adj_int_dis[100]
+ * @brief Number
+ * @note Stat Table (INT) -- disarming
+ */
+extern byte adj_int_dis[100];
+
+/** @var adj_dex_ta[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- bonus to ac (plus 128)
+ */
+extern byte adj_dex_ta[100];
+
+/** @var adj_str_td[100]
+ * @brief Number
+ * @note Stat Table (STR) -- bonus to dam (plus 128)
+ */
+extern byte adj_str_td[100];
+
+/** @var adj_dex_th[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- bonus to hit (plus 128)
+ */
+extern byte adj_dex_th[100];
+
+/** @var adj_str_th[100]
+ * @brief Number
+ * @note Stat Table (STR) -- bonus to hit (plus 128)
+ */
+extern byte adj_str_th[100];
+
+/** @var adj_str_wgt[100]
+ * @brief Number
+ * @note Stat Table (STR) -- weight limit in deca-pounds
+ */
+extern byte adj_str_wgt[100];
+
+/** @var adj_str_hold[100]
+ * @brief Number
+ * @note Stat Table (STR) -- weapon weight limit in pounds
+ */
+extern byte adj_str_hold[100];
+
+/** @var adj_str_dig[100]
+ * @brief Number
+ * @note Stat Table (STR) -- digging value
+ */
+extern byte adj_str_dig[100];
+
+/** @var adj_str_blow[100]
+ * @brief Number
+ * @note Stat Table (STR) -- help index into the "blow" table
+ */
+extern byte adj_str_blow[100];
+
+/** @var adj_dex_blow[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- index into the "blow" table
+ */
+extern byte adj_dex_blow[100];
+
+/** @var adj_dex_safe[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- chance of avoiding "theft" and "falling"
+ */
+extern byte adj_dex_safe[100];
+
+/** @var adj_con_fix[100]
+ * @brief Number
+ * @note Stat Table (CON) -- base regeneration rate
+ */
+extern byte adj_con_fix[100];
+
+/** @var adj_con_mhp[100]
+ * @brief Number
+ * @note Stat Table (CON) -- extra half-hitpoints per level (plus 128)
+ */
+extern byte adj_con_mhp[100];
+
+/* Repeat stuff */
+/** @fn repeat_push(int what)
+ * @brief Push key "what" onto the end of the repeat_key array.\n
+ * @param what Number \n the key to be repeated.
+ * @brief Key
+ * @note (see file util.c)
+ */
+extern void repeat_push(int what);
+
+/** @fn repeat_pull(int *what = 0)
+ * @brief Pull key from the repeat__key array.\n
+ * @param *what Number
+ * @brief Key
+ * @return Boolean \n TRUE if key was pulled, otherwise FALSE.
+ * @return *what Number \n the key pulled.
+ * @note
+ * This functions starts from an index, which may not be at the start of the
+ * array.
+ * @note (see file util.c)
+ */
+extern bool repeat_pull(int *what = 0);
+
+/** @fn repeat_check(void)
+ * @brief Check if the last command is repeated.
+ * @note
+ * Ignore certain commands: ESC, space, newline.\n
+ * 'n' repeats the last command (index is set to 0).\n
+ * Other commands reset the repeat array (index and count are set to 0).
+ * @note (see file util.c)
+ */
+extern void repeat_check(void);
+
+/** @fn get_count(int number, int max)
+ * @brief Allow the user to select multiple items without pressing '0'.\n
+ * @param number Number \n the default number.
+ * @brief Default
+ * @param max Number \n the maximum value allowed.
+ * @brief Maximum
+ * The user is prompted with "How many?"
+ * @note (see file util.c)
+ */
+extern void get_count(int number, int max);
+
+/** @name Feature Flags
+ * @{
+ */
+/** @def FF1_NO_WALK */
+#define FF1_NO_WALK 0x00000001L
+
+/** @def FF1_NO_VISION */
+#define FF1_NO_VISION 0x00000002L
+
+/** @def FF1_CAN_LEVITATE */
+#define FF1_CAN_LEVITATE 0x00000004L
+
+/** @def FF1_CAN_PASS */
+#define FF1_CAN_PASS 0x00000008L
+
+/** @def FF1_FLOOR */
+#define FF1_FLOOR 0x00000010L
+
+/** @def FF1_WALL */
+#define FF1_WALL 0x00000020L
+
+/** @def FF1_PERMANENT */
+#define FF1_PERMANENT 0x00000040L
+
+/** @def FF1_CAN_FLY */
+#define FF1_CAN_FLY 0x00000080L
+
+/** @def FF1_REMEMBER */
+#define FF1_REMEMBER 0x00000100L
+
+/** @def FF1_NOTICE */
+#define FF1_NOTICE 0x00000200L
+
+/** @def FF1_DONT_NOTICE_RUNNING */
+#define FF1_DONT_NOTICE_RUNNING 0x00000400L
+
+/** @def FF1_CAN_RUN */
+#define FF1_CAN_RUN 0x00000800L
+
+/** @def FF1_DOOR */
+#define FF1_DOOR 0x00001000L
+
+/** @def FF1_SUPPORT_LIGHT */
+#define FF1_SUPPORT_LIGHT 0x00002000L
+
+/** @def FF1_CAN_CLIMB */
+#define FF1_CAN_CLIMB 0x00004000L
+
+/** @def FF1_TUNNELABLE */
+#define FF1_TUNNELABLE 0x00008000L
+
+/** @def FF1_WEB */
+#define FF1_WEB 0x00010000L
+
+/** @def FF1_ATTR_MULTI */
+#define FF1_ATTR_MULTI 0x00020000L
+
+/** @def FF1_SUPPORT_GROWTH */
+#define FF1_SUPPORT_GROWTH 0x00040000L
+/** @} */
+
+
+/* Cave stuff */
+/** @struct cave_type
+ */
+struct cave_type
+{
+ /** @structvar info
+ * @brief Number
+ * @note Hack -- cave flags
+ */
+ u16b info;
+
+ /** @structvar feat
+ * @brief Number
+ * @note Hack -- feature type
+ */
+ byte feat;
+
+ /** @structvar o_idx
+ * @brief Number
+ * @note Object in this grid
+ */
+ s16b o_idx;
+
+ /** @structvar m_idx
+ * @brief Number
+ * @note Monster in this grid
+ */
+ s16b m_idx;
+
+ /** @structvar t_idx
+ * @brief Number
+ * @note trap index (in t_list) or zero
+ */
+ s16b t_idx;
+
+ /** @structvar special
+ * @brief Number
+ */
+ s16b special;
+ /** @structvar special2
+ * @brief Number
+ * @note Special cave info
+ */
+ s16b special2;
+
+ /** @structvar inscription
+ * @brief Number
+ * @note Inscription of the grid
+ */
+ s16b inscription;
+
+ /** @structvar mana
+ * @brief Number
+ * @note Magical energy of the grid
+ */
+ byte mana;
+
+ /** @structvar mimic
+ * @brief Number
+ * @note Feature to mimic
+ */
+ byte mimic;
+
+ /** @structvar effect
+ * @brief Number
+ * @note The lasting effects
+ */
+ s16b effect;
+};
+
+/** @var ANGBAND_SYS
+ * @brief String
+ * @note
+ * Hack -- The special Angband "System Suffix"\n
+ * This variable is used to choose an appropriate "pref-xxx" file
+ */
+extern cptr ANGBAND_SYS;
+
+/** @var ANGBAND_KEYBOARD
+ * @brief String
+ * @note
+ * Hack -- The special Angband "Keyboard Suffix"\n
+ * This variable is used to choose an appropriate macro-trigger definition
+ */
+extern cptr ANGBAND_KEYBOARD;
+
+/** @var ANGBAND_GRAF
+ * @brief String
+ * @note
+ * Hack -- The special Angband "Graphics Suffix"\n
+ * This variable is used to choose an appropriate "graf-xxx" file
+ */
+extern cptr ANGBAND_GRAF;
+
+/** @var ANGBAND_DIR
+ * @brief String
+ * @note
+ * Path name: The main "lib" directory\n
+ * This variable is not actually used anywhere in the code
+ */
+extern cptr ANGBAND_DIR;
+
+/** @var ANGBAND_DIR_APEX
+ * @brief String
+ * @note
+ * High score files (binary)\n
+ * These files may be portable between platforms
+ */
+extern cptr ANGBAND_DIR_APEX;
+
+/** @var ANGBAND_DIR_CORE
+ * @brief String
+ * @note
+ * Core lua system\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_CORE;
+
+/** @var ANGBAND_DIR_DNGN
+ * @brief String
+ * @note
+ * Textual dungeon level definition files\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_DNGN;
+
+/** @var ANGBAND_DIR_DATA
+ * @brief String
+ * @note
+ * Binary image files for the "*_info" arrays (binary)\n
+ * These files are not portable between platforms
+ */
+extern cptr ANGBAND_DIR_DATA;
+
+/** @var ANGBAND_DIR_EDIT
+ * @brief String
+ * @note
+ * Textual template files for the "*_info" arrays (ascii)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_EDIT;
+
+/** @var ANGBAND_DIR_FILE
+ * @brief String
+ * @note
+ * Various extra files (ascii)\n
+ * These files may be portable between platforms
+ */
+extern cptr ANGBAND_DIR_FILE;
+
+/** @var ANGBAND_DIR_HELP
+ * @brief String
+ * @note
+ * Help files (normal) for the online help (ascii)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_HELP;
+
+/** @var ANGBAND_DIR_INFO
+ * @brief String
+ * @note
+ * Help files (spoilers) for the online help (ascii)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_INFO;
+
+/** @var ANGBAND_DIR_MODULES
+ * @brief String
+ * @note
+ * Modules, those subdirectories are half-mirrors of lib/
+ */
+extern cptr ANGBAND_DIR_MODULES;
+
+/** @var ANGBAND_DIR_NOTE
+ * @brief String
+ * @note
+ * Textual template files for the plot files (ascii)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_NOTE;
+
+/** @var ANGBAND_DIR_SAVE
+ * @brief String
+ * @note
+ * Savefiles for current characters (binary)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_SAVE;
+
+/** @var ANGBAND_DIR_SCPT
+ * @brief String
+ * @note
+ * Scripts.\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_SCPT;
+
+/** @var ANGBAND_DIR_PREF
+ * @brief String
+ * @note
+ * Default "preference" files (ascii)\n
+ * These files are rarely portable between platforms
+ */
+extern cptr ANGBAND_DIR_PREF;
+
+/** @var ANGBAND_DIR_PATCH
+ * @brief String
+ * @note
+ * Patches, contains one subdir per patch with a patch.lua file
+ * in it and a patch_init() function in it
+ */
+extern cptr ANGBAND_DIR_PATCH;
+
+/** @var ANGBAND_DIR_USER
+ * @brief String
+ * @note
+ * User "preference" files (ascii)\n
+ * These files are rarely portable between platforms
+ */
+extern cptr ANGBAND_DIR_USER;
+
+/** @var ANGBAND_DIR_XTRA
+ * @brief String
+ * @note
+ * Various extra files (binary)\n
+ * These files are rarely portable between platforms
+ */
+extern cptr ANGBAND_DIR_XTRA;
+
+/** @var ANGBAND_DIR_CMOV
+ * @brief String
+ * @note
+ * Cmovie files of entire games (ascii)\n
+ * Apart from possible newline things, likely portable btw platforms
+ */
+extern cptr ANGBAND_DIR_CMOV;
+
+
+/** @fn los(int y1, int x1, int y2, int x2)
+ * @brief Determine if a line of sight can be traced from (x1,y1) to (x2,y2).\n
+ * @param y1 Number \n y-coordinate of the origin.
+ * @brief Origin y-coordinate
+ * @param x1 Number \n x-coordinate of the origin.
+ * @brief Origin x-coordinate
+ * @param y2 Number \n y-coordinate of the target.
+ * @brief Target y-coordinate
+ * @param x2 Number \n x-coordinate of the target.
+ * @brief Target x-coordinate
+ * @return Boolean \n TRUE if origin has line of sight to target, otherwise
+ * FALSE.
+ * @note
+ * A simple, fast, integer-based line-of-sight algorithm. By Joseph Hall,
+ * 4116 Brewster Drive, Raleigh NC 27606. Email to jnh@ecemwl.ncsu.edu.\n\n
+ * Returns TRUE if a line of sight can be traced from (x1,y1) to (x2,y2).\n\n
+ * The LOS begins at the center of the tile (x1,y1) and ends at the center of
+ * the tile (x2,y2). If los() is to return TRUE, all of the tiles this line
+ * passes through must be floor tiles, except for (x1,y1) and (x2,y2).\n\n
+ * We assume that the "mathematical corner" of a non-floor tile does not
+ * block line of sight.\n\n
+ * Because this function uses (short) ints for all calculations, overflow may
+ * occur if dx and dy exceed 90.\n\n
+ * Once all the degenerate cases are eliminated, the values "qx", "qy", and
+ * "m" are multiplied by a scale factor "f1 = abs(dx * dy * 2)", so that
+ * we can use integer arithmetic.\n\n
+ * We travel from start to finish along the longer axis, starting at the border
+ * between the first and second tiles, where the y offset = .5 * slope, taking
+ * into account the scale factor. See below.\n\n
+ * Also note that this function and the "move towards target" code do NOT
+ * share the same properties. Thus, you can see someone, target them, and
+ * then fire a bolt at them, but the bolt may hit a wall, not them. However,
+ * by clever choice of target locations, you can sometimes throw a "curve".\n\n
+ * Note that "line of sight" is not "reflexive" in all cases.\n\n
+ * Use the "projectable()" routine to test "spell/missile line of sight".\n\n*
+ * Use the "update_view()" function to determine player line-of-sight.\n\n
+ * @note (see file cave.c)
+ */
+extern bool los(int y1, int x1, int y2, int x2);
+$static bool lua_cave_is(cave_type *c_ptr, s32b flag) { return (f_info[c_ptr->feat].flags1 & flag) ? TRUE : FALSE; }
+
+/** @fn cave_is(cave_type *c_ptr, s32b flag);
+ * @brief Determine if cave "c_ptr" has feature "flag".\n
+ * @param *c_ptr cave_type \n the cave.
+ * @brief Cave
+ * @param flag Number \n the required feature flag.
+ * @brief Feature
+ * @return Boolean \n TRUE if the cave features include "flag", otherwise
+ * FALSE.
+ * @note (see file w_util.c)
+ */
+static bool lua_cave_is @ cave_is(cave_type *c_ptr, s32b flag);
+
+/** @fn cave(int y, int x);
+ * @brief Return the type of cave at grid coordinate (x,y).\n
+ * @param y Number \n y-coordinate of grid.
+ * @brief Y-coordinate
+ * @param x Number \n x-coordinate of grid.
+ * @brief X-coordinate
+ * @return cave_type \n The type of cave at grid coordinate (x,y).
+ * @note (see file lua_bind.c)
+ */
+extern cave_type *lua_get_cave @ cave(int y, int x);
+
+/** @fn set_target(int y, int x)
+ * @brief Set grid coordinate (x,y) as the target grid.\n
+ * @param y Number \n y-coordinate of grid.
+ * @brief Y-coordinate
+ * @param x Number \n x-coordinate of grid.
+ * @brief X-coordinate
+ * @note (see file lua_bind.c)
+ */
+extern void set_target(int y, int x);
+
+/** @fn get_target(int dir, int *y = 0, int *x = 0)
+ * @brief Get a target based on direction "dir" from the player.\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param *y Number
+ * @brief Target y-coordinate
+ * @param *x Number
+ * @brief Target x-coordinate
+ * @return *y Number \n The y-coordinate of the target.
+ * @return *x Number \n The x-coordinate of the target.
+ * @note
+ * The target is actually 100 grids away in direction "dir". If "dir" is 5,
+ * the actual target, if one is set, is returned.
+ * @note (see file lua_bind.c)
+ */
+extern void get_target(int dir, int *y = 0, int *x = 0);
+
+/** @var m_allow_special[max_r_idx]
+ * @brief Boolean
+ * @note "Special gene" flags for monsters
+ */
+extern bool m_allow_special[max_r_idx];
+
+/** @var k_allow_special[max_k_idx]
+ * @brief Boolean
+ * @note "Special gene" flags for objects
+ */
+extern bool k_allow_special[max_k_idx];
+
+/** @var a_allow_special[max_a_idx]
+ * @brief Boolean
+ * @note "Special gene" flags for artifacts
+ */
+extern bool a_allow_special[max_a_idx];
+
+/** @fn cave_set_feat(int y, int x, int feat)
+ * @brief Change the "feat" flag for a grid, and notice/redraw the grid
+ * @param y Number \n y-coordinate of grid.
+ * @brief Y-coordinate
+ * @param x Number \n x-coordinate of grid.
+ * @brief X-coordinate
+ * @param feat Number \n new set of feature flags.
+ * @brief Features
+ * @note (see file cave.c)
+ */
+extern void cave_set_feat(int y, int x, int feat);
+
+/** @fn show_file(cptr name, cptr what, int line, int mode)
+ * @brief Show a help file.\n
+ * @param name String \n name of the help file.
+ * @brief Filename
+ * @param what String \n hyperlink caption.
+ * @brief Caption
+ * @param line Number \n the line number from where to start the display of
+ * the file.
+ * @brief Starting line
+ * @param mode Number \n *unused*
+ * @brief *Unused*
+ * @return Boolean \n TRUE if file was shown successfully, otherwise FALSE.\n
+ * @note
+ * If the file is not found, the function will search the help, info, and file
+ * directories for the file. If it is still not found, a message is displayed
+ * and the function returns FALSE.\n\n
+ * The file is parsed once to extract colour, tag, and hyperlink
+ * information.\n\n
+ * The file is parse again to show it on the screen.
+ * @note (see file files.c)
+ */
+extern bool show_file(cptr name, cptr what, int line, int mode);
+
+/** @var target_who
+ * @brief Number
+ * @note
+ * If this is -1, the target is the player.\n
+ * If this is 0, there is no target.\n
+ * If this is >0, the target is the monster m_idx[target_who].
+ */
+extern s16b target_who;
+
+/** @var target_col
+ * @brief Number
+ * @note The column of the target grid
+ */
+extern s16b target_col;
+
+/** @var target_row
+ * @brief Number
+ * @note The row of the target grid
+ */
+extern s16b target_row;
+
+/** @var max_bact
+ * @brief Number
+ * @note Maximum building actions
+ */
+extern int max_bact;
+
+/** @var ddd[9]
+ * @brief Number
+ * @note Global array for looping through the "keypad directions"
+ */
+extern s16b ddd[9];
+
+/** @var ddx[10]
+ * @brief Number
+ * @note Global array for converting "keypad direction" into x offsets
+ */
+extern s16b ddx[10];
+
+/** @var ddy[10]
+ * @brief Number
+ * @note Global array for converting "keypad direction" into y offsets
+ */
+extern s16b ddy[10];
+
+/** @var ddx_ddd[9]
+ * @brief Number
+ * @note Global array for optimizing "ddx[ddd[i]]"
+ */
+extern s16b ddx_ddd[9];
+
+/** @var ddy_ddd[9]
+ * @brief Number
+ * @note Global array for optimizing "ddy[ddd[i]]"
+ */
+extern s16b ddy_ddd[9];
+
+
+/* Gen stuff */
+
+/** @fn load_map(char *name, int *y = 2, int *x = 2)
+ * @brief Load the map in file "name".\n
+ * @param *name String \n the name of the map file.
+ * @brief Map
+ * @param *y Number
+ * @brief Maximum y-coordinate
+ * @param *x Number
+ * @brief Maximum x-coordinate
+ * @return *y Number \n The maximum y-coordinate of the map.
+ * @return *x Number \n The maximum x-coordinate of the map.
+ * @note
+ * The map is loaded and the player is placed at the starting position.
+ * @note (see file lua_bind.c)
+ */
+extern void load_map(char *name, int *y = 2, int *x = 2);
+
+/** @fn alloc_room(int by0, int bx0, int ysize, int xsize, int *y1 = 0, int *x1 = 0, int *y2 = 0, int *x2 = 0)
+ * @brief Allocate the space needed by a room in the room_map array.\n
+ * @param by0 Number \n the y-coordinate of the block to contain the room.
+ * @brief Block y-coordinate
+ * @param bx0 Number \n the x-coordinate of the block to contain the room.
+ * @brief Block x-coordinate
+ * @param ysize Number \n the vertical size (height) of the room.
+ * @brief Room height
+ * @param xsize Number \n the horizontal size (width) of the room.
+ * @brief Room width
+ * @param *y1 Number
+ * @brief Top-right y-coordinate
+ * @param *x1 Number
+ * @brief Top-right x-coordinate
+ * @param *y2 Number
+ * @brief Bottom-left y-coordinate
+ * @param *x2 Number
+ * @brief Bottom-right x-coordinate
+ * @return Boolean \n TRUE if the room was allocated successfully, otherwise
+ * FALSE.
+ * @return *y1 Number \n The y-coordinate of the top left corner.
+ * @return *x1 Number \n The x-coordinate of the top left corner.
+ * @return *y2 Number \n The y-coordinate of the bottom right corner.
+ * @return *x2 Number \n The x-coordinate of the bottom right corner.
+ * @note
+ * Dungeon generation is not something to be messed around with unless you
+ * really, really, really know what you are doing (or you are DarkGod).
+ * @note (see file lua_bind.c, generate.c)
+ */
+extern bool alloc_room(int by0, int bx0, int ysize, int xsize, int *y1 = 0, int *x1 = 0, int *y2 = 0, int *x2 = 0);
+
+/** @var option_ingame_help
+ * @brief Boolean
+ * @note Ingame contextual help flag
+ */
+extern bool option_ingame_help;
+
+/* Misc stuff */
+/** @fn input_box(cptr title, int max);
+ * @brief Create an input box and ask the user a question.\n
+ * @param title String \n the title of the box, which should take the form of
+ * a question. For example, "New name?".
+ * @brief Title
+ * @param max Number \n the maximum length of the response.
+ * @brief Maximum response length
+ * @return String \n The answer to the question.
+ * @note
+ * The input box is placed in the middle of the screen. The default reponse is
+ * blank, and can be up to 79 characters long.
+ * @note (see file lua_bind.c, util.c)
+ */
+extern char *lua_input_box@input_box(cptr title, int max);
+
+/** @fn msg_box(cptr title);
+ * @brief Create a msg box and ask a question.\n
+ * @param title String \n the question.
+ * @brief Question
+ * @return String \n The answer.
+ * @note
+ * The message box is placed in the middle of the screen. The answer is a
+ * single character / key press.
+ * @note (see file lua_bind.c, util.c)
+ */
+extern char lua_msg_box@msg_box(cptr title);
+
+/** @fn rescale(s32b x, s32b max, s32b new_max)
+ * @brief Rescale value "x".\n
+ * @param x Number \n the original value.
+ * @brief Value
+ * @param max Number \n the original maximum that value could have.
+ * @brief Original maximum
+ * @param new_max Number \n the new maximum that value can have.
+ * @brief New maximum
+ * @return Number \n The rescaled value of "x".
+ * @note
+ * There is no error checking here. Please don't set "max" to zero.
+ * @note (see file util.c)
+ */
+extern s32b rescale(s32b x, s32b max, s32b new_max);
+$static const char *player_name_lua(void){return (const char *)player_name;}
+
+/** @fn player_name()
+ * @brief Return the player's name.
+ * @return String \n The player's name.
+ * @note (see file w_util.c)
+ */
+const char *player_name_lua@player_name();
+
+/* Quarks */
+/** @fn quark_str(s16b num)
+ * @brief Return a quark (inscription) from the quark array.\n
+ * @param num Number \n the index to the quark string array. If this is less
+ * than zero or more than the maximum number of quarks, it is treated as zero.
+ * @brief Quark index
+ * @return String \n The quark.
+ * @note
+ * We use a global array for all inscriptions to reduce the memory
+ * spent maintaining inscriptions. Of course, it is still possible
+ * to run out of inscription memory, especially if too many different
+ * inscriptions are used, but hopefully this will be rare.\n\n
+ * We use dynamic string allocation because otherwise it is necessary
+ * to pre-guess the amount of quark activity. We limit the total
+ * number of quarks, but this is much easier to "expand" as needed.\n\n
+ * Any two items with the same inscription will have the same "quark"
+ * index, which should greatly reduce the need for inscription space.\n\n
+ * Note that "quark zero" is NULL and should not be "dereferenced".
+ * @note (see file util.c)
+ */
+extern cptr quark_str(s16b num);
+
+/** @fn quark_add(cptr str)
+ * @brief Add a quark (inscription) to the quark array.\n
+ * @param str String \n the quark to add to the array.
+ * @brief Quark
+ * @return Number \n The index to the quark array for this quark
+ * @note
+ * The array is searched to see if the quark already exists. If so, the index
+ * to the existing quark is returned.\n
+ * If there is no room, 0 (NULL reference) is returned.
+ * @note (see file util.c)
+ */
+extern s16b quark_add(cptr str);
+
+/* Modules */
+/** @fn module_reset_dir(cptr dir, cptr new_path)
+ * @brief Redirect one of the ToME directories.\n
+ * @param dir String \n the name of the directory (not the full path).
+ * @brief Directory
+ * @param new_path String \n the new path of "dir" under ANGBAND_DIR_MODULES.\n
+ * @brief New path
+ * @note (see file modules.c)
+ */
+extern void module_reset_dir(cptr dir, cptr new_path);
+
+/** @fn scansubdir(cptr dir)
+ * @brief Scan sub-directory "dir".\n
+ * @param dir String \n the sub-directory to scan.
+ * @brief Directory
+ * @note
+ * Nicer wrapper around TERM_XTRA_SCANSUBDIR\n\n
+ * This function sets scansubdir_dir and calls the SCANSUBDIR terminal hook.
+ * @note (see file util.c)
+ */
+extern void scansubdir(cptr dir);
+
+/** @fn file_exist(char *buf)
+ * @brief Check if file "buf" exists.\n
+ * @param *buf String \n the file to be tested.
+ * @brief Filename
+ * @return Boolean \n TRUE if the file exists, otherwise FALSE.
+ * @note (see file loadsave.c)
+ */
+extern bool file_exist(char *buf);
+
+/** @var game_module
+ * @brief String
+ * @note The name of the current game module
+ */
+extern cptr game_module;
+
+/* Input */
+/** @fn get_keymap_dir(char ch)
+ * @brief Get a direction from the keyboard according to the keymap.\n
+ * @param ch String \n the character representing a direction.
+ * @brief Direction
+ * @return Number \n The direction represented by "ch". It will be in the
+ * range 0 to 9.
+ * @note
+ * If "ch" is a number, the number is used. Otherwise the direction is
+ * chosen from the Original or Rogue keymaps.\n
+ * If the direction is 5, it is set to 0.
+ * @note (see file util.c)
+ */
+extern int get_keymap_dir(char ch);
+
+/*
+ * Timers
+ */
+/** @struct timer_type
+ */
+struct timer_type
+{
+ /** @structvar *next
+ * @brief timer_type
+ * @note The next timer in the list
+ */
+ timer_type *next;
+
+ /** @structvar enabled
+ * @brief Boolean
+ * @note Is it currently counting?
+ */
+ bool enabled;
+
+ /** @structvar delay
+ * @brief Number
+ * @note Delay between activations
+ */
+ s32b delay;
+ /** @structvar countdown
+ * @brief Number
+ * @note The current number of turns passed, when it reaches delay it fires
+ */
+ s32b countdown;
+
+ /** @structvar callback
+ * @brief String
+ * @note The lua function to call upon firing(no C callback yet .. maybe)
+ */
+ cptr callback;
+};
+
+/** @fn *new_timer(cptr callback, s32b delay)
+ * @brief Create a timer with callback "callback" and delay "delay".\n
+ * @param callback String \n the callback associated with the timer.
+ * @brief Callback
+ * @param delay Number \n the delay associated with the timer.
+ * @brief Delay
+ * @return timer_type \n The new timer.
+ * @note
+ * The timer's countdown is also set to "delay". The timer is disabled.
+ * @note (see file util.c)
+ */
+extern timer_type *new_timer(cptr callback, s32b delay);
+
+/** @fn del_timer(timer_type *t_ptr)
+ * @brief Delete timer "t_ptr".\n
+ * @param *t_ptr timer_type \n the timer to be deleted.
+ * @brief Timer
+ * @note (see file util.c)
+ */
+extern void del_timer(timer_type *t_ptr);
+
+/*
+ * Lists
+ */
+/** @struct list_type
+ */
+struct list_type
+{
+};
+
+/** @fn create_list(int size);
+ * @dgonly
+ * @brief Create an empty list big enough to store "size" strings.\n
+ * @param size Number \n the number of strings the list will hold.
+ * @brief List size
+ * @return list_type \n The empty list.
+ * @note (see file lua_bind.c)
+ */
+extern list_type *lua_create_list@create_list(int size);
+
+/** @fn delete_list(list_type *, int size);
+ * @dgonly
+ * @brief Delete the list of strings.\n
+ * @param * list_type \n the list of strings.
+ * @brief List
+ * @param size Number \n the number of strings the list holds.
+ * @brief List size
+ * @note
+ * All the strings in the list are deleted first, then the list is deleted.
+ * @note (see file lua_bind.c)
+ */
+extern void lua_delete_list@delete_list(list_type *, int size);
+
+/** @fn add_to_list(list_type *, int idx, cptr str);
+ * @dgonly
+ * @brief Add string "str" to list in position "idx".\n
+ * @param * list_type \n the list of strings.
+ * @brief List
+ * @param idx Number \n the index of the list where the string will be added.
+ * @brief Index
+ * @param str String \n the string to be added.
+ * @brief String
+ * @note
+ * Too bad if there was something in that position already.
+ * You have been warned.
+ * @note (see file lua_bind.c)
+ */
+extern void lua_add_to_list@add_to_list(list_type *, int idx, cptr str);
+
+/** @fn display_list(int y, int x, int h, int w, cptr title, list_type *list, int max, int begin, int sel, byte sel_color);
+ * @dgonly
+ * @brief Display a scrollable boxed list with a selected item.\n
+ * @param y Number \n the y-coordinate of the top-left corner of the box.
+ * @brief Top-left y-coordinate
+ * @param x Number \n the x-coordinate of the top-left corner of the box.
+ * @brief Top-left x-coordinate
+ * @param h Number \n the height of the box.
+ * @brief Height
+ * @param w Number \n the width of the box.
+ * @brief Width
+ * @param title String \n the title for the list box.
+ * @brief Title
+ * @param *list list_type \n the list of strings to be displayed.
+ * @brief List
+ * @param max Number \n the maximum number of strings to display.
+ * @brief Maximum displayed strings
+ * @param begin Number \n the index of the first string to display.
+ * @brief Start index
+ * @param sel Number \n the index of the selected string.
+ * @brief Selected index
+ * @param sel_color Number \n the colour of the selected string.
+ * @brief Selected colour
+ * @note
+ * The title of the list is displayed in TERM_L_BLUE and the unselected strings
+ * are displayed in TERM_WHITE.
+ * @note (see file util.c)
+ */
+extern void lua_display_list@display_list(int y, int x, int h, int w, cptr title, list_type *list, int max, int begin, int sel, byte sel_color);
+
+extern errr file_character(cptr name, bool full);
+extern void calc_bonuses(bool silent);
+
+extern void note_spot(int y, int x);
+extern void lite_spot(int y, int x);
+
+extern bool drop_text_left(byte c, cptr s, int y, int o);
+extern bool drop_text_right(byte c, cptr s, int y, int o);
diff --git a/src/variable.c b/src/variable.c
new file mode 100644
index 00000000..0eb0fadf
--- /dev/null
+++ b/src/variable.c
@@ -0,0 +1,1604 @@
+/* File: variable.c */
+
+/* Purpose: Angband variables */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Hack -- Link a copyright message into the executable
+ */
+cptr copyright[5] =
+{
+ "Copyright (c) 1989 James E. Wilson, Robert A. Keoneke",
+ "",
+ "This software may be copied and distributed for educational, research,",
+ "and not for profit purposes provided that this copyright and statement",
+ "are included in all such copies."
+};
+
+int max_macrotrigger = 0;
+char *macro_template = NULL;
+char *macro_modifier_chr;
+char *macro_modifier_name[MAX_MACRO_MOD];
+char *macro_trigger_name[MAX_MACRO_TRIG];
+char *macro_trigger_keycode[2][MAX_MACRO_TRIG];
+
+/*
+ * Executable version
+ */
+byte version_major;
+byte version_minor;
+byte version_patch;
+byte version_extra = VERSION_EXTRA;
+
+/*
+ * Savefile version
+ */
+byte sf_major; /* Savefile's "version_major" */
+byte sf_minor; /* Savefile's "version_minor" */
+byte sf_patch; /* Savefile's "version_patch" */
+byte sf_extra; /* Savefile's "version_extra" */
+u32b vernum;
+
+/*
+ * Savefile information
+ */
+u32b sf_xtra; /* Operating system info */
+u32b sf_when; /* Time when savefile created */
+u16b sf_lives; /* Number of past "lives" with this file */
+u16b sf_saves; /* Number of "saves" during this life */
+
+/*
+ * Run-time arguments
+ */
+bool_ arg_fiddle; /* Command arg -- Request fiddle mode */
+bool_ arg_wizard; /* Command arg -- Request wizard mode */
+bool_ arg_sound; /* Command arg -- Request special sounds */
+bool_ arg_graphics; /* Command arg -- Request graphics mode */
+bool_ arg_force_original; /* Command arg -- Request original keyset */
+bool_ arg_force_roguelike; /* Command arg -- Request roguelike keyset */
+bool_ arg_bigtile = FALSE; /* Command arg -- Request big tile mode */
+
+/*
+ * Various things
+ */
+
+bool_ character_generated; /* The character exists */
+bool_ character_dungeon; /* The character has a dungeon */
+bool_ character_loaded; /* The character was loaded from a savefile */
+bool_ character_saved; /* The character was just saved to a savefile */
+
+bool_ character_icky; /* The game is in an icky full screen mode */
+bool_ character_xtra; /* The game is in an icky startup mode */
+
+u32b seed_flavor; /* Hack -- consistent object colors */
+
+s16b command_cmd; /* Current "Angband Command" */
+
+s16b command_arg; /* Gives argument of current command */
+s16b command_rep; /* Gives repetition of current command */
+s16b command_dir; /* Gives direction of current command */
+
+s16b command_wrk; /* See "cmd1.c" */
+
+s16b command_new; /* Command chaining from inven/equip view */
+
+s32b energy_use; /* Energy use this turn */
+
+bool_ create_up_stair; /* Auto-create "up stairs" */
+bool_ create_down_stair; /* Auto-create "down stairs" */
+
+bool_ create_up_shaft; /* Auto-create "up shaft" */
+bool_ create_down_shaft; /* Auto-create "down shaft" */
+
+bool_ msg_flag; /* Used in msg_print() for "buffering" */
+
+bool_ alive; /* True if game is running */
+
+bool_ death; /* True if player has died */
+
+s16b running; /* Current counter for running, if any */
+s16b resting; /* Current counter for resting, if any */
+
+s16b cur_hgt; /* Current dungeon height */
+s16b cur_wid; /* Current dungeon width */
+s16b dun_level; /* Current dungeon level */
+s16b old_dun_level; /* Old dungeon level */
+s16b num_repro; /* Current reproducer count */
+s16b object_level; /* Current object creation level */
+s16b monster_level; /* Current monster creation level */
+
+s32b turn; /* Current game turn */
+s32b old_turn; /* Turn when level began (feelings) */
+
+bool_ wizard; /* Is the player currently in Wizard mode? */
+
+bool_ use_sound; /* The "sound" mode is enabled */
+bool_ use_graphics; /* The "graphics" mode is enabled */
+bool_ use_bigtile = FALSE;
+byte graphics_mode; /* Current graphics mode */
+
+u16b total_winner; /* Semi-Hack -- Game has been won */
+u16b has_won; /* Semi-Hack -- Game has been won */
+
+u16b noscore; /* Track various "cheating" conditions */
+
+s16b signal_count; /* Hack -- Count interupts */
+
+bool_ inkey_base; /* See the "inkey()" function */
+bool_ inkey_xtra; /* See the "inkey()" function */
+bool_ inkey_scan; /* See the "inkey()" function */
+bool_ inkey_flag; /* See the "inkey()" function */
+
+s16b coin_type; /* Hack -- force coin type */
+
+bool_ opening_chest; /* Hack -- prevent chest generation */
+
+bool_ shimmer_monsters; /* Hack -- optimize multi-hued monsters */
+bool_ shimmer_objects; /* Hack -- optimize multi-hued objects */
+
+bool_ repair_monsters; /* Hack -- optimize detect monsters */
+bool_ repair_objects; /* Hack -- optimize detect objects */
+
+s16b inven_nxt; /* Hack -- unused */
+bool_ hack_mind;
+bool_ hack_corruption;
+int artifact_bias;
+bool_ is_autosave = FALSE;
+
+s16b inven_cnt; /* Number of items in inventory */
+s16b equip_cnt; /* Number of items in equipment */
+
+s16b o_max = 1; /* Number of allocated objects */
+s16b o_cnt = 0; /* Number of live objects */
+
+s16b m_max = 1; /* Number of allocated monsters */
+s16b m_cnt = 0; /* Number of live monsters */
+
+s16b hack_m_idx = 0; /* Hack -- see "process_monsters()" */
+s16b hack_m_idx_ii = 0;
+bool_ multi_rew = FALSE;
+char summon_kin_type; /* Hack, by Julian Lighton: summon 'relatives' */
+
+int total_friends = 0;
+s32b total_friend_levels = 0;
+
+int leaving_quest = 0;
+
+
+
+/*
+ * Hack - the destination file for text_out_to_file.
+ */
+FILE *text_out_file = NULL;
+
+
+/*
+ * Hack -- function hook to output (colored) text to the
+ * screen or to a file.
+ */
+void (*text_out_hook)(byte a, cptr str) = text_out_to_screen;
+
+
+/*
+ * Hack -- Where to wrap the text when using text_out(). Use the default
+ * value (for example the screen width) when 'text_out_wrap' is 0.
+ */
+int text_out_wrap = 0;
+
+
+/*
+ * Hack -- Indentation for the text when using text_out().
+ */
+int text_out_indent = 0;
+
+
+/*
+ * The "highscore" file descriptor, if available.
+ */
+int highscore_fd = -1;
+
+
+/*
+ * Software options (set via the '=' command). See "tables.c"
+ */
+
+
+/* Option Set 1 -- User Interface */
+
+bool_ rogue_like_commands; /* Rogue-like commands */
+bool_ quick_messages; /* Activate quick messages */
+bool_ other_query_flag; /* Prompt for various information */
+bool_ carry_query_flag; /* Prompt before picking things up */
+bool_ use_old_target; /* Use old target by default */
+bool_ always_pickup; /* Pick things up by default */
+bool_ prompt_pickup_heavy; /* Don't pick up the corpses */
+bool_ always_repeat; /* Repeat obvious commands */
+bool_ depth_in_feet; /* Show dungeon level in feet */
+
+bool_ stack_force_notes; /* Merge inscriptions when stacking */
+bool_ stack_force_costs; /* Merge discounts when stacking */
+
+bool_ show_labels; /* Show labels in object listings */
+bool_ show_weights; /* Show weights in object listings */
+bool_ show_choices; /* Show choices in certain sub-windows */
+bool_ show_details; /* Show details in certain sub-windows */
+
+bool_ ring_bell; /* Ring the bell (on errors, etc) */
+
+bool_ show_inven_graph; /* Show graphics in inventory */
+bool_ show_equip_graph; /* Show graphics in equip list */
+bool_ show_store_graph; /* Show graphics in store */
+
+
+
+/* Option Set 2 -- Disturbance */
+
+bool_ find_ignore_stairs; /* Run past stairs */
+bool_ find_ignore_doors; /* Run through open doors */
+bool_ find_cut; /* Run past known corners */
+bool_ find_examine; /* Run into potential corners */
+
+bool_ disturb_move; /* Disturb whenever any monster moves */
+bool_ disturb_near; /* Disturb whenever viewable monster moves */
+bool_ disturb_panel; /* Disturb whenever map panel changes */
+bool_ disturb_detect; /* Disturb whenever leaving trap-detected area */
+bool_ disturb_state; /* Disturn whenever player state changes */
+bool_ disturb_minor; /* Disturb whenever boring things happen */
+bool_ disturb_other; /* Disturb whenever various things happen */
+
+bool_ alert_hitpoint; /* Alert user to critical hitpoints */
+bool_ alert_failure; /* Alert user to various failures */
+bool_ last_words; /* Get last words upon dying */
+bool_ speak_unique; /* Speaking uniques + shopkeepers */
+bool_ small_levels; /* Allow unusually small dungeon levels */
+bool_ empty_levels; /* Allow empty 'arena' levels */
+bool_ always_small_level; /* Small levels */
+bool_ player_symbols; /* Use varying symbols for the player char */
+bool_ plain_descriptions; /* Plain object descriptions */
+bool_ stupid_monsters; /* Monsters use old AI */
+bool_ auto_destroy; /* Known worthless items are destroyed without confirmation */
+bool_ confirm_stairs; /* Prompt before staircases... */
+bool_ wear_confirm; /* Confirm before putting on known cursed items */
+bool_ disturb_pets; /* Pets moving nearby disturb us */
+
+
+/* Option Set 3 -- Game-Play */
+
+bool_ auto_haggle; /* Auto-haggle in stores */
+
+bool_ auto_scum; /* Auto-scum for good levels */
+
+bool_ stack_allow_items; /* Allow weapons and armor to stack */
+bool_ stack_allow_wands; /* Allow wands/staffs/rods to stack */
+
+bool_ expand_look; /* Expand the power of the look command */
+bool_ expand_list; /* Expand the power of the list commands */
+
+bool_ view_perma_grids; /* Map remembers all perma-lit grids */
+bool_ view_torch_grids; /* Map remembers all torch-lit grids */
+
+bool_ monster_lite; /* Allow some monsters to carry light */
+
+bool_ dungeon_align; /* Generate dungeons with aligned rooms */
+bool_ dungeon_stair; /* Generate dungeons with connected stairs */
+
+bool_ flow_by_sound; /* Monsters track new player location */
+
+bool_ track_follow; /* Monsters follow the player */
+bool_ track_target; /* Monsters target the player */
+
+bool_ smart_learn; /* Monsters learn from their mistakes */
+bool_ smart_cheat; /* Monsters exploit player weaknesses */
+
+
+/* Option Set 4 -- Efficiency */
+
+bool_ view_reduce_lite; /* Reduce lite-radius when running */
+bool_ view_reduce_view; /* Reduce view-radius in town */
+
+bool_ avoid_abort; /* Avoid checking for user abort */
+bool_ avoid_shimmer; /* Avoid processing extra shimmering */
+bool_ avoid_other; /* Avoid processing special colors */
+
+bool_ flush_failure; /* Flush input on any failure */
+bool_ flush_disturb; /* Flush input on disturbance */
+bool_ flush_command; /* Flush input before every command */
+
+bool_ fresh_before; /* Flush output before normal commands */
+bool_ fresh_after; /* Flush output after normal commands */
+bool_ fresh_message; /* Flush output after all messages */
+
+bool_ hilite_player; /* Hilite the player with the cursor */
+
+bool_ view_yellow_lite; /* Use special colors for torch-lit grids */
+bool_ view_bright_lite; /* Use special colors for 'viewable' grids */
+
+bool_ view_granite_lite; /* Use special colors for wall grids (slow) */
+bool_ view_special_lite; /* Use special colors for floor grids (slow) */
+
+/* Option set 5 -- Testing */
+
+bool_ testing_stack; /* Test the stacking code */
+
+bool_ testing_carry; /* Test the carrying code */
+
+
+/* Cheating options */
+
+bool_ cheat_peek; /* Peek into object creation */
+bool_ cheat_hear; /* Peek into monster creation */
+bool_ cheat_room; /* Peek into dungeon creation */
+bool_ cheat_xtra; /* Peek into something else */
+bool_ cheat_know; /* Know complete monster info */
+bool_ cheat_live; /* Allow player to avoid death */
+
+
+/* Special options */
+
+byte hitpoint_warn; /* Hitpoint warning (0 to 9) */
+
+byte delay_factor; /* Delay factor (0 to 9) */
+
+bool_ autosave_l; /* Autosave before entering new levels */
+bool_ autosave_t; /* Timed autosave */
+s16b autosave_freq; /* Autosave frequency */
+
+
+/*
+ * Dungeon variables
+ */
+
+s16b feeling; /* Most recent feeling */
+s16b rating; /* Level's current rating */
+
+bool_ good_item_flag; /* True if "Artifact" on this level */
+
+bool_ closing_flag; /* Dungeon is closing */
+
+/*
+ * Dungeon size info
+ */
+
+s16b max_panel_rows, max_panel_cols;
+s16b panel_row_min, panel_row_max;
+s16b panel_col_min, panel_col_max;
+s16b panel_col_prt, panel_row_prt;
+
+/*
+ * Dungeon graphics info
+ * Why the first two are byte and the rest s16b???
+ */
+byte feat_wall_outer = FEAT_WALL_OUTER; /* Outer wall of rooms */
+byte feat_wall_inner = FEAT_WALL_INNER; /* Inner wall of rooms */
+s16b floor_type[100]; /* Dungeon floor */
+s16b fill_type[100]; /* Dungeon filler */
+
+/*
+ * Targetting variables
+ */
+s16b target_who;
+s16b target_col;
+s16b target_row;
+
+/*
+ * Health bar variable -DRS-
+ */
+s16b health_who;
+
+/*
+ * Monster race to track
+ */
+s16b monster_race_idx;
+s16b monster_ego_idx;
+
+/*
+ * Object to track
+ */
+object_type *tracked_object;
+
+
+
+/*
+ * User info
+ */
+int player_uid;
+
+/*
+ * Current player's character name
+ */
+char player_name[32];
+
+/*
+ * Stripped version of "player_name"
+ */
+char player_base[32];
+
+/*
+ * What killed the player
+ */
+char died_from[80];
+
+/*
+ * Hack -- Textual "history" for the Player
+ */
+char history[4][60];
+
+/*
+ * Buffer to hold the current savefile name
+ */
+char savefile[1024];
+
+
+/*
+ * Array of grids lit by player lite (see "cave.c")
+ */
+s16b lite_n;
+s16b lite_y[LITE_MAX];
+s16b lite_x[LITE_MAX];
+
+/*
+ * Array of grids viewable to the player (see "cave.c")
+ */
+s16b view_n;
+byte view_y[VIEW_MAX];
+byte view_x[VIEW_MAX];
+
+/*
+ * Array of grids for use by various functions (see "cave.c")
+ */
+s16b temp_n;
+byte temp_y[TEMP_MAX];
+byte temp_x[TEMP_MAX];
+
+
+/*
+ * Number of active macros.
+ */
+s16b macro__num;
+
+/*
+ * Array of macro patterns [MACRO_MAX]
+ */
+cptr *macro__pat;
+
+/*
+ * Array of macro actions [MACRO_MAX]
+ */
+cptr *macro__act;
+
+/*
+ * Array of macro types [MACRO_MAX]
+ */
+bool_ *macro__cmd;
+
+/*
+ * Current macro action [1024]
+ */
+char *macro__buf;
+
+
+/*
+ * The number of quarks
+ */
+s16b quark__num;
+
+/*
+ * The pointers to the quarks [QUARK_MAX]
+ */
+cptr *quark__str;
+
+
+/*
+ * The next "free" index to use
+ */
+u16b message__next;
+
+/*
+ * The index of the oldest message (none yet)
+ */
+u16b message__last;
+
+/*
+ * The next "free" offset
+ */
+u16b message__head;
+
+/*
+ * The offset to the oldest used char (none yet)
+ */
+u16b message__tail;
+
+/*
+ * The array of offsets, by index [MESSAGE_MAX]
+ */
+u16b *message__ptr;
+
+/*
+ * The array of colors, by index [MESSAGE_MAX]
+ */
+byte *message__color;
+
+/*
+ * The array of type, by index [MESSAGE_MAX]
+ */
+byte *message__type;
+
+/*
+ * The array of message counts, by index [MESSAGE_MAX]
+ */
+u16b *message__count;
+
+/*
+ * The array of chars, by offset [MESSAGE_BUF]
+ */
+char *message__buf;
+
+
+/*
+ * The array of normal options
+ */
+u32b option_flag[8];
+u32b option_mask[8];
+
+
+/*
+ * The array of window options
+ */
+u32b window_flag[8];
+u32b window_mask[8];
+
+
+/*
+ * The array of window pointers
+ */
+term *angband_term[ANGBAND_TERM_MAX];
+
+
+/*
+ * Standard window names
+ */
+char angband_term_name[ANGBAND_TERM_MAX][80] =
+{
+ "ToME",
+ "Mirror",
+ "Recall",
+ "Choice",
+ "Term-4",
+ "Term-5",
+ "Term-6",
+ "Term-7"
+};
+
+
+/*
+ * Global table of color definitions
+ */
+byte angband_color_table[256][4] =
+{
+ {0x00, 0x00, 0x00, 0x00}, /* TERM_DARK */
+ {0x00, 0xFF, 0xFF, 0xFF}, /* TERM_WHITE */
+ {0x00, 0x80, 0x80, 0x80}, /* TERM_SLATE */
+ {0x00, 0xFF, 0x80, 0x00}, /* TERM_ORANGE */
+ {0x00, 0xC0, 0x00, 0x00}, /* TERM_RED */
+ {0x00, 0x00, 0x80, 0x40}, /* TERM_GREEN */
+ {0x00, 0x00, 0x00, 0xFF}, /* TERM_BLUE */
+ {0x00, 0x80, 0x40, 0x00}, /* TERM_UMBER */
+ {0x00, 0x40, 0x40, 0x40}, /* TERM_L_DARK */
+ {0x00, 0xC0, 0xC0, 0xC0}, /* TERM_L_WHITE */
+ {0x00, 0xFF, 0x00, 0xFF}, /* TERM_VIOLET */
+ {0x00, 0xFF, 0xFF, 0x00}, /* TERM_YELLOW */
+ {0x00, 0xFF, 0x00, 0x00}, /* TERM_L_RED */
+ {0x00, 0x00, 0xFF, 0x00}, /* TERM_L_GREEN */
+ {0x00, 0x00, 0xFF, 0xFF}, /* TERM_L_BLUE */
+ {0x00, 0xC0, 0x80, 0x40} /* TERM_L_UMBER */
+};
+
+
+/*
+ * Standard sound names
+ */
+char angband_sound_name[SOUND_MAX][16] =
+{
+ "",
+ "hit",
+ "miss",
+ "flee",
+ "drop",
+ "kill",
+ "level",
+ "death",
+ "study",
+ "teleport",
+ "shoot",
+ "quaff",
+ "zap",
+ "walk",
+ "tpother",
+ "hitwall",
+ "eat",
+ "store1",
+ "store2",
+ "store3",
+ "store4",
+ "dig",
+ "opendoor",
+ "shutdoor",
+ "tplevel",
+ "scroll",
+ "buy",
+ "sell",
+ "warn",
+ "rocket",
+ "n_kill",
+ "u_kill",
+ "quest",
+ "heal",
+ "x_heal",
+ "bite",
+ "claw",
+ "m_spell",
+ "summon",
+ "breath",
+ "ball",
+ "m_heal",
+ "atkspell",
+ "evil",
+ "touch",
+ "sting",
+ "crush",
+ "slime",
+ "wail",
+ "winner",
+ "fire",
+ "acid",
+ "elec",
+ "cold",
+ "illegal",
+ "fail",
+ "wakeup",
+ "invuln",
+ "fall",
+ "pain",
+ "destitem",
+ "moan",
+ "show",
+ "unused",
+ "explode",
+};
+
+
+/*
+ * The array of "cave grids" [MAX_WID][MAX_HGT].
+ * Not completely allocated, that would be inefficient
+ * Not completely hardcoded, that would overflow memory
+ */
+cave_type *cave[MAX_HGT];
+
+/*
+ * The array of dungeon items [max_o_idx]
+ */
+object_type *o_list;
+
+/*
+ * The array of dungeon monsters [max_m_idx]
+ */
+monster_type *m_list;
+
+/*
+ * The array of to keep monsters [max_m_idx]
+ */
+monster_type *km_list;
+
+
+/*
+ * Maximum number of towns
+ */
+u16b max_towns;
+u16b max_real_towns;
+
+/*
+ * The towns [max_towns]
+ */
+town_type *town_info;
+
+/*
+ * The size of "alloc_kind_table" (at most max_k_idx * 4)
+ */
+s16b alloc_kind_size;
+
+/*
+ * The entries in the "kind allocator table"
+ */
+alloc_entry *alloc_kind_table;
+
+/*
+ * The flag to tell if alloc_kind_table contains valid entries
+ * for normal (i.e. kind_is_legal) object allocation
+ */
+bool_ alloc_kind_table_valid = FALSE;
+
+
+/*
+ * The size of "alloc_race_table" (at most max_r_idx)
+ */
+s16b alloc_race_size;
+
+/*
+ * The entries in the "race allocator table"
+ */
+alloc_entry *alloc_race_table;
+
+
+/*
+ * Specify attr/char pairs for visual special effects
+ * Be sure to use "index & 0x7F" to avoid illegal access
+ */
+byte misc_to_attr[256];
+char misc_to_char[256];
+
+
+/*
+ * Specify attr/char pairs for inventory items (by tval)
+ * Be sure to use "index & 0x7F" to avoid illegal access
+ */
+byte tval_to_attr[128];
+char tval_to_char[128];
+
+
+/*
+ * Keymaps for each "mode" associated with each keypress.
+ */
+cptr keymap_act[KEYMAP_MODES][256];
+
+
+
+/*** Player information ***/
+
+/*
+ * Static player info record
+ */
+player_type p_body;
+
+/*
+ * Pointer to the player info
+ */
+player_type *p_ptr = &p_body;
+
+/*
+ * Pointer to the player tables
+ * (sex, race, race mod, class, magic)
+ */
+player_sex *sp_ptr;
+player_race *rp_ptr;
+player_race_mod *rmp_ptr;
+player_class *cp_ptr;
+player_spec *spp_ptr;
+
+
+/*
+ * More spell info
+ */
+u32b alchemist_known_egos[32];
+u32b alchemist_known_artifacts[6];
+u32b alchemist_gained;
+
+
+/*
+ * Calculated base hp values for player at each level,
+ * store them so that drain life + restore life does not
+ * affect hit points. Also prevents shameless use of backup
+ * savefiles for hitpoint acquirement.
+ */
+s16b player_hp[PY_MAX_LEVEL];
+
+/*
+ * The alchemy recipe arrays
+ */
+header *al_head;
+alchemist_recipe *alchemist_recipes;
+char *al_name;
+artifact_select_flag *a_select_flags;
+
+/*
+ * The vault generation arrays
+ */
+header *v_head;
+vault_type *v_info;
+char *v_name;
+char *v_text;
+
+/*
+ * The terrain feature arrays
+ */
+header *f_head;
+feature_type *f_info;
+char *f_name;
+char *f_text;
+
+/*
+ * The object kind arrays
+ */
+header *k_head;
+object_kind *k_info;
+char *k_name;
+char *k_text;
+
+/*
+ * The artifact arrays
+ */
+header *a_head;
+artifact_type *a_info;
+char *a_name;
+char *a_text;
+
+/*
+ * The item set arrays
+ */
+header *set_head;
+set_type *set_info;
+char *set_name;
+char *set_text;
+
+/*
+ * The ego-item arrays
+ */
+header *e_head;
+ego_item_type *e_info;
+char *e_name;
+char *e_text;
+
+/*
+ * The randart arrays
+ */
+header *ra_head;
+randart_part_type *ra_info;
+randart_gen_type ra_gen[30];
+
+/* jk */
+/* the trap-arrays */
+header *t_head;
+trap_type *t_info;
+char *t_name;
+char *t_text;
+
+/*
+ * The monster race arrays
+ */
+header *r_head;
+monster_race *r_info;
+char *r_name;
+char *r_text;
+
+/*
+ * The monster ego race arrays
+ */
+header *re_head;
+monster_ego *re_info;
+char *re_name;
+
+/*
+ * The dungeon types arrays
+ */
+header *d_head;
+dungeon_info_type *d_info;
+char *d_name;
+char *d_text;
+
+/*
+ * Player abilities arrays
+ */
+header *ab_head;
+ability_type *ab_info;
+char *ab_name;
+char *ab_text;
+
+/*
+ * Player skills arrays
+ */
+header *s_head;
+skill_type *s_info;
+char *s_name;
+char *s_text;
+
+/*
+ * Player race arrays
+ */
+header *rp_head;
+player_race *race_info;
+char *rp_name;
+char *rp_text;
+
+/*
+ * Player mod race arrays
+ */
+header *rmp_head;
+player_race_mod *race_mod_info;
+char *rmp_name;
+char *rmp_text;
+
+/*
+ * Player class arrays
+ */
+header *c_head;
+player_class *class_info;
+char *c_name;
+char *c_text;
+meta_class_type *meta_class_info;
+
+/*
+ * The wilderness features arrays
+ */
+header *wf_head;
+wilderness_type_info *wf_info;
+char *wf_name;
+char *wf_text;
+int wildc2i[256];
+
+/*
+ * The store/building types arrays
+ */
+header *st_head;
+store_info_type *st_info;
+char *st_name;
+/* char *st_text; */
+
+/*
+ * The building actions types arrays
+ */
+header *ba_head;
+store_action_type *ba_info;
+char *ba_name;
+/* char *ba_text; */
+
+/*
+ * The owner types arrays
+ */
+header *ow_head;
+owner_type *ow_info;
+char *ow_name;
+/* char *ow_text; */
+
+/*
+ * The dungeon types arrays
+ */
+header *d_head;
+dungeon_info_type *d_info;
+char *d_name;
+char *d_text;
+
+/*
+ * Hack -- The special Angband "System Suffix"
+ * This variable is used to choose an appropriate "pref-xxx" file
+ */
+cptr ANGBAND_SYS = "xxx";
+
+/*
+ * Hack -- The special Angband "Keyboard Suffix"
+ * This variable is used to choose an appropriate macro-trigger definition
+ */
+#ifdef JP
+cptr ANGBAND_KEYBOARD = "JAPAN";
+#else
+cptr ANGBAND_KEYBOARD = "0";
+#endif
+
+/*
+ * Hack -- The special Angband "Graphics Suffix"
+ * This variable is used to choose an appropriate "graf-xxx" file
+ */
+cptr ANGBAND_GRAF = "old";
+
+/*
+ * Path name: The main "lib" directory
+ * This variable is not actually used anywhere in the code
+ */
+cptr ANGBAND_DIR;
+
+/*
+ * High score files (binary)
+ * These files may be portable between platforms
+ */
+cptr ANGBAND_DIR_APEX;
+
+/*
+ * Core lua system
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_CORE;
+
+/*
+ * Textual dungeon level definition files
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_DNGN;
+
+/*
+ * Binary image files for the "*_info" arrays (binary)
+ * These files are not portable between platforms
+ */
+cptr ANGBAND_DIR_DATA;
+
+/*
+ * Textual template files for the "*_info" arrays (ascii)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_EDIT;
+
+/*
+ * Various extra files (ascii)
+ * These files may be portable between platforms
+ */
+cptr ANGBAND_DIR_FILE;
+
+/*
+ * Help files (normal) for the online help (ascii)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_HELP;
+
+/*
+ * Help files (spoilers) for the online help (ascii)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_INFO;
+
+/*
+ * Modules, those subdirectories are half-mirrors of lib/
+ */
+cptr ANGBAND_DIR_MODULES;
+
+/*
+ * Patches, contains one subdir per patch with a patch.lua file
+ * in it and a patch_init() function in it
+ */
+cptr ANGBAND_DIR_PATCH;
+
+/*
+ * Textual template files for the plot files (ascii)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_NOTE;
+
+/*
+ * Savefiles for current characters (binary)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_SAVE;
+
+/*
+ * Scripts.
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_SCPT;
+
+/*
+ * Default "preference" files (ascii)
+ * These files are rarely portable between platforms
+ */
+cptr ANGBAND_DIR_PREF;
+
+/*
+ * User "preference" files (ascii)
+ * These files are rarely portable between platforms
+ */
+cptr ANGBAND_DIR_USER;
+
+/*
+ * Various extra files (binary)
+ * These files are rarely portable between platforms
+ */
+cptr ANGBAND_DIR_XTRA;
+
+/*
+ * Cmovie files of entire games (ascii)
+ * Apart from possible newline things, likely portable btw platforms
+ */
+
+cptr ANGBAND_DIR_CMOV;
+
+/*
+ * Some variables values are created on the fly XXX XXX
+ */
+
+char pref_tmp_value[8];
+
+
+
+/*
+ * Total Hack -- allow all items to be listed (even empty ones)
+ * This is only used by "do_cmd_inven_e()" and is cleared there.
+ */
+bool_ item_tester_full;
+
+
+/*
+ * Here is a "pseudo-hook" used during calls to "get_item()" and
+ * "show_inven()" and "show_equip()", and the choice window routines.
+ */
+byte item_tester_tval;
+
+
+/*
+ * Here is a "hook" used during calls to "get_item()" and
+ * "show_inven()" and "show_equip()", and the choice window routines.
+ */
+bool_ (*item_tester_hook)(object_type*);
+
+
+
+/*
+ * Current "comp" function for ang_sort()
+ */
+bool_ (*ang_sort_comp)(vptr u, vptr v, int a, int b);
+
+
+/*
+ * Current "swap" function for ang_sort()
+ */
+void (*ang_sort_swap)(vptr u, vptr v, int a, int b);
+
+
+
+/*
+ * Hack -- function hooks to restrict "get_mon_num_prep()" function
+ */
+bool_ (*get_mon_num_hook)(int r_idx);
+bool_ (*get_mon_num2_hook)(int r_idx);
+
+
+/*
+ * Hack -- function hook to restrict "get_obj_num_prep()" function
+ */
+bool_ (*get_obj_num_hook)(int k_idx);
+
+
+/* Hack, monk armour */
+bool_ monk_armour_aux;
+bool_ monk_notify_aux;
+
+bool_ easy_open = TRUE;
+bool_ easy_disarm = TRUE;
+bool_ easy_tunnel = FALSE;
+
+
+/*
+ * Maximum size of the wilderness map
+ */
+u16b max_wild_x;
+u16b max_wild_y;
+
+/*
+ * Wilderness map
+ */
+wilderness_map **wild_map;
+
+
+/*
+ * Maximum number of skills in s_info.txt
+ */
+u16b old_max_s_idx = 0;
+u16b max_s_idx;
+
+/*
+ * Maximum number of abilities in ab_info.txt
+ */
+u16b max_ab_idx;
+
+/*
+ * Maximum number of monsters in r_info.txt
+ */
+u16b max_r_idx;
+
+/*
+ * Maximum number of ego monsters in re_info.txt
+ */
+u16b max_re_idx;
+
+/*
+ * Maximum number of items in k_info.txt
+ */
+u16b max_k_idx;
+
+/*
+ * Maximum number of vaults in v_info.txt
+ */
+u16b max_v_idx;
+
+/*
+ * Maximum number of terrain features in f_info.txt
+ */
+u16b max_f_idx;
+
+/*
+ * Maximum number of alchemist recipies in al_info.txt
+ */
+u16b max_al_idx;
+
+/*
+ * Maximum number of artifacts in a_info.txt
+ */
+u16b max_a_idx;
+
+/*
+ * Maximum number of ego-items in e_info.txt
+ */
+u16b max_e_idx;
+
+/*
+ * Maximum number of randarts in ra_info.txt
+ */
+u16b max_ra_idx;
+
+/*
+ * Maximum number of dungeon types in d_info.txt
+ */
+u16b max_d_idx;
+
+/*
+ * Maximum number of stores types in st_info.txt
+ */
+u16b max_st_idx;
+
+/*
+ * Item sets
+ */
+s16b max_set_idx = 1;
+
+/*
+ * Maximum number of players info in p_info.txt
+ */
+u16b max_rp_idx;
+u16b max_rmp_idx;
+u16b max_c_idx;
+u16b max_mc_idx;
+
+/*
+ * Maximum number of actions types in ba_info.txt
+ */
+u16b max_ba_idx;
+
+/*
+ * Maximum number of owner types in ow_info.txt
+ */
+u16b max_ow_idx;
+
+/*
+ * Maximum number of objects in the level
+ */
+u16b max_o_idx;
+
+/*
+ * Maximum number of monsters in the level
+ */
+u16b max_m_idx;
+
+/*
+ * Maximum number of traps in tr_info.txt
+ */
+u16b max_t_idx;
+
+/*
+ * Maximum number of wilderness features in wf_info.txt
+ */
+u16b max_wf_idx;
+
+/*
+ * Flags for initialization
+ */
+int init_flags;
+
+/* True if on an ambush */
+bool_ ambush_flag;
+
+/* True if on fated level */
+bool_ fate_flag;
+
+/* No breeders */
+s16b no_breeds;
+
+/* Carried monsters can't take the damage if this is them which attack the player */
+bool_ carried_monster_hit = FALSE;
+
+/*
+ * Random artifacts.
+ */
+random_artifact random_artifacts[MAX_RANDARTS];
+/* These three used to be constants but now are set by modules */
+s32b RANDART_WEAPON;
+s32b RANDART_ARMOR;
+s32b RANDART_JEWEL;
+
+/*
+ * Current bounties. An array of tuples of two, with the first being the
+ * r_idx of the monster, and the second the monster's worth.
+ */
+s16b bounties[MAX_BOUNTIES][2];
+
+/*
+ * Spell description
+ */
+bool_ info_spell = FALSE;
+char spell_txt[50];
+
+/*
+ * Random spells.
+ */
+random_spell random_spells[MAX_SPELLS];
+s16b spell_num;
+
+/*
+ * Runecrafter's selfmade spells.
+ */
+rune_spell rune_spells[MAX_RUNES];
+s16b rune_num;
+
+/*
+ * Fate.
+ */
+fate fates[MAX_FATES];
+
+/*
+ * Which dungeon ?
+ * 0 = Wilderness
+ * 1 = Mirkwood
+ * 2 = Mordor
+ * 3 = Angband
+ * 4 = Barrow Downs
+ * 5 = Mount Doom
+ * 6 = Nether Realm
+ * etc. (see d_info.txt)
+ */
+byte dungeon_type;
+s16b *max_dlv;
+
+/*
+ * Number of total bounties the player had had.
+ */
+u32b total_bounties;
+
+/* The Doppleganger index in m_list */
+s16b doppleganger;
+
+/* To allow wilderness encounters */
+bool_ generate_encounter;
+
+/* Permanent dungeons ? */
+bool_ permanent_levels;
+
+/* Autoroler */
+bool_ autoroll;
+
+/* Point based */
+bool_ point_based;
+
+/* Maximize, preserve, special levels, ironman_rooms */
+bool_ maximize, preserve, special_lvls, ironman_rooms;
+
+/* In inventory option window, just erase the letters,
+ * rather that displaying the list without the invalid
+ * selections */
+bool_ inventory_no_move;
+
+/* Notes patch */
+bool_ take_notes, auto_notes;
+
+/*
+ * Such an ugly hack ...
+ */
+bool_ *m_allow_special;
+bool_ *k_allow_special;
+bool_ *a_allow_special;
+
+/*
+ * Gives a random object to newly created characters
+ */
+bool_ rand_birth;
+
+/*
+ * Fast autoroller
+ */
+bool_ fast_autoroller;
+
+/*
+ * Which monsters are allowed ?
+ */
+bool_ joke_monsters;
+
+/*
+ * How will mana staf & weapons of life act
+ */
+bool_ munchkin_multipliers = TRUE;
+
+/*
+ * Center view
+ */
+bool_ center_player = FALSE;
+
+/*
+ * Plots
+ */
+s16b plots[MAX_PLOTS];
+
+/*
+ * Random quest
+ */
+random_quest random_quests[MAX_RANDOM_QUEST];
+
+/*
+ * Show exp left
+ */
+bool_ exp_need;
+
+/*
+ * Auto load old colors;
+ */
+bool_ autoload_old_colors;
+
+/*
+ * Fated ?
+ */
+bool_ fate_option;
+
+/*
+ * Special levels
+ */
+bool_ *special_lvl[MAX_DUNGEON_DEPTH];
+bool_ generate_special_feeling = FALSE;
+
+/*
+ * Auto more
+ */
+bool_ auto_more;
+
+/*
+ * Dungeon flags
+ */
+u32b dungeon_flags1;
+u32b dungeon_flags2;
+
+/*
+ * The last character displayed
+ */
+birther previous_char;
+
+/*
+ * Race histories
+ */
+hist_type *bg;
+int max_bg_idx;
+
+/*
+ * Powers
+ */
+s16b power_max = POWER_MAX_INIT;
+power_type *powers_type;
+
+/*
+ * Variable savefile stuff
+ */
+s32b extra_savefile_parts = 0;
+
+/*
+ * Quests
+ */
+s16b max_q_idx = MAX_Q_IDX_INIT;
+quest_type *quest;
+
+/*
+ * Display the player as a special symbol when in bad health ?
+ */
+bool_ player_char_health;
+
+
+/*
+ * The spell list of schools
+ */
+s16b max_spells;
+spell_type *school_spells;
+s16b max_schools;
+school_type *schools;
+
+/*
+ * Lasting spell effects
+ */
+int project_time = 0;
+s32b project_time_effect = 0;
+effect_type effects[MAX_EFFECTS];
+
+/*
+ * General skills set
+ */
+char gen_skill_basem[MAX_SKILLS];
+u32b gen_skill_base[MAX_SKILLS];
+char gen_skill_modm[MAX_SKILLS];
+s16b gen_skill_mod[MAX_SKILLS];
+
+/*
+ * Display stats as linear
+ */
+bool_ linear_stats;
+
+/*
+ * Table of "cli" macros.
+ */
+cli_comm *cli_info;
+int cli_total = 0;
+
+/*
+ * max_bact, only used so that lua scripts can add new bacts without worrying about the numbers
+ */
+int max_bact = 54;
+
+/*
+ * Max corruptions
+ */
+s16b max_corruptions = 0;
+
+/*
+ * Ingame contextual help
+ */
+bool_ option_ingame_help = TRUE;
+
+/*
+ * Automatizer enabled status
+ */
+bool_ automatizer_enabled = FALSE;
+
+/*
+ * Location of the last teleportation thath affected the level
+ */
+s16b last_teleportation_y = -1;
+s16b last_teleportation_x = -1;
+
+/*
+ * The current game module
+ */
+cptr game_module;
+s32b VERSION_MAJOR;
+s32b VERSION_MINOR;
+s32b VERSION_PATCH;
+
+/*
+ * Some module info
+ */
+s32b max_plev = 50;
+s32b DUNGEON_DEATH = 28;
+
+/*
+ * Gods
+ */
+deity_type *deity_info;
+s32b max_gods = MAX_GODS_INIT;
+
+/*
+ * Timers
+ */
+timer_type *gl_timers = NULL;
+
+/**
+ * Get the version string.
+ */
+const char *get_version_string()
+{
+ static char version_str[80];
+ static bool_ initialized = 0;
+ if (!initialized) {
+ sprintf(version_str, "%s %ld.%ld.%ld%s",
+ game_module,
+ (long int) VERSION_MAJOR,
+ (long int) VERSION_MINOR,
+ (long int) VERSION_PATCH, IS_CVS);
+ initialized = TRUE;
+ }
+ return version_str;
+}
diff --git a/src/wild.c b/src/wild.c
new file mode 100644
index 00000000..7a9d1c51
--- /dev/null
+++ b/src/wild.c
@@ -0,0 +1,1275 @@
+/* File: generate.c */
+
+/* Purpose: Wilderness & Town related things */
+
+/*
+ * Copyright (c) 2001 James E. Wilson, Robert A. Koeneke, DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+
+/*
+ * Various defines for the wilderness
+ */
+#define DUN_WILD_VAULT 50 /* Chance of finding a wilderness vault. */
+
+/*
+ * Various defines for the towns
+ */
+#define TOWN_NORMAL_FLOOR 70
+#define TOWN_BORDER 90
+
+
+/*
+ * Helper for plasma generation.
+ */
+static void perturb_point_mid(int x1, int x2, int x3, int x4,
+ int xmid, int ymid, int rough, int depth_max)
+{
+ /*
+ * Average the four corners & perturb it a bit.
+ * tmp is a random int +/- rough
+ */
+ int tmp2 = rough * 2 + 1;
+ int tmp = randint(tmp2) - (rough + 1);
+
+ int avg = ((x1 + x2 + x3 + x4) / 4) + tmp;
+
+ /* Division always rounds down, so we round up again */
+ if (((x1 + x2 + x3 + x4) % 4) > 1) avg++;
+
+ /* Normalize */
+ if (avg < 0) avg = 0;
+ if (avg > depth_max) avg = depth_max;
+
+ /* Set the new value. */
+ cave[ymid][xmid].feat = (byte)avg;
+}
+
+
+static void perturb_point_end(int x1, int x2, int x3,
+ int xmid, int ymid, int rough, int depth_max)
+{
+ /*
+ * Average the three corners & perturb it a bit.
+ * tmp is a random int +/- rough
+ */
+ int tmp2 = rough * 2 + 1;
+ int tmp = randint(tmp2) - (rough + 1);
+
+ int avg = ((x1 + x2 + x3) / 3) + tmp;
+
+ /* Division always rounds down, so we round up again */
+ if ((x1 + x2 + x3) % 3) avg++;
+
+ /* Normalize */
+ if (avg < 0) avg = 0;
+ if (avg > depth_max) avg = depth_max;
+
+ /* Set the new value. */
+ cave[ymid][xmid].feat = (byte)avg;
+}
+
+
+/*
+ * A generic function to generate the plasma fractal.
+ * Note that it uses ``cave_feat'' as temporary storage.
+ * The values in ``cave_feat'' after this function
+ * are NOT actual features; They are raw heights which
+ * need to be converted to features.
+ *
+ * So we shouldn't call cave_set_feat in the helper functions
+ * above.
+ */
+static void plasma_recursive(int x1, int y1, int x2, int y2,
+ int depth_max, int rough)
+{
+ /* Find middle */
+ int xmid = (x2 - x1) / 2 + x1;
+ int ymid = (y2 - y1) / 2 + y1;
+
+ /* Are we done? */
+ if (x1 + 1 == x2) return;
+
+ perturb_point_mid(cave[y1][x1].feat, cave[y2][x1].feat, cave[y1][x2].feat,
+ cave[y2][x2].feat, xmid, ymid, rough, depth_max);
+
+ perturb_point_end(cave[y1][x1].feat, cave[y1][x2].feat, cave[ymid][xmid].feat,
+ xmid, y1, rough, depth_max);
+
+ perturb_point_end(cave[y1][x2].feat, cave[y2][x2].feat, cave[ymid][xmid].feat,
+ x2, ymid, rough, depth_max);
+
+ perturb_point_end(cave[y2][x2].feat, cave[y2][x1].feat, cave[ymid][xmid].feat,
+ xmid, y2, rough, depth_max);
+
+ perturb_point_end(cave[y2][x1].feat, cave[y1][x1].feat, cave[ymid][xmid].feat,
+ x1, ymid, rough, depth_max);
+
+
+ /* Recurse the four quadrants */
+ plasma_recursive(x1, y1, xmid, ymid, depth_max, rough);
+ plasma_recursive(xmid, y1, x2, ymid, depth_max, rough);
+ plasma_recursive(x1, ymid, xmid, y2, depth_max, rough);
+ plasma_recursive(xmid, ymid, x2, y2, depth_max, rough);
+}
+
+
+/*
+ * Load a town or generate a terrain level using "plasma" fractals.
+ *
+ * x and y are the coordinates of the area in the wilderness.
+ * Border and corner are optimization flags to speed up the
+ * generation of the fractal terrain.
+ * If border is set then only the border of the terrain should
+ * be generated (for initializing the border structure).
+ * If corner is set then only the corners of the area are needed.
+ *
+ * Return the number of floor grids
+ */
+int generate_area(int y, int x, bool_ border, bool_ corner, bool_ refresh)
+{
+ int road, entrance;
+ int x1, y1;
+ int hack_floor = 0;
+
+ /* Number of the town (if any) */
+ p_ptr->town_num = wf_info[wild_map[y][x].feat].entrance;
+ if (!p_ptr->town_num) p_ptr->town_num = wild_map[y][x].entrance;
+
+ {
+ int roughness = 1; /* The roughness of the level. */
+ int terrain[3][3]; /* The terrain around the current area */
+ int ym, xm, yp, xp;
+
+ /* Place the player at the center */
+ if (!p_ptr->oldpx) p_ptr->oldpx = MAX_WID / 2;
+ if (!p_ptr->oldpy) p_ptr->oldpy = MAX_HGT / 2;
+
+ /* Initialize the terrain array */
+ ym = ((y - 1) < 0) ? 0 : (y - 1);
+ xm = ((x - 1) < 0) ? 0 : (x - 1);
+ yp = ((y + 1) >= max_wild_y) ? (max_wild_y - 1) : (y + 1);
+ xp = ((x + 1) >= max_wild_x) ? (max_wild_x - 1) : (x + 1);
+ terrain[0][0] = wild_map[ym][xm].feat;
+ terrain[0][1] = wild_map[ym][x].feat;
+ terrain[0][2] = wild_map[ym][xp].feat;
+ terrain[1][0] = wild_map[y][xm].feat;
+ terrain[1][1] = wild_map[y][x].feat;
+ terrain[1][2] = wild_map[y][xp].feat;
+ terrain[2][0] = wild_map[yp][xm].feat;
+ terrain[2][1] = wild_map[yp][x].feat;
+ terrain[2][2] = wild_map[yp][xp].feat;
+
+ /* Hack -- Use the "simple" RNG */
+ Rand_quick = TRUE;
+
+ /* Hack -- Induce consistant town layout */
+ Rand_value = wild_map[y][x].seed;
+
+ if (!corner)
+ {
+ /* Create level background */
+ for (y1 = 0; y1 < MAX_HGT; y1++)
+ {
+ for (x1 = 0; x1 < MAX_WID; x1++)
+ {
+ cave_set_feat(y1, x1, MAX_WILD_TERRAIN / 2);
+ }
+ }
+ }
+
+ /*
+ * Initialize the four corners
+ * ToDo: calculate the medium height of the adjacent
+ * terrains for every corner.
+ */
+ cave_set_feat(1, 1, (byte)rand_int(MAX_WILD_TERRAIN));
+ cave_set_feat(MAX_HGT - 2, 1, (byte)rand_int(MAX_WILD_TERRAIN));
+ cave_set_feat(1, MAX_WID - 2, (byte)rand_int(MAX_WILD_TERRAIN));
+ cave_set_feat(MAX_HGT - 2, MAX_WID - 2, (byte)rand_int(MAX_WILD_TERRAIN));
+
+ if (!corner)
+ {
+ /* x1, y1, x2, y2, num_depths, roughness */
+ plasma_recursive(1, 1, MAX_WID - 2, MAX_HGT - 2, MAX_WILD_TERRAIN - 1, roughness);
+ }
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+
+ for (y1 = 1; y1 < MAX_HGT - 1; y1++)
+ {
+ for (x1 = 1; x1 < MAX_WID - 1; x1++)
+ {
+ cave_set_feat(y1, x1,
+ wf_info[terrain[1][1]].terrain[cave[y1][x1].feat]);
+ }
+ }
+
+ }
+
+ /* Should we create a town ? */
+ if ((p_ptr->town_num > 0) && (p_ptr->town_num < 1000))
+ {
+ /* Create the town */
+ int xstart = 0;
+ int ystart = 0;
+
+ /* Initialize the town */
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("t_info.txt", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ }
+ else
+ {
+ /* Reset the town flag */
+ p_ptr->town_num = 0;
+ }
+
+ if (!corner)
+ {
+ /*
+ * Place roads in the wilderness
+ * ToDo: make the road a bit more interresting
+ */
+ road = wf_info[wild_map[y][x].feat].road;
+
+ if (road & ROAD_NORTH)
+ {
+ /* North road */
+ for (y1 = 1; y1 < MAX_HGT / 2; y1++)
+ {
+ x1 = MAX_WID / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+
+ if (road & ROAD_SOUTH)
+ {
+ /* North road */
+ for (y1 = MAX_HGT / 2; y1 < MAX_HGT - 1; y1++)
+ {
+ x1 = MAX_WID / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+
+ if (road & ROAD_EAST)
+ {
+ /* East road */
+ for (x1 = MAX_WID / 2; x1 < MAX_WID - 1; x1++)
+ {
+ y1 = MAX_HGT / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+
+ if (road & ROAD_WEST)
+ {
+ /* West road */
+ for (x1 = 1; x1 < MAX_WID / 2; x1++)
+ {
+ y1 = MAX_HGT / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+ }
+
+ /* Hack -- Use the "simple" RNG */
+ Rand_quick = TRUE;
+
+ /* Hack -- Induce consistant town layout */
+ Rand_value = wild_map[y][x].seed;
+
+ entrance = wf_info[wild_map[y][x].feat].entrance;
+ if (!entrance) entrance = wild_map[y][x].entrance;
+
+ /* Create the dungeon if requested on the map */
+ if (entrance >= 1000)
+ {
+ int dy, dx;
+
+ dy = rand_range(6, cur_hgt - 6);
+ dx = rand_range(6, cur_wid - 6);
+
+ cave_set_feat(dy, dx, FEAT_MORE);
+ cave[dy][dx].special = entrance - 1000;
+ cave[dy][dx].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+
+ /* MEGA HACK -- set at least one floor grid */
+ for (y1 = 1; y1 < cur_hgt - 1; y1++)
+ {
+ for (x1 = 1; x1 < cur_wid - 1; x1++)
+ {
+ if (cave_floor_bold(y1, x1)) hack_floor++;
+ }
+ }
+
+ /* NO floor ? put one */
+ if (!hack_floor)
+ {
+ cave_set_feat(cur_hgt / 2, cur_wid / 2, FEAT_GRASS);
+ cave[cur_hgt / 2][cur_wid / 2].special = 0;
+ hack_floor = 1;
+ }
+
+ /* Set the monster generation level to the wilderness level */
+ monster_level = wf_info[wild_map[y][x].feat].level;
+
+ /* Set the object generation level to the wilderness level */
+ object_level = wf_info[wild_map[y][x].feat].level;
+
+ return hack_floor;
+}
+
+/*
+ * Border of the wilderness area
+ */
+static border_type border;
+
+/*
+ * Build the wilderness area outside of the town.
+ * -KMW-
+ */
+void wilderness_gen(int refresh)
+{
+ int i, y, x, hack_floor;
+ bool_ daytime;
+ int xstart = 0;
+ int ystart = 0;
+ cave_type *c_ptr;
+
+ /* Init the wilderness */
+ process_dungeon_file("w_info.txt", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+
+ x = p_ptr->wilderness_x;
+ y = p_ptr->wilderness_y;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* North border */
+ generate_area(y - 1, x, TRUE, FALSE, refresh);
+
+ for (i = 1; i < MAX_WID - 1; i++)
+ {
+ border.north[i] = cave[MAX_HGT - 2][i].feat;
+ }
+
+ /* South border */
+ generate_area(y + 1, x, TRUE, FALSE, refresh);
+
+ for (i = 1; i < MAX_WID - 1; i++)
+ {
+ border.south[i] = cave[1][i].feat;
+ }
+
+ /* West border */
+ generate_area(y, x - 1, TRUE, FALSE, refresh);
+
+ for (i = 1; i < MAX_HGT - 1; i++)
+ {
+ border.west[i] = cave[i][MAX_WID - 2].feat;
+ }
+
+ /* East border */
+ generate_area(y, x + 1, TRUE, FALSE, refresh);
+
+ for (i = 1; i < MAX_HGT - 1; i++)
+ {
+ border.east[i] = cave[i][1].feat;
+ }
+
+ /* North west corner */
+ generate_area(y - 1, x - 1, FALSE, TRUE, refresh);
+ border.north_west = cave[MAX_HGT - 2][MAX_WID - 2].feat;
+
+ /* North east corner */
+ generate_area(y - 1, x + 1, FALSE, TRUE, refresh);
+ border.north_east = cave[MAX_HGT - 2][1].feat;
+
+ /* South west corner */
+ generate_area(y + 1, x - 1, FALSE, TRUE, refresh);
+ border.south_west = cave[1][MAX_WID - 2].feat;
+
+ /* South east corner */
+ generate_area(y + 1, x + 1, FALSE, TRUE, refresh);
+ border.south_east = cave[1][1].feat;
+
+
+ /* Create terrain of the current area */
+ hack_floor = generate_area(y, x, FALSE, FALSE, refresh);
+
+
+ /* Special boundary walls -- North */
+ for (i = 0; i < MAX_WID; i++)
+ {
+ cave[0][i].mimic = border.north[i];
+ cave_set_feat(0, i, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- South */
+ for (i = 0; i < MAX_WID; i++)
+ {
+ cave[MAX_HGT - 1][i].mimic = border.south[i];
+ cave_set_feat(MAX_HGT - 1, i, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- West */
+ for (i = 0; i < MAX_HGT; i++)
+ {
+ cave[i][0].mimic = border.west[i];
+ cave_set_feat(i, 0, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- East */
+ for (i = 0; i < MAX_HGT; i++)
+ {
+ cave[i][MAX_WID - 1].mimic = border.east[i];
+ cave_set_feat(i, MAX_WID - 1, FEAT_PERM_SOLID);
+ }
+
+ /* North west corner */
+ cave[0][0].mimic = border.north_west;
+
+ /* Make sure it has correct CAVE_WALL flag set */
+ cave_set_feat(0, 0, cave[0][0].feat);
+
+ /* North east corner */
+ cave[0][MAX_WID - 1].mimic = border.north_east;
+
+ /* Make sure it has correct CAVE_WALL flag set */
+ cave_set_feat(0, MAX_WID - 1, cave[0][MAX_WID - 1].feat);
+
+ /* South west corner */
+ cave[MAX_HGT - 1][0].mimic = border.south_west;
+
+ /* Make sure it has correct CAVE_WALL flag set */
+ cave_set_feat(MAX_HGT - 1, 0, cave[MAX_HGT - 1][0].feat);
+
+ /* South east corner */
+ cave[MAX_HGT - 1][MAX_WID - 1].mimic = border.south_east;
+
+ /* Make sure it has correct CAVE_WALL flag set */
+ cave_set_feat(MAX_HGT - 1, MAX_WID - 1, cave[MAX_HGT - 1][MAX_WID - 1].feat);
+
+
+ /* Day time */
+ if ((turn % (10L * DAY)) < ((10L * DAY) / 2))
+ daytime = TRUE;
+ else
+ daytime = FALSE;
+
+ /* Light up or darken the area */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ if (daytime)
+ {
+ /* Assume lit */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Hack -- Memorize lit grids if allowed */
+ if (view_perma_grids) c_ptr->info |= (CAVE_MARK);
+ }
+ else
+ {
+ /* Darken "boring" features */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_REMEMBER))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ }
+ }
+
+ player_place(p_ptr->oldpy, p_ptr->oldpx);
+
+ if (!refresh)
+ {
+ int lim = (generate_encounter == TRUE) ? 60 : MIN_M_ALLOC_TN;
+
+ /*
+ * Can't have more monsters than floor grids -1(for the player,
+ * not needed but safer
+ */
+ if (lim > hack_floor - 1) lim = hack_floor - 1;
+
+ /* Make some residents */
+ for (i = 0; i < lim; i++)
+ {
+ /* Make a resident */
+ (void)alloc_monster((generate_encounter == TRUE) ? 0 : 3, (generate_encounter == TRUE) ? FALSE : TRUE);
+ }
+
+ if (generate_encounter) ambush_flag = TRUE;
+ generate_encounter = FALSE;
+ }
+
+ /* Set rewarded quests to finished */
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (quest[i].status == QUEST_STATUS_REWARDED)
+ quest[i].status = QUEST_STATUS_FINISHED;
+ }
+
+ process_hooks(HOOK_WILD_GEN, "(d)", FALSE);
+}
+
+/*
+ * Build the wilderness area.
+ * -DG-
+ */
+void wilderness_gen_small()
+{
+ int i, j, entrance;
+ int xstart = 0;
+ int ystart = 0;
+
+ /* To prevent stupid things */
+ for (i = 0; i < MAX_WID; i++)
+ {
+ for (j = 0; j < MAX_HGT; j++)
+ {
+ cave_set_feat(j, i, FEAT_EKKAIA);
+ }
+ }
+
+ /* Init the wilderness */
+ process_dungeon_file("w_info.txt", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+
+ /* Fill the map */
+ for (i = 0; i < max_wild_x; i++)
+ {
+ for (j = 0; j < max_wild_y; j++)
+ {
+ entrance = wf_info[wild_map[j][i].feat].entrance;
+ if (!entrance) entrance = wild_map[j][i].entrance;
+
+ if (wild_map[j][i].entrance)
+ {
+ cave_set_feat(j, i, FEAT_MORE);
+ }
+ else
+ {
+ cave_set_feat(j, i, wf_info[wild_map[j][i].feat].feat);
+ }
+
+ if ((cave[j][i].feat == FEAT_MORE) && (entrance >= 1000))
+ {
+ cave[j][i].special = entrance - 1000;
+ }
+
+ /* Show it if we know it */
+ if (wild_map[j][i].known)
+ {
+ cave[j][i].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ }
+
+ /* Place the player */
+ p_ptr->px = p_ptr->wilderness_x;
+ p_ptr->py = p_ptr->wilderness_y;
+
+ /* Set rewarded quests to finished */
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (quest[i].status == QUEST_STATUS_REWARDED)
+ quest[i].status = QUEST_STATUS_FINISHED;
+ }
+
+ process_hooks(HOOK_WILD_GEN, "(d)", TRUE);
+}
+
+/* Show a small radius of wilderness around the player */
+void reveal_wilderness_around_player(int y, int x, int h, int w)
+{
+ int i, j;
+
+ /* Circle or square ? */
+ if (h == 0)
+ {
+ for (i = x - w; i < x + w; i++)
+ {
+ for (j = y - w; j < y + w; j++)
+ {
+ /* Bound checking */
+ if (!in_bounds(j, i)) continue;
+
+ /* Severe bound checking */
+ if ((i < 0) || (i >= max_wild_x) || (j < 0) || (j >= max_wild_y)) continue;
+
+ /* We want a radius, not a "squarus" :) */
+ if (distance(y, x, j, i) >= w) continue;
+
+ /* New we know here */
+ wild_map[j][i].known = TRUE;
+
+ /* Only if we are in overview */
+ if (p_ptr->wild_mode)
+ {
+ cave[j][i].info |= (CAVE_GLOW | CAVE_MARK);
+
+ /* Show it */
+ lite_spot(j, i);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (i = x; i < x + w; i++)
+ {
+ for (j = y; j < y + h; j++)
+ {
+ /* Bound checking */
+ if (!in_bounds(j, i)) continue;
+
+ /* New we know here */
+ wild_map[j][i].known = TRUE;
+
+ /* Only if we are in overview */
+ if (p_ptr->wild_mode)
+ {
+ cave[j][i].info |= (CAVE_GLOW | CAVE_MARK);
+
+ /* Show it */
+ lite_spot(j, i);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Builds a store at a given pseudo-location
+ *
+ * As of 2.8.1 (?) the town is actually centered in the middle of a
+ * complete level, and thus the top left corner of the town itself
+ * is no longer at (0,0), but rather, at (qy,qx), so the constants
+ * in the comments below should be mentally modified accordingly.
+ *
+ * As of 2.7.4 (?) the stores are placed in a more "user friendly"
+ * configuration, such that the four "center" buildings always
+ * have at least four grids between them, to allow easy running,
+ * and the store doors tend to face the middle of town.
+ *
+ * The stores now lie inside boxes from 3-9 and 12-18 vertically,
+ * and from 7-17, 21-31, 35-45, 49-59. Note that there are thus
+ * always at least 2 open grids between any disconnected walls.
+ *
+ * Note the use of "town_illuminate()" to handle all "illumination"
+ * and "memorization" issues.
+ */
+static void build_store(int qy, int qx, int n, int yy, int xx)
+{
+ int y, x, y0, x0, y1, x1, y2, x2, tmp;
+
+ /* Find the "center" of the store */
+ y0 = qy + yy * 9 + 6;
+ x0 = qx + xx * 14 + 12;
+
+ /* Determine the store boundaries */
+ y1 = y0 - randint((yy == 0) ? 3 : 2);
+ y2 = y0 + randint((yy == 1) ? 3 : 2);
+ x1 = x0 - randint(5);
+ x2 = x0 + randint(5);
+
+ /* Build an invulnerable rectangular building */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ /* Create the building */
+ cave_set_feat(y, x, FEAT_PERM_EXTRA);
+ }
+ }
+
+ /* Pick a door direction (S,N,E,W) */
+ tmp = rand_int(4);
+
+ /* Re-roll "annoying" doors */
+ if (((tmp == 0) && (yy == 1)) ||
+ ((tmp == 1) && (yy == 0)) ||
+ ((tmp == 2) && (xx == 3)) ||
+ ((tmp == 3) && (xx == 0)))
+ {
+ /* Pick a new direction */
+ tmp = rand_int(4);
+ }
+
+ /* Extract a "door location" */
+ switch (tmp)
+ {
+ /* Bottom side */
+ case 0:
+ {
+ y = y2;
+ x = rand_range(x1, x2);
+ break;
+ }
+
+ /* Top side */
+ case 1:
+ {
+ y = y1;
+ x = rand_range(x1, x2);
+ break;
+ }
+
+ /* Right side */
+ case 2:
+ {
+ y = rand_range(y1, y2);
+ x = x2;
+ break;
+ }
+
+ /* Left side */
+ default:
+ {
+ y = rand_range(y1, y2);
+ x = x1;
+ break;
+ }
+ }
+
+ /* Clear previous contents, add a store door */
+ cave_set_feat(y, x, FEAT_SHOP);
+ cave[y][x].special = n;
+ cave[y][x].info |= CAVE_FREE;
+}
+
+static void build_store_circle(int qy, int qx, int n, int yy, int xx)
+{
+ int tmp, y, x, y0, x0, rad = 2 + rand_int(2);
+
+ /* Find the "center" of the store */
+ y0 = qy + yy * 9 + 6;
+ x0 = qx + xx * 14 + 12;
+
+ /* Determine the store boundaries */
+
+ /* Build an invulnerable circular building */
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ if (distance(y0, x0, y, x) > rad) continue;
+
+ /* Create the building */
+ cave_set_feat(y, x, FEAT_PERM_EXTRA);
+ }
+ }
+
+ /* Pick a door direction (S,N,E,W) */
+ tmp = rand_int(4);
+
+ /* Re-roll "annoying" doors */
+ if (((tmp == 0) && (yy == 1)) ||
+ ((tmp == 1) && (yy == 0)) ||
+ ((tmp == 2) && (xx == 3)) ||
+ ((tmp == 3) && (xx == 0)))
+ {
+ /* Pick a new direction */
+ tmp = rand_int(4);
+ }
+
+ /* Extract a "door location" */
+ switch (tmp)
+ {
+ /* Bottom side */
+ case 0:
+ {
+ for (y = y0; y <= y0 + rad; y++) cave_set_feat(y, x0, FEAT_FLOOR);
+ break;
+ }
+
+ /* Top side */
+ case 1:
+ {
+ for (y = y0 - rad; y <= y0; y++) cave_set_feat(y, x0, FEAT_FLOOR);
+ break;
+ }
+
+ /* Right side */
+ case 2:
+ {
+ for (x = x0; x <= x0 + rad; x++) cave_set_feat(y0, x, FEAT_FLOOR);
+ break;
+ }
+
+ /* Left side */
+ default:
+ {
+ for (x = x0 - rad; x <= x0; x++) cave_set_feat(y0, x, FEAT_FLOOR);
+ break;
+ }
+ }
+
+ /* Clear previous contents, add a store door */
+ cave_set_feat(y0, x0, FEAT_SHOP);
+ cave[y0][x0].special = n;
+ cave[y0][x0].info |= CAVE_FREE;
+}
+
+static void build_store_hidden(int n, int yy, int xx)
+{
+ /* Clear previous contents, add a store door */
+ cave_set_feat(yy, xx, FEAT_SHOP);
+ cave[yy][xx].special = n;
+ cave[yy][xx].info |= CAVE_FREE;
+}
+
+/* Return a list of stores */
+static int get_shops(int *rooms)
+{
+ int i, n, num = 0;
+
+ for (n = 0; n < max_st_idx; n++)
+ {
+ rooms[n] = 0;
+ }
+
+ for (n = 0; n < max_st_idx; n++)
+ {
+ int chance = 50;
+
+ if (st_info[n].flags1 & SF1_COMMON) chance += 30;
+ if (st_info[n].flags1 & SF1_RARE) chance -= 20;
+ if (st_info[n].flags1 & SF1_VERY_RARE) chance -= 30;
+
+ if (!magik(chance)) continue;
+
+ for (i = 0; i < num; ++i)
+ if (rooms[i] == n)
+ continue;
+
+ if (st_info[n].flags1 & SF1_RANDOM) rooms[num++] = n;
+ }
+
+ return num;
+}
+
+/* Generate town borders */
+static void set_border(int y, int x)
+{
+ cave_type *c_ptr;
+
+ /* Paranoia */
+ if (!in_bounds(y, x)) return;
+
+ /* Was a floor */
+ if (cave_floor_bold(y, x) ||
+ (f_info[cave[y][x].feat].flags1 & FF1_DOOR))
+ {
+ cave_set_feat(y, x, FEAT_DOOR_HEAD);
+ }
+
+ /* Was a wall */
+ else
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+
+ }
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear special attrs */
+ c_ptr->mimic = 0;
+ c_ptr->special = 0;
+ c_ptr->info |= (CAVE_ROOM);
+}
+
+
+static void town_borders(int t_idx, int qy, int qx)
+{
+ int y, x;
+
+ x = qx;
+ for (y = qy; y < qy + SCREEN_HGT - 1; y++)
+ {
+ set_border(y, x);
+ }
+
+ x = qx + SCREEN_WID - 1;
+ for (y = qy; y < qy + SCREEN_HGT - 1; y++)
+ {
+ set_border(y, x);
+ }
+
+ y = qy;
+ for (x = qx; x < qx + SCREEN_WID - 1; x++)
+ {
+ set_border(y, x);
+ }
+
+ y = qy + SCREEN_HGT - 1;
+ for (x = qx; x < qx + SCREEN_WID; x++)
+ {
+ set_border(y, x);
+ }
+}
+
+static bool_ create_townpeople_hook(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->d_char == 't') return TRUE;
+ else return FALSE;
+}
+
+
+/*
+ * Generate the "consistent" town features, and place the player
+ *
+ * Hack -- play with the R.N.G. to always yield the same town
+ * layout, including the size and shape of the buildings, the
+ * locations of the doorways, and the location of the stairs.
+ */
+static void town_gen_hack(int t_idx, int qy, int qx)
+{
+ int y, x, floor, num = 0;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ int *rooms;
+
+ /* Do we use dungeon floor or normal one */
+ if (magik(TOWN_NORMAL_FLOOR)) floor = FEAT_FLOOR;
+ else floor = 0;
+
+ /* Place some floors */
+ for (y = qy + 1; y < qy + SCREEN_HGT - 1; y++)
+ {
+ for (x = qx + 1; x < qx + SCREEN_WID - 1; x++)
+ {
+ /* Create empty floor */
+ cave_set_feat(y, x, (floor) ? floor : floor_type[rand_int(100)]);
+ cave[y][x].info |= (CAVE_ROOM | CAVE_FREE);
+ }
+ }
+
+ /* Prepare an Array of "remaining stores", and count them */
+ C_MAKE(rooms, max_st_idx, int);
+ num = get_shops(rooms);
+
+ /* Place two rows of stores */
+ for (y = 0; y < 2; y++)
+ {
+ /* Place four stores per row */
+ for (x = 0; x < 4; x++)
+ {
+ if(--num > -1)
+ {
+ /* Build that store at the proper location */
+ build_store(qy, qx, rooms[num], y, x);
+ }
+ }
+ }
+ C_FREE(rooms, max_st_idx, int);
+
+ /* Generates the town's borders */
+ if (magik(TOWN_NORMAL_FLOOR)) town_borders(t_idx, qy, qx);
+
+
+ /* Some inhabitants(leveled .. hehe :) */
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = create_townpeople_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ for (x = qx; x < qx + SCREEN_WID; x++)
+ for (y = qy; y < qy + SCREEN_HGT; y++)
+ {
+ int m_idx, r_idx;
+
+ /* Only in town */
+ if (!in_bounds(y, x)) continue;
+ if (!(cave[y][x].info & CAVE_FREE)) continue;
+ if (!cave_empty_bold(y, x)) continue;
+
+ if (rand_int(100)) continue;
+
+ r_idx = get_mon_num(0);
+ m_allow_special[r_idx] = TRUE;
+ m_idx = place_monster_one(y, x, r_idx, 0, TRUE, MSTATUS_ENEMY);
+ m_allow_special[r_idx] = FALSE;
+
+ if (m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+ if (m_ptr->level < (dun_level / 2))
+ {
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + (dun_level / 2) + randint(dun_level / 2));
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+}
+
+static void town_gen_circle(int t_idx, int qy, int qx)
+{
+ int y, x, cy, cx, rad, floor, num = 0;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ int *rooms;
+
+ /* Do we use dungeon floor or normal one */
+ if (magik(TOWN_NORMAL_FLOOR)) floor = FEAT_FLOOR;
+ else floor = 0;
+
+ rad = (SCREEN_HGT / 2);
+
+ y = qy;
+ for (x = qx + rad; x < qx + SCREEN_WID - rad; x++)
+ {
+ set_border(y, x);
+ }
+
+ y = qy + SCREEN_HGT - 1;
+ for (x = qx + rad; x < qx + SCREEN_WID - rad; x++)
+ {
+ set_border(y, x);
+ }
+ /* Place some floors */
+ for (y = qy + 1; y < qy + SCREEN_HGT - 1; y++)
+ {
+ for (x = qx + rad; x < qx + SCREEN_WID - rad; x++)
+ {
+ /* Create empty floor */
+ cave_set_feat(y, x, (floor) ? floor : floor_type[rand_int(100)]);
+ cave[y][x].info |= CAVE_ROOM | CAVE_FREE;
+ }
+ }
+
+ cy = qy + (SCREEN_HGT / 2);
+
+ cx = qx + rad;
+ for (y = cy - rad; y < cy + rad; y++)
+ for (x = cx - rad; x < cx + 1; x++)
+ {
+ int d = distance(cy, cx, y, x);
+
+ if ((d == rad) || (d == rad - 1)) set_border(y, x);
+
+ if (d < rad - 1)
+ {
+ cave_set_feat(y, x, (floor) ? floor : floor_type[rand_int(100)]);
+ cave[y][x].info |= CAVE_ROOM | CAVE_FREE;
+ }
+ }
+
+ cx = qx + SCREEN_WID - rad - 1;
+ for (y = cy - rad; y < cy + rad; y++)
+ for (x = cx; x < cx + rad + 1; x++)
+ {
+ int d = distance(cy, cx, y, x);
+
+ if ((d == rad) || (d == rad - 1)) set_border(y, x);
+
+ if (d < rad - 1)
+ {
+ cave_set_feat(y, x, (floor) ? floor : floor_type[rand_int(100)]);
+ cave[y][x].info |= CAVE_ROOM | CAVE_FREE;
+ }
+ }
+
+ /* Prepare an Array of "remaining stores", and count them */
+ C_MAKE(rooms, max_st_idx, int);
+ num = get_shops(rooms);
+
+ /* Place two rows of stores */
+ for (y = 0; y < 2; y++)
+ {
+ /* Place four stores per row */
+ for (x = 0; x < 4; x++)
+ {
+ if(--num > -1)
+ {
+ /* Build that store at the proper location */
+ build_store_circle(qy, qx, rooms[num], y, x);
+ }
+ }
+ }
+ C_FREE(rooms, max_st_idx, int);
+
+ /* Some inhabitants(leveled .. hehe :) */
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = create_townpeople_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ for (x = qx; x < qx + SCREEN_WID; x++)
+ for (y = qy; y < qy + SCREEN_HGT; y++)
+ {
+ int m_idx, r_idx;
+
+ /* Only in town */
+ if (!in_bounds(y, x)) continue;
+ if (!(cave[y][x].info & CAVE_FREE)) continue;
+ if (!cave_empty_bold(y, x)) continue;
+
+ if (rand_int(100)) continue;
+
+ r_idx = get_mon_num(0);
+ m_allow_special[r_idx] = TRUE;
+ m_idx = place_monster_one(y, x, r_idx, 0, TRUE, MSTATUS_ENEMY);
+ m_allow_special[r_idx] = FALSE;
+ if (m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+ if (m_ptr->level < (dun_level / 2))
+ {
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + (dun_level / 2) + randint(dun_level / 2));
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+}
+
+
+static void town_gen_hidden(int t_idx, int qy, int qx)
+{
+ int y, x, n, num = 0, i;
+
+ int *rooms;
+
+ /* Prepare an Array of "remaining stores", and count them */
+ C_MAKE(rooms, max_st_idx, int);
+ num = get_shops(rooms);
+
+ /* Get a number of stores to place */
+ n = rand_int(num / 2) + (num / 2);
+
+ /* Place k stores */
+ for (i = 0; i < n; i++)
+ {
+ /* Find a good spot */
+ while (TRUE)
+ {
+ y = rand_range(1, cur_hgt - 2);
+ x = rand_range(1, cur_wid - 2);
+
+ if (cave_empty_bold(y, x)) break;
+ }
+
+ if(--num > -1)
+ {
+ /* Build that store at the proper location */
+ build_store_hidden(rooms[num], y, x);
+ }
+ }
+ C_FREE(rooms, max_st_idx, int);
+}
+
+
+
+/*
+ * Town logic flow for generation of new town
+ *
+ * We start with a fully wiped cave of normal floors.
+ *
+ * Note that town_gen_hack() plays games with the R.N.G.
+ *
+ * This function does NOT do anything about the owners of the stores,
+ * nor the contents thereof. It only handles the physical layout.
+ *
+ * We place the player on the stairs at the same time we make them.
+ *
+ * Hack -- since the player always leaves the dungeon by the stairs,
+ * he is always placed on the stairs, even if he left the dungeon via
+ * word of recall or teleport level.
+ */
+void town_gen(int t_idx)
+{
+ int qy, qx;
+
+ /* Level too small to contain a town */
+ if (cur_hgt < SCREEN_HGT) return;
+ if (cur_wid < SCREEN_WID) return;
+
+ /* Center fo the level */
+ qy = (cur_hgt - SCREEN_HGT) / 2;
+ qx = (cur_wid - SCREEN_WID) / 2;
+
+ /* Build stuff */
+ switch (rand_int(3))
+ {
+ case 0:
+ {
+ town_gen_hack(t_idx, qy, qx);
+ if (wizard)
+ {
+ msg_format("Town level(normal) (%d, seed %d)",
+ t_idx, town_info[t_idx].seed);
+ }
+ break;
+ }
+
+ case 1:
+ {
+ town_gen_circle(t_idx, qy, qx);
+ if (wizard)
+ {
+ msg_format("Town level(circle)(%d, seed %d)",
+ t_idx, town_info[t_idx].seed);
+ }
+ break;
+ }
+
+ case 2:
+ {
+ town_gen_hidden(t_idx, qy, qx);
+ if (wizard)
+ {
+ msg_format("Town level(hidden)(%d, seed %d)",
+ t_idx, town_info[t_idx].seed);
+ }
+ break;
+ }
+ }
+
+ p_ptr->town_num = t_idx;
+}
diff --git a/src/wizard1.c b/src/wizard1.c
new file mode 100644
index 00000000..7daef324
--- /dev/null
+++ b/src/wizard1.c
@@ -0,0 +1,2756 @@
+/* File: wizard1.c */
+
+/* Purpose: Spoiler generation -BEN- */
+
+#include "angband.h"
+
+
+/*
+ * The spoiler file being created
+ */
+static FILE *fff = NULL;
+
+
+/*
+ * Write out `n' of the character `c' to the spoiler file
+ */
+static void spoiler_out_n_chars(int n, char c)
+{
+ while (--n >= 0) fputc(c, fff);
+}
+
+
+/*
+ * Write out `n' blank lines to the spoiler file
+ */
+static void spoiler_blanklines(int n)
+{
+ spoiler_out_n_chars(n, '\n');
+}
+
+
+/*
+ * Write a line to the spoiler file and then "underline" it with hyphens
+ */
+static void spoiler_underline(cptr str)
+{
+ fprintf(fff, "%s\n", str);
+ spoiler_out_n_chars(strlen(str), '-');
+ fprintf(fff, "\n");
+}
+
+
+/*
+ * Buffer text to the given file. (-SHAWN-)
+ * This is basically c_roff() from mon-desc.c with a few changes.
+ */
+static void spoil_out(cptr str)
+{
+ cptr r;
+
+ /* Line buffer */
+ static char roff_buf[256];
+
+ /* Current pointer into line roff_buf */
+ static char *roff_p = roff_buf;
+
+ /* Last space saved into roff_buf */
+ static char *roff_s = NULL;
+
+ /* Special handling for "new sequence" */
+ if (!str)
+ {
+ if (roff_p != roff_buf) roff_p--;
+ while (*roff_p == ' ' && roff_p != roff_buf) roff_p--;
+ if (roff_p == roff_buf) fprintf(fff, "\n");
+ else
+ {
+ *(roff_p + 1) = '\0';
+ fprintf(fff, "%s\n\n", roff_buf);
+ }
+ roff_p = roff_buf;
+ roff_s = NULL;
+ roff_buf[0] = '\0';
+ return;
+ }
+
+ /* Scan the given string, character at a time */
+ for (; *str; str++)
+ {
+ char ch = *str;
+ int wrap = (ch == '\n');
+
+ if (!isprint(ch)) ch = ' ';
+ if (roff_p >= roff_buf + 75) wrap = 1;
+ if ((ch == ' ') && (roff_p + 2 >= roff_buf + 75)) wrap = 1;
+
+ /* Handle line-wrap */
+ if (wrap)
+ {
+ *roff_p = '\0';
+ r = roff_p;
+ if (roff_s && (ch != ' '))
+ {
+ *roff_s = '\0';
+ r = roff_s + 1;
+ }
+ fprintf(fff, "%s\n", roff_buf);
+ roff_s = NULL;
+ roff_p = roff_buf;
+ while (*r) *roff_p++ = *r++;
+ }
+
+ /* Save the char */
+ if ((roff_p > roff_buf) || (ch != ' '))
+ {
+ if (ch == ' ') roff_s = roff_p;
+ *roff_p++ = ch;
+ }
+ }
+}
+
+
+/*
+ * Extract a textual representation of an attribute
+ */
+static cptr attr_to_text(byte a)
+{
+ switch (a)
+ {
+ case TERM_DARK:
+ return ("xxx");
+ case TERM_WHITE:
+ return ("White");
+ case TERM_SLATE:
+ return ("Slate");
+ case TERM_ORANGE:
+ return ("Orange");
+ case TERM_RED:
+ return ("Red");
+ case TERM_GREEN:
+ return ("Green");
+ case TERM_BLUE:
+ return ("Blue");
+ case TERM_UMBER:
+ return ("Umber");
+ case TERM_L_DARK:
+ return ("L.Dark");
+ case TERM_L_WHITE:
+ return ("L.Slate");
+ case TERM_VIOLET:
+ return ("Violet");
+ case TERM_YELLOW:
+ return ("Yellow");
+ case TERM_L_RED:
+ return ("L.Red");
+ case TERM_L_GREEN:
+ return ("L.Green");
+ case TERM_L_BLUE:
+ return ("L.Blue");
+ case TERM_L_UMBER:
+ return ("L.Umber");
+ }
+
+ /* Oops */
+ return ("Icky");
+}
+
+
+
+/*
+ * A tval grouper
+ */
+typedef struct
+{
+ byte tval;
+ cptr name;
+}
+grouper;
+
+
+
+/*
+ * Item Spoilers by: benh@phial.com (Ben Harrison)
+ */
+
+
+/*
+ * The basic items categorized by type
+ */
+static grouper group_item[] =
+{
+ { TV_SWORD, "Melee Weapons" },
+ { TV_POLEARM, NULL },
+ { TV_HAFTED, NULL },
+ { TV_AXE, NULL },
+ { TV_MSTAFF, NULL },
+
+ { TV_BOW, "Bows and Slings" },
+
+ { TV_SHOT, "Ammo" },
+ { TV_ARROW, NULL },
+ { TV_BOLT, NULL },
+
+ { TV_BOOMERANG, "Boomerangs" },
+
+ { TV_INSTRUMENT, "Instruments" },
+
+ { TV_SOFT_ARMOR, "Armour (Body)" },
+ { TV_HARD_ARMOR, NULL },
+ { TV_DRAG_ARMOR, NULL },
+
+ { TV_SHIELD, "Armour (Misc)" },
+ { TV_HELM, NULL },
+ { TV_CROWN, NULL },
+ { TV_GLOVES, NULL },
+ { TV_BOOTS, NULL },
+
+ { TV_CLOAK, "Cloaks" },
+ { TV_AMULET, "Amulets" },
+ { TV_RING, "Rings" },
+
+ { TV_SCROLL, "Scrolls" },
+ { TV_POTION, "Potions" },
+ { TV_POTION2, NULL },
+
+ { TV_FOOD, "Food" },
+
+ { TV_ROD_MAIN, "Rods" },
+ { TV_ROD, "Rod Tips" },
+ { TV_WAND, "Wands" },
+ { TV_STAFF, "Staves" },
+
+ { TV_BOOK, "Books (Magic, Gods, Music)" },
+ { TV_DAEMON_BOOK, "Demonic Equipment" },
+
+ { TV_RUNE1, "Runes" },
+ { TV_RUNE2, NULL },
+
+ { TV_BATERIE, "Essences" },
+
+ { TV_PARCHMENT, "Parchments" },
+
+ { TV_DIGGING, "Tools" },
+ { TV_TOOL, NULL },
+
+ { TV_TRAPKIT, "Trapping Kits" },
+
+ { TV_CHEST, "Chests" },
+
+ { TV_SPIKE, "Various" },
+ { TV_LITE, NULL },
+ { TV_FLASK, NULL },
+ { TV_BOTTLE, NULL },
+ { TV_JUNK, NULL },
+
+ { TV_SKELETON, "Corpses and Eggs" },
+ { TV_CORPSE, NULL },
+ { TV_EGG, NULL },
+
+ { 0, "" }
+};
+
+
+/*
+ * Describe the kind
+ */
+static void kind_info(char *buf, char *dam, char *wgt, int *lev, s32b *val, int k)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ object_kind *k_ptr;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Prepare a fake item */
+ object_prep(q_ptr, k);
+
+ /* Obtain the "kind" info */
+ k_ptr = &k_info[q_ptr->k_idx];
+
+ /* It is known */
+ q_ptr->ident |= (IDENT_KNOWN);
+
+ /* Cancel bonuses */
+ q_ptr->to_a = 0;
+ q_ptr->to_h = 0;
+ q_ptr->to_d = 0;
+
+ if ((k_ptr->tval == TV_WAND) || (k_ptr->tval == TV_STAFF))
+ {
+ hack_apply_magic_power = -99;
+ apply_magic(q_ptr, 0, FALSE, FALSE, FALSE);
+ }
+
+ /* Level */
+ (*lev) = k_ptr->level;
+
+ /* Value */
+ (*val) = object_value(q_ptr);
+
+
+ /* Hack */
+ if (!buf || !dam || !wgt) return;
+
+
+ /* Description (too brief) */
+ object_desc_store(buf, q_ptr, FALSE, 0);
+
+
+ /* Misc info */
+ strcpy(dam, "");
+
+ /* Damage */
+ switch (q_ptr->tval)
+ {
+ /* Bows */
+ case TV_BOW:
+ {
+ break;
+ }
+
+ /* Ammo */
+ case TV_SHOT:
+ case TV_BOLT:
+ case TV_ARROW:
+
+ /* Boomerangs */
+ case TV_BOOMERANG:
+
+ /* Weapons */
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_MSTAFF:
+
+ /* Tools */
+ case TV_DIGGING:
+ {
+ sprintf(dam, "%dd%d", q_ptr->dd, q_ptr->ds);
+ break;
+ }
+
+ /* Armour */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_SHIELD:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ sprintf(dam, "%d", q_ptr->ac);
+ break;
+ }
+ }
+
+
+ /* Weight */
+ sprintf(wgt, "%3ld.%ld", (long int) (q_ptr->weight / 10), (long int) (q_ptr->weight % 10));
+}
+
+
+/*
+ * Create a spoiler file for items
+ */
+static void spoil_obj_desc(cptr fname)
+{
+ int i, k, s, t, n = 0;
+
+ u16b who[200];
+
+ char buf[1024];
+
+ char wgt[80];
+ char dam[80];
+
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+
+ /* Header */
+ sprintf(buf, "Basic Items Spoilers for %s", get_version_string());
+ spoiler_underline(buf);
+ spoiler_blanklines(2);
+
+ /* More Header */
+ fprintf(fff, "%-45s %8s%7s%5s%9s\n",
+ "Description", "Dam/AC", "Wgt", "Lev", "Cost");
+ fprintf(fff, "%-45s %8s%7s%5s%9s\n",
+ "----------------------------------------",
+ "------", "---", "---", "----");
+
+ /* List the groups */
+ for (i = 0; TRUE; i++)
+ {
+ /* Write out the group title */
+ if (group_item[i].name)
+ {
+ /* Hack -- bubble-sort by cost and then level */
+ for (s = 0; s < n - 1; s++)
+ {
+ for (t = 0; t < n - 1; t++)
+ {
+ int i1 = t;
+ int i2 = t + 1;
+
+ int e1;
+ int e2;
+
+ s32b t1;
+ s32b t2;
+
+ kind_info(NULL, NULL, NULL, &e1, &t1, who[i1]);
+ kind_info(NULL, NULL, NULL, &e2, &t2, who[i2]);
+
+ if ((t1 > t2) || ((t1 == t2) && (e1 > e2)))
+ {
+ int tmp = who[i1];
+ who[i1] = who[i2];
+ who[i2] = tmp;
+ }
+ }
+ }
+
+ /* Spoil each item */
+ for (s = 0; s < n; s++)
+ {
+ int e;
+ s32b v;
+
+ /* Describe the kind */
+ kind_info(buf, dam, wgt, &e, &v, who[s]);
+
+ /* Dump it */
+ fprintf(fff, " %-45s%8s%7s%5d%9ld\n",
+ buf, dam, wgt, e, (long)(v));
+ }
+
+ /* Start a new set */
+ n = 0;
+
+ /* Notice the end */
+ if (!group_item[i].tval) break;
+
+ /* Start a new set */
+ fprintf(fff, "\n\n%s\n\n", group_item[i].name);
+ }
+
+ /* Acquire legal item types */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Skip wrong tval's */
+ if (k_ptr->tval != group_item[i].tval) continue;
+
+ /* Hack -- Skip artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART | TR3_NORM_ART)) continue;
+
+ /* Hack -- Skip Ring of Powers */
+ if (k == 785) continue;
+
+ /* Save the index */
+ who[n++] = k;
+ }
+ }
+
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+/*
+ * Artifact Spoilers by: randy@PICARD.tamu.edu (Randy Hutson)
+ */
+
+
+/*
+ * Returns a "+" string if a number is non-negative and an empty
+ * string if negative
+ */
+#define POSITIZE(v) (((v) >= 0) ? "+" : "")
+
+/*
+ * These are used to format the artifact spoiler file. INDENT1 is used
+ * to indent all but the first line of an artifact spoiler. INDENT2 is
+ * used when a line "wraps". (Bladeturner's resistances cause this.)
+ */
+#define INDENT1 " "
+#define INDENT2 " "
+
+/*
+ * MAX_LINE_LEN specifies when a line should wrap.
+ */
+#define MAX_LINE_LEN 75
+
+/*
+ * Given an array, determine how many elements are in the array
+ */
+#define N_ELEMENTS(a) (sizeof (a) / sizeof ((a)[0]))
+
+/*
+ * The artifacts categorized by type
+ */
+static grouper group_artifact[] =
+{
+ { TV_SWORD, "Edged Weapons" },
+ { TV_POLEARM, "Polearms" },
+ { TV_HAFTED, "Hafted Weapons" },
+ { TV_AXE, "Axes" },
+
+ { TV_MSTAFF, "Mage Staffs" },
+
+ { TV_BOW, "Bows" },
+
+ { TV_SHOT, "Ammo" },
+ { TV_ARROW, NULL },
+ { TV_BOLT, NULL },
+
+ { TV_BOOMERANG, "Boomerangs" },
+
+ { TV_INSTRUMENT, "Instruments" },
+
+ { TV_SOFT_ARMOR, "Body Armor" },
+ { TV_HARD_ARMOR, NULL },
+ { TV_DRAG_ARMOR, NULL },
+
+ { TV_CLOAK, "Cloaks" },
+ { TV_SHIELD, "Shields" },
+ { TV_HELM, "Helms/Crowns" },
+ { TV_CROWN, NULL },
+ { TV_GLOVES, "Gloves" },
+ { TV_BOOTS, "Boots" },
+
+ { TV_DAEMON_BOOK, "Demonic Equipment" },
+
+ { TV_LITE, "Light Sources" },
+ { TV_AMULET, "Amulets" },
+ { TV_RING, "Rings" },
+
+ { TV_TOOL, "Tools" },
+ { TV_DIGGING, NULL },
+ { TV_TRAPKIT, "Trapping Kits" },
+
+ { 0, NULL }
+};
+
+
+
+/*
+ * Pair together a constant flag with a textual description.
+ *
+ * Used by both "init.c" and "wiz-spo.c".
+ *
+ * Note that it sometimes more efficient to actually make an array
+ * of textual names, where entry 'N' is assumed to be paired with
+ * the flag whose value is "1L << N", but that requires hard-coding.
+ */
+
+typedef struct flag_desc flag_desc;
+
+struct flag_desc
+{
+ const u32b flag;
+ const char *const desc;
+};
+
+
+
+/*
+ * These are used for "+3 to STR, DEX", etc. These are separate from
+ * the other pval affected traits to simplify the case where an object
+ * affects all stats. In this case, "All stats" is used instead of
+ * listing each stat individually.
+ */
+
+static flag_desc stat_flags_desc[] =
+{
+ { TR1_STR, "STR" },
+ { TR1_INT, "INT" },
+ { TR1_WIS, "WIS" },
+ { TR1_DEX, "DEX" },
+ { TR1_CON, "CON" },
+ { TR1_CHR, "CHR" }
+};
+
+/*
+ * Besides stats, these are the other player traits
+ * which may be affected by an object's pval
+ */
+
+static flag_desc pval_flags1_desc[] =
+{
+ { TR1_STEALTH, "Stealth" },
+ { TR1_SEARCH, "Searching" },
+ { TR1_INFRA, "Infravision" },
+ { TR1_TUNNEL, "Tunnelling" },
+ { TR1_BLOWS, "Attacks" },
+ { TR1_SPEED, "Speed" }
+};
+
+/*
+ * Slaying preferences for weapons
+ */
+
+static flag_desc slay_flags_desc[] =
+{
+ { TR1_SLAY_ANIMAL, "Animal" },
+ { TR1_SLAY_EVIL, "Evil" },
+ { TR1_SLAY_UNDEAD, "Undead" },
+ { TR1_SLAY_DEMON, "Demon" },
+ { TR1_SLAY_ORC, "Orc" },
+ { TR1_SLAY_TROLL, "Troll" },
+ { TR1_SLAY_GIANT, "Giant" },
+ { TR1_SLAY_DRAGON, "Dragon" },
+ { TR1_KILL_DRAGON, "Xdragon" }
+};
+
+/*
+ * Elemental brands for weapons
+ *
+ * Clearly, TR1_IMPACT is a bit out of place here. To simplify
+ * coding, it has been included here along with the elemental
+ * brands. It does seem to fit in with the brands and slaying
+ * more than the miscellaneous section.
+ */
+static flag_desc brand_flags_desc[] =
+{
+ { TR1_BRAND_ACID, "Acid Brand" },
+ { TR1_BRAND_ELEC, "Lightning Brand" },
+ { TR1_BRAND_FIRE, "Flame Tongue" },
+ { TR1_BRAND_COLD, "Frost Brand" },
+ { TR1_BRAND_POIS, "Poisoned" },
+
+ { TR1_CHAOTIC, "Mark of Chaos" },
+ { TR1_VAMPIRIC, "Vampiric" },
+ { TR1_IMPACT, "Earthquake impact on hit" },
+ { TR1_VORPAL, "Very sharp" },
+};
+
+
+/*
+ * The 15 resistables
+ */
+static const flag_desc resist_flags_desc[] =
+{
+ { TR2_RES_ACID, "Acid" },
+ { TR2_RES_ELEC, "Lightning" },
+ { TR2_RES_FIRE, "Fire" },
+ { TR2_RES_COLD, "Cold" },
+ { TR2_RES_POIS, "Poison" },
+ { TR2_RES_FEAR, "Fear"},
+ { TR2_RES_LITE, "Light" },
+ { TR2_RES_DARK, "Dark" },
+ { TR2_RES_BLIND, "Blindness" },
+ { TR2_RES_CONF, "Confusion" },
+ { TR2_RES_SOUND, "Sound" },
+ { TR2_RES_SHARDS, "Shards" },
+ { TR2_RES_NETHER, "Nether" },
+ { TR2_RES_NEXUS, "Nexus" },
+ { TR2_RES_CHAOS, "Chaos" },
+ { TR2_RES_DISEN, "Disenchantment" },
+};
+
+/*
+ * Elemental immunities (along with poison)
+ */
+
+static const flag_desc immune_flags_desc[] =
+{
+ { TR2_IM_ACID, "Acid" },
+ { TR2_IM_ELEC, "Lightning" },
+ { TR2_IM_FIRE, "Fire" },
+ { TR2_IM_COLD, "Cold" },
+};
+
+/*
+ * Sustain stats - these are given their "own" line in the
+ * spoiler file, mainly for simplicity
+ */
+static const flag_desc sustain_flags_desc[] =
+{
+ { TR2_SUST_STR, "STR" },
+ { TR2_SUST_INT, "INT" },
+ { TR2_SUST_WIS, "WIS" },
+ { TR2_SUST_DEX, "DEX" },
+ { TR2_SUST_CON, "CON" },
+ { TR2_SUST_CHR, "CHR" },
+};
+
+/*
+ * Miscellaneous magic given by an object's "flags2" field
+ */
+
+static const flag_desc misc_flags2_desc[] =
+{
+ { TR2_REFLECT, "Reflection" },
+ { TR2_FREE_ACT, "Free Action" },
+ { TR2_HOLD_LIFE, "Hold Life" },
+};
+
+/*
+ * Miscellaneous magic given by an object's "flags3" field
+ *
+ * Note that cursed artifacts and objects with permanent light
+ * are handled "directly" -- see analyze_misc_magic()
+ */
+
+static const flag_desc misc_flags3_desc[] =
+{
+ { TR3_SH_FIRE, "Fiery Aura" },
+ { TR3_SH_ELEC, "Electric Aura" },
+ { TR3_NO_TELE, "Prevent Teleportation" },
+ { TR3_NO_MAGIC, "Anti-Magic" },
+ { TR3_WRAITH, "Wraith Form" },
+ { TR3_FEATHER, "Levitation" },
+ { TR3_SEE_INVIS, "See Invisible" },
+ { TR3_SLOW_DIGEST, "Slow Digestion" },
+ { TR3_REGEN, "Regeneration" },
+ { TR3_XTRA_SHOTS, "+1 Extra Shot" }, /* always +1? */
+ { TR3_DRAIN_EXP, "Drains Experience" },
+ { TR3_AGGRAVATE, "Aggravates" },
+ { TR3_BLESSED, "Blessed Blade" },
+};
+
+
+/*
+ * A special type used just for dealing with pvals
+ */
+typedef struct
+{
+ /*
+ * This will contain a string such as "+2", "-10", etc.
+ */
+ char pval_desc[12];
+
+ /*
+ * A list of various player traits affected by an object's pval such
+ * as stats, speed, stealth, etc. "Extra attacks" is NOT included in
+ * this list since it will probably be desirable to format its
+ * description differently.
+ *
+ * Note that room need only be reserved for the number of stats - 1
+ * since the description "All stats" is used if an object affects all
+ * all stats. Also, room must be reserved for a sentinel NULL pointer.
+ *
+ * This will be a list such as ["STR", "DEX", "Stealth", NULL] etc.
+ *
+ * This list includes extra attacks, for simplicity.
+ */
+ cptr pval_affects[N_ELEMENTS(stat_flags_desc) - 1 +
+ N_ELEMENTS(pval_flags1_desc) + 1];
+
+}
+pval_info_type;
+
+
+/*
+ * An "object analysis structure"
+ *
+ * It will be filled with descriptive strings detailing an object's
+ * various magical powers. The "ignore X" traits are not noted since
+ * all artifacts ignore "normal" destruction.
+ */
+
+typedef struct
+{
+ /* "The Longsword Dragonsmiter (6d4) (+20, +25)" */
+ char description[160];
+
+ /* Description of what is affected by an object's pval */
+ pval_info_type pval_info;
+
+ /* A list of an object's slaying preferences */
+ cptr slays[N_ELEMENTS(slay_flags_desc) + 1];
+
+ /* A list if an object's elemental brands */
+ cptr brands[N_ELEMENTS(brand_flags_desc) + 1];
+
+ /* A list of immunities granted by an object */
+ cptr immunities[N_ELEMENTS(immune_flags_desc) + 1];
+
+ /* A list of resistances granted by an object */
+ cptr resistances[N_ELEMENTS(resist_flags_desc) + 1];
+
+ /* A list of stats sustained by an object */
+ cptr sustains[N_ELEMENTS(sustain_flags_desc) - 1 + 1];
+
+ /* A list of various magical qualities an object may have */
+ cptr misc_magic[N_ELEMENTS(misc_flags2_desc) + N_ELEMENTS(misc_flags3_desc)
+ + 1 /* Permanent Light */
+ + 1 /* type of curse */
+ + 1]; /* sentinel NULL */
+
+ /* A string describing an artifact's activation */
+ cptr activation;
+
+ /* "Level 20, Rarity 30, 3.0 lbs, 20000 Gold" */
+ char misc_desc[80];
+}
+obj_desc_list;
+
+
+
+
+
+
+/*
+ * This function does most of the actual "analysis". Given a set of bit flags
+ * (which will be from one of the flags fields from the object in question),
+ * a "flag description structure", a "description list", and the number of
+ * elements in the "flag description structure", this function sets the
+ * "description list" members to the appropriate descriptions contained in
+ * the "flag description structure".
+ *
+ * The possibly updated description pointer is returned.
+ */
+static cptr *spoiler_flag_aux(const u32b art_flags, const flag_desc *flag_ptr,
+ cptr *desc_ptr, const int n_elmnts)
+{
+ int i;
+
+ for (i = 0; i < n_elmnts; ++i)
+ {
+ if (art_flags & flag_ptr[i].flag)
+ {
+ *desc_ptr++ = flag_ptr[i].desc;
+ }
+ }
+
+ return desc_ptr;
+}
+
+
+/*
+ * Acquire a "basic" description "The Cloak of Death [1,+10]"
+ */
+static void analyze_general (object_type *o_ptr, char *desc_ptr)
+{
+ /* Get a "useful" description of the object */
+ object_desc_store(desc_ptr, o_ptr, TRUE, 1);
+}
+
+
+/*
+ * List "player traits" altered by an artifact's pval. These include stats,
+ * speed, infravision, tunnelling, stealth, searching, and extra attacks.
+ */
+static void analyze_pval (object_type *o_ptr, pval_info_type *p_ptr)
+{
+ const u32b all_stats = (TR1_STR | TR1_INT | TR1_WIS |
+ TR1_DEX | TR1_CON | TR1_CHR);
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr *affects_list;
+
+ /* If pval == 0, there is nothing to do. */
+ if (!o_ptr->pval)
+ {
+ /* An "empty" pval description indicates that pval == 0 */
+ p_ptr->pval_desc[0] = '\0';
+ return;
+ }
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ affects_list = p_ptr->pval_affects;
+
+ /* Create the "+N" string */
+ sprintf(p_ptr->pval_desc, "%s%ld", POSITIZE(o_ptr->pval), (long int) o_ptr->pval);
+
+ /* First, check to see if the pval affects all stats */
+ if ((f1 & all_stats) == all_stats)
+ {
+ *affects_list++ = "All stats";
+ }
+
+ /* Are any stats affected? */
+ else if (f1 & all_stats)
+ {
+ affects_list = spoiler_flag_aux(f1, stat_flags_desc,
+ affects_list,
+ N_ELEMENTS(stat_flags_desc));
+ }
+
+ /* And now the "rest" */
+ affects_list = spoiler_flag_aux(f1, pval_flags1_desc,
+ affects_list,
+ N_ELEMENTS(pval_flags1_desc));
+
+ /* Terminate the description list */
+ *affects_list = NULL;
+}
+
+
+/* Note the slaying specialties of a weapon */
+static void analyze_slay (object_type *o_ptr, cptr *slay_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ slay_list = spoiler_flag_aux(f1, slay_flags_desc, slay_list,
+ N_ELEMENTS(slay_flags_desc));
+
+ /* Terminate the description list */
+ *slay_list = NULL;
+}
+
+/* Note an object's elemental brands */
+static void analyze_brand (object_type *o_ptr, cptr *brand_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ brand_list = spoiler_flag_aux(f1, brand_flags_desc, brand_list,
+ N_ELEMENTS(brand_flags_desc));
+
+ /* Terminate the description list */
+ *brand_list = NULL;
+}
+
+
+/* Note the resistances granted by an object */
+static void analyze_resist (object_type *o_ptr, cptr *resist_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ resist_list = spoiler_flag_aux(f2, resist_flags_desc,
+ resist_list, N_ELEMENTS(resist_flags_desc));
+
+ /* Terminate the description list */
+ *resist_list = NULL;
+}
+
+
+/* Note the immunities granted by an object */
+static void analyze_immune (object_type *o_ptr, cptr *immune_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ immune_list = spoiler_flag_aux(f2, immune_flags_desc,
+ immune_list, N_ELEMENTS(immune_flags_desc));
+
+ /* Terminate the description list */
+ *immune_list = NULL;
+}
+
+/* Note which stats an object sustains */
+
+static void analyze_sustains (object_type *o_ptr, cptr *sustain_list)
+{
+ const u32b all_sustains = (TR2_SUST_STR | TR2_SUST_INT | TR2_SUST_WIS |
+ TR2_SUST_DEX | TR2_SUST_CON | TR2_SUST_CHR);
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Simplify things if an item sustains all stats */
+ if ((f2 & all_sustains) == all_sustains)
+ {
+ *sustain_list++ = "All stats";
+ }
+
+ /* Should we bother? */
+ else if ((f2 & all_sustains))
+ {
+ sustain_list = spoiler_flag_aux(f2, sustain_flags_desc,
+ sustain_list,
+ N_ELEMENTS(sustain_flags_desc));
+ }
+
+ /* Terminate the description list */
+ *sustain_list = NULL;
+}
+
+
+/*
+ * Note miscellaneous powers bestowed by an artifact such as see invisible,
+ * free action, permanent light, etc.
+ */
+static void analyze_misc_magic (object_type *o_ptr, cptr *misc_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ int radius = 0;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ misc_list = spoiler_flag_aux(f2, misc_flags2_desc, misc_list,
+ N_ELEMENTS(misc_flags2_desc));
+
+ misc_list = spoiler_flag_aux(f3, misc_flags3_desc, misc_list,
+ N_ELEMENTS(misc_flags3_desc));
+
+ /*
+ * Glowing artifacts -- small radius light.
+ */
+
+ if (f3 & TR3_LITE1) radius++;
+ if (f4 & TR4_LITE2) radius += 2;
+ if (f4 & TR4_LITE3) radius += 3;
+
+ if (f4 & TR4_FUEL_LITE)
+ {
+ *misc_list++ = format("It provides light (radius %d) forever.", radius);
+ }
+ else
+ {
+ *misc_list++ = format("It provides light (radius %d) when fueled.", radius);
+ }
+
+ /*
+ * Handle cursed objects here to avoid redundancies such as noting
+ * that a permanently cursed object is heavily cursed as well as
+ * being "lightly cursed".
+ */
+
+ if (cursed_p(o_ptr))
+ {
+ if (f3 & (TR3_TY_CURSE))
+ {
+ *misc_list++ = "Ancient Curse";
+ }
+ if (f3 & (TR3_PERMA_CURSE))
+ {
+ *misc_list++ = "Permanently Cursed";
+ }
+ else if (f3 & (TR3_HEAVY_CURSE))
+ {
+ *misc_list++ = "Heavily Cursed";
+ }
+ else
+ {
+ *misc_list++ = "Cursed";
+ }
+ }
+
+ /* Terminate the description list */
+ *misc_list = NULL;
+}
+
+
+
+
+/*
+ * Determine the minimum depth an artifact can appear, its rarity, its weight,
+ * and its value in gold pieces
+ */
+static void analyze_misc (object_type *o_ptr, char *misc_desc)
+{
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ sprintf(misc_desc, "Level %u, Rarity %u, %d.%d lbs, %ld Gold",
+ a_ptr->level, a_ptr->rarity,
+ a_ptr->weight / 10, a_ptr->weight % 10, (long int) a_ptr->cost);
+}
+
+
+/*
+ * Fill in an object description structure for a given object
+ */
+static void object_analyze(object_type *o_ptr, obj_desc_list *desc_ptr)
+{
+ analyze_general(o_ptr, desc_ptr->description);
+
+ analyze_pval(o_ptr, &desc_ptr->pval_info);
+
+ analyze_brand(o_ptr, desc_ptr->brands);
+
+ analyze_slay(o_ptr, desc_ptr->slays);
+
+ analyze_immune(o_ptr, desc_ptr->immunities);
+
+ analyze_resist(o_ptr, desc_ptr->resistances);
+
+ analyze_sustains(o_ptr, desc_ptr->sustains);
+
+ analyze_misc_magic(o_ptr, desc_ptr->misc_magic);
+
+ analyze_misc(o_ptr, desc_ptr->misc_desc);
+
+ desc_ptr->activation = item_activation(o_ptr, 0);
+}
+
+
+static void print_header(void)
+{
+ char buf[80];
+
+ sprintf(buf, "Artifact Spoilers for %s", get_version_string());
+ spoiler_underline(buf);
+}
+
+/*
+ * This is somewhat ugly.
+ *
+ * Given a header ("Resist", e.g.), a list ("Fire", "Cold", Acid", e.g.),
+ * and a separator character (',', e.g.), write the list to the spoiler file
+ * in a "nice" format, such as:
+ *
+ * Resist Fire, Cold, Acid
+ *
+ * That was a simple example, but when the list is long, a line wrap
+ * should occur, and this should induce a new level of indention if
+ * a list is being spread across lines. So for example, Bladeturner's
+ * list of resistances should look something like this
+ *
+ * Resist Acid, Lightning, Fire, Cold, Poison, Light, Dark, Blindness,
+ * Confusion, Sound, Shards, Nether, Nexus, Chaos, Disenchantment
+ *
+ * However, the code distinguishes between a single list of many items vs.
+ * many lists. (The separator is used to make this determination.) A single
+ * list of many items will not cause line wrapping (since there is no
+ * apparent reason to do so). So the lists of Ulmo's miscellaneous traits
+ * might look like this:
+ *
+ * Free Action; Hold Life; See Invisible; Slow Digestion; Regeneration
+ * Blessed Blade
+ *
+ * So comparing the two, "Regeneration" has no trailing separator and
+ * "Blessed Blade" was not indented. (Also, Ulmo's lists have no headers,
+ * but that's not relevant to line wrapping and indention.)
+ */
+
+/* ITEM_SEP separates items within a list */
+#define ITEM_SEP ','
+
+
+/* LIST_SEP separates lists */
+#define LIST_SEP ';'
+
+
+/* Create a spoiler file entry for an artifact */
+
+static void spoiler_print_art(obj_desc_list *art_ptr, int name1, int set, object_type *o_ptr)
+{
+ /* Don't indent the first line */
+ fprintf(fff, "%s\n ", art_ptr->description);
+ text_out_indent = 4;
+ object_out_desc(o_ptr, fff, FALSE, TRUE);
+ text_out_indent = 0;
+
+ /* End with the miscellaneous facts */
+ fprintf(fff, "%s%s\n\n", INDENT1, art_ptr->misc_desc);
+}
+
+
+/*
+ * Hack -- Create a "forged" artifact
+ */
+static bool_ make_fake_artifact(object_type *o_ptr, int name1)
+{
+ int i;
+ int cur;
+
+ artifact_type *a_ptr = &a_info[name1];
+
+
+ /* Ignore "empty" artifacts */
+ if (!a_ptr->name) return FALSE;
+
+ /* Acquire the "kind" index */
+ i = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Oops */
+ if (!i) return (FALSE);
+
+ /* Create the artifact */
+ object_prep(o_ptr, i);
+
+ /* Save the name */
+ o_ptr->name1 = name1;
+
+ /* Keep the One Ring untouched by apply_magic */
+ if (name1 != ART_POWER)
+ {
+ cur = a_ptr->cur_num;
+ apply_magic(o_ptr, -1, TRUE, TRUE, TRUE);
+ a_ptr->cur_num = cur;
+ }
+ else
+ {
+ o_ptr->pval = a_ptr->pval;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Create a spoiler file for artifacts
+ */
+static void spoil_artifact(cptr fname)
+{
+ int i, j;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ obj_desc_list artifact;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Dump the header */
+ print_header();
+
+ /* List the artifacts by tval */
+ for (i = 0; group_artifact[i].tval; i++)
+ {
+ /* Write out the group title */
+ if (group_artifact[i].name)
+ {
+ spoiler_blanklines(2);
+ spoiler_underline(group_artifact[i].name);
+ spoiler_blanklines(1);
+ }
+
+ /* Now search through all of the artifacts */
+ for (j = 1; j < max_a_idx; ++j)
+ {
+ artifact_type *a_ptr = &a_info[j];
+
+ /* We only want objects in the current group */
+ if (a_ptr->tval != group_artifact[i].tval) continue;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Attempt to "forge" the artifact */
+ if (!make_fake_artifact(q_ptr, j)) continue;
+
+ /* Aware and Known */
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+
+ /* Analyze the artifact */
+ object_analyze(q_ptr, &artifact);
+
+ /* Write out the artifact description to the spoiler file */
+ spoiler_print_art(&artifact, j, a_ptr->set, q_ptr);
+ }
+ }
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+
+
+/*
+ * Create a spoiler file for monsters -BEN-
+ */
+static void spoil_mon_desc(cptr fname)
+{
+ int i, n = 0;
+
+ s16b *who;
+
+ char buf[1024];
+
+ char nam[80];
+ char lev[80];
+ char rar[80];
+ char spd[80];
+ char ac[80];
+ char hp[80];
+ char exp[80];
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Allocate the "who" array */
+ C_MAKE(who, max_r_idx, s16b);
+
+ /* Dump the header */
+ sprintf(buf, "Monster Spoilers for %s", get_version_string());
+ spoiler_underline(buf);
+ spoiler_blanklines(2);
+
+ /* Dump the header */
+ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n",
+ "Name", "Lev", "Rar", "Spd", "Hp", "Ac", "Visual Info");
+ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n",
+ "----", "---", "---", "---", "--", "--", "-----------");
+
+
+ /* Scan the monsters */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Use that monster */
+ if (r_ptr->name) who[n++] = i;
+ }
+
+
+ /* Scan again */
+ for (i = 0; i < n; i++)
+ {
+ monster_race *r_ptr = &r_info[who[i]];
+
+ cptr name = (r_name + r_ptr->name);
+
+ /* Get the "name" */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ sprintf(nam, "[U] %s", name);
+ }
+ else
+ {
+ sprintf(nam, "The %s", name);
+ }
+
+
+ /* Level */
+ sprintf(lev, "%d", r_ptr->level);
+
+ /* Rarity */
+ sprintf(rar, "%d", r_ptr->rarity);
+
+ /* Speed */
+ if (r_ptr->speed >= 110)
+ {
+ sprintf(spd, "+%d", (r_ptr->speed - 110));
+ }
+ else
+ {
+ sprintf(spd, "-%d", (110 - r_ptr->speed));
+ }
+
+ /* Armor Class */
+ sprintf(ac, "%d", r_ptr->ac);
+
+ /* Hitpoints */
+ if ((r_ptr->flags1 & (RF1_FORCE_MAXHP)) || (r_ptr->hside == 1))
+ {
+ sprintf(hp, "%d", r_ptr->hdice * r_ptr->hside);
+ }
+ else
+ {
+ sprintf(hp, "%dd%d", r_ptr->hdice, r_ptr->hside);
+ }
+
+
+ /* Experience */
+ sprintf(exp, "%ld", (long)(r_ptr->mexp));
+
+ /* Hack -- use visual instead */
+ sprintf(exp, "%s '%c'", attr_to_text(r_ptr->d_attr), r_ptr->d_char);
+
+ /* Dump the info */
+ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n",
+ nam, lev, rar, spd, hp, ac, exp);
+ }
+
+ /* End it */
+ fprintf(fff, "\n");
+
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, s16b);
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Worked */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+
+/*
+ * Monster spoilers by: smchorse@ringer.cs.utsa.edu (Shawn McHorse)
+ *
+ * Primarily based on code already in mon-desc.c, mostly by -BEN-
+ */
+
+/*
+ * Pronoun arrays
+ */
+static cptr wd_che[3] = { "It", "He", "She" };
+static cptr wd_lhe[3] = { "it", "he", "she" };
+
+
+
+/*
+ * Create a spoiler file for monsters (-SHAWN-)
+ */
+static void spoil_mon_info(cptr fname)
+{
+ char buf[1024];
+ int msex, vn, i, j, k, n;
+ bool_ breath, magic, sin;
+ cptr p, q;
+ cptr vp[64];
+ u32b flags1, flags2, flags3, flags4, flags5, flags6, flags9;
+
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+
+ /* Dump the header */
+ sprintf(buf, "Monster Spoilers for %s", get_version_string());
+ spoiler_underline(buf);
+ spoiler_blanklines(2);
+
+ /*
+ * List all monsters in order.
+ */
+ for (n = 1; n < max_r_idx; n++)
+ {
+ monster_race *r_ptr = &r_info[n];
+
+ /* Extract the flags */
+ flags1 = r_ptr->flags1;
+ flags2 = r_ptr->flags2;
+ flags3 = r_ptr->flags3;
+ flags4 = r_ptr->flags4;
+ flags5 = r_ptr->flags5;
+ flags6 = r_ptr->flags6;
+ flags9 = r_ptr->flags9;
+ breath = FALSE;
+ magic = FALSE;
+
+ /* Extract a gender (if applicable) */
+ if (flags1 & (RF1_FEMALE)) msex = 2;
+ else if (flags1 & (RF1_MALE)) msex = 1;
+ else msex = 0;
+
+
+ /* Prefix */
+ if (flags1 & (RF1_UNIQUE))
+ {
+ spoil_out("[U] ");
+ }
+ else
+ {
+ spoil_out("The ");
+ }
+
+ /* Name */
+ sprintf(buf, "%s (", (r_name + r_ptr->name)); /* ---)--- */
+ spoil_out(buf);
+
+ /* Color */
+ spoil_out(attr_to_text(r_ptr->d_attr));
+
+ /* Symbol --(-- */
+ sprintf(buf, " '%c')\n", r_ptr->d_char);
+ spoil_out(buf);
+
+
+ /* Indent */
+ sprintf(buf, "=== ");
+ spoil_out(buf);
+
+ /* Number */
+ sprintf(buf, "Num:%d ", n);
+ spoil_out(buf);
+
+ /* Level */
+ sprintf(buf, "Lev:%d ", r_ptr->level);
+ spoil_out(buf);
+
+ /* Rarity */
+ sprintf(buf, "Rar:%d ", r_ptr->rarity);
+ spoil_out(buf);
+
+ /* Speed */
+ if (r_ptr->speed >= 110)
+ {
+ sprintf(buf, "Spd:+%d ", (r_ptr->speed - 110));
+ }
+ else
+ {
+ sprintf(buf, "Spd:-%d ", (110 - r_ptr->speed));
+ }
+ spoil_out(buf);
+
+ /* Hitpoints */
+ if ((flags1 & (RF1_FORCE_MAXHP)) || (r_ptr->hside == 1))
+ {
+ sprintf(buf, "Hp:%d ", r_ptr->hdice * r_ptr->hside);
+ }
+ else
+ {
+ sprintf(buf, "Hp:%dd%d ", r_ptr->hdice, r_ptr->hside);
+ }
+ spoil_out(buf);
+
+ /* Armor Class */
+ sprintf(buf, "Ac:%d ", r_ptr->ac);
+ spoil_out(buf);
+
+ /* Experience */
+ sprintf(buf, "Exp:%ld\n", (long)(r_ptr->mexp));
+ spoil_out(buf);
+
+
+ /* Describe */
+ spoil_out(r_text + r_ptr->text);
+ spoil_out(" ");
+
+
+ spoil_out("This");
+
+ if (flags2 & (RF2_ELDRITCH_HORROR)) spoil_out (" sanity-blasting");
+ if (flags3 & (RF3_ANIMAL)) spoil_out(" natural");
+ if (flags3 & (RF3_EVIL)) spoil_out(" evil");
+ if (flags3 & (RF3_GOOD)) spoil_out(" good");
+ if (flags3 & (RF3_UNDEAD)) spoil_out(" undead");
+
+ if (flags3 & (RF3_DRAGON)) spoil_out(" dragon");
+ else if (flags3 & (RF3_DEMON)) spoil_out(" demon");
+ else if (flags3 & (RF3_GIANT)) spoil_out(" giant");
+ else if (flags3 & (RF3_TROLL)) spoil_out(" troll");
+ else if (flags3 & (RF3_ORC)) spoil_out(" orc");
+ else if (flags3 & (RF3_THUNDERLORD)) spoil_out (" Thunderlord");
+ else spoil_out(" creature");
+
+ spoil_out(" moves");
+
+ if ((flags1 & (RF1_RAND_50)) && (flags1 & (RF1_RAND_25)))
+ {
+ spoil_out(" extremely erratically");
+ }
+ else if (flags1 & (RF1_RAND_50))
+ {
+ spoil_out(" somewhat erratically");
+ }
+ else if (flags1 & (RF1_RAND_25))
+ {
+ spoil_out(" a bit erratically");
+ }
+ else
+ {
+ spoil_out(" normally");
+ }
+
+ if (flags1 & (RF1_NEVER_MOVE))
+ {
+ spoil_out(", but does not deign to chase intruders");
+ }
+
+ spoil_out(". ");
+
+ if (!r_ptr->level || (flags1 & (RF1_FORCE_DEPTH)))
+ {
+ sprintf(buf, "%s is never found out of depth. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags1 & (RF1_FORCE_SLEEP))
+ {
+ sprintf(buf, "%s is always created sluggish. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags2 & (RF2_AURA_FIRE))
+ {
+ sprintf(buf, "%s is surrounded by flames. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags2 & (RF2_AURA_ELEC))
+ {
+ sprintf(buf, "%s is surrounded by electricity. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags2 & (RF2_REFLECTING))
+ {
+ sprintf(buf, "%s reflects bolt spells. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags1 & (RF1_ESCORT))
+ {
+ sprintf(buf, "%s usually appears with ", wd_che[msex]);
+ spoil_out(buf);
+ if (flags1 & (RF1_ESCORTS)) spoil_out("escorts. ");
+ else spoil_out("an escort. ");
+ }
+
+ if ((flags1 & (RF1_FRIEND)) || (flags1 & (RF1_FRIENDS)))
+ {
+ sprintf(buf, "%s usually appears in groups. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ /* Collect innate attacks */
+ vn = 0;
+ if (flags4 & (RF4_SHRIEK)) vp[vn++] = "shriek for help";
+ if (flags4 & (RF4_ROCKET)) vp[vn++] = "shoot a rocket";
+ if (flags4 & (RF4_ARROW_1)) vp[vn++] = "fire arrows";
+ if (flags4 & (RF4_ARROW_2)) vp[vn++] = "fire arrows";
+ if (flags4 & (RF4_ARROW_3)) vp[vn++] = "fire missiles";
+ if (flags4 & (RF4_ARROW_4)) vp[vn++] = "fire missiles";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" may ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" or ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ /* Collect breaths */
+ vn = 0;
+ if (flags4 & (RF4_BR_ACID)) vp[vn++] = "acid";
+ if (flags4 & (RF4_BR_ELEC)) vp[vn++] = "lightning";
+ if (flags4 & (RF4_BR_FIRE)) vp[vn++] = "fire";
+ if (flags4 & (RF4_BR_COLD)) vp[vn++] = "frost";
+ if (flags4 & (RF4_BR_POIS)) vp[vn++] = "poison";
+ if (flags4 & (RF4_BR_NETH)) vp[vn++] = "nether";
+ if (flags4 & (RF4_BR_LITE)) vp[vn++] = "light";
+ if (flags4 & (RF4_BR_DARK)) vp[vn++] = "darkness";
+ if (flags4 & (RF4_BR_CONF)) vp[vn++] = "confusion";
+ if (flags4 & (RF4_BR_SOUN)) vp[vn++] = "sound";
+ if (flags4 & (RF4_BR_CHAO)) vp[vn++] = "chaos";
+ if (flags4 & (RF4_BR_DISE)) vp[vn++] = "disenchantment";
+ if (flags4 & (RF4_BR_NEXU)) vp[vn++] = "nexus";
+ if (flags4 & (RF4_BR_TIME)) vp[vn++] = "time";
+ if (flags4 & (RF4_BR_INER)) vp[vn++] = "inertia";
+ if (flags4 & (RF4_BR_GRAV)) vp[vn++] = "gravity";
+ if (flags4 & (RF4_BR_SHAR)) vp[vn++] = "shards";
+ if (flags4 & (RF4_BR_PLAS)) vp[vn++] = "plasma";
+ if (flags4 & (RF4_BR_WALL)) vp[vn++] = "force";
+ if (flags4 & (RF4_BR_MANA)) vp[vn++] = "mana";
+ if (flags4 & (RF4_BR_NUKE)) vp[vn++] = "toxic waste";
+ if (flags4 & (RF4_BR_DISI)) vp[vn++] = "disintegration";
+
+ if (vn)
+ {
+ breath = TRUE;
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" may breathe ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" or ");
+ spoil_out(vp[i]);
+ }
+ if (flags2 & (RF2_POWERFUL)) spoil_out(" powerfully");
+ }
+
+ /* Collect spells */
+ vn = 0;
+ if (flags5 & (RF5_BA_ACID)) vp[vn++] = "produce acid balls";
+ if (flags5 & (RF5_BA_ELEC)) vp[vn++] = "produce lightning balls";
+ if (flags5 & (RF5_BA_FIRE)) vp[vn++] = "produce fire balls";
+ if (flags5 & (RF5_BA_COLD)) vp[vn++] = "produce frost balls";
+ if (flags5 & (RF5_BA_POIS)) vp[vn++] = "produce poison balls";
+ if (flags5 & (RF5_BA_NETH)) vp[vn++] = "produce nether balls";
+ if (flags5 & (RF5_BA_WATE)) vp[vn++] = "produce water balls";
+ if (flags4 & (RF4_BA_NUKE)) vp[vn++] = "produce balls of radiation";
+ if (flags5 & (RF5_BA_MANA)) vp[vn++] = "produce mana storms";
+ if (flags5 & (RF5_BA_DARK)) vp[vn++] = "produce darkness storms";
+ if (flags4 & (RF4_BA_CHAO)) vp[vn++] = "invoke raw Chaos";
+ if (flags6 & (RF6_HAND_DOOM)) vp[vn++] = "invoke the Hand of Doom";
+ if (flags5 & (RF5_DRAIN_MANA)) vp[vn++] = "drain mana";
+ if (flags5 & (RF5_MIND_BLAST)) vp[vn++] = "cause mind blasting";
+ if (flags5 & (RF5_BRAIN_SMASH)) vp[vn++] = "cause brain smashing";
+ if (flags5 & (RF5_CAUSE_1)) vp[vn++] = "cause light wounds and cursing";
+ if (flags5 & (RF5_CAUSE_2)) vp[vn++] = "cause serious wounds and cursing";
+ if (flags5 & (RF5_CAUSE_3)) vp[vn++] = "cause critical wounds and cursing";
+ if (flags5 & (RF5_CAUSE_4)) vp[vn++] = "cause mortal wounds";
+ if (flags5 & (RF5_BO_ACID)) vp[vn++] = "produce acid bolts";
+ if (flags5 & (RF5_BO_ELEC)) vp[vn++] = "produce lightning bolts";
+ if (flags5 & (RF5_BO_FIRE)) vp[vn++] = "produce fire bolts";
+ if (flags5 & (RF5_BO_COLD)) vp[vn++] = "produce frost bolts";
+ if (flags5 & (RF5_BO_POIS)) vp[vn++] = "produce poison bolts";
+ if (flags5 & (RF5_BO_NETH)) vp[vn++] = "produce nether bolts";
+ if (flags5 & (RF5_BO_WATE)) vp[vn++] = "produce water bolts";
+ if (flags5 & (RF5_BO_MANA)) vp[vn++] = "produce mana bolts";
+ if (flags5 & (RF5_BO_PLAS)) vp[vn++] = "produce plasma bolts";
+ if (flags5 & (RF5_BO_ICEE)) vp[vn++] = "produce ice bolts";
+ if (flags5 & (RF5_MISSILE)) vp[vn++] = "produce magic missiles";
+ if (flags5 & (RF5_SCARE)) vp[vn++] = "terrify";
+ if (flags5 & (RF5_BLIND)) vp[vn++] = "blind";
+ if (flags5 & (RF5_CONF)) vp[vn++] = "confuse";
+ if (flags5 & (RF5_SLOW)) vp[vn++] = "slow";
+ if (flags5 & (RF5_HOLD)) vp[vn++] = "paralyse";
+ if (flags6 & (RF6_HASTE)) vp[vn++] = "haste-self";
+ if (flags6 & (RF6_HEAL)) vp[vn++] = "heal-self";
+ if (flags6 & (RF6_BLINK)) vp[vn++] = "blink-self";
+ if (flags6 & (RF6_TPORT)) vp[vn++] = "teleport-self";
+ if (flags6 & (RF6_S_BUG)) vp[vn++] = "summon software bugs";
+ if (flags6 & (RF6_S_RNG)) vp[vn++] = "summon RNGs";
+ if (flags6 & (RF6_TELE_TO)) vp[vn++] = "teleport to";
+ if (flags6 & (RF6_TELE_AWAY)) vp[vn++] = "teleport away";
+ if (flags6 & (RF6_TELE_LEVEL)) vp[vn++] = "teleport level";
+ if (flags6 & (RF6_DARKNESS)) vp[vn++] = "create darkness";
+ if (flags6 & (RF6_TRAPS)) vp[vn++] = "create traps";
+ if (flags6 & (RF6_FORGET)) vp[vn++] = "cause amnesia";
+ if (flags6 & (RF6_RAISE_DEAD)) vp[vn++] = "raise dead";
+ if (flags6 & (RF6_S_THUNDERLORD)) vp[vn++] = "summon a thunderlord";
+ if (flags6 & (RF6_S_MONSTER)) vp[vn++] = "summon a monster";
+ if (flags6 & (RF6_S_MONSTERS)) vp[vn++] = "summon monsters";
+ if (flags6 & (RF6_S_KIN)) vp[vn++] = "summon aid";
+ if (flags6 & (RF6_S_ANT)) vp[vn++] = "summon ants";
+ if (flags6 & (RF6_S_SPIDER)) vp[vn++] = "summon spiders";
+ if (flags6 & (RF6_S_HOUND)) vp[vn++] = "summon hounds";
+ if (flags6 & (RF6_S_HYDRA)) vp[vn++] = "summon hydras";
+ if (flags6 & (RF6_S_ANGEL)) vp[vn++] = "summon an angel";
+ if (flags6 & (RF6_S_DEMON)) vp[vn++] = "summon a demon";
+ if (flags6 & (RF6_S_UNDEAD)) vp[vn++] = "summon an undead";
+ if (flags6 & (RF6_S_DRAGON)) vp[vn++] = "summon a dragon";
+ if (flags4 & (RF4_S_ANIMAL)) vp[vn++] = "summon animal";
+ if (flags6 & (RF6_S_ANIMALS)) vp[vn++] = "summon animals";
+ if (flags6 & (RF6_S_HI_UNDEAD)) vp[vn++] = "summon greater undead";
+ if (flags6 & (RF6_S_HI_DRAGON)) vp[vn++] = "summon ancient dragons";
+ if (flags6 & (RF6_S_HI_DEMON)) vp[vn++] = "summon greater demons";
+ if (flags6 & (RF6_S_WRAITH)) vp[vn++] = "summon Ringwraith";
+ if (flags6 & (RF6_S_UNIQUE)) vp[vn++] = "summon unique monsters";
+
+ if (vn)
+ {
+ magic = TRUE;
+ if (breath)
+ {
+ spoil_out(", and is also");
+ }
+ else
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is");
+ }
+
+ spoil_out(" magical, casting spells");
+ if (flags2 & (RF2_SMART)) spoil_out(" intelligently");
+
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" which ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" or ");
+ spoil_out(vp[i]);
+ }
+ }
+
+ if (breath || magic)
+ {
+ int times = r_ptr->freq_inate + r_ptr->freq_spell;
+ sprintf(buf, "; 1 time in %d. ",
+ 200 / ((times) ? times : 1));
+ spoil_out(buf);
+ }
+
+ /* Collect special abilities. */
+ vn = 0;
+ if (flags2 & (RF2_OPEN_DOOR)) vp[vn++] = "open doors";
+ if (flags2 & (RF2_BASH_DOOR)) vp[vn++] = "bash down doors";
+ if (flags2 & (RF2_PASS_WALL)) vp[vn++] = "pass through walls";
+ if (flags2 & (RF2_KILL_WALL)) vp[vn++] = "bore through walls";
+ if (flags2 & (RF2_MOVE_BODY)) vp[vn++] = "push past weaker monsters";
+ if (flags2 & (RF2_KILL_BODY)) vp[vn++] = "destroy weaker monsters";
+ if (flags2 & (RF2_TAKE_ITEM)) vp[vn++] = "pick up objects";
+ if (flags2 & (RF2_KILL_ITEM)) vp[vn++] = "destroy objects";
+ if (flags9 & (RF9_HAS_LITE)) vp[vn++] = "illuminate the dungeon";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" can ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" and ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ if (flags2 & (RF2_INVISIBLE))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is invisible. ");
+ }
+ if (flags2 & (RF2_COLD_BLOOD))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is cold blooded. ");
+ }
+ if (flags2 & (RF2_EMPTY_MIND))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is not detected by telepathy. ");
+ }
+ if (flags2 & (RF2_WEIRD_MIND))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is rarely detected by telepathy. ");
+ }
+ if (flags4 & (RF4_MULTIPLY))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" breeds explosively. ");
+ }
+ if (flags2 & (RF2_REGENERATE))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" regenerates quickly. ");
+ }
+
+ /* Collect susceptibilities */
+ vn = 0;
+ if (flags3 & (RF3_HURT_ROCK)) vp[vn++] = "rock remover";
+ if (flags3 & (RF3_HURT_LITE)) vp[vn++] = "bright light";
+ if (flags3 & (RF3_SUSCEP_FIRE)) vp[vn++] = "fire";
+ if (flags3 & (RF3_SUSCEP_COLD)) vp[vn++] = "cold";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" is hurt by ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" and ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ /* Collect immunities */
+ vn = 0;
+ if (flags3 & (RF3_IM_ACID)) vp[vn++] = "acid";
+ if (flags3 & (RF3_IM_ELEC)) vp[vn++] = "lightning";
+ if (flags3 & (RF3_IM_FIRE)) vp[vn++] = "fire";
+ if (flags3 & (RF3_IM_COLD)) vp[vn++] = "cold";
+ if (flags3 & (RF3_IM_POIS)) vp[vn++] = "poison";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" resists ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" and ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ /* Collect resistances */
+ vn = 0;
+ if (flags3 & (RF3_RES_NETH)) vp[vn++] = "nether";
+ if (flags3 & (RF3_RES_WATE)) vp[vn++] = "water";
+ if (flags3 & (RF3_RES_PLAS)) vp[vn++] = "plasma";
+ if (flags3 & (RF3_RES_NEXU)) vp[vn++] = "nexus";
+ if (flags3 & (RF3_RES_DISE)) vp[vn++] = "disenchantment";
+ if (flags3 & (RF3_RES_TELE)) vp[vn++] = "teleportation";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" resists ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" and ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ /* Collect non-effects */
+ vn = 0;
+ if (flags3 & (RF3_NO_STUN)) vp[vn++] = "stunned";
+ if (flags3 & (RF3_NO_FEAR)) vp[vn++] = "frightened";
+ if (flags3 & (RF3_NO_CONF)) vp[vn++] = "confused";
+ if (flags3 & (RF3_NO_SLEEP)) vp[vn++] = "slept";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" cannot be ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" or ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ spoil_out(wd_che[msex]);
+ if (r_ptr->sleep > 200) spoil_out(" prefers to ignore");
+ else if (r_ptr->sleep > 95) spoil_out(" pays very little attention to");
+ else if (r_ptr->sleep > 75) spoil_out(" pays little attention to");
+ else if (r_ptr->sleep > 45) spoil_out(" tends to overlook");
+ else if (r_ptr->sleep > 25) spoil_out(" takes quite a while to see");
+ else if (r_ptr->sleep > 10) spoil_out(" takes a while to see");
+ else if (r_ptr->sleep > 5) spoil_out(" is fairly observant of");
+ else if (r_ptr->sleep > 3) spoil_out(" is observant of");
+ else if (r_ptr->sleep > 1) spoil_out(" is very observant of");
+ else if (r_ptr->sleep > 0) spoil_out(" is vigilant for");
+ else spoil_out(" is ever vigilant for");
+
+ sprintf(buf, " intruders, which %s may notice from %d feet. ",
+ wd_lhe[msex], 10 * r_ptr->aaf);
+ spoil_out(buf);
+
+ i = 0;
+ if (flags1 & (RF1_DROP_60)) i += 1;
+ if (flags1 & (RF1_DROP_90)) i += 2;
+ if (flags1 & (RF1_DROP_1D2)) i += 2;
+ if (flags1 & (RF1_DROP_2D2)) i += 4;
+ if (flags1 & (RF1_DROP_3D2)) i += 6;
+ if (flags1 & (RF1_DROP_4D2)) i += 8;
+
+ /* Drops gold and/or items */
+ if (i)
+ {
+ sin = FALSE;
+ spoil_out(wd_che[msex]);
+ spoil_out(" will carry");
+
+ if (i == 1)
+ {
+ spoil_out(" a");
+ sin = TRUE;
+ }
+ else if (i == 2)
+ {
+ spoil_out(" one or two");
+ }
+ else
+ {
+ sprintf(buf, " up to %u", i);
+ spoil_out(buf);
+ }
+
+ if (flags1 & (RF1_DROP_GREAT))
+ {
+ if (sin) spoil_out("n");
+ spoil_out(" exceptional object");
+ }
+ else if (flags1 & (RF1_DROP_GOOD))
+ {
+ spoil_out(" good object");
+ }
+ else if (flags1 & (RF1_DROP_USEFUL))
+ {
+ spoil_out(" useful object");
+ }
+ else if (flags1 & (RF1_ONLY_ITEM))
+ {
+ spoil_out(" object");
+ }
+ else if (flags1 & (RF1_ONLY_GOLD))
+ {
+ spoil_out(" treasure");
+ }
+ else
+ {
+ if (sin) spoil_out("n");
+ spoil_out(" object");
+ if (i > 1) spoil_out("s");
+ spoil_out(" or treasure");
+ }
+ if (i > 1) spoil_out("s");
+
+ if (flags1 & (RF1_DROP_CHOSEN))
+ {
+ spoil_out(", in addition to chosen objects");
+ }
+
+ spoil_out(". ");
+ }
+
+ /* Count the actual attacks */
+ for (i = 0, j = 0; j < 4; j++)
+ {
+ if (r_ptr->blow[j].method) i++;
+ }
+
+ /* Examine the actual attacks */
+ for (k = 0, j = 0; j < 4; j++)
+ {
+ if (!r_ptr->blow[j].method) continue;
+
+ /* No method yet */
+ p = "???";
+
+ /* Acquire the method */
+ switch (r_ptr->blow[j].method)
+ {
+ case RBM_HIT:
+ p = "hit";
+ break;
+ case RBM_TOUCH:
+ p = "touch";
+ break;
+ case RBM_PUNCH:
+ p = "punch";
+ break;
+ case RBM_KICK:
+ p = "kick";
+ break;
+ case RBM_CLAW:
+ p = "claw";
+ break;
+ case RBM_BITE:
+ p = "bite";
+ break;
+ case RBM_STING:
+ p = "sting";
+ break;
+ case RBM_XXX1:
+ break;
+ case RBM_BUTT:
+ p = "butt";
+ break;
+ case RBM_CRUSH:
+ p = "crush";
+ break;
+ case RBM_ENGULF:
+ p = "engulf";
+ break;
+ case RBM_CHARGE:
+ p = "charge";
+ break;
+ case RBM_CRAWL:
+ p = "crawl on you";
+ break;
+ case RBM_DROOL:
+ p = "drool on you";
+ break;
+ case RBM_SPIT:
+ p = "spit";
+ break;
+ case RBM_EXPLODE:
+ p = "explode";
+ break;
+ case RBM_GAZE:
+ p = "gaze";
+ break;
+ case RBM_WAIL:
+ p = "wail";
+ break;
+ case RBM_SPORE:
+ p = "release spores";
+ break;
+ case RBM_XXX4:
+ break;
+ case RBM_BEG:
+ p = "beg";
+ break;
+ case RBM_INSULT:
+ p = "insult";
+ break;
+ case RBM_MOAN:
+ p = "moan";
+ break;
+ case RBM_SHOW:
+ p = "sing";
+ break;
+ }
+
+
+ /* Default effect */
+ q = "???";
+
+ /* Acquire the effect */
+ switch (r_ptr->blow[j].effect)
+ {
+ case RBE_HURT:
+ q = "attack";
+ break;
+ case RBE_POISON:
+ q = "poison";
+ break;
+ case RBE_UN_BONUS:
+ q = "disenchant";
+ break;
+ case RBE_UN_POWER:
+ q = "drain charges";
+ break;
+ case RBE_EAT_GOLD:
+ q = "steal gold";
+ break;
+ case RBE_EAT_ITEM:
+ q = "steal items";
+ break;
+ case RBE_EAT_FOOD:
+ q = "eat your food";
+ break;
+ case RBE_EAT_LITE:
+ q = "absorb light";
+ break;
+ case RBE_ACID:
+ q = "shoot acid";
+ break;
+ case RBE_ELEC:
+ q = "electrocute";
+ break;
+ case RBE_FIRE:
+ q = "burn";
+ break;
+ case RBE_COLD:
+ q = "freeze";
+ break;
+ case RBE_BLIND:
+ q = "blind";
+ break;
+ case RBE_CONFUSE:
+ q = "confuse";
+ break;
+ case RBE_TERRIFY:
+ q = "terrify";
+ break;
+ case RBE_PARALYZE:
+ q = "paralyze";
+ break;
+ case RBE_LOSE_STR:
+ q = "reduce strength";
+ break;
+ case RBE_LOSE_INT:
+ q = "reduce intelligence";
+ break;
+ case RBE_LOSE_WIS:
+ q = "reduce wisdom";
+ break;
+ case RBE_LOSE_DEX:
+ q = "reduce dexterity";
+ break;
+ case RBE_LOSE_CON:
+ q = "reduce constitution";
+ break;
+ case RBE_LOSE_CHR:
+ q = "reduce charisma";
+ break;
+ case RBE_LOSE_ALL:
+ q = "reduce all stats";
+ break;
+ case RBE_SHATTER:
+ q = "shatter";
+ break;
+ case RBE_EXP_10:
+ q = "lower experience (by 10d6+)";
+ break;
+ case RBE_EXP_20:
+ q = "lower experience (by 20d6+)";
+ break;
+ case RBE_EXP_40:
+ q = "lower experience (by 40d6+)";
+ break;
+ case RBE_EXP_80:
+ q = "lower experience (by 80d6+)";
+ break;
+ case RBE_DISEASE:
+ q = "disease";
+ break;
+ case RBE_TIME:
+ q = "time";
+ break;
+ case RBE_SANITY:
+ q = "make insane";
+ break;
+ case RBE_HALLU:
+ q = "cause hallucinations";
+ break;
+ case RBE_PARASITE:
+ q = "parasite";
+ break;
+ }
+
+
+ if (!k)
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" can ");
+ }
+ else if (k < i - 1)
+ {
+ spoil_out(", ");
+ }
+ else
+ {
+ spoil_out(", and ");
+ }
+
+ /* Describe the method */
+ spoil_out(p);
+
+ /* Describe the effect, if any */
+ if (r_ptr->blow[j].effect)
+ {
+ spoil_out(" to ");
+ spoil_out(q);
+ if (r_ptr->blow[j].d_dice && r_ptr->blow[j].d_side)
+ {
+ spoil_out(" with damage");
+ if (r_ptr->blow[j].d_side == 1)
+ sprintf(buf, " %d", r_ptr->blow[j].d_dice);
+ else
+ sprintf(buf, " %dd%d",
+ r_ptr->blow[j].d_dice, r_ptr->blow[j].d_side);
+ spoil_out(buf);
+ }
+ }
+
+ k++;
+ }
+
+ if (k)
+ {
+ spoil_out(". ");
+ }
+ else if (flags1 & (RF1_NEVER_BLOW))
+ {
+ sprintf(buf, "%s has no physical attacks. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ spoil_out(NULL);
+ }
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+char *long_intro =
+ "Essences are the tools of the trade for Alchemists, "
+ "and unfortunately are useless for any other class. "
+ "Alchemists use essences to create magical items for them to use.\n\n"
+ "They can be either found on the floor while exploring the dungeon, "
+ "or extracted from other magical items the alchemist finds "
+ "during their adventures.\n\n"
+ "To create an artifact, the alchemist will have to sacrifice 10 hit points, "
+ "and an amount of magic essence similar to his skill in alchemy. "
+ "The alchemist then allows the artifact to gain experience, "
+ "and when it has enough, "
+ "uses that experience to add abilities to the artifact. "
+ "The alchemist can allow the artifact to continue to gain experience, "
+ "thus keeping open the option to add more abilities later. "
+ "This requires a similar amount of magic essence, "
+ "but does not require the sacrifice of other hit points.\n\n"
+ "Note that the experience you gain is divided among the artifacts "
+ "that you have as well as going to yourself, "
+ "so you will gain levels more slowly when empowering artifacts. "
+ "Also, the artifact only gets 60% of the experience. "
+ "So killing a creature worth 20xp would gain 10 for you, "
+ "and 6 for the artifact.\n\n"
+ "You can also modify existing artifacts when you attain skill level 50. "
+ "Also at skill level 50 you will gain the ability to make temporary artifacts, "
+ "which don't require the complex empowerments that regular items require, "
+ "but also vanish after awhile.\n\n"
+ "You cannot give an artifact an ability "
+ "unless you have *Identified* an artifact which has that ability.\n\n"
+ "For every four levels gained in the alchemy skill, "
+ "the alchemist learns about objects of level (skill level)/4, "
+ "starting by learning about level 1 objects at skill level 0. "
+ "(actually 1, but who's counting?)\n\n"
+ "At skill level 5 you gain the ability to make ego items - but watch it! "
+ "Your base failure rate will be 90%, "
+ "and won't be 0% until you reach skill level 50. "
+ "Adding gold will increase the chances of success "
+ "in direct proportion to the value of the item you are trying to create. "
+ "Note that this results in automatic success "
+ "when the item you are trying to create "
+ "happens to pick up a curse in the process.\n\n"
+ "At skill level 5 you also gain knowledge of some basic ego item recipes. "
+ "These are: Acidic, Shocking, Fiery, Frozen, Venomous, and Chaotic weapons, "
+ "Resist Fire armour, and light sources of Fearlessness.\n\n"
+ "At skill level 10 you will gain knowledge of digging ego items, "
+ "if you have selected the option "
+ "'always generate very unusual rooms' (ironman_rooms).\n\n"
+ "At skill level 15 you can create ego wands, staves, rings, etc.\n\n"
+ "At skill level 25 you gain the ability to empower artifacts "
+ "and double ego items.\n\n"
+ "At skill level 50 you gain the ability to create temporary artifacts, "
+ "which don't require any exotic ingredients "
+ "beyond a single corpse of any type.\n\n"
+ "Between skill levels 25 and 50, "
+ "you will steadily gain the ability to set more and more flags.\n\n"
+ "To finalise an artifact, you 'P'ower it, and select the powers you want.\n"
+ "Powers are divided into the following six categories:\n"
+ "*****essences.txt*03[Stats, Sustains, Luck, Speed, Vision, etc.]\n"
+ "*****essences.txt*04[Misc. (Auras, Light, See Invisibility, etc.)]\n"
+ "*****essences.txt*05[Weapon Brands]\n"
+ "*****essences.txt*06[Resistances and Immunities]\n"
+ "*****essences.txt*07[ESP and Curses]\n"
+ "*****essences.txt*08[Artifact Activations]\n";
+
+/*
+ * Create a spoiler file for essences
+ */
+static void spoil_bateries(cptr fname)
+{
+ int j, i, tval, sval, group;
+ object_type forge, *o_ptr;
+
+ char buf[1024];
+
+ tval = sval = group = -1;
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Dump the header */
+
+ fprintf(fff,
+ "|||||oy\n"
+ "~~~~~01|Spoilers|Essences\n"
+ "~~~~~02|Alchemist|Essence Spoiler\n"
+ "#####REssence Spoiler for %s\n"
+ "#####R-----------------------------------\n\n",
+ get_version_string());
+
+
+ /*New code starts here -*/
+ /*print header, including artifact header*/
+ /*Cycle through artifact flags*/
+ /* print desc*/
+ /* cycle through all alchemist_recipies*/
+ /* print matching*/
+ /*Print items header.*/
+ /*Cycle through alchemist_recipies*/
+ /* sval or tval changed?*/
+ /* skip artifacts (tval=0)*/
+ /* print item desc (ego (tval=1) or item)*/
+ /* print essences required*/
+ /*Done!*/
+
+ /*Print basic header.*/
+ spoil_out(long_intro);
+
+ /*Cycle through artifact flags*/
+ for ( i = 0 ; a_select_flags[i].group ; i++ )
+ {
+ if ( a_select_flags[i].group != group )
+ {
+ group = a_select_flags[i].group;
+ spoil_out("\n~~~~~");
+ switch (group)
+ {
+ case 1:
+ spoil_out("03\n#####GStats, Sustains, Luck, Speed, Vision, etc.\n");
+ break;
+ case 2:
+ spoil_out("04\n#####GMisc. (Auras, Light, See Invisibility, etc.)\n");
+ break;
+ case 3:
+ spoil_out("05\n#####GWeapon Brands\n");
+ break;
+ case 4:
+ spoil_out("06\n#####GResistances and Immunities\n");
+ break;
+ case 5:
+ spoil_out("07\n#####GESP and Curses\n");
+ break;
+ case 88:
+ spoil_out("08\n#####GArtifact Activations\n");
+ break;
+ default:
+ spoil_out(format("09\n#####GExtra Group=%d\n", group));
+ }
+ spoil_out("lvl xp Power\n");
+
+ }
+ /* print desc*/
+ spoil_out(format("%-2d %8d %-24s %s\n",
+ a_select_flags[i].level,
+ a_select_flags[i].xp,
+ al_name + a_select_flags[i].desc,
+ al_name + a_select_flags[i].item_desc));
+ /* cycle through all alchemist_recipies*/
+ for ( j = 0 ; j < max_al_idx ; j++ )
+ {
+ /* print matching*/
+ if (alchemist_recipes[j].tval == 0
+ && alchemist_recipes[j].sval == a_select_flags[i].flag
+ && alchemist_recipes[j].qty
+ )
+ {
+ spoil_out(format(" %d essences of %s\n", alchemist_recipes[j].qty,
+ k_name + k_info[lookup_kind(TV_BATERIE, alchemist_recipes[j].sval_essence)].name));
+ }
+ }
+ }
+
+ spoil_out("\n\nThe following basic item recipes also exist:\n");
+ /*Cycle through alchemist_recipies*/
+ for ( i = 0 ; i < max_al_idx ; i ++)
+ {
+ alchemist_recipe *ar_ptr = &alchemist_recipes[i];
+
+ /* sval or tval changed?*/
+ if (tval != ar_ptr->tval || sval != ar_ptr->sval)
+ {
+ char o_name[80];
+ /* skip artifacts (tval=0)*/
+ if (ar_ptr->tval == 0 )
+ continue;
+
+ tval = ar_ptr->tval;
+ sval = ar_ptr->sval;
+
+ /* print item desc (ego (tval=1)or item)*/
+ if (ar_ptr->tval == 1)
+ {
+ strcpy(o_name, e_name + e_info[ar_ptr->sval].name);
+ }
+ else
+ {
+ /* Find the name of that object */
+ o_ptr = &forge;
+ object_wipe(o_ptr);
+ object_prep(o_ptr, lookup_kind(tval, sval));
+ apply_magic(o_ptr, 1, FALSE, FALSE, FALSE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->name2 = o_ptr->name2b = 0;
+ /* the 0 mode means only the text, leaving off any numbers */
+ object_desc(o_name, o_ptr, FALSE, 0);
+ }
+ spoil_out("\n");
+ spoil_out(o_name);
+ }
+ /* print essence required*/
+ spoil_out(format(" %d %s", ar_ptr->qty,
+ k_name + k_info[lookup_kind(TV_BATERIE, ar_ptr->sval_essence)].name));
+ }
+
+ spoil_out(NULL);
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+ return;
+}
+
+
+/*
+ * Print a bookless spell list
+ */
+void print_magic_powers( magic_power *powers, int max_powers, void(*power_info)(char *p, int power), int skill_num )
+{
+ int i, save_skill;
+
+ char buf[80];
+
+ magic_power spell;
+
+ /* Use a maximal skill */
+ save_skill = s_info[skill_num].value;
+ s_info[skill_num].value = SKILL_MAX;
+
+ /* Dump the header line */
+ spoiler_blanklines(2);
+ sprintf(buf, "%s", s_info[skill_num].name + s_name);
+ spoiler_underline(buf);
+ spoiler_blanklines(1);
+
+ fprintf(fff, " Name Lvl Mana Fail Info\n");
+
+ /* Dump the spells */
+ for (i = 0; i < max_powers; i++)
+ {
+ /* Access the spell */
+ spell = powers[i];
+
+ /* Get the additional info */
+ power_info(buf, i);
+
+ /* Dump the spell */
+ spoil_out(format("%c) %-30s%2d %4d %3d%%%s\n",
+ I2A(i), spell.name,
+ spell.min_lev, spell.mana_cost, spell.fail, buf));
+ spoil_out(format("%s\n", spell.desc));
+ }
+
+ /* Restore skill */
+ s_info[skill_num].value = save_skill;
+}
+
+
+/*
+ * Create a spoiler file for spells
+ */
+
+static void spoil_spells(cptr fname)
+{
+ char buf[1024];
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Dump the header */
+ sprintf(buf, "Spell Spoiler (Skill Level 50) for %s", get_version_string());
+ spoiler_underline(buf);
+
+ /* Dump the bookless magic powers in alphabetical order */
+
+ /* Mimicry */
+ print_magic_powers(mimic_powers, MAX_MIMIC_POWERS, mimic_info, SKILL_MIMICRY);
+
+ /* Mindcraft */
+ print_magic_powers(mindcraft_powers, MAX_MINDCRAFT_POWERS, mindcraft_info, SKILL_MINDCRAFT);
+
+ /* Necromancy */
+ print_magic_powers(necro_powers, MAX_NECRO_POWERS, necro_info, SKILL_NECROMANCY);
+
+ /* Symbiosis */
+ print_magic_powers(symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info, SKILL_SYMBIOTIC);
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+/*
+ * Forward declare
+ */
+extern void do_cmd_spoilers(void);
+
+/*
+ * Create Spoiler files -BEN-
+ */
+void do_cmd_spoilers(void)
+{
+ int i;
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Interact */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Info */
+ prt("Create a spoiler file.", 2, 0);
+
+ /* Prompt for a file */
+ prt("(1) Brief Object Info (obj-desc.spo)", 5, 5);
+ prt("(2) Full Artifact Info (artifact.spo)", 6, 5);
+ prt("(3) Brief Monster Info (mon-desc.spo)", 7, 5);
+ prt("(4) Full Monster Info (mon-info.spo)", 8, 5);
+ prt("(5) Full Essences Info (ess-info.spo)", 9, 5);
+ prt("(6) Spell Info (spell.spo)", 10, 5);
+
+ /* Prompt */
+ prt("Command: ", 12, 0);
+
+ /* Get a choice */
+ i = inkey();
+
+ /* Escape */
+ if (i == ESCAPE)
+ {
+ break;
+ }
+
+ /* Option (1) */
+ else if (i == '1')
+ {
+ spoil_obj_desc("obj-desc.spo");
+ }
+
+ /* Option (2) */
+ else if (i == '2')
+ {
+ spoil_artifact("artifact.spo");
+ }
+
+ /* Option (3) */
+ else if (i == '3')
+ {
+ spoil_mon_desc("mon-desc.spo");
+ }
+
+ /* Option (4) */
+ else if (i == '4')
+ {
+ spoil_mon_info("mon-info.spo");
+ }
+
+ /* Option (5) */
+ else if (i == '5')
+ {
+ spoil_bateries("ess-info.spo");
+ }
+
+ /* Option (6) */
+ else if (i == '6')
+ {
+ spoil_spells("spell.spo");
+ }
+
+ /* Oops */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
diff --git a/src/wizard2.c b/src/wizard2.c
new file mode 100644
index 00000000..a19b72e0
--- /dev/null
+++ b/src/wizard2.c
@@ -0,0 +1,1950 @@
+/* File: wizard2.c */
+
+/* Purpose: Wizard commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+void do_cmd_wizard_body(s16b);
+extern void status_main(void);
+
+/*
+ * Adds a lvl to a monster
+ */
+void wiz_inc_monster_level(int level)
+{
+ monster_type *m_ptr;
+ int ii, jj;
+
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + level);
+ monster_check_experience(cave[jj][ii].m_idx, FALSE);
+ }
+}
+
+void wiz_align_monster(int status)
+{
+ monster_type *m_ptr;
+ int ii, jj;
+
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ m_ptr->status = status;
+ }
+}
+
+/*
+ * Teleport directly to a town
+ */
+void teleport_player_town(int town)
+{
+ int x = 0, y = 0;
+
+ autosave_checkpoint();
+
+ /* Change town */
+ dun_level = 0;
+ p_ptr->town_num = town;
+
+ for (x = 0; x < max_wild_x; x++)
+ for (y = 0; y < max_wild_y; y++)
+ if (p_ptr->town_num == wf_info[wild_map[y][x].feat].entrance) goto finteletown;
+finteletown:
+ p_ptr->wilderness_y = y;
+ p_ptr->wilderness_x = x;
+
+ p_ptr->inside_arena = 0;
+ leaving_quest = p_ptr->inside_quest;
+ p_ptr->inside_quest = 0;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Hack -- Rerate Hitpoints
+ */
+void do_cmd_rerate(void)
+{
+ int min_value, max_value, i, percent;
+
+ min_value = (PY_MAX_LEVEL * 3 * (p_ptr->hitdie - 1)) / 8;
+ min_value += PY_MAX_LEVEL;
+
+ max_value = (PY_MAX_LEVEL * 5 * (p_ptr->hitdie - 1)) / 8;
+ max_value += PY_MAX_LEVEL;
+
+ player_hp[0] = p_ptr->hitdie;
+
+ /* Rerate */
+ while (1)
+ {
+ /* Collect values */
+ for (i = 1; i < PY_MAX_LEVEL; i++)
+ {
+ player_hp[i] = randint(p_ptr->hitdie);
+ player_hp[i] += player_hp[i - 1];
+ }
+
+ /* Legal values */
+ if ((player_hp[PY_MAX_LEVEL - 1] >= min_value) &&
+ (player_hp[PY_MAX_LEVEL - 1] <= max_value)) break;
+ }
+
+ percent = (int)(((long)player_hp[PY_MAX_LEVEL - 1] * 200L) /
+ (p_ptr->hitdie + ((PY_MAX_LEVEL - 1) * p_ptr->hitdie)));
+
+ /* Update and redraw hitpoints */
+ p_ptr->update |= (PU_HP);
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Message */
+ msg_format("Current Life Rating is %d/100.", percent);
+}
+
+
+/*
+ * Create the artifact of the specified number -- DAN
+ *
+ */
+static void wiz_create_named_art()
+{
+ object_type forge;
+ object_type *q_ptr;
+ int i, a_idx;
+ cptr p = "Number of the artifact :";
+ char out_val[80] = "";
+ artifact_type *a_ptr;
+
+ if (!get_string(p, out_val, 4)) return;
+ a_idx = atoi(out_val);
+
+ /* Return if out-of-bounds */
+ if ((a_idx <= 0) || (a_idx >= max_a_idx)) return;
+
+ a_ptr = &a_info[a_idx];
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Ignore "empty" artifacts */
+ if (!a_ptr->name) return;
+
+ /* Acquire the "kind" index */
+ i = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Oops */
+ if (!i) return;
+
+ /* Create the artifact */
+ object_prep(q_ptr, i);
+
+ /* Save the name */
+ q_ptr->name1 = a_idx;
+
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Identify it fully */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+
+ /* All done */
+ msg_print("Allocated.");
+}
+
+/*
+ * Hack -- quick debugging hook
+ */
+void do_cmd_wiz_hack_ben(int num)
+{
+ s32b a;
+
+ /* MAKE(r_ptr, monster_race);
+ COPY(r_ptr, &r_info[500], monster_race);
+
+ r_ptr->level = 1;
+ r_ptr->flags6 |= RF6_BLINK;
+ r_ptr->freq_inate = r_ptr->freq_spell = 90;
+
+ place_monster_one_race = r_ptr;
+ place_monster_one(p_ptr->py - 1, p_ptr->px, 500, 0, TRUE, MSTATUS_PET);*/
+
+ get_lua_var("a", 'd', &a);
+ msg_format("a: %d", a);
+
+ /* Success */
+ return;
+}
+
+void do_cmd_lua_script()
+{
+ char script[80] = "tome_dofile_anywhere(ANGBAND_DIR_CORE, 'gen_idx.lua')";
+
+ if (!get_string("Script:", script, 80)) return;
+
+ exec_lua(script);
+
+ /* Success */
+ return;
+}
+
+
+#ifdef MONSTER_HORDES
+
+/* Summon a horde of monsters */
+static void do_cmd_summon_horde()
+{
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 1000;
+
+ while (--attempts)
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 3);
+ if (cave_naked_bold(wy, wx)) break;
+ }
+
+ (void)alloc_horde(wy, wx);
+}
+
+#endif /* MONSTER_HORDES */
+
+
+/*
+ * Output a long int in binary format.
+ */
+static void prt_binary(u32b flags, int row, int col)
+{
+ int i;
+ u32b bitmask;
+
+ /* Scan the flags */
+ for (i = bitmask = 1; i <= 32; i++, bitmask *= 2)
+ {
+ /* Dump set bits */
+ if (flags & bitmask)
+ {
+ Term_putch(col++, row, TERM_BLUE, '*');
+ }
+
+ /* Dump unset bits */
+ else
+ {
+ Term_putch(col++, row, TERM_WHITE, '-');
+ }
+ }
+}
+
+
+/*
+ * Hack -- Teleport to the target
+ */
+static void do_cmd_wiz_bamf(void)
+{
+ /* Must have a target */
+ if (!target_who) return;
+
+ /* Teleport to the target */
+ teleport_player_to(target_row, target_col);
+}
+
+
+/*
+ * Aux function for "do_cmd_wiz_change()". -RAK-
+ */
+static void do_cmd_wiz_change_aux(void)
+{
+ int i;
+ int tmp_int;
+ long tmp_long;
+ char tmp_val[160];
+ char ppp[80];
+
+
+ /* Query the stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Prompt */
+ sprintf(ppp, "%s: (3-118): ", stat_names[i]);
+
+ /* Default */
+ sprintf(tmp_val, "%d", p_ptr->stat_max[i]);
+
+ /* Query */
+ if (!get_string(ppp, tmp_val, 3)) return;
+
+ /* Extract */
+ tmp_int = atoi(tmp_val);
+
+ /* Verify */
+ if (tmp_int > 18 + 100) tmp_int = 18 + 100;
+ else if (tmp_int < 3) tmp_int = 3;
+
+ /* Save it */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = tmp_int;
+ }
+
+
+ /* Default */
+ sprintf(tmp_val, "%ld", (long)(p_ptr->au));
+
+ /* Query */
+ if (!get_string("Gold: ", tmp_val, 9)) return;
+
+ /* Extract */
+ tmp_long = atol(tmp_val);
+
+ /* Verify */
+ if (tmp_long < 0) tmp_long = 0L;
+
+ /* Save */
+ p_ptr->au = tmp_long;
+
+
+ /* Default */
+ sprintf(tmp_val, "%ld", (long)(p_ptr->max_exp));
+
+ /* Query */
+ if (!get_string("Experience: ", tmp_val, 9)) return;
+
+ /* Extract */
+ tmp_long = atol(tmp_val);
+
+ /* Verify */
+ if (tmp_long < 0) tmp_long = 0L;
+
+ /* Save */
+ p_ptr->max_exp = tmp_long;
+ p_ptr->exp = tmp_long;
+
+ /* Update */
+ check_experience();
+
+ /* Default */
+ sprintf(tmp_val, "%d", p_ptr->luck_base);
+
+ /* Query */
+ if (!get_string("Luck(base): ", tmp_val, 3)) return;
+
+ /* Extract */
+ tmp_long = atol(tmp_val);
+
+ /* Save */
+ p_ptr->luck_base = tmp_long;
+ p_ptr->luck_max = tmp_long;
+}
+
+
+/*
+ * Change various "permanent" player variables.
+ */
+static void do_cmd_wiz_change(void)
+{
+ /* Interact */
+ do_cmd_wiz_change_aux();
+
+ /* Redraw everything */
+ do_cmd_redraw();
+}
+
+
+/*
+ * Wizard routines for creating objects -RAK-
+ * And for manipulating them! -Bernd-
+ *
+ * This has been rewritten to make the whole procedure
+ * of debugging objects much easier and more comfortable.
+ *
+ * The following functions are meant to play with objects:
+ * Create, modify, roll for them (for statistic purposes) and more.
+ * The original functions were by RAK.
+ * The function to show an item's debug information was written
+ * by David Reeve Sward <sward+@CMU.EDU>.
+ * Bernd (wiebelt@mathematik.hu-berlin.de)
+ *
+ * Here are the low-level functions
+ * - wiz_display_item()
+ * display an item's debug-info
+ * - wiz_create_itemtype()
+ * specify tval and sval (type and subtype of object)
+ * - wiz_tweak_item()
+ * specify pval, +AC, +tohit, +todam
+ * Note that the wizard can leave this function anytime,
+ * thus accepting the default-values for the remaining values.
+ * pval comes first now, since it is most important.
+ * - wiz_reroll_item()
+ * apply some magic to the item or turn it into an artifact.
+ * - wiz_roll_item()
+ * Get some statistics about the rarity of an item:
+ * We create a lot of fake items and see if they are of the
+ * same type (tval and sval), then we compare pval and +AC.
+ * If the fake-item is better or equal it is counted.
+ * Note that cursed items that are better or equal (absolute values)
+ * are counted, too.
+ * HINT: This is *very* useful for balancing the game!
+ * - wiz_quantity_item()
+ * change the quantity of an item, but be sane about it.
+ *
+ * And now the high-level functions
+ * - do_cmd_wiz_play()
+ * play with an existing object
+ * - wiz_create_item()
+ * create a new object
+ *
+ * Note -- You do not have to specify "pval" and other item-properties
+ * directly. Just apply magic until you are satisfied with the item.
+ *
+ * Note -- For some items (such as wands, staffs, some rings, etc), you
+ * must apply magic, or you will get "broken" or "uncharged" objects.
+ *
+ * Note -- Redefining artifacts via "do_cmd_wiz_play()" may destroy
+ * the artifact. Be careful.
+ *
+ * Hack -- this function will allow you to create multiple artifacts.
+ * This "feature" may induce crashes or other nasty effects.
+ */
+
+/*
+ * Just display an item's properties (debug-info)
+ * Originally by David Reeve Sward <sward+@CMU.EDU>
+ * Verbose item flags by -Bernd-
+ */
+static void wiz_display_item(object_type *o_ptr)
+{
+ int i, j = 13;
+ u32b f1, f2, f3, f4, f5, esp;
+ char buf[256];
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Clear the screen */
+ for (i = 1; i <= 23; i++) prt("", i, j - 2);
+
+ /* Describe fully */
+ object_desc_store(buf, o_ptr, TRUE, 3);
+
+ prt(buf, 2, j);
+
+ prt(format("kind = %-5d level = %-4d tval = %-5d sval = %-5d",
+ o_ptr->k_idx, k_info[o_ptr->k_idx].level,
+ o_ptr->tval, o_ptr->sval), 4, j);
+
+ prt(format("number = %-3d wgt = %-6d ac = %-5d damage = %dd%d",
+ o_ptr->number, o_ptr->weight,
+ o_ptr->ac, o_ptr->dd, o_ptr->ds), 5, j);
+
+ prt(format("pval = %-5d toac = %-5d tohit = %-4d todam = %-4d",
+ o_ptr->pval, o_ptr->to_a, o_ptr->to_h, o_ptr->to_d), 6, j);
+
+ prt(format("name1 = %-4d name2 = %-4d cost = %ld pval2 = %-5d",
+ o_ptr->name1, o_ptr->name2, (long)object_value(o_ptr), o_ptr->pval2), 7, j);
+
+ prt(format("ident = %04x timeout = %-d",
+ o_ptr->ident, o_ptr->timeout), 8, j);
+
+ prt("+------------FLAGS1------------+", 10, j);
+ prt("AFFECT........SLAY........BRAND.", 11, j);
+ prt(" cvae xsqpaefc", 12, j);
+ prt("siwdcc ssidsahanvudotgddhuoclio", 13, j);
+ prt("tnieoh trnipttmiinmrrnrrraiierl", 14, j);
+ prt("rtsxna..lcfgdkcpmldncltggpksdced", 15, j);
+ prt_binary(f1, 16, j);
+
+ prt("+------------FLAGS2------------+", 17, j);
+ prt("SUST....IMMUN.RESIST............", 18, j);
+ prt(" aefcprpsaefcpfldbc sn ", 19, j);
+ prt("siwdcc cliooeatcliooeialoshtncd", 20, j);
+ prt("tnieoh ierlifraierliatrnnnrhehi", 21, j);
+ prt("rtsxna..dcedslatdcedsrekdfddrxss", 22, j);
+ prt_binary(f2, 23, j);
+
+ prt("+------------FLAGS3------------+", 10, j + 32);
+ prt("fe ehsi st iiiiadta hp", 11, j + 32);
+ prt("il n taihnf ee ggggcregb vr", 12, j + 32);
+ prt("re nowysdose eld nnnntalrl ym", 13, j + 32);
+ prt("ec omrcyewta ieirmsrrrriieaeccc", 14, j + 32);
+ prt("aa taauktmatlnpgeihaefcvnpvsuuu", 15, j + 32);
+ prt("uu egirnyoahivaeggoclioaeoasrrr", 16, j + 32);
+ prt("rr litsopdretitsehtierltxrtesss", 17, j + 32);
+ prt("aa echewestreshtntsdcedeptedeee", 18, j + 32);
+ prt_binary(f3, 19, j + 32);
+}
+
+
+/*
+ * A list of tvals and their textual names
+ */
+tval_desc2 tvals[] =
+{
+ { TV_SWORD, "Sword" },
+ { TV_POLEARM, "Polearm" },
+ { TV_HAFTED, "Hafted Weapon" },
+ { TV_AXE, "Axe" },
+ { TV_BOW, "Bow" },
+ { TV_BOOMERANG, "Boomerang" },
+ { TV_ARROW, "Arrows" },
+ { TV_BOLT, "Bolts" },
+ { TV_SHOT, "Shots" },
+ { TV_SHIELD, "Shield" },
+ { TV_CROWN, "Crown" },
+ { TV_HELM, "Helm" },
+ { TV_GLOVES, "Gloves" },
+ { TV_BOOTS, "Boots" },
+ { TV_CLOAK, "Cloak" },
+ { TV_DRAG_ARMOR, "Dragon Scale Mail" },
+ { TV_HARD_ARMOR, "Hard Armor" },
+ { TV_SOFT_ARMOR, "Soft Armor" },
+ { TV_RING, "Ring" },
+ { TV_AMULET, "Amulet" },
+ { TV_LITE, "Lite" },
+ { TV_POTION, "Potion" },
+ { TV_POTION2, "Potion" },
+ { TV_SCROLL, "Scroll" },
+ { TV_WAND, "Wand" },
+ { TV_STAFF, "Staff" },
+ { TV_ROD_MAIN, "Rod" },
+ { TV_ROD, "Rod Tip" },
+ { TV_BOOK, "Schools Spellbook", },
+ { TV_SYMBIOTIC_BOOK, "Symbiotic Spellbook", },
+ { TV_DRUID_BOOK, "Elemental Stone" },
+ { TV_MUSIC_BOOK, "Music Book" },
+ { TV_DAEMON_BOOK, "Daemon Book" },
+ { TV_SPIKE, "Spikes" },
+ { TV_DIGGING, "Digger" },
+ { TV_CHEST, "Chest" },
+ { TV_FOOD, "Food" },
+ { TV_FLASK, "Flask" },
+ { TV_MSTAFF, "Mage Staff" },
+ { TV_BATERIE, "Essence" },
+ { TV_PARCHMENT, "Parchment" },
+ { TV_INSTRUMENT, "Musical Instrument" },
+ { TV_RUNE1, "Rune 1" },
+ { TV_RUNE2, "Rune 2" },
+ { TV_JUNK, "Junk" },
+ { TV_TRAPKIT, "Trapping Kit" },
+ { 0, NULL }
+};
+
+
+/*
+ * Strip an "object name" into a buffer
+ */
+static void strip_name(char *buf, int k_idx)
+{
+ char *t;
+
+ object_kind *k_ptr = &k_info[k_idx];
+
+ cptr str = (k_name + k_ptr->name);
+
+
+ /* Skip past leading characters */
+ while ((*str == ' ') || (*str == '&')) str++;
+
+ /* Copy useful chars */
+ for (t = buf; *str; str++)
+ {
+ if (*str != '~') *t++ = *str;
+ }
+
+ /* Terminate the new name */
+ *t = '\0';
+}
+
+
+/*
+ * Hack -- title for each column
+ *
+ * XXX XXX XXX This will not work with "EBCDIC", I would think.
+ */
+static char head[4] = { 'a', 'A', '0', ':' };
+
+
+/*
+ * Print a string as required by wiz_create_itemtype().
+ * Trims characters from the beginning until it fits in the space
+ * before the next row or the edge of the screen.
+ */
+static void wci_string(cptr string, int num)
+{
+ int row = 2 + (num % 20), col = 30 * (num / 20);
+ int ch = head[num / 20] + (num % 20), max_len = 0;
+
+ if (76-col < (signed)max_len)
+ max_len = 76-col;
+ else
+ max_len = 30-6;
+
+ if (strlen(string) > (unsigned)max_len)
+ string = string + (strlen(string) - max_len);
+
+ prt(format("[%c] %s", ch, string), row, col);
+}
+
+/*
+ * Specify tval and sval (type and subtype of object) originally
+ * by RAK, heavily modified by -Bernd-
+ *
+ * This function returns the k_idx of an object type, or zero if failed
+ *
+ * List up to 50 choices in three columns
+ */
+static int wiz_create_itemtype(void)
+{
+ int i, num, max_num;
+ int tval;
+
+ cptr tval_desc2;
+ char ch;
+
+ int choice[60];
+
+ char buf[160];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Print all tval's and their descriptions */
+ for (num = 0; (num < 60) && tvals[num].tval; num++)
+ {
+ wci_string(tvals[num].desc, num);
+ }
+
+ /* Me need to know the maximal possible tval_index */
+ max_num = num;
+
+ /* Choose! */
+ if (!get_com("Get what type of object? ", &ch)) return (0);
+
+ /* Analyze choice */
+ num = -1;
+ if ((ch >= head[0]) && (ch < head[0] + 20)) num = ch - head[0];
+ if ((ch >= head[1]) && (ch < head[1] + 20)) num = ch - head[1] + 20;
+ if ((ch >= head[2]) && (ch < head[2] + 17)) num = ch - head[2] + 40;
+
+ /* Bail out if choice is illegal */
+ if ((num < 0) || (num >= max_num)) return (0);
+
+ /* Base object type chosen, fill in tval */
+ tval = tvals[num].tval;
+ tval_desc2 = tvals[num].desc;
+
+
+ /*** And now we go for k_idx ***/
+
+ /* Clear screen */
+ Term_clear();
+
+ /* We have to search the whole itemlist. */
+ for (num = 0, i = 1; (num < 60) && (i < max_k_idx); i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Analyze matching items */
+ if (k_ptr->tval == tval)
+ {
+ /* Hack -- Skip instant artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /* Acquire the "name" of object "i" */
+ strip_name(buf, i);
+
+ /* Print it */
+ wci_string(buf, num);
+
+ /* Remember the object index */
+ choice[num++] = i;
+ }
+ }
+
+ /* Me need to know the maximal possible remembered object_index */
+ max_num = num;
+
+ /* Choose! */
+ if (!get_com(format("What Kind of %s? ", tval_desc2), &ch)) return (0);
+
+ /* Analyze choice */
+ num = -1;
+ if ((ch >= head[0]) && (ch < head[0] + 20)) num = ch - head[0];
+ if ((ch >= head[1]) && (ch < head[1] + 20)) num = ch - head[1] + 20;
+ if ((ch >= head[2]) && (ch < head[2] + 17)) num = ch - head[2] + 40;
+
+ /* Bail out if choice is "illegal" */
+ if ((num < 0) || (num >= max_num)) return (0);
+
+ /* And return successful */
+ return (choice[num]);
+}
+
+
+/*
+ * Tweak an item
+ */
+static void wiz_tweak_item(object_type *o_ptr)
+{
+ cptr p;
+ char tmp_val[80];
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ p = "Enter new 'pval' setting: ";
+ sprintf(tmp_val, "%ld", (long int) o_ptr->pval);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->pval = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'pval2' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->pval2);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->pval2 = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'pval3' setting: ";
+ sprintf(tmp_val, "%ld", (long int) o_ptr->pval3);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->pval3 = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'to_a' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->to_a);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->to_a = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'to_h' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->to_h);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->to_h = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'to_d' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->to_d);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->to_d = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'name2' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->name2);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->name2 = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'name2b' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->name2b);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->name2b = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'sval' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->sval);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->sval = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'obj exp' setting: ";
+ sprintf(tmp_val, "%ld", (long int) o_ptr->exp);
+ if (!get_string(p, tmp_val, 9)) return;
+ wiz_display_item(o_ptr);
+ o_ptr->exp = atoi(tmp_val);
+ if (f4 & TR4_LEVELS) check_experience_obj(o_ptr);
+
+ p = "Enter new 'timeout' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->timeout);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->timeout = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+}
+
+
+/*
+ * Apply magic to an item or turn it into an artifact. -Bernd-
+ */
+static void wiz_reroll_item(object_type *o_ptr)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ char ch;
+
+ bool_ changed = FALSE;
+
+
+ /* Hack -- leave artifacts alone */
+ if (artifact_p(o_ptr) || o_ptr->art_name) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Copy the object */
+ object_copy(q_ptr, o_ptr);
+
+
+ /* Main loop. Ask for magification and artifactification */
+ while (TRUE)
+ {
+ /* Display full item debug information */
+ wiz_display_item(q_ptr);
+
+ /* Ask wizard what to do. */
+ if (!get_com("[a]ccept, [b]ad, [n]ormal, [g]ood, [e]xcellent, [r]andart? ", &ch))
+ {
+ changed = FALSE;
+ break;
+ }
+
+ /* Create/change it! */
+ if (ch == 'A' || ch == 'a')
+ {
+ changed = TRUE;
+ break;
+ }
+
+ /* Apply bad magic, but first clear object */
+ else if (ch == 'b' || ch == 'B')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ hack_apply_magic_power = -2;
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+ }
+
+ /* Apply normal magic, but first clear object */
+ else if (ch == 'n' || ch == 'N')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+ }
+
+ /* Apply good magic, but first clear object */
+ else if (ch == 'g' || ch == 'g')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ apply_magic(q_ptr, dun_level, FALSE, TRUE, FALSE);
+ }
+
+ /* Apply great magic, but first clear object */
+ else if (ch == 'e' || ch == 'e')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ apply_magic(q_ptr, dun_level, FALSE, TRUE, TRUE);
+ }
+
+ /* Apply great magic, but first clear object */
+ else if (ch == 'r' || ch == 'r')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ create_artifact(q_ptr, FALSE, TRUE);
+ }
+ }
+
+
+ /* Notice change */
+ if (changed)
+ {
+ /* Apply changes */
+ object_copy(o_ptr, q_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+}
+
+
+
+/*
+ * Maximum number of rolls
+ */
+#define TEST_ROLL 100000
+
+
+/*
+ * Try to create an item again. Output some statistics. -Bernd-
+ *
+ * The statistics are correct now. We acquire a clean grid, and then
+ * repeatedly place an object in this grid, copying it into an item
+ * holder, and then deleting the object. We fiddle with the artifact
+ * counter flags to prevent weirdness. We use the items to collect
+ * statistics on item creation relative to the initial item.
+ */
+static void wiz_statistics(object_type *o_ptr)
+{
+ long i, matches, better, worse, other;
+
+ char ch;
+ char *quality;
+
+ bool_ good, great;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ obj_theme theme;
+
+ cptr q = "Rolls: %ld, Matches: %ld, Better: %ld, Worse: %ld, Other: %ld";
+
+ /* We can have everything */
+ theme.treasure = OBJ_GENE_TREASURE;
+ theme.combat = OBJ_GENE_COMBAT;
+ theme.magic = OBJ_GENE_MAGIC;
+ theme.tools = OBJ_GENE_TOOL;
+
+ /* XXX XXX XXX Mega-Hack -- allow multiple artifacts */
+ if (artifact_p(o_ptr))
+ {
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = FALSE;
+ }
+ else
+ {
+ a_info[o_ptr->name1].cur_num = 0;
+ }
+ }
+
+
+ /* Interact */
+ while (TRUE)
+ {
+ cptr pmt = "Roll for [n]ormal, [g]ood, or [e]xcellent treasure? ";
+
+ /* Display item */
+ wiz_display_item(o_ptr);
+
+ /* Get choices */
+ if (!get_com(pmt, &ch)) break;
+
+ if (ch == 'n' || ch == 'N')
+ {
+ good = FALSE;
+ great = FALSE;
+ quality = "normal";
+ }
+ else if (ch == 'g' || ch == 'G')
+ {
+ good = TRUE;
+ great = FALSE;
+ quality = "good";
+ }
+ else if (ch == 'e' || ch == 'E')
+ {
+ good = TRUE;
+ great = TRUE;
+ quality = "excellent";
+ }
+ else
+ {
+ good = FALSE;
+ great = FALSE;
+ break;
+ }
+
+ /* Let us know what we are doing */
+ msg_format("Creating a lot of %s items. Base level = %d.",
+ quality, dun_level);
+ msg_print(NULL);
+
+ /* Set counters to zero */
+ matches = better = worse = other = 0;
+
+ /* Let's rock and roll */
+ for (i = 0; i <= TEST_ROLL; i++)
+ {
+ /* Output every few rolls */
+ if ((i < 100) || (i % 100 == 0))
+ {
+ /* Do not wait */
+ inkey_scan = TRUE;
+
+ /* Allow interupt */
+ if (inkey())
+ {
+ /* Flush */
+ flush();
+
+ /* Stop rolling */
+ break;
+ }
+
+ /* Dump the stats */
+ prt(format(q, i, matches, better, worse, other), 0, 0);
+ Term_fresh();
+ }
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Create an object */
+ make_object(q_ptr, good, great, theme);
+
+
+ /* XXX XXX XXX Mega-Hack -- allow multiple artifacts */
+ if (artifact_p(q_ptr))
+ {
+ if (q_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[q_ptr->sval].generated = FALSE;
+ }
+ else
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ }
+
+
+ /* Test for the same tval and sval. */
+ if ((o_ptr->tval) != (q_ptr->tval)) continue;
+ if ((o_ptr->sval) != (q_ptr->sval)) continue;
+
+ /* Check for match */
+ if ((q_ptr->pval == o_ptr->pval) &&
+ (q_ptr->to_a == o_ptr->to_a) &&
+ (q_ptr->to_h == o_ptr->to_h) &&
+ (q_ptr->to_d == o_ptr->to_d))
+ {
+ matches++;
+ }
+
+ /* Check for better */
+ else if ((q_ptr->pval >= o_ptr->pval) &&
+ (q_ptr->to_a >= o_ptr->to_a) &&
+ (q_ptr->to_h >= o_ptr->to_h) &&
+ (q_ptr->to_d >= o_ptr->to_d))
+ {
+ better++;
+ }
+
+ /* Check for worse */
+ else if ((q_ptr->pval <= o_ptr->pval) &&
+ (q_ptr->to_a <= o_ptr->to_a) &&
+ (q_ptr->to_h <= o_ptr->to_h) &&
+ (q_ptr->to_d <= o_ptr->to_d))
+ {
+ worse++;
+ }
+
+ /* Assume different */
+ else
+ {
+ other++;
+ }
+ }
+
+ /* Final dump */
+ msg_format(q, i, matches, better, worse, other);
+ msg_print(NULL);
+ }
+
+
+ /* Hack -- Normally only make a single artifact */
+ if (artifact_p(o_ptr))
+ {
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = TRUE;
+ }
+ else
+ {
+ a_info[o_ptr->name1].cur_num = 1;
+ }
+ }
+}
+
+
+/*
+ * Change the quantity of a the item
+ */
+static void wiz_quantity_item(object_type *o_ptr)
+{
+ int tmp_int;
+ char tmp_val[100];
+
+ /* Default */
+ sprintf(tmp_val, "%d", o_ptr->number);
+
+ /* Query */
+ if (get_string("Quantity: ", tmp_val, 2))
+ {
+ /* Extract */
+ tmp_int = atoi(tmp_val);
+
+ /* Paranoia */
+ if (tmp_int < 1) tmp_int = 1;
+ if (tmp_int > 99) tmp_int = 99;
+
+ /* Accept modifications */
+ o_ptr->number = tmp_int;
+ }
+}
+
+
+
+/*
+ * Play with an item. Options include:
+ * - Output statistics (via wiz_roll_item)
+ * - Reroll item (via wiz_reroll_item)
+ * - Change properties (via wiz_tweak_item)
+ * - Change the number of items (via wiz_quantity_item)
+ */
+static void do_cmd_wiz_play(void)
+{
+ int item;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ char ch;
+
+ bool_ changed;
+
+ cptr q, s;
+
+ /* Get an item */
+ q = "Play with which object? ";
+ s = "You have nothing to play with.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+
+ /* The item was not changed */
+ changed = FALSE;
+
+
+ /* Icky */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Copy object */
+ object_copy(q_ptr, o_ptr);
+
+
+ /* The main loop */
+ while (TRUE)
+ {
+ /* Display the item */
+ wiz_display_item(q_ptr);
+
+ /* Get choice */
+ if (!get_com("[a]ccept [s]tatistics [r]eroll [t]weak [q]uantity apply[m]agic? ", &ch))
+ {
+ changed = FALSE;
+ break;
+ }
+
+ if (ch == 'A' || ch == 'a')
+ {
+ changed = TRUE;
+ break;
+ }
+
+ if (ch == 's' || ch == 'S')
+ {
+ wiz_statistics(q_ptr);
+ }
+
+ if (ch == 'r' || ch == 'r')
+ {
+ wiz_reroll_item(q_ptr);
+ }
+
+ if (ch == 't' || ch == 'T')
+ {
+ wiz_tweak_item(q_ptr);
+ }
+
+ if (ch == 'q' || ch == 'Q')
+ {
+ wiz_quantity_item(q_ptr);
+ }
+
+ if (ch == 'm' || ch == 'M')
+ {
+ int e = q_ptr->name2, eb = q_ptr->name2b;
+
+ object_prep(q_ptr, q_ptr->k_idx);
+ q_ptr->name2 = e;
+ q_ptr->name2b = eb;
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+ }
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Not Icky */
+ character_icky = FALSE;
+
+
+ /* Accept change */
+ if (changed)
+ {
+ /* Message */
+ msg_print("Changes accepted.");
+
+ /* Change */
+ object_copy(o_ptr, q_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+
+ /* Ignore change */
+ else
+ {
+ msg_print("Changes ignored.");
+ }
+}
+
+
+/*
+ * Wizard routine for creating objects -RAK-
+ * Heavily modified to allow magification and artifactification -Bernd-
+ *
+ * Note that wizards cannot create objects on top of other objects.
+ *
+ * Hack -- this routine always makes a "dungeon object", and applies
+ * magic to it, and attempts to decline cursed items.
+ */
+static void wiz_create_item(void)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ int k_idx;
+
+
+ /* Icky */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Get object base type */
+ k_idx = wiz_create_itemtype();
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Not Icky */
+ character_icky = FALSE;
+
+
+ /* Return if failed */
+ if (!k_idx) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create the item */
+ object_prep(q_ptr, k_idx);
+
+ /* Apply magic (no messages, no artifacts) */
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Drop the object from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+
+ /* All done */
+ msg_print("Allocated.");
+}
+
+/*
+ * As above, but takes the k_idx as a parameter instead of using menus.
+ */
+static void wiz_create_item_2(void)
+{
+ object_type forge;
+ object_type *q_ptr;
+ int a_idx;
+ cptr p = "Number of the object :";
+ char out_val[80] = "";
+
+ if (!get_string(p, out_val, 4)) return;
+ a_idx = atoi(out_val);
+
+ /* Return if failed or out-of-bounds */
+ if ((a_idx <= 0) || (a_idx >= max_k_idx)) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create the item */
+ object_prep(q_ptr, a_idx);
+
+ /* Apply magic (no messages, no artifacts) */
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Drop the object from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+
+ /* All done */
+ msg_print("Allocated.");
+}
+
+
+/*
+ * Cure everything instantly
+ */
+void do_cmd_wiz_cure_all(void)
+{
+ object_type *o_ptr;
+
+ /* Remove curses */
+ (void)remove_all_curse();
+
+ /* Restore stats */
+ (void)res_stat(A_STR, TRUE);
+ (void)res_stat(A_INT, TRUE);
+ (void)res_stat(A_WIS, TRUE);
+ (void)res_stat(A_CON, TRUE);
+ (void)res_stat(A_DEX, TRUE);
+ (void)res_stat(A_CHR, TRUE);
+
+ /* Restore the level */
+ (void)restore_level();
+
+ /* Heal the player */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Cure insanity of player */
+ p_ptr->csane = p_ptr->msane;
+ p_ptr->csane_frac = 0;
+
+ /* Heal the player monster */
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ if (o_ptr->k_idx)
+ {
+ o_ptr->pval2 = o_ptr->pval3;
+ }
+
+ /* Restore mana */
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+
+ /* Cure stuff */
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_poisoned(0);
+ (void)set_afraid(0);
+ (void)set_paralyzed(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ (void)set_slow(0);
+ p_ptr->black_breath = FALSE;
+
+ /* No longer hungry */
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ /* Redraw everything */
+ do_cmd_redraw();
+}
+
+
+/*
+ * Go to any level
+ */
+static void do_cmd_wiz_jump(void)
+{
+ /* Ask for level */
+ if (command_arg <= 0)
+ {
+ char ppp[80];
+
+ char tmp_val[160];
+
+ /* Prompt */
+ msg_format("dungeon_type : %d", dungeon_type);
+ sprintf(ppp, "Jump to level (0-%d): ", d_info[dungeon_type].maxdepth);
+
+ /* Default */
+ sprintf(tmp_val, "%d", dun_level);
+
+ /* Ask for a level */
+ if (!get_string(ppp, tmp_val, 10)) return;
+
+ /* Extract request */
+ command_arg = atoi(tmp_val);
+ }
+
+ /* Paranoia */
+ if (command_arg < 0) command_arg = 0;
+
+ /* Paranoia */
+ if (command_arg > d_info[dungeon_type].maxdepth) command_arg = d_info[dungeon_type].maxdepth;
+
+ /* Accept request */
+ msg_format("You jump to dungeon level %d.", command_arg);
+
+ autosave_checkpoint();
+
+ /* Change level */
+ dun_level = command_arg;
+
+ p_ptr->inside_arena = 0;
+ leaving_quest = p_ptr->inside_quest;
+
+ p_ptr->inside_quest = 0;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Become aware of a lot of objects
+ */
+static void do_cmd_wiz_learn(void)
+{
+ int i;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Scan every object */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Induce awareness */
+ if (k_ptr->level <= command_arg)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Prepare object */
+ object_prep(q_ptr, i);
+
+ /* Awareness */
+ object_aware(q_ptr);
+ }
+ }
+}
+
+
+/*
+ * Summon some creatures
+ */
+static void do_cmd_wiz_summon(int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ {
+ (void)summon_specific(p_ptr->py, p_ptr->px, dun_level, 0);
+ }
+}
+
+
+/*
+ * Summon a creature of the specified type
+ *
+ * XXX XXX XXX This function is rather dangerous
+ */
+static void do_cmd_wiz_named(int r_idx, bool_ slp)
+{
+ int i, x, y;
+
+ /* Paranoia */
+ /* if (!r_idx) return; */
+
+ /* Prevent illegal monsters */
+ if (r_idx >= max_r_idx) return;
+
+ /* Try 10 times */
+ for (i = 0; i < 10; i++)
+ {
+ int d = 1;
+
+ /* Pick a location */
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ /* Require empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Place it (allow groups) */
+ m_allow_special[r_idx] = TRUE;
+ if (place_monster_aux(y, x, r_idx, slp, TRUE, MSTATUS_ENEMY)) break;
+ m_allow_special[r_idx] = FALSE;
+ }
+}
+
+
+/*
+ * Summon a creature of the specified type
+ *
+ * XXX XXX XXX This function is rather dangerous
+ */
+void do_cmd_wiz_named_friendly(int r_idx, bool_ slp)
+{
+ int i, x, y;
+
+ /* Paranoia */
+ /* if (!r_idx) return; */
+
+ /* Prevent illegal monsters */
+ if (r_idx >= max_r_idx) return;
+
+ /* Try 10 times */
+ m_allow_special[r_idx] = TRUE;
+ for (i = 0; i < 10; i++)
+ {
+ int d = 1;
+
+ /* Pick a location */
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ /* Require empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Place it (allow groups) */
+ if (place_monster_aux(y, x, r_idx, slp, TRUE, MSTATUS_PET)) break;
+ }
+ m_allow_special[r_idx] = FALSE;
+}
+
+
+
+/*
+ * Hack -- Delete all nearby monsters
+ */
+static void do_cmd_wiz_zap(void)
+{
+ int i;
+
+ /* Genocide everyone nearby */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Delete nearby monsters */
+ if (m_ptr->cdis <= MAX_SIGHT) delete_monster_idx(i);
+ }
+}
+
+
+extern void do_cmd_wiz_body(s16b bidx)
+ /* Might create problems with equipment slots. For safety,
+ be nude when calling this function */
+{
+ p_ptr->body_monster = bidx;
+ p_ptr->disembodied = FALSE;
+ p_ptr->chp = maxroll( (&r_info[bidx])->hdice, (&r_info[bidx])->hside);
+ do_cmd_redraw();
+}
+
+
+/*
+ * External function
+ */
+extern void do_cmd_spoilers(void);
+
+
+
+/*
+ * Hack -- declare external function
+ */
+extern void do_cmd_debug(void);
+
+
+
+/*
+ * Ask for and parse a "debug command"
+ * The "command_arg" may have been set.
+ */
+void do_cmd_debug(void)
+{
+ int x, y;
+ char cmd;
+
+
+ /* Get a "debug command" */
+ get_com("Debug Command: ", &cmd);
+
+ /* Analyze the command */
+ switch (cmd)
+ {
+ /* Nothing */
+ case ESCAPE:
+ case ' ':
+ case '\n':
+ case '\r':
+ break;
+
+
+ /* Hack -- Generate Spoilers */
+ case '"':
+ do_cmd_spoilers();
+ break;
+
+ case 'A':
+ status_main();
+ break;
+
+ /* Hack -- Help */
+ case '?':
+ do_cmd_help();
+ break;
+
+
+ /* Cure all maladies */
+ case 'a':
+ do_cmd_wiz_cure_all();
+ break;
+
+ /* Teleport to target */
+ case 'b':
+ do_cmd_wiz_bamf();
+ break;
+
+ case 'B':
+ do_cmd_wiz_body(command_arg);
+ break;
+
+ /* Create any object */
+ case '-':
+ wiz_create_item_2();
+ break;
+
+ /* Create any object */
+ case 'c':
+ wiz_create_item();
+ break;
+
+ /* Create a named artifact */
+ case 'C':
+ wiz_create_named_art();
+ break;
+
+ /* Detect everything */
+ case 'd':
+ detect_all(DEFAULT_RADIUS);
+ break;
+
+ /* Change of Dungeon type */
+ case 'D':
+ if ((command_arg >= 0) && (command_arg < max_d_idx))
+ {
+ dungeon_type = command_arg;
+ dun_level = d_info[dungeon_type].mindepth;
+ msg_format("You go into %s", d_text + d_info[dungeon_type].text);
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ break;
+
+ /* Edit character */
+ case 'e':
+ do_cmd_wiz_change();
+ break;
+
+ /* Change grid's mana */
+ case 'E':
+ cave[p_ptr->py][p_ptr->px].mana = command_arg;
+ break;
+
+ /* View item info */
+ case 'f':
+ identify_fully();
+ break;
+
+ /* Good Objects */
+ case 'g':
+ if (command_arg <= 0) command_arg = 1;
+ acquirement(p_ptr->py, p_ptr->px, command_arg, FALSE, TRUE);
+ break;
+
+ /* Hitpoint rerating */
+ case 'h':
+ do_cmd_rerate(); break;
+
+#ifdef MONSTER_HORDES
+ case 'H':
+ do_cmd_summon_horde(); break;
+#endif /* MONSTER_HORDES */
+
+ /* Identify */
+ case 'i':
+ (void)ident_spell();
+ break;
+
+ /* Go up or down in the dungeon */
+ case 'j':
+ do_cmd_wiz_jump();
+ break;
+
+ /* Self-Knowledge */
+ case 'k':
+ self_knowledge(NULL);
+ break;
+
+ /* Learn about objects */
+ case 'l':
+ do_cmd_wiz_learn();
+ break;
+
+ /* Magic Mapping */
+ case 'm':
+ map_area();
+ break;
+
+ /* corruption */
+ case 'M':
+ (void) gain_random_corruption(command_arg);
+ break;
+
+ /* Specific reward */
+ case 'r':
+ (void) gain_level_reward(command_arg);
+ break;
+
+ /* Create a trap */
+ case 'R':
+ wiz_place_trap(p_ptr->py, p_ptr->px, command_arg);
+ break;
+
+ /* Summon _friendly_ named monster */
+ case 'N':
+ do_cmd_wiz_named_friendly(command_arg, TRUE);
+ break;
+
+ /* Summon Named Monster */
+ case 'n':
+ do_cmd_wiz_named(command_arg, TRUE);
+ break;
+
+ /* Object playing routines */
+ case 'o':
+ do_cmd_wiz_play();
+ break;
+
+ /* Phase Door */
+ case 'p':
+ teleport_player(10);
+ break;
+
+ /* get a Quest */
+ case 'q':
+ {
+ /* if (quest[command_arg].status == QUEST_STATUS_UNTAKEN)*/
+ if ((command_arg >= 1) && (command_arg < MAX_Q_IDX_INIT) && (command_arg != QUEST_RANDOM))
+ {
+ quest[command_arg].status = QUEST_STATUS_TAKEN;
+ *(quest[command_arg].plot) = command_arg;
+ if (quest[command_arg].type == HOOK_TYPE_C) quest[command_arg].init(command_arg);
+ break;
+ }
+ break;
+ }
+
+ /* Make every dungeon square "known" to test streamers -KMW- */
+ case 'u':
+ {
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave[y][x].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ wiz_lite();
+ break;
+ }
+
+ case 'U':
+ {
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ do_cmd_wiz_named(5, TRUE);
+
+ p_ptr->necro_extra2 = 1;
+
+ /* Display the hitpoints */
+ p_ptr->update |= (PU_HP);
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ break;
+ }
+
+ /* Summon Random Monster(s) */
+ case 's':
+ if (command_arg <= 0) command_arg = 1;
+ do_cmd_wiz_summon(command_arg);
+ break;
+
+ /* Change the feature of the map */
+ case 'S':
+ cave[p_ptr->py][p_ptr->px].special = command_arg;
+ break;
+
+ /* Teleport */
+ case 't':
+ teleport_player_bypass = TRUE;
+ teleport_player(100);
+ teleport_player_bypass = FALSE;
+ break;
+
+ /* Teleport to a town */
+ case 'T':
+ if ((command_arg >= 1) && (command_arg <= max_real_towns))
+ teleport_player_town(command_arg);
+ break;
+
+ /* Very Good Objects */
+ case 'v':
+ if (command_arg <= 0) command_arg = 1;
+ acquirement(p_ptr->py, p_ptr->px, command_arg, TRUE, TRUE);
+ break;
+
+ /* Wizard Light the Level */
+ case 'w':
+ wiz_lite();
+ break;
+
+ /* Make a wish */
+ case 'W':
+ make_wish();
+ break;
+
+ /* Increase Experience */
+ case 'x':
+ if (command_arg)
+ {
+ gain_exp(command_arg);
+ }
+ else
+ {
+ gain_exp(p_ptr->exp + 1);
+ }
+ break;
+
+ /* Zap Monsters (Genocide) */
+ case 'z':
+ do_cmd_wiz_zap();
+ break;
+
+ /* Hack -- whatever I desire */
+ case '_':
+ do_cmd_wiz_hack_ben(command_arg);
+ break;
+
+ /* Mimic shape changing */
+ case '*':
+ p_ptr->tim_mimic = 100;
+ p_ptr->mimic_form = command_arg;
+ /* Redraw title */
+ p_ptr->redraw |= (PR_TITLE);
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+ break;
+
+ /* Gain a fate */
+ case '+':
+ {
+ int i;
+ gain_fate(command_arg);
+ for (i = 0; i < MAX_FATES; i++)
+ fates[i].know = TRUE;
+ break;
+ }
+
+ /* Change the feature of the map */
+ case 'F':
+ msg_format("Trap: %d", cave[p_ptr->py][p_ptr->px].t_idx);
+ msg_format("Old feature: %d", cave[p_ptr->py][p_ptr->px].feat);
+ msg_format("Special: %d", cave[p_ptr->py][p_ptr->px].special);
+ cave_set_feat(p_ptr->py, p_ptr->px, command_arg);
+ break;
+
+ case '=':
+ wiz_align_monster(command_arg);
+ break;
+
+ case '@':
+ wiz_inc_monster_level(command_arg);
+ break;
+
+ case '/':
+ summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], command_arg);
+ break;
+
+ case '>':
+ do_cmd_lua_script();
+ break;
+
+ /* Not a Wizard Command */
+ default:
+ if (!process_hooks(HOOK_DEBUG_COMMAND, "(d)", cmd))
+ msg_print("That is not a valid debug command.");
+ break;
+ }
+}
diff --git a/src/xtra1.c b/src/xtra1.c
new file mode 100644
index 00000000..933bc265
--- /dev/null
+++ b/src/xtra1.c
@@ -0,0 +1,4772 @@
+/* File: misc.c */
+
+/* Purpose: misc code */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Converts stat num into a six-char (right justified) string
+ */
+void cnv_stat(int val, char *out_val)
+{
+ if (!linear_stats)
+ {
+ /* Above 18 */
+ if (val > 18)
+ {
+ int bonus = (val - 18);
+
+ if (bonus >= 220)
+ {
+ sprintf(out_val, "18/%3s", "***");
+ }
+ else if (bonus >= 100)
+ {
+ sprintf(out_val, "18/%03d", bonus);
+ }
+ else
+ {
+ sprintf(out_val, " 18/%02d", bonus);
+ }
+ }
+
+ /* From 3 to 18 */
+ else
+ {
+ sprintf(out_val, " %2d", val);
+ }
+ }
+ else
+ {
+ /* Above 18 */
+ if (val > 18)
+ {
+ int bonus = (val - 18);
+
+ if (bonus >= 220)
+ {
+ sprintf(out_val, " 40");
+ }
+ else
+ {
+ sprintf(out_val, " %2d", 18 + (bonus / 10));
+ }
+ }
+
+ /* From 3 to 18 */
+ else
+ {
+ sprintf(out_val, " %2d", val);
+ }
+ }
+}
+
+
+
+/*
+ * Modify a stat value by a "modifier", return new value
+ *
+ * Stats go up: 3,4,...,17,18,18/10,18/20,...,18/220
+ * Or even: 18/13, 18/23, 18/33, ..., 18/220
+ *
+ * Stats go down: 18/220, 18/210,..., 18/10, 18, 17, ..., 3
+ * Or even: 18/13, 18/03, 18, 17, ..., 3
+ */
+s16b modify_stat_value(int value, int amount)
+{
+ int i;
+
+ /* Reward */
+ if (amount > 0)
+ {
+ /* Apply each point */
+ for (i = 0; i < amount; i++)
+ {
+ /* One point at a time */
+ if (value < 18) value++;
+
+ /* Ten "points" at a time */
+ else value += 10;
+ }
+ }
+
+ /* Penalty */
+ else if (amount < 0)
+ {
+ /* Apply each point */
+ for (i = 0; i < (0 - amount); i++)
+ {
+ /* Ten points at a time */
+ if (value >= 18 + 10) value -= 10;
+
+ /* Hack -- prevent weirdness */
+ else if (value > 18) value = 18;
+
+ /* One point at a time */
+ else if (value > 3) value--;
+ }
+ }
+
+ /* Return new value */
+ return (value);
+}
+
+
+
+/*
+ * Print character info at given row, column in a 13 char field
+ */
+static void prt_field(cptr info, int row, int col)
+{
+ /* Dump 13 spaces to clear */
+ c_put_str(TERM_WHITE, " ", row, col);
+
+ /* Dump the info itself */
+ c_put_str(TERM_L_BLUE, info, row, col);
+}
+
+/*
+ * Prints players max/cur piety
+ */
+static void prt_piety(void)
+{
+ char tmp[32];
+
+ /* Do not show piety unless it matters */
+ if (!p_ptr->pgod) return;
+
+ c_put_str(TERM_L_WHITE, "Pt ", ROW_PIETY, COL_PIETY);
+
+ sprintf(tmp, "%9ld", (long) p_ptr->grace);
+
+ c_put_str((p_ptr->praying) ? TERM_L_BLUE : TERM_GREEN, tmp, ROW_PIETY,
+ COL_PIETY + 3);
+}
+
+
+/*
+ * Prints the player's current sanity.
+ */
+static void prt_sane(void)
+{
+ char tmp[32];
+ byte color;
+ int perc;
+
+ if (p_ptr->msane == 0)
+ {
+ perc = 100;
+ }
+ else
+ {
+ perc = (100 * p_ptr->csane) / p_ptr->msane;
+ }
+
+ c_put_str(TERM_ORANGE, "SN ", ROW_SANITY, COL_SANITY);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->csane, p_ptr->msane);
+
+ if (perc >= 100)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (perc > (10 * hitpoint_warn))
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+
+ c_put_str(color, tmp, ROW_SANITY, COL_SANITY + 3);
+}
+
+
+/*
+ * Print character stat in given row, column
+ */
+static void prt_stat(int stat)
+{
+ char tmp[32];
+
+ cnv_stat(p_ptr->stat_use[stat], tmp);
+
+ /* Display "injured" stat */
+ if (p_ptr->stat_cur[stat] < p_ptr->stat_max[stat])
+ {
+ int colour;
+
+ if (p_ptr->stat_cnt[stat])
+ colour = TERM_ORANGE;
+ else
+ colour = TERM_YELLOW;
+
+ put_str(format("%s: ", stat_names_reduced[stat]), ROW_STAT + stat, 0);
+ c_put_str(colour, tmp, ROW_STAT + stat, COL_STAT + 6);
+ }
+
+ /* Display "healthy" stat */
+ else
+ {
+ put_str(format("%s: ", stat_names[stat]), ROW_STAT + stat, 0);
+ c_put_str(TERM_L_GREEN, tmp, ROW_STAT + stat, COL_STAT + 6);
+ }
+
+ /* Display "boosted" stat */
+ if (p_ptr->stat_cur[stat] > p_ptr->stat_max[stat])
+ {
+ put_str(format("%s: ", stat_names[stat]), ROW_STAT + stat, 0);
+ c_put_str(TERM_VIOLET, tmp, ROW_STAT + stat, COL_STAT + 6);
+ }
+
+ /* Indicate natural maximum */
+ if (p_ptr->stat_max[stat] == 18 + 100)
+ {
+ put_str("!", ROW_STAT + stat, 3);
+ }
+}
+
+
+
+
+/*
+ * Prints "title", including "wizard" or "winner" as needed.
+ */
+static void prt_title(void)
+{
+ cptr p = "";
+
+ /* Mimic shape */
+ if (p_ptr->mimic_form)
+ {
+ call_lua("get_mimic_info", "(d,s)", "s", p_ptr->mimic_form, "show_name", &p);
+ }
+
+ /* Wizard */
+ else if (wizard)
+ {
+ p = "[=-WIZARD-=]";
+ }
+
+ /* Winner */
+ else if (total_winner == WINNER_NORMAL)
+ {
+ p = "***WINNER***";
+ }
+
+ /* Ultra Winner */
+ else if (total_winner == WINNER_ULTRA)
+ {
+ p = "***GOD***";
+ }
+
+ /* Normal */
+ else
+ {
+ p = cp_ptr->titles[(p_ptr->lev - 1) / 5] + c_text;
+
+ }
+
+ prt_field(p, ROW_TITLE, COL_TITLE);
+}
+
+
+/*
+ * Prints level
+ */
+static void prt_level(void)
+{
+ char tmp[32];
+
+ sprintf(tmp, "%6d", p_ptr->lev);
+
+ if (p_ptr->lev >= p_ptr->max_plv)
+ {
+ put_str("LEVEL ", ROW_LEVEL, 0);
+ c_put_str(TERM_L_GREEN, tmp, ROW_LEVEL, COL_LEVEL + 6);
+ }
+ else
+ {
+ put_str("Level ", ROW_LEVEL, 0);
+ c_put_str(TERM_YELLOW, tmp, ROW_LEVEL, COL_LEVEL + 6);
+ }
+}
+
+
+/*
+ * Display the experience
+ */
+static void prt_exp(void)
+{
+ char out_val[32];
+
+ if (!exp_need)
+ {
+ (void)sprintf(out_val, "%8ld", (long)p_ptr->exp);
+ }
+ else
+ {
+ if ((p_ptr->lev >= PY_MAX_LEVEL) || (p_ptr->lev >= max_plev))
+ {
+ (void)sprintf(out_val, "********");
+ }
+ else
+ {
+ (void)sprintf(out_val, "%8ld", (long)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L) - p_ptr->exp);
+ }
+ }
+
+ if (p_ptr->exp >= p_ptr->max_exp)
+ {
+ put_str("EXP ", ROW_EXP, 0);
+ c_put_str(TERM_L_GREEN, out_val, ROW_EXP, COL_EXP + 4);
+ }
+ else
+ {
+ put_str("Exp ", ROW_EXP, 0);
+ c_put_str(TERM_YELLOW, out_val, ROW_EXP, COL_EXP + 4);
+ }
+}
+
+
+/*
+ * Prints current gold
+ */
+static void prt_gold(void)
+{
+ char tmp[32];
+
+ put_str("AU ", ROW_GOLD, COL_GOLD);
+ sprintf(tmp, "%9ld", (long)p_ptr->au);
+ c_put_str(TERM_L_GREEN, tmp, ROW_GOLD, COL_GOLD + 3);
+}
+
+
+
+/*
+ * Prints current AC
+ */
+static void prt_ac(void)
+{
+ char tmp[32];
+
+ put_str("Cur AC ", ROW_AC, COL_AC);
+ sprintf(tmp, "%5d", p_ptr->dis_ac + p_ptr->dis_to_a);
+ c_put_str(TERM_L_GREEN, tmp, ROW_AC, COL_AC + 7);
+}
+
+
+/*
+ * Prints Cur/Max hit points
+ */
+static void prt_hp(void)
+{
+ char tmp[32];
+
+ byte color;
+
+ if (player_char_health) lite_spot(p_ptr->py, p_ptr->px);
+
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ c_put_str(TERM_L_DARK, "DP ", ROW_HP, COL_HP);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->chp, p_ptr->mhp);
+
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_BLUE;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_VIOLET;
+ }
+ else
+ {
+ color = TERM_L_RED;
+ }
+ c_put_str(color, tmp, ROW_HP, COL_HP + 3);
+ }
+ else
+ {
+ c_put_str(TERM_RED, "HP ", ROW_HP, COL_HP);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->chp, p_ptr->mhp);
+
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ c_put_str(color, tmp, ROW_HP, COL_HP + 3);
+ }
+}
+
+/*
+ * Prints Cur/Max monster hit points
+ */
+static void prt_mh(void)
+{
+ char tmp[32];
+
+ byte color;
+
+ object_type *o_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (!o_ptr->pval2)
+ {
+ put_str(" ", ROW_MH, COL_MH);
+ return;
+ }
+
+ put_str("MH ", ROW_MH, COL_MH);
+
+ sprintf(tmp, "%4d/%4d", o_ptr->pval2, (int)o_ptr->pval3);
+ if (o_ptr->pval2 >= o_ptr->pval3)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (o_ptr->pval2 > (o_ptr->pval3 * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ c_put_str(color, tmp, ROW_MH, COL_MH + 3);
+}
+
+
+/*
+ * Prints players max/cur spell points
+ */
+static void prt_sp(void)
+{
+ char tmp[32];
+ byte color;
+
+
+ c_put_str(TERM_L_GREEN, "SP ", ROW_SP, COL_SP);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->csp, p_ptr->msp);
+ if (p_ptr->csp >= p_ptr->msp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->csp > (p_ptr->msp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ c_put_str(color, tmp, ROW_SP, COL_SP + 3);
+}
+
+
+/*
+ * Prints depth in stat area
+ */
+static void prt_depth(void)
+{
+ char depths[32];
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ if (p_ptr->wild_mode)
+ {
+ strcpy(depths, " ");
+ }
+ else if (p_ptr->inside_arena)
+ {
+ strcpy(depths, "Arena");
+ }
+ else if (get_dungeon_name(depths))
+ {
+ /* Empty */
+ }
+ else if (dungeon_flags2 & DF2_SPECIAL)
+ {
+ strcpy(depths, "Special");
+ }
+ else if (p_ptr->inside_quest)
+ {
+ strcpy(depths, "Quest");
+ }
+ else if (!dun_level)
+ {
+ if (wf_info[wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat].name + wf_name)
+ strcpy(depths, wf_info[wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat].name + wf_name);
+ else
+ strcpy(depths, "Town/Wild");
+ }
+ else if (depth_in_feet)
+ {
+ if (dungeon_flags1 & DF1_TOWER)
+ {
+ (void)strnfmt(depths, 32, "%c%c%c -%d ft",
+ d_ptr->short_name[0],
+ d_ptr->short_name[1],
+ d_ptr->short_name[2],
+ dun_level * 50);
+ }
+ else
+ {
+ (void)strnfmt(depths, 32, "%c%c%c %d ft",
+ d_ptr->short_name[0],
+ d_ptr->short_name[1],
+ d_ptr->short_name[2],
+ dun_level * 50);
+ }
+ }
+ else
+ {
+ if (dungeon_flags1 & DF1_TOWER)
+ {
+ (void)strnfmt(depths, 32, "%c%c%c -%d",
+ d_ptr->short_name[0],
+ d_ptr->short_name[1],
+ d_ptr->short_name[2],
+ dun_level);
+ }
+ else
+ {
+ (void)strnfmt(depths, 32, "%c%c%c %d",
+ d_ptr->short_name[0],
+ d_ptr->short_name[1],
+ d_ptr->short_name[2],
+ dun_level);
+ }
+ }
+
+ /* Right-Adjust the "depth", and clear old values */
+ if (p_ptr->word_recall)
+ c_prt(TERM_ORANGE, format("%13s", depths), ROW_DEPTH, COL_DEPTH);
+ else
+ prt(format("%13s", depths), ROW_DEPTH, COL_DEPTH);
+}
+
+
+/*
+ * Prints status of hunger
+ */
+static void prt_hunger(void)
+{
+ /* Fainting / Starving */
+ if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ c_put_str(TERM_RED, "Weak ", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Weak */
+ else if (p_ptr->food < PY_FOOD_WEAK)
+ {
+ c_put_str(TERM_ORANGE, "Weak ", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Hungry */
+ else if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ c_put_str(TERM_YELLOW, "Hungry", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Normal */
+ else if (p_ptr->food < PY_FOOD_FULL)
+ {
+ c_put_str(TERM_L_GREEN, " ", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Full */
+ else if (p_ptr->food < PY_FOOD_MAX)
+ {
+ c_put_str(TERM_L_GREEN, "Full ", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Gorged */
+ else
+ {
+ c_put_str(TERM_GREEN, "Gorged", ROW_HUNGRY, COL_HUNGRY);
+ }
+}
+
+
+/*
+ * Prints Blind status
+ */
+static void prt_blind(void)
+{
+ if (p_ptr->blind)
+ {
+ c_put_str(TERM_ORANGE, "Blind", ROW_BLIND, COL_BLIND);
+ }
+ else
+ {
+ put_str(" ", ROW_BLIND, COL_BLIND);
+ }
+}
+
+
+/*
+ * Prints Confusion status
+ */
+static void prt_confused(void)
+{
+ if (p_ptr->confused)
+ {
+ c_put_str(TERM_ORANGE, "Conf", ROW_CONFUSED, COL_CONFUSED);
+ }
+ else
+ {
+ put_str(" ", ROW_CONFUSED, COL_CONFUSED);
+ }
+}
+
+
+/*
+ * Prints Fear status
+ */
+static void prt_afraid(void)
+{
+ if (p_ptr->afraid)
+ {
+ c_put_str(TERM_ORANGE, "Afraid", ROW_AFRAID, COL_AFRAID);
+ }
+ else
+ {
+ put_str(" ", ROW_AFRAID, COL_AFRAID);
+ }
+}
+
+
+/*
+ * Prints Poisoned status
+ */
+static void prt_poisoned(void)
+{
+ if (p_ptr->poisoned)
+ {
+ c_put_str(TERM_ORANGE, "Poison", ROW_POISONED, COL_POISONED);
+ }
+ else
+ {
+ put_str(" ", ROW_POISONED, COL_POISONED);
+ }
+}
+
+
+/*
+ * Prints trap detection status
+ */
+static void prt_dtrap(void)
+{
+ if (cave[p_ptr->py][p_ptr->px].info & CAVE_DETECT)
+ {
+ c_put_str(TERM_L_GREEN, "DTrap", ROW_DTRAP, COL_DTRAP);
+ }
+ else
+ {
+ put_str(" ", ROW_DTRAP, COL_DTRAP);
+ }
+}
+
+
+/*
+ * Prints Searching, Resting, Paralysis, or 'count' status
+ * Display is always exactly 10 characters wide (see below)
+ *
+ * This function was a major bottleneck when resting, so a lot of
+ * the text formatting code was optimized in place below.
+ */
+static void prt_state(void)
+{
+ byte attr = TERM_WHITE;
+
+ char text[16];
+
+
+ /* Paralysis */
+ if (p_ptr->paralyzed)
+ {
+ attr = TERM_RED;
+
+ strcpy(text, "Paralyzed!");
+ }
+
+ /* Resting */
+ else if (resting)
+ {
+ int i;
+
+ /* Start with "Rest" */
+ strcpy(text, "Rest ");
+
+ /* Extensive (timed) rest */
+ if (resting >= 1000)
+ {
+ i = resting / 100;
+ text[9] = '0';
+ text[8] = '0';
+ text[7] = '0' + (i % 10);
+ if (i >= 10)
+ {
+ i = i / 10;
+ text[6] = '0' + (i % 10);
+ if (i >= 10)
+ {
+ text[5] = '0' + (i / 10);
+ }
+ }
+ }
+
+ /* Long (timed) rest */
+ else if (resting >= 100)
+ {
+ i = resting;
+ text[9] = '0' + (i % 10);
+ i = i / 10;
+ text[8] = '0' + (i % 10);
+ text[7] = '0' + (i / 10);
+ }
+
+ /* Medium (timed) rest */
+ else if (resting >= 10)
+ {
+ i = resting;
+ text[9] = '0' + (i % 10);
+ text[8] = '0' + (i / 10);
+ }
+
+ /* Short (timed) rest */
+ else if (resting > 0)
+ {
+ i = resting;
+ text[9] = '0' + (i);
+ }
+
+ /* Rest until healed */
+ else if (resting == -1)
+ {
+ text[5] = text[6] = text[7] = text[8] = text[9] = '*';
+ }
+
+ /* Rest until done */
+ else if (resting == -2)
+ {
+ text[5] = text[6] = text[7] = text[8] = text[9] = '&';
+ }
+ }
+
+ /* Repeating */
+ else if (command_rep)
+ {
+ if (command_rep > 999)
+ {
+ (void)sprintf(text, "Rep. %3d00", command_rep / 100);
+ }
+ else
+ {
+ (void)sprintf(text, "Repeat %3d", command_rep);
+ }
+ }
+
+ /* Searching */
+ else if (p_ptr->searching)
+ {
+ strcpy(text, "Searching ");
+ }
+
+ /* Nothing interesting */
+ else
+ {
+ strcpy(text, " ");
+ }
+
+ /* Display the info (or blanks) */
+ c_put_str(attr, text, ROW_STATE, COL_STATE);
+}
+
+
+/*
+ * Prints the speed of a character. -CJS-
+ */
+static void prt_speed(void)
+{
+ int i = p_ptr->pspeed;
+
+ byte attr = TERM_WHITE;
+ char buf[32] = "";
+
+ /* Hack -- Visually "undo" the Search Mode Slowdown */
+ if (p_ptr->searching) i += 10;
+
+ /* Fast */
+ if (i > 110)
+ {
+ attr = TERM_L_GREEN;
+ sprintf(buf, "Fast (+%d)", (i - 110));
+ }
+
+ /* Slow */
+ else if (i < 110)
+ {
+ attr = TERM_L_UMBER;
+ sprintf(buf, "Slow (-%d)", (110 - i));
+ }
+
+ /* Display the speed */
+ c_put_str(attr, format("%-10s", buf), ROW_SPEED, COL_SPEED);
+}
+
+
+static void prt_study(void)
+{
+ if (p_ptr->skill_points)
+ {
+ put_str("Skill", ROW_STUDY, COL_STUDY);
+ }
+ else
+ {
+ put_str(" ", ROW_STUDY, COL_STUDY);
+ }
+}
+
+
+static void prt_cut(void)
+{
+ int c = p_ptr->cut;
+
+ if (c > 1000)
+ {
+ c_put_str(TERM_L_RED, "Mortal wound", ROW_CUT, COL_CUT);
+ }
+ else if (c > 200)
+ {
+ c_put_str(TERM_RED, "Deep gash ", ROW_CUT, COL_CUT);
+ }
+ else if (c > 100)
+ {
+ c_put_str(TERM_RED, "Severe cut ", ROW_CUT, COL_CUT);
+ }
+ else if (c > 50)
+ {
+ c_put_str(TERM_ORANGE, "Nasty cut ", ROW_CUT, COL_CUT);
+ }
+ else if (c > 25)
+ {
+ c_put_str(TERM_ORANGE, "Bad cut ", ROW_CUT, COL_CUT);
+ }
+ else if (c > 10)
+ {
+ c_put_str(TERM_YELLOW, "Light cut ", ROW_CUT, COL_CUT);
+ }
+ else if (c)
+ {
+ c_put_str(TERM_YELLOW, "Graze ", ROW_CUT, COL_CUT);
+ }
+ else
+ {
+ put_str(" ", ROW_CUT, COL_CUT);
+ }
+}
+
+
+
+static void prt_stun(void)
+{
+ int s = p_ptr->stun;
+
+ if (s > 100)
+ {
+ c_put_str(TERM_RED, "Knocked out ", ROW_STUN, COL_STUN);
+ }
+ else if (s > 50)
+ {
+ c_put_str(TERM_ORANGE, "Heavy stun ", ROW_STUN, COL_STUN);
+ }
+ else if (s)
+ {
+ c_put_str(TERM_ORANGE, "Stun ", ROW_STUN, COL_STUN);
+ }
+ else
+ {
+ put_str(" ", ROW_STUN, COL_STUN);
+ }
+}
+
+
+
+/*
+ * Redraw the "monster health bar" -DRS-
+ * Rather extensive modifications by -BEN-
+ *
+ * The "monster health bar" provides visual feedback on the "health"
+ * of the monster currently being "tracked". There are several ways
+ * to "track" a monster, including targetting it, attacking it, and
+ * affecting it (and nobody else) with a ranged attack.
+ *
+ * Display the monster health bar (affectionately known as the
+ * "health-o-meter"). Clear health bar if nothing is being tracked.
+ * Auto-track current target monster when bored. Note that the
+ * health-bar stops tracking any monster that "disappears".
+ */
+static void health_redraw(void)
+{
+
+#ifdef DRS_SHOW_HEALTH_BAR
+
+ /* Not tracking */
+ if (!health_who)
+ {
+ /* Erase the health bar */
+ Term_erase(COL_INFO, ROW_INFO, 12);
+ }
+
+ /* Tracking an unseen monster */
+ else if (!m_list[health_who].ml)
+ {
+ /* Indicate that the monster health is "unknown" */
+ Term_putstr(COL_INFO, ROW_INFO, 12, TERM_WHITE, "[----------]");
+ }
+
+ /* Tracking a hallucinatory monster */
+ else if (p_ptr->image)
+ {
+ /* Indicate that the monster health is "unknown" */
+ Term_putstr(COL_INFO, ROW_INFO, 12, TERM_WHITE, "[----------]");
+ }
+
+ /* Tracking a dead monster (???) */
+ else if (!m_list[health_who].hp < 0)
+ {
+ /* Indicate that the monster health is "unknown" */
+ Term_putstr(COL_INFO, ROW_INFO, 12, TERM_WHITE, "[----------]");
+ }
+
+ /* Tracking a visible monster */
+ else
+ {
+ int pct, len;
+
+ monster_type *m_ptr = &m_list[health_who];
+
+ /* Default to almost dead */
+ byte attr = TERM_RED;
+
+ /* Extract the "percent" of health */
+ pct = 100L * m_ptr->hp / m_ptr->maxhp;
+
+ /* Badly wounded */
+ if (pct >= 10) attr = TERM_L_RED;
+
+ /* Wounded */
+ if (pct >= 25) attr = TERM_ORANGE;
+
+ /* Somewhat Wounded */
+ if (pct >= 60) attr = TERM_YELLOW;
+
+ /* Healthy */
+ if (pct >= 100) attr = TERM_L_GREEN;
+
+ /* Afraid */
+ if (m_ptr->monfear) attr = TERM_VIOLET;
+
+ /* Asleep */
+ if (m_ptr->csleep) attr = TERM_BLUE;
+
+ /* Poisoned */
+ if (m_ptr->poisoned) attr = TERM_GREEN;
+
+ /* Bleeding */
+ if (m_ptr->bleeding) attr = TERM_RED;
+
+ /* Convert percent into "health" */
+ len = (pct < 10) ? 1 : (pct < 90) ? (pct / 10 + 1) : 10;
+
+ /* Default to "unknown" */
+ Term_putstr(COL_INFO, ROW_INFO, 12, TERM_WHITE, "[----------]");
+
+ /* Dump the current "health" (use '*' symbols) */
+ Term_putstr(COL_INFO + 1, ROW_INFO, len, attr, "**********");
+ }
+
+#endif
+
+}
+
+
+
+/*
+ * Display basic info (mostly left of map)
+ */
+static void prt_frame_basic(void)
+{
+ int i;
+
+ /* Race and Class */
+ prt_field(rp_ptr->title + rp_name, ROW_RACE, COL_RACE);
+ prt_field(spp_ptr->title + c_name, ROW_CLASS, COL_CLASS);
+
+ /* Title */
+ prt_title();
+
+ /* Level/Experience */
+ prt_level();
+ prt_exp();
+
+ /* All Stats */
+ for (i = 0; i < 6; i++) prt_stat(i);
+
+ /* Armor */
+ prt_ac();
+
+ /* Hitpoints */
+ prt_hp();
+
+ /* Current sanity */
+ prt_sane();
+
+ /* Spellpoints */
+ prt_sp();
+
+ /* Piety */
+ prt_piety();
+
+ /* Monster hitpoints */
+ prt_mh();
+
+ /* Gold */
+ prt_gold();
+
+ /* Current depth */
+ prt_depth();
+
+ /* Special */
+ health_redraw();
+}
+
+
+/*
+ * Display extra info (mostly below map)
+ */
+static void prt_frame_extra(void)
+{
+ /* Cut/Stun */
+ prt_cut();
+ prt_stun();
+
+ /* Food */
+ prt_hunger();
+
+ /* Various */
+ prt_blind();
+ prt_confused();
+ prt_afraid();
+ prt_poisoned();
+ prt_dtrap();
+
+ /* State */
+ prt_state();
+
+ /* Speed */
+ prt_speed();
+
+ /* Study spells */
+ prt_study();
+}
+
+
+/*
+ * Hack -- display inventory in sub-windows
+ */
+static void fix_inven(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_INVEN))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display inventory */
+ display_inven();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+
+/*
+ * Hack -- display equipment in sub-windows
+ */
+static void fix_equip(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_EQUIP))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display equipment */
+ display_equip();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+/*
+ * Hack -- display character in sub-windows
+ */
+static void fix_player(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_PLAYER))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display player */
+ display_player(0);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+
+/*
+ * Hack -- display recent messages in sub-windows
+ *
+ * XXX XXX XXX Adjust for width and split messages
+ */
+void fix_message(void)
+{
+ int j, i;
+ int w, h;
+ int x, y;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_MESSAGE))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Get size */
+ Term_get_size(&w, &h);
+
+ /* Dump messages */
+ for (i = 0; i < h; i++)
+ {
+ /* Dump the message on the appropriate line */
+ display_message(0, (h - 1) - i, strlen(message_str((s16b)i)), message_color((s16b)i), message_str((s16b)i));
+
+ /* Cursor */
+ Term_locate(&x, &y);
+
+ /* Clear to end of line */
+ Term_erase(x, y, 255);
+ }
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display overhead view in sub-windows
+ *
+ * Note that the "player" symbol does NOT appear on the map.
+ */
+static void fix_overhead(void)
+{
+ int j;
+
+ int cy, cx;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_OVERHEAD))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Redraw map */
+ display_map(&cy, &cx);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display monster recall in sub-windows
+ */
+static void fix_monster(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_MONSTER))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display monster race info */
+ if (monster_race_idx) display_roff(monster_race_idx, monster_ego_idx);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display object recall in sub-windows
+ */
+static void fix_object(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_OBJECT))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Clear */
+ Term_clear();
+
+ /* Display object info */
+ if (tracked_object)
+ if (!object_out_desc(tracked_object, NULL, FALSE, FALSE)) text_out("You see nothing special.");
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+/* Show the monster list in a window */
+
+static void fix_m_list(void)
+{
+ int i, j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ int c = 0;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_M_LIST))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Clear */
+ Term_clear();
+
+ /* Hallucination */
+ if (p_ptr->image)
+ {
+ c_prt(TERM_WHITE, "You can not see clearly", 0, 0);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+
+ return;
+ }
+
+ /* reset visible count */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ r_ptr->total_visible = 0;
+ }
+
+ /* Count up the number visible in each race */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Skip dead monsters */
+ if (m_ptr->hp < 0) continue;
+
+ /* Skip unseen monsters */
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ /* Memorized objects */
+ if (!o_ptr->marked) continue;
+ }
+ else
+ {
+ if (!m_ptr->ml) continue;
+ }
+
+ /* Increase for this race */
+ r_ptr->total_visible++;
+
+ /* Increase total Count */
+ c++;
+ }
+
+ /* Are monsters visible? */
+ if (c)
+ {
+ int w, h, num = 0;
+
+ (void)Term_get_size(&w, &h);
+
+ c_prt(TERM_WHITE, format("You can see %d monster%s", c, (c > 1 ? "s:" : ":")), 0, 0);
+
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Default Colour */
+ byte attr = TERM_SLATE;
+
+ /* Only visible monsters */
+ if (!r_ptr->total_visible) continue;
+
+ /* Uniques */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ attr = TERM_L_BLUE;
+ }
+
+ /* Have we ever killed one? */
+ if (r_ptr->r_tkills)
+ {
+ if (r_ptr->level > dun_level)
+ {
+ attr = TERM_VIOLET;
+
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ attr = TERM_RED;
+ }
+ }
+ }
+ else
+ {
+ if (!(r_ptr->flags1 & RF1_UNIQUE)) attr = TERM_GREEN;
+ }
+
+
+ /* Dump the monster name */
+ if (r_ptr->total_visible == 1)
+ {
+ c_prt(attr, (r_name + r_ptr->name), (num % (h - 1)) + 1, (num / (h - 1) * 26));
+ }
+ else
+ {
+ c_prt(attr, format("%s (x%d)", r_name + r_ptr->name, r_ptr->total_visible), (num % (h - 1)) + 1, (num / (h - 1)) * 26);
+ }
+
+ num++;
+
+ }
+
+ }
+ else
+ {
+ c_prt(TERM_WHITE, "You see no monsters.", 0, 0);
+ }
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Calculate number of spells player should have, and forget,
+ * or remember, spells until that number is properly reflected.
+ *
+ * Note that this function induces various "status" messages,
+ * which must be bypasses until the character is created.
+ */
+static void calc_spells(void)
+{
+ p_ptr->new_spells = 0;
+}
+
+/* Ugly hack */
+bool_ calc_powers_silent = FALSE;
+
+/* Calc the player powers */
+static void calc_powers(void)
+{
+ int i, p = 0;
+ bool_ *old_powers;
+
+ /* Hack -- wait for creation */
+ if (!character_generated) return;
+
+ /* Hack -- handle "xtra" mode */
+ if (character_xtra) return;
+
+ C_MAKE(old_powers, power_max, bool_);
+
+ /* Save old powers */
+ for (i = 0; i < power_max; i++) old_powers[i] = p_ptr->powers[i];
+
+ /* Get intrinsincs */
+ for (i = 0; i < POWER_MAX_INIT; i++) p_ptr->powers[i] = p_ptr->powers_mod[i];
+ for (; i < power_max; i++) p_ptr->powers[i] = 0;
+
+ /* Hooked powers */
+ process_hooks(HOOK_CALC_POWERS, "()");
+
+ /* Add objects powers */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+
+ p = object_power(o_ptr);
+ if (p != -1) p_ptr->powers[p] = TRUE;
+ }
+
+ if ((!p_ptr->tim_mimic) && (!p_ptr->body_monster))
+ {
+ /* Add in racial and subracial powers */
+ for (i = 0; i < 4; i++)
+ {
+ p = rp_ptr->powers[i];
+ if (p != -1) p_ptr->powers[p] = TRUE;
+
+ p = rmp_ptr->powers[i];
+ if (p != -1) p_ptr->powers[p] = TRUE;
+ }
+ }
+ else if (p_ptr->mimic_form)
+ call_lua("calc_mimic_power", "(d)", "", p_ptr->mimic_form);
+
+ /* Add in class powers */
+ for (i = 0; i < 4; i++)
+ {
+ p = cp_ptr->powers[i];
+ if (p != -1) p_ptr->powers[p] = TRUE;
+ }
+
+ if (p_ptr->disembodied)
+ {
+ p = PWR_INCARNATE;
+ p_ptr->powers[p] = TRUE;
+ }
+
+ /* Now lets warn the player */
+ for (i = 0; i < power_max; i++)
+ {
+ s32b old = old_powers[i];
+ s32b new_ = p_ptr->powers[i];
+
+ if (new_ > old)
+ {
+ if (!calc_powers_silent) cmsg_print(TERM_GREEN, powers_type[i].gain_text);
+ }
+ else if (new_ < old)
+ {
+ if (!calc_powers_silent) cmsg_print(TERM_RED, powers_type[i].lose_text);
+ }
+ }
+
+ calc_powers_silent = FALSE;
+ C_FREE(old_powers, power_max, bool_);
+}
+
+
+/*
+ * Calculate the player's sanity
+ */
+
+void calc_sanity(void)
+{
+ int bonus, msane;
+
+ /* Hack -- use the con/hp table for sanity/wis */
+ bonus = ((int)(adj_con_mhp[p_ptr->stat_ind[A_WIS]]) - 128);
+
+ /* Hack -- assume 5 sanity points per level. */
+ msane = 5 * (p_ptr->lev + 1) + (bonus * p_ptr->lev / 2);
+
+ if (msane < p_ptr->lev + 1) msane = p_ptr->lev + 1;
+
+ if (p_ptr->msane != msane)
+ {
+ /* Sanity carries over between levels. */
+ p_ptr->csane += (msane - p_ptr->msane);
+
+ p_ptr->msane = msane;
+
+ if (p_ptr->csane >= msane)
+ {
+ p_ptr->csane = msane;
+ p_ptr->csane_frac = 0;
+ }
+
+ p_ptr->redraw |= (PR_SANITY);
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+/*
+ * Calculate maximum mana. You do not need to know any spells.
+ * Note that mana is lowered by heavy (or inappropriate) armor.
+ *
+ * This function induces status messages.
+ */
+static void calc_mana(void)
+{
+ int msp, levels, cur_wgt, max_wgt;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_type *o_ptr;
+
+ levels = p_ptr->lev;
+
+ /* Hack -- no negative mana */
+ if (levels < 0) levels = 0;
+
+ /* Extract total mana */
+ msp = get_skill_scale(SKILL_MAGIC, 200) +
+ (adj_mag_mana[
+ (p_ptr->stat_ind[A_INT] > p_ptr->stat_ind[A_WIS]) ?
+ p_ptr->stat_ind[A_INT] : p_ptr->stat_ind[A_WIS]
+ ] * levels / 4);
+
+ /* Hack -- usually add one mana */
+ if (msp) msp++;
+
+ /* Possessors mana is different */
+ if (p_ptr->body_monster && (!p_ptr->disembodied))
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ int f = 100 / (r_ptr->freq_spell ? r_ptr->freq_spell : 1);
+
+ msp = 21 - f;
+
+ if (msp < 1) msp = 1;
+ }
+
+ /* Apply race mod mana */
+ msp = msp * rmp_ptr->mana / 100;
+
+ /* Apply class mana */
+ msp += msp * cp_ptr->mana / 100;
+
+ /* Apply Eru mana */
+ GOD(GOD_ERU)
+ {
+ s32b tmp = p_ptr->grace;
+
+ if (tmp >= 35000) tmp = 35000;
+ tmp /= 100;
+ msp += msp * tmp / 1000;
+ }
+
+ /* Only mages are affected */
+ if (forbid_gloves())
+ {
+ /* Assume player is not encumbered by gloves */
+ p_ptr->cumber_glove = FALSE;
+
+ /* Get the gloves */
+ o_ptr = &p_ptr->inventory[INVEN_HANDS];
+
+ /* Examine the gloves */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Normal gloves hurt mage-type spells */
+ if (o_ptr->k_idx &&
+ !(f2 & (TR2_FREE_ACT)) &&
+ !((f1 & (TR1_DEX)) && (o_ptr->pval > 0)) &&
+ !(f5 & TR5_SPELL_CONTAIN))
+ {
+ /* Encumbered */
+ p_ptr->cumber_glove = TRUE;
+
+ /* Reduce mana */
+ msp = (3 * msp) / 4;
+ }
+ }
+
+ /* Augment mana */
+ if (munchkin_multipliers)
+ {
+ if (p_ptr->to_m) msp += msp * p_ptr->to_m / 5;
+ }
+ else
+ {
+ if (p_ptr->to_m) msp += msp * p_ptr->to_m / 10;
+ }
+
+ /* Assume player not encumbered by armor */
+ p_ptr->cumber_armor = FALSE;
+
+ /* Weigh the armor */
+ cur_wgt = 0;
+ cur_wgt += p_ptr->inventory[INVEN_BODY].weight;
+ cur_wgt += p_ptr->inventory[INVEN_HEAD].weight;
+ cur_wgt += p_ptr->inventory[INVEN_ARM].weight;
+ cur_wgt += p_ptr->inventory[INVEN_OUTER].weight;
+ cur_wgt += p_ptr->inventory[INVEN_HANDS].weight;
+ cur_wgt += p_ptr->inventory[INVEN_FEET].weight;
+
+ /* Determine the weight allowance */
+ max_wgt = 200 + get_skill_scale(SKILL_COMBAT, 500);
+
+ /* Heavy armor penalizes mana */
+ if (((cur_wgt - max_wgt) / 10) > 0)
+ {
+ /* Encumbered */
+ p_ptr->cumber_armor = TRUE;
+
+ /* Reduce mana */
+ msp -= ((cur_wgt - max_wgt) / 10);
+ }
+
+ /* When meditating your mana is increased ! */
+ if (p_ptr->meditation)
+ {
+ msp += 50;
+ p_ptr->csp += 50;
+ }
+
+ /* Sp mods? */
+ if (process_hooks_ret(HOOK_CALC_MANA, "d", "(d)", msp))
+ {
+ msp = process_hooks_return[0].num;
+ }
+
+ /* Mana can never be negative */
+ if (msp < 0) msp = 0;
+
+
+ /* Maximum mana has changed */
+ if (p_ptr->msp != msp)
+ {
+ /* Save new limit */
+ p_ptr->msp = msp;
+
+ /* Enforce new limit */
+ if (p_ptr->csp >= msp)
+ {
+ p_ptr->csp = msp;
+ p_ptr->csp_frac = 0;
+ }
+
+ /* Display mana later */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+
+ /* Hack -- handle "xtra" mode */
+ if (character_xtra) return;
+
+ /* Take note when "glove state" changes */
+ if (p_ptr->old_cumber_glove != p_ptr->cumber_glove)
+ {
+ /* Message */
+ if (p_ptr->cumber_glove)
+ {
+ msg_print("Your covered hands feel unsuitable for spellcasting.");
+ }
+ else
+ {
+ msg_print("Your hands feel more suitable for spellcasting.");
+ }
+
+ /* Save it */
+ p_ptr->old_cumber_glove = p_ptr->cumber_glove;
+ }
+
+
+ /* Take note when "armor state" changes */
+ if (p_ptr->old_cumber_armor != p_ptr->cumber_armor)
+ {
+ /* Message */
+ if (p_ptr->cumber_armor)
+ {
+ msg_print("The weight of your armor encumbers your movement.");
+ }
+ else
+ {
+ msg_print("You feel able to move more freely.");
+ }
+
+ /* Save it */
+ p_ptr->old_cumber_armor = p_ptr->cumber_armor;
+ }
+}
+
+
+
+/*
+ * Calculate the players (maximal) hit points
+ * Adjust current hitpoints if necessary
+ */
+void calc_hitpoints(void)
+{
+ int bonus, mhp;
+
+ /* Un-inflate "half-hitpoint bonus per level" value */
+ bonus = ((int)(adj_con_mhp[p_ptr->stat_ind[A_CON]]) - 128);
+
+ /* Calculate hitpoints */
+ mhp = player_hp[p_ptr->lev - 1] + (bonus * p_ptr->lev / 2);
+
+ /* Always have at least one hitpoint per level */
+ if (mhp < p_ptr->lev + 1) mhp = p_ptr->lev + 1;
+
+ /* Factor in the pernament hp modifications */
+ mhp += p_ptr->hp_mod;
+ if (mhp < 1) mhp = 1;
+
+ /* Hack: Sorcery impose a hp penality */
+ if (mhp && (get_skill(SKILL_SORCERY)))
+ {
+ mhp -= mhp * get_skill_scale(SKILL_SORCERY, 50) / 100;
+ if (mhp < 1) mhp = 1;
+ }
+
+ /* Factor in the melkor hp modifications */
+ GOD(GOD_MELKOR)
+ {
+ mhp -= (p_ptr->melkor_sacrifice * 10);
+ if (mhp < 1) mhp = 1;
+ }
+
+ /* Factor in the hero / superhero settings */
+ if (p_ptr->hero) mhp += 10;
+ if (p_ptr->shero) mhp += 30;
+
+ /* Augment Hitpoint */
+ if (munchkin_multipliers)
+ {
+ mhp += mhp * p_ptr->to_l / 5;
+ }
+ else
+ {
+ mhp += mhp * p_ptr->to_l / 10;
+ }
+ if (mhp < 1) mhp = 1;
+
+ if (p_ptr->body_monster)
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ u32b rhp = maxroll(r_ptr->hdice, r_ptr->hside);
+
+ /* Adjust the hp with the possession skill */
+ rhp = (rhp * (20 + get_skill_scale(SKILL_POSSESSION, 80))) / 100;
+
+ mhp = (rhp + sroot(rhp) + mhp) / 3;
+ }
+ if (p_ptr->disembodied) mhp = 1;
+
+ /* HACK - being undead means less DP */
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ int divisor = p_ptr->lev / 4;
+
+ /* Beware of the horrible division by zero ! :) */
+ if (divisor == 0) divisor = 1;
+
+ /* Actually decrease the max hp */
+ mhp /= divisor;
+
+ /* Never less than 1 */
+ if (mhp < 1) mhp = 1;
+ }
+
+ /* Hp mods? */
+ if (process_hooks_ret(HOOK_CALC_HP, "d", "(d)", mhp))
+ {
+ mhp = process_hooks_return[0].num;
+ }
+
+ /* Never less than 1 */
+ if (mhp < 1) mhp = 1;
+
+ /* New maximum hitpoints */
+ if (p_ptr->mhp != mhp)
+ {
+ /* XXX XXX XXX New hitpoint maintenance */
+
+ /* Enforce maximum */
+ if (p_ptr->chp >= mhp)
+ {
+ p_ptr->chp = mhp;
+ p_ptr->chp_frac = 0;
+ }
+
+ /* Save the new max-hitpoints */
+ p_ptr->mhp = mhp;
+
+ /* Display hitpoints (later) */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+
+/*
+ * Extract and set the current "lite radius"
+ *
+ * SWD: Experimental modification: multiple light sources have additive effect.
+ *
+ */
+static void calc_torch(void)
+{
+ int i;
+ object_type *o_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Assume no light */
+ p_ptr->cur_lite = 0;
+
+ /* Loop through all wielded items */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty slots */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* does this item glow? */
+ if (((f4 & TR4_FUEL_LITE) && (o_ptr->timeout > 0)) || (!(f4 & TR4_FUEL_LITE)))
+ {
+ if (f3 & TR3_LITE1) p_ptr->cur_lite++;
+ if (f4 & TR4_LITE2) p_ptr->cur_lite += 2;
+ if (f4 & TR4_LITE3) p_ptr->cur_lite += 3;
+ }
+
+ }
+
+ if (p_ptr->tim_lite) p_ptr->cur_lite += 2;
+
+ if (p_ptr->holy) p_ptr->cur_lite += 1;
+
+ /* max radius is 5 without rewriting other code -- */
+ /* see cave.c:update_lite() and defines.h:LITE_MAX */
+ if (p_ptr->cur_lite > 5) p_ptr->cur_lite = 5;
+
+ /* check if the player doesn't have a lite source, */
+ /* but does glow as an intrinsic. */
+ if (p_ptr->cur_lite == 0 && p_ptr->lite) p_ptr->cur_lite = 1;
+
+ /* Hooked powers */
+ process_hooks(HOOK_CALC_LITE, "()");
+
+ /* end experimental mods */
+
+ /* Reduce lite in the small-scale wilderness map */
+ if (p_ptr->wild_mode)
+ {
+ /* Reduce the lite radius if needed */
+ if (p_ptr->cur_lite > WILDERNESS_SEE_RADIUS)
+ {
+ p_ptr->cur_lite = WILDERNESS_SEE_RADIUS;
+ }
+ }
+
+
+ /* Reduce lite when running if requested */
+ if (running && view_reduce_lite)
+ {
+ /* Reduce the lite radius if needed */
+ if (p_ptr->cur_lite > 1) p_ptr->cur_lite = 1;
+ }
+
+ /* Notice changes in the "lite radius" */
+ if (p_ptr->old_lite != p_ptr->cur_lite)
+ {
+ /* Update the view */
+ p_ptr->update |= (PU_VIEW);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Remember the old lite */
+ p_ptr->old_lite = p_ptr->cur_lite;
+ }
+}
+
+
+
+/*
+ * Computes current weight limit.
+ */
+int weight_limit(void)
+{
+ int i;
+
+ /* Weight limit based only on strength */
+ i = adj_str_wgt[p_ptr->stat_ind[A_STR]] * 100;
+
+ if (process_hooks_ret(HOOK_CALC_WEIGHT, "d", "(d)", i))
+ i = process_hooks_return[0].num;
+
+ /* Return the result */
+ return (i);
+}
+
+void calc_wield_monster()
+{
+ object_type *o_ptr;
+ monster_race *r_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (o_ptr->k_idx)
+ {
+ r_ptr = &r_info[o_ptr->pval];
+
+ if (r_ptr->flags2 & RF2_INVISIBLE)
+ p_ptr->invis += 20;
+ if (r_ptr->flags2 & RF2_REFLECTING)
+ p_ptr->reflect = TRUE;
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ p_ptr->ffall = TRUE;
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ p_ptr->water_breath = TRUE;
+ }
+}
+
+/*
+ * Calc which body parts the player have, based on the
+ * monster he incarnate, note that that's bnot a hack
+ * since body parts of the player when in it's own body
+ * are also defined in r_info(monster 0)
+ */
+void calc_body()
+{
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ int i, b_weapon, b_legs, b_arms;
+ byte *body_parts, bp[BODY_MAX];
+
+ if (!p_ptr->body_monster)
+ {
+ body_parts = bp;
+ for (i = 0; i < BODY_MAX; i++)
+ {
+ int b;
+
+ b = rp_ptr->body_parts[i] + rmp_ptr->body_parts[i];
+ if (b < 0) b = 0;
+
+ if (p_ptr->mimic_form == resolve_mimic_name("Bear"))
+ {
+ if (i == BODY_ARMS) b = 0;
+ else if (i == BODY_LEGS) b = 0;
+ }
+
+ bp[i] = b;
+ }
+ }
+ else
+ {
+ body_parts = bp;
+ for (i = 0; i < BODY_MAX; i++)
+ {
+ int b;
+
+ b = r_ptr->body_parts[i];
+ if (b < 0) b = 0;
+
+ bp[i] = b;
+ }
+ }
+
+ for (i = 0; i < BODY_MAX; i++)
+ {
+ int b;
+
+ b = bp[i] + cp_ptr->body_parts[i];
+ if (b < 0) b = 0;
+ if (b > max_body_part[i]) b = max_body_part[i];
+
+ bp[i] = b;
+ }
+
+ b_weapon = body_parts[BODY_WEAPON];
+ b_arms = body_parts[BODY_ARMS];
+ b_legs = body_parts[BODY_LEGS];
+
+ if (p_ptr->mimic_extra & CLASS_ARMS)
+ {
+ b_weapon++;
+ b_arms++;
+
+ if (b_weapon > 3) b_weapon = 3;
+ if (b_arms > 3) b_arms = 3;
+ }
+
+ if (p_ptr->mimic_extra & CLASS_LEGS)
+ {
+ b_legs++;
+
+ if (b_legs > 2) b_legs = 2;
+ }
+
+ for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++)
+ p_ptr->body_parts[i] = 0;
+
+ for (i = 0; i < b_weapon; i++)
+ p_ptr->body_parts[INVEN_WIELD - INVEN_WIELD + i] = INVEN_WIELD;
+ if (body_parts[BODY_WEAPON])
+ p_ptr->body_parts[INVEN_BOW - INVEN_WIELD] = INVEN_BOW;
+
+ for (i = 0; i < body_parts[BODY_TORSO]; i++)
+ {
+ p_ptr->body_parts[INVEN_BODY - INVEN_WIELD + i] = INVEN_BODY;
+ p_ptr->body_parts[INVEN_OUTER - INVEN_WIELD + i] = INVEN_OUTER;
+ p_ptr->body_parts[INVEN_LITE - INVEN_WIELD + i] = INVEN_LITE;
+ p_ptr->body_parts[INVEN_AMMO - INVEN_WIELD + i] = INVEN_AMMO;
+ p_ptr->body_parts[INVEN_CARRY - INVEN_WIELD + i] = INVEN_CARRY;
+ }
+
+ for (i = 0; i < body_parts[BODY_FINGER]; i++)
+ p_ptr->body_parts[INVEN_RING - INVEN_WIELD + i] = INVEN_RING;
+
+ for (i = 0; i < body_parts[BODY_HEAD]; i++)
+ {
+ p_ptr->body_parts[INVEN_HEAD - INVEN_WIELD + i] = INVEN_HEAD;
+ p_ptr->body_parts[INVEN_NECK - INVEN_WIELD + i] = INVEN_NECK;
+ }
+
+ for (i = 0; i < b_arms; i++)
+ {
+ p_ptr->body_parts[INVEN_ARM - INVEN_WIELD + i] = INVEN_ARM;
+ p_ptr->body_parts[INVEN_HANDS - INVEN_WIELD + i] = INVEN_HANDS;
+ }
+ if (body_parts[BODY_ARMS])
+ p_ptr->body_parts[INVEN_TOOL - INVEN_WIELD] = INVEN_TOOL;
+
+ for (i = 0; i < b_legs; i++)
+ p_ptr->body_parts[INVEN_FEET - INVEN_WIELD + i] = INVEN_FEET;
+
+ /* Ok now if the player lost a body part, he must drop the object he had on it */
+ for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++)
+ {
+ if ((!p_ptr->body_parts[i]) && (p_ptr->inventory[i + INVEN_WIELD].k_idx))
+ {
+ /* Drop it NOW ! */
+ inven_takeoff(i + INVEN_WIELD, 255, TRUE);
+ }
+ }
+}
+
+/* Should be called by every calc_bonus call */
+void calc_body_bonus()
+{
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ /* If in the player body nothing have to be done */
+ if (!p_ptr->body_monster) return;
+
+ if (p_ptr->disembodied)
+ {
+ p_ptr->wraith_form = TRUE;
+ return;
+ }
+
+ p_ptr->ac += r_ptr->ac;
+ p_ptr->pspeed = r_ptr->speed;
+
+ if (r_ptr->flags1 & RF1_NEVER_MOVE) p_ptr->immovable = TRUE;
+ if (r_ptr->flags2 & RF2_STUPID) p_ptr->stat_add[A_INT] -= 1;
+ if (r_ptr->flags2 & RF2_SMART) p_ptr->stat_add[A_INT] += 1;
+ if (r_ptr->flags2 & RF2_REFLECTING) p_ptr->reflect = TRUE;
+ if (r_ptr->flags2 & RF2_INVISIBLE) p_ptr->invis += 20;
+ if (r_ptr->flags2 & RF2_REGENERATE) p_ptr->regenerate = TRUE;
+ if (r_ptr->flags2 & RF2_AURA_FIRE) p_ptr->sh_fire = TRUE;
+ if (r_ptr->flags2 & RF2_AURA_ELEC) p_ptr->sh_elec = TRUE;
+ if (r_ptr->flags2 & RF2_PASS_WALL) p_ptr->wraith_form = TRUE;
+ if (r_ptr->flags3 & RF3_SUSCEP_FIRE) p_ptr->sensible_fire = TRUE;
+ if (r_ptr->flags3 & RF3_IM_ACID) p_ptr->resist_acid = TRUE;
+ if (r_ptr->flags3 & RF3_IM_ELEC) p_ptr->resist_elec = TRUE;
+ if (r_ptr->flags3 & RF3_IM_FIRE) p_ptr->resist_fire = TRUE;
+ if (r_ptr->flags3 & RF3_IM_POIS) p_ptr->resist_pois = TRUE;
+ if (r_ptr->flags3 & RF3_IM_COLD) p_ptr->resist_cold = TRUE;
+ if (r_ptr->flags3 & RF3_RES_NETH) p_ptr->resist_neth = TRUE;
+ if (r_ptr->flags3 & RF3_RES_NEXU) p_ptr->resist_nexus = TRUE;
+ if (r_ptr->flags3 & RF3_RES_DISE) p_ptr->resist_disen = TRUE;
+ if (r_ptr->flags3 & RF3_NO_FEAR) p_ptr->resist_fear = TRUE;
+ if (r_ptr->flags3 & RF3_NO_SLEEP) p_ptr->free_act = TRUE;
+ if (r_ptr->flags3 & RF3_NO_CONF) p_ptr->resist_conf = TRUE;
+ if (r_ptr->flags7 & RF7_CAN_FLY) p_ptr->ffall = TRUE;
+ if (r_ptr->flags7 & RF7_AQUATIC) p_ptr->water_breath = TRUE;
+}
+
+
+byte calc_mimic()
+{
+ s32b blow = 0;
+
+ call_lua("calc_mimic", "(d)", "d", p_ptr->mimic_form, &blow);
+ return blow;
+}
+
+/* Returns the number of extra blows based on abilities. */
+static int get_extra_blows_ability() {
+ /* Count bonus abilities */
+ int num = 0;
+ if (has_ability(AB_MAX_BLOW1)) num++;
+ if (has_ability(AB_MAX_BLOW2)) num++;
+ return num;
+}
+
+/* Returns the blow information based on class */
+void analyze_blow(int *num, int *wgt, int *mul)
+{
+ *num = cp_ptr->blow_num;
+ *wgt = cp_ptr->blow_wgt;
+ *mul = cp_ptr->blow_mul;
+
+ /* Count bonus abilities */
+ (*num) += get_extra_blows_ability();
+}
+
+/* Are all the weapons wielded of the right type ? */
+int get_weaponmastery_skill()
+{
+ int i, skill = 0;
+ object_type *o_ptr;
+
+ i = 0;
+ /* All weapons must be of the same type */
+ while ((p_ptr->body_parts[i] == INVEN_WIELD) && (i < INVEN_TOTAL))
+ {
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ if (!o_ptr->k_idx)
+ {
+ i++;
+ continue;
+ }
+ switch (o_ptr->tval)
+ {
+ case TV_DAEMON_BOOK:
+ case TV_SWORD:
+ if ((!skill) || (skill == SKILL_SWORD)) skill = SKILL_SWORD;
+ else skill = -1;
+ break;
+ case TV_AXE:
+ if ((!skill) || (skill == SKILL_AXE)) skill = SKILL_AXE;
+ else skill = -1;
+ break;
+ case TV_HAFTED:
+ if ((!skill) || (skill == SKILL_HAFTED)) skill = SKILL_HAFTED;
+ else skill = -1;
+ break;
+ case TV_POLEARM:
+ if ((!skill) || (skill == SKILL_POLEARM)) skill = SKILL_POLEARM;
+ else skill = -1;
+ break;
+ }
+ i++;
+ }
+
+ /* Everything is ok */
+ return skill;
+}
+
+/* Are all the ranged weapons wielded of the right type ? */
+int get_archery_skill()
+{
+ int i, skill = 0;
+
+ i = INVEN_BOW - INVEN_WIELD;
+ /* All weapons must be of the same type */
+ while (p_ptr->body_parts[i] == INVEN_BOW)
+ {
+ if (p_ptr->inventory[INVEN_WIELD + i].tval == TV_BOW)
+ {
+ switch (p_ptr->inventory[INVEN_WIELD + i].sval / 10)
+ {
+ case 0:
+ if ((!skill) || (skill == SKILL_SLING)) skill = SKILL_SLING;
+ else skill = -1;
+ break;
+ case 1:
+ if ((!skill) || (skill == SKILL_BOW)) skill = SKILL_BOW;
+ else skill = -1;
+ break;
+ case 2:
+ if ((!skill) || (skill == SKILL_XBOW)) skill = SKILL_XBOW;
+ else skill = -1;
+ break;
+ }
+ }
+ else
+ {
+ if ((!skill) || (skill == SKILL_BOOMERANG)) skill = SKILL_BOOMERANG;
+ else skill = -1;
+ }
+
+ i++;
+ }
+
+ /* Everything is ok */
+ return skill;
+}
+
+/* Apply gods */
+void calc_gods()
+{
+ /* Boost WIS if the player follows Eru */
+ GOD(GOD_ERU)
+ {
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_WIS] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_WIS] += 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_WIS] += 1;
+ }
+
+ /* Boost str, con, chr and reduce int, wis if the player follows Melkor */
+ GOD(GOD_MELKOR)
+ {
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_STR] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_STR] += 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_STR] += 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_CON] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_CON] += 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_CON] += 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_CHR] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_CHR] += 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_CHR] += 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_INT] -= 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_INT] -= 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_INT] -= 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_WIS] -= 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_WIS] -= 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_WIS] -= 1;
+
+ PRAY_GOD(GOD_MELKOR)
+ {
+ if (p_ptr->grace > 5000) p_ptr->invis += 30;
+ if (p_ptr->grace > 15000) p_ptr->immune_fire = TRUE;
+ }
+ p_ptr->resist_fire = TRUE;
+ }
+
+ /* Gifts of Manwe if the player is praying to Manwe */
+ PRAY_GOD(GOD_MANWE)
+ {
+ s32b add = p_ptr->grace;
+
+ /* provides speed every 5000 grace */
+ if (add > 35000) add = 35000;
+ add /= 5000;
+ p_ptr->pspeed += add;
+
+ /* Provides fly & FA */
+ if (p_ptr->grace >= 7000) p_ptr->free_act = TRUE;
+ if (p_ptr->grace >= 15000) p_ptr->fly = TRUE;
+ }
+
+ /* Manwe bonus not requiring the praying status */
+ GOD(GOD_MANWE)
+ {
+ if (p_ptr->grace >= 2000) p_ptr->ffall = TRUE;
+ }
+
+ /* Boost Str and Con if the player is following Tulkas */
+ GOD(GOD_TULKAS)
+ {
+ if (p_ptr->grace > 5000) p_ptr->stat_add[A_CON] += 1;
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_CON] += 1;
+ if (p_ptr->grace > 15000) p_ptr->stat_add[A_CON] += 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_STR] += 1;
+ if (p_ptr->grace > 15000) p_ptr->stat_add[A_STR] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_STR] += 1;
+ }
+}
+
+/* Apply flags */
+static int extra_blows;
+static int extra_shots;
+void apply_flags(u32b f1, u32b f2, u32b f3, u32b f4, u32b f5, u32b esp, s16b pval, s16b tval, s16b to_h, s16b to_d, s16b to_a)
+{
+ s16b antimagic_mod;
+
+ /* Affect stats */
+ if (f1 & (TR1_STR)) p_ptr->stat_add[A_STR] += pval;
+ if (f1 & (TR1_INT)) p_ptr->stat_add[A_INT] += pval;
+ if (f1 & (TR1_WIS)) p_ptr->stat_add[A_WIS] += pval;
+ if (f1 & (TR1_DEX)) p_ptr->stat_add[A_DEX] += pval;
+ if (f1 & (TR1_CON)) p_ptr->stat_add[A_CON] += pval;
+ if (f1 & (TR1_CHR)) p_ptr->stat_add[A_CHR] += pval;
+ if (f5 & (TR5_LUCK)) p_ptr->luck_cur += pval;
+
+ /* Affect spell power */
+ if (f1 & (TR1_SPELL)) p_ptr->to_s += pval;
+
+ /* Affect mana capacity */
+ if (f1 & (TR1_MANA)) p_ptr->to_m += pval;
+
+ /* Affect life capacity */
+ if (f2 & (TR2_LIFE)) p_ptr->to_l += pval;
+
+ /* Affect stealth */
+ if (f1 & (TR1_STEALTH)) p_ptr->skill_stl += pval;
+
+ /* Affect searching ability (factor of five) */
+ if (f1 & (TR1_SEARCH)) p_ptr->skill_srh += (pval * 5);
+
+ /* Affect searching frequency (factor of five) */
+ if (f1 & (TR1_SEARCH)) p_ptr->skill_fos += (pval * 5);
+
+ /* Affect infravision */
+ if (f1 & (TR1_INFRA)) p_ptr->see_infra += pval;
+
+ /* Affect digging (factor of 20) */
+ if (f1 & (TR1_TUNNEL)) p_ptr->skill_dig += (pval * 20);
+
+ /* Affect speed */
+ if (f1 & (TR1_SPEED)) p_ptr->pspeed += pval;
+
+ /* Affect blows */
+ if (f1 & (TR1_BLOWS)) extra_blows += pval;
+ if (f5 & (TR5_CRIT)) p_ptr->xtra_crit += pval;
+
+ /* Hack -- Sensible fire */
+ if (f2 & (TR2_SENS_FIRE)) p_ptr->sensible_fire = TRUE;
+
+ /* Hack -- cause earthquakes */
+ if (f1 & (TR1_IMPACT)) p_ptr->impact = TRUE;
+
+ /* Affect invisibility */
+ if (f2 & (TR2_INVIS)) p_ptr->invis += (pval * 10);
+
+ /* Boost shots */
+ if (f3 & (TR3_XTRA_SHOTS)) extra_shots++;
+
+ /* Various flags */
+ if (f3 & (TR3_AGGRAVATE)) p_ptr->aggravate = TRUE;
+ if (f3 & (TR3_TELEPORT)) p_ptr->teleport = TRUE;
+ if (f5 & (TR5_DRAIN_MANA)) p_ptr->drain_mana++;
+ if (f5 & (TR5_DRAIN_HP)) p_ptr->drain_life++;
+ if (f3 & (TR3_DRAIN_EXP)) p_ptr->exp_drain = TRUE;
+ if (f3 & (TR3_BLESSED)) p_ptr->bless_blade = TRUE;
+ if (f3 & (TR3_XTRA_MIGHT)) p_ptr->xtra_might += pval;
+ if (f3 & (TR3_SLOW_DIGEST)) p_ptr->slow_digest = TRUE;
+ if (f3 & (TR3_REGEN)) p_ptr->regenerate = TRUE;
+ if (esp) p_ptr->telepathy |= esp;
+ if ((tval != TV_LITE) && (f3 & (TR3_LITE1))) p_ptr->lite = TRUE;
+ if ((tval != TV_LITE) && (f4 & (TR4_LITE2))) p_ptr->lite = TRUE;
+ if ((tval != TV_LITE) && (f4 & (TR4_LITE3))) p_ptr->lite = TRUE;
+ if (f3 & (TR3_SEE_INVIS)) p_ptr->see_inv = TRUE;
+ if (f2 & (TR2_FREE_ACT)) p_ptr->free_act = TRUE;
+ if (f2 & (TR2_HOLD_LIFE)) p_ptr->hold_life = TRUE;
+ if (f3 & (TR3_WRAITH)) p_ptr->wraith_form = TRUE;
+ if (f3 & (TR3_FEATHER)) p_ptr->ffall = TRUE;
+ if (f4 & (TR4_FLY)) p_ptr->fly = TRUE;
+ if (f4 & (TR4_CLIMB)) p_ptr->climb = TRUE;
+
+ /* Immunity flags */
+ if (f2 & (TR2_IM_FIRE)) p_ptr->immune_fire = TRUE;
+ if (f2 & (TR2_IM_ACID)) p_ptr->immune_acid = TRUE;
+ if (f2 & (TR2_IM_COLD)) p_ptr->immune_cold = TRUE;
+ if (f2 & (TR2_IM_ELEC)) p_ptr->immune_elec = TRUE;
+
+ /* Resistance flags */
+ if (f2 & (TR2_RES_ACID)) p_ptr->resist_acid = TRUE;
+ if (f2 & (TR2_RES_ELEC)) p_ptr->resist_elec = TRUE;
+ if (f2 & (TR2_RES_FIRE)) p_ptr->resist_fire = TRUE;
+ if (f2 & (TR2_RES_COLD)) p_ptr->resist_cold = TRUE;
+ if (f2 & (TR2_RES_POIS)) p_ptr->resist_pois = TRUE;
+ if (f2 & (TR2_RES_FEAR)) p_ptr->resist_fear = TRUE;
+ if (f2 & (TR2_RES_CONF)) p_ptr->resist_conf = TRUE;
+ if (f2 & (TR2_RES_SOUND)) p_ptr->resist_sound = TRUE;
+ if (f2 & (TR2_RES_LITE)) p_ptr->resist_lite = TRUE;
+ if (f2 & (TR2_RES_DARK)) p_ptr->resist_dark = TRUE;
+ if (f2 & (TR2_RES_CHAOS)) p_ptr->resist_chaos = TRUE;
+ if (f2 & (TR2_RES_DISEN)) p_ptr->resist_disen = TRUE;
+ if (f2 & (TR2_RES_SHARDS)) p_ptr->resist_shard = TRUE;
+ if (f2 & (TR2_RES_NEXUS)) p_ptr->resist_nexus = TRUE;
+ if (f2 & (TR2_RES_BLIND)) p_ptr->resist_blind = TRUE;
+ if (f2 & (TR2_RES_NETHER)) p_ptr->resist_neth = TRUE;
+ if (f4 & (TR4_IM_NETHER)) p_ptr->immune_neth = TRUE;
+
+ if (f2 & (TR2_REFLECT)) p_ptr->reflect = TRUE;
+ if (f3 & (TR3_SH_FIRE)) p_ptr->sh_fire = TRUE;
+ if (f3 & (TR3_SH_ELEC)) p_ptr->sh_elec = TRUE;
+ if (f3 & (TR3_NO_MAGIC)) p_ptr->anti_magic = TRUE;
+ if (f3 & (TR3_NO_TELE)) p_ptr->anti_tele = TRUE;
+
+ /* Sustain flags */
+ if (f2 & (TR2_SUST_STR)) p_ptr->sustain_str = TRUE;
+ if (f2 & (TR2_SUST_INT)) p_ptr->sustain_int = TRUE;
+ if (f2 & (TR2_SUST_WIS)) p_ptr->sustain_wis = TRUE;
+ if (f2 & (TR2_SUST_DEX)) p_ptr->sustain_dex = TRUE;
+ if (f2 & (TR2_SUST_CON)) p_ptr->sustain_con = TRUE;
+ if (f2 & (TR2_SUST_CHR)) p_ptr->sustain_chr = TRUE;
+
+ if (f4 & (TR4_PRECOGNITION)) p_ptr->precognition = TRUE;
+
+ antimagic_mod = to_h + to_d + to_a;
+
+ if (f4 & (TR4_ANTIMAGIC_50))
+ {
+ s32b tmp;
+
+ tmp = 10 + get_skill_scale(SKILL_ANTIMAGIC, 40) - antimagic_mod;
+ if (tmp > 0) p_ptr->antimagic += tmp;
+
+ tmp = 1 + get_skill_scale(SKILL_ANTIMAGIC, 4) - antimagic_mod / 15;
+ if (tmp > 0) p_ptr->antimagic_dis += tmp;
+ }
+
+ if (f4 & (TR4_ANTIMAGIC_30))
+ {
+ s32b tmp;
+
+ tmp = 7 + get_skill_scale(SKILL_ANTIMAGIC, 33) - antimagic_mod;
+ if (tmp > 0) p_ptr->antimagic += tmp;
+
+ tmp = 1 + get_skill_scale(SKILL_ANTIMAGIC, 2) - antimagic_mod / 15;
+ if (tmp > 0) p_ptr->antimagic_dis += tmp;
+ }
+
+ if (f4 & (TR4_ANTIMAGIC_20))
+ {
+ s32b tmp;
+
+ tmp = 5 + get_skill_scale(SKILL_ANTIMAGIC, 15) - antimagic_mod;
+ if (tmp > 0) p_ptr->antimagic += tmp;
+
+ p_ptr->antimagic_dis += 2;
+ }
+
+ if (f4 & (TR4_ANTIMAGIC_10))
+ {
+ s32b tmp;
+
+ tmp = 1 + get_skill_scale(SKILL_ANTIMAGIC, 9) - antimagic_mod;
+ if (tmp > 0) p_ptr->antimagic += tmp;
+
+ p_ptr->antimagic_dis += 1;
+ }
+
+ if (f4 & (TR4_AUTO_ID))
+ {
+ p_ptr->auto_id = TRUE;
+ }
+
+ /* The new code implementing Tolkien's concept of "Black Breath"
+ * takes advantage of the existing drain_exp character flag, renamed
+ * "black_breath". This flag can also be set by a unlucky blow from
+ * an undead. -LM-
+ */
+ if (f4 & (TR4_BLACK_BREATH)) p_ptr->black_breath = TRUE;
+
+ if (f5 & (TR5_IMMOVABLE)) p_ptr->immovable = TRUE;
+
+ /* Breaths */
+ if (f5 & (TR5_WATER_BREATH)) p_ptr->water_breath = TRUE;
+ if (f5 & (TR5_MAGIC_BREATH))
+ {
+ p_ptr->magical_breath = TRUE;
+ p_ptr->water_breath = TRUE;
+ }
+}
+
+/*
+ * Calculate the players current "state", taking into account
+ * not only race/class intrinsics, but also objects being worn
+ * and temporary spell effects.
+ *
+ * See also calc_mana() and calc_hitpoints().
+ *
+ * Take note of the new "speed code", in particular, a very strong
+ * player will start slowing down as soon as he reaches 150 pounds,
+ * but not until he reaches 450 pounds will he be half as fast as
+ * a normal kobold. This both hurts and helps the player, hurts
+ * because in the old days a player could just avoid 300 pounds,
+ * and helps because now carrying 300 pounds is not very painful.
+ *
+ * The "weapon" and "bow" do *not* add to the bonuses to hit or to
+ * damage, since that would affect non-combat things. These values
+ * are actually added in later, at the appropriate place.
+ *
+ * This function induces various "status" messages, unless silent is
+ * TRUE.
+ */
+void calc_bonuses(bool_ silent)
+{
+ int i, j, hold;
+ int old_speed;
+ u32b old_telepathy;
+ int old_see_inv;
+ int old_dis_ac;
+ int old_dis_to_a;
+ object_type *o_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Save the old speed */
+ old_speed = p_ptr->pspeed;
+
+ /* Save the old vision stuff */
+ old_telepathy = p_ptr->telepathy;
+ old_see_inv = p_ptr->see_inv;
+
+ /* Save the old armor class */
+ old_dis_ac = p_ptr->dis_ac;
+ old_dis_to_a = p_ptr->dis_to_a;
+
+ /* Clear extra blows/shots */
+ extra_blows = extra_shots = 0;
+
+ /* Clear the stat modifiers */
+ for (i = 0; i < 6; i++) p_ptr->stat_add[i] = 0;
+
+ /* Mana multiplier */
+ p_ptr->to_m = 0;
+
+ /* Life multiplier */
+ p_ptr->to_l = 0;
+
+ /* Spell power */
+ p_ptr->to_s = 0;
+
+ /* Clear the Displayed/Real armor class */
+ p_ptr->dis_ac = p_ptr->ac = 0;
+
+ /* Clear the Displayed/Real Bonuses */
+ p_ptr->dis_to_h = p_ptr->to_h = p_ptr->to_h_melee = p_ptr->to_h_ranged = 0;
+ p_ptr->dis_to_d = p_ptr->to_d = p_ptr->to_d_melee = p_ptr->to_d_ranged = 0;
+ p_ptr->dis_to_a = p_ptr->to_a = 0;
+
+ /* Start with "normal" speed */
+ p_ptr->pspeed = 110;
+
+ /* Start with 0% additionnal crits */
+ p_ptr->xtra_crit = 0;
+
+ /* Start with a single blow per turn */
+ p_ptr->num_blow = 1;
+
+ /* Start with a single shot per turn */
+ p_ptr->num_fire = 1;
+
+ /* Starts with single throwing damage */
+ p_ptr->throw_mult = 1;
+
+ /* Reset the "xtra" tval */
+ p_ptr->tval_xtra = 0;
+
+ /* Reset the "ammo" tval */
+ p_ptr->tval_ammo = 0;
+
+ /* Clear all the flags */
+ p_ptr->invis = 0;
+ p_ptr->immovable = FALSE;
+ p_ptr->aggravate = FALSE;
+ p_ptr->teleport = FALSE;
+ p_ptr->exp_drain = FALSE;
+ p_ptr->drain_mana = 0;
+ p_ptr->drain_life = 0;
+ p_ptr->bless_blade = FALSE;
+ p_ptr->xtra_might = 0;
+ p_ptr->auto_id = FALSE;
+ p_ptr->impact = FALSE;
+ p_ptr->see_inv = FALSE;
+ p_ptr->free_act = FALSE;
+ p_ptr->slow_digest = FALSE;
+ p_ptr->regenerate = FALSE;
+ p_ptr->fly = FALSE;
+ p_ptr->climb = FALSE;
+ p_ptr->ffall = FALSE;
+ p_ptr->hold_life = FALSE;
+ p_ptr->telepathy = 0;
+ p_ptr->lite = FALSE;
+ p_ptr->sustain_str = FALSE;
+ p_ptr->sustain_int = FALSE;
+ p_ptr->sustain_wis = FALSE;
+ p_ptr->sustain_con = FALSE;
+ p_ptr->sustain_dex = FALSE;
+ p_ptr->sustain_chr = FALSE;
+ p_ptr->resist_acid = FALSE;
+ p_ptr->resist_elec = FALSE;
+ p_ptr->resist_fire = FALSE;
+ p_ptr->resist_cold = FALSE;
+ p_ptr->resist_pois = FALSE;
+ p_ptr->resist_conf = FALSE;
+ p_ptr->resist_sound = FALSE;
+ p_ptr->resist_lite = FALSE;
+ p_ptr->resist_dark = FALSE;
+ p_ptr->resist_chaos = FALSE;
+ p_ptr->resist_disen = FALSE;
+ p_ptr->resist_shard = FALSE;
+ p_ptr->resist_nexus = FALSE;
+ p_ptr->resist_blind = FALSE;
+ p_ptr->resist_neth = FALSE;
+ p_ptr->immune_neth = FALSE;
+ p_ptr->resist_fear = FALSE;
+ p_ptr->resist_continuum = FALSE;
+ p_ptr->reflect = FALSE;
+ p_ptr->sh_fire = FALSE;
+ p_ptr->sh_elec = FALSE;
+ p_ptr->anti_magic = FALSE;
+ p_ptr->anti_tele = FALSE;
+ p_ptr->water_breath = FALSE;
+ p_ptr->magical_breath = FALSE;
+
+ p_ptr->sensible_fire = FALSE;
+ p_ptr->sensible_lite = FALSE;
+
+ p_ptr->immune_acid = FALSE;
+ p_ptr->immune_elec = FALSE;
+ p_ptr->immune_fire = FALSE;
+ p_ptr->immune_cold = FALSE;
+
+ p_ptr->precognition = FALSE;
+
+ p_ptr->wraith_form = FALSE;
+
+ /* The anti magic field surrounding the player */
+ p_ptr->antimagic = 0;
+ p_ptr->antimagic_dis = 0;
+
+
+ /* Base infravision (purely racial) */
+ p_ptr->see_infra = rp_ptr->infra + rmp_ptr->infra;
+
+
+ /* Base skill -- disarming */
+ p_ptr->skill_dis = 0;
+
+ /* Base skill -- magic devices */
+ p_ptr->skill_dev = 0;
+
+ /* Base skill -- saving throw */
+ p_ptr->skill_sav = 0;
+
+ /* Base skill -- stealth */
+ p_ptr->skill_stl = 0;
+
+ /* Base skill -- searching ability */
+ p_ptr->skill_srh = 0;
+
+ /* Base skill -- searching frequency */
+ p_ptr->skill_fos = 0;
+
+ /* Base skill -- combat (normal) */
+ p_ptr->skill_thn = 0;
+
+ /* Base skill -- combat (shooting) */
+ p_ptr->skill_thb = 0;
+
+ /* Base skill -- combat (throwing) */
+ p_ptr->skill_tht = 0;
+
+
+ /* Base skill -- digging */
+ p_ptr->skill_dig = 0;
+
+ /* Xtra player flags */
+ p_ptr->xtra_f1 = 0;
+ p_ptr->xtra_f2 = 0;
+ p_ptr->xtra_f3 = 0;
+ p_ptr->xtra_f4 = 0;
+ p_ptr->xtra_f5 = 0;
+ p_ptr->xtra_esp = 0;
+
+ /* Hide the skills that should auto hide */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ if (s_info[i].flags1 & SKF1_AUTO_HIDE)
+ s_info[i].hidden = TRUE;
+ }
+
+ /* Base Luck */
+ p_ptr->luck_cur = p_ptr->luck_base;
+
+ /* Mimic override body's bonuses */
+ if (p_ptr->mimic_form)
+ {
+ extra_blows += calc_mimic();
+ }
+ else
+ {
+ calc_body_bonus();
+ }
+
+ /* Let the scripts do what they need */
+ process_hooks(HOOK_CALC_BONUS, "()");
+
+ /* The powers gived by the wielded monster */
+ calc_wield_monster();
+
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ apply_flags(cp_ptr->oflags1[i], cp_ptr->oflags2[i], cp_ptr->oflags3[i], cp_ptr->oflags4[i], cp_ptr->oflags5[i], cp_ptr->oesp[i], cp_ptr->opval[i], 0, 0, 0, 0);
+ }
+
+ if (p_ptr->melee_style == SKILL_HAND)
+ {
+ /* Unencumbered Monks become faster every 10 levels */
+ if (!(monk_heavy_armor()))
+ p_ptr->pspeed += get_skill_scale(SKILL_HAND, 5);
+
+ /* Free action if unencumbered at level 25 */
+ if ((get_skill(SKILL_HAND) > 24) && !(monk_heavy_armor()))
+ p_ptr->free_act = TRUE;
+ }
+
+ if (get_skill(SKILL_ANTIMAGIC))
+ {
+ p_ptr->antimagic += get_skill(SKILL_ANTIMAGIC);
+ p_ptr->antimagic_dis += get_skill_scale(SKILL_ANTIMAGIC, 10) + 1;
+
+ if (p_ptr->antimagic_extra & CLASS_ANTIMAGIC)
+ {
+ p_ptr->anti_tele = TRUE;
+ p_ptr->resist_continuum = TRUE;
+ }
+ }
+
+ if (get_skill(SKILL_DAEMON) > 20) p_ptr->resist_conf = TRUE;
+ if (get_skill(SKILL_DAEMON) > 30) p_ptr->resist_fear = TRUE;
+
+ if ( get_skill(SKILL_MINDCRAFT) >= 40 ) p_ptr->telepathy = ESP_ALL;
+
+ if (p_ptr->astral)
+ {
+ p_ptr->wraith_form = TRUE;
+ }
+
+ /***** Races ****/
+ if ((!p_ptr->mimic_form) && (!p_ptr->body_monster))
+ {
+ int i;
+
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ apply_flags(rp_ptr->oflags1[i], rp_ptr->oflags2[i], rp_ptr->oflags3[i], rp_ptr->oflags4[i], rp_ptr->oflags5[i], rp_ptr->oesp[i], rp_ptr->opval[i], 0, 0, 0, 0);
+ apply_flags(rmp_ptr->oflags1[i], rmp_ptr->oflags2[i], rmp_ptr->oflags3[i], rmp_ptr->oflags4[i], rmp_ptr->oflags5[i], rmp_ptr->oesp[i], rmp_ptr->opval[i], 0, 0, 0, 0);
+ }
+
+ if (PRACE_FLAG(PR1_HURT_LITE))
+ p_ptr->sensible_lite = TRUE;
+ }
+
+ /* The extra flags */
+ apply_flags(p_ptr->xtra_f1, p_ptr->xtra_f2, p_ptr->xtra_f3, p_ptr->xtra_f4, p_ptr->xtra_f5, p_ptr->xtra_esp, 0, 0, 0, 0, 0);
+
+ /* Hack -- apply racial/class stat maxes */
+ if (p_ptr->maximize)
+ {
+ /* Apply the racial modifiers */
+ for (i = 0; i < 6; i++)
+ {
+ /* Modify the stats for "race" */
+ p_ptr->stat_add[i] += (rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i]);
+ }
+ }
+
+
+ /* Scan the usable inventory */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the item flags */
+ object_flags_no_set = TRUE;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ object_flags_no_set = FALSE;
+
+ /* MEGA ugly hack -- set spacetime distortion resistance */
+ if (o_ptr->name1 == ART_ANCHOR)
+ {
+ p_ptr->resist_continuum = TRUE;
+ }
+
+ /* Hack - don't give the Black Breath when merely inspecting a weapon */
+ if (silent)
+ {
+ f4 &= ~TR4_BLACK_BREATH;
+ }
+
+ apply_flags(f1, f2, f3, f4, f5, esp, o_ptr->pval, o_ptr->tval, o_ptr->to_h, o_ptr->to_d, o_ptr->to_a);
+
+ if (o_ptr->name1)
+ {
+ apply_set(o_ptr->name1, a_info[o_ptr->name1].set);
+ }
+
+ /* Modify the base armor class */
+ p_ptr->ac += o_ptr->ac;
+
+ /* The base armor class is always known */
+ p_ptr->dis_ac += o_ptr->ac;
+
+ /* Apply the bonuses to armor class */
+ p_ptr->to_a += o_ptr->to_a;
+
+ /* Apply the mental bonuses to armor class, if known */
+ if (object_known_p(o_ptr)) p_ptr->dis_to_a += o_ptr->to_a;
+
+ /* Hack -- do not apply "weapon" bonuses */
+ if (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_WIELD) continue;
+
+ /* Hack -- do not apply "bow" bonuses */
+ if (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_BOW) continue;
+
+ /* Hack -- do not apply "ammo" bonuses */
+ if (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_AMMO) continue;
+
+ /* Hack -- do not apply "tool" bonuses */
+ if (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_TOOL) continue;
+
+ /* Apply the bonuses to hit/damage */
+ p_ptr->to_h += o_ptr->to_h;
+ p_ptr->to_d += o_ptr->to_d;
+
+ /* Apply the mental bonuses tp hit/damage, if known */
+ if (object_known_p(o_ptr)) p_ptr->dis_to_h += o_ptr->to_h;
+ if (object_known_p(o_ptr)) p_ptr->dis_to_d += o_ptr->to_d;
+ }
+
+ /* Monks get extra ac for armour _not worn_ */
+ if ((p_ptr->melee_style == SKILL_HAND) && !(monk_heavy_armor()))
+ {
+ if (!(p_ptr->inventory[INVEN_BODY].k_idx))
+ {
+ p_ptr->to_a += get_skill_scale(SKILL_HAND, 75);
+ p_ptr->dis_to_a += get_skill_scale(SKILL_HAND, 75);
+ }
+ if (!(p_ptr->inventory[INVEN_OUTER].k_idx) && (get_skill(SKILL_HAND) > 15))
+ {
+ p_ptr->to_a += ((get_skill(SKILL_HAND) - 13) / 3);
+ p_ptr->dis_to_a += ((get_skill(SKILL_HAND) - 13) / 3);
+ }
+ if (!(p_ptr->inventory[INVEN_ARM].k_idx) && (get_skill(SKILL_HAND) > 10))
+ {
+ p_ptr->to_a += ((get_skill(SKILL_HAND) - 8) / 3);
+ p_ptr->dis_to_a += ((get_skill(SKILL_HAND) - 8) / 3);
+ }
+ if (!(p_ptr->inventory[INVEN_HEAD].k_idx) && (get_skill(SKILL_HAND) > 4))
+ {
+ p_ptr->to_a += (get_skill(SKILL_HAND) - 2) / 3;
+ p_ptr->dis_to_a += (get_skill(SKILL_HAND) - 2) / 3;
+ }
+ if (!(p_ptr->inventory[INVEN_HANDS].k_idx))
+ {
+ p_ptr->to_a += (get_skill(SKILL_HAND) / 2);
+ p_ptr->dis_to_a += (get_skill(SKILL_HAND) / 2);
+ }
+ if (!(p_ptr->inventory[INVEN_FEET].k_idx))
+ {
+ p_ptr->to_a += (get_skill(SKILL_HAND) / 3);
+ p_ptr->dis_to_a += (get_skill(SKILL_HAND) / 3);
+ }
+ }
+
+ /* Hack -- aura of fire also provides light */
+ if (p_ptr->sh_fire) p_ptr->lite = TRUE;
+
+ if (PRACE_FLAG(PR1_AC_LEVEL))
+ {
+ p_ptr->to_a += 20 + (p_ptr->lev / 5);
+ p_ptr->dis_to_a += 20 + (p_ptr->lev / 5);
+ }
+
+ /* Take care of gods */
+ calc_gods();
+
+ /* Calculate stats */
+ for (i = 0; i < 6; i++)
+ {
+ int top, use, ind;
+
+
+ /* Extract the new "stat_use" value for the stat */
+ top = modify_stat_value(p_ptr->stat_max[i], p_ptr->stat_add[i]);
+
+ /* Notice changes */
+ if (p_ptr->stat_top[i] != top)
+ {
+ /* Save the new value */
+ p_ptr->stat_top[i] = top;
+
+ /* Redisplay the stats later */
+ p_ptr->redraw |= (PR_STATS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ /* Extract the new "stat_use" value for the stat */
+ use = modify_stat_value(p_ptr->stat_cur[i], p_ptr->stat_add[i]);
+
+ /* Notice changes */
+ if (p_ptr->stat_use[i] != use)
+ {
+ /* Save the new value */
+ p_ptr->stat_use[i] = use;
+
+ /* Redisplay the stats later */
+ p_ptr->redraw |= (PR_STATS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+
+ /* Values: 3, 4, ..., 17 */
+ if (use <= 18) ind = (use - 3);
+
+ /* Ranges: 18/00-18/09, ..., 18/210-18/219 */
+ else if (use <= 18 + 219) ind = (15 + (use - 18) / 10);
+
+ /* Range: 18/220+ */
+ else ind = (37);
+
+ /* Notice changes */
+ if (p_ptr->stat_ind[i] != ind)
+ {
+ /* Save the new index */
+ p_ptr->stat_ind[i] = ind;
+
+ /* Change in CON affects Hitpoints */
+ if (i == A_CON)
+ {
+ p_ptr->update |= (PU_HP);
+ }
+
+ /* Change in WIS affects Sanity Points */
+ else if (i == A_WIS)
+ {
+ p_ptr->update |= (PU_MANA | PU_SANITY);
+ }
+
+ /* Change in spell stat affects Mana/Spells */
+ if (i == A_INT)
+ {
+ p_ptr->update |= (PU_MANA | PU_SPELLS);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ }
+
+ /* Provide the damage we get from sacrifice */
+ GOD(GOD_MELKOR)
+ {
+ int x = wisdom_scale(4);
+ if (x < 1) x = 1;
+
+ p_ptr->dis_to_d += x * p_ptr->melkor_sacrifice;
+ p_ptr->to_d += x * p_ptr->melkor_sacrifice;
+ }
+
+ /* jk - add in the tactics */
+ p_ptr->dis_to_h += tactic_info[(byte)p_ptr->tactic].to_hit;
+ p_ptr->to_h += tactic_info[(byte)p_ptr->tactic].to_hit;
+ p_ptr->dis_to_d += tactic_info[(byte)p_ptr->tactic].to_dam;
+ p_ptr->to_d += tactic_info[(byte)p_ptr->tactic].to_dam;
+ p_ptr->dis_to_a += tactic_info[(byte)p_ptr->tactic].to_ac;
+ p_ptr->to_a += tactic_info[(byte)p_ptr->tactic].to_ac;
+
+ p_ptr->skill_stl += tactic_info[(byte)p_ptr->tactic].to_stealth;
+ p_ptr->skill_dis += tactic_info[(byte)p_ptr->tactic].to_disarm;
+ p_ptr->skill_sav += tactic_info[(byte)p_ptr->tactic].to_saving;
+
+ p_ptr->pspeed += move_info[(byte)p_ptr->movement].to_speed;
+ p_ptr->skill_srh += move_info[(byte)p_ptr->movement].to_search;
+ p_ptr->skill_fos += move_info[(byte)p_ptr->movement].to_percep;
+ p_ptr->skill_stl += move_info[(byte)p_ptr->movement].to_stealth;
+
+ /* Apply temporary "stun" */
+ if (p_ptr->stun > 50)
+ {
+ p_ptr->to_h -= 20;
+ p_ptr->dis_to_h -= 20;
+ p_ptr->to_d -= 20;
+ p_ptr->dis_to_d -= 20;
+ }
+ else if (p_ptr->stun)
+ {
+ p_ptr->to_h -= 5;
+ p_ptr->dis_to_h -= 5;
+ p_ptr->to_d -= 5;
+ p_ptr->dis_to_d -= 5;
+ }
+
+
+ /* Invulnerability */
+ if (p_ptr->invuln)
+ {
+ p_ptr->to_a += 100;
+ p_ptr->dis_to_a += 100;
+ }
+
+ /* Breath */
+ if (p_ptr->tim_water_breath)
+ {
+ p_ptr->water_breath = TRUE;
+ }
+ if (p_ptr->tim_magic_breath)
+ {
+ p_ptr->magical_breath = TRUE;
+ }
+
+ /* wraith_form */
+ if (p_ptr->tim_wraith)
+ {
+ if (p_ptr->disembodied)
+ {
+ p_ptr->to_a += 10;
+ p_ptr->dis_to_a += 10;
+ }
+ else
+ {
+ p_ptr->to_a += 50;
+ p_ptr->dis_to_a += 50;
+ p_ptr->reflect = TRUE;
+ }
+ p_ptr->wraith_form = TRUE;
+ }
+
+ /* Temporary holy aura */
+ if (p_ptr->holy)
+ {
+ p_ptr->hold_life = TRUE;
+ p_ptr->luck_cur += 5;
+ }
+
+ /* Temporary blessing */
+ if (p_ptr->blessed)
+ {
+ p_ptr->to_a += 5;
+ p_ptr->dis_to_a += 5;
+ p_ptr->to_h += 10;
+ p_ptr->dis_to_h += 10;
+ }
+
+ /* Temporary invisibility */
+ if (p_ptr->tim_invisible)
+ {
+ p_ptr->invis += p_ptr->tim_inv_pow;
+ }
+
+ /* Temporary shield */
+ if (p_ptr->shield)
+ {
+ p_ptr->to_a += p_ptr->shield_power;
+ p_ptr->dis_to_a += p_ptr->shield_power;
+ }
+
+ /* Temporary "Hero" */
+ if (p_ptr->hero)
+ {
+ p_ptr->to_h += 12;
+ p_ptr->dis_to_h += 12;
+ }
+
+ /* Temporary "roots" */
+ if (p_ptr->tim_roots)
+ {
+ set_stun(0);
+ p_ptr->to_d_melee += p_ptr->tim_roots_dam;
+ p_ptr->to_a += p_ptr->tim_roots_ac;
+ p_ptr->dis_to_a += p_ptr->tim_roots_ac;
+ }
+
+ /* Temporary "Beserk" */
+ if (p_ptr->shero)
+ {
+ p_ptr->to_h += 24;
+ p_ptr->dis_to_h += 24;
+ p_ptr->to_a -= 10;
+ p_ptr->dis_to_a -= 10;
+ }
+
+ /* Temporary "Accurancy" */
+ if (p_ptr->strike)
+ {
+ p_ptr->to_d += 15;
+ p_ptr->dis_to_d += 15;
+ p_ptr->to_h += 15;
+ p_ptr->dis_to_h += 15;
+ }
+
+ /* Temporary "Meditation" */
+ if (p_ptr->meditation)
+ {
+ p_ptr->to_d -= 25;
+ p_ptr->dis_to_d -= 25;
+ p_ptr->to_h -= 25;
+ p_ptr->dis_to_h -= 25;
+ }
+
+ /* Temporary "Reflection" */
+ if (p_ptr->tim_reflect)
+ {
+ p_ptr->reflect = TRUE;
+ }
+
+ /* Temporary "Time Resistance" */
+ if (p_ptr->tim_res_time)
+ {
+ p_ptr->resist_continuum = TRUE;
+ }
+
+ /* Temporary "Levitation" and "Flying" */
+ if (p_ptr->tim_ffall)
+ {
+ p_ptr->ffall = TRUE;
+ }
+ if (p_ptr->tim_fly)
+ {
+ p_ptr->fly = TRUE;
+ }
+
+ /* Temporary "Fire Aura" */
+ if (p_ptr->tim_fire_aura)
+ {
+ p_ptr->sh_fire = TRUE;
+ }
+
+ /* Oppose Light & Dark */
+ if (p_ptr->oppose_ld)
+ {
+ p_ptr->resist_lite = TRUE;
+ p_ptr->resist_dark = TRUE;
+ }
+
+ /* Oppose Chaos & Confusion */
+ if (p_ptr->oppose_cc)
+ {
+ p_ptr->resist_chaos = TRUE;
+ p_ptr->resist_conf = TRUE;
+ }
+
+ /* Oppose Sound & Shards */
+ if (p_ptr->oppose_ss)
+ {
+ p_ptr->resist_sound = TRUE;
+ p_ptr->resist_shard = TRUE;
+ }
+
+ /* Oppose Nexus */
+ if (p_ptr->oppose_nex)
+ {
+ p_ptr->resist_nexus = TRUE;
+ }
+
+ /* Mental barrier */
+ if (p_ptr->tim_mental_barrier)
+ {
+ p_ptr->sustain_int = TRUE;
+ p_ptr->sustain_wis = TRUE;
+ }
+
+ /* Temporary "fast" */
+ if (p_ptr->fast)
+ {
+ p_ptr->pspeed += p_ptr->speed_factor;
+ }
+
+ /* Temporary "light speed" */
+ if (p_ptr->lightspeed)
+ {
+ p_ptr->pspeed += 50;
+ }
+
+ /* Temporary "slow" */
+ if (p_ptr->slow)
+ {
+ p_ptr->pspeed -= 10;
+ }
+
+ if (p_ptr->tim_esp)
+ {
+ p_ptr->telepathy |= ESP_ALL;
+ }
+
+ /* Temporary see invisible */
+ if (p_ptr->tim_invis)
+ {
+ p_ptr->see_inv = TRUE;
+ }
+
+ /* Temporary infravision boost */
+ if (p_ptr->tim_infra)
+ {
+ p_ptr->see_infra++;
+ }
+
+ /* Hack -- Magic breath -> Water breath */
+ if (p_ptr->magical_breath)
+ {
+ p_ptr->water_breath = TRUE;
+ }
+
+ /* Hack -- Can Fly -> Can Levitate */
+ if (p_ptr->fly)
+ {
+ p_ptr->ffall = TRUE;
+ }
+
+ /* Hack -- Res Chaos -> Res Conf */
+ if (p_ptr->resist_chaos)
+ {
+ p_ptr->resist_conf = TRUE;
+ }
+
+ /* Hack -- Hero/Shero -> Res fear */
+ if (p_ptr->hero || p_ptr->shero)
+ {
+ p_ptr->resist_fear = TRUE;
+ }
+
+
+ /* Hack -- Telepathy Change */
+ if (p_ptr->telepathy != old_telepathy)
+ {
+ p_ptr->update |= (PU_MONSTERS);
+ }
+
+ /* Hack -- See Invis Change */
+ if (p_ptr->see_inv != old_see_inv)
+ {
+ p_ptr->update |= (PU_MONSTERS);
+ }
+
+
+ /* Extract the current weight (in tenth pounds) */
+ j = calc_total_weight();
+
+ /* Extract the "weight limit" (in tenth pounds) */
+ i = weight_limit();
+
+ /* XXX XXX XXX Apply "encumbrance" from weight */
+ if (j > i / 2) p_ptr->pspeed -= ((j - (i / 2)) / (i / 10));
+
+ /* Bloating slows the player down (a little) */
+ if (p_ptr->food >= PY_FOOD_MAX) p_ptr->pspeed -= 10;
+
+ /* Searching slows the player down */
+ if (p_ptr->searching) p_ptr->pspeed -= 10;
+
+ /* In order to get a "nice" mana path druids need to ahve a 0 speed */
+ if ((p_ptr->druid_extra2 == CLASS_MANA_PATH) && (p_ptr->pspeed > 110))
+ p_ptr->pspeed = 110;
+
+ /* Display the speed (if needed) */
+ if (p_ptr->pspeed != old_speed) p_ptr->redraw |= (PR_SPEED);
+
+
+ /* Actual Modifier Bonuses (Un-inflate stat bonuses) */
+ p_ptr->to_a += ((int)(adj_dex_ta[p_ptr->stat_ind[A_DEX]]) - 128);
+ p_ptr->to_d += ((int)(adj_str_td[p_ptr->stat_ind[A_STR]]) - 128);
+ p_ptr->to_h += ((int)(adj_dex_th[p_ptr->stat_ind[A_DEX]]) - 128);
+ p_ptr->to_h += ((int)(adj_str_th[p_ptr->stat_ind[A_STR]]) - 128);
+
+ /* Displayed Modifier Bonuses (Un-inflate stat bonuses) */
+ p_ptr->dis_to_a += ((int)(adj_dex_ta[p_ptr->stat_ind[A_DEX]]) - 128);
+ p_ptr->dis_to_d += ((int)(adj_str_td[p_ptr->stat_ind[A_STR]]) - 128);
+ p_ptr->dis_to_h += ((int)(adj_dex_th[p_ptr->stat_ind[A_DEX]]) - 128);
+ p_ptr->dis_to_h += ((int)(adj_str_th[p_ptr->stat_ind[A_STR]]) - 128);
+
+ /* Redraw armor (if needed) */
+ if ((p_ptr->dis_ac != old_dis_ac) || (p_ptr->dis_to_a != old_dis_to_a))
+ {
+ /* Redraw */
+ p_ptr->redraw |= (PR_ARMOR);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+
+ /* Obtain the "hold" value */
+ hold = adj_str_hold[p_ptr->stat_ind[A_STR]];
+
+
+ /* Examine the "current bow" */
+ o_ptr = &p_ptr->inventory[INVEN_BOW];
+
+
+ /* Assume not heavy */
+ p_ptr->heavy_shoot = FALSE;
+
+ /* It is hard to carholdry a heavy bow */
+ if (hold < o_ptr->weight / 10)
+ {
+ /* Hard to wield a heavy bow */
+ p_ptr->to_h += 2 * (hold - o_ptr->weight / 10);
+ p_ptr->dis_to_h += 2 * (hold - o_ptr->weight / 10);
+
+ /* Heavy Bow */
+ p_ptr->heavy_shoot = TRUE;
+ }
+
+ /* Take note of required "tval" for missiles */
+ switch (o_ptr->sval)
+ {
+ case SV_SLING:
+ {
+ p_ptr->tval_ammo = TV_SHOT;
+ break;
+ }
+
+ case SV_SHORT_BOW:
+ case SV_LONG_BOW:
+ {
+ p_ptr->tval_ammo = TV_ARROW;
+ break;
+ }
+
+ case SV_LIGHT_XBOW:
+ case SV_HEAVY_XBOW:
+ {
+ p_ptr->tval_ammo = TV_BOLT;
+ break;
+ }
+ }
+
+ /* Compute "extra shots" if needed */
+ if (o_ptr->k_idx && !p_ptr->heavy_shoot)
+ {
+ int archery = get_archery_skill();
+
+ if (archery != -1)
+ {
+ p_ptr->to_h_ranged += get_skill_scale(archery, 25);
+ p_ptr->num_fire += (get_skill(archery) / 16);
+ p_ptr->xtra_might += (get_skill(archery) / 25);
+ switch (archery)
+ {
+ case SKILL_SLING:
+ if (p_ptr->tval_ammo == TV_SHOT) p_ptr->xtra_might += get_skill(archery) / 30;
+ break;
+ case SKILL_BOW:
+ if (p_ptr->tval_ammo == TV_ARROW) p_ptr->xtra_might += get_skill(archery) / 30;
+ break;
+ case SKILL_XBOW:
+ if (p_ptr->tval_ammo == TV_BOLT) p_ptr->xtra_might += get_skill(archery) / 30;
+ break;
+ }
+ }
+
+ /* Add in the "bonus shots" */
+ p_ptr->num_fire += extra_shots;
+
+ /* Require at least one shot */
+ if (p_ptr->num_fire < 1) p_ptr->num_fire = 1;
+ }
+
+ if (PRACE_FLAG(PR1_XTRA_MIGHT_BOW) && p_ptr->tval_ammo == TV_ARROW)
+ p_ptr->xtra_might += 1;
+
+ if (PRACE_FLAG(PR1_XTRA_MIGHT_SLING) && p_ptr->tval_ammo == TV_SHOT)
+ p_ptr->xtra_might += 1;
+
+ if (PRACE_FLAG(PR1_XTRA_MIGHT_XBOW) && p_ptr->tval_ammo == TV_BOLT)
+ p_ptr->xtra_might += 1;
+
+ /* Examine the "current tool" */
+ o_ptr = &p_ptr->inventory[INVEN_TOOL];
+
+ /* Boost digging skill by tool weight */
+ if (o_ptr->k_idx && (o_ptr->tval == TV_DIGGING))
+ {
+ p_ptr->skill_dig += (o_ptr->weight / 10);
+ }
+
+ /* Examine the main weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Assume not heavy */
+ p_ptr->heavy_wield = FALSE;
+
+ /* Normal weapons */
+ if (o_ptr->k_idx && !p_ptr->heavy_wield)
+ {
+ int str_index, dex_index;
+
+ int num = 0, wgt = 0, mul = 0, div = 0;
+
+ analyze_blow(&num, &wgt, &mul);
+
+ /* Enforce a minimum "weight" (tenth pounds) */
+ div = ((o_ptr->weight < wgt) ? wgt : o_ptr->weight);
+
+ /* Access the strength vs weight */
+ str_index = (adj_str_blow[p_ptr->stat_ind[A_STR]] * mul / div);
+
+ /* Maximal value */
+ if (str_index > 11) str_index = 11;
+
+ /* Index by dexterity */
+ dex_index = (adj_dex_blow[p_ptr->stat_ind[A_DEX]]);
+
+ /* Maximal value */
+ if (dex_index > 11) dex_index = 11;
+
+ /* Use the blows table */
+ p_ptr->num_blow = blows_table[str_index][dex_index];
+
+ /* Maximal value */
+ if (p_ptr->num_blow > num) p_ptr->num_blow = num;
+
+ /* Add in the "bonus blows" */
+ p_ptr->num_blow += extra_blows;
+
+ /* Special class bonus blows */
+ p_ptr->num_blow += p_ptr->lev * cp_ptr->extra_blows / 50;
+
+ /* Weapon specialization bonus blows */
+ if (get_weaponmastery_skill() != -1)
+ p_ptr->num_blow += get_skill_scale(get_weaponmastery_skill(), 2);
+
+ /* Bonus blows for plain weaponmastery skill */
+ p_ptr->num_blow += get_skill_scale(SKILL_MASTERY, 3);
+
+ /* Require at least one blow */
+ if (p_ptr->num_blow < 1) p_ptr->num_blow = 1;
+ }
+ /* Different calculation for bear form with empty hands */
+ else if ((p_ptr->melee_style == SKILL_HAND) && monk_empty_hands())
+ {
+ int plev = get_skill(SKILL_HAND);
+
+ p_ptr->num_blow = get_extra_blows_ability();
+
+ if (plev > 9) p_ptr->num_blow++;
+ if (plev > 19) p_ptr->num_blow++;
+ if (plev > 29) p_ptr->num_blow++;
+ if (plev > 34) p_ptr->num_blow++;
+ if (plev > 39) p_ptr->num_blow++;
+ if (plev > 44) p_ptr->num_blow++;
+ if (plev > 49) p_ptr->num_blow++;
+
+ if (monk_heavy_armor()) p_ptr->num_blow /= 2;
+
+ p_ptr->num_blow += 1 + extra_blows;
+
+ if (!monk_heavy_armor())
+ {
+ p_ptr->to_h += (plev / 3);
+ p_ptr->to_d += (plev / 3);
+
+ p_ptr->dis_to_h += (plev / 3);
+ p_ptr->dis_to_d += (plev / 3);
+ }
+ }
+
+ /* Monsters that only have their "natural" attacks */
+ else if (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON])
+ {
+ int str_index, dex_index;
+ int num = 0, wgt = 0, mul = 0;
+ monster_race *r_ptr = race_info_idx(p_ptr->body_monster, 0);
+
+ analyze_blow(&num, &wgt, &mul);
+
+ /* Access the strength vs weight */
+ str_index = (adj_str_blow[p_ptr->stat_ind[A_STR]] * mul / 3);
+
+ /* Maximal value */
+ if (str_index > 11) str_index = 11;
+
+ /* Index by dexterity */
+ dex_index = (adj_dex_blow[p_ptr->stat_ind[A_DEX]]);
+
+ /* Maximal value */
+ if (dex_index > 11) dex_index = 11;
+
+ /* Use the blows table */
+ p_ptr->num_blow = blows_table[str_index][dex_index];
+
+ /* Add in the "bonus blows" */
+ p_ptr->num_blow += extra_blows;
+
+ /* Maximal value */
+ if (p_ptr->num_blow > 4) p_ptr->num_blow = 4;
+
+ /* Require at least one blow */
+ if (p_ptr->num_blow < 1) p_ptr->num_blow = 1;
+
+ /* Limit as defined by monster body */
+ for (num = 0; num < p_ptr->num_blow; num++)
+ if (!r_ptr->blow[num].effect)
+ break;
+ p_ptr->num_blow = num;
+ }
+
+ /* Different calculation for monks with empty hands */
+ else if ((!p_ptr->body_monster) && (p_ptr->mimic_form == resolve_mimic_name("Bear")) && (p_ptr->melee_style == SKILL_BEAR))
+ {
+ int plev = get_skill(SKILL_BEAR);
+
+ p_ptr->num_blow = 0;
+
+ p_ptr->num_blow += 2 + (plev / 5) + extra_blows;
+
+ p_ptr->to_h -= (plev / 5);
+ p_ptr->dis_to_h -= (plev / 5);
+
+ p_ptr->to_d += (plev / 2);
+ p_ptr->dis_to_d += (plev / 2);
+ }
+
+ /* Assume okay */
+ p_ptr->icky_wield = FALSE;
+ monk_armour_aux = FALSE;
+
+ if (get_weaponmastery_skill() != -1)
+ {
+ int lev = get_skill(get_weaponmastery_skill());
+
+ p_ptr->to_h_melee += lev;
+ p_ptr->to_d_melee += lev / 2;
+ }
+
+ if (get_skill(SKILL_COMBAT))
+ {
+ int lev = get_skill_scale(SKILL_COMBAT, 10);
+
+ p_ptr->to_d += lev;
+ p_ptr->dis_to_d += lev;
+ }
+
+ if (get_skill(SKILL_DODGE))
+ {
+ /* Get the armor weight */
+ int cur_wgt = 0;
+
+ cur_wgt += p_ptr->inventory[INVEN_BODY].weight;
+ cur_wgt += p_ptr->inventory[INVEN_HEAD].weight;
+ cur_wgt += p_ptr->inventory[INVEN_ARM].weight;
+ cur_wgt += p_ptr->inventory[INVEN_OUTER].weight;
+ cur_wgt += p_ptr->inventory[INVEN_HANDS].weight;
+ cur_wgt += p_ptr->inventory[INVEN_FEET].weight;
+
+ /* Base dodge chance */
+ p_ptr->dodge_chance = get_skill_scale(SKILL_DODGE, 150) + get_skill(SKILL_HAND);
+
+ /* Armor weight bonus/penalty */
+ p_ptr->dodge_chance -= cur_wgt * 2;
+
+ /* Encumberance bonus/penalty */
+ p_ptr->dodge_chance = p_ptr->dodge_chance - (calc_total_weight() / 100);
+
+ /* Never below 0 */
+ if (p_ptr->dodge_chance < 0) p_ptr->dodge_chance = 0;
+ }
+ else
+ {
+ p_ptr->dodge_chance = 0;
+ }
+
+ /* Parse all the weapons */
+ i = 0;
+ while (p_ptr->body_parts[i] == INVEN_WIELD)
+ {
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ /* 2handed weapon and shield = less damage */
+ if (p_ptr->inventory[INVEN_WIELD + i].k_idx && p_ptr->inventory[INVEN_ARM + i].k_idx)
+ {
+ /* Extract the item flags */
+ object_flags(&p_ptr->inventory[INVEN_WIELD + i], &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_COULD2H)
+ {
+ int tmp;
+
+ /* Reduce the bonuses */
+ tmp = o_ptr->to_h / 2;
+ if (tmp < 0) tmp = -tmp;
+ p_ptr->to_h_melee -= tmp;
+
+ tmp = o_ptr->to_d / 2;
+ if (tmp < 0) tmp = -tmp;
+ tmp += (o_ptr->dd * o_ptr->ds) / 2;
+ p_ptr->to_d_melee -= tmp;
+ }
+ }
+
+ /* Priest weapon penalty for non-blessed edged weapons */
+ if (((forbid_non_blessed()) && (!p_ptr->bless_blade) &&
+ ((o_ptr->tval == TV_AXE) || (o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM))) && (o_ptr->k_idx))
+ {
+ /* Reduce the real bonuses */
+ p_ptr->to_h -= 15;
+ p_ptr->to_d -= 15;
+
+ /* Reduce the mental bonuses */
+ p_ptr->dis_to_h -= 15;
+ p_ptr->dis_to_d -= 15;
+
+ /* Icky weapon */
+ p_ptr->icky_wield = TRUE;
+ }
+
+ /* Sorcerer can't wield a weapon unless it's a mage staff */
+ if (get_skill(SKILL_SORCERY))
+ {
+ int malus = get_skill_scale(SKILL_SORCERY, 100);
+
+ if ((o_ptr->tval != TV_MSTAFF) && (o_ptr->k_idx))
+ {
+ /* Reduce the real bonuses */
+ p_ptr->to_h -= malus;
+ p_ptr->to_d -= malus;
+
+ /* Reduce the mental bonuses */
+ p_ptr->dis_to_h -= malus;
+ p_ptr->dis_to_d -= malus;
+
+ /* Icky weapon */
+ p_ptr->icky_wield = TRUE;
+ }
+ else
+ {
+ /* Reduce the real bonuses */
+ p_ptr->to_h -= malus / 10;
+ p_ptr->to_d -= malus / 10;
+
+ /* Reduce the mental bonuses */
+ p_ptr->dis_to_h -= malus / 10;
+ p_ptr->dis_to_d -= malus / 10;
+ }
+ }
+
+ /* Check next weapon */
+ i++;
+ }
+
+ if (monk_heavy_armor())
+ {
+ monk_armour_aux = TRUE;
+ }
+
+ /* Affect Skill -- stealth (bonus one) */
+ p_ptr->skill_stl += 1;
+
+ /* Affect Skill -- disarming (DEX and INT) */
+ p_ptr->skill_dis += adj_dex_dis[p_ptr->stat_ind[A_DEX]];
+ p_ptr->skill_dis += adj_int_dis[p_ptr->stat_ind[A_INT]];
+
+ /* Affect Skill -- magic devices (INT) */
+ p_ptr->skill_dev += get_skill_scale(SKILL_DEVICE, 20);
+
+ /* Affect Skill -- saving throw (WIS) */
+ p_ptr->skill_sav += adj_wis_sav[p_ptr->stat_ind[A_WIS]];
+
+ /* Affect Skill -- digging (STR) */
+ p_ptr->skill_dig += adj_str_dig[p_ptr->stat_ind[A_STR]];
+
+ /* Affect Skill -- disarming (skill) */
+ p_ptr->skill_dis += (get_skill_scale(SKILL_DISARMING, 75));
+
+ /* Affect Skill -- magic devices (skill) */
+ p_ptr->skill_dev += (get_skill_scale(SKILL_DEVICE, 150));
+
+ /* Affect Skill -- saving throw (skill and level) */
+ p_ptr->skill_sav += (get_skill_scale(SKILL_SPIRITUALITY, 75));
+
+ /* Affect Skill -- stealth (skill) */
+ p_ptr->skill_stl += (get_skill_scale(SKILL_STEALTH, 25));
+
+ /* Affect Skill -- search ability (Sneakiness skill) */
+ p_ptr->skill_srh += (get_skill_scale(SKILL_SNEAK, 35));
+
+ /* Affect Skill -- search frequency (Sneakiness skill) */
+ p_ptr->skill_fos += (get_skill_scale(SKILL_SNEAK, 25));
+
+ /* Affect Skill -- combat (Combat skill + mastery) */
+ p_ptr->skill_thn += (50 * (((7 * get_skill(p_ptr->melee_style)) + (3 * get_skill(SKILL_COMBAT))) / 10) / 10);
+
+ /* Affect Skill -- combat (shooting) (Level, by Class) */
+ p_ptr->skill_thb += (50 * (((7 * get_skill(SKILL_ARCHERY)) + (3 * get_skill(SKILL_COMBAT))) / 10) / 10);
+
+ /* Affect Skill -- combat (throwing) (Level) */
+ p_ptr->skill_tht += (50 * p_ptr->lev / 10);
+
+
+ /* Limit Skill -- stealth from 0 to 30 */
+ if (p_ptr->skill_stl > 30) p_ptr->skill_stl = 30;
+ if (p_ptr->skill_stl < 0) p_ptr->skill_stl = 0;
+
+ /* Limit Skill -- digging from 1 up */
+ if (p_ptr->skill_dig < 1) p_ptr->skill_dig = 1;
+
+ if ((p_ptr->anti_magic) && (p_ptr->skill_sav < 95)) p_ptr->skill_sav = 95;
+
+ /* Hack -- handle "xtra" mode */
+ if (character_xtra) return;
+
+ /* Take note when "heavy bow" changes */
+ if (p_ptr->old_heavy_shoot != p_ptr->heavy_shoot)
+ {
+ if (silent)
+ {
+ /* do nothing */
+ }
+ /* Message */
+ else if (p_ptr->heavy_shoot)
+ {
+ msg_print("You have trouble wielding such a heavy bow.");
+ }
+ else if (p_ptr->inventory[INVEN_BOW].k_idx)
+ {
+ msg_print("You have no trouble wielding your bow.");
+ }
+ else
+ {
+ msg_print("You feel relieved to put down your heavy bow.");
+ }
+
+ /* Save it */
+ p_ptr->old_heavy_shoot = p_ptr->heavy_shoot;
+ }
+
+
+ /* Take note when "heavy weapon" changes */
+ if (p_ptr->old_heavy_wield != p_ptr->heavy_wield)
+ {
+ if (silent)
+ {
+ /* do nothing */
+ }
+ /* Message */
+ else if (p_ptr->heavy_wield)
+ {
+ msg_print("You have trouble wielding such a heavy weapon.");
+ }
+ else if (p_ptr->inventory[INVEN_WIELD].k_idx)
+ {
+ msg_print("You have no trouble wielding your weapon.");
+ }
+ else
+ {
+ msg_print("You feel relieved to put down your heavy weapon.");
+ }
+
+ /* Save it */
+ p_ptr->old_heavy_wield = p_ptr->heavy_wield;
+ }
+
+
+ /* Take note when "illegal weapon" changes */
+ if (p_ptr->old_icky_wield != p_ptr->icky_wield)
+ {
+ if (silent)
+ {
+ /* do nothing */
+ }
+ /* Message */
+ else if (p_ptr->icky_wield)
+ {
+ msg_print("You do not feel comfortable with your weapon.");
+ }
+ else if (p_ptr->inventory[INVEN_WIELD].k_idx)
+ {
+ msg_print("You feel comfortable with your weapon.");
+ }
+ else
+ {
+ msg_print("You feel more comfortable after removing your weapon.");
+ }
+
+ /* Save it */
+ p_ptr->old_icky_wield = p_ptr->icky_wield;
+ }
+
+ if (monk_armour_aux != monk_notify_aux)
+ {
+ if ((p_ptr->melee_style != SKILL_HAND) || silent)
+ {
+ /* do nothing */
+ }
+ else if (monk_heavy_armor())
+ msg_print("The weight of your armor disrupts your balance.");
+ else
+ msg_print("You regain your balance.");
+ monk_notify_aux = monk_armour_aux;
+ }
+
+ /* Resist lite & senseible lite negates one an other */
+ if (p_ptr->resist_lite && p_ptr->sensible_lite)
+ {
+ p_ptr->resist_lite = p_ptr->sensible_lite = FALSE;
+ }
+
+ /* resistance to fire cancel sensibility to fire */
+ if (p_ptr->resist_fire || p_ptr->oppose_fire || p_ptr->immune_fire)
+ p_ptr->sensible_fire = FALSE;
+
+ /* Minimum saving throw */
+ if(p_ptr->skill_sav <= 10)
+ p_ptr->skill_sav = 10;
+ else
+ p_ptr->skill_sav += 10;
+
+ /* Let the scripts do what they need */
+ process_hooks(HOOK_CALC_BONUS_END, "(d)", silent);
+}
+
+
+
+/*
+ * Handle "p_ptr->notice"
+ */
+void notice_stuff(void)
+{
+ /* Notice stuff */
+ if (!p_ptr->notice) return;
+
+
+ /* Combine the pack */
+ if (p_ptr->notice & (PN_COMBINE))
+ {
+ p_ptr->notice &= ~(PN_COMBINE);
+ combine_pack();
+ }
+
+ /* Reorder the pack */
+ if (p_ptr->notice & (PN_REORDER))
+ {
+ p_ptr->notice &= ~(PN_REORDER);
+ reorder_pack();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->update"
+ */
+void update_stuff(void)
+{
+ /* Update stuff */
+ if (!p_ptr->update) return;
+
+
+ if (p_ptr->update & (PU_BODY))
+ {
+ p_ptr->update &= ~(PU_BODY);
+ calc_body();
+ }
+
+ if (p_ptr->update & (PU_BONUS))
+ {
+ /* Ok now THAT is an ugly hack */
+ p_ptr->update &= ~(PU_POWERS);
+ calc_powers();
+
+ p_ptr->update &= ~(PU_BONUS);
+ calc_bonuses(FALSE);
+ }
+
+ if (p_ptr->update & (PU_TORCH))
+ {
+ p_ptr->update &= ~(PU_TORCH);
+ calc_torch();
+ }
+
+ if (p_ptr->update & (PU_HP))
+ {
+ p_ptr->update &= ~(PU_HP);
+ calc_hitpoints();
+ }
+
+ if (p_ptr->update & (PU_SANITY))
+ {
+ p_ptr->update &= ~(PU_SANITY);
+ calc_sanity();
+ }
+
+ if (p_ptr->update & (PU_MANA))
+ {
+ p_ptr->update &= ~(PU_MANA);
+ calc_mana();
+ }
+
+ if (p_ptr->update & (PU_SPELLS))
+ {
+ p_ptr->update &= ~(PU_SPELLS);
+ calc_spells();
+ }
+
+ if (p_ptr->update & (PU_POWERS))
+ {
+ p_ptr->update &= ~(PU_POWERS);
+ calc_powers();
+ }
+
+ /* Character is not ready yet, no screen updates */
+ if (!character_generated) return;
+
+
+ /* Character is in "icky" mode, no screen updates */
+ if (character_icky) return;
+
+
+ if (p_ptr->update & (PU_UN_VIEW))
+ {
+ p_ptr->update &= ~(PU_UN_VIEW);
+ forget_view();
+ }
+
+ if (p_ptr->update & (PU_VIEW))
+ {
+ p_ptr->update &= ~(PU_VIEW);
+ update_view();
+ }
+
+ if (p_ptr->update & (PU_FLOW))
+ {
+ p_ptr->update &= ~(PU_FLOW);
+ update_flow();
+ }
+
+ if (p_ptr->update & (PU_DISTANCE))
+ {
+ p_ptr->update &= ~(PU_DISTANCE);
+ p_ptr->update &= ~(PU_MONSTERS);
+ update_monsters(TRUE);
+ }
+
+ if (p_ptr->update & (PU_MONSTERS))
+ {
+ p_ptr->update &= ~(PU_MONSTERS);
+ update_monsters(FALSE);
+ }
+
+ if (p_ptr->update & (PU_MON_LITE))
+ {
+ p_ptr->update &= ~(PU_MON_LITE);
+ if (monster_lite) update_mon_lite();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->redraw"
+ */
+void redraw_stuff(void)
+{
+ /* Redraw stuff */
+ if (!p_ptr->redraw) return;
+
+
+ /* Character is not ready yet, no screen updates */
+ if (!character_generated) return;
+
+
+ /* Character is in "icky" mode, no screen updates */
+ if (character_icky) return;
+
+
+ /* Should we tell lua to redisplay too ? */
+ process_hooks(HOOK_REDRAW, "()");
+
+
+ /* Hack -- clear the screen */
+ if (p_ptr->redraw & (PR_WIPE))
+ {
+ p_ptr->redraw &= ~(PR_WIPE);
+ msg_print(NULL);
+ Term_clear();
+ }
+
+
+ if (p_ptr->redraw & (PR_MAP))
+ {
+ p_ptr->redraw &= ~(PR_MAP);
+ prt_map();
+ }
+
+
+ if (p_ptr->redraw & (PR_BASIC))
+ {
+ p_ptr->redraw &= ~(PR_BASIC);
+ p_ptr->redraw &= ~(PR_MISC | PR_TITLE | PR_STATS);
+ p_ptr->redraw &= ~(PR_LEV | PR_EXP | PR_GOLD);
+ p_ptr->redraw &= ~(PR_ARMOR | PR_HP | PR_MANA | PR_PIETY | PR_MH);
+ p_ptr->redraw &= ~(PR_DEPTH | PR_HEALTH);
+ prt_frame_basic();
+ }
+
+ if (p_ptr->redraw & (PR_MISC))
+ {
+ p_ptr->redraw &= ~(PR_MISC);
+ prt_field(rp_ptr->title + rp_name, ROW_RACE, COL_RACE);
+ prt_field(spp_ptr->title + c_name, ROW_CLASS, COL_CLASS);
+ }
+
+ if (p_ptr->redraw & (PR_TITLE))
+ {
+ p_ptr->redraw &= ~(PR_TITLE);
+ prt_title();
+ }
+
+ if (p_ptr->redraw & (PR_LEV))
+ {
+ p_ptr->redraw &= ~(PR_LEV);
+ prt_level();
+ }
+
+ if (p_ptr->redraw & (PR_EXP))
+ {
+ p_ptr->redraw &= ~(PR_EXP);
+ prt_exp();
+ }
+
+ if (p_ptr->redraw & (PR_STATS))
+ {
+ p_ptr->redraw &= ~(PR_STATS);
+ prt_stat(A_STR);
+ prt_stat(A_INT);
+ prt_stat(A_WIS);
+ prt_stat(A_DEX);
+ prt_stat(A_CON);
+ prt_stat(A_CHR);
+ }
+
+ if (p_ptr->redraw & (PR_ARMOR))
+ {
+ p_ptr->redraw &= ~(PR_ARMOR);
+ prt_ac();
+ }
+
+ if (p_ptr->redraw & (PR_HP))
+ {
+ p_ptr->redraw &= ~(PR_HP);
+ prt_hp();
+ }
+
+ if (p_ptr->redraw & (PR_MANA))
+ {
+ p_ptr->redraw &= ~(PR_MANA);
+ prt_sp();
+ }
+
+ if (p_ptr->redraw & (PR_PIETY))
+ {
+ p_ptr->redraw &= ~(PR_PIETY);
+ prt_piety();
+ }
+
+ if (p_ptr->redraw & (PR_MH))
+ {
+ p_ptr->redraw &= ~(PR_MH);
+ prt_mh();
+ }
+
+ if (p_ptr->redraw & (PR_GOLD))
+ {
+ p_ptr->redraw &= ~(PR_GOLD);
+ prt_gold();
+ }
+
+ if (p_ptr->redraw & (PR_DEPTH))
+ {
+ p_ptr->redraw &= ~(PR_DEPTH);
+ prt_depth();
+ }
+
+ if (p_ptr->redraw & (PR_HEALTH))
+ {
+ p_ptr->redraw &= ~(PR_HEALTH);
+ health_redraw();
+ }
+
+
+ if (p_ptr->redraw & (PR_EXTRA))
+ {
+ p_ptr->redraw &= ~(PR_EXTRA);
+ p_ptr->redraw &= ~(PR_CUT | PR_STUN);
+ p_ptr->redraw &= ~(PR_HUNGER);
+ p_ptr->redraw &= ~(PR_BLIND | PR_CONFUSED);
+ p_ptr->redraw &= ~(PR_AFRAID | PR_POISONED);
+ p_ptr->redraw &= ~(PR_STATE | PR_SPEED | PR_STUDY | PR_SANITY);
+ prt_frame_extra();
+ }
+
+ if (p_ptr->redraw & (PR_CUT))
+ {
+ p_ptr->redraw &= ~(PR_CUT);
+ prt_cut();
+ }
+
+ if (p_ptr->redraw & (PR_STUN))
+ {
+ p_ptr->redraw &= ~(PR_STUN);
+ prt_stun();
+ }
+
+ if (p_ptr->redraw & (PR_HUNGER))
+ {
+ p_ptr->redraw &= ~(PR_HUNGER);
+ prt_hunger();
+ }
+
+ if (p_ptr->redraw & (PR_BLIND))
+ {
+ p_ptr->redraw &= ~(PR_BLIND);
+ prt_blind();
+ }
+
+ if (p_ptr->redraw & (PR_CONFUSED))
+ {
+ p_ptr->redraw &= ~(PR_CONFUSED);
+ prt_confused();
+ }
+
+ if (p_ptr->redraw & (PR_AFRAID))
+ {
+ p_ptr->redraw &= ~(PR_AFRAID);
+ prt_afraid();
+ }
+
+ if (p_ptr->redraw & (PR_POISONED))
+ {
+ p_ptr->redraw &= ~(PR_POISONED);
+ prt_poisoned();
+ }
+
+ if (p_ptr->redraw & (PR_DTRAP))
+ {
+ p_ptr->redraw &= ~(PR_DTRAP);
+ prt_dtrap();
+ }
+
+ if (p_ptr->redraw & (PR_STATE))
+ {
+ p_ptr->redraw &= ~(PR_STATE);
+ prt_state();
+ }
+
+ if (p_ptr->redraw & (PR_SPEED))
+ {
+ p_ptr->redraw &= ~(PR_SPEED);
+ prt_speed();
+ }
+
+ if (p_ptr->redraw & (PR_STUDY))
+ {
+ p_ptr->redraw &= ~(PR_STUDY);
+ prt_study();
+ }
+
+ if (p_ptr->redraw & (PR_SANITY))
+ {
+ p_ptr->redraw &= ~(PR_SANITY);
+ prt_sane();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->window"
+ */
+void window_stuff(void)
+{
+ int j;
+
+ u32b mask = 0L;
+
+
+ /* Nothing to do */
+ if (!p_ptr->window) return;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ /* Save usable flags */
+ if (angband_term[j]) mask |= window_flag[j];
+ }
+
+ /* Apply usable flags */
+ p_ptr->window &= mask;
+
+ /* Nothing to do */
+ if (!p_ptr->window) return;
+
+
+ /* Display p_ptr->inventory */
+ if (p_ptr->window & (PW_INVEN))
+ {
+ p_ptr->window &= ~(PW_INVEN);
+ fix_inven();
+ }
+
+ /* Display equipment */
+ if (p_ptr->window & (PW_EQUIP))
+ {
+ p_ptr->window &= ~(PW_EQUIP);
+ fix_equip();
+ }
+
+ /* Display player */
+ if (p_ptr->window & (PW_PLAYER))
+ {
+ p_ptr->window &= ~(PW_PLAYER);
+ fix_player();
+ }
+
+ /* Display monster list */
+ if (p_ptr->window & (PW_M_LIST))
+ {
+ p_ptr->window &= ~(PW_M_LIST);
+ fix_m_list();
+ }
+
+ /* Display overhead view */
+ if (p_ptr->window & (PW_MESSAGE))
+ {
+ p_ptr->window &= ~(PW_MESSAGE);
+ fix_message();
+ }
+
+ /* Display overhead view */
+ if (p_ptr->window & (PW_OVERHEAD))
+ {
+ p_ptr->window &= ~(PW_OVERHEAD);
+ fix_overhead();
+ }
+
+ /* Display monster recall */
+ if (p_ptr->window & (PW_MONSTER))
+ {
+ p_ptr->window &= ~(PW_MONSTER);
+ fix_monster();
+ }
+
+ /* Display object recall */
+ if (p_ptr->window & (PW_OBJECT))
+ {
+ p_ptr->window &= ~(PW_OBJECT);
+ fix_object();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->update" and "p_ptr->redraw" and "p_ptr->window"
+ */
+void handle_stuff(void)
+{
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+}
+
+
+bool_ monk_empty_hands(void)
+{
+ int i;
+ object_type *o_ptr;
+
+ if (p_ptr->melee_style != SKILL_HAND) return FALSE;
+
+ i = 0;
+ while (p_ptr->body_parts[i] == INVEN_WIELD)
+ {
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ if (o_ptr->k_idx) return FALSE;
+
+ i++;
+ }
+
+ return TRUE;
+}
+
+bool_ monk_heavy_armor(void)
+{
+ u16b monk_arm_wgt = 0;
+
+ if (p_ptr->melee_style != SKILL_HAND) return FALSE;
+
+ /* Weight the armor */
+ monk_arm_wgt += p_ptr->inventory[INVEN_BODY].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_HEAD].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_ARM].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_OUTER].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_HANDS].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_FEET].weight;
+
+ return (monk_arm_wgt > (100 + (get_skill(SKILL_HAND) * 4))) ;
+}
+
+static int get_artifact_idx(int level)
+{
+ int count = 0, i;
+
+ while (count < 1000)
+ {
+ artifact_type *a_ptr;
+
+ count++;
+ i = randint(max_a_idx - 1);
+ a_ptr = &a_info[i];
+ if (!a_ptr->tval) continue;
+
+ /* It is found/lost */
+ if (a_ptr->cur_num) continue;
+
+ /* OoD */
+ if (a_ptr->level > level) continue;
+
+ /* Avoid granting SPECIAL_GENE artifacts */
+ if (a_ptr->flags4 & TR4_SPECIAL_GENE) continue;
+
+ return i;
+ }
+
+ /* No matches found */
+ /* Grant a randart */
+ return 0;
+}
+
+/* Chose a fate */
+void gain_fate(byte fate)
+{
+ int i;
+ int level;
+
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if (!fates[i].fate)
+ {
+ fates[i].level = 0;
+
+ cmsg_print(TERM_VIOLET, "More of your prophecy has been unearthed!");
+ cmsg_print(TERM_VIOLET, "You should see a soothsayer quickly.");
+
+ if (fate)
+ fates[i].fate = fate;
+ else
+ /* If lucky (current luck > 0) avoid death fate */
+ switch (rand_int(p_ptr->luck_cur > 0 ? 17 : 18))
+ {
+ case 6:
+ case 2:
+ case 3:
+ case 7:
+ case 8:
+ case 9:
+ case 13:
+ fates[i].fate = FATE_FIND_O;
+ break;
+ case 1:
+ case 4:
+ case 5:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ fates[i].fate = FATE_FIND_R;
+ break;
+ case 15:
+ case 16:
+ fates[i].fate = FATE_FIND_A;
+ break;
+ case 17:
+ fates[i].fate = FATE_DIE;
+ break;
+ case 0:
+ {
+ /* The deepest the better */
+ int chance = dun_level / 4;
+
+ /* No more than 1/2 chances */
+ if (chance > 50) chance = 50;
+
+ /* It's HARD to get now */
+ if (magik(chance))
+ {
+ fates[i].fate = FATE_NO_DIE_MORTAL;
+ }
+ else
+ {
+ fates[i].fate = FATE_FIND_O;
+ }
+ break;
+ }
+ }
+
+ switch (fates[i].fate)
+ {
+ case FATE_FIND_O:
+ {
+ while (TRUE)
+ {
+ object_kind *k_ptr;
+ obj_theme theme;
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+ init_match_theme(theme);
+
+ /* Apply restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Rebuild allocation table */
+ get_obj_num_prep();
+
+ fates[i].o_idx = get_obj_num(max_dlv[dungeon_type] + randint(10));
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+
+ k_ptr = &k_info[fates[i].o_idx];
+
+ if (!(k_ptr->flags3 & TR3_INSTA_ART) && !(k_ptr->flags3 & TR3_NORM_ART)) break;
+ }
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = rand_int(2);
+ fates[i].know = FALSE;
+ if (wizard) msg_format("New fate : Find object %d on level %d", fates[i].o_idx, fates[i].level);
+ break;
+ }
+ case FATE_FIND_R:
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ fates[i].r_idx = get_mon_num(max_dlv[dungeon_type] + randint(10));
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = rand_int(2);
+ fates[i].know = FALSE;
+ if (wizard) msg_format("New fate : Meet monster %d on level %d", fates[i].r_idx, fates[i].level);
+ break;
+
+ case FATE_FIND_A:
+ fates[i].a_idx = get_artifact_idx(max_dlv[dungeon_type] + randint(10));
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = TRUE;
+ fates[i].know = FALSE;
+ if (wizard) msg_format("New fate : Find artifact %d on level %d", fates[i].a_idx, fates[i].level);
+ break;
+
+ case FATE_DIE:
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = TRUE;
+ fates[i].know = FALSE;
+ if ((wizard) || (p_ptr->precognition)) msg_format("New fate : Death on level %d", fates[i].level);
+ break;
+
+ case FATE_NO_DIE_MORTAL:
+ fates[i].serious = TRUE;
+ p_ptr->no_mortal = TRUE;
+ if ((wizard) || (p_ptr->precognition)) msg_format("New fate : Never to die by the hand of a mortal being.");
+ break;
+ }
+
+ break;
+ }
+ }
+}
+
+void fate_desc(char *desc, int fate)
+{
+ char buf[120];
+
+ if (fates[fate].serious)
+ {
+ strcpy(desc, "You are fated to ");
+ }
+ else
+ {
+ strcpy(desc, "You may ");
+ }
+ switch (fates[fate].fate)
+ {
+ case FATE_FIND_O:
+ {
+ object_type *o_ptr, forge;
+ char o_name[80];
+
+ o_ptr = &forge;
+ object_prep(o_ptr, fates[fate].o_idx);
+ object_desc_store(o_name, o_ptr, 1, 0);
+
+ sprintf(buf, "find %s on level %d.", o_name, fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_FIND_A:
+ {
+ object_type *q_ptr, forge;
+ char o_name[80];
+ artifact_type *a_ptr = &a_info[fates[fate].a_idx];
+ int I_kind;
+
+ /* Failed artefact allocation XXX XXX XXX */
+ if (fates[fate].a_idx == 0)
+ {
+ strcpy(o_name, "something special");
+ }
+
+ /* Legal artefacts */
+ else
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = fates[fate].a_idx;
+
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+
+ /* Hack -- acquire "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) q_ptr->ident |= (IDENT_CURSED);
+
+ random_artifact_resistance(q_ptr);
+
+ object_desc_store(o_name, q_ptr, 1, 0);
+ }
+
+ sprintf(buf, "find %s on level %d.", o_name, fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_FIND_R:
+ {
+ char m_name[80];
+
+ monster_race_desc(m_name, fates[fate].r_idx, 0);
+ sprintf(buf, "meet %s on level %d.", m_name, fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_DIE:
+ {
+ sprintf(buf, "die on level %d.", fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_NO_DIE_MORTAL:
+ {
+ strcat(desc, "never to die by the hand of a mortal being.");
+ break;
+ }
+ }
+}
+
+void dump_fates(FILE *outfile)
+{
+ int i;
+ char buf[120];
+ bool_ pending = FALSE;
+
+ if (!outfile) return;
+
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if ((fates[i].fate) && (fates[i].know))
+ {
+ fate_desc(buf, i);
+ fprintf(outfile, "%s\n", buf);
+ }
+ if ((fates[i].fate) && !(fates[i].know)) pending = TRUE;
+ }
+ if (pending)
+ {
+ fprintf(outfile, "You do not know all of your fate.\n");
+ }
+}
+
+/*
+ * Return a luck number between a certain range
+ */
+int luck(int min, int max)
+{
+ int luck = p_ptr->luck_cur;
+ int range = max - min;
+
+ if (luck < -30) luck = -30;
+ if (luck > 30) luck = 30;
+ luck += 30;
+
+ luck *= range;
+ luck /= 60;
+
+ return (luck + min);
+}
diff --git a/src/xtra2.c b/src/xtra2.c
new file mode 100644
index 00000000..70978e47
--- /dev/null
+++ b/src/xtra2.c
@@ -0,0 +1,6158 @@
+/* File: xtra2.c */
+/* File: xtra2.c */
+
+/* Purpose: effects of various "objects", targetting and panel handling */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Invoke The Rush
+ */
+bool_ set_rush(int v)
+{
+ int j;
+
+ /* Invoke The Bust */
+ if (!v)
+ {
+ p_ptr->rush = 0;
+
+ j = 50 - randint(p_ptr->lev);
+ set_paralyzed(j);
+ set_slow(j + 50 - randint(p_ptr->lev));
+ return TRUE;
+ }
+
+ /* When is The Bust going to happen? */
+ p_ptr->rush = v;
+
+ /* The bonuses of The Rush */
+ set_hero(p_ptr->hero + v);
+ set_tim_deadly(p_ptr->tim_deadly + v);
+ set_strike(p_ptr->strike + v);
+ if (magik(p_ptr->lev / 2))
+ {
+ set_light_speed(p_ptr->lightspeed + v);
+ }
+ else
+ {
+ set_fast(p_ptr->fast + v, 10);
+ }
+ if (magik(p_ptr->lev / 2)) set_tim_esp(p_ptr->tim_esp + v);
+ return TRUE;
+}
+
+
+/*
+ * Set "p_ptr->parasite" and "p_ptr->parasite_r_idx"
+ * notice observable changes
+ */
+bool_ set_parasite(int v, int r)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->parasite)
+ {
+ msg_print("You feel something growing in you.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->parasite)
+ {
+ if (magik(80))
+ {
+ char r_name[80];
+ int wx, wy;
+ int attempts = 500;
+
+ monster_race_desc(r_name, p_ptr->parasite_r_idx, 0);
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 10);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (place_monster_one(wy, wx, p_ptr->parasite_r_idx, 0, FALSE, MSTATUS_ENEMY))
+ {
+ cmsg_format(TERM_L_BLUE, "Your body convulses and spawns %s.", r_name);
+ p_ptr->food -= 750;
+ if (p_ptr->food < 100) p_ptr->food = 100;
+ }
+ }
+ else
+ {
+ cmsg_print(TERM_L_BLUE, "The hideous thing growing in you seems to die.");
+ }
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->parasite = v;
+ p_ptr->parasite_r_idx = r;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set a simple player field.
+ */
+static bool_ set_simple_field(
+ s16b *p_field,
+ s16b v,
+ byte activate_color,
+ cptr activate_msg,
+ byte deactivate_color,
+ cptr deactivate_msg)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!*p_field)
+ {
+ cmsg_print(activate_color, activate_msg);
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (*p_field)
+ {
+ cmsg_print(deactivate_color, deactivate_msg);
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ *p_field = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_project" and others
+ * notice observable changes
+ */
+bool_ set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_project, v,
+ TERM_WHITE, "Your weapon starts glowing.",
+ TERM_WHITE, "Your weapon stops glowing.");
+
+ /* Use the values */
+ p_ptr->tim_project_gf = gf;
+ p_ptr->tim_project_dam = dam;
+ p_ptr->tim_project_rad = rad;
+ p_ptr->tim_project_flag = flag;
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_roots" and others
+ * notice observable changes
+ */
+bool_ set_roots(int v, s16b ac, s16b dam)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_roots, v,
+ TERM_WHITE, "Roots dive into the floor from your feet.",
+ TERM_WHITE, "The roots of your feet suddenly vanish.");
+
+ /* Use the values */
+ p_ptr->tim_roots_dam = dam;
+ p_ptr->tim_roots_ac = ac;
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_(magic|water)_breath" and others
+ * notice observable changes
+ */
+bool_ set_tim_breath(int v, bool_ magical)
+{
+ if (magical)
+ {
+ return set_simple_field(
+ &p_ptr->tim_magic_breath, v,
+ TERM_WHITE, "Air seems to fill your lungs without breathing.",
+ TERM_WHITE, "You need to breathe again.");
+ }
+ else
+ {
+ return set_simple_field(
+ &p_ptr->tim_water_breath, v,
+ TERM_WHITE, "Water seems to fill your lungs.",
+ TERM_WHITE, "The water filling your lungs evaporates.");
+ }
+}
+
+/*
+ * Set "p_ptr->absorb_soul"
+ * notice observable changes
+ */
+bool_ set_absorb_soul(int v)
+{
+ return set_simple_field(
+ &p_ptr->absorb_soul, v,
+ TERM_L_DARK, "You start absorbing the souls of your foes.",
+ TERM_L_DARK, "You stop absorbing the souls of dead foes.");
+}
+
+/*
+ * Set "p_ptr->disrupt_shield"
+ * notice observable changes
+ */
+bool_ set_disrupt_shield(int v)
+{
+ return set_simple_field(
+ &p_ptr->disrupt_shield, v,
+ TERM_L_BLUE, "You feel invulnerable.",
+ TERM_L_RED, "You are more vulnerable.");
+}
+
+/*
+ * Set "p_ptr->prob_travel"
+ * notice observable changes
+ */
+bool_ set_prob_travel(int v)
+{
+ return set_simple_field(
+ &p_ptr->prob_travel, v,
+ TERM_WHITE, "You feel instable.",
+ TERM_WHITE, "You are more stable.");
+}
+
+/*
+ * Set "p_ptr->tim_invis", and "p_ptr->tim_inv_pow",
+ * notice observable changes
+ */
+bool_ set_invis(int v, int p)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_invisible, v,
+ TERM_WHITE, "You feel your body fade away.",
+ TERM_WHITE, "You are no longer invisible.");
+
+ /* Use the power value */
+ p_ptr->tim_inv_pow = p;
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_poison",
+ * notice observable changes
+ */
+bool_ set_poison(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_poison, v,
+ TERM_WHITE, "Your hands are dripping with venom.",
+ TERM_WHITE, "The venom source dries out.");
+}
+
+/*
+ * Set "no_breeds"
+ */
+bool_ set_no_breeders(int v)
+{
+ return set_simple_field(
+ &no_breeds, v,
+ TERM_WHITE, "You feel an anti-sexual aura.",
+ TERM_WHITE, "You no longer feel an anti-sexual aura.");
+}
+
+/*
+ * Set "p_ptr->tim_deadly"
+ */
+bool_ set_tim_deadly(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_deadly, v,
+ TERM_WHITE, "You feel extremely accurate.",
+ TERM_WHITE, "You are suddenly much less accurate.");
+}
+
+/*
+ * Set "p_ptr->tim_ffall"
+ */
+bool_ set_tim_ffall(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_ffall, v,
+ TERM_WHITE, "You feel very light.",
+ TERM_WHITE, "You are suddenly heavier.");
+}
+
+/*
+ * Set "p_ptr->tim_fly"
+ */
+bool_ set_tim_fly(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_fly, v,
+ TERM_WHITE, "You feel able to reach the clouds.",
+ TERM_WHITE, "You are suddenly a lot heavier.");
+}
+
+/*
+ * Set "p_ptr->meditation"
+ */
+bool_ set_meditation(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->meditation, v,
+ TERM_WHITE, "You start meditating on yourself...",
+ TERM_WHITE, "You stop your self meditation.");
+
+ /* Recalculate bonuses */
+ if (notice)
+ {
+ p_ptr->update |= (PU_MANA);
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_reflect"
+ */
+bool_ set_tim_reflect(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_reflect, v,
+ TERM_WHITE, "You start reflecting the world around you.",
+ TERM_WHITE, "You stop reflecting.");
+}
+
+/*
+ * Set "p_ptr->tim_res_time"
+ */
+bool_ set_tim_res_time(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_res_time, v,
+ TERM_WHITE, "You are now protected against space-time distortions.",
+ TERM_WHITE, "You are no longer protected against space-time distortions.");
+}
+
+/*
+ * Set "p_ptr->tim_fire_aura"
+ */
+bool_ set_tim_fire_aura(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_fire_aura, v,
+ TERM_WHITE, "You are enveloped in flames.",
+ TERM_WHITE, "You are no longer enveloped in flames.");
+}
+
+/*
+ * Set "p_ptr->strike"
+ */
+bool_ set_strike(int v)
+{
+ return set_simple_field(
+ &p_ptr->strike, v,
+ TERM_WHITE, "You feel very accurate.",
+ TERM_WHITE, "You are no longer very accurate.");
+}
+
+/*
+ * Set "p_ptr->oppose_ld"
+ */
+bool_ set_oppose_ld(int v)
+{
+ return set_simple_field(
+ &p_ptr->oppose_ld, v,
+ TERM_WHITE, "You feel protected against light's fluctuation.",
+ TERM_WHITE, "You are no longer protected against light's fluctuation.");
+}
+
+/*
+ * Set "p_ptr->oppose_cc"
+ */
+bool_ set_oppose_cc(int v)
+{
+ return set_simple_field(
+ &p_ptr->oppose_cc, v,
+ TERM_WHITE, "You feel protected against raw chaos.",
+ TERM_WHITE, "You are no longer protected against chaos.");
+}
+
+/*
+ * Set "p_ptr->oppose_ss"
+ */
+bool_ set_oppose_ss(int v)
+{
+ return set_simple_field(
+ &p_ptr->oppose_ss, v,
+ TERM_WHITE, "You feel protected against the ravages of sound and shards.",
+ TERM_WHITE, "You are no longer protected against the ravages of sound and shards.");
+}
+
+/*
+ * Set "p_ptr->oppose_nex"
+ */
+bool_ set_oppose_nex(int v)
+{
+ return set_simple_field(
+ &p_ptr->oppose_nex, v,
+ TERM_WHITE, "You feel protected against the strange forces of nexus.",
+ TERM_WHITE, "You are no longer protected against the strange forces of nexus.");
+}
+
+/*
+ * Set "p_ptr->tim_mimic", and "p_ptr->mimic_form",
+ * notice observable changes
+ */
+bool_ set_mimic(int v, int p, int level)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_mimic)
+ {
+ msg_print("You feel your body change.");
+ p_ptr->mimic_form = p;
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_mimic)
+ {
+ msg_print("You are no longer transformed.");
+ p_ptr->mimic_form = 0;
+ notice = TRUE;
+ if (p == resolve_mimic_name("Bear"))
+ {
+ s_info[SKILL_BEAR].hidden = TRUE;
+ select_default_melee();
+ }
+ p = 0;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_mimic = v;
+ p_ptr->mimic_level = level;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BODY | PU_BONUS | PU_SANITY);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->blind", notice observable changes
+ *
+ * Note the use of "PU_UN_VIEW", which is needed to memorize any terrain
+ * features which suddenly become "visible".
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ */
+bool_ set_blind(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->blind, v,
+ TERM_WHITE, "You are blind!",
+ TERM_WHITE, "You can see again.");
+
+ if (notice)
+ {
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Redraw the "blind" */
+ p_ptr->redraw |= (PR_BLIND);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_lite", notice observable changes
+ *
+ * Note the use of "PU_VIEW", which is needed to
+ * memorize any terrain features which suddenly become "visible".
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ */
+bool_ set_lite(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_lite, v,
+ TERM_WHITE, "You suddenly seem brighter!",
+ TERM_WHITE, "You are no longer bright.");
+
+ if (notice)
+ {
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->confused", notice observable changes
+ */
+bool_ set_confused(int v)
+{
+ bool_ notice =
+ set_simple_field(
+ &p_ptr->confused, v,
+ TERM_WHITE, "You are confused!",
+ TERM_WHITE, "You feel less confused now.");
+
+ if (notice)
+ {
+ /* Redraw the "confused" */
+ p_ptr->redraw |= (PR_CONFUSED);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->poisoned", notice observable changes
+ */
+bool_ set_poisoned(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->poisoned, v,
+ TERM_WHITE, "You are poisoned!",
+ TERM_WHITE, "You are no longer poisoned.");
+
+ if (notice)
+ {
+ /* Redraw the "poisoned" */
+ p_ptr->redraw |= (PR_POISONED);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->afraid", notice observable changes
+ */
+bool_ set_afraid(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->afraid, v,
+ TERM_WHITE, "You are terrified!",
+ TERM_WHITE, "You feel bolder now.");
+
+ if (notice)
+ {
+ /* Redraw the "afraid" */
+ p_ptr->redraw |= (PR_AFRAID);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->paralyzed", notice observable changes
+ */
+bool_ set_paralyzed(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->paralyzed, v,
+ TERM_WHITE, "You are paralyzed!",
+ TERM_WHITE, "You can move again.");
+
+ if (notice)
+ {
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->image", notice observable changes
+ *
+ * Note that we must redraw the map when hallucination changes.
+ */
+bool_ set_image(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->image, v,
+ TERM_WHITE, "Oh, wow! Everything looks so cosmic now!",
+ TERM_WHITE, "You can see clearly again.");
+
+ if (notice)
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD | PW_M_LIST);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->lightspeed", notice observable changes
+ */
+bool_ set_light_speed(int v)
+{
+ bool_ notice =
+ set_simple_field(
+ &p_ptr->lightspeed, v,
+ TERM_WHITE, "You feel as if time has stopped!",
+ TERM_WHITE, "You feel time returning to its normal rate.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+bool_ set_fast(int v, int p)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->fast)
+ {
+ msg_print("You feel yourself moving faster!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->fast)
+ {
+ msg_print("You feel yourself slow down.");
+ p = 0;
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->fast = v;
+ p_ptr->speed_factor = p;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->slow", notice observable changes
+ */
+bool_ set_slow(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->slow, v,
+ TERM_WHITE, "You feel yourself moving slower!",
+ TERM_WHITE, "You feel yourself speed up.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->shield", notice observable changes
+ */
+bool_ set_shield(int v, int p, s16b o, s16b d1, s16b d2)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->shield, v,
+ TERM_WHITE, "A mystic shield forms around your body!",
+ TERM_WHITE, "Your mystic shield crumbles away.");
+
+ /* Use the values */
+ p_ptr->shield_power = p;
+ p_ptr->shield_opt = o;
+ p_ptr->shield_power_opt = d1;
+ p_ptr->shield_power_opt2 = d2;
+
+ /* Notice? */
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+
+/*
+ * Set "p_ptr->blessed", notice observable changes
+ */
+bool_ set_blessed(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->blessed, v,
+ TERM_WHITE, "You feel righteous!",
+ TERM_WHITE, "The prayer has expired.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->hero", notice observable changes
+ */
+bool_ set_hero(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->hero, v,
+ TERM_WHITE, "You feel like a hero!",
+ TERM_WHITE, "The heroism wears off.");
+
+ if (notice)
+ {
+ /* Recalculate hitpoints */
+ p_ptr->update |= (PU_HP);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->holy", notice observable changes
+ */
+bool_ set_holy(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->holy, v,
+ TERM_WHITE, "You feel a holy aura around you!",
+ TERM_WHITE, "The holy aura vanishes.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->walk_water", notice observable changes
+ */
+bool_ set_walk_water(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->walk_water, v,
+ TERM_WHITE, "You feel strangely buoyant!",
+ TERM_WHITE, "You feel much less buoyant.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->shero", notice observable changes
+ */
+bool_ set_shero(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->shero, v,
+ TERM_WHITE, "You feel like a killing machine!",
+ TERM_WHITE, "You feel less berserk.");
+
+ if (notice)
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS | PU_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->protevil", notice observable changes
+ */
+bool_ set_protevil(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->protevil, v,
+ TERM_WHITE, "You feel safe from evil!",
+ TERM_WHITE, "You no longer feel safe from evil.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->protgood", notice observable changes
+ */
+bool_ set_protgood(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->protgood, v,
+ TERM_WHITE, "You feel safe from good!",
+ TERM_WHITE, "You no longer feel safe from good.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->protundead", notice observable changes
+ */
+bool_ set_protundead(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->protundead, v,
+ TERM_WHITE, "You feel safe from undead!",
+ TERM_WHITE, "You no longer feel safe from undead.");
+
+ if (notice) {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->set_shadow", notice observable changes
+ */
+bool_ set_shadow(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_wraith, v,
+ TERM_WHITE, "You leave the physical world and turn into a wraith-being!",
+ TERM_WHITE, "You feel opaque.");
+
+ if (notice)
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+
+
+/*
+ * Set "p_ptr->invuln", notice observable changes
+ */
+bool_ set_invuln(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->invuln, v,
+ TERM_L_BLUE, "Invulnerability!",
+ TERM_L_RED, "The invulnerability wears off.");
+
+ if (notice)
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+
+/*
+ * Set "p_ptr->tim_esp", notice observable changes
+ */
+bool_ set_tim_esp(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_esp, v,
+ TERM_WHITE, "You feel your consciousness expand!",
+ TERM_WHITE, "Your consciousness contracts again.");
+
+ if (notice)
+ {
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_thunder", notice observable changes
+ */
+bool_ set_tim_thunder(int v, int p1, int p2)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_thunder)
+ {
+ msg_print("The air around you charges with lightning!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_thunder)
+ {
+ msg_print("The air around you discharges.");
+ notice = TRUE;
+ p1 = p2 = 0;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_thunder = v;
+ p_ptr->tim_thunder_p1 = p1;
+ p_ptr->tim_thunder_p2 = p2;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_invis", notice observable changes
+ */
+bool_ set_tim_invis(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_invis, v,
+ TERM_WHITE, "Your eyes feel very sensitive!",
+ TERM_WHITE, "Your eyes feel less sensitive.");
+
+ if (notice)
+ {
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->tim_infra", notice observable changes
+ */
+bool_ set_tim_infra(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_infra, v,
+ TERM_WHITE, "Your eyes begin to tingle!",
+ TERM_WHITE, "Your eyes stop tingling.");
+
+ if (notice)
+ {
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->tim_mental_barrier", notice observable changes
+ */
+bool_ set_mental_barrier(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_mental_barrier, v,
+ TERM_WHITE, "Your mind grows stronger!",
+ TERM_WHITE, "Your mind is no longer especially strong.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->oppose_acid", notice observable changes
+ */
+bool_ set_oppose_acid(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_acid, v,
+ TERM_WHITE, "You feel resistant to acid!",
+ TERM_WHITE, "You feel less resistant to acid.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->oppose_elec", notice observable changes
+ */
+bool_ set_oppose_elec(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_elec, v,
+ TERM_WHITE, "You feel resistant to electricity!",
+ TERM_WHITE, "You feel less resistant to electricity.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->oppose_fire", notice observable changes
+ */
+bool_ set_oppose_fire(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_fire, v,
+ TERM_WHITE, "You feel resistant to fire!",
+ TERM_WHITE, "You feel less resistant to fire.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->oppose_cold", notice observable changes
+ */
+bool_ set_oppose_cold(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_cold, v,
+ TERM_WHITE, "You feel resistant to cold!",
+ TERM_WHITE, "You feel less resistant to cold.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->oppose_pois", notice observable changes
+ */
+bool_ set_oppose_pois(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_pois, v,
+ TERM_WHITE, "You feel resistant to poison!",
+ TERM_WHITE, "You feel less resistant to poison.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->tim_regen", notice observable changes
+ */
+bool_ set_tim_regen(int v, int p)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_regen)
+ {
+ msg_print("Your body regenerates much more quickly!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_regen)
+ {
+ p = 0;
+ msg_print("Your body regenerates much more slowly.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_regen = v;
+ p_ptr->tim_regen_pow = p;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->stun", notice observable changes
+ *
+ * Note the special code to only notice "range" changes.
+ */
+bool_ set_stun(int v)
+{
+ int old_aux, new_aux;
+ bool_ notice = FALSE;
+
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ if (PRACE_FLAG(PR1_NO_STUN)) v = 0;
+
+ /* Knocked out */
+ if (p_ptr->stun > 100)
+ {
+ old_aux = 3;
+ }
+
+ /* Heavy stun */
+ else if (p_ptr->stun > 50)
+ {
+ old_aux = 2;
+ }
+
+ /* Stun */
+ else if (p_ptr->stun > 0)
+ {
+ old_aux = 1;
+ }
+
+ /* None */
+ else
+ {
+ old_aux = 0;
+ }
+
+ /* Knocked out */
+ if (v > 100)
+ {
+ new_aux = 3;
+ }
+
+ /* Heavy stun */
+ else if (v > 50)
+ {
+ new_aux = 2;
+ }
+
+ /* Stun */
+ else if (v > 0)
+ {
+ new_aux = 1;
+ }
+
+ /* None */
+ else
+ {
+ new_aux = 0;
+ }
+
+ /* Increase cut */
+ if (new_aux > old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* Stun */
+ case 1:
+ msg_print("You have been stunned.");
+ break;
+
+ /* Heavy stun */
+ case 2:
+ msg_print("You have been heavily stunned.");
+ break;
+
+ /* Knocked out */
+ case 3:
+ msg_print("You have been knocked out.");
+ break;
+ }
+
+ if (randint(1000) < v || randint(16) == 1)
+ {
+
+ msg_print("A vicious blow hits your head.");
+ if (randint(3) == 1)
+ {
+ if (!p_ptr->sustain_int)
+ {
+ (void) do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ }
+ if (!p_ptr->sustain_wis)
+ {
+ (void) do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ }
+ }
+ else if (randint(2) == 1)
+ {
+ if (!p_ptr->sustain_int)
+ {
+ (void) do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ }
+ }
+ else
+ {
+ if (!p_ptr->sustain_wis)
+ {
+ (void) do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ }
+ }
+ }
+
+ /* Notice */
+ notice = TRUE;
+ }
+
+ /* Decrease cut */
+ else if (new_aux < old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* None */
+ case 0:
+ msg_print("You are no longer stunned.");
+ if (disturb_state) disturb(0, 0);
+ break;
+ }
+
+ /* Notice */
+ notice = TRUE;
+ }
+
+ /* Use the value */
+ p_ptr->stun = v;
+
+ /* No change */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the "stun" */
+ p_ptr->redraw |= (PR_STUN);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->cut", notice observable changes
+ *
+ * Note the special code to only notice "range" changes.
+ */
+bool_ set_cut(int v)
+{
+ int old_aux, new_aux;
+
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ if (PRACE_FLAG(PR1_NO_CUT)) v = 0;
+
+ /* Mortal wound */
+ if (p_ptr->cut > 1000)
+ {
+ old_aux = 7;
+ }
+
+ /* Deep gash */
+ else if (p_ptr->cut > 200)
+ {
+ old_aux = 6;
+ }
+
+ /* Severe cut */
+ else if (p_ptr->cut > 100)
+ {
+ old_aux = 5;
+ }
+
+ /* Nasty cut */
+ else if (p_ptr->cut > 50)
+ {
+ old_aux = 4;
+ }
+
+ /* Bad cut */
+ else if (p_ptr->cut > 25)
+ {
+ old_aux = 3;
+ }
+
+ /* Light cut */
+ else if (p_ptr->cut > 10)
+ {
+ old_aux = 2;
+ }
+
+ /* Graze */
+ else if (p_ptr->cut > 0)
+ {
+ old_aux = 1;
+ }
+
+ /* None */
+ else
+ {
+ old_aux = 0;
+ }
+
+ /* Mortal wound */
+ if (v > 1000)
+ {
+ new_aux = 7;
+ }
+
+ /* Deep gash */
+ else if (v > 200)
+ {
+ new_aux = 6;
+ }
+
+ /* Severe cut */
+ else if (v > 100)
+ {
+ new_aux = 5;
+ }
+
+ /* Nasty cut */
+ else if (v > 50)
+ {
+ new_aux = 4;
+ }
+
+ /* Bad cut */
+ else if (v > 25)
+ {
+ new_aux = 3;
+ }
+
+ /* Light cut */
+ else if (v > 10)
+ {
+ new_aux = 2;
+ }
+
+ /* Graze */
+ else if (v > 0)
+ {
+ new_aux = 1;
+ }
+
+ /* None */
+ else
+ {
+ new_aux = 0;
+ }
+
+ /* Increase cut */
+ if (new_aux > old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* Graze */
+ case 1:
+ msg_print("You have been given a graze.");
+ break;
+
+ /* Light cut */
+ case 2:
+ msg_print("You have been given a light cut.");
+ break;
+
+ /* Bad cut */
+ case 3:
+ msg_print("You have been given a bad cut.");
+ break;
+
+ /* Nasty cut */
+ case 4:
+ msg_print("You have been given a nasty cut.");
+ break;
+
+ /* Severe cut */
+ case 5:
+ msg_print("You have been given a severe cut.");
+ break;
+
+ /* Deep gash */
+ case 6:
+ msg_print("You have been given a deep gash.");
+ break;
+
+ /* Mortal wound */
+ case 7:
+ msg_print("You have been given a mortal wound.");
+ break;
+ }
+
+ /* Notice */
+ notice = TRUE;
+
+ if (randint(1000) < v || randint(16) == 1)
+ {
+ if (!p_ptr->sustain_chr)
+ {
+ msg_print("You have been horribly scarred.");
+
+ do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+ }
+ }
+ }
+
+ /* Decrease cut */
+ else if (new_aux < old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* None */
+ case 0:
+ msg_print("You are no longer bleeding.");
+ if (disturb_state) disturb(0, 0);
+ break;
+ }
+
+ /* Notice */
+ notice = TRUE;
+ }
+
+ /* Use the value */
+ p_ptr->cut = v;
+
+ /* No change */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the "cut" */
+ p_ptr->redraw |= (PR_CUT);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+void drop_from_wild()
+{
+ /* Hack -- Not if player were in normal mode in previous turn */
+ if (!p_ptr->old_wild_mode) return;
+
+ if (p_ptr->wild_mode && (!dun_level))
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ change_wild_mode();
+ p_ptr->energy = 100;
+ energy_use = 0;
+ }
+}
+
+/*
+ * Set "p_ptr->food", notice observable changes
+ *
+ * The "p_ptr->food" variable can get as large as 20000, allowing the
+ * addition of the most "filling" item, Elvish Waybread, which adds
+ * 7500 food units, without overflowing the 32767 maximum limit.
+ *
+ * Perhaps we should disturb the player with various messages,
+ * especially messages about hunger status changes. XXX XXX XXX
+ *
+ * Digestion of food is handled in "dungeon.c", in which, normally,
+ * the player digests about 20 food units per 100 game turns, more
+ * when "fast", more when "regenerating", less with "slow digestion",
+ * but when the player is "gorged", he digests 100 food units per 10
+ * game turns, or a full 1000 food units per 100 game turns.
+ *
+ * Note that the player's speed is reduced by 10 units while gorged,
+ * so if the player eats a single food ration (5000 food units) when
+ * full (15000 food units), he will be gorged for (5000/100)*10 = 500
+ * game turns, or 500/(100/5) = 25 player turns (if nothing else is
+ * affecting the player speed).
+ */
+bool_ set_food(int v)
+{
+ int old_aux, new_aux;
+
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 20000) ? 20000 : (v < 0) ? 0 : v;
+
+ /* Fainting / Starving */
+ if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ old_aux = 0;
+ }
+
+ /* Weak */
+ else if (p_ptr->food < PY_FOOD_WEAK)
+ {
+ old_aux = 1;
+ }
+
+ /* Hungry */
+ else if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ old_aux = 2;
+ }
+
+ /* Normal */
+ else if (p_ptr->food < PY_FOOD_FULL)
+ {
+ old_aux = 3;
+ }
+
+ /* Full */
+ else if (p_ptr->food < PY_FOOD_MAX)
+ {
+ old_aux = 4;
+ }
+
+ /* Gorged */
+ else
+ {
+ old_aux = 5;
+ }
+
+ /* Fainting / Starving */
+ if (v < PY_FOOD_FAINT)
+ {
+ new_aux = 0;
+ }
+
+ /* Weak */
+ else if (v < PY_FOOD_WEAK)
+ {
+ new_aux = 1;
+ }
+
+ /* Hungry */
+ else if (v < PY_FOOD_ALERT)
+ {
+ new_aux = 2;
+ }
+
+ /* Normal */
+ else if (v < PY_FOOD_FULL)
+ {
+ new_aux = 3;
+ }
+
+ /* Full */
+ else if (v < PY_FOOD_MAX)
+ {
+ new_aux = 4;
+ }
+
+ /* Gorged */
+ else
+ {
+ new_aux = 5;
+ }
+
+ /* Food increase */
+ if (new_aux > old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* Weak */
+ case 1:
+ msg_print("You are still weak.");
+ break;
+
+ /* Hungry */
+ case 2:
+ msg_print("You are still hungry.");
+ break;
+
+ /* Normal */
+ case 3:
+ msg_print("You are no longer hungry.");
+ break;
+
+ /* Full */
+ case 4:
+ msg_print("You are full!");
+ break;
+
+ /* Bloated */
+ case 5:
+ msg_print("You have gorged yourself!");
+ break;
+ }
+
+ /* Change */
+ notice = TRUE;
+ }
+
+ /* Food decrease */
+ else if (new_aux < old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* Fainting / Starving */
+ case 0:
+ msg_print("You are getting faint from hunger!");
+ drop_from_wild();
+ break;
+
+ /* Weak */
+ case 1:
+ msg_print("You are getting weak from hunger!");
+ drop_from_wild();
+ break;
+
+ /* Hungry */
+ case 2:
+ msg_print("You are getting hungry.");
+ break;
+
+ /* Normal */
+ case 3:
+ msg_print("You are no longer full.");
+ break;
+
+ /* Full */
+ case 4:
+ msg_print("You are no longer gorged.");
+ break;
+ }
+
+ /* Change */
+ notice = TRUE;
+ }
+
+ /* Use the value */
+ p_ptr->food = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw hunger */
+ p_ptr->redraw |= (PR_HUNGER);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Advance experience levels and print experience
+ */
+void check_experience(void)
+{
+ int gained = 0;
+ bool_ level_reward = FALSE;
+ bool_ level_corruption = FALSE;
+
+
+ /* Hack -- lower limit */
+ if (p_ptr->exp < 0) p_ptr->exp = 0;
+
+ /* Hack -- lower limit */
+ if (p_ptr->max_exp < 0) p_ptr->max_exp = 0;
+
+ /* Hack -- upper limit */
+ if (p_ptr->exp > PY_MAX_EXP) p_ptr->exp = PY_MAX_EXP;
+
+ /* Hack -- upper limit */
+ if (p_ptr->max_exp > PY_MAX_EXP) p_ptr->max_exp = PY_MAX_EXP;
+
+ /* Hack -- maintain "max" experience */
+ if (p_ptr->exp > p_ptr->max_exp) p_ptr->max_exp = p_ptr->exp;
+
+ /* Redraw experience */
+ p_ptr->redraw |= (PR_EXP);
+
+ /* Handle stuff */
+ handle_stuff();
+
+
+ /* Lose levels while possible */
+ while ((p_ptr->lev > 1) &&
+ (p_ptr->exp < (player_exp[p_ptr->lev - 2] * p_ptr->expfact / 100L)))
+ {
+ /* Lose a level */
+ p_ptr->lev--;
+ gained--;
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Update some stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_SANITY);
+
+ /* Redraw some stuff */
+ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+
+ /* Gain levels while possible */
+ while ((p_ptr->lev < PY_MAX_LEVEL) && (p_ptr->lev < max_plev) &&
+ (p_ptr->exp >= (player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L)))
+ {
+ /* Gain a level */
+ p_ptr->lev++;
+ gained++;
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Save the highest level */
+ if (p_ptr->lev > p_ptr->max_plv)
+ {
+ p_ptr->max_plv = p_ptr->lev;
+ if ((PRACE_FLAG(PR1_CORRUPT)) &&
+ (randint(3) == 1))
+ {
+ level_corruption = TRUE;
+ }
+ }
+
+ /* Sound */
+ sound(SOUND_LEVEL);
+
+ /* Message */
+ cmsg_format(TERM_L_GREEN, "Welcome to level %d.", p_ptr->lev);
+
+ if (p_ptr->skill_last_level < p_ptr->lev)
+ {
+ s32b pts;
+ call_lua("exec_module_info", "(s)", "d", "skill_per_level", &pts);
+
+ p_ptr->skill_last_level = p_ptr->lev;
+ p_ptr->skill_points += pts;
+ cmsg_format(TERM_L_GREEN, "You can increase %d more skills.", p_ptr->skill_points);
+ p_ptr->redraw |= PR_STUDY;
+ }
+
+ /* Gain this level's abilities */
+ apply_level_abilities(p_ptr->lev);
+
+ /* If auto-note taking enabled, write a note to the file.
+ * Only write this note when the level is gained for the first
+ * time.
+ */
+ if (take_notes && auto_notes)
+ {
+ char note[80];
+
+ /* Write note */
+ sprintf(note, "Reached level %d", p_ptr->lev);
+ add_note(note, 'L');
+ }
+
+ /* Update some stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_SANITY);
+
+ /* Redraw some stuff */
+ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ if (level_reward)
+ {
+ gain_level_reward(0);
+ level_reward = FALSE;
+ }
+
+ if (level_corruption)
+ {
+ msg_print("You feel different...");
+ corrupt_corrupted();
+ level_corruption = FALSE;
+ }
+ }
+
+ /* Hook it! */
+ process_hooks(HOOK_PLAYER_LEVEL, "(d)", gained);
+}
+/*
+ * Advance experience levels and print experience
+ */
+void check_experience_obj(object_type *o_ptr)
+{
+ /* Hack -- lower limit */
+ if (o_ptr->exp < 0) o_ptr->exp = 0;
+
+ /* Hack -- upper limit */
+ if (o_ptr->exp > PY_MAX_EXP) o_ptr->exp = PY_MAX_EXP;
+
+ /* Gain levels while possible */
+ while ((o_ptr->elevel < PY_MAX_LEVEL) &&
+ (o_ptr->exp >= (player_exp[o_ptr->elevel - 1] * 5 / 2)))
+ {
+ char buf[100];
+
+ /* Add a level */
+ o_ptr->elevel++;
+
+ /* Get object name */
+ object_desc(buf, o_ptr, 1, 0);
+ cmsg_format(TERM_L_BLUE, "%s gains a level!", buf);
+
+ /* What does it gains ? */
+ object_gain_level(o_ptr);
+ }
+}
+
+
+/*
+ * Gain experience (share it to objects if needed)
+ */
+void gain_exp(s32b amount)
+{
+ int i, num = 1;
+
+ /* Count the gaining xp objects */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (!o_ptr->k_idx) continue;
+
+ if (f4 & TR4_ART_EXP) num++;
+ }
+
+ /* Now give the xp */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (!o_ptr->k_idx) continue;
+
+ if (f4 & TR4_ART_EXP)
+ {
+ o_ptr->exp += 2 * amount / (num * 3);
+
+ /* Hack -- upper limit */
+ if (o_ptr->exp > PY_MAX_EXP) o_ptr->exp = PY_MAX_EXP;
+ }
+ }
+
+ if ((p_ptr->max_exp > 0) && (PRACE_FLAG(PR1_CORRUPT)))
+ {
+ if ((randint(p_ptr->max_exp) < amount) || (randint(12000000) < amount))
+ {
+ msg_print("You feel different...");
+ corrupt_corrupted();
+ };
+ /* 12,000,000 is equal to double Morgoth's raw XP value (60,000 * his Dlev (100))*/
+ };
+
+ /* Gain some experience */
+ p_ptr->exp += amount / num;
+
+ /* Hook it! */
+ process_hooks(HOOK_PLAYER_EXP, "(d)", amount / num);
+
+ /* Slowly recover from experience drainage */
+ if (p_ptr->exp < p_ptr->max_exp)
+ {
+ /* Gain max experience (20%) (was 10%) */
+ p_ptr->max_exp += amount / 5;
+ }
+
+ /* Check Experience */
+ check_experience();
+}
+
+
+/*
+ * Lose experience
+ */
+void lose_exp(s32b amount)
+{
+ /* Never drop below zero experience */
+ if (amount > p_ptr->exp) amount = p_ptr->exp;
+
+ /* Lose some experience */
+ p_ptr->exp -= amount;
+
+ /* Hook it! */
+ process_hooks(HOOK_PLAYER_EXP, "(d)", amount);
+
+ /* Check Experience */
+ check_experience();
+}
+
+
+
+
+/*
+ * Hack -- Return the "automatic coin type" of a monster race
+ * Used to allocate proper treasure when "Creeping coins" die
+ *
+ * XXX XXX XXX Note the use of actual "monster names"
+ */
+int get_coin_type(monster_race *r_ptr)
+{
+ cptr name = (r_name + r_ptr->name);
+
+ /* Analyze "coin" monsters */
+ if (r_ptr->d_char == '$')
+ {
+ /* Look for textual clues */
+ if (strstr(name, " copper ")) return (2);
+ if (strstr(name, " silver ")) return (5);
+ if (strstr(name, " gold ")) return (10);
+ if (strstr(name, " mithril ")) return (16);
+ if (strstr(name, " adamantite ")) return (17);
+
+ /* Look for textual clues */
+ if (strstr(name, "Copper ")) return (2);
+ if (strstr(name, "Silver ")) return (5);
+ if (strstr(name, "Gold ")) return (10);
+ if (strstr(name, "Mithril ")) return (16);
+ if (strstr(name, "Adamantite ")) return (17);
+ }
+
+ /* Assume nothing */
+ return (0);
+}
+
+/*
+ * This routine handles the production of corpses/skeletons/heads/skulls
+ * when a monster is killed.
+ */
+void place_corpse(monster_type *m_ptr)
+{
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ int x = m_ptr->fx;
+ int y = m_ptr->fy;
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* It has a physical form */
+ if (r_ptr->flags9 & RF9_DROP_CORPSE)
+ {
+ /* Wipe the object */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE));
+
+ /* Unique corpses are unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ object_aware(i_ptr);
+ i_ptr->name1 = 201;
+ }
+
+ /* Calculate length of time before decay */
+ i_ptr->pval = r_ptr->weight + rand_int(r_ptr->weight);
+
+ /* Set weight */
+ i_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1;
+
+ /* Remember what we are */
+ i_ptr->pval2 = m_ptr->r_idx;
+
+ /* Some hp */
+ i_ptr->pval3 = ((maxroll(r_ptr->hdice, r_ptr->hside) + p_ptr->mhp) / 2);
+ i_ptr->pval3 -= randint(i_ptr->pval3) / 3;
+
+ i_ptr->found = OBJ_FOUND_MONSTER;
+ i_ptr->found_aux1 = m_ptr->r_idx;
+ i_ptr->found_aux2 = m_ptr->ego;
+ i_ptr->found_aux3 = dungeon_type;
+ i_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(i_ptr, -1, y, x);
+ }
+
+ /* The creature is an animated skeleton. */
+ if (!(r_ptr->flags9 & RF9_DROP_CORPSE) && (r_ptr->flags9 & RF9_DROP_SKELETON))
+ {
+ /* Wipe the object */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKELETON));
+
+ /* Unique corpses are unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ object_aware(i_ptr);
+ i_ptr->name1 = 201;
+ }
+
+ i_ptr->pval = 0;
+
+ /* Set weight */
+ i_ptr->weight = (r_ptr->weight / 4 + rand_int(r_ptr->weight) / 40) + 1;
+
+ /* Remember what we are */
+ i_ptr->pval2 = m_ptr->r_idx;
+
+ i_ptr->found = OBJ_FOUND_MONSTER;
+ i_ptr->found_aux1 = m_ptr->r_idx;
+ i_ptr->found_aux2 = m_ptr->ego;
+ i_ptr->found_aux3 = dungeon_type;
+ i_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(i_ptr, -1, y, x);
+ }
+
+}
+
+
+/*
+ * Handle the "death" of a monster.
+ *
+ * Disperse treasures centered at the monster location based on the
+ * various flags contained in the monster flags fields.
+ *
+ * Check for "Quest" completion when a quest monster is killed.
+ *
+ * Note that only the player can induce "monster_death()" on Uniques.
+ * Thus (for now) all Quest monsters should be Uniques.
+ *
+ * Note that monsters can now carry objects, and when a monster dies,
+ * it drops all of its objects, which may disappear in crowded rooms.
+ */
+void monster_death(int m_idx)
+{
+ int i, y, x, ny, nx;
+
+ int dump_item = 0;
+ int dump_gold = 0;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ bool_ visible = (m_ptr->ml || (r_ptr->flags1 & (RF1_UNIQUE)));
+
+
+ bool_ create_stairs = FALSE;
+ int force_coin = get_coin_type(r_ptr);
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get the location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_MONSTER_DEATH, "(d)", m_idx);
+
+ /* If companion dies, take note */
+ if (m_ptr->status == MSTATUS_COMPANION) p_ptr->companion_killed++;
+
+ /* Handle reviving if undead */
+ if ((p_ptr->necro_extra & CLASS_UNDEAD) && p_ptr->necro_extra2)
+ {
+ p_ptr->necro_extra2--;
+
+ if (!p_ptr->necro_extra2)
+ {
+ msg_print("Your death has been avenged -- you return to life.");
+ p_ptr->necro_extra &= ~CLASS_UNDEAD;
+
+ /* Display the hitpoints */
+ p_ptr->update |= (PU_HP);
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else
+ {
+ msg_format("You still have to kill %d monster%s.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s");
+ }
+ }
+
+ /* Handle the possibility of player vanquishing arena combatant -KMW- */
+ if (p_ptr->inside_arena)
+ {
+ p_ptr->exit_bldg = TRUE;
+ msg_print("Victorious! You're on your way to becoming Champion.");
+ p_ptr->arena_number++;
+ }
+
+ /* If the doppleganger die, the variable must be set accordingly */
+ if (r_ptr->flags9 & RF9_DOPPLEGANGER) doppleganger = 0;
+
+ /* Drop objects being carried */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Paranoia */
+ o_ptr->held_m_idx = 0;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Copy the object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ if (q_ptr->tval == TV_GOLD) dump_gold++;
+ else dump_item++;
+
+ /* Drop it */
+ drop_near(q_ptr, -1, y, x);
+ }
+
+ /* Forget objects */
+ m_ptr->hold_o_idx = 0;
+
+ /* Average dungeon and monster levels */
+ object_level = (dun_level + m_ptr->level) / 2;
+
+ /* Mega^2-hack -- destroying the Stormbringer gives it us! */
+ if (strstr((r_name + r_ptr->name), "Stormbringer"))
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Prepare to make the Stormbringer */
+ object_prep(q_ptr, lookup_kind(TV_SWORD, SV_BLADE_OF_CHAOS));
+
+ /* Mega-Hack -- Name the sword */
+
+ q_ptr->art_name = quark_add("'Stormbringer'");
+ q_ptr->to_h = 16;
+ q_ptr->to_d = 16;
+ q_ptr->ds = 6;
+ q_ptr->dd = 6;
+ q_ptr->pval = 2;
+
+ q_ptr->art_flags1 |= ( TR1_VAMPIRIC | TR1_STR | TR1_CON | TR1_BLOWS );
+ q_ptr->art_flags2 |= ( TR2_FREE_ACT | TR2_HOLD_LIFE |
+ TR2_RES_NEXUS | TR2_RES_CHAOS | TR2_RES_NETHER |
+ TR2_RES_CONF ); /* No longer resist_disen */
+ q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD);
+ /* Just to be sure */
+
+ q_ptr->art_flags3 |= TR3_NO_TELE; /* How's that for a downside? */
+
+ /* For game balance... */
+ q_ptr->art_flags3 |= (TR3_CURSED | TR3_HEAVY_CURSE);
+ q_ptr->ident |= IDENT_CURSED;
+
+ if (randint(2) == 1)
+ q_ptr->art_flags3 |= (TR3_DRAIN_EXP);
+ else
+ q_ptr->art_flags3 |= (TR3_AGGRAVATE);
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+
+ /*
+ * Mega^3-hack: killing a 'Warrior of the Dawn' is likely to
+ * spawn another in the fallen one's place!
+ */
+ else if (strstr((r_name + r_ptr->name), "the Dawn"))
+ {
+ if (!(randint(20) == 13))
+ {
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 100;
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 20);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (attempts > 0)
+ {
+ if (is_friend(m_ptr) > 0)
+ {
+ if (summon_specific_friendly(wy, wx, 100, SUMMON_DAWN, FALSE))
+ {
+ if (player_can_see_bold(wy, wx))
+ msg_print ("A new warrior steps forth!");
+ }
+ }
+ else
+ {
+ if (summon_specific(wy, wx, 100, SUMMON_DAWN))
+ {
+ if (player_can_see_bold(wy, wx))
+ msg_print ("A new warrior steps forth!");
+ }
+ }
+ }
+ }
+ }
+
+ /* One more ultra-hack: An Unmaker goes out with a big bang! */
+ else if (strstr((r_name + r_ptr->name), "Unmaker"))
+ {
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+ (void)project(m_idx, 6, y, x, 100, GF_CHAOS, flg);
+ }
+ /* Pink horrors are replaced with 2 Blue horrors */
+ else if (strstr((r_name + r_ptr->name), "ink horror"))
+ {
+ for (i = 0; i < 2; i++)
+ {
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 100;
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 3);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (attempts > 0)
+ {
+ if (summon_specific(wy, wx, 100, SUMMON_BLUE_HORROR))
+ {
+ if (player_can_see_bold(wy, wx))
+ msg_print ("A blue horror appears!");
+ }
+ }
+ }
+ }
+
+ /* Mega-Hack -- drop "winner" treasures */
+ else if (r_ptr->flags1 & (RF1_DROP_CHOSEN))
+ {
+ if (strstr((r_name + r_ptr->name), "Morgoth, Lord of Darkness"))
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Mega-Hack -- Prepare to make "Grond" */
+ object_prep(q_ptr, lookup_kind(TV_HAFTED, SV_GROND));
+
+ /* Mega-Hack -- Mark this item as "Grond" */
+ q_ptr->name1 = ART_GROND;
+
+ /* Mega-Hack -- Actually create "Grond" */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Mega-Hack -- Prepare to make "Morgoth" */
+ object_prep(q_ptr, lookup_kind(TV_CROWN, SV_MORGOTH));
+
+ /* Mega-Hack -- Mark this item as "Morgoth" */
+ q_ptr->name1 = ART_MORGOTH;
+
+ /* Mega-Hack -- Actually create "Morgoth" */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+ else if (strstr((r_name + r_ptr->name), "Smeagol"))
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ object_wipe(q_ptr);
+
+ /* Mega-Hack -- Prepare to make a ring of invisibility */
+ object_prep(q_ptr, lookup_kind(TV_RING, SV_RING_INVIS));
+ q_ptr->number = 1;
+
+ apply_magic(q_ptr, -1, TRUE, TRUE, FALSE);
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+ else if (r_ptr->flags7 & RF7_NAZGUL)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ object_wipe(q_ptr);
+
+ /* Mega-Hack -- Prepare to make a Ring of Power */
+ object_prep(q_ptr, lookup_kind(TV_RING, SV_RING_SPECIAL));
+ q_ptr->number = 1;
+
+ apply_magic(q_ptr, -1, TRUE, TRUE, FALSE);
+
+ /* Create a random artifact */
+ create_artifact(q_ptr, TRUE, FALSE);
+
+ /* Save the inscription */
+ q_ptr->art_name = quark_add(format("of %s", r_name + r_ptr->name));
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+ else
+ {
+ byte a_idx = 0;
+ int chance = 0;
+ int I_kind = 0;
+
+ if (strstr((r_name + r_ptr->name), "Marda, rider of the Gold Laronth"))
+ {
+ a_idx = ART_MARDA;
+ chance = 50;
+ }
+ else if (strstr((r_name + r_ptr->name), "Saruman of Many Colours"))
+ {
+ a_idx = ART_PALANTIR;
+ chance = 30;
+ }
+ else if (strstr((r_name + r_ptr->name), "Hagen, son of Alberich"))
+ {
+ a_idx = ART_NIMLOTH;
+ chance = 66;
+ }
+ else if (strstr((r_name + r_ptr->name), "Durin's Bane"))
+ {
+ a_idx = ART_CALRIS;
+ chance = 60;
+ }
+ else if (strstr((r_name + r_ptr->name), "Gothmog, the High Captain of Balrogs"))
+ {
+ a_idx = ART_GOTHMOG;
+ chance = 50;
+ }
+ else if (strstr((r_name + r_ptr->name), "Eol, the Dark Elf"))
+ {
+ a_idx = ART_ANGUIREL;
+ chance = 50;
+ }
+
+ if ((a_idx > 0) && ((randint(99) < chance) || (wizard)))
+ {
+ if (a_info[a_idx].cur_num == 0)
+ {
+ artifact_type *a_ptr = &a_info[a_idx];
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = a_idx;
+
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+
+ /* Hack -- acquire "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) q_ptr->ident |= (IDENT_CURSED);
+
+ random_artifact_resistance(q_ptr);
+
+ a_info[a_idx].cur_num = 1;
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, y, x);
+ }
+ }
+ }
+ }
+
+ /* Hack - the protected monsters must be advanged */
+ else if (r_ptr->flags9 & RF9_WYRM_PROTECT)
+ {
+ int xx = x, yy = y;
+ int attempts = 100;
+
+ cmsg_print(TERM_VIOLET, "This monster was under the protection of a Great Wyrm of Power!");
+
+ do
+ {
+ scatter(&yy, &xx, y, x, 6);
+ }
+ while (!(in_bounds(yy, xx) && cave_floor_bold(yy, xx)) && --attempts);
+
+ place_monster_aux(yy, xx, test_monster_name("Great Wyrm of Power"), FALSE, FALSE, m_ptr->status);
+ }
+
+ /* Let monsters explode! */
+ for (i = 0; i < 4; i++)
+ {
+ if (m_ptr->blow[i].method == RBM_EXPLODE)
+ {
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+ int typ = GF_MISSILE;
+ int d_dice = m_ptr->blow[i].d_dice;
+ int d_side = m_ptr->blow[i].d_side;
+ int damage = damroll(d_dice, d_side);
+
+ switch (m_ptr->blow[i].effect)
+ {
+ case RBE_HURT:
+ typ = GF_MISSILE;
+ break;
+ case RBE_POISON:
+ typ = GF_POIS;
+ break;
+ case RBE_UN_BONUS:
+ typ = GF_DISENCHANT;
+ break;
+ case RBE_UN_POWER:
+ typ = GF_MISSILE;
+ break; /* ToDo: Apply the correct effects */
+ case RBE_EAT_GOLD:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EAT_ITEM:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EAT_FOOD:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EAT_LITE:
+ typ = GF_MISSILE;
+ break;
+ case RBE_ACID:
+ typ = GF_ACID;
+ break;
+ case RBE_ELEC:
+ typ = GF_ELEC;
+ break;
+ case RBE_FIRE:
+ typ = GF_FIRE;
+ break;
+ case RBE_COLD:
+ typ = GF_COLD;
+ break;
+ case RBE_BLIND:
+ typ = GF_MISSILE;
+ break;
+ case RBE_HALLU:
+ typ = GF_CONFUSION;
+ break;
+ case RBE_CONFUSE:
+ typ = GF_CONFUSION;
+ break;
+ case RBE_TERRIFY:
+ typ = GF_MISSILE;
+ break;
+ case RBE_PARALYZE:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_STR:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_DEX:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_CON:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_INT:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_WIS:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_CHR:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_ALL:
+ typ = GF_MISSILE;
+ break;
+ case RBE_PARASITE:
+ typ = GF_MISSILE;
+ break;
+ case RBE_SHATTER:
+ typ = GF_ROCKET;
+ break;
+ case RBE_EXP_10:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EXP_20:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EXP_40:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EXP_80:
+ typ = GF_MISSILE;
+ break;
+ case RBE_DISEASE:
+ typ = GF_POIS;
+ break;
+ case RBE_TIME:
+ typ = GF_TIME;
+ break;
+ case RBE_SANITY:
+ typ = GF_MISSILE;
+ break;
+ }
+
+ project(m_idx, 3, y, x, damage, typ, flg);
+ break;
+ }
+ }
+
+ if ((!force_coin) && (magik(10 + get_skill_scale(SKILL_PRESERVATION, 75))) && (!(m_ptr->mflag & MFLAG_NO_DROP)))
+ place_corpse(m_ptr);
+
+ /* Take note of any dropped treasure */
+ if (visible && (dump_item || dump_gold))
+ {
+ /* Take notes on treasure */
+ lore_treasure(m_idx, dump_item, dump_gold);
+ }
+
+ /* Current quest */
+ i = p_ptr->inside_quest;
+
+ /* Create a magical staircase */
+ if (create_stairs && (dun_level < d_info[dungeon_type].maxdepth))
+ {
+ int i, j;
+
+ for (i = -1; i <= 1; i++)
+ for (j = -1; j <= 1; j++)
+ if (!(f_info[cave[y + j][x + i].feat].flags1 & FF1_PERMANENT)) cave_set_feat(y + j, x + i, d_info[dungeon_type].floor1);
+
+ /* Stagger around */
+ while (!cave_valid_bold(y, x))
+ {
+ int d = 1;
+
+ /* Pick a location */
+ scatter(&ny, &nx, y, x, d);
+
+ /* Stagger */
+ y = ny;
+ x = nx;
+ }
+
+ /* Destroy any objects */
+ delete_object(y, x);
+
+ /* Explain the staircase */
+ msg_print("A magical staircase appears...");
+
+ /* Create stairs down */
+ cave_set_feat(y, x, FEAT_MORE);
+
+ /* Remember to update everything */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS);
+ }
+}
+
+
+
+
+/*
+ * Decreases monsters hit points, handling monster death.
+ *
+ * We return TRUE if the monster has been killed (and deleted).
+ *
+ * We announce monster death (using an optional "death message"
+ * if given, and a otherwise a generic killed/destroyed message).
+ *
+ * Only "physical attacks" can induce the "You have slain" message.
+ * Missile and Spell attacks will induce the "dies" message, or
+ * various "specialized" messages. Note that "You have destroyed"
+ * and "is destroyed" are synonyms for "You have slain" and "dies".
+ *
+ * Hack -- unseen monsters yield "You have killed it." message.
+ *
+ * Added fear (DGK) and check whether to print fear messages -CWS
+ *
+ * Genericized name, sex, and capitilization -BEN-
+ *
+ * Hack -- we "delay" fear messages by passing around a "fear" flag.
+ *
+ * XXX XXX XXX Consider decreasing monster experience over time, say,
+ * by using "(m_exp * m_lev * (m_lev)) / (p_lev * (m_lev + n_killed))"
+ * instead of simply "(m_exp * m_lev) / (p_lev)", to make the first
+ * monster worth more than subsequent monsters. This would also need
+ * to induce changes in the monster recall code.
+ */
+bool_ mon_take_hit(int m_idx, int dam, bool_ *fear, cptr note)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ s32b div, new_exp, new_exp_frac;
+
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Some mosnters are immune to death */
+ if (r_ptr->flags7 & RF7_NO_DEATH) return FALSE;
+
+ /* Wake it up */
+ m_ptr->csleep = 0;
+
+ /* Hurt it */
+ m_ptr->hp -= dam;
+
+ /* It is dead now */
+ if (m_ptr->hp < 0)
+ {
+ char m_name[80];
+
+ /* Lets face it, you cannot get rid of a possessor that easily */
+ if (m_ptr->possessor)
+ {
+ ai_deincarnate(m_idx);
+
+ return FALSE;
+ }
+
+ /* Extract monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ if ((r_ptr->flags7 & RF7_DG_CURSE) && (randint(2) == 1))
+ {
+ int curses = 2 + randint(5);
+
+ cmsg_format(TERM_VIOLET, "%^s puts a terrible Morgothian curse on you!", m_name);
+ curse_equipment_dg(100, 50);
+
+ do
+ {
+ activate_dg_curse();
+ }
+ while (--curses);
+ }
+
+ if (speak_unique && (r_ptr->flags2 & (RF2_CAN_SPEAK)))
+ {
+ char line_got[80];
+ /* Dump a message */
+
+ get_rnd_line("mondeath.txt", line_got);
+ msg_format("%^s says: %s", m_name, line_got);
+ }
+
+
+ /* Make a sound */
+ sound(SOUND_KILL);
+
+ /* Death by Missile/Spell attack */
+ if (note)
+ {
+ cmsg_format(TERM_L_RED, "%^s%s", m_name, note);
+ }
+
+ /* Death by physical attack -- invisible monster */
+ else if (!m_ptr->ml)
+ {
+ cmsg_format(TERM_L_RED, "You have killed %s.", m_name);
+ }
+
+ /* Death by Physical attack -- non-living monster */
+ else if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ cmsg_format(TERM_L_RED, "You have destroyed %s.", m_name);
+ }
+
+ /* Death by Physical attack -- living monster */
+ else
+ {
+ cmsg_format(TERM_L_RED, "You have slain %s.", m_name);
+ }
+
+ /* Maximum player level */
+ div = p_ptr->max_plv;
+
+ if (m_ptr->status < MSTATUS_FRIEND)
+ {
+ /* Give some experience for the kill */
+ new_exp = ((long)r_ptr->mexp * m_ptr->level) / div;
+
+ /* Handle fractional experience */
+ new_exp_frac = ((((long)r_ptr->mexp * m_ptr->level) % div)
+ * 0x10000L / div) + p_ptr->exp_frac;
+
+ /* Keep track of experience */
+ if (new_exp_frac >= 0x10000L)
+ {
+ new_exp++;
+ p_ptr->exp_frac = new_exp_frac - 0x10000L;
+ }
+ else
+ {
+ p_ptr->exp_frac = new_exp_frac;
+ }
+
+ /* Gain experience */
+ gain_exp(new_exp);
+ }
+
+ if (!note)
+ {
+ object_type *o_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Access the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Can the weapon gain levels ? */
+ if ((o_ptr->k_idx) && (f4 & TR4_LEVELS))
+ {
+ /* Give some experience for the kill */
+ new_exp = ((long)r_ptr->mexp * m_ptr->level) / (div * 2);
+
+ /* Gain experience */
+ o_ptr->exp += new_exp;
+ check_experience_obj(o_ptr);
+ }
+ }
+
+ /* When the player kills a Unique, it stays dead */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ r_ptr->max_num = 0;
+ }
+
+ /* Generate treasure */
+ monster_death(m_idx);
+
+ /* Eru doesn't appreciate good monster death */
+ if (r_ptr->flags3 & RF3_GOOD)
+ {
+ inc_piety(GOD_ERU, -7 * m_ptr->level);
+ inc_piety(GOD_MANWE, -10 * m_ptr->level);
+ inc_piety(GOD_MELKOR, 3 * m_ptr->level);
+ }
+ else
+ {
+ inc_piety(GOD_MELKOR, 1 + m_ptr->level / 2);
+ }
+
+ /* Manwe appreciate evil monster death */
+ if (r_ptr->flags3 & RF3_EVIL)
+ {
+ int inc = m_ptr->level / 2;
+
+ if (!inc) inc = 1;
+ PRAY_GOD(GOD_MANWE) inc_piety(GOD_MANWE, inc);
+
+ if (inc < 2) inc = 2;
+ inc_piety(GOD_TULKAS, inc / 2);
+ PRAY_GOD(GOD_TULKAS)
+ {
+ inc_piety(GOD_TULKAS, inc / 2);
+ if (r_ptr->flags3 & RF3_DEMON) inc_piety(GOD_TULKAS, inc);
+ }
+ }
+
+ /* Yavanna likes when corruption is destroyed */
+ if ((r_ptr->flags3 & RF3_NONLIVING) || (r_ptr->flags3 & RF3_DEMON) || (r_ptr->flags3 & RF3_UNDEAD))
+ {
+ int inc = m_ptr->level / 2;
+
+ if (!inc) inc = 1;
+ inc_piety(GOD_YAVANNA, inc);
+ }
+
+ /* Yavanna doesnt like any killing in her name */
+ PRAY_GOD(GOD_YAVANNA)
+ {
+ int inc = m_ptr->level / 2;
+
+ if (!inc) inc = 1;
+ inc_piety(GOD_YAVANNA, -inc);
+
+ /* Killing animals in her name is a VERY bad idea */
+ if (r_ptr->flags3 & RF3_ANIMAL)
+ inc_piety(GOD_YAVANNA, -(inc * 3));
+ }
+
+ /* SHould we absorb its soul? */
+ if (p_ptr->absorb_soul && (!(r_ptr->flags3 & RF3_UNDEAD)) && (!(r_ptr->flags3 & RF3_NONLIVING)))
+ {
+ msg_print("You absorb the life of the dying soul.");
+ hp_player(1 + (m_ptr->level / 2) + get_skill_scale(SKILL_NECROMANCY, 40));
+ }
+
+ /*
+ * XXX XXX XXX Mega-Hack -- Remove random quest rendered
+ * impossible
+ */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ int i;
+
+ /* Search for all the random quests */
+ for (i = 0; i < MAX_RANDOM_QUEST; i++)
+ {
+ random_quest *q_ptr = &random_quests[i];
+
+ /* Ignore invalid entries */
+ if (q_ptr->type == 0) continue;
+
+ /* It's done */
+ if (q_ptr->done) continue;
+
+ /*
+ * XXX XXX XXX Not yet completed quest is
+ * to kill a unique you've just killed
+ */
+ if (r_ptr == &r_info[q_ptr->r_idx])
+ {
+ /* Invalidate it */
+ q_ptr->type = 0;
+ }
+ }
+ }
+
+ /* If the player kills a Unique, and the notes options are on, write a note */
+ if ((r_ptr->flags1 & RF1_UNIQUE) && take_notes && auto_notes)
+ {
+ char note[80];
+
+ /* Get true name even if blinded/hallucinating */
+ cptr monst = (r_name + r_ptr->name);
+
+ /* Write note */
+ sprintf(note, "Killed %s", monst);
+
+ add_note(note, 'U');
+ }
+
+ /* Recall even invisible uniques or winners */
+ if (m_ptr->ml || (r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ /* Count kills this life */
+ if (r_ptr->r_pkills < MAX_SHORT) r_ptr->r_pkills++;
+
+ /* Count kills in all lives */
+ if (r_ptr->r_tkills < MAX_SHORT) r_ptr->r_tkills++;
+
+ /* Hack -- Auto-recall */
+ monster_race_track(m_ptr->r_idx, m_ptr->ego);
+ }
+
+ /* Delete the monster */
+ delete_monster_idx(m_idx);
+
+ /* Not afraid */
+ (*fear) = FALSE;
+
+ /* Monster is dead */
+ return (TRUE);
+ }
+
+
+#ifdef ALLOW_FEAR
+
+ /* Mega-Hack -- Pain cancels fear */
+ if (m_ptr->monfear && (dam > 0))
+ {
+ int tmp = randint(dam);
+
+ /* Cure a little fear */
+ if (tmp < m_ptr->monfear)
+ {
+ /* Reduce fear */
+ m_ptr->monfear -= tmp;
+ }
+
+ /* Cure all the fear */
+ else
+ {
+ /* Cure fear */
+ m_ptr->monfear = 0;
+
+ /* No more fear */
+ (*fear) = FALSE;
+ }
+ }
+
+ /* Sometimes a monster gets scared by damage */
+ if (!m_ptr->monfear && !(r_ptr->flags3 & (RF3_NO_FEAR)))
+ {
+ int percentage;
+
+ /* Percentage of fully healthy */
+ percentage = (100L * m_ptr->hp) / m_ptr->maxhp;
+
+ /*
+ * Run (sometimes) if at 10% or less of max hit points,
+ * or (usually) when hit for half its current hit points
+ */
+ if (((percentage <= 10) && (rand_int(10) < percentage)) ||
+ ((dam >= m_ptr->hp) && (rand_int(100) < 80)))
+ {
+ /* Hack -- note fear */
+ (*fear) = TRUE;
+
+ /* XXX XXX XXX Hack -- Add some timed fear */
+ m_ptr->monfear = (randint(10) +
+ (((dam >= m_ptr->hp) && (percentage > 7)) ?
+ 20 : ((11 - percentage) * 5)));
+ }
+ }
+
+#endif
+
+
+ /* Not dead yet */
+ return (FALSE);
+}
+
+
+/*
+ * Get term size and calculate screen size
+ */
+void get_screen_size(int *wid_p, int *hgt_p)
+{
+ Term_get_size(wid_p, hgt_p);
+ *hgt_p -= ROW_MAP + 1;
+ *wid_p -= COL_MAP + 1;
+ if (use_bigtile) *wid_p /= 2;
+}
+
+/*
+ * Calculates current boundaries
+ * Called below.
+ */
+static void panel_bounds(void)
+{
+ int wid, hgt;
+
+ /* Retrieve current screen size */
+ get_screen_size(&wid, &hgt);
+
+ /* + 24 - 1 - 2 = + 21 */
+ panel_row_max = panel_row_min + hgt - 1;
+ panel_row_prt = panel_row_min - ROW_MAP;
+
+ /* Paranoia -- Boundary check */
+ if (panel_row_max > cur_hgt - 1) panel_row_max = cur_hgt - 1;
+
+ panel_col_max = panel_col_min + wid - 1;
+ panel_col_prt = panel_col_min - COL_MAP;
+
+ /* Paranoia -- Boundary check */
+ if (panel_col_max > cur_wid - 1) panel_col_max = cur_wid - 1;
+}
+
+
+/*
+ * Handle a request to change the current panel
+ *
+ * Return TRUE if the panel was changed.
+ *
+ * Also used in do_cmd_locate()
+ */
+bool_ change_panel(int dy, int dx)
+{
+ int y, x;
+ int wid, hgt;
+
+ /* Get size */
+ get_screen_size(&wid, &hgt);
+
+ /* Apply the motion */
+ y = panel_row_min + dy * (hgt / 2);
+ x = panel_col_min + dx * (wid / 2);
+
+ /* Calculate bounds */
+ if (y > cur_hgt - hgt) y = cur_hgt - hgt;
+ if (y < 0) y = 0;
+ if (x > cur_wid - wid) x = cur_wid - wid;
+ if (x < 0) x = 0;
+
+ /* Handle changes */
+ if ((y != panel_row_min) || (x != panel_col_min))
+ {
+ /* Save the new panel info */
+ panel_row_min = y;
+ panel_col_min = x;
+
+ /* Recalculate the boundaries */
+ panel_bounds();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* No changes */
+ return (FALSE);
+}
+
+
+/*
+ * Given an row (y) and col (x), this routine detects when a move
+ * off the screen has occurred and figures new borders. -RAK-
+ *
+ * "Update" forces a "full update" to take place.
+ *
+ * The map is reprinted if necessary, and "TRUE" is returned.
+ */
+void verify_panel(void)
+{
+ int y = p_ptr->py;
+ int x = p_ptr->px;
+
+ int wid, hgt;
+
+ int prow_min;
+ int pcol_min;
+
+ int panel_hgt, panel_wid;
+
+ int max_prow_min;
+ int max_pcol_min;
+
+
+ /*
+ * Make sure panel_row/col_max have correct values -- now taken care of
+ * by the hook function below, which eliminates glitches, but does it
+ * in a very hackish way XXX XXX XXX
+ */
+ /* panel_bounds(); */
+
+ /* Get size */
+ get_screen_size(&wid, &hgt);
+
+ /* Calculate scroll amount */
+ panel_hgt = hgt / 2;
+ panel_wid = wid / 2;
+
+ /* Upper boundary of panel_row/col_min */
+ max_prow_min = cur_hgt - hgt;
+ max_pcol_min = cur_wid - wid;
+
+ /* Boundary check */
+ if (max_prow_min < 0) max_prow_min = 0;
+ if (max_pcol_min < 0) max_pcol_min = 0;
+
+ /* An option: center on player */
+ if (center_player)
+ {
+ /* Center vertically */
+ prow_min = y - panel_hgt;
+
+ /* Boundary check */
+ if (prow_min < 0) prow_min = 0;
+ else if (prow_min > max_prow_min) prow_min = max_prow_min;
+
+ /* Center horizontally */
+ pcol_min = x - panel_wid;
+
+ /* Boundary check */
+ if (pcol_min < 0) pcol_min = 0;
+ else if (pcol_min > max_pcol_min) pcol_min = max_pcol_min;
+ }
+
+ /* No centering */
+ else
+ {
+ prow_min = panel_row_min;
+ pcol_min = panel_col_min;
+
+ /* Scroll screen when 2 grids from top/bottom edge */
+ if (y > panel_row_max - 2)
+ {
+ while (y > prow_min + hgt - 2)
+ {
+ prow_min += panel_hgt;
+ }
+
+ if (prow_min > max_prow_min) prow_min = max_prow_min;
+ }
+
+ if (y < panel_row_min + 2)
+ {
+ while (y < prow_min + 2)
+ {
+ prow_min -= panel_hgt;
+ }
+
+ if (prow_min < 0) prow_min = 0;
+ }
+
+ /* Scroll screen when 4 grids from left/right edge */
+ if (x > panel_col_max - 4)
+ {
+ while (x > pcol_min + wid - 4)
+ {
+ pcol_min += panel_wid;
+ }
+
+ if (pcol_min > max_pcol_min) pcol_min = max_pcol_min;
+ }
+
+ if (x < panel_col_min + 4)
+ {
+ while (x < pcol_min + 4)
+ {
+ pcol_min -= panel_wid;
+ }
+
+ if (pcol_min < 0) pcol_min = 0;
+ }
+ }
+
+ /* Check for "no change" */
+ if ((prow_min == panel_row_min) && (pcol_min == panel_col_min)) return;
+
+ /* Save the new panel info */
+ panel_row_min = prow_min;
+ panel_col_min = pcol_min;
+
+ /* Hack -- optional disturb on "panel change" */
+ if (disturb_panel && !center_player) disturb(0, 0);
+
+ /* Recalculate the boundaries */
+ panel_bounds();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+/*
+ * Map resizing whenever the main term changes size
+ */
+void resize_map(void)
+{
+ /* Only if the dungeon exists */
+ if (!character_dungeon) return;
+
+ /* Mega-Hack -- No panel yet, assume illegal panel */
+ panel_row_min = cur_hgt;
+ panel_row_max = 0;
+ panel_col_min = cur_wid;
+ panel_col_max = 0;
+
+ /* Select player panel */
+ verify_panel();
+
+ /*
+ * The following should be the same as the main window code
+ * in the do_cmd_redraw()
+ */
+
+ /* Combine and reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Update torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ /* Forget and update view */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw everything */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+
+ /* Hack -- update */
+ handle_stuff();
+
+ /* Redraw */
+ Term_redraw();
+
+ /* Refresh */
+ Term_fresh();
+}
+
+
+/*
+ * Redraw a term when it is resized
+ */
+void resize_window(void)
+{
+ /* Only if the dungeon exists */
+ if (!character_dungeon) return;
+
+ /* Hack -- Activate the Angband window for the redraw */
+ Term_activate(&term_screen[0]);
+
+ /* Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_M_LIST | PW_MESSAGE | PW_OVERHEAD |
+ PW_MONSTER | PW_OBJECT);
+
+
+ /* Hack -- update */
+ handle_stuff();
+
+ /* Refresh */
+ Term_fresh();
+}
+
+
+
+
+/*
+ * Monster health description
+ */
+cptr look_mon_desc(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ bool_ living = TRUE;
+ int perc;
+
+
+ /* Determine if the monster is "living" (vs "undead") */
+ if (r_ptr->flags3 & (RF3_UNDEAD)) living = FALSE;
+ if (r_ptr->flags3 & (RF3_DEMON)) living = FALSE;
+ if (r_ptr->flags3 & (RF3_NONLIVING)) living = FALSE;
+ if (strchr("Egv", r_ptr->d_char)) living = FALSE;
+
+
+ /* Healthy monsters */
+ if (m_ptr->hp >= m_ptr->maxhp)
+ {
+ /* No damage */
+ return (living ? "unhurt" : "undamaged");
+ }
+
+
+ /* Calculate a health "percentage" */
+ perc = 100L * m_ptr->hp / m_ptr->maxhp;
+
+ if (perc >= 60)
+ {
+ return (living ? "somewhat wounded" : "somewhat damaged");
+ }
+
+ if (perc >= 25)
+ {
+ return (living ? "wounded" : "damaged");
+ }
+
+ if (perc >= 10)
+ {
+ return (living ? "badly wounded" : "badly damaged");
+ }
+
+ return (living ? "almost dead" : "almost destroyed");
+}
+
+
+
+/*
+ * Angband sorting algorithm -- quick sort in place
+ *
+ * Note that the details of the data we are sorting is hidden,
+ * and we rely on the "ang_sort_comp()" and "ang_sort_swap()"
+ * function hooks to interact with the data, which is given as
+ * two pointers, and which may have any user-defined form.
+ */
+void ang_sort_aux(vptr u, vptr v, int p, int q)
+{
+ int z, a, b;
+
+ /* Done sort */
+ if (p >= q) return;
+
+ /* Pivot */
+ z = p;
+
+ /* Begin */
+ a = p;
+ b = q;
+
+ /* Partition */
+ while (TRUE)
+ {
+ /* Slide i2 */
+ while (!(*ang_sort_comp)(u, v, b, z)) b--;
+
+ /* Slide i1 */
+ while (!(*ang_sort_comp)(u, v, z, a)) a++;
+
+ /* Done partition */
+ if (a >= b) break;
+
+ /* Swap */
+ (*ang_sort_swap)(u, v, a, b);
+
+ /* Advance */
+ a++, b--;
+ }
+
+ /* Recurse left side */
+ ang_sort_aux(u, v, p, b);
+
+ /* Recurse right side */
+ ang_sort_aux(u, v, b + 1, q);
+}
+
+
+/*
+ * Angband sorting algorithm -- quick sort in place
+ *
+ * Note that the details of the data we are sorting is hidden,
+ * and we rely on the "ang_sort_comp()" and "ang_sort_swap()"
+ * function hooks to interact with the data, which is given as
+ * two pointers, and which may have any user-defined form.
+ */
+void ang_sort(vptr u, vptr v, int n)
+{
+ /* Sort the array */
+ ang_sort_aux(u, v, 0, n - 1);
+}
+
+
+
+/*** Targetting Code ***/
+
+
+/*
+ * Determine is a monster makes a reasonable target
+ *
+ * The concept of "targetting" was stolen from "Morgul" (?)
+ *
+ * The player can target any location, or any "target-able" monster.
+ *
+ * Currently, a monster is "target_able" if it is visible, and if
+ * the player can hit it with a projection, and the player is not
+ * hallucinating. This allows use of "use closest target" macros.
+ *
+ * Future versions may restrict the ability to target "trappers"
+ * and "mimics", but the semantics is a little bit weird.
+ */
+bool_ target_able(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Monster must be alive */
+ if (!m_ptr->r_idx) return (FALSE);
+
+ /* Monster must be visible */
+ if (!m_ptr->ml) return (FALSE);
+
+ /* Monster must be projectable */
+ if (!projectable(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx)) return (FALSE);
+
+ /* Hack -- no targeting hallucinations */
+ if (p_ptr->image) return (FALSE);
+
+ /* Dont target pets */
+ if (is_friend(m_ptr) > 0) return (FALSE);
+
+ /* Honor flag */
+ if (r_info[m_ptr->r_idx].flags7 & RF7_NO_TARGET) return (FALSE);
+
+ /* XXX XXX XXX Hack -- Never target trappers */
+ /* if (CLEAR_ATTR && (CLEAR_CHAR)) return (FALSE); */
+
+ /* Assume okay */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Update (if necessary) and verify (if possible) the target.
+ *
+ * We return TRUE if the target is "okay" and FALSE otherwise.
+ */
+bool_ target_okay(void)
+{
+ /* Accept stationary targets */
+ if (target_who < 0) return (TRUE);
+
+ /* Check moving targets */
+ if (target_who > 0)
+ {
+ /* Accept reasonable targets */
+ if (target_able(target_who))
+ {
+ monster_type *m_ptr = &m_list[target_who];
+
+ /* Acquire monster location */
+ target_row = m_ptr->fy;
+ target_col = m_ptr->fx;
+
+ /* Good target */
+ return (TRUE);
+ }
+ }
+
+ /* Assume no target */
+ return (FALSE);
+}
+
+
+
+/*
+ * Sorting hook -- comp function -- by "distance to player"
+ *
+ * We use "u" and "v" to point to arrays of "x" and "y" positions,
+ * and sort the arrays by double-distance to the player.
+ */
+static bool_ ang_sort_comp_distance(vptr u, vptr v, int a, int b)
+{
+ byte *x = (byte*)(u);
+ byte *y = (byte*)(v);
+
+ int da, db, kx, ky;
+
+ /* Absolute distance components */
+ kx = x[a];
+ kx -= p_ptr->px;
+ kx = ABS(kx);
+ ky = y[a];
+ ky -= p_ptr->py;
+ ky = ABS(ky);
+
+ /* Approximate Double Distance to the first point */
+ da = ((kx > ky) ? (kx + kx + ky) : (ky + ky + kx));
+
+ /* Absolute distance components */
+ kx = x[b];
+ kx -= p_ptr->px;
+ kx = ABS(kx);
+ ky = y[b];
+ ky -= p_ptr->py;
+ ky = ABS(ky);
+
+ /* Approximate Double Distance to the first point */
+ db = ((kx > ky) ? (kx + kx + ky) : (ky + ky + kx));
+
+ /* Compare the distances */
+ return (da <= db);
+}
+
+
+/*
+ * Sorting hook -- swap function -- by "distance to player"
+ *
+ * We use "u" and "v" to point to arrays of "x" and "y" positions,
+ * and sort the arrays by distance to the player.
+ */
+static void ang_sort_swap_distance(vptr u, vptr v, int a, int b)
+{
+ byte *x = (byte*)(u);
+ byte *y = (byte*)(v);
+
+ byte temp;
+
+ /* Swap "x" */
+ temp = x[a];
+ x[a] = x[b];
+ x[b] = temp;
+
+ /* Swap "y" */
+ temp = y[a];
+ y[a] = y[b];
+ y[b] = temp;
+}
+
+
+
+/*
+ * Hack -- help "select" a location (see below)
+ */
+static s16b target_pick(int y1, int x1, int dy, int dx)
+{
+ int i, v;
+
+ int x2, y2, x3, y3, x4, y4;
+
+ int b_i = -1, b_v = 9999;
+
+
+ /* Scan the locations */
+ for (i = 0; i < temp_n; i++)
+ {
+ /* Point 2 */
+ x2 = temp_x[i];
+ y2 = temp_y[i];
+
+ /* Directed distance */
+ x3 = (x2 - x1);
+ y3 = (y2 - y1);
+
+ /* Verify quadrant */
+ if (dx && (x3 * dx <= 0)) continue;
+ if (dy && (y3 * dy <= 0)) continue;
+
+ /* Absolute distance */
+ x4 = ABS(x3);
+ y4 = ABS(y3);
+
+ /* Verify quadrant */
+ if (dy && !dx && (x4 > y4)) continue;
+ if (dx && !dy && (y4 > x4)) continue;
+
+ /* Approximate Double Distance */
+ v = ((x4 > y4) ? (x4 + x4 + y4) : (y4 + y4 + x4));
+
+ /* XXX XXX XXX Penalize location */
+
+ /* Track best */
+ if ((b_i >= 0) && (v >= b_v)) continue;
+
+ /* Track best */
+ b_i = i;
+ b_v = v;
+ }
+
+ /* Result */
+ return (b_i);
+}
+
+
+/*
+ * Hack -- determine if a given location is "interesting"
+ */
+static bool_ target_set_accept(int y, int x)
+{
+ cave_type *c_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Player grid is always interesting */
+ if ((y == p_ptr->py) && (x == p_ptr->px)) return (TRUE);
+
+
+ /* Handle hallucination */
+ if (p_ptr->image) return (FALSE);
+
+
+ /* Examine the grid */
+ c_ptr = &cave[y][x];
+
+ /* Visible monsters */
+ if (c_ptr->m_idx && c_ptr->m_idx < max_r_idx)
+ {
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ /* Visible monsters */
+ if (m_ptr->ml) return (TRUE);
+ }
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Memorized object */
+ if (o_ptr->marked) return (TRUE);
+ }
+
+ /* Interesting memorized features */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ /* Traps are interesting */
+ if (c_ptr->info & (CAVE_TRDT)) return (TRUE);
+
+ /* Hack -- Doors are boring */
+ if (c_ptr->feat == FEAT_OPEN) return (FALSE);
+ if (c_ptr->feat == FEAT_BROKEN) return (FALSE);
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) return (FALSE);
+
+ /* Accept 'naturally' interesting features */
+ if (f_info[c_ptr->feat].flags1 & FF1_NOTICE) return (TRUE);
+ }
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+/*
+ * Prepare the "temp" array for "target_set"
+ *
+ * Return the number of target_able monsters in the set.
+ */
+static void target_set_prepare(int mode)
+{
+ int y, x;
+
+ /* Reset "temp" array */
+ temp_n = 0;
+
+ /* Scan the current panel */
+ for (y = panel_row_min; y <= panel_row_max; y++)
+ {
+ for (x = panel_col_min; x <= panel_col_max; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Require line of sight, unless "look" is "expanded" */
+ if (!expand_look && !player_has_los_bold(y, x)) continue;
+
+ /* Require "interesting" contents */
+ if (!target_set_accept(y, x)) continue;
+
+ /* Require target_able monsters for "TARGET_KILL" */
+ if ((mode & (TARGET_KILL)) && !target_able(c_ptr->m_idx)) continue;
+
+ /* Save the location */
+ temp_x[temp_n] = x;
+ temp_y[temp_n] = y;
+ temp_n++;
+ }
+ }
+
+ /* Set the sort hooks */
+ ang_sort_comp = ang_sort_comp_distance;
+ ang_sort_swap = ang_sort_swap_distance;
+
+ /* Sort the positions */
+ ang_sort(temp_x, temp_y, temp_n);
+}
+
+
+bool_ target_object(int y, int x, int mode, cptr info, bool_ *boring,
+ object_type *o_ptr, char *out_val, cptr *s1, cptr *s2, cptr *s3,
+ int *query)
+{
+ char o_name[80];
+
+ /* Not boring */
+ *boring = FALSE;
+
+ /* Obtain an object description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe the object */
+ sprintf(out_val, "%s%s%s%s [%s]", *s1, *s2, *s3, o_name, info);
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ *query = inkey();
+
+ /* Always stop at "normal" keys */
+ if ((*query != '\r') && (*query != '\n') && (*query != ' ')) return (TRUE);
+
+ /* Sometimes stop at "space" key */
+ if ((*query == ' ') && !(mode & (TARGET_LOOK))) return (TRUE);
+
+ /* Change the intro */
+ *s1 = "It is ";
+
+ /* Plurals */
+ if (o_ptr->number != 1) *s1 = "They are ";
+
+ /* Preposition */
+ *s2 = "on ";
+ return (FALSE);
+}
+
+/*
+ * Examine a grid, return a keypress.
+ *
+ * The "mode" argument contains the "TARGET_LOOK" bit flag, which
+ * indicates that the "space" key should scan through the contents
+ * of the grid, instead of simply returning immediately. This lets
+ * the "look" command get complete information, without making the
+ * "target" command annoying.
+ *
+ * The "info" argument contains the "commands" which should be shown
+ * inside the "[xxx]" text. This string must never be empty, or grids
+ * containing monsters will be displayed with an extra comma.
+ *
+ * Note that if a monster is in the grid, we update both the monster
+ * recall info and the health bar info to track that monster.
+ *
+ * Eventually, we may allow multiple objects per grid, or objects
+ * and terrain features in the same grid. XXX XXX XXX
+ *
+ * This function must handle blindness/hallucination.
+ */
+static int target_set_aux(int y, int x, int mode, cptr info)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ cptr s1, s2, s3;
+
+ bool_ boring;
+
+ int feat;
+
+ int query;
+
+ char out_val[160];
+
+
+ /* Repeat forever */
+ while (1)
+ {
+ /* Paranoia */
+ query = ' ';
+
+ /* Assume boring */
+ boring = TRUE;
+
+ /* Default */
+ s1 = "You see ";
+ s2 = "";
+ s3 = "";
+
+ /* Hack -- under the player */
+ if ((y == p_ptr->py) && (x == p_ptr->px))
+ {
+ /* Description */
+ s1 = "You are ";
+
+ /* Preposition */
+ s2 = "on ";
+ }
+
+
+ /* Hack -- hallucination */
+ if (p_ptr->image)
+ {
+ cptr name = "something strange";
+
+ /* Display a message */
+ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name, info);
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ query = inkey();
+
+ /* Stop on everything but "return" */
+ if ((query != '\r') && (query != '\n')) break;
+
+ /* Repeat forever */
+ continue;
+ }
+
+
+ /* Actual monsters */
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Mimics special treatment -- looks like an object */
+ if ((r_ptr->flags9 & RF9_MIMIC) && (m_ptr->csleep))
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ if (o_ptr->marked)
+ {
+ if (target_object(y, x, mode, info, &boring, o_ptr, out_val, &s1, &s2, &s3, &query)) break;
+ }
+ }
+ else
+ {
+ /* Visible */
+ if (m_ptr->ml)
+ {
+ bool_ recall = FALSE;
+
+ char m_name[80];
+
+ /* Not boring */
+ boring = FALSE;
+
+ /* Get the monster name ("a kobold") */
+ monster_desc(m_name, m_ptr, 0x08);
+
+ /* Hack -- track this monster race */
+ monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack -- health bar for this monster */
+ health_track(c_ptr->m_idx);
+
+ /* Hack -- handle stuff */
+ handle_stuff();
+
+ /* Interact */
+ while (1)
+ {
+ /* Recall */
+ if (recall)
+ {
+ /* Save */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Recall on screen */
+ screen_roff(m_ptr->r_idx, m_ptr->ego, 0);
+
+ /* Hack -- Complete the prompt (again) */
+ Term_addstr( -1, TERM_WHITE, format(" [r,%s]", info));
+
+ /* Command */
+ query = inkey();
+
+ /* Restore */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Normal */
+ else
+ {
+ cptr mstat;
+
+ switch (m_ptr->status)
+ {
+ case MSTATUS_NEUTRAL:
+ case MSTATUS_NEUTRAL_M:
+ case MSTATUS_NEUTRAL_P:
+ mstat = " (neutral) ";
+ break;
+ case MSTATUS_PET:
+ mstat = " (pet) ";
+ break;
+ case MSTATUS_FRIEND:
+ mstat = " (coaligned) ";
+ break;
+ case MSTATUS_COMPANION:
+ mstat = " (companion) ";
+ break;
+ default:
+ mstat = " ";
+ break;
+ }
+ if (m_ptr->mflag & MFLAG_PARTIAL) mstat = " (partial) ";
+
+ /* Describe, and prompt for recall */
+ sprintf(out_val, "%s%s%s%s (level %d, %s%s)%s%s[r,%s]",
+ s1, s2, s3, m_name,
+ m_ptr->level, look_mon_desc(c_ptr->m_idx),
+ (m_ptr->mflag & MFLAG_QUEST) ? ", quest" : "",
+ (m_ptr->smart & SM_CLONED ? " (clone)" : ""),
+ (mstat), info);
+
+ prt(out_val, 0, 0);
+
+ /* Place cursor */
+ move_cursor_relative(y, x);
+
+ /* Command */
+ query = inkey();
+ }
+
+ /* Normal commands */
+ if (query != 'r') break;
+
+ /* Toggle recall */
+ recall = !recall;
+ }
+
+ /* Always stop at "normal" keys */
+ if ((query != '\r') && (query != '\n') && (query != ' ')) break;
+
+ /* Sometimes stop at "space" key */
+ if ((query == ' ') && !(mode & (TARGET_LOOK))) break;
+
+ /* Change the intro */
+ s1 = "It is ";
+
+ /* Hack -- take account of gender */
+ if (r_ptr->flags1 & (RF1_FEMALE)) s1 = "She is ";
+ else if (r_ptr->flags1 & (RF1_MALE)) s1 = "He is ";
+
+ /* Use a preposition */
+ s2 = "carrying ";
+
+ /* Scan all objects being carried */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ char o_name[80];
+
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Obtain an object description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe the object */
+ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, o_name, info);
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ query = inkey();
+
+ /* Always stop at "normal" keys */
+ if ((query != '\r') && (query != '\n') && (query != ' ')) break;
+
+ /* Sometimes stop at "space" key */
+ if ((query == ' ') && !(mode & (TARGET_LOOK))) break;
+
+ /* Change the intro */
+ s2 = "also carrying ";
+ }
+
+ /* Double break */
+ if (this_o_idx) break;
+
+ /* Use a preposition */
+ s2 = "on ";
+ }
+ }
+ }
+
+
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Describe it */
+ if (o_ptr->marked)
+ {
+ if (target_object(y, x, mode, info, &boring, o_ptr, out_val, &s1, &s2, &s3, &query)) break;
+ }
+ }
+
+ /* Double break */
+ if (this_o_idx) break;
+
+ /* Actual traps */
+ if ((c_ptr->info & (CAVE_TRDT)) && c_ptr->t_idx)
+ {
+ cptr name = "a trap", s4;
+
+ /* Name trap */
+ if (t_info[c_ptr->t_idx].ident)
+ {
+ s4 = format("(%s)", t_name + t_info[c_ptr->t_idx].name);
+ }
+ else
+ {
+ s4 = "an unknown trap";
+ }
+
+ /* Display a message */
+ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name, s4);
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ query = inkey();
+
+ /* Stop on everything but "return" */
+ if ((query != '\r') && (query != '\n')) break;
+
+ /* Repeat forever */
+ continue;
+ }
+
+ /* Feature (apply "mimic") */
+ if (c_ptr->mimic)
+ {
+ feat = c_ptr->mimic;
+ }
+ else
+ {
+ feat = f_info[c_ptr->feat].mimic;
+ }
+
+ /* Require knowledge about grid, or ability to see grid */
+ if (!(c_ptr->info & (CAVE_MARK)) && !player_can_see_bold(y, x))
+ {
+ /* Forget feature */
+ feat = FEAT_NONE;
+ }
+
+ /* Terrain feature if needed */
+ if (boring || (feat >= FEAT_GLYPH))
+ {
+ cptr name;
+
+ /* Hack -- special handling for building doors */
+ if (feat == FEAT_SHOP)
+ {
+ name = st_name + st_info[c_ptr->special].name;
+ }
+ else
+ {
+ name = f_name + f_info[feat].name;
+ }
+
+ /* Hack -- handle unknown grids */
+ if (feat == FEAT_NONE) name = "unknown grid";
+
+ /* Pick a prefix */
+ if (*s2 &&
+ (((feat >= FEAT_MINOR_GLYPH) &&
+ (feat <= FEAT_PATTERN_XTRA2)) ||
+ (feat == FEAT_DIRT) ||
+ (feat == FEAT_GRASS) ||
+ (feat == FEAT_FLOWER))) s2 = "on ";
+ else if (*s2 && (feat == FEAT_SMALL_TREES)) s2 = "by ";
+ else if (*s2 && (feat >= FEAT_DOOR_HEAD)) s2 = "in ";
+
+ /* Pick proper indefinite article */
+ s3 = (is_a_vowel(name[0])) ? "an " : "a ";
+
+ /* Hack -- special introduction for store & building doors */
+ if (feat == FEAT_SHOP)
+ {
+ s3 = "the entrance to the ";
+ }
+
+ if ((feat == FEAT_MORE) && c_ptr->special)
+ {
+ s3 = "";
+ name = d_text + d_info[c_ptr->special].text;
+ }
+
+ if (p_ptr->wild_mode && (feat == FEAT_TOWN))
+ {
+ s3 = "";
+ name = format("%s(%s)",
+ wf_name + wf_info[wild_map[y][x].feat].name,
+ wf_text + wf_info[wild_map[y][x].feat].text);
+ }
+
+ if ((feat == FEAT_FOUNTAIN) && (c_ptr->info & CAVE_IDNT))
+ {
+ object_kind *k_ptr;
+ int tv, sv;
+
+ if (c_ptr->special <= SV_POTION_LAST)
+ {
+ tv = TV_POTION;
+ sv = c_ptr->special;
+ }
+ else
+ {
+ tv = TV_POTION2;
+ sv = c_ptr->special - SV_POTION_LAST;
+ }
+
+ k_ptr = &k_info[lookup_kind(tv, sv)];
+ info = k_name + k_ptr->name;
+ }
+
+ /* Display a message */
+ if (!wizard)
+ {
+ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name, info);
+ }
+ else
+ {
+ sprintf(out_val, "%s%s%s%s [%s] (%d:%d:%d)",
+ s1, s2, s3, name, info,
+ c_ptr->feat, c_ptr->mimic, c_ptr->special);
+ }
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ query = inkey();
+
+ /* Always stop at "normal" keys */
+ if ((query != '\r') && (query != '\n') && (query != ' ')) break;
+ }
+
+ /* Stop on everything but "return" */
+ if ((query != '\r') && (query != '\n')) break;
+ }
+
+ /* Keep going */
+ return (query);
+}
+
+
+
+
+/*
+ * Handle "target" and "look".
+ *
+ * Note that this code can be called from "get_aim_dir()".
+ *
+ * All locations must be on the current panel. Consider the use of
+ * "panel_bounds()" to allow "off-panel" targets, perhaps by using
+ * some form of "scrolling" the map around the cursor. XXX XXX XXX
+ * That is, consider the possibility of "auto-scrolling" the screen
+ * while the cursor moves around. This may require changes in the
+ * "update_mon()" code to allow "visibility" even if off panel, and
+ * may require dynamic recalculation of the "temp" grid set.
+ *
+ * Hack -- targetting/observing an "outer border grid" may induce
+ * problems, so this is not currently allowed.
+ *
+ * The player can use the direction keys to move among "interesting"
+ * grids in a heuristic manner, or the "space", "+", and "-" keys to
+ * move through the "interesting" grids in a sequential manner, or
+ * can enter "location" mode, and use the direction keys to move one
+ * grid at a time in any direction. The "t" (set target) command will
+ * only target a monster (as opposed to a location) if the monster is
+ * target_able and the "interesting" mode is being used.
+ *
+ * The current grid is described using the "look" method above, and
+ * a new command may be entered at any time, but note that if the
+ * "TARGET_LOOK" bit flag is set (or if we are in "location" mode,
+ * where "space" has no obvious meaning) then "space" will scan
+ * through the description of the current grid until done, instead
+ * of immediately jumping to the next "interesting" grid. This
+ * allows the "target" command to retain its old semantics.
+ *
+ * The "*", "+", and "-" keys may always be used to jump immediately
+ * to the next (or previous) interesting grid, in the proper mode.
+ *
+ * The "return" key may always be used to scan through a complete
+ * grid description (forever).
+ *
+ * This command will cancel any old target, even if used from
+ * inside the "look" command.
+ */
+bool_ target_set(int mode)
+{
+ int i, d, m;
+ int y = p_ptr->py;
+ int x = p_ptr->px;
+
+ bool_ done = FALSE;
+
+ bool_ flag = TRUE;
+
+ char query;
+
+ char info[80];
+
+ cave_type *c_ptr;
+
+ int screen_wid, screen_hgt;
+ int panel_wid, panel_hgt;
+
+ /* Get size */
+ get_screen_size(&screen_wid, &screen_hgt);
+
+ /* Calculate the amount of panel movement */
+ panel_hgt = screen_hgt / 2;
+ panel_wid = screen_wid / 2;
+
+ /* Cancel target */
+ target_who = 0;
+
+
+ /* Cancel tracking */
+ /* health_track(0); */
+
+
+ /* Prepare the "temp" array */
+ target_set_prepare(mode);
+
+ /* Start near the player */
+ m = 0;
+
+ /* Interact */
+ while (!done)
+ {
+ /* Interesting grids */
+ if (flag && temp_n)
+ {
+ y = temp_y[m];
+ x = temp_x[m];
+
+ /* Access */
+ c_ptr = &cave[y][x];
+
+ /* Allow target */
+ if (target_able(c_ptr->m_idx))
+ {
+ strcpy(info, "q,t,p,o,+,-,'dir'");
+ }
+
+ /* Dis-allow target */
+ else
+ {
+ strcpy(info, "q,p,o,+,-,'dir'");
+ }
+
+ /* Describe and Prompt */
+ query = target_set_aux(y, x, mode, info);
+
+ /* Cancel tracking */
+ /* health_track(0); */
+
+ /* Assume no "direction" */
+ d = 0;
+
+ /* Analyze */
+ switch (query)
+ {
+ case ESCAPE:
+ case 'q':
+ {
+ done = TRUE;
+ break;
+ }
+
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ if (target_able(c_ptr->m_idx))
+ {
+ health_track(c_ptr->m_idx);
+ target_who = c_ptr->m_idx;
+ target_row = y;
+ target_col = x;
+ done = TRUE;
+ }
+ else
+ {
+ bell();
+ }
+ break;
+ }
+
+ case ' ':
+ case '*':
+ case '+':
+ {
+ if (++m == temp_n)
+ {
+ m = 0;
+ if (!expand_list) done = TRUE;
+ }
+ break;
+ }
+
+ case '-':
+ {
+ if (m-- == 0)
+ {
+ m = temp_n - 1;
+ if (!expand_list) done = TRUE;
+ }
+ break;
+ }
+
+ case 'p':
+ {
+ /* Recenter the map around the player */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Recalculate interesting grids */
+ target_set_prepare(mode);
+
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Fall through... */
+ }
+
+ case 'o':
+ {
+ flag = FALSE;
+ break;
+ }
+
+ case 'm':
+ {
+ break;
+ }
+
+ default:
+ {
+ /* Extract the action (if any) */
+ d = get_keymap_dir(query);
+
+ if (!d) bell();
+ break;
+ }
+ }
+
+ /* Hack -- move around */
+ if (d)
+ {
+ /* Find a new monster */
+ i = target_pick(temp_y[m], temp_x[m], ddy[d], ddx[d]);
+
+ /* Scroll to find interesting grid */
+ if (i < 0)
+ {
+ int dy;
+ int dx;
+
+ dy = ddy[d];
+ dx = ddx[d];
+
+ /* Note panel change */
+ if (change_panel(dy, dx))
+ {
+ int ty = temp_y[m];
+ int tx = temp_x[m];
+
+ /* Recalculate interesting grids */
+ target_set_prepare(mode);
+
+ /* Find a new monster */
+ i = target_pick(ty, tx, dy, dx);
+
+ /* Restore panel if needed */
+ if (i < 0)
+ {
+ /* Restore panel */
+ change_panel( -dy, -dx);
+
+ /* Recalculate interesting grids */
+ target_set_prepare(mode);
+ }
+ }
+ }
+
+ /* Use that grids */
+ if (i >= 0) m = i;
+ }
+ }
+
+ /* Arbitrary grids */
+ else
+ {
+ /* Access */
+ c_ptr = &cave[y][x];
+
+ /* Default prompt */
+ strcpy(info, "q,t,p,m,+,-,'dir'");
+
+ /* Describe and Prompt (enable "TARGET_LOOK") */
+ query = target_set_aux(y, x, mode | TARGET_LOOK, info);
+
+ /* Cancel tracking */
+ /* health_track(0); */
+
+ /* Assume no direction */
+ d = 0;
+
+ /* Analyze the keypress */
+ switch (query)
+ {
+ case ESCAPE:
+ case 'q':
+ {
+ done = TRUE;
+ break;
+ }
+
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ target_who = -1;
+ target_row = y;
+ target_col = x;
+ done = TRUE;
+ break;
+ }
+
+ case ' ':
+ case '*':
+ case '+':
+ case '-':
+ {
+ break;
+ }
+
+ case 'p':
+ {
+ y = p_ptr->py;
+ x = p_ptr->px;
+ }
+
+ case 'o':
+ {
+ break;
+ }
+
+ case 'm':
+ {
+ flag = TRUE;
+ break;
+ }
+
+ default:
+ {
+ /* Extract the action (if any) */
+ d = get_keymap_dir(query);
+
+ if (!d) bell();
+ break;
+ }
+ }
+
+ /* Handle "direction" */
+ if (d)
+ {
+ int dy = ddy[d];
+ int dx = ddx[d];
+
+ /* Move */
+ y += dy;
+ x += dx;
+
+ /* Do not move horizontally if unnecessary */
+ if (((x < panel_col_min + panel_wid) && (dx > 0)) ||
+ ((x > panel_col_min + panel_wid) && (dx < 0)))
+ {
+ dx = 0;
+ }
+
+ /* Do not move vertically if unnecessary */
+ if (((y < panel_row_min + panel_hgt) && (dy > 0)) ||
+ ((y > panel_row_min + panel_hgt) && (dy < 0)))
+ {
+ dy = 0;
+ }
+ /* Apply the motion */
+ if ((y >= panel_row_min + screen_hgt) ||
+ (y < panel_row_min) ||
+ (x > panel_col_min + screen_wid) ||
+ (x < panel_col_min))
+ {
+ /* Change panel and recalculate interesting grids */
+ if (change_panel(dy, dx)) target_set_prepare(mode);
+ }
+
+ /* Boundary checks */
+ if (!wizard)
+ {
+ /* Hack -- Verify y */
+ if (y <= 0) y = 1;
+ else if (y >= cur_hgt - 1) y = cur_hgt - 2;
+
+ /* Hack -- Verify x */
+ if (x <= 0) x = 1;
+ else if (x >= cur_wid - 1) x = cur_wid - 2;
+ }
+ else
+ {
+ /* Hack -- Verify y */
+ if (y < 0) y = 0;
+ else if (y > cur_hgt - 1) y = cur_hgt - 1;
+
+ /* Hack -- Verify x */
+ if (x < 0) x = 0;
+ else if (x > cur_wid - 1) x = cur_wid - 1;
+ }
+ }
+ }
+ }
+
+ /* Forget */
+ temp_n = 0;
+
+ /* Clear the top line */
+ prt("", 0, 0);
+
+ /* Recenter the map around the player */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Failure to set target */
+ if (!target_who) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Get an "aiming direction" from the user.
+ *
+ * The "dir" is loaded with 1,2,3,4,6,7,8,9 for "actual direction", and
+ * "0" for "current target", and "-1" for "entry aborted".
+ *
+ * Note that "Force Target", if set, will pre-empt user interaction,
+ * if there is a usable target already set.
+ *
+ * Note that confusion over-rides any (explicit?) user choice.
+ */
+bool_ get_aim_dir(int *dp)
+{
+ int dir;
+
+ char command;
+
+ cptr p;
+
+ if (repeat_pull(dp))
+ {
+ /* Confusion? */
+
+ /* Verify */
+ if (!(*dp == 5 && !target_okay()))
+ {
+ return (TRUE);
+ }
+ }
+
+ /* Initialize */
+ (*dp) = 0;
+
+ /* Global direction */
+ dir = command_dir;
+
+ /* Hack -- auto-target if requested */
+ if (use_old_target && target_okay()) dir = 5;
+
+ /* Ask until satisfied */
+ while (!dir)
+ {
+ /* Choose a prompt */
+ if (!target_okay())
+ {
+ p = "Direction ('*' to choose a target, Escape to cancel)? ";
+ }
+ else
+ {
+ p = "Direction ('5' for target, '*' to re-target, Escape to cancel)? ";
+ }
+
+ /* Get a command (or Cancel) */
+ if (!get_com(p, &command)) break;
+
+ /* Convert various keys to "standard" keys */
+ switch (command)
+ {
+ /* Use current target */
+ case 'T':
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ dir = 5;
+ break;
+ }
+
+ /* Set new target */
+ case '*':
+ {
+ if (target_set(TARGET_KILL)) dir = 5;
+ break;
+ }
+
+ default:
+ {
+ /* Extract the action (if any) */
+ dir = get_keymap_dir(command);
+
+ break;
+ }
+ }
+
+ /* Verify requested targets */
+ if ((dir == 5) && !target_okay()) dir = 0;
+
+ /* Error */
+ if (!dir) bell();
+ }
+
+ /* No direction */
+ if (!dir) return (FALSE);
+
+ /* Save the direction */
+ command_dir = dir;
+
+ /* Check for confusion */
+ if (p_ptr->confused)
+ {
+ /* XXX XXX XXX */
+ /* Random direction */
+ dir = ddd[rand_int(8)];
+ }
+
+ /* Notice confusion */
+ if (command_dir != dir)
+ {
+ /* Warn the user */
+ msg_print("You are confused.");
+ }
+
+ /* Save direction */
+ (*dp) = dir;
+
+
+ repeat_push(dir);
+
+ /* A "valid" direction was entered */
+ return (TRUE);
+}
+
+
+
+/*
+ * Request a "movement" direction (1,2,3,4,6,7,8,9) from the user,
+ * and place it into "command_dir", unless we already have one.
+ *
+ * This function should be used for all "repeatable" commands, such as
+ * run, walk, open, close, bash, disarm, spike, tunnel, etc, as well
+ * as all commands which must reference a grid adjacent to the player,
+ * and which may not reference the grid under the player. Note that,
+ * for example, it is no longer possible to "disarm" or "open" chests
+ * in the same grid as the player.
+ *
+ * Direction "5" is illegal and will (cleanly) abort the command.
+ *
+ * This function tracks and uses the "global direction", and uses
+ * that as the "desired direction", to which "confusion" is applied.
+ */
+bool_ get_rep_dir(int *dp)
+{
+ int dir;
+
+ if (repeat_pull(dp))
+ {
+ return (TRUE);
+ }
+
+ /* Initialize */
+ (*dp) = 0;
+
+ /* Global direction */
+ dir = command_dir;
+
+ /* Get a direction */
+ while (!dir)
+ {
+ char ch;
+
+ /* Get a command (or Cancel) */
+ if (!get_com("Direction (Escape to cancel)? ", &ch)) break;
+
+ /* Look up the direction */
+ dir = get_keymap_dir(ch);
+
+ /* Oops */
+ if (!dir) bell();
+ }
+
+ /* Prevent weirdness */
+ if (dir == 5) dir = 0;
+
+ /* Aborted */
+ if (!dir) return (FALSE);
+
+ /* Save desired direction */
+ command_dir = dir;
+
+ /* Apply "confusion" */
+ if (p_ptr->confused)
+ {
+ /* Standard confusion */
+ if (rand_int(100) < 75)
+ {
+ /* Random direction */
+ dir = ddd[rand_int(8)];
+ }
+ }
+
+ /* Notice confusion */
+ if (command_dir != dir)
+ {
+ /* Warn the user */
+ msg_print("You are confused.");
+ }
+
+ /* Save direction */
+ (*dp) = dir;
+
+
+ repeat_push(dir);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+int get_chaos_patron(void)
+{
+ return (((p_ptr->age) + (p_ptr->sc)) % MAX_PATRON);
+}
+
+
+void gain_level_reward(int chosen_reward)
+{
+ object_type *q_ptr;
+ object_type forge;
+ char wrath_reason[32] = "";
+ int nasty_chance = 6;
+ int dummy = 0, dummy2 = 0;
+ int type, effect;
+
+
+ if (p_ptr->lev == 13) nasty_chance = 2;
+ else if (!(p_ptr->lev % 13)) nasty_chance = 3;
+ else if (!(p_ptr->lev % 14)) nasty_chance = 12;
+
+ if (randint(nasty_chance) == 1)
+ type = randint(20); /* Allow the 'nasty' effects */
+ else
+ type = randint(15) + 5; /* Or disallow them */
+
+ if (type < 1) type = 1;
+ if (type > 20) type = 20;
+ type--;
+
+
+ sprintf(wrath_reason, "the Wrath of %s",
+ chaos_patrons[p_ptr->chaos_patron]);
+
+ effect = chaos_rewards[p_ptr->chaos_patron][type];
+
+ if ((randint(6) == 1) && !chosen_reward)
+ {
+ msg_format("%^s rewards you with a corruption!",
+ chaos_patrons[p_ptr->chaos_patron]);
+ (void)gain_random_corruption(0);
+ return;
+ }
+
+ switch (chosen_reward ? chosen_reward : effect)
+ {
+ case REW_POLY_SLF :
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou needst a new form, mortal!'");
+ do_poly_self();
+ break;
+
+ case REW_GAIN_EXP:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Well done, mortal! Lead on!'");
+ if (p_ptr->exp < PY_MAX_EXP)
+ {
+ s32b ee = (p_ptr->exp / 2) + 10;
+ if (ee > 100000L) ee = 100000L;
+ msg_print("You feel more experienced.");
+ gain_exp(ee);
+ }
+ break;
+
+ case REW_LOSE_EXP:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou didst not deserve that, slave.'");
+ lose_exp(p_ptr->exp / 6);
+ break;
+
+ case REW_GOOD_OBJ:
+ msg_format("The voice of %s whispers:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Use my gift wisely.'");
+ acquirement(p_ptr->py, p_ptr->px, 1, FALSE, FALSE);
+ break;
+
+ case REW_GREA_OBJ:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Use my gift wisely.'");
+ acquirement(p_ptr->py, p_ptr->px, 1, TRUE, FALSE);
+ break;
+
+ case REW_CHAOS_WP:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thy deed hath earned thee a worthy blade.'");
+ /* Get local object */
+ q_ptr = &forge;
+ dummy = TV_SWORD;
+ switch (randint(p_ptr->lev))
+ {
+ case 0:
+ case 1:
+ dummy2 = SV_DAGGER;
+ break;
+ case 2:
+ case 3:
+ dummy2 = SV_MAIN_GAUCHE;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ dummy2 = SV_RAPIER;
+ break;
+ case 7:
+ case 8:
+ dummy2 = SV_SMALL_SWORD;
+ break;
+ case 9:
+ case 10:
+ dummy2 = SV_BASILLARD;
+ break;
+ case 11:
+ case 12:
+ case 13:
+ dummy2 = SV_SHORT_SWORD;
+ break;
+ case 14:
+ case 15:
+ dummy2 = SV_SABRE;
+ break;
+ case 16:
+ case 17:
+ dummy2 = SV_CUTLASS;
+ break;
+ case 18:
+ case 19:
+ dummy2 = SV_KHOPESH;
+ break;
+ case 20:
+ dummy2 = SV_TULWAR;
+ break;
+ case 21:
+ dummy2 = SV_BROAD_SWORD;
+ break;
+ case 22:
+ case 23:
+ dummy2 = SV_LONG_SWORD;
+ break;
+ case 24:
+ case 25:
+ dummy2 = SV_SCIMITAR;
+ break;
+ case 26:
+ case 27:
+ dummy2 = SV_KATANA;
+ break;
+ case 28:
+ case 29:
+ dummy2 = SV_BASTARD_SWORD;
+ break;
+ case 30:
+ dummy2 = SV_GREAT_SCIMITAR;
+ break;
+ case 31:
+ dummy2 = SV_CLAYMORE;
+ break;
+ case 32:
+ dummy2 = SV_ESPADON;
+ break;
+ case 33:
+ dummy2 = SV_TWO_HANDED_SWORD;
+ break;
+ case 34:
+ dummy2 = SV_FLAMBERGE;
+ break;
+ case 35:
+ case 36:
+ dummy2 = SV_EXECUTIONERS_SWORD;
+ break;
+ case 37:
+ dummy2 = SV_ZWEIHANDER;
+ break;
+ default:
+ dummy2 = SV_BLADE_OF_CHAOS;
+ }
+
+ object_prep(q_ptr, lookup_kind(dummy, dummy2));
+ q_ptr->to_h = 3 + (randint(dun_level)) % 10;
+ q_ptr->to_d = 3 + (randint(dun_level)) % 10;
+ random_resistance(q_ptr, FALSE, (randint(34) + 4));
+ q_ptr->name2 = EGO_CHAOTIC;
+
+ /* Apply the ego */
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+ break;
+
+ case REW_GOOD_OBS:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thy deed hath earned thee a worthy reward.'");
+ acquirement(p_ptr->py, p_ptr->px, randint(2) + 1, FALSE, FALSE);
+ break;
+
+ case REW_GREA_OBS:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Behold, mortal, how generously I reward thy loyalty.'");
+ acquirement(p_ptr->py, p_ptr->px, randint(2) + 1, TRUE, FALSE);
+ break;
+
+ case REW_TY_CURSE:
+ msg_format("The voice of %s thunders:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou art growing arrogant, mortal.'");
+ activate_ty_curse();
+ break;
+
+ case REW_SUMMON_M:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'My pets, destroy the arrogant mortal!'");
+ for (dummy = 0; dummy < randint(5) + 1; dummy++)
+ {
+ (void)summon_specific(p_ptr->py, p_ptr->px, dun_level, 0);
+ }
+ break;
+
+ case REW_H_SUMMON:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou needst worthier opponents!'");
+ activate_hi_summon();
+ break;
+
+ case REW_DO_HAVOC:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Death and destruction! This pleaseth me!'");
+ call_chaos();
+ break;
+
+ case REW_GAIN_ABL:
+ msg_format("The voice of %s rings out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Stay, mortal, and let me mold thee.'");
+ if ((randint(3) == 1) &&
+ !(chaos_stats[p_ptr->chaos_patron] < 0))
+ do_inc_stat(chaos_stats[p_ptr->chaos_patron]);
+ else
+ do_inc_stat(randint(6) - 1);
+ break;
+
+ case REW_LOSE_ABL:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'I grow tired of thee, mortal.'");
+ if ((randint(3) == 1) &&
+ !(chaos_stats[p_ptr->chaos_patron] < 0))
+ do_dec_stat(chaos_stats[p_ptr->chaos_patron], STAT_DEC_NORMAL);
+ else
+ (void)do_dec_stat(randint(6) - 1, STAT_DEC_NORMAL);
+ break;
+
+ case REW_RUIN_ABL:
+ msg_format("The voice of %s thunders:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou needst a lesson in humility, mortal!'");
+ msg_print("You feel less powerful!");
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void)dec_stat(dummy, 10 + randint(15), TRUE);
+ }
+ break;
+
+ case REW_POLY_WND:
+ msg_format("You feel the power of %s touch you.",
+ chaos_patrons[p_ptr->chaos_patron]);
+ do_poly_wounds();
+ break;
+
+ case REW_AUGM_ABL:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Receive this modest gift from me!'");
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void) do_inc_stat(dummy);
+ }
+ break;
+
+ case REW_HURT_LOT:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Suffer, pathetic fool!'");
+ fire_ball(GF_DISINTEGRATE, 0, (p_ptr->lev * 4), 4);
+ take_hit(p_ptr->lev * 4, wrath_reason);
+ break;
+
+ case REW_HEAL_FUL:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Rise, my servant!'");
+ restore_level();
+ (void)set_poisoned(0);
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ hp_player(5000);
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void) do_res_stat(dummy, TRUE);
+ }
+ break;
+
+ case REW_CURSE_WP:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou reliest too much on thy weapon.'");
+ (void)curse_weapon();
+ break;
+
+ case REW_CURSE_AR:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou reliest too much on thine equipment.'");
+ (void)curse_armor();
+ break;
+
+ case REW_PISS_OFF:
+ msg_format("The voice of %s whispers:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Now thou shalt pay for annoying me.'");
+ switch (randint(4))
+ {
+ case 1:
+ activate_ty_curse();
+ break;
+ case 2:
+ activate_hi_summon();
+ break;
+ case 3:
+ if (randint(2) == 1) (void)curse_weapon();
+ else (void)curse_armor();
+ break;
+ default:
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void) dec_stat(dummy, 10 + randint(15), TRUE);
+ }
+ break;
+ }
+ break;
+
+ case REW_WRATH:
+ msg_format("The voice of %s thunders:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Die, mortal!'");
+ take_hit(p_ptr->lev * 4, wrath_reason);
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void) dec_stat(dummy, 10 + randint(15), FALSE);
+ }
+ activate_hi_summon();
+ activate_ty_curse();
+ if (randint(2) == 1) (void)curse_weapon();
+ if (randint(2) == 1) (void)curse_armor();
+ break;
+
+ case REW_DESTRUCT:
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ {
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Death and destruction! This pleaseth me!'");
+ destroy_area(p_ptr->py, p_ptr->px, 25, TRUE, FALSE);
+ }
+ break;
+
+ case REW_GENOCIDE:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Let me relieve thee of thine oppressors!'");
+ (void) genocide(FALSE);
+ break;
+
+ case REW_MASS_GEN:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Let me relieve thee of thine oppressors!'");
+ (void) mass_genocide(FALSE);
+ break;
+
+ case REW_DISPEL_C:
+ msg_format("You can feel the power of %s assault your enemies!",
+ chaos_patrons[p_ptr->chaos_patron]);
+ (void) dispel_monsters(p_ptr->lev * 4);
+ break;
+
+ case REW_IGNORE:
+ msg_format("%s ignores you.",
+ chaos_patrons[p_ptr->chaos_patron]);
+ break;
+
+ case REW_SER_DEMO:
+ msg_format("%s rewards you with a demonic servant!", chaos_patrons[p_ptr->chaos_patron]);
+ if (!(summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DEMON, FALSE)))
+ msg_print("Nobody ever turns up...");
+ break;
+
+ case REW_SER_MONS:
+ msg_format("%s rewards you with a servant!", chaos_patrons[p_ptr->chaos_patron]);
+ if (!(summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_NO_UNIQUES, FALSE)))
+ msg_print("Nobody ever turns up...");
+ break;
+
+ case REW_SER_UNDE:
+ msg_format("%s rewards you with an undead servant!", chaos_patrons[p_ptr->chaos_patron]);
+ if (!(summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD, FALSE)))
+ msg_print("Nobody ever turns up...");
+ break;
+
+ default:
+ msg_format("The voice of %s stammers:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_format("'Uh... uh... the answer's %d/%d, what's the question?'", type,
+ effect );
+ break;
+ }
+}
+
+
+/*
+ * old -- from PsiAngband.
+ */
+bool_ tgt_pt(int *x, int *y)
+{
+ char ch = 0;
+ int d, cu, cv;
+ int screen_wid, screen_hgt;
+ bool_ success = FALSE;
+
+ *x = p_ptr->px;
+ *y = p_ptr->py;
+
+ /* Get size */
+ get_screen_size(&screen_wid, &screen_hgt);
+
+ cu = Term->scr->cu;
+ cv = Term->scr->cv;
+ Term->scr->cu = 0;
+ Term->scr->cv = 1;
+ msg_print("Select a point and press space.");
+
+ while ((ch != 27) && (ch != ' '))
+ {
+ move_cursor_relative(*y, *x);
+ ch = inkey();
+ switch (ch)
+ {
+ case 27:
+ break;
+ case ' ':
+ success = TRUE;
+ break;
+ default:
+ /* Look up the direction */
+ d = get_keymap_dir(ch);
+
+ if (!d) break;
+
+ *x += ddx[d];
+ *y += ddy[d];
+
+ /* Hack -- Verify x */
+ if ((*x >= cur_wid - 1) || (*x >= panel_col_min + screen_wid)) (*x)--;
+ else if ((*x <= 0) || (*x <= panel_col_min)) (*x)++;
+
+ /* Hack -- Verify y */
+ if ((*y >= cur_hgt - 1) || (*y >= panel_row_min + screen_hgt)) (*y)--;
+ else if ((*y <= 0) || (*y <= panel_row_min)) (*y)++;
+
+ break;
+ }
+ }
+
+ Term->scr->cu = cu;
+ Term->scr->cv = cv;
+ Term_fresh();
+ return success;
+}
+
+
+bool_ gain_random_corruption(int choose_mut)
+{
+ exec_lua("gain_corruption()");
+ return (FALSE);
+}
+
+bool_ lose_corruption(int choose_mut)
+{
+ exec_lua("lose_corruption()");
+ return (FALSE);
+}
+
+bool_ lose_all_corruptions(void)
+{
+ exec_lua("lose_all_corruptions()");
+ return (FALSE);
+}
+
+bool_ get_hack_dir(int *dp)
+{
+ int dir;
+ cptr p;
+ char command;
+
+
+ /* Initialize */
+ (*dp) = 0;
+
+ /* Global direction */
+ dir = 0;
+
+ /* (No auto-targetting */
+
+ /* Ask until satisfied */
+ while (!dir)
+ {
+ /* Choose a prompt */
+ if (!target_okay())
+ {
+ p = "Direction ('*' to choose a target, Escape to cancel)? ";
+ }
+ else
+ {
+ p = "Direction ('5' for target, '*' to re-target, Escape to cancel)? ";
+ }
+
+ /* Get a command (or Cancel) */
+ if (!get_com(p, &command)) break;
+
+ /* Convert various keys to "standard" keys */
+ switch (command)
+ {
+ /* Use current target */
+ case 'T':
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ dir = 5;
+ break;
+ }
+
+ /* Set new target */
+ case '*':
+ {
+ if (target_set(TARGET_KILL)) dir = 5;
+ break;
+ }
+
+ default:
+ {
+ /* Look up the direction */
+ dir = get_keymap_dir(command);
+
+ break;
+ }
+ }
+
+ /* Verify requested targets */
+ if ((dir == 5) && !target_okay()) dir = 0;
+
+ /* Error */
+ if (!dir) bell();
+ }
+
+ /* No direction */
+ if (!dir) return (FALSE);
+
+ /* Save the direction */
+ command_dir = dir;
+
+ /* Check for confusion */
+ if (p_ptr->confused)
+ {
+ /* XXX XXX XXX */
+ /* Random direction */
+ dir = ddd[rand_int(8)];
+ }
+
+ /* Notice confusion */
+ if (command_dir != dir)
+ {
+ /* Warn the user */
+ msg_print("You are confused.");
+ }
+
+ /* Save direction */
+ (*dp) = dir;
+
+ /* A "valid" direction was entered */
+ return (TRUE);
+}
+
+/*
+ * Do we have at least one corruption?
+ */
+bool_ got_corruptions()
+{
+ int i, max;
+
+ max = exec_lua("return __corruptions_max");
+
+ for (i = 0; i < max; i++)
+ {
+ if (exec_lua(format("if test_depend_corrupt(%d) == TRUE then return TRUE else return FALSE end", i)))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Dump the corruption list
+ */
+void dump_corruptions(FILE *fff, bool_ color)
+{
+ int i, max;
+
+ if (!fff) return;
+
+ max = exec_lua("return __corruptions_max");
+
+ for (i = 0; i < max; i++)
+ {
+ if (exec_lua(format("if test_depend_corrupt(%d) == TRUE then return TRUE else return FALSE end", i)))
+ {
+ int c = exec_lua(format("return __corruptions[%d].color", i));
+
+ if (color)
+ fprintf(fff, "#####%c%s:\n", conv_color[c], string_exec_lua(format("return __corruptions[%d].name", i)));
+ else
+ fprintf(fff, "%s:\n", string_exec_lua(format("return __corruptions[%d].name", i)));
+ fprintf(fff, "%s\n", string_exec_lua(format("return __corruptions[%d].desc", i)));
+ }
+ }
+}
+
+/*
+ * Set "p_ptr->grace", notice observable changes
+ */
+void set_grace(s32b v)
+{
+ if (v < -300000) v = -300000;
+ if (v > 300000) v = 300000;
+ p_ptr->grace = v;
+ p_ptr->update |= PU_BONUS;
+ p_ptr->redraw |= (PR_PIETY);
+ handle_stuff();
+}
+
+bool_ test_object_wish(char *name, object_type *o_ptr, object_type *forge, char *what)
+{
+ int i, j, jb, save_aware;
+ char buf[200];
+
+ /* try all objects, this *IS* a very ugly and slow method :( */
+ for (i = 0; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ o_ptr = forge;
+
+ if (!k_ptr->name) continue;
+ if (k_ptr->flags3 & TR3_NORM_ART) continue;
+ if (k_ptr->flags3 & TR3_INSTA_ART) continue;
+ if (k_ptr->tval == TV_GOLD) continue;
+
+ object_prep(o_ptr, i);
+ o_ptr->name1 = 0;
+ o_ptr->name2 = 0;
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ /* Hack : aware status must be restored after describing the item name */
+ save_aware = k_ptr->aware;
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ object_desc(buf, o_ptr, FALSE, 0);
+ strlower(buf);
+ k_ptr->aware = save_aware;
+
+ if (strstr(name, buf) ||
+ /* Hack hack hackery */
+ (o_ptr->tval == TV_ROD_MAIN && strstr(name, "rod of")))
+ {
+ /* try all ego */
+ for (j = max_e_idx - 1; j >= 0; j--)
+ {
+ ego_item_type *e_ptr = &e_info[j];
+ bool_ ok = FALSE;
+
+ if (j && !e_ptr->name) continue;
+
+ /* Must have the correct fields */
+ if (j)
+ {
+ int z;
+
+ for (z = 0; z < 6; z++)
+ {
+ if (e_ptr->tval[z] == k_ptr->tval)
+ {
+ if ((e_ptr->min_sval[z] <= k_ptr->sval) &&
+ (e_ptr->max_sval[z] >= k_ptr->sval)) ok = TRUE;
+ }
+ if (ok) break;
+ }
+ if (!ok)
+ {
+ continue;
+ }
+ }
+
+ /* try all ego */
+ for (jb = max_e_idx - 1; jb >= 0; jb--)
+ {
+ ego_item_type *eb_ptr = &e_info[jb];
+ bool_ ok = FALSE;
+
+ if (jb && !eb_ptr->name) continue;
+
+ if (j && jb && (e_ptr->before == eb_ptr->before)) continue;
+
+ /* Must have the correct fields */
+ if (jb)
+ {
+ int z;
+
+ for (z = 0; z < 6; z++)
+ {
+ if (eb_ptr->tval[z] == k_ptr->tval)
+ {
+ if ((eb_ptr->min_sval[z] <= k_ptr->sval) &&
+ (eb_ptr->max_sval[z] >= k_ptr->sval)) ok = TRUE;
+ }
+ if (ok) break;
+ }
+ if (!ok)
+ {
+ continue;
+ }
+ }
+
+ object_prep(o_ptr, i);
+ o_ptr->name1 = 0;
+ o_ptr->name2 = j;
+ o_ptr->name2b = jb;
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ object_desc(buf, o_ptr, FALSE, 0);
+ strlower(buf);
+
+ if (!stricmp(buf, name))
+ {
+ /* Don't search any more */
+ return TRUE;
+ }
+ else
+ {
+ /* Restore again the aware status */
+ k_ptr->aware = save_aware;
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+void clean_wish_name(char *buf, char *name)
+{
+ char *p;
+ int i, j;
+
+ /* Lowercase the wish */
+ strlower(buf);
+
+ /* Nuke uneccesary spaces */
+ p = buf;
+ while (*p == ' ') p++;
+ i = 0;
+ j = 0;
+ while (p[i])
+ {
+ if ((p[i] == ' ') && (p[i + 1] == ' '))
+ {
+ i++;
+ continue;
+ }
+ name[j++] = p[i++];
+ }
+ name[j++] = '\0';
+ if (j)
+ {
+ j--;
+ while (j && (name[j] == ' '))
+ {
+ name[j] = '\0';
+ j--;
+ }
+ }
+}
+
+/*
+ * Allow the player to make a wish
+ */
+void make_wish(void)
+{
+ char buf[200], name[200], *mname;
+ int i, j, mstatus = MSTATUS_ENEMY;
+ object_type forge, *o_ptr = &forge;
+
+ /* Make an empty string */
+ buf[0] = 0;
+
+ /* Ask for the wish */
+ if (!get_string("Wish for what? ", buf, 80)) return;
+
+ clean_wish_name(buf, name);
+
+ /* You can't wish for a wish! */
+ if (strstr(name, "wish"))
+ {
+ msg_print("You can't wish for a wish!");
+ return;
+ }
+
+ if (test_object_wish(name, o_ptr, &forge, "wish"))
+ {
+ msg_print("Your wish becomes truth!");
+
+ /* Give it to the player */
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+
+ return;
+ }
+
+ /* try monsters */
+ if (prefix(name, "enemy "))
+ {
+ mstatus = MSTATUS_ENEMY;
+ mname = name + 6;
+ }
+ else if (prefix(name, "neutral "))
+ {
+ mstatus = MSTATUS_NEUTRAL;
+ mname = name + 8;
+ }
+ else if (prefix(name, "friendly "))
+ {
+ mstatus = MSTATUS_FRIEND;
+ mname = name + 9;
+ }
+ else if (prefix(name, "pet "))
+ {
+ mstatus = MSTATUS_PET;
+ mname = name + 4;
+ }
+ else if (prefix(name, "companion "))
+ {
+ if (can_create_companion()) mstatus = MSTATUS_COMPANION;
+ else mstatus = MSTATUS_PET;
+ mname = name + 10;
+ }
+ else mname = name;
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ if (!r_ptr->name) continue;
+
+ if (r_ptr->flags9 & RF9_SPECIAL_GENE) continue;
+ if (r_ptr->flags9 & RF9_NEVER_GENE) continue;
+ if (r_ptr->flags1 & RF1_UNIQUE) continue;
+
+ sprintf(buf, "%s", r_ptr->name + r_name);
+ strlower(buf);
+
+ if (strstr(mname, buf))
+ {
+ /* try all ego */
+ for (j = max_re_idx - 1; j >= 0; j--)
+ {
+ monster_ego *re_ptr = &re_info[j];
+
+ if (j && !re_ptr->name) continue;
+
+ if (!mego_ok(i, j)) continue;
+
+ if (j)
+ {
+ if (re_ptr->before) sprintf(buf, "%s %s", re_name + re_ptr->name, r_ptr->name + r_name);
+ else sprintf(buf, "%s %s", r_ptr->name + r_name, re_name + re_ptr->name);
+ }
+ else
+ {
+ sprintf(buf, "%s", r_ptr->name + r_name);
+ }
+ strlower(buf);
+
+ if (!stricmp(mname, buf))
+ {
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 100;
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 5);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ /* Create the monster */
+ if (place_monster_one(wy, wx, i, j, FALSE, mstatus))
+ msg_print("Your wish becomes truth!");
+
+ /* Don't search any more */
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Corrupted have a 1/3 chance of losing a mutation each time this is called,
+ * assuming they have any in the first place
+ */
+void corrupt_corrupted(void)
+{
+ if (magik(45))
+ {
+ lose_corruption(0);
+ }
+ else
+ {
+ gain_random_corruption(0);
+ }
+
+ /* We are done. */
+ return;
+}
+
+/*
+ * Change to an other class
+ */
+void switch_class(int sclass)
+{
+ p_ptr->pclass = sclass;
+ cp_ptr = &class_info[p_ptr->pclass];
+}
+
+/*
+ * Change to an other subclass
+ */
+void switch_subclass(int sclass)
+{
+ p_ptr->pspec = sclass;
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+}
+
+/*
+ * Change to an other subrace
+ */
+void switch_subrace(int racem, bool_ copy_old)
+{
+ if ((racem < 0) && (racem >= max_rmp_idx)) return;
+
+ /* If we switch to the saved subrace, we copy over the old subrace data */
+ if (copy_old && (racem == SUBRACE_SAVE))
+ {
+ s32b old_title = race_mod_info[SUBRACE_SAVE].title;
+ s32b old_desc = race_mod_info[SUBRACE_SAVE].desc;
+
+ COPY(&race_mod_info[SUBRACE_SAVE], &race_mod_info[p_ptr->pracem], player_race_mod);
+
+ race_mod_info[SUBRACE_SAVE].title = old_title;
+ race_mod_info[SUBRACE_SAVE].desc = old_desc;
+ strcpy(race_mod_info[SUBRACE_SAVE].title + rmp_name, race_mod_info[p_ptr->pracem].title + rmp_name);
+ }
+
+ p_ptr->pracem = racem;
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+}
+
+cptr get_subrace_title(int racem)
+{
+ return race_mod_info[racem].title + rmp_name;
+}
+
+void set_subrace_title(int racem, cptr name)
+{
+ strcpy(race_mod_info[racem].title + rmp_name, name);
+}
+
+/*
+ * Rebirth, recalc hp & exp/level
+ */
+void do_rebirth()
+{
+ /* Experience factor */
+ p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp;
+
+ /* Hitdice */
+ p_ptr->hitdie = rp_ptr->r_mhp + rmp_ptr->r_mhp + cp_ptr->c_mhp;
+
+ /* Recalc HP */
+ do_cmd_rerate();
+
+ /* Change the level if needed */
+ check_experience();
+ p_ptr->max_plv = p_ptr->lev;
+
+ /* Redraw/calc stuff */
+ p_ptr->redraw |= (PR_BASIC);
+ p_ptr->update |= (PU_BONUS);
+ handle_stuff();
+
+ lite_spot(p_ptr->py, p_ptr->px);
+}
+
+/*
+ * Quick mimic name to index function
+ */
+int resolve_mimic_name(cptr name)
+{
+ s32b idx;
+
+ call_lua("resolve_mimic_name", "(s)", "d", name, &idx);
+ return idx;
+}
diff --git a/src/z-form.c b/src/z-form.c
new file mode 100644
index 00000000..b3d5d005
--- /dev/null
+++ b/src/z-form.c
@@ -0,0 +1,831 @@
+/* File: z-form.c */
+
+/* Purpose: Low level text formatting -BEN- */
+
+#include "z-form.h"
+
+#include "z-util.h"
+#include "z-virt.h"
+
+
+/*
+ * Here is some information about the routines in this file.
+ *
+ * In general, the following routines take a "buffer", a "max length",
+ * a "format string", and some "arguments", and use the format string
+ * and the arguments to create a (terminated) string in the buffer
+ * (using only the first "max length" bytes), and return the "length"
+ * of the resulting string, not including the (mandatory) terminator.
+ *
+ * The format strings allow the basic "sprintf()" format sequences, though
+ * some of them are processed slightly more carefully or portably, as well
+ * as a few "special" sequences, including the "%r" and "%v" sequences, and
+ * the "capilitization" sequences of "%C", "%S", and "%V".
+ *
+ * Note that some "limitations" are enforced by the current implementation,
+ * for example, no "format sequence" can exceed 100 characters, including any
+ * "length" restrictions, and the result of combining and "format sequence"
+ * with the relevent "arguments" must not exceed 1000 characters.
+ *
+ * These limitations could be fixed by stealing some of the code from,
+ * say, "vsprintf()" and placing it into my "vstrnfmt()" function.
+ *
+ * Note that a "^" inside a "format sequence" causes the first non-space
+ * character in the string resulting from the combination of the format
+ * sequence and the argument(s) to be "capitalized" if possible. Note
+ * that the "^" character is removed before the "standard" formatting
+ * routines are called. Likewise, a "*" inside a "format sequence" is
+ * removed from the "format sequence", and replaced by the textual form
+ * of the next argument in the argument list. See examples below.
+ *
+ * Legal format characters: %,n,p,c,s,d,i,o,u,X,x,E,e,F,f,G,g,r,v.
+ *
+ * Format("%%")
+ * Append the literal "%".
+ * No legal modifiers.
+ *
+ * Format("%n", int *np)
+ * Save the current length into (*np).
+ * No legal modifiers.
+ *
+ * Format("%p", vptr v)
+ * Append the pointer "v" (implementation varies).
+ * No legal modifiers.
+ *
+ * Format("%E", double r)
+ * Format("%F", double r)
+ * Format("%G", double r)
+ * Format("%e", double r)
+ * Format("%f", double r)
+ * Format("%g", double r)
+ * Append the double "r", in various formats.
+ *
+ * Format("%ld", long int i)
+ * Append the long integer "i".
+ *
+ * Format("%d", int i)
+ * Append the integer "i".
+ *
+ * Format("%lu", unsigned long int i)
+ * Append the unsigned long integer "i".
+ *
+ * Format("%u", unsigned int i)
+ * Append the unsigned integer "i".
+ *
+ * Format("%lo", unsigned long int i)
+ * Append the unsigned long integer "i", in octal.
+ *
+ * Format("%o", unsigned int i)
+ * Append the unsigned integer "i", in octal.
+ *
+ * Format("%lX", unsigned long int i)
+ * Note -- use all capital letters
+ * Format("%lx", unsigned long int i)
+ * Append the unsigned long integer "i", in hexidecimal.
+ *
+ * Format("%X", unsigned int i)
+ * Note -- use all capital letters
+ * Format("%x", unsigned int i)
+ * Append the unsigned integer "i", in hexidecimal.
+ *
+ * Format("%c", char c)
+ * Append the character "c".
+ * Do not use the "+" or "0" flags.
+ *
+ * Format("%s", cptr s)
+ * Append the string "s".
+ * Do not use the "+" or "0" flags.
+ * Note that a "NULL" value of "s" is converted to the empty string.
+ *
+ * Format("%V", vptr v)
+ * Note -- possibly significant mode flag
+ * Format("%v", vptr v)
+ * Append the object "v", using the current "user defined print routine".
+ * User specified modifiers, often ignored.
+ *
+ * Format("%r", vstrnfmt_aux_func *fp)
+ * Set the "user defined print routine" (vstrnfmt_aux) to "fp".
+ * No legal modifiers.
+ *
+ *
+ * For examples below, assume "int n = 0; int m = 100; char buf[100];",
+ * plus "char *s = NULL;", and unknown values "char *txt; int i;".
+ *
+ * For example: "n = strnfmt(buf, -1, "(Max %d)", i);" will have a
+ * similar effect as "sprintf(buf, "(Max %d)", i); n = strlen(buf);".
+ *
+ * For example: "(void)strnfmt(buf, 16, "%s", txt);" will have a similar
+ * effect as "strncpy(buf, txt, 16); buf[15] = '\0';".
+ *
+ * For example: "if (strnfmt(buf, 16, "%s", txt) < 16) ..." will have
+ * a similar effect as "strcpy(buf, txt)" but with bounds checking.
+ *
+ * For example: "s = buf; s += vstrnfmt(s, -1, ...); ..." will allow
+ * multiple "appends" to "buf" (at the cost of losing the max-length info).
+ *
+ * For example: "s = buf; n = vstrnfmt(s+n, 100-n, ...); ..." will allow
+ * multiple bounded "appends" to "buf", with constant access to "strlen(buf)".
+ *
+ * For example: "format("The %r%v was destroyed!", obj_desc, obj);"
+ * (where "obj_desc(buf, max, fmt, obj)" will "append" a "description"
+ * of the given object to the given buffer, and return the total length)
+ * will return a "useful message" about the object "obj", for example,
+ * "The Large Shield was destroyed!".
+ *
+ * For example: "format("%^-.*s", i, txt)" will produce a string containing
+ * the first "i" characters of "txt", left justified, with the first non-space
+ * character capitilized, if reasonable.
+ */
+
+
+
+
+
+/*
+ * The "type" of the "user defined print routine" pointer
+ */
+typedef uint (*vstrnfmt_aux_func)(char *buf, uint max, cptr fmt, vptr arg);
+
+/*
+ * The "default" user defined print routine. Ignore the "fmt" string.
+ */
+static uint vstrnfmt_aux_dflt(char *buf, uint max, cptr fmt, vptr arg)
+{
+ uint len;
+ char tmp[32];
+
+ /* XXX XXX */
+ fmt = fmt ? fmt : 0;
+
+ /* Pointer display */
+ sprintf(tmp, "<<%p>>", arg);
+ len = strlen(tmp);
+ if (len >= max) len = max - 1;
+ tmp[len] = '\0';
+ strcpy(buf, tmp);
+ return (len);
+}
+
+/*
+ * The "current" user defined print routine. It can be changed
+ * dynamically by sending the proper "%r" sequence to "vstrnfmt()"
+ */
+static vstrnfmt_aux_func vstrnfmt_aux = vstrnfmt_aux_dflt;
+
+
+
+/*
+ * Basic "vararg" format function.
+ *
+ * This function takes a buffer, a max byte count, a format string, and
+ * a va_list of arguments to the format string, and uses the format string
+ * and the arguments to create a string to the buffer. The string is
+ * derived from the format string and the arguments in the manner of the
+ * "sprintf()" function, but with some extra "format" commands. Note that
+ * this function will never use more than the given number of bytes in the
+ * buffer, preventing messy invalid memory references. This function then
+ * returns the total number of non-null bytes written into the buffer.
+ *
+ * Method: Let "str" be the (unlimited) created string, and let "len" be the
+ * smaller of "max-1" and "strlen(str)". We copy the first "len" chars of
+ * "str" into "buf", place "\0" into buf[len], and return "len".
+ *
+ * In English, we do a sprintf() into "buf", a buffer with size "max",
+ * and we return the resulting value of "strlen(buf)", but we allow some
+ * special format commands, and we are more careful than "sprintf()".
+ *
+ * Typically, "max" is in fact the "size" of "buf", and thus represents
+ * the "number" of chars in "buf" which are ALLOWED to be used. An
+ * alternative definition would have required "buf" to hold at least
+ * "max+1" characters, and would have used that extra character only
+ * in the case where "buf" was too short for the result. This would
+ * give an easy test for "overflow", but a less "obvious" semantics.
+ *
+ * Note that if the buffer was "too short" to hold the result, we will
+ * always return "max-1", but we also return "max-1" if the buffer was
+ * "just long enough". We could have returned "max" if the buffer was
+ * too short, not written a null, and forced the programmer to deal with
+ * this special case, but I felt that it is better to at least give a
+ * "usable" result when the buffer was too long instead of either giving
+ * a memory overwrite like "sprintf()" or a non-terminted string like
+ * "strncpy()". Note that "strncpy()" also "null-pads" the result.
+ *
+ * Note that in most cases "just long enough" is probably "too short".
+ *
+ * We should also consider extracting and processing the "width" and other
+ * "flags" by hand, it might be more "accurate", and it would allow us to
+ * remove the limit (1000 chars) on the result of format sequences.
+ *
+ * Also, some sequences, such as "%+d" by hand, do not work on all machines,
+ * and could thus be correctly handled here.
+ *
+ * Error detection in this routine is not very graceful, in particular,
+ * if an error is detected in the format string, we simply "pre-terminate"
+ * the given buffer to a length of zero, and return a "length" of zero.
+ * The contents of "buf", except for "buf[0]", may then be undefined.
+ */
+uint vstrnfmt(char *buf, uint max, cptr fmt, va_list vp)
+{
+ cptr s;
+
+ /* The argument is "long" */
+ bool_ do_long;
+
+ /* The argument needs "processing" */
+ bool_ do_xtra;
+
+ /* Bytes used in buffer */
+ uint n;
+
+ /* Bytes used in format sequence */
+ uint q;
+
+ /* Format sequence */
+ char aux[128];
+
+ /* Resulting string */
+ char tmp[1024];
+
+
+ /* Mega-Hack -- treat "illegal" length as "infinite" */
+ if (!max) max = 32767;
+
+ /* Mega-Hack -- treat "no format" as "empty string" */
+ if (!fmt) fmt = "";
+
+
+ /* Begin the buffer */
+ n = 0;
+
+ /* Begin the format string */
+ s = fmt;
+
+ /* Scan the format string */
+ while (TRUE)
+ {
+ /* All done */
+ if (!*s) break;
+
+ /* Normal character */
+ if (*s != '%')
+ {
+ /* Check total length */
+ if (n == max - 1) break;
+
+ /* Save the character */
+ buf[n++] = *s++;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Skip the "percent" */
+ s++;
+
+ /* Pre-process "%%" */
+ if (*s == '%')
+ {
+ /* Check total length */
+ if (n == max - 1) break;
+
+ /* Save the percent */
+ buf[n++] = '%';
+
+ /* Skip the "%" */
+ s++;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Pre-process "%n" */
+ if (*s == 'n')
+ {
+ int *arg;
+
+ /* Access the next argument */
+ arg = va_arg(vp, int *);
+
+ /* Save the current length */
+ (*arg) = n;
+
+ /* Skip the "n" */
+ s++;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Hack -- Pre-process "%r" */
+ if (*s == 'r')
+ {
+ /* Extract the next argument, and save it (globally) */
+ vstrnfmt_aux = va_arg(vp, vstrnfmt_aux_func);
+
+ /* Skip the "r" */
+ s++;
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Begin the "aux" string */
+ q = 0;
+
+ /* Save the "percent" */
+ aux[q++] = '%';
+
+ /* Assume no "long" argument */
+ do_long = FALSE;
+
+ /* Assume no "xtra" processing */
+ do_xtra = FALSE;
+
+ /* Build the "aux" string */
+ while (TRUE)
+ {
+ /* Error -- format sequence is not terminated */
+ if (!*s)
+ {
+ /* Terminate the buffer */
+ buf[0] = '\0';
+
+ /* Return "error" */
+ return (0);
+ }
+
+ /* Error -- format sequence may be too long */
+ if (q > 100)
+ {
+ /* Terminate the buffer */
+ buf[0] = '\0';
+
+ /* Return "error" */
+ return (0);
+ }
+
+ /* Handle "alphabetic" chars */
+ if (isalpha(*s))
+ {
+ /* Hack -- handle "long" request */
+ if (*s == 'l')
+ {
+ /* Save the character */
+ aux[q++] = *s++;
+
+ /* Note the "long" flag */
+ do_long = TRUE;
+ }
+
+ /* Mega-Hack -- handle "extra-long" request */
+ else if (*s == 'L')
+ {
+ /* Error -- illegal format char */
+ buf[0] = '\0';
+
+ /* Return "error" */
+ return (0);
+ }
+
+ /* Handle normal end of format sequence */
+ else
+ {
+ /* Save the character */
+ aux[q++] = *s++;
+
+ /* Stop processing the format sequence */
+ break;
+ }
+ }
+
+ /* Handle "non-alphabetic" chars */
+ else
+ {
+ /* Hack -- Handle 'star' (for "variable length" argument) */
+ if (*s == '*')
+ {
+ int arg;
+
+ /* Access the next argument */
+ arg = va_arg(vp, int);
+
+ /* Hack -- append the "length" */
+ sprintf(aux + q, "%d", arg);
+
+ /* Hack -- accept the "length" */
+ while (aux[q]) q++;
+
+ /* Skip the "*" */
+ s++;
+ }
+
+ /* Mega-Hack -- Handle 'caret' (for "uppercase" request) */
+ else if (*s == '^')
+ {
+ /* Note the "xtra" flag */
+ do_xtra = TRUE;
+
+ /* Skip the "^" */
+ s++;
+ }
+
+ /* Collect "normal" characters (digits, "-", "+", ".", etc) */
+ else
+ {
+ /* Save the character */
+ aux[q++] = *s++;
+ }
+ }
+ }
+
+
+ /* Terminate "aux" */
+ aux[q] = '\0';
+
+ /* Clear "tmp" */
+ tmp[0] = '\0';
+
+ /* Process the "format" char */
+ switch (aux[q - 1])
+ {
+ /* Simple Character -- standard format */
+ case 'c':
+ {
+ int arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, int);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+ /* Signed Integers -- standard format */
+ case 'd':
+ case 'i':
+ {
+ if (do_long)
+ {
+ long arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, long);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+ }
+ else
+ {
+ int arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, int);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+ }
+
+ /* Done */
+ break;
+ }
+
+ /* Unsigned Integers -- various formats */
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ {
+ if (do_long)
+ {
+ unsigned long arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, unsigned long);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+ }
+ else
+ {
+ unsigned int arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, unsigned int);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+ }
+
+ /* Done */
+ break;
+ }
+
+ /* Floating Point -- various formats */
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ {
+ double arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, double);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+ /* Pointer -- implementation varies */
+ case 'p':
+ {
+ vptr arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, vptr);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+ /* String */
+ case 's':
+ {
+ cptr arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, cptr);
+
+ /* Hack -- convert NULL to EMPTY */
+ if (!arg) arg = "";
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+ /* User defined data */
+ case 'V':
+ case 'v':
+ {
+ vptr arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, vptr);
+
+ /* Format the "user data" */
+ (void)vstrnfmt_aux(tmp, 1000, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+
+ /* Oops */
+ default:
+ {
+ /* Error -- illegal format char */
+ buf[0] = '\0';
+
+ /* Return "error" */
+ return (0);
+ }
+ }
+
+
+ /* Mega-Hack -- handle "capitilization" */
+ if (do_xtra)
+ {
+ /* Now append "tmp" to "buf" */
+ for (q = 0; tmp[q]; q++)
+ {
+ /* Notice first non-space */
+ if (!isspace(tmp[q]))
+ {
+ /* Capitalize if possible */
+ if (islower(tmp[q])) tmp[q] = toupper(tmp[q]);
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+ /* Now append "tmp" to "buf" */
+ for (q = 0; tmp[q]; q++)
+ {
+ /* Check total length */
+ if (n == max - 1) break;
+
+ /* Save the character */
+ buf[n++] = tmp[q];
+ }
+ }
+
+
+ /* Terminate buffer */
+ buf[n] = '\0';
+
+ /* Return length */
+ return (n);
+}
+
+
+/*
+ * Do a vstrnfmt (see above) into a (growable) static buffer.
+ * This buffer is usable for very short term formatting of results.
+ */
+char *vformat(cptr fmt, va_list vp)
+{
+ static char *format_buf = NULL;
+ static huge format_len = 0;
+
+ /* Initial allocation */
+ if (!format_buf)
+ {
+ format_len = 1024;
+ C_MAKE(format_buf, format_len, char);
+ }
+
+ /* Null format yields last result */
+ if (!fmt) return (format_buf);
+
+ /* Keep going until successful */
+ while (1)
+ {
+ uint len;
+
+ /* Build the string */
+ len = vstrnfmt(format_buf, format_len, fmt, vp);
+
+ /* Success */
+ if (len < format_len - 1) break;
+
+ /* Grow the buffer */
+ C_KILL(format_buf, format_len, char);
+ format_len = format_len * 2;
+ C_MAKE(format_buf, format_len, char);
+ }
+
+ /* Return the new buffer */
+ return (format_buf);
+}
+
+
+
+/*
+ * Do a vstrnfmt (see above) into a buffer of a given size.
+ */
+uint strnfmt(char *buf, uint max, cptr fmt, ...)
+{
+ uint len;
+
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Do a virtual fprintf to stderr */
+ len = vstrnfmt(buf, max, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Return the number of bytes written */
+ return (len);
+}
+
+
+/*
+ * Do a vstrnfmt (see above) into a buffer of unknown size.
+ * Since the buffer size is unknown, the user better verify the args.
+ */
+uint strfmt(char *buf, cptr fmt, ...)
+{
+ uint len;
+
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Build the string, assume 32K buffer */
+ len = vstrnfmt(buf, 32767, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Return the number of bytes written */
+ return (len);
+}
+
+
+
+
+/*
+ * Do a vstrnfmt() into (see above) into a (growable) static buffer.
+ * This buffer is usable for very short term formatting of results.
+ * Note that the buffer is (technically) writable, but only up to
+ * the length of the string contained inside it.
+ */
+char *format(cptr fmt, ...)
+{
+ char *res;
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args */
+ res = vformat(fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Return the result */
+ return (res);
+}
+
+
+
+
+/*
+ * Vararg interface to plog()
+ */
+void plog_fmt(cptr fmt, ...)
+{
+ char *res;
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args */
+ res = vformat(fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Call plog */
+ plog(res);
+}
+
+
+
+/*
+ * Vararg interface to quit()
+ */
+void quit_fmt(cptr fmt, ...)
+{
+ char *res;
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format */
+ res = vformat(fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Call quit() */
+ quit(res);
+}
+
+
+
+/*
+ * Vararg interface to core()
+ */
+void core_fmt(cptr fmt, ...)
+{
+ char *res;
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* If requested, Do a virtual fprintf to stderr */
+ res = vformat(fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Call core() */
+ core(res);
+}
+
+
diff --git a/src/z-form.h b/src/z-form.h
new file mode 100644
index 00000000..2dcfa96c
--- /dev/null
+++ b/src/z-form.h
@@ -0,0 +1,54 @@
+/* File z-form.h */
+
+#ifndef INCLUDED_Z_FORM_H
+#define INCLUDED_Z_FORM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "h-basic.h"
+
+/*
+ * This file provides functions very similar to "sprintf()", but which
+ * not only parse some additional "format sequences", but also enforce
+ * bounds checking, and allow repeated "appends" to the same buffer.
+ *
+ * See "z-form.c" for more detailed information about the routines,
+ * including a list of the legal "format sequences".
+ *
+ * This file makes use of both "z-util.c" and "z-virt.c"
+ */
+
+
+/**** Available Functions ****/
+
+/* Format arguments into given bounded-length buffer */
+extern uint vstrnfmt(char *buf, uint max, cptr fmt, va_list vp);
+
+/* Simple interface to "vstrnfmt()" */
+extern uint strnfmt(char *buf, uint max, cptr fmt, ...);
+
+/* Simple interface to "vstrnfmt()", assuming infinite length */
+extern uint strfmt(char *buf, cptr fmt, ...);
+
+/* Format arguments into a static resizing buffer */
+extern char *vformat(cptr fmt, va_list vp);
+
+/* Simple interface to "vformat()" */
+extern char *format(cptr fmt, ...);
+
+/* Vararg interface to "plog()", using "format()" */
+extern void plog_fmt(cptr fmt, ...);
+
+/* Vararg interface to "quit()", using "format()" */
+extern void quit_fmt(cptr fmt, ...);
+
+/* Vararg interface to "core()", using "format()" */
+extern void core_fmt(cptr fmt, ...);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/src/z-rand.c b/src/z-rand.c
new file mode 100644
index 00000000..ca5b49ae
--- /dev/null
+++ b/src/z-rand.c
@@ -0,0 +1,355 @@
+/* File: z-rand.c */
+
+/* Purpose: a simple random number generator -BEN- */
+
+#include "z-rand.h"
+
+
+
+
+/*
+ * Angband 2.7.9 introduced a new (optimized) random number generator,
+ * based loosely on the old "random.c" from Berkeley but with some major
+ * optimizations and algorithm changes. See below for more details.
+ *
+ * Code by myself (benh@phial.com) and Randy (randy@stat.tamu.edu).
+ *
+ * This code provides (1) a "decent" RNG, based on the "BSD-degree-63-RNG"
+ * used in Angband 2.7.8, but rather optimized, and (2) a "simple" RNG,
+ * based on the simple "LCRNG" currently used in Angband, but "corrected"
+ * to give slightly better values. Both of these are available in two
+ * flavors, first, the simple "mod" flavor, which is fast, but slightly
+ * biased at high values, and second, the simple "div" flavor, which is
+ * less fast (and potentially non-terminating) but which is not biased
+ * and is much less subject to low-bit-non-randomness problems.
+ *
+ * You can select your favorite flavor by proper definition of the
+ * "rand_int()" macro in the "defines.h" file.
+ *
+ * Note that, in Angband 2.8.0, the "state" table will be saved in the
+ * savefile, so a special "initialization" phase will be necessary.
+ *
+ * Note the use of the "simple" RNG, first you activate it via
+ * "Rand_quick = TRUE" and "Rand_value = seed" and then it is used
+ * automatically used instead of the "complex" RNG, and when you are
+ * done, you de-activate it via "Rand_quick = FALSE" or choose a new
+ * seed via "Rand_value = seed".
+ */
+
+
+/*
+ * Random Number Generator -- Linear Congruent RNG
+ */
+#define LCRNG(X) ((X) * 1103515245 + 12345)
+
+
+
+/*
+ * Use the "simple" LCRNG
+ */
+bool_ Rand_quick = TRUE;
+
+
+/*
+ * Current "value" of the "simple" RNG
+ */
+u32b Rand_value;
+
+
+/*
+ * Current "index" for the "complex" RNG
+ */
+u16b Rand_place;
+
+/*
+ * Current "state" table for the "complex" RNG
+ */
+u32b Rand_state[RAND_DEG];
+
+
+
+/*
+ * Initialize the "complex" RNG using a new seed
+ */
+void Rand_state_init(u32b seed)
+{
+ int i, j;
+
+ /* Seed the table */
+ Rand_state[0] = seed;
+
+ /* Propagate the seed */
+ for (i = 1; i < RAND_DEG; i++) Rand_state[i] = LCRNG(Rand_state[i - 1]);
+
+ /* Cycle the table ten times per degree */
+ for (i = 0; i < RAND_DEG * 10; i++)
+ {
+ /* Acquire the next index */
+ j = Rand_place + 1;
+ if (j == RAND_DEG) j = 0;
+
+ /* Update the table, extract an entry */
+ Rand_state[j] += Rand_state[Rand_place];
+
+ /* Advance the index */
+ Rand_place = j;
+ }
+}
+
+
+/*
+ * Extract a "random" number from 0 to m-1, via "modulus"
+ *
+ * Note that "m" should probably be less than 500000, or the
+ * results may be rather biased towards low values.
+ */
+s32b Rand_mod(s32b m)
+{
+ int j;
+ u32b r;
+
+ /* Hack -- simple case */
+ if (m <= 1) return (0);
+
+ /* Use the "simple" RNG */
+ if (Rand_quick)
+ {
+ /* Cycle the generator */
+ r = (Rand_value = LCRNG(Rand_value));
+
+ /* Mutate a 28-bit "random" number */
+ r = (r >> 4) % m;
+ }
+
+ /* Use the "complex" RNG */
+ else
+ {
+ /* Acquire the next index */
+ j = Rand_place + 1;
+ if (j == RAND_DEG) j = 0;
+
+ /* Update the table, extract an entry */
+ r = (Rand_state[j] += Rand_state[Rand_place]);
+
+ /* Advance the index */
+ Rand_place = j;
+
+ /* Extract a "random" number */
+ r = (r >> 4) % m;
+ }
+
+ /* Use the value */
+ return (r);
+}
+
+
+/*
+ * Extract a "random" number from 0 to m-1, via "division"
+ *
+ * This method selects "random" 28-bit numbers, and then uses
+ * division to drop those numbers into "m" different partitions,
+ * plus a small non-partition to reduce bias, taking as the final
+ * value the first "good" partition that a number falls into.
+ *
+ * This method has no bias, and is much less affected by patterns
+ * in the "low" bits of the underlying RNG's.
+ */
+s32b Rand_div(s32b m)
+{
+ u32b r, n;
+
+ /* Hack -- simple case */
+ if (m <= 1) return (0);
+
+ /* Partition size */
+ n = (0x10000000 / m);
+
+ /* Use a simple RNG */
+ if (Rand_quick)
+ {
+ /* Wait for it */
+ while (1)
+ {
+ /* Cycle the generator */
+ r = (Rand_value = LCRNG(Rand_value));
+
+ /* Mutate a 28-bit "random" number */
+ r = ((r >> 4) & 0x0FFFFFFF) / n;
+
+ /* Done */
+ if (r < (u32b)m) break;
+ }
+ }
+
+ /* Use a complex RNG */
+ else
+ {
+ /* Wait for it */
+ while (1)
+ {
+ int j;
+
+ /* Acquire the next index */
+ j = Rand_place + 1;
+ if (j == RAND_DEG) j = 0;
+
+ /* Update the table, extract an entry */
+ r = (Rand_state[j] += Rand_state[Rand_place]);
+
+ /* Hack -- extract a 28-bit "random" number */
+ r = ((r >> 4) & 0x0FFFFFFF) / n;
+
+ /* Advance the index */
+ Rand_place = j;
+
+ /* Done */
+ if (r < (u32b)m) break;
+ }
+ }
+
+ /* Use the value */
+ return (r);
+}
+
+
+
+
+/*
+ * The number of entries in the "randnor_table"
+ */
+#define RANDNOR_NUM 256
+
+/*
+ * The standard deviation of the "randnor_table"
+ */
+#define RANDNOR_STD 64
+
+/*
+ * The normal distribution table for the "randnor()" function (below)
+ */
+static s16b randnor_table[RANDNOR_NUM] =
+{
+ 206, 613, 1022, 1430, 1838, 2245, 2652, 3058,
+ 3463, 3867, 4271, 4673, 5075, 5475, 5874, 6271,
+ 6667, 7061, 7454, 7845, 8234, 8621, 9006, 9389,
+ 9770, 10148, 10524, 10898, 11269, 11638, 12004, 12367,
+ 12727, 13085, 13440, 13792, 14140, 14486, 14828, 15168,
+ 15504, 15836, 16166, 16492, 16814, 17133, 17449, 17761,
+ 18069, 18374, 18675, 18972, 19266, 19556, 19842, 20124,
+ 20403, 20678, 20949, 21216, 21479, 21738, 21994, 22245,
+
+ 22493, 22737, 22977, 23213, 23446, 23674, 23899, 24120,
+ 24336, 24550, 24759, 24965, 25166, 25365, 25559, 25750,
+ 25937, 26120, 26300, 26476, 26649, 26818, 26983, 27146,
+ 27304, 27460, 27612, 27760, 27906, 28048, 28187, 28323,
+ 28455, 28585, 28711, 28835, 28955, 29073, 29188, 29299,
+ 29409, 29515, 29619, 29720, 29818, 29914, 30007, 30098,
+ 30186, 30272, 30356, 30437, 30516, 30593, 30668, 30740,
+ 30810, 30879, 30945, 31010, 31072, 31133, 31192, 31249,
+
+ 31304, 31358, 31410, 31460, 31509, 31556, 31601, 31646,
+ 31688, 31730, 31770, 31808, 31846, 31882, 31917, 31950,
+ 31983, 32014, 32044, 32074, 32102, 32129, 32155, 32180,
+ 32205, 32228, 32251, 32273, 32294, 32314, 32333, 32352,
+ 32370, 32387, 32404, 32420, 32435, 32450, 32464, 32477,
+ 32490, 32503, 32515, 32526, 32537, 32548, 32558, 32568,
+ 32577, 32586, 32595, 32603, 32611, 32618, 32625, 32632,
+ 32639, 32645, 32651, 32657, 32662, 32667, 32672, 32677,
+
+ 32682, 32686, 32690, 32694, 32698, 32702, 32705, 32708,
+ 32711, 32714, 32717, 32720, 32722, 32725, 32727, 32729,
+ 32731, 32733, 32735, 32737, 32739, 32740, 32742, 32743,
+ 32745, 32746, 32747, 32748, 32749, 32750, 32751, 32752,
+ 32753, 32754, 32755, 32756, 32757, 32757, 32758, 32758,
+ 32759, 32760, 32760, 32761, 32761, 32761, 32762, 32762,
+ 32763, 32763, 32763, 32764, 32764, 32764, 32764, 32765,
+ 32765, 32765, 32765, 32766, 32766, 32766, 32766, 32767,
+};
+
+
+
+/*
+ * Generate a random integer number of NORMAL distribution
+ *
+ * The table above is used to generate a psuedo-normal distribution,
+ * in a manner which is much faster than calling a transcendental
+ * function to calculate a true normal distribution.
+ *
+ * Basically, entry 64*N in the table above represents the number of
+ * times out of 32767 that a random variable with normal distribution
+ * will fall within N standard deviations of the mean. That is, about
+ * 68 percent of the time for N=1 and 95 percent of the time for N=2.
+ *
+ * The table above contains a "faked" final entry which allows us to
+ * pretend that all values in a normal distribution are strictly less
+ * than four standard deviations away from the mean. This results in
+ * "conservative" distribution of approximately 1/32768 values.
+ *
+ * Note that the binary search takes up to 16 quick iterations.
+ */
+s16b randnor(int mean, int stand)
+{
+ s16b tmp;
+ s16b offset;
+
+ s16b low = 0;
+ s16b high = RANDNOR_NUM;
+
+ /* Paranoia */
+ if (stand < 1) return (mean);
+
+ /* Roll for probability */
+ tmp = (s16b)rand_int(32768);
+
+ /* Binary Search */
+ while (low < high)
+ {
+ int mid = (low + high) >> 1;
+
+ /* Move right if forced */
+ if (randnor_table[mid] < tmp)
+ {
+ low = mid + 1;
+ }
+
+ /* Move left otherwise */
+ else
+ {
+ high = mid;
+ }
+ }
+
+ /* Convert the index into an offset */
+ offset = (long)stand * (long)low / RANDNOR_STD;
+
+ /* One half should be negative */
+ if (rand_int(100) < 50) return (mean - offset);
+
+ /* One half should be positive */
+ return (mean + offset);
+}
+
+
+
+/*
+ * Generates damage for "2d6" style dice rolls
+ */
+s32b damroll(s16b num, s16b sides)
+{
+ int i;
+ s32b sum = 0;
+ for (i = 0; i < num; i++) sum += randint(sides);
+ return (sum);
+}
+
+
+/*
+ * Same as above, but always maximal
+ */
+s32b maxroll(s16b num, s16b sides)
+{
+ return (num * sides);
+}
+
+
+
diff --git a/src/z-rand.h b/src/z-rand.h
new file mode 100644
index 00000000..39cc958c
--- /dev/null
+++ b/src/z-rand.h
@@ -0,0 +1,96 @@
+/* File: z-rand.h */
+
+#ifndef INCLUDED_Z_RAND_H
+#define INCLUDED_Z_RAND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "h-basic.h"
+
+
+
+/**** Available constants ****/
+
+
+/*
+ * Random Number Generator -- Degree of "complex" RNG -- see "misc.c"
+ * This value is hard-coded at 63 for a wide variety of reasons.
+ */
+#define RAND_DEG 63
+
+
+
+
+/**** Available macros ****/
+
+
+/*
+ * Generates a random long integer X where O<=X<M.
+ * The integer X falls along a uniform distribution.
+ * For example, if M is 100, you get "percentile dice"
+ */
+#define rand_int(M) \
+ (Rand_div(M))
+
+/*
+ * Generates a random long integer X where A<=X<=B
+ * The integer X falls along a uniform distribution.
+ * Note: rand_range(0,N-1) == rand_int(N)
+ */
+#define rand_range(A,B) \
+ ((A) + (rand_int(1+(B)-(A))))
+
+/*
+ * Generate a random long integer X where A-D<=X<=A+D
+ * The integer X falls along a uniform distribution.
+ * Note: rand_spread(A,D) == rand_range(A-D,A+D)
+ */
+#define rand_spread(A,D) \
+ ((A) + (rand_int(1+(D)+(D))) - (D))
+
+
+/*
+ * Generate a random long integer X where 1<=X<=M
+ * Also, "correctly" handle the case of M<=1
+ */
+#define randint(M) \
+ (rand_int(M) + 1)
+
+
+/*
+ * Evaluate to TRUE "P" percent of the time
+ */
+#define magik(P) \
+ (rand_int(100) < (P))
+
+
+
+
+/**** Available Variables ****/
+
+
+extern bool_ Rand_quick;
+extern u32b Rand_value;
+extern u16b Rand_place;
+extern u32b Rand_state[RAND_DEG];
+
+
+/**** Available Functions ****/
+
+
+extern void Rand_state_init(u32b seed);
+extern s32b Rand_mod(s32b m);
+extern s32b Rand_div(s32b m);
+extern s16b randnor(int mean, int stand);
+extern s32b damroll(s16b num, s16b sides);
+extern s32b maxroll(s16b num, s16b sides);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif
diff --git a/src/z-term.c b/src/z-term.c
new file mode 100644
index 00000000..4e89ffb7
--- /dev/null
+++ b/src/z-term.c
@@ -0,0 +1,2776 @@
+/* File: z-term.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/* Purpose: a generic, efficient, terminal window package -BEN- */
+
+#include "angband.h"
+
+#include "z-term.h"
+
+#include "z-virt.h"
+
+
+/*
+ * This file provides a generic, efficient, terminal window package,
+ * which can be used not only on standard terminal environments such
+ * as dumb terminals connected to a Unix box, but also in more modern
+ * "graphic" environments, such as the Macintosh or Unix/X11.
+ *
+ * Each "window" works like a standard "dumb terminal", that is, it
+ * can display a two dimensional array of grids containing colored
+ * textual symbols, plus an optional cursor, and it can be used to
+ * get keypress events from the user.
+ *
+ * In fact, this package can simply be used, if desired, to support
+ * programs which will look the same on a dumb terminal as they do
+ * on a graphic platform such as the Macintosh.
+ *
+ * This package was designed to help port the game "Angband" to a wide
+ * variety of different platforms. Angband, like many other games in
+ * the "rogue-like" heirarchy, requires, at the minimum, the ability
+ * to display "colored textual symbols" in a standard 80x24 "window",
+ * such as that provided by most dumb terminals, and many old personal
+ * computers, and to check for "keypresses" from the user. The major
+ * concerns were thus portability and efficiency, so Angband could be
+ * easily ported to many different systems, with minimal effort, and
+ * yet would run quickly on each of these systems, no matter what kind
+ * of underlying hardware/software support was being used.
+ *
+ * It is important to understand the differences between the older
+ * "dumb terminals" and the newer "graphic interface" machines, since
+ * this package was designed to work with both types of systems.
+ *
+ * New machines:
+ * waiting for a keypress is complex
+ * checking for a keypress is often cheap
+ * changing "colors" may be expensive
+ * the "color" of a "blank" is rarely important
+ * moving the "cursor" is relatively cheap
+ * use a "software" cursor (only moves when requested)
+ * drawing characters normally will not erase old ones
+ * drawing a character on the cursor often erases it
+ * may have fast routines for "clear a region"
+ * the bottom right corner is usually not special
+ *
+ * Old machines:
+ * waiting for a keypress is simple
+ * checking for a keypress is often expensive
+ * changing "colors" is usually cheap
+ * the "color" of a "blank" may be important
+ * moving the "cursor" may be expensive
+ * use a "hardware" cursor (moves during screen updates)
+ * drawing new symbols automatically erases old ones
+ * characters may only be drawn at the cursor location
+ * drawing a character on the cursor will move the cursor
+ * may have fast routines for "clear entire window"
+ * may have fast routines for "clear to end of line"
+ * the bottom right corner is often dangerous
+ *
+ *
+ * This package provides support for multiple windows, each of an
+ * arbitrary size (up to 255x255), each with its own set of flags,
+ * and its own hooks to handle several low-level procedures which
+ * differ from platform to platform. Then the main program simply
+ * creates one or more "term" structures, setting the various flags
+ * and hooks in a manner appropriate for the current platform, and
+ * then it can use the various "term" structures without worrying
+ * about the underlying platform.
+ *
+ *
+ * This package allows each "grid" in each window to hold an attr/char
+ * pair, with each ranging from 0 to 255, and makes very few assumptions
+ * about the meaning of any attr/char values. Normally, we assume that
+ * "attr 0" is "black", with the semantics that "black" text should be
+ * sent to "Term_wipe()" instead of "Term_text()", but this sematics is
+ * modified if either the "always_pict" or the "always_text" flags are
+ * set. We assume that "char 0" is "dangerous", since placing such a
+ * "char" in the middle of a string "terminates" the string, and usually
+ * we prevent its use.
+ *
+ * Finally, we use a special attr/char pair, defaulting to "attr 0" and
+ * "char 32", also known as "black space", when we "erase" or "clear"
+ * any window, but this pair can be redefined to any pair, including
+ * the standard "white space", or the bizarre "emptiness" ("attr 0"
+ * and "char 0"), as long as various obscure restrictions are met.
+ *
+ *
+ * This package provides several functions which allow a program to
+ * interact with the "term" structures. Most of the functions allow
+ * the program to "request" certain changes to the current "term",
+ * such as moving the cursor, drawing an attr/char pair, erasing a
+ * region of grids, hiding the cursor, etc. Then there is a special
+ * function which causes all of the "pending" requests to be performed
+ * in an efficient manner. There is another set of functions which
+ * allow the program to query the "requested state" of the current
+ * "term", such as asking for the cursor location, or what attr/char
+ * is at a given location, etc. There is another set of functions
+ * dealing with "keypress" events, which allows the program to ask if
+ * the user has pressed any keys, or to forget any keys the user pressed.
+ * There is a pair of functions to allow this package to memorize the
+ * contents of the current "term", and to restore these contents at
+ * a later time. There is a special function which allows the program
+ * to specify which "term" structure should be the "current" one. At
+ * the lowest level, there is a set of functions which allow a new
+ * "term" to be initialized or destroyed, and which allow this package,
+ * or a program, to access the special "hooks" defined for the current
+ * "term", and a set of functions which those "hooks" can use to inform
+ * this package of the results of certain occurances, for example, one
+ * such function allows this package to learn about user keypresses,
+ * detected by one of the special "hooks".
+ *
+ * We provide, among other things, the functions "Term_keypress()"
+ * to "react" to keypress events, and "Term_redraw()" to redraw the
+ * entire window, plus "Term_resize()" to note a new size.
+ *
+ *
+ * Note that the current "term" contains two "window images". One of
+ * these images represents the "requested" contents of the "term", and
+ * the other represents the "actual" contents of the "term", at the time
+ * of the last performance of pending requests. This package uses these
+ * two images to determine the "minimal" amount of work needed to make
+ * the "actual" contents of the "term" match the "requested" contents of
+ * the "term". This method is not perfect, but it often reduces the
+ * amount of work needed to perform the pending requests, which thus
+ * increases the speed of the program itself. This package promises
+ * that the requested changes will appear to occur either "all at once"
+ * or in a "top to bottom" order. In addition, a "cursor" is maintained,
+ * and this cursor is updated along with the actual window contents.
+ *
+ * Currently, the "Term_fresh()" routine attempts to perform the "minimum"
+ * number of physical updates, in terms of total "work" done by the hooks
+ * Term_wipe(), Term_text(), and Term_pict(), making use of the fact that
+ * adjacent characters of the same color can both be drawn together using
+ * the "Term_text()" hook, and that "black" text can often be sent to the
+ * "Term_wipe()" hook instead of the "Term_text()" hook, and if something
+ * is already displayed in a window, then it is not necessary to display
+ * it again. Unfortunately, this may induce slightly non-optimal results
+ * in some cases, in particular, those in which, say, a string of ten
+ * characters needs to be written, but the fifth character has already
+ * been displayed. Currently, this will cause the "Term_text()" routine
+ * to be called once for each half of the string, instead of once for the
+ * whole string, which, on some machines, may be non-optimal behavior.
+ *
+ * The new formalism includes a "displayed" screen image (old) which
+ * is actually seen by the user, a "requested" screen image (scr)
+ * which is being prepared for display, a "memorized" screen image
+ * (mem) which is used to save and restore screen images, and a
+ * "temporary" screen image (tmp) which is currently unused.
+ *
+ *
+ * Several "flags" are available in each "term" to allow the underlying
+ * visual system (which initializes the "term" structure) to "optimize"
+ * the performance of this package for the given system, or to request
+ * certain behavior which is helpful/required for the given system.
+ *
+ * The "soft_cursor" flag indicates the use of a "soft" cursor, which
+ * only moves when explicitly requested,and which is "erased" when
+ * any characters are drawn on top of it. This flag is used for all
+ * "graphic" systems which handle the cursor by "drawing" it.
+ *
+ * The "icky_corner" flag indicates that the bottom right "corner"
+ * of the windows are "icky", and "printing" anything there may
+ * induce "messy" behavior, such as "scrolling". This flag is used
+ * for most old "dumb terminal" systems.
+ *
+ *
+ * The "term" structure contains the following function "hooks":
+ *
+ * Term->init_hook = Init the term
+ * Term->nuke_hook = Nuke the term
+ * Term->user_hook = Perform user actions
+ * Term->xtra_hook = Perform extra actions
+ * Term->curs_hook = Draw (or Move) the cursor
+ * Term->wipe_hook = Draw some blank spaces
+ * Term->text_hook = Draw some text in the window
+ * Term->pict_hook = Draw some attr/chars in the window
+ *
+ * The "Term->user_hook" hook provides a simple hook to an implementation
+ * defined function, with application defined semantics. It is available
+ * to the program via the "Term_user()" function.
+ *
+ * The "Term->xtra_hook" hook provides a variety of different functions,
+ * based on the first parameter (which should be taken from the various
+ * TERM_XTRA_* defines) and the second parameter (which may make sense
+ * only for some first parameters). It is available to the program via
+ * the "Term_xtra()" function, though some first parameters are only
+ * "legal" when called from inside this package.
+ *
+ * The "Term->curs_hook" hook provides this package with a simple way
+ * to "move" or "draw" the cursor to the grid "x,y", depending on the
+ * setting of the "soft_cursor" flag. Note that the cursor is never
+ * redrawn if "nothing" has happened to the screen (even temporarily).
+ * This hook is required.
+ *
+ * The "Term->wipe_hook" hook provides this package with a simple way
+ * to "erase", starting at "x,y", the next "n" grids. This hook assumes
+ * that the input is valid. This hook is required, unless the setting
+ * of the "always_pict" or "always_text" flags makes it optional.
+ *
+ * The "Term->text_hook" hook provides this package with a simple way
+ * to "draw", starting at "x,y", the "n" chars contained in "cp", using
+ * the attr "a". This hook assumes that the input is valid, and that
+ * "n" is between 1 and 256 inclusive, but it should NOT assume that
+ * the contents of "cp" are null-terminated. This hook is required,
+ * unless the setting of the "always_pict" flag makes it optional.
+ *
+ * The "Term->pict_hook" hook provides this package with a simple way
+ * to "draw", starting at "x,y", the "n" attr/char pairs contained in
+ * the arrays "ap" and "cp". This hook assumes that the input is valid,
+ * and that "n" is between 1 and 256 inclusive, but it should NOT assume
+ * that the contents of "cp" are null-terminated. This hook is optional,
+ * unless the setting of the "always_pict" or "higher_pict" flags make
+ * it required. Note that recently, this hook was changed from taking
+ * a byte "a" and a char "c" to taking a length "n", an array of bytes
+ * "ap" and an array of chars "cp". Old implementations of this hook
+ * should now iterate over all "n" attr/char pairs.
+ *
+ *
+ * The game "Angband" uses a set of files called "main-xxx.c", for
+ * various "xxx" suffixes. Most of these contain a function called
+ * "init_xxx()", that will prepare the underlying visual system for
+ * use with Angband, and then create one or more "term" structures,
+ * using flags and hooks appropriate to the given platform, so that
+ * the "main()" function can call one (or more) of the "init_xxx()"
+ * functions, as appropriate, to prepare the required "term" structs
+ * (one for each desired sub-window), and these "init_xxx()" functions
+ * are called from a centralized "main()" function in "main.c". Other
+ * "main-xxx.c" systems contain their own "main()" function which, in
+ * addition to doing everything needed to initialize the actual program,
+ * also does everything that the normal "init_xxx()" functions would do.
+ *
+ * The game "Angband" defines, in addition to "attr 0", all of the
+ * attr codes from 1 to 15, using definitions in "defines.h", and
+ * thus the "main-xxx.c" files used by Angband must handle these
+ * attr values correctly. Also, they must handle all other attr
+ * values, though they may do so in any way they wish, for example,
+ * by always taking every attr code mod 16. Many of the "main-xxx.c"
+ * files use "white space" ("attr 1" / "char 32") to "erase" or "clear"
+ * any window, for efficiency.
+ *
+ * The game "Angband" uses the "Term_user" hook to allow any of the
+ * "main-xxx.c" files to interact with the user, by calling this hook
+ * whenever the user presses the "!" key when the game is waiting for
+ * a new command. This could be used, for example, to provide "unix
+ * shell commands" to the Unix versions of the game.
+ *
+ * See "main-xxx.c" for a simple skeleton file which can be used to
+ * create a "visual system" for a new platform when porting Angband.
+ */
+
+
+
+/*
+ * The current "term"
+ */
+term *Term = NULL;
+
+/* File handler for saving movies */
+FILE *movfile = NULL;
+int do_movies = 0; /* Later set this as a global */
+/* set to 1 if you want movies made */
+time_t lastc;
+int last_paused = 0;
+int cmovie_get_msecond(void);
+
+/* Record cmovies with millisecond frame rate */
+long cmov_last_time_msec;
+long cmov_delta_time_msec;
+
+
+/*** Local routines ***/
+
+
+/*
+ * Nuke a term_win (see below)
+ */
+static errr term_win_nuke(term_win *s, int w, int h)
+{
+ /* Free the window access arrays */
+ C_KILL(s->a, h, byte*);
+ C_KILL(s->c, h, char*);
+
+ /* Free the window content arrays */
+ C_KILL(s->va, h * w, byte);
+ C_KILL(s->vc, h * w, char);
+
+ /* Free the terrain access arrays */
+ C_KILL(s->ta, h, byte*);
+ C_KILL(s->tc, h, char*);
+
+ /* Free the terrain content arrays */
+ C_KILL(s->vta, h * w, byte);
+ C_KILL(s->vtc, h * w, char);
+
+ /* Free the ego graphics access arrays */
+ C_KILL(s->ea, h, byte*);
+ C_KILL(s->ec, h, char*);
+
+ /* Free the ego graphics content arrays */
+ C_KILL(s->vea, h * w, byte);
+ C_KILL(s->vec, h * w, char);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize a "term_win" (using the given window size)
+ */
+static errr term_win_init(term_win *s, int w, int h)
+{
+ int y;
+
+ /* Make the window access arrays */
+ C_MAKE(s->a, h, byte*);
+ C_MAKE(s->c, h, char*);
+
+ /* Make the window content arrays */
+ C_MAKE(s->va, h * w, byte);
+ C_MAKE(s->vc, h * w, char);
+
+ /* Make the terrain access arrays */
+ C_MAKE(s->ta, h, byte*);
+ C_MAKE(s->tc, h, char*);
+
+ /* Make the terrain content arrays */
+ C_MAKE(s->vta, h * w, byte);
+ C_MAKE(s->vtc, h * w, char);
+
+ /* Make the ego graphics access arrays */
+ C_MAKE(s->ea, h, byte*);
+ C_MAKE(s->ec, h, char*);
+
+ /* Make the ego graphics content arrays */
+ C_MAKE(s->vea, h * w, byte);
+ C_MAKE(s->vec, h * w, char);
+
+
+ /* Prepare the window access arrays */
+ for (y = 0; y < h; y++)
+ {
+ s->a[y] = s->va + w * y;
+ s->c[y] = s->vc + w * y;
+
+ s->ta[y] = s->vta + w * y;
+ s->tc[y] = s->vtc + w * y;
+
+ s->ea[y] = s->vea + w * y;
+ s->ec[y] = s->vec + w * y;
+
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Copy a "term_win" from another
+ */
+static errr term_win_copy(term_win *s, term_win *f, int w, int h)
+{
+ int x, y;
+
+ /* Copy contents */
+ for (y = 0; y < h; y++)
+ {
+ byte *f_aa = f->a[y];
+ char *f_cc = f->c[y];
+
+ byte *s_aa = s->a[y];
+ char *s_cc = s->c[y];
+
+ byte *f_taa = f->ta[y];
+ char *f_tcc = f->tc[y];
+
+ byte *s_taa = s->ta[y];
+ char *s_tcc = s->tc[y];
+
+ byte *f_eaa = f->ea[y];
+ char *f_ecc = f->ec[y];
+
+ byte *s_eaa = s->ea[y];
+ char *s_ecc = s->ec[y];
+
+ for (x = 0; x < w; x++)
+ {
+ *s_aa++ = *f_aa++;
+ *s_cc++ = *f_cc++;
+
+ *s_taa++ = *f_taa++;
+ *s_tcc++ = *f_tcc++;
+
+ *s_eaa++ = *f_eaa++;
+ *s_ecc++ = *f_ecc++;
+ }
+ }
+
+ /* Copy cursor */
+ s->cx = f->cx;
+ s->cy = f->cy;
+ s->cu = f->cu;
+ s->cv = f->cv;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** External hooks ***/
+
+
+/*
+ * Execute the "Term->user_hook" hook, if available (see above).
+ */
+errr Term_user(int n)
+{
+ /* Verify the hook */
+ if (!Term->user_hook) return ( -1);
+
+ /* Call the hook */
+ return ((*Term->user_hook)(n));
+}
+
+/*
+ * Execute the "Term->xtra_hook" hook, if available (see above).
+ * And *hacky* get a return code
+ */
+long Term_xtra_long;
+char scansubdir_dir[1024];
+int scansubdir_max = 0;
+cptr scansubdir_result[255];
+errr Term_xtra(int n, int v)
+{
+ /* Verify the hook */
+ if (!Term->xtra_hook) return ( -1);
+
+ /* Call the hook */
+ return ((*Term->xtra_hook)(n, v));
+}
+
+
+
+/*** Fake hooks ***/
+
+
+/*
+ * Hack -- fake hook for "Term_curs()" (see above)
+ */
+static errr Term_curs_hack(int x, int y)
+{
+ /* Compiler silliness */
+ if (x || y) return ( -2);
+
+ /* Oops */
+ return ( -1);
+}
+
+/*
+ * Hack -- fake hook for "Term_wipe()" (see above)
+ */
+static errr Term_wipe_hack(int x, int y, int n)
+{
+ /* Compiler silliness */
+ if (x || y || n) return ( -2);
+
+ /* Oops */
+ return ( -1);
+}
+
+/*
+ * Hack -- fake hook for "Term_text()" (see above)
+ */
+static errr Term_text_hack(int x, int y, int n, byte a, const char *cp)
+{
+ /* Compiler silliness */
+ if (x || y || n || a || cp) return ( -2);
+
+ /* Oops */
+ return ( -1);
+}
+
+/*
+ * Hack -- fake hook for "Term_pict()" (see above)
+ */
+static errr Term_pict_hack(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+{
+ /* Compiler silliness */
+ if (x || y || n || ap || cp || tap || tcp || eap || ecp) return ( -2);
+
+ /* Oops */
+ return ( -1);
+}
+
+
+
+/*** Efficient routines ***/
+
+
+/*
+ * Mentally draw an attr/char at a given location
+ *
+ * Assumes given location and values are valid.
+ */
+void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc, byte ea, char ec)
+{
+ term_win *scrn = Term->scr;
+
+ byte *scr_aa = &scrn->a[y][x];
+ char *scr_cc = &scrn->c[y][x];
+
+ byte *scr_taa = &scrn->ta[y][x];
+ char *scr_tcc = &scrn->tc[y][x];
+
+ byte *scr_eaa = &scrn->ea[y][x];
+ char *scr_ecc = &scrn->ec[y][x];
+
+ /* Hack -- Ignore non-changes */
+ if ((*scr_aa == a) && (*scr_cc == c) &&
+ (*scr_taa == ta) && (*scr_tcc == tc) &&
+ (*scr_eaa == ea) && (*scr_ecc == ec)) return;
+
+ /* Save the "literal" information */
+ *scr_aa = a;
+ *scr_cc = c;
+
+ *scr_taa = ta;
+ *scr_tcc = tc;
+
+ *scr_eaa = ea;
+ *scr_ecc = ec;
+
+ /* Check for new min/max row info */
+ if (y < Term->y1) Term->y1 = y;
+ if (y > Term->y2) Term->y2 = y;
+
+ /* Check for new min/max col info for this row */
+ if (x < Term->x1[y]) Term->x1[y] = x;
+ if (x > Term->x2[y]) Term->x2[y] = x;
+}
+
+
+/*
+ * Mentally draw a string of attr/chars at a given location
+ *
+ * Assumes given location and values are valid.
+ *
+ * This function is designed to be fast, with no consistancy checking.
+ * It is used to update the map in the game.
+ */
+void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc, byte *ea, char *ec)
+{
+ term_win *scrn = Term->scr;
+
+ int x1 = -1;
+ int x2 = -1;
+
+ byte *scr_aa = &scrn->a[y][x];
+ char *scr_cc = &scrn->c[y][x];
+
+ byte *scr_taa = &scrn->ta[y][x];
+ char *scr_tcc = &scrn->tc[y][x];
+
+ byte *scr_eaa = &scrn->ea[y][x];
+ char *scr_ecc = &scrn->ec[y][x];
+
+ while (n--)
+ {
+
+ /* Hack -- Ignore non-changes */
+ if ((*scr_aa == *a) && (*scr_cc == *c) &&
+ (*scr_taa == *ta) && (*scr_tcc == *tc) &&
+ (*scr_eaa == *ea) && (*scr_ecc == *ec))
+ {
+ x++;
+ a++;
+ c++;
+ ta++;
+ tc++;
+ ea++;
+ ec++;
+ scr_aa++;
+ scr_cc++;
+ scr_taa++;
+ scr_tcc++;
+ scr_eaa++;
+ scr_ecc++;
+ continue;
+ }
+
+ /* Save the "literal" information */
+ *scr_taa++ = *ta++;
+ *scr_tcc++ = *tc++;
+
+ /* Save the "literal" information */
+ *scr_eaa++ = *ea++;
+ *scr_ecc++ = *ec++;
+
+ /* Save the "literal" information */
+ *scr_aa++ = *a++;
+ *scr_cc++ = *c++;
+
+ /* Track minimum changed column */
+ if (x1 < 0) x1 = x;
+
+ /* Track maximum changed column */
+ x2 = x;
+
+ x++;
+ }
+
+ /* Expand the "change area" as needed */
+ if (x1 >= 0)
+ {
+ /* Check for new min/max row info */
+ if (y < Term->y1) Term->y1 = y;
+ if (y > Term->y2) Term->y2 = y;
+
+ /* Check for new min/max col info in this row */
+ if (x1 < Term->x1[y]) Term->x1[y] = x1;
+ if (x2 > Term->x2[y]) Term->x2[y] = x2;
+ }
+}
+
+
+
+/*
+ * Mentally draw some attr/chars at a given location
+ *
+ * Assumes that (x,y) is a valid location, that the first "n" characters
+ * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
+ * a valid location, so the first "n" characters of "s" can all be added
+ * starting at (x,y) without causing any illegal operations.
+ */
+void Term_queue_chars(int x, int y, int n, byte a, cptr s)
+{
+ int x1 = -1, x2 = -1;
+
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+ byte *scr_taa = Term->scr->ta[y];
+ char *scr_tcc = Term->scr->tc[y];
+
+ byte *scr_eaa = Term->scr->ea[y];
+ char *scr_ecc = Term->scr->ec[y];
+
+ /* Queue the attr/chars */
+ for ( ; n; x++, s++, n--)
+ {
+ int oa = scr_aa[x];
+ int oc = scr_cc[x];
+
+ int ota = scr_taa[x];
+ int otc = scr_tcc[x];
+
+ int oea = scr_eaa[x];
+ int oec = scr_ecc[x];
+
+ /* Hack -- Ignore non-changes */
+ if ((oa == a) && (oc == *s) &&
+ (ota == 0) && (otc == 0) &&
+ (oea == 0) && (oec == 0)) continue;
+
+
+ /* Save the "literal" information */
+ scr_aa[x] = a;
+ scr_cc[x] = *s;
+
+ scr_taa[x] = 0;
+ scr_tcc[x] = 0;
+
+ scr_taa[x] = 0;
+ scr_tcc[x] = 0;
+
+ /* Note the "range" of window updates */
+ if (x1 < 0) x1 = x;
+ x2 = x;
+ }
+
+ /* Expand the "change area" as needed */
+ if (x1 >= 0)
+ {
+ /* Check for new min/max row info */
+ if (y < Term->y1) Term->y1 = y;
+ if (y > Term->y2) Term->y2 = y;
+
+ /* Check for new min/max col info in this row */
+ if (x1 < Term->x1[y]) Term->x1[y] = x1;
+ if (x2 > Term->x2[y]) Term->x2[y] = x2;
+ }
+}
+
+
+
+/*** Refresh routines ***/
+
+
+/*
+ * Flush a row of the current window (see "Term_fresh")
+ *
+ * Display text using "Term_pict()"
+ */
+static void Term_fresh_row_pict(int y, int x1, int x2)
+{
+ int x;
+
+ byte *old_aa = Term->old->a[y];
+ char *old_cc = Term->old->c[y];
+
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+ byte *old_taa = Term->old->ta[y];
+ char *old_tcc = Term->old->tc[y];
+
+ byte *scr_taa = Term->scr->ta[y];
+ char *scr_tcc = Term->scr->tc[y];
+
+ byte ota;
+ char otc;
+
+ byte nta;
+ char ntc;
+
+ byte *old_eaa = Term->old->ea[y];
+ char *old_ecc = Term->old->ec[y];
+
+ byte *scr_eaa = Term->scr->ea[y];
+ char *scr_ecc = Term->scr->ec[y];
+
+ byte oea;
+ char oec;
+
+ byte nea;
+ char nec;
+
+
+
+ /* Pending length */
+ int fn = 0;
+
+ /* Pending start */
+ int fx = 0;
+
+ byte oa;
+ char oc;
+
+ byte na;
+ char nc;
+
+ /* Scan "modified" columns */
+ for (x = x1; x <= x2; x++)
+ {
+ /* See what is currently here */
+ oa = old_aa[x];
+ oc = old_cc[x];
+
+ /* See what is desired there */
+ na = scr_aa[x];
+ nc = scr_cc[x];
+
+ ota = old_taa[x];
+ otc = old_tcc[x];
+
+ nta = scr_taa[x];
+ ntc = scr_tcc[x];
+
+ oea = old_eaa[x];
+ oec = old_ecc[x];
+
+ nea = scr_eaa[x];
+ nec = scr_ecc[x];
+
+ /* Handle unchanged grids */
+ if ((na == oa) && (nc == oc) &&
+ (nta == ota) && (ntc == otc) &&
+ (nea == oea) && (nec == oec))
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending attr/char pairs */
+ (void)((*Term->pict_hook)(fx, y, fn,
+ &scr_aa[fx], &scr_cc[fx],
+ &scr_taa[fx], &scr_tcc[fx],
+ &scr_eaa[fx], &scr_ecc[fx]));
+
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Skip */
+ continue;
+ }
+ /* Save new contents */
+ old_aa[x] = na;
+ old_cc[x] = nc;
+
+ old_taa[x] = nta;
+ old_tcc[x] = ntc;
+
+ old_eaa[x] = nea;
+ old_ecc[x] = nec;
+
+ /* Restart and Advance */
+ if (fn++ == 0) fx = x;
+ }
+
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending attr/char pairs */
+ (void)((*Term->pict_hook)(fx, y, fn,
+ &scr_aa[fx], &scr_cc[fx],
+ &scr_taa[fx], &scr_tcc[fx],
+ &scr_eaa[fx], &scr_ecc[fx]));
+ }
+}
+
+
+
+/*
+ * Flush a row of the current window (see "Term_fresh")
+ *
+ * Display text using "Term_text()" and "Term_wipe()",
+ * but use "Term_pict()" for high-bit attr/char pairs
+ */
+static void Term_fresh_row_both(int y, int x1, int x2)
+{
+ int x;
+
+ byte *old_aa = Term->old->a[y];
+ char *old_cc = Term->old->c[y];
+
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+ byte *old_taa = Term->old->ta[y];
+ char *old_tcc = Term->old->tc[y];
+ byte *scr_taa = Term->scr->ta[y];
+ char *scr_tcc = Term->scr->tc[y];
+
+ byte ota;
+ char otc;
+ byte nta;
+ char ntc;
+
+ byte *old_eaa = Term->old->ea[y];
+ char *old_ecc = Term->old->ec[y];
+ byte *scr_eaa = Term->scr->ea[y];
+ char *scr_ecc = Term->scr->ec[y];
+
+ byte oea;
+ char oec;
+ byte nea;
+ char nec;
+
+ /* The "always_text" flag */
+ int always_text = Term->always_text;
+
+ /* Pending length */
+ int fn = 0;
+
+ /* Pending start */
+ int fx = 0;
+
+ /* Pending attr */
+ byte fa = Term->attr_blank;
+
+ byte oa;
+ char oc;
+
+ byte na;
+ char nc;
+
+ /* Scan "modified" columns */
+ for (x = x1; x <= x2; x++)
+ {
+ /* See what is currently here */
+ oa = old_aa[x];
+ oc = old_cc[x];
+
+ /* See what is desired there */
+ na = scr_aa[x];
+ nc = scr_cc[x];
+
+ ota = old_taa[x];
+ otc = old_tcc[x];
+
+ nta = scr_taa[x];
+ ntc = scr_tcc[x];
+
+ oea = old_eaa[x];
+ oec = old_ecc[x];
+
+ nea = scr_eaa[x];
+ nec = scr_ecc[x];
+
+ /* Handle unchanged grids */
+ if ((na == oa) && (nc == oc) &&
+ (nta == ota) && (ntc == otc) &&
+ (nea == oea) && (nec == oec))
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Skip */
+ continue;
+ }
+
+ /* Save new contents */
+ old_aa[x] = na;
+ old_cc[x] = nc;
+
+ old_taa[x] = nta;
+ old_tcc[x] = ntc;
+
+ old_eaa[x] = nea;
+ old_ecc[x] = nec;
+
+ /* 2nd byte of bigtile */
+ if (na == 255) continue;
+
+ /* Handle high-bit attr/chars */
+ if (na & 0x80)
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Hack -- Draw the special attr/char pair */
+ (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc, &nea, &nec));
+
+ /* Skip */
+ continue;
+ }
+
+ /* Notice new color */
+ if (fa != na)
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw the pending chars */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+ /* Hack -- Erase "leading" spaces */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Save the new color */
+ fa = na;
+ }
+
+ /* Restart and Advance */
+ if (fn++ == 0) fx = x;
+ }
+
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ }
+}
+
+
+/*
+ * Flush a row of the current window (see "Term_fresh")
+ *
+ * Display text using "Term_text()" and "Term_wipe()"
+ */
+static void Term_fresh_row_text(int y, int x1, int x2)
+{
+ int x;
+
+ byte *old_aa = Term->old->a[y];
+ char *old_cc = Term->old->c[y];
+
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+ /* The "always_text" flag */
+ int always_text = Term->always_text;
+
+ /* Pending length */
+ int fn = 0;
+
+ /* Pending start */
+ int fx = 0;
+
+ /* Pending attr */
+ byte fa = Term->attr_blank;
+
+ byte oa;
+ char oc;
+
+ byte na;
+ char nc;
+
+ /* Scan "modified" columns */
+ for (x = x1; x <= x2; x++)
+ {
+ /* See what is currently here */
+ oa = old_aa[x];
+ oc = old_cc[x];
+
+ /* See what is desired there */
+ na = scr_aa[x];
+ nc = scr_cc[x];
+
+ /* Handle unchanged grids */
+ if ((na == oa) && (nc == oc))
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Skip */
+ continue;
+ }
+
+ /* Save new contents */
+ old_aa[x] = na;
+ old_cc[x] = nc;
+
+ /* Notice new color */
+ if (fa != na)
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw the pending chars */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+
+ /* Hack -- Erase "leading" spaces */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Save the new color */
+ fa = na;
+ }
+
+ /* Restart and Advance */
+ if (fn++ == 0) fx = x;
+ }
+
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ }
+}
+
+
+
+
+
+/*
+ * Actually perform all requested changes to the window
+ *
+ * If absolutely nothing has changed, not even temporarily, or if the
+ * current "Term" is not mapped, then this function will return 1 and
+ * do absolutely nothing.
+ *
+ * Note that when "soft_cursor" is true, we erase the cursor (if needed)
+ * whenever anything has changed, and redraw it (if needed) after all of
+ * the screen updates are complete. This will induce a small amount of
+ * "cursor flicker" but only when the screen has been updated. If the
+ * screen is updated and then restored, you may still get this flicker.
+ *
+ * When "soft_cursor" is not true, we make the cursor invisible before
+ * doing anything else if it is supposed to be invisible by the time we
+ * are done, and we make it visible after moving it to its final location
+ * after all of the screen updates are complete.
+ *
+ * Note that "Term_xtra(TERM_XTRA_CLEAR,0)" must erase the entire screen,
+ * including the cursor, if needed, and may place the cursor anywhere.
+ *
+ * Note that "Term_xtra(TERM_XTRA_FROSH,y)" will be always be called
+ * after any row "y" has been "flushed", unless the "Term->never_frosh"
+ * flag is set, and "Term_xtra(TERM_XTRA_FRESH,0)" will be called after
+ * all of the rows have been "flushed".
+ *
+ * Note the use of three different functions to handle the actual flush,
+ * based on the settings of the "Term->always_pict" and "Term->higher_pict"
+ * flags (see below).
+ *
+ * The three helper functions (above) work by collecting similar adjacent
+ * grids into stripes, and then sending each stripe to "Term->pict_hook",
+ * "Term->text_hook", or "Term->wipe_hook", based on the settings of the
+ * "Term->always_pict" and "Term->higher_pict" flags, which select which
+ * of the helper functions to call to flush each row.
+ *
+ * The helper functions currently "skip" any grids which already contain
+ * the desired contents. This may or may not be the best method, especially
+ * when the desired content fits nicely into the current stripe. For example,
+ * it might be better to go ahead and queue them while allowed, but keep a
+ * count of the "trailing skipables", then, when time to flush, or when a
+ * "non skippable" is found, force a flush if there are too many skippables.
+ *
+ * Perhaps an "initialization" stage, where the "text" (and "attr")
+ * buffers are "filled" with information, converting "blanks" into
+ * a convenient representation, and marking "skips" with "zero chars",
+ * and then some "processing" is done to determine which chars to skip.
+ *
+ * Currently, the helper functions are optimal for systems which prefer
+ * to "print a char + move a char + print a char" to "print three chars",
+ * and for applications that do a lot of "detailed" color printing.
+ *
+ * In the two "queue" functions, total "non-changes" are "pre-skipped".
+ * The helper functions must also handle situations in which the contents
+ * of a grid are changed, but then changed back to the original value,
+ * and situations in which two grids in the same row are changed, but
+ * the grids between them are unchanged.
+ *
+ * If the "Term->always_pict" flag is set, then "Term_fresh_row_pict()"
+ * will be used instead of "Term_fresh_row_text()". This allows all the
+ * modified grids to be collected into stripes of attr/char pairs, which
+ * are then sent to the "Term->pict_hook" hook, which can draw these pairs
+ * in whatever way it would like.
+ *
+ * If the "Term->higher_pict" flag is set, then "Term_fresh_row_both()"
+ * will be used instead of "Term_fresh_row_text()". This allows all the
+ * "special" attr/char pairs (in which both the attr and char have the
+ * high-bit set) to be sent (one pair at a time) to the "Term->pict_hook"
+ * hook, which can draw these pairs in whatever way it would like.
+ *
+ * Normally, the "Term_wipe()" function is used only to display "blanks"
+ * that were induced by "Term_clear()" or "Term_erase()", and then only
+ * if the "attr_blank" and "char_blank" fields have not been redefined
+ * to use "white space" instead of the default "black space". Actually,
+ * the "Term_wipe()" function is used to display all "black" text, such
+ * as the default "spaces" created by "Term_clear()" and "Term_erase()".
+ *
+ * Note that the "Term->always_text" flag will disable the use of the
+ * "Term_wipe()" function hook entirely, and force all text, even text
+ * drawn in the color "black", to be explicitly drawn. This is useful
+ * for machines which implement "Term_wipe()" by just drawing spaces.
+ *
+ * Note that the "Term->always_pict" flag will disable the use of the
+ * "Term_wipe()" function entirely, and force everything, even text
+ * drawn in the attr "black", to be explicitly drawn.
+ *
+ * Note that if no "black" text is ever drawn, and if "attr_blank" is
+ * not "zero", then the "Term_wipe" hook will never be used, even if
+ * the "Term->always_text" flag is not set.
+ *
+ * This function does nothing unless the "Term" is "mapped", which allows
+ * certain systems to optimize the handling of "closed" windows.
+ *
+ * On systems with a "soft" cursor, we must explicitly erase the cursor
+ * before flushing the output, if needed, to prevent a "jumpy" refresh.
+ * The actual method for this is horrible, but there is very little that
+ * we can do to simplify it efficiently. XXX XXX XXX
+ *
+ * On systems with a "hard" cursor, we will "hide" the cursor before
+ * flushing the output, if needed, to avoid a "flickery" refresh. It
+ * would be nice to *always* hide the cursor during the refresh, but
+ * this might be expensive (and/or ugly) on some machines.
+ *
+ * The "Term->icky_corner" flag is used to avoid calling "Term_wipe()"
+ * or "Term_pict()" or "Term_text()" on the bottom right corner of the
+ * window, which might induce "scrolling" or other nasty stuff on old
+ * dumb terminals. This flag is handled very efficiently. We assume
+ * that the "Term_curs()" call will prevent placing the cursor in the
+ * corner, if needed, though I doubt such placement is ever a problem.
+ * Currently, the use of "Term->icky_corner" and "Term->soft_cursor"
+ * together may result in undefined behavior.
+ */
+errr Term_fresh(void)
+{
+ int x, y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ int y1 = Term->y1;
+ int y2 = Term->y2;
+
+ term_win *old = Term->old;
+ term_win *scr = Term->scr;
+
+
+ /* Do nothing unless "mapped" */
+ if (!Term->mapped_flag) return (1);
+
+
+ /* Trivial Refresh */
+ if ((y1 > y2) &&
+ (scr->cu == old->cu) &&
+ (scr->cv == old->cv) &&
+ (scr->cx == old->cx) &&
+ (scr->cy == old->cy) &&
+ !(Term->total_erase))
+ {
+ /* Nothing */
+ return (1);
+ }
+
+
+ /* Paranoia -- use "fake" hooks to prevent core dumps */
+ if (!Term->curs_hook) Term->curs_hook = Term_curs_hack;
+ if (!Term->wipe_hook) Term->wipe_hook = Term_wipe_hack;
+ if (!Term->text_hook) Term->text_hook = Term_text_hack;
+ if (!Term->pict_hook) Term->pict_hook = Term_pict_hack;
+
+
+ /* Handle "total erase" */
+ if (Term->total_erase)
+ {
+ byte na = Term->attr_blank;
+ char nc = Term->char_blank;
+
+ if ((do_movies == 1) && IN_MAINWINDOW)
+ {
+ if (!cmovie_get_msecond())
+ {
+ fprintf(movfile, "S:%ld:\n", cmov_delta_time_msec);
+ }
+ fprintf(movfile, "C:\n");
+ last_paused = 0;
+ }
+
+ /* Physically erase the entire window */
+ Term_xtra(TERM_XTRA_CLEAR, 0);
+
+ /* Hack -- clear all "cursor" data */
+ old->cv = old->cu = old->cx = old->cy = 0;
+
+ /* Wipe each row */
+ for (y = 0; y < h; y++)
+ {
+ byte *aa = old->a[y];
+ char *cc = old->c[y];
+
+ byte *taa = old->ta[y];
+ char *tcc = old->tc[y];
+
+ byte *eaa = old->ea[y];
+ char *ecc = old->ec[y];
+
+
+ /* Wipe each column */
+ for (x = 0; x < w; x++)
+ {
+ /* Wipe each grid */
+ *aa++ = na;
+ *cc++ = nc;
+
+ *taa++ = na;
+ *tcc++ = nc;
+
+ *eaa++ = na;
+ *ecc++ = nc;
+ }
+ }
+
+ /* Redraw every row */
+ Term->y1 = y1 = 0;
+ Term->y2 = y2 = h - 1;
+
+ /* Redraw every column */
+ for (y = 0; y < h; y++)
+ {
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Forget "total erase" */
+ Term->total_erase = FALSE;
+ }
+
+
+ /* Cursor update -- Erase old Cursor */
+ if (Term->soft_cursor)
+ {
+ /* Cursor was visible */
+ if (!old->cu && old->cv)
+ {
+ int tx = old->cx;
+ int ty = old->cy;
+
+ byte *old_aa = old->a[ty];
+ char *old_cc = old->c[ty];
+
+ byte oa = old_aa[tx];
+ char oc = old_cc[tx];
+
+ byte *old_taa = old->ta[ty];
+ char *old_tcc = old->tc[ty];
+
+ byte ota = old_taa[tx];
+ char otc = old_tcc[tx];
+
+ byte *old_eaa = old->ea[ty];
+ char *old_ecc = old->ec[ty];
+
+ byte oea = old_eaa[tx];
+ char oec = old_ecc[tx];
+
+ /* Hack -- use "Term_pict()" always */
+ if (Term->always_pict)
+ {
+ (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc, &ota, &otc, &oea, &oec));
+ }
+
+ /* Hack -- use "Term_pict()" sometimes */
+ else if (Term->higher_pict && (oa & 0x80))
+ {
+ (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc, &ota, &otc, &oea, &oec));
+ }
+
+ /* Hack -- restore the actual character */
+ else if (oa || Term->always_text)
+ {
+ (void)((*Term->text_hook)(tx, ty, 1, oa, &oc));
+ }
+
+ /* Hack -- erase the grid */
+ else
+ {
+ (void)((*Term->wipe_hook)(tx, ty, 1));
+ }
+ }
+ }
+
+ /* Cursor Update -- Erase old Cursor */
+ else
+ {
+ /* Cursor will be invisible */
+ if (scr->cu || !scr->cv)
+ {
+ /* Make the cursor invisible */
+ Term_xtra(TERM_XTRA_SHAPE, 0);
+ }
+ }
+
+
+ /* Something to update */
+ if (y1 <= y2)
+ {
+ /* Handle "icky corner" */
+ if (Term->icky_corner)
+ {
+ /* Avoid the corner */
+ if (y2 >= h - 1)
+ {
+ /* Avoid the corner */
+ if (Term->x2[h - 1] > w - 2)
+ {
+ /* Avoid the corner */
+ Term->x2[h - 1] = w - 2;
+ }
+ }
+ }
+
+
+ /* Scan the "modified" rows */
+ for (y = y1; y <= y2; ++y)
+ {
+ int x1 = Term->x1[y];
+ int x2 = Term->x2[y];
+
+ /* Flush each "modified" row */
+ if (x1 <= x2)
+ {
+ if ((do_movies == 1) && IN_MAINWINDOW)
+ {
+ /* Most magic happens here */
+ cmovie_record_line(y);
+ last_paused = 0;
+ }
+
+ /* Always use "Term_pict()" */
+ if (Term->always_pict)
+ {
+ /* Flush the row */
+ Term_fresh_row_pict(y, x1, x2);
+ }
+
+ /* Sometimes use "Term_pict()" */
+ else if (Term->higher_pict)
+ {
+ /* Flush the row */
+ Term_fresh_row_both(y, x1, x2);
+ }
+
+ /* Never use "Term_pict()" */
+ else
+ {
+ /* Flush the row */
+ Term_fresh_row_text(y, x1, x2);
+ }
+
+ /* This row is all done */
+ Term->x1[y] = w;
+ Term->x2[y] = 0;
+
+ /* Hack -- Flush that row (if allowed) */
+ if (!Term->never_frosh) Term_xtra(TERM_XTRA_FROSH, y);
+ }
+ }
+
+ /* No rows are invalid */
+ Term->y1 = h;
+ Term->y2 = 0;
+ }
+
+
+ /* Cursor update -- Show new Cursor */
+ if (Term->soft_cursor)
+ {
+ /* Draw the cursor */
+ if (!scr->cu && scr->cv)
+ {
+ /* Call the cursor display routine */
+ (void)((*Term->curs_hook)(scr->cx, scr->cy));
+ }
+ }
+
+ /* Cursor Update -- Show new Cursor */
+ else
+ {
+ /* The cursor is useless, hide it */
+ if (scr->cu)
+ {
+ /* Paranoia -- Put the cursor NEAR where it belongs */
+ (void)((*Term->curs_hook)(w - 1, scr->cy));
+
+ /* Make the cursor invisible */
+ /* Term_xtra(TERM_XTRA_SHAPE, 0); */
+ }
+
+ /* The cursor is invisible, hide it */
+ else if (!scr->cv)
+ {
+ /* Paranoia -- Put the cursor where it belongs */
+ (void)((*Term->curs_hook)(scr->cx, scr->cy));
+
+ /* Make the cursor invisible */
+ /* Term_xtra(TERM_XTRA_SHAPE, 0); */
+ }
+
+ /* The cursor is visible, display it correctly */
+ else
+ {
+ /* Put the cursor where it belongs */
+ (void)((*Term->curs_hook)(scr->cx, scr->cy));
+
+ /* Make the cursor visible */
+ Term_xtra(TERM_XTRA_SHAPE, 1);
+ }
+ }
+
+
+ /* Save the "cursor state" */
+ old->cu = scr->cu;
+ old->cv = scr->cv;
+ old->cx = scr->cx;
+ old->cy = scr->cy;
+
+
+ /* Actually flush the output */
+ Term_xtra(TERM_XTRA_FRESH, 0);
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Output routines ***/
+
+
+/*
+ * Set the cursor visibility
+ */
+errr Term_set_cursor(int v)
+{
+ /* Already done */
+ if (Term->scr->cv == v) return (1);
+
+ /* Change */
+ Term->scr->cv = v;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Place the cursor at a given location
+ *
+ * Note -- "illegal" requests do not move the cursor.
+ */
+errr Term_gotoxy(int x, int y)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Verify */
+ if ((x < 0) || (x >= w)) return ( -1);
+ if ((y < 0) || (y >= h)) return ( -1);
+
+ /* Remember the cursor */
+ Term->scr->cx = x;
+ Term->scr->cy = y;
+
+ /* The cursor is not useless */
+ Term->scr->cu = 0;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * At a given location, place an attr/char
+ * Do not change the cursor position
+ * No visual changes until "Term_fresh()".
+ */
+errr Term_draw(int x, int y, byte a, char c)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Verify location */
+ if ((x < 0) || (x >= w)) return ( -1);
+ if ((y < 0) || (y >= h)) return ( -1);
+
+ /* Paranoia -- illegal char */
+ if (!c) return ( -2);
+
+ /* Queue it for later */
+ Term_queue_char(x, y, a, c, 0, 0, 0, 0);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Using the given attr, add the given char at the cursor.
+ *
+ * We return "-2" if the character is "illegal". XXX XXX
+ *
+ * We return "-1" if the cursor is currently unusable.
+ *
+ * We queue the given attr/char for display at the current
+ * cursor location, and advance the cursor to the right,
+ * marking it as unuable and returning "1" if it leaves
+ * the screen, and otherwise returning "0".
+ *
+ * So when this function, or the following one, return a
+ * positive value, future calls to either function will
+ * return negative ones.
+ */
+errr Term_addch(byte a, char c)
+{
+ int w = Term->wid;
+
+ /* Handle "unusable" cursor */
+ if (Term->scr->cu) return ( -1);
+
+ /* Paranoia -- no illegal chars */
+ if (!c) return ( -2);
+
+ /* Queue the given character for display */
+ Term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0, 0, 0);
+
+ /* Advance the cursor */
+ Term->scr->cx++;
+
+ /* Success */
+ if (Term->scr->cx < w) return (0);
+
+ /* Note "Useless" cursor */
+ Term->scr->cu = 1;
+
+ /* Note "Useless" cursor */
+ return (1);
+}
+
+
+/*
+ * At the current location, using an attr, add a string
+ *
+ * We also take a length "n", using negative values to imply
+ * the largest possible value, and then we use the minimum of
+ * this length and the "actual" length of the string as the
+ * actual number of characters to attempt to display, never
+ * displaying more characters than will actually fit, since
+ * we do NOT attempt to "wrap" the cursor at the screen edge.
+ *
+ * We return "-1" if the cursor is currently unusable.
+ * We return "N" if we were "only" able to write "N" chars,
+ * even if all of the given characters fit on the screen,
+ * and mark the cursor as unusable for future attempts.
+ *
+ * So when this function, or the preceding one, return a
+ * positive value, future calls to either function will
+ * return negative ones.
+ */
+errr Term_addstr(int n, byte a, cptr s)
+{
+ int k;
+
+ int w = Term->wid;
+
+ errr res = 0;
+
+ /* Handle "unusable" cursor */
+ if (Term->scr->cu) return ( -1);
+
+ /* Obtain maximal length */
+ k = (n < 0) ? (w + 1) : n;
+
+ /* Obtain the usable string length */
+ for (n = 0; (n < k) && s[n]; n++) /* loop */;
+
+ /* React to reaching the edge of the screen */
+ if (Term->scr->cx + n >= w) res = n = w - Term->scr->cx;
+
+ /* Queue the first "n" characters for display */
+ Term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
+
+ /* Advance the cursor */
+ Term->scr->cx += n;
+
+ /* Hack -- Notice "Useless" cursor */
+ if (res) Term->scr->cu = 1;
+
+ /* Success (usually) */
+ return (res);
+}
+
+
+/*
+ * Move to a location and, using an attr, add a char
+ */
+errr Term_putch(int x, int y, byte a, char c)
+{
+ errr res;
+
+ /* Move first */
+ if ((res = Term_gotoxy(x, y)) != 0) return (res);
+
+ /* Then add the char */
+ if ((res = Term_addch(a, c)) != 0) return (res);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Move to a location and, using an attr, add a string
+ */
+errr Term_putstr(int x, int y, int n, byte a, cptr s)
+{
+ errr res;
+
+ /* Move first */
+ if ((res = Term_gotoxy(x, y)) != 0) return (res);
+
+ /* Then add the string */
+ if ((res = Term_addstr(n, a, s)) != 0) return (res);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Place cursor at (x,y), and clear the next "n" chars
+ */
+errr Term_erase(int x, int y, int n)
+{
+ int i;
+
+ int w = Term->wid;
+ /* int h = Term->hgt; */
+
+ int x1 = -1;
+ int x2 = -1;
+
+ int na = Term->attr_blank;
+ int nc = Term->char_blank;
+
+ byte *scr_aa;
+ char *scr_cc;
+
+ byte *scr_taa;
+ char *scr_tcc;
+
+ byte *scr_eaa;
+ char *scr_ecc;
+
+ /* Place cursor */
+ if (Term_gotoxy(x, y)) return ( -1);
+
+ /* Force legal size */
+ if (x + n > w) n = w - x;
+
+ /* Fast access */
+ scr_aa = Term->scr->a[y];
+ scr_cc = Term->scr->c[y];
+
+ scr_taa = Term->scr->ta[y];
+ scr_tcc = Term->scr->tc[y];
+
+ scr_eaa = Term->scr->ea[y];
+ scr_ecc = Term->scr->ec[y];
+
+ if (n > 0 && (byte)scr_cc[x] == 255 && scr_aa[x] == 255)
+ {
+ x--;
+ n++;
+ }
+
+ /* Scan every column */
+ for (i = 0; i < n; i++, x++)
+ {
+ int oa = scr_aa[x];
+ int oc = scr_cc[x];
+
+ /* Hack -- Ignore "non-changes" */
+ if ((oa == na) && (oc == nc)) continue;
+
+ /* Save the "literal" information */
+ scr_aa[x] = na;
+ scr_cc[x] = nc;
+
+ scr_taa[x] = 0;
+ scr_tcc[x] = 0;
+
+ scr_eaa[x] = 0;
+ scr_ecc[x] = 0;
+
+ /* Track minimum changed column */
+ if (x1 < 0) x1 = x;
+
+ /* Track maximum changed column */
+ x2 = x;
+ }
+
+ /* Expand the "change area" as needed */
+ if (x1 >= 0)
+ {
+ /* Check for new min/max row info */
+ if (y < Term->y1) Term->y1 = y;
+ if (y > Term->y2) Term->y2 = y;
+
+ /* Check for new min/max col info in this row */
+ if (x1 < Term->x1[y]) Term->x1[y] = x1;
+ if (x2 > Term->x2[y]) Term->x2[y] = x2;
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Clear the entire window, and move to the top left corner
+ *
+ * Note the use of the special "total_erase" code
+ */
+errr Term_clear(void)
+{
+ int x, y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ byte na = Term->attr_blank;
+ char nc = Term->char_blank;
+
+ /* Cursor usable */
+ Term->scr->cu = 0;
+
+ /* Cursor to the top left */
+ Term->scr->cx = Term->scr->cy = 0;
+
+ /* Wipe each row */
+ for (y = 0; y < h; y++)
+ {
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+ byte *scr_taa = Term->scr->ta[y];
+ char *scr_tcc = Term->scr->tc[y];
+
+ byte *scr_eaa = Term->scr->ea[y];
+ char *scr_ecc = Term->scr->ec[y];
+
+ /* Wipe each column */
+ for (x = 0; x < w; x++)
+ {
+ scr_aa[x] = na;
+ scr_cc[x] = nc;
+
+ scr_taa[x] = 0;
+ scr_tcc[x] = 0;
+
+ scr_eaa[x] = 0;
+ scr_ecc[x] = 0;
+ }
+
+ /* This row has changed */
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Every row has changed */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Force "total erase" */
+ Term->total_erase = TRUE;
+
+ /* Success */
+ return (0);
+}
+
+
+
+
+
+/*
+ * Redraw (and refresh) the whole window.
+ */
+errr Term_redraw(void)
+{
+ /* Pat */
+ if ((do_movies == 1) && IN_MAINWINDOW)
+ {
+ if (!cmovie_get_msecond())
+ {
+ fprintf(movfile, "S:%ld:\n", cmov_delta_time_msec);
+ }
+ last_paused = 1;
+ }
+ /* Endpat */
+
+ /* Force "total erase" */
+ Term->total_erase = TRUE;
+
+ /* Hack -- Refresh */
+ Term_fresh();
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Redraw part of a window.
+ */
+errr Term_redraw_section(int x1, int y1, int x2, int y2)
+{
+ int i, j;
+
+ char *c_ptr;
+
+ /* Bounds checking */
+ if (y2 >= Term->hgt) y2 = Term->hgt - 1;
+ if (x2 >= Term->wid) x2 = Term->wid - 1;
+ if (y1 < 0) y1 = 0;
+ if (x1 < 0) x1 = 0;
+
+ /* Set y limits */
+ Term->y1 = y1;
+ Term->y2 = y2;
+
+ /* Set the x limits */
+ for (i = Term->y1; i <= Term->y2; i++)
+ {
+ Term->x1[i] = x1;
+ Term->x2[i] = x2;
+
+ c_ptr = Term->old->c[i];
+
+ /* Clear the section so it is redrawn */
+ for (j = x1; j <= x2; j++)
+ {
+ /* Hack - set the old character to "none" */
+ c_ptr[j] = 0;
+ }
+ }
+
+ /* Hack -- Refresh */
+ Term_fresh();
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Access routines ***/
+
+
+/*
+ * Extract the cursor visibility
+ */
+errr Term_get_cursor(int *v)
+{
+ /* Extract visibility */
+ (*v) = Term->scr->cv;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Extract the current window size
+ */
+errr Term_get_size(int *w, int *h)
+{
+ /* Access the cursor */
+ (*w) = Term->wid;
+ (*h) = Term->hgt;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Extract the current cursor location
+ */
+errr Term_locate(int *x, int *y)
+{
+ /* Access the cursor */
+ (*x) = Term->scr->cx;
+ (*y) = Term->scr->cy;
+
+ /* Warn about "useless" cursor */
+ if (Term->scr->cu) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * At a given location, determine the "current" attr and char
+ * Note that this refers to what will be on the window after the
+ * next call to "Term_fresh()". It may or may not already be there.
+ */
+errr Term_what(int x, int y, byte *a, char *c)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Verify location */
+ if ((x < 0) || (x >= w)) return ( -1);
+ if ((y < 0) || (y >= h)) return ( -1);
+
+ /* Direct access */
+ (*a) = Term->scr->a[y][x];
+ (*c) = Term->scr->c[y][x];
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Input routines ***/
+
+
+/*
+ * Flush and forget the input
+ */
+errr Term_flush(void)
+{
+ /* Hack -- Flush all events */
+ Term_xtra(TERM_XTRA_FLUSH, 0);
+
+ /* Forget all keypresses */
+ Term->key_head = Term->key_tail = 0;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Add a keypress to the "queue"
+ */
+errr Term_keypress(int k)
+{
+ /* Hack -- Refuse to enqueue non-keys */
+ if (!k) return ( -1);
+
+ /* Store the char, advance the queue */
+ Term->key_queue[Term->key_head++] = k;
+
+ /* Circular queue, handle wrap */
+ if (Term->key_head == Term->key_size) Term->key_head = 0;
+
+ /* Success (unless overflow) */
+ if (Term->key_head != Term->key_tail) return (0);
+
+ /* Problem */
+ return (1);
+}
+
+
+/*
+ * Add a keypress to the FRONT of the "queue"
+ */
+errr Term_key_push(int k)
+{
+ /* Hack -- Refuse to enqueue non-keys */
+ if (!k) return ( -1);
+
+ /* Hack -- Overflow may induce circular queue */
+ if (Term->key_tail == 0) Term->key_tail = Term->key_size;
+
+ /* Back up, Store the char */
+ Term->key_queue[--Term->key_tail] = k;
+
+ /* Success (unless overflow) */
+ if (Term->key_head != Term->key_tail) return (0);
+
+ /* Problem */
+ return (1);
+}
+
+
+
+
+
+/*
+ * Check for a pending keypress on the key queue.
+ *
+ * Store the keypress, if any, in "ch", and return "0".
+ * Otherwise store "zero" in "ch", and return "1".
+ *
+ * Wait for a keypress if "wait" is true.
+ *
+ * Remove the keypress if "take" is true.
+ */
+errr Term_inkey(char *ch, bool_ wait, bool_ take)
+{
+ /* Assume no key */
+ (*ch) = '\0';
+
+ /* Hack -- get bored */
+ if (!Term->never_bored)
+ {
+ /* Process random events */
+ Term_xtra(TERM_XTRA_BORED, 0);
+ }
+
+ /* PatN */
+ if ((do_movies == 1) && (last_paused == 0) && (!cmovie_get_msecond()))
+ {
+ fprintf(movfile, "S:%ld:\n", cmov_delta_time_msec);
+ last_paused = 1;
+ }
+ /* PatNEnd */
+
+ /* Wait */
+ if (wait)
+ {
+ /* Process pending events while necessary */
+ while (Term->key_head == Term->key_tail)
+ {
+ /* Process events (wait for one) */
+ Term_xtra(TERM_XTRA_EVENT, TRUE);
+ }
+ }
+
+ /* Do not Wait */
+ else
+ {
+ /* Process pending events if necessary */
+ if (Term->key_head == Term->key_tail)
+ {
+ /* Process events (do not wait) */
+ Term_xtra(TERM_XTRA_EVENT, FALSE);
+ }
+ }
+
+ /* No keys are ready */
+ if (Term->key_head == Term->key_tail) return (1);
+
+ /* Extract the next keypress */
+ (*ch) = Term->key_queue[Term->key_tail];
+
+ /* If requested, advance the queue, wrap around if necessary */
+ if (take && (++Term->key_tail == Term->key_size)) Term->key_tail = 0;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Extra routines ***/
+
+
+/*
+ * Save the "requested" screen into the "memorized" screen
+ *
+ * Every "Term_save()" should match exactly one "Term_load()"
+ */
+errr Term_save(void)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Create */
+ if (!Term->mem)
+ {
+ /* Allocate window */
+ MAKE(Term->mem, term_win);
+
+ /* Initialize window */
+ term_win_init(Term->mem, w, h);
+ }
+
+ /* Grab */
+ term_win_copy(Term->mem, Term->scr, w, h);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Same as before but can save more than once
+ */
+term_win* Term_save_to(void)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+ term_win *save;
+
+ /* Allocate window */
+ MAKE(save, term_win);
+
+ /* Initialize window */
+ term_win_init(save, w, h);
+
+ /* Grab */
+ term_win_copy(save, Term->scr, w, h);
+
+ /* Success */
+ return (save);
+}
+
+/*
+ * Restore the "requested" contents (see above).
+ *
+ * Every "Term_save()" should match exactly one "Term_load()"
+ */
+errr Term_load(void)
+{
+ int y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Create */
+ if (!Term->mem)
+ {
+ /* Allocate window */
+ MAKE(Term->mem, term_win);
+
+ /* Initialize window */
+ term_win_init(Term->mem, w, h);
+ }
+
+ /* Load */
+ term_win_copy(Term->scr, Term->mem, w, h);
+
+ /* Assume change */
+ for (y = 0; y < h; y++)
+ {
+ /* Assume change */
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Assume change */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Same as previous but allow to save more than one
+ */
+errr Term_load_from(term_win *save, bool_ final)
+{
+ int y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Create */
+ if (!save)
+ {
+ return (1);
+ }
+
+ /* Load */
+ term_win_copy(Term->scr, save, w, h);
+
+ /* Assume change */
+ for (y = 0; y < h; y++)
+ {
+ /* Assume change */
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Assume change */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Free is requested */
+ if (final)
+ FREE(save, term_win);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Exchange the "requested" screen with the "tmp" screen
+ */
+errr Term_exchange(void)
+{
+ int y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ term_win *exchanger;
+
+
+ /* Create */
+ if (!Term->tmp)
+ {
+ /* Allocate window */
+ MAKE(Term->tmp, term_win);
+
+ /* Initialize window */
+ term_win_init(Term->tmp, w, h);
+ }
+
+ /* Swap */
+ exchanger = Term->scr;
+ Term->scr = Term->tmp;
+ Term->tmp = exchanger;
+
+ /* Assume change */
+ for (y = 0; y < h; y++)
+ {
+ /* Assume change */
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Assume change */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * React to a new physical window size.
+ */
+errr Term_resize(int w, int h)
+{
+ int i;
+
+ int wid, hgt;
+
+ byte *hold_x1;
+ byte *hold_x2;
+
+ term_win *hold_old;
+ term_win *hold_scr;
+ term_win *hold_mem;
+ term_win *hold_tmp;
+
+ /* Resizing is forbidden */
+ if (Term->fixed_shape) return ( -1);
+
+ /* Ignore illegal changes */
+ if ((w < 1) || (h < 1)) return ( -1);
+
+
+ /* Ignore non-changes */
+ if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile)) return (1);
+ use_bigtile = arg_bigtile;
+
+ /* Minimum dimensions */
+ wid = MIN(Term->wid, w);
+ hgt = MIN(Term->hgt, h);
+
+ /* Save scanners */
+ hold_x1 = Term->x1;
+ hold_x2 = Term->x2;
+
+ /* Save old window */
+ hold_old = Term->old;
+
+ /* Save old window */
+ hold_scr = Term->scr;
+
+ /* Save old window */
+ hold_mem = Term->mem;
+
+ /* Save old window */
+ hold_tmp = Term->tmp;
+
+ /* Create new scanners */
+ C_MAKE(Term->x1, h, byte);
+ C_MAKE(Term->x2, h, byte);
+
+ /* Create new window */
+ MAKE(Term->old, term_win);
+
+ /* Initialize new window */
+ term_win_init(Term->old, w, h);
+
+ /* Save the contents */
+ term_win_copy(Term->old, hold_old, wid, hgt);
+
+ /* Create new window */
+ MAKE(Term->scr, term_win);
+
+ /* Initialize new window */
+ term_win_init(Term->scr, w, h);
+
+ /* Save the contents */
+ term_win_copy(Term->scr, hold_scr, wid, hgt);
+
+ /* If needed */
+ if (hold_mem)
+ {
+ /* Create new window */
+ MAKE(Term->mem, term_win);
+
+ /* Initialize new window */
+ term_win_init(Term->mem, w, h);
+
+ /* Save the contents */
+ term_win_copy(Term->mem, hold_mem, wid, hgt);
+ }
+
+ /* If needed */
+ if (hold_tmp)
+ {
+ /* Create new window */
+ MAKE(Term->tmp, term_win);
+
+ /* Initialize new window */
+ term_win_init(Term->tmp, w, h);
+
+ /* Save the contents */
+ term_win_copy(Term->tmp, hold_tmp, wid, hgt);
+ }
+
+ /* Free some arrays */
+ C_KILL(hold_x1, Term->hgt, byte);
+ C_KILL(hold_x2, Term->hgt, byte);
+
+ /* Nuke */
+ term_win_nuke(hold_old, Term->wid, Term->hgt);
+
+ /* Kill */
+ KILL(hold_old, term_win);
+
+ /* Illegal cursor */
+ if (Term->old->cx >= w) Term->old->cu = 1;
+ if (Term->old->cy >= h) Term->old->cu = 1;
+
+ /* Nuke */
+ term_win_nuke(hold_scr, Term->wid, Term->hgt);
+
+ /* Kill */
+ KILL(hold_scr, term_win);
+
+ /* Illegal cursor */
+ if (Term->scr->cx >= w) Term->scr->cu = 1;
+ if (Term->scr->cy >= h) Term->scr->cu = 1;
+
+ /* If needed */
+ if (hold_mem)
+ {
+ /* Nuke */
+ term_win_nuke(hold_mem, Term->wid, Term->hgt);
+
+ /* Kill */
+ KILL(hold_mem, term_win);
+
+ /* Illegal cursor */
+ if (Term->mem->cx >= w) Term->mem->cu = 1;
+ if (Term->mem->cy >= h) Term->mem->cu = 1;
+ }
+
+ /* If needed */
+ if (hold_tmp)
+ {
+ /* Nuke */
+ term_win_nuke(hold_tmp, Term->wid, Term->hgt);
+
+ /* Kill */
+ KILL(hold_tmp, term_win);
+
+ /* Illegal cursor */
+ if (Term->tmp->cx >= w) Term->tmp->cu = 1;
+ if (Term->tmp->cy >= h) Term->tmp->cu = 1;
+ }
+
+ /* Save new size */
+ Term->wid = w;
+ Term->hgt = h;
+
+ /* Force "total erase" */
+ Term->total_erase = TRUE;
+
+ /* Assume change */
+ for (i = 0; i < h; i++)
+ {
+ /* Assume change */
+ Term->x1[i] = 0;
+ Term->x2[i] = w - 1;
+ }
+
+ /* Assume change */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Execute the "resize_hook" hook, if available */
+ if (Term->resize_hook)
+ {
+ Term->resize_hook();
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Activate a new Term (and deactivate the current Term)
+ *
+ * This function is extremely important, and also somewhat bizarre.
+ * It is the only function that should "modify" the value of "Term".
+ *
+ * To "create" a valid "term", one should do "term_init(t)", then
+ * set the various flags and hooks, and then do "Term_activate(t)".
+ */
+errr Term_activate(term *t)
+{
+ /* Hack -- already done */
+ if (Term == t) return (1);
+
+ /* Deactivate the old Term */
+ if (Term) Term_xtra(TERM_XTRA_LEVEL, 0);
+
+ /* Hack -- Call the special "init" hook */
+ if (t && !t->active_flag)
+ {
+ /* Call the "init" hook */
+ if (t->init_hook) (*t->init_hook)(t);
+
+ /* Remember */
+ t->active_flag = TRUE;
+
+ /* Assume mapped */
+ t->mapped_flag = TRUE;
+ }
+
+ /* Remember the Term */
+ Term = t;
+
+ /* Activate the new Term */
+ if (Term) Term_xtra(TERM_XTRA_LEVEL, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Nuke a term
+ */
+errr term_nuke(term *t)
+{
+ int w = t->wid;
+ int h = t->hgt;
+
+ /* Hack -- Call the special "nuke" hook */
+ if (t->active_flag)
+ {
+ /* Call the "nuke" hook */
+ if (t->nuke_hook) (*t->nuke_hook)(t);
+
+ /* Remember */
+ t->active_flag = FALSE;
+
+ /* Assume not mapped */
+ t->mapped_flag = FALSE;
+ }
+
+
+ /* Nuke "displayed" */
+ term_win_nuke(t->old, w, h);
+
+ /* Kill "displayed" */
+ KILL(t->old, term_win);
+
+ /* Nuke "requested" */
+ term_win_nuke(t->scr, w, h);
+
+ /* Kill "requested" */
+ KILL(t->scr, term_win);
+
+ /* If needed */
+ if (t->mem)
+ {
+ /* Nuke "memorized" */
+ term_win_nuke(t->mem, w, h);
+
+ /* Kill "memorized" */
+ KILL(t->mem, term_win);
+ }
+
+ /* If needed */
+ if (t->tmp)
+ {
+ /* Nuke "temporary" */
+ term_win_nuke(t->tmp, w, h);
+
+ /* Kill "temporary" */
+ KILL(t->tmp, term_win);
+ }
+
+ /* Free some arrays */
+ C_KILL(t->x1, h, byte);
+ C_KILL(t->x2, h, byte);
+
+ /* Free the input queue */
+ C_KILL(t->key_queue, t->key_size, char);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize a term, using a window of the given size.
+ * Also prepare the "input queue" for "k" keypresses
+ * By default, the cursor starts out "invisible"
+ * By default, we "erase" using "black spaces"
+ */
+errr term_init(term *t, int w, int h, int k)
+{
+ int y;
+
+ /* Wipe it */
+ (void)WIPE(t, term);
+
+
+ /* Prepare the input queue */
+ t->key_head = t->key_tail = 0;
+
+ /* Determine the input queue size */
+ t->key_size = k;
+
+ /* Allocate the input queue */
+ C_MAKE(t->key_queue, t->key_size, char);
+
+
+ /* Save the size */
+ t->wid = w;
+ t->hgt = h;
+
+ /* Allocate change arrays */
+ C_MAKE(t->x1, h, byte);
+ C_MAKE(t->x2, h, byte);
+
+
+ /* Allocate "displayed" */
+ MAKE(t->old, term_win);
+
+ /* Initialize "displayed" */
+ term_win_init(t->old, w, h);
+
+
+ /* Allocate "requested" */
+ MAKE(t->scr, term_win);
+
+ /* Initialize "requested" */
+ term_win_init(t->scr, w, h);
+
+
+ /* Assume change */
+ for (y = 0; y < h; y++)
+ {
+ /* Assume change */
+ t->x1[y] = 0;
+ t->x2[y] = w - 1;
+ }
+
+ /* Assume change */
+ t->y1 = 0;
+ t->y2 = h - 1;
+
+ /* Force "total erase" */
+ t->total_erase = TRUE;
+
+
+ /* Default "blank" */
+ t->attr_blank = 0;
+ t->char_blank = ' ';
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Determine if we are called in the same second as the last time?
+ * This *ASSUMES* that time_t is seconds past something. Is this portable?
+ */
+int cmovie_get_msecond(void)
+{
+#ifndef USE_PRECISE_CMOVIE
+ /* Not very precise, but portable */
+ time_t thisc;
+
+ thisc = time(NULL);
+
+ cmov_delta_time_msec = 300;
+
+ if (thisc == lastc)
+ {
+ return 1;
+ }
+ return 0;
+#else /* Very precise but needs main-foo.c to define TERM_XTRA_GET_DELAY */
+Term_xtra(TERM_XTRA_GET_DELAY, 0);
+
+ cmov_delta_time_msec = Term_xtra_long - cmov_last_time_msec;
+ cmov_last_time_msec = Term_xtra_long;
+ return 0;
+#endif
+}
+
+void cmovie_init_second()
+{
+#ifndef USE_PRECISE_CMOVIE
+ /* Not very precise, but portable */
+ cmov_last_time_msec = 0;
+#else /* Precise but need main-foo.c to have TERM_XTRA_GET_DELAY */
+ Term_xtra(TERM_XTRA_GET_DELAY, 0);
+ cmov_last_time_msec = Term_xtra_long;
+#endif
+}
diff --git a/src/z-term.h b/src/z-term.h
new file mode 100644
index 00000000..31e5b308
--- /dev/null
+++ b/src/z-term.h
@@ -0,0 +1,343 @@
+/* File: z-term.h */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#ifndef INCLUDED_Z_TERM_H
+#define INCLUDED_Z_TERM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "h-basic.h"
+
+#define IN_MAINWINDOW (Term == term_screen)
+
+/*
+ * A term_win is a "window" for a Term
+ *
+ * - Cursor Useless/Visible codes
+ * - Cursor Location (see "Useless")
+ *
+ * - Array[h] -- Access to the attribute array
+ * - Array[h] -- Access to the character array
+ *
+ * - Array[h*w] -- Attribute array
+ * - Array[h*w] -- Character array
+ *
+ * Note that the attr/char pair at (x,y) is a[y][x]/c[y][x]
+ * and that the row of attr/chars at (0,y) is a[y]/c[y]
+ */
+
+typedef struct term_win term_win;
+
+struct term_win
+{
+ bool_ cu, cv;
+ byte cx, cy;
+
+ byte **a;
+ char **c;
+
+ byte *va;
+ char *vc;
+
+ byte **ta;
+ char **tc;
+
+ byte *vta;
+ char *vtc;
+
+ byte **ea;
+ char **ec;
+
+ byte *vea;
+ char *vec;
+
+};
+
+
+
+/*
+ * An actual "term" structure
+ *
+ * - Extra "user" info (used by application)
+ *
+ * - Extra "data" info (used by implementation)
+ *
+ *
+ * - Flag "user_flag"
+ * An extra "user" flag (used by application)
+ *
+ *
+ * - Flag "data_flag"
+ * An extra "data" flag (used by implementation)
+ *
+ *
+ * - Flag "active_flag"
+ * This "term" is "active"
+ *
+ * - Flag "mapped_flag"
+ * This "term" is "mapped"
+ *
+ * - Flag "total_erase"
+ * This "term" should be fully erased
+ *
+ * - Flag "fixed_shape"
+ * This "term" is not allowed to resize
+ *
+ * - Flag "icky_corner"
+ * This "term" has an "icky" corner grid
+ *
+ * - Flag "soft_cursor"
+ * This "term" uses a "software" cursor
+ *
+ * - Flag "always_pict"
+ * Use the "Term_pict()" routine for all text
+ *
+ * - Flag "higher_pict"
+ * Use the "Term_pict()" routine for special text
+ *
+ * - Flag "always_text"
+ * Use the "Term_text()" routine for invisible text
+ *
+ * - Flag "unused_flag"
+ * Reserved for future use
+ *
+ * - Flag "never_bored"
+ * Never call the "TERM_XTRA_BORED" action
+ *
+ * - Flag "never_frosh"
+ * Never call the "TERM_XTRA_FROSH" action
+ *
+ *
+ * - Value "attr_blank"
+ * Use this "attr" value for "blank" grids
+ *
+ * - Value "char_blank"
+ * Use this "char" value for "blank" grids
+ *
+ *
+ * - Ignore this pointer
+ *
+ * - Keypress Queue -- various data
+ *
+ * - Keypress Queue -- pending keys
+ *
+ *
+ * - Window Width (max 255)
+ * - Window Height (max 255)
+ *
+ * - Minimum modified row
+ * - Maximum modified row
+ *
+ * - Minimum modified column (per row)
+ * - Maximum modified column (per row)
+ *
+ *
+ * - Displayed screen image
+ * - Requested screen image
+ *
+ * - Temporary screen image
+ * - Memorized screen image
+ *
+ *
+ * - Hook for init-ing the term
+ * - Hook for nuke-ing the term
+ *
+ * - Hook for user actions
+ *
+ * - Hook for extra actions
+ *
+ * - Hook for placing the cursor
+ *
+ * - Hook for drawing some blank spaces
+ *
+ * - Hook for drawing a string of chars using an attr
+ *
+ * - Hook for drawing a sequence of special attr/char pairs
+ */
+
+typedef struct term term;
+
+struct term
+{
+ vptr user;
+
+ vptr data;
+
+ bool_ user_flag;
+
+ bool_ data_flag;
+
+ bool_ active_flag;
+ bool_ mapped_flag;
+ bool_ total_erase;
+ bool_ fixed_shape;
+ bool_ icky_corner;
+ bool_ soft_cursor;
+ bool_ always_pict;
+ bool_ higher_pict;
+ bool_ always_text;
+ bool_ unused_flag;
+ bool_ never_bored;
+ bool_ never_frosh;
+
+ byte attr_blank;
+ char char_blank;
+
+ char *key_queue;
+
+ u16b key_head;
+ u16b key_tail;
+ u16b key_xtra;
+ u16b key_size;
+
+ byte wid;
+ byte hgt;
+
+ byte y1;
+ byte y2;
+
+ byte *x1;
+ byte *x2;
+
+ term_win *old;
+ term_win *scr;
+
+ term_win *tmp;
+ term_win *mem;
+
+ void (*init_hook)(term *t);
+ void (*nuke_hook)(term *t);
+
+ errr (*user_hook)(int n);
+
+ errr (*xtra_hook)(int n, int v);
+
+ errr (*curs_hook)(int x, int y);
+
+ errr (*wipe_hook)(int x, int y, int n);
+
+ errr (*text_hook)(int x, int y, int n, byte a, cptr s);
+
+ void (*resize_hook)(void);
+
+ errr (*pict_hook)(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp);
+
+};
+
+
+
+
+
+
+
+/**** Available Constants ****/
+
+
+/*
+ * Definitions for the "actions" of "Term_xtra()"
+ *
+ * These values may be used as the first parameter of "Term_xtra()",
+ * with the second parameter depending on the "action" itself. Many
+ * of the actions shown below are optional on at least one platform.
+ *
+ * The "TERM_XTRA_EVENT" action uses "v" to "wait" for an event
+ * The "TERM_XTRA_SHAPE" action uses "v" to "show" the cursor
+ * The "TERM_XTRA_FROSH" action uses "v" for the index of the row
+ * The "TERM_XTRA_SOUND" action uses "v" for the index of a sound
+ * The "TERM_XTRA_ALIVE" action uses "v" to "activate" (or "close")
+ * The "TERM_XTRA_LEVEL" action uses "v" to "resume" (or "suspend")
+ * The "TERM_XTRA_DELAY" action uses "v" as a "millisecond" value
+ *
+ * The other actions do not need a "v" code, so "zero" is used.
+ */
+#define TERM_XTRA_EVENT 1 /* Process some pending events */
+#define TERM_XTRA_FLUSH 2 /* Flush all pending events */
+#define TERM_XTRA_CLEAR 3 /* Clear the entire window */
+#define TERM_XTRA_SHAPE 4 /* Set cursor shape (optional) */
+#define TERM_XTRA_FROSH 5 /* Flush one row (optional) */
+#define TERM_XTRA_FRESH 6 /* Flush all rows (optional) */
+#define TERM_XTRA_NOISE 7 /* Make a noise (optional) */
+#define TERM_XTRA_SOUND 8 /* Make a sound (optional) */
+#define TERM_XTRA_BORED 9 /* Handle stuff when bored (optional) */
+#define TERM_XTRA_REACT 10 /* React to global changes (optional) */
+#define TERM_XTRA_ALIVE 11 /* Change the "hard" level (optional) */
+#define TERM_XTRA_LEVEL 12 /* Change the "soft" level (optional) */
+#define TERM_XTRA_DELAY 13 /* Delay some milliseconds (optional) */
+#define TERM_XTRA_GET_DELAY 14 /* Get the cuyrrent time in milliseconds (optional) */
+#define TERM_XTRA_SCANSUBDIR 15 /* Scan for subdir in a dir */
+#define TERM_XTRA_RENAME_MAIN_WIN 16 /* Rename the main game window */
+
+
+/**** Available Variables ****/
+
+extern term *Term;
+extern FILE *movfile;
+extern int do_movies;
+extern int last_paused;
+
+
+/**** Available Functions ****/
+
+extern errr Term_user(int n);
+extern errr Term_xtra(int n, int v);
+extern long Term_xtra_long;
+extern char scansubdir_dir[1024];
+extern int scansubdir_max;
+extern cptr scansubdir_result[255];
+
+extern void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc, byte ea, char ec);
+extern void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc, byte *ea, char *ec);
+extern void Term_queue_chars(int x, int y, int n, byte a, cptr s);
+
+extern errr Term_fresh(void);
+extern errr Term_set_cursor(int v);
+extern errr Term_gotoxy(int x, int y);
+extern errr Term_draw(int x, int y, byte a, char c);
+extern errr Term_addch(byte a, char c);
+extern errr Term_addstr(int n, byte a, cptr s);
+extern errr Term_putch(int x, int y, byte a, char c);
+extern errr Term_putstr(int x, int y, int n, byte a, cptr s);
+extern errr Term_erase(int x, int y, int n);
+extern errr Term_clear(void);
+extern errr Term_redraw(void);
+extern errr Term_redraw_section(int x1, int y1, int x2, int y2);
+
+extern errr Term_get_cursor(int *v);
+extern errr Term_get_size(int *w, int *h);
+extern errr Term_locate(int *x, int *y);
+extern errr Term_what(int x, int y, byte *a, char *c);
+
+extern errr Term_flush(void);
+extern errr Term_keypress(int k);
+extern errr Term_key_push(int k);
+extern errr Term_inkey(char *ch, bool_ wait, bool_ take);
+
+extern errr Term_save(void);
+extern term_win* Term_save_to(void);
+extern errr Term_load(void);
+extern errr Term_load_from(term_win *save, bool_ final);
+
+extern errr Term_exchange(void);
+
+extern errr Term_resize(int w, int h);
+
+extern errr Term_activate(term *t);
+
+extern errr term_nuke(term *t);
+extern errr term_init(term *t, int w, int h, int k);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+
diff --git a/src/z-util.c b/src/z-util.c
new file mode 100644
index 00000000..7c5374f3
--- /dev/null
+++ b/src/z-util.c
@@ -0,0 +1,234 @@
+/* File: z-util.c */
+
+/* Purpose: Low level utilities -BEN- */
+
+#include "z-util.h"
+
+
+
+/*
+ * Global variables for temporary use
+ */
+char char_tmp = 0;
+byte byte_tmp = 0;
+sint sint_tmp = 0;
+uint uint_tmp = 0;
+long long_tmp = 0;
+huge huge_tmp = 0;
+errr errr_tmp = 0;
+
+
+/*
+ * Global pointers for temporary use
+ */
+cptr cptr_tmp = NULL;
+vptr vptr_tmp = NULL;
+
+
+
+
+/*
+ * Constant bool meaning true
+ */
+bool_ bool_true = 1;
+
+/*
+ * Constant bool meaning false
+ */
+bool_ bool_false = 0;
+
+
+/*
+ * Global NULL cptr
+ */
+cptr cptr_null = NULL;
+
+
+/*
+ * Global NULL vptr
+ */
+vptr vptr_null = NULL;
+
+
+
+/*
+ * Global SELF vptr
+ */
+vptr vptr_self = (vptr)(&vptr_self);
+
+
+
+/*
+ * Convenient storage of the program name
+ */
+cptr argv0 = NULL;
+
+
+
+/*
+ * A routine that does nothing
+ */
+void func_nothing(void)
+{
+ /* Do nothing */
+}
+
+
+/*
+ * A routine that always returns "success"
+ */
+errr func_success(void)
+{
+ return (0);
+}
+
+
+/*
+ * A routine that always returns a simple "problem code"
+ */
+errr func_problem(void)
+{
+ return (1);
+}
+
+
+/*
+ * A routine that always returns a simple "failure code"
+ */
+errr func_failure(void)
+{
+ return ( -1);
+}
+
+
+
+/*
+ * A routine that always returns "true"
+ */
+bool_ func_true(void)
+{
+ return (1);
+}
+
+
+/*
+ * A routine that always returns "false"
+ */
+bool_ func_false(void)
+{
+ return (0);
+}
+
+
+
+
+/*
+ * Determine if string "t" is equal to string "t"
+ */
+bool_ streq(cptr a, cptr b)
+{
+ if ((a == NULL) && (b == NULL)) { return TRUE; }
+ if (a == NULL) { return FALSE; }
+ if (b == NULL) { return FALSE; }
+ return (!strcmp(a, b));
+}
+
+
+/*
+ * Determine if string "t" is a suffix of string "s"
+ */
+bool_ suffix(cptr s, cptr t)
+{
+ int tlen = strlen(t);
+ int slen = strlen(s);
+
+ /* Check for incompatible lengths */
+ if (tlen > slen) return (FALSE);
+
+ /* Compare "t" to the end of "s" */
+ return (!strcmp(s + slen - tlen, t));
+}
+
+
+
+
+/*
+ * Redefinable "plog" action
+ */
+void (*plog_aux)(cptr) = NULL;
+
+/*
+ * Print (or log) a "warning" message (ala "perror()")
+ * Note the use of the (optional) "plog_aux" hook.
+ */
+void plog(cptr str)
+{
+ /* Use the "alternative" function if possible */
+ if (plog_aux) (*plog_aux)(str);
+
+ /* Just do a labeled fprintf to stderr */
+ else (void)(fprintf(stderr, "%s: %s\n", argv0 ? argv0 : "???", str));
+}
+
+
+
+/*
+ * Redefinable "quit" action
+ */
+void (*quit_aux)(cptr) = NULL;
+
+/*
+ * Exit (ala "exit()"). If 'str' is NULL, do "exit(0)".
+ * If 'str' begins with "+" or "-", do "exit(atoi(str))".
+ * Otherwise, plog() 'str' and exit with an error code of -1.
+ * But always use 'quit_aux', if set, before anything else.
+ */
+void quit(cptr str)
+{
+ /* Attempt to use the aux function */
+ if (quit_aux) (*quit_aux)(str);
+
+ /* Success */
+ if (!str) (void)(exit(0));
+
+ /* Extract a "special error code" */
+ if ((str[0] == '-') || (str[0] == '+')) (void)(exit(atoi(str)));
+
+ /* Send the string to plog() */
+ plog(str);
+
+ /* Failure */
+ (void)(exit( -1));
+}
+
+
+
+/*
+ * Redefinable "core" action
+ */
+void (*core_aux)(cptr) = NULL;
+
+/*
+ * Dump a core file, after printing a warning message
+ * As with "quit()", try to use the "core_aux()" hook first.
+ */
+void core(cptr str)
+{
+ char *crash = NULL;
+
+ /* Use the aux function */
+ if (core_aux) (*core_aux)(str);
+
+ /* Dump the warning string */
+ if (str) plog(str);
+
+ /* Attempt to Crash */
+ (*crash) = (*crash);
+
+ /* Be sure we exited */
+ quit("core() failed");
+}
+
+
+
+
diff --git a/src/z-util.h b/src/z-util.h
new file mode 100644
index 00000000..11dbdb4e
--- /dev/null
+++ b/src/z-util.h
@@ -0,0 +1,91 @@
+/* File z-util.h */
+
+#ifndef INCLUDED_Z_UTIL_H
+#define INCLUDED_Z_UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "h-basic.h"
+
+
+/*
+ * Extremely basic stuff, like global temp and constant variables.
+ * Also, some very useful low level functions, such as "streq()".
+ * All variables and functions in this file are "addressable".
+ */
+
+
+/**** Available variables ****/
+
+/* Temporary Vars */
+extern char char_tmp;
+extern byte byte_tmp;
+extern sint sint_tmp;
+extern uint uint_tmp;
+extern long long_tmp;
+extern huge huge_tmp;
+extern errr errr_tmp;
+
+/* Temporary Pointers */
+extern cptr cptr_tmp;
+extern vptr vptr_tmp;
+
+
+/* Constant pointers (NULL) */
+extern cptr cptr_null;
+extern vptr vptr_null;
+
+
+/* A bizarre vptr that always points at itself */
+extern vptr vptr_self;
+
+
+/* A cptr to the name of the program */
+extern cptr argv0;
+
+
+/* Aux functions */
+extern void (*plog_aux)(cptr);
+extern void (*quit_aux)(cptr);
+extern void (*core_aux)(cptr);
+
+
+/**** Available Functions ****/
+
+/* Function that does nothing */
+extern void func_nothing(void);
+
+/* Functions that return basic "errr" codes */
+extern errr func_success(void);
+extern errr func_problem(void);
+extern errr func_failure(void);
+
+/* Functions that return bools */
+extern bool_ func_true(void);
+extern bool_ func_false(void);
+
+
+/* Test equality, prefix, suffix */
+extern bool_ streq(cptr s, cptr t);
+extern bool_ prefix(cptr s, cptr t);
+extern bool_ suffix(cptr s, cptr t);
+
+
+/* Print an error message */
+extern void plog(cptr str);
+
+/* Exit, with optional message */
+extern void quit(cptr str);
+
+/* Dump core, with optional message */
+extern void core(cptr str);
+
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/src/z-virt.c b/src/z-virt.c
new file mode 100644
index 00000000..c9277166
--- /dev/null
+++ b/src/z-virt.c
@@ -0,0 +1,187 @@
+/* File: z-virt.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/* Purpose: Memory management routines -BEN- */
+
+#include "z-virt.h"
+
+#include "z-util.h"
+
+
+/*
+ * Allow debugging messages to track memory usage.
+ */
+#ifdef VERBOSE_RALLOC
+static long virt_make = 0;
+static long virt_kill = 0;
+static long virt_size = 0;
+#endif
+
+
+/*
+ * Optional auxiliary "rnfree" function
+ */
+vptr (*rnfree_aux)(vptr, huge) = NULL;
+
+/*
+ * Free some memory (allocated by ralloc), return NULL
+ */
+vptr rnfree(vptr p, huge len)
+{
+ /* Easy to free zero bytes */
+ if (len == 0) return (NULL);
+
+#ifdef VERBOSE_RALLOC
+
+ /* Decrease memory count */
+ virt_kill += len;
+
+ /* Message */
+ if (len > virt_size)
+ {
+ char buf[80];
+ sprintf(buf, "Kill (%ld): %ld - %ld = %ld.",
+ len, virt_make, virt_kill, virt_make - virt_kill);
+ plog(buf);
+ }
+
+#endif
+
+ /* Use the "aux" function */
+ if (rnfree_aux) return ((*rnfree_aux)(p, len));
+
+ /* Use "free" */
+ free ((char*)(p));
+
+ /* Done */
+ return (NULL);
+}
+
+
+/*
+ * Optional auxiliary "rpanic" function
+ */
+vptr (*rpanic_aux)(huge) = NULL;
+
+/*
+ * The system is out of memory, so panic. If "rpanic_aux" is set,
+ * it can be used to free up some memory and do a new "ralloc()",
+ * or if not, it can be used to save things, clean up, and exit.
+ * By default, this function simply crashes the computer.
+ */
+vptr rpanic(huge len)
+{
+ /* Hopefully, we have a real "panic" function */
+ if (rpanic_aux) return ((*rpanic_aux)(len));
+
+ /* Attempt to crash before icky things happen */
+ core("Out of Memory!");
+
+ /* Paranoia */
+ return ((vptr)(NULL));
+}
+
+
+/*
+ * Optional auxiliary "ralloc" function
+ */
+vptr (*ralloc_aux)(huge) = NULL;
+
+
+/*
+ * Allocate some memory
+ */
+vptr ralloc(huge len)
+{
+ vptr mem;
+
+ /* Allow allocation of "zero bytes" */
+ if (len == 0) return ((vptr)(NULL));
+
+#ifdef VERBOSE_RALLOC
+
+ /* Count allocated memory */
+ virt_make += len;
+
+ /* Log important allocations */
+ if (len > virt_size)
+ {
+ char buf[80];
+ sprintf(buf, "Make (%ld): %ld - %ld = %ld.",
+ len, virt_make, virt_kill, virt_make - virt_kill);
+ plog(buf);
+ }
+
+#endif
+
+ /* Use the aux function if set */
+ if (ralloc_aux) mem = (*ralloc_aux)(len);
+
+ /* Use malloc() to allocate some memory */
+ else mem = ((vptr)(malloc((size_t)(len))));
+
+ /* We were able to acquire memory */
+ if (!mem) mem = rpanic(len);
+
+ /* Return the memory, if any */
+ return (mem);
+}
+
+
+
+
+/*
+ * Allocate a constant string, containing the same thing as 'str'
+ */
+cptr string_make(cptr str)
+{
+ huge len = 0;
+ cptr t = str;
+ char *s, *res;
+
+ /* Simple sillyness */
+ if (!str) return (str);
+
+ /* Get the number of chars in the string, including terminator */
+ while (str[len++]) /* loop */;
+
+ /* Allocate space for the string */
+ s = res = (char*)(ralloc(len));
+
+ /* Copy the string (with terminator) */
+ while ((*s++ = *t++) != 0) /* loop */;
+
+ /* Return the allocated, initialized, string */
+ return (res);
+}
+
+
+/*
+ * Un-allocate a string allocated above.
+ * Depends on no changes being made to the string.
+ */
+errr string_free(cptr str)
+{
+ huge len = 0;
+
+ /* Succeed on non-strings */
+ if (!str) return (0);
+
+ /* Count the number of chars in 'str' plus the terminator */
+ while (str[len++]) /* loop */;
+
+ /* Kill the buffer of chars we must have allocated above */
+ rnfree((vptr)(str), len);
+
+ /* Success */
+ return (0);
+}
+
+
diff --git a/src/z-virt.h b/src/z-virt.h
new file mode 100644
index 00000000..a7880f2f
--- /dev/null
+++ b/src/z-virt.h
@@ -0,0 +1,168 @@
+/* File: z-virt.h */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#ifndef INCLUDED_Z_VIRT_H
+#define INCLUDED_Z_VIRT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "h-basic.h"
+
+#ifdef CHECK_MEMORY_LEAKS
+#include <leak_detector.h>
+#endif
+
+/*
+ * Memory management routines.
+ *
+ * Set ralloc_aux to modify the memory allocation routine.
+ * Set rnfree_aux to modify the memory de-allocation routine.
+ * Set rpanic_aux to let the program react to memory failures.
+ *
+ * These routines work best as a *replacement* for malloc/free.
+ *
+ * The string_make() and string_free() routines handle dynamic strings.
+ * A dynamic string is a string allocated at run-time, which should not
+ * be modified once it has been created.
+ *
+ * Note the macros below which simplify the details of allocation,
+ * deallocation, setting, clearing, casting, size extraction, etc.
+ *
+ * The macros MAKE/C_MAKE and KILL/C_KILL have a "procedural" metaphor,
+ * and they actually modify their arguments.
+ *
+ * Note that, for some reason, some allocation macros may disallow
+ * "stars" in type names, but you can use typedefs to circumvent
+ * this. For example, instead of "type **p; MAKE(p,type*);" you
+ * can use "typedef type *type_ptr; type_ptr *p; MAKE(p,type_ptr)".
+ *
+ * Note that it is assumed that "memset()" will function correctly,
+ * in particular, that it returns its first argument.
+ */
+
+
+
+/**** Available macros ****/
+
+
+/* Size of 'N' things of type 'T' */
+#define C_SIZE(N,T) \
+ ((huge)((N)*(sizeof(T))))
+
+/* Size of one thing of type 'T' */
+#define SIZE(T) \
+ ((huge)(sizeof(T)))
+
+
+/* Wipe an array of type T[N], at location P, and return P */
+#define C_WIPE(P,N,T) \
+ (memset((char*)(P),0,C_SIZE(N,T)))
+
+/* Wipe a thing of type T, at location P, and return P */
+#define WIPE(P,T) \
+ (memset((char*)(P),0,SIZE(T)))
+
+
+/* Load an array of type T[N], at location P1, from another, at location P2 */
+#define C_COPY(P1,P2,N,T) \
+ (memcpy((char*)(P1),(char*)(P2),C_SIZE(N,T)))
+
+/* Load a thing of type T, at location P1, from another, at location P2 */
+#define COPY(P1,P2,T) \
+ (memcpy((char*)(P1),(char*)(P2),SIZE(T)))
+
+
+/* Free an array of N things of type T at P, return NULL */
+#define C_FREE(P,N,T) \
+ (rnfree(P,C_SIZE(N,T)))
+
+/* Free one thing of type T at P, return NULL */
+#define FREE(P,T) \
+ (rnfree(P,SIZE(T)))
+
+
+/* Allocate, and return, an array of type T[N] */
+#define C_RNEW(N,T) \
+ (ralloc(C_SIZE(N,T)))
+
+/* Allocate, and return, a thing of type T */
+#define RNEW(T) \
+ (ralloc(SIZE(T)))
+
+
+/* Allocate, wipe, and return an array of type T[N] */
+#define C_ZNEW(N,T) \
+ (C_WIPE(C_RNEW(N,T),N,T))
+
+/* Allocate, wipe, and return a thing of type T */
+#define ZNEW(T) \
+ (WIPE(RNEW(T),T))
+
+
+/* Allocate a wiped array of type T[N], assign to pointer P */
+#define C_MAKE(P,N,T) \
+ ((P)=C_ZNEW(N,T))
+
+/* Allocate a wiped thing of type T, assign to pointer P */
+#define MAKE(P,T) \
+ ((P)=ZNEW(T))
+
+
+/* Free an array of type T[N], at location P, and set P to NULL */
+#define C_KILL(P,N,T) \
+ ((P)=C_FREE(P,N,T))
+
+/* Free a thing of type T, at location P, and set P to NULL */
+#define KILL(P,T) \
+ ((P)=FREE(P,T))
+
+
+
+/**** Available variables ****/
+
+/* Replacement hook for "rnfree()" */
+extern vptr (*rnfree_aux)(vptr, huge);
+
+/* Replacement hook for "rpanic()" */
+extern vptr (*rpanic_aux)(huge);
+
+/* Replacement hook for "ralloc()" */
+extern vptr (*ralloc_aux)(huge);
+
+
+/**** Available functions ****/
+
+/* De-allocate a given amount of memory */
+extern vptr rnfree(vptr p, huge len);
+
+/* Panic, attempt to Allocate 'len' bytes */
+extern vptr rpanic(huge len);
+
+/* Allocate (and return) 'len', or dump core */
+extern vptr ralloc(huge len);
+
+/* Create a "dynamic string" */
+extern cptr string_make(cptr str);
+
+/* Free a string allocated with "string_make()" */
+extern errr string_free(cptr str);
+
+
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+
+#endif
diff --git a/src/z_pack.pkg b/src/z_pack.pkg
new file mode 100644
index 00000000..5a46b3b2
--- /dev/null
+++ b/src/z_pack.pkg
@@ -0,0 +1,398 @@
+/* File: z_pack.pkg */
+
+/*
+ * Purpose: Lua interface defitions for z-*.c
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @name Terminal actions
+ * @{ */
+/** @def TERM_XTRA_EVENT
+ * @note Process some pending events
+ */
+#define TERM_XTRA_EVENT 1
+/** @def TERM_XTRA_FLUSH
+ * @note Flush all pending events
+ */
+#define TERM_XTRA_FLUSH 2
+/** @def TERM_XTRA_CLEAR
+ * @note Clear the entire window
+ */
+#define TERM_XTRA_CLEAR 3
+/** @def TERM_XTRA_SHAPE
+ * @note Set cursor shape (optional)
+ */
+#define TERM_XTRA_SHAPE 4
+/** @def TERM_XTRA_FROSH
+ * @note Flush one row (optional)
+ */
+#define TERM_XTRA_FROSH 5
+/** @def TERM_XTRA_FRESH
+ * @note Flush all rows (optional)
+ */
+#define TERM_XTRA_FRESH 6
+/** @def TERM_XTRA_NOISE
+ * @note Make a noise (optional)
+ */
+#define TERM_XTRA_NOISE 7
+/** @def TERM_XTRA_SOUND
+ * @note Make a sound (optional)
+ */
+#define TERM_XTRA_SOUND 8
+/** @def TERM_XTRA_BORED
+ * @note Handle stuff when bored (optional)
+ */
+#define TERM_XTRA_BORED 9
+/** @def TERM_XTRA_REACT
+ * @note React to global changes (optional)
+ */
+#define TERM_XTRA_REACT 10
+/** @def TERM_XTRA_ALIVE
+ * @note Change the "hard" level (optional)
+ */
+#define TERM_XTRA_ALIVE 11
+/** @def TERM_XTRA_LEVEL
+ * @note Change the "soft" level (optional)
+ */
+#define TERM_XTRA_LEVEL 12
+/** @def TERM_XTRA_DELAY
+ * @note Delay some milliseconds (optional)
+ */
+#define TERM_XTRA_DELAY 13
+/** @def TERM_XTRA_GET_DELAY
+ * @note Get the cuyrrent time in milliseconds (optional)
+ */
+#define TERM_XTRA_GET_DELAY 14
+/** @def TERM_XTRA_SCANSUBDIR
+ * @note Scan for subdir in a dir
+ */
+#define TERM_XTRA_SCANSUBDIR 15
+/** @} */
+
+/** @var Term_xtra_long
+ * @brief Number
+ */
+extern long Term_xtra_long;
+
+/** @var scansubdir_dir[1024]
+ * @brief String
+ * @note The directory which is scanned for sub-directories.
+ */
+char scansubdir_dir[1024];
+
+/** @var scansubdir_max
+ * @brief Number
+ * @note The number of entries in the scansubdir_result array.
+ */
+int scansubdir_max;
+
+/** @var scansubdir_result[scansubdir_max]
+ * @brief String
+ * @note The sub-directories of scansubdir_dir directory.
+ */
+cptr scansubdir_result[scansubdir_max];
+
+/** @fn Term_xtra(int n, int v)
+ * @brief Generic function to perform system dependant terminal actions.\n
+ * @param n Number \n a terminal action (see TERM_XTRA fields).
+ * @brief Terminal action
+ * @param v Number \n variable depending on the terminal action.
+ * @brief Variable
+ * @return Number \n Result of the terminal action.
+ * @note
+ * The "Term->xtra_hook" hook provides a variety of different functions,
+ * based on the first parameter (which should be taken from the various
+ * TERM_XTRA_* defines) and the second parameter (which may make sense
+ * only for some first parameters). It is available to the program via
+ * the "Term_xtra()" function, though some first parameters are only
+ * "legal" when called from inside this package.
+ * @note (see file z-term.c)
+ */
+extern errr Term_xtra(int n, int v);
+
+/** @fn Term_set_cursor(int v)
+ * @brief Set the cursor visibility.\n
+ * @param v Number \n v is the visibility.
+ * @brief Visibility
+ * @return Number \n 1 if visibility was unchanged, otherwise 0.
+ * @note
+ * Cursor visibility (field "cv") is defined as a boolean, so take care what
+ * value is assigned to "v".
+ * @note (see file z-term.c)
+ */
+extern errr Term_set_cursor(int v);
+
+/** @fn Term_gotoxy(int x, int y)
+ * @brief Place the cursor at a given location.\n
+ * @param x Number \n x-coordinate of target location.
+ * @brief X-coordinate
+ * @param y Number \n y-coordinate of target location.
+ * @brief Y-coordinate
+ * @return Number \n -1 if cursor could not be placed at given location,
+ * otherwise 0.
+ * @note
+ * Note -- "illegal" requests do not move the cursor.\n\n
+ * The cursor is flagged as useful if it placed okay.
+ * @note (see file z-term.c)
+ */
+extern errr Term_gotoxy(int x, int y);
+
+/** @fn Term_putch(int x, int y, byte a, char c)
+ * @brief Move to a location and, using an attr, add a char.\n
+ * @param x Number \n x-coordinate of target location.
+ * @brief X-coordinate
+ * @param y Number \n y-coordinate of target location.
+ * @brief Y-coordinate
+ * @param a Number \n attribute of character.
+ * @brief Attribute
+ * @param c String \n the character.
+ * @brief Character
+ * @return Number \n <0 if error, 0 if success, 1 if success but cursor is
+ * useless.
+ * @note
+ * We return "-2" if the character is "illegal". XXX XXX\n\n
+ * We return "-1" if the cursor is currently unusable.\n\n
+ * We queue the given attr/char for display at the current
+ * cursor location, and advance the cursor to the right,
+ * marking it as unuable and returning "1" if it leaves
+ * the screen, and otherwise returning "0".\n\n
+ * So when this function returns a positive value, future calls to this
+ * function will return negative ones.
+ * @note (see file z-term.c)
+ */
+extern errr Term_putch(int x, int y, byte a, char c);
+
+/** @fn Term_putstr(int x, int y, int n, byte a, cptr s)
+ * @brief Move to a location and, using an attr, add a string.\n
+ * @param x Number \n x-coordinate of target location.
+ * @brief X-coordinate
+ * @param y Number \n y-coordinate of target location.
+ * @brief Y-coordinate
+ * @param n Number \n length of string.
+ * @brief Length
+ * @param a Number \n attribute of string.
+ * @brief Attribute
+ * @param s String \n the string.
+ * @brief String
+ * @return Number \n <0 if error, 0 if success, 1 if success but cursor is
+ * useless.
+ * @note
+ * For length "n", using negative values to imply the largest possible value,
+ * and then we use the minimum of this length and the "actual" length of the
+ * string as the actual number of characters to attempt to display, never
+ * displaying more characters than will actually fit, since we do NOT attempt
+ * to "wrap" the cursor at the screen edge.\n\n
+ * We return "-1" if the cursor is currently unusable.\n
+ * We return "N" if we were "only" able to write "N" chars, even if all of the
+ * given characters fit on the screen, and mark the cursor as unusable for
+ * future attempts.\n\n
+ * So when this function, returns a positive value, future calls to this
+ * function will return negative ones.
+ * @note (see file z-term.c)
+ */
+extern errr Term_putstr(int x, int y, int n, byte a, cptr s);
+
+/** @fn Term_clear(void)
+ * @brief Clear the entire window, and move to the top left corner.
+ * @return Number \n 0 (always).
+ * @note
+ * Note the use of the special "total_erase" code
+ * @note (see file z-term.c)
+ */
+extern errr Term_clear(void);
+
+/** @fn Term_redraw(void)
+ * @brief Redraw (and refresh) the whole window.
+ * @return Number \n 0 (always).
+ * @note (see file z-term.c)
+ */
+extern errr Term_redraw(void);
+
+/** @fn Term_redraw_section(int x1, int y1, int x2, int y2)
+ * @brief Redraw part of a window.\n
+ * @param x1 Number \n x-coordinate of top-left location.
+ * @brief X-coordinate (top left)
+ * @param y1 Number \n y-coordinate of top-left location.
+ * @brief Y-coordinate (top left)
+ * @param x2 Number \n x-coordinate of bottom-right location.
+ * @brief X-coordinate (bottom right)
+ * @param y2 Number \n y-coordinate of bottom-right location.
+ * @brief Y-coordinate (bottom right)
+ * @return Number \n 0 (always).
+ * @note (see file z-term.c)
+ */
+extern errr Term_redraw_section(int x1, int y1, int x2, int y2);
+
+/** @fn Term_get_size(int *w, int *h)
+ * @brief Extract the current window size.\n
+ * @param *w Number
+ * @brief Screen width
+ * @param *h Number
+ * @brief Screen height
+ * @return Number \n 0 (always).
+ * @return *w Number \n The width of the screen (in characters).
+ * @return *h Number \n The height of the screen (in characters).
+ * @note (see file z-term.c)
+ */
+extern errr Term_get_size(int *w, int *h);
+
+/*
+ * random numbers
+ */
+$static s32b lua_rand_int(s32b m) {return rand_int(m);}
+
+/** @fn rand_int(s32b m);
+ * @brief Generate a random integer between 0 and (m - 1).\n
+ * @param m Number \n maximum value of random integer. The random integer
+ * will be less than "m".
+ * @brief Maximum
+ * @return Number \n The random number.
+ * @note (see file w_z_pack.c)
+ */
+static s32b lua_rand_int @ rand_int(s32b m);
+
+/*
+ * Generates a random long integer X where A<=X<=B
+ * The integer X falls along a uniform distribution.
+ * Note: rand_range(0,N-1) == rand_int(N)
+ */
+$static s32b lua_rand_range(s32b A, s32b B) {return ((A) + (rand_int(1+(B)-(A))));}
+
+/** @fn rand_range(s32b A, s32b B);
+ * @brief Generate a random integer between A and B inclusive.\n
+ * @param A Number \n minimum number.
+ * @brief Minimum
+ * @param B Number \n maximum number.
+ * @brief Maximum
+ * @return Number \n The random number.
+ * @note (see file w_z_pack.c)
+ */
+static s32b lua_rand_range @ rand_range(s32b A, s32b B);
+
+/*
+ * Generate a random long integer X where A-D<=X<=A+D
+ * The integer X falls along a uniform distribution.
+ * Note: rand_spread(A,D) == rand_range(A-D,A+D)
+ */
+$static s32b lua_rand_spread(s32b A, s32b D) {return ((A) + (rand_int(1+(D)+(D))) - (D));}
+
+/** @fn rand_spread(s32b A, s32b D);
+ * @brief Generate a radom integer between A-D and A+D inclusive.\n
+ * @param A Number \n average number.
+ * @brief Average
+ * @param D Number \n deviation from average.
+ * @brief Deviation
+ * @return Number \n The random number.
+ * @note (see file w_z_pack.c)
+ */
+static s32b lua_rand_spread @ rand_spread(s32b A, s32b D);
+
+
+/*
+ * Generate a random long integer X where 1<=X<=M
+ * Also, "correctly" handle the case of M<=1
+ */
+$static s32b lua_randint(s32b m) {return rand_int(m) + 1;}
+
+/** @fn randint(s32b m);
+ * @brief Generate a random integer between 1 and M inclusive.\n
+ * @param m Number \n maximum value of random integer.
+ * @brief Maximum
+ * @return Number \n The random number.
+ * @note (see file w_z_pack.c)
+ */
+static s32b lua_randint @ randint(s32b m);
+
+
+/*
+ * Evaluate to TRUE "P" percent of the time
+ */
+$static bool lua_magik(s32b P) {return (rand_int(100) < (P));}
+
+/** @fn magik(s32b P);
+ * @brief Return TRUE "P" % of the time.
+ * @param P Number \n percent chance the function returns TRUE.
+ * @brief Percent true
+ * @return Boolean \n TRUE if a random number from 0 to 99 is less than P,
+ * otherwise FALSE.
+ * @note (see file w_z_pack.c)
+ */
+static bool lua_magik @ magik(s32b P);
+
+
+/**** Available Variables ****/
+/** @var Rand_quick
+ * @brief Boolean
+ * @note
+ * If this is TRUE, then use the "simple" Random Number Generator.\n
+ * If this is FALSE, then use the "complex" Random Number Generator.
+ */
+extern bool Rand_quick;
+
+/** @var Rand_value
+ * @brief Number
+ * @note
+ * The current value (seed) of the simple Random Number Generator.
+ */
+extern u32b Rand_value;
+
+/**** Available Functions ****/
+/** @fn damroll(int num, int sides)
+ * @brief Generates damage for "2d6" style dice rolls.\n
+ * @param num Number \n the number of "dice" used.
+ * @brief Number
+ * @param sides Number \n the number of sides on each "die".
+ * @brief Sides
+ * @return Number \n The random number.
+ * @note
+ * The function simulates the rolling of "num" "sides"-sided dice. Each die
+ * will result in a random number from 1 to "sides".
+ * @note (see file z-rand.c)
+ */
+extern s16b damroll(int num, int sides);
+
+/** @fn maxroll(int num, int sides)
+ * @brief Generate the maximum damage for "num" dice with "sides" sides each.
+ * @param num Number \n The number of "dice" used.
+ * @brief Number
+ * @param sides Number \n The number of sides on each "die".
+ * @brief Sides
+ * @return Number \n "num" * "sides".
+ * @note (see file z-rand.c)
+ */
+extern s16b maxroll(int num, int sides);